@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,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { useAlchemyActions } from "@ai-matrx/alchemy/react/host";
|
|
4
|
+
import { ensureRichDocumentProvider } from "./provider.js";
|
|
5
|
+
function useRichDocumentProvider() {
|
|
6
|
+
const { registry } = useAlchemyActions();
|
|
7
|
+
React.useLayoutEffect(() => {
|
|
8
|
+
ensureRichDocumentProvider(registry);
|
|
9
|
+
}, [registry]);
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
useRichDocumentProvider
|
|
13
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host's own types the rich-document contract carries (its store's dispatch and state, its
|
|
3
|
+
* note record, its application scope). The host fills them in by augmentation — matrx-frontend:
|
|
4
|
+
*
|
|
5
|
+
* declare module "@ai-matrx/rich-content/rich-document/host-types" {
|
|
6
|
+
* interface RichDocumentHostTypes { dispatch: AppDispatch; state: RootState; note: Note; ... }
|
|
7
|
+
* }
|
|
8
|
+
*
|
|
9
|
+
* Unaugmented, each is a structural default.
|
|
10
|
+
*/
|
|
11
|
+
export interface RichDocumentHostTypes {
|
|
12
|
+
}
|
|
13
|
+
type HostType<K extends string, Default> = RichDocumentHostTypes extends Record<K, infer T> ? T : Default;
|
|
14
|
+
export type AppDispatch = HostType<"dispatch", (action: any) => any>;
|
|
15
|
+
export type RootState = HostType<"state", Record<string, unknown>>;
|
|
16
|
+
export type ApplicationScope = HostType<"applicationScope", Record<string, unknown>>;
|
|
17
|
+
export type Note = HostType<"note", {
|
|
18
|
+
id: string;
|
|
19
|
+
content?: string | null;
|
|
20
|
+
label?: string | null;
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
}>;
|
|
23
|
+
export type NoteSaveReceipt = HostType<"noteSaveReceipt", {
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
}>;
|
|
26
|
+
/** The saved record a content source names (matrx-frontend: annotations/record-of-source). */
|
|
27
|
+
export interface AnnotationRecord {
|
|
28
|
+
token: string;
|
|
29
|
+
id: string;
|
|
30
|
+
title?: string;
|
|
31
|
+
contentVersion?: number;
|
|
32
|
+
href?: string;
|
|
33
|
+
/** A chat answer's conversation. */
|
|
34
|
+
conversationId?: string;
|
|
35
|
+
}
|
|
36
|
+
/** The reading set's record + body (matrx-frontend: annotations/types `AnnotationSource`). */
|
|
37
|
+
export interface AnnotationSource {
|
|
38
|
+
token: string;
|
|
39
|
+
id: string;
|
|
40
|
+
title: string;
|
|
41
|
+
body: string;
|
|
42
|
+
contentVersion: number;
|
|
43
|
+
href?: string;
|
|
44
|
+
conversationId?: string;
|
|
45
|
+
}
|
|
46
|
+
/** A server-processed block the full renderer accepts (matrx-frontend: EnhancedChatMarkdown). */
|
|
47
|
+
export type ServerProcessedBlock = HostType<"serverProcessedBlock", {
|
|
48
|
+
type: string;
|
|
49
|
+
[key: string]: unknown;
|
|
50
|
+
}>;
|
|
51
|
+
export {};
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function ensureRichDocumentRegistrations(): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { hostCapability } from "../host/registry.js";
|
|
2
|
+
const ENSURED = /* @__PURE__ */ Symbol.for("ai-matrx.rich-content.rich-document.registrations-ensured");
|
|
3
|
+
function ensureRichDocumentRegistrations() {
|
|
4
|
+
const g = globalThis;
|
|
5
|
+
if (g[ENSURED]) return;
|
|
6
|
+
const ensure = hostCapability("ensureRichDocumentRegistrations");
|
|
7
|
+
if (!ensure) return;
|
|
8
|
+
g[ENSURED] = true;
|
|
9
|
+
ensure();
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
ensureRichDocumentRegistrations
|
|
13
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RichDocumentActionContext } from "../types.js";
|
|
2
|
+
interface ApplyTarget {
|
|
3
|
+
ctx: RichDocumentActionContext;
|
|
4
|
+
/** What the source is called, for the action's label. */
|
|
5
|
+
label: string;
|
|
6
|
+
}
|
|
7
|
+
/** True when an answer about this text could be applied back to it. */
|
|
8
|
+
export declare function canApplyBack(ctx: RichDocumentActionContext): boolean;
|
|
9
|
+
export declare function registerApplyTarget(ctx: RichDocumentActionContext, label: string): string;
|
|
10
|
+
export declare function bindConversationToApplyTarget(conversationId: string, targetId: string): void;
|
|
11
|
+
export declare function applyTargetForConversation(conversationId: string | null | undefined): {
|
|
12
|
+
id: string;
|
|
13
|
+
label: string;
|
|
14
|
+
} | null;
|
|
15
|
+
export declare function getApplyTarget(id: string | null | undefined): ApplyTarget | null;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const targets = /* @__PURE__ */ new Map();
|
|
2
|
+
const byConversation = /* @__PURE__ */ new Map();
|
|
3
|
+
let seq = 0;
|
|
4
|
+
const APPLYABLE_SOURCES = /* @__PURE__ */ new Set(["note", "chat-message", "working-document"]);
|
|
5
|
+
function canApplyBack(ctx) {
|
|
6
|
+
return APPLYABLE_SOURCES.has(ctx.source.type) && ctx.source.readOnly !== true;
|
|
7
|
+
}
|
|
8
|
+
function registerApplyTarget(ctx, label) {
|
|
9
|
+
seq += 1;
|
|
10
|
+
const id = `apply-target-${seq}`;
|
|
11
|
+
targets.set(id, { ctx, label });
|
|
12
|
+
return id;
|
|
13
|
+
}
|
|
14
|
+
function bindConversationToApplyTarget(conversationId, targetId) {
|
|
15
|
+
if (targets.has(targetId)) byConversation.set(conversationId, targetId);
|
|
16
|
+
}
|
|
17
|
+
function applyTargetForConversation(conversationId) {
|
|
18
|
+
if (!conversationId) return null;
|
|
19
|
+
const id = byConversation.get(conversationId);
|
|
20
|
+
const target = id ? targets.get(id) : void 0;
|
|
21
|
+
return id && target ? { id, label: target.label } : null;
|
|
22
|
+
}
|
|
23
|
+
function getApplyTarget(id) {
|
|
24
|
+
return id ? targets.get(id) ?? null : null;
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
applyTargetForConversation,
|
|
28
|
+
bindConversationToApplyTarget,
|
|
29
|
+
canApplyBack,
|
|
30
|
+
getApplyTarget,
|
|
31
|
+
registerApplyTarget
|
|
32
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type SourceEdit, type SpliceResult } from "@ai-matrx/content-ir/source";
|
|
2
|
+
export interface ProposedEdit {
|
|
3
|
+
/** The one block-aligned edit, or null when nothing differs. */
|
|
4
|
+
edit: SourceEdit | null;
|
|
5
|
+
/** Blocks of the original that stay byte-identical. */
|
|
6
|
+
keptBlocks: number;
|
|
7
|
+
/** Blocks of the original the edit replaces. */
|
|
8
|
+
replacedBlocks: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function reduceToBlockEdit(original: string, proposed: string): ProposedEdit;
|
|
11
|
+
/**
|
|
12
|
+
* The text to persist for a proposal: `original` with only the changed run
|
|
13
|
+
* spliced in. Throws `SourceSpliceError` when the splice would disturb an
|
|
14
|
+
* island it did not name — the caller surfaces that, never forces it.
|
|
15
|
+
*/
|
|
16
|
+
export declare function spliceProposal(original: string, proposed: string): SpliceResult | null;
|
|
17
|
+
/**
|
|
18
|
+
* A splice refusal in words a person reads — never offsets or function names
|
|
19
|
+
* ("edit [0, 776) changes … island at [141, 233); islands change only through
|
|
20
|
+
* islandEdit()" was shown verbatim, verify-RC-B5 round 3 F7).
|
|
21
|
+
*/
|
|
22
|
+
export declare function explainSpliceRefusal(error: unknown): string;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SourceSpliceError,
|
|
3
|
+
spliceSave,
|
|
4
|
+
tokenizeSource
|
|
5
|
+
} from "@ai-matrx/content-ir/source";
|
|
6
|
+
function reduceToBlockEdit(original, proposed) {
|
|
7
|
+
const a = tokenizeSource(original);
|
|
8
|
+
const b = tokenizeSource(proposed);
|
|
9
|
+
let head = 0;
|
|
10
|
+
while (head < a.length && head < b.length && a[head].raw === b[head].raw) {
|
|
11
|
+
head += 1;
|
|
12
|
+
}
|
|
13
|
+
if (head === a.length && head === b.length) {
|
|
14
|
+
return { edit: null, keptBlocks: a.length, replacedBlocks: 0 };
|
|
15
|
+
}
|
|
16
|
+
let tail = 0;
|
|
17
|
+
while (tail < a.length - head && tail < b.length - head && a[a.length - 1 - tail].raw === b[b.length - 1 - tail].raw) {
|
|
18
|
+
tail += 1;
|
|
19
|
+
}
|
|
20
|
+
const oldFirst = head;
|
|
21
|
+
const oldLast = a.length - 1 - tail;
|
|
22
|
+
const start = oldFirst < a.length ? a[oldFirst].start : original.length;
|
|
23
|
+
const end = oldLast >= oldFirst ? a[oldLast].end : start;
|
|
24
|
+
const newStart = head < b.length ? b[head].start : proposed.length;
|
|
25
|
+
const newEnd = b.length - 1 - tail >= head ? b[b.length - 1 - tail].end : newStart;
|
|
26
|
+
return {
|
|
27
|
+
edit: { start, end, text: proposed.slice(newStart, newEnd) },
|
|
28
|
+
keptBlocks: head + tail,
|
|
29
|
+
replacedBlocks: Math.max(0, oldLast - oldFirst + 1)
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function spliceProposal(original, proposed) {
|
|
33
|
+
const { edit } = reduceToBlockEdit(original, proposed);
|
|
34
|
+
if (!edit) return null;
|
|
35
|
+
return spliceSave(original, [edit]);
|
|
36
|
+
}
|
|
37
|
+
function explainSpliceRefusal(error) {
|
|
38
|
+
const code = error instanceof SourceSpliceError ? error.code : null;
|
|
39
|
+
if (code === "island_edit" || code === "integrity") {
|
|
40
|
+
return "This change would alter protected content \u2014 code, math, a table, a section or hidden reasoning \u2014 which is only changed in its own editor.";
|
|
41
|
+
}
|
|
42
|
+
return "This change could not be placed exactly into the saved text.";
|
|
43
|
+
}
|
|
44
|
+
export {
|
|
45
|
+
explainSpliceRefusal,
|
|
46
|
+
reduceToBlockEdit,
|
|
47
|
+
spliceProposal
|
|
48
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface MaskedText {
|
|
2
|
+
/** What the agent receives. */
|
|
3
|
+
text: string;
|
|
4
|
+
/** Original bytes of each span; marker `⟦Pn⟧` stands for spans[n-1]. */
|
|
5
|
+
spans: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function maskProtectedSpans(text: string): MaskedText;
|
|
8
|
+
export type UnmaskResult = {
|
|
9
|
+
text: string;
|
|
10
|
+
} | {
|
|
11
|
+
error: string;
|
|
12
|
+
};
|
|
13
|
+
/** Put every protected span back, byte-for-byte — or say which ones the agent broke. */
|
|
14
|
+
export declare function unmaskProtectedSpans(result: string, spans: readonly string[]): UnmaskResult;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { listIslands, tokenizeSource } from "@ai-matrx/content-ir/source";
|
|
2
|
+
const ESCAPES = (
|
|
3
|
+
// eslint-disable-next-line no-control-regex
|
|
4
|
+
/\u001b\[[0-?]*[ -/]*[@-~]|\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)|\u001b[@-Z\\-_]|[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g
|
|
5
|
+
);
|
|
6
|
+
const MARKER = /⟦P(\d+)⟧/g;
|
|
7
|
+
function maskProtectedSpans(text) {
|
|
8
|
+
if (MARKER.test(text)) {
|
|
9
|
+
MARKER.lastIndex = 0;
|
|
10
|
+
return { text, spans: [] };
|
|
11
|
+
}
|
|
12
|
+
MARKER.lastIndex = 0;
|
|
13
|
+
const ranges = [];
|
|
14
|
+
for (const m of text.matchAll(ESCAPES)) {
|
|
15
|
+
ranges.push({ start: m.index ?? 0, end: (m.index ?? 0) + m[0].length });
|
|
16
|
+
}
|
|
17
|
+
for (const island of listIslands(tokenizeSource(text))) {
|
|
18
|
+
if (island.inline || island.islandType === "json") {
|
|
19
|
+
ranges.push({ start: island.start, end: island.end });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
ranges.sort((a, b) => a.start - b.start || b.end - a.end);
|
|
23
|
+
const kept = [];
|
|
24
|
+
for (const r of ranges) {
|
|
25
|
+
const last = kept[kept.length - 1];
|
|
26
|
+
if (last && r.start < last.end) continue;
|
|
27
|
+
kept.push(r);
|
|
28
|
+
}
|
|
29
|
+
if (kept.length === 0) return { text, spans: [] };
|
|
30
|
+
const spans = [];
|
|
31
|
+
let out = "";
|
|
32
|
+
let at = 0;
|
|
33
|
+
for (const r of kept) {
|
|
34
|
+
out += text.slice(at, r.start);
|
|
35
|
+
spans.push(text.slice(r.start, r.end));
|
|
36
|
+
out += `\u27E6P${spans.length}\u27E7`;
|
|
37
|
+
at = r.end;
|
|
38
|
+
}
|
|
39
|
+
out += text.slice(at);
|
|
40
|
+
return { text: out, spans };
|
|
41
|
+
}
|
|
42
|
+
function unmaskProtectedSpans(result, spans) {
|
|
43
|
+
if (spans.length === 0) return { text: result };
|
|
44
|
+
const seen = /* @__PURE__ */ new Map();
|
|
45
|
+
for (const m of result.matchAll(MARKER)) {
|
|
46
|
+
const n = Number(m[1]);
|
|
47
|
+
seen.set(n, (seen.get(n) ?? 0) + 1);
|
|
48
|
+
}
|
|
49
|
+
const broken = spans.map((_, i) => i + 1).filter((n) => seen.get(n) !== 1);
|
|
50
|
+
const invented = [...seen.keys()].filter((n) => n < 1 || n > spans.length);
|
|
51
|
+
if (broken.length > 0 || invented.length > 0) {
|
|
52
|
+
return {
|
|
53
|
+
error: "The agent's result dropped or changed protected text (terminal codes, variables or similar), so it cannot be applied. Run it again, or make the change in the editor."
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return { text: result.replace(MARKER, (_, n) => spans[Number(n) - 1]) };
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
maskProtectedSpans,
|
|
60
|
+
unmaskProtectedSpans
|
|
61
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { RichDocumentActionSpec, RichDocumentSurfaceRegistration } from "../types.js";
|
|
2
|
+
export interface ActionSurfacesState {
|
|
3
|
+
/** surfaceId → ordered stack of providers (oldest first, newest at end). */
|
|
4
|
+
bySurfaceId: Record<string, RichDocumentSurfaceRegistration[]>;
|
|
5
|
+
}
|
|
6
|
+
/** Push a provider onto a surface's stack; a re-registration by providerId updates in place. */
|
|
7
|
+
export declare function registerProvider(payload: {
|
|
8
|
+
surfaceId: string;
|
|
9
|
+
registration: RichDocumentSurfaceRegistration;
|
|
10
|
+
}): void;
|
|
11
|
+
/** Update the action-spec snapshot for an already-registered provider. */
|
|
12
|
+
export declare function updateProviderSpecs(payload: {
|
|
13
|
+
surfaceId: string;
|
|
14
|
+
providerId: string;
|
|
15
|
+
computedActionSpecs: RichDocumentActionSpec[];
|
|
16
|
+
contentSourceId: string;
|
|
17
|
+
}): void;
|
|
18
|
+
/** Remove a provider by id; the remaining order is preserved. */
|
|
19
|
+
export declare function unregisterProvider(payload: {
|
|
20
|
+
surfaceId: string;
|
|
21
|
+
providerId: string;
|
|
22
|
+
}): void;
|
|
23
|
+
/** Test/maintenance: clear everything. */
|
|
24
|
+
export declare function clearAll(): void;
|
|
25
|
+
export declare function getActionSurfacesState(): ActionSurfacesState;
|
|
26
|
+
export declare function selectTopProvider(state: ActionSurfacesState, surfaceId: string): RichDocumentSurfaceRegistration | null;
|
|
27
|
+
export declare function selectProviderStack(state: ActionSurfacesState, surfaceId: string): RichDocumentSurfaceRegistration[];
|
|
28
|
+
export declare function selectSurfaceActionSpecs(state: ActionSurfacesState, surfaceId: string): RichDocumentActionSpec[];
|
|
29
|
+
/** The top-of-stack provider for a surface, live. */
|
|
30
|
+
export declare function useTopProvider(surfaceId: string): RichDocumentSurfaceRegistration | null;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { useSyncExternalStore } from "react";
|
|
2
|
+
const KEY = /* @__PURE__ */ Symbol.for("ai-matrx.rich-content.rich-document.action-surfaces");
|
|
3
|
+
function store() {
|
|
4
|
+
const g = globalThis;
|
|
5
|
+
return g[KEY] ??= { state: { bySurfaceId: {} }, listeners: /* @__PURE__ */ new Set() };
|
|
6
|
+
}
|
|
7
|
+
function commit(next) {
|
|
8
|
+
const s = store();
|
|
9
|
+
s.state = { bySurfaceId: next };
|
|
10
|
+
for (const listener of s.listeners) listener();
|
|
11
|
+
}
|
|
12
|
+
function registerProvider(payload) {
|
|
13
|
+
const { surfaceId, registration } = payload;
|
|
14
|
+
const stack = [...store().state.bySurfaceId[surfaceId] ?? []];
|
|
15
|
+
const idx = stack.findIndex((entry) => entry.providerId === registration.providerId);
|
|
16
|
+
if (idx >= 0) stack[idx] = registration;
|
|
17
|
+
else stack.push(registration);
|
|
18
|
+
commit({ ...store().state.bySurfaceId, [surfaceId]: stack });
|
|
19
|
+
}
|
|
20
|
+
function updateProviderSpecs(payload) {
|
|
21
|
+
const { surfaceId, providerId, computedActionSpecs, contentSourceId } = payload;
|
|
22
|
+
const stack = store().state.bySurfaceId[surfaceId];
|
|
23
|
+
if (!stack) return;
|
|
24
|
+
const idx = stack.findIndex((entry) => entry.providerId === providerId);
|
|
25
|
+
if (idx < 0) return;
|
|
26
|
+
const next = [...stack];
|
|
27
|
+
next[idx] = { ...stack[idx], computedActionSpecs, contentSourceId };
|
|
28
|
+
commit({ ...store().state.bySurfaceId, [surfaceId]: next });
|
|
29
|
+
}
|
|
30
|
+
function unregisterProvider(payload) {
|
|
31
|
+
const { surfaceId, providerId } = payload;
|
|
32
|
+
const stack = store().state.bySurfaceId[surfaceId];
|
|
33
|
+
if (!stack) return;
|
|
34
|
+
const next = stack.filter((entry) => entry.providerId !== providerId);
|
|
35
|
+
const all = { ...store().state.bySurfaceId };
|
|
36
|
+
if (next.length === 0) delete all[surfaceId];
|
|
37
|
+
else all[surfaceId] = next;
|
|
38
|
+
commit(all);
|
|
39
|
+
}
|
|
40
|
+
function clearAll() {
|
|
41
|
+
commit({});
|
|
42
|
+
}
|
|
43
|
+
function getActionSurfacesState() {
|
|
44
|
+
return store().state;
|
|
45
|
+
}
|
|
46
|
+
function subscribe(listener) {
|
|
47
|
+
store().listeners.add(listener);
|
|
48
|
+
return () => store().listeners.delete(listener);
|
|
49
|
+
}
|
|
50
|
+
const EMPTY_PROVIDER_STACK = [];
|
|
51
|
+
const EMPTY_ACTION_SPECS = [];
|
|
52
|
+
function selectTopProvider(state, surfaceId) {
|
|
53
|
+
const stack = state.bySurfaceId[surfaceId];
|
|
54
|
+
return stack && stack.length > 0 ? stack[stack.length - 1] : null;
|
|
55
|
+
}
|
|
56
|
+
function selectProviderStack(state, surfaceId) {
|
|
57
|
+
return state.bySurfaceId[surfaceId] ?? EMPTY_PROVIDER_STACK;
|
|
58
|
+
}
|
|
59
|
+
function selectSurfaceActionSpecs(state, surfaceId) {
|
|
60
|
+
return selectTopProvider(state, surfaceId)?.computedActionSpecs ?? EMPTY_ACTION_SPECS;
|
|
61
|
+
}
|
|
62
|
+
function useTopProvider(surfaceId) {
|
|
63
|
+
return useSyncExternalStore(
|
|
64
|
+
subscribe,
|
|
65
|
+
() => selectTopProvider(store().state, surfaceId),
|
|
66
|
+
() => null
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
clearAll,
|
|
71
|
+
getActionSurfacesState,
|
|
72
|
+
registerProvider,
|
|
73
|
+
selectProviderStack,
|
|
74
|
+
selectSurfaceActionSpecs,
|
|
75
|
+
selectTopProvider,
|
|
76
|
+
unregisterProvider,
|
|
77
|
+
updateProviderSpecs,
|
|
78
|
+
useTopProvider
|
|
79
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RichDocumentAction, RichDocumentActionId, RichDocumentActionsVariant } from "../types.js";
|
|
2
|
+
export type ContextMenuOption = boolean | {
|
|
3
|
+
extra?: RichDocumentAction[];
|
|
4
|
+
exclude?: (RichDocumentActionId | string)[];
|
|
5
|
+
};
|
|
6
|
+
export declare function contextMenuFor(prop: ContextMenuOption | undefined, variant: RichDocumentActionsVariant): ContextMenuOption | false;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ClickTarget } from "@ai-matrx/alchemy/actions";
|
|
2
|
+
import type { RichDocumentAction, RichDocumentActionContext } from "../types.js";
|
|
3
|
+
export interface ProviderBridge {
|
|
4
|
+
/** Builds a fresh action context from the host's live state/refs. */
|
|
5
|
+
getCtx: () => RichDocumentActionContext;
|
|
6
|
+
/** The actions visible for this provider (already filtered + sorted). */
|
|
7
|
+
resolvedActions: RichDocumentAction[];
|
|
8
|
+
/** The Alchemy click target the package layouts resolve against (ALC-15). */
|
|
9
|
+
target: ClickTarget;
|
|
10
|
+
}
|
|
11
|
+
export declare function registerBridge(providerId: string, bridge: ProviderBridge): void;
|
|
12
|
+
/**
|
|
13
|
+
* Refresh an existing bridge (no semantic difference from register today,
|
|
14
|
+
* but kept separate so call sites are self-documenting).
|
|
15
|
+
*/
|
|
16
|
+
export declare function updateBridge(providerId: string, bridge: ProviderBridge): void;
|
|
17
|
+
export declare function unregisterBridge(providerId: string): void;
|
|
18
|
+
export declare function getBridge(providerId: string): ProviderBridge | undefined;
|
|
19
|
+
/** Diagnostics only. */
|
|
20
|
+
export declare function _getAllBridges(): ReadonlyMap<string, ProviderBridge>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const BRIDGES = /* @__PURE__ */ new Map();
|
|
2
|
+
function registerBridge(providerId, bridge) {
|
|
3
|
+
BRIDGES.set(providerId, bridge);
|
|
4
|
+
}
|
|
5
|
+
function updateBridge(providerId, bridge) {
|
|
6
|
+
BRIDGES.set(providerId, bridge);
|
|
7
|
+
}
|
|
8
|
+
function unregisterBridge(providerId) {
|
|
9
|
+
BRIDGES.delete(providerId);
|
|
10
|
+
}
|
|
11
|
+
function getBridge(providerId) {
|
|
12
|
+
return BRIDGES.get(providerId);
|
|
13
|
+
}
|
|
14
|
+
function _getAllBridges() {
|
|
15
|
+
return BRIDGES;
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
_getAllBridges,
|
|
19
|
+
getBridge,
|
|
20
|
+
registerBridge,
|
|
21
|
+
unregisterBridge,
|
|
22
|
+
updateBridge
|
|
23
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ClickTarget } from "@ai-matrx/alchemy/actions";
|
|
2
|
+
import type { ContentSource, RichDocumentAction, RichDocumentActionContext, RichDocumentActionsProp, RichDocumentActionsVariant } from "../types.js";
|
|
3
|
+
export interface UseActionSurfaceProviderArgs {
|
|
4
|
+
content: string | undefined;
|
|
5
|
+
source: ContentSource;
|
|
6
|
+
actions?: RichDocumentActionsProp;
|
|
7
|
+
/** Only "remote" registers to the Redux surface; all variants register the bridge. */
|
|
8
|
+
actionsVariant: RichDocumentActionsVariant;
|
|
9
|
+
/** Required when actionsVariant === "remote". */
|
|
10
|
+
actionsSurfaceId?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface UseActionSurfaceProviderResult {
|
|
13
|
+
/** Render-time context (from props). Safe to read during render — used to
|
|
14
|
+
* resolve auxiliary action sets (e.g. context-menu actions). */
|
|
15
|
+
ctx: RichDocumentActionContext;
|
|
16
|
+
/** Ref-based context factory. Read inside event handlers only. */
|
|
17
|
+
getCtx: () => RichDocumentActionContext;
|
|
18
|
+
/** The actions visible for this source + this consumer's exclude/extra. */
|
|
19
|
+
resolvedActions: RichDocumentAction[];
|
|
20
|
+
/**
|
|
21
|
+
* The Alchemy click target for this content (ALC-15): what the package's
|
|
22
|
+
* layouts (bar, ⋯, sheet) resolve the one registry against. Stable until
|
|
23
|
+
* something that changes eligibility changes (content, source, auth, org,
|
|
24
|
+
* exclude/extra).
|
|
25
|
+
*/
|
|
26
|
+
target: ClickTarget;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Register a RichDocument action provider (specs + bridge) for a surface and
|
|
30
|
+
* expose the live context + resolved action list. See the file header for the
|
|
31
|
+
* no-functions-in-Redux invariant.
|
|
32
|
+
*/
|
|
33
|
+
export declare function useActionSurfaceProvider(args: UseActionSurfaceProviderArgs): UseActionSurfaceProviderResult;
|