@anionex/dsh-pinned-sessions 0.1.1 → 0.1.2

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 ADDED
@@ -0,0 +1,41 @@
1
+ # Changelog
2
+
3
+ ## 0.1.2 — 2026-10-01
4
+
5
+ - Use DSH 0.2's official session-menu slot and workspace navigation service. Keep the legacy menu bridge for older hosts, and bundle icons whose old host export names were removed.
6
+ - 使用 DSH 0.2 的官方会话菜单 slot 和工作区导航服务;保留旧版菜单桥接,并打包已被宿主移除旧名称的图标。
7
+ - The plugin's top section remains independent of the new native workspace pin action. Menu labels explicitly name the top section.
8
+ - 插件顶部分组与新版宿主工作区内置置顶分别保留;菜单文字明确指向顶部分组。
9
+ - Verified DSH `0.2.0-rc.2` on Web and the official Electron Desktop: clean tarball installation, pin, restart persistence, navigation from the pinned section, and unpin.
10
+ - 已在 DSH `0.2.0-rc.2` Web 及官方 Electron Desktop 实测压缩包安装、置顶、重启持久化、置顶会话导航和取消置顶。
11
+
12
+ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) while its public API remains pre-1.0.
13
+
14
+ ## [Unreleased]
15
+
16
+ ## [0.1.1] - 2026-09-04
17
+
18
+ ### Added
19
+
20
+ - A hover/focus ellipsis menu on every pinned Session row.
21
+ - Native Rename, Fork, Unpin, and Archive actions on Web.
22
+ - Optional Delete and action-failure Toasts when the Desktop Archive Manager capability is active.
23
+ - Keyboard menu navigation and focus handoff after a pinned row disappears.
24
+ - English and Chinese product visuals, setup guidance, compatibility notes, and project policies.
25
+
26
+ ### Changed
27
+
28
+ - Replaced the direct unpin control with the same Menu, Modal, Button, and icon primitives used by DSH.
29
+ - Made Desktop delete availability react to service activation and disposal.
30
+
31
+ ## [0.1.0] - 2026-09-04
32
+
33
+ ### Added
34
+
35
+ - Initial Web and Desktop profile bundle.
36
+ - Exact Session ID capture from native menus.
37
+ - Local pinned-session storage and a compact Workspace-header section.
38
+
39
+ [Unreleased]: https://github.com/Anionex/dsh-pinned-sessions/compare/v0.1.1...HEAD
40
+ [0.1.1]: https://github.com/Anionex/dsh-pinned-sessions/compare/v0.1.0...v0.1.1
41
+ [0.1.0]: https://github.com/Anionex/dsh-pinned-sessions/releases/tag/v0.1.0
package/README.md CHANGED
@@ -25,7 +25,7 @@ DeepSeek Harness keeps Sessions inside their original Workspaces. Once the list
25
25
  <img src="assets/pinned-session-menu.png" alt="A pinned Session at the top of the Workspace sidebar with Rename, Fork, Unpin, and Archive actions open" width="465">
26
26
  </p>
27
27
 
28
- Hover a pinned row to reveal its ellipsis menu. The Web profile shows **Rename**, **Fork session**, **Unpin session**, and **Archive session**. The Desktop profile also shows **Delete session** when Archive Manager provides the same action on native rows.
28
+ Hover a pinned row to reveal its ellipsis menu. The Web profile shows **Rename**, **Fork session**, **Unpin from top section**, and **Archive session**. The Desktop profile also shows **Delete session** when Archive Manager provides the same action on native rows.
29
29
 
30
30
  ## Highlights
31
31
 
@@ -45,7 +45,7 @@ dsh plugin add @anionex/dsh-pinned-sessions --profile web
45
45
  dsh plugin add @anionex/dsh-pinned-sessions --profile desktop
46
46
  ```
47
47
 
48
- Refresh Web or restart Desktop. Open any native Session ellipsis menu and choose **Pin session**. The pinned copy appears below the Workspace header; use **Unpin session** from either menu to remove it.
48
+ Refresh Web or restart Desktop. Open any native Session ellipsis menu and choose **Pin to top section**. The pinned copy appears below the Workspace header; use **Unpin from top section** from either menu to remove it.
49
49
 
50
50
  ### Requirements
51
51
 
@@ -60,7 +60,7 @@ Refresh Web or restart Desktop. Open any native Session ellipsis menu and choose
60
60
  | Web | Rename, Fork, Unpin, Archive | Official DSH primitives and Session/Workspace services |
61
61
  | Desktop | Rename, Fork, Unpin, Archive, optional Delete | Delete and failure Toasts appear only while Archive Manager exposes its Session deletion capability |
62
62
 
63
- The package declares the same pre-`0.2.0` DSH client range that it tests. It uses stable slot and ARIA anchors instead of generated CSS module names.
63
+ Existing release ranges are preserved. Version 0.1.2 adds DSH `0.2.0-rc.2` installation support and uses its official session-menu slot and workspace navigation service. The plugin's top section remains separate from native workspace pinning. Web and official Electron Desktop installation and interactions have been verified; see [CHANGELOG](CHANGELOG.md). Legacy hosts retain the stable slot/ARIA bridge.
64
64
 
65
65
  ## How It Works
66
66
 
@@ -77,7 +77,7 @@ The store keeps at most 500 unique IDs and prunes Sessions that no longer exist
77
77
  - The plugin stores only Session IDs and `pinnedAt` timestamps in `localStorage`.
78
78
  - Pinned rows duplicate native rows by design; they do not move or remove the originals.
79
79
  - Delete stays hidden unless the Desktop deletion provider is active.
80
- - DSH client APIs are still pre-`0.2.0`; install a compatible plugin release when DSH changes that contract.
80
+ - DSH client APIs change across prereleases; use the exact tested release verdicts in the package compatibility metadata.
81
81
 
82
82
  ## Development
83
83
 
package/README.zh.md CHANGED
@@ -25,7 +25,7 @@ DeepSeek Harness 会把 Session 保留在原工作区中。会话增多后,返
25
25
  <img src="assets/pinned-session-menu.png" alt="工作区侧栏顶部的一条置顶会话,已打开重命名、分叉、取消置顶和归档操作" width="465">
26
26
  </p>
27
27
 
28
- 悬浮置顶行即可显示三点菜单。Web profile 提供**重命名**、**分叉会话**、**取消置顶**和**归档会话**。Desktop profile 在 Archive Manager 为原生会话提供删除能力时,还会显示**删除会话**。
28
+ 悬浮置顶行即可显示三点菜单。Web profile 提供**重命名**、**分叉会话**、**从顶部分组取消置顶**和**归档会话**。Desktop profile 在 Archive Manager 为原生会话提供删除能力时,还会显示**删除会话**。
29
29
 
30
30
  ## 主要能力
31
31
 
@@ -45,7 +45,7 @@ dsh plugin add @anionex/dsh-pinned-sessions --profile web
45
45
  dsh plugin add @anionex/dsh-pinned-sessions --profile desktop
46
46
  ```
47
47
 
48
- 刷新 Web 或重启 Desktop。打开任意原生 Session 的三点菜单,选择**置顶会话**。置顶副本会出现在工作区标题下;从任意一侧菜单选择**取消置顶**即可移除。
48
+ 刷新 Web 或重启 Desktop。打开任意原生 Session 的三点菜单,选择**置顶到顶部分组**。置顶副本会出现在工作区标题下;从任意一侧菜单选择**从顶部分组取消置顶**即可移除。
49
49
 
50
50
  ### 环境要求
51
51
 
@@ -60,7 +60,7 @@ dsh plugin add @anionex/dsh-pinned-sessions --profile desktop
60
60
  | Web | 重命名、分叉、取消置顶、归档 | 使用 DSH 官方组件和 Session/Workspace 服务 |
61
61
  | Desktop | 重命名、分叉、取消置顶、归档、可选删除 | 仅在 Archive Manager 暴露删除能力时显示删除和失败 Toast |
62
62
 
63
- 包声明并测试了同一段 `0.2.0` 之前的 DSH 客户端版本范围。DOM 接入依赖稳定的 slot 和 ARIA 标记,不依赖构建生成的 CSS Module 类名。
63
+ 保留既有版本范围。0.1.2 版 增加 DSH `0.2.0-rc.2` 安装支持,使用其官方会话菜单 slot 和工作区导航服务。插件顶部分组与宿主工作区内置置顶分别保留。已通过 Web 及官方 Electron Desktop 安装与交互验收,见 [CHANGELOG](CHANGELOG.md)。旧版宿主继续使用稳定的 slot/ARIA 桥接。
64
64
 
65
65
  ## 工作原理
66
66
 
@@ -77,7 +77,7 @@ dsh plugin add @anionex/dsh-pinned-sessions --profile desktop
77
77
  - 插件只在 `localStorage` 中保存 Session ID 和 `pinnedAt` 时间戳。
78
78
  - 置顶行是原生行的快捷副本,不会移动或删除原位置。
79
79
  - Desktop 删除能力未加载时,删除菜单项保持隐藏。
80
- - DSH 客户端 API 仍处于 `0.2.0` 之前;DSH 更改接口后,需要安装与其匹配的插件版本。
80
+ - DSH 客户端 API 会随预发布版本变化;请以包兼容元数据中明确实测的版本结论为准。
81
81
 
82
82
  ## 开发
83
83
 
package/lib/client.js CHANGED
@@ -101,6 +101,40 @@ var createLucideIcon = (iconName, iconNode) => {
101
101
  return Component;
102
102
  };
103
103
 
104
+ // node_modules/lucide-react/dist/esm/icons/archive.js
105
+ var Archive = createLucideIcon("Archive", [
106
+ ["rect", { width: "20", height: "5", x: "2", y: "3", rx: "1", key: "1wp1u1" }],
107
+ ["path", { d: "M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8", key: "1s80jp" }],
108
+ ["path", { d: "M10 12h4", key: "a56b0p" }]
109
+ ]);
110
+
111
+ // node_modules/lucide-react/dist/esm/icons/ellipsis.js
112
+ var Ellipsis = createLucideIcon("Ellipsis", [
113
+ ["circle", { cx: "12", cy: "12", r: "1", key: "41hilf" }],
114
+ ["circle", { cx: "19", cy: "12", r: "1", key: "1wjl8i" }],
115
+ ["circle", { cx: "5", cy: "12", r: "1", key: "1pcz8c" }]
116
+ ]);
117
+
118
+ // node_modules/lucide-react/dist/esm/icons/git-branch.js
119
+ var GitBranch = createLucideIcon("GitBranch", [
120
+ ["line", { x1: "6", x2: "6", y1: "3", y2: "15", key: "17qcm7" }],
121
+ ["circle", { cx: "18", cy: "6", r: "3", key: "1h7g24" }],
122
+ ["circle", { cx: "6", cy: "18", r: "3", key: "fqmcym" }],
123
+ ["path", { d: "M18 9a9 9 0 0 1-9 9", key: "n2h4wq" }]
124
+ ]);
125
+
126
+ // node_modules/lucide-react/dist/esm/icons/pencil.js
127
+ var Pencil = createLucideIcon("Pencil", [
128
+ [
129
+ "path",
130
+ {
131
+ d: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",
132
+ key: "1a8usu"
133
+ }
134
+ ],
135
+ ["path", { d: "m15 5 4 4", key: "1mk7zo" }]
136
+ ]);
137
+
104
138
  // node_modules/lucide-react/dist/esm/icons/pin-off.js
105
139
  var PinOff = createLucideIcon("PinOff", [
106
140
  ["path", { d: "M12 17v5", key: "bb1du9" }],
@@ -127,6 +161,15 @@ var Pin = createLucideIcon("Pin", [
127
161
  ]
128
162
  ]);
129
163
 
164
+ // node_modules/lucide-react/dist/esm/icons/trash-2.js
165
+ var Trash2 = createLucideIcon("Trash2", [
166
+ ["path", { d: "M3 6h18", key: "d0wm0j" }],
167
+ ["path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6", key: "4alrt4" }],
168
+ ["path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2", key: "v07s0e" }],
169
+ ["line", { x1: "10", x2: "10", y1: "11", y2: "17", key: "1uufr5" }],
170
+ ["line", { x1: "14", x2: "14", y1: "11", y2: "17", key: "xtxkd" }]
171
+ ]);
172
+
130
173
  // src/client/index.tsx
131
174
  var import_react_dom = require("react-dom");
132
175
 
@@ -460,8 +503,8 @@ var MENU_ASSOCIATION_MS = 2e3;
460
503
  var zh = {
461
504
  "menu.rename": "\u91CD\u547D\u540D",
462
505
  "menu.fork": "\u5206\u53C9\u4F1A\u8BDD",
463
- "menu.pin": "\u7F6E\u9876\u4F1A\u8BDD",
464
- "menu.unpin": "\u53D6\u6D88\u7F6E\u9876",
506
+ "menu.pin": "\u7F6E\u9876\u5230\u9876\u90E8\u5206\u7EC4",
507
+ "menu.unpin": "\u4ECE\u9876\u90E8\u5206\u7EC4\u53D6\u6D88\u7F6E\u9876",
465
508
  "menu.archive": "\u5F52\u6863\u4F1A\u8BDD",
466
509
  "menu.delete": "\u5220\u9664\u4F1A\u8BDD",
467
510
  "section.label": "\u7F6E\u9876\u4F1A\u8BDD",
@@ -483,8 +526,8 @@ var zh = {
483
526
  var en = {
484
527
  "menu.rename": "Rename",
485
528
  "menu.fork": "Fork session",
486
- "menu.pin": "Pin session",
487
- "menu.unpin": "Unpin session",
529
+ "menu.pin": "Pin to top section",
530
+ "menu.unpin": "Unpin from top section",
488
531
  "menu.archive": "Archive session",
489
532
  "menu.delete": "Delete session",
490
533
  "section.label": "Pinned sessions",
@@ -572,6 +615,44 @@ function apply(ctx) {
572
615
  const store = new PinStore(storage);
573
616
  const deleteAvailability = new BooleanStore();
574
617
  const actions = makePinnedSessionActions(ctx, deleteAvailability);
618
+ const modernMenus = new BooleanStore();
619
+ let openSession = (sessionId) => {
620
+ ctx.sessions.open(sessionId);
621
+ };
622
+ const navigationActions = {
623
+ ...actions,
624
+ openSession: (id) => {
625
+ openSession(id);
626
+ },
627
+ forkSession: async (id) => {
628
+ openSession(await ctx.sessions.fork({ sessionId: id, increaseTitle: true }));
629
+ }
630
+ };
631
+ ctx.inject(["uiWorkspace"], (navigation) => {
632
+ const service = navigation.get("uiWorkspace");
633
+ openSession = (id) => {
634
+ service.openSession(id);
635
+ };
636
+ navigation.effect(() => () => {
637
+ openSession = (id) => {
638
+ ctx.sessions.open(id);
639
+ };
640
+ });
641
+ });
642
+ ctx.slots.inject("sidebar.workspaces.session.menu.item", () => {
643
+ modernMenus.set(true);
644
+ const dispose = ctx.slots.register({
645
+ name: "sidebar.workspaces.session.menu.item",
646
+ id: "pinned-sessions",
647
+ order: 90,
648
+ locale: NS,
649
+ inject: () => ({ store })
650
+ }, PinTopSectionMenuItem);
651
+ return () => {
652
+ dispose();
653
+ modernMenus.set(false);
654
+ };
655
+ });
575
656
  ctx.inject(["remote.workspaceRegistry"], (serviceCtx) => {
576
657
  const available = deleteSessionService(serviceCtx) !== null;
577
658
  deleteAvailability.set(available);
@@ -606,10 +687,19 @@ function apply(ctx) {
606
687
  id: "pinned-sessions-bridge",
607
688
  order: 40,
608
689
  locale: NS,
609
- inject: () => ({ store, sessions: ctx.sessions, actions })
690
+ inject: () => ({ store, sessions: ctx.sessions, actions: navigationActions, modernMenus })
610
691
  }, PinnedSessionsBridge));
611
692
  }
612
- function PinnedSessionsBridge({ store, sessions, actions, useSessions, useWorkspaces, t }) {
693
+ function PinTopSectionMenuItem({ sessionId, store, useMenuOpenState, t }) {
694
+ (0, import_react3.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
695
+ const [, setMenuOpen] = useMenuOpenState();
696
+ const pinned = store.isPinned(sessionId);
697
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.MenuItemButton, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PinIcon, {}), onSelect: () => {
698
+ store.toggle(sessionId);
699
+ setMenuOpen(false);
700
+ }, children: t(pinned ? "menu.unpin" : "menu.pin") });
701
+ }
702
+ function PinnedSessionsBridge({ store, sessions, actions, useSessions, useWorkspaces, t, modernMenus }) {
613
703
  const sessionSnapshot = useSessions((snapshot) => snapshot);
614
704
  const workspaceSnapshot = useWorkspaces((snapshot) => snapshot);
615
705
  const pins = (0, import_react3.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
@@ -706,6 +796,7 @@ function PinnedSessionsBridge({ store, sessions, actions, useSessions, useWorksp
706
796
  }
707
797
  const onClickCapture = (event) => {
708
798
  pending.current = null;
799
+ if (modernMenus?.getSnapshot()) return;
709
800
  const target = findSessionActionTarget(event.target);
710
801
  if (target === null) return;
711
802
  const sessionId = captureSessionId(target.row, sessions);
@@ -749,7 +840,7 @@ function PinnedSessionsBridge({ store, sessions, actions, useSessions, useWorksp
749
840
  disposeActiveMenu();
750
841
  removeBridgeArtifacts(document);
751
842
  };
752
- }, [sessions, store, t]);
843
+ }, [sessions, store, t, modernMenus]);
753
844
  (0, import_react3.useLayoutEffect)(() => {
754
845
  if (sidebarHost === null) return;
755
846
  const root = sidebarHost.parentElement;
@@ -803,7 +894,8 @@ function PinnedSessionsBridge({ store, sessions, actions, useSessions, useWorksp
803
894
  currentId: sessionSnapshot.current,
804
895
  rows,
805
896
  open: (sessionId) => {
806
- sessions.open(sessionId);
897
+ if (actions.openSession) actions.openSession(sessionId);
898
+ else sessions.open(sessionId);
807
899
  },
808
900
  prepareRemoval: (sessionId, index) => {
809
901
  focusAfterRemoval.current = { sessionId, index };
@@ -936,11 +1028,11 @@ function PinnedSessionRow({ actions, cancelRemoval, current, index, open, prepar
936
1028
  };
937
1029
  }, [menuOpen]);
938
1030
  const menuItems = [
939
- { id: "rename", label: t("menu.rename"), icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconEditOutline16, {}) },
940
- { id: "fork", label: t("menu.fork"), icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconBranchOutline16, {}) },
1031
+ { id: "rename", label: t("menu.rename"), icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pencil, { size: 16 }) },
1032
+ { id: "fork", label: t("menu.fork"), icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitBranch, { size: 16 }) },
941
1033
  { id: "unpin", label: t("menu.unpin"), icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PinOff, { size: 16, strokeWidth: 1.8, "aria-hidden": "true" }) },
942
- { id: "archive", label: t("menu.archive"), icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconArchiveOutline20, { size: 16 }) },
943
- ...canDelete ? [{ id: "delete", label: t("menu.delete"), icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconTrashOutline16, {}), danger: true }] : []
1034
+ { id: "archive", label: t("menu.archive"), icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Archive, { size: 16 }) },
1035
+ ...canDelete ? [{ id: "delete", label: t("menu.delete"), icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Trash2, { size: 16 }), danger: true }] : []
944
1036
  ];
945
1037
  const closeRename = () => {
946
1038
  if (renaming) return;
@@ -1068,7 +1160,7 @@ function PinnedSessionRow({ actions, cancelRemoval, current, index, open, prepar
1068
1160
  event.preventDefault();
1069
1161
  openActionMenu(event.key === "ArrowDown" ? "first" : "last");
1070
1162
  },
1071
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconEllipsisOutline16, {})
1163
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Ellipsis, { size: 16 })
1072
1164
  }
1073
1165
  )
1074
1166
  }
@@ -1159,8 +1251,13 @@ lucide-react/dist/esm/shared/src/utils.js:
1159
1251
  lucide-react/dist/esm/defaultAttributes.js:
1160
1252
  lucide-react/dist/esm/Icon.js:
1161
1253
  lucide-react/dist/esm/createLucideIcon.js:
1254
+ lucide-react/dist/esm/icons/archive.js:
1255
+ lucide-react/dist/esm/icons/ellipsis.js:
1256
+ lucide-react/dist/esm/icons/git-branch.js:
1257
+ lucide-react/dist/esm/icons/pencil.js:
1162
1258
  lucide-react/dist/esm/icons/pin-off.js:
1163
1259
  lucide-react/dist/esm/icons/pin.js:
1260
+ lucide-react/dist/esm/icons/trash-2.js:
1164
1261
  lucide-react/dist/esm/lucide-react.js:
1165
1262
  (**
1166
1263
  * @license lucide-react v0.468.0 - ISC
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/index.tsx","../node_modules/shared/src/utils.ts","../node_modules/lucide-react/src/defaultAttributes.ts","../node_modules/lucide-react/src/Icon.ts","../node_modules/lucide-react/src/createLucideIcon.ts","../node_modules/lucide-react/src/icons/pin-off.ts","../node_modules/lucide-react/src/icons/pin.ts","../node_modules/lucide/src/defaultAttributes.ts","../node_modules/lucide/src/icons/pin-off.ts","../node_modules/lucide/src/icons/pin.ts","../src/client/dom-bridge.ts","../src/client/pin-store.ts","../src/client/presentation.ts"],"sourcesContent":["import {\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type ReactNode,\n} from 'react'\nimport {\n Button,\n IconArchiveOutline20,\n IconBranchOutline16,\n IconEditOutline16,\n IconEllipsisOutline16,\n IconTrashOutline16,\n Menu,\n Modal,\n Toast,\n type MenuEntry,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport { Pin, PinOff } from 'lucide-react'\nimport { createPortal } from 'react-dom'\nimport {\n attachSessionMenuHost,\n captureSessionId,\n ensurePinnedHost,\n findSessionActionTarget,\n findUnclaimedPortalMenu,\n focusAfterPinnedRemoval,\n listPortalMenus,\n MENU_OWNER_ATTRIBUTE,\n removeBridgeArtifacts,\n SIDEBAR_SLOT_SELECTOR,\n updateSessionMenuItem,\n type SessionMenuTarget,\n type TriggerRect,\n type SessionsForCapture,\n} from './dom-bridge.js'\nimport { PIN_STORAGE_KEY, PinStore } from './pin-store.js'\nimport { sessionDisplayTitle } from './presentation.js'\n\ninterface SessionSummaryLike {\n readonly id: string\n readonly title?: string | undefined\n readonly cwd?: string | undefined\n readonly blank?: boolean | undefined\n readonly running?: boolean | undefined\n readonly completed?: boolean | undefined\n}\n\ninterface SessionsSnapshotLike {\n readonly ids: readonly string[]\n readonly byId: Readonly<Record<string, SessionSummaryLike | undefined>>\n readonly current?: string | undefined\n readonly phase: string\n}\n\ninterface WorkspaceLike {\n readonly workspaceId: string\n readonly title: string\n readonly sessionIds: readonly string[]\n}\n\ninterface WorkspacesSnapshotLike {\n readonly items: readonly WorkspaceLike[]\n readonly archivedSessionIds: readonly string[]\n readonly phase: string\n}\n\ntype SelectorHook<T> = <S>(selector: (snapshot: T) => S, equal?: (left: S, right: S) => boolean) => S\ntype Translate = (key: string, params?: Readonly<Record<string, unknown>>) => string\n\ninterface SlotsLike {\n inject(name: string, install: () => unknown): void\n register<I, P>(\n entry: {\n readonly name: string\n readonly id: string\n readonly order?: number\n readonly locale?: string\n readonly inject?: () => I\n },\n component: (props: P) => ReactNode,\n ): () => void\n}\n\ninterface LocaleLike {\n register(namespace: string, dictionaries: Readonly<Record<string, Readonly<Record<string, string>>>>): () => void\n}\n\ninterface ResultLike {\n readonly ok: boolean\n readonly error?: { readonly message: string } | undefined\n}\n\ninterface ClientSessionsLike extends SessionsForCapture {\n binding(sessionId: string): { readonly session: { rename(title: string): Promise<ResultLike> } } | undefined\n fork(input: { readonly sessionId: string; readonly increaseTitle: boolean }): Promise<string>\n}\n\ninterface WorkspacesServiceLike {\n archiveSession(sessionId: string): Promise<void>\n}\n\ninterface DeleteSessionServiceLike {\n deleteSession(sessionId: string): Promise<ResultLike>\n}\n\ninterface BooleanStoreLike {\n readonly getSnapshot: () => boolean\n readonly subscribe: (listener: () => void) => () => void\n}\n\nclass BooleanStore implements BooleanStoreLike {\n private value = false\n private readonly listeners = new Set<() => void>()\n\n readonly getSnapshot = (): boolean => this.value\n readonly subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n set(value: boolean): void {\n if (value === this.value) return\n this.value = value\n for (const listener of this.listeners) listener()\n }\n}\n\ninterface ClientContextLike {\n readonly slots: SlotsLike\n readonly sessions: ClientSessionsLike\n readonly locale: LocaleLike\n get(name: string): unknown\n inject(dependencies: readonly string[], setup: (ctx: ClientContextLike) => void): unknown\n effect(setup: () => (() => void), label?: string): unknown\n}\n\nexport interface PinnedSessionActions {\n readonly renameSession: (sessionId: string, title: string) => Promise<void>\n readonly forkSession: (sessionId: string) => Promise<void>\n readonly archiveSession: (sessionId: string) => Promise<void>\n readonly deleteAvailability: BooleanStoreLike\n readonly deleteSession: (sessionId: string) => Promise<void>\n}\n\ninterface BridgeProps {\n readonly store: PinStore\n readonly sessions: SessionsForCapture\n readonly actions: PinnedSessionActions\n readonly useSessions: SelectorHook<SessionsSnapshotLike>\n readonly useWorkspaces: SelectorHook<WorkspacesSnapshotLike>\n readonly t: Translate\n}\n\ninterface PinIconProps {\n readonly size?: number\n}\n\ninterface PendingMenuRequest {\n readonly sessionId: string\n readonly createdAt: number\n readonly existingMenus: ReadonlySet<HTMLElement>\n readonly triggerRect: TriggerRect\n}\n\ninterface ActiveMenuBinding {\n readonly target: SessionMenuTarget\n readonly onClick: (event: MouseEvent) => void\n}\n\ninterface PendingFocusRemoval {\n readonly sessionId: string\n readonly index: number\n}\n\nconst NS = 'pinnedSessions'\nconst STYLE_ID = '@anionex/dsh-pinned-sessions'\nconst MENU_ASSOCIATION_MS = 2_000\n\nconst zh = {\n 'menu.rename': '重命名',\n 'menu.fork': '分叉会话',\n 'menu.pin': '置顶会话',\n 'menu.unpin': '取消置顶',\n 'menu.archive': '归档会话',\n 'menu.delete': '删除会话',\n 'section.label': '置顶会话',\n 'session.open': '打开置顶会话“{name}”',\n 'session.actions': '置顶会话“{name}”的操作',\n 'session.archiveFailed': '归档会话失败:{detail}',\n 'session.forkFailed': '分叉会话失败:{detail}',\n 'session.unpin': '取消置顶“{name}”',\n 'dialog.close': '关闭',\n 'dialog.cancel': '取消',\n 'rename.title': '重命名会话',\n 'rename.field': '会话名称',\n 'rename.confirm': '重命名',\n 'delete.title': '删除会话',\n 'delete.description': '将永久删除会话“{name}”及其子代理(含正在运行的)和全部记录(对话内容、统计、缓存),此操作不可恢复。',\n 'delete.pending': '正在删除会话…',\n 'workspace.ungrouped': '未分组',\n}\n\nconst en = {\n 'menu.rename': 'Rename',\n 'menu.fork': 'Fork session',\n 'menu.pin': 'Pin session',\n 'menu.unpin': 'Unpin session',\n 'menu.archive': 'Archive session',\n 'menu.delete': 'Delete session',\n 'section.label': 'Pinned sessions',\n 'session.open': 'Open pinned session “{name}”',\n 'session.actions': 'Actions for pinned session “{name}”',\n 'session.archiveFailed': 'Could not archive session: {detail}',\n 'session.forkFailed': 'Could not fork session: {detail}',\n 'session.unpin': 'Unpin “{name}”',\n 'dialog.close': 'Close',\n 'dialog.cancel': 'Cancel',\n 'rename.title': 'Rename session',\n 'rename.field': 'Session name',\n 'rename.confirm': 'Rename',\n 'delete.title': 'Delete session',\n 'delete.description': 'This permanently deletes session “{name}”, its child agents (including any that are still running), and all of its records (conversation, stats, cache). This cannot be undone.',\n 'delete.pending': 'Deleting session…',\n 'workspace.ungrouped': 'Ungrouped',\n}\n\nconst styles = `\n[${'data-dsh-pinned-sessions-host'}]{box-sizing:border-box;min-width:0;flex:none}\n.dsh-pins-section{box-sizing:border-box;min-width:0;margin:0 4px 6px;padding:0 0 6px;border-bottom:1px solid var(--dsw-alias-border-l1)}\n.dsh-pins-header{display:flex;align-items:center;gap:6px;height:28px;padding:0 8px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;font-weight:500}\n.dsh-pins-header svg{flex:none;color:var(--dsw-alias-state-business-primary)}\n.dsh-pins-list{box-sizing:border-box;max-height:192px;min-width:0;margin:0;padding:0;overflow-y:auto;overscroll-behavior:contain;list-style:none}\n.dsh-pins-row{box-sizing:border-box;display:flex;align-items:center;gap:0;height:32px;min-width:0;border-radius:6px;color:var(--dsw-alias-label-primary)}\n.dsh-pins-row:hover,.dsh-pins-row[data-current=\"true\"],.dsh-pins-row[data-menu-open=\"true\"]{background:var(--dsw-alias-interactive-bg-hover)}\n.dsh-pins-open{box-sizing:border-box;display:flex;align-items:center;gap:6px;min-width:0;height:32px;padding:0 4px 0 8px;border:0;background:transparent;color:inherit;cursor:pointer;flex:1;text-align:left}\n.dsh-pins-open:focus-visible,.dsh-pins-actions:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}\n.dsh-pins-open>svg{flex:none;color:var(--dsw-alias-state-business-primary)}\n.dsh-pins-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;line-height:20px;flex:1}\n.dsh-pins-workspace{max-width:34%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:18px;flex:none}\n.dsh-pins-actions{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;margin-right:3px;padding:0;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;opacity:0;pointer-events:none;flex:none}\n.dsh-pins-row:hover .dsh-pins-actions,.dsh-pins-row:focus-within .dsh-pins-actions,.dsh-pins-row[data-menu-open=\"true\"] .dsh-pins-actions{opacity:1;pointer-events:auto}\n.dsh-pins-actions:hover,.dsh-pins-row[data-menu-open=\"true\"] .dsh-pins-actions{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}\n.dsh-pins-action-menu{display:inline-flex;flex:none}\n.dsh-pins-rename-input{box-sizing:border-box;width:100%;height:36px;padding:0 11px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:0;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit}\n.dsh-pins-rename-input:focus{border-color:var(--dsw-alias-state-business-primary)}\n.dsh-pins-dialog-error{margin-top:8px;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}\n.dsh-pins-dialog-status{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}\n.dsh-pins-delete-action{color:var(--dsw-alias-state-error-primary)!important;border-color:var(--dsw-alias-state-error-primary)!important}\n@media (max-width:767px){.dsh-pins-workspace{display:none}}\n@media (hover:none),(max-width:767px){.dsh-pins-actions{opacity:1;pointer-events:auto}}\n`\n\nexport const inject = ['slots', 'sessions', 'workspaces', 'locale']\n\nfunction deleteSessionService(ctx: ClientContextLike): DeleteSessionServiceLike | null {\n try {\n const service = ctx.get('remote.workspaceRegistry') as Partial<DeleteSessionServiceLike> | undefined\n return typeof service?.deleteSession === 'function' ? service as DeleteSessionServiceLike : null\n } catch {\n return null\n }\n}\n\nfunction makePinnedSessionActions(ctx: ClientContextLike, deleteAvailability: BooleanStoreLike): PinnedSessionActions {\n const workspaces = ctx.get('workspaces') as WorkspacesServiceLike\n return {\n renameSession: async (sessionId, title) => {\n const session = ctx.sessions.binding(sessionId)?.session\n if (session === undefined) throw new Error(`unknown session \"${sessionId}\"`)\n const result = await session.rename(title)\n if (!result.ok) throw new Error(result.error?.message ?? 'Session rename failed')\n },\n forkSession: async sessionId => {\n const childId = await ctx.sessions.fork({ sessionId, increaseTitle: true })\n ctx.sessions.open(childId)\n },\n archiveSession: async sessionId => { await workspaces.archiveSession(sessionId) },\n deleteAvailability,\n deleteSession: async sessionId => {\n const service = deleteSessionService(ctx)\n if (service === null) throw new Error('Session deletion is unavailable')\n const result = await service.deleteSession(sessionId)\n if (!result.ok) throw new Error(result.error?.message ?? 'Session deletion failed')\n },\n }\n}\n\n/** Register the lifecycle bridge in the additive frame overlay slot. */\nexport function apply(ctx: ClientContextLike): void {\n let storage: Storage | undefined\n try {\n storage = window.localStorage\n } catch {\n storage = undefined\n }\n const store = new PinStore(storage)\n const deleteAvailability = new BooleanStore()\n const actions = makePinnedSessionActions(ctx, deleteAvailability)\n\n ctx.inject(['remote.workspaceRegistry'], serviceCtx => {\n const available = deleteSessionService(serviceCtx) !== null\n deleteAvailability.set(available)\n serviceCtx.effect(() => () => { deleteAvailability.set(false) }, 'pinned-sessions: delete capability')\n })\n\n ctx.effect(() => {\n const onStorage = (event: StorageEvent): void => {\n if (event.key === PIN_STORAGE_KEY || event.key === null) store.reload()\n }\n window.addEventListener('storage', onStorage)\n return () => { window.removeEventListener('storage', onStorage) }\n }, 'pinned-sessions: cross-tab state')\n\n ctx.effect(() => {\n if (document.querySelector(`style[data-plugin-css=\"${STYLE_ID}\"]`) !== null) return () => {}\n const tag = document.createElement('style')\n tag.dataset.plugin = '@anionex/dsh-pinned-sessions'\n tag.dataset.pluginCss = STYLE_ID\n tag.textContent = styles\n document.head.appendChild(tag)\n return () => { tag.remove() }\n }, 'pinned-sessions: styles')\n\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'pinned-sessions: dictionaries')\n\n ctx.slots.inject('shell.overlay', () => ctx.slots.register({\n name: 'shell.overlay',\n id: 'pinned-sessions-bridge',\n order: 40,\n locale: NS,\n inject: () => ({ store, sessions: ctx.sessions, actions }),\n }, PinnedSessionsBridge))\n}\n\n/** Keep native behavior intact while mounting the sidebar portal and unmanaged menu item. */\nexport function PinnedSessionsBridge({ store, sessions, actions, useSessions, useWorkspaces, t }: BridgeProps): ReactNode {\n const sessionSnapshot = useSessions(snapshot => snapshot)\n const workspaceSnapshot = useWorkspaces(snapshot => snapshot)\n const pins = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot)\n const [sidebarHost, setSidebarHost] = useState<HTMLDivElement | null>(null)\n const [sidebarWide, setSidebarWide] = useState(true)\n const pending = useRef<PendingMenuRequest | null>(null)\n const activeMenu = useRef<ActiveMenuBinding | null>(null)\n const focusAfterRemoval = useRef<PendingFocusRemoval | null>(null)\n\n useLayoutEffect(() => {\n let active = true\n let queued = false\n let settleTimer: number | undefined\n let observedSidebar: Element | null = null\n const sidebarObserver = new MutationObserver(() => { queueRefresh() })\n\n const disposeActiveMenu = (): void => {\n const binding = activeMenu.current\n if (binding === null) return\n activeMenu.current = null\n binding.target.button.removeEventListener('click', binding.onClick)\n binding.target.host.remove()\n if (binding.target.menu.getAttribute(MENU_OWNER_ATTRIBUTE) === binding.target.sessionId) {\n binding.target.menu.removeAttribute(MENU_OWNER_ATTRIBUTE)\n }\n }\n const updateActiveMenu = (): void => {\n const binding = activeMenu.current\n if (binding === null) return\n const pinned = store.isPinned(binding.target.sessionId)\n updateSessionMenuItem(binding.target, pinned, t(pinned ? 'menu.unpin' : 'menu.pin'))\n }\n const observeSidebar = (): void => {\n const slot = document.querySelector(SIDEBAR_SLOT_SELECTOR)\n if (slot === observedSidebar) return\n sidebarObserver.disconnect()\n observedSidebar = slot\n if (slot !== null) {\n sidebarObserver.observe(slot, {\n attributes: true,\n attributeFilter: ['class', 'style'],\n childList: true,\n subtree: true,\n })\n }\n }\n const refresh = (): void => {\n if (!active) return\n const nextHost = ensurePinnedHost(document)\n setSidebarHost(current => current === nextHost ? current : nextHost)\n observeSidebar()\n const rootWidth = nextHost?.parentElement?.getBoundingClientRect().width\n if (rootWidth !== undefined) setSidebarWide(rootWidth >= 160)\n\n const binding = activeMenu.current\n if (binding !== null && (!binding.target.host.isConnected || !binding.target.menu.isConnected)) {\n disposeActiveMenu()\n }\n const request = pending.current\n if (request === null) return\n if (Date.now() - request.createdAt > MENU_ASSOCIATION_MS) {\n pending.current = null\n return\n }\n const menu = findUnclaimedPortalMenu(document, request.existingMenus, request.triggerRect)\n if (menu === null) return\n const target = attachSessionMenuHost(menu, request.sessionId)\n if (target === null) return\n const onClick = (event: MouseEvent): void => {\n event.preventDefault()\n event.stopPropagation()\n store.toggle(target.sessionId)\n disposeActiveMenu()\n document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))\n }\n target.button.addEventListener('click', onClick)\n activeMenu.current = { target, onClick }\n pending.current = null\n updateActiveMenu()\n }\n function queueRefresh(): void {\n if (!active) return\n if (settleTimer === undefined) {\n settleTimer = window.setTimeout(() => {\n settleTimer = undefined\n refresh()\n }, 250)\n }\n if (queued) return\n queued = true\n queueMicrotask(() => {\n queued = false\n refresh()\n })\n }\n const onClickCapture = (event: MouseEvent): void => {\n pending.current = null\n const target = findSessionActionTarget(event.target)\n if (target === null) return\n const sessionId = captureSessionId(target.row, sessions)\n if (sessionId === null) return\n const rect = target.button.getBoundingClientRect()\n pending.current = {\n sessionId,\n createdAt: Date.now(),\n existingMenus: new Set(listPortalMenus(document)),\n triggerRect: {\n top: rect.top,\n right: rect.right,\n bottom: rect.bottom,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n },\n }\n queueRefresh()\n }\n const onKeyDownCapture = (): void => { pending.current = null }\n\n refresh()\n document.addEventListener('click', onClickCapture, true)\n document.addEventListener('keydown', onKeyDownCapture, true)\n const bodyObserver = new MutationObserver(() => { queueRefresh() })\n bodyObserver.observe(document.body, { childList: true })\n const unsubscribe = store.subscribe(updateActiveMenu)\n return () => {\n active = false\n pending.current = null\n if (settleTimer !== undefined) window.clearTimeout(settleTimer)\n unsubscribe()\n bodyObserver.disconnect()\n sidebarObserver.disconnect()\n document.removeEventListener('click', onClickCapture, true)\n document.removeEventListener('keydown', onKeyDownCapture, true)\n disposeActiveMenu()\n removeBridgeArtifacts(document)\n }\n }, [sessions, store, t])\n\n useLayoutEffect(() => {\n if (sidebarHost === null) return\n const root = sidebarHost.parentElement\n if (root === null) return\n const update = (): void => { setSidebarWide(root.getBoundingClientRect().width >= 160) }\n update()\n if (typeof ResizeObserver === 'undefined') return\n const observer = new ResizeObserver(update)\n observer.observe(root)\n return () => { observer.disconnect() }\n }, [sidebarHost])\n\n useEffect(() => {\n if (sessionSnapshot.phase !== 'ready' || workspaceSnapshot.phase !== 'ready') return\n const valid = new Set(sessionSnapshot.ids)\n const archived = new Set(workspaceSnapshot.archivedSessionIds)\n store.prune(sessionId => valid.has(sessionId) && !archived.has(sessionId))\n }, [sessionSnapshot, store, workspaceSnapshot])\n\n const rows = useMemo(() => {\n const workspaceBySession = new Map<string, string>()\n for (const workspace of workspaceSnapshot.items) {\n for (const sessionId of workspace.sessionIds) {\n if (!workspaceBySession.has(sessionId)) workspaceBySession.set(sessionId, workspace.title)\n }\n }\n return pins.flatMap(pin => {\n const session = sessionSnapshot.byId[pin.id]\n if (session === undefined || session.blank === true) return []\n const workspaceTitle = workspaceBySession.get(pin.id)\n const workspace = workspaceTitle ?? t('workspace.ungrouped')\n return [{ session, title: sessionDisplayTitle(session, workspaceTitle), workspace }]\n })\n }, [pins, sessionSnapshot.byId, t, workspaceSnapshot.items])\n\n useLayoutEffect(() => {\n const pendingRemoval = focusAfterRemoval.current\n if (pendingRemoval === null || rows.some(row => row.session.id === pendingRemoval.sessionId)) return\n focusAfterRemoval.current = null\n focusAfterPinnedRemoval(document, sidebarHost, pendingRemoval.index)\n }, [rows, sidebarHost])\n\n return sidebarHost !== null && sidebarHost.isConnected && sidebarWide && rows.length > 0\n ? createPortal(\n <PinnedSessionSection\n actions={actions}\n cancelRemoval={sessionId => {\n if (focusAfterRemoval.current?.sessionId === sessionId) focusAfterRemoval.current = null\n }}\n currentId={sessionSnapshot.current}\n rows={rows}\n open={sessionId => { sessions.open(sessionId) }}\n prepareRemoval={(sessionId, index) => {\n focusAfterRemoval.current = { sessionId, index }\n }}\n unpin={(sessionId, index) => {\n focusAfterRemoval.current = { sessionId, index }\n store.toggle(sessionId)\n }}\n t={t}\n />,\n sidebarHost,\n )\n : null\n}\n\ninterface PinnedSessionSectionProps {\n readonly actions: PinnedSessionActions\n readonly cancelRemoval: (sessionId: string) => void\n readonly currentId?: string | undefined\n readonly rows: readonly { readonly session: SessionSummaryLike; readonly title: string; readonly workspace: string }[]\n readonly open: (sessionId: string) => void\n readonly prepareRemoval: (sessionId: string, index: number) => void\n readonly unpin: (sessionId: string, index: number) => void\n readonly t: Translate\n}\n\nfunction PinnedSessionSection({ actions, cancelRemoval, currentId, rows, open, prepareRemoval, unpin, t }: PinnedSessionSectionProps): ReactNode {\n return <section className=\"dsh-pins-section\" aria-label={t('section.label')}>\n <div className=\"dsh-pins-header\">\n <PinIcon size={13} />\n <span>{t('section.label')}</span>\n </div>\n <ul className=\"dsh-pins-list\">\n {rows.map(({ session, title, workspace }, index) => <PinnedSessionRow\n actions={actions}\n cancelRemoval={cancelRemoval}\n current={session.id === currentId}\n index={index}\n key={session.id}\n open={open}\n prepareRemoval={prepareRemoval}\n session={session}\n t={t}\n title={title}\n unpin={unpin}\n workspace={workspace}\n />)}\n </ul>\n </section>\n}\n\ninterface PinnedSessionRowProps {\n readonly actions: PinnedSessionActions\n readonly cancelRemoval: (sessionId: string) => void\n readonly current: boolean\n readonly index: number\n readonly open: (sessionId: string) => void\n readonly prepareRemoval: (sessionId: string, index: number) => void\n readonly session: SessionSummaryLike\n readonly t: Translate\n readonly title: string\n readonly unpin: (sessionId: string, index: number) => void\n readonly workspace: string\n}\n\nfunction PinnedSessionRow({ actions, cancelRemoval, current, index, open, prepareRemoval, session, t, title, unpin, workspace }: PinnedSessionRowProps): ReactNode {\n const [menuOpen, setMenuOpen] = useState(false)\n const [renameOpen, setRenameOpen] = useState(false)\n const [renameDraft, setRenameDraft] = useState('')\n const [renaming, setRenaming] = useState(false)\n const [renameError, setRenameError] = useState<string | null>(null)\n const [deleteOpen, setDeleteOpen] = useState(false)\n const [deleting, setDeleting] = useState(false)\n const [deleteError, setDeleteError] = useState<string | null>(null)\n const [actionToast, setActionToast] = useState<{ readonly text: string; readonly seq: number } | null>(null)\n const toastSeq = useRef(0)\n const composing = useRef(false)\n const trigger = useRef<HTMLButtonElement | null>(null)\n const menusBeforeOpen = useRef<ReadonlySet<HTMLElement>>(new Set())\n const focusMenuEdge = useRef<'first' | 'last' | null>(null)\n const actionLabel = t('session.actions', { name: title })\n const renameTrimmed = renameDraft.trim()\n const renameBlocked = renaming || renameTrimmed === ''\n const canDelete = useSyncExternalStore(\n actions.deleteAvailability.subscribe,\n actions.deleteAvailability.getSnapshot,\n actions.deleteAvailability.getSnapshot,\n )\n const showDesktopActionError = (key: string, reason: unknown): void => {\n if (!canDelete) return\n toastSeq.current += 1\n setActionToast({\n text: t(key, { detail: reason instanceof Error ? reason.message : String(reason) }),\n seq: toastSeq.current,\n })\n }\n\n const restoreTriggerFocus = (): void => {\n queueMicrotask(() => { trigger.current?.focus({ preventScroll: true }) })\n }\n const openActionMenu = (focusEdge: 'first' | 'last' | null): void => {\n menusBeforeOpen.current = new Set(document.querySelectorAll<HTMLElement>('[role=\"menu\"]'))\n focusMenuEdge.current = focusEdge\n setMenuOpen(true)\n }\n\n useLayoutEffect(() => {\n if (!menuOpen) return\n let menu: HTMLElement | undefined\n let observer: MutationObserver | undefined\n let disposed = false\n const items = (): HTMLButtonElement[] => menu === undefined\n ? []\n : [...menu.querySelectorAll<HTMLButtonElement>('[role=\"menuitem\"]:not(:disabled)')]\n const onMenuKeyDown = (event: KeyboardEvent): void => {\n if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) return\n const available = items()\n if (available.length === 0) return\n const currentIndex = available.indexOf(document.activeElement as HTMLButtonElement)\n let nextIndex: number\n if (event.key === 'Home') nextIndex = 0\n else if (event.key === 'End') nextIndex = available.length - 1\n else if (event.key === 'ArrowDown') nextIndex = currentIndex < 0 ? 0 : (currentIndex + 1) % available.length\n else nextIndex = currentIndex <= 0 ? available.length - 1 : currentIndex - 1\n event.preventDefault()\n available[nextIndex]?.focus({ preventScroll: true })\n }\n const onDocumentKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') restoreTriggerFocus()\n }\n const install = (): void => {\n if (disposed) return\n menu = [...document.querySelectorAll<HTMLElement>('[role=\"menu\"]')]\n .find(candidate => !menusBeforeOpen.current.has(candidate))\n if (menu === undefined) return\n observer?.disconnect()\n menu.addEventListener('keydown', onMenuKeyDown)\n const edge = focusMenuEdge.current\n focusMenuEdge.current = null\n if (edge === null) return\n const available = items()\n const target = edge === 'first' ? available[0] : available.at(-1)\n queueMicrotask(() => {\n if (!disposed && target?.isConnected === true) target.focus({ preventScroll: true })\n })\n }\n document.addEventListener('keydown', onDocumentKeyDown, true)\n install()\n if (menu === undefined) {\n observer = new MutationObserver(install)\n observer.observe(document.body, { childList: true, subtree: true })\n install()\n }\n return () => {\n disposed = true\n observer?.disconnect()\n menu?.removeEventListener('keydown', onMenuKeyDown)\n document.removeEventListener('keydown', onDocumentKeyDown, true)\n }\n }, [menuOpen])\n\n const menuItems: MenuEntry[] = [\n { id: 'rename', label: t('menu.rename'), icon: <IconEditOutline16 /> },\n { id: 'fork', label: t('menu.fork'), icon: <IconBranchOutline16 /> },\n { id: 'unpin', label: t('menu.unpin'), icon: <PinOff size={16} strokeWidth={1.8} aria-hidden=\"true\" /> },\n { id: 'archive', label: t('menu.archive'), icon: <IconArchiveOutline20 size={16} /> },\n ...(canDelete ? [{ id: 'delete', label: t('menu.delete'), icon: <IconTrashOutline16 />, danger: true }] : []),\n ]\n\n const closeRename = (): void => {\n if (renaming) return\n setRenameOpen(false)\n setRenameError(null)\n restoreTriggerFocus()\n }\n const confirmRename = (): void => {\n if (renameBlocked) return\n setRenaming(true)\n setRenameError(null)\n void actions.renameSession(session.id, renameTrimmed).then(() => {\n setRenaming(false)\n setRenameOpen(false)\n restoreTriggerFocus()\n }).catch(reason => {\n setRenaming(false)\n setRenameError(reason instanceof Error ? reason.message : String(reason))\n })\n }\n const closeDelete = (): void => {\n if (deleting) return\n setDeleteOpen(false)\n setDeleteError(null)\n restoreTriggerFocus()\n }\n const confirmDelete = (): void => {\n if (deleting) return\n prepareRemoval(session.id, index)\n setDeleting(true)\n setDeleteError(null)\n void actions.deleteSession(session.id).then(() => {\n setDeleting(false)\n setDeleteOpen(false)\n restoreTriggerFocus()\n }).catch(reason => {\n cancelRemoval(session.id)\n setDeleting(false)\n setDeleteError(reason instanceof Error ? reason.message : String(reason))\n })\n }\n const onSelect = (id: string): void => {\n setMenuOpen(false)\n if (id === 'rename') {\n setRenameDraft(session.title ?? title)\n setRenameError(null)\n setRenameOpen(true)\n }\n if (id === 'fork') {\n restoreTriggerFocus()\n void actions.forkSession(session.id).catch(reason => {\n showDesktopActionError('session.forkFailed', reason)\n })\n }\n if (id === 'unpin') unpin(session.id, index)\n if (id === 'archive') {\n prepareRemoval(session.id, index)\n restoreTriggerFocus()\n void actions.archiveSession(session.id).catch(reason => {\n cancelRemoval(session.id)\n restoreTriggerFocus()\n showDesktopActionError('session.archiveFailed', reason)\n if (!canDelete) console.warn('session archive rejected:', reason)\n })\n }\n if (id === 'delete' && canDelete) {\n setDeleteError(null)\n setDeleteOpen(true)\n }\n }\n\n return <>\n <li\n className=\"dsh-pins-row\"\n data-current={current ? 'true' : 'false'}\n data-menu-open={menuOpen ? 'true' : undefined}\n >\n <button\n type=\"button\"\n className=\"dsh-pins-open\"\n aria-current={current ? 'page' : undefined}\n aria-label={t('session.open', { name: title })}\n onClick={() => { open(session.id) }}\n >\n <PinIcon size={13} />\n <span className=\"dsh-pins-title\">{title}</span>\n <span className=\"dsh-pins-workspace\">{workspace}</span>\n </button>\n <Menu\n className=\"dsh-pins-action-menu\"\n open={menuOpen}\n onClose={() => { setMenuOpen(false) }}\n items={menuItems}\n onSelect={onSelect}\n portal\n closeOnPointerLeave\n anchor={<button\n ref={trigger}\n type=\"button\"\n className=\"dsh-pins-actions\"\n aria-expanded={menuOpen}\n aria-haspopup=\"menu\"\n aria-label={actionLabel}\n title={actionLabel}\n onClick={event => {\n event.stopPropagation()\n if (menuOpen) setMenuOpen(false)\n else openActionMenu(null)\n }}\n onKeyDown={event => {\n if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return\n event.preventDefault()\n openActionMenu(event.key === 'ArrowDown' ? 'first' : 'last')\n }}\n >\n <IconEllipsisOutline16 />\n </button>}\n />\n </li>\n <Modal\n open={renameOpen}\n onClose={closeRename}\n closeLabel={t('dialog.close')}\n title={t('rename.title')}\n footer={<>\n <Button variant=\"outline\" disabled={renaming} onClick={closeRename}>{t('dialog.cancel')}</Button>\n <Button variant=\"primary\" disabled={renameBlocked} onClick={confirmRename}>{t('rename.confirm')}</Button>\n </>}\n >\n <input\n className=\"dsh-pins-rename-input\"\n value={renameDraft}\n aria-label={t('rename.field')}\n autoFocus\n disabled={renaming}\n onFocus={event => { event.currentTarget.select() }}\n onChange={event => {\n setRenameDraft(event.currentTarget.value)\n setRenameError(null)\n }}\n onCompositionStart={() => { composing.current = true }}\n onCompositionEnd={() => { composing.current = false }}\n onKeyDown={event => {\n if (event.key !== 'Enter' || composing.current) return\n event.preventDefault()\n confirmRename()\n }}\n />\n {renameError !== null && <div className=\"dsh-pins-dialog-error\" role=\"alert\">{renameError}</div>}\n </Modal>\n <Modal\n open={deleteOpen}\n onClose={closeDelete}\n closeLabel={t('dialog.close')}\n title={t('delete.title')}\n description={t('delete.description', { name: title })}\n footer={<>\n <Button variant=\"outline\" autoFocus disabled={deleting} onClick={closeDelete}>{t('dialog.cancel')}</Button>\n <Button variant=\"outline\" className=\"dsh-pins-delete-action\" disabled={deleting} onClick={confirmDelete}>{t('delete.title')}</Button>\n </>}\n >\n {deleting && <div className=\"dsh-pins-dialog-status\" role=\"status\">{t('delete.pending')}</div>}\n {deleteError !== null && <div className=\"dsh-pins-dialog-error\" role=\"alert\">{deleteError}</div>}\n </Modal>\n {actionToast !== null && <Toast\n key={actionToast.seq}\n text={actionToast.text}\n onDone={() => { setActionToast(null) }}\n />}\n </>\n}\n\nfunction PinIcon({ size = 16 }: PinIconProps): ReactNode {\n return <Pin size={size} strokeWidth={1.8} aria-hidden=\"true\" />\n}\n","import { CamelToPascal } from './utility-types';\n\n/**\n * Converts string to kebab case\n *\n * @param {string} string\n * @returns {string} A kebabized string\n */\nexport const toKebabCase = (string: string) =>\n string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();\n\n/**\n * Converts string to camel case\n *\n * @param {string} string\n * @returns {string} A camelized string\n */\nexport const toCamelCase = <T extends string>(string: T) =>\n string.replace(/^([A-Z])|[\\s-_]+(\\w)/g, (match, p1, p2) =>\n p2 ? p2.toUpperCase() : p1.toLowerCase(),\n );\n\n/**\n * Converts string to pascal case\n *\n * @param {string} string\n * @returns {string} A pascalized string\n */\nexport const toPascalCase = <T extends string>(string: T): CamelToPascal<T> => {\n const camelCase = toCamelCase(string);\n\n return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal<T>;\n};\n\n/**\n * Merges classes into a single string\n *\n * @param {array} classes\n * @returns {string} A string of classes\n */\nexport const mergeClasses = <ClassType = string | undefined | null>(...classes: ClassType[]) =>\n classes\n .filter((className, index, array) => {\n return (\n Boolean(className) &&\n (className as string).trim() !== '' &&\n array.indexOf(className) === index\n );\n })\n .join(' ')\n .trim();\n","export default {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 24,\n height: 24,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n};\n","import { createElement, forwardRef } from 'react';\nimport defaultAttributes from './defaultAttributes';\nimport { IconNode, LucideProps } from './types';\nimport { mergeClasses } from '@lucide/shared';\n\ninterface IconComponentProps extends LucideProps {\n iconNode: IconNode;\n}\n\n/**\n * Lucide icon component\n *\n * @component Icon\n * @param {object} props\n * @param {string} props.color - The color of the icon\n * @param {number} props.size - The size of the icon\n * @param {number} props.strokeWidth - The stroke width of the icon\n * @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width\n * @param {string} props.className - The class name of the icon\n * @param {IconNode} props.children - The children of the icon\n * @param {IconNode} props.iconNode - The icon node of the icon\n *\n * @returns {ForwardRefExoticComponent} LucideIcon\n */\nconst Icon = forwardRef<SVGSVGElement, IconComponentProps>(\n (\n {\n color = 'currentColor',\n size = 24,\n strokeWidth = 2,\n absoluteStrokeWidth,\n className = '',\n children,\n iconNode,\n ...rest\n },\n ref,\n ) => {\n return createElement(\n 'svg',\n {\n ref,\n ...defaultAttributes,\n width: size,\n height: size,\n stroke: color,\n strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,\n className: mergeClasses('lucide', className),\n ...rest,\n },\n [\n ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),\n ...(Array.isArray(children) ? children : [children]),\n ],\n );\n },\n);\n\nexport default Icon;\n","import { createElement, forwardRef } from 'react';\nimport { mergeClasses, toKebabCase } from '@lucide/shared';\nimport { IconNode, LucideProps } from './types';\nimport Icon from './Icon';\n\n/**\n * Create a Lucide icon component\n * @param {string} iconName\n * @param {array} iconNode\n * @returns {ForwardRefExoticComponent} LucideIcon\n */\nconst createLucideIcon = (iconName: string, iconNode: IconNode) => {\n const Component = forwardRef<SVGSVGElement, LucideProps>(({ className, ...props }, ref) =>\n createElement(Icon, {\n ref,\n iconNode,\n className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className),\n ...props,\n }),\n );\n\n Component.displayName = `${iconName}`;\n\n return Component;\n};\n\nexport default createLucideIcon;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name PinOff\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cGF0aCBkPSJNMTIgMTd2NSIgLz4KICA8cGF0aCBkPSJNMTUgOS4zNFY3YTEgMSAwIDAgMSAxLTEgMiAyIDAgMCAwIDAtNEg3Ljg5IiAvPgogIDxwYXRoIGQ9Im0yIDIgMjAgMjAiIC8+CiAgPHBhdGggZD0iTTkgOXYxLjc2YTIgMiAwIDAgMS0xLjExIDEuNzlsLTEuNzguOUEyIDIgMCAwIDAgNSAxNS4yNFYxNmExIDEgMCAwIDAgMSAxaDExIiAvPgo8L3N2Zz4K) - https://lucide.dev/icons/pin-off\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst PinOff = createLucideIcon('PinOff', [\n ['path', { d: 'M12 17v5', key: 'bb1du9' }],\n ['path', { d: 'M15 9.34V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H7.89', key: 'znwnzq' }],\n ['path', { d: 'm2 2 20 20', key: '1ooewy' }],\n [\n 'path',\n {\n d: 'M9 9v1.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h11',\n key: 'c9qhm2',\n },\n ],\n]);\n\nexport default PinOff;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Pin\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cGF0aCBkPSJNMTIgMTd2NSIgLz4KICA8cGF0aCBkPSJNOSAxMC43NmEyIDIgMCAwIDEtMS4xMSAxLjc5bC0xLjc4LjlBMiAyIDAgMCAwIDUgMTUuMjRWMTZhMSAxIDAgMCAwIDEgMWgxMmExIDEgMCAwIDAgMS0xdi0uNzZhMiAyIDAgMCAwLTEuMTEtMS43OWwtMS43OC0uOUEyIDIgMCAwIDEgMTUgMTAuNzZWN2ExIDEgMCAwIDEgMS0xIDIgMiAwIDAgMCAwLTRIOGEyIDIgMCAwIDAgMCA0IDEgMSAwIDAgMSAxIDF6IiAvPgo8L3N2Zz4K) - https://lucide.dev/icons/pin\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Pin = createLucideIcon('Pin', [\n ['path', { d: 'M12 17v5', key: 'bb1du9' }],\n [\n 'path',\n {\n d: 'M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z',\n key: '1nkz8b',\n },\n ],\n]);\n\nexport default Pin;\n","import { SVGProps } from './types';\n\nconst defaultAttributes: SVGProps = {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 24,\n height: 24,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': 2,\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n};\n\nexport default defaultAttributes;\n","import defaultAttributes from '../defaultAttributes';\nimport type { IconNode } from '../types';\n\n/**\n * @name pin-off\n * @description Lucide SVG icon node.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cGF0aCBkPSJNMTIgMTd2NSIgLz4KICA8cGF0aCBkPSJNMTUgOS4zNFY3YTEgMSAwIDAgMSAxLTEgMiAyIDAgMCAwIDAtNEg3Ljg5IiAvPgogIDxwYXRoIGQ9Im0yIDIgMjAgMjAiIC8+CiAgPHBhdGggZD0iTTkgOXYxLjc2YTIgMiAwIDAgMS0xLjExIDEuNzlsLTEuNzguOUEyIDIgMCAwIDAgNSAxNS4yNFYxNmExIDEgMCAwIDAgMSAxaDExIiAvPgo8L3N2Zz4K) - https://lucide.dev/icons/pin-off\n * @see https://lucide.dev/guide/packages/lucide - Documentation\n *\n * @returns {Array}\n *\n */\nconst PinOff: IconNode = [\n 'svg',\n defaultAttributes,\n [\n ['path', { d: 'M12 17v5' }],\n ['path', { d: 'M15 9.34V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H7.89' }],\n ['path', { d: 'm2 2 20 20' }],\n ['path', { d: 'M9 9v1.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h11' }],\n ],\n];\n\nexport default PinOff;\n","import defaultAttributes from '../defaultAttributes';\nimport type { IconNode } from '../types';\n\n/**\n * @name pin\n * @description Lucide SVG icon node.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cGF0aCBkPSJNMTIgMTd2NSIgLz4KICA8cGF0aCBkPSJNOSAxMC43NmEyIDIgMCAwIDEtMS4xMSAxLjc5bC0xLjc4LjlBMiAyIDAgMCAwIDUgMTUuMjRWMTZhMSAxIDAgMCAwIDEgMWgxMmExIDEgMCAwIDAgMS0xdi0uNzZhMiAyIDAgMCAwLTEuMTEtMS43OWwtMS43OC0uOUEyIDIgMCAwIDEgMTUgMTAuNzZWN2ExIDEgMCAwIDEgMS0xIDIgMiAwIDAgMCAwLTRIOGEyIDIgMCAwIDAgMCA0IDEgMSAwIDAgMSAxIDF6IiAvPgo8L3N2Zz4K) - https://lucide.dev/icons/pin\n * @see https://lucide.dev/guide/packages/lucide - Documentation\n *\n * @returns {Array}\n *\n */\nconst Pin: IconNode = [\n 'svg',\n defaultAttributes,\n [\n ['path', { d: 'M12 17v5' }],\n [\n 'path',\n {\n d: 'M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z',\n },\n ],\n ],\n];\n\nexport default Pin;\n","import { Pin as PinNode, PinOff as PinOffNode, type IconNode } from 'lucide'\n\nexport const SIDEBAR_SLOT_SELECTOR = '[data-slot=\"sidebar.workspaces\"]'\nexport const PINNED_HOST_ATTRIBUTE = 'data-dsh-pinned-sessions-host'\nexport const MENU_HOST_ATTRIBUTE = 'data-dsh-pinned-session-menu-host'\nexport const MENU_OWNER_ATTRIBUTE = 'data-dsh-pinned-session-menu'\n\nexport interface SessionsForCapture {\n readonly list: {\n getSnapshot(): { readonly current?: string | undefined }\n }\n open(sessionId: string): void\n}\n\nexport interface SessionActionTarget {\n readonly button: HTMLButtonElement\n readonly row: HTMLElement\n}\n\nexport interface SessionMenuTarget {\n readonly host: HTMLDivElement\n readonly menu: HTMLElement\n readonly sessionId: string\n readonly button: HTMLButtonElement\n readonly icon: HTMLSpanElement\n readonly label: HTMLSpanElement\n}\n\nexport interface TriggerRect {\n readonly top: number\n readonly right: number\n readonly bottom: number\n readonly left: number\n readonly width: number\n readonly height: number\n}\n\nfunction elementFromTarget(target: EventTarget | null): Element | null {\n return target !== null && (target as Node).nodeType === 1 ? target as Element : null\n}\n\n/** Identify a native ellipsis trigger inside a real Session row. */\nexport function findSessionActionTarget(target: EventTarget | null): SessionActionTarget | null {\n const element = elementFromTarget(target)\n const button = element?.closest('button')\n if (!(button instanceof HTMLButtonElement)) return null\n const row = button.closest<HTMLElement>('[role=\"treeitem\"][aria-selected]')\n if (row === null || row === button) return null\n if (row.closest(SIDEBAR_SLOT_SELECTOR) === null) return null\n if (row.closest(`[${PINNED_HOST_ATTRIBUTE}]`) !== null) return null\n return { button, row }\n}\n\n/**\n * Ask the native row closure for its Session ID without changing selection.\n * The Workspace browser's row callback calls sessions.open(node.id), so a\n * synchronous, verified interceptor captures the exact ID and is restored\n * before the user's original ellipsis click continues.\n */\nexport function captureSessionId(row: HTMLElement, sessions: SessionsForCapture): string | null {\n const hadOwn = Object.prototype.hasOwnProperty.call(sessions, 'open')\n const ownDescriptor = hadOwn ? Object.getOwnPropertyDescriptor(sessions, 'open') : undefined\n const original = sessions.open\n let captured: string | null = null\n const interceptor = (sessionId: string): void => {\n if (typeof sessionId === 'string' && sessionId.length > 0) captured = sessionId\n }\n\n try {\n Object.defineProperty(sessions, 'open', {\n configurable: true,\n enumerable: ownDescriptor?.enumerable ?? false,\n writable: true,\n value: interceptor,\n })\n if (sessions.open !== interceptor) return null\n row.click()\n } catch {\n return null\n } finally {\n try {\n if (hadOwn && ownDescriptor !== undefined) Object.defineProperty(sessions, 'open', ownDescriptor)\n else delete (sessions as { open?: SessionsForCapture['open'] }).open\n } catch {\n try {\n Object.defineProperty(sessions, 'open', {\n configurable: true,\n writable: true,\n value: original,\n })\n } catch {\n // The verified patch path is configurable; this is a defensive fallback.\n }\n }\n }\n return captured\n}\n\n/** Insert the pinned region immediately after the native Workspace header. */\nexport function ensurePinnedHost(doc: Document): HTMLDivElement | null {\n const slot = doc.querySelector<HTMLElement>(SIDEBAR_SLOT_SELECTOR)\n const root = slot?.firstElementChild\n if (!(root instanceof HTMLElement)) return null\n const existing = root.querySelector<HTMLDivElement>(`:scope > [${PINNED_HOST_ATTRIBUTE}]`)\n if (existing !== null) return existing\n const header = root.firstElementChild\n if (header === null) return null\n const host = doc.createElement('div')\n host.setAttribute(PINNED_HOST_ATTRIBUTE, '')\n header.insertAdjacentElement('afterend', host)\n return host\n}\n\n/** List direct-body portal menus so one trigger can exclude every pre-existing menu. */\nexport function listPortalMenus(doc: Document): readonly HTMLElement[] {\n return [...doc.body.children].filter((node): node is HTMLElement => (\n node instanceof HTMLElement && node.getAttribute('role') === 'menu'\n ))\n}\n\nfunction belongsToTrigger(menu: HTMLElement, trigger: TriggerRect | undefined): boolean {\n if (trigger === undefined || trigger.width === 0 || trigger.height === 0) return true\n const menuRect = menu.getBoundingClientRect()\n if (menuRect.width === 0 || menuRect.height === 0) return true\n const horizontalGap = Math.min(\n Math.abs(menuRect.left - trigger.left),\n Math.abs(menuRect.right - trigger.right),\n )\n const verticalGap = Math.max(\n 0,\n menuRect.top - trigger.bottom,\n trigger.top - menuRect.bottom,\n )\n return horizontalGap <= 64 && verticalGap <= 32\n}\n\n/** Find only a new, geometrically associated portal menu after one trigger click. */\nexport function findUnclaimedPortalMenu(\n doc: Document,\n excluded: ReadonlySet<HTMLElement> = new Set(),\n trigger?: TriggerRect,\n): HTMLElement | null {\n const menus = listPortalMenus(doc).filter(menu => (\n !excluded.has(menu)\n && !menu.hasAttribute(MENU_OWNER_ATTRIBUTE)\n && belongsToTrigger(menu, trigger)\n ))\n return menus.at(-1) ?? null\n}\n\nfunction createLucideIcon(doc: Document, node: IconNode, name: string): SVGSVGElement {\n const [, attributes, children = []] = node\n const svg = doc.createElementNS('http://www.w3.org/2000/svg', 'svg')\n for (const [key, value] of Object.entries(attributes)) svg.setAttribute(key, String(value))\n svg.setAttribute('width', '16')\n svg.setAttribute('height', '16')\n svg.setAttribute('stroke-width', '1.8')\n svg.setAttribute('class', `lucide lucide-${name}`)\n svg.setAttribute('aria-hidden', 'true')\n for (const [tag, childAttributes] of children) {\n const child = doc.createElementNS('http://www.w3.org/2000/svg', tag)\n for (const [key, value] of Object.entries(childAttributes)) child.setAttribute(key, String(value))\n svg.appendChild(child)\n }\n return svg\n}\n\n/** Update the unmanaged item without moving it into the plugin's React tree. */\nexport function updateSessionMenuItem(target: SessionMenuTarget, pinned: boolean, label: string): void {\n target.button.setAttribute('aria-label', label)\n target.label.textContent = label\n target.icon.replaceChildren(createLucideIcon(\n target.menu.ownerDocument,\n pinned ? PinOffNode : PinNode,\n pinned ? 'pin-off' : 'pin',\n ))\n}\n\n/** Add an unmanaged native-styled row before the Session menu's archive tail. */\nexport function attachSessionMenuHost(menu: HTMLElement, sessionId: string): SessionMenuTarget | null {\n if (menu.hasAttribute(MENU_OWNER_ATTRIBUTE)) return null\n const viewport = [...menu.children].find((node): node is HTMLElement => (\n node instanceof HTMLElement && node.getAttribute('role') === 'presentation'\n ))\n if (viewport === undefined) return null\n const wrappers = [...viewport.children].filter((node): node is HTMLElement => (\n node instanceof HTMLElement\n && node.querySelector(':scope > button[role=\"menuitem\"]') instanceof HTMLButtonElement\n ))\n if (wrappers.length < 3 || wrappers.length > 4) return null\n const templateButton = wrappers[0]?.querySelector<HTMLButtonElement>(':scope > button[role=\"menuitem\"]')\n if (templateButton === null || templateButton === undefined) return null\n const spans = [...templateButton.children].filter((node): node is HTMLSpanElement => node instanceof HTMLSpanElement)\n if (spans.length < 2) return null\n\n const doc = menu.ownerDocument\n const host = doc.createElement('div')\n host.className = wrappers[0]?.className ?? ''\n host.setAttribute(MENU_HOST_ATTRIBUTE, '')\n const button = doc.createElement('button')\n button.type = 'button'\n button.setAttribute('role', 'menuitem')\n button.className = `${templateButton.className} dsh-pins-menu-button`\n const icon = doc.createElement('span')\n icon.className = spans[0]?.className ?? ''\n icon.setAttribute('aria-hidden', 'true')\n const label = doc.createElement('span')\n label.className = spans.at(-1)?.className ?? ''\n button.append(icon, label)\n host.appendChild(button)\n viewport.insertBefore(host, wrappers[2] ?? null)\n menu.setAttribute(MENU_OWNER_ATTRIBUTE, sessionId)\n\n const target = { host, menu, sessionId, button, icon, label }\n const view = doc.defaultView\n if (view !== null) view.dispatchEvent(new view.Event('resize'))\n return target\n}\n\n/** Restore useful keyboard focus after a pinned row removes itself. */\nexport function focusAfterPinnedRemoval(doc: Document, host: HTMLElement | null, removedIndex: number): boolean {\n const remaining = host === null ? [] : [...host.querySelectorAll<HTMLButtonElement>('.dsh-pins-open')]\n const next = remaining[Math.min(removedIndex, remaining.length - 1)]\n if (next !== undefined) {\n next.focus({ preventScroll: true })\n return true\n }\n const nativeRow = doc.querySelector<HTMLElement>(`${SIDEBAR_SLOT_SELECTOR} [role=\"treeitem\"][aria-selected=\"true\"]`)\n ?? doc.querySelector<HTMLElement>(`${SIDEBAR_SLOT_SELECTOR} [role=\"treeitem\"]`)\n if (nativeRow === null) return false\n const hadTabIndex = nativeRow.hasAttribute('tabindex')\n if (!hadTabIndex) nativeRow.tabIndex = -1\n nativeRow.focus({ preventScroll: true })\n if (doc.activeElement === nativeRow) return true\n if (!hadTabIndex) nativeRow.removeAttribute('tabindex')\n return false\n}\n\nexport function removeBridgeArtifacts(doc: Document): void {\n for (const node of doc.querySelectorAll(`[${PINNED_HOST_ATTRIBUTE}], [${MENU_HOST_ATTRIBUTE}]`)) node.remove()\n for (const menu of doc.querySelectorAll(`[${MENU_OWNER_ATTRIBUTE}]`)) menu.removeAttribute(MENU_OWNER_ATTRIBUTE)\n}\n","export const PIN_STORAGE_KEY = 'dsh.pinned-sessions.v1'\nconst MAX_PINS = 500\n\nexport interface PinRecord {\n readonly id: string\n readonly pinnedAt: number\n}\n\ninterface PersistedPins {\n readonly version: 1\n readonly pins: readonly PinRecord[]\n}\n\nexport interface StorageLike {\n getItem(key: string): string | null\n setItem(key: string, value: string): void\n}\n\nfunction isPinRecord(value: unknown): value is PinRecord {\n if (typeof value !== 'object' || value === null) return false\n const row = value as Record<string, unknown>\n return typeof row.id === 'string'\n && row.id.length > 0\n && Number.isFinite(row.pinnedAt)\n && (row.pinnedAt as number) >= 0\n}\n\n/** Decode, de-duplicate, and normalize one persisted pin snapshot. */\nexport function decodePins(raw: string | null): readonly PinRecord[] {\n if (raw === null) return []\n try {\n const parsed = JSON.parse(raw) as Partial<PersistedPins>\n if (parsed.version !== 1 || !Array.isArray(parsed.pins)) return []\n const byId = new Map<string, PinRecord>()\n for (const candidate of parsed.pins) {\n if (!isPinRecord(candidate)) continue\n const previous = byId.get(candidate.id)\n if (previous === undefined || candidate.pinnedAt > previous.pinnedAt) {\n byId.set(candidate.id, { id: candidate.id, pinnedAt: candidate.pinnedAt })\n }\n }\n return [...byId.values()]\n .sort((left, right) => right.pinnedAt - left.pinnedAt || left.id.localeCompare(right.id))\n .slice(0, MAX_PINS)\n } catch {\n return []\n }\n}\n\n/** Browser-local, versioned pin state with React-compatible subscriptions. */\nexport class PinStore {\n private snapshot: readonly PinRecord[]\n private readonly listeners = new Set<() => void>()\n\n constructor(private readonly storage: StorageLike | undefined) {\n this.snapshot = this.read()\n }\n\n readonly getSnapshot = (): readonly PinRecord[] => this.snapshot\n\n readonly subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n isPinned(id: string): boolean {\n return this.snapshot.some(row => row.id === id)\n }\n\n toggle(id: string, now = Date.now()): void {\n if (id.length === 0) return\n const without = this.snapshot.filter(row => row.id !== id)\n this.publish(this.isPinned(id) ? without : [{ id, pinnedAt: now }, ...without])\n }\n\n prune(keep: (id: string) => boolean): void {\n const next = this.snapshot.filter(row => keep(row.id))\n if (next.length !== this.snapshot.length) this.publish(next)\n }\n\n reload(): void {\n const next = this.read()\n if (samePins(this.snapshot, next)) return\n this.snapshot = next\n this.emit()\n }\n\n private read(): readonly PinRecord[] {\n try {\n return decodePins(this.storage?.getItem(PIN_STORAGE_KEY) ?? null)\n } catch {\n return []\n }\n }\n\n private publish(next: readonly PinRecord[]): void {\n this.snapshot = [...next]\n .sort((left, right) => right.pinnedAt - left.pinnedAt || left.id.localeCompare(right.id))\n .slice(0, MAX_PINS)\n try {\n this.storage?.setItem(PIN_STORAGE_KEY, JSON.stringify({ version: 1, pins: this.snapshot }))\n } catch {\n // Storage can be unavailable in hardened or quota-constrained browser profiles.\n }\n this.emit()\n }\n\n private emit(): void {\n for (const listener of [...this.listeners]) listener()\n }\n}\n\nfunction samePins(left: readonly PinRecord[], right: readonly PinRecord[]): boolean {\n return left.length === right.length\n && left.every((row, index) => row.id === right[index]?.id && row.pinnedAt === right[index]?.pinnedAt)\n}\n","interface SessionPresentationInput {\n readonly id: string\n readonly title?: string | undefined\n readonly cwd?: string | undefined\n}\n\nfunction nonBlank(value: string | undefined): string | undefined {\n const trimmed = value?.trim()\n return trimmed === undefined || trimmed.length === 0 ? undefined : trimmed\n}\n\n/** Match the native sidebar's useful fallback for sessions without a title. */\nexport function sessionDisplayTitle(\n session: SessionPresentationInput,\n workspaceTitle: string | undefined,\n): string {\n const explicit = nonBlank(session.title)\n if (explicit !== undefined) return explicit\n const workspace = nonBlank(workspaceTitle)\n if (workspace !== undefined) return workspace\n const cwd = nonBlank(session.cwd)\n if (cwd !== undefined) {\n const leaf = cwd.replace(/[\\\\/]+$/u, '').split(/[\\\\/]/u).at(-1)\n if (leaf !== undefined && leaf.length > 0) return leaf\n }\n return session.id\n}\n"],"mappings":"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","names":["import_react","defaultAttributes","PinOff","defaultAttributes","Pin","defaultAttributes","createLucideIcon","PinOff","Pin"],"file":"client.js"}
1
+ {"version":3,"sources":["../src/client/index.tsx","../node_modules/shared/src/utils.ts","../node_modules/lucide-react/src/defaultAttributes.ts","../node_modules/lucide-react/src/Icon.ts","../node_modules/lucide-react/src/createLucideIcon.ts","../node_modules/lucide-react/src/icons/archive.ts","../node_modules/lucide-react/src/icons/ellipsis.ts","../node_modules/lucide-react/src/icons/git-branch.ts","../node_modules/lucide-react/src/icons/pencil.ts","../node_modules/lucide-react/src/icons/pin-off.ts","../node_modules/lucide-react/src/icons/pin.ts","../node_modules/lucide-react/src/icons/trash-2.ts","../node_modules/lucide/src/defaultAttributes.ts","../node_modules/lucide/src/icons/pin-off.ts","../node_modules/lucide/src/icons/pin.ts","../src/client/dom-bridge.ts","../src/client/pin-store.ts","../src/client/presentation.ts"],"sourcesContent":["import {\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type ReactNode,\n} from 'react'\nimport {\n Button,\n Menu,\n MenuItemButton,\n Modal,\n Toast,\n type MenuEntry,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport { Pin, PinOff, Archive, GitBranch, Pencil, Ellipsis, Trash2 } from 'lucide-react'\nimport { createPortal } from 'react-dom'\nimport {\n attachSessionMenuHost,\n captureSessionId,\n ensurePinnedHost,\n findSessionActionTarget,\n findUnclaimedPortalMenu,\n focusAfterPinnedRemoval,\n listPortalMenus,\n MENU_OWNER_ATTRIBUTE,\n removeBridgeArtifacts,\n SIDEBAR_SLOT_SELECTOR,\n updateSessionMenuItem,\n type SessionMenuTarget,\n type TriggerRect,\n type SessionsForCapture,\n} from './dom-bridge.js'\nimport { PIN_STORAGE_KEY, PinStore } from './pin-store.js'\nimport { sessionDisplayTitle } from './presentation.js'\n\ninterface SessionSummaryLike {\n readonly id: string\n readonly title?: string | undefined\n readonly cwd?: string | undefined\n readonly blank?: boolean | undefined\n readonly running?: boolean | undefined\n readonly completed?: boolean | undefined\n}\n\ninterface SessionsSnapshotLike {\n readonly ids: readonly string[]\n readonly byId: Readonly<Record<string, SessionSummaryLike | undefined>>\n readonly current?: string | undefined\n readonly phase: string\n}\n\ninterface WorkspaceLike {\n readonly workspaceId: string\n readonly title: string\n readonly sessionIds: readonly string[]\n}\n\ninterface WorkspacesSnapshotLike {\n readonly items: readonly WorkspaceLike[]\n readonly archivedSessionIds: readonly string[]\n readonly phase: string\n}\n\ntype SelectorHook<T> = <S>(selector: (snapshot: T) => S, equal?: (left: S, right: S) => boolean) => S\ntype Translate = (key: string, params?: Readonly<Record<string, unknown>>) => string\n\ninterface SlotsLike {\n inject(name: string, install: () => unknown): void\n register<I, P>(\n entry: {\n readonly name: string\n readonly id: string\n readonly order?: number\n readonly locale?: string\n readonly inject?: () => I\n },\n component: (props: P) => ReactNode,\n ): () => void\n}\n\ninterface LocaleLike {\n register(namespace: string, dictionaries: Readonly<Record<string, Readonly<Record<string, string>>>>): () => void\n}\n\ninterface ResultLike {\n readonly ok: boolean\n readonly error?: { readonly message: string } | undefined\n}\n\ninterface ClientSessionsLike extends SessionsForCapture {\n binding(sessionId: string): { readonly session: { rename(title: string): Promise<ResultLike> } } | undefined\n fork(input: { readonly sessionId: string; readonly increaseTitle: boolean }): Promise<string>\n}\n\ninterface WorkspacesServiceLike {\n archiveSession(sessionId: string): Promise<void>\n}\n\ninterface DeleteSessionServiceLike {\n deleteSession(sessionId: string): Promise<ResultLike>\n}\n\ninterface BooleanStoreLike {\n readonly getSnapshot: () => boolean\n readonly subscribe: (listener: () => void) => () => void\n}\n\nclass BooleanStore implements BooleanStoreLike {\n private value = false\n private readonly listeners = new Set<() => void>()\n\n readonly getSnapshot = (): boolean => this.value\n readonly subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n set(value: boolean): void {\n if (value === this.value) return\n this.value = value\n for (const listener of this.listeners) listener()\n }\n}\n\ninterface ClientContextLike {\n readonly slots: SlotsLike\n readonly sessions: ClientSessionsLike\n readonly locale: LocaleLike\n get(name: string): unknown\n inject(dependencies: readonly string[], setup: (ctx: ClientContextLike) => void): unknown\n effect(setup: () => (() => void), label?: string): unknown\n}\n\nexport interface PinnedSessionActions {\n readonly openSession?: (sessionId: string) => void\n readonly renameSession: (sessionId: string, title: string) => Promise<void>\n readonly forkSession: (sessionId: string) => Promise<void>\n readonly archiveSession: (sessionId: string) => Promise<void>\n readonly deleteAvailability: BooleanStoreLike\n readonly deleteSession: (sessionId: string) => Promise<void>\n}\n\ninterface BridgeProps {\n readonly store: PinStore\n readonly sessions: SessionsForCapture\n readonly actions: PinnedSessionActions\n readonly useSessions: SelectorHook<SessionsSnapshotLike>\n readonly useWorkspaces: SelectorHook<WorkspacesSnapshotLike>\n readonly t: Translate\n readonly modernMenus?: BooleanStoreLike\n}\n\ninterface PinIconProps {\n readonly size?: number\n}\n\ninterface PendingMenuRequest {\n readonly sessionId: string\n readonly createdAt: number\n readonly existingMenus: ReadonlySet<HTMLElement>\n readonly triggerRect: TriggerRect\n}\n\ninterface ActiveMenuBinding {\n readonly target: SessionMenuTarget\n readonly onClick: (event: MouseEvent) => void\n}\n\ninterface PendingFocusRemoval {\n readonly sessionId: string\n readonly index: number\n}\n\nconst NS = 'pinnedSessions'\nconst STYLE_ID = '@anionex/dsh-pinned-sessions'\nconst MENU_ASSOCIATION_MS = 2_000\n\nconst zh = {\n 'menu.rename': '重命名',\n 'menu.fork': '分叉会话',\n 'menu.pin': '置顶到顶部分组',\n 'menu.unpin': '从顶部分组取消置顶',\n 'menu.archive': '归档会话',\n 'menu.delete': '删除会话',\n 'section.label': '置顶会话',\n 'session.open': '打开置顶会话“{name}”',\n 'session.actions': '置顶会话“{name}”的操作',\n 'session.archiveFailed': '归档会话失败:{detail}',\n 'session.forkFailed': '分叉会话失败:{detail}',\n 'session.unpin': '取消置顶“{name}”',\n 'dialog.close': '关闭',\n 'dialog.cancel': '取消',\n 'rename.title': '重命名会话',\n 'rename.field': '会话名称',\n 'rename.confirm': '重命名',\n 'delete.title': '删除会话',\n 'delete.description': '将永久删除会话“{name}”及其子代理(含正在运行的)和全部记录(对话内容、统计、缓存),此操作不可恢复。',\n 'delete.pending': '正在删除会话…',\n 'workspace.ungrouped': '未分组',\n}\n\nconst en = {\n 'menu.rename': 'Rename',\n 'menu.fork': 'Fork session',\n 'menu.pin': 'Pin to top section',\n 'menu.unpin': 'Unpin from top section',\n 'menu.archive': 'Archive session',\n 'menu.delete': 'Delete session',\n 'section.label': 'Pinned sessions',\n 'session.open': 'Open pinned session “{name}”',\n 'session.actions': 'Actions for pinned session “{name}”',\n 'session.archiveFailed': 'Could not archive session: {detail}',\n 'session.forkFailed': 'Could not fork session: {detail}',\n 'session.unpin': 'Unpin “{name}”',\n 'dialog.close': 'Close',\n 'dialog.cancel': 'Cancel',\n 'rename.title': 'Rename session',\n 'rename.field': 'Session name',\n 'rename.confirm': 'Rename',\n 'delete.title': 'Delete session',\n 'delete.description': 'This permanently deletes session “{name}”, its child agents (including any that are still running), and all of its records (conversation, stats, cache). This cannot be undone.',\n 'delete.pending': 'Deleting session…',\n 'workspace.ungrouped': 'Ungrouped',\n}\n\nconst styles = `\n[${'data-dsh-pinned-sessions-host'}]{box-sizing:border-box;min-width:0;flex:none}\n.dsh-pins-section{box-sizing:border-box;min-width:0;margin:0 4px 6px;padding:0 0 6px;border-bottom:1px solid var(--dsw-alias-border-l1)}\n.dsh-pins-header{display:flex;align-items:center;gap:6px;height:28px;padding:0 8px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;font-weight:500}\n.dsh-pins-header svg{flex:none;color:var(--dsw-alias-state-business-primary)}\n.dsh-pins-list{box-sizing:border-box;max-height:192px;min-width:0;margin:0;padding:0;overflow-y:auto;overscroll-behavior:contain;list-style:none}\n.dsh-pins-row{box-sizing:border-box;display:flex;align-items:center;gap:0;height:32px;min-width:0;border-radius:6px;color:var(--dsw-alias-label-primary)}\n.dsh-pins-row:hover,.dsh-pins-row[data-current=\"true\"],.dsh-pins-row[data-menu-open=\"true\"]{background:var(--dsw-alias-interactive-bg-hover)}\n.dsh-pins-open{box-sizing:border-box;display:flex;align-items:center;gap:6px;min-width:0;height:32px;padding:0 4px 0 8px;border:0;background:transparent;color:inherit;cursor:pointer;flex:1;text-align:left}\n.dsh-pins-open:focus-visible,.dsh-pins-actions:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}\n.dsh-pins-open>svg{flex:none;color:var(--dsw-alias-state-business-primary)}\n.dsh-pins-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;line-height:20px;flex:1}\n.dsh-pins-workspace{max-width:34%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:18px;flex:none}\n.dsh-pins-actions{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;margin-right:3px;padding:0;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;opacity:0;pointer-events:none;flex:none}\n.dsh-pins-row:hover .dsh-pins-actions,.dsh-pins-row:focus-within .dsh-pins-actions,.dsh-pins-row[data-menu-open=\"true\"] .dsh-pins-actions{opacity:1;pointer-events:auto}\n.dsh-pins-actions:hover,.dsh-pins-row[data-menu-open=\"true\"] .dsh-pins-actions{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}\n.dsh-pins-action-menu{display:inline-flex;flex:none}\n.dsh-pins-rename-input{box-sizing:border-box;width:100%;height:36px;padding:0 11px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:0;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit}\n.dsh-pins-rename-input:focus{border-color:var(--dsw-alias-state-business-primary)}\n.dsh-pins-dialog-error{margin-top:8px;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}\n.dsh-pins-dialog-status{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}\n.dsh-pins-delete-action{color:var(--dsw-alias-state-error-primary)!important;border-color:var(--dsw-alias-state-error-primary)!important}\n@media (max-width:767px){.dsh-pins-workspace{display:none}}\n@media (hover:none),(max-width:767px){.dsh-pins-actions{opacity:1;pointer-events:auto}}\n`\n\nexport const inject = ['slots', 'sessions', 'workspaces', 'locale']\n\nfunction deleteSessionService(ctx: ClientContextLike): DeleteSessionServiceLike | null {\n try {\n const service = ctx.get('remote.workspaceRegistry') as Partial<DeleteSessionServiceLike> | undefined\n return typeof service?.deleteSession === 'function' ? service as DeleteSessionServiceLike : null\n } catch {\n return null\n }\n}\n\nfunction makePinnedSessionActions(ctx: ClientContextLike, deleteAvailability: BooleanStoreLike): PinnedSessionActions {\n const workspaces = ctx.get('workspaces') as WorkspacesServiceLike\n return {\n renameSession: async (sessionId, title) => {\n const session = ctx.sessions.binding(sessionId)?.session\n if (session === undefined) throw new Error(`unknown session \"${sessionId}\"`)\n const result = await session.rename(title)\n if (!result.ok) throw new Error(result.error?.message ?? 'Session rename failed')\n },\n forkSession: async sessionId => {\n const childId = await ctx.sessions.fork({ sessionId, increaseTitle: true })\n ctx.sessions.open(childId)\n },\n archiveSession: async sessionId => { await workspaces.archiveSession(sessionId) },\n deleteAvailability,\n deleteSession: async sessionId => {\n const service = deleteSessionService(ctx)\n if (service === null) throw new Error('Session deletion is unavailable')\n const result = await service.deleteSession(sessionId)\n if (!result.ok) throw new Error(result.error?.message ?? 'Session deletion failed')\n },\n }\n}\n\n/** Register the lifecycle bridge in the additive frame overlay slot. */\nexport function apply(ctx: ClientContextLike): void {\n let storage: Storage | undefined\n try {\n storage = window.localStorage\n } catch {\n storage = undefined\n }\n const store = new PinStore(storage)\n const deleteAvailability = new BooleanStore()\n const actions = makePinnedSessionActions(ctx, deleteAvailability)\n const modernMenus = new BooleanStore()\n let openSession = (sessionId: string): void => { ctx.sessions.open(sessionId) }\n const navigationActions = {\n ...actions,\n openSession: (id: string) => { openSession(id) },\n forkSession: async (id: string) => { openSession(await ctx.sessions.fork({ sessionId: id, increaseTitle: true })) },\n }\n ctx.inject(['uiWorkspace'], navigation => {\n const service = navigation.get('uiWorkspace') as { openSession(id: string): void }\n openSession = id => { service.openSession(id) }\n navigation.effect(() => () => { openSession = id => { ctx.sessions.open(id) } })\n })\n\n ctx.slots.inject('sidebar.workspaces.session.menu.item', () => {\n modernMenus.set(true)\n const dispose = ctx.slots.register({\n name: 'sidebar.workspaces.session.menu.item', id: 'pinned-sessions', order: 90, locale: NS,\n inject: () => ({ store }),\n }, PinTopSectionMenuItem)\n return () => { dispose(); modernMenus.set(false) }\n })\n\n ctx.inject(['remote.workspaceRegistry'], serviceCtx => {\n const available = deleteSessionService(serviceCtx) !== null\n deleteAvailability.set(available)\n serviceCtx.effect(() => () => { deleteAvailability.set(false) }, 'pinned-sessions: delete capability')\n })\n\n ctx.effect(() => {\n const onStorage = (event: StorageEvent): void => {\n if (event.key === PIN_STORAGE_KEY || event.key === null) store.reload()\n }\n window.addEventListener('storage', onStorage)\n return () => { window.removeEventListener('storage', onStorage) }\n }, 'pinned-sessions: cross-tab state')\n\n ctx.effect(() => {\n if (document.querySelector(`style[data-plugin-css=\"${STYLE_ID}\"]`) !== null) return () => {}\n const tag = document.createElement('style')\n tag.dataset.plugin = '@anionex/dsh-pinned-sessions'\n tag.dataset.pluginCss = STYLE_ID\n tag.textContent = styles\n document.head.appendChild(tag)\n return () => { tag.remove() }\n }, 'pinned-sessions: styles')\n\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'pinned-sessions: dictionaries')\n\n ctx.slots.inject('shell.overlay', () => ctx.slots.register({\n name: 'shell.overlay',\n id: 'pinned-sessions-bridge',\n order: 40,\n locale: NS,\n inject: () => ({ store, sessions: ctx.sessions, actions: navigationActions, modernMenus }),\n }, PinnedSessionsBridge))\n}\n\n/** The official 0.2 menu slot supplies the exact Session identity. */\nfunction PinTopSectionMenuItem({ sessionId, store, useMenuOpenState, t }: {\n sessionId: string\n store: PinStore\n useMenuOpenState: () => [boolean, (open: boolean) => void]\n t: Translate\n}): ReactNode {\n useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot)\n const [, setMenuOpen] = useMenuOpenState()\n const pinned = store.isPinned(sessionId)\n return <MenuItemButton icon={<PinIcon />} onSelect={() => { store.toggle(sessionId); setMenuOpen(false) }}>\n {t(pinned ? 'menu.unpin' : 'menu.pin')}\n </MenuItemButton>\n}\n\n/** Keep native behavior intact while mounting the sidebar portal and unmanaged menu item. */\nexport function PinnedSessionsBridge({ store, sessions, actions, useSessions, useWorkspaces, t, modernMenus }: BridgeProps): ReactNode {\n const sessionSnapshot = useSessions(snapshot => snapshot)\n const workspaceSnapshot = useWorkspaces(snapshot => snapshot)\n const pins = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot)\n const [sidebarHost, setSidebarHost] = useState<HTMLDivElement | null>(null)\n const [sidebarWide, setSidebarWide] = useState(true)\n const pending = useRef<PendingMenuRequest | null>(null)\n const activeMenu = useRef<ActiveMenuBinding | null>(null)\n const focusAfterRemoval = useRef<PendingFocusRemoval | null>(null)\n\n useLayoutEffect(() => {\n let active = true\n let queued = false\n let settleTimer: number | undefined\n let observedSidebar: Element | null = null\n const sidebarObserver = new MutationObserver(() => { queueRefresh() })\n\n const disposeActiveMenu = (): void => {\n const binding = activeMenu.current\n if (binding === null) return\n activeMenu.current = null\n binding.target.button.removeEventListener('click', binding.onClick)\n binding.target.host.remove()\n if (binding.target.menu.getAttribute(MENU_OWNER_ATTRIBUTE) === binding.target.sessionId) {\n binding.target.menu.removeAttribute(MENU_OWNER_ATTRIBUTE)\n }\n }\n const updateActiveMenu = (): void => {\n const binding = activeMenu.current\n if (binding === null) return\n const pinned = store.isPinned(binding.target.sessionId)\n updateSessionMenuItem(binding.target, pinned, t(pinned ? 'menu.unpin' : 'menu.pin'))\n }\n const observeSidebar = (): void => {\n const slot = document.querySelector(SIDEBAR_SLOT_SELECTOR)\n if (slot === observedSidebar) return\n sidebarObserver.disconnect()\n observedSidebar = slot\n if (slot !== null) {\n sidebarObserver.observe(slot, {\n attributes: true,\n attributeFilter: ['class', 'style'],\n childList: true,\n subtree: true,\n })\n }\n }\n const refresh = (): void => {\n if (!active) return\n const nextHost = ensurePinnedHost(document)\n setSidebarHost(current => current === nextHost ? current : nextHost)\n observeSidebar()\n const rootWidth = nextHost?.parentElement?.getBoundingClientRect().width\n if (rootWidth !== undefined) setSidebarWide(rootWidth >= 160)\n\n const binding = activeMenu.current\n if (binding !== null && (!binding.target.host.isConnected || !binding.target.menu.isConnected)) {\n disposeActiveMenu()\n }\n const request = pending.current\n if (request === null) return\n if (Date.now() - request.createdAt > MENU_ASSOCIATION_MS) {\n pending.current = null\n return\n }\n const menu = findUnclaimedPortalMenu(document, request.existingMenus, request.triggerRect)\n if (menu === null) return\n const target = attachSessionMenuHost(menu, request.sessionId)\n if (target === null) return\n const onClick = (event: MouseEvent): void => {\n event.preventDefault()\n event.stopPropagation()\n store.toggle(target.sessionId)\n disposeActiveMenu()\n document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))\n }\n target.button.addEventListener('click', onClick)\n activeMenu.current = { target, onClick }\n pending.current = null\n updateActiveMenu()\n }\n function queueRefresh(): void {\n if (!active) return\n if (settleTimer === undefined) {\n settleTimer = window.setTimeout(() => {\n settleTimer = undefined\n refresh()\n }, 250)\n }\n if (queued) return\n queued = true\n queueMicrotask(() => {\n queued = false\n refresh()\n })\n }\n const onClickCapture = (event: MouseEvent): void => {\n pending.current = null\n if (modernMenus?.getSnapshot()) return\n const target = findSessionActionTarget(event.target)\n if (target === null) return\n const sessionId = captureSessionId(target.row, sessions)\n if (sessionId === null) return\n const rect = target.button.getBoundingClientRect()\n pending.current = {\n sessionId,\n createdAt: Date.now(),\n existingMenus: new Set(listPortalMenus(document)),\n triggerRect: {\n top: rect.top,\n right: rect.right,\n bottom: rect.bottom,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n },\n }\n queueRefresh()\n }\n const onKeyDownCapture = (): void => { pending.current = null }\n\n refresh()\n document.addEventListener('click', onClickCapture, true)\n document.addEventListener('keydown', onKeyDownCapture, true)\n const bodyObserver = new MutationObserver(() => { queueRefresh() })\n bodyObserver.observe(document.body, { childList: true })\n const unsubscribe = store.subscribe(updateActiveMenu)\n return () => {\n active = false\n pending.current = null\n if (settleTimer !== undefined) window.clearTimeout(settleTimer)\n unsubscribe()\n bodyObserver.disconnect()\n sidebarObserver.disconnect()\n document.removeEventListener('click', onClickCapture, true)\n document.removeEventListener('keydown', onKeyDownCapture, true)\n disposeActiveMenu()\n removeBridgeArtifacts(document)\n }\n }, [sessions, store, t, modernMenus])\n\n useLayoutEffect(() => {\n if (sidebarHost === null) return\n const root = sidebarHost.parentElement\n if (root === null) return\n const update = (): void => { setSidebarWide(root.getBoundingClientRect().width >= 160) }\n update()\n if (typeof ResizeObserver === 'undefined') return\n const observer = new ResizeObserver(update)\n observer.observe(root)\n return () => { observer.disconnect() }\n }, [sidebarHost])\n\n useEffect(() => {\n if (sessionSnapshot.phase !== 'ready' || workspaceSnapshot.phase !== 'ready') return\n const valid = new Set(sessionSnapshot.ids)\n const archived = new Set(workspaceSnapshot.archivedSessionIds)\n store.prune(sessionId => valid.has(sessionId) && !archived.has(sessionId))\n }, [sessionSnapshot, store, workspaceSnapshot])\n\n const rows = useMemo(() => {\n const workspaceBySession = new Map<string, string>()\n for (const workspace of workspaceSnapshot.items) {\n for (const sessionId of workspace.sessionIds) {\n if (!workspaceBySession.has(sessionId)) workspaceBySession.set(sessionId, workspace.title)\n }\n }\n return pins.flatMap(pin => {\n const session = sessionSnapshot.byId[pin.id]\n if (session === undefined || session.blank === true) return []\n const workspaceTitle = workspaceBySession.get(pin.id)\n const workspace = workspaceTitle ?? t('workspace.ungrouped')\n return [{ session, title: sessionDisplayTitle(session, workspaceTitle), workspace }]\n })\n }, [pins, sessionSnapshot.byId, t, workspaceSnapshot.items])\n\n useLayoutEffect(() => {\n const pendingRemoval = focusAfterRemoval.current\n if (pendingRemoval === null || rows.some(row => row.session.id === pendingRemoval.sessionId)) return\n focusAfterRemoval.current = null\n focusAfterPinnedRemoval(document, sidebarHost, pendingRemoval.index)\n }, [rows, sidebarHost])\n\n return sidebarHost !== null && sidebarHost.isConnected && sidebarWide && rows.length > 0\n ? createPortal(\n <PinnedSessionSection\n actions={actions}\n cancelRemoval={sessionId => {\n if (focusAfterRemoval.current?.sessionId === sessionId) focusAfterRemoval.current = null\n }}\n currentId={sessionSnapshot.current}\n rows={rows}\n open={sessionId => { if (actions.openSession) actions.openSession(sessionId); else sessions.open(sessionId) }}\n prepareRemoval={(sessionId, index) => {\n focusAfterRemoval.current = { sessionId, index }\n }}\n unpin={(sessionId, index) => {\n focusAfterRemoval.current = { sessionId, index }\n store.toggle(sessionId)\n }}\n t={t}\n />,\n sidebarHost,\n )\n : null\n}\n\ninterface PinnedSessionSectionProps {\n readonly actions: PinnedSessionActions\n readonly cancelRemoval: (sessionId: string) => void\n readonly currentId?: string | undefined\n readonly rows: readonly { readonly session: SessionSummaryLike; readonly title: string; readonly workspace: string }[]\n readonly open: (sessionId: string) => void\n readonly prepareRemoval: (sessionId: string, index: number) => void\n readonly unpin: (sessionId: string, index: number) => void\n readonly t: Translate\n}\n\nfunction PinnedSessionSection({ actions, cancelRemoval, currentId, rows, open, prepareRemoval, unpin, t }: PinnedSessionSectionProps): ReactNode {\n return <section className=\"dsh-pins-section\" aria-label={t('section.label')}>\n <div className=\"dsh-pins-header\">\n <PinIcon size={13} />\n <span>{t('section.label')}</span>\n </div>\n <ul className=\"dsh-pins-list\">\n {rows.map(({ session, title, workspace }, index) => <PinnedSessionRow\n actions={actions}\n cancelRemoval={cancelRemoval}\n current={session.id === currentId}\n index={index}\n key={session.id}\n open={open}\n prepareRemoval={prepareRemoval}\n session={session}\n t={t}\n title={title}\n unpin={unpin}\n workspace={workspace}\n />)}\n </ul>\n </section>\n}\n\ninterface PinnedSessionRowProps {\n readonly actions: PinnedSessionActions\n readonly cancelRemoval: (sessionId: string) => void\n readonly current: boolean\n readonly index: number\n readonly open: (sessionId: string) => void\n readonly prepareRemoval: (sessionId: string, index: number) => void\n readonly session: SessionSummaryLike\n readonly t: Translate\n readonly title: string\n readonly unpin: (sessionId: string, index: number) => void\n readonly workspace: string\n}\n\nfunction PinnedSessionRow({ actions, cancelRemoval, current, index, open, prepareRemoval, session, t, title, unpin, workspace }: PinnedSessionRowProps): ReactNode {\n const [menuOpen, setMenuOpen] = useState(false)\n const [renameOpen, setRenameOpen] = useState(false)\n const [renameDraft, setRenameDraft] = useState('')\n const [renaming, setRenaming] = useState(false)\n const [renameError, setRenameError] = useState<string | null>(null)\n const [deleteOpen, setDeleteOpen] = useState(false)\n const [deleting, setDeleting] = useState(false)\n const [deleteError, setDeleteError] = useState<string | null>(null)\n const [actionToast, setActionToast] = useState<{ readonly text: string; readonly seq: number } | null>(null)\n const toastSeq = useRef(0)\n const composing = useRef(false)\n const trigger = useRef<HTMLButtonElement | null>(null)\n const menusBeforeOpen = useRef<ReadonlySet<HTMLElement>>(new Set())\n const focusMenuEdge = useRef<'first' | 'last' | null>(null)\n const actionLabel = t('session.actions', { name: title })\n const renameTrimmed = renameDraft.trim()\n const renameBlocked = renaming || renameTrimmed === ''\n const canDelete = useSyncExternalStore(\n actions.deleteAvailability.subscribe,\n actions.deleteAvailability.getSnapshot,\n actions.deleteAvailability.getSnapshot,\n )\n const showDesktopActionError = (key: string, reason: unknown): void => {\n if (!canDelete) return\n toastSeq.current += 1\n setActionToast({\n text: t(key, { detail: reason instanceof Error ? reason.message : String(reason) }),\n seq: toastSeq.current,\n })\n }\n\n const restoreTriggerFocus = (): void => {\n queueMicrotask(() => { trigger.current?.focus({ preventScroll: true }) })\n }\n const openActionMenu = (focusEdge: 'first' | 'last' | null): void => {\n menusBeforeOpen.current = new Set(document.querySelectorAll<HTMLElement>('[role=\"menu\"]'))\n focusMenuEdge.current = focusEdge\n setMenuOpen(true)\n }\n\n useLayoutEffect(() => {\n if (!menuOpen) return\n let menu: HTMLElement | undefined\n let observer: MutationObserver | undefined\n let disposed = false\n const items = (): HTMLButtonElement[] => menu === undefined\n ? []\n : [...menu.querySelectorAll<HTMLButtonElement>('[role=\"menuitem\"]:not(:disabled)')]\n const onMenuKeyDown = (event: KeyboardEvent): void => {\n if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) return\n const available = items()\n if (available.length === 0) return\n const currentIndex = available.indexOf(document.activeElement as HTMLButtonElement)\n let nextIndex: number\n if (event.key === 'Home') nextIndex = 0\n else if (event.key === 'End') nextIndex = available.length - 1\n else if (event.key === 'ArrowDown') nextIndex = currentIndex < 0 ? 0 : (currentIndex + 1) % available.length\n else nextIndex = currentIndex <= 0 ? available.length - 1 : currentIndex - 1\n event.preventDefault()\n available[nextIndex]?.focus({ preventScroll: true })\n }\n const onDocumentKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') restoreTriggerFocus()\n }\n const install = (): void => {\n if (disposed) return\n menu = [...document.querySelectorAll<HTMLElement>('[role=\"menu\"]')]\n .find(candidate => !menusBeforeOpen.current.has(candidate))\n if (menu === undefined) return\n observer?.disconnect()\n menu.addEventListener('keydown', onMenuKeyDown)\n const edge = focusMenuEdge.current\n focusMenuEdge.current = null\n if (edge === null) return\n const available = items()\n const target = edge === 'first' ? available[0] : available.at(-1)\n queueMicrotask(() => {\n if (!disposed && target?.isConnected === true) target.focus({ preventScroll: true })\n })\n }\n document.addEventListener('keydown', onDocumentKeyDown, true)\n install()\n if (menu === undefined) {\n observer = new MutationObserver(install)\n observer.observe(document.body, { childList: true, subtree: true })\n install()\n }\n return () => {\n disposed = true\n observer?.disconnect()\n menu?.removeEventListener('keydown', onMenuKeyDown)\n document.removeEventListener('keydown', onDocumentKeyDown, true)\n }\n }, [menuOpen])\n\n const menuItems: MenuEntry[] = [\n { id: 'rename', label: t('menu.rename'), icon: <Pencil size={16} /> },\n { id: 'fork', label: t('menu.fork'), icon: <GitBranch size={16} /> },\n { id: 'unpin', label: t('menu.unpin'), icon: <PinOff size={16} strokeWidth={1.8} aria-hidden=\"true\" /> },\n { id: 'archive', label: t('menu.archive'), icon: <Archive size={16} /> },\n ...(canDelete ? [{ id: 'delete', label: t('menu.delete'), icon: <Trash2 size={16} />, danger: true }] : []),\n ]\n\n const closeRename = (): void => {\n if (renaming) return\n setRenameOpen(false)\n setRenameError(null)\n restoreTriggerFocus()\n }\n const confirmRename = (): void => {\n if (renameBlocked) return\n setRenaming(true)\n setRenameError(null)\n void actions.renameSession(session.id, renameTrimmed).then(() => {\n setRenaming(false)\n setRenameOpen(false)\n restoreTriggerFocus()\n }).catch(reason => {\n setRenaming(false)\n setRenameError(reason instanceof Error ? reason.message : String(reason))\n })\n }\n const closeDelete = (): void => {\n if (deleting) return\n setDeleteOpen(false)\n setDeleteError(null)\n restoreTriggerFocus()\n }\n const confirmDelete = (): void => {\n if (deleting) return\n prepareRemoval(session.id, index)\n setDeleting(true)\n setDeleteError(null)\n void actions.deleteSession(session.id).then(() => {\n setDeleting(false)\n setDeleteOpen(false)\n restoreTriggerFocus()\n }).catch(reason => {\n cancelRemoval(session.id)\n setDeleting(false)\n setDeleteError(reason instanceof Error ? reason.message : String(reason))\n })\n }\n const onSelect = (id: string): void => {\n setMenuOpen(false)\n if (id === 'rename') {\n setRenameDraft(session.title ?? title)\n setRenameError(null)\n setRenameOpen(true)\n }\n if (id === 'fork') {\n restoreTriggerFocus()\n void actions.forkSession(session.id).catch(reason => {\n showDesktopActionError('session.forkFailed', reason)\n })\n }\n if (id === 'unpin') unpin(session.id, index)\n if (id === 'archive') {\n prepareRemoval(session.id, index)\n restoreTriggerFocus()\n void actions.archiveSession(session.id).catch(reason => {\n cancelRemoval(session.id)\n restoreTriggerFocus()\n showDesktopActionError('session.archiveFailed', reason)\n if (!canDelete) console.warn('session archive rejected:', reason)\n })\n }\n if (id === 'delete' && canDelete) {\n setDeleteError(null)\n setDeleteOpen(true)\n }\n }\n\n return <>\n <li\n className=\"dsh-pins-row\"\n data-current={current ? 'true' : 'false'}\n data-menu-open={menuOpen ? 'true' : undefined}\n >\n <button\n type=\"button\"\n className=\"dsh-pins-open\"\n aria-current={current ? 'page' : undefined}\n aria-label={t('session.open', { name: title })}\n onClick={() => { open(session.id) }}\n >\n <PinIcon size={13} />\n <span className=\"dsh-pins-title\">{title}</span>\n <span className=\"dsh-pins-workspace\">{workspace}</span>\n </button>\n <Menu\n className=\"dsh-pins-action-menu\"\n open={menuOpen}\n onClose={() => { setMenuOpen(false) }}\n items={menuItems}\n onSelect={onSelect}\n portal\n closeOnPointerLeave\n anchor={<button\n ref={trigger}\n type=\"button\"\n className=\"dsh-pins-actions\"\n aria-expanded={menuOpen}\n aria-haspopup=\"menu\"\n aria-label={actionLabel}\n title={actionLabel}\n onClick={event => {\n event.stopPropagation()\n if (menuOpen) setMenuOpen(false)\n else openActionMenu(null)\n }}\n onKeyDown={event => {\n if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return\n event.preventDefault()\n openActionMenu(event.key === 'ArrowDown' ? 'first' : 'last')\n }}\n >\n <Ellipsis size={16} />\n </button>}\n />\n </li>\n <Modal\n open={renameOpen}\n onClose={closeRename}\n closeLabel={t('dialog.close')}\n title={t('rename.title')}\n footer={<>\n <Button variant=\"outline\" disabled={renaming} onClick={closeRename}>{t('dialog.cancel')}</Button>\n <Button variant=\"primary\" disabled={renameBlocked} onClick={confirmRename}>{t('rename.confirm')}</Button>\n </>}\n >\n <input\n className=\"dsh-pins-rename-input\"\n value={renameDraft}\n aria-label={t('rename.field')}\n autoFocus\n disabled={renaming}\n onFocus={event => { event.currentTarget.select() }}\n onChange={event => {\n setRenameDraft(event.currentTarget.value)\n setRenameError(null)\n }}\n onCompositionStart={() => { composing.current = true }}\n onCompositionEnd={() => { composing.current = false }}\n onKeyDown={event => {\n if (event.key !== 'Enter' || composing.current) return\n event.preventDefault()\n confirmRename()\n }}\n />\n {renameError !== null && <div className=\"dsh-pins-dialog-error\" role=\"alert\">{renameError}</div>}\n </Modal>\n <Modal\n open={deleteOpen}\n onClose={closeDelete}\n closeLabel={t('dialog.close')}\n title={t('delete.title')}\n description={t('delete.description', { name: title })}\n footer={<>\n <Button variant=\"outline\" autoFocus disabled={deleting} onClick={closeDelete}>{t('dialog.cancel')}</Button>\n <Button variant=\"outline\" className=\"dsh-pins-delete-action\" disabled={deleting} onClick={confirmDelete}>{t('delete.title')}</Button>\n </>}\n >\n {deleting && <div className=\"dsh-pins-dialog-status\" role=\"status\">{t('delete.pending')}</div>}\n {deleteError !== null && <div className=\"dsh-pins-dialog-error\" role=\"alert\">{deleteError}</div>}\n </Modal>\n {actionToast !== null && <Toast\n key={actionToast.seq}\n text={actionToast.text}\n onDone={() => { setActionToast(null) }}\n />}\n </>\n}\n\nfunction PinIcon({ size = 16 }: PinIconProps): ReactNode {\n return <Pin size={size} strokeWidth={1.8} aria-hidden=\"true\" />\n}\n","import { CamelToPascal } from './utility-types';\n\n/**\n * Converts string to kebab case\n *\n * @param {string} string\n * @returns {string} A kebabized string\n */\nexport const toKebabCase = (string: string) =>\n string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();\n\n/**\n * Converts string to camel case\n *\n * @param {string} string\n * @returns {string} A camelized string\n */\nexport const toCamelCase = <T extends string>(string: T) =>\n string.replace(/^([A-Z])|[\\s-_]+(\\w)/g, (match, p1, p2) =>\n p2 ? p2.toUpperCase() : p1.toLowerCase(),\n );\n\n/**\n * Converts string to pascal case\n *\n * @param {string} string\n * @returns {string} A pascalized string\n */\nexport const toPascalCase = <T extends string>(string: T): CamelToPascal<T> => {\n const camelCase = toCamelCase(string);\n\n return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal<T>;\n};\n\n/**\n * Merges classes into a single string\n *\n * @param {array} classes\n * @returns {string} A string of classes\n */\nexport const mergeClasses = <ClassType = string | undefined | null>(...classes: ClassType[]) =>\n classes\n .filter((className, index, array) => {\n return (\n Boolean(className) &&\n (className as string).trim() !== '' &&\n array.indexOf(className) === index\n );\n })\n .join(' ')\n .trim();\n","export default {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 24,\n height: 24,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n};\n","import { createElement, forwardRef } from 'react';\nimport defaultAttributes from './defaultAttributes';\nimport { IconNode, LucideProps } from './types';\nimport { mergeClasses } from '@lucide/shared';\n\ninterface IconComponentProps extends LucideProps {\n iconNode: IconNode;\n}\n\n/**\n * Lucide icon component\n *\n * @component Icon\n * @param {object} props\n * @param {string} props.color - The color of the icon\n * @param {number} props.size - The size of the icon\n * @param {number} props.strokeWidth - The stroke width of the icon\n * @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width\n * @param {string} props.className - The class name of the icon\n * @param {IconNode} props.children - The children of the icon\n * @param {IconNode} props.iconNode - The icon node of the icon\n *\n * @returns {ForwardRefExoticComponent} LucideIcon\n */\nconst Icon = forwardRef<SVGSVGElement, IconComponentProps>(\n (\n {\n color = 'currentColor',\n size = 24,\n strokeWidth = 2,\n absoluteStrokeWidth,\n className = '',\n children,\n iconNode,\n ...rest\n },\n ref,\n ) => {\n return createElement(\n 'svg',\n {\n ref,\n ...defaultAttributes,\n width: size,\n height: size,\n stroke: color,\n strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,\n className: mergeClasses('lucide', className),\n ...rest,\n },\n [\n ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),\n ...(Array.isArray(children) ? children : [children]),\n ],\n );\n },\n);\n\nexport default Icon;\n","import { createElement, forwardRef } from 'react';\nimport { mergeClasses, toKebabCase } from '@lucide/shared';\nimport { IconNode, LucideProps } from './types';\nimport Icon from './Icon';\n\n/**\n * Create a Lucide icon component\n * @param {string} iconName\n * @param {array} iconNode\n * @returns {ForwardRefExoticComponent} LucideIcon\n */\nconst createLucideIcon = (iconName: string, iconNode: IconNode) => {\n const Component = forwardRef<SVGSVGElement, LucideProps>(({ className, ...props }, ref) =>\n createElement(Icon, {\n ref,\n iconNode,\n className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className),\n ...props,\n }),\n );\n\n Component.displayName = `${iconName}`;\n\n return Component;\n};\n\nexport default createLucideIcon;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Archive\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cmVjdCB3aWR0aD0iMjAiIGhlaWdodD0iNSIgeD0iMiIgeT0iMyIgcng9IjEiIC8+CiAgPHBhdGggZD0iTTQgOHYxMWEyIDIgMCAwIDAgMiAyaDEyYTIgMiAwIDAgMCAyLTJWOCIgLz4KICA8cGF0aCBkPSJNMTAgMTJoNCIgLz4KPC9zdmc+Cg==) - https://lucide.dev/icons/archive\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Archive = createLucideIcon('Archive', [\n ['rect', { width: '20', height: '5', x: '2', y: '3', rx: '1', key: '1wp1u1' }],\n ['path', { d: 'M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8', key: '1s80jp' }],\n ['path', { d: 'M10 12h4', key: 'a56b0p' }],\n]);\n\nexport default Archive;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Ellipsis\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxIiAvPgogIDxjaXJjbGUgY3g9IjE5IiBjeT0iMTIiIHI9IjEiIC8+CiAgPGNpcmNsZSBjeD0iNSIgY3k9IjEyIiByPSIxIiAvPgo8L3N2Zz4K) - https://lucide.dev/icons/ellipsis\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Ellipsis = createLucideIcon('Ellipsis', [\n ['circle', { cx: '12', cy: '12', r: '1', key: '41hilf' }],\n ['circle', { cx: '19', cy: '12', r: '1', key: '1wjl8i' }],\n ['circle', { cx: '5', cy: '12', r: '1', key: '1pcz8c' }],\n]);\n\nexport default Ellipsis;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name GitBranch\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8bGluZSB4MT0iNiIgeDI9IjYiIHkxPSIzIiB5Mj0iMTUiIC8+CiAgPGNpcmNsZSBjeD0iMTgiIGN5PSI2IiByPSIzIiAvPgogIDxjaXJjbGUgY3g9IjYiIGN5PSIxOCIgcj0iMyIgLz4KICA8cGF0aCBkPSJNMTggOWE5IDkgMCAwIDEtOSA5IiAvPgo8L3N2Zz4K) - https://lucide.dev/icons/git-branch\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst GitBranch = createLucideIcon('GitBranch', [\n ['line', { x1: '6', x2: '6', y1: '3', y2: '15', key: '17qcm7' }],\n ['circle', { cx: '18', cy: '6', r: '3', key: '1h7g24' }],\n ['circle', { cx: '6', cy: '18', r: '3', key: 'fqmcym' }],\n ['path', { d: 'M18 9a9 9 0 0 1-9 9', key: 'n2h4wq' }],\n]);\n\nexport default GitBranch;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Pencil\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cGF0aCBkPSJNMjEuMTc0IDYuODEyYTEgMSAwIDAgMC0zLjk4Ni0zLjk4N0wzLjg0MiAxNi4xNzRhMiAyIDAgMCAwLS41LjgzbC0xLjMyMSA0LjM1MmEuNS41IDAgMCAwIC42MjMuNjIybDQuMzUzLTEuMzJhMiAyIDAgMCAwIC44My0uNDk3eiIgLz4KICA8cGF0aCBkPSJtMTUgNSA0IDQiIC8+Cjwvc3ZnPgo=) - https://lucide.dev/icons/pencil\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Pencil = createLucideIcon('Pencil', [\n [\n 'path',\n {\n d: 'M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z',\n key: '1a8usu',\n },\n ],\n ['path', { d: 'm15 5 4 4', key: '1mk7zo' }],\n]);\n\nexport default Pencil;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name PinOff\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cGF0aCBkPSJNMTIgMTd2NSIgLz4KICA8cGF0aCBkPSJNMTUgOS4zNFY3YTEgMSAwIDAgMSAxLTEgMiAyIDAgMCAwIDAtNEg3Ljg5IiAvPgogIDxwYXRoIGQ9Im0yIDIgMjAgMjAiIC8+CiAgPHBhdGggZD0iTTkgOXYxLjc2YTIgMiAwIDAgMS0xLjExIDEuNzlsLTEuNzguOUEyIDIgMCAwIDAgNSAxNS4yNFYxNmExIDEgMCAwIDAgMSAxaDExIiAvPgo8L3N2Zz4K) - https://lucide.dev/icons/pin-off\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst PinOff = createLucideIcon('PinOff', [\n ['path', { d: 'M12 17v5', key: 'bb1du9' }],\n ['path', { d: 'M15 9.34V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H7.89', key: 'znwnzq' }],\n ['path', { d: 'm2 2 20 20', key: '1ooewy' }],\n [\n 'path',\n {\n d: 'M9 9v1.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h11',\n key: 'c9qhm2',\n },\n ],\n]);\n\nexport default PinOff;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Pin\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cGF0aCBkPSJNMTIgMTd2NSIgLz4KICA8cGF0aCBkPSJNOSAxMC43NmEyIDIgMCAwIDEtMS4xMSAxLjc5bC0xLjc4LjlBMiAyIDAgMCAwIDUgMTUuMjRWMTZhMSAxIDAgMCAwIDEgMWgxMmExIDEgMCAwIDAgMS0xdi0uNzZhMiAyIDAgMCAwLTEuMTEtMS43OWwtMS43OC0uOUEyIDIgMCAwIDEgMTUgMTAuNzZWN2ExIDEgMCAwIDEgMS0xIDIgMiAwIDAgMCAwLTRIOGEyIDIgMCAwIDAgMCA0IDEgMSAwIDAgMSAxIDF6IiAvPgo8L3N2Zz4K) - https://lucide.dev/icons/pin\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Pin = createLucideIcon('Pin', [\n ['path', { d: 'M12 17v5', key: 'bb1du9' }],\n [\n 'path',\n {\n d: 'M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z',\n key: '1nkz8b',\n },\n ],\n]);\n\nexport default Pin;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Trash2\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cGF0aCBkPSJNMyA2aDE4IiAvPgogIDxwYXRoIGQ9Ik0xOSA2djE0YzAgMS0xIDItMiAySDdjLTEgMC0yLTEtMi0yVjYiIC8+CiAgPHBhdGggZD0iTTggNlY0YzAtMSAxLTIgMi0yaDRjMSAwIDIgMSAyIDJ2MiIgLz4KICA8bGluZSB4MT0iMTAiIHgyPSIxMCIgeTE9IjExIiB5Mj0iMTciIC8+CiAgPGxpbmUgeDE9IjE0IiB4Mj0iMTQiIHkxPSIxMSIgeTI9IjE3IiAvPgo8L3N2Zz4K) - https://lucide.dev/icons/trash-2\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Trash2 = createLucideIcon('Trash2', [\n ['path', { d: 'M3 6h18', key: 'd0wm0j' }],\n ['path', { d: 'M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6', key: '4alrt4' }],\n ['path', { d: 'M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2', key: 'v07s0e' }],\n ['line', { x1: '10', x2: '10', y1: '11', y2: '17', key: '1uufr5' }],\n ['line', { x1: '14', x2: '14', y1: '11', y2: '17', key: 'xtxkd' }],\n]);\n\nexport default Trash2;\n","import { SVGProps } from './types';\n\nconst defaultAttributes: SVGProps = {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 24,\n height: 24,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': 2,\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n};\n\nexport default defaultAttributes;\n","import defaultAttributes from '../defaultAttributes';\nimport type { IconNode } from '../types';\n\n/**\n * @name pin-off\n * @description Lucide SVG icon node.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cGF0aCBkPSJNMTIgMTd2NSIgLz4KICA8cGF0aCBkPSJNMTUgOS4zNFY3YTEgMSAwIDAgMSAxLTEgMiAyIDAgMCAwIDAtNEg3Ljg5IiAvPgogIDxwYXRoIGQ9Im0yIDIgMjAgMjAiIC8+CiAgPHBhdGggZD0iTTkgOXYxLjc2YTIgMiAwIDAgMS0xLjExIDEuNzlsLTEuNzguOUEyIDIgMCAwIDAgNSAxNS4yNFYxNmExIDEgMCAwIDAgMSAxaDExIiAvPgo8L3N2Zz4K) - https://lucide.dev/icons/pin-off\n * @see https://lucide.dev/guide/packages/lucide - Documentation\n *\n * @returns {Array}\n *\n */\nconst PinOff: IconNode = [\n 'svg',\n defaultAttributes,\n [\n ['path', { d: 'M12 17v5' }],\n ['path', { d: 'M15 9.34V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H7.89' }],\n ['path', { d: 'm2 2 20 20' }],\n ['path', { d: 'M9 9v1.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h11' }],\n ],\n];\n\nexport default PinOff;\n","import defaultAttributes from '../defaultAttributes';\nimport type { IconNode } from '../types';\n\n/**\n * @name pin\n * @description Lucide SVG icon node.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cGF0aCBkPSJNMTIgMTd2NSIgLz4KICA8cGF0aCBkPSJNOSAxMC43NmEyIDIgMCAwIDEtMS4xMSAxLjc5bC0xLjc4LjlBMiAyIDAgMCAwIDUgMTUuMjRWMTZhMSAxIDAgMCAwIDEgMWgxMmExIDEgMCAwIDAgMS0xdi0uNzZhMiAyIDAgMCAwLTEuMTEtMS43OWwtMS43OC0uOUEyIDIgMCAwIDEgMTUgMTAuNzZWN2ExIDEgMCAwIDEgMS0xIDIgMiAwIDAgMCAwLTRIOGEyIDIgMCAwIDAgMCA0IDEgMSAwIDAgMSAxIDF6IiAvPgo8L3N2Zz4K) - https://lucide.dev/icons/pin\n * @see https://lucide.dev/guide/packages/lucide - Documentation\n *\n * @returns {Array}\n *\n */\nconst Pin: IconNode = [\n 'svg',\n defaultAttributes,\n [\n ['path', { d: 'M12 17v5' }],\n [\n 'path',\n {\n d: 'M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z',\n },\n ],\n ],\n];\n\nexport default Pin;\n","import { Pin as PinNode, PinOff as PinOffNode, type IconNode } from 'lucide'\n\nexport const SIDEBAR_SLOT_SELECTOR = '[data-slot=\"sidebar.workspaces\"]'\nexport const PINNED_HOST_ATTRIBUTE = 'data-dsh-pinned-sessions-host'\nexport const MENU_HOST_ATTRIBUTE = 'data-dsh-pinned-session-menu-host'\nexport const MENU_OWNER_ATTRIBUTE = 'data-dsh-pinned-session-menu'\n\nexport interface SessionsForCapture {\n readonly list: {\n getSnapshot(): { readonly current?: string | undefined }\n }\n open(sessionId: string): void\n}\n\nexport interface SessionActionTarget {\n readonly button: HTMLButtonElement\n readonly row: HTMLElement\n}\n\nexport interface SessionMenuTarget {\n readonly host: HTMLDivElement\n readonly menu: HTMLElement\n readonly sessionId: string\n readonly button: HTMLButtonElement\n readonly icon: HTMLSpanElement\n readonly label: HTMLSpanElement\n}\n\nexport interface TriggerRect {\n readonly top: number\n readonly right: number\n readonly bottom: number\n readonly left: number\n readonly width: number\n readonly height: number\n}\n\nfunction elementFromTarget(target: EventTarget | null): Element | null {\n return target !== null && (target as Node).nodeType === 1 ? target as Element : null\n}\n\n/** Identify a native ellipsis trigger inside a real Session row. */\nexport function findSessionActionTarget(target: EventTarget | null): SessionActionTarget | null {\n const element = elementFromTarget(target)\n const button = element?.closest('button')\n if (!(button instanceof HTMLButtonElement)) return null\n const row = button.closest<HTMLElement>('[role=\"treeitem\"][aria-selected]')\n if (row === null || row === button) return null\n if (row.closest(SIDEBAR_SLOT_SELECTOR) === null) return null\n if (row.closest(`[${PINNED_HOST_ATTRIBUTE}]`) !== null) return null\n return { button, row }\n}\n\n/**\n * Ask the native row closure for its Session ID without changing selection.\n * The Workspace browser's row callback calls sessions.open(node.id), so a\n * synchronous, verified interceptor captures the exact ID and is restored\n * before the user's original ellipsis click continues.\n */\nexport function captureSessionId(row: HTMLElement, sessions: SessionsForCapture): string | null {\n const hadOwn = Object.prototype.hasOwnProperty.call(sessions, 'open')\n const ownDescriptor = hadOwn ? Object.getOwnPropertyDescriptor(sessions, 'open') : undefined\n const original = sessions.open\n let captured: string | null = null\n const interceptor = (sessionId: string): void => {\n if (typeof sessionId === 'string' && sessionId.length > 0) captured = sessionId\n }\n\n try {\n Object.defineProperty(sessions, 'open', {\n configurable: true,\n enumerable: ownDescriptor?.enumerable ?? false,\n writable: true,\n value: interceptor,\n })\n if (sessions.open !== interceptor) return null\n row.click()\n } catch {\n return null\n } finally {\n try {\n if (hadOwn && ownDescriptor !== undefined) Object.defineProperty(sessions, 'open', ownDescriptor)\n else delete (sessions as { open?: SessionsForCapture['open'] }).open\n } catch {\n try {\n Object.defineProperty(sessions, 'open', {\n configurable: true,\n writable: true,\n value: original,\n })\n } catch {\n // The verified patch path is configurable; this is a defensive fallback.\n }\n }\n }\n return captured\n}\n\n/** Insert the pinned region immediately after the native Workspace header. */\nexport function ensurePinnedHost(doc: Document): HTMLDivElement | null {\n const slot = doc.querySelector<HTMLElement>(SIDEBAR_SLOT_SELECTOR)\n const root = slot?.firstElementChild\n if (!(root instanceof HTMLElement)) return null\n const existing = root.querySelector<HTMLDivElement>(`:scope > [${PINNED_HOST_ATTRIBUTE}]`)\n if (existing !== null) return existing\n const header = root.firstElementChild\n if (header === null) return null\n const host = doc.createElement('div')\n host.setAttribute(PINNED_HOST_ATTRIBUTE, '')\n header.insertAdjacentElement('afterend', host)\n return host\n}\n\n/** List direct-body portal menus so one trigger can exclude every pre-existing menu. */\nexport function listPortalMenus(doc: Document): readonly HTMLElement[] {\n return [...doc.body.children].filter((node): node is HTMLElement => (\n node instanceof HTMLElement && node.getAttribute('role') === 'menu'\n ))\n}\n\nfunction belongsToTrigger(menu: HTMLElement, trigger: TriggerRect | undefined): boolean {\n if (trigger === undefined || trigger.width === 0 || trigger.height === 0) return true\n const menuRect = menu.getBoundingClientRect()\n if (menuRect.width === 0 || menuRect.height === 0) return true\n const horizontalGap = Math.min(\n Math.abs(menuRect.left - trigger.left),\n Math.abs(menuRect.right - trigger.right),\n )\n const verticalGap = Math.max(\n 0,\n menuRect.top - trigger.bottom,\n trigger.top - menuRect.bottom,\n )\n return horizontalGap <= 64 && verticalGap <= 32\n}\n\n/** Find only a new, geometrically associated portal menu after one trigger click. */\nexport function findUnclaimedPortalMenu(\n doc: Document,\n excluded: ReadonlySet<HTMLElement> = new Set(),\n trigger?: TriggerRect,\n): HTMLElement | null {\n const menus = listPortalMenus(doc).filter(menu => (\n !excluded.has(menu)\n && !menu.hasAttribute(MENU_OWNER_ATTRIBUTE)\n && belongsToTrigger(menu, trigger)\n ))\n return menus.at(-1) ?? null\n}\n\nfunction createLucideIcon(doc: Document, node: IconNode, name: string): SVGSVGElement {\n const [, attributes, children = []] = node\n const svg = doc.createElementNS('http://www.w3.org/2000/svg', 'svg')\n for (const [key, value] of Object.entries(attributes)) svg.setAttribute(key, String(value))\n svg.setAttribute('width', '16')\n svg.setAttribute('height', '16')\n svg.setAttribute('stroke-width', '1.8')\n svg.setAttribute('class', `lucide lucide-${name}`)\n svg.setAttribute('aria-hidden', 'true')\n for (const [tag, childAttributes] of children) {\n const child = doc.createElementNS('http://www.w3.org/2000/svg', tag)\n for (const [key, value] of Object.entries(childAttributes)) child.setAttribute(key, String(value))\n svg.appendChild(child)\n }\n return svg\n}\n\n/** Update the unmanaged item without moving it into the plugin's React tree. */\nexport function updateSessionMenuItem(target: SessionMenuTarget, pinned: boolean, label: string): void {\n target.button.setAttribute('aria-label', label)\n target.label.textContent = label\n target.icon.replaceChildren(createLucideIcon(\n target.menu.ownerDocument,\n pinned ? PinOffNode : PinNode,\n pinned ? 'pin-off' : 'pin',\n ))\n}\n\n/** Add an unmanaged native-styled row before the Session menu's archive tail. */\nexport function attachSessionMenuHost(menu: HTMLElement, sessionId: string): SessionMenuTarget | null {\n if (menu.hasAttribute(MENU_OWNER_ATTRIBUTE)) return null\n const viewport = [...menu.children].find((node): node is HTMLElement => (\n node instanceof HTMLElement && node.getAttribute('role') === 'presentation'\n ))\n if (viewport === undefined) return null\n const wrappers = [...viewport.children].filter((node): node is HTMLElement => (\n node instanceof HTMLElement\n && node.querySelector(':scope > button[role=\"menuitem\"]') instanceof HTMLButtonElement\n ))\n if (wrappers.length < 3 || wrappers.length > 4) return null\n const templateButton = wrappers[0]?.querySelector<HTMLButtonElement>(':scope > button[role=\"menuitem\"]')\n if (templateButton === null || templateButton === undefined) return null\n const spans = [...templateButton.children].filter((node): node is HTMLSpanElement => node instanceof HTMLSpanElement)\n if (spans.length < 2) return null\n\n const doc = menu.ownerDocument\n const host = doc.createElement('div')\n host.className = wrappers[0]?.className ?? ''\n host.setAttribute(MENU_HOST_ATTRIBUTE, '')\n const button = doc.createElement('button')\n button.type = 'button'\n button.setAttribute('role', 'menuitem')\n button.className = `${templateButton.className} dsh-pins-menu-button`\n const icon = doc.createElement('span')\n icon.className = spans[0]?.className ?? ''\n icon.setAttribute('aria-hidden', 'true')\n const label = doc.createElement('span')\n label.className = spans.at(-1)?.className ?? ''\n button.append(icon, label)\n host.appendChild(button)\n viewport.insertBefore(host, wrappers[2] ?? null)\n menu.setAttribute(MENU_OWNER_ATTRIBUTE, sessionId)\n\n const target = { host, menu, sessionId, button, icon, label }\n const view = doc.defaultView\n if (view !== null) view.dispatchEvent(new view.Event('resize'))\n return target\n}\n\n/** Restore useful keyboard focus after a pinned row removes itself. */\nexport function focusAfterPinnedRemoval(doc: Document, host: HTMLElement | null, removedIndex: number): boolean {\n const remaining = host === null ? [] : [...host.querySelectorAll<HTMLButtonElement>('.dsh-pins-open')]\n const next = remaining[Math.min(removedIndex, remaining.length - 1)]\n if (next !== undefined) {\n next.focus({ preventScroll: true })\n return true\n }\n const nativeRow = doc.querySelector<HTMLElement>(`${SIDEBAR_SLOT_SELECTOR} [role=\"treeitem\"][aria-selected=\"true\"]`)\n ?? doc.querySelector<HTMLElement>(`${SIDEBAR_SLOT_SELECTOR} [role=\"treeitem\"]`)\n if (nativeRow === null) return false\n const hadTabIndex = nativeRow.hasAttribute('tabindex')\n if (!hadTabIndex) nativeRow.tabIndex = -1\n nativeRow.focus({ preventScroll: true })\n if (doc.activeElement === nativeRow) return true\n if (!hadTabIndex) nativeRow.removeAttribute('tabindex')\n return false\n}\n\nexport function removeBridgeArtifacts(doc: Document): void {\n for (const node of doc.querySelectorAll(`[${PINNED_HOST_ATTRIBUTE}], [${MENU_HOST_ATTRIBUTE}]`)) node.remove()\n for (const menu of doc.querySelectorAll(`[${MENU_OWNER_ATTRIBUTE}]`)) menu.removeAttribute(MENU_OWNER_ATTRIBUTE)\n}\n","export const PIN_STORAGE_KEY = 'dsh.pinned-sessions.v1'\nconst MAX_PINS = 500\n\nexport interface PinRecord {\n readonly id: string\n readonly pinnedAt: number\n}\n\ninterface PersistedPins {\n readonly version: 1\n readonly pins: readonly PinRecord[]\n}\n\nexport interface StorageLike {\n getItem(key: string): string | null\n setItem(key: string, value: string): void\n}\n\nfunction isPinRecord(value: unknown): value is PinRecord {\n if (typeof value !== 'object' || value === null) return false\n const row = value as Record<string, unknown>\n return typeof row.id === 'string'\n && row.id.length > 0\n && Number.isFinite(row.pinnedAt)\n && (row.pinnedAt as number) >= 0\n}\n\n/** Decode, de-duplicate, and normalize one persisted pin snapshot. */\nexport function decodePins(raw: string | null): readonly PinRecord[] {\n if (raw === null) return []\n try {\n const parsed = JSON.parse(raw) as Partial<PersistedPins>\n if (parsed.version !== 1 || !Array.isArray(parsed.pins)) return []\n const byId = new Map<string, PinRecord>()\n for (const candidate of parsed.pins) {\n if (!isPinRecord(candidate)) continue\n const previous = byId.get(candidate.id)\n if (previous === undefined || candidate.pinnedAt > previous.pinnedAt) {\n byId.set(candidate.id, { id: candidate.id, pinnedAt: candidate.pinnedAt })\n }\n }\n return [...byId.values()]\n .sort((left, right) => right.pinnedAt - left.pinnedAt || left.id.localeCompare(right.id))\n .slice(0, MAX_PINS)\n } catch {\n return []\n }\n}\n\n/** Browser-local, versioned pin state with React-compatible subscriptions. */\nexport class PinStore {\n private snapshot: readonly PinRecord[]\n private readonly listeners = new Set<() => void>()\n\n constructor(private readonly storage: StorageLike | undefined) {\n this.snapshot = this.read()\n }\n\n readonly getSnapshot = (): readonly PinRecord[] => this.snapshot\n\n readonly subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n isPinned(id: string): boolean {\n return this.snapshot.some(row => row.id === id)\n }\n\n toggle(id: string, now = Date.now()): void {\n if (id.length === 0) return\n const without = this.snapshot.filter(row => row.id !== id)\n this.publish(this.isPinned(id) ? without : [{ id, pinnedAt: now }, ...without])\n }\n\n prune(keep: (id: string) => boolean): void {\n const next = this.snapshot.filter(row => keep(row.id))\n if (next.length !== this.snapshot.length) this.publish(next)\n }\n\n reload(): void {\n const next = this.read()\n if (samePins(this.snapshot, next)) return\n this.snapshot = next\n this.emit()\n }\n\n private read(): readonly PinRecord[] {\n try {\n return decodePins(this.storage?.getItem(PIN_STORAGE_KEY) ?? null)\n } catch {\n return []\n }\n }\n\n private publish(next: readonly PinRecord[]): void {\n this.snapshot = [...next]\n .sort((left, right) => right.pinnedAt - left.pinnedAt || left.id.localeCompare(right.id))\n .slice(0, MAX_PINS)\n try {\n this.storage?.setItem(PIN_STORAGE_KEY, JSON.stringify({ version: 1, pins: this.snapshot }))\n } catch {\n // Storage can be unavailable in hardened or quota-constrained browser profiles.\n }\n this.emit()\n }\n\n private emit(): void {\n for (const listener of [...this.listeners]) listener()\n }\n}\n\nfunction samePins(left: readonly PinRecord[], right: readonly PinRecord[]): boolean {\n return left.length === right.length\n && left.every((row, index) => row.id === right[index]?.id && row.pinnedAt === right[index]?.pinnedAt)\n}\n","interface SessionPresentationInput {\n readonly id: string\n readonly title?: string | undefined\n readonly cwd?: string | undefined\n}\n\nfunction nonBlank(value: string | undefined): string | undefined {\n const trimmed = value?.trim()\n return trimmed === undefined || trimmed.length === 0 ? undefined : trimmed\n}\n\n/** Match the native sidebar's useful fallback for sessions without a title. */\nexport function sessionDisplayTitle(\n session: SessionPresentationInput,\n workspaceTitle: string | undefined,\n): string {\n const explicit = nonBlank(session.title)\n if (explicit !== undefined) return explicit\n const workspace = nonBlank(workspaceTitle)\n if (workspace !== undefined) return workspace\n const cwd = nonBlank(session.cwd)\n if (cwd !== undefined) {\n const leaf = cwd.replace(/[\\\\/]+$/u, '').split(/[\\\\/]/u).at(-1)\n if (leaf !== undefined && leaf.length > 0) return leaf\n }\n return session.id\n}\n"],"mappings":"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","names":["import_react","defaultAttributes","PinOff","defaultAttributes","Pin","defaultAttributes","createLucideIcon","PinOff","Pin"],"file":"client.js"}
@@ -70,6 +70,7 @@ interface ClientContextLike {
70
70
  effect(setup: () => (() => void), label?: string): unknown;
71
71
  }
72
72
  export interface PinnedSessionActions {
73
+ readonly openSession?: (sessionId: string) => void;
73
74
  readonly renameSession: (sessionId: string, title: string) => Promise<void>;
74
75
  readonly forkSession: (sessionId: string) => Promise<void>;
75
76
  readonly archiveSession: (sessionId: string) => Promise<void>;
@@ -83,10 +84,11 @@ interface BridgeProps {
83
84
  readonly useSessions: SelectorHook<SessionsSnapshotLike>;
84
85
  readonly useWorkspaces: SelectorHook<WorkspacesSnapshotLike>;
85
86
  readonly t: Translate;
87
+ readonly modernMenus?: BooleanStoreLike;
86
88
  }
87
89
  export declare const inject: string[];
88
90
  /** Register the lifecycle bridge in the additive frame overlay slot. */
89
91
  export declare function apply(ctx: ClientContextLike): void;
90
92
  /** Keep native behavior intact while mounting the sidebar portal and unmanaged menu item. */
91
- export declare function PinnedSessionsBridge({ store, sessions, actions, useSessions, useWorkspaces, t }: BridgeProps): ReactNode;
93
+ export declare function PinnedSessionsBridge({ store, sessions, actions, useSessions, useWorkspaces, t, modernMenus }: BridgeProps): ReactNode;
92
94
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anionex/dsh-pinned-sessions",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Pin important DeepSeek Harness sessions at the top of the workspace sidebar",
5
5
  "repository": {
6
6
  "type": "git",
@@ -31,7 +31,8 @@
31
31
  "cordis.patch.yml",
32
32
  "README.md",
33
33
  "README.zh.md",
34
- "LICENSE"
34
+ "LICENSE",
35
+ "CHANGELOG.md"
35
36
  ],
36
37
  "dsh": {
37
38
  "bundle": {
@@ -49,10 +50,11 @@
49
50
  ]
50
51
  },
51
52
  "compatibility": {
52
- "dsh": ">=0.1.0-rc.8 <0.2.0",
53
+ "dsh": ">=0.1.0-rc.8 <0.2.0 || 0.2.0-rc.2",
53
54
  "dshReleases": {
54
55
  "0.1.1-rc.1": "compatible",
55
- "0.1.2-alpha.1": "compatible"
56
+ "0.1.2-alpha.1": "compatible",
57
+ "0.2.0-rc.2": "compatible"
56
58
  },
57
59
  "profiles": [
58
60
  "web",
@@ -76,7 +78,7 @@
76
78
  "license": "MIT",
77
79
  "peerDependencies": {
78
80
  "@deepseek-ai/cordis": "^4.0.1",
79
- "@deepseek-ai/dsh-client-ui-primitives": ">=0.1.0-rc.8 <0.2.0",
81
+ "@deepseek-ai/dsh-client-ui-primitives": ">=0.1.0-rc.8 <0.2.0 || 0.2.0-rc.2",
80
82
  "react": "^18.2.0",
81
83
  "react-dom": "^18.2.0"
82
84
  },
@@ -9,6 +9,7 @@ declare module '@deepseek-ai/dsh-client-ui-primitives' {
9
9
  }
10
10
 
11
11
  export type MenuEntry = MenuItem
12
+ export function MenuItemButton(props: { icon?: ReactNode; onSelect: () => void; children: ReactNode }): ReactNode
12
13
 
13
14
  export function Menu(props: {
14
15
  readonly open: boolean
@@ -9,17 +9,13 @@ import {
9
9
  } from 'react'
10
10
  import {
11
11
  Button,
12
- IconArchiveOutline20,
13
- IconBranchOutline16,
14
- IconEditOutline16,
15
- IconEllipsisOutline16,
16
- IconTrashOutline16,
17
12
  Menu,
13
+ MenuItemButton,
18
14
  Modal,
19
15
  Toast,
20
16
  type MenuEntry,
21
17
  } from '@deepseek-ai/dsh-client-ui-primitives'
22
- import { Pin, PinOff } from 'lucide-react'
18
+ import { Pin, PinOff, Archive, GitBranch, Pencil, Ellipsis, Trash2 } from 'lucide-react'
23
19
  import { createPortal } from 'react-dom'
24
20
  import {
25
21
  attachSessionMenuHost,
@@ -139,6 +135,7 @@ interface ClientContextLike {
139
135
  }
140
136
 
141
137
  export interface PinnedSessionActions {
138
+ readonly openSession?: (sessionId: string) => void
142
139
  readonly renameSession: (sessionId: string, title: string) => Promise<void>
143
140
  readonly forkSession: (sessionId: string) => Promise<void>
144
141
  readonly archiveSession: (sessionId: string) => Promise<void>
@@ -153,6 +150,7 @@ interface BridgeProps {
153
150
  readonly useSessions: SelectorHook<SessionsSnapshotLike>
154
151
  readonly useWorkspaces: SelectorHook<WorkspacesSnapshotLike>
155
152
  readonly t: Translate
153
+ readonly modernMenus?: BooleanStoreLike
156
154
  }
157
155
 
158
156
  interface PinIconProps {
@@ -183,8 +181,8 @@ const MENU_ASSOCIATION_MS = 2_000
183
181
  const zh = {
184
182
  'menu.rename': '重命名',
185
183
  'menu.fork': '分叉会话',
186
- 'menu.pin': '置顶会话',
187
- 'menu.unpin': '取消置顶',
184
+ 'menu.pin': '置顶到顶部分组',
185
+ 'menu.unpin': '从顶部分组取消置顶',
188
186
  'menu.archive': '归档会话',
189
187
  'menu.delete': '删除会话',
190
188
  'section.label': '置顶会话',
@@ -207,8 +205,8 @@ const zh = {
207
205
  const en = {
208
206
  'menu.rename': 'Rename',
209
207
  'menu.fork': 'Fork session',
210
- 'menu.pin': 'Pin session',
211
- 'menu.unpin': 'Unpin session',
208
+ 'menu.pin': 'Pin to top section',
209
+ 'menu.unpin': 'Unpin from top section',
212
210
  'menu.archive': 'Archive session',
213
211
  'menu.delete': 'Delete session',
214
212
  'section.label': 'Pinned sessions',
@@ -300,6 +298,27 @@ export function apply(ctx: ClientContextLike): void {
300
298
  const store = new PinStore(storage)
301
299
  const deleteAvailability = new BooleanStore()
302
300
  const actions = makePinnedSessionActions(ctx, deleteAvailability)
301
+ const modernMenus = new BooleanStore()
302
+ let openSession = (sessionId: string): void => { ctx.sessions.open(sessionId) }
303
+ const navigationActions = {
304
+ ...actions,
305
+ openSession: (id: string) => { openSession(id) },
306
+ forkSession: async (id: string) => { openSession(await ctx.sessions.fork({ sessionId: id, increaseTitle: true })) },
307
+ }
308
+ ctx.inject(['uiWorkspace'], navigation => {
309
+ const service = navigation.get('uiWorkspace') as { openSession(id: string): void }
310
+ openSession = id => { service.openSession(id) }
311
+ navigation.effect(() => () => { openSession = id => { ctx.sessions.open(id) } })
312
+ })
313
+
314
+ ctx.slots.inject('sidebar.workspaces.session.menu.item', () => {
315
+ modernMenus.set(true)
316
+ const dispose = ctx.slots.register({
317
+ name: 'sidebar.workspaces.session.menu.item', id: 'pinned-sessions', order: 90, locale: NS,
318
+ inject: () => ({ store }),
319
+ }, PinTopSectionMenuItem)
320
+ return () => { dispose(); modernMenus.set(false) }
321
+ })
303
322
 
304
323
  ctx.inject(['remote.workspaceRegistry'], serviceCtx => {
305
324
  const available = deleteSessionService(serviceCtx) !== null
@@ -332,12 +351,27 @@ export function apply(ctx: ClientContextLike): void {
332
351
  id: 'pinned-sessions-bridge',
333
352
  order: 40,
334
353
  locale: NS,
335
- inject: () => ({ store, sessions: ctx.sessions, actions }),
354
+ inject: () => ({ store, sessions: ctx.sessions, actions: navigationActions, modernMenus }),
336
355
  }, PinnedSessionsBridge))
337
356
  }
338
357
 
358
+ /** The official 0.2 menu slot supplies the exact Session identity. */
359
+ function PinTopSectionMenuItem({ sessionId, store, useMenuOpenState, t }: {
360
+ sessionId: string
361
+ store: PinStore
362
+ useMenuOpenState: () => [boolean, (open: boolean) => void]
363
+ t: Translate
364
+ }): ReactNode {
365
+ useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot)
366
+ const [, setMenuOpen] = useMenuOpenState()
367
+ const pinned = store.isPinned(sessionId)
368
+ return <MenuItemButton icon={<PinIcon />} onSelect={() => { store.toggle(sessionId); setMenuOpen(false) }}>
369
+ {t(pinned ? 'menu.unpin' : 'menu.pin')}
370
+ </MenuItemButton>
371
+ }
372
+
339
373
  /** Keep native behavior intact while mounting the sidebar portal and unmanaged menu item. */
340
- export function PinnedSessionsBridge({ store, sessions, actions, useSessions, useWorkspaces, t }: BridgeProps): ReactNode {
374
+ export function PinnedSessionsBridge({ store, sessions, actions, useSessions, useWorkspaces, t, modernMenus }: BridgeProps): ReactNode {
341
375
  const sessionSnapshot = useSessions(snapshot => snapshot)
342
376
  const workspaceSnapshot = useWorkspaces(snapshot => snapshot)
343
377
  const pins = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot)
@@ -435,6 +469,7 @@ export function PinnedSessionsBridge({ store, sessions, actions, useSessions, us
435
469
  }
436
470
  const onClickCapture = (event: MouseEvent): void => {
437
471
  pending.current = null
472
+ if (modernMenus?.getSnapshot()) return
438
473
  const target = findSessionActionTarget(event.target)
439
474
  if (target === null) return
440
475
  const sessionId = captureSessionId(target.row, sessions)
@@ -475,7 +510,7 @@ export function PinnedSessionsBridge({ store, sessions, actions, useSessions, us
475
510
  disposeActiveMenu()
476
511
  removeBridgeArtifacts(document)
477
512
  }
478
- }, [sessions, store, t])
513
+ }, [sessions, store, t, modernMenus])
479
514
 
480
515
  useLayoutEffect(() => {
481
516
  if (sidebarHost === null) return
@@ -528,7 +563,7 @@ export function PinnedSessionsBridge({ store, sessions, actions, useSessions, us
528
563
  }}
529
564
  currentId={sessionSnapshot.current}
530
565
  rows={rows}
531
- open={sessionId => { sessions.open(sessionId) }}
566
+ open={sessionId => { if (actions.openSession) actions.openSession(sessionId); else sessions.open(sessionId) }}
532
567
  prepareRemoval={(sessionId, index) => {
533
568
  focusAfterRemoval.current = { sessionId, index }
534
569
  }}
@@ -690,11 +725,11 @@ function PinnedSessionRow({ actions, cancelRemoval, current, index, open, prepar
690
725
  }, [menuOpen])
691
726
 
692
727
  const menuItems: MenuEntry[] = [
693
- { id: 'rename', label: t('menu.rename'), icon: <IconEditOutline16 /> },
694
- { id: 'fork', label: t('menu.fork'), icon: <IconBranchOutline16 /> },
728
+ { id: 'rename', label: t('menu.rename'), icon: <Pencil size={16} /> },
729
+ { id: 'fork', label: t('menu.fork'), icon: <GitBranch size={16} /> },
695
730
  { id: 'unpin', label: t('menu.unpin'), icon: <PinOff size={16} strokeWidth={1.8} aria-hidden="true" /> },
696
- { id: 'archive', label: t('menu.archive'), icon: <IconArchiveOutline20 size={16} /> },
697
- ...(canDelete ? [{ id: 'delete', label: t('menu.delete'), icon: <IconTrashOutline16 />, danger: true }] : []),
731
+ { id: 'archive', label: t('menu.archive'), icon: <Archive size={16} /> },
732
+ ...(canDelete ? [{ id: 'delete', label: t('menu.delete'), icon: <Trash2 size={16} />, danger: true }] : []),
698
733
  ]
699
734
 
700
735
  const closeRename = (): void => {
@@ -811,7 +846,7 @@ function PinnedSessionRow({ actions, cancelRemoval, current, index, open, prepar
811
846
  openActionMenu(event.key === 'ArrowDown' ? 'first' : 'last')
812
847
  }}
813
848
  >
814
- <IconEllipsisOutline16 />
849
+ <Ellipsis size={16} />
815
850
  </button>}
816
851
  />
817
852
  </li>