@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.
- package/dist/copy/copy-commands.d.ts +42 -0
- package/dist/copy/copy-commands.js +119 -0
- package/dist/copy/markdown-copy-html.d.ts +3 -0
- package/dist/copy/markdown-copy-html.js +8 -0
- package/dist/copy/markdown-readable-text.d.ts +4 -0
- package/dist/copy/markdown-readable-text.js +62 -0
- package/dist/display/chat-markdown/block-registry/BlockComponentRegistry.js +2 -0
- package/dist/display/chat-markdown/block-registry/block-dispatch.js +2 -0
- package/dist/host/app-bindings.d.ts +30 -0
- package/dist/host/app-bindings.js +38 -0
- package/dist/host/feedback-subject.d.ts +16 -0
- package/dist/host/feedback-subject.js +0 -0
- package/dist/host/manual-copy.d.ts +7 -0
- package/dist/host/manual-copy.js +11 -0
- package/dist/host/registry.d.ts +2 -0
- package/dist/host/registry.js +10 -0
- package/dist/host/session-knob.d.ts +5 -0
- package/dist/host/session-knob.js +7 -0
- package/dist/host/types.d.ts +24 -0
- package/dist/rich-document/RegistryContextMenu.d.ts +29 -0
- package/dist/rich-document/RegistryContextMenu.js +40 -0
- package/dist/rich-document/RichDocument.d.ts +78 -0
- package/dist/rich-document/RichDocument.js +218 -0
- package/dist/rich-document/RichDocumentActionProvider.d.ts +18 -0
- package/dist/rich-document/RichDocumentActionProvider.js +18 -0
- package/dist/rich-document/RichDocumentActionSurface.d.ts +11 -0
- package/dist/rich-document/RichDocumentActionSurface.js +59 -0
- package/dist/rich-document/RichDocumentActions.d.ts +12 -0
- package/dist/rich-document/RichDocumentActions.js +29 -0
- package/dist/rich-document/actions/hostActions.d.ts +5 -0
- package/dist/rich-document/actions/hostActions.js +31 -0
- package/dist/rich-document/actions/labels.d.ts +9 -0
- package/dist/rich-document/actions/labels.js +8 -0
- package/dist/rich-document/actions/markdownTable.d.ts +9 -0
- package/dist/rich-document/actions/markdownTable.js +39 -0
- package/dist/rich-document/actions/provider.d.ts +58 -0
- package/dist/rich-document/actions/provider.js +245 -0
- package/dist/rich-document/actions/resumePendingAuthAction.d.ts +3 -0
- package/dist/rich-document/actions/resumePendingAuthAction.js +31 -0
- package/dist/rich-document/actions/sources/index.d.ts +9 -0
- package/dist/rich-document/actions/sources/index.js +22 -0
- package/dist/rich-document/actions/sources/raw.d.ts +4 -0
- package/dist/rich-document/actions/sources/raw.js +35 -0
- package/dist/rich-document/actions/useRichDocumentProvider.d.ts +1 -0
- package/dist/rich-document/actions/useRichDocumentProvider.js +13 -0
- package/dist/rich-document/host-types.d.ts +51 -0
- package/dist/rich-document/host-types.js +0 -0
- package/dist/rich-document/registrations.d.ts +1 -0
- package/dist/rich-document/registrations.js +13 -0
- package/dist/rich-document/review/applyTargets.d.ts +16 -0
- package/dist/rich-document/review/applyTargets.js +32 -0
- package/dist/rich-document/review/proposedEdit.d.ts +22 -0
- package/dist/rich-document/review/proposedEdit.js +48 -0
- package/dist/rich-document/review/protectedSpans.d.ts +14 -0
- package/dist/rich-document/review/protectedSpans.js +61 -0
- package/dist/rich-document/runtime/actionSurfacesStore.d.ts +30 -0
- package/dist/rich-document/runtime/actionSurfacesStore.js +79 -0
- package/dist/rich-document/runtime/contextMenuFor.d.ts +6 -0
- package/dist/rich-document/runtime/contextMenuFor.js +7 -0
- package/dist/rich-document/runtime/providerBridge.d.ts +20 -0
- package/dist/rich-document/runtime/providerBridge.js +23 -0
- package/dist/rich-document/runtime/useActionSurfaceProvider.d.ts +33 -0
- package/dist/rich-document/runtime/useActionSurfaceProvider.js +214 -0
- package/dist/rich-document/search/ContentFindControl.d.ts +8 -0
- package/dist/rich-document/search/ContentFindControl.js +46 -0
- package/dist/rich-document/search/RenderedFindBar.d.ts +8 -0
- package/dist/rich-document/search/RenderedFindBar.js +110 -0
- package/dist/rich-document/search/renderedFind.d.ts +1 -0
- package/dist/rich-document/search/renderedFind.js +1 -0
- package/dist/rich-document/types.d.ts +384 -0
- package/dist/rich-document/types.js +0 -0
- package/dist/rich-document/variants/ActionBar.d.ts +17 -0
- package/dist/rich-document/variants/ActionBar.js +48 -0
- package/dist/rich-document/variants/MenuVariant.d.ts +15 -0
- package/dist/rich-document/variants/MenuVariant.js +17 -0
- package/dist/rich-document/variants/MiniActionBar.d.ts +5 -0
- package/dist/rich-document/variants/MiniActionBar.js +11 -0
- package/dist/rich-document/variants/RegistryActionList.d.ts +17 -0
- package/dist/rich-document/variants/RegistryActionList.js +94 -0
- package/dist/rich-document/variants/RegistryActionMenu.d.ts +24 -0
- package/dist/rich-document/variants/RegistryActionMenu.js +74 -0
- package/dist/rich-document/variants/shared/AlchemyDocumentMenu.d.ts +7 -0
- package/dist/rich-document/variants/shared/AlchemyDocumentMenu.js +83 -0
- package/dist/rich-document/variants/shared/OpenOneMenuButton.d.ts +9 -0
- package/dist/rich-document/variants/shared/OpenOneMenuButton.js +70 -0
- package/dist/rich-document/variants/shared/StandaloneOneMenu.d.ts +8 -0
- package/dist/rich-document/variants/shared/StandaloneOneMenu.js +32 -0
- package/dist/rich-document/variants/shared/categories.d.ts +4 -0
- package/dist/rich-document/variants/shared/categories.js +31 -0
- package/dist/rich-document/variants/shared/iconMap.d.ts +2 -0
- package/dist/rich-document/variants/shared/iconMap.js +73 -0
- package/dist/rich-document/variants/shared/menuStructure.d.ts +81 -0
- package/dist/rich-document/variants/shared/menuStructure.js +271 -0
- package/dist/rich-document/variants/shared/runAction.d.ts +17 -0
- package/dist/rich-document/variants/shared/runAction.js +24 -0
- package/dist/selection-toolbar/SelectionToolbarFrame.d.ts +64 -0
- package/dist/selection-toolbar/SelectionToolbarFrame.js +191 -0
- package/dist/selection-toolbar/common-actions.d.ts +2 -0
- package/dist/selection-toolbar/common-actions.js +103 -0
- package/dist/selection-toolbar/selection-actions.d.ts +126 -0
- package/dist/selection-toolbar/selection-actions.js +240 -0
- package/dist/selection-toolbar/selection-copy.d.ts +19 -0
- package/dist/selection-toolbar/selection-copy.js +98 -0
- package/dist/selection-toolbar/selection-shape.d.ts +1 -0
- package/dist/selection-toolbar/selection-shape.js +15 -0
- package/dist/selection-toolbar/selection-zones.d.ts +90 -0
- package/dist/selection-toolbar/selection-zones.js +85 -0
- package/dist/selection-toolbar/textarea-caret.d.ts +8 -0
- package/dist/selection-toolbar/textarea-caret.js +69 -0
- package/dist/utils/alchemy-icon-keys.d.ts +18 -0
- package/dist/utils/alchemy-icon-keys.js +29 -0
- package/dist/utils/rendered-find.d.ts +9 -0
- package/dist/utils/rendered-find.js +68 -0
- 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;
|