name: new-panel
description: Scaffold a new Workbench panel — full stack (server DTOs/service/controller/tests + client hooks/store/panel/context-menu/tests + wiring + Playwright canary) that compiles on first invocation
argument-hint:
new-panel — Scaffold a Workbench Panel
Generates every file needed for a new dockable panel in the Typhon Workbench. Output compiles on first invocation: dotnet build green, tsc --noEmit green, npm run lint green, vitest green, one Playwright canary green. The service body returns an empty list / default DTO so you can wire a UI immediately and fill in the engine-read code as a second pass.
Input
$ARGUMENTS should contain two PascalCase identifiers separated by whitespace:
ModuleName— the module this panel belongs to (e.g.,QueryConsole,SchemaInspector,DataBrowser). Case-sensitive, no spaces, no dots.PanelName— the specific panel within the module (e.g.,QueryEditor,ArchetypeBrowser). Case-sensitive, no spaces.
If either is missing, use AskUserQuestion to collect them.
If $ARGUMENTS contains --help or -h, display the block below and stop — do not execute the workflow.
/new-panel <ModuleName> <PanelName>
Scaffold a full-stack Workbench panel that compiles on first invocation.
Arguments:
ModuleName Module this panel belongs to (PascalCase, e.g. QueryConsole)
PanelName Panel name within the module (PascalCase, e.g. QueryEditor)
--help, -h Show this help
What it does:
- Creates/extends the module's service + controller + DTOs on the server
- Creates the panel component + context menu + hook on the client
- Wires dockview registration + menu + palette + open-helper
- Generates a vitest smoke test + a Playwright canary
- Verifies the whole stack compiles
Examples:
/new-panel QueryConsole QueryEditor
/new-panel SchemaInspector DefaultsDiff
Preflight
Before generating any file:
Parse args — validate both identifiers are PascalCase, no punctuation. Derive:
moduleSlug= lowerCamel of ModuleName (e.g.queryConsole)moduleKebab= kebab of ModuleName (e.g.query-console)panelSlug= lowerCamel of PanelName (e.g.queryEditor)panelKebab= kebab of PanelName (e.g.query-editor)
Detect module state — check if a server-side
<ModuleName>Service.csexists undertools/Typhon.Workbench/<ModuleName>/Service/(or wherever the first module was placed). If missing, this is a new-module scaffold — add the full server stack. If present, this is a new-panel-in-existing-module scaffold — only add the panel-level files and skip the server stack.Confirm target with the user via
AskUserQuestion:- "New module
<ModuleName>(generate server + client stack)" OR "Existing module, add panel<PanelName>only" - "Continue" / "Abort"
- "New module
Reference patterns
Do not embed long templates in this file. Read the existing implementations and adapt. The authoritative references:
| What | Reference |
|---|---|
| Server service method + error types | tools/Typhon.Workbench/Schema/SchemaService.cs |
Server controller + auth attributes + Invoke<T> error mapping |
tools/Typhon.Workbench/Controllers/SchemaController.cs |
| DTO records (camelCase JSON) | tools/Typhon.Workbench/Dtos/Schema/ComponentSummaryDto.cs |
Server tests + WorkbenchFactory.CreateAuthenticatedClient |
test/Typhon.Workbench.Tests/Schema/SchemaControllerTests.cs |
| Module-scoped zustand store + selection + touchedAt pattern | tools/Typhon.Workbench/ClientApp/src/stores/useSchemaInspectorStore.ts |
Normalized types + Orval-style normalizeX functions |
tools/Typhon.Workbench/ClientApp/src/hooks/schema/types.ts |
| Query hook via Orval generated function + normalizer | tools/Typhon.Workbench/ClientApp/src/hooks/schema/useComponentList.ts |
Query hook via raw customFetch (use this path for the scaffold) |
tools/Typhon.Workbench/ClientApp/src/hooks/schema/useArchetypeList.ts |
| Panel component + empty/loading/error states | tools/Typhon.Workbench/ClientApp/src/panels/SchemaInspector/SchemaIndexPanel.tsx |
| Panel with search + filter chips + sortable Table | tools/Typhon.Workbench/ClientApp/src/panels/SchemaBrowser/SchemaBrowserPanel.tsx |
| Co-located context menu | tools/Typhon.Workbench/ClientApp/src/panels/SchemaBrowser/SchemaBrowserContextMenu.tsx |
Module-level dock api registry + openX() helpers |
tools/Typhon.Workbench/ClientApp/src/shell/commands/openSchemaBrowser.ts |
| Dockview components-map registration | tools/Typhon.Workbench/ClientApp/src/shell/DockHost.tsx |
| Menu bar entry (possibly gated on selection) | tools/Typhon.Workbench/ClientApp/src/shell/MenuBar.tsx |
| Palette command registration | tools/Typhon.Workbench/ClientApp/src/shell/commands/baseCommands.ts |
| Vitest for a zustand store | tools/Typhon.Workbench/ClientApp/src/stores/__tests__/useSchemaInspectorStore.test.ts |
| Playwright canary — panel opens via menu | tools/Typhon.Workbench/ClientApp/e2e/schema-inspector.spec.ts |
When generating each file, Read the reference, adapt the patterns — don't blindly copy. The Workbench conventions shift and the scaffolder stays current by deferring to the live code.
Generation plan
Execute in this order so intermediate states are buildable:
New-module path (full stack)
Server DTOs (
tools/Typhon.Workbench/Dtos/<ModuleName>/)- One placeholder DTO record. Name it
<PanelName>ItemDto— the shape the list-endpoint returns an array of. Example fields:string Name, int Count— just enough to demonstrate camelCase JSON without pretending to model the future data shape.
- One placeholder DTO record. Name it
Server service (
tools/Typhon.Workbench/<ModuleName>/<ModuleName>Service.cs)- Single public method
List<PanelName>s(Guid sessionId)returning<PanelName>ItemDto[]. - Body:
return [];plus a// TODO: read real data from the enginecomment. - Use the same
_sessions+RequireOpenEnginepattern asSchemaService.
- Single public method
Server controller (
tools/Typhon.Workbench/Controllers/<ModuleName>Controller.cs)- Route
api/sessions/{sessionId:guid}/<moduleKebab>. - One
GET <panelKebab>sendpoint calling the service method, wrapped in the standardInvoke<T>(copy the wrapper fromSchemaController). [RequireBootstrapToken][RequireSession][Tags("<ModuleName>")].
- Route
Server DI registration (
tools/Typhon.Workbench/Hosting/ServiceExtensions.cs)- Register
<ModuleName>Serviceas scoped alongside existing services.
- Register
Server tests (
test/Typhon.Workbench.Tests/<ModuleName>/<ModuleName>ControllerTests.cs)- Two tests:
List_Returns200_WithEmptyArray(happy path against a session) andList_WithoutBootstrapToken_Returns401. - Use
WorkbenchFactory.CreateAuthenticatedClientand the fixture-session helper the existing tests use.
- Two tests:
Client normalized types (
tools/Typhon.Workbench/ClientApp/src/hooks/<moduleSlug>/types.ts)- Interface
<PanelName>Itemmirroring the DTO, anormalize<PanelName>Item(raw)function. - Since the DTO is locally declared in the hook file (we're using
customFetch), import the shape from a local interface, not from@/api/generated.
- Interface
Client query hook (
tools/Typhon.Workbench/ClientApp/src/hooks/<moduleSlug>/use<PanelName>List.ts)- Use
customFetch+ TanStack Query directly (seeuseArchetypeList.ts). Declare the DTO shape locally as a TS interface so compile works before Orval regen. - Add a
// TODO: swap to generated hook once npm run generate-types runsnote.
- Use
Client store (
tools/Typhon.Workbench/ClientApp/src/stores/use<ModuleName>Store.ts)- Zustand store with a single
selected<PanelName>Id: string | null, aselect(id)action, areset()action, and atouchedAt: numberthat bumps on select (for DetailPanel recency — follow the pattern inuseSchemaInspectorStore).
- Zustand store with a single
Shared path (both new-module and panel-in-existing-module)
Panel component (
tools/Typhon.Workbench/ClientApp/src/panels/<ModuleName>/<PanelName>Panel.tsx)- IDockviewPanelProps signature.
- Uses the query hook + the module store.
- Renders: header strip (title + row count + refresh button), empty/loading/error state stack, a
<Table>of rows with a single "Name" column. - Row click →
store.select(row.id). - Keep the h-full w-full overflow-hidden wrapper per
panels/CLAUDE.md.
Context menu (
tools/Typhon.Workbench/ClientApp/src/panels/<ModuleName>/<PanelName>ContextMenu.tsx)- Three disabled stubs: "Copy Name", "Open in Other Panel", "Open in Query Console" — flesh out post-scaffold.
Dockview registration — edit
shell/DockHost.tsx:- Import the new panel component.
- Append
<PanelName>: <PanelName>Panelto thecomponentsmap.
Open helper (
tools/Typhon.Workbench/ClientApp/src/shell/commands/open<ModuleName>.ts)- If new module: create the file following
openSchemaBrowser.ts. - If existing module: append a new
open<PanelName>()function that calls the sameopenDockPanelhelper.
- If new module: create the file following
Menu wiring — edit
shell/MenuBar.tsx:- Add a
MenubarItemunder the View menu pointing atopen<PanelName>(). - Title attr
"Select an item first"if the panel requires a selection state.
- Add a
Palette command — edit
shell/commands/baseCommands.ts:- Add
{ id: '<moduleKebab>-<panelKebab>', label: 'Open <PanelName>', keywords: '<module lowercase> <panel lowercase>', action: open<PanelName> }.
- Add
Vitest smoke (
tools/Typhon.Workbench/ClientApp/src/stores/__tests__/use<ModuleName>Store.test.ts)selectpersists id,selectbumpstouchedAt,resetclears.
Playwright canary (
tools/Typhon.Workbench/ClientApp/e2e/<moduleKebab>.spec.ts)- One test: open demo session, View →
<PanelName>, assert a unique placeholder or title substring is visible. - Copy the
openDemo(page, request)helper frome2e/schema-inspector.spec.ts— Playwright specs don't share modules yet.
- One test: open demo session, View →
Verification (gate before reporting success)
Run these sequentially; fail early and report which step broke.
# 1. Server build
dotnet build tools/Typhon.Workbench/Typhon.Workbench.csproj -c Debug --nologo 2>&1 | tail -5
# 2. Server tests (if new-module path)
dotnet test test/Typhon.Workbench.Tests/Typhon.Workbench.Tests.csproj --filter "FullyQualifiedName~<ModuleName>" --nologo --no-build 2>&1 | tail -3
# 3. Client typecheck
cd tools/Typhon.Workbench/ClientApp && npx tsc --noEmit 2>&1 | tail -5
# 4. Client lint
npm run lint 2>&1 | tail -3
# 5. Client vitest (store test)
npm test -- --run src/stores/__tests__/use<ModuleName>Store.test.ts 2>&1 | tail -5
Do not run Playwright in verification — that requires Kestrel + Vite running, which may not be the case. Instead, print a reminder: "run npx playwright test e2e/<moduleKebab>.spec.ts once dev servers are up".
Output
Report to the user:
- Files created (absolute paths)
- Files edited (DockHost.tsx, MenuBar.tsx, baseCommands.ts, ServiceExtensions.cs if new-module)
- Verification results (pass / fail per step)
- Next steps markdown inline in the chat, listing:
- Where to fill in the real service body (file + method)
npm run generate-typesas the swap point fromcustomFetch→ Orval generated hooknpx playwright test e2e/<moduleKebab>.spec.tsto confirm the canary- Suggested follow-ups: enrich DTO, flesh out context menu actions, add sibling panels, add e2e coverage for the real features
Notes
- Prefer
select-none(default for the app) — the panel shell inherits. If any part of the new panel should be copyable, addselect-texton that specific region. - Use
StatusBadge(not raw Badge + ad-hoc colors) for any status chip — seecomponents/ui/status-badge.tsx. - If the panel includes a canvas, follow
SchemaLayoutRenderertheme-token pattern — read CSS variables, listen for.darkclass changes via MutationObserver. - If the panel has a table, row-height convention is 22 px (see
panels/CLAUDE.md). - Do not create barrel
index.tsfiles — import directly (seesrc/CLAUDE.md).