dsh-sidenote 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -78,16 +78,30 @@ window.__ModuleLoader__.load({
78
78
  stopped: "Stopped",
79
79
  toolLabel: "Tool",
80
80
  failed: "failed",
81
+ todoTitle: "Tasks",
82
+ todoDone: "{n} done",
83
+ todoDoing: "{n} in progress",
84
+ todoPending: "{n} pending",
81
85
  running: "running…",
82
86
  inputPlaceholder: "Message… (Enter to send, Shift+Enter for a newline)",
83
87
  modelLabel: "Model: {name}",
84
88
  modelSwitchTitle: "Switch the side chat model",
85
89
  permChipTitle: "Permission mode",
86
90
  permFullAccess: "Full access",
87
- pendingNotice: "⚠ 侧边会话有审批/提问待处理——点击跳到主视图处理",
88
- writeNotice: "侧边会话与主会话同权限,可以修改文件(当前模式见输入框旁 chip)",
91
+ pendingNotice: "⚠ The side session has a pending approval/question — click to handle it in the main view",
92
+ writeNotice: "The side session shares the main session's permissions and can modify files (current mode: chip next to the input)",
93
+ pendingApprovalTitle: "Waiting for approval",
94
+ pendingApprovalDesc: "Tool {name} requests elevated execution",
95
+ pendingReject: "Reject",
96
+ pendingAllowOnce: "Allow once",
97
+ pendingQuestionTitle: "Question",
98
+ pendingPlanReview: "Plan review",
99
+ pendingQuestionSubmit: "Submit",
100
+ pendingQuestionCancel: "Cancel",
101
+ pendingCustomPlaceholder: "Custom answer…",
89
102
  modelFollowsMain: "follows main session",
90
103
  stopReply: "Stop",
104
+ sideChatDone: "「{title}」finished replying",
91
105
  stopReplyTitle: "Stop the current reply",
92
106
  send: "Send",
93
107
  attachTitle: "Attach images",
@@ -164,6 +178,7 @@ window.__ModuleLoader__.load({
164
178
  reflowAllDone: "Reflowed ✓",
165
179
  promote: "Save as session",
166
180
  promoteTitle: "Fork this side chat into a standalone session in the main list",
181
+ promoteDone: "Saved as a regular session — it's open in the main view now",
167
182
  readWindow: "{shown} of {total} lines",
168
183
  searchPathsSummary: "{shown} of {total} paths",
169
184
  searchMatchesSummary: "{shown} of {total} matches in {files} files",
@@ -197,6 +212,10 @@ window.__ModuleLoader__.load({
197
212
  stopped: "已停止",
198
213
  toolLabel: "工具",
199
214
  failed: "失败",
215
+ todoTitle: "任务",
216
+ todoDone: "{n} 已完成",
217
+ todoDoing: "{n} 进行中",
218
+ todoPending: "{n} 待处理",
200
219
  running: "执行中…",
201
220
  inputPlaceholder: "输入消息…(Enter 发送,Shift+Enter 换行)",
202
221
  modelLabel: "模型:{name}",
@@ -205,8 +224,18 @@ window.__ModuleLoader__.load({
205
224
  permFullAccess: "Full access",
206
225
  pendingNotice: "⚠ 侧边会话有审批/提问待处理——点击跳到主视图处理",
207
226
  writeNotice: "侧边会话与主会话同权限,可以修改文件(当前模式见输入框旁 chip)",
227
+ pendingApprovalTitle: "等待审批",
228
+ pendingApprovalDesc: "工具 {name} 请求越权执行",
229
+ pendingReject: "拒绝",
230
+ pendingAllowOnce: "允许一次",
231
+ pendingQuestionTitle: "提问",
232
+ pendingPlanReview: "计划审批",
233
+ pendingQuestionSubmit: "提交回答",
234
+ pendingQuestionCancel: "取消",
235
+ pendingCustomPlaceholder: "自定义回答…",
208
236
  modelFollowsMain: "跟随主会话",
209
237
  stopReply: "停止",
238
+ sideChatDone: "「{title}」回复完成",
210
239
  stopReplyTitle: "停止当前回复",
211
240
  send: "发送",
212
241
  attachTitle: "添加图片附件",
@@ -283,6 +312,7 @@ window.__ModuleLoader__.load({
283
312
  reflowAllDone: "已回流 ✓",
284
313
  promote: "保存为正式会话",
285
314
  promoteTitle: "把侧边聊天转为会话列表里的正式会话",
315
+ promoteDone: "已保存为正式会话——已在主视图打开",
286
316
  readWindow: "{shown}/{total} 行",
287
317
  searchPathsSummary: "{shown}/{total} 个路径",
288
318
  searchMatchesSummary: "{shown}/{total} 处匹配 · {files} 个文件",
@@ -827,6 +857,57 @@ window.__ModuleLoader__.load({
827
857
  return `${cwdBase.replace(/\/+$/, "")}/${cwd}`;
828
858
  }
829
859
  /**
860
+ * 展示态路径缩短:>3 段的绝对路径只保末 3 段(≈仓库相对形态,与主区
861
+ * 「Read · dsh-sidenote/scripts/x.sh」同款)。窄栏标题位只够放语义尾部。
862
+ */
863
+ function shortPath(p) {
864
+ if (!p.startsWith("/")) return p;
865
+ const segs = p.split("/").filter((s) => s !== "");
866
+ return segs.length > 3 ? segs.slice(-3).join("/") : p;
867
+ }
868
+ /**
869
+ * 标题整形:「Tool /abs/path」→「Tool · 末3段」(主区同名同款);
870
+ * 裸绝对路径标题 → 末 3 段;不含路径的标题原样。
871
+ */
872
+ function displayTitle(title) {
873
+ const m = /^([A-Za-z][\w-]*) (\/.+)$/.exec(title);
874
+ const name = m?.[1];
875
+ const path = m?.[2];
876
+ if (name !== void 0 && path !== void 0) return `${name} · ${shortPath(path)}`;
877
+ return title.startsWith("/") ? shortPath(title) : title;
878
+ }
879
+ /** todo_write 入参形状校验(字段不齐即放弃 todo 卡)。两种数据源形状:
880
+ * 0.1.1 wire:callView.rawInput = todos 数组本身(dsh-tool-todo presentCall 实证);
881
+ * 0.1.2 argsRaw:{ todos: [...] }。 */
882
+ function todoItemsOf(raw) {
883
+ const list = Array.isArray(raw) ? raw : typeof raw === "object" && raw !== null ? raw.todos : void 0;
884
+ if (!Array.isArray(list) || list.length === 0) return void 0;
885
+ const items = [];
886
+ for (const it of list) {
887
+ const o = it;
888
+ if (typeof o?.content !== "string" || typeof o?.status !== "string") return void 0;
889
+ items.push({
890
+ content: o.content,
891
+ status: o.status
892
+ });
893
+ }
894
+ return items;
895
+ }
896
+ /** todo 卡标题:任务 · 非零状态组计数(主区「任务 · N 已完成 · …」同款)。 */
897
+ function todoTitleOf(items) {
898
+ let done = 0;
899
+ let doing = 0;
900
+ let pending = 0;
901
+ for (const it of items) if (it.status === "completed") done += 1;
902
+ else if (it.status === "in_progress") doing += 1;
903
+ else pending += 1;
904
+ const parts = [];
905
+ if (done > 0) parts.push(t("todoDone", { n: done }));
906
+ if (doing > 0) parts.push(t("todoDoing", { n: doing }));
907
+ if (pending > 0) parts.push(t("todoPending", { n: pending }));
908
+ return parts.length === 0 ? t("todoTitle") : `${t("todoTitle")} · ${parts.join(" · ")}`;
909
+ }
910
+ /**
830
911
  * callView + resultView → 一张卡的视图模型。三层判别全在这里;
831
912
  * 结果态优先(title 等字段 result 覆盖 call),缺省逐字段回退。
832
913
  */
@@ -839,9 +920,11 @@ window.__ModuleLoader__.load({
839
920
  case "terminal": {
840
921
  const rc = result?.card === "terminal" ? result : void 0;
841
922
  const cc = call?.card === "terminal" ? call : void 0;
923
+ const command = rc?.title ?? cc?.title ?? input.toolName;
842
924
  return {
843
925
  kind: "terminal",
844
- title: rc?.title ?? cc?.title ?? input.toolName,
926
+ title: cc?.description !== void 0 ? `${displayToolName(input.toolName)} · ${cc.description}` : command,
927
+ command,
845
928
  ...cc?.description !== void 0 ? { description: cc.description } : {},
846
929
  ...resolveCwd(cc?.cwd, input.cwdBase) !== void 0 ? { cwd: resolveCwd(cc?.cwd, input.cwdBase) } : {},
847
930
  ...rc?.output !== void 0 ? { output: rc.output } : {},
@@ -854,7 +937,7 @@ window.__ModuleLoader__.load({
854
937
  const cc = call?.card === "diff" ? call : void 0;
855
938
  return {
856
939
  kind: "diff",
857
- title: rc?.title ?? cc?.title ?? input.toolName,
940
+ title: displayTitle(rc?.title ?? cc?.title ?? input.toolName),
858
941
  diffs: rc?.diffs ?? cc?.diffs ?? [],
859
942
  ...cc?.locations !== void 0 ? { locations: cc.locations } : {}
860
943
  };
@@ -886,7 +969,7 @@ window.__ModuleLoader__.load({
886
969
  if (rc === void 0) return genericModel(input, call, result);
887
970
  return {
888
971
  kind: "read",
889
- title: rc.title ?? call?.title ?? input.toolName,
972
+ title: `Read · ${shortPath(rc.path)}`,
890
973
  path: rc.path,
891
974
  lines: rc.lines,
892
975
  totalLines: rc.totalLines,
@@ -932,15 +1015,27 @@ window.__ModuleLoader__.load({
932
1015
  function genericModel(input, call, result) {
933
1016
  const gc = call?.card === "generic" ? call : void 0;
934
1017
  const gr = result?.card === "generic" ? result : void 0;
1018
+ if (input.toolName === "todo_write") {
1019
+ const items = todoItemsOf(gc?.rawInput);
1020
+ if (items !== void 0) return {
1021
+ kind: "todo",
1022
+ title: todoTitleOf(items),
1023
+ items
1024
+ };
1025
+ }
935
1026
  return {
936
1027
  kind: "generic",
937
- title: gr?.title ?? gc?.title ?? input.toolName,
1028
+ title: displayTitle(gr?.title ?? gc?.title ?? input.toolName),
938
1029
  icon: gc?.kind ?? "other",
939
1030
  ...gc?.rawInput !== void 0 ? { rawInput: gc.rawInput } : {},
940
1031
  bodyText: blocksText(gr?.content) ?? blocksText(gc?.content) ?? input.rawText,
941
1032
  ...gc?.locations !== void 0 ? { locations: gc.locations } : {}
942
1033
  };
943
1034
  }
1035
+ /** 工具机器名 → 展示名(主区「Bash · …」的同款首字母大写)。 */
1036
+ function displayToolName(name) {
1037
+ return name.charAt(0).toUpperCase() + name.slice(1);
1038
+ }
944
1039
  /** 结果正文尾标解析(宿主 parseExitStatus 的线格式)。 */
945
1040
  function parseExitStatus(text) {
946
1041
  const m = /\n\[(?:exit code: (\d+)|killed by signal: ([A-Z]+))\]\s*$/.exec(text);
@@ -980,6 +1075,114 @@ window.__ModuleLoader__.load({
980
1075
  ...typeof m.lang === "string" ? { lang: m.lang } : {}
981
1076
  };
982
1077
  }
1078
+ /**
1079
+ * FsDiffMeta 校验(0.1.2 diff 卡,dsh-tool-fs computeHunkDiffs 产物的忠实子集)。
1080
+ * 返回值三态:{diffs 非空} / 'empty'(空数组——write create/同内容覆写是合法值,
1081
+ * 客户端回退 args 整文件 diff)/ undefined(malformed——降级)。
1082
+ * 逐项校验,一项违规整卡降级(官方 narrowDiffs 同款)。
1083
+ */
1084
+ function diffMetaOf(meta) {
1085
+ if (typeof meta !== "object" || meta === null || Array.isArray(meta)) return void 0;
1086
+ const m = meta;
1087
+ if (!Array.isArray(m.diffs)) return void 0;
1088
+ if (m.diffs.length === 0) return "empty";
1089
+ const diffs = [];
1090
+ for (const raw of m.diffs) {
1091
+ const d = raw;
1092
+ if (typeof d?.path !== "string") return void 0;
1093
+ if (d.oldText !== null && typeof d.oldText !== "string") return void 0;
1094
+ if (typeof d.newText !== "string") return void 0;
1095
+ diffs.push({
1096
+ path: d.path,
1097
+ oldText: d.oldText,
1098
+ newText: d.newText
1099
+ });
1100
+ }
1101
+ return { diffs };
1102
+ }
1103
+ /** SearchMeta 校验(0.1.2 search 卡;truncated/total 是硬字段;空结果合法)。 */
1104
+ function searchMetaOf(meta) {
1105
+ if (typeof meta !== "object" || meta === null || Array.isArray(meta)) return void 0;
1106
+ const m = meta;
1107
+ if (typeof m.truncated !== "boolean") return void 0;
1108
+ if (typeof m.total !== "number" || !Number.isInteger(m.total) || m.total < 0) return void 0;
1109
+ if (m.shape === "matches") {
1110
+ if (!Array.isArray(m.files)) return void 0;
1111
+ const files = [];
1112
+ for (const rawF of m.files) {
1113
+ const f = rawF;
1114
+ if (typeof f?.path !== "string" || !Array.isArray(f.matches)) return void 0;
1115
+ const matches = [];
1116
+ for (const rawM of f.matches) {
1117
+ const mm = rawM;
1118
+ if (typeof mm?.lineNumber !== "number" || !Number.isInteger(mm.lineNumber) || mm.lineNumber < 1) return void 0;
1119
+ if (typeof mm.line !== "string") return void 0;
1120
+ matches.push({
1121
+ lineNumber: mm.lineNumber,
1122
+ line: mm.line
1123
+ });
1124
+ }
1125
+ files.push({
1126
+ path: f.path,
1127
+ matches
1128
+ });
1129
+ }
1130
+ return {
1131
+ shape: "matches",
1132
+ files,
1133
+ truncated: m.truncated,
1134
+ total: m.total
1135
+ };
1136
+ }
1137
+ if (m.shape === "paths") {
1138
+ if (!Array.isArray(m.paths) || !m.paths.every((p) => typeof p === "string")) return void 0;
1139
+ return {
1140
+ shape: "paths",
1141
+ paths: m.paths,
1142
+ truncated: m.truncated,
1143
+ total: m.total
1144
+ };
1145
+ }
1146
+ }
1147
+ /** WebSearchMeta 校验(sources 逐项 url 必填、title/snippet 可选;publishedAt 校验后丢弃)。 */
1148
+ function webSearchMetaOf(meta) {
1149
+ if (typeof meta !== "object" || meta === null || Array.isArray(meta)) return void 0;
1150
+ const m = meta;
1151
+ if (typeof m.truncated !== "boolean") return void 0;
1152
+ if (!Array.isArray(m.sources)) return void 0;
1153
+ const sources = [];
1154
+ for (const raw of m.sources) {
1155
+ const s = raw;
1156
+ if (typeof s?.url !== "string") return void 0;
1157
+ if (s.title !== void 0 && typeof s.title !== "string") return void 0;
1158
+ if (s.snippet !== void 0 && typeof s.snippet !== "string") return void 0;
1159
+ if (s.publishedAt !== void 0 && typeof s.publishedAt !== "string") return void 0;
1160
+ sources.push({
1161
+ url: s.url,
1162
+ ...s.title !== void 0 ? { title: s.title } : {},
1163
+ ...s.snippet !== void 0 ? { snippet: s.snippet } : {}
1164
+ });
1165
+ }
1166
+ if (m.answer !== void 0 && typeof m.answer !== "string") return void 0;
1167
+ return {
1168
+ sources,
1169
+ truncated: m.truncated,
1170
+ ...m.answer !== void 0 ? { answer: m.answer } : {}
1171
+ };
1172
+ }
1173
+ /** WebFetchMeta 校验(url + 整数 statusCode + truncated 硬字段)。 */
1174
+ function webFetchMetaOf(meta) {
1175
+ if (typeof meta !== "object" || meta === null || Array.isArray(meta)) return void 0;
1176
+ const m = meta;
1177
+ if (typeof m.url !== "string") return void 0;
1178
+ if (typeof m.statusCode !== "number" || !Number.isInteger(m.statusCode)) return void 0;
1179
+ if (typeof m.truncated !== "boolean") return void 0;
1180
+ return {
1181
+ url: m.url,
1182
+ statusCode: m.statusCode,
1183
+ truncated: m.truncated
1184
+ };
1185
+ }
983
1186
  /** argsRaw(JSON 字符串)里尽力提取展示字段。 */
984
1187
  function argsSummary(argsRaw) {
985
1188
  if (argsRaw === void 0 || argsRaw === "") return {};
@@ -990,7 +1193,15 @@ window.__ModuleLoader__.load({
990
1193
  return {
991
1194
  ...typeof a.command === "string" ? { command: a.command } : {},
992
1195
  ...typeof a.description === "string" ? { description: a.description } : {},
993
- ...typeof a.file_path === "string" ? { path: a.file_path } : typeof a.path === "string" ? { path: a.path } : {}
1196
+ ...typeof a.file_path === "string" ? { path: a.file_path } : typeof a.path === "string" ? { path: a.path } : {},
1197
+ ...Array.isArray(a.todos) ? { todos: a.todos } : {},
1198
+ ...typeof a.content === "string" ? { content: a.content } : {},
1199
+ ...typeof a.old_string === "string" ? { oldString: a.old_string } : {},
1200
+ ...typeof a.new_string === "string" ? { newString: a.new_string } : {},
1201
+ ...typeof a.pattern === "string" ? { pattern: a.pattern } : {},
1202
+ ...typeof a.include === "string" ? { include: a.include } : {},
1203
+ ...Array.isArray(a.queries) && a.queries.every((q) => typeof q === "string") ? { queries: a.queries } : {},
1204
+ ...typeof a.url === "string" ? { url: a.url } : {}
994
1205
  };
995
1206
  } catch {
996
1207
  return {};
@@ -1005,9 +1216,11 @@ window.__ModuleLoader__.load({
1005
1216
  const name = input.name;
1006
1217
  if (name === "bash" || name === "bash-persistent" || name === "pwsh" || name === "pwsh-persistent") {
1007
1218
  const { output, exitCode, signal } = parseExitStatus(input.rawText);
1219
+ const command = args.command ?? name;
1008
1220
  return {
1009
1221
  kind: "terminal",
1010
- title: args.command ?? name,
1222
+ title: args.description !== void 0 ? `${displayToolName(name)} · ${args.description}` : command,
1223
+ command,
1011
1224
  ...args.description !== void 0 ? { description: args.description } : {},
1012
1225
  output,
1013
1226
  ...exitCode !== void 0 ? { exitCode } : {},
@@ -1018,33 +1231,113 @@ window.__ModuleLoader__.load({
1018
1231
  const meta = readMetaOf(input.meta);
1019
1232
  if (meta !== void 0) return {
1020
1233
  kind: "read",
1021
- title: `Read ${meta.path}`,
1234
+ title: `Read · ${shortPath(meta.path)}`,
1022
1235
  path: meta.path,
1023
1236
  lines: meta.lines,
1024
1237
  totalLines: meta.totalLines,
1025
1238
  ...meta.lang !== void 0 ? { lang: meta.lang } : {}
1026
1239
  };
1027
1240
  }
1241
+ if (name === "todo_write") {
1242
+ const items = todoItemsOf(args.todos);
1243
+ if (items !== void 0) return {
1244
+ kind: "todo",
1245
+ title: todoTitleOf(items),
1246
+ items
1247
+ };
1248
+ }
1249
+ if ((name === "write" || name === "edit") && input.isError !== true) {
1250
+ const path = args.path;
1251
+ if (path !== void 0 && path.trim() !== "") {
1252
+ const dm = diffMetaOf(input.meta);
1253
+ if (dm !== void 0 && dm !== "empty") return {
1254
+ kind: "diff",
1255
+ title: displayTitle(`${name === "write" ? "Write" : "Edit"} ${path}`),
1256
+ diffs: dm.diffs,
1257
+ locations: [{ path }]
1258
+ };
1259
+ if (name === "write" && args.content !== void 0) return {
1260
+ kind: "diff",
1261
+ title: displayTitle(`Write ${path}`),
1262
+ diffs: [{
1263
+ path,
1264
+ oldText: null,
1265
+ newText: args.content
1266
+ }],
1267
+ locations: [{ path }]
1268
+ };
1269
+ }
1270
+ }
1271
+ if ((name === "grep" || name === "glob") && input.isError !== true) {
1272
+ const sm = searchMetaOf(input.meta);
1273
+ const wantShape = name === "grep" ? "matches" : "paths";
1274
+ if (sm !== void 0 && sm.shape === wantShape && args.pattern !== void 0) {
1275
+ const where = args.path !== void 0 && args.path.trim() !== "" ? ` in ${shortPath(args.path)}` : "";
1276
+ const inc = name === "grep" && args.include !== void 0 && args.include.trim() !== "" ? ` (${args.include})` : "";
1277
+ const title = `${name === "grep" ? "Grep" : "Glob"} ${args.pattern}${where}${inc}`;
1278
+ return sm.shape === "matches" ? {
1279
+ kind: "search",
1280
+ title,
1281
+ shape: "matches",
1282
+ files: sm.files,
1283
+ truncated: sm.truncated,
1284
+ total: sm.total
1285
+ } : {
1286
+ kind: "search",
1287
+ title,
1288
+ shape: "paths",
1289
+ paths: sm.paths,
1290
+ truncated: sm.truncated,
1291
+ total: sm.total
1292
+ };
1293
+ }
1294
+ }
1295
+ if (name === "web_search" && input.isError !== true) {
1296
+ const wm = webSearchMetaOf(input.meta);
1297
+ const queries = args.queries?.filter((q) => q.trim() !== "") ?? [];
1298
+ if (wm !== void 0 && queries.length > 0) return {
1299
+ kind: "web",
1300
+ title: queries.join(", "),
1301
+ webKind: "search",
1302
+ sources: wm.sources,
1303
+ truncated: wm.truncated,
1304
+ ...wm.answer !== void 0 ? { answer: wm.answer } : {}
1305
+ };
1306
+ }
1307
+ if (name === "web_fetch" && input.isError !== true) {
1308
+ const wm = webFetchMetaOf(input.meta);
1309
+ if (wm !== void 0 && args.url !== void 0 && args.url.trim() !== "") return {
1310
+ kind: "web",
1311
+ title: args.url,
1312
+ webKind: "fetch",
1313
+ url: wm.url,
1314
+ statusCode: wm.statusCode,
1315
+ truncated: wm.truncated
1316
+ };
1317
+ }
1028
1318
  return {
1029
1319
  kind: "generic",
1030
- title: args.path !== void 0 ? `${name} ${args.path}` : args.command ?? name,
1320
+ title: args.path !== void 0 ? `${name} · ${shortPath(args.path)}` : args.command ?? name,
1031
1321
  icon: KIND_BY_NAME[name] ?? "other",
1032
1322
  bodyText: input.rawText === "" ? void 0 : input.rawText
1033
1323
  };
1034
1324
  }
1035
- /** 工具名 → 图标类别的静态映射(0.1.2 没有 callView.kind 可用)。 */
1325
+ /** 工具名 → 图标类别的静态映射(0.1.2 没有 callView.kind 可用)。
1326
+ * wire 名实证:str_replace_editor 是下划线(uitool client.js L381);
1327
+ * web_search 的 0.1.1 wire kind 是 'search'(presentSearchCall),
1328
+ * 'fetch' 是 web_fetch 的(presentFetchCall)。 */
1036
1329
  const KIND_BY_NAME = {
1037
1330
  read: "read",
1038
1331
  write: "edit",
1039
1332
  edit: "edit",
1040
- "str-replace-editor": "edit",
1333
+ str_replace_editor: "edit",
1041
1334
  bash: "execute",
1042
1335
  "bash-persistent": "execute",
1043
1336
  pwsh: "execute",
1044
1337
  "pwsh-persistent": "execute",
1045
1338
  grep: "search",
1046
1339
  glob: "search",
1047
- web_search: "fetch",
1340
+ web_search: "search",
1048
1341
  web_fetch: "fetch"
1049
1342
  };
1050
1343
  //#endregion
@@ -1135,7 +1428,8 @@ window.__ModuleLoader__.load({
1135
1428
  name: toolName,
1136
1429
  argsRaw: typeof call?.argsRaw === "string" ? call.argsRaw : void 0,
1137
1430
  meta: n.meta,
1138
- rawText: text
1431
+ rawText: text,
1432
+ ...n.isError === true ? { isError: true } : {}
1139
1433
  });
1140
1434
  return {
1141
1435
  key: seqKey("t", n),
@@ -1390,7 +1684,7 @@ window.__ModuleLoader__.load({
1390
1684
  }
1391
1685
  //#endregion
1392
1686
  //#region \0dsh-css:/home/runner/work/dsh-sidenote/dsh-sidenote/src/client/sidechat/sidechat.module.css.mjs
1393
- const css$1 = "._5KEEsG_root{height:100%;color:var(--dsw-alias-label-primary);flex-direction:column;font-size:13px;display:flex;position:relative}._5KEEsG_body{flex-direction:column;flex:1;padding:12px 10px;display:flex;overflow-y:auto}._5KEEsG_transcript{flex-direction:column;gap:12px;display:flex}._5KEEsG_empty,._5KEEsG_stateScreen{text-align:center;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:8px;padding:24px 16px;display:flex}._5KEEsG_emptyIcon{color:var(--dsw-alias-label-tertiary)}._5KEEsG_emptyTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:600}._5KEEsG_emptyText{color:var(--dsw-alias-label-tertiary);max-width:240px;font-size:12px;line-height:1.6}._5KEEsG_stateDetail{color:var(--dsw-alias-state-error-primary);word-break:break-word;max-width:260px;font-size:12px;line-height:1.6}._5KEEsG_userRow{justify-content:flex-end;display:flex}._5KEEsG_userBubble{background:var(--dsw-specific-bubble,#edf3fe);max-width:88%;color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;border-radius:16px;padding:8px 12px;font-size:13px;line-height:1.55}._5KEEsG_assistantRow{justify-content:flex-start;display:flex;position:relative}._5KEEsG_rowActions{opacity:0;z-index:2;transition:opacity .12s ease-out;position:absolute;top:-8px;right:0}._5KEEsG_assistantRow:hover ._5KEEsG_rowActions{opacity:1}._5KEEsG_reflowButton{border:1px solid var(--dsw-alias-border-l1,#0000001a);background:var(--dsw-alias-bg-layer-1,#fff);width:22px;height:22px;color:var(--dsw-alias-label-tertiary,#8f959e);cursor:pointer;border-radius:999px;place-items:center;padding:0;display:grid;box-shadow:0 2px 8px #00000014}._5KEEsG_reflowButton:hover{color:var(--dsw-alias-state-business-primary,#2f88ff);border-color:var(--dsw-alias-state-business-primary,#2f88ff)}._5KEEsG_assistantBody{word-break:break-word;max-width:100%;font-size:13px;line-height:1.6}._5KEEsG_reasoning{border-left:3px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);margin-bottom:6px;padding-left:8px;font-size:12px}._5KEEsG_reasoning>summary{cursor:pointer;user-select:none}._5KEEsG_reasoningBody{white-space:pre-wrap;word-break:break-word;margin-top:4px}._5KEEsG_streamingHint{color:var(--dsw-alias-label-dimmed)}._5KEEsG_toolCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:8px;overflow:hidden}._5KEEsG_toolHead{background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;padding:5px 10px;font-size:12px;display:flex}._5KEEsG_toolError{color:var(--dsw-alias-state-error-primary)}._5KEEsG_toolRunning{color:var(--dsw-alias-label-tertiary)}._5KEEsG_toolBody{color:var(--dsw-alias-label-tertiary);white-space:pre-wrap;word-break:break-word;max-height:160px;padding:8px 10px;font-size:12px;overflow-y:auto}._5KEEsG_errorRow{color:var(--dsw-alias-state-error-primary);white-space:pre-wrap;padding:4px 2px;font-size:12px}._5KEEsG_noticeRow{color:var(--dsw-alias-label-dimmed);text-align:center;padding:2px 0;font-size:12px}._5KEEsG_composer{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:6px;padding:8px 10px;display:flex;position:relative}._5KEEsG_input{resize:vertical;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);width:100%;min-height:40px;max-height:160px;color:var(--dsw-alias-label-primary);font:inherit;box-sizing:border-box;border-radius:8px;padding:8px 10px}._5KEEsG_input:focus{border-color:var(--dsw-alias-state-business-primary);outline:none}._5KEEsG_composerFoot{justify-content:space-between;align-items:center;gap:8px;display:flex}._5KEEsG_modelLabel{color:var(--dsw-alias-label-dimmed);text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}._5KEEsG_sendButton,._5KEEsG_stopButton{appearance:none;border:1px solid var(--dsw-alias-state-business-primary);background:var(--dsw-alias-button-info-fill);color:#fff;cursor:pointer;border-radius:8px;flex:none;align-items:center;gap:4px;padding:6px 12px;font-size:13px;display:inline-flex}._5KEEsG_sendButton:disabled{opacity:.55;cursor:default}._5KEEsG_headerSide{border:1px solid var(--dsw-alias-border-l1,#0000001a);color:var(--dsw-alias-label-secondary,#3f454d);cursor:pointer;user-select:none;background:0 0;border-radius:999px;align-items:center;gap:5px;padding:3px 10px;font-size:12px;line-height:18px;display:inline-flex}._5KEEsG_headerSide:hover{background:var(--dsw-alias-interactive-bg-hover,#0000000d);color:var(--dsw-alias-label-primary,#1f2329)}._5KEEsG_sentChipRow{align-items:center;gap:6px;margin-right:8px;display:inline-flex}._5KEEsG_sentChip{border:1px solid var(--dsw-alias-border-l1,#0000001a);background:var(--dsw-alias-interactive-bg-hover,#0000000d);color:var(--dsw-alias-label-secondary,#3f454d);user-select:none;border-radius:999px;align-items:center;gap:4px;padding:1px 8px;font-size:11px;line-height:16px;display:inline-flex}._5KEEsG_toolCardV2{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:8px;font-size:12px;overflow:hidden}._5KEEsG_toolGenericBody{flex-direction:column;gap:6px;padding:8px 10px;display:flex}._5KEEsG_toolBodyText{color:var(--dsw-alias-label-secondary);white-space:pre-wrap;word-break:break-word;font-size:12px;line-height:1.55;font-family:var(--dsw-alias-font-mono,ui-monospace, monospace);margin:0}._5KEEsG_toolLocations{color:var(--dsw-alias-label-tertiary);white-space:pre-wrap;word-break:break-all;font-size:11px}._5KEEsG_inheritedBody{border-top:1px dashed var(--dsw-alias-border-l2);opacity:.92;padding:6px 0 2px}._5KEEsG_densityRow{justify-content:flex-end;align-items:center;padding:2px 2px 4px;display:flex}._5KEEsG_densityToggle{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:4px;padding:2px 6px;font-size:11px}._5KEEsG_densityToggle:hover{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}._5KEEsG_jumpBottom{z-index:5;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);cursor:pointer;border-radius:999px;padding:3px 10px;font-size:11px;position:absolute;bottom:96px;right:12px;box-shadow:0 2px 8px #0000001f}._5KEEsG_jumpBottom:hover{background:var(--dsw-alias-bg-layer-2)}._5KEEsG_modelSelect{color:var(--dsw-alias-label-secondary);cursor:pointer;text-overflow:ellipsis;white-space:nowrap;background:0 0;border:none;border-radius:4px;max-width:180px;padding:2px 6px;font-size:12px;overflow:hidden}._5KEEsG_modelSelect:hover{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}._5KEEsG_composerChips{align-items:center;gap:4px;min-width:0;display:inline-flex;overflow:hidden}._5KEEsG_slashMenu{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);z-index:20;border-radius:10px;max-height:260px;margin-bottom:6px;padding:4px;position:absolute;bottom:100%;left:8px;right:8px;overflow-y:auto;box-shadow:0 8px 24px #00000024}._5KEEsG_slashGroupTitle{color:var(--dsw-alias-label-tertiary);text-transform:none;padding:4px 8px 2px;font-size:11px}._5KEEsG_slashItem,._5KEEsG_slashItemActive{text-align:left;cursor:pointer;width:100%;color:var(--dsw-alias-label-primary);background:0 0;border:none;border-radius:6px;align-items:baseline;gap:8px;padding:6px 8px;font-size:13px;display:flex}._5KEEsG_slashItemActive,._5KEEsG_slashItem:hover{background:var(--dsw-alias-interactive-bg-hover)}._5KEEsG_slashItemName{flex:none;font-weight:500}._5KEEsG_slashItemDetail{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}._5KEEsG_attachRail{flex-wrap:wrap;gap:6px;padding:2px 0;display:flex}._5KEEsG_attachThumb{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;width:44px;height:44px;position:relative;overflow:hidden}._5KEEsG_attachImg{object-fit:cover;width:100%;height:100%;display:block}._5KEEsG_attachRemove{color:#fff;cursor:pointer;background:#0000008c;border:none;border-radius:999px;place-items:center;width:16px;height:16px;padding:0;font-size:11px;line-height:1;display:grid;position:absolute;top:1px;right:1px}._5KEEsG_attachButton{color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:6px;padding:4px;display:inline-flex}._5KEEsG_attachButton:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}._5KEEsG_pendingBar{border:1px solid var(--dsw-alias-state-warning-primary,#d4a017);background:var(--dsw-alias-bg-layer-2);width:calc(100% - 16px);color:var(--dsw-alias-state-warning-primary,#d4a017);text-align:left;cursor:pointer;border-radius:8px;margin:0 8px 6px;padding:6px 10px;font-size:12px;display:block}._5KEEsG_pendingBar:hover{background:var(--dsw-alias-bg-layer-3)}._5KEEsG_writeNotice{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);border-radius:8px;align-items:center;gap:8px;margin:0 8px 6px;padding:6px 10px;font-size:12px;display:flex}._5KEEsG_writeNoticeClose{color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;margin-left:auto;padding:0 4px;font-size:13px}._5KEEsG_parentStrip{border:none;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;color:var(--dsw-alias-label-secondary);cursor:pointer;text-align:left;align-items:center;gap:6px;padding:5px 12px;font-size:11px;display:flex}._5KEEsG_parentStrip:hover{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary)}._5KEEsG_dotRunning,._5KEEsG_dotPending,._5KEEsG_dotIdle{border-radius:999px;flex:none;width:7px;height:7px}._5KEEsG_dotRunning{background:var(--dsw-alias-state-business-primary,#2f88ff);animation:1.2s ease-in-out infinite _5KEEsG_sidechat-pulse}._5KEEsG_dotPending{background:var(--dsw-alias-state-warning-primary,#d4a017)}._5KEEsG_dotIdle{background:var(--dsw-alias-label-dimmed)}@keyframes _5KEEsG_sidechat-pulse{50%{opacity:.45}}._5KEEsG_actionGroup{gap:2px;margin-right:auto;display:inline-flex}";
1687
+ const css$1 = "._5KEEsG_root{height:100%;color:var(--dsw-alias-label-primary);flex-direction:column;font-size:13px;display:flex;position:relative}._5KEEsG_body{flex-direction:column;flex:1;padding:12px 10px;display:flex;overflow-y:auto}._5KEEsG_transcript{flex-direction:column;gap:10px;display:flex}._5KEEsG_empty,._5KEEsG_stateScreen{text-align:center;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:8px;padding:24px 16px;display:flex}._5KEEsG_emptyIcon{color:var(--dsw-alias-label-tertiary)}._5KEEsG_emptyTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:600}._5KEEsG_emptyText{color:var(--dsw-alias-label-tertiary);max-width:240px;font-size:12px;line-height:1.6}._5KEEsG_stateDetail{color:var(--dsw-alias-state-error-primary);word-break:break-word;max-width:260px;font-size:12px;line-height:1.6}._5KEEsG_userRow{justify-content:flex-end;display:flex}._5KEEsG_userBubble{background:var(--dsw-specific-bubble,#edf3fe);max-width:88%;color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;border-radius:16px;padding:8px 12px;font-size:13px;line-height:1.55}._5KEEsG_assistantRow{justify-content:flex-start;display:flex;position:relative}._5KEEsG_rowActions{opacity:0;z-index:2;transition:opacity .12s ease-out;position:absolute;top:-8px;right:0}._5KEEsG_assistantRow:hover ._5KEEsG_rowActions{opacity:1}._5KEEsG_reflowButton{border:1px solid var(--dsw-alias-border-l1,#0000001a);background:var(--dsw-alias-bg-layer-1,#fff);width:22px;height:22px;color:var(--dsw-alias-label-tertiary,#8f959e);cursor:pointer;border-radius:999px;place-items:center;padding:0;display:grid;box-shadow:0 2px 8px #00000014}._5KEEsG_reflowButton:hover{color:var(--dsw-alias-state-business-primary,#2f88ff);border-color:var(--dsw-alias-state-business-primary,#2f88ff)}._5KEEsG_assistantBody{word-break:break-word;max-width:100%;font-size:13px;line-height:1.6}._5KEEsG_reasoning{border-left:3px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);margin-bottom:6px;padding-left:8px;font-size:12px}._5KEEsG_reasoning>summary{cursor:pointer;user-select:none}._5KEEsG_reasoningBody{white-space:pre-wrap;word-break:break-word;color:var(--dsw-alias-label-tertiary);margin-top:2px;padding-bottom:2px;padding-left:22px}._5KEEsG_streamingHint{color:var(--dsw-alias-label-dimmed)}._5KEEsG_toolCard{font-size:12px}._5KEEsG_toolHead{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;font-size:12px;display:flex}._5KEEsG_toolError{color:var(--dsw-alias-state-error-primary)}._5KEEsG_toolRunning{color:var(--dsw-alias-label-tertiary)}._5KEEsG_toolBody{color:var(--dsw-alias-label-tertiary);white-space:pre-wrap;word-break:break-word;max-height:160px;padding:2px 0 2px 22px;font-size:12px;overflow-y:auto}._5KEEsG_errorRow{color:var(--dsw-alias-state-error-primary);white-space:pre-wrap;padding:4px 2px;font-size:12px}._5KEEsG_noticeRow{color:var(--dsw-alias-label-dimmed);text-align:center;padding:2px 0;font-size:12px}._5KEEsG_composer{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:6px;padding:8px 10px;display:flex;position:relative}._5KEEsG_composerCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:14px;flex-direction:column;gap:2px;padding:4px 6px 6px;transition:border-color .12s;display:flex}._5KEEsG_composerCard:focus-within{border-color:var(--dsw-alias-state-business-primary)}._5KEEsG_input{resize:none;width:100%;min-height:36px;max-height:160px;color:var(--dsw-alias-label-primary);font:inherit;box-sizing:border-box;background:0 0;border:none;padding:6px 6px 2px}._5KEEsG_input:focus{outline:none}._5KEEsG_composerFoot{justify-content:space-between;align-items:center;gap:8px;padding:0 2px;display:flex}._5KEEsG_modelLabel{color:var(--dsw-alias-label-dimmed);text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}._5KEEsG_sendButton,._5KEEsG_stopButton{appearance:none;background:var(--dsw-alias-button-info-fill);color:#fff;cursor:pointer;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;width:28px;height:28px;padding:0;display:inline-flex}._5KEEsG_sendButton:disabled{opacity:.45;cursor:default}._5KEEsG_headerSide{border:1px solid var(--dsw-alias-border-l1,#0000001a);color:var(--dsw-alias-label-secondary,#3f454d);cursor:pointer;user-select:none;background:0 0;border-radius:999px;align-items:center;gap:5px;padding:3px 10px;font-size:12px;line-height:18px;display:inline-flex}._5KEEsG_headerSide:hover{background:var(--dsw-alias-interactive-bg-hover,#0000000d);color:var(--dsw-alias-label-primary,#1f2329)}._5KEEsG_sentChipRow{align-items:center;gap:6px;margin-right:8px;display:inline-flex}._5KEEsG_sentChip{border:1px solid var(--dsw-alias-border-l1,#0000001a);background:var(--dsw-alias-interactive-bg-hover,#0000000d);color:var(--dsw-alias-label-secondary,#3f454d);user-select:none;border-radius:999px;align-items:center;gap:4px;padding:1px 8px;font-size:11px;line-height:16px;display:inline-flex}._5KEEsG_flowRow{font-size:12px}._5KEEsG_rowPreview{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-dimmed);flex:1;margin-left:8px;font-size:12px;overflow:hidden}._5KEEsG_flowRowError{color:var(--dsw-alias-state-error-primary)}._5KEEsG_todoBody{flex-direction:column;gap:3px;padding:2px 0 4px 22px;display:flex}._5KEEsG_todoItem{align-items:baseline;gap:7px;min-width:0;font-size:12px;line-height:1.5;display:flex}._5KEEsG_todoDot{background:var(--dsw-alias-label-dimmed);border-radius:50%;flex:none;width:6px;height:6px;transform:translateY(-1px)}._5KEEsG_todoDot[data-status=completed]{background:var(--dsw-alias-state-success-primary,#34a853)}._5KEEsG_todoDot[data-status=in_progress]{background:var(--dsw-alias-state-business-primary)}._5KEEsG_todoText[data-status=completed]{color:var(--dsw-alias-label-dimmed)}._5KEEsG_toolGenericBody{flex-direction:column;gap:6px;padding:4px 0 4px 22px;display:flex}._5KEEsG_toolBodyText{color:var(--dsw-alias-label-secondary);white-space:pre-wrap;word-break:break-word;font-size:12px;line-height:1.55;font-family:var(--dsw-alias-font-mono,ui-monospace, monospace);margin:0}._5KEEsG_toolLocations{color:var(--dsw-alias-label-tertiary);white-space:pre-wrap;word-break:break-all;font-size:11px}._5KEEsG_inheritedBody{border-top:1px dashed var(--dsw-alias-border-l2);opacity:.92;padding:6px 0 2px}._5KEEsG_densityRow{justify-content:flex-end;align-items:center;padding:2px 2px 4px;display:flex}._5KEEsG_densityToggle{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:4px;padding:2px 6px;font-size:11px}._5KEEsG_densityToggle:hover{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}._5KEEsG_jumpBottom{z-index:5;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);cursor:pointer;border-radius:999px;padding:3px 10px;font-size:11px;position:absolute;bottom:96px;right:12px;box-shadow:0 2px 8px #0000001f}._5KEEsG_jumpBottom:hover{background:var(--dsw-alias-bg-layer-2)}._5KEEsG_modelSelect{color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-radius:6px;align-items:center;gap:3px;max-width:180px;padding:3px 6px;font-size:12px;display:inline-flex;overflow:hidden}._5KEEsG_modelSelect>span:first-child{text-overflow:ellipsis;overflow:hidden}._5KEEsG_modelSelect>svg{color:var(--dsw-alias-label-dimmed);flex:none}._5KEEsG_modelSelect:hover{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}._5KEEsG_composerChips{align-items:center;gap:4px;min-width:0;display:inline-flex;overflow:hidden}._5KEEsG_slashMenu{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);z-index:20;border-radius:10px;max-height:260px;margin-bottom:6px;padding:4px;position:absolute;bottom:100%;left:8px;right:8px;overflow-y:auto;box-shadow:0 8px 24px #00000024}._5KEEsG_slashGroupTitle{color:var(--dsw-alias-label-tertiary);text-transform:none;padding:4px 8px 2px;font-size:11px}._5KEEsG_slashItem,._5KEEsG_slashItemActive{text-align:left;cursor:pointer;width:100%;color:var(--dsw-alias-label-primary);background:0 0;border:none;border-radius:6px;align-items:baseline;gap:8px;padding:6px 8px;font-size:13px;display:flex}._5KEEsG_slashItemActive,._5KEEsG_slashItem:hover{background:var(--dsw-alias-interactive-bg-hover)}._5KEEsG_slashItemName{flex:none;font-weight:500}._5KEEsG_slashItemDetail{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}._5KEEsG_attachRail{flex-wrap:wrap;gap:6px;padding:2px 0;display:flex}._5KEEsG_attachThumb{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;width:44px;height:44px;position:relative;overflow:hidden}._5KEEsG_attachImg{object-fit:cover;width:100%;height:100%;display:block}._5KEEsG_attachRemove{color:#fff;cursor:pointer;background:#0000008c;border:none;border-radius:999px;place-items:center;width:16px;height:16px;padding:0;font-size:11px;line-height:1;display:grid;position:absolute;top:1px;right:1px}._5KEEsG_attachButton{color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:6px;padding:4px;display:inline-flex}._5KEEsG_attachButton:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}._5KEEsG_pendingCard{border:1px solid var(--dsw-alias-border-l2);border-left:3px solid var(--dsw-alias-state-warning-primary,#e8a13a);background:var(--dsw-alias-bg-layer-2);border-radius:10px;flex-direction:column;gap:6px;margin:0 10px 6px;padding:10px 12px;font-size:12px;display:flex}._5KEEsG_pendingHead{color:var(--dsw-alias-state-warning-primary,#e8a13a);align-items:center;gap:6px;display:flex}._5KEEsG_pendingTitle{color:var(--dsw-alias-label-primary);font-weight:600}._5KEEsG_pendingDesc{color:var(--dsw-alias-label-secondary)}._5KEEsG_pendingReason{color:var(--dsw-alias-label-tertiary);white-space:pre-wrap;word-break:break-word}._5KEEsG_pendingQuestion{flex-direction:column;gap:4px;padding:4px 0;display:flex}._5KEEsG_pendingQHeader{color:var(--dsw-alias-label-primary);font-weight:600}._5KEEsG_pendingQText{color:var(--dsw-alias-label-secondary)}._5KEEsG_pendingOption{text-align:left;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border-radius:6px;flex-direction:column;align-items:flex-start;gap:2px;padding:5px 8px;font-size:12px;display:flex}._5KEEsG_pendingOption:hover{background:var(--dsw-alias-interactive-bg-hover,#0000000d)}._5KEEsG_pendingOptionActive{text-align:left;border:1px solid var(--dsw-alias-state-business-primary);background:var(--dsw-alias-interactive-bg-hover,#0000000d);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;flex-direction:column;align-items:flex-start;gap:2px;padding:5px 8px;font-size:12px;display:flex}._5KEEsG_pendingOptionDesc{color:var(--dsw-alias-label-dimmed);font-size:11px}._5KEEsG_pendingCustom{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);font-size:12px;font:inherit;background:0 0;border-radius:6px;padding:5px 8px}._5KEEsG_pendingCustom:focus{border-color:var(--dsw-alias-state-business-primary);outline:none}._5KEEsG_pendingActions{justify-content:flex-end;gap:8px;margin-top:2px;display:flex}._5KEEsG_pendingBtnGhost{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border-radius:6px;padding:4px 12px;font-size:12px}._5KEEsG_pendingBtnPrimary{background:var(--dsw-alias-button-info-fill);color:#fff;cursor:pointer;border:none;border-radius:6px;padding:4px 12px;font-size:12px}._5KEEsG_pendingBtnGhost:disabled,._5KEEsG_pendingBtnPrimary:disabled{opacity:.5;cursor:default}._5KEEsG_pendingBar{border:1px solid var(--dsw-alias-state-warning-primary,#d4a017);background:var(--dsw-alias-bg-layer-2);width:calc(100% - 16px);color:var(--dsw-alias-state-warning-primary,#d4a017);text-align:left;cursor:pointer;border-radius:8px;margin:0 8px 6px;padding:6px 10px;font-size:12px;display:block}._5KEEsG_pendingBar:hover{background:var(--dsw-alias-bg-layer-3)}._5KEEsG_writeNotice{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);border-radius:8px;align-items:center;gap:8px;margin:0 8px 6px;padding:6px 10px;font-size:12px;display:flex}._5KEEsG_writeNoticeClose{color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;margin-left:auto;padding:0 4px;font-size:13px}._5KEEsG_parentStrip{border:none;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;color:var(--dsw-alias-label-secondary);cursor:pointer;text-align:left;align-items:center;gap:6px;padding:5px 12px;font-size:11px;display:flex}._5KEEsG_parentStrip:hover{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary)}._5KEEsG_dotRunning,._5KEEsG_dotPending,._5KEEsG_dotIdle{border-radius:999px;flex:none;width:7px;height:7px}._5KEEsG_dotRunning{background:var(--dsw-alias-state-business-primary,#2f88ff);animation:1.2s ease-in-out infinite _5KEEsG_sidechat-pulse}._5KEEsG_dotPending{background:var(--dsw-alias-state-warning-primary,#d4a017)}._5KEEsG_dotIdle{background:var(--dsw-alias-label-dimmed)}@keyframes _5KEEsG_sidechat-pulse{50%{opacity:.45}}._5KEEsG_actionGroup{gap:2px;margin-right:auto;display:inline-flex}";
1394
1688
  const tagId$1 = "dsh-sidenote/sidechat.module.css";
1395
1689
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
1396
1690
  const tag = document.createElement("style");
@@ -1400,70 +1694,92 @@ window.__ModuleLoader__.load({
1400
1694
  document.head.appendChild(tag);
1401
1695
  }
1402
1696
  var sidechat_module_css_default = {
1403
- "toolBodyText": "_5KEEsG_toolBodyText",
1404
- "reasoningBody": "_5KEEsG_reasoningBody",
1405
- "root": "_5KEEsG_root",
1406
- "composerFoot": "_5KEEsG_composerFoot",
1697
+ "composerCard": "_5KEEsG_composerCard",
1698
+ "streamingHint": "_5KEEsG_streamingHint",
1699
+ "stopButton": "_5KEEsG_stopButton",
1700
+ "transcript": "_5KEEsG_transcript",
1701
+ "rowPreview": "_5KEEsG_rowPreview",
1702
+ "toolGenericBody": "_5KEEsG_toolGenericBody",
1407
1703
  "densityRow": "_5KEEsG_densityRow",
1408
- "headerSide": "_5KEEsG_headerSide",
1409
- "assistantRow": "_5KEEsG_assistantRow",
1410
- "dotPending": "_5KEEsG_dotPending",
1704
+ "pendingReason": "_5KEEsG_pendingReason",
1705
+ "toolError": "_5KEEsG_toolError",
1706
+ "pendingOptionDesc": "_5KEEsG_pendingOptionDesc",
1707
+ "empty": "_5KEEsG_empty",
1411
1708
  "stateScreen": "_5KEEsG_stateScreen",
1412
- "toolHead": "_5KEEsG_toolHead",
1413
- "transcript": "_5KEEsG_transcript",
1709
+ "slashMenu": "_5KEEsG_slashMenu",
1710
+ "userBubble": "_5KEEsG_userBubble",
1711
+ "dotPending": "_5KEEsG_dotPending",
1712
+ "pendingOptionActive": "_5KEEsG_pendingOptionActive",
1713
+ "pendingDesc": "_5KEEsG_pendingDesc",
1714
+ "pendingActions": "_5KEEsG_pendingActions",
1715
+ "todoDot": "_5KEEsG_todoDot",
1716
+ "todoText": "_5KEEsG_todoText",
1414
1717
  "emptyIcon": "_5KEEsG_emptyIcon",
1718
+ "reflowButton": "_5KEEsG_reflowButton",
1719
+ "pendingBtnPrimary": "_5KEEsG_pendingBtnPrimary",
1720
+ "reasoningBody": "_5KEEsG_reasoningBody",
1721
+ "inheritedBody": "_5KEEsG_inheritedBody",
1415
1722
  "attachRail": "_5KEEsG_attachRail",
1416
- "streamingHint": "_5KEEsG_streamingHint",
1417
- "slashItemName": "_5KEEsG_slashItemName",
1418
- "dotRunning": "_5KEEsG_dotRunning",
1419
- "modelLabel": "_5KEEsG_modelLabel",
1420
- "toolLocations": "_5KEEsG_toolLocations",
1421
- "writeNoticeClose": "_5KEEsG_writeNoticeClose",
1723
+ "pendingBtnGhost": "_5KEEsG_pendingBtnGhost",
1422
1724
  "dotIdle": "_5KEEsG_dotIdle",
1423
- "emptyTitle": "_5KEEsG_emptyTitle",
1424
- "toolRunning": "_5KEEsG_toolRunning",
1725
+ "slashItemName": "_5KEEsG_slashItemName",
1425
1726
  "sendButton": "_5KEEsG_sendButton",
1426
- "sentChip": "_5KEEsG_sentChip",
1427
- "attachThumb": "_5KEEsG_attachThumb",
1428
- "assistantBody": "_5KEEsG_assistantBody",
1429
- "slashMenu": "_5KEEsG_slashMenu",
1727
+ "slashItemActive": "_5KEEsG_slashItemActive",
1430
1728
  "stateDetail": "_5KEEsG_stateDetail",
1431
- "toolError": "_5KEEsG_toolError",
1432
- "rowActions": "_5KEEsG_rowActions",
1433
- "userBubble": "_5KEEsG_userBubble",
1434
- "slashGroupTitle": "_5KEEsG_slashGroupTitle",
1435
- "actionGroup": "_5KEEsG_actionGroup",
1729
+ "emptyTitle": "_5KEEsG_emptyTitle",
1730
+ "pendingCustom": "_5KEEsG_pendingCustom",
1731
+ "dotRunning": "_5KEEsG_dotRunning",
1732
+ "densityToggle": "_5KEEsG_densityToggle",
1733
+ "pendingOption": "_5KEEsG_pendingOption",
1734
+ "modelLabel": "_5KEEsG_modelLabel",
1735
+ "body": "_5KEEsG_body",
1736
+ "toolHead": "_5KEEsG_toolHead",
1737
+ "reasoning": "_5KEEsG_reasoning",
1738
+ "pendingQHeader": "_5KEEsG_pendingQHeader",
1739
+ "writeNotice": "_5KEEsG_writeNotice",
1436
1740
  "input": "_5KEEsG_input",
1437
- "empty": "_5KEEsG_empty",
1438
- "pendingBar": "_5KEEsG_pendingBar",
1439
- "toolBody": "_5KEEsG_toolBody",
1741
+ "sentChipRow": "_5KEEsG_sentChipRow",
1742
+ "todoItem": "_5KEEsG_todoItem",
1743
+ "composer": "_5KEEsG_composer",
1744
+ "slashItem": "_5KEEsG_slashItem",
1745
+ "flowRow": "_5KEEsG_flowRow",
1746
+ "pendingHead": "_5KEEsG_pendingHead",
1747
+ "composerChips": "_5KEEsG_composerChips",
1440
1748
  "errorRow": "_5KEEsG_errorRow",
1441
- "noticeRow": "_5KEEsG_noticeRow",
1442
- "parentStrip": "_5KEEsG_parentStrip",
1443
- "toolGenericBody": "_5KEEsG_toolGenericBody",
1444
1749
  "slashItemDetail": "_5KEEsG_slashItemDetail",
1445
- "slashItem": "_5KEEsG_slashItem",
1446
- "reflowButton": "_5KEEsG_reflowButton",
1447
- "reasoning": "_5KEEsG_reasoning",
1448
- "attachButton": "_5KEEsG_attachButton",
1449
- "body": "_5KEEsG_body",
1750
+ "toolRunning": "_5KEEsG_toolRunning",
1751
+ "noticeRow": "_5KEEsG_noticeRow",
1752
+ "pendingTitle": "_5KEEsG_pendingTitle",
1753
+ "jumpBottom": "_5KEEsG_jumpBottom",
1754
+ "flowRowError": "_5KEEsG_flowRowError",
1450
1755
  "sidechat-pulse": "_5KEEsG_sidechat-pulse",
1451
- "writeNotice": "_5KEEsG_writeNotice",
1756
+ "root": "_5KEEsG_root",
1757
+ "emptyText": "_5KEEsG_emptyText",
1758
+ "slashGroupTitle": "_5KEEsG_slashGroupTitle",
1452
1759
  "toolCard": "_5KEEsG_toolCard",
1453
- "sentChipRow": "_5KEEsG_sentChipRow",
1454
- "densityToggle": "_5KEEsG_densityToggle",
1455
- "stopButton": "_5KEEsG_stopButton",
1456
- "attachRemove": "_5KEEsG_attachRemove",
1760
+ "assistantBody": "_5KEEsG_assistantBody",
1761
+ "composerFoot": "_5KEEsG_composerFoot",
1762
+ "attachButton": "_5KEEsG_attachButton",
1457
1763
  "userRow": "_5KEEsG_userRow",
1458
- "composerChips": "_5KEEsG_composerChips",
1459
- "jumpBottom": "_5KEEsG_jumpBottom",
1460
- "slashItemActive": "_5KEEsG_slashItemActive",
1461
- "composer": "_5KEEsG_composer",
1462
- "toolCardV2": "_5KEEsG_toolCardV2",
1463
- "modelSelect": "_5KEEsG_modelSelect",
1764
+ "todoBody": "_5KEEsG_todoBody",
1765
+ "toolBodyText": "_5KEEsG_toolBodyText",
1766
+ "pendingCard": "_5KEEsG_pendingCard",
1767
+ "parentStrip": "_5KEEsG_parentStrip",
1768
+ "toolLocations": "_5KEEsG_toolLocations",
1769
+ "actionGroup": "_5KEEsG_actionGroup",
1770
+ "headerSide": "_5KEEsG_headerSide",
1464
1771
  "attachImg": "_5KEEsG_attachImg",
1465
- "emptyText": "_5KEEsG_emptyText",
1466
- "inheritedBody": "_5KEEsG_inheritedBody"
1772
+ "attachRemove": "_5KEEsG_attachRemove",
1773
+ "assistantRow": "_5KEEsG_assistantRow",
1774
+ "pendingQuestion": "_5KEEsG_pendingQuestion",
1775
+ "writeNoticeClose": "_5KEEsG_writeNoticeClose",
1776
+ "toolBody": "_5KEEsG_toolBody",
1777
+ "sentChip": "_5KEEsG_sentChip",
1778
+ "pendingBar": "_5KEEsG_pendingBar",
1779
+ "rowActions": "_5KEEsG_rowActions",
1780
+ "modelSelect": "_5KEEsG_modelSelect",
1781
+ "attachThumb": "_5KEEsG_attachThumb",
1782
+ "pendingQText": "_5KEEsG_pendingQText"
1467
1783
  };
1468
1784
  //#endregion
1469
1785
  //#region src/client/chat/ToolCard.tsx
@@ -1495,15 +1811,17 @@ window.__ModuleLoader__.load({
1495
1811
  function ToolCard(props) {
1496
1812
  const { model, rowKey, fold } = props;
1497
1813
  const open = useFoldOpen(fold, rowKey);
1498
- const icon = model.kind === "terminal" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlayOutline16, { size: 14 }) : model.kind === "diff" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 14 }) : model.kind === "read" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCodeOutline16, { size: 14 }) : model.kind === "generic" ? kindIcon(model.icon) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCordisPluginOutline14, { size: 14 });
1814
+ const icon = model.kind === "terminal" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlayOutline16, { size: 14 }) : model.kind === "diff" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 14 }) : model.kind === "read" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCodeOutline16, { size: 14 }) : model.kind === "todo" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChecklistOutline14, { size: 14 }) : model.kind === "generic" ? kindIcon(model.icon) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCordisPluginOutline14, { size: 14 });
1499
1815
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1500
- className: sidechat_module_css_default.toolCardV2,
1816
+ className: sidechat_module_css_default.flowRow,
1501
1817
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, {
1502
1818
  icon,
1503
1819
  title: model.title,
1504
1820
  open,
1505
1821
  expandable: true,
1506
1822
  expandOnRowClick: true,
1823
+ previewChevron: true,
1824
+ ...props.error === true ? { titleClassName: sidechat_module_css_default.flowRowError } : {},
1507
1825
  onToggle: () => {
1508
1826
  fold.toggle(rowKey);
1509
1827
  },
@@ -1517,7 +1835,7 @@ window.__ModuleLoader__.load({
1517
1835
  function ToolCardBody({ model, streaming }) {
1518
1836
  switch (model.kind) {
1519
1837
  case "terminal": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TerminalBlock, {
1520
- command: model.title,
1838
+ command: model.command,
1521
1839
  labels: terminalLabels(),
1522
1840
  ...model.cwd !== void 0 ? { cwd: model.cwd } : {},
1523
1841
  ...model.output !== void 0 ? { output: model.output } : {},
@@ -1525,6 +1843,20 @@ window.__ModuleLoader__.load({
1525
1843
  ...model.signal !== void 0 ? { signal: model.signal } : {},
1526
1844
  ...streaming === true ? { running: true } : {}
1527
1845
  });
1846
+ case "todo": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1847
+ className: sidechat_module_css_default.todoBody,
1848
+ children: model.items.map((item, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1849
+ className: sidechat_module_css_default.todoItem,
1850
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1851
+ className: sidechat_module_css_default.todoDot,
1852
+ "data-status": item.status
1853
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1854
+ className: sidechat_module_css_default.todoText,
1855
+ "data-status": item.status,
1856
+ children: item.content
1857
+ })]
1858
+ }, i))
1859
+ });
1528
1860
  case "diff": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DiffBlock, {
1529
1861
  labels: diffLabels(),
1530
1862
  diffs: model.diffs.map((d) => ({
@@ -1606,18 +1938,32 @@ window.__ModuleLoader__.load({
1606
1938
  * (用户在看推理),完成后默认折叠;用户显式收起过就不再自动展开
1607
1939
  * (fold.has 区分「未触碰」与「显式收起」)。
1608
1940
  */
1941
+ /** 首行非空文本(折叠态预览——主区「Think · 首行摘要」同款)。 */
1942
+ function firstLineOf(text) {
1943
+ for (const line of text.split("\n")) {
1944
+ const s = line.trim();
1945
+ if (s !== "") return s;
1946
+ }
1947
+ return "";
1948
+ }
1609
1949
  function ReasoningRow(props) {
1610
1950
  const { rowKey, fold } = props;
1611
1951
  const touched = (0, react.useSyncExternalStore)((fn) => fold.subscribe(fn), () => fold.has(rowKey));
1612
1952
  const open = (0, react.useSyncExternalStore)((fn) => fold.subscribe(fn), () => fold.isOpen(rowKey)) || props.streaming === true && !touched;
1953
+ const preview = firstLineOf(props.text);
1613
1954
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1614
- className: sidechat_module_css_default.toolCardV2,
1955
+ className: sidechat_module_css_default.flowRow,
1615
1956
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, {
1616
1957
  icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconThinkOutline16, { size: 14 }),
1617
1958
  title: t("thinking"),
1618
1959
  open,
1619
1960
  expandable: true,
1620
1961
  expandOnRowClick: true,
1962
+ previewChevron: true,
1963
+ ...preview !== "" ? { collapsedContent: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1964
+ className: sidechat_module_css_default.rowPreview,
1965
+ children: preview
1966
+ }) } : {},
1621
1967
  onToggle: () => {
1622
1968
  fold.toggle(rowKey);
1623
1969
  },
@@ -1642,13 +1988,14 @@ window.__ModuleLoader__.load({
1642
1988
  const { rowKey, fold } = props;
1643
1989
  const open = (0, react.useSyncExternalStore)((fn) => fold.subscribe(fn), () => fold.isOpen(rowKey));
1644
1990
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1645
- className: sidechat_module_css_default.toolCardV2,
1991
+ className: sidechat_module_css_default.flowRow,
1646
1992
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, {
1647
1993
  icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, { size: 14 }),
1648
1994
  title: t("inheritedLabel", { n: props.count }),
1649
1995
  open,
1650
1996
  expandable: true,
1651
1997
  expandOnRowClick: true,
1998
+ previewChevron: true,
1652
1999
  onToggle: () => {
1653
2000
  fold.toggle(rowKey);
1654
2001
  },
@@ -1887,7 +2234,8 @@ window.__ModuleLoader__.load({
1887
2234
  model: message.card,
1888
2235
  rowKey: message.key,
1889
2236
  fold,
1890
- ...message.streaming === true ? { streaming: true } : {}
2237
+ ...message.streaming === true ? { streaming: true } : {},
2238
+ ...message.isError === true ? { error: true } : {}
1891
2239
  });
1892
2240
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1893
2241
  className: sidechat_module_css_default.toolCard,
@@ -2204,14 +2552,14 @@ window.__ModuleLoader__.load({
2204
2552
  side: "top",
2205
2553
  portal: true,
2206
2554
  compact: true,
2207
- anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2555
+ anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2208
2556
  type: "button",
2209
2557
  className: sidechat_module_css_default.modelSelect,
2210
2558
  title: t("modelSwitchTitle"),
2211
2559
  onClick: () => {
2212
2560
  open ? setOpen(false) : openMenu();
2213
2561
  },
2214
- children: t("modelLabel", { name: props.modelName ?? t("modelFollowsMain") })
2562
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("modelLabel", { name: props.modelName ?? t("modelFollowsMain") }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 12 })]
2215
2563
  }),
2216
2564
  items,
2217
2565
  onClose: () => {
@@ -2268,14 +2616,18 @@ window.__ModuleLoader__.load({
2268
2616
  side: "top",
2269
2617
  portal: true,
2270
2618
  compact: true,
2271
- anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2619
+ anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2272
2620
  type: "button",
2273
2621
  className: sidechat_module_css_default.modelSelect,
2274
2622
  title: t("permChipTitle"),
2275
2623
  onClick: () => {
2276
2624
  setOpen(!open);
2277
2625
  },
2278
- children: presetLabel(effective)
2626
+ children: [
2627
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconInspectOutline12, { size: 12 }),
2628
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: presetLabel(effective) }),
2629
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 12 })
2630
+ ]
2279
2631
  }),
2280
2632
  items,
2281
2633
  onClose: () => {
@@ -2416,110 +2768,110 @@ window.__ModuleLoader__.load({
2416
2768
  triggerCtl.pick(source, index);
2417
2769
  }
2418
2770
  }),
2419
- composer.imagePreviews.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2420
- className: sidechat_module_css_default.attachRail,
2421
- children: composer.imagePreviews.map((img) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2422
- className: sidechat_module_css_default.attachThumb,
2423
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
2424
- src: img.url,
2425
- alt: "",
2426
- className: sidechat_module_css_default.attachImg
2427
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2428
- type: "button",
2429
- className: sidechat_module_css_default.attachRemove,
2430
- "aria-label": t("attachRemove"),
2431
- onClick: () => {
2432
- composer.removeImage(img.id);
2433
- },
2434
- children: "×"
2435
- })]
2436
- }, img.id))
2437
- }),
2438
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
2439
- ref: inputRef,
2440
- className: sidechat_module_css_default.input,
2441
- placeholder: t("inputPlaceholder"),
2442
- value: composer.draft,
2443
- onChange: (event) => {
2444
- composer.setDraft(event.target.value);
2445
- triggerCtl?.track(event.target.value, event.target.selectionStart ?? event.target.value.length, guardOf(composer.phase), composer.draftRev ?? 0);
2446
- },
2447
- onKeyDown: (event) => {
2448
- if (menuOpen && triggerCtl !== null) {
2449
- const arbKey = arbitrateKeyOf(event.key);
2450
- if (arbKey !== null) {
2451
- if (triggerCtl.arbitrate(arbKey, event.nativeEvent.isComposing) !== "pass") {
2452
- event.preventDefault();
2453
- return;
2454
- }
2455
- }
2456
- }
2457
- if (event.key !== "Enter" || event.shiftKey) return;
2458
- if (event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) return;
2459
- event.preventDefault();
2460
- composer.submit(event.metaKey || event.ctrlKey ? "steer" : void 0);
2461
- }
2462
- }),
2463
2771
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2464
- className: sidechat_module_css_default.composerFoot,
2772
+ className: sidechat_module_css_default.composerCard,
2465
2773
  children: [
2466
- composer.canAttach && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2467
- type: "button",
2468
- className: sidechat_module_css_default.attachButton,
2469
- title: t("attachTitle"),
2470
- "aria-label": t("attachTitle"),
2471
- onClick: () => {
2472
- fileInputRef.current?.click();
2473
- },
2474
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPaperclipOutline16, { size: 14 })
2774
+ composer.imagePreviews.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2775
+ className: sidechat_module_css_default.attachRail,
2776
+ children: composer.imagePreviews.map((img) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2777
+ className: sidechat_module_css_default.attachThumb,
2778
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
2779
+ src: img.url,
2780
+ alt: "",
2781
+ className: sidechat_module_css_default.attachImg
2782
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2783
+ type: "button",
2784
+ className: sidechat_module_css_default.attachRemove,
2785
+ "aria-label": t("attachRemove"),
2786
+ onClick: () => {
2787
+ composer.removeImage(img.id);
2788
+ },
2789
+ children: "×"
2790
+ })]
2791
+ }, img.id))
2475
2792
  }),
2476
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2477
- ref: fileInputRef,
2478
- type: "file",
2479
- accept: "image/*",
2480
- multiple: true,
2481
- hidden: true,
2793
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
2794
+ ref: inputRef,
2795
+ className: sidechat_module_css_default.input,
2796
+ placeholder: t("inputPlaceholder"),
2797
+ value: composer.draft,
2482
2798
  onChange: (event) => {
2483
- const files = [...event.target.files ?? []];
2484
- if (files.length > 0) composer.attachImages(files);
2485
- event.target.value = "";
2799
+ composer.setDraft(event.target.value);
2800
+ triggerCtl?.track(event.target.value, event.target.selectionStart ?? event.target.value.length, guardOf(composer.phase), composer.draftRev ?? 0);
2801
+ },
2802
+ onKeyDown: (event) => {
2803
+ if (menuOpen && triggerCtl !== null) {
2804
+ const arbKey = arbitrateKeyOf(event.key);
2805
+ if (arbKey !== null) {
2806
+ if (triggerCtl.arbitrate(arbKey, event.nativeEvent.isComposing) !== "pass") {
2807
+ event.preventDefault();
2808
+ return;
2809
+ }
2810
+ }
2811
+ }
2812
+ if (event.key !== "Enter" || event.shiftKey) return;
2813
+ if (event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) return;
2814
+ event.preventDefault();
2815
+ composer.submit(event.metaKey || event.ctrlKey ? "steer" : void 0);
2486
2816
  }
2487
2817
  }),
2488
- childIdForMenu !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2489
- className: sidechat_module_css_default.composerChips,
2490
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PermissionChip, { session }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
2491
- ctx: props.ctx,
2492
- sessionId: childIdForMenu,
2493
- modelName: props.modelName,
2494
- onSwitched: props.onModelSwitched
2495
- })]
2496
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2497
- className: sidechat_module_css_default.modelLabel,
2498
- children: t("modelLabel", { name: props.modelName ?? t("modelFollowsMain") })
2499
- }),
2500
- running ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2501
- type: "button",
2502
- className: sidechat_module_css_default.stopButton,
2503
- title: t("stopReplyTitle"),
2504
- onClick: () => {
2505
- session?.cancel().catch(() => {});
2506
- },
2818
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2819
+ className: sidechat_module_css_default.composerFoot,
2507
2820
  children: [
2508
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconStopFill16, { size: 14 }),
2509
- " ",
2510
- t("stopReply")
2511
- ]
2512
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2513
- type: "button",
2514
- className: sidechat_module_css_default.sendButton,
2515
- disabled: composer.draft.trim() === "",
2516
- onClick: () => {
2517
- composer.submit();
2518
- },
2519
- children: [
2520
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSendOutline16, { size: 14 }),
2521
- " ",
2522
- t("send")
2821
+ composer.canAttach && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2822
+ type: "button",
2823
+ className: sidechat_module_css_default.attachButton,
2824
+ title: t("attachTitle"),
2825
+ "aria-label": t("attachTitle"),
2826
+ onClick: () => {
2827
+ fileInputRef.current?.click();
2828
+ },
2829
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPaperclipOutline16, { size: 14 })
2830
+ }),
2831
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2832
+ ref: fileInputRef,
2833
+ type: "file",
2834
+ accept: "image/*",
2835
+ multiple: true,
2836
+ hidden: true,
2837
+ onChange: (event) => {
2838
+ const files = [...event.target.files ?? []];
2839
+ if (files.length > 0) composer.attachImages(files);
2840
+ event.target.value = "";
2841
+ }
2842
+ }),
2843
+ childIdForMenu !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2844
+ className: sidechat_module_css_default.composerChips,
2845
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PermissionChip, { session }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
2846
+ ctx: props.ctx,
2847
+ sessionId: childIdForMenu,
2848
+ modelName: props.modelName,
2849
+ onSwitched: props.onModelSwitched
2850
+ })]
2851
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2852
+ className: sidechat_module_css_default.modelLabel,
2853
+ children: t("modelLabel", { name: props.modelName ?? t("modelFollowsMain") })
2854
+ }),
2855
+ running ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2856
+ type: "button",
2857
+ className: sidechat_module_css_default.stopButton,
2858
+ title: t("stopReplyTitle"),
2859
+ "aria-label": t("stopReplyTitle"),
2860
+ onClick: () => {
2861
+ session?.cancel().catch(() => {});
2862
+ },
2863
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconStopFill16, { size: 12 })
2864
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2865
+ type: "button",
2866
+ className: sidechat_module_css_default.sendButton,
2867
+ title: t("send"),
2868
+ "aria-label": t("send"),
2869
+ disabled: composer.draft.trim() === "",
2870
+ onClick: () => {
2871
+ composer.submit();
2872
+ },
2873
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSendOutline16, { size: 14 })
2874
+ })
2523
2875
  ]
2524
2876
  })
2525
2877
  ]
@@ -2581,6 +2933,394 @@ window.__ModuleLoader__.load({
2581
2933
  };
2582
2934
  }
2583
2935
  //#endregion
2936
+ //#region src/client/sidechat/toast.tsx
2937
+ /**
2938
+ * 全局瞬态通知(P4 确认反馈):body 级独立 React 根,寿命不随任何 Tab——
2939
+ * 「保存为正式会话」后主视图跳转 + 侧聊 Tab 关闭,面板自己的组件树已卸载,
2940
+ * toast 必须挂在更长寿的宿主上(annotate overlay 同款 ctx.effect 模式:
2941
+ * HMR/插件禁用时 fiber 撤销即清理)。
2942
+ */
2943
+ let current = null;
2944
+ const listeners = /* @__PURE__ */ new Set();
2945
+ function emit() {
2946
+ for (const fn of listeners) fn();
2947
+ }
2948
+ /** 全局瞬态通知入口(任何模块可调;未挂载宿主时静默丢弃)。 */
2949
+ function showToast(text) {
2950
+ current = {
2951
+ seq: (current?.seq ?? 0) + 1,
2952
+ text
2953
+ };
2954
+ emit();
2955
+ }
2956
+ function subscribe(fn) {
2957
+ listeners.add(fn);
2958
+ return () => {
2959
+ listeners.delete(fn);
2960
+ };
2961
+ }
2962
+ function ToastHost() {
2963
+ const snap = (0, react.useSyncExternalStore)(subscribe, () => current);
2964
+ if (snap === null) return null;
2965
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
2966
+ text: snap.text,
2967
+ onDone: () => {
2968
+ current = null;
2969
+ emit();
2970
+ }
2971
+ }, snap.seq);
2972
+ }
2973
+ /** 挂载全局 toast 宿主(registerSideChat 里调用一次)。 */
2974
+ function registerToastHost(ctx) {
2975
+ ctx.effect(() => {
2976
+ const host = document.createElement("div");
2977
+ host.dataset.dshSidenoteToast = "";
2978
+ document.body.appendChild(host);
2979
+ const root = (0, react_dom_client.createRoot)(host);
2980
+ root.render(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToastHost, {}));
2981
+ return () => {
2982
+ root.unmount();
2983
+ host.remove();
2984
+ };
2985
+ }, "dsh-sidenote: toast host");
2986
+ }
2987
+ //#endregion
2988
+ //#region src/client/sidechat/pending.ts
2989
+ function adapt012(pending) {
2990
+ if (typeof pending !== "object" || pending === null) return [];
2991
+ const o = pending;
2992
+ if (o.kind === "approval" && typeof o.answer === "function") return [{
2993
+ kind: "approval",
2994
+ key: typeof o.key === "string" ? o.key : "approval",
2995
+ toolName: typeof o.toolName === "string" ? o.toolName : "",
2996
+ ...typeof o.reason === "string" ? { reason: o.reason } : {},
2997
+ answer: (outcome) => o.answer.call(o, outcome)
2998
+ }];
2999
+ if ((o.kind === "question" || o.kind === "plan-review") && typeof o.answer === "function" && typeof o.cancel === "function") {
3000
+ const questions = normalizeQuestions(o.questions ?? o.payload?.questions);
3001
+ if (questions === void 0) return [];
3002
+ return [{
3003
+ kind: "question",
3004
+ key: typeof o.key === "string" ? o.key : "question",
3005
+ planReview: o.kind === "plan-review",
3006
+ questions,
3007
+ answer: (answers) => o.answer.call(o, { answers }),
3008
+ cancel: () => o.cancel.call(o)
3009
+ }];
3010
+ }
3011
+ return [];
3012
+ }
3013
+ function adapt011(pending) {
3014
+ const items = [];
3015
+ for (const raw of pending) {
3016
+ const w = raw;
3017
+ if (typeof w.respond !== "function") continue;
3018
+ const respond = w.respond;
3019
+ const sessionId = typeof w.sessionId === "string" ? w.sessionId : void 0;
3020
+ if (sessionId === void 0) continue;
3021
+ const payload = w.payload ?? {};
3022
+ const key = typeof w.key === "string" ? w.key : `pending:${items.length}`;
3023
+ if (w.kind === "approval") {
3024
+ const approvalId = payload.approvalId;
3025
+ if (typeof approvalId !== "string") continue;
3026
+ items.push({
3027
+ kind: "approval",
3028
+ key,
3029
+ toolName: typeof payload.toolName === "string" ? payload.toolName : "",
3030
+ ...typeof payload.reason === "string" ? { reason: payload.reason } : {},
3031
+ answer: async (outcome) => {
3032
+ if ((await respond({
3033
+ ok: true,
3034
+ value: {
3035
+ sessionId,
3036
+ approvalId,
3037
+ outcome
3038
+ }
3039
+ })).accepted === false) throw new Error("approval response rejected (not pending)");
3040
+ }
3041
+ });
3042
+ } else if (w.kind === "question") {
3043
+ const questions = normalizeQuestions(payload.questions);
3044
+ if (questions === void 0) continue;
3045
+ items.push({
3046
+ kind: "question",
3047
+ key,
3048
+ planReview: false,
3049
+ questions,
3050
+ answer: async (answers) => {
3051
+ if ((await respond({
3052
+ ok: true,
3053
+ value: {
3054
+ sessionId,
3055
+ answer: { answers }
3056
+ }
3057
+ })).accepted === false) throw new Error("question response rejected (not pending)");
3058
+ },
3059
+ cancel: async () => {
3060
+ await respond({
3061
+ ok: false,
3062
+ error: {
3063
+ code: "cancelled",
3064
+ message: "the user closed this question request",
3065
+ details: {}
3066
+ }
3067
+ });
3068
+ }
3069
+ });
3070
+ }
3071
+ }
3072
+ return items;
3073
+ }
3074
+ /** questions 字段归一(选项 {label,description?} 与纯字符串两形都收)。 */
3075
+ function normalizeQuestions(raw) {
3076
+ if (!Array.isArray(raw)) return void 0;
3077
+ const out = [];
3078
+ for (const rawQ of raw) {
3079
+ if (typeof rawQ !== "object" || rawQ === null) return void 0;
3080
+ const q = rawQ;
3081
+ if (typeof q.question !== "string") return void 0;
3082
+ const options = [];
3083
+ if (Array.isArray(q.options)) for (const rawO of q.options) if (typeof rawO === "string") options.push({ label: rawO });
3084
+ else if (typeof rawO === "object" && rawO !== null && typeof rawO.label === "string") {
3085
+ const oo = rawO;
3086
+ options.push({
3087
+ label: oo.label,
3088
+ ...typeof oo.description === "string" ? { description: oo.description } : {}
3089
+ });
3090
+ } else return void 0;
3091
+ out.push({
3092
+ id: typeof q.id === "string" ? q.id : `q${out.length}`,
3093
+ question: q.question,
3094
+ ...typeof q.header === "string" ? { header: q.header } : {},
3095
+ ...typeof q.detail === "string" ? { detail: q.detail } : {},
3096
+ options,
3097
+ multiSelect: q.multiSelect === true
3098
+ });
3099
+ }
3100
+ return out;
3101
+ }
3102
+ /**
3103
+ * 双版本探测链(chatSourceOf 同款纪律):0.1.2 uiSession.pendingInteractions
3104
+ * (getSnapshot() 是 Map 才认)优先;0.1.1 Session 快照 pending 数组回退;
3105
+ * 双缺席 undefined(面板回退 pendingBar 跳转)。
3106
+ */
3107
+ function createPendingStore(ctx, session, childId) {
3108
+ try {
3109
+ const pim = (typeof ctx.get === "function" ? ctx.get("uiSession") : void 0)?.pendingInteractions;
3110
+ if (pim !== void 0 && typeof pim.subscribe === "function" && typeof pim.getSnapshot === "function") {
3111
+ const obs = pim;
3112
+ if (obs.getSnapshot() instanceof Map) {
3113
+ let cachedMap = null;
3114
+ let cachedItems = [];
3115
+ return {
3116
+ available: true,
3117
+ subscribe: (fn) => obs.subscribe(fn),
3118
+ getSnapshot: () => {
3119
+ const map = obs.getSnapshot();
3120
+ if (map === cachedMap) return cachedItems;
3121
+ cachedMap = map;
3122
+ cachedItems = map instanceof Map ? adapt012(map.get(childId)) : [];
3123
+ return cachedItems;
3124
+ }
3125
+ };
3126
+ }
3127
+ }
3128
+ } catch {}
3129
+ if (session === void 0) return void 0;
3130
+ try {
3131
+ const snap = session.getSnapshot();
3132
+ if (Array.isArray(snap?.pending)) {
3133
+ let cachedArr = null;
3134
+ let cachedItems = [];
3135
+ return {
3136
+ available: true,
3137
+ subscribe: (fn) => session.subscribe(fn),
3138
+ getSnapshot: () => {
3139
+ const arr = session.getSnapshot()?.pending;
3140
+ if (arr === cachedArr) return cachedItems;
3141
+ cachedArr = arr;
3142
+ cachedItems = Array.isArray(arr) ? adapt011(arr) : [];
3143
+ return cachedItems;
3144
+ }
3145
+ };
3146
+ }
3147
+ } catch {}
3148
+ }
3149
+ //#endregion
3150
+ //#region src/client/sidechat/PendingCard.tsx
3151
+ /**
3152
+ * R8 审批/提问答复卡(L2 视图):钉在 composer 上方(原 pendingBar 位置)。
3153
+ * 自绘 + primitives 图标(材质同源,不求像素级复刻宿主审批卡)。
3154
+ *
3155
+ * 竞答纪律:先答先得——点击后置 answered 本地态;answer 失败(已被主视图
3156
+ * 抢答/settle)catch 吞错并回退按钮态(宿主 `.catch(() => setAnswered(false))`
3157
+ * 同款);卡片消失由快照驱动(pending 项消失即卸载),UI 不自造状态机。
3158
+ */
3159
+ function PendingCardList({ items }) {
3160
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: items.map((item) => item.kind === "approval" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ApprovalCard, { item }, item.key) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuestionCard, { item }, item.key)) });
3161
+ }
3162
+ /** 审批卡:reason + 拒绝 / 允许一次。 */
3163
+ function ApprovalCard({ item }) {
3164
+ useLocaleTick();
3165
+ const [answered, setAnswered] = (0, react.useState)(false);
3166
+ const answer = (outcome) => {
3167
+ setAnswered(true);
3168
+ item.answer(outcome).catch(() => {
3169
+ setAnswered(false);
3170
+ });
3171
+ };
3172
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3173
+ className: sidechat_module_css_default.pendingCard,
3174
+ "data-kind": "approval",
3175
+ children: [
3176
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3177
+ className: sidechat_module_css_default.pendingHead,
3178
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, { size: 14 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3179
+ className: sidechat_module_css_default.pendingTitle,
3180
+ children: t("pendingApprovalTitle")
3181
+ })]
3182
+ }),
3183
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3184
+ className: sidechat_module_css_default.pendingDesc,
3185
+ children: t("pendingApprovalDesc", { name: item.toolName })
3186
+ }),
3187
+ item.reason !== void 0 && item.reason !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3188
+ className: sidechat_module_css_default.pendingReason,
3189
+ children: item.reason
3190
+ }),
3191
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3192
+ className: sidechat_module_css_default.pendingActions,
3193
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3194
+ type: "button",
3195
+ className: sidechat_module_css_default.pendingBtnGhost,
3196
+ disabled: answered,
3197
+ onClick: () => {
3198
+ answer("rejected");
3199
+ },
3200
+ children: t("pendingReject")
3201
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3202
+ type: "button",
3203
+ className: sidechat_module_css_default.pendingBtnPrimary,
3204
+ disabled: answered,
3205
+ onClick: () => {
3206
+ answer("allowed-once");
3207
+ },
3208
+ children: t("pendingAllowOnce")
3209
+ })]
3210
+ })
3211
+ ]
3212
+ });
3213
+ }
3214
+ /** 提问卡:选项(单选即点即答语义并入整批提交)+ 自定义 + 整批提交/取消。 */
3215
+ function QuestionCard({ item }) {
3216
+ useLocaleTick();
3217
+ const [selections, setSelections] = (0, react.useState)(/* @__PURE__ */ new Map());
3218
+ const [customs, setCustoms] = (0, react.useState)(/* @__PURE__ */ new Map());
3219
+ const [answered, setAnswered] = (0, react.useState)(false);
3220
+ const toggle = (qid, label, multi) => {
3221
+ const next = new Map(selections);
3222
+ const cur = new Set(next.get(qid) ?? []);
3223
+ if (multi) {
3224
+ if (cur.has(label)) cur.delete(label);
3225
+ else cur.add(label);
3226
+ } else {
3227
+ cur.clear();
3228
+ cur.add(label);
3229
+ }
3230
+ next.set(qid, cur);
3231
+ setSelections(next);
3232
+ };
3233
+ const ready = item.questions.every((q) => (selections.get(q.id)?.size ?? 0) > 0 || (customs.get(q.id)?.trim() ?? "") !== "");
3234
+ const submit = () => {
3235
+ setAnswered(true);
3236
+ item.answer(item.questions.map((q) => ({
3237
+ id: q.id,
3238
+ selected: [...selections.get(q.id) ?? []],
3239
+ ...(customs.get(q.id)?.trim() ?? "") !== "" ? { custom: customs.get(q.id).trim() } : {}
3240
+ }))).catch(() => {
3241
+ setAnswered(false);
3242
+ });
3243
+ };
3244
+ const cancel = () => {
3245
+ setAnswered(true);
3246
+ item.cancel().catch(() => {
3247
+ setAnswered(false);
3248
+ });
3249
+ };
3250
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3251
+ className: sidechat_module_css_default.pendingCard,
3252
+ "data-kind": "question",
3253
+ children: [
3254
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3255
+ className: sidechat_module_css_default.pendingHead,
3256
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQuestionOutline14, { size: 14 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3257
+ className: sidechat_module_css_default.pendingTitle,
3258
+ children: item.planReview ? t("pendingPlanReview") : t("pendingQuestionTitle")
3259
+ })]
3260
+ }),
3261
+ item.questions.map((q) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3262
+ className: sidechat_module_css_default.pendingQuestion,
3263
+ children: [
3264
+ q.header !== void 0 && q.header !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3265
+ className: sidechat_module_css_default.pendingQHeader,
3266
+ children: q.header
3267
+ }),
3268
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3269
+ className: sidechat_module_css_default.pendingQText,
3270
+ children: q.question
3271
+ }),
3272
+ q.detail !== void 0 && q.detail !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3273
+ className: sidechat_module_css_default.pendingReason,
3274
+ children: q.detail
3275
+ }),
3276
+ q.options.map((o) => {
3277
+ const active = selections.get(q.id)?.has(o.label) === true;
3278
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3279
+ type: "button",
3280
+ className: active ? sidechat_module_css_default.pendingOptionActive : sidechat_module_css_default.pendingOption,
3281
+ disabled: answered,
3282
+ onClick: () => {
3283
+ toggle(q.id, o.label, q.multiSelect);
3284
+ },
3285
+ children: [o.label, o.description !== void 0 && o.description !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3286
+ className: sidechat_module_css_default.pendingOptionDesc,
3287
+ children: o.description
3288
+ })]
3289
+ }, o.label);
3290
+ }),
3291
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3292
+ className: sidechat_module_css_default.pendingCustom,
3293
+ placeholder: t("pendingCustomPlaceholder"),
3294
+ disabled: answered,
3295
+ value: customs.get(q.id) ?? "",
3296
+ onChange: (event) => {
3297
+ const next = new Map(customs);
3298
+ next.set(q.id, event.target.value);
3299
+ setCustoms(next);
3300
+ }
3301
+ })
3302
+ ]
3303
+ }, q.id)),
3304
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3305
+ className: sidechat_module_css_default.pendingActions,
3306
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3307
+ type: "button",
3308
+ className: sidechat_module_css_default.pendingBtnGhost,
3309
+ disabled: answered,
3310
+ onClick: cancel,
3311
+ children: t("pendingQuestionCancel")
3312
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3313
+ type: "button",
3314
+ className: sidechat_module_css_default.pendingBtnPrimary,
3315
+ disabled: answered || !ready,
3316
+ onClick: submit,
3317
+ children: t("pendingQuestionSubmit")
3318
+ })]
3319
+ })
3320
+ ]
3321
+ });
3322
+ }
3323
+ //#endregion
2584
3324
  //#region src/client/sidechat/SideChatPanel.tsx
2585
3325
  /**
2586
3326
  * 侧边聊天 Tab 面板(L2 薄视图):相位编排 + 消息流 + composer。
@@ -2597,6 +3337,8 @@ window.__ModuleLoader__.load({
2597
3337
  * 否则消息流永远为空(client-runtime 只为 staged 会话开窗的已知偏差)。
2598
3338
  */
2599
3339
  const NOOP_UNSUBSCRIBE = () => {};
3340
+ /** uSES 空快照常量(禁产新引用——`?? []` 字面量每次新数组会无限重渲)。 */
3341
+ const NO_PENDING = [];
2600
3342
  /** D2 写型工具名(无 diff 卡时的名字兜底)。 */
2601
3343
  const WRITE_TOOLS = /* @__PURE__ */ new Set([
2602
3344
  "edit",
@@ -2657,6 +3399,12 @@ window.__ModuleLoader__.load({
2657
3399
  const fold = (0, react.useMemo)(() => createFoldStore(childId === void 0 ? void 0 : `dsh-sidenote:fold:v1:${childId}`), [childId]);
2658
3400
  const parentSession = meta.parentSessionId === void 0 ? void 0 : ctx.sessions.binding(meta.parentSessionId)?.session;
2659
3401
  const parentSnap = (0, react.useSyncExternalStore)((0, react.useCallback)((notify) => visible && parentSession !== void 0 ? parentSession.subscribe(notify) : NOOP_UNSUBSCRIBE, [visible, parentSession]), () => parentSession === void 0 ? null : parentSession.getSnapshot());
3402
+ const pendingStore = (0, react.useMemo)(() => childId === void 0 || session === void 0 ? void 0 : createPendingStore(ctx, session, childId), [
3403
+ ctx,
3404
+ session,
3405
+ childId
3406
+ ]);
3407
+ const pendingItems = (0, react.useSyncExternalStore)((0, react.useCallback)((notify) => visible && pendingStore !== void 0 ? pendingStore.subscribe(notify) : NOOP_UNSUBSCRIBE, [visible, pendingStore]), () => pendingStore?.getSnapshot() ?? NO_PENDING);
2660
3408
  const [writeNoticeDismissed, setWriteNoticeDismissed] = (0, react.useState)(() => typeof localStorage !== "undefined" && localStorage.getItem(WRITE_NOTICE_KEY) === "1");
2661
3409
  const composer = useComposer(ctx, session, childId);
2662
3410
  const [modelName, setModelName] = (0, react.useState)(null);
@@ -2699,6 +3447,7 @@ window.__ModuleLoader__.load({
2699
3447
  });
2700
3448
  ctx.sessions.open(promoted);
2701
3449
  ctx.betterSidebar.closeTab(tab.id, { sessionId: scope.sessionId });
3450
+ showToast(t("promoteDone"));
2702
3451
  } catch (error) {
2703
3452
  console.warn("[dsh-sidenote] 保存为正式会话失败:", error);
2704
3453
  }
@@ -2829,7 +3578,8 @@ window.__ModuleLoader__.load({
2829
3578
  children: "×"
2830
3579
  })]
2831
3580
  }),
2832
- pendingCount > 0 && childId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3581
+ pendingStore !== void 0 && pendingItems.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PendingCardList, { items: pendingItems }),
3582
+ pendingStore === void 0 && pendingCount > 0 && childId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2833
3583
  type: "button",
2834
3584
  className: sidechat_module_css_default.pendingBar,
2835
3585
  onClick: () => {
@@ -3068,6 +3818,66 @@ window.__ModuleLoader__.load({
3068
3818
  }
3069
3819
  }
3070
3820
  //#endregion
3821
+ //#region src/client/sidechat/notify.ts
3822
+ /** 挂接保护窗:watcher 建立后该窗口内的翻转不视为「一个真实 turn 跑完」。 */
3823
+ const ATTACH_GRACE_MS = 800;
3824
+ /**
3825
+ * 翻转侦测核心(抽出来纯可测):running true→false 且距挂接超过保护窗
3826
+ * 时返回 true。调用方持状态。
3827
+ */
3828
+ function shouldNotify(wasRunning, running, attachedAt, now) {
3829
+ return wasRunning && !running && now - attachedAt > ATTACH_GRACE_MS;
3830
+ }
3831
+ function registerCompletionNotify(ctx) {
3832
+ ctx.effect(() => {
3833
+ const watchers = /* @__PURE__ */ new Map();
3834
+ const rescan = () => {
3835
+ let tabs = [];
3836
+ try {
3837
+ tabs = collectSideTabs(ctx.betterSidebar.getSnapshot());
3838
+ } catch {
3839
+ return;
3840
+ }
3841
+ const alive = /* @__PURE__ */ new Set();
3842
+ for (const tab of tabs) {
3843
+ const childId = parseSideChatMeta(tab.meta).childId;
3844
+ if (childId === void 0) continue;
3845
+ alive.add(childId);
3846
+ if (watchers.has(childId)) continue;
3847
+ try {
3848
+ const session = ctx.sessions.binding(childId)?.session;
3849
+ if (session === void 0) continue;
3850
+ openSessionWindow(session);
3851
+ const title = tab.title;
3852
+ const watcher = {
3853
+ unsub: () => {},
3854
+ wasRunning: false,
3855
+ since: Date.now()
3856
+ };
3857
+ watcher.unsub = session.subscribe(() => {
3858
+ const running = session.getSnapshot()?.running === true;
3859
+ if (shouldNotify(watcher.wasRunning, running, watcher.since, Date.now())) showToast(t("sideChatDone", { title }));
3860
+ watcher.wasRunning = running;
3861
+ });
3862
+ watcher.wasRunning = session.getSnapshot()?.running === true;
3863
+ watchers.set(childId, watcher);
3864
+ } catch {}
3865
+ }
3866
+ for (const [id, w] of watchers) if (!alive.has(id)) {
3867
+ w.unsub();
3868
+ watchers.delete(id);
3869
+ }
3870
+ };
3871
+ const offState = ctx.betterSidebar.subscribeState(rescan);
3872
+ rescan();
3873
+ return () => {
3874
+ offState();
3875
+ for (const w of watchers.values()) w.unsub();
3876
+ watchers.clear();
3877
+ };
3878
+ }, "dsh-sidenote: completion notify");
3879
+ }
3880
+ //#endregion
3071
3881
  //#region src/client/sidechat/index.tsx
3072
3882
  /**
3073
3883
  * Workitem 01 — 侧边聊天面板。
@@ -3125,6 +3935,8 @@ window.__ModuleLoader__.load({
3125
3935
  registerSideCommand(ctx);
3126
3936
  registerSideChatReferenceSource(ctx);
3127
3937
  registerHeaderEntry(ctx);
3938
+ registerToastHost(ctx);
3939
+ registerCompletionNotify(ctx);
3128
3940
  }
3129
3941
  //#endregion
3130
3942
  //#region src/client/annotate/model.ts
@@ -3577,43 +4389,43 @@ window.__ModuleLoader__.load({
3577
4389
  document.head.appendChild(tag);
3578
4390
  }
3579
4391
  var annotate_module_css_default = {
4392
+ "toolbarButton": "_SVViG_toolbarButton",
4393
+ "editorSpacer": "_SVViG_editorSpacer",
4394
+ "sentCardTitle": "_SVViG_sentCardTitle",
4395
+ "chipRemove": "_SVViG_chipRemove",
3580
4396
  "toolbarBelow": "_SVViG_toolbarBelow",
3581
- "sentCardNote": "_SVViG_sentCardNote",
3582
- "editorEdit": "_SVViG_editorEdit",
3583
- "editorAsk": "_SVViG_editorAsk",
3584
- "badgeSent": "_SVViG_badgeSent",
3585
- "sentChip": "_SVViG_sentChip",
3586
- "chipClearAll": "_SVViG_chipClearAll",
3587
- "deleteButton": "_SVViG_deleteButton",
3588
- "dshSidenoteHintIn": "_SVViG_dshSidenoteHintIn",
3589
- "chipRow": "_SVViG_chipRow",
3590
- "saveButton": "_SVViG_saveButton",
3591
- "editorAskRow": "_SVViG_editorAskRow",
3592
- "chipNote": "_SVViG_chipNote",
4397
+ "editorTextarea": "_SVViG_editorTextarea",
3593
4398
  "chipWrap": "_SVViG_chipWrap",
3594
- "toolbar": "_SVViG_toolbar",
4399
+ "chipClearAll": "_SVViG_chipClearAll",
3595
4400
  "editorNew": "_SVViG_editorNew",
3596
- "editorInput": "_SVViG_editorInput",
3597
- "hint": "_SVViG_hint",
3598
- "sentChipRow": "_SVViG_sentChipRow",
3599
- "sentCardTitle": "_SVViG_sentCardTitle",
3600
- "chipText": "_SVViG_chipText",
3601
- "editorTarget": "_SVViG_editorTarget",
3602
- "chipPanel": "_SVViG_chipPanel",
3603
- "confirmButton": "_SVViG_confirmButton",
3604
4401
  "badge": "_SVViG_badge",
3605
- "toolbarButton": "_SVViG_toolbarButton",
4402
+ "editorAskRow": "_SVViG_editorAskRow",
4403
+ "chipText": "_SVViG_chipText",
4404
+ "badgeSent": "_SVViG_badgeSent",
4405
+ "editorError": "_SVViG_editorError",
3606
4406
  "cancelButton": "_SVViG_cancelButton",
3607
- "editorSpacer": "_SVViG_editorSpacer",
4407
+ "deleteButton": "_SVViG_deleteButton",
4408
+ "chipPanel": "_SVViG_chipPanel",
4409
+ "chipNote": "_SVViG_chipNote",
4410
+ "highlight": "_SVViG_highlight",
4411
+ "dshSidenoteHintIn": "_SVViG_dshSidenoteHintIn",
4412
+ "sentChipRow": "_SVViG_sentChipRow",
4413
+ "hintClose": "_SVViG_hintClose",
3608
4414
  "chipNumber": "_SVViG_chipNumber",
3609
- "chipRemove": "_SVViG_chipRemove",
3610
- "sentCardQuote": "_SVViG_sentCardQuote",
3611
- "editorError": "_SVViG_editorError",
4415
+ "saveButton": "_SVViG_saveButton",
3612
4416
  "chip": "_SVViG_chip",
3613
- "editorFooter": "_SVViG_editorFooter",
3614
- "editorTextarea": "_SVViG_editorTextarea",
3615
- "hintClose": "_SVViG_hintClose",
3616
- "highlight": "_SVViG_highlight"
4417
+ "chipRow": "_SVViG_chipRow",
4418
+ "sentChip": "_SVViG_sentChip",
4419
+ "sentCardQuote": "_SVViG_sentCardQuote",
4420
+ "hint": "_SVViG_hint",
4421
+ "editorInput": "_SVViG_editorInput",
4422
+ "toolbar": "_SVViG_toolbar",
4423
+ "confirmButton": "_SVViG_confirmButton",
4424
+ "editorEdit": "_SVViG_editorEdit",
4425
+ "editorAsk": "_SVViG_editorAsk",
4426
+ "editorTarget": "_SVViG_editorTarget",
4427
+ "sentCardNote": "_SVViG_sentCardNote",
4428
+ "editorFooter": "_SVViG_editorFooter"
3617
4429
  };
3618
4430
  //#endregion
3619
4431
  //#region src/client/annotate/hint.tsx