@rooode/dsh-plugin-preview 0.1.17 → 0.1.18

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
@@ -394,75 +394,108 @@
394
394
  return result;
395
395
  }
396
396
 
397
- function getCurrentWorkspaceRoot() {
398
- // Priority 1: User manual override selection (if explicitly chosen by user)
397
+ function resolveActiveWorkspace(ctx) {
399
398
  if (globalManualWorkspace && typeof globalManualWorkspace === 'string' && globalManualWorkspace.trim()) {
400
- return globalManualWorkspace.trim();
399
+ const p = globalManualWorkspace.trim().replace(/[\\/]+$/, '');
400
+ return { path: p, id: 'manual', title: getFileName(p) || p };
401
401
  }
402
-
403
- // Priority 2: Derive active workspace from clientCtx (workspaces + sessions)
404
- if (clientCtx) {
402
+ if (ctx) {
405
403
  try {
406
- const sSnap = clientCtx.sessions?.list?.getSnapshot?.();
407
- const wSnap = clientCtx.workspaces?.list?.getSnapshot?.();
408
- const currentSessionId = sSnap?.current;
409
-
410
- // A. Find workspace in wSnap.items containing currentSessionId
411
- if (currentSessionId && wSnap && Array.isArray(wSnap.items) && wSnap.items.length > 0) {
412
- const match = wSnap.items.find(item => item.sessionIds && Array.isArray(item.sessionIds) && item.sessionIds.includes(currentSessionId));
413
- if (match && match.path && typeof match.path === 'string' && match.path.trim()) {
414
- return match.path.trim();
404
+ const sSnap = ctx.sessions?.list?.getSnapshot?.();
405
+ const wSnap = ctx.workspaces?.list?.getSnapshot?.();
406
+ const curSessionId = sSnap?.current;
407
+
408
+ // 1. Session cwd or workspaceId
409
+ if (curSessionId && sSnap?.byId?.[curSessionId]) {
410
+ const sess = sSnap.byId[curSessionId];
411
+ let wsPath = sess.cwd;
412
+ let wsId = sess.workspaceId;
413
+ let wsTitle = sess.title;
414
+
415
+ if (sess.workspaceId && wSnap?.items) {
416
+ const match = wSnap.items.find(item => item.workspaceId === sess.workspaceId);
417
+ if (match) {
418
+ if (match.path) wsPath = match.path;
419
+ if (match.title) wsTitle = match.title;
420
+ wsId = match.workspaceId;
421
+ }
422
+ }
423
+ if (wsPath && typeof wsPath === 'string' && wsPath.trim()) {
424
+ const clean = wsPath.trim().replace(/[\\/]+$/, '');
425
+ return {
426
+ path: clean,
427
+ id: wsId || clean,
428
+ title: wsTitle || getFileName(clean) || '工作区',
429
+ };
415
430
  }
416
431
  }
417
432
 
418
- // B. Check active session summary cwd or workspaceId
419
- if (currentSessionId && sSnap && sSnap.byId && sSnap.byId[currentSessionId]) {
420
- const sess = sSnap.byId[currentSessionId];
421
- if (sess.cwd && typeof sess.cwd === 'string' && sess.cwd.trim()) {
422
- return sess.cwd.trim();
423
- }
424
- if (sess.workspaceId && wSnap && Array.isArray(wSnap.items)) {
425
- const match = wSnap.items.find(item => item.workspaceId === sess.workspaceId);
426
- if (match && match.path && typeof match.path === 'string' && match.path.trim()) {
427
- return match.path.trim();
428
- }
433
+ // 2. Find in wSnap.items by sessionIds
434
+ if (curSessionId && wSnap?.items) {
435
+ const match = wSnap.items.find(item => Array.isArray(item.sessionIds) && item.sessionIds.includes(curSessionId));
436
+ if (match?.path) {
437
+ const clean = match.path.trim().replace(/[\\/]+$/, '');
438
+ return {
439
+ path: clean,
440
+ id: match.workspaceId || clean,
441
+ title: match.title || getFileName(clean) || '工作区',
442
+ };
429
443
  }
430
444
  }
431
445
 
432
- // C. Check recentWorkspaceId in wSnap
433
- if (wSnap && wSnap.recentWorkspaceId && Array.isArray(wSnap.items)) {
446
+ // 3. Recent workspace
447
+ if (wSnap?.recentWorkspaceId && wSnap?.items) {
434
448
  const recent = wSnap.items.find(item => item.workspaceId === wSnap.recentWorkspaceId);
435
- if (recent && recent.path && typeof recent.path === 'string' && recent.path.trim()) {
436
- return recent.path.trim();
449
+ if (recent?.path) {
450
+ const clean = recent.path.trim().replace(/[\\/]+$/, '');
451
+ return {
452
+ path: clean,
453
+ id: recent.workspaceId || clean,
454
+ title: recent.title || getFileName(clean) || '工作区',
455
+ };
437
456
  }
438
457
  }
439
458
 
440
- // D. Fallback to first workspace in wSnap.items
441
- if (wSnap && Array.isArray(wSnap.items) && wSnap.items.length > 0) {
459
+ // 4. First workspace item
460
+ if (wSnap?.items && wSnap.items.length > 0) {
442
461
  const first = wSnap.items[0];
443
- const dir = first.path || first.directory || first.cwd;
444
- if (dir && typeof dir === 'string' && dir.trim()) {
445
- return dir.trim();
462
+ const p = first.path || first.directory || first.cwd;
463
+ if (p && typeof p === 'string' && p.trim()) {
464
+ const clean = p.trim().replace(/[\\/]+$/, '');
465
+ return {
466
+ path: clean,
467
+ id: first.workspaceId || clean,
468
+ title: first.title || getFileName(clean) || '工作区',
469
+ };
446
470
  }
447
471
  }
448
472
  } catch (e) {
449
- console.warn('[Preview] getCurrentWorkspaceRoot failed:', e);
473
+ console.warn('[Preview] resolveActiveWorkspace error:', e);
450
474
  }
451
475
  }
452
476
 
453
- // Priority 3: URL hash or search parameter
454
477
  if (typeof location !== 'undefined') {
455
478
  if (location.hash) {
456
479
  const m = location.hash.match(/workspace=([^&]+)/);
457
- if (m) return decodeURIComponent(m[1]);
480
+ if (m) {
481
+ const p = decodeURIComponent(m[1]).replace(/[\\/]+$/, '');
482
+ return { path: p, id: p, title: getFileName(p) || p };
483
+ }
458
484
  }
459
485
  if (location.search) {
460
486
  const m = location.search.match(/workspace=([^&]+)/);
461
- if (m) return decodeURIComponent(m[1]);
487
+ if (m) {
488
+ const p = decodeURIComponent(m[1]).replace(/[\\/]+$/, '');
489
+ return { path: p, id: p, title: getFileName(p) || p };
490
+ }
462
491
  }
463
492
  }
464
493
 
465
- return '';
494
+ return { path: '.', id: 'default', title: '工作区' };
495
+ }
496
+
497
+ function getCurrentWorkspaceRoot() {
498
+ return resolveActiveWorkspace(clientCtx).path || '.';
466
499
  }
467
500
 
468
501
  function resolveAbsoluteFilePath(targetPath) {
@@ -1813,11 +1846,11 @@
1813
1846
  }
1814
1847
  }, [searchQuery, treeData]);
1815
1848
 
1816
- // Listen to external workspace switch events (e.g. from Git plugin)
1849
+ // Listen to external workspace switch events (e.g. from DSH sidebar or Git plugin)
1817
1850
  useEffect(() => {
1818
1851
  const handleWsSwitch = (e) => {
1819
- if (e.detail?.path && e.detail.path !== globalManualWorkspace) {
1820
- setManualWorkspace(e.detail.path);
1852
+ if (e.detail?.path) {
1853
+ setCurrentWs(e.detail.path);
1821
1854
  }
1822
1855
  };
1823
1856
  window.addEventListener('dsh:workspace:switch', handleWsSwitch);
@@ -6399,6 +6432,59 @@
6399
6432
  injectStyles();
6400
6433
  setupFileInterceptors(ctx);
6401
6434
 
6435
+ // Subscribe to DSH Session & Workspace store changes (Left Sidebar updates)
6436
+ let lastTrackedSessionId = null;
6437
+ let lastTrackedWsPath = getCurrentWorkspaceRoot();
6438
+
6439
+ function handleDshSessionOrWorkspaceChange() {
6440
+ try {
6441
+ const sSnap = ctx.sessions?.list?.getSnapshot?.();
6442
+ const curSessionId = sSnap?.current;
6443
+
6444
+ // When user clicks a session on the left sidebar:
6445
+ if (curSessionId && curSessionId !== lastTrackedSessionId) {
6446
+ lastTrackedSessionId = curSessionId;
6447
+ // Clear manual override when user actively selects a session in the left sidebar
6448
+ if (globalManualWorkspace) {
6449
+ globalManualWorkspace = null;
6450
+ try { localStorage.removeItem(STORAGE_KEY_MANUAL_WS); } catch (e) {}
6451
+ }
6452
+ }
6453
+
6454
+ const activeWsInfo = resolveActiveWorkspace(ctx);
6455
+ const activeWsPath = activeWsInfo.path;
6456
+
6457
+ if (activeWsPath && activeWsPath !== lastTrackedWsPath) {
6458
+ lastTrackedWsPath = activeWsPath;
6459
+ syncCurrentWorkspaceTabs(activeWsPath);
6460
+ notifyStateChange();
6461
+ if (typeof window !== 'undefined') {
6462
+ window.dispatchEvent(new CustomEvent('dsh:workspace:switch', {
6463
+ detail: { path: activeWsPath, id: activeWsInfo.id, title: activeWsInfo.title }
6464
+ }));
6465
+ }
6466
+ }
6467
+ } catch (e) {
6468
+ console.warn('[Preview] Session/Workspace change handler error:', e);
6469
+ }
6470
+ }
6471
+
6472
+ if (ctx.sessions?.list?.subscribe) {
6473
+ ctx.sessions.list.subscribe(handleDshSessionOrWorkspaceChange);
6474
+ }
6475
+ if (ctx.workspaces?.list?.subscribe) {
6476
+ ctx.workspaces.list.subscribe(handleDshSessionOrWorkspaceChange);
6477
+ }
6478
+
6479
+ // Safety polling every 800ms for route or state changes
6480
+ setInterval(handleDshSessionOrWorkspaceChange, 800);
6481
+
6482
+ // Listen to popstate / hashchange
6483
+ if (typeof window !== 'undefined') {
6484
+ window.addEventListener('popstate', handleDshSessionOrWorkspaceChange);
6485
+ window.addEventListener('hashchange', handleDshSessionOrWorkspaceChange);
6486
+ }
6487
+
6402
6488
  console.log('[Preview] Markdown Preview Client Plugin loaded successfully!');
6403
6489
 
6404
6490
  // 1. Mount Unified Right Activity Rail Host into shell.overlay
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.17)');
32
+ console.log('[PreviewService] Registered /api/preview route on webServer (v0.1.18)');
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.17",
3
+ "version": "0.1.18",
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",