soulseek, extracted into plugins/soulseek — the platform half
Seventh extraction, and the first of something that was already dead. Soulseek had been switched off since 2026-08-13: hono.ts carried the router import and its PROTECTED_MOUNTS entry commented out, so createSidecarProxy never ran and registerProxiedPrefix was never called; officer-slskd was not in the ecosystem at all. Because the permission entry had been reduced to api: [], the totality check was satisfied and the server booted clean — the UI rendered and every request 404'd. Worth naming: the boot check proves every MOUNTED prefix has a permission, and is silent about a feature that mounts nothing. Also closes a live data hazard: schema.ts had the soulseek export commented out, so the three tables were absent from the drizzle barrel and bun db:push would have dropped soulseek_favorites, soulseek_browse_snapshots and soulseek_browse_dirs on any server holding them. rebuild-soulseek-tree.ts documented the workaround rather than the problem; deleted with the rest. The connection stays in core service_connections under service 'slskd' while the mount is now /api/soulseek. Those differ on purpose. Test fixtures: slskd was canonical in three app-store tests, the third extraction running to break them. installer.test.ts now builds a literal fixture instead of reaching into the catalogue — it pins planSteps, which is installer logic, and no catalogue entry is both provisioned and members:'none' any more. catalogue.test.ts and members.test.ts re-anchored to vnc/notify/gitea. Unrelated but mine: check:theme caught 5 palette classes I introduced in CommandHQ earlier tonight against a baseline of 0. Converted to bg-success / bg-warning. check:theme is not part of bun test, which is why the green suite never showed it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -80,8 +80,6 @@ export function App() {
|
||||
<Route path="/files/*" element={<Dashboard.FilesScreen />} />
|
||||
<Route path="/calendar" element={<Dashboard.CalendarScreen />} />
|
||||
<Route path="/contacts" element={<Dashboard.ContactsScreen />} />
|
||||
<Route path="/soulseek" element={<Dashboard.SoulseekScreen />} />
|
||||
<Route path="/soulseek/:section" element={<Dashboard.SoulseekScreen />} />
|
||||
<Route path="/command-hq" element={<Dashboard.CommandHqScreen />} />
|
||||
<Route path="/command-hq/:sessionId" element={<Dashboard.CommandHqScreen />} />
|
||||
<Route path="/app-store" element={<Dashboard.AppStoreScreen />} />
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Navigate, useParams } from 'react-router';
|
||||
import type { LayoutNode } from 'officerdev';
|
||||
import { WorkspaceView, DEFAULT_SOULSEEK_SECTION, soulseekSectionPath, isSoulseekSection } from 'officerdev';
|
||||
import { useDashboardState } from 'state/useDashboardState';
|
||||
import { defaultLayout } from './defaultLayout';
|
||||
|
||||
// /soulseek uses the Workspace/Panel system (like /command-hq): a section nav (soulseek-nav) on the left and
|
||||
// a section view (soulseek-view) on the right, both reading `:section` from the URL. Deeper selection —
|
||||
// which search, which peer — is the query string. Both talk to slskd through the /api/slskd auth proxy.
|
||||
|
||||
export const SoulseekScreen = () => {
|
||||
const { section } = useParams();
|
||||
// v2: nav + view (replaced the earlier search + transfers split) — new key so the old persisted
|
||||
// layout doesn't resurrect as two mismatched panels.
|
||||
const workspace = useDashboardState<LayoutNode>('screens/soulseek-v2', defaultLayout);
|
||||
|
||||
// After the hooks — it returns early.
|
||||
if (!isSoulseekSection(section)) return <Navigate to={soulseekSectionPath(DEFAULT_SOULSEEK_SECTION)} replace />;
|
||||
|
||||
return (
|
||||
<div className="h-full w-full pt-2">
|
||||
<WorkspaceView
|
||||
workspace={workspace}
|
||||
locked
|
||||
appTypes={{ allowed: ['soulseek-nav', 'soulseek-view'], fallback: 'soulseek-view' }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
import type { LayoutNode } from 'officerdev';
|
||||
|
||||
export const defaultLayout: LayoutNode = {
|
||||
type: 'group',
|
||||
id: 'soulseek-root',
|
||||
direction: 'horizontal',
|
||||
children: [
|
||||
{ node: { type: 'panel', id: 'soulseek-nav', appType: 'soulseek-nav' }, size: 22 },
|
||||
{ node: { type: 'panel', id: 'soulseek-view', appType: 'soulseek-view' }, size: 78 },
|
||||
],
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export * from './SoulseekScreen';
|
||||
@@ -13,7 +13,6 @@ export * from './Tasks';
|
||||
export * from './Files';
|
||||
export * from './Calendar';
|
||||
export * from './Contacts';
|
||||
export * from './Soulseek';
|
||||
export * from './CommandHQ';
|
||||
export * from './Jellyfin';
|
||||
export * from './Gitea';
|
||||
|
||||
@@ -24,7 +24,6 @@ const RULES: TitleRule[] = [
|
||||
{ match: (p) => p.startsWith('/marketplace'), title: 'Marketplace' },
|
||||
{ match: (p) => p.startsWith('/plugins'), title: 'Marketplace' },
|
||||
{ match: (p) => p.startsWith('/jellyfin'), title: 'Video' },
|
||||
{ match: (p) => p.startsWith('/soulseek'), title: 'Soulseek' },
|
||||
{ match: (p) => p.startsWith('/command-hq'), title: 'Command HQ' },
|
||||
{ match: (p) => p.startsWith('/gitea'), title: 'Gitea' },
|
||||
{ match: (p) => p.startsWith('/invoices'), title: 'Invoices' },
|
||||
|
||||
Reference in New Issue
Block a user