a plugin owns the routing under its mount

Reverts the manifest route table. Declaring a plugin's URLs to the platform put
the platform in the middle of a decision that is not its own, and it was more
code than what it replaced.

One splat where there were two routes. A splat matches zero segments, so the
bare route still works, and the plugin reads its own position off the location.
The platform owns where a plugin starts and nothing after it — the same line
mountPrefix already draws around its API.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-01 07:17:33 +01:00
co-authored by Claude Opus 5
parent 1fe767ea77
commit 898209725a
5 changed files with 24 additions and 201 deletions
+1 -29
View File
@@ -96,25 +96,8 @@ export function generatePluginsModule(states: PluginState[]): string {
// would accept the other's panels.
const screenId = screen.segment ? `${plugin.appName}/${screen.segment}` : plugin.appName;
const route = screen.segment ? `/${screen.segment}` : mountPrefix(plugin);
/*
The screen's route table, each entry carrying the layout it renders.
A route whose layout is the screen's own reuses `${alias}Layout` rather than importing the same
module twice — the specifier would be identical, and two imports of one file under two names is
two objects where the layout key assumes one.
*/
const routeEntries = screen.routes.map((route_, index) => {
let layoutRef = `${alias}Layout`;
if (route_.layout !== screen.layout) {
layoutRef = `${alias}Route${index}Layout`;
imports.push(`import { defaultLayout as ${layoutRef} } from '${specifier(route_.layout)}';`);
}
return `{ path: ${JSON.stringify(route_.path)}, layout: ${layoutRef} }`;
});
entries.push(
` { appName: '${plugin.appName}', screenId: '${screenId}', route: '${route}', panels: ${alias}Panels, layout: ${alias}Layout, routes: [${routeEntries.join(', ')}] },`,
` { appName: '${plugin.appName}', screenId: '${screenId}', route: '${route}', panels: ${alias}Panels, layout: ${alias}Layout },`,
);
}
}
@@ -150,17 +133,6 @@ export type GeneratedPlugin = {
panels: AppRegistryMeta[];
/** How they are arranged. The shell renders \`WorkspaceView\` around them. */
layout: LayoutNode;
/**
* The screen's own route table, when it declared one.
*
* Empty means the screen takes the pair every plugin has always had — \`/<route>\` and
* \`/<route>/:section\`. A table replaces that pair entirely: the plugin has said what its URLs are,
* so a guessed one alongside them would be a route nothing points at.
*
* \`path\` is react-router's, relative to \`route\`. It is also the LAYOUT KEY suffix, so two routes
* with different arrangements remember their own dividers.
*/
routes: { path: string; layout: LayoutNode }[];
};
export const plugins: GeneratedPlugin[] = [