@ai-matrx/rich-content 0.2.10 → 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.
- 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 +268 -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 +22 -6
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { markdownToReadableText } from "./markdown-readable-text.js";
|
|
2
|
+
interface CopyOptions {
|
|
3
|
+
isMarkdown?: boolean;
|
|
4
|
+
formatForGoogleDocs?: boolean;
|
|
5
|
+
formatForWordPress?: boolean;
|
|
6
|
+
formatJson?: boolean;
|
|
7
|
+
showHtmlPreview?: boolean;
|
|
8
|
+
includeThinking?: boolean;
|
|
9
|
+
onSuccess?: () => void;
|
|
10
|
+
onError?: (err: unknown) => void;
|
|
11
|
+
onShowHtmlPreview?: (html: string) => boolean | void | Promise<boolean | void>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Copy CONTENT (markdown, text, or any JSON-able value) through Alchemy's serialization and browser transport.
|
|
15
|
+
* Not the plain-string copy — that is `copyToClipboard` in `@/lib/clipboard/copy`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function copyContent(content: unknown, options?: CopyOptions): Promise<boolean>;
|
|
18
|
+
export type CopyFlavor = "default" | "markdown" | "text";
|
|
19
|
+
export { markdownToReadableText };
|
|
20
|
+
/** The knob that decides what a single Copy click puts in a plain field. */
|
|
21
|
+
export declare const COPY_DEFAULT_FLAVOR_KNOB: {
|
|
22
|
+
readonly feature: "copy";
|
|
23
|
+
readonly key: "default_flavor";
|
|
24
|
+
};
|
|
25
|
+
/** The plain-text flavor of a single Copy click for this session ("markdown" until the knob answers). */
|
|
26
|
+
export declare function defaultCopyFlavor(): "markdown" | "text";
|
|
27
|
+
export interface RichCopyOptions {
|
|
28
|
+
/** Keep `<thinking>` / reasoning blocks (default: removed). */
|
|
29
|
+
includeThinking?: boolean;
|
|
30
|
+
/** Toast on success: a label, or false for none. Default: the flavor's label. */
|
|
31
|
+
toast?: string | false;
|
|
32
|
+
}
|
|
33
|
+
/** The bytes a flavor writes — pure enough to test; the HTML is loaded at click time. */
|
|
34
|
+
export declare function richCopyPlainText(markdown: string, flavor: CopyFlavor, defaultFlavor?: "markdown" | "text"): string;
|
|
35
|
+
/**
|
|
36
|
+
* Write the plain flavor, or html + plain in one clipboard item (kit's `copyRich`; `html` may be a
|
|
37
|
+
* promise so the item is created inside the click). A refused rich write degrades to plain text and
|
|
38
|
+
* says so; when nothing lands the person is handed the text to copy by hand.
|
|
39
|
+
*/
|
|
40
|
+
export declare function writeClipboardFlavors(plain: string, html?: Promise<string> | string): Promise<boolean>;
|
|
41
|
+
/** THE copy of rich content. Returns true when the clipboard holds it. */
|
|
42
|
+
export declare function copyRichContent(markdown: string, flavor?: CopyFlavor, options?: RichCopyOptions): Promise<boolean>;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createBrowserTransport,
|
|
3
|
+
createDraft,
|
|
4
|
+
capture,
|
|
5
|
+
formatAdapter,
|
|
6
|
+
normalizeTransferJson,
|
|
7
|
+
serialize
|
|
8
|
+
} from "@ai-matrx/alchemy/operate";
|
|
9
|
+
import { copyRich, copyText } from "@ai-matrx/kit/clipboard";
|
|
10
|
+
import { showManualCopy } from "../host/manual-copy.js";
|
|
11
|
+
import { toast } from "../host/toast.js";
|
|
12
|
+
import { getSessionKnob } from "../host/session-knob.js";
|
|
13
|
+
import { markdownToReadableText } from "./markdown-readable-text.js";
|
|
14
|
+
import { formattedCopyHtml, removeThinkingContent } from "./markdown-copy-html.js";
|
|
15
|
+
async function copyContent(content, options = {}) {
|
|
16
|
+
let capturedText;
|
|
17
|
+
try {
|
|
18
|
+
let payload;
|
|
19
|
+
if (typeof content === "string") {
|
|
20
|
+
const text = options.includeThinking ? content : removeThinkingContent(content);
|
|
21
|
+
payload = { kind: options.isMarkdown ? "markdown" : "text", text };
|
|
22
|
+
if (options.formatJson !== false) {
|
|
23
|
+
let parsed;
|
|
24
|
+
try {
|
|
25
|
+
parsed = JSON.parse(text);
|
|
26
|
+
} catch {
|
|
27
|
+
parsed = void 0;
|
|
28
|
+
}
|
|
29
|
+
if (parsed !== void 0)
|
|
30
|
+
payload = { kind: "json", value: normalizeTransferJson(parsed) };
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
payload = { kind: "json", value: normalizeTransferJson(content) };
|
|
34
|
+
}
|
|
35
|
+
const signal = new AbortController().signal;
|
|
36
|
+
const { snapshot } = await capture(payload, signal);
|
|
37
|
+
const draft = createDraft(snapshot);
|
|
38
|
+
const rich = payload.kind === "markdown" && (options.formatForGoogleDocs || options.formatForWordPress);
|
|
39
|
+
const artifact = rich ? await (await formatAdapter("rich-html").load()).build(
|
|
40
|
+
draft,
|
|
41
|
+
{ profile: options.formatForGoogleDocs ? "google-docs" : "generic" },
|
|
42
|
+
signal
|
|
43
|
+
) : serialize(
|
|
44
|
+
draft,
|
|
45
|
+
payload.kind === "json" ? options.formatJson === false ? "compact-json" : "json" : "plain"
|
|
46
|
+
);
|
|
47
|
+
capturedText = artifact.plainText;
|
|
48
|
+
if (options.showHtmlPreview) {
|
|
49
|
+
if (!options.onShowHtmlPreview || !artifact.html)
|
|
50
|
+
throw new Error(
|
|
51
|
+
"HTML preview requires a formatted Markdown source and a preview callback."
|
|
52
|
+
);
|
|
53
|
+
const previewDelivered = await options.onShowHtmlPreview(artifact.html);
|
|
54
|
+
if (previewDelivered === false) return false;
|
|
55
|
+
options.onSuccess?.();
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
const outcome = await createBrowserTransport().copy(artifact, signal);
|
|
59
|
+
if (outcome.status === "success") {
|
|
60
|
+
options.onSuccess?.();
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
if (outcome.status === "degraded") {
|
|
64
|
+
toast.info(`Copied as plain text: ${outcome.reason}`);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
if (outcome.status === "cancelled") return false;
|
|
68
|
+
throw new Error(outcome.message);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (capturedText !== void 0) {
|
|
71
|
+
showManualCopy({ text: capturedText });
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
options.onError?.(error);
|
|
75
|
+
if (!options.onError)
|
|
76
|
+
toast.error(
|
|
77
|
+
error instanceof Error ? error.message : "Could not copy content"
|
|
78
|
+
);
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const COPY_DEFAULT_FLAVOR_KNOB = { feature: "copy", key: "default_flavor" };
|
|
83
|
+
function defaultCopyFlavor() {
|
|
84
|
+
return getSessionKnob(COPY_DEFAULT_FLAVOR_KNOB) === "text" ? "text" : "markdown";
|
|
85
|
+
}
|
|
86
|
+
const FLAVOR_TOAST = {
|
|
87
|
+
default: "Copied",
|
|
88
|
+
markdown: "Markdown copied",
|
|
89
|
+
text: "Text copied"
|
|
90
|
+
};
|
|
91
|
+
function richCopyPlainText(markdown, flavor, defaultFlavor = "markdown") {
|
|
92
|
+
const plainFlavor = flavor === "default" ? defaultFlavor : flavor;
|
|
93
|
+
return plainFlavor === "text" ? markdownToReadableText(markdown) : markdown;
|
|
94
|
+
}
|
|
95
|
+
async function writeClipboardFlavors(plain, html) {
|
|
96
|
+
const landed = html === void 0 ? await copyText(plain) : await copyRich(
|
|
97
|
+
{ text: plain, html },
|
|
98
|
+
{ onDegraded: (reason) => toast.info(`Copied without formatting: ${reason}.`) }
|
|
99
|
+
);
|
|
100
|
+
if (!landed) showManualCopy({ text: plain });
|
|
101
|
+
return landed;
|
|
102
|
+
}
|
|
103
|
+
async function copyRichContent(markdown, flavor = "default", options = {}) {
|
|
104
|
+
const source = options.includeThinking ? markdown : removeThinkingContent(markdown);
|
|
105
|
+
const plain = richCopyPlainText(source, flavor, defaultCopyFlavor());
|
|
106
|
+
const ok = await writeClipboardFlavors(plain, flavor === "default" ? formattedCopyHtml(source) : void 0);
|
|
107
|
+
const label = options.toast === void 0 ? FLAVOR_TOAST[flavor] : options.toast;
|
|
108
|
+
if (ok && label) toast.success(label);
|
|
109
|
+
return ok;
|
|
110
|
+
}
|
|
111
|
+
export {
|
|
112
|
+
COPY_DEFAULT_FLAVOR_KNOB,
|
|
113
|
+
copyContent,
|
|
114
|
+
copyRichContent,
|
|
115
|
+
defaultCopyFlavor,
|
|
116
|
+
markdownToReadableText,
|
|
117
|
+
richCopyPlainText,
|
|
118
|
+
writeClipboardFlavors
|
|
119
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { removeThinkingContent } from "@ai-matrx/print/markdown";
|
|
2
|
+
function formattedCopyHtml(markdown) {
|
|
3
|
+
return import("@ai-matrx/print/markdown").then(({ markdownToHtml }) => markdownToHtml(markdown));
|
|
4
|
+
}
|
|
5
|
+
export {
|
|
6
|
+
formattedCopyHtml,
|
|
7
|
+
removeThinkingContent
|
|
8
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Inline markup → its words. Code spans are kept as their text. */
|
|
2
|
+
export declare function readableInline(line: string): string;
|
|
3
|
+
/** Markdown → readable plain text (envelopes unwrapped first). */
|
|
4
|
+
export declare function markdownToReadableText(markdown: string): string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { unwrapKindEnvelopes } from "@ai-matrx/content-ir/source";
|
|
2
|
+
import {
|
|
3
|
+
findTableEnd,
|
|
4
|
+
rowCells,
|
|
5
|
+
tableStartsAt,
|
|
6
|
+
unescapeCellPipes
|
|
7
|
+
} from "../display/markdown-classification/processors/utils/gfm-table-lines.js";
|
|
8
|
+
const FENCE = /^( {0,3})(`{3,}|~{3,})/;
|
|
9
|
+
function readableInline(line) {
|
|
10
|
+
const codes = [];
|
|
11
|
+
let out = line.replace(/(`+)([\s\S]*?[^`])\1(?!`)/g, (_m, _ticks, body) => {
|
|
12
|
+
codes.push(body.replace(/^ (.*) $/, "$1"));
|
|
13
|
+
return `\uE000${codes.length - 1}\uE000`;
|
|
14
|
+
});
|
|
15
|
+
out = out.replace(/!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, (_m, alt, url) => alt ? `${alt} (${url})` : url).replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, (_m, text, url) => {
|
|
16
|
+
const bare = url.replace(/^mailto:/, "");
|
|
17
|
+
return text === url || text === bare ? bare : `${text} (${bare})`;
|
|
18
|
+
}).replace(/<((?:https?:\/\/|mailto:)[^>\s]+)>/g, (_m, url) => url.replace(/^mailto:/, "")).replace(/(\*\*\*|___)(?=\S)([\s\S]*?\S)\1/g, "$2").replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, "$2").replace(/~~(?=\S)([\s\S]*?\S)~~/g, "$1").replace(/(^|[^\w*])\*(?=\S)([^*]*?\S)\*(?!\w)/g, "$1$2").replace(/(^|[^\w])_(?=\S)([^_]*?\S)_(?!\w)/g, "$1$2").replace(/ {2,}$|\\$/g, "").replace(/\\([!-/:-@[-`{-~])/g, "$1");
|
|
19
|
+
return out.replace(/\uE000(\d+)\uE000/g, (_m, i) => codes[Number(i)] ?? "");
|
|
20
|
+
}
|
|
21
|
+
function markdownToReadableText(markdown) {
|
|
22
|
+
const lines = unwrapKindEnvelopes(markdown).replace(/\r\n?/g, "\n").split("\n");
|
|
23
|
+
const out = [];
|
|
24
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
25
|
+
const line = lines[i];
|
|
26
|
+
const fence = FENCE.exec(line);
|
|
27
|
+
if (fence) {
|
|
28
|
+
const marker = fence[2];
|
|
29
|
+
let j = i + 1;
|
|
30
|
+
while (j < lines.length && !new RegExp(`^ {0,3}${marker[0] === "`" ? "`" : "~"}{${marker.length},}\\s*$`).test(lines[j])) {
|
|
31
|
+
out.push(lines[j]);
|
|
32
|
+
j += 1;
|
|
33
|
+
}
|
|
34
|
+
i = j;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (tableStartsAt(lines, i)) {
|
|
38
|
+
const end = findTableEnd(lines, i);
|
|
39
|
+
for (let j = i; j < end; j += 1) {
|
|
40
|
+
if (j === i + 1) continue;
|
|
41
|
+
out.push(rowCells(lines[j]).map((c) => readableInline(unescapeCellPipes(c).trim())).join(" "));
|
|
42
|
+
}
|
|
43
|
+
i = end - 1;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (/^ {0,3}([-*_])( *\1){2,} *$/.test(line)) {
|
|
47
|
+
out.push("");
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (/^ {0,3}(=+|-+) *$/.test(line) && out.length && out[out.length - 1].trim()) continue;
|
|
51
|
+
let rest = line;
|
|
52
|
+
rest = rest.replace(/^ {0,3}(?:> ?)+/, "");
|
|
53
|
+
rest = rest.replace(/^ {0,3}#{1,6}[ \t]+/, "").replace(/[ \t]+#+[ \t]*$/, "");
|
|
54
|
+
rest = rest.replace(/^(\s*)[-*+][ \t]+\[[ ]\][ \t]+/, "$1\u2610 ").replace(/^(\s*)[-*+][ \t]+\[[xX]\][ \t]+/, "$1\u2611 ").replace(/^(\s*)[-*+][ \t]+/, "$1\u2022 ").replace(/^(\s*)(\d{1,9})[.)][ \t]+/, "$1$2. ");
|
|
55
|
+
out.push(readableInline(rest));
|
|
56
|
+
}
|
|
57
|
+
return out.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
markdownToReadableText,
|
|
61
|
+
readableInline
|
|
62
|
+
};
|
|
@@ -69,8 +69,10 @@ const BUILTIN_BLOCK_COMPONENTS = {
|
|
|
69
69
|
const BUILTIN_LOADING_COMPONENTS = {
|
|
70
70
|
DiagramLoading: () => /* @__PURE__ */ jsx(LazyBlockWrapper, { children: /* @__PURE__ */ jsx(DiagramLoadingVisualization, {}) })
|
|
71
71
|
};
|
|
72
|
+
import { ensureHostBlockRegistrations } from "../../../host/registry.js";
|
|
72
73
|
const REGISTRY_KEY = /* @__PURE__ */ Symbol.for("ai-matrx.rich-content.block-components");
|
|
73
74
|
function registry() {
|
|
75
|
+
ensureHostBlockRegistrations();
|
|
74
76
|
const g = globalThis;
|
|
75
77
|
return g[REGISTRY_KEY] ??= { blocks: {}, loading: {} };
|
|
76
78
|
}
|
|
@@ -472,6 +472,7 @@ const BLOCK_DISPATCH_CLASSIFICATION = {
|
|
|
472
472
|
const DISPATCH_MAP = new Map(
|
|
473
473
|
Object.entries(BLOCK_DISPATCH)
|
|
474
474
|
);
|
|
475
|
+
import { ensureHostBlockRegistrations } from "../../../host/registry.js";
|
|
475
476
|
const REGISTERED_KEY = /* @__PURE__ */ Symbol.for("ai-matrx.rich-content.block-dispatch");
|
|
476
477
|
function registered() {
|
|
477
478
|
const g = globalThis;
|
|
@@ -481,6 +482,7 @@ function registerBlockDispatch(entries) {
|
|
|
481
482
|
for (const [type, fn] of Object.entries(entries)) if (fn) registered().set(type, fn);
|
|
482
483
|
}
|
|
483
484
|
function resolveBlockDispatch(type) {
|
|
485
|
+
ensureHostBlockRegistrations();
|
|
484
486
|
return registered().get(type) ?? DISPATCH_MAP.get(type) ?? null;
|
|
485
487
|
}
|
|
486
488
|
export {
|
|
@@ -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;
|
|
@@ -128,6 +143,20 @@ export declare const KindInstanceRender: HostComponent;
|
|
|
128
143
|
export declare const KindRecordChrome: HostComponent;
|
|
129
144
|
export declare const kindHasRecordChrome: HostFn;
|
|
130
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;
|
|
131
160
|
export declare const MarkdownStream: HostComponent;
|
|
132
161
|
export declare const CitationMarkerInline: HostComponent;
|
|
133
162
|
export declare const CopyButtons: HostComponent;
|
|
@@ -142,6 +171,7 @@ export declare const IconInputWithValidation: HostComponent;
|
|
|
142
171
|
export declare const LocatedTableViewer: HostComponent;
|
|
143
172
|
export declare const OpenDestinationDialog: HostComponent;
|
|
144
173
|
export declare function hostHtmlPageService(): unknown;
|
|
174
|
+
export declare const holdDeliberateIntent: <T>(work: () => T | Promise<T>) => Promise<T>;
|
|
145
175
|
export declare const ensureOrganizationContext: HostFn;
|
|
146
176
|
export declare const isOrganizationSelectionCancelled: HostFn;
|
|
147
177
|
export declare const loadExportTargets: HostFn;
|
|
@@ -68,6 +68,24 @@ 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");
|
|
73
91
|
const CopyButtons = component("CopyButtons", null, "no copy menu on rendered content");
|
|
@@ -84,6 +102,11 @@ const OpenDestinationDialog = component("OpenDestinationDialog", null, "no desti
|
|
|
84
102
|
function hostHtmlPageService() {
|
|
85
103
|
return binding("HTMLPageService") ?? null;
|
|
86
104
|
}
|
|
105
|
+
const holdDeliberateIntent = fn(
|
|
106
|
+
"holdDeliberateIntent",
|
|
107
|
+
(work) => Promise.resolve().then(work),
|
|
108
|
+
"actions run without an intent hold"
|
|
109
|
+
);
|
|
87
110
|
const ensureOrganizationContext = fn("ensureOrganizationContext", async () => null, "no organization can be chosen");
|
|
88
111
|
const isOrganizationSelectionCancelled = fn("isOrganizationSelectionCancelled", () => false, "no organization can be chosen");
|
|
89
112
|
const loadExportTargets = fn("loadExportTargets", async () => null, "no workbook export");
|
|
@@ -121,12 +144,17 @@ export {
|
|
|
121
144
|
NonEditableContextMenu,
|
|
122
145
|
OpenDestinationDialog,
|
|
123
146
|
RawJsonExplorer,
|
|
147
|
+
RecordAnnotations,
|
|
124
148
|
StructuredValueView,
|
|
149
|
+
annotationRecordOf,
|
|
125
150
|
announceProposedGoogleWrite,
|
|
151
|
+
contentSourceKey,
|
|
152
|
+
convertOriginForSource,
|
|
126
153
|
createMermaidEditorScope,
|
|
127
154
|
ensureOrganizationContext,
|
|
128
155
|
getArtifactDef,
|
|
129
156
|
hasArtifactRenderer,
|
|
157
|
+
holdDeliberateIntent,
|
|
130
158
|
hostCodeEditorAgents,
|
|
131
159
|
hostHtmlPageService,
|
|
132
160
|
hostMermaidEditorManifest,
|
|
@@ -136,19 +164,28 @@ export {
|
|
|
136
164
|
kindHasRecordChrome,
|
|
137
165
|
loadExportTargets,
|
|
138
166
|
loadSendToGoogle,
|
|
167
|
+
openContextMenuForElement,
|
|
168
|
+
recordKeyOf,
|
|
139
169
|
reportCanvasOpenDrop,
|
|
140
170
|
resolveArtifactDef,
|
|
171
|
+
sameContentSource,
|
|
141
172
|
setImageKnobMapEntry,
|
|
173
|
+
useActiveOrganizationId,
|
|
142
174
|
useAdvancedMenu,
|
|
175
|
+
useAlchemyDisclosure,
|
|
143
176
|
useCanvas,
|
|
144
177
|
useCanvasOpenGuard,
|
|
145
178
|
useDiagramAgents,
|
|
179
|
+
useDocumentDialogsHost,
|
|
146
180
|
useHideReasoning,
|
|
147
181
|
useHideToolResults,
|
|
182
|
+
useHostDispatch,
|
|
183
|
+
useHostGetState,
|
|
148
184
|
useHostUser,
|
|
149
185
|
useImageKnob,
|
|
150
186
|
useIsAdminDebugger,
|
|
151
187
|
useIsAuthenticated,
|
|
188
|
+
useIsSuperAdmin,
|
|
152
189
|
useMachineFramesVisible,
|
|
153
190
|
useMermaidPreferences,
|
|
154
191
|
useOpenArtifactInCanvas,
|
|
@@ -156,6 +193,7 @@ export {
|
|
|
156
193
|
useOpenOverlay,
|
|
157
194
|
useOpenSmartCodeEditorWindow,
|
|
158
195
|
useOpenTableViewerWindow,
|
|
196
|
+
useRegistryMenuSource,
|
|
159
197
|
useSaveAndOpenInCodeEditor,
|
|
160
198
|
useSetMermaidPreferences,
|
|
161
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,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
|
+
};
|
package/dist/host/registry.d.ts
CHANGED
|
@@ -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;
|
package/dist/host/registry.js
CHANGED
|
@@ -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,
|
package/dist/host/types.d.ts
CHANGED
|
@@ -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
|
+
};
|