From f1bd75853d94f2e6d28a2b8e241dfad42368cff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sat, 15 Aug 2026 14:33:44 +0000 Subject: [PATCH] the music widget moves into the plugin, parked like cliamp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit src/workspaces/widgets/MusicPlayer/ → plugins/music/widgets/, its export dropped from the widgets package, and its registration removed from WidgetRegistry. Plugins cannot contribute widgets and that mechanism is not being invented now, so it is parked next to cliamp rather than left in a workspace package the platform ships. `../Widget` became `widgets/Widget` — the sibling import turned into that package's declared export, which it already had. This removes the last non-plugin consumer of officerdev/src/MusicPlayer, and that matters more than the move. The argument for keeping the player in the platform was that the widget imported useMusicPlayer and PlayerTrack from officerdev, and the platform cannot import from a plugin — so the player STATE had to stay whatever was decided about the UI, and the engine stayed with the state. That constraint is now gone. The complete remaining platform dependency on the player is one line: DashboardLayout.tsx:66, ``. So the seam is no longer "the widget pins it". It is purely the global overlay question, which is a platform gap about plugins owning a render slot outside — the same shape as plugins owning a websocket. Recorded rather than acted on; the decision is the owner's and the previous reasoning for it no longer holds. bunx tsgo clean. 797 tests, 787 pass, same 7 pre-existing failures. --- .../music/widgets}/index.tsx | 33 +++++++++++++++---- .../src/WidgetRegistry/WidgetRegistry.tsx | 10 +----- src/workspaces/widgets/package.json | 3 +- 3 files changed, 29 insertions(+), 17 deletions(-) rename {src/workspaces/widgets/MusicPlayer => plugins/music/widgets}/index.tsx (90%) diff --git a/src/workspaces/widgets/MusicPlayer/index.tsx b/plugins/music/widgets/index.tsx similarity index 90% rename from src/workspaces/widgets/MusicPlayer/index.tsx rename to plugins/music/widgets/index.tsx index cb76fd77..13c35a67 100644 --- a/src/workspaces/widgets/MusicPlayer/index.tsx +++ b/plugins/music/widgets/index.tsx @@ -3,7 +3,7 @@ import { useMusicPlayer } from 'officerdev'; import { useState, useEffect } from 'react'; import { Music, ChevronLeft, Play, Folder } from 'lucide-react'; import { useClient } from 'hooks/useClient'; -import { Widget } from '../Widget'; +import { Widget } from 'widgets/Widget'; // Music Player widget — a BROWSER over the library. Top-level dirs of ~/Music are "libraries" (tabs); // within a library you drill through folders (Artist → Albums → …) until a folder has tracks (songs). @@ -36,7 +36,14 @@ export const MusicPlayer = () => { // Top-level libraries (once). useEffect(() => { get(`/file-browser/ls?path=${encodeURIComponent(MUSIC_ROOT)}`) - .then((r) => setLibraries(r.entries.filter((e) => e.type === 'directory').map((e) => e.name).sort())) + .then((r) => + setLibraries( + r.entries + .filter((e) => e.type === 'directory') + .map((e) => e.name) + .sort(), + ), + ) .catch(() => setLibraries([])); }, []); @@ -50,7 +57,12 @@ export const MusicPlayer = () => { get(`/file-browser/ls?path=${encodeURIComponent(cwd)}`) .then(async (r) => { if (cancelled) return; - setDirs(r.entries.filter((e) => e.type === 'directory').map((e) => e.name).sort()); + setDirs( + r.entries + .filter((e) => e.type === 'directory') + .map((e) => e.name) + .sort(), + ); const audio = r.entries.filter((e) => e.type === 'file' && isAudio(e.name)).map((e) => e.name); if (audio.length) { const rel = cwd.slice(MUSIC_ROOT.length + 1); // /<…> @@ -92,7 +104,12 @@ export const MusicPlayer = () => { }; const play = (i: number) => { - const queue: PlayerTrack[] = songs.map((t) => ({ albumRel: relToMusic, file: t.file, title: t.title, artist: t.artist })); + const queue: PlayerTrack[] = songs.map((t) => ({ + albumRel: relToMusic, + file: t.file, + title: t.title, + artist: t.artist, + })); player.playQueue(queue, i); }; const isCurrent = (file: string) => player.current?.albumRel === relToMusic && player.current?.file === file; @@ -107,7 +124,9 @@ export const MusicPlayer = () => { type="button" onClick={() => selectLibrary(lib)} className={`shrink-0 rounded-full px-2.5 py-1 text-xs ${ - library === lib ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground hover:text-foreground' + library === lib + ? 'bg-primary text-primary-foreground' + : 'bg-muted text-muted-foreground hover:text-foreground' }`} > {lib} @@ -144,7 +163,9 @@ export const MusicPlayer = () => { />
-

{breadcrumb[breadcrumb.length - 1] ?? library}

+

+ {breadcrumb[breadcrumb.length - 1] ?? library} +

{breadcrumb[breadcrumb.length - 2] ?? ''}