@btst/stack 4.2.0 → 4.4.0
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/README.md +2 -2
- package/dist/packages/stack/src/plugins/ai-chat/api/compaction.cjs +249 -0
- package/dist/packages/stack/src/plugins/ai-chat/api/compaction.mjs +241 -0
- package/dist/packages/stack/src/plugins/ai-chat/api/operations.cjs +20 -0
- package/dist/packages/stack/src/plugins/ai-chat/api/operations.mjs +20 -0
- package/dist/packages/stack/src/plugins/ai-chat/api/plugin.cjs +5 -0
- package/dist/packages/stack/src/plugins/ai-chat/api/plugin.mjs +5 -0
- package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-interface.cjs +38 -3
- package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-interface.mjs +38 -3
- package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-layout.cjs +173 -72
- package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-layout.mjs +176 -75
- package/dist/packages/stack/src/plugins/ai-chat/client/context-checkpoint.cjs +30 -0
- package/dist/packages/stack/src/plugins/ai-chat/client/context-checkpoint.mjs +27 -0
- package/dist/packages/stack/src/plugins/ai-chat/schemas.cjs +2 -0
- package/dist/packages/stack/src/plugins/ai-chat/schemas.mjs +2 -0
- package/dist/packages/ui/src/components/markdown-content.cjs +9 -9
- package/dist/packages/ui/src/components/markdown-content.mjs +9 -9
- package/dist/plugins/ai-chat/api/index.d.cts +2 -2
- package/dist/plugins/ai-chat/api/index.d.mts +2 -2
- package/dist/plugins/ai-chat/api/index.d.ts +2 -2
- package/dist/plugins/ai-chat/client/components/index.d.cts +1 -1
- package/dist/plugins/ai-chat/client/components/index.d.mts +1 -1
- package/dist/plugins/ai-chat/client/components/index.d.ts +1 -1
- package/dist/plugins/ai-chat/client/hooks/index.d.cts +2 -2
- package/dist/plugins/ai-chat/client/hooks/index.d.mts +2 -2
- package/dist/plugins/ai-chat/client/hooks/index.d.ts +2 -2
- package/dist/plugins/ai-chat/client/index.d.cts +3 -3
- package/dist/plugins/ai-chat/client/index.d.mts +3 -3
- package/dist/plugins/ai-chat/client/index.d.ts +3 -3
- package/dist/plugins/ai-chat/query-keys.d.cts +1 -1
- package/dist/plugins/ai-chat/query-keys.d.mts +1 -1
- package/dist/plugins/ai-chat/query-keys.d.ts +1 -1
- package/dist/plugins/blog/api/index.d.cts +2 -2
- package/dist/plugins/blog/api/index.d.mts +2 -2
- package/dist/plugins/blog/api/index.d.ts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
- package/dist/plugins/blog/client/index.d.cts +2 -2
- package/dist/plugins/blog/client/index.d.mts +2 -2
- package/dist/plugins/blog/client/index.d.ts +2 -2
- package/dist/plugins/blog/query-keys.d.cts +2 -2
- package/dist/plugins/blog/query-keys.d.mts +2 -2
- package/dist/plugins/blog/query-keys.d.ts +2 -2
- package/dist/shared/{stack.BTStHVTy.d.cts → stack.BGzgFx3R.d.cts} +5 -5
- package/dist/shared/{stack.DBH1Bjko.d.ts → stack.C4zspX0T.d.ts} +13 -2
- package/dist/shared/{stack.94kN3pEK.d.mts → stack.CjCSDfWy.d.mts} +5 -5
- package/dist/shared/{stack.D5b9l24t.d.ts → stack.Clil75lW.d.ts} +35 -35
- package/dist/shared/{stack.xAiy2VtO.d.cts → stack.DDQlE2Pm.d.cts} +10 -3
- package/dist/shared/{stack.Ce6V7eDj.d.cts → stack.DNw6Hv5o.d.cts} +35 -35
- package/dist/shared/{stack.CTneIW0u.d.mts → stack.DhZtP2JZ.d.mts} +35 -35
- package/dist/shared/{stack.C-eQN_Jt.d.ts → stack.DlehgNZZ.d.ts} +10 -3
- package/dist/shared/{stack.DfrRYOIl.d.ts → stack.Dpg_JjIo.d.ts} +5 -5
- package/dist/shared/{stack.B7Mj74BQ.d.mts → stack.Ps3MDhTC.d.mts} +13 -2
- package/dist/shared/{stack.C5FZr-jJ.d.mts → stack.tAUeRAtd.d.mts} +10 -3
- package/dist/shared/{stack.Dgt3MCtW.d.cts → stack.ymWsP4HZ.d.cts} +13 -2
- package/package.json +2 -2
- package/src/__tests__/markdown-link-navigation.test.tsx +29 -0
- package/src/plugins/ai-chat/__tests__/classnames-overrides.test.tsx +22 -2
- package/src/plugins/ai-chat/__tests__/compaction.test.ts +272 -0
- package/src/plugins/ai-chat/__tests__/page-widget.test.tsx +103 -1
- package/src/plugins/ai-chat/api/compaction.ts +310 -0
- package/src/plugins/ai-chat/api/operations.ts +25 -0
- package/src/plugins/ai-chat/api/plugin.ts +14 -0
- package/src/plugins/ai-chat/client/components/chat-interface.tsx +58 -3
- package/src/plugins/ai-chat/client/components/chat-layout.tsx +197 -72
- package/src/plugins/ai-chat/client/context-checkpoint.ts +47 -0
- package/src/plugins/ai-chat/client/localization/index.ts +5 -0
- package/src/plugins/ai-chat/schemas.ts +2 -0
- package/dist/shared/{stack.CKc_Bais.d.cts → stack.Csi-4Mgz.d.cts} +7 -7
- package/dist/shared/{stack.CKc_Bais.d.mts → stack.Csi-4Mgz.d.mts} +7 -7
- package/dist/shared/{stack.CKc_Bais.d.ts → stack.Csi-4Mgz.d.ts} +7 -7
|
@@ -21,6 +21,7 @@ const chatHooks = require('../hooks/chat-hooks.cjs');
|
|
|
21
21
|
const plugins_aiChat_client_context_pageAiContext = require('../../../../../../../plugins/ai-chat/client/context/page-ai-context.cjs');
|
|
22
22
|
const chatAnalytics = require('../context/chat-analytics.cjs');
|
|
23
23
|
const navigation = require('../navigation.cjs');
|
|
24
|
+
const contextCheckpoint = require('../context-checkpoint.cjs');
|
|
24
25
|
|
|
25
26
|
function reconcilePersistedMessageIds(messages, persistedMessages) {
|
|
26
27
|
const userMessages = messages.filter((message) => message.role === "user");
|
|
@@ -208,6 +209,9 @@ function ChatInterface({
|
|
|
208
209
|
const latestStreamRequestGeneration = React.useRef(0);
|
|
209
210
|
const pendingStreamRequests = React.useRef([]);
|
|
210
211
|
const latestMessages = React.useRef([]);
|
|
212
|
+
const contextCheckpoint$1 = React.useRef(void 0);
|
|
213
|
+
const submittedMessages = React.useRef([]);
|
|
214
|
+
const [isCompacting, setIsCompacting] = React.useState(false);
|
|
211
215
|
const conversationsListQueryKey = React.useMemo(() => {
|
|
212
216
|
if (isPublicMode) return ["ai-chat", "disabled"];
|
|
213
217
|
const client$1 = client.createApiClient({
|
|
@@ -307,6 +311,7 @@ function ChatInterface({
|
|
|
307
311
|
pendingStreamRequests.current.push(request);
|
|
308
312
|
activeStreamPartitionKey.current = request.identityPartitionKey;
|
|
309
313
|
}
|
|
314
|
+
setIsCompacting(false);
|
|
310
315
|
const currentPageContext = pageAIContextRef.current;
|
|
311
316
|
const pageContextBody = currentPageContext?.pageDescription ? {
|
|
312
317
|
pageContext: currentPageContext.pageDescription,
|
|
@@ -319,18 +324,20 @@ function ChatInterface({
|
|
|
319
324
|
if (newUserMessage) {
|
|
320
325
|
messagesToSend.push(newUserMessage);
|
|
321
326
|
}
|
|
327
|
+
submittedMessages.current = messagesToSend;
|
|
322
328
|
editMessagesRef.current = null;
|
|
323
329
|
return {
|
|
324
330
|
body: {
|
|
325
|
-
messages: messagesToSend,
|
|
331
|
+
...isPublicMode ? contextCheckpoint.contextRequest(messagesToSend, contextCheckpoint$1.current) : { messages: messagesToSend },
|
|
326
332
|
conversationId: conversationIdRef.current,
|
|
327
333
|
...pageContextBody
|
|
328
334
|
}
|
|
329
335
|
};
|
|
330
336
|
}
|
|
337
|
+
submittedMessages.current = hookMessages;
|
|
331
338
|
return {
|
|
332
339
|
body: {
|
|
333
|
-
messages: hookMessages,
|
|
340
|
+
...isPublicMode ? contextCheckpoint.contextRequest(hookMessages, contextCheckpoint$1.current) : { messages: hookMessages },
|
|
334
341
|
conversationId: conversationIdRef.current,
|
|
335
342
|
...pageContextBody
|
|
336
343
|
}
|
|
@@ -374,6 +381,20 @@ function ChatInterface({
|
|
|
374
381
|
} = react.useChat({
|
|
375
382
|
id: `${chatInstanceId}:${isPublicMode ? "public" : identitySessionVersion}`,
|
|
376
383
|
transport,
|
|
384
|
+
onData: ({ type, data }) => {
|
|
385
|
+
if (!isPublicMode || !mounted.current || !data || typeof data !== "object")
|
|
386
|
+
return;
|
|
387
|
+
if (type === "data-context-status" && "compacting" in data) {
|
|
388
|
+
setIsCompacting(data.compacting === true);
|
|
389
|
+
}
|
|
390
|
+
if (type === "data-context-checkpoint" && "summary" in data && "throughMessageId" in data && typeof data.summary === "string" && typeof data.throughMessageId === "string") {
|
|
391
|
+
contextCheckpoint$1.current = contextCheckpoint.captureContextCheckpoint(
|
|
392
|
+
submittedMessages.current,
|
|
393
|
+
data.summary,
|
|
394
|
+
data.throughMessageId
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
},
|
|
377
398
|
// Automatically resubmit after all client-side tool results are provided
|
|
378
399
|
sendAutomaticallyWhen: (options) => mounted.current && (isPublicMode || identitySessionVersion === identitySessionGeneration.current) && ai.lastAssistantMessageIsCompleteWithToolCalls(options),
|
|
379
400
|
onToolCall: async ({ toolCall }) => {
|
|
@@ -424,6 +445,7 @@ function ChatInterface({
|
|
|
424
445
|
}
|
|
425
446
|
},
|
|
426
447
|
onError: (err) => {
|
|
448
|
+
setIsCompacting(false);
|
|
427
449
|
if (!mounted.current) return;
|
|
428
450
|
console.error("useChat onError:", err);
|
|
429
451
|
if (!isPublicMode && identitySessionVersion !== identitySessionGeneration.current) {
|
|
@@ -437,6 +459,7 @@ function ChatInterface({
|
|
|
437
459
|
trackFailure();
|
|
438
460
|
},
|
|
439
461
|
onFinish: async (completion) => {
|
|
462
|
+
setIsCompacting(false);
|
|
440
463
|
if (!mounted.current || !isPublicMode && identitySessionVersion !== identitySessionGeneration.current)
|
|
441
464
|
return;
|
|
442
465
|
if (completion?.isError || completion?.isDisconnect) trackFailure();
|
|
@@ -932,7 +955,19 @@ function ChatInterface({
|
|
|
932
955
|
},
|
|
933
956
|
m.id || `msg-${index}`
|
|
934
957
|
)),
|
|
935
|
-
isLoading &&
|
|
958
|
+
isLoading && isCompacting && /* @__PURE__ */ jsxRuntime.jsx(
|
|
959
|
+
"div",
|
|
960
|
+
{
|
|
961
|
+
role: "status",
|
|
962
|
+
className: "text-muted-foreground text-sm py-4",
|
|
963
|
+
children: tr(
|
|
964
|
+
"CHAT_COMPACTING",
|
|
965
|
+
"aiChat.chat.compacting",
|
|
966
|
+
"Summarizing earlier conversation\u2026"
|
|
967
|
+
)
|
|
968
|
+
}
|
|
969
|
+
),
|
|
970
|
+
isLoading && !isCompacting && messages[messages.length - 1]?.role !== "assistant" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 text-muted-foreground text-sm py-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-pulse", children: tr("CHAT_LOADING", "aiChat.chat.loading", "Thinking...") }) }),
|
|
936
971
|
(error || historySyncError) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-destructive text-sm py-4 px-3 bg-destructive/10 rounded-md", children: [
|
|
937
972
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: tr(
|
|
938
973
|
"CHAT_ERROR",
|
|
@@ -19,6 +19,7 @@ import { useAiChatIdentityPartition, useConversation, useConversations } from '.
|
|
|
19
19
|
import { usePageAIContext } from '../../../../../../../plugins/ai-chat/client/context/page-ai-context.mjs';
|
|
20
20
|
import { useChatAnalytics } from '../context/chat-analytics.mjs';
|
|
21
21
|
import { navigateAiChatCrossOrigin } from '../navigation.mjs';
|
|
22
|
+
import { contextRequest, captureContextCheckpoint } from '../context-checkpoint.mjs';
|
|
22
23
|
|
|
23
24
|
function reconcilePersistedMessageIds(messages, persistedMessages) {
|
|
24
25
|
const userMessages = messages.filter((message) => message.role === "user");
|
|
@@ -206,6 +207,9 @@ function ChatInterface({
|
|
|
206
207
|
const latestStreamRequestGeneration = useRef(0);
|
|
207
208
|
const pendingStreamRequests = useRef([]);
|
|
208
209
|
const latestMessages = useRef([]);
|
|
210
|
+
const contextCheckpoint = useRef(void 0);
|
|
211
|
+
const submittedMessages = useRef([]);
|
|
212
|
+
const [isCompacting, setIsCompacting] = useState(false);
|
|
209
213
|
const conversationsListQueryKey = useMemo(() => {
|
|
210
214
|
if (isPublicMode) return ["ai-chat", "disabled"];
|
|
211
215
|
const client = createApiClient({
|
|
@@ -305,6 +309,7 @@ function ChatInterface({
|
|
|
305
309
|
pendingStreamRequests.current.push(request);
|
|
306
310
|
activeStreamPartitionKey.current = request.identityPartitionKey;
|
|
307
311
|
}
|
|
312
|
+
setIsCompacting(false);
|
|
308
313
|
const currentPageContext = pageAIContextRef.current;
|
|
309
314
|
const pageContextBody = currentPageContext?.pageDescription ? {
|
|
310
315
|
pageContext: currentPageContext.pageDescription,
|
|
@@ -317,18 +322,20 @@ function ChatInterface({
|
|
|
317
322
|
if (newUserMessage) {
|
|
318
323
|
messagesToSend.push(newUserMessage);
|
|
319
324
|
}
|
|
325
|
+
submittedMessages.current = messagesToSend;
|
|
320
326
|
editMessagesRef.current = null;
|
|
321
327
|
return {
|
|
322
328
|
body: {
|
|
323
|
-
messages: messagesToSend,
|
|
329
|
+
...isPublicMode ? contextRequest(messagesToSend, contextCheckpoint.current) : { messages: messagesToSend },
|
|
324
330
|
conversationId: conversationIdRef.current,
|
|
325
331
|
...pageContextBody
|
|
326
332
|
}
|
|
327
333
|
};
|
|
328
334
|
}
|
|
335
|
+
submittedMessages.current = hookMessages;
|
|
329
336
|
return {
|
|
330
337
|
body: {
|
|
331
|
-
messages: hookMessages,
|
|
338
|
+
...isPublicMode ? contextRequest(hookMessages, contextCheckpoint.current) : { messages: hookMessages },
|
|
332
339
|
conversationId: conversationIdRef.current,
|
|
333
340
|
...pageContextBody
|
|
334
341
|
}
|
|
@@ -372,6 +379,20 @@ function ChatInterface({
|
|
|
372
379
|
} = useChat({
|
|
373
380
|
id: `${chatInstanceId}:${isPublicMode ? "public" : identitySessionVersion}`,
|
|
374
381
|
transport,
|
|
382
|
+
onData: ({ type, data }) => {
|
|
383
|
+
if (!isPublicMode || !mounted.current || !data || typeof data !== "object")
|
|
384
|
+
return;
|
|
385
|
+
if (type === "data-context-status" && "compacting" in data) {
|
|
386
|
+
setIsCompacting(data.compacting === true);
|
|
387
|
+
}
|
|
388
|
+
if (type === "data-context-checkpoint" && "summary" in data && "throughMessageId" in data && typeof data.summary === "string" && typeof data.throughMessageId === "string") {
|
|
389
|
+
contextCheckpoint.current = captureContextCheckpoint(
|
|
390
|
+
submittedMessages.current,
|
|
391
|
+
data.summary,
|
|
392
|
+
data.throughMessageId
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
},
|
|
375
396
|
// Automatically resubmit after all client-side tool results are provided
|
|
376
397
|
sendAutomaticallyWhen: (options) => mounted.current && (isPublicMode || identitySessionVersion === identitySessionGeneration.current) && lastAssistantMessageIsCompleteWithToolCalls(options),
|
|
377
398
|
onToolCall: async ({ toolCall }) => {
|
|
@@ -422,6 +443,7 @@ function ChatInterface({
|
|
|
422
443
|
}
|
|
423
444
|
},
|
|
424
445
|
onError: (err) => {
|
|
446
|
+
setIsCompacting(false);
|
|
425
447
|
if (!mounted.current) return;
|
|
426
448
|
console.error("useChat onError:", err);
|
|
427
449
|
if (!isPublicMode && identitySessionVersion !== identitySessionGeneration.current) {
|
|
@@ -435,6 +457,7 @@ function ChatInterface({
|
|
|
435
457
|
trackFailure();
|
|
436
458
|
},
|
|
437
459
|
onFinish: async (completion) => {
|
|
460
|
+
setIsCompacting(false);
|
|
438
461
|
if (!mounted.current || !isPublicMode && identitySessionVersion !== identitySessionGeneration.current)
|
|
439
462
|
return;
|
|
440
463
|
if (completion?.isError || completion?.isDisconnect) trackFailure();
|
|
@@ -930,7 +953,19 @@ function ChatInterface({
|
|
|
930
953
|
},
|
|
931
954
|
m.id || `msg-${index}`
|
|
932
955
|
)),
|
|
933
|
-
isLoading &&
|
|
956
|
+
isLoading && isCompacting && /* @__PURE__ */ jsx(
|
|
957
|
+
"div",
|
|
958
|
+
{
|
|
959
|
+
role: "status",
|
|
960
|
+
className: "text-muted-foreground text-sm py-4",
|
|
961
|
+
children: tr(
|
|
962
|
+
"CHAT_COMPACTING",
|
|
963
|
+
"aiChat.chat.compacting",
|
|
964
|
+
"Summarizing earlier conversation\u2026"
|
|
965
|
+
)
|
|
966
|
+
}
|
|
967
|
+
),
|
|
968
|
+
isLoading && !isCompacting && messages[messages.length - 1]?.role !== "assistant" && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 text-muted-foreground text-sm py-4", children: /* @__PURE__ */ jsx("div", { className: "animate-pulse", children: tr("CHAT_LOADING", "aiChat.chat.loading", "Thinking...") }) }),
|
|
934
969
|
(error || historySyncError) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-destructive text-sm py-4 px-3 bg-destructive/10 rounded-md", children: [
|
|
935
970
|
/* @__PURE__ */ jsx("span", { className: "flex-1", children: tr(
|
|
936
971
|
"CHAT_ERROR",
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
const jsxRuntime = require('react/jsx-runtime');
|
|
5
5
|
const React = require('react');
|
|
6
|
+
const ReactDOM = require('react-dom');
|
|
7
|
+
const dialog = require('../../../../../../ui/src/components/dialog.cjs');
|
|
6
8
|
const button = require('../../../../../../ui/src/components/button.cjs');
|
|
7
9
|
const badge = require('../../../../../../ui/src/components/badge.cjs');
|
|
8
10
|
const sheet = require('../../../../../../ui/src/components/sheet.cjs');
|
|
@@ -36,14 +38,44 @@ function ChatLayoutContent(props) {
|
|
|
36
38
|
const resolvedMode = overrides.resolveAiChatMode(plugins?.aiChat?.config);
|
|
37
39
|
const showSidebar = requestedShowSidebar && resolvedMode !== "public";
|
|
38
40
|
const tr = index.useAiChatTranslation(localization);
|
|
39
|
-
const widgetHeight = props.layout === "widget" ? props.widgetHeight ?? "min(
|
|
40
|
-
const widgetWidth = props.layout === "widget" ? props.widgetWidth ?? "min(
|
|
41
|
+
const widgetHeight = props.layout === "widget" ? props.widgetHeight ?? "min(640px, calc(100dvh - 112px))" : void 0;
|
|
42
|
+
const widgetWidth = props.layout === "widget" ? props.widgetWidth ?? "min(440px, calc(100vw - 32px))" : void 0;
|
|
41
43
|
const defaultOpen = props.layout === "widget" ? props.defaultOpen ?? false : false;
|
|
42
44
|
const showTrigger = props.layout === "widget" ? props.showTrigger ?? true : true;
|
|
45
|
+
const triggerIcon = props.layout === "widget" ? props.triggerIcon : void 0;
|
|
43
46
|
const [sidebarOpen, setSidebarOpen] = React.useState(true);
|
|
44
47
|
const [mobileSidebarOpen, setMobileSidebarOpen] = React.useState(false);
|
|
45
48
|
const [chatResetKey, setChatResetKey] = React.useState(0);
|
|
46
49
|
const [widgetOpen, setWidgetOpen] = React.useState(defaultOpen);
|
|
50
|
+
const [expanded, setExpanded] = React.useState(false);
|
|
51
|
+
const expandable = props.layout === "widget" && props.expandable === true;
|
|
52
|
+
const widgetExpanded = expandable && expanded && widgetOpen;
|
|
53
|
+
React.useEffect(() => {
|
|
54
|
+
if (!expandable) setExpanded(false);
|
|
55
|
+
}, [expandable]);
|
|
56
|
+
const compactPanelRef = React.useRef(null);
|
|
57
|
+
const triggerRef = React.useRef(null);
|
|
58
|
+
const expandRef = React.useRef(null);
|
|
59
|
+
const [panelContainer, setPanelContainer] = React.useState(
|
|
60
|
+
null
|
|
61
|
+
);
|
|
62
|
+
const expandLabel = widgetExpanded ? tr("A11Y_COLLAPSE_CHAT", "aiChat.a11y.collapseChat", "Collapse chat") : tr("A11Y_EXPAND_CHAT", "aiChat.a11y.expandChat", "Expand chat");
|
|
63
|
+
React.useEffect(() => {
|
|
64
|
+
if (layout !== "widget") return;
|
|
65
|
+
const container = document.createElement("div");
|
|
66
|
+
container.className = "flex h-full min-h-0 flex-col";
|
|
67
|
+
compactPanelRef.current?.appendChild(container);
|
|
68
|
+
setPanelContainer(container);
|
|
69
|
+
return () => container.remove();
|
|
70
|
+
}, [layout]);
|
|
71
|
+
const mountExpandedPanel = React.useCallback(
|
|
72
|
+
(node) => {
|
|
73
|
+
if (panelContainer) {
|
|
74
|
+
(node ?? compactPanelRef.current)?.appendChild(panelContainer);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
[panelContainer]
|
|
78
|
+
);
|
|
47
79
|
const [widgetResetKey, setWidgetResetKey] = React.useState(0);
|
|
48
80
|
const [widgetEverOpened, setWidgetEverOpened] = React.useState(defaultOpen);
|
|
49
81
|
const [tipDismissed, setTipDismissed] = React.useState(defaultOpen);
|
|
@@ -121,97 +153,166 @@ function ChatLayoutContent(props) {
|
|
|
121
153
|
]
|
|
122
154
|
}
|
|
123
155
|
),
|
|
124
|
-
/* @__PURE__ */ jsxRuntime.
|
|
156
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
125
157
|
"div",
|
|
126
158
|
{
|
|
159
|
+
ref: compactPanelRef,
|
|
160
|
+
role: "dialog",
|
|
161
|
+
tabIndex: -1,
|
|
162
|
+
"aria-label": tr("A11Y_CHAT_TITLE", "aiChat.a11y.title", "AI Chat"),
|
|
163
|
+
className: utils.cn(
|
|
164
|
+
"overflow-hidden rounded-xl border bg-background shadow-xl",
|
|
165
|
+
(!widgetOpen || widgetExpanded) && "hidden"
|
|
166
|
+
),
|
|
167
|
+
style: { height: widgetHeight, width: widgetWidth }
|
|
168
|
+
}
|
|
169
|
+
),
|
|
170
|
+
/* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: widgetExpanded, onOpenChange: setExpanded, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
171
|
+
dialog.DialogContent,
|
|
172
|
+
{
|
|
173
|
+
showCloseButton: false,
|
|
174
|
+
style: {
|
|
175
|
+
...style,
|
|
176
|
+
position: "fixed",
|
|
177
|
+
inset: 0,
|
|
178
|
+
top: 0,
|
|
179
|
+
right: 0,
|
|
180
|
+
bottom: 0,
|
|
181
|
+
left: 0,
|
|
182
|
+
width: "100vw",
|
|
183
|
+
height: "100dvh",
|
|
184
|
+
minWidth: 0,
|
|
185
|
+
minHeight: 0,
|
|
186
|
+
maxWidth: "none",
|
|
187
|
+
maxHeight: "none",
|
|
188
|
+
margin: 0,
|
|
189
|
+
transform: "none",
|
|
190
|
+
translate: "none",
|
|
191
|
+
zIndex: 50,
|
|
192
|
+
animation: "none"
|
|
193
|
+
},
|
|
194
|
+
"aria-describedby": void 0,
|
|
127
195
|
className: utils.cn(
|
|
128
|
-
|
|
129
|
-
|
|
196
|
+
className,
|
|
197
|
+
classNames?.container,
|
|
198
|
+
"fixed inset-0 z-50 flex h-dvh w-screen max-w-none translate-x-0 translate-y-0 flex-col gap-0 rounded-none border-0 p-0 sm:max-w-none pt-[env(safe-area-inset-top)] pb-[env(safe-area-inset-bottom)] pl-[env(safe-area-inset-left)] pr-[env(safe-area-inset-right)]"
|
|
130
199
|
),
|
|
131
|
-
|
|
200
|
+
onCloseAutoFocus: (event) => {
|
|
201
|
+
const target = widgetOpen ? expandRef.current ?? compactPanelRef.current : triggerRef.current;
|
|
202
|
+
if (target) {
|
|
203
|
+
event.preventDefault();
|
|
204
|
+
target.focus();
|
|
205
|
+
}
|
|
206
|
+
},
|
|
132
207
|
children: [
|
|
133
|
-
/* @__PURE__ */ jsxRuntime.
|
|
134
|
-
|
|
135
|
-
pageAIContext ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
136
|
-
badge.Badge,
|
|
137
|
-
{
|
|
138
|
-
variant: "secondary",
|
|
139
|
-
className: "text-xs",
|
|
140
|
-
"data-testid": "page-context-badge",
|
|
141
|
-
children: pageAIContext.routeName
|
|
142
|
-
}
|
|
143
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground font-medium", children: tr("A11Y_CHAT_TITLE", "aiChat.a11y.title", "AI Chat") }),
|
|
144
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" }),
|
|
145
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
146
|
-
button.Button,
|
|
147
|
-
{
|
|
148
|
-
variant: "ghost",
|
|
149
|
-
size: "icon",
|
|
150
|
-
className: "h-5 w-5",
|
|
151
|
-
onClick: () => {
|
|
152
|
-
track({ type: "conversation_cleared" });
|
|
153
|
-
onClear?.();
|
|
154
|
-
setWidgetResetKey((prev) => prev + 1);
|
|
155
|
-
},
|
|
156
|
-
"aria-label": tr(
|
|
157
|
-
"A11Y_CLEAR_CHAT",
|
|
158
|
-
"aiChat.a11y.clearChat",
|
|
159
|
-
"Clear chat"
|
|
160
|
-
),
|
|
161
|
-
title: tr(
|
|
162
|
-
"A11Y_CLEAR_CHAT",
|
|
163
|
-
"aiChat.a11y.clearChat",
|
|
164
|
-
"Clear chat"
|
|
165
|
-
),
|
|
166
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Trash2, { className: "h-3.5 w-3.5" })
|
|
167
|
-
}
|
|
168
|
-
),
|
|
169
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
170
|
-
button.Button,
|
|
171
|
-
{
|
|
172
|
-
variant: "ghost",
|
|
173
|
-
size: "icon",
|
|
174
|
-
className: "h-5 w-5",
|
|
175
|
-
onClick: () => {
|
|
176
|
-
setWidgetOpen(false);
|
|
177
|
-
track({ type: "widget_closed" });
|
|
178
|
-
},
|
|
179
|
-
"aria-label": tr(
|
|
180
|
-
"A11Y_CLOSE_CHAT",
|
|
181
|
-
"aiChat.a11y.closeChat",
|
|
182
|
-
"Close chat"
|
|
183
|
-
),
|
|
184
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.X, { className: "h-3.5 w-3.5" })
|
|
185
|
-
}
|
|
186
|
-
)
|
|
187
|
-
] }),
|
|
188
|
-
widgetEverOpened && /* @__PURE__ */ jsxRuntime.jsx(
|
|
189
|
-
chatInterface.ChatInterface,
|
|
190
|
-
{
|
|
191
|
-
id: conversationId,
|
|
192
|
-
variant: "widget",
|
|
193
|
-
initialMessages,
|
|
194
|
-
onMessagesChange
|
|
195
|
-
},
|
|
196
|
-
`widget-${conversationId ?? "new"}-${widgetResetKey}`
|
|
197
|
-
)
|
|
208
|
+
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { className: "sr-only", children: tr("A11Y_CHAT_TITLE", "aiChat.a11y.title", "AI Chat") }),
|
|
209
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { ref: mountExpandedPanel, className: "min-h-0 flex-1" })
|
|
198
210
|
]
|
|
199
211
|
}
|
|
212
|
+
) }),
|
|
213
|
+
panelContainer && ReactDOM.createPortal(
|
|
214
|
+
/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
215
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-1.5 px-3 py-1.5 border-b bg-muted/40", children: [
|
|
216
|
+
/* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Sparkles, { className: "h-3 w-3 text-muted-foreground" }),
|
|
217
|
+
pageAIContext ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
218
|
+
badge.Badge,
|
|
219
|
+
{
|
|
220
|
+
variant: "secondary",
|
|
221
|
+
className: "text-xs",
|
|
222
|
+
"data-testid": "page-context-badge",
|
|
223
|
+
children: pageAIContext.routeName
|
|
224
|
+
}
|
|
225
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground font-medium", children: tr("A11Y_CHAT_TITLE", "aiChat.a11y.title", "AI Chat") }),
|
|
226
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" }),
|
|
227
|
+
expandable && /* @__PURE__ */ jsxRuntime.jsx(
|
|
228
|
+
button.Button,
|
|
229
|
+
{
|
|
230
|
+
type: "button",
|
|
231
|
+
variant: "ghost",
|
|
232
|
+
size: "icon",
|
|
233
|
+
className: "h-7 w-7",
|
|
234
|
+
ref: expandRef,
|
|
235
|
+
onClick: () => setExpanded((prev) => !prev),
|
|
236
|
+
"aria-label": expandLabel,
|
|
237
|
+
title: expandLabel,
|
|
238
|
+
"aria-expanded": widgetExpanded,
|
|
239
|
+
children: widgetExpanded ? /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Minimize2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) : /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Maximize2, { className: "h-3.5 w-3.5", "aria-hidden": "true" })
|
|
240
|
+
}
|
|
241
|
+
),
|
|
242
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
243
|
+
button.Button,
|
|
244
|
+
{
|
|
245
|
+
variant: "ghost",
|
|
246
|
+
size: "icon",
|
|
247
|
+
className: "h-5 w-5",
|
|
248
|
+
onClick: () => {
|
|
249
|
+
track({ type: "conversation_cleared" });
|
|
250
|
+
onClear?.();
|
|
251
|
+
setWidgetResetKey((prev) => prev + 1);
|
|
252
|
+
},
|
|
253
|
+
"aria-label": tr(
|
|
254
|
+
"A11Y_CLEAR_CHAT",
|
|
255
|
+
"aiChat.a11y.clearChat",
|
|
256
|
+
"Clear chat"
|
|
257
|
+
),
|
|
258
|
+
title: tr(
|
|
259
|
+
"A11Y_CLEAR_CHAT",
|
|
260
|
+
"aiChat.a11y.clearChat",
|
|
261
|
+
"Clear chat"
|
|
262
|
+
),
|
|
263
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Trash2, { className: "h-3.5 w-3.5" })
|
|
264
|
+
}
|
|
265
|
+
),
|
|
266
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
267
|
+
button.Button,
|
|
268
|
+
{
|
|
269
|
+
variant: "ghost",
|
|
270
|
+
size: "icon",
|
|
271
|
+
className: "h-5 w-5",
|
|
272
|
+
onClick: () => {
|
|
273
|
+
setWidgetOpen(false);
|
|
274
|
+
setExpanded(false);
|
|
275
|
+
triggerRef.current?.focus();
|
|
276
|
+
track({ type: "widget_closed" });
|
|
277
|
+
},
|
|
278
|
+
"aria-label": tr(
|
|
279
|
+
"A11Y_CLOSE_CHAT",
|
|
280
|
+
"aiChat.a11y.closeChat",
|
|
281
|
+
"Close chat"
|
|
282
|
+
),
|
|
283
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.X, { className: "h-3.5 w-3.5" })
|
|
284
|
+
}
|
|
285
|
+
)
|
|
286
|
+
] }),
|
|
287
|
+
widgetEverOpened && /* @__PURE__ */ jsxRuntime.jsx(
|
|
288
|
+
chatInterface.ChatInterface,
|
|
289
|
+
{
|
|
290
|
+
id: conversationId,
|
|
291
|
+
variant: "widget",
|
|
292
|
+
initialMessages,
|
|
293
|
+
onMessagesChange
|
|
294
|
+
},
|
|
295
|
+
`widget-${conversationId ?? "new"}-${widgetResetKey}`
|
|
296
|
+
)
|
|
297
|
+
] }),
|
|
298
|
+
panelContainer
|
|
200
299
|
),
|
|
201
300
|
showTrigger && /* @__PURE__ */ jsxRuntime.jsx(
|
|
202
301
|
button.Button,
|
|
203
302
|
{
|
|
303
|
+
ref: triggerRef,
|
|
204
304
|
size: "icon",
|
|
205
305
|
className: "h-12 w-12 rounded-full shadow-lg",
|
|
206
306
|
onClick: () => {
|
|
207
307
|
dismissTip("open");
|
|
208
308
|
track({ type: widgetOpen ? "widget_closed" : "widget_opened" });
|
|
209
309
|
setWidgetOpen((prev) => !prev);
|
|
310
|
+
setExpanded(false);
|
|
210
311
|
setWidgetEverOpened(true);
|
|
211
312
|
},
|
|
212
313
|
"aria-label": widgetOpen ? tr("A11Y_CLOSE_CHAT", "aiChat.a11y.closeChat", "Close chat") : tr("A11Y_OPEN_CHAT", "aiChat.a11y.openChat", "Open chat"),
|
|
213
314
|
"data-testid": "widget-trigger",
|
|
214
|
-
children: widgetOpen ? /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.X, { className: "h-5 w-5" }) : /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Sparkles, { className: "h-5 w-5" })
|
|
315
|
+
children: widgetOpen ? /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.X, { className: "h-5 w-5" }) : triggerIcon ?? /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Sparkles, { className: "h-5 w-5" })
|
|
215
316
|
}
|
|
216
317
|
)
|
|
217
318
|
]
|