@ai-matrx/chat 0.4.9 → 0.4.12
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 +53 -0
- package/dist/agents/components/context-items/bodies/ContextInputBody.js +1 -1
- package/dist/agents/components/context-items/bodies/GenericBody.js +1 -1
- package/dist/agents/components/conversation-actions/conversation-verbs.js +1 -1
- package/dist/agents/components/inputs/smart-input/RunSkillPicker.js +1 -1
- package/dist/agents/components/live-run/LiveRunDisplay.js +1 -1
- package/dist/agents/components/live-run/LiveRunProgress.js +1 -1
- package/dist/agents/components/live-run/RunSetDisplay.js +1 -1
- package/dist/agents/components/messages-display/assistant/AgentAssistantMessage.js +2 -2
- package/dist/agents/components/messages-display/assistant/InPlaceAnswerEditor.js +1 -1
- package/dist/agents/components/messages-display/conversation-tools/find-in-conversation.js +1 -1
- package/dist/agents/components/messages-display/message-options/buildTaskSeedFromMessage.js +1 -1
- package/dist/agents/components/messages-display/user/AgentUserMessage.js +1 -1
- package/dist/agents/components/messages-display/user/FirstTurnVariables.js +1 -1
- package/dist/agents/components/run/AgentStatusIndicator.js +1 -1
- package/dist/agents/components/run/RunJobWorkingLine.js +1 -1
- package/dist/agents/components/shared/AgentNewRunButton.js +1 -1
- package/dist/agents/components/shared/AgentPlanningIndicator.js +1 -1
- package/dist/agents/conversation-export/conversation-transfer.js +1 -1
- package/dist/agents/conversation-export/document-markdown.js +1 -1
- package/dist/agents/hooks/useConversationRoutePromotion.js +1 -1
- package/dist/agents/hooks/useModelControls.js +1 -1
- package/dist/agents/redux/agent-definition/converters.js +1 -1
- package/dist/agents/redux/agent-definition/slice.js +1 -1
- package/dist/agents/redux/conversation-list/conversation-list.thunks.js +1 -1
- package/dist/agents/redux/execution-system/active-requests/active-requests.selectors.js +1 -1
- package/dist/agents/redux/execution-system/instance-context/sync-canvas-item-context.thunk.js +1 -1
- package/dist/agents/redux/execution-system/thunks/process-stream.js +1 -1
- package/dist/agents/redux/execution-system/thunks/resolve-base-url.js +17 -0
- package/dist/agents/redux/execution-system/thunks/run-ai-stream.d.ts +7 -0
- package/dist/agents/redux/execution-system/thunks/run-ai-stream.js +13 -0
- package/dist/agents/redux/execution-system/thunks/run-headless-agent-json.js +14 -2
- package/dist/agents/redux/execution-system/utils/assemble-cx-content-blocks.js +1 -1
- package/dist/agents/redux/execution-system/utils/normalize-content-blocks.js +1 -1
- package/dist/agents/redux/execution-system/utils/output-contract-guard.js +1 -1
- package/dist/agents/redux/execution-system/utils/stream-block-accumulator.js +8 -8
- package/dist/agents/routing/confidential-routing.d.ts +52 -0
- package/dist/agents/routing/confidential-routing.js +38 -0
- package/dist/agents/utils/canvasItemContext.js +1 -1
- package/dist/canvas/workspace/ChatPanelTitleMenu.js +1 -1
- package/dist/canvas/workspace/useCanvasWorkspaceConversation.js +1 -1
- package/dist/cx-chat/components/SsrSidebarChats.js +1 -1
- package/dist/cx-chat/components/core/ChatConversationClient.js +1 -1
- package/dist/cx-chat/components/messages/AssistantMessage.js +1 -1
- package/dist/cx-chat/hooks/useInstanceBootstrap.js +1 -1
- package/dist/cx-conversation/AssistantMessage.js +1 -1
- package/dist/host/configure.js +4 -1
- package/dist/host/contract.d.ts +14 -1
- package/dist/host/index.d.ts +1 -0
- package/dist/host/index.js +1 -0
- package/dist/mandates/llm-params.d.ts +1 -1
- package/dist/mandates/output-contract.js +1 -1
- package/dist/mandates/service.d.ts +1 -1
- package/dist/mandates/service.js +1 -1
- package/dist/mandates/test-run-core.js +1 -1
- package/dist/public-chat/components/PublicMessageOptionsMenu.js +1 -1
- package/dist/surfaces/services/surface-config.service.js +1 -1
- package/dist/tool-call-visualization/components/ToolCallBatch.js +1 -1
- package/dist/tool-call-visualization/components/ToolCallVisualization.js +1 -1
- package/dist/tool-call-visualization/renderers/agent-call/AgentCallInline.js +1 -1
- package/dist/tool-call-visualization/renderers/agent-call/CollabCallCard.js +1 -1
- package/dist/tool-call-visualization/renderers/agent-call/collab-subtitle.js +3 -3
- package/dist/tool-call-visualization/renderers/skill/SkillInline.js +1 -1
- package/dist/tool-call-visualization/result-fields/KeyValueGrid.js +1 -1
- package/dist/tool-call-visualization/result-fields/ResultValue.js +1 -1
- package/dist/tool-call-visualization/result-fields/shape.js +1 -1
- package/dist/tool-call-visualization/utils/cxToolCallToLifecycleEntry.js +2 -2
- package/dist/tool-call-visualization/utils/human-copy.js +3 -3
- package/dist/ui/mandates-storage/provision-shapes.d.ts +1 -1
- package/dist/ui/mandates-storage/provision-shapes.js +1 -1
- package/dist/ui/markdown-stream/EnhancedChatMarkdown.js +1 -1
- package/dist/ui/treatment-shape.d.ts +1 -1
- package/dist/ui/treatment-shape.js +1 -1
- package/dist/voice-agent/relay/useVoiceRelaySession.js +1 -1
- package/package.json +26 -9
- package/dist/agents/hooks/useWarmAgent.d.ts +0 -8
- package/dist/agents/hooks/useWarmAgent.js +0 -25
- package/dist/agents/utils/artifactId.d.ts +0 -34
- package/dist/agents/utils/artifactId.js +0 -15
- package/dist/ui/ShimmerText.d.ts +0 -5
- package/dist/ui/ShimmerText.js +0 -21
- package/dist/ui/addressWithoutNavigating.d.ts +0 -68
- package/dist/ui/addressWithoutNavigating.js +0 -60
- package/dist/utils/content-ir/decision-answers-ids.d.ts +0 -3
- package/dist/utils/content-ir/decision-answers-ids.js +0 -6
- package/dist/utils/content-ir/envelope-parity.d.ts +0 -12
- package/dist/utils/content-ir/envelope-parity.js +0 -32
- package/dist/utils/content-ir/instance-title.d.ts +0 -26
- package/dist/utils/content-ir/instance-title.js +0 -39
- package/dist/utils/content-ir/kinds/kind-markdown-utils.d.ts +0 -84
- package/dist/utils/content-ir/kinds/kind-markdown-utils.js +0 -233
- package/dist/utils/content-ir/kinds/kind-value-markdown.d.ts +0 -5
- package/dist/utils/content-ir/kinds/kind-value-markdown.js +0 -17
- package/dist/utils/content-ir/legacy-quiz-envelope.d.ts +0 -3
- package/dist/utils/content-ir/legacy-quiz-envelope.js +0 -45
- package/dist/utils/content-ir/surfaces/embedded-kind-json.d.ts +0 -140
- package/dist/utils/content-ir/surfaces/embedded-kind-json.js +0 -527
- package/dist/utils/content-ir/surfaces/json-kind-signal.d.ts +0 -307
- package/dist/utils/content-ir/surfaces/json-kind-signal.js +0 -945
- package/dist/utils/content-ir/surfaces/kind-image-alt.d.ts +0 -27
- package/dist/utils/content-ir/surfaces/kind-image-alt.js +0 -117
- package/dist/utils/content-ir/surfaces/kind-one-line.d.ts +0 -67
- package/dist/utils/content-ir/surfaces/kind-one-line.js +0 -209
- package/dist/utils/content-ir/surfaces/kind-text-label.d.ts +0 -20
- package/dist/utils/content-ir/surfaces/kind-text-label.js +0 -50
- package/dist/utils/content-ir/surfaces/kind-text-to-markdown.d.ts +0 -47
- package/dist/utils/content-ir/surfaces/kind-text-to-markdown.js +0 -399
- package/dist/utils/content-ir/surfaces/markdown-escaped-kind.d.ts +0 -54
- package/dist/utils/content-ir/surfaces/markdown-escaped-kind.js +0 -128
- package/dist/utils/content-ir/surfaces/quoted-kind-lift.d.ts +0 -61
- package/dist/utils/content-ir/surfaces/quoted-kind-lift.js +0 -215
- package/dist/utils/inline-decision/decision-options.d.ts +0 -21
- package/dist/utils/inline-decision/decision-options.js +0 -32
- package/dist/utils/json.d.ts +0 -63
- package/dist/utils/json.js +0 -22
- package/dist/utils/markdown/plain-title.d.ts +0 -25
- package/dist/utils/markdown/plain-title.js +0 -51
- package/dist/utils/rendered-find.d.ts +0 -9
- package/dist/utils/rendered-find.js +0 -68
- package/dist/utils/rich-editor/core/caret-context.d.ts +0 -24
- package/dist/utils/rich-editor/core/caret-context.js +0 -73
- package/dist/utils/rich-editor/in-place/caret-handoff.d.ts +0 -3
- package/dist/utils/rich-editor/in-place/caret-handoff.js +0 -15
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
import { FenceReader, fenceOpenerOf } from "@ai-matrx/content-ir/source";
|
|
2
|
-
import { jsonKindSignal } from "./json-kind-signal.js";
|
|
3
|
-
const JSON_FENCE_LANGS = /* @__PURE__ */ new Set(["", "json", "jsonc", "json5"]);
|
|
4
|
-
const QUOTE_PREFIX = /^ {0,3}>(?: ?>)* ?/;
|
|
5
|
-
function quoteDepth(prefix) {
|
|
6
|
-
return (prefix.match(/>/g) ?? []).length;
|
|
7
|
-
}
|
|
8
|
-
function prefixOfDepth(line, depth) {
|
|
9
|
-
const match = new RegExp(`^ {0,3}>(?: ?>){${depth - 1}} ?`).exec(line);
|
|
10
|
-
return match ? match[0] : null;
|
|
11
|
-
}
|
|
12
|
-
function bracketDelta(line) {
|
|
13
|
-
let delta = 0;
|
|
14
|
-
let inString = false;
|
|
15
|
-
let escaped = false;
|
|
16
|
-
for (const ch of line) {
|
|
17
|
-
if (inString) {
|
|
18
|
-
if (escaped) escaped = false;
|
|
19
|
-
else if (ch === "\\") escaped = true;
|
|
20
|
-
else if (ch === '"') inString = false;
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
if (ch === '"') inString = true;
|
|
24
|
-
else if (ch === "{" || ch === "[") delta++;
|
|
25
|
-
else if (ch === "}" || ch === "]") delta--;
|
|
26
|
-
}
|
|
27
|
-
return delta;
|
|
28
|
-
}
|
|
29
|
-
class QuotedKindLift {
|
|
30
|
-
mode = { kind: "none" };
|
|
31
|
-
/** The current line's bytes not yet handed on (held while undecided). */
|
|
32
|
-
held = "";
|
|
33
|
-
/** The current line's bytes so far (handed on or not), as received. */
|
|
34
|
-
line = "";
|
|
35
|
-
/** Whether the rest of the current line passes straight through. */
|
|
36
|
-
passing = false;
|
|
37
|
-
/** Prefix stripped from the current line's handed-on bytes (passing lines). */
|
|
38
|
-
stripping = 0;
|
|
39
|
-
push(text) {
|
|
40
|
-
let out = "";
|
|
41
|
-
for (const ch of text) out += this.pushChar(ch);
|
|
42
|
-
return out;
|
|
43
|
-
}
|
|
44
|
-
flush() {
|
|
45
|
-
let out = "";
|
|
46
|
-
if (this.mode.kind === "candidate") {
|
|
47
|
-
out += this.mode.raw.join("\n") + (this.mode.raw.length ? "\n" : "");
|
|
48
|
-
this.mode = { kind: "none" };
|
|
49
|
-
}
|
|
50
|
-
out += this.held;
|
|
51
|
-
this.held = "";
|
|
52
|
-
this.line = "";
|
|
53
|
-
this.passing = false;
|
|
54
|
-
this.stripping = 0;
|
|
55
|
-
return out;
|
|
56
|
-
}
|
|
57
|
-
pushChar(ch) {
|
|
58
|
-
if (ch === "\n") return this.endLine();
|
|
59
|
-
this.line += ch;
|
|
60
|
-
if (this.passing) {
|
|
61
|
-
if (this.stripping > 0) {
|
|
62
|
-
this.stripping--;
|
|
63
|
-
return "";
|
|
64
|
-
}
|
|
65
|
-
return ch;
|
|
66
|
-
}
|
|
67
|
-
this.held += ch;
|
|
68
|
-
return this.decideMidLine();
|
|
69
|
-
}
|
|
70
|
-
/** Try to decide the current line before it ends; returns bytes released. */
|
|
71
|
-
decideMidLine() {
|
|
72
|
-
const mode = this.mode;
|
|
73
|
-
if (mode.kind === "source_fence") {
|
|
74
|
-
return this.release(this.held, 0);
|
|
75
|
-
}
|
|
76
|
-
if (mode.kind === "lifted_fence" || mode.kind === "lifted_bare") {
|
|
77
|
-
const prefix2 = prefixOfDepth(this.held, mode.depth);
|
|
78
|
-
if (prefix2 !== null && (this.held.length > prefix2.length || /> $/.test(prefix2))) {
|
|
79
|
-
const rest2 = this.held.slice(prefix2.length);
|
|
80
|
-
if (mode.kind === "lifted_fence" && /^\s*[`~]/.test(rest2)) return "";
|
|
81
|
-
return this.release(rest2, 0);
|
|
82
|
-
}
|
|
83
|
-
if (!/^ {0,3}(?:> ?)*$/.test(this.held)) {
|
|
84
|
-
return this.release(this.held, 0);
|
|
85
|
-
}
|
|
86
|
-
return "";
|
|
87
|
-
}
|
|
88
|
-
if (mode.kind === "candidate") return "";
|
|
89
|
-
if (!/^ {0,3}(?:>|$)/.test(this.held) && !/^ {0,3}$/.test(this.held)) {
|
|
90
|
-
return this.release(this.held, 0);
|
|
91
|
-
}
|
|
92
|
-
const prefix = QUOTE_PREFIX.exec(this.held)?.[0];
|
|
93
|
-
if (!prefix) return "";
|
|
94
|
-
const rest = this.held.slice(prefix.length);
|
|
95
|
-
if (!rest.trim()) return "";
|
|
96
|
-
const first = rest.trimStart()[0];
|
|
97
|
-
if (first === "`" || first === "~") return "";
|
|
98
|
-
if (first === "{" || first === "[") {
|
|
99
|
-
if (jsonKindSignal(rest) === "kind") {
|
|
100
|
-
this.mode = { kind: "lifted_bare", depth: quoteDepth(prefix), balance: 0 };
|
|
101
|
-
return this.release(rest, 0);
|
|
102
|
-
}
|
|
103
|
-
return "";
|
|
104
|
-
}
|
|
105
|
-
return this.release(this.held, 0);
|
|
106
|
-
}
|
|
107
|
-
/** Hand on `bytes` and let the rest of the line pass straight through. */
|
|
108
|
-
release(bytes, strip) {
|
|
109
|
-
this.held = "";
|
|
110
|
-
this.passing = true;
|
|
111
|
-
this.stripping = strip;
|
|
112
|
-
return bytes;
|
|
113
|
-
}
|
|
114
|
-
endLine() {
|
|
115
|
-
const line = this.line;
|
|
116
|
-
const held = this.held;
|
|
117
|
-
this.line = "";
|
|
118
|
-
this.held = "";
|
|
119
|
-
this.passing = false;
|
|
120
|
-
this.stripping = 0;
|
|
121
|
-
const mode = this.mode;
|
|
122
|
-
if (mode.kind === "source_fence") {
|
|
123
|
-
const content = mode.depth > 0 ? line.replace(QUOTE_PREFIX, "") : line;
|
|
124
|
-
if (mode.reader.feed(content)) this.mode = { kind: "none" };
|
|
125
|
-
return held + "\n";
|
|
126
|
-
}
|
|
127
|
-
if (mode.kind === "lifted_fence") {
|
|
128
|
-
const prefix = prefixOfDepth(line, mode.depth);
|
|
129
|
-
const content = prefix !== null ? line.slice(prefix.length) : line;
|
|
130
|
-
if (mode.reader.feed(content)) this.mode = { kind: "none" };
|
|
131
|
-
const sent = line.length - held.length;
|
|
132
|
-
const contentSent = Math.max(0, sent - (prefix?.length ?? 0));
|
|
133
|
-
return content.slice(contentSent) + "\n";
|
|
134
|
-
}
|
|
135
|
-
if (mode.kind === "lifted_bare") {
|
|
136
|
-
const prefix = prefixOfDepth(line, mode.depth);
|
|
137
|
-
const content = prefix !== null ? line.slice(prefix.length) : line;
|
|
138
|
-
mode.balance += bracketDelta(content);
|
|
139
|
-
if (mode.balance <= 0) this.mode = { kind: "none" };
|
|
140
|
-
const sent = line.length - held.length;
|
|
141
|
-
const contentSent = Math.max(0, sent - (prefix?.length ?? 0));
|
|
142
|
-
return content.slice(contentSent) + "\n";
|
|
143
|
-
}
|
|
144
|
-
if (mode.kind === "candidate") {
|
|
145
|
-
const prefix = prefixOfDepth(line, mode.depth);
|
|
146
|
-
if (prefix === null) {
|
|
147
|
-
const out = mode.raw.join("\n") + "\n";
|
|
148
|
-
this.mode = { kind: "none" };
|
|
149
|
-
return out + this.lineFromNone(line);
|
|
150
|
-
}
|
|
151
|
-
mode.raw.push(line);
|
|
152
|
-
mode.stripped.push(line.slice(prefix.length));
|
|
153
|
-
return this.settleCandidate(mode);
|
|
154
|
-
}
|
|
155
|
-
return this.lineFromNone(line, held);
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* A complete line met with no region open. `held` is what was not handed
|
|
159
|
-
* on yet: the whole line when it was held to the end, otherwise "" (it was
|
|
160
|
-
* released mid-line as prose or as an unquoted line).
|
|
161
|
-
*/
|
|
162
|
-
lineFromNone(line, held = line) {
|
|
163
|
-
const prefix = QUOTE_PREFIX.exec(line)?.[0];
|
|
164
|
-
if (held.length < line.length || !prefix) {
|
|
165
|
-
if (!prefix) {
|
|
166
|
-
const opener = fenceOpenerOf(line);
|
|
167
|
-
if (opener) {
|
|
168
|
-
this.mode = { kind: "source_fence", reader: new FenceReader(opener), depth: 0 };
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
return held + "\n";
|
|
172
|
-
}
|
|
173
|
-
const rest = line.slice(prefix.length);
|
|
174
|
-
const depth = quoteDepth(prefix);
|
|
175
|
-
const trimmed = rest.trimStart();
|
|
176
|
-
if (trimmed.startsWith("`") || trimmed.startsWith("~")) {
|
|
177
|
-
const opener = fenceOpenerOf(rest);
|
|
178
|
-
if (opener && JSON_FENCE_LANGS.has(opener.lang.toLowerCase())) {
|
|
179
|
-
this.mode = { kind: "lifted_fence", reader: new FenceReader(opener), depth };
|
|
180
|
-
return rest + "\n";
|
|
181
|
-
}
|
|
182
|
-
if (opener) {
|
|
183
|
-
this.mode = { kind: "source_fence", reader: new FenceReader(opener), depth };
|
|
184
|
-
}
|
|
185
|
-
return held + "\n";
|
|
186
|
-
}
|
|
187
|
-
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
188
|
-
const candidate = { kind: "candidate", depth, raw: [line], stripped: [rest] };
|
|
189
|
-
this.mode = candidate;
|
|
190
|
-
return this.settleCandidate(candidate);
|
|
191
|
-
}
|
|
192
|
-
return held + "\n";
|
|
193
|
-
}
|
|
194
|
-
/** A held quoted JSON start, re-judged after each complete line. */
|
|
195
|
-
settleCandidate(mode) {
|
|
196
|
-
const signal = jsonKindSignal(mode.stripped.join("\n"));
|
|
197
|
-
if (signal === "undecided") return "";
|
|
198
|
-
if (signal === "not_kind") {
|
|
199
|
-
this.mode = { kind: "none" };
|
|
200
|
-
return mode.raw.join("\n") + "\n";
|
|
201
|
-
}
|
|
202
|
-
const balance = mode.stripped.reduce((sum, l) => sum + bracketDelta(l), 0);
|
|
203
|
-
this.mode = balance > 0 ? { kind: "lifted_bare", depth: mode.depth, balance } : { kind: "none" };
|
|
204
|
-
return mode.stripped.join("\n") + "\n";
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
function liftQuotedKindRegions(source) {
|
|
208
|
-
if (!source.includes(">")) return source;
|
|
209
|
-
const lift = new QuotedKindLift();
|
|
210
|
-
return lift.push(source) + lift.flush();
|
|
211
|
-
}
|
|
212
|
-
export {
|
|
213
|
-
QuotedKindLift,
|
|
214
|
-
liftQuotedKindRegions
|
|
215
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The ONE place a `<decision>` body becomes options. Used by the splitter
|
|
3
|
-
* (content-splitter-core), the stream accumulator, and parseDecisionXml — the
|
|
4
|
-
* server twin is aidream matrx_ai/processing/blocks/parsers/decision_parser.py
|
|
5
|
-
* and MUST stay in lockstep with this file.
|
|
6
|
-
*
|
|
7
|
-
* Tolerant of real model output (captured 2026-10-04): extra attributes on
|
|
8
|
-
* <option> (`id` before/after `label`), single or double quotes, indentation
|
|
9
|
-
* and blank lines, and self-describing options with no `label` attribute
|
|
10
|
-
* (`<option id="x">Label text</option>` — the text names the choice).
|
|
11
|
-
* The choice id is the model's `id` attribute when present (unique), else
|
|
12
|
-
* `opt-N`. Only options whose closing tag has arrived count.
|
|
13
|
-
*/
|
|
14
|
-
export interface ParsedDecisionOption {
|
|
15
|
-
/** The model's own `id` attribute when present and unique, else `opt-N`. */
|
|
16
|
-
id: string;
|
|
17
|
-
label: string;
|
|
18
|
-
text: string;
|
|
19
|
-
}
|
|
20
|
-
export declare function parseOptionAttributes(attrString: string): Record<string, string>;
|
|
21
|
-
export declare function parseDecisionOptionsFromBody(body: string): ParsedDecisionOption[];
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
const OPTION_RE = /<option\b([^>]*)>([\s\S]*?)<\/option\s*>/gi;
|
|
2
|
-
const ATTR_RE = /([\w-]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
|
|
3
|
-
function parseOptionAttributes(attrString) {
|
|
4
|
-
const attrs = {};
|
|
5
|
-
ATTR_RE.lastIndex = 0;
|
|
6
|
-
let m;
|
|
7
|
-
while ((m = ATTR_RE.exec(attrString)) !== null) {
|
|
8
|
-
attrs[m[1]] = m[2] ?? m[3] ?? "";
|
|
9
|
-
}
|
|
10
|
-
return attrs;
|
|
11
|
-
}
|
|
12
|
-
function parseDecisionOptionsFromBody(body) {
|
|
13
|
-
const options = [];
|
|
14
|
-
const seen = /* @__PURE__ */ new Set();
|
|
15
|
-
const re = new RegExp(OPTION_RE.source, OPTION_RE.flags);
|
|
16
|
-
let m;
|
|
17
|
-
while ((m = re.exec(body)) !== null) {
|
|
18
|
-
const attrs = parseOptionAttributes(m[1]);
|
|
19
|
-
const text = m[2].trim();
|
|
20
|
-
const label = (attrs.label ?? attrs.title ?? attrs.name ?? "").trim() || text.split("\n")[0].trim();
|
|
21
|
-
if (label === "") continue;
|
|
22
|
-
const own = (attrs.id ?? "").trim();
|
|
23
|
-
const id = own !== "" && !seen.has(own) ? own : `opt-${options.length}`;
|
|
24
|
-
seen.add(id);
|
|
25
|
-
options.push({ id, label, text });
|
|
26
|
-
}
|
|
27
|
-
return options;
|
|
28
|
-
}
|
|
29
|
-
export {
|
|
30
|
-
parseDecisionOptionsFromBody,
|
|
31
|
-
parseOptionAttributes
|
|
32
|
-
};
|
package/dist/utils/json.d.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Canonical JSON value types + narrowing guards.
|
|
3
|
-
*
|
|
4
|
-
* ─── Why this exists ───────────────────────────────────────────────────────
|
|
5
|
-
* This repo patches the Supabase-generated `Json` alias to `unknown`
|
|
6
|
-
* (scripts/patch-db-types.sh) so that typed objects are freely assignable INTO
|
|
7
|
-
* JSONB columns on the write side. The cost lands on the READ side: a JSONB
|
|
8
|
-
* column comes back as bare `unknown`, and to do anything with it you must
|
|
9
|
-
* narrow it.
|
|
10
|
-
*
|
|
11
|
-
* The lazy/cheating reaction (the #1 source of `as unknown as` in the codebase)
|
|
12
|
-
* is to cast the WHOLE row — `row as unknown as MyTypedRow` — which re-asserts
|
|
13
|
-
* every already-correctly-typed column just to reach the one `unknown` JSON
|
|
14
|
-
* field. That silently discards real type information and is exactly the
|
|
15
|
-
* "type cheating" this system exists to kill.
|
|
16
|
-
*
|
|
17
|
-
* The honest move is to narrow ONLY the JSON field, and to have a real name for
|
|
18
|
-
* "this is just a JSON object, accept it" that is NOT `any` and NOT a bare
|
|
19
|
-
* `unknown`:
|
|
20
|
-
*
|
|
21
|
-
* import { type JsonObject, isJsonObject } from "@/types/json";
|
|
22
|
-
*
|
|
23
|
-
* // 1. "It's just an object" — type the ONE field, leave the row typed:
|
|
24
|
-
* interface MyRow { id: string; name: string; config: JsonObject }
|
|
25
|
-
*
|
|
26
|
-
* // 2. Narrowing a bare `unknown` DB field honestly (runtime-checked):
|
|
27
|
-
* const cfg = isJsonObject(row.config) ? row.config : undefined;
|
|
28
|
-
* // cfg is JsonObject | undefined — no cast, no `any`, no whole-row nuke.
|
|
29
|
-
*
|
|
30
|
-
* // 3. Reading a leaf — JsonObject values are JsonValue, narrow as needed:
|
|
31
|
-
* const label = typeof cfg?.label === "string" ? cfg.label : null;
|
|
32
|
-
*
|
|
33
|
-
* For a concrete, known shape (an API/DB frame), prefer a Zod parse at the
|
|
34
|
-
* boundary (see TYPESCRIPT_STANDARDS.md §4). These types are for the genuinely
|
|
35
|
-
* open / loosely-shaped JSON the standards doc calls "just an object" — they
|
|
36
|
-
* give it an honest name so nobody reaches for `any` or a whole-row cast.
|
|
37
|
-
*/
|
|
38
|
-
export type JsonPrimitive = string | number | boolean | null;
|
|
39
|
-
/**
|
|
40
|
-
* A JSON object. Values are `JsonValue | undefined` so optional keys read
|
|
41
|
-
* cleanly (mirrors Supabase's original generated `Json` object member).
|
|
42
|
-
*/
|
|
43
|
-
export interface JsonObject {
|
|
44
|
-
[key: string]: JsonValue | undefined;
|
|
45
|
-
}
|
|
46
|
-
export type JsonArray = JsonValue[];
|
|
47
|
-
export type JsonValue = JsonPrimitive | JsonObject | JsonArray;
|
|
48
|
-
/**
|
|
49
|
-
* Narrow an `unknown` (e.g. a bare JSONB column) to a `JsonObject`.
|
|
50
|
-
* Plain object only — arrays and `null` return false.
|
|
51
|
-
*/
|
|
52
|
-
export declare function isJsonObject(value: unknown): value is JsonObject;
|
|
53
|
-
/**
|
|
54
|
-
* Convert a `JsonObject` (values `JsonValue | undefined`) into the strict
|
|
55
|
-
* `Record<string, JsonValue>` shape generated API request bodies demand,
|
|
56
|
-
* dropping `undefined` entries (they don't exist in real JSON — `undefined`
|
|
57
|
-
* only appears via optional-key reads). Cast-free by construction.
|
|
58
|
-
*/
|
|
59
|
-
export declare function toJsonRecord(obj: JsonObject): Record<string, JsonValue>;
|
|
60
|
-
/** Narrow an `unknown` to a `JsonArray`. */
|
|
61
|
-
export declare function isJsonArray(value: unknown): value is JsonArray;
|
|
62
|
-
/** Narrow an `unknown` to a JSON primitive (string | number | boolean | null). */
|
|
63
|
-
export declare function isJsonPrimitive(value: unknown): value is JsonPrimitive;
|
package/dist/utils/json.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
function isJsonObject(value) {
|
|
2
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3
|
-
}
|
|
4
|
-
function toJsonRecord(obj) {
|
|
5
|
-
const out = {};
|
|
6
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
7
|
-
if (value !== void 0) out[key] = value;
|
|
8
|
-
}
|
|
9
|
-
return out;
|
|
10
|
-
}
|
|
11
|
-
function isJsonArray(value) {
|
|
12
|
-
return Array.isArray(value);
|
|
13
|
-
}
|
|
14
|
-
function isJsonPrimitive(value) {
|
|
15
|
-
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
16
|
-
}
|
|
17
|
-
export {
|
|
18
|
-
isJsonArray,
|
|
19
|
-
isJsonObject,
|
|
20
|
-
isJsonPrimitive,
|
|
21
|
-
toJsonRecord
|
|
22
|
-
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/** Strip markdown syntax from ONE line, keeping its readable text. */
|
|
2
|
-
export declare function plainTextOfMarkdownLine(line: string): string;
|
|
3
|
-
export interface PlainTitleOptions {
|
|
4
|
-
/** Longest title; longer titles are cut at a word boundary with "…". */
|
|
5
|
-
maxLength?: number;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* The plain-text title of markdown content: its first heading or line with
|
|
9
|
-
* the syntax removed. Returns "" when the content has no nameable line.
|
|
10
|
-
*/
|
|
11
|
-
export declare function plainTitleFromMarkdown(source: string | null | undefined, { maxLength }?: PlainTitleOptions): string;
|
|
12
|
-
/**
|
|
13
|
-
* Display form of a STORED title that may still carry syntax (rows written
|
|
14
|
-
* before the projection existed). Clean titles come back unchanged; a title
|
|
15
|
-
* that projects to nothing keeps its original text rather than going blank.
|
|
16
|
-
*/
|
|
17
|
-
export declare function displayTitle(title: string | null | undefined): string;
|
|
18
|
-
/**
|
|
19
|
-
* A read-boundary copy of `row` whose `key` title is in display form.
|
|
20
|
-
* Services that return rows whose title may have been derived from markdown
|
|
21
|
-
* (flashcard sets, assessments, study media) pass them through this, so every
|
|
22
|
-
* list, header, picker and export shows the clean name. Storage is untouched;
|
|
23
|
-
* a person who edits and saves the name saves what they see.
|
|
24
|
-
*/
|
|
25
|
-
export declare function withDisplayTitle<T, K extends keyof T>(row: T, key: K): T;
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { fenceLineKinds } from "@ai-matrx/content-ir/source";
|
|
2
|
-
import { hasJsonKindKey } from "../content-ir/surfaces/json-kind-signal.js";
|
|
3
|
-
import { kindTextLabel } from "../content-ir/surfaces/kind-text-label.js";
|
|
4
|
-
function firstContentLine(source) {
|
|
5
|
-
const lines = source.split(/\r?\n/);
|
|
6
|
-
let i = 0;
|
|
7
|
-
if (lines[0]?.trim() === "---") {
|
|
8
|
-
const end = lines.findIndex((l, k) => k > 0 && /^(---|\.\.\.)\s*$/.test(l));
|
|
9
|
-
if (end > 0) i = end + 1;
|
|
10
|
-
}
|
|
11
|
-
const kinds = fenceLineKinds(source);
|
|
12
|
-
for (; i < lines.length; i += 1) {
|
|
13
|
-
const line = lines[i];
|
|
14
|
-
if (kinds[i] !== "prose") continue;
|
|
15
|
-
if (/^\s*([-=*_])(\s*\1){2,}\s*$/.test(line)) continue;
|
|
16
|
-
if (line.trim()) return line;
|
|
17
|
-
}
|
|
18
|
-
return "";
|
|
19
|
-
}
|
|
20
|
-
function plainTextOfMarkdownLine(line) {
|
|
21
|
-
let text = line.replace(/^\s{0,3}#{1,6}(\s+|$)/, "").replace(/^\s*(>\s?)+/, "").replace(/^\s*([-*+]|\d+[.)])\s+/, "").replace(/^\s*\[[ xX]\]\s+/, "").replace(/\s+#+\s*$/, "");
|
|
22
|
-
text = text.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/\[([^\]]+)\]\([^)]*\)/g, "$1").replace(/\[([^\]]+)\]\[[^\]]*\]/g, "$1").replace(/<(https?:\/\/[^>\s]+)>/g, "$1").replace(/<\/?[A-Za-z][\w:-]*(\s[^<>]*)?\/?>/g, "").replace(/(`+)([\s\S]*?)\1/g, "$2").replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, "$2").replace(/\*(?=\S)([^*]*?\S)\*/g, "$1").replace(/(^|\W)_(?=\S)([^_]*?\S)_(?=\W|$)/g, "$1$2").replace(/~~(?=\S)([\s\S]*?\S)~~/g, "$1").replace(/\\([\\`*_{}#+\-.!>~|])/g, "$1");
|
|
23
|
-
return text.replace(/\s+/g, " ").trim();
|
|
24
|
-
}
|
|
25
|
-
function plainTitleFromMarkdown(source, { maxLength } = {}) {
|
|
26
|
-
const text = source ?? "";
|
|
27
|
-
const holdsKind = hasJsonKindKey(text);
|
|
28
|
-
const title = plainTextOfMarkdownLine(holdsKind ? kindTextLabel(text, 200) : firstContentLine(text)) || // A kind always names its record (an `<artifact>`-wrapped kind used to
|
|
29
|
-
// project to "") — R4, round 6.
|
|
30
|
-
(holdsKind ? kindTextLabel(text, 200) : "");
|
|
31
|
-
if (!maxLength || title.length <= maxLength) return title;
|
|
32
|
-
const cut = title.slice(0, maxLength - 1);
|
|
33
|
-
const space = cut.lastIndexOf(" ");
|
|
34
|
-
return `${(space > maxLength * 0.6 ? cut.slice(0, space) : cut).trimEnd()}\u2026`;
|
|
35
|
-
}
|
|
36
|
-
function displayTitle(title) {
|
|
37
|
-
const raw = title ?? "";
|
|
38
|
-
return plainTitleFromMarkdown(raw) || raw.trim();
|
|
39
|
-
}
|
|
40
|
-
function withDisplayTitle(row, key) {
|
|
41
|
-
const value = row[key];
|
|
42
|
-
if (typeof value !== "string") return row;
|
|
43
|
-
const clean = displayTitle(value);
|
|
44
|
-
return clean === value ? row : { ...row, [key]: clean };
|
|
45
|
-
}
|
|
46
|
-
export {
|
|
47
|
-
displayTitle,
|
|
48
|
-
plainTextOfMarkdownLine,
|
|
49
|
-
plainTitleFromMarkdown,
|
|
50
|
-
withDisplayTitle
|
|
51
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/** Search the visible text of a rendered document without changing its DOM. */
|
|
2
|
-
export interface RenderedFindOptions {
|
|
3
|
-
caseSensitive?: boolean;
|
|
4
|
-
wholeWord?: boolean;
|
|
5
|
-
regex?: boolean;
|
|
6
|
-
}
|
|
7
|
-
export declare function findRenderedTextMatches(text: string, query: string, options?: RenderedFindOptions): Array<[number, number]>;
|
|
8
|
-
/** Ranges can cross inline formatting boundaries, such as a word split by <strong>. */
|
|
9
|
-
export declare function collectRenderedFindRanges(root: HTMLElement, query: string, options?: RenderedFindOptions): Range[];
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
function findRenderedTextMatches(text, query, options = {}) {
|
|
2
|
-
if (!query.trim()) return [];
|
|
3
|
-
let expression;
|
|
4
|
-
try {
|
|
5
|
-
const pattern = options.regex ? query : query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6
|
-
expression = new RegExp(pattern, options.caseSensitive ? "gu" : "giu");
|
|
7
|
-
} catch {
|
|
8
|
-
return [];
|
|
9
|
-
}
|
|
10
|
-
const matches = [];
|
|
11
|
-
for (const match of text.matchAll(expression)) {
|
|
12
|
-
if (!match[0] || match.index === void 0) continue;
|
|
13
|
-
const start = match.index;
|
|
14
|
-
const end = start + match[0].length;
|
|
15
|
-
if (options.wholeWord && (/[\p{L}\p{N}\p{M}_]/u.test(text[start - 1] ?? "") || /[\p{L}\p{N}\p{M}_]/u.test(text[end] ?? ""))) continue;
|
|
16
|
-
matches.push([start, end]);
|
|
17
|
-
}
|
|
18
|
-
return matches;
|
|
19
|
-
}
|
|
20
|
-
const IGNORE_SELECTOR = "[data-find-ignore], button, [role='button'], [role='menu'], script, style, textarea, input, [aria-hidden='true']";
|
|
21
|
-
function collectRenderedFindRanges(root, query, options = {}) {
|
|
22
|
-
if (!query.trim()) return [];
|
|
23
|
-
const doc = root.ownerDocument;
|
|
24
|
-
const walker = doc.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
|
|
25
|
-
acceptNode(node) {
|
|
26
|
-
const parent = node.parentElement;
|
|
27
|
-
return parent && node.nodeValue && !parent.closest(IGNORE_SELECTOR) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
const nodes = [];
|
|
31
|
-
const starts = [];
|
|
32
|
-
let text = "";
|
|
33
|
-
let previousBlock = null;
|
|
34
|
-
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
35
|
-
const block = node.parentElement?.closest("p,li,h1,h2,h3,h4,h5,h6,blockquote,pre,td,th,figcaption,div,section,article") ?? root;
|
|
36
|
-
if (previousBlock && block !== previousBlock) text += "\n";
|
|
37
|
-
previousBlock = block;
|
|
38
|
-
nodes.push(node);
|
|
39
|
-
starts.push(text.length);
|
|
40
|
-
text += node.nodeValue ?? "";
|
|
41
|
-
}
|
|
42
|
-
const locate = (offset, isEnd) => {
|
|
43
|
-
let lo = 0;
|
|
44
|
-
let hi = nodes.length - 1;
|
|
45
|
-
while (lo <= hi) {
|
|
46
|
-
const mid = lo + hi >> 1;
|
|
47
|
-
const start = starts[mid];
|
|
48
|
-
const length = nodes[mid].nodeValue?.length ?? 0;
|
|
49
|
-
if (isEnd ? offset > start && offset <= start + length : offset >= start && offset < start + length) return [nodes[mid], offset - start];
|
|
50
|
-
if (offset < start || isEnd && offset === start) hi = mid - 1;
|
|
51
|
-
else lo = mid + 1;
|
|
52
|
-
}
|
|
53
|
-
return null;
|
|
54
|
-
};
|
|
55
|
-
return findRenderedTextMatches(text, query, options).flatMap(([start, end]) => {
|
|
56
|
-
const from = locate(start, false);
|
|
57
|
-
const to = locate(end, true);
|
|
58
|
-
if (!from || !to) return [];
|
|
59
|
-
const range = doc.createRange();
|
|
60
|
-
range.setStart(from[0], from[1]);
|
|
61
|
-
range.setEnd(to[0], to[1]);
|
|
62
|
-
return [range];
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
export {
|
|
66
|
-
collectRenderedFindRanges,
|
|
67
|
-
findRenderedTextMatches
|
|
68
|
-
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/** The text around a click in the rendered view. */
|
|
2
|
-
export interface CaretContext {
|
|
3
|
-
/** Up to `CONTEXT_CHARS` rendered characters before the click. */
|
|
4
|
-
before: string;
|
|
5
|
-
/** Up to `CONTEXT_CHARS` rendered characters after the click. */
|
|
6
|
-
after: string;
|
|
7
|
-
}
|
|
8
|
-
export declare const CONTEXT_CHARS = 40;
|
|
9
|
-
/**
|
|
10
|
-
* Where the caret goes in `text` (an index 0..text.length), or null when the
|
|
11
|
-
* context cannot be found. Tries the longest context first (before + after),
|
|
12
|
-
* then shorter ones, then each side alone; the first occurrence wins.
|
|
13
|
-
*/
|
|
14
|
-
export declare function locateCaret(text: string, context: CaretContext): number | null;
|
|
15
|
-
/**
|
|
16
|
-
* The context of a point in a rendered element: the text before and after the
|
|
17
|
-
* DOM position (`node`, `offset`) within `root`.
|
|
18
|
-
*/
|
|
19
|
-
export declare function caretContextAt(root: Node, node: Node, offset: number): CaretContext | null;
|
|
20
|
-
/** The DOM position under a viewport point (both browser APIs). */
|
|
21
|
-
export declare function domPointAt(doc: Document, x: number, y: number): {
|
|
22
|
-
node: Node;
|
|
23
|
-
offset: number;
|
|
24
|
-
} | null;
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
const CONTEXT_CHARS = 40;
|
|
2
|
-
function collapse(text) {
|
|
3
|
-
let flat = "";
|
|
4
|
-
const at = [];
|
|
5
|
-
let lastSpace = false;
|
|
6
|
-
for (let i = 0; i < text.length; i++) {
|
|
7
|
-
const ch = text[i];
|
|
8
|
-
if (/\s/.test(ch)) {
|
|
9
|
-
if (lastSpace) continue;
|
|
10
|
-
lastSpace = true;
|
|
11
|
-
flat += " ";
|
|
12
|
-
} else {
|
|
13
|
-
lastSpace = false;
|
|
14
|
-
flat += ch;
|
|
15
|
-
}
|
|
16
|
-
at.push(i);
|
|
17
|
-
}
|
|
18
|
-
return { flat, at };
|
|
19
|
-
}
|
|
20
|
-
function locateCaret(text, context) {
|
|
21
|
-
const { flat, at } = collapse(text);
|
|
22
|
-
const before = collapse(context.before).flat;
|
|
23
|
-
const after = collapse(context.after).flat;
|
|
24
|
-
const toOriginal = (flatIndex) => flatIndex >= at.length ? text.length : at[flatIndex];
|
|
25
|
-
for (const len of [CONTEXT_CHARS, 24, 12, 6, 3]) {
|
|
26
|
-
const b = before.slice(-len);
|
|
27
|
-
const a = after.slice(0, len);
|
|
28
|
-
if (!b && !a) continue;
|
|
29
|
-
const idx = flat.indexOf(b + a);
|
|
30
|
-
if (idx >= 0 && (b + a).length >= Math.min(3, before.length + after.length)) return toOriginal(idx + b.length);
|
|
31
|
-
}
|
|
32
|
-
for (const len of [CONTEXT_CHARS, 12, 4]) {
|
|
33
|
-
const a = after.slice(0, len);
|
|
34
|
-
if (a.trim().length >= 3) {
|
|
35
|
-
const idx = flat.indexOf(a);
|
|
36
|
-
if (idx >= 0) return toOriginal(idx);
|
|
37
|
-
}
|
|
38
|
-
const b = before.slice(-len);
|
|
39
|
-
if (b.trim().length >= 3) {
|
|
40
|
-
const idx = flat.indexOf(b);
|
|
41
|
-
if (idx >= 0) return toOriginal(idx + b.length);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
function caretContextAt(root, node, offset) {
|
|
47
|
-
if (!root.contains(node)) return null;
|
|
48
|
-
const doc = root.ownerDocument;
|
|
49
|
-
if (!doc) return null;
|
|
50
|
-
const head = doc.createRange();
|
|
51
|
-
head.selectNodeContents(root);
|
|
52
|
-
head.setEnd(node, offset);
|
|
53
|
-
const tail = doc.createRange();
|
|
54
|
-
tail.selectNodeContents(root);
|
|
55
|
-
tail.setStart(node, offset);
|
|
56
|
-
const before = head.toString();
|
|
57
|
-
const after = tail.toString();
|
|
58
|
-
return { before: before.slice(-CONTEXT_CHARS), after: after.slice(0, CONTEXT_CHARS) };
|
|
59
|
-
}
|
|
60
|
-
function domPointAt(doc, x, y) {
|
|
61
|
-
const withPosition = doc;
|
|
62
|
-
const position = withPosition.caretPositionFromPoint?.(x, y);
|
|
63
|
-
if (position) return { node: position.offsetNode, offset: position.offset };
|
|
64
|
-
const range = withPosition.caretRangeFromPoint?.(x, y);
|
|
65
|
-
if (range) return { node: range.startContainer, offset: range.startOffset };
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
export {
|
|
69
|
-
CONTEXT_CHARS,
|
|
70
|
-
caretContextAt,
|
|
71
|
-
domPointAt,
|
|
72
|
-
locateCaret
|
|
73
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
const PENDING_KEY = /* @__PURE__ */ Symbol.for("ai-matrx.rich-editor.in-place-caret-handoff");
|
|
2
|
-
const pending = globalThis[PENDING_KEY] ??= /* @__PURE__ */ new Map();
|
|
3
|
-
function handInPlaceCaret(key, caret) {
|
|
4
|
-
if (caret) pending.set(key, caret);
|
|
5
|
-
else pending.delete(key);
|
|
6
|
-
}
|
|
7
|
-
function takeInPlaceCaret(key) {
|
|
8
|
-
const caret = pending.get(key) ?? null;
|
|
9
|
-
pending.delete(key);
|
|
10
|
-
return caret;
|
|
11
|
-
}
|
|
12
|
-
export {
|
|
13
|
-
handInPlaceCaret,
|
|
14
|
-
takeInPlaceCaret
|
|
15
|
-
};
|