Files
music/web/useLyricsOpen.ts
T
Claude Opus 5 6e07da7a36 music, extracted from the platform into its own repository
Everything the plugin is, moved out of officerdev/platform on 2026-08-15 —
41 files, unchanged from the tree they left.

  manifest.ts   identity, one permission, ffmpeg/ffprobe declared
  api/          the sidecar proxy; the prefix comes from mountPrefix()
  sidecar/      the whole /api/music contract — indexing, streaming, per-user state
  db/           music_favorites, _playlists, _playlist_items, _now_playing
  web/          panels, layout, and the player: engine, bar, lyrics, favourites
  cliamp/       the second playback path, parked — not working, kept deliberately
  widgets/      the dashboard widget, parked — plugins cannot contribute widgets
  assets/       icon.png, the dock tile
  scripts/      the reindex CLI

PLUGIN.md is the design record: what moved, what stayed, what broke, and why.
MUSIC_API.md is the contract the phone and tablet apps speak, and the reason
the sidecar's HTTP shape is not free to change.

── It does not build here, and that is the point ──

The platform resolves `hooks/useClient`, `officerdev`, `officerdb/db` and `@@/*`
through the workspace links in its own node_modules. Measured from this
directory, outside the platform checkout, every one of them fails to resolve —
7 imports in the backend, ~29 in the frontend.

So this repository is the source of truth, not yet a buildable unit. Making it
one means the host API becoming something a plugin can depend on rather than
something it reaches into. That is the next problem, and having the code here
is what makes it unavoidable rather than theoretical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 17:34:51 +00:00

26 lines
1.0 KiB
TypeScript

import { usePanelChannel } from 'hooks/usePanelChannel';
export const MUSIC_LYRICS_CHANNEL = 'music:lyrics';
const STORAGE_KEY = 'music.lyrics';
// Read once, at import: the play dock re-renders every animation frame, and this is its initial value.
const initialOpen = localStorage.getItem(STORAGE_KEY) === '1';
/**
* Whether the lyrics panel is open. Shared by the two microphone buttons — the one in the play dock and
* the one on the album header — which are the same switch shown twice, so it lives in a channel rather
* than in either component. Seeded from localStorage so the choice survives a reload.
*/
export const useLyricsOpen = () => {
const [open, setOpen] = usePanelChannel<boolean>(MUSIC_LYRICS_CHANNEL, initialOpen);
// Deliberately not a functional update: useGlobal applies those to the render-time snapshot, and `open`
// is that snapshot anyway.
const toggleLyrics = () => {
localStorage.setItem(STORAGE_KEY, open ? '0' : '1');
setOpen(!open);
};
return [open, toggleLyrics] as const;
};