@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.
- package/CHANGELOG.md +9 -0
- 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/blocks/json/json-tabular-utils.d.ts +7 -5
- package/dist/display/blocks/json/json-tabular-utils.js +26 -17
- package/dist/display/blocks/table/StreamingTableRenderer.js +11 -16
- package/dist/display/chat-markdown/BasicMarkdownContent.js +8 -6
- package/dist/display/chat-markdown/ConfigurableMarkdownContent.js +8 -6
- 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/display/tables/MarkdownTable.js +11 -16
- package/dist/display/tables/TableWithSeparatedControls.js +11 -16
- package/dist/display/tables/table-csv.d.ts +7 -0
- package/dist/display/tables/table-csv.js +19 -0
- package/dist/host/app-bindings.d.ts +32 -0
- package/dist/host/app-bindings.js +40 -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 +21 -5
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useActionSurfaceProvider } from "./runtime/useActionSurfaceProvider.js";
|
|
3
|
+
function RichDocumentActionProvider(props) {
|
|
4
|
+
const { content, source, surfaceId, actions } = props;
|
|
5
|
+
useActionSurfaceProvider({
|
|
6
|
+
content,
|
|
7
|
+
source,
|
|
8
|
+
actions,
|
|
9
|
+
actionsVariant: "remote",
|
|
10
|
+
actionsSurfaceId: surfaceId
|
|
11
|
+
});
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
var RichDocumentActionProvider_default = RichDocumentActionProvider;
|
|
15
|
+
export {
|
|
16
|
+
RichDocumentActionProvider,
|
|
17
|
+
RichDocumentActionProvider_default as default
|
|
18
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface RichDocumentActionSurfaceProps {
|
|
3
|
+
surfaceId: string;
|
|
4
|
+
/** Which inline variant to render. Defaults to "bar". */
|
|
5
|
+
variant?: "bar" | "mini-bar" | "menu";
|
|
6
|
+
className?: string;
|
|
7
|
+
/** Rendered when no provider is currently registered. */
|
|
8
|
+
fallback?: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
export declare function RichDocumentActionSurface(props: RichDocumentActionSurfaceProps): React.ReactElement | null;
|
|
11
|
+
export default RichDocumentActionSurface;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from "@ai-matrx/design-system";
|
|
4
|
+
import { useTopProvider } from "./runtime/actionSurfacesStore.js";
|
|
5
|
+
import { getBridge } from "./runtime/providerBridge.js";
|
|
6
|
+
import { ActionBar } from "./variants/ActionBar.js";
|
|
7
|
+
import { MiniActionBar } from "./variants/MiniActionBar.js";
|
|
8
|
+
import { MenuVariant } from "./variants/MenuVariant.js";
|
|
9
|
+
function RichDocumentActionSurface(props) {
|
|
10
|
+
const { surfaceId, variant = "bar", className, fallback = null } = props;
|
|
11
|
+
const provider = useTopProvider(surfaceId);
|
|
12
|
+
if (!provider) {
|
|
13
|
+
return /* @__PURE__ */ jsx(Fragment, { children: fallback });
|
|
14
|
+
}
|
|
15
|
+
const bridge = getBridge(provider.providerId);
|
|
16
|
+
if (!bridge) {
|
|
17
|
+
return /* @__PURE__ */ jsx(Fragment, { children: fallback });
|
|
18
|
+
}
|
|
19
|
+
switch (variant) {
|
|
20
|
+
case "bar":
|
|
21
|
+
return /* @__PURE__ */ jsx(
|
|
22
|
+
ActionBar,
|
|
23
|
+
{
|
|
24
|
+
actions: bridge.resolvedActions,
|
|
25
|
+
getCtx: bridge.getCtx,
|
|
26
|
+
target: bridge.target,
|
|
27
|
+
sourceId: provider.contentSourceId,
|
|
28
|
+
className
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
case "mini-bar":
|
|
32
|
+
return /* @__PURE__ */ jsx(
|
|
33
|
+
MiniActionBar,
|
|
34
|
+
{
|
|
35
|
+
actions: bridge.resolvedActions,
|
|
36
|
+
getCtx: bridge.getCtx,
|
|
37
|
+
target: bridge.target,
|
|
38
|
+
sourceId: provider.contentSourceId,
|
|
39
|
+
className
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
case "menu":
|
|
43
|
+
return /* @__PURE__ */ jsx(
|
|
44
|
+
MenuVariant,
|
|
45
|
+
{
|
|
46
|
+
target: bridge.target,
|
|
47
|
+
getCtx: bridge.getCtx,
|
|
48
|
+
className: cn(className)
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
default:
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
var RichDocumentActionSurface_default = RichDocumentActionSurface;
|
|
56
|
+
export {
|
|
57
|
+
RichDocumentActionSurface,
|
|
58
|
+
RichDocumentActionSurface_default as default
|
|
59
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ContentSource, RichDocumentActionsProp } from "./types.js";
|
|
3
|
+
export interface RichDocumentActionsProps {
|
|
4
|
+
content: string;
|
|
5
|
+
source: ContentSource;
|
|
6
|
+
actions?: RichDocumentActionsProp;
|
|
7
|
+
className?: string;
|
|
8
|
+
/** Hide the ⋯ overflow menu (the primary row still renders). */
|
|
9
|
+
hideOverflow?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function RichDocumentActions(props: RichDocumentActionsProps): React.ReactElement;
|
|
12
|
+
export default RichDocumentActions;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useActionSurfaceProvider } from "./runtime/useActionSurfaceProvider.js";
|
|
4
|
+
import { ActionBar } from "./variants/ActionBar.js";
|
|
5
|
+
function RichDocumentActions(props) {
|
|
6
|
+
const { content, source, actions, className, hideOverflow } = props;
|
|
7
|
+
const { ctx, getCtx, resolvedActions, target } = useActionSurfaceProvider({
|
|
8
|
+
content,
|
|
9
|
+
source,
|
|
10
|
+
actions,
|
|
11
|
+
actionsVariant: "bar"
|
|
12
|
+
});
|
|
13
|
+
return /* @__PURE__ */ jsx(
|
|
14
|
+
ActionBar,
|
|
15
|
+
{
|
|
16
|
+
actions: resolvedActions,
|
|
17
|
+
getCtx,
|
|
18
|
+
target,
|
|
19
|
+
sourceId: ctx.instanceKey("alchemy"),
|
|
20
|
+
className,
|
|
21
|
+
hideOverflow
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
var RichDocumentActions_default = RichDocumentActions;
|
|
26
|
+
export {
|
|
27
|
+
RichDocumentActions,
|
|
28
|
+
RichDocumentActions_default as default
|
|
29
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { RichDocumentAction } from "../types.js";
|
|
2
|
+
/** "Clear" — the host empties its own content (a transcript pad). */
|
|
3
|
+
export declare function clearContentAction(onClear: () => void, label?: string): RichDocumentAction;
|
|
4
|
+
/** A host copy that joins more than this content (transcript + cleaned text). */
|
|
5
|
+
export declare function hostCopyAction(id: string, label: string, run: () => void | Promise<void>): RichDocumentAction;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Files, Trash2 } from "lucide-react";
|
|
2
|
+
function clearContentAction(onClear, label = "Clear") {
|
|
3
|
+
return {
|
|
4
|
+
id: "host:clear-content",
|
|
5
|
+
label,
|
|
6
|
+
icon: Trash2,
|
|
7
|
+
iconColor: "text-muted-foreground",
|
|
8
|
+
category: "edit",
|
|
9
|
+
supportedSources: "*",
|
|
10
|
+
renderSlot: "both",
|
|
11
|
+
order: 90,
|
|
12
|
+
run: () => onClear()
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function hostCopyAction(id, label, run) {
|
|
16
|
+
return {
|
|
17
|
+
id: `host:${id}`,
|
|
18
|
+
label,
|
|
19
|
+
icon: Files,
|
|
20
|
+
iconColor: "text-muted-foreground",
|
|
21
|
+
category: "copy",
|
|
22
|
+
supportedSources: "*",
|
|
23
|
+
renderSlot: "both",
|
|
24
|
+
order: 80,
|
|
25
|
+
run: () => run()
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
clearContentAction,
|
|
30
|
+
hostCopyAction
|
|
31
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** The engine half of the rich-document action helpers (the host keeps its handler helpers). */
|
|
2
|
+
import type { RichDocumentActionContext } from "../types.js";
|
|
3
|
+
/** Storage key for "do this action after the person signs in" payloads. */
|
|
4
|
+
export declare const PENDING_ACTION_KEY = "matrx_pending_post_auth_action";
|
|
5
|
+
/**
|
|
6
|
+
* Resolve an action label to a string. Supports both static labels and `(ctx) => string`
|
|
7
|
+
* callbacks so labels like "Edit history (3)" can pull counts from the live context.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveActionLabel(label: string | ((ctx: RichDocumentActionContext) => string), ctx: RichDocumentActionContext): string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface ParsedTable {
|
|
2
|
+
headers: string[];
|
|
3
|
+
rows: string[][];
|
|
4
|
+
}
|
|
5
|
+
export declare function cleanCell(text: string): string;
|
|
6
|
+
/** The first markdown table in `content`, or null when there is none. */
|
|
7
|
+
export declare function parseFirstMarkdownTable(content: string): ParsedTable | null;
|
|
8
|
+
/** RFC 4180 CSV (comma) or TSV (tab) text for a parsed table. */
|
|
9
|
+
export declare function tableToDelimited(table: ParsedTable, delimiter: "," | "\t"): string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { tableToCsv } from "../../display/tables/table-csv.js";
|
|
2
|
+
import { unwrapCodeSpans } from "@ai-matrx/content-ir/source";
|
|
3
|
+
import {
|
|
4
|
+
findTableEnd,
|
|
5
|
+
rowCells,
|
|
6
|
+
tableStartsAt,
|
|
7
|
+
unescapeCellPipes
|
|
8
|
+
} from "../../display/markdown-classification/processors/utils/gfm-table-lines.js";
|
|
9
|
+
function splitRow(line) {
|
|
10
|
+
return rowCells(line).map((cell) => cleanCell(unescapeCellPipes(cell)));
|
|
11
|
+
}
|
|
12
|
+
function cleanCell(text) {
|
|
13
|
+
return unwrapCodeSpans(text).replace(/\*\*([^*]+)\*\*/g, "$1").replace(/__([^_]+)__/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/~~([^~]+)~~/g, "$1").replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1").replace(/<br\s*\/?>/gi, " ").trim();
|
|
14
|
+
}
|
|
15
|
+
function parseFirstMarkdownTable(content) {
|
|
16
|
+
const lines = content.split("\n");
|
|
17
|
+
for (let i = 0; i + 1 < lines.length; i += 1) {
|
|
18
|
+
if (!tableStartsAt(lines, i)) continue;
|
|
19
|
+
const headers = splitRow(lines[i]);
|
|
20
|
+
const rows = [];
|
|
21
|
+
const end = findTableEnd(lines, i);
|
|
22
|
+
for (let j = i + 2; j < end; j += 1) {
|
|
23
|
+
const cells = splitRow(lines[j]);
|
|
24
|
+
rows.push(headers.map((_, k) => cells[k] ?? ""));
|
|
25
|
+
}
|
|
26
|
+
return { headers, rows };
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
function tableToDelimited(table, delimiter) {
|
|
31
|
+
if (delimiter === ",") return tableToCsv(table.headers, table.rows);
|
|
32
|
+
const flat = (value) => value.replace(/[\t\r\n]+/g, " ");
|
|
33
|
+
return [table.headers, ...table.rows].map((row) => row.map(flat).join(" ")).join("\n");
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
cleanCell,
|
|
37
|
+
parseFirstMarkdownTable,
|
|
38
|
+
tableToDelimited
|
|
39
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type Action, type ActionProvider, type ClickTarget } from "@ai-matrx/alchemy/actions";
|
|
2
|
+
import type { WriteTarget } from "@ai-matrx/alchemy/declare";
|
|
3
|
+
import type { RichDocumentAction, RichDocumentActionContext, RichDocumentActionId } from "../types.js";
|
|
4
|
+
export declare const RICH_DOCUMENT_PROVIDER_ID = "rich-document";
|
|
5
|
+
/** The write target every source adapter serves (edit, delete, regenerate, fork…). */
|
|
6
|
+
export declare const RICH_DOCUMENT_SOURCE_TARGET = "rich_document_source";
|
|
7
|
+
export declare const SOURCE_WRITE_TARGET: WriteTarget;
|
|
8
|
+
/** Register an action at module load. A duplicate id is refused. */
|
|
9
|
+
export declare function registerAction(action: RichDocumentAction): void;
|
|
10
|
+
/** Get one action by ID. Returns undefined if unregistered. */
|
|
11
|
+
export declare function getAction(id: RichDocumentActionId | string): RichDocumentAction | undefined;
|
|
12
|
+
/** Get all registered actions. */
|
|
13
|
+
export declare function getAllActions(): RichDocumentAction[];
|
|
14
|
+
/** The one id for a named menu group, shared with the context-menu provider. */
|
|
15
|
+
export declare function richDocumentSectionId(label: string): string;
|
|
16
|
+
export interface RichDocumentTargetHost {
|
|
17
|
+
kind: "rich-document";
|
|
18
|
+
/** Render-time context: labels, shown/hidden, toggle state. */
|
|
19
|
+
ctx: RichDocumentActionContext;
|
|
20
|
+
/** Click-time context factory: what handlers run against (live content). */
|
|
21
|
+
getCtx: () => RichDocumentActionContext;
|
|
22
|
+
/** This host's own actions (the `actions.extra` prop). */
|
|
23
|
+
extra: readonly RichDocumentAction[];
|
|
24
|
+
}
|
|
25
|
+
/** The rich-document half of a composite host, for hosts that build one. */
|
|
26
|
+
export declare function richDocumentTargetHost(ctx: RichDocumentActionContext, options?: {
|
|
27
|
+
getCtx?: () => RichDocumentActionContext;
|
|
28
|
+
extra?: readonly RichDocumentAction[];
|
|
29
|
+
}): RichDocumentTargetHost;
|
|
30
|
+
/** THE ClickTarget for a rich-document context. */
|
|
31
|
+
export declare function richDocumentClickTarget(ctx: RichDocumentActionContext, options?: {
|
|
32
|
+
getCtx?: () => RichDocumentActionContext;
|
|
33
|
+
exclude?: readonly string[];
|
|
34
|
+
extra?: readonly RichDocumentAction[];
|
|
35
|
+
}): ClickTarget;
|
|
36
|
+
export declare function toAlchemyAction(rd: RichDocumentAction): Action;
|
|
37
|
+
/**
|
|
38
|
+
* Register this provider into the app's one registry, once per registry.
|
|
39
|
+
* Called by the rich-document layouts (the only hosts that need it), so the
|
|
40
|
+
* app's root host never imports the handler graph.
|
|
41
|
+
*/
|
|
42
|
+
export declare function ensureRichDocumentProvider(registry: {
|
|
43
|
+
register(p: ActionProvider): () => void;
|
|
44
|
+
}): void;
|
|
45
|
+
/** The provider registered into the app's Alchemy action registry. */
|
|
46
|
+
export declare const richDocumentActionProvider: ActionProvider;
|
|
47
|
+
/** Re-render hook for hosts: the list changed (a handler module loaded). */
|
|
48
|
+
export declare function subscribeRichDocumentActions(listener: () => void): () => void;
|
|
49
|
+
/**
|
|
50
|
+
* The actions eligible for a context — synchronously, through the package's
|
|
51
|
+
* ONE eligibility pass. Used where a host needs the list during render (the
|
|
52
|
+
* remote-surface spec snapshot, the code-block bridge, the parity tests).
|
|
53
|
+
* A host extra whose id collides with a built-in is refused (the built-in wins).
|
|
54
|
+
*/
|
|
55
|
+
export declare function resolveActions(ctx: RichDocumentActionContext, options?: {
|
|
56
|
+
exclude?: (RichDocumentActionId | string)[];
|
|
57
|
+
extra?: RichDocumentAction[];
|
|
58
|
+
}): RichDocumentAction[];
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { holdDeliberateIntent } from "../../host/app-bindings.js";
|
|
2
|
+
import {
|
|
3
|
+
computeEligibility,
|
|
4
|
+
createClickTarget
|
|
5
|
+
} from "@ai-matrx/alchemy/actions";
|
|
6
|
+
import { registerAlchemyIcon } from "../../utils/alchemy-icon-keys.js";
|
|
7
|
+
import { MENU_STRUCTURE, menuStructureVersion } from "../variants/shared/menuStructure.js";
|
|
8
|
+
import { resolveActionLabel } from "./labels.js";
|
|
9
|
+
import { ensureRichDocumentRegistrations } from "../registrations.js";
|
|
10
|
+
const RICH_DOCUMENT_PROVIDER_ID = "rich-document";
|
|
11
|
+
const RICH_DOCUMENT_SOURCE_TARGET = "rich_document_source";
|
|
12
|
+
const SOURCE_WRITE_TARGET = {
|
|
13
|
+
name: RICH_DOCUMENT_SOURCE_TARGET,
|
|
14
|
+
label: "This content",
|
|
15
|
+
description: "The record the content came from, changed through its source adapter.",
|
|
16
|
+
valueType: "document",
|
|
17
|
+
mode: "entity"
|
|
18
|
+
};
|
|
19
|
+
function store() {
|
|
20
|
+
const key = /* @__PURE__ */ Symbol.for("ai-matrx.rich-document.actions");
|
|
21
|
+
const g = globalThis;
|
|
22
|
+
let s = g[key];
|
|
23
|
+
if (!s) {
|
|
24
|
+
s = { actions: /* @__PURE__ */ new Map(), listeners: /* @__PURE__ */ new Set() };
|
|
25
|
+
g[key] = s;
|
|
26
|
+
}
|
|
27
|
+
return s;
|
|
28
|
+
}
|
|
29
|
+
function readStore() {
|
|
30
|
+
ensureRichDocumentRegistrations();
|
|
31
|
+
return store();
|
|
32
|
+
}
|
|
33
|
+
function registerAction(action) {
|
|
34
|
+
const { actions: ACTIONS, listeners } = store();
|
|
35
|
+
if (ACTIONS.has(action.id)) {
|
|
36
|
+
if (true) {
|
|
37
|
+
console.warn(
|
|
38
|
+
`[rich-document] action "${action.id}" re-registered (fast refresh) \u2014 replaced.`
|
|
39
|
+
);
|
|
40
|
+
} else {
|
|
41
|
+
throw new DuplicateActionError(action.id, RICH_DOCUMENT_PROVIDER_ID, RICH_DOCUMENT_PROVIDER_ID);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
ACTIONS.set(action.id, action);
|
|
45
|
+
for (const l of [...listeners]) l();
|
|
46
|
+
}
|
|
47
|
+
function getAction(id) {
|
|
48
|
+
return readStore().actions.get(id);
|
|
49
|
+
}
|
|
50
|
+
function getAllActions() {
|
|
51
|
+
return Array.from(readStore().actions.values());
|
|
52
|
+
}
|
|
53
|
+
function richDocumentSectionId(label) {
|
|
54
|
+
return `rd:${label.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
|
|
55
|
+
}
|
|
56
|
+
let placementCache = null;
|
|
57
|
+
let placementVersion = -1;
|
|
58
|
+
function placementOf(id) {
|
|
59
|
+
if (placementCache === null || placementVersion !== menuStructureVersion()) {
|
|
60
|
+
placementVersion = menuStructureVersion();
|
|
61
|
+
placementCache = buildPlacement();
|
|
62
|
+
}
|
|
63
|
+
return placementCache.get(id);
|
|
64
|
+
}
|
|
65
|
+
function buildPlacement() {
|
|
66
|
+
const map = /* @__PURE__ */ new Map();
|
|
67
|
+
MENU_STRUCTURE.forEach((section, sectionIndex) => {
|
|
68
|
+
section.actionIds.forEach((id, index) => {
|
|
69
|
+
const order = sectionIndex * 100 + index;
|
|
70
|
+
if (section.submenu === null || section.inline) {
|
|
71
|
+
map.set(id, { order });
|
|
72
|
+
} else {
|
|
73
|
+
const icon = section.icon ? registerAlchemyIcon(section.icon) : void 0;
|
|
74
|
+
map.set(id, {
|
|
75
|
+
order,
|
|
76
|
+
...section.layoutCategory ? { category: section.layoutCategory } : {},
|
|
77
|
+
section: {
|
|
78
|
+
id: richDocumentSectionId(section.submenu),
|
|
79
|
+
label: section.submenu,
|
|
80
|
+
...icon ? { icon } : {},
|
|
81
|
+
...section.target ? { kind: "target" } : {}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
return map;
|
|
88
|
+
}
|
|
89
|
+
const MENU_ALIASES = {
|
|
90
|
+
"regenerate-latest": "regenerate-response"
|
|
91
|
+
};
|
|
92
|
+
function hostOf(target) {
|
|
93
|
+
const host = target.host;
|
|
94
|
+
if (!host) return null;
|
|
95
|
+
if (host.kind === "rich-document") return host;
|
|
96
|
+
return host.richDocument?.kind === "rich-document" ? host.richDocument : null;
|
|
97
|
+
}
|
|
98
|
+
function richDocumentTargetHost(ctx, options = {}) {
|
|
99
|
+
return { kind: "rich-document", ctx, getCtx: options.getCtx ?? (() => ctx), extra: options.extra ?? [] };
|
|
100
|
+
}
|
|
101
|
+
function richDocumentClickTarget(ctx, options = {}) {
|
|
102
|
+
const readOnly = Boolean(ctx.source.readOnly);
|
|
103
|
+
const host = {
|
|
104
|
+
kind: "rich-document",
|
|
105
|
+
ctx,
|
|
106
|
+
getCtx: options.getCtx ?? (() => ctx),
|
|
107
|
+
extra: options.extra ?? []
|
|
108
|
+
};
|
|
109
|
+
return createClickTarget({
|
|
110
|
+
readOnly,
|
|
111
|
+
writable: readOnly ? [] : [SOURCE_WRITE_TARGET],
|
|
112
|
+
excludedActionIds: options.exclude ?? [],
|
|
113
|
+
organizationId: ctx.organizationId,
|
|
114
|
+
auth: { authenticated: ctx.isAuthenticated, admin: ctx.isAdmin, creator: ctx.isCreator },
|
|
115
|
+
payloadKinds: ["markdown"],
|
|
116
|
+
host
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function eligibleFor(rd, target) {
|
|
120
|
+
const host = hostOf(target);
|
|
121
|
+
if (!host) return { status: "absent" };
|
|
122
|
+
const ctx = host.ctx;
|
|
123
|
+
if (rd.supportedSources !== "*" && !rd.supportedSources.includes(ctx.source.type)) {
|
|
124
|
+
return { status: "absent" };
|
|
125
|
+
}
|
|
126
|
+
if (rd.visible && !rd.visible(ctx)) return { status: "absent" };
|
|
127
|
+
const disabled = rd.disabled?.(ctx);
|
|
128
|
+
if (disabled === true || typeof disabled === "object" && disabled !== null) {
|
|
129
|
+
return { status: "absent" };
|
|
130
|
+
}
|
|
131
|
+
return { status: "available" };
|
|
132
|
+
}
|
|
133
|
+
const converted = /* @__PURE__ */ new WeakMap();
|
|
134
|
+
function toAlchemyAction(rd) {
|
|
135
|
+
const cached = converted.get(rd);
|
|
136
|
+
if (cached) return cached;
|
|
137
|
+
const place = placementOf(rd.id);
|
|
138
|
+
const ctxOf = (t) => hostOf(t)?.ctx;
|
|
139
|
+
const action = {
|
|
140
|
+
id: rd.id,
|
|
141
|
+
provider: RICH_DOCUMENT_PROVIDER_ID,
|
|
142
|
+
label: (t) => {
|
|
143
|
+
const ctx = ctxOf(t);
|
|
144
|
+
return ctx ? resolveActionLabel(rd.label, ctx) : typeof rd.label === "string" ? rd.label : rd.id;
|
|
145
|
+
},
|
|
146
|
+
icon: registerAlchemyIcon(rd.icon),
|
|
147
|
+
...rd.iconColor ? { iconTone: rd.iconColor } : {},
|
|
148
|
+
...rd.stateIcon ? {
|
|
149
|
+
stateIcon: (t) => {
|
|
150
|
+
const ctx = ctxOf(t);
|
|
151
|
+
const live = ctx ? rd.stateIcon?.(ctx) : null;
|
|
152
|
+
return live ? { icon: registerAlchemyIcon(live.icon), ...live.spin ? { spin: true } : {} } : null;
|
|
153
|
+
}
|
|
154
|
+
} : {},
|
|
155
|
+
category: place?.category ?? rd.category,
|
|
156
|
+
order: place?.order ?? 1e4 + (rd.order ?? 0),
|
|
157
|
+
placement: rd.renderSlot ?? "overflow",
|
|
158
|
+
...place?.section ? { section: place.section } : {},
|
|
159
|
+
...rd.requiresAuth ? { requiresAuth: true } : {},
|
|
160
|
+
...rd.writesSource ? { writes: [RICH_DOCUMENT_SOURCE_TARGET] } : {},
|
|
161
|
+
...rd.preserveSelection ? { preserveSelection: true } : {},
|
|
162
|
+
...rd.active ? {
|
|
163
|
+
pressed: (t) => {
|
|
164
|
+
const ctx = ctxOf(t);
|
|
165
|
+
return ctx ? Boolean(rd.active?.(ctx)) : false;
|
|
166
|
+
}
|
|
167
|
+
} : {},
|
|
168
|
+
...rd.subscribe ? {
|
|
169
|
+
subscribe: (onChange, t) => {
|
|
170
|
+
const ctx = ctxOf(t);
|
|
171
|
+
if (!ctx || !rd.subscribe) return () => void 0;
|
|
172
|
+
let subscribing = true;
|
|
173
|
+
const off = rd.subscribe(() => {
|
|
174
|
+
if (!subscribing) onChange();
|
|
175
|
+
}, ctx);
|
|
176
|
+
subscribing = false;
|
|
177
|
+
return off;
|
|
178
|
+
}
|
|
179
|
+
} : {},
|
|
180
|
+
eligible: (t) => eligibleFor(rd, t),
|
|
181
|
+
run: async (t) => {
|
|
182
|
+
const host = hostOf(t);
|
|
183
|
+
if (!host) return;
|
|
184
|
+
await holdDeliberateIntent(() => rd.run(host.getCtx()));
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
converted.set(rd, action);
|
|
188
|
+
return action;
|
|
189
|
+
}
|
|
190
|
+
function candidates(target) {
|
|
191
|
+
const host = hostOf(target);
|
|
192
|
+
if (!host) return [];
|
|
193
|
+
const ACTIONS = readStore().actions;
|
|
194
|
+
const all = [...ACTIONS.values(), ...host.extra.filter((a) => !ACTIONS.has(a.id))];
|
|
195
|
+
const present = new Set(all.map((a) => a.id));
|
|
196
|
+
return all.filter((a) => {
|
|
197
|
+
const canonical = MENU_ALIASES[a.id];
|
|
198
|
+
return !(canonical && present.has(canonical));
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
const REGISTERED = /* @__PURE__ */ new WeakSet();
|
|
202
|
+
function ensureRichDocumentProvider(registry) {
|
|
203
|
+
if (REGISTERED.has(registry)) return;
|
|
204
|
+
REGISTERED.add(registry);
|
|
205
|
+
registry.register(richDocumentActionProvider);
|
|
206
|
+
}
|
|
207
|
+
const richDocumentActionProvider = {
|
|
208
|
+
id: RICH_DOCUMENT_PROVIDER_ID,
|
|
209
|
+
tier: "T0",
|
|
210
|
+
declaredIds: () => [...readStore().actions.keys()],
|
|
211
|
+
actions: (target) => candidates(target).map(toAlchemyAction)
|
|
212
|
+
};
|
|
213
|
+
function subscribeRichDocumentActions(listener) {
|
|
214
|
+
const { listeners } = store();
|
|
215
|
+
listeners.add(listener);
|
|
216
|
+
return () => {
|
|
217
|
+
listeners.delete(listener);
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function resolveActions(ctx, options) {
|
|
221
|
+
const target = richDocumentClickTarget(ctx, {
|
|
222
|
+
exclude: options?.exclude ?? [],
|
|
223
|
+
extra: options?.extra ?? []
|
|
224
|
+
});
|
|
225
|
+
return candidates(target).filter((rd) => computeEligibility(toAlchemyAction(rd), target, { authResumable: false }).status === "available").sort((a, b) => {
|
|
226
|
+
if (a.category !== b.category) return a.category.localeCompare(b.category);
|
|
227
|
+
return (a.order ?? 0) - (b.order ?? 0);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
export {
|
|
231
|
+
RICH_DOCUMENT_PROVIDER_ID,
|
|
232
|
+
RICH_DOCUMENT_SOURCE_TARGET,
|
|
233
|
+
SOURCE_WRITE_TARGET,
|
|
234
|
+
ensureRichDocumentProvider,
|
|
235
|
+
getAction,
|
|
236
|
+
getAllActions,
|
|
237
|
+
registerAction,
|
|
238
|
+
resolveActions,
|
|
239
|
+
richDocumentActionProvider,
|
|
240
|
+
richDocumentClickTarget,
|
|
241
|
+
richDocumentSectionId,
|
|
242
|
+
richDocumentTargetHost,
|
|
243
|
+
subscribeRichDocumentActions,
|
|
244
|
+
toAlchemyAction
|
|
245
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getAction } from "./provider.js";
|
|
2
|
+
import { PENDING_ACTION_KEY } from "./labels.js";
|
|
3
|
+
function resumePendingAuthAction(ctx) {
|
|
4
|
+
if (!ctx.isAuthenticated) return;
|
|
5
|
+
let pending = null;
|
|
6
|
+
try {
|
|
7
|
+
const raw = sessionStorage.getItem(PENDING_ACTION_KEY);
|
|
8
|
+
if (!raw) return;
|
|
9
|
+
pending = JSON.parse(raw);
|
|
10
|
+
} catch {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (!pending || pending.savedContent !== ctx.content) return;
|
|
14
|
+
try {
|
|
15
|
+
sessionStorage.removeItem(PENDING_ACTION_KEY);
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
const action = typeof pending.action === "string" ? getAction(pending.action) : void 0;
|
|
19
|
+
if (!action) {
|
|
20
|
+
console.error(
|
|
21
|
+
`[RichDocument] post-auth resume: no registered action "${String(pending.action)}" \u2014 the pending request was dropped.`
|
|
22
|
+
);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
void Promise.resolve(action.run(ctx)).catch((err) => {
|
|
26
|
+
console.error(`[RichDocument] post-auth resume of ${action.id} threw`, err);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
resumePendingAuthAction
|
|
31
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContentSourceType → ContentSourceAdapter. The engine ships `raw`; the host registers the rest
|
|
3
|
+
* (matrx-frontend: chat-message, note, prompt-result, artifact, scraper-result, working-document)
|
|
4
|
+
* through `registerSourceAdapter`, on import of its registration module.
|
|
5
|
+
*/
|
|
6
|
+
import type { ContentSourceAdapter, ContentSourceType } from "../../types.js";
|
|
7
|
+
/** Register (or replace) the adapter for one source type. */
|
|
8
|
+
export declare function registerSourceAdapter(sourceType: ContentSourceType, adapter: ContentSourceAdapter): void;
|
|
9
|
+
export declare function getSourceAdapter(sourceType: ContentSourceType): ContentSourceAdapter;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { rawAdapter } from "./raw.js";
|
|
2
|
+
import { ensureRichDocumentRegistrations } from "../../registrations.js";
|
|
3
|
+
const KEY = /* @__PURE__ */ Symbol.for("ai-matrx.rich-content.rich-document.source-adapters");
|
|
4
|
+
function adapters() {
|
|
5
|
+
const g = globalThis;
|
|
6
|
+
return g[KEY] ??= /* @__PURE__ */ new Map([["raw", rawAdapter]]);
|
|
7
|
+
}
|
|
8
|
+
function registerSourceAdapter(sourceType, adapter) {
|
|
9
|
+
adapters().set(sourceType, adapter);
|
|
10
|
+
}
|
|
11
|
+
function getSourceAdapter(sourceType) {
|
|
12
|
+
ensureRichDocumentRegistrations();
|
|
13
|
+
const adapter = adapters().get(sourceType);
|
|
14
|
+
if (!adapter) {
|
|
15
|
+
throw new Error(`No source adapter registered for type: ${sourceType}`);
|
|
16
|
+
}
|
|
17
|
+
return adapter;
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
getSourceAdapter,
|
|
21
|
+
registerSourceAdapter
|
|
22
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const LONG_INPUT = 4096;
|
|
2
|
+
const recent = /* @__PURE__ */ new Map();
|
|
3
|
+
function shortHash(input) {
|
|
4
|
+
if (input.length >= LONG_INPUT) {
|
|
5
|
+
const hit = recent.get(input);
|
|
6
|
+
if (hit !== void 0) return hit;
|
|
7
|
+
const computed = fnv1a(input);
|
|
8
|
+
if (recent.size >= 8) recent.delete(recent.keys().next().value);
|
|
9
|
+
recent.set(input, computed);
|
|
10
|
+
return computed;
|
|
11
|
+
}
|
|
12
|
+
return fnv1a(input);
|
|
13
|
+
}
|
|
14
|
+
function fnv1a(input) {
|
|
15
|
+
let hash = 2166136261;
|
|
16
|
+
for (let i = 0; i < input.length; i++) {
|
|
17
|
+
hash ^= input.charCodeAt(i);
|
|
18
|
+
hash = hash * 16777619 >>> 0;
|
|
19
|
+
}
|
|
20
|
+
return hash.toString(16).padStart(8, "0");
|
|
21
|
+
}
|
|
22
|
+
const rawAdapter = {
|
|
23
|
+
instanceKeyPrefix: (source) => {
|
|
24
|
+
if (source.type !== "raw") {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`rawAdapter received non-raw source: ${source.type}`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
return "raw";
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
export {
|
|
33
|
+
rawAdapter,
|
|
34
|
+
shortHash
|
|
35
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useRichDocumentProvider(): void;
|