@qilin-agent/client-ui-workspace 3.1.6 → 3.1.8

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 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: faf570bcad825439
31
- zh: a65300b0b40a8550
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: dc93d10e80153f62
52
- zh: 5caa3399dbf84f62
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.
@@ -724,29 +838,49 @@ window.__ModuleLoader__.load({
724
838
  };
725
839
  }, "ui-workspace: Workspace navigation policy");
726
840
  }
727
- async connectWorkspace(workspaceId) {
841
+ async connectWorkspace(workspaceId, options) {
728
842
  const workspace = this.workspaces.list.getSnapshot().items.find((item) => item.workspaceId === workspaceId);
729
843
  if (workspace === void 0) throw new Error(`uiWorkspace.connectWorkspace: unknown workspace ${workspaceId}`);
730
844
  const inflight = this.connecting.get(workspaceId);
731
- if (inflight !== void 0) return inflight;
845
+ if (inflight !== void 0 && inflight.agentPreset === options?.agentPreset) return inflight.attempt;
732
846
  const archived = this.workspaces.list.getSnapshot().archivedSessionIds;
733
847
  const sessions = this.sessions.list.getSnapshot();
734
848
  for (const id of sessions.ids) {
735
849
  const summary = sessions.byId[id];
736
- if (summary !== void 0 && summary.blank && summary.cwd === workspace.path && workspace.sessionIds.includes(summary.id) && !archived.includes(summary.id)) return summary.id;
850
+ const listed = summary?.projectionValues?.agentPreset;
851
+ if (summary !== void 0 && summary.blank && summary.cwd === workspace.path && workspace.sessionIds.includes(summary.id) && !archived.includes(summary.id) && (options?.agentPreset === void 0 || typeof listed !== "string" || listed === options.agentPreset)) {
852
+ const reuse = this.sessions.create({
853
+ workspaceId,
854
+ sessionId: summary.id,
855
+ ...options?.agentPreset === void 0 ? {} : { agentPreset: options.agentPreset }
856
+ }).finally(() => {
857
+ this.connecting.delete(workspaceId);
858
+ });
859
+ this.connecting.set(workspaceId, {
860
+ agentPreset: options?.agentPreset,
861
+ attempt: reuse
862
+ });
863
+ return reuse;
864
+ }
737
865
  }
738
- const attempt = this.sessions.create({ workspaceId }).finally(() => {
866
+ const attempt = this.sessions.create({
867
+ workspaceId,
868
+ ...options?.agentPreset === void 0 ? {} : { agentPreset: options.agentPreset }
869
+ }).finally(() => {
739
870
  this.connecting.delete(workspaceId);
740
871
  });
741
- this.connecting.set(workspaceId, attempt);
872
+ this.connecting.set(workspaceId, {
873
+ agentPreset: options?.agentPreset,
874
+ attempt
875
+ });
742
876
  return attempt;
743
877
  }
744
878
  openSession(target) {
745
879
  this.replaceMain(target, this.lifetime.signal);
746
880
  }
747
- async openWorkspace(workspaceId, beforeOpen) {
881
+ async openWorkspace(workspaceId, beforeOpen, options) {
748
882
  const navigation = AbortSignal.any([this.ctx.layout.beginNavigation(), this.lifetime.signal]);
749
- const sessionId = await this.connectWorkspace(workspaceId);
883
+ const sessionId = await this.connectWorkspace(workspaceId, options);
750
884
  if (navigation.aborted) return;
751
885
  this.replaceMain(sessionId, navigation, beforeOpen);
752
886
  }
@@ -759,8 +893,9 @@ window.__ModuleLoader__.load({
759
893
  if (!navigation.aborted) this.replaceMain(childId, navigation);
760
894
  }
761
895
  startSession(workspaceId, options) {
762
- const draftOptions = options === void 0 ? void 0 : structuredClone(options);
763
- const initializeDraft = draftOptions !== void 0 && (draftOptions.prompt !== void 0 || draftOptions.clearPreviousDraft === true);
896
+ const { agentPreset, ...draft } = options ?? {};
897
+ const draftOptions = options === void 0 ? void 0 : structuredClone(draft);
898
+ const initializeDraft = draftOptions !== void 0 && (draftOptions.prompt !== void 0 || (draftOptions.whenDraftExists ?? (draftOptions.clearPreviousDraft === true ? "replace" : "keep")) === "replace");
764
899
  const workspace = this.workspaces.list.getSnapshot();
765
900
  const sessions = this.sessions.list.getSnapshot();
766
901
  const current = this.mainReference?.sessionId;
@@ -782,7 +917,7 @@ window.__ModuleLoader__.load({
782
917
  const binding = this.sessions.binding(id);
783
918
  if (binding === void 0) this.draftPreparationFailed();
784
919
  this.prepareDraft(binding, draftOptions);
785
- } : void 0).catch((reason) => {
920
+ } : void 0, agentPreset === void 0 ? void 0 : { agentPreset }).catch((reason) => {
786
921
  console.warn("new session failed:", reason);
787
922
  });
788
923
  }
@@ -956,7 +1091,7 @@ window.__ModuleLoader__.load({
956
1091
  }
957
1092
  //#endregion
958
1093
  //#region \0qilin-css:/home/runner/work/QiLin/QiLin/packages/client/ui-workspace/src/client/rows/Rows.module.css.mjs
959
- const css$2 = ".RrwzeW_projectRow,.RrwzeW_sessionRow{padding:0 8px;cursor:pointer;user-select:none;color:var(--qilin-alias-label-primary);border-radius:8px;align-items:center;gap:6px;padding-inline-start:calc(8px + var(--dsh-workspace-indent,0px));display:flex}.RrwzeW_projectRow:hover,.RrwzeW_sessionRow:hover,.RrwzeW_sessionRow.RrwzeW_selected{background:var(--qilin-alias-interactive-bg-hover)}.RrwzeW_searchResultRow{box-sizing:border-box;cursor:pointer;text-align:left;width:100%;min-height:48px;color:var(--qilin-alias-label-primary);background:0 0;border:none;border-radius:8px;flex-direction:column;align-items:stretch;padding:4px 8px;display:flex}.RrwzeW_searchResultRow:hover,.RrwzeW_searchResultRow.RrwzeW_selected{background:var(--qilin-alias-interactive-bg-hover)}.RrwzeW_searchResultHeading{align-items:center;min-width:0;display:flex}.RrwzeW_searchResultTitle{text-overflow:ellipsis;white-space:nowrap;flex:0 auto;min-width:0;margin-left:4px;font-size:14px;line-height:20px;overflow:hidden}.RrwzeW_searchResultMeta{align-items:center;gap:6px;min-width:0;margin-left:20px;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{height:32px;animation:RrwzeW_row-in .15s var(--ds-ease-in-out);gap:0}.RrwzeW_sessionRow .RrwzeW_title{margin:0 6px 0 4px}@keyframes RrwzeW_row-in{0%{opacity:0}}.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}.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:.5px 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;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:hover .RrwzeW_pinIndicator,.RrwzeW_sessionRow.RrwzeW_menuOpen .RrwzeW_pinIndicator{display:none}.RrwzeW_rowActions{flex:none;align-items:center;gap:15px;display:none}.RrwzeW_projectRow:hover .RrwzeW_rowActions,.RrwzeW_sessionRow:hover .RrwzeW_rowActions,.RrwzeW_projectRow.RrwzeW_menuOpen .RrwzeW_rowActions,.RrwzeW_sessionRow.RrwzeW_menuOpen .RrwzeW_rowActions{display:inline-flex}.RrwzeW_sessionRow:hover .RrwzeW_time,.RrwzeW_sessionRow.RrwzeW_menuOpen .RrwzeW_time{display:none}.RrwzeW_projectRow.RrwzeW_menuOpen,.RrwzeW_sessionRow.RrwzeW_menuOpen{background: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;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:4px}.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:16px;height:16px;color:var(--qilin-alias-label-tertiary);background:0 0;border:none;border-radius:4px;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.RrwzeW_iconButton:hover{color:var(--qilin-alias-label-primary)}.RrwzeW_chevron{color:var(--qilin-alias-label-caption)}@media (prefers-reduced-motion:reduce){.RrwzeW_sessionRow,.RrwzeW_arrow{transition:none;animation:none}}";
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}}";
960
1095
  const tagId$2 = "@qilin-agent/client-ui-workspace/Rows.module.css";
961
1096
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
962
1097
  const tag = document.createElement("style");
@@ -986,7 +1121,6 @@ window.__ModuleLoader__.load({
986
1121
  "projectRow": "RrwzeW_projectRow",
987
1122
  "projectText": "RrwzeW_projectText",
988
1123
  "renameInput": "RrwzeW_renameInput",
989
- "row-in": "RrwzeW_row-in",
990
1124
  "rowActions": "RrwzeW_rowActions",
991
1125
  "searchResultHeading": "RrwzeW_searchResultHeading",
992
1126
  "searchResultMeta": "RrwzeW_searchResultMeta",
@@ -994,6 +1128,7 @@ window.__ModuleLoader__.load({
994
1128
  "searchResultSnippet": "RrwzeW_searchResultSnippet",
995
1129
  "searchResultTitle": "RrwzeW_searchResultTitle",
996
1130
  "searchResultWorkspace": "RrwzeW_searchResultWorkspace",
1131
+ "secondaryAction": "RrwzeW_secondaryAction",
997
1132
  "selected": "RrwzeW_selected",
998
1133
  "sessionRow": "RrwzeW_sessionRow",
999
1134
  "slot": "RrwzeW_slot",
@@ -1008,9 +1143,10 @@ window.__ModuleLoader__.load({
1008
1143
  * all data and callbacks arrive via props. Hover swaps (folder->chevron,
1009
1144
  * time->ellipsis, action buttons) are CSS-only, and a session row's clipped
1010
1145
  * title marquees programmatically while the row is hovered, fading both
1011
- * clipping edges from the depth of its travel. Row ... menus are
1012
- * visual-only except workspace Rename/Delete and session Rename/Fork/Archive; the
1013
- * session and workspace hover cards are suppressed while a menu is open.
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.
1014
1150
  */
1015
1151
  /** Row display title: blank rows show the localized New Session label. */
1016
1152
  function displayTitle(node, t) {
@@ -1018,10 +1154,6 @@ window.__ModuleLoader__.load({
1018
1154
  }
1019
1155
  const MIN_TITLE_REVEAL_PX = 8;
1020
1156
  const TITLE_MARQUEE_PX_PER_MS = .03;
1021
- /** Whether the platform asked for reduced motion; jsdom implements no matchMedia. */
1022
- function prefersReducedMotion() {
1023
- return typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
1024
- }
1025
1157
  /**
1026
1158
  * Place the title's scroll position and publish the stylesheet's fade-mask
1027
1159
  * hooks: `data-scrolled` while the title has left its start (left fade) and
@@ -1073,13 +1205,14 @@ window.__ModuleLoader__.load({
1073
1205
  cancelAnimationFrame(frame.current);
1074
1206
  }, []);
1075
1207
  return (0, react.useMemo)(() => ({
1076
- enter: () => {
1208
+ enter: (event) => {
1209
+ if (event.pointerType === "touch") return;
1077
1210
  /* v8 ignore next -- defensive: the title span renders unconditionally. */
1078
1211
  if (title.current === null) return;
1079
1212
  const element = title.current;
1080
1213
  const range = element.scrollWidth - element.clientWidth;
1081
1214
  if (range <= MIN_TITLE_REVEAL_PX) return;
1082
- if (prefersReducedMotion()) {
1215
+ if ((0, _qilin_agent_client_ui_primitives.prefersReducedMotion)()) {
1083
1216
  placeTitle(element, range, range);
1084
1217
  return;
1085
1218
  }
@@ -1165,7 +1298,7 @@ window.__ModuleLoader__.load({
1165
1298
  * @param props.t - the browser root's locale seat.
1166
1299
  * @returns the row element.
1167
1300
  */
1168
- function ProjectRowItem({ group, containsCurrentDescendant = false, onToggle, onCreate, actions, drag, home, t }) {
1301
+ function ProjectRowItem({ group, containsCurrentDescendant = false, onToggle, onCreate, actions, drag, home, menu, t }) {
1169
1302
  const row = group;
1170
1303
  const label = row.workspaceId === void 0 ? t("group.ungrouped") : row.label;
1171
1304
  const active = containsCurrentDescendant || group.expanded && group.containsCurrent;
@@ -1173,18 +1306,29 @@ window.__ModuleLoader__.load({
1173
1306
  const workspaceMenuItems = [{
1174
1307
  id: "rename",
1175
1308
  label: t("rename"),
1176
- icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconEditOutline16, {})
1309
+ icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconEditOutlineRegular, {})
1177
1310
  }, {
1178
1311
  id: "delete",
1179
1312
  label: t("delete.workspace"),
1180
- icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconTrashOutline16, {}),
1313
+ icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconTrashOutlineRegular, {}),
1181
1314
  danger: true
1182
1315
  }];
1316
+ const menuProps = row.workspaceId === void 0 ? void 0 : menu?.(row.workspaceId, row.cwd, label);
1183
1317
  const ownRow = (0, react_jsx_runtime.jsxs)("div", {
1184
1318
  className: clsx(Rows_module_css_default.projectRow, menuOpen && Rows_module_css_default.menuOpen),
1185
1319
  role: "treeitem",
1320
+ "data-row-key": `workspace:${row.key}`,
1186
1321
  "aria-expanded": row.expanded,
1322
+ tabIndex: 0,
1323
+ ...menuProps,
1187
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
+ },
1188
1332
  draggable: drag !== void 0,
1189
1333
  onDragStart: drag === void 0 ? void 0 : (e) => {
1190
1334
  e.dataTransfer.effectAllowed = "move";
@@ -1195,11 +1339,11 @@ window.__ModuleLoader__.load({
1195
1339
  children: [
1196
1340
  (0, react_jsx_runtime.jsx)("span", {
1197
1341
  className: clsx(Rows_module_css_default.slot, Rows_module_css_default.folder, active && Rows_module_css_default.folderActive),
1198
- children: row.expanded ? (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconFolderOpen16, {}) : (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconFolderClose16, {})
1342
+ children: row.expanded ? (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconFolderOpenRegular, {}) : (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconFolderCloseRegular, {})
1199
1343
  }),
1200
1344
  (0, react_jsx_runtime.jsx)("span", {
1201
1345
  className: clsx(Rows_module_css_default.slot, Rows_module_css_default.chevron),
1202
- children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconTriangleRightFill14, { className: clsx(Rows_module_css_default.arrow, row.expanded && Rows_module_css_default.arrowOpen) })
1346
+ children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconTriangleRightFillRegular, { className: clsx(Rows_module_css_default.arrow, row.expanded && Rows_module_css_default.arrowOpen) })
1203
1347
  }),
1204
1348
  (0, react_jsx_runtime.jsx)("span", {
1205
1349
  className: Rows_module_css_default.projectText,
@@ -1233,7 +1377,7 @@ window.__ModuleLoader__.load({
1233
1377
  e.stopPropagation();
1234
1378
  setMenuOpen((v) => !v);
1235
1379
  },
1236
- children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconEllipsisOutline16, {})
1380
+ children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconEllipsisOutlineRegular, {})
1237
1381
  })
1238
1382
  }), (0, react_jsx_runtime.jsx)("button", {
1239
1383
  type: "button",
@@ -1243,7 +1387,7 @@ window.__ModuleLoader__.load({
1243
1387
  e.stopPropagation();
1244
1388
  onCreate();
1245
1389
  },
1246
- children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconPlusOutline16, {})
1390
+ children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconPlusOutlineRegular, { size: 14 })
1247
1391
  })]
1248
1392
  })
1249
1393
  ]
@@ -1424,8 +1568,9 @@ window.__ModuleLoader__.load({
1424
1568
  * @param props.t - the browser root's locale seat.
1425
1569
  * @returns the session row.
1426
1570
  */
1427
- 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 }) {
1428
1572
  const row = node;
1573
+ const menuProps = row.blank ? void 0 : menu?.(node.id, row.title);
1429
1574
  const title = displayTitle(node, t);
1430
1575
  const selected = node.id === currentId;
1431
1576
  const statuses = sessionStatuses(node, t);
@@ -1449,17 +1594,17 @@ window.__ModuleLoader__.load({
1449
1594
  {
1450
1595
  id: "rename",
1451
1596
  label: t("rename"),
1452
- icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconEditOutline16, {})
1597
+ icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconEditOutlineRegular, {})
1453
1598
  },
1454
1599
  {
1455
1600
  id: "fork",
1456
1601
  label: t("menu.fork"),
1457
- icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconBranchOutline16, {})
1602
+ icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconBranchOutlineRegular, {})
1458
1603
  },
1459
1604
  {
1460
1605
  id: "archive",
1461
1606
  label: t("menu.archiveSession"),
1462
- icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconArchiveOutline20, { size: 16 })
1607
+ icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconArchiveOutlineRegular, { size: 16 })
1463
1608
  },
1464
1609
  {
1465
1610
  id: "copyId",
@@ -1472,7 +1617,9 @@ window.__ModuleLoader__.load({
1472
1617
  ref: rowRef,
1473
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),
1474
1619
  role: "treeitem",
1620
+ "data-row-key": `session:${node.id}`,
1475
1621
  "aria-selected": selected,
1622
+ ...menuProps,
1476
1623
  onClick: () => {
1477
1624
  onOpen(node.id);
1478
1625
  },
@@ -1538,7 +1685,7 @@ window.__ModuleLoader__.load({
1538
1685
  e.stopPropagation();
1539
1686
  setMenuOpen((v) => !v);
1540
1687
  },
1541
- children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconEllipsisOutline16, {})
1688
+ children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconEllipsisOutlineRegular, {})
1542
1689
  })
1543
1690
  }),
1544
1691
  (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.Tooltip, {
@@ -1548,13 +1695,13 @@ window.__ModuleLoader__.load({
1548
1695
  portal: true,
1549
1696
  children: (0, react_jsx_runtime.jsx)("button", {
1550
1697
  type: "button",
1551
- className: Rows_module_css_default.iconButton,
1698
+ className: clsx(Rows_module_css_default.iconButton, Rows_module_css_default.secondaryAction),
1552
1699
  "aria-label": t("menu.archiveSession"),
1553
1700
  onClick: (e) => {
1554
1701
  e.stopPropagation();
1555
1702
  onArchive(node.id);
1556
1703
  },
1557
- children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconArchiveOutline20, { size: 16 })
1704
+ children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconArchiveOutlineRegular, { size: 16 })
1558
1705
  })
1559
1706
  }),
1560
1707
  (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.Tooltip, {
@@ -1629,14 +1776,14 @@ window.__ModuleLoader__.load({
1629
1776
  const addEntries = flowAvailable ? [{
1630
1777
  id: ADD_WORKSPACE,
1631
1778
  label: t("menu.addWorkspace"),
1632
- icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconPlusOutline16, { size: 16 }),
1779
+ icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconPlusOutlineRegular, { size: 16 }),
1633
1780
  disabled: flowBusy
1634
1781
  }] : [];
1635
1782
  const pinAdd = !addOnly && workspaces.length > 0;
1636
1783
  const items = pinAdd ? workspaces.map((workspace) => ({
1637
1784
  id: workspace.workspaceId,
1638
1785
  label: workspace.title,
1639
- icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconFolderClose16, { size: 16 }),
1786
+ icon: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconFolderCloseRegular, { size: 16 }),
1640
1787
  disabled: flowBusy
1641
1788
  })) : addEntries;
1642
1789
  const menuIsEmpty = items.length === 0;
@@ -1761,7 +1908,7 @@ window.__ModuleLoader__.load({
1761
1908
  }
1762
1909
  //#endregion
1763
1910
  //#region \0qilin-css:/home/runner/work/QiLin/QiLin/packages/client/ui-workspace/src/client/rows/WorkspaceBrowser.module.css.mjs
1764
- 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;height:36px;color:var(--qilin-alias-label-tertiary);border-radius:12px;flex:none;justify-content:flex-end;align-items:center;gap:4px;margin-bottom:4px;padding-left:4px;display:flex;overflow:hidden}.CO5s3G_root:not(.CO5s3G_rail) .CO5s3G_sectionHeader{margin-top:2px;margin-right:-4px}.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;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:.5px solid var(--qilin-alias-border-l4);width:calc(100% + 4px);height:30px;color:var(--qilin-alias-label-caption);background:0 0;border-radius:10px;margin-inline:-2px;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:30px}.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 12px 0 calc(28px + var(--qilin-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:.5px 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_search,.CO5s3G_sectionLabel,.CO5s3G_searchSlot,.CO5s3G_searchInput,.CO5s3G_headerActions{transition:none}}";
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}}";
1765
1912
  const tagId = "@qilin-agent/client-ui-workspace/WorkspaceBrowser.module.css";
1766
1913
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
1767
1914
  const tag = document.createElement("style");
@@ -1969,7 +2116,7 @@ window.__ModuleLoader__.load({
1969
2116
  onClick: () => {
1970
2117
  setOpen((v) => !v);
1971
2118
  },
1972
- children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconPersonalizationOutline16, {})
2119
+ children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconPersonalizationOutlineRegular, {})
1973
2120
  })
1974
2121
  })
1975
2122
  });
@@ -1980,17 +2127,19 @@ window.__ModuleLoader__.load({
1980
2127
  return e.clientY < rect.top + rect.height / 2 ? "before" : "after";
1981
2128
  }
1982
2129
  /** The scrolling session tree; unmounting drops the sessions subscription and expand-all state. */
1983
- 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 }) {
1984
2131
  const panelActive = usePanelInfo((info) => info.activePanelId !== null);
1985
2132
  const statuses = useSessionStatus((s) => s);
1986
2133
  const current = panelActive ? void 0 : Object.values(list.byId).find((session) => (session.retainedBy.mainView ?? 0) > 0)?.id;
1987
2134
  const revealGroup = revealSessionId === void 0 || !workspaceReady ? void 0 : owningGroupKey(workspaces, revealSessionId);
1988
2135
  const [storedWatermarks, setStoredWatermarks] = (0, react.useState)({});
2136
+ const [watermarkMotionKey, setWatermarkMotionKey] = (0, react.useState)(0);
1989
2137
  const [drag, setDrag] = (0, react.useState)(null);
1990
2138
  const sessionDropCommitted = (0, react.useRef)(false);
1991
2139
  const [workspaceDrag, setWorkspaceDrag] = (0, react.useState)(null);
1992
2140
  const workspaceDropCommitted = (0, react.useRef)(false);
1993
- useNativeDragAcceptance(drag !== null || workspaceDrag !== null);
2141
+ const nativeDragActive = drag !== null || workspaceDrag !== null;
2142
+ useNativeDragAcceptance(nativeDragActive);
1994
2143
  const currentGroup = current === void 0 || !workspaceReady ? void 0 : owningGroupKey(workspaces, current);
1995
2144
  (0, react.useEffect)(() => {
1996
2145
  if (current === void 0 || currentGroup === void 0 || Object.hasOwn(groupExpansion, currentGroup)) return;
@@ -2138,6 +2287,7 @@ window.__ModuleLoader__.load({
2138
2287
  }, [groups, parents]);
2139
2288
  const rootGroups = childrenByParent.get(void 0) ?? [];
2140
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"] : [];
2141
2291
  const renderGroup = (group, depth) => {
2142
2292
  const workspaceId = group.workspaceId;
2143
2293
  const children = childrenByParent.get(group.key) ?? [];
@@ -2146,6 +2296,10 @@ window.__ModuleLoader__.load({
2146
2296
  const collapsed = watermarkRows(group.sessions, baselines.get(group.key) ?? SESSION_WATERMARK_BASE);
2147
2297
  const visible = watermarkRows(group.sessions, watermark);
2148
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}`);
2149
2303
  const workspaceMarker = workspaceId !== void 0 && workspaceDrag?.over?.id === workspaceId ? workspaceDrag.over.half : null;
2150
2304
  const workspaceDragProps = workspaceId === void 0 ? void 0 : {
2151
2305
  start: () => {
@@ -2220,6 +2374,7 @@ window.__ModuleLoader__.load({
2220
2374
  }
2221
2375
  },
2222
2376
  drag: workspaceDragProps,
2377
+ menu: menu.workspace,
2223
2378
  actions: group.workspaceId === void 0 ? void 0 : {
2224
2379
  rename: () => {
2225
2380
  /* v8 ignore next -- narrowing guard: the actions object exists only for real-workspace groups. */
@@ -2233,7 +2388,7 @@ window.__ModuleLoader__.load({
2233
2388
  }),
2234
2389
  group.expanded && children.length > 0 && (0, react_jsx_runtime.jsx)("div", {
2235
2390
  role: "group",
2236
- children: children.map((child) => renderGroup(child, depth + 1))
2391
+ children: childRows
2237
2392
  }),
2238
2393
  visible.rows.map((node) => {
2239
2394
  const sameGroupDrag = drag !== null && drag.accountKey === group.key;
@@ -2291,12 +2446,14 @@ window.__ModuleLoader__.load({
2291
2446
  } : void 0,
2292
2447
  drag: dragProps,
2293
2448
  renderSlot,
2449
+ menu: menu.session,
2294
2450
  t
2295
2451
  }, node.id);
2296
2452
  }),
2297
2453
  collapsed.hiddenCount > 0 && (0, react_jsx_runtime.jsx)("button", {
2298
2454
  type: "button",
2299
2455
  className: WorkspaceBrowser_module_css_default.sessionOverflowButton,
2456
+ "data-row-key": `overflow:${group.key}`,
2300
2457
  "aria-expanded": sessionsExpanded,
2301
2458
  onClick: () => {
2302
2459
  if (sessionsExpanded) {
@@ -2307,12 +2464,14 @@ window.__ModuleLoader__.load({
2307
2464
  ...marks,
2308
2465
  [group.key]: visible.hiddenCount <= SESSION_WATERMARK_BASE ? Infinity : watermark + SESSION_WATERMARK_BASE
2309
2466
  }));
2467
+ setWatermarkMotionKey((key) => key + 1);
2310
2468
  },
2311
2469
  children: sessionsExpanded ? t("sessions.collapse") : t("sessions.expand", { n: visible.hiddenCount })
2312
2470
  })
2313
2471
  ]
2314
2472
  }, group.key);
2315
2473
  };
2474
+ const groupRows = rootGroups.map((group) => renderGroup(group, 0));
2316
2475
  return (0, react_jsx_runtime.jsxs)("div", {
2317
2476
  className: clsx(WorkspaceBrowser_module_css_default.treeBody, WorkspaceBrowser_module_css_default.wide),
2318
2477
  children: [
@@ -2320,21 +2479,25 @@ window.__ModuleLoader__.load({
2320
2479
  className: WorkspaceBrowser_module_css_default.listTopDropIndicator,
2321
2480
  "aria-hidden": "true"
2322
2481
  }),
2323
- (0, react_jsx_runtime.jsxs)("div", {
2482
+ (0, react_jsx_runtime.jsxs)(_qilin_agent_client_ui_primitives.AnimatedRows, {
2324
2483
  className: clsx(WorkspaceBrowser_module_css_default.list, workspaceDropAtListStart && WorkspaceBrowser_module_css_default.listTopDropActive),
2325
2484
  role: "tree",
2326
- "aria-label": t("section.sessions"),
2485
+ label: t("section.sessions"),
2486
+ rowKeys,
2487
+ ready: list.phase === "ready" && workspaceReady && !nativeDragActive,
2488
+ resetKey: JSON.stringify([animationResetKey, watermarkMotionKey]),
2327
2489
  children: [groups.length === 0 && (0, react_jsx_runtime.jsx)("div", {
2328
2490
  className: WorkspaceBrowser_module_css_default.empty,
2491
+ "data-row-key": "empty",
2329
2492
  children: t("empty.none")
2330
- }), rootGroups.map((group) => renderGroup(group, 0))]
2493
+ }), groupRows]
2331
2494
  }),
2332
2495
  (0, react_jsx_runtime.jsx)("span", { className: WorkspaceBrowser_module_css_default.fade })
2333
2496
  ]
2334
2497
  });
2335
2498
  }
2336
2499
  /** The flat "In one list" body: every session is one draggable top-level row. */
2337
- 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 }) {
2338
2501
  const panelActive = usePanelInfo((info) => info.activePanelId !== null);
2339
2502
  const statuses = useSessionStatus((s) => s);
2340
2503
  const rows = (0, react.useMemo)(() => deriveFlat(list, sessionIds, pinnedSessionIds, statuses), [
@@ -2367,12 +2530,16 @@ window.__ModuleLoader__.load({
2367
2530
  const now = Date.now();
2368
2531
  return (0, react_jsx_runtime.jsxs)("div", {
2369
2532
  className: clsx(WorkspaceBrowser_module_css_default.treeBody, WorkspaceBrowser_module_css_default.wide),
2370
- children: [(0, react_jsx_runtime.jsxs)("div", {
2533
+ children: [(0, react_jsx_runtime.jsxs)(_qilin_agent_client_ui_primitives.AnimatedRows, {
2371
2534
  className: clsx(WorkspaceBrowser_module_css_default.list, WorkspaceBrowser_module_css_default.flatList),
2372
2535
  role: "tree",
2373
- "aria-label": t("section.sessions"),
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,
2374
2540
  children: [rows.length === 0 && (0, react_jsx_runtime.jsx)("div", {
2375
2541
  className: WorkspaceBrowser_module_css_default.empty,
2542
+ "data-row-key": "empty",
2376
2543
  children: t("empty.none")
2377
2544
  }), rows.map((node) => {
2378
2545
  const active = drag !== null;
@@ -2426,6 +2593,7 @@ window.__ModuleLoader__.load({
2426
2593
  dropCommitted.current = false;
2427
2594
  }
2428
2595
  },
2596
+ menu: menu.session,
2429
2597
  t
2430
2598
  }, node.id);
2431
2599
  })]
@@ -2498,7 +2666,7 @@ window.__ModuleLoader__.load({
2498
2666
  * @param props - composed slot props (shell owner share + store + injected actions).
2499
2667
  * @returns the region element tree.
2500
2668
  */
2501
- 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 }) {
2502
2670
  const home = useHostInfo((info) => info.home);
2503
2671
  const list = useSessions((state) => state);
2504
2672
  const workspaces = useWorkspaces((state) => state.items);
@@ -2861,6 +3029,54 @@ window.__ModuleLoader__.load({
2861
3029
  setDeleteError(reason instanceof Error ? reason.message : String(reason));
2862
3030
  });
2863
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]);
2864
3080
  return (0, react_jsx_runtime.jsxs)("div", {
2865
3081
  className: clsx(WorkspaceBrowser_module_css_default.root, !wide && WorkspaceBrowser_module_css_default.rail),
2866
3082
  children: [
@@ -2900,7 +3116,7 @@ window.__ModuleLoader__.load({
2900
3116
  setSearchExpanded(true);
2901
3117
  requestSearch();
2902
3118
  },
2903
- children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconSearchOutline16, { size: searchExpanded ? 11 : 14 })
3119
+ children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconSearchOutlineRegular, { size: searchExpanded ? 11 : 14 })
2904
3120
  })
2905
3121
  }),
2906
3122
  (0, react_jsx_runtime.jsx)("input", {
@@ -2929,7 +3145,7 @@ window.__ModuleLoader__.load({
2929
3145
  setQuery("");
2930
3146
  setSearchExpanded(false);
2931
3147
  },
2932
- children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconCloseFill14, {})
3148
+ children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconCloseFillRegular, {})
2933
3149
  })
2934
3150
  ]
2935
3151
  })
@@ -2960,7 +3176,7 @@ window.__ModuleLoader__.load({
2960
3176
  onClick: () => {
2961
3177
  requestAddWorkspace();
2962
3178
  },
2963
- children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconProjectAddOutline16, { size: 16 })
3179
+ children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconProjectAddOutlineRegular, { size: 16 })
2964
3180
  })
2965
3181
  })]
2966
3182
  }),
@@ -3001,6 +3217,8 @@ window.__ModuleLoader__.load({
3001
3217
  usePanelInfo,
3002
3218
  list,
3003
3219
  sessionIds: orderedFlatSessionIds,
3220
+ workspaceReady,
3221
+ animationResetKey: `${groupBy}/${orderBy}`,
3004
3222
  useSessionStatus,
3005
3223
  open,
3006
3224
  forkSession,
@@ -3014,13 +3232,16 @@ window.__ModuleLoader__.load({
3014
3232
  revealSessionId,
3015
3233
  onSessionRevealed: acknowledgeSessionReveal,
3016
3234
  renderSlot,
3235
+ menu: menus,
3017
3236
  t
3018
3237
  }) : (0, react_jsx_runtime.jsx)(SessionTree, {
3019
3238
  usePanelInfo,
3020
3239
  list,
3240
+ animationResetKey: `${groupBy}/${orderBy}`,
3021
3241
  useSessionStatus,
3022
3242
  copySessionId,
3023
3243
  renderSlot,
3244
+ menu: menus,
3024
3245
  onSessionRename,
3025
3246
  onSessionArchive,
3026
3247
  onSessionPin,
@@ -3461,6 +3682,7 @@ window.__ModuleLoader__.load({
3461
3682
  * declaration through `slots.inject()` instead of assuming order.
3462
3683
  */
3463
3684
  const inject = [
3685
+ "contextMenus",
3464
3686
  "slots",
3465
3687
  "sessions",
3466
3688
  "workspaces",
@@ -3486,6 +3708,8 @@ window.__ModuleLoader__.load({
3486
3708
  create: () => viewInstance
3487
3709
  };
3488
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);
3489
3713
  let toastSeq = 0;
3490
3714
  const notify = (toast) => {
3491
3715
  rowToast.set({
@@ -3555,16 +3779,69 @@ window.__ModuleLoader__.load({
3555
3779
  copySessionId: async (sessionId) => {
3556
3780
  notify({ kind: await (0, _qilin_agent_client_ui_primitives.writeClipboard)(sessionId) ? "sessionIdCopied" : "sessionIdCopyFailed" });
3557
3781
  },
3782
+ settleSessionRename: () => {
3783
+ sessionRenameRequest.set(null);
3784
+ },
3785
+ settleWorkspaceAction: () => {
3786
+ workspaceActionRequest.set(null);
3787
+ },
3558
3788
  hooks: {
3559
3789
  directoryFlow: browserFlowSource,
3560
3790
  hostInfo,
3561
3791
  workspaceShortcuts: shortcutControls.state,
3562
- shortcuts: ctx.shortcuts.catalog
3792
+ shortcuts: ctx.shortcuts.catalog,
3793
+ sessionRenameRequest,
3794
+ workspaceActionRequest
3563
3795
  }
3564
3796
  });
3565
3797
  installWorkspaceShortcuts(ctx, uiWorkspace, shortcutControls, (sessionId) => {
3566
3798
  uiWorkspace.archiveSession(sessionId);
3567
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
+ });
3568
3845
  registerToasts(ctx);
3569
3846
  const rowToastInjected = () => ({
3570
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 = {
@@ -14,8 +14,11 @@ export interface MainSelection {
14
14
  readonly sessionId?: SessionId;
15
15
  readonly subagentAddress?: SubagentAddress;
16
16
  }
17
- /** Optional content preparation for the resolved target Session. */
18
- export type StartSessionOptions = DraftInitializationOptions;
17
+ /** Content preparation and Agent preset for the Session a Workspace flow opens. */
18
+ export interface StartSessionOptions extends DraftInitializationOptions {
19
+ /** Agent preset the Session runs; the Host default when omitted. */
20
+ readonly agentPreset?: string;
21
+ }
19
22
  /** Workspace archive and directory operations consumed by Client UI domains. */
20
23
  export interface UiWorkspace {
21
24
  /**
@@ -34,9 +37,10 @@ export interface UiWorkspace {
34
37
  * @param workspaceId - target Workspace.
35
38
  * @param beforeOpen - optional synchronous preparation for the selected Session,
36
39
  * skipped after supersession; a throw aborts the open and releases the retained reference.
40
+ * @param options - choices for the Session the connection creates or reuses.
37
41
  * @returns completion; a superseded request may create a Session but does not open it.
38
42
  */
39
- openWorkspace(workspaceId: WorkspaceId, beforeOpen?: (sessionId: SessionId) => void): Promise<void>;
43
+ openWorkspace(workspaceId: WorkspaceId, beforeOpen?: (sessionId: SessionId) => void, options?: Pick<StartSessionOptions, 'agentPreset'>): Promise<void>;
40
44
  /**
41
45
  * Fork a Session and open the child unless a later navigation supersedes it.
42
46
  * @param sessionId - source Session.
@@ -46,13 +50,15 @@ export interface UiWorkspace {
46
50
  /**
47
51
  * Resolve the reusable or newly created blank Session for a Workspace.
48
52
  * @param workspaceId - target Workspace.
53
+ * @param options - choices for the created or reused Session, including the Agent preset.
49
54
  * @returns a Session already addressable through the Session Controller.
50
55
  */
51
- connectWorkspace(workspaceId: WorkspaceId): Promise<SessionId>;
56
+ connectWorkspace(workspaceId: WorkspaceId, options?: Pick<StartSessionOptions, 'agentPreset'>): Promise<SessionId>;
52
57
  /**
53
58
  * Start a New Session flow and navigate to its Session.
54
59
  * @param workspaceId - explicit target; absent inherits the current or most recent Workspace.
55
- * @param options - initial content; existing text or attachments are preserved unless clearPreviousDraft is true.
60
+ * @param options - initial content, the Agent preset, and `whenDraftExists`;
61
+ * existing text or attachments are preserved unless that handling replaces them.
56
62
  */
57
63
  startSession(workspaceId?: WorkspaceId, options?: StartSessionOptions): void;
58
64
  /**
@@ -133,9 +139,9 @@ declare class UiWorkspaceService extends Service implements UiWorkspace {
133
139
  * @param notify - show one notice through the Workspace notice channel.
134
140
  */
135
141
  constructor(ctx: Context, directoryPicker: ClientRemote['directoryPicker'], workspaces: IWorkspaces, sessions: ISessions, view: Pick<WorkspaceViewStoreActions, 'pinSessionOrder'>, notify: (toast: RowToast) => void);
136
- connectWorkspace(workspaceId: WorkspaceId): Promise<SessionId>;
142
+ connectWorkspace(workspaceId: WorkspaceId, options?: Pick<StartSessionOptions, 'agentPreset'>): Promise<SessionId>;
137
143
  openSession(target: SessionTarget): void;
138
- openWorkspace(workspaceId: WorkspaceId, beforeOpen?: (sessionId: SessionId) => void): Promise<void>;
144
+ openWorkspace(workspaceId: WorkspaceId, beforeOpen?: (sessionId: SessionId) => void, options?: Pick<StartSessionOptions, 'agentPreset'>): Promise<void>;
139
145
  forkSession(sessionId: SessionId): Promise<void>;
140
146
  startSession(workspaceId?: WorkspaceId, options?: StartSessionOptions): void;
141
147
  private prepareDraft;
@@ -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.6",
4
+ "version": "3.1.8",
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.5"
52
+ "@qilin-agent/kylin": "~4.0.6-alpha.2"
52
53
  },
53
54
  "devDependencies": {
55
+ "@qilin-agent/api-remotes": "3.1.8",
56
+ "@qilin-agent/api-session-controller": "3.1.8",
57
+ "@qilin-agent/api-workspace-controller": "3.1.8",
58
+ "@qilin-agent/client-connection": "3.1.8",
59
+ "@qilin-agent/client-locale": "3.1.8",
60
+ "@qilin-agent/client-shortcuts": "3.1.8",
61
+ "@qilin-agent/client-store": "3.1.8",
62
+ "@qilin-agent/client-test-runtime": "3.1.8",
63
+ "@qilin-agent/client-ui-context-menu": "3.1.8",
64
+ "@qilin-agent/client-ui-conversation": "3.1.8",
65
+ "@qilin-agent/client-ui-layout": "3.1.8",
66
+ "@qilin-agent/client-ui-primitives": "3.1.8",
67
+ "@qilin-agent/client-ui-renderer": "3.1.8",
68
+ "@qilin-agent/client-ui-session": "3.1.8",
69
+ "@qilin-agent/client-ui-sidebar": "3.1.8",
70
+ "@qilin-agent/client-ui-slots": "3.1.8",
71
+ "@qilin-agent/kylin": "~4.0.6-alpha.2",
72
+ "@qilin-agent/schedule": "3.1.8",
73
+ "@qilin-agent/session": "3.1.8",
74
+ "@qilin-agent/subagent": "3.1.8",
75
+ "@qilin-agent/typert-protocol": "3.1.8",
76
+ "@qilin-agent/util-workspace-path": "3.1.8",
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.6",
58
- "@qilin-agent/api-session-controller": "3.1.6",
59
- "@qilin-agent/api-workspace-controller": "3.1.6",
60
- "@qilin-agent/client-locale": "3.1.6",
61
- "@qilin-agent/client-shortcuts": "3.1.6",
62
- "@qilin-agent/client-connection": "3.1.6",
63
- "@qilin-agent/client-test-runtime": "3.1.6",
64
- "@qilin-agent/client-ui-conversation": "3.1.6",
65
- "@qilin-agent/client-store": "3.1.6",
66
- "@qilin-agent/client-ui-layout": "3.1.6",
67
- "@qilin-agent/client-ui-primitives": "3.1.6",
68
- "@qilin-agent/client-ui-renderer": "3.1.6",
69
- "@qilin-agent/client-ui-session": "3.1.6",
70
- "@qilin-agent/client-ui-sidebar": "3.1.6",
71
- "@qilin-agent/client-ui-slots": "3.1.6",
72
- "@qilin-agent/schedule": "3.1.6",
73
- "@qilin-agent/session": "3.1.6",
74
- "@qilin-agent/subagent": "3.1.6",
75
- "@qilin-agent/typert-protocol": "3.1.6",
76
- "@qilin-agent/util-workspace-path": "3.1.6",
77
- "@qilin-agent/kylin": "~4.0.5"
79
+ "react": "^18.2.0"
78
80
  },
79
81
  "files": [
80
82
  "lib/index.js",