dsh-up-session 1.0.2 → 1.0.4
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 +17 -1
- package/README.en.md +27 -5
- package/README.md +27 -5
- package/client.js +124 -20
- package/index.js +3 -2
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
# 更新日志
|
|
2
2
|
|
|
3
|
+
## 1.0.4 - 2026-11-07
|
|
4
|
+
|
|
5
|
+
### 新增
|
|
6
|
+
|
|
7
|
+
- 置顶区和最近区的会话行可打开宿主会话菜单:右键或键盘菜单键可置顶、重命名、分叉、复制 Session ID、在文件管理器中显示和归档。菜单项与工作区列表中的同一条会话一致。
|
|
8
|
+
|
|
9
|
+
### 修复
|
|
10
|
+
|
|
11
|
+
- 将 DSH 最低兼容版本提高到 `0.2.2-alpha.3`,避免旧版 DSH 因缺少会话菜单接口而在安装插件后无法启动。
|
|
12
|
+
|
|
13
|
+
## 1.0.3 - 2026-10-26
|
|
14
|
+
|
|
15
|
+
### 变更
|
|
16
|
+
|
|
17
|
+
- 置顶区和最近区并入「工作区」列表顶部,成为工作区列表的前两个分组,随列表一起滚动;使用工作区搜索时暂时让位给搜索结果。
|
|
18
|
+
|
|
3
19
|
## 1.0.2 - 2026-10-26
|
|
4
20
|
|
|
5
21
|
### 修复
|
|
6
22
|
|
|
7
|
-
-
|
|
23
|
+
- DSH 兼容版本改为 `>=0.2.1-rc.2`。
|
|
8
24
|
|
|
9
25
|
## 1.0.1 - 2026-10-24
|
|
10
26
|
|
package/README.en.md
CHANGED
|
@@ -6,16 +6,18 @@
|
|
|
6
6
|
|
|
7
7
|
[简体中文](./README.md) | English
|
|
8
8
|
|
|
9
|
-
A simple DeepSeek Harness sidebar plugin: it inserts two
|
|
9
|
+
A simple DeepSeek Harness sidebar plugin: it inserts two groups, "Pinned" and "Recent", at the top of the "Workspaces" list, gathering pinned sessions from all workspaces into one place and collecting your latest sessions into another.
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<img src="./assets/screenshot.png" width="32%" alt="The sidebar showing the Pinned and Recent
|
|
12
|
+
<img src="./assets/screenshot.png" width="32%" alt="The sidebar showing the Pinned and Recent groups at the top of the Workspaces list">
|
|
13
13
|
<img src="./assets/settings.png" width="64%" alt="The plugin settings popup with toggles for Show Pinned, Show Recent, and Reveal the session in its workspace">
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
16
|
## Installation and compatibility
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
**Upgrade to DSH 0.2.2-alpha.3 or later. This plugin uses the session menu API introduced in that version.**
|
|
19
|
+
|
|
20
|
+
Plugin version 1.0.4 requires DSH `>=0.2.2-alpha.3`. Installing it on an earlier DSH version may prevent DSH from starting.
|
|
19
21
|
|
|
20
22
|
**Web**
|
|
21
23
|
|
|
@@ -29,19 +31,39 @@ Restart DSH after installation. If you use a custom profile, replace `web` with
|
|
|
29
31
|
|
|
30
32
|
Open **Plugins** in the sidebar, click **Add plugin**, enter `dsh-up-session`, and click **Enable now** after installation.
|
|
31
33
|
|
|
34
|
+
### Installing 1.0.3 on older DSH versions
|
|
35
|
+
|
|
36
|
+
If your DSH version is older than 0.2.2-alpha.3 and falls within plugin version 1.0.3's compatibility range (`>=0.2.1-rc.2`), install `dsh-up-session@1.0.3`.
|
|
37
|
+
|
|
38
|
+
**Web**
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
dsh plugin --profile web add dsh-up-session@1.0.3
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Restart DSH after installation. If you use a custom profile, replace `web` with its name.
|
|
45
|
+
|
|
46
|
+
**Desktop**
|
|
47
|
+
|
|
48
|
+
Open **Plugins** in the sidebar, click **Add plugin**, enter `dsh-up-session@1.0.3`, and click **Enable now** after installation.
|
|
49
|
+
|
|
32
50
|
## Pinned section
|
|
33
51
|
|
|
34
52
|
Gathers the pinned sessions of every workspace, newest activity first, and collapses like the host's own sections. Hovering a row reveals its archive and unpin buttons.
|
|
35
53
|
|
|
54
|
+
Right-clicking a pinned or recent row, or focusing it and pressing the menu key or Shift+F10, opens the same session menu as a row in the workspace list: pin or unpin, rename, fork, copy the Session ID, reveal in the file manager, and archive. The items are the host's own contributions, so they stay in step with the same session further down the list. Rename is prefilled with the session's own title; an untitled session is not prefilled with the "Untitled" label.
|
|
55
|
+
|
|
56
|
+
Both Pinned and Recent sit at the top of the workspaces list as its first two groups and scroll together with the workspace groups below. While the workspace search is open, they step aside so the list can show the results.
|
|
57
|
+
|
|
36
58
|
## Recent section
|
|
37
59
|
|
|
38
|
-
Sits between the pinned section and the
|
|
60
|
+
Sits between the pinned section and the workspace rows, lists the sessions that are not pinned, newest activity first, and shows the latest five by default. It looks and collapses exactly like the pinned section. Once there are more than five sessions, a "Show more" button appears at the bottom of the list:
|
|
39
61
|
|
|
40
62
|
- each click reveals five more sessions,
|
|
41
63
|
- once every session is on screen the button reads "Show less",
|
|
42
64
|
- clicking "Show less" folds the list back to the latest five.
|
|
43
65
|
|
|
44
|
-
Hovering a row reveals its archive and pin buttons. Pinned sessions appear only in the pinned section, so no conversation is listed twice.
|
|
66
|
+
Hovering a row reveals its archive and pin buttons. The context menu is the same as in the pinned section. Pinned sessions appear only in the pinned section, so no conversation is listed twice.
|
|
45
67
|
|
|
46
68
|
## Settings
|
|
47
69
|
|
package/README.md
CHANGED
|
@@ -6,16 +6,18 @@
|
|
|
6
6
|
|
|
7
7
|
简体中文 | [English](./README.en.md)
|
|
8
8
|
|
|
9
|
-
一个简单的 DeepSeek Harness
|
|
9
|
+
一个简单的 DeepSeek Harness 侧边栏插件:在「工作区」列表顶部插入「置顶」和「最近」两个分组,把分散在各个工作区里的置顶会话汇总到一处,也把最近的会话收拢到一处。
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<img src="./assets/screenshot.png" width="32%" alt="
|
|
12
|
+
<img src="./assets/screenshot.png" width="32%" alt="侧边栏「工作区」列表顶部依次显示置顶区与最近区">
|
|
13
13
|
<img src="./assets/settings.png" width="64%" alt="插件设置弹窗,包含显示置顶、显示最近和展开工作区中的原会话三个开关">
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
16
|
## 安装与兼容
|
|
17
17
|
|
|
18
|
-
DSH
|
|
18
|
+
**建议升级到 DSH 0.2.2-alpha.3 以上版本,本插件使用该版本提供的会话菜单接口。**
|
|
19
|
+
|
|
20
|
+
插件 1.0.4 的 DSH 兼容版本为 `>=0.2.2-alpha.3`,在更早的 DSH 版本上安装可能导致 DSH 无法启动。
|
|
19
21
|
|
|
20
22
|
**Web 端**
|
|
21
23
|
|
|
@@ -29,19 +31,39 @@ dsh plugin --profile web add dsh-up-session
|
|
|
29
31
|
|
|
30
32
|
打开侧边栏「插件」,点击「添加插件」,输入 `dsh-up-session`,安装完成后点击「立即启用」。
|
|
31
33
|
|
|
34
|
+
### 在旧版 DSH 上安装 1.0.3
|
|
35
|
+
|
|
36
|
+
如果你的 DSH 版本早于 0.2.2-alpha.3,且符合插件 1.0.3 的兼容范围 `>=0.2.1-rc.2`,请指定版本安装 `dsh-up-session@1.0.3`。
|
|
37
|
+
|
|
38
|
+
**Web 端**
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
dsh plugin --profile web add dsh-up-session@1.0.3
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
安装后重启 DSH。如果使用自定义 profile,将命令中的 `web` 替换为对应名称。
|
|
45
|
+
|
|
46
|
+
**桌面端**
|
|
47
|
+
|
|
48
|
+
打开侧边栏「插件」,点击「添加插件」,输入 `dsh-up-session@1.0.3`,安装完成后点击「立即启用」。
|
|
49
|
+
|
|
32
50
|
## 置顶区
|
|
33
51
|
|
|
34
52
|
汇总所有工作区里被置顶的会话,按最近更新时间排序,可展开收起。每行悬停时出现归档和取消置顶两个按钮。
|
|
35
53
|
|
|
54
|
+
在置顶区或最近区的会话行上右键,或聚焦该行后按菜单键 / Shift+F10,会打开与工作区列表相同的会话菜单(置顶或取消置顶、重命名、分叉会话、复制 Session ID、在文件管理器中显示、归档)。菜单项由宿主注册,因此和下面工作区里的同一条会话保持一致。重命名对话框预填的是会话自己的标题,未命名会话不会被填成「未命名」。
|
|
55
|
+
|
|
56
|
+
置顶区和最近区都位于「工作区」列表顶部,是该列表的前两个分组,会和下方的工作区分组一起滚动;使用「工作区」的搜索功能时,两个区暂时隐藏,把列表让给搜索结果。
|
|
57
|
+
|
|
36
58
|
## 最近区
|
|
37
59
|
|
|
38
|
-
|
|
60
|
+
位于置顶区与工作区列表之间,按最近更新时间排序显示尚未置顶的会话,默认显示最新的 5 条,样式与置顶区一致,同样可以展开收起。会话超过 5 条时,列表底部出现「展开更多」:
|
|
39
61
|
|
|
40
62
|
- 每点一次「展开更多」再显示 5 条;
|
|
41
63
|
- 全部会话都显示完后,按钮变为「收起」;
|
|
42
64
|
- 点击「收起」把列表收回到最新的 5 条。
|
|
43
65
|
|
|
44
|
-
|
|
66
|
+
每行悬停时出现归档和置顶两个按钮。右键菜单与置顶区相同。已置顶的会话只出现在置顶区,两区不会重复显示同一个会话。
|
|
45
67
|
|
|
46
68
|
## 设置
|
|
47
69
|
|
package/client.js
CHANGED
|
@@ -7,8 +7,8 @@ window.__ModuleLoader__.load({
|
|
|
7
7
|
|
|
8
8
|
/** Dictionary namespace owned by this plugin. */
|
|
9
9
|
const NS = 'upSession';
|
|
10
|
-
/** Services required to read pin state, render,
|
|
11
|
-
const inject = ['slots', 'locale', 'uiWorkspace'];
|
|
10
|
+
/** Services required to read pin state, render, pin, unpin, archive, and open the host session menu. */
|
|
11
|
+
const inject = ['slots', 'locale', 'uiWorkspace', 'contextMenus'];
|
|
12
12
|
|
|
13
13
|
/** Simplified Chinese dictionary and key source of truth. */
|
|
14
14
|
const zh = {
|
|
@@ -150,7 +150,7 @@ window.__ModuleLoader__.load({
|
|
|
150
150
|
// twice, so the section copies the real geometry instead of predicting it.
|
|
151
151
|
'.dshUS-mount{box-sizing:border-box;flex:none;display:flex;flex-direction:column;gap:4px;',
|
|
152
152
|
'padding-left:4px;margin:0 0 4px -4px}',
|
|
153
|
-
'.dshUS-mount[data-rail="true"]{display:none}',
|
|
153
|
+
'.dshUS-mount[data-rail="true"],.dshUS-mount[data-searching="true"]{display:none}',
|
|
154
154
|
'.dshUS-root{box-sizing:border-box;display:flex;flex-direction:column;gap:2px}',
|
|
155
155
|
// The header copies the host's "Workspaces" section header: same box, same
|
|
156
156
|
// 14px/20px label, same tertiary label colour. The whole row is the toggle,
|
|
@@ -204,7 +204,7 @@ window.__ModuleLoader__.load({
|
|
|
204
204
|
'border:0;text-align:left;width:100%}',
|
|
205
205
|
'.dshUS-row:hover{background:var(--dsw-alias-interactive-bg-hover)}',
|
|
206
206
|
// Matches the sidebar's own selected row (same surface, same label weight).
|
|
207
|
-
'.dshUS-current{background:var(--dsw-alias-interactive-bg-hover);',
|
|
207
|
+
'.dshUS-current,.dshUS-menuOpen{background:var(--dsw-alias-interactive-bg-hover);',
|
|
208
208
|
'color:var(--dsw-alias-label-primary)}',
|
|
209
209
|
'.dshUS-row:focus-visible{outline:var(--dsw-focus-ring-width,2px) solid ',
|
|
210
210
|
'var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary,#4d6bfe));outline-offset:-2px}',
|
|
@@ -290,7 +290,8 @@ window.__ModuleLoader__.load({
|
|
|
290
290
|
'color:var(--dsw-alias-label-caption,currentColor)}',
|
|
291
291
|
'.dshUS-row:hover .dshUS-actions,',
|
|
292
292
|
'.dshUS-row:focus-visible .dshUS-actions,',
|
|
293
|
-
'.dshUS-row:focus-within .dshUS-actions
|
|
293
|
+
'.dshUS-row:focus-within .dshUS-actions,',
|
|
294
|
+
'.dshUS-menuOpen .dshUS-actions{display:inline-flex}',
|
|
294
295
|
'.dshUS-action:hover{color:var(--dsw-alias-label-primary)}',
|
|
295
296
|
'.dshUS-action:focus-visible{outline:var(--dsw-focus-ring-width,2px) solid ',
|
|
296
297
|
'var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary,#4d6bfe));outline-offset:1px}',
|
|
@@ -303,7 +304,9 @@ window.__ModuleLoader__.load({
|
|
|
303
304
|
'.dshUS-row:focus-within .dshUS-time,',
|
|
304
305
|
'.dshUS-row:hover .dshUS-pinMark,',
|
|
305
306
|
'.dshUS-row:focus-visible .dshUS-pinMark,',
|
|
306
|
-
'.dshUS-row:focus-within .dshUS-pinMark
|
|
307
|
+
'.dshUS-row:focus-within .dshUS-pinMark,',
|
|
308
|
+
'.dshUS-menuOpen .dshUS-time,',
|
|
309
|
+
'.dshUS-menuOpen .dshUS-pinMark{display:none}',
|
|
307
310
|
// A touch surface never hovers, so the actions stay visible there and the
|
|
308
311
|
// time and marker stay hidden with them.
|
|
309
312
|
'@media (hover:none){.dshUS-actions{display:inline-flex}',
|
|
@@ -544,6 +547,8 @@ window.__ModuleLoader__.load({
|
|
|
544
547
|
const NO_STATUSES = new Map();
|
|
545
548
|
/** Stand-in for a host without the status hook, so the sections still render. */
|
|
546
549
|
const useNoSessionStatus = (selector) => selector(NO_STATUSES);
|
|
550
|
+
/** Used when the host has no context-menu target store; the row still opens the menu. */
|
|
551
|
+
const useNoContextMenuTarget = () => undefined;
|
|
547
552
|
|
|
548
553
|
/**
|
|
549
554
|
* The pending-interaction kind the sidebar renders, or undefined for a kind
|
|
@@ -917,6 +922,8 @@ window.__ModuleLoader__.load({
|
|
|
917
922
|
return {
|
|
918
923
|
id: summary.id,
|
|
919
924
|
title: summary.displayTitle || untitled,
|
|
925
|
+
// The host menu's rename draft is the raw title, not the untitled label.
|
|
926
|
+
menuTitle: summary.blank ? '' : summary.title?.trim() ?? '',
|
|
920
927
|
workspace: owner?.title ?? '',
|
|
921
928
|
// A known owner names its Workspace; once the list has loaded, a Session
|
|
922
929
|
// no Workspace accounts for sits in the "Ungrouped" bucket (`''`).
|
|
@@ -1009,8 +1016,8 @@ window.__ModuleLoader__.load({
|
|
|
1009
1016
|
/**
|
|
1010
1017
|
* The root of the plugin's sidebar surface: the pinned section, the recent
|
|
1011
1018
|
* section, and the one archive confirmation both sections can raise. It is
|
|
1012
|
-
* rendered through a portal into the host node the plugin keeps
|
|
1013
|
-
* the workspace
|
|
1019
|
+
* rendered through a portal into the host node the plugin keeps at the top
|
|
1020
|
+
* of the workspace list, inside the "Workspaces" section.
|
|
1014
1021
|
* @param props - composed slot props plus the injected host and actions.
|
|
1015
1022
|
* @returns the portal, or null when there is nowhere to portal into.
|
|
1016
1023
|
*/
|
|
@@ -1028,6 +1035,8 @@ window.__ModuleLoader__.load({
|
|
|
1028
1035
|
useSessionStatus,
|
|
1029
1036
|
usePanelInfo,
|
|
1030
1037
|
settingsEvents,
|
|
1038
|
+
openMenu,
|
|
1039
|
+
useContextMenuTarget,
|
|
1031
1040
|
} = props;
|
|
1032
1041
|
const pinnedSessionIds = useWorkspaces((state) => state.pinnedSessionIds);
|
|
1033
1042
|
const archivedSessionIds = useWorkspaces((state) => state.archivedSessionIds);
|
|
@@ -1149,10 +1158,18 @@ window.__ModuleLoader__.load({
|
|
|
1149
1158
|
t,
|
|
1150
1159
|
});
|
|
1151
1160
|
|
|
1161
|
+
// The host menu's captured target, so the row that opened it stays marked
|
|
1162
|
+
// after the pointer leaves. Undefined until a session menu is open.
|
|
1163
|
+
const readMenuTarget = useContextMenuTarget ?? useNoContextMenuTarget;
|
|
1164
|
+
const menuSessionId = readMenuTarget(
|
|
1165
|
+
(target) => (target?.kind === 'session' ? target.value.sessionId : undefined),
|
|
1166
|
+
);
|
|
1152
1167
|
const shared = {
|
|
1153
1168
|
currentId,
|
|
1154
1169
|
onOpen: openRow,
|
|
1155
1170
|
onArchive: raiseArchive,
|
|
1171
|
+
onOpenMenu: openMenu,
|
|
1172
|
+
menuSessionId,
|
|
1156
1173
|
onOpenSettings: () => setSettingsOpen(true),
|
|
1157
1174
|
settingsOpen,
|
|
1158
1175
|
t,
|
|
@@ -1240,29 +1257,62 @@ window.__ModuleLoader__.load({
|
|
|
1240
1257
|
* keyboard focus. A pinned row keeps the host's own pin marker where the
|
|
1241
1258
|
* time would end, so a pinned row looks pinned at rest exactly as the
|
|
1242
1259
|
* sidebar's rows do.
|
|
1243
|
-
* @param props - the row, the current Session id, the open handler, the trailing buttons, the pin marker, and the translator.
|
|
1260
|
+
* @param props - the row, the current Session id, the open handler, the host menu opener, the trailing buttons, the pin marker, and the translator.
|
|
1244
1261
|
* @returns the row element.
|
|
1245
1262
|
*/
|
|
1246
|
-
function SessionRow({ row, currentId, onOpen, actions, pinned, t }) {
|
|
1263
|
+
function SessionRow({ row, currentId, onOpen, onOpenMenu, menuOpen, actions, pinned, t }) {
|
|
1247
1264
|
const now = Date.now();
|
|
1248
1265
|
// A live status is the row's own leading cell, exactly as the sidebar
|
|
1249
1266
|
// draws it; an idle row draws none, so quiet rows keep the section's left
|
|
1250
1267
|
// edge instead of the sidebar's deeper indent.
|
|
1251
1268
|
const statusLabel = row.status === undefined ? undefined : statusText(row.status, t);
|
|
1269
|
+
// Same capture the host's session rows use: right-click, the Context Menu
|
|
1270
|
+
// key, or Shift+F10 open the shared session menu at the pointer or the row.
|
|
1271
|
+
const showMenu = (point, event) => {
|
|
1272
|
+
if (typeof onOpenMenu !== 'function') return;
|
|
1273
|
+
event.preventDefault();
|
|
1274
|
+
event.stopPropagation();
|
|
1275
|
+
if (event.currentTarget instanceof HTMLElement) event.currentTarget.focus({ preventScroll: true });
|
|
1276
|
+
onOpenMenu(row.id, row.menuTitle ?? '', point);
|
|
1277
|
+
};
|
|
1278
|
+
const className = ['dshUS-row', row.id === currentId ? 'dshUS-current' : '', menuOpen ? 'dshUS-menuOpen' : '']
|
|
1279
|
+
.filter(Boolean)
|
|
1280
|
+
.join(' ');
|
|
1252
1281
|
return h(
|
|
1253
1282
|
'div',
|
|
1254
1283
|
{
|
|
1255
|
-
className
|
|
1284
|
+
className,
|
|
1256
1285
|
role: 'button',
|
|
1257
1286
|
tabIndex: 0,
|
|
1258
1287
|
'aria-current': row.id === currentId ? 'true' : undefined,
|
|
1288
|
+
'aria-haspopup': 'menu',
|
|
1259
1289
|
title: row.workspace === '' ? row.title : `${row.title} · ${row.workspace}`,
|
|
1260
1290
|
'aria-label':
|
|
1261
1291
|
statusLabel === undefined
|
|
1262
1292
|
? t('openNamed', { title: row.title })
|
|
1263
1293
|
: t('openNamedStatus', { title: row.title, status: statusLabel }),
|
|
1264
1294
|
onClick: () => onOpen(row),
|
|
1295
|
+
onContextMenu: (event) => {
|
|
1296
|
+
const keyboard = event.clientX === 0 && event.clientY === 0;
|
|
1297
|
+
const rect = keyboard ? event.currentTarget.getBoundingClientRect() : undefined;
|
|
1298
|
+
showMenu(
|
|
1299
|
+
{ x: rect?.left ?? event.clientX, y: rect?.bottom ?? event.clientY },
|
|
1300
|
+
event,
|
|
1301
|
+
);
|
|
1302
|
+
},
|
|
1265
1303
|
onKeyDown: (event) => {
|
|
1304
|
+
if (
|
|
1305
|
+
!event.repeat &&
|
|
1306
|
+
!event.nativeEvent.isComposing &&
|
|
1307
|
+
!event.ctrlKey &&
|
|
1308
|
+
!event.altKey &&
|
|
1309
|
+
!event.metaKey &&
|
|
1310
|
+
(event.key === 'ContextMenu' || (event.key === 'F10' && event.shiftKey))
|
|
1311
|
+
) {
|
|
1312
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
1313
|
+
showMenu({ x: rect.left, y: rect.bottom }, event);
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1266
1316
|
if (event.key !== 'Enter' && event.key !== ' ') return;
|
|
1267
1317
|
event.preventDefault();
|
|
1268
1318
|
onOpen(row);
|
|
@@ -1337,6 +1387,8 @@ window.__ModuleLoader__.load({
|
|
|
1337
1387
|
onOpen,
|
|
1338
1388
|
onArchive,
|
|
1339
1389
|
onUnpin,
|
|
1390
|
+
onOpenMenu,
|
|
1391
|
+
menuSessionId,
|
|
1340
1392
|
onOpenSettings,
|
|
1341
1393
|
settingsOpen,
|
|
1342
1394
|
t,
|
|
@@ -1368,6 +1420,8 @@ window.__ModuleLoader__.load({
|
|
|
1368
1420
|
row,
|
|
1369
1421
|
currentId,
|
|
1370
1422
|
onOpen,
|
|
1423
|
+
onOpenMenu,
|
|
1424
|
+
menuOpen: menuSessionId === row.id,
|
|
1371
1425
|
t,
|
|
1372
1426
|
// Every row here is pinned, so every row wears the marker.
|
|
1373
1427
|
pinned: true,
|
|
@@ -1399,6 +1453,8 @@ window.__ModuleLoader__.load({
|
|
|
1399
1453
|
onOpen,
|
|
1400
1454
|
onArchive,
|
|
1401
1455
|
onPin,
|
|
1456
|
+
onOpenMenu,
|
|
1457
|
+
menuSessionId,
|
|
1402
1458
|
onOpenSettings,
|
|
1403
1459
|
settingsOpen,
|
|
1404
1460
|
t,
|
|
@@ -1447,6 +1503,8 @@ window.__ModuleLoader__.load({
|
|
|
1447
1503
|
row,
|
|
1448
1504
|
currentId,
|
|
1449
1505
|
onOpen,
|
|
1506
|
+
onOpenMenu,
|
|
1507
|
+
menuOpen: menuSessionId === row.id,
|
|
1450
1508
|
t,
|
|
1451
1509
|
// No row here is pinned, so no row wears the marker and the
|
|
1452
1510
|
// time is the row's trailing cell, flush with its edge.
|
|
@@ -1838,11 +1896,32 @@ window.__ModuleLoader__.load({
|
|
|
1838
1896
|
}
|
|
1839
1897
|
|
|
1840
1898
|
/**
|
|
1841
|
-
*
|
|
1842
|
-
*
|
|
1843
|
-
*
|
|
1844
|
-
*
|
|
1845
|
-
*
|
|
1899
|
+
* The scroll box holding the workspace list. In the grouped and flat views
|
|
1900
|
+
* the `role="tree"` box scrolls itself; while searching, the results tree
|
|
1901
|
+
* sits one level inside that same box, so the nearest scrolling ancestor is
|
|
1902
|
+
* the seat either way. The sections live at the top of this box, inside the
|
|
1903
|
+
* "Workspaces" section, and scroll away with the workspace rows instead of
|
|
1904
|
+
* taking a fixed slice of the sidebar above them.
|
|
1905
|
+
* @param anchor - the Workspaces slot anchor.
|
|
1906
|
+
* @returns the scroll box, or null while the sidebar renders no list.
|
|
1907
|
+
*/
|
|
1908
|
+
function workspaceScrollBox(anchor) {
|
|
1909
|
+
const tree = anchor.querySelector('[role="tree"]');
|
|
1910
|
+
if (!(tree instanceof Element)) return null;
|
|
1911
|
+
for (let node = tree; node !== null && node !== anchor; node = node.parentElement) {
|
|
1912
|
+
const { overflowY } = getComputedStyle(node);
|
|
1913
|
+
if (overflowY === 'auto' || overflowY === 'scroll') return node;
|
|
1914
|
+
}
|
|
1915
|
+
return null;
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
/**
|
|
1919
|
+
* Keep the host node at the top of the workspace list. Both sections share
|
|
1920
|
+
* that node and stack inside it, so they read as the first two groups of
|
|
1921
|
+
* the "Workspaces" section rather than as sections of their own. Hiding in
|
|
1922
|
+
* the collapsed rail mirrors the shell's own rule, and the list cannot hold
|
|
1923
|
+
* the sections while the user is searching its rows, so they step aside for
|
|
1924
|
+
* the results.
|
|
1846
1925
|
* @param host - the container the portal renders into.
|
|
1847
1926
|
* @returns the disposer that detaches the node and its observers.
|
|
1848
1927
|
*/
|
|
@@ -1852,12 +1931,25 @@ window.__ModuleLoader__.load({
|
|
|
1852
1931
|
|
|
1853
1932
|
const place = () => {
|
|
1854
1933
|
const anchor = document.querySelector('[data-slot="sidebar.workspaces"]');
|
|
1855
|
-
if (!(anchor instanceof Element))
|
|
1934
|
+
if (!(anchor instanceof Element)) {
|
|
1935
|
+
host.remove();
|
|
1936
|
+
return;
|
|
1937
|
+
}
|
|
1856
1938
|
const parent = anchor.parentElement;
|
|
1857
1939
|
if (parent === null) return;
|
|
1858
|
-
|
|
1859
|
-
|
|
1940
|
+
const seat = workspaceScrollBox(anchor);
|
|
1941
|
+
if (seat === null) {
|
|
1942
|
+
host.remove();
|
|
1943
|
+
return;
|
|
1944
|
+
}
|
|
1945
|
+
if (host.parentElement !== seat || seat.firstElementChild !== host) {
|
|
1946
|
+
seat.insertBefore(host, seat.firstChild);
|
|
1860
1947
|
}
|
|
1948
|
+
// Search results replace the list's rows; the sections would otherwise
|
|
1949
|
+
// sit above those results as unrelated pinned and recent rows.
|
|
1950
|
+
const search = anchor.querySelector('input[type="text"]');
|
|
1951
|
+
host.dataset.searching =
|
|
1952
|
+
search instanceof HTMLInputElement && search.value.trim() !== '' ? 'true' : 'false';
|
|
1861
1953
|
if (resize === undefined) {
|
|
1862
1954
|
// The seat spans the sidebar's inner width, which is narrower than the
|
|
1863
1955
|
// column by its inline padding; the rail seat is far narrower still.
|
|
@@ -1891,7 +1983,7 @@ window.__ModuleLoader__.load({
|
|
|
1891
1983
|
};
|
|
1892
1984
|
}
|
|
1893
1985
|
|
|
1894
|
-
/** Contribute the pinned and recent sections
|
|
1986
|
+
/** Contribute the pinned and recent sections inside the workspace list. */
|
|
1895
1987
|
function apply(ctx) {
|
|
1896
1988
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'up-session: dictionaries');
|
|
1897
1989
|
const t = ctx.locale.bind(NS);
|
|
@@ -1954,6 +2046,18 @@ window.__ModuleLoader__.load({
|
|
|
1954
2046
|
}
|
|
1955
2047
|
},
|
|
1956
2048
|
stopAndArchive: (sessionId) => ctx.uiWorkspace.archiveSession(sessionId, { stopActivity: true }),
|
|
2049
|
+
// The sidebar's own session menu (pin, rename, fork, copy id, Finder, archive).
|
|
2050
|
+
// Opening it here reuses every contribution registered for target "session".
|
|
2051
|
+
openMenu: (sessionId, displayTitle, point) => {
|
|
2052
|
+
ctx.contextMenus.open(
|
|
2053
|
+
{
|
|
2054
|
+
kind: 'session',
|
|
2055
|
+
value: { sessionId, displayTitle },
|
|
2056
|
+
},
|
|
2057
|
+
point,
|
|
2058
|
+
);
|
|
2059
|
+
},
|
|
2060
|
+
hooks: { contextMenuTarget: ctx.contextMenus.active },
|
|
1957
2061
|
});
|
|
1958
2062
|
|
|
1959
2063
|
ctx.slots.inject('shell.overlay', () =>
|
package/index.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Host half of the dsh-up-session bundle.
|
|
3
3
|
*
|
|
4
4
|
* The plugin is a pure sidebar decoration: it reads pin state through the
|
|
5
|
-
* Client's workspace controller and renders
|
|
6
|
-
* no Host-side behavior, so this module only satisfies the bundle's
|
|
5
|
+
* Client's workspace controller and renders at the top of the workspace list.
|
|
6
|
+
* There is no Host-side behavior, so this module only satisfies the bundle's
|
|
7
|
+
* Host row.
|
|
7
8
|
*/
|
|
8
9
|
export function apply() {}
|
package/locale/en.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"title": "dsh-up-session",
|
|
4
|
-
"description": "Adds pinned and recent
|
|
4
|
+
"description": "Adds pinned and recent groups at the top of the workspace list in the sidebar, gathering pinned and recent sessions. Rows open the same context menu as the workspace list, and each group header has a settings popup."
|
|
5
5
|
}
|
|
6
6
|
}
|
package/locale/zh.json
CHANGED
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-up-session",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Adds pinned and recent session
|
|
5
|
+
"description": "Adds pinned and recent session groups at the top of the workspace list in the DSH sidebar. Rows open the host session menu, and each group header has a settings popup.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "kyrsieger",
|
|
8
8
|
"repository": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"CHANGELOG.md"
|
|
43
43
|
],
|
|
44
44
|
"engines": {
|
|
45
|
-
"dsh": ">=0.2.
|
|
45
|
+
"dsh": ">=0.2.2-alpha.3"
|
|
46
46
|
},
|
|
47
47
|
"dsh": {
|
|
48
48
|
"bundle": {
|