diff --git a/docs/content/1.guide/16.hub.md b/docs/content/1.guide/16.hub.md index 6f274c17..28698a94 100644 --- a/docs/content/1.guide/16.hub.md +++ b/docs/content/1.guide/16.hub.md @@ -46,6 +46,22 @@ ctx.commands.register({ `args` takes positional [Standard Schema](https://standardschema.dev/) schemas (a single `v.object(...)` unwraps into the input); omit for zero-arg. `safety` defaults to `'action'`; `when` clauses are unenforced for agent calls. +## Nested commands + +A command's `children` nest arbitrarily deep. The palette drills into each level, and every command in the tree is bindable at any depth — a shortcut assigned to a leaf several levels down fires as directly as one on a top-level command, and each appears as its own row under **Settings → Shortcuts**, indented by nesting level. + +```ts +ctx.commands.register({ + id: 'app:cache', + title: 'Cache', + children: [ + { id: 'app:cache:clear', title: 'Clear', keybindings: [{ key: 'Mod+Shift+K' }], handler: clearCache }, + ], +}) +``` + +Set `showInPalette: 'without-children'` on a parent to keep its whole subtree out of root search while leaving it reachable by drilling down. + ## Cross-iframe dock activation A mounted devframe's iframe uses `hub:docks:activate` to switch the active dock. @@ -181,7 +197,7 @@ ctx.docks.register({ title: 'Nuxt', icon: 'logos:nuxt-icon', category: 'framework', - defaultChildId: 'nuxt:overview', // optional; popover-only when omitted + defaultChildId: 'nuxt:overview', // optional; see "Activating a group" below }) ctx.docks.register({ @@ -194,7 +210,19 @@ ctx.docks.register({ }) ``` -Group and members stay independent top-level entries in `devframe:docks`. Activating the group reopens the member last opened in it (remembered per tab), and `defaultChildId` before any member has been opened. Grouping affects the dock rail, not iframes — to share **one** soft-navigated iframe, give docks a shared `frameId` and mark the anchor with `subTabs` ([Shared-iframe soft navigation](/guide/client-context#shared-iframe-soft-navigation)). +Group and members stay independent top-level entries in `devframe:docks`. Grouping affects the dock rail, not iframes — to share **one** soft-navigated iframe, give docks a shared `frameId` and mark the anchor with `subTabs` ([Shared-iframe soft navigation](/guide/client-context#shared-iframe-soft-navigation)). + +### Activating a group + +Activating a group resolves to one of its members. + +The dock rail button reopens the member last opened in the group (remembered per tab), then `defaultChildId`; with neither, it reveals the member popover. + +A group command — activated by its keyboard shortcut or a command-palette pick — opens that same remembered or default member, then the only visible member when there is exactly one. With several visible peers and no preferred member, it opens the command palette scoped to those members, so the choice stays with the user. Pressing the same shortcut again closes that palette. + +`hub:docks:activate` follows programmatic dock switching: it opens the remembered or default member, then the first registered member. + +Declare `defaultChildId` when one member is the natural landing spot; leave it off when the members are peers. ### The dual role of `category` diff --git a/docs/content/8.references/6.hub-api.md b/docs/content/8.references/6.hub-api.md index abb6972a..37108013 100644 --- a/docs/content/8.references/6.hub-api.md +++ b/docs/content/8.references/6.hub-api.md @@ -106,7 +106,7 @@ The properties of `DevframeClientContext` — [The client context](/guide/client | `clientType` | `'embedded'` (inside the user app) or `'standalone'` (independent hub page). | | `docks` | `entries`, `selected`, `groupedEntries`, `switchEntry()`, `toggleEntry()`, `getStateById()`, `register()` / `update()` for [client-only docks](/guide/client-context#client-only-docks). | | `panel` | Dock panel state: position, size, drag/resize. | -| `commands` | Command palette: `register()`, `execute()`, `getKeybindings()`. | +| `commands` | Command palette: `register()`, `execute()`, `getKeybindings()`, `paletteOpen`, `paletteScopeId`, `openPalette(atCommandId?)`. Passing an id opens the palette drilled into that command's children ([Activating a group](/guide/hub#activating-a-group)). | | `renderers` | Dock-renderer registry — `register()`, `get()`, `has()`, `mount(entry, container)`. Routes a dock `type` to a renderer (local boot or the hub's [manifest](/guide/hub-initiate#renderer-modules); local wins). `mount()` resolves a `status`: `mounted` (with `dispose`), `missing-renderer`, or `load-error` (with `error`). | | `when` | The [when-clause](/references/when-clauses) context. | | `connection` | Live [connection status](/guide/client#handling-connection-and-auth-errors) — `status`, `error`, `events`. | diff --git a/packages/hub-ui/src/client/components/command-palette/CommandPalette.stories.ts b/packages/hub-ui/src/client/components/command-palette/CommandPalette.stories.ts index b6709144..ccf9eb0a 100644 --- a/packages/hub-ui/src/client/components/command-palette/CommandPalette.stories.ts +++ b/packages/hub-ui/src/client/components/command-palette/CommandPalette.stories.ts @@ -45,3 +45,25 @@ export const Open: Story = { ), }), } + +/** + * The palette opened *scoped* to a dock group, listing only that group's + * members — what activating a group with no `defaultChildId` does, so a group + * stays reachable by keyboard with the choice of member left to the user. + * Backspace or Escape steps back out to the root list. + */ +export const ScopedToGroup: Story = { + render: () => ({ + setup: () => mountWithContext( + { entries: groupedEntries }, + ctx => h(defineComponent({ + setup() { + onMounted(() => { + ctx.commands.openPalette('devframes:docks:playground') + }) + return () => h(CommandPalette, { context: ctx }) + }, + })), + ), + }), +} diff --git a/packages/hub-ui/src/client/components/command-palette/CommandPalette.vue b/packages/hub-ui/src/client/components/command-palette/CommandPalette.vue index 1aba472c..befb4e59 100644 --- a/packages/hub-ui/src/client/components/command-palette/CommandPalette.vue +++ b/packages/hub-ui/src/client/components/command-palette/CommandPalette.vue @@ -1,8 +1,10 @@