clay-server 5.3.0-beta.4 → 5.3.0-beta.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/lib/ask-user-request-identity.js +18 -1
- package/lib/clay-product-control-tools.js +2 -2
- package/lib/clay-product-control.js +1 -1
- package/lib/clay-screen-controls.js +1 -0
- package/lib/clay-split-view-awareness.js +5 -2
- package/lib/clay-split-view-controls.js +14 -7
- package/lib/clay-split-view-input.js +5 -4
- package/lib/clay-split-view-render-proof.js +14 -4
- package/lib/clay-view-workspace-input.js +71 -0
- package/lib/project-delegated-follow-up.js +18 -9
- package/lib/project-file-history.js +11 -9
- package/lib/project-file-watch.js +26 -7
- package/lib/project-filesystem.js +5 -4
- package/lib/project-shared-browser.js +7 -3
- package/lib/project-split-view-document.js +16 -2
- package/lib/project-split-views.js +15 -294
- package/lib/project-user-message.js +4 -3
- package/lib/project-vendor-login.js +2 -2
- package/lib/project-view-input.js +11 -0
- package/lib/project-view-pair.js +26 -0
- package/lib/project-view-store.js +346 -0
- package/lib/project-view-terminal-binding.js +16 -0
- package/lib/project-view-workspace.js +126 -0
- package/lib/project.js +1 -0
- package/lib/public/app.js +2 -0
- package/lib/public/css/issues.css +3 -0
- package/lib/public/css/pane.css +5 -1
- package/lib/public/css/right-workbench.css +25 -3
- package/lib/public/css/scheduled-tasks.css +7 -0
- package/lib/public/css/shared-browser.css +21 -0
- package/lib/public/css/split-view-layout.css +37 -3
- package/lib/public/css/sticky-notes.css +3 -2
- package/lib/public/css/tool-pane.css +127 -0
- package/lib/public/index.html +3 -3
- package/lib/public/modules/app-header.js +24 -2
- package/lib/public/modules/app-messages.js +1 -1
- package/lib/public/modules/clay-file-links.js +2 -1
- package/lib/public/modules/clay-session-links.js +1 -1
- package/lib/public/modules/clay-split-view-screen.js +6 -3
- package/lib/public/modules/clay-tool-pane-screen.js +35 -0
- package/lib/public/modules/file-history-snapshot.js +12 -0
- package/lib/public/modules/file-pane-entrypoints.js +55 -0
- package/lib/public/modules/file-pane-history.js +78 -0
- package/lib/public/modules/file-pane-instances.js +140 -0
- package/lib/public/modules/file-pane-links.js +41 -0
- package/lib/public/modules/file-pane-render.js +48 -0
- package/lib/public/modules/file-pane-review.js +26 -0
- package/lib/public/modules/file-pane-toolbar.js +54 -0
- package/lib/public/modules/file-viewer-source.js +70 -0
- package/lib/public/modules/filebrowser.js +44 -83
- package/lib/public/modules/git-panel.js +5 -0
- package/lib/public/modules/issues.js +3 -4
- package/lib/public/modules/linear-panel.js +2 -2
- package/lib/public/modules/mate-knowledge-workbench.js +10 -6
- package/lib/public/modules/notifications.js +4 -1
- package/lib/public/modules/pane-activation.js +5 -0
- package/lib/public/modules/pane-binding.js +5 -1
- package/lib/public/modules/pane-browser-bridge.js +20 -6
- package/lib/public/modules/pane-browser-transport.js +110 -0
- package/lib/public/modules/pane-header-runtime.js +18 -0
- package/lib/public/modules/plantuml-preview.js +12 -12
- package/lib/public/modules/right-workbench.js +116 -67
- package/lib/public/modules/scheduled-tasks.js +7 -6
- package/lib/public/modules/shared-browser.js +75 -9
- package/lib/public/modules/split-pane-renderer.js +7 -8
- package/lib/public/modules/split-pane-runtime.js +23 -0
- package/lib/public/modules/split-view-drag-preview.js +90 -0
- package/lib/public/modules/split-view-layout-client.js +1 -1
- package/lib/public/modules/split-view-presentation-drag.js +36 -13
- package/lib/public/modules/split-view-presentation-drop.js +21 -18
- package/lib/public/modules/split-view-presentation-renderer.js +15 -10
- package/lib/public/modules/split-view-presentations.js +25 -33
- package/lib/public/modules/split-view.js +17 -16
- package/lib/public/modules/sticky-notes-browser.js +2 -2
- package/lib/public/modules/sticky-notes.js +8 -10
- package/lib/public/modules/terminal.js +45 -13
- package/lib/public/modules/theme.js +1 -1
- package/lib/public/modules/tool-pane-chrome.js +82 -0
- package/lib/public/modules/tool-pane-drag.js +134 -0
- package/lib/public/modules/tool-pane-layout.js +95 -0
- package/lib/public/modules/tool-pane-legacy.js +60 -0
- package/lib/public/modules/tool-pane-tree.js +51 -0
- package/lib/public/modules/view-legacy-coordinates.js +16 -0
- package/lib/public/modules/view-mutations.js +24 -0
- package/lib/public/modules/view-pair-presentation.js +74 -0
- package/lib/public/modules/view-pair-validation.js +12 -0
- package/lib/public/modules/view-presentation-state.js +37 -0
- package/lib/public/modules/view-workspace-controller.js +210 -0
- package/lib/public/modules/view-workspace-state.js +103 -0
- package/lib/public/modules/view-workspace-validation.js +47 -0
- package/lib/public/modules/wireframe-workbench.js +11 -0
- package/lib/public/modules/workbench-layout-preference.js +12 -5
- package/lib/public/modules/workbench-panels.js +21 -0
- package/lib/public/modules/workspace-assignment-card.js +9 -6
- package/lib/public/pane-assets/{app-ED2VCGNQ.js → app-7UZ727NA.js} +1 -1
- package/lib/public/pane-assets/chunk-2AIWCI6Y.js +1 -0
- package/lib/public/pane-assets/chunk-6MIHIQ2W.js +1 -0
- package/lib/public/pane-assets/chunk-6NYCNWVF.js +1 -0
- package/lib/public/pane-assets/chunk-6QFLYOVY.js +1 -0
- package/lib/public/pane-assets/chunk-6UJ2ALOB.js +1 -0
- package/lib/public/pane-assets/chunk-6XK2OATZ.js +1 -0
- package/lib/public/pane-assets/chunk-BKIDR2ME.js +1 -0
- package/lib/public/pane-assets/chunk-BMHTBRMO.js +1 -0
- package/lib/public/pane-assets/chunk-GEFCFZEN.js +1 -0
- package/lib/public/pane-assets/chunk-GN2NQEVC.js +2 -0
- package/lib/public/pane-assets/chunk-IMLMBPT4.js +166 -0
- package/lib/public/pane-assets/chunk-JIV7O5GN.js +1 -0
- package/lib/public/pane-assets/chunk-K5S6AILM.js +1 -0
- package/lib/public/pane-assets/chunk-LDIZLKB2.js +1 -0
- package/lib/public/pane-assets/chunk-OWNNLZ36.js +1 -0
- package/lib/public/pane-assets/chunk-P2C3LOWL.js +3 -0
- package/lib/public/pane-assets/chunk-PV4PCH2V.js +2 -0
- package/lib/public/pane-assets/chunk-R4LMPTKU.js +1 -0
- package/lib/public/pane-assets/chunk-R7QVVFUG.js +1 -0
- package/lib/public/pane-assets/chunk-TMGSDFJI.js +1 -0
- package/lib/public/pane-assets/chunk-VKYBAVWT.js +1 -0
- package/lib/public/pane-assets/chunk-VNAV23CM.js +1 -0
- package/lib/public/pane-assets/chunk-VWTHIQCE.js +1 -0
- package/lib/public/pane-assets/chunk-VZ2OKDAO.js +1 -0
- package/lib/public/pane-assets/chunk-WM3K5AFH.js +1 -0
- package/lib/public/pane-assets/chunk-XOB3O327.js +1 -0
- package/lib/public/pane-assets/chunk-XOOUCFYT.js +1 -0
- package/lib/public/pane-assets/manifest.json +383 -336
- package/lib/public/pane-assets/style-ZM7OOU3M.css +1 -0
- package/lib/public/style.css +1 -0
- package/lib/server-workbench-layout-settings.js +3 -3
- package/lib/server.js +1 -0
- package/lib/split-view-layout.js +11 -7
- package/lib/users-workbench-layout-preferences.js +14 -4
- package/lib/workbench-window-layout.js +30 -0
- package/lib/workspace-assignment-service.js +56 -11
- package/lib/workspace-follow-up-queue.js +92 -0
- package/lib/workspace-query-mcp-server.js +1 -1
- package/package.json +1 -1
- package/lib/public/pane-assets/chunk-6D4DPE5X.js +0 -1
- package/lib/public/pane-assets/chunk-6SM2WQJX.js +0 -1
- package/lib/public/pane-assets/chunk-7H36SUZA.js +0 -2
- package/lib/public/pane-assets/chunk-DGOFGEUK.js +0 -1
- package/lib/public/pane-assets/chunk-I57URYSL.js +0 -1
- package/lib/public/pane-assets/chunk-IYFILQHZ.js +0 -2
- package/lib/public/pane-assets/chunk-IZ2ZANEY.js +0 -167
- package/lib/public/pane-assets/chunk-JAEQCN5S.js +0 -1
- package/lib/public/pane-assets/chunk-ST2Q62ET.js +0 -1
- package/lib/public/pane-assets/style-JVCOCG3W.css +0 -1
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import { independentFilesActive, closeActiveFilePane, expandActiveFilePane, reopenIndependentFile, refreshIndependentFile, presentIndependentFile } from './file-pane-entrypoints.js';
|
|
2
|
+
import { openIndependentFile, routeFilePaneMessage } from './file-pane-instances.js';
|
|
3
|
+
import { activeSavedView } from './view-workspace-state.js';
|
|
4
|
+
import { mapExtToLanguage, renderCodeWithLineNumbers, renderLargeTextFile, formatSize } from './file-viewer-source.js';
|
|
1
5
|
import { presentedDocument, refreshPresentedDocument, closePresentedDocument } from './clay-studio-document.js';
|
|
2
6
|
import { registerStudioPaint } from './clay-studio-render.js';
|
|
3
7
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
4
8
|
import { escapeHtml, copyToClipboard } from './utils.js';
|
|
5
9
|
import { renderMarkdown, highlightCodeBlocks, renderMermaidBlocks, exportMarkdownAsPdf } from './markdown.js';
|
|
6
10
|
import { closeSidebar } from './sidebar.js';
|
|
7
|
-
import { claimRightWorkbench, registerRightWorkbench, releaseRightWorkbench, isRightWorkbenchCurrent } from './right-workbench.js';
|
|
11
|
+
import { claimRightWorkbench, registerRightWorkbench, releaseRightWorkbench, isRightWorkbenchCurrent, rightWorkbenchRevisionFor } from './right-workbench.js';
|
|
8
12
|
import { renderUnifiedDiff, renderSplitDiff } from './diff.js';
|
|
9
13
|
import { initFileIcons, getFileIconSvg, getFolderIconSvg } from './fileicons.js';
|
|
10
14
|
import { copyMarkdownFormatting } from './rich-clipboard.js';
|
|
@@ -47,7 +51,7 @@ export function initFileBrowser(_ctx) {
|
|
|
47
51
|
onFocus: function(path) {
|
|
48
52
|
claimRightWorkbench("file-viewer");
|
|
49
53
|
ctx.fileViewerEl.classList.remove("hidden");
|
|
50
|
-
if (path !== currentFilePath || (store.get("fileViewerIsHtml") && isRendered && !
|
|
54
|
+
if (path !== currentFilePath || (store.get("fileViewerIsHtml") && isRendered && !ctx.fileViewerEl.querySelector(".file-viewer-html-preview"))) requestFileContent(path);
|
|
51
55
|
},
|
|
52
56
|
onEmpty: function() { resetMarkdownViewModes(); teardownFileViewer(); },
|
|
53
57
|
});
|
|
@@ -499,24 +503,30 @@ function ensureRenderedMarkdown() {
|
|
|
499
503
|
}
|
|
500
504
|
|
|
501
505
|
export function closeFileViewer() {
|
|
506
|
+
if (independentFilesActive()) return closeActiveFilePane();
|
|
502
507
|
releaseRightWorkbench("file-viewer");
|
|
503
508
|
teardownFileViewer();
|
|
504
509
|
}
|
|
505
510
|
|
|
506
511
|
function teardownFileViewer() {
|
|
507
512
|
cancelPlantUmlPreview();
|
|
508
|
-
var htmlFrame =
|
|
513
|
+
var htmlFrame = ctx.fileViewerEl.querySelector(".file-viewer-html-preview");
|
|
509
514
|
if (htmlFrame) htmlFrame.remove();
|
|
510
515
|
if (currentFilePath && isFollowingMarkdown(currentFilePath)) cancelMarkdownFollow();
|
|
511
516
|
sendUnwatch();
|
|
512
517
|
inlineDiffActive = false;
|
|
513
518
|
exitMarkdownSlides();
|
|
514
519
|
ctx.fileViewerEl.classList.remove("file-viewer-wide");
|
|
515
|
-
|
|
520
|
+
setLegacyFileViewerFullscreen(false);
|
|
516
521
|
ctx.fileViewerEl.classList.add("hidden");
|
|
517
522
|
}
|
|
518
523
|
|
|
519
524
|
export function setFileViewerFullscreen(enabled) {
|
|
525
|
+
if (independentFilesActive()) return expandActiveFilePane(enabled);
|
|
526
|
+
return setLegacyFileViewerFullscreen(enabled);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function setLegacyFileViewerFullscreen(enabled) {
|
|
520
530
|
var isFullscreen = !!enabled;
|
|
521
531
|
var viewer = ctx && ctx.fileViewerEl ? ctx.fileViewerEl : document.getElementById("file-viewer");
|
|
522
532
|
var button = document.getElementById("file-viewer-fullscreen");
|
|
@@ -575,6 +585,11 @@ var pendingRenderedOpen = false;
|
|
|
575
585
|
|
|
576
586
|
export function openFile(filePath, opts) {
|
|
577
587
|
if (!filePath) return;
|
|
588
|
+
if (activeSavedView() && !store.get('paneMode')) {
|
|
589
|
+
var opened = openIndependentFile(filePath, opts);
|
|
590
|
+
if (opened !== false) closePresentedDocument();
|
|
591
|
+
return opened;
|
|
592
|
+
}
|
|
578
593
|
var state = store.snap();
|
|
579
594
|
if (!state.connected) return false;
|
|
580
595
|
if (opts && opts.projectSlug && opts.projectSlug !== state.currentSlug) return false;
|
|
@@ -597,6 +612,7 @@ export function openFile(filePath, opts) {
|
|
|
597
612
|
}
|
|
598
613
|
|
|
599
614
|
export function reopenFileViewer() {
|
|
615
|
+
if (independentFilesActive()) return reopenIndependentFile();
|
|
600
616
|
var workbenchRevision = claimRightWorkbench("file-viewer");
|
|
601
617
|
store.set({ fileViewerOpenRevision: workbenchRevision });
|
|
602
618
|
if (!focusedFileViewerTab()) return false;
|
|
@@ -616,6 +632,7 @@ export function reopenFileViewer() {
|
|
|
616
632
|
|
|
617
633
|
export function openWorkingTreeDiff(diff) {
|
|
618
634
|
if (!diff || !diff.path) return;
|
|
635
|
+
if (independentFilesActive()) return presentIndependentFile({ path: diff.path, content: diff.newContent, binary: diff.binary }, diff.binary ? null : { oldStr: diff.oldContent || "", newStr: diff.newContent || "" }, diff.review);
|
|
619
636
|
var workbenchRevision = claimRightWorkbench("file-viewer");
|
|
620
637
|
store.set({ fileViewerOpenRevision: workbenchRevision });
|
|
621
638
|
openFileViewerTab(diff.path);
|
|
@@ -636,6 +653,7 @@ export function openWorkingTreeDiff(diff) {
|
|
|
636
653
|
|
|
637
654
|
export function presentMarkdownEdit(msg) {
|
|
638
655
|
if (!msg || !beginMarkdownPresentation(msg.path)) return;
|
|
656
|
+
if (independentFilesActive()) return presentIndependentFile(msg);
|
|
639
657
|
var workbenchRevision = claimRightWorkbench("file-viewer");
|
|
640
658
|
store.set({ fileViewerOpenRevision: workbenchRevision });
|
|
641
659
|
openFileViewerTab(msg.path, { content: msg.content });
|
|
@@ -739,7 +757,7 @@ function requestFileContent(filePath) {
|
|
|
739
757
|
if (refreshPresentedDocument(filePath)) return;
|
|
740
758
|
var requestId = "file-read-" + Date.now() + "-" + Math.random().toString(36).slice(2);
|
|
741
759
|
var state = store.snap();
|
|
742
|
-
store.set({ fileReadRequest: { requestId: requestId, path: filePath, projectSlug: state.currentSlug || null, sessionId: state.activeSessionId != null ? String(state.activeSessionId) : null, accountId: state.myUserId || null, workbenchRevision:
|
|
760
|
+
store.set({ fileReadRequest: { requestId: requestId, path: filePath, projectSlug: state.currentSlug || null, sessionId: state.activeSessionId != null ? String(state.activeSessionId) : null, accountId: state.myUserId || null, viewId: state.projectSplitViewActiveId || null, socketGeneration: state.projectSplitViewSocketGeneration, workbenchRevision: rightWorkbenchRevisionFor('file-viewer') } });
|
|
743
761
|
var pending = store.get('pendingFileNavigation');
|
|
744
762
|
if (pending && pending.path === filePath) store.set({ pendingFileNavigation: Object.assign({}, pending, { requestId: requestId }) });
|
|
745
763
|
if (ctx.ws && ctx.connected) {
|
|
@@ -750,6 +768,7 @@ function requestFileContent(filePath) {
|
|
|
750
768
|
var pendingRefresh = false;
|
|
751
769
|
|
|
752
770
|
export function refreshIfOpen(filePath) {
|
|
771
|
+
if (independentFilesActive()) return refreshIndependentFile(filePath);
|
|
753
772
|
if (!currentFilePath || ctx.fileViewerEl.classList.contains("hidden")) return;
|
|
754
773
|
// Don't refresh while history panel or inline diff is showing
|
|
755
774
|
if (historyVisible || inlineDiffActive) return;
|
|
@@ -863,7 +882,7 @@ function renderFilteredTree(container, tree, depth, query) {
|
|
|
863
882
|
(function (rowEl, childEl, folderName) {
|
|
864
883
|
rowEl.addEventListener("click", function (e) {
|
|
865
884
|
e.stopPropagation();
|
|
866
|
-
claimRightWorkbench("file-viewer");
|
|
885
|
+
if (!activeSavedView()) claimRightWorkbench("file-viewer");
|
|
867
886
|
var isExpanded = rowEl.classList.contains("expanded");
|
|
868
887
|
rowEl.classList.toggle("expanded");
|
|
869
888
|
childEl.classList.toggle("hidden", isExpanded);
|
|
@@ -886,6 +905,11 @@ function renderFilteredTree(container, tree, depth, query) {
|
|
|
886
905
|
(function (filePath, rowEl) {
|
|
887
906
|
rowEl.addEventListener("click", function (e) {
|
|
888
907
|
e.stopPropagation();
|
|
908
|
+
if (activeSavedView() && !store.get('paneMode')) {
|
|
909
|
+
openIndependentFile(filePath);
|
|
910
|
+
if (window.innerWidth <= 768) closeSidebar();
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
889
913
|
claimRightWorkbench("file-viewer");
|
|
890
914
|
if (followedFileChanged(filePath)) cancelMarkdownFollow();
|
|
891
915
|
var prev = ctx.fileTreeEl.querySelector(".file-tree-item.active");
|
|
@@ -1018,8 +1042,10 @@ function restoreExpanded(expandedSet) {
|
|
|
1018
1042
|
}
|
|
1019
1043
|
|
|
1020
1044
|
export function handleFsRead(msg) {
|
|
1045
|
+
if (routeFilePaneMessage(msg)) return;
|
|
1021
1046
|
var request = store.get('fileReadRequest');
|
|
1022
1047
|
var state = store.snap();
|
|
1048
|
+
if (request && (request.viewId !== (state.projectSplitViewActiveId || null) || request.socketGeneration !== state.projectSplitViewSocketGeneration)) return;
|
|
1023
1049
|
if (!request || !state.connected || msg.requestId !== request.requestId || msg.path !== request.path) return;
|
|
1024
1050
|
if ((request.projectSlug || null) !== (state.currentSlug || null)) return;
|
|
1025
1051
|
if ((request.sessionId || null) !== (state.activeSessionId == null ? null : String(state.activeSessionId))) return;
|
|
@@ -1094,7 +1120,7 @@ function renderEntries(container, entries, depth) {
|
|
|
1094
1120
|
(function (dirPath, childEl, rowEl, folderName) {
|
|
1095
1121
|
rowEl.addEventListener("click", function (e) {
|
|
1096
1122
|
e.stopPropagation();
|
|
1097
|
-
claimRightWorkbench("file-viewer");
|
|
1123
|
+
if (!activeSavedView()) claimRightWorkbench("file-viewer");
|
|
1098
1124
|
var isExpanded = rowEl.classList.contains("expanded");
|
|
1099
1125
|
if (isExpanded) {
|
|
1100
1126
|
rowEl.classList.remove("expanded");
|
|
@@ -1134,6 +1160,11 @@ function renderEntries(container, entries, depth) {
|
|
|
1134
1160
|
(function (filePath, rowEl) {
|
|
1135
1161
|
rowEl.addEventListener("click", function (e) {
|
|
1136
1162
|
e.stopPropagation();
|
|
1163
|
+
if (activeSavedView() && !store.get('paneMode')) {
|
|
1164
|
+
openIndependentFile(filePath);
|
|
1165
|
+
if (window.innerWidth <= 768) closeSidebar();
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1137
1168
|
claimRightWorkbench("file-viewer");
|
|
1138
1169
|
if (followedFileChanged(filePath)) cancelMarkdownFollow();
|
|
1139
1170
|
// Mark active
|
|
@@ -1382,6 +1413,7 @@ function pathsReferToSameFile(left, right) {
|
|
|
1382
1413
|
}
|
|
1383
1414
|
|
|
1384
1415
|
export function handleFileChanged(msg, presentedSource) {
|
|
1416
|
+
if (routeFilePaneMessage(msg)) return;
|
|
1385
1417
|
var source = store.get('studioDocumentView');
|
|
1386
1418
|
if (source && source.descriptor.path === msg.path && (!presentedSource || presentedDocument(msg.path) !== presentedSource)) return;
|
|
1387
1419
|
if (!msg.path || !pathsReferToSameFile(msg.path, currentFilePath)) return;
|
|
@@ -1545,81 +1577,7 @@ function showInlineDiff(oldStr, newStr, review) {
|
|
|
1545
1577
|
render();
|
|
1546
1578
|
}
|
|
1547
1579
|
|
|
1548
|
-
function mapExtToLanguage(
|
|
1549
|
-
var map = {
|
|
1550
|
-
js: "javascript", ts: "typescript", jsx: "javascript", tsx: "typescript",
|
|
1551
|
-
py: "python", rb: "ruby", go: "go", rs: "rust", java: "java",
|
|
1552
|
-
css: "css", html: "xml", xml: "xml", json: "json", yaml: "yaml",
|
|
1553
|
-
yml: "yaml", md: "markdown", sh: "bash", bash: "bash", zsh: "bash",
|
|
1554
|
-
sql: "sql", c: "c", cpp: "cpp", h: "c", hpp: "cpp",
|
|
1555
|
-
cs: "csharp", swift: "swift", kt: "kotlin", vue: "xml", svelte: "xml"
|
|
1556
|
-
};
|
|
1557
|
-
return map[ext] || null;
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
function currentLang() {
|
|
1561
|
-
if (!currentFilePath) return null;
|
|
1562
|
-
var ext = currentFilePath.split(".").pop().toLowerCase();
|
|
1563
|
-
return mapExtToLanguage(ext);
|
|
1564
|
-
}
|
|
1565
|
-
|
|
1566
|
-
function renderCodeWithLineNumbers(bodyEl, content, ext) {
|
|
1567
|
-
var lang = mapExtToLanguage(ext);
|
|
1568
|
-
var lines = content.split("\n");
|
|
1569
|
-
var lineCount = lines.length;
|
|
1570
|
-
|
|
1571
|
-
var viewer = document.createElement("div");
|
|
1572
|
-
viewer.className = "file-viewer-code";
|
|
1573
|
-
|
|
1574
|
-
var gutter = document.createElement("pre");
|
|
1575
|
-
gutter.className = "file-viewer-gutter";
|
|
1576
|
-
var nums = [];
|
|
1577
|
-
for (var i = 1; i <= lineCount; i++) nums.push(i);
|
|
1578
|
-
gutter.textContent = nums.join("\n");
|
|
1579
|
-
|
|
1580
|
-
var codeWrap = document.createElement("pre");
|
|
1581
|
-
codeWrap.className = "file-viewer-code-content";
|
|
1582
|
-
var codeEl = document.createElement("code");
|
|
1583
|
-
if (lang) codeEl.className = "language-" + lang;
|
|
1584
|
-
codeEl.textContent = content;
|
|
1585
|
-
codeWrap.appendChild(codeEl);
|
|
1586
|
-
|
|
1587
|
-
viewer.appendChild(gutter);
|
|
1588
|
-
viewer.appendChild(codeWrap);
|
|
1589
|
-
|
|
1590
|
-
bodyEl.innerHTML = "";
|
|
1591
|
-
bodyEl.appendChild(viewer);
|
|
1592
|
-
|
|
1593
|
-
if (typeof hljs !== "undefined" && lang) {
|
|
1594
|
-
hljs.highlightElement(codeEl);
|
|
1595
|
-
}
|
|
1596
|
-
}
|
|
1597
|
-
|
|
1598
|
-
function renderLargeTextFile(bodyEl, content, size) {
|
|
1599
|
-
var viewer = document.createElement("div");
|
|
1600
|
-
viewer.className = "file-viewer-large-text";
|
|
1601
|
-
|
|
1602
|
-
var notice = document.createElement("div");
|
|
1603
|
-
notice.className = "file-viewer-large-notice";
|
|
1604
|
-
notice.innerHTML = iconHtml("file-text") +
|
|
1605
|
-
"<span>Large file (" + formatSize(size || 0) + ") — showing plain text for performance</span>";
|
|
1606
|
-
|
|
1607
|
-
var codeWrap = document.createElement("pre");
|
|
1608
|
-
var codeEl = document.createElement("code");
|
|
1609
|
-
codeEl.textContent = content;
|
|
1610
|
-
codeWrap.appendChild(codeEl);
|
|
1611
|
-
|
|
1612
|
-
viewer.appendChild(notice);
|
|
1613
|
-
viewer.appendChild(codeWrap);
|
|
1614
|
-
bodyEl.innerHTML = "";
|
|
1615
|
-
bodyEl.appendChild(viewer);
|
|
1616
|
-
}
|
|
1617
|
-
|
|
1618
|
-
function formatSize(bytes) {
|
|
1619
|
-
if (bytes < 1024) return bytes + " B";
|
|
1620
|
-
if (bytes < 1048576) return (bytes / 1024).toFixed(1) + " KB";
|
|
1621
|
-
return (bytes / 1048576).toFixed(1) + " MB";
|
|
1622
|
-
}
|
|
1580
|
+
function currentLang() { return currentFilePath ? mapExtToLanguage(currentFilePath.split('.').pop().toLowerCase()) : null; }
|
|
1623
1581
|
|
|
1624
1582
|
// --- File edit history ---
|
|
1625
1583
|
|
|
@@ -1639,6 +1597,7 @@ function requestGitDiff(hash, hash2) {
|
|
|
1639
1597
|
}
|
|
1640
1598
|
|
|
1641
1599
|
export function handleFileHistory(msg) {
|
|
1600
|
+
if (routeFilePaneMessage(msg)) return;
|
|
1642
1601
|
currentHistoryEntries = msg.entries || [];
|
|
1643
1602
|
var historyBtn = document.getElementById("file-viewer-history");
|
|
1644
1603
|
|
|
@@ -1655,6 +1614,7 @@ export function handleFileHistory(msg) {
|
|
|
1655
1614
|
}
|
|
1656
1615
|
|
|
1657
1616
|
export function handleGitDiff(msg) {
|
|
1617
|
+
if (routeFilePaneMessage(msg)) return;
|
|
1658
1618
|
if (msg.hash && msg.diff !== undefined) {
|
|
1659
1619
|
var key = msg.hash2 ? msg.hash + ".." + msg.hash2 : msg.hash;
|
|
1660
1620
|
gitDiffCache[key] = msg.diff;
|
|
@@ -1673,6 +1633,7 @@ function requestFileAt(hash) {
|
|
|
1673
1633
|
}
|
|
1674
1634
|
|
|
1675
1635
|
export function handleFileAt(msg) {
|
|
1636
|
+
if (routeFilePaneMessage(msg)) return;
|
|
1676
1637
|
if (msg.hash && msg.content !== undefined) {
|
|
1677
1638
|
fileAtCache[msg.hash] = msg.content;
|
|
1678
1639
|
}
|
|
@@ -2370,7 +2331,7 @@ function renderViewFileContent(entry, content) {
|
|
|
2370
2331
|
bodyEl.appendChild(codeContainer);
|
|
2371
2332
|
}
|
|
2372
2333
|
|
|
2373
|
-
function navigateToEdit(edit) {
|
|
2334
|
+
export function navigateToEdit(edit) {
|
|
2374
2335
|
// If already in the same session, scroll directly without replaying history
|
|
2375
2336
|
if (ctx.activeSessionId === edit.sessionLocalId) {
|
|
2376
2337
|
scrollToToolElement(edit.toolId, edit.assistantUuid);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fileEntryContext } from './file-pane-entrypoints.js';
|
|
1
2
|
import { currentGithubWorkMarkup, refreshGithubWork, resetGithubWork } from './session-github.js';
|
|
2
3
|
import { store } from './store.js';
|
|
3
4
|
import { refreshIcons, iconHtml } from './icons.js';
|
|
@@ -332,7 +333,9 @@ function runAction(action, paths) {
|
|
|
332
333
|
}
|
|
333
334
|
|
|
334
335
|
function openFileDiff(filePath, reviewIndex) {
|
|
336
|
+
var receipt = fileEntryContext();
|
|
335
337
|
requestJson(apiPath("file-diff?path=" + encodeURIComponent(filePath)), { cache: "no-store" }).then(function (result) {
|
|
338
|
+
if (receipt !== fileEntryContext()) return;
|
|
336
339
|
var paths = currentStatus ? currentStatus.files.map(function (file) { return file.path; }) : [filePath];
|
|
337
340
|
var index = typeof reviewIndex === "number" ? reviewIndex : paths.indexOf(filePath);
|
|
338
341
|
if (index < 0) index = 0;
|
|
@@ -350,8 +353,10 @@ function openFileDiff(filePath, reviewIndex) {
|
|
|
350
353
|
}
|
|
351
354
|
|
|
352
355
|
function openSessionDiff(sessionKey, filePath) {
|
|
356
|
+
var receipt = fileEntryContext();
|
|
353
357
|
var url = apiPath("session-diff?session=" + encodeURIComponent(sessionKey) + "&path=" + encodeURIComponent(filePath));
|
|
354
358
|
requestJson(url, { cache: "no-store" }).then(function (result) {
|
|
359
|
+
if (receipt !== fileEntryContext()) return;
|
|
355
360
|
openWorkingTreeDiff(result);
|
|
356
361
|
showToast("Compared with start of " + (result.sessionTitle || "session"));
|
|
357
362
|
}).catch(function (err) {
|
|
@@ -2,7 +2,7 @@ import { openLinear, initLinearPanel } from './linear-panel.js';
|
|
|
2
2
|
import { store } from './store.js';
|
|
3
3
|
import { getWs } from './ws-ref.js';
|
|
4
4
|
import { showToast } from './utils.js';
|
|
5
|
-
import { claimRightWorkbench, registerRightWorkbench, releaseRightWorkbench } from './right-workbench.js';
|
|
5
|
+
import { handlesWorkbenchKey, claimRightWorkbench, registerRightWorkbench, releaseRightWorkbench } from './right-workbench.js';
|
|
6
6
|
import { refreshIcons } from './icons.js';
|
|
7
7
|
import { enhanceClayLogLinks } from './clay-log-links.js';
|
|
8
8
|
import { updateIssueLink } from './clay-issue-links.js';
|
|
@@ -169,7 +169,7 @@ export function initIssues() {
|
|
|
169
169
|
registerRightWorkbench("issues", closeIssues);
|
|
170
170
|
var el = document.createElement('section');
|
|
171
171
|
el.id = 'issues-panel'; el.className = 'hidden'; el.setAttribute('aria-label', 'Project Issues');
|
|
172
|
-
el.innerHTML = '<header class="issues-topbar"><button class="issues-icon-btn" data-issues-back aria-label="Back to issues" title="Back to issue list"><i data-lucide="arrow-left"></i></button><span class="issues-title"><i data-lucide="circle-dot"></i>Issues</span><span class="issues-subtitle">Manage issues through Driver chat.</span><
|
|
172
|
+
el.innerHTML = '<header class="issues-topbar"><button class="issues-icon-btn" data-issues-back aria-label="Back to issues" title="Back to issue list"><i data-lucide="arrow-left"></i></button><span class="issues-title"><i data-lucide="circle-dot"></i>Issues</span><span class="issues-subtitle">Manage issues through Driver chat.</span><div class="issues-window-actions"><button class="issues-icon-btn" data-issues-wide aria-label="Widen Issues" title="Widen Issues" aria-pressed="false"><i data-lucide="chevrons-left-right"></i></button><button class="issues-icon-btn" data-issues-full aria-label="Toggle Issues fullscreen" title="Toggle fullscreen" aria-pressed="false"><i data-lucide="maximize-2"></i></button><button class="issues-icon-btn" data-issues-close aria-label="Close Issues" title="Close Issues"><i data-lucide="x"></i></button></div></header><div class="issues-filters"><label class="issues-search"><i data-lucide="search"></i><input name="query" type="search" placeholder="Search issues" aria-label="Search issues"></label><select name="filter-status" aria-label="Filter status"><option value="">All statuses</option><option value="open">Open</option><option value="in_progress">In progress</option><option value="resolved">Resolved</option><option value="closed">Closed</option></select><select name="filter-type" aria-label="Filter type"><option value="">All types</option><option value="bug">Bug</option><option value="feature">Feature</option><option value="plan">Plan</option></select><button type="button" class="issues-linear-link" data-open-linear title="Open Linear" aria-label="Open Linear">Linear<i data-lucide="chevron-right"></i></button></div><p class="issues-notice" role="status"></p><main class="issues-content" role="region" aria-live="polite"></main><div class="issues-delete-dialog hidden" role="dialog" aria-modal="true" aria-labelledby="issues-delete-heading"><div class="issues-delete-card"><h2 id="issues-delete-heading">Delete this issue?</h2><p>This removes the issue from the board but preserves its history.</p><p class="issues-delete-title"></p><p class="issues-delete-status" role="status"></p><div><button type="button" data-issue-delete-cancel>Cancel</button><button type="button" data-issue-delete-confirm>Delete issue</button></div></div></div>';
|
|
173
173
|
host.appendChild(el);
|
|
174
174
|
el.querySelector('[data-open-linear]').addEventListener('click', function () { openLinear(); });
|
|
175
175
|
el.addEventListener('input', handleIssueGithubInput);
|
|
@@ -226,9 +226,8 @@ export function initIssues() {
|
|
|
226
226
|
});
|
|
227
227
|
store.subscribe(function (state, previous) {
|
|
228
228
|
if (state.currentSlug !== previous.currentSlug || (previous.connected && !state.connected)) { closeIssues(); store.set({ issuesRequests: {}, issuesSelected: null, issuesCache: {}, issuesMutationRequest: null }); }
|
|
229
|
-
if (state.issuesOpen && (state.projectLogsOpen || state.scheduledTasksOpen)) closeIssues();
|
|
230
229
|
});
|
|
231
|
-
document.addEventListener('keydown', function (event) { if (store.get('issuesOpen')) { deleteDialogKeydown(event); if (!event.defaultPrevented && event.key === 'Escape') closeIssues(); } });
|
|
230
|
+
document.addEventListener('keydown', function (event) { if (store.get('issuesOpen') && handlesWorkbenchKey('issues', event)) { deleteDialogKeydown(event); if (!event.defaultPrevented && event.key === 'Escape') closeIssues(); } });
|
|
232
231
|
}
|
|
233
232
|
function showIssueEdit(entry) {
|
|
234
233
|
if (!entry) return;
|
|
@@ -4,7 +4,7 @@ import { escapeHtml } from './utils.js';
|
|
|
4
4
|
import { renderMarkdown } from './markdown.js';
|
|
5
5
|
import { refreshIcons } from './icons.js';
|
|
6
6
|
import { linearIconMarkup } from './session-github.js';
|
|
7
|
-
import { claimRightWorkbench, registerRightWorkbench, releaseRightWorkbench } from './right-workbench.js';
|
|
7
|
+
import { handlesWorkbenchKey, claimRightWorkbench, registerRightWorkbench, releaseRightWorkbench } from './right-workbench.js';
|
|
8
8
|
|
|
9
9
|
function esc(value) { return escapeHtml(String(value || '')).replace(/"/g, '"').replace(/'/g, '''); }
|
|
10
10
|
function panel() { return document.getElementById('linear-panel'); }
|
|
@@ -145,7 +145,7 @@ export function initLinearPanel() {
|
|
|
145
145
|
else if (issue && button.hasAttribute('data-linear-start')) { button.disabled = true; request('linear_start_work', { url: issue.url }); }
|
|
146
146
|
else if (issue && button.hasAttribute('data-linear-more')) { button.disabled = true; request('linear_read', { url: issue.url, cursor: issue.pageInfo.endCursor }); }
|
|
147
147
|
});
|
|
148
|
-
document.addEventListener('keydown', function (event) { if (event.key === 'Escape' && store.get('linearOpen')) closeLinear(); });
|
|
148
|
+
document.addEventListener('keydown', function (event) { if (event.key === 'Escape' && store.get('linearOpen') && handlesWorkbenchKey('linear', event)) closeLinear(); });
|
|
149
149
|
store.subscribe(function (state, previous) {
|
|
150
150
|
if (state.currentSlug !== previous.currentSlug || state.myUserId !== previous.myUserId || state.dmMode !== previous.dmMode || (previous.connected && !state.connected)) closeLinear();
|
|
151
151
|
else if (state.linearOpen && (state.githubWorkSessions !== previous.githubWorkSessions || state.activeSessionId !== previous.activeSessionId) && state.linearIssue) renderIssue();
|
|
@@ -103,7 +103,7 @@ function bindPanel() {
|
|
|
103
103
|
panel.querySelector('[data-knowledge-forward]').addEventListener('click', function () { navigateHistory('forward'); });
|
|
104
104
|
panel.querySelector('.knowledge-search input').addEventListener('input', scheduleSearch);
|
|
105
105
|
panel.querySelector('[data-knowledge-wide]').addEventListener('click', function () { var key = workspaceKey(); patchState(key, { wide: !stateFor(key).wide }); renderShell(); });
|
|
106
|
-
panel.querySelector('[data-knowledge-full]').addEventListener('click',
|
|
106
|
+
panel.querySelector('[data-knowledge-full]').addEventListener('click', togglePaneExpansion);
|
|
107
107
|
panel.querySelector('[data-delete-cancel]').addEventListener('click', closeDelete);
|
|
108
108
|
panel.querySelector('[data-delete-confirm]').addEventListener('click', confirmDelete);
|
|
109
109
|
panel.addEventListener('click', handlePanelClick);
|
|
@@ -326,10 +326,14 @@ function retrySync() {
|
|
|
326
326
|
simpleRequest('knowledge_sync', { names: retry.names }, { syncRetry: retry });
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
+
function togglePaneExpansion() { var key = workspaceKey(), state = stateFor(key);
|
|
330
|
+
if (state.fullscreen) { patchState(key, { paneExpanded: false }); exitEditor(); return; }
|
|
331
|
+
patchState(key, { paneExpanded: !state.paneExpanded }); renderShell();
|
|
332
|
+
}
|
|
329
333
|
function renderShell() {
|
|
330
|
-
var state = stateFor(workspaceKey()); panel.classList.toggle('knowledge-wide', state.wide); panel.classList.toggle('panel-fullscreen', state.fullscreen);
|
|
331
|
-
panel.querySelector('[data-knowledge-wide]').setAttribute('aria-pressed', String(state.wide)); panel.querySelector('[data-knowledge-full]').setAttribute('aria-pressed', String(state.fullscreen));
|
|
332
|
-
panel.querySelector('[data-knowledge-full]').hidden =
|
|
334
|
+
var state = stateFor(workspaceKey()); panel.classList.toggle('knowledge-wide', state.wide); panel.classList.toggle('panel-fullscreen', !!(state.fullscreen || state.paneExpanded));
|
|
335
|
+
panel.querySelector('[data-knowledge-wide]').setAttribute('aria-pressed', String(state.wide)); panel.querySelector('[data-knowledge-full]').setAttribute('aria-pressed', String(!!(state.fullscreen || state.paneExpanded)));
|
|
336
|
+
panel.querySelector('[data-knowledge-full]').hidden = false; panel.querySelector('[data-knowledge-wide]').hidden = state.fullscreen;
|
|
333
337
|
panel.querySelector('[data-knowledge-back]').disabled = !(state.navigationBack || []).length; panel.querySelector('[data-knowledge-forward]').disabled = !(state.navigationForward || []).length;
|
|
334
338
|
}
|
|
335
339
|
function renderStatus() {
|
|
@@ -433,12 +437,12 @@ export function openKnowledgeWorkbench() {
|
|
|
433
437
|
ensurePanel(); if (!panel || !isMateWorkspace(store.snap())) return; returnFocus = document.activeElement;
|
|
434
438
|
claimRightWorkbench('mate-knowledge'); panel.classList.remove('hidden'); setKnowledgeNarrowBackground(panel, true); var button = document.getElementById('knowledge-workbench-btn'); if (button) button.classList.add('active');
|
|
435
439
|
var key = workspaceKey(); var state = stateFor(key); var drafts = Object.assign({}, state.drafts); if (state.selected && drafts[state.selected]) drafts[state.selected] = Object.assign({}, drafts[state.selected], { mode: 'read' });
|
|
436
|
-
setState(key, Object.assign({}, state, { drafts: drafts, fullscreen: false, loading: true, error: '' })); simpleRequest('knowledge_list'); if (stateFor(key).query.trim()) { patchState(key, { searching: true }); requestSearch(key, stateFor(key).query, 0, false); } render(); setTimeout(function () { var search = panel.querySelector('.knowledge-search input'); if (search) search.focus(); }, 0);
|
|
440
|
+
setState(key, Object.assign({}, state, { drafts: drafts, fullscreen: false, paneExpanded: false, loading: true, error: '' })); simpleRequest('knowledge_list'); if (stateFor(key).query.trim()) { patchState(key, { searching: true }); requestSearch(key, stateFor(key).query, 0, false); } render(); setTimeout(function () { var search = panel.querySelector('.knowledge-search input'); if (search) search.focus(); }, 0);
|
|
437
441
|
}
|
|
438
442
|
export function closeKnowledgeWorkbench(skipSave) {
|
|
439
443
|
if (!panel) return; var active = document.activeElement; var shouldReturn = skipSave !== true && (!active || active === document.body || panel.contains(active));
|
|
440
444
|
var key = workspaceKey(); cancelScheduledSearch(); if (deleteTarget) closeDelete(); if (skipSave !== true) { saveScroll(); var selected = stateFor(key).selected; if (selected) saveDraftNow(key, selected); }
|
|
441
|
-
patchState(key, { fullscreen: false }); destroyKnowledgeEditor(); panel.classList.add('hidden'); setKnowledgeNarrowBackground(panel, false); releaseRightWorkbench('mate-knowledge');
|
|
445
|
+
patchState(key, { fullscreen: false, paneExpanded: false }); destroyKnowledgeEditor(); panel.classList.add('hidden'); setKnowledgeNarrowBackground(panel, false); releaseRightWorkbench('mate-knowledge');
|
|
442
446
|
var button = document.getElementById('knowledge-workbench-btn'); if (button) button.classList.remove('active'); if (shouldReturn && returnFocus && returnFocus.isConnected) returnFocus.focus(); returnFocus = null;
|
|
443
447
|
}
|
|
444
448
|
export function initMateKnowledgeWorkbench() {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
1
2
|
import { initNotificationSettings, saveNotificationPreference, updatePushSettings } from './notification-settings.js';
|
|
2
3
|
import { initExtensionSettings } from './extension-settings.js';
|
|
3
4
|
import { copyToClipboard, showToast } from './utils.js';
|
|
@@ -71,7 +72,9 @@ export function initNotifications(_ctx) {
|
|
|
71
72
|
var $ = ctx.$;
|
|
72
73
|
|
|
73
74
|
// --- Mobile viewport (iOS keyboard handling) ---
|
|
74
|
-
|
|
75
|
+
// Embedded panes follow the host viewport. Their layout resize is not a
|
|
76
|
+
// software keyboard opening and must not force the transcript to its tail.
|
|
77
|
+
if (window.visualViewport && !store.get('paneMode')) {
|
|
75
78
|
var layout = $("layout");
|
|
76
79
|
var mobileTabBar = document.getElementById("mobile-tab-bar");
|
|
77
80
|
var keyboardWasOpen = false;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { paneHeaderRuntime, initPaneHeaderRuntime } from './pane-header-runtime.js';
|
|
2
|
+
import { sendBoundBrowserCommand } from './pane-browser-transport.js';
|
|
1
3
|
import { disarmStickyBottom } from './chat-render-runtime.js';
|
|
2
4
|
import { store } from './store.js';
|
|
3
5
|
import { getWs } from './ws-ref.js';
|
|
@@ -204,10 +206,13 @@ export function initPaneActivation() {
|
|
|
204
206
|
return binding && { ownerId: binding.viewerId, projectSlug: binding.projectSlug, sessionId: binding.sessionId,
|
|
205
207
|
binding: binding, ready: store.get('connected') && store.get('paneReady') && !store.get('paneSuspended') };
|
|
206
208
|
},
|
|
209
|
+
headerRuntime: paneHeaderRuntime,
|
|
210
|
+
browserCommand: sendBoundBrowserCommand,
|
|
207
211
|
activate: activatePaneSession,
|
|
208
212
|
checkDraft: function () { if (!store.get('panePendingBinding')) { var draft = takeComposerDraft(); restoreComposerDraft(draft); } },
|
|
209
213
|
suspend: suspendPane,
|
|
210
214
|
};
|
|
215
|
+
initPaneHeaderRuntime(frame);
|
|
211
216
|
// A pointer that began on the previous conversation cannot become a Stop or
|
|
212
217
|
// approval click on the replacement conversation after hydration.
|
|
213
218
|
document.addEventListener('pointerdown', function () { store.set({ panePointerBinding: store.get('paneCommittedBinding') }); }, true);
|
|
@@ -63,7 +63,11 @@ export function livePaneBinding(frame, message) {
|
|
|
63
63
|
var target = live && paneTarget(state, state.paneBindingSessions || [], live.sessionId);
|
|
64
64
|
var inLegacySplit = !!(live && split && split.panes.some(function (pane) { return pane.slug === live.projectSlug && pane.sessionId === live.sessionId; }));
|
|
65
65
|
var activePresentation = (state.projectSplitViews || []).find(function (view) { return view.id === state.projectSplitViewActiveId; });
|
|
66
|
-
var
|
|
66
|
+
var pair = activePresentation && activePresentation.workspace && activePresentation.workspace.linkedPair;
|
|
67
|
+
var linkedPresentation = !!(split && pair && split.groupId === pair.groupId && (state.splitGroups || []).some(function (group) {
|
|
68
|
+
return group.id === pair.groupId && group.pair && (state.paneBindingSessions || []).some(function (session) { return session.id === group.pair.driverId && session.sessionOriginId === pair.driverOriginId; });
|
|
69
|
+
}));
|
|
70
|
+
var inPresentation = !!(live && live.binding && (!split || linkedPresentation) && activePresentation && activePresentation.sessions.some(function (session) {
|
|
67
71
|
return session.id === live.sessionId && session.sessionOriginId === live.binding.sessionOriginId;
|
|
68
72
|
}));
|
|
69
73
|
var inSingletonPresentation = !!(live && live.binding && !split && !activePresentation &&
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { store } from './store.js';
|
|
2
|
+
import { selectBrowserPane } from './pane-browser-transport.js';
|
|
2
3
|
import { livePaneBinding, paneMessageBinding } from './pane-binding.js';
|
|
3
4
|
import { showToast } from './utils.js';
|
|
4
5
|
|
|
@@ -9,14 +10,27 @@ export function forwardPaneBrowserLink(url) {
|
|
|
9
10
|
}
|
|
10
11
|
export function handlePaneBrowserMessage(event, host) {
|
|
11
12
|
if (!event || event.origin !== window.location.origin || !host || !event.data || event.data.type !== 'clay-pane-open-browser') return false;
|
|
12
|
-
var msg = event.data, s = store.snap();
|
|
13
|
+
var msg = event.data, s = Object.assign({}, store.snap());
|
|
13
14
|
var frame = Array.from(host.querySelectorAll('.split-pane-frame')).find(function (item) { return item.contentWindow === event.source; });
|
|
14
|
-
var pane = frame && s.splitPanes && s.splitPanes.panes && s.splitPanes.panes.find(function (item) { return String(item.slug) === frame.dataset.projectSlug && String(item.sessionId) === frame.dataset.sessionId; });
|
|
15
|
-
if (!pane || msg.projectSlug !== s.currentSlug || msg.projectSlug !== frame.dataset.projectSlug || String(msg.sessionId) !== frame.dataset.sessionId) return true;
|
|
15
|
+
var pane = frame && frame.clayPane ? { slug: frame.dataset.projectSlug, sessionId: frame.dataset.sessionId } : frame && s.splitPanes && s.splitPanes.panes && s.splitPanes.panes.find(function (item) { return String(item.slug) === frame.dataset.projectSlug && String(item.sessionId) === frame.dataset.sessionId; });
|
|
16
|
+
if (!s.connected || !pane || msg.projectSlug !== s.currentSlug || msg.projectSlug !== frame.dataset.projectSlug || String(msg.sessionId) !== frame.dataset.sessionId) return true;
|
|
16
17
|
var expected = new URL('/p/' + encodeURIComponent(pane.slug) + '/?pane=1&session=' + encodeURIComponent(pane.sessionId), window.location.href).href;
|
|
17
18
|
if (frame.clayPane ? !livePaneBinding(frame, msg) : frame.src !== expected) return true;
|
|
18
|
-
//
|
|
19
|
-
var binding = frame.clayPane && frame.clayPane.identity().binding;
|
|
20
|
-
|
|
19
|
+
// Present in the host while browser effects retain the exact source pane binding.
|
|
20
|
+
var binding = frame.clayPane && frame.clayPane.identity().binding, document = frame.contentDocument;
|
|
21
|
+
function current() {
|
|
22
|
+
var now = store.snap();
|
|
23
|
+
if (!now.connected || now.currentSlug !== s.currentSlug || now.myUserId !== s.myUserId ||
|
|
24
|
+
now.currentProjectOwnerId !== s.currentProjectOwnerId || now.projectSplitViewSocketGeneration !== s.projectSplitViewSocketGeneration ||
|
|
25
|
+
frame.contentWindow !== event.source || frame.contentDocument !== document ||
|
|
26
|
+
frame.dataset.projectSlug !== msg.projectSlug || String(frame.dataset.sessionId) !== String(msg.sessionId) ||
|
|
27
|
+
!Array.from(host.querySelectorAll('.split-pane-frame')).includes(frame)) return false;
|
|
28
|
+
if (binding) return !!frame.clayPane && livePaneBinding(frame, msg) && frame.clayPane.identity().binding === binding;
|
|
29
|
+
return frame.src === expected && !!(now.splitPanes && now.splitPanes.panes.some(function (item) { return String(item.slug) === pane.slug && String(item.sessionId) === String(pane.sessionId); }));
|
|
30
|
+
}
|
|
31
|
+
import('./shared-browser.js').then(function (module) {
|
|
32
|
+
if (!current() || binding && !selectBrowserPane(frame, binding)) return;
|
|
33
|
+
module.openSharedBrowserAt(msg.url);
|
|
34
|
+
}).catch(function (error) { showToast(error.message || 'Could not open the browser.', 'warn'); });
|
|
21
35
|
return true;
|
|
22
36
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getWs } from './ws-ref.js';
|
|
3
|
+
import { livePaneBinding, paneMessageBinding, samePaneBinding, paneTarget } from './pane-binding.js';
|
|
4
|
+
|
|
5
|
+
// Browser-only presentation bridge. Effects still use the source pane's real
|
|
6
|
+
// authenticated socket and the existing server browser service.
|
|
7
|
+
function browserSource() { return store.get('paneBrowserSource') || null; }
|
|
8
|
+
function focusedOrigin() {
|
|
9
|
+
var focused = store.get('projectSplitViewFocusedOrigin') || store.get('projectSplitViewSingletonOrigin');
|
|
10
|
+
if (focused) return focused;
|
|
11
|
+
var view = (store.get('projectSplitViews') || []).find(function (item) { return item.id === store.get('projectSplitViewActiveId'); });
|
|
12
|
+
return view && view.activeSessionOriginId || store.get('sessionOriginId') || null;
|
|
13
|
+
}
|
|
14
|
+
var receive = null;
|
|
15
|
+
var changed = null;
|
|
16
|
+
var fieldsByAction = {
|
|
17
|
+
state_request: [], open: ['url', 'newTab', 'incognito'], view: ['browserId', 'visible'],
|
|
18
|
+
control: ['browserId', 'control'], close: ['browserId'], end: ['browserId'], input: ['browserId', 'epoch', 'event'],
|
|
19
|
+
};
|
|
20
|
+
function frames() {
|
|
21
|
+
return Array.from(document.querySelectorAll('#split-host .split-pane:not(.split-pane-cached) .split-pane-frame'));
|
|
22
|
+
}
|
|
23
|
+
export function hasBrowserPaneHost() { return !store.get('paneMode') && frames().some(function (frame) { return !!frame.clayPane || !!paneTarget(store.snap(), store.get('paneBindingSessions') || [], Number(frame.dataset.sessionId)); }); }
|
|
24
|
+
function current() {
|
|
25
|
+
var source = browserSource();
|
|
26
|
+
if (!source || source.socket !== getWs() || source.owner !== store.get('myUserId') || source.project !== store.get('currentSlug')) return false;
|
|
27
|
+
var frame = source.frame;
|
|
28
|
+
return frames().includes(frame) && frame.contentWindow === source.window && frame.contentDocument === source.document &&
|
|
29
|
+
!frame.dataset.requestedSessionId && livePaneBinding(frame, { paneBinding: source.binding });
|
|
30
|
+
}
|
|
31
|
+
export function browserPaneSessionId() { return current() ? browserSource().binding.sessionId : null; }
|
|
32
|
+
export function browserPaneStamp() { var source = browserSource(); return current() ? source.binding.requestId + ':' + source.binding.generation : null; }
|
|
33
|
+
export function browserPaneBound() { return !!browserSource(); }
|
|
34
|
+
export function selectBrowserPane(frame, binding) {
|
|
35
|
+
var source = browserSource();
|
|
36
|
+
if (!frame || !binding || !frames().includes(frame) || frame.dataset.requestedSessionId || !livePaneBinding(frame, { paneBinding: binding })) return false;
|
|
37
|
+
if (current() && source.frame === frame && samePaneBinding(source.binding, binding)) {
|
|
38
|
+
if (source.focusedOrigin !== focusedOrigin()) store.set({ paneBrowserSource: Object.assign({}, source, { focusedOrigin: focusedOrigin() }) });
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
var previous = store.get('sharedBrowser');
|
|
42
|
+
if (current() && previous) sendBrowserPaneCommand('view', { browserId: previous.id, visible: false });
|
|
43
|
+
store.set({ paneBrowserSource: { frame: frame, window: frame.contentWindow, document: frame.contentDocument, binding: binding,
|
|
44
|
+
socket: getWs(), owner: store.get('myUserId'), project: store.get('currentSlug'), focusedOrigin: focusedOrigin() }, paneBrowserTarget: binding });
|
|
45
|
+
if (changed) changed(true);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
export function ensureBrowserPaneSource() {
|
|
49
|
+
if (current() && browserSource().focusedOrigin === focusedOrigin()) return true;
|
|
50
|
+
var candidates = frames();
|
|
51
|
+
var origin = focusedOrigin();
|
|
52
|
+
var frame = candidates.find(function (item) { return origin && item.dataset.sessionOriginId === origin; }) ||
|
|
53
|
+
candidates.find(function (item) { var pane = item.closest('.split-pane'); return pane && pane.dataset.active === 'true'; }) || candidates[0];
|
|
54
|
+
var live = frame && frame.clayPane && frame.clayPane.identity();
|
|
55
|
+
return !!live && selectBrowserPane(frame, live.binding);
|
|
56
|
+
}
|
|
57
|
+
export function sendBrowserPaneCommand(action, fields) {
|
|
58
|
+
var source = browserSource();
|
|
59
|
+
if (!Object.prototype.hasOwnProperty.call(fieldsByAction, action)) return false;
|
|
60
|
+
if (!source && !hasBrowserPaneHost()) return null;
|
|
61
|
+
if (!current() || !source.frame.clayPane || typeof source.frame.clayPane.browserCommand !== 'function') return false;
|
|
62
|
+
return source.frame.clayPane.browserCommand(action, fields || {}, source.binding);
|
|
63
|
+
}
|
|
64
|
+
export function sendBoundBrowserCommand(action, fields, binding) {
|
|
65
|
+
var allowed = Object.prototype.hasOwnProperty.call(fieldsByAction, action) && fieldsByAction[action], ws = getWs();
|
|
66
|
+
if (!allowed || !ws || ws.readyState !== 1 || !samePaneBinding(binding, paneMessageBinding()) ||
|
|
67
|
+
!store.get('connected') || store.get('panePendingBinding') || store.get('activeSessionId') !== binding.sessionId) return false;
|
|
68
|
+
var message = { type: 'shared_browser_' + action, sessionId: binding.sessionId, paneBinding: binding };
|
|
69
|
+
allowed.forEach(function (key) { if (Object.prototype.hasOwnProperty.call(fields, key)) message[key] = fields[key]; });
|
|
70
|
+
ws.send(JSON.stringify(message));
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
export function forwardBrowserPaneMessage(message) {
|
|
74
|
+
var frame = window.frameElement;
|
|
75
|
+
if (!store.get('paneMode') || !frame || !frame.clayPane) return false;
|
|
76
|
+
if (['shared_browser_state', 'shared_browser_frame', 'shared_browser_error'].indexOf(message.type) < 0) return false;
|
|
77
|
+
var binding = paneMessageBinding();
|
|
78
|
+
if (binding && samePaneBinding(binding, message.paneActivation)) {
|
|
79
|
+
window.parent.postMessage({ type: 'clay-pane-browser-update', paneBinding: binding, message: message }, location.origin);
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
export function initBrowserPaneTransport(onMessage, onChanged) {
|
|
84
|
+
if (store.get('paneMode')) return;
|
|
85
|
+
receive = onMessage; changed = onChanged;
|
|
86
|
+
window.addEventListener('message', function (event) {
|
|
87
|
+
var data = event.data;
|
|
88
|
+
if (event.origin !== location.origin || !data || data.type !== 'clay-pane-browser-update' || !data.message) return;
|
|
89
|
+
var frame = frames().find(function (item) { return item.contentWindow === event.source; });
|
|
90
|
+
var message = data.message, source = browserSource();
|
|
91
|
+
if (!frame || !livePaneBinding(frame, data) || !samePaneBinding(data.paneBinding, message.paneActivation) ||
|
|
92
|
+
message.sessionId !== data.paneBinding.sessionId || ['shared_browser_state', 'shared_browser_frame', 'shared_browser_error'].indexOf(message.type) < 0) return;
|
|
93
|
+
if (!current() || source.frame !== frame || !samePaneBinding(source.binding, data.paneBinding)) {
|
|
94
|
+
if (message.type !== 'shared_browser_state' || !message.present || !selectBrowserPane(frame, data.paneBinding)) return;
|
|
95
|
+
}
|
|
96
|
+
receive(message, true);
|
|
97
|
+
});
|
|
98
|
+
document.addEventListener('clay-pane-ready', function () {
|
|
99
|
+
queueMicrotask(function () {
|
|
100
|
+
if (!current() && ensureBrowserPaneSource()) sendBrowserPaneCommand('state_request', {});
|
|
101
|
+
});
|
|
102
|
+
}, true);
|
|
103
|
+
store.subscribe(function () {
|
|
104
|
+
var source = browserSource();
|
|
105
|
+
if (source && !current()) {
|
|
106
|
+
store.set({ paneBrowserSource: null, paneBrowserTarget: null });
|
|
107
|
+
if (changed) changed(false);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|