TidaLuna settings page design reworked: better UI & UX in the Tidal design, with better usability and a clearer layout - #197
Conversation
The plugin store list lives in plugins/ui/src/SettingsPage/PluginStoreTab/index.tsx today, so adding a store needs a code change plus a client release, and removing one never reaches users who already have it persisted. store/stores.json is the list instead. Seeded with the 22 stores currently hardcoded and 7 tombstones recovered from the git history of index.tsx, three of which are hard 404s that have been showing up as error cards for months. store/validate.sh checks schema, unknown keys, url and repo consistency, duplicates and that every active store.json actually resolves with a non empty plugins array. Needs only jq and curl, runs in about 9 seconds.
Flake Review Results for #197Available systems: aarch64-darwin, aarch64-linux, armv6l-linux, armv7l-linux, i686-linux, powerpc64le-linux, riscv64-linux, x86_64-darwin, x86_64-freebsd, x86_64-linux 🔄 Modified (2)
Generated by flake-review |
|
Hey! I noticed your ui/settings-overhaul branch while working on Lunatic UI. We are actually solving very similar problems from different sides: plugin store redesign, plugin cards, settings components and TIDAL-style tokens. |
|
Sorry for not having commented on these PR's yet. I'm not sure if I'll merge the changes for some bits as overhauling the UI is a lot to maintain. For the stores impl I feel it's a bit overcomplicated (but that's just due to AI being used so heavily). But when I get time to properly look at things I can talk more on it. |
…ng it Replaces the 22 addToStores calls with a fetch of store/stores.json from raw githubusercontent, pinned to master so a merged store reaches clients without a release. The old storeUrls key mixed defaults and user additions in one list, so removing a default brought it back on the next start and removing one from the code never reached anyone who already had it. Split into three: userStoreUrls for what the user added, hiddenStoreUrls for defaults they dismissed, and the fetched list, which is persisted and doubles as the offline cache. Tombstoned entries drop dead stores from clients that already carry them. The decision logic lives in registry.logic.ts, separate from the reactive state and the fetching in registry.ts. Three things found on the way are fixed here because the wiring touched them: - the deeplink handler lowercased the whole url and pushed it without stripping /store.json, persisting a url that always 404s - Storage.tsx had a second addToStores writing to a different idb store, so adding a store from the themes tab silently went nowhere - LunaStore mapped only the first space in a plugin name and called .filter on unvalidated third party json, which takes down the whole settings page
The [master] Release workflow only runs on pushes to master that touch package.json, so without this the merge would not produce a release build.
Turns store/stores.json into store/registry.json nightly with stars, repo health and per plugin download counts, so the client makes one raw fetch instead of hitting the GitHub API once per store. The client tries registry.json and falls back to stores.json, which satisfies the same shape, so it works before the first run has happened. Metrics older than seven days are dropped rather than shown stale, since the numbers go wrong silently when the generator stops running.
Reads Tidal's wave custom properties rather than approximating them, so the page follows along when Tidal retunes its palette. One module defines every surface, border, radius and shadow. Icons come from Tidal's own sprite. It ships no icon font and no third party pack, it mounts a sprite of ~108 symbols under body and draws its interface from it with use href, so Luna draws from the same one and @mui/icons-material leaves the tree. Six icons the sprite lacks are drawn at the weight the sprite itself uses. The symbols are not drawn to a common margin, ink runs from 50% to 100% of the box, so each carries a crop normalising it to the sprite's own 90% median. Installed plugins are grouped into errors, enabled and disabled and move between groups as they are toggled. Adding a store moved from a section at the bottom of the store tab onto a plus beside the search. A plugin can set image in its package.json and the store card shows it, click to enlarge. Urls are limited to GitHub hosts: the store renders a card for every plugin of every listed store, so an unrestricted host would let any listed author log the IP of everyone who opens the tab. An author avatar goes through the same check for the same reason. LunaPlugin.refreshPackage comes with it, since fromStorage returns an instance created earlier in the session without refetching, so a card pinned whatever the package looked like when it was first rendered. PluginStoreTab/InstallFromUrl.tsx is removed as a near duplicate of the one in Storage.tsx, which is the copy that has the .css branch this one's own label promised. LiveReloadToggle.tsx and LunaPluginHeader.tsx go too, both orphaned by the rewrite.
This stacks on Inrixia#195, which bumps to 1.17.0-beta. Each merge to master needs its own package.json touch to trigger the [master] Release build, so this PR carries the next patch.
One outlined control token now backs both the Select and the TextField wrappers instead of two near identical copies, so every field shares one radius, fill and border from a single place. LunaIcon, LunaSearch, LunaList and LunaPreviewImage lose props and helpers nothing calls. LunaSetting no longer takes StackProps, so LunaSecureTextSetting drops the spacing and wrapper it passed for the old Stack layout. Also disposes the themes list subscription, which was left running when the tab unmounted.
f935d51 to
347a20a
Compare
No rush, and a no on either is a fine answer. On the store code, that is fair, it was overcomplicated. It was 247 lines across registry.ts and a second file that existed only as an indirection layer so the logic could be unit tested. That file is gone, it is now 146 lines in one file with direct mutations, live on the PR head. What I am trying to solve with Inrixia/TidaLuna#197: most people running TidaLuna are not developers. They installed it for one feature they wanted, and the settings page is the only part of it they ever see. Right now it asks them to know things they have no reason to know. So the work aims at three things. Finding what you have. Installed plugins group into errors, enabled and disabled, and move between groups the moment you toggle one. Search filters as you type. Knowing what you are about to install. A store card can carry one preview image, plus the store's stars and optionally a download count, which stays off unless the author enables it. Before this, deciding whether to install something meant reading a name. Preview urls are limited to four GitHub hosts, since the tab draws a card for every plugin of every listed store, so an open host would let any listed author log the IP of everyone who opens it. Not looking bolted on. tidalTokens.ts resolves 15 of TIDAL's own CSS custom properties with fallbacks, and the icons are referenced from TIDAL's own sprite rather than a pack. @mui/icons-material goes from sixteen imports across nine files down to one, the chevron in LunaList.tsx, so it stays in the tree. Inrixia/TidaLuna#195 On maintenance you are right, and the cost lands on you rather than me. I can split |
Requires #195. That PR moves the store list into
store/stores.json, and the nightly action in this one reads that file, so this cannot land first.Opened as a draft. It currently includes #195's commits so it builds and can be reviewed as one piece; once #195 merges I rebase onto master and the diff drops to this PR's own work.
Most people running TidaLuna are not developers. They installed it for one feature they wanted, and the settings page is the only part of it they ever see. It should be understandable without knowing how any of it works, and it should look like it belongs in Tidal rather than sitting on top of it. That is what this is.
It looks like Tidal now
Every colour, radius, surface and easing comes from Tidal's own
wavedesign tokens rather than being approximated. Because Luna references the variables instead of copying the values out, the page follows along when Tidal retunes its palette instead of slowly drifting out of date.The icons are Tidal's actual icons. Tidal ships no icon font and no third party pack, it mounts its own sprite and draws its whole interface from it, so Luna now draws from the same sprite.
@mui/icons-materialdrops from sixteen imports across nine files to one, the expand chevron inLunaList.tsx.You can find things
Installed plugins are grouped into errors, enabled and disabled, and move between groups the moment you toggle one, so the list stays organised while you work in it. Search filters as you type.
You can see what a plugin is before installing it
Plugin authors can add one preview image, and the store card shows it. Click it and it opens full size.
Cards also show the store's stars, and optionally how many times a plugin has been downloaded. The download counter is off by default and stays off unless the author turns it on, either per plugin or once for a whole store, so nobody has a number put next to their work without choosing to.
Preview image urls are restricted to GitHub hosts. The store draws a card for every plugin of every listed store, so an unrestricted host would let any listed author log the IP of everyone who opens the tab.
Notes for review
The store metrics come from a nightly action that turns
stores.jsonintoregistry.json, so the client makes one raw fetch rather than hitting the GitHub API per store. Metrics older than seven days are dropped rather than shown stale.One fix rode along:
PluginStoreTab/InstallFromUrl.tsxwas a near duplicate of the one inStorage.tsxmissing the.cssbranch its own label promised, so it is gone and the shared one is used.Bumps the version to 1.17.1-beta, on top of #195's 1.17.0-beta, so the merge produces its own release build.
Checked against a running client throughout rather than only built. Scrolling the store with 80 backdrop-filter layers holds a 6.1ms median frame.