@klarkxy/dsh-session-manager 0.3.0 → 0.3.1

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.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [简体中文](README.zh-CN.md)
6
6
 
7
- Find and read DeepSeek Harness sessions across working directories, start a chat in a temporary workspace, continue with one tap on an empty composer, and get a welcome-back recap after time away.
7
+ Find and read sessions across working directories, start a chat in a temporary workspace, continue with one tap from an empty composer, and get a welcome-back recap after time away.
8
8
 
9
9
  ## Install
10
10
 
@@ -16,20 +16,20 @@ Replace `web` with your profile name and restart the profile.
16
16
 
17
17
  ## Use
18
18
 
19
- - **Agent tools**: `list_sessions`, `search_sessions`, and `read_session` let the agent find sessions by title or content and read a conversation, its full details, or a handoff summary. Reading a conversation returns one preview per turn, newest first; the agent pages further back with the returned cursor and opens full original content through details mode.
20
- - **Session context menu**: right-click a session row in the sidebar, or its **⋯** button, to open the session's actions menu without switching conversations. Left-click the row to open the conversation. Search results and blank new-session rows do not have this menu.
21
- - **Temporary workspace**: choose it in the new-session workspace picker to start a chat in its own new folder.
22
- - **Continue ghost**: with an empty composer, a grey suggestion floats in the input area. Press `Tab` to fill it into the draft, or click it to send it immediately. **Fixed text** uses the configured text, or "Continue" when that is empty. **Model-generated** uses the model chosen in that same switch. The model runs once after the previous turn ends and the line stays cached in the session, so the same turn does not call it again. Mode, fixed text, and the ghost model are all under **Continue ghost** (equivalent keys: `continueMode`, `continueText`).
23
- - **Welcome back**: returning to a session whose last response is older than `recapMinutes` (default 15) shows an inline card at the end of the conversation, after the last turn, with a model-written recap of what you were discussing, the progress so far, and a suggested next step. Past `compactMinutes` (default 60), the same card adds a prompt-cache expiry note with two optional buttons: **Compact context** invokes the host's native compaction command; **Continue in a new session** creates a blank session in the source session's workspace (or keeps the original directory when the source is ungrouped) and fills its draft with the recap and source session ID for you to review and send, without copying the full history. You can also ignore both buttons and send normally; nothing is compacted or switched automatically. The card disappears once you continue the conversation. Toggle each feature and set its idle threshold under the **Welcome back** settings group. After **Save** is confirmed, the current conversation is checked against the new thresholds. Time is measured from the last response, not from when the settings were saved.
24
- - **Welcome-back model**: the **Recap model** field under the **Welcome-back recap** toggle in the **Welcome back** settings group is only for the welcome-back recap. The ghost model is chosen inside **Continue ghost** after switching to **Model-generated**. Leaving either on the default option follows the current conversation or the host default model. The model menu supports provider groups and search. If the model list fails to load, a message and retry button appear; retrying keeps your unsaved settings.
19
+ - **Agent tools**: `list_sessions`, `search_sessions`, and `read_session` find sessions by title or content and read a conversation, its full details, or a handoff summary. Reading a conversation returns one preview per turn, newest first; page back with the returned cursor and open full content in details mode.
20
+ - **Session context menu**: right-click a session row or use its **⋯** button to open its actions menu without switching conversations; left-click the row to open it. Search results and blank new-session rows have no such menu.
21
+ - **Temporary workspace**: pick it in the new-session workspace picker to chat in its own new folder.
22
+ - **Continue ghost**: with an empty composer, a grey suggestion floats in the input area. Press `Tab` to fill it into the draft, or click it to send it at once. **Fixed text** uses the configured text, or "Continue" when empty. **Model-generated** uses the model chosen in that same switch; it runs once after the previous turn ends and the line stays cached in the session, so the turn is not called again. Mode, fixed text, and ghost model are under **Continue ghost** (keys: `continueMode`, `continueText`).
23
+ - **Welcome back**: returning to a session whose last response is older than `recapMinutes` (default 15) shows an inline card after the last turn, recapping what you were discussing, the progress, and a suggested next step. Past `compactMinutes` (default 60), the same card adds a prompt-cache expiry note and two optional buttons: **Compact context** invokes the host's native compaction; **Continue in a new session** creates a blank session in the source session's workspace (or keeps the original directory when the source is ungrouped) and fills its draft with the recap and source session ID to review and send, without copying the history. You can ignore both and send normally; nothing is compacted or switched automatically. The card disappears once you continue the conversation. Toggle each feature and set its idle threshold under **Welcome back**; after **Save** is confirmed, the current conversation is checked against the new thresholds. Time is measured from the last response, not from when settings were saved.
24
+ - **Welcome-back model**: the **Recap model** field under the **Welcome-back recap** toggle in **Welcome back** applies only to the welcome-back recap. The ghost model is chosen inside **Continue ghost** after switching to **Model-generated**. Leaving either on the default follows the current conversation or the host default model. The model menu supports provider groups and search; if the list fails to load, a message and retry button appear, and retrying keeps your unsaved settings.
25
25
 
26
26
  ## Limits
27
27
 
28
28
  - Temporary workspaces are not cleaned up automatically; their files stay after restarts and uninstall.
29
29
  - Content read from other sessions is background information, not instructions.
30
- - Free chats, deleting chats, and retrying or editing a sent message in place need a DSH host with these features. The npm plugin alone does not add them; the buttons appear only when the host supports them.
31
- - The welcome-back recap is model-generated; when no model route is available or the call fails, the card stays silent (a compaction suggestion still shows on its own). The recap call only reads the session and never adds to its history. Compaction needs the host's native command; its conversation entry reports the result. A new session carries only a short recap, which may omit details; use the source session ID to look them up. The original session and draft remain unchanged.
32
- - The card is checked when you return to a session (window focus, tab visibility, or switching sessions) and periodically while the page stays visible, so crossing a threshold does not require switching windows. It never intercepts your next send. Sending an empty composer means sending the ghost text; the composer itself still requires non-empty input.
30
+ - Free chats, deleting chats, and retrying or editing a sent message in place need a DSH host with these features. The npm plugin alone does not add them; buttons appear only when the host supports them.
31
+ - The welcome-back recap is model-generated; when no model route is available or the call fails, the card stays silent (a compaction suggestion still shows on its own). The recap call only reads the session and never adds to its history. Compaction needs the host's native command; its conversation entry reports the result. A new session carries only a short recap, which may omit details; use the source session ID to look them up. The original session and draft stay unchanged.
32
+ - The card is checked when you return to a session (window focus, tab visibility, or switching sessions) and periodically while the page stays visible, so crossing a threshold needs no window switch. It never intercepts your next send. Sending an empty composer sends the ghost text; the composer itself still requires non-empty input.
33
33
 
34
34
  ## License
35
35
 
package/README.zh-CN.md CHANGED
@@ -16,20 +16,20 @@ dsh plugin --profile web add @klarkxy/dsh-session-manager
16
16
 
17
17
  ## 使用
18
18
 
19
- - **智能体工具**:`list_sessions`、`search_sessions` 和 `read_session` 让智能体按标题或内容查找会话,并阅读对话、完整细节或交接摘要。阅读对话时按轮返回预览(最新在前),智能体可用返回的游标继续向前翻页,并通过细节模式查看完整原文。
20
- - **会话右键菜单**:在左侧列表右键点击会话行或它的「⋯」按钮,打开该会话的操作菜单,不切换当前对话。左键点击会话行仍打开对话。搜索结果和空白新会话行不提供此菜单。
19
+ - **智能体工具**:`list_sessions`、`search_sessions` 和 `read_session` 按标题或内容查找会话,并阅读对话、完整细节或交接摘要。阅读对话时按轮返回预览(最新在前),可用返回的游标向前翻页,并通过细节模式查看完整原文。
20
+ - **会话右键菜单**:右键点击左侧会话行或它的「⋯」按钮,打开该会话的操作菜单,不切换当前对话;左键仍打开对话。搜索结果和空白新会话行没有此菜单。
21
21
  - **临时工作区**:在新会话的工作区选择器中选择它,即可在一个新的独立目录中开始对话。
22
- - **继续灰影**:输入框为空时,输入区域会浮现灰色建议文本。按 `Tab` 填入草稿,点击它则直接发送。切到「固定文本」就使用配置的文本,留空则使用「继续」;切到「模型生成」就使用所选模型。模型只在上一轮结束后生成一次,并缓存在这个会话中,同一轮不会重复调用。模式、固定文本和灰影模型都在插件设置页的「继续灰影」分组中调整(对应配置键:`continueMode`、`continueText`)。
23
- - **欢迎回来**:回到一个距上次响应超过 `recapMinutes` 分钟(默认 15)的会话时,在对话末尾(最后一轮回复之后)内嵌显示模型生成的摘要卡片,说明之前在聊什么、进展到哪一步、建议的下一步。超过 `compactMinutes` 分钟(默认 60)时,同一卡片还会提示缓存可能已失效,并提供「压缩上下文」和「在新会话中继续」两个可选按钮:前者调用宿主的原生压缩命令,后者在来源会话所属的工作区内新建空白会话(来源未归属工作区时沿用原目录),将摘要和原会话 ID 填入草稿供确认后发送,不复制完整历史。也可以不点按钮,直接发送;不会自动压缩或切换会话。继续对话后卡片自动消失。两项开关和各自的空闲阈值在设置的「欢迎回来」分组中调整,点击「保存」并确认成功后,当前会话会按新阈值重新检查。时间从上次响应计算,不从保存设置时重新计时。
24
- - **欢迎回来模型**:「欢迎回来」分组里「欢迎回来摘要」开关下的「摘要模型」只用于欢迎回来摘要。灰影的模型在「继续灰影」里切到「模型生成」后单独选择。两者留空都跟随当前会话或宿主默认模型。模型菜单支持按供应商分组和搜索;列表加载失败时会显示原因与重试按钮,重试会保留未保存的设置。
22
+ - **继续灰影**:输入框为空时,输入区域会浮现灰色建议文本。按 `Tab` 填入草稿,点击则直接发送。切到「固定文本」使用配置的文本,留空则用「继续」;切到「模型生成」使用所选模型,模型只在上一轮结束后生成一次并缓存在会话中,同一轮不会重复调用。模式、固定文本和灰影模型都在「继续灰影」分组中调整(配置键:`continueMode`、`continueText`)。
23
+ - **欢迎回来**:回到距上次响应超过 `recapMinutes` 分钟(默认 15)的会话时,在最后一轮回复之后内嵌显示模型生成的摘要卡片,说明之前在聊什么、进展和建议的下一步。超过 `compactMinutes` 分钟(默认 60)时,同一卡片还会提示缓存可能已失效,并提供「压缩上下文」和「在新会话中继续」两个可选按钮:前者调用宿主原生压缩命令,后者在来源会话所属工作区内新建空白会话(来源未归属工作区时沿用原目录),把摘要和原会话 ID 填入草稿供确认后发送,不复制历史。也可以不点按钮直接发送,不会自动压缩或切换会话。继续对话后卡片自动消失。两项开关和各自的空闲阈值在「欢迎回来」分组中调整,点击「保存」并确认成功后,当前会话按新阈值重新检查。时间从上次响应计算,不从保存设置时重新计时。
24
+ - **欢迎回来模型**:在「欢迎回来」的「欢迎回来摘要」开关下,「摘要模型」只用于欢迎回来摘要。灰影的模型在「继续灰影」切到「模型生成」后单独选择。两者留空都跟随当前会话或宿主默认模型。模型菜单支持按供应商分组和搜索;列表加载失败时显示原因与重试按钮,重试会保留未保存的设置。
25
25
 
26
26
  ## 限制
27
27
 
28
28
  - 临时工作区不会自动清理,文件在重启和卸载后仍然保留。
29
29
  - 从其他会话读到的内容只是背景资料,不是指令。
30
- - 自由聊天、删除聊天、在原会话中重试或编辑已发送消息,需要宿主支持这些功能。只安装 npm 插件不会增加这些能力;宿主支持时才会显示对应按钮。
31
- - 「欢迎回来」摘要由模型生成;没有可用模型或调用失败时卡片不会出现(压缩建议仍会单独出现)。摘要只读取会话,不会向历史写入任何内容。压缩能力依赖宿主的原生命令;结果由对话中的压缩记录反馈。新会话只携带简短摘要,可能遗漏细节,需按原会话 ID 查阅;原会话和原草稿保持不变。
32
- - 卡片在你回到会话时检查(窗口聚焦、标签页可见或切换会话);页面持续可见时也会定期检查,到达阈值后不必切换窗口。它不会拦截你的下一次发送。空输入发送指的是发送灰影文本;输入框本身仍然要求非空内容。
30
+ - 自由聊天、删除聊天、在原会话中重试或编辑已发送消息,需要宿主支持这些功能。只安装 npm 插件不会增加这些能力;宿主支持时才显示对应按钮。
31
+ - 「欢迎回来」摘要由模型生成;没有可用模型或调用失败时卡片不会出现(压缩建议仍会单独出现)。摘要只读取会话,不写入历史。压缩依赖宿主的原生命令,结果由对话中的压缩记录反馈。新会话只携带简短摘要,可能遗漏细节,需按原会话 ID 查阅;原会话和原草稿保持不变。
32
+ - 卡片在你回到会话时检查(窗口聚焦、标签页可见或切换会话),页面持续可见时也会定期检查,到达阈值后不必切换窗口。它不会拦截你的下一次发送。空输入发送指发送灰影文本;输入框本身仍要求非空内容。
33
33
 
34
34
  ## 许可证
35
35
 
@@ -412,6 +412,17 @@ ${scope} {
412
412
  /* --- Layout scaffolding. --- */
413
413
  & .dsh-ui-stack { display: flex; flex-direction: column; gap: 12px; }
414
414
  & .dsh-ui-stack-lg { display: flex; flex-direction: column; gap: 20px; }
415
+ /* Plugin detail pages share one reading column. These are layout recipes;
416
+ * controls keep their own primitive styles. Form spacing follows the host's
417
+ * settings-form/fields.module.css and SettingsForm.module.css. */
418
+ & .dsh-ui-page { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 760px; min-width: 0; }
419
+ & .dsh-ui-form { display: flex; flex-direction: column; gap: 0; min-width: 0; }
420
+ &.dsh-ui-form > .dsh-ui-field, & .dsh-ui-form > .dsh-ui-field { padding: 12px 0; }
421
+ & .dsh-ui-form-footer { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding-top: 16px; min-width: 0; }
422
+ & .dsh-ui-fields-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 20px; min-width: 0; margin: 0; padding: 0; border: 0; }
423
+ @media (max-width: 560px) {
424
+ & .dsh-ui-fields-grid { grid-template-columns: minmax(0, 1fr); }
425
+ }
415
426
  & .dsh-ui-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
416
427
  & .dsh-ui-row-wrap { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
417
428
  & .dsh-ui-spacer { flex: 1; min-width: 0; }
@@ -439,6 +450,9 @@ ${scope} {
439
450
  * official primitive, so this only owns the vertical rhythm. --- */
440
451
  & .dsh-ui-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
441
452
  & .dsh-ui-field + .dsh-ui-field { border-top: 0.5px solid var(--dsw-alias-border-l2); padding-top: 12px; }
453
+ /* Grid fields share a row, so the stacked-field divider must not win later in
454
+ * the cascade. Reset it here, after the field recipe. */
455
+ & .dsh-ui-fields-grid > .dsh-ui-field { padding: 0; border: 0; }
442
456
  & .dsh-ui-label { display: flex; align-items: center; gap: 6px; font-size: 13px; line-height: 20px; font-weight: 500; color: var(--dsw-alias-label-primary); }
443
457
  & .dsh-ui-label-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
444
458
  & .dsh-ui-help { font-size: 12px; line-height: 1.6; color: var(--dsw-alias-label-secondary); }
@@ -629,7 +643,7 @@ ${scope} {
629
643
  & *, & *::before, & *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
630
644
  }
631
645
  }
632
- `.replace(/^& (\.dsh-ui-[a-z0-9-]+)(?![+>~])/gm, (_match, className) => `&${className}, & ${className}`);
646
+ `.replace(/^& (\.dsh-ui-[a-z0-9-]+)(?=\s*\{)/gm, (_match, className) => `&${className}, & ${className}`);
633
647
  }
634
648
  //#endregion
635
649
  //#region ../dsh-model-route/src/route.ts
@@ -861,7 +875,14 @@ function useModelCatalog(load, options = {}) {
861
875
  refresh
862
876
  };
863
877
  }
864
- /** Also accepts a mapped state from a caller that owns broader async orchestration. */
878
+ /**
879
+ * Also accepts a mapped state from a caller that owns broader async orchestration.
880
+ *
881
+ * The dsh-ui-* classes carry the styling and resolve through the consuming
882
+ * plugin's own officialUiCss scope (every consumer mounts its root class, so
883
+ * e.g. `.dsh-model-hub .dsh-ui-loading` paints this); dsh-model-catalog-status
884
+ * stays as a structural hook for tests and layout, never a colour source.
885
+ */
865
886
  function ModelCatalogStatus({ state, locale }) {
866
887
  const zh = locale?.toLowerCase().startsWith("zh") === true;
867
888
  const copy = zh ? {
@@ -877,15 +898,17 @@ function ModelCatalogStatus({ state, locale }) {
877
898
  };
878
899
  if (!state.loading && !state.error && state.failures.length === 0) return null;
879
900
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
880
- className: "dsh-model-catalog-status",
901
+ className: "dsh-model-catalog-status dsh-ui-stack",
881
902
  lang: zh ? "zh" : "en",
882
903
  children: [
883
904
  state.loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
884
905
  role: "status",
906
+ className: "dsh-ui-loading",
885
907
  children: copy.loading
886
908
  }) : null,
887
909
  state.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
888
910
  role: "alert",
911
+ className: "dsh-ui-error dsh-ui-wrap",
889
912
  children: [
890
913
  copy.error,
891
914
  ": ",
@@ -894,20 +917,27 @@ function ModelCatalogStatus({ state, locale }) {
894
917
  }) : null,
895
918
  state.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
896
919
  role: "status",
897
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: copy.partial }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", { children: state.failures.map((failure, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
898
- failure.name || failure.id,
899
- ": ",
900
- failure.message
901
- ] }, `${failure.id}:${index}`)) })]
920
+ className: "dsh-ui-meta",
921
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: copy.partial }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
922
+ className: "dsh-ui-list",
923
+ children: state.failures.map((failure, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
924
+ failure.name || failure.id,
925
+ ": ",
926
+ failure.message
927
+ ] }, `${failure.id}:${index}`))
928
+ })]
902
929
  }) : null,
903
- state.error || state.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
904
- type: "button",
905
- size: "sm",
906
- disabled: state.loading,
907
- onClick: () => {
908
- state.refresh();
909
- },
910
- children: copy.retry
930
+ state.error || state.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
931
+ className: "dsh-ui-actions",
932
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
933
+ type: "button",
934
+ size: "sm",
935
+ disabled: state.loading,
936
+ onClick: () => {
937
+ state.refresh();
938
+ },
939
+ children: copy.retry
940
+ })
911
941
  }) : null
912
942
  ]
913
943
  });
@@ -967,7 +997,8 @@ function groupModelMenuChoices(choices, query) {
967
997
  * Sticky group headings, subsequence ranking, highlight walking and focus
968
998
  * hand-back all match the host. SYNC POINT: when the host's ModelSelect or
969
999
  * ModelSelect.module.css changes, this copy must be re-transcribed; styles
970
- * below may only reference --dsw-* theme tokens, never literal colors.
1000
+ * below may only reference allowlisted theme tokens (the --dsw-* set plus the
1001
+ * host's --ds-* motion tokens), never literal colors.
971
1002
  *
972
1003
  * It statically imports the primitives, so only browser clients should load
973
1004
  * this entry; host code and node tests use the core entry (`./index.ts`).
@@ -1475,7 +1506,8 @@ function ModelMenu(props) {
1475
1506
  * sticky group headings (MenuGroup's own CSS), and the l1-stroke prominent
1476
1507
  * elevation at portal z-index 1100. The trigger mirrors the composer's 28px
1477
1508
  * model pill, chevron rotating open. MenuSurface paints fill and backdrop.
1478
- * Only --dsw-* theme tokens may appear here — a literal color is a bug.
1509
+ * Only allowlisted --dsw- and --ds- theme tokens may appear here — a literal
1510
+ * color is a bug.
1479
1511
  */
1480
1512
  const modelMenuCss = `
1481
1513
  .dsh-model-menu-root { min-width: 0; position: relative; display: inline-flex; }
@@ -1498,12 +1530,14 @@ const modelMenuCss = `
1498
1530
  display: flex;
1499
1531
  }
1500
1532
  .dsh-model-menu-trigger:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
1501
- .dsh-model-menu-trigger:focus-visible:not([data-selection-focus]) { box-shadow: 0 0 0 2px var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary)); }
1533
+ .dsh-model-menu-trigger:focus-visible:not([data-selection-focus]) { box-shadow: 0 0 0 var(--dsw-focus-ring-width) var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary)); }
1502
1534
  .dsh-model-menu-trigger:disabled { color: var(--dsw-alias-label-dimmed); cursor: default; }
1503
1535
  .dsh-model-menu-triggerLabel { text-overflow: ellipsis; white-space: nowrap; min-width: 0; overflow: hidden; }
1504
1536
  .dsh-model-menu-triggerEffort { text-overflow: ellipsis; white-space: nowrap; min-width: 0; color: var(--dsw-alias-label-caption); flex-shrink: 1000; overflow: hidden; }
1505
- .dsh-model-menu-chevron { color: var(--dsw-alias-label-caption); flex: none; transition: transform 0.12s; }
1537
+ .dsh-model-menu-chevron { color: var(--dsw-alias-label-caption); flex: none; transition: transform var(--ds-transition-duration) var(--ds-ease-in-out); }
1506
1538
  .dsh-model-menu-chevronOpen { transform: rotate(180deg); }
1539
+ /* Portal overlay: the host publishes no z-index token, so the card keeps the
1540
+ * native menu layer's 1100 verbatim. */
1507
1541
  .dsh-model-menu-menu {
1508
1542
  z-index: 1100;
1509
1543
  --dsw-elevation-stroke-color: var(--dsw-alias-border-l1);
@@ -1529,6 +1563,8 @@ const modelMenuCss = `
1529
1563
  .dsh-model-menu-searchWithQuery { padding-right: 34px; }
1530
1564
  .dsh-model-menu-searchRow .dsh-model-menu-search input { padding: 0; font-size: 12px; line-height: normal; }
1531
1565
  .dsh-model-menu-searchRow .dsh-model-menu-search input::placeholder { color: var(--dsw-alias-label-caption); }
1566
+ /* The clear button is a circle by geometry (border-radius: 50%), a shape the
1567
+ * radius token scale does not describe. */
1532
1568
  .dsh-model-menu-searchClear {
1533
1569
  width: 24px;
1534
1570
  height: 24px;
@@ -1697,20 +1733,6 @@ ${modelMenuCss}
1697
1733
  `;
1698
1734
  //#endregion
1699
1735
  //#region src/free-panel.tsx
1700
- function FreeChatIcon({ size }) {
1701
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1702
- width: size,
1703
- height: size,
1704
- viewBox: "0 0 16 16",
1705
- "aria-hidden": "true",
1706
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1707
- d: "M2.5 3.5h11v7.5h-4.2L6 13.5V11H2.5z",
1708
- fill: "none",
1709
- stroke: "currentColor",
1710
- strokeWidth: "1.2"
1711
- })
1712
- });
1713
- }
1714
1736
  /** Create failures used to surface inside the standalone panel. The panel is gone,
1715
1737
  * so they get a small overlay instead; closing it dismisses the stored notice. */
1716
1738
  function CreateNoticeDialog(props) {
@@ -1908,7 +1930,7 @@ function TemporaryWorkspacePicker(props) {
1908
1930
  }] : [], {
1909
1931
  id: "::temporary-workspace",
1910
1932
  label: uiText(locale, "tempWorkspace"),
1911
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FreeChatIcon, { size: 16 }),
1933
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconNewChatOutlineRegular, { size: 16 }),
1912
1934
  disabled: busy
1913
1935
  }],
1914
1936
  onSelect: (id) => {
@@ -1921,6 +1943,7 @@ function TemporaryWorkspacePicker(props) {
1921
1943
  }),
1922
1944
  props.open && workspaces.phase === "pending" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1923
1945
  role: "status",
1946
+ className: `dsh-free-chat dsh-ui-loading`,
1924
1947
  children: props.t("picker.loading")
1925
1948
  }),
1926
1949
  props.renderSlot(TEMP_WORKSPACE_FLOW_SLOT, {
@@ -1956,6 +1979,7 @@ function TemporaryWorkspacePicker(props) {
1956
1979
  }),
1957
1980
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1958
1981
  open: failure !== void 0,
1982
+ className: FREE_CHAT_ROOT,
1959
1983
  title: props.t("folderError.title"),
1960
1984
  closeLabel: props.t("close"),
1961
1985
  onClose: () => setFailure(void 0),
@@ -1980,6 +2004,7 @@ function TemporaryWorkspaceNotice({ model, locale }) {
1980
2004
  const state = (0, react.useSyncExternalStore)(model.subscribe, model.getSnapshot, model.getSnapshot);
1981
2005
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1982
2006
  open: state.failure !== void 0,
2007
+ className: FREE_CHAT_ROOT,
1983
2008
  title: uiText(locale, "createFailed"),
1984
2009
  closeLabel: uiText(locale, "close"),
1985
2010
  onClose: model.dismissFailure,
@@ -3612,7 +3637,7 @@ function SessionManagerSettings(props) {
3612
3637
  });
3613
3638
  };
3614
3639
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3615
- className: `${FREE_CHAT_ROOT} dsh-ui-panel`,
3640
+ className: `${FREE_CHAT_ROOT} dsh-ui-page`,
3616
3641
  children: [
3617
3642
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3618
3643
  className: "dsh-ui-card",
@@ -3879,7 +3904,7 @@ function SessionManagerSettings(props) {
3879
3904
  ]
3880
3905
  }),
3881
3906
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3882
- className: "dsh-ui-actions",
3907
+ className: "dsh-ui-form-footer",
3883
3908
  children: [
3884
3909
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3885
3910
  variant: "primary",