@rooode/dsh-plugin-preview 0.1.13 → 0.1.15
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 +131 -72
- package/lib/index.js +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -252,12 +252,37 @@
|
|
|
252
252
|
|
|
253
253
|
function getAllAvailableWorkspaces() {
|
|
254
254
|
const map = new Map();
|
|
255
|
-
const currentRoot = getCurrentWorkspaceRoot().replace(/[\\/]+$/, '').toLowerCase();
|
|
255
|
+
const currentRoot = (getCurrentWorkspaceRoot() || '').replace(/[\\/]+$/, '').toLowerCase();
|
|
256
256
|
|
|
257
|
-
// 1. Collect from
|
|
257
|
+
// 1. Collect from workspaces store in clientCtx.workspaces
|
|
258
|
+
if (clientCtx && clientCtx.workspaces) {
|
|
259
|
+
try {
|
|
260
|
+
const wSnap = clientCtx.workspaces.list?.getSnapshot?.();
|
|
261
|
+
const items = (wSnap && (wSnap.items || wSnap.workspaces || wSnap.byId)) || [];
|
|
262
|
+
const list = Array.isArray(items) ? items : Object.values(items);
|
|
263
|
+
for (const w of list) {
|
|
264
|
+
const dir = (w && (w.path || w.directory || w.cwd)) || '';
|
|
265
|
+
if (dir && typeof dir === 'string' && dir.trim()) {
|
|
266
|
+
const norm = dir.trim().replace(/[\\/]+$/, '');
|
|
267
|
+
const key = norm.toLowerCase();
|
|
268
|
+
if (!map.has(key)) {
|
|
269
|
+
map.set(key, {
|
|
270
|
+
id: w.workspaceId || w.id || norm,
|
|
271
|
+
path: norm,
|
|
272
|
+
name: w.title || w.name || getFileName(norm) || norm,
|
|
273
|
+
isCurrent: key === currentRoot,
|
|
274
|
+
source: 'workspace',
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
} catch (e) {}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// 2. Collect from active & historical sessions in clientCtx.sessions
|
|
258
283
|
if (clientCtx && clientCtx.sessions) {
|
|
259
284
|
try {
|
|
260
|
-
const sSnap = clientCtx.sessions.list?.getSnapshot();
|
|
285
|
+
const sSnap = clientCtx.sessions.list?.getSnapshot?.();
|
|
261
286
|
if (sSnap && sSnap.byId) {
|
|
262
287
|
for (const sid of Object.keys(sSnap.byId)) {
|
|
263
288
|
const s = sSnap.byId[sid];
|
|
@@ -279,33 +304,6 @@
|
|
|
279
304
|
} catch (e) {}
|
|
280
305
|
}
|
|
281
306
|
|
|
282
|
-
// 2. Collect from workspaces store in clientCtx.workspaces
|
|
283
|
-
if (clientCtx && clientCtx.workspaces) {
|
|
284
|
-
try {
|
|
285
|
-
const wSnap = clientCtx.workspaces.list?.getSnapshot();
|
|
286
|
-
if (wSnap) {
|
|
287
|
-
const rawList = wSnap.workspaces || wSnap.items || wSnap.byId || [];
|
|
288
|
-
const list = Array.isArray(rawList) ? rawList : Object.values(rawList);
|
|
289
|
-
for (const w of list) {
|
|
290
|
-
const dir = (w && (w.directory || w.path || w.cwd)) || '';
|
|
291
|
-
if (dir && typeof dir === 'string' && dir.trim()) {
|
|
292
|
-
const norm = dir.trim().replace(/[\\/]+$/, '');
|
|
293
|
-
const key = norm.toLowerCase();
|
|
294
|
-
if (!map.has(key)) {
|
|
295
|
-
map.set(key, {
|
|
296
|
-
id: w.id || norm,
|
|
297
|
-
path: norm,
|
|
298
|
-
name: w.title || w.name || getFileName(norm) || norm,
|
|
299
|
-
isCurrent: key === currentRoot,
|
|
300
|
-
source: 'workspace',
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
} catch (e) {}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
307
|
// 3. Collect from globalManualWorkspace if set
|
|
310
308
|
if (globalManualWorkspace && typeof globalManualWorkspace === 'string' && globalManualWorkspace.trim()) {
|
|
311
309
|
const norm = globalManualWorkspace.trim().replace(/[\\/]+$/, '');
|
|
@@ -331,51 +329,62 @@
|
|
|
331
329
|
}
|
|
332
330
|
|
|
333
331
|
function getCurrentWorkspaceRoot() {
|
|
334
|
-
// Priority 1: User manual override selection
|
|
332
|
+
// Priority 1: User manual override selection (if explicitly chosen by user)
|
|
335
333
|
if (globalManualWorkspace && typeof globalManualWorkspace === 'string' && globalManualWorkspace.trim()) {
|
|
336
334
|
return globalManualWorkspace.trim();
|
|
337
335
|
}
|
|
338
336
|
|
|
339
|
-
// Priority 2:
|
|
340
|
-
if (clientCtx
|
|
337
|
+
// Priority 2: Derive active workspace from clientCtx (workspaces + sessions)
|
|
338
|
+
if (clientCtx) {
|
|
341
339
|
try {
|
|
342
|
-
const sSnap = clientCtx.sessions
|
|
340
|
+
const sSnap = clientCtx.sessions?.list?.getSnapshot?.();
|
|
341
|
+
const wSnap = clientCtx.workspaces?.list?.getSnapshot?.();
|
|
343
342
|
const currentSessionId = sSnap?.current;
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
343
|
+
|
|
344
|
+
// A. Find workspace in wSnap.items containing currentSessionId
|
|
345
|
+
if (currentSessionId && wSnap && Array.isArray(wSnap.items) && wSnap.items.length > 0) {
|
|
346
|
+
const match = wSnap.items.find(item => item.sessionIds && Array.isArray(item.sessionIds) && item.sessionIds.includes(currentSessionId));
|
|
347
|
+
if (match && match.path && typeof match.path === 'string' && match.path.trim()) {
|
|
348
|
+
return match.path.trim();
|
|
348
349
|
}
|
|
349
350
|
}
|
|
350
|
-
} catch (e) {}
|
|
351
|
-
}
|
|
352
351
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
const curId = wSnap.current;
|
|
359
|
-
if (curId && wSnap.workspaces && wSnap.workspaces[curId]) {
|
|
360
|
-
const w = wSnap.workspaces[curId];
|
|
361
|
-
const dir = w.directory || w.path || w.cwd;
|
|
362
|
-
if (dir) return dir.trim();
|
|
352
|
+
// B. Check active session summary cwd or workspaceId
|
|
353
|
+
if (currentSessionId && sSnap && sSnap.byId && sSnap.byId[currentSessionId]) {
|
|
354
|
+
const sess = sSnap.byId[currentSessionId];
|
|
355
|
+
if (sess.cwd && typeof sess.cwd === 'string' && sess.cwd.trim()) {
|
|
356
|
+
return sess.cwd.trim();
|
|
363
357
|
}
|
|
364
|
-
if (
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
|
|
358
|
+
if (sess.workspaceId && wSnap && Array.isArray(wSnap.items)) {
|
|
359
|
+
const match = wSnap.items.find(item => item.workspaceId === sess.workspaceId);
|
|
360
|
+
if (match && match.path && typeof match.path === 'string' && match.path.trim()) {
|
|
361
|
+
return match.path.trim();
|
|
362
|
+
}
|
|
368
363
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// C. Check recentWorkspaceId in wSnap
|
|
367
|
+
if (wSnap && wSnap.recentWorkspaceId && Array.isArray(wSnap.items)) {
|
|
368
|
+
const recent = wSnap.items.find(item => item.workspaceId === wSnap.recentWorkspaceId);
|
|
369
|
+
if (recent && recent.path && typeof recent.path === 'string' && recent.path.trim()) {
|
|
370
|
+
return recent.path.trim();
|
|
373
371
|
}
|
|
374
372
|
}
|
|
375
|
-
|
|
373
|
+
|
|
374
|
+
// D. Fallback to first workspace in wSnap.items
|
|
375
|
+
if (wSnap && Array.isArray(wSnap.items) && wSnap.items.length > 0) {
|
|
376
|
+
const first = wSnap.items[0];
|
|
377
|
+
const dir = first.path || first.directory || first.cwd;
|
|
378
|
+
if (dir && typeof dir === 'string' && dir.trim()) {
|
|
379
|
+
return dir.trim();
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
} catch (e) {
|
|
383
|
+
console.warn('[Preview] getCurrentWorkspaceRoot failed:', e);
|
|
384
|
+
}
|
|
376
385
|
}
|
|
377
386
|
|
|
378
|
-
// Priority
|
|
387
|
+
// Priority 3: URL hash or search parameter
|
|
379
388
|
if (typeof location !== 'undefined') {
|
|
380
389
|
if (location.hash) {
|
|
381
390
|
const m = location.hash.match(/workspace=([^&]+)/);
|
|
@@ -1629,13 +1638,28 @@
|
|
|
1629
1638
|
const [dragOverNode, setDragOverNode] = useState(null);
|
|
1630
1639
|
const [clipboard, setClipboard] = useState(null); // { action: 'cut', path: string, name: string, isDir: boolean } | null
|
|
1631
1640
|
|
|
1632
|
-
const currentWs = getCurrentWorkspaceRoot();
|
|
1641
|
+
const [currentWs, setCurrentWs] = useState(getCurrentWorkspaceRoot());
|
|
1642
|
+
|
|
1643
|
+
useEffect(() => {
|
|
1644
|
+
const handleStateUpdate = () => {
|
|
1645
|
+
const newWs = getCurrentWorkspaceRoot();
|
|
1646
|
+
if (newWs !== currentWs) {
|
|
1647
|
+
setCurrentWs(newWs);
|
|
1648
|
+
}
|
|
1649
|
+
};
|
|
1650
|
+
stateListeners.add(handleStateUpdate);
|
|
1651
|
+
return () => stateListeners.delete(handleStateUpdate);
|
|
1652
|
+
}, [currentWs]);
|
|
1633
1653
|
|
|
1634
|
-
const loadTree = useCallback(async (force = false) => {
|
|
1654
|
+
const loadTree = useCallback(async (force = false, targetWs) => {
|
|
1635
1655
|
setIsLoading(true);
|
|
1636
1656
|
setError(null);
|
|
1637
1657
|
try {
|
|
1638
|
-
const rootPath = getCurrentWorkspaceRoot();
|
|
1658
|
+
const rootPath = targetWs || currentWs || getCurrentWorkspaceRoot();
|
|
1659
|
+
if (!rootPath) {
|
|
1660
|
+
setTreeData(null);
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1639
1663
|
const query = encodeURIComponent(rootPath);
|
|
1640
1664
|
const res = await fetch(`/api/preview/workspace-tree?path=${query}`);
|
|
1641
1665
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
@@ -1652,11 +1676,11 @@
|
|
|
1652
1676
|
} finally {
|
|
1653
1677
|
setIsLoading(false);
|
|
1654
1678
|
}
|
|
1655
|
-
}, []);
|
|
1679
|
+
}, [currentWs]);
|
|
1656
1680
|
|
|
1657
1681
|
useEffect(() => {
|
|
1658
|
-
loadTree();
|
|
1659
|
-
}, [loadTree, globalManualWorkspace]);
|
|
1682
|
+
loadTree(false, currentWs);
|
|
1683
|
+
}, [loadTree, currentWs, globalManualWorkspace]);
|
|
1660
1684
|
|
|
1661
1685
|
const handleToggleExpand = (dirPath) => {
|
|
1662
1686
|
setExpandedPaths(prev => {
|
|
@@ -3134,10 +3158,12 @@
|
|
|
3134
3158
|
}, [normalizedContent, category, isHighlighted, searchQuery]);
|
|
3135
3159
|
|
|
3136
3160
|
const lineCount = highlightedLines.length;
|
|
3137
|
-
const digitCount = Math.max(
|
|
3138
|
-
const gutterWidth = Math.max(24, digitCount * 7.5 + 12);
|
|
3161
|
+
const digitCount = Math.max(1, String(lineCount).length);
|
|
3139
3162
|
|
|
3140
|
-
return h('div', {
|
|
3163
|
+
return h('div', {
|
|
3164
|
+
className: `dsh-code-editor-view ${isWordWrap ? 'word-wrap' : ''}`,
|
|
3165
|
+
style: { '--dsh-gutter-digits': digitCount }
|
|
3166
|
+
},
|
|
3141
3167
|
highlightedLines.map((lineHtml, i) => {
|
|
3142
3168
|
const lineNum = i + 1;
|
|
3143
3169
|
return h('div', {
|
|
@@ -3148,7 +3174,6 @@
|
|
|
3148
3174
|
h('span', {
|
|
3149
3175
|
id: `line-gutter-${lineNum}`,
|
|
3150
3176
|
className: 'dsh-line-gutter',
|
|
3151
|
-
style: { minWidth: `${gutterWidth}px` },
|
|
3152
3177
|
onClick: () => onLineClick && onLineClick(lineNum),
|
|
3153
3178
|
title: `第 ${lineNum} 行 (点击定位)`,
|
|
3154
3179
|
}, lineNum),
|
|
@@ -3951,6 +3976,37 @@
|
|
|
3951
3976
|
}
|
|
3952
3977
|
}, true); // Use capture phase to intercept early!
|
|
3953
3978
|
}
|
|
3979
|
+
|
|
3980
|
+
// 3. Listen to DSH Workspaces and Sessions state changes for auto-switching
|
|
3981
|
+
let lastKnownWorkspace = getCurrentWorkspaceRoot();
|
|
3982
|
+
|
|
3983
|
+
const handleWorkspaceSync = () => {
|
|
3984
|
+
// Clear manual override when user switches workspace/session in host DSH
|
|
3985
|
+
globalManualWorkspace = null;
|
|
3986
|
+
const current = getCurrentWorkspaceRoot();
|
|
3987
|
+
if (current && current !== lastKnownWorkspace) {
|
|
3988
|
+
console.log('[Preview] Active workspace auto-switched:', lastKnownWorkspace, '->', current);
|
|
3989
|
+
lastKnownWorkspace = current;
|
|
3990
|
+
notifyStateChange();
|
|
3991
|
+
}
|
|
3992
|
+
};
|
|
3993
|
+
|
|
3994
|
+
if (ctx && ctx.workspaces && ctx.workspaces.list && typeof ctx.workspaces.list.subscribe === 'function') {
|
|
3995
|
+
try {
|
|
3996
|
+
ctx.workspaces.list.subscribe(handleWorkspaceSync);
|
|
3997
|
+
} catch (e) {}
|
|
3998
|
+
}
|
|
3999
|
+
|
|
4000
|
+
if (ctx && ctx.sessions && ctx.sessions.list && typeof ctx.sessions.list.subscribe === 'function') {
|
|
4001
|
+
try {
|
|
4002
|
+
ctx.sessions.list.subscribe(handleWorkspaceSync);
|
|
4003
|
+
} catch (e) {}
|
|
4004
|
+
}
|
|
4005
|
+
|
|
4006
|
+
if (typeof window !== 'undefined') {
|
|
4007
|
+
window.addEventListener('hashchange', handleWorkspaceSync);
|
|
4008
|
+
window.addEventListener('popstate', handleWorkspaceSync);
|
|
4009
|
+
}
|
|
3954
4010
|
}
|
|
3955
4011
|
|
|
3956
4012
|
// =========================================================================
|
|
@@ -5906,10 +5962,13 @@
|
|
|
5906
5962
|
.dsh-line-gutter {
|
|
5907
5963
|
display: inline-block;
|
|
5908
5964
|
box-sizing: border-box;
|
|
5909
|
-
|
|
5965
|
+
width: calc(var(--dsh-gutter-digits, 2) * 1ch + 8px);
|
|
5966
|
+
min-width: calc(var(--dsh-gutter-digits, 2) * 1ch + 8px);
|
|
5967
|
+
padding: 0 4px 0 2px;
|
|
5910
5968
|
text-align: right;
|
|
5911
5969
|
user-select: none;
|
|
5912
5970
|
color: var(--dsw-alias-label-tertiary, #94a3b8);
|
|
5971
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Cascadia Code", "Fira Code", monospace;
|
|
5913
5972
|
font-size: 11px;
|
|
5914
5973
|
line-height: 20px;
|
|
5915
5974
|
flex-shrink: 0;
|
|
@@ -5932,7 +5991,7 @@
|
|
|
5932
5991
|
.dsh-line-code {
|
|
5933
5992
|
flex: 1;
|
|
5934
5993
|
min-width: 0;
|
|
5935
|
-
padding: 0
|
|
5994
|
+
padding: 0 8px 0 6px;
|
|
5936
5995
|
white-space: pre;
|
|
5937
5996
|
font-family: inherit;
|
|
5938
5997
|
font-size: inherit;
|
package/lib/index.js
CHANGED
|
@@ -29,7 +29,7 @@ export class PreviewService extends Service {
|
|
|
29
29
|
handler: this.handleHttpRequest.bind(this),
|
|
30
30
|
});
|
|
31
31
|
});
|
|
32
|
-
console.log('[PreviewService] Registered /api/preview route on webServer (v0.1.
|
|
32
|
+
console.log('[PreviewService] Registered /api/preview route on webServer (v0.1.15)');
|
|
33
33
|
} else {
|
|
34
34
|
console.warn('[PreviewService] webServer not available yet in ctx');
|
|
35
35
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rooode/dsh-plugin-preview",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "DeepSeek Harness Markdown 文档与工作空间文件浏览器右侧预览插件 (支持工作区文件树、文件夹创建与文件移动、JSON 交互结构树/格式化、Java/C++/Python/JS/TS/YAML/Go/Rust 多语言语法高亮与符号大纲、自动换行与多标签 FileTabs)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|