@co0ontty/wand 1.7.0 → 1.9.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/dist/config.js +40 -0
- package/dist/git-worktree.d.ts +17 -1
- package/dist/git-worktree.js +244 -0
- package/dist/server-session-routes.js +153 -0
- package/dist/server.js +34 -0
- package/dist/storage.d.ts +1 -0
- package/dist/storage.js +204 -66
- package/dist/types.d.ts +51 -0
- package/dist/web-ui/content/scripts.js +802 -218
- package/dist/web-ui/content/styles.css +1085 -185
- package/package.json +1 -1
|
@@ -60,6 +60,335 @@
|
|
|
60
60
|
var configPath = "${escapeHtml(configPath)}";
|
|
61
61
|
var CHAT_EXPAND_STATE_STORAGE_KEY = "wand-chat-expand-state-v1";
|
|
62
62
|
var CHAT_AUTO_FOLLOW_STORAGE_KEY = "wand-chat-auto-follow";
|
|
63
|
+
var DEFAULT_PANEL_STATE = {
|
|
64
|
+
sessionsDrawerOpen: false,
|
|
65
|
+
filePanelOpen: false,
|
|
66
|
+
shortcutsExpanded: false,
|
|
67
|
+
claudeHistoryExpanded: true,
|
|
68
|
+
chatMessageExpanded: true,
|
|
69
|
+
structuredThinkingExpanded: true,
|
|
70
|
+
structuredToolGroupExpanded: false,
|
|
71
|
+
structuredInlineToolExpanded: false,
|
|
72
|
+
structuredTerminalExpanded: false,
|
|
73
|
+
structuredToolCardExpanded: false,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
function getConfiguredPanelDefaults(configOverride) {
|
|
77
|
+
var currentConfig = configOverride;
|
|
78
|
+
if (!currentConfig || typeof currentConfig !== "object") {
|
|
79
|
+
return {
|
|
80
|
+
sessionsDrawerOpen: DEFAULT_PANEL_STATE.sessionsDrawerOpen,
|
|
81
|
+
filePanelOpen: DEFAULT_PANEL_STATE.filePanelOpen,
|
|
82
|
+
shortcutsExpanded: DEFAULT_PANEL_STATE.shortcutsExpanded,
|
|
83
|
+
claudeHistoryExpanded: DEFAULT_PANEL_STATE.claudeHistoryExpanded,
|
|
84
|
+
chatMessageExpanded: DEFAULT_PANEL_STATE.chatMessageExpanded,
|
|
85
|
+
structuredThinkingExpanded: DEFAULT_PANEL_STATE.structuredThinkingExpanded,
|
|
86
|
+
structuredToolGroupExpanded: DEFAULT_PANEL_STATE.structuredToolGroupExpanded,
|
|
87
|
+
structuredInlineToolExpanded: DEFAULT_PANEL_STATE.structuredInlineToolExpanded,
|
|
88
|
+
structuredTerminalExpanded: DEFAULT_PANEL_STATE.structuredTerminalExpanded,
|
|
89
|
+
structuredToolCardExpanded: DEFAULT_PANEL_STATE.structuredToolCardExpanded,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
var preferences = currentConfig.uiPreferences;
|
|
93
|
+
var configured = preferences && typeof preferences === "object" ? preferences.defaultPanelState : null;
|
|
94
|
+
return {
|
|
95
|
+
sessionsDrawerOpen: configured && typeof configured.sessionsDrawerOpen === "boolean" ? configured.sessionsDrawerOpen : DEFAULT_PANEL_STATE.sessionsDrawerOpen,
|
|
96
|
+
filePanelOpen: configured && typeof configured.filePanelOpen === "boolean" ? configured.filePanelOpen : DEFAULT_PANEL_STATE.filePanelOpen,
|
|
97
|
+
shortcutsExpanded: configured && typeof configured.shortcutsExpanded === "boolean" ? configured.shortcutsExpanded : DEFAULT_PANEL_STATE.shortcutsExpanded,
|
|
98
|
+
claudeHistoryExpanded: configured && typeof configured.claudeHistoryExpanded === "boolean" ? configured.claudeHistoryExpanded : DEFAULT_PANEL_STATE.claudeHistoryExpanded,
|
|
99
|
+
chatMessageExpanded: configured && typeof configured.chatMessageExpanded === "boolean" ? configured.chatMessageExpanded : DEFAULT_PANEL_STATE.chatMessageExpanded,
|
|
100
|
+
structuredThinkingExpanded: configured && typeof configured.structuredThinkingExpanded === "boolean" ? configured.structuredThinkingExpanded : DEFAULT_PANEL_STATE.structuredThinkingExpanded,
|
|
101
|
+
structuredToolGroupExpanded: configured && typeof configured.structuredToolGroupExpanded === "boolean" ? configured.structuredToolGroupExpanded : DEFAULT_PANEL_STATE.structuredToolGroupExpanded,
|
|
102
|
+
structuredInlineToolExpanded: configured && typeof configured.structuredInlineToolExpanded === "boolean" ? configured.structuredInlineToolExpanded : DEFAULT_PANEL_STATE.structuredInlineToolExpanded,
|
|
103
|
+
structuredTerminalExpanded: configured && typeof configured.structuredTerminalExpanded === "boolean" ? configured.structuredTerminalExpanded : DEFAULT_PANEL_STATE.structuredTerminalExpanded,
|
|
104
|
+
structuredToolCardExpanded: configured && typeof configured.structuredToolCardExpanded === "boolean" ? configured.structuredToolCardExpanded : DEFAULT_PANEL_STATE.structuredToolCardExpanded,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getStoredBoolean(key) {
|
|
109
|
+
try {
|
|
110
|
+
var saved = localStorage.getItem(key);
|
|
111
|
+
if (saved === "true") return true;
|
|
112
|
+
if (saved === "false") return false;
|
|
113
|
+
return null;
|
|
114
|
+
} catch (e) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function getInitialPanelBoolean(key, fieldName) {
|
|
120
|
+
var stored = getStoredBoolean(key);
|
|
121
|
+
if (stored !== null) return stored;
|
|
122
|
+
return DEFAULT_PANEL_STATE[fieldName];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function persistPanelBoolean(key, value) {
|
|
126
|
+
try {
|
|
127
|
+
localStorage.setItem(key, String(!!value));
|
|
128
|
+
} catch (e) {}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function applyConfiguredPanelDefaults() {
|
|
132
|
+
var defaults = getConfiguredPanelDefaults((typeof state !== "undefined" && state) ? state.config : null);
|
|
133
|
+
if (getStoredBoolean("wand-sessions-drawer-open") === null) {
|
|
134
|
+
state.sessionsDrawerOpen = defaults.sessionsDrawerOpen;
|
|
135
|
+
}
|
|
136
|
+
if (getStoredBoolean("wand-file-panel-open") === null) {
|
|
137
|
+
state.filePanelOpen = defaults.filePanelOpen;
|
|
138
|
+
}
|
|
139
|
+
if (getStoredBoolean("wand-shortcuts-expanded") === null) {
|
|
140
|
+
state.shortcutsExpanded = defaults.shortcutsExpanded;
|
|
141
|
+
}
|
|
142
|
+
if (getStoredBoolean("wand-claude-history-expanded") === null) {
|
|
143
|
+
state.claudeHistoryExpanded = defaults.claudeHistoryExpanded;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function getPanelStateSettingsFormValues() {
|
|
148
|
+
return {
|
|
149
|
+
sessionsDrawerOpen: !!((document.getElementById("cfg-panel-sessions-drawer") || {}).checked),
|
|
150
|
+
filePanelOpen: !!((document.getElementById("cfg-panel-file") || {}).checked),
|
|
151
|
+
shortcutsExpanded: !!((document.getElementById("cfg-panel-shortcuts") || {}).checked),
|
|
152
|
+
claudeHistoryExpanded: !!((document.getElementById("cfg-panel-history") || {}).checked),
|
|
153
|
+
chatMessageExpanded: !!((document.getElementById("cfg-panel-chat-message") || {}).checked),
|
|
154
|
+
structuredThinkingExpanded: !!((document.getElementById("cfg-panel-structured-thinking") || {}).checked),
|
|
155
|
+
structuredToolGroupExpanded: !!((document.getElementById("cfg-panel-structured-tool-group") || {}).checked),
|
|
156
|
+
structuredInlineToolExpanded: !!((document.getElementById("cfg-panel-structured-inline-tool") || {}).checked),
|
|
157
|
+
structuredTerminalExpanded: !!((document.getElementById("cfg-panel-structured-terminal") || {}).checked),
|
|
158
|
+
structuredToolCardExpanded: !!((document.getElementById("cfg-panel-structured-tool-card") || {}).checked),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function syncPanelStateSettingsForm(panelDefaults) {
|
|
163
|
+
var defaults = panelDefaults || getConfiguredPanelDefaults();
|
|
164
|
+
var sessionsDrawerEl = document.getElementById("cfg-panel-sessions-drawer");
|
|
165
|
+
var filePanelEl = document.getElementById("cfg-panel-file");
|
|
166
|
+
var shortcutsEl = document.getElementById("cfg-panel-shortcuts");
|
|
167
|
+
var historyEl = document.getElementById("cfg-panel-history");
|
|
168
|
+
var chatMessageEl = document.getElementById("cfg-panel-chat-message");
|
|
169
|
+
var structuredThinkingEl = document.getElementById("cfg-panel-structured-thinking");
|
|
170
|
+
var structuredToolGroupEl = document.getElementById("cfg-panel-structured-tool-group");
|
|
171
|
+
var structuredInlineToolEl = document.getElementById("cfg-panel-structured-inline-tool");
|
|
172
|
+
var structuredTerminalEl = document.getElementById("cfg-panel-structured-terminal");
|
|
173
|
+
var structuredToolCardEl = document.getElementById("cfg-panel-structured-tool-card");
|
|
174
|
+
if (sessionsDrawerEl) sessionsDrawerEl.checked = !!defaults.sessionsDrawerOpen;
|
|
175
|
+
if (filePanelEl) filePanelEl.checked = !!defaults.filePanelOpen;
|
|
176
|
+
if (shortcutsEl) shortcutsEl.checked = !!defaults.shortcutsExpanded;
|
|
177
|
+
if (historyEl) historyEl.checked = !!defaults.claudeHistoryExpanded;
|
|
178
|
+
if (chatMessageEl) chatMessageEl.checked = !!defaults.chatMessageExpanded;
|
|
179
|
+
if (structuredThinkingEl) structuredThinkingEl.checked = !!defaults.structuredThinkingExpanded;
|
|
180
|
+
if (structuredToolGroupEl) structuredToolGroupEl.checked = !!defaults.structuredToolGroupExpanded;
|
|
181
|
+
if (structuredInlineToolEl) structuredInlineToolEl.checked = !!defaults.structuredInlineToolExpanded;
|
|
182
|
+
if (structuredTerminalEl) structuredTerminalEl.checked = !!defaults.structuredTerminalExpanded;
|
|
183
|
+
if (structuredToolCardEl) structuredToolCardEl.checked = !!defaults.structuredToolCardExpanded;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function applySettingsConfig(config) {
|
|
187
|
+
state.config = config || null;
|
|
188
|
+
applyConfiguredPanelDefaults();
|
|
189
|
+
updatePanelDefaultControls();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function renderSettingsNav() {
|
|
193
|
+
return '<div class="settings-nav">' +
|
|
194
|
+
'<button class="settings-tab active" data-tab="about">关于</button>' +
|
|
195
|
+
'<button class="settings-tab" data-tab="general">基本配置</button>' +
|
|
196
|
+
'<button class="settings-tab" data-tab="security">安全</button>' +
|
|
197
|
+
'<button class="settings-tab" data-tab="presets">命令预设</button>' +
|
|
198
|
+
'</div>';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function renderAppearanceSettingsCard() {
|
|
202
|
+
return '<div class="settings-card settings-card-accent">' +
|
|
203
|
+
'<div class="settings-card-header">' +
|
|
204
|
+
'<div>' +
|
|
205
|
+
'<h3 class="settings-section-title">界面偏好</h3>' +
|
|
206
|
+
'<p class="settings-hint">这些选项决定新页面或未保存本地偏好的默认展开状态。</p>' +
|
|
207
|
+
'</div>' +
|
|
208
|
+
'</div>' +
|
|
209
|
+
'<div class="settings-toggle-list">' +
|
|
210
|
+
'<label class="settings-toggle-item" for="cfg-panel-sessions-drawer">' +
|
|
211
|
+
'<div><span class="settings-toggle-title">默认展开会话侧栏</span><span class="settings-toggle-desc">进入页面时左侧会话列表默认展开。</span></div>' +
|
|
212
|
+
'<input id="cfg-panel-sessions-drawer" type="checkbox" class="field-checkbox" />' +
|
|
213
|
+
'</label>' +
|
|
214
|
+
'<label class="settings-toggle-item" for="cfg-panel-file">' +
|
|
215
|
+
'<div><span class="settings-toggle-title">默认展开文件面板</span><span class="settings-toggle-desc">右侧文件浏览器在初始状态下打开。</span></div>' +
|
|
216
|
+
'<input id="cfg-panel-file" type="checkbox" class="field-checkbox" />' +
|
|
217
|
+
'</label>' +
|
|
218
|
+
'<label class="settings-toggle-item" for="cfg-panel-shortcuts">' +
|
|
219
|
+
'<div><span class="settings-toggle-title">默认展开快捷键栏</span><span class="settings-toggle-desc">移动端快捷键面板首次显示时展开完整行。</span></div>' +
|
|
220
|
+
'<input id="cfg-panel-shortcuts" type="checkbox" class="field-checkbox" />' +
|
|
221
|
+
'</label>' +
|
|
222
|
+
'<label class="settings-toggle-item" for="cfg-panel-history">' +
|
|
223
|
+
'<div><span class="settings-toggle-title">默认展开 Claude 历史</span><span class="settings-toggle-desc">侧栏里的 Claude 历史分组默认展开。</span></div>' +
|
|
224
|
+
'<input id="cfg-panel-history" type="checkbox" class="field-checkbox" />' +
|
|
225
|
+
'</label>' +
|
|
226
|
+
'<label class="settings-toggle-item" for="cfg-panel-chat-message">' +
|
|
227
|
+
'<div><span class="settings-toggle-title">默认展开聊天详情</span><span class="settings-toggle-desc">当某条消息没有本地展开记录时,采用这里的默认值。</span></div>' +
|
|
228
|
+
'<input id="cfg-panel-chat-message" type="checkbox" class="field-checkbox" />' +
|
|
229
|
+
'</label>' +
|
|
230
|
+
'<label class="settings-toggle-item" for="cfg-panel-structured-thinking">' +
|
|
231
|
+
'<div><span class="settings-toggle-title">默认展开思考卡片</span><span class="settings-toggle-desc">结构化模式中的 thinking 块默认展开。</span></div>' +
|
|
232
|
+
'<input id="cfg-panel-structured-thinking" type="checkbox" class="field-checkbox" />' +
|
|
233
|
+
'</label>' +
|
|
234
|
+
'<label class="settings-toggle-item" for="cfg-panel-structured-tool-group">' +
|
|
235
|
+
'<div><span class="settings-toggle-title">默认展开工具组</span><span class="settings-toggle-desc">连续工具调用合并后的工具组默认展开。</span></div>' +
|
|
236
|
+
'<input id="cfg-panel-structured-tool-group" type="checkbox" class="field-checkbox" />' +
|
|
237
|
+
'</label>' +
|
|
238
|
+
'<label class="settings-toggle-item" for="cfg-panel-structured-inline-tool">' +
|
|
239
|
+
'<div><span class="settings-toggle-title">默认展开内联工具</span><span class="settings-toggle-desc">Read、Grep、Glob 等内联工具结果默认展开。</span></div>' +
|
|
240
|
+
'<input id="cfg-panel-structured-inline-tool" type="checkbox" class="field-checkbox" />' +
|
|
241
|
+
'</label>' +
|
|
242
|
+
'<label class="settings-toggle-item" for="cfg-panel-structured-terminal">' +
|
|
243
|
+
'<div><span class="settings-toggle-title">默认展开终端卡片</span><span class="settings-toggle-desc">Bash 等终端输出卡片默认展开。</span></div>' +
|
|
244
|
+
'<input id="cfg-panel-structured-terminal" type="checkbox" class="field-checkbox" />' +
|
|
245
|
+
'</label>' +
|
|
246
|
+
'<label class="settings-toggle-item" for="cfg-panel-structured-tool-card">' +
|
|
247
|
+
'<div><span class="settings-toggle-title">默认展开通用工具卡</span><span class="settings-toggle-desc">工具调用、计划类卡片等通用卡片默认展开。</span></div>' +
|
|
248
|
+
'<input id="cfg-panel-structured-tool-card" type="checkbox" class="field-checkbox" />' +
|
|
249
|
+
'</label>' +
|
|
250
|
+
'</div>' +
|
|
251
|
+
'</div>';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function buildSettingsGeneralPanel() {
|
|
255
|
+
return '<div class="settings-panel" id="settings-tab-general">' +
|
|
256
|
+
'<div class="settings-card settings-card-accent">' +
|
|
257
|
+
'<div class="settings-card-header">' +
|
|
258
|
+
'<div>' +
|
|
259
|
+
'<h3 class="settings-section-title">基础运行配置</h3>' +
|
|
260
|
+
'<p class="settings-hint">影响服务器监听、默认模式和 CLI 行为。</p>' +
|
|
261
|
+
'</div>' +
|
|
262
|
+
'</div>' +
|
|
263
|
+
'<div class="field-row">' +
|
|
264
|
+
'<div class="field">' +
|
|
265
|
+
'<label class="field-label" for="cfg-host">监听地址 (host)</label>' +
|
|
266
|
+
'<input id="cfg-host" type="text" class="field-input" placeholder="127.0.0.1" />' +
|
|
267
|
+
'</div>' +
|
|
268
|
+
'<div class="field">' +
|
|
269
|
+
'<label class="field-label" for="cfg-port">端口 (port)</label>' +
|
|
270
|
+
'<input id="cfg-port" type="number" class="field-input" placeholder="8443" min="1" max="65535" />' +
|
|
271
|
+
'</div>' +
|
|
272
|
+
'</div>' +
|
|
273
|
+
'<div class="field field-inline">' +
|
|
274
|
+
'<input id="cfg-https" type="checkbox" class="field-checkbox" />' +
|
|
275
|
+
'<label class="field-label" for="cfg-https">启用 HTTPS</label>' +
|
|
276
|
+
'</div>' +
|
|
277
|
+
'<div class="field-row">' +
|
|
278
|
+
'<div class="field">' +
|
|
279
|
+
'<label class="field-label" for="cfg-mode">默认执行模式</label>' +
|
|
280
|
+
'<select id="cfg-mode" class="field-input">' +
|
|
281
|
+
'<option value="default">default</option>' +
|
|
282
|
+
'<option value="assist">assist</option>' +
|
|
283
|
+
'<option value="agent">agent</option>' +
|
|
284
|
+
'<option value="agent-max">agent-max</option>' +
|
|
285
|
+
'<option value="auto-edit">auto-edit</option>' +
|
|
286
|
+
'<option value="full-access">full-access</option>' +
|
|
287
|
+
'<option value="native">native</option>' +
|
|
288
|
+
'<option value="managed">managed</option>' +
|
|
289
|
+
'</select>' +
|
|
290
|
+
'</div>' +
|
|
291
|
+
'<div class="field">' +
|
|
292
|
+
'<label class="field-label" for="cfg-language">回复语言</label>' +
|
|
293
|
+
'<select id="cfg-language" class="field-input">' +
|
|
294
|
+
'<option value="">自动(不指定)</option>' +
|
|
295
|
+
'<option value="中文">中文</option>' +
|
|
296
|
+
'<option value="English">English</option>' +
|
|
297
|
+
'<option value="日本語">日本語</option>' +
|
|
298
|
+
'<option value="한국어">한국어</option>' +
|
|
299
|
+
'<option value="Español">Español</option>' +
|
|
300
|
+
'<option value="Français">Français</option>' +
|
|
301
|
+
'<option value="Deutsch">Deutsch</option>' +
|
|
302
|
+
'<option value="Русский">Русский</option>' +
|
|
303
|
+
'</select>' +
|
|
304
|
+
'</div>' +
|
|
305
|
+
'</div>' +
|
|
306
|
+
'<p class="field-hint settings-inline-hint">设置回复语言后,Claude 将尽量使用指定语言回复。</p>' +
|
|
307
|
+
'<div class="field">' +
|
|
308
|
+
'<label class="field-label" for="cfg-cwd">默认工作目录</label>' +
|
|
309
|
+
'<input id="cfg-cwd" type="text" class="field-input" placeholder="/home/user" />' +
|
|
310
|
+
'</div>' +
|
|
311
|
+
'<div class="field">' +
|
|
312
|
+
'<label class="field-label" for="cfg-shell">Shell</label>' +
|
|
313
|
+
'<input id="cfg-shell" type="text" class="field-input" placeholder="/bin/bash" />' +
|
|
314
|
+
'</div>' +
|
|
315
|
+
'</div>' +
|
|
316
|
+
renderAppearanceSettingsCard() +
|
|
317
|
+
'<button id="save-config-button" class="btn btn-primary btn-block">保存配置</button>' +
|
|
318
|
+
'<p id="config-message" class="hint hidden"></p>' +
|
|
319
|
+
'</div>';
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function persistSettingsBackedUiState() {
|
|
323
|
+
persistPanelBoolean("wand-sessions-drawer-open", state.sessionsDrawerOpen);
|
|
324
|
+
persistPanelBoolean("wand-file-panel-open", state.filePanelOpen);
|
|
325
|
+
persistPanelBoolean("wand-shortcuts-expanded", state.shortcutsExpanded);
|
|
326
|
+
persistPanelBoolean("wand-claude-history-expanded", state.claudeHistoryExpanded);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function resetSettingsBackedUiStateToDefaults() {
|
|
330
|
+
var defaults = getConfiguredPanelDefaults();
|
|
331
|
+
state.sessionsDrawerOpen = defaults.sessionsDrawerOpen;
|
|
332
|
+
state.filePanelOpen = defaults.filePanelOpen;
|
|
333
|
+
state.shortcutsExpanded = defaults.shortcutsExpanded;
|
|
334
|
+
state.claudeHistoryExpanded = defaults.claudeHistoryExpanded;
|
|
335
|
+
persistSettingsBackedUiState();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function handleSettingsConfigSaved(nextConfig) {
|
|
339
|
+
applySettingsConfig(nextConfig || state.config);
|
|
340
|
+
syncPanelStateSettingsForm();
|
|
341
|
+
resetSettingsBackedUiStateToDefaults();
|
|
342
|
+
updateLayoutState();
|
|
343
|
+
updateSessionsList();
|
|
344
|
+
updateCurrentSession();
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function updateSettingsActiveNav() {
|
|
348
|
+
var activeTab = document.querySelector(".settings-tab.active");
|
|
349
|
+
var nav = document.querySelector(".settings-nav");
|
|
350
|
+
if (!nav) return;
|
|
351
|
+
if (activeTab) {
|
|
352
|
+
nav.setAttribute("data-active-tab", activeTab.getAttribute("data-tab") || "");
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function updateCollapsedShortcutsUi() {
|
|
357
|
+
var wrap = document.querySelector(".inline-shortcuts-wrap");
|
|
358
|
+
var row = document.querySelector(".inline-shortcuts-expanded-row");
|
|
359
|
+
var toggle = document.querySelector(".shortcuts-toggle");
|
|
360
|
+
if (wrap) wrap.classList.toggle("expanded", state.shortcutsExpanded);
|
|
361
|
+
if (row) row.classList.toggle("visible", state.shortcutsExpanded);
|
|
362
|
+
if (toggle) {
|
|
363
|
+
toggle.classList.toggle("active", state.shortcutsExpanded);
|
|
364
|
+
toggle.textContent = state.shortcutsExpanded ? "\u203a" : "\u2039";
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function updatePanelDefaultControls() {
|
|
369
|
+
syncPanelStateSettingsForm();
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function persistDrawerState() {
|
|
373
|
+
persistPanelBoolean("wand-sessions-drawer-open", state.sessionsDrawerOpen);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function persistHistoryPanelState() {
|
|
377
|
+
persistPanelBoolean("wand-claude-history-expanded", state.claudeHistoryExpanded);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function persistShortcutsExpandedState() {
|
|
381
|
+
persistPanelBoolean("wand-shortcuts-expanded", state.shortcutsExpanded);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function persistFilePanelState() {
|
|
385
|
+
persistPanelBoolean("wand-file-panel-open", state.filePanelOpen);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function refreshUiAfterPanelStateChange() {
|
|
389
|
+
updateLayoutState();
|
|
390
|
+
updateSessionsList();
|
|
391
|
+
}
|
|
63
392
|
|
|
64
393
|
var state = {
|
|
65
394
|
selectedId: (function() {
|
|
@@ -108,7 +437,7 @@
|
|
|
108
437
|
loginPending: false,
|
|
109
438
|
loginChecked: false,
|
|
110
439
|
bootstrapping: true,
|
|
111
|
-
sessionsDrawerOpen:
|
|
440
|
+
sessionsDrawerOpen: getInitialPanelBoolean("wand-sessions-drawer-open", "sessionsDrawerOpen"),
|
|
112
441
|
modalOpen: false,
|
|
113
442
|
presetValue: "",
|
|
114
443
|
cwdValue: "",
|
|
@@ -117,6 +446,11 @@
|
|
|
117
446
|
sessionCreateKind: "structured",
|
|
118
447
|
sessionCreateWorktree: false,
|
|
119
448
|
sessionTool: "claude",
|
|
449
|
+
activeWorktreeMergeSessionId: null,
|
|
450
|
+
worktreeMergeCheckResult: null,
|
|
451
|
+
worktreeMergeLoading: false,
|
|
452
|
+
worktreeMergeSubmitting: false,
|
|
453
|
+
worktreeMergeError: "",
|
|
120
454
|
preferredCommand: "claude",
|
|
121
455
|
structuredRunner: "claude-cli-print",
|
|
122
456
|
lastResize: { cols: 0, rows: 0 },
|
|
@@ -136,13 +470,7 @@
|
|
|
136
470
|
})(),
|
|
137
471
|
terminalBaseFontSize: 13,
|
|
138
472
|
keyboardPopupOpen: false,
|
|
139
|
-
filePanelOpen: (
|
|
140
|
-
try {
|
|
141
|
-
return localStorage.getItem("wand-file-panel-open") === "true";
|
|
142
|
-
} catch (e) {
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
})(),
|
|
473
|
+
filePanelOpen: getInitialPanelBoolean("wand-file-panel-open", "filePanelOpen"),
|
|
146
474
|
chatAutoFollow: (function() {
|
|
147
475
|
try {
|
|
148
476
|
var saved = localStorage.getItem(CHAT_AUTO_FOLLOW_STORAGE_KEY);
|
|
@@ -166,14 +494,14 @@
|
|
|
166
494
|
currentTask: null, // Current task title from Claude
|
|
167
495
|
terminalInteractive: false,
|
|
168
496
|
miniKeyboardVisible: false,
|
|
169
|
-
shortcutsExpanded:
|
|
497
|
+
shortcutsExpanded: getInitialPanelBoolean("wand-shortcuts-expanded", "shortcutsExpanded"),
|
|
170
498
|
modifiers: { ctrl: false, alt: false, shift: false },
|
|
171
499
|
fileSearchQuery: "",
|
|
172
500
|
fileExplorerLoading: false,
|
|
173
501
|
allFiles: [],
|
|
174
502
|
claudeHistory: [],
|
|
175
503
|
claudeHistoryLoaded: false,
|
|
176
|
-
claudeHistoryExpanded:
|
|
504
|
+
claudeHistoryExpanded: getInitialPanelBoolean("wand-claude-history-expanded", "claudeHistoryExpanded"),
|
|
177
505
|
claudeHistoryExpandedDirs: {},
|
|
178
506
|
sessionsManageMode: false,
|
|
179
507
|
selectedSessionIds: {},
|
|
@@ -535,6 +863,56 @@
|
|
|
535
863
|
}
|
|
536
864
|
}
|
|
537
865
|
|
|
866
|
+
function getDefaultChatMessageExpanded() {
|
|
867
|
+
return getConfiguredPanelDefaults().chatMessageExpanded;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
function getDefaultStructuredCardExpanded(cardType, fallbackValue) {
|
|
871
|
+
var defaults = getConfiguredPanelDefaults();
|
|
872
|
+
if (cardType === "thinking") {
|
|
873
|
+
return defaults.structuredThinkingExpanded;
|
|
874
|
+
}
|
|
875
|
+
if (cardType === "tool-group") {
|
|
876
|
+
return defaults.structuredToolGroupExpanded;
|
|
877
|
+
}
|
|
878
|
+
if (cardType === "inline-tool") {
|
|
879
|
+
return defaults.structuredInlineToolExpanded;
|
|
880
|
+
}
|
|
881
|
+
if (cardType === "terminal") {
|
|
882
|
+
return defaults.structuredTerminalExpanded;
|
|
883
|
+
}
|
|
884
|
+
if (cardType === "tool-card") {
|
|
885
|
+
return defaults.structuredToolCardExpanded;
|
|
886
|
+
}
|
|
887
|
+
if (typeof fallbackValue === "boolean") {
|
|
888
|
+
return fallbackValue;
|
|
889
|
+
}
|
|
890
|
+
return defaults.chatMessageExpanded;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
function hasPersistedExpandState(itemKey) {
|
|
894
|
+
if (!itemKey || !state.selectedId) return false;
|
|
895
|
+
var sessionState = getCurrentChatExpandState();
|
|
896
|
+
return typeof sessionState[itemKey] === "boolean";
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
function getExpandState(itemKey, cardType, fallbackValue) {
|
|
900
|
+
if (!itemKey || !state.selectedId) {
|
|
901
|
+
if (typeof fallbackValue === "boolean") return fallbackValue;
|
|
902
|
+
return getDefaultStructuredCardExpanded(cardType, fallbackValue);
|
|
903
|
+
}
|
|
904
|
+
var sessionState = getCurrentChatExpandState();
|
|
905
|
+
if (typeof sessionState[itemKey] === "boolean") return sessionState[itemKey];
|
|
906
|
+
return getDefaultStructuredCardExpanded(cardType, fallbackValue);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
function getPersistedExpandState(itemKey) {
|
|
910
|
+
if (!itemKey || !state.selectedId) return null;
|
|
911
|
+
var sessionState = getCurrentChatExpandState();
|
|
912
|
+
if (typeof sessionState[itemKey] === "boolean") return sessionState[itemKey];
|
|
913
|
+
return null;
|
|
914
|
+
}
|
|
915
|
+
|
|
538
916
|
function saveChatExpandStateMap(map) {
|
|
539
917
|
try {
|
|
540
918
|
if (!map || Object.keys(map).length === 0) {
|
|
@@ -555,12 +933,6 @@
|
|
|
555
933
|
return sessionState && typeof sessionState === "object" ? sessionState : {};
|
|
556
934
|
}
|
|
557
935
|
|
|
558
|
-
function getPersistedExpandState(itemKey) {
|
|
559
|
-
if (!itemKey || !state.selectedId) return null;
|
|
560
|
-
var sessionState = getCurrentChatExpandState();
|
|
561
|
-
return typeof sessionState[itemKey] === "boolean" ? sessionState[itemKey] : null;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
936
|
function setPersistedExpandState(itemKey, expanded) {
|
|
565
937
|
if (!itemKey || !state.selectedId) return;
|
|
566
938
|
var map = loadChatExpandStateMap();
|
|
@@ -677,9 +1049,8 @@
|
|
|
677
1049
|
container.querySelectorAll("[data-expand-key]").forEach(function(el) {
|
|
678
1050
|
var itemKey = getElementExpandKey(el);
|
|
679
1051
|
var kind = el.dataset.expandKind || "";
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
applyExpandedState(el, kind, persisted);
|
|
1052
|
+
if (!kind || !hasPersistedExpandState(itemKey)) return;
|
|
1053
|
+
applyExpandedState(el, kind, getPersistedExpandState(itemKey));
|
|
683
1054
|
});
|
|
684
1055
|
}
|
|
685
1056
|
|
|
@@ -822,7 +1193,7 @@
|
|
|
822
1193
|
})
|
|
823
1194
|
.then(function(config) {
|
|
824
1195
|
if (!config) return;
|
|
825
|
-
|
|
1196
|
+
applySettingsConfig(config);
|
|
826
1197
|
state.loginChecked = true;
|
|
827
1198
|
requestAnimationFrame(function() {
|
|
828
1199
|
try {
|
|
@@ -1042,12 +1413,9 @@
|
|
|
1042
1413
|
var preferredTool = getComposerTool();
|
|
1043
1414
|
var composerMode = getSafeModeForTool(preferredTool, state.chatMode);
|
|
1044
1415
|
|
|
1416
|
+
var showTerminalHeaderControls = !!selectedSession && state.currentView === "terminal";
|
|
1417
|
+
var showChatHeaderControls = !!selectedSession && state.currentView !== "terminal";
|
|
1045
1418
|
return '<div class="app-container">' +
|
|
1046
|
-
'<button id="sessions-toggle-button" class="floating-sidebar-toggle' + (state.sessionsDrawerOpen ? ' active' : '') + '" aria-label="Toggle sidebar">' +
|
|
1047
|
-
'<span class="hamburger-icon">' +
|
|
1048
|
-
'<span></span><span></span><span></span>' +
|
|
1049
|
-
'</span>' +
|
|
1050
|
-
'</button>' +
|
|
1051
1419
|
'<div id="sessions-drawer-backdrop" class="drawer-backdrop' + drawerClass + '"></div>' +
|
|
1052
1420
|
'<div class="main-layout' + (state.sessionsDrawerOpen ? ' sidebar-open' : '') + '">' +
|
|
1053
1421
|
'<aside id="sessions-drawer" class="sidebar' + drawerClass + '">' +
|
|
@@ -1089,11 +1457,37 @@
|
|
|
1089
1457
|
'</div>' +
|
|
1090
1458
|
'</aside>' +
|
|
1091
1459
|
'<main class="main-content">' +
|
|
1092
|
-
'<
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1460
|
+
'<div class="main-content-header">' +
|
|
1461
|
+
'<div class="main-content-header-left">' +
|
|
1462
|
+
'<button id="sessions-toggle-button" class="main-header-btn menu-toggle-btn' + (state.sessionsDrawerOpen ? ' active' : '') + '" type="button" aria-label="打开菜单" title="菜单">' +
|
|
1463
|
+
'<span class="hamburger-icon">' +
|
|
1464
|
+
'<span></span><span></span><span></span>' +
|
|
1465
|
+
'</span>' +
|
|
1466
|
+
'</button>' +
|
|
1467
|
+
'<span class="current-task hidden" id="current-task"></span>' +
|
|
1468
|
+
'</div>' +
|
|
1469
|
+
'<div class="main-content-header-center">' +
|
|
1470
|
+
'<div class="view-toggle-bar' + (state.selectedId ? '' : ' hidden') + '" id="view-toggle-bar">' +
|
|
1471
|
+
'<button id="view-terminal-btn" class="topbar-btn' + (state.currentView === "terminal" ? ' active' : '') + '" type="button" title="查看原始终端输出">终端</button>' +
|
|
1472
|
+
'<button id="view-chat-btn" class="topbar-btn' + (state.currentView !== "terminal" ? ' active' : '') + '" type="button" title="查看聊天解析视图">聊天</button>' +
|
|
1473
|
+
'</div>' +
|
|
1474
|
+
'</div>' +
|
|
1475
|
+
'<div class="main-content-header-right">' +
|
|
1476
|
+
'<div class="main-header-controls' + (showTerminalHeaderControls ? '' : ' hidden') + '" id="terminal-header-controls">' +
|
|
1477
|
+
'<button id="terminal-scale-down-top" class="main-header-btn terminal-scale-btn" type="button" title="缩小">−</button>' +
|
|
1478
|
+
'<span class="main-header-label terminal-scale-label" id="terminal-scale-label-top">' + Math.round(state.terminalScale * 100) + '%</span>' +
|
|
1479
|
+
'<button id="terminal-scale-up-top" class="main-header-btn terminal-scale-btn" type="button" title="放大">+</button>' +
|
|
1480
|
+
'<button id="page-refresh-btn" class="main-header-btn" type="button" title="刷新页面">↻</button>' +
|
|
1481
|
+
'<button id="terminal-jump-bottom" class="main-header-btn jump-latest-btn' + (state.showTerminalJumpToBottom ? ' visible' : '') + '" type="button" title="回到底部">↓ 最新</button>' +
|
|
1482
|
+
'</div>' +
|
|
1483
|
+
'<div class="main-header-controls' + (showChatHeaderControls ? '' : ' hidden') + '" id="chat-header-controls">' +
|
|
1484
|
+
'<button id="chat-follow-toggle" class="chat-follow-toggle topbar-btn' + (state.chatAutoFollow ? ' active' : '') + '" type="button" aria-pressed="' + (state.chatAutoFollow ? 'true' : 'false') + '" title="' + (state.chatAutoFollow ? '追踪底部:开启' : '追踪底部:已暂停') + '">' + (state.chatAutoFollow ? '追底' : '暂停') + '</button>' +
|
|
1485
|
+
'<button id="chat-jump-bottom" class="main-header-btn jump-latest-btn' + (state.showChatJumpToBottom ? ' visible' : '') + '" type="button" title="回到底部并继续追底">↓ 最新</button>' +
|
|
1486
|
+
'</div>' +
|
|
1487
|
+
'<button id="topbar-new-session-button" class="main-header-btn main-header-new-session" type="button" title="新对话">+ 新对话</button>' +
|
|
1488
|
+
'</div>' +
|
|
1096
1489
|
'</div>' +
|
|
1490
|
+
'<div class="main-content-body">' +
|
|
1097
1491
|
// File panel backdrop (mobile)
|
|
1098
1492
|
'<div id="file-panel-backdrop" class="file-panel-backdrop' + (state.filePanelOpen ? " open" : "") + '"></div>' +
|
|
1099
1493
|
// File side panel
|
|
@@ -1114,22 +1508,8 @@
|
|
|
1114
1508
|
'<div class="file-explorer" id="file-explorer">' + renderFileExplorer(selectedSession && selectedSession.cwd ? selectedSession.cwd : getConfigCwd()) + '</div>' +
|
|
1115
1509
|
'</div>' +
|
|
1116
1510
|
'</div>' +
|
|
1117
|
-
'<div id="output" class="terminal-container' + (state.selectedId ? "" : " hidden") + ' active">' +
|
|
1118
|
-
|
|
1119
|
-
'<button id="terminal-scale-down-top" class="terminal-scale-overlay-btn terminal-scale-btn" type="button" title="缩小">−</button>' +
|
|
1120
|
-
'<span class="terminal-scale-overlay-label terminal-scale-label" id="terminal-scale-label-top">' + Math.round(state.terminalScale * 100) + '%</span>' +
|
|
1121
|
-
'<button id="terminal-scale-up-top" class="terminal-scale-overlay-btn terminal-scale-btn" type="button" title="放大">+</button>' +
|
|
1122
|
-
'<span class="terminal-scale-overlay-divider"></span>' +
|
|
1123
|
-
'<button id="page-refresh-btn" class="terminal-scale-overlay-btn" type="button" title="刷新页面">↻</button>' +
|
|
1124
|
-
'</div>' +
|
|
1125
|
-
'<button id="terminal-jump-bottom" class="terminal-jump-bottom' + (state.showTerminalJumpToBottom ? ' visible' : '') + '" type="button" title="回到底部">↓ 最新</button>' +
|
|
1126
|
-
'</div>' +
|
|
1127
|
-
'<div id="chat-output" class="chat-container hidden">' +
|
|
1128
|
-
'<div class="chat-overlay-controls">' +
|
|
1129
|
-
'<button id="chat-follow-toggle" class="chat-follow-toggle topbar-btn' + (state.chatAutoFollow ? ' active' : '') + '" type="button" aria-pressed="' + (state.chatAutoFollow ? 'true' : 'false') + '" title="' + (state.chatAutoFollow ? '追踪底部:开启' : '追踪底部:已暂停') + '">' + (state.chatAutoFollow ? '追底' : '暂停') + '</button>' +
|
|
1130
|
-
'</div>' +
|
|
1131
|
-
'<button id="chat-jump-bottom" class="chat-jump-bottom' + (state.showChatJumpToBottom ? ' visible' : '') + '" type="button" title="回到底部并继续追底">↓ 最新</button>' +
|
|
1132
|
-
'</div>' +
|
|
1511
|
+
'<div id="output" class="terminal-container' + (state.selectedId ? "" : " hidden") + ' active"></div>' +
|
|
1512
|
+
'<div id="chat-output" class="chat-container hidden"></div>' +
|
|
1133
1513
|
'<div id="blank-chat" class="blank-chat' + (state.selectedId ? " hidden" : "") + '">' +
|
|
1134
1514
|
'<div class="blank-chat-inner">' +
|
|
1135
1515
|
'<div class="blank-chat-logo">W</div>' +
|
|
@@ -1239,7 +1619,29 @@
|
|
|
1239
1619
|
'</section>' +
|
|
1240
1620
|
'</main>' +
|
|
1241
1621
|
'</div>' +
|
|
1242
|
-
'</div>' + renderSessionModal() + renderSettingsModal();
|
|
1622
|
+
'</div>' + renderSessionModal() + renderWorktreeMergeModal() + renderSettingsModal();
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
function renderWorktreeMergeModal() {
|
|
1626
|
+
return '<section id="worktree-merge-modal" class="modal-backdrop hidden">' +
|
|
1627
|
+
'<div class="modal worktree-merge-modal">' +
|
|
1628
|
+
'<div class="modal-header">' +
|
|
1629
|
+
'<div>' +
|
|
1630
|
+
'<h2 class="modal-title">合并 Worktree</h2>' +
|
|
1631
|
+
'<p class="modal-subtitle">检查当前任务分支并快捷合并到主分支。</p>' +
|
|
1632
|
+
'</div>' +
|
|
1633
|
+
'<button id="close-worktree-merge-button" class="btn btn-ghost btn-icon">×</button>' +
|
|
1634
|
+
'</div>' +
|
|
1635
|
+
'<div class="modal-body">' +
|
|
1636
|
+
'<div id="worktree-merge-content" class="worktree-merge-content"></div>' +
|
|
1637
|
+
'<p id="worktree-merge-error" class="error-message hidden"></p>' +
|
|
1638
|
+
'<div class="worktree-merge-actions">' +
|
|
1639
|
+
'<button id="worktree-merge-cancel-button" class="btn btn-secondary">取消</button>' +
|
|
1640
|
+
'<button id="worktree-merge-confirm-button" class="btn btn-primary">确认合并并清理</button>' +
|
|
1641
|
+
'</div>' +
|
|
1642
|
+
'</div>' +
|
|
1643
|
+
'</div>' +
|
|
1644
|
+
'</section>';
|
|
1243
1645
|
}
|
|
1244
1646
|
|
|
1245
1647
|
function renderSettingsModal() {
|
|
@@ -1249,16 +1651,9 @@
|
|
|
1249
1651
|
'<h2 class="modal-title">设置</h2>' +
|
|
1250
1652
|
'<button id="close-settings-button" class="btn btn-ghost btn-icon">×</button>' +
|
|
1251
1653
|
'</div>' +
|
|
1252
|
-
'<div class="modal-body">' +
|
|
1253
|
-
|
|
1254
|
-
'<div class="settings-
|
|
1255
|
-
'<button class="settings-tab active" data-tab="about">关于</button>' +
|
|
1256
|
-
'<button class="settings-tab" data-tab="general">基本配置</button>' +
|
|
1257
|
-
'<button class="settings-tab" data-tab="security">安全</button>' +
|
|
1258
|
-
'<button class="settings-tab" data-tab="presets">命令预设</button>' +
|
|
1259
|
-
'</div>' +
|
|
1260
|
-
|
|
1261
|
-
// About tab
|
|
1654
|
+
'<div class="modal-body settings-layout">' +
|
|
1655
|
+
renderSettingsNav() +
|
|
1656
|
+
'<div class="settings-content">' +
|
|
1262
1657
|
'<div class="settings-panel active" id="settings-tab-about">' +
|
|
1263
1658
|
'<div class="settings-about-info">' +
|
|
1264
1659
|
'<div class="settings-about-row"><span class="settings-label">包名</span><span class="settings-value" id="settings-pkg-name">-</span></div>' +
|
|
@@ -1291,63 +1686,7 @@
|
|
|
1291
1686
|
'</div>' +
|
|
1292
1687
|
'</div>' +
|
|
1293
1688
|
|
|
1294
|
-
|
|
1295
|
-
'<div class="settings-panel" id="settings-tab-general">' +
|
|
1296
|
-
'<div class="field-row">' +
|
|
1297
|
-
'<div class="field">' +
|
|
1298
|
-
'<label class="field-label" for="cfg-host">监听地址 (host)</label>' +
|
|
1299
|
-
'<input id="cfg-host" type="text" class="field-input" placeholder="127.0.0.1" />' +
|
|
1300
|
-
'</div>' +
|
|
1301
|
-
'<div class="field">' +
|
|
1302
|
-
'<label class="field-label" for="cfg-port">端口 (port)</label>' +
|
|
1303
|
-
'<input id="cfg-port" type="number" class="field-input" placeholder="8443" min="1" max="65535" />' +
|
|
1304
|
-
'</div>' +
|
|
1305
|
-
'</div>' +
|
|
1306
|
-
'<div class="field field-inline">' +
|
|
1307
|
-
'<input id="cfg-https" type="checkbox" class="field-checkbox" />' +
|
|
1308
|
-
'<label class="field-label" for="cfg-https">启用 HTTPS</label>' +
|
|
1309
|
-
'</div>' +
|
|
1310
|
-
'<div class="field-row">' +
|
|
1311
|
-
'<div class="field">' +
|
|
1312
|
-
'<label class="field-label" for="cfg-mode">默认执行模式</label>' +
|
|
1313
|
-
'<select id="cfg-mode" class="field-input">' +
|
|
1314
|
-
'<option value="default">default</option>' +
|
|
1315
|
-
'<option value="assist">assist</option>' +
|
|
1316
|
-
'<option value="agent">agent</option>' +
|
|
1317
|
-
'<option value="agent-max">agent-max</option>' +
|
|
1318
|
-
'<option value="auto-edit">auto-edit</option>' +
|
|
1319
|
-
'<option value="full-access">full-access</option>' +
|
|
1320
|
-
'<option value="native">native</option>' +
|
|
1321
|
-
'<option value="managed">managed</option>' +
|
|
1322
|
-
'</select>' +
|
|
1323
|
-
'</div>' +
|
|
1324
|
-
'<div class="field">' +
|
|
1325
|
-
'<label class="field-label" for="cfg-language">回复语言</label>' +
|
|
1326
|
-
'<select id="cfg-language" class="field-input">' +
|
|
1327
|
-
'<option value="">自动(不指定)</option>' +
|
|
1328
|
-
'<option value="中文">中文</option>' +
|
|
1329
|
-
'<option value="English">English</option>' +
|
|
1330
|
-
'<option value="日本語">日本語</option>' +
|
|
1331
|
-
'<option value="한국어">한국어</option>' +
|
|
1332
|
-
'<option value="Español">Español</option>' +
|
|
1333
|
-
'<option value="Français">Français</option>' +
|
|
1334
|
-
'<option value="Deutsch">Deutsch</option>' +
|
|
1335
|
-
'<option value="Русский">Русский</option>' +
|
|
1336
|
-
'</select>' +
|
|
1337
|
-
'</div>' +
|
|
1338
|
-
'</div>' +
|
|
1339
|
-
'<p class="field-hint" style="margin-top:-4px;">设置回复语言后,Claude 将尽量使用指定语言回复。</p>' +
|
|
1340
|
-
'<div class="field">' +
|
|
1341
|
-
'<label class="field-label" for="cfg-cwd">默认工作目录</label>' +
|
|
1342
|
-
'<input id="cfg-cwd" type="text" class="field-input" placeholder="/home/user" />' +
|
|
1343
|
-
'</div>' +
|
|
1344
|
-
'<div class="field">' +
|
|
1345
|
-
'<label class="field-label" for="cfg-shell">Shell</label>' +
|
|
1346
|
-
'<input id="cfg-shell" type="text" class="field-input" placeholder="/bin/bash" />' +
|
|
1347
|
-
'</div>' +
|
|
1348
|
-
'<button id="save-config-button" class="btn btn-primary btn-block">保存配置</button>' +
|
|
1349
|
-
'<p id="config-message" class="hint hidden"></p>' +
|
|
1350
|
-
'</div>' +
|
|
1689
|
+
buildSettingsGeneralPanel() +
|
|
1351
1690
|
|
|
1352
1691
|
// Security tab
|
|
1353
1692
|
'<div class="settings-panel" id="settings-tab-security">' +
|
|
@@ -1385,6 +1724,7 @@
|
|
|
1385
1724
|
'<div class="settings-panel" id="settings-tab-presets">' +
|
|
1386
1725
|
'<div id="presets-list" class="presets-list"></div>' +
|
|
1387
1726
|
'</div>' +
|
|
1727
|
+
'</div>' +
|
|
1388
1728
|
'</div>' +
|
|
1389
1729
|
'</div>' +
|
|
1390
1730
|
'</section>';
|
|
@@ -1747,9 +2087,7 @@
|
|
|
1747
2087
|
|
|
1748
2088
|
function setFilePanelOpen(nextOpen) {
|
|
1749
2089
|
state.filePanelOpen = nextOpen;
|
|
1750
|
-
|
|
1751
|
-
localStorage.setItem("wand-file-panel-open", String(state.filePanelOpen));
|
|
1752
|
-
} catch (e) {}
|
|
2090
|
+
persistFilePanelState();
|
|
1753
2091
|
if (state.filePanelOpen && isMobileLayout()) {
|
|
1754
2092
|
state.sessionsDrawerOpen = false;
|
|
1755
2093
|
}
|
|
@@ -2296,9 +2634,18 @@
|
|
|
2296
2634
|
recoveryHint = '<span class="session-id" title="自动恢复的会话">自动恢复</span>';
|
|
2297
2635
|
}
|
|
2298
2636
|
|
|
2637
|
+
var canOpenMerge = !state.sessionsManageMode && session.worktreeEnabled && session.worktree && session.worktree.branch && session.worktree.path;
|
|
2638
|
+
var needsCleanup = session.worktreeMergeStatus === "merged" && session.worktreeMergeInfo && session.worktreeMergeInfo.cleanupDone === false;
|
|
2639
|
+
var mergeDisabled = session.status === "running" || session.worktreeMergeStatus === "merging";
|
|
2640
|
+
var mergeTitle = needsCleanup ? "重试清理 worktree" : "合并到主分支";
|
|
2641
|
+
var mergeButton = canOpenMerge && session.worktreeMergeStatus !== "merged"
|
|
2642
|
+
? '<button class="session-action-btn merge-btn" data-action="worktree-merge" data-session-id="' + session.id + '" type="button" aria-label="' + escapeHtml(mergeTitle) + '" title="' + escapeHtml(mergeTitle) + '"' + (mergeDisabled ? ' disabled' : '') + '><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 7h10"/><path d="M7 12h10"/><path d="M7 17h10"/><path d="M5 7l-2 2 2 2"/><path d="M19 15l2 2-2 2"/></svg></button>'
|
|
2643
|
+
: needsCleanup
|
|
2644
|
+
? '<button class="session-action-btn merge-btn" data-action="worktree-cleanup" data-session-id="' + session.id + '" type="button" aria-label="重试清理 worktree" title="重试清理 worktree"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/></svg></button>'
|
|
2645
|
+
: "";
|
|
2299
2646
|
var deleteButton = state.sessionsManageMode ? '' : '<button class="session-action-btn delete-btn" data-action="delete-session" data-session-id="' + session.id + '" type="button" aria-label="删除会话" title="删除此会话"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/></svg></button>';
|
|
2300
2647
|
var modeBadge = renderSessionKindBadge(session);
|
|
2301
|
-
var actionsHtml = '<span class="session-actions">' + resumeButton + deleteButton + '</span>';
|
|
2648
|
+
var actionsHtml = '<span class="session-actions">' + resumeButton + mergeButton + deleteButton + '</span>';
|
|
2302
2649
|
|
|
2303
2650
|
return '<div class="session-item' + activeClass + selectedClass + '" data-session-id="' + session.id + '" role="button" tabindex="0">' +
|
|
2304
2651
|
'<div class="session-item-content">' +
|
|
@@ -2322,12 +2669,35 @@
|
|
|
2322
2669
|
'</div>';
|
|
2323
2670
|
}
|
|
2324
2671
|
|
|
2672
|
+
function getWorktreeMergeStatusLabel(session) {
|
|
2673
|
+
if (!session || !session.worktreeMergeStatus) return "";
|
|
2674
|
+
var labels = {
|
|
2675
|
+
ready: "可合并",
|
|
2676
|
+
checking: "检查中",
|
|
2677
|
+
merging: "合并中",
|
|
2678
|
+
merged: session.worktreeMergeInfo && session.worktreeMergeInfo.cleanupDone === false ? "已合并待清理" : "已合并",
|
|
2679
|
+
failed: "合并失败"
|
|
2680
|
+
};
|
|
2681
|
+
return labels[session.worktreeMergeStatus] || "";
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2684
|
+
function renderWorktreeMergeBadge(session) {
|
|
2685
|
+
var label = getWorktreeMergeStatusLabel(session);
|
|
2686
|
+
if (!label) return "";
|
|
2687
|
+
return '<span class="session-kind-badge worktree-merge ' + escapeHtml(session.worktreeMergeStatus || "") + '">' + escapeHtml(label) + '</span>';
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2325
2690
|
function renderWorktreeBadge(session) {
|
|
2326
2691
|
if (!session || !session.worktreeEnabled) return "";
|
|
2327
|
-
var
|
|
2328
|
-
|
|
2329
|
-
: '
|
|
2330
|
-
|
|
2692
|
+
var titleParts = [];
|
|
2693
|
+
if (session.worktree && session.worktree.branch) {
|
|
2694
|
+
titleParts.push('Worktree: ' + session.worktree.branch);
|
|
2695
|
+
}
|
|
2696
|
+
if (session.worktree && session.worktree.path) {
|
|
2697
|
+
titleParts.push('Path: ' + session.worktree.path);
|
|
2698
|
+
}
|
|
2699
|
+
var title = titleParts.length > 0 ? ' title="' + escapeHtml(titleParts.join('\n')) + '"' : '';
|
|
2700
|
+
return '<span class="session-kind-badge worktree"' + title + '>Worktree</span>' + renderWorktreeMergeBadge(session);
|
|
2331
2701
|
}
|
|
2332
2702
|
|
|
2333
2703
|
function renderSessionKindBadge(session) {
|
|
@@ -2723,15 +3093,7 @@
|
|
|
2723
3093
|
});
|
|
2724
3094
|
var savePassBtn = document.getElementById("save-password-button");
|
|
2725
3095
|
if (savePassBtn) savePassBtn.addEventListener("click", savePassword);
|
|
2726
|
-
|
|
2727
|
-
var settingsTabs = document.querySelectorAll(".settings-tab");
|
|
2728
|
-
for (var ti = 0; ti < settingsTabs.length; ti++) {
|
|
2729
|
-
settingsTabs[ti].addEventListener("click", function(e) {
|
|
2730
|
-
switchSettingsTab(e.target.getAttribute("data-tab"));
|
|
2731
|
-
});
|
|
2732
|
-
}
|
|
2733
|
-
var saveConfigBtn = document.getElementById("save-config-button");
|
|
2734
|
-
if (saveConfigBtn) saveConfigBtn.addEventListener("click", saveConfigSettings);
|
|
3096
|
+
bindSettingsModalEvents();
|
|
2735
3097
|
var uploadCertBtn = document.getElementById("upload-cert-button");
|
|
2736
3098
|
if (uploadCertBtn) uploadCertBtn.addEventListener("click", uploadCertificates);
|
|
2737
3099
|
var checkUpdateBtn = document.getElementById("check-update-button");
|
|
@@ -2754,6 +3116,12 @@
|
|
|
2754
3116
|
if (drawerNewSessBtn) drawerNewSessBtn.addEventListener("click", openSessionModal);
|
|
2755
3117
|
var closeModalBtn = document.getElementById("close-modal-button");
|
|
2756
3118
|
if (closeModalBtn) closeModalBtn.addEventListener("click", closeSessionModal);
|
|
3119
|
+
var closeWorktreeMergeBtn = document.getElementById("close-worktree-merge-button");
|
|
3120
|
+
if (closeWorktreeMergeBtn) closeWorktreeMergeBtn.addEventListener("click", closeWorktreeMergeModal);
|
|
3121
|
+
var worktreeMergeCancelBtn = document.getElementById("worktree-merge-cancel-button");
|
|
3122
|
+
if (worktreeMergeCancelBtn) worktreeMergeCancelBtn.addEventListener("click", closeWorktreeMergeModal);
|
|
3123
|
+
var worktreeMergeConfirmBtn = document.getElementById("worktree-merge-confirm-button");
|
|
3124
|
+
if (worktreeMergeConfirmBtn) worktreeMergeConfirmBtn.addEventListener("click", confirmWorktreeMerge);
|
|
2757
3125
|
var runBtn = document.getElementById("run-button");
|
|
2758
3126
|
if (runBtn) runBtn.addEventListener("click", runCommand);
|
|
2759
3127
|
var approvePermissionBtn = document.getElementById("approve-permission-btn");
|
|
@@ -2778,6 +3146,7 @@
|
|
|
2778
3146
|
var sessionModal = document.getElementById("session-modal");
|
|
2779
3147
|
if (sessionModal) sessionModal.addEventListener("click", function(e) {
|
|
2780
3148
|
if (e.target.id === "session-modal") closeSessionModal();
|
|
3149
|
+
if (e.target.id === "worktree-merge-modal") closeWorktreeMergeModal();
|
|
2781
3150
|
});
|
|
2782
3151
|
|
|
2783
3152
|
var inputBox = document.getElementById("input-box");
|
|
@@ -2821,15 +3190,8 @@
|
|
|
2821
3190
|
if (shortcutsToggleBtn) shortcutsToggleBtn.addEventListener("click", function(e) {
|
|
2822
3191
|
e.stopPropagation();
|
|
2823
3192
|
state.shortcutsExpanded = !state.shortcutsExpanded;
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
var row = document.querySelector(".inline-shortcuts-expanded-row");
|
|
2827
|
-
if (wrap) wrap.classList.toggle("expanded", state.shortcutsExpanded);
|
|
2828
|
-
if (row) row.classList.toggle("visible", state.shortcutsExpanded);
|
|
2829
|
-
if (toggle) {
|
|
2830
|
-
toggle.classList.toggle("active", state.shortcutsExpanded);
|
|
2831
|
-
toggle.textContent = state.shortcutsExpanded ? "\u203a" : "\u2039";
|
|
2832
|
-
}
|
|
3193
|
+
persistShortcutsExpandedState();
|
|
3194
|
+
updateCollapsedShortcutsUi();
|
|
2833
3195
|
});
|
|
2834
3196
|
// Close shortcuts strip on outside click
|
|
2835
3197
|
document.addEventListener("click", function(e) {
|
|
@@ -2839,13 +3201,8 @@
|
|
|
2839
3201
|
var clickedInsideRow = expandedRow && expandedRow.contains(e.target);
|
|
2840
3202
|
if (wrap && !wrap.contains(e.target) && !clickedInsideRow) {
|
|
2841
3203
|
state.shortcutsExpanded = false;
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
var toggle = document.querySelector(".shortcuts-toggle");
|
|
2845
|
-
if (toggle) {
|
|
2846
|
-
toggle.classList.remove("active");
|
|
2847
|
-
toggle.textContent = "\u2039";
|
|
2848
|
-
}
|
|
3204
|
+
persistShortcutsExpandedState();
|
|
3205
|
+
updateCollapsedShortcutsUi();
|
|
2849
3206
|
}
|
|
2850
3207
|
});
|
|
2851
3208
|
|
|
@@ -3324,6 +3681,7 @@
|
|
|
3324
3681
|
event.preventDefault();
|
|
3325
3682
|
event.stopPropagation();
|
|
3326
3683
|
state.claudeHistoryExpanded = !state.claudeHistoryExpanded;
|
|
3684
|
+
persistHistoryPanelState();
|
|
3327
3685
|
if (state.claudeHistoryExpanded && !state.claudeHistoryLoaded) {
|
|
3328
3686
|
loadClaudeHistory();
|
|
3329
3687
|
}
|
|
@@ -3371,6 +3729,10 @@
|
|
|
3371
3729
|
handleResumeAction(actionButton);
|
|
3372
3730
|
} else if (actionButton.dataset.action === "resume-history" && actionButton.dataset.claudeSessionId) {
|
|
3373
3731
|
handleResumeHistoryAction(actionButton);
|
|
3732
|
+
} else if (actionButton.dataset.action === "worktree-merge" && actionButton.dataset.sessionId) {
|
|
3733
|
+
openWorktreeMergeModal(actionButton.dataset.sessionId);
|
|
3734
|
+
} else if (actionButton.dataset.action === "worktree-cleanup" && actionButton.dataset.sessionId) {
|
|
3735
|
+
retryWorktreeCleanup(actionButton.dataset.sessionId);
|
|
3374
3736
|
}
|
|
3375
3737
|
return;
|
|
3376
3738
|
}
|
|
@@ -3991,7 +4353,7 @@
|
|
|
3991
4353
|
})
|
|
3992
4354
|
.then(function(res) { return res.json(); })
|
|
3993
4355
|
.then(function(config) {
|
|
3994
|
-
|
|
4356
|
+
applySettingsConfig(config);
|
|
3995
4357
|
var statusDot = document.getElementById("status-dot");
|
|
3996
4358
|
var statusText = document.getElementById("status-text");
|
|
3997
4359
|
if (statusDot) statusDot.classList.add("active");
|
|
@@ -4029,9 +4391,16 @@
|
|
|
4029
4391
|
state.sessions = [];
|
|
4030
4392
|
state.claudeHistory = [];
|
|
4031
4393
|
state.claudeHistoryLoaded = false;
|
|
4032
|
-
state.claudeHistoryExpanded =
|
|
4394
|
+
state.claudeHistoryExpanded = getConfiguredPanelDefaults().claudeHistoryExpanded;
|
|
4395
|
+
persistHistoryPanelState();
|
|
4033
4396
|
state.claudeHistoryExpandedDirs = {};
|
|
4034
|
-
state.sessionsDrawerOpen =
|
|
4397
|
+
state.sessionsDrawerOpen = getConfiguredPanelDefaults().sessionsDrawerOpen;
|
|
4398
|
+
persistDrawerState();
|
|
4399
|
+
state.filePanelOpen = getConfiguredPanelDefaults().filePanelOpen;
|
|
4400
|
+
persistFilePanelState();
|
|
4401
|
+
state.shortcutsExpanded = getConfiguredPanelDefaults().shortcutsExpanded;
|
|
4402
|
+
persistShortcutsExpandedState();
|
|
4403
|
+
updateCollapsedShortcutsUi();
|
|
4035
4404
|
render();
|
|
4036
4405
|
}
|
|
4037
4406
|
|
|
@@ -4697,11 +5066,10 @@
|
|
|
4697
5066
|
|
|
4698
5067
|
function toggleSessionsDrawer() {
|
|
4699
5068
|
state.sessionsDrawerOpen = !state.sessionsDrawerOpen;
|
|
5069
|
+
persistDrawerState();
|
|
4700
5070
|
if (state.sessionsDrawerOpen && isMobileLayout()) {
|
|
4701
5071
|
state.filePanelOpen = false;
|
|
4702
|
-
|
|
4703
|
-
localStorage.setItem("wand-file-panel-open", "false");
|
|
4704
|
-
} catch (e) {}
|
|
5072
|
+
persistFilePanelState();
|
|
4705
5073
|
}
|
|
4706
5074
|
updateLayoutState();
|
|
4707
5075
|
}
|
|
@@ -4710,6 +5078,7 @@
|
|
|
4710
5078
|
if (!state.sessionsDrawerOpen) return;
|
|
4711
5079
|
closeSwipedItem();
|
|
4712
5080
|
state.sessionsDrawerOpen = false;
|
|
5081
|
+
persistDrawerState();
|
|
4713
5082
|
updateLayoutState();
|
|
4714
5083
|
}
|
|
4715
5084
|
|
|
@@ -4795,9 +5164,166 @@
|
|
|
4795
5164
|
document.addEventListener("keydown", focusTrapHandler);
|
|
4796
5165
|
}
|
|
4797
5166
|
|
|
5167
|
+
function getActiveWorktreeMergeSession() {
|
|
5168
|
+
if (!state.activeWorktreeMergeSessionId) return null;
|
|
5169
|
+
return state.sessions.find(function(session) { return session.id === state.activeWorktreeMergeSessionId; }) || null;
|
|
5170
|
+
}
|
|
5171
|
+
|
|
5172
|
+
function renderWorktreeMergeContent() {
|
|
5173
|
+
var container = document.getElementById("worktree-merge-content");
|
|
5174
|
+
var confirmBtn = document.getElementById("worktree-merge-confirm-button");
|
|
5175
|
+
var errorEl = document.getElementById("worktree-merge-error");
|
|
5176
|
+
var session = getActiveWorktreeMergeSession();
|
|
5177
|
+
var result = state.worktreeMergeCheckResult;
|
|
5178
|
+
if (!container || !confirmBtn) return;
|
|
5179
|
+
if (!session || !session.worktree) {
|
|
5180
|
+
container.innerHTML = '<p class="field-hint">未找到可合并的 worktree 会话。</p>';
|
|
5181
|
+
confirmBtn.disabled = true;
|
|
5182
|
+
return;
|
|
5183
|
+
}
|
|
5184
|
+
if (errorEl) {
|
|
5185
|
+
if (state.worktreeMergeError) {
|
|
5186
|
+
showError(errorEl, state.worktreeMergeError);
|
|
5187
|
+
} else {
|
|
5188
|
+
hideError(errorEl);
|
|
5189
|
+
}
|
|
5190
|
+
}
|
|
5191
|
+
var rows = [
|
|
5192
|
+
'<div class="worktree-merge-row"><span>来源分支</span><strong>' + escapeHtml(session.worktree.branch || "-") + '</strong></div>',
|
|
5193
|
+
'<div class="worktree-merge-row"><span>工作目录</span><strong>' + escapeHtml(session.worktree.path || "-") + '</strong></div>'
|
|
5194
|
+
];
|
|
5195
|
+
if (result) {
|
|
5196
|
+
rows.push('<div class="worktree-merge-row"><span>目标分支</span><strong>' + escapeHtml(result.targetBranch || "-") + '</strong></div>');
|
|
5197
|
+
rows.push('<div class="worktree-merge-row"><span>待合并提交</span><strong>' + escapeHtml(String(result.aheadCount || 0)) + '</strong></div>');
|
|
5198
|
+
rows.push('<div class="worktree-merge-row"><span>未提交改动</span><strong>' + escapeHtml(result.hasUncommittedChanges ? "有" : "无") + '</strong></div>');
|
|
5199
|
+
rows.push('<div class="worktree-merge-row"><span>冲突风险</span><strong>' + escapeHtml(result.hasConflicts ? "有" : "无") + '</strong></div>');
|
|
5200
|
+
if (result.reason) {
|
|
5201
|
+
rows.push('<p class="field-hint">' + escapeHtml(result.reason) + '</p>');
|
|
5202
|
+
}
|
|
5203
|
+
} else if (state.worktreeMergeLoading) {
|
|
5204
|
+
rows.push('<p class="field-hint">正在检查 worktree 合并状态…</p>');
|
|
5205
|
+
}
|
|
5206
|
+
container.innerHTML = rows.join("");
|
|
5207
|
+
confirmBtn.disabled = state.worktreeMergeLoading || state.worktreeMergeSubmitting || !result || result.ok !== true;
|
|
5208
|
+
confirmBtn.textContent = state.worktreeMergeSubmitting ? "合并中..." : "确认合并并清理";
|
|
5209
|
+
}
|
|
5210
|
+
|
|
5211
|
+
function openWorktreeMergeModal(sessionId) {
|
|
5212
|
+
state.activeWorktreeMergeSessionId = sessionId;
|
|
5213
|
+
state.worktreeMergeCheckResult = null;
|
|
5214
|
+
state.worktreeMergeLoading = true;
|
|
5215
|
+
state.worktreeMergeSubmitting = false;
|
|
5216
|
+
state.worktreeMergeError = "";
|
|
5217
|
+
closeSessionModal();
|
|
5218
|
+
closeSettingsModal();
|
|
5219
|
+
var modal = document.getElementById("worktree-merge-modal");
|
|
5220
|
+
if (modal) {
|
|
5221
|
+
modal.classList.remove("hidden");
|
|
5222
|
+
lastFocusedElement = document.activeElement;
|
|
5223
|
+
setupFocusTrap(modal);
|
|
5224
|
+
}
|
|
5225
|
+
renderWorktreeMergeContent();
|
|
5226
|
+
fetch("/api/sessions/" + encodeURIComponent(sessionId) + "/worktree/merge/check", {
|
|
5227
|
+
method: "POST",
|
|
5228
|
+
credentials: "same-origin"
|
|
5229
|
+
})
|
|
5230
|
+
.then(function(res) { return res.json(); })
|
|
5231
|
+
.then(function(data) {
|
|
5232
|
+
if (data && data.error) {
|
|
5233
|
+
throw new Error(data.error);
|
|
5234
|
+
}
|
|
5235
|
+
if (data && data.session) {
|
|
5236
|
+
updateSessionSnapshot(data.session);
|
|
5237
|
+
}
|
|
5238
|
+
state.worktreeMergeCheckResult = data.result || null;
|
|
5239
|
+
state.worktreeMergeError = "";
|
|
5240
|
+
})
|
|
5241
|
+
.catch(function(error) {
|
|
5242
|
+
state.worktreeMergeError = (error && error.message) || "无法检查 worktree 合并状态。";
|
|
5243
|
+
})
|
|
5244
|
+
.finally(function() {
|
|
5245
|
+
state.worktreeMergeLoading = false;
|
|
5246
|
+
renderWorktreeMergeContent();
|
|
5247
|
+
});
|
|
5248
|
+
}
|
|
5249
|
+
|
|
5250
|
+
function closeWorktreeMergeModal() {
|
|
5251
|
+
var modal = document.getElementById("worktree-merge-modal");
|
|
5252
|
+
state.activeWorktreeMergeSessionId = null;
|
|
5253
|
+
state.worktreeMergeCheckResult = null;
|
|
5254
|
+
state.worktreeMergeLoading = false;
|
|
5255
|
+
state.worktreeMergeSubmitting = false;
|
|
5256
|
+
state.worktreeMergeError = "";
|
|
5257
|
+
if (modal) {
|
|
5258
|
+
modal.classList.add("hidden");
|
|
5259
|
+
}
|
|
5260
|
+
if (focusTrapHandler) {
|
|
5261
|
+
document.removeEventListener("keydown", focusTrapHandler);
|
|
5262
|
+
focusTrapHandler = null;
|
|
5263
|
+
}
|
|
5264
|
+
if (lastFocusedElement && typeof lastFocusedElement.focus === "function") {
|
|
5265
|
+
lastFocusedElement.focus();
|
|
5266
|
+
}
|
|
5267
|
+
}
|
|
5268
|
+
|
|
5269
|
+
function confirmWorktreeMerge() {
|
|
5270
|
+
if (!state.activeWorktreeMergeSessionId || state.worktreeMergeSubmitting) return;
|
|
5271
|
+
state.worktreeMergeSubmitting = true;
|
|
5272
|
+
state.worktreeMergeError = "";
|
|
5273
|
+
renderWorktreeMergeContent();
|
|
5274
|
+
fetch("/api/sessions/" + encodeURIComponent(state.activeWorktreeMergeSessionId) + "/worktree/merge", {
|
|
5275
|
+
method: "POST",
|
|
5276
|
+
credentials: "same-origin",
|
|
5277
|
+
headers: { "Content-Type": "application/json" },
|
|
5278
|
+
body: JSON.stringify({})
|
|
5279
|
+
})
|
|
5280
|
+
.then(function(res) { return res.json(); })
|
|
5281
|
+
.then(function(data) {
|
|
5282
|
+
if (data && data.error) {
|
|
5283
|
+
throw new Error(data.error);
|
|
5284
|
+
}
|
|
5285
|
+
if (data && data.session) {
|
|
5286
|
+
updateSessionSnapshot(data.session);
|
|
5287
|
+
}
|
|
5288
|
+
showToast("已合并到 " + escapeHtml((data.result && data.result.targetBranch) || "主分支") + ((data.result && data.result.cleanupDone === false) ? ",但工作树待清理。" : "。"), "info");
|
|
5289
|
+
closeWorktreeMergeModal();
|
|
5290
|
+
return refreshAll();
|
|
5291
|
+
})
|
|
5292
|
+
.catch(function(error) {
|
|
5293
|
+
state.worktreeMergeError = (error && error.message) || "无法合并 worktree。";
|
|
5294
|
+
renderWorktreeMergeContent();
|
|
5295
|
+
})
|
|
5296
|
+
.finally(function() {
|
|
5297
|
+
state.worktreeMergeSubmitting = false;
|
|
5298
|
+
renderWorktreeMergeContent();
|
|
5299
|
+
});
|
|
5300
|
+
}
|
|
5301
|
+
|
|
5302
|
+
function retryWorktreeCleanup(sessionId) {
|
|
5303
|
+
fetch("/api/sessions/" + encodeURIComponent(sessionId) + "/worktree/cleanup", {
|
|
5304
|
+
method: "POST",
|
|
5305
|
+
credentials: "same-origin"
|
|
5306
|
+
})
|
|
5307
|
+
.then(function(res) { return res.json(); })
|
|
5308
|
+
.then(function(data) {
|
|
5309
|
+
if (data && data.error) {
|
|
5310
|
+
throw new Error(data.error);
|
|
5311
|
+
}
|
|
5312
|
+
if (data && data.session) {
|
|
5313
|
+
updateSessionSnapshot(data.session);
|
|
5314
|
+
}
|
|
5315
|
+
showToast("已完成 worktree 清理。", "info");
|
|
5316
|
+
return refreshAll();
|
|
5317
|
+
})
|
|
5318
|
+
.catch(function(error) {
|
|
5319
|
+
showToast((error && error.message) || "无法清理 worktree。", "error");
|
|
5320
|
+
});
|
|
5321
|
+
}
|
|
5322
|
+
|
|
4798
5323
|
function openSettingsModal() {
|
|
4799
5324
|
// Close session modal first if open (mutual exclusion)
|
|
4800
5325
|
closeSessionModal();
|
|
5326
|
+
refreshSettingsModalUi();
|
|
4801
5327
|
var modal = document.getElementById("settings-modal");
|
|
4802
5328
|
if (modal) {
|
|
4803
5329
|
modal.classList.remove("hidden");
|
|
@@ -4808,9 +5334,8 @@
|
|
|
4808
5334
|
if (confirmEl) confirmEl.value = "";
|
|
4809
5335
|
hideSettingsMessages();
|
|
4810
5336
|
setupFocusTrap(modal);
|
|
4811
|
-
|
|
5337
|
+
bindSettingsModalEvents();
|
|
4812
5338
|
switchSettingsTab("about");
|
|
4813
|
-
// Load settings data
|
|
4814
5339
|
loadSettingsData();
|
|
4815
5340
|
}
|
|
4816
5341
|
}
|
|
@@ -4901,62 +5426,120 @@
|
|
|
4901
5426
|
panels[j].classList.remove("active");
|
|
4902
5427
|
}
|
|
4903
5428
|
}
|
|
5429
|
+
updateSettingsActiveNav();
|
|
5430
|
+
}
|
|
5431
|
+
|
|
5432
|
+
function refreshSettingsGeneralPanel() {
|
|
5433
|
+
var existingPanel = document.getElementById("settings-tab-general");
|
|
5434
|
+
if (!existingPanel) return;
|
|
5435
|
+
var wrapper = document.createElement("div");
|
|
5436
|
+
wrapper.innerHTML = buildSettingsGeneralPanel();
|
|
5437
|
+
var nextPanel = wrapper.firstChild;
|
|
5438
|
+
if (!nextPanel) return;
|
|
5439
|
+
existingPanel.replaceWith(nextPanel);
|
|
5440
|
+
var saveConfigBtn = document.getElementById("save-config-button");
|
|
5441
|
+
if (saveConfigBtn) saveConfigBtn.addEventListener("click", saveConfigSettings);
|
|
5442
|
+
syncPanelStateSettingsForm();
|
|
5443
|
+
}
|
|
5444
|
+
|
|
5445
|
+
function bindSettingsTabEvents() {
|
|
5446
|
+
var settingsTabs = document.querySelectorAll(".settings-tab");
|
|
5447
|
+
for (var ti = 0; ti < settingsTabs.length; ti++) {
|
|
5448
|
+
settingsTabs[ti].addEventListener("click", function(e) {
|
|
5449
|
+
switchSettingsTab(e.target.getAttribute("data-tab"));
|
|
5450
|
+
});
|
|
5451
|
+
}
|
|
5452
|
+
}
|
|
5453
|
+
|
|
5454
|
+
function bindSettingsConfigActions() {
|
|
5455
|
+
var saveConfigBtn = document.getElementById("save-config-button");
|
|
5456
|
+
if (saveConfigBtn) saveConfigBtn.addEventListener("click", saveConfigSettings);
|
|
5457
|
+
}
|
|
5458
|
+
|
|
5459
|
+
function bindSettingsModalEvents() {
|
|
5460
|
+
bindSettingsTabEvents();
|
|
5461
|
+
bindSettingsConfigActions();
|
|
5462
|
+
}
|
|
5463
|
+
|
|
5464
|
+
function refreshSettingsModalUi() {
|
|
5465
|
+
refreshSettingsGeneralPanel();
|
|
5466
|
+
updateSettingsActiveNav();
|
|
5467
|
+
}
|
|
5468
|
+
|
|
5469
|
+
function normalizePanelStateSettings(cfg) {
|
|
5470
|
+
var panelState = cfg && cfg.uiPreferences && cfg.uiPreferences.defaultPanelState;
|
|
5471
|
+
var defaults = getConfiguredPanelDefaults(cfg);
|
|
5472
|
+
return {
|
|
5473
|
+
sessionsDrawerOpen: panelState && typeof panelState.sessionsDrawerOpen === "boolean" ? panelState.sessionsDrawerOpen : defaults.sessionsDrawerOpen,
|
|
5474
|
+
filePanelOpen: panelState && typeof panelState.filePanelOpen === "boolean" ? panelState.filePanelOpen : defaults.filePanelOpen,
|
|
5475
|
+
shortcutsExpanded: panelState && typeof panelState.shortcutsExpanded === "boolean" ? panelState.shortcutsExpanded : defaults.shortcutsExpanded,
|
|
5476
|
+
claudeHistoryExpanded: panelState && typeof panelState.claudeHistoryExpanded === "boolean" ? panelState.claudeHistoryExpanded : defaults.claudeHistoryExpanded,
|
|
5477
|
+
chatMessageExpanded: panelState && typeof panelState.chatMessageExpanded === "boolean" ? panelState.chatMessageExpanded : defaults.chatMessageExpanded,
|
|
5478
|
+
structuredThinkingExpanded: panelState && typeof panelState.structuredThinkingExpanded === "boolean" ? panelState.structuredThinkingExpanded : defaults.structuredThinkingExpanded,
|
|
5479
|
+
structuredToolGroupExpanded: panelState && typeof panelState.structuredToolGroupExpanded === "boolean" ? panelState.structuredToolGroupExpanded : defaults.structuredToolGroupExpanded,
|
|
5480
|
+
structuredInlineToolExpanded: panelState && typeof panelState.structuredInlineToolExpanded === "boolean" ? panelState.structuredInlineToolExpanded : defaults.structuredInlineToolExpanded,
|
|
5481
|
+
structuredTerminalExpanded: panelState && typeof panelState.structuredTerminalExpanded === "boolean" ? panelState.structuredTerminalExpanded : defaults.structuredTerminalExpanded,
|
|
5482
|
+
structuredToolCardExpanded: panelState && typeof panelState.structuredToolCardExpanded === "boolean" ? panelState.structuredToolCardExpanded : defaults.structuredToolCardExpanded,
|
|
5483
|
+
};
|
|
5484
|
+
}
|
|
5485
|
+
|
|
5486
|
+
function applyLoadedSettingsData(data) {
|
|
5487
|
+
var cfg = data.config || {};
|
|
5488
|
+
applySettingsConfig(cfg);
|
|
5489
|
+
|
|
5490
|
+
var nameEl = document.getElementById("settings-pkg-name");
|
|
5491
|
+
var verEl = document.getElementById("settings-version");
|
|
5492
|
+
var nodeEl = document.getElementById("settings-node-req");
|
|
5493
|
+
var repoEl = document.getElementById("settings-repo-url");
|
|
5494
|
+
if (nameEl) nameEl.textContent = data.packageName || "-";
|
|
5495
|
+
if (verEl) verEl.textContent = data.version || "-";
|
|
5496
|
+
if (nodeEl) nodeEl.textContent = data.nodeVersion || "-";
|
|
5497
|
+
if (repoEl && data.repoUrl) {
|
|
5498
|
+
repoEl.innerHTML = '<a href="' + escapeHtml(data.repoUrl) + '" target="_blank" rel="noopener">' + escapeHtml(data.repoUrl) + '</a>';
|
|
5499
|
+
}
|
|
5500
|
+
|
|
5501
|
+
var hostEl = document.getElementById("cfg-host");
|
|
5502
|
+
var portEl = document.getElementById("cfg-port");
|
|
5503
|
+
var httpsEl = document.getElementById("cfg-https");
|
|
5504
|
+
var modeEl = document.getElementById("cfg-mode");
|
|
5505
|
+
var cwdEl = document.getElementById("cfg-cwd");
|
|
5506
|
+
var shellEl = document.getElementById("cfg-shell");
|
|
5507
|
+
if (hostEl) hostEl.value = cfg.host || "";
|
|
5508
|
+
if (portEl) portEl.value = cfg.port || "";
|
|
5509
|
+
if (httpsEl) httpsEl.checked = cfg.https === true;
|
|
5510
|
+
if (modeEl) modeEl.value = cfg.defaultMode || "default";
|
|
5511
|
+
if (cwdEl) cwdEl.value = cfg.defaultCwd || "";
|
|
5512
|
+
if (shellEl) shellEl.value = cfg.shell || "";
|
|
5513
|
+
var langEl = document.getElementById("cfg-language");
|
|
5514
|
+
if (langEl) langEl.value = cfg.language || "";
|
|
5515
|
+
syncPanelStateSettingsForm(normalizePanelStateSettings(cfg));
|
|
5516
|
+
|
|
5517
|
+
var certStatus = document.getElementById("cert-status");
|
|
5518
|
+
if (certStatus) {
|
|
5519
|
+
certStatus.textContent = data.hasCert ? "已安装 SSL 证书" : "未安装证书(使用自签名或 HTTP)";
|
|
5520
|
+
certStatus.style.color = data.hasCert ? "var(--success)" : "var(--text-secondary)";
|
|
5521
|
+
}
|
|
5522
|
+
|
|
5523
|
+
var presetsList = document.getElementById("presets-list");
|
|
5524
|
+
if (presetsList && cfg.commandPresets) {
|
|
5525
|
+
var html = "";
|
|
5526
|
+
for (var i = 0; i < cfg.commandPresets.length; i++) {
|
|
5527
|
+
var p = cfg.commandPresets[i];
|
|
5528
|
+
html += '<div class="preset-item">' +
|
|
5529
|
+
'<span class="preset-label">' + escapeHtml(p.label) + '</span>' +
|
|
5530
|
+
'<span class="preset-detail">' + escapeHtml(p.command) + (p.mode ? ' (' + escapeHtml(p.mode) + ')' : '') + '</span>' +
|
|
5531
|
+
'</div>';
|
|
5532
|
+
}
|
|
5533
|
+
if (!html) html = '<div class="empty-state-compact"><span class="empty-icon">\u2699</span><span>没有命令预设</span><span class="hint">在 config.json 的 commandPresets 中配置</span></div>';
|
|
5534
|
+
presetsList.innerHTML = html;
|
|
5535
|
+
}
|
|
4904
5536
|
}
|
|
4905
5537
|
|
|
4906
5538
|
function loadSettingsData() {
|
|
4907
5539
|
fetch("/api/settings", { credentials: "same-origin" })
|
|
4908
5540
|
.then(function(res) { return res.json(); })
|
|
4909
5541
|
.then(function(data) {
|
|
4910
|
-
|
|
4911
|
-
var nameEl = document.getElementById("settings-pkg-name");
|
|
4912
|
-
var verEl = document.getElementById("settings-version");
|
|
4913
|
-
var nodeEl = document.getElementById("settings-node-req");
|
|
4914
|
-
var repoEl = document.getElementById("settings-repo-url");
|
|
4915
|
-
if (nameEl) nameEl.textContent = data.packageName || "-";
|
|
4916
|
-
if (verEl) verEl.textContent = data.version || "-";
|
|
4917
|
-
if (nodeEl) nodeEl.textContent = data.nodeVersion || "-";
|
|
4918
|
-
if (repoEl && data.repoUrl) {
|
|
4919
|
-
repoEl.innerHTML = '<a href="' + escapeHtml(data.repoUrl) + '" target="_blank" rel="noopener">' + escapeHtml(data.repoUrl) + '</a>';
|
|
4920
|
-
}
|
|
4921
|
-
|
|
4922
|
-
// Config fields
|
|
4923
|
-
var cfg = data.config || {};
|
|
4924
|
-
var hostEl = document.getElementById("cfg-host");
|
|
4925
|
-
var portEl = document.getElementById("cfg-port");
|
|
4926
|
-
var httpsEl = document.getElementById("cfg-https");
|
|
4927
|
-
var modeEl = document.getElementById("cfg-mode");
|
|
4928
|
-
var cwdEl = document.getElementById("cfg-cwd");
|
|
4929
|
-
var shellEl = document.getElementById("cfg-shell");
|
|
4930
|
-
if (hostEl) hostEl.value = cfg.host || "";
|
|
4931
|
-
if (portEl) portEl.value = cfg.port || "";
|
|
4932
|
-
if (httpsEl) httpsEl.checked = cfg.https === true;
|
|
4933
|
-
if (modeEl) modeEl.value = cfg.defaultMode || "default";
|
|
4934
|
-
if (cwdEl) cwdEl.value = cfg.defaultCwd || "";
|
|
4935
|
-
if (shellEl) shellEl.value = cfg.shell || "";
|
|
4936
|
-
var langEl = document.getElementById("cfg-language");
|
|
4937
|
-
if (langEl) langEl.value = cfg.language || "";
|
|
4938
|
-
|
|
4939
|
-
// Cert status
|
|
4940
|
-
var certStatus = document.getElementById("cert-status");
|
|
4941
|
-
if (certStatus) {
|
|
4942
|
-
certStatus.textContent = data.hasCert ? "已安装 SSL 证书" : "未安装证书(使用自签名或 HTTP)";
|
|
4943
|
-
certStatus.style.color = data.hasCert ? "var(--success)" : "var(--text-secondary)";
|
|
4944
|
-
}
|
|
4945
|
-
|
|
4946
|
-
// Presets
|
|
4947
|
-
var presetsList = document.getElementById("presets-list");
|
|
4948
|
-
if (presetsList && cfg.commandPresets) {
|
|
4949
|
-
var html = "";
|
|
4950
|
-
for (var i = 0; i < cfg.commandPresets.length; i++) {
|
|
4951
|
-
var p = cfg.commandPresets[i];
|
|
4952
|
-
html += '<div class="preset-item">' +
|
|
4953
|
-
'<span class="preset-label">' + escapeHtml(p.label) + '</span>' +
|
|
4954
|
-
'<span class="preset-detail">' + escapeHtml(p.command) + (p.mode ? ' (' + escapeHtml(p.mode) + ')' : '') + '</span>' +
|
|
4955
|
-
'</div>';
|
|
4956
|
-
}
|
|
4957
|
-
if (!html) html = '<div class="empty-state-compact"><span class="empty-icon">\u2699</span><span>\u6ca1\u6709\u547d\u4ee4\u9884\u8bbe</span><span class="hint">\u5728 config.json \u7684 commandPresets \u4e2d\u914d\u7f6e</span></div>';
|
|
4958
|
-
presetsList.innerHTML = html;
|
|
4959
|
-
}
|
|
5542
|
+
applyLoadedSettingsData(data);
|
|
4960
5543
|
})
|
|
4961
5544
|
.catch(function() {});
|
|
4962
5545
|
}
|
|
@@ -4973,6 +5556,9 @@
|
|
|
4973
5556
|
defaultCwd: (document.getElementById("cfg-cwd") || {}).value,
|
|
4974
5557
|
shell: (document.getElementById("cfg-shell") || {}).value,
|
|
4975
5558
|
language: (document.getElementById("cfg-language") || {}).value || "",
|
|
5559
|
+
uiPreferences: {
|
|
5560
|
+
defaultPanelState: getPanelStateSettingsFormValues(),
|
|
5561
|
+
}
|
|
4976
5562
|
};
|
|
4977
5563
|
|
|
4978
5564
|
fetch("/api/settings/config", {
|
|
@@ -4990,6 +5576,7 @@
|
|
|
4990
5576
|
} else {
|
|
4991
5577
|
msgEl.textContent = "配置已保存,部分更改需要重启后生效。";
|
|
4992
5578
|
msgEl.style.color = "var(--success)";
|
|
5579
|
+
handleSettingsConfigSaved(data.config);
|
|
4993
5580
|
}
|
|
4994
5581
|
msgEl.classList.remove("hidden");
|
|
4995
5582
|
}
|
|
@@ -5003,6 +5590,7 @@
|
|
|
5003
5590
|
});
|
|
5004
5591
|
}
|
|
5005
5592
|
|
|
5593
|
+
|
|
5006
5594
|
function uploadCertificates() {
|
|
5007
5595
|
var keyFile = document.getElementById("cert-key-file");
|
|
5008
5596
|
var certFile = document.getElementById("cert-cert-file");
|
|
@@ -9281,12 +9869,12 @@
|
|
|
9281
9869
|
if (cards.length > 0) {
|
|
9282
9870
|
var firstCard = cards[0];
|
|
9283
9871
|
var firstCardKey = getElementExpandKey(firstCard);
|
|
9284
|
-
if (
|
|
9872
|
+
if (!hasPersistedExpandState(firstCardKey) && !getConfiguredPanelDefaults().structuredToolCardExpanded) {
|
|
9285
9873
|
firstCard.classList.remove("collapsed");
|
|
9286
9874
|
}
|
|
9287
9875
|
for (var ci = 1; ci < cards.length; ci++) {
|
|
9288
9876
|
var cardKey = getElementExpandKey(cards[ci]);
|
|
9289
|
-
if (
|
|
9877
|
+
if (!hasPersistedExpandState(cardKey) && !getConfiguredPanelDefaults().structuredToolCardExpanded) {
|
|
9290
9878
|
cards[ci].classList.add("collapsed");
|
|
9291
9879
|
}
|
|
9292
9880
|
}
|
|
@@ -9304,7 +9892,7 @@
|
|
|
9304
9892
|
var allCards = container.querySelectorAll(".tool-use-card");
|
|
9305
9893
|
allCards.forEach(function(c) {
|
|
9306
9894
|
var cardKey = getElementExpandKey(c);
|
|
9307
|
-
if (
|
|
9895
|
+
if (hasPersistedExpandState(cardKey) || getConfiguredPanelDefaults().structuredToolCardExpanded) return;
|
|
9308
9896
|
// Keep expanded if this card is inside a newly added message
|
|
9309
9897
|
if (newEls) {
|
|
9310
9898
|
for (var i = 0; i < newEls.length; i++) {
|
|
@@ -9428,7 +10016,7 @@
|
|
|
9428
10016
|
var newestCard = null;
|
|
9429
10017
|
allCards.forEach(function(c) {
|
|
9430
10018
|
var cardKey = getElementExpandKey(c);
|
|
9431
|
-
if (
|
|
10019
|
+
if (hasPersistedExpandState(cardKey) || getConfiguredPanelDefaults().structuredToolCardExpanded) return;
|
|
9432
10020
|
if (newestMsgEl && newestMsgEl.contains(c)) {
|
|
9433
10021
|
if (!newestCard) newestCard = c;
|
|
9434
10022
|
else c.classList.add("collapsed");
|
|
@@ -10518,7 +11106,7 @@
|
|
|
10518
11106
|
// Thinking card (deep thought) — from PTY parsing
|
|
10519
11107
|
if (msg.role === "thinking") {
|
|
10520
11108
|
var thinkingKey = buildExpandKey("thinking", [getMessageKey(msg, messageIndex), "pty"]);
|
|
10521
|
-
var thinkingExpanded =
|
|
11109
|
+
var thinkingExpanded = getExpandState(thinkingKey, "thinking");
|
|
10522
11110
|
return '<div class="chat-message thinking">' +
|
|
10523
11111
|
'<div class="thinking-inline thinking-pty ' + (thinkingExpanded ? 'expanded' : 'collapsed') + '" data-expand-kind="thinking" data-expand-key="' + escapeHtml(thinkingKey) + '" data-thinking="" onclick="__thinkingToggle(this)">' +
|
|
10524
11112
|
'<span class="thinking-inline-icon">⦿</span>' +
|
|
@@ -10655,8 +11243,7 @@
|
|
|
10655
11243
|
}
|
|
10656
11244
|
var summaryText = parts.join(" · ");
|
|
10657
11245
|
var groupKey = buildExpandKey("tool-group", [messageKey, items[0] && items[0].index, items.length]);
|
|
10658
|
-
var
|
|
10659
|
-
var shouldExpand = persistedExpanded === null ? false : persistedExpanded;
|
|
11246
|
+
var shouldExpand = getExpandState(groupKey, "tool-group");
|
|
10660
11247
|
|
|
10661
11248
|
// Render each item's inline-tool card
|
|
10662
11249
|
var innerHtml = "";
|
|
@@ -10767,7 +11354,7 @@
|
|
|
10767
11354
|
'</div>';
|
|
10768
11355
|
}
|
|
10769
11356
|
var thinkingKey = buildExpandKey("thinking", [messageKey, index]);
|
|
10770
|
-
var thinkingExpanded =
|
|
11357
|
+
var thinkingExpanded = getExpandState(thinkingKey, "thinking");
|
|
10771
11358
|
return '<div class="thinking-inline ' + (thinkingExpanded ? 'expanded' : 'collapsed') + '" data-expand-kind="thinking" data-expand-key="' + escapeHtml(thinkingKey) + '" data-thinking="' + escapeHtml(thinkingText) + '" onclick="__thinkingToggle(this)">' +
|
|
10772
11359
|
'<span class="thinking-inline-icon">⦿</span>' +
|
|
10773
11360
|
'<span class="thinking-inline-preview">' + escapeHtml(thinkingExpanded ? thinkingText : preview) + '</span>' +
|
|
@@ -10793,7 +11380,6 @@
|
|
|
10793
11380
|
function renderInlineTool(block, toolResult, toolName, fileInfo, extraInfo, messageKey, index) {
|
|
10794
11381
|
var toolId = block.id || "tool-" + toolName;
|
|
10795
11382
|
var expandKey = buildExpandKey("inline-tool", [messageKey, toolId || index, index]);
|
|
10796
|
-
var persistedExpanded = getPersistedExpandState(expandKey);
|
|
10797
11383
|
var inputData = block.input || {};
|
|
10798
11384
|
var resultContent = extractToolResultText(toolResult && toolResult.content);
|
|
10799
11385
|
|
|
@@ -10864,7 +11450,7 @@
|
|
|
10864
11450
|
var fullResult = resultContent;
|
|
10865
11451
|
|
|
10866
11452
|
var expandedHtml = "";
|
|
10867
|
-
var shouldExpand =
|
|
11453
|
+
var shouldExpand = getExpandState(expandKey, "inline-tool");
|
|
10868
11454
|
if (hasResult) {
|
|
10869
11455
|
expandedHtml = '<div class="inline-tool-expanded" style="display: ' + (shouldExpand ? 'block' : 'none') + ';">' +
|
|
10870
11456
|
'<div class="inline-tool-result">' + formatInlineResult(resultContent, toolName) + '</div>' +
|
|
@@ -10904,7 +11490,6 @@
|
|
|
10904
11490
|
var resultContent = extractToolResultText(toolResult && toolResult.content);
|
|
10905
11491
|
var toolId = block.id || "tool-" + toolName;
|
|
10906
11492
|
var expandKey = buildExpandKey("terminal", [messageKey, toolId || index, index]);
|
|
10907
|
-
var persistedExpanded = getPersistedExpandState(expandKey);
|
|
10908
11493
|
|
|
10909
11494
|
var isError = toolResult && toolResult.is_error;
|
|
10910
11495
|
var exitCode = inputData.exitCode;
|
|
@@ -10942,7 +11527,7 @@
|
|
|
10942
11527
|
|
|
10943
11528
|
// Show command preview in header (truncate long commands)
|
|
10944
11529
|
var cmdPreview = command.length > 80 ? command.slice(0, 77) + "…" : command;
|
|
10945
|
-
var shouldExpand =
|
|
11530
|
+
var shouldExpand = getExpandState(expandKey, "terminal");
|
|
10946
11531
|
|
|
10947
11532
|
return '<div class="inline-terminal" data-expand-kind="terminal" data-expand-key="' + escapeHtml(expandKey) + '" data-expanded="' + (shouldExpand ? 'true' : 'false') + '">' +
|
|
10948
11533
|
'<div class="term-header" onclick="__terminalExpand(this)">' +
|
|
@@ -11140,8 +11725,7 @@
|
|
|
11140
11725
|
}
|
|
11141
11726
|
|
|
11142
11727
|
var expandKey = buildExpandKey("tool-card", [messageKey, toolId]);
|
|
11143
|
-
var
|
|
11144
|
-
var shouldExpand = persistedExpanded === null ? statusClass === "loading" : persistedExpanded;
|
|
11728
|
+
var shouldExpand = getExpandState(expandKey, "tool-card", statusClass === "loading");
|
|
11145
11729
|
var collapsedClass = shouldExpand ? "" : " collapsed";
|
|
11146
11730
|
var toggleHtml = '<span class="tool-use-toggle">▼</span>';
|
|
11147
11731
|
return '<div class="tool-use-card ' + statusClass + collapsedClass + '" data-expand-kind="tool-card" data-expand-key="' + escapeHtml(expandKey) + '" data-tool-use-id="' + escapeHtml(toolId) + '">' +
|