the music widget moves into the plugin, parked like cliamp
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, `<MusicPlayerHost />`. 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 <Routes> — 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.
This commit is contained in:
@@ -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<LsResult>(`/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<LsResult>(`/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); // <library>/<…>
|
||||
@@ -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 = () => {
|
||||
/>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-semibold text-foreground">{breadcrumb[breadcrumb.length - 1] ?? library}</p>
|
||||
<p className="truncate text-sm font-semibold text-foreground">
|
||||
{breadcrumb[breadcrumb.length - 1] ?? library}
|
||||
</p>
|
||||
<p className="truncate text-xs text-muted-foreground">{breadcrumb[breadcrumb.length - 2] ?? ''}</p>
|
||||
</div>
|
||||
<button
|
||||
@@ -3,20 +3,12 @@ import { widgetRegistryMetas as weatherMetas } from 'widgets/Weather';
|
||||
import { widgetRegistryMetas as pomodoroMetas } from 'widgets/Pomodoro';
|
||||
import { widgetRegistryMetas as dailyGoalsMetas } from 'widgets/DailyGoals';
|
||||
import { widgetRegistryMetas as quickNotesMetas } from 'widgets/QuickNotes';
|
||||
import { widgetRegistryMetas as musicPlayerMetas } from 'widgets/MusicPlayer';
|
||||
import type { QueryClient } from '@tanstack/react-query';
|
||||
import { globalQueryKey } from 'hooks/useGlobal';
|
||||
import type { WidgetRegistry as WidgetRegistryMap } from './types';
|
||||
import type { WidgetRegistryMeta } from './useWidgetRegistry';
|
||||
|
||||
const widgets = [
|
||||
...clockMetas,
|
||||
...weatherMetas,
|
||||
...pomodoroMetas,
|
||||
...dailyGoalsMetas,
|
||||
...quickNotesMetas,
|
||||
...musicPlayerMetas,
|
||||
];
|
||||
const widgets = [...clockMetas, ...weatherMetas, ...pomodoroMetas, ...dailyGoalsMetas, ...quickNotesMetas];
|
||||
|
||||
const metasToRegistry = (metas: WidgetRegistryMeta[]): WidgetRegistryMap =>
|
||||
Object.fromEntries(metas.map(({ key, ...entry }) => [key, entry]));
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
"./Pomodoro": "./Pomodoro/index.tsx",
|
||||
"./DailyGoals": "./DailyGoals/index.tsx",
|
||||
"./QuickNotes": "./QuickNotes/index.tsx",
|
||||
"./Workspaces": "./Workspaces/index.tsx",
|
||||
"./MusicPlayer": "./MusicPlayer/index.tsx"
|
||||
"./Workspaces": "./Workspaces/index.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user