@1yefuwang1/dsh-worktrees 0.2.13 → 0.2.14

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,16 @@
1
1
  # @1yefuwang1/dsh-worktrees changelog
2
2
 
3
+ ## 0.2.14 — Native sidebar view switch
4
+
5
+ - Move the view switch from the footer into native sidebar navigation, between
6
+ Automation tasks and Worktrees, using the native row styling and icon sizing.
7
+ - Label the action by its destination: **Switch to Projects View** or
8
+ **Switch to Workspace View**, with matching Chinese translations.
9
+ - Preserve native sidebar declarations and extensions through owned slot aliases;
10
+ switching views leaves the current conversation and other panel actions intact.
11
+ - Add regression coverage for navigation order, labels, callback routing and
12
+ native sidebar restoration on plugin unload.
13
+
3
14
  ## 0.2.13 — First scoped release preparation
4
15
 
5
16
  - Package as `@1yefuwang1/dsh-worktrees` to avoid an unrelated unscoped npm name;
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @1yefuwang1/dsh-worktrees
2
2
 
3
3
  Multi-folder projects with Local and isolated Git worktree threads for DeepSeek
4
- Harness. This experimental Host + Web Client package is **`0.2.13`**, prepared for
5
- scoped npm release with maintainer review/2FA. It does not require Copilot, the
4
+ Harness. This experimental Host + Web Client package is **`0.2.14`**; new releases
5
+ use staged npm publishing with maintainer review/2FA. It does not require Copilot, the
6
6
  catalog plugin, or the search plugin. The scoped name avoids collision with the
7
7
  unrelated npm package `dsh-worktrees`; stable tool, command and metadata names are
8
8
  unchanged.
@@ -14,6 +14,9 @@ A **thread** belongs to that project and executes in one selected folder: either
14
14
  its Local directory or a managed linked Git checkout. Project membership is not
15
15
  native Workspace membership, a working-directory change or a permission grant.
16
16
 
17
+ - The native sidebar navigation places the view switch between **Automation tasks**
18
+ and **Worktrees**. Its destination label is **Switch to Projects View** or
19
+ **Switch to Workspace View**; switching leaves the current conversation intact.
17
20
  - The compact **Projects** header uses Search, View options and Add project icon
18
21
  controls. Search appears only when opened; Escape/Close clears it and restores
19
22
  focus. Archive filters are in View options, not an always-visible dropdown.
@@ -331,7 +334,7 @@ After the staged release is approved and appears on npm, install the scoped
331
334
  package (the similarly named unscoped package is unrelated):
332
335
 
333
336
  ```sh
334
- dsh plugin --profile <profile> add @1yefuwang1/dsh-worktrees@0.2.13
337
+ dsh plugin --profile <profile> add @1yefuwang1/dsh-worktrees@0.2.14
335
338
  ```
336
339
 
337
340
  For local development or while release approval is pending, build explicitly from
package/client.js CHANGED
@@ -9,8 +9,10 @@ window.__ModuleLoader__.load({
9
9
  const h = React.createElement;
10
10
  const NS = 'worktrees.ui';
11
11
  const PANEL = 'dsh-worktrees';
12
+ const VIEW_SWITCH = PANEL + '-projects-view';
12
13
  const en = {
13
14
  searchProjects: 'Search projects and sessions', closeSearch: 'Close search', viewOptions: 'View options',
15
+ switchProjectsView: 'Switch to Projects View', switchWorkspaceView: 'Switch to Workspace View',
14
16
  'time.now': 'now', 'time.minutes': '{n}min', 'time.hours': '{n}h', 'time.days': '{n}d', 'time.months': '{n}mo', 'time.years': '{n}y', 'date.ymd': '{y}-{m}-{d}', lastActive: 'Last active {time}',
15
17
  projects: 'Projects', folderView: 'Folder view', newProject: 'New project', editProject: 'Manage project', projectName: 'Project name', project: 'Project', folders: 'Folders', folder: 'Folder', addFolder: 'Add folder', chooseDirectory: 'Choose directory', browse: 'Browse folders', absolutePath: 'Absolute Host directory path', save: 'Save', newThread: 'New thread', threadMode: 'Thread mode', threadActions: 'Thread actions', threadDetails: 'Thread details', unassigned: 'Other threads', showMore: 'Show {n} more sessions', showLess: 'Show less', filterThreads: 'Search threads or projects', allThreads: 'All threads', archivedOnly: 'Archived only', hideArchived: 'Hide archived', untitled: 'Untitled', blankThread: 'New thread', collapse: 'Collapse project', expand: 'Expand project', waiting: 'Waiting for interaction', done: 'Finished, not yet viewed', localThread: 'Local thread', worktreeThread: 'Worktree thread', draftWarning: 'This draft contains text or attachments. Keep it in this thread before choosing another project or folder.', chooseFolder: 'Choose a folder', createFolder: 'Create folder', folderName: 'Folder name', chooseThisFolder: 'Use this folder', noProjects: 'Add a project to organize your threads.',
16
18
  repoUnavailable: 'Worktree mode is unavailable or unverified for this folder. Choose Local to continue.',
@@ -45,6 +47,7 @@ window.__ModuleLoader__.load({
45
47
  };
46
48
  const zh = { ...en,
47
49
  searchProjects: '搜索项目和会话', closeSearch: '关闭搜索', viewOptions: '视图选项',
50
+ switchProjectsView: '切换到项目视图', switchWorkspaceView: '切换到工作区视图',
48
51
  'time.now': '刚刚', 'time.minutes': '{n}分钟', 'time.hours': '{n}小时', 'time.days': '{n}天', 'time.months': '{n}个月', 'time.years': '{n}年', 'date.ymd': '{y}年{m}月{d}日', lastActive: '最后活跃于 {time}',
49
52
  projects: '项目', folderView: '文件夹视图', newProject: '新建项目', editProject: '管理项目', projectName: '项目名称', project: '项目', folders: '文件夹', folder: '文件夹', addFolder: '添加文件夹', chooseDirectory: '选择目录', browse: '浏览文件夹', absolutePath: '主机绝对目录路径', save: '保存', newThread: '新建对话', threadMode: '对话模式', threadActions: '对话操作', threadDetails: '对话详情', unassigned: '其他对话', showMore: '展开其余 {n} 个会话', showLess: '收起', filterThreads: '搜索对话或项目', allThreads: '所有对话', archivedOnly: '仅已归档', hideArchived: '隐藏已归档', untitled: '未命名', blankThread: '新对话', collapse: '收起项目', expand: '展开项目', waiting: '等待交互', done: '已完成,尚未查看', localThread: '本地对话', worktreeThread: '工作树对话', draftWarning: '此草稿包含文字或附件。请先保留当前对话,再选择其他项目或文件夹。', chooseFolder: '选择文件夹', createFolder: '创建文件夹', folderName: '文件夹名称', chooseThisFolder: '使用此文件夹', noProjects: '添加项目以组织您的对话。',
50
53
  repoUnavailable: '此文件夹的工作树模式不可用或尚未验证,请选择本地模式继续。',
@@ -851,7 +854,7 @@ window.__ModuleLoader__.load({
851
854
  // Only plugin-owned aliases are declared/mirrored; unloading never clears or
852
855
  // redeclares installed extension seats. Copy their inject/locale/store faces,
853
856
  // not DOM or private components from a Harness module import.
854
- function mirrorProjectSlot(ctx, source, destination = projectSlotName(source)) {
857
+ function mirrorProjectSlot(ctx, source, destination = projectSlotName(source), mapKey = projectSlotName) {
855
858
  return ctx.slots.inject(destination, () => ctx.slots.inject(source, () => {
856
859
  const copies = new Map();
857
860
  const clear = () => { for (const dispose of copies.values()) dispose(); copies.clear(); };
@@ -860,10 +863,10 @@ window.__ModuleLoader__.load({
860
863
  for (const [entry, dispose] of copies) if (!entries.includes(entry)) { dispose(); copies.delete(entry); }
861
864
  for (const entry of entries) {
862
865
  if (copies.has(entry)) continue;
863
- const children = entry.children && Object.fromEntries(Object.entries(entry.children).map(([key, spec]) => [projectSlotName(key), spec]));
864
- const component = children ? props => h(entry.component, { ...props, renderSlot: (key, owner, options) => props.renderSlot(projectSlotName(key), owner, options), ...(props.renderSlotChain ? { renderSlotChain: (key, owner, options) => props.renderSlotChain(projectSlotName(key), owner, options) } : {}) }) : entry.component;
866
+ const children = entry.children && Object.fromEntries(Object.entries(entry.children).map(([key, spec]) => [mapKey(key), spec]));
867
+ const component = children ? props => h(entry.component, { ...props, renderSlot: (key, owner, options) => props.renderSlot(mapKey(key), owner, options), ...(props.renderSlotChain ? { renderSlotChain: (key, owner, options) => props.renderSlotChain(mapKey(key), owner, options) } : {}) }) : entry.component;
865
868
  const remove = ctx.slots.register({ ...entry.options, name: destination, ...(entry.inject ? { inject: entry.inject } : {}), ...(entry.locale ? { locale: entry.locale } : {}), ...(entry.store ? { store: entry.store } : {}), ...(entry.select ? { select: entry.select } : {}), ...(children ? { children } : {}) }, component);
866
- const nested = Object.keys(entry.children || {}).map(key => mirrorProjectSlot(ctx, key));
869
+ const nested = Object.keys(entry.children || {}).map(key => mirrorProjectSlot(ctx, key, mapKey(key), mapKey));
867
870
  copies.set(entry, () => { for (const dispose of nested) dispose(); remove(); });
868
871
  }
869
872
  };
@@ -876,6 +879,31 @@ window.__ModuleLoader__.load({
876
879
  const store = { getSnapshot: () => native, subscribe: listener => { listeners.add(listener); return () => listeners.delete(listener); } };
877
880
  return { store, toggle() { if (disposed) return; if (native) registration = register(); else { registration(); registration = null; } native = !native; for (const listener of [...listeners]) listener(); }, dispose() { disposed = true; registration?.(); listeners.clear(); } };
878
881
  }
882
+ const sidebarViewLabel = (sidebarMode, t) => t(sidebarMode.store.getSnapshot() ? 'switchProjectsView' : 'switchWorkspaceView');
883
+ function registerSidebarViewSwitch(ctx, sidebarMode, component) {
884
+ const t = ctx.locale.bind(NS);
885
+ return ctx.slots.inject('sidebar', () => ctx.slots.inject('sidebar.panellist', () => {
886
+ let removeRow = () => {};
887
+ const synchronize = () => {
888
+ removeRow();
889
+ removeRow = ctx.slots.register({ name: 'sidebar.panellist', id: VIEW_SWITCH, order: 400, label: () => sidebarViewLabel(sidebarMode, t) }, component);
890
+ };
891
+ synchronize();
892
+ const unsubscribe = sidebarMode.store.subscribe(synchronize);
893
+ // Keep native declarations installed. The renderer authorizes child
894
+ // dispatch per entry, so bridge the native component through owned aliases.
895
+ const native = ctx.slots.entriesOfSlot('sidebar')[0], inject = native.inject;
896
+ const mapKey = key => VIEW_SWITCH + '.' + key;
897
+ const children = Object.fromEntries(Object.entries(native.children).map(([key, spec]) => [mapKey(key), spec]));
898
+ const componentWithActions = props => h(native.component, { ...props, renderSlot: (key, owner, options) => props.renderSlot(mapKey(key), owner, options) });
899
+ const removeSidebar = ctx.slots.register({ name: 'sidebar', priority: -50, locale: native.locale, children, inject: (...args) => {
900
+ const props = inject(...args);
901
+ return { ...props, selectPanel(id) { if (id === VIEW_SWITCH) sidebarMode.toggle(); else props.selectPanel(id); } };
902
+ } }, componentWithActions);
903
+ const mirrors = Object.keys(native.children).map(key => mirrorProjectSlot(ctx, key, mapKey(key), mapKey));
904
+ return () => { for (const stop of mirrors) stop(); removeSidebar(); unsubscribe(); removeRow(); };
905
+ }));
906
+ }
879
907
  function syncProjectSpinner(element) {
880
908
  if (element === null) return;
881
909
  for (const animation of element.getAnimations?.({ subtree: true }) ?? []) animation.startTime = 0;
@@ -893,8 +921,8 @@ window.__ModuleLoader__.load({
893
921
  h('g', { className: 'dsh-wt-state-motion' }, h('circle', { className: 'dsh-wt-state-track', cx: 12, cy: 12, r: 9.5 }), h('circle', { className: 'dsh-wt-state-arc', cx: 12, cy: 12, r: 9.5 })));
894
922
  }
895
923
  // Native workspace-folder artwork copied locally; React is the only runtime import.
896
- function WorkspaceFolderIcon({ expanded = false, className }) {
897
- return h('svg', { width: 16, height: 16, className, viewBox: '0 0 16 16', fill: 'none', strokeWidth: 1, 'aria-hidden': true }, expanded ? [
924
+ function WorkspaceFolderIcon({ expanded = false, className, size = 16 }) {
925
+ return h('svg', { width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', strokeWidth: 1, 'aria-hidden': true }, expanded ? [
898
926
  h('path', { key: 'tint', d: 'M2.55912 7.93683C2.67584 7.49906 3.0723 7.19446 3.52536 7.19446H13.6491C14.3061 7.19446 14.7846 7.81725 14.6153 8.45209L13.4411 12.856C13.3244 13.2938 12.9279 13.5984 12.4748 13.5984H2.35113C1.69411 13.5984 1.21562 12.9756 1.38489 12.3407L2.55912 7.93683Z', fill: 'currentColor', opacity: 0.16 }),
899
927
  h('path', { key: 'front', d: 'M13.6491 6.69446C14.6346 6.69453 15.3522 7.62895 15.0983 8.58118L13.9245 12.9845C13.7494 13.6412 13.1539 14.0988 12.4743 14.0988H2.35126C1.36574 14.0988 0.648153 13.1643 0.902044 12.212L2.07587 7.80774C2.25102 7.15128 2.84567 6.69455 3.52509 6.69446H13.6491ZM3.52509 7.69446C3.29865 7.69455 3.10004 7.84674 3.04169 8.06555L1.86786 12.4698C1.78345 12.7872 2.02285 13.0988 2.35126 13.0988H12.4743C12.7007 13.0988 12.8992 12.9463 12.9577 12.7277L14.1325 8.32336C14.2171 8.00598 13.9776 7.69453 13.6491 7.69446H3.52509Z', fill: 'currentColor' }),
900
928
  h('path', { key: 'back', d: 'M4.7666 1.90137C5.13227 1.90144 5.48571 2.03525 5.75977 2.27734L7.27246 3.61328C7.36379 3.69382 7.48174 3.73828 7.60352 3.73828H12.3994C13.2276 3.73841 13.8993 4.41005 13.8994 5.23828V6.7168C13.8183 6.70327 13.735 6.69436 13.6494 6.69434H12.8994V5.23828C12.8993 4.96233 12.6754 4.73841 12.3994 4.73828H7.60352C7.23781 4.73828 6.88446 4.60438 6.61035 4.3623L5.09766 3.02637C5.00636 2.94576 4.88838 2.90144 4.7666 2.90137H2.0498C1.77366 2.90137 1.5498 3.12523 1.5498 3.40137V9.78223L0.902344 12.2119C0.648452 13.1642 1.36604 14.0986 2.35156 14.0986H2.0498C1.2214 14.0986 0.549838 13.427 0.549805 12.5986V3.40137C0.549805 2.57294 1.22138 1.90137 2.0498 1.90137H4.7666Z', fill: 'currentColor' }),
@@ -1064,9 +1092,8 @@ window.__ModuleLoader__.load({
1064
1092
  (searching || !collapsed) && h('div', { role: 'group' }, ...rows.map(row => h(ThreadRow, { key: row.id, row, selected: row.id === selectedId, runtime, metadata, renderSlot, t, now })),
1065
1093
  !searching && hasOverflow && h(Button, { className: 'dsh-wt-session-overflow', 'aria-expanded': remaining === 0, onClick: () => setLimit(value => remaining === 0 ? 5 : remaining <= 5 ? Infinity : value + 5) }, remaining ? t('showMore', { n: remaining }) : t('showLess'))));
1066
1094
  }
1067
- function SidebarToggle({ sidebarMode, wide, t }) {
1068
- const native = React.useSyncExternalStore(sidebarMode.store.subscribe, sidebarMode.store.getSnapshot);
1069
- return h('span', { className: 'dsh-wt' }, h(Styles), h(Button, { title: t(native ? 'projects' : 'folderView'), 'aria-label': t(native ? 'projects' : 'folderView'), 'aria-pressed': !native, onClick: sidebarMode.toggle }, h(WorkspaceFolderIcon), wide && ' ' + t(native ? 'projects' : 'folderView')));
1095
+ function SidebarToggle({ size }) {
1096
+ return h(WorkspaceFolderIcon, { size });
1070
1097
  }
1071
1098
  function ProjectSidebarToolbar({ query, onQuery, archived, onArchived, onCreate, newProjectTrigger, t }) {
1072
1099
  const [searchOpen, setSearchOpen] = React.useState(!!query), [optionsOpen, setOptionsOpen] = React.useState(false);
@@ -1385,7 +1412,7 @@ window.__ModuleLoader__.load({
1385
1412
  sidebarMode = createSidebarMode(() => seat('sidebar.workspaces', { name: 'sidebar.workspaces', priority: -50, children: projectChildren(ctx) }, ProjectsSidebar));
1386
1413
  ctx.effect(() => () => sidebarMode.dispose());
1387
1414
  for (const source of projectSlots) mirrorProjectSlot(ctx, source);
1388
- seat('sidebar.footer.action', { name: 'sidebar.footer.action', id: PANEL + '-projects-view', order: 450 }, SidebarToggle);
1415
+ registerSidebarViewSwitch(ctx, sidebarMode, SidebarToggle);
1389
1416
  seat('conversation.input.left', { name: 'conversation.input.left', id: PANEL, order: 15 }, NewWorktreeControls);
1390
1417
  seat('conversation.input.dock', { name: 'conversation.input.dock', id: PANEL + '-setup', order: 5 }, SetupProgress);
1391
1418
  seat('plugins.row.config', { name: 'plugins.row.config', key: '@1yefuwang1/dsh-worktrees#git-worktrees' }, RootFolderSettings);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1yefuwang1/dsh-worktrees",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "description": "Multi-folder projects with Local and isolated Git worktree threads, freshly fetched remote branches and guarded local handoff.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",