@ai-matrx/chat 0.7.5 → 0.7.6

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.
@@ -3,6 +3,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useCallback, useEffect, useState } from "react";
4
4
  import { X } from "lucide-react";
5
5
  import { downloadFile as saveBlob } from "@ai-matrx/kit/download";
6
+ import { formatFileSize } from "@ai-matrx/kit/format";
6
7
  import { FileIcon, MediaThumbnail } from "@ai-matrx/media/react";
7
8
  import { downloadFile } from "@ai-matrx/media/files/engine/api/files";
8
9
  import { useFile } from "@ai-matrx/media/files/engine/handler/hooks/useFile";
@@ -115,7 +116,7 @@ function useDefaultFileResourceFamily(fileId) {
115
116
  if (state.fileId !== fileId) return { data: null, loading: true, error: null };
116
117
  return { data: state.data, loading: false, error: state.error };
117
118
  }
118
- const formatBytes = (n) => n == null ? null : n < 1024 ? `${n} B` : n < 1024 * 1024 ? `${(n / 1024).toFixed(0)} KB` : `${(n / 1024 / 1024).toFixed(1)} MB`;
119
+ const formatBytes = (n) => n == null ? null : formatFileSize(n);
119
120
  function DefaultFileResourceChip({ fileId, fileHint, nameOverride, badge, onOpen, onRemove, className }) {
120
121
  const { file, status } = useFile(fileId && !isSyntheticId(fileId) ? { kind: "file_id", fileId } : null);
121
122
  const name = nameOverride || file?.meta.fileName || fileHint?.fileName || (status === "resolving" ? "Loading\u2026" : "File");
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { cn } from "@ai-matrx/design-system";
2
+ import { Badge, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, cn } from "@ai-matrx/design-system";
3
3
  import { MarkdownStream } from "../../ui/markdown-stream/MarkdownStream.js";
4
4
  import { formatFileSize } from "@ai-matrx/kit/format";
5
5
  import { Globe } from "lucide-react";
@@ -115,16 +115,7 @@ function formatToMime(format) {
115
115
  if (f === "gif") return "image/gif";
116
116
  return `image/${f}`;
117
117
  }
118
- const Badge = ({ value, className }) => /* @__PURE__ */ jsx(
119
- "span",
120
- {
121
- className: cn(
122
- "inline-flex rounded-full bg-muted px-2 py-0.5 text-[10px] text-muted-foreground",
123
- className
124
- ),
125
- children: String(value ?? "")
126
- }
127
- );
118
+ const BadgeField = ({ value, className }) => /* @__PURE__ */ jsx(Badge, { variant: "neutral", className, children: String(value ?? "") });
128
119
  const Chips = ({ value, className }) => {
129
120
  const items = Array.isArray(value) ? value.map((v) => v == null ? "" : String(v)).filter(Boolean) : value == null ? [] : [String(value)];
130
121
  if (items.length === 0) return null;
@@ -179,7 +170,7 @@ const TabCard = ({ value, className }) => {
179
170
  }
180
171
  );
181
172
  };
182
- const Table = ({ value, className }) => {
173
+ const ExtractTableField = ({ value, className }) => {
183
174
  if (value == null || typeof value !== "object") return null;
184
175
  const obj = value;
185
176
  const columns = Array.isArray(obj.columns) ? obj.columns : [];
@@ -194,37 +185,24 @@ const Table = ({ value, className }) => {
194
185
  });
195
186
  });
196
187
  if (visibleCols.length === 0) return null;
197
- return /* @__PURE__ */ jsxs(
198
- "div",
199
- {
200
- className: cn("overflow-auto rounded-md border border-border/40 bg-background/40", className),
201
- children: [
202
- /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-[11px]", children: [
203
- /* @__PURE__ */ jsx("thead", { className: "bg-muted/40", children: /* @__PURE__ */ jsx("tr", { children: visibleCols.map((c) => /* @__PURE__ */ jsx(
204
- "th",
205
- {
206
- className: "border-b border-border/40 px-2 py-1 text-left font-medium text-muted-foreground",
207
- children: (c.path ?? []).join(" \u203A ")
208
- },
209
- c.index
210
- )) }) }),
211
- /* @__PURE__ */ jsx("tbody", { children: rows.map((r, ri) => /* @__PURE__ */ jsx("tr", { className: "border-t border-border/20 hover:bg-muted/30", children: visibleCols.map((c) => {
212
- const cell = r.cells?.[c.index];
213
- return /* @__PURE__ */ jsx(
214
- "td",
215
- {
216
- className: "max-w-[20rem] truncate px-2 py-1 align-top text-foreground",
217
- title: typeof cell?.value === "string" ? cell.value : void 0,
218
- children: typeof cell?.value === "string" ? cell.value : cell?.value == null ? "" : String(cell.value)
219
- },
220
- c.index
221
- );
222
- }) }, r.index ?? ri)) })
223
- ] }),
224
- obj.truncated === true && /* @__PURE__ */ jsx("div", { className: "border-t border-border/30 bg-muted/30 px-2 py-1 text-[10px] text-muted-foreground", children: "Table was truncated" })
225
- ]
226
- }
227
- );
188
+ return /* @__PURE__ */ jsxs("div", { className: cn("rounded-md border border-border/40", className), children: [
189
+ /* @__PURE__ */ jsxs(Table, { size: "sm", children: [
190
+ /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsx(TableRow, { children: visibleCols.map((c) => /* @__PURE__ */ jsx(TableHead, { children: (c.path ?? []).join(" \u203A ") }, c.index)) }) }),
191
+ /* @__PURE__ */ jsx(TableBody, { children: rows.map((r, ri) => /* @__PURE__ */ jsx(TableRow, { children: visibleCols.map((c) => {
192
+ const cell = r.cells?.[c.index];
193
+ return /* @__PURE__ */ jsx(
194
+ TableCell,
195
+ {
196
+ className: "max-w-[20rem] truncate align-top",
197
+ title: typeof cell?.value === "string" ? cell.value : void 0,
198
+ children: typeof cell?.value === "string" ? cell.value : cell?.value == null ? "" : String(cell.value)
199
+ },
200
+ c.index
201
+ );
202
+ }) }, r.index ?? ri)) })
203
+ ] }),
204
+ obj.truncated === true && /* @__PURE__ */ jsx("div", { className: "border-t border-border/30 bg-muted/30 px-2 py-1 text-[10px] text-muted-foreground", children: "Table was truncated" })
205
+ ] });
228
206
  };
229
207
  const fieldComponents = {
230
208
  BoldLabel,
@@ -234,10 +212,10 @@ const fieldComponents = {
234
212
  Json,
235
213
  Image,
236
214
  Base64Image,
237
- Badge,
215
+ Badge: BadgeField,
238
216
  Chips,
239
217
  TabCard,
240
- Table
218
+ Table: ExtractTableField
241
219
  };
242
220
  export {
243
221
  fieldComponents
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/chat",
3
- "version": "0.7.5",
3
+ "version": "0.7.6",
4
4
  "description": "THE one AI Matrx person-to-agent chat system: the full chat (running agents, conversations, tool calls, streaming, context, surfaces, voice) as explicit per-module subpaths @ai-matrx/chat/<domain>/<module> (no wildcards: scripts/public-modules.json) over a host contract (/host, /host/react) whose only required value is a db. One chat, one renderer: the old kernel entries (., /external-store, /matrx, /react, /tools, styles.css, tokens.css) are retired.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -4666,10 +4666,11 @@
4666
4666
  "server-only": "^0.0.1",
4667
4667
  "uuid": "^14.0.2",
4668
4668
  "zod": "^4.6.5",
4669
- "@ai-matrx/alchemy": "latest",
4669
+ "@ai-matrx/browser-audio": "latest",
4670
+ "@ai-matrx/canvas": "latest",
4670
4671
  "@ai-matrx/associations": "latest",
4671
4672
  "@ai-matrx/content-ir": "latest",
4672
- "@ai-matrx/agents": "latest",
4673
+ "@ai-matrx/content-ir-react": "latest",
4673
4674
  "@ai-matrx/data": "latest",
4674
4675
  "@ai-matrx/design-system": "latest",
4675
4676
  "@ai-matrx/diff": "latest",
@@ -4678,13 +4679,12 @@
4678
4679
  "@ai-matrx/media": "latest",
4679
4680
  "@ai-matrx/print": "latest",
4680
4681
  "@ai-matrx/realtime": "latest",
4682
+ "@ai-matrx/agents": "latest",
4681
4683
  "@ai-matrx/records": "latest",
4682
- "@ai-matrx/records-ui": "latest",
4684
+ "@ai-matrx/rich-content": "latest",
4683
4685
  "@ai-matrx/rich-editor": "latest",
4684
- "@ai-matrx/content-ir-react": "latest",
4685
- "@ai-matrx/canvas": "latest",
4686
- "@ai-matrx/browser-audio": "latest",
4687
- "@ai-matrx/rich-content": "latest"
4686
+ "@ai-matrx/records-ui": "latest",
4687
+ "@ai-matrx/alchemy": "latest"
4688
4688
  },
4689
4689
  "devDependencies": {
4690
4690
  "@arethetypeswrong/cli": "^0.18.5",
@@ -4803,5 +4803,5 @@
4803
4803
  "check:side-effects": "node ../scripts/check-side-effects-listed.mjs . && node ../scripts/check-side-effects-listed.mjs --self-test",
4804
4804
  "check:public-surface": "node ./scripts/sync-public-surface.mjs --check && node ./scripts/sync-public-surface.mjs --self-test"
4805
4805
  },
4806
- "gitHead": "f454ad2b6bb89fd398ef3fb05603ded30ca95dc0"
4806
+ "gitHead": "393a0a08beeb948741a57c8efcbe806d24e64c4a"
4807
4807
  }
@@ -1,50 +0,0 @@
1
- /**
2
- * Content Pre-Filter — Fast-reject layers for splitContentIntoBlocksV2
3
- *
4
- * Designed to run BEFORE any regex, JSON parsing, or XML extraction.
5
- * Returns a set of candidate categories for a given line so the main
6
- * parser can skip entire detection branches.
7
- *
8
- * Layer 0: Single-character scan (nanoseconds)
9
- * Layer 1: Cheap secondary validation (tens of nanoseconds)
10
- *
11
- * If no candidates are found → the line is guaranteed to be plain text.
12
- */
13
- /**
14
- * Bit flags — a line can be a candidate for multiple categories simultaneously.
15
- * Using bits so the main parser can do `if (candidates & Candidate.CODE)`.
16
- */
17
- export declare const Candidate: {
18
- readonly NONE: 0;
19
- readonly TEXT: 0;
20
- readonly CODE: number;
21
- readonly XML_SIMPLE: number;
22
- readonly XML_ATTR: number;
23
- readonly TABLE: number;
24
- readonly IMAGE: number;
25
- readonly VIDEO: number;
26
- readonly DIVIDER: number;
27
- readonly TREE: number;
28
- readonly BARE_JSON: number;
29
- readonly AUDIO: number;
30
- readonly MATRX_FILE: number;
31
- };
32
- export type CandidateFlags = number;
33
- /**
34
- * Scans a line and returns a bitmask of candidate categories.
35
- *
36
- * This scans the FULL line (not just the start) to support inline blocks
37
- * like "Hello <decision prompt="...">". The scan is still cheap because
38
- * we only look at individual characters — no regex, no string splitting.
39
- *
40
- * @param line The raw line (before trimming)
41
- * @param trimmed The normalized+trimmed line (caller already has this)
42
- * @returns Bitmask of Candidate flags
43
- */
44
- export declare function classifyLine(line: string, trimmed: string): CandidateFlags;
45
- export declare function isPlainText(flags: CandidateFlags): boolean;
46
- /**
47
- * Quick check for whether flags include a specific candidate.
48
- * Usage: `if (hasCandidate(flags, Candidate.CODE)) { ... }`
49
- */
50
- export declare function hasCandidate(flags: CandidateFlags, candidate: number): boolean;
@@ -1,243 +0,0 @@
1
- import { OUR_FILE_URL_MARKERS } from "@ai-matrx/media/files/engine";
2
- const Candidate = {
3
- NONE: 0,
4
- TEXT: 0,
5
- // No special block detected — plain text
6
- CODE: 1 << 0,
7
- // ``` or ~~~ fenced code block
8
- XML_SIMPLE: 1 << 1,
9
- // <thinking>, <flashcards>, etc.
10
- XML_ATTR: 1 << 2,
11
- // <decision prompt="...">, <artifact ...>
12
- TABLE: 1 << 3,
13
- // | col | col |
14
- IMAGE: 1 << 4,
15
- // ![alt](url) or [Image URL: ...]
16
- VIDEO: 1 << 5,
17
- // [Video URL: ...]
18
- DIVIDER: 1 << 7,
19
- // *** or # ===
20
- TREE: 1 << 8,
21
- // Box-drawing / ASCII tree characters
22
- BARE_JSON: 1 << 9,
23
- // { "key": ... } JSON object without code fences
24
- AUDIO: 1 << 10,
25
- // [Audio URL: ...] or a standalone …/clip.mp3 link
26
- MATRX_FILE: 1 << 11
27
- // link/URL to one of OUR files (signed S3 / CDN / public / share)
28
- };
29
- const AUDIO_EXT_RE = /\.(mp3|wav|m4a|aac|ogg|oga|opus|flac|weba|webm)(\?[^\s)]*)?$/i;
30
- function isStandaloneAudioLink(trimmed) {
31
- const link = trimmed.match(/^!?\[.*?\]\((https?:\/\/[^\s)]+)\)$/);
32
- if (link) return AUDIO_EXT_RE.test(link[1]);
33
- if (/^https?:\/\/[^\s)]+$/.test(trimmed)) return AUDIO_EXT_RE.test(trimmed);
34
- return false;
35
- }
36
- function mightBeOurFile(trimmed) {
37
- const markers = OUR_FILE_URL_MARKERS;
38
- for (let mi = 0; mi < markers.length; mi++) {
39
- if (trimmed.includes(markers[mi])) return true;
40
- }
41
- return false;
42
- }
43
- const SIMPLE_XML_TAGS = /* @__PURE__ */ new Set([
44
- "thinking",
45
- "think",
46
- "reasoning",
47
- "info",
48
- "task",
49
- "database",
50
- "private",
51
- "plan",
52
- "event",
53
- "tool",
54
- "questionnaire",
55
- "flashcards",
56
- "cooking_recipe",
57
- "timeline",
58
- "progress_tracker",
59
- "troubleshooting",
60
- "resources",
61
- "research"
62
- ]);
63
- const ATTR_XML_TAGS = /* @__PURE__ */ new Set([
64
- "decision",
65
- "artifact",
66
- // Editor pill tags — carry attributes (file, line, severity, language, …)
67
- // so they're classified as ATTR. The chat-markdown renderer parses them
68
- // back into chips on load.
69
- "editor_error",
70
- "editor_code_snippet"
71
- ]);
72
- const ALL_XML_TAGS = /* @__PURE__ */ new Set([...SIMPLE_XML_TAGS, ...ATTR_XML_TAGS]);
73
- const TREE_CHARS = /* @__PURE__ */ new Set([
74
- "\u251C",
75
- "\u2514",
76
- "\u2502",
77
- "\u250C",
78
- "\u2510",
79
- "\u2518",
80
- "\u252C",
81
- "\u2534",
82
- "\u2524",
83
- "\u253C",
84
- "\u2500"
85
- ]);
86
- function classifyLine(line, trimmed) {
87
- let flags = Candidate.NONE;
88
- const len = trimmed.length;
89
- if (len === 0) return flags;
90
- const first = trimmed[0];
91
- if (first === "`" || first === "~") {
92
- if (len >= 3 && trimmed[1] === first && trimmed[2] === first) {
93
- flags |= Candidate.CODE;
94
- }
95
- }
96
- if (first === "{") {
97
- flags |= Candidate.BARE_JSON;
98
- }
99
- if (first === "|") {
100
- if (trimmed.indexOf("|", 1) !== -1) {
101
- flags |= Candidate.TABLE;
102
- }
103
- }
104
- if (first === "!" && len >= 2 && trimmed[1] === "[") {
105
- flags |= Candidate.IMAGE;
106
- }
107
- if (first === "*" || first === "#") {
108
- if (first === "*") {
109
- if (isStarDivider(trimmed)) {
110
- flags |= Candidate.DIVIDER;
111
- }
112
- } else {
113
- if (len >= 4 && trimmed.indexOf("=") !== -1) {
114
- flags |= Candidate.DIVIDER;
115
- }
116
- }
117
- }
118
- if (TREE_CHARS.has(first)) {
119
- flags |= Candidate.TREE;
120
- }
121
- let searchFrom = 0;
122
- while (searchFrom < len) {
123
- const ltPos = trimmed.indexOf("<", searchFrom);
124
- if (ltPos === -1) break;
125
- if (ltPos + 2 < len && trimmed[ltPos + 1] === "<" && trimmed[ltPos + 2] === "<") {
126
- searchFrom = ltPos + 3;
127
- continue;
128
- }
129
- if (ltPos + 1 < len) {
130
- const next = trimmed[ltPos + 1];
131
- if (isLetter(next)) {
132
- const tagName = extractTagName(trimmed, ltPos + 1);
133
- if (tagName) {
134
- if (SIMPLE_XML_TAGS.has(tagName)) {
135
- flags |= Candidate.XML_SIMPLE;
136
- }
137
- if (ATTR_XML_TAGS.has(tagName)) {
138
- flags |= Candidate.XML_ATTR;
139
- }
140
- }
141
- }
142
- }
143
- searchFrom = ltPos + 1;
144
- }
145
- if (!(flags & Candidate.IMAGE) || !(flags & Candidate.VIDEO) || !(flags & Candidate.AUDIO)) {
146
- const bracketIdx = trimmed.indexOf("[");
147
- if (bracketIdx !== -1) {
148
- if (bracketIdx + 10 < len) {
149
- const after = trimmed[bracketIdx + 1];
150
- if (after === "I" && trimmed.substring(bracketIdx + 1, bracketIdx + 11) === "Image URL:") {
151
- flags |= Candidate.IMAGE;
152
- } else if (after === "V" && trimmed.substring(bracketIdx + 1, bracketIdx + 11) === "Video URL:") {
153
- flags |= Candidate.VIDEO;
154
- } else if (after === "A" && trimmed.substring(bracketIdx + 1, bracketIdx + 11) === "Audio URL:") {
155
- flags |= Candidate.AUDIO;
156
- }
157
- }
158
- }
159
- }
160
- if (!(flags & Candidate.AUDIO) && (first === "[" || first === "!" || first === "h") && isStandaloneAudioLink(trimmed)) {
161
- flags |= Candidate.AUDIO;
162
- }
163
- if (!(flags & Candidate.TABLE) && first !== "|") {
164
- if (trimmed.indexOf("|") !== -1) {
165
- const firstPipe = trimmed.indexOf("|");
166
- if (firstPipe !== -1 && trimmed.indexOf("|", firstPipe + 1) !== -1) {
167
- flags |= Candidate.TABLE;
168
- }
169
- }
170
- }
171
- if (!(flags & Candidate.TREE)) {
172
- for (let ci = 1; ci < len; ci++) {
173
- if (TREE_CHARS.has(trimmed[ci])) {
174
- flags |= Candidate.TREE;
175
- break;
176
- }
177
- }
178
- }
179
- if (!(flags & Candidate.TREE) && (first === " " || first === " " || first === "\u2502" || first === "|" || first === "+")) {
180
- if (looksLikeAsciiTree(trimmed)) {
181
- flags |= Candidate.TREE;
182
- }
183
- }
184
- if (!(flags & Candidate.MATRX_FILE) && mightBeOurFile(trimmed)) {
185
- flags |= Candidate.MATRX_FILE;
186
- }
187
- return flags;
188
- }
189
- function isLetter(ch) {
190
- const c = ch.charCodeAt(0);
191
- return c >= 65 && c <= 90 || c >= 97 && c <= 122;
192
- }
193
- function extractTagName(str, start) {
194
- let end = start;
195
- while (end < str.length) {
196
- const c = str.charCodeAt(end);
197
- if (c >= 97 && c <= 122 || c >= 65 && c <= 90 || c >= 48 && c <= 57 || c === 95) {
198
- end++;
199
- } else {
200
- break;
201
- }
202
- }
203
- if (end === start) return null;
204
- return str.substring(start, end).toLowerCase();
205
- }
206
- function isStarDivider(trimmed) {
207
- let starCount = 0;
208
- for (let i = 0; i < trimmed.length; i++) {
209
- const ch = trimmed[i];
210
- if (ch === "*") {
211
- starCount++;
212
- } else if (ch !== " ") {
213
- return false;
214
- }
215
- }
216
- return starCount >= 3;
217
- }
218
- function looksLikeAsciiTree(trimmed) {
219
- let i = 0;
220
- while (i < trimmed.length) {
221
- const ch2 = trimmed[i];
222
- if (ch2 === " " || ch2 === " " || ch2 === "\u2502" || ch2 === "|") {
223
- i++;
224
- } else {
225
- break;
226
- }
227
- }
228
- if (i >= trimmed.length) return false;
229
- const ch = trimmed[i];
230
- return ch === "\u251C" || ch === "\u2514" || ch === "+";
231
- }
232
- function isPlainText(flags) {
233
- return flags === Candidate.NONE;
234
- }
235
- function hasCandidate(flags, candidate) {
236
- return (flags & candidate) !== 0;
237
- }
238
- export {
239
- Candidate,
240
- classifyLine,
241
- hasCandidate,
242
- isPlainText
243
- };