@docyrus/ui-pro-ai-assistant 0.2.8 → 0.3.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.
@@ -9,10 +9,12 @@ export interface PromptFeatureFlags {
9
9
  files: boolean;
10
10
  }
11
11
  export interface AgentTriggerPromptProps {
12
+ agentId: string | null;
12
13
  capabilities: AgentCapabilities | null;
13
14
  models: Model[];
14
15
  suggestions: string[];
15
16
  placeholder?: string;
16
- onSubmit: (prompt: string, features: PromptFeatureFlags, modelId?: string) => void;
17
+ promptOptimizationChoice?: string | null;
18
+ onSubmit: (prompt: string, features: PromptFeatureFlags, modelId?: string, files?: File[]) => void;
17
19
  }
18
- export declare function AgentTriggerPrompt({ capabilities, models, suggestions, placeholder, onSubmit }: AgentTriggerPromptProps): import("react/jsx-runtime").JSX.Element;
20
+ export declare function AgentTriggerPrompt({ agentId, capabilities, models, suggestions, placeholder, promptOptimizationChoice, onSubmit }: AgentTriggerPromptProps): import("react/jsx-runtime").JSX.Element;
@@ -3,6 +3,6 @@ export interface AgentTriggerWidgetProps {
3
3
  agentIds: string[];
4
4
  className?: string;
5
5
  placeholder?: string;
6
- onSubmit?: (agentId: string, prompt: string, features: PromptFeatureFlags, modelId?: string) => void;
6
+ onSubmit?: (agentId: string, prompt: string, features: PromptFeatureFlags, modelId?: string, files?: File[]) => void;
7
7
  }
8
8
  export declare function AgentTriggerWidget({ agentIds, className, placeholder, onSubmit }: AgentTriggerWidgetProps): import("react/jsx-runtime").JSX.Element;
@@ -44,6 +44,14 @@ interface AIInputAreaProps {
44
44
  threadId?: string;
45
45
  hasMessages?: boolean;
46
46
  tenantAiProjectId?: string;
47
+ initialModelId?: string;
48
+ initialFeatures?: {
49
+ webSearch?: boolean;
50
+ thinking?: boolean;
51
+ deepResearch?: boolean;
52
+ documentSearch?: boolean;
53
+ workCanvas?: boolean;
54
+ };
47
55
  }
48
56
  export declare const AIInputArea: FC<AIInputAreaProps>;
49
57
  export {};
@@ -1,5 +1,5 @@
1
1
  import { type RefObject } from 'react';
2
2
  import { type DocyAssistantProps } from './types';
3
- export declare const DocyAssistant: ({ ref, isOpen, onClose, supportWebSearch, supportThinking, supportFiles, supportDocumentSearch, supportDeepResearch, supportMultiModels, supportWorkCanvas, apiEndpoint, title: titleProp, description: descriptionProp, placeholder: placeholderProp, logo, footerText: footerTextProp, variant, renderMode, enableSidebar, enableNavDropdown, enableVoice, enableMicrophone, tenantAiAgentId, onMessageSend, onVoiceStart, onVoiceEnd, className, defaultFullscreen, hideExpand, hideCloseButton, hideAgentSelector, hideBorder, showHeader, agentSelectorUrl, baseAgentSelectorUrl, onAgentChange, initialPrompt, ...props }: DocyAssistantProps & {
3
+ export declare const DocyAssistant: ({ ref, isOpen, onClose, supportWebSearch, supportThinking, supportFiles, supportDocumentSearch, supportDeepResearch, supportMultiModels, supportWorkCanvas, apiEndpoint, title: titleProp, description: descriptionProp, placeholder: placeholderProp, logo, footerText: footerTextProp, variant, renderMode, enableSidebar, enableNavDropdown, enableVoice, enableMicrophone, tenantAiAgentId, onMessageSend, onVoiceStart, onVoiceEnd, className, defaultFullscreen, hideExpand, hideCloseButton, hideAgentSelector, hideBorder, showHeader, agentSelectorUrl, baseAgentSelectorUrl, onAgentChange, initialPrompt, initialModelId, initialFeatures, initialFiles, ...props }: DocyAssistantProps & {
4
4
  ref?: RefObject<HTMLDivElement | null>;
5
5
  }) => import("react/jsx-runtime").JSX.Element;
@@ -9,6 +9,7 @@ export interface AgentTriggerData {
9
9
  capabilities: AgentCapabilities;
10
10
  models: Model[];
11
11
  standardSuggestions: string[];
12
+ promptOptimizationChoice: string | null;
12
13
  }
13
14
  export interface UseAgentsDataResult {
14
15
  agents: AgentTriggerData[];
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import { Input } from '@docyrus/ui-pro-shared/components/input';
9
9
  import { Tabs as Tabs$1, TabsList as TabsList$1, TabsTrigger as TabsTrigger$1, TabsContent as TabsContent$1 } from '@docyrus/ui-pro-shared/components/tabs';
10
10
  import { cn } from '@docyrus/ui-pro-shared/lib/utils';
11
11
  import { DefaultChatTransport, lastAssistantMessageIsCompleteWithToolCalls } from 'ai';
12
- import { PilcrowIcon, Heading1Icon, Heading2Icon, Heading3Icon, SquareIcon, ListIcon, ListOrderedIcon, ChevronDownIcon, Code2Icon, QuoteIcon, LightbulbIcon, Columns3Icon, GripVertical, FileUpIcon, TableIcon, ImageIcon, FilmIcon, AudioLinesIcon, TableOfContentsIcon, RadicalIcon, RectangleVerticalIcon, CalendarIcon, PlusIcon, FileCodeIcon, MinusIcon, ChevronRightIcon, ListTree, PenToolIcon, Link2Icon, Check, Copy, FilesIcon, Link, Text, ExternalLink, Unlink, Bold, Italic, Underline, Strikethrough, Code2, MoreHorizontal, ArrowLeftIcon, ArrowRightIcon, Minus, Plus, CircleArrowDown, Minimize2, Trash2, FileUp, CheckCircle2, FileText, Loader2, CornerDownLeftIcon, PencilLineIcon, MessageSquareTextIcon, MessagesSquareIcon, ArrowUpIcon, CheckIcon, CaptionsIcon, ZoomInIcon, CircleArrowDownIcon, MoveUpRightIcon, MoreHorizontalIcon, Eye, Pencil, PlusCircle, HelpCircle, Maximize2, X, Download, FileSpreadsheet, ChevronDown, Search, Globe, CheckCircle, FolderOpen, User, ArrowRight, MapPin, CalendarClock, List, RefreshCw, FilePlus, XIcon, CornerUpLeftIcon, AlbumIcon, FeatherIcon, ListMinusIcon, ListPlusIcon, ListEnd, Wand, LanguagesIcon, BadgeHelpIcon, PenLineIcon, SearchIcon, MusicIcon, CompassIcon, SmileIcon, LeafIcon, ClockIcon, AppleIcon, FlagIcon, StarIcon, DeleteIcon, AlignLeft, AlignCenter, AlignRight, RotateCcw, AlertTriangle, XCircle, AlertCircle, ArrowUpDown, ArrowDownToLine, PenLine, PencilIcon, TrashIcon, RefreshCwIcon, PaintRoller, MessageSquareText, ArrowLeft, ChevronLeft, Bot, ChevronRight, Brain, Lightbulb, BookOpen, PenTool, SlidersHorizontal, Mic, RefreshCcw, Code, Star, Trash, Edit, FolderInput, Archive, Sparkles, PanelLeft, MessageSquare, NotebookText, CirclePlus, MoreVertical, FileSearch, Microscope, Ruler, AudioLines, BrainCircuit, File as File$1, Plug, Inbox, Menu, LayoutDashboard, Table2, WandSparklesIcon, ArrowUpToLineIcon, BoldIcon, ItalicIcon, UnderlineIcon, StrikethroughIcon, HighlighterIcon, Undo2Icon, Redo2Icon, ArrowDownToLineIcon, AlignLeftIcon, AlignCenterIcon, AlignRightIcon, AlignJustifyIcon, ListOrdered, ListTodoIcon, ListCollapseIcon, Table, Grid3x3Icon, Combine, Ungroup, ArrowUp, ArrowDown, Trash2Icon, LinkIcon, WrapText, OutdentIcon, IndentIcon, EyeIcon, PenIcon } from 'lucide-react';
12
+ import { PilcrowIcon, Heading1Icon, Heading2Icon, Heading3Icon, SquareIcon, ListIcon, ListOrderedIcon, ChevronDownIcon, Code2Icon, QuoteIcon, LightbulbIcon, Columns3Icon, GripVertical, FileUpIcon, TableIcon, ImageIcon, FilmIcon, AudioLinesIcon, TableOfContentsIcon, RadicalIcon, RectangleVerticalIcon, CalendarIcon, PlusIcon, FileCodeIcon, MinusIcon, ChevronRightIcon, ListTree, PenToolIcon, Link2Icon, Check, Copy, FilesIcon, Link, Text, ExternalLink, Unlink, Bold, Italic, Underline, Strikethrough, Code2, MoreHorizontal, ArrowLeftIcon, ArrowRightIcon, Minus, Plus, CircleArrowDown, Minimize2, Trash2, FileUp, CheckCircle2, FileText, Loader2, CornerDownLeftIcon, PencilLineIcon, MessageSquareTextIcon, MessagesSquareIcon, ArrowUpIcon, CheckIcon, CaptionsIcon, ZoomInIcon, CircleArrowDownIcon, MoveUpRightIcon, MoreHorizontalIcon, Eye, Pencil, PlusCircle, HelpCircle, Maximize2, X, Download, FileSpreadsheet, ChevronDown, Search, Globe, CheckCircle, FolderOpen, User, ArrowRight, MapPin, CalendarClock, List, RefreshCw, FilePlus, XIcon, CornerUpLeftIcon, AlbumIcon, FeatherIcon, ListMinusIcon, ListPlusIcon, ListEnd, Wand, LanguagesIcon, BadgeHelpIcon, PenLineIcon, SearchIcon, MusicIcon, CompassIcon, SmileIcon, LeafIcon, ClockIcon, AppleIcon, FlagIcon, StarIcon, DeleteIcon, AlignLeft, AlignCenter, AlignRight, RotateCcw, AlertTriangle, XCircle, AlertCircle, ArrowUpDown, ArrowDownToLine, PenLine, PencilIcon, TrashIcon, RefreshCwIcon, PaintRoller, MessageSquareText, ArrowLeft, ChevronLeft, Bot, ChevronRight, Brain, Lightbulb, BookOpen, PenTool, SlidersHorizontal, Wand2, Mic, RefreshCcw, Code, Star, Trash, Edit, FolderInput, Archive, Sparkles, PanelLeft, MessageSquare, NotebookText, CirclePlus, MoreVertical, FileSearch, Microscope, Ruler, AudioLines, BrainCircuit, File as File$1, Plug, Inbox, Menu, LayoutDashboard, Table2, WandSparklesIcon, ArrowUpToLineIcon, BoldIcon, ItalicIcon, UnderlineIcon, StrikethroughIcon, HighlighterIcon, Undo2Icon, Redo2Icon, ArrowDownToLineIcon, AlignLeftIcon, AlignCenterIcon, AlignRightIcon, AlignJustifyIcon, ListOrdered, ListTodoIcon, ListCollapseIcon, Table, Grid3x3Icon, Combine, Ungroup, ArrowUp, ArrowDown, Trash2Icon, LinkIcon, WrapText, OutdentIcon, IndentIcon, EyeIcon, PenIcon } from 'lucide-react';
13
13
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
14
14
  import { AsyncTokenManager, RestApiClient } from '@docyrus/api-client';
15
15
  import { TooltipProvider, Tooltip as Tooltip$1, TooltipTrigger, TooltipContent } from '@docyrus/ui-pro-shared/components/tooltip';
@@ -2552,7 +2552,8 @@ function toAgentTriggerData(id, data) {
2552
2552
  description: agent.description ?? null,
2553
2553
  capabilities: extractCapabilities(agent),
2554
2554
  models: agent.models ?? [],
2555
- standardSuggestions: agent.standardSuggestions ?? agent.standard_suggestions ?? []
2555
+ standardSuggestions: agent.standardSuggestions ?? agent.standard_suggestions ?? [],
2556
+ promptOptimizationChoice: agent.promptOptimizationChoice ?? agent.prompt_optimization_choice ?? null
2556
2557
  };
2557
2558
  }
2558
2559
  function useAgentsData(agentIds) {
@@ -3978,7 +3979,9 @@ var AIInputArea = ({
3978
3979
  compactToolbar = false,
3979
3980
  threadId,
3980
3981
  hasMessages = false,
3981
- tenantAiProjectId
3982
+ tenantAiProjectId,
3983
+ initialModelId,
3984
+ initialFeatures
3982
3985
  }) => {
3983
3986
  const { t } = useAssistantTranslation();
3984
3987
  const isBaseAgent = !deploymentId && !!tenantAiAgentId;
@@ -4003,11 +4006,21 @@ var AIInputArea = ({
4003
4006
  const effectiveSupportThinking = capabilities ? capabilities.supportThinking : supportThinking;
4004
4007
  const effectiveSupportDeepResearch = capabilities ? capabilities.supportDeepResearch : supportDeepResearch;
4005
4008
  const effectiveSupportWorkCanvas = capabilities ? capabilities.supportWorkCanvas : supportWorkCanvas;
4006
- const [webSearchActive, setWebSearchActive] = useState(false);
4007
- const [documentSearchActive, setDocumentSearchActive] = useState(false);
4008
- const [thinkingActive, setThinkingActive] = useState(false);
4009
- const [deepResearchActive, setDeepResearchActive] = useState(false);
4010
- const [workCanvasActive, setWorkCanvasActive] = useState(false);
4009
+ const [webSearchActive, setWebSearchActive] = useState(initialFeatures?.webSearch ?? false);
4010
+ const [documentSearchActive, setDocumentSearchActive] = useState(initialFeatures?.documentSearch ?? false);
4011
+ const [thinkingActive, setThinkingActive] = useState(initialFeatures?.thinking ?? false);
4012
+ const [deepResearchActive, setDeepResearchActive] = useState(initialFeatures?.deepResearch ?? false);
4013
+ const [workCanvasActive, setWorkCanvasActive] = useState(initialFeatures?.workCanvas ?? false);
4014
+ const initialModelAppliedRef = useRef(false);
4015
+ useEffect(() => {
4016
+ if (initialModelId && models.length > 0 && !initialModelAppliedRef.current) {
4017
+ const match = models.find((m) => m.id === initialModelId);
4018
+ if (match) {
4019
+ setSelectedModel(match);
4020
+ initialModelAppliedRef.current = true;
4021
+ }
4022
+ }
4023
+ }, [initialModelId, models, setSelectedModel]);
4011
4024
  const toggleActiveClass = "bg-primary/10 text-primary hover:bg-primary/20 ring-1 ring-primary/30";
4012
4025
  const [memoryExtractionOpen, setMemoryExtractionOpen] = useState(false);
4013
4026
  const [memoryExtractionLoading, setMemoryExtractionLoading] = useState(false);
@@ -7370,7 +7383,9 @@ function ChatPanel({
7370
7383
  renderThreadHeader,
7371
7384
  messagesClassName,
7372
7385
  compactToolbar,
7373
- threadId
7386
+ threadId,
7387
+ initialModelId,
7388
+ initialFeatures
7374
7389
  }) {
7375
7390
  return /* @__PURE__ */ jsxs(Fragment, { children: [
7376
7391
  renderThreadHeader?.(),
@@ -7418,7 +7433,9 @@ function ChatPanel({
7418
7433
  showToolbar: !compactToolbar,
7419
7434
  compactToolbar,
7420
7435
  threadId,
7421
- hasMessages: messages.length > 0
7436
+ hasMessages: messages.length > 0,
7437
+ initialModelId,
7438
+ initialFeatures
7422
7439
  }
7423
7440
  ) })
7424
7441
  ] });
@@ -22504,6 +22521,8 @@ var AssistantView = ({ ref, ...props }) => {
22504
22521
  onToolAction: commonProps2.onToolAction,
22505
22522
  openCanvasView: handleOpenCanvasView,
22506
22523
  threadId: commonProps2.threadId,
22524
+ initialModelId: commonProps2.initialModelId,
22525
+ initialFeatures: commonProps2.initialFeatures,
22507
22526
  messagesClassName: "p-4"
22508
22527
  }
22509
22528
  );
@@ -22953,6 +22972,8 @@ var AssistantView = ({ ref, ...props }) => {
22953
22972
  openCanvasView: handleOpenCanvasViewInline,
22954
22973
  renderThreadHeader,
22955
22974
  threadId: commonProps.threadId,
22975
+ initialModelId: commonProps.initialModelId,
22976
+ initialFeatures: commonProps.initialFeatures,
22956
22977
  compactToolbar: !isFullscreen
22957
22978
  }
22958
22979
  )
@@ -24677,6 +24698,9 @@ var DocyAssistant = ({
24677
24698
  baseAgentSelectorUrl = "/ai/agent-deployments/base",
24678
24699
  onAgentChange,
24679
24700
  initialPrompt,
24701
+ initialModelId,
24702
+ initialFeatures,
24703
+ initialFiles,
24680
24704
  ...props
24681
24705
  }) => {
24682
24706
  const config3 = useAssistantConfig();
@@ -25024,7 +25048,42 @@ var DocyAssistant = ({
25024
25048
  if (!initialPrompt || initialPromptSentRef.current) return;
25025
25049
  initialPromptSentRef.current = true;
25026
25050
  setInput(initialPrompt);
25027
- void handleSendMessage(void 0, void 0, initialPrompt);
25051
+ const buildOptionsAndSend = async () => {
25052
+ const initialOptions = {};
25053
+ if (initialModelId) initialOptions.modelId = initialModelId;
25054
+ if (initialFeatures) {
25055
+ initialOptions.supportWebSearch = initialFeatures.webSearch;
25056
+ initialOptions.supportThinking = initialFeatures.thinking;
25057
+ initialOptions.supportDeepResearch = initialFeatures.deepResearch;
25058
+ initialOptions.supportDocumentSearch = initialFeatures.documentSearch;
25059
+ initialOptions.supportWorkCanvas = initialFeatures.workCanvas;
25060
+ initialOptions.supportFiles = initialFeatures.files;
25061
+ }
25062
+ if (initialFiles && initialFiles.length > 0) {
25063
+ const fileData = [];
25064
+ for (const file of initialFiles) {
25065
+ try {
25066
+ const url = await new Promise((resolve, reject) => {
25067
+ const reader = new FileReader();
25068
+ reader.onload = () => resolve(reader.result);
25069
+ reader.onerror = reject;
25070
+ reader.readAsDataURL(file);
25071
+ });
25072
+ fileData.push({ url, mediaType: file.type, filename: file.name });
25073
+ } catch {
25074
+ }
25075
+ }
25076
+ if (fileData.length > 0) {
25077
+ initialOptions.files = fileData;
25078
+ }
25079
+ }
25080
+ void handleSendMessage(
25081
+ void 0,
25082
+ Object.keys(initialOptions).length > 0 ? initialOptions : void 0,
25083
+ initialPrompt
25084
+ );
25085
+ };
25086
+ void buildOptionsAndSend();
25028
25087
  }, [initialPrompt]);
25029
25088
  const handleInputChange = useCallback((e) => {
25030
25089
  setInput(e.target.value);
@@ -25193,6 +25252,8 @@ var DocyAssistant = ({
25193
25252
  supportMultiModels,
25194
25253
  deploymentId,
25195
25254
  tenantAiAgentId: activeAgentId,
25255
+ initialModelId,
25256
+ initialFeatures,
25196
25257
  enableMicrophone,
25197
25258
  enableVoice,
25198
25259
  isRecording,
@@ -25845,6 +25906,7 @@ var CAPABILITY_TOGGLES = [
25845
25906
  labelKey: "tools.file_upload"
25846
25907
  }
25847
25908
  ];
25909
+ var ACCEPTED_FILE_TYPES = ".pdf,.doc,.docx,.txt,.csv,.xlsx,.xls,.json,.xml,.png,.jpg,.jpeg,.gif,.webp";
25848
25910
  function ToggleSwitch({
25849
25911
  checked,
25850
25912
  onChange,
@@ -25887,13 +25949,16 @@ function useClickOutside(ref, open, onClose) {
25887
25949
  }, [ref, open, onClose]);
25888
25950
  }
25889
25951
  function AgentTriggerPrompt({
25952
+ agentId,
25890
25953
  capabilities,
25891
25954
  models,
25892
25955
  suggestions,
25893
25956
  placeholder,
25957
+ promptOptimizationChoice,
25894
25958
  onSubmit
25895
25959
  }) {
25896
25960
  const { t } = useAssistantTranslation();
25961
+ const apiClient = useApiClient();
25897
25962
  const [prompt, setPrompt] = useState("");
25898
25963
  const [selectedModel, setSelectedModel] = useState(
25899
25964
  models[0]
@@ -25913,9 +25978,24 @@ function AgentTriggerPrompt({
25913
25978
  const settingsRef = useRef(null);
25914
25979
  const modelRef = useRef(null);
25915
25980
  const reasoningRef = useRef(null);
25981
+ const [attachedFiles, setAttachedFiles] = useState([]);
25982
+ const fileInputRef = useRef(null);
25983
+ const [generatedSuggestions, setGeneratedSuggestions] = useState([]);
25984
+ const [isGeneratingSuggestions, setIsGeneratingSuggestions] = useState(false);
25985
+ const [isRecording, setIsRecording] = useState(false);
25986
+ const [isTranscribing, setIsTranscribing] = useState(false);
25987
+ const mediaRecorderRef = useRef(null);
25988
+ const audioChunksRef = useRef([]);
25989
+ const [isOptimizing, setIsOptimizing] = useState(false);
25990
+ const showOptimize = promptOptimizationChoice === "manual";
25916
25991
  useClickOutside(settingsRef, settingsOpen, () => setSettingsOpen(false));
25917
25992
  useClickOutside(modelRef, modelDropdownOpen, () => setModelDropdownOpen(false));
25918
25993
  useClickOutside(reasoningRef, reasoningDropdownOpen, () => setReasoningDropdownOpen(false));
25994
+ useEffect(() => {
25995
+ if (models.length > 0 && !models.find((m) => m.id === selectedModel?.id)) {
25996
+ setSelectedModel(models[0]);
25997
+ }
25998
+ }, [models, selectedModel?.id]);
25919
25999
  const hasThinking = capabilities?.supportThinking || selectedModel?.supportThinking;
25920
26000
  const reasoningLevels = useMemo(
25921
26001
  () => hasThinking ? selectedModel?.reasoningLevels ?? [] : [],
@@ -25932,16 +26012,130 @@ function AgentTriggerPrompt({
25932
26012
  const toggleFeature = useCallback((key) => {
25933
26013
  setFeatures((prev) => ({ ...prev, [key]: !prev[key] }));
25934
26014
  }, []);
26015
+ const allSuggestions = useMemo(() => {
26016
+ const set = /* @__PURE__ */ new Set([...suggestions, ...generatedSuggestions]);
26017
+ return [...set];
26018
+ }, [suggestions, generatedSuggestions]);
26019
+ const generateSuggestions = useCallback(async () => {
26020
+ if (!agentId || isGeneratingSuggestions) return;
26021
+ setIsGeneratingSuggestions(true);
26022
+ try {
26023
+ const res = await apiClient.post(`/ai/agents/${agentId}/suggestions`, {
26024
+ prompt: prompt || void 0
26025
+ });
26026
+ if (res.success && res.data) {
26027
+ const data = res.data;
26028
+ if (data.promptSuggestions?.length) {
26029
+ setGeneratedSuggestions(data.promptSuggestions);
26030
+ }
26031
+ }
26032
+ } catch {
26033
+ } finally {
26034
+ setIsGeneratingSuggestions(false);
26035
+ }
26036
+ }, [
26037
+ agentId,
26038
+ apiClient,
26039
+ isGeneratingSuggestions,
26040
+ prompt
26041
+ ]);
26042
+ const handleFileSelect = useCallback(() => {
26043
+ fileInputRef.current?.click();
26044
+ }, []);
26045
+ const handleFileChange = useCallback((e) => {
26046
+ const { files } = e.target;
26047
+ if (!files) return;
26048
+ setAttachedFiles((prev) => [...prev, ...Array.from(files)]);
26049
+ e.target.value = "";
26050
+ }, []);
26051
+ const removeFile = useCallback((index) => {
26052
+ setAttachedFiles((prev) => prev.filter((_, i) => i !== index));
26053
+ }, []);
26054
+ const startRecording = useCallback(async () => {
26055
+ try {
26056
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
26057
+ const mediaRecorder = new MediaRecorder(stream);
26058
+ audioChunksRef.current = [];
26059
+ mediaRecorder.ondataavailable = (e) => {
26060
+ if (e.data.size > 0) {
26061
+ audioChunksRef.current.push(e.data);
26062
+ }
26063
+ };
26064
+ mediaRecorder.onstop = async () => {
26065
+ stream.getTracks().forEach((track) => track.stop());
26066
+ const audioBlob = new Blob(audioChunksRef.current, { type: "audio/webm" });
26067
+ if (audioBlob.size === 0) return;
26068
+ setIsTranscribing(true);
26069
+ try {
26070
+ const formData = new FormData();
26071
+ formData.append("file", audioBlob, "recording.webm");
26072
+ const res = await apiClient.post("/ai/transcribe", formData);
26073
+ if (res.success && res.data) {
26074
+ const data = res.data;
26075
+ const transcript = data.transcription?.text || "";
26076
+ if (transcript) {
26077
+ setPrompt((prev) => prev ? `${prev} ${transcript}` : transcript);
26078
+ }
26079
+ }
26080
+ } catch {
26081
+ } finally {
26082
+ setIsTranscribing(false);
26083
+ }
26084
+ };
26085
+ mediaRecorderRef.current = mediaRecorder;
26086
+ mediaRecorder.start();
26087
+ setIsRecording(true);
26088
+ } catch {
26089
+ }
26090
+ }, [apiClient]);
26091
+ const stopRecording = useCallback(() => {
26092
+ if (mediaRecorderRef.current && mediaRecorderRef.current.state === "recording") {
26093
+ mediaRecorderRef.current.stop();
26094
+ setIsRecording(false);
26095
+ }
26096
+ }, []);
26097
+ const handleMicClick = useCallback(() => {
26098
+ if (isRecording) {
26099
+ stopRecording();
26100
+ } else {
26101
+ void startRecording();
26102
+ }
26103
+ }, [isRecording, startRecording, stopRecording]);
26104
+ const optimizePrompt = useCallback(async () => {
26105
+ if (!agentId || !prompt.trim() || isOptimizing) return;
26106
+ setIsOptimizing(true);
26107
+ try {
26108
+ const res = await apiClient.post(`/ai/agents/${agentId}/optimize`, {
26109
+ prompt: prompt.trim()
26110
+ });
26111
+ if (res.success && res.data) {
26112
+ const data = res.data;
26113
+ if (data.optimizedPrompt) {
26114
+ setPrompt(data.optimizedPrompt);
26115
+ }
26116
+ }
26117
+ } catch {
26118
+ } finally {
26119
+ setIsOptimizing(false);
26120
+ }
26121
+ }, [
26122
+ agentId,
26123
+ apiClient,
26124
+ isOptimizing,
26125
+ prompt
26126
+ ]);
25935
26127
  const handleSubmit = useCallback(() => {
25936
26128
  const trimmed = prompt.trim();
25937
26129
  if (!trimmed) return;
25938
- onSubmit(trimmed, features, selectedModel?.id);
26130
+ onSubmit(trimmed, features, selectedModel?.id, attachedFiles.length > 0 ? attachedFiles : void 0);
25939
26131
  setPrompt("");
26132
+ setAttachedFiles([]);
25940
26133
  }, [
25941
26134
  prompt,
25942
26135
  features,
25943
26136
  selectedModel,
25944
- onSubmit
26137
+ onSubmit,
26138
+ attachedFiles
25945
26139
  ]);
25946
26140
  const handleKeyDown = useCallback(
25947
26141
  (e) => {
@@ -25964,17 +26158,32 @@ function AgentTriggerPrompt({
25964
26158
  return true;
25965
26159
  }) : [];
25966
26160
  return /* @__PURE__ */ jsxs("div", { className: "mt-4 mb-1 w-full max-w-[54rem]", children: [
25967
- suggestions.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-2", children: [
25968
- /* @__PURE__ */ jsx(
26161
+ /* @__PURE__ */ jsx(
26162
+ "input",
26163
+ {
26164
+ ref: fileInputRef,
26165
+ type: "file",
26166
+ multiple: true,
26167
+ accept: ACCEPTED_FILE_TYPES,
26168
+ className: "hidden",
26169
+ onChange: handleFileChange
26170
+ }
26171
+ ),
26172
+ agentId && /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-2", children: [
26173
+ isGeneratingSuggestions ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs font-medium text-muted-foreground", children: [
26174
+ /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }),
26175
+ /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: "Generating suggestions..." })
26176
+ ] }) : /* @__PURE__ */ jsx(
25969
26177
  "button",
25970
26178
  {
25971
26179
  type: "button",
25972
- title: t("common.generate_suggestions") || "Generate Suggestions",
26180
+ title: "Generate Suggestions",
26181
+ onClick: generateSuggestions,
25973
26182
  className: "flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground transition-colors hover:bg-muted/80 hover:text-foreground",
25974
26183
  children: /* @__PURE__ */ jsx(Plus, { className: "size-4" })
25975
26184
  }
25976
26185
  ),
25977
- /* @__PURE__ */ jsx("div", { className: "suggestions-container flex items-center gap-2 overflow-x-auto", children: suggestions.map((s) => /* @__PURE__ */ jsx(
26186
+ allSuggestions.length > 0 && /* @__PURE__ */ jsx("div", { className: "suggestions-container flex items-center gap-2 overflow-x-auto", children: allSuggestions.map((s) => /* @__PURE__ */ jsx(
25978
26187
  "button",
25979
26188
  {
25980
26189
  type: "button",
@@ -25998,6 +26207,26 @@ function AgentTriggerPrompt({
25998
26207
  rows: 2
25999
26208
  }
26000
26209
  ),
26210
+ attachedFiles.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 px-3 pb-2", children: attachedFiles.map((file, index) => /* @__PURE__ */ jsxs(
26211
+ "div",
26212
+ {
26213
+ className: "flex items-center gap-1.5 rounded-md border border-border bg-background px-2 py-1 text-xs text-foreground",
26214
+ children: [
26215
+ /* @__PURE__ */ jsx(FileUp, { className: "size-3.5 text-muted-foreground" }),
26216
+ /* @__PURE__ */ jsx("span", { className: "max-w-32 truncate", children: file.name }),
26217
+ /* @__PURE__ */ jsx(
26218
+ "button",
26219
+ {
26220
+ type: "button",
26221
+ onClick: () => removeFile(index),
26222
+ className: "ml-0.5 text-muted-foreground transition-colors hover:text-foreground",
26223
+ children: /* @__PURE__ */ jsx(X, { className: "size-3" })
26224
+ }
26225
+ )
26226
+ ]
26227
+ },
26228
+ `${file.name}-${index}`
26229
+ )) }),
26001
26230
  /* @__PURE__ */ jsxs("div", { className: "flex h-10 items-center justify-between px-2", children: [
26002
26231
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
26003
26232
  capabilities?.supportFiles && /* @__PURE__ */ jsx(
@@ -26005,6 +26234,7 @@ function AgentTriggerPrompt({
26005
26234
  {
26006
26235
  type: "button",
26007
26236
  title: t("tools.file_upload"),
26237
+ onClick: handleFileSelect,
26008
26238
  className: "flex size-7 items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground",
26009
26239
  children: /* @__PURE__ */ jsx(Plus, { className: "size-5" })
26010
26240
  }
@@ -26118,14 +26348,34 @@ function AgentTriggerPrompt({
26118
26348
  )) })
26119
26349
  ] })
26120
26350
  ] }),
26121
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsx(
26122
- "button",
26123
- {
26124
- type: "button",
26125
- className: "flex size-7 items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground",
26126
- children: /* @__PURE__ */ jsx(Mic, { className: "size-5" })
26127
- }
26128
- ) })
26351
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
26352
+ showOptimize && /* @__PURE__ */ jsx(
26353
+ "button",
26354
+ {
26355
+ type: "button",
26356
+ title: t("common.optimize_prompt") || "Optimize Prompt",
26357
+ disabled: isOptimizing || !prompt.trim(),
26358
+ onClick: () => void optimizePrompt(),
26359
+ className: "flex size-7 items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground disabled:opacity-50",
26360
+ children: isOptimizing ? /* @__PURE__ */ jsx(Loader2, { className: "size-5 animate-spin" }) : /* @__PURE__ */ jsx(Wand2, { className: "size-5" })
26361
+ }
26362
+ ),
26363
+ /* @__PURE__ */ jsx(
26364
+ "button",
26365
+ {
26366
+ type: "button",
26367
+ title: isRecording ? t("common.stop_recording") || "Stop Recording" : t("common.start_recording") || "Start Recording",
26368
+ disabled: isTranscribing,
26369
+ onClick: handleMicClick,
26370
+ className: cn(
26371
+ "flex size-7 items-center justify-center rounded transition-colors",
26372
+ isRecording ? "text-red-500 animate-pulse hover:text-red-600" : "text-muted-foreground hover:text-foreground",
26373
+ isTranscribing && "opacity-50"
26374
+ ),
26375
+ children: isTranscribing ? /* @__PURE__ */ jsx(Loader2, { className: "size-5 animate-spin" }) : /* @__PURE__ */ jsx(Mic, { className: "size-5" })
26376
+ }
26377
+ )
26378
+ ] })
26129
26379
  ] })
26130
26380
  ] })
26131
26381
  ] });
@@ -26156,9 +26406,9 @@ function AgentTriggerWidget({
26156
26406
  [agents.length]
26157
26407
  );
26158
26408
  const handlePromptSubmit = useCallback(
26159
- (prompt, features, modelId) => {
26409
+ (prompt, features, modelId, files) => {
26160
26410
  if (!currentAgent) return;
26161
- onSubmit?.(currentAgent.id, prompt, features, modelId);
26411
+ onSubmit?.(currentAgent.id, prompt, features, modelId, files);
26162
26412
  },
26163
26413
  [currentAgent, onSubmit]
26164
26414
  );
@@ -26242,9 +26492,11 @@ function AgentTriggerWidget({
26242
26492
  /* @__PURE__ */ jsx("div", { className: "flex h-fit w-full grow items-end justify-center px-6 pb-6", children: /* @__PURE__ */ jsx(
26243
26493
  AgentTriggerPrompt,
26244
26494
  {
26495
+ agentId: currentAgent?.id ?? null,
26245
26496
  capabilities: currentAgent?.capabilities ?? null,
26246
26497
  models: currentAgent?.models ?? [],
26247
26498
  suggestions: currentAgent?.standardSuggestions ?? [],
26499
+ promptOptimizationChoice: currentAgent?.promptOptimizationChoice,
26248
26500
  placeholder,
26249
26501
  onSubmit: handlePromptSubmit
26250
26502
  }