import { BrowserRouter, Routes, Route, Navigate, useLocation } from 'react-router';
import * as Authentication from './Screens/Authentication';
import * as Dashboard from './Screens/Dashboard';
import { useAuth } from 'hooks/useAuth';
import { useServerSettings } from 'state/useServerSettings';
import { useServerEnvironment } from 'state/useServerEnvironment';
import { useInitialData } from '@/state/useInitialData';
// `installedPlugins`, not `plugins`: App.tsx already destructures a `plugins` from useServerSettings(),
// which is the DEAD plugin system — /server-settings/plugins scans src/workspaces/plugins/, a directory
// that does not exist, so it is always []. Different thing entirely; see plugins/offscale/PLUGIN.md.
import { plugins as installedPlugins } from './Plugins.gen';
/**
* `/plugins` → `/marketplace`, KEEPING the query string.
*
* `` drops it, and that was not theoretical: every row in the plugin list
* linked to `/plugins?selected=`, so clicking one redirected to a bare `/marketplace` and the detail
* pane sat on "Select a plugin." The screen looked broken while both halves were working — the link set a
* param the redirect then threw away.
*
* Rows now point at `/marketplace` directly, so this only serves old links and bookmarks. It preserves
* the search anyway, because that is what made the redirect wrong in the first place.
*/
const PluginsRedirect = () => {
const { search } = useLocation();
return ;
};
export function App() {
const { isLoading, isAuthenticated } = useAuth();
const { plugins, isLoading: isServerSettingsLoading } = useServerSettings();
useServerEnvironment();
useInitialData();
if (isLoading || isServerSettingsLoading) return null;
return (
{!isAuthenticated && (
} />
} />
} />
} />
)}
{isAuthenticated && (
} />
{/* Route pairs, like /command-hq and /soulseek below: the bare page route exists so the
existing links into it keep working, and its `SettingsRoute` guard redirects to the
first section. The section is the URL rather than a `*_SELECTED` global, so a settings
sub-page is linkable, bookmarkable and cmd-clickable. */}
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
{/* A project group is a working directory, so it is spelled as a path, not ?cwd= — see
apps/ChatHistory/chat-routes.ts. The splat has to be last in a pattern, which is why
"new" comes before the group rather than after it. A session carries no group: the
transcript records its own cwd and the server resolves it by id. */}
} />
} />
} />
} />
} />
{/* Splat, because the folder is the URL now: `/files/Tests/test folder`. The bare `/files`
still matches with an empty splat and means home, so every existing link and the dock
entry keep working. Only DIRECTORIES live here — an open file stays `?view=`, since it is
what is on top of you rather than where you are. */}
} />
} />
} />
} />
} />
} />
} />
{/* The screen was `/plugins` until marketplace.officer.dev existed. Redirected rather than
dropped: the word appears in the dock, in docs, and in whatever anyone bookmarked, and a
404 on a route that worked yesterday reads as the feature having been removed. */}
} />
{/* Installed plugins. Core routes above stay hand-written; everything below is generated from
what is installed, because a bundler cannot follow a runtime import specifier. The wildcard
hands the whole subtree to the plugin's own router, which react-router nests natively. */}
{installedPlugins.flatMap((plugin) => [
} />,
// The section pair, exactly as the core screens do it (`/soulseek/:section`): the plugin's
// panels read `useParams` themselves, so which section is open is the URL rather than state
// passed between them.
}
/>,
])}
} />
} />
{/* TEMPORARY / EXPERIMENTAL — offline file transfer over animated QR codes */}
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
)}
);
}