@ai-matrx/rich-content 0.2.9 → 0.2.15

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 (124) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/copy/copy-commands.d.ts +42 -0
  3. package/dist/copy/copy-commands.js +119 -0
  4. package/dist/copy/markdown-copy-html.d.ts +3 -0
  5. package/dist/copy/markdown-copy-html.js +8 -0
  6. package/dist/copy/markdown-readable-text.d.ts +4 -0
  7. package/dist/copy/markdown-readable-text.js +62 -0
  8. package/dist/display/blocks/json/json-tabular-utils.d.ts +7 -5
  9. package/dist/display/blocks/json/json-tabular-utils.js +26 -17
  10. package/dist/display/blocks/table/StreamingTableRenderer.js +11 -16
  11. package/dist/display/chat-markdown/BasicMarkdownContent.js +8 -6
  12. package/dist/display/chat-markdown/ConfigurableMarkdownContent.js +8 -6
  13. package/dist/display/chat-markdown/block-registry/BlockComponentRegistry.js +2 -0
  14. package/dist/display/chat-markdown/block-registry/block-dispatch.js +2 -0
  15. package/dist/display/tables/MarkdownTable.js +11 -16
  16. package/dist/display/tables/TableWithSeparatedControls.js +11 -16
  17. package/dist/display/tables/table-csv.d.ts +7 -0
  18. package/dist/display/tables/table-csv.js +19 -0
  19. package/dist/host/app-bindings.d.ts +32 -0
  20. package/dist/host/app-bindings.js +40 -0
  21. package/dist/host/feedback-subject.d.ts +16 -0
  22. package/dist/host/feedback-subject.js +0 -0
  23. package/dist/host/manual-copy.d.ts +7 -0
  24. package/dist/host/manual-copy.js +11 -0
  25. package/dist/host/registry.d.ts +2 -0
  26. package/dist/host/registry.js +10 -0
  27. package/dist/host/session-knob.d.ts +5 -0
  28. package/dist/host/session-knob.js +7 -0
  29. package/dist/host/types.d.ts +24 -0
  30. package/dist/rich-document/RegistryContextMenu.d.ts +29 -0
  31. package/dist/rich-document/RegistryContextMenu.js +40 -0
  32. package/dist/rich-document/RichDocument.d.ts +78 -0
  33. package/dist/rich-document/RichDocument.js +218 -0
  34. package/dist/rich-document/RichDocumentActionProvider.d.ts +18 -0
  35. package/dist/rich-document/RichDocumentActionProvider.js +18 -0
  36. package/dist/rich-document/RichDocumentActionSurface.d.ts +11 -0
  37. package/dist/rich-document/RichDocumentActionSurface.js +59 -0
  38. package/dist/rich-document/RichDocumentActions.d.ts +12 -0
  39. package/dist/rich-document/RichDocumentActions.js +29 -0
  40. package/dist/rich-document/actions/hostActions.d.ts +5 -0
  41. package/dist/rich-document/actions/hostActions.js +31 -0
  42. package/dist/rich-document/actions/labels.d.ts +9 -0
  43. package/dist/rich-document/actions/labels.js +8 -0
  44. package/dist/rich-document/actions/markdownTable.d.ts +9 -0
  45. package/dist/rich-document/actions/markdownTable.js +39 -0
  46. package/dist/rich-document/actions/provider.d.ts +58 -0
  47. package/dist/rich-document/actions/provider.js +245 -0
  48. package/dist/rich-document/actions/resumePendingAuthAction.d.ts +3 -0
  49. package/dist/rich-document/actions/resumePendingAuthAction.js +31 -0
  50. package/dist/rich-document/actions/sources/index.d.ts +9 -0
  51. package/dist/rich-document/actions/sources/index.js +22 -0
  52. package/dist/rich-document/actions/sources/raw.d.ts +4 -0
  53. package/dist/rich-document/actions/sources/raw.js +35 -0
  54. package/dist/rich-document/actions/useRichDocumentProvider.d.ts +1 -0
  55. package/dist/rich-document/actions/useRichDocumentProvider.js +13 -0
  56. package/dist/rich-document/host-types.d.ts +51 -0
  57. package/dist/rich-document/host-types.js +0 -0
  58. package/dist/rich-document/registrations.d.ts +1 -0
  59. package/dist/rich-document/registrations.js +13 -0
  60. package/dist/rich-document/review/applyTargets.d.ts +16 -0
  61. package/dist/rich-document/review/applyTargets.js +32 -0
  62. package/dist/rich-document/review/proposedEdit.d.ts +22 -0
  63. package/dist/rich-document/review/proposedEdit.js +48 -0
  64. package/dist/rich-document/review/protectedSpans.d.ts +14 -0
  65. package/dist/rich-document/review/protectedSpans.js +61 -0
  66. package/dist/rich-document/runtime/actionSurfacesStore.d.ts +30 -0
  67. package/dist/rich-document/runtime/actionSurfacesStore.js +79 -0
  68. package/dist/rich-document/runtime/contextMenuFor.d.ts +6 -0
  69. package/dist/rich-document/runtime/contextMenuFor.js +7 -0
  70. package/dist/rich-document/runtime/providerBridge.d.ts +20 -0
  71. package/dist/rich-document/runtime/providerBridge.js +23 -0
  72. package/dist/rich-document/runtime/useActionSurfaceProvider.d.ts +33 -0
  73. package/dist/rich-document/runtime/useActionSurfaceProvider.js +214 -0
  74. package/dist/rich-document/search/ContentFindControl.d.ts +8 -0
  75. package/dist/rich-document/search/ContentFindControl.js +46 -0
  76. package/dist/rich-document/search/RenderedFindBar.d.ts +8 -0
  77. package/dist/rich-document/search/RenderedFindBar.js +110 -0
  78. package/dist/rich-document/search/renderedFind.d.ts +1 -0
  79. package/dist/rich-document/search/renderedFind.js +1 -0
  80. package/dist/rich-document/types.d.ts +384 -0
  81. package/dist/rich-document/types.js +0 -0
  82. package/dist/rich-document/variants/ActionBar.d.ts +17 -0
  83. package/dist/rich-document/variants/ActionBar.js +48 -0
  84. package/dist/rich-document/variants/MenuVariant.d.ts +15 -0
  85. package/dist/rich-document/variants/MenuVariant.js +17 -0
  86. package/dist/rich-document/variants/MiniActionBar.d.ts +5 -0
  87. package/dist/rich-document/variants/MiniActionBar.js +11 -0
  88. package/dist/rich-document/variants/RegistryActionList.d.ts +17 -0
  89. package/dist/rich-document/variants/RegistryActionList.js +94 -0
  90. package/dist/rich-document/variants/RegistryActionMenu.d.ts +24 -0
  91. package/dist/rich-document/variants/RegistryActionMenu.js +74 -0
  92. package/dist/rich-document/variants/shared/AlchemyDocumentMenu.d.ts +7 -0
  93. package/dist/rich-document/variants/shared/AlchemyDocumentMenu.js +83 -0
  94. package/dist/rich-document/variants/shared/OpenOneMenuButton.d.ts +9 -0
  95. package/dist/rich-document/variants/shared/OpenOneMenuButton.js +70 -0
  96. package/dist/rich-document/variants/shared/StandaloneOneMenu.d.ts +8 -0
  97. package/dist/rich-document/variants/shared/StandaloneOneMenu.js +32 -0
  98. package/dist/rich-document/variants/shared/categories.d.ts +4 -0
  99. package/dist/rich-document/variants/shared/categories.js +31 -0
  100. package/dist/rich-document/variants/shared/iconMap.d.ts +2 -0
  101. package/dist/rich-document/variants/shared/iconMap.js +73 -0
  102. package/dist/rich-document/variants/shared/menuStructure.d.ts +81 -0
  103. package/dist/rich-document/variants/shared/menuStructure.js +268 -0
  104. package/dist/rich-document/variants/shared/runAction.d.ts +17 -0
  105. package/dist/rich-document/variants/shared/runAction.js +24 -0
  106. package/dist/selection-toolbar/SelectionToolbarFrame.d.ts +64 -0
  107. package/dist/selection-toolbar/SelectionToolbarFrame.js +191 -0
  108. package/dist/selection-toolbar/common-actions.d.ts +2 -0
  109. package/dist/selection-toolbar/common-actions.js +103 -0
  110. package/dist/selection-toolbar/selection-actions.d.ts +126 -0
  111. package/dist/selection-toolbar/selection-actions.js +240 -0
  112. package/dist/selection-toolbar/selection-copy.d.ts +19 -0
  113. package/dist/selection-toolbar/selection-copy.js +98 -0
  114. package/dist/selection-toolbar/selection-shape.d.ts +1 -0
  115. package/dist/selection-toolbar/selection-shape.js +15 -0
  116. package/dist/selection-toolbar/selection-zones.d.ts +90 -0
  117. package/dist/selection-toolbar/selection-zones.js +85 -0
  118. package/dist/selection-toolbar/textarea-caret.d.ts +8 -0
  119. package/dist/selection-toolbar/textarea-caret.js +69 -0
  120. package/dist/utils/alchemy-icon-keys.d.ts +18 -0
  121. package/dist/utils/alchemy-icon-keys.js +29 -0
  122. package/dist/utils/rendered-find.d.ts +9 -0
  123. package/dist/utils/rendered-find.js +68 -0
  124. package/package.json +21 -5
@@ -32,6 +32,21 @@ export interface RichContentAppBindings {
32
32
  useHostUser: HostFn;
33
33
  useIsAdminDebugger: HostFn;
34
34
  useIsAuthenticated: HostFn;
35
+ useHostDispatch: HostFn;
36
+ useHostGetState: HostFn;
37
+ useIsSuperAdmin: HostFn;
38
+ useActiveOrganizationId: HostFn;
39
+ holdDeliberateIntent: HostFn;
40
+ useAlchemyDisclosure: HostFn;
41
+ useRegistryMenuSource: HostFn;
42
+ sameContentSource: HostFn;
43
+ contentSourceKey: HostFn;
44
+ openContextMenuForElement: HostFn;
45
+ convertOriginForSource: HostFn;
46
+ useDocumentDialogsHost: HostFn;
47
+ RecordAnnotations: HostComponent;
48
+ annotationRecordOf: HostFn;
49
+ recordKeyOf: HostFn;
35
50
  useMermaidPreferences: HostFn;
36
51
  useSetMermaidPreferences: HostFn;
37
52
  useImageKnob: HostFn;
@@ -47,6 +62,7 @@ export interface RichContentAppBindings {
47
62
  MarkdownStream: HostComponent;
48
63
  CitationMarkerInline: HostComponent;
49
64
  InlineCopyButton: HostComponent;
65
+ CopyButtons: HostComponent;
50
66
  AdvancedMenu: HostComponent;
51
67
  useAdvancedMenu: HostFn;
52
68
  EntityRef: HostComponent;
@@ -127,8 +143,23 @@ export declare const KindInstanceRender: HostComponent;
127
143
  export declare const KindRecordChrome: HostComponent;
128
144
  export declare const kindHasRecordChrome: HostFn;
129
145
  export declare const KindFixItBar: HostComponent;
146
+ export declare const useHostDispatch: HostFn;
147
+ export declare const useHostGetState: HostFn;
148
+ export declare const useIsSuperAdmin: HostFn;
149
+ export declare const useActiveOrganizationId: HostFn;
150
+ export declare const useAlchemyDisclosure: HostFn;
151
+ export declare const useRegistryMenuSource: HostFn;
152
+ export declare const sameContentSource: HostFn;
153
+ export declare const contentSourceKey: HostFn;
154
+ export declare const openContextMenuForElement: HostFn;
155
+ export declare const convertOriginForSource: HostFn;
156
+ export declare const useDocumentDialogsHost: HostFn;
157
+ export declare const RecordAnnotations: HostComponent;
158
+ export declare const annotationRecordOf: HostFn;
159
+ export declare const recordKeyOf: HostFn;
130
160
  export declare const MarkdownStream: HostComponent;
131
161
  export declare const CitationMarkerInline: HostComponent;
162
+ export declare const CopyButtons: HostComponent;
132
163
  export declare const InlineCopyButton: HostComponent;
133
164
  export declare const AdvancedMenu: HostComponent;
134
165
  export declare const useAdvancedMenu: HostFn;
@@ -140,6 +171,7 @@ export declare const IconInputWithValidation: HostComponent;
140
171
  export declare const LocatedTableViewer: HostComponent;
141
172
  export declare const OpenDestinationDialog: HostComponent;
142
173
  export declare function hostHtmlPageService(): unknown;
174
+ export declare const holdDeliberateIntent: <T>(work: () => T | Promise<T>) => Promise<T>;
143
175
  export declare const ensureOrganizationContext: HostFn;
144
176
  export declare const isOrganizationSelectionCancelled: HostFn;
145
177
  export declare const loadExportTargets: HostFn;
@@ -68,8 +68,27 @@ const KindInstanceRender = component("KindInstanceRender", ValueFloor, "kind ins
68
68
  const KindRecordChrome = component("KindRecordChrome", Passthrough, "kind records show without record chrome");
69
69
  const kindHasRecordChrome = fn("kindHasRecordChrome", () => false, "kind records show without record chrome");
70
70
  const KindFixItBar = component("KindFixItBar", null, "no fix-it bar on broken kinds");
71
+ const useHostDispatch = fn("useHostDispatch", () => (action) => action, "actions dispatch nowhere");
72
+ const useHostGetState = fn("useHostGetState", () => () => ({}), "actions read an empty host state");
73
+ const useIsSuperAdmin = fn("useIsSuperAdmin", () => false, "admin-only actions are absent");
74
+ const useActiveOrganizationId = fn("useActiveOrganizationId", () => null, "no organization is active");
75
+ const useAlchemyDisclosure = fn("useAlchemyDisclosure", noop, "no Alchemy disclosure");
76
+ const useRegistryMenuSource = fn("useRegistryMenuSource", () => null, "no menu carries a content source");
77
+ const sameContentSource = fn(
78
+ "sameContentSource",
79
+ (a, b) => Boolean(a && b && JSON.stringify(a) === JSON.stringify(b)),
80
+ "content sources compare by value"
81
+ );
82
+ const contentSourceKey = fn("contentSourceKey", (s) => JSON.stringify(s), "content sources key by value");
83
+ const openContextMenuForElement = fn("openContextMenuForElement", noop, "no context menu opens");
84
+ const convertOriginForSource = fn("convertOriginForSource", () => null, "convert is absent");
85
+ const useDocumentDialogsHost = fn("useDocumentDialogsHost", () => ({ callbacks: {} }), "document dialogs are absent");
86
+ const RecordAnnotations = component("RecordAnnotations", Passthrough, "no reading set on saved records");
87
+ const annotationRecordOf = fn("annotationRecordOf", () => null, "no saved record is known");
88
+ const recordKeyOf = fn("recordKeyOf", (r) => `${r.token}:${r.id}`, "records key as token:id");
71
89
  const MarkdownStream = component("MarkdownStream", null, "the full renderer is not registered");
72
90
  const CitationMarkerInline = component("CitationMarkerInline", TextOnly, "citation markers show as text");
91
+ const CopyButtons = component("CopyButtons", null, "no copy menu on rendered content");
73
92
  const InlineCopyButton = component("InlineCopyButton", null, "no inline copy button");
74
93
  const AdvancedMenu = component("AdvancedMenu", null, "no block menus");
75
94
  const useAdvancedMenu = fn("useAdvancedMenu", () => ({ isOpen: false, open: noop, close: noop, toggle: noop, menuProps: {} }), "no block menus");
@@ -83,6 +102,11 @@ const OpenDestinationDialog = component("OpenDestinationDialog", null, "no desti
83
102
  function hostHtmlPageService() {
84
103
  return binding("HTMLPageService") ?? null;
85
104
  }
105
+ const holdDeliberateIntent = fn(
106
+ "holdDeliberateIntent",
107
+ (work) => Promise.resolve().then(work),
108
+ "actions run without an intent hold"
109
+ );
86
110
  const ensureOrganizationContext = fn("ensureOrganizationContext", async () => null, "no organization can be chosen");
87
111
  const isOrganizationSelectionCancelled = fn("isOrganizationSelectionCancelled", () => false, "no organization can be chosen");
88
112
  const loadExportTargets = fn("loadExportTargets", async () => null, "no workbook export");
@@ -105,6 +129,7 @@ export {
105
129
  ArtifactRender,
106
130
  CitationMarkerInline,
107
131
  CodeBlockWithContextAttach,
132
+ CopyButtons,
108
133
  EntityRef,
109
134
  IconInputWithValidation,
110
135
  InlineCopyButton,
@@ -119,12 +144,17 @@ export {
119
144
  NonEditableContextMenu,
120
145
  OpenDestinationDialog,
121
146
  RawJsonExplorer,
147
+ RecordAnnotations,
122
148
  StructuredValueView,
149
+ annotationRecordOf,
123
150
  announceProposedGoogleWrite,
151
+ contentSourceKey,
152
+ convertOriginForSource,
124
153
  createMermaidEditorScope,
125
154
  ensureOrganizationContext,
126
155
  getArtifactDef,
127
156
  hasArtifactRenderer,
157
+ holdDeliberateIntent,
128
158
  hostCodeEditorAgents,
129
159
  hostHtmlPageService,
130
160
  hostMermaidEditorManifest,
@@ -134,19 +164,28 @@ export {
134
164
  kindHasRecordChrome,
135
165
  loadExportTargets,
136
166
  loadSendToGoogle,
167
+ openContextMenuForElement,
168
+ recordKeyOf,
137
169
  reportCanvasOpenDrop,
138
170
  resolveArtifactDef,
171
+ sameContentSource,
139
172
  setImageKnobMapEntry,
173
+ useActiveOrganizationId,
140
174
  useAdvancedMenu,
175
+ useAlchemyDisclosure,
141
176
  useCanvas,
142
177
  useCanvasOpenGuard,
143
178
  useDiagramAgents,
179
+ useDocumentDialogsHost,
144
180
  useHideReasoning,
145
181
  useHideToolResults,
182
+ useHostDispatch,
183
+ useHostGetState,
146
184
  useHostUser,
147
185
  useImageKnob,
148
186
  useIsAdminDebugger,
149
187
  useIsAuthenticated,
188
+ useIsSuperAdmin,
150
189
  useMachineFramesVisible,
151
190
  useMermaidPreferences,
152
191
  useOpenArtifactInCanvas,
@@ -154,6 +193,7 @@ export {
154
193
  useOpenOverlay,
155
194
  useOpenSmartCodeEditorWindow,
156
195
  useOpenTableViewerWindow,
196
+ useRegistryMenuSource,
157
197
  useSaveAndOpenInCodeEditor,
158
198
  useSetMermaidPreferences,
159
199
  useThemeMode
@@ -0,0 +1,16 @@
1
+ /**
2
+ * What a feedback report points at (matrx-frontend: features/overlays/openers/feedbackDialog
3
+ * `FeedbackSubject`). The engine only builds the text-passage subject; the host owns the rest.
4
+ */
5
+ export interface TextPassageFeedbackSubject {
6
+ kind: "text_passage";
7
+ sourceToken: string;
8
+ sourceId: string;
9
+ sourceTitle: string;
10
+ quote: string;
11
+ anchor?: unknown;
12
+ [extra: string]: unknown;
13
+ }
14
+ export type FeedbackSubject = TextPassageFeedbackSubject | ({
15
+ kind: string;
16
+ } & Record<string, unknown>);
File without changes
@@ -0,0 +1,7 @@
1
+ export interface ManualCopyOptions {
2
+ /** The text the person needs on their clipboard. */
3
+ text: string;
4
+ title?: string;
5
+ description?: string;
6
+ }
7
+ export declare function showManualCopy(opts: ManualCopyOptions): void;
@@ -0,0 +1,11 @@
1
+ import { announceBareHost, hostCapability } from "./registry.js";
2
+ function showManualCopy(opts) {
3
+ if (!opts.text) return;
4
+ const show = hostCapability("showManualCopy");
5
+ if (show) return show(opts);
6
+ announceBareHost("showManualCopy", "a refused copy is written to the console");
7
+ console.warn("[rich-content] the clipboard refused this copy; the text follows:\n" + opts.text);
8
+ }
9
+ export {
10
+ showManualCopy
11
+ };
@@ -25,3 +25,5 @@ export declare function announceBareHost(capability: string, degraded: string):
25
25
  export declare const BARE_HOST_ATTR: {
26
26
  readonly "data-rich-content-host": "bare";
27
27
  };
28
+ /** Run the host's `ensureBlockRegistrations` once (see RichContentHost). */
29
+ export declare function ensureHostBlockRegistrations(): void;
@@ -44,10 +44,20 @@ function announceBareHost(capability, degraded) {
44
44
  console.warn(`[@ai-matrx/rich-content] The host registered no "${capability}", so ${degraded}.`);
45
45
  }
46
46
  const BARE_HOST_ATTR = { "data-rich-content-host": "bare" };
47
+ const ENSURED_KEY = /* @__PURE__ */ Symbol.for("ai-matrx.rich-content.block-registrations-ensured");
48
+ function ensureHostBlockRegistrations() {
49
+ const g = globalThis;
50
+ if (g[ENSURED_KEY]) return;
51
+ const ensure = hostCapability("ensureBlockRegistrations");
52
+ if (!ensure) return;
53
+ g[ENSURED_KEY] = true;
54
+ ensure();
55
+ }
47
56
  export {
48
57
  BARE_HOST_ATTR,
49
58
  announceBareHost,
50
59
  configureRichContent,
60
+ ensureHostBlockRegistrations,
51
61
  hostCapability,
52
62
  resetRichContentHost,
53
63
  richContentHostVersion,
@@ -0,0 +1,5 @@
1
+ export interface KnobRef {
2
+ feature: string;
3
+ key: string;
4
+ }
5
+ export declare function getSessionKnob(knob: KnobRef): unknown;
@@ -0,0 +1,7 @@
1
+ import { hostCapability } from "./registry.js";
2
+ function getSessionKnob(knob) {
3
+ return hostCapability("readSessionKnob")?.(knob);
4
+ }
5
+ export {
6
+ getSessionKnob
7
+ };
@@ -149,6 +149,30 @@ export interface RichContentHost {
149
149
  splitterEnvelopes: RichContentSplitterEnvelopes;
150
150
  /** A hook returning the host's "save these rows as a table" opener, or null. Default: no opener (the button is absent). */
151
151
  useOpenSaveToTable: () => ((options: Record<string, unknown>) => unknown) | null;
152
+ /**
153
+ * Loads the host's own block registrations on first use (matrx-frontend's Jest setup requires its
154
+ * registration module here, so suites that mock a block still see the mock). Called once, on the
155
+ * first block-registry or dispatch lookup. Default: none (registrations arrive by import).
156
+ */
157
+ ensureBlockRegistrations: () => void;
158
+ /** Loads the host's rich-document registrations (handlers, source adapters) on first registry read. */
159
+ ensureRichDocumentRegistrations: () => void;
160
+ /** The manual-copy dialog for a copy the clipboard refused. Default: the console. */
161
+ showManualCopy: (opts: {
162
+ text: string;
163
+ title?: string;
164
+ description?: string;
165
+ }) => void;
166
+ /** A knob's effective value for the signed-in person, synchronously. Default: undefined. */
167
+ readSessionKnob: (knob: {
168
+ feature: string;
169
+ key: string;
170
+ }) => unknown;
171
+ /**
172
+ * The editor's HTML → markdown converter (matrx-frontend: @ai-matrx/rich-editor core), loaded at
173
+ * copy time. Default: none — a copied selection keeps its shape text (tables and lists).
174
+ */
175
+ loadHtmlToMarkdown: () => Promise<(html: string) => string>;
152
176
  /** Loads mermaid (`() => import("mermaid").then((m) => m.default)`). Default: none — diagrams show their source. */
153
177
  loadMermaid: () => Promise<unknown>;
154
178
  /** Loads the ELK layout loaders (`() => import("@mermaid-js/layout-elk").then((m) => m.default)`). Default: dagre only. */
@@ -0,0 +1,29 @@
1
+ import * as React from "react";
2
+ import { NonEditableContextMenu } from "../host/app-bindings.js";
3
+ import type { ContentSource, RichDocumentAction, RichDocumentActionId, RichDocumentActionsProp } from "./types.js";
4
+ export interface RegistryContextMenuProps {
5
+ content: string;
6
+ source: ContentSource;
7
+ actions?: RichDocumentActionsProp;
8
+ /** Context-menu-only additions / removals. */
9
+ extra?: RichDocumentAction[];
10
+ exclude?: (RichDocumentActionId | string)[];
11
+ /** Yield to the native menu (e.g. while the content streams). */
12
+ suppressed?: boolean;
13
+ sourceFeature?: React.ComponentProps<typeof NonEditableContextMenu>["sourceFeature"];
14
+ surfaceName?: string;
15
+ /**
16
+ * The surface's declared values (e.g. `conversation_id` for the assistant
17
+ * message surface) — merged into the menu scope so bound agents and
18
+ * shortcuts receive them.
19
+ */
20
+ contextData?: Record<string, unknown>;
21
+ /**
22
+ * Which agent-menu placements show (e.g. `{ "content-block": "hide" }` over
23
+ * a rendered answer, where a block could only be copied, never inserted).
24
+ */
25
+ placementMode?: React.ComponentProps<typeof NonEditableContextMenu>["placementMode"];
26
+ children: React.ReactNode;
27
+ }
28
+ export declare function RegistryContextMenu(props: RegistryContextMenuProps): React.ReactElement;
29
+ export default RegistryContextMenu;
@@ -0,0 +1,40 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { NonEditableContextMenu } from "../host/app-bindings.js";
4
+ import { useActionSurfaceProvider } from "./runtime/useActionSurfaceProvider.js";
5
+ function RegistryContextMenu(props) {
6
+ const { content, source, actions, extra, exclude, suppressed, children } = props;
7
+ const { ctx } = useActionSurfaceProvider({
8
+ content,
9
+ source,
10
+ actions,
11
+ actionsVariant: "none"
12
+ });
13
+ const excludes = [...actions?.exclude ?? [], ...exclude ?? []];
14
+ return /* @__PURE__ */ jsx(
15
+ NonEditableContextMenu,
16
+ {
17
+ sourceFeature: props.sourceFeature ?? "documents",
18
+ surfaceName: props.surfaceName,
19
+ suppressed,
20
+ contentSource: source,
21
+ contextData: { ...props.contextData, content: ctx.content },
22
+ placementMode: props.placementMode,
23
+ excludedRichActions: excludes,
24
+ richDocCtxExtras: {
25
+ callbacks: actions?.callbacks,
26
+ extensions: actions?.extensions,
27
+ metadata: actions?.metadata ?? null,
28
+ isCreator: actions?.isCreator ?? false,
29
+ surfaceKey: actions?.surfaceKey ?? null
30
+ },
31
+ extraRichActions: [...actions?.extra ?? [], ...extra ?? []],
32
+ children
33
+ }
34
+ );
35
+ }
36
+ var RegistryContextMenu_default = RegistryContextMenu;
37
+ export {
38
+ RegistryContextMenu,
39
+ RegistryContextMenu_default as default
40
+ };
@@ -0,0 +1,78 @@
1
+ import * as React from "react";
2
+ import { type SpecimenMode } from "../display/specimen/SpecimenContext.js";
3
+ import type { ContentSource, RichDocumentAction, RichDocumentActionId, RichDocumentActionsProp, RichDocumentActionsVariant, RichDocumentActionsPosition, RichDocumentActionsBehavior } from "./types.js";
4
+ import type { AnnotationRecord, ServerProcessedBlock } from "./host-types.js";
5
+ import type { TypedStreamEvent } from "../display/chat-markdown/types.js";
6
+ export interface RichDocumentProps {
7
+ content?: string;
8
+ events?: TypedStreamEvent[];
9
+ serverProcessedBlocks?: ServerProcessedBlock[];
10
+ taskId?: string;
11
+ requestId?: string;
12
+ turnId?: string;
13
+ conversationId?: string;
14
+ isStreamActive?: boolean;
15
+ /** Required. Use { type: "raw" } when the source is genuinely unknown. */
16
+ source: ContentSource;
17
+ renderer?: "basic" | "configurable" | "stream";
18
+ actions?: RichDocumentActionsProp;
19
+ actionsVariant?: RichDocumentActionsVariant;
20
+ /** Where the action surface sits. Default "below". */
21
+ actionsPosition?: RichDocumentActionsPosition;
22
+ /** Visibility. Default "always". */
23
+ actionsBehavior?: RichDocumentActionsBehavior;
24
+ /** Required when actionsVariant === "remote". */
25
+ actionsSurfaceId?: string;
26
+ /**
27
+ * DECLARE that this content is a specimen — generated work that is meant to
28
+ * look right and be wrong (the Bad Example probe, a wrong explanation, a
29
+ * decoy option). Suppresses every action surface, the right-click menu, and
30
+ * the actions inside the content itself (table export / workbook / Google
31
+ * Sheet / edit / open-in-window), and prints the specimen banner in their
32
+ * place. Pass `true` for the default wording, or override the two lines.
33
+ * See @ai-matrx/rich-content/display/specimen/SpecimenContext.tsx.
34
+ */
35
+ specimen?: boolean | Partial<SpecimenMode>;
36
+ /**
37
+ * Enable a right-click context menu over the content. Lazy-loaded (the
38
+ * menu chunk only ships after the first right-click) and streaming-safe
39
+ * (yields to the native browser menu while isStreamActive). Pass an object
40
+ * to add context-menu-only `extra` actions or `exclude` specific ones.
41
+ * This is the extension point for future per-surface right-click actions.
42
+ */
43
+ enableContextMenu?: boolean | {
44
+ extra?: RichDocumentAction[];
45
+ exclude?: (RichDocumentActionId | string)[];
46
+ };
47
+ /**
48
+ * The SAVED RECORD this content is, for the reading set (highlight, comment,
49
+ * suggest, link) and its Notes & comments dock. Derived from `source` for a
50
+ * note, a chat answer and a saved working document; pass it when the source
51
+ * does not say (the studio previewing a loaded document), or `null` to turn
52
+ * the reading set off. A read-only copy, a stream in flight and a specimen
53
+ * never get one.
54
+ */
55
+ annotationRecord?: AnnotationRecord | null;
56
+ className?: string;
57
+ contentClassName?: string;
58
+ actionsClassName?: string;
59
+ onContentChange?: (newContent: string) => void;
60
+ applyLocalEdits?: boolean;
61
+ analysisData?: unknown;
62
+ messageId?: string;
63
+ allowFullScreenEditor?: boolean;
64
+ hideCopyButton?: boolean;
65
+ onError?: (error: string) => void;
66
+ onPhaseUpdate?: (phase: string) => void;
67
+ strictServerData?: boolean;
68
+ /**
69
+ * WHO WROTE this content — "ai" (a model), "other" (someone else) or "self"
70
+ * (the viewer). Decides whether remote images load by themselves
71
+ * (components/rich-content/prose/remote-image-policy.tsx, chair ruling
72
+ * 2026-09-25). Declare it at every call site — guard:
73
+ * components/rich-content/__tests__/image-policy-declared.test.ts.
74
+ */
75
+ imagePolicy?: "self" | "other" | "ai" | "inherit";
76
+ }
77
+ export declare function RichDocument(props: RichDocumentProps): React.ReactElement;
78
+ export default RichDocument;
@@ -0,0 +1,218 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { cn } from "@ai-matrx/design-system";
4
+ import { useActionSurfaceProvider } from "./runtime/useActionSurfaceProvider.js";
5
+ import { RegistryContextMenu } from "./RegistryContextMenu.js";
6
+ import { getSourceAdapter } from "./actions/sources/index.js";
7
+ import {
8
+ convertOriginForSource,
9
+ useDocumentDialogsHost,
10
+ RecordAnnotations,
11
+ annotationRecordOf,
12
+ recordKeyOf,
13
+ MarkdownStream
14
+ } from "../host/app-bindings.js";
15
+ import { ActionBar } from "./variants/ActionBar.js";
16
+ import { MiniActionBar } from "./variants/MiniActionBar.js";
17
+ import { MenuVariant } from "./variants/MenuVariant.js";
18
+ import { contextMenuFor } from "./runtime/contextMenuFor.js";
19
+ import {
20
+ SpecimenProvider,
21
+ SpecimenBanner,
22
+ resolveSpecimenMode
23
+ } from "../display/specimen/SpecimenContext.js";
24
+ function RichDocument(props) {
25
+ const {
26
+ source,
27
+ actions: actionsProp,
28
+ actionsVariant = "none",
29
+ actionsPosition = "below",
30
+ actionsBehavior = "always",
31
+ actionsSurfaceId,
32
+ specimen,
33
+ enableContextMenu,
34
+ className,
35
+ contentClassName,
36
+ actionsClassName,
37
+ content,
38
+ events,
39
+ serverProcessedBlocks,
40
+ taskId,
41
+ requestId,
42
+ turnId,
43
+ conversationId,
44
+ isStreamActive,
45
+ onContentChange,
46
+ applyLocalEdits,
47
+ analysisData,
48
+ messageId,
49
+ allowFullScreenEditor,
50
+ hideCopyButton,
51
+ onError,
52
+ onPhaseUpdate,
53
+ strictServerData,
54
+ imagePolicy,
55
+ annotationRecord
56
+ } = props;
57
+ const specimenMode = resolveSpecimenMode(specimen);
58
+ const effectiveActionsVariant = specimenMode ? "none" : actionsVariant;
59
+ const dialogsHost = useDocumentDialogsHost({
60
+ convertOrigin: convertOriginForSource(
61
+ source,
62
+ source.type === "note" ? "Note" : "Chat response"
63
+ ),
64
+ text: content ?? "",
65
+ // Clean up / Help / Custom agent need somewhere to APPLY the result.
66
+ writable: !source.readOnly && Boolean(getSourceAdapter(source.type).edit) && !(actionsProp?.extensions?.type === "chat-message" && (actionsProp.extensions.contentIsStructuredRaw || actionsProp.extensions.role === "user")),
67
+ chatMessage: source.type === "chat-message" && !source.readOnly ? {
68
+ conversationId: source.conversationId,
69
+ messageId: source.messageId,
70
+ surfaceKey: actionsProp?.surfaceKey ?? null
71
+ } : null
72
+ });
73
+ const record = annotationRecord !== void 0 ? annotationRecord : source.readOnly ? null : annotationRecordOf(source);
74
+ const hostedActions = {
75
+ ...actionsProp,
76
+ callbacks: {
77
+ ...dialogsHost.callbacks,
78
+ // A host that names its record explicitly (the studio's loaded document
79
+ // rides a raw source) tells the ⋯ "Notes & comments" row which dock it is.
80
+ ...record && annotationRecord !== void 0 ? { annotationRecordKey: () => recordKeyOf(record) } : {},
81
+ ...actionsProp?.callbacks
82
+ }
83
+ };
84
+ const { ctx, getCtx, resolvedActions, target } = useActionSurfaceProvider({
85
+ content,
86
+ source,
87
+ actions: hostedActions,
88
+ actionsVariant: effectiveActionsVariant,
89
+ actionsSurfaceId
90
+ });
91
+ let variantNode = null;
92
+ switch (effectiveActionsVariant) {
93
+ case "bar":
94
+ variantNode = /* @__PURE__ */ jsx(
95
+ ActionBar,
96
+ {
97
+ actions: resolvedActions,
98
+ getCtx,
99
+ target,
100
+ sourceId: ctx.instanceKey("alchemy")
101
+ }
102
+ );
103
+ break;
104
+ case "mini-bar":
105
+ variantNode = /* @__PURE__ */ jsx(
106
+ MiniActionBar,
107
+ {
108
+ actions: resolvedActions,
109
+ getCtx,
110
+ target,
111
+ sourceId: ctx.instanceKey("alchemy")
112
+ }
113
+ );
114
+ break;
115
+ case "menu":
116
+ case "icon-only":
117
+ variantNode = /* @__PURE__ */ jsx(MenuVariant, { getCtx, target });
118
+ break;
119
+ case "remote":
120
+ case "none":
121
+ default:
122
+ variantNode = null;
123
+ }
124
+ const isAbsolute = actionsPosition === "top-right" || actionsPosition === "top-left" || actionsPosition === "middle-right" || actionsPosition === "middle-left";
125
+ const absolutePositionClass = isAbsolute ? cn(
126
+ "absolute z-10",
127
+ actionsPosition === "top-right" && "right-1 top-1",
128
+ actionsPosition === "top-left" && "left-1 top-1",
129
+ actionsPosition === "middle-right" && "right-1 top-1/2 -translate-y-1/2",
130
+ actionsPosition === "middle-left" && "left-1 top-1/2 -translate-y-1/2"
131
+ ) : null;
132
+ const hoverClass = actionsBehavior === "hover-only" ? "opacity-0 transition-opacity group-hover:opacity-100 focus-within:opacity-100" : null;
133
+ const inFlowClass = !isAbsolute ? actionsPosition === "above" ? "mb-1" : "mt-1" : null;
134
+ const actionsNode = variantNode ? /* @__PURE__ */ jsx(
135
+ "div",
136
+ {
137
+ className: cn(
138
+ absolutePositionClass,
139
+ hoverClass,
140
+ inFlowClass,
141
+ actionsClassName
142
+ ),
143
+ children: variantNode
144
+ }
145
+ ) : null;
146
+ const rootClassName = cn(
147
+ "rich-document",
148
+ (isAbsolute || actionsBehavior === "hover-only") && "relative group",
149
+ className
150
+ );
151
+ const annotationSource = record && !specimenMode && !isStreamActive && typeof content === "string" && content.trim() ? {
152
+ token: record.token,
153
+ id: record.id,
154
+ title: record.title ?? "",
155
+ body: content,
156
+ contentVersion: Math.max(1, record.contentVersion ?? 1),
157
+ ...record.href ? { href: record.href } : {},
158
+ ...record.conversationId ? { conversationId: record.conversationId } : {}
159
+ } : null;
160
+ const engineInner = /* @__PURE__ */ jsx(RecordAnnotations, { record: annotationSource, children: /* @__PURE__ */ jsx("div", { className: cn("rich-document__content", contentClassName), children: /* @__PURE__ */ jsx(
161
+ MarkdownStream,
162
+ {
163
+ imagePolicy,
164
+ content,
165
+ events,
166
+ serverProcessedBlocks,
167
+ taskId,
168
+ requestId,
169
+ turnId,
170
+ conversationId,
171
+ isStreamActive,
172
+ onContentChange,
173
+ applyLocalEdits,
174
+ analysisData,
175
+ messageId,
176
+ allowFullScreenEditor,
177
+ hideCopyButton,
178
+ onError,
179
+ onPhaseUpdate,
180
+ strictServerData
181
+ }
182
+ ) }) });
183
+ let engine = engineInner;
184
+ const contextMenu = contextMenuFor(enableContextMenu, effectiveActionsVariant);
185
+ if (contextMenu && !specimenMode) {
186
+ const cmOptions = typeof contextMenu === "object" ? contextMenu : {};
187
+ engine = /* @__PURE__ */ jsx(
188
+ RegistryContextMenu,
189
+ {
190
+ content: content ?? "",
191
+ source,
192
+ actions: hostedActions,
193
+ extra: cmOptions.extra,
194
+ exclude: cmOptions.exclude,
195
+ suppressed: isStreamActive,
196
+ children: engineInner
197
+ }
198
+ );
199
+ }
200
+ if (specimenMode) {
201
+ return /* @__PURE__ */ jsxs("div", { className: rootClassName, children: [
202
+ /* @__PURE__ */ jsx(SpecimenBanner, { mode: specimenMode }),
203
+ /* @__PURE__ */ jsx(SpecimenProvider, { value: specimenMode, children: engine })
204
+ ] });
205
+ }
206
+ return /* @__PURE__ */ jsxs("div", { className: rootClassName, children: [
207
+ isAbsolute ? actionsNode : null,
208
+ !isAbsolute && actionsPosition === "above" ? actionsNode : null,
209
+ engine,
210
+ !isAbsolute && actionsPosition !== "above" ? actionsNode : null,
211
+ dialogsHost.dialogs
212
+ ] });
213
+ }
214
+ var RichDocument_default = RichDocument;
215
+ export {
216
+ RichDocument,
217
+ RichDocument_default as default
218
+ };
@@ -0,0 +1,18 @@
1
+ import * as React from "react";
2
+ import type { ContentSource, RichDocumentActionsProp } from "./types.js";
3
+ export interface RichDocumentActionProviderProps {
4
+ /** Live content the actions operate on (re-read at click time via the bridge). */
5
+ content: string;
6
+ /** Source identity — drives which actions show + save-to-task parent linking. */
7
+ source: ContentSource;
8
+ /** The surface a <RichDocumentActionSurface/> consumes. Required. */
9
+ surfaceId: string;
10
+ /** Trim/extend the action set + supply host callbacks (same shape as RichDocument). */
11
+ actions?: RichDocumentActionsProp;
12
+ }
13
+ /**
14
+ * Registers a remote RichDocument action provider for `surfaceId`. Renders
15
+ * nothing. See the file header for when to reach for this over RichDocument.
16
+ */
17
+ export declare function RichDocumentActionProvider(props: RichDocumentActionProviderProps): React.ReactElement | null;
18
+ export default RichDocumentActionProvider;