Skip to content

feat(hub): add toggle dock actions and entry descriptions - #469

Merged
antfu merged 1 commit into
mainfrom
feat/dock-action-toggle
Oct 30, 2026
Merged

antfu merged 1 commit into
mainfrom
feat/dock-action-toggle

Conversation

@antfubot

Copy link
Copy Markdown
Collaborator

Why

Nuxt DevTools v4 is built on the hub and the hub UI dock. After 4.0.0, users said:

  • they want the Vue component inspector one click away on the small dock, without opening the panel and going to another page;
  • some buttons are not clear: "we thought the magnifying glass was the Vue inspector but it opened a different inspector."

Nuxt DevTools wants a top-level "Inspect Vue component" dock entry. It turns an in-page element picker (vite-plugin-vue-tracer) on and off, does not open the panel, and shows as active while picking. See https://github.com/nuxt/devtools.

What was missing

An action entry already runs a client script. But when a click runs an action:

  • the action becomes the selected dock, and hub-ui sets session.open = true;
  • a script that hides the panel itself (session.open = false) makes the dock bar minimize, so the active highlight is not visible;
  • the dock that was open before is lost. switchEntry(null) after a pick closes everything;
  • there is no Escape key to stop the tool;
  • dock buttons show only the title in a tooltip, with no room for a longer hint.

API

interface DevframeDockEntryBase {
  /** One-line hint, shown under the title in the dock button tooltip. */
  description?: string
}

interface DevframeViewAction {
  type: 'action'
  action: ClientScriptEntry
  /** Make the action an on/off tool. @default false */
  toggle?: boolean
}

interface DocksEntriesContext {
  /** Turn a toggle action on or off without touching the selection. */
  setActionActive: (id: string, active: boolean) => Promise<boolean>
}

With toggle: true:

  • the script runs once (cached, like other client scripts), then drives the tool from entry:activated / entry:deactivated;
  • switchEntry(id) (a click) turns it on or off. It never takes the selection;
  • while it is on, an open panel is hidden. The panel comes back when the tool turns off;
  • getStateById(id).isActive is true while it is on. The button renders as active (also inside groups), and the dock bar does not minimize;
  • it turns off on a second click, on docks.setActionActive(id, false), or on Escape (new devframes:stop-active-tool command in hub-ui);
  • selecting another dock, or switchEntry(null), also turns it off. Then the panel is not restored, because the new selection wins.

Without toggle, actions work as before. The scripts that use the current entry:activated + switchEntry(null) pattern (vite-plugin-vue-tracer) keep working.

Both runtimes implement this: createDevframeClientRuntime() (@devframes/hub/client) and the hub-ui context. The reference hosts (custom-hub-vite, custom-hub-next) fire actions by hand and are unchanged, so they stay at parity.

Usage

ctx.docks.register({
  id: 'nuxt:inspect-component',
  type: 'action',
  toggle: true,
  title: 'Inspect Vue component',
  description: 'Pick an element on the page to open its Vue component',
  icon: 'tabler:focus-2',
  action: { importFrom: '…/inspect-client.mjs' },
})
// inspect-client.mjs
export default function setup(ctx) {
  const id = ctx.current.entryMeta.id
  ctx.current.events.on('entry:activated', () => {
    inspector.enable({ onPick: () => ctx.docks.setActionActive(id, false) })
  })
  ctx.current.events.on('entry:deactivated', () => inspector.disable())
}

Tooltips

Top-level buttons and the group side rail already showed the title tooltip. This PR adds the optional description under it. Group popover rows already show the title as text, so they show a tooltip only when description is set.

Tests and docs

  • packages/hub/src/client/__tests__/host.test.ts and packages/hub-ui/src/client/state/client-script.integration.test.ts cover: on/off, the selection is kept, the panel is hidden and restored, Escape, setActionActive, and turning off without a restore on a new selection.
  • Updated the docs: client context guide (new "Toggle actions" section), Hub API reference, Build Your Own Hub UI.
  • Updated the hub API snapshots (tsnapi).
  • pnpm lint && pnpm knip && pnpm test && pnpm typecheck && pnpm build all pass.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
devframe Ready Ready Preview Oct 30, 2026 9:40pm UTC

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

👁️‍🗨️ Review this pull request with grouped, summarized diffs at:
👉 https://pulls.review/gh/devframes/devframe/469?from=github-actions[bot]

raw result

💭 analyzed by vercel-ai-gateway/anthropic/claude-sonnet-5
🕰️ 2026-10-30 21:39 UTC
🔗 head 85aa249
🤖 automated by pulls.review

{
  "headSha": "85aa2498be74452a069c95dedc91c3108bddad78",
  "result": {
    "overallSummary": "Adds a `toggle` mode to `action` dock entries so a click turns an in-page tool (e.g. a Vue component inspector) on/off without stealing the dock selection or opening the panel: the script runs once and is driven by activation events, an open panel is hidden while the tool is on and restored when it turns off (unless another dock takes over), and Escape can stop it via a new command. Also adds an optional `description` shown under a dock button's title in tooltips. Implemented in parity across both client runtimes (`@devframes/hub/client` and hub-ui), with matching UI updates, i18n strings, tests, and docs.",
    "groups": [
      {
        "key": "toggle-action-core",
        "label": "Toggle action runtime",
        "summary": "Adds the `toggle` option to action dock entries and `setActionActive()` to both runtimes (`@devframes/hub/client` and hub-ui). A toggle action runs its client script once, drives on/off state from `entry:activated`/`entry:deactivated`, never takes the dock selection, and hides/restores the panel while active. Also adds `description` to `DevframeDockEntryBase`.",
        "category": "core",
        "filePaths": [
          "packages/hub/src/client/docks.ts",
          "packages/hub/src/client/host.ts",
          "packages/hub/src/types/docks.ts",
          "packages/hub-ui/src/client/state/context.ts",
          "packages/hub-ui/src/client/state/docks.ts"
        ],
        "critical": true,
        "fileNotes": [
          {
            "path": "packages/hub-ui/src/client/state/context.ts",
            "text": "Handles the trickiest part: tracking whether the panel was open before a tool turned on (`reopenPanel`), so a new selection wins and skips restoring it, while a direct second toggle does restore it."
          },
          {
            "path": "packages/hub/src/client/host.ts",
            "text": "Mirrors the hub-ui logic for the standalone client runtime; keep the two implementations in sync (`switchToggleAction`, `turnOffActions`, `restorePanel`)."
          }
        ]
      },
      {
        "key": "dock-ui-active-tooltip",
        "label": "Dock UI active state & tooltips",
        "summary": "Updates dock buttons, group popover/sidebar and the dock bar to render an active toggle action (without stealing the selection highlight) and show the dock bar even while an active tool hides the panel, plus a new `description` line under the title in tooltips.",
        "category": "ui",
        "filePaths": [
          "packages/hub-ui/src/client/components/dock/Dock.vue",
          "packages/hub-ui/src/client/components/dock/DockEdge.vue",
          "packages/hub-ui/src/client/components/dock/DockEntries.vue",
          "packages/hub-ui/src/client/components/dock/DockEntry.vue",
          "packages/hub-ui/src/client/components/dock/DockGroupButton.vue",
          "packages/hub-ui/src/client/components/dock/DockGroupPopover.vue",
          "packages/hub-ui/src/client/components/dock/DockGroupSidebar.vue",
          "packages/hub-ui/src/client/state/floating-tooltip.ts"
        ],
        "fileNotes": [
          {
            "path": "packages/hub-ui/src/client/components/dock/DockGroupPopover.vue",
            "text": "Adds hover tooltips to popover rows, but only when `description` is set since the row already shows the title as text."
          }
        ]
      },
      {
        "key": "i18n-stop-tool",
        "label": "Stop active tool translations",
        "summary": "Adds the `command.stopActiveTool` string for the new `devframes:stop-active-tool` Escape command, in all supported locales.",
        "category": "i18n",
        "filePaths": [
          "packages/hub-ui/src/client/i18n/locales/de.json",
          "packages/hub-ui/src/client/i18n/locales/en.json",
          "packages/hub-ui/src/client/i18n/locales/es.json",
          "packages/hub-ui/src/client/i18n/locales/fr.json",
          "packages/hub-ui/src/client/i18n/locales/ja.json",
          "packages/hub-ui/src/client/i18n/locales/ko.json",
          "packages/hub-ui/src/client/i18n/locales/pt-BR.json",
          "packages/hub-ui/src/client/i18n/locales/ru.json",
          "packages/hub-ui/src/client/i18n/locales/zh-CN.json",
          "packages/hub-ui/src/client/i18n/locales/zh-TW.json"
        ]
      },
      {
        "key": "tests",
        "label": "Toggle action tests & snapshots",
        "summary": "Covers toggle on/off, selection preservation, panel hide/restore, Escape, `setActionActive`, and the no-restore-on-new-selection case for both runtimes, and updates the public API type snapshots accordingly.",
        "category": "tests",
        "filePaths": [
          "packages/hub/src/client/__tests__/host.test.ts",
          "packages/hub-ui/src/client/state/client-script.integration.test.ts",
          "tests/__snapshots__/tsnapi/@devframes/hub/client.snapshot.d.ts",
          "tests/__snapshots__/tsnapi/@devframes/hub/index.snapshot.d.ts"
        ]
      },
      {
        "key": "docs",
        "label": "Documentation updates",
        "summary": "Documents toggle actions (new 'Toggle actions' guide section), the `description` field, and renderer expectations for hosts that render dock entries themselves.",
        "category": "docs",
        "filePaths": [
          "docs/content/1.guide/17.client-context.md",
          "docs/content/1.guide/22.build-your-own-hub-ui.md",
          "docs/content/8.references/6.hub-api.md"
        ]
      }
    ],
    "schemaVersion": 1,
    "source": "llm",
    "generatedAt": "2026-10-30T21:39:12.118Z",
    "model": "vercel-ai-gateway/anthropic/claude-sonnet-5",
    "locale": "en"
  }
}

A `toggle: true` action is an on/off tool, such as an in-page element
picker. It never takes the selection: it hides an open panel while on,
restores it when off, and its dock button renders as active. A click,
`docks.setActionActive(id, false)`, or Escape in hub-ui turns it off.

Dock entries also get an optional `description`, shown under the title
in the dock button tooltip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@antfubot
antfubot force-pushed the feat/dock-action-toggle branch from 2a8b61c to 85aa249 Compare October 30, 2026 21:38
@antfu
antfu merged commit b93b8fa into main Oct 30, 2026
5 of 14 checks passed
@antfu
antfu deleted the feat/dock-action-toggle branch October 30, 2026 21:38

This branch was successfully deployed

1 active deployment
Preview — 85aa2498 Deployed Oct 30, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants