@case-framework/survey-assistant 0.2.3 → 0.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 +12 -4
- package/dist/{authoring-references-ChDTVnms.mjs → authoring-references-yyYC4Ydi.mjs} +2 -2
- package/dist/{authoring-references-ChDTVnms.mjs.map → authoring-references-yyYC4Ydi.mjs.map} +1 -1
- package/dist/capabilities-default.d.mts +1 -1
- package/dist/capabilities-default.mjs +1 -1
- package/dist/{constants-BK1-g6pR.d.mts → constants-k1b_evIb.d.mts} +7 -4
- package/dist/constants-k1b_evIb.d.mts.map +1 -0
- package/dist/{controller-proxy-DY36XFV3.mjs → controller-proxy-jWIPOZAv.mjs} +18 -8
- package/dist/controller-proxy-jWIPOZAv.mjs.map +1 -0
- package/dist/{controller-proxy-CIDRd8Wu.d.mts → controller-proxy-quQjtA7j.d.mts} +5 -2
- package/dist/controller-proxy-quQjtA7j.d.mts.map +1 -0
- package/dist/{engine-CWTuN3o2.mjs → engine-BvfUx_Jx.mjs} +85 -12
- package/dist/engine-BvfUx_Jx.mjs.map +1 -0
- package/dist/engine.d.mts +2 -2
- package/dist/engine.mjs +3 -3
- package/dist/{form-authoring-DCd9XfXf.mjs → form-authoring-BQh40yDb.mjs} +2 -2
- package/dist/{form-authoring-DCd9XfXf.mjs.map → form-authoring-BQh40yDb.mjs.map} +1 -1
- package/dist/index-8x1Eqlnz.d.mts +741 -0
- package/dist/index-8x1Eqlnz.d.mts.map +1 -0
- package/dist/{index-SPHf3OoJ.d.mts → index-BDp6S7r9.d.mts} +14 -5
- package/dist/index-BDp6S7r9.d.mts.map +1 -0
- package/dist/{index-Bp5W_oPa.d.mts → index-biZnRl-h.d.mts} +26 -24
- package/dist/index-biZnRl-h.d.mts.map +1 -0
- package/dist/{index-CPOQgkTN.d.mts → index-r8HVcn5b.d.mts} +9 -4
- package/dist/index-r8HVcn5b.d.mts.map +1 -0
- package/dist/lifecycle-DWv5CiSu.d.mts +523 -0
- package/dist/lifecycle-DWv5CiSu.d.mts.map +1 -0
- package/dist/{memory-thread-repository-acbz6HZJ.mjs → memory-thread-repository-xBZOog_q.mjs} +49 -21
- package/dist/memory-thread-repository-xBZOog_q.mjs.map +1 -0
- package/dist/{protocol-DATvkr1S.mjs → protocol-DJUP4gc0.mjs} +10 -4
- package/dist/protocol-DJUP4gc0.mjs.map +1 -0
- package/dist/protocol.d.mts +2 -1
- package/dist/protocol.mjs +1 -1
- package/dist/{react-3kSE6WtS.mjs → react-CY6N7lVi.mjs} +30 -8
- package/dist/react-CY6N7lVi.mjs.map +1 -0
- package/dist/react-integration.d.mts +1 -1
- package/dist/react-integration.mjs +1 -1
- package/dist/react.d.mts +2 -2
- package/dist/react.mjs +2 -2
- package/dist/{request-context-BEjnaaFr.mjs → request-context-DixY5DYu.mjs} +3 -3
- package/dist/request-context-DixY5DYu.mjs.map +1 -0
- package/dist/server-agent.d.mts +26 -24
- package/dist/server-agent.d.mts.map +1 -1
- package/dist/server-agent.mjs +7 -6
- package/dist/server-agent.mjs.map +1 -1
- package/dist/server-runtime.d.mts +341 -8
- package/dist/server-runtime.d.mts.map +1 -1
- package/dist/server-runtime.mjs +321 -23
- package/dist/server-runtime.mjs.map +1 -1
- package/dist/server-tasks.d.mts +1 -1
- package/dist/server-tasks.mjs +8 -6
- package/dist/server-tasks.mjs.map +1 -1
- package/dist/server-tools.d.mts +1 -1
- package/dist/server-tools.mjs +1 -1
- package/dist/server.d.mts +8 -8
- package/dist/server.d.mts.map +1 -1
- package/dist/server.mjs +3 -3
- package/dist/storage-postgres.d.mts +1 -1
- package/dist/{tasks-DIZgr4nD.d.mts → tasks-BWmTMXoK.d.mts} +3 -3
- package/dist/{tasks-DIZgr4nD.d.mts.map → tasks-BWmTMXoK.d.mts.map} +1 -1
- package/dist/{thread-documents-C3kf0Vds.d.mts → thread-documents-Dvo6XS7o.d.mts} +1 -1
- package/dist/{thread-documents-C3kf0Vds.d.mts.map → thread-documents-Dvo6XS7o.d.mts.map} +1 -1
- package/dist/{thread-handlers-DZ1WsorO.d.mts → thread-handlers-D8gkLfU6.d.mts} +15 -7
- package/dist/{thread-handlers-DZ1WsorO.d.mts.map → thread-handlers-D8gkLfU6.d.mts.map} +1 -1
- package/dist/{tools-GDd5_GUh.mjs → tools-TXI8bfzZ.mjs} +28 -9
- package/dist/tools-TXI8bfzZ.mjs.map +1 -0
- package/dist/ui.css +2 -2
- package/dist/ui.d.mts +94 -10
- package/dist/ui.d.mts.map +1 -1
- package/dist/ui.mjs +561 -203
- package/dist/ui.mjs.map +1 -1
- package/docs/integration.md +84 -63
- package/package.json +25 -19
- package/dist/constants-BK1-g6pR.d.mts.map +0 -1
- package/dist/controller-proxy-CIDRd8Wu.d.mts.map +0 -1
- package/dist/controller-proxy-DY36XFV3.mjs.map +0 -1
- package/dist/engine-CWTuN3o2.mjs.map +0 -1
- package/dist/index-Bp5W_oPa.d.mts.map +0 -1
- package/dist/index-CPOQgkTN.d.mts.map +0 -1
- package/dist/index-EDL1V168.d.mts +0 -1257
- package/dist/index-EDL1V168.d.mts.map +0 -1
- package/dist/index-SPHf3OoJ.d.mts.map +0 -1
- package/dist/memory-thread-repository-acbz6HZJ.mjs.map +0 -1
- package/dist/protocol-DATvkr1S.mjs.map +0 -1
- package/dist/react-3kSE6WtS.mjs.map +0 -1
- package/dist/request-context-BEjnaaFr.mjs.map +0 -1
- package/dist/tools-GDd5_GUh.mjs.map +0 -1
package/dist/ui.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jt as parseSurveyAssistantError, l as surveyAssistantTurnFinalSchema, n as SURVEY_ASSISTANT_TURN_FINAL_PART_TYPE } from "./protocol-
|
|
3
|
-
import { f as unwrapSurveyAssistantToolOutput, i as createSurveyAssistantThreadClient, l as parseSurveyChangeSet, p as isSurveyAssistantTurnActive, r as useSurveyAssistantModelCatalog, t as useSurveyAssistantSession } from "./react-
|
|
4
|
-
import { o as connectSurveyAssistantThreadLauncher } from "./controller-proxy-
|
|
2
|
+
import { jt as parseSurveyAssistantError, l as surveyAssistantTurnFinalSchema, n as SURVEY_ASSISTANT_TURN_FINAL_PART_TYPE } from "./protocol-DJUP4gc0.mjs";
|
|
3
|
+
import { f as unwrapSurveyAssistantToolOutput, i as createSurveyAssistantThreadClient, l as parseSurveyChangeSet, p as isSurveyAssistantTurnActive, r as useSurveyAssistantModelCatalog, t as useSurveyAssistantSession } from "./react-CY6N7lVi.mjs";
|
|
4
|
+
import { o as connectSurveyAssistantThreadLauncher } from "./controller-proxy-jWIPOZAv.mjs";
|
|
5
5
|
import { getToolName, isFileUIPart, isReasoningUIPart, isToolUIPart } from "ai";
|
|
6
6
|
import { createContext, createElement, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
7
7
|
import { Collapsible, Select, Slot } from "radix-ui";
|
|
@@ -9,7 +9,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
9
9
|
import { clsx } from "clsx";
|
|
10
10
|
import { twMerge } from "tailwind-merge";
|
|
11
11
|
import { useControllableState } from "@radix-ui/react-use-controllable-state";
|
|
12
|
-
import { ArrowDownIcon, ArrowUpIcon, BotIcon, BrainIcon, CheckCircleIcon, CheckIcon, ChevronDownIcon, ChevronRightIcon, ChevronUpIcon, ClockIcon, DotIcon, FileIcon, HistoryIcon, ListChecksIcon, LoaderCircleIcon, MessageSquarePlusIcon, PaperclipIcon, PencilIcon, PlusIcon, SparklesIcon, SquareIcon, Trash2Icon, WandSparklesIcon, WrenchIcon, XCircleIcon, XIcon } from "lucide-react";
|
|
12
|
+
import { ArrowDownIcon, ArrowUpIcon, BotIcon, BrainIcon, CheckCircleIcon, CheckIcon, ChevronDownIcon, ChevronRightIcon, ChevronUpIcon, ClockIcon, DotIcon, FileIcon, HistoryIcon, ListChecksIcon, LoaderCircleIcon, MessageSquarePlusIcon, PaperclipIcon, PencilIcon, PlusIcon, SearchIcon, SparklesIcon, SquareIcon, Trash2Icon, WandSparklesIcon, WrenchIcon, XCircleIcon, XIcon } from "lucide-react";
|
|
13
13
|
import { motion } from "motion/react";
|
|
14
14
|
import { createLocalizationTranslator, mergeLocalizationCatalogs } from "@case-framework/ui-localization";
|
|
15
15
|
import { cva } from "class-variance-authority";
|
|
@@ -80,13 +80,30 @@ const surveyAssistantEnMessages = {
|
|
|
80
80
|
},
|
|
81
81
|
empty: {
|
|
82
82
|
title: "What are you working on?",
|
|
83
|
-
description: "Draft, inspect, or refine your survey - just describe what you need, or start with one of these."
|
|
83
|
+
description: "Draft, inspect, or refine your survey - just describe what you need, or start with one of these.",
|
|
84
|
+
newSurveyTitle: "Create with the assistant",
|
|
85
|
+
newSurveyDescription: "Describe the survey you need, share a brief, or plan the right questions together."
|
|
84
86
|
},
|
|
85
87
|
quickActions: {
|
|
88
|
+
draftSurvey: {
|
|
89
|
+
label: "Draft a survey",
|
|
90
|
+
description: "Create a first draft from your goals",
|
|
91
|
+
prompt: "Help me create a first draft of a new survey. Ask me about its topic, audience, and goals before drafting it."
|
|
92
|
+
},
|
|
93
|
+
useBrief: {
|
|
94
|
+
label: "Use a brief or notes",
|
|
95
|
+
description: "Turn requirements into a survey",
|
|
96
|
+
prompt: "Help me turn a brief or set of notes into a survey. Ask me to paste the text or attach the document."
|
|
97
|
+
},
|
|
98
|
+
planSurvey: {
|
|
99
|
+
label: "Plan the questions",
|
|
100
|
+
description: "Outline the structure before drafting",
|
|
101
|
+
prompt: "Help me plan the questions and structure for a new survey before we draft it."
|
|
102
|
+
},
|
|
86
103
|
review: {
|
|
87
104
|
label: "Review survey",
|
|
88
105
|
description: "Spot issues and suggest improvements",
|
|
89
|
-
prompt: "
|
|
106
|
+
prompt: "Run a deterministic audit of the current survey, including expression-reference integrity, then review its quality and suggest improvements."
|
|
90
107
|
},
|
|
91
108
|
create: {
|
|
92
109
|
label: "New question",
|
|
@@ -97,6 +114,11 @@ const surveyAssistantEnMessages = {
|
|
|
97
114
|
label: "Refine wording",
|
|
98
115
|
description: "Polish respondent-facing text",
|
|
99
116
|
prompt: "Suggest improvements to the respondent-facing wording."
|
|
117
|
+
},
|
|
118
|
+
refineSelection: {
|
|
119
|
+
label: "Refine selected item",
|
|
120
|
+
description: "Polish the wording in context",
|
|
121
|
+
prompt: "Suggest improvements to the respondent-facing wording of the selected item."
|
|
100
122
|
}
|
|
101
123
|
},
|
|
102
124
|
input: {
|
|
@@ -111,6 +133,8 @@ const surveyAssistantEnMessages = {
|
|
|
111
133
|
defaultModel: "Default model"
|
|
112
134
|
},
|
|
113
135
|
conversation: {
|
|
136
|
+
loadEarlier: "Load earlier messages",
|
|
137
|
+
loadingEarlier: "Loading earlier messages…",
|
|
114
138
|
noMessages: "No messages yet",
|
|
115
139
|
startConversation: "Start a conversation to see messages here",
|
|
116
140
|
scrollToLatest: "Scroll to latest message",
|
|
@@ -246,6 +270,11 @@ const surveyAssistantEnMessages = {
|
|
|
246
270
|
newChat: "New chat",
|
|
247
271
|
history: "Chat history",
|
|
248
272
|
recent: "Recent chats",
|
|
273
|
+
search: "Search chats",
|
|
274
|
+
searching: "Searching older chats…",
|
|
275
|
+
noMatches: "No matching chats",
|
|
276
|
+
loadOlder: "Load older chats",
|
|
277
|
+
loadingOlder: "Loading older chats…",
|
|
249
278
|
automaticTitle: "Use automatic title",
|
|
250
279
|
automaticShort: "Auto",
|
|
251
280
|
rename: "Rename {title}",
|
|
@@ -266,13 +295,30 @@ const surveyAssistantNlMessages = {
|
|
|
266
295
|
},
|
|
267
296
|
empty: {
|
|
268
297
|
title: "Waar werk je aan?",
|
|
269
|
-
description: "Maak, controleer of verbeter je survey. Beschrijf wat je nodig hebt of kies een van deze opties."
|
|
298
|
+
description: "Maak, controleer of verbeter je survey. Beschrijf wat je nodig hebt of kies een van deze opties.",
|
|
299
|
+
newSurveyTitle: "Maak iets met de assistent",
|
|
300
|
+
newSurveyDescription: "Beschrijf de survey die je nodig hebt, deel een briefing of plan samen de juiste vragen."
|
|
270
301
|
},
|
|
271
302
|
quickActions: {
|
|
303
|
+
draftSurvey: {
|
|
304
|
+
label: "Een survey opstellen",
|
|
305
|
+
description: "Maak een eerste versie vanuit je doelen",
|
|
306
|
+
prompt: "Help me een eerste versie van een nieuwe survey te maken. Vraag me vóór het opstellen naar het onderwerp, de doelgroep en de doelen."
|
|
307
|
+
},
|
|
308
|
+
useBrief: {
|
|
309
|
+
label: "Gebruik een briefing of notities",
|
|
310
|
+
description: "Zet vereisten om in een survey",
|
|
311
|
+
prompt: "Help me een briefing of verzameling notities om te zetten in een survey. Vraag me de tekst te plakken of het document toe te voegen."
|
|
312
|
+
},
|
|
313
|
+
planSurvey: {
|
|
314
|
+
label: "Plan de vragen",
|
|
315
|
+
description: "Schets eerst de structuur",
|
|
316
|
+
prompt: "Help me de vragen en structuur voor een nieuwe survey te plannen voordat we die opstellen."
|
|
317
|
+
},
|
|
272
318
|
review: {
|
|
273
319
|
label: "Survey controleren",
|
|
274
320
|
description: "Problemen vinden en verbeteringen voorstellen",
|
|
275
|
-
prompt: "
|
|
321
|
+
prompt: "Voer een deterministische controle van de huidige survey uit, inclusief de geldigheid van expressieverwijzingen, beoordeel daarna de kwaliteit en stel verbeteringen voor."
|
|
276
322
|
},
|
|
277
323
|
create: {
|
|
278
324
|
label: "Nieuwe vraag",
|
|
@@ -283,6 +329,11 @@ const surveyAssistantNlMessages = {
|
|
|
283
329
|
label: "Formulering verbeteren",
|
|
284
330
|
description: "Tekst voor respondenten aanscherpen",
|
|
285
331
|
prompt: "Stel verbeteringen voor voor de tekst die respondenten zien."
|
|
332
|
+
},
|
|
333
|
+
refineSelection: {
|
|
334
|
+
label: "Geselecteerd item verbeteren",
|
|
335
|
+
description: "Scherp de formulering in context aan",
|
|
336
|
+
prompt: "Stel verbeteringen voor voor de respondentgerichte formulering van het geselecteerde item."
|
|
286
337
|
}
|
|
287
338
|
},
|
|
288
339
|
input: {
|
|
@@ -297,6 +348,8 @@ const surveyAssistantNlMessages = {
|
|
|
297
348
|
defaultModel: "Standaardmodel"
|
|
298
349
|
},
|
|
299
350
|
conversation: {
|
|
351
|
+
loadEarlier: "Eerdere berichten laden",
|
|
352
|
+
loadingEarlier: "Eerdere berichten laden…",
|
|
300
353
|
noMessages: "Nog geen berichten",
|
|
301
354
|
startConversation: "Start een gesprek om hier berichten te zien",
|
|
302
355
|
scrollToLatest: "Naar het nieuwste bericht scrollen",
|
|
@@ -432,6 +485,11 @@ const surveyAssistantNlMessages = {
|
|
|
432
485
|
newChat: "Nieuwe chat",
|
|
433
486
|
history: "Chatgeschiedenis",
|
|
434
487
|
recent: "Recente chats",
|
|
488
|
+
search: "Chats zoeken",
|
|
489
|
+
searching: "Oudere chats zoeken…",
|
|
490
|
+
noMatches: "Geen overeenkomende chats",
|
|
491
|
+
loadOlder: "Oudere chats laden",
|
|
492
|
+
loadingOlder: "Oudere chats laden…",
|
|
435
493
|
automaticTitle: "Automatische titel gebruiken",
|
|
436
494
|
automaticShort: "Automatisch",
|
|
437
495
|
rename: "{title} hernoemen",
|
|
@@ -708,7 +766,7 @@ const Message = ({ className, from, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
|
708
766
|
...props
|
|
709
767
|
});
|
|
710
768
|
const MessageContent = ({ children, className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
711
|
-
className: cn("w-fit min-w-0 max-w-full break-words text-sm", "group-data-[role=user]:whitespace-pre-wrap group-data-[role=user]:rounded-xl group-data-[role=user]:border group-data-[role=user]:border-blue-200/70 group-data-[role=user]:bg-blue-50 group-data-[role=user]:px-3 group-data-[role=user]:py-2 group-data-[role=user]:text-foreground group-data-[role=user]:shadow-xs dark:group-data-[role=user]:border-blue-800/60 dark:group-data-[role=user]:bg-blue-950/50", "group-data-[role=assistant]:text-foreground", className),
|
|
769
|
+
className: cn("w-fit min-w-0 max-w-full break-words text-sm", "group-data-[role=user]:whitespace-pre-wrap group-data-[role=user]:rounded-xl group-data-[role=user]:border group-data-[role=user]:border-blue-200/70 group-data-[role=user]:bg-blue-50/80 group-data-[role=user]:px-3 group-data-[role=user]:py-2 group-data-[role=user]:text-foreground group-data-[role=user]:shadow-xs dark:group-data-[role=user]:border-blue-800/60 dark:group-data-[role=user]:bg-blue-950/50", "group-data-[role=assistant]:text-foreground", className),
|
|
712
770
|
...props,
|
|
713
771
|
children
|
|
714
772
|
});
|
|
@@ -739,13 +797,18 @@ const usePromptInput = () => {
|
|
|
739
797
|
if (!context) throw new Error("PromptInput components must be used within PromptInput.");
|
|
740
798
|
return context;
|
|
741
799
|
};
|
|
742
|
-
const PromptInput = ({ attachmentsDisabled = false, children, className, onDragEnter, onDragLeave, onDragOver, onDrop, onPaste, onSubmit, ...props }) => {
|
|
743
|
-
const [
|
|
800
|
+
const PromptInput = ({ attachmentsDisabled = false, children, className, onDragEnter, onDragLeave, onDragOver, onDrop, onPaste, onSubmit, onValueChange, value: controlledValue, ...props }) => {
|
|
801
|
+
const [uncontrolledValue, setUncontrolledValue] = useState("");
|
|
744
802
|
const [files, setFiles] = useState([]);
|
|
745
803
|
const [isDraggingFiles, setIsDraggingFiles] = useState(false);
|
|
746
804
|
const [unsupportedFileNames, setUnsupportedFileNames] = useState([]);
|
|
747
805
|
const fileDragDepthRef = useRef(0);
|
|
748
806
|
const t = useSurveyAssistantTranslation();
|
|
807
|
+
const value = controlledValue ?? uncontrolledValue;
|
|
808
|
+
const setValue = useCallback((nextValue) => {
|
|
809
|
+
if (controlledValue === void 0) setUncontrolledValue(nextValue);
|
|
810
|
+
onValueChange?.(nextValue);
|
|
811
|
+
}, [controlledValue, onValueChange]);
|
|
749
812
|
const addFiles = useCallback((fileList) => {
|
|
750
813
|
const nextFiles = Array.from(fileList ?? []);
|
|
751
814
|
const supportedFiles = nextFiles.filter(isSupportedFile);
|
|
@@ -947,15 +1010,6 @@ const PromptInputSubmit = ({ className, status, onStop, onClick, ...props }) =>
|
|
|
947
1010
|
});
|
|
948
1011
|
};
|
|
949
1012
|
//#endregion
|
|
950
|
-
//#region src/ui/internal/tool-technical-details.ts
|
|
951
|
-
const formatOmittedLength = (length) => length.toLocaleString("en-US");
|
|
952
|
-
const stringifyToolTechnicalDetails = (value) => JSON.stringify(value, function redactEmbeddedBinary(key, currentValue) {
|
|
953
|
-
const parent = this && typeof this === "object" ? this : void 0;
|
|
954
|
-
if (key === "data" && typeof currentValue === "string" && parent?.type === "embedded" && parent.encoding === "base64") return `[embedded base64 omitted: ${formatOmittedLength(currentValue.length)} characters]`;
|
|
955
|
-
if (typeof currentValue === "string" && currentValue.length > 512 && /^data:[^;,]+;base64,/.test(currentValue)) return `[base64 data URL omitted: ${formatOmittedLength(currentValue.length)} characters]`;
|
|
956
|
-
return currentValue;
|
|
957
|
-
}, 2);
|
|
958
|
-
//#endregion
|
|
959
1013
|
//#region src/ui/internal/select.tsx
|
|
960
1014
|
function Select$1({ ...props }) {
|
|
961
1015
|
return /* @__PURE__ */ jsx(Select.Root, {
|
|
@@ -1030,6 +1084,88 @@ function SelectScrollDownButton({ className, ...props }) {
|
|
|
1030
1084
|
});
|
|
1031
1085
|
}
|
|
1032
1086
|
//#endregion
|
|
1087
|
+
//#region src/ui/survey-assistant-prompt-input.tsx
|
|
1088
|
+
const SurveyAssistantModelSelect = ({ disabled = false, modelId, models, onModelChange }) => {
|
|
1089
|
+
const t = useSurveyAssistantTranslation();
|
|
1090
|
+
const selectedModelLabel = models.find((model) => model.id === modelId)?.label ?? modelId ?? t("input.defaultModel");
|
|
1091
|
+
if (models.length > 1) return /* @__PURE__ */ jsxs(Select$1, {
|
|
1092
|
+
disabled,
|
|
1093
|
+
onValueChange: onModelChange,
|
|
1094
|
+
value: modelId ?? "",
|
|
1095
|
+
children: [/* @__PURE__ */ jsxs(SelectTrigger, {
|
|
1096
|
+
"aria-label": t("input.model"),
|
|
1097
|
+
className: "h-8 min-w-0 max-w-48 gap-1.5 border-0 bg-transparent px-2 text-xs shadow-none hover:bg-muted hover:text-foreground focus-visible:ring-1",
|
|
1098
|
+
size: "sm",
|
|
1099
|
+
title: modelId,
|
|
1100
|
+
children: [/* @__PURE__ */ jsx(BotIcon, { className: "size-3.5" }), /* @__PURE__ */ jsx(SelectValue, {})]
|
|
1101
|
+
}), /* @__PURE__ */ jsx(SelectContent, {
|
|
1102
|
+
align: "end",
|
|
1103
|
+
className: "min-w-52 rounded-lg border-border p-0 shadow-lg",
|
|
1104
|
+
position: "popper",
|
|
1105
|
+
children: models.map((model) => /* @__PURE__ */ jsx(SelectItem, {
|
|
1106
|
+
className: "min-h-9 rounded-md px-2 py-2 text-xs focus:bg-muted",
|
|
1107
|
+
title: model.id,
|
|
1108
|
+
value: model.id,
|
|
1109
|
+
children: model.label
|
|
1110
|
+
}, model.id))
|
|
1111
|
+
})]
|
|
1112
|
+
});
|
|
1113
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1114
|
+
className: "flex h-8 min-w-0 max-w-48 items-center gap-1.5 px-2 text-muted-foreground text-xs",
|
|
1115
|
+
title: modelId,
|
|
1116
|
+
children: [/* @__PURE__ */ jsx(BotIcon, { className: "size-3.5 shrink-0" }), /* @__PURE__ */ jsx("span", {
|
|
1117
|
+
className: "truncate",
|
|
1118
|
+
children: selectedModelLabel
|
|
1119
|
+
})]
|
|
1120
|
+
});
|
|
1121
|
+
};
|
|
1122
|
+
const SurveyAssistantPromptInput = ({ attachmentsDisabled = false, attachmentButtonClassName, className, disabled = false, footerClassName, formProps, modelDisabled = false, modelId, models, onModelChange, onStop, onSubmit, onValueChange, placeholder, status, textareaClassName, textareaProps, textareaRef, value }) => {
|
|
1123
|
+
const t = useSurveyAssistantTranslation();
|
|
1124
|
+
const canStop = Boolean(onStop) && (status === "submitted" || status === "streaming");
|
|
1125
|
+
return /* @__PURE__ */ jsxs(PromptInput, {
|
|
1126
|
+
...formProps,
|
|
1127
|
+
attachmentsDisabled: attachmentsDisabled || disabled,
|
|
1128
|
+
className: cn("rounded-xl border-border bg-background shadow-sm", className),
|
|
1129
|
+
onSubmit,
|
|
1130
|
+
onValueChange,
|
|
1131
|
+
value,
|
|
1132
|
+
children: [/* @__PURE__ */ jsx(PromptInputBody, { children: /* @__PURE__ */ jsx(PromptInputTextarea, {
|
|
1133
|
+
...textareaProps,
|
|
1134
|
+
className: cn("min-h-20 px-3 py-2 disabled:cursor-not-allowed disabled:text-muted-foreground", textareaClassName),
|
|
1135
|
+
disabled,
|
|
1136
|
+
placeholder: placeholder ?? t("input.placeholder"),
|
|
1137
|
+
ref: textareaRef
|
|
1138
|
+
}) }), /* @__PURE__ */ jsxs(PromptInputFooter, {
|
|
1139
|
+
className: cn("justify-between border-0 px-1.5 pt-0 pb-1.5", footerClassName),
|
|
1140
|
+
children: [/* @__PURE__ */ jsx(PromptInputAttachmentButton, {
|
|
1141
|
+
className: attachmentButtonClassName,
|
|
1142
|
+
disabled: attachmentsDisabled || disabled
|
|
1143
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1144
|
+
className: "flex min-w-0 items-center justify-end gap-1",
|
|
1145
|
+
children: [/* @__PURE__ */ jsx(SurveyAssistantModelSelect, {
|
|
1146
|
+
disabled: disabled || modelDisabled,
|
|
1147
|
+
modelId,
|
|
1148
|
+
models,
|
|
1149
|
+
onModelChange
|
|
1150
|
+
}), /* @__PURE__ */ jsx(PromptInputSubmit, {
|
|
1151
|
+
disabled: disabled && !canStop,
|
|
1152
|
+
onStop,
|
|
1153
|
+
status
|
|
1154
|
+
})]
|
|
1155
|
+
})]
|
|
1156
|
+
})]
|
|
1157
|
+
});
|
|
1158
|
+
};
|
|
1159
|
+
//#endregion
|
|
1160
|
+
//#region src/ui/internal/tool-technical-details.ts
|
|
1161
|
+
const formatOmittedLength = (length) => length.toLocaleString("en-US");
|
|
1162
|
+
const stringifyToolTechnicalDetails = (value) => JSON.stringify(value, function redactEmbeddedBinary(key, currentValue) {
|
|
1163
|
+
const parent = this && typeof this === "object" ? this : void 0;
|
|
1164
|
+
if (key === "data" && typeof currentValue === "string" && parent?.type === "embedded" && parent.encoding === "base64") return `[embedded base64 omitted: ${formatOmittedLength(currentValue.length)} characters]`;
|
|
1165
|
+
if (typeof currentValue === "string" && currentValue.length > 512 && /^data:[^;,]+;base64,/.test(currentValue)) return `[base64 data URL omitted: ${formatOmittedLength(currentValue.length)} characters]`;
|
|
1166
|
+
return currentValue;
|
|
1167
|
+
}, 2);
|
|
1168
|
+
//#endregion
|
|
1033
1169
|
//#region src/ui/internal/code-block.tsx
|
|
1034
1170
|
const isItalic = (fontStyle) => fontStyle && fontStyle & 1;
|
|
1035
1171
|
const isBold = (fontStyle) => fontStyle && fontStyle & 2;
|
|
@@ -1273,26 +1409,61 @@ const getPersistedMessageTurnStatus = (message) => {
|
|
|
1273
1409
|
};
|
|
1274
1410
|
//#endregion
|
|
1275
1411
|
//#region src/ui/survey-assistant-panel.tsx
|
|
1276
|
-
const
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
prompt: t("quickActions.create.prompt")
|
|
1288
|
-
},
|
|
1289
|
-
{
|
|
1290
|
-
label: t("quickActions.refine.label"),
|
|
1291
|
-
description: t("quickActions.refine.description"),
|
|
1292
|
-
icon: WandSparklesIcon,
|
|
1293
|
-
prompt: t("quickActions.refine.prompt")
|
|
1412
|
+
const isRootSurveyItem = (item) => typeof item.config === "object" && item.config !== null && item.config.isRoot === true;
|
|
1413
|
+
const getAssistantEmptyStateContext = (controller) => {
|
|
1414
|
+
try {
|
|
1415
|
+
const snapshot = controller?.getSnapshot();
|
|
1416
|
+
if (!snapshot) return "survey";
|
|
1417
|
+
const surveyItems = snapshot.survey.surveyItems.filter((item) => !isRootSurveyItem(item));
|
|
1418
|
+
if (surveyItems.length === 0) return "new-survey";
|
|
1419
|
+
if (surveyItems.some((item) => item.id === snapshot.ui.selectedItemId)) return "selected-item";
|
|
1420
|
+
return "survey";
|
|
1421
|
+
} catch {
|
|
1422
|
+
return "survey";
|
|
1294
1423
|
}
|
|
1295
|
-
|
|
1424
|
+
};
|
|
1425
|
+
const getAssistantQuickPrompts = (t, context) => {
|
|
1426
|
+
if (context === "new-survey") return [
|
|
1427
|
+
{
|
|
1428
|
+
label: t("quickActions.draftSurvey.label"),
|
|
1429
|
+
description: t("quickActions.draftSurvey.description"),
|
|
1430
|
+
icon: WandSparklesIcon,
|
|
1431
|
+
prompt: t("quickActions.draftSurvey.prompt")
|
|
1432
|
+
},
|
|
1433
|
+
{
|
|
1434
|
+
label: t("quickActions.useBrief.label"),
|
|
1435
|
+
description: t("quickActions.useBrief.description"),
|
|
1436
|
+
icon: FileIcon,
|
|
1437
|
+
prompt: t("quickActions.useBrief.prompt")
|
|
1438
|
+
},
|
|
1439
|
+
{
|
|
1440
|
+
label: t("quickActions.planSurvey.label"),
|
|
1441
|
+
description: t("quickActions.planSurvey.description"),
|
|
1442
|
+
icon: ListChecksIcon,
|
|
1443
|
+
prompt: t("quickActions.planSurvey.prompt")
|
|
1444
|
+
}
|
|
1445
|
+
];
|
|
1446
|
+
return [
|
|
1447
|
+
{
|
|
1448
|
+
label: t("quickActions.review.label"),
|
|
1449
|
+
description: t("quickActions.review.description"),
|
|
1450
|
+
icon: ListChecksIcon,
|
|
1451
|
+
prompt: t("quickActions.review.prompt")
|
|
1452
|
+
},
|
|
1453
|
+
{
|
|
1454
|
+
label: t("quickActions.create.label"),
|
|
1455
|
+
description: t("quickActions.create.description"),
|
|
1456
|
+
icon: MessageSquarePlusIcon,
|
|
1457
|
+
prompt: t("quickActions.create.prompt")
|
|
1458
|
+
},
|
|
1459
|
+
{
|
|
1460
|
+
label: t(context === "selected-item" ? "quickActions.refineSelection.label" : "quickActions.refine.label"),
|
|
1461
|
+
description: t(context === "selected-item" ? "quickActions.refineSelection.description" : "quickActions.refine.description"),
|
|
1462
|
+
icon: WandSparklesIcon,
|
|
1463
|
+
prompt: t(context === "selected-item" ? "quickActions.refineSelection.prompt" : "quickActions.refine.prompt")
|
|
1464
|
+
}
|
|
1465
|
+
];
|
|
1466
|
+
};
|
|
1296
1467
|
const isSurveyAssistantAttachmentReferencePart = (part) => {
|
|
1297
1468
|
if (part.type !== "data-survey-assistant-attachment" || typeof part.data !== "object" || part.data === null) return false;
|
|
1298
1469
|
const data = part.data;
|
|
@@ -1321,6 +1492,7 @@ const getAssistantErrorPresentation = (error, t) => {
|
|
|
1321
1492
|
title: t(`errors.${parsed.code}.title`),
|
|
1322
1493
|
description: parsed.detail ?? t("errors.fallbackDetail"),
|
|
1323
1494
|
hint: t(`errors.${parsed.code}.hint`),
|
|
1495
|
+
retryable: parsed.retryable ?? (parsed.code === "rate_limited" || parsed.code === "request_failed"),
|
|
1324
1496
|
tone: parsed.code === "model_unavailable" || parsed.code === "rate_limited" ? "warning" : "error"
|
|
1325
1497
|
};
|
|
1326
1498
|
};
|
|
@@ -1349,7 +1521,7 @@ const SurveyAssistantToolOutputSummary = ({ applyResult, output }) => {
|
|
|
1349
1521
|
const statusLabel = status === "applied" ? t("changes.applied") : status === "stale" ? t("changes.stale") : status === "failed" ? t("changes.failed") : isValid ? t("changes.prepared") : t("changes.rejected");
|
|
1350
1522
|
const statusMessage = applyResult?.message ?? unwrappedOutput.summary;
|
|
1351
1523
|
return /* @__PURE__ */ jsxs("div", {
|
|
1352
|
-
className: cn("relative rounded-md border px-2.5 py-2 text-xs", isPositiveStatus ? "border-emerald-200/
|
|
1524
|
+
className: cn("relative rounded-md border px-2.5 py-2 text-xs", isPositiveStatus ? "border-emerald-200/70 bg-emerald-50/40 text-emerald-950" : status === "stale" ? "border-amber-200/70 bg-amber-50/40 text-amber-900" : "border-red-200/70 bg-red-50/40 text-red-900"),
|
|
1353
1525
|
children: [
|
|
1354
1526
|
/* @__PURE__ */ jsxs("div", {
|
|
1355
1527
|
className: "flex items-center gap-2 font-medium",
|
|
@@ -1447,11 +1619,20 @@ const SurveyAssistantToolStep = ({ applyResultsByChangeId, terminalTurnStatus, p
|
|
|
1447
1619
|
const errorText = getToolErrorText(part);
|
|
1448
1620
|
const changeSet = output === void 0 ? null : getChangeSet(output);
|
|
1449
1621
|
const applyResult = changeSet ? applyResultsByChangeId[changeSet.id] : void 0;
|
|
1622
|
+
const changeHasError = changeSet?.status === "rejected" || applyResult?.status === "rejected" || applyResult?.status === "failed";
|
|
1450
1623
|
const title = changeSet ? toolName === "survey_expression" ? t("activity.expressionChange") : t("activity.surveyChange") : getToolTitle(t, toolName, part.state);
|
|
1451
1624
|
const meta = terminalTurnStatus && toolStatusMeta[part.state].stepStatus === "active" ? terminalTurnStatus === "aborted" ? getStoppedToolStatusMeta(t) : {
|
|
1452
1625
|
icon: /* @__PURE__ */ jsx(XCircleIcon, { className: "size-3 text-red-600" }),
|
|
1453
1626
|
label: t("activity.failed"),
|
|
1454
1627
|
stepStatus: "complete"
|
|
1628
|
+
} : changeHasError ? {
|
|
1629
|
+
icon: /* @__PURE__ */ jsx(XCircleIcon, { className: "size-3 text-red-900" }),
|
|
1630
|
+
label: null,
|
|
1631
|
+
stepStatus: "complete"
|
|
1632
|
+
} : applyResult?.status === "stale" ? {
|
|
1633
|
+
icon: /* @__PURE__ */ jsx(ClockIcon, { className: "size-3 text-amber-600" }),
|
|
1634
|
+
label: null,
|
|
1635
|
+
stepStatus: "complete"
|
|
1455
1636
|
} : toolStatusMeta[part.state];
|
|
1456
1637
|
const metaTitle = meta.label ?? (part.state === "output-denied" ? t("activity.skipped") : part.state === "output-error" ? t("activity.failed") : meta.stepStatus === "active" ? t("activity.workingShort") : t("activity.done"));
|
|
1457
1638
|
const [detailsOpen, setDetailsOpen] = useState(part.state === "output-error");
|
|
@@ -1483,16 +1664,16 @@ const SurveyAssistantToolStep = ({ applyResultsByChangeId, terminalTurnStatus, p
|
|
|
1483
1664
|
onOpenChange: setDetailsOpen,
|
|
1484
1665
|
open: detailsOpen,
|
|
1485
1666
|
children: [/* @__PURE__ */ jsxs(CollapsibleTrigger, {
|
|
1486
|
-
className: "flex items-center gap-1 text-muted-foreground
|
|
1667
|
+
className: "-ml-1 flex items-center gap-0.5 rounded px-1 py-0.5 text-[11px] text-muted-foreground/80 transition-colors hover:bg-background/60 hover:text-foreground",
|
|
1487
1668
|
children: [/* @__PURE__ */ jsx(ChevronRightIcon, { className: cn("size-3 transition-transform", detailsOpen && "rotate-90") }), t(detailsOpen ? "activity.hideTechnicalDetails" : "activity.technicalDetails")]
|
|
1488
1669
|
}), /* @__PURE__ */ jsxs(CollapsibleContent, {
|
|
1489
|
-
className: "mt-
|
|
1670
|
+
className: "mt-1.5 space-y-1.5",
|
|
1490
1671
|
children: [part.input !== void 0 && /* @__PURE__ */ jsx(CodeBlock, {
|
|
1491
|
-
className: "border-border/
|
|
1672
|
+
className: "border-border/60 bg-background/70 [&_code]:text-xs [&_pre]:p-2.5 [&_pre]:text-xs",
|
|
1492
1673
|
code: stringifyToolTechnicalDetails(part.input),
|
|
1493
1674
|
language: "json"
|
|
1494
1675
|
}), (output !== void 0 || errorText) && /* @__PURE__ */ jsx(CodeBlock, {
|
|
1495
|
-
className: cn("bg-
|
|
1676
|
+
className: cn("bg-background/70 [&_code]:text-xs [&_pre]:p-2.5 [&_pre]:text-xs", errorText ? "border-destructive/25" : "border-border/60"),
|
|
1496
1677
|
code: errorText ?? stringifyToolTechnicalDetails(output),
|
|
1497
1678
|
language: "json"
|
|
1498
1679
|
})]
|
|
@@ -1625,7 +1806,7 @@ const SurveyAssistantMessageParts = ({ applyResultsByChangeId, isLastMessage, is
|
|
|
1625
1806
|
const isChainActive = isLastMessage && isStreaming && isLastBlock;
|
|
1626
1807
|
return /* @__PURE__ */ jsxs(ChainOfThought, {
|
|
1627
1808
|
autoClose: true,
|
|
1628
|
-
className: "w-full rounded-md bg-muted/
|
|
1809
|
+
className: "w-full rounded-md bg-muted/60 px-2 py-1.5",
|
|
1629
1810
|
defaultDuration: persistedDuration,
|
|
1630
1811
|
defaultOpen: isChainActive,
|
|
1631
1812
|
duration: isChainActive ? void 0 : persistedDuration,
|
|
@@ -1721,10 +1902,11 @@ const SurveyAssistantHeaderIdentity = ({ indicatorClassName, status, title }) =>
|
|
|
1721
1902
|
})]
|
|
1722
1903
|
})]
|
|
1723
1904
|
});
|
|
1724
|
-
const SurveyAssistantChatPanel = ({ api, applyPolicy, controller, embedded = false, initialLaunch, initialMessages, modelId, models, onInitialLaunchHandled, onModelChange, onFirstSubmit, onTurnSettled, onApplyReceipts, onSettleApply, onReviewChangeSets, onTurnActiveChange, resourceId, surveyId, threadControls, threadId, threadTitle }) => {
|
|
1905
|
+
const SurveyAssistantChatPanel = ({ api, applyPolicy, controller, embedded = false, hasEarlierMessages, initialLaunch, initialMessages, modelId, models, onInitialLaunchHandled, onModelChange, onFirstSubmit, onLoadEarlierMessages, onTurnSettled, onApplyReceipts, onSettleApply, onReviewChangeSets, onTurnActiveChange, resourceId, surveyId, threadControls, threadId, threadTitle }) => {
|
|
1725
1906
|
const t = useSurveyAssistantTranslation();
|
|
1726
|
-
const
|
|
1727
|
-
const
|
|
1907
|
+
const emptyStateContext = getAssistantEmptyStateContext(controller);
|
|
1908
|
+
const assistantQuickPrompts = getAssistantQuickPrompts(t, emptyStateContext);
|
|
1909
|
+
const { applyResultsByChangeId, authorizationError, dismissError, error, isStreaming, latestTurnOutcome, messages, prependMessages, state, status, stop, submit, retry } = useSurveyAssistantSession({
|
|
1728
1910
|
api,
|
|
1729
1911
|
applyPolicy,
|
|
1730
1912
|
controller,
|
|
@@ -1741,7 +1923,6 @@ const SurveyAssistantChatPanel = ({ api, applyPolicy, controller, embedded = fal
|
|
|
1741
1923
|
const hasSubmittedMessageRef = useRef((initialMessages?.length ?? 0) > 0);
|
|
1742
1924
|
const showAssistantError = Boolean(authorizationError) || Boolean(error);
|
|
1743
1925
|
const assistantErrorPresentation = getAssistantErrorPresentation(authorizationError ?? error, t);
|
|
1744
|
-
const selectedModelLabel = models.find((model) => model.id === modelId)?.label ?? modelId ?? t("input.defaultModel");
|
|
1745
1926
|
const lastMessage = messages.at(-1);
|
|
1746
1927
|
const isAssistantTurnActive = isSurveyAssistantTurnActive({
|
|
1747
1928
|
isStreaming,
|
|
@@ -1750,6 +1931,20 @@ const SurveyAssistantChatPanel = ({ api, applyPolicy, controller, embedded = fal
|
|
|
1750
1931
|
const promptTextareaRef = useRef(null);
|
|
1751
1932
|
const shouldRestorePromptFocusRef = useRef(false);
|
|
1752
1933
|
const turnWasActiveRef = useRef(false);
|
|
1934
|
+
const [loadingEarlierMessages, setLoadingEarlierMessages] = useState(false);
|
|
1935
|
+
const loadEarlierMessages = useCallback(async () => {
|
|
1936
|
+
if (loadingEarlierMessages) return;
|
|
1937
|
+
setLoadingEarlierMessages(true);
|
|
1938
|
+
try {
|
|
1939
|
+
prependMessages(await onLoadEarlierMessages());
|
|
1940
|
+
} finally {
|
|
1941
|
+
setLoadingEarlierMessages(false);
|
|
1942
|
+
}
|
|
1943
|
+
}, [
|
|
1944
|
+
loadingEarlierMessages,
|
|
1945
|
+
onLoadEarlierMessages,
|
|
1946
|
+
prependMessages
|
|
1947
|
+
]);
|
|
1753
1948
|
const isAssistantTurnSettled = !isStreaming && (state === "completed" || state === "failed" || state === "aborted" || state === "applied");
|
|
1754
1949
|
useEffect(() => {
|
|
1755
1950
|
if (!isAssistantTurnActive) {
|
|
@@ -1815,8 +2010,10 @@ const SurveyAssistantChatPanel = ({ api, applyPolicy, controller, embedded = fal
|
|
|
1815
2010
|
}), [handleSubmit]);
|
|
1816
2011
|
useEffect(() => {
|
|
1817
2012
|
if (!initialLaunch) return;
|
|
2013
|
+
if (initialLaunch.modelId) onModelChange(initialLaunch.modelId);
|
|
1818
2014
|
if (submit({
|
|
1819
2015
|
files: initialLaunch.files ?? [],
|
|
2016
|
+
modelId: initialLaunch.modelId,
|
|
1820
2017
|
text: initialLaunch.prompt,
|
|
1821
2018
|
uiContext: initialLaunch.uiContext
|
|
1822
2019
|
})) {
|
|
@@ -1830,6 +2027,7 @@ const SurveyAssistantChatPanel = ({ api, applyPolicy, controller, embedded = fal
|
|
|
1830
2027
|
initialLaunch,
|
|
1831
2028
|
onFirstSubmit,
|
|
1832
2029
|
onInitialLaunchHandled,
|
|
2030
|
+
onModelChange,
|
|
1833
2031
|
submit
|
|
1834
2032
|
]);
|
|
1835
2033
|
return /* @__PURE__ */ jsxs("aside", {
|
|
@@ -1837,7 +2035,7 @@ const SurveyAssistantChatPanel = ({ api, applyPolicy, controller, embedded = fal
|
|
|
1837
2035
|
className: cn("flex h-full min-h-0 flex-col overflow-visible bg-background", !embedded && "rounded-xl border border-border shadow-sm"),
|
|
1838
2036
|
children: [
|
|
1839
2037
|
/* @__PURE__ */ jsxs("header", {
|
|
1840
|
-
className: "box-content flex h-9 shrink-0 items-center justify-between gap-2 border-b border-border bg-
|
|
2038
|
+
className: "box-content flex h-9 shrink-0 items-center justify-between gap-2 border-b border-border bg-background px-2.5",
|
|
1841
2039
|
children: [/* @__PURE__ */ jsx(SurveyAssistantHeaderIdentity, {
|
|
1842
2040
|
indicatorClassName: showAssistantError || state === "failed" ? "bg-destructive" : isAssistantTurnActive ? "animate-pulse bg-blue-500" : state === "aborted" ? "bg-muted-foreground" : "bg-emerald-500",
|
|
1843
2041
|
status: sessionStatusLabel,
|
|
@@ -1845,7 +2043,7 @@ const SurveyAssistantChatPanel = ({ api, applyPolicy, controller, embedded = fal
|
|
|
1845
2043
|
}), threadControls]
|
|
1846
2044
|
}),
|
|
1847
2045
|
/* @__PURE__ */ jsxs(Conversation, {
|
|
1848
|
-
className: "min-h-0",
|
|
2046
|
+
className: "min-h-0 bg-(--surface-bg-color)",
|
|
1849
2047
|
children: [/* @__PURE__ */ jsx(ConversationContent, {
|
|
1850
2048
|
className: "gap-3 px-3 py-2.5",
|
|
1851
2049
|
children: messages.length === 0 ? /* @__PURE__ */ jsx(ConversationEmptyState, {
|
|
@@ -1854,21 +2052,21 @@ const SurveyAssistantChatPanel = ({ api, applyPolicy, controller, embedded = fal
|
|
|
1854
2052
|
className: "mx-auto w-full max-w-sm",
|
|
1855
2053
|
children: [
|
|
1856
2054
|
/* @__PURE__ */ jsx("div", {
|
|
1857
|
-
className: "mb-3 flex size-
|
|
1858
|
-
children: /* @__PURE__ */ jsx(SparklesIcon, { className: "size-
|
|
2055
|
+
className: "mb-3 flex size-9 items-center justify-center rounded-xl border border-border bg-muted text-foreground shadow-xs",
|
|
2056
|
+
children: /* @__PURE__ */ jsx(SparklesIcon, { className: "size-4" })
|
|
1859
2057
|
}),
|
|
1860
2058
|
/* @__PURE__ */ jsx("h3", {
|
|
1861
2059
|
className: "font-semibold text-foreground text-base tracking-tight",
|
|
1862
|
-
children: t("empty.title")
|
|
2060
|
+
children: t(emptyStateContext === "new-survey" ? "empty.newSurveyTitle" : "empty.title")
|
|
1863
2061
|
}),
|
|
1864
2062
|
/* @__PURE__ */ jsx("p", {
|
|
1865
2063
|
className: "mt-1 text-muted-foreground text-xs leading-relaxed",
|
|
1866
|
-
children: t("empty.description")
|
|
2064
|
+
children: t(emptyStateContext === "new-survey" ? "empty.newSurveyDescription" : "empty.description")
|
|
1867
2065
|
}),
|
|
1868
2066
|
/* @__PURE__ */ jsx("div", {
|
|
1869
2067
|
className: "mt-4 flex flex-col gap-1.5",
|
|
1870
2068
|
children: assistantQuickPrompts.map(({ label, description, icon: Icon, prompt }) => /* @__PURE__ */ jsxs("button", {
|
|
1871
|
-
className: "group flex w-full items-center gap-2.5 rounded-lg border border-border bg-
|
|
2069
|
+
className: "group flex w-full items-center gap-2.5 rounded-lg border border-border bg-popover px-2.5 py-2 text-left shadow-xs transition-colors hover:border-foreground/20 hover:bg-muted/60",
|
|
1872
2070
|
onClick: () => handleQuickPrompt(prompt),
|
|
1873
2071
|
type: "button",
|
|
1874
2072
|
children: [
|
|
@@ -1892,40 +2090,50 @@ const SurveyAssistantChatPanel = ({ api, applyPolicy, controller, embedded = fal
|
|
|
1892
2090
|
})
|
|
1893
2091
|
]
|
|
1894
2092
|
})
|
|
1895
|
-
}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
2093
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2094
|
+
hasEarlierMessages && /* @__PURE__ */ jsxs("button", {
|
|
2095
|
+
className: "mx-auto flex h-8 items-center justify-center rounded-md px-3 text-muted-foreground text-xs hover:bg-muted hover:text-foreground disabled:pointer-events-none",
|
|
2096
|
+
disabled: loadingEarlierMessages || isAssistantTurnActive,
|
|
2097
|
+
onClick: () => void loadEarlierMessages(),
|
|
2098
|
+
type: "button",
|
|
2099
|
+
children: [loadingEarlierMessages && /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "mr-1.5 size-3.5 animate-spin" }), loadingEarlierMessages ? t("conversation.loadingEarlier") : t("conversation.loadEarlier")]
|
|
2100
|
+
}),
|
|
2101
|
+
messages.map((message, index) => {
|
|
2102
|
+
const isLastMessage = index === messages.length - 1;
|
|
2103
|
+
const persistedTurnStatus = getPersistedMessageTurnStatus(message);
|
|
2104
|
+
const terminalTurnStatus = persistedTurnStatus === "aborted" || persistedTurnStatus === "failed" ? persistedTurnStatus : isLastMessage && (state === "aborted" || state === "failed") ? state : void 0;
|
|
2105
|
+
const shouldShowProgress = isAssistantTurnActive && isLastMessage && message.role === "assistant";
|
|
2106
|
+
return /* @__PURE__ */ jsx(Message, {
|
|
2107
|
+
className: "max-w-[96%] gap-1",
|
|
2108
|
+
from: message.role,
|
|
2109
|
+
children: /* @__PURE__ */ jsx(MessageContent, {
|
|
2110
|
+
className: cn(message.role === "assistant" && "w-full space-y-2"),
|
|
2111
|
+
children: hasRenderableParts(message) ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(SurveyAssistantMessageParts, {
|
|
2112
|
+
applyResultsByChangeId,
|
|
2113
|
+
isLastMessage,
|
|
2114
|
+
isStreaming: isAssistantTurnActive,
|
|
2115
|
+
message,
|
|
2116
|
+
showProgress: shouldShowProgress,
|
|
2117
|
+
terminalTurnStatus
|
|
2118
|
+
}), isLastMessage && latestTurnOutcome && /* @__PURE__ */ jsx(SurveyAssistantTurnOutcomeNotice, { ...latestTurnOutcome })] }) : shouldShowProgress ? /* @__PURE__ */ jsx(SurveyAssistantProgressIndicator, { message }) : isLastMessage && latestTurnOutcome ? /* @__PURE__ */ jsx(SurveyAssistantTurnOutcomeNotice, { ...latestTurnOutcome }) : state === "aborted" ? /* @__PURE__ */ jsx("span", {
|
|
2119
|
+
className: "text-muted-foreground text-sm",
|
|
2120
|
+
children: t("conversation.stopped")
|
|
2121
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
2122
|
+
className: "text-muted-foreground text-sm",
|
|
2123
|
+
children: t("conversation.incomplete")
|
|
2124
|
+
})
|
|
2125
|
+
})
|
|
2126
|
+
}, message.id);
|
|
2127
|
+
}),
|
|
2128
|
+
showPendingAssistantRow && /* @__PURE__ */ jsx(Message, {
|
|
1901
2129
|
className: "max-w-[96%] gap-1",
|
|
1902
|
-
from:
|
|
2130
|
+
from: "assistant",
|
|
1903
2131
|
children: /* @__PURE__ */ jsx(MessageContent, {
|
|
1904
|
-
className:
|
|
1905
|
-
children:
|
|
1906
|
-
applyResultsByChangeId,
|
|
1907
|
-
isLastMessage,
|
|
1908
|
-
isStreaming: isAssistantTurnActive,
|
|
1909
|
-
message,
|
|
1910
|
-
showProgress: shouldShowProgress,
|
|
1911
|
-
terminalTurnStatus
|
|
1912
|
-
}), isLastMessage && latestTurnOutcome && /* @__PURE__ */ jsx(SurveyAssistantTurnOutcomeNotice, { ...latestTurnOutcome })] }) : shouldShowProgress ? /* @__PURE__ */ jsx(SurveyAssistantProgressIndicator, { message }) : isLastMessage && latestTurnOutcome ? /* @__PURE__ */ jsx(SurveyAssistantTurnOutcomeNotice, { ...latestTurnOutcome }) : state === "aborted" ? /* @__PURE__ */ jsx("span", {
|
|
1913
|
-
className: "text-muted-foreground text-sm",
|
|
1914
|
-
children: t("conversation.stopped")
|
|
1915
|
-
}) : /* @__PURE__ */ jsx("span", {
|
|
1916
|
-
className: "text-muted-foreground text-sm",
|
|
1917
|
-
children: t("conversation.incomplete")
|
|
1918
|
-
})
|
|
2132
|
+
className: "w-full",
|
|
2133
|
+
children: /* @__PURE__ */ jsx(SurveyAssistantProgressIndicator, {})
|
|
1919
2134
|
})
|
|
1920
|
-
}, message.id);
|
|
1921
|
-
}), showPendingAssistantRow && /* @__PURE__ */ jsx(Message, {
|
|
1922
|
-
className: "max-w-[96%] gap-1",
|
|
1923
|
-
from: "assistant",
|
|
1924
|
-
children: /* @__PURE__ */ jsx(MessageContent, {
|
|
1925
|
-
className: "w-full",
|
|
1926
|
-
children: /* @__PURE__ */ jsx(SurveyAssistantProgressIndicator, {})
|
|
1927
2135
|
})
|
|
1928
|
-
|
|
2136
|
+
] })
|
|
1929
2137
|
}), /* @__PURE__ */ jsx(ConversationScrollButton, { className: "bottom-2.5" })]
|
|
1930
2138
|
}),
|
|
1931
2139
|
showAssistantError && /* @__PURE__ */ jsxs("div", {
|
|
@@ -1948,7 +2156,7 @@ const SurveyAssistantChatPanel = ({ api, applyPolicy, controller, embedded = fal
|
|
|
1948
2156
|
className: "mt-1 text-muted-foreground/80",
|
|
1949
2157
|
children: assistantErrorPresentation.hint
|
|
1950
2158
|
}),
|
|
1951
|
-
/* @__PURE__ */ jsx("button", {
|
|
2159
|
+
assistantErrorPresentation.retryable && /* @__PURE__ */ jsx("button", {
|
|
1952
2160
|
className: "mt-1.5 font-medium text-foreground text-xs hover:underline",
|
|
1953
2161
|
onClick: retry,
|
|
1954
2162
|
type: "button",
|
|
@@ -1965,58 +2173,21 @@ const SurveyAssistantChatPanel = ({ api, applyPolicy, controller, embedded = fal
|
|
|
1965
2173
|
})]
|
|
1966
2174
|
}),
|
|
1967
2175
|
/* @__PURE__ */ jsx("div", {
|
|
1968
|
-
className: "shrink-0 border-t border-border bg-
|
|
1969
|
-
children: /* @__PURE__ */
|
|
1970
|
-
"
|
|
1971
|
-
|
|
1972
|
-
|
|
2176
|
+
className: "shrink-0 border-t border-border bg-background p-2",
|
|
2177
|
+
children: /* @__PURE__ */ jsx(SurveyAssistantPromptInput, {
|
|
2178
|
+
attachmentButtonClassName: "size-8",
|
|
2179
|
+
className: cn("rounded-xl border-border bg-popover shadow-sm transition-colors", isAssistantTurnActive && "border-border/60 bg-muted/50 shadow-none"),
|
|
2180
|
+
disabled: isAssistantTurnActive,
|
|
2181
|
+
footerClassName: cn("transition-colors", isAssistantTurnActive && "bg-muted/30"),
|
|
2182
|
+
formProps: { "aria-busy": isAssistantTurnActive },
|
|
2183
|
+
modelId,
|
|
2184
|
+
models,
|
|
2185
|
+
onModelChange,
|
|
2186
|
+
onStop: stop,
|
|
1973
2187
|
onSubmit: handleSubmit,
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
placeholder: t("input.placeholder"),
|
|
1978
|
-
ref: promptTextareaRef
|
|
1979
|
-
}) }), /* @__PURE__ */ jsxs(PromptInputFooter, {
|
|
1980
|
-
className: cn("justify-between border-0 px-1.5 pt-0 pb-1.5 transition-colors", isAssistantTurnActive && "bg-muted/30"),
|
|
1981
|
-
children: [/* @__PURE__ */ jsx(PromptInputAttachmentButton, {
|
|
1982
|
-
className: "size-8",
|
|
1983
|
-
disabled: isAssistantTurnActive
|
|
1984
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
1985
|
-
className: "flex min-w-0 items-center justify-end gap-1",
|
|
1986
|
-
children: [models.length > 1 && modelId ? /* @__PURE__ */ jsxs(Select$1, {
|
|
1987
|
-
disabled: isAssistantTurnActive,
|
|
1988
|
-
onValueChange: onModelChange,
|
|
1989
|
-
value: modelId,
|
|
1990
|
-
children: [/* @__PURE__ */ jsxs(SelectTrigger, {
|
|
1991
|
-
"aria-label": t("input.model"),
|
|
1992
|
-
className: "h-8 min-w-0 max-w-48 gap-1.5 border-0 bg-transparent px-2 text-xs shadow-none hover:bg-muted hover:text-foreground focus-visible:ring-1",
|
|
1993
|
-
size: "sm",
|
|
1994
|
-
title: modelId,
|
|
1995
|
-
children: [/* @__PURE__ */ jsx(BotIcon, { className: "size-3.5" }), /* @__PURE__ */ jsx(SelectValue, {})]
|
|
1996
|
-
}), /* @__PURE__ */ jsx(SelectContent, {
|
|
1997
|
-
align: "end",
|
|
1998
|
-
className: "min-w-52 rounded-lg border-border p-0 shadow-lg",
|
|
1999
|
-
position: "popper",
|
|
2000
|
-
children: models.map((model) => /* @__PURE__ */ jsx(SelectItem, {
|
|
2001
|
-
className: "min-h-9 rounded-md px-2 py-2 text-xs focus:bg-muted",
|
|
2002
|
-
title: model.id,
|
|
2003
|
-
value: model.id,
|
|
2004
|
-
children: model.label
|
|
2005
|
-
}, model.id))
|
|
2006
|
-
})]
|
|
2007
|
-
}) : /* @__PURE__ */ jsxs("div", {
|
|
2008
|
-
className: "flex h-8 min-w-0 max-w-48 items-center gap-1.5 px-2 text-muted-foreground text-xs",
|
|
2009
|
-
title: modelId,
|
|
2010
|
-
children: [/* @__PURE__ */ jsx(BotIcon, { className: "size-3.5 shrink-0" }), /* @__PURE__ */ jsx("span", {
|
|
2011
|
-
className: "truncate",
|
|
2012
|
-
children: selectedModelLabel
|
|
2013
|
-
})]
|
|
2014
|
-
}), /* @__PURE__ */ jsx(PromptInputSubmit, {
|
|
2015
|
-
onStop: stop,
|
|
2016
|
-
status
|
|
2017
|
-
})]
|
|
2018
|
-
})]
|
|
2019
|
-
})]
|
|
2188
|
+
status,
|
|
2189
|
+
textareaClassName: "min-h-11 max-h-28 px-3 pt-2.5 pb-1.5 transition-colors disabled:placeholder:text-muted-foreground/60",
|
|
2190
|
+
textareaRef: promptTextareaRef
|
|
2020
2191
|
})
|
|
2021
2192
|
})
|
|
2022
2193
|
]
|
|
@@ -2029,14 +2200,32 @@ const createThreadTitle = (text, fallback) => {
|
|
|
2029
2200
|
const words = normalized.split(" ").slice(0, 8).join(" ");
|
|
2030
2201
|
return words.length > 64 ? `${words.slice(0, 61).trimEnd()}...` : words;
|
|
2031
2202
|
};
|
|
2203
|
+
const mergeThreads = (current, incoming) => {
|
|
2204
|
+
const byId = new Map(current.map((thread) => [thread.id, thread]));
|
|
2205
|
+
for (const thread of incoming) byId.set(thread.id, thread);
|
|
2206
|
+
return [...byId.values()].sort((left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt));
|
|
2207
|
+
};
|
|
2208
|
+
const mergeRecentThreadPage = (current, recent) => {
|
|
2209
|
+
if (recent.length === 0) return [];
|
|
2210
|
+
const oldestRecentUpdate = Math.min(...recent.map((thread) => Date.parse(thread.updatedAt)));
|
|
2211
|
+
return mergeThreads(Number.isFinite(oldestRecentUpdate) ? current.filter((thread) => Date.parse(thread.updatedAt) <= oldestRecentUpdate) : current, recent);
|
|
2212
|
+
};
|
|
2213
|
+
const listThreadPage = async (client, cursor) => {
|
|
2214
|
+
if (client.listPage) return client.listPage(cursor ? { cursor } : void 0);
|
|
2215
|
+
return cursor ? { threads: [] } : { threads: await client.list() };
|
|
2216
|
+
};
|
|
2032
2217
|
const SurveyAssistantPanelContent = ({ embedded = false, modelId, models = [], onConfirmDeleteThread, onModelChange = () => {}, onTurnActiveChange: onAssistantTurnActiveChange, resourceId, selectedThreadStorageKey, surveyId, threadApi, threadClient: providedThreadClient, threadLauncher, ...sessionProps }) => {
|
|
2033
2218
|
const t = useSurveyAssistantTranslation();
|
|
2034
2219
|
const threadClient = useMemo(() => providedThreadClient ?? createSurveyAssistantThreadClient(threadApi), [providedThreadClient, threadApi]);
|
|
2035
2220
|
const storageKey = selectedThreadStorageKey === false ? null : selectedThreadStorageKey ?? `${SELECTED_THREAD_STORAGE_KEY}:${encodeURIComponent(resourceId)}${surveyId ? `:${encodeURIComponent(surveyId)}` : ""}`;
|
|
2036
2221
|
const [threads, setThreads] = useState([]);
|
|
2222
|
+
const [nextThreadCursor, setNextThreadCursor] = useState();
|
|
2037
2223
|
const [selectedThread, setSelectedThread] = useState(null);
|
|
2038
2224
|
const [initialMessages, setInitialMessages] = useState([]);
|
|
2225
|
+
const [nextMessageCursor, setNextMessageCursor] = useState();
|
|
2039
2226
|
const [historyOpen, setHistoryOpen] = useState(false);
|
|
2227
|
+
const [threadSearch, setThreadSearch] = useState("");
|
|
2228
|
+
const [loadingOlderThreads, setLoadingOlderThreads] = useState(false);
|
|
2040
2229
|
const [editingThreadId, setEditingThreadId] = useState(null);
|
|
2041
2230
|
const [editingTitle, setEditingTitle] = useState("");
|
|
2042
2231
|
const [threadError, setThreadError] = useState(null);
|
|
@@ -2044,21 +2233,25 @@ const SurveyAssistantPanelContent = ({ embedded = false, modelId, models = [], o
|
|
|
2044
2233
|
const [assistantTurnActive, setAssistantTurnActive] = useState(false);
|
|
2045
2234
|
const [initialLaunch, setInitialLaunch] = useState();
|
|
2046
2235
|
const historyControlsRef = useRef(null);
|
|
2236
|
+
const loadingAllThreadsRef = useRef(false);
|
|
2047
2237
|
const selectModel = useCallback((modelId) => {
|
|
2048
2238
|
if (!models.some((model) => model.id === modelId)) return;
|
|
2049
2239
|
onModelChange(modelId);
|
|
2050
2240
|
}, [models, onModelChange]);
|
|
2051
|
-
const selectThread = useCallback(async (thread) => {
|
|
2241
|
+
const selectThread = useCallback(async (thread, reportError = true) => {
|
|
2052
2242
|
setLoadingThread(true);
|
|
2053
2243
|
setThreadError(null);
|
|
2054
2244
|
try {
|
|
2055
2245
|
const transcript = await threadClient.read(thread.id);
|
|
2056
2246
|
setSelectedThread(transcript.thread);
|
|
2057
2247
|
setInitialMessages(transcript.messages);
|
|
2248
|
+
setNextMessageCursor(transcript.nextCursor);
|
|
2058
2249
|
setHistoryOpen(false);
|
|
2059
2250
|
if (storageKey) window.localStorage.setItem(storageKey, thread.id);
|
|
2251
|
+
return transcript.thread;
|
|
2060
2252
|
} catch (error) {
|
|
2061
|
-
setThreadError(parseSurveyAssistantError(error).detail ?? t("errors.fallbackDetail"));
|
|
2253
|
+
if (reportError) setThreadError(parseSurveyAssistantError(error).detail ?? t("errors.fallbackDetail"));
|
|
2254
|
+
return null;
|
|
2062
2255
|
} finally {
|
|
2063
2256
|
setLoadingThread(false);
|
|
2064
2257
|
}
|
|
@@ -2072,9 +2265,10 @@ const SurveyAssistantPanelContent = ({ embedded = false, modelId, models = [], o
|
|
|
2072
2265
|
setThreadError(null);
|
|
2073
2266
|
try {
|
|
2074
2267
|
const thread = await threadClient.create(launch?.title);
|
|
2075
|
-
setThreads((current) => [thread
|
|
2268
|
+
setThreads((current) => mergeThreads(current, [thread]));
|
|
2076
2269
|
setSelectedThread(thread);
|
|
2077
2270
|
setInitialMessages([]);
|
|
2271
|
+
setNextMessageCursor(void 0);
|
|
2078
2272
|
setHistoryOpen(false);
|
|
2079
2273
|
if (storageKey) window.localStorage.setItem(storageKey, thread.id);
|
|
2080
2274
|
return thread;
|
|
@@ -2092,11 +2286,21 @@ const SurveyAssistantPanelContent = ({ embedded = false, modelId, models = [], o
|
|
|
2092
2286
|
]);
|
|
2093
2287
|
useEffect(() => {
|
|
2094
2288
|
let cancelled = false;
|
|
2095
|
-
threadClient
|
|
2289
|
+
listThreadPage(threadClient).then(async (page) => {
|
|
2096
2290
|
if (cancelled) return;
|
|
2291
|
+
const availableThreads = page.threads;
|
|
2097
2292
|
setThreads(availableThreads);
|
|
2293
|
+
setNextThreadCursor(page.nextCursor);
|
|
2098
2294
|
const rememberedId = storageKey ? window.localStorage.getItem(storageKey) : null;
|
|
2099
|
-
const
|
|
2295
|
+
const rememberedThread = availableThreads.find((thread) => thread.id === rememberedId);
|
|
2296
|
+
if (rememberedId && !rememberedThread) {
|
|
2297
|
+
const restored = await selectThread({ id: rememberedId }, false);
|
|
2298
|
+
if (restored) {
|
|
2299
|
+
setThreads((current) => mergeThreads(current, [restored]));
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
const initialThread = rememberedThread ?? availableThreads[0];
|
|
2100
2304
|
if (initialThread) await selectThread(initialThread);
|
|
2101
2305
|
else await createThread();
|
|
2102
2306
|
}).catch((error) => {
|
|
@@ -2115,6 +2319,66 @@ const SurveyAssistantPanelContent = ({ embedded = false, modelId, models = [], o
|
|
|
2115
2319
|
t,
|
|
2116
2320
|
threadClient
|
|
2117
2321
|
]);
|
|
2322
|
+
const loadOlderThreads = useCallback(async () => {
|
|
2323
|
+
if (!nextThreadCursor || loadingOlderThreads) return;
|
|
2324
|
+
setLoadingOlderThreads(true);
|
|
2325
|
+
try {
|
|
2326
|
+
const page = await listThreadPage(threadClient, nextThreadCursor);
|
|
2327
|
+
setThreads((current) => mergeThreads(current, page.threads));
|
|
2328
|
+
setNextThreadCursor(page.nextCursor);
|
|
2329
|
+
} catch (error) {
|
|
2330
|
+
setThreadError(parseSurveyAssistantError(error).detail ?? t("errors.fallbackDetail"));
|
|
2331
|
+
} finally {
|
|
2332
|
+
setLoadingOlderThreads(false);
|
|
2333
|
+
}
|
|
2334
|
+
}, [
|
|
2335
|
+
loadingOlderThreads,
|
|
2336
|
+
nextThreadCursor,
|
|
2337
|
+
t,
|
|
2338
|
+
threadClient
|
|
2339
|
+
]);
|
|
2340
|
+
const loadEarlierMessages = useCallback(async () => {
|
|
2341
|
+
if (!selectedThread || !nextMessageCursor) return [];
|
|
2342
|
+
try {
|
|
2343
|
+
const transcript = await threadClient.read(selectedThread.id, nextMessageCursor);
|
|
2344
|
+
setNextMessageCursor(transcript.nextCursor);
|
|
2345
|
+
return transcript.messages;
|
|
2346
|
+
} catch (error) {
|
|
2347
|
+
setThreadError(parseSurveyAssistantError(error).detail ?? t("errors.fallbackDetail"));
|
|
2348
|
+
return [];
|
|
2349
|
+
}
|
|
2350
|
+
}, [
|
|
2351
|
+
nextMessageCursor,
|
|
2352
|
+
selectedThread,
|
|
2353
|
+
t,
|
|
2354
|
+
threadClient
|
|
2355
|
+
]);
|
|
2356
|
+
const loadAllThreadsForSearch = useCallback(async () => {
|
|
2357
|
+
if (!nextThreadCursor || loadingAllThreadsRef.current) return;
|
|
2358
|
+
loadingAllThreadsRef.current = true;
|
|
2359
|
+
setLoadingOlderThreads(true);
|
|
2360
|
+
try {
|
|
2361
|
+
let cursor = nextThreadCursor;
|
|
2362
|
+
while (cursor) {
|
|
2363
|
+
const page = await listThreadPage(threadClient, cursor);
|
|
2364
|
+
setThreads((current) => mergeThreads(current, page.threads));
|
|
2365
|
+
cursor = page.nextCursor;
|
|
2366
|
+
setNextThreadCursor(cursor);
|
|
2367
|
+
}
|
|
2368
|
+
} catch (error) {
|
|
2369
|
+
setThreadError(parseSurveyAssistantError(error).detail ?? t("errors.fallbackDetail"));
|
|
2370
|
+
} finally {
|
|
2371
|
+
loadingAllThreadsRef.current = false;
|
|
2372
|
+
setLoadingOlderThreads(false);
|
|
2373
|
+
}
|
|
2374
|
+
}, [
|
|
2375
|
+
nextThreadCursor,
|
|
2376
|
+
t,
|
|
2377
|
+
threadClient
|
|
2378
|
+
]);
|
|
2379
|
+
useEffect(() => {
|
|
2380
|
+
if (threadSearch.trim()) loadAllThreadsForSearch();
|
|
2381
|
+
}, [loadAllThreadsForSearch, threadSearch]);
|
|
2118
2382
|
useEffect(() => {
|
|
2119
2383
|
if (!threadLauncher || loadingThread) return;
|
|
2120
2384
|
return connectSurveyAssistantThreadLauncher(threadLauncher, async (launch) => {
|
|
@@ -2222,9 +2486,9 @@ const SurveyAssistantPanelContent = ({ embedded = false, modelId, models = [], o
|
|
|
2222
2486
|
}, [initialLaunch]);
|
|
2223
2487
|
const refreshThreadList = useCallback(async (reportError) => {
|
|
2224
2488
|
try {
|
|
2225
|
-
const
|
|
2226
|
-
setThreads(
|
|
2227
|
-
const current =
|
|
2489
|
+
const page = await listThreadPage(threadClient);
|
|
2490
|
+
setThreads((current) => mergeRecentThreadPage(current, page.threads));
|
|
2491
|
+
const current = page.threads.find((thread) => thread.id === selectedThread?.id);
|
|
2228
2492
|
if (current && !editingThreadId) setSelectedThread((selected) => {
|
|
2229
2493
|
if (!selected || selected.id !== current.id) return selected;
|
|
2230
2494
|
return selected.title === current.title && selected.titleMode === current.titleMode && selected.updatedAt === current.updatedAt ? selected : current;
|
|
@@ -2289,7 +2553,7 @@ const SurveyAssistantPanelContent = ({ embedded = false, modelId, models = [], o
|
|
|
2289
2553
|
if (loadingThread || !selectedThread) return /* @__PURE__ */ jsxs("aside", {
|
|
2290
2554
|
className: cn("flex h-full min-h-0 flex-col overflow-visible bg-background", !embedded && "rounded-xl border border-border shadow-sm"),
|
|
2291
2555
|
children: [/* @__PURE__ */ jsx("header", {
|
|
2292
|
-
className: "box-content flex h-9 shrink-0 items-center border-b border-border bg-
|
|
2556
|
+
className: "box-content flex h-9 shrink-0 items-center border-b border-border bg-background px-2.5",
|
|
2293
2557
|
children: /* @__PURE__ */ jsx(SurveyAssistantHeaderIdentity, {
|
|
2294
2558
|
indicatorClassName: "animate-pulse bg-blue-500",
|
|
2295
2559
|
status: t("panel.loadingChats"),
|
|
@@ -2300,6 +2564,8 @@ const SurveyAssistantPanelContent = ({ embedded = false, modelId, models = [], o
|
|
|
2300
2564
|
children: [/* @__PURE__ */ jsx(LoaderCircleIcon, { className: "mr-2 size-4 animate-spin" }), t("panel.loadingChats")]
|
|
2301
2565
|
})]
|
|
2302
2566
|
});
|
|
2567
|
+
const normalizedThreadSearch = threadSearch.trim().toLocaleLowerCase();
|
|
2568
|
+
const visibleThreads = normalizedThreadSearch ? threads.filter((thread) => thread.title.toLocaleLowerCase().includes(normalizedThreadSearch)) : threads;
|
|
2303
2569
|
const threadControls = /* @__PURE__ */ jsxs("div", {
|
|
2304
2570
|
className: "relative flex shrink-0 items-center gap-1",
|
|
2305
2571
|
ref: historyControlsRef,
|
|
@@ -2325,65 +2591,92 @@ const SurveyAssistantPanelContent = ({ embedded = false, modelId, models = [], o
|
|
|
2325
2591
|
}),
|
|
2326
2592
|
historyOpen && /* @__PURE__ */ jsxs("div", {
|
|
2327
2593
|
className: "absolute top-10 right-0 z-30 w-72 overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-lg",
|
|
2328
|
-
children: [
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
className:
|
|
2594
|
+
children: [
|
|
2595
|
+
/* @__PURE__ */ jsx("div", {
|
|
2596
|
+
className: "border-b border-border px-3 py-2 font-medium text-xs",
|
|
2597
|
+
children: t("threads.recent")
|
|
2598
|
+
}),
|
|
2599
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2600
|
+
className: "relative border-b border-border p-2",
|
|
2601
|
+
children: [/* @__PURE__ */ jsx(SearchIcon, { className: "pointer-events-none absolute top-1/2 left-4 size-3.5 -translate-y-1/2 text-muted-foreground" }), /* @__PURE__ */ jsx("input", {
|
|
2602
|
+
"aria-label": t("threads.search"),
|
|
2603
|
+
className: "h-8 w-full rounded-md border border-input bg-background pr-2 pl-8 text-xs outline-none placeholder:text-muted-foreground focus:ring-1 focus:ring-ring",
|
|
2604
|
+
onChange: (event) => setThreadSearch(event.currentTarget.value),
|
|
2605
|
+
placeholder: t("threads.search"),
|
|
2606
|
+
type: "search",
|
|
2607
|
+
value: threadSearch
|
|
2608
|
+
})]
|
|
2609
|
+
}),
|
|
2610
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2611
|
+
className: "max-h-72 overflow-y-auto p-1",
|
|
2335
2612
|
children: [
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
event.
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2613
|
+
visibleThreads.map((thread) => /* @__PURE__ */ jsxs("div", {
|
|
2614
|
+
className: cn("group flex min-h-10 items-center gap-1 rounded-md px-2", thread.id === selectedThread.id ? "bg-muted" : "hover:bg-muted/60"),
|
|
2615
|
+
children: [
|
|
2616
|
+
editingThreadId === thread.id ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("input", {
|
|
2617
|
+
autoFocus: true,
|
|
2618
|
+
className: "min-w-0 flex-1 rounded border border-input bg-background px-1.5 py-1 text-xs outline-none focus:ring-1 focus:ring-ring",
|
|
2619
|
+
onBlur: () => void commitEditingThread(),
|
|
2620
|
+
onChange: (event) => setEditingTitle(event.currentTarget.value),
|
|
2621
|
+
onKeyDown: (event) => {
|
|
2622
|
+
if (event.key === "Enter") {
|
|
2623
|
+
event.preventDefault();
|
|
2624
|
+
commitEditingThread();
|
|
2625
|
+
}
|
|
2626
|
+
if (event.key === "Escape") {
|
|
2627
|
+
event.preventDefault();
|
|
2628
|
+
event.stopPropagation();
|
|
2629
|
+
cancelEditingThread();
|
|
2630
|
+
setHistoryOpen(false);
|
|
2631
|
+
}
|
|
2632
|
+
},
|
|
2633
|
+
value: editingTitle
|
|
2634
|
+
}), thread.titleMode === "manual" && /* @__PURE__ */ jsxs("button", {
|
|
2635
|
+
"aria-label": t("threads.automaticTitle"),
|
|
2636
|
+
className: "flex h-7 shrink-0 items-center gap-1 rounded px-1.5 text-muted-foreground text-xs hover:bg-background hover:text-foreground",
|
|
2637
|
+
onClick: () => void setAutomaticTitle(thread),
|
|
2638
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
2639
|
+
title: t("threads.automaticTitle"),
|
|
2640
|
+
type: "button",
|
|
2641
|
+
children: [/* @__PURE__ */ jsx(SparklesIcon, { className: "size-3" }), /* @__PURE__ */ jsx("span", { children: t("threads.automaticShort") })]
|
|
2642
|
+
})] }) : /* @__PURE__ */ jsx("button", {
|
|
2643
|
+
className: "min-w-0 flex-1 truncate py-2 text-left text-xs",
|
|
2644
|
+
onClick: () => void selectThread(thread),
|
|
2645
|
+
type: "button",
|
|
2646
|
+
children: thread.title
|
|
2647
|
+
}),
|
|
2648
|
+
/* @__PURE__ */ jsx("button", {
|
|
2649
|
+
"aria-label": t("threads.rename", { title: thread.title }),
|
|
2650
|
+
className: "flex size-7 shrink-0 items-center justify-center rounded text-muted-foreground opacity-0 hover:bg-background hover:text-foreground group-hover:opacity-100",
|
|
2651
|
+
onClick: () => beginEditingThread(thread),
|
|
2652
|
+
title: t("threads.renameTitle"),
|
|
2653
|
+
type: "button",
|
|
2654
|
+
children: /* @__PURE__ */ jsx(PencilIcon, { className: "size-3.5" })
|
|
2655
|
+
}),
|
|
2656
|
+
/* @__PURE__ */ jsx("button", {
|
|
2657
|
+
"aria-label": t("threads.delete", { title: thread.title }),
|
|
2658
|
+
className: "flex size-7 shrink-0 items-center justify-center rounded text-muted-foreground opacity-0 hover:bg-background hover:text-foreground group-hover:opacity-100",
|
|
2659
|
+
onClick: () => void deleteThread(thread),
|
|
2660
|
+
title: t("threads.deleteTitle"),
|
|
2661
|
+
type: "button",
|
|
2662
|
+
children: /* @__PURE__ */ jsx(Trash2Icon, { className: "size-3.5" })
|
|
2663
|
+
})
|
|
2664
|
+
]
|
|
2665
|
+
}, thread.id)),
|
|
2666
|
+
visibleThreads.length === 0 && !loadingOlderThreads && /* @__PURE__ */ jsx("div", {
|
|
2667
|
+
className: "px-3 py-5 text-center text-muted-foreground text-xs",
|
|
2668
|
+
children: t("threads.noMatches")
|
|
2375
2669
|
}),
|
|
2376
|
-
/* @__PURE__ */ jsx("button", {
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
onClick: () => void
|
|
2380
|
-
title: t("threads.deleteTitle"),
|
|
2670
|
+
(nextThreadCursor || loadingOlderThreads) && /* @__PURE__ */ jsx("button", {
|
|
2671
|
+
className: "flex h-9 w-full items-center justify-center rounded-md text-muted-foreground text-xs hover:bg-muted hover:text-foreground disabled:pointer-events-none",
|
|
2672
|
+
disabled: loadingOlderThreads || Boolean(normalizedThreadSearch),
|
|
2673
|
+
onClick: () => void loadOlderThreads(),
|
|
2381
2674
|
type: "button",
|
|
2382
|
-
children: /* @__PURE__ */ jsx(
|
|
2675
|
+
children: loadingOlderThreads ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(LoaderCircleIcon, { className: "mr-1.5 size-3.5 animate-spin" }), normalizedThreadSearch ? t("threads.searching") : t("threads.loadingOlder")] }) : t("threads.loadOlder")
|
|
2383
2676
|
})
|
|
2384
2677
|
]
|
|
2385
|
-
}
|
|
2386
|
-
|
|
2678
|
+
})
|
|
2679
|
+
]
|
|
2387
2680
|
})
|
|
2388
2681
|
]
|
|
2389
2682
|
});
|
|
@@ -2392,6 +2685,7 @@ const SurveyAssistantPanelContent = ({ embedded = false, modelId, models = [], o
|
|
|
2392
2685
|
children: [/* @__PURE__ */ createElement(SurveyAssistantChatPanel, {
|
|
2393
2686
|
...sessionProps,
|
|
2394
2687
|
embedded,
|
|
2688
|
+
hasEarlierMessages: Boolean(nextMessageCursor),
|
|
2395
2689
|
initialLaunch: initialLaunch?.input,
|
|
2396
2690
|
initialMessages,
|
|
2397
2691
|
key: selectedThread.id,
|
|
@@ -2400,6 +2694,7 @@ const SurveyAssistantPanelContent = ({ embedded = false, modelId, models = [], o
|
|
|
2400
2694
|
onInitialLaunchHandled: handleInitialLaunchHandled,
|
|
2401
2695
|
onModelChange: selectModel,
|
|
2402
2696
|
onFirstSubmit: handleFirstSubmit,
|
|
2697
|
+
onLoadEarlierMessages: loadEarlierMessages,
|
|
2403
2698
|
onTurnSettled: handleTurnSettled,
|
|
2404
2699
|
onTurnActiveChange: handleTurnActiveChange,
|
|
2405
2700
|
resourceId,
|
|
@@ -2408,7 +2703,7 @@ const SurveyAssistantPanelContent = ({ embedded = false, modelId, models = [], o
|
|
|
2408
2703
|
threadId: selectedThread.id,
|
|
2409
2704
|
threadTitle: selectedThread.title
|
|
2410
2705
|
}), threadError && /* @__PURE__ */ jsxs("div", {
|
|
2411
|
-
className: "absolute right-3 bottom-3 left-3 z-40 rounded-lg border border-destructive/20 bg-
|
|
2706
|
+
className: "absolute right-3 bottom-3 left-3 z-40 rounded-lg border border-destructive/20 bg-popover px-3 py-2 text-xs shadow-lg",
|
|
2412
2707
|
children: [
|
|
2413
2708
|
/* @__PURE__ */ jsx("div", {
|
|
2414
2709
|
className: "font-medium",
|
|
@@ -2440,6 +2735,69 @@ const SurveyAssistantPanel = ({ language, localizations, ...props }) => /* @__PU
|
|
|
2440
2735
|
})
|
|
2441
2736
|
});
|
|
2442
2737
|
//#endregion
|
|
2738
|
+
//#region src/ui/survey-assistant-thread-launcher-prompt.tsx
|
|
2739
|
+
const SurveyAssistantThreadLauncherPromptContent = ({ className, disabled = false, modelCatalogApi, modelStorageKey, onError, onModelCatalogError, onSubmitted, onValueChange, placeholder, textareaClassName, textareaProps, textareaRef, threadLauncher, uiContext, value }) => {
|
|
2740
|
+
const catalog = useSurveyAssistantModelCatalog({
|
|
2741
|
+
api: modelCatalogApi,
|
|
2742
|
+
storageKey: modelStorageKey
|
|
2743
|
+
});
|
|
2744
|
+
const [submitting, setSubmitting] = useState(false);
|
|
2745
|
+
useEffect(() => {
|
|
2746
|
+
if (catalog.error) onModelCatalogError?.(catalog.error);
|
|
2747
|
+
}, [catalog.error, onModelCatalogError]);
|
|
2748
|
+
const submit = useCallback(async ({ files, text }) => {
|
|
2749
|
+
if (disabled || submitting) return false;
|
|
2750
|
+
setSubmitting(true);
|
|
2751
|
+
try {
|
|
2752
|
+
await threadLauncher.startThread({
|
|
2753
|
+
files,
|
|
2754
|
+
modelId: catalog.modelId,
|
|
2755
|
+
prompt: text,
|
|
2756
|
+
uiContext
|
|
2757
|
+
});
|
|
2758
|
+
onSubmitted();
|
|
2759
|
+
return true;
|
|
2760
|
+
} catch (error) {
|
|
2761
|
+
onError?.(error);
|
|
2762
|
+
return false;
|
|
2763
|
+
} finally {
|
|
2764
|
+
setSubmitting(false);
|
|
2765
|
+
}
|
|
2766
|
+
}, [
|
|
2767
|
+
catalog.modelId,
|
|
2768
|
+
disabled,
|
|
2769
|
+
onError,
|
|
2770
|
+
onSubmitted,
|
|
2771
|
+
submitting,
|
|
2772
|
+
threadLauncher,
|
|
2773
|
+
uiContext
|
|
2774
|
+
]);
|
|
2775
|
+
return /* @__PURE__ */ jsx(SurveyAssistantPromptInput, {
|
|
2776
|
+
attachmentsDisabled: submitting,
|
|
2777
|
+
className,
|
|
2778
|
+
disabled: disabled || submitting,
|
|
2779
|
+
modelDisabled: catalog.loading || submitting,
|
|
2780
|
+
modelId: catalog.modelId,
|
|
2781
|
+
models: catalog.models,
|
|
2782
|
+
onModelChange: catalog.selectModel,
|
|
2783
|
+
onSubmit: submit,
|
|
2784
|
+
onValueChange,
|
|
2785
|
+
placeholder,
|
|
2786
|
+
textareaClassName,
|
|
2787
|
+
textareaProps,
|
|
2788
|
+
textareaRef,
|
|
2789
|
+
value
|
|
2790
|
+
});
|
|
2791
|
+
};
|
|
2792
|
+
const SurveyAssistantThreadLauncherPrompt = ({ language, localizations, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
2793
|
+
className: "case-survey-assistant",
|
|
2794
|
+
children: /* @__PURE__ */ jsx(SurveyAssistantLocalizationProvider, {
|
|
2795
|
+
language,
|
|
2796
|
+
localizations,
|
|
2797
|
+
children: /* @__PURE__ */ jsx(SurveyAssistantThreadLauncherPromptContent, { ...props })
|
|
2798
|
+
})
|
|
2799
|
+
});
|
|
2800
|
+
//#endregion
|
|
2443
2801
|
//#region src/ui/connected-survey-assistant-panel.tsx
|
|
2444
2802
|
const ConnectedSurveyAssistantPanel = ({ modelCatalogApi, modelStorageKey, onModelCatalogError, onModelChange, ...props }) => {
|
|
2445
2803
|
const catalog = useSurveyAssistantModelCatalog({
|
|
@@ -2458,6 +2816,6 @@ const ConnectedSurveyAssistantPanel = ({ modelCatalogApi, modelStorageKey, onMod
|
|
|
2458
2816
|
});
|
|
2459
2817
|
};
|
|
2460
2818
|
//#endregion
|
|
2461
|
-
export { ConnectedSurveyAssistantPanel, SurveyAssistantLocalizationProvider, SurveyAssistantPanel, defaultSurveyAssistantLocalizations, surveyAssistantEnMessages, surveyAssistantNlMessages, useSurveyAssistantTranslation };
|
|
2819
|
+
export { ConnectedSurveyAssistantPanel, SurveyAssistantLocalizationProvider, SurveyAssistantPanel, SurveyAssistantPromptInput, SurveyAssistantThreadLauncherPrompt, defaultSurveyAssistantLocalizations, surveyAssistantEnMessages, surveyAssistantNlMessages, useSurveyAssistantTranslation };
|
|
2462
2820
|
|
|
2463
2821
|
//# sourceMappingURL=ui.mjs.map
|