dsh-linked-workspace 0.5.12 → 0.5.13

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 CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.13 — 2026-11-05
4
+
5
+ - The linked-workspace directory panel can now be resized by dragging the divider; double-click the divider to reset to 300 px. The width is persisted.
6
+ - The local directory tree now refreshes silently every 4 seconds (and on window focus), so newly created local subdirectories appear without a manual refresh; expanded folders are reloaded in place.
7
+
3
8
  ## 0.5.12 — 2026-11-04
4
9
 
5
10
  - Published as **dsh-linked-workspace** on npm (public).
package/README.en.md CHANGED
@@ -99,6 +99,10 @@ Old “Remote directories” tabs are restored after refresh through hidden lega
99
99
 
100
100
  See [CHANGELOG.md](CHANGELOG.md) for the full version history.
101
101
 
102
+ ### 0.5.13
103
+
104
+ The directory panel divider is draggable (double-click resets to 300 px; width persists). The local tree refreshes silently every 4 seconds and on window focus, so new local subdirectories appear without a manual refresh.
105
+
102
106
  ## Releases and marketplace updates
103
107
 
104
108
  - The npm package is `dsh-linked-workspace`. To release, bump `package.json` and push a `v*` tag; `.github/workflows/release.yml` runs `npm publish --access public` (set an `NPM_TOKEN` repository secret).
package/lib/client.js CHANGED
@@ -2057,6 +2057,7 @@ function fileIconSpec(name, isDir) {
2057
2057
  openFileHint: '从工作区目录树中选择文件',
2058
2058
  toggleDirectory: '开启/关闭联动工作区目录面板',
2059
2059
  closeFile: '关闭文件标签',
2060
+ resizeDirectory: '拖动调整目录面板宽度;双击恢复默认宽度',
2060
2061
  remoteTabTitle: '远程目录',
2061
2062
  remoteTabDesc: '只读查看已添加的远程目录及其文件,不允许编辑。',
2062
2063
  remoteDirsTitle: '远程目录',
@@ -2331,6 +2332,7 @@ function fileIconSpec(name, isDir) {
2331
2332
  openFileHint: 'Select a file from the workspace directory tree',
2332
2333
  toggleDirectory: 'Show or hide the linked workspace directory panel',
2333
2334
  closeFile: 'Close file tab',
2335
+ resizeDirectory: 'Drag to resize the directory panel; double-click to reset',
2334
2336
  remoteTabTitle: 'Remote directories',
2335
2337
  remoteTabDesc: 'Read-only view of configured remote directories and files; editing is disabled.',
2336
2338
  remoteDirsTitle: 'Remote directories',
@@ -2934,7 +2936,9 @@ function fileIconSpec(name, isDir) {
2934
2936
  '.rw-ctx-backdrop{position:fixed;inset:0;z-index:70}',
2935
2937
  '.rw-workspace-body{display:flex;flex:1 1 auto;min-height:0;min-width:0}',
2936
2938
  '.rw-content-pane{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0}',
2937
- '.rw-directory-pane{display:flex;flex-direction:column;flex:0 0 300px;max-width:48%;min-height:0;border-left:1px solid var(--dsw-alias-border-l1)}',
2939
+ '.rw-directory-pane{display:flex;flex-direction:column;flex:0 0 auto;max-width:60%;min-width:240px;min-height:0;border-left:1px solid var(--dsw-alias-border-l1)}',
2940
+ '.rw-pane-resizer{flex:0 0 6px;align-self:stretch;cursor:col-resize;background:transparent;position:relative;z-index:2;touch-action:none}',
2941
+ '.rw-pane-resizer:hover,.rw-pane-resizer:active{background:var(--dsw-alias-brand-primary)}',
2938
2942
  '.rw-directory-head{display:flex;align-items:center;gap:6px;padding:5px 8px;font-size:10.5px;line-height:16px;letter-spacing:.03em;text-transform:uppercase;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-1);border-bottom:1px solid var(--dsw-alias-border-l1);flex:none}',
2939
2943
  '.rw-directory-body{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}',
2940
2944
  '.rw-file-tabs{display:flex;align-items:stretch;gap:2px;min-height:30px;padding:3px 5px;overflow:auto;background:var(--dsw-alias-bg-layer-1);border-bottom:1px solid var(--dsw-alias-border-l1);flex:none}',
@@ -3889,6 +3893,11 @@ function fileIconSpec(name, isDir) {
3889
3893
  var setCache = cacheState[1];
3890
3894
  var pending = useRef(new Set());
3891
3895
 
3896
+ useEffect(function () {
3897
+ if (!props.refreshTick) return;
3898
+ Array.from(expanded).forEach(function (path) { load(path, true); });
3899
+ }, [props.refreshTick]);
3900
+
3892
3901
  function toggle(path) {
3893
3902
  setExpanded(function (previous) {
3894
3903
  var next = new Set(previous);
@@ -3898,13 +3907,13 @@ function fileIconSpec(name, isDir) {
3898
3907
  });
3899
3908
  }
3900
3909
 
3901
- function load(path) {
3910
+ function load(path, silent) {
3902
3911
  if (pending.current.has(path)) return;
3903
3912
  pending.current.add(path);
3904
3913
  setCache(function (previous) {
3905
3914
  var entries = new Map(previous.entries);
3906
3915
  var existing = entries.get(path) || {};
3907
- entries.set(path, { loading: true, error: null, entries: existing.entries || [], loaded: existing.loaded });
3916
+ entries.set(path, { loading: !silent, error: null, entries: existing.entries || [], loaded: existing.loaded });
3908
3917
  return { entries: entries };
3909
3918
  });
3910
3919
  var request = props.kind === 'local'
@@ -4445,10 +4454,68 @@ function fileIconSpec(name, isDir) {
4445
4454
  var directoryOpenState = useState(true);
4446
4455
  var directoryOpen = directoryOpenState[0];
4447
4456
  var setDirectoryOpen = directoryOpenState[1];
4457
+ var directoryWidthState = useState(function () {
4458
+ try {
4459
+ var saved = parseInt(window.localStorage.getItem('dsh-linked-workspace:directory-width'), 10);
4460
+ return saved >= 240 && saved <= 900 ? saved : 300;
4461
+ } catch (error) { return 300; }
4462
+ });
4463
+ var directoryWidth = directoryWidthState[0];
4464
+ var setDirectoryWidth = directoryWidthState[1];
4465
+ var directoryWidthRef = useRef(directoryWidth);
4466
+ var workbenchRef = useRef(null);
4467
+ var localTickState = useState(0);
4468
+ var localTick = localTickState[0];
4469
+ var setLocalTick = localTickState[1];
4448
4470
  var fileDirtyState = useState({});
4449
4471
  var fileDirty = fileDirtyState[0];
4450
4472
  var setFileDirty = fileDirtyState[1];
4451
4473
  var directoryTabId = props.tab && props.tab.id;
4474
+
4475
+ useEffect(function () {
4476
+ directoryWidthRef.current = directoryWidth;
4477
+ try { window.localStorage.setItem('dsh-linked-workspace:directory-width', String(directoryWidth)); } catch (error) { /* storage unavailable */ }
4478
+ }, [directoryWidth]);
4479
+
4480
+ useEffect(function () {
4481
+ if (!directoryOpen) return undefined;
4482
+ var bump = function () {
4483
+ if (typeof document !== 'undefined' && document.visibilityState === 'hidden') return;
4484
+ setLocalTick(function (value) { return value + 1; });
4485
+ };
4486
+ var timer = setInterval(bump, 4000);
4487
+ if (timer && typeof timer.unref === 'function') timer.unref();
4488
+ window.addEventListener('focus', bump);
4489
+ return function () {
4490
+ clearInterval(timer);
4491
+ window.removeEventListener('focus', bump);
4492
+ };
4493
+ }, [directoryOpen, selectedKey]);
4494
+
4495
+ function beginResize(event) {
4496
+ if (event.button !== undefined && event.button !== 0) return;
4497
+ event.preventDefault();
4498
+ var startX = event.clientX;
4499
+ var startWidth = directoryWidthRef.current;
4500
+ var workbench = workbenchRef.current;
4501
+ var maxWidth = workbench ? Math.max(260, workbench.clientWidth - 320) : 720;
4502
+ document.body.style.cursor = 'col-resize';
4503
+ document.body.style.userSelect = 'none';
4504
+ function move(moveEvent) {
4505
+ var next = Math.max(240, Math.min(maxWidth, startWidth + (startX - moveEvent.clientX)));
4506
+ setDirectoryWidth(next);
4507
+ }
4508
+ function finish() {
4509
+ document.removeEventListener('pointermove', move);
4510
+ document.removeEventListener('pointerup', finish);
4511
+ document.removeEventListener('pointercancel', finish);
4512
+ document.body.style.cursor = '';
4513
+ document.body.style.userSelect = '';
4514
+ }
4515
+ document.addEventListener('pointermove', move);
4516
+ document.addEventListener('pointerup', finish);
4517
+ document.addEventListener('pointercancel', finish);
4518
+ }
4452
4519
  function restoreDirectoryTab() {
4453
4520
  if (!sidebar || typeof sidebar.activateTab !== 'function' || !directoryTabId) return;
4454
4521
  setTimeout(function () {
@@ -4860,7 +4927,7 @@ function fileIconSpec(name, isDir) {
4860
4927
  }, [searchOpen, searchQuery, selectedKey, version]);
4861
4928
 
4862
4929
  if (!selected) {
4863
- return h('div', { className: 'rw-workbench rw-workspace' },
4930
+ return h('div', { className: 'rw-workbench rw-workspace', ref: workbenchRef },
4864
4931
  h('div', { className: 'rw-tree-scroll' },
4865
4932
  data.data.loading
4866
4933
  ? h('div', { className: 'rw-row-msg' }, Spinner(13), t('loading'))
@@ -4923,6 +4990,7 @@ function fileIconSpec(name, isDir) {
4923
4990
  selectedPath: selectedEntry && selectedEntry.side === 'local' ? selectedEntry.path : '',
4924
4991
  onSelectPath: function (info) { setSelectedEntry(Object.assign({ side: 'local' }, info)); },
4925
4992
  onSelect: function (filePath) { openByMode('local', filePath); },
4993
+ refreshTick: localTick,
4926
4994
  onContextMenu: function (info) { setSelectedEntry(Object.assign({ side: 'local' }, info)); openContextMenu('local', info); },
4927
4995
  }))
4928
4996
  : null);
@@ -5024,7 +5092,17 @@ function fileIconSpec(name, isDir) {
5024
5092
  : null,
5025
5093
  h('div', { className: 'rw-content-body' }, contentArea)),
5026
5094
  directoryOpen
5027
- ? h('div', { className: 'rw-directory-pane' },
5095
+ ? h('div', {
5096
+ className: 'rw-pane-resizer',
5097
+ role: 'separator',
5098
+ 'aria-orientation': 'vertical',
5099
+ title: t('resizeDirectory'),
5100
+ onPointerDown: beginResize,
5101
+ onDoubleClick: function () { setDirectoryWidth(300); },
5102
+ })
5103
+ : null,
5104
+ directoryOpen
5105
+ ? h('div', { className: 'rw-directory-pane', style: { flexBasis: directoryWidth + 'px' } },
5028
5106
  h('div', { className: 'rw-directory-head' },
5029
5107
  IconFolder(11, false),
5030
5108
  h('span', { style: { flex: '1 1 auto', minWidth: 0 } }, remoteOnly ? t('remoteTabTitle') : t('tabTitle')),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-linked-workspace",
3
- "version": "0.5.12",
3
+ "version": "0.5.13",
4
4
  "description": "Linked local ⇄ remote workspaces and SSH-backed remote directories for DeepSeek Harness: theme-aware split editor, file-type icons, and an agent tool that edits remote files directly.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",