@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 +41 -0
- package/README.md +4 -4
- package/README.zh.md +4 -4
- package/lib/client.js +110 -13
- package/lib/client.js.map +1 -1
- package/lib/types/client/index.d.ts +3 -1
- package/package.json +7 -5
- package/src/client/dsh-primitives.d.ts +1 -0
- package/src/client/index.tsx +54 -19
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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\
|
|
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
|
|
487
|
-
"menu.unpin": "Unpin
|
|
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
|
|
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
|
-
|
|
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)(
|
|
940
|
-
{ id: "fork", label: t("menu.fork"), icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
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)(
|
|
943
|
-
...canDelete ? [{ id: "delete", label: t("menu.delete"), icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
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)(
|
|
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  - 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  - 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  - 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  - 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  - 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  - 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  - 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  - 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  - 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  - 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  - 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  - 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  - 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.
|
|
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
|
package/src/client/index.tsx
CHANGED
|
@@ -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
|
|
211
|
-
'menu.unpin': 'Unpin
|
|
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: <
|
|
694
|
-
{ id: 'fork', label: t('menu.fork'), icon: <
|
|
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: <
|
|
697
|
-
...(canDelete ? [{ id: 'delete', label: t('menu.delete'), icon: <
|
|
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
|
-
<
|
|
849
|
+
<Ellipsis size={16} />
|
|
815
850
|
</button>}
|
|
816
851
|
/>
|
|
817
852
|
</li>
|