diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3116632e9..451258e35 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -177,6 +177,7 @@ jobs: packages/editor/components/AppHeader.webmcpIndicator.test.tsx packages/ui/components/MathBlock.firstPaint.test.tsx packages/ui/components/DiagramBlock.lazyRetry.test.tsx + packages/ui/components/DecisionControl.test.tsx opencode-v2: name: OpenCode 2 installed package diff --git a/packages/core/guide-viewer-manifest.ts b/packages/core/guide-viewer-manifest.ts index a1e483867..05fbbad36 100644 --- a/packages/core/guide-viewer-manifest.ts +++ b/packages/core/guide-viewer-manifest.ts @@ -6,9 +6,9 @@ import type { GuideViewerAssets } from "./guide-format"; export const GUIDE_VIEWER_MANIFEST: Omit = { js: "viewer.AnU5z-dd.js", - css: "viewer.BWA5Rzwo.css", + css: "viewer.D7WwEXBg.css", jsIntegrity: "sha384-iOm8MrJ1lGtn93UvbQr02EvCOHwQqXcDd84+5pUwfCSoYy146a+tLaemgZjNql8c", - cssIntegrity: "sha384-Q9u2GcsAQO3r93fnqIo1YUOHl11vUm908qK7wPn0jiJd3rVoJZH/HqL4KPzwGWk0", + cssIntegrity: "sha384-9mL9Mhu3b+Tc4SXsoajp8zIRnjx6hy8eDVIPufiPADXCThZ0Dw45jSRUSCQUEGQv", langs: { "astro": "chunks/astro.BykyiR6i.js", "c": "chunks/c.BIGW1oBm.js", diff --git a/packages/ui/HANDOFF.md b/packages/ui/HANDOFF.md index 6e996d216..20ce9f5b5 100644 --- a/packages/ui/HANDOFF.md +++ b/packages/ui/HANDOFF.md @@ -237,6 +237,7 @@ Don't import these in a host. Each hits hardcoded Plannotator endpoints: - `utils/sharing` — Plannotator's public paste service (share-URL feature). - `hooks/useUpdateCheck`, `components/MenuVersionSection`, `components/PlanHeaderMenu` — Plannotator release checks. - `utils/planAgentInstructions`, `utils/reviewAgentInstructions` — generate agent instructions that curl Plannotator's local API. +- `components/DecisionControl`, `utils/decisionSpec`, `hooks/useDismissablePopover` — session decision chrome for Plannotator's own approve/deny/exit endpoints (a host's session decisions are its own outcomes against its own backend). If Workspaces ever wants one of these surfaces, the path is the same as everything else: add a seam to the module in a Plannotator PR, don't fork the component. @@ -676,8 +677,8 @@ Additive only, but required: `@plannotator/ui` 0.32.0 imports the new `@plannota ## Publishing & versioning -- The current pair is `@plannotator/ui` `0.37.0` on `@plannotator/core` `0.25.1`. Core 0.25.1 publishes the `annotation-threads` subpath already used by `AnnotationPanel` and `utils/parser`; UI pins that corrected core exactly. UI 0.37.0 adds the Viewer-owned document-header seam (a new public API, hence the minor bump; 0.36.1 was reserved for it but never published) while retaining the `hideQuickLabel` and `StickyHeaderLane` seams from the 0.35.x and 0.36.0 releases. -- Recent pairs, for the consumer's install matrix: ui 0.32.0 on core 0.25.0 (lockstep, `html-anchor`), ui 0.33.0 and ui 0.34.0 on core 0.25.0 (ui only), and ui 0.35.2, ui 0.36.0, and ui 0.37.0 on core 0.25.1 (0.36.1 was never published). Do not consume ui 0.35.0 externally because its published manifest contains `workspace:*`; do not consume ui 0.35.1 because its exact core 0.25.0 dependency lacks the `annotation-threads` export. +- The current pair is `@plannotator/ui` `0.38.0` on `@plannotator/core` `0.25.1`. UI 0.38.0 adds only internal decision-control primitives (`DecisionControl`, `decisionSpec`, `useDismissablePopover` — not host-supported surface, see the unsupported list). UI 0.37.0 added the Viewer-owned document-header seam (a new public API, hence the minor bump; 0.36.1 was reserved for it but never published) while retaining the `hideQuickLabel` and `StickyHeaderLane` seams from the 0.35.x and 0.36.0 releases; core 0.25.1 publishes the `annotation-threads` subpath already used by `AnnotationPanel` and `utils/parser`, and UI pins that corrected core exactly. +- Recent pairs, for the consumer's install matrix: ui 0.32.0 on core 0.25.0 (lockstep, `html-anchor`), ui 0.33.0 and ui 0.34.0 on core 0.25.0 (ui only), and ui 0.35.2, ui 0.36.0, ui 0.37.0, and ui 0.38.0 on core 0.25.1 (0.36.1 was never published). Do not consume ui 0.35.0 externally because its published manifest contains `workspace:*`; do not consume ui 0.35.1 because its exact core 0.25.0 dependency lacks the `annotation-threads` export. - When both packages change, **publish `core` first**: ui 0.32.0 imports the `@plannotator/core/html-anchor` subpath, which no earlier published core (0.24.0 and before) has, just as ui 0.29.0 needed core 0.23.0 for `@plannotator/core/annotatable`. Bump core, update UI's exact core dependency to the same new version, and run `bun install` so `bun.lock` records the new workspace versions before packing either package. - The HTML annotation seams also changed the guides.show viewer **stylesheet** (five utility rules from `HtmlSurfaceControls`; the viewer JS is unchanged), so `packages/core/guide-viewer-manifest.ts` now pins a CSS hash that exists on guides.show only after the deploy workflow has published this build's `/v1/` assets. A guide exported from this build before that deploy would pin a stylesheet the host does not serve yet: **deploy guides.show before any release that ships this manifest.** - UI declares the already published core version exactly in its source manifest. Do not replace it with `workspace:*`: direct publication can preserve that protocol and make the package impossible to install outside this repository. Bun links the local core workspace whenever its version matches the exact dependency. Before publishing, run `bun run --cwd packages/ui smoke:package`; it checks the source and packed manifests, required tarball subpaths, local Bun linking, and a real pnpm install in an external temporary consumer. When both packages change, publish **`core` first, then `ui`**. diff --git a/packages/ui/README.md b/packages/ui/README.md index 76555bd60..06e943865 100644 --- a/packages/ui/README.md +++ b/packages/ui/README.md @@ -250,7 +250,7 @@ npm install @plannotator/ui @plannotator/core - `@plannotator/core` — pure utils + types, zero deps, browser-safe (CI enforces no `node:` imports). Published. - `@plannotator/ui` — React components/hooks + theme + `configure()`. Depends on an exact published `@plannotator/core` version. Published. - `@plannotator/shared`, `@plannotator/ai` — stay private to the monorepo; `shared` re-exports `core`'s modules via shims so Plannotator's internals are untouched. -- Currently `@plannotator/ui` 0.37.0 depends exactly on `@plannotator/core` 0.25.1. `core` is bumped only when something under `packages/core` changes, so `ui` can advance alone. Keep the published core version exact in `packages/ui/package.json`; do not use a `workspace:` protocol there, because a directly published manifest must remain installable outside this monorepo. Bun still links the matching local workspace during development. When both packages change, publish `core` first, then build and publish the UI tarball. See HANDOFF.md "Publishing & versioning" for the verification command. +- Currently `@plannotator/ui` 0.38.0 depends exactly on `@plannotator/core` 0.25.1. `core` is bumped only when something under `packages/core` changes, so `ui` can advance alone. Keep the published core version exact in `packages/ui/package.json`; do not use a `workspace:` protocol there, because a directly published manifest must remain installable outside this monorepo. Bun still links the matching local workspace during development. When both packages change, publish `core` first, then build and publish the UI tarball. See HANDOFF.md "Publishing & versioning" for the verification command. ## The one rule diff --git a/packages/ui/components/ActionMenu.tsx b/packages/ui/components/ActionMenu.tsx index 4993ab2b3..4f7186b19 100644 --- a/packages/ui/components/ActionMenu.tsx +++ b/packages/ui/components/ActionMenu.tsx @@ -69,6 +69,12 @@ interface ActionMenuItemProps { subtitle?: string; badge?: React.ReactNode; disabled?: boolean; + /** ARIA menu semantics for hosts that render a real `role="menu"` popover + * (DecisionControl). Default undefined so existing consumers are unchanged. */ + role?: 'menuitem'; + /** Appended to the row's classes (e.g. a tone token). Default undefined so + * existing consumers are byte-identical. */ + className?: string; } export const ActionMenuItem: React.FC = ({ @@ -78,13 +84,16 @@ export const ActionMenuItem: React.FC = ({ subtitle, badge, disabled = false, + role, + className, }) => (