Extracts the TOC-injection pattern into a reusable component:
src/components/toc-sidebar.js
injectTocTop(id, element) — prepends an element to #observablehq-toc,
removing any previous instance with the same id first so reactive cells
can re-inject on each poll without accumulating duplicates.
decisions.md now uses injectTocTop to place a single widget (live
indicator + Decision Health KPI box) into the right-column sidebar,
removing the standalone live-indicator cell and the ad-hoc id/remove
pattern that was previously inlined.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
KPI infobox
- Replace slim kpi-bar with a boxed card (border, shadow, 195–240px) floating
right in a flex header alongside the live indicator
- Rows: avg resolve time (last ≤5 resolved) + avg open age (all open)
- avg open age colored via CSS var --oc: red/orange/black per threshold
- "no open decisions" shown as muted italic when queue is empty
doc-overlay component (src/components/doc-overlay.js)
- withDocHelp(element, docPath) — adds absolute-positioned ? button
that is invisible until the parent is hovered; click opens overlay
- Overlay: fixed backdrop + animated box with iframe; closes on Esc,
backdrop click, or the close button
- CSS injected once via style tag (STYLE_ID guard, same pattern as MultiSelect)
? buttons wired up in decisions.md
- KPI infobox → /docs/decisions-kpi
- Cumulative chart (wrapped in position:relative div) → /docs/decisions-kpi
- Filter & List section header → /docs/decisions-kpi
Reference page (src/docs/decisions-kpi.md)
- Standalone Observable Framework page at /docs/decisions-kpi
- Documents: KPI card (avg resolve, avg open age, color thresholds),
Resolution History chart (cumulative, period→resolution mapping, filter
interaction, timestamp logic), Filter & List (type/status/search, card
age badge, escalation)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds src/components/multiselect.js — a compact dropdown multi-select that is
Observable-compatible (exposes .value, dispatches bubbling input events) so it
works with view(), Inputs.form, and Generators.input without modification.
Component behaviour:
- Closed state: pill button showing "Label: All" (muted) or active selection
(1-2 items shown by name, 3+ shown as "N of M"); blue border when active
- Open state: dropdown with per-item checkboxes + "Clear selection" link
(only visible when something is selected); closes on outside click / Escape
- Styles injected once into document.head (STYLE_ID guard prevents duplicates)
- Uses CSS custom properties for light/dark mode compatibility
Workstreams page update:
- Domain and Status filters now use MultiSelect instead of Inputs.checkbox
- Filter bar layout reduced to a tight inline row (0.5rem gap)
- Owner text filter restyled to match trigger button height
- No changes to filter logic or downstream cells (filters.domain / .status
are still string[] with empty = show all)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>