@ai-matrx/chat 0.4.15 → 0.4.18
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 +43 -0
- package/dist/agent-copy/AlchemySurfaceBridge.js +1 -1
- package/dist/agents/components/agent-widgets/chat-assistant/AssistantCardStack.js +1 -1
- package/dist/agents/components/ambient-assistant/ScrollAssistantLauncher.js +1 -1
- package/dist/agents/components/conversation-actions/rename/ConversationRenameDialogHost.js +1 -1
- package/dist/agents/components/messages-display/assistant/AgentEmptyMessageDisplay.js +1 -1
- package/dist/agents/components/messages-display/assistant/AssistantMessageFooter.js +3 -1
- package/dist/agents/components/messages-display/user/AgentUserMessage.js +95 -85
- package/dist/agents/components/messages-display/user/useCollapsibleMessageText.d.ts +1 -1
- package/dist/agents/components/messages-display/user/useCollapsibleMessageText.js +5 -4
- package/dist/agents/components/shared/AgentConversationColumn.d.ts +7 -1
- package/dist/agents/components/shared/AgentConversationColumn.js +14 -7
- package/dist/agents/components/shared/transcript-layout.d.ts +31 -0
- package/dist/agents/components/shared/transcript-layout.js +38 -0
- package/dist/agents/redux/execution-system/message-crud/answer-text-splice.js +1 -1
- package/dist/agents/redux/execution-system/messages/messages.selectors.js +1 -1
- package/dist/canvas/workspace/ChatPanelTitleMenu.js +8 -2
- package/dist/canvas/workspace/ShellChatDock.js +60 -10
- package/dist/canvas/workspace/shell-chat-route.d.ts +5 -4
- package/dist/canvas/workspace/shell-chat-route.js +2 -4
- package/dist/canvas/workspace/side-chat-address.d.ts +91 -0
- package/dist/canvas/workspace/side-chat-address.js +110 -0
- package/dist/canvas/workspace/useCanvasWorkspaceConversation.d.ts +31 -12
- package/dist/canvas/workspace/useCanvasWorkspaceConversation.js +105 -36
- package/dist/cx-chat/components/ChatMobileAgentName.js +1 -1
- package/dist/cx-chat/components/ChatSidebarClient.js +1 -1
- package/dist/cx-chat/components/ChatWelcomeClient.js +1 -1
- package/dist/cx-chat/components/core/ChatConversationClient.js +1 -1
- package/dist/cx-chat/components/user-input/ConversationInput.js +1 -1
- package/dist/cx-conversation/ConversationInput.js +1 -1
- package/dist/cx-conversation/MessageList.js +1 -1
- package/dist/host/db-types.d.ts +9 -12
- package/dist/host/ui-slots.js +17 -2
- package/dist/surfaces/components/chrome/SurfaceAgentsHeaderButton.d.ts +1 -1
- package/dist/surfaces/components/chrome/SurfaceAgentsHeaderButton.js +1 -1
- package/dist/tool-call-visualization/db-renderer/DbToolRenderer.js +1 -1
- package/dist/{next → ui}/lazy/AgentAssistantMessage.js +3 -4
- package/dist/{next → ui}/lazy/AgentPickerSheet.js +3 -4
- package/dist/{next → ui}/lazy/ChatVariableInputs.js +5 -7
- package/dist/{next → ui}/lazy/ConversationRenameDialogHostImpl.d.ts +1 -1
- package/dist/{next → ui}/lazy/ConversationRenameDialogHostImpl.js +3 -3
- package/dist/{next → ui}/lazy/CxAssistantMessage.js +3 -4
- package/dist/{next → ui}/lazy/DbToolRendererImpl.js +3 -3
- package/dist/{next → ui}/lazy/IconResolver.js +3 -4
- package/dist/{next → ui}/lazy/PublicChatVariableInputs.js +5 -7
- package/dist/{next → ui}/lazy/ScrollAssistantLaunchers.d.ts +1 -1
- package/dist/{next → ui}/lazy/ScrollAssistantLaunchers.js +3 -5
- package/dist/{next → ui}/lazy/SurfaceAgentsPanelImpl.js +2 -3
- package/dist/utils/client-lazy.d.ts +16 -0
- package/dist/utils/client-lazy.js +27 -0
- package/package.json +7 -7
- /package/dist/{next → ui}/lazy/AgentAssistantMessage.d.ts +0 -0
- /package/dist/{next → ui}/lazy/AgentPickerSheet.d.ts +0 -0
- /package/dist/{next → ui}/lazy/ChatVariableInputs.d.ts +0 -0
- /package/dist/{next → ui}/lazy/CxAssistantMessage.d.ts +0 -0
- /package/dist/{next → ui}/lazy/DbToolRendererImpl.d.ts +0 -0
- /package/dist/{next → ui}/lazy/IconResolver.d.ts +0 -0
- /package/dist/{next → ui}/lazy/PublicChatVariableInputs.d.ts +0 -0
- /package/dist/{next → ui}/lazy/SurfaceAgentsPanelImpl.d.ts +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# @ai-matrx/chat
|
|
2
2
|
|
|
3
|
+
## 0.4.18 — Side Chat: an unsent chat's menu, and a taken id heals itself
|
|
4
|
+
|
|
5
|
+
- The chat panel's title menu (`ChatPanelTitleMenu`) shows Rename and Open in full chat only once
|
|
6
|
+
the conversation exists on the server (not cache-only, or it has a conversation-list row). An
|
|
7
|
+
unsent New chat shows New chat and the conversation list only.
|
|
8
|
+
- A reserved new-chat id is released (and the page's memory takes over) the moment the first send
|
|
9
|
+
STARTS, not when the server confirms — a kept id could be sent again from a second tab or after a
|
|
10
|
+
reload, and the server answered 409 `conversation_already_exists` forever.
|
|
11
|
+
- Self-heal: when a send is refused with 409 `conversation_already_exists`, the panel starts the
|
|
12
|
+
same chat (same agent / job) again under a FRESH id and puts what the person sent back into its
|
|
13
|
+
composer. `isConversationIdTaken` names the refusal.
|
|
14
|
+
|
|
15
|
+
Consumer action: none.
|
|
16
|
+
|
|
17
|
+
## 0.4.17 — a lighter first chat screen
|
|
18
|
+
|
|
19
|
+
- The message selectors and the answer-text splice strip thinking with THE rule
|
|
20
|
+
(`@ai-matrx/content-ir/source` `stripThinking`) instead of importing `@ai-matrx/print/markdown`
|
|
21
|
+
(KaTeX + an HTML parser on the first screen). The chat room marks its surface through
|
|
22
|
+
`@ai-matrx/alchemy/react/surface` (not the whole workspace). With `@ai-matrx/icons` 0.3.4 the
|
|
23
|
+
bare Vite canary's first chat screen drops from 2,429 kB to the number in
|
|
24
|
+
`side-panel-bundle-budget.json` (gzip); nothing a person can do changed.
|
|
25
|
+
|
|
26
|
+
## 0.4.16 — the Side Chat's URL contract
|
|
27
|
+
|
|
28
|
+
- ONE param family on every home opens, closes and aims the Side Chat (the shell chat dock):
|
|
29
|
+
`side_chat` = `open` | `closed` | `new` | `<conversation uuid>`; `side_chat_agent` =
|
|
30
|
+
`<agent uuid>` | `mandate:<key>`; `side_chat_mode` = `chat` | `work` | `advanced`. The link wins
|
|
31
|
+
over the remembered conversation, the family cookie and the defaults for its visit, and never
|
|
32
|
+
writes the person's open / closed choice. Invalid values are ignored with a `[side-chat]` warning.
|
|
33
|
+
- After reading, the one-shot params are consumed; the address names the shown conversation as
|
|
34
|
+
`side_chat=<uuid>` while the panel is open, and drops it when the person closes the panel.
|
|
35
|
+
- `/chat` and `/code` show the Side Chat when (and only when) a `side_chat*` param asks.
|
|
36
|
+
- New module `canvas/workspace/side-chat-address` (`readSideChatLink`, `sideChatOpen`,
|
|
37
|
+
`addressAfterSideChatLink`, `sideChatAddress`, `sideChatSignature`, the param names).
|
|
38
|
+
- Removed (no legacy): `?pageChat=` and the boards / Education `?chat=`, `ShellChatHome.addressParam`,
|
|
39
|
+
`conversationInAddress`, `addressWithConversation`; `useCanvasWorkspaceConversation`'s
|
|
40
|
+
`addressParam` option is now `address: { link, open, hostedElsewhere, onWrite? }`.
|
|
41
|
+
|
|
42
|
+
Consumer action: any link that opened the dock with `?pageChat=<id>` or a board's / Education's
|
|
43
|
+
`?chat=<id>` now writes `?side_chat=<id>` (matrx-frontend: `continueWithAgentHref`). A host calling
|
|
44
|
+
`useCanvasWorkspaceConversation` with `addressParam` passes `address` instead.
|
|
45
|
+
|
|
3
46
|
## 0.4.15
|
|
4
47
|
|
|
5
48
|
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
@@ -4,7 +4,7 @@ import { useEffect, useId, useRef } from "react";
|
|
|
4
4
|
import {
|
|
5
5
|
createSurfaceTransferHandle
|
|
6
6
|
} from "@ai-matrx/alchemy/operate";
|
|
7
|
-
import { ContentTransferSurfaceProvider } from "@ai-matrx/alchemy/react/
|
|
7
|
+
import { ContentTransferSurfaceProvider } from "@ai-matrx/alchemy/react/surface";
|
|
8
8
|
import { getSurfaceManifest } from "../host/ui-slots.js";
|
|
9
9
|
function useAlchemySurfaceHandle(surfaceName, getScope) {
|
|
10
10
|
const instanceId = useId();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useRef, useMemo } from "react";
|
|
4
|
-
import { AgentAssistantMessage } from "../../../../
|
|
4
|
+
import { AgentAssistantMessage } from "../../../../ui/lazy/AgentAssistantMessage.js";
|
|
5
5
|
import { useAppSelector, useAppDispatch } from "../../../../store/hooks.js";
|
|
6
6
|
import { selectConversationMessages } from "../../../redux/execution-system/messages/messages.selectors.js";
|
|
7
7
|
import {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { ScrollAssistantLauncherImpl, ScrollVoiceAssistantLauncherImpl } from "../../../
|
|
3
|
+
import { ScrollAssistantLauncherImpl, ScrollVoiceAssistantLauncherImpl } from "../../../ui/lazy/ScrollAssistantLaunchers.js";
|
|
4
4
|
import { usePathname } from "../../../host/navigation.js";
|
|
5
5
|
import { useEffect, useState } from "react";
|
|
6
6
|
import { useIsMobile } from "@ai-matrx/kit/media-query";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { ConversationRenameDialogHostImpl } from "../../../../
|
|
3
|
+
import { ConversationRenameDialogHostImpl } from "../../../../ui/lazy/ConversationRenameDialogHostImpl.js";
|
|
4
4
|
function ConversationRenameDialogHost() {
|
|
5
5
|
return /* @__PURE__ */ jsx(ConversationRenameDialogHostImpl, {});
|
|
6
6
|
}
|
|
@@ -3,7 +3,7 @@ import { useAppSelector } from "../../../../store/hooks.js";
|
|
|
3
3
|
import { selectInstanceAgentName } from "../../../redux/execution-system/instance-ui-state/instance-ui-state.selectors.js";
|
|
4
4
|
import { selectInstanceAgentDescription } from "../../../redux/execution-system/instance-ui-state/instance-ui-state.selectors.js";
|
|
5
5
|
import { Webhook } from "lucide-react";
|
|
6
|
-
import { IconResolver } from "../../../../
|
|
6
|
+
import { IconResolver } from "../../../../ui/lazy/IconResolver.js";
|
|
7
7
|
import { selectVisibleInputDefinitions } from "../../../redux/execution-system/instance-variable-values/bound-variable.selectors.js";
|
|
8
8
|
import { selectIsVariableFormShown } from "../../../redux/execution-system/selectors/aggregate.selectors.js";
|
|
9
9
|
import { emptyStateInstruction } from "./empty-state-instruction.js";
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
import { buildConversationMessageTitle } from "../../../utils/conversation-message-title.js";
|
|
32
32
|
import { resolveAssistantEditTarget } from "../message-options/resolveAssistantEditTarget.js";
|
|
33
33
|
import { MessageTimestamp } from "../MessageTimestamp.js";
|
|
34
|
+
import { useTranscriptLayout } from "../../shared/transcript-layout.js";
|
|
34
35
|
function useAssistantMessageActions({
|
|
35
36
|
messageId,
|
|
36
37
|
conversationId,
|
|
@@ -162,7 +163,8 @@ function AssistantMessageFooter(props) {
|
|
|
162
163
|
showOptions,
|
|
163
164
|
isLatestAssistant
|
|
164
165
|
} = useAssistantMessageActions(props);
|
|
165
|
-
const
|
|
166
|
+
const answerActions = useTranscriptLayout().answerActions;
|
|
167
|
+
const isHoverOnly = answerActions === "hover" || density === "compact" && !isLatestAssistant;
|
|
166
168
|
const rowRef = useRef(null);
|
|
167
169
|
useEffect(() => {
|
|
168
170
|
const row = rowRef.current;
|
|
@@ -30,6 +30,8 @@ import {
|
|
|
30
30
|
} from "../../../redux/execution-system/messages/message-context-receipt.js";
|
|
31
31
|
import { useMachineFramesVisible } from "../../shared/transcript-audience.js";
|
|
32
32
|
import { useCollapsibleMessageText } from "./useCollapsibleMessageText.js";
|
|
33
|
+
import { useTranscriptLayout } from "../../shared/transcript-layout.js";
|
|
34
|
+
const COMPACT_COLLAPSE_THRESHOLD_PX = 192;
|
|
33
35
|
import {
|
|
34
36
|
selectHostSubmittedFirstTurnValues,
|
|
35
37
|
selectOwnSubmittedFirstTurnValues
|
|
@@ -197,7 +199,8 @@ function AgentUserMessage({
|
|
|
197
199
|
bodyBlocks,
|
|
198
200
|
trimmedText
|
|
199
201
|
]);
|
|
200
|
-
const
|
|
202
|
+
const compactUserMessage = useTranscriptLayout().userMessage === "compact";
|
|
203
|
+
const { isCollapsed, setIsCollapsed, shouldBeCollapsible, measureRef } = useCollapsibleMessageText(collapseSignature, compactUserMessage ? COMPACT_COLLAPSE_THRESHOLD_PX : void 0);
|
|
201
204
|
const authoredByHost = metadata?.authored_by === "host";
|
|
202
205
|
const isSuperAdmin = useAppSelector(selectIsSuperAdmin);
|
|
203
206
|
useEffect(() => {
|
|
@@ -281,95 +284,102 @@ function AgentUserMessage({
|
|
|
281
284
|
}, title: "Collapse", "aria-label": "Collapse" })
|
|
282
285
|
}
|
|
283
286
|
),
|
|
284
|
-
/* @__PURE__ */ jsx(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
),
|
|
323
|
-
/* @__PURE__ */ jsx(
|
|
324
|
-
EditInPlace,
|
|
325
|
-
{
|
|
326
|
-
value: content,
|
|
327
|
-
canEdit: canEditText,
|
|
328
|
-
editing: editingText,
|
|
329
|
-
onEditingChange: setEditingText,
|
|
330
|
-
write: (text) => routeUserEditAction(dispatch, { actionId: "save", conversationId, messageId, newContent: text, surfaceKey }),
|
|
331
|
-
actions: USER_EDIT_ACTIONS.filter((a) => a.id !== "save").map((a) => ({
|
|
332
|
-
id: a.id,
|
|
333
|
-
label: a.label,
|
|
334
|
-
run: (text) => routeUserEditAction(dispatch, { actionId: a.id, conversationId, messageId, newContent: text, surfaceKey })
|
|
335
|
-
})),
|
|
336
|
-
discardDescription: "Your message stays exactly as it was sent; what you typed here is dropped.",
|
|
337
|
-
editor: {
|
|
338
|
-
imagePolicy: "other",
|
|
339
|
-
surfaceName: "matrx-user/chat",
|
|
340
|
-
sourceFeature: "chat",
|
|
341
|
-
contentSource: { type: "chat-message", conversationId, messageId }
|
|
342
|
-
},
|
|
343
|
-
children: /* @__PURE__ */ jsx(
|
|
344
|
-
AgentUserMessageContent,
|
|
287
|
+
/* @__PURE__ */ jsx(
|
|
288
|
+
"div",
|
|
289
|
+
{
|
|
290
|
+
className: compactUserMessage ? "bg-muted rounded-md px-3 py-1.5" : "bg-muted border border-border rounded-lg px-2 py-2",
|
|
291
|
+
"data-user-message-style": compactUserMessage ? "compact" : "card",
|
|
292
|
+
children: /* @__PURE__ */ jsxs("div", { className: "relative min-w-0", children: [
|
|
293
|
+
/* @__PURE__ */ jsxs(
|
|
294
|
+
"div",
|
|
295
|
+
{
|
|
296
|
+
ref: measureRef,
|
|
297
|
+
className: cn(
|
|
298
|
+
"space-y-1.5 overflow-hidden transition-all duration-300",
|
|
299
|
+
// Clamp leaves room below the fade: the first lines (what was
|
|
300
|
+
// sent, the context strip) read at full contrast, and the
|
|
301
|
+
// expand chevron never sits on top of text.
|
|
302
|
+
shouldBeCollapsible && isCollapsed && !editingText && "max-h-24 pb-7"
|
|
303
|
+
),
|
|
304
|
+
children: [
|
|
305
|
+
machineFramesVisible && isFirstTurnMessage && /* @__PURE__ */ jsx(FirstTurnVariables, { conversationId }),
|
|
306
|
+
showLaunchInputs && /* @__PURE__ */ jsx(
|
|
307
|
+
FirstTurnLaunchInputs,
|
|
308
|
+
{
|
|
309
|
+
conversationId,
|
|
310
|
+
onOpenChange: (open) => {
|
|
311
|
+
if (open) setIsCollapsed(false);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
),
|
|
315
|
+
!database ? null : machineFramesVisible && contextReceipt && (contextReceipt.rows?.length ?? 0) > 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-1.5", children: /* @__PURE__ */ jsx(
|
|
316
|
+
MessageContextReceipt,
|
|
317
|
+
{
|
|
318
|
+
conversationId,
|
|
319
|
+
messageId,
|
|
320
|
+
receipt: contextReceipt,
|
|
321
|
+
mismatches: contextMismatches
|
|
322
|
+
}
|
|
323
|
+
) }) : machineFramesVisible && contextSnapshot && contextSnapshot.length > 0 && /* @__PURE__ */ jsx(
|
|
324
|
+
ContextPolicyChipStrip,
|
|
345
325
|
{
|
|
346
326
|
conversationId,
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
327
|
+
agentId,
|
|
328
|
+
entries: contextSnapshot
|
|
329
|
+
}
|
|
330
|
+
),
|
|
331
|
+
/* @__PURE__ */ jsx(
|
|
332
|
+
EditInPlace,
|
|
333
|
+
{
|
|
334
|
+
value: content,
|
|
335
|
+
canEdit: canEditText,
|
|
336
|
+
editing: editingText,
|
|
337
|
+
onEditingChange: setEditingText,
|
|
338
|
+
write: (text) => routeUserEditAction(dispatch, { actionId: "save", conversationId, messageId, newContent: text, surfaceKey }),
|
|
339
|
+
actions: USER_EDIT_ACTIONS.filter((a) => a.id !== "save").map((a) => ({
|
|
340
|
+
id: a.id,
|
|
341
|
+
label: a.label,
|
|
342
|
+
run: (text) => routeUserEditAction(dispatch, { actionId: a.id, conversationId, messageId, newContent: text, surfaceKey })
|
|
343
|
+
})),
|
|
344
|
+
discardDescription: "Your message stays exactly as it was sent; what you typed here is dropped.",
|
|
345
|
+
editor: {
|
|
346
|
+
imagePolicy: "other",
|
|
347
|
+
surfaceName: "matrx-user/chat",
|
|
348
|
+
sourceFeature: "chat",
|
|
349
|
+
contentSource: { type: "chat-message", conversationId, messageId }
|
|
350
|
+
},
|
|
351
|
+
children: /* @__PURE__ */ jsx(
|
|
352
|
+
AgentUserMessageContent,
|
|
353
|
+
{
|
|
354
|
+
conversationId,
|
|
355
|
+
text: trimmedText,
|
|
356
|
+
attachmentParts,
|
|
357
|
+
bodyBlocks
|
|
358
|
+
}
|
|
359
|
+
)
|
|
350
360
|
}
|
|
351
361
|
)
|
|
362
|
+
]
|
|
363
|
+
}
|
|
364
|
+
),
|
|
365
|
+
shouldBeCollapsible && isCollapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
366
|
+
/* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 h-10 bg-gradient-to-t from-muted via-muted/80 to-transparent pointer-events-none" }),
|
|
367
|
+
/* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 flex justify-center pb-1", children: /* @__PURE__ */ jsx(
|
|
368
|
+
Button,
|
|
369
|
+
{
|
|
370
|
+
variant: "quiet",
|
|
371
|
+
onClick: (e) => {
|
|
372
|
+
e.stopPropagation();
|
|
373
|
+
setIsCollapsed(false);
|
|
374
|
+
},
|
|
375
|
+
title: "Expand message",
|
|
376
|
+
children: /* @__PURE__ */ jsx(ChevronDown, { className: "w-4 h-4" })
|
|
352
377
|
}
|
|
353
|
-
)
|
|
354
|
-
]
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
/* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 h-10 bg-gradient-to-t from-muted via-muted/80 to-transparent pointer-events-none" }),
|
|
359
|
-
/* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 flex justify-center pb-1", children: /* @__PURE__ */ jsx(
|
|
360
|
-
Button,
|
|
361
|
-
{
|
|
362
|
-
variant: "quiet",
|
|
363
|
-
onClick: (e) => {
|
|
364
|
-
e.stopPropagation();
|
|
365
|
-
setIsCollapsed(false);
|
|
366
|
-
},
|
|
367
|
-
title: "Expand message",
|
|
368
|
-
children: /* @__PURE__ */ jsx(ChevronDown, { className: "w-4 h-4" })
|
|
369
|
-
}
|
|
370
|
-
) })
|
|
371
|
-
] })
|
|
372
|
-
] }) }),
|
|
378
|
+
) })
|
|
379
|
+
] })
|
|
380
|
+
] })
|
|
381
|
+
}
|
|
382
|
+
),
|
|
373
383
|
/* @__PURE__ */ jsx(
|
|
374
384
|
"div",
|
|
375
385
|
{
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* physically clicks expand. A late remeasure re-collapses unless the user has
|
|
24
24
|
* explicitly toggled.
|
|
25
25
|
*/
|
|
26
|
-
export declare function useCollapsibleMessageText(contentKey: string): {
|
|
26
|
+
export declare function useCollapsibleMessageText(contentKey: string, thresholdPx?: number): {
|
|
27
27
|
isCollapsed: boolean;
|
|
28
28
|
setIsCollapsed: (value: boolean | ((prev: boolean) => boolean)) => void;
|
|
29
29
|
shouldBeCollapsible: boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useLayoutEffect, useRef, useState } from "react";
|
|
2
2
|
const COLLAPSE_THRESHOLD_PX = 48;
|
|
3
|
-
function useCollapsibleMessageText(contentKey) {
|
|
3
|
+
function useCollapsibleMessageText(contentKey, thresholdPx = COLLAPSE_THRESHOLD_PX) {
|
|
4
4
|
const [isCollapsed, setIsCollapsedState] = useState(true);
|
|
5
5
|
const [shouldBeCollapsible, setShouldBeCollapsible] = useState(false);
|
|
6
6
|
const measureRef = useRef(null);
|
|
@@ -10,8 +10,9 @@ function useCollapsibleMessageText(contentKey) {
|
|
|
10
10
|
const node = measureRef.current;
|
|
11
11
|
if (!node) return void 0;
|
|
12
12
|
const measure = () => {
|
|
13
|
-
const
|
|
14
|
-
const
|
|
13
|
+
const padding = typeof getComputedStyle === "function" ? parseFloat(getComputedStyle(node).paddingBottom) || 0 : 0;
|
|
14
|
+
const contentHeight = node.scrollHeight - padding;
|
|
15
|
+
const isLong = contentHeight > thresholdPx;
|
|
15
16
|
const contentChanged = previousContentRef.current !== contentKey;
|
|
16
17
|
setShouldBeCollapsible(isLong);
|
|
17
18
|
if (contentChanged) {
|
|
@@ -31,7 +32,7 @@ function useCollapsibleMessageText(contentKey) {
|
|
|
31
32
|
const observer = new ResizeObserver(measure);
|
|
32
33
|
observer.observe(node);
|
|
33
34
|
return () => observer.disconnect();
|
|
34
|
-
}, [contentKey]);
|
|
35
|
+
}, [contentKey, thresholdPx]);
|
|
35
36
|
const setIsCollapsed = useCallback(
|
|
36
37
|
(value) => {
|
|
37
38
|
userToggledRef.current = true;
|
|
@@ -2,6 +2,7 @@ import type { ComposerPresentation } from "../inputs/smart-input/composer/compos
|
|
|
2
2
|
import type { AttachedContextRailItem } from "../inputs/smart-input/ConversationContextRail.js";
|
|
3
3
|
import type { VariablesPanelStyle } from "../inputs/variable-input-variations/variable-input-options.js";
|
|
4
4
|
import { type TranscriptAudience } from "./transcript-audience.js";
|
|
5
|
+
import { type TranscriptLayout } from "./transcript-layout.js";
|
|
5
6
|
interface SmartInputForwardProps {
|
|
6
7
|
/** See `SmartAgentInput`'s `draftAlias`: the per-record unsent-draft key. */
|
|
7
8
|
draftAlias?: string;
|
|
@@ -114,6 +115,11 @@ interface AgentConversationColumnBaseProps {
|
|
|
114
115
|
* ./transcript-audience.tsx.
|
|
115
116
|
*/
|
|
116
117
|
audience?: TranscriptAudience;
|
|
118
|
+
/**
|
|
119
|
+
* The host's layout knobs (width, alignment, the person's message style). Omitted = today's
|
|
120
|
+
* web chat exactly. See ./transcript-layout.tsx.
|
|
121
|
+
*/
|
|
122
|
+
layout?: TranscriptLayout;
|
|
117
123
|
}
|
|
118
|
-
export declare function AgentConversationColumn({ conversationId, displayConversationId, surfaceKey, constrainWidth, edgeToEdgeScroll, smartInputProps, landingContent, hideInput, hideCreatorPanel, afterMessages, aboveInput, deferColdMarkdown, audience, }: AgentConversationColumnProps): import("react").JSX.Element;
|
|
124
|
+
export declare function AgentConversationColumn({ conversationId, displayConversationId, surfaceKey, constrainWidth, edgeToEdgeScroll, smartInputProps, landingContent, hideInput, hideCreatorPanel, afterMessages, aboveInput, deferColdMarkdown, audience, layout, }: AgentConversationColumnProps): import("react").JSX.Element;
|
|
119
125
|
export {};
|
|
@@ -32,9 +32,13 @@ import {
|
|
|
32
32
|
} from "./transcript-audience.js";
|
|
33
33
|
import { cn } from "@ai-matrx/design-system";
|
|
34
34
|
import {
|
|
35
|
-
ASSISTANT_MESSAGE_COLUMN_CLASS,
|
|
36
35
|
ASSISTANT_MESSAGE_COLUMN_INSET_CLASS
|
|
37
36
|
} from "./assistant-message-layout.js";
|
|
37
|
+
import {
|
|
38
|
+
TranscriptLayoutProvider,
|
|
39
|
+
resolveTranscriptLayout,
|
|
40
|
+
transcriptColumnClass
|
|
41
|
+
} from "./transcript-layout.js";
|
|
38
42
|
import {
|
|
39
43
|
isWarRoomThreadAgentSurface,
|
|
40
44
|
traceWarRoomRenderPath
|
|
@@ -58,8 +62,11 @@ function AgentConversationColumn({
|
|
|
58
62
|
afterMessages,
|
|
59
63
|
aboveInput,
|
|
60
64
|
deferColdMarkdown = false,
|
|
61
|
-
audience = "builder"
|
|
65
|
+
audience = "builder",
|
|
66
|
+
layout
|
|
62
67
|
}) {
|
|
68
|
+
const resolvedLayout = resolveTranscriptLayout(layout);
|
|
69
|
+
const columnClass = transcriptColumnClass(resolvedLayout);
|
|
63
70
|
const dispatch = useAppDispatch();
|
|
64
71
|
const displayId = displayConversationId ?? conversationId;
|
|
65
72
|
const scrollRef = useRef(null);
|
|
@@ -202,8 +209,8 @@ function AgentConversationColumn({
|
|
|
202
209
|
});
|
|
203
210
|
}, []);
|
|
204
211
|
const edgeScroll = constrainWidth && edgeToEdgeScroll;
|
|
205
|
-
const centerWrap = edgeScroll ? cn(
|
|
206
|
-
return /* @__PURE__ */ jsx(ConversationAbilitiesProvider, { conversationId: displayId, children: /* @__PURE__ */ jsx(TranscriptAudienceProvider, { audience, children: /* @__PURE__ */ jsxs(
|
|
212
|
+
const centerWrap = edgeScroll ? cn(columnClass, ASSISTANT_MESSAGE_COLUMN_INSET_CLASS) : "contents";
|
|
213
|
+
return /* @__PURE__ */ jsx(ConversationAbilitiesProvider, { conversationId: displayId, children: /* @__PURE__ */ jsx(TranscriptAudienceProvider, { audience, children: /* @__PURE__ */ jsx(TranscriptLayoutProvider, { layout: resolvedLayout, children: /* @__PURE__ */ jsxs(
|
|
207
214
|
"div",
|
|
208
215
|
{
|
|
209
216
|
className: cn(
|
|
@@ -211,7 +218,7 @@ function AgentConversationColumn({
|
|
|
211
218
|
// Legacy: pad the whole column. Edge mode: padding lives on the
|
|
212
219
|
// centered inner wrappers instead so the scroll area runs to the edge.
|
|
213
220
|
!edgeScroll && "px-2",
|
|
214
|
-
constrainWidth && !edgeScroll &&
|
|
221
|
+
constrainWidth && !edgeScroll && columnClass,
|
|
215
222
|
constrainWidth && !edgeScroll && "pb-2",
|
|
216
223
|
edgeScroll && "w-full pb-2"
|
|
217
224
|
),
|
|
@@ -319,7 +326,7 @@ function AgentConversationColumn({
|
|
|
319
326
|
animate: { opacity: 1, y: 0 },
|
|
320
327
|
transition: { duration: 0.28, ease: [0.32, 0.72, 0, 1] },
|
|
321
328
|
className: edgeScroll ? cn(
|
|
322
|
-
|
|
329
|
+
columnClass,
|
|
323
330
|
ASSISTANT_MESSAGE_COLUMN_INSET_CLASS
|
|
324
331
|
) : void 0,
|
|
325
332
|
children: [
|
|
@@ -348,7 +355,7 @@ function AgentConversationColumn({
|
|
|
348
355
|
)
|
|
349
356
|
]
|
|
350
357
|
}
|
|
351
|
-
) }) });
|
|
358
|
+
) }) }) });
|
|
352
359
|
}
|
|
353
360
|
export {
|
|
354
361
|
AgentConversationColumn
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a host lays the ONE conversation column out (HOST-OWNED-SOURCE-PLAN P28e look bar).
|
|
3
|
+
*
|
|
4
|
+
* Knobs, never forks: every value has today's web default, and a host that says nothing gets
|
|
5
|
+
* exactly the web chat. Matrx 2 (desktop) fills its pane, starts at the left edge and draws the
|
|
6
|
+
* person's turn as a compact block — the look its coding screen had before the column.
|
|
7
|
+
*
|
|
8
|
+
* - `width`: "reading" (the canonical `max-w-3xl` reading column) | "fill" (the host's pane).
|
|
9
|
+
* - `align`: "center" | "start" — where a reading-width column sits in a wider pane.
|
|
10
|
+
* - `userMessage`: "card" (the bordered card that collapses a long message behind a chevron) |
|
|
11
|
+
* "compact" (a tight block; only a genuinely long message collapses).
|
|
12
|
+
* - `answerActions`: "shown" (the answer's action row always visible on the latest answer) |
|
|
13
|
+
* "hover" (revealed on hover / keyboard focus — a finished answer ends on its last word, not on
|
|
14
|
+
* a lone ⋯ that reads as "still writing" in a host with few actions).
|
|
15
|
+
*/
|
|
16
|
+
import { type ReactNode } from "react";
|
|
17
|
+
export interface TranscriptLayout {
|
|
18
|
+
width?: "reading" | "fill";
|
|
19
|
+
align?: "center" | "start";
|
|
20
|
+
userMessage?: "card" | "compact";
|
|
21
|
+
answerActions?: "shown" | "hover";
|
|
22
|
+
}
|
|
23
|
+
export declare const DEFAULT_TRANSCRIPT_LAYOUT: Required<TranscriptLayout>;
|
|
24
|
+
export declare function resolveTranscriptLayout(layout?: TranscriptLayout): Required<TranscriptLayout>;
|
|
25
|
+
/** The column's width + placement classes for a resolved layout. Web default = the canonical class. */
|
|
26
|
+
export declare function transcriptColumnClass(layout: Required<TranscriptLayout>): string;
|
|
27
|
+
export declare function TranscriptLayoutProvider(props: {
|
|
28
|
+
layout?: TranscriptLayout;
|
|
29
|
+
children: ReactNode;
|
|
30
|
+
}): import("react").FunctionComponentElement<import("react").ProviderProps<Required<TranscriptLayout>>>;
|
|
31
|
+
export declare function useTranscriptLayout(): Required<TranscriptLayout>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createContext, createElement, useContext, useMemo } from "react";
|
|
3
|
+
import {
|
|
4
|
+
ASSISTANT_MESSAGE_COLUMN_CLASS
|
|
5
|
+
} from "./assistant-message-layout.js";
|
|
6
|
+
const DEFAULT_TRANSCRIPT_LAYOUT = {
|
|
7
|
+
width: "reading",
|
|
8
|
+
align: "center",
|
|
9
|
+
userMessage: "card",
|
|
10
|
+
answerActions: "shown"
|
|
11
|
+
};
|
|
12
|
+
const TranscriptLayoutContext = createContext(DEFAULT_TRANSCRIPT_LAYOUT);
|
|
13
|
+
function resolveTranscriptLayout(layout) {
|
|
14
|
+
return { ...DEFAULT_TRANSCRIPT_LAYOUT, ...layout ?? {} };
|
|
15
|
+
}
|
|
16
|
+
function transcriptColumnClass(layout) {
|
|
17
|
+
if (layout.width === "fill") return "w-full";
|
|
18
|
+
if (layout.align === "start") return "w-full max-w-3xl";
|
|
19
|
+
return ASSISTANT_MESSAGE_COLUMN_CLASS;
|
|
20
|
+
}
|
|
21
|
+
function TranscriptLayoutProvider(props) {
|
|
22
|
+
const { width, align, userMessage, answerActions } = resolveTranscriptLayout(props.layout);
|
|
23
|
+
const value = useMemo(
|
|
24
|
+
() => ({ width, align, userMessage, answerActions }),
|
|
25
|
+
[width, align, userMessage, answerActions]
|
|
26
|
+
);
|
|
27
|
+
return createElement(TranscriptLayoutContext.Provider, { value }, props.children);
|
|
28
|
+
}
|
|
29
|
+
function useTranscriptLayout() {
|
|
30
|
+
return useContext(TranscriptLayoutContext);
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
DEFAULT_TRANSCRIPT_LAYOUT,
|
|
34
|
+
TranscriptLayoutProvider,
|
|
35
|
+
resolveTranscriptLayout,
|
|
36
|
+
transcriptColumnClass,
|
|
37
|
+
useTranscriptLayout
|
|
38
|
+
};
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
spliceSave,
|
|
7
7
|
tokenizeSource
|
|
8
8
|
} from "@ai-matrx/content-ir/source";
|
|
9
|
-
import { removeThinkingContent } from "@ai-matrx/
|
|
9
|
+
import { stripThinking as removeThinkingContent } from "@ai-matrx/content-ir/source";
|
|
10
10
|
function isTextPart(part) {
|
|
11
11
|
return part.type === "text" || part.type === void 0;
|
|
12
12
|
}
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
readEnvelope,
|
|
18
18
|
reconstructRegionValue
|
|
19
19
|
} from "@ai-matrx/content-ir";
|
|
20
|
-
import { removeThinkingContent } from "@ai-matrx/
|
|
20
|
+
import { stripThinking as removeThinkingContent } from "@ai-matrx/content-ir/source";
|
|
21
21
|
import { NON_ANSWER_BLOCK_TYPES } from "../active-requests/active-requests.selectors.js";
|
|
22
22
|
import {
|
|
23
23
|
buildMessageCitationIndex,
|
|
@@ -5,7 +5,10 @@ import { ChevronDown, ExternalLink, PencilLine, Plus } from "lucide-react";
|
|
|
5
5
|
import { AppLink } from "../../host/ui-slots.js";
|
|
6
6
|
import { Popover, PopoverContent, PopoverTrigger } from "@ai-matrx/design-system";
|
|
7
7
|
import { useAppSelector } from "../../store/hooks.js";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
selectConversationTitle,
|
|
10
|
+
selectIsCacheOnly
|
|
11
|
+
} from "../../agents/redux/execution-system/conversations/conversations.selectors.js";
|
|
9
12
|
import { selectConversationListItemById } from "../../agents/redux/conversation-list/conversation-list.selectors.js";
|
|
10
13
|
import { conversationRenameOpener } from "../../agents/components/conversation-actions/rename/conversationRenameOpener.js";
|
|
11
14
|
import { conversationTitleText } from "@ai-matrx/content-ir/surfaces";
|
|
@@ -33,6 +36,9 @@ function ChatPanelTitleMenu({
|
|
|
33
36
|
const [open, setOpen] = useState(false);
|
|
34
37
|
const realTitle = useChatRealTitle(conversationId);
|
|
35
38
|
const title = realTitle ?? "New chat";
|
|
39
|
+
const started = useAppSelector(
|
|
40
|
+
(state) => conversationId ? !selectIsCacheOnly(conversationId)(state) || selectConversationListItemById(conversationId)(state) !== null : false
|
|
41
|
+
);
|
|
36
42
|
const close = () => setOpen(false);
|
|
37
43
|
return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
|
|
38
44
|
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "quiet", iconEnd: /* @__PURE__ */ jsx(ChevronDown, {}), className: "min-w-0", title, children: title }) }),
|
|
@@ -56,7 +62,7 @@ function ChatPanelTitleMenu({
|
|
|
56
62
|
}
|
|
57
63
|
}
|
|
58
64
|
),
|
|
59
|
-
conversationId ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
65
|
+
conversationId && started ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
60
66
|
/* @__PURE__ */ jsx(
|
|
61
67
|
Tile,
|
|
62
68
|
{
|