@co0ontty/wand 1.32.0 → 1.32.1
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/web-ui/content/scripts.js +175 -109
- package/dist/web-ui/content/styles.css +190 -89
- package/package.json +1 -1
|
@@ -80,6 +80,33 @@
|
|
|
80
80
|
var configPath = "${escapeHtml(configPath)}";
|
|
81
81
|
var CHAT_EXPAND_STATE_STORAGE_KEY = "wand-chat-expand-state-v1";
|
|
82
82
|
|
|
83
|
+
// ===== 一次性 localStorage 迁移 =====
|
|
84
|
+
// 用 schema 版本号确保每个 migration 只跑一次。每加一项就 ++LS_SCHEMA_VERSION
|
|
85
|
+
// 并在 LS_MIGRATIONS append 一个函数。已升级用户的 wand-ls-schema 大于等于
|
|
86
|
+
// 当前长度时整段跳过;新用户首次加载会一口气把所有 migration 都跑完再写
|
|
87
|
+
// schema 号 —— 因此每个 migration 函数对「key 不存在」的输入也必须是无害的。
|
|
88
|
+
var LS_MIGRATIONS = [
|
|
89
|
+
// v1(2026-05)取消独立的「图钉」按钮,呼出侧栏即常驻。旧版残留的
|
|
90
|
+
// wand-sidebar-pinned=false 会让老用户继续走 drawer 模式看不到新行为,
|
|
91
|
+
// 这里直接清掉,让 state 初始化回退到默认 true。
|
|
92
|
+
function migrateSidebarPinDefault() {
|
|
93
|
+
try { localStorage.removeItem("wand-sidebar-pinned"); } catch (e) {}
|
|
94
|
+
}
|
|
95
|
+
];
|
|
96
|
+
(function runLocalStorageMigrations() {
|
|
97
|
+
try {
|
|
98
|
+
var raw = localStorage.getItem("wand-ls-schema");
|
|
99
|
+
var applied = raw == null ? 0 : parseInt(raw, 10);
|
|
100
|
+
if (!(applied >= 0)) applied = 0;
|
|
101
|
+
for (var i = applied; i < LS_MIGRATIONS.length; i++) {
|
|
102
|
+
try { LS_MIGRATIONS[i](); } catch (e) {}
|
|
103
|
+
}
|
|
104
|
+
if (applied < LS_MIGRATIONS.length) {
|
|
105
|
+
localStorage.setItem("wand-ls-schema", String(LS_MIGRATIONS.length));
|
|
106
|
+
}
|
|
107
|
+
} catch (e) { /* localStorage 不可用就跳过,按默认行为运行 */ }
|
|
108
|
+
})();
|
|
109
|
+
|
|
83
110
|
var state = {
|
|
84
111
|
selectedId: (function() {
|
|
85
112
|
try { return localStorage.getItem("wand-selected-session") || null; } catch (e) { return null; }
|
|
@@ -154,7 +181,12 @@
|
|
|
154
181
|
bootstrapping: true,
|
|
155
182
|
sessionsDrawerOpen: false,
|
|
156
183
|
sidebarPinned: (function() {
|
|
157
|
-
|
|
184
|
+
// 新交互:桌面默认呼出即常驻;只有用户主动 X 关闭过才记 "false"。
|
|
185
|
+
// 老用户的旧值("true"/"false")继续生效,没存过 key 时回退到 true。
|
|
186
|
+
try {
|
|
187
|
+
var v = localStorage.getItem("wand-sidebar-pinned");
|
|
188
|
+
return v === null ? true : v !== "false";
|
|
189
|
+
} catch (e) { return true; }
|
|
158
190
|
})(),
|
|
159
191
|
sidebarCollapsed: (function() {
|
|
160
192
|
try { return localStorage.getItem("wand-sidebar-collapsed") === "true"; } catch (e) { return false; }
|
|
@@ -1380,6 +1412,36 @@
|
|
|
1380
1412
|
});
|
|
1381
1413
|
}
|
|
1382
1414
|
|
|
1415
|
+
// ===== 桌面:点 sidebar 外的空白处自动收起 =====
|
|
1416
|
+
// 旧版 drawer 模式下点 backdrop 关闭的便利性,在「呼出即常驻」之后用
|
|
1417
|
+
// document 级捕获 handler 续上。
|
|
1418
|
+
// - 仅 desktop + 全尺寸(非窄条)+ 已打开 时生效
|
|
1419
|
+
// - 窄条态不触发(窄条本来就是稳定常驻形态)
|
|
1420
|
+
// - 手机端由 .drawer-backdrop 元素自己接住点击,不在这里重复处理
|
|
1421
|
+
// - 各类弹层(modal / topbar-more / overflow 菜单 / 文件夹下拉等)不算
|
|
1422
|
+
// 「sidebar 外的空白」,否则点弹层会顺带把 sidebar 关掉
|
|
1423
|
+
// 用 capture 阶段是为了绕过下游按钮自己的 stopPropagation。
|
|
1424
|
+
document.addEventListener("click", function(e) {
|
|
1425
|
+
if (isMobileLayout()) return;
|
|
1426
|
+
if (!state.sidebarPinned) return;
|
|
1427
|
+
if (state.sidebarCollapsed) return;
|
|
1428
|
+
if (!state.sessionsDrawerOpen) return;
|
|
1429
|
+
var target = e.target;
|
|
1430
|
+
if (!target || !(target instanceof Element)) return;
|
|
1431
|
+
if (target.closest("#sessions-drawer")) return;
|
|
1432
|
+
if (target.closest("#sessions-toggle-button")) return;
|
|
1433
|
+
if (target.closest(".floating-sidebar-toggle")) return;
|
|
1434
|
+
if (target.closest(".sidebar-tile-bubble")) return;
|
|
1435
|
+
if (target.closest(
|
|
1436
|
+
".modal-backdrop, .modal-overlay, .modal-container, " +
|
|
1437
|
+
"[role='dialog'], [role='menu'], " +
|
|
1438
|
+
".topbar-more-menu, .sidebar-header-overflow, " +
|
|
1439
|
+
".folder-picker-dropdown, .path-suggestions, " +
|
|
1440
|
+
".permission-prompt-overlay, .restart-overlay"
|
|
1441
|
+
)) return;
|
|
1442
|
+
closeSessionsDrawer();
|
|
1443
|
+
}, true);
|
|
1444
|
+
|
|
1383
1445
|
renderBootLoading();
|
|
1384
1446
|
restoreLoginSession();
|
|
1385
1447
|
|
|
@@ -1604,9 +1666,6 @@
|
|
|
1604
1666
|
'</button>' +
|
|
1605
1667
|
'</div>' +
|
|
1606
1668
|
'</div>' +
|
|
1607
|
-
'<button id="sidebar-pin-btn" class="btn btn-ghost btn-sm sidebar-pin-toggle' + (state.sidebarPinned ? ' pinned' : '') + '" type="button" title="' + (state.sidebarPinned ? '取消固定侧栏' : '固定侧栏') + '">' +
|
|
1608
|
-
'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="17" x2="12" y2="22"/><path d="M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24z"/></svg>' +
|
|
1609
|
-
'</button>' +
|
|
1610
1669
|
'<button id="sidebar-collapse-btn" class="btn btn-ghost btn-sm sidebar-collapse-toggle' + (isCollapsed ? ' collapsed' : '') + '" type="button" title="' + (isCollapsed ? '展开侧栏' : '收起为窄条') + '" aria-label="' + (isCollapsed ? '展开侧栏' : '收起为窄条') + '">' +
|
|
1611
1670
|
(isCollapsed
|
|
1612
1671
|
? '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="10 6 16 12 10 18"/><line x1="20" y1="5" x2="20" y2="19"/></svg>'
|
|
@@ -1841,13 +1900,8 @@
|
|
|
1841
1900
|
'<button id="stop-button" class="btn-circle btn-circle-stop' + (state.selectedId ? "" : " hidden") + '" title="停止">' +
|
|
1842
1901
|
'<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor"><rect x="3" y="3" width="10" height="10" rx="2"/></svg>' +
|
|
1843
1902
|
'</button>' +
|
|
1844
|
-
//
|
|
1845
|
-
//
|
|
1846
|
-
// 用 pill 形态 + 文字 + 脉动,让用户一眼就看到「立即发送」这条快捷路径。
|
|
1847
|
-
'<button id="interrupt-send-button" class="btn-pill btn-pill-interrupt hidden" type="button" title="中断当前回复并立即发送新输入(Cmd/Ctrl+Enter)" aria-label="立即发送">' +
|
|
1848
|
-
'<svg class="btn-pill-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="13 17 18 12 13 7"/><polyline points="6 17 11 12 6 7"/></svg>' +
|
|
1849
|
-
'<span class="btn-pill-label">立即</span>' +
|
|
1850
|
-
'</button>' +
|
|
1903
|
+
// 「立即发送」按钮已下线 —— 默认行为永远是排队(气泡),想插队
|
|
1904
|
+
// 请点输入框上方那条气泡(chip)。Cmd/Ctrl+Enter 快捷键仍保留。
|
|
1851
1905
|
'<button id="send-input-button" class="btn-circle btn-circle-send" title="发送">' +
|
|
1852
1906
|
'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>' +
|
|
1853
1907
|
'</button>' +
|
|
@@ -5823,8 +5877,6 @@
|
|
|
5823
5877
|
if (drawerBackdrop) drawerBackdrop.addEventListener("click", closeSessionsDrawer);
|
|
5824
5878
|
var closeDrawerBtn = document.getElementById("close-drawer-button");
|
|
5825
5879
|
if (closeDrawerBtn) closeDrawerBtn.addEventListener("click", closeSessionsDrawer);
|
|
5826
|
-
var pinBtn = document.getElementById("sidebar-pin-btn");
|
|
5827
|
-
if (pinBtn) pinBtn.addEventListener("click", toggleSidebarPin);
|
|
5828
5880
|
var collapseBtn = document.getElementById("sidebar-collapse-btn");
|
|
5829
5881
|
if (collapseBtn) collapseBtn.addEventListener("click", toggleSidebarCollapsed);
|
|
5830
5882
|
var sidebarMoreBtn = document.getElementById("sidebar-more-btn");
|
|
@@ -6092,11 +6144,6 @@
|
|
|
6092
6144
|
closeSessionsDrawer();
|
|
6093
6145
|
sendOrStart();
|
|
6094
6146
|
});
|
|
6095
|
-
var interruptSendBtn = document.getElementById("interrupt-send-button");
|
|
6096
|
-
if (interruptSendBtn) interruptSendBtn.addEventListener("click", function() {
|
|
6097
|
-
closeSessionsDrawer();
|
|
6098
|
-
sendOrStart({ interrupt: true });
|
|
6099
|
-
});
|
|
6100
6147
|
var stopBtn = document.getElementById("stop-button");
|
|
6101
6148
|
if (stopBtn) stopBtn.addEventListener("click", stopSession);
|
|
6102
6149
|
var modeSelect = document.getElementById("chat-mode-select");
|
|
@@ -8159,14 +8206,17 @@
|
|
|
8159
8206
|
// Keep placeholders short so they don't wrap on portrait mobile screens.
|
|
8160
8207
|
// Only show informative state hints; drop the redundant "send to X" labels.
|
|
8161
8208
|
if (terminalInteractive) return "终端交互中";
|
|
8162
|
-
|
|
8209
|
+
// 只有真正进入终止态(exited / failed / stopped)才提示"会话已结束"。
|
|
8210
|
+
// 结构化会话刚创建或一次回复结束后会回到 "idle"——那是等待下一条输入的
|
|
8211
|
+
// 正常状态,不应该被当成结束。
|
|
8212
|
+
if (session && (session.status === "exited" || session.status === "failed" || session.status === "stopped")) {
|
|
8163
8213
|
if (canAutoResumeSession(session)) return "";
|
|
8164
8214
|
return "会话已结束";
|
|
8165
8215
|
}
|
|
8166
8216
|
// 结构化会话在出 token 时,输入框仍然可用——告诉用户默认行为是排队,
|
|
8167
|
-
//
|
|
8217
|
+
// 想插队请直接点上面的气泡。短语尽量短,避免在窄屏手机上换行。
|
|
8168
8218
|
if (isStructuredSession(session) && session.structuredState && session.structuredState.inFlight) {
|
|
8169
|
-
return "回复中…Enter 排队 ·
|
|
8219
|
+
return "回复中…Enter 排队 · 点气泡插队";
|
|
8170
8220
|
}
|
|
8171
8221
|
return "";
|
|
8172
8222
|
}
|
|
@@ -9266,7 +9316,6 @@
|
|
|
9266
9316
|
function updatePinState() {
|
|
9267
9317
|
var drawer = document.getElementById("sessions-drawer");
|
|
9268
9318
|
var mainLayout = document.querySelector(".main-layout");
|
|
9269
|
-
var pinBtn = document.getElementById("sidebar-pin-btn");
|
|
9270
9319
|
// 与 renderAppShell 保持一致:手机端只允许窄条形态 anchored。
|
|
9271
9320
|
var isMobile = isMobileLayout();
|
|
9272
9321
|
var isCollapsed = !!state.sidebarPinned && !!state.sidebarCollapsed;
|
|
@@ -9279,10 +9328,6 @@
|
|
|
9279
9328
|
mainLayout.classList.toggle("sidebar-pinned", isAnchored);
|
|
9280
9329
|
mainLayout.classList.toggle("sidebar-collapsed", isCollapsed);
|
|
9281
9330
|
}
|
|
9282
|
-
if (pinBtn) {
|
|
9283
|
-
pinBtn.classList.toggle("pinned", state.sidebarPinned);
|
|
9284
|
-
pinBtn.title = state.sidebarPinned ? "取消固定侧栏" : "固定侧栏";
|
|
9285
|
-
}
|
|
9286
9331
|
}
|
|
9287
9332
|
|
|
9288
9333
|
function updateDrawerState() {
|
|
@@ -9306,9 +9351,26 @@
|
|
|
9306
9351
|
}
|
|
9307
9352
|
|
|
9308
9353
|
function toggleSessionsDrawer() {
|
|
9309
|
-
|
|
9354
|
+
var isMobile = isMobileLayout();
|
|
9355
|
+
if (!isMobile) {
|
|
9356
|
+
// 桌面:呼出 = 常驻全尺寸;再次点击 = 完全收起(floating-toggle 重新出现)。
|
|
9357
|
+
// 取消了独立的「图钉」概念,sidebarPinned 现在由呼出/关闭自动管理。
|
|
9358
|
+
var willOpen = !state.sidebarPinned;
|
|
9359
|
+
state.sidebarPinned = willOpen;
|
|
9360
|
+
state.sessionsDrawerOpen = willOpen;
|
|
9361
|
+
if (willOpen) {
|
|
9362
|
+
// 桌面重新呼出默认回到全尺寸;窄条形态需用户主动点 collapse 按钮切换。
|
|
9363
|
+
state.sidebarCollapsed = false;
|
|
9364
|
+
try { localStorage.setItem("wand-sidebar-collapsed", "false"); } catch (e) {}
|
|
9365
|
+
}
|
|
9366
|
+
try { localStorage.setItem("wand-sidebar-pinned", String(willOpen)); } catch (e) {}
|
|
9367
|
+
updateLayoutState();
|
|
9368
|
+
scheduleTerminalRefitAfterPaddingTransition();
|
|
9369
|
+
return;
|
|
9370
|
+
}
|
|
9371
|
+
// 手机端:保持原 drawer 行为。
|
|
9310
9372
|
state.sessionsDrawerOpen = !state.sessionsDrawerOpen;
|
|
9311
|
-
if (state.sessionsDrawerOpen
|
|
9373
|
+
if (state.sessionsDrawerOpen) {
|
|
9312
9374
|
state.filePanelOpen = false;
|
|
9313
9375
|
try {
|
|
9314
9376
|
localStorage.setItem("wand-file-panel-open", "false");
|
|
@@ -9318,13 +9380,42 @@
|
|
|
9318
9380
|
}
|
|
9319
9381
|
|
|
9320
9382
|
function closeSessionsDrawer() {
|
|
9321
|
-
|
|
9383
|
+
var isMobile = isMobileLayout();
|
|
9384
|
+
if (!isMobile) {
|
|
9385
|
+
// 桌面:X 按钮 / backdrop 点击 = 完全收起,撤掉常驻状态,floating-toggle 重新出现。
|
|
9386
|
+
// 窄条状态下没有 X 按钮(CSS 隐藏),不会走到这里,因此无需特判 collapsed。
|
|
9387
|
+
if (!state.sidebarPinned && !state.sessionsDrawerOpen) return;
|
|
9388
|
+
closeSwipedItem();
|
|
9389
|
+
state.sidebarPinned = false;
|
|
9390
|
+
state.sessionsDrawerOpen = false;
|
|
9391
|
+
try { localStorage.setItem("wand-sidebar-pinned", "false"); } catch (e) {}
|
|
9392
|
+
updateLayoutState();
|
|
9393
|
+
scheduleTerminalRefitAfterPaddingTransition();
|
|
9394
|
+
return;
|
|
9395
|
+
}
|
|
9396
|
+
// 手机端:保持原 drawer 关闭行为。
|
|
9322
9397
|
if (!state.sessionsDrawerOpen) return;
|
|
9323
9398
|
closeSwipedItem();
|
|
9324
9399
|
state.sessionsDrawerOpen = false;
|
|
9325
9400
|
updateLayoutState();
|
|
9326
9401
|
}
|
|
9327
9402
|
|
|
9403
|
+
// 桌面 padding-left transition 结束后重新拟合终端尺寸。
|
|
9404
|
+
// 抽出来给 toggleSessionsDrawer / closeSessionsDrawer / toggleSidebarCollapsed 复用。
|
|
9405
|
+
function scheduleTerminalRefitAfterPaddingTransition() {
|
|
9406
|
+
var mainLayout = document.querySelector(".main-layout");
|
|
9407
|
+
if (mainLayout) {
|
|
9408
|
+
var onEnd = function(e) {
|
|
9409
|
+
if (e.propertyName === "padding-left") {
|
|
9410
|
+
mainLayout.removeEventListener("transitionend", onEnd);
|
|
9411
|
+
scheduleTerminalResize(true);
|
|
9412
|
+
}
|
|
9413
|
+
};
|
|
9414
|
+
mainLayout.addEventListener("transitionend", onEnd);
|
|
9415
|
+
}
|
|
9416
|
+
setTimeout(function() { scheduleTerminalResize(true); }, 350);
|
|
9417
|
+
}
|
|
9418
|
+
|
|
9328
9419
|
var collapsedTileBubbleEl = null;
|
|
9329
9420
|
function ensureCollapsedTileBubble() {
|
|
9330
9421
|
if (collapsedTileBubbleEl && document.body.contains(collapsedTileBubbleEl)) {
|
|
@@ -9390,8 +9481,7 @@
|
|
|
9390
9481
|
|
|
9391
9482
|
function toggleSidebarCollapsed() {
|
|
9392
9483
|
var isMobile = isMobileLayout();
|
|
9393
|
-
//
|
|
9394
|
-
// 用户直觉是「点了就该看到窄条」,过去这里 early return 让按钮看上去没反应。
|
|
9484
|
+
// 任何形态下点窄条按钮都意味着「我要常驻」,确保 pinned 写上。
|
|
9395
9485
|
if (!state.sidebarPinned) {
|
|
9396
9486
|
state.sidebarPinned = true;
|
|
9397
9487
|
try {
|
|
@@ -9415,47 +9505,13 @@
|
|
|
9415
9505
|
localStorage.setItem("wand-sidebar-pinned", "false");
|
|
9416
9506
|
} catch (e) {}
|
|
9417
9507
|
} else {
|
|
9418
|
-
// 桌面端展开窄条 → 300px
|
|
9508
|
+
// 桌面端展开窄条 → 300px 全栏常驻。
|
|
9419
9509
|
state.sessionsDrawerOpen = true;
|
|
9420
9510
|
}
|
|
9421
9511
|
render();
|
|
9422
|
-
|
|
9423
|
-
if (mainLayout) {
|
|
9424
|
-
var onEnd = function(e) {
|
|
9425
|
-
if (e.propertyName === "padding-left") {
|
|
9426
|
-
mainLayout.removeEventListener("transitionend", onEnd);
|
|
9427
|
-
scheduleTerminalResize(true);
|
|
9428
|
-
}
|
|
9429
|
-
};
|
|
9430
|
-
mainLayout.addEventListener("transitionend", onEnd);
|
|
9431
|
-
}
|
|
9432
|
-
setTimeout(function() { scheduleTerminalResize(true); }, 350);
|
|
9512
|
+
scheduleTerminalRefitAfterPaddingTransition();
|
|
9433
9513
|
}
|
|
9434
9514
|
|
|
9435
|
-
function toggleSidebarPin() {
|
|
9436
|
-
if (isMobileLayout()) return;
|
|
9437
|
-
state.sidebarPinned = !state.sidebarPinned;
|
|
9438
|
-
try {
|
|
9439
|
-
localStorage.setItem("wand-sidebar-pinned", String(state.sidebarPinned));
|
|
9440
|
-
} catch (e) {}
|
|
9441
|
-
if (state.sidebarPinned) {
|
|
9442
|
-
state.sessionsDrawerOpen = true;
|
|
9443
|
-
}
|
|
9444
|
-
updateLayoutState();
|
|
9445
|
-
// Refit terminal after padding-left transition completes
|
|
9446
|
-
var mainLayout = document.querySelector(".main-layout");
|
|
9447
|
-
if (mainLayout) {
|
|
9448
|
-
var onEnd = function(e) {
|
|
9449
|
-
if (e.propertyName === "padding-left") {
|
|
9450
|
-
mainLayout.removeEventListener("transitionend", onEnd);
|
|
9451
|
-
scheduleTerminalResize(true);
|
|
9452
|
-
}
|
|
9453
|
-
};
|
|
9454
|
-
mainLayout.addEventListener("transitionend", onEnd);
|
|
9455
|
-
}
|
|
9456
|
-
// Fallback refit in case transition doesn't fire
|
|
9457
|
-
setTimeout(function() { scheduleTerminalResize(true); }, 350);
|
|
9458
|
-
}
|
|
9459
9515
|
|
|
9460
9516
|
// Store last focused element for focus trap
|
|
9461
9517
|
var lastFocusedElement = null;
|
|
@@ -12257,9 +12313,10 @@
|
|
|
12257
12313
|
|
|
12258
12314
|
function postStructuredInput(input, inputBox, session, opts) {
|
|
12259
12315
|
opts = opts || {};
|
|
12260
|
-
//
|
|
12261
|
-
//
|
|
12262
|
-
//
|
|
12316
|
+
// interrupt:true 现在只来自 Cmd/Ctrl+Enter 快捷键,或点队列气泡触发的
|
|
12317
|
+
// queueBarPromoteIndex()。普通 Enter / 点发送在上一条还在流式时默认走
|
|
12318
|
+
// queue —— 后端 sendMessage(...) 会把它追加到 queuedMessages,等当前 turn
|
|
12319
|
+
// 结束自动 flush;想插队就点输入框上方那条气泡。
|
|
12263
12320
|
var requestedInterrupt = !!opts.interrupt;
|
|
12264
12321
|
console.log("[WAND] postStructuredInput selectedId:", state.selectedId, "input:", input && input.substring(0, 50), "requestedInterrupt:", requestedInterrupt, "session:", session && { id: session.id, sessionKind: session.sessionKind, runner: session.runner, status: session.status, inFlight: session.structuredState && session.structuredState.inFlight });
|
|
12265
12322
|
if (!state.selectedId || !input) return Promise.resolve();
|
|
@@ -12297,7 +12354,7 @@
|
|
|
12297
12354
|
updateSessionSnapshot(optimisticPatch);
|
|
12298
12355
|
var queueRefreshed = state.sessions.find(function(s) { return s.id === session.id; }) || session;
|
|
12299
12356
|
state.currentMessages = buildMessagesForRender(queueRefreshed, getPreferredMessages(queueRefreshed, queueRefreshed.output, false));
|
|
12300
|
-
updateInputHint("
|
|
12357
|
+
updateInputHint("已加入排队…");
|
|
12301
12358
|
renderChat(true);
|
|
12302
12359
|
updateStructuredQueueCounter();
|
|
12303
12360
|
// 乐观 toast:原本只在 POST 完成后才提示,Claude 流式拖太久时用户根本
|
|
@@ -12458,8 +12515,9 @@
|
|
|
12458
12515
|
// · 默认只展开队首(即下一个要发的那条),显示编号 + 文本 + × 删除
|
|
12459
12516
|
// · 其他消息收起成一根小横杠(指示存在但不占空间)
|
|
12460
12517
|
// · 鼠标悬到任意小横杠 → 该条展开、原本展开的那条收回小横杠
|
|
12461
|
-
// ·
|
|
12462
|
-
//
|
|
12518
|
+
// · 点一下任意气泡 → 中断当前回复、把这条作为新输入插队发出去
|
|
12519
|
+
// · 按住任意气泡向上 / 向下拖拽 → 换序
|
|
12520
|
+
// 末尾跟一个 ⚡ 按钮:等价于点队首气泡(保留作为快速插队的视觉提示)。
|
|
12463
12521
|
// 数据源:session.queuedMessages(后端 WS + postStructuredInput 乐观更新)。
|
|
12464
12522
|
// ──────────────────────────────────────────────────────────────────────────
|
|
12465
12523
|
|
|
@@ -12499,8 +12557,8 @@
|
|
|
12499
12557
|
var itemClass = "queue-bar-item";
|
|
12500
12558
|
if (isExpanded) itemClass += " expanded";
|
|
12501
12559
|
if (single) itemClass += " queue-bar-item-single";
|
|
12502
|
-
//
|
|
12503
|
-
var titleAttr = isExpanded ? raw + "
|
|
12560
|
+
// 整个 chip 既是拖拽起手区,也是"点一下立即发送"的触发点;delete 按钮单独占点击。
|
|
12561
|
+
var titleAttr = isExpanded ? raw + "(点一下立即发送 · 按住可拖动调序)" : raw + "(点一下立即发送)";
|
|
12504
12562
|
chips +=
|
|
12505
12563
|
'<li class="' + itemClass + '" data-index="' + i + '" data-action="drag"' +
|
|
12506
12564
|
' title="' + escapeHtml(titleAttr) + '">' +
|
|
@@ -12654,19 +12712,27 @@
|
|
|
12654
12712
|
}
|
|
12655
12713
|
|
|
12656
12714
|
function queueBarPromoteHead() {
|
|
12715
|
+
queueBarPromoteIndex(0);
|
|
12716
|
+
}
|
|
12717
|
+
|
|
12718
|
+
// 把队列里第 index 条剥下来,作为新的输入立刻发送出去。
|
|
12719
|
+
// - inFlight:interrupt + preserveQueue(中断当前回复,保留其它排队)
|
|
12720
|
+
// - 非 inFlight:当作普通新消息发出去
|
|
12721
|
+
// 用户路径:点输入框上方的气泡(chip)→ 这里。
|
|
12722
|
+
function queueBarPromoteIndex(index) {
|
|
12657
12723
|
var session = state.sessions.find(function(s) { return s.id === state.selectedId; });
|
|
12658
12724
|
if (!session) return;
|
|
12659
12725
|
var queue = Array.isArray(session.queuedMessages) ? session.queuedMessages.slice() : [];
|
|
12660
|
-
if (queue.length
|
|
12661
|
-
var
|
|
12662
|
-
var rest = queue.slice(1);
|
|
12726
|
+
if (index < 0 || index >= queue.length) return;
|
|
12727
|
+
var picked = queue[index];
|
|
12728
|
+
var rest = queue.slice(0, index).concat(queue.slice(index + 1));
|
|
12663
12729
|
var prev = queue.slice();
|
|
12664
12730
|
var inFlight = !!(session.structuredState && session.structuredState.inFlight && session.status === "running");
|
|
12665
12731
|
|
|
12666
|
-
//
|
|
12732
|
+
// 乐观:剥掉这一条;hover 下标随之收缩
|
|
12667
12733
|
if (typeof state.queueBarHoverIndex === "number") {
|
|
12668
|
-
if (state.queueBarHoverIndex ===
|
|
12669
|
-
else state.queueBarHoverIndex -= 1;
|
|
12734
|
+
if (state.queueBarHoverIndex === index) state.queueBarHoverIndex = null;
|
|
12735
|
+
else if (state.queueBarHoverIndex > index) state.queueBarHoverIndex -= 1;
|
|
12670
12736
|
}
|
|
12671
12737
|
updateSessionSnapshot({ id: session.id, queuedMessages: rest });
|
|
12672
12738
|
|
|
@@ -12674,14 +12740,14 @@
|
|
|
12674
12740
|
? crypto.randomUUID()
|
|
12675
12741
|
: (Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 10));
|
|
12676
12742
|
|
|
12677
|
-
var body = { input:
|
|
12743
|
+
var body = { input: picked, idempotencyKey: idempotencyKey };
|
|
12678
12744
|
if (inFlight) {
|
|
12679
12745
|
// 中断 + 保留剩余队列
|
|
12680
12746
|
body.interrupt = true;
|
|
12681
12747
|
body.preserveQueue = true;
|
|
12682
12748
|
}
|
|
12683
12749
|
// 给一个乐观 toast,让用户瞬间知道点击生效了
|
|
12684
|
-
showToast(inFlight ? "
|
|
12750
|
+
showToast(inFlight ? "已请求中断当前回复,立即发送这条。" : "已立即发送这条消息。", "info");
|
|
12685
12751
|
|
|
12686
12752
|
fetch("/api/structured-sessions/" + session.id + "/messages", {
|
|
12687
12753
|
method: "POST",
|
|
@@ -12715,6 +12781,9 @@
|
|
|
12715
12781
|
}
|
|
12716
12782
|
|
|
12717
12783
|
// ── 拖拽排序(Pointer Events + 真实高度的 sort/animate)──
|
|
12784
|
+
// 单条气泡的 pointerdown 也会进这里,但 queue.length <= 1 时直接返回,让
|
|
12785
|
+
// 系统 click 事件穿透到 #queue-bar-host 的 click delegate(那里再判断"点击
|
|
12786
|
+
// 气泡 → 立即发送")。
|
|
12718
12787
|
function queueBarDragStart(ev, chipEl) {
|
|
12719
12788
|
var session = state.sessions.find(function(s) { return s.id === state.selectedId; });
|
|
12720
12789
|
if (!session) return;
|
|
@@ -12746,6 +12815,7 @@
|
|
|
12746
12815
|
startY: ev.clientY,
|
|
12747
12816
|
gap: gap,
|
|
12748
12817
|
queueSnapshot: queue,
|
|
12818
|
+
moved: false, // 没真正拖动过 → 抬手时按 tap 处理:promote 这条
|
|
12749
12819
|
};
|
|
12750
12820
|
|
|
12751
12821
|
chipEl.classList.add("dragging");
|
|
@@ -12793,6 +12863,8 @@
|
|
|
12793
12863
|
if (!d || ev.pointerId !== d.pointerId) return;
|
|
12794
12864
|
ev.preventDefault();
|
|
12795
12865
|
var deltaY = ev.clientY - d.startY;
|
|
12866
|
+
// 4px 阈值过滤抖动 / 触屏轻微滑动;超过才算"真的在拖",否则抬手当 tap。
|
|
12867
|
+
if (Math.abs(deltaY) > 4) d.moved = true;
|
|
12796
12868
|
d.itemEl.style.transform = "translateY(" + deltaY + "px)";
|
|
12797
12869
|
|
|
12798
12870
|
// 拖动中心 Y 决定目标插入位置
|
|
@@ -12827,6 +12899,7 @@
|
|
|
12827
12899
|
var origIndex = d.origIndex;
|
|
12828
12900
|
var targetIndex = d.targetIndex;
|
|
12829
12901
|
var queueSnapshot = d.queueSnapshot;
|
|
12902
|
+
var wasTap = !d.moved;
|
|
12830
12903
|
|
|
12831
12904
|
// 清掉 inline transform 让 CSS 自然回位
|
|
12832
12905
|
d.siblings.forEach(function(el) {
|
|
@@ -12838,8 +12911,9 @@
|
|
|
12838
12911
|
state.queueBarDrag = null;
|
|
12839
12912
|
|
|
12840
12913
|
if (origIndex === targetIndex) {
|
|
12841
|
-
//
|
|
12914
|
+
// 没真的移动过 → 按 tap 处理:把这条剥下来插队发送。
|
|
12842
12915
|
updateQueueBar();
|
|
12916
|
+
if (wasTap) queueBarPromoteIndex(origIndex);
|
|
12843
12917
|
return;
|
|
12844
12918
|
}
|
|
12845
12919
|
|
|
@@ -12886,7 +12960,16 @@
|
|
|
12886
12960
|
var actionEl = ev.target && ev.target.closest ? ev.target.closest("[data-action]") : null;
|
|
12887
12961
|
if (!actionEl || !host.contains(actionEl)) return;
|
|
12888
12962
|
var action = actionEl.getAttribute("data-action");
|
|
12889
|
-
if (action === "drag")
|
|
12963
|
+
if (action === "drag") {
|
|
12964
|
+
// queue.length > 1 时 pointerdown 已经 preventDefault → click 不会到这;
|
|
12965
|
+
// queue.length === 1 时 drag-start 早退、click 会落到这里:当成 tap,
|
|
12966
|
+
// 把这条直接 promote 出去。
|
|
12967
|
+
ev.preventDefault();
|
|
12968
|
+
ev.stopPropagation();
|
|
12969
|
+
var idx = Number(actionEl.getAttribute("data-index"));
|
|
12970
|
+
queueBarPromoteIndex(idx);
|
|
12971
|
+
return;
|
|
12972
|
+
}
|
|
12890
12973
|
ev.preventDefault();
|
|
12891
12974
|
ev.stopPropagation();
|
|
12892
12975
|
if (action === "promote") { queueBarPromoteHead(); return; }
|
|
@@ -13502,12 +13585,6 @@
|
|
|
13502
13585
|
: (isCodex ? (isRunning ? "发送给 Codex" : "Codex 会话已结束") : (!selectedSession || isRunning || canResumeOnSend ? "发送" : "会话已结束")));
|
|
13503
13586
|
sendBtn.classList.toggle("queue-mode", structuredInFlight);
|
|
13504
13587
|
}
|
|
13505
|
-
var interruptBtn = document.getElementById("interrupt-send-button");
|
|
13506
|
-
if (interruptBtn) {
|
|
13507
|
-
// 仅结构化 + inFlight 时显示。pty 会话有自己的 Ctrl+C / stop 按钮,
|
|
13508
|
-
// 用不上这套语义。
|
|
13509
|
-
interruptBtn.classList.toggle("hidden", !structuredInFlight);
|
|
13510
|
-
}
|
|
13511
13588
|
var container = document.getElementById("output");
|
|
13512
13589
|
if (container) container.classList.toggle("interactive", !structured && state.terminalInteractive);
|
|
13513
13590
|
}
|
|
@@ -16993,24 +17070,13 @@
|
|
|
16993
17070
|
return;
|
|
16994
17071
|
}
|
|
16995
17072
|
|
|
16996
|
-
//
|
|
16997
|
-
//
|
|
16998
|
-
//
|
|
16999
|
-
|
|
17000
|
-
|
|
17001
|
-
|
|
17002
|
-
|
|
17003
|
-
turnDone = !(sel.structuredState && sel.structuredState.inFlight);
|
|
17004
|
-
} else {
|
|
17005
|
-
turnDone = sel.status !== "running";
|
|
17006
|
-
}
|
|
17007
|
-
}
|
|
17008
|
-
if (turnDone) {
|
|
17009
|
-
container.classList.add("hidden");
|
|
17010
|
-
if (bodyEl) bodyEl.classList.add("hidden");
|
|
17011
|
-
return;
|
|
17012
|
-
}
|
|
17013
|
-
|
|
17073
|
+
// 之前这里在 turn 结束(结构化 inFlight=false 或 PTY 非 running)时
|
|
17074
|
+
// 把进度条收起来,理由是「模型经常忘了发最后一条全 completed 的
|
|
17075
|
+
// TodoWrite,让用户对着 5/6 干瞪眼很别扭」。但反馈是:在结构化模式下
|
|
17076
|
+
// inFlight 在流间隙会短暂置假,进度条因此跟着闪没;而且 turn 刚结束
|
|
17077
|
+
// 时用户其实想再看一眼最终进度。改回「只要当前 turn 里有 todos 就显
|
|
17078
|
+
// 示,allDone 时再隐藏」,跨 turn 残留交给开头那段「最后一条 user
|
|
17079
|
+
// 消息后才扫 TodoWrite」的 scoping 兜住。
|
|
17014
17080
|
container.classList.remove("hidden");
|
|
17015
17081
|
if (bodyEl) bodyEl.classList.remove("hidden");
|
|
17016
17082
|
|
|
@@ -515,33 +515,15 @@
|
|
|
515
515
|
transition: width 0.3s var(--ease-out-expo), transform 0.35s var(--ease-out-expo), box-shadow 0.35s ease, opacity 0.25s ease;
|
|
516
516
|
}
|
|
517
517
|
|
|
518
|
-
/*
|
|
518
|
+
/* 窄条(collapsed)状态:不显示 X 关闭按钮。
|
|
519
|
+
窄条只保留「展开」按钮,关闭操作必须先回到全尺寸。
|
|
520
|
+
多塞一层 .sidebar-header-actions 把 specificity 抬到 0,4,0,
|
|
519
521
|
压过文件后段 .sidebar-header-actions .btn-ghost.btn-sm
|
|
520
522
|
与 .drawer-close-btn 的 display: inline-flex 重置规则。 */
|
|
521
|
-
.sidebar.pinned .sidebar-header-actions .sidebar-close {
|
|
522
|
-
display: none;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
/* 窄条(collapsed)状态:明确不显示 X 关闭按钮。
|
|
526
|
-
虽然 collapsed 当前必然蕴含 pinned(见 isSidebarNarrow),
|
|
527
|
-
上一条 .sidebar.pinned 规则已经能把它藏住,但单独写一条更直观,
|
|
528
|
-
也防止以后允许 drawer 模式下 collapse 时漏掉。
|
|
529
|
-
展开(drawer 模式)下才显示关闭按钮供用户收起侧栏。 */
|
|
530
523
|
.sidebar.collapsed .sidebar-header-actions .sidebar-close {
|
|
531
524
|
display: none;
|
|
532
525
|
}
|
|
533
526
|
|
|
534
|
-
/* 与上方对称:drawer 模式(未 pin)下,X 关闭按钮已能把侧栏关掉,
|
|
535
|
-
再放一个「收起为窄条」按钮会和 X 视觉上重复,且它的行为
|
|
536
|
-
(自动 pin + collapse)也容易让人误以为是关闭。
|
|
537
|
-
只在 pinned 模式保留这颗,drawer 模式下隐藏。
|
|
538
|
-
注意:必须用 .sidebar-header-actions 多嵌一层把 specificity 提到 0,4,0,
|
|
539
|
-
否则会和文件后段 .sidebar-header-actions .btn-ghost.btn-sm (0,3,0) 打平
|
|
540
|
-
而因后定义胜出,导致 drawer 模式下窄条按钮和 X 同时显示。 */
|
|
541
|
-
.sidebar:not(.pinned) .sidebar-header-actions .sidebar-collapse-toggle {
|
|
542
|
-
display: none;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
527
|
/* ===== 侧栏窄条模式(仅 desktop pin 模式生效)===== */
|
|
546
528
|
.sidebar.pinned.collapsed {
|
|
547
529
|
width: var(--sidebar-collapsed-width);
|
|
@@ -553,7 +535,6 @@
|
|
|
553
535
|
}
|
|
554
536
|
.sidebar.pinned.collapsed .sidebar-header-main,
|
|
555
537
|
.sidebar.pinned.collapsed .sidebar-header-more,
|
|
556
|
-
.sidebar.pinned.collapsed .sidebar-pin-toggle,
|
|
557
538
|
.sidebar.pinned.collapsed .sidebar-footer {
|
|
558
539
|
display: none;
|
|
559
540
|
}
|
|
@@ -700,15 +681,6 @@
|
|
|
700
681
|
.sidebar-collapse-toggle.collapsed {
|
|
701
682
|
color: var(--primary);
|
|
702
683
|
}
|
|
703
|
-
/* ===== 图钉按钮 ===== */
|
|
704
|
-
.sidebar-pin-toggle {
|
|
705
|
-
flex-shrink: 0;
|
|
706
|
-
transition: transform 0.3s var(--ease-out-expo), color var(--transition-fast);
|
|
707
|
-
}
|
|
708
|
-
.sidebar-pin-toggle.pinned {
|
|
709
|
-
color: var(--primary);
|
|
710
|
-
transform: rotate(45deg);
|
|
711
|
-
}
|
|
712
684
|
|
|
713
685
|
/* ===== 侧边栏头部 ===== */
|
|
714
686
|
.sidebar-header {
|
|
@@ -2617,6 +2589,11 @@
|
|
|
2617
2589
|
flex: 1;
|
|
2618
2590
|
display: flex;
|
|
2619
2591
|
flex-direction: column;
|
|
2592
|
+
/* min-width: 0 是关键:作为 .main-layout(flex row)的子项,默认
|
|
2593
|
+
min-width:auto 会让 main-content 跟着内部最宽的不可断节点一起撑大,
|
|
2594
|
+
窄屏上结果就是 chat 卡片把布局推宽 → 内容被 overflow:hidden 截掉
|
|
2595
|
+
却又拖不动。这里强制可收缩,配合下游 .chat-* 的 break-word 才有效。 */
|
|
2596
|
+
min-width: 0;
|
|
2620
2597
|
min-height: 0;
|
|
2621
2598
|
overflow: hidden;
|
|
2622
2599
|
background: transparent;
|
|
@@ -4062,14 +4039,21 @@
|
|
|
4062
4039
|
color: var(--text-tertiary);
|
|
4063
4040
|
}
|
|
4064
4041
|
|
|
4065
|
-
/* ===== 消息气泡 =====
|
|
4042
|
+
/* ===== 消息气泡 =====
|
|
4043
|
+
兜底约束:气泡不应超出聊天容器宽度。即便子节点(长 URL、不可断字符串、
|
|
4044
|
+
嵌入元素)想撑宽,也由 max-width / overflow-x 截断或换行。 */
|
|
4066
4045
|
.chat-message-bubble {
|
|
4067
4046
|
padding: 12px 16px;
|
|
4068
4047
|
border-radius: var(--radius-md);
|
|
4069
4048
|
font-size: 0.875rem;
|
|
4070
4049
|
line-height: var(--line-height-relaxed);
|
|
4071
4050
|
word-wrap: break-word;
|
|
4051
|
+
overflow-wrap: anywhere;
|
|
4052
|
+
word-break: break-word;
|
|
4072
4053
|
white-space: pre-wrap;
|
|
4054
|
+
max-width: 100%;
|
|
4055
|
+
min-width: 0;
|
|
4056
|
+
overflow-x: hidden;
|
|
4073
4057
|
box-shadow: var(--shadow-sm);
|
|
4074
4058
|
transition: box-shadow var(--transition-fast);
|
|
4075
4059
|
}
|
|
@@ -4123,13 +4107,20 @@
|
|
|
4123
4107
|
display: flex;
|
|
4124
4108
|
flex-direction: column;
|
|
4125
4109
|
gap: 2px;
|
|
4110
|
+
width: 100%;
|
|
4126
4111
|
max-width: 100%;
|
|
4127
4112
|
min-width: 0;
|
|
4113
|
+
/* overflow-x: hidden 兜底——内部 tool-use-card / inline-terminal /
|
|
4114
|
+
markdown 如果还有不可断超长 token,绝不让它把整张消息推宽。
|
|
4115
|
+
结合 word-break + overflow-wrap,用户看到的永远是换行不是横滚条。 */
|
|
4116
|
+
overflow-x: hidden;
|
|
4128
4117
|
font-size: 0.875rem;
|
|
4129
4118
|
line-height: var(--line-height-relaxed);
|
|
4130
4119
|
word-wrap: break-word;
|
|
4120
|
+
word-break: break-word;
|
|
4131
4121
|
overflow-wrap: anywhere;
|
|
4132
4122
|
color: var(--text-primary);
|
|
4123
|
+
box-sizing: border-box;
|
|
4133
4124
|
}
|
|
4134
4125
|
|
|
4135
4126
|
.chat-message-content > .thinking-inline,
|
|
@@ -4148,7 +4139,10 @@
|
|
|
4148
4139
|
display: flex;
|
|
4149
4140
|
flex-direction: column;
|
|
4150
4141
|
gap: 6px;
|
|
4142
|
+
width: 100%;
|
|
4151
4143
|
max-width: 100%;
|
|
4144
|
+
min-width: 0;
|
|
4145
|
+
box-sizing: border-box;
|
|
4152
4146
|
}
|
|
4153
4147
|
/* user turn 默认 align-self: flex-end(右对齐);多段渲染时强制
|
|
4154
4148
|
容器内左对齐,否则 subagent 段会被推到右边,border-left 色条断裂。 */
|
|
@@ -4160,12 +4154,22 @@
|
|
|
4160
4154
|
display: flex;
|
|
4161
4155
|
flex-direction: column;
|
|
4162
4156
|
align-self: flex-start;
|
|
4157
|
+
width: 100%;
|
|
4158
|
+
max-width: 100%;
|
|
4159
|
+
min-width: 0;
|
|
4160
|
+
box-sizing: border-box;
|
|
4163
4161
|
}
|
|
4164
4162
|
.chat-message.multi-agent .chat-message-segment.subagent {
|
|
4165
4163
|
padding-left: 10px;
|
|
4166
4164
|
margin-left: 6px;
|
|
4167
4165
|
border-left: 2px solid var(--agent-color, var(--border-subtle));
|
|
4168
4166
|
border-radius: 0 4px 4px 0;
|
|
4167
|
+
/* 父 .chat-message-segment 是 width:100%; box-sizing:border-box,padding
|
|
4168
|
+
和 border 已被 border-box 吃掉,但 margin-left:6px 是外间距,会把
|
|
4169
|
+
整段推出父容器 6px 撞屏幕右边。这里把宽度从父宽减掉同等 margin,
|
|
4170
|
+
视觉上左缘内缩 6px、右缘对齐父容器,刚好不溢出。 */
|
|
4171
|
+
width: calc(100% - 6px);
|
|
4172
|
+
max-width: calc(100% - 6px);
|
|
4169
4173
|
}
|
|
4170
4174
|
.chat-message.multi-agent .chat-message-segment.subagent .chat-message-content {
|
|
4171
4175
|
/* 子 agent 段内的气泡稍微弱化背景,区分主线 */
|
|
@@ -4179,13 +4183,19 @@
|
|
|
4179
4183
|
color: var(--agent-color, var(--accent));
|
|
4180
4184
|
}
|
|
4181
4185
|
|
|
4182
|
-
/* "勤劳初二 ↳ 让 侦探猫 帮忙" 分隔提示
|
|
4186
|
+
/* "勤劳初二 ↳ 让 侦探猫 帮忙" 分隔提示
|
|
4187
|
+
注:color-mix() 需要 Chrome/WebView 111+(2023.04)。Android APK 上常有更老
|
|
4188
|
+
的 WebView,会把 color-mix(...) 当作 invalid 整条扔掉,导致背景/边框消失。
|
|
4189
|
+
下面凡是 color-mix 的属性都先写一条 rgba()/纯色 fallback,再被 color-mix
|
|
4190
|
+
覆盖;旧浏览器看到 fallback、新浏览器自动用 color-mix。同一文件里下方
|
|
4191
|
+
.chat-handoff-tag / .subagent-reply / .subagent-reply-cycle 等也是同思路。 */
|
|
4183
4192
|
.chat-handoff {
|
|
4184
4193
|
align-self: flex-start;
|
|
4185
4194
|
margin: 4px 0 2px 4px;
|
|
4186
4195
|
padding: 2px 10px;
|
|
4187
4196
|
font-size: 0.7rem;
|
|
4188
4197
|
color: var(--text-tertiary);
|
|
4198
|
+
background: rgba(197, 101, 61, 0.08);
|
|
4189
4199
|
background: color-mix(in srgb, var(--agent-color, var(--accent)) 8%, transparent);
|
|
4190
4200
|
border-left: 2px solid var(--agent-color, var(--accent));
|
|
4191
4201
|
border-radius: 0 8px 8px 0;
|
|
@@ -4216,7 +4226,9 @@
|
|
|
4216
4226
|
letter-spacing: 0.04em;
|
|
4217
4227
|
text-transform: uppercase;
|
|
4218
4228
|
color: var(--agent-color, var(--accent));
|
|
4229
|
+
background: rgba(197, 101, 61, 0.14);
|
|
4219
4230
|
background: color-mix(in srgb, var(--agent-color, var(--accent)) 14%, transparent);
|
|
4231
|
+
border: 1px solid rgba(197, 101, 61, 0.32);
|
|
4220
4232
|
border: 1px solid color-mix(in srgb, var(--agent-color, var(--accent)) 32%, transparent);
|
|
4221
4233
|
border-radius: 4px;
|
|
4222
4234
|
vertical-align: 1px;
|
|
@@ -4226,7 +4238,9 @@
|
|
|
4226
4238
|
|
|
4227
4239
|
/* subagent 回复气泡(群聊角色完成任务后的发言) */
|
|
4228
4240
|
.subagent-reply {
|
|
4241
|
+
background: rgba(197, 101, 61, 0.06);
|
|
4229
4242
|
background: color-mix(in srgb, var(--agent-color, var(--accent)) 6%, var(--bg-surface));
|
|
4243
|
+
border: 1px solid rgba(197, 101, 61, 0.20);
|
|
4230
4244
|
border: 1px solid color-mix(in srgb, var(--agent-color, var(--accent)) 20%, transparent);
|
|
4231
4245
|
border-radius: 10px;
|
|
4232
4246
|
padding: 10px 14px;
|
|
@@ -4236,6 +4250,10 @@
|
|
|
4236
4250
|
color: var(--text-primary);
|
|
4237
4251
|
word-break: break-word;
|
|
4238
4252
|
overflow-wrap: anywhere;
|
|
4253
|
+
/* box-sizing: border-box 让 padding(14px*2)+border(1px*2) 算进 width:100%,
|
|
4254
|
+
不再外鼓 30px 撑出 subagent 段右边缘。 */
|
|
4255
|
+
box-sizing: border-box;
|
|
4256
|
+
width: 100%;
|
|
4239
4257
|
max-width: 100%;
|
|
4240
4258
|
min-width: 0;
|
|
4241
4259
|
overflow-x: hidden;
|
|
@@ -4245,7 +4263,9 @@
|
|
|
4245
4263
|
.subagent-reply > :last-child { margin-bottom: 0; }
|
|
4246
4264
|
|
|
4247
4265
|
.subagent-reply.error {
|
|
4266
|
+
background: rgba(226, 87, 76, 0.08);
|
|
4248
4267
|
background: color-mix(in srgb, var(--danger, #e2574c) 8%, var(--bg-surface));
|
|
4268
|
+
border-color: rgba(226, 87, 76, 0.35);
|
|
4249
4269
|
border-color: color-mix(in srgb, var(--danger, #e2574c) 35%, transparent);
|
|
4250
4270
|
}
|
|
4251
4271
|
|
|
@@ -4336,8 +4356,11 @@
|
|
|
4336
4356
|
font-size: 0.7rem;
|
|
4337
4357
|
font-weight: 500;
|
|
4338
4358
|
line-height: 1.4;
|
|
4359
|
+
color: var(--accent);
|
|
4339
4360
|
color: var(--agent-color, var(--accent));
|
|
4361
|
+
background: rgba(255, 248, 240, 0.95);
|
|
4340
4362
|
background: color-mix(in srgb, var(--agent-color, var(--accent)) 8%, var(--bg-surface, #fff));
|
|
4363
|
+
border: 1px solid rgba(197, 101, 61, 0.28);
|
|
4341
4364
|
border: 1px solid color-mix(in srgb, var(--agent-color, var(--accent)) 28%, transparent);
|
|
4342
4365
|
border-radius: 999px;
|
|
4343
4366
|
cursor: pointer;
|
|
@@ -4345,7 +4368,9 @@
|
|
|
4345
4368
|
transition: background 0.15s ease, transform 0.1s ease, border-color 0.15s ease;
|
|
4346
4369
|
}
|
|
4347
4370
|
.subagent-reply-cycle:hover {
|
|
4371
|
+
background: rgba(248, 226, 210, 1);
|
|
4348
4372
|
background: color-mix(in srgb, var(--agent-color, var(--accent)) 16%, var(--bg-surface, #fff));
|
|
4373
|
+
border-color: rgba(197, 101, 61, 0.45);
|
|
4349
4374
|
border-color: color-mix(in srgb, var(--agent-color, var(--accent)) 45%, transparent);
|
|
4350
4375
|
}
|
|
4351
4376
|
.subagent-reply-cycle:active {
|
|
@@ -4362,6 +4387,7 @@
|
|
|
4362
4387
|
height: 6px;
|
|
4363
4388
|
}
|
|
4364
4389
|
.subagent-reply-scroll::-webkit-scrollbar-thumb {
|
|
4390
|
+
background: rgba(197, 101, 61, 0.35);
|
|
4365
4391
|
background: color-mix(in srgb, var(--agent-color, var(--accent)) 35%, transparent);
|
|
4366
4392
|
border-radius: 3px;
|
|
4367
4393
|
}
|
|
@@ -4442,10 +4468,15 @@
|
|
|
4442
4468
|
font-size: 0.7rem;
|
|
4443
4469
|
color: var(--text-secondary);
|
|
4444
4470
|
white-space: pre-wrap;
|
|
4471
|
+
overflow-wrap: anywhere;
|
|
4445
4472
|
word-break: break-all;
|
|
4446
4473
|
max-height: 240px;
|
|
4474
|
+
max-width: 100%;
|
|
4475
|
+
min-width: 0;
|
|
4476
|
+
overflow-x: hidden;
|
|
4447
4477
|
overflow-y: auto;
|
|
4448
4478
|
border-top: 1px dashed var(--border-subtle);
|
|
4479
|
+
box-sizing: border-box;
|
|
4449
4480
|
}
|
|
4450
4481
|
.unknown-block.collapsed .unknown-block-body {
|
|
4451
4482
|
display: none;
|
|
@@ -4601,6 +4632,9 @@
|
|
|
4601
4632
|
overflow: hidden;
|
|
4602
4633
|
width: 100%;
|
|
4603
4634
|
max-width: var(--chat-card-max-width, 720px);
|
|
4635
|
+
/* min-width: 0 让卡片不被内部 <pre>/长路径撑出父容器;缺这一行时
|
|
4636
|
+
手机 APK 上长命令 / 长 JSON 会把整张卡片推宽到屏幕外被截掉。 */
|
|
4637
|
+
min-width: 0;
|
|
4604
4638
|
box-sizing: border-box;
|
|
4605
4639
|
background: linear-gradient(180deg, rgba(255, 251, 245, 0.88) 0%, rgba(255, 246, 234, 0.72) 100%);
|
|
4606
4640
|
box-shadow: 0 1px 2px rgba(89, 58, 32, 0.04);
|
|
@@ -4780,8 +4814,12 @@
|
|
|
4780
4814
|
font-family: var(--font-mono);
|
|
4781
4815
|
color: var(--text-secondary);
|
|
4782
4816
|
white-space: pre-wrap;
|
|
4783
|
-
overflow-wrap:
|
|
4817
|
+
overflow-wrap: anywhere;
|
|
4818
|
+
word-break: break-word;
|
|
4784
4819
|
max-height: 400px;
|
|
4820
|
+
max-width: 100%;
|
|
4821
|
+
min-width: 0;
|
|
4822
|
+
overflow-x: hidden;
|
|
4785
4823
|
overflow-y: auto;
|
|
4786
4824
|
line-height: 1.55;
|
|
4787
4825
|
}
|
|
@@ -4796,13 +4834,20 @@
|
|
|
4796
4834
|
font-family: var(--font-mono);
|
|
4797
4835
|
color: var(--text-secondary);
|
|
4798
4836
|
white-space: pre-wrap;
|
|
4799
|
-
|
|
4837
|
+
/* anywhere + break-all 兜底长 token / 长路径 / 长 URL:手机 APK 上
|
|
4838
|
+
遇到不可断字符串时换行,绝不横向溢出。 */
|
|
4839
|
+
overflow-wrap: anywhere;
|
|
4840
|
+
word-break: break-all;
|
|
4800
4841
|
max-height: 320px;
|
|
4842
|
+
max-width: 100%;
|
|
4843
|
+
min-width: 0;
|
|
4844
|
+
overflow-x: hidden;
|
|
4801
4845
|
overflow-y: auto;
|
|
4802
4846
|
line-height: 1.55;
|
|
4803
4847
|
background: rgba(125, 91, 57, 0.035);
|
|
4804
4848
|
border-radius: var(--radius-xs);
|
|
4805
4849
|
padding: 8px 10px;
|
|
4850
|
+
box-sizing: border-box;
|
|
4806
4851
|
}
|
|
4807
4852
|
.tool-use-result-empty {
|
|
4808
4853
|
font-size: 0.75rem;
|
|
@@ -5046,7 +5091,8 @@
|
|
|
5046
5091
|
}
|
|
5047
5092
|
.thinking-inline.expanded .thinking-inline-preview {
|
|
5048
5093
|
white-space: pre-wrap;
|
|
5049
|
-
overflow-wrap:
|
|
5094
|
+
overflow-wrap: anywhere;
|
|
5095
|
+
word-break: break-word;
|
|
5050
5096
|
}
|
|
5051
5097
|
.thinking-inline-action {
|
|
5052
5098
|
flex-shrink: 0;
|
|
@@ -5120,6 +5166,8 @@
|
|
|
5120
5166
|
margin: 4px 0;
|
|
5121
5167
|
width: 100%;
|
|
5122
5168
|
max-width: var(--chat-card-max-width, 720px);
|
|
5169
|
+
/* 同 .tool-use-card:内部嵌套 inline-tool 长路径不能撑爆窄屏父容器。 */
|
|
5170
|
+
min-width: 0;
|
|
5123
5171
|
box-sizing: border-box;
|
|
5124
5172
|
border-radius: var(--radius-sm);
|
|
5125
5173
|
border: 1px solid var(--border-subtle);
|
|
@@ -5248,6 +5296,8 @@
|
|
|
5248
5296
|
margin: 1px 0;
|
|
5249
5297
|
width: 100%;
|
|
5250
5298
|
max-width: var(--chat-card-max-width, 720px);
|
|
5299
|
+
/* 同 .tool-use-card:长路径 / 长查询不能撑爆窄屏父容器。 */
|
|
5300
|
+
min-width: 0;
|
|
5251
5301
|
box-sizing: border-box;
|
|
5252
5302
|
border-radius: var(--radius-xs);
|
|
5253
5303
|
cursor: pointer;
|
|
@@ -5267,6 +5317,8 @@
|
|
|
5267
5317
|
font-family: var(--font-mono);
|
|
5268
5318
|
line-height: 1.45;
|
|
5269
5319
|
border-radius: var(--radius-xs);
|
|
5320
|
+
max-width: 100%;
|
|
5321
|
+
min-width: 0;
|
|
5270
5322
|
transition: background 0.16s ease, color 0.16s ease;
|
|
5271
5323
|
}
|
|
5272
5324
|
.inline-tool:hover .inline-tool-row {
|
|
@@ -5339,6 +5391,9 @@
|
|
|
5339
5391
|
padding: 10px 12px;
|
|
5340
5392
|
margin-top: 6px;
|
|
5341
5393
|
max-height: 320px;
|
|
5394
|
+
max-width: 100%;
|
|
5395
|
+
min-width: 0;
|
|
5396
|
+
overflow-x: hidden;
|
|
5342
5397
|
overflow-y: auto;
|
|
5343
5398
|
scrollbar-width: thin;
|
|
5344
5399
|
scrollbar-color: rgba(125, 91, 57, 0.2) transparent;
|
|
@@ -5359,7 +5414,10 @@
|
|
|
5359
5414
|
line-height: 1.55;
|
|
5360
5415
|
color: var(--text-secondary);
|
|
5361
5416
|
white-space: pre-wrap;
|
|
5417
|
+
overflow-wrap: anywhere;
|
|
5362
5418
|
word-break: break-all;
|
|
5419
|
+
max-width: 100%;
|
|
5420
|
+
min-width: 0;
|
|
5363
5421
|
margin: 0;
|
|
5364
5422
|
}
|
|
5365
5423
|
.inline-tool-error-inline {
|
|
@@ -5442,6 +5500,8 @@
|
|
|
5442
5500
|
margin: 4px 0;
|
|
5443
5501
|
width: 100%;
|
|
5444
5502
|
max-width: var(--chat-card-max-width, 720px);
|
|
5503
|
+
/* 同 .tool-use-card:长命令 / 长输出不能撑爆窄屏父容器。 */
|
|
5504
|
+
min-width: 0;
|
|
5445
5505
|
box-sizing: border-box;
|
|
5446
5506
|
border: 1px solid rgba(15, 12, 9, 0.6);
|
|
5447
5507
|
border-radius: var(--radius-sm);
|
|
@@ -5471,6 +5531,8 @@
|
|
|
5471
5531
|
cursor: pointer;
|
|
5472
5532
|
user-select: none;
|
|
5473
5533
|
position: relative;
|
|
5534
|
+
max-width: 100%;
|
|
5535
|
+
min-width: 0;
|
|
5474
5536
|
}
|
|
5475
5537
|
.term-header:hover {
|
|
5476
5538
|
background:
|
|
@@ -5550,7 +5612,10 @@
|
|
|
5550
5612
|
line-height: 1.55;
|
|
5551
5613
|
margin-bottom: 6px;
|
|
5552
5614
|
white-space: pre-wrap;
|
|
5615
|
+
overflow-wrap: anywhere;
|
|
5553
5616
|
word-break: break-all;
|
|
5617
|
+
max-width: 100%;
|
|
5618
|
+
min-width: 0;
|
|
5554
5619
|
}
|
|
5555
5620
|
.term-prompt {
|
|
5556
5621
|
color: #6ee09a;
|
|
@@ -5563,6 +5628,9 @@
|
|
|
5563
5628
|
padding-top: 8px;
|
|
5564
5629
|
border-top: 1px dashed rgba(255, 255, 255, 0.06);
|
|
5565
5630
|
max-height: 360px;
|
|
5631
|
+
max-width: 100%;
|
|
5632
|
+
min-width: 0;
|
|
5633
|
+
overflow-x: hidden;
|
|
5566
5634
|
overflow-y: auto;
|
|
5567
5635
|
scrollbar-width: thin;
|
|
5568
5636
|
scrollbar-color: rgba(255, 255, 255, 0.15) transparent;
|
|
@@ -5583,7 +5651,10 @@
|
|
|
5583
5651
|
color: #d4c2a3;
|
|
5584
5652
|
line-height: 1.55;
|
|
5585
5653
|
white-space: pre-wrap;
|
|
5654
|
+
overflow-wrap: anywhere;
|
|
5586
5655
|
word-break: break-all;
|
|
5656
|
+
max-width: 100%;
|
|
5657
|
+
min-width: 0;
|
|
5587
5658
|
}
|
|
5588
5659
|
.term-exit {
|
|
5589
5660
|
margin-top: 10px;
|
|
@@ -5605,6 +5676,8 @@
|
|
|
5605
5676
|
margin: 4px 0;
|
|
5606
5677
|
width: 100%;
|
|
5607
5678
|
max-width: var(--chat-card-max-width, 720px);
|
|
5679
|
+
/* 同 .tool-use-card:长路径 / 长 diff 不能撑爆窄屏父容器。 */
|
|
5680
|
+
min-width: 0;
|
|
5608
5681
|
box-sizing: border-box;
|
|
5609
5682
|
border: 1px solid var(--border-subtle);
|
|
5610
5683
|
border-radius: var(--radius-sm);
|
|
@@ -5626,6 +5699,8 @@
|
|
|
5626
5699
|
font-size: 0.8125rem;
|
|
5627
5700
|
cursor: pointer;
|
|
5628
5701
|
transition: background 0.18s ease;
|
|
5702
|
+
max-width: 100%;
|
|
5703
|
+
min-width: 0;
|
|
5629
5704
|
}
|
|
5630
5705
|
.diff-header:hover {
|
|
5631
5706
|
background: rgba(197, 101, 61, 0.04);
|
|
@@ -5701,11 +5776,15 @@
|
|
|
5701
5776
|
display: flex;
|
|
5702
5777
|
gap: 1px;
|
|
5703
5778
|
background: rgba(125, 91, 57, 0.08);
|
|
5779
|
+
max-width: 100%;
|
|
5780
|
+
min-width: 0;
|
|
5704
5781
|
}
|
|
5705
5782
|
.diff-col {
|
|
5706
|
-
flex: 1;
|
|
5783
|
+
flex: 1 1 0;
|
|
5707
5784
|
min-width: 0;
|
|
5785
|
+
max-width: 100%;
|
|
5708
5786
|
background: rgba(255, 253, 248, 0.85);
|
|
5787
|
+
overflow: hidden;
|
|
5709
5788
|
}
|
|
5710
5789
|
.diff-col-half {
|
|
5711
5790
|
flex: 1;
|
|
@@ -5731,7 +5810,12 @@
|
|
|
5731
5810
|
font-size: 0.75rem;
|
|
5732
5811
|
line-height: 1.55;
|
|
5733
5812
|
white-space: pre-wrap;
|
|
5813
|
+
/* 长 token / 长 URL 必须按字符断,否则不可断字符串会撑爆 .diff-col。 */
|
|
5814
|
+
overflow-wrap: anywhere;
|
|
5734
5815
|
word-break: break-all;
|
|
5816
|
+
max-width: 100%;
|
|
5817
|
+
min-width: 0;
|
|
5818
|
+
box-sizing: border-box;
|
|
5735
5819
|
}
|
|
5736
5820
|
.diff-add {
|
|
5737
5821
|
background: rgba(79, 122, 88, 0.08);
|
|
@@ -5821,12 +5905,20 @@
|
|
|
5821
5905
|
margin: 0;
|
|
5822
5906
|
font-size: 0.7rem;
|
|
5823
5907
|
max-height: 600px;
|
|
5908
|
+
max-width: 100%;
|
|
5909
|
+
min-width: 0;
|
|
5910
|
+
overflow-x: hidden;
|
|
5824
5911
|
overflow-y: auto;
|
|
5912
|
+
white-space: pre-wrap;
|
|
5913
|
+
overflow-wrap: anywhere;
|
|
5914
|
+
word-break: break-all;
|
|
5825
5915
|
}
|
|
5826
5916
|
.tool-result-content code {
|
|
5827
5917
|
font-family: var(--font-mono);
|
|
5828
5918
|
white-space: pre-wrap;
|
|
5919
|
+
overflow-wrap: anywhere;
|
|
5829
5920
|
word-break: break-all;
|
|
5921
|
+
max-width: 100%;
|
|
5830
5922
|
}
|
|
5831
5923
|
|
|
5832
5924
|
/* Markdown Content */
|
|
@@ -5902,8 +5994,14 @@
|
|
|
5902
5994
|
overflow-wrap: anywhere;
|
|
5903
5995
|
word-break: break-all;
|
|
5904
5996
|
}
|
|
5997
|
+
/* 表格容器:先让 table-layout:fixed 强制按 100% 收缩,单元格内
|
|
5998
|
+
overflow-wrap:anywhere 会负责换行;只有在内容真的塞不下(比如
|
|
5999
|
+
超长不可断字符串撑爆 fixed 布局)才退回横向滚动,这样大多数情况
|
|
6000
|
+
下用户看到的是换行而不是横滚条。 */
|
|
5905
6001
|
.markdown-content .md-table-wrap {
|
|
5906
6002
|
margin: 12px 0;
|
|
6003
|
+
max-width: 100%;
|
|
6004
|
+
min-width: 0;
|
|
5907
6005
|
overflow-x: auto;
|
|
5908
6006
|
border: 1px solid var(--border);
|
|
5909
6007
|
border-radius: var(--radius-sm);
|
|
@@ -6452,10 +6550,10 @@
|
|
|
6452
6550
|
}
|
|
6453
6551
|
.queue-bar-item.expanded:active { cursor: grabbing; }
|
|
6454
6552
|
|
|
6455
|
-
/*
|
|
6456
|
-
.queue-bar-item-single { cursor:
|
|
6457
|
-
.queue-bar-item-single.expanded { cursor:
|
|
6458
|
-
.queue-bar-item-single.expanded:active { cursor:
|
|
6553
|
+
/* 单条排队时不能拖排序,但仍可点击 → 立即发送 */
|
|
6554
|
+
.queue-bar-item-single { cursor: pointer; }
|
|
6555
|
+
.queue-bar-item-single.expanded { cursor: pointer; }
|
|
6556
|
+
.queue-bar-item-single.expanded:active { cursor: pointer; }
|
|
6459
6557
|
|
|
6460
6558
|
/* sibling 在被拖期间平滑回位 */
|
|
6461
6559
|
.queue-bar-item-sliding { transition: transform 160ms cubic-bezier(0.2, 0.7, 0.2, 1); }
|
|
@@ -6615,11 +6713,15 @@
|
|
|
6615
6713
|
}
|
|
6616
6714
|
|
|
6617
6715
|
.input-hint {
|
|
6618
|
-
font-size: 0.
|
|
6716
|
+
font-size: 0.5rem; /* 8px — 让位给同一行的按钮,桌面端仍清晰可读 */
|
|
6717
|
+
line-height: 1.2;
|
|
6619
6718
|
color: var(--text-muted);
|
|
6620
6719
|
white-space: nowrap;
|
|
6621
6720
|
user-select: none;
|
|
6622
6721
|
opacity: 0.7;
|
|
6722
|
+
max-width: 38ch; /* 防止突然变长的状态文案撑爆右侧区域 */
|
|
6723
|
+
overflow: hidden;
|
|
6724
|
+
text-overflow: ellipsis;
|
|
6623
6725
|
}
|
|
6624
6726
|
|
|
6625
6727
|
/* Session info bar at bottom of input composer */
|
|
@@ -7519,40 +7621,8 @@
|
|
|
7519
7621
|
.btn-pill-label {
|
|
7520
7622
|
display: inline;
|
|
7521
7623
|
}
|
|
7522
|
-
.btn-pill-interrupt
|
|
7523
|
-
|
|
7524
|
-
color: var(--accent);
|
|
7525
|
-
margin-right: 4px;
|
|
7526
|
-
box-shadow: 0 0 0 1px rgba(197, 101, 61, 0.30) inset;
|
|
7527
|
-
animation: interruptPulse 1.8s ease-in-out infinite;
|
|
7528
|
-
}
|
|
7529
|
-
.btn-pill-interrupt:hover {
|
|
7530
|
-
background: linear-gradient(180deg, var(--accent) 0%, #a8522f 100%);
|
|
7531
|
-
color: #fff;
|
|
7532
|
-
box-shadow: 0 3px 10px rgba(197, 101, 61, 0.35);
|
|
7533
|
-
transform: translateY(-1px);
|
|
7534
|
-
animation: none;
|
|
7535
|
-
}
|
|
7536
|
-
.btn-pill-interrupt:active {
|
|
7537
|
-
transform: translateY(0) scale(0.97);
|
|
7538
|
-
}
|
|
7539
|
-
.btn-pill-interrupt.hidden {
|
|
7540
|
-
display: none;
|
|
7541
|
-
}
|
|
7542
|
-
@keyframes interruptPulse {
|
|
7543
|
-
0%, 100% { box-shadow: 0 0 0 1px rgba(197, 101, 61, 0.30) inset, 0 0 0 0 rgba(197, 101, 61, 0.0); }
|
|
7544
|
-
50% { box-shadow: 0 0 0 1px rgba(197, 101, 61, 0.45) inset, 0 0 0 4px rgba(197, 101, 61, 0.12); }
|
|
7545
|
-
}
|
|
7546
|
-
/* 屏宽 < 480 时把"立即"文字藏掉,只剩 » 图标,避免和 send 一起把行挤爆。
|
|
7547
|
-
hover/focus 期间临时回显,让长按移动端用户也能确认按钮含义。 */
|
|
7548
|
-
@media (max-width: 480px) {
|
|
7549
|
-
.btn-pill-interrupt {
|
|
7550
|
-
padding: 0 8px;
|
|
7551
|
-
}
|
|
7552
|
-
.btn-pill-interrupt .btn-pill-label {
|
|
7553
|
-
display: none;
|
|
7554
|
-
}
|
|
7555
|
-
}
|
|
7624
|
+
/* .btn-pill-interrupt 与 @keyframes interruptPulse 已下线:
|
|
7625
|
+
默认行为永远是"排队(气泡)",想插队请点输入框上方那条气泡。 */
|
|
7556
7626
|
|
|
7557
7627
|
/* send 按钮在「排队模式」下退到次要色,让相邻的"立即发送"成为视觉重点。
|
|
7558
7628
|
不动尺寸 / 圆形,避免 layout shift。 */
|
|
@@ -8944,9 +9014,8 @@
|
|
|
8944
9014
|
line-height: 1.5;
|
|
8945
9015
|
}
|
|
8946
9016
|
|
|
8947
|
-
/* Sidebar header icon buttons —
|
|
9017
|
+
/* Sidebar header icon buttons — collapse / more / close — uniform 32×32 ghost */
|
|
8948
9018
|
.sidebar-header-actions .btn-ghost.btn-sm,
|
|
8949
|
-
.sidebar-pin-toggle,
|
|
8950
9019
|
.drawer-close-btn {
|
|
8951
9020
|
width: 32px;
|
|
8952
9021
|
height: 32px;
|
|
@@ -8965,16 +9034,10 @@
|
|
|
8965
9034
|
color 0.16s ease,
|
|
8966
9035
|
transform 0.22s cubic-bezier(0.34, 1.4, 0.64, 1);
|
|
8967
9036
|
}
|
|
8968
|
-
.sidebar-header-actions .btn-ghost.btn-sm:hover
|
|
8969
|
-
.sidebar-pin-toggle:hover {
|
|
9037
|
+
.sidebar-header-actions .btn-ghost.btn-sm:hover {
|
|
8970
9038
|
background: rgba(125, 91, 57, 0.08);
|
|
8971
9039
|
color: var(--text-primary);
|
|
8972
9040
|
}
|
|
8973
|
-
.sidebar-pin-toggle.pinned {
|
|
8974
|
-
background: rgba(197, 101, 61, 0.12);
|
|
8975
|
-
color: var(--accent);
|
|
8976
|
-
transform: rotate(45deg);
|
|
8977
|
-
}
|
|
8978
9041
|
|
|
8979
9042
|
/* Drawer close — same pattern as modal-close (rotate + danger tint) */
|
|
8980
9043
|
.drawer-close-btn {
|
|
@@ -9705,9 +9768,6 @@
|
|
|
9705
9768
|
|
|
9706
9769
|
/* 平板适配 */
|
|
9707
9770
|
@media (max-width: 768px) {
|
|
9708
|
-
/* 手机隐藏「固定侧栏」按钮:手机上完整 300px 常驻太占地,
|
|
9709
|
-
只保留「收起为窄条」按钮,让用户能切到 56px 窄条形态。 */
|
|
9710
|
-
.sidebar-pin-toggle { display: none; }
|
|
9711
9771
|
/* drawer 模式(pinned 但非窄条)下若未打开,则隐藏到屏幕左侧。
|
|
9712
9772
|
窄条模式(pinned + collapsed)由 .sidebar.pinned.collapsed 的 width:56px
|
|
9713
9773
|
规则常驻显示,不进入这条隐藏分支。 */
|
|
@@ -10065,8 +10125,12 @@
|
|
|
10065
10125
|
padding: 0 14px 0 4px;
|
|
10066
10126
|
border-radius: 5px;
|
|
10067
10127
|
}
|
|
10128
|
+
/* 窄屏(手机)保留提示,但用更小字号 + 更紧的宽度上限,避免把发送按钮挤出行外。
|
|
10129
|
+
状态切换("思考中…" / "已加入排队…")能截断成省略号也比整条消失友好。 */
|
|
10068
10130
|
.input-hint {
|
|
10069
|
-
|
|
10131
|
+
font-size: 0.45rem;
|
|
10132
|
+
max-width: 18ch;
|
|
10133
|
+
opacity: 0.6;
|
|
10070
10134
|
}
|
|
10071
10135
|
|
|
10072
10136
|
/* 移动端内联快捷键 - 折叠为展开按钮,展开到独立第二行 */
|
|
@@ -10501,6 +10565,43 @@
|
|
|
10501
10565
|
.markdown-content pre,
|
|
10502
10566
|
.markdown-content .code-block pre,
|
|
10503
10567
|
.subagent-reply pre { padding: 10px; }
|
|
10568
|
+
/* diff 卡片:窄屏下两列 side-by-side 每列只剩 ~160px,旧/新被挤成
|
|
10569
|
+
单字一行的"窄条"几乎不可读。改成垂直堆叠(旧在上,新在下),
|
|
10570
|
+
单列占满屏宽,长行靠 word-break 自然换行,杜绝横向溢出。 */
|
|
10571
|
+
.diff-columns {
|
|
10572
|
+
flex-direction: column;
|
|
10573
|
+
}
|
|
10574
|
+
.diff-col,
|
|
10575
|
+
.diff-col-half,
|
|
10576
|
+
.diff-col-full {
|
|
10577
|
+
flex: 1 1 auto;
|
|
10578
|
+
width: 100%;
|
|
10579
|
+
max-width: 100%;
|
|
10580
|
+
}
|
|
10581
|
+
/* 终端命令头部也放开 wrap,长命令在 preview 里仍 ellipsis,但 dot/toggle
|
|
10582
|
+
之类不会把卡片撑宽。 */
|
|
10583
|
+
.term-header {
|
|
10584
|
+
flex-wrap: wrap;
|
|
10585
|
+
row-gap: 4px;
|
|
10586
|
+
}
|
|
10587
|
+
.diff-header {
|
|
10588
|
+
flex-wrap: wrap;
|
|
10589
|
+
row-gap: 4px;
|
|
10590
|
+
}
|
|
10591
|
+
.diff-path {
|
|
10592
|
+
flex-basis: 100%;
|
|
10593
|
+
order: 99;
|
|
10594
|
+
}
|
|
10595
|
+
/* inline-tool meta(搜索路径等)窄屏给整行,不再贴右挤标题。 */
|
|
10596
|
+
.inline-tool-row {
|
|
10597
|
+
flex-wrap: wrap;
|
|
10598
|
+
row-gap: 2px;
|
|
10599
|
+
}
|
|
10600
|
+
.inline-tool-meta {
|
|
10601
|
+
max-width: 100%;
|
|
10602
|
+
flex-basis: 100%;
|
|
10603
|
+
padding-left: 22px;
|
|
10604
|
+
}
|
|
10504
10605
|
}
|
|
10505
10606
|
|
|
10506
10607
|
/* Blank chat mobile optimization */
|