Skip to content

Plugin Guide v1: in-app plugin API docs with SDK-synced surface map - #2248

Merged
brsbl merged 83 commits into
mainfrom
bb/build-bb-plugin-api-docs-thr_7dczktandc
Aug 26, 2026
Merged

Plugin Guide v1: in-app plugin API docs with SDK-synced surface map#2248
brsbl merged 83 commits into
mainfrom
bb/build-bb-plugin-api-docs-thr_7dczktandc

Conversation

@brsbl

@brsbl brsbl commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds the built-in Plugin Guide (plugins/plugin-api-docs, rendering packages/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

  • Seven pages — app window, command palette, composer, home, settings, Extensions, backend — with 26 sequential annotations over realistic fixtures (real tab rows, menus, message actions, split panes).
  • Responsive by contract: the page list is the only horizontal scroller; every spatial fixture scales as one unit to fit the window and restores authored size up to a cap on large displays.
  • Copy for agent: each card writes 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.
  • A committed public-SDK inventory plus api-sync.test.ts — CI fails if a public SDK export, app.slots.* method, or BbPluginApi property changes without a matching Guide surface.
  • The plugin-guide-maintenance skill ships with the plugin and encodes the fidelity/annotation/copy workflow.
  • Display name is Plugin Guide (Extensions already shows the BB Official tag); the id stays plugin-api-docs.
  • Layout derives, it isn't authored (follow-up commits 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 is clamp(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 one FIXTURE_WIDTH_BANDS owner on the measured element); the 100dvh-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.
  • One gesture per interaction: opening a card glides the fixture once — from a top-center origin around a scale-independent centering offset — to the size that fits the page's tallest card, pre-measured by a hidden probe; closing glides the same gesture back. Nothing re-scales while stepping through annotations. The stage height follows the slide frame-by-frame instead of chasing it with its own ease, so the card tracks the mock 1:1 rather than trailing it; the card's scroll-into-view waits out the glide instead of layering a second motion on it; and following a cross-page reference pans and opens in one commit rather than pan-then-open. Annotation chips counter-scale against the fixture (bounded, and clamped into the slide when the annotation gutter cannot hold them), so a chip never shrinks below its authored size — 4.6px → 15.8px at mobile, 9.3px → 20px in a split pane. The command palette page restores its own palette after its demo command runs, so the page's subject can never be left dismissed. A committed relationship sweep (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:

  • P0 — full-window /extensions/plugins/:pluginId rendered the plugins overview: the URL is owned by SplitWorkspaceRoute under path="*", so useParams never carried the id. ToolsView now takes an explicit pluginId prop; regression test added under the production catch-all mount.
  • P1 — the upgrade test pinned PLUGIN_SDK_VERSION to 0.4.18; it now asserts the relational contract (newer than the frozen fixture).
  • P1 — three composer highlight rings rendered ~22px below their targets (anchored in a wrapper containing the sub-composer strip); they now render inside the prompt box's own coordinate space.

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 current main (SDK 0.4.22 inventory regenerated; the Guide's AiBrowser icon mapped to an SF Symbol for the new mobile gate). origin/main is fully contained in the branch — 0 behind.

  • Tests: @bb/plugin-api-map 74/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.
  • Relationship sweep — PASS at the exact head across 390×844, 768×900, 1440×900, and 2030×1100: every rendered annotation in bounds and topmost, inventory reconciled, engaged rings on targets, transient clearances, caret adjacency ≤8px, scale within (0, 1.2] with zero page overflow, and gap within its clamp (record).
  • Responsive + sustained-motion evidence — PASS at 008a397f5 across 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).
  • Chrome for Testing 151 — root-contracts PASS at candidate 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).
  • Safari 26.5.2 — responsive contract with hard reload and sustained motion, tab overlays, composer transients, highlight alignment, window fit (no horizontal overflow), and the full-window detail page PASS at bc30d52cc.
  • Records: Chrome root-contracts · Safari responsive · Safari review-fix

Screenshots

Wide display: the fixture upscales toward the 1.2 cap in the grown content column, carets hugging the page list Command palette: a plugin action over a realistic thread
Full-size app-window page Command palette page
Composer: the engaged ring is the target's own box, chips measured from their targets Copy for agent: two composable pills with editable framing
Composer highlight ring Composable surface pills
Mobile (390×844): one-line page scroller, whole fixture fits Full-window plugin detail page (review fix, renamed)
Mobile responsive fixture Full-window plugin detail
Narrow panel (768×900): chips hold a legible size as the fixture scales down Split pane (730×1300): chips at authored size beside full-size card text
Chips at 768 wide Chips in a split pane

No linked issue.

BB-Thread-ID: thr_7dczktandc

BB-Thread-ID: thr_7f4g7pd89a

BB-Thread-ID: thr_n5zzpc884f

AGENT GENERATED: by GPT-5.6-Sol

brsbl and others added 28 commits August 19, 2026 02:38
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

# 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
brsbl and others added 28 commits August 25, 2026 14:42
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>
@brsbl
brsbl merged commit 7dc6756 into main Aug 26, 2026
15 checks passed
@brsbl
brsbl deleted the bb/build-bb-plugin-api-docs-thr_7dczktandc branch August 26, 2026 08:43
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.

1 participant