From 30fcab2bd3151e9a73939ff7f91a66f4e1f86401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Fri, 7 Aug 2026 11:07:36 +0000 Subject: [PATCH] mark 5.6 resolved, and stop three docs claiming a channel that has no publisher MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The todo entry said the file-viewer registration was dead; tracing it confirmed that and turned up the reason it looked alive — the ephemeral file viewer is a different mounting path entirely. Recorded, with what was checked in the database before deleting anything. CLAUDE.md, navigation-audit.md and workspace-panels.md all listed FILE_VIEWER_CHANNEL among the legitimate refresh/signal channels. It never had a publisher, and no longer exists. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 2 +- docs/navigation-audit.md | 4 +++- docs/workspace-panel-todo.md | 25 +++++++++++++++++++++++-- docs/workspace-panels.md | 7 ++++--- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3a2e8fa1..b857e9be 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -254,7 +254,7 @@ link-focusable). Half the app still does this; none of the new code should. - **Addressable state goes in the URL** (`useParams` / `useSearchParams`), never in a channel. `usePanelChannel` is for genuine signals and refresh buses (`preview:refresh`, - `files:refresh-signal`, `SLSKD_REFRESH_CHANNEL`, `MUSIC_RESYNC_CHANNEL`, `FILE_VIEWER_CHANNEL`). + `files:refresh-signal`, `SLSKD_REFRESH_CHANNEL`, `MUSIC_RESYNC_CHANNEL`). "Which thing is open" is a URL. Panels each read the URL rather than passing it between themselves. - **Rows and nav items are real links.** `` for rows (exemplar: the `/chat` session list, `f35c145`); **react-router's ``** for nav chrome, so active state comes from the router. diff --git a/docs/navigation-audit.md b/docs/navigation-audit.md index 51e15b47..b43b969b 100644 --- a/docs/navigation-audit.md +++ b/docs/navigation-audit.md @@ -124,7 +124,9 @@ This is the primary surface to convert to URL-driven selection. | local `useState` selections | search / room / conversation / peer / open file / preview slug | respective nested routes | `SearchView`, `SoulseekRooms`, `SoulseekChat`, `SoulseekUsers`, `CodeEditor/useEditorState`, `PreviewProvider` | **Excluded — event-bus / refresh signals, NOT selection:** `preview:refresh`, `files:refresh-signal`, -`SLSKD_REFRESH_CHANNEL`, `MUSIC_RESYNC_CHANNEL`, `FILE_VIEWER_CHANNEL`, `chat:active-session` (embedded-chat signal). +`SLSKD_REFRESH_CHANNEL`, `MUSIC_RESYNC_CHANNEL`, `chat:active-session` (embedded-chat signal). +(`FILE_VIEWER_CHANNEL` was listed here too; it had no publisher and has been deleted — the file viewer +reads `?view=` from the URL.) --- diff --git a/docs/workspace-panel-todo.md b/docs/workspace-panel-todo.md index 1c29cd21..ff8cc4ae 100644 --- a/docs/workspace-panel-todo.md +++ b/docs/workspace-panel-todo.md @@ -536,13 +536,34 @@ work disagree permanently about the roster, with neither told — a direct contr providing `localStorage`, and `MusicPlayer/useLyricsOpen.ts` reads it at *import* time, so the whole app graph was unimportable from a test. That is now fixed, which unblocks testing anything else that pulls in a panel app. -- [ ] **Seeding depends on undocumented mount ordering.** Three call sites call `useAppRegistry()` with +- [x] **Seeding depends on undocumented mount ordering.** Three call sites call `useAppRegistry()` with no argument, defaulting to `[]`. It works only because `` sits at `frontend.tsx:32`, above `` at `:34`. Mount a `WorkspaceView` above that and every panel renders empty. -- [ ] **`officerdev/file-viewer` is a dead registration.** Its provider reads + **Resolved `9fcc9c2`** — and the mechanism is worse than "ordering", which is why the fix is not + "move the component up". `` seeded through `useGlobal`'s `initialData`, and + **`initialData` is not a write**: it applies only to whichever component reads the slot first. A + `WorkspaceView` that rendered first would create the slot as `{}` and the seeder had no second + chance — every panel on that screen an empty box, for a reason visible nowhere near it. + Both registries are now written into the query cache by `seedAppRegistry` / `seedWidgetRegistry` + before `createRoot().render()`, so there is no ordering to depend on. They take the `QueryClient` + rather than running as a module-scope side effect because the app list imports every panel app and + every panel app imports the Workspace framework; keeping the call in `frontend.tsx` — the one + module that is nobody's dependency — is what stops that being an import cycle. (Making + `useAppRegistry` simply default to the static list was the obvious fix and is exactly that cycle.) + `registerApp` and `registerWidget` are deleted with the components: nothing ever called either, and + a registry that can be added to at runtime is a registry whose contents depend on what has mounted. +- [x] **`officerdev/file-viewer` is a dead registration.** Its provider reads `usePanelChannel('file-viewer:' + panelId)` and **nothing in the repo writes that channel**. Also `availableOnPanel: false`, so it can't be picked. If it ever appeared in a layout it would say "No file selected" forever. + **Resolved `9fcc9c2`** — traced and confirmed dead, then removed rather than repaired. The file + viewer that users actually see is mounted by `useFileViewerPanels` as an *ephemeral* panel, which + supplies `FileViewerBody`/`FileViewerHeader` itself with a provider reading the path from + `?view=`/`?ephemeral=` — it never touched the registry. No stored layout referenced the key + (checked across `dashboards`, `screens`, `dashboard_defaults`, `user_state`, `user_settings`: zero + rows), so the meta and `FileViewerPanelWrapper.tsx` are gone. Side effect worth knowing: no app in + the registry now uses the `provider` field. It is kept, because the ephemeral path + (`PanelComponentEntry.provider`) still does and `PanelSlot` resolves the two symmetrically. - [ ] **`dashboard-list` is pickable in every panel on every dashboard.** `apps/Dashboards/index.ts:12-16` omits `availableOnPanel` while its sibling `dashboard-preview` sets `false`. Almost certainly unintended — the flag is opt-_out_, and 12 of 44 metas omit it. Consider making it opt-_in_. diff --git a/docs/workspace-panels.md b/docs/workspace-panels.md index bb0382d7..04e421c0 100644 --- a/docs/workspace-panels.md +++ b/docs/workspace-panels.md @@ -131,9 +131,10 @@ library location, so no album is linkable), `soulseek:section` and `soulseek:use workspace has no URL), `chat:selected-session`, `monitor:scope`. Two are view toggles where a channel is genuinely fine (`music:favorites`, `music:lyrics`). -Three are wired to nobody: `preview:refresh` and `chat:active-session` have publishers and no -subscribers; `file-viewer:` has a subscriber and no publisher, which is why the registry-mounted -`officerdev/file-viewer` app renders "No file selected" forever. +Two are wired to nobody: `preview:refresh` and `chat:active-session` have publishers and no subscribers. +A third, `file-viewer:`, had a subscriber and no publisher — it is gone, along with the +`officerdev/file-viewer` registration it fed, which would have rendered "No file selected" forever. The +file viewer users actually see is an *ephemeral* panel from `useFileViewerPanels` and reads the URL. Four channel names are **bare string literals with no shared constant**, repeated across up to four files. A typo does not error — you silently get a fresh channel stuck on its initial value. And because