@co0ontty/wand 1.14.2 → 1.14.6
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 +279 -74
- package/dist/web-ui/content/styles.css +149 -4
- package/package.json +1 -1
|
@@ -109,6 +109,9 @@
|
|
|
109
109
|
loginChecked: false,
|
|
110
110
|
bootstrapping: true,
|
|
111
111
|
sessionsDrawerOpen: false,
|
|
112
|
+
sidebarPinned: (function() {
|
|
113
|
+
try { return localStorage.getItem("wand-sidebar-pinned") === "true"; } catch (e) { return false; }
|
|
114
|
+
})(),
|
|
112
115
|
modalOpen: false,
|
|
113
116
|
presetValue: "",
|
|
114
117
|
cwdValue: "",
|
|
@@ -652,6 +655,7 @@
|
|
|
652
655
|
if (!el) return false;
|
|
653
656
|
switch (kind) {
|
|
654
657
|
case "tool-card":
|
|
658
|
+
case "diff":
|
|
655
659
|
return !el.classList.contains("collapsed");
|
|
656
660
|
case "thinking":
|
|
657
661
|
return el.classList.contains("expanded") && !el.classList.contains("collapsed");
|
|
@@ -672,7 +676,8 @@
|
|
|
672
676
|
function applyExpandedState(el, kind, expanded) {
|
|
673
677
|
if (!el) return;
|
|
674
678
|
switch (kind) {
|
|
675
|
-
case "tool-card":
|
|
679
|
+
case "tool-card":
|
|
680
|
+
case "diff": {
|
|
676
681
|
el.classList.toggle("collapsed", !expanded);
|
|
677
682
|
break;
|
|
678
683
|
}
|
|
@@ -932,6 +937,10 @@
|
|
|
932
937
|
// Suppress CSS transitions during initial DOM build
|
|
933
938
|
document.documentElement.classList.add("no-transition");
|
|
934
939
|
|
|
940
|
+
// Apply persisted pin state before rendering
|
|
941
|
+
if (state.sidebarPinned && !isMobileLayout()) {
|
|
942
|
+
state.sessionsDrawerOpen = true;
|
|
943
|
+
}
|
|
935
944
|
app.innerHTML = isLoggedIn ? renderAppShell() : renderLogin();
|
|
936
945
|
// Reset chat render tracking since DOM was fully replaced
|
|
937
946
|
resetChatRenderCache();
|
|
@@ -1083,8 +1092,8 @@
|
|
|
1083
1092
|
|
|
1084
1093
|
return '<div class="app-container">' +
|
|
1085
1094
|
'<div id="sessions-drawer-backdrop" class="drawer-backdrop' + drawerClass + '"></div>' +
|
|
1086
|
-
'<div class="main-layout' + (state.sessionsDrawerOpen ? ' sidebar-open' : '') + '">' +
|
|
1087
|
-
'<aside id="sessions-drawer" class="sidebar' + drawerClass + '">' +
|
|
1095
|
+
'<div class="main-layout' + (state.sessionsDrawerOpen ? ' sidebar-open' : '') + (state.sidebarPinned && !isMobileLayout() ? ' sidebar-pinned' : '') + '">' +
|
|
1096
|
+
'<aside id="sessions-drawer" class="sidebar' + drawerClass + (state.sidebarPinned && !isMobileLayout() ? ' pinned' : '') + '">' +
|
|
1088
1097
|
'<div class="sidebar-header">' +
|
|
1089
1098
|
'<div class="sidebar-header-main">' +
|
|
1090
1099
|
'<div class="topbar-logo-icon">W</div>' +
|
|
@@ -1098,6 +1107,9 @@
|
|
|
1098
1107
|
'<button id="sidebar-refresh-btn" class="btn btn-ghost btn-sm" type="button" title="刷新页面">' +
|
|
1099
1108
|
'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg>' +
|
|
1100
1109
|
'</button>' +
|
|
1110
|
+
'<button id="sidebar-pin-btn" class="btn btn-ghost btn-sm sidebar-pin-toggle' + (state.sidebarPinned ? ' pinned' : '') + '" type="button" title="' + (state.sidebarPinned ? '取消固定侧栏' : '固定侧栏') + '">' +
|
|
1111
|
+
'<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>' +
|
|
1112
|
+
'</button>' +
|
|
1101
1113
|
'<button id="close-drawer-button" class="btn btn-ghost btn-sm sidebar-close" type="button" aria-label="关闭菜单">×</button>' +
|
|
1102
1114
|
'</div>' +
|
|
1103
1115
|
'</div>' +
|
|
@@ -1379,6 +1391,17 @@
|
|
|
1379
1391
|
'<label class="field-label" for="cfg-notif-bubble">\u5e94\u7528\u5185\u901a\u77e5\u6c14\u6ce1</label>' +
|
|
1380
1392
|
'</div>' +
|
|
1381
1393
|
'<p class="hint" style="margin-top:0;margin-bottom:10px">\u5728\u9875\u9762\u9876\u90e8\u5f39\u51fa\u6d6e\u52a8\u901a\u77e5\u6c14\u6ce1</p>' +
|
|
1394
|
+
'<div id="native-sound-section" class="settings-notification-section hidden" style="margin-top:6px">' +
|
|
1395
|
+
'<div class="settings-section-title">\u7cfb\u7edf\u901a\u77e5\u94c3\u58f0</div>' +
|
|
1396
|
+
'<div class="settings-about-row">' +
|
|
1397
|
+
'<span class="settings-label">\u94c3\u58f0</span>' +
|
|
1398
|
+
'<div style="display:flex;align-items:center;gap:6px">' +
|
|
1399
|
+
'<select id="native-sound-select" class="field-select" style="min-width:100px"></select>' +
|
|
1400
|
+
'<button id="native-sound-preview" class="btn btn-ghost btn-sm">\u25b6 \u8bd5\u542c</button>' +
|
|
1401
|
+
'</div>' +
|
|
1402
|
+
'</div>' +
|
|
1403
|
+
'<p class="hint" style="margin-top:0">\u9009\u62e9 Android \u7cfb\u7edf\u901a\u77e5\u4f7f\u7528\u7684\u94c3\u58f0</p>' +
|
|
1404
|
+
'</div>' +
|
|
1382
1405
|
'<div class="settings-notification-section" style="margin-top:6px">' +
|
|
1383
1406
|
'<div class="settings-section-title">\u6d4f\u89c8\u5668\u901a\u77e5</div>' +
|
|
1384
1407
|
'<div class="settings-about-row">' +
|
|
@@ -1514,32 +1537,49 @@
|
|
|
1514
1537
|
'<div class="settings-panel" id="settings-tab-display">' +
|
|
1515
1538
|
'<div class="settings-section-title">卡片默认展开状态</div>' +
|
|
1516
1539
|
'<p class="hint" style="margin-top:-4px;margin-bottom:12px">设置结构化聊天视图中各类卡片的默认展开/折叠状态。手动操作的展开状态优先于此默认设置。</p>' +
|
|
1517
|
-
'<div class="
|
|
1518
|
-
'<
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
'
|
|
1534
|
-
'<label class="
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
+
'<div class="switch-card-list">' +
|
|
1541
|
+
'<label class="switch-card" for="cfg-card-edit">' +
|
|
1542
|
+
'<div class="switch-card-header">' +
|
|
1543
|
+
'<span class="switch-card-title">编辑卡片 (Edit/Write)</span>' +
|
|
1544
|
+
'<input id="cfg-card-edit" type="checkbox" class="switch-toggle" />' +
|
|
1545
|
+
'<span class="switch-slider"></span>' +
|
|
1546
|
+
'</div>' +
|
|
1547
|
+
'<div class="switch-card-desc">文件编辑和写入操作的 diff 视图</div>' +
|
|
1548
|
+
'</label>' +
|
|
1549
|
+
'<label class="switch-card" for="cfg-card-inline">' +
|
|
1550
|
+
'<div class="switch-card-header">' +
|
|
1551
|
+
'<span class="switch-card-title">内联工具 (Read/Glob/Grep)</span>' +
|
|
1552
|
+
'<input id="cfg-card-inline" type="checkbox" class="switch-toggle" />' +
|
|
1553
|
+
'<span class="switch-slider"></span>' +
|
|
1554
|
+
'</div>' +
|
|
1555
|
+
'<div class="switch-card-desc">文件读取、搜索等工具的结果</div>' +
|
|
1556
|
+
'</label>' +
|
|
1557
|
+
'<label class="switch-card" for="cfg-card-terminal">' +
|
|
1558
|
+
'<div class="switch-card-header">' +
|
|
1559
|
+
'<span class="switch-card-title">终端输出 (Bash)</span>' +
|
|
1560
|
+
'<input id="cfg-card-terminal" type="checkbox" class="switch-toggle" />' +
|
|
1561
|
+
'<span class="switch-slider"></span>' +
|
|
1562
|
+
'</div>' +
|
|
1563
|
+
'<div class="switch-card-desc">命令行执行结果</div>' +
|
|
1564
|
+
'</label>' +
|
|
1565
|
+
'<label class="switch-card" for="cfg-card-thinking">' +
|
|
1566
|
+
'<div class="switch-card-header">' +
|
|
1567
|
+
'<span class="switch-card-title">思考过程 (Thinking)</span>' +
|
|
1568
|
+
'<input id="cfg-card-thinking" type="checkbox" class="switch-toggle" />' +
|
|
1569
|
+
'<span class="switch-slider"></span>' +
|
|
1570
|
+
'</div>' +
|
|
1571
|
+
'<div class="switch-card-desc">Claude 的思考过程块</div>' +
|
|
1572
|
+
'</label>' +
|
|
1573
|
+
'<label class="switch-card" for="cfg-card-toolgroup">' +
|
|
1574
|
+
'<div class="switch-card-header">' +
|
|
1575
|
+
'<span class="switch-card-title">工具组</span>' +
|
|
1576
|
+
'<input id="cfg-card-toolgroup" type="checkbox" class="switch-toggle" />' +
|
|
1577
|
+
'<span class="switch-slider"></span>' +
|
|
1578
|
+
'</div>' +
|
|
1579
|
+
'<div class="switch-card-desc">连续同类工具调用的折叠组</div>' +
|
|
1580
|
+
'</label>' +
|
|
1540
1581
|
'</div>' +
|
|
1541
|
-
'<
|
|
1542
|
-
'<button id="save-display-button" class="btn btn-primary btn-block">保存显示设置</button>' +
|
|
1582
|
+
'<button id="save-display-button" class="btn btn-primary btn-block" style="margin-top:16px">保存显示设置</button>' +
|
|
1543
1583
|
'<p id="display-message" class="hint hidden"></p>' +
|
|
1544
1584
|
'</div>' +
|
|
1545
1585
|
'</div>' +
|
|
@@ -2757,11 +2797,12 @@
|
|
|
2757
2797
|
}
|
|
2758
2798
|
|
|
2759
2799
|
window.__tcToggle = function(e, headerEl) {
|
|
2760
|
-
var card = headerEl.closest(".tool-use-card");
|
|
2800
|
+
var card = headerEl.closest(".tool-use-card") || headerEl.closest(".inline-diff");
|
|
2761
2801
|
if (card) {
|
|
2762
2802
|
var wasCollapsed = card.classList.contains("collapsed");
|
|
2763
2803
|
card.classList.toggle("collapsed");
|
|
2764
|
-
|
|
2804
|
+
var expandKind = card.dataset.expandKind || "tool-card";
|
|
2805
|
+
persistElementExpandState(card, expandKind);
|
|
2765
2806
|
// Lazy-load truncated content on expand
|
|
2766
2807
|
if (wasCollapsed && card.dataset.truncated === "true" && card.dataset.loaded !== "true") {
|
|
2767
2808
|
var toolUseId = card.dataset.toolUseId;
|
|
@@ -2876,6 +2917,7 @@
|
|
|
2876
2917
|
}
|
|
2877
2918
|
});
|
|
2878
2919
|
}
|
|
2920
|
+
}
|
|
2879
2921
|
};
|
|
2880
2922
|
// Update streaming thinking content (called from WebSocket handler)
|
|
2881
2923
|
function updateStreamingThinking(text) {
|
|
@@ -3112,6 +3154,8 @@
|
|
|
3112
3154
|
if (drawerBackdrop) drawerBackdrop.addEventListener("click", closeSessionsDrawer);
|
|
3113
3155
|
var closeDrawerBtn = document.getElementById("close-drawer-button");
|
|
3114
3156
|
if (closeDrawerBtn) closeDrawerBtn.addEventListener("click", closeSessionsDrawer);
|
|
3157
|
+
var pinBtn = document.getElementById("sidebar-pin-btn");
|
|
3158
|
+
if (pinBtn) pinBtn.addEventListener("click", toggleSidebarPin);
|
|
3115
3159
|
var homeBtn = document.getElementById("sidebar-home-btn");
|
|
3116
3160
|
if (homeBtn) homeBtn.addEventListener("click", function() {
|
|
3117
3161
|
state.selectedId = null;
|
|
@@ -3219,6 +3263,35 @@
|
|
|
3219
3263
|
var notifTestBtn = document.getElementById("notification-test-btn");
|
|
3220
3264
|
if (notifTestBtn) notifTestBtn.addEventListener("click", testNotification);
|
|
3221
3265
|
updateNotificationStatus();
|
|
3266
|
+
// Native notification sound selector (APK only)
|
|
3267
|
+
if (_hasNativeBridge && typeof WandNative.getAvailableSounds === "function") {
|
|
3268
|
+
var nativeSoundSection = document.getElementById("native-sound-section");
|
|
3269
|
+
var nativeSoundSelect = document.getElementById("native-sound-select");
|
|
3270
|
+
var nativeSoundPreview = document.getElementById("native-sound-preview");
|
|
3271
|
+
if (nativeSoundSection && nativeSoundSelect) {
|
|
3272
|
+
nativeSoundSection.classList.remove("hidden");
|
|
3273
|
+
try {
|
|
3274
|
+
var sounds = JSON.parse(WandNative.getAvailableSounds());
|
|
3275
|
+
var current = WandNative.getNotificationSound();
|
|
3276
|
+
nativeSoundSelect.innerHTML = "";
|
|
3277
|
+
for (var si = 0; si < sounds.length; si++) {
|
|
3278
|
+
var opt = document.createElement("option");
|
|
3279
|
+
opt.value = sounds[si].id;
|
|
3280
|
+
opt.textContent = sounds[si].name;
|
|
3281
|
+
if (sounds[si].id === current) opt.selected = true;
|
|
3282
|
+
nativeSoundSelect.appendChild(opt);
|
|
3283
|
+
}
|
|
3284
|
+
nativeSoundSelect.addEventListener("change", function() {
|
|
3285
|
+
try { WandNative.setNotificationSound(nativeSoundSelect.value); } catch (_e) {}
|
|
3286
|
+
});
|
|
3287
|
+
if (nativeSoundPreview) {
|
|
3288
|
+
nativeSoundPreview.addEventListener("click", function() {
|
|
3289
|
+
try { WandNative.previewSound(nativeSoundSelect.value); } catch (_e) {}
|
|
3290
|
+
});
|
|
3291
|
+
}
|
|
3292
|
+
} catch (_e) {}
|
|
3293
|
+
}
|
|
3294
|
+
}
|
|
3222
3295
|
var newSessBtn = document.getElementById("topbar-new-session-button");
|
|
3223
3296
|
if (newSessBtn) newSessBtn.addEventListener("click", openSessionModal);
|
|
3224
3297
|
var drawerNewSessBtn = document.getElementById("drawer-new-session-button");
|
|
@@ -3785,7 +3858,9 @@
|
|
|
3785
3858
|
} else {
|
|
3786
3859
|
selectSession(sessionId);
|
|
3787
3860
|
}
|
|
3788
|
-
|
|
3861
|
+
if (!state.sidebarPinned || isMobileLayout()) {
|
|
3862
|
+
closeSessionsDrawer();
|
|
3863
|
+
}
|
|
3789
3864
|
}
|
|
3790
3865
|
|
|
3791
3866
|
function handleSessionItemClick(event) {
|
|
@@ -4290,6 +4365,12 @@
|
|
|
4290
4365
|
} else {
|
|
4291
4366
|
updateTerminalJumpToBottomButton();
|
|
4292
4367
|
}
|
|
4368
|
+
// When switching sessions, re-fit the terminal so the PTY receives
|
|
4369
|
+
// the correct dimensions for this client's viewport.
|
|
4370
|
+
if (sessionChanged && state.fitAddon) {
|
|
4371
|
+
state.terminalViewportSize = { width: 0, height: 0 };
|
|
4372
|
+
scheduleTerminalResize(true);
|
|
4373
|
+
}
|
|
4293
4374
|
return wrote || sessionChanged;
|
|
4294
4375
|
}
|
|
4295
4376
|
|
|
@@ -4364,6 +4445,26 @@
|
|
|
4364
4445
|
state.fitAddon = fitAddonConstructor ? new fitAddonConstructor() : null;
|
|
4365
4446
|
if (state.fitAddon) {
|
|
4366
4447
|
state.terminal.loadAddon(state.fitAddon);
|
|
4448
|
+
// Patch: FitAddon subtracts 14px for a scrollbar that CSS hides;
|
|
4449
|
+
// recalculate cols without the scrollbar deduction.
|
|
4450
|
+
var _origPropose = state.fitAddon.proposeDimensions;
|
|
4451
|
+
state.fitAddon.proposeDimensions = function() {
|
|
4452
|
+
var result = _origPropose.call(state.fitAddon);
|
|
4453
|
+
if (result && state.terminal) {
|
|
4454
|
+
try {
|
|
4455
|
+
var core = state.terminal._core;
|
|
4456
|
+
var cellW = core._renderService.dimensions.css.cell.width;
|
|
4457
|
+
var el = state.terminal.element;
|
|
4458
|
+
if (cellW > 0 && el && el.parentElement) {
|
|
4459
|
+
var pw = Math.max(0, parseInt(window.getComputedStyle(el.parentElement).getPropertyValue("width")));
|
|
4460
|
+
var es = window.getComputedStyle(el);
|
|
4461
|
+
var ePad = parseInt(es.getPropertyValue("padding-left")) + parseInt(es.getPropertyValue("padding-right"));
|
|
4462
|
+
result.cols = Math.max(2, Math.floor((pw - ePad) / cellW));
|
|
4463
|
+
}
|
|
4464
|
+
} catch(e) {}
|
|
4465
|
+
}
|
|
4466
|
+
return result;
|
|
4467
|
+
};
|
|
4367
4468
|
} else {
|
|
4368
4469
|
console.error("[wand] xterm fit addon failed to load; continuing without fit support.");
|
|
4369
4470
|
}
|
|
@@ -4382,6 +4483,24 @@
|
|
|
4382
4483
|
// Retry-based fit: wait for browser to complete layout before measuring and fitting
|
|
4383
4484
|
if (state.fitAddon) {
|
|
4384
4485
|
ensureTerminalFit();
|
|
4486
|
+
// Secondary fit after fonts are loaded — FitAddon measures character
|
|
4487
|
+
// dimensions from the rendered font; if a custom web font (e.g. Geist
|
|
4488
|
+
// Mono) hasn't loaded yet the initial fit() uses fallback metrics and
|
|
4489
|
+
// computes too few columns.
|
|
4490
|
+
if (document.fonts && document.fonts.ready) {
|
|
4491
|
+
document.fonts.ready.then(function() {
|
|
4492
|
+
state.terminalViewportSize = { width: 0, height: 0 };
|
|
4493
|
+
ensureTerminalFit();
|
|
4494
|
+
});
|
|
4495
|
+
}
|
|
4496
|
+
// Safety-net fit after layout has fully stabilised (CSS transitions,
|
|
4497
|
+
// deferred reflows, late font loads, etc.)
|
|
4498
|
+
setTimeout(function() {
|
|
4499
|
+
if (state.terminal && state.fitAddon) {
|
|
4500
|
+
state.terminalViewportSize = { width: 0, height: 0 };
|
|
4501
|
+
ensureTerminalFit();
|
|
4502
|
+
}
|
|
4503
|
+
}, 500);
|
|
4385
4504
|
}
|
|
4386
4505
|
|
|
4387
4506
|
var viewport = getTerminalViewport();
|
|
@@ -5152,6 +5271,22 @@
|
|
|
5152
5271
|
subscribeToSession(id);
|
|
5153
5272
|
}
|
|
5154
5273
|
|
|
5274
|
+
function updatePinState() {
|
|
5275
|
+
var drawer = document.getElementById("sessions-drawer");
|
|
5276
|
+
var mainLayout = document.querySelector(".main-layout");
|
|
5277
|
+
var pinBtn = document.getElementById("sidebar-pin-btn");
|
|
5278
|
+
if (drawer) {
|
|
5279
|
+
drawer.classList.toggle("pinned", state.sidebarPinned && !isMobileLayout());
|
|
5280
|
+
}
|
|
5281
|
+
if (mainLayout) {
|
|
5282
|
+
mainLayout.classList.toggle("sidebar-pinned", state.sidebarPinned && !isMobileLayout());
|
|
5283
|
+
}
|
|
5284
|
+
if (pinBtn) {
|
|
5285
|
+
pinBtn.classList.toggle("pinned", state.sidebarPinned);
|
|
5286
|
+
pinBtn.title = state.sidebarPinned ? "取消固定侧栏" : "固定侧栏";
|
|
5287
|
+
}
|
|
5288
|
+
}
|
|
5289
|
+
|
|
5155
5290
|
function updateDrawerState() {
|
|
5156
5291
|
var drawer = document.getElementById("sessions-drawer");
|
|
5157
5292
|
var backdrop = document.getElementById("sessions-drawer-backdrop");
|
|
@@ -5169,9 +5304,11 @@
|
|
|
5169
5304
|
if (toggleBtn) {
|
|
5170
5305
|
toggleBtn.classList.toggle("active", state.sessionsDrawerOpen);
|
|
5171
5306
|
}
|
|
5307
|
+
updatePinState();
|
|
5172
5308
|
}
|
|
5173
5309
|
|
|
5174
5310
|
function toggleSessionsDrawer() {
|
|
5311
|
+
if (state.sidebarPinned && !isMobileLayout()) return;
|
|
5175
5312
|
state.sessionsDrawerOpen = !state.sessionsDrawerOpen;
|
|
5176
5313
|
if (state.sessionsDrawerOpen && isMobileLayout()) {
|
|
5177
5314
|
state.filePanelOpen = false;
|
|
@@ -5183,12 +5320,38 @@
|
|
|
5183
5320
|
}
|
|
5184
5321
|
|
|
5185
5322
|
function closeSessionsDrawer() {
|
|
5323
|
+
if (state.sidebarPinned && !isMobileLayout()) return;
|
|
5186
5324
|
if (!state.sessionsDrawerOpen) return;
|
|
5187
5325
|
closeSwipedItem();
|
|
5188
5326
|
state.sessionsDrawerOpen = false;
|
|
5189
5327
|
updateLayoutState();
|
|
5190
5328
|
}
|
|
5191
5329
|
|
|
5330
|
+
function toggleSidebarPin() {
|
|
5331
|
+
if (isMobileLayout()) return;
|
|
5332
|
+
state.sidebarPinned = !state.sidebarPinned;
|
|
5333
|
+
try {
|
|
5334
|
+
localStorage.setItem("wand-sidebar-pinned", String(state.sidebarPinned));
|
|
5335
|
+
} catch (e) {}
|
|
5336
|
+
if (state.sidebarPinned) {
|
|
5337
|
+
state.sessionsDrawerOpen = true;
|
|
5338
|
+
}
|
|
5339
|
+
updateLayoutState();
|
|
5340
|
+
// Refit terminal after padding-left transition completes
|
|
5341
|
+
var mainLayout = document.querySelector(".main-layout");
|
|
5342
|
+
if (mainLayout) {
|
|
5343
|
+
var onEnd = function(e) {
|
|
5344
|
+
if (e.propertyName === "padding-left") {
|
|
5345
|
+
mainLayout.removeEventListener("transitionend", onEnd);
|
|
5346
|
+
scheduleTerminalResize(true);
|
|
5347
|
+
}
|
|
5348
|
+
};
|
|
5349
|
+
mainLayout.addEventListener("transitionend", onEnd);
|
|
5350
|
+
}
|
|
5351
|
+
// Fallback refit in case transition doesn't fire
|
|
5352
|
+
setTimeout(function() { scheduleTerminalResize(true); }, 350);
|
|
5353
|
+
}
|
|
5354
|
+
|
|
5192
5355
|
// Store last focused element for focus trap
|
|
5193
5356
|
var lastFocusedElement = null;
|
|
5194
5357
|
var focusTrapHandler = null;
|
|
@@ -5454,6 +5617,13 @@
|
|
|
5454
5617
|
if (typeof WandNative !== "undefined" && typeof WandNative.getAppIcon === "function") {
|
|
5455
5618
|
try { _updateAppIconSelection(WandNative.getAppIcon() || "shorthair"); } catch (_e) {}
|
|
5456
5619
|
}
|
|
5620
|
+
// Sync native notification sound selector (APK only)
|
|
5621
|
+
if (_hasNativeBridge && typeof WandNative.getNotificationSound === "function") {
|
|
5622
|
+
try {
|
|
5623
|
+
var nsSel = document.getElementById("native-sound-select");
|
|
5624
|
+
if (nsSel) nsSel.value = WandNative.getNotificationSound();
|
|
5625
|
+
} catch (_e) {}
|
|
5626
|
+
}
|
|
5457
5627
|
}
|
|
5458
5628
|
}
|
|
5459
5629
|
|
|
@@ -9277,7 +9447,22 @@
|
|
|
9277
9447
|
state.resizeObserver = new ResizeObserver(function() { scheduleTerminalResize(true); });
|
|
9278
9448
|
state.resizeObserver.observe(output);
|
|
9279
9449
|
}
|
|
9280
|
-
|
|
9450
|
+
var lastKnownDesktop = !isMobileLayout();
|
|
9451
|
+
state.resizeHandler = function() {
|
|
9452
|
+
scheduleTerminalResize(true);
|
|
9453
|
+
// Handle sidebar pin state across mobile/desktop breakpoint
|
|
9454
|
+
var isDesktop = !isMobileLayout();
|
|
9455
|
+
if (lastKnownDesktop !== isDesktop) {
|
|
9456
|
+
lastKnownDesktop = isDesktop;
|
|
9457
|
+
if (!isDesktop && state.sidebarPinned && state.sessionsDrawerOpen) {
|
|
9458
|
+
state.sessionsDrawerOpen = false;
|
|
9459
|
+
updateDrawerState();
|
|
9460
|
+
} else if (isDesktop && state.sidebarPinned && !state.sessionsDrawerOpen) {
|
|
9461
|
+
state.sessionsDrawerOpen = true;
|
|
9462
|
+
updateDrawerState();
|
|
9463
|
+
}
|
|
9464
|
+
}
|
|
9465
|
+
};
|
|
9281
9466
|
window.addEventListener("resize", state.resizeHandler);
|
|
9282
9467
|
// Also listen to visualViewport resize for pinch-zoom / browser zoom
|
|
9283
9468
|
if (window.visualViewport) {
|
|
@@ -9347,8 +9532,24 @@
|
|
|
9347
9532
|
updateTerminalJumpToBottomButton();
|
|
9348
9533
|
}
|
|
9349
9534
|
|
|
9535
|
+
function sendTerminalResize(cols, rows) {
|
|
9536
|
+
if (!state.selectedId) return;
|
|
9537
|
+
var selectedSess = state.sessions.find(function(s) { return s.id === state.selectedId; });
|
|
9538
|
+
if (isStructuredSession(selectedSess)) return;
|
|
9539
|
+
var nextSize = { cols: cols, rows: rows };
|
|
9540
|
+
if (state.lastResize.cols !== nextSize.cols || state.lastResize.rows !== nextSize.rows) {
|
|
9541
|
+
state.lastResize = nextSize;
|
|
9542
|
+
fetch("/api/sessions/" + state.selectedId + "/resize", {
|
|
9543
|
+
method: "POST",
|
|
9544
|
+
headers: { "Content-Type": "application/json" },
|
|
9545
|
+
credentials: "same-origin",
|
|
9546
|
+
body: JSON.stringify(nextSize)
|
|
9547
|
+
}).catch(function() {});
|
|
9548
|
+
}
|
|
9549
|
+
}
|
|
9550
|
+
|
|
9350
9551
|
function ensureTerminalFit() {
|
|
9351
|
-
var maxAttempts =
|
|
9552
|
+
var maxAttempts = 20;
|
|
9352
9553
|
var attempt = 0;
|
|
9353
9554
|
function tryFit() {
|
|
9354
9555
|
attempt++;
|
|
@@ -9356,19 +9557,19 @@
|
|
|
9356
9557
|
if (shouldResizeTerminalViewport() && state.fitAddon) {
|
|
9357
9558
|
state.fitAddon.fit();
|
|
9358
9559
|
maybeScrollTerminalToBottom("resize");
|
|
9359
|
-
if (state.
|
|
9360
|
-
|
|
9361
|
-
|
|
9362
|
-
|
|
9363
|
-
|
|
9364
|
-
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9560
|
+
if (state.terminal) {
|
|
9561
|
+
sendTerminalResize(state.terminal.cols, state.terminal.rows);
|
|
9562
|
+
}
|
|
9563
|
+
// Validate: if the fitted cols look suspiciously small relative to
|
|
9564
|
+
// the container width, schedule another attempt — the font metrics
|
|
9565
|
+
// or layout may not have settled yet.
|
|
9566
|
+
var output = document.getElementById("output");
|
|
9567
|
+
if (output && state.terminal) {
|
|
9568
|
+
var containerW = output.getBoundingClientRect().width;
|
|
9569
|
+
var expectedMinCols = Math.floor(containerW / 20); // very conservative estimate
|
|
9570
|
+
if (state.terminal.cols < expectedMinCols && attempt < maxAttempts) {
|
|
9571
|
+
requestAnimationFrame(tryFit);
|
|
9572
|
+
return;
|
|
9372
9573
|
}
|
|
9373
9574
|
}
|
|
9374
9575
|
} else if (attempt < maxAttempts) {
|
|
@@ -9401,27 +9602,7 @@
|
|
|
9401
9602
|
maybeScrollTerminalToBottom("resize");
|
|
9402
9603
|
}
|
|
9403
9604
|
|
|
9404
|
-
|
|
9405
|
-
cols: state.terminal.cols,
|
|
9406
|
-
rows: state.terminal.rows
|
|
9407
|
-
};
|
|
9408
|
-
|
|
9409
|
-
if (!state.selectedId) return;
|
|
9410
|
-
|
|
9411
|
-
// Skip resize for structured sessions (no PTY)
|
|
9412
|
-
var resizeSess = state.sessions.find(function(s) { return s.id === state.selectedId; });
|
|
9413
|
-
if (isStructuredSession(resizeSess)) return;
|
|
9414
|
-
|
|
9415
|
-
// Only send resize API call if dimensions actually changed
|
|
9416
|
-
if (state.lastResize.cols !== nextSize.cols || state.lastResize.rows !== nextSize.rows) {
|
|
9417
|
-
state.lastResize = nextSize;
|
|
9418
|
-
fetch("/api/sessions/" + state.selectedId + "/resize", {
|
|
9419
|
-
method: "POST",
|
|
9420
|
-
headers: { "Content-Type": "application/json" },
|
|
9421
|
-
credentials: "same-origin",
|
|
9422
|
-
body: JSON.stringify(nextSize)
|
|
9423
|
-
}).catch(function() {});
|
|
9424
|
-
}
|
|
9605
|
+
sendTerminalResize(state.terminal.cols, state.terminal.rows);
|
|
9425
9606
|
}
|
|
9426
9607
|
|
|
9427
9608
|
function startPolling() {
|
|
@@ -9464,6 +9645,12 @@
|
|
|
9464
9645
|
subscribeToSession(state.selectedId);
|
|
9465
9646
|
// Flush pending messages after reconnection
|
|
9466
9647
|
flushPendingMessages();
|
|
9648
|
+
// Re-fit terminal on reconnect — the viewport may have changed
|
|
9649
|
+
// while disconnected, and the PTY needs up-to-date dimensions.
|
|
9650
|
+
if (state.terminal && state.fitAddon) {
|
|
9651
|
+
state.terminalViewportSize = { width: 0, height: 0 };
|
|
9652
|
+
ensureTerminalFit();
|
|
9653
|
+
}
|
|
9467
9654
|
};
|
|
9468
9655
|
|
|
9469
9656
|
ws.onmessage = function(event) {
|
|
@@ -10317,7 +10504,7 @@
|
|
|
10317
10504
|
// Only expand the single newest tool card (first chat-message = newest due to column-reverse)
|
|
10318
10505
|
var firstMsg = chatMessages.querySelector(".chat-message:not(.system-info)");
|
|
10319
10506
|
if (firstMsg) {
|
|
10320
|
-
var cards = firstMsg.querySelectorAll(".tool-use-card");
|
|
10507
|
+
var cards = firstMsg.querySelectorAll(".tool-use-card, .inline-diff[data-expand-key]");
|
|
10321
10508
|
if (cards.length > 0) {
|
|
10322
10509
|
var firstCard = cards[0];
|
|
10323
10510
|
var firstCardKey = getElementExpandKey(firstCard);
|
|
@@ -10327,6 +10514,8 @@
|
|
|
10327
10514
|
for (var ci = 1; ci < cards.length; ci++) {
|
|
10328
10515
|
var cardKey = getElementExpandKey(cards[ci]);
|
|
10329
10516
|
if (getPersistedExpandState(cardKey) === null) {
|
|
10517
|
+
// Never collapse unanswered AskUserQuestion cards
|
|
10518
|
+
if (cards[ci].classList.contains("ask-user") && !cards[ci].classList.contains("ask-user-answered")) continue;
|
|
10330
10519
|
cards[ci].classList.add("collapsed");
|
|
10331
10520
|
}
|
|
10332
10521
|
}
|
|
@@ -10342,10 +10531,13 @@
|
|
|
10342
10531
|
// Collapse all tool-use cards except those in the new message elements (marked with animate-in)
|
|
10343
10532
|
// newEls: NodeList/Array of newly added message elements, or null to keep only the first card expanded
|
|
10344
10533
|
function collapseOldToolCards(container, newEls) {
|
|
10345
|
-
var allCards = container.querySelectorAll(".tool-use-card");
|
|
10534
|
+
var allCards = container.querySelectorAll(".tool-use-card, .inline-diff[data-expand-key]");
|
|
10346
10535
|
allCards.forEach(function(c) {
|
|
10347
10536
|
var cardKey = getElementExpandKey(c);
|
|
10348
10537
|
if (getPersistedExpandState(cardKey) !== null) return;
|
|
10538
|
+
// Never collapse unanswered AskUserQuestion cards — the user
|
|
10539
|
+
// needs to interact with the options.
|
|
10540
|
+
if (c.classList.contains("ask-user") && !c.classList.contains("ask-user-answered")) return;
|
|
10349
10541
|
// Keep expanded if this card is inside a newly added message
|
|
10350
10542
|
if (newEls) {
|
|
10351
10543
|
for (var i = 0; i < newEls.length; i++) {
|
|
@@ -10465,11 +10657,13 @@
|
|
|
10465
10657
|
smartScrollToBottom(chatMessages);
|
|
10466
10658
|
});
|
|
10467
10659
|
var newestMsgEl = chatMessages.querySelector(".chat-message");
|
|
10468
|
-
var allCards = chatMessages.querySelectorAll(".tool-use-card");
|
|
10660
|
+
var allCards = chatMessages.querySelectorAll(".tool-use-card, .inline-diff[data-expand-key]");
|
|
10469
10661
|
var newestCard = null;
|
|
10470
10662
|
allCards.forEach(function(c) {
|
|
10471
10663
|
var cardKey = getElementExpandKey(c);
|
|
10472
10664
|
if (getPersistedExpandState(cardKey) !== null) return;
|
|
10665
|
+
// Never collapse unanswered AskUserQuestion cards
|
|
10666
|
+
if (c.classList.contains("ask-user") && !c.classList.contains("ask-user-answered")) return;
|
|
10473
10667
|
if (newestMsgEl && newestMsgEl.contains(c)) {
|
|
10474
10668
|
if (!newestCard) newestCard = c;
|
|
10475
10669
|
else c.classList.add("collapsed");
|
|
@@ -12029,10 +12223,11 @@
|
|
|
12029
12223
|
return "";
|
|
12030
12224
|
}
|
|
12031
12225
|
|
|
12032
|
-
function renderDiffTool(block, toolResult, toolName) {
|
|
12226
|
+
function renderDiffTool(block, toolResult, toolName, messageKey, index) {
|
|
12033
12227
|
var inputData = block.input || {};
|
|
12034
12228
|
var path = inputData.file_path || inputData.path || "";
|
|
12035
12229
|
var fileName = path.split("/").pop() || path;
|
|
12230
|
+
var toolId = block.id || "tool-" + toolName + "-" + (typeof index === "number" ? index : 0);
|
|
12036
12231
|
|
|
12037
12232
|
var oldStr = inputData.old_string || "";
|
|
12038
12233
|
var newStr = inputData.new_string || inputData.content || "";
|
|
@@ -12077,16 +12272,26 @@
|
|
|
12077
12272
|
statusText = "执行中";
|
|
12078
12273
|
}
|
|
12079
12274
|
|
|
12275
|
+
// Expand state: respect cardDefaults.editCards and persisted state
|
|
12276
|
+
var expandKey = buildExpandKey("diff", [messageKey, toolId || index, index]);
|
|
12277
|
+
var persistedExpanded = getPersistedExpandState(expandKey);
|
|
12278
|
+
var cardDefaultExpand = !!(state.config && state.config.cardDefaults && state.config.cardDefaults.editCards);
|
|
12279
|
+
var shouldExpand = persistedExpanded === null ? (statusClass === "diff-pending" || cardDefaultExpand) : persistedExpanded;
|
|
12280
|
+
var collapsedClass = shouldExpand ? "" : " collapsed";
|
|
12281
|
+
|
|
12080
12282
|
// If only one column has content, show full width
|
|
12081
12283
|
var bothCols = leftCol && rightCol;
|
|
12082
12284
|
var colClass = bothCols ? "diff-col-half" : "diff-col-full";
|
|
12083
12285
|
|
|
12084
|
-
return '<div class="inline-diff" data-tool-name="' + escapeHtml(toolName) + '"
|
|
12085
|
-
'
|
|
12286
|
+
return '<div class="inline-diff' + collapsedClass + '" data-tool-name="' + escapeHtml(toolName) + '"' +
|
|
12287
|
+
' data-expand-kind="diff" data-expand-key="' + escapeHtml(expandKey) + '"' +
|
|
12288
|
+
' data-tool-use-id="' + escapeHtml(toolId) + '">' +
|
|
12289
|
+
'<div class="diff-header" onclick="__tcToggle(event,this)">' +
|
|
12086
12290
|
'<span class="diff-file-icon"></span>' +
|
|
12087
12291
|
'<span class="diff-file-name">' + escapeHtml(fileName) + '</span>' +
|
|
12088
12292
|
'<span class="diff-path">' + escapeHtml(path) + '</span>' +
|
|
12089
12293
|
'<span class="diff-status ' + statusClass + '">' + statusText + '</span>' +
|
|
12294
|
+
'<span class="diff-toggle">▼</span>' +
|
|
12090
12295
|
'</div>' +
|
|
12091
12296
|
'<div class="diff-body">' +
|
|
12092
12297
|
'<div class="diff-columns">' +
|
|
@@ -12120,7 +12325,7 @@
|
|
|
12120
12325
|
|
|
12121
12326
|
// ── Diff-style: Edit, Write, MultiEdit
|
|
12122
12327
|
if (toolName === "Edit" || toolName === "Write" || toolName === "MultiEdit") {
|
|
12123
|
-
return renderDiffTool(block, toolResult, toolName);
|
|
12328
|
+
return renderDiffTool(block, toolResult, toolName, messageKey, index);
|
|
12124
12329
|
}
|
|
12125
12330
|
|
|
12126
12331
|
// ── AskUserQuestion tool — special card with batch submit
|
|
@@ -370,12 +370,20 @@
|
|
|
370
370
|
transition: padding-left var(--transition-normal);
|
|
371
371
|
}
|
|
372
372
|
/* .sidebar-open class toggled for semantic purposes only; sidebar overlays without resizing main layout */
|
|
373
|
-
.sidebar-open .input-panel {
|
|
373
|
+
.sidebar-open:not(.sidebar-pinned) .input-panel {
|
|
374
374
|
opacity: 0;
|
|
375
375
|
pointer-events: none;
|
|
376
376
|
transition: opacity 0.2s ease;
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
+
/* ===== 侧边栏常驻 ===== */
|
|
380
|
+
.main-layout.sidebar-pinned {
|
|
381
|
+
padding-left: var(--sidebar-width);
|
|
382
|
+
}
|
|
383
|
+
.main-layout.sidebar-pinned .floating-sidebar-toggle {
|
|
384
|
+
display: none;
|
|
385
|
+
}
|
|
386
|
+
|
|
379
387
|
/* ===== 抽屉背景遮罩 ===== */
|
|
380
388
|
.drawer-backdrop {
|
|
381
389
|
position: fixed;
|
|
@@ -426,6 +434,27 @@
|
|
|
426
434
|
opacity: 1;
|
|
427
435
|
}
|
|
428
436
|
|
|
437
|
+
.sidebar.pinned {
|
|
438
|
+
transform: translateX(0);
|
|
439
|
+
pointer-events: auto;
|
|
440
|
+
opacity: 1;
|
|
441
|
+
box-shadow: none;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.sidebar.pinned .sidebar-close {
|
|
445
|
+
display: none;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/* ===== 图钉按钮 ===== */
|
|
449
|
+
.sidebar-pin-toggle {
|
|
450
|
+
flex-shrink: 0;
|
|
451
|
+
transition: transform var(--transition-fast), color var(--transition-fast);
|
|
452
|
+
}
|
|
453
|
+
.sidebar-pin-toggle.pinned {
|
|
454
|
+
color: var(--primary);
|
|
455
|
+
transform: rotate(45deg);
|
|
456
|
+
}
|
|
457
|
+
|
|
429
458
|
/* ===== 侧边栏头部 ===== */
|
|
430
459
|
.sidebar-header {
|
|
431
460
|
display: flex;
|
|
@@ -2139,7 +2168,6 @@
|
|
|
2139
2168
|
radial-gradient(circle at top right, rgba(91, 58, 34, 0.2), transparent 35%),
|
|
2140
2169
|
radial-gradient(circle at bottom left, rgba(197, 101, 61, 0.08), transparent 40%),
|
|
2141
2170
|
linear-gradient(180deg, #2a221c 0%, #1f1b17 50%, #1a1613 100%);
|
|
2142
|
-
padding: 10px;
|
|
2143
2171
|
overflow: hidden;
|
|
2144
2172
|
min-height: 0;
|
|
2145
2173
|
min-width: 0;
|
|
@@ -2165,8 +2193,6 @@
|
|
|
2165
2193
|
left: 0;
|
|
2166
2194
|
right: 0;
|
|
2167
2195
|
bottom: 0;
|
|
2168
|
-
width: 100%;
|
|
2169
|
-
height: 100%;
|
|
2170
2196
|
padding: 0;
|
|
2171
2197
|
overflow: hidden;
|
|
2172
2198
|
}
|
|
@@ -4084,6 +4110,26 @@
|
|
|
4084
4110
|
padding: 6px 10px;
|
|
4085
4111
|
background: rgba(0, 0, 0, 0.03);
|
|
4086
4112
|
font-size: 0.75rem;
|
|
4113
|
+
cursor: pointer;
|
|
4114
|
+
}
|
|
4115
|
+
.diff-header:hover {
|
|
4116
|
+
background: rgba(0, 0, 0, 0.05);
|
|
4117
|
+
}
|
|
4118
|
+
.diff-toggle {
|
|
4119
|
+
font-size: 0.625rem;
|
|
4120
|
+
color: var(--text-muted);
|
|
4121
|
+
transition: transform 0.3s var(--ease-spring);
|
|
4122
|
+
flex-shrink: 0;
|
|
4123
|
+
margin-left: auto;
|
|
4124
|
+
}
|
|
4125
|
+
.inline-diff.collapsed .diff-toggle {
|
|
4126
|
+
transform: rotate(-90deg);
|
|
4127
|
+
}
|
|
4128
|
+
.inline-diff.collapsed .diff-body {
|
|
4129
|
+
max-height: 0;
|
|
4130
|
+
overflow: hidden;
|
|
4131
|
+
opacity: 0;
|
|
4132
|
+
transition: max-height 0.35s var(--ease-out-expo), opacity 0.25s ease;
|
|
4087
4133
|
}
|
|
4088
4134
|
.diff-file-icon {
|
|
4089
4135
|
display: none;
|
|
@@ -6068,6 +6114,18 @@
|
|
|
6068
6114
|
|
|
6069
6115
|
/* 平板适配 */
|
|
6070
6116
|
@media (max-width: 768px) {
|
|
6117
|
+
.sidebar-pin-toggle { display: none; }
|
|
6118
|
+
.sidebar.pinned:not(.open) {
|
|
6119
|
+
transform: translateX(-100%);
|
|
6120
|
+
pointer-events: none;
|
|
6121
|
+
opacity: 0;
|
|
6122
|
+
}
|
|
6123
|
+
.main-layout.sidebar-pinned {
|
|
6124
|
+
padding-left: 0;
|
|
6125
|
+
}
|
|
6126
|
+
.main-layout.sidebar-pinned .floating-sidebar-toggle {
|
|
6127
|
+
display: inline-flex;
|
|
6128
|
+
}
|
|
6071
6129
|
.app-container {
|
|
6072
6130
|
--layout-main-file-panel-width: 0px;
|
|
6073
6131
|
}
|
|
@@ -7473,6 +7531,93 @@
|
|
|
7473
7531
|
cursor: pointer;
|
|
7474
7532
|
}
|
|
7475
7533
|
|
|
7534
|
+
/* Switch card list */
|
|
7535
|
+
.switch-card-list {
|
|
7536
|
+
display: flex;
|
|
7537
|
+
flex-direction: column;
|
|
7538
|
+
gap: 8px;
|
|
7539
|
+
}
|
|
7540
|
+
.switch-card {
|
|
7541
|
+
display: block;
|
|
7542
|
+
background: var(--bg-secondary);
|
|
7543
|
+
border: 1px solid var(--border);
|
|
7544
|
+
border-radius: 10px;
|
|
7545
|
+
padding: 12px 14px;
|
|
7546
|
+
cursor: pointer;
|
|
7547
|
+
transition: border-color 0.2s, background 0.2s;
|
|
7548
|
+
user-select: none;
|
|
7549
|
+
}
|
|
7550
|
+
.switch-card:hover {
|
|
7551
|
+
border-color: var(--text-muted);
|
|
7552
|
+
}
|
|
7553
|
+
.switch-card-header {
|
|
7554
|
+
display: flex;
|
|
7555
|
+
align-items: center;
|
|
7556
|
+
gap: 10px;
|
|
7557
|
+
}
|
|
7558
|
+
.switch-card-title {
|
|
7559
|
+
flex: 1;
|
|
7560
|
+
font-size: 0.8125rem;
|
|
7561
|
+
font-weight: 600;
|
|
7562
|
+
color: var(--text-primary);
|
|
7563
|
+
}
|
|
7564
|
+
.switch-card-desc {
|
|
7565
|
+
font-size: 0.75rem;
|
|
7566
|
+
color: var(--text-muted);
|
|
7567
|
+
margin-top: 0;
|
|
7568
|
+
max-height: 0;
|
|
7569
|
+
overflow: hidden;
|
|
7570
|
+
opacity: 0;
|
|
7571
|
+
transition: max-height 0.25s ease, opacity 0.2s ease, margin-top 0.25s ease;
|
|
7572
|
+
}
|
|
7573
|
+
/* When switch is ON, expand the description */
|
|
7574
|
+
.switch-card:has(.switch-toggle:checked) .switch-card-desc {
|
|
7575
|
+
max-height: 40px;
|
|
7576
|
+
opacity: 1;
|
|
7577
|
+
margin-top: 8px;
|
|
7578
|
+
}
|
|
7579
|
+
.switch-card:has(.switch-toggle:checked) {
|
|
7580
|
+
border-color: var(--accent);
|
|
7581
|
+
background: color-mix(in srgb, var(--accent) 6%, var(--bg-secondary));
|
|
7582
|
+
}
|
|
7583
|
+
|
|
7584
|
+
/* Switch toggle (iOS style) */
|
|
7585
|
+
.switch-toggle {
|
|
7586
|
+
position: absolute;
|
|
7587
|
+
opacity: 0;
|
|
7588
|
+
width: 0;
|
|
7589
|
+
height: 0;
|
|
7590
|
+
pointer-events: none;
|
|
7591
|
+
}
|
|
7592
|
+
.switch-slider {
|
|
7593
|
+
position: relative;
|
|
7594
|
+
display: inline-block;
|
|
7595
|
+
width: 40px;
|
|
7596
|
+
height: 22px;
|
|
7597
|
+
min-width: 40px;
|
|
7598
|
+
background: #c4b8a8;
|
|
7599
|
+
border-radius: 11px;
|
|
7600
|
+
transition: background 0.25s;
|
|
7601
|
+
}
|
|
7602
|
+
.switch-slider::after {
|
|
7603
|
+
content: "";
|
|
7604
|
+
position: absolute;
|
|
7605
|
+
top: 3px;
|
|
7606
|
+
left: 3px;
|
|
7607
|
+
width: 16px;
|
|
7608
|
+
height: 16px;
|
|
7609
|
+
background: #fff;
|
|
7610
|
+
border-radius: 50%;
|
|
7611
|
+
transition: transform 0.25s;
|
|
7612
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
|
|
7613
|
+
}
|
|
7614
|
+
.switch-toggle:checked + .switch-slider {
|
|
7615
|
+
background: var(--accent);
|
|
7616
|
+
}
|
|
7617
|
+
.switch-toggle:checked + .switch-slider::after {
|
|
7618
|
+
transform: translateX(18px);
|
|
7619
|
+
}
|
|
7620
|
+
|
|
7476
7621
|
.field-file {
|
|
7477
7622
|
font-size: 0.8rem;
|
|
7478
7623
|
padding: 6px;
|