Files
yellowjacket/frontend/src/utils/icon-language.ts
T
logan b5d70ac1cd
CI / check (push) Skipped
CI / e2e (push) Skipped
CI / check (pull_request) Successful in 2m49s
CI / e2e (pull_request) Successful in 6m12s
feat(explore): offer the autotag match on the album page
The complaint was having to notice the metadata was missing, then go
and hunt the album down on the Autotag page. The album page now says it
while you are looking at the thing: "MusicBrainz has a match for this
album: <release> by <artist>", with Apply tags and Review in Autotag.

Four things about it are load-bearing.

**Applying is offered only where it would do the whole album.** A
tagging group is a folder, so a multi-disc album is several, and one
button that applied to the best-scoring group would leave the album
holding a mix of old and new tags — the exact case the app's Blocking
notification level exists for. `groupCount` is the test, and the answer
there is review rather than apply.

**It rewrites files, so it asks.** `confirmAction()` with an impact
line that says it cannot be undone and that nothing is moved or
deleted, because "rewrites your files" reads worse than it is. The
apply goes through `ApplyAsync`, the registered-job path, so progress
belongs to the jobs indicator and this page does not grow a second one
— what it owes the user is the acknowledgement, because the button is
here. The suggestion clears itself on success rather than inviting a
second click while the job runs.

**The banner does not quote a percentage.** The backend has a score and
deliberately keeps it out of the sentence: 0.95 reads as a probability
and is not one. Which release it is, is the part a person can judge.

**"Review in Autotag" lands on that album.** The queue is sorted by
score so the intended folder is often near the top, and "often" is a
link that sometimes opens a different album. Autotag is a cached
primary view, so there is no construction to hand a payload to: the
request goes on as an attribute and the view *consumes* it, or every
later visit would reopen a folder the user finished with long ago.

`ICON_AUTOTAG` joins the vocabulary at the same time, on the rule
`ICON_PLAYLIST` was chosen by — an icon names the noun it acts on, so a
suggestion pointing at Autotag wears the Autotag destination's own
mark. It was written inline in the sidebar; two call sites is where a
name stops being one component's detail, so the sweep governs it now.

Verified against the running app with a staged match: the banner, the
confirm dialog's wording, and the navigation landing on the right
folder with the attribute consumed.

Closes #28
2026-08-19 02:34:11 -04:00

112 lines
4.0 KiB
TypeScript

/**
* What each icon in this app means, once.
*
* The set was a mix: `plus` meant "add to the queue", "add to a
* playlist", "make a new playlist" and "you do not own this" — the
* first two *adjacent in the same context menu* — while `list` meant
* the queue, the Playlists destination, and (in `queue-panel` alone)
* adding to the queue. Two icons carrying seven meanings between them
* is not a vocabulary, and a user cannot learn one that says four
* things.
*
* The rule these are chosen by: **an icon names the noun it acts on,
* not the verb.** "Add to queue" and "add to playlist" are the same
* verb on different nouns, so the noun is what has to differ — which is
* also why adding to a playlist wears the Playlists destination's own
* icon rather than a generic plus. `plus` survives for exactly the one
* thing it is unambiguous about, making something that did not exist.
*
* Import these rather than writing a name inline. A literal string is
* how the last set drifted, and nothing catches it: a wrong-but-real
* icon renders perfectly.
*/
/** Start playing this now. */
export const ICON_PLAY = 'play';
/** Start playing this now, in a shuffled order. */
export const ICON_SHUFFLE = 'shuffle';
/**
* The queue, and putting something into it.
*
* One glyph for the noun and the action, so the button that opens the
* queue and the menu item that adds to it are visibly the same subject.
* The queue used to wear `list`, which is the Playlists destination.
*/
export const ICON_QUEUE = 'bars-staggered';
/** Put this next in the queue rather than at the end. */
export const ICON_PLAY_NEXT = 'forward-step';
/**
* A playlist, and adding something to one.
*
* The same icon as the Playlists destination in the sidebar, which is
* the point: the menu item says where the thing is going.
*/
export const ICON_PLAYLIST = 'list';
/**
* Make a new thing that did not exist — a playlist, a rule, a library.
*
* This is the only meaning `plus` keeps. It used to carry four.
*/
export const ICON_NEW = 'plus';
/**
* The request ("want") toggle, as an outline/solid pair.
*
* Two states of one control have to read as each other's opposite,
* which a plus and a bookmark do not. The pair was already in the app
* and already correct — `explore-album-details`'s "Want this" button
* has used it since it was written, and `favorites-controller` uses the
* same shape for `regular/heart` → `heart` — while the badge forty
* pixels away showed a plus for the same state.
*
* That is `utils/library-status.ts`'s fault one layer down: it made the
* two surfaces agree on *what wanting means* and left them disagreeing
* on what it looks like.
*/
export const ICON_CAN_REQUEST = 'regular/bookmark';
export const ICON_REQUESTED = 'solid/bookmark';
/**
* You have this.
*
* Deliberately not drawn on the common case — see the tracklist, where
* absence is what gets marked. This is for the places that answer the
* question directly, like the badge on a catalog card.
*/
export const ICON_IN_LIBRARY = 'check';
/**
* The autotagger, and a match it is offering.
*
* The same icon as the Autotag destination in the sidebar, on the rule
* `ICON_PLAYLIST` was chosen by: an icon names the noun it acts on, so
* a suggestion on the album page wears the mark of the page it would
* send you to. Governed from the moment there were two call sites,
* which is when a name stops being a detail of one component.
*/
export const ICON_AUTOTAG = 'tag';
/**
* Something is being fetched right now.
*
* Distinct from `ICON_REQUESTED`: a request may sit on the list
* forever without anything happening, which is exactly why the badge's
* "queued" state stopped being an hourglass.
*/
export const ICON_DOWNLOADING = 'download';
/**
* Take this away.
*
* One icon for removing from a playlist, from the queue and from the
* library, because the difference that matters is stated in the words
* beside it and in the confirmation — "Remove from Library" says in its
* impact line that the files are not deleted.
*/
export const ICON_REMOVE = 'trash';