@qilin-agent/client-ui-workspace 3.1.7 → 3.1.9
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/README.i18n.yaml +4 -4
- package/README.md +5 -1
- package/README.zh.md +5 -1
- package/lib/client.js +283 -27
- package/lib/types/client/context-menus.d.ts +28 -0
- package/lib/types/client/contract/slots.d.ts +29 -0
- package/lib/types/client/rows/Rows.d.ts +18 -2
- package/lib/types/client/rows/WorkspaceBrowser.d.ts +1 -1
- package/package.json +26 -24
package/README.i18n.yaml
CHANGED
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
en: 5e049b90c55eddb3
|
|
28
28
|
zh: e06dc8d0d25c013f
|
|
29
29
|
/qilin-agent-client-ui-workspace/use-this-package/search:
|
|
30
|
-
en:
|
|
31
|
-
zh:
|
|
30
|
+
en: 77fd4ac670f676cf
|
|
31
|
+
zh: 89277e09cafa0af4
|
|
32
32
|
/qilin-agent-client-ui-workspace/use-this-package/managing-sessions:
|
|
33
33
|
en: 36a910e6ad337e1c
|
|
34
34
|
zh: 9607173765bac0c7
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
en: 745e917ae00da6ab
|
|
49
49
|
zh: 32c745f9078829b8
|
|
50
50
|
/qilin-agent-client-ui-workspace/understand-the-implementation/view-state:
|
|
51
|
-
en:
|
|
52
|
-
zh:
|
|
51
|
+
en: 4b7f2f65d6259d33
|
|
52
|
+
zh: 7bbccd66050725f4
|
|
53
53
|
/qilin-agent-client-ui-workspace/understand-the-implementation/hover-cards:
|
|
54
54
|
en: f236cadca4c98088
|
|
55
55
|
zh: 8b36850c1d5f73d2
|
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ Hierarchy uses registered canonical paths only. It does not scan for projects or
|
|
|
43
43
|
|
|
44
44
|
### Search
|
|
45
45
|
|
|
46
|
-
Collapsed search is one header action beside the view and add actions: activating it expands the field across the header. A non-blank query replaces either browsing mode with one flat result list — case-insensitive title and Workspace substring matches appear immediately, while a 250 ms debounced Host request adds ranked current-conversation content matches and snippets. Each new query aborts the preceding request; a failed content search leaves metadata matches visible with a warning. The list is capped at 20. Choosing a result clears and collapses search, opens the Session, and scrolls its row into view in the configured browsing mode; grouped browsing also expands its Workspace and the full Session list when required.
|
|
46
|
+
Collapsed search is one header action beside the view and add actions: activating it expands the field across the header. A non-blank query replaces either browsing mode with one flat result list — case-insensitive title and Workspace substring matches appear immediately, while a 250 ms debounced Host request adds ranked current-conversation content matches and snippets. Each new query aborts the preceding request; a failed content search leaves metadata matches visible with a warning. The list is capped at 20. Choosing a result clears and collapses search, opens the Session, and scrolls its row into view in the configured browsing mode; grouped browsing also expands its Workspace and the full Session list when required. Opening or closing search in the expanded sidebar animates the header's trailing inset, inline padding, and gap together with the field over 180 ms, and a reduced-motion preference settles them at once.
|
|
47
47
|
|
|
48
48
|
### Managing sessions
|
|
49
49
|
|
|
@@ -89,8 +89,12 @@ The chip occupies the conversation hero's `conversation.hero.agentPreset` hole a
|
|
|
89
89
|
|
|
90
90
|
### View state
|
|
91
91
|
|
|
92
|
+
Workspace and Session rows take their metrics from the properties the sidebar root inherits to this region. The expanded-row padding minus the paint inset sets the leading cell, the label gap spaces the cells, and the inset itself rides a transparent border so the hover fill stays inset without moving content; a Session row shows the same gap on its title margin instead of the row. Trailing actions sit 13 px apart on 16 px control cells, and each button carries a 24 px click target centred by a -4 px margin. The time label pulls 1 px so its edge matches the action strip, the section label keeps a 1 px left offset, and Session insertion markers compensate the row's transparent border. On a coarse pointer the hover-only verb strip is always drawn, the archive verb yields its cell to the menu and pin verbs (it stays in the row menu), a tapped row keeps no hover fill, and a Workspace row toggles from Enter or Space.
|
|
93
|
+
|
|
92
94
|
Once the Workspace list baseline is ready, browser-persisted expansion and manual Session-order records retain only current Workspace ids plus Ungrouped and the flat-list account. `WorkspaceView.sessionIds` supplies real-Workspace membership, not Session display order. View actions require the current account orders explicitly. Flat-list membership and ordering use Session ids; row rendering adds status indicators once. Entering Manual snapshots every active account from the current display; reconciliation retains saved members that still belong to the account, removes departed members, and appends newly known members by recency. A new membership entry without a Session summary is omitted until that summary arrives, while an already saved slot survives a temporarily missing summary. During Workspace reconnection, Manual records an observed blank Session at the front of its saved flat and known group orders without removing other saved members; full membership reconciliation waits for the Workspace baseline. This reconciliation remains mounted while the sidebar is a rail or search replaces its body. Last updated derives directly from each current list snapshot without reading or writing saved positions; equal timestamps use Session ids as a stable tie-break. The shared sidebar projection hides rows whose durable Session summary has `origin: 'subagent'`, and each visible ordinary row inherits the blue activity indicator while any descendant reached through uninterrupted subagent-origin lineage is running. The same pure derivation reads the Schedule key from list projection values for grouped, flat, and search nodes; the package uses only the type-only `@qilin-agent/schedule/client` dependency and does not import the Schedule runtime or `ui-schedule`.
|
|
93
95
|
|
|
96
|
+
Row motion belongs to [AnimatedRows](../ui-primitives/src/AnimatedRows.tsx). The grouped tree and the flat list render through it and key every row with `data-row-key` (`workspace:<key>`, `session:<id>`, `overflow:<group>`, `empty`); it measures keyed rows before and after commits that change membership or order, glides and fades them with native animations, and paints removed rows as inert copies outside the scrolling list. Initial loading, drag commits, overflow expansion, and view-option changes settle immediately, and a reduced-motion preference skips the animation.
|
|
97
|
+
|
|
94
98
|
### Hover cards
|
|
95
99
|
|
|
96
100
|
Workspace and Session hover cards copy the value their row clips: activating a Workspace card writes its full directory path, while activating a non-blank Session card writes its full display title. A provisional blank New Session card remains read-only because its localized label is a placeholder rather than session content.
|
package/README.zh.md
CHANGED
|
@@ -43,7 +43,7 @@ kind: "package-reference"
|
|
|
43
43
|
|
|
44
44
|
### 搜索
|
|
45
45
|
|
|
46
|
-
折叠搜索是视图和添加操作旁的一枚区头按钮:激活后输入框会扩展并占据区头。非空白查询会以单一扁平结果列表替代任一浏览模式——不区分大小写的标题和 Workspace 子串匹配项会立即显示,经 250 ms 防抖的 Host 请求则会加入经过排序的当前对话内容匹配项及其摘要片段。每次新查询都会中止前一个请求;内容搜索失败时,元数据匹配项仍会显示,同时给出警告。列表最多显示 20 条结果。选择结果会清空并收起搜索、打开 Session,并在当前浏览模式中将其行滚动到可见区域;分组浏览还会按需展开所属 Workspace 和完整 Session
|
|
46
|
+
折叠搜索是视图和添加操作旁的一枚区头按钮:激活后输入框会扩展并占据区头。非空白查询会以单一扁平结果列表替代任一浏览模式——不区分大小写的标题和 Workspace 子串匹配项会立即显示,经 250 ms 防抖的 Host 请求则会加入经过排序的当前对话内容匹配项及其摘要片段。每次新查询都会中止前一个请求;内容搜索失败时,元数据匹配项仍会显示,同时给出警告。列表最多显示 20 条结果。选择结果会清空并收起搜索、打开 Session,并在当前浏览模式中将其行滚动到可见区域;分组浏览还会按需展开所属 Workspace 和完整 Session 列表。在展开的侧边栏中,打开或收起搜索会以 180 ms 动画一并改变区头的尾部内缩、行内内边距和间距,开启减少动态效果时则立即落位。
|
|
47
47
|
|
|
48
48
|
### 管理会话
|
|
49
49
|
|
|
@@ -89,8 +89,12 @@ chip 占据会话英雄区的 `conversation.hero.agentPreset` 槽位,把工作
|
|
|
89
89
|
|
|
90
90
|
### 视图状态
|
|
91
91
|
|
|
92
|
+
Workspace 与 Session 行采用侧边栏根节点继承给本区域的属性:展开态行内边距减去底色内缩决定前置单元格,标签间距决定单元格间隔,内缩本身由一条透明边框承载,因此悬停底色保持内缩而内容不移动;Session 行不在行上留该间距,而是在标题外边距上留同样的值。行尾操作按钮以 13 px 间隔排在 16 px 控制格上,每个按钮以 -4 px 补偿外边距承载居中的 24 px 点击热区。时间标签左收 1 px 以与操作条边缘对齐,区头标签保留 1 px 左偏移,Session 插入标记补偿该透明边框。粗指针(触屏)上仅悬停可见的操作条始终显示,归档动词把位置让给菜单与置顶动词(仍保留在行菜单里),被点按的行不会残留悬停底色,Workspace 行可用 Enter 或空格切换折叠。
|
|
93
|
+
|
|
92
94
|
Workspace 列表基线就绪后,浏览器持久化的展开状态和手动 Session 顺序记录只保留当前 Workspace id、Ungrouped 和单列表记账。`WorkspaceView.sessionIds` 提供真实 Workspace 的成员关系,而不提供 Session 显示顺序。视图操作要求显式传入当前各记账的顺序。单列表的成员筛选和排序使用 Session id,行渲染只计算一次状态指示。进入手动排序会从当前显示结果一次性记录每个有效记账;对账会保留仍属于该记账的已保存成员、移除已经离开的成员,并按最近更新时间追加新发现的成员。尚无 Session 摘要的新成员会等摘要到达后再加入,而已经保存的位置在摘要暂时缺失时仍会保留。Workspace 重连期间,手动排序会将已观察到的空白 Session 记录到已保存的单列表及已知分组顺序首位,不移除其他已保存成员;完整成员对账等待 Workspace 基线到齐。即使侧边栏收成窄栏或搜索替代列表主体,这项对账也保持挂载。最近更新直接从每份当前列表快照派生,不读取或写入已保存位置;时间相同时按 Session id 稳定排序。共享侧边栏投影会隐藏持久化 Session 摘要中带有 `origin: 'subagent'` 的行;每个可见普通行都会在经不间断的 subagent 谱系可达的任一后代运行时继承蓝色活动指示器。同一项纯派生逻辑还会为分组、平铺与搜索节点读取列表 projection value 中的 Schedule key;本包只使用纯类型依赖 `@qilin-agent/schedule/client`,不会导入 Schedule 运行时或 `ui-schedule`。
|
|
93
95
|
|
|
96
|
+
行动效由 [AnimatedRows](../ui-primitives/src/AnimatedRows.tsx) 负责。分组树与平铺列表都经它渲染,并为每一行写上 `data-row-key`(`workspace:<key>`、`session:<id>`、`overflow:<group>`、`empty`);它在改变成员关系或顺序的提交前后测量带键行,以原生动画平移与渐隐,并把离场行作为惰性副本绘制在滚动列表之外。首次加载、拖拽提交、展开溢出和视图选项切换都立即落位,开启减少动态效果时完全跳过动画。
|
|
97
|
+
|
|
94
98
|
### 悬浮卡片
|
|
95
99
|
|
|
96
100
|
Workspace 与 Session 悬浮卡片会复制对应行被截断的值:激活 Workspace 卡片会写入其完整目录路径,激活非空白 Session 卡片则会写入其完整显示标题。临时的空白「新会话」卡片保持只读,因为其本地化标签是占位文案,并非会话内容。
|
package/lib/client.js
CHANGED
|
@@ -9,6 +9,120 @@ window.__ModuleLoader__.load({
|
|
|
9
9
|
let _qilin_agent_kylin = require("@qilin-agent/kylin");
|
|
10
10
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
11
11
|
let react = require("react");
|
|
12
|
+
//#region lib/types/client/context-menus.js
|
|
13
|
+
/**
|
|
14
|
+
* Register the built-in Session and Workspace actions on the shared menu.
|
|
15
|
+
* @param ctx - workspace plugin context owning menus, membership, and locale.
|
|
16
|
+
* @param actions - shared row callbacks; execution always uses the captured object.
|
|
17
|
+
*/
|
|
18
|
+
function registerWorkspaceMenus(ctx, actions) {
|
|
19
|
+
const t = ctx.locale.bind("workspace");
|
|
20
|
+
const state = () => ctx.workspaces.list.getSnapshot();
|
|
21
|
+
ctx.effect(() => ctx.contextMenus.register({
|
|
22
|
+
id: "workspace.session.pin",
|
|
23
|
+
target: "session",
|
|
24
|
+
order: 100,
|
|
25
|
+
observe: () => [ctx.workspaces.list, ctx.locale],
|
|
26
|
+
resolve: ({ sessionId }) => {
|
|
27
|
+
const pinned = state().pinnedSessionIds.includes(sessionId);
|
|
28
|
+
return [{
|
|
29
|
+
id: "pin",
|
|
30
|
+
label: t(pinned ? "menu.unpinSession" : "menu.pinSession"),
|
|
31
|
+
icon: { key: pinned ? "workspace.unpin" : "workspace.pin" },
|
|
32
|
+
run: () => {
|
|
33
|
+
(pinned ? actions.unpin : actions.pin)(sessionId);
|
|
34
|
+
}
|
|
35
|
+
}];
|
|
36
|
+
}
|
|
37
|
+
}), "ui-workspace: pin menu");
|
|
38
|
+
ctx.effect(() => ctx.contextMenus.register({
|
|
39
|
+
id: "workspace.session.rename",
|
|
40
|
+
target: "session",
|
|
41
|
+
order: 200,
|
|
42
|
+
resolve: ({ sessionId, displayTitle }) => [{
|
|
43
|
+
id: "rename",
|
|
44
|
+
label: t("rename"),
|
|
45
|
+
icon: { key: "workspace.rename" },
|
|
46
|
+
commandId: "session.rename",
|
|
47
|
+
run: () => {
|
|
48
|
+
actions.requestSessionRename(sessionId, displayTitle);
|
|
49
|
+
}
|
|
50
|
+
}]
|
|
51
|
+
}), "ui-workspace: rename menu");
|
|
52
|
+
ctx.effect(() => ctx.contextMenus.register({
|
|
53
|
+
id: "workspace.session.fork",
|
|
54
|
+
target: "session",
|
|
55
|
+
order: 300,
|
|
56
|
+
resolve: ({ sessionId }) => [{
|
|
57
|
+
id: "fork",
|
|
58
|
+
label: t("menu.fork"),
|
|
59
|
+
icon: { key: "workspace.fork" },
|
|
60
|
+
commandId: "session.fork",
|
|
61
|
+
run: () => {
|
|
62
|
+
actions.fork(sessionId);
|
|
63
|
+
}
|
|
64
|
+
}]
|
|
65
|
+
}), "ui-workspace: fork menu");
|
|
66
|
+
ctx.effect(() => ctx.contextMenus.register({
|
|
67
|
+
id: "workspace.session.id",
|
|
68
|
+
target: "session",
|
|
69
|
+
order: 450,
|
|
70
|
+
resolve: ({ sessionId }) => [{
|
|
71
|
+
id: "copy",
|
|
72
|
+
label: t("menu.sessionId"),
|
|
73
|
+
icon: { key: "workspace.copy" },
|
|
74
|
+
separatorBefore: true,
|
|
75
|
+
run: () => {
|
|
76
|
+
actions.copySessionId(sessionId);
|
|
77
|
+
}
|
|
78
|
+
}]
|
|
79
|
+
}), "ui-workspace: Session id menu");
|
|
80
|
+
ctx.effect(() => ctx.contextMenus.register({
|
|
81
|
+
id: "workspace.session.archive",
|
|
82
|
+
target: "session",
|
|
83
|
+
order: 600,
|
|
84
|
+
resolve: ({ sessionId }) => [{
|
|
85
|
+
id: "archive",
|
|
86
|
+
label: t("menu.archiveSession"),
|
|
87
|
+
icon: { key: "workspace.archive" },
|
|
88
|
+
separatorBefore: true,
|
|
89
|
+
afterContent: true,
|
|
90
|
+
commandId: "session.archive",
|
|
91
|
+
run: () => {
|
|
92
|
+
actions.archive(sessionId);
|
|
93
|
+
}
|
|
94
|
+
}]
|
|
95
|
+
}), "ui-workspace: archive menu");
|
|
96
|
+
ctx.effect(() => ctx.contextMenus.register({
|
|
97
|
+
id: "workspace.header.rename",
|
|
98
|
+
target: "workspace",
|
|
99
|
+
order: 100,
|
|
100
|
+
resolve: ({ workspaceId, displayTitle }) => [{
|
|
101
|
+
id: "rename",
|
|
102
|
+
label: t("rename"),
|
|
103
|
+
icon: { key: "workspace.rename" },
|
|
104
|
+
run: () => {
|
|
105
|
+
actions.requestWorkspaceAction(workspaceId, displayTitle, "rename");
|
|
106
|
+
}
|
|
107
|
+
}]
|
|
108
|
+
}), "ui-workspace: Workspace rename menu");
|
|
109
|
+
ctx.effect(() => ctx.contextMenus.register({
|
|
110
|
+
id: "workspace.header.delete",
|
|
111
|
+
target: "workspace",
|
|
112
|
+
order: 300,
|
|
113
|
+
resolve: ({ workspaceId, displayTitle }) => [{
|
|
114
|
+
id: "delete",
|
|
115
|
+
label: t("delete.workspace"),
|
|
116
|
+
icon: { key: "workspace.delete" },
|
|
117
|
+
danger: true,
|
|
118
|
+
separatorBefore: true,
|
|
119
|
+
run: () => {
|
|
120
|
+
actions.requestWorkspaceAction(workspaceId, displayTitle, "delete");
|
|
121
|
+
}
|
|
122
|
+
}]
|
|
123
|
+
}), "ui-workspace: Workspace delete menu");
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
12
126
|
//#region lib/types/client/shortcuts.js
|
|
13
127
|
/**
|
|
14
128
|
* Create the private browser request source shared by commands and controls.
|
|
@@ -977,7 +1091,7 @@ window.__ModuleLoader__.load({
|
|
|
977
1091
|
}
|
|
978
1092
|
//#endregion
|
|
979
1093
|
//#region \0qilin-css:/home/runner/work/QiLin/QiLin/packages/client/ui-workspace/src/client/rows/Rows.module.css.mjs
|
|
980
|
-
const css$2 = ".RrwzeW_projectRow,.RrwzeW_sessionRow{padding:0 8px;cursor:pointer;user-select:none;color:var(--qilin-alias-label-primary);border-radius:8px;
|
|
1094
|
+
const css$2 = ".RrwzeW_projectRow,.RrwzeW_sessionRow{align-items:center;gap:var(--qilin-sidebar-label-gap);border-inline:var(--qilin-sidebar-paint-inset) solid transparent;box-sizing:border-box;padding:0 8px;cursor:pointer;user-select:none;color:var(--qilin-alias-label-primary);background-clip:padding-box;border-radius:8px;padding-inline-start:calc(var(--qilin-sidebar-row-padding) - var(--qilin-sidebar-paint-inset) + var(--dsh-workspace-indent,0px));display:flex}@media (hover:hover){.RrwzeW_projectRow:hover,.RrwzeW_sessionRow:hover{background-color:var(--qilin-alias-interactive-bg-hover)}}.RrwzeW_sessionRow.RrwzeW_selected{background-color:var(--qilin-alias-interactive-bg-hover)}.RrwzeW_searchResultRow{box-sizing:border-box;width:100%;min-height:48px;padding:4px calc(var(--qilin-sidebar-row-padding) - var(--qilin-sidebar-paint-inset));border:none;border-inline:var(--qilin-sidebar-paint-inset) solid transparent;cursor:pointer;text-align:left;color:var(--qilin-alias-label-primary);background:padding-box padding-box;border-radius:8px;flex-direction:column;align-items:stretch;display:flex}@media (hover:hover){.RrwzeW_searchResultRow:hover{background-color:var(--qilin-alias-interactive-bg-hover)}}.RrwzeW_searchResultRow.RrwzeW_selected{background-color:var(--qilin-alias-interactive-bg-hover)}.RrwzeW_searchResultHeading{align-items:center;min-width:0;display:flex}.RrwzeW_searchResultTitle{min-width:0;margin:0 var(--qilin-sidebar-label-gap);text-overflow:ellipsis;white-space:nowrap;flex:0 auto;font-size:14px;line-height:20px;overflow:hidden}.RrwzeW_searchResultMeta{min-width:0;margin-left:calc(16px + var(--qilin-sidebar-label-gap));align-items:center;gap:6px;display:flex}.RrwzeW_searchResultWorkspace,.RrwzeW_searchResultSnippet{text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:17px;overflow:hidden}.RrwzeW_searchResultWorkspace{max-width:40%;color:var(--qilin-alias-label-tertiary);flex:none}.RrwzeW_searchResultSnippet{min-width:0;color:var(--qilin-alias-label-secondary);flex:1}.RrwzeW_projectRow{box-sizing:border-box;align-items:center;height:34px}.RrwzeW_projectRow .RrwzeW_rowActions{height:20px}.RrwzeW_sessionRow{gap:0;height:32px}.RrwzeW_sessionRow .RrwzeW_title{margin:0 6px 0 var(--qilin-sidebar-label-gap)}.RrwzeW_slot{width:16px;height:20px;color:var(--qilin-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:inline-flex}.RrwzeW_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}.RrwzeW_folderActive{color:var(--qilin-alias-state-business-primary)}.RrwzeW_projectRow .RrwzeW_chevron{display:none}@media (hover:hover){.RrwzeW_projectRow:hover .RrwzeW_chevron{display:inline-flex}.RrwzeW_projectRow:hover .RrwzeW_folder{display:none}}.RrwzeW_arrow{transition:transform .15s var(--ds-ease-in-out)}.RrwzeW_arrowOpen{transform:rotate(90deg)}.RrwzeW_projectText{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.RrwzeW_title{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:14px;line-height:20px;overflow:hidden}.RrwzeW_renameInput{border:1px solid var(--qilin-alias-border-l4);background:var(--qilin-alias-button-elevated-fill);min-width:0;color:inherit;border-radius:4px;outline:none;padding:0 2px;font-size:14px;line-height:20px}.RrwzeW_sessionRow .RrwzeW_title{flex:1}.RrwzeW_sessionRow .RrwzeW_title[data-scrolled]{mask-image:linear-gradient(90deg,#0000,#000 12px)}.RrwzeW_sessionRow .RrwzeW_title[data-clipped]{mask-image:linear-gradient(270deg,#0000,#000 12px)}.RrwzeW_sessionRow .RrwzeW_title[data-scrolled][data-clipped]{mask-image:linear-gradient(90deg,#0000,#000 12px calc(100% - 12px),#0000)}@media (hover:hover){.RrwzeW_sessionRow:hover .RrwzeW_title,.RrwzeW_sessionRow.RrwzeW_menuOpen .RrwzeW_title{text-overflow:clip}}.RrwzeW_meta{text-overflow:ellipsis;white-space:nowrap;color:var(--qilin-alias-label-tertiary);font-size:12px;line-height:20px;overflow:hidden}.RrwzeW_time{color:var(--qilin-alias-label-tertiary);flex:none;margin-inline-end:-1px;font-size:12px;line-height:20px}.RrwzeW_dot{flex:none}.RrwzeW_pinIndicator{width:16px;height:20px;color:var(--qilin-alias-label-caption);flex:none;justify-content:center;align-items:center;margin-left:6px;display:inline-flex}.RrwzeW_sessionRow.RrwzeW_menuOpen .RrwzeW_pinIndicator{display:none}@media (hover:hover){.RrwzeW_sessionRow:hover .RrwzeW_pinIndicator{display:none}}@media (hover:none){.RrwzeW_sessionRow .RrwzeW_pinIndicator{display:none}}.RrwzeW_rowActions{flex:none;align-items:center;gap:13px;display:none}.RrwzeW_projectRow.RrwzeW_menuOpen .RrwzeW_rowActions,.RrwzeW_sessionRow.RrwzeW_menuOpen .RrwzeW_rowActions,.RrwzeW_projectRow:focus-within .RrwzeW_rowActions,.RrwzeW_sessionRow:focus-within .RrwzeW_rowActions{display:inline-flex}.RrwzeW_sessionRow.RrwzeW_menuOpen .RrwzeW_time{display:none}@media (hover:hover){.RrwzeW_projectRow:hover .RrwzeW_rowActions,.RrwzeW_sessionRow:hover .RrwzeW_rowActions{display:inline-flex}.RrwzeW_sessionRow:hover .RrwzeW_time{display:none}}@media (hover:none){.RrwzeW_projectRow .RrwzeW_rowActions,.RrwzeW_sessionRow .RrwzeW_rowActions{display:inline-flex}.RrwzeW_projectRow[aria-expanded=false] .RrwzeW_rowActions,.RrwzeW_sessionRow .RrwzeW_time,.RrwzeW_secondaryAction{display:none}}.RrwzeW_projectRow.RrwzeW_menuOpen,.RrwzeW_sessionRow.RrwzeW_menuOpen{background-color:var(--qilin-alias-interactive-bg-hover)}.RrwzeW_sessionRow.RrwzeW_dropBefore,.RrwzeW_sessionRow.RrwzeW_dropAfter{position:relative}.RrwzeW_sessionRow.RrwzeW_dropBefore:before,.RrwzeW_sessionRow.RrwzeW_dropAfter:after{content:\"\";z-index:1;left:calc(-1 * var(--qilin-sidebar-paint-inset));right:calc(4px - var(--qilin-sidebar-paint-inset));background:linear-gradient(55deg, transparent calc(50% - 1px), var(--qilin-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 0 / 5px 7px no-repeat, linear-gradient(125deg, transparent calc(50% - 1px), var(--qilin-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 5px / 5px 7px no-repeat, linear-gradient(var(--qilin-alias-state-business-primary) 0 0) 4px 5px / calc(100% - 4px) 2px no-repeat;pointer-events:none;height:12px;position:absolute}.RrwzeW_sessionRow.RrwzeW_dropBefore:before{top:-7px}.RrwzeW_sessionRow.RrwzeW_dropAfter:after{bottom:-7px}.RrwzeW_hoverContent{flex-direction:column;gap:8px;display:flex}.RrwzeW_hoverTitle{color:#fff;overflow-wrap:break-word;font-size:14px;line-height:20px}.RrwzeW_hoverPath{color:#cfd3d6;word-break:break-all;font-size:12px;line-height:16px}.RrwzeW_hoverTime{color:#cfd3d6;font-size:12px;line-height:16px}.RrwzeW_hoverStatus{color:#adb2b8;align-items:center;gap:8px;font-size:12px;line-height:20px;display:flex}.RrwzeW_iconButton{cursor:pointer;width:24px;height:24px;color:var(--qilin-alias-label-tertiary);background:0 0;border:none;border-radius:4px;flex:none;justify-content:center;align-items:center;margin:-4px;padding:0;display:inline-flex}.RrwzeW_iconButton:focus-visible{outline-offset:-2px}.RrwzeW_iconButton:hover{color:var(--qilin-alias-label-primary)}.RrwzeW_chevron{color:var(--qilin-alias-label-caption)}@media (prefers-reduced-motion:reduce){.RrwzeW_arrow{transition:none;animation:none}}";
|
|
981
1095
|
const tagId$2 = "@qilin-agent/client-ui-workspace/Rows.module.css";
|
|
982
1096
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
983
1097
|
const tag = document.createElement("style");
|
|
@@ -1007,7 +1121,6 @@ window.__ModuleLoader__.load({
|
|
|
1007
1121
|
"projectRow": "RrwzeW_projectRow",
|
|
1008
1122
|
"projectText": "RrwzeW_projectText",
|
|
1009
1123
|
"renameInput": "RrwzeW_renameInput",
|
|
1010
|
-
"row-in": "RrwzeW_row-in",
|
|
1011
1124
|
"rowActions": "RrwzeW_rowActions",
|
|
1012
1125
|
"searchResultHeading": "RrwzeW_searchResultHeading",
|
|
1013
1126
|
"searchResultMeta": "RrwzeW_searchResultMeta",
|
|
@@ -1015,6 +1128,7 @@ window.__ModuleLoader__.load({
|
|
|
1015
1128
|
"searchResultSnippet": "RrwzeW_searchResultSnippet",
|
|
1016
1129
|
"searchResultTitle": "RrwzeW_searchResultTitle",
|
|
1017
1130
|
"searchResultWorkspace": "RrwzeW_searchResultWorkspace",
|
|
1131
|
+
"secondaryAction": "RrwzeW_secondaryAction",
|
|
1018
1132
|
"selected": "RrwzeW_selected",
|
|
1019
1133
|
"sessionRow": "RrwzeW_sessionRow",
|
|
1020
1134
|
"slot": "RrwzeW_slot",
|
|
@@ -1029,9 +1143,10 @@ window.__ModuleLoader__.load({
|
|
|
1029
1143
|
* all data and callbacks arrive via props. Hover swaps (folder->chevron,
|
|
1030
1144
|
* time->ellipsis, action buttons) are CSS-only, and a session row's clipped
|
|
1031
1145
|
* title marquees programmatically while the row is hovered, fading both
|
|
1032
|
-
* clipping edges from the depth of its travel. Row ... menus
|
|
1033
|
-
*
|
|
1034
|
-
*
|
|
1146
|
+
* clipping edges from the depth of its travel. Row ... menus cover workspace
|
|
1147
|
+
* Rename/Delete and session Rename/Fork/Archive; a right-click on the row
|
|
1148
|
+
* captures the same object for the shared context menu. The session and
|
|
1149
|
+
* workspace hover cards are suppressed while a menu is open.
|
|
1035
1150
|
*/
|
|
1036
1151
|
/** Row display title: blank rows show the localized New Session label. */
|
|
1037
1152
|
function displayTitle(node, t) {
|
|
@@ -1039,10 +1154,6 @@ window.__ModuleLoader__.load({
|
|
|
1039
1154
|
}
|
|
1040
1155
|
const MIN_TITLE_REVEAL_PX = 8;
|
|
1041
1156
|
const TITLE_MARQUEE_PX_PER_MS = .03;
|
|
1042
|
-
/** Whether the platform asked for reduced motion; jsdom implements no matchMedia. */
|
|
1043
|
-
function prefersReducedMotion() {
|
|
1044
|
-
return typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1045
|
-
}
|
|
1046
1157
|
/**
|
|
1047
1158
|
* Place the title's scroll position and publish the stylesheet's fade-mask
|
|
1048
1159
|
* hooks: `data-scrolled` while the title has left its start (left fade) and
|
|
@@ -1094,13 +1205,14 @@ window.__ModuleLoader__.load({
|
|
|
1094
1205
|
cancelAnimationFrame(frame.current);
|
|
1095
1206
|
}, []);
|
|
1096
1207
|
return (0, react.useMemo)(() => ({
|
|
1097
|
-
enter: () => {
|
|
1208
|
+
enter: (event) => {
|
|
1209
|
+
if (event.pointerType === "touch") return;
|
|
1098
1210
|
/* v8 ignore next -- defensive: the title span renders unconditionally. */
|
|
1099
1211
|
if (title.current === null) return;
|
|
1100
1212
|
const element = title.current;
|
|
1101
1213
|
const range = element.scrollWidth - element.clientWidth;
|
|
1102
1214
|
if (range <= MIN_TITLE_REVEAL_PX) return;
|
|
1103
|
-
if (prefersReducedMotion()) {
|
|
1215
|
+
if ((0, _qilin_agent_client_ui_primitives.prefersReducedMotion)()) {
|
|
1104
1216
|
placeTitle(element, range, range);
|
|
1105
1217
|
return;
|
|
1106
1218
|
}
|
|
@@ -1186,7 +1298,7 @@ window.__ModuleLoader__.load({
|
|
|
1186
1298
|
* @param props.t - the browser root's locale seat.
|
|
1187
1299
|
* @returns the row element.
|
|
1188
1300
|
*/
|
|
1189
|
-
function ProjectRowItem({ group, containsCurrentDescendant = false, onToggle, onCreate, actions, drag, home, t }) {
|
|
1301
|
+
function ProjectRowItem({ group, containsCurrentDescendant = false, onToggle, onCreate, actions, drag, home, menu, t }) {
|
|
1190
1302
|
const row = group;
|
|
1191
1303
|
const label = row.workspaceId === void 0 ? t("group.ungrouped") : row.label;
|
|
1192
1304
|
const active = containsCurrentDescendant || group.expanded && group.containsCurrent;
|
|
@@ -1201,11 +1313,22 @@ window.__ModuleLoader__.load({
|
|
|
1201
1313
|
icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconTrashOutlineRegular, {}),
|
|
1202
1314
|
danger: true
|
|
1203
1315
|
}];
|
|
1316
|
+
const menuProps = row.workspaceId === void 0 ? void 0 : menu?.(row.workspaceId, row.cwd, label);
|
|
1204
1317
|
const ownRow = (0, react_jsx_runtime.jsxs)("div", {
|
|
1205
1318
|
className: clsx(Rows_module_css_default.projectRow, menuOpen && Rows_module_css_default.menuOpen),
|
|
1206
1319
|
role: "treeitem",
|
|
1320
|
+
"data-row-key": `workspace:${row.key}`,
|
|
1207
1321
|
"aria-expanded": row.expanded,
|
|
1322
|
+
tabIndex: 0,
|
|
1323
|
+
...menuProps,
|
|
1208
1324
|
onClick: onToggle,
|
|
1325
|
+
onKeyDown: (event) => {
|
|
1326
|
+
menuProps?.onKeyDown?.(event);
|
|
1327
|
+
if (event.defaultPrevented) return;
|
|
1328
|
+
if (event.target !== event.currentTarget || !["Enter", " "].includes(event.key)) return;
|
|
1329
|
+
event.preventDefault();
|
|
1330
|
+
onToggle();
|
|
1331
|
+
},
|
|
1209
1332
|
draggable: drag !== void 0,
|
|
1210
1333
|
onDragStart: drag === void 0 ? void 0 : (e) => {
|
|
1211
1334
|
e.dataTransfer.effectAllowed = "move";
|
|
@@ -1264,7 +1387,7 @@ window.__ModuleLoader__.load({
|
|
|
1264
1387
|
e.stopPropagation();
|
|
1265
1388
|
onCreate();
|
|
1266
1389
|
},
|
|
1267
|
-
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconPlusOutlineRegular, {})
|
|
1390
|
+
children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconPlusOutlineRegular, { size: 14 })
|
|
1268
1391
|
})]
|
|
1269
1392
|
})
|
|
1270
1393
|
]
|
|
@@ -1445,8 +1568,9 @@ window.__ModuleLoader__.load({
|
|
|
1445
1568
|
* @param props.t - the browser root's locale seat.
|
|
1446
1569
|
* @returns the session row.
|
|
1447
1570
|
*/
|
|
1448
|
-
function SessionNodeItem({ node, currentId, now, onOpen, onRename, onFork, onArchive, onCopyId, onPin, onUnpin, onReveal, drag, renderSlot, t }) {
|
|
1571
|
+
function SessionNodeItem({ node, currentId, now, onOpen, onRename, onFork, onArchive, onCopyId, onPin, onUnpin, onReveal, drag, renderSlot, menu, t }) {
|
|
1449
1572
|
const row = node;
|
|
1573
|
+
const menuProps = row.blank ? void 0 : menu?.(node.id, row.title);
|
|
1450
1574
|
const title = displayTitle(node, t);
|
|
1451
1575
|
const selected = node.id === currentId;
|
|
1452
1576
|
const statuses = sessionStatuses(node, t);
|
|
@@ -1493,7 +1617,9 @@ window.__ModuleLoader__.load({
|
|
|
1493
1617
|
ref: rowRef,
|
|
1494
1618
|
className: clsx(Rows_module_css_default.sessionRow, selected && Rows_module_css_default.selected, menuOpen && Rows_module_css_default.menuOpen, drag?.marker === "before" && Rows_module_css_default.dropBefore, drag?.marker === "after" && Rows_module_css_default.dropAfter),
|
|
1495
1619
|
role: "treeitem",
|
|
1620
|
+
"data-row-key": `session:${node.id}`,
|
|
1496
1621
|
"aria-selected": selected,
|
|
1622
|
+
...menuProps,
|
|
1497
1623
|
onClick: () => {
|
|
1498
1624
|
onOpen(node.id);
|
|
1499
1625
|
},
|
|
@@ -1569,7 +1695,7 @@ window.__ModuleLoader__.load({
|
|
|
1569
1695
|
portal: true,
|
|
1570
1696
|
children: (0, react_jsx_runtime.jsx)("button", {
|
|
1571
1697
|
type: "button",
|
|
1572
|
-
className: Rows_module_css_default.iconButton,
|
|
1698
|
+
className: clsx(Rows_module_css_default.iconButton, Rows_module_css_default.secondaryAction),
|
|
1573
1699
|
"aria-label": t("menu.archiveSession"),
|
|
1574
1700
|
onClick: (e) => {
|
|
1575
1701
|
e.stopPropagation();
|
|
@@ -1782,7 +1908,7 @@ window.__ModuleLoader__.load({
|
|
|
1782
1908
|
}
|
|
1783
1909
|
//#endregion
|
|
1784
1910
|
//#region \0qilin-css:/home/runner/work/QiLin/QiLin/packages/client/ui-workspace/src/client/rows/WorkspaceBrowser.module.css.mjs
|
|
1785
|
-
const css = ".CO5s3G_root{--qilin-session-list-edge-inset:var(--qilin-sidebar-inline-padding);--qilin-session-list-scrollbar-width:8px;--qilin-session-list-scrollbar-offset:2px;box-sizing:border-box;min-height:0;padding-right:var(--qilin-session-list-edge-inset);flex-direction:column;flex:1;display:flex}.CO5s3G_root.CO5s3G_rail{padding-right:0}.CO5s3G_iconButton{corner-shape:round;cursor:pointer;width:28px;height:28px;color:var(--qilin-alias-label-secondary);background:0 0;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.CO5s3G_iconButton:focus-visible,.CO5s3G_searchButton:focus-visible,.CO5s3G_clearButton:focus-visible{outline:var(--qilin-focus-ring-width) solid var(--qilin-focus-ring-color,var(--qilin-alias-state-business-primary));outline-offset:-2px}.CO5s3G_iconButton:hover{background:var(--qilin-alias-interactive-bg-hover)}.CO5s3G_sectionHeader{box-sizing:border-box;
|
|
1911
|
+
const css = ".CO5s3G_root{--qilin-session-list-edge-inset:var(--qilin-sidebar-inline-padding);--qilin-session-list-scrollbar-width:8px;--qilin-session-list-scrollbar-offset:2px;box-sizing:border-box;min-height:0;padding-right:var(--qilin-session-list-edge-inset);flex-direction:column;flex:1;display:flex}.CO5s3G_root.CO5s3G_rail{padding-right:0}.CO5s3G_iconButton{corner-shape:round;cursor:pointer;width:28px;height:28px;color:var(--qilin-alias-label-secondary);background:0 0;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.CO5s3G_iconButton:focus-visible,.CO5s3G_searchButton:focus-visible,.CO5s3G_clearButton:focus-visible{outline:var(--qilin-focus-ring-width) solid var(--qilin-focus-ring-color,var(--qilin-alias-state-business-primary));outline-offset:-2px}.CO5s3G_iconButton:hover{background:var(--qilin-alias-interactive-bg-hover)}.CO5s3G_sectionHeader{height:36px;padding-left:var(--qilin-sidebar-row-padding);box-sizing:border-box;color:var(--qilin-alias-label-tertiary);border-radius:12px;flex:none;justify-content:flex-end;align-items:center;gap:4px;margin-bottom:4px;display:flex;overflow:hidden}.CO5s3G_root:not(.CO5s3G_rail) .CO5s3G_sectionHeader{transition:margin-right .18s var(--ds-ease-in-out), padding-inline .18s var(--ds-ease-in-out), gap .18s var(--ds-ease-in-out);margin-top:2px;margin-right:-4px}.CO5s3G_root:not(.CO5s3G_rail) .CO5s3G_sectionHeader:has(.CO5s3G_searchExpanded){padding-inline:calc(var(--qilin-sidebar-paint-inset) + 1px);gap:0;margin-right:0}.CO5s3G_sectionHeader:has(.CO5s3G_searchExpanded) .CO5s3G_sectionLabelHidden{margin-right:0}.CO5s3G_sectionLabel{white-space:nowrap;opacity:1;visibility:visible;min-width:0;max-width:45%;transition:max-width .18s var(--ds-ease-in-out), margin-right .18s var(--ds-ease-in-out), opacity .12s var(--ds-ease-in-out), transform .18s var(--ds-ease-in-out), visibility 0s linear;flex:none;line-height:20px;position:relative;left:-1px;overflow:hidden}.CO5s3G_sectionLabelHidden{opacity:0;visibility:hidden;max-width:0;margin-right:-4px;transition-delay:0s,0s,0s,0s,.18s;transform:translate(-4px)}.CO5s3G_searchSlot{box-sizing:border-box;min-width:0;max-width:28px;transition:max-width .18s var(--ds-ease-in-out), padding-left .18s var(--ds-ease-in-out);flex:1;align-items:center;margin-left:auto;padding-left:0;display:flex}.CO5s3G_searchSlotExpanded{max-width:100%;padding-left:0}.CO5s3G_headerActions{opacity:1;visibility:visible;max-width:60px;transition:max-width .18s var(--ds-ease-in-out), opacity .12s var(--ds-ease-in-out), transform .18s var(--ds-ease-in-out), visibility 0s linear;flex:none;align-items:center;gap:4px;display:flex;overflow:hidden}.CO5s3G_headerActionsHidden{opacity:0;visibility:hidden;pointer-events:none;max-width:0;transition-delay:0s,0s,0s,.18s;transform:translate(4px)}.CO5s3G_search{box-sizing:border-box;corner-shape:round;cursor:text;width:100%;height:28px;color:var(--qilin-alias-label-secondary);transition:width .18s var(--ds-ease-in-out), padding .18s var(--ds-ease-in-out), border-color .18s var(--ds-ease-in-out), background-color .18s var(--ds-ease-in-out);background:0 0;border:none;border-radius:50%;flex:none;align-items:center;gap:0;margin:0;padding:0;display:flex;overflow:hidden}.CO5s3G_searchExpanded{border:1px solid var(--qilin-alias-border-l4);width:100%;height:32px;color:var(--qilin-alias-label-caption);background:0 0;border-radius:10px;margin-inline:0;padding:0 4px 0 0}.CO5s3G_searchButton{corner-shape:round;cursor:pointer;width:28px;height:28px;color:inherit;background:0 0;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.CO5s3G_searchExpanded .CO5s3G_searchButton{width:28px;height:32px}.CO5s3G_searchButton:hover{background:var(--qilin-alias-interactive-bg-hover)}.CO5s3G_searchExpanded .CO5s3G_searchButton:hover{background:0 0}.CO5s3G_searchInput{opacity:0;pointer-events:none;width:0;min-width:0;color:var(--qilin-alias-label-primary);transition:opacity .12s var(--ds-ease-in-out);background:0 0;border:none;outline:none;flex:1;font-size:13px;line-height:18px}.CO5s3G_searchExpanded .CO5s3G_searchInput{opacity:1;pointer-events:auto;margin-left:-2px}.CO5s3G_searchInput::placeholder{color:var(--qilin-alias-label-tertiary)}.CO5s3G_clearButton{corner-shape:round;cursor:pointer;width:24px;height:24px;color:var(--qilin-alias-label-secondary);background:0 0;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.CO5s3G_clearButton:hover{background:var(--qilin-alias-interactive-bg-hover)}.CO5s3G_rail .CO5s3G_sectionHeader{justify-content:flex-start;gap:0;margin-bottom:12px;padding-left:0}.CO5s3G_listArea{min-height:0;margin-left:-4px;margin-right:calc(-1 * var(--qilin-session-list-edge-inset));flex-direction:column;flex:1;padding-left:4px;display:flex;overflow:visible}.CO5s3G_rail .CO5s3G_listArea{margin-left:0;margin-right:0;padding-left:0}.CO5s3G_treeBody{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}.CO5s3G_fade{left:0;right:var(--qilin-session-list-edge-inset);background:linear-gradient(to bottom, transparent, var(--qilin-specific-sidebar-fill));pointer-events:none;height:24px;position:absolute;bottom:0}[data-platform=darwin] .CO5s3G_fade{display:none}.CO5s3G_wide{animation:CO5s3G_wide-in .2s var(--ds-ease-in-out)}@keyframes CO5s3G_wide-in{0%{opacity:0}}.CO5s3G_list{min-height:0;margin-left:-4px;margin-right:var(--qilin-session-list-scrollbar-offset);padding-left:4px;padding-right:calc(var(--qilin-session-list-edge-inset) - var(--qilin-session-list-scrollbar-width) - var(--qilin-session-list-scrollbar-offset));scrollbar-gutter:stable;flex:1;padding-bottom:16px;overflow-y:auto}.CO5s3G_flatList>*+*,.CO5s3G_searchTree>[role=treeitem]+[role=treeitem],.CO5s3G_groupSection>*+*{margin-top:2px}.CO5s3G_searchStatus,.CO5s3G_searchWarning{color:var(--qilin-alias-label-tertiary);padding:10px 12px;font-size:12px;line-height:18px}.CO5s3G_searchWarning{color:var(--qilin-alias-label-secondary)}.CO5s3G_groupSection{position:relative}.CO5s3G_groupSection+.CO5s3G_groupSection{margin-top:4px}.CO5s3G_listTopDropIndicator,.CO5s3G_workspaceDropBefore:before,.CO5s3G_workspaceDropAfter:after{content:\"\";z-index:1;background:linear-gradient(55deg, transparent calc(50% - 1px), var(--qilin-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 0 / 5px 7px no-repeat, linear-gradient(125deg, transparent calc(50% - 1px), var(--qilin-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 5px / 5px 7px no-repeat, linear-gradient(var(--qilin-alias-state-business-primary) 0 0) 4px 5px / calc(100% - 4px) 2px no-repeat;pointer-events:none;height:12px;position:absolute;left:0;right:0}.CO5s3G_listTopDropIndicator{top:-8px;left:0;right:var(--qilin-session-list-edge-inset)}.CO5s3G_listTopDropActive>.CO5s3G_workspaceDropBefore:first-child:before{display:none}.CO5s3G_workspaceDropBefore:before{top:-8px}.CO5s3G_workspaceDropAfter:after{bottom:-8px}.CO5s3G_sessionOverflowButton{width:100%;height:28px;padding:0 var(--qilin-sidebar-row-padding) 0 calc(var(--qilin-sidebar-row-padding) + 16px + var(--qilin-sidebar-label-gap) + var(--dsh-workspace-indent,0px));cursor:pointer;text-align:left;color:var(--qilin-alias-label-tertiary);background:0 0;border:none;border-radius:8px;font-size:12px}.CO5s3G_groupSection>.CO5s3G_sessionOverflowButton{margin-top:0}.CO5s3G_sessionOverflowButton:hover{color:var(--qilin-alias-label-secondary);background:0 0}.CO5s3G_empty{color:var(--qilin-alias-label-tertiary);padding:16px 12px;font-size:13px}.CO5s3G_renameInput{box-sizing:border-box;border:1px solid var(--qilin-alias-border-l4);width:100%;height:44px;color:var(--qilin-alias-label-primary);background:0 0;border-radius:22px;outline:none;padding:7px 14px;font-size:14px;font-weight:400;line-height:22px}.CO5s3G_renameInput:disabled{color:var(--qilin-alias-label-dimmed)}.CO5s3G_renameError{color:var(--qilin-alias-state-error-primary);margin-top:8px;font-size:12px;line-height:18px}.CO5s3G_deleteAction:not(:disabled){color:var(--qilin-alias-state-error-primary)}.CO5s3G_deleteStatus{color:var(--qilin-alias-label-secondary);font-size:12px;line-height:18px}@media (prefers-reduced-motion:reduce){.CO5s3G_wide{animation:none}.CO5s3G_root:not(.CO5s3G_rail) .CO5s3G_sectionHeader,.CO5s3G_search,.CO5s3G_sectionLabel,.CO5s3G_searchSlot,.CO5s3G_searchInput,.CO5s3G_headerActions{transition:none}}";
|
|
1786
1912
|
const tagId = "@qilin-agent/client-ui-workspace/WorkspaceBrowser.module.css";
|
|
1787
1913
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
1788
1914
|
const tag = document.createElement("style");
|
|
@@ -2001,17 +2127,19 @@ window.__ModuleLoader__.load({
|
|
|
2001
2127
|
return e.clientY < rect.top + rect.height / 2 ? "before" : "after";
|
|
2002
2128
|
}
|
|
2003
2129
|
/** The scrolling session tree; unmounting drops the sessions subscription and expand-all state. */
|
|
2004
|
-
function SessionTree({ list, useSessionStatus, startSession, open, forkSession, copySessionId, workspaces, ungroupedSessionIds, archivedSessionIds, pinnedSessionIds, onSessionPin, onSessionUnpin, workspaceReady, usePanelInfo, onRenameRequest, onDeleteRequest, onSessionRename, onSessionArchive, insertWorkspaceBefore, nestWorkspaces, groupExpansion, setGroupExpanded, setSessionOrder, home, t, renderSlot, revealSessionId, onSessionRevealed }) {
|
|
2130
|
+
function SessionTree({ list, useSessionStatus, startSession, open, forkSession, copySessionId, workspaces, ungroupedSessionIds, archivedSessionIds, pinnedSessionIds, onSessionPin, onSessionUnpin, workspaceReady, usePanelInfo, onRenameRequest, onDeleteRequest, onSessionRename, onSessionArchive, insertWorkspaceBefore, nestWorkspaces, groupExpansion, setGroupExpanded, setSessionOrder, home, t, renderSlot, menu, revealSessionId, onSessionRevealed, animationResetKey }) {
|
|
2005
2131
|
const panelActive = usePanelInfo((info) => info.activePanelId !== null);
|
|
2006
2132
|
const statuses = useSessionStatus((s) => s);
|
|
2007
2133
|
const current = panelActive ? void 0 : Object.values(list.byId).find((session) => (session.retainedBy.mainView ?? 0) > 0)?.id;
|
|
2008
2134
|
const revealGroup = revealSessionId === void 0 || !workspaceReady ? void 0 : owningGroupKey(workspaces, revealSessionId);
|
|
2009
2135
|
const [storedWatermarks, setStoredWatermarks] = (0, react.useState)({});
|
|
2136
|
+
const [watermarkMotionKey, setWatermarkMotionKey] = (0, react.useState)(0);
|
|
2010
2137
|
const [drag, setDrag] = (0, react.useState)(null);
|
|
2011
2138
|
const sessionDropCommitted = (0, react.useRef)(false);
|
|
2012
2139
|
const [workspaceDrag, setWorkspaceDrag] = (0, react.useState)(null);
|
|
2013
2140
|
const workspaceDropCommitted = (0, react.useRef)(false);
|
|
2014
|
-
|
|
2141
|
+
const nativeDragActive = drag !== null || workspaceDrag !== null;
|
|
2142
|
+
useNativeDragAcceptance(nativeDragActive);
|
|
2015
2143
|
const currentGroup = current === void 0 || !workspaceReady ? void 0 : owningGroupKey(workspaces, current);
|
|
2016
2144
|
(0, react.useEffect)(() => {
|
|
2017
2145
|
if (current === void 0 || currentGroup === void 0 || Object.hasOwn(groupExpansion, currentGroup)) return;
|
|
@@ -2159,6 +2287,7 @@ window.__ModuleLoader__.load({
|
|
|
2159
2287
|
}, [groups, parents]);
|
|
2160
2288
|
const rootGroups = childrenByParent.get(void 0) ?? [];
|
|
2161
2289
|
const workspaceDropAtListStart = rootGroups[0]?.workspaceId !== void 0 && workspaceDrag?.over?.id === rootGroups[0].workspaceId && workspaceDrag.over.half === "before";
|
|
2290
|
+
const rowKeys = groups.length === 0 ? ["empty"] : [];
|
|
2162
2291
|
const renderGroup = (group, depth) => {
|
|
2163
2292
|
const workspaceId = group.workspaceId;
|
|
2164
2293
|
const children = childrenByParent.get(group.key) ?? [];
|
|
@@ -2167,6 +2296,10 @@ window.__ModuleLoader__.load({
|
|
|
2167
2296
|
const collapsed = watermarkRows(group.sessions, baselines.get(group.key) ?? SESSION_WATERMARK_BASE);
|
|
2168
2297
|
const visible = watermarkRows(group.sessions, watermark);
|
|
2169
2298
|
const sessionsExpanded = visible.hiddenCount === 0;
|
|
2299
|
+
rowKeys.push(`workspace:${group.key}`);
|
|
2300
|
+
const childRows = group.expanded ? children.map((child) => renderGroup(child, depth + 1)) : [];
|
|
2301
|
+
for (const node of visible.rows) rowKeys.push(`session:${node.id}`);
|
|
2302
|
+
if (collapsed.hiddenCount > 0) rowKeys.push(`overflow:${group.key}`);
|
|
2170
2303
|
const workspaceMarker = workspaceId !== void 0 && workspaceDrag?.over?.id === workspaceId ? workspaceDrag.over.half : null;
|
|
2171
2304
|
const workspaceDragProps = workspaceId === void 0 ? void 0 : {
|
|
2172
2305
|
start: () => {
|
|
@@ -2241,6 +2374,7 @@ window.__ModuleLoader__.load({
|
|
|
2241
2374
|
}
|
|
2242
2375
|
},
|
|
2243
2376
|
drag: workspaceDragProps,
|
|
2377
|
+
menu: menu.workspace,
|
|
2244
2378
|
actions: group.workspaceId === void 0 ? void 0 : {
|
|
2245
2379
|
rename: () => {
|
|
2246
2380
|
/* v8 ignore next -- narrowing guard: the actions object exists only for real-workspace groups. */
|
|
@@ -2254,7 +2388,7 @@ window.__ModuleLoader__.load({
|
|
|
2254
2388
|
}),
|
|
2255
2389
|
group.expanded && children.length > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
2256
2390
|
role: "group",
|
|
2257
|
-
children:
|
|
2391
|
+
children: childRows
|
|
2258
2392
|
}),
|
|
2259
2393
|
visible.rows.map((node) => {
|
|
2260
2394
|
const sameGroupDrag = drag !== null && drag.accountKey === group.key;
|
|
@@ -2312,12 +2446,14 @@ window.__ModuleLoader__.load({
|
|
|
2312
2446
|
} : void 0,
|
|
2313
2447
|
drag: dragProps,
|
|
2314
2448
|
renderSlot,
|
|
2449
|
+
menu: menu.session,
|
|
2315
2450
|
t
|
|
2316
2451
|
}, node.id);
|
|
2317
2452
|
}),
|
|
2318
2453
|
collapsed.hiddenCount > 0 && (0, react_jsx_runtime.jsx)("button", {
|
|
2319
2454
|
type: "button",
|
|
2320
2455
|
className: WorkspaceBrowser_module_css_default.sessionOverflowButton,
|
|
2456
|
+
"data-row-key": `overflow:${group.key}`,
|
|
2321
2457
|
"aria-expanded": sessionsExpanded,
|
|
2322
2458
|
onClick: () => {
|
|
2323
2459
|
if (sessionsExpanded) {
|
|
@@ -2328,12 +2464,14 @@ window.__ModuleLoader__.load({
|
|
|
2328
2464
|
...marks,
|
|
2329
2465
|
[group.key]: visible.hiddenCount <= SESSION_WATERMARK_BASE ? Infinity : watermark + SESSION_WATERMARK_BASE
|
|
2330
2466
|
}));
|
|
2467
|
+
setWatermarkMotionKey((key) => key + 1);
|
|
2331
2468
|
},
|
|
2332
2469
|
children: sessionsExpanded ? t("sessions.collapse") : t("sessions.expand", { n: visible.hiddenCount })
|
|
2333
2470
|
})
|
|
2334
2471
|
]
|
|
2335
2472
|
}, group.key);
|
|
2336
2473
|
};
|
|
2474
|
+
const groupRows = rootGroups.map((group) => renderGroup(group, 0));
|
|
2337
2475
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
2338
2476
|
className: clsx(WorkspaceBrowser_module_css_default.treeBody, WorkspaceBrowser_module_css_default.wide),
|
|
2339
2477
|
children: [
|
|
@@ -2341,21 +2479,25 @@ window.__ModuleLoader__.load({
|
|
|
2341
2479
|
className: WorkspaceBrowser_module_css_default.listTopDropIndicator,
|
|
2342
2480
|
"aria-hidden": "true"
|
|
2343
2481
|
}),
|
|
2344
|
-
(0, react_jsx_runtime.jsxs)(
|
|
2482
|
+
(0, react_jsx_runtime.jsxs)(_qilin_agent_client_ui_primitives.AnimatedRows, {
|
|
2345
2483
|
className: clsx(WorkspaceBrowser_module_css_default.list, workspaceDropAtListStart && WorkspaceBrowser_module_css_default.listTopDropActive),
|
|
2346
2484
|
role: "tree",
|
|
2347
|
-
|
|
2485
|
+
label: t("section.sessions"),
|
|
2486
|
+
rowKeys,
|
|
2487
|
+
ready: list.phase === "ready" && workspaceReady && !nativeDragActive,
|
|
2488
|
+
resetKey: JSON.stringify([animationResetKey, watermarkMotionKey]),
|
|
2348
2489
|
children: [groups.length === 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
2349
2490
|
className: WorkspaceBrowser_module_css_default.empty,
|
|
2491
|
+
"data-row-key": "empty",
|
|
2350
2492
|
children: t("empty.none")
|
|
2351
|
-
}),
|
|
2493
|
+
}), groupRows]
|
|
2352
2494
|
}),
|
|
2353
2495
|
(0, react_jsx_runtime.jsx)("span", { className: WorkspaceBrowser_module_css_default.fade })
|
|
2354
2496
|
]
|
|
2355
2497
|
});
|
|
2356
2498
|
}
|
|
2357
2499
|
/** The flat "In one list" body: every session is one draggable top-level row. */
|
|
2358
|
-
function FlatList({ list, sessionIds, pinnedSessionIds, useSessionStatus, open, forkSession, copySessionId, onSessionRename, onSessionArchive, onSessionPin, onSessionUnpin, usePanelInfo, setSessionOrder, revealSessionId, onSessionRevealed, t, renderSlot }) {
|
|
2500
|
+
function FlatList({ list, sessionIds, pinnedSessionIds, useSessionStatus, open, forkSession, copySessionId, onSessionRename, onSessionArchive, onSessionPin, onSessionUnpin, usePanelInfo, setSessionOrder, workspaceReady, animationResetKey, revealSessionId, onSessionRevealed, t, renderSlot, menu }) {
|
|
2359
2501
|
const panelActive = usePanelInfo((info) => info.activePanelId !== null);
|
|
2360
2502
|
const statuses = useSessionStatus((s) => s);
|
|
2361
2503
|
const rows = (0, react.useMemo)(() => deriveFlat(list, sessionIds, pinnedSessionIds, statuses), [
|
|
@@ -2388,12 +2530,16 @@ window.__ModuleLoader__.load({
|
|
|
2388
2530
|
const now = Date.now();
|
|
2389
2531
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
2390
2532
|
className: clsx(WorkspaceBrowser_module_css_default.treeBody, WorkspaceBrowser_module_css_default.wide),
|
|
2391
|
-
children: [(0, react_jsx_runtime.jsxs)(
|
|
2533
|
+
children: [(0, react_jsx_runtime.jsxs)(_qilin_agent_client_ui_primitives.AnimatedRows, {
|
|
2392
2534
|
className: clsx(WorkspaceBrowser_module_css_default.list, WorkspaceBrowser_module_css_default.flatList),
|
|
2393
2535
|
role: "tree",
|
|
2394
|
-
|
|
2536
|
+
label: t("section.sessions"),
|
|
2537
|
+
rowKeys: rows.length === 0 ? ["empty"] : rows.map((row) => `session:${row.id}`),
|
|
2538
|
+
ready: list.phase === "ready" && workspaceReady && drag === null,
|
|
2539
|
+
resetKey: animationResetKey,
|
|
2395
2540
|
children: [rows.length === 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
2396
2541
|
className: WorkspaceBrowser_module_css_default.empty,
|
|
2542
|
+
"data-row-key": "empty",
|
|
2397
2543
|
children: t("empty.none")
|
|
2398
2544
|
}), rows.map((node) => {
|
|
2399
2545
|
const active = drag !== null;
|
|
@@ -2447,6 +2593,7 @@ window.__ModuleLoader__.load({
|
|
|
2447
2593
|
dropCommitted.current = false;
|
|
2448
2594
|
}
|
|
2449
2595
|
},
|
|
2596
|
+
menu: menu.session,
|
|
2450
2597
|
t
|
|
2451
2598
|
}, node.id);
|
|
2452
2599
|
})]
|
|
@@ -2519,7 +2666,7 @@ window.__ModuleLoader__.load({
|
|
|
2519
2666
|
* @param props - composed slot props (shell owner share + store + injected actions).
|
|
2520
2667
|
* @returns the region element tree.
|
|
2521
2668
|
*/
|
|
2522
|
-
function WorkspaceBrowser({ wide, usePanelInfo, expandSidebar, useSessions, useSessionStatus, useWorkspaces, useStore, actions, startSession, open, renameSession, forkSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, archiveSession, copySessionId, pinSession, unpinSession, createWorkspace, searchSessions, searchResultLimit, requestSearch, requestAddWorkspace, closeAddWorkspace, closeRenameRequest, setDirectoryBusy, useDirectoryFlow, useHostInfo, useWorkspaceShortcuts, useShortcuts, renderSlot, t }) {
|
|
2669
|
+
function WorkspaceBrowser({ wide, usePanelInfo, expandSidebar, useSessions, useSessionStatus, useWorkspaces, useStore, actions, startSession, open, renameSession, forkSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, archiveSession, copySessionId, pinSession, unpinSession, createWorkspace, searchSessions, searchResultLimit, requestSearch, requestAddWorkspace, closeAddWorkspace, closeRenameRequest, setDirectoryBusy, useDirectoryFlow, useHostInfo, useWorkspaceShortcuts, useShortcuts, useContextMenu, useSessionRenameRequest, useWorkspaceActionRequest, settleSessionRename, settleWorkspaceAction, renderSlot, t }) {
|
|
2523
2670
|
const home = useHostInfo((info) => info.home);
|
|
2524
2671
|
const list = useSessions((state) => state);
|
|
2525
2672
|
const workspaces = useWorkspaces((state) => state.items);
|
|
@@ -2882,6 +3029,54 @@ window.__ModuleLoader__.load({
|
|
|
2882
3029
|
setDeleteError(reason instanceof Error ? reason.message : String(reason));
|
|
2883
3030
|
});
|
|
2884
3031
|
};
|
|
3032
|
+
const { getTriggerProps } = useContextMenu();
|
|
3033
|
+
const menus = {
|
|
3034
|
+
session: (sessionId, displayTitle) => getTriggerProps({
|
|
3035
|
+
kind: "session",
|
|
3036
|
+
value: {
|
|
3037
|
+
sessionId,
|
|
3038
|
+
displayTitle
|
|
3039
|
+
}
|
|
3040
|
+
}),
|
|
3041
|
+
workspace: (workspaceId, path, displayTitle) => getTriggerProps({
|
|
3042
|
+
kind: "workspace",
|
|
3043
|
+
value: {
|
|
3044
|
+
workspaceId,
|
|
3045
|
+
path: path ?? "",
|
|
3046
|
+
displayTitle
|
|
3047
|
+
}
|
|
3048
|
+
})
|
|
3049
|
+
};
|
|
3050
|
+
const pendingSessionRename = useSessionRenameRequest((request) => request);
|
|
3051
|
+
(0, react.useEffect)(() => {
|
|
3052
|
+
if (pendingSessionRename === null) return;
|
|
3053
|
+
setSessionRenameTarget({
|
|
3054
|
+
sessionId: pendingSessionRename.sessionId,
|
|
3055
|
+
currentTitle: pendingSessionRename.currentTitle
|
|
3056
|
+
});
|
|
3057
|
+
setSessionRenameDraft(pendingSessionRename.currentTitle);
|
|
3058
|
+
setSessionRenameError(null);
|
|
3059
|
+
settleSessionRename();
|
|
3060
|
+
}, [pendingSessionRename, settleSessionRename]);
|
|
3061
|
+
const pendingWorkspaceAction = useWorkspaceActionRequest((request) => request);
|
|
3062
|
+
(0, react.useEffect)(() => {
|
|
3063
|
+
if (pendingWorkspaceAction === null) return;
|
|
3064
|
+
if (pendingWorkspaceAction.action === "rename") {
|
|
3065
|
+
setRenameTarget({
|
|
3066
|
+
workspaceId: pendingWorkspaceAction.workspaceId,
|
|
3067
|
+
currentTitle: pendingWorkspaceAction.currentTitle
|
|
3068
|
+
});
|
|
3069
|
+
setRenameDraft(pendingWorkspaceAction.currentTitle);
|
|
3070
|
+
setRenameError(null);
|
|
3071
|
+
} else {
|
|
3072
|
+
setDeleteTarget({
|
|
3073
|
+
workspaceId: pendingWorkspaceAction.workspaceId,
|
|
3074
|
+
title: pendingWorkspaceAction.currentTitle
|
|
3075
|
+
});
|
|
3076
|
+
setDeleteError(null);
|
|
3077
|
+
}
|
|
3078
|
+
settleWorkspaceAction();
|
|
3079
|
+
}, [pendingWorkspaceAction, settleWorkspaceAction]);
|
|
2885
3080
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
2886
3081
|
className: clsx(WorkspaceBrowser_module_css_default.root, !wide && WorkspaceBrowser_module_css_default.rail),
|
|
2887
3082
|
children: [
|
|
@@ -3022,6 +3217,8 @@ window.__ModuleLoader__.load({
|
|
|
3022
3217
|
usePanelInfo,
|
|
3023
3218
|
list,
|
|
3024
3219
|
sessionIds: orderedFlatSessionIds,
|
|
3220
|
+
workspaceReady,
|
|
3221
|
+
animationResetKey: `${groupBy}/${orderBy}`,
|
|
3025
3222
|
useSessionStatus,
|
|
3026
3223
|
open,
|
|
3027
3224
|
forkSession,
|
|
@@ -3035,13 +3232,16 @@ window.__ModuleLoader__.load({
|
|
|
3035
3232
|
revealSessionId,
|
|
3036
3233
|
onSessionRevealed: acknowledgeSessionReveal,
|
|
3037
3234
|
renderSlot,
|
|
3235
|
+
menu: menus,
|
|
3038
3236
|
t
|
|
3039
3237
|
}) : (0, react_jsx_runtime.jsx)(SessionTree, {
|
|
3040
3238
|
usePanelInfo,
|
|
3041
3239
|
list,
|
|
3240
|
+
animationResetKey: `${groupBy}/${orderBy}`,
|
|
3042
3241
|
useSessionStatus,
|
|
3043
3242
|
copySessionId,
|
|
3044
3243
|
renderSlot,
|
|
3244
|
+
menu: menus,
|
|
3045
3245
|
onSessionRename,
|
|
3046
3246
|
onSessionArchive,
|
|
3047
3247
|
onSessionPin,
|
|
@@ -3482,6 +3682,7 @@ window.__ModuleLoader__.load({
|
|
|
3482
3682
|
* declaration through `slots.inject()` instead of assuming order.
|
|
3483
3683
|
*/
|
|
3484
3684
|
const inject = [
|
|
3685
|
+
"contextMenus",
|
|
3485
3686
|
"slots",
|
|
3486
3687
|
"sessions",
|
|
3487
3688
|
"workspaces",
|
|
@@ -3507,6 +3708,8 @@ window.__ModuleLoader__.load({
|
|
|
3507
3708
|
create: () => viewInstance
|
|
3508
3709
|
};
|
|
3509
3710
|
const rowToast = (0, _qilin_agent_client_store.createSnapshotStore)(null);
|
|
3711
|
+
const sessionRenameRequest = (0, _qilin_agent_client_store.createSnapshotStore)(null);
|
|
3712
|
+
const workspaceActionRequest = (0, _qilin_agent_client_store.createSnapshotStore)(null);
|
|
3510
3713
|
let toastSeq = 0;
|
|
3511
3714
|
const notify = (toast) => {
|
|
3512
3715
|
rowToast.set({
|
|
@@ -3576,16 +3779,69 @@ window.__ModuleLoader__.load({
|
|
|
3576
3779
|
copySessionId: async (sessionId) => {
|
|
3577
3780
|
notify({ kind: await (0, _qilin_agent_client_ui_primitives.writeClipboard)(sessionId) ? "sessionIdCopied" : "sessionIdCopyFailed" });
|
|
3578
3781
|
},
|
|
3782
|
+
settleSessionRename: () => {
|
|
3783
|
+
sessionRenameRequest.set(null);
|
|
3784
|
+
},
|
|
3785
|
+
settleWorkspaceAction: () => {
|
|
3786
|
+
workspaceActionRequest.set(null);
|
|
3787
|
+
},
|
|
3579
3788
|
hooks: {
|
|
3580
3789
|
directoryFlow: browserFlowSource,
|
|
3581
3790
|
hostInfo,
|
|
3582
3791
|
workspaceShortcuts: shortcutControls.state,
|
|
3583
|
-
shortcuts: ctx.shortcuts.catalog
|
|
3792
|
+
shortcuts: ctx.shortcuts.catalog,
|
|
3793
|
+
sessionRenameRequest,
|
|
3794
|
+
workspaceActionRequest
|
|
3584
3795
|
}
|
|
3585
3796
|
});
|
|
3586
3797
|
installWorkspaceShortcuts(ctx, uiWorkspace, shortcutControls, (sessionId) => {
|
|
3587
3798
|
uiWorkspace.archiveSession(sessionId);
|
|
3588
3799
|
});
|
|
3800
|
+
ctx.slots.inject("contextMenu.icon", function* () {
|
|
3801
|
+
for (const [key, Icon] of Object.entries({
|
|
3802
|
+
"workspace.rename": _qilin_agent_client_ui_primitives.IconEditOutlineRegular,
|
|
3803
|
+
"workspace.delete": _qilin_agent_client_ui_primitives.IconTrashOutlineRegular,
|
|
3804
|
+
"workspace.pin": _qilin_agent_client_ui_primitives.IconPinOutlineRegular,
|
|
3805
|
+
"workspace.unpin": _qilin_agent_client_ui_primitives.IconPinFillRegular,
|
|
3806
|
+
"workspace.archive": _qilin_agent_client_ui_primitives.IconArchiveOutlineRegular,
|
|
3807
|
+
"workspace.fork": _qilin_agent_client_ui_primitives.IconBranchOutlineRegular,
|
|
3808
|
+
"workspace.copy": _qilin_agent_client_ui_primitives.IconCopyOutlineRegular
|
|
3809
|
+
})) yield ctx.slots.register({
|
|
3810
|
+
name: "contextMenu.icon",
|
|
3811
|
+
key
|
|
3812
|
+
}, Icon);
|
|
3813
|
+
});
|
|
3814
|
+
registerWorkspaceMenus(ctx, {
|
|
3815
|
+
pin: (sessionId) => {
|
|
3816
|
+
uiWorkspace.pinSession(sessionId);
|
|
3817
|
+
},
|
|
3818
|
+
unpin: (sessionId) => {
|
|
3819
|
+
uiWorkspace.unpinSession(sessionId);
|
|
3820
|
+
},
|
|
3821
|
+
requestSessionRename: (sessionId, currentTitle) => {
|
|
3822
|
+
sessionRenameRequest.set({
|
|
3823
|
+
sessionId,
|
|
3824
|
+
currentTitle
|
|
3825
|
+
});
|
|
3826
|
+
},
|
|
3827
|
+
fork: (sessionId) => {
|
|
3828
|
+
browserInjected().forkSession(sessionId);
|
|
3829
|
+
},
|
|
3830
|
+
copySessionId: (sessionId) => {
|
|
3831
|
+
browserInjected().copySessionId(sessionId);
|
|
3832
|
+
},
|
|
3833
|
+
archive: (sessionId) => {
|
|
3834
|
+
browserInjected().archiveSession(sessionId);
|
|
3835
|
+
},
|
|
3836
|
+
requestWorkspaceAction: (workspaceId, displayTitle, action) => {
|
|
3837
|
+
if (workspaces.list.getSnapshot().items.find((row) => row.workspaceId === workspaceId) === void 0) return;
|
|
3838
|
+
workspaceActionRequest.set({
|
|
3839
|
+
action,
|
|
3840
|
+
workspaceId,
|
|
3841
|
+
currentTitle: displayTitle
|
|
3842
|
+
});
|
|
3843
|
+
}
|
|
3844
|
+
});
|
|
3589
3845
|
registerToasts(ctx);
|
|
3590
3846
|
const rowToastInjected = () => ({
|
|
3591
3847
|
hooks: { toast: rowToast },
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Structured Session and Workspace actions over the workspace owner's existing callbacks. */
|
|
2
|
+
import type { Context } from '@qilin-agent/kylin';
|
|
3
|
+
import type { WorkspaceId } from '@qilin-agent/api-workspace-controller/client';
|
|
4
|
+
import type { SessionId } from '@qilin-agent/session/types';
|
|
5
|
+
/** The callbacks the browser already runs for its own row menus. */
|
|
6
|
+
export interface WorkspaceMenuActions {
|
|
7
|
+
/** Pin a Session ahead of unpinned rows. */
|
|
8
|
+
pin: (sessionId: SessionId) => void;
|
|
9
|
+
/** Drop one Session's pin. */
|
|
10
|
+
unpin: (sessionId: SessionId) => void;
|
|
11
|
+
/** Open the browser-owned Session rename dialog. */
|
|
12
|
+
requestSessionRename: (sessionId: SessionId, currentTitle: string) => void;
|
|
13
|
+
/** Fork a Session at its last completed turn. */
|
|
14
|
+
fork: (sessionId: SessionId) => void;
|
|
15
|
+
/** Copy a Session's complete id and report the outcome. */
|
|
16
|
+
copySessionId: (sessionId: SessionId) => void;
|
|
17
|
+
/** Archive a Session into the registry-global set. */
|
|
18
|
+
archive: (sessionId: SessionId) => void;
|
|
19
|
+
/** Open the browser-owned Workspace rename or delete dialog. */
|
|
20
|
+
requestWorkspaceAction: (workspaceId: WorkspaceId, currentTitle: string, action: 'rename' | 'delete') => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Register the built-in Session and Workspace actions on the shared menu.
|
|
24
|
+
* @param ctx - workspace plugin context owning menus, membership, and locale.
|
|
25
|
+
* @param actions - shared row callbacks; execution always uses the captured object.
|
|
26
|
+
*/
|
|
27
|
+
export declare function registerWorkspaceMenus(ctx: Context, actions: WorkspaceMenuActions): void;
|
|
28
|
+
//# sourceMappingURL=context-menus.d.ts.map
|
|
@@ -30,6 +30,27 @@ import type { SessionId } from '@qilin-agent/session/types';
|
|
|
30
30
|
import type { createWorkspaceViewStore } from '../stores.ts';
|
|
31
31
|
import type { ShortcutCatalogEntry } from '@qilin-agent/client-shortcuts/client';
|
|
32
32
|
import type { WorkspaceShortcutState } from '../shortcuts.ts';
|
|
33
|
+
declare module '@qilin-agent/client-ui-context-menu/client' {
|
|
34
|
+
interface ContextMenuTargetMap {
|
|
35
|
+
/** Registered Workspace header; path and title belong to the clicked row. */
|
|
36
|
+
workspace: {
|
|
37
|
+
readonly workspaceId: WorkspaceId;
|
|
38
|
+
readonly path: string;
|
|
39
|
+
readonly displayTitle: string;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** One menu request for the browser-owned Session rename dialog. */
|
|
44
|
+
export type SessionRenameRequest = {
|
|
45
|
+
readonly sessionId: SessionId;
|
|
46
|
+
readonly currentTitle: string;
|
|
47
|
+
};
|
|
48
|
+
/** One menu request for the browser-owned Workspace rename or delete dialog. */
|
|
49
|
+
export type WorkspaceActionRequest = {
|
|
50
|
+
readonly action: 'rename' | 'delete';
|
|
51
|
+
readonly workspaceId: WorkspaceId;
|
|
52
|
+
readonly currentTitle: string;
|
|
53
|
+
};
|
|
33
54
|
/**
|
|
34
55
|
* Owner share of the directory-flow holes: the complete conversation between
|
|
35
56
|
* the trigger surface and the picking interaction. The occupant reads `open`
|
|
@@ -130,6 +151,10 @@ export type WorkspaceBrowserInjected = {
|
|
|
130
151
|
workspaceShortcuts: HostObservable<WorkspaceShortcutState>;
|
|
131
152
|
/** Effective command catalog for row shortcut hints. */
|
|
132
153
|
shortcuts: HostObservable<readonly ShortcutCatalogEntry[]>;
|
|
154
|
+
/** Session rename requested by the shared context menu. */
|
|
155
|
+
sessionRenameRequest: HostObservable<SessionRenameRequest | null>;
|
|
156
|
+
/** Workspace rename or delete requested by the shared context menu. */
|
|
157
|
+
workspaceActionRequest: HostObservable<WorkspaceActionRequest | null>;
|
|
133
158
|
};
|
|
134
159
|
/** Open the browser search and focus its input. */
|
|
135
160
|
requestSearch: () => void;
|
|
@@ -192,6 +217,10 @@ export type WorkspaceBrowserInjected = {
|
|
|
192
217
|
}) => Promise<WorkspaceView>;
|
|
193
218
|
/** Copy the complete Session id and report acceptance or failure through the keyed notice. */
|
|
194
219
|
copySessionId: (sessionId: SessionId) => Promise<void>;
|
|
220
|
+
/** Consume a menu-originated Session rename request once its dialog is open. */
|
|
221
|
+
settleSessionRename: () => void;
|
|
222
|
+
/** Consume a menu-originated Workspace action request once its dialog is open. */
|
|
223
|
+
settleWorkspaceAction: () => void;
|
|
195
224
|
};
|
|
196
225
|
/** One transient Workspace notice rendered by the overlay toast entry. */
|
|
197
226
|
export type RowToast = {
|
|
@@ -1,8 +1,20 @@
|
|
|
1
|
+
import type { ContextMenuTriggerProps } from '@qilin-agent/client-ui-context-menu/client';
|
|
1
2
|
import type { PropsRenderSlots } from '@qilin-agent/client-ui-slots';
|
|
3
|
+
import type { WorkspaceId } from '@qilin-agent/api-workspace-controller/client';
|
|
2
4
|
import type { WorkspaceBrowserProps } from '../contract/slots.ts';
|
|
3
5
|
import type { GroupNode, SearchResultNode, SessionNode } from '../tree.ts';
|
|
4
6
|
/** The standard locale seat, prop-passed from the browser root. */
|
|
5
7
|
type RowTranslate = WorkspaceBrowserProps['t'];
|
|
8
|
+
/**
|
|
9
|
+
* Shared-menu gesture bindings the browser resolves for one row. The rows own
|
|
10
|
+
* no menu state: the menu plugin captures the target and renders the actions.
|
|
11
|
+
*/
|
|
12
|
+
export interface RowMenus {
|
|
13
|
+
/** Bind one Session row's captured identity. */
|
|
14
|
+
session: (sessionId: SessionNode['id'], displayTitle: string) => ContextMenuTriggerProps;
|
|
15
|
+
/** Bind one Workspace header's captured identity and directory. */
|
|
16
|
+
workspace: (workspaceId: WorkspaceId, path: string | undefined, displayTitle: string) => ContextMenuTriggerProps;
|
|
17
|
+
}
|
|
6
18
|
/**
|
|
7
19
|
* Row drag wiring supplied by the tree owner. `drop` reports the half of the
|
|
8
20
|
* row where the pointer released so the owner can resolve an insert anchor.
|
|
@@ -38,7 +50,7 @@ interface WorkspaceRowDragProps {
|
|
|
38
50
|
* @param props.t - the browser root's locale seat.
|
|
39
51
|
* @returns the row element.
|
|
40
52
|
*/
|
|
41
|
-
export declare function ProjectRowItem({ group, containsCurrentDescendant, onToggle, onCreate, actions, drag, home, t }: {
|
|
53
|
+
export declare function ProjectRowItem({ group, containsCurrentDescendant, onToggle, onCreate, actions, drag, home, menu, t }: {
|
|
42
54
|
group: GroupNode;
|
|
43
55
|
containsCurrentDescendant?: boolean;
|
|
44
56
|
onToggle: () => void;
|
|
@@ -48,6 +60,8 @@ export declare function ProjectRowItem({ group, containsCurrentDescendant, onTog
|
|
|
48
60
|
rename: () => void;
|
|
49
61
|
delete: () => void;
|
|
50
62
|
} | undefined;
|
|
63
|
+
/** Shared-menu gesture bindings for a real Workspace header. */
|
|
64
|
+
menu?: RowMenus['workspace'] | undefined;
|
|
51
65
|
/** Present only for real Workspace rows in the grouped view. */
|
|
52
66
|
drag?: WorkspaceRowDragProps | undefined;
|
|
53
67
|
/** Host account home; POSIX home-rooted hover paths display as `~`. */
|
|
@@ -91,7 +105,7 @@ export declare function SearchResultItem({ result, currentId, onOpen, t }: {
|
|
|
91
105
|
* @param props.t - the browser root's locale seat.
|
|
92
106
|
* @returns the session row.
|
|
93
107
|
*/
|
|
94
|
-
export declare function SessionNodeItem({ node, currentId, now, onOpen, onRename, onFork, onArchive, onCopyId, onPin, onUnpin, onReveal, drag, renderSlot, t, }: {
|
|
108
|
+
export declare function SessionNodeItem({ node, currentId, now, onOpen, onRename, onFork, onArchive, onCopyId, onPin, onUnpin, onReveal, drag, renderSlot, menu, t, }: {
|
|
95
109
|
node: SessionNode;
|
|
96
110
|
currentId: string | undefined;
|
|
97
111
|
now: number;
|
|
@@ -114,6 +128,8 @@ export declare function SessionNodeItem({ node, currentId, now, onOpen, onRename
|
|
|
114
128
|
drag?: RowDragProps | undefined;
|
|
115
129
|
/** Child-seat renderer for the row's leading decoration and hover-card section. */
|
|
116
130
|
renderSlot: SessionRowRenderSlots;
|
|
131
|
+
/** Shared-menu gesture bindings; blank rows capture nothing. */
|
|
132
|
+
menu?: RowMenus['session'] | undefined;
|
|
117
133
|
t: RowTranslate;
|
|
118
134
|
}): import("react").JSX.Element;
|
|
119
135
|
export {};
|
|
@@ -4,5 +4,5 @@ import type { WorkspaceBrowserProps } from '../contract/slots.ts';
|
|
|
4
4
|
* @param props - composed slot props (shell owner share + store + injected actions).
|
|
5
5
|
* @returns the region element tree.
|
|
6
6
|
*/
|
|
7
|
-
export declare function WorkspaceBrowser({ wide, usePanelInfo, expandSidebar, useSessions, useSessionStatus, useWorkspaces, useStore, actions, startSession, open, renameSession, forkSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, archiveSession, copySessionId, pinSession, unpinSession, createWorkspace, searchSessions, searchResultLimit, requestSearch, requestAddWorkspace, closeAddWorkspace, closeRenameRequest, setDirectoryBusy, useDirectoryFlow, useHostInfo, useWorkspaceShortcuts, useShortcuts, renderSlot, t, }: WorkspaceBrowserProps): import("react").JSX.Element;
|
|
7
|
+
export declare function WorkspaceBrowser({ wide, usePanelInfo, expandSidebar, useSessions, useSessionStatus, useWorkspaces, useStore, actions, startSession, open, renameSession, forkSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, archiveSession, copySessionId, pinSession, unpinSession, createWorkspace, searchSessions, searchResultLimit, requestSearch, requestAddWorkspace, closeAddWorkspace, closeRenameRequest, setDirectoryBusy, useDirectoryFlow, useHostInfo, useWorkspaceShortcuts, useShortcuts, useContextMenu, useSessionRenameRequest, useWorkspaceActionRequest, settleSessionRename, settleWorkspaceAction, renderSlot, t, }: WorkspaceBrowserProps): import("react").JSX.Element;
|
|
8
8
|
//# sourceMappingURL=WorkspaceBrowser.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qilin-agent/client-ui-workspace",
|
|
3
3
|
"description": "Workspace picker plugin: one WorkspacePicker registered into the sidebar and empty-state workspace slots",
|
|
4
|
-
"version": "3.1.
|
|
4
|
+
"version": "3.1.9",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"@qilin-agent/client-connection",
|
|
38
38
|
"@qilin-agent/client-locale",
|
|
39
39
|
"@qilin-agent/client-shortcuts",
|
|
40
|
+
"@qilin-agent/client-ui-context-menu",
|
|
40
41
|
"@qilin-agent/client-ui-conversation",
|
|
41
42
|
"@qilin-agent/client-ui-layout",
|
|
42
43
|
"@qilin-agent/client-ui-renderer",
|
|
@@ -48,33 +49,34 @@
|
|
|
48
49
|
},
|
|
49
50
|
"license": "MIT",
|
|
50
51
|
"peerDependencies": {
|
|
51
|
-
"@qilin-agent/kylin": "~4.0.
|
|
52
|
+
"@qilin-agent/kylin": "~4.0.7"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
55
|
+
"@qilin-agent/api-remotes": "3.1.9",
|
|
56
|
+
"@qilin-agent/api-session-controller": "3.1.9",
|
|
57
|
+
"@qilin-agent/api-workspace-controller": "3.1.9",
|
|
58
|
+
"@qilin-agent/client-connection": "3.1.9",
|
|
59
|
+
"@qilin-agent/client-locale": "3.1.9",
|
|
60
|
+
"@qilin-agent/client-shortcuts": "3.1.9",
|
|
61
|
+
"@qilin-agent/client-store": "3.1.9",
|
|
62
|
+
"@qilin-agent/client-test-runtime": "3.1.9",
|
|
63
|
+
"@qilin-agent/client-ui-context-menu": "3.1.9",
|
|
64
|
+
"@qilin-agent/client-ui-conversation": "3.1.9",
|
|
65
|
+
"@qilin-agent/client-ui-layout": "3.1.9",
|
|
66
|
+
"@qilin-agent/client-ui-primitives": "3.1.9",
|
|
67
|
+
"@qilin-agent/client-ui-renderer": "3.1.9",
|
|
68
|
+
"@qilin-agent/client-ui-session": "3.1.9",
|
|
69
|
+
"@qilin-agent/client-ui-sidebar": "3.1.9",
|
|
70
|
+
"@qilin-agent/client-ui-slots": "3.1.9",
|
|
71
|
+
"@qilin-agent/kylin": "~4.0.7",
|
|
72
|
+
"@qilin-agent/schedule": "3.1.9",
|
|
73
|
+
"@qilin-agent/session": "3.1.9",
|
|
74
|
+
"@qilin-agent/subagent": "3.1.9",
|
|
75
|
+
"@qilin-agent/typert-protocol": "3.1.9",
|
|
76
|
+
"@qilin-agent/util-workspace-path": "3.1.9",
|
|
54
77
|
"@types/react": "~18.3.1",
|
|
55
78
|
"clsx": "^2.0.0",
|
|
56
|
-
"react": "^18.2.0"
|
|
57
|
-
"@qilin-agent/api-remotes": "3.1.7",
|
|
58
|
-
"@qilin-agent/api-session-controller": "3.1.7",
|
|
59
|
-
"@qilin-agent/api-workspace-controller": "3.1.7",
|
|
60
|
-
"@qilin-agent/client-connection": "3.1.7",
|
|
61
|
-
"@qilin-agent/client-shortcuts": "3.1.7",
|
|
62
|
-
"@qilin-agent/client-locale": "3.1.7",
|
|
63
|
-
"@qilin-agent/client-test-runtime": "3.1.7",
|
|
64
|
-
"@qilin-agent/client-store": "3.1.7",
|
|
65
|
-
"@qilin-agent/client-ui-conversation": "3.1.7",
|
|
66
|
-
"@qilin-agent/client-ui-layout": "3.1.7",
|
|
67
|
-
"@qilin-agent/client-ui-primitives": "3.1.7",
|
|
68
|
-
"@qilin-agent/client-ui-renderer": "3.1.7",
|
|
69
|
-
"@qilin-agent/client-ui-session": "3.1.7",
|
|
70
|
-
"@qilin-agent/client-ui-sidebar": "3.1.7",
|
|
71
|
-
"@qilin-agent/client-ui-slots": "3.1.7",
|
|
72
|
-
"@qilin-agent/schedule": "3.1.7",
|
|
73
|
-
"@qilin-agent/session": "3.1.7",
|
|
74
|
-
"@qilin-agent/subagent": "3.1.7",
|
|
75
|
-
"@qilin-agent/typert-protocol": "3.1.7",
|
|
76
|
-
"@qilin-agent/util-workspace-path": "3.1.7",
|
|
77
|
-
"@qilin-agent/kylin": "~4.0.5"
|
|
79
|
+
"react": "^18.2.0"
|
|
78
80
|
},
|
|
79
81
|
"files": [
|
|
80
82
|
"lib/index.js",
|