clay-server 3.4.0-beta.22 → 3.4.0-beta.24
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/lib/public/css/filebrowser.css +6 -1
- package/lib/public/css/pane.css +6 -0
- package/lib/public/index.html +2 -1
- package/lib/public/modules/app-connection.js +6 -0
- package/lib/public/modules/app-messages.js +5 -1
- package/lib/public/modules/filebrowser-tabs.js +65 -0
- package/lib/public/modules/filebrowser.js +23 -3
- package/lib/public/modules/pane-bridge.js +6 -0
- package/lib/public/modules/split-view.js +8 -1
- package/package.json +1 -1
|
@@ -960,7 +960,7 @@
|
|
|
960
960
|
width: 50%;
|
|
961
961
|
max-width: 720px;
|
|
962
962
|
min-width: 360px;
|
|
963
|
-
border-
|
|
963
|
+
border-right: 1px solid var(--border);
|
|
964
964
|
background: var(--bg);
|
|
965
965
|
display: flex;
|
|
966
966
|
flex-direction: column;
|
|
@@ -1011,6 +1011,11 @@
|
|
|
1011
1011
|
min-height: 44px;
|
|
1012
1012
|
}
|
|
1013
1013
|
|
|
1014
|
+
.file-viewer-tabs { display: flex; min-width: 0; flex: 1; overflow-x: auto; gap: 2px; }
|
|
1015
|
+
.file-viewer-tab { display: inline-flex; align-items: center; gap: 6px; max-width: 170px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border: 0; border-radius: 5px; padding: 4px 7px; background: transparent; color: var(--text-secondary); font: inherit; font-size: 12px; cursor: pointer; }
|
|
1016
|
+
.file-viewer-tab.active { background: var(--sidebar-hover); color: var(--text); }
|
|
1017
|
+
.file-viewer-tab-close { font-size: 16px; line-height: 12px; color: var(--text-muted); }
|
|
1018
|
+
|
|
1014
1019
|
.file-viewer-path {
|
|
1015
1020
|
flex: 1;
|
|
1016
1021
|
font-family: "Roboto Mono", monospace;
|
package/lib/public/css/pane.css
CHANGED
|
@@ -23,6 +23,12 @@ body.pane-mode #sticky-notes-container {
|
|
|
23
23
|
display: none !important;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
body.pane-mode #file-viewer { display: none !important; }
|
|
27
|
+
|
|
28
|
+
body.pane-mode #connect-overlay { background: var(--bg); }
|
|
29
|
+
body.pane-mode #connect-overlay #ascii-logo-canvas { display: none; }
|
|
30
|
+
body.pane-mode #connect-overlay-msg { color: var(--text-dimmer); }
|
|
31
|
+
|
|
26
32
|
body.pane-mode .notif-banner-container {
|
|
27
33
|
display: none !important;
|
|
28
34
|
}
|
package/lib/public/index.html
CHANGED
|
@@ -403,7 +403,7 @@
|
|
|
403
403
|
<div id="app">
|
|
404
404
|
<div id="connect-overlay">
|
|
405
405
|
<canvas id="ascii-logo-canvas"></canvas>
|
|
406
|
-
<p id="connect-overlay-msg">
|
|
406
|
+
<p id="connect-overlay-msg">Connecting…</p>
|
|
407
407
|
</div>
|
|
408
408
|
<div id="sticky-notes-container" class="hidden"></div>
|
|
409
409
|
<div id="debate-info-float" class="hidden"></div>
|
|
@@ -680,6 +680,7 @@
|
|
|
680
680
|
|
|
681
681
|
<div id="file-viewer" class="hidden">
|
|
682
682
|
<div class="file-viewer-header">
|
|
683
|
+
<div class="file-viewer-tabs" id="file-viewer-tabs" role="tablist"></div>
|
|
683
684
|
<span class="file-viewer-path" id="file-viewer-path"></span>
|
|
684
685
|
<span class="file-viewer-live-status hidden" id="file-viewer-live-status" aria-live="polite">
|
|
685
686
|
<span class="file-viewer-live-dot"></span>
|
|
@@ -17,6 +17,7 @@ var reconnectTimer = null;
|
|
|
17
17
|
var reconnectDelay = 1000;
|
|
18
18
|
var connectTimeoutId = null;
|
|
19
19
|
var connectOverlay = null;
|
|
20
|
+
var hasConnectedOnce = false;
|
|
20
21
|
|
|
21
22
|
export function initConnection() {
|
|
22
23
|
connectOverlay = document.getElementById("connect-overlay");
|
|
@@ -39,6 +40,7 @@ export function initConnection() {
|
|
|
39
40
|
if (state.connected !== prev.connected) {
|
|
40
41
|
var sendBtn = getSendBtn();
|
|
41
42
|
if (state.connected) {
|
|
43
|
+
hasConnectedOnce = true;
|
|
42
44
|
if (sendBtn) sendBtn.disabled = false;
|
|
43
45
|
if (connectOverlay) connectOverlay.classList.add("hidden");
|
|
44
46
|
var updPill = document.getElementById("update-pill-wrap");
|
|
@@ -46,6 +48,10 @@ export function initConnection() {
|
|
|
46
48
|
stopLogoAnimation();
|
|
47
49
|
} else {
|
|
48
50
|
if (sendBtn) sendBtn.disabled = true;
|
|
51
|
+
if (hasConnectedOnce && connectOverlay) {
|
|
52
|
+
var overlayMessage = document.getElementById("connect-overlay-msg");
|
|
53
|
+
if (overlayMessage) overlayMessage.textContent = "Reconnecting to server…";
|
|
54
|
+
}
|
|
49
55
|
if (connectOverlay) connectOverlay.classList.remove("hidden");
|
|
50
56
|
startLogoAnimation();
|
|
51
57
|
}
|
|
@@ -28,6 +28,7 @@ import { startThinking, appendThinking, stopThinking, resetThinkingGroup, create
|
|
|
28
28
|
import { showDoneNotification, playDoneSound, isNotifAlertEnabled, isNotifSoundEnabled } from './notifications.js';
|
|
29
29
|
import { handleFsList, handleFsRead, handleFileChanged, handleDirChanged, handleFileHistory, handleGitDiff, handleFileAt, refreshIfOpen, getPendingNavigate, handleFsSearch, presentMarkdownEdit } from './filebrowser.js';
|
|
30
30
|
import { beginMarkdownTurn, finishMarkdownTurn, markdownPathFromToolInput } from './markdown-live-edit.js';
|
|
31
|
+
import { forwardPaneMarkdownPresentation } from './pane-bridge.js';
|
|
31
32
|
import { isProjectSettingsOpen, handleInstructionsRead, handleInstructionsWrite, handleProjectEnv, handleProjectEnvSaved, handleProjectSharedEnv, handleProjectSharedEnvSaved, handleProjectOwnerChanged } from './project-settings.js';
|
|
32
33
|
import { updateSettingsModels, updateSettingsStats, updateDaemonConfig, handleSetPinResult, handleKeepAwakeChanged, handleInheritGroupsChanged, handleAutoContinueChanged, handleRestartResult, handleShutdownResult, handleSharedEnv, handleSharedEnvSaved, handleGlobalClaudeMdRead, handleGlobalClaudeMdWrite } from './server-settings.js';
|
|
33
34
|
import { handleTermList, handleTermCreated, sendTerminalCommand, handleTermOutput, handleTermResized, handleTermExited, handleTermClosed } from './terminal.js';
|
|
@@ -1400,7 +1401,10 @@ export function processMessage(msg) {
|
|
|
1400
1401
|
break;
|
|
1401
1402
|
|
|
1402
1403
|
case "markdown_edit_present":
|
|
1403
|
-
if (!store.get('replayingHistory') && !store.get('dmMode'))
|
|
1404
|
+
if (!store.get('replayingHistory') && !store.get('dmMode')) {
|
|
1405
|
+
if (store.get('paneMode')) forwardPaneMarkdownPresentation(msg);
|
|
1406
|
+
else if (!store.get('splitPanes')) presentMarkdownEdit(msg);
|
|
1407
|
+
}
|
|
1404
1408
|
break;
|
|
1405
1409
|
|
|
1406
1410
|
case "fs_dir_changed":
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
var tabs = new Map();
|
|
2
|
+
var focusedPath = null;
|
|
3
|
+
var onFocus = null;
|
|
4
|
+
var onEmpty = null;
|
|
5
|
+
|
|
6
|
+
function label(path) {
|
|
7
|
+
var parts = String(path || "").split("/");
|
|
8
|
+
return parts[parts.length - 1] || path;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function render() {
|
|
12
|
+
var root = document.getElementById("file-viewer-tabs");
|
|
13
|
+
if (!root) return;
|
|
14
|
+
root.innerHTML = "";
|
|
15
|
+
tabs.forEach(function(value, path) {
|
|
16
|
+
var tab = document.createElement("button");
|
|
17
|
+
tab.type = "button";
|
|
18
|
+
tab.className = "file-viewer-tab" + (path === focusedPath ? " active" : "");
|
|
19
|
+
tab.textContent = label(path);
|
|
20
|
+
tab.title = path;
|
|
21
|
+
tab.addEventListener("click", function() { focusFileViewerTab(path); });
|
|
22
|
+
var close = document.createElement("span");
|
|
23
|
+
close.className = "file-viewer-tab-close";
|
|
24
|
+
close.textContent = "×";
|
|
25
|
+
close.addEventListener("click", function(event) { event.stopPropagation(); closeFileViewerTab(path); });
|
|
26
|
+
tab.appendChild(close);
|
|
27
|
+
root.appendChild(tab);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function initFileViewerTabs(options) {
|
|
32
|
+
onFocus = options && options.onFocus;
|
|
33
|
+
onEmpty = options && options.onEmpty;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function openFileViewerTab(path, data) {
|
|
37
|
+
if (!path) return false;
|
|
38
|
+
var exists = tabs.has(path);
|
|
39
|
+
tabs.set(path, Object.assign(tabs.get(path) || {}, data || {}));
|
|
40
|
+
focusedPath = path;
|
|
41
|
+
render();
|
|
42
|
+
return !exists;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function focusFileViewerTab(path) {
|
|
46
|
+
if (!tabs.has(path)) return false;
|
|
47
|
+
focusedPath = path;
|
|
48
|
+
render();
|
|
49
|
+
if (onFocus) onFocus(path, tabs.get(path));
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function closeFileViewerTab(path) {
|
|
54
|
+
if (!tabs.has(path)) return;
|
|
55
|
+
var paths = Array.from(tabs.keys());
|
|
56
|
+
var index = paths.indexOf(path);
|
|
57
|
+
tabs.delete(path);
|
|
58
|
+
if (focusedPath === path) focusedPath = paths[index + 1] || paths[index - 1] || null;
|
|
59
|
+
render();
|
|
60
|
+
if (focusedPath && onFocus) onFocus(focusedPath, tabs.get(focusedPath));
|
|
61
|
+
if (!focusedPath && onEmpty) onEmpty();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function focusedFileViewerTab() { return focusedPath; }
|
|
65
|
+
export function clearFileViewerTabs() { tabs.clear(); focusedPath = null; render(); }
|
|
@@ -9,6 +9,7 @@ import { copyMarkdownFormatting } from './rich-clipboard.js';
|
|
|
9
9
|
import { animateMarkdownChange, beginMarkdownPresentation, cancelMarkdownFollow, isFollowingMarkdown } from './markdown-live-edit.js';
|
|
10
10
|
import { store } from './store.js';
|
|
11
11
|
import { enterMarkdownSlides, exitMarkdownSlides, handleMarkdownSlideKey, syncMarkdownSlidesButton, toggleMarkdownSlideLevelMenu } from './markdown-slides.js';
|
|
12
|
+
import { initFileViewerTabs, openFileViewerTab, closeFileViewerTab, focusedFileViewerTab, clearFileViewerTabs } from './filebrowser-tabs.js';
|
|
12
13
|
|
|
13
14
|
var ctx;
|
|
14
15
|
var showDropHint = function () {};
|
|
@@ -30,6 +31,14 @@ var pendingFileAt = null; // callback for pending file-at
|
|
|
30
31
|
|
|
31
32
|
export function initFileBrowser(_ctx) {
|
|
32
33
|
ctx = _ctx;
|
|
34
|
+
var mainPanels = document.getElementById("main-panels");
|
|
35
|
+
if (mainPanels && ctx.fileViewerEl) mainPanels.insertBefore(ctx.fileViewerEl, mainPanels.firstChild);
|
|
36
|
+
initFileViewerTabs({
|
|
37
|
+
onFocus: function(path) {
|
|
38
|
+
if (path !== currentFilePath) requestFileContent(path);
|
|
39
|
+
},
|
|
40
|
+
onEmpty: function() { teardownFileViewer(); },
|
|
41
|
+
});
|
|
33
42
|
|
|
34
43
|
// Load material file icons in background
|
|
35
44
|
initFileIcons().then(function () {
|
|
@@ -133,7 +142,7 @@ export function initFileBrowser(_ctx) {
|
|
|
133
142
|
|
|
134
143
|
// Close button
|
|
135
144
|
document.getElementById("file-viewer-close").addEventListener("click", function () {
|
|
136
|
-
|
|
145
|
+
closeFileViewerTab(focusedFileViewerTab());
|
|
137
146
|
});
|
|
138
147
|
|
|
139
148
|
// Full-viewport presentation toggle
|
|
@@ -448,6 +457,14 @@ function ensureRenderedMarkdown() {
|
|
|
448
457
|
}
|
|
449
458
|
|
|
450
459
|
export function closeFileViewer() {
|
|
460
|
+
if (focusedFileViewerTab()) {
|
|
461
|
+
closeFileViewerTab(focusedFileViewerTab());
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
teardownFileViewer();
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function teardownFileViewer() {
|
|
451
468
|
if (currentFilePath && isFollowingMarkdown(currentFilePath)) cancelMarkdownFollow();
|
|
452
469
|
sendUnwatch();
|
|
453
470
|
inlineDiffActive = false;
|
|
@@ -475,8 +492,8 @@ export function setFileViewerFullscreen(enabled) {
|
|
|
475
492
|
}
|
|
476
493
|
|
|
477
494
|
export function resetFileBrowser() {
|
|
478
|
-
|
|
479
|
-
|
|
495
|
+
clearFileViewerTabs();
|
|
496
|
+
teardownFileViewer();
|
|
480
497
|
// Clear all cached state
|
|
481
498
|
treeData = {};
|
|
482
499
|
currentContent = null;
|
|
@@ -513,6 +530,7 @@ export function openFile(filePath, opts) {
|
|
|
513
530
|
cancelMarkdownFollow();
|
|
514
531
|
}
|
|
515
532
|
pendingRenderedOpen = !!(opts && opts.rendered);
|
|
533
|
+
openFileViewerTab(filePath);
|
|
516
534
|
if (opts && opts.diff) {
|
|
517
535
|
pendingOpenMode = { type: "diff", oldStr: opts.diff.oldStr, newStr: opts.diff.newStr };
|
|
518
536
|
} else {
|
|
@@ -540,6 +558,7 @@ export function openWorkingTreeDiff(diff) {
|
|
|
540
558
|
|
|
541
559
|
export function presentMarkdownEdit(msg) {
|
|
542
560
|
if (!msg || !beginMarkdownPresentation(msg.path)) return;
|
|
561
|
+
openFileViewerTab(msg.path, { content: msg.content });
|
|
543
562
|
pendingOpenMode = null;
|
|
544
563
|
pendingRenderedOpen = true;
|
|
545
564
|
pendingRefresh = false;
|
|
@@ -1031,6 +1050,7 @@ function renderEntries(container, entries, depth) {
|
|
|
1031
1050
|
// --- File viewer ---
|
|
1032
1051
|
|
|
1033
1052
|
function showFileContent(msg) {
|
|
1053
|
+
openFileViewerTab(msg.path, { content: msg.content });
|
|
1034
1054
|
var pathEl = document.getElementById("file-viewer-path");
|
|
1035
1055
|
var bodyEl = document.getElementById("file-viewer-body");
|
|
1036
1056
|
var renderBtn = document.getElementById("file-viewer-render");
|
|
@@ -23,6 +23,12 @@ export function reportPaneContext(data) {
|
|
|
23
23
|
}, window.location.origin);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
export function forwardPaneMarkdownPresentation(message) {
|
|
27
|
+
if (!store.get('paneMode') || window.parent === window) return false;
|
|
28
|
+
window.parent.postMessage({ type: "clay-pane-present-markdown", message: message }, window.location.origin);
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
// Toggle without setContextView: panes share localStorage with the main app,
|
|
27
33
|
// so persisting here would silently rewrite the user's main-view preference.
|
|
28
34
|
function togglePaneContextPanel() {
|
|
@@ -10,6 +10,7 @@ import { VENDOR_AVATARS, VENDOR_NAMES } from './app-rendering.js';
|
|
|
10
10
|
import { groupedSessionIds, findSplitGroup } from './split-group-helpers.js';
|
|
11
11
|
import { showConfirm } from './app-misc.js';
|
|
12
12
|
import { syncPairChrome } from './split-pair-ui.js';
|
|
13
|
+
import { presentMarkdownEdit } from './filebrowser.js';
|
|
13
14
|
|
|
14
15
|
var host = null;
|
|
15
16
|
var nativeApp = null;
|
|
@@ -269,7 +270,13 @@ function updatePaneContextChip(paneEl, msg) {
|
|
|
269
270
|
function handlePaneMessage(event) {
|
|
270
271
|
if (event.origin !== window.location.origin) return;
|
|
271
272
|
var msg = event.data;
|
|
272
|
-
if (!msg ||
|
|
273
|
+
if (!msg || !host) return;
|
|
274
|
+
if (msg.type === "clay-pane-present-markdown") {
|
|
275
|
+
var present = msg.message;
|
|
276
|
+
if (present) presentMarkdownEdit(present);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (msg.type !== "clay-pane-context") return;
|
|
273
280
|
var frames = host.querySelectorAll(".split-pane-frame");
|
|
274
281
|
for (var i = 0; i < frames.length; i++) {
|
|
275
282
|
if (frames[i].contentWindow === event.source) {
|
package/package.json
CHANGED