claude-relay 2.2.3 → 2.3.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/README.md +12 -0
- package/bin/cli.js +271 -22
- package/lib/config.js +39 -2
- package/lib/daemon.js +53 -1
- package/lib/ipc.js +7 -3
- package/lib/pages.js +15 -1
- package/lib/project.js +324 -27
- package/lib/public/app.js +313 -7
- package/lib/public/css/base.css +5 -0
- package/lib/public/css/diff.css +128 -0
- package/lib/public/css/filebrowser.css +541 -0
- package/lib/public/css/input.css +1 -0
- package/lib/public/css/menus.css +89 -5
- package/lib/public/css/messages.css +84 -49
- package/lib/public/css/overlays.css +40 -0
- package/lib/public/index.html +100 -17
- package/lib/public/modules/diff.js +398 -0
- package/lib/public/modules/filebrowser.js +1023 -11
- package/lib/public/modules/input.js +96 -2
- package/lib/public/modules/notifications.js +29 -3
- package/lib/public/modules/qrcode.js +11 -2
- package/lib/public/modules/rewind.js +51 -2
- package/lib/public/modules/tools.js +43 -104
- package/lib/public/modules/utils.js +10 -2
- package/lib/public/style.css +1 -0
- package/lib/public/sw.js +21 -7
- package/lib/push.js +5 -1
- package/lib/sdk-bridge.js +40 -7
- package/lib/server.js +37 -4
- package/lib/sessions.js +14 -5
- package/lib/terminal.js +2 -1
- package/package.json +1 -1
package/lib/public/app.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { copyToClipboard, escapeHtml } from './modules/utils.js';
|
|
1
|
+
import { showToast, copyToClipboard, escapeHtml } from './modules/utils.js';
|
|
2
2
|
import { refreshIcons, iconHtml, randomThinkingVerb } from './modules/icons.js';
|
|
3
3
|
import { renderMarkdown, highlightCodeBlocks, renderMermaidBlocks, closeMermaidModal } from './modules/markdown.js';
|
|
4
4
|
import { initSidebar, renderSessionList, handleSearchResults, updatePageTitle, getActiveSearchQuery, buildSearchTimeline, removeSearchTimeline } from './modules/sidebar.js';
|
|
5
5
|
import { initRewind, setRewindMode, showRewindModal, clearPendingRewindUuid } from './modules/rewind.js';
|
|
6
6
|
import { initNotifications, showDoneNotification, playDoneSound, isNotifAlertEnabled, isNotifSoundEnabled } from './modules/notifications.js';
|
|
7
|
-
import { initInput, clearPendingImages, handleInputSync, autoResize } from './modules/input.js';
|
|
7
|
+
import { initInput, clearPendingImages, handleInputSync, autoResize, builtinCommands } from './modules/input.js';
|
|
8
8
|
import { initQrCode } from './modules/qrcode.js';
|
|
9
|
-
import { initFileBrowser, loadRootDirectory, handleFsList, handleFsRead, refreshIfOpen, handleFileChanged, closeFileViewer } from './modules/filebrowser.js';
|
|
9
|
+
import { initFileBrowser, loadRootDirectory, refreshTree, handleFsList, handleFsRead, handleDirChanged, refreshIfOpen, handleFileChanged, handleFileHistory, handleGitDiff, handleFileAt, getPendingNavigate, closeFileViewer } from './modules/filebrowser.js';
|
|
10
10
|
import { initTerminal, openTerminal, closeTerminal, resetTerminals, handleTermList, handleTermCreated, handleTermOutput, handleTermExited, handleTermClosed } from './modules/terminal.js';
|
|
11
11
|
import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUserQuestion, markAskUserAnswered, renderPermissionRequest, markPermissionResolved, markPermissionCancelled, renderPlanBanner, renderPlanCard, handleTodoWrite, handleTaskCreate, handleTaskUpdate, startThinking, appendThinking, stopThinking, createToolItem, updateToolExecuting, updateToolResult, markAllToolsDone, addTurnMeta, enableMainInput, getTools, getPlanContent, setPlanContent, isPlanFilePath, getTodoTools } from './modules/tools.js';
|
|
12
12
|
|
|
@@ -137,7 +137,10 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
137
137
|
});
|
|
138
138
|
|
|
139
139
|
document.addEventListener("keydown", function (e) {
|
|
140
|
-
if (e.key === "Escape")
|
|
140
|
+
if (e.key === "Escape") {
|
|
141
|
+
closeProjectDropdown();
|
|
142
|
+
closeImageModal();
|
|
143
|
+
}
|
|
141
144
|
});
|
|
142
145
|
|
|
143
146
|
if (projectHintDismiss) {
|
|
@@ -156,6 +159,22 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
156
159
|
});
|
|
157
160
|
$("mermaid-modal").querySelector(".confirm-backdrop").addEventListener("click", closeMermaidModal);
|
|
158
161
|
$("mermaid-modal").querySelector(".mermaid-modal-btn[title='Close']").addEventListener("click", closeMermaidModal);
|
|
162
|
+
$("image-modal").querySelector(".confirm-backdrop").addEventListener("click", closeImageModal);
|
|
163
|
+
$("image-modal").querySelector(".image-modal-close").addEventListener("click", closeImageModal);
|
|
164
|
+
|
|
165
|
+
function showImageModal(src) {
|
|
166
|
+
var modal = $("image-modal");
|
|
167
|
+
var img = $("image-modal-img");
|
|
168
|
+
if (!modal || !img) return;
|
|
169
|
+
img.src = src;
|
|
170
|
+
modal.classList.remove("hidden");
|
|
171
|
+
refreshIcons(modal);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function closeImageModal() {
|
|
175
|
+
var modal = $("image-modal");
|
|
176
|
+
if (modal) modal.classList.add("hidden");
|
|
177
|
+
}
|
|
159
178
|
|
|
160
179
|
// --- State ---
|
|
161
180
|
var ws = null;
|
|
@@ -657,6 +676,229 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
657
676
|
});
|
|
658
677
|
}
|
|
659
678
|
|
|
679
|
+
// --- Status panel ---
|
|
680
|
+
var statusPanel = $("status-panel");
|
|
681
|
+
var statusPanelClose = $("status-panel-close");
|
|
682
|
+
var statusPidEl = $("status-pid");
|
|
683
|
+
var statusUptimeEl = $("status-uptime");
|
|
684
|
+
var statusRssEl = $("status-rss");
|
|
685
|
+
var statusHeapUsedEl = $("status-heap-used");
|
|
686
|
+
var statusHeapTotalEl = $("status-heap-total");
|
|
687
|
+
var statusExternalEl = $("status-external");
|
|
688
|
+
var statusSessionsEl = $("status-sessions");
|
|
689
|
+
var statusProcessingEl = $("status-processing");
|
|
690
|
+
var statusClientsEl = $("status-clients");
|
|
691
|
+
var statusTerminalsEl = $("status-terminals");
|
|
692
|
+
var statusRefreshTimer = null;
|
|
693
|
+
|
|
694
|
+
function formatBytes(n) {
|
|
695
|
+
if (n >= 1073741824) return (n / 1073741824).toFixed(1) + " GB";
|
|
696
|
+
if (n >= 1048576) return (n / 1048576).toFixed(1) + " MB";
|
|
697
|
+
if (n >= 1024) return (n / 1024).toFixed(1) + " KB";
|
|
698
|
+
return n + " B";
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function formatUptime(seconds) {
|
|
702
|
+
var d = Math.floor(seconds / 86400);
|
|
703
|
+
var h = Math.floor((seconds % 86400) / 3600);
|
|
704
|
+
var m = Math.floor((seconds % 3600) / 60);
|
|
705
|
+
var s = Math.floor(seconds % 60);
|
|
706
|
+
if (d > 0) return d + "d " + h + "h " + m + "m";
|
|
707
|
+
if (h > 0) return h + "h " + m + "m " + s + "s";
|
|
708
|
+
return m + "m " + s + "s";
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function updateStatusPanel(data) {
|
|
712
|
+
if (!statusPidEl) return;
|
|
713
|
+
statusPidEl.textContent = String(data.pid);
|
|
714
|
+
statusUptimeEl.textContent = formatUptime(data.uptime);
|
|
715
|
+
statusRssEl.textContent = formatBytes(data.memory.rss);
|
|
716
|
+
statusHeapUsedEl.textContent = formatBytes(data.memory.heapUsed);
|
|
717
|
+
statusHeapTotalEl.textContent = formatBytes(data.memory.heapTotal);
|
|
718
|
+
statusExternalEl.textContent = formatBytes(data.memory.external);
|
|
719
|
+
statusSessionsEl.textContent = String(data.sessions);
|
|
720
|
+
statusProcessingEl.textContent = String(data.processing);
|
|
721
|
+
statusClientsEl.textContent = String(data.clients);
|
|
722
|
+
statusTerminalsEl.textContent = String(data.terminals);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function requestProcessStats() {
|
|
726
|
+
if (ws && ws.readyState === 1) {
|
|
727
|
+
ws.send(JSON.stringify({ type: "process_stats" }));
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
function toggleStatusPanel() {
|
|
732
|
+
if (!statusPanel) return;
|
|
733
|
+
var opening = statusPanel.classList.contains("hidden");
|
|
734
|
+
statusPanel.classList.toggle("hidden");
|
|
735
|
+
if (opening) {
|
|
736
|
+
requestProcessStats();
|
|
737
|
+
statusRefreshTimer = setInterval(requestProcessStats, 5000);
|
|
738
|
+
} else {
|
|
739
|
+
if (statusRefreshTimer) {
|
|
740
|
+
clearInterval(statusRefreshTimer);
|
|
741
|
+
statusRefreshTimer = null;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
refreshIcons();
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
if (statusPanelClose) {
|
|
748
|
+
statusPanelClose.addEventListener("click", function () {
|
|
749
|
+
statusPanel.classList.add("hidden");
|
|
750
|
+
if (statusRefreshTimer) {
|
|
751
|
+
clearInterval(statusRefreshTimer);
|
|
752
|
+
statusRefreshTimer = null;
|
|
753
|
+
}
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// --- Context panel ---
|
|
758
|
+
var contextPanel = $("context-panel");
|
|
759
|
+
var contextPanelClose = $("context-panel-close");
|
|
760
|
+
var contextPanelMinimize = $("context-panel-minimize");
|
|
761
|
+
var contextBarFill = $("context-bar-fill");
|
|
762
|
+
var contextBarPct = $("context-bar-pct");
|
|
763
|
+
var contextUsedEl = $("context-used");
|
|
764
|
+
var contextWindowEl = $("context-window");
|
|
765
|
+
var contextMaxOutputEl = $("context-max-output");
|
|
766
|
+
var contextInputEl = $("context-input");
|
|
767
|
+
var contextOutputEl = $("context-output");
|
|
768
|
+
var contextCacheReadEl = $("context-cache-read");
|
|
769
|
+
var contextCacheWriteEl = $("context-cache-write");
|
|
770
|
+
var contextModelEl = $("context-model");
|
|
771
|
+
var contextCostEl = $("context-cost");
|
|
772
|
+
var contextTurnsEl = $("context-turns");
|
|
773
|
+
var contextMini = $("context-mini");
|
|
774
|
+
var contextMiniFill = $("context-mini-fill");
|
|
775
|
+
var contextMiniLabel = $("context-mini-label");
|
|
776
|
+
var contextData = { contextWindow: 0, maxOutputTokens: 0, model: "-", cost: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, turns: 0 };
|
|
777
|
+
|
|
778
|
+
function contextPctClass(pct) {
|
|
779
|
+
return pct >= 85 ? " danger" : pct >= 60 ? " warn" : "";
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
function updateContextPanel() {
|
|
783
|
+
if (!contextUsedEl) return;
|
|
784
|
+
// Context window usage = input tokens (includes cache read/write) + output tokens
|
|
785
|
+
var used = contextData.input + contextData.output;
|
|
786
|
+
var win = contextData.contextWindow;
|
|
787
|
+
var pct = win > 0 ? Math.min(100, (used / win) * 100) : 0;
|
|
788
|
+
var cls = contextPctClass(pct);
|
|
789
|
+
// Panel bar
|
|
790
|
+
contextBarFill.style.width = pct.toFixed(1) + "%";
|
|
791
|
+
contextBarFill.className = "context-bar-fill" + cls;
|
|
792
|
+
contextBarPct.textContent = pct.toFixed(0) + "%";
|
|
793
|
+
// Mini bar
|
|
794
|
+
if (contextMiniFill) {
|
|
795
|
+
contextMiniFill.style.width = pct.toFixed(1) + "%";
|
|
796
|
+
contextMiniFill.className = "context-mini-fill" + cls;
|
|
797
|
+
}
|
|
798
|
+
if (contextMiniLabel) {
|
|
799
|
+
contextMiniLabel.textContent = (win > 0 ? formatTokens(used) + "/" + formatTokens(win) : "0%");
|
|
800
|
+
}
|
|
801
|
+
contextUsedEl.textContent = formatTokens(used);
|
|
802
|
+
contextWindowEl.textContent = win > 0 ? formatTokens(win) : "-";
|
|
803
|
+
contextMaxOutputEl.textContent = contextData.maxOutputTokens > 0 ? formatTokens(contextData.maxOutputTokens) : "-";
|
|
804
|
+
contextInputEl.textContent = formatTokens(contextData.input);
|
|
805
|
+
contextOutputEl.textContent = formatTokens(contextData.output);
|
|
806
|
+
contextCacheReadEl.textContent = formatTokens(contextData.cacheRead);
|
|
807
|
+
contextCacheWriteEl.textContent = formatTokens(contextData.cacheWrite);
|
|
808
|
+
contextModelEl.textContent = contextData.model;
|
|
809
|
+
contextCostEl.textContent = "$" + contextData.cost.toFixed(4);
|
|
810
|
+
contextTurnsEl.textContent = String(contextData.turns);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
function accumulateContext(cost, usage, modelUsage) {
|
|
814
|
+
if (cost != null) contextData.cost += cost;
|
|
815
|
+
// Use latest turn values (not cumulative) since each turn's input_tokens
|
|
816
|
+
// already includes the full conversation context up to that point
|
|
817
|
+
if (usage) {
|
|
818
|
+
contextData.input = usage.input_tokens || usage.inputTokens || 0;
|
|
819
|
+
contextData.output = usage.output_tokens || usage.outputTokens || 0;
|
|
820
|
+
contextData.cacheRead = usage.cache_read_input_tokens || usage.cacheReadInputTokens || 0;
|
|
821
|
+
contextData.cacheWrite = usage.cache_creation_input_tokens || usage.cacheCreationInputTokens || 0;
|
|
822
|
+
}
|
|
823
|
+
contextData.turns++;
|
|
824
|
+
if (modelUsage) {
|
|
825
|
+
var models = Object.keys(modelUsage);
|
|
826
|
+
if (models.length > 0) {
|
|
827
|
+
var m = models[0];
|
|
828
|
+
var mu = modelUsage[m];
|
|
829
|
+
contextData.model = m;
|
|
830
|
+
if (mu.contextWindow) contextData.contextWindow = mu.contextWindow;
|
|
831
|
+
if (mu.maxOutputTokens) contextData.maxOutputTokens = mu.maxOutputTokens;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
updateContextPanel();
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// contextView: "off" | "mini" | "panel"
|
|
838
|
+
function getContextView() {
|
|
839
|
+
try { return localStorage.getItem("claude-relay-context-view") || "off"; } catch (e) { return "off"; }
|
|
840
|
+
}
|
|
841
|
+
function setContextView(v) {
|
|
842
|
+
try { localStorage.setItem("claude-relay-context-view", v); } catch (e) {}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
function applyContextView(view) {
|
|
846
|
+
if (contextPanel) contextPanel.classList.toggle("hidden", view !== "panel");
|
|
847
|
+
if (contextMini) contextMini.classList.toggle("hidden", view !== "mini");
|
|
848
|
+
if (view === "panel") refreshIcons();
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function resetContextData() {
|
|
852
|
+
contextData = { contextWindow: 0, maxOutputTokens: 0, model: "-", cost: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, turns: 0 };
|
|
853
|
+
updateContextPanel();
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
function resetContext() {
|
|
857
|
+
resetContextData();
|
|
858
|
+
// Keep view state, just reset data
|
|
859
|
+
applyContextView(getContextView());
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
function minimizeContext() {
|
|
863
|
+
setContextView("mini");
|
|
864
|
+
applyContextView("mini");
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
function expandContext() {
|
|
868
|
+
setContextView("panel");
|
|
869
|
+
applyContextView("panel");
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
function toggleContextPanel() {
|
|
873
|
+
if (!contextPanel) return;
|
|
874
|
+
var view = getContextView();
|
|
875
|
+
if (view === "panel") {
|
|
876
|
+
setContextView("mini");
|
|
877
|
+
applyContextView("mini");
|
|
878
|
+
} else {
|
|
879
|
+
setContextView("panel");
|
|
880
|
+
applyContextView("panel");
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if (contextPanelClose) {
|
|
885
|
+
contextPanelClose.addEventListener("click", function () {
|
|
886
|
+
setContextView("off");
|
|
887
|
+
applyContextView("off");
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
if (contextPanelMinimize) {
|
|
892
|
+
contextPanelMinimize.addEventListener("click", minimizeContext);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// Restore context view on load
|
|
896
|
+
applyContextView(getContextView());
|
|
897
|
+
|
|
898
|
+
if (contextMini) {
|
|
899
|
+
contextMini.addEventListener("click", expandContext);
|
|
900
|
+
}
|
|
901
|
+
|
|
660
902
|
function addToMessages(el) {
|
|
661
903
|
if (prependAnchor) messagesEl.insertBefore(el, prependAnchor);
|
|
662
904
|
else messagesEl.appendChild(el);
|
|
@@ -740,6 +982,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
740
982
|
var img = document.createElement("img");
|
|
741
983
|
img.src = "data:" + images[i].mediaType + ";base64," + images[i].data;
|
|
742
984
|
img.className = "bubble-img";
|
|
985
|
+
img.addEventListener("click", function () { showImageModal(this.src); });
|
|
743
986
|
imgRow.appendChild(img);
|
|
744
987
|
}
|
|
745
988
|
bubble.appendChild(imgRow);
|
|
@@ -894,6 +1137,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
894
1137
|
setStatus("connected");
|
|
895
1138
|
enableMainInput();
|
|
896
1139
|
resetUsage();
|
|
1140
|
+
resetContext();
|
|
897
1141
|
}
|
|
898
1142
|
|
|
899
1143
|
// --- WebSocket ---
|
|
@@ -1002,6 +1246,22 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1002
1246
|
if (pendingQuery) {
|
|
1003
1247
|
requestAnimationFrame(function() { buildSearchTimeline(pendingQuery); });
|
|
1004
1248
|
}
|
|
1249
|
+
// Scroll to tool element if navigating from file edit history
|
|
1250
|
+
var nav = getPendingNavigate();
|
|
1251
|
+
if (nav && (nav.toolId || nav.assistantUuid)) {
|
|
1252
|
+
requestAnimationFrame(function() {
|
|
1253
|
+
// Prefer scrolling to the exact tool element
|
|
1254
|
+
var target = nav.toolId ? messagesEl.querySelector('[data-tool-id="' + nav.toolId + '"]') : null;
|
|
1255
|
+
if (!target && nav.assistantUuid) {
|
|
1256
|
+
target = messagesEl.querySelector('[data-uuid="' + nav.assistantUuid + '"]');
|
|
1257
|
+
}
|
|
1258
|
+
if (target) {
|
|
1259
|
+
target.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
1260
|
+
target.classList.add("message-blink");
|
|
1261
|
+
setTimeout(function() { target.classList.remove("message-blink"); }, 2000);
|
|
1262
|
+
}
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1005
1265
|
break;
|
|
1006
1266
|
|
|
1007
1267
|
case "info":
|
|
@@ -1017,6 +1277,11 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1017
1277
|
var debugWrap = $("debug-menu-wrap");
|
|
1018
1278
|
if (debugWrap) debugWrap.classList.remove("hidden");
|
|
1019
1279
|
}
|
|
1280
|
+
if (msg.lanHost) window.__lanHost = msg.lanHost;
|
|
1281
|
+
if (msg.dangerouslySkipPermissions) {
|
|
1282
|
+
var spBanner = $("skip-perms-banner");
|
|
1283
|
+
if (spBanner) spBanner.classList.remove("hidden");
|
|
1284
|
+
}
|
|
1020
1285
|
updateProjectSwitcher(msg);
|
|
1021
1286
|
break;
|
|
1022
1287
|
|
|
@@ -1045,7 +1310,10 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1045
1310
|
break;
|
|
1046
1311
|
|
|
1047
1312
|
case "slash_commands":
|
|
1048
|
-
|
|
1313
|
+
var reserved = new Set(builtinCommands.map(function (c) { return c.name; }));
|
|
1314
|
+
slashCommands = (msg.commands || []).filter(function (name) {
|
|
1315
|
+
return !reserved.has(name);
|
|
1316
|
+
}).map(function (name) {
|
|
1049
1317
|
return { name: name, desc: "Skill" };
|
|
1050
1318
|
});
|
|
1051
1319
|
break;
|
|
@@ -1067,6 +1335,10 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1067
1335
|
}
|
|
1068
1336
|
break;
|
|
1069
1337
|
|
|
1338
|
+
case "toast":
|
|
1339
|
+
showToast(msg.message, msg.level, msg.detail);
|
|
1340
|
+
break;
|
|
1341
|
+
|
|
1070
1342
|
case "input_sync":
|
|
1071
1343
|
handleInputSync(msg.text);
|
|
1072
1344
|
break;
|
|
@@ -1093,6 +1365,9 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1093
1365
|
var draft = sessionDrafts[activeSessionId] || "";
|
|
1094
1366
|
inputEl.value = draft;
|
|
1095
1367
|
autoResize();
|
|
1368
|
+
if (!("ontouchstart" in window)) {
|
|
1369
|
+
inputEl.focus();
|
|
1370
|
+
}
|
|
1096
1371
|
break;
|
|
1097
1372
|
|
|
1098
1373
|
case "session_id":
|
|
@@ -1260,6 +1535,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1260
1535
|
finalizeAssistantBlock();
|
|
1261
1536
|
addTurnMeta(msg.cost, msg.duration);
|
|
1262
1537
|
accumulateUsage(msg.cost, msg.usage);
|
|
1538
|
+
accumulateContext(msg.cost, msg.usage, msg.modelUsage);
|
|
1263
1539
|
break;
|
|
1264
1540
|
|
|
1265
1541
|
case "done":
|
|
@@ -1272,7 +1548,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1272
1548
|
enableMainInput();
|
|
1273
1549
|
resetToolState();
|
|
1274
1550
|
if (document.hidden) {
|
|
1275
|
-
if (isNotifAlertEnabled()) showDoneNotification();
|
|
1551
|
+
if (isNotifAlertEnabled() && !window._pushSubscription) showDoneNotification();
|
|
1276
1552
|
if (isNotifSoundEnabled()) playDoneSound();
|
|
1277
1553
|
}
|
|
1278
1554
|
break;
|
|
@@ -1297,7 +1573,10 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1297
1573
|
|
|
1298
1574
|
case "rewind_complete":
|
|
1299
1575
|
setRewindMode(false);
|
|
1300
|
-
|
|
1576
|
+
var rewindText = "Rewound to earlier point. Files have been restored.";
|
|
1577
|
+
if (msg.mode === "chat") rewindText = "Conversation rewound to earlier point.";
|
|
1578
|
+
else if (msg.mode === "files") rewindText = "Files restored to earlier point.";
|
|
1579
|
+
addSystemMessage(rewindText, false);
|
|
1301
1580
|
break;
|
|
1302
1581
|
|
|
1303
1582
|
case "rewind_error":
|
|
@@ -1317,6 +1596,22 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1317
1596
|
handleFileChanged(msg);
|
|
1318
1597
|
break;
|
|
1319
1598
|
|
|
1599
|
+
case "fs_dir_changed":
|
|
1600
|
+
handleDirChanged(msg);
|
|
1601
|
+
break;
|
|
1602
|
+
|
|
1603
|
+
case "fs_file_history_result":
|
|
1604
|
+
handleFileHistory(msg);
|
|
1605
|
+
break;
|
|
1606
|
+
|
|
1607
|
+
case "fs_git_diff_result":
|
|
1608
|
+
handleGitDiff(msg);
|
|
1609
|
+
break;
|
|
1610
|
+
|
|
1611
|
+
case "fs_file_at_result":
|
|
1612
|
+
handleFileAt(msg);
|
|
1613
|
+
break;
|
|
1614
|
+
|
|
1320
1615
|
case "term_list":
|
|
1321
1616
|
handleTermList(msg);
|
|
1322
1617
|
break;
|
|
@@ -1336,6 +1631,10 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1336
1631
|
case "term_closed":
|
|
1337
1632
|
handleTermClosed(msg);
|
|
1338
1633
|
break;
|
|
1634
|
+
|
|
1635
|
+
case "process_stats":
|
|
1636
|
+
updateStatusPanel(msg);
|
|
1637
|
+
break;
|
|
1339
1638
|
}
|
|
1340
1639
|
}
|
|
1341
1640
|
|
|
@@ -1467,6 +1766,10 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1467
1766
|
addUserMessage: addUserMessage,
|
|
1468
1767
|
addSystemMessage: addSystemMessage,
|
|
1469
1768
|
toggleUsagePanel: toggleUsagePanel,
|
|
1769
|
+
toggleStatusPanel: toggleStatusPanel,
|
|
1770
|
+
toggleContextPanel: toggleContextPanel,
|
|
1771
|
+
resetContextData: resetContextData,
|
|
1772
|
+
showImageModal: showImageModal,
|
|
1470
1773
|
});
|
|
1471
1774
|
|
|
1472
1775
|
// --- Notifications module (viewport, banners, notifications, debug, service worker) ---
|
|
@@ -1479,6 +1782,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1479
1782
|
scrollToBottom: scrollToBottom,
|
|
1480
1783
|
basePath: basePath,
|
|
1481
1784
|
toggleUsagePanel: toggleUsagePanel,
|
|
1785
|
+
toggleStatusPanel: toggleStatusPanel,
|
|
1482
1786
|
});
|
|
1483
1787
|
|
|
1484
1788
|
// --- QR code ---
|
|
@@ -1488,6 +1792,8 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1488
1792
|
initFileBrowser({
|
|
1489
1793
|
get ws() { return ws; },
|
|
1490
1794
|
get connected() { return connected; },
|
|
1795
|
+
get activeSessionId() { return activeSessionId; },
|
|
1796
|
+
messagesEl: messagesEl,
|
|
1491
1797
|
fileTreeEl: $("file-tree"),
|
|
1492
1798
|
fileViewerEl: $("file-viewer"),
|
|
1493
1799
|
});
|
package/lib/public/css/base.css
CHANGED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
Diff Views (unified + split)
|
|
3
|
+
========================================================================== */
|
|
4
|
+
|
|
5
|
+
.diff-unified,
|
|
6
|
+
.diff-split-view {
|
|
7
|
+
overflow: auto;
|
|
8
|
+
font-family: "SF Mono", Menlo, Monaco, monospace;
|
|
9
|
+
font-size: 12px;
|
|
10
|
+
line-height: 1.5;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.diff-table {
|
|
14
|
+
border-collapse: collapse;
|
|
15
|
+
width: 100%;
|
|
16
|
+
table-layout: fixed;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* --- Line numbers --- */
|
|
20
|
+
.diff-ln {
|
|
21
|
+
width: 40px;
|
|
22
|
+
min-width: 40px;
|
|
23
|
+
padding: 0 8px 0 4px;
|
|
24
|
+
text-align: right;
|
|
25
|
+
color: var(--text-dimmer);
|
|
26
|
+
user-select: none;
|
|
27
|
+
vertical-align: top;
|
|
28
|
+
white-space: nowrap;
|
|
29
|
+
opacity: 0.6;
|
|
30
|
+
border-right: 1px solid var(--border-subtle);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* --- Marker column (unified only) --- */
|
|
34
|
+
.diff-marker {
|
|
35
|
+
width: 16px;
|
|
36
|
+
min-width: 16px;
|
|
37
|
+
padding: 0 2px;
|
|
38
|
+
text-align: center;
|
|
39
|
+
color: var(--text-dimmer);
|
|
40
|
+
user-select: none;
|
|
41
|
+
vertical-align: top;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* --- Code cells --- */
|
|
45
|
+
.diff-code {
|
|
46
|
+
padding: 0 12px;
|
|
47
|
+
white-space: pre;
|
|
48
|
+
vertical-align: top;
|
|
49
|
+
tab-size: 2;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* --- Unified diff row colors --- */
|
|
53
|
+
.diff-row-remove {
|
|
54
|
+
background: rgba(229, 83, 75, 0.12);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.diff-row-remove .diff-marker {
|
|
58
|
+
color: var(--diff-remove, #E5534B);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.diff-row-add {
|
|
62
|
+
background: rgba(87, 171, 90, 0.12);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.diff-row-add .diff-marker {
|
|
66
|
+
color: var(--diff-add, #57AB5A);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* --- Hunk header row (patch diffs) --- */
|
|
70
|
+
.diff-row-hunk {
|
|
71
|
+
background: rgba(128, 128, 128, 0.05);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.diff-row-hunk .diff-hunk-text {
|
|
75
|
+
color: var(--text-dimmer);
|
|
76
|
+
font-style: italic;
|
|
77
|
+
padding: 2px 12px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* --- Split diff specifics --- */
|
|
81
|
+
.diff-table-split .diff-code-old,
|
|
82
|
+
.diff-table-split .diff-code-new {
|
|
83
|
+
max-width: 0;
|
|
84
|
+
overflow: hidden;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Split: divider between left and right */
|
|
88
|
+
.diff-table-split .diff-code-old {
|
|
89
|
+
border-right: 1px solid var(--border-subtle);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Split: change rows */
|
|
93
|
+
.diff-row-change .diff-code-old,
|
|
94
|
+
.diff-row-remove .diff-code-old {
|
|
95
|
+
background: rgba(229, 83, 75, 0.12);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.diff-row-change .diff-code-new,
|
|
99
|
+
.diff-row-add .diff-code-new {
|
|
100
|
+
background: rgba(87, 171, 90, 0.12);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Empty cells in split view */
|
|
104
|
+
.diff-row-remove .diff-code-new,
|
|
105
|
+
.diff-row-add .diff-code-old {
|
|
106
|
+
background: rgba(128, 128, 128, 0.03);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* --- Compact mode for inline previews --- */
|
|
110
|
+
.diff-compact .diff-table {
|
|
111
|
+
font-size: 11px;
|
|
112
|
+
line-height: 1.4;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.diff-compact .diff-ln {
|
|
116
|
+
width: 28px;
|
|
117
|
+
min-width: 28px;
|
|
118
|
+
padding: 0 4px 0 2px;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.diff-compact .diff-code {
|
|
122
|
+
padding: 0 6px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.diff-compact .diff-marker {
|
|
126
|
+
width: 12px;
|
|
127
|
+
min-width: 12px;
|
|
128
|
+
}
|