dsh-workspace-explorer 1.1.0 → 1.1.1
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 +8 -0
- package/README.md +7 -5
- package/lib/client.js +783 -162
- package/package.json +53 -52
- package/scripts/build-client.mjs +1 -0
- package/src/client/conversation-drop.js +142 -0
- package/src/client/main.js +318 -91
- package/src/client/sidebar-motion.js +69 -0
- package/src/host/files.js +1 -1
- package/src/ui/explorer.js +42 -42
- package/src/ui/favorites.js +54 -2
- package/src/ui/navigation.js +9 -0
- package/src/ui/preview.js +58 -12
- package/src/ui/styles.js +74 -15
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;
|
|
11311
11471
|
}
|
|
11312
|
-
.dwe-
|
|
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;
|
|
11477
|
+
}
|
|
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,266 @@ 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 sourceSelector = '[data-presented-file] > button[title], [data-chat-flow] button[class*="_fileMention_"]';
|
|
11663
|
+
const dragThreshold = 5;
|
|
11664
|
+
|
|
11665
|
+
function installConversationDrop(mounted, readFile, openTab) {
|
|
11666
|
+
const lifetime = new AbortController();
|
|
11667
|
+
const overlay = document.createElement('div');
|
|
11668
|
+
overlay.className = 'dwe-conversation-drop';
|
|
11669
|
+
overlay.setAttribute('aria-hidden', 'true');
|
|
11670
|
+
const area = document.createElement('div');
|
|
11671
|
+
area.className = 'dwe-conversation-drop-area';
|
|
11672
|
+
area.textContent = '拖动到这里以新标签页打开';
|
|
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 session = new AbortController();
|
|
11678
|
+
let gesture;
|
|
11679
|
+
let suppressClick = false;
|
|
11680
|
+
|
|
11681
|
+
function reset() {
|
|
11682
|
+
const previous = gesture;
|
|
11683
|
+
gesture = undefined;
|
|
11684
|
+
if (previous && document.body.hasPointerCapture(previous.pointerId)) {
|
|
11685
|
+
document.body.releasePointerCapture(previous.pointerId);
|
|
11686
|
+
}
|
|
11687
|
+
overlay.remove();
|
|
11688
|
+
overlay.removeAttribute('data-dwe-drop-active');
|
|
11689
|
+
if (previous?.file) iconRoot.render(null);
|
|
11690
|
+
}
|
|
11691
|
+
|
|
11692
|
+
function arm(event) {
|
|
11693
|
+
if (event.button !== 0 || event.pointerType !== 'mouse' || !(event.target instanceof Element)) return;
|
|
11694
|
+
suppressClick = false;
|
|
11695
|
+
reset();
|
|
11696
|
+
const button = event.target.closest(sourceSelector);
|
|
11697
|
+
const conversation = button?.closest('[data-conversation-content]');
|
|
11698
|
+
const sessionId = conversation?.dataset.conversationSession;
|
|
11699
|
+
if (!sessionId || sessionId !== mounted.getSnapshot()) return;
|
|
11700
|
+
gesture = { button, conversation, pointerId: event.pointerId, x: event.clientX, y: event.clientY };
|
|
11701
|
+
}
|
|
11702
|
+
|
|
11703
|
+
function layout() {
|
|
11704
|
+
if (!gesture?.file) return;
|
|
11705
|
+
const rightbar = document.querySelector('[data-rightbar-col]');
|
|
11706
|
+
const center = rightbar?.previousElementSibling ?? gesture.conversation;
|
|
11707
|
+
const centerBounds = center.getBoundingClientRect();
|
|
11708
|
+
const left = Math.max(0, centerBounds.left);
|
|
11709
|
+
const right = rightbar?.getBoundingClientRect();
|
|
11710
|
+
overlay.style.top = Math.max(0, centerBounds.top) + 'px';
|
|
11711
|
+
overlay.style.left = (right?.width && right.left > left ? right.left : (left + innerWidth) / 2) + 'px';
|
|
11712
|
+
}
|
|
11713
|
+
|
|
11714
|
+
function inDropArea(x, y) {
|
|
11715
|
+
const { left, right, top, bottom } = overlay.getBoundingClientRect();
|
|
11716
|
+
return x >= left && x < right && y >= top && y < bottom;
|
|
11717
|
+
}
|
|
11718
|
+
|
|
11719
|
+
function move(event) {
|
|
11720
|
+
if (gesture?.pointerId !== event.pointerId) return;
|
|
11721
|
+
if (!(event.buttons & 1)) {
|
|
11722
|
+
reset();
|
|
11723
|
+
return;
|
|
11724
|
+
}
|
|
11725
|
+
if (!gesture.file) {
|
|
11726
|
+
if (Math.hypot(event.clientX - gesture.x, event.clientY - gesture.y) < dragThreshold) return;
|
|
11727
|
+
const current = gesture;
|
|
11728
|
+
const file = readFile(current.button);
|
|
11729
|
+
if (gesture !== current || !file) {
|
|
11730
|
+
reset();
|
|
11731
|
+
return;
|
|
11732
|
+
}
|
|
11733
|
+
gesture.file = file;
|
|
11734
|
+
suppressClick = true;
|
|
11735
|
+
// Keep tracking even when the chat rerenders the file card during the drag.
|
|
11736
|
+
document.body.setPointerCapture(event.pointerId);
|
|
11737
|
+
layout();
|
|
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
|
+
openTab(file, paneId, session.signal);
|
|
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
|
+
function switchSession() {
|
|
11777
|
+
reset();
|
|
11778
|
+
session.abort();
|
|
11779
|
+
session = new AbortController();
|
|
11780
|
+
}
|
|
11781
|
+
|
|
11782
|
+
const options = { capture: true, signal: lifetime.signal };
|
|
11783
|
+
const stop = mounted.subscribe(switchSession);
|
|
11784
|
+
document.addEventListener('pointerdown', arm, options);
|
|
11785
|
+
document.addEventListener('pointermove', move, options);
|
|
11786
|
+
document.addEventListener('pointerup', release, options);
|
|
11787
|
+
document.addEventListener('pointercancel', cancelPointer, options);
|
|
11788
|
+
document.addEventListener('lostpointercapture', cancelPointer, options);
|
|
11789
|
+
document.addEventListener('dragstart', event => {
|
|
11790
|
+
if (gesture?.button.contains(event.target)) event.preventDefault();
|
|
11791
|
+
}, options);
|
|
11792
|
+
document.addEventListener('keydown', cancelEscape, options);
|
|
11793
|
+
document.addEventListener('click', preventDragClick, options);
|
|
11794
|
+
window.addEventListener('resize', layout, options);
|
|
11795
|
+
window.addEventListener('blur', reset, { signal: lifetime.signal });
|
|
11796
|
+
return () => {
|
|
11797
|
+
reset();
|
|
11798
|
+
iconRoot.unmount();
|
|
11799
|
+
stop();
|
|
11800
|
+
session.abort();
|
|
11801
|
+
lifetime.abort();
|
|
11802
|
+
};
|
|
11803
|
+
}
|
|
11804
|
+
|
|
11805
|
+
__exports["installConversationDrop"] = installConversationDrop;
|
|
11806
|
+
};
|
|
11807
|
+
|
|
11414
11808
|
__modules["client/main.js"] = function (module, exports, require) {
|
|
11415
11809
|
"use strict";
|
|
11416
11810
|
const __exports = exports;
|
|
11417
11811
|
const { Explorer, ExplorerTitle } = __require("ui/explorer.js");
|
|
11418
11812
|
const { explorerCss } = __require("ui/styles.js");
|
|
11419
11813
|
const { scrollHorizontally } = __require("ui/horizontal-wheel.js");
|
|
11420
|
-
const { underPath } = __require("ui/navigation.js");
|
|
11814
|
+
const { parentOf, sessionFilePath, underPath, windowsPath } = __require("ui/navigation.js");
|
|
11421
11815
|
const { renameFavorites } = __require("ui/favorites.js");
|
|
11816
|
+
const { installSidebarMotion } = __require("client/sidebar-motion.js");
|
|
11817
|
+
const { installConversationDrop } = __require("client/conversation-drop.js");
|
|
11422
11818
|
|
|
11423
11819
|
const id = 'dsh-workspace-explorer';
|
|
11424
11820
|
const inject = ['slots'];
|
|
11425
11821
|
const h = react.createElement;
|
|
11426
11822
|
const instancePrefix = 'dsh-resource://workspace-explorer/tab/';
|
|
11427
|
-
const occurrence = (sessionId, tabId) => JSON.stringify([sessionId, tabId]);
|
|
11428
11823
|
|
|
11429
11824
|
function apply(ctx) {
|
|
11430
11825
|
if (location.protocol !== 'dsh-app:' || location.hostname !== 'app') return;
|
|
11431
11826
|
if (!navigator.platform.startsWith('Win')) return;
|
|
11432
|
-
ctx.inject(['sidebarRightTabs', 'sidebarRight', 'remote', 'remote.session'], (scope) => {
|
|
11827
|
+
ctx.inject(['sidebarRightTabs', 'sidebarRight', 'remote', 'remote.session', 'remote.workspaceFiles'], (scope) => {
|
|
11433
11828
|
const sidebar = scope.sidebarRight;
|
|
11434
|
-
const
|
|
11829
|
+
const bindings = new Map();
|
|
11435
11830
|
const folderTabs = new Map();
|
|
11436
11831
|
const listeners = new Set();
|
|
11437
11832
|
let viewList = [];
|
|
11438
11833
|
let activeView;
|
|
11834
|
+
let presentedSession;
|
|
11835
|
+
let workspace;
|
|
11836
|
+
let expanded;
|
|
11837
|
+
let fileFocused = false;
|
|
11838
|
+
let projecting = false;
|
|
11839
|
+
let detached = false;
|
|
11840
|
+
let captureQueued = false;
|
|
11841
|
+
let handoffFrame;
|
|
11842
|
+
let handoffAnimation;
|
|
11439
11843
|
|
|
11440
11844
|
function subscribe(listener) {
|
|
11441
11845
|
listeners.add(listener);
|
|
@@ -11446,9 +11850,171 @@ window.__ModuleLoader__.load({
|
|
|
11446
11850
|
for (const listener of listeners) listener();
|
|
11447
11851
|
}
|
|
11448
11852
|
|
|
11449
|
-
function
|
|
11450
|
-
|
|
11451
|
-
|
|
11853
|
+
function filesIn(sessionId) {
|
|
11854
|
+
return sidebar.openTabs.getSnapshot().filter(tab => tab.sessionId === sessionId && tab.kind === 'files');
|
|
11855
|
+
}
|
|
11856
|
+
|
|
11857
|
+
function createView(initialPath) {
|
|
11858
|
+
const host = document.createElement('div');
|
|
11859
|
+
host.className = 'dwe-host';
|
|
11860
|
+
const view = { host, address: instancePrefix + crypto.randomUUID(), path: initialPath ?? null, initialPath };
|
|
11861
|
+
view.getTarget = () => view.target;
|
|
11862
|
+
view.onPath = path => {
|
|
11863
|
+
if (view.path === path) return;
|
|
11864
|
+
view.path = path;
|
|
11865
|
+
notify();
|
|
11866
|
+
};
|
|
11867
|
+
view.bindFileActions = actions => {
|
|
11868
|
+
view.fileActions = actions;
|
|
11869
|
+
if (view.pendingPreview) {
|
|
11870
|
+
const preview = view.pendingPreview;
|
|
11871
|
+
view.pendingPreview = undefined;
|
|
11872
|
+
actions.reveal(...preview);
|
|
11873
|
+
}
|
|
11874
|
+
return () => { view.fileActions = undefined; };
|
|
11875
|
+
};
|
|
11876
|
+
return view;
|
|
11877
|
+
}
|
|
11878
|
+
|
|
11879
|
+
function boundView(sessionId, tabId) {
|
|
11880
|
+
return sessionId === presentedSession ? bindings.get(tabId) : undefined;
|
|
11881
|
+
}
|
|
11882
|
+
|
|
11883
|
+
function rememberPresentation() {
|
|
11884
|
+
if (projecting || detached || !presentedSession || sidebar.mounted.getSnapshot() !== presentedSession) return;
|
|
11885
|
+
const tab = sidebar.active();
|
|
11886
|
+
const view = tab && bindings.get(tab.id);
|
|
11887
|
+
if (view) activeView = view;
|
|
11888
|
+
fileFocused = Boolean(view);
|
|
11889
|
+
expanded = sidebar.isExpanded();
|
|
11890
|
+
}
|
|
11891
|
+
|
|
11892
|
+
function rememberOrder(sessionId) {
|
|
11893
|
+
const selector = '[data-sidebar-right-session="' + CSS.escape(sessionId) + '"] [data-dockkit-tab]';
|
|
11894
|
+
const ordered = new Set();
|
|
11895
|
+
for (const chip of document.querySelectorAll(selector)) {
|
|
11896
|
+
const view = bindings.get(chip.dataset.dockkitTab);
|
|
11897
|
+
if (view) ordered.add(view);
|
|
11898
|
+
}
|
|
11899
|
+
const next = [...ordered, ...viewList.filter(view => !ordered.has(view))];
|
|
11900
|
+
if (next.some((view, index) => view !== viewList[index])) {
|
|
11901
|
+
viewList = next;
|
|
11902
|
+
notify();
|
|
11903
|
+
}
|
|
11904
|
+
}
|
|
11905
|
+
|
|
11906
|
+
function capturePresentation(sessionId) {
|
|
11907
|
+
if (captureQueued) return;
|
|
11908
|
+
captureQueued = true;
|
|
11909
|
+
queueMicrotask(() => {
|
|
11910
|
+
captureQueued = false;
|
|
11911
|
+
if (presentedSession !== sessionId || sidebar.mounted.getSnapshot() !== sessionId) return;
|
|
11912
|
+
rememberPresentation();
|
|
11913
|
+
rememberOrder(sessionId);
|
|
11914
|
+
});
|
|
11915
|
+
}
|
|
11916
|
+
|
|
11917
|
+
function syncTabs() {
|
|
11918
|
+
if (projecting || detached || !presentedSession || sidebar.mounted.getSnapshot() !== presentedSession) return;
|
|
11919
|
+
const tabs = filesIn(presentedSession);
|
|
11920
|
+
const live = new Set(tabs.map(tab => tab.tabId));
|
|
11921
|
+
const added = [];
|
|
11922
|
+
const removed = new Set();
|
|
11923
|
+
for (const [tabId, view] of bindings) {
|
|
11924
|
+
if (live.has(tabId)) continue;
|
|
11925
|
+
bindings.delete(tabId);
|
|
11926
|
+
removed.add(view);
|
|
11927
|
+
}
|
|
11928
|
+
for (const tab of tabs) {
|
|
11929
|
+
if (bindings.has(tab.tabId)) continue;
|
|
11930
|
+
const view = createView(folderTabs.get(tab.contentId) ?? workspace);
|
|
11931
|
+
bindings.set(tab.tabId, view);
|
|
11932
|
+
added.push(view);
|
|
11933
|
+
}
|
|
11934
|
+
if (removed.size || added.length) {
|
|
11935
|
+
viewList = [...viewList.filter(view => !removed.has(view)), ...added];
|
|
11936
|
+
if (removed.has(activeView)) activeView = undefined;
|
|
11937
|
+
notify();
|
|
11938
|
+
}
|
|
11939
|
+
rememberPresentation();
|
|
11940
|
+
}
|
|
11941
|
+
|
|
11942
|
+
function closeDepartingTabs() {
|
|
11943
|
+
const arriving = sidebar.mounted.getSnapshot();
|
|
11944
|
+
if (!arriving || !presentedSession) return;
|
|
11945
|
+
if (arriving === presentedSession) {
|
|
11946
|
+
if (detached) queueMicrotask(() => {
|
|
11947
|
+
if (detached && sidebar.mounted.getSnapshot() === arriving) present(arriving, workspace);
|
|
11948
|
+
});
|
|
11949
|
+
return;
|
|
11950
|
+
}
|
|
11951
|
+
if (detached) return;
|
|
11952
|
+
rememberOrder(presentedSession);
|
|
11953
|
+
detached = true;
|
|
11954
|
+
if (expanded && viewList.length) beginHandoff();
|
|
11955
|
+
// The old native store is still adopted before React commits the session change.
|
|
11956
|
+
for (const tab of filesIn(presentedSession)) sidebar.closeIn?.(presentedSession, tab.tabId);
|
|
11957
|
+
}
|
|
11958
|
+
|
|
11959
|
+
function beginHandoff() {
|
|
11960
|
+
const frame = document.querySelector('[data-rightbar-col]')?.parentElement;
|
|
11961
|
+
if (!frame) return;
|
|
11962
|
+
if (handoffFrame !== frame) {
|
|
11963
|
+
finishHandoff();
|
|
11964
|
+
handoffFrame = frame;
|
|
11965
|
+
frame.style.setProperty('--dwe-handoff-columns', getComputedStyle(frame).gridTemplateColumns);
|
|
11966
|
+
frame.dataset.dweSidebarHandoff = '';
|
|
11967
|
+
}
|
|
11968
|
+
cancelAnimationFrame(handoffAnimation);
|
|
11969
|
+
handoffAnimation = requestAnimationFrame(() => {
|
|
11970
|
+
handoffAnimation = requestAnimationFrame(finishHandoff);
|
|
11971
|
+
});
|
|
11972
|
+
}
|
|
11973
|
+
|
|
11974
|
+
function finishHandoff() {
|
|
11975
|
+
cancelAnimationFrame(handoffAnimation);
|
|
11976
|
+
if (!handoffFrame) return;
|
|
11977
|
+
delete handoffFrame.dataset.dweSidebarHandoff;
|
|
11978
|
+
handoffFrame.style.removeProperty('--dwe-handoff-columns');
|
|
11979
|
+
handoffFrame = undefined;
|
|
11980
|
+
}
|
|
11981
|
+
|
|
11982
|
+
function placeView(view, options) {
|
|
11983
|
+
sidebar.openResource(view.address, { kind: 'files', ...options });
|
|
11984
|
+
const tab = sidebar.active();
|
|
11985
|
+
if (tab?.kind === 'files' && tab.contentId === view.address) bindings.set(tab.id, view);
|
|
11986
|
+
}
|
|
11987
|
+
|
|
11988
|
+
function present(sessionId, cwd) {
|
|
11989
|
+
workspace = windowsPath(cwd);
|
|
11990
|
+
if (sessionId !== presentedSession || detached) {
|
|
11991
|
+
const first = !presentedSession;
|
|
11992
|
+
presentedSession = sessionId;
|
|
11993
|
+
detached = false;
|
|
11994
|
+
bindings.clear();
|
|
11995
|
+
if (first) {
|
|
11996
|
+
syncTabs();
|
|
11997
|
+
} else {
|
|
11998
|
+
const nativeActive = sidebar.active();
|
|
11999
|
+
const preserveExpansion = expanded;
|
|
12000
|
+
projecting = true;
|
|
12001
|
+
try {
|
|
12002
|
+
for (const tab of filesIn(sessionId)) sidebar.close(tab.tabId);
|
|
12003
|
+
const replaceTab = fileFocused && sidebar.active()?.kind === 'guide' ? sidebar.active()?.id : undefined;
|
|
12004
|
+
viewList.forEach((view, index) => placeView(view, index === 0 ? { replaceTab } : undefined));
|
|
12005
|
+
if (viewList.length) {
|
|
12006
|
+
const restoreTab = !fileFocused && nativeActive?.kind !== 'files' ? nativeActive?.id
|
|
12007
|
+
: [...bindings].find(([, view]) => view === activeView)?.[0];
|
|
12008
|
+
if (restoreTab) sidebar.focus(restoreTab);
|
|
12009
|
+
if (preserveExpansion !== undefined && sidebar.isExpanded() !== preserveExpansion) sidebar.toggleExpanded();
|
|
12010
|
+
}
|
|
12011
|
+
} finally {
|
|
12012
|
+
projecting = false;
|
|
12013
|
+
}
|
|
12014
|
+
notify();
|
|
12015
|
+
rememberPresentation();
|
|
12016
|
+
}
|
|
12017
|
+
}
|
|
11452
12018
|
}
|
|
11453
12019
|
|
|
11454
12020
|
const openLocal = async (path, action) => {
|
|
@@ -11456,17 +12022,101 @@ window.__ModuleLoader__.load({
|
|
|
11456
12022
|
if (!result.ok) throw result.error;
|
|
11457
12023
|
};
|
|
11458
12024
|
|
|
12025
|
+
function openDocument(address, options) {
|
|
12026
|
+
sidebar.openResource(address, { ...options, kind: 'text' });
|
|
12027
|
+
}
|
|
12028
|
+
|
|
11459
12029
|
function openFolderTab(path, paneId) {
|
|
11460
12030
|
const address = instancePrefix + crypto.randomUUID();
|
|
11461
12031
|
folderTabs.set(address, path);
|
|
11462
12032
|
try {
|
|
11463
12033
|
sidebar.openResource(address, { kind: 'files', paneId });
|
|
11464
|
-
|
|
12034
|
+
syncTabs();
|
|
12035
|
+
const tab = sidebar.active();
|
|
12036
|
+
return tab?.contentId === address ? bindings.get(tab.id) : undefined;
|
|
12037
|
+
} finally {
|
|
11465
12038
|
folderTabs.delete(address);
|
|
11466
|
-
throw error;
|
|
11467
12039
|
}
|
|
11468
12040
|
}
|
|
11469
12041
|
|
|
12042
|
+
async function statFile(sessionId, file, signal) {
|
|
12043
|
+
try {
|
|
12044
|
+
const result = await scope.remote.workspaceFiles.stat(sessionId, file, signal);
|
|
12045
|
+
const absolute = windowsPath(result.ok && result.value.absolutePath);
|
|
12046
|
+
if (absolute) return { absolute, name: absolute.split('\\').at(-1), size: result.value.bytes };
|
|
12047
|
+
} catch { /* The built-in preview can report a failed session file lookup. */ }
|
|
12048
|
+
}
|
|
12049
|
+
|
|
12050
|
+
function revealFile(view, file, line) {
|
|
12051
|
+
const preview = [file.absolute, file.name, file.size, line];
|
|
12052
|
+
if (view.fileActions) view.fileActions.reveal(...preview);
|
|
12053
|
+
else view.pendingPreview = preview;
|
|
12054
|
+
}
|
|
12055
|
+
|
|
12056
|
+
function fileRequest(address, options) {
|
|
12057
|
+
const sessionId = sidebar.mounted.getSnapshot();
|
|
12058
|
+
const file = workspace && sessionId === presentedSession && sessionFilePath(address, sessionId);
|
|
12059
|
+
if (file) return { address, sessionId, file, params: options?.params };
|
|
12060
|
+
}
|
|
12061
|
+
|
|
12062
|
+
function currentExplorer() {
|
|
12063
|
+
return bindings.get(sidebar.active()?.id) ?? activeView ?? viewList[0];
|
|
12064
|
+
}
|
|
12065
|
+
|
|
12066
|
+
async function openConversationFile(request, signal, { view, newTab, paneId }) {
|
|
12067
|
+
const { address, sessionId, file, params } = request;
|
|
12068
|
+
const metadata = await statFile(sessionId, file, signal);
|
|
12069
|
+
if (signal.aborted || sidebar.mounted.getSnapshot() !== sessionId) return;
|
|
12070
|
+
if (!metadata) {
|
|
12071
|
+
openDocument(address, { params, paneId });
|
|
12072
|
+
return;
|
|
12073
|
+
}
|
|
12074
|
+
const existing = newTab ? undefined : viewList.includes(view) ? view : currentExplorer();
|
|
12075
|
+
const tabId = existing && [...bindings].find(([, item]) => item === existing)?.[0];
|
|
12076
|
+
if (tabId) {
|
|
12077
|
+
sidebar.focus(tabId);
|
|
12078
|
+
if (!sidebar.isExpanded() && !existing.target?.isFloating) sidebar.toggleExpanded();
|
|
12079
|
+
}
|
|
12080
|
+
const target = tabId ? existing : openFolderTab(parentOf(metadata.absolute), paneId);
|
|
12081
|
+
if (target) revealFile(target, metadata, params?.line);
|
|
12082
|
+
}
|
|
12083
|
+
|
|
12084
|
+
function installFileOpener() {
|
|
12085
|
+
const openResource = sidebar.openResource;
|
|
12086
|
+
let fileClick;
|
|
12087
|
+
let dragSource;
|
|
12088
|
+
function open(address, options) {
|
|
12089
|
+
const request = (!options || Object.keys(options).every(key => key === 'params')) && fileRequest(address, options);
|
|
12090
|
+
if (!request) return openResource.call(this, address, options);
|
|
12091
|
+
if (dragSource) {
|
|
12092
|
+
dragSource.file = request;
|
|
12093
|
+
return;
|
|
12094
|
+
}
|
|
12095
|
+
fileClick?.abort();
|
|
12096
|
+
fileClick = new AbortController();
|
|
12097
|
+
void openConversationFile(request, fileClick.signal, { view: currentExplorer() });
|
|
12098
|
+
}
|
|
12099
|
+
function readDragSource(button) {
|
|
12100
|
+
// Let Harness resolve the same path and line as a real click, without opening a tab yet.
|
|
12101
|
+
const capture = {};
|
|
12102
|
+
dragSource = capture;
|
|
12103
|
+
try { button.click(); }
|
|
12104
|
+
finally { dragSource = undefined; }
|
|
12105
|
+
return capture.file;
|
|
12106
|
+
}
|
|
12107
|
+
sidebar.openResource = open;
|
|
12108
|
+
const stopDrop = installConversationDrop(sidebar.mounted, readDragSource, (request, paneId, signal) => {
|
|
12109
|
+
void openConversationFile(request, signal, { newTab: true, paneId });
|
|
12110
|
+
});
|
|
12111
|
+
const stop = sidebar.mounted.subscribe(() => fileClick?.abort());
|
|
12112
|
+
return () => {
|
|
12113
|
+
stopDrop();
|
|
12114
|
+
stop();
|
|
12115
|
+
fileClick?.abort();
|
|
12116
|
+
if (sidebar.openResource === open) sidebar.openResource = openResource;
|
|
12117
|
+
};
|
|
12118
|
+
}
|
|
12119
|
+
|
|
11470
12120
|
function refreshDirectories(paths, moved = []) {
|
|
11471
12121
|
const changed = new Set(paths.map(path => path.toLowerCase()));
|
|
11472
12122
|
for (const view of viewList) {
|
|
@@ -11483,101 +12133,81 @@ window.__ModuleLoader__.load({
|
|
|
11483
12133
|
}
|
|
11484
12134
|
|
|
11485
12135
|
function Anchor({ sessionId, useTabInfo }) {
|
|
11486
|
-
const { tab } = useTabInfo();
|
|
12136
|
+
const { tab, panel } = useTabInfo();
|
|
11487
12137
|
const anchor = react.useRef(null);
|
|
11488
|
-
const
|
|
12138
|
+
const view = react.useSyncExternalStore(subscribe, () => boundView(sessionId, tab.id));
|
|
12139
|
+
const navigation = tab.navigation;
|
|
11489
12140
|
react.useLayoutEffect(() => {
|
|
11490
|
-
if (!tab.visible) return;
|
|
12141
|
+
if (!tab.visible || !view) return;
|
|
11491
12142
|
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 };
|
|
12143
|
+
const target = { sessionId, paneId: panel.id, isFloating: panel.isFloating };
|
|
11513
12144
|
view.target = target;
|
|
11514
12145
|
element.appendChild(view.host);
|
|
11515
12146
|
notify();
|
|
12147
|
+
capturePresentation(sessionId);
|
|
11516
12148
|
const dispose = tab.actions.bindCommands?.({ refresh: () => view.fileActions?.refresh() });
|
|
11517
12149
|
return () => {
|
|
11518
12150
|
dispose?.();
|
|
11519
12151
|
if (view.target !== target) return;
|
|
11520
12152
|
view.target = undefined;
|
|
11521
12153
|
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
12154
|
notify();
|
|
12155
|
+
capturePresentation(sessionId);
|
|
11527
12156
|
};
|
|
11528
|
-
}, [
|
|
11529
|
-
react.
|
|
11530
|
-
if (tab.visible
|
|
11531
|
-
|
|
12157
|
+
}, [view, sessionId, tab.visible, tab.actions, panel.id, panel.isFloating]);
|
|
12158
|
+
react.useEffect(() => {
|
|
12159
|
+
if (!tab.visible || !view || view.handledNavigation === navigation) return;
|
|
12160
|
+
const { address, params } = navigation;
|
|
12161
|
+
const file = sessionFilePath(address, sessionId);
|
|
12162
|
+
if (!file) return;
|
|
12163
|
+
|
|
12164
|
+
const controller = new AbortController();
|
|
12165
|
+
const { signal } = controller;
|
|
12166
|
+
const isCurrent = () => !signal.aborted && !tab.signal?.aborted
|
|
12167
|
+
&& sidebar.mounted.getSnapshot() === sessionId && boundView(sessionId, tab.id) === view;
|
|
12168
|
+
async function reveal() {
|
|
12169
|
+
const metadata = await statFile(sessionId, file, signal);
|
|
12170
|
+
if (!isCurrent()) return;
|
|
12171
|
+
view.handledNavigation = navigation;
|
|
12172
|
+
if (metadata) {
|
|
12173
|
+
view.revealedFile = true;
|
|
12174
|
+
revealFile(view, metadata, params?.line);
|
|
12175
|
+
} else {
|
|
12176
|
+
openDocument(address, { paneId: panel.id, params, replaceTab: view.revealedFile ? undefined : tab.id });
|
|
12177
|
+
}
|
|
12178
|
+
}
|
|
12179
|
+
void reveal();
|
|
12180
|
+
return () => controller.abort();
|
|
12181
|
+
}, [view, sessionId, tab.id, tab.visible, navigation, panel.id]);
|
|
11532
12182
|
return h('div', { className: 'dwe-host', ref: anchor });
|
|
11533
12183
|
}
|
|
11534
12184
|
|
|
11535
12185
|
function Title({ sessionId, useTabInfo }) {
|
|
11536
12186
|
const { tab } = useTabInfo();
|
|
11537
|
-
const path = react.useSyncExternalStore(subscribe, () =>
|
|
12187
|
+
const path = react.useSyncExternalStore(subscribe, () => boundView(sessionId, tab.id)?.path ?? null);
|
|
12188
|
+
react.useLayoutEffect(() => {
|
|
12189
|
+
if (tab.visible) capturePresentation(sessionId);
|
|
12190
|
+
});
|
|
11538
12191
|
return h(ExplorerTitle, { path });
|
|
11539
12192
|
}
|
|
11540
12193
|
|
|
11541
|
-
function View({ view,
|
|
12194
|
+
function View({ view, cwd }) {
|
|
11542
12195
|
const target = react.useSyncExternalStore(subscribe, view.getTarget);
|
|
11543
12196
|
const sessionId = target?.sessionId;
|
|
11544
|
-
const cwd = useSessions(state => sessionId ? state.byId[sessionId]?.cwd : undefined);
|
|
11545
12197
|
return reactDom.createPortal(h(Explorer, {
|
|
11546
12198
|
sessionId, cwd, initialPath: view.initialPath, getTarget: view.getTarget, bindFileActions: view.bindFileActions,
|
|
11547
|
-
onPath: view.onPath, openLocal, openFolderTab, refreshDirectories, updateRenamedPath,
|
|
12199
|
+
onPath: view.onPath, openLocal, openDocument, openFolderTab, refreshDirectories, updateRenamedPath,
|
|
11548
12200
|
}), view.host);
|
|
11549
12201
|
}
|
|
11550
12202
|
|
|
11551
12203
|
function Keeper({ useSessions }) {
|
|
11552
12204
|
const sessionId = react.useSyncExternalStore(sidebar.mounted.subscribe, sidebar.mounted.getSnapshot);
|
|
11553
12205
|
const currentViews = react.useSyncExternalStore(subscribe, () => viewList);
|
|
11554
|
-
const
|
|
12206
|
+
const cwd = useSessions(state => sessionId ? state.byId[sessionId]?.cwd : undefined);
|
|
11555
12207
|
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 }));
|
|
12208
|
+
if (sessionId && sidebar.mounted.getSnapshot() === sessionId) present(sessionId, cwd);
|
|
12209
|
+
}, [sessionId, cwd]);
|
|
12210
|
+
return currentViews.map(view => h(View, { key: view.address, view, cwd }));
|
|
11581
12211
|
}
|
|
11582
12212
|
|
|
11583
12213
|
scope.effect(() => {
|
|
@@ -11595,26 +12225,17 @@ window.__ModuleLoader__.load({
|
|
|
11595
12225
|
}
|
|
11596
12226
|
}
|
|
11597
12227
|
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
|
-
});
|
|
12228
|
+
const disposeFileOpener = installFileOpener();
|
|
12229
|
+
const disposeMotion = installSidebarMotion(sidebar);
|
|
12230
|
+
const disposeTabs = sidebar.openTabs.subscribe(syncTabs);
|
|
12231
|
+
const disposeSwitch = sidebar.mounted.subscribe(closeDepartingTabs);
|
|
11616
12232
|
return () => {
|
|
11617
|
-
|
|
12233
|
+
disposeFileOpener();
|
|
12234
|
+
disposeMotion();
|
|
12235
|
+
disposeTabs();
|
|
12236
|
+
disposeSwitch();
|
|
12237
|
+
detached = false;
|
|
12238
|
+
finishHandoff();
|
|
11618
12239
|
document.removeEventListener('wheel', wheel, true);
|
|
11619
12240
|
style.remove();
|
|
11620
12241
|
};
|
|
@@ -11628,7 +12249,7 @@ window.__ModuleLoader__.load({
|
|
|
11628
12249
|
scope.slots.register(key ? { name, key } : { name, id }, component)));
|
|
11629
12250
|
}
|
|
11630
12251
|
scope.effect(() => scope.sidebarRightTabs.register({
|
|
11631
|
-
id, kind: 'files', priority: 'extension', title: () => '文件浏览',
|
|
12252
|
+
id, kind: 'files', priority: 'extension', title: () => '文件浏览', multiple: true,
|
|
11632
12253
|
guide: [{
|
|
11633
12254
|
id: 'workspace', commandId: 'workspace.files', order: 10, title: () => '文件浏览',
|
|
11634
12255
|
description: () => '浏览本机磁盘,在下方独立预览文件',
|