@ai-matrx/rich-content 0.2.10 → 0.2.16

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 (114) hide show
  1. package/dist/copy/copy-commands.d.ts +42 -0
  2. package/dist/copy/copy-commands.js +119 -0
  3. package/dist/copy/markdown-copy-html.d.ts +3 -0
  4. package/dist/copy/markdown-copy-html.js +8 -0
  5. package/dist/copy/markdown-readable-text.d.ts +4 -0
  6. package/dist/copy/markdown-readable-text.js +62 -0
  7. package/dist/display/chat-markdown/block-registry/BlockComponentRegistry.js +2 -0
  8. package/dist/display/chat-markdown/block-registry/block-dispatch.js +2 -0
  9. package/dist/host/app-bindings.d.ts +30 -0
  10. package/dist/host/app-bindings.js +38 -0
  11. package/dist/host/feedback-subject.d.ts +16 -0
  12. package/dist/host/feedback-subject.js +0 -0
  13. package/dist/host/manual-copy.d.ts +7 -0
  14. package/dist/host/manual-copy.js +11 -0
  15. package/dist/host/registry.d.ts +2 -0
  16. package/dist/host/registry.js +10 -0
  17. package/dist/host/session-knob.d.ts +5 -0
  18. package/dist/host/session-knob.js +7 -0
  19. package/dist/host/types.d.ts +24 -0
  20. package/dist/rich-document/RegistryContextMenu.d.ts +29 -0
  21. package/dist/rich-document/RegistryContextMenu.js +40 -0
  22. package/dist/rich-document/RichDocument.d.ts +78 -0
  23. package/dist/rich-document/RichDocument.js +218 -0
  24. package/dist/rich-document/RichDocumentActionProvider.d.ts +18 -0
  25. package/dist/rich-document/RichDocumentActionProvider.js +18 -0
  26. package/dist/rich-document/RichDocumentActionSurface.d.ts +11 -0
  27. package/dist/rich-document/RichDocumentActionSurface.js +59 -0
  28. package/dist/rich-document/RichDocumentActions.d.ts +12 -0
  29. package/dist/rich-document/RichDocumentActions.js +29 -0
  30. package/dist/rich-document/actions/hostActions.d.ts +5 -0
  31. package/dist/rich-document/actions/hostActions.js +31 -0
  32. package/dist/rich-document/actions/labels.d.ts +9 -0
  33. package/dist/rich-document/actions/labels.js +8 -0
  34. package/dist/rich-document/actions/markdownTable.d.ts +9 -0
  35. package/dist/rich-document/actions/markdownTable.js +39 -0
  36. package/dist/rich-document/actions/provider.d.ts +58 -0
  37. package/dist/rich-document/actions/provider.js +245 -0
  38. package/dist/rich-document/actions/resumePendingAuthAction.d.ts +3 -0
  39. package/dist/rich-document/actions/resumePendingAuthAction.js +31 -0
  40. package/dist/rich-document/actions/sources/index.d.ts +9 -0
  41. package/dist/rich-document/actions/sources/index.js +22 -0
  42. package/dist/rich-document/actions/sources/raw.d.ts +4 -0
  43. package/dist/rich-document/actions/sources/raw.js +35 -0
  44. package/dist/rich-document/actions/useRichDocumentProvider.d.ts +1 -0
  45. package/dist/rich-document/actions/useRichDocumentProvider.js +13 -0
  46. package/dist/rich-document/host-types.d.ts +51 -0
  47. package/dist/rich-document/host-types.js +0 -0
  48. package/dist/rich-document/registrations.d.ts +1 -0
  49. package/dist/rich-document/registrations.js +13 -0
  50. package/dist/rich-document/review/applyTargets.d.ts +16 -0
  51. package/dist/rich-document/review/applyTargets.js +32 -0
  52. package/dist/rich-document/review/proposedEdit.d.ts +22 -0
  53. package/dist/rich-document/review/proposedEdit.js +48 -0
  54. package/dist/rich-document/review/protectedSpans.d.ts +14 -0
  55. package/dist/rich-document/review/protectedSpans.js +61 -0
  56. package/dist/rich-document/runtime/actionSurfacesStore.d.ts +30 -0
  57. package/dist/rich-document/runtime/actionSurfacesStore.js +79 -0
  58. package/dist/rich-document/runtime/contextMenuFor.d.ts +6 -0
  59. package/dist/rich-document/runtime/contextMenuFor.js +7 -0
  60. package/dist/rich-document/runtime/providerBridge.d.ts +20 -0
  61. package/dist/rich-document/runtime/providerBridge.js +23 -0
  62. package/dist/rich-document/runtime/useActionSurfaceProvider.d.ts +33 -0
  63. package/dist/rich-document/runtime/useActionSurfaceProvider.js +214 -0
  64. package/dist/rich-document/search/ContentFindControl.d.ts +8 -0
  65. package/dist/rich-document/search/ContentFindControl.js +46 -0
  66. package/dist/rich-document/search/RenderedFindBar.d.ts +8 -0
  67. package/dist/rich-document/search/RenderedFindBar.js +110 -0
  68. package/dist/rich-document/search/renderedFind.d.ts +1 -0
  69. package/dist/rich-document/search/renderedFind.js +1 -0
  70. package/dist/rich-document/types.d.ts +384 -0
  71. package/dist/rich-document/types.js +0 -0
  72. package/dist/rich-document/variants/ActionBar.d.ts +17 -0
  73. package/dist/rich-document/variants/ActionBar.js +48 -0
  74. package/dist/rich-document/variants/MenuVariant.d.ts +15 -0
  75. package/dist/rich-document/variants/MenuVariant.js +17 -0
  76. package/dist/rich-document/variants/MiniActionBar.d.ts +5 -0
  77. package/dist/rich-document/variants/MiniActionBar.js +11 -0
  78. package/dist/rich-document/variants/RegistryActionList.d.ts +17 -0
  79. package/dist/rich-document/variants/RegistryActionList.js +94 -0
  80. package/dist/rich-document/variants/RegistryActionMenu.d.ts +24 -0
  81. package/dist/rich-document/variants/RegistryActionMenu.js +74 -0
  82. package/dist/rich-document/variants/shared/AlchemyDocumentMenu.d.ts +7 -0
  83. package/dist/rich-document/variants/shared/AlchemyDocumentMenu.js +83 -0
  84. package/dist/rich-document/variants/shared/OpenOneMenuButton.d.ts +9 -0
  85. package/dist/rich-document/variants/shared/OpenOneMenuButton.js +70 -0
  86. package/dist/rich-document/variants/shared/StandaloneOneMenu.d.ts +8 -0
  87. package/dist/rich-document/variants/shared/StandaloneOneMenu.js +32 -0
  88. package/dist/rich-document/variants/shared/categories.d.ts +4 -0
  89. package/dist/rich-document/variants/shared/categories.js +31 -0
  90. package/dist/rich-document/variants/shared/iconMap.d.ts +2 -0
  91. package/dist/rich-document/variants/shared/iconMap.js +73 -0
  92. package/dist/rich-document/variants/shared/menuStructure.d.ts +81 -0
  93. package/dist/rich-document/variants/shared/menuStructure.js +271 -0
  94. package/dist/rich-document/variants/shared/runAction.d.ts +17 -0
  95. package/dist/rich-document/variants/shared/runAction.js +24 -0
  96. package/dist/selection-toolbar/SelectionToolbarFrame.d.ts +64 -0
  97. package/dist/selection-toolbar/SelectionToolbarFrame.js +191 -0
  98. package/dist/selection-toolbar/common-actions.d.ts +2 -0
  99. package/dist/selection-toolbar/common-actions.js +103 -0
  100. package/dist/selection-toolbar/selection-actions.d.ts +126 -0
  101. package/dist/selection-toolbar/selection-actions.js +240 -0
  102. package/dist/selection-toolbar/selection-copy.d.ts +19 -0
  103. package/dist/selection-toolbar/selection-copy.js +98 -0
  104. package/dist/selection-toolbar/selection-shape.d.ts +1 -0
  105. package/dist/selection-toolbar/selection-shape.js +15 -0
  106. package/dist/selection-toolbar/selection-zones.d.ts +90 -0
  107. package/dist/selection-toolbar/selection-zones.js +85 -0
  108. package/dist/selection-toolbar/textarea-caret.d.ts +8 -0
  109. package/dist/selection-toolbar/textarea-caret.js +69 -0
  110. package/dist/utils/alchemy-icon-keys.d.ts +18 -0
  111. package/dist/utils/alchemy-icon-keys.js +29 -0
  112. package/dist/utils/rendered-find.d.ts +9 -0
  113. package/dist/utils/rendered-find.js +68 -0
  114. package/package.json +23 -7
@@ -0,0 +1,384 @@
1
+ import type { ApplicationScope } from "./host-types.js";
2
+ import type { LucideIcon } from "lucide-react";
3
+ import type { AppDispatch, RootState } from "./host-types.js";
4
+ import type { Note } from "./host-types.js";
5
+ import type { NoteSaveReceipt } from "./host-types.js";
6
+ export type ContentSourceType = "chat-message" | "note" | "prompt-result" | "artifact" | "scraper-result" | "working-document" | "raw";
7
+ export interface NoteEditBase {
8
+ noteId: string;
9
+ organizationId: string;
10
+ /** The persisted CAS revision. Revision zero is valid. */
11
+ version: number;
12
+ actorId: string;
13
+ }
14
+ /** Full physical values displayed when a Notes editor was opened. */
15
+ export type NoteDisplayedPhysicalSnapshot = Omit<Pick<Note, "id" | "organization_id" | "version" | "content" | "label" | "folder_name" | "folder_id" | "tags" | "metadata" | "shown_to" | "published_to_web" | "position" | "project_id" | "task_id">, "content"> & {
16
+ content: string;
17
+ };
18
+ export type NoteIdentityContentSource = {
19
+ type: "note";
20
+ mode: "identity";
21
+ noteId: string;
22
+ sourceId: string;
23
+ };
24
+ export type NoteEditableContentSource = {
25
+ type: "note";
26
+ mode: "editable";
27
+ noteId: string;
28
+ sourceId: string;
29
+ snapshotId: string;
30
+ editBase: NoteEditBase;
31
+ /** Full persisted physical state for the captured CAS base. */
32
+ acknowledgedPhysicalSnapshot: NoteDisplayedPhysicalSnapshot;
33
+ displayedPhysicalSnapshot: NoteDisplayedPhysicalSnapshot;
34
+ /** A selection is an action target only; it is never the persisted body. */
35
+ actingSelection?: string;
36
+ };
37
+ /**
38
+ * A source can be a READ-ONLY copy (the proving route loads a real record into
39
+ * a scratch buffer). The flag rides the source itself, so every surface that
40
+ * shows it — bar, ⋯ menu, right-click, hosted dialogs — honors it: no action
41
+ * with `writesSource` is offered, and editors open without Save.
42
+ */
43
+ export type ContentSource = ContentSourceIdentity & {
44
+ readOnly?: boolean;
45
+ };
46
+ type ContentSourceIdentity = {
47
+ type: "chat-message";
48
+ messageId: string;
49
+ conversationId: string;
50
+ streamRequestId?: string | null;
51
+ } | NoteIdentityContentSource | NoteEditableContentSource | {
52
+ type: "prompt-result";
53
+ executionId: string;
54
+ promptId?: string;
55
+ } | {
56
+ type: "artifact";
57
+ artifactId: string;
58
+ } | {
59
+ type: "scraper-result";
60
+ runId: string;
61
+ } | {
62
+ type: "working-document";
63
+ conversationId: string;
64
+ kind: "working" | "scratch";
65
+ documentId?: string | null;
66
+ } | {
67
+ type: "raw";
68
+ /** What this content is called (a report, a transcript) — seeds titles and file names. */
69
+ title?: string;
70
+ };
71
+ export type RichDocumentActionId = "thumbs-up" | "thumbs-down" | "copy" | "copy-with-thinking" | "save-as-file" | "save-to-notes" | "save-to-scratch" | "save-code-to-scratch" | "save-to-code" | "save-to-task" | "html-preview" | "copy-html-page" | "email-to-me" | "print" | "full-print" | "edit" | "edit-history" | "fork-at-message" | "delete-message" | "tts-play" | "open-fullscreen-editor" | "analyze-response" | "debug-stream" | "submit-feedback" | "add-to-docs" | "add-to-rulebook" | "set-context-value" | "save-as-message-template" | "save-to-files" | "save-as-pdf" | "save-shape-instance" | "convert-to-study" | "share-webpage" | "send-google-doc" | "summarize-for-listening" | "summarize-and-listen" | "copy-markdown" | "copy-plain-text" | "copy-html-source" | "copy-table-csv" | "copy-table-tsv" | "save-table-as-data" | "download-html" | "download-pdf" | "save-as-flashcard" | "quote-into-chat" | "ask-followup" | "send-to-agent" | "apply-to-source" | "save-run-as-shortcut" | "bind-run-to-page" | "notes-and-comments" | "text-cleanup" | "text-help" | "text-custom-agent" | "edit-and-resubmit" | "fork-and-regenerate" | "server-api-admin";
72
+ export type ActionCategory = "feedback" | "copy" | "export" | "save" | "edit" | "share" | "creator" | "app" | "listen" | "study" | "ask" | "ai" | "admin";
73
+ export type SourceExtensions = ChatMessageExtensions | {
74
+ type: "note";
75
+ isOwner: boolean;
76
+ } | {
77
+ type: "prompt-result";
78
+ canReRun: boolean;
79
+ } | {
80
+ type: "artifact";
81
+ canEdit: boolean;
82
+ } | {
83
+ type: "scraper-result";
84
+ } | {
85
+ type: "working-document";
86
+ } | {
87
+ type: "raw";
88
+ };
89
+ /**
90
+ * What a chat message carries beyond its identity. `ctx.content` is what the
91
+ * reader SEES (the whole multi-iteration turn when the host groups one);
92
+ * `messageContent` is the ONE cx_message row the write-back actions (edit,
93
+ * HTML publish save) mutate. Built by `buildChatMessageActions` — the single
94
+ * builder every chat host and the proving route share.
95
+ */
96
+ export interface ChatMessageExtensions {
97
+ type: "chat-message";
98
+ role: "assistant" | "user";
99
+ /** Single-message text of `source.messageId` (write-back target). */
100
+ messageContent: string;
101
+ /** True when the text is the JSON raw view of a structured payload. */
102
+ contentIsStructuredRaw: boolean;
103
+ /** Text-bearing row the assistant editor saves to (grouped turns). */
104
+ editTarget: {
105
+ content: string;
106
+ messageId: string;
107
+ isStructuredRaw: boolean;
108
+ } | null;
109
+ streamRequestId: string | null;
110
+ contentHistoryCount: number;
111
+ showFullPrint: boolean;
112
+ isCapturing: boolean;
113
+ groupMessageIds: string[];
114
+ }
115
+ export interface ContentSourceAdapter {
116
+ /**
117
+ * Persist edited content back to the source. Implementations dispatch the
118
+ * appropriate thunk or call the service. For chat-message this dispatches
119
+ * editMessage; for note this calls NotesAPI.update; etc.
120
+ */
121
+ edit?: (args: {
122
+ newContent: string;
123
+ /**
124
+ * The text the editor opened on, when that was a DISPLAY projection rather
125
+ * than the stored bytes (chat: whitespace-normalized) — the adapter then
126
+ * splices only the changed span into what is stored.
127
+ */
128
+ previousContent?: string;
129
+ source: ContentSource;
130
+ dispatch: AppDispatch;
131
+ }) => Promise<void | NoteSaveReceipt | ChatAnswerSaveReceipt>;
132
+ /**
133
+ * Resolves an action's editable snapshot before an overlay or callback group
134
+ * exists. Non-note adapters may omit this; Notes uses it to turn an identity
135
+ * trigger into one authorized, actor-bound physical snapshot.
136
+ */
137
+ prepareEdit?: (args: {
138
+ source: ContentSource;
139
+ actionText: string;
140
+ dispatch: AppDispatch;
141
+ isAuthenticated: boolean;
142
+ }) => Promise<{
143
+ source: ContentSource;
144
+ content: string;
145
+ }>;
146
+ /** Delete the source record. */
147
+ delete?: (args: {
148
+ source: ContentSource;
149
+ dispatch: AppDispatch;
150
+ }) => Promise<void> | void;
151
+ /** Re-run / regenerate the content (prompt-result only at first). */
152
+ reRun?: (args: {
153
+ source: ContentSource;
154
+ dispatch: AppDispatch;
155
+ }) => Promise<void> | void;
156
+ /**
157
+ * Returns a stable, source-specific prefix used to build overlay
158
+ * instanceIds. Example for note: `note-${noteId}`. Implementations should
159
+ * include enough of the source identifier that two distinct sources never
160
+ * collide. Used by ctx.instanceKey(prefix).
161
+ */
162
+ instanceKeyPrefix: (source: ContentSource) => string;
163
+ }
164
+ export interface PreparedContentEdit {
165
+ source: ContentSource;
166
+ /** Always the complete displayed physical body for prepared Notes sources. */
167
+ content: string;
168
+ }
169
+ export interface RichDocumentActionContextCallbacks {
170
+ /** Notify the host that the user committed an edit. Used by chat to route
171
+ * through OverlayController's atomic edit path (no closure in Redux). */
172
+ onEdit?: (newContent: string) => void;
173
+ /** Trigger the parent's full-page print pipeline (chat only today). */
174
+ onFullPrint?: () => void;
175
+ /** Open the host-owned destructive-vs-fork dialog (chat only today). */
176
+ onRequestDelete?: () => void;
177
+ /** Open the host-owned edit-history dialog (chat only today). */
178
+ onRequestEditHistory?: () => void;
179
+ /** Open the host-owned ConvertContentDialog (the ONE convert-source dialog). */
180
+ onRequestConvert?: () => void;
181
+ /** Open the host-owned "save as flashcard" prompt (the text is the answer). */
182
+ onRequestFlashcard?: (answer: string) => void;
183
+ /**
184
+ * Run one of a text field's own AI powers (clean up, help with this, custom
185
+ * agent). Only a host that can APPLY a result back into the text supplies
186
+ * it — ProTextarea today — so elsewhere those actions are absent.
187
+ */
188
+ onRequestTextAgentAction?: (actionId: "cleanup" | "help" | "customAgent", ctx: RichDocumentActionContext) => void;
189
+ /**
190
+ * The saved record this content is (`token:id`) when the source does not say —
191
+ * the studio previewing a loaded document over a raw source. The ⋯ "Notes &
192
+ * comments" row reads it (annotations/RecordAnnotations).
193
+ */
194
+ annotationRecordKey?: () => string | null;
195
+ /**
196
+ * The rows this content already IS — a transcript's lines, a thread's messages — for content
197
+ * whose text has no table shape of its own. "Save to a table" offers them (typed columns) when
198
+ * neither the selection nor the text reads as rows. `selection` is the selected words, when any,
199
+ * so the host can narrow to that part; null or [] means "nothing to offer".
200
+ */
201
+ tableRows?: (selection: string | null) => ReadonlyArray<Record<string, unknown>> | null;
202
+ /**
203
+ * The rendered table the right-click landed on, as table text with its shape kept (header +
204
+ * rows) — set by the context-menu shell from the clicked element, so "Save to a table" opens on
205
+ * THAT table, not on the first list or table in the whole answer. Null when the click was not
206
+ * inside a table.
207
+ */
208
+ tableAtTarget?: () => string | null;
209
+ }
210
+ /** What a chat-answer save actually wrote (RC-B5) — the source of any "saved" sentence. */
211
+ export interface ChatAnswerSaveReceipt {
212
+ kind: "chat-answer";
213
+ written: boolean;
214
+ /** Places (lines) of the answer that changed; everything else is byte-identical. */
215
+ changedSpans: number;
216
+ /** The change touched most of the answer — reported as a rewrite, not a count. */
217
+ mostlyRewritten: boolean;
218
+ }
219
+ export interface RichDocumentActionContext {
220
+ content: string;
221
+ source: ContentSource;
222
+ metadata: Record<string, unknown> | null;
223
+ dispatch: AppDispatch;
224
+ /** Synchronous store read — titles, the answered question, org, roles. */
225
+ getState: () => RootState;
226
+ /** Active organization supplied by the action host for note writes. */
227
+ organizationId: string | null;
228
+ isAuthenticated: boolean;
229
+ isAdmin: boolean;
230
+ isCreator: boolean;
231
+ surfaceKey: string | null;
232
+ onClose: () => void;
233
+ /** Build an overlay instanceId stable per source — e.g. `save-notes-${noteId}`. */
234
+ instanceKey: (prefix: string) => string;
235
+ /** Source-specific edit/delete/re-run bridge. */
236
+ sourceAdapter: ContentSourceAdapter;
237
+ /** Per-instance callbacks the host wants to plug in. */
238
+ callbacks?: RichDocumentActionContextCallbacks;
239
+ /** Source-specific extensions. Discriminated so chat-only fields don't pollute the base shape. */
240
+ extensions?: SourceExtensions;
241
+ /**
242
+ * The scope the right-click menu captured for this open (selection, whole
243
+ * content, text around it, every surface value). Absent on bars.
244
+ */
245
+ applicationScope?: ApplicationScope;
246
+ }
247
+ export interface RichDocumentAction {
248
+ /** Unique ID. Built-ins use RichDocumentActionId; extras may use any string. */
249
+ id: RichDocumentActionId | string;
250
+ label: string | ((ctx: RichDocumentActionContext) => string);
251
+ icon: LucideIcon;
252
+ /**
253
+ * Optional Tailwind color class(es) applied to the icon. Preserves the
254
+ * per-action visual variety from the legacy registry (text-blue-500
255
+ * dark:text-blue-400, etc.). Variant renderers may ignore this if they
256
+ * have a stricter visual system.
257
+ */
258
+ iconColor?: string;
259
+ category: ActionCategory;
260
+ /** Which source types this action operates on. "*" = all sources. */
261
+ supportedSources: ContentSourceType[] | "*";
262
+ /** Optional show predicate. Default: true. */
263
+ visible?: (ctx: RichDocumentActionContext) => boolean;
264
+ /** Optional disabled predicate. Returns boolean OR a reason for tooltips. */
265
+ disabled?: (ctx: RichDocumentActionContext) => boolean | {
266
+ reason: string;
267
+ };
268
+ /** The handler. May be async. */
269
+ run: (ctx: RichDocumentActionContext) => void | Promise<void>;
270
+ /**
271
+ * Default render slot.
272
+ * "primary" — inline bar only.
273
+ * "overflow" — ⋯ menu only.
274
+ * "both" — mirrored in both.
275
+ * Default: "overflow".
276
+ */
277
+ renderSlot?: "primary" | "overflow" | "both";
278
+ /** Default sort weight within category (lower = earlier). */
279
+ order?: number;
280
+ /** Action requires authentication. Hidden when isAuthenticated is false. */
281
+ requiresAuth?: boolean;
282
+ /**
283
+ * The action changes the SOURCE record (edit, delete, fork, pin, regenerate,
284
+ * apply an AI result…). Absent on a read-only source.
285
+ */
286
+ writesSource?: boolean;
287
+ /**
288
+ * A toggle's live state (a thumb's verdict, read-aloud playing). Renderers
289
+ * paint the action "on" (its iconColor, aria-pressed) only while true; an
290
+ * action with `active` defined reads as muted otherwise.
291
+ */
292
+ active?: (ctx: RichDocumentActionContext) => boolean;
293
+ /**
294
+ * A toggle's live glyph (read-aloud: Pause while playing, Play while paused,
295
+ * a spinner while starting). Null/absent = the static `icon`.
296
+ */
297
+ stateIcon?: (ctx: RichDocumentActionContext) => {
298
+ icon: LucideIcon;
299
+ spin?: boolean;
300
+ } | null;
301
+ /**
302
+ * Subscribe to whatever `active`/`label` read (a module store). Called by
303
+ * the renderer; returns the unsubscribe. May also start a hydration read.
304
+ */
305
+ subscribe?: (onChange: () => void, ctx: RichDocumentActionContext) => () => void;
306
+ /**
307
+ * Keep the reader's text selection alive through the click (mousedown would
308
+ * clear it) — read-aloud reads the selection when there is one.
309
+ */
310
+ preserveSelection?: boolean;
311
+ }
312
+ export type RichDocumentActionsVariant =
313
+ /** Full inline action bar with primary buttons + overflow menu. */
314
+ "bar"
315
+ /** Condensed icons-only bar + overflow. */
316
+ | "mini-bar"
317
+ /** Single ⋯ overflow button, all actions in dropdown. */
318
+ | "menu"
319
+ /** Single ⋯ trigger — same renderer as "menu", named for absolute/hover use. */
320
+ | "icon-only"
321
+ /** No inline UI — registers to a remote <RichDocumentActionSurface/>. */
322
+ | "remote"
323
+ /** Hide actions entirely. */
324
+ | "none";
325
+ /** Where the action surface sits relative to the content. */
326
+ export type RichDocumentActionsPosition =
327
+ /** In-flow, below the content (default). */
328
+ "below"
329
+ /** In-flow, above the content. */
330
+ | "above"
331
+ /** Absolutely positioned, layered over the content. */
332
+ | "top-right" | "top-left" | "middle-right" | "middle-left";
333
+ /** Visibility behavior of the action surface. */
334
+ export type RichDocumentActionsBehavior =
335
+ /** Always visible (default). */
336
+ "always"
337
+ /** Hidden until the content is hovered/focused (fades in). */
338
+ | "hover-only";
339
+ export interface RichDocumentActionsProp {
340
+ /** Built-in action IDs (or custom IDs) to hide. All built-ins included by default. */
341
+ exclude?: (RichDocumentActionId | string)[];
342
+ /** Custom actions appended to the registry. Typically open an overlay via dispatch. */
343
+ extra?: RichDocumentAction[];
344
+ /** Optional callbacks the registry's handlers can call. */
345
+ callbacks?: RichDocumentActionContextCallbacks;
346
+ /** Source-specific extensions to merge into the context. */
347
+ extensions?: SourceExtensions;
348
+ /** Source metadata (cx_message.metadata, …) — rides into saves/exports. */
349
+ metadata?: Record<string, unknown> | null;
350
+ /** Viewer owns the agent that produced the content (creator tools). */
351
+ isCreator?: boolean;
352
+ /** UI surface the host belongs to — fork/delete outcomes route through it. */
353
+ surfaceKey?: string | null;
354
+ }
355
+ /**
356
+ * A snapshot of action metadata stored in Redux for a remote surface.
357
+ *
358
+ * Intentionally CONTAINS NO FUNCTIONS — handlers, getters, and callbacks are
359
+ * kept out of Redux state. The renderer looks up handlers by `id` from the
360
+ * in-memory registry at render time, using the live getters supplied via the
361
+ * provider's imperative API (registered on mount, kept in module scope).
362
+ */
363
+ export interface RichDocumentActionSpec {
364
+ id: RichDocumentActionId | string;
365
+ label: string;
366
+ iconName: string;
367
+ category: ActionCategory;
368
+ renderSlot: "primary" | "overflow" | "both";
369
+ order: number;
370
+ disabled: boolean;
371
+ disabledReason?: string;
372
+ }
373
+ export interface RichDocumentSurfaceRegistration {
374
+ /** Stable per-instance ID generated by the registering RichDocument. */
375
+ providerId: string;
376
+ /** Pure metadata; no functions. Registration order is encoded by the
377
+ * provider stack's array order, so no timestamp is needed. */
378
+ computedActionSpecs: RichDocumentActionSpec[];
379
+ /** Render-safe canonical identity for transfer workspace cache ownership. */
380
+ contentSourceId: string;
381
+ /** The source type — useful for the renderer to label the surface. */
382
+ sourceType: ContentSourceType;
383
+ }
384
+ export {};
File without changes
@@ -0,0 +1,17 @@
1
+ import * as React from "react";
2
+ import type { ClickTarget } from "@ai-matrx/alchemy/actions";
3
+ import type { RichDocumentAction, RichDocumentActionContext } from "../types.js";
4
+ export interface ActionBarProps {
5
+ actions: RichDocumentAction[];
6
+ getCtx: () => RichDocumentActionContext;
7
+ /** The click target the host built (`richDocumentClickTarget`). */
8
+ target: ClickTarget;
9
+ sourceId: string;
10
+ className?: string;
11
+ /** Hide the ⋯ overflow (a host setting turned the options off). */
12
+ hideOverflow?: boolean;
13
+ /** Condensed presentation (the "mini-bar" variant). */
14
+ mini?: boolean;
15
+ }
16
+ export declare function ActionBar(props: ActionBarProps): React.ReactElement;
17
+ export default ActionBar;
@@ -0,0 +1,48 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { ActionBar as AlchemyActionBar } from "@ai-matrx/alchemy/react/bar";
4
+ import { useRichDocumentProvider } from "../actions/useRichDocumentProvider.js";
5
+ import { cn } from "@ai-matrx/design-system";
6
+ import { AlchemyDocumentMenu } from "./shared/AlchemyDocumentMenu.js";
7
+ import { OpenOneMenuButton, useOneMenuFor } from "./shared/OpenOneMenuButton.js";
8
+ import { StandaloneOneMenu } from "./shared/StandaloneOneMenu.js";
9
+ const BAR_RESTRICT = { exclude: ["copy"] };
10
+ function ActionBar(props) {
11
+ const { actions, getCtx, target, sourceId, className, hideOverflow = false, mini = false } = props;
12
+ useRichDocumentProvider();
13
+ const oneMenu = useOneMenuFor(getCtx().source);
14
+ const hasCopy = actions.some((action) => action.category === "copy");
15
+ return /* @__PURE__ */ jsxs(
16
+ "div",
17
+ {
18
+ className: cn(
19
+ // flex-wrap, never squeeze: a host narrower than the row (the compact
20
+ // modal, the Chat window at 375) wrapped nothing, so the 44px touch
21
+ // targets shrank-and-spilled under the ⋯ — tapping 👍 opened the menu.
22
+ // Wrapping keeps every target whole and apart (guard:
23
+ // answer-bar-targets-never-overlap).
24
+ mini ? "inline-flex max-w-full flex-wrap items-center gap-0.5 px-0.5 py-0.5" : "inline-flex max-w-full flex-wrap items-center gap-1 px-1 py-1",
25
+ className
26
+ ),
27
+ children: [
28
+ hasCopy ? /* @__PURE__ */ jsx(AlchemyDocumentMenu, { getCtx, sourceId, size: mini ? "xs" : "sm" }, sourceId) : null,
29
+ /* @__PURE__ */ jsx(
30
+ AlchemyActionBar,
31
+ {
32
+ target,
33
+ mini,
34
+ restrict: BAR_RESTRICT,
35
+ className: "flex-wrap",
36
+ hideOverflow: true
37
+ }
38
+ ),
39
+ hideOverflow ? null : oneMenu ? /* @__PURE__ */ jsx(OpenOneMenuButton, { source: getCtx().source }) : /* @__PURE__ */ jsx(StandaloneOneMenu, { getCtx, target })
40
+ ]
41
+ }
42
+ );
43
+ }
44
+ var ActionBar_default = ActionBar;
45
+ export {
46
+ ActionBar,
47
+ ActionBar_default as default
48
+ };
@@ -0,0 +1,15 @@
1
+ import * as React from "react";
2
+ import type { ClickTarget } from "@ai-matrx/alchemy/actions";
3
+ import type { RichDocumentActionContext } from "../types.js";
4
+ export interface MenuVariantProps {
5
+ getCtx: () => RichDocumentActionContext;
6
+ target: ClickTarget;
7
+ className?: string;
8
+ }
9
+ /**
10
+ * "menu" / "icon-only" — a single ⋯ that opens THE ONE context-menu engine:
11
+ * the enclosing right-click menu for this content, else a shell of the same
12
+ * content. Desktop dropdown and phone sheet are that engine's layouts.
13
+ */
14
+ export declare function MenuVariant(props: MenuVariantProps): React.ReactElement;
15
+ export default MenuVariant;
@@ -0,0 +1,17 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { useRichDocumentProvider } from "../actions/useRichDocumentProvider.js";
4
+ import { cn } from "@ai-matrx/design-system";
5
+ import { OpenOneMenuButton, useOneMenuFor } from "./shared/OpenOneMenuButton.js";
6
+ import { StandaloneOneMenu } from "./shared/StandaloneOneMenu.js";
7
+ function MenuVariant(props) {
8
+ const { getCtx, target, className } = props;
9
+ useRichDocumentProvider();
10
+ const oneMenu = useOneMenuFor(getCtx().source);
11
+ return /* @__PURE__ */ jsx("div", { className: cn("inline-flex items-center", className), children: oneMenu ? /* @__PURE__ */ jsx(OpenOneMenuButton, { source: getCtx().source }) : /* @__PURE__ */ jsx(StandaloneOneMenu, { getCtx, target }) });
12
+ }
13
+ var MenuVariant_default = MenuVariant;
14
+ export {
15
+ MenuVariant,
16
+ MenuVariant_default as default
17
+ };
@@ -0,0 +1,5 @@
1
+ import * as React from "react";
2
+ import { type ActionBarProps } from "./ActionBar.js";
3
+ export type MiniActionBarProps = Omit<ActionBarProps, "mini" | "hideOverflow">;
4
+ export declare function MiniActionBar(props: MiniActionBarProps): React.ReactElement;
5
+ export default MiniActionBar;
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { ActionBar } from "./ActionBar.js";
4
+ function MiniActionBar(props) {
5
+ return /* @__PURE__ */ jsx(ActionBar, { ...props, mini: true });
6
+ }
7
+ var MiniActionBar_default = MiniActionBar;
8
+ export {
9
+ MiniActionBar,
10
+ MiniActionBar_default as default
11
+ };
@@ -0,0 +1,17 @@
1
+ import * as React from "react";
2
+ import type { ContentSource, RichDocumentActionsProp } from "../types.js";
3
+ export interface RegistryActionListProps {
4
+ content: string;
5
+ source: ContentSource;
6
+ actions?: RichDocumentActionsProp;
7
+ /** Called after a row runs (the host closes its popover). */
8
+ onClose: () => void;
9
+ /**
10
+ * The host's own AI rows (ProTextarea's bound agents) — rendered INSIDE the
11
+ * "Improve with AI" group, never as a parallel AI family.
12
+ */
13
+ aiSlot?: React.ReactNode;
14
+ className?: string;
15
+ }
16
+ export declare function RegistryActionList(props: RegistryActionListProps): React.ReactElement | null;
17
+ export default RegistryActionList;
@@ -0,0 +1,94 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { ChevronRight } from "lucide-react";
5
+ import { cn } from "@ai-matrx/design-system";
6
+ import { useActionSurfaceProvider } from "../runtime/useActionSurfaceProvider.js";
7
+ import {
8
+ AI_SUBMENU_LABEL,
9
+ buildMenuTree,
10
+ withAiSlot
11
+ } from "./shared/menuStructure.js";
12
+ import { resolveActionDisplay, runAction } from "./shared/runAction.js";
13
+ import { menuActions } from "./RegistryActionMenu.js";
14
+ const ROW = "flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm text-foreground transition-colors hover:bg-accent hover:text-accent-foreground disabled:opacity-50";
15
+ function RegistryActionList(props) {
16
+ const { content, source, actions, onClose, className, aiSlot } = props;
17
+ const { ctx, getCtx, resolvedActions } = useActionSurfaceProvider({
18
+ content,
19
+ source,
20
+ actions,
21
+ actionsVariant: "none"
22
+ });
23
+ const [openGroup, setOpenGroup] = React.useState(null);
24
+ const getListCtx = () => ({
25
+ ...getCtx(),
26
+ onClose
27
+ });
28
+ const tree = buildMenuTree(menuActions(resolvedActions), { aiSlot: Boolean(aiSlot) });
29
+ const submenus = withAiSlot(tree.submenus, Boolean(aiSlot));
30
+ if (!aiSlot && tree.topLevel.length === 0 && tree.submenus.length === 0 && tree.extras.length === 0) {
31
+ return null;
32
+ }
33
+ const row = (action, nested = false) => {
34
+ const display = resolveActionDisplay(action, ctx);
35
+ const Icon = display.Icon;
36
+ return /* @__PURE__ */ jsxs(
37
+ "button",
38
+ {
39
+ type: "button",
40
+ disabled: display.isDisabled,
41
+ title: display.disabledReason,
42
+ onClick: () => {
43
+ runAction(action, getListCtx);
44
+ if (action.category !== "ai") onClose();
45
+ },
46
+ className: cn(ROW, nested && "pl-7"),
47
+ children: [
48
+ /* @__PURE__ */ jsx(Icon, { className: cn("h-4 w-4 shrink-0", display.iconColor) }),
49
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: display.label })
50
+ ]
51
+ },
52
+ action.id
53
+ );
54
+ };
55
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", className), children: [
56
+ tree.topLevel.map((a) => row(a)),
57
+ submenus.map((submenu) => {
58
+ const Icon = submenu.icon ?? submenu.actions[0]?.icon ?? ChevronRight;
59
+ const isOpen = openGroup === submenu.label;
60
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
61
+ /* @__PURE__ */ jsxs(
62
+ "button",
63
+ {
64
+ type: "button",
65
+ "aria-expanded": isOpen,
66
+ onClick: () => setOpenGroup(isOpen ? null : submenu.label),
67
+ className: ROW,
68
+ children: [
69
+ /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
70
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: submenu.label }),
71
+ /* @__PURE__ */ jsx(
72
+ ChevronRight,
73
+ {
74
+ className: cn(
75
+ "h-3.5 w-3.5 text-muted-foreground transition-transform",
76
+ isOpen && "rotate-90"
77
+ )
78
+ }
79
+ )
80
+ ]
81
+ }
82
+ ),
83
+ isOpen ? submenu.actions.map((a) => row(a, true)) : null,
84
+ isOpen && submenu.label === AI_SUBMENU_LABEL && aiSlot ? /* @__PURE__ */ jsx("div", { className: "pl-5", children: aiSlot }) : null
85
+ ] }, submenu.label);
86
+ }),
87
+ tree.extras.map((a) => row(a))
88
+ ] });
89
+ }
90
+ var RegistryActionList_default = RegistryActionList;
91
+ export {
92
+ RegistryActionList,
93
+ RegistryActionList_default as default
94
+ };
@@ -0,0 +1,24 @@
1
+ import * as React from "react";
2
+ import { type MenuItem } from "../../host/app-bindings.js";
3
+ import { registryMenuActions } from "./shared/menuStructure.js";
4
+ import type { ContentSource, RichDocumentAction, RichDocumentActionContext, RichDocumentActionsProp } from "../types.js";
5
+ export interface RegistryActionMenuProps {
6
+ isOpen: boolean;
7
+ onClose: () => void;
8
+ content: string;
9
+ source: ContentSource;
10
+ actions?: RichDocumentActionsProp;
11
+ anchorElement?: HTMLElement | null;
12
+ title?: string;
13
+ }
14
+ /** Overflow-slot actions only — the host renders the primary row inline. */
15
+ /** The one menu selector (shared/menuStructure.ts). */
16
+ export declare const menuActions: typeof registryMenuActions;
17
+ /** The registry tree → AdvancedMenu rows (submenus become drill-in rows). */
18
+ export declare function toAdvancedMenuItems(actions: RichDocumentAction[],
19
+ /** Render-time context — labels and disabled state only. */
20
+ ctx: RichDocumentActionContext,
21
+ /** Click-time context factory — what handlers run against. */
22
+ getCtx: () => RichDocumentActionContext): MenuItem[];
23
+ export declare function RegistryActionMenu(props: RegistryActionMenuProps): React.ReactElement;
24
+ export default RegistryActionMenu;