Skip to content

feat(plugin): let composer pills render their own popover content - #59764

Merged
neriousy merged 1 commit into
client-done-checkfrom
goal-pill-menu
Oct 27, 2026
Merged

neriousy merged 1 commit into
client-done-checkfrom
goal-pill-menu

Conversation

@opencode-agent

Copy link
Copy Markdown
Contributor

Stacked on #59437, targeting client-done-check.

Lets a composer pill render its own popover content instead of adding more declarative fields to the view. The host keeps the pill and the overlay; the plugin owns only what's inside, like every other slot.

SDK (packages/plugin/src/gui/registries.ts)

  • Remove ComposerPillAction and ComposerPillView.actions (added in feat(core): add /goal with a client-side done check #59437, never shipped).
  • Add ComposerPill.menu?(input: ComposerPillMenuProps): JSX.Element, where ComposerPillMenuProps is ComposerPillProps plus close().
  • menu lives on the pill rather than the view: show re-runs whenever its inputs change, so a render function on the view would remount the content on every goal update. On the item it renders once per open and updates through its own reactivity, like Slot.render and Panel.render.
  • When a pill has menu, clicking it opens the popover and the view's rows / run are unused. The capsule stays locked to label, icon, tone and title.

Host (packages/app/src/session/composer/plugin-pills.tsx)

  • A pill with menu opens a Kobalte Popover (as the todo pill does), not Menu, because arbitrary content does not belong in a menu's keyboard model. The host renders the heading (title ?? label, three lines max) and the panel chrome, then the plugin's content, untracked and inside the plugin's existing Contribution error boundary.
  • The rows path goes back to dev's behavior, except for the three-line heading from feat(core): add /goal with a client-side done check #59437.

Goal plugin

  • Pause/resume/clear are now @opencode/ui Buttons in the goal's menu. Each one closes the popover and then sends the command.

Tests

  • Restored the existing composer pill story to dev's version and added a story covering menu: the host heading, the plugin content, menu winning over rows, close(), and a fresh render on reopen. Making close a no-op makes the new story fail.

Checks

  • Typecheck for plugin, gui-plugins and app
  • bun run lint:changed
  • script/sdk-docs.ts
  • plugin-host.spec.ts -g "composer pill": 2 passed

I haven't checked the goal popover visually against a real session.

Requested by: @neriousy (Filip via Slack)

Replace ComposerPillView.actions with ComposerPill.menu: the host keeps the
capsule and the popover (placement, focus, dismissal, heading) and the plugin
renders what goes inside. The goal pill uses it for its pause, resume and clear
buttons.
@neriousy
neriousy merged commit 26af6ab into client-done-check Oct 27, 2026
9 checks passed
@neriousy
neriousy deleted the goal-pill-menu branch October 27, 2026 23:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant