@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.
Files changed (123) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/agents/components/context-items/bodies/ContextInputBody.js +1 -1
  3. package/dist/agents/components/context-items/bodies/GenericBody.js +1 -1
  4. package/dist/agents/components/conversation-actions/conversation-verbs.js +1 -1
  5. package/dist/agents/components/inputs/smart-input/RunSkillPicker.js +1 -1
  6. package/dist/agents/components/live-run/LiveRunDisplay.js +1 -1
  7. package/dist/agents/components/live-run/LiveRunProgress.js +1 -1
  8. package/dist/agents/components/live-run/RunSetDisplay.js +1 -1
  9. package/dist/agents/components/messages-display/assistant/AgentAssistantMessage.js +2 -2
  10. package/dist/agents/components/messages-display/assistant/InPlaceAnswerEditor.js +1 -1
  11. package/dist/agents/components/messages-display/conversation-tools/find-in-conversation.js +1 -1
  12. package/dist/agents/components/messages-display/message-options/buildTaskSeedFromMessage.js +1 -1
  13. package/dist/agents/components/messages-display/user/AgentUserMessage.js +1 -1
  14. package/dist/agents/components/messages-display/user/FirstTurnVariables.js +1 -1
  15. package/dist/agents/components/run/AgentStatusIndicator.js +1 -1
  16. package/dist/agents/components/run/RunJobWorkingLine.js +1 -1
  17. package/dist/agents/components/shared/AgentNewRunButton.js +1 -1
  18. package/dist/agents/components/shared/AgentPlanningIndicator.js +1 -1
  19. package/dist/agents/conversation-export/conversation-transfer.js +1 -1
  20. package/dist/agents/conversation-export/document-markdown.js +1 -1
  21. package/dist/agents/hooks/useConversationRoutePromotion.js +1 -1
  22. package/dist/agents/hooks/useModelControls.js +1 -1
  23. package/dist/agents/redux/agent-definition/converters.js +1 -1
  24. package/dist/agents/redux/agent-definition/slice.js +1 -1
  25. package/dist/agents/redux/conversation-list/conversation-list.thunks.js +1 -1
  26. package/dist/agents/redux/execution-system/active-requests/active-requests.selectors.js +1 -1
  27. package/dist/agents/redux/execution-system/instance-context/sync-canvas-item-context.thunk.js +1 -1
  28. package/dist/agents/redux/execution-system/thunks/process-stream.js +1 -1
  29. package/dist/agents/redux/execution-system/thunks/resolve-base-url.js +17 -0
  30. package/dist/agents/redux/execution-system/thunks/run-ai-stream.d.ts +7 -0
  31. package/dist/agents/redux/execution-system/thunks/run-ai-stream.js +13 -0
  32. package/dist/agents/redux/execution-system/thunks/run-headless-agent-json.js +14 -2
  33. package/dist/agents/redux/execution-system/utils/assemble-cx-content-blocks.js +1 -1
  34. package/dist/agents/redux/execution-system/utils/normalize-content-blocks.js +1 -1
  35. package/dist/agents/redux/execution-system/utils/output-contract-guard.js +1 -1
  36. package/dist/agents/redux/execution-system/utils/stream-block-accumulator.js +8 -8
  37. package/dist/agents/routing/confidential-routing.d.ts +52 -0
  38. package/dist/agents/routing/confidential-routing.js +38 -0
  39. package/dist/agents/utils/canvasItemContext.js +1 -1
  40. package/dist/canvas/workspace/ChatPanelTitleMenu.js +1 -1
  41. package/dist/canvas/workspace/useCanvasWorkspaceConversation.js +1 -1
  42. package/dist/cx-chat/components/SsrSidebarChats.js +1 -1
  43. package/dist/cx-chat/components/core/ChatConversationClient.js +1 -1
  44. package/dist/cx-chat/components/messages/AssistantMessage.js +1 -1
  45. package/dist/cx-chat/hooks/useInstanceBootstrap.js +1 -1
  46. package/dist/cx-conversation/AssistantMessage.js +1 -1
  47. package/dist/host/configure.js +4 -1
  48. package/dist/host/contract.d.ts +14 -1
  49. package/dist/host/index.d.ts +1 -0
  50. package/dist/host/index.js +1 -0
  51. package/dist/mandates/llm-params.d.ts +1 -1
  52. package/dist/mandates/output-contract.js +1 -1
  53. package/dist/mandates/service.d.ts +1 -1
  54. package/dist/mandates/service.js +1 -1
  55. package/dist/mandates/test-run-core.js +1 -1
  56. package/dist/public-chat/components/PublicMessageOptionsMenu.js +1 -1
  57. package/dist/surfaces/services/surface-config.service.js +1 -1
  58. package/dist/tool-call-visualization/components/ToolCallBatch.js +1 -1
  59. package/dist/tool-call-visualization/components/ToolCallVisualization.js +1 -1
  60. package/dist/tool-call-visualization/renderers/agent-call/AgentCallInline.js +1 -1
  61. package/dist/tool-call-visualization/renderers/agent-call/CollabCallCard.js +1 -1
  62. package/dist/tool-call-visualization/renderers/agent-call/collab-subtitle.js +3 -3
  63. package/dist/tool-call-visualization/renderers/skill/SkillInline.js +1 -1
  64. package/dist/tool-call-visualization/result-fields/KeyValueGrid.js +1 -1
  65. package/dist/tool-call-visualization/result-fields/ResultValue.js +1 -1
  66. package/dist/tool-call-visualization/result-fields/shape.js +1 -1
  67. package/dist/tool-call-visualization/utils/cxToolCallToLifecycleEntry.js +2 -2
  68. package/dist/tool-call-visualization/utils/human-copy.js +3 -3
  69. package/dist/ui/mandates-storage/provision-shapes.d.ts +1 -1
  70. package/dist/ui/mandates-storage/provision-shapes.js +1 -1
  71. package/dist/ui/markdown-stream/EnhancedChatMarkdown.js +1 -1
  72. package/dist/ui/treatment-shape.d.ts +1 -1
  73. package/dist/ui/treatment-shape.js +1 -1
  74. package/dist/voice-agent/relay/useVoiceRelaySession.js +1 -1
  75. package/package.json +26 -9
  76. package/dist/agents/hooks/useWarmAgent.d.ts +0 -8
  77. package/dist/agents/hooks/useWarmAgent.js +0 -25
  78. package/dist/agents/utils/artifactId.d.ts +0 -34
  79. package/dist/agents/utils/artifactId.js +0 -15
  80. package/dist/ui/ShimmerText.d.ts +0 -5
  81. package/dist/ui/ShimmerText.js +0 -21
  82. package/dist/ui/addressWithoutNavigating.d.ts +0 -68
  83. package/dist/ui/addressWithoutNavigating.js +0 -60
  84. package/dist/utils/content-ir/decision-answers-ids.d.ts +0 -3
  85. package/dist/utils/content-ir/decision-answers-ids.js +0 -6
  86. package/dist/utils/content-ir/envelope-parity.d.ts +0 -12
  87. package/dist/utils/content-ir/envelope-parity.js +0 -32
  88. package/dist/utils/content-ir/instance-title.d.ts +0 -26
  89. package/dist/utils/content-ir/instance-title.js +0 -39
  90. package/dist/utils/content-ir/kinds/kind-markdown-utils.d.ts +0 -84
  91. package/dist/utils/content-ir/kinds/kind-markdown-utils.js +0 -233
  92. package/dist/utils/content-ir/kinds/kind-value-markdown.d.ts +0 -5
  93. package/dist/utils/content-ir/kinds/kind-value-markdown.js +0 -17
  94. package/dist/utils/content-ir/legacy-quiz-envelope.d.ts +0 -3
  95. package/dist/utils/content-ir/legacy-quiz-envelope.js +0 -45
  96. package/dist/utils/content-ir/surfaces/embedded-kind-json.d.ts +0 -140
  97. package/dist/utils/content-ir/surfaces/embedded-kind-json.js +0 -527
  98. package/dist/utils/content-ir/surfaces/json-kind-signal.d.ts +0 -307
  99. package/dist/utils/content-ir/surfaces/json-kind-signal.js +0 -945
  100. package/dist/utils/content-ir/surfaces/kind-image-alt.d.ts +0 -27
  101. package/dist/utils/content-ir/surfaces/kind-image-alt.js +0 -117
  102. package/dist/utils/content-ir/surfaces/kind-one-line.d.ts +0 -67
  103. package/dist/utils/content-ir/surfaces/kind-one-line.js +0 -209
  104. package/dist/utils/content-ir/surfaces/kind-text-label.d.ts +0 -20
  105. package/dist/utils/content-ir/surfaces/kind-text-label.js +0 -50
  106. package/dist/utils/content-ir/surfaces/kind-text-to-markdown.d.ts +0 -47
  107. package/dist/utils/content-ir/surfaces/kind-text-to-markdown.js +0 -399
  108. package/dist/utils/content-ir/surfaces/markdown-escaped-kind.d.ts +0 -54
  109. package/dist/utils/content-ir/surfaces/markdown-escaped-kind.js +0 -128
  110. package/dist/utils/content-ir/surfaces/quoted-kind-lift.d.ts +0 -61
  111. package/dist/utils/content-ir/surfaces/quoted-kind-lift.js +0 -215
  112. package/dist/utils/inline-decision/decision-options.d.ts +0 -21
  113. package/dist/utils/inline-decision/decision-options.js +0 -32
  114. package/dist/utils/json.d.ts +0 -63
  115. package/dist/utils/json.js +0 -22
  116. package/dist/utils/markdown/plain-title.d.ts +0 -25
  117. package/dist/utils/markdown/plain-title.js +0 -51
  118. package/dist/utils/rendered-find.d.ts +0 -9
  119. package/dist/utils/rendered-find.js +0 -68
  120. package/dist/utils/rich-editor/core/caret-context.d.ts +0 -24
  121. package/dist/utils/rich-editor/core/caret-context.js +0 -73
  122. package/dist/utils/rich-editor/in-place/caret-handoff.d.ts +0 -3
  123. 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
- };
@@ -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;
@@ -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,3 +0,0 @@
1
- import type { CaretContext } from "../core/caret-context.js";
2
- export declare function handInPlaceCaret(key: string, caret: CaretContext | null): void;
3
- export declare function takeInPlaceCaret(key: string): CaretContext | null;
@@ -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
- };