Skip to content

feat(ui): fluid skin — the validated editorial direction as a sixth skin - #920

Closed
InstaZDLL wants to merge 10 commits into
mainfrom
feat/fluid-editorial-skin
Closed

InstaZDLL wants to merge 10 commits into
mainfrom
feat/fluid-editorial-skin

Conversation

@InstaZDLL

@InstaZDLL InstaZDLL commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • The Fluid Editorial direction, reviewed and approved on feat(home): fluid editorial home prototype behind a dev-only flag #905 as a dev-only prototype, becomes a real sixth skin, fluid, alongside Studio, Editorial, Lounge, Pulse and Liquid — opt-in from Settings → Appearance, dark-only like Lounge and Pulse, working with all 14 themes and the three accent modes of feat(ui): custom accent colour over any theme, or the playing track's cover #895 (the skin reads --accent-400).
  • Why a skin and not a replacement: the app's skin system is exactly the lever for "a different language on the same features", and it keeps every existing identity intact. The skin restyles every view by landmark (sidebar, header, footer, main surfaces, inputs, dialogs, track table) and recomposes the Home: HomeView is now a switch that renders FluidHome under Fluid (listening canvas around the current or last-played track, Mood Radio as a tempo spectrum, an editorial mosaic for the mixes with the all-time top album featured, a shelf for recent additions, a two-column history), all on real data and real actions.
  • Two production components branch on the skin and keep every control: MoodRadioGrid renders MoodSpectrum (same start_mood_radio, keyboard radiogroup), PlayerBar mounts ProgressBar variant="edge" as its top edge (same slider, same keyboard contract, three times the seek travel).
  • The backend's skin-recovery whitelist (skin_recovery.rs) accepts the new id — it refused it with "unknown skin", which made setSkinId fail silently. One line of Rust, formatted.
  • 17 locales carry the skin's label/description and the Home strings (home.fluid.*, plural forms per locale). Docs: ui.md skins table + architecture notes, CLAUDE.md, README.
  • The prototype (src/prototype, its fixtures script, the Vite middleware, the ?proto= branch) is removed: this branch continues feat(home): fluid editorial home prototype behind a dev-only flag #905 and the diff against main no longer contains it.

How I tested

  • bun run tauri dev with the WebView2 debug port, switched the running app to Fluid through CDP (the provider's setSkinId), and captured: Home at the top and scrolled (canvas, spectrum, mosaic, shelf, history), Library → Albums and Tracks, Settings → Appearance (the picker shows Fluid selected, cards and inputs take the tonal treatment, the title's accent bar becomes the flow line).
  • Checked live: the queue after the cursor appears as "up next" and jumps on click, the last-played strip starts the play, the mood spectrum starts a radio, the cover's glow lands on the root (atmosphere pools + player bar gradient), the edge progress seeks by pointer and keyboard.
  • bun run typecheck, bun run lint, rustfmt --check on the one Rust file; the Rust change compiled through tauri dev's rebuild. cargo check was not run separately (it would fight the running dev build for the target directory) — CI covers it.
  • Local CodeRabbit pass on the committed diff: three findings, all applied (glow written on the root, history row keyboard handling, docs count).

Screenshots / clips

Local captures in prototypes/fluid-editorial/screenshots/app-*.png (the user's own covers, not committed).

Checklist

  • Title uses Conventional Commits (type(scope): subject, kebab-case scope)
  • bun run lint + bun run typecheck pass locally
  • cargo fmt --check on the touched file; cargo check left to CI (see above)
  • UI strings changed: every locale in src/i18n/locales/ updated (fr + 16)
  • Cross-cutting pattern changed: CLAUDE.md and docs/features/ui.md updated
  • Breaking change? None — Studio stays the default; Fluid is opt-in

Linked issues

Refs #905 (the prototype this supersedes).

Summary by CodeRabbit

  • Nouvelles fonctionnalités
    • Ajout du skin Fluid, portant à six le nombre de skins disponibles. Il propose une nouvelle identité visuelle et une page d’accueil personnalisée avec écoutes récentes, albums récemment ajoutés et playlists recommandées.
    • Avec Fluid, la barre de lecture affiche une progression en forme d’onde. La page d’accueil propose également un spectre d’ambiances pour lancer une radio musicale.
  • Documentation
    • Mise à jour du nombre de skins indiqué dans la documentation ; les 14 thèmes restent inchangés.

…flag

An isolated exploration of a new visual direction for the Home page,
mounted instead of the app when the URL carries ?proto=fluid-editorial
in development builds only. The production shell, HomeView and every
provider are untouched; vite build drops the branch and its chunk.

- Listening Canvas replaces the greeting banner and the four stat cards
- Mood Radio becomes a continuous tempo spectrum with keyboard support
- editorial mosaic, shelf and journal replace the uniform card grids
- compact player bar with a wave progress indicator, quieter sidebar
- accent and atmosphere derived from the playing artwork
- fixtures generated from a real profile by scripts/fluid-proto-fixtures.ts;
  album art stays in the gitignored prototypes/ folder, served by a
  serve-only Vite middleware
- the mood segment no longer nests a control inside the radio button:
  the start button is a sibling drawn over the reserved footprint, and
  arrow keys walk the moods that have tracks only
- a cover tile forgets a load failure when its source changes
- a late colour sample cannot land on a track that has no cover
- the fixtures generator runs one resize + palette job per cover, shared
  by the album lists that overlap
- the dev middleware is confined to prototypes/fluid-editorial with a
  separator-aware prefix check
- the dev middleware forwards a stream error to Vite instead of hanging
- the sidebar toggle announces its state with aria-expanded
- a mosaic cell whose image fails falls back to the palette gradient
- the fixtures generator refuses a profile with no play history rather
  than writing a JSON the page cannot render from
- drop the ts-expect-error on process.env: the node imports added to
  vite.config.ts brought the Node globals, so the directive was unused
…type

- the fixtures generator probes its copy of the database and copies
  again when a checkpoint landed mid-copy; the README says so
- plays without an album are dropped by the generator too, and the
  typed loader fails loudly instead of leaving recentPlays[0] undefined
- reaching the end of a track advances the queue instead of looping;
  the refs behind play/next are written synchronously so two calls in
  one tick do not read the same state
- reduced motion also stops button transitions and the hover/press lifts
- the covers path in the fixtures comment matches the generator
Review of the first iteration asked for better use of a wide screen, a
cover that lights the canvas rather than sitting apart from it, larger
secondary type, a reason for the oversized tile and a player bar that
speaks the same language.

- container widened to 1680 px with wider gutters from 2xl
- a blurred, masked copy of the current cover glows behind the title
  and the queue, cross-fading on track change
- secondary sizes up one notch, tertiary ink lighter, labels heavier
- the lead "recently added" tile carries "latest addition · when"
- the progress wave becomes the player bar's top edge, full width,
  elapsed and total at its ends; the bar stays at 88 px
The Fluid Editorial prototype, reviewed and approved as a direction, now
lives in the app as a skin alongside Studio, Editorial, Lounge, Pulse
and Liquid, on real data, with every control kept.

- skins.ts: the `fluid` preset (DM Sans Variable, pill actions, tonal
  surfaces); app.css lists it with the dark-only skins; index.html
  whitelists it for the first paint; the backend's skin-recovery
  whitelist accepts it (it refused the id with "unknown skin")
- styles/skins/fluid.css: tokens, the atmosphere (two pools of the
  cover's colour + grain), chrome by landmark (quiet sidebar with an
  accent bar for the active row, flat header, borderless footer with the
  cover's light), tonal cards and inputs for every view, the flow line
  under section titles, high-contrast and reduced-motion blocks
- HomeView becomes a switch: under Fluid it renders FluidHome, which
  loads recent plays, albums by added_at, the all-time top album, the
  listening total and the queue after the cursor; the listening canvas,
  the mood spectrum, the editorial mosaic, the shelf and the history
  list are wired to playback and navigation
- MoodRadioGrid renders MoodSpectrum under Fluid (same counts, same
  start_mood_radio, keyboard radiogroup over the moods with tracks)
- ProgressBar gains an `edge` variant (full-width wave with the times at
  its ends, same slider and keyboard contract); PlayerBar mounts it as
  its top edge under Fluid and keeps only the transport in the centre
- useCoverGlow samples the cover's average through dominantColor() and
  writes --fe-glow; the accent stays --accent-400, so the theme, custom
  and cover accent modes drive it
- 17 locales: the skin's label and description, and the Home's strings
  (home.fluid.*, plural forms per locale)
- docs: ui.md skins table and architecture notes, CLAUDE.md, README

The dev-only prototype (src/prototype, the fixtures script, the Vite
middleware and the ?proto= branch in main.tsx) is removed: the skin
supersedes it.
@coderabbitai

coderabbitai Bot commented Oct 19, 2026

Copy link
Copy Markdown

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Configuration used: Repository: InstaZDLL/WaveFlow/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: a9ac064c-1f64-4198-b9a5-2d08eba9a274
📥 Commits

Reviewing files that changed from the base of the PR and between 682a29d and 65d2807.

⛔ Files ignored due to path filters: 17
  • src/i18n/locales/ar.json is excluded by !src/i18n/locales/**
  • src/i18n/locales/de.json is excluded by !src/i18n/locales/**
  • src/i18n/locales/en.json is excluded by !src/i18n/locales/**
  • src/i18n/locales/es.json is excluded by !src/i18n/locales/**
  • src/i18n/locales/fr.json is excluded by !src/i18n/locales/**
  • src/i18n/locales/hi.json is excluded by !src/i18n/locales/**
  • src/i18n/locales/id.json is excluded by !src/i18n/locales/**
  • src/i18n/locales/it.json is excluded by !src/i18n/locales/**
  • src/i18n/locales/ja.json is excluded by !src/i18n/locales/**
  • src/i18n/locales/ko.json is excluded by !src/i18n/locales/**
  • src/i18n/locales/nl.json is excluded by !src/i18n/locales/**
  • src/i18n/locales/pt-BR.json is excluded by !src/i18n/locales/**
  • src/i18n/locales/pt.json is excluded by !src/i18n/locales/**
  • src/i18n/locales/ru.json is excluded by !src/i18n/locales/**
  • src/i18n/locales/tr.json is excluded by !src/i18n/locales/**
  • src/i18n/locales/zh-CN.json is excluded by !src/i18n/locales/**
  • src/i18n/locales/zh-TW.json is excluded by !src/i18n/locales/**
📒 Files selected for processing: 22
  • CLAUDE.md
  • README.md
  • docs/features/ui.md
  • index.html
  • src-tauri/crates/app/src/skin_recovery.rs
  • src/app.css
  • src/components/player/PlayerBar.tsx
  • src/components/player/ProgressBar.tsx
  • src/components/views/HomeView.tsx
  • src/components/views/home/MoodRadioGrid.tsx
  • src/components/views/home/MoodSpectrum.tsx
  • src/components/views/home/fluid/FluidHome.tsx
  • src/components/views/home/fluid/ForYouMosaic.tsx
  • src/components/views/home/fluid/ListeningCanvas.tsx
  • src/components/views/home/fluid/ListeningHistory.tsx
  • src/components/views/home/fluid/RecentlyAdded.tsx
  • src/components/views/home/fluid/SectionHeader.tsx
  • src/components/views/home/fluid/format.ts
  • src/components/views/home/fluid/useCoverGlow.ts
  • src/components/views/home/recentPlayToTrack.ts
  • src/lib/skins.ts
  • src/styles/skins/fluid.css
+-----------------------------------------------------------------------+
| You could simplify this. Not because I said so - because reality did. |
+-----------------------------------------------------------------------+
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@InstaZDLL InstaZDLL self-assigned this Oct 19, 2026
@InstaZDLL InstaZDLL added type: feat New feature scope: frontend React/Vite frontend (src/) labels Oct 19, 2026
@InstaZDLL

Copy link
Copy Markdown
Owner Author

Closed for now: the branch continues with the view-by-view polish. The final PR opens once the work is complete and validated, so the bot reviews a finished diff rather than every iteration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: frontend React/Vite frontend (src/) type: feat New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant