Plugin Guide v1: in-app plugin API docs with SDK-synced surface map - #2248
Merged
Conversation
Documents the bb plugin API as an annotated map of the product plus the generated reference behind it. - apps/web/scripts/generate-plugin-api-docs.mjs parses the SDK's committed bundled declarations into a committed model; --check fails when stale. - packages/plugin-api-map holds the surface inventory, the bb UI skeletons, and the annotation cards, shared so the docs page and the in-app plugin cannot disagree about what bb can be extended with. - plugins/plugin-api-docs renders that map inside bb as a nav panel, with the host's real composer embedded in the composer and home diagrams. - The docs page leads with the reference; the diagram sits below it as a supporting explainer. - An anatomy manifest pins the skeletons to the real app component order, and apps/app's docs-anatomy-manifest test fails when the app reorders. - apps/web route changes animate through the View Transition API, content only, with reduced motion falling back to an instant swap. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
These changes were already uncommitted in this worktree when the plugin API docs work began and are unrelated to it: blog rendering, landing analytics and endpoints, connect/auth routes, server token handling, and small web UI primitive tweaks. Committed separately so they can be reviewed or dropped on their own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The bb Plugin Guide (plugins/plugin-api-docs, rendering packages/plugin-api-map) becomes bb's only plugin API documentation: six slides — annotated skeletons of the app window, composer, home, settings, and Extensions plugin pages, plus a backend capability grid — with a reviewed card per surface, cross-references, and "Used by" links. packages/plugin-api-map/test/api-sync.test.ts fails the build when the map and the SDK drift apart; the apps/web docs route, generated API model, and generator are deleted in its favor. Host and SDK changes the guide rides on, all additive: - experimental_suppressPluginUi on experimental_NewThreadComposer, plumbed through the new-thread prompt box path, so an embedded composer can render without other plugins' composer UI (a prompt improver's action was landing inside the diagrams and rewriting the persisted example draft). - Route anchors inside plugin UI navigate client-side; links to a plugin's Extensions page open it beside the current pane. - New plugin-detail pane kind so a plugin's Extensions page can live in a split; ordinary navigation to it stays full-window. - Plugin CSS is awaited before registrations publish, removing the unstyled first-paint flash on plugin load and reload. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-ready-thr_kg7xb7qipg
…-ready-thr_kg7xb7qipg
…-ready-thr_kg7xb7qipg # Conflicts: # apps/web/src/styles.css
…l-fixes-thr_7f4g7pd89a # Conflicts: # apps/app/package.json # pnpm-lock.yaml
brsbl
added a commit
that referenced
this pull request
Aug 24, 2026
Review of the cumulative Plugin Guide branch found three defects: - P0: the full-window /extensions/plugins/:pluginId page rendered the plugins overview instead of the detail page. SplitWorkspaceRoute owns that URL under a path="*" route, so ToolsView's useParams() could never see the plugin id. ToolsView now takes the id as an explicit prop (like PluginDetailPaneView already does) and SplitWorkspaceRoute passes the id it derives from the URL. Regression test added under the production catch-all mount. - P1: the persisted-0.4.8-scaffold upgrade test pinned PLUGIN_SDK_VERSION to 0.4.18 and failed against main's 0.4.20. The assertion is now relational (newer than the frozen fixture), which is what the upgrade test actually means. - P1: the composer-plus-menu, provider-picker, and composer-actions highlight rings were authored in the prompt box's coordinate space but rendered in a wrapper whose bottom edge is the sub-composer strip, landing ~22px below their targets. The rings now render inside the prompt box's own relative context in StaticEmbeddedComposer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Extensions page already labels the plugin BB Official, so the bb prefix in the display name was redundant. The plugin id, package, and route (plugin-api-docs) are unchanged; only the user-visible name, the mention label, the clipboard pill context, and the maintenance-skill heading change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…y-thr_6n85axkwev # Conflicts: # apps/app/src/components/ui/app-route-anchor.tsx
- Add the fidelity sources wireframes.test.ts and maintenance-skill.test.ts actually read (ThreadDetailView.tsx, plugins/plugin-api-docs/**) to the @bb/plugin-api-map#test turbo inputs, so edits to them can no longer cache-hit a stale pass. - Delete the dead ComposerWireframe export (replaced by RealComposerAnnotated; the package is private and it had no consumers). - Reorder the flat headless surface array to match the rendered section order, so card Previous/Next never jumps between sections; assert order equality in surfaces.test.ts. - Open a followed cross-reference's card on the pan's own transitionend (state-driven, superseded by any newer pan and cleaned up on unmount) instead of an uncancelled setTimeout that duplicated the CSS duration. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Collapse the Guide's seven annotation mechanisms into one placement system with zero per-instance coordinates: - Mark/RegionMark chips take a declared placement variant (corner, corner-inset, side, outside-above) from one CHIP_PLACEMENT_CLASS owner, replacing every authored chipClassName offset. - MeasuredBadge replaces GuideBadge, OverlayMark, RightPanelTabBadge and its geometry-duplicating layer, and CommandPaletteActionBadge: exterior, lane, and floating chips measure the element they annotate (in layout coordinates, invariant under the scale-together transform) and derive their column from the window frame's own box. - GuideHighlight is gone: the engaged ring is the target element's own styling, so it wraps content by construction. Content scripts tint the frame itself. - The composer draft line renders in flow inside the prompt box (no hand-synced overlay/reservation pair), and both composer menus anchor to the element the real menu flips against instead of authored offsets. - The app-shell fixture keeps one width owner (the gutter floor). Static tests assert existence and placement declarations; runtime- measured geometry is verified by the rendered QA sweep. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- The caret+label group shrink-wraps and centers as one unit, so the pan carets hug the page list and only reach the row's edges on real overflow. The scroller keeps sole horizontal-scroll ownership. - One content-width token (the map column) sizes the column, stage, and card so their edges align, and grows on wide displays. - The stage-to-card gap is clamp(8px, 3cqh, 28px), driven by the consumer-declared container on the plugin's scrollport; without a declared container the 8px floor holds. - spatialFixtureScale derives from both axes with a 1.3 legibility cap: fixtures shrink under pressure and grow on roomy panels (the squished-on-large-displays fix). Available height is measured scroll-invariantly from the declared scrollport, strictly upstream of anything the scale resizes, and the height reserve is unconditional. - Fixtures declare one authored height each; the 100dvh-minus-chrome clamps (frozen guesses of app chrome) are gone. - The maintenance skill's geometry checklist now states the derivation contracts (variant/measured chip placement, two-axis scale, derived gap) instead of the retired constants. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ship scripts/verify-guide-chrome.mjs with the maintenance skill: a Chrome-for-Testing sweep that discovers every rendered annotation on every page at four viewport classes, reconciles them against the declared inventory (a missing annotation cannot pass silently), and asserts relationships — badge bounds and topmost hit-tests, engaged rings on targets, transient clearance, caret-to-label adjacency, scale bounds with zero page overflow, the 8-28px gap clamp on the in-flow card, and the wide-viewport fill gate. Two placement fixes the sweep caught on its first run: - A measured badge whose container sits inside the clipping window frame (the palette dialog on a narrow panel) now clamps into the frame's interior instead of hanging past its edge into nothing. - The composer + menu's bottom margin is the outside-above chip lane (CHIP_SIZE + CHIP_GAP), so the open menu clears the draft line's chips by construction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Main's 0.40.0 release prep advanced @get-bb/plugin-sdk to 0.4.22 with declaration changes in ., ./app, and ./testing/app (no new public export — every subpath still maps to its Guide surface, 73/73 including api-sync). The stale-inventory failure only surfaced now because the turbo-inputs fix invalidated the cache that had been serving pre-bump declarations. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Main's iOS-first mobile redesign now requires every mobile icon name except the brand marks to map to an SF Symbol; this PR's AiBrowser (the Plugin Guide's sidebar glyph) predates that gate. 888/888 mobile tests pass with the mapping. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Opening a card pinned its bottom flush to the fold at every size — the fixture consumed all available height, so the card's visible band was a viewport constant (reading as a fixed-height card) and opening one scrolled the page chrome away. The open in-flow card's footprint (height plus its gap margin) now subtracts from the fixture's available height, so the fixture makes room, the card ends where its content ends, and the page never scrolls on open. The sweep asserts both: open card fully above the fold and scrollTop stays 0 at desktop widths. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oller fades, focus rings Implements the visual-polish audit's accepted findings: - Fixture width bands move onto SpatialFixture's measured element (one table, one owner per fixture): a band on a nested wrapper is invisible to the scale measurement because a block's scrollWidth can never be smaller than its own clientWidth — which is why upscaling never engaged for fluid fixtures. Every fixture now uses the upscale path on roomy displays, and MAX_FIXTURE_SCALE drops 1.3 → 1.2 to keep transform-scaled hairlines crisp. - WindowFrame lifts onto bg-surface-raised-solid: in dark themes shadows vanish and a same-canvas frame dissolved into the page; a surface step is the standard dark-elevation answer and stays token-derived. - The composer + menu seats flush against the box it flips from (the real menu's sideOffset), and the draft-line chips it would cover hide while it is open — the tour-platform convention — instead of the menu floating clear of them. - One shared chip-bar treatment for horizontal scrollers (scroll-edges module): hidden scrollbar plus an overflow-side edge fade, applied to the page list and the Used-by row, so cut entries read as scrollable rather than torn. - Platform-grid taglines clamp to two lines in narrow panels (container-keyed) instead of truncating mid-thought. - One FOCUS_RING_CLASS owner puts keyboard focus on the product's ring token for annotation anchors, measured badges, pan carets, page buttons, and platform cards. - The card copy's em-based inline-code size is documented as the deliberate relative-sizing convention. 73/73 tests, typechecks, and the full relationship sweep pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Navigating Previous/Next between cards of different heights re-derived the fixture scale from each card's exact footprint, snapping the transform a few percent per step against the stage's animated height — a startling jitter on every card change. - The card reserve now ratchets: while cards stay open it only grows to the tallest card seen (at most one step per open run, when a taller card first needs room) and resets when every card closes. Revisits and shorter cards move nothing. - The reserve height, transform, and centering margin glide on the stage's 300ms ease, so the remaining legitimate re-budgets morph instead of snapping. - MeasuredBadge switches from client-rect-divided-by-scale math to pure layout offsets (offsetLeft/offsetTop accumulation), which are transform-independent — exact mid-transition, and no longer stale when a pure transform settles without a resize. - The sweep gives engaged measurements a transition's settle time; all contracts unchanged and passing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The skill now states the FIXTURE_WIDTH_BANDS single owner, the ratcheted card reserve, the shared scroll-edges chip-bar treatment, and the FOCUS_RING_CLASS keyboard-focus owner, with test pins locking each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
From the second authorized review's P2 list, the three items that make this PR's existing claims true (behavior changes stay follow-ups): - Rewrite three comments describing removed behavior: the card always opens in flow below the diagram (no gutter placement), the pan-wait exists for the height re-budget (cards no longer measure marker geometry), and the sweep's in-flow check is defensive rather than an overlay branch. - Apply FOCUS_RING_CLASS to the card's own controls (Previous/Next, Close, Copy for agent) and the cross-reference links in card copy, so 'every Guide interactive' is actually true. - Document layoutOrigin's ≤1px caveat: offsetLeft omits the borders of positioned intermediate offsetParents (WindowFrame's 1px), which a thicker-bordered frame would need to compensate. 73/73 tests and the full relationship sweep pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Clicking an annotation, clicking away, and clicking another replayed the whole make-room swing every time: the reserve reset to zero when the card closed, so the fixture grew back to the cap and shrank again on the next open. The ratchet now never resets on close — the fixture makes room once, keeps honest whitespace while no card is open, and later opens move it only when a strictly taller card first needs the room. Verified with the reported gesture: open (one step), click-away close (no motion), open a taller card (one 1.5% step). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per-card height budgeting re-scaled the fixture on every card interaction: each taller card stepped the fixture down again, and the reader saw the mock keep shrinking as they walked annotations. Reserving the tallest card up front fixed the motion but permanently shrank every landing view. Now a hidden probe renders every card a slide can open and pre-measures the tallest, and the reserve engages only once a card has actually opened. The landing view renders at its full derived scale; the first open glides once to the size that fits the tallest card; after that nothing re-scales for the slide's lifetime — not on close, reopen, or Previous/Next through any card. SurfaceCard gains a probe mode that skips dialog semantics, the Escape listener, and the scroll-into-view effect so the hidden pre-measure can never steal focus or scroll position. Verified: 73/73 package tests, full relationship sweep PASS (4 viewports x 7 pages), CDP scale trace constant after first open on app-shell (0.8444 landing -> 0.5743 once) and composer (0.7442 throughout). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… ways The make-room animation read as stepped motion — down, then right, then back left — because three properties animated against each other: the frame height, the fixture scale around a top-left origin, and a scale-dependent margin-left re-centering the shrunken box. And after the one-motion fix the fixture never grew back, leaving closed pages at card size. The fixture now scales from a top-center origin around a static centering offset that depends only on the frame and authored widths, so a card opening or closing animates exactly two properties — frame height and transform — on the same 300ms ease: one symmetric center-outward gesture with zero horizontal drift. The reserve exists only while a card is open: opening glides down once to the size fitting the slide's tallest card (the hidden probe keeps Previous/Next motionless), and closing glides the same gesture back to the full landing size. The live ratchet stays as a probe-less backstop and resets when the card leaves the flow. Verified: 73/73 package tests, full relationship sweep PASS (4 viewports x 7 pages), CDP trace on app-shell (0.8444 -> 0.5743 open -> 0.8444 close, margin-left frozen at -98) and composer (1.2 -> 0.755 -> 1.2, margin frozen at 148), open scale constant across different cards on both. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The first card open still read as two animations: the card's mount effect called scrollIntoView immediately, against the not-yet-shrunken layout, so the page jumped down to the below-fold card and then clamped back up as the 300ms re-budget shortened the page beneath it. The scroll now waits out the glide (350ms) — the shrink brings the card above the fold on its own, making the scroll a no-op — and scrolls smoothly only when the card is still genuinely out of view on a short screen. Verified: 73/73 package tests, relationship sweep PASS (4 viewports x 7 pages), CDP trace at 1440x900 and 1440x640: scrollTop stays 0 through the entire first open and the card ends fully visible from the glide alone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Following a card's cross-reference to another page was still a two-step: close the card, pan 300ms, wait for transitionend (or a 600ms fallback timer), then open the destination card and re-budget — a second, separate animation after the first. The wait existed because opening mid-pan used to animate two reactively-measured heights against each other; the hidden probe has since made the destination's card-open size deterministic up front, so the reason is gone. goToSurface now pans and opens in the same commit: the pan, the destination's re-budget, and the card's arrival all ride one 300ms gesture, and the card never blinks closed in between. The pendingOpenId state, transitionend handshake, and PAN_FALLBACK_MS ceiling are deleted. The stage-scale and scroll fixes from the previous commits are owned by the shared SpatialFixture and SurfaceCard, so every page already inherits them; this was the one remaining sequential animation. Verified: 73/73 package tests, relationship sweep PASS (4 viewports x 7 pages), CDP trace composer -> app-shell reference: destination active with final scale 0.5743 at t=100ms, dialog open continuously through the pan. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two follow-ups from watching the card re-budget in use. The card still trailed the mock by ~300ms on every page: the stage's height transition chased the slide's already-animated height, restarting its ease against each ResizeObserver tick, so the mock settled at ~305ms while the card kept drifting to ~600ms — one animation, then a second. The stage height now animates only across pans, where it steps between two slides; a same-page re-budget follows the slide's gliding height frame by frame, so the card tracks the mock 1:1 (both settle within one frame of each other on every page). Running the palette page's demo command latched the palette closed, with only the header shortcut to bring it back — the page's subject could be left dismissed. The run now hides the palette as a timed beat (2.4s, long enough to read the checklist panel it opens), then restores it on its own; the panel stays open behind it as the run's lasting effect. Verified: 73/73 package tests (palette test now pins the self-restore under fake timers), relationship sweep PASS (4 viewports x 7 pages), CDP settle trace app-shell/composer/settings: card settles at 321-326ms vs mock 309-321ms (previously 548-602ms), palette overlay restored 2.4s after the run with the panel still open. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The clipboard framing hardcoded a '. ' suffix after the surface pill, so every paste gained a trailing period the source content never had. The suffix is now a single plain space — still separating consecutive pastes and follow-on typing from the pill label — with no added punctuation, in both the text/plain and text/html clipboard flavors (the HTML shares the same suffix constant). Verified: 73/73 package tests (agent-reference expectations updated), CDP end-to-end clipboard read after clicking Copy for agent: "Build a plugin that uses @banners " with no trailing period. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A dense fixture in a narrow panel scales to a fraction of its authored size, and the numbered chips rode that transform down with it — about 9px in a split pane, 5px on mobile, against card text at full size. The chip is how a reader gets from the mock to its card, so a chip too small to read or click cannot do its job. Chips now counter-scale against the fixture: SpatialFixture publishes --guide-chip-scale and the chip class undoes the shrink, so a chip never renders below its authored size. The counter-scale is bounded by MAX_CHIP_COUNTER_SCALE so a thumbnail-scale fixture is not blanketed by its own annotations, and every measured gap, tuck, and clamp is now taken against the chip's effective footprint rather than its authored one. The annotation gutter is authored for a chip at its own size, so a counter-scaled chip can outgrow it. Measured chips clamp into their slide and ride the frame edge instead of leaving it to be clipped away; the sweep caught exactly this, with four badges escaping the slide at mobile and narrow before the clamp existed. Rendered chip size, app-shell: 390x844 4.6px -> 15.8px, 768x900 5.4px -> 18.5px, 730x1300 (split pane) 9.3px -> 20px, 1440x900 16.9px -> 20px, 2560x1440 22.1px unchanged. Keeping chips inside the container also stops them widening it, so the mock itself scales up at narrow widths. Verified: 74/74 package tests (new pure-function coverage for the counter-scale boundary), relationship sweep PASS (4 viewports x 7 pages), CDP chip measurements above. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the built-in Plugin Guide (
plugins/plugin-api-docs, renderingpackages/plugin-api-map): an interactive, annotated map of every public plugin surface, drawn as the real bb UI. Each numbered annotation opens a capability card with the authoritative SDK symbols, first-party usage, and a Copy for agent action that pastes a structured, composable surface reference into the composer.What's in it
Build a plugin that uses+ a structured pill + one plain space — no added punctuation, so pasted output keeps exactly the source content's own; multiple copies paste as distinct, composable pills.api-sync.test.ts— CI fails if a public SDK export,app.slots.*method, orBbPluginApiproperty changes without a matching Guide surface.plugin-guide-maintenanceskill ships with the plugin and encodes the fidelity/annotation/copy workflow.plugin-api-docs.e97545201..7e2ca90bf): one annotation placement system replaces seven mechanisms — chips take declared variants or measure the element they annotate, the engaged ring is the target's own styling, and transients anchor to what the real menu flips against. Pan carets hug the page list; the stage-to-card gap isclamp(8px, 3cqh, 28px); fixtures scale from both viewport axes up to a 1.2 legibility cap (large displays render them proportionally larger; the width bands live in oneFIXTURE_WIDTH_BANDSowner on the measured element); the100dvh-minus-chrome guesses are gone. A visual-polish audit round adds dark-mode window elevation, flush transient menus (colliding chips hide while one is open), shared hidden-scrollbar + edge-fade treatment for horizontal scrollers, container-keyed tagline clamps, one keyboard-focus ring owner, and a card-height reserve so card navigation never re-scales the fixture.plugin-guide-maintenance/scripts/verify-guide-chrome.mjs) discovers every annotation at four viewport classes, reconciles against the declared inventory, and asserts relationships instead of exact pixels.Review
One authorized final review ran on the cumulative branch; its P0/P1 findings are fixed in
bc30d52cc, P2 notes are recorded as follow-ups and intentionally not implemented:/extensions/plugins/:pluginIdrendered the plugins overview: the URL is owned bySplitWorkspaceRouteunderpath="*", souseParamsnever carried the id.ToolsViewnow takes an explicitpluginIdprop; regression test added under the production catch-all mount.PLUGIN_SDK_VERSIONto0.4.18; it now asserts the relational contract (newer than the frozen fixture).Verification
Head
008a397f5= review fixes + display-name rename + the layout-derivation overhaul + the visual-polish audit round (upscale bands, dark elevation, scroller fades, focus rings) + the one-gesture card motion, chip legibility, palette restore, and punctuation-free copy above + clean merges of currentmain(SDK 0.4.22 inventory regenerated; the Guide's AiBrowser icon mapped to an SF Symbol for the new mobile gate).origin/mainis fully contained in the branch — 0 behind.@bb/plugin-api-map74/74 (incl. api-sync and the migrated structural annotation contracts), app routing 30/30, clipboard/paste/mention 120/120, server plugins 29/29 on Node 22; Turbo typechecks pass; exact-checkout plugin build passes.008a397f5across all four viewport classes (390×844, 768×900, 1440×900, 3440×1440): each ran five complete card open/close cycles with identical open and closed scales every cycle, closing restoring its baseline exactly, no inline overflow through adjacent-width transitions, and chips at 15.8–22.1px (record).8656827: all 7 pages and 26 cards, responsive states 390×844 → 3440×1440, sole-scroller and clearance contracts, highlight-ring geometry (each ring contains its target's center inside the prompt box), command-palette flow, and the full-window detail page. Later commits on the head are main-only merges (no UI/Guide files).bc30d52cc.Screenshots
No linked issue.
BB-Thread-ID: thr_7dczktandc
BB-Thread-ID: thr_7f4g7pd89a
BB-Thread-ID: thr_n5zzpc884f