dsh-workspace-explorer 1.1.0 → 1.1.2
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/CHANGELOG.md +15 -0
- package/README.md +11 -9
- package/docs/screenshots/grid-favorites.png +0 -0
- package/docs/screenshots/stl-preview.png +0 -0
- package/docs/screenshots/workspace-search.png +0 -0
- package/lib/client.js +820 -162
- package/package.json +53 -52
- package/scripts/build-client.mjs +1 -0
- package/src/client/conversation-drop.js +135 -0
- package/src/client/main.js +361 -91
- package/src/client/sidebar-motion.js +69 -0
- package/src/document-types.js +2 -2
- package/src/host/ExplorerShell.cs +183 -183
- package/src/host/files.js +25 -6
- package/src/host/pdf-assets.js +30 -30
- package/src/host/shell-files.js +81 -81
- package/src/thumbnail-types.js +18 -18
- package/src/ui/composer-drop.js +119 -119
- package/src/ui/explorer.js +42 -42
- package/src/ui/favorites.js +176 -124
- package/src/ui/file-menu.js +125 -125
- package/src/ui/horizontal-wheel.js +11 -11
- package/src/ui/navigation.js +9 -0
- package/src/ui/pdf-preview.js +135 -135
- package/src/ui/preview-unavailable.js +6 -6
- package/src/ui/preview-zoom.js +47 -47
- package/src/ui/preview.js +58 -12
- package/src/ui/styles.js +74 -15
- package/src/ui/thumbnail.js +105 -105
- package/src/vendor/ui/extra-views.js +1 -1
package/lib/client.js
CHANGED
|
@@ -16,6 +16,7 @@ window.__ModuleLoader__.load({
|
|
|
16
16
|
// Frozen platform table entries (see the shell's static module table).
|
|
17
17
|
var react = require("react");
|
|
18
18
|
var reactDom = require("react-dom");
|
|
19
|
+
var reactDomClient = require("react-dom/client");
|
|
19
20
|
var jsxRuntime = require("react/jsx-runtime");
|
|
20
21
|
var primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
21
22
|
|
|
@@ -77,6 +78,15 @@ window.__ModuleLoader__.load({
|
|
|
77
78
|
return 'dsh-resource://file/' + (sessionId ? 'session/' + encodeURIComponent(sessionId) : 'absolute') + '/' + encoded;
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
function sessionFilePath(address, sessionId) {
|
|
82
|
+
const match = /^dsh-resource:\/\/file\/session\/([^/?#]+)\/([^?#]+)(?:[?#].*)?$/.exec(address);
|
|
83
|
+
if (!match || !sessionId) return null;
|
|
84
|
+
try {
|
|
85
|
+
if (decodeURIComponent(match[1]) !== sessionId) return null;
|
|
86
|
+
return match[2].split('/').map(decodeURIComponent).join('/');
|
|
87
|
+
} catch { return null; }
|
|
88
|
+
}
|
|
89
|
+
|
|
80
90
|
__exports["move"] = move;
|
|
81
91
|
__exports["visit"] = visit;
|
|
82
92
|
__exports["joinPath"] = joinPath;
|
|
@@ -85,6 +95,7 @@ window.__ModuleLoader__.load({
|
|
|
85
95
|
__exports["underPath"] = underPath;
|
|
86
96
|
__exports["renamePath"] = renamePath;
|
|
87
97
|
__exports["resourceAddress"] = resourceAddress;
|
|
98
|
+
__exports["sessionFilePath"] = sessionFilePath;
|
|
88
99
|
};
|
|
89
100
|
|
|
90
101
|
__modules["vendor/model/common.js"] = function (module, exports, require) {
|
|
@@ -9826,7 +9837,7 @@ window.__ModuleLoader__.load({
|
|
|
9826
9837
|
const { PdfPreview } = __require("ui/pdf-preview.js");
|
|
9827
9838
|
const { usePreviewZoom } = __require("ui/preview-zoom.js");
|
|
9828
9839
|
|
|
9829
|
-
const { createElement: h, useEffect, useRef, useState } = react;
|
|
9840
|
+
const { createElement: h, useEffect, useLayoutEffect, useRef, useState } = react;
|
|
9830
9841
|
const coverActions = {
|
|
9831
9842
|
[PREVIEW_IDS.archive]: '解压预览',
|
|
9832
9843
|
[PREVIEW_IDS.model]: '查看模型',
|
|
@@ -9840,6 +9851,14 @@ window.__ModuleLoader__.load({
|
|
|
9840
9851
|
[PREVIEW_IDS.dfu]: '查看固件',
|
|
9841
9852
|
};
|
|
9842
9853
|
|
|
9854
|
+
function formatSize(bytes) {
|
|
9855
|
+
if (typeof bytes !== 'number') return '—';
|
|
9856
|
+
if (bytes < 1024) return bytes + ' B';
|
|
9857
|
+
if (bytes < 1024 ** 2) return (bytes / 1024).toFixed(1) + ' KB';
|
|
9858
|
+
if (bytes < 1024 ** 3) return (bytes / 1024 ** 2).toFixed(1) + ' MB';
|
|
9859
|
+
return (bytes / 1024 ** 3).toFixed(1) + ' GB';
|
|
9860
|
+
}
|
|
9861
|
+
|
|
9843
9862
|
class PreviewBoundary extends react.Component {
|
|
9844
9863
|
state = { failed: false };
|
|
9845
9864
|
|
|
@@ -9854,6 +9873,18 @@ window.__ModuleLoader__.load({
|
|
|
9854
9873
|
|
|
9855
9874
|
function PreviewCover({ file, root, videoType, label, archive, start }) {
|
|
9856
9875
|
const [poster, setPoster] = useState(null);
|
|
9876
|
+
const [resolvedSize, setResolvedSize] = useState(undefined);
|
|
9877
|
+
const { absolute, name, size } = file;
|
|
9878
|
+
|
|
9879
|
+
useEffect(() => {
|
|
9880
|
+
if (size !== undefined) return;
|
|
9881
|
+
const controller = new AbortController();
|
|
9882
|
+
void disks.locate(absolute, controller.signal, root).then(result => {
|
|
9883
|
+
if (!controller.signal.aborted) setResolvedSize(result.size);
|
|
9884
|
+
}, () => {});
|
|
9885
|
+
return () => controller.abort();
|
|
9886
|
+
}, [absolute, size, root]);
|
|
9887
|
+
|
|
9857
9888
|
useEffect(() => {
|
|
9858
9889
|
if (!videoType) return;
|
|
9859
9890
|
const controller = new AbortController();
|
|
@@ -9869,16 +9900,22 @@ window.__ModuleLoader__.load({
|
|
|
9869
9900
|
h('rect', { x: 5, y: 3, width: 26, height: 30, rx: 5, fill: '#7462c4' }),
|
|
9870
9901
|
h('path', { d: 'M18 5h3v4h-3v4h3v4h-3v4m-2 0h6v6h-6z', fill: 'none', stroke: '#fff', strokeWidth: 1.6, strokeLinejoin: 'round' }))
|
|
9871
9902
|
: h(primitives.FileTypeIcon, { path: file.name, size: 36 });
|
|
9872
|
-
return h('div', {
|
|
9903
|
+
return h('div', {
|
|
9904
|
+
className: 'dwe-preview-cover', 'data-video': Boolean(videoType) || undefined,
|
|
9905
|
+
'data-poster': Boolean(poster) || undefined,
|
|
9906
|
+
},
|
|
9873
9907
|
poster && h('img', { src: poster, alt: '', draggable: false }),
|
|
9874
|
-
h('div', { className: 'dwe-cover-
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
h('
|
|
9881
|
-
|
|
9908
|
+
h('div', { className: 'dwe-cover-content' },
|
|
9909
|
+
h('div', { className: 'dwe-cover-action' },
|
|
9910
|
+
!videoType && fileIcon,
|
|
9911
|
+
h('button', {
|
|
9912
|
+
type: 'button', className: videoType ? 'dwe-cover-play' : 'dwe-cover-open',
|
|
9913
|
+
title: label, 'aria-label': label, onClick: start,
|
|
9914
|
+
}, videoType ? h('svg', { width: 24, height: 24, viewBox: '0 0 24 24', 'aria-hidden': true },
|
|
9915
|
+
h('path', { d: 'M9 5v14l11-7z', fill: 'currentColor' })) : label)),
|
|
9916
|
+
h('div', { className: 'dwe-cover-details' },
|
|
9917
|
+
h('div', { className: 'dwe-cover-file', title: name }, name),
|
|
9918
|
+
h('div', { className: 'dwe-cover-size' }, formatSize(size ?? resolvedSize)))));
|
|
9882
9919
|
}
|
|
9883
9920
|
|
|
9884
9921
|
function ImagePreview({ url, name, onError }) {
|
|
@@ -9906,6 +9943,27 @@ window.__ModuleLoader__.load({
|
|
|
9906
9943
|
}));
|
|
9907
9944
|
}
|
|
9908
9945
|
|
|
9946
|
+
function TextPreview({ text, eof, file }) {
|
|
9947
|
+
const content = useRef(null);
|
|
9948
|
+
useLayoutEffect(() => {
|
|
9949
|
+
if (!file.line || !text) return;
|
|
9950
|
+
let offset = 0;
|
|
9951
|
+
for (let line = 1; line < file.line; line++) {
|
|
9952
|
+
const end = text.indexOf('\n', offset);
|
|
9953
|
+
if (end < 0) return;
|
|
9954
|
+
offset = end + 1;
|
|
9955
|
+
}
|
|
9956
|
+
if (offset === text.length) return;
|
|
9957
|
+
const range = document.createRange();
|
|
9958
|
+
range.setStart(content.current.firstChild, offset);
|
|
9959
|
+
range.setEnd(content.current.firstChild, offset + 1);
|
|
9960
|
+
const scroll = content.current.parentElement;
|
|
9961
|
+
scroll.scrollTop += range.getBoundingClientRect().top - scroll.getBoundingClientRect().top - 12;
|
|
9962
|
+
}, [file, text]);
|
|
9963
|
+
return h('pre', { ref: content, className: 'dwe-text' },
|
|
9964
|
+
text || '(空文件)', eof ? '' : '\n\n… 仅显示首页,可在原生预览中继续查看');
|
|
9965
|
+
}
|
|
9966
|
+
|
|
9909
9967
|
function Preview({ file, root }) {
|
|
9910
9968
|
const [result, setResult] = useState({ kind: 'loading' });
|
|
9911
9969
|
const [started, setStarted] = useState(false);
|
|
@@ -9974,8 +10032,7 @@ window.__ModuleLoader__.load({
|
|
|
9974
10032
|
onError: () => setResult({ kind: 'unavailable' }),
|
|
9975
10033
|
});
|
|
9976
10034
|
case 'text':
|
|
9977
|
-
return h(
|
|
9978
|
-
result.text || '(空文件)', result.eof ? '' : '\n\n… 仅显示首页,可在原生预览中继续查看');
|
|
10035
|
+
return h(TextPreview, { text: result.text, eof: result.eof, file });
|
|
9979
10036
|
case 'unavailable':
|
|
9980
10037
|
return h(PreviewUnavailable);
|
|
9981
10038
|
default:
|
|
@@ -9987,6 +10044,7 @@ window.__ModuleLoader__.load({
|
|
|
9987
10044
|
|
|
9988
10045
|
|
|
9989
10046
|
|
|
10047
|
+
__exports["formatSize"] = formatSize;
|
|
9990
10048
|
__exports["PreviewBoundary"] = PreviewBoundary;
|
|
9991
10049
|
__exports["Preview"] = Preview;
|
|
9992
10050
|
};
|
|
@@ -10258,6 +10316,7 @@ window.__ModuleLoader__.load({
|
|
|
10258
10316
|
const { renamePath, underPath, windowsPath } = __require("ui/navigation.js");
|
|
10259
10317
|
|
|
10260
10318
|
const key = 'dsh-workspace-explorer.favorites';
|
|
10319
|
+
const dragType = 'application/x-dwe-favorite';
|
|
10261
10320
|
const { createElement: h, useLayoutEffect, useRef, useSyncExternalStore } = react;
|
|
10262
10321
|
const listeners = new Set();
|
|
10263
10322
|
|
|
@@ -10323,8 +10382,33 @@ window.__ModuleLoader__.load({
|
|
|
10323
10382
|
update(unique(folders.map(path => renamePath(path, from, to))));
|
|
10324
10383
|
}
|
|
10325
10384
|
|
|
10385
|
+
function moveFavorite(from, to, after) {
|
|
10386
|
+
if (from === to || !folders.includes(from) || !folders.includes(to)) return;
|
|
10387
|
+
const next = folders.filter(path => path !== from);
|
|
10388
|
+
next.splice(next.indexOf(to) + Number(after), 0, from);
|
|
10389
|
+
update(next);
|
|
10390
|
+
}
|
|
10391
|
+
|
|
10326
10392
|
function Favorites({ anchor, close, folders, navigate, currentPath, removeIcon, workspaceMode, root }) {
|
|
10327
10393
|
const panel = useRef(null);
|
|
10394
|
+
const dragged = useRef(null);
|
|
10395
|
+
const dropRow = useRef(null);
|
|
10396
|
+
|
|
10397
|
+
function clearDropRow() {
|
|
10398
|
+
if (dropRow.current) delete dropRow.current.dataset.dweFavoriteDrop;
|
|
10399
|
+
dropRow.current = null;
|
|
10400
|
+
}
|
|
10401
|
+
|
|
10402
|
+
function resetDrag() {
|
|
10403
|
+
dragged.current = null;
|
|
10404
|
+
clearDropRow();
|
|
10405
|
+
}
|
|
10406
|
+
|
|
10407
|
+
function isAfter(event) {
|
|
10408
|
+
const bounds = event.currentTarget.getBoundingClientRect();
|
|
10409
|
+
return event.clientY >= bounds.top + bounds.height / 2;
|
|
10410
|
+
}
|
|
10411
|
+
|
|
10328
10412
|
useLayoutEffect(() => {
|
|
10329
10413
|
const popup = panel.current;
|
|
10330
10414
|
const button = anchor.current;
|
|
@@ -10365,9 +10449,35 @@ window.__ModuleLoader__.load({
|
|
|
10365
10449
|
return h('section', { ref: panel, className: 'dwe-favorites', 'aria-label': '收藏夹' },
|
|
10366
10450
|
h('header', null, h('strong', null, '收藏夹'),
|
|
10367
10451
|
h('button', { type: 'button', className: 'dwe-clear-favorites', disabled: !folders.length, onClick: clearFavorites }, '清空收藏')),
|
|
10368
|
-
folders.length ? h('ul',
|
|
10452
|
+
folders.length ? h('ul', { onDragLeave: event => {
|
|
10453
|
+
if (!event.currentTarget.contains(event.relatedTarget)) clearDropRow();
|
|
10454
|
+
} }, ...folders.map(path => {
|
|
10369
10455
|
const name = path.length === 3 ? path.slice(0, 2) : path.split('\\').at(-1);
|
|
10370
|
-
return h('li', {
|
|
10456
|
+
return h('li', {
|
|
10457
|
+
key: path, draggable: true,
|
|
10458
|
+
onDragStart: event => {
|
|
10459
|
+
dragged.current = path;
|
|
10460
|
+
event.dataTransfer.effectAllowed = 'move';
|
|
10461
|
+
event.dataTransfer.setData(dragType, path);
|
|
10462
|
+
},
|
|
10463
|
+
onDragOver: event => {
|
|
10464
|
+
if (!dragged.current || !event.dataTransfer.types.includes(dragType)) return;
|
|
10465
|
+
if (dropRow.current !== event.currentTarget) clearDropRow();
|
|
10466
|
+
if (dragged.current === path) return;
|
|
10467
|
+
event.preventDefault();
|
|
10468
|
+
event.dataTransfer.dropEffect = 'move';
|
|
10469
|
+
dropRow.current = event.currentTarget;
|
|
10470
|
+
dropRow.current.dataset.dweFavoriteDrop = isAfter(event) ? 'after' : 'before';
|
|
10471
|
+
},
|
|
10472
|
+
onDrop: event => {
|
|
10473
|
+
const from = dragged.current;
|
|
10474
|
+
if (!from || event.dataTransfer.getData(dragType) !== from) return;
|
|
10475
|
+
event.preventDefault();
|
|
10476
|
+
resetDrag();
|
|
10477
|
+
moveFavorite(from, path, isAfter(event));
|
|
10478
|
+
},
|
|
10479
|
+
onDragEnd: resetDrag,
|
|
10480
|
+
},
|
|
10371
10481
|
h('button', {
|
|
10372
10482
|
type: 'button', className: 'dwe-favorite-location', title: path, onClick: () => navigate(path),
|
|
10373
10483
|
disabled: workspaceMode && !underPath(path, root),
|
|
@@ -10393,7 +10503,7 @@ window.__ModuleLoader__.load({
|
|
|
10393
10503
|
"use strict";
|
|
10394
10504
|
const __exports = exports;
|
|
10395
10505
|
const { joinPath, move, parentOf, renamePath, resourceAddress, underPath, visit, windowsPath } = __require("ui/navigation.js");
|
|
10396
|
-
const { Preview, PreviewBoundary } = __require("ui/preview.js");
|
|
10506
|
+
const { formatSize, Preview, PreviewBoundary } = __require("ui/preview.js");
|
|
10397
10507
|
const { disks } = __require("ui/workspace.js");
|
|
10398
10508
|
const { useComposerDrop } = __require("ui/composer-drop.js");
|
|
10399
10509
|
const { Thumbnail } = __require("ui/thumbnail.js");
|
|
@@ -10474,14 +10584,6 @@ window.__ModuleLoader__.load({
|
|
|
10474
10584
|
return { names, anchor: name };
|
|
10475
10585
|
}
|
|
10476
10586
|
|
|
10477
|
-
function formatSize(bytes) {
|
|
10478
|
-
if (typeof bytes !== 'number') return '—';
|
|
10479
|
-
if (bytes < 1024) return bytes + ' B';
|
|
10480
|
-
if (bytes < 1024 ** 2) return (bytes / 1024).toFixed(1) + ' KB';
|
|
10481
|
-
if (bytes < 1024 ** 3) return (bytes / 1024 ** 2).toFixed(1) + ' MB';
|
|
10482
|
-
return (bytes / 1024 ** 3).toFixed(1) + ' GB';
|
|
10483
|
-
}
|
|
10484
|
-
|
|
10485
10587
|
function entryType(entry) {
|
|
10486
10588
|
if (entry.type === 'drive') return '磁盘';
|
|
10487
10589
|
if (entry.type === 'directory') return '文件夹';
|
|
@@ -10569,21 +10671,19 @@ window.__ModuleLoader__.load({
|
|
|
10569
10671
|
}
|
|
10570
10672
|
|
|
10571
10673
|
function Explorer({
|
|
10572
|
-
cwd, sessionId, initialPath, bindFileActions, getTarget, onPath, openLocal, openFolderTab,
|
|
10674
|
+
cwd, sessionId, initialPath, bindFileActions, getTarget, onPath, openLocal, openDocument, openFolderTab,
|
|
10573
10675
|
refreshDirectories, updateRenamedPath,
|
|
10574
10676
|
}) {
|
|
10575
10677
|
const workspace = windowsPath(cwd);
|
|
10576
10678
|
const [localHistory, setLocalHistory] = useState(() => firstPath(initialPath ?? workspace));
|
|
10577
10679
|
const [scoped, setScoped] = useState(null);
|
|
10578
10680
|
const workspaceMode = scoped !== null;
|
|
10579
|
-
const root =
|
|
10580
|
-
const history =
|
|
10681
|
+
const root = scoped?.root ?? null;
|
|
10682
|
+
const history = scoped?.history ?? localHistory;
|
|
10581
10683
|
const changeHistory = useCallback(change => {
|
|
10582
|
-
if (workspaceMode) setScoped(current => current && {
|
|
10583
|
-
root, history: change(current.root === root ? current.history : firstPath(root)),
|
|
10584
|
-
});
|
|
10684
|
+
if (workspaceMode) setScoped(current => current && { ...current, history: change(current.history) });
|
|
10585
10685
|
else setLocalHistory(change);
|
|
10586
|
-
}, [workspaceMode
|
|
10686
|
+
}, [workspaceMode]);
|
|
10587
10687
|
const [revision, setRevision] = useState(0);
|
|
10588
10688
|
const [listing, setListing] = useState(null);
|
|
10589
10689
|
const [selection, setSelection] = useState(emptySelection);
|
|
@@ -10638,7 +10738,7 @@ window.__ModuleLoader__.load({
|
|
|
10638
10738
|
if (!current) return current;
|
|
10639
10739
|
const absolute = renamePath(current.absolute, from, to);
|
|
10640
10740
|
return absolute === current.absolute ? current
|
|
10641
|
-
: { absolute, name: absolute.split('\\').at(-1), address: resourceAddress(absolute) };
|
|
10741
|
+
: { ...current, absolute, name: absolute.split('\\').at(-1), address: resourceAddress(absolute) };
|
|
10642
10742
|
});
|
|
10643
10743
|
}, []);
|
|
10644
10744
|
const closeMenu = useCallback((restore = false) => {
|
|
@@ -10689,12 +10789,6 @@ window.__ModuleLoader__.load({
|
|
|
10689
10789
|
setScoped(workspaceMode ? null : { root: workspace, history: firstPath(workspace) });
|
|
10690
10790
|
}
|
|
10691
10791
|
|
|
10692
|
-
useLayoutEffect(() => {
|
|
10693
|
-
if (!workspaceMode || !root || root === scoped.root) return;
|
|
10694
|
-
resetNavigation();
|
|
10695
|
-
setScoped({ root, history: firstPath(root) });
|
|
10696
|
-
}, [workspaceMode, root, scoped?.root, resetNavigation]);
|
|
10697
|
-
|
|
10698
10792
|
useLayoutEffect(() => { onPath(path); }, [onPath, path]);
|
|
10699
10793
|
|
|
10700
10794
|
useEffect(() => {
|
|
@@ -10725,7 +10819,6 @@ window.__ModuleLoader__.load({
|
|
|
10725
10819
|
}, [root, term, revision, sessionId]);
|
|
10726
10820
|
|
|
10727
10821
|
useEffect(() => () => pathRequest.current?.abort(), []);
|
|
10728
|
-
useLayoutEffect(() => bindFileActions({ refresh, rename }), [bindFileActions, refresh, rename]);
|
|
10729
10822
|
useLayoutEffect(() => { scroll.current.scrollTop = 0; }, [path, term]);
|
|
10730
10823
|
useLayoutEffect(() => {
|
|
10731
10824
|
const result = resultToReveal.current;
|
|
@@ -10750,20 +10843,33 @@ window.__ModuleLoader__.load({
|
|
|
10750
10843
|
changeHistory(value => move(value, delta));
|
|
10751
10844
|
}
|
|
10752
10845
|
|
|
10753
|
-
|
|
10754
|
-
|
|
10755
|
-
|
|
10846
|
+
const openPreview = useCallback((absolute, name, size, line) => {
|
|
10847
|
+
const targetLine = Number.isSafeInteger(line) && line > 0 ? line : undefined;
|
|
10848
|
+
setPreview(current => current?.absolute === absolute && !targetLine ? current : {
|
|
10849
|
+
name, absolute, size, line: targetLine, address: resourceAddress(absolute),
|
|
10756
10850
|
});
|
|
10757
|
-
}
|
|
10851
|
+
}, []);
|
|
10758
10852
|
|
|
10759
|
-
|
|
10760
|
-
|
|
10761
|
-
|
|
10762
|
-
|
|
10763
|
-
|
|
10764
|
-
|
|
10765
|
-
|
|
10853
|
+
const revealFile = useCallback((absolute, name, size, line) => {
|
|
10854
|
+
const folder = parentOf(absolute);
|
|
10855
|
+
if (workspaceMode && !underPath(folder, root)) {
|
|
10856
|
+
resetNavigation();
|
|
10857
|
+
setScoped(null);
|
|
10858
|
+
setLocalHistory(history => visit(history, folder));
|
|
10859
|
+
} else {
|
|
10860
|
+
navigate(folder);
|
|
10861
|
+
if (folder === path && ready && !ready.entries.some(entry => entry.name === name)) refresh();
|
|
10862
|
+
}
|
|
10863
|
+
resultToReveal.current = { folder, name };
|
|
10864
|
+
setSelection(singleSelection(name));
|
|
10865
|
+
openPreview(absolute, name, size, line);
|
|
10766
10866
|
setPreviewOpen(true);
|
|
10867
|
+
}, [workspaceMode, root, path, ready, resetNavigation, navigate, refresh, openPreview]);
|
|
10868
|
+
|
|
10869
|
+
useLayoutEffect(() => bindFileActions({ refresh, rename, reveal: revealFile }), [bindFileActions, refresh, rename, revealFile]);
|
|
10870
|
+
|
|
10871
|
+
function openSearchHit(match) {
|
|
10872
|
+
if (underPath(match.path, root)) revealFile(match.path, match.name);
|
|
10767
10873
|
}
|
|
10768
10874
|
|
|
10769
10875
|
function select(event, entry) {
|
|
@@ -10772,7 +10878,7 @@ window.__ModuleLoader__.load({
|
|
|
10772
10878
|
setSelection(current => selectNames(current, entry.name, entries, shiftKey, ctrlKey));
|
|
10773
10879
|
setActionError('');
|
|
10774
10880
|
setActionNotice('');
|
|
10775
|
-
if (!shiftKey && !ctrlKey && entry.type === 'file') openPreview(joinPath(path, entry.name), entry.name);
|
|
10881
|
+
if (!shiftKey && !ctrlKey && entry.type === 'file') openPreview(joinPath(path, entry.name), entry.name, entry.size);
|
|
10776
10882
|
}
|
|
10777
10883
|
|
|
10778
10884
|
async function openWithWindows(path) {
|
|
@@ -10905,7 +11011,7 @@ window.__ModuleLoader__.load({
|
|
|
10905
11011
|
if (target.type === 'directory') navigate(windowsPath(target.path));
|
|
10906
11012
|
else {
|
|
10907
11013
|
closeEditor();
|
|
10908
|
-
openPreview(target.path, target.name);
|
|
11014
|
+
openPreview(target.path, target.name, target.size);
|
|
10909
11015
|
}
|
|
10910
11016
|
scroll.current.focus();
|
|
10911
11017
|
} catch (error) {
|
|
@@ -10923,7 +11029,9 @@ window.__ModuleLoader__.load({
|
|
|
10923
11029
|
if (!target || !shownPreview) return;
|
|
10924
11030
|
try {
|
|
10925
11031
|
if (root) await disks.locate(shownPreview.absolute, undefined, root);
|
|
10926
|
-
if (getTarget() === target)
|
|
11032
|
+
if (getTarget() === target) openDocument(resourceAddress(shownPreview.absolute, target.sessionId), {
|
|
11033
|
+
paneId: target.paneId, params: shownPreview.line ? { line: shownPreview.line } : undefined,
|
|
11034
|
+
});
|
|
10927
11035
|
} catch (error) {
|
|
10928
11036
|
setActionError(error?.message || String(error));
|
|
10929
11037
|
}
|
|
@@ -10980,6 +11088,7 @@ window.__ModuleLoader__.load({
|
|
|
10980
11088
|
tool('home', workspaceMode ? '返回工作区根目录' : '返回此电脑', () => navigate(root), path === root),
|
|
10981
11089
|
tool('workspace', '跳转至工作区', () => navigate(workspace), !workspace || path === workspace || workspaceMode)),
|
|
10982
11090
|
h('div', { className: 'dwe-nav' },
|
|
11091
|
+
h('span', { className: 'dwe-v-divider', 'aria-hidden': true }),
|
|
10983
11092
|
tool('copy', '复制', () => runAction('copy', selectedEntries), !modifySelected),
|
|
10984
11093
|
tool('cut', '剪切', () => runAction('cut', selectedEntries), !modifySelected),
|
|
10985
11094
|
tool('paste', '粘贴', () => runAction('paste'), !path || working),
|
|
@@ -11045,14 +11154,15 @@ window.__ModuleLoader__.load({
|
|
|
11045
11154
|
!grid && h('span', { className: 'dwe-meta' }, entryType(entry)),
|
|
11046
11155
|
!grid && h('span', { className: 'dwe-meta' }, formatSize(entry.size))))))),
|
|
11047
11156
|
h('footer', { className: 'dwe-status-bar' },
|
|
11157
|
+
h('div', { className: 'dwe-nav', role: 'group', 'aria-label': '文件视图' },
|
|
11158
|
+
tool('list', '列表', () => changeView('list'), false, !grid),
|
|
11159
|
+
tool('grid', '大图标', () => changeView('grid'), false, grid)),
|
|
11160
|
+
h('span', { className: 'dwe-v-divider', 'aria-hidden': true }),
|
|
11048
11161
|
h('span', { className: 'dwe-status', role: 'status', title: actionNotice },
|
|
11049
11162
|
term ? found?.matches ? found.matches.length + ' 个结果' + (found.truncated ? ' · 请缩小关键词' : '') : ' '
|
|
11050
11163
|
: actionNotice || (selectedEntries.length > 1 ? '已选择 ' + selectedEntries.length + ' 项'
|
|
11051
11164
|
: ready?.entries ? ready.entries.length + ' 个项目' + (ready.truncated ? ' · 列表已截断' : '') : ' ')),
|
|
11052
|
-
|
|
11053
|
-
tool('list', '列表', () => changeView('list'), false, !grid),
|
|
11054
|
-
tool('grid', '大图标', () => changeView('grid'), false, grid),
|
|
11055
|
-
tool('preview', '预览窗格', () => setPreviewOpen(open => !open), false, previewOpen)))),
|
|
11165
|
+
tool('preview', '预览窗格', () => setPreviewOpen(open => !open), false, previewOpen))),
|
|
11056
11166
|
previewOpen && h(PreviewDivider, { root: layout, ratio: splitRatio, setRatio: setSplitRatio }),
|
|
11057
11167
|
previewOpen && h('section', { className: 'dwe-preview', 'aria-label': '文件预览' },
|
|
11058
11168
|
h('header', { className: 'dwe-preview-bar' },
|
|
@@ -11102,6 +11212,38 @@ window.__ModuleLoader__.load({
|
|
|
11102
11212
|
"use strict";
|
|
11103
11213
|
const __exports = exports;
|
|
11104
11214
|
const explorerCss = `
|
|
11215
|
+
[data-dwe-sidebar-handoff] {
|
|
11216
|
+
grid-template-columns: var(--dwe-handoff-columns) !important;
|
|
11217
|
+
transition: none !important;
|
|
11218
|
+
}
|
|
11219
|
+
[data-dwe-sidebar-track-instant],
|
|
11220
|
+
[data-sidebar-right-panel] :is([data-dockkit-host='dock'], [data-dockkit-empty], [data-dockkit-divider]):not([data-dwe-sidebar-toggle-motion]),
|
|
11221
|
+
[data-dwe-sidebar-handoff] [data-sidebar-right-panel] :is([data-dockkit-host='dock'], [data-dockkit-empty], [data-dockkit-divider]) {
|
|
11222
|
+
transition: none !important;
|
|
11223
|
+
}
|
|
11224
|
+
.dwe-conversation-drop {
|
|
11225
|
+
position: fixed; inset: 0; z-index: 2147483647;
|
|
11226
|
+
display: grid; box-sizing: border-box; padding: 0 6px 6px 3px;
|
|
11227
|
+
--dwe-drop-blue: #4176E6;
|
|
11228
|
+
pointer-events: none; user-select: none;
|
|
11229
|
+
}
|
|
11230
|
+
.dwe-conversation-drop-area {
|
|
11231
|
+
position: relative; display: grid; place-items: center; padding: 24px;
|
|
11232
|
+
border-radius: 10px; overflow: hidden;
|
|
11233
|
+
background: color-mix(in srgb, var(--dsw-alias-bg-base, Canvas) 38%, transparent);
|
|
11234
|
+
backdrop-filter: blur(7.5px); color: var(--dwe-drop-blue);
|
|
11235
|
+
text-align: center; font-size: 24px;
|
|
11236
|
+
}
|
|
11237
|
+
.dwe-conversation-drop-area::before {
|
|
11238
|
+
content: ''; position: absolute; inset: 0; border: 4px solid var(--dwe-drop-blue); border-radius: inherit;
|
|
11239
|
+
}
|
|
11240
|
+
.dwe-conversation-drop[data-dwe-drop-active] .dwe-conversation-drop-area::before { border-width: 8px; }
|
|
11241
|
+
.dwe-conversation-drag-icon {
|
|
11242
|
+
position: fixed; top: 0; left: 0; width: 40px; height: 40px;
|
|
11243
|
+
display: grid; place-items: center; border-radius: 8px;
|
|
11244
|
+
background: var(--dsw-alias-bg-base, Canvas); box-shadow: 0 2px 8px #0004;
|
|
11245
|
+
transform: translate3d(clamp(0px, var(--dwe-drag-x), 100vw - 44px), clamp(0px, var(--dwe-drag-y), 100vh - 44px), 0);
|
|
11246
|
+
}
|
|
11105
11247
|
.dwe-host { height: 100%; min-height: 0; min-width: 0; }
|
|
11106
11248
|
[data-dockkit-tab]:has([data-dwe-tab-title]) {
|
|
11107
11249
|
flex-shrink: 0; min-inline-size: calc(5em + 59px); max-inline-size: max(170px, calc(5em + 59px));
|
|
@@ -11115,6 +11257,7 @@ window.__ModuleLoader__.load({
|
|
|
11115
11257
|
[data-dwe-tab-title] > span { min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
11116
11258
|
.dwe {
|
|
11117
11259
|
--dwe-line: var(--dsw-alias-border-l3, color-mix(in srgb, currentColor 14%, transparent));
|
|
11260
|
+
--dwe-rule-inset: 8px;
|
|
11118
11261
|
--dwe-tint: color-mix(in srgb, currentColor 5%, transparent);
|
|
11119
11262
|
--dwe-hover: color-mix(in srgb, currentColor 8%, transparent);
|
|
11120
11263
|
--dwe-blue: #458bf5;
|
|
@@ -11151,11 +11294,17 @@ window.__ModuleLoader__.load({
|
|
|
11151
11294
|
.dwe-divider:focus-visible { outline: 2px solid var(--dwe-blue); outline-offset: -2px; }
|
|
11152
11295
|
.dwe-toolbar, .dwe-preview-bar {
|
|
11153
11296
|
display: flex; flex: none; align-items: center; gap: 6px;
|
|
11154
|
-
padding: 3px 8px; min-width: 0; border-bottom: 1px solid
|
|
11297
|
+
padding: 3px 8px; min-width: 0; border-bottom: 1px solid transparent;
|
|
11298
|
+
}
|
|
11299
|
+
.dwe-toolbar, .dwe-head, .dwe-status-bar, .dwe-preview-bar {
|
|
11300
|
+
background-image: linear-gradient(#F5F5F5, #F5F5F5);
|
|
11301
|
+
background-repeat: no-repeat; background-origin: border-box;
|
|
11302
|
+
background-size: calc(100% - 2 * var(--dwe-rule-inset)) 1px; background-position: center bottom;
|
|
11155
11303
|
}
|
|
11156
11304
|
.dwe-address-bar { display: flex; min-width: 0; flex: none; align-items: center; gap: 3px; padding: 5px 8px 0; }
|
|
11157
11305
|
.dwe-toolbar { justify-content: space-between; gap: 4px; overflow-x: auto; scrollbar-width: none; }
|
|
11158
11306
|
.dwe-nav { display: flex; align-items: center; gap: 2px; flex: none; }
|
|
11307
|
+
.dwe-v-divider { flex: 0 0 1px; height: 15px; margin-inline: 3px; background: var(--dwe-line); }
|
|
11159
11308
|
.dwe button {
|
|
11160
11309
|
font: inherit; color: inherit; border: 0; background: transparent; cursor: default;
|
|
11161
11310
|
}
|
|
@@ -11169,8 +11318,9 @@ window.__ModuleLoader__.load({
|
|
|
11169
11318
|
background: color-mix(in srgb, var(--dwe-blue) 20%, transparent);
|
|
11170
11319
|
}
|
|
11171
11320
|
.dwe-tool[data-dwe-tool="star"][aria-pressed="true"] svg { color: #a36a00; fill: #ffca58; }
|
|
11172
|
-
.dwe-tool[data-dwe-tool="
|
|
11173
|
-
|
|
11321
|
+
.dwe-tool:is(.dwe-scope-tool, .dwe-favorites-tool, [data-dwe-tool="preview"], [data-dwe-tool="list"], [data-dwe-tool="grid"]) {
|
|
11322
|
+
&:is([aria-pressed="true"], [aria-expanded="true"]) { background: var(--dwe-hover); }
|
|
11323
|
+
&:hover:not(:disabled) { background: color-mix(in srgb, var(--dwe-blue) 20%, transparent); }
|
|
11174
11324
|
}
|
|
11175
11325
|
.dwe-favorites {
|
|
11176
11326
|
position: absolute; z-index: 4; display: flex; flex-direction: column; box-sizing: border-box;
|
|
@@ -11183,7 +11333,12 @@ window.__ModuleLoader__.load({
|
|
|
11183
11333
|
padding-inline-start: 6px; min-height: 25px;
|
|
11184
11334
|
}
|
|
11185
11335
|
.dwe-favorites ul { list-style: none; margin: 0; padding: 0; overflow: auto; }
|
|
11186
|
-
.dwe-favorites li { display: flex; align-items: center; gap: 4px; }
|
|
11336
|
+
.dwe-favorites li { position: relative; display: flex; align-items: center; gap: 4px; }
|
|
11337
|
+
.dwe-favorites li[data-dwe-favorite-drop]::after {
|
|
11338
|
+
content: ''; position: absolute; inset-inline: 4px; height: 2px; background: var(--dwe-blue); pointer-events: none;
|
|
11339
|
+
}
|
|
11340
|
+
.dwe-favorites li[data-dwe-favorite-drop='before']::after { top: 0; }
|
|
11341
|
+
.dwe-favorites li[data-dwe-favorite-drop='after']::after { bottom: 0; }
|
|
11187
11342
|
.dwe-favorite-location {
|
|
11188
11343
|
display: flex; flex: 1; min-width: 0; align-items: center; gap: 7px; text-align: start;
|
|
11189
11344
|
min-height: 28px; padding: 2px 4px; border-radius: 5px;
|
|
@@ -11235,7 +11390,7 @@ window.__ModuleLoader__.load({
|
|
|
11235
11390
|
.dwe-columns > span { padding-inline: 8px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
11236
11391
|
.dwe-head {
|
|
11237
11392
|
position: sticky; top: 0; z-index: 1; padding: 5px 6px; font-size: 12px;
|
|
11238
|
-
background: var(--dsw-alias-bg-base, Canvas); border-bottom: 1px solid
|
|
11393
|
+
background-color: var(--dsw-alias-bg-base, Canvas); border-bottom: 1px solid transparent;
|
|
11239
11394
|
}
|
|
11240
11395
|
.dwe-head span + span { border-inline-start: 1px solid var(--dwe-line); }
|
|
11241
11396
|
.dwe-columns > span:last-child { text-align: end; }
|
|
@@ -11249,7 +11404,7 @@ window.__ModuleLoader__.load({
|
|
|
11249
11404
|
.dwe-tile .dwe-name { flex-direction: column; gap: 5px; }
|
|
11250
11405
|
.dwe-thumbnail {
|
|
11251
11406
|
position: relative; display: grid; place-items: center; inline-size: 100%; max-inline-size: 110px;
|
|
11252
|
-
block-size: 60px; pointer-events: none;
|
|
11407
|
+
grid-template: minmax(0, 1fr) / minmax(0, 1fr); block-size: 60px; pointer-events: none;
|
|
11253
11408
|
}
|
|
11254
11409
|
.dwe-thumbnail img {
|
|
11255
11410
|
display: block; max-inline-size: 100%; max-block-size: 100%; object-fit: contain; border-radius: 3px;
|
|
@@ -11274,13 +11429,14 @@ window.__ModuleLoader__.load({
|
|
|
11274
11429
|
.dwe-meta, .dwe-status { opacity: .72; font-size: 12px; }
|
|
11275
11430
|
.dwe-status { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
11276
11431
|
.dwe-status-bar {
|
|
11277
|
-
flex: none; display: flex; align-items: center;
|
|
11278
|
-
padding: 2px
|
|
11432
|
+
flex: none; display: flex; align-items: center; gap: 4px;
|
|
11433
|
+
padding: 2px 8px; border-top: 1px solid transparent; background-position: center top;
|
|
11279
11434
|
}
|
|
11435
|
+
.dwe-status-bar > .dwe-tool { margin-inline-start: auto; }
|
|
11280
11436
|
.dwe-preview {
|
|
11281
11437
|
min-height: 0; display: flex; flex-direction: column;
|
|
11282
11438
|
}
|
|
11283
|
-
.dwe-preview-bar { background: var(--dsw-alias-bg-base, Canvas); }
|
|
11439
|
+
.dwe-preview-bar { background-color: var(--dsw-alias-bg-base, Canvas); }
|
|
11284
11440
|
.dwe-preview-label { flex: none; margin-inline: 4px; line-height: 28px; }
|
|
11285
11441
|
.dwe-preview-name {
|
|
11286
11442
|
min-width: 0; height: 28px; box-sizing: border-box; padding-inline: 7px 3px;
|
|
@@ -11291,13 +11447,17 @@ window.__ModuleLoader__.load({
|
|
|
11291
11447
|
.dwe-preview-body { flex: 1 1 auto; min-height: 0; overflow: auto; }
|
|
11292
11448
|
.dwe-preview-cover {
|
|
11293
11449
|
position: relative; display: grid; place-items: center; isolation: isolate;
|
|
11294
|
-
height: 100%; min-height:
|
|
11450
|
+
height: 100%; min-height: 112px; box-sizing: border-box; padding: 6px 12px;
|
|
11451
|
+
overflow: hidden; background: var(--dwe-tint);
|
|
11295
11452
|
}
|
|
11453
|
+
.dwe-preview-cover[data-video] { min-height: 128px; }
|
|
11296
11454
|
.dwe-preview-cover[data-poster] { background: #15171a; }
|
|
11297
11455
|
.dwe-preview-cover > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; z-index: -1; }
|
|
11456
|
+
.dwe-cover-content { display: grid; grid-template-columns: minmax(0, 1fr); justify-items: center; gap: 8px; width: 100%; }
|
|
11298
11457
|
.dwe-cover-action { display: flex; align-items: center; gap: 10px; }
|
|
11299
11458
|
.dwe .dwe-cover-play {
|
|
11300
|
-
display: grid; place-items: center; width: 52px; height: 52px;
|
|
11459
|
+
display: grid; place-items: center; width: 52px; height: 52px; padding: 0; box-sizing: border-box;
|
|
11460
|
+
border-radius: 50%; corner-shape: round;
|
|
11301
11461
|
background: #243953; color: #fff; box-shadow: 0 2px 8px #0003; cursor: pointer;
|
|
11302
11462
|
}
|
|
11303
11463
|
.dwe .dwe-cover-play:hover:not(:disabled) { background: #34577f; }
|
|
@@ -11305,11 +11465,17 @@ window.__ModuleLoader__.load({
|
|
|
11305
11465
|
padding: 7px 12px; border: 1px solid var(--dwe-line); border-radius: 6px;
|
|
11306
11466
|
background: var(--dsw-alias-bg-base, Canvas); white-space: nowrap; cursor: pointer;
|
|
11307
11467
|
}
|
|
11308
|
-
.dwe-cover-
|
|
11309
|
-
|
|
11310
|
-
max-width:
|
|
11468
|
+
.dwe-cover-details {
|
|
11469
|
+
display: grid; grid-template-columns: minmax(0, 1fr); justify-items: center; gap: 3px;
|
|
11470
|
+
max-width: 100%; min-width: 0; box-sizing: border-box; padding: 2px 8px; border-radius: 5px;
|
|
11471
|
+
}
|
|
11472
|
+
.dwe-cover-file { max-width: 100%; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
11473
|
+
.dwe-cover-size { font-size: 12px; opacity: .72; }
|
|
11474
|
+
.dwe-preview-cover[data-video] .dwe-cover-details {
|
|
11475
|
+
position: absolute; inset: 0 0 auto; display: flex; align-items: center; gap: 6px;
|
|
11476
|
+
padding: 8px; border-radius: 0; background: #0009; color: #fff;
|
|
11311
11477
|
}
|
|
11312
|
-
.dwe-preview-cover[data-
|
|
11478
|
+
.dwe-preview-cover[data-video] .dwe-cover-size { flex: none; white-space: nowrap; }
|
|
11313
11479
|
.dwe-pdf-scroll {
|
|
11314
11480
|
height: 100%; overflow: auto; scrollbar-gutter: stable both-edges; background: var(--dwe-tint);
|
|
11315
11481
|
}
|
|
@@ -11377,6 +11543,9 @@ window.__ModuleLoader__.load({
|
|
|
11377
11543
|
.dwe-dialog-actions button[type="submit"] { background: color-mix(in srgb, var(--dwe-blue) 23%, transparent); }
|
|
11378
11544
|
.dwe-dialog-actions button[data-dwe-action="delete"] { background: #ad303d; color: #fff; }
|
|
11379
11545
|
.dwe-dialog-actions button[data-dwe-action="delete"]:hover:not(:disabled) { background: #942634; }
|
|
11546
|
+
@container (max-width: 440px) {
|
|
11547
|
+
.dwe-toolbar .dwe-tool:not(.dwe-scope-tool) { width: 26px; height: 26px; flex-basis: 26px; }
|
|
11548
|
+
}
|
|
11380
11549
|
@container (max-width: 380px) {
|
|
11381
11550
|
.dwe-columns { grid-template-columns: minmax(0, 1fr) 62px; }
|
|
11382
11551
|
.dwe-columns > span:nth-child(2) { display: none; }
|
|
@@ -11411,31 +11580,261 @@ window.__ModuleLoader__.load({
|
|
|
11411
11580
|
__exports["scrollHorizontally"] = scrollHorizontally;
|
|
11412
11581
|
};
|
|
11413
11582
|
|
|
11583
|
+
__modules["client/sidebar-motion.js"] = function (module, exports, require) {
|
|
11584
|
+
"use strict";
|
|
11585
|
+
const __exports = exports;
|
|
11586
|
+
const dockItems = ':is([data-dockkit-host="dock"], [data-dockkit-empty], [data-dockkit-divider])';
|
|
11587
|
+
const toggleButtons = 'button:is([data-sidebar-right-expand], [data-sidebar-right-toggle])';
|
|
11588
|
+
|
|
11589
|
+
function installSidebarMotion(sidebar) {
|
|
11590
|
+
let allowed;
|
|
11591
|
+
let collectFrame;
|
|
11592
|
+
const instantFrames = new Map();
|
|
11593
|
+
|
|
11594
|
+
function stopMotion() {
|
|
11595
|
+
cancelAnimationFrame(collectFrame);
|
|
11596
|
+
if (!allowed) return;
|
|
11597
|
+
for (const item of allowed.items) delete item.dataset.dweSidebarToggleMotion;
|
|
11598
|
+
allowed = undefined;
|
|
11599
|
+
}
|
|
11600
|
+
|
|
11601
|
+
function allowButtonMotion(event) {
|
|
11602
|
+
const button = event.target instanceof Element && event.target.closest(toggleButtons);
|
|
11603
|
+
if (!button || button.disabled) return;
|
|
11604
|
+
const sessionId = sidebar.mounted.getSnapshot();
|
|
11605
|
+
const panel = sessionId && document.querySelector(
|
|
11606
|
+
'[data-sidebar-right-panel][data-sidebar-right-session="' + CSS.escape(sessionId) + '"]',
|
|
11607
|
+
);
|
|
11608
|
+
const frame = panel?.closest('[data-rightbar-col]')?.parentElement;
|
|
11609
|
+
if (!frame) return;
|
|
11610
|
+
|
|
11611
|
+
// A tab selected during the slide must not inherit permission to animate.
|
|
11612
|
+
stopMotion();
|
|
11613
|
+
const items = [...panel.querySelectorAll(dockItems + ':not([hidden])')];
|
|
11614
|
+
const motion = { frame, items };
|
|
11615
|
+
allowed = motion;
|
|
11616
|
+
for (const item of items) item.dataset.dweSidebarToggleMotion = '';
|
|
11617
|
+
collectFrame = requestAnimationFrame(() => {
|
|
11618
|
+
const transitions = [...frame.getAnimations(), ...items.flatMap(item => item.getAnimations())]
|
|
11619
|
+
.filter(animation => ['transform', 'grid-template-columns'].includes(animation.transitionProperty));
|
|
11620
|
+
Promise.allSettled(transitions.map(animation => animation.finished)).then(() => {
|
|
11621
|
+
if (allowed === motion) stopMotion();
|
|
11622
|
+
});
|
|
11623
|
+
});
|
|
11624
|
+
}
|
|
11625
|
+
|
|
11626
|
+
// The shell shares one grid transition between the left and right columns.
|
|
11627
|
+
// Snap right-track changes outside the toggle controls so the left controls still animate.
|
|
11628
|
+
const track = new MutationObserver(records => {
|
|
11629
|
+
for (const frame of new Set(records.map(record => record.target))) {
|
|
11630
|
+
if (allowed?.frame === frame || !frame.querySelector(':scope > [data-rightbar-col]')) continue;
|
|
11631
|
+
cancelAnimationFrame(instantFrames.get(frame));
|
|
11632
|
+
frame.dataset.dweSidebarTrackInstant = '';
|
|
11633
|
+
instantFrames.set(frame, requestAnimationFrame(() => {
|
|
11634
|
+
instantFrames.set(frame, requestAnimationFrame(() => {
|
|
11635
|
+
delete frame.dataset.dweSidebarTrackInstant;
|
|
11636
|
+
instantFrames.delete(frame);
|
|
11637
|
+
}));
|
|
11638
|
+
}));
|
|
11639
|
+
}
|
|
11640
|
+
});
|
|
11641
|
+
track.observe(document.documentElement, { subtree: true, attributeFilter: ['data-rightbar-collapsed'] });
|
|
11642
|
+
document.addEventListener('click', allowButtonMotion, true);
|
|
11643
|
+
const disposeSwitch = sidebar.mounted.subscribe(stopMotion);
|
|
11644
|
+
return () => {
|
|
11645
|
+
stopMotion();
|
|
11646
|
+
disposeSwitch();
|
|
11647
|
+
track.disconnect();
|
|
11648
|
+
for (const [frame, request] of instantFrames) {
|
|
11649
|
+
cancelAnimationFrame(request);
|
|
11650
|
+
delete frame.dataset.dweSidebarTrackInstant;
|
|
11651
|
+
}
|
|
11652
|
+
document.removeEventListener('click', allowButtonMotion, true);
|
|
11653
|
+
};
|
|
11654
|
+
}
|
|
11655
|
+
|
|
11656
|
+
__exports["installSidebarMotion"] = installSidebarMotion;
|
|
11657
|
+
};
|
|
11658
|
+
|
|
11659
|
+
__modules["client/conversation-drop.js"] = function (module, exports, require) {
|
|
11660
|
+
"use strict";
|
|
11661
|
+
const __exports = exports;
|
|
11662
|
+
const changedFileButton = '[data-changed-files] button[aria-describedby]';
|
|
11663
|
+
const sourceSelector = '[data-presented-file] > button[title], [data-chat-flow] button[class*="_fileMention_"], ' + changedFileButton;
|
|
11664
|
+
const dragThreshold = 5;
|
|
11665
|
+
|
|
11666
|
+
function installConversationDrop(mounted, readFile, openPreview) {
|
|
11667
|
+
const lifetime = new AbortController();
|
|
11668
|
+
const overlay = document.createElement('div');
|
|
11669
|
+
overlay.className = 'dwe-conversation-drop';
|
|
11670
|
+
overlay.setAttribute('aria-hidden', 'true');
|
|
11671
|
+
const area = document.createElement('div');
|
|
11672
|
+
area.className = 'dwe-conversation-drop-area';
|
|
11673
|
+
const icon = document.createElement('div');
|
|
11674
|
+
icon.className = 'dwe-conversation-drag-icon';
|
|
11675
|
+
const iconRoot = reactDomClient.createRoot(icon);
|
|
11676
|
+
overlay.append(area, icon);
|
|
11677
|
+
let gesture;
|
|
11678
|
+
let suppressClick = false;
|
|
11679
|
+
|
|
11680
|
+
function reset() {
|
|
11681
|
+
const previous = gesture;
|
|
11682
|
+
gesture = undefined;
|
|
11683
|
+
if (previous && document.body.hasPointerCapture(previous.pointerId)) {
|
|
11684
|
+
document.body.releasePointerCapture(previous.pointerId);
|
|
11685
|
+
}
|
|
11686
|
+
overlay.remove();
|
|
11687
|
+
overlay.removeAttribute('data-dwe-drop-active');
|
|
11688
|
+
if (previous?.file) iconRoot.render(null);
|
|
11689
|
+
}
|
|
11690
|
+
|
|
11691
|
+
function arm(event) {
|
|
11692
|
+
if (event.button !== 0 || event.pointerType !== 'mouse' || !(event.target instanceof Element)) return;
|
|
11693
|
+
suppressClick = false;
|
|
11694
|
+
reset();
|
|
11695
|
+
const button = event.target.closest(sourceSelector);
|
|
11696
|
+
const conversation = button?.closest('[data-conversation-content]');
|
|
11697
|
+
const sessionId = conversation?.dataset.conversationSession;
|
|
11698
|
+
if (!sessionId || sessionId !== mounted.getSnapshot()) return;
|
|
11699
|
+
gesture = { button, conversation, pointerId: event.pointerId, x: event.clientX, y: event.clientY };
|
|
11700
|
+
}
|
|
11701
|
+
|
|
11702
|
+
function layout() {
|
|
11703
|
+
if (!gesture?.file) return;
|
|
11704
|
+
const rightbar = document.querySelector('[data-rightbar-col]');
|
|
11705
|
+
const center = rightbar?.previousElementSibling ?? gesture.conversation;
|
|
11706
|
+
const centerBounds = center.getBoundingClientRect();
|
|
11707
|
+
const left = Math.max(0, centerBounds.left);
|
|
11708
|
+
const right = rightbar?.getBoundingClientRect();
|
|
11709
|
+
overlay.style.top = Math.max(0, centerBounds.top) + 'px';
|
|
11710
|
+
overlay.style.left = (right?.width && right.left > left ? right.left : (left + innerWidth) / 2) + 'px';
|
|
11711
|
+
}
|
|
11712
|
+
|
|
11713
|
+
function inDropArea(x, y) {
|
|
11714
|
+
const { left, right, top, bottom } = overlay.getBoundingClientRect();
|
|
11715
|
+
return x >= left && x < right && y >= top && y < bottom;
|
|
11716
|
+
}
|
|
11717
|
+
|
|
11718
|
+
function move(event) {
|
|
11719
|
+
if (gesture?.pointerId !== event.pointerId) return;
|
|
11720
|
+
if (!(event.buttons & 1)) {
|
|
11721
|
+
reset();
|
|
11722
|
+
return;
|
|
11723
|
+
}
|
|
11724
|
+
if (!gesture.file) {
|
|
11725
|
+
if (Math.hypot(event.clientX - gesture.x, event.clientY - gesture.y) < dragThreshold) return;
|
|
11726
|
+
const current = gesture;
|
|
11727
|
+
const file = readFile(current.button);
|
|
11728
|
+
if (gesture !== current || !file) {
|
|
11729
|
+
reset();
|
|
11730
|
+
return;
|
|
11731
|
+
}
|
|
11732
|
+
gesture.file = file;
|
|
11733
|
+
suppressClick = true;
|
|
11734
|
+
// Keep tracking even when the chat rerenders the file card during the drag.
|
|
11735
|
+
document.body.setPointerCapture(event.pointerId);
|
|
11736
|
+
layout();
|
|
11737
|
+
area.textContent = file.review ? '拖到此处在DSH差异审阅中打开' : '拖到此处在DSH文档预览中打开';
|
|
11738
|
+
document.body.append(overlay);
|
|
11739
|
+
iconRoot.render(react.createElement(primitives.FileTypeIcon, { path: file.file, size: 28 }));
|
|
11740
|
+
}
|
|
11741
|
+
event.preventDefault();
|
|
11742
|
+
icon.style.setProperty('--dwe-drag-x', event.clientX + 12 + 'px');
|
|
11743
|
+
icon.style.setProperty('--dwe-drag-y', event.clientY + 12 + 'px');
|
|
11744
|
+
overlay.toggleAttribute('data-dwe-drop-active', inDropArea(event.clientX, event.clientY));
|
|
11745
|
+
}
|
|
11746
|
+
|
|
11747
|
+
function release(event) {
|
|
11748
|
+
if (gesture?.pointerId !== event.pointerId || event.button !== 0) return;
|
|
11749
|
+
const { file } = gesture;
|
|
11750
|
+
const shouldOpen = file && inDropArea(event.clientX, event.clientY);
|
|
11751
|
+
reset();
|
|
11752
|
+
if (!shouldOpen) return;
|
|
11753
|
+
const hit = document.elementFromPoint(event.clientX, event.clientY);
|
|
11754
|
+
const paneId = hit?.closest('[data-sidebar-right-panel] [data-dockkit-pane]')?.dataset.dockkitPane;
|
|
11755
|
+
openPreview(file, paneId);
|
|
11756
|
+
}
|
|
11757
|
+
|
|
11758
|
+
function cancelPointer(event) {
|
|
11759
|
+
if (gesture?.pointerId === event.pointerId) reset();
|
|
11760
|
+
}
|
|
11761
|
+
|
|
11762
|
+
function cancelEscape(event) {
|
|
11763
|
+
if (event.key !== 'Escape' || !gesture?.file) return;
|
|
11764
|
+
event.preventDefault();
|
|
11765
|
+
event.stopPropagation();
|
|
11766
|
+
reset();
|
|
11767
|
+
}
|
|
11768
|
+
|
|
11769
|
+
function preventDragClick(event) {
|
|
11770
|
+
if (!suppressClick || !event.isTrusted || event.detail === 0) return;
|
|
11771
|
+
suppressClick = false;
|
|
11772
|
+
event.preventDefault();
|
|
11773
|
+
event.stopImmediatePropagation();
|
|
11774
|
+
}
|
|
11775
|
+
|
|
11776
|
+
const options = { capture: true, signal: lifetime.signal };
|
|
11777
|
+
const stop = mounted.subscribe(reset);
|
|
11778
|
+
document.addEventListener('pointerdown', arm, options);
|
|
11779
|
+
document.addEventListener('pointermove', move, options);
|
|
11780
|
+
document.addEventListener('pointerup', release, options);
|
|
11781
|
+
document.addEventListener('pointercancel', cancelPointer, options);
|
|
11782
|
+
document.addEventListener('lostpointercapture', cancelPointer, options);
|
|
11783
|
+
document.addEventListener('dragstart', event => {
|
|
11784
|
+
if (gesture?.button.contains(event.target)) event.preventDefault();
|
|
11785
|
+
}, options);
|
|
11786
|
+
document.addEventListener('keydown', cancelEscape, options);
|
|
11787
|
+
document.addEventListener('click', preventDragClick, options);
|
|
11788
|
+
window.addEventListener('resize', layout, options);
|
|
11789
|
+
window.addEventListener('blur', reset, { signal: lifetime.signal });
|
|
11790
|
+
return () => {
|
|
11791
|
+
reset();
|
|
11792
|
+
iconRoot.unmount();
|
|
11793
|
+
stop();
|
|
11794
|
+
lifetime.abort();
|
|
11795
|
+
};
|
|
11796
|
+
}
|
|
11797
|
+
|
|
11798
|
+
__exports["changedFileButton"] = changedFileButton;
|
|
11799
|
+
__exports["installConversationDrop"] = installConversationDrop;
|
|
11800
|
+
};
|
|
11801
|
+
|
|
11414
11802
|
__modules["client/main.js"] = function (module, exports, require) {
|
|
11415
11803
|
"use strict";
|
|
11416
11804
|
const __exports = exports;
|
|
11417
11805
|
const { Explorer, ExplorerTitle } = __require("ui/explorer.js");
|
|
11418
11806
|
const { explorerCss } = __require("ui/styles.js");
|
|
11419
11807
|
const { scrollHorizontally } = __require("ui/horizontal-wheel.js");
|
|
11420
|
-
const { underPath } = __require("ui/navigation.js");
|
|
11808
|
+
const { parentOf, resourceAddress, sessionFilePath, underPath, windowsPath } = __require("ui/navigation.js");
|
|
11421
11809
|
const { renameFavorites } = __require("ui/favorites.js");
|
|
11810
|
+
const { installSidebarMotion } = __require("client/sidebar-motion.js");
|
|
11811
|
+
const { changedFileButton, installConversationDrop } = __require("client/conversation-drop.js");
|
|
11422
11812
|
|
|
11423
11813
|
const id = 'dsh-workspace-explorer';
|
|
11424
11814
|
const inject = ['slots'];
|
|
11425
11815
|
const h = react.createElement;
|
|
11426
11816
|
const instancePrefix = 'dsh-resource://workspace-explorer/tab/';
|
|
11427
|
-
const
|
|
11817
|
+
const changesReviewPrefix = 'dsh-resource://changes-review/session/';
|
|
11428
11818
|
|
|
11429
11819
|
function apply(ctx) {
|
|
11430
11820
|
if (location.protocol !== 'dsh-app:' || location.hostname !== 'app') return;
|
|
11431
11821
|
if (!navigator.platform.startsWith('Win')) return;
|
|
11432
|
-
ctx.inject(['sidebarRightTabs', 'sidebarRight', 'remote', 'remote.session'], (scope) => {
|
|
11822
|
+
ctx.inject(['sidebarRightTabs', 'sidebarRight', 'remote', 'remote.session', 'remote.workspaceFiles'], (scope) => {
|
|
11433
11823
|
const sidebar = scope.sidebarRight;
|
|
11434
|
-
const
|
|
11824
|
+
const bindings = new Map();
|
|
11435
11825
|
const folderTabs = new Map();
|
|
11436
11826
|
const listeners = new Set();
|
|
11437
11827
|
let viewList = [];
|
|
11438
11828
|
let activeView;
|
|
11829
|
+
let presentedSession;
|
|
11830
|
+
let workspace;
|
|
11831
|
+
let expanded;
|
|
11832
|
+
let fileFocused = false;
|
|
11833
|
+
let projecting = false;
|
|
11834
|
+
let detached = false;
|
|
11835
|
+
let captureQueued = false;
|
|
11836
|
+
let handoffFrame;
|
|
11837
|
+
let handoffAnimation;
|
|
11439
11838
|
|
|
11440
11839
|
function subscribe(listener) {
|
|
11441
11840
|
listeners.add(listener);
|
|
@@ -11446,9 +11845,171 @@ window.__ModuleLoader__.load({
|
|
|
11446
11845
|
for (const listener of listeners) listener();
|
|
11447
11846
|
}
|
|
11448
11847
|
|
|
11449
|
-
function
|
|
11450
|
-
|
|
11451
|
-
|
|
11848
|
+
function filesIn(sessionId) {
|
|
11849
|
+
return sidebar.openTabs.getSnapshot().filter(tab => tab.sessionId === sessionId && tab.kind === 'files');
|
|
11850
|
+
}
|
|
11851
|
+
|
|
11852
|
+
function createView(initialPath) {
|
|
11853
|
+
const host = document.createElement('div');
|
|
11854
|
+
host.className = 'dwe-host';
|
|
11855
|
+
const view = { host, address: instancePrefix + crypto.randomUUID(), path: initialPath ?? null, initialPath };
|
|
11856
|
+
view.getTarget = () => view.target;
|
|
11857
|
+
view.onPath = path => {
|
|
11858
|
+
if (view.path === path) return;
|
|
11859
|
+
view.path = path;
|
|
11860
|
+
notify();
|
|
11861
|
+
};
|
|
11862
|
+
view.bindFileActions = actions => {
|
|
11863
|
+
view.fileActions = actions;
|
|
11864
|
+
if (view.pendingPreview) {
|
|
11865
|
+
const preview = view.pendingPreview;
|
|
11866
|
+
view.pendingPreview = undefined;
|
|
11867
|
+
actions.reveal(...preview);
|
|
11868
|
+
}
|
|
11869
|
+
return () => { view.fileActions = undefined; };
|
|
11870
|
+
};
|
|
11871
|
+
return view;
|
|
11872
|
+
}
|
|
11873
|
+
|
|
11874
|
+
function boundView(sessionId, tabId) {
|
|
11875
|
+
return sessionId === presentedSession ? bindings.get(tabId) : undefined;
|
|
11876
|
+
}
|
|
11877
|
+
|
|
11878
|
+
function rememberPresentation() {
|
|
11879
|
+
if (projecting || detached || !presentedSession || sidebar.mounted.getSnapshot() !== presentedSession) return;
|
|
11880
|
+
const tab = sidebar.active();
|
|
11881
|
+
const view = tab && bindings.get(tab.id);
|
|
11882
|
+
if (view) activeView = view;
|
|
11883
|
+
fileFocused = Boolean(view);
|
|
11884
|
+
expanded = sidebar.isExpanded();
|
|
11885
|
+
}
|
|
11886
|
+
|
|
11887
|
+
function rememberOrder(sessionId) {
|
|
11888
|
+
const selector = '[data-sidebar-right-session="' + CSS.escape(sessionId) + '"] [data-dockkit-tab]';
|
|
11889
|
+
const ordered = new Set();
|
|
11890
|
+
for (const chip of document.querySelectorAll(selector)) {
|
|
11891
|
+
const view = bindings.get(chip.dataset.dockkitTab);
|
|
11892
|
+
if (view) ordered.add(view);
|
|
11893
|
+
}
|
|
11894
|
+
const next = [...ordered, ...viewList.filter(view => !ordered.has(view))];
|
|
11895
|
+
if (next.some((view, index) => view !== viewList[index])) {
|
|
11896
|
+
viewList = next;
|
|
11897
|
+
notify();
|
|
11898
|
+
}
|
|
11899
|
+
}
|
|
11900
|
+
|
|
11901
|
+
function capturePresentation(sessionId) {
|
|
11902
|
+
if (captureQueued) return;
|
|
11903
|
+
captureQueued = true;
|
|
11904
|
+
queueMicrotask(() => {
|
|
11905
|
+
captureQueued = false;
|
|
11906
|
+
if (presentedSession !== sessionId || sidebar.mounted.getSnapshot() !== sessionId) return;
|
|
11907
|
+
rememberPresentation();
|
|
11908
|
+
rememberOrder(sessionId);
|
|
11909
|
+
});
|
|
11910
|
+
}
|
|
11911
|
+
|
|
11912
|
+
function syncTabs() {
|
|
11913
|
+
if (projecting || detached || !presentedSession || sidebar.mounted.getSnapshot() !== presentedSession) return;
|
|
11914
|
+
const tabs = filesIn(presentedSession);
|
|
11915
|
+
const live = new Set(tabs.map(tab => tab.tabId));
|
|
11916
|
+
const added = [];
|
|
11917
|
+
const removed = new Set();
|
|
11918
|
+
for (const [tabId, view] of bindings) {
|
|
11919
|
+
if (live.has(tabId)) continue;
|
|
11920
|
+
bindings.delete(tabId);
|
|
11921
|
+
removed.add(view);
|
|
11922
|
+
}
|
|
11923
|
+
for (const tab of tabs) {
|
|
11924
|
+
if (bindings.has(tab.tabId)) continue;
|
|
11925
|
+
const view = createView(folderTabs.get(tab.contentId) ?? workspace);
|
|
11926
|
+
bindings.set(tab.tabId, view);
|
|
11927
|
+
added.push(view);
|
|
11928
|
+
}
|
|
11929
|
+
if (removed.size || added.length) {
|
|
11930
|
+
viewList = [...viewList.filter(view => !removed.has(view)), ...added];
|
|
11931
|
+
if (removed.has(activeView)) activeView = undefined;
|
|
11932
|
+
notify();
|
|
11933
|
+
}
|
|
11934
|
+
rememberPresentation();
|
|
11935
|
+
}
|
|
11936
|
+
|
|
11937
|
+
function closeDepartingTabs() {
|
|
11938
|
+
const arriving = sidebar.mounted.getSnapshot();
|
|
11939
|
+
if (!arriving || !presentedSession) return;
|
|
11940
|
+
if (arriving === presentedSession) {
|
|
11941
|
+
if (detached) queueMicrotask(() => {
|
|
11942
|
+
if (detached && sidebar.mounted.getSnapshot() === arriving) present(arriving, workspace);
|
|
11943
|
+
});
|
|
11944
|
+
return;
|
|
11945
|
+
}
|
|
11946
|
+
if (detached) return;
|
|
11947
|
+
rememberOrder(presentedSession);
|
|
11948
|
+
detached = true;
|
|
11949
|
+
if (expanded && viewList.length) beginHandoff();
|
|
11950
|
+
// The old native store is still adopted before React commits the session change.
|
|
11951
|
+
for (const tab of filesIn(presentedSession)) sidebar.closeIn?.(presentedSession, tab.tabId);
|
|
11952
|
+
}
|
|
11953
|
+
|
|
11954
|
+
function beginHandoff() {
|
|
11955
|
+
const frame = document.querySelector('[data-rightbar-col]')?.parentElement;
|
|
11956
|
+
if (!frame) return;
|
|
11957
|
+
if (handoffFrame !== frame) {
|
|
11958
|
+
finishHandoff();
|
|
11959
|
+
handoffFrame = frame;
|
|
11960
|
+
frame.style.setProperty('--dwe-handoff-columns', getComputedStyle(frame).gridTemplateColumns);
|
|
11961
|
+
frame.dataset.dweSidebarHandoff = '';
|
|
11962
|
+
}
|
|
11963
|
+
cancelAnimationFrame(handoffAnimation);
|
|
11964
|
+
handoffAnimation = requestAnimationFrame(() => {
|
|
11965
|
+
handoffAnimation = requestAnimationFrame(finishHandoff);
|
|
11966
|
+
});
|
|
11967
|
+
}
|
|
11968
|
+
|
|
11969
|
+
function finishHandoff() {
|
|
11970
|
+
cancelAnimationFrame(handoffAnimation);
|
|
11971
|
+
if (!handoffFrame) return;
|
|
11972
|
+
delete handoffFrame.dataset.dweSidebarHandoff;
|
|
11973
|
+
handoffFrame.style.removeProperty('--dwe-handoff-columns');
|
|
11974
|
+
handoffFrame = undefined;
|
|
11975
|
+
}
|
|
11976
|
+
|
|
11977
|
+
function placeView(view, options) {
|
|
11978
|
+
sidebar.openResource(view.address, { kind: 'files', ...options });
|
|
11979
|
+
const tab = sidebar.active();
|
|
11980
|
+
if (tab?.kind === 'files' && tab.contentId === view.address) bindings.set(tab.id, view);
|
|
11981
|
+
}
|
|
11982
|
+
|
|
11983
|
+
function present(sessionId, cwd) {
|
|
11984
|
+
workspace = windowsPath(cwd);
|
|
11985
|
+
if (sessionId !== presentedSession || detached) {
|
|
11986
|
+
const first = !presentedSession;
|
|
11987
|
+
presentedSession = sessionId;
|
|
11988
|
+
detached = false;
|
|
11989
|
+
bindings.clear();
|
|
11990
|
+
if (first) {
|
|
11991
|
+
syncTabs();
|
|
11992
|
+
} else {
|
|
11993
|
+
const nativeActive = sidebar.active();
|
|
11994
|
+
const preserveExpansion = expanded;
|
|
11995
|
+
projecting = true;
|
|
11996
|
+
try {
|
|
11997
|
+
for (const tab of filesIn(sessionId)) sidebar.close(tab.tabId);
|
|
11998
|
+
const replaceTab = fileFocused && sidebar.active()?.kind === 'guide' ? sidebar.active()?.id : undefined;
|
|
11999
|
+
viewList.forEach((view, index) => placeView(view, index === 0 ? { replaceTab } : undefined));
|
|
12000
|
+
if (viewList.length) {
|
|
12001
|
+
const restoreTab = !fileFocused && nativeActive?.kind !== 'files' ? nativeActive?.id
|
|
12002
|
+
: [...bindings].find(([, view]) => view === activeView)?.[0];
|
|
12003
|
+
if (restoreTab) sidebar.focus(restoreTab);
|
|
12004
|
+
if (preserveExpansion !== undefined && sidebar.isExpanded() !== preserveExpansion) sidebar.toggleExpanded();
|
|
12005
|
+
}
|
|
12006
|
+
} finally {
|
|
12007
|
+
projecting = false;
|
|
12008
|
+
}
|
|
12009
|
+
notify();
|
|
12010
|
+
rememberPresentation();
|
|
12011
|
+
}
|
|
12012
|
+
}
|
|
11452
12013
|
}
|
|
11453
12014
|
|
|
11454
12015
|
const openLocal = async (path, action) => {
|
|
@@ -11456,17 +12017,143 @@ window.__ModuleLoader__.load({
|
|
|
11456
12017
|
if (!result.ok) throw result.error;
|
|
11457
12018
|
};
|
|
11458
12019
|
|
|
12020
|
+
function openDocument(address, options) {
|
|
12021
|
+
sidebar.openResource(address, { ...options, kind: 'text' });
|
|
12022
|
+
}
|
|
12023
|
+
|
|
11459
12024
|
function openFolderTab(path, paneId) {
|
|
11460
12025
|
const address = instancePrefix + crypto.randomUUID();
|
|
11461
12026
|
folderTabs.set(address, path);
|
|
11462
12027
|
try {
|
|
11463
12028
|
sidebar.openResource(address, { kind: 'files', paneId });
|
|
11464
|
-
|
|
12029
|
+
syncTabs();
|
|
12030
|
+
const tab = sidebar.active();
|
|
12031
|
+
return tab?.contentId === address ? bindings.get(tab.id) : undefined;
|
|
12032
|
+
} finally {
|
|
11465
12033
|
folderTabs.delete(address);
|
|
11466
|
-
throw error;
|
|
11467
12034
|
}
|
|
11468
12035
|
}
|
|
11469
12036
|
|
|
12037
|
+
async function statFile(sessionId, file, signal) {
|
|
12038
|
+
try {
|
|
12039
|
+
const result = await scope.remote.workspaceFiles.stat(sessionId, file, signal);
|
|
12040
|
+
const absolute = windowsPath(result.ok && result.value.absolutePath);
|
|
12041
|
+
if (absolute) return { absolute, name: absolute.split('\\').at(-1), size: result.value.bytes };
|
|
12042
|
+
} catch { /* The built-in preview can report a failed session file lookup. */ }
|
|
12043
|
+
}
|
|
12044
|
+
|
|
12045
|
+
function revealFile(view, file, line) {
|
|
12046
|
+
const preview = [file.absolute, file.name, file.size, line];
|
|
12047
|
+
if (view.fileActions) view.fileActions.reveal(...preview);
|
|
12048
|
+
else view.pendingPreview = preview;
|
|
12049
|
+
}
|
|
12050
|
+
|
|
12051
|
+
function fileRequest(address, options) {
|
|
12052
|
+
const sessionId = sidebar.mounted.getSnapshot();
|
|
12053
|
+
const file = workspace && sessionId === presentedSession && sessionFilePath(address, sessionId);
|
|
12054
|
+
if (file) return { address, sessionId, file, params: options?.params };
|
|
12055
|
+
}
|
|
12056
|
+
|
|
12057
|
+
function currentExplorer() {
|
|
12058
|
+
return bindings.get(sidebar.active()?.id) ?? activeView ?? viewList[0];
|
|
12059
|
+
}
|
|
12060
|
+
|
|
12061
|
+
async function openConversationFile(request, signal, { view, onUnavailable }) {
|
|
12062
|
+
const { address, sessionId, file, params } = request;
|
|
12063
|
+
const metadata = await statFile(sessionId, file, signal);
|
|
12064
|
+
if (signal.aborted || sidebar.mounted.getSnapshot() !== sessionId) return;
|
|
12065
|
+
if (!metadata) {
|
|
12066
|
+
if (onUnavailable) onUnavailable();
|
|
12067
|
+
else openDocument(address, { params });
|
|
12068
|
+
return;
|
|
12069
|
+
}
|
|
12070
|
+
const existing = viewList.includes(view) ? view : currentExplorer();
|
|
12071
|
+
const tabId = existing && [...bindings].find(([, item]) => item === existing)?.[0];
|
|
12072
|
+
if (tabId) {
|
|
12073
|
+
sidebar.focus(tabId);
|
|
12074
|
+
if (!sidebar.isExpanded() && !existing.target?.isFloating) sidebar.toggleExpanded();
|
|
12075
|
+
}
|
|
12076
|
+
const target = tabId ? existing : openFolderTab(parentOf(metadata.absolute));
|
|
12077
|
+
if (target) revealFile(target, metadata, params?.line);
|
|
12078
|
+
}
|
|
12079
|
+
|
|
12080
|
+
function installFileOpener() {
|
|
12081
|
+
const openResource = sidebar.openResource;
|
|
12082
|
+
let fileClick;
|
|
12083
|
+
let dragSource;
|
|
12084
|
+
let openingReview = false;
|
|
12085
|
+
function openFile(request, onUnavailable) {
|
|
12086
|
+
fileClick?.abort();
|
|
12087
|
+
fileClick = new AbortController();
|
|
12088
|
+
void openConversationFile(request, fileClick.signal, { view: currentExplorer(), onUnavailable });
|
|
12089
|
+
}
|
|
12090
|
+
function open(address, options) {
|
|
12091
|
+
const changed = dragSource?.changed;
|
|
12092
|
+
if (changed && address.startsWith(changesReviewPrefix)) {
|
|
12093
|
+
if (address.startsWith(changesReviewPrefix + encodeURIComponent(changed.sessionId) + '/')) {
|
|
12094
|
+
dragSource.file = { ...changed, review: { address, params: options?.params } };
|
|
12095
|
+
}
|
|
12096
|
+
return;
|
|
12097
|
+
}
|
|
12098
|
+
const request = (!options || Object.keys(options).every(key => key === 'params')) && fileRequest(address, options);
|
|
12099
|
+
if (!request) return openResource.call(this, address, options);
|
|
12100
|
+
if (dragSource) {
|
|
12101
|
+
dragSource.file = request;
|
|
12102
|
+
return;
|
|
12103
|
+
}
|
|
12104
|
+
openFile(request);
|
|
12105
|
+
}
|
|
12106
|
+
function changedFileRequest(button) {
|
|
12107
|
+
const card = button.closest('[data-changed-files]');
|
|
12108
|
+
const description = document.getElementById(button.getAttribute('aria-describedby'));
|
|
12109
|
+
const sessionId = card?.closest('[data-conversation-content]')?.dataset.conversationSession;
|
|
12110
|
+
if (sessionId !== sidebar.mounted.getSnapshot() || !card?.contains(description)) return;
|
|
12111
|
+
const absolute = windowsPath(description.textContent);
|
|
12112
|
+
if (absolute) return fileRequest(resourceAddress(absolute, sessionId));
|
|
12113
|
+
}
|
|
12114
|
+
function clickChangedFile(event) {
|
|
12115
|
+
// Shift-click retains the native change review for an individual file.
|
|
12116
|
+
if (openingReview || dragSource?.changed || event.shiftKey || !(event.target instanceof Element)) return;
|
|
12117
|
+
const button = event.target.closest(changedFileButton);
|
|
12118
|
+
const request = button && changedFileRequest(button);
|
|
12119
|
+
if (!request) return;
|
|
12120
|
+
event.preventDefault();
|
|
12121
|
+
event.stopPropagation();
|
|
12122
|
+
openFile(request, () => {
|
|
12123
|
+
if (!button.isConnected) return;
|
|
12124
|
+
openingReview = true;
|
|
12125
|
+
try { button.click(); }
|
|
12126
|
+
finally { openingReview = false; }
|
|
12127
|
+
});
|
|
12128
|
+
}
|
|
12129
|
+
function readDragSource(button) {
|
|
12130
|
+
const isChanged = button.matches(changedFileButton);
|
|
12131
|
+
const changed = isChanged ? changedFileRequest(button) : undefined;
|
|
12132
|
+
if (isChanged && !changed) return;
|
|
12133
|
+
// Capture Harness's file or review address without opening the tab until drop.
|
|
12134
|
+
const capture = { changed };
|
|
12135
|
+
dragSource = capture;
|
|
12136
|
+
try { button.click(); }
|
|
12137
|
+
finally { dragSource = undefined; }
|
|
12138
|
+
return capture.file ?? changed;
|
|
12139
|
+
}
|
|
12140
|
+
sidebar.openResource = open;
|
|
12141
|
+
const stopDrop = installConversationDrop(sidebar.mounted, readDragSource, ({ address, sessionId, params, review }, paneId) => {
|
|
12142
|
+
if (sidebar.mounted.getSnapshot() !== sessionId) return;
|
|
12143
|
+
if (review) sidebar.openResource(review.address, { paneId, params: review.params });
|
|
12144
|
+
else openDocument(address, { paneId, params });
|
|
12145
|
+
});
|
|
12146
|
+
document.addEventListener('click', clickChangedFile, true);
|
|
12147
|
+
const stop = sidebar.mounted.subscribe(() => fileClick?.abort());
|
|
12148
|
+
return () => {
|
|
12149
|
+
document.removeEventListener('click', clickChangedFile, true);
|
|
12150
|
+
stopDrop();
|
|
12151
|
+
stop();
|
|
12152
|
+
fileClick?.abort();
|
|
12153
|
+
if (sidebar.openResource === open) sidebar.openResource = openResource;
|
|
12154
|
+
};
|
|
12155
|
+
}
|
|
12156
|
+
|
|
11470
12157
|
function refreshDirectories(paths, moved = []) {
|
|
11471
12158
|
const changed = new Set(paths.map(path => path.toLowerCase()));
|
|
11472
12159
|
for (const view of viewList) {
|
|
@@ -11483,101 +12170,81 @@ window.__ModuleLoader__.load({
|
|
|
11483
12170
|
}
|
|
11484
12171
|
|
|
11485
12172
|
function Anchor({ sessionId, useTabInfo }) {
|
|
11486
|
-
const { tab } = useTabInfo();
|
|
12173
|
+
const { tab, panel } = useTabInfo();
|
|
11487
12174
|
const anchor = react.useRef(null);
|
|
11488
|
-
const
|
|
12175
|
+
const view = react.useSyncExternalStore(subscribe, () => boundView(sessionId, tab.id));
|
|
12176
|
+
const navigation = tab.navigation;
|
|
11489
12177
|
react.useLayoutEffect(() => {
|
|
11490
|
-
if (!tab.visible) return;
|
|
12178
|
+
if (!tab.visible || !view) return;
|
|
11491
12179
|
const element = anchor.current;
|
|
11492
|
-
|
|
11493
|
-
if (!view) {
|
|
11494
|
-
const host = document.createElement('div');
|
|
11495
|
-
host.className = 'dwe-host';
|
|
11496
|
-
const initialPath = folderTabs.get(tab.contentId);
|
|
11497
|
-
folderTabs.delete(tab.contentId);
|
|
11498
|
-
view = { host, address: initialPath ? tab.contentId : instancePrefix + crypto.randomUUID(), path: null, initialPath };
|
|
11499
|
-
view.getTarget = () => view.target;
|
|
11500
|
-
view.onPath = path => {
|
|
11501
|
-
if (view.path === path) return;
|
|
11502
|
-
view.path = path;
|
|
11503
|
-
notify();
|
|
11504
|
-
};
|
|
11505
|
-
view.bindFileActions = actions => {
|
|
11506
|
-
view.fileActions = actions;
|
|
11507
|
-
return () => { view.fileActions = undefined; };
|
|
11508
|
-
};
|
|
11509
|
-
views.set(key, view);
|
|
11510
|
-
updateViews();
|
|
11511
|
-
}
|
|
11512
|
-
const target = { sessionId, actions: tab.actions };
|
|
12180
|
+
const target = { sessionId, paneId: panel.id, isFloating: panel.isFloating };
|
|
11513
12181
|
view.target = target;
|
|
11514
12182
|
element.appendChild(view.host);
|
|
11515
12183
|
notify();
|
|
12184
|
+
capturePresentation(sessionId);
|
|
11516
12185
|
const dispose = tab.actions.bindCommands?.({ refresh: () => view.fileActions?.refresh() });
|
|
11517
12186
|
return () => {
|
|
11518
12187
|
dispose?.();
|
|
11519
12188
|
if (view.target !== target) return;
|
|
11520
12189
|
view.target = undefined;
|
|
11521
12190
|
if (view.host.parentNode === element) view.host.remove();
|
|
11522
|
-
if (activeView === view && sidebar.mounted.getSnapshot() === sessionId
|
|
11523
|
-
&& (!sidebar.isExpanded() || sidebar.active()?.kind !== 'files')) {
|
|
11524
|
-
activeView = undefined;
|
|
11525
|
-
}
|
|
11526
12191
|
notify();
|
|
12192
|
+
capturePresentation(sessionId);
|
|
11527
12193
|
};
|
|
11528
|
-
}, [
|
|
11529
|
-
react.
|
|
11530
|
-
if (tab.visible
|
|
11531
|
-
|
|
12194
|
+
}, [view, sessionId, tab.visible, tab.actions, panel.id, panel.isFloating]);
|
|
12195
|
+
react.useEffect(() => {
|
|
12196
|
+
if (!tab.visible || !view || view.handledNavigation === navigation) return;
|
|
12197
|
+
const { address, params } = navigation;
|
|
12198
|
+
const file = sessionFilePath(address, sessionId);
|
|
12199
|
+
if (!file) return;
|
|
12200
|
+
|
|
12201
|
+
const controller = new AbortController();
|
|
12202
|
+
const { signal } = controller;
|
|
12203
|
+
const isCurrent = () => !signal.aborted && !tab.signal?.aborted
|
|
12204
|
+
&& sidebar.mounted.getSnapshot() === sessionId && boundView(sessionId, tab.id) === view;
|
|
12205
|
+
async function reveal() {
|
|
12206
|
+
const metadata = await statFile(sessionId, file, signal);
|
|
12207
|
+
if (!isCurrent()) return;
|
|
12208
|
+
view.handledNavigation = navigation;
|
|
12209
|
+
if (metadata) {
|
|
12210
|
+
view.revealedFile = true;
|
|
12211
|
+
revealFile(view, metadata, params?.line);
|
|
12212
|
+
} else {
|
|
12213
|
+
openDocument(address, { paneId: panel.id, params, replaceTab: view.revealedFile ? undefined : tab.id });
|
|
12214
|
+
}
|
|
12215
|
+
}
|
|
12216
|
+
void reveal();
|
|
12217
|
+
return () => controller.abort();
|
|
12218
|
+
}, [view, sessionId, tab.id, tab.visible, navigation, panel.id]);
|
|
11532
12219
|
return h('div', { className: 'dwe-host', ref: anchor });
|
|
11533
12220
|
}
|
|
11534
12221
|
|
|
11535
12222
|
function Title({ sessionId, useTabInfo }) {
|
|
11536
12223
|
const { tab } = useTabInfo();
|
|
11537
|
-
const path = react.useSyncExternalStore(subscribe, () =>
|
|
12224
|
+
const path = react.useSyncExternalStore(subscribe, () => boundView(sessionId, tab.id)?.path ?? null);
|
|
12225
|
+
react.useLayoutEffect(() => {
|
|
12226
|
+
if (tab.visible) capturePresentation(sessionId);
|
|
12227
|
+
});
|
|
11538
12228
|
return h(ExplorerTitle, { path });
|
|
11539
12229
|
}
|
|
11540
12230
|
|
|
11541
|
-
function View({ view,
|
|
12231
|
+
function View({ view, cwd }) {
|
|
11542
12232
|
const target = react.useSyncExternalStore(subscribe, view.getTarget);
|
|
11543
12233
|
const sessionId = target?.sessionId;
|
|
11544
|
-
const cwd = useSessions(state => sessionId ? state.byId[sessionId]?.cwd : undefined);
|
|
11545
12234
|
return reactDom.createPortal(h(Explorer, {
|
|
11546
12235
|
sessionId, cwd, initialPath: view.initialPath, getTarget: view.getTarget, bindFileActions: view.bindFileActions,
|
|
11547
|
-
onPath: view.onPath, openLocal, openFolderTab, refreshDirectories, updateRenamedPath,
|
|
12236
|
+
onPath: view.onPath, openLocal, openDocument, openFolderTab, refreshDirectories, updateRenamedPath,
|
|
11548
12237
|
}), view.host);
|
|
11549
12238
|
}
|
|
11550
12239
|
|
|
11551
12240
|
function Keeper({ useSessions }) {
|
|
11552
12241
|
const sessionId = react.useSyncExternalStore(sidebar.mounted.subscribe, sidebar.mounted.getSnapshot);
|
|
11553
12242
|
const currentViews = react.useSyncExternalStore(subscribe, () => viewList);
|
|
11554
|
-
const
|
|
12243
|
+
const cwd = useSessions(state => sessionId ? state.byId[sessionId]?.cwd : undefined);
|
|
11555
12244
|
react.useLayoutEffect(() => {
|
|
11556
|
-
if (
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
if (!view) return;
|
|
11560
|
-
// Wait for the new conversation's dock store to be adopted.
|
|
11561
|
-
queueMicrotask(() => {
|
|
11562
|
-
if (sidebar.mounted.getSnapshot() !== sessionId) return;
|
|
11563
|
-
const existing = sidebar.openTabs.getSnapshot().find(tab =>
|
|
11564
|
-
tab.sessionId === sessionId && views.get(occurrence(sessionId, tab.tabId)) === view);
|
|
11565
|
-
if (existing) {
|
|
11566
|
-
if (!sidebar.isExpanded()) sidebar.toggleExpanded();
|
|
11567
|
-
sidebar.focus(existing.tabId);
|
|
11568
|
-
return;
|
|
11569
|
-
}
|
|
11570
|
-
sidebar.openResource(view.address, { kind: 'files' });
|
|
11571
|
-
const tab = sidebar.active();
|
|
11572
|
-
if (tab?.kind !== 'files') return;
|
|
11573
|
-
const key = occurrence(sessionId, tab.id);
|
|
11574
|
-
if (!views.has(key)) {
|
|
11575
|
-
views.set(key, view);
|
|
11576
|
-
updateViews();
|
|
11577
|
-
}
|
|
11578
|
-
});
|
|
11579
|
-
}, [sessionId]);
|
|
11580
|
-
return currentViews.map(view => h(View, { key: view.address, view, useSessions }));
|
|
12245
|
+
if (sessionId && sidebar.mounted.getSnapshot() === sessionId) present(sessionId, cwd);
|
|
12246
|
+
}, [sessionId, cwd]);
|
|
12247
|
+
return currentViews.map(view => h(View, { key: view.address, view, cwd }));
|
|
11581
12248
|
}
|
|
11582
12249
|
|
|
11583
12250
|
scope.effect(() => {
|
|
@@ -11595,26 +12262,17 @@ window.__ModuleLoader__.load({
|
|
|
11595
12262
|
}
|
|
11596
12263
|
}
|
|
11597
12264
|
document.addEventListener('wheel', wheel, { passive: false, capture: true });
|
|
11598
|
-
const
|
|
11599
|
-
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
for (const address of folderTabs.keys()) {
|
|
11603
|
-
if (!addresses.has(address)) folderTabs.delete(address);
|
|
11604
|
-
}
|
|
11605
|
-
let removed = false;
|
|
11606
|
-
for (const key of views.keys()) {
|
|
11607
|
-
if (live.has(key)) continue;
|
|
11608
|
-
views.delete(key);
|
|
11609
|
-
removed = true;
|
|
11610
|
-
}
|
|
11611
|
-
if (removed) {
|
|
11612
|
-
updateViews();
|
|
11613
|
-
if (!viewList.includes(activeView)) activeView = undefined;
|
|
11614
|
-
}
|
|
11615
|
-
});
|
|
12265
|
+
const disposeFileOpener = installFileOpener();
|
|
12266
|
+
const disposeMotion = installSidebarMotion(sidebar);
|
|
12267
|
+
const disposeTabs = sidebar.openTabs.subscribe(syncTabs);
|
|
12268
|
+
const disposeSwitch = sidebar.mounted.subscribe(closeDepartingTabs);
|
|
11616
12269
|
return () => {
|
|
11617
|
-
|
|
12270
|
+
disposeFileOpener();
|
|
12271
|
+
disposeMotion();
|
|
12272
|
+
disposeTabs();
|
|
12273
|
+
disposeSwitch();
|
|
12274
|
+
detached = false;
|
|
12275
|
+
finishHandoff();
|
|
11618
12276
|
document.removeEventListener('wheel', wheel, true);
|
|
11619
12277
|
style.remove();
|
|
11620
12278
|
};
|
|
@@ -11628,7 +12286,7 @@ window.__ModuleLoader__.load({
|
|
|
11628
12286
|
scope.slots.register(key ? { name, key } : { name, id }, component)));
|
|
11629
12287
|
}
|
|
11630
12288
|
scope.effect(() => scope.sidebarRightTabs.register({
|
|
11631
|
-
id, kind: 'files', priority: 'extension', title: () => '文件浏览',
|
|
12289
|
+
id, kind: 'files', priority: 'extension', title: () => '文件浏览', multiple: true,
|
|
11632
12290
|
guide: [{
|
|
11633
12291
|
id: 'workspace', commandId: 'workspace.files', order: 10, title: () => '文件浏览',
|
|
11634
12292
|
description: () => '浏览本机磁盘,在下方独立预览文件',
|