@ai-matrx/alchemy 0.8.59 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +2 -0
  3. package/dist/actions.cjs +1 -1
  4. package/dist/actions.cjs.map +1 -1
  5. package/dist/actions.d.cts +300 -3
  6. package/dist/actions.d.ts +300 -3
  7. package/dist/actions.js +1 -1
  8. package/dist/actions.js.map +1 -1
  9. package/dist/checks.cjs +1 -1
  10. package/dist/checks.cjs.map +1 -1
  11. package/dist/checks.d.cts +22 -1
  12. package/dist/checks.d.ts +22 -1
  13. package/dist/checks.js +2 -2
  14. package/dist/checks.js.map +1 -1
  15. package/dist/core.d.cts +141 -26
  16. package/dist/core.d.ts +141 -26
  17. package/dist/declare.cjs +1 -1
  18. package/dist/declare.cjs.map +1 -1
  19. package/dist/declare.d.cts +12 -4
  20. package/dist/declare.d.ts +12 -4
  21. package/dist/declare.js +1 -1
  22. package/dist/declare.js.map +1 -1
  23. package/dist/index.d.cts +141 -26
  24. package/dist/index.d.ts +141 -26
  25. package/dist/menu.cjs +1 -1
  26. package/dist/menu.cjs.map +1 -1
  27. package/dist/menu.d.cts +109 -2
  28. package/dist/menu.d.ts +109 -2
  29. package/dist/menu.js +1 -1
  30. package/dist/menu.js.map +1 -1
  31. package/dist/operate-server.d.cts +6 -0
  32. package/dist/operate-server.d.ts +6 -0
  33. package/dist/operate.d.cts +141 -26
  34. package/dist/operate.d.ts +141 -26
  35. package/dist/ports.cjs.map +1 -1
  36. package/dist/ports.d.cts +116 -1
  37. package/dist/ports.d.ts +116 -1
  38. package/dist/react-bar.cjs +2 -2
  39. package/dist/react-bar.cjs.map +1 -1
  40. package/dist/react-bar.d.cts +4 -2
  41. package/dist/react-bar.d.ts +4 -2
  42. package/dist/react-bar.js +2 -2
  43. package/dist/react-bar.js.map +1 -1
  44. package/dist/react-host.d.cts +124 -3
  45. package/dist/react-host.d.ts +124 -3
  46. package/dist/react-menu.cjs +2 -2
  47. package/dist/react-menu.cjs.map +1 -1
  48. package/dist/react-menu.d.cts +20 -4
  49. package/dist/react-menu.d.ts +20 -4
  50. package/dist/react-menu.js +2 -2
  51. package/dist/react-menu.js.map +1 -1
  52. package/dist/react-overflow.cjs +2 -2
  53. package/dist/react-overflow.cjs.map +1 -1
  54. package/dist/react-overflow.d.cts +109 -2
  55. package/dist/react-overflow.d.ts +109 -2
  56. package/dist/react-overflow.js +2 -2
  57. package/dist/react-overflow.js.map +1 -1
  58. package/dist/react-palette.cjs +1 -1
  59. package/dist/react-palette.cjs.map +1 -1
  60. package/dist/react-palette.d.cts +4 -2
  61. package/dist/react-palette.d.ts +4 -2
  62. package/dist/react-palette.js +1 -1
  63. package/dist/react-palette.js.map +1 -1
  64. package/dist/react-selection.cjs +2 -2
  65. package/dist/react-selection.cjs.map +1 -1
  66. package/dist/react-selection.d.cts +4 -2
  67. package/dist/react-selection.d.ts +4 -2
  68. package/dist/react-selection.js +2 -2
  69. package/dist/react-selection.js.map +1 -1
  70. package/dist/react-sheet.cjs +2 -2
  71. package/dist/react-sheet.cjs.map +1 -1
  72. package/dist/react-sheet.d.cts +4 -2
  73. package/dist/react-sheet.d.ts +4 -2
  74. package/dist/react-sheet.js +2 -2
  75. package/dist/react-sheet.js.map +1 -1
  76. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ports.ts"],"sourcesContent":["/**\n * `@ai-matrx/alchemy/ports` — the host ports (Matrx Alchemy CONTRACT §2.2).\n *\n * The app binds these ONCE, at its boundary (where the Alchemy host is\n * mounted). The package never imports the host.\n *\n * ── Ports arrive with the wave that needs them (chair ruling N8) ────────────\n *\n * This entry declares only the ports the current waves use:\n *\n * ALC-13 `kinds` (content-ir's validator, injected), `diagnostics` (required)\n * ALC-14 `identity`, `persistence`\n * ALC-15 `store`, `notify`, `overlay`, `icons`, `knobs`, `i18n`, `auth`, `dialogs`\n *\n * ALC-16 `transport`, `assets`, `server`\n * PP-01a `window` (host-rendered windows for a live preparation session)\n * PP-13a `transferKnobs` (organization default recipes)\n *\n * ALC-17 adds approvals/bodies/receipts.\n *\n * ── Absent-port semantics ───────────────────────────────────────────────────\n *\n * Every port except `diagnostics` is OPTIONAL. A port the host does not bind\n * means that capability is ABSENT: the actions, formats or writes that need\n * it are not offered at all. A missing port is never stubbed, never faked\n * with a default that pretends to work, and never shown as a dead control.\n *\n * kinds absent → kind validation is absent (no value can be checked,\n * so a kind-bearing write is not offered)\n * diagnostics REQUIRED → nothing fails silently; contract breaks land here\n * identity absent → organization-scoped declarations are absent\n * persistence absent → per-person settings are absent (organization and\n * platform defaults apply)\n * store absent → actions that read named host state or run named\n * host commands are absent\n * notify absent → outcomes are not announced (diagnostics still captures)\n * overlay absent → actions that open an overlay or navigate are absent\n * icons absent → rows render label-only (a key is never shown as text)\n * knobs absent → the platform default layout/density applies\n * i18n absent → labels as written\n * auth absent → a signed-out person does not see requiresAuth actions\n * (sign-in-then-resume is the capability that is missing)\n * dialogs absent → actions that need a host dialog are absent\n * window absent → the preparation workspace window is absent (its\n * Prepare entry is not offered; it never falls back\n * to an overlay carrying the session as data)\n * transferKnobs absent → no organization default recipe; a person's own\n * default (persistence) or the package's Full applies\n *\n * Types only: this entry has no runtime code.\n */\n\n/** JSON as it crosses a port. */\nexport type Json =\n | null\n | boolean\n | number\n | string\n | Json[]\n | { [key: string]: Json };\n\n/**\n * content-ir's kind validator, injected so Alchemy validates without\n * importing the app or the kind catalog. The host builds it from\n * `@ai-matrx/content-ir`'s `createKindValidator` over its own\n * `SchemaSourcePort` (which content-ir defines and exports; Alchemy never\n * declares it).\n *\n * `unverifiable: true` marks a verdict where the kind could not be checked\n * (not registered, no schema, uncompilable schema, catalog unreachable) — a\n * skip, which is never a pass.\n */\nexport interface KindValidatorPort {\n validate(\n kindKey: string,\n value: unknown,\n signal: AbortSignal,\n ): Promise<\n | { ok: true }\n | { ok: false; sentence: string; remedy: string; unverifiable?: true }\n >;\n}\n\nimport type { ClickTarget } from \"./actions/click-target\";\n\n/** Where contract breaks are captured. Required: nothing fails silently. */\nexport interface DiagnosticsPort {\n capture(\n error: unknown,\n context: {\n area: \"declare\" | \"operate\" | \"write\" | \"actions\" | \"menu\";\n detail: Json;\n },\n ): void;\n}\n\n/** Who is using the app right now, and in which organization. */\nexport interface IdentityPort {\n /** `null` while the host has no resolved identity yet. */\n current(): {\n userId: string;\n organizationId: string | null;\n isAuthenticated: boolean;\n isAdmin: boolean;\n } | null;\n /** Called whenever `current()` may answer differently; returns unsubscribe. */\n onChange(listener: () => void): () => void;\n}\n\n/** Per-person settings (layout, density, transfer preferences), layered by the host's settings system. */\nexport interface PersistencePort {\n readSetting<T extends Json>(key: string): Promise<T | null>;\n writeSetting(key: string, value: Json): Promise<void>;\n}\n\n/** Neutral state access: named reads and named commands. No Redux type or dispatch crosses this line. */\nexport interface StorePort {\n /** A named, typed read the host registered (\"activeConversationId\", \"organizationName\"). */\n read<T extends Json>(key: string): T | null;\n /** A named command the host registered (\"tasks.setPendingSource\"); resolves with its result. */\n command(name: string, input: Json): Promise<Json | void>;\n subscribe(key: string, listener: () => void): () => void;\n}\n\nexport interface NotifyPort {\n success(sentence: string): void;\n info(sentence: string): void;\n /** Every failure names its remedy. */\n error(sentence: string, remedy: string): void;\n}\n\n/** Overlays are dispatched through the host; the menu never renders a modal. */\nexport interface OverlayHostPort {\n open(overlayId: string, data: Json, instanceId?: string): void;\n close(overlayId: string, instanceId?: string): void;\n navigate(href: string): void;\n}\n\n/** Icon key → the host's icon component. The model only ever holds keys. */\nexport interface IconResolverPort {\n resolve(key: string): unknown /* host component */ | null;\n}\n\nexport type ContextArrangementKnob = \"classic\" | \"tiered\" | \"command\";\nexport type DensityKnob = \"comfortable\" | \"compact\";\n\n/** ORGANIZATION defaults only; every method takes the organization. */\nexport interface MenuKnobsPort {\n /** Replaces the constant PLACEMENT_LABEL_OVERRIDE. */\n placementLabel(placement: string, organizationId: string | null): string | null;\n /** Replaces the constant DEFAULT_PLACEMENT_MODE. */\n placementMode(placement: string, organizationId: string | null): \"show\" | \"hide\";\n /** The organization's default layout and density (platform default when null: command / comfortable). */\n layout(organizationId: string | null): { context: ContextArrangementKnob; density: DensityKnob } | null;\n}\n\n/** Label translation. Absent = labels as written. */\nexport interface I18nPort {\n t(key: string, fallback: string): string;\n}\n\n/** Sign in, then resume the action the person picked. */\nexport interface AuthResumePort {\n isSignedIn(): boolean;\n /** Stash {actionId, target fingerprint} and open sign-in; after sign-in the next matching menu host resumes it. */\n signInThenResume(pending: { actionId: string; contentFingerprint: string }): void;\n takePending(contentFingerprint: string): { actionId: string } | null;\n}\n\n/** Per-instance host dialogs (today's RichDocumentActionContextCallbacks). */\nexport interface HostDialogsPort {\n requestDelete?(target: ClickTarget): void;\n requestConvert?(target: ClickTarget): void;\n requestEditHistory?(target: ClickTarget): void;\n requestFullPrint?(target: ClickTarget): void;\n requestSaveTable?(table: { headers: string[]; rows: string[][] }): void;\n requestFlashcard?(answer: string): void;\n}\n\n/** The ports a host binds. Optional = absent capability when unbound (see above). */\nexport interface AlchemyHostPorts {\n // ALC-13\n kinds?: KindValidatorPort;\n /** REQUIRED from ALC-13 (the only non-optional port): contract breaks are captured here. */\n diagnostics: DiagnosticsPort;\n // ALC-14\n identity?: IdentityPort;\n persistence?: PersistencePort;\n // ALC-15\n store?: StorePort;\n notify?: NotifyPort;\n overlay?: OverlayHostPort;\n icons?: IconResolverPort;\n /** Organization defaults for menu knobs; a person's own choice is layered over them via persistence (N9). */\n knobs?: MenuKnobsPort;\n i18n?: I18nPort;\n auth?: AuthResumePort;\n dialogs?: HostDialogsPort;\n // ALC-16\n /** Clipboard and download for this runtime (the browser default ships in /operate). */\n transport?: TransportPort;\n /** Private or signed images → share-safe URLs before HTML is sealed. Absent = images keep their URL. */\n assets?: AssetResolverPort;\n /** Present when the host can reach aidream server operations; absent = server formats are not offered. */\n server?: ServerPort;\n // Preparation parity (PP-01a, PP-13a)\n /** Host-rendered windows for a LIVE preparation session (functions, subscriptions, sealed artifacts). */\n window?: WindowPort;\n /** Organization default recipes, resolved by the host's Feature Knob ladder (platform → organization). */\n transferKnobs?: TransferKnobsPort;\n}\n\n// ── PP-01a — WindowPort ─────────────────────────────────────────────────────\n\n/**\n * Why a port and not `OverlayHostPort`: an overlay's `data` is `Json`, and a live preparation\n * session is not — it holds functions, subscriptions and sealed-artifact handles. So the workspace\n * never travels as overlay data; the package hands the host a render function and the host draws it\n * inside its own window system, with the session still live in the closure.\n */\nexport interface WindowSize {\n width: number;\n height: number;\n}\nexport interface WindowOpenOptions {\n /** The window's title, as a person reads it. */\n title: string;\n /** Preferred size; `\"full\"` = near-full-screen. The host clamps to the viewport. */\n size?: WindowSize | \"full\";\n /**\n * Stable identity. Opening an id that is already open focuses that window and draws the new\n * render in it instead of opening a second one.\n */\n instanceId?: string;\n /** Called exactly once when the window closes, whoever closed it (the person, `close()`, the host). */\n onClose?: () => void;\n}\nexport interface WindowHandle {\n readonly instanceId: string;\n /** Close the window; idempotent. */\n close(): void;\n /** Bring the window to the front; a no-op once closed. */\n focus(): void;\n isOpen(): boolean;\n}\nexport interface WindowPort {\n /**\n * What the host really draws. `\"window\"`: non-blocking, movable, resizable. `\"dialog\"`: a blocking\n * full-screen dialog — a surface on a dialog host never says \"window\" and never offers a second one.\n */\n readonly presentation: \"window\" | \"dialog\";\n /** True only when several windows can be open and usable at once. False = never offer \"open another\". */\n readonly multiInstance: boolean;\n /**\n * Draw `render(handle)` inside a host window. `render` returns a host UI node (a React element in\n * a React host) and is called by the host while the window is open.\n */\n open(render: (window: WindowHandle) => unknown, options: WindowOpenOptions): WindowHandle;\n}\n\n// ── PP-13a — TransferKnobsPort ──────────────────────────────────────────────\n\n/**\n * ORGANIZATION defaults for transfer, resolved by the host's Feature Knob ladder: the platform\n * default an admin sets, overridden by the organization. Never per-person — a person's own choice\n * lives only in `PersistencePort` and is layered over this by the package (`resolveDefaultRecipe`).\n */\nexport interface TransferKnobsPort {\n /**\n * The organization's default recipe for one source kind (the platform default when the\n * organization set none), or null when neither names one. A read that fails rejects; it never\n * answers null for \"could not ask\".\n */\n defaultRecipe(sourceKind: string, organizationId: string | null): Promise<Json | null>;\n}\n\n// ── ALC-16 ports (Operate) ──────────────────────────────────────────────────\n\n/** A transferable artifact as the transport sees it (structurally `@ai-matrx/alchemy/operate`'s Artifact). */\nexport interface TransportArtifact {\n format: string;\n plainText: string;\n html?: string;\n file?: { filename: string; mime: string; bytes: Uint8Array };\n}\nexport type TransportOutcome =\n | { status: \"success\"; delivered: \"clipboard\" | \"download-started\"; mimeTypes: string[] }\n | { status: \"degraded\"; delivered: \"plain-text\"; reason: string }\n | { status: \"cancelled\" }\n | { status: \"error\"; code: string; message: string; retryable: boolean };\n/** Clipboard and download for a non-browser runtime (MV3 service worker, Tauri — CONTRACT R8). */\nexport interface TransportPort {\n copy(artifact: TransportArtifact, signal: AbortSignal): Promise<TransportOutcome>;\n download(artifact: TransportArtifact, signal: AbortSignal): Promise<TransportOutcome>;\n}\nexport interface AssetResolverPort {\n resolve(url: string, signal: AbortSignal): Promise<{ url: string } | null>;\n}\nexport type ServerResult =\n | { status: \"bytes\"; filename: string; mime: string; bytes: Uint8Array; notices: readonly { code: string; sentence: string; remedy: string }[] }\n | { status: \"receipt\"; receipt: { status: string; sentence: string; remedy?: string } };\nexport interface ServerPort {\n call(operationId: string, input: Json, signal: AbortSignal): Promise<ServerResult>;\n}\n"],"mappings":"sjBAAA","names":[]}
1
+ {"version":3,"sources":["../src/ports.ts"],"sourcesContent":["/**\n * `@ai-matrx/alchemy/ports` — the host ports (Matrx Alchemy CONTRACT §2.2).\n *\n * The app binds these ONCE, at its boundary (where the Alchemy host is\n * mounted). The package never imports the host.\n *\n * ── Ports arrive with the wave that needs them (chair ruling N8) ────────────\n *\n * This entry declares only the ports the current waves use:\n *\n * ALC-13 `kinds` (content-ir's validator, injected), `diagnostics` (required)\n * ALC-14 `identity`, `persistence`\n * ALC-15 `store`, `notify`, `overlay`, `icons`, `knobs`, `i18n`, `auth`, `dialogs`\n *\n * ALC-16 `transport`, `assets`, `server`\n * PP-01a `window` (host-rendered windows for a live preparation session)\n * PP-13a `transferKnobs` (organization default recipes)\n *\n * ALC-17 adds approvals/bodies/receipts.\n *\n * ── Absent-port semantics ───────────────────────────────────────────────────\n *\n * Every port except `diagnostics` is OPTIONAL. A port the host does not bind\n * means that capability is ABSENT: the actions, formats or writes that need\n * it are not offered at all. A missing port is never stubbed, never faked\n * with a default that pretends to work, and never shown as a dead control.\n *\n * kinds absent → kind validation is absent (no value can be checked,\n * so a kind-bearing write is not offered)\n * diagnostics REQUIRED → nothing fails silently; contract breaks land here\n * identity absent → organization-scoped declarations are absent\n * persistence absent → per-person settings are absent (organization and\n * platform defaults apply)\n * store absent → actions that read named host state or run named\n * host commands are absent\n * notify absent → outcomes are not announced (diagnostics still captures)\n * overlay absent → actions that open an overlay or navigate are absent\n * icons absent → rows render label-only (a key is never shown as text)\n * knobs absent → the platform default layout/density applies\n * i18n absent → labels as written\n * auth absent → a signed-out person does not see requiresAuth actions\n * (sign-in-then-resume is the capability that is missing)\n * dialogs absent → actions that need a host dialog are absent\n * window absent → the preparation workspace window is absent (its\n * Prepare entry is not offered; it never falls back\n * to an overlay carrying the session as data)\n * transferKnobs absent → no organization default recipe; a person's own\n * default (persistence) or the package's Full applies\n * serverActions absent → `runs: \"server\"` Actions refuse with \"not available here\"\n * mandates absent → `runs: \"mandate\"` Actions refuse the same way\n * pageActions absent → `runs: \"page\"` Actions refuse the same way\n * approvals absent → a policy \"ask\" Action started by an agent, applet or automation is refused\n * (\"needs a person's approval\"); a person's own click is the approval\n * (`runs: \"navigate\"` goes through `overlay.navigate`; overlay absent → refused)\n *\n * Types only: this entry has no runtime code.\n */\n\n/** JSON as it crosses a port. */\nexport type Json =\n | null\n | boolean\n | number\n | string\n | Json[]\n | { [key: string]: Json };\n\n/**\n * content-ir's kind validator, injected so Alchemy validates without\n * importing the app or the kind catalog. The host builds it from\n * `@ai-matrx/content-ir`'s `createKindValidator` over its own\n * `SchemaSourcePort` (which content-ir defines and exports; Alchemy never\n * declares it).\n *\n * `unverifiable: true` marks a verdict where the kind could not be checked\n * (not registered, no schema, uncompilable schema, catalog unreachable) — a\n * skip, which is never a pass.\n */\nexport interface KindValidatorPort {\n validate(\n kindKey: string,\n value: unknown,\n signal: AbortSignal,\n ): Promise<\n | { ok: true }\n | { ok: false; sentence: string; remedy: string; unverifiable?: true }\n >;\n}\n\nimport type { ClickTarget } from \"./actions/click-target\";\nimport type {\n ActionContext,\n ActionResult,\n MandateRunRequest,\n ServerActionRunRequest,\n ServerActionRunResponse,\n} from \"./actions/applet-types\";\n\n/** Where contract breaks are captured. Required: nothing fails silently. */\nexport interface DiagnosticsPort {\n capture(\n error: unknown,\n context: {\n area: \"declare\" | \"operate\" | \"write\" | \"actions\" | \"menu\";\n detail: Json;\n },\n ): void;\n}\n\n/** Who is using the app right now, and in which organization. */\nexport interface IdentityPort {\n /** `null` while the host has no resolved identity yet. */\n current(): {\n userId: string;\n organizationId: string | null;\n isAuthenticated: boolean;\n isAdmin: boolean;\n } | null;\n /** Called whenever `current()` may answer differently; returns unsubscribe. */\n onChange(listener: () => void): () => void;\n}\n\n/** Per-person settings (layout, density, transfer preferences), layered by the host's settings system. */\nexport interface PersistencePort {\n readSetting<T extends Json>(key: string): Promise<T | null>;\n writeSetting(key: string, value: Json): Promise<void>;\n}\n\n/** Neutral state access: named reads and named commands. No Redux type or dispatch crosses this line. */\nexport interface StorePort {\n /** A named, typed read the host registered (\"activeConversationId\", \"organizationName\"). */\n read<T extends Json>(key: string): T | null;\n /** A named command the host registered (\"tasks.setPendingSource\"); resolves with its result. */\n command(name: string, input: Json): Promise<Json | void>;\n subscribe(key: string, listener: () => void): () => void;\n}\n\nexport interface NotifyPort {\n success(sentence: string): void;\n info(sentence: string): void;\n /** Every failure names its remedy. */\n error(sentence: string, remedy: string): void;\n}\n\n/** Overlays are dispatched through the host; the menu never renders a modal. */\nexport interface OverlayHostPort {\n open(overlayId: string, data: Json, instanceId?: string): void;\n close(overlayId: string, instanceId?: string): void;\n navigate(href: string): void;\n}\n\n/** Icon key → the host's icon component. The model only ever holds keys. */\nexport interface IconResolverPort {\n resolve(key: string): unknown /* host component */ | null;\n}\n\nexport type ContextArrangementKnob = \"classic\" | \"tiered\" | \"command\";\nexport type DensityKnob = \"comfortable\" | \"compact\";\n\n/** ORGANIZATION defaults only; every method takes the organization. */\nexport interface MenuKnobsPort {\n /** Replaces the constant PLACEMENT_LABEL_OVERRIDE. */\n placementLabel(placement: string, organizationId: string | null): string | null;\n /** Replaces the constant DEFAULT_PLACEMENT_MODE. */\n placementMode(placement: string, organizationId: string | null): \"show\" | \"hide\";\n /** The organization's default layout and density (platform default when null: command / comfortable). */\n layout(organizationId: string | null): { context: ContextArrangementKnob; density: DensityKnob } | null;\n}\n\n/** Label translation. Absent = labels as written. */\nexport interface I18nPort {\n t(key: string, fallback: string): string;\n}\n\n/** Sign in, then resume the action the person picked. */\nexport interface AuthResumePort {\n isSignedIn(): boolean;\n /** Stash {actionId, target fingerprint} and open sign-in; after sign-in the next matching menu host resumes it. */\n signInThenResume(pending: { actionId: string; contentFingerprint: string }): void;\n takePending(contentFingerprint: string): { actionId: string } | null;\n}\n\n/** Per-instance host dialogs (today's RichDocumentActionContextCallbacks). */\nexport interface HostDialogsPort {\n requestDelete?(target: ClickTarget): void;\n requestConvert?(target: ClickTarget): void;\n requestEditHistory?(target: ClickTarget): void;\n requestFullPrint?(target: ClickTarget): void;\n requestSaveTable?(table: { headers: string[]; rows: string[][] }): void;\n requestFlashcard?(answer: string): void;\n}\n\n/** The ports a host binds. Optional = absent capability when unbound (see above). */\nexport interface AlchemyHostPorts {\n // ALC-13\n kinds?: KindValidatorPort;\n /** REQUIRED from ALC-13 (the only non-optional port): contract breaks are captured here. */\n diagnostics: DiagnosticsPort;\n // ALC-14\n identity?: IdentityPort;\n persistence?: PersistencePort;\n // ALC-15\n store?: StorePort;\n notify?: NotifyPort;\n overlay?: OverlayHostPort;\n icons?: IconResolverPort;\n /** Organization defaults for menu knobs; a person's own choice is layered over them via persistence (N9). */\n knobs?: MenuKnobsPort;\n i18n?: I18nPort;\n auth?: AuthResumePort;\n dialogs?: HostDialogsPort;\n // ALC-16\n /** Clipboard and download for this runtime (the browser default ships in /operate). */\n transport?: TransportPort;\n /** Private or signed images → share-safe URLs before HTML is sealed. Absent = images keep their URL. */\n assets?: AssetResolverPort;\n /** Present when the host can reach aidream server operations; absent = server formats are not offered. */\n server?: ServerPort;\n // Preparation parity (PP-01a, PP-13a)\n /** Host-rendered windows for a LIVE preparation session (functions, subscriptions, sealed artifacts). */\n window?: WindowPort;\n /** Organization default recipes, resolved by the host's Feature Knob ladder (platform → organization). */\n transferKnobs?: TransferKnobsPort;\n // Surface Actions (Applets CONTRACTS §6)\n /** aidream `POST /actions/run`: a registered server action by name, as the caller, under the apply-policy cascade. */\n serverActions?: ServerActionPort;\n /** Runs a mandate by alias or key (the host binds `@ai-matrx/agents`; alchemy never imports it). */\n mandates?: MandatePort;\n /** The page's own handlers for `runs: \"page\"` Actions (in an applet frame: routed as `{ action, input, runId }`). */\n pageActions?: PageActionPort;\n /** Asks a person, in place, to approve an `ask` Action that an agent, applet or automation started. */\n approvals?: ApprovalPort;\n}\n\n// ── Surface Actions (Applets CONTRACTS §6) ──────────────────────────────────\n\nexport interface ServerActionPort {\n run(request: ServerActionRunRequest): Promise<ServerActionRunResponse>;\n}\nexport interface MandatePort {\n /** Resolves with the run's result (a kind envelope or JSON); rejects with the system's sentence. */\n run(request: MandateRunRequest): Promise<unknown>;\n}\nexport interface ApprovalRequest {\n action: string;\n label: string;\n description: string;\n input: Json;\n by: \"agent\" | \"applet\" | \"automation\";\n surfaceName: string;\n}\nexport interface ApprovalPort {\n /** Resolves true when the person approves; false when they decline or dismiss. */\n ask(request: ApprovalRequest): Promise<boolean>;\n}\nexport interface PageActionPort {\n dispatch(name: string, input: unknown, context: ActionContext): Promise<ActionResult<unknown>>;\n}\n\n// ── PP-01a — WindowPort ─────────────────────────────────────────────────────\n\n/**\n * Why a port and not `OverlayHostPort`: an overlay's `data` is `Json`, and a live preparation\n * session is not — it holds functions, subscriptions and sealed-artifact handles. So the workspace\n * never travels as overlay data; the package hands the host a render function and the host draws it\n * inside its own window system, with the session still live in the closure.\n */\nexport interface WindowSize {\n width: number;\n height: number;\n}\nexport interface WindowOpenOptions {\n /** The window's title, as a person reads it. */\n title: string;\n /** Preferred size; `\"full\"` = near-full-screen. The host clamps to the viewport. */\n size?: WindowSize | \"full\";\n /**\n * Stable identity. Opening an id that is already open focuses that window and draws the new\n * render in it instead of opening a second one.\n */\n instanceId?: string;\n /** Called exactly once when the window closes, whoever closed it (the person, `close()`, the host). */\n onClose?: () => void;\n}\nexport interface WindowHandle {\n readonly instanceId: string;\n /** Close the window; idempotent. */\n close(): void;\n /** Bring the window to the front; a no-op once closed. */\n focus(): void;\n isOpen(): boolean;\n}\nexport interface WindowPort {\n /**\n * What the host really draws. `\"window\"`: non-blocking, movable, resizable. `\"dialog\"`: a blocking\n * full-screen dialog — a surface on a dialog host never says \"window\" and never offers a second one.\n */\n readonly presentation: \"window\" | \"dialog\";\n /** True only when several windows can be open and usable at once. False = never offer \"open another\". */\n readonly multiInstance: boolean;\n /**\n * Draw `render(handle)` inside a host window. `render` returns a host UI node (a React element in\n * a React host) and is called by the host while the window is open.\n */\n open(render: (window: WindowHandle) => unknown, options: WindowOpenOptions): WindowHandle;\n}\n\n// ── PP-13a — TransferKnobsPort ──────────────────────────────────────────────\n\n/**\n * ORGANIZATION defaults for transfer, resolved by the host's Feature Knob ladder: the platform\n * default an admin sets, overridden by the organization. Never per-person — a person's own choice\n * lives only in `PersistencePort` and is layered over this by the package (`resolveDefaultRecipe`).\n */\nexport interface TransferKnobsPort {\n /**\n * The organization's default recipe for one source kind (the platform default when the\n * organization set none), or null when neither names one. A read that fails rejects; it never\n * answers null for \"could not ask\".\n */\n defaultRecipe(sourceKind: string, organizationId: string | null): Promise<Json | null>;\n}\n\n// ── ALC-16 ports (Operate) ──────────────────────────────────────────────────\n\n/** A transferable artifact as the transport sees it (structurally `@ai-matrx/alchemy/operate`'s Artifact). */\nexport interface TransportArtifact {\n format: string;\n plainText: string;\n html?: string;\n file?: { filename: string; mime: string; bytes: Uint8Array };\n}\nexport type TransportOutcome =\n | { status: \"success\"; delivered: \"clipboard\" | \"download-started\"; mimeTypes: string[] }\n | { status: \"degraded\"; delivered: \"plain-text\"; reason: string }\n | { status: \"cancelled\" }\n | { status: \"error\"; code: string; message: string; retryable: boolean };\n/** Clipboard and download for a non-browser runtime (MV3 service worker, Tauri — CONTRACT R8). */\nexport interface TransportPort {\n copy(artifact: TransportArtifact, signal: AbortSignal): Promise<TransportOutcome>;\n download(artifact: TransportArtifact, signal: AbortSignal): Promise<TransportOutcome>;\n}\nexport interface AssetResolverPort {\n resolve(url: string, signal: AbortSignal): Promise<{ url: string } | null>;\n}\nexport type ServerResult =\n | { status: \"bytes\"; filename: string; mime: string; bytes: Uint8Array; notices: readonly { code: string; sentence: string; remedy: string }[] }\n | { status: \"receipt\"; receipt: { status: string; sentence: string; remedy?: string } };\nexport interface ServerPort {\n call(operationId: string, input: Json, signal: AbortSignal): Promise<ServerResult>;\n}\n"],"mappings":"sjBAAA","names":[]}
package/dist/ports.d.cts CHANGED
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Matrx Alchemy — Declare types (CONTRACT §2.1).
3
+ *
4
+ * A declaration is plain JSON data (chair ruling N2): no functions, no classes,
5
+ * no React. Runtime behaviour lives in `MountedSurface` (`/declare/host`) and in
6
+ * handler registrations, never here. Zero React, zero `@ai-matrx/agents`.
7
+ */
8
+ type Json$1 = null | boolean | number | string | Json$1[] | {
9
+ [key: string]: Json$1;
10
+ };
1
11
  /** Unchanged from `ui_surface_value_type_chk`. */
2
12
  type ValueType = "string" | "number" | "boolean" | "object" | "array" | "document";
3
13
  type Classification = "ordinary" | "secret" | "credential";
@@ -204,6 +214,75 @@ interface ClickTarget {
204
214
  host?: unknown;
205
215
  }
206
216
 
217
+ /**
218
+ * Surface Actions — the shapes (Applets CONTRACTS §6, FROZEN; exported here as a superset).
219
+ *
220
+ * An Action is one thing a surface can have done to it, by a person, an agent,
221
+ * an applet or an automation: a write target, a client tool, a Kind Action or a
222
+ * row in `ui.ui_surface_action`. All of them project onto `ActionDeclaration`
223
+ * (`./declarations.ts`) and run through ONE runner (`./surface-run.ts`). There is
224
+ * never a second registry: `name` IS the alchemy action id, and `category`
225
+ * defaults to "applet" in alchemy's closed `ActionCategory` union.
226
+ *
227
+ * Types only — no runtime code, no React, no `@ai-matrx/agents`. `/ports`
228
+ * imports from here, so this file imports nothing but local types.
229
+ */
230
+
231
+ /** Who started a run. */
232
+ type ActionCaller = "person" | "agent" | "applet" | "automation";
233
+ /** Who may run it without a click: "manual" = only a person; "ask" = proposed first; "auto" = runs. */
234
+ type ActionPolicy = "ask" | "auto" | "manual";
235
+ interface ActionContext {
236
+ runId: string;
237
+ by: ActionCaller;
238
+ viewerId: string | null;
239
+ surfaceName?: string;
240
+ policy?: ActionPolicy;
241
+ }
242
+ /** The system's own sentence, never thrown into a UI. Superset of RecordsError (CONTRACTS §0 `AppletError`). */
243
+ interface ActionError {
244
+ code: string;
245
+ message: string;
246
+ retryable: boolean;
247
+ diagnostic?: string;
248
+ }
249
+ /** CONTRACTS §0 `Result<T>`. */
250
+ type ActionResult<T = unknown> = {
251
+ ok: true;
252
+ data: T;
253
+ } | {
254
+ ok: false;
255
+ error: ActionError;
256
+ };
257
+ /** `POST /actions/run` (aidream) — the request. */
258
+ interface ServerActionRunRequest {
259
+ name: string;
260
+ input: Json$1;
261
+ by: ActionCaller;
262
+ surface_name?: string;
263
+ policy?: ActionPolicy;
264
+ }
265
+ /** `POST /actions/run` (aidream) — the response. */
266
+ interface ServerActionRunResponse {
267
+ status: "applied" | "proposed" | "refused";
268
+ message: string;
269
+ receipts: Json$1[];
270
+ proposal?: Json$1;
271
+ }
272
+ /** A mandate run request: by alias (an Action's `target`) or by key. */
273
+ type MandateRunRequest = ({
274
+ alias: string;
275
+ key?: never;
276
+ } | {
277
+ key: string;
278
+ alias?: never;
279
+ }) & {
280
+ variables: Record<string, Json$1>;
281
+ userInput?: string;
282
+ by: ActionCaller;
283
+ policy: ActionPolicy;
284
+ };
285
+
207
286
  /**
208
287
  * `@ai-matrx/alchemy/ports` — the host ports (Matrx Alchemy CONTRACT §2.2).
209
288
  *
@@ -252,6 +331,12 @@ interface ClickTarget {
252
331
  * to an overlay carrying the session as data)
253
332
  * transferKnobs absent → no organization default recipe; a person's own
254
333
  * default (persistence) or the package's Full applies
334
+ * serverActions absent → `runs: "server"` Actions refuse with "not available here"
335
+ * mandates absent → `runs: "mandate"` Actions refuse the same way
336
+ * pageActions absent → `runs: "page"` Actions refuse the same way
337
+ * approvals absent → a policy "ask" Action started by an agent, applet or automation is refused
338
+ * ("needs a person's approval"); a person's own click is the approval
339
+ * (`runs: "navigate"` goes through `overlay.navigate`; overlay absent → refused)
255
340
  *
256
341
  * Types only: this entry has no runtime code.
257
342
  */
@@ -397,6 +482,36 @@ interface AlchemyHostPorts {
397
482
  window?: WindowPort;
398
483
  /** Organization default recipes, resolved by the host's Feature Knob ladder (platform → organization). */
399
484
  transferKnobs?: TransferKnobsPort;
485
+ /** aidream `POST /actions/run`: a registered server action by name, as the caller, under the apply-policy cascade. */
486
+ serverActions?: ServerActionPort;
487
+ /** Runs a mandate by alias or key (the host binds `@ai-matrx/agents`; alchemy never imports it). */
488
+ mandates?: MandatePort;
489
+ /** The page's own handlers for `runs: "page"` Actions (in an applet frame: routed as `{ action, input, runId }`). */
490
+ pageActions?: PageActionPort;
491
+ /** Asks a person, in place, to approve an `ask` Action that an agent, applet or automation started. */
492
+ approvals?: ApprovalPort;
493
+ }
494
+ interface ServerActionPort {
495
+ run(request: ServerActionRunRequest): Promise<ServerActionRunResponse>;
496
+ }
497
+ interface MandatePort {
498
+ /** Resolves with the run's result (a kind envelope or JSON); rejects with the system's sentence. */
499
+ run(request: MandateRunRequest): Promise<unknown>;
500
+ }
501
+ interface ApprovalRequest {
502
+ action: string;
503
+ label: string;
504
+ description: string;
505
+ input: Json;
506
+ by: "agent" | "applet" | "automation";
507
+ surfaceName: string;
508
+ }
509
+ interface ApprovalPort {
510
+ /** Resolves true when the person approves; false when they decline or dismiss. */
511
+ ask(request: ApprovalRequest): Promise<boolean>;
512
+ }
513
+ interface PageActionPort {
514
+ dispatch(name: string, input: unknown, context: ActionContext): Promise<ActionResult<unknown>>;
400
515
  }
401
516
  /**
402
517
  * Why a port and not `OverlayHostPort`: an overlay's `data` is `Json`, and a live preparation
@@ -515,4 +630,4 @@ interface ServerPort {
515
630
  call(operationId: string, input: Json, signal: AbortSignal): Promise<ServerResult>;
516
631
  }
517
632
 
518
- export type { AlchemyHostPorts, AssetResolverPort, AuthResumePort, ContextArrangementKnob, DensityKnob, DiagnosticsPort, HostDialogsPort, I18nPort, IconResolverPort, IdentityPort, Json, KindValidatorPort, MenuKnobsPort, NotifyPort, OverlayHostPort, PersistencePort, ServerPort, ServerResult, StorePort, TransferKnobsPort, TransportArtifact, TransportOutcome, TransportPort, WindowHandle, WindowOpenOptions, WindowPort, WindowSize };
633
+ export type { AlchemyHostPorts, ApprovalPort, ApprovalRequest, AssetResolverPort, AuthResumePort, ContextArrangementKnob, DensityKnob, DiagnosticsPort, HostDialogsPort, I18nPort, IconResolverPort, IdentityPort, Json, KindValidatorPort, MandatePort, MenuKnobsPort, NotifyPort, OverlayHostPort, PageActionPort, PersistencePort, ServerActionPort, ServerPort, ServerResult, StorePort, TransferKnobsPort, TransportArtifact, TransportOutcome, TransportPort, WindowHandle, WindowOpenOptions, WindowPort, WindowSize };
package/dist/ports.d.ts CHANGED
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Matrx Alchemy — Declare types (CONTRACT §2.1).
3
+ *
4
+ * A declaration is plain JSON data (chair ruling N2): no functions, no classes,
5
+ * no React. Runtime behaviour lives in `MountedSurface` (`/declare/host`) and in
6
+ * handler registrations, never here. Zero React, zero `@ai-matrx/agents`.
7
+ */
8
+ type Json$1 = null | boolean | number | string | Json$1[] | {
9
+ [key: string]: Json$1;
10
+ };
1
11
  /** Unchanged from `ui_surface_value_type_chk`. */
2
12
  type ValueType = "string" | "number" | "boolean" | "object" | "array" | "document";
3
13
  type Classification = "ordinary" | "secret" | "credential";
@@ -204,6 +214,75 @@ interface ClickTarget {
204
214
  host?: unknown;
205
215
  }
206
216
 
217
+ /**
218
+ * Surface Actions — the shapes (Applets CONTRACTS §6, FROZEN; exported here as a superset).
219
+ *
220
+ * An Action is one thing a surface can have done to it, by a person, an agent,
221
+ * an applet or an automation: a write target, a client tool, a Kind Action or a
222
+ * row in `ui.ui_surface_action`. All of them project onto `ActionDeclaration`
223
+ * (`./declarations.ts`) and run through ONE runner (`./surface-run.ts`). There is
224
+ * never a second registry: `name` IS the alchemy action id, and `category`
225
+ * defaults to "applet" in alchemy's closed `ActionCategory` union.
226
+ *
227
+ * Types only — no runtime code, no React, no `@ai-matrx/agents`. `/ports`
228
+ * imports from here, so this file imports nothing but local types.
229
+ */
230
+
231
+ /** Who started a run. */
232
+ type ActionCaller = "person" | "agent" | "applet" | "automation";
233
+ /** Who may run it without a click: "manual" = only a person; "ask" = proposed first; "auto" = runs. */
234
+ type ActionPolicy = "ask" | "auto" | "manual";
235
+ interface ActionContext {
236
+ runId: string;
237
+ by: ActionCaller;
238
+ viewerId: string | null;
239
+ surfaceName?: string;
240
+ policy?: ActionPolicy;
241
+ }
242
+ /** The system's own sentence, never thrown into a UI. Superset of RecordsError (CONTRACTS §0 `AppletError`). */
243
+ interface ActionError {
244
+ code: string;
245
+ message: string;
246
+ retryable: boolean;
247
+ diagnostic?: string;
248
+ }
249
+ /** CONTRACTS §0 `Result<T>`. */
250
+ type ActionResult<T = unknown> = {
251
+ ok: true;
252
+ data: T;
253
+ } | {
254
+ ok: false;
255
+ error: ActionError;
256
+ };
257
+ /** `POST /actions/run` (aidream) — the request. */
258
+ interface ServerActionRunRequest {
259
+ name: string;
260
+ input: Json$1;
261
+ by: ActionCaller;
262
+ surface_name?: string;
263
+ policy?: ActionPolicy;
264
+ }
265
+ /** `POST /actions/run` (aidream) — the response. */
266
+ interface ServerActionRunResponse {
267
+ status: "applied" | "proposed" | "refused";
268
+ message: string;
269
+ receipts: Json$1[];
270
+ proposal?: Json$1;
271
+ }
272
+ /** A mandate run request: by alias (an Action's `target`) or by key. */
273
+ type MandateRunRequest = ({
274
+ alias: string;
275
+ key?: never;
276
+ } | {
277
+ key: string;
278
+ alias?: never;
279
+ }) & {
280
+ variables: Record<string, Json$1>;
281
+ userInput?: string;
282
+ by: ActionCaller;
283
+ policy: ActionPolicy;
284
+ };
285
+
207
286
  /**
208
287
  * `@ai-matrx/alchemy/ports` — the host ports (Matrx Alchemy CONTRACT §2.2).
209
288
  *
@@ -252,6 +331,12 @@ interface ClickTarget {
252
331
  * to an overlay carrying the session as data)
253
332
  * transferKnobs absent → no organization default recipe; a person's own
254
333
  * default (persistence) or the package's Full applies
334
+ * serverActions absent → `runs: "server"` Actions refuse with "not available here"
335
+ * mandates absent → `runs: "mandate"` Actions refuse the same way
336
+ * pageActions absent → `runs: "page"` Actions refuse the same way
337
+ * approvals absent → a policy "ask" Action started by an agent, applet or automation is refused
338
+ * ("needs a person's approval"); a person's own click is the approval
339
+ * (`runs: "navigate"` goes through `overlay.navigate`; overlay absent → refused)
255
340
  *
256
341
  * Types only: this entry has no runtime code.
257
342
  */
@@ -397,6 +482,36 @@ interface AlchemyHostPorts {
397
482
  window?: WindowPort;
398
483
  /** Organization default recipes, resolved by the host's Feature Knob ladder (platform → organization). */
399
484
  transferKnobs?: TransferKnobsPort;
485
+ /** aidream `POST /actions/run`: a registered server action by name, as the caller, under the apply-policy cascade. */
486
+ serverActions?: ServerActionPort;
487
+ /** Runs a mandate by alias or key (the host binds `@ai-matrx/agents`; alchemy never imports it). */
488
+ mandates?: MandatePort;
489
+ /** The page's own handlers for `runs: "page"` Actions (in an applet frame: routed as `{ action, input, runId }`). */
490
+ pageActions?: PageActionPort;
491
+ /** Asks a person, in place, to approve an `ask` Action that an agent, applet or automation started. */
492
+ approvals?: ApprovalPort;
493
+ }
494
+ interface ServerActionPort {
495
+ run(request: ServerActionRunRequest): Promise<ServerActionRunResponse>;
496
+ }
497
+ interface MandatePort {
498
+ /** Resolves with the run's result (a kind envelope or JSON); rejects with the system's sentence. */
499
+ run(request: MandateRunRequest): Promise<unknown>;
500
+ }
501
+ interface ApprovalRequest {
502
+ action: string;
503
+ label: string;
504
+ description: string;
505
+ input: Json;
506
+ by: "agent" | "applet" | "automation";
507
+ surfaceName: string;
508
+ }
509
+ interface ApprovalPort {
510
+ /** Resolves true when the person approves; false when they decline or dismiss. */
511
+ ask(request: ApprovalRequest): Promise<boolean>;
512
+ }
513
+ interface PageActionPort {
514
+ dispatch(name: string, input: unknown, context: ActionContext): Promise<ActionResult<unknown>>;
400
515
  }
401
516
  /**
402
517
  * Why a port and not `OverlayHostPort`: an overlay's `data` is `Json`, and a live preparation
@@ -515,4 +630,4 @@ interface ServerPort {
515
630
  call(operationId: string, input: Json, signal: AbortSignal): Promise<ServerResult>;
516
631
  }
517
632
 
518
- export type { AlchemyHostPorts, AssetResolverPort, AuthResumePort, ContextArrangementKnob, DensityKnob, DiagnosticsPort, HostDialogsPort, I18nPort, IconResolverPort, IdentityPort, Json, KindValidatorPort, MenuKnobsPort, NotifyPort, OverlayHostPort, PersistencePort, ServerPort, ServerResult, StorePort, TransferKnobsPort, TransportArtifact, TransportOutcome, TransportPort, WindowHandle, WindowOpenOptions, WindowPort, WindowSize };
633
+ export type { AlchemyHostPorts, ApprovalPort, ApprovalRequest, AssetResolverPort, AuthResumePort, ContextArrangementKnob, DensityKnob, DiagnosticsPort, HostDialogsPort, I18nPort, IconResolverPort, IdentityPort, Json, KindValidatorPort, MandatePort, MenuKnobsPort, NotifyPort, OverlayHostPort, PageActionPort, PersistencePort, ServerActionPort, ServerPort, ServerResult, StorePort, TransferKnobsPort, TransportArtifact, TransportOutcome, TransportPort, WindowHandle, WindowOpenOptions, WindowPort, WindowSize };
@@ -1,4 +1,4 @@
1
1
  "use client";
2
- "use strict";var __create=Object.create;var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __getProtoOf=Object.getPrototypeOf,__hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toESM=(mod,isNodeMode,target)=>(target=mod!=null?__create(__getProtoOf(mod)):{},__copyProps(isNodeMode||!mod||!mod.__esModule?__defProp(target,"default",{value:mod,enumerable:!0}):target,mod)),__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var react_bar_exports={};__export(react_bar_exports,{ActionBar:()=>ActionBar});module.exports=__toCommonJS(react_bar_exports);var React4=__toESM(require("react"),1),import_design_system2=require("@ai-matrx/design-system");function sectionLabel(section){return section.label??null}function fold(section){return{kind:"submenu",id:`${section.id}:fold`,label:section.label,...section.icon!==void 0?{icon:section.icon}:{},children:section.nodes}}function rowCount(nodes){return nodes.filter(n=>n.kind!=="separator"&&n.kind!=="label").length}function placementOf(node){return"placement"in node&&node.placement?node.placement:"overflow"}function foldNamed(sections){return sections.map(s=>s.label&&!s.primary&&rowCount(s.nodes)>3?{id:s.id,group:s.group,nodes:[fold({...s,label:s.label})]}:s)}function bar(model,_options={}){let primary=[],overflowSections=[];for(let s of model.sections){let rest=[];for(let n of s.nodes){if(n.kind==="separator"||n.kind==="label"){rest.push(n);continue}let p=placementOf(n);(p==="primary"||p==="both")&&primary.push(n),(p==="overflow"||p==="both")&&rest.push(n)}rowCount(rest)>0&&overflowSections.push({...s,nodes:rest})}return{primary,overflow:foldNamed(overflowSections)}}var React2=__toESM(require("react"),1);var DEFAULT_SENSITIVITY=Object.freeze({exportable:!0,classification:"ordinary",includedByDefault:!0});function resolveSensitivity(value){return{exportable:value.sensitivity?.exportable??DEFAULT_SENSITIVITY.exportable,classification:value.sensitivity?.classification??DEFAULT_SENSITIVITY.classification,includedByDefault:value.sensitivity?.includedByDefault??DEFAULT_SENSITIVITY.includedByDefault}}function toJson(value){if(!(value===void 0||typeof value=="function"||typeof value=="symbol")){if(value===null||typeof value=="string"||typeof value=="boolean")return value;if(typeof value=="number")return Number.isFinite(value)?value:null;if(typeof value=="bigint")return value.toString();if(value instanceof Date)return value.toISOString();if(Array.isArray(value))return value.map(v=>toJson(v)??null);if(typeof value=="object"){let out={};for(let[k,v]of Object.entries(value)){let j=toJson(v);j!==void 0&&(out[k]=j)}return out}}}function projectValues(raw,declared,pathPrefix=""){let byName=new Map(declared.map(d=>[d.name,d])),values={},omissions=[];for(let[name,value]of Object.entries(raw)){let path=pathPrefix?`${pathPrefix}.${name}`:name,decl=byName.get(name);if(!decl){omissions.push({path,reason:"filtered"});continue}let sensitivity=resolveSensitivity(decl);if(sensitivity.classification!=="ordinary"){omissions.push({path,reason:"excluded",classification:sensitivity.classification});continue}if(!sensitivity.exportable){omissions.push({path,reason:"excluded"});continue}let json=toJson(value);json!==void 0&&(values[name]=json)}return{values,omissions}}function createProjectedReads(source){return{async values(signal){if(!source.surface||!source.declaration)return{values:{},omissions:[]};let raw=await source.surface.readValues(signal),screenValues=source.declaration.values;return projectValues(raw,screenValues)},async item(signal){let item=source.item;if(!item||!source.declaration)return null;let type=source.declaration.itemTypes?.find(t=>t.name===item.itemType);if(!type)return{values:{},omissions:[{path:item.itemType,reason:"filtered"}]};let raw=await item.readValues(signal);return projectValues(raw,type.values,item.itemType)},async live(signal){return source.surface?.readLive?source.surface.readLive(signal):{}}}}async function runAction(action,target,options){let{ports}=options;if(action.requiresAuth&&!target.auth.authenticated)return ports.auth?(ports.auth.signInThenResume({actionId:action.id,contentFingerprint:options.contentFingerprint??action.id}),{status:"auth-pending"}):(ports.diagnostics.capture(new Error(`requiresAuth action "${action.id}" ran signed out with no auth port`),{area:"actions",detail:{actionId:action.id}}),{status:"failed",error:new Error("sign-in unavailable")});let reads=createProjectedReads(options.source??{surface:null,declaration:target.declaration,item:null}),signal=options.signal??new AbortController().signal;try{return{status:"ran",outcome:await action.run(target,{reads,ports,signal})}}catch(error){ports.diagnostics.capture(error,{area:"actions",detail:{actionId:action.id,stage:"run"}});let label=typeof action.label=="string"?action.label:action.id;return ports.notify?.error(`${label} did not finish.`,"Try again; if it keeps failing, report it from the feedback button."),{status:"failed",error}}}var APPROVED_HEADINGS=new Set(["Save","Copy as","Export","Edit","Creator tools","Admin","App","Compare","History"]),CONTAINER_WORD=/^(more|more…|more\.\.\.|other|others|misc|miscellaneous|additional|extra|extras|actions|options|advanced|stuff|everything else)$/i,TARGET_SHAPE=/^(Cell|Row|Column|Table|Range|Selection|Record) · .+$/;function isContainerWord(label){return CONTAINER_WORD.test(label.trim())}function isApprovedHeading(label,kind="group",approved=APPROVED_HEADINGS){let l=label.trim();return!l||isContainerWord(l)?!1:approved.has(l)?!0:kind==="target"||TARGET_SHAPE.test(l)}function defaultHeadingPolicy(){return globalThis.process?.env?.NODE_ENV==="production"?"inline":"throw"}var UnapprovedHeadingError=class extends Error{heading;constructor(heading,where){super(`Menu heading "${heading}" (${where}) is not an approved heading. Remedy: show these rows inline \u2014 declare no section (or kind "target" for the clicked thing's own label) \u2014 or ask Arman to add the name to APPROVED_HEADINGS in @ai-matrx/alchemy/menu. A generic container word ("More\u2026", "Other") is never approved.`),this.name="UnapprovedHeadingError",this.heading=heading}};var CATEGORY_GROUP={clipboard:"clipboard",copy:"transfer",export:"transfer",save:"document",share:"document",edit:"editable",history:"history",ai:"ai",ask:"ai",listen:"tools",study:"tools",app:"tools",feedback:"quick",creator:"admin",admin:"admin","surface-info":"surface-info"},GROUP_ORDER=["clipboard","editable","transfer","document","history","ai","tools","quick","surface","admin","surface-info"],CATEGORY_ORDER=["clipboard","edit","copy","export","save","share","history","ask","ai","listen","study","app","feedback","creator","admin","surface-info"];function translate(i18n,key,fallback){if(!i18n)return fallback;try{return i18n.t(key,fallback)}catch{return fallback}}function actionToNode(resolved,target,options={}){let{action,eligibility}=resolved,rawLabel=typeof action.label=="function"?action.label(target):action.label,label=translate(options.i18n,action.labelKey??action.id,rawLabel),base={id:action.id,actionId:action.id,...action.icon!==void 0?{icon:action.icon}:{},...action.placement!==void 0?{placement:action.placement}:{}};if(action.expand)return{...base,kind:"submenu",label,lazy:!0,loading:!1,...action.pending?.(target)?{pending:!0}:{},children:[],...action.emptyLabel!==void 0?{emptyLabel:action.emptyLabel}:{}};if(action.href!==void 0){let href=typeof action.href=="function"?action.href(target):action.href;return{...base,kind:"link",label,href}}if(action.toggle)return{...base,kind:"checkbox",label,checked:action.pressed?.(target)??!1,...action.description!==void 0?{description:action.description}:{}};let state=action.stateIcon?.(target)??null,pressed=action.pressed?.(target),hint=action.hint!==void 0?translate(options.i18n,action.hintKey??`${action.id}.hint`,action.hint):void 0;return{...base,kind:"item",label,...action.iconTone!==void 0?{iconTone:action.iconTone}:{},...state?{stateIcon:state}:{},...action.description!==void 0?{description:action.description}:{},...hint!==void 0?{hint}:{},...pressed!==void 0?{pressed}:{},...action.destructive?{destructive:!0}:{},...action.preserveSelection?{preserveSelection:!0}:{},...action.keepsMenuOpen?{keepsMenuOpen:!0}:{},...eligibility.status==="unavailable-verb"||eligibility.status==="unavailable"?{unavailable:{sentence:eligibility.sentence}}:{}}}function buildMenuModel(target,resolved,options={}){let policy=options.headingPolicy??defaultHeadingPolicy();resolved=resolved.map(r=>{let sec=r.action.section;if(r.action.expand&&isContainerWord(typeof r.action.label=="string"?r.action.label:"")&&policy==="throw")throw new UnapprovedHeadingError(r.action.label,`submenu of ${r.action.id}`);if(!sec)return r;let kind=sec.kind??(sec.primary?"target":"group");if(isApprovedHeading(sec.label,kind))return r;if(policy==="throw")throw new UnapprovedHeadingError(sec.label,`section of ${r.action.id}`);let{section:_dropped,...rest}=r.action;return{...r,action:rest}});let ordered=resolved.map((r,index)=>({r,index})).sort((a,b)=>{let ca=CATEGORY_ORDER.indexOf(a.r.action.category),cb=CATEGORY_ORDER.indexOf(b.r.action.category);if(ca!==cb)return ca-cb;let oa=a.r.action.order??0,ob=b.r.action.order??0;return oa!==ob?oa-ob:a.index-b.index}).map(x=>x.r),sections=new Map,roles={},explicit=new Map;resolved.forEach((r,index)=>{let sec=r.action.section;if(!sec)return;let entry=explicit.get(sec.id)??{group:CATEGORY_GROUP[r.action.category],rows:[]};entry.rows.push({r,index}),explicit.set(sec.id,entry)});let placed=new Set;for(let r of ordered){let sec=r.action.section;if(sec&&placed.has(sec.id))continue;let group=sec?explicit.get(sec.id)?.group??CATEGORY_GROUP[r.action.category]:CATEGORY_GROUP[r.action.category],sectionId=sec?`section:${sec.id}`:group,section=sections.get(sectionId);section||(section={id:sectionId,group,nodes:[],...sec?{label:translate(options.i18n,sec.labelKey??`section.${sec.id}`,sec.label)}:{},...sec?.icon!==void 0?{icon:sec.icon}:{},...sec?.primary?{primary:!0}:{},...sec&&(sec.kind??(sec.primary?"target":"group"))==="target"?{target:!0}:{}},sections.set(sectionId,section));let rows=sec?(explicit.get(sec.id)?.rows??[]).sort((x,y)=>(x.r.action.order??0)-(y.r.action.order??0)||x.index-y.index).map(x=>x.r):[r];sec&&placed.add(sec.id);for(let row of rows)row.action.startsGroup&&section.nodes.length>0&&section.nodes.push({kind:"separator",id:`${row.action.id}:group`}),section.nodes.push(actionToNode(row,target,options)),row.action.verb&&(roles[row.action.verb]=row.action.id)}let list=[...sections.values()].sort((a,b)=>!!a.primary!=!!b.primary?a.primary?-1:1:GROUP_ORDER.indexOf(a.group)-GROUP_ORDER.indexOf(b.group)),selected=target.selection?.text?.trim(),content=options.content?.trim(),fieldName=options.contentLabel?.trim();return{header:selected?{label:"Selected",text:target.selection?.text??""}:fieldName?{label:fieldName,text:options.content??""}:content?{label:"Content",text:options.content??""}:null,sections:list,roles}}function withGroupDividers(children,nodes){let out=[];return children.forEach((child,i)=>{let node=nodes[i];node&&(i>0&&(child.startsGroup||(children[i-1].section?.id??null)!==(child.section?.id??null))&&out.push({kind:"separator",id:`${child.id}:group`}),out.push(node))}),out}function withExpandedChildren(model,nodeId,children){let visit=nodes=>nodes.map(n=>n.kind!=="submenu"?n:n.id===nodeId?children==="loading"?{...n,loading:!0}:{...n,lazy:!1,loading:!1,pending:!1,children}:{...n,children:visit(n.children)});return{...model,sections:model.sections.map(s=>({...s,nodes:visit(s.nodes)}))}}function findNode(nodes,id){for(let n of nodes){if(n.id===id)return n;if(n.kind==="submenu"){let hit=findNode(n.children,id);if(hit)return hit}}return null}var React=__toESM(require("react"),1),KEY=Symbol.for("ai-matrx.alchemy.actions-context"),AlchemyActionsContext=globalThis[KEY]??=React.createContext(null);function useAlchemyActions(){let value=React.useContext(AlchemyActionsContext);if(!value)throw new Error("Alchemy actions: render inside <AlchemyActionsProvider> (@ai-matrx/alchemy/react/host).");return value}function isLatestExpansion(capturedTargetEpoch,currentTargetEpoch,capturedNodeSequence,currentNodeSequence){return capturedTargetEpoch===currentTargetEpoch&&capturedNodeSequence===currentNodeSequence}var import_jsx_runtime=require("react/jsx-runtime");function nodeInModel(model,id){for(let s of model.sections){let hit=findNode(s.nodes,id);if(hit)return hit}return null}function hasLoadedRows(model,id){let n=nodeInModel(model,id);return!!(n&&n.kind==="submenu"&&!n.lazy&&!n.loading)}function loadedChildren(model,id){let n=nodeInModel(model,id);return n&&n.kind==="submenu"&&!n.lazy?n.children:[]}function carryOpened(prev,next,opened){return next.map(n=>{if(n.kind!=="submenu")return n;let old=prev.find(o=>o.id===n.id);return!old||old.kind!=="submenu"||old.lazy?n:n.lazy?opened.has(n.id)?{...n,lazy:!1,loading:!1,pending:!1,children:old.children}:n:{...n,children:carryOpened(old.children,n.children,opened)}})}function carryOpenedModel(prev,next,opened){if(opened.size===0)return next;let prevNodes=prev.sections.flatMap(s=>s.nodes);return{...next,sections:next.sections.map(s=>({...s,nodes:carryOpened(prevNodes,s.nodes,opened)}))}}function useMenuEngine(target,options={}){let{registry,ports}=useAlchemyActions(),[resolved,setResolved]=React2.useState([]),[model,setModel]=React2.useState(null),[tick,setTick]=React2.useState(0),expanded=React2.useRef(new Map),opened=React2.useRef(new Set),expandSeq=React2.useRef(new Map),targetEpoch=React2.useRef(0),currentTarget=React2.useRef(target);currentTarget.current!==target&&(currentTarget.current=target,targetEpoch.current+=1);let active=options.active??!0,optionsRef=React2.useRef(options);optionsRef.current=options,React2.useEffect(()=>registry.subscribe(()=>setTick(t=>t+1)),[registry]);let expandAction=React2.useCallback(async(action,nodeId)=>{if(!target||!action.expand)return;let epoch=targetEpoch.current,seq=(expandSeq.current.get(nodeId)??0)+1;expandSeq.current.set(nodeId,seq);let latest=()=>isLatestExpansion(epoch,targetEpoch.current,seq,expandSeq.current.get(nodeId));setModel(m=>m&&!hasLoadedRows(m,nodeId)?withExpandedChildren(m,nodeId,"loading"):m);try{let children=await action.expand(target,new AbortController().signal);if(!latest())return;let nodes=withGroupDividers(children,children.map(child=>(expanded.current.set(child.id,child),actionToNode({action:child,eligibility:{status:"available"}},target,ports.i18n?{i18n:ports.i18n}:{}))));setModel(m=>m&&withExpandedChildren(m,nodeId,carryOpened(loadedChildren(m,nodeId),nodes,opened.current)));for(let child of children)child.expand&&opened.current.has(child.id)&&expandAction(child,child.id)}catch(error){if(!latest())return;ports.diagnostics.capture(error,{area:"menu",detail:{nodeId,stage:"expand"}}),setModel(m=>m&&withExpandedChildren(m,nodeId,[]))}},[target,ports]);React2.useEffect(()=>{expanded.current.clear(),opened.current.clear()},[target]),React2.useEffect(()=>{if(!target||!active)return;let live=!0;return registry.resolve(target,optionsRef.current.restrict).then(list=>{if(!live)return;setResolved(list);let built=buildMenuModel(target,list,{...ports.i18n?{i18n:ports.i18n}:{},content:optionsRef.current.content??null,contentLabel:optionsRef.current.contentLabel??null});setModel(prev=>prev?carryOpenedModel(prev,built,opened.current):built);for(let{action}of list)opened.current.has(action.id)&&action.expand&&expandAction(action,action.id)}),()=>{live=!1}},[registry,ports,target,active,tick,options.revision,expandAction]);let resolvedRef=React2.useRef(resolved);resolvedRef.current=resolved;let subscribedIds=resolved.map(r=>r.action.id).join(`
3
- `);React2.useEffect(()=>{if(!target)return;let subscribing=!0,onChange=()=>{subscribing||setTick(t=>t+1)},offs=resolvedRef.current.map(r=>r.action.subscribe?.(onChange,target)).filter(off=>typeof off=="function");return subscribing=!1,()=>offs.forEach(off=>off())},[subscribedIds,target]);let findAction=React2.useCallback(id=>resolved.find(r=>r.action.id===id)?.action??expanded.current.get(id)??void 0,[resolved]),run=React2.useCallback(async nodeId=>{if(!target||!model)return null;let node=model.sections.map(s=>findNode(s.nodes,nodeId)).find(Boolean)??null,action=findAction(node&&"actionId"in node&&node.actionId?node.actionId:nodeId);if(!action)return null;let o=optionsRef.current,result=await runAction(action,target,{ports,...o.source?{source:o.source}:{},...o.contentFingerprint?{contentFingerprint:o.contentFingerprint}:{}});return o.onRan?.(nodeId,result,{keepOpen:!!action.keepsMenuOpen}),result},[target,model,findAction,ports]),expand=React2.useCallback(async nodeId=>{let action=findAction(nodeId);!target||!action?.expand||(opened.current.add(nodeId),await expandAction(action,nodeId))},[findAction,target,expandAction]),icon=React2.useCallback((key,tone)=>{if(!key||!ports.icons)return null;let Resolved=ports.icons.resolve(key);return Resolved?(0,import_jsx_runtime.jsx)(Resolved,{className:`h-4 w-4 shrink-0 ${tone??""}`,"aria-hidden":!0}):null},[ports.icons]),explain=React2.useCallback(sentence=>{ports.notify?ports.notify.info(sentence):ports.diagnostics.capture(new Error(`No notify port to explain: ${sentence}`),{area:"menu",detail:{sentence}})},[ports]);return{model,resolved,run,expand,icon,explain}}var React3=__toESM(require("react"),1),import_design_system=require("@ai-matrx/design-system");var import_jsx_runtime2=require("react/jsx-runtime");function NodeList({nodes,engine}){return(0,import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment,{children:nodes.map(node=>(0,import_jsx_runtime2.jsx)(MenuNodeView,{node,engine},node.id))})}function SectionList({sections,engine}){return(0,import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment,{children:sections.map((section,index)=>(0,import_jsx_runtime2.jsxs)(React3.Fragment,{children:[index>0?(0,import_jsx_runtime2.jsx)(import_design_system.DropdownMenuSeparator,{}):null,sectionLabel(section)?(0,import_jsx_runtime2.jsx)(import_design_system.DropdownMenuLabel,{className:"text-xs font-medium text-muted-foreground",children:sectionLabel(section)}):null,(0,import_jsx_runtime2.jsx)(NodeList,{nodes:section.nodes,engine})]},section.id))})}function iconOf(node,engine){if(node.kind!=="item")return"icon"in node?engine.icon(node.icon):null;if(node.stateIcon){let glyph=engine.icon(node.stateIcon.icon);return node.stateIcon.spin?(0,import_jsx_runtime2.jsx)("span",{className:"inline-flex animate-spin",children:glyph}):glyph}if(!("icon"in node))return null;let tone=node.pressed===!1?void 0:node.iconTone;return engine.icon(node.icon,tone)}function MenuNodeView({node,engine}){switch(node.kind){case"separator":return(0,import_jsx_runtime2.jsx)(import_design_system.DropdownMenuSeparator,{});case"label":return(0,import_jsx_runtime2.jsx)(import_design_system.DropdownMenuLabel,{className:"text-xs font-medium text-muted-foreground",children:node.label});case"submenu":return(0,import_jsx_runtime2.jsx)(SubmenuView,{node,engine});case"checkbox":return(0,import_jsx_runtime2.jsxs)(import_design_system.DropdownMenuCheckboxItem,{"data-alchemy-node":node.id,checked:node.checked,onSelect:()=>{engine.run(node.id)},className:"gap-2",children:[iconOf(node,engine),(0,import_jsx_runtime2.jsx)("span",{className:"truncate",children:node.label})]});case"link":return(0,import_jsx_runtime2.jsx)(import_design_system.DropdownMenuItem,{asChild:!0,"data-alchemy-node":node.id,children:(0,import_jsx_runtime2.jsxs)("a",{href:node.href,target:node.target,className:"gap-2",children:[iconOf(node,engine),(0,import_jsx_runtime2.jsx)("span",{className:"truncate",children:node.label})]})});case"item":return(0,import_jsx_runtime2.jsx)(LeafItem,{node,engine})}}function SubmenuView({node,engine}){let triggerRef=React3.useRef(null);return(0,import_jsx_runtime2.jsxs)(import_design_system.DropdownMenuSub,{onOpenChange:open=>{open&&node.lazy&&!node.loading&&engine.expand(node.id)},children:[(0,import_jsx_runtime2.jsxs)(import_design_system.DropdownMenuSubTrigger,{ref:triggerRef,"data-alchemy-node":node.id,className:"gap-2","aria-busy":node.pending?!0:void 0,children:[iconOf(node,engine),(0,import_jsx_runtime2.jsx)("span",{className:"truncate",children:node.label}),node.pending?(0,import_jsx_runtime2.jsx)("span",{className:"ml-auto text-xs text-muted-foreground",children:"Loading\u2026"}):null]}),(0,import_jsx_runtime2.jsx)(import_design_system.DropdownMenuSubContent,{"data-alchemy-submenu":node.id,onFocusOutside:event=>{let to=event.target,trigger=triggerRef.current;to instanceof Element&&trigger&&to.getAttribute("role")==="menu"&&to.contains(trigger)&&event.preventDefault()},className:"w-72 max-w-[calc(100vw-1rem)] max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto",children:node.loading?(0,import_jsx_runtime2.jsx)(import_design_system.DropdownMenuLabel,{className:"text-xs text-muted-foreground",children:"Loading\u2026"}):node.children.length===0&&!node.lazy?(0,import_jsx_runtime2.jsx)(import_design_system.DropdownMenuLabel,{className:"text-xs text-muted-foreground",children:node.emptyLabel??"Nothing here yet"}):(0,import_jsx_runtime2.jsx)(NodeList,{nodes:node.children,engine})})]})}function LeafItem({node,engine}){let row=(0,import_jsx_runtime2.jsxs)(import_design_system.DropdownMenuItem,{"data-alchemy-node":node.id,"aria-disabled":node.unavailable?!0:void 0,"aria-pressed":node.pressed,onSelect:event=>{if(node.unavailable){event.preventDefault();return}node.keepsMenuOpen&&event.preventDefault(),engine.run(node.id)},className:`gap-2 ${node.unavailable?"opacity-50":""} ${node.destructive?"text-destructive":""}`,children:[iconOf(node,engine),(0,import_jsx_runtime2.jsxs)("span",{className:"flex min-w-0 flex-1 flex-col",children:[(0,import_jsx_runtime2.jsx)("span",{className:"truncate",children:node.label}),node.description?(0,import_jsx_runtime2.jsx)("span",{className:"truncate text-xs text-muted-foreground",children:node.description}):null]}),node.hint?(0,import_jsx_runtime2.jsx)("span",{className:"ml-auto text-xs text-muted-foreground",children:node.hint}):null]});return node.unavailable?(0,import_jsx_runtime2.jsxs)(import_design_system.Tooltip,{children:[(0,import_jsx_runtime2.jsx)(import_design_system.TooltipTrigger,{asChild:!0,children:row}),(0,import_jsx_runtime2.jsx)(import_design_system.TooltipContent,{children:node.unavailable.sentence})]}):row}function LeafButton({node,engine,showLabel}){if(node.kind!=="item"&&node.kind!=="checkbox"&&node.kind!=="link")return null;let unavailable=node.kind==="item"?node.unavailable:void 0,pressed=node.kind==="item"?node.pressed:node.kind==="checkbox"?node.checked:void 0,glyph=iconOf(node,engine),button=(0,import_jsx_runtime2.jsxs)("button",{type:"button","data-alchemy-node":node.id,"data-matrx-button":"","data-tap-floor":"","data-tap-floor-square":"","aria-label":node.label,"aria-disabled":unavailable?!0:void 0,"aria-pressed":pressed,onMouseDown:e=>{node.kind==="item"&&node.preserveSelection&&e.preventDefault()},onClick:()=>{if(unavailable){engine.explain(unavailable.sentence);return}if(node.kind==="link"){window.location.assign(node.href);return}engine.run(node.id)},className:`inline-flex h-7 min-w-7 pointer-coarse:h-11 pointer-coarse:min-w-11 shrink-0 items-center justify-center gap-1.5 rounded-md px-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground ${unavailable?"cursor-default opacity-50 hover:bg-transparent":""} ${pressed?"text-foreground":""}`,children:[glyph,showLabel||!glyph?(0,import_jsx_runtime2.jsx)("span",{className:"whitespace-nowrap",children:node.label}):null]});return(0,import_jsx_runtime2.jsxs)(import_design_system.Tooltip,{children:[(0,import_jsx_runtime2.jsx)(import_design_system.TooltipTrigger,{asChild:!0,children:button}),(0,import_jsx_runtime2.jsx)(import_design_system.TooltipContent,{children:unavailable?unavailable.sentence:node.label})]})}var import_jsx_runtime3=require("react/jsx-runtime");function isNarrow(width){return typeof window>"u"?!1:window.matchMedia?.(`(max-width: ${width-1}px)`).matches??!1}var TRIGGER_CLASS="inline-flex h-7 min-w-7 pointer-coarse:h-11 pointer-coarse:min-w-11 items-center justify-center gap-1 rounded-md px-1.5 text-muted-foreground hover:bg-accent hover:text-foreground";function PrimarySubmenu({node,engine}){return(0,import_jsx_runtime3.jsxs)(import_design_system2.DropdownMenu,{onOpenChange:open=>{open&&node.lazy&&!node.loading&&engine.expand(node.id)},children:[(0,import_jsx_runtime3.jsx)(import_design_system2.DropdownMenuTrigger,{asChild:!0,children:(0,import_jsx_runtime3.jsx)("button",{type:"button","aria-label":node.label,"data-alchemy-node":node.id,className:TRIGGER_CLASS,children:engine.icon(node.icon)??(0,import_jsx_runtime3.jsx)("span",{className:"text-sm",children:node.label})})}),(0,import_jsx_runtime3.jsx)(import_design_system2.DropdownMenuContent,{align:"start",className:"w-64 max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto",children:node.loading?(0,import_jsx_runtime3.jsx)("div",{className:"px-2 py-1.5 text-xs text-muted-foreground",children:"Loading\u2026"}):(0,import_jsx_runtime3.jsx)(NodeList,{nodes:node.children,engine})})]})}function ActionBar({target,mini=!1,className,hideOverflow=!1,sheetBelow=768,loadSheet=()=>import("@ai-matrx/alchemy/react/sheet"),...options}){let engine=useMenuEngine(target,options),[sheetOpen,setSheetOpen]=React4.useState(!1),[Sheet,setSheet]=React4.useState(null);if(!engine.model)return null;let arranged=bar(engine.model,{mini});return arranged.primary.length===0&&arranged.overflow.length===0?null:(0,import_jsx_runtime3.jsx)(import_design_system2.TooltipProvider,{children:(0,import_jsx_runtime3.jsxs)("div",{role:"toolbar","aria-label":"Actions","data-alchemy-layout":"bar",className:`flex items-center ${mini?"gap-0":"gap-0.5"} ${className??""}`,children:[arranged.primary.map(node=>node.kind==="submenu"?(0,import_jsx_runtime3.jsx)(PrimarySubmenu,{node,engine},node.id):(0,import_jsx_runtime3.jsx)(LeafButton,{node,engine,showLabel:!1},node.id)),hideOverflow||arranged.overflow.length===0?null:isNarrow(sheetBelow)?(0,import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment,{children:[(0,import_jsx_runtime3.jsx)("button",{type:"button","aria-label":"More actions","data-alchemy-trigger":"overflow",className:TRIGGER_CLASS,onClick:()=>{setSheetOpen(!0),Sheet||loadSheet().then(m=>setSheet(()=>m.ActionSheet))},children:(0,import_jsx_runtime3.jsx)("span",{"aria-hidden":!0,className:"text-base leading-none",children:"\u22EF"})}),Sheet?(0,import_jsx_runtime3.jsx)(Sheet,{...options,target,open:sheetOpen,onOpenChange:setSheetOpen}):null]}):(0,import_jsx_runtime3.jsxs)(import_design_system2.DropdownMenu,{children:[(0,import_jsx_runtime3.jsx)(import_design_system2.DropdownMenuTrigger,{asChild:!0,children:(0,import_jsx_runtime3.jsx)("button",{type:"button","aria-label":"More actions","data-alchemy-trigger":"overflow",className:TRIGGER_CLASS,children:(0,import_jsx_runtime3.jsx)("span",{"aria-hidden":!0,className:"text-base leading-none",children:"\u22EF"})})}),(0,import_jsx_runtime3.jsx)(import_design_system2.DropdownMenuContent,{align:"end",className:"w-64 max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto",children:(0,import_jsx_runtime3.jsx)(SectionList,{sections:arranged.overflow,engine})})]})]})})}
2
+ "use strict";var __create=Object.create;var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __getProtoOf=Object.getPrototypeOf,__hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toESM=(mod,isNodeMode,target)=>(target=mod!=null?__create(__getProtoOf(mod)):{},__copyProps(isNodeMode||!mod||!mod.__esModule?__defProp(target,"default",{value:mod,enumerable:!0}):target,mod)),__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var react_bar_exports={};__export(react_bar_exports,{ActionBar:()=>ActionBar});module.exports=__toCommonJS(react_bar_exports);var React5=__toESM(require("react"),1),import_design_system2=require("@ai-matrx/design-system");function sectionLabel(section){return section.label??null}function fold(section){return{kind:"submenu",id:`${section.id}:fold`,label:section.label,...section.icon!==void 0?{icon:section.icon}:{},children:section.nodes}}function rowCount(nodes){return nodes.filter(n=>n.kind!=="separator"&&n.kind!=="label").length}function placementOf(node){return"placement"in node&&node.placement?node.placement:"overflow"}function foldNamed(sections){return sections.map(s=>s.label&&!s.primary&&rowCount(s.nodes)>3?{id:s.id,group:s.group,nodes:[fold({...s,label:s.label})]}:s)}function bar(model,_options={}){let primary=[],overflowSections=[];for(let s of model.sections){let rest=[];for(let n of s.nodes){if(n.kind==="separator"||n.kind==="label"){rest.push(n);continue}let p=placementOf(n);(p==="primary"||p==="both")&&primary.push(n),(p==="overflow"||p==="both")&&rest.push(n)}rowCount(rest)>0&&overflowSections.push({...s,nodes:rest})}return{primary,overflow:foldNamed(overflowSections)}}var React2=__toESM(require("react"),1);var DEFAULT_SENSITIVITY=Object.freeze({exportable:!0,classification:"ordinary",includedByDefault:!0});function resolveSensitivity(value){return{exportable:value.sensitivity?.exportable??DEFAULT_SENSITIVITY.exportable,classification:value.sensitivity?.classification??DEFAULT_SENSITIVITY.classification,includedByDefault:value.sensitivity?.includedByDefault??DEFAULT_SENSITIVITY.includedByDefault}}function toJson(value){if(!(value===void 0||typeof value=="function"||typeof value=="symbol")){if(value===null||typeof value=="string"||typeof value=="boolean")return value;if(typeof value=="number")return Number.isFinite(value)?value:null;if(typeof value=="bigint")return value.toString();if(value instanceof Date)return value.toISOString();if(Array.isArray(value))return value.map(v=>toJson(v)??null);if(typeof value=="object"){let out={};for(let[k,v]of Object.entries(value)){let j=toJson(v);j!==void 0&&(out[k]=j)}return out}}}function projectValues(raw,declared,pathPrefix=""){let byName=new Map(declared.map(d=>[d.name,d])),values={},omissions=[];for(let[name,value]of Object.entries(raw)){let path=pathPrefix?`${pathPrefix}.${name}`:name,decl=byName.get(name);if(!decl){omissions.push({path,reason:"filtered"});continue}let sensitivity=resolveSensitivity(decl);if(sensitivity.classification!=="ordinary"){omissions.push({path,reason:"excluded",classification:sensitivity.classification});continue}if(!sensitivity.exportable){omissions.push({path,reason:"excluded"});continue}let json=toJson(value);json!==void 0&&(values[name]=json)}return{values,omissions}}function createProjectedReads(source){return{async values(signal){if(!source.surface||!source.declaration)return{values:{},omissions:[]};let raw=await source.surface.readValues(signal),screenValues=source.declaration.values;return projectValues(raw,screenValues)},async item(signal){let item=source.item;if(!item||!source.declaration)return null;let type=source.declaration.itemTypes?.find(t=>t.name===item.itemType);if(!type)return{values:{},omissions:[{path:item.itemType,reason:"filtered"}]};let raw=await item.readValues(signal);return projectValues(raw,type.values,item.itemType)},async live(signal){return source.surface?.readLive?source.surface.readLive(signal):{}}}}async function runAction(action,target,options){let{ports}=options;if(action.requiresAuth&&!target.auth.authenticated)return ports.auth?(ports.auth.signInThenResume({actionId:action.id,contentFingerprint:options.contentFingerprint??action.id}),{status:"auth-pending"}):(ports.diagnostics.capture(new Error(`requiresAuth action "${action.id}" ran signed out with no auth port`),{area:"actions",detail:{actionId:action.id}}),{status:"failed",error:new Error("sign-in unavailable")});let reads=createProjectedReads(options.source??{surface:null,declaration:target.declaration,item:null}),signal=options.signal??new AbortController().signal;try{return{status:"ran",outcome:await action.run(target,{reads,ports,signal})}}catch(error){ports.diagnostics.capture(error,{area:"actions",detail:{actionId:action.id,stage:"run"}});let label=typeof action.label=="string"?action.label:action.id;return ports.notify?.error(`${label} did not finish.`,"Try again; if it keeps failing, report it from the feedback button."),{status:"failed",error}}}var APPROVED_HEADINGS=new Set(["Save","Copy as","Export","Edit","Creator tools","Admin","App","Compare","History"]),CONTAINER_WORD=/^(more|more…|more\.\.\.|other|others|misc|miscellaneous|additional|extra|extras|actions|options|advanced|stuff|everything else)$/i,TARGET_SHAPE=/^(Cell|Row|Column|Table|Range|Selection|Record) · .+$/;function isContainerWord(label){return CONTAINER_WORD.test(label.trim())}function isApprovedHeading(label,kind="group",approved=APPROVED_HEADINGS){let l=label.trim();return!l||isContainerWord(l)?!1:approved.has(l)?!0:kind==="target"||TARGET_SHAPE.test(l)}function defaultHeadingPolicy(){return globalThis.process?.env?.NODE_ENV==="production"?"inline":"throw"}var UnapprovedHeadingError=class extends Error{heading;constructor(heading,where){super(`Menu heading "${heading}" (${where}) is not an approved heading. Remedy: show these rows inline \u2014 declare no section (or kind "target" for the clicked thing's own label) \u2014 or ask Arman to add the name to APPROVED_HEADINGS in @ai-matrx/alchemy/menu. A generic container word ("More\u2026", "Other") is never approved.`),this.name="UnapprovedHeadingError",this.heading=heading}};var CATEGORY_GROUP={clipboard:"clipboard",copy:"transfer",export:"transfer",save:"document",share:"document",edit:"editable",history:"history",ai:"ai",ask:"ai",listen:"tools",study:"tools",app:"tools",applet:"tools",feedback:"quick",creator:"admin",admin:"admin","surface-info":"surface-info"},GROUP_ORDER=["clipboard","editable","transfer","document","history","ai","tools","quick","surface","admin","surface-info"],CATEGORY_ORDER=["clipboard","edit","copy","export","save","share","history","ask","ai","listen","study","app","applet","feedback","creator","admin","surface-info"];function translate(i18n,key,fallback){if(!i18n)return fallback;try{return i18n.t(key,fallback)}catch{return fallback}}function actionToNode(resolved,target,options={}){let{action,eligibility}=resolved,rawLabel=typeof action.label=="function"?action.label(target):action.label,label=translate(options.i18n,action.labelKey??action.id,rawLabel),base={id:action.id,actionId:action.id,...action.icon!==void 0?{icon:action.icon}:{},...action.placement!==void 0?{placement:action.placement}:{}};if(action.expand)return{...base,kind:"submenu",label,lazy:!0,loading:!1,...action.pending?.(target)?{pending:!0}:{},children:[],...action.emptyLabel!==void 0?{emptyLabel:action.emptyLabel}:{}};if(action.href!==void 0){let href=typeof action.href=="function"?action.href(target):action.href;return{...base,kind:"link",label,href}}if(action.toggle)return{...base,kind:"checkbox",label,checked:action.pressed?.(target)??!1,...action.description!==void 0?{description:action.description}:{}};let state=action.stateIcon?.(target)??null,pressed=action.pressed?.(target),hint=action.hint!==void 0?translate(options.i18n,action.hintKey??`${action.id}.hint`,action.hint):void 0;return{...base,kind:"item",label,...action.iconTone!==void 0?{iconTone:action.iconTone}:{},...state?{stateIcon:state}:{},...action.description!==void 0?{description:action.description}:{},...hint!==void 0?{hint}:{},...pressed!==void 0?{pressed}:{},...action.destructive?{destructive:!0}:{},...action.preserveSelection?{preserveSelection:!0}:{},...action.keepsMenuOpen?{keepsMenuOpen:!0}:{},...eligibility.status==="unavailable-verb"||eligibility.status==="unavailable"?{unavailable:{sentence:eligibility.sentence}}:{}}}function buildMenuModel(target,resolved,options={}){let policy=options.headingPolicy??defaultHeadingPolicy();resolved=resolved.map(r=>{let sec=r.action.section;if(r.action.expand&&isContainerWord(typeof r.action.label=="string"?r.action.label:"")&&policy==="throw")throw new UnapprovedHeadingError(r.action.label,`submenu of ${r.action.id}`);if(!sec)return r;let kind=sec.kind??(sec.primary?"target":"group");if(isApprovedHeading(sec.label,kind))return r;if(policy==="throw")throw new UnapprovedHeadingError(sec.label,`section of ${r.action.id}`);let{section:_dropped,...rest}=r.action;return{...r,action:rest}});let ordered=resolved.map((r,index)=>({r,index})).sort((a,b)=>{let ca=CATEGORY_ORDER.indexOf(a.r.action.category),cb=CATEGORY_ORDER.indexOf(b.r.action.category);if(ca!==cb)return ca-cb;let oa=a.r.action.order??0,ob=b.r.action.order??0;return oa!==ob?oa-ob:a.index-b.index}).map(x=>x.r),sections=new Map,roles={},explicit=new Map;resolved.forEach((r,index)=>{let sec=r.action.section;if(!sec)return;let entry=explicit.get(sec.id)??{group:CATEGORY_GROUP[r.action.category],rows:[]};entry.rows.push({r,index}),explicit.set(sec.id,entry)});let placed=new Set;for(let r of ordered){let sec=r.action.section;if(sec&&placed.has(sec.id))continue;let group=sec?explicit.get(sec.id)?.group??CATEGORY_GROUP[r.action.category]:CATEGORY_GROUP[r.action.category],sectionId=sec?`section:${sec.id}`:group,section=sections.get(sectionId);section||(section={id:sectionId,group,nodes:[],...sec?{label:translate(options.i18n,sec.labelKey??`section.${sec.id}`,sec.label)}:{},...sec?.icon!==void 0?{icon:sec.icon}:{},...sec?.primary?{primary:!0}:{},...sec&&(sec.kind??(sec.primary?"target":"group"))==="target"?{target:!0}:{}},sections.set(sectionId,section));let rows=sec?(explicit.get(sec.id)?.rows??[]).sort((x,y)=>(x.r.action.order??0)-(y.r.action.order??0)||x.index-y.index).map(x=>x.r):[r];sec&&placed.add(sec.id);for(let row of rows)row.action.startsGroup&&section.nodes.length>0&&section.nodes.push({kind:"separator",id:`${row.action.id}:group`}),section.nodes.push(actionToNode(row,target,options)),row.action.verb&&(roles[row.action.verb]=row.action.id)}let list=[...sections.values()].sort((a,b)=>!!a.primary!=!!b.primary?a.primary?-1:1:GROUP_ORDER.indexOf(a.group)-GROUP_ORDER.indexOf(b.group)),selected=target.selection?.text?.trim(),content=options.content?.trim(),fieldName=options.contentLabel?.trim();return{header:selected?{label:"Selected",text:target.selection?.text??""}:fieldName?{label:fieldName,text:options.content??""}:content?{label:"Content",text:options.content??""}:null,sections:list,roles}}function withGroupDividers(children,nodes){let out=[];return children.forEach((child,i)=>{let node=nodes[i];node&&(i>0&&(child.startsGroup||(children[i-1].section?.id??null)!==(child.section?.id??null))&&out.push({kind:"separator",id:`${child.id}:group`}),out.push(node))}),out}function withExpandedChildren(model,nodeId,children){let visit=nodes=>nodes.map(n=>n.kind!=="submenu"?n:n.id===nodeId?children==="loading"?{...n,loading:!0}:{...n,lazy:!1,loading:!1,pending:!1,children}:{...n,children:visit(n.children)});return{...model,sections:model.sections.map(s=>({...s,nodes:visit(s.nodes)}))}}function findNode(nodes,id){for(let n of nodes){if(n.id===id)return n;if(n.kind==="submenu"){let hit=findNode(n.children,id);if(hit)return hit}}return null}var React=__toESM(require("react"),1),KEY=Symbol.for("ai-matrx.alchemy.actions-context"),AlchemyActionsContext=globalThis[KEY]??=React.createContext(null);function useAlchemyActions(){let value=React.useContext(AlchemyActionsContext);if(!value)throw new Error("Alchemy actions: render inside <AlchemyActionsProvider> (@ai-matrx/alchemy/react/host).");return value}function isLatestExpansion(capturedTargetEpoch,currentTargetEpoch,capturedNodeSequence,currentNodeSequence){return capturedTargetEpoch===currentTargetEpoch&&capturedNodeSequence===currentNodeSequence}var import_jsx_runtime=require("react/jsx-runtime");function nodeInModel(model,id){for(let s of model.sections){let hit=findNode(s.nodes,id);if(hit)return hit}return null}function hasLoadedRows(model,id){let n=nodeInModel(model,id);return!!(n&&n.kind==="submenu"&&!n.lazy&&!n.loading)}function loadedChildren(model,id){let n=nodeInModel(model,id);return n&&n.kind==="submenu"&&!n.lazy?n.children:[]}function carryOpened(prev,next,opened){return next.map(n=>{if(n.kind!=="submenu")return n;let old=prev.find(o=>o.id===n.id);return!old||old.kind!=="submenu"||old.lazy?n:n.lazy?opened.has(n.id)?{...n,lazy:!1,loading:!1,pending:!1,children:old.children}:n:{...n,children:carryOpened(old.children,n.children,opened)}})}function carryOpenedModel(prev,next,opened){if(opened.size===0)return next;let prevNodes=prev.sections.flatMap(s=>s.nodes);return{...next,sections:next.sections.map(s=>({...s,nodes:carryOpened(prevNodes,s.nodes,opened)}))}}function useMenuEngine(target,options={}){let{registry,ports}=useAlchemyActions(),[resolved,setResolved]=React2.useState([]),[model,setModel]=React2.useState(null),[tick,setTick]=React2.useState(0),expanded=React2.useRef(new Map),opened=React2.useRef(new Set),expandSeq=React2.useRef(new Map),targetEpoch=React2.useRef(0),currentTarget=React2.useRef(target);currentTarget.current!==target&&(currentTarget.current=target,targetEpoch.current+=1);let active=options.active??!0,optionsRef=React2.useRef(options);optionsRef.current=options,React2.useEffect(()=>registry.subscribe(()=>setTick(t=>t+1)),[registry]);let expandAction=React2.useCallback(async(action,nodeId)=>{if(!target||!action.expand)return;let epoch=targetEpoch.current,seq=(expandSeq.current.get(nodeId)??0)+1;expandSeq.current.set(nodeId,seq);let latest=()=>isLatestExpansion(epoch,targetEpoch.current,seq,expandSeq.current.get(nodeId));setModel(m=>m&&!hasLoadedRows(m,nodeId)?withExpandedChildren(m,nodeId,"loading"):m);try{let children=await action.expand(target,new AbortController().signal);if(!latest())return;let nodes=withGroupDividers(children,children.map(child=>(expanded.current.set(child.id,child),actionToNode({action:child,eligibility:{status:"available"}},target,ports.i18n?{i18n:ports.i18n}:{}))));setModel(m=>m&&withExpandedChildren(m,nodeId,carryOpened(loadedChildren(m,nodeId),nodes,opened.current)));for(let child of children)child.expand&&opened.current.has(child.id)&&expandAction(child,child.id)}catch(error){if(!latest())return;ports.diagnostics.capture(error,{area:"menu",detail:{nodeId,stage:"expand"}}),setModel(m=>m&&withExpandedChildren(m,nodeId,[]))}},[target,ports]);React2.useEffect(()=>{expanded.current.clear(),opened.current.clear()},[target]),React2.useEffect(()=>{if(!target||!active)return;let live=!0;return registry.resolve(target,optionsRef.current.restrict).then(list=>{if(!live)return;setResolved(list);let built=buildMenuModel(target,list,{...ports.i18n?{i18n:ports.i18n}:{},content:optionsRef.current.content??null,contentLabel:optionsRef.current.contentLabel??null});setModel(prev=>prev?carryOpenedModel(prev,built,opened.current):built);for(let{action}of list)opened.current.has(action.id)&&action.expand&&expandAction(action,action.id)}),()=>{live=!1}},[registry,ports,target,active,tick,options.revision,expandAction]);let resolvedRef=React2.useRef(resolved);resolvedRef.current=resolved;let subscribedIds=resolved.map(r=>r.action.id).join(`
3
+ `);React2.useEffect(()=>{if(!target)return;let subscribing=!0,onChange=()=>{subscribing||setTick(t=>t+1)},offs=resolvedRef.current.map(r=>r.action.subscribe?.(onChange,target)).filter(off=>typeof off=="function");return subscribing=!1,()=>offs.forEach(off=>off())},[subscribedIds,target]);let findAction=React2.useCallback(id=>resolved.find(r=>r.action.id===id)?.action??expanded.current.get(id)??void 0,[resolved]),run=React2.useCallback(async nodeId=>{if(!target||!model)return null;let node=model.sections.map(s=>findNode(s.nodes,nodeId)).find(Boolean)??null,action=findAction(node&&"actionId"in node&&node.actionId?node.actionId:nodeId);if(!action)return null;let o=optionsRef.current,result=await runAction(action,target,{ports,...o.source?{source:o.source}:{},...o.contentFingerprint?{contentFingerprint:o.contentFingerprint}:{}});return o.onRan?.(nodeId,result,{keepOpen:!!action.keepsMenuOpen}),result},[target,model,findAction,ports]),expand=React2.useCallback(async nodeId=>{let action=findAction(nodeId);!target||!action?.expand||(opened.current.add(nodeId),await expandAction(action,nodeId))},[findAction,target,expandAction]),icon=React2.useCallback((key,tone)=>{if(!key||!ports.icons)return null;let Resolved=ports.icons.resolve(key);return Resolved?(0,import_jsx_runtime.jsx)(Resolved,{className:`h-4 w-4 shrink-0 ${tone??""}`,"aria-hidden":!0}):null},[ports.icons]),explain=React2.useCallback(sentence=>{ports.notify?ports.notify.info(sentence):ports.diagnostics.capture(new Error(`No notify port to explain: ${sentence}`),{area:"menu",detail:{sentence}})},[ports]);return{model,resolved,run,expand,icon,explain}}var React4=__toESM(require("react"),1),import_design_system=require("@ai-matrx/design-system");var React3=__toESM(require("react"),1),MenuSizeContext=React3.createContext("standard");function useMenuSize(){return React3.useContext(MenuSizeContext)}var MENU_SIZE_CLASSES={standard:{panel:"w-72",row:"gap-2",strip:"flex items-center gap-0.5 px-1 py-1",stripButton:"h-7 min-w-7 px-1.5",label:"text-xs",filter:"h-7 text-sm",hint:"text-xs"},large:{panel:"w-[22.5rem] [&_svg]:size-[18px]",row:"gap-3 min-h-9 px-3 text-[15px]",strip:"flex items-center justify-between gap-1 px-2 py-1.5",stripButton:"h-9 min-w-9 px-2",label:"text-[13px]",filter:"h-9 text-[15px]",hint:"text-[13px]"}};var import_jsx_runtime2=require("react/jsx-runtime");function NodeList({nodes,engine}){return(0,import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment,{children:nodes.map(node=>(0,import_jsx_runtime2.jsx)(MenuNodeView,{node,engine},node.id))})}function SectionList({sections,engine}){let size=MENU_SIZE_CLASSES[useMenuSize()];return(0,import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment,{children:sections.map((section,index)=>(0,import_jsx_runtime2.jsxs)(React4.Fragment,{children:[index>0?(0,import_jsx_runtime2.jsx)(import_design_system.DropdownMenuSeparator,{}):null,sectionLabel(section)?(0,import_jsx_runtime2.jsx)(import_design_system.DropdownMenuLabel,{className:`${size.label} font-medium text-muted-foreground`,children:sectionLabel(section)}):null,(0,import_jsx_runtime2.jsx)(NodeList,{nodes:section.nodes,engine})]},section.id))})}function iconOf(node,engine){if(node.kind!=="item")return"icon"in node?engine.icon(node.icon):null;if(node.stateIcon){let glyph=engine.icon(node.stateIcon.icon);return node.stateIcon.spin?(0,import_jsx_runtime2.jsx)("span",{className:"inline-flex animate-spin",children:glyph}):glyph}if(!("icon"in node))return null;let tone=node.pressed===!1?void 0:node.iconTone;return engine.icon(node.icon,tone)}function MenuNodeView({node,engine}){let size=MENU_SIZE_CLASSES[useMenuSize()];switch(node.kind){case"separator":return(0,import_jsx_runtime2.jsx)(import_design_system.DropdownMenuSeparator,{});case"label":return(0,import_jsx_runtime2.jsx)(import_design_system.DropdownMenuLabel,{className:`${size.label} font-medium text-muted-foreground`,children:node.label});case"submenu":return(0,import_jsx_runtime2.jsx)(SubmenuView,{node,engine});case"checkbox":return(0,import_jsx_runtime2.jsxs)(import_design_system.DropdownMenuCheckboxItem,{"data-alchemy-node":node.id,checked:node.checked,onSelect:()=>{engine.run(node.id)},className:size.row,children:[iconOf(node,engine),(0,import_jsx_runtime2.jsx)("span",{className:"truncate",children:node.label})]});case"link":return(0,import_jsx_runtime2.jsx)(import_design_system.DropdownMenuItem,{asChild:!0,"data-alchemy-node":node.id,children:(0,import_jsx_runtime2.jsxs)("a",{href:node.href,target:node.target,className:size.row,children:[iconOf(node,engine),(0,import_jsx_runtime2.jsx)("span",{className:"truncate",children:node.label})]})});case"item":return(0,import_jsx_runtime2.jsx)(LeafItem,{node,engine})}}function SubmenuView({node,engine}){let triggerRef=React4.useRef(null),size=MENU_SIZE_CLASSES[useMenuSize()];return(0,import_jsx_runtime2.jsxs)(import_design_system.DropdownMenuSub,{onOpenChange:open=>{open&&node.lazy&&!node.loading&&engine.expand(node.id)},children:[(0,import_jsx_runtime2.jsxs)(import_design_system.DropdownMenuSubTrigger,{ref:triggerRef,"data-alchemy-node":node.id,className:size.row,"aria-busy":node.pending?!0:void 0,children:[iconOf(node,engine),(0,import_jsx_runtime2.jsx)("span",{className:"truncate",children:node.label}),node.pending?(0,import_jsx_runtime2.jsx)("span",{className:"ml-auto text-xs text-muted-foreground",children:"Loading\u2026"}):null]}),(0,import_jsx_runtime2.jsx)(import_design_system.DropdownMenuSubContent,{"data-alchemy-submenu":node.id,onFocusOutside:event=>{let to=event.target,trigger=triggerRef.current;to instanceof Element&&trigger&&to.getAttribute("role")==="menu"&&to.contains(trigger)&&event.preventDefault()},className:`${size.panel} max-w-[calc(100vw-1rem)] max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto`,children:node.loading?(0,import_jsx_runtime2.jsx)(import_design_system.DropdownMenuLabel,{className:"text-xs text-muted-foreground",children:"Loading\u2026"}):node.children.length===0&&!node.lazy?(0,import_jsx_runtime2.jsx)(import_design_system.DropdownMenuLabel,{className:"text-xs text-muted-foreground",children:node.emptyLabel??"Nothing here yet"}):(0,import_jsx_runtime2.jsx)(NodeList,{nodes:node.children,engine})})]})}function LeafItem({node,engine}){let size=MENU_SIZE_CLASSES[useMenuSize()],row=(0,import_jsx_runtime2.jsxs)(import_design_system.DropdownMenuItem,{"data-alchemy-node":node.id,"aria-disabled":node.unavailable?!0:void 0,"aria-pressed":node.pressed,onSelect:event=>{if(node.unavailable){event.preventDefault();return}node.keepsMenuOpen&&event.preventDefault(),engine.run(node.id)},className:`${size.row} ${node.unavailable?"opacity-50":""} ${node.destructive?"text-destructive":""}`,children:[iconOf(node,engine),(0,import_jsx_runtime2.jsxs)("span",{className:"flex min-w-0 flex-1 flex-col",children:[(0,import_jsx_runtime2.jsx)("span",{className:"truncate",children:node.label}),node.description?(0,import_jsx_runtime2.jsx)("span",{className:"truncate text-xs text-muted-foreground",children:node.description}):null]}),node.hint?(0,import_jsx_runtime2.jsx)("span",{className:`ml-auto ${size.hint} text-muted-foreground`,children:node.hint}):null]});return node.unavailable?(0,import_jsx_runtime2.jsxs)(import_design_system.Tooltip,{children:[(0,import_jsx_runtime2.jsx)(import_design_system.TooltipTrigger,{asChild:!0,children:row}),(0,import_jsx_runtime2.jsx)(import_design_system.TooltipContent,{children:node.unavailable.sentence})]}):row}function LeafButton({node,engine,showLabel}){if(node.kind!=="item"&&node.kind!=="checkbox"&&node.kind!=="link")return null;let unavailable=node.kind==="item"?node.unavailable:void 0,pressed=node.kind==="item"?node.pressed:node.kind==="checkbox"?node.checked:void 0,glyph=iconOf(node,engine),size=MENU_SIZE_CLASSES[useMenuSize()],destructive=node.kind==="item"&&node.destructive,button=(0,import_jsx_runtime2.jsxs)("button",{type:"button","data-alchemy-node":node.id,"data-matrx-button":"","data-tap-floor":"","data-tap-floor-square":"","aria-label":node.label,"aria-disabled":unavailable?!0:void 0,"aria-pressed":pressed,onMouseDown:e=>{node.kind==="item"&&node.preserveSelection&&e.preventDefault()},onClick:()=>{if(unavailable){engine.explain(unavailable.sentence);return}if(node.kind==="link"){window.location.assign(node.href);return}engine.run(node.id)},className:`inline-flex ${size.stripButton} pointer-coarse:h-11 pointer-coarse:min-w-11 shrink-0 items-center justify-center gap-1.5 rounded-md text-sm ${destructive?"text-destructive hover:bg-destructive/10":"text-muted-foreground hover:bg-accent hover:text-foreground"} ${unavailable?"cursor-default opacity-50 hover:bg-transparent":""} ${pressed?"text-foreground":""}`,children:[glyph,showLabel||!glyph?(0,import_jsx_runtime2.jsx)("span",{className:"whitespace-nowrap",children:node.label}):null]});return(0,import_jsx_runtime2.jsxs)(import_design_system.Tooltip,{children:[(0,import_jsx_runtime2.jsx)(import_design_system.TooltipTrigger,{asChild:!0,children:button}),(0,import_jsx_runtime2.jsx)(import_design_system.TooltipContent,{children:unavailable?unavailable.sentence:node.label})]})}var import_jsx_runtime3=require("react/jsx-runtime");function isNarrow(width){return typeof window>"u"?!1:window.matchMedia?.(`(max-width: ${width-1}px)`).matches??!1}var TRIGGER_CLASS="inline-flex h-7 min-w-7 pointer-coarse:h-11 pointer-coarse:min-w-11 items-center justify-center gap-1 rounded-md px-1.5 text-muted-foreground hover:bg-accent hover:text-foreground";function PrimarySubmenu({node,engine}){return(0,import_jsx_runtime3.jsxs)(import_design_system2.DropdownMenu,{onOpenChange:open=>{open&&node.lazy&&!node.loading&&engine.expand(node.id)},children:[(0,import_jsx_runtime3.jsx)(import_design_system2.DropdownMenuTrigger,{asChild:!0,children:(0,import_jsx_runtime3.jsx)("button",{type:"button","aria-label":node.label,"data-alchemy-node":node.id,className:TRIGGER_CLASS,children:engine.icon(node.icon)??(0,import_jsx_runtime3.jsx)("span",{className:"text-sm",children:node.label})})}),(0,import_jsx_runtime3.jsx)(import_design_system2.DropdownMenuContent,{align:"start",className:"w-64 max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto",children:node.loading?(0,import_jsx_runtime3.jsx)("div",{className:"px-2 py-1.5 text-xs text-muted-foreground",children:"Loading\u2026"}):(0,import_jsx_runtime3.jsx)(NodeList,{nodes:node.children,engine})})]})}function ActionBar({target,mini=!1,className,hideOverflow=!1,sheetBelow=768,loadSheet=()=>import("@ai-matrx/alchemy/react/sheet"),...options}){let engine=useMenuEngine(target,options),[sheetOpen,setSheetOpen]=React5.useState(!1),[Sheet,setSheet]=React5.useState(null);if(!engine.model)return null;let arranged=bar(engine.model,{mini});return arranged.primary.length===0&&arranged.overflow.length===0?null:(0,import_jsx_runtime3.jsx)(import_design_system2.TooltipProvider,{children:(0,import_jsx_runtime3.jsxs)("div",{role:"toolbar","aria-label":"Actions","data-alchemy-layout":"bar",className:`flex items-center ${mini?"gap-0":"gap-0.5"} ${className??""}`,children:[arranged.primary.map(node=>node.kind==="submenu"?(0,import_jsx_runtime3.jsx)(PrimarySubmenu,{node,engine},node.id):(0,import_jsx_runtime3.jsx)(LeafButton,{node,engine,showLabel:!1},node.id)),hideOverflow||arranged.overflow.length===0?null:isNarrow(sheetBelow)?(0,import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment,{children:[(0,import_jsx_runtime3.jsx)("button",{type:"button","aria-label":"More actions","data-alchemy-trigger":"overflow",className:TRIGGER_CLASS,onClick:()=>{setSheetOpen(!0),Sheet||loadSheet().then(m=>setSheet(()=>m.ActionSheet))},children:(0,import_jsx_runtime3.jsx)("span",{"aria-hidden":!0,className:"text-base leading-none",children:"\u22EF"})}),Sheet?(0,import_jsx_runtime3.jsx)(Sheet,{...options,target,open:sheetOpen,onOpenChange:setSheetOpen}):null]}):(0,import_jsx_runtime3.jsxs)(import_design_system2.DropdownMenu,{children:[(0,import_jsx_runtime3.jsx)(import_design_system2.DropdownMenuTrigger,{asChild:!0,children:(0,import_jsx_runtime3.jsx)("button",{type:"button","aria-label":"More actions","data-alchemy-trigger":"overflow",className:TRIGGER_CLASS,children:(0,import_jsx_runtime3.jsx)("span",{"aria-hidden":!0,className:"text-base leading-none",children:"\u22EF"})})}),(0,import_jsx_runtime3.jsx)(import_design_system2.DropdownMenuContent,{align:"end",className:"w-64 max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto",children:(0,import_jsx_runtime3.jsx)(SectionList,{sections:arranged.overflow,engine})})]})]})})}
4
4
  //# sourceMappingURL=react-bar.cjs.map