@ai-matrx/chat 0.4.16 → 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 +23 -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/useCanvasWorkspaceConversation.d.ts +10 -0
- package/dist/canvas/workspace/useCanvasWorkspaceConversation.js +50 -3
- 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/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,28 @@
|
|
|
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
|
+
|
|
3
26
|
## 0.4.16 — the Side Chat's URL contract
|
|
4
27
|
|
|
5
28
|
- ONE param family on every home opens, closes and aims the Side Chat (the shell chat dock):
|
|
@@ -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
|
{
|
|
@@ -85,6 +85,16 @@ export declare function rememberWorkspaceConversation(surfaceKey: string, conver
|
|
|
85
85
|
export declare function reservedChatKey(surfaceKey: string): string;
|
|
86
86
|
export declare function reserveWorkspaceConversationId(surfaceKey: string, fresh: boolean): string | undefined;
|
|
87
87
|
export declare function releaseWorkspaceConversationId(surfaceKey: string): void;
|
|
88
|
+
/**
|
|
89
|
+
* The server refused a new chat's id because it is already TAKEN (409
|
|
90
|
+
* `conversation_already_exists`, run-ai-stream): a reserved id another tab
|
|
91
|
+
* already sent with, or one kept past a send. Retrying the same id can never
|
|
92
|
+
* succeed.
|
|
93
|
+
*/
|
|
94
|
+
export declare function isConversationIdTaken(error: {
|
|
95
|
+
message?: unknown;
|
|
96
|
+
error_type?: unknown;
|
|
97
|
+
} | undefined): boolean;
|
|
88
98
|
export interface CanvasWorkspaceConversationController {
|
|
89
99
|
conversation: CanvasWorkspaceConversation;
|
|
90
100
|
/** The id when ready, else null. */
|
|
@@ -7,6 +7,10 @@ import { resumeConversation } from "../../agents/redux/execution-system/thunks/r
|
|
|
7
7
|
import { useAppDispatch, useAppSelector } from "../../store/hooks.js";
|
|
8
8
|
import { selectShouldPromptForOrganization } from "../../host/ui-slots.js";
|
|
9
9
|
import { selectIsCacheOnly } from "../../agents/redux/execution-system/conversations/conversations.selectors.js";
|
|
10
|
+
import { selectLatestRequestId } from "../../agents/redux/execution-system/selectors/aggregate.selectors.js";
|
|
11
|
+
import { selectRequestError } from "../../agents/redux/execution-system/active-requests/active-requests.selectors.js";
|
|
12
|
+
import { extractFlatText } from "../../agents/redux/execution-system/messages/messages.selectors.js";
|
|
13
|
+
import { setUserInputText } from "../../agents/redux/execution-system/instance-user-input/instance-user-input.slice.js";
|
|
10
14
|
import { replaceAddressWithoutNavigating } from "@ai-matrx/rich-content/utils/addressWithoutNavigating";
|
|
11
15
|
import { describeLaunchError } from "./describe-launch-error.js";
|
|
12
16
|
import { sideChatAddress } from "./side-chat-address.js";
|
|
@@ -65,6 +69,11 @@ function releaseWorkspaceConversationId(surfaceKey) {
|
|
|
65
69
|
} catch {
|
|
66
70
|
}
|
|
67
71
|
}
|
|
72
|
+
function isConversationIdTaken(error) {
|
|
73
|
+
if (!error) return false;
|
|
74
|
+
const text = `${typeof error.error_type === "string" ? error.error_type : ""} ${typeof error.message === "string" ? error.message : ""}`;
|
|
75
|
+
return /conversation_already_exists|Conversation already exists/i.test(text);
|
|
76
|
+
}
|
|
68
77
|
function isCanvasWorkspaceStart(input) {
|
|
69
78
|
return "kind" in input;
|
|
70
79
|
}
|
|
@@ -103,6 +112,21 @@ function useCanvasWorkspaceConversation(surfaceKey, input) {
|
|
|
103
112
|
const serverHasIt = useAppSelector(
|
|
104
113
|
(state) => conversationId ? !selectIsCacheOnly(conversationId)(state) : false
|
|
105
114
|
);
|
|
115
|
+
const latestRequestId = useAppSelector(
|
|
116
|
+
(state) => conversationId ? selectLatestRequestId(conversationId)(state) : void 0
|
|
117
|
+
);
|
|
118
|
+
const sendStarted = latestRequestId !== void 0;
|
|
119
|
+
const idTaken = useAppSelector(
|
|
120
|
+
(state) => latestRequestId ? isConversationIdTaken(selectRequestError(latestRequestId)(state)) : false
|
|
121
|
+
);
|
|
122
|
+
const refusedText = useAppSelector((state) => {
|
|
123
|
+
if (!idTaken || !conversationId) return "";
|
|
124
|
+
const entry = state.messages?.byConversationId?.[conversationId];
|
|
125
|
+
const lastUser = entry?.orderedIds.map((id) => entry.byId[id]).filter((record) => record?.role === "user").at(-1);
|
|
126
|
+
const sent = lastUser ? extractFlatText(lastUser).trim() : "";
|
|
127
|
+
const typed = state.instanceUserInput?.byConversationId?.[conversationId]?.text?.trim() ?? "";
|
|
128
|
+
return [sent, typed === sent ? "" : typed].filter(Boolean).join("\n\n");
|
|
129
|
+
});
|
|
106
130
|
const [keyedFor, setKeyedFor] = useState({ surfaceKey, hasAddress });
|
|
107
131
|
if (keyedFor.surfaceKey !== surfaceKey || keyedFor.hasAddress !== hasAddress) {
|
|
108
132
|
setKeyedFor({ surfaceKey, hasAddress });
|
|
@@ -141,14 +165,36 @@ function useCanvasWorkspaceConversation(surfaceKey, input) {
|
|
|
141
165
|
if (!conversationId && addressOpen) return;
|
|
142
166
|
const shown = conversationId && (request.kind === "open" || serverHasIt) ? conversationId : null;
|
|
143
167
|
if (conversationId) {
|
|
144
|
-
|
|
145
|
-
|
|
168
|
+
const remembered = shown ?? (sendStarted && !idTaken ? conversationId : null);
|
|
169
|
+
rememberWorkspaceConversation(surfaceKey, remembered);
|
|
170
|
+
if (remembered) releaseWorkspaceConversationId(surfaceKey);
|
|
146
171
|
}
|
|
147
172
|
const search = sideChatAddress(window.location.search, { conversationId: shown, open: addressOpen, hostedElsewhere });
|
|
148
173
|
if (search === window.location.search) return;
|
|
149
174
|
onAddressWrite.current?.(search);
|
|
150
175
|
replaceAddressWithoutNavigating(`${window.location.pathname}${search}${window.location.hash}`);
|
|
151
|
-
}, [hasAddress, addressRead, addressOpen, hostedElsewhere, conversationId, request.kind, serverHasIt, surfaceKey]);
|
|
176
|
+
}, [hasAddress, addressRead, addressOpen, hostedElsewhere, conversationId, request.kind, serverHasIt, sendStarted, idTaken, surfaceKey]);
|
|
177
|
+
const healed = useRef(null);
|
|
178
|
+
const carry = useRef(null);
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
if (!idTaken || serverHasIt || !conversationId || !latestRequestId) return;
|
|
181
|
+
if (healed.current === latestRequestId) return;
|
|
182
|
+
healed.current = latestRequestId;
|
|
183
|
+
console.warn("[canvas-workspace] the new chat's id was already taken (409); starting it again under a fresh id", conversationId);
|
|
184
|
+
releaseWorkspaceConversationId(surfaceKey);
|
|
185
|
+
if (hasAddress) rememberWorkspaceConversation(surfaceKey, null);
|
|
186
|
+
carry.current = refusedText ? { text: refusedText, from: conversationId } : null;
|
|
187
|
+
setConversationId(null);
|
|
188
|
+
setFailure(null);
|
|
189
|
+
setRequest(
|
|
190
|
+
(current) => current.kind === "agent" ? { kind: "agent", agentId: current.agentId, nonce: current.nonce + 1 } : { kind: "new", nonce: current.nonce + 1, ...current.kind === "new" && current.mandateKey ? { mandateKey: current.mandateKey } : {} }
|
|
191
|
+
);
|
|
192
|
+
}, [idTaken, serverHasIt, conversationId, latestRequestId, surfaceKey, hasAddress, refusedText]);
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
if (!conversationId || !carry.current || carry.current.from === conversationId) return;
|
|
195
|
+
dispatch(setUserInputText({ conversationId, text: carry.current.text }));
|
|
196
|
+
carry.current = null;
|
|
197
|
+
}, [conversationId, dispatch]);
|
|
152
198
|
useEffect(() => {
|
|
153
199
|
if (!enabled || waitingForOrganization || !addressRead) return;
|
|
154
200
|
const key = `${surfaceKey}#${request.kind}#${request.nonce}`;
|
|
@@ -239,6 +285,7 @@ function useCanvasWorkspaceConversation(surfaceKey, input) {
|
|
|
239
285
|
return { conversation, conversationId, startNew, openExisting, startWith };
|
|
240
286
|
}
|
|
241
287
|
export {
|
|
288
|
+
isConversationIdTaken,
|
|
242
289
|
recallWorkspaceConversation,
|
|
243
290
|
releaseWorkspaceConversationId,
|
|
244
291
|
rememberWorkspaceConversation,
|
|
@@ -3,7 +3,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useState, useCallback } from "react";
|
|
4
4
|
import { ChevronDown } from "lucide-react";
|
|
5
5
|
import { usePathname, useSearchParams, useRouter } from "../../host/navigation.js";
|
|
6
|
-
import { AgentPickerSheet } from "../../
|
|
6
|
+
import { AgentPickerSheet } from "../../ui/lazy/AgentPickerSheet.js";
|
|
7
7
|
import { useAgentName } from "../../agents/identity/agent-identity.js";
|
|
8
8
|
import { pushAppHref } from "../../host/ui-slots.js";
|
|
9
9
|
import { Button } from "@ai-matrx/design-system/controls";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useCallback, useRef, useEffect } from "react";
|
|
4
4
|
import { useRouter, usePathname, useSearchParams } from "../../host/navigation.js";
|
|
5
|
-
import { AgentPickerSheet } from "../../
|
|
5
|
+
import { AgentPickerSheet } from "../../ui/lazy/AgentPickerSheet.js";
|
|
6
6
|
import { ChevronDown } from "lucide-react";
|
|
7
7
|
import { SidebarActions } from "./sidebar/SidebarActions.js";
|
|
8
8
|
import { SsrSidebarAgents } from "./SsrSidebarAgents.js";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useRef, useState, useCallback } from "react";
|
|
4
4
|
import { useRouter } from "../../host/navigation.js";
|
|
5
|
-
import { AgentPickerSheet } from "../../
|
|
5
|
+
import { AgentPickerSheet } from "../../ui/lazy/AgentPickerSheet.js";
|
|
6
6
|
import { useAppSelector, useAppDispatch } from "../../store/hooks.js";
|
|
7
7
|
import { selectAgentById } from "../../agents/redux/agent-definition/selectors.js";
|
|
8
8
|
import { selectLatestConversationId } from "../../agents/redux/execution-system/selectors/aggregate.selectors.js";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useRef, useCallback, useState } from "react";
|
|
4
4
|
import { useRouter } from "../../../host/navigation.js";
|
|
5
|
-
import { AgentPickerSheet } from "../../../
|
|
5
|
+
import { AgentPickerSheet } from "../../../ui/lazy/AgentPickerSheet.js";
|
|
6
6
|
import { useAppSelector } from "../../../store/hooks.js";
|
|
7
7
|
import { selectIsAdmin, selectIsAuthenticated } from "../../../host/identity.js";
|
|
8
8
|
import { useDebugContext } from "../../../host/prefs-react.js";
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
useMemo
|
|
10
10
|
} from "react";
|
|
11
11
|
import { useDebugContext } from "../../../host/prefs-react.js";
|
|
12
|
-
import { GuidedVariableInputs, StackedVariableInputs } from "../../../
|
|
12
|
+
import { GuidedVariableInputs, StackedVariableInputs } from "../../../ui/lazy/ChatVariableInputs.js";
|
|
13
13
|
import { useRouter, useSearchParams, usePathname } from "../../../host/navigation.js";
|
|
14
14
|
import {
|
|
15
15
|
CornerDownLeft,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
useCallback,
|
|
9
9
|
useMemo
|
|
10
10
|
} from "react";
|
|
11
|
-
import { GuidedVariableInputs, PublicVariableInputs } from "../
|
|
11
|
+
import { GuidedVariableInputs, PublicVariableInputs } from "../ui/lazy/PublicChatVariableInputs.js";
|
|
12
12
|
import { useRouter, useSearchParams, usePathname } from "../host/navigation.js";
|
|
13
13
|
import {
|
|
14
14
|
CornerDownLeft,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
selectUIState
|
|
11
11
|
} from "./_legacy-stubs.js";
|
|
12
12
|
import { chatConversationsActions } from "./_legacy-stubs.js";
|
|
13
|
-
import { AssistantMessage } from "../
|
|
13
|
+
import { AssistantMessage } from "../ui/lazy/CxAssistantMessage.js";
|
|
14
14
|
import { MessageErrorBoundary } from "./MessageErrorBoundary.js";
|
|
15
15
|
import { UserMessage } from "./UserMessage.js";
|
|
16
16
|
function StreamingAssistantMessage({
|
package/dist/host/ui-slots.js
CHANGED
|
@@ -291,7 +291,22 @@ const readListRpc = slotFn("readListRpc");
|
|
|
291
291
|
const ReadFailure = slotComponent("ReadFailure", unregisteredWidget("ReadFailure"));
|
|
292
292
|
const ItemRow = slotComponent("ItemRow", unregisteredWidget("ItemRow"));
|
|
293
293
|
const UntrustedCount = slotComponent("UntrustedCount", unregisteredWidget("UntrustedCount"));
|
|
294
|
-
|
|
294
|
+
function PlainStaleDataNotice(props) {
|
|
295
|
+
const what = props.what ?? "this";
|
|
296
|
+
const sentence = props.hasData ? `Showing what was last read; ${what} could not be refreshed.` : `Could not read ${what}.`;
|
|
297
|
+
return createElement(
|
|
298
|
+
"div",
|
|
299
|
+
{ role: "status", className: "text-xs text-muted-foreground", "data-stale-data-notice": "" },
|
|
300
|
+
sentence,
|
|
301
|
+
props.detail ? ` ${props.detail}` : null,
|
|
302
|
+
props.onRetry ? createElement(
|
|
303
|
+
"button",
|
|
304
|
+
{ type: "button", className: "ml-2 underline", onClick: () => props.onRetry?.() },
|
|
305
|
+
"Try again"
|
|
306
|
+
) : null
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
const StaleDataNotice = slotComponent("StaleDataNotice", PlainStaleDataNotice);
|
|
295
310
|
const WorkspaceGate = slotComponent("WorkspaceGate", DefaultWorkspaceGate);
|
|
296
311
|
const OrganizationContextNotice = slotComponent("OrganizationContextNotice", unregisteredWidget("OrganizationContextNotice"));
|
|
297
312
|
const JsonInspector = slotComponent("JsonInspector", unregisteredWidget("JsonInspector"));
|
|
@@ -315,7 +330,7 @@ const ReferenceCopyMenuItem = slotComponent("ReferenceCopyMenuItem", unregistere
|
|
|
315
330
|
const ReferenceCopyButton = slotComponent("ReferenceCopyButton", unregisteredWidget("ReferenceCopyButton"));
|
|
316
331
|
const MandateNotesPanel = slotComponent("MandateNotesPanel", unregisteredWidget("MandateNotesPanel"));
|
|
317
332
|
const SurfaceBoundAgentsList = slotComponent("SurfaceBoundAgentsList", unregisteredWidget("SurfaceBoundAgentsList"));
|
|
318
|
-
const ProposedDirectivesZone = slotComponent("ProposedDirectivesZone",
|
|
333
|
+
const ProposedDirectivesZone = slotComponent("ProposedDirectivesZone", optionalWidget("ProposedDirectivesZone"));
|
|
319
334
|
const EntityCommentPopover = slotComponent("EntityCommentPopover", unregisteredWidget("EntityCommentPopover"));
|
|
320
335
|
const ProTextarea = slotComponent("ProTextarea", (props) => {
|
|
321
336
|
const { onChange, value, className, placeholder, rows, id, ref, autoFocus, disabled } = props;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SurfaceAgentsPanelImpl } from "../../../
|
|
1
|
+
import { SurfaceAgentsPanelImpl } from "../../../ui/lazy/SurfaceAgentsPanelImpl.js";
|
|
2
2
|
/** What a guest is told when they reach for Agents — one copy for every door. */
|
|
3
3
|
export declare const AGENTS_AUTH_GATE: {
|
|
4
4
|
featureName: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useRef, useState } from "react";
|
|
4
|
-
import { SurfaceAgentsPanelImpl } from "../../../
|
|
4
|
+
import { SurfaceAgentsPanelImpl } from "../../../ui/lazy/SurfaceAgentsPanelImpl.js";
|
|
5
5
|
import { TapTargetButtonTransparent } from "@ai-matrx/tap-target";
|
|
6
6
|
import { INTELLIGENCE_ICON } from "@ai-matrx/icons/domain";
|
|
7
7
|
import {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { DbToolRendererImplLazy as LazyImpl } from "../../
|
|
3
|
+
import { DbToolRendererImplLazy as LazyImpl } from "../../ui/lazy/DbToolRendererImpl.js";
|
|
4
4
|
const DbToolRenderer = (props) => {
|
|
5
5
|
return /* @__PURE__ */ jsx(LazyImpl, { ...props });
|
|
6
6
|
};
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import
|
|
3
|
-
const AgentAssistantMessage =
|
|
2
|
+
import { clientLazy } from "../../utils/client-lazy.js";
|
|
3
|
+
const AgentAssistantMessage = clientLazy(
|
|
4
4
|
() => import("../../agents/components/messages-display/assistant/AgentAssistantMessage.js").then(
|
|
5
5
|
(m) => ({
|
|
6
6
|
default: m.AgentAssistantMessage
|
|
7
7
|
})
|
|
8
|
-
)
|
|
9
|
-
{ ssr: false }
|
|
8
|
+
)
|
|
10
9
|
);
|
|
11
10
|
export {
|
|
12
11
|
AgentAssistantMessage
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import
|
|
3
|
-
const AgentPickerSheet =
|
|
2
|
+
import { clientLazy } from "../../utils/client-lazy.js";
|
|
3
|
+
const AgentPickerSheet = clientLazy(
|
|
4
4
|
() => import("../../cx-chat/components/agent/AgentPickerSheet.js").then(
|
|
5
5
|
(m) => ({ default: m.AgentPickerSheet })
|
|
6
|
-
)
|
|
7
|
-
{ ssr: false }
|
|
6
|
+
)
|
|
8
7
|
);
|
|
9
8
|
export {
|
|
10
9
|
AgentPickerSheet
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import
|
|
3
|
-
const GuidedVariableInputs =
|
|
2
|
+
import { clientLazy } from "../../utils/client-lazy.js";
|
|
3
|
+
const GuidedVariableInputs = clientLazy(
|
|
4
4
|
() => import("../../cx-chat/components/user-input/GuidedVariableInputs.js").then(
|
|
5
5
|
(m) => ({ default: m.GuidedVariableInputs })
|
|
6
|
-
)
|
|
7
|
-
{ ssr: false }
|
|
6
|
+
)
|
|
8
7
|
);
|
|
9
|
-
const StackedVariableInputs =
|
|
8
|
+
const StackedVariableInputs = clientLazy(
|
|
10
9
|
() => import("../../cx-chat/components/user-input/StackedVariableInputs.js").then(
|
|
11
10
|
(m) => ({ default: m.StackedVariableInputs })
|
|
12
|
-
)
|
|
13
|
-
{ ssr: false }
|
|
11
|
+
)
|
|
14
12
|
);
|
|
15
13
|
export {
|
|
16
14
|
GuidedVariableInputs,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const ConversationRenameDialogHostImpl: import("react").ComponentType<
|
|
1
|
+
export declare const ConversationRenameDialogHostImpl: import("react").ComponentType<object>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import
|
|
3
|
-
const ConversationRenameDialogHostImpl =
|
|
2
|
+
import { clientLazy } from "../../utils/client-lazy.js";
|
|
3
|
+
const ConversationRenameDialogHostImpl = clientLazy(
|
|
4
4
|
() => import("../../agents/components/conversation-actions/rename/ConversationRenameDialogHostImpl.js"),
|
|
5
|
-
{
|
|
5
|
+
{ loading: () => null }
|
|
6
6
|
);
|
|
7
7
|
export {
|
|
8
8
|
ConversationRenameDialogHostImpl
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import
|
|
3
|
-
const AssistantMessage =
|
|
2
|
+
import { clientLazy } from "../../utils/client-lazy.js";
|
|
3
|
+
const AssistantMessage = clientLazy(
|
|
4
4
|
() => import("../../cx-conversation/AssistantMessage.js").then(
|
|
5
5
|
(m) => m.AssistantMessage
|
|
6
|
-
)
|
|
7
|
-
{ ssr: false }
|
|
6
|
+
)
|
|
8
7
|
);
|
|
9
8
|
export {
|
|
10
9
|
AssistantMessage
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import
|
|
3
|
-
const DbToolRendererImplLazy =
|
|
2
|
+
import { clientLazy } from "../../utils/client-lazy.js";
|
|
3
|
+
const DbToolRendererImplLazy = clientLazy(
|
|
4
4
|
() => import("../../tool-call-visualization/db-renderer/DbToolRendererImpl.js").then((m) => ({
|
|
5
5
|
default: m.DbToolRendererImpl
|
|
6
6
|
})),
|
|
7
|
-
{
|
|
7
|
+
{ loading: () => null }
|
|
8
8
|
);
|
|
9
9
|
export {
|
|
10
10
|
DbToolRendererImplLazy
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
const IconResolver =
|
|
1
|
+
import { clientLazy } from "../../utils/client-lazy.js";
|
|
2
|
+
const IconResolver = clientLazy(
|
|
3
3
|
() => import("@ai-matrx/icons").then((m) => ({
|
|
4
4
|
default: m.IconResolver
|
|
5
|
-
}))
|
|
6
|
-
{ ssr: false }
|
|
5
|
+
}))
|
|
7
6
|
);
|
|
8
7
|
export {
|
|
9
8
|
IconResolver
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import
|
|
3
|
-
const GuidedVariableInputs =
|
|
2
|
+
import { clientLazy } from "../../utils/client-lazy.js";
|
|
3
|
+
const GuidedVariableInputs = clientLazy(
|
|
4
4
|
() => import("../../public-chat/components/GuidedVariableInputs.js").then(
|
|
5
5
|
(m) => ({ default: m.GuidedVariableInputs })
|
|
6
|
-
)
|
|
7
|
-
{ ssr: false }
|
|
6
|
+
)
|
|
8
7
|
);
|
|
9
|
-
const PublicVariableInputs =
|
|
8
|
+
const PublicVariableInputs = clientLazy(
|
|
10
9
|
() => import("../../public-chat/components/PublicVariableInputs.js").then(
|
|
11
10
|
(m) => ({ default: m.PublicVariableInputs })
|
|
12
|
-
)
|
|
13
|
-
{ ssr: false }
|
|
11
|
+
)
|
|
14
12
|
);
|
|
15
13
|
export {
|
|
16
14
|
GuidedVariableInputs,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { ScrollAssistantLauncherImplProps } from "../../agents/components/ambient-assistant/ScrollAssistantLauncherImpl.js";
|
|
2
2
|
export declare const ScrollAssistantLauncherImpl: import("react").ComponentType<ScrollAssistantLauncherImplProps>;
|
|
3
|
-
export declare const ScrollVoiceAssistantLauncherImpl: import("react").ComponentType<
|
|
3
|
+
export declare const ScrollVoiceAssistantLauncherImpl: import("react").ComponentType<object>;
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import
|
|
3
|
-
const ScrollAssistantLauncherImpl =
|
|
2
|
+
import { clientLazy } from "../../utils/client-lazy.js";
|
|
3
|
+
const ScrollAssistantLauncherImpl = clientLazy(
|
|
4
4
|
() => import("../../agents/components/ambient-assistant/ScrollAssistantLauncherImpl.js"),
|
|
5
5
|
{
|
|
6
|
-
ssr: false,
|
|
7
6
|
// A prop-blind loading shell would jump between the two supported heights.
|
|
8
7
|
// Reveal the correctly sized implementation once its chunk is ready.
|
|
9
8
|
loading: () => null
|
|
10
9
|
}
|
|
11
10
|
);
|
|
12
|
-
const ScrollVoiceAssistantLauncherImpl =
|
|
11
|
+
const ScrollVoiceAssistantLauncherImpl = clientLazy(
|
|
13
12
|
() => import("../../agents/components/ambient-assistant/ScrollVoiceAssistantLauncherImpl.js"),
|
|
14
13
|
{
|
|
15
|
-
ssr: false,
|
|
16
14
|
loading: () => null
|
|
17
15
|
}
|
|
18
16
|
);
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import
|
|
3
|
+
import { clientLazy } from "../../utils/client-lazy.js";
|
|
4
4
|
import { Loader2 } from "lucide-react";
|
|
5
|
-
const SurfaceAgentsPanelImpl =
|
|
5
|
+
const SurfaceAgentsPanelImpl = clientLazy(
|
|
6
6
|
() => import("../../surfaces/components/chrome/SurfaceAgentsPanelImpl.js"),
|
|
7
7
|
{
|
|
8
|
-
ssr: false,
|
|
9
8
|
loading: () => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2 p-8 text-xs text-muted-foreground", children: [
|
|
10
9
|
/* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }),
|
|
11
10
|
"Loading agents\u2026"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `clientLazy` — the package's host-neutral replacement for `next/dynamic(..., { ssr: false })`.
|
|
3
|
+
* A lazily loaded component that renders only on the client: on the server and during hydration
|
|
4
|
+
* it renders `loading` (hydration-safe through `useSyncExternalStore`); on the client it renders
|
|
5
|
+
* the loaded component inside a Suspense boundary with the same `loading` as the fallback.
|
|
6
|
+
*
|
|
7
|
+
* Plain React (`lazy` + `Suspense`), so a Vite / Electron / extension host needs no `next`
|
|
8
|
+
* installed (HOST-OWNED-SOURCE-PLAN P28e). Guard: `host/__tests__/no-next-import-outside-next-binding`.
|
|
9
|
+
*/
|
|
10
|
+
import { type ComponentType } from "react";
|
|
11
|
+
export declare function useIsClient(): boolean;
|
|
12
|
+
export declare function clientLazy<P extends object>(load: () => Promise<{
|
|
13
|
+
default: ComponentType<P>;
|
|
14
|
+
} | ComponentType<P>>, options?: {
|
|
15
|
+
loading?: ComponentType;
|
|
16
|
+
}): ComponentType<P>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createElement, lazy, Suspense, useSyncExternalStore } from "react";
|
|
3
|
+
const subscribe = () => () => {
|
|
4
|
+
};
|
|
5
|
+
const onClient = () => true;
|
|
6
|
+
const onServer = () => false;
|
|
7
|
+
function useIsClient() {
|
|
8
|
+
return useSyncExternalStore(subscribe, onClient, onServer);
|
|
9
|
+
}
|
|
10
|
+
function clientLazy(load, options = {}) {
|
|
11
|
+
const Lazy = lazy(async () => {
|
|
12
|
+
const m = await load();
|
|
13
|
+
return typeof m === "function" || !("default" in m) ? { default: m } : m;
|
|
14
|
+
});
|
|
15
|
+
const Loading = options.loading;
|
|
16
|
+
function ClientLazy(props) {
|
|
17
|
+
const client = useIsClient();
|
|
18
|
+
const fallback = Loading ? createElement(Loading) : null;
|
|
19
|
+
if (!client) return fallback;
|
|
20
|
+
return createElement(Suspense, { fallback }, createElement(Lazy, props));
|
|
21
|
+
}
|
|
22
|
+
return ClientLazy;
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
clientLazy,
|
|
26
|
+
useIsClient
|
|
27
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/chat",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.18",
|
|
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 per-module subpaths @ai-matrx/chat/<domain>/<module> over a host contract (/host, /host/react) whose only required value is a db; plus the kernel compatibility entries (., /external-store, /matrx, /react, /tools, styles.css, tokens.css) until P28.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -229,23 +229,23 @@
|
|
|
229
229
|
"@ai-matrx/agents": "latest",
|
|
230
230
|
"@ai-matrx/alchemy": "latest",
|
|
231
231
|
"@ai-matrx/browser-audio": "latest",
|
|
232
|
+
"@ai-matrx/associations": "latest",
|
|
232
233
|
"@ai-matrx/canvas": "latest",
|
|
233
234
|
"@ai-matrx/content-ir": "latest",
|
|
234
235
|
"@ai-matrx/content-ir-react": "latest",
|
|
235
|
-
"@ai-matrx/associations": "latest",
|
|
236
236
|
"@ai-matrx/data": "latest",
|
|
237
|
-
"@ai-matrx/kit": "latest",
|
|
238
237
|
"@ai-matrx/design-system": "latest",
|
|
239
|
-
"@ai-matrx/media": "latest",
|
|
240
238
|
"@ai-matrx/icons": "latest",
|
|
239
|
+
"@ai-matrx/media": "latest",
|
|
241
240
|
"@ai-matrx/print": "latest",
|
|
241
|
+
"@ai-matrx/diff": "latest",
|
|
242
242
|
"@ai-matrx/realtime": "latest",
|
|
243
243
|
"@ai-matrx/records": "latest",
|
|
244
244
|
"@ai-matrx/records-ui": "latest",
|
|
245
245
|
"@ai-matrx/rich-content": "latest",
|
|
246
|
-
"@ai-matrx/
|
|
247
|
-
"@ai-matrx/
|
|
248
|
-
"@ai-matrx/
|
|
246
|
+
"@ai-matrx/rich-editor": "latest",
|
|
247
|
+
"@ai-matrx/kit": "latest",
|
|
248
|
+
"@ai-matrx/tap-target": "latest"
|
|
249
249
|
},
|
|
250
250
|
"devDependencies": {
|
|
251
251
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|