@molecule/app-ide-react 1.0.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.
Files changed (250) hide show
  1. package/LICENSE +115 -0
  2. package/dist/chatTimelineOrdering.d.ts +50 -0
  3. package/dist/chatTimelineOrdering.d.ts.map +1 -0
  4. package/dist/chatTimelineOrdering.js +46 -0
  5. package/dist/chatTimelineOrdering.js.map +1 -0
  6. package/dist/command-metadata.d.ts +78 -0
  7. package/dist/command-metadata.d.ts.map +1 -0
  8. package/dist/command-metadata.js +214 -0
  9. package/dist/command-metadata.js.map +1 -0
  10. package/dist/components/ActivityCard.d.ts +28 -0
  11. package/dist/components/ActivityCard.d.ts.map +1 -0
  12. package/dist/components/ActivityCard.js +62 -0
  13. package/dist/components/ActivityCard.js.map +1 -0
  14. package/dist/components/AutoCommitBadge.d.ts +58 -0
  15. package/dist/components/AutoCommitBadge.d.ts.map +1 -0
  16. package/dist/components/AutoCommitBadge.js +78 -0
  17. package/dist/components/AutoCommitBadge.js.map +1 -0
  18. package/dist/components/ChatPanel.d.ts +184 -0
  19. package/dist/components/ChatPanel.d.ts.map +1 -0
  20. package/dist/components/ChatPanel.js +6691 -0
  21. package/dist/components/ChatPanel.js.map +1 -0
  22. package/dist/components/CommandPalette.d.ts +18 -0
  23. package/dist/components/CommandPalette.d.ts.map +1 -0
  24. package/dist/components/CommandPalette.js +33 -0
  25. package/dist/components/CommandPalette.js.map +1 -0
  26. package/dist/components/DeviceFrameSelector.d.ts +60 -0
  27. package/dist/components/DeviceFrameSelector.d.ts.map +1 -0
  28. package/dist/components/DeviceFrameSelector.js +230 -0
  29. package/dist/components/DeviceFrameSelector.js.map +1 -0
  30. package/dist/components/EditorPanel.d.ts +17 -0
  31. package/dist/components/EditorPanel.d.ts.map +1 -0
  32. package/dist/components/EditorPanel.js +241 -0
  33. package/dist/components/EditorPanel.js.map +1 -0
  34. package/dist/components/FileExplorer.d.ts +64 -0
  35. package/dist/components/FileExplorer.d.ts.map +1 -0
  36. package/dist/components/FileExplorer.js +1111 -0
  37. package/dist/components/FileExplorer.js.map +1 -0
  38. package/dist/components/FileExplorerContextMenu.d.ts +37 -0
  39. package/dist/components/FileExplorerContextMenu.d.ts.map +1 -0
  40. package/dist/components/FileExplorerContextMenu.js +241 -0
  41. package/dist/components/FileExplorerContextMenu.js.map +1 -0
  42. package/dist/components/HelpCard.d.ts +34 -0
  43. package/dist/components/HelpCard.d.ts.map +1 -0
  44. package/dist/components/HelpCard.js +28 -0
  45. package/dist/components/HelpCard.js.map +1 -0
  46. package/dist/components/Icon.d.ts +50 -0
  47. package/dist/components/Icon.d.ts.map +1 -0
  48. package/dist/components/Icon.js +25 -0
  49. package/dist/components/Icon.js.map +1 -0
  50. package/dist/components/KeyboardShortcutsPanel.d.ts +19 -0
  51. package/dist/components/KeyboardShortcutsPanel.d.ts.map +1 -0
  52. package/dist/components/KeyboardShortcutsPanel.js +187 -0
  53. package/dist/components/KeyboardShortcutsPanel.js.map +1 -0
  54. package/dist/components/MarkdownContent.d.ts +52 -0
  55. package/dist/components/MarkdownContent.d.ts.map +1 -0
  56. package/dist/components/MarkdownContent.js +303 -0
  57. package/dist/components/MarkdownContent.js.map +1 -0
  58. package/dist/components/ModelsTable.d.ts +38 -0
  59. package/dist/components/ModelsTable.d.ts.map +1 -0
  60. package/dist/components/ModelsTable.js +96 -0
  61. package/dist/components/ModelsTable.js.map +1 -0
  62. package/dist/components/PreviewPanel.d.ts +66 -0
  63. package/dist/components/PreviewPanel.d.ts.map +1 -0
  64. package/dist/components/PreviewPanel.js +1752 -0
  65. package/dist/components/PreviewPanel.js.map +1 -0
  66. package/dist/components/QuickOpen.d.ts +20 -0
  67. package/dist/components/QuickOpen.d.ts.map +1 -0
  68. package/dist/components/QuickOpen.js +72 -0
  69. package/dist/components/QuickOpen.js.map +1 -0
  70. package/dist/components/QuickPicker.d.ts +21 -0
  71. package/dist/components/QuickPicker.d.ts.map +1 -0
  72. package/dist/components/QuickPicker.js +174 -0
  73. package/dist/components/QuickPicker.js.map +1 -0
  74. package/dist/components/RelevantSkillSuggestion.d.ts +41 -0
  75. package/dist/components/RelevantSkillSuggestion.d.ts.map +1 -0
  76. package/dist/components/RelevantSkillSuggestion.js +85 -0
  77. package/dist/components/RelevantSkillSuggestion.js.map +1 -0
  78. package/dist/components/ReportModal.d.ts +35 -0
  79. package/dist/components/ReportModal.d.ts.map +1 -0
  80. package/dist/components/ReportModal.js +112 -0
  81. package/dist/components/ReportModal.js.map +1 -0
  82. package/dist/components/ResizeHandle.d.ts +21 -0
  83. package/dist/components/ResizeHandle.d.ts.map +1 -0
  84. package/dist/components/ResizeHandle.js +109 -0
  85. package/dist/components/ResizeHandle.js.map +1 -0
  86. package/dist/components/ScriptsCard.d.ts +37 -0
  87. package/dist/components/ScriptsCard.d.ts.map +1 -0
  88. package/dist/components/ScriptsCard.js +177 -0
  89. package/dist/components/ScriptsCard.js.map +1 -0
  90. package/dist/components/SearchPanel.d.ts +18 -0
  91. package/dist/components/SearchPanel.d.ts.map +1 -0
  92. package/dist/components/SearchPanel.js +449 -0
  93. package/dist/components/SearchPanel.js.map +1 -0
  94. package/dist/components/SettingsCard.d.ts +49 -0
  95. package/dist/components/SettingsCard.d.ts.map +1 -0
  96. package/dist/components/SettingsCard.js +59 -0
  97. package/dist/components/SettingsCard.js.map +1 -0
  98. package/dist/components/ShareModal.d.ts +32 -0
  99. package/dist/components/ShareModal.d.ts.map +1 -0
  100. package/dist/components/ShareModal.js +126 -0
  101. package/dist/components/ShareModal.js.map +1 -0
  102. package/dist/components/SidebarTabs.d.ts +18 -0
  103. package/dist/components/SidebarTabs.d.ts.map +1 -0
  104. package/dist/components/SidebarTabs.js +20 -0
  105. package/dist/components/SidebarTabs.js.map +1 -0
  106. package/dist/components/SkillsCard.d.ts +86 -0
  107. package/dist/components/SkillsCard.d.ts.map +1 -0
  108. package/dist/components/SkillsCard.js +227 -0
  109. package/dist/components/SkillsCard.js.map +1 -0
  110. package/dist/components/StreamingIndicator.d.ts +50 -0
  111. package/dist/components/StreamingIndicator.d.ts.map +1 -0
  112. package/dist/components/StreamingIndicator.js +142 -0
  113. package/dist/components/StreamingIndicator.js.map +1 -0
  114. package/dist/components/TabBar.d.ts +37 -0
  115. package/dist/components/TabBar.d.ts.map +1 -0
  116. package/dist/components/TabBar.js +187 -0
  117. package/dist/components/TabBar.js.map +1 -0
  118. package/dist/components/TipCard.d.ts +22 -0
  119. package/dist/components/TipCard.d.ts.map +1 -0
  120. package/dist/components/TipCard.js +73 -0
  121. package/dist/components/TipCard.js.map +1 -0
  122. package/dist/components/ToolCallCard.d.ts +18 -0
  123. package/dist/components/ToolCallCard.d.ts.map +1 -0
  124. package/dist/components/ToolCallCard.js +716 -0
  125. package/dist/components/ToolCallCard.js.map +1 -0
  126. package/dist/components/UserAvatar.d.ts +45 -0
  127. package/dist/components/UserAvatar.d.ts.map +1 -0
  128. package/dist/components/UserAvatar.js +80 -0
  129. package/dist/components/UserAvatar.js.map +1 -0
  130. package/dist/components/WorkspaceLayout.d.ts +19 -0
  131. package/dist/components/WorkspaceLayout.d.ts.map +1 -0
  132. package/dist/components/WorkspaceLayout.js +56 -0
  133. package/dist/components/WorkspaceLayout.js.map +1 -0
  134. package/dist/components/activity-utilities.d.ts +107 -0
  135. package/dist/components/activity-utilities.d.ts.map +1 -0
  136. package/dist/components/activity-utilities.js +154 -0
  137. package/dist/components/activity-utilities.js.map +1 -0
  138. package/dist/components/chat-autocommit-utilities.d.ts +172 -0
  139. package/dist/components/chat-autocommit-utilities.d.ts.map +1 -0
  140. package/dist/components/chat-autocommit-utilities.js +185 -0
  141. package/dist/components/chat-autocommit-utilities.js.map +1 -0
  142. package/dist/components/chat-card-style.d.ts +64 -0
  143. package/dist/components/chat-card-style.d.ts.map +1 -0
  144. package/dist/components/chat-card-style.js +74 -0
  145. package/dist/components/chat-card-style.js.map +1 -0
  146. package/dist/components/chat-commands.d.ts +17 -0
  147. package/dist/components/chat-commands.d.ts.map +1 -0
  148. package/dist/components/chat-commands.js +17 -0
  149. package/dist/components/chat-commands.js.map +1 -0
  150. package/dist/components/chat-commit-utilities.d.ts +29 -0
  151. package/dist/components/chat-commit-utilities.d.ts.map +1 -0
  152. package/dist/components/chat-commit-utilities.js +31 -0
  153. package/dist/components/chat-commit-utilities.js.map +1 -0
  154. package/dist/components/chat-cost-utilities.d.ts +62 -0
  155. package/dist/components/chat-cost-utilities.d.ts.map +1 -0
  156. package/dist/components/chat-cost-utilities.js +66 -0
  157. package/dist/components/chat-cost-utilities.js.map +1 -0
  158. package/dist/components/chat-effort-utilities.d.ts +96 -0
  159. package/dist/components/chat-effort-utilities.d.ts.map +1 -0
  160. package/dist/components/chat-effort-utilities.js +97 -0
  161. package/dist/components/chat-effort-utilities.js.map +1 -0
  162. package/dist/components/chat-help-utilities.d.ts +89 -0
  163. package/dist/components/chat-help-utilities.d.ts.map +1 -0
  164. package/dist/components/chat-help-utilities.js +131 -0
  165. package/dist/components/chat-help-utilities.js.map +1 -0
  166. package/dist/components/chat-model-mode-utilities.d.ts +143 -0
  167. package/dist/components/chat-model-mode-utilities.d.ts.map +1 -0
  168. package/dist/components/chat-model-mode-utilities.js +191 -0
  169. package/dist/components/chat-model-mode-utilities.js.map +1 -0
  170. package/dist/components/chat-models-utilities.d.ts +98 -0
  171. package/dist/components/chat-models-utilities.d.ts.map +1 -0
  172. package/dist/components/chat-models-utilities.js +136 -0
  173. package/dist/components/chat-models-utilities.js.map +1 -0
  174. package/dist/components/chat-report-utilities.d.ts +149 -0
  175. package/dist/components/chat-report-utilities.d.ts.map +1 -0
  176. package/dist/components/chat-report-utilities.js +151 -0
  177. package/dist/components/chat-report-utilities.js.map +1 -0
  178. package/dist/components/chat-scripts-utilities.d.ts +140 -0
  179. package/dist/components/chat-scripts-utilities.d.ts.map +1 -0
  180. package/dist/components/chat-scripts-utilities.js +158 -0
  181. package/dist/components/chat-scripts-utilities.js.map +1 -0
  182. package/dist/components/chat-settings-utilities.d.ts +72 -0
  183. package/dist/components/chat-settings-utilities.d.ts.map +1 -0
  184. package/dist/components/chat-settings-utilities.js +56 -0
  185. package/dist/components/chat-settings-utilities.js.map +1 -0
  186. package/dist/components/chat-share-utilities.d.ts +104 -0
  187. package/dist/components/chat-share-utilities.d.ts.map +1 -0
  188. package/dist/components/chat-share-utilities.js +90 -0
  189. package/dist/components/chat-share-utilities.js.map +1 -0
  190. package/dist/components/chat-skills-utilities.d.ts +242 -0
  191. package/dist/components/chat-skills-utilities.d.ts.map +1 -0
  192. package/dist/components/chat-skills-utilities.js +433 -0
  193. package/dist/components/chat-skills-utilities.js.map +1 -0
  194. package/dist/components/chat-stream-utilities.d.ts +74 -0
  195. package/dist/components/chat-stream-utilities.d.ts.map +1 -0
  196. package/dist/components/chat-stream-utilities.js +103 -0
  197. package/dist/components/chat-stream-utilities.js.map +1 -0
  198. package/dist/components/chat-tips-utilities.d.ts +108 -0
  199. package/dist/components/chat-tips-utilities.d.ts.map +1 -0
  200. package/dist/components/chat-tips-utilities.js +137 -0
  201. package/dist/components/chat-tips-utilities.js.map +1 -0
  202. package/dist/components/device-cycle.d.ts +84 -0
  203. package/dist/components/device-cycle.d.ts.map +1 -0
  204. package/dist/components/device-cycle.js +77 -0
  205. package/dist/components/device-cycle.js.map +1 -0
  206. package/dist/components/index.d.ts +36 -0
  207. package/dist/components/index.d.ts.map +1 -0
  208. package/dist/components/index.js +33 -0
  209. package/dist/components/index.js.map +1 -0
  210. package/dist/components/tool-call-utilities.d.ts +69 -0
  211. package/dist/components/tool-call-utilities.d.ts.map +1 -0
  212. package/dist/components/tool-call-utilities.js +368 -0
  213. package/dist/components/tool-call-utilities.js.map +1 -0
  214. package/dist/components/user-avatar-utilities.d.ts +41 -0
  215. package/dist/components/user-avatar-utilities.d.ts.map +1 -0
  216. package/dist/components/user-avatar-utilities.js +56 -0
  217. package/dist/components/user-avatar-utilities.js.map +1 -0
  218. package/dist/components/workspace-layout-utilities.d.ts +37 -0
  219. package/dist/components/workspace-layout-utilities.d.ts.map +1 -0
  220. package/dist/components/workspace-layout-utilities.js +58 -0
  221. package/dist/components/workspace-layout-utilities.js.map +1 -0
  222. package/dist/customEventCards.d.ts +117 -0
  223. package/dist/customEventCards.d.ts.map +1 -0
  224. package/dist/customEventCards.js +36 -0
  225. package/dist/customEventCards.js.map +1 -0
  226. package/dist/hooks/index.d.ts +8 -0
  227. package/dist/hooks/index.d.ts.map +1 -0
  228. package/dist/hooks/index.js +8 -0
  229. package/dist/hooks/index.js.map +1 -0
  230. package/dist/hooks/useKeyboardShortcuts.d.ts +48 -0
  231. package/dist/hooks/useKeyboardShortcuts.d.ts.map +1 -0
  232. package/dist/hooks/useKeyboardShortcuts.js +112 -0
  233. package/dist/hooks/useKeyboardShortcuts.js.map +1 -0
  234. package/dist/hooks/useViewport.d.ts +42 -0
  235. package/dist/hooks/useViewport.d.ts.map +1 -0
  236. package/dist/hooks/useViewport.js +61 -0
  237. package/dist/hooks/useViewport.js.map +1 -0
  238. package/dist/index.d.ts +75 -0
  239. package/dist/index.d.ts.map +1 -0
  240. package/dist/index.js +74 -0
  241. package/dist/index.js.map +1 -0
  242. package/dist/settings-metadata.d.ts +78 -0
  243. package/dist/settings-metadata.d.ts.map +1 -0
  244. package/dist/settings-metadata.js +131 -0
  245. package/dist/settings-metadata.js.map +1 -0
  246. package/dist/types.d.ts +751 -0
  247. package/dist/types.d.ts.map +1 -0
  248. package/dist/types.js +25 -0
  249. package/dist/types.js.map +1 -0
  250. package/package.json +90 -0
@@ -0,0 +1,716 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { memo, useCallback, useMemo, useState } from 'react';
3
+ import { t } from '@molecule/app-i18n';
4
+ import { useThemeMode } from '@molecule/app-react';
5
+ import { getClassMap } from '@molecule/app-ui';
6
+ import { useCoarsePointer, useNarrowViewport } from '../hooks/useViewport.js';
7
+ import { MarkdownContent } from './MarkdownContent.js';
8
+ import { basename, extractFilePath, fileDiffStats, moleculeDocPath, toolLabel, toolSummary, } from './tool-call-utilities.js';
9
+ /**
10
+ * Normalize a model-authored string for display: models sometimes double-escape
11
+ * tool arguments, so a literal "\n"/"\t"/"\r" (backslash + letter) ends up in
12
+ * the text instead of a real control character. Convert those back so markdown
13
+ * line breaks render correctly.
14
+ *
15
+ * @param s - The raw string.
16
+ * @returns The string with literal escape sequences turned into real ones.
17
+ */
18
+ function unescapeLiterals(s) {
19
+ return s.replace(/\\r\\n|\\n|\\r/g, '\n').replace(/\\t/g, '\t');
20
+ }
21
+ // ---------------------------------------------------------------------------
22
+ // Label renderer — turns `backtick` segments into inline <code> spans
23
+ // ---------------------------------------------------------------------------
24
+ const CODE_STYLE = {
25
+ fontFamily: '"SF Mono", Menlo, Consolas, "Courier New", monospace',
26
+ fontSize: 'inherit',
27
+ };
28
+ /**
29
+ * Clickable filename code — single click opens preview, double click pins tab.
30
+ * @param props - Component props.
31
+ * @returns The rendered clickable code element.
32
+ */
33
+ function FileCodeLink({ filePath, onFileOpen, onFileDoubleClick, children, }) {
34
+ return (_jsx("code", { style: { ...CODE_STYLE, cursor: 'pointer', opacity: 0.75, transition: 'opacity 100ms' }, onClick: (e) => {
35
+ e.stopPropagation();
36
+ onFileOpen(filePath);
37
+ }, onDoubleClick: (e) => {
38
+ e.stopPropagation();
39
+ onFileDoubleClick?.(filePath);
40
+ }, onMouseEnter: (e) => {
41
+ ;
42
+ e.currentTarget.style.opacity = '1';
43
+ }, onMouseLeave: (e) => {
44
+ ;
45
+ e.currentTarget.style.opacity = '0.75';
46
+ }, children: children }));
47
+ }
48
+ /**
49
+ * Renders a tool label with backtick segments converted to clickable code spans.
50
+ * @param name - The tool name.
51
+ * @param input - The raw tool input payload.
52
+ * @param filePath - The primary file path for clickable code links, or null.
53
+ * @param onFileOpen - Callback to preview a file on single click.
54
+ * @param onFileDoubleClick - Callback to pin a file tab on double click.
55
+ * @returns A ReactNode with formatted label text and clickable file references.
56
+ */
57
+ function renderLabel(name, input, filePath, onFileOpen, onFileDoubleClick) {
58
+ const text = toolLabel(name, input);
59
+ const parts = text.split(new RegExp('`([^`]+)`'));
60
+ if (parts.length === 1)
61
+ return text;
62
+ return (_jsx(_Fragment, { children: parts.map((part, i) => i % 2 === 0 ? (part) : filePath && onFileOpen ? (_jsx(FileCodeLink, { filePath: filePath, onFileOpen: onFileOpen, onFileDoubleClick: onFileDoubleClick, children: part }, i)) : (_jsx("code", { style: { ...CODE_STYLE, opacity: 0.75 }, children: part }, i))) }));
63
+ }
64
+ /**
65
+ * Extract the package matches from a find_package output. Prefers the
66
+ * structured `results` array; falls back to parsing package names out of the
67
+ * model-facing `packages` markdown (conversations recorded before `results`
68
+ * existed).
69
+ * @param output - The raw find_package output payload.
70
+ * @returns The package results, possibly empty.
71
+ */
72
+ function packageResults(output) {
73
+ const out = (output ?? {});
74
+ if (Array.isArray(out.results)) {
75
+ return out.results.filter((r) => typeof r?.name === 'string');
76
+ }
77
+ if (typeof out.packages === 'string') {
78
+ return [...out.packages.matchAll(/^### `([^`]+)`(?: — (.+))?$/gm)].map((m) => ({
79
+ name: m[1],
80
+ label: m[2],
81
+ }));
82
+ }
83
+ return [];
84
+ }
85
+ /**
86
+ * Clickable rows for find_package matches — each opens the package's
87
+ * MOLECULE.md in the editor.
88
+ * @param props - Component props.
89
+ * @returns The rendered result rows.
90
+ */
91
+ function PackageResultRows({ results, onFileOpen, }) {
92
+ const isCoarse = useCoarsePointer();
93
+ if (results.length === 0) {
94
+ return (_jsx("span", { style: { opacity: 0.5 }, children: t('ide.toolCall.noMatches', undefined, { defaultValue: 'No matches' }) }));
95
+ }
96
+ return (_jsx("div", { style: { fontFamily: PRE.fontFamily, fontSize: '11px', lineHeight: 1.7 }, children: results.map((r, i) => {
97
+ const docPath = moleculeDocPath(r.name);
98
+ const clickable = docPath != null && onFileOpen != null;
99
+ return (_jsxs("div", { role: clickable ? 'button' : undefined, tabIndex: clickable ? 0 : undefined, "data-mol-id": `find-package-result-${i}`, title: clickable
100
+ ? t('ide.toolCall.openPackageDoc', undefined, {
101
+ defaultValue: 'Open package docs',
102
+ })
103
+ : undefined, onClick: clickable
104
+ ? (e) => {
105
+ e.stopPropagation();
106
+ onFileOpen(docPath);
107
+ }
108
+ : undefined, onKeyDown: clickable
109
+ ? (e) => {
110
+ if (e.key === 'Enter' || e.key === ' ') {
111
+ e.preventDefault();
112
+ e.stopPropagation();
113
+ onFileOpen(docPath);
114
+ }
115
+ }
116
+ : undefined, onMouseEnter: (e) => {
117
+ if (clickable)
118
+ e.currentTarget.style.opacity = '1';
119
+ }, onMouseLeave: (e) => {
120
+ if (clickable)
121
+ e.currentTarget.style.opacity = '0.8';
122
+ }, style: {
123
+ display: 'flex',
124
+ gap: '8px',
125
+ alignItems: 'baseline',
126
+ // Touch: ~19px result rows are untappable — 32px dense-row floor.
127
+ ...(isCoarse && clickable ? { minHeight: 32, alignItems: 'center' } : {}),
128
+ cursor: clickable ? 'pointer' : 'default',
129
+ opacity: clickable ? 0.8 : 1,
130
+ transition: 'opacity 100ms',
131
+ }, children: [_jsx("span", { style: { whiteSpace: 'nowrap' }, children: r.name }), r.label && (_jsx("span", { style: {
132
+ opacity: 0.55,
133
+ overflow: 'hidden',
134
+ textOverflow: 'ellipsis',
135
+ whiteSpace: 'nowrap',
136
+ }, children: r.label }))] }, i));
137
+ }) }));
138
+ }
139
+ // ---------------------------------------------------------------------------
140
+ // Detail renderers — IN and OUT panes
141
+ // ---------------------------------------------------------------------------
142
+ const PRE = {
143
+ margin: 0,
144
+ whiteSpace: 'pre',
145
+ overflowX: 'auto',
146
+ fontFamily: '"SF Mono", Menlo, Consolas, "Courier New", monospace',
147
+ fontSize: '11px',
148
+ lineHeight: 1.5,
149
+ };
150
+ /**
151
+ * Render the input section for a tool call.
152
+ * @param name - The tool name.
153
+ * @param input - The raw tool input payload.
154
+ * @returns A ReactNode showing the formatted input content.
155
+ */
156
+ function renderIn(name, input) {
157
+ const inp = (input ?? {});
158
+ switch (name) {
159
+ case 'exec_command':
160
+ return (_jsxs("pre", { style: PRE, children: [_jsx("span", { style: { opacity: 0.5 }, children: "$ " }), inp.command ?? ''] }));
161
+ case 'write_file':
162
+ // Full file content is too noisy — show only the path
163
+ return _jsx("pre", { style: PRE, children: inp.path ?? '' });
164
+ case 'edit_file': {
165
+ const replacements = Array.isArray(inp.replacements)
166
+ ? inp.replacements
167
+ : [];
168
+ const nodes = [];
169
+ replacements.forEach((r, i) => {
170
+ if (i > 0)
171
+ nodes.push('\n\n');
172
+ r.old_string.split('\n').forEach((line, li) => {
173
+ if (li > 0)
174
+ nodes.push('\n');
175
+ nodes.push(_jsxs("span", { style: { color: '#f47067', fontFamily: 'inherit' }, children: ['- ', line] }, `r${i}-${li}`));
176
+ });
177
+ nodes.push('\n');
178
+ r.new_string.split('\n').forEach((line, li) => {
179
+ if (li > 0)
180
+ nodes.push('\n');
181
+ nodes.push(_jsxs("span", { style: { color: '#57ab5a', fontFamily: 'inherit' }, children: ['+ ', line] }, `a${i}-${li}`));
182
+ });
183
+ });
184
+ return _jsx("pre", { style: PRE, children: nodes });
185
+ }
186
+ case 'search_files':
187
+ return (_jsxs("pre", { style: PRE, children: [`/${inp.pattern ?? ''}/`, inp.path ? ` in ${inp.path}` : '', inp.include ? ` (${inp.include})` : ''] }));
188
+ case 'find_files':
189
+ return (_jsxs("pre", { style: PRE, children: [inp.pattern ?? '', inp.path ? ` in ${inp.path}` : ''] }));
190
+ case 'read_file':
191
+ case 'delete_file':
192
+ case 'list_files':
193
+ case 'create_directory':
194
+ return _jsx("pre", { style: PRE, children: inp.path ?? '' });
195
+ case 'rename_file':
196
+ return (_jsxs("pre", { style: PRE, children: [inp.old_path ?? '', " \u2192 ", inp.new_path ?? ''] }));
197
+ case 'web_fetch':
198
+ return (_jsxs("pre", { style: PRE, children: [inp.method ?? 'GET', " ", inp.url ?? ''] }));
199
+ default:
200
+ return _jsx("pre", { style: PRE, children: JSON.stringify(input, null, 2) });
201
+ }
202
+ }
203
+ /**
204
+ * Render the output section for a tool call.
205
+ * @param name - The tool name.
206
+ * @param output - The raw tool output payload.
207
+ * @returns A ReactNode showing the formatted output content.
208
+ */
209
+ function renderOut(name, output) {
210
+ const out = (output ?? {});
211
+ if (typeof out === 'object' && out !== null && 'error' in out) {
212
+ return _jsx("pre", { style: { ...PRE, color: '#f47067' }, children: out.error });
213
+ }
214
+ switch (name) {
215
+ case 'exec_command': {
216
+ const stdout = (out.stdout ?? '').trimEnd();
217
+ const stderr = (out.stderr ?? '').trimEnd();
218
+ const exitCode = out.exitCode;
219
+ return (_jsxs("div", { children: [stdout && _jsx("pre", { style: PRE, children: stdout }), stderr && (_jsx("pre", { style: { ...PRE, color: '#f47067', marginTop: stdout ? '4px' : 0 }, children: stderr })), exitCode != null && exitCode !== 0 && (_jsxs("div", { style: { marginTop: '4px', color: '#f47067', fontSize: '10px' }, children: ["exit code ", exitCode] })), !stdout && !stderr && (_jsx("span", { style: { opacity: 0.5 }, children: t('ide.toolCall.noOutput', undefined, { defaultValue: '(no output)' }) }))] }));
220
+ }
221
+ case 'write_file': {
222
+ const diff = out.diff;
223
+ if (!diff)
224
+ return (_jsx("span", { style: { opacity: 0.6 }, children: t('ide.toolCall.written', undefined, { defaultValue: 'Written' }) }));
225
+ if (diff.type === 'unchanged')
226
+ return (_jsx("span", { style: { opacity: 0.6 }, children: t('ide.toolCall.statusUnchanged', undefined, { defaultValue: 'Unchanged' }) }));
227
+ return (_jsxs("div", { style: { fontFamily: PRE.fontFamily, fontSize: '11px' }, children: [diff.type === 'new' && (_jsx("div", { style: { color: '#57ab5a' }, children: t('ide.toolCall.newFileLines', { count: diff.linesAdded }, { defaultValue: 'new file, {{count}} lines' }) })), diff.type === 'modified' && (_jsxs(_Fragment, { children: [diff.linesAdded > 0 && (_jsx("div", { style: { color: '#57ab5a' }, children: t('ide.toolCall.linesAdded', { count: diff.linesAdded }, { defaultValue: '+{{count}} lines' }) })), diff.linesRemoved > 0 && (_jsx("div", { style: { color: '#f47067' }, children: t('ide.toolCall.linesRemoved', { count: diff.linesRemoved }, { defaultValue: '−{{count}} lines' }) }))] }))] }));
228
+ }
229
+ case 'edit_file': {
230
+ const n = out.replacementsApplied;
231
+ return (_jsx("span", { style: { opacity: 0.6, fontSize: '11px' }, children: n != null
232
+ ? t('ide.toolCall.changesApplied', { count: n }, { defaultValue: '{{count}} changes applied' })
233
+ : t('ide.toolCall.applied', undefined, { defaultValue: 'Applied' }) }));
234
+ }
235
+ case 'read_file': {
236
+ const content = out.content;
237
+ if (!content)
238
+ return (_jsx("span", { style: { opacity: 0.5 }, children: t('ide.toolCall.empty', undefined, { defaultValue: '(empty)' }) }));
239
+ const truncated = content.length > 3000;
240
+ return (_jsx("pre", { style: PRE, children: truncated
241
+ ? content.slice(0, 3000) +
242
+ '\n' +
243
+ t('ide.toolCall.truncated', undefined, { defaultValue: '… (truncated)' })
244
+ : content }));
245
+ }
246
+ case 'list_files': {
247
+ const entries = out.entries;
248
+ if (!entries?.length)
249
+ return (_jsx("span", { style: { opacity: 0.5 }, children: t('ide.toolCall.empty', undefined, { defaultValue: '(empty)' }) }));
250
+ return (_jsx("div", { style: { fontFamily: PRE.fontFamily, fontSize: '11px', lineHeight: 1.6 }, children: entries.map((e, i) => (_jsxs("div", { style: { display: 'flex', gap: '6px' }, children: [_jsx("span", { style: { opacity: 0.4, width: '10px', flexShrink: 0 }, children: e.type === 'directory' ? '▶' : '' }), _jsxs("span", { children: [e.name, e.type === 'directory' ? '/' : ''] })] }, i))) }));
251
+ }
252
+ case 'find_files': {
253
+ const files = out.files;
254
+ if (!files?.length)
255
+ return (_jsx("span", { style: { opacity: 0.5 }, children: t('ide.toolCall.noFilesFound', undefined, { defaultValue: 'No files found' }) }));
256
+ return (_jsx("div", { style: { fontFamily: PRE.fontFamily, fontSize: '11px', lineHeight: 1.6 }, children: files.map((f, i) => (_jsx("div", { children: f }, i))) }));
257
+ }
258
+ case 'search_files': {
259
+ const matches = out.matches;
260
+ if (!matches?.length)
261
+ return (_jsx("span", { style: { opacity: 0.5 }, children: t('ide.toolCall.noMatches', undefined, { defaultValue: 'No matches' }) }));
262
+ return (_jsxs("div", { style: { fontFamily: PRE.fontFamily, fontSize: '11px', lineHeight: 1.6 }, children: [matches.slice(0, 30).map((m, i) => (_jsxs("div", { style: { display: 'flex', gap: '8px', marginBottom: '2px' }, children: [_jsxs("span", { style: { opacity: 0.5, flexShrink: 0, whiteSpace: 'nowrap' }, children: [basename(m.file), ":", m.line] }), _jsx("span", { style: { whiteSpace: 'pre' }, children: m.content.trim() })] }, i))), matches.length > 30 && (_jsx("div", { style: { opacity: 0.5, marginTop: '4px' }, children: t('ide.toolCall.andMore', { count: matches.length - 30 }, { defaultValue: '… and {{count}} more' }) }))] }));
263
+ }
264
+ case 'web_fetch': {
265
+ const status = out.status;
266
+ const body = out.body;
267
+ const ok = status != null && status >= 200 && status < 300;
268
+ return (_jsxs("div", { children: [status != null && (_jsxs("div", { style: {
269
+ color: ok ? '#57ab5a' : '#f47067',
270
+ marginBottom: body ? '4px' : 0,
271
+ fontSize: '11px',
272
+ }, children: ["HTTP ", status] })), body && (_jsx("pre", { style: PRE, children: body.length > 2000
273
+ ? body.slice(0, 2000) +
274
+ '\n' +
275
+ t('ide.toolCall.truncated', undefined, { defaultValue: '… (truncated)' })
276
+ : body }))] }));
277
+ }
278
+ default:
279
+ return _jsx("pre", { style: PRE, children: JSON.stringify(output, null, 2) });
280
+ }
281
+ }
282
+ // ---------------------------------------------------------------------------
283
+ // Component
284
+ // ---------------------------------------------------------------------------
285
+ /**
286
+ * Compact tool-call row with status dot, label, summary, and expandable detail pane.
287
+ * @param props - Component props.
288
+ * @returns The rendered tool call card element.
289
+ */
290
+ export const ToolCallCard = memo(function ToolCallCard({ id, name, input, output, status, fileDiff, isUndone: isUndoneProp, onUndoToggle, onFileOpen, onFileDoubleClick, onFileDiff, onFileRevert, onAskUserResponse, className, }) {
291
+ const cm = getClassMap();
292
+ const isLight = useThemeMode() === 'light';
293
+ // Touch/phone branches (called unconditionally, before the per-tool early
294
+ // returns, so the hook order stays stable): hover-revealed controls become
295
+ // visible-by-default and compact hit areas grow; fine-pointer desktop
296
+ // rendering is unchanged.
297
+ const isCoarse = useCoarsePointer();
298
+ const isNarrow = useNarrowViewport();
299
+ const [expanded, setExpanded] = useState(false);
300
+ const [isHovered, setIsHovered] = useState(false);
301
+ const [isUndoneLocal, setIsUndoneLocal] = useState(false);
302
+ const isUndone = isUndoneProp ?? isUndoneLocal;
303
+ const [isReverting, setIsReverting] = useState(false);
304
+ const hasError = (() => {
305
+ if (status === 'error')
306
+ return true;
307
+ if (typeof output !== 'object' || output === null)
308
+ return false;
309
+ const out = output;
310
+ if ('error' in out)
311
+ return true;
312
+ if (name === 'exec_command') {
313
+ const exitCode = out.exitCode;
314
+ return exitCode != null && exitCode !== 0;
315
+ }
316
+ if (name === 'web_fetch') {
317
+ const s = out.status;
318
+ return s != null && s >= 400;
319
+ }
320
+ return false;
321
+ })();
322
+ // gray → orange → green or red
323
+ const dotColor = status === 'pending'
324
+ ? '#888888'
325
+ : status === 'running'
326
+ ? '#e8a000'
327
+ : hasError
328
+ ? '#f04040'
329
+ : '#3fb950';
330
+ const summary = toolSummary(name, output, status);
331
+ // File path for the clickable filename <code> in the label. For load_skill the
332
+ // path isn't in the input — the loaded skill's SKILL.md path comes back in the
333
+ // output ({ name, path, content }) — so clicking the skill name opens its markdown.
334
+ const filePath = name === 'load_skill'
335
+ ? (output?.path ?? null)
336
+ : extractFilePath(name, input);
337
+ // New files open directly (no diff to show); edits/modifications open the diff viewer.
338
+ const isNewFile = name === 'write_file' && output?.diff?.type === 'new';
339
+ const isFileDiff = !isNewFile &&
340
+ (name === 'edit_file' || name === 'write_file') &&
341
+ filePath != null &&
342
+ onFileDiff != null;
343
+ // Memoize the expensive O(n*m) LCS diff computation so it only re-runs when inputs change.
344
+ const diffStats = useMemo(() => fileDiffStats(name, input, output), [name, input, output]);
345
+ // Undo/redo: available for file modifications (not new files) when a snapshot exists.
346
+ const canRevert = !isNewFile &&
347
+ (name === 'edit_file' || name === 'write_file') &&
348
+ filePath != null &&
349
+ fileDiff != null &&
350
+ onFileRevert != null;
351
+ const handleRevert = useCallback(async (e) => {
352
+ e.stopPropagation();
353
+ if (!canRevert || isReverting)
354
+ return;
355
+ const content = isUndone ? fileDiff.modified : fileDiff.original;
356
+ setIsReverting(true);
357
+ try {
358
+ await onFileRevert(filePath, content);
359
+ const newUndone = !isUndone;
360
+ setIsUndoneLocal(newUndone);
361
+ onUndoToggle?.(id, newUndone);
362
+ }
363
+ finally {
364
+ setIsReverting(false);
365
+ }
366
+ }, [canRevert, isReverting, isUndone, fileDiff, filePath, onFileRevert, onUndoToggle, id]);
367
+ // Tools that expand to show details inline.
368
+ const EXPANDABLE = new Set([
369
+ 'exec_command',
370
+ 'web_fetch',
371
+ 'rename_file',
372
+ 'list_files',
373
+ 'find_files',
374
+ 'search_files',
375
+ 'find_package',
376
+ ]);
377
+ const hasDetails = EXPANDABLE.has(name) && (input !== undefined || output !== undefined);
378
+ // read_molecule_doc: clicking the row opens the package's MOLECULE.md in the
379
+ // editor (filePath is derived from the input's package name).
380
+ const isDocOpen = name === 'read_molecule_doc' && filePath != null && onFileOpen != null;
381
+ // Only exec_command and web_fetch get the labeled IN / OUT pane treatment.
382
+ const showInOut = name === 'exec_command' || name === 'web_fetch';
383
+ const inContent = showInOut && input !== undefined ? renderIn(name, input) : null;
384
+ const outContent = showInOut && output !== undefined ? renderOut(name, output) : null;
385
+ const handleClick = (isNewFile || isDocOpen) && filePath && onFileOpen
386
+ ? () => {
387
+ onFileOpen(filePath);
388
+ }
389
+ : isFileDiff
390
+ ? () => {
391
+ onFileDiff(filePath, fileDiff);
392
+ }
393
+ : hasDetails
394
+ ? () => {
395
+ setExpanded((e) => !e);
396
+ }
397
+ : undefined;
398
+ // ── save_plan: clickable row that opens the plan file in the editor ──────────
399
+ if (name === 'save_plan') {
400
+ const planOutput = (output ?? {});
401
+ const planPath = planOutput.path ?? null;
402
+ // Render as a standard tool card row — click opens the plan file
403
+ const planHandleClick = planPath && onFileOpen
404
+ ? () => {
405
+ onFileOpen(planPath);
406
+ }
407
+ : undefined;
408
+ return (_jsx("div", { className: className, style: { marginBottom: '4px' }, children: _jsxs("button", { type: "button", onClick: planHandleClick, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), style: {
409
+ display: 'flex',
410
+ alignItems: 'flex-start',
411
+ gap: '6px',
412
+ background: 'none',
413
+ border: 'none',
414
+ cursor: planHandleClick ? 'pointer' : 'default',
415
+ color: 'inherit',
416
+ textAlign: 'left',
417
+ padding: '2px 0',
418
+ // Touch: ~20px rows are untappable — 32px is the dense-row floor.
419
+ ...(isCoarse ? { minHeight: 32 } : {}),
420
+ width: '100%',
421
+ }, children: [_jsx("span", { style: { flex: 1, minWidth: 0 }, children: _jsxs("span", { style: { fontSize: '13px', display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsxs("svg", { width: "10", height: "10", viewBox: "0 0 10 10", style: { flexShrink: 0 }, children: [_jsx("circle", { cx: "5", cy: "5", r: "3", fill: dotColor, opacity: "0.35" }), _jsx("circle", { cx: "5", cy: "5", r: "3", fill: "none", stroke: dotColor, strokeWidth: "2" })] }), _jsx("span", { style: {
422
+ flex: 1,
423
+ minWidth: 0,
424
+ overflow: 'hidden',
425
+ textOverflow: 'ellipsis',
426
+ whiteSpace: 'nowrap',
427
+ }, children: renderLabel(name, input, planPath, onFileOpen, onFileDoubleClick) })] }) }), planHandleClick && (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", width: "14", height: "14", style: {
428
+ display: 'block',
429
+ flexShrink: 0,
430
+ marginTop: '3px',
431
+ transition: 'opacity 100ms',
432
+ opacity: isHovered ? 0.85 : 0.35,
433
+ }, children: _jsx("polyline", { points: "6,4 10,8 6,12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }))] }) }));
434
+ }
435
+ // ── ask_user: render interactive option list instead of a normal tool card ──
436
+ if (name === 'ask_user') {
437
+ const askInput = (input ?? {});
438
+ const askOutput = output;
439
+ const serverAwaiting = typeof askOutput === 'object' && askOutput?.status === 'awaiting_response';
440
+ const isResponded = typeof askOutput === 'string';
441
+ const [localAnswer, setLocalAnswer] = useState(null);
442
+ const isAwaiting = serverAwaiting && localAnswer === null;
443
+ const selectedAnswer = isResponded ? askOutput : localAnswer;
444
+ const [freeText, setFreeText] = useState('');
445
+ const [hoveredIdx, setHoveredIdx] = useState(null);
446
+ // Don't render until the question has streamed in. `tool_use_start` surfaces
447
+ // the card the instant the call begins — before its input arrives — which
448
+ // would otherwise flash an empty question + option list. Once the full input
449
+ // is parsed (final `tool_use`), the card appears fully formed. (Placed after
450
+ // the hooks above so the hook order stays stable across renders.)
451
+ if (!askInput.question)
452
+ return null;
453
+ const borderClr = isLight ? '#d0d7de' : '#3d444d';
454
+ const labelChar = (i) => String.fromCharCode(65 + i); // A, B, C, …
455
+ return (_jsxs("div", { className: className, style: {
456
+ marginBottom: '8px',
457
+ marginTop: '8px',
458
+ borderRadius: '8px',
459
+ border: `1px solid ${borderClr}`,
460
+ background: isLight ? '#f6f8fa' : 'rgba(255,255,255,0.04)',
461
+ overflow: 'hidden',
462
+ }, children: [_jsx("div", { style: {
463
+ padding: '4px 12px 10px',
464
+ fontSize: '13px',
465
+ borderBottom: `1px solid ${borderClr}`,
466
+ }, children: _jsx(MarkdownContent, { text: unescapeLiterals(askInput.question ?? ''), isStreaming: false }) }), askInput.options?.map((option, i) => {
467
+ const isSelected = selectedAnswer === option;
468
+ const isFaded = !isAwaiting && !isSelected;
469
+ const isHover = isAwaiting && hoveredIdx === i;
470
+ return (_jsxs("button", { type: "button", "data-mol-id": `ask-user-option-${i}`, disabled: !isAwaiting, onClick: () => {
471
+ setLocalAnswer(option);
472
+ onAskUserResponse?.(option);
473
+ }, onMouseEnter: () => {
474
+ if (isAwaiting)
475
+ setHoveredIdx(i);
476
+ }, onMouseLeave: () => setHoveredIdx(null), style: {
477
+ display: 'flex',
478
+ alignItems: 'center',
479
+ gap: '10px',
480
+ width: '100%',
481
+ padding: '8px 12px',
482
+ // Touch: full 44px rows — these are the PRIMARY discovery answers,
483
+ // so they get the standalone-control floor, not the dense-row 32.
484
+ ...(isCoarse ? { minHeight: 44 } : {}),
485
+ border: 'none',
486
+ borderTop: i > 0 ? `1px solid ${borderClr}` : 'none',
487
+ background: isSelected
488
+ ? isLight
489
+ ? '#dbeafe'
490
+ : 'rgba(59,130,246,0.2)'
491
+ : isHover
492
+ ? isLight
493
+ ? '#eaeef2'
494
+ : 'rgba(255,255,255,0.06)'
495
+ : 'transparent',
496
+ color: 'inherit',
497
+ cursor: isAwaiting ? 'pointer' : 'default',
498
+ textAlign: 'left',
499
+ fontSize: '13px',
500
+ opacity: isFaded ? 0.4 : 1,
501
+ transition: 'background 80ms, opacity 80ms',
502
+ }, children: [_jsx("span", { style: {
503
+ display: 'inline-flex',
504
+ alignItems: 'center',
505
+ justifyContent: 'center',
506
+ width: 22,
507
+ height: 22,
508
+ borderRadius: '5px',
509
+ border: `1px solid ${isSelected ? (isLight ? '#93c5fd' : '#3b82f6') : borderClr}`,
510
+ background: isSelected
511
+ ? isLight
512
+ ? '#3b82f6'
513
+ : '#2563eb'
514
+ : isLight
515
+ ? '#fff'
516
+ : 'rgba(255,255,255,0.08)',
517
+ color: isSelected ? '#fff' : isLight ? '#57606a' : '#848d97',
518
+ fontSize: '11px',
519
+ fontWeight: 600,
520
+ flexShrink: 0,
521
+ fontFamily: '"SF Mono", Menlo, Consolas, "Courier New", monospace',
522
+ }, children: labelChar(i) }), _jsx("span", { style: { flex: 1 }, children: option }), isSelected && (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", width: "14", height: "14", fill: isLight ? '#2563eb' : '#60a5fa', children: _jsx("path", { d: "M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z" }) }))] }, i));
523
+ }), isAwaiting && askInput.allowFreeText !== false && (_jsxs("div", { style: {
524
+ display: 'flex',
525
+ gap: '4px',
526
+ padding: '8px 12px',
527
+ borderTop: `1px solid ${borderClr}`,
528
+ }, children: [_jsx("input", { type: "text", value: freeText, onChange: (e) => setFreeText(e.target.value), onKeyDown: (e) => {
529
+ if (e.key === 'Enter' && freeText.trim()) {
530
+ setLocalAnswer(freeText.trim());
531
+ onAskUserResponse?.(freeText.trim());
532
+ setFreeText('');
533
+ }
534
+ }, placeholder: (askInput.options?.length ?? 0) === 0
535
+ ? t('ide.chat.askUserPlaceholderEmpty', undefined, {
536
+ defaultValue: 'Type your answer…',
537
+ })
538
+ : t('ide.chat.askUserPlaceholder', undefined, {
539
+ defaultValue: 'Or something else…',
540
+ }), style: {
541
+ flex: 1,
542
+ minWidth: 0,
543
+ padding: '5px 8px',
544
+ borderRadius: '5px',
545
+ border: `1px solid ${borderClr}`,
546
+ background: 'transparent',
547
+ color: 'inherit',
548
+ // Deliberate iOS-zoom guard: a focused input below 16px makes iOS
549
+ // Safari zoom the whole page on phone-width / touch-first viewports.
550
+ fontSize: isNarrow || isCoarse ? '16px' : '12px',
551
+ outline: 'none',
552
+ } }), _jsx("button", { type: "button", disabled: !freeText.trim(), onClick: () => {
553
+ if (freeText.trim()) {
554
+ setLocalAnswer(freeText.trim());
555
+ onAskUserResponse?.(freeText.trim());
556
+ setFreeText('');
557
+ }
558
+ }, onMouseEnter: (e) => {
559
+ if (freeText.trim()) {
560
+ e.currentTarget.style.background = 'rgba(64,112,224,0.3)';
561
+ e.currentTarget.style.borderColor = 'rgba(64,112,224,0.65)';
562
+ e.currentTarget.style.color = '#6090f0';
563
+ }
564
+ }, onMouseLeave: (e) => {
565
+ e.currentTarget.style.background = freeText.trim()
566
+ ? 'rgba(64,112,224,0.2)'
567
+ : 'transparent';
568
+ e.currentTarget.style.borderColor = freeText.trim()
569
+ ? 'rgba(64,112,224,0.4)'
570
+ : 'transparent';
571
+ e.currentTarget.style.color = freeText.trim() ? '#4070e0' : 'inherit';
572
+ }, style: {
573
+ // Touch floor for the free-text Send action (40px in this dense
574
+ // composer row); fine pointers keep the compact 30px.
575
+ height: isCoarse ? 40 : 30,
576
+ padding: '0 10px',
577
+ borderRadius: 6,
578
+ border: freeText.trim()
579
+ ? '1px solid rgba(64,112,224,0.4)'
580
+ : '1px solid transparent',
581
+ background: freeText.trim() ? 'rgba(64,112,224,0.2)' : 'transparent',
582
+ color: freeText.trim() ? '#4070e0' : 'inherit',
583
+ cursor: freeText.trim() ? 'pointer' : 'default',
584
+ fontSize: '12px',
585
+ fontWeight: 500,
586
+ opacity: freeText.trim() ? 1 : 0.3,
587
+ transition: 'background 100ms, border-color 100ms, color 100ms',
588
+ }, children: t('ide.chat.askUserSubmit', undefined, { defaultValue: 'Send' }) })] })), selectedAnswer && !askInput.options?.includes(selectedAnswer) && (_jsx("div", { style: {
589
+ padding: '8px 12px',
590
+ borderTop: `1px solid ${borderClr}`,
591
+ fontSize: '12px',
592
+ fontStyle: 'italic',
593
+ }, children: selectedAnswer })), askInput.hint && (_jsx("div", { style: {
594
+ padding: '8px 12px',
595
+ borderTop: `1px solid ${borderClr}`,
596
+ fontSize: '11px',
597
+ fontStyle: 'italic',
598
+ opacity: 0.5,
599
+ }, children: askInput.hint }))] }));
600
+ }
601
+ return (_jsxs("div", { className: className, style: { marginBottom: '4px' }, children: [_jsxs("button", { type: "button", onClick: handleClick, onDoubleClick: isNewFile && filePath && onFileDoubleClick
602
+ ? () => {
603
+ onFileDoubleClick(filePath);
604
+ }
605
+ : undefined, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), style: {
606
+ display: 'flex',
607
+ // Coarse: the undo icon's 32px touch box inflates the label row, so
608
+ // top-alignment would leave the diff stats/chevron riding high —
609
+ // center everything instead. Desktop keeps flex-start + px nudges.
610
+ alignItems: isCoarse ? 'center' : 'flex-start',
611
+ gap: '6px',
612
+ background: 'none',
613
+ border: 'none',
614
+ cursor: handleClick ? 'pointer' : 'default',
615
+ color: 'inherit',
616
+ textAlign: 'left',
617
+ padding: '2px 0',
618
+ // Touch: ~20px rows are untappable — 32px is the dense-row floor.
619
+ ...(isCoarse ? { minHeight: 32 } : {}),
620
+ width: '100%',
621
+ }, children: [_jsx("span", { style: { flex: 1, minWidth: 0 }, children: _jsxs("span", { style: { fontSize: '13px', display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsxs("svg", { width: "10", height: "10", viewBox: "0 0 10 10", style: { flexShrink: 0 }, children: [_jsx("circle", { cx: "5", cy: "5", r: "3", fill: dotColor, opacity: "0.35" }), _jsx("circle", { cx: "5", cy: "5", r: "3", fill: "none", stroke: dotColor, strokeWidth: "2" })] }), _jsx("span", { style: {
622
+ flex: 1,
623
+ minWidth: 0,
624
+ overflow: 'hidden',
625
+ textOverflow: 'ellipsis',
626
+ whiteSpace: 'nowrap',
627
+ }, children: renderLabel(name, input, filePath, onFileOpen, onFileDoubleClick) }), summary && (_jsx("span", { className: cm.cn(cm.textMuted, cm.textSize('xs')), style: { flexShrink: 0, marginLeft: '8px', whiteSpace: 'nowrap' }, children: summary })), canRevert && status !== 'running' && (_jsx("span", { role: "button", tabIndex: 0, title: isUndone
628
+ ? t('ide.chat.redoChange', undefined, { defaultValue: 'Re-apply this change' })
629
+ : t('ide.chat.undoChange', undefined, { defaultValue: 'Undo this change' }), onClick: handleRevert, onKeyDown: (e) => {
630
+ if (e.key === 'Enter' || e.key === ' ') {
631
+ e.preventDefault();
632
+ handleRevert(e);
633
+ }
634
+ }, onMouseEnter: (e) => {
635
+ e.currentTarget.style.background = 'rgba(128,128,128,0.2)';
636
+ e.currentTarget.style.opacity = '1';
637
+ }, onMouseLeave: (e) => {
638
+ e.currentTarget.style.background = 'transparent';
639
+ e.currentTarget.style.opacity = '';
640
+ }, style: {
641
+ display: 'inline-flex',
642
+ alignItems: 'center',
643
+ justifyContent: 'center',
644
+ // Touch: hover can't reveal it, so it rests visible at 0.6 and
645
+ // gets a 32px hit box (the floor for these dense inline rows).
646
+ width: isCoarse ? 32 : 20,
647
+ height: isCoarse ? 32 : 20,
648
+ borderRadius: 4,
649
+ flexShrink: 0,
650
+ // Nudge up 1px: the 13px glyph sat slightly below the text's
651
+ // optical center of the compact 20px box (moot at 32px).
652
+ position: 'relative',
653
+ top: isCoarse ? 0 : '-1px',
654
+ cursor: isReverting ? 'wait' : 'pointer',
655
+ opacity: isReverting ? 0.3 : isHovered || isCoarse ? 0.6 : 0,
656
+ transition: 'opacity 100ms, background 100ms',
657
+ }, children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", width: "13", height: "13", fill: "currentColor", children: isUndone ? (_jsx("path", { d: "M14.78 6.28a.749.749 0 0 0 0-1.06l-3.5-3.5a.749.749 0 1 0-1.06 1.06L12.439 5H5.251l-.001.007L5.251 5a.8.8 0 0 0-.171.019A4.501 4.501 0 0 0 5.5 14h1.704a.75.75 0 0 0 0-1.5H5.5a3 3 0 1 1 0-6h6.939L10.22 8.72a.749.749 0 1 0 1.06 1.06l3.5-3.5Z" })) : (_jsx("path", { d: "M1.22 6.28a.749.749 0 0 1 0-1.06l3.5-3.5a.749.749 0 1 1 1.06 1.06L3.561 5h7.188l.001.007L10.749 5c.058 0 .116.007.171.019A4.501 4.501 0 0 1 10.5 14H8.796a.75.75 0 0 1 0-1.5H10.5a3 3 0 1 0 0-6H3.561L5.78 8.72a.749.749 0 1 1-1.06 1.06l-3.5-3.5Z" })) }) }))] }) }), diffStats && (_jsxs("span", { style: {
658
+ display: 'flex',
659
+ gap: '4px',
660
+ flexShrink: 0,
661
+ marginTop: isCoarse ? 0 : '2px',
662
+ fontSize: '11px',
663
+ fontFamily: '"SF Mono", Menlo, Consolas, "Courier New", monospace',
664
+ opacity: isHovered ? 1 : 0.6,
665
+ transition: 'opacity 100ms',
666
+ }, children: [diffStats.added > 0 && (_jsxs("span", { style: {
667
+ color: isUndone
668
+ ? isLight
669
+ ? '#cf222e'
670
+ : '#f47067'
671
+ : isLight
672
+ ? '#1a7f37'
673
+ : '#57ab5a',
674
+ textDecoration: isUndone ? 'line-through' : undefined,
675
+ }, children: ["+", diffStats.added] })), diffStats.removed > 0 && (_jsxs("span", { style: {
676
+ color: isUndone
677
+ ? isLight
678
+ ? '#1a7f37'
679
+ : '#57ab5a'
680
+ : isLight
681
+ ? '#cf222e'
682
+ : '#f47067',
683
+ textDecoration: isUndone ? 'line-through' : undefined,
684
+ }, children: ["-", diffStats.removed] }))] })), (hasDetails || isFileDiff || isDocOpen || (isNewFile && filePath && onFileOpen)) && (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", width: "14", height: "14", style: {
685
+ display: 'block',
686
+ flexShrink: 0,
687
+ marginTop: isCoarse ? 0 : '3px',
688
+ transform: expanded ? 'rotate(90deg)' : 'rotate(0deg)',
689
+ transition: 'transform 150ms, opacity 100ms',
690
+ opacity: isHovered ? 0.85 : 0.35,
691
+ }, children: _jsx("polyline", { points: "6,4 10,8 6,12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }))] }), expanded && hasDetails && (_jsx("div", { className: cm.surfaceSecondary, style: {
692
+ marginLeft: '14px',
693
+ marginTop: '4px',
694
+ marginBottom: '4px',
695
+ borderRadius: '4px',
696
+ overflowX: 'auto',
697
+ }, children: showInOut ? (_jsxs(_Fragment, { children: [inContent && (_jsxs("div", { style: { padding: '6px 10px' }, children: [_jsx("div", { className: cm.textMuted, style: {
698
+ fontSize: '9px',
699
+ textTransform: 'uppercase',
700
+ letterSpacing: '0.6px',
701
+ marginBottom: '4px',
702
+ fontWeight: 600,
703
+ }, children: "IN" }), inContent] })), outContent && (_jsxs("div", { style: {
704
+ padding: '6px 10px',
705
+ borderTop: inContent ? '1px solid rgba(128,128,128,0.15)' : undefined,
706
+ }, children: [_jsx("div", { className: cm.textMuted, style: {
707
+ fontSize: '9px',
708
+ textTransform: 'uppercase',
709
+ letterSpacing: '0.6px',
710
+ marginBottom: '4px',
711
+ fontWeight: 600,
712
+ }, children: "OUT" }), outContent] }))] })) : (_jsxs("div", { style: { padding: '6px 10px' }, children: [name === 'rename_file' && (_jsxs("pre", { style: PRE, children: [(input ?? {}).old_path ?? '', ' → ', (input ?? {}).new_path ?? ''] })), (name === 'list_files' || name === 'find_files' || name === 'search_files') &&
713
+ output !== undefined &&
714
+ renderOut(name, output), name === 'find_package' && output !== undefined && (_jsx(PackageResultRows, { results: packageResults(output), onFileOpen: onFileOpen }))] })) }))] }));
715
+ });
716
+ //# sourceMappingURL=ToolCallCard.js.map