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/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', { className: 'dwe-preview-cover', 'data-poster': Boolean(poster) || undefined },
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-action' },
9875
- !videoType && fileIcon,
9876
- h('button', {
9877
- type: 'button', className: videoType ? 'dwe-cover-play' : 'dwe-cover-open',
9878
- title: label, 'aria-label': label, onClick: start,
9879
- }, videoType ? h('svg', { width: 24, height: 24, viewBox: '0 0 24 24', 'aria-hidden': true },
9880
- h('path', { d: 'M9 5v14l11-7z', fill: 'currentColor' })) : label)),
9881
- h('div', { className: 'dwe-cover-file', title: file.name }, file.name));
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('pre', { className: 'dwe-text' },
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', null, ...folders.map(path => {
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', { key: path },
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 = workspaceMode ? (sessionId ? workspace : scoped.root) : null;
10580
- const history = workspaceMode ? (root === scoped.root ? scoped.history : firstPath(root)) : localHistory;
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, root]);
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
- function openPreview(absolute, name) {
10754
- setPreview(current => current?.absolute === absolute ? current : {
10755
- name, absolute, address: resourceAddress(absolute),
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
- function openSearchHit(match) {
10760
- if (!underPath(match.path, root)) return;
10761
- const folder = parentOf(match.path);
10762
- navigate(folder);
10763
- resultToReveal.current = { folder, name: match.name };
10764
- setSelection(singleSelection(match.name));
10765
- openPreview(match.path, match.name);
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) target.actions.openResource(resourceAddress(shownPreview.absolute, target.sessionId));
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
- h('div', { className: 'dwe-nav', role: 'group', 'aria-label': '视图与预览' },
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 var(--dwe-line);
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="favorites"][aria-expanded="true"] {
11173
- background: color-mix(in srgb, var(--dwe-blue) 20%, transparent);
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 var(--dwe-line);
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; justify-content: space-between; gap: 4px;
11278
- padding: 2px 6px 2px 12px; border-top: 1px solid var(--dwe-line);
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: 100px; overflow: hidden; background: var(--dwe-tint);
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; border-radius: 50%;
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-file {
11309
- position: absolute; inset-block-end: 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
11310
- max-width: calc(100% - 24px); padding: 3px 8px; border-radius: 5px;
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-preview-cover[data-poster] .dwe-cover-file { background: #15171abb; color: #fff; }
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 views = new Map();
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 updateViews() {
11450
- viewList = [...new Set(views.values())];
11451
- notify();
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
- } catch (error) {
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 key = occurrence(sessionId, tab.id);
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
- let view = views.get(key);
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
- }, [key, sessionId, tab.visible, tab.actions]);
11529
- react.useLayoutEffect(() => {
11530
- if (tab.visible && sidebar.active()?.id === tab.id) activeView = views.get(key);
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, () => views.get(occurrence(sessionId, tab.id))?.path ?? null);
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, useSessions }) {
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 previous = react.useRef();
12206
+ const cwd = useSessions(state => sessionId ? state.byId[sessionId]?.cwd : undefined);
11555
12207
  react.useLayoutEffect(() => {
11556
- if (!sessionId || previous.current === sessionId) return;
11557
- const view = previous.current === undefined ? undefined : activeView;
11558
- previous.current = sessionId;
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 dispose = sidebar.openTabs.subscribe(() => {
11599
- const inventory = sidebar.openTabs.getSnapshot();
11600
- const live = new Set(inventory.map(tab => occurrence(tab.sessionId, tab.tabId)));
11601
- const addresses = new Set(inventory.map(tab => tab.contentId));
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
- dispose();
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: () => '文件浏览', keepMounted: true, multiple: true,
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: () => '浏览本机磁盘,在下方独立预览文件',