@ai-matrx/chat 0.7.14 → 0.7.16
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 +12 -0
- package/dist/agents/components/messages-display/AgentConversationDisplay.js +27 -4
- package/dist/agents/components/messages-display/time-groups.d.ts +17 -0
- package/dist/agents/components/messages-display/time-groups.js +29 -0
- package/dist/agents/components/shared/transcript-layout.d.ts +6 -0
- package/dist/agents/components/shared/transcript-layout.js +5 -4
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @ai-matrx/chat
|
|
2
2
|
|
|
3
|
+
## 0.7.16
|
|
4
|
+
|
|
5
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
6
|
+
`git diff npm/chat/v0.7.15..npm/chat/v0.7.16 -- apps/shared/chat`).
|
|
7
|
+
No source changes intended and no consumer action required.
|
|
8
|
+
|
|
9
|
+
## 0.7.15
|
|
10
|
+
|
|
11
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
12
|
+
`git diff npm/chat/v0.7.14..npm/chat/v0.7.15 -- apps/shared/chat`).
|
|
13
|
+
No source changes intended and no consumer action required.
|
|
14
|
+
|
|
3
15
|
## 0.7.14
|
|
4
16
|
### Unreleased
|
|
5
17
|
### An answer never paints header-first (spend register item 32, cold-load CLS at 1280/1440/1920)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useConversationAbilities } from "../../redux/execution-system/host-conversations/conversation-abilities.js";
|
|
4
|
-
import { useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { Fragment, useEffect, useMemo, useRef, useState } from "react";
|
|
5
5
|
import { useAppDispatch, useAppSelector } from "../../../store/hooks.js";
|
|
6
6
|
import { loadConversation } from "../../redux/execution-system/thunks/load-conversation.thunk.js";
|
|
7
7
|
import { StaleDataNotice } from "../../../host/ui-slots.js";
|
|
@@ -48,6 +48,8 @@ import { FollowUpSuggestions } from "./conversation-tools/FollowUpSuggestions.js
|
|
|
48
48
|
import { groupMessageIds, groupsToRender } from "./conversation-tools/pinned-filter.js";
|
|
49
49
|
import { loadFullConversationHistory } from "../../conversation-export/load-full-history.js";
|
|
50
50
|
import { useMessageListInteractions } from "./conversation-tools/useMessageListInteractions.js";
|
|
51
|
+
import { useTranscriptLayout } from "../shared/transcript-layout.js";
|
|
52
|
+
import { timeGroupLabel, timeGroupStarts } from "./time-groups.js";
|
|
51
53
|
import { Button } from "@ai-matrx/design-system/controls";
|
|
52
54
|
function AgentConversationDisplay({
|
|
53
55
|
conversationId,
|
|
@@ -132,6 +134,17 @@ function AgentConversationDisplay({
|
|
|
132
134
|
pinnedOnly,
|
|
133
135
|
pinned: pinnedIds
|
|
134
136
|
});
|
|
137
|
+
const { timestamps } = useTranscriptLayout();
|
|
138
|
+
const groupTimes = useMemo(() => {
|
|
139
|
+
if (timestamps !== "grouped") return null;
|
|
140
|
+
const at = new Map(messages.map((m) => [m.id, Date.parse(m.createdAt)]));
|
|
141
|
+
const timed = visibleGroups.map((g) => {
|
|
142
|
+
const first = groupMessageIds(g).map((id) => at.get(id)).find((t) => t !== void 0 && Number.isFinite(t));
|
|
143
|
+
return { key: g.key, atMs: first ?? null };
|
|
144
|
+
});
|
|
145
|
+
const starts = timeGroupStarts(timed);
|
|
146
|
+
return new Map(timed.filter((t) => starts.has(t.key) && t.atMs !== null).map((t) => [t.key, t.atMs]));
|
|
147
|
+
}, [timestamps, messages, visibleGroups]);
|
|
135
148
|
let latestAssistantKey;
|
|
136
149
|
for (let i = displayGroups.length - 1; i >= 0; i--) {
|
|
137
150
|
if (displayGroups[i].kind === "assistant") {
|
|
@@ -392,9 +405,19 @@ function AgentConversationDisplay({
|
|
|
392
405
|
"aria-label": "Conversation",
|
|
393
406
|
className: `${spacingClass} p-2 scrollbar-hide ${bottomPinned ? "min-h-full flex flex-col justify-end" : ""}`,
|
|
394
407
|
children: [
|
|
395
|
-
visibleGroups.map(
|
|
396
|
-
|
|
397
|
-
|
|
408
|
+
visibleGroups.map((group, index) => {
|
|
409
|
+
const at = groupTimes?.get(group.key);
|
|
410
|
+
if (at === void 0) return wrapGroup(group, index, renderGroupBody(group));
|
|
411
|
+
const label = timeGroupLabel(at);
|
|
412
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
413
|
+
/* @__PURE__ */ jsxs("div", { "data-time-group": "", className: "pt-2 text-center text-[11px] leading-[14px] text-muted-foreground", role: "separator", "aria-label": `${label.day} ${label.time}`, children: [
|
|
414
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold", children: label.day }),
|
|
415
|
+
" ",
|
|
416
|
+
label.time
|
|
417
|
+
] }),
|
|
418
|
+
wrapGroup(group, index, renderGroupBody(group))
|
|
419
|
+
] }, group.key);
|
|
420
|
+
}),
|
|
398
421
|
pinnedOnly && visibleGroups.length === 0 && /* @__PURE__ */ jsxs("div", { className: "py-10 text-center text-sm text-muted-foreground", children: [
|
|
399
422
|
"Nothing pinned in this conversation.",
|
|
400
423
|
" ",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Messages' time grouping (the transcript layout's `timestamps: "grouped"`): a centred line over the
|
|
3
|
+
* first message and over any message that follows an hour's quiet — "Today 9:41 AM",
|
|
4
|
+
* "Yesterday 4:02 PM", "Monday 9:41 AM" within the week, "Oct 6, 2026 at 9:41 AM" before it.
|
|
5
|
+
*/
|
|
6
|
+
/** Quiet this long between two messages starts a new time group (Messages: about an hour). */
|
|
7
|
+
export declare const TIME_GROUP_GAP_MS: number;
|
|
8
|
+
/** The keys of the groups that open a time group: the first, and each after `gapMs` of quiet. */
|
|
9
|
+
export declare function timeGroupStarts(groups: ReadonlyArray<{
|
|
10
|
+
key: string;
|
|
11
|
+
atMs: number | null;
|
|
12
|
+
}>, gapMs?: number): Set<string>;
|
|
13
|
+
/** The line's two parts, Messages' way: the day (drawn bold) and the time. */
|
|
14
|
+
export declare function timeGroupLabel(atMs: number, nowMs?: number, locale?: string): {
|
|
15
|
+
day: string;
|
|
16
|
+
time: string;
|
|
17
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const TIME_GROUP_GAP_MS = 60 * 6e4;
|
|
2
|
+
function timeGroupStarts(groups, gapMs = TIME_GROUP_GAP_MS) {
|
|
3
|
+
const starts = /* @__PURE__ */ new Set();
|
|
4
|
+
let last = null;
|
|
5
|
+
for (const g of groups) {
|
|
6
|
+
if (g.atMs === null) continue;
|
|
7
|
+
if (last === null || g.atMs - last >= gapMs) starts.add(g.key);
|
|
8
|
+
last = g.atMs;
|
|
9
|
+
}
|
|
10
|
+
return starts;
|
|
11
|
+
}
|
|
12
|
+
const startOfDay = (ms) => {
|
|
13
|
+
const d = new Date(ms);
|
|
14
|
+
d.setHours(0, 0, 0, 0);
|
|
15
|
+
return d.getTime();
|
|
16
|
+
};
|
|
17
|
+
function timeGroupLabel(atMs, nowMs = Date.now(), locale) {
|
|
18
|
+
const time = new Date(atMs).toLocaleTimeString(locale, { hour: "numeric", minute: "2-digit" });
|
|
19
|
+
const days = Math.round((startOfDay(nowMs) - startOfDay(atMs)) / 864e5);
|
|
20
|
+
if (days <= 0) return { day: "Today", time };
|
|
21
|
+
if (days === 1) return { day: "Yesterday", time };
|
|
22
|
+
if (days < 7) return { day: new Date(atMs).toLocaleDateString(locale, { weekday: "long" }), time };
|
|
23
|
+
return { day: new Date(atMs).toLocaleDateString(locale, { month: "short", day: "numeric", year: "numeric" }), time: `at ${time}` };
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
TIME_GROUP_GAP_MS,
|
|
27
|
+
timeGroupLabel,
|
|
28
|
+
timeGroupStarts
|
|
29
|
+
};
|
|
@@ -22,6 +22,12 @@ export interface TranscriptLayout {
|
|
|
22
22
|
align?: "center" | "start";
|
|
23
23
|
userMessage?: "card" | "compact" | "bubble";
|
|
24
24
|
answerActions?: "shown" | "latest" | "hover";
|
|
25
|
+
/**
|
|
26
|
+
* When each message was sent: `hover` (the web chat: the time at the end of an answer's action
|
|
27
|
+
* row, shown on hover) or `grouped` (Messages, Xcode's coding assistant: a centred "Today 9:41 AM"
|
|
28
|
+
* line over the first message and over any that follow an hour's quiet).
|
|
29
|
+
*/
|
|
30
|
+
timestamps?: "hover" | "grouped";
|
|
25
31
|
}
|
|
26
32
|
export declare const DEFAULT_TRANSCRIPT_LAYOUT: Required<TranscriptLayout>;
|
|
27
33
|
export declare function resolveTranscriptLayout(layout?: TranscriptLayout): Required<TranscriptLayout>;
|
|
@@ -7,7 +7,8 @@ const DEFAULT_TRANSCRIPT_LAYOUT = {
|
|
|
7
7
|
width: "reading",
|
|
8
8
|
align: "center",
|
|
9
9
|
userMessage: "compact",
|
|
10
|
-
answerActions: "hover"
|
|
10
|
+
answerActions: "hover",
|
|
11
|
+
timestamps: "hover"
|
|
11
12
|
};
|
|
12
13
|
const TranscriptLayoutContext = createContext(DEFAULT_TRANSCRIPT_LAYOUT);
|
|
13
14
|
function resolveTranscriptLayout(layout) {
|
|
@@ -19,10 +20,10 @@ function transcriptColumnClass(layout) {
|
|
|
19
20
|
return ASSISTANT_MESSAGE_COLUMN_CLASS;
|
|
20
21
|
}
|
|
21
22
|
function TranscriptLayoutProvider(props) {
|
|
22
|
-
const { width, align, userMessage, answerActions } = resolveTranscriptLayout(props.layout);
|
|
23
|
+
const { width, align, userMessage, answerActions, timestamps } = resolveTranscriptLayout(props.layout);
|
|
23
24
|
const value = useMemo(
|
|
24
|
-
() => ({ width, align, userMessage, answerActions }),
|
|
25
|
-
[width, align, userMessage, answerActions]
|
|
25
|
+
() => ({ width, align, userMessage, answerActions, timestamps }),
|
|
26
|
+
[width, align, userMessage, answerActions, timestamps]
|
|
26
27
|
);
|
|
27
28
|
return createElement(TranscriptLayoutContext.Provider, { value }, props.children);
|
|
28
29
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/chat",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.16",
|
|
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",
|
|
@@ -4682,15 +4682,16 @@
|
|
|
4682
4682
|
"uuid": "^14.0.2",
|
|
4683
4683
|
"zod": "^4.6.5",
|
|
4684
4684
|
"@ai-matrx/alchemy": "latest",
|
|
4685
|
+
"@ai-matrx/associations": "latest",
|
|
4685
4686
|
"@ai-matrx/browser-audio": "latest",
|
|
4686
4687
|
"@ai-matrx/agents": "latest",
|
|
4688
|
+
"@ai-matrx/canvas": "latest",
|
|
4687
4689
|
"@ai-matrx/content-ir": "latest",
|
|
4688
|
-
"@ai-matrx/content-ir-react": "latest",
|
|
4689
|
-
"@ai-matrx/associations": "latest",
|
|
4690
|
-
"@ai-matrx/data": "latest",
|
|
4691
4690
|
"@ai-matrx/design-system": "latest",
|
|
4692
4691
|
"@ai-matrx/icons": "latest",
|
|
4692
|
+
"@ai-matrx/data": "latest",
|
|
4693
4693
|
"@ai-matrx/kit": "latest",
|
|
4694
|
+
"@ai-matrx/media": "latest",
|
|
4694
4695
|
"@ai-matrx/print": "latest",
|
|
4695
4696
|
"@ai-matrx/realtime": "latest",
|
|
4696
4697
|
"@ai-matrx/records": "latest",
|
|
@@ -4698,8 +4699,7 @@
|
|
|
4698
4699
|
"@ai-matrx/rich-content": "latest",
|
|
4699
4700
|
"@ai-matrx/rich-editor": "latest",
|
|
4700
4701
|
"@ai-matrx/diff": "latest",
|
|
4701
|
-
"@ai-matrx/
|
|
4702
|
-
"@ai-matrx/media": "latest"
|
|
4702
|
+
"@ai-matrx/content-ir-react": "latest"
|
|
4703
4703
|
},
|
|
4704
4704
|
"devDependencies": {
|
|
4705
4705
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
@@ -4818,5 +4818,5 @@
|
|
|
4818
4818
|
"check:side-effects": "node ../scripts/check-side-effects-listed.mjs . && node ../scripts/check-side-effects-listed.mjs --self-test",
|
|
4819
4819
|
"check:public-surface": "node ./scripts/sync-public-surface.mjs --check && node ./scripts/sync-public-surface.mjs --self-test"
|
|
4820
4820
|
},
|
|
4821
|
-
"gitHead": "
|
|
4821
|
+
"gitHead": "6158809f49ab5fe092f5dc6c56c60898eb0f92a6"
|
|
4822
4822
|
}
|