@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.
- package/LICENSE +115 -0
- package/dist/chatTimelineOrdering.d.ts +50 -0
- package/dist/chatTimelineOrdering.d.ts.map +1 -0
- package/dist/chatTimelineOrdering.js +46 -0
- package/dist/chatTimelineOrdering.js.map +1 -0
- package/dist/command-metadata.d.ts +78 -0
- package/dist/command-metadata.d.ts.map +1 -0
- package/dist/command-metadata.js +214 -0
- package/dist/command-metadata.js.map +1 -0
- package/dist/components/ActivityCard.d.ts +28 -0
- package/dist/components/ActivityCard.d.ts.map +1 -0
- package/dist/components/ActivityCard.js +62 -0
- package/dist/components/ActivityCard.js.map +1 -0
- package/dist/components/AutoCommitBadge.d.ts +58 -0
- package/dist/components/AutoCommitBadge.d.ts.map +1 -0
- package/dist/components/AutoCommitBadge.js +78 -0
- package/dist/components/AutoCommitBadge.js.map +1 -0
- package/dist/components/ChatPanel.d.ts +184 -0
- package/dist/components/ChatPanel.d.ts.map +1 -0
- package/dist/components/ChatPanel.js +6691 -0
- package/dist/components/ChatPanel.js.map +1 -0
- package/dist/components/CommandPalette.d.ts +18 -0
- package/dist/components/CommandPalette.d.ts.map +1 -0
- package/dist/components/CommandPalette.js +33 -0
- package/dist/components/CommandPalette.js.map +1 -0
- package/dist/components/DeviceFrameSelector.d.ts +60 -0
- package/dist/components/DeviceFrameSelector.d.ts.map +1 -0
- package/dist/components/DeviceFrameSelector.js +230 -0
- package/dist/components/DeviceFrameSelector.js.map +1 -0
- package/dist/components/EditorPanel.d.ts +17 -0
- package/dist/components/EditorPanel.d.ts.map +1 -0
- package/dist/components/EditorPanel.js +241 -0
- package/dist/components/EditorPanel.js.map +1 -0
- package/dist/components/FileExplorer.d.ts +64 -0
- package/dist/components/FileExplorer.d.ts.map +1 -0
- package/dist/components/FileExplorer.js +1111 -0
- package/dist/components/FileExplorer.js.map +1 -0
- package/dist/components/FileExplorerContextMenu.d.ts +37 -0
- package/dist/components/FileExplorerContextMenu.d.ts.map +1 -0
- package/dist/components/FileExplorerContextMenu.js +241 -0
- package/dist/components/FileExplorerContextMenu.js.map +1 -0
- package/dist/components/HelpCard.d.ts +34 -0
- package/dist/components/HelpCard.d.ts.map +1 -0
- package/dist/components/HelpCard.js +28 -0
- package/dist/components/HelpCard.js.map +1 -0
- package/dist/components/Icon.d.ts +50 -0
- package/dist/components/Icon.d.ts.map +1 -0
- package/dist/components/Icon.js +25 -0
- package/dist/components/Icon.js.map +1 -0
- package/dist/components/KeyboardShortcutsPanel.d.ts +19 -0
- package/dist/components/KeyboardShortcutsPanel.d.ts.map +1 -0
- package/dist/components/KeyboardShortcutsPanel.js +187 -0
- package/dist/components/KeyboardShortcutsPanel.js.map +1 -0
- package/dist/components/MarkdownContent.d.ts +52 -0
- package/dist/components/MarkdownContent.d.ts.map +1 -0
- package/dist/components/MarkdownContent.js +303 -0
- package/dist/components/MarkdownContent.js.map +1 -0
- package/dist/components/ModelsTable.d.ts +38 -0
- package/dist/components/ModelsTable.d.ts.map +1 -0
- package/dist/components/ModelsTable.js +96 -0
- package/dist/components/ModelsTable.js.map +1 -0
- package/dist/components/PreviewPanel.d.ts +66 -0
- package/dist/components/PreviewPanel.d.ts.map +1 -0
- package/dist/components/PreviewPanel.js +1752 -0
- package/dist/components/PreviewPanel.js.map +1 -0
- package/dist/components/QuickOpen.d.ts +20 -0
- package/dist/components/QuickOpen.d.ts.map +1 -0
- package/dist/components/QuickOpen.js +72 -0
- package/dist/components/QuickOpen.js.map +1 -0
- package/dist/components/QuickPicker.d.ts +21 -0
- package/dist/components/QuickPicker.d.ts.map +1 -0
- package/dist/components/QuickPicker.js +174 -0
- package/dist/components/QuickPicker.js.map +1 -0
- package/dist/components/RelevantSkillSuggestion.d.ts +41 -0
- package/dist/components/RelevantSkillSuggestion.d.ts.map +1 -0
- package/dist/components/RelevantSkillSuggestion.js +85 -0
- package/dist/components/RelevantSkillSuggestion.js.map +1 -0
- package/dist/components/ReportModal.d.ts +35 -0
- package/dist/components/ReportModal.d.ts.map +1 -0
- package/dist/components/ReportModal.js +112 -0
- package/dist/components/ReportModal.js.map +1 -0
- package/dist/components/ResizeHandle.d.ts +21 -0
- package/dist/components/ResizeHandle.d.ts.map +1 -0
- package/dist/components/ResizeHandle.js +109 -0
- package/dist/components/ResizeHandle.js.map +1 -0
- package/dist/components/ScriptsCard.d.ts +37 -0
- package/dist/components/ScriptsCard.d.ts.map +1 -0
- package/dist/components/ScriptsCard.js +177 -0
- package/dist/components/ScriptsCard.js.map +1 -0
- package/dist/components/SearchPanel.d.ts +18 -0
- package/dist/components/SearchPanel.d.ts.map +1 -0
- package/dist/components/SearchPanel.js +449 -0
- package/dist/components/SearchPanel.js.map +1 -0
- package/dist/components/SettingsCard.d.ts +49 -0
- package/dist/components/SettingsCard.d.ts.map +1 -0
- package/dist/components/SettingsCard.js +59 -0
- package/dist/components/SettingsCard.js.map +1 -0
- package/dist/components/ShareModal.d.ts +32 -0
- package/dist/components/ShareModal.d.ts.map +1 -0
- package/dist/components/ShareModal.js +126 -0
- package/dist/components/ShareModal.js.map +1 -0
- package/dist/components/SidebarTabs.d.ts +18 -0
- package/dist/components/SidebarTabs.d.ts.map +1 -0
- package/dist/components/SidebarTabs.js +20 -0
- package/dist/components/SidebarTabs.js.map +1 -0
- package/dist/components/SkillsCard.d.ts +86 -0
- package/dist/components/SkillsCard.d.ts.map +1 -0
- package/dist/components/SkillsCard.js +227 -0
- package/dist/components/SkillsCard.js.map +1 -0
- package/dist/components/StreamingIndicator.d.ts +50 -0
- package/dist/components/StreamingIndicator.d.ts.map +1 -0
- package/dist/components/StreamingIndicator.js +142 -0
- package/dist/components/StreamingIndicator.js.map +1 -0
- package/dist/components/TabBar.d.ts +37 -0
- package/dist/components/TabBar.d.ts.map +1 -0
- package/dist/components/TabBar.js +187 -0
- package/dist/components/TabBar.js.map +1 -0
- package/dist/components/TipCard.d.ts +22 -0
- package/dist/components/TipCard.d.ts.map +1 -0
- package/dist/components/TipCard.js +73 -0
- package/dist/components/TipCard.js.map +1 -0
- package/dist/components/ToolCallCard.d.ts +18 -0
- package/dist/components/ToolCallCard.d.ts.map +1 -0
- package/dist/components/ToolCallCard.js +716 -0
- package/dist/components/ToolCallCard.js.map +1 -0
- package/dist/components/UserAvatar.d.ts +45 -0
- package/dist/components/UserAvatar.d.ts.map +1 -0
- package/dist/components/UserAvatar.js +80 -0
- package/dist/components/UserAvatar.js.map +1 -0
- package/dist/components/WorkspaceLayout.d.ts +19 -0
- package/dist/components/WorkspaceLayout.d.ts.map +1 -0
- package/dist/components/WorkspaceLayout.js +56 -0
- package/dist/components/WorkspaceLayout.js.map +1 -0
- package/dist/components/activity-utilities.d.ts +107 -0
- package/dist/components/activity-utilities.d.ts.map +1 -0
- package/dist/components/activity-utilities.js +154 -0
- package/dist/components/activity-utilities.js.map +1 -0
- package/dist/components/chat-autocommit-utilities.d.ts +172 -0
- package/dist/components/chat-autocommit-utilities.d.ts.map +1 -0
- package/dist/components/chat-autocommit-utilities.js +185 -0
- package/dist/components/chat-autocommit-utilities.js.map +1 -0
- package/dist/components/chat-card-style.d.ts +64 -0
- package/dist/components/chat-card-style.d.ts.map +1 -0
- package/dist/components/chat-card-style.js +74 -0
- package/dist/components/chat-card-style.js.map +1 -0
- package/dist/components/chat-commands.d.ts +17 -0
- package/dist/components/chat-commands.d.ts.map +1 -0
- package/dist/components/chat-commands.js +17 -0
- package/dist/components/chat-commands.js.map +1 -0
- package/dist/components/chat-commit-utilities.d.ts +29 -0
- package/dist/components/chat-commit-utilities.d.ts.map +1 -0
- package/dist/components/chat-commit-utilities.js +31 -0
- package/dist/components/chat-commit-utilities.js.map +1 -0
- package/dist/components/chat-cost-utilities.d.ts +62 -0
- package/dist/components/chat-cost-utilities.d.ts.map +1 -0
- package/dist/components/chat-cost-utilities.js +66 -0
- package/dist/components/chat-cost-utilities.js.map +1 -0
- package/dist/components/chat-effort-utilities.d.ts +96 -0
- package/dist/components/chat-effort-utilities.d.ts.map +1 -0
- package/dist/components/chat-effort-utilities.js +97 -0
- package/dist/components/chat-effort-utilities.js.map +1 -0
- package/dist/components/chat-help-utilities.d.ts +89 -0
- package/dist/components/chat-help-utilities.d.ts.map +1 -0
- package/dist/components/chat-help-utilities.js +131 -0
- package/dist/components/chat-help-utilities.js.map +1 -0
- package/dist/components/chat-model-mode-utilities.d.ts +143 -0
- package/dist/components/chat-model-mode-utilities.d.ts.map +1 -0
- package/dist/components/chat-model-mode-utilities.js +191 -0
- package/dist/components/chat-model-mode-utilities.js.map +1 -0
- package/dist/components/chat-models-utilities.d.ts +98 -0
- package/dist/components/chat-models-utilities.d.ts.map +1 -0
- package/dist/components/chat-models-utilities.js +136 -0
- package/dist/components/chat-models-utilities.js.map +1 -0
- package/dist/components/chat-report-utilities.d.ts +149 -0
- package/dist/components/chat-report-utilities.d.ts.map +1 -0
- package/dist/components/chat-report-utilities.js +151 -0
- package/dist/components/chat-report-utilities.js.map +1 -0
- package/dist/components/chat-scripts-utilities.d.ts +140 -0
- package/dist/components/chat-scripts-utilities.d.ts.map +1 -0
- package/dist/components/chat-scripts-utilities.js +158 -0
- package/dist/components/chat-scripts-utilities.js.map +1 -0
- package/dist/components/chat-settings-utilities.d.ts +72 -0
- package/dist/components/chat-settings-utilities.d.ts.map +1 -0
- package/dist/components/chat-settings-utilities.js +56 -0
- package/dist/components/chat-settings-utilities.js.map +1 -0
- package/dist/components/chat-share-utilities.d.ts +104 -0
- package/dist/components/chat-share-utilities.d.ts.map +1 -0
- package/dist/components/chat-share-utilities.js +90 -0
- package/dist/components/chat-share-utilities.js.map +1 -0
- package/dist/components/chat-skills-utilities.d.ts +242 -0
- package/dist/components/chat-skills-utilities.d.ts.map +1 -0
- package/dist/components/chat-skills-utilities.js +433 -0
- package/dist/components/chat-skills-utilities.js.map +1 -0
- package/dist/components/chat-stream-utilities.d.ts +74 -0
- package/dist/components/chat-stream-utilities.d.ts.map +1 -0
- package/dist/components/chat-stream-utilities.js +103 -0
- package/dist/components/chat-stream-utilities.js.map +1 -0
- package/dist/components/chat-tips-utilities.d.ts +108 -0
- package/dist/components/chat-tips-utilities.d.ts.map +1 -0
- package/dist/components/chat-tips-utilities.js +137 -0
- package/dist/components/chat-tips-utilities.js.map +1 -0
- package/dist/components/device-cycle.d.ts +84 -0
- package/dist/components/device-cycle.d.ts.map +1 -0
- package/dist/components/device-cycle.js +77 -0
- package/dist/components/device-cycle.js.map +1 -0
- package/dist/components/index.d.ts +36 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +33 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/tool-call-utilities.d.ts +69 -0
- package/dist/components/tool-call-utilities.d.ts.map +1 -0
- package/dist/components/tool-call-utilities.js +368 -0
- package/dist/components/tool-call-utilities.js.map +1 -0
- package/dist/components/user-avatar-utilities.d.ts +41 -0
- package/dist/components/user-avatar-utilities.d.ts.map +1 -0
- package/dist/components/user-avatar-utilities.js +56 -0
- package/dist/components/user-avatar-utilities.js.map +1 -0
- package/dist/components/workspace-layout-utilities.d.ts +37 -0
- package/dist/components/workspace-layout-utilities.d.ts.map +1 -0
- package/dist/components/workspace-layout-utilities.js +58 -0
- package/dist/components/workspace-layout-utilities.js.map +1 -0
- package/dist/customEventCards.d.ts +117 -0
- package/dist/customEventCards.d.ts.map +1 -0
- package/dist/customEventCards.js +36 -0
- package/dist/customEventCards.js.map +1 -0
- package/dist/hooks/index.d.ts +8 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +8 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/useKeyboardShortcuts.d.ts +48 -0
- package/dist/hooks/useKeyboardShortcuts.d.ts.map +1 -0
- package/dist/hooks/useKeyboardShortcuts.js +112 -0
- package/dist/hooks/useKeyboardShortcuts.js.map +1 -0
- package/dist/hooks/useViewport.d.ts +42 -0
- package/dist/hooks/useViewport.d.ts.map +1 -0
- package/dist/hooks/useViewport.js +61 -0
- package/dist/hooks/useViewport.js.map +1 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -0
- package/dist/settings-metadata.d.ts +78 -0
- package/dist/settings-metadata.d.ts.map +1 -0
- package/dist/settings-metadata.js +131 -0
- package/dist/settings-metadata.js.map +1 -0
- package/dist/types.d.ts +751 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +25 -0
- package/dist/types.js.map +1 -0
- 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
|