@ai-matrx/context-menu 0.0.0-stage → 0.1.0

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 (46) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/LICENSE +21 -0
  3. package/README.md +15 -2
  4. package/dist/builder.cjs +1665 -0
  5. package/dist/builder.cjs.map +1 -0
  6. package/dist/builder.d.cts +22 -0
  7. package/dist/builder.d.ts +22 -0
  8. package/dist/builder.js +1650 -0
  9. package/dist/builder.js.map +1 -0
  10. package/dist/design-store.cjs +195 -0
  11. package/dist/design-store.cjs.map +1 -0
  12. package/dist/design-store.d.cts +42 -0
  13. package/dist/design-store.d.ts +42 -0
  14. package/dist/design-store.js +175 -0
  15. package/dist/design-store.js.map +1 -0
  16. package/dist/engine.cjs +3069 -0
  17. package/dist/engine.cjs.map +1 -0
  18. package/dist/engine.d.cts +731 -0
  19. package/dist/engine.d.ts +731 -0
  20. package/dist/engine.js +3250 -0
  21. package/dist/engine.js.map +1 -0
  22. package/dist/grouping-CB3zlD1T.d.ts +623 -0
  23. package/dist/grouping-CGcWsltu.d.cts +623 -0
  24. package/dist/host-types-AOiEYwR8.d.cts +9 -0
  25. package/dist/host-types-AOiEYwR8.d.ts +9 -0
  26. package/dist/index.cjs +486 -0
  27. package/dist/index.cjs.map +1 -0
  28. package/dist/index.d.cts +46 -0
  29. package/dist/index.d.ts +46 -0
  30. package/dist/index.js +464 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/menu-content.cjs +3146 -0
  33. package/dist/menu-content.cjs.map +1 -0
  34. package/dist/menu-content.d.cts +30 -0
  35. package/dist/menu-content.d.ts +30 -0
  36. package/dist/menu-content.js +3197 -0
  37. package/dist/menu-content.js.map +1 -0
  38. package/dist/model-BdF0TqZ3.d.cts +107 -0
  39. package/dist/model-BdF0TqZ3.d.ts +107 -0
  40. package/dist/react.cjs +3322 -0
  41. package/dist/react.cjs.map +1 -0
  42. package/dist/react.d.cts +133 -0
  43. package/dist/react.d.ts +133 -0
  44. package/dist/react.js +3345 -0
  45. package/dist/react.js.map +1 -0
  46. package/package.json +168 -4
@@ -0,0 +1,623 @@
1
+ import * as _ai_matrx_icons from '@ai-matrx/icons';
2
+ import * as lucide_react from 'lucide-react';
3
+ import './host-types-AOiEYwR8.js';
4
+ import { PlacementKey, PlacementVisibility, ContextMenuExtraSection, MenuContentProps } from '@ai-matrx/chat/context-menu/types';
5
+ import { ComponentType } from 'react';
6
+ import { ResolvedSurfaceManifest } from '@ai-matrx/chat/surfaces/types';
7
+ import { SklRenderDefinition } from '@ai-matrx/chat/agents/redux/agent-shortcuts/skl-types';
8
+ import { ConfirmOptions } from '@ai-matrx/kit/confirm-opener';
9
+ import { RichDocumentActionContext, RichDocumentAction } from '@ai-matrx/rich-content/rich-document/types';
10
+ import { ApplicationScope } from '@ai-matrx/chat/agents/types/scope.types';
11
+ import { AgentShortcutRecord } from '@ai-matrx/chat/agents/redux/agent-shortcuts/types';
12
+ import { AgentShortcutCategoryRecord } from '@ai-matrx/chat/agents/redux/agent-shortcut-categories/types';
13
+ import { Scope } from '@ai-matrx/chat/agents/redux/shared/scope';
14
+ import { SurfaceBoundAgentSection, SurfaceBoundAgentEntry } from '@ai-matrx/chat/surfaces/services/surface-bound-agents.service';
15
+ import { SelectionRange } from '@ai-matrx/chat/context-menu/utils/selection-tracking';
16
+ import { JsonDetection } from '@ai-matrx/kit/json-format';
17
+ import { ActionCategory, ResolvedAction, Action, ClickTarget } from '@ai-matrx/alchemy/actions';
18
+ import { ContextArrangement, MenuModel } from '@ai-matrx/alchemy/menu';
19
+
20
+ /**
21
+ * THE CONTEXT MENU HOST PORT — everything the engine needs that only the app
22
+ * running it can answer.
23
+ *
24
+ * The engine (model, layouts, handlers, data fetch, scope resolution) lives
25
+ * in this package. What stays with the host is identity-shaped: which overlay
26
+ * opens for "Find", what the app's diff window is, which of its admin lanes
27
+ * is open, its surface-manifest registry, its toast/confirm/manual-copy
28
+ * doors. A host registers them ONCE at boot:
29
+ *
30
+ * configureContextMenuHost({ useBindings, getSurfaceManifest, ... });
31
+ *
32
+ * Organization, user id and toasts are NOT here — they come through the chat
33
+ * package's host (`@ai-matrx/chat/host/*`), which every menu host already
34
+ * configures because the menu launches agents.
35
+ *
36
+ * Every member has a shipped default. A default never pretends: it either
37
+ * does the honest minimum (kit `confirm`) or announces once, by name, that
38
+ * the host did not wire it (`[context-menu] host port "<name>" is not wired`).
39
+ *
40
+ * The registration lives on `globalThis` under a `Symbol.for` key so the ESM
41
+ * and CJS builds (and a host that loads both) share one host.
42
+ */
43
+
44
+ interface ListenSummaryWindowOptions {
45
+ agentId: string;
46
+ agentName: string | null;
47
+ sourceText: string;
48
+ style: string;
49
+ autoPlay: boolean;
50
+ }
51
+ interface DiffWindowOptions {
52
+ original: string;
53
+ modified: string;
54
+ originalLabel: string;
55
+ modifiedLabel: string;
56
+ title: string;
57
+ engine: "light";
58
+ }
59
+ interface FindReplaceOptions {
60
+ getTargetElement: () => HTMLElement | null;
61
+ onReplace: ((nextText: string) => void) | undefined;
62
+ }
63
+ /** What the reference picker hands back. */
64
+ interface ReferencePick {
65
+ /** The canonical ```matrx fence carrying the minified one-line shell. */
66
+ fence: string;
67
+ /** The bare minified shell (what the fence wraps). */
68
+ shell: string;
69
+ /** Grammar class the fence carries. */
70
+ directiveClass: string;
71
+ /** Reference type token on the wire (`conversation`, `note`, `file`, `url`, …). */
72
+ type: string;
73
+ /** Display title of the picked record, when known. */
74
+ title: string | null;
75
+ delivery: "insert" | "copy";
76
+ }
77
+ interface ReferencePickerOptions {
78
+ mode: "insert" | "copy";
79
+ onPicked: (pick: ReferencePick) => void;
80
+ }
81
+ interface ContextAssignmentOptions {
82
+ subject: {
83
+ entityType: string;
84
+ entityId: string;
85
+ title: string | undefined;
86
+ };
87
+ }
88
+ interface LinkRecordSheetOptions {
89
+ target: {
90
+ token: string;
91
+ id: string;
92
+ title: string | undefined;
93
+ };
94
+ }
95
+ interface ShareModalOptions {
96
+ resourceType: string;
97
+ resourceId: string;
98
+ resourceName: string | undefined;
99
+ }
100
+ interface SurfaceContextWindowOptions {
101
+ surfaceName: string;
102
+ isEditable: boolean;
103
+ }
104
+ interface SurfaceAgentBindOptions {
105
+ surfaceName: string;
106
+ }
107
+ interface SurfaceInspectorOptions {
108
+ surfaceName: string | null;
109
+ scope: unknown;
110
+ isEditable: boolean;
111
+ preferRuntime?: boolean | undefined;
112
+ }
113
+ interface CompareBaseInput {
114
+ content: string;
115
+ label: string;
116
+ language: string | null;
117
+ }
118
+ /** The quick launchers the menu's "Open" group offers. */
119
+ interface ContextMenuQuickActions {
120
+ openChatWindow: () => void;
121
+ openQuickChat: () => void;
122
+ openQuickData: () => void;
123
+ openQuickFiles: () => void;
124
+ openQuickNotes: () => void;
125
+ openQuickTasks: () => void;
126
+ openVoicePad: () => void;
127
+ }
128
+ /** Everything the engine reads or opens per render. Returned by the host's `useBindings` HOOK. */
129
+ interface ContextMenuHostBindings {
130
+ /** Admin DEBUGGING power on this page (surface key, menu version, Admin Tools). */
131
+ isAdminDebugger: boolean;
132
+ /** Admin POWER on this page (Surface Context Admin, admin document actions). */
133
+ hasAdminPower: boolean;
134
+ isDebugMode: boolean;
135
+ isAdminIndicatorOpen: boolean;
136
+ hasCompareBase: boolean;
137
+ /** The person's selected scopes/context, landed on the ambient scope key. */
138
+ activeScopeIds: unknown;
139
+ toggleDebugMode: () => void;
140
+ toggleAdminIndicator: () => void;
141
+ setCompareBase: (input: CompareBaseInput) => void;
142
+ /** Resolves false when there was no base to compare with. */
143
+ openCompareWithBase: (input: {
144
+ current: string;
145
+ currentLabel: string;
146
+ }) => Promise<boolean>;
147
+ openListenSummaryWindow: (options: ListenSummaryWindowOptions) => void;
148
+ openDiffWindow: (options: DiffWindowOptions) => void;
149
+ openFindReplace: (options: FindReplaceOptions) => void;
150
+ openReferencePicker: (options: ReferencePickerOptions) => void;
151
+ openContextAssignment: (options: ContextAssignmentOptions) => void;
152
+ openLinkRecordSheet: (options: LinkRecordSheetOptions) => void;
153
+ openShareModalWindow: (options: ShareModalOptions) => void;
154
+ openStateViewer: () => void;
155
+ openSurfaceInspector: (options: SurfaceInspectorOptions) => void;
156
+ openSurfaceContextWindow: (options: SurfaceContextWindowOptions) => void;
157
+ openBindAgent: (options: SurfaceAgentBindOptions) => void;
158
+ quickActions: ContextMenuQuickActions;
159
+ }
160
+ interface ManualCopyOptions {
161
+ text: string;
162
+ title?: string | undefined;
163
+ description?: string | undefined;
164
+ }
165
+ /** Props the host's document-review component receives (the "AI result replaces the selection" flow). */
166
+ type DocumentAgentReviewProps = Record<string, unknown>;
167
+ interface ContextMenuHost {
168
+ /** A React HOOK, called once per open menu, unconditionally. */
169
+ useBindings: () => ContextMenuHostBindings;
170
+ /** The host's surface-manifest registry. */
171
+ getSurfaceManifest: (surfaceName: string) => ResolvedSurfaceManifest | undefined;
172
+ /** The render-definition ("content block") rows already in the host store. */
173
+ selectRenderDefinitions: (state: unknown) => readonly SklRenderDefinition[];
174
+ confirm: (options: ConfirmOptions) => Promise<boolean>;
175
+ /** The last-resort dialog when the clipboard refuses a write. */
176
+ showManualCopy: (options: ManualCopyOptions) => void;
177
+ /** The surface whose `spoken_summary` role every surface falls back to. */
178
+ listeningHomeSurface: string;
179
+ /** The host's document agent review (rendered when an AI result can replace a selection). */
180
+ DocumentAgentReview: ComponentType<DocumentAgentReviewProps> | null;
181
+ }
182
+ declare const DEFAULT_CONTEXT_MENU_HOST: ContextMenuHost;
183
+ /** Register the host's answers. Partial: anything omitted keeps its default. */
184
+ declare function configureContextMenuHost(host: Partial<ContextMenuHost>): void;
185
+ /** The registered host (defaults where the host registered nothing). */
186
+ declare function getContextMenuHost(): ContextMenuHost;
187
+ /** Tests: back to the shipped defaults. */
188
+ declare function resetContextMenuHost(): void;
189
+ /** The per-render bindings — a hook; call it at the top level of a component or hook. */
190
+ declare function useContextMenuHostBindings(): ContextMenuHostBindings;
191
+
192
+ type AgentMenuEntry = ({
193
+ entryType: "agent_shortcut";
194
+ scopeLevel: Scope;
195
+ } & AgentShortcutRecord) | ({
196
+ entryType: "content_block";
197
+ scopeLevel: Scope;
198
+ } & SklRenderDefinition);
199
+ interface AgentMenuCategoryGroup {
200
+ category: AgentShortcutCategoryRecord & {
201
+ scopeLevel: Scope;
202
+ };
203
+ items: AgentMenuEntry[];
204
+ children: AgentMenuCategoryGroup[];
205
+ }
206
+ interface UseUnifiedAgentContextMenuArgs {
207
+ /**
208
+ * Which placement submenus this host renders. LAYOUT ONLY — see
209
+ * `buildCategoryGroups`. It never decides whether an ITEM is available.
210
+ */
211
+ placementTypes: string[];
212
+ /**
213
+ * The Surface Registry `ui_surface.name` (`<client>/<surface>`) this menu is
214
+ * mounted on, or null on a page the platform cannot name. It is the
215
+ * page-level rung of the scope hierarchy and the key to the exclusion valve.
216
+ */
217
+ surfaceName?: string | null | undefined;
218
+ /**
219
+ * Every surface value name that has a READ PATH here: the manifest's
220
+ * declared values + the baseline floor + whatever actually landed in the
221
+ * resolved scope. Built by `buildAvailableKeys` in the engine.
222
+ */
223
+ availableKeys: ReadonlySet<string>;
224
+ /** True when the resolved scope carries a non-empty `selection`. */
225
+ hasSelection: boolean;
226
+ /** The surface's exclusion valve (`menu` surface-config namespace). */
227
+ excludedItemIds?: ReadonlySet<string> | undefined;
228
+ enabled?: boolean | undefined;
229
+ scope?: Scope | undefined;
230
+ scopeId?: string | null | undefined;
231
+ }
232
+ interface UseUnifiedAgentContextMenuResult {
233
+ categoryGroups: AgentMenuCategoryGroup[];
234
+ loading: boolean;
235
+ error: string | null;
236
+ refresh: () => Promise<void>;
237
+ }
238
+ interface BuildCategoryGroupsArgs {
239
+ placementTypes: string[];
240
+ surfaceName?: string | null | undefined;
241
+ availableKeys: ReadonlySet<string>;
242
+ hasSelection: boolean;
243
+ excludedItemIds?: ReadonlySet<string> | undefined;
244
+ shortcuts: AgentShortcutRecord[];
245
+ categories: AgentShortcutCategoryRecord[];
246
+ contentBlocks: SklRenderDefinition[];
247
+ }
248
+ /**
249
+ * THE grouping function — pure and unit-tested
250
+ * (`__tests__/build-category-groups.test.ts`,
251
+ * `../model/__tests__/requirement-gate.test.ts`). Turns the fetched
252
+ * shortcut/category/content-block rows into the nested category tree each
253
+ * placement submenu renders.
254
+ *
255
+ * 🚨 PHASE 6.7 — AVAILABILITY IS DERIVED (THE-MODEL law 3). An item is
256
+ * offered here iff every surface value it consumes has a read path on this
257
+ * surface; then its authored scope (global / domain / page) must reach here;
258
+ * then the surface's exclusion valve gets the last word. The 16 hardcoded
259
+ * `enabled_features` slugs and the RED `legacyMatch` rows they produced are
260
+ * GONE: an item qualifies or is absent, and nothing renders as broken.
261
+ *
262
+ * 🚨 CATEGORIES GROUP, THEY NEVER GATE. A category's `placementType` still
263
+ * decides WHICH placement submenu it renders under — that is layout, and it
264
+ * is the only thing `placementTypes` does here. A category is never dropped
265
+ * for failing a context filter, and an item's availability never consults its
266
+ * category.
267
+ *
268
+ * Guarantees the tests certify:
269
+ * - the derived gate: missing key → absent; key declared-but-empty → present;
270
+ * - the hierarchy: global reaches everywhere, a domain reaches its surfaces,
271
+ * a page-pinned item reaches only its page;
272
+ * - the valve: an excluded id is absent even though it qualified;
273
+ * - placement fidelity: a row renders under exactly its category's
274
+ * placementType;
275
+ * - nested categories nest under their parent, sorted by sortOrder then label;
276
+ * - an EMPTY category still returns (with `items: []`) so the UI can render it
277
+ * greyed — never silently dropped;
278
+ * - an entry with no agent is kept (the renderer disables it as "Not configured");
279
+ * - scope precedence dedupe (task > project > user > organization > global),
280
+ * ties broken by screen specificity (page > domain > global), then id —
281
+ * never by row order.
282
+ */
283
+ declare function buildCategoryGroups(args: BuildCategoryGroupsArgs): AgentMenuCategoryGroup[];
284
+ declare function useUnifiedAgentContextMenu(args: UseUnifiedAgentContextMenuArgs): UseUnifiedAgentContextMenuResult;
285
+
286
+ interface ResolveScopeArgs {
287
+ /** Live scope builder from the surface — wins when present. */
288
+ getApplicationScope?: (() => ApplicationScope) | undefined;
289
+ /** Static + per-target-merged value payload from the surface. */
290
+ contextData: Record<string, unknown>;
291
+ /** The text the user has selected (captured by the shell at open). */
292
+ selectedText: string;
293
+ /** The captured selection range (drives text_before / text_after / content). */
294
+ selectionRange: SelectionRange | null;
295
+ /**
296
+ * Plain text of the right-clicked DOM subtree, captured by the shell. Used as
297
+ * `content` ONLY when nothing else resolved it — the net that stops a
298
+ * read-only surface from presenting a dead Copy.
299
+ */
300
+ fallbackContent?: string | undefined;
301
+ /**
302
+ * The surface this menu declared. Used to adopt the LIVE mounted
303
+ * `SurfaceRuntime` for that same surface as an underlay (see
304
+ * `adoptMountedRuntimeScope`).
305
+ */
306
+ surfaceName?: string | undefined;
307
+ /**
308
+ * AMBIENT STATE — what is true around the user right now (active org, active
309
+ * scopes, surface, whether a run is streaming). Collected by the engine from
310
+ * Redux and landed on ONE namespaced `ambient` key, never spread across the
311
+ * scope. The weakest layer there is: a surface that emits its own `ambient`
312
+ * keeps it untouched.
313
+ */
314
+ ambient?: Record<string, unknown> | undefined;
315
+ }
316
+ declare function resolveApplicationScope(args: ResolveScopeArgs): ApplicationScope;
317
+ /**
318
+ * The text Copy / AI actions should operate on: the selection if there is one,
319
+ * otherwise the resolved primary content. This is what makes Copy work on a
320
+ * read-only surface where the user merely right-clicked.
321
+ */
322
+ interface ResolvedActionText {
323
+ text: string;
324
+ source: "selection" | "content" | "none";
325
+ }
326
+ declare function resolveActionText(scope: ApplicationScope): ResolvedActionText;
327
+ /** True when there is anything for the content-acting half of the menu to do. */
328
+ declare function hasActionableContent(scope: ApplicationScope): boolean;
329
+ interface InertMenuDiagnostic {
330
+ /** True when the menu opened with nothing meaningful to do. */
331
+ inert: boolean;
332
+ reasons: string[];
333
+ }
334
+ /**
335
+ * A menu is INERT when, on a read-only surface, there is no selection, no
336
+ * resolvable content, and the surface injected no items of its own — i.e. the
337
+ * user sees Copy/AI rows that do nothing. (Editable surfaces are never inert:
338
+ * Paste is always meaningful.)
339
+ */
340
+ declare function detectInertMenu(args: {
341
+ scope: ApplicationScope;
342
+ isEditable: boolean;
343
+ hasExtraSections: boolean;
344
+ }): InertMenuDiagnostic;
345
+ /**
346
+ * Compare a surface's DECLARED `alwaysAvailable` values against what actually
347
+ * landed in the resolved scope. A declared-but-missing value silently breaks
348
+ * the shortcuts/agents that depend on it — the exact "values not passed"
349
+ * failure. Baseline values are skipped (they are empty-floored by design, so a
350
+ * missing one is not an error). Returns human-readable warnings.
351
+ */
352
+ declare function auditSurfaceScope(surfaceName: string | undefined, scope: ApplicationScope): string[];
353
+ /**
354
+ * Run both loud guards and SCREAM in dev. A recovery/diagnostic layer that fires
355
+ * means a real defect got past the surface wiring — per doctrine it must be
356
+ * impossible to miss, not a silent default. No-op in production.
357
+ */
358
+ declare function reportMenuDiagnostics(args: {
359
+ surfaceName: string | undefined;
360
+ scope: ApplicationScope;
361
+ isEditable: boolean;
362
+ hasExtraSections: boolean;
363
+ }): InertMenuDiagnostic;
364
+
365
+ interface JsonMenuAction {
366
+ id: string;
367
+ label: string;
368
+ /** Real before -> after size, e.g. "13 lines -> 3 lines". */
369
+ hint?: string | undefined;
370
+ disabled?: boolean | undefined;
371
+ run: () => void | Promise<void>;
372
+ }
373
+ interface JsonMenuSection {
374
+ /** The detection that produced this section (null when the text isn't JSON-ish). */
375
+ detection: JsonDetection;
376
+ /** True when the surface will rewrite text; false when actions copy instead. */
377
+ writes: boolean;
378
+ actions: JsonMenuAction[];
379
+ }
380
+ interface BuildJsonMenuActionsParams {
381
+ /** The text the menu is acting on (selection, else resolved content). */
382
+ text: string;
383
+ /** True when the action should REWRITE the text rather than copy it. */
384
+ canWrite: boolean;
385
+ /** Rewrite the acted-on text with `next`. Only called when `canWrite`. */
386
+ onReplace: (next: string) => void;
387
+ /** Copy `next` to the clipboard. Used on read-only surfaces. */
388
+ onCopy: (next: string) => void | Promise<void>;
389
+ }
390
+ /**
391
+ * Build the JSON section for the given text, or `null` when the text is not
392
+ * JSON-shaped at all (the overwhelmingly common case — the section simply
393
+ * does not render).
394
+ */
395
+ declare function buildJsonMenuSection(params: BuildJsonMenuActionsParams): JsonMenuSection | null;
396
+ /** Short label for the submenu trigger, e.g. "JSON · 13 lines". */
397
+ declare function jsonSectionLabel(section: JsonMenuSection): string;
398
+
399
+ declare const DEFAULT_PLACEMENT_MODE: Record<PlacementKey, PlacementVisibility>;
400
+ declare const ALL_DB_PLACEMENTS: PlacementKey[];
401
+ /** User-facing relabels matching the v3 taxonomy (My Items / Org Items). */
402
+ declare const PLACEMENT_LABEL_OVERRIDE: Partial<Record<string, string>>;
403
+ declare const PLACEMENT_COLOR: Record<string, string>;
404
+ declare function getPlacementIcon(placementType: string): lucide_react.LucideIcon;
405
+ declare function getPlacementLabel(placementType: string): string;
406
+ declare function resolveIcon(iconName: string | null | undefined, fallback?: string): _ai_matrx_icons.IconComponentType;
407
+ declare function groupsByPlacement(groups: AgentMenuCategoryGroup[]): Record<string, AgentMenuCategoryGroup[]>;
408
+ declare function hasItemsRecursive(group: AgentMenuCategoryGroup): boolean;
409
+ /** Resolve a rich-document action's label + disabled state for rendering. */
410
+ declare function resolveRichActionView(action: RichDocumentAction, ctx: RichDocumentActionContext): {
411
+ label: string;
412
+ disabled: boolean;
413
+ };
414
+ interface ContextMenuActions {
415
+ scope: ApplicationScope;
416
+ actionText: ResolvedActionText;
417
+ /** The name of the field the menu opened in (its label), or null. */
418
+ fieldLabel: string | null;
419
+ /** JSON verbs for a JSON-shaped selection/content. `null` = not JSON. */
420
+ jsonSection: JsonMenuSection | null;
421
+ resolvedPlacementMode: Record<PlacementKey, PlacementVisibility>;
422
+ categoryGroups: AgentMenuCategoryGroup[];
423
+ grouped: Record<string, AgentMenuCategoryGroup[]>;
424
+ loading: boolean;
425
+ /** Why the agent libraries (AI Actions, My Items, …) did not load — e.g. past the menu's deadline. */
426
+ librariesError: string | null;
427
+ boundAgentSections: SurfaceBoundAgentSection[];
428
+ boundAgentsLoading: boolean;
429
+ boundAgentsError: string | null;
430
+ /** Re-run the library fetches after a failure or a missed deadline. */
431
+ retryLibraries: () => void;
432
+ /** Rich-document action ids this menu must not draw (surface exclusions + already-here). */
433
+ excludedRichActionIds: string[];
434
+ richDocCtx: RichDocumentActionContext;
435
+ /** The registry actions this menu shows — rendered as the ONE tree. */
436
+ registryActions: RichDocumentAction[];
437
+ copyVariantActions: RichDocumentAction[];
438
+ exportActions: RichDocumentAction[];
439
+ convertActions: RichDocumentAction[];
440
+ hasCompareBase: boolean;
441
+ isAdmin: boolean;
442
+ isDebugMode: boolean;
443
+ isAdminIndicatorOpen: boolean;
444
+ canNativeUndo: boolean;
445
+ handleCopy: () => Promise<void>;
446
+ handleSpeak: () => void;
447
+ handleSpokenSummary: () => void;
448
+ handleSpokenSummaryLive: () => void;
449
+ spokenSummaryAvailable: boolean;
450
+ handleCut: () => Promise<void>;
451
+ handlePaste: () => Promise<void>;
452
+ handleSelectAll: () => void;
453
+ handleUndo: () => void;
454
+ handleRedo: () => void;
455
+ handleCompareClipboard: () => Promise<void>;
456
+ handleSetCompareBase: () => void;
457
+ handleCompareWithBase: () => Promise<void>;
458
+ handleShortcutExecute: (entry: Extract<AgentMenuEntry, {
459
+ entryType: "agent_shortcut";
460
+ }>) => Promise<void>;
461
+ handleBoundAgentExecute: (entry: SurfaceBoundAgentEntry) => Promise<void>;
462
+ handleContentBlockInsert: (entry: Extract<AgentMenuEntry, {
463
+ entryType: "content_block";
464
+ }>) => void;
465
+ handleEntrySelect: (entry: AgentMenuEntry) => void;
466
+ handleDelete: () => Promise<void>;
467
+ handleFind: () => void;
468
+ /** "Insert reference…" (editable) / "Copy reference…" (read-only). */
469
+ handleInsertReference: () => void;
470
+ handleAttach: () => void;
471
+ /** "Link a record…" — the ONE record picker pointed at this menu's entity. */
472
+ handleLinkRecord: () => void;
473
+ handleShare: () => void;
474
+ handleInspectValues: () => void;
475
+ handleInspectState: () => void;
476
+ handleToggleDebugMode: () => void;
477
+ handleToggleAdminIndicator: () => void;
478
+ quickActions: ContextMenuQuickActions;
479
+ /**
480
+ * The page's surface, as ONE nested menu entry titled with the surface's
481
+ * display label (e.g. "Notes", "Marketing Site Workspace") — the same data
482
+ * and actions the shell-header Agents button offers: location, Surface
483
+ * Context, Surface Context Admin, the bound agents + bind action, related
484
+ * surfaces, and (admins) the menu revision. Shaped as an extra section so
485
+ * BOTH renderers draw it with their existing extra-item code.
486
+ */
487
+ surfaceSection: ContextMenuExtraSection;
488
+ }
489
+ declare function useContextMenuActions(props: Omit<MenuContentProps, "variant">): ContextMenuActions;
490
+
491
+ /** One submenu the grouping creates. The label is a plain, conventional word. */
492
+ interface MenuGroupDef {
493
+ key: string;
494
+ label: string;
495
+ /** One line: what the person gets from a row in this submenu. */
496
+ definition: string;
497
+ /** Icon KEY for the Alchemy icon port. */
498
+ icon?: string | undefined;
499
+ /** Decides where the submenu row sits among the other rows (the package's group order). */
500
+ category: ActionCategory;
501
+ order: number;
502
+ }
503
+ type GroupingDestination =
504
+ /** Top level, lifted to the very top: the page's own actions. */
505
+ {
506
+ kind: "page-first";
507
+ }
508
+ /** Top level, where the package places it (verbs stay in the icon strip). */
509
+ | {
510
+ kind: "top";
511
+ } | {
512
+ kind: "group";
513
+ key: string;
514
+ };
515
+ interface GroupingRule {
516
+ /** Matches when ANY listed condition matches. */
517
+ when: {
518
+ ids?: readonly string[] | undefined;
519
+ idPrefixes?: readonly string[] | undefined;
520
+ categories?: readonly ActionCategory[] | undefined;
521
+ /** The clicked thing's own section (a row's or a note's actions). */
522
+ pageOwn?: boolean | undefined;
523
+ /**
524
+ * A page's OWN row whose label matches one of these (case-insensitive
525
+ * regular expressions): a record action that duplicates a category
526
+ * ("Export as Markdown", "Share link…") moves into that category.
527
+ */
528
+ pageOwnLabels?: readonly string[] | undefined;
529
+ };
530
+ to: GroupingDestination;
531
+ /**
532
+ * Universal rows that do the same thing. When one of them is in the menu,
533
+ * the matched row merges into it (one row, recorded as merged) instead of
534
+ * sitting beside it.
535
+ */
536
+ mergeWithIds?: readonly string[] | undefined;
537
+ }
538
+ interface MenuGrouping {
539
+ name: string;
540
+ groups: readonly MenuGroupDef[];
541
+ /** First match wins. */
542
+ rules: readonly GroupingRule[];
543
+ /** Where an action no rule names goes. */
544
+ fallback: GroupingDestination;
545
+ /** A group with fewer members than this is not made: its member stays at the top level. */
546
+ minMembers: number;
547
+ }
548
+ interface RegroupOptions {
549
+ /** Inside one submenu, a second row with the same name as an earlier one is merged into it. */
550
+ mergeSameName: boolean;
551
+ }
552
+ /** Why an action sits where it sits in the proposed menu. */
553
+ type ProposedHome = {
554
+ kind: "page-first";
555
+ } | {
556
+ kind: "top";
557
+ note?: string;
558
+ } | {
559
+ kind: "group";
560
+ key: string;
561
+ label: string;
562
+ } | {
563
+ kind: "merged";
564
+ intoId: string;
565
+ intoLabel: string;
566
+ groupLabel: string;
567
+ /** The merged row runs THIS page's handler under the twin's label and place. */
568
+ pageActionKept?: boolean | undefined;
569
+ };
570
+ interface RegroupResult {
571
+ /** What the proposed menu resolves: top-level actions plus one submenu action per group. */
572
+ resolved: ResolvedAction[];
573
+ /** The rows inside each submenu, by group key, in order. */
574
+ members: Map<string, ResolvedAction[]>;
575
+ home: Map<string, ProposedHome>;
576
+ }
577
+ declare const REGROUP_ID_PREFIX = "regroup:";
578
+ declare function actionLabel(action: Action, target: ClickTarget): string;
579
+ /** The rule that places this action, or null when it falls through to `fallback`. */
580
+ declare function matchRule(grouping: MenuGrouping, action: Pick<Action, "id" | "category" | "section"> & {
581
+ label?: Action["label"];
582
+ }, target?: ClickTarget): GroupingRule | null;
583
+ declare function destinationFor(grouping: MenuGrouping, action: Action, target?: ClickTarget): GroupingDestination;
584
+ declare function currentModel(target: ClickTarget, resolved: readonly ResolvedAction[]): MenuModel;
585
+ /**
586
+ * Apply a grouping to the resolved actions. Actions greyed with a reason stay
587
+ * at the top level: a submenu row is always drawn runnable, so moving a greyed
588
+ * row into one would make it look usable when it is not.
589
+ */
590
+ declare function regroupResolved(target: ClickTarget, resolved: readonly ResolvedAction[], grouping: MenuGrouping, options: RegroupOptions): RegroupResult;
591
+ /** Where each top-level action id is drawn in a context arrangement. */
592
+ declare function drawnPlaces(model: MenuModel, arrangement: ContextArrangement): Map<string, string>;
593
+ /** How many rows a person sees when the menu opens (strip icons count as one row). */
594
+ declare function topLevelRowCount(model: MenuModel, arrangement: ContextArrangement): number;
595
+ interface AuditRow {
596
+ id: string;
597
+ label: string;
598
+ category: ActionCategory;
599
+ isSubmenu: boolean;
600
+ /** The clicked thing's own row (surface-specific), not a universal one. */
601
+ pageOwn: boolean;
602
+ current: string;
603
+ proposed: ProposedHome | null;
604
+ proposedPlace: string;
605
+ }
606
+ interface RegroupAudit {
607
+ rows: AuditRow[];
608
+ /** Current actions the regrouped menu does not hold anywhere and does not explain. */
609
+ lost: AuditRow[];
610
+ currentRows: number;
611
+ proposedRows: number;
612
+ }
613
+ /**
614
+ * Recompute, from what the regrouped menu HOLDS, where every current action
615
+ * went. An action counts as placed only if it is a top-level action of the
616
+ * regrouped menu, a member of one of its submenus, or recorded as merged into
617
+ * a row that is itself placed.
618
+ */
619
+ declare function auditRegroup(target: ClickTarget, resolved: readonly ResolvedAction[], grouping: MenuGrouping, options: RegroupOptions, arrangement: ContextArrangement,
620
+ /** The regroup under audit (tests hand in a broken one to prove the guard fires). */
621
+ regroup?: typeof regroupResolved): RegroupAudit;
622
+
623
+ export { topLevelRowCount as $, type AuditRow as A, type BuildJsonMenuActionsParams as B, type ContextMenuActions as C, DEFAULT_CONTEXT_MENU_HOST as D, configureContextMenuHost as E, type FindReplaceOptions as F, type GroupingDestination as G, currentModel as H, type InertMenuDiagnostic as I, type JsonMenuAction as J, destinationFor as K, type LinkRecordSheetOptions as L, type MenuGrouping as M, detectInertMenu as N, drawnPlaces as O, type ProposedHome as P, getContextMenuHost as Q, REGROUP_ID_PREFIX as R, type ShareModalOptions as S, hasActionableContent as T, jsonSectionLabel as U, matchRule as V, regroupResolved as W, reportMenuDiagnostics as X, resetContextMenuHost as Y, resolveActionText as Z, resolveApplicationScope as _, type CompareBaseInput as a, useContextMenuHostBindings as a0, ALL_DB_PLACEMENTS as a1, type AgentMenuCategoryGroup as a2, type AgentMenuEntry as a3, type BuildCategoryGroupsArgs as a4, DEFAULT_PLACEMENT_MODE as a5, PLACEMENT_COLOR as a6, PLACEMENT_LABEL_OVERRIDE as a7, type UseUnifiedAgentContextMenuArgs as a8, type UseUnifiedAgentContextMenuResult as a9, buildCategoryGroups as aa, getPlacementIcon as ab, getPlacementLabel as ac, groupsByPlacement as ad, hasItemsRecursive as ae, resolveIcon as af, resolveRichActionView as ag, useContextMenuActions as ah, useUnifiedAgentContextMenu as ai, type ContextAssignmentOptions as b, type ContextMenuHost as c, type ContextMenuHostBindings as d, type ContextMenuQuickActions as e, type DiffWindowOptions as f, type DocumentAgentReviewProps as g, type GroupingRule as h, type JsonMenuSection as i, type ListenSummaryWindowOptions as j, type ManualCopyOptions as k, type MenuGroupDef as l, type ReferencePick as m, type ReferencePickerOptions as n, type RegroupAudit as o, type RegroupOptions as p, type RegroupResult as q, type ResolveScopeArgs as r, type ResolvedActionText as s, type SurfaceAgentBindOptions as t, type SurfaceContextWindowOptions as u, type SurfaceInspectorOptions as v, actionLabel as w, auditRegroup as x, auditSurfaceScope as y, buildJsonMenuSection as z };