dsh-better-workspace 0.13.0 → 0.14.0

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/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-external/dsh-better-workspace",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "main": "./src/index.js",
5
5
  "description": "侧边栏工作区两层层级树:工作区按磁盘目录嵌套(官方 workspace-tree 语义),名称中的 / 即虚拟分组(web/前端 · web/后端);添加工作区由官方目录流承载。Two-layer hierarchy tree for the DSH sidebar workspace list.",
6
6
  "engines": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-better-workspace",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "侧边栏工作区两层层级树:磁盘目录嵌套(官方 workspace-tree 语义)+ 名称 / 虚拟分组,添加工作区走官方目录流。Two-layer workspace tree for the DSH sidebar: disk-path nesting plus \"/\"-name groups; add-workspace rides the official directory flow.",
5
5
  "type": "module",
6
6
  "repository": {
package/src/client.js CHANGED
@@ -4726,6 +4726,7 @@ window.__ModuleLoader__.load({
4726
4726
  insertWorkspaceBefore, insertSessionBefore,
4727
4727
  useDirectoryFlow,
4728
4728
  pickDirectory, listDirectory, createDirectory,
4729
+ officialT,
4729
4730
  t,
4730
4731
  } = props
4731
4732
 
@@ -5659,30 +5660,42 @@ window.__ModuleLoader__.load({
5659
5660
 
5660
5661
  /* ------------------------- context menu ------------------------ */
5661
5662
 
5662
- const ctxItems = () => {
5663
+ // Context-menu entries mirror the OFFICIAL row menus item-for-item
5664
+ // (ui-workspace Rows.tsx workspaceMenuItems / sessionMenuItems): same
5665
+ // ids, same icons, same danger semantics, and — through the official
5666
+ // 'workspace' dictionary — the same copy, so upstream rewording lands
5667
+ // here without a plugin release. What the official menus cannot
5668
+ // provide is the bw-only surface: name-group / session-group rename
5669
+ // and Customize appearance, appended after a separator.
5670
+ const ot = typeof officialT === 'function' ? officialT : null
5671
+ const menuEntries = () => {
5663
5672
  if (ctx === null) return []
5673
+ const customizeEntry = { id: 'customize', label: t('custom.title'), icon: icon('IconPersonalizationOutline16', 16) }
5664
5674
  if (ctx.kind === 'folder') return [
5665
- { id: 'rename-folder', label: t('menu.renameFolder') },
5666
- { sep: true },
5667
- { id: 'customize', label: t('custom.title') },
5675
+ { id: 'rename-folder', label: t('menu.renameFolder'), icon: icon('IconEditOutline16', 16) },
5676
+ { type: 'separator', id: 'bw-sep' },
5677
+ customizeEntry,
5668
5678
  ]
5669
5679
  if (ctx.kind === 'workspace') return [
5670
- { id: 'rename', label: t('menu.rename') },
5671
- { id: 'delete', label: t('menu.delete'), danger: true },
5672
- { sep: true },
5673
- { id: 'customize', label: t('custom.title') },
5680
+ { id: 'rename', label: ot ? ot('rename') : t('menu.rename'), icon: icon('IconEditOutline16', 16) },
5681
+ { id: 'delete', label: ot ? ot('delete.workspace') : t('menu.delete'), icon: icon('IconTrashOutline16', 16), danger: true },
5682
+ { type: 'separator', id: 'bw-sep' },
5683
+ customizeEntry,
5674
5684
  ]
5675
5685
  if (ctx.kind === 'session') return [
5676
- { id: 'rename', label: t('menu.rename') },
5677
- { id: 'fork', label: t('menu.fork') },
5678
- { id: 'archive', label: t('menu.archive'), danger: true },
5679
- { sep: true },
5680
- { id: 'customize', label: t('custom.title') },
5686
+ { id: 'rename', label: ot ? ot('rename') : t('menu.rename'), icon: icon('IconEditOutline16', 16) },
5687
+ { id: 'fork', label: ot ? ot('menu.fork') : t('menu.fork'), icon: icon('IconBranchOutline16', 16) },
5688
+ // Official semantics (Rows.tsx): archive hides the row through the
5689
+ // registry-global archive set and never touches the session log —
5690
+ // deliberately NOT styled destructive.
5691
+ { id: 'archive', label: ot ? ot('menu.archiveSession') : t('menu.archive'), icon: icon('IconArchiveOutline20', 16) },
5692
+ { type: 'separator', id: 'bw-sep' },
5693
+ customizeEntry,
5681
5694
  ]
5682
5695
  return [
5683
- { id: 'rename-sgroup', label: t('menu.renameSgroup') },
5684
- { sep: true },
5685
- { id: 'customize', label: t('custom.title') },
5696
+ { id: 'rename-sgroup', label: t('menu.renameSgroup'), icon: icon('IconEditOutline16', 16) },
5697
+ { type: 'separator', id: 'bw-sep' },
5698
+ customizeEntry,
5686
5699
  ]
5687
5700
  }
5688
5701
  const handleCtxPick = (id) => {
@@ -5835,7 +5848,20 @@ window.__ModuleLoader__.load({
5835
5848
  t,
5836
5849
  }),
5837
5850
  dialogElement,
5838
- ctx !== null ? E('div', {
5851
+ (ctx !== null && typeof ui.Menu === 'function') ? E(ui.Menu, {
5852
+ // Right-click opens the SAME menu surface the official ⋯ button
5853
+ // renders (primitives Menu, portal, dense): getAnchorRect feeds
5854
+ // the event coordinates as the anchor rect — portal mode positions
5855
+ // from it directly, no in-place wrapper needed.
5856
+ open: true,
5857
+ onClose: () => setCtx(null),
5858
+ items: menuEntries(),
5859
+ onSelect: handleCtxPick,
5860
+ align: 'start',
5861
+ dense: true,
5862
+ portal: true,
5863
+ getAnchorRect: () => new DOMRect(ctx.x, ctx.y, 0, 0),
5864
+ }) : (ctx !== null ? E('div', {
5839
5865
  className: 'bw-ctx-overlay',
5840
5866
  onMouseDown: () => setCtx(null),
5841
5867
  onContextMenu: (e) => e.preventDefault(),
@@ -5846,7 +5872,7 @@ window.__ModuleLoader__.load({
5846
5872
  onMouseDown: (e) => e.stopPropagation(),
5847
5873
  onContextMenu: (e) => e.preventDefault(),
5848
5874
  },
5849
- ctxItems().map((item, index) => item.sep
5875
+ menuEntries().map((item, index) => item.type === 'separator'
5850
5876
  ? E('div', { key: 'sep-' + index, className: 'bw-ctx-sep' })
5851
5877
  : E('button', {
5852
5878
  key: item.id,
@@ -5856,7 +5882,7 @@ window.__ModuleLoader__.load({
5856
5882
  }, item.label),
5857
5883
  ),
5858
5884
  ),
5859
- ) : null,
5885
+ ) : null),
5860
5886
  E(CustomizeDialog, {
5861
5887
  open: customize !== null,
5862
5888
  kind: customize ? customize.kind : undefined,
@@ -5919,6 +5945,14 @@ window.__ModuleLoader__.load({
5919
5945
  // the handle before any flow can open.
5920
5946
  pickerState.api = uiWorkspace
5921
5947
 
5948
+ // The OFFICIAL browser's own dictionary (NS 'workspace'), bound so the
5949
+ // context menu COPY follows upstream wording automatically — the menu
5950
+ // mirrors the official row menus item-for-item (Rows.tsx), and this
5951
+ // way upstream rewording lands here without a plugin release. Falls
5952
+ // back to this plugin's aligned dictionary when bind fails.
5953
+ let officialT = null
5954
+ try { officialT = ctx.locale.bind('workspace') } catch { officialT = null }
5955
+
5922
5956
  if (ctx.locale && typeof ctx.locale.register === 'function') {
5923
5957
  ctx.effect(() => {
5924
5958
  try {
@@ -5955,6 +5989,9 @@ window.__ModuleLoader__.load({
5955
5989
  }
5956
5990
 
5957
5991
  const browserInjected = () => ({
5992
+ // Official 'workspace' dictionary translate (menu copy follows upstream);
5993
+ // null when the bind failed — the menu then uses the aligned bw copy.
5994
+ officialT,
5958
5995
  startSession: (workspaceId) => { uiWorkspace.startSession(workspaceId) },
5959
5996
  open: (sessionId) => { uiWorkspace.openSession(sessionId) },
5960
5997
  searchSessions,