Skip to content

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 29 May 06:32
· 124 commits to main since this release
e74df63

Highlights

  • Widget plugin-UI contribution protocol. Plugins can now ship their own widget surface alongside their server-side manifest. A new window.__HOVER_WIDGET__ host API accepts six contribution surfaces:
    • Namespaced CSS — auto-prefixed with [data-plugin-active="<name>"] so plugin styles only apply while engaged.
    • Declarative DOM mutationshide: string[] and addClass: Record<sel, cls>, reverted on deactivate. (Intended for plugin-owned DOM only; default mode owns its own widgets.)
    • Toolbar buttons appended to the panel header, with optional badge fns that re-run on state changes.
    • Full-panel overlays (slide-over from the panel header, plugin owns the render fn + state mapping).
    • WS message handlers keyed by namespaced event type (e.g. 'security:flow:added'), only routed while the plugin's mode is active.
    • onActivate / onDeactivate lifecycle callbacks.
  • Single-mode exclusivity — at most one plugin's contributions are visible at any moment. Default mode equals "no plugin active." Host's applyMode(newMode) deactivates the prior plugin's UI before installing the new one.
  • Symmetric mode ownership. Default mode listens for modes payload changes and hides its own widgets (Record, Fix) when a plugin mode takes over. Plugins never need to know default-mode selectors — adding a new plugin no longer means "list which core buttons should I hide." In-flight recording / fix-picking sessions cancel cleanly when mode leaves default.
  • @hover-dev/security migrates onto the new protocol. Its network panel, flow row rendering, orange theme, and status code colour buckets all live in packages/security/src/widget.js now — not hardcoded in client.js. ~180 lines of if (mode === 'security') branches removed from core. Net: client.js shrank from 3337 to ~3155 lines.
  • cursor-agent joins the agent registry as a third option alongside claude (hard sandbox) and codex (soft sandbox). Cursor is soft-sandbox (⚠ in the dropdown). Install: curl https://cursor.com/install -fsS | bash. Known limits surfaced in the descriptor: no --max-budget-usd, no --mcp-config (users add the Playwright MCP to ~/.cursor/mcp.json themselves), no token/cost data in the stream (widget renders for cursor sessions).
  • @hover-dev/astro / @hover-dev/nuxt / webpack-plugin-hover accept plugins. Previously only vite-plugin-hover and @hover-dev/next did. hover() / new HoverPlugin() gain a ...plugins: HoverPluginManifest[] varargs slot (Nuxt uses plugins?: HoverPluginManifest[] on the module options object). Older hover({}) calls without plugins continue to work unchanged.

Bug fixes (user-reported in flight)

  • Fix popover sat at top: 48px and overlapped the 28px mode bar. Pre-existing layout bug from when mode bar was added in v0.7 — .fix-popover never made it into the .panel.has-modebar overlay-offset selector list. Fixed.
  • Tooltips punched through the Fix popover. Mouseover events still fired on header buttons under the popover; tip's z-index is higher than the popover's, so it rendered visible over the top of the modal. mouseover handler now short-circuits when .fix-popover.visible or any .plugin-overlay.open element exists in the shadow root.
  • el.hidden = true silently no-op'd on elements with explicit display. Browser UA [hidden] { display: none } has lowest specificity; any author rule with display: inline-flex wins. Added a catch-all [hidden] { display: none !important } near the top of style.css so hidden = true reliably collapses any widget element (including plugin-contributed DOM).

Internal cleanup

  • client.js -180 lines. Removed state.flows, 7 networkXxx DOM handles, renderFlowRow, renderNetworkOverlay, upsertFlow, the networkBtn.hidden = !engaged + recordBtn.hidden = engaged lines in renderModeButton, and the hardcoded security:flow:added / :updated WS branch.
  • template.html -33 lines. Removed .networkbtn header button + .network-overlay block.
  • style.css -85 / +19. Removed security-specific selectors (.network-overlay, .network-badge, .flow-row, .flow-status-*, etc.). Added a generic .plugin-overlay shell + the catch-all [hidden] rule.
  • New packages/widget-bootstrap/src/widget/host.js — 449 lines. Hand-rolled CSS namespacer (top-level selector-list split, @-rule passthrough), DOM-mutation applier with state recording for revert, overlay shell builder, toolbar button construction with badge refresh, fail-silent everywhere with structured [hover/plugin "<name>"] <where> failed: console logging.
  • 3 new Playwright tests under examples/basic-app/__vibe_tests__/plugin-host.spec.ts — covers __HOVER_PLUGINS__ descriptor injection, __HOVER_WIDGET__ host API exposure with apiVersion: 1, and default mode rendering zero plugin contributions while security is installed-but-inactive.
  • cursor.ts 351 lines, 18 new vitest cases. Wire shape: cursor-agent -p "<prompt>" --output-format stream-json --force. Defensive walking of *ToolCall wrapper variants (Cursor doesn't publish a comprehensive schema). Soft-sandbox preface prepended to the user prompt (Cursor has no --append-system-prompt flag).

Validation

  • pnpm typecheck clean across all 10 publishable packages.
  • pnpm test: 106 unit tests pass (@hover-dev/core agents / specs).
  • pnpm test:e2e: 5/5 Playwright (2 login-and-counter + 3 plugin-host) pass.
  • Manual smoke: switching to security mode in examples/basic-app shows Network button, hides Record + Fix, applies orange theme; switching back restores defaults.

What's next

  • v0.10.x (planned) — multi-tab / cross-origin polish (Stripe, OAuth flows), more agents (aider / gemini-cli / qwen-code), Chrome extension.
  • v0.11.x (planned) — recording semantics for security mode. Now that the widget plugin-UI protocol is in place, this becomes a security-side change with zero core widget edits.

Full diff: v0.8.0...v0.9.0