@topconsultnpm/sdkui-react 6.22.0-dev3.24 → 6.22.0-dev3.26

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.
Files changed (149) hide show
  1. package/lib/components/NewComponents/ContextMenu/styles.d.ts +43 -45
  2. package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +60 -67
  3. package/lib/components/base/Styled.d.ts +77 -93
  4. package/lib/components/base/TMAlert.js +6 -8
  5. package/lib/components/base/TMAreaManager.js +53 -55
  6. package/lib/components/base/TMCounterBar.d.ts +2 -2
  7. package/lib/components/base/TMCounterContainer.d.ts +1 -2
  8. package/lib/components/base/TMCustomButton.d.ts +1 -1
  9. package/lib/components/base/TMCustomButton.js +39 -48
  10. package/lib/components/base/TMDataGridExportForm.d.ts +1 -2
  11. package/lib/components/base/TMFileManager.d.ts +1 -2
  12. package/lib/components/base/TMFileManagerDataGridView.d.ts +1 -2
  13. package/lib/components/base/TMFileManagerThumbnailItems.d.ts +1 -1
  14. package/lib/components/base/TMFileManagerThumbnailsView.d.ts +1 -1
  15. package/lib/components/base/TMFileManagerUtils.d.ts +6 -4
  16. package/lib/components/base/TMLayout.d.ts +4 -4
  17. package/lib/components/base/TMList.d.ts +1 -2
  18. package/lib/components/base/TMListView.d.ts +1 -1
  19. package/lib/components/base/TMPanel.js +167 -167
  20. package/lib/components/base/TMSpinner.d.ts +1 -2
  21. package/lib/components/base/TMTab.d.ts +3 -4
  22. package/lib/components/base/TMTabControl.js +208 -208
  23. package/lib/components/base/TMTooltip.d.ts +1 -1
  24. package/lib/components/base/TMTreeView.d.ts +8 -14
  25. package/lib/components/base/TMTreeView.js +14 -0
  26. package/lib/components/base/TMUserAvatar.d.ts +1 -2
  27. package/lib/components/base/TMVilViewer.d.ts +1 -2
  28. package/lib/components/base/TMWaitPanel.d.ts +2 -3
  29. package/lib/components/base/waitPanel/TMWaitPanelConfirm.d.ts +12 -0
  30. package/lib/components/base/waitPanel/TMWaitPanelConfirm.js +185 -0
  31. package/lib/components/base/waitPanel/TMWaitPanelManager.d.ts +169 -0
  32. package/lib/components/base/waitPanel/TMWaitPanelManager.js +320 -0
  33. package/lib/components/base/waitPanel/TMWaitPanelView.d.ts +52 -0
  34. package/lib/components/base/waitPanel/TMWaitPanelView.js +274 -0
  35. package/lib/components/base/waitPanel/index.d.ts +1 -0
  36. package/lib/components/base/waitPanel/index.js +1 -0
  37. package/lib/components/choosers/TMDynDataListItemChooser.d.ts +1 -1
  38. package/lib/components/choosers/TMInvoiceRetrieveFormats.d.ts +1 -2
  39. package/lib/components/choosers/TMOrderRetrieveFormats.d.ts +1 -2
  40. package/lib/components/choosers/TMPathChooser.js +17 -34
  41. package/lib/components/choosers/TMQuickSearchInfo.d.ts +1 -2
  42. package/lib/components/choosers/TMRelationChooser.d.ts +3 -3
  43. package/lib/components/choosers/TMUserChooser.d.ts +2 -2
  44. package/lib/components/editors/TMEditorStyled.d.ts +42 -20
  45. package/lib/components/editors/TMFormulaEditor.d.ts +1 -1
  46. package/lib/components/editors/TMHtmlContentDisplay.d.ts +1 -2
  47. package/lib/components/editors/TMHtmlEditor.d.ts +1 -2
  48. package/lib/components/editors/TMTreeDropDown.d.ts +1 -1
  49. package/lib/components/features/assistant/TMHelpOnLineChat.d.ts +28 -0
  50. package/lib/components/features/assistant/TMHelpOnLineChat.js +871 -0
  51. package/lib/components/features/assistant/TMToppyDraggableHelpCenter.d.ts +1 -1
  52. package/lib/components/features/assistant/TMToppyDraggableHelpCenter.js +2 -2
  53. package/lib/components/features/blog/TMBlogCommentForm.d.ts +1 -2
  54. package/lib/components/features/documents/TMBatchUpdateForm.js +3 -4
  55. package/lib/components/features/documents/TMCopyToFolderForm.js +32 -35
  56. package/lib/components/features/documents/TMDcmtForm.js +48 -60
  57. package/lib/components/features/documents/TMDcmtFormActionButtons.d.ts +1 -1
  58. package/lib/components/features/documents/TMDcmtIcon.d.ts +1 -4
  59. package/lib/components/features/documents/TMDcmtIcon.js +3 -5
  60. package/lib/components/features/documents/TMDcmtPreview.d.ts +6 -8
  61. package/lib/components/features/documents/TMDcmtPreview.js +5 -3
  62. package/lib/components/features/documents/TMDcmtTasks.d.ts +1 -2
  63. package/lib/components/features/documents/TMDragDropOverlay.d.ts +1 -1
  64. package/lib/components/features/documents/TMFileUploader.js +21 -21
  65. package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +2 -0
  66. package/lib/components/features/documents/TMMasterDetailDcmts.js +45 -147
  67. package/lib/components/features/documents/TMMasterInfoFields.d.ts +1 -2
  68. package/lib/components/features/documents/TMMergeToPdfForm.d.ts +1 -2
  69. package/lib/components/features/documents/TMMergeToPdfForm.js +48 -47
  70. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.d.ts +1 -2
  71. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +14 -15
  72. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +4 -4
  73. package/lib/components/features/documents/TMRelationViewer.d.ts +3 -14
  74. package/lib/components/features/documents/TMRelationViewer.js +115 -79
  75. package/lib/components/features/search/TMDcmtCheckoutInfoForm.d.ts +1 -2
  76. package/lib/components/features/search/TMMetadataOutputForm.d.ts +1 -2
  77. package/lib/components/features/search/TMMetadataSorterForm.d.ts +1 -2
  78. package/lib/components/features/search/TMSavedQuerySelector.d.ts +2 -2
  79. package/lib/components/features/search/TMSearch.d.ts +1 -2
  80. package/lib/components/features/search/TMSearchQueryPanel.d.ts +3 -9
  81. package/lib/components/features/search/TMSearchResult.js +6 -8
  82. package/lib/components/features/search/TMSearchResultFloatingActionButton.d.ts +1 -1
  83. package/lib/components/features/search/TMSignatureInfoContent.d.ts +1 -2
  84. package/lib/components/features/search/TMViewHistoryDcmt.d.ts +1 -2
  85. package/lib/components/features/search/TMViewHistoryDcmt.js +14 -25
  86. package/lib/components/features/tasks/TMTaskForm.d.ts +1 -1
  87. package/lib/components/features/tasks/TMTaskFormUtils.d.ts +10 -10
  88. package/lib/components/features/tasks/TMTasksAgenda.d.ts +1 -1
  89. package/lib/components/features/tasks/TMTasksCalendar.d.ts +1 -1
  90. package/lib/components/features/tasks/TMTasksHeader.d.ts +1 -1
  91. package/lib/components/features/tasks/TMTasksPanelContent.d.ts +1 -1
  92. package/lib/components/features/tasks/TMTasksUtilsView.d.ts +5 -5
  93. package/lib/components/features/tasks/TMTasksView.d.ts +1 -1
  94. package/lib/components/features/tasks/TMTasksView.js +91 -90
  95. package/lib/components/features/wg/TMWGsCopyMoveForm.d.ts +2 -3
  96. package/lib/components/features/wg/TMWGsCopyMoveForm.js +91 -91
  97. package/lib/components/features/workflow/TMWorkflowPopup.d.ts +7 -8
  98. package/lib/components/features/workflow/diagram/WorkitemRecipientsEditor.d.ts +1 -3
  99. package/lib/components/forms/Login/ChangePasswordInputs.d.ts +1 -3
  100. package/lib/components/forms/Login/Chooser.d.ts +2 -2
  101. package/lib/components/forms/TMChooserForm.d.ts +1 -2
  102. package/lib/components/forms/TMSaveForm.d.ts +4 -4
  103. package/lib/components/grids/TMBlogAttachments.d.ts +1 -2
  104. package/lib/components/grids/TMBlogHeader.d.ts +1 -1
  105. package/lib/components/grids/TMBlogsPost.d.ts +1 -1
  106. package/lib/components/grids/TMBlogsPost.js +92 -96
  107. package/lib/components/grids/TMBlogsPostUtils.d.ts +9 -9
  108. package/lib/components/grids/TMValidationItemsList.d.ts +1 -2
  109. package/lib/components/index.d.ts +4 -0
  110. package/lib/components/index.js +3 -0
  111. package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +1 -2
  112. package/lib/components/layout/panelManager/TMPanelManagerContext.d.ts +2 -2
  113. package/lib/components/layout/panelManager/TMPanelManagerToolbar.d.ts +6 -5
  114. package/lib/components/layout/panelManager/TMPanelManagerWithPersistenceProvider.d.ts +2 -2
  115. package/lib/components/layout/panelManager/TMPanelWrapper.d.ts +2 -2
  116. package/lib/components/pages/TMPage.d.ts +1 -1
  117. package/lib/components/pages/TMPage.js +29 -31
  118. package/lib/components/query/TMQueryEditor.d.ts +10 -14
  119. package/lib/components/settings/SettingsAppearance.d.ts +1 -2
  120. package/lib/components/sidebar/TMAboutApp.d.ts +1 -2
  121. package/lib/components/sidebar/TMCommandsPanel.d.ts +4 -4
  122. package/lib/components/sidebar/TMHeader.d.ts +3 -3
  123. package/lib/components/viewers/TMDataListItemViewer.d.ts +2 -3
  124. package/lib/components/viewers/TMDataUserIdItemViewer.d.ts +2 -3
  125. package/lib/components/viewers/TMMidViewer.d.ts +2 -2
  126. package/lib/components/viewers/TMTidViewer.d.ts +2 -2
  127. package/lib/helper/SDKUI_Localizator.d.ts +7 -0
  128. package/lib/helper/SDKUI_Localizator.js +70 -0
  129. package/lib/helper/TMAppUpdatedModal.d.ts +1 -2
  130. package/lib/helper/TMAppUpdatedModal.js +93 -93
  131. package/lib/helper/TMCommandsContextMenu.d.ts +1 -1
  132. package/lib/helper/TMIcons.d.ts +280 -280
  133. package/lib/helper/TMPdfViewer.d.ts +1 -2
  134. package/lib/helper/TMToppyMessage.d.ts +2 -2
  135. package/lib/helper/TMUtils.d.ts +4 -10
  136. package/lib/helper/ZipManager.d.ts +1 -1
  137. package/lib/helper/ZipManager.js +2 -1
  138. package/lib/helper/helpers.d.ts +1 -1
  139. package/lib/hooks/useCheckInOutOperations.d.ts +0 -6
  140. package/lib/hooks/useCheckInOutOperations.js +14 -34
  141. package/lib/hooks/useDcmtOperations.d.ts +3 -12
  142. package/lib/hooks/useDcmtOperations.js +130 -160
  143. package/lib/hooks/useDocumentOperations.js +12 -23
  144. package/lib/hooks/useInputDialog.d.ts +1 -2
  145. package/lib/hooks/useMultiMasterDetailDcmts.d.ts +0 -15
  146. package/lib/hooks/useMultiMasterDetailDcmts.js +62 -65
  147. package/lib/hooks/useWorkflowApprove.js +33 -17
  148. package/lib/services/platform_services.d.ts +2 -3
  149. package/package.json +1 -1
@@ -0,0 +1,871 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
+ import styled, { keyframes } from 'styled-components';
4
+ import { HolAskDescriptor, SDK_Globals, TopMediaServer } from '@topconsultnpm/sdk-ts';
5
+ import TMTooltip from '../../base/TMTooltip';
6
+ import { TMColors } from '../../../utils/theme';
7
+ import ToppyImage from '../../../assets/Toppy-help-center.png';
8
+ const generateGUID = () => {
9
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replaceAll(/[xy]/g, (c) => {
10
+ const r = Math.trunc(Math.random() * 16);
11
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
12
+ return v.toString(16);
13
+ });
14
+ };
15
+ const PANEL_DEFAULT_WIDTH = 600;
16
+ const PANEL_MIN_WIDTH = 340;
17
+ const PANEL_MAX_WIDTH = 900;
18
+ const getSafeSession = () => {
19
+ try {
20
+ return SDK_Globals.tmSession;
21
+ }
22
+ catch {
23
+ return undefined;
24
+ }
25
+ };
26
+ let preLoginSession;
27
+ let preLoginSessionUrl;
28
+ const getPreLoginSession = (endpointUrl) => {
29
+ if (!endpointUrl) {
30
+ return undefined;
31
+ }
32
+ if (!preLoginSession || preLoginSessionUrl !== endpointUrl) {
33
+ const tmServer = new TopMediaServer(endpointUrl);
34
+ preLoginSession = tmServer.NewSession();
35
+ preLoginSessionUrl = endpointUrl;
36
+ }
37
+ return preLoginSession;
38
+ };
39
+ const TMHelpOnLineChat = ({ isOpen, showChatButton, isLogged, messages, conversationId, isThinking, endpointUrl, onClose, onToggle, onNewConversation, onAddMessage, onSetThinking, onSetConversationId }) => {
40
+ const [width, setWidth] = useState(PANEL_DEFAULT_WIDTH);
41
+ const showFab = !isLogged && showChatButton && !isOpen;
42
+ return (_jsxs(_Fragment, { children: [isOpen && (_jsxs(_Fragment, { children: [_jsx(Overlay, { onClick: onClose }), _jsx(ChatPanel, { width: width, onWidthChange: setWidth, messages: messages, conversationId: conversationId, isThinking: isThinking, endpointUrl: endpointUrl, onClose: onClose, onNewConversation: onNewConversation, onAddMessage: onAddMessage, onSetThinking: onSetThinking, onSetConversationId: onSetConversationId })] })), showFab && (_jsx(Fab, { onClick: onToggle, title: "Chiedi a Toppy", "aria-label": "Chiedi a Toppy", children: _jsx(FabAvatar, { src: ToppyImage, alt: "Toppy" }) }))] }));
43
+ };
44
+ export default TMHelpOnLineChat;
45
+ //#region Header button
46
+ export const TMToppyHeaderButton = ({ showChatButton, onToggle }) => {
47
+ if (!showChatButton)
48
+ return null;
49
+ return (_jsx(TMTooltip, { content: "Chiedi a Toppy", children: _jsxs(ToppyHeaderButton, { onClick: onToggle, title: "Chiedi a Toppy", children: [_jsx(ToppyHeaderAvatar, { src: ToppyImage, alt: "Toppy" }), _jsx(ToppyHeaderText, { children: "Chiedi a Toppy" })] }) }));
50
+ };
51
+ const ChatPanel = ({ width, onWidthChange, messages, conversationId, isThinking, endpointUrl, onClose, onNewConversation, onAddMessage, onSetThinking, onSetConversationId }) => {
52
+ const [chatInput, setChatInput] = useState('');
53
+ const inputRef = useRef(null);
54
+ const messagesEndRef = useRef(null);
55
+ const onCloseRef = useRef(onClose);
56
+ onCloseRef.current = onClose;
57
+ const userName = getSafeSession()?.SessionDescr?.userName ?? '';
58
+ useEffect(() => {
59
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
60
+ }, [messages, isThinking]);
61
+ useEffect(() => {
62
+ inputRef.current?.focus();
63
+ }, []);
64
+ useEffect(() => {
65
+ const onKeyDown = (e) => {
66
+ if (e.key === 'Escape')
67
+ onCloseRef.current();
68
+ };
69
+ window.addEventListener('keydown', onKeyDown);
70
+ return () => window.removeEventListener('keydown', onKeyDown);
71
+ }, []);
72
+ const onResizeStart = useCallback((e) => {
73
+ e.preventDefault();
74
+ const startX = e.clientX;
75
+ const startWidth = width;
76
+ const onMove = (ev) => {
77
+ const next = startWidth + (startX - ev.clientX);
78
+ const max = Math.min(PANEL_MAX_WIDTH, globalThis.innerWidth - 40);
79
+ onWidthChange(Math.min(Math.max(next, PANEL_MIN_WIDTH), max));
80
+ };
81
+ const onUp = () => {
82
+ document.body.style.userSelect = '';
83
+ document.body.style.cursor = '';
84
+ globalThis.removeEventListener('mousemove', onMove);
85
+ globalThis.removeEventListener('mouseup', onUp);
86
+ };
87
+ document.body.style.userSelect = 'none';
88
+ document.body.style.cursor = 'col-resize';
89
+ globalThis.addEventListener('mousemove', onMove);
90
+ globalThis.addEventListener('mouseup', onUp);
91
+ }, [width, onWidthChange]);
92
+ const askHandler = useCallback(async () => {
93
+ const question = chatInput.trim();
94
+ if (question.length < 2 || isThinking) {
95
+ return;
96
+ }
97
+ onAddMessage({ id: generateGUID(), text: question, isUser: true });
98
+ setChatInput('');
99
+ onSetThinking(true);
100
+ let convId = conversationId;
101
+ if (!convId) {
102
+ convId = generateGUID();
103
+ onSetConversationId(convId);
104
+ }
105
+ try {
106
+ const globalSession = getSafeSession();
107
+ const preSession = globalSession ? undefined : getPreLoginSession(endpointUrl);
108
+ const session = globalSession ?? preSession;
109
+ let answer = '';
110
+ if (session) {
111
+ const hol_d = new HolAskDescriptor();
112
+ hol_d.question = question;
113
+ hol_d.conversationId = convId;
114
+ const raw = await session.HelpOnLineAskAsync(hol_d);
115
+ const parsed = typeof raw === 'string' ? safeJsonParse(raw) : raw;
116
+ if (parsed && typeof parsed === 'object') {
117
+ answer = parsed.answer ?? '';
118
+ }
119
+ else if (typeof raw === 'string') {
120
+ answer = raw;
121
+ }
122
+ }
123
+ else {
124
+ console.error('[HOL] NO SESSION -> nothing called. endpointUrl =', endpointUrl);
125
+ }
126
+ onAddMessage({ id: generateGUID(), text: answer || 'Nessuna risposta ricevuta.', isUser: false });
127
+ }
128
+ catch (error) {
129
+ onAddMessage({ id: generateGUID(), text: 'Si è verificato un errore, riprova più tardi.', isUser: false });
130
+ console.error('[HOL]', error);
131
+ }
132
+ finally {
133
+ onSetThinking(false);
134
+ }
135
+ }, [chatInput, conversationId, isThinking, endpointUrl, onAddMessage, onSetThinking, onSetConversationId]);
136
+ return (_jsxs(PanelContainer, { $width: width, children: [_jsx(ResizeHandle, { onMouseDown: onResizeStart }), _jsxs(ChatHeader, { children: [_jsxs(HeaderLeft, { children: [_jsx(ToppyAvatar, { src: ToppyImage, alt: "Toppy" }), _jsxs(HeaderText, { children: [_jsxs(HeaderGreeting, { children: ["Ciao ", userName] }), _jsx(HeaderQuestion, { children: "Come posso aiutarti?" })] })] }), _jsxs(HeaderRight, { children: [_jsx(TMTooltip, { content: "Nuova conversazione", children: _jsx(HeaderButton, { onClick: onNewConversation, disabled: !conversationId && messages.length === 0, title: "Nuova conversazione", children: _jsx(IconNewChat, { color: "white", fontSize: 18 }) }) }), _jsx(TMTooltip, { content: "Chiudi", children: _jsx(HeaderButton, { onClick: onClose, title: "Chiudi", children: _jsx(IconClose, { fontSize: 18 }) }) })] })] }), _jsxs(ChatBody, { children: [messages.length === 0 ? (_jsxs(WelcomeMessage, { children: [_jsx(WelcomeIcon, { children: "\uD83D\uDCA1" }), _jsxs(WelcomeText, { children: ["Ciao! Sono ", _jsx("strong", { children: "Toppy" }), ", il tuo assistente.", _jsx("br", {}), "Fai una domanda e ti aiuter\u00F2 a trovare la risposta."] })] })) : (messages.map(msg => (_jsxs(MessageBubble, { $isUser: msg.isUser, children: [!msg.isUser && _jsx(BotAvatar, { src: ToppyImage, alt: "Toppy" }), _jsx(MessageContent, { $isUser: msg.isUser, children: msg.isUser ? msg.text : _jsx(MarkdownContent, { children: renderMarkdown(msg.text) }) })] }, msg.id)))), isThinking && (_jsxs(MessageBubble, { $isUser: false, children: [_jsx(BotAvatar, { src: ToppyImage, alt: "Toppy" }), _jsx(ThinkingStatus, {}, messages.length)] })), _jsx("div", { ref: messagesEndRef })] }), _jsx(ChatFooter, { children: _jsx(TMChatInput, { inputRef: inputRef, value: chatInput, onChange: setChatInput, onSend: askHandler, placeholder: "Scrivi la tua domanda...", tooltip: "Invia", disabled: isThinking }) })] }));
137
+ };
138
+ const TMChatInput = ({ value, onChange, onSend, placeholder = 'Scrivi la tua domanda...', disabled = false, inputRef, tooltip = 'Ricerca AI' }) => {
139
+ const isDisabled = disabled || value.length < 2;
140
+ return (_jsxs(ChatInputWrapper, { children: [_jsx(ChatInputTextarea, { ref: inputRef, placeholder: placeholder, value: value, onChange: (e) => onChange(e.target.value) }), _jsx(TMTooltip, { content: tooltip, children: _jsx(InlineSendButton, { $disabled: isDisabled, disabled: isDisabled, onClick: onSend, title: tooltip, children: _jsx(IconSendFilled, { fontSize: 20 }) }) })] }));
141
+ };
142
+ //#endregion
143
+ //#region Thinking status (phrases + elapsed seconds)
144
+ const THINKING_PHRASES = [
145
+ { at: 0, text: 'Ci sto pensando' },
146
+ { at: 3, text: 'Sto cercando tra le informazioni' },
147
+ { at: 7, text: 'Sto leggendo i contenuti più pertinenti' },
148
+ { at: 12, text: 'Sto collegando i punti' },
149
+ { at: 18, text: 'Sto verificando i dettagli' },
150
+ { at: 25, text: 'Sto mettendo in ordine la risposta' },
151
+ { at: 33, text: 'Ci siamo quasi' },
152
+ { at: 42, text: 'Ancora un momento, sto rifinendo' },
153
+ { at: 55, text: 'Domanda impegnativa, ci vuole un attimo in più' },
154
+ { at: 70, text: 'Ultimo sforzo, sto completando la risposta' },
155
+ ];
156
+ const COUNTER_VISIBLE_FROM = 2;
157
+ const getPhraseIndex = (seconds) => {
158
+ let index = 0;
159
+ for (let i = 1; i < THINKING_PHRASES.length; i++) {
160
+ if (seconds < THINKING_PHRASES[i].at)
161
+ break;
162
+ index = i;
163
+ }
164
+ return index;
165
+ };
166
+ const formatElapsed = (seconds) => seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, '0')}`;
167
+ const ThinkingStatus = () => {
168
+ const [seconds, setSeconds] = useState(0);
169
+ useEffect(() => {
170
+ const startedAt = performance.now();
171
+ const intervalId = globalThis.setInterval(() => {
172
+ setSeconds(Math.floor((performance.now() - startedAt) / 1000));
173
+ }, 1000);
174
+ return () => globalThis.clearInterval(intervalId);
175
+ }, []);
176
+ const phraseIndex = getPhraseIndex(seconds);
177
+ return (_jsx(ThinkingIndicator, { children: _jsxs(ThinkingCaption, { children: [_jsxs(ThinkingPhrase, { role: "status", "aria-live": "polite", children: [THINKING_PHRASES[phraseIndex].text, _jsxs(ThinkingDots, { "aria-hidden": "true", children: [_jsx(ThinkingDot, { $delay: 0, children: "." }), _jsx(ThinkingDot, { $delay: 0.2, children: "." }), _jsx(ThinkingDot, { $delay: 0.4, children: "." })] })] }, phraseIndex), _jsx(ThinkingCounter, { $visible: seconds >= COUNTER_VISIBLE_FROM, "aria-hidden": "true", children: formatElapsed(seconds) })] }) }));
178
+ };
179
+ //#endregion
180
+ //#region Markdown rendering (no third-party library)
181
+ const safeJsonParse = (value) => {
182
+ try {
183
+ return JSON.parse(value);
184
+ }
185
+ catch {
186
+ return value;
187
+ }
188
+ };
189
+ const renderInline = (text, keyPrefix) => {
190
+ const nodes = [];
191
+ const pattern = /(\*\*([^*]+)\*\*)|(~~([^~]+)~~)|(`([^`]+)`)|(\[([^\]]+)\]\(([^)]+)\))|(\*([^*\n]+)\*)|(_([^_\n]+)_)/g;
192
+ let lastIndex = 0;
193
+ let match;
194
+ let i = 0;
195
+ while ((match = pattern.exec(text)) !== null) {
196
+ if (match.index > lastIndex)
197
+ nodes.push(text.slice(lastIndex, match.index));
198
+ const key = `${keyPrefix}-${i}`;
199
+ if (match[2] !== undefined) {
200
+ nodes.push(_jsx("strong", { children: renderInline(match[2], key) }, key));
201
+ }
202
+ else if (match[4] !== undefined) {
203
+ nodes.push(_jsx("s", { children: renderInline(match[4], key) }, key));
204
+ }
205
+ else if (match[6] !== undefined) {
206
+ nodes.push(_jsx(MdCode, { children: match[6] }, key));
207
+ }
208
+ else if (match[8] !== undefined) {
209
+ nodes.push(_jsx("a", { href: match[9], target: "_blank", rel: "noopener noreferrer", children: match[8] }, key));
210
+ }
211
+ else if (match[11] !== undefined) {
212
+ nodes.push(_jsx("em", { children: match[11] }, key));
213
+ }
214
+ else if (match[13] !== undefined) {
215
+ nodes.push(_jsx("em", { children: match[13] }, key));
216
+ }
217
+ lastIndex = pattern.lastIndex;
218
+ i++;
219
+ }
220
+ if (lastIndex < text.length)
221
+ nodes.push(text.slice(lastIndex));
222
+ return nodes;
223
+ };
224
+ const splitTableCells = (row) => {
225
+ let s = row.trim();
226
+ if (s.startsWith('|'))
227
+ s = s.slice(1);
228
+ if (s.endsWith('|') && !s.endsWith('\\|'))
229
+ s = s.slice(0, -1);
230
+ const cells = [];
231
+ let cur = '';
232
+ for (let i = 0; i < s.length; i++) {
233
+ if (s[i] === '\\' && s[i + 1] === '|') {
234
+ cur += '|';
235
+ i++;
236
+ continue;
237
+ }
238
+ if (s[i] === '|') {
239
+ cells.push(cur.trim());
240
+ cur = '';
241
+ continue;
242
+ }
243
+ cur += s[i];
244
+ }
245
+ cells.push(cur.trim());
246
+ return cells;
247
+ };
248
+ const isTableSeparator = (line) => {
249
+ if (!line?.includes('-'))
250
+ return false;
251
+ const cells = splitTableCells(line);
252
+ return cells.length > 0 && cells.every(c => /^:?-+:?$/.test(c));
253
+ };
254
+ const getCellAlign = (sep) => {
255
+ const left = sep.startsWith(':');
256
+ const right = sep.endsWith(':');
257
+ if (left && right)
258
+ return 'center';
259
+ if (right)
260
+ return 'right';
261
+ if (left)
262
+ return 'left';
263
+ return undefined;
264
+ };
265
+ //#endregion
266
+ const startsBlock = (lines, idx) => {
267
+ const line = lines[idx];
268
+ if (line.trim() === '')
269
+ return true;
270
+ if (line.trim().startsWith('```'))
271
+ return true;
272
+ if (/^#{1,6}\s+/.test(line))
273
+ return true;
274
+ if (/^\s*[-*+]\s+/.test(line))
275
+ return true;
276
+ if (/^\s*\d+\.\s+/.test(line))
277
+ return true;
278
+ if (/^\s*>\s?/.test(line))
279
+ return true;
280
+ if (/^\s*(-{3,}|\*{3,}|_{3,})\s*$/.test(line))
281
+ return true;
282
+ if (line.includes('|') && isTableSeparator(lines[idx + 1]))
283
+ return true;
284
+ return false;
285
+ };
286
+ const renderMarkdown = (raw) => {
287
+ const lines = raw.replace(/\r\n/g, '\n').split('\n');
288
+ const blocks = [];
289
+ let i = 0;
290
+ let key = 0;
291
+ while (i < lines.length) {
292
+ const line = lines[i];
293
+ if (line.trim().startsWith('```')) {
294
+ const codeLines = [];
295
+ i++;
296
+ while (i < lines.length && !lines[i].trim().startsWith('```')) {
297
+ codeLines.push(lines[i]);
298
+ i++;
299
+ }
300
+ i++;
301
+ blocks.push(_jsx(MdPre, { children: codeLines.join('\n') }, `b-${key++}`));
302
+ continue;
303
+ }
304
+ if (line.includes('|') && isTableSeparator(lines[i + 1])) {
305
+ const headers = splitTableCells(line);
306
+ const aligns = splitTableCells(lines[i + 1]).map(getCellAlign);
307
+ i += 2;
308
+ const rows = [];
309
+ while (i < lines.length && lines[i].trim() !== '' && lines[i].includes('|')) {
310
+ rows.push(splitTableCells(lines[i]));
311
+ i++;
312
+ }
313
+ const tKey = key++;
314
+ blocks.push(_jsx(MdTableWrapper, { children: _jsxs(MdTable, { children: [_jsx("thead", { children: _jsx("tr", { children: headers.map((cell, ci) => (_jsx(MdTh, { $align: aligns[ci], children: renderInline(cell, `th-${tKey}-${ci}`) }, `th-${tKey}-${ci}`))) }) }), _jsx("tbody", { children: rows.map((row, ri) => (_jsx("tr", { children: headers.map((_, ci) => (_jsx(MdTd, { $align: aligns[ci], children: renderInline(row[ci] ?? '', `td-${tKey}-${ri}-${ci}`) }, `td-${tKey}-${ri}-${ci}`))) }, `tr-${tKey}-${ri}`))) })] }) }, `b-${tKey}`));
315
+ continue;
316
+ }
317
+ if (/^\s*(-{3,}|\*{3,}|_{3,})\s*$/.test(line)) {
318
+ blocks.push(_jsx(MdHr, {}, `b-${key++}`));
319
+ i++;
320
+ continue;
321
+ }
322
+ if (/^\s*>\s?/.test(line)) {
323
+ const quoteLines = [];
324
+ while (i < lines.length && /^\s*>\s?/.test(lines[i])) {
325
+ quoteLines.push(lines[i].replace(/^\s*>\s?/, ''));
326
+ i++;
327
+ }
328
+ blocks.push(_jsx(MdBlockquote, { children: renderMarkdown(quoteLines.join('\n')) }, `b-${key++}`));
329
+ continue;
330
+ }
331
+ const heading = line.match(/^(#{1,6})\s+(.*)$/);
332
+ if (heading) {
333
+ const level = heading[1].length;
334
+ blocks.push(_jsx(MdHeading, { $level: level, children: renderInline(heading[2], `h-${key}`) }, `b-${key}`));
335
+ key++;
336
+ i++;
337
+ continue;
338
+ }
339
+ if (/^\s*[-*+]\s+/.test(line)) {
340
+ const items = [];
341
+ while (i < lines.length && /^\s*[-*+]\s+/.test(lines[i])) {
342
+ const content = lines[i].replace(/^\s*[-*+]\s+/, '');
343
+ items.push(_jsx("li", { children: renderInline(content, `li-${key}-${items.length}`) }, `li-${key}-${items.length}`));
344
+ i++;
345
+ }
346
+ blocks.push(_jsx(MdUl, { children: items }, `b-${key++}`));
347
+ continue;
348
+ }
349
+ if (/^\s*\d+\.\s+/.test(line)) {
350
+ const items = [];
351
+ while (i < lines.length && /^\s*\d+\.\s+/.test(lines[i])) {
352
+ const content = lines[i].replace(/^\s*\d+\.\s+/, '');
353
+ items.push(_jsx("li", { children: renderInline(content, `oi-${key}-${items.length}`) }, `oi-${key}-${items.length}`));
354
+ i++;
355
+ }
356
+ blocks.push(_jsx(MdOl, { children: items }, `b-${key++}`));
357
+ continue;
358
+ }
359
+ if (line.trim() === '') {
360
+ i++;
361
+ continue;
362
+ }
363
+ const paraLines = [];
364
+ while (i < lines.length && !startsBlock(lines, i)) {
365
+ paraLines.push(lines[i]);
366
+ i++;
367
+ }
368
+ const paraNodes = paraLines.flatMap((pl, idx) => {
369
+ const inline = renderInline(pl, `p-${key}-${idx}`);
370
+ return idx === 0 ? inline : [_jsx("br", {}, `br-${key}-${idx}`), ...inline];
371
+ });
372
+ blocks.push(_jsx(MdParagraph, { children: paraNodes }, `b-${key++}`));
373
+ }
374
+ return blocks;
375
+ };
376
+ //#endregion
377
+ //#region Icons
378
+ function IconClose(props) {
379
+ return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", width: "1em", height: "1em", fill: "currentColor", ...props, children: _jsx("path", { d: "M18.3 5.71 12 12.01l-6.3-6.3-1.41 1.41 6.3 6.3-6.3 6.3 1.41 1.41 6.3-6.3 6.3 6.3 1.41-1.41-6.3-6.3 6.3-6.3z" }) }));
380
+ }
381
+ function IconSendFilled(props) {
382
+ return (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", width: "1em", height: "1em", ...props, children: [_jsx("defs", { children: _jsx("path", { id: "lsiconSendFilled0", d: "M12.97 2.67a.5.5 0 0 0-.64-.64l-11 4a.5.5 0 0 0-.016.934l4.433 1.773l2.9-3.09l.707.707l-2.98 3.176l1.662 4.156a.5.5 0 0 0 .934-.015z" }) }), _jsxs("g", { fill: "currentColor", fillRule: "evenodd", clipRule: "evenodd", children: [_jsx("use", { href: "#lsiconSendFilled0" }), _jsx("use", { href: "#lsiconSendFilled0" })] })] }));
383
+ }
384
+ function IconNewChat(props) {
385
+ return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", width: "1em", height: "1em", fill: props.color || "currentColor", ...props, children: _jsx("path", { d: "M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z" }) }));
386
+ }
387
+ //#endregion
388
+ //#region Styled Components
389
+ const fadeIn = keyframes `
390
+ from { opacity: 0; }
391
+ to { opacity: 1; }
392
+ `;
393
+ const slideInRight = keyframes `
394
+ from { transform: translateX(100%); }
395
+ to { transform: translateX(0); }
396
+ `;
397
+ const phraseIn = keyframes `
398
+ from { opacity: 0; transform: translateY(4px); }
399
+ to { opacity: 1; transform: translateY(0); }
400
+ `;
401
+ const dotCycle = keyframes `
402
+ 0% { opacity: 0; }
403
+ 25%, 75% { opacity: 1; }
404
+ 100% { opacity: 0; }
405
+ `;
406
+ const ToppyHeaderButton = styled.button `
407
+ display: flex;
408
+ align-items: center;
409
+ gap: 8px;
410
+ height: 36px;
411
+ padding: 0 14px 0 5px;
412
+ border-radius: 18px;
413
+ border: 1px solid ${TMColors.primary}35;
414
+ background: ${TMColors.primary}0d;
415
+ color: ${TMColors.primary};
416
+ font-size: 13px;
417
+ font-weight: 600;
418
+ white-space: nowrap;
419
+ cursor: pointer;
420
+ flex-shrink: 0;
421
+ transition: all 0.2s ease;
422
+
423
+ &:hover {
424
+ background: ${TMColors.primary}1a;
425
+ border-color: ${TMColors.primary}66;
426
+ transform: translateY(-1px);
427
+ box-shadow: 0 4px 12px ${TMColors.primary}25;
428
+ }
429
+ &:active { transform: translateY(0); }
430
+ `;
431
+ const ToppyHeaderAvatar = styled.img `
432
+ width: 28px;
433
+ height: 28px;
434
+ border-radius: 50%;
435
+ object-fit: contain;
436
+ pointer-events: none;
437
+ `;
438
+ const ToppyHeaderText = styled.span `
439
+ line-height: 1;
440
+ `;
441
+ const Fab = styled.button `
442
+ position: fixed;
443
+ bottom: 24px;
444
+ right: 24px;
445
+ width: 60px;
446
+ height: 60px;
447
+ border-radius: 50%;
448
+ border: none;
449
+ padding: 0;
450
+ cursor: pointer;
451
+ display: flex;
452
+ align-items: center;
453
+ justify-content: center;
454
+ background: white;
455
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
456
+ z-index: 100000;
457
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
458
+ animation: ${fadeIn} 0.3s ease-out;
459
+
460
+ &:hover { transform: scale(1.08); box-shadow: 0 8px 26px rgba(0, 0, 0, 0.3); }
461
+ &:active { transform: scale(0.96); }
462
+ `;
463
+ const FabAvatar = styled.img `
464
+ width: 52px;
465
+ height: 52px;
466
+ border-radius: 50%;
467
+ object-fit: contain;
468
+ pointer-events: none;
469
+ `;
470
+ const Overlay = styled.div `
471
+ position: fixed;
472
+ inset: 0;
473
+ background: rgba(0, 0, 0, 0.45);
474
+ z-index: 100000;
475
+ animation: ${fadeIn} 0.2s ease-out;
476
+ `;
477
+ const PanelContainer = styled.div `
478
+ position: fixed;
479
+ top: 0;
480
+ right: 0;
481
+ height: 100vh;
482
+ width: ${props => props.$width}px;
483
+ max-width: 100vw;
484
+ background: white;
485
+ display: flex;
486
+ flex-direction: column;
487
+ box-shadow: -8px 0 32px rgba(0, 0, 0, 0.28);
488
+ z-index: 100001;
489
+ animation: ${slideInRight} 0.25s ease-out;
490
+ `;
491
+ const ResizeHandle = styled.div `
492
+ position: absolute;
493
+ left: 0;
494
+ top: 0;
495
+ bottom: 0;
496
+ width: 6px;
497
+ cursor: col-resize;
498
+ background: transparent;
499
+ z-index: 2;
500
+ transition: background 0.2s ease;
501
+
502
+ &:hover { background: ${TMColors.primary}55; }
503
+ `;
504
+ const ChatHeader = styled.div `
505
+ display: flex;
506
+ align-items: center;
507
+ justify-content: space-between;
508
+ padding: 16px 20px;
509
+ background: linear-gradient(135deg, ${TMColors.primary} 0%, ${TMColors.primary}dd 100%);
510
+ color: white;
511
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
512
+ `;
513
+ const HeaderLeft = styled.div `
514
+ display: flex;
515
+ align-items: center;
516
+ gap: 12px;
517
+ flex: 1;
518
+ min-width: 0;
519
+ `;
520
+ const HeaderRight = styled.div `
521
+ display: flex;
522
+ align-items: center;
523
+ gap: 8px;
524
+ margin-left: 12px;
525
+ flex-shrink: 0;
526
+ `;
527
+ const ToppyAvatar = styled.img `
528
+ width: 44px;
529
+ height: 44px;
530
+ border-radius: 50%;
531
+ background: white;
532
+ padding: 4px;
533
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
534
+ flex-shrink: 0;
535
+ object-fit: contain;
536
+ `;
537
+ const HeaderText = styled.div `
538
+ display: flex;
539
+ flex-direction: column;
540
+ gap: 2px;
541
+ min-width: 0;
542
+ `;
543
+ const HeaderGreeting = styled.h2 `
544
+ margin: 0;
545
+ font-size: 17px;
546
+ font-weight: 600;
547
+ color: white;
548
+ white-space: nowrap;
549
+ overflow: hidden;
550
+ text-overflow: ellipsis;
551
+ `;
552
+ const HeaderQuestion = styled.p `
553
+ margin: 0;
554
+ font-size: 13px;
555
+ color: rgba(255, 255, 255, 0.9);
556
+ `;
557
+ const HeaderButton = styled.button `
558
+ width: 32px;
559
+ height: 32px;
560
+ border-radius: 50%;
561
+ background: rgba(255, 255, 255, 0.2);
562
+ border: 1px solid rgba(255, 255, 255, 0.3);
563
+ color: white;
564
+ cursor: pointer;
565
+ display: flex;
566
+ align-items: center;
567
+ justify-content: center;
568
+ flex-shrink: 0;
569
+ transition: all 0.2s ease;
570
+
571
+ &:hover:not(:disabled) { background: rgba(255, 255, 255, 0.3); transform: scale(1.05); }
572
+ &:active:not(:disabled) { transform: scale(0.95); }
573
+ &:disabled { opacity: 0.45; cursor: default; }
574
+ `;
575
+ const ChatBody = styled.div `
576
+ flex: 1;
577
+ padding: 20px;
578
+ overflow-y: auto;
579
+ background: linear-gradient(to bottom, #f8f9fa 0%, #ffffff 100%);
580
+ `;
581
+ const WelcomeMessage = styled.div `
582
+ display: flex;
583
+ gap: 16px;
584
+ padding: 18px;
585
+ background: white;
586
+ border-radius: 12px;
587
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
588
+ border-left: 4px solid ${TMColors.primary};
589
+ animation: ${fadeIn} 0.5s ease-out;
590
+ `;
591
+ const WelcomeIcon = styled.div `
592
+ font-size: 30px;
593
+ flex-shrink: 0;
594
+ `;
595
+ const WelcomeText = styled.p `
596
+ margin: 0;
597
+ font-size: 14px;
598
+ line-height: 1.6;
599
+ color: #242424;
600
+
601
+ strong { color: ${TMColors.primary}; font-weight: 600; }
602
+ `;
603
+ const MessageBubble = styled.div `
604
+ display: flex;
605
+ gap: 10px;
606
+ margin-bottom: 16px;
607
+ align-items: flex-start;
608
+ justify-content: ${props => (props.$isUser ? 'flex-end' : 'flex-start')};
609
+ animation: ${fadeIn} 0.3s ease-out;
610
+ `;
611
+ const BotAvatar = styled.img `
612
+ width: 32px;
613
+ height: 32px;
614
+ border-radius: 50%;
615
+ background: white;
616
+ padding: 2px;
617
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
618
+ flex-shrink: 0;
619
+ object-fit: contain;
620
+ `;
621
+ const MessageContent = styled.div `
622
+ max-width: 80%;
623
+ padding: 10px 14px;
624
+ border-radius: 12px;
625
+ font-size: 14px;
626
+ line-height: 1.5;
627
+ background: ${props => (props.$isUser ? TMColors.primary : '#f0f2f5')};
628
+ color: ${props => (props.$isUser ? 'white' : '#242424')};
629
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
630
+ word-wrap: break-word;
631
+ overflow-wrap: anywhere;
632
+ white-space: ${props => (props.$isUser ? 'pre-wrap' : 'normal')};
633
+ border-bottom-${props => (props.$isUser ? 'right' : 'left')}-radius: 4px;
634
+ `;
635
+ const MarkdownContent = styled.div `
636
+ & > *:first-child { margin-top: 0; }
637
+ & > *:last-child { margin-bottom: 0; }
638
+
639
+ a { color: ${TMColors.primary}; text-decoration: underline; }
640
+ strong { font-weight: 600; }
641
+ `;
642
+ const MdParagraph = styled.p `
643
+ margin: 0 0 8px 0;
644
+ line-height: 1.55;
645
+ `;
646
+ const MdHeading = styled.div `
647
+ margin: 12px 0 6px 0;
648
+ font-weight: 700;
649
+ line-height: 1.3;
650
+ font-size: ${props => `${Math.max(14, 20 - (props.$level - 1) * 2)}px`};
651
+ `;
652
+ const MdUl = styled.ul `
653
+ margin: 4px 0 8px 0;
654
+ padding-left: 20px;
655
+ li { margin: 2px 0; line-height: 1.5; }
656
+ `;
657
+ const MdOl = styled.ol `
658
+ margin: 4px 0 8px 0;
659
+ padding-left: 22px;
660
+ li { margin: 2px 0; line-height: 1.5; }
661
+ `;
662
+ const MdCode = styled.code `
663
+ background: rgba(0, 0, 0, 0.06);
664
+ padding: 1px 5px;
665
+ border-radius: 4px;
666
+ font-family: 'Consolas', 'Monaco', monospace;
667
+ font-size: 0.9em;
668
+ `;
669
+ const MdPre = styled.pre `
670
+ background: rgba(0, 0, 0, 0.06);
671
+ padding: 10px 12px;
672
+ border-radius: 8px;
673
+ overflow-x: auto;
674
+ margin: 8px 0;
675
+ font-family: 'Consolas', 'Monaco', monospace;
676
+ font-size: 0.88em;
677
+ white-space: pre;
678
+ `;
679
+ const MdTableWrapper = styled.div `
680
+ margin: 8px 0;
681
+ max-width: 100%;
682
+ overflow-x: auto;
683
+ border: 1px solid #dcdfe3;
684
+ border-radius: 8px;
685
+
686
+ &::-webkit-scrollbar { height: 6px; }
687
+ &::-webkit-scrollbar-track { background: transparent; }
688
+ &::-webkit-scrollbar-thumb { background: #ccc; border-radius: 3px; }
689
+ &::-webkit-scrollbar-thumb:hover { background: #999; }
690
+ `;
691
+ const MdTable = styled.table `
692
+ border-collapse: collapse;
693
+ width: 100%;
694
+ font-size: 0.92em;
695
+ background: white;
696
+ `;
697
+ const MdTh = styled.th `
698
+ padding: 7px 10px;
699
+ background: ${TMColors.primary}14;
700
+ color: #242424;
701
+ font-weight: 600;
702
+ text-align: ${props => props.$align ?? 'left'};
703
+ border-bottom: 1px solid #dcdfe3;
704
+ white-space: nowrap;
705
+
706
+ &:not(:last-child) { border-right: 1px solid #e6e8eb; }
707
+ `;
708
+ const MdTd = styled.td `
709
+ padding: 7px 10px;
710
+ text-align: ${props => props.$align ?? 'left'};
711
+ vertical-align: top;
712
+ border-top: 1px solid #eceef0;
713
+
714
+ &:not(:last-child) { border-right: 1px solid #eceef0; }
715
+
716
+ tr:nth-child(even) & { background: #fafbfc; }
717
+ `;
718
+ const MdBlockquote = styled.blockquote `
719
+ margin: 8px 0;
720
+ padding: 6px 12px;
721
+ border-left: 3px solid ${TMColors.primary}66;
722
+ background: ${TMColors.primary}0a;
723
+ border-radius: 0 6px 6px 0;
724
+ color: #444;
725
+
726
+ & > *:first-child { margin-top: 0; }
727
+ & > *:last-child { margin-bottom: 0; }
728
+ `;
729
+ const MdHr = styled.hr `
730
+ border: none;
731
+ border-top: 1px solid #dcdfe3;
732
+ margin: 12px 0;
733
+ `;
734
+ const ThinkingIndicator = styled.div `
735
+ display: flex;
736
+ align-items: center;
737
+ gap: 10px;
738
+ max-width: 80%;
739
+ padding: 7px 12px;
740
+ background: #f0f2f5;
741
+ border-radius: 12px;
742
+ border-bottom-left-radius: 4px;
743
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
744
+ `;
745
+ const ThinkingCaption = styled.div `
746
+ display: flex;
747
+ flex-direction: column;
748
+ align-items: flex-start;
749
+ gap: 0;
750
+ min-width: 0;
751
+ `;
752
+ const ThinkingPhrase = styled.span `
753
+ display: inline-flex;
754
+ align-items: center;
755
+ min-width: 0;
756
+ font-size: 12px;
757
+ font-style: italic;
758
+ line-height: 1.15;
759
+ color: #6b7280;
760
+ animation: ${phraseIn} 0.25s ease-out;
761
+ `;
762
+ const ThinkingDots = styled.span `
763
+ display: inline-flex;
764
+ width: 12px;
765
+ flex-shrink: 0;
766
+ `;
767
+ const ThinkingDot = styled.span `
768
+ animation: ${dotCycle} 1.2s linear infinite;
769
+ animation-delay: ${props => props.$delay}s;
770
+ will-change: opacity;
771
+
772
+ @media (prefers-reduced-motion: reduce) {
773
+ animation: none;
774
+ opacity: 1;
775
+ }
776
+ `;
777
+ const ThinkingCounter = styled.span `
778
+ margin-top: 2px;
779
+ font-size: 10px;
780
+ font-weight: 600;
781
+ line-height: 1.1;
782
+ letter-spacing: 0.02em;
783
+ color: ${TMColors.primary}b3;
784
+ font-variant-numeric: tabular-nums;
785
+ font-feature-settings: 'tnum';
786
+ opacity: ${props => (props.$visible ? 1 : 0)};
787
+ transition: opacity 0.3s ease;
788
+ `;
789
+ const ChatFooter = styled.div `
790
+ padding: 16px 20px;
791
+ background: white;
792
+ border-top: 1px solid #e1e5e9;
793
+ box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
794
+ `;
795
+ const ChatInputWrapper = styled.div `
796
+ position: relative;
797
+ flex: 1;
798
+ display: flex;
799
+ `;
800
+ const ChatInputTextarea = styled.textarea `
801
+ width: 100%;
802
+ padding: 14px 60px 14px 16px;
803
+ border: 2px solid #e1e5e9;
804
+ border-radius: 12px;
805
+ font-size: 15px;
806
+ font-family: inherit;
807
+ line-height: 1.5;
808
+ transition: all 0.2s ease;
809
+ background: #f8f9fa;
810
+ resize: none;
811
+ min-height: 100px;
812
+ max-height: 120px;
813
+ overflow-y: hidden;
814
+
815
+ &:focus {
816
+ outline: none;
817
+ border-color: ${TMColors.primary};
818
+ background: white;
819
+ box-shadow: 0 0 0 4px ${TMColors.primary}15;
820
+ }
821
+
822
+ &::placeholder {
823
+ color: #999;
824
+ }
825
+
826
+ &::-webkit-scrollbar {
827
+ width: 6px;
828
+ }
829
+
830
+ &::-webkit-scrollbar-track {
831
+ background: transparent;
832
+ }
833
+
834
+ &::-webkit-scrollbar-thumb {
835
+ background: #ccc;
836
+ border-radius: 3px;
837
+ }
838
+
839
+ &::-webkit-scrollbar-thumb:hover {
840
+ background: #999;
841
+ }
842
+ `;
843
+ const InlineSendButton = styled.button `
844
+ position: absolute;
845
+ bottom: 8px;
846
+ right: 8px;
847
+ width: 40px;
848
+ height: 40px;
849
+ border-radius: 10px;
850
+ background: ${props => props.$disabled ? '#ccc' : '#4caf50'};
851
+ border: none;
852
+ color: white;
853
+ cursor: ${props => props.$disabled ? 'default' : 'pointer'};
854
+ display: flex;
855
+ align-items: center;
856
+ justify-content: center;
857
+ transition: all 0.2s ease;
858
+
859
+ &:hover:not(:disabled) {
860
+ background: #45a049;
861
+ transform: scale(1.05);
862
+ }
863
+
864
+ &:active:not(:disabled) {
865
+ transform: scale(0.95);
866
+ }
867
+
868
+ &:disabled {
869
+ opacity: 0.6;
870
+ }
871
+ `;