dsh-task-dispatch-table 0.1.1 → 0.1.3

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/dist/client.js CHANGED
@@ -169,6 +169,8 @@ window.__ModuleLoader__.load({
169
169
  *
170
170
  * ⚠️ 2026-10-04 从 `task-list.tsx` 上提到这里:卡片「执行记录」面板与**执行记录总查询页**都要用,
171
171
  * 同一解析写两遍就是违规(本仓规矩:一类东西一个实现)。
172
+ *
173
+ * ⚠️ 2026-10-10:`process_outputs`(过程文件桶)**同一个形状**,因此继续用它解析,不另写一份。
172
174
  */
173
175
  function outputsOf(raw) {
174
176
  if (raw === null || raw === void 0 || raw === "") return [];
@@ -503,7 +505,9 @@ window.__ModuleLoader__.load({
503
505
  explorerRootName: "(工作区根目录)",
504
506
  explorerExpand: "展开目录",
505
507
  explorerCollapse: "收起目录",
508
+ explorerEnter: "进入",
506
509
  colOutputs: "产出",
510
+ procFilesTitle: "过程文件 · {count} 项",
507
511
  deliverRowTitle: "交付文件",
508
512
  deliverRowPreparing: "准备交付",
509
513
  deliverRowRunning: "正在交付",
@@ -602,8 +606,10 @@ window.__ModuleLoader__.load({
602
606
  editorUploadErrEmpty: "文件内容为空",
603
607
  editorUploadErrGeneric: "上传失败,请重试",
604
608
  editorPickerNoSession: "该工作区下还没有会话,无法读取文件。",
605
- editorAttachmentsHint: "附加文件会随任务一起派发给执行的 Agent:任务执行过程中,Agent 可读取或操作这些文件的内容。",
609
+ editorAttachmentsHint: "附加文件会随任务一起派发给执行的 Agent:任务执行过程中,Agent 可读取或操作这些文件的内容。也可从工作区选择整个目录(连带其下所有文件):Agent 拿到的是这个目录的路径,自己在里面找文件,提示词里写「附件里的 目录名/子文件路径」即可指定其中某一个。",
606
610
  editorPickerPick: "选择此文件",
611
+ editorPickerPickDir: "选择此目录(连带其下所有文件)",
612
+ editorPickerSelect: "选择",
607
613
  editorOpenEditor: "全屏编辑",
608
614
  editorPromptEditorTitle: "提示词编辑器(.md)",
609
615
  editorNoVersions: "暂无版本,保存后可在此回滚",
@@ -632,6 +638,7 @@ window.__ModuleLoader__.load({
632
638
  editorFullPermB2: "执行命令、发起网络请求等敏感操作",
633
639
  editorFullPermCheck: "我已了解风险,并愿意为该任务的执行结果负责。",
634
640
  editorTaskSaved: "任务已保存",
641
+ editorTaskDeleted: "任务已删除",
635
642
  editorSchedHourlyOnce: "每小时执行一次",
636
643
  editorSchedNoDaySuffix: ",但还没选生效日",
637
644
  editorSchedWeeklyDayPrefix: "每周",
@@ -769,6 +776,9 @@ window.__ModuleLoader__.load({
769
776
  listNextFullTitle: "下次执行:{when}",
770
777
  listEmpty: "还没有任务。点右上角「+ 新建任务」创建第一个。",
771
778
  listEmptyFiltered: "没有符合当前筛选的任务。",
779
+ listEmptyTitle: "创建你的第一个定时任务",
780
+ listEmptyHint: "写好提示词,到点自动执行;支持每天 / 每周 / 间隔循环。",
781
+ listEmptyAction: "+ 新建定时任务",
772
782
  listSectionAttachments: "附加文件",
773
783
  listSectionDepends: "前置任务",
774
784
  listFieldWorkspace: "工作区",
@@ -1164,7 +1174,9 @@ window.__ModuleLoader__.load({
1164
1174
  explorerRootName: "(workspace root)",
1165
1175
  explorerExpand: "Expand directory",
1166
1176
  explorerCollapse: "Collapse directory",
1177
+ explorerEnter: "Open",
1167
1178
  colOutputs: "Outputs",
1179
+ procFilesTitle: "Process files · {count}",
1168
1180
  deliverRowTitle: "Deliver files",
1169
1181
  deliverRowPreparing: "Preparing delivery",
1170
1182
  deliverRowRunning: "Delivering",
@@ -1263,8 +1275,10 @@ window.__ModuleLoader__.load({
1263
1275
  editorUploadErrEmpty: "File is empty",
1264
1276
  editorUploadErrGeneric: "Upload failed, please retry",
1265
1277
  editorPickerNoSession: "No sessions in this workspace yet — its files cannot be read.",
1266
- editorAttachmentsHint: "Attachments are dispatched along with the task: while it runs, the agent can read and operate on their contents.",
1278
+ editorAttachmentsHint: "Attachments are dispatched along with the task: while it runs, the agent can read and operate on their contents. You can also pick a whole folder: the agent gets the folder path and explores it itself, so refer to a file inside as \"<folder name>/<relative path>\" in the prompt.",
1267
1279
  editorPickerPick: "Pick this file",
1280
+ editorPickerPickDir: "Pick this folder (with everything under it)",
1281
+ editorPickerSelect: "Select",
1268
1282
  editorOpenEditor: "Full-screen edit",
1269
1283
  editorPromptEditorTitle: "Prompt editor (.md)",
1270
1284
  editorNoVersions: "No versions yet — save one to roll back here",
@@ -1293,6 +1307,7 @@ window.__ModuleLoader__.load({
1293
1307
  editorFullPermB2: "run commands, make network requests and other sensitive operations",
1294
1308
  editorFullPermCheck: "I understand the risk and accept responsibility for this task's results.",
1295
1309
  editorTaskSaved: "Task saved",
1310
+ editorTaskDeleted: "Task deleted",
1296
1311
  editorSchedHourlyOnce: "runs hourly",
1297
1312
  editorSchedNoDaySuffix: ", but no active day is selected",
1298
1313
  editorSchedWeeklyDayPrefix: "every ",
@@ -1430,6 +1445,9 @@ window.__ModuleLoader__.load({
1430
1445
  listNextFullTitle: "Next run: {when}",
1431
1446
  listEmpty: "No tasks yet. Use “+ New task” to create the first one.",
1432
1447
  listEmptyFiltered: "No task matches the current filter.",
1448
+ listEmptyTitle: "Create your first scheduled task",
1449
+ listEmptyHint: "Write a prompt and it runs automatically on time — every day, every week, or on an interval.",
1450
+ listEmptyAction: "+ New scheduled task",
1433
1451
  listSectionAttachments: "Attachments",
1434
1452
  listSectionDepends: "Preceding tasks",
1435
1453
  listFieldWorkspace: "Workspace",
@@ -2212,6 +2230,22 @@ body[data-ds-dark-theme]{
2212
2230
  内层盒子裁了、按「半截文本」平移导致的)。鼠标移开 ⇒ :hover 失效 ⇒ 自动复位回省略号态。 */
2213
2231
  .dsh-tdt-mq-run:hover .dsh-tdt-mq-in{max-width:none;overflow:visible;animation:dsh-tdt-mq-scroll var(--dsh-tdt-mq-dur,6s) linear .4s 1 forwards;}
2214
2232
  @keyframes dsh-tdt-mq-scroll{from{transform:translateX(0)}to{transform:translateX(var(--dsh-tdt-mq-dist,-40px))}}
2233
+ `;
2234
+ /** 「过程文件」块的皮肤规则(2026-10-10:回执过程文件桶的次级展示件)。 */
2235
+ const PROCESS_FILES_CSS = `
2236
+ /* ── 「过程文件」块:会话弹窗交付卡下方 / 执行记录展开区 / 查看档产出清单三处共用 ──────────
2237
+ (结构在 client/process-files.tsx)。观感纪律 = **次级**:小字浅灰标题 + 数量、默认收起,
2238
+ 不许抢主文件(交付卡 / 产出物清单)的视线;展开后的文件行复用 .dsh-tdt-filechip--inline。 */
2239
+ .dsh-tdt-proc{display:flex;flex-direction:column;gap:4px;margin-top:10px;min-width:0;}
2240
+ .dsh-tdt-proc-head{appearance:none;-webkit-appearance:none;align-self:flex-start;display:inline-flex;align-items:center;
2241
+ gap:6px;padding:2px 6px;border:0;border-radius:var(--tdt-radius-xs);background:transparent;color:var(--tdt-fg-3);
2242
+ font:inherit;font-size:var(--tdt-font-xs);line-height:var(--tdt-line-sm);cursor:pointer;
2243
+ transition:background-color var(--tdt-dur) var(--tdt-ease),color var(--tdt-dur) var(--tdt-ease);}
2244
+ .dsh-tdt-proc-head:hover{background:var(--tdt-chip-bg);color:var(--tdt-fg-2);}
2245
+ .dsh-tdt-proc-head:focus-visible{outline:2px solid var(--tdt-focus);outline-offset:1px;}
2246
+ .dsh-tdt-proc-list{display:flex;flex-wrap:wrap;gap:2px 4px;min-width:0;}
2247
+
2248
+ @media (prefers-reduced-motion: reduce){.dsh-tdt-proc-head{transition:none;}}
2215
2249
  `;
2216
2250
  /** 控件皮肤域的固定名(注入顺序在 tokens 之后)。 */
2217
2251
  const CONTROLS_DOMAIN = "controls";
@@ -2219,7 +2253,7 @@ body[data-ds-dark-theme]{
2219
2253
  * 确保控件皮肤已登记并注入(幂等;组件渲染时调用一次即可)。
2220
2254
  */
2221
2255
  function ensureControlsStyle() {
2222
- applyStyle(CONTROLS_DOMAIN, SEGMENTED_CSS + BUTTON_CSS + FIELD_CSS + DATETIME_CSS + SELECT_CSS);
2256
+ applyStyle(CONTROLS_DOMAIN, SEGMENTED_CSS + BUTTON_CSS + FIELD_CSS + DATETIME_CSS + SELECT_CSS + PROCESS_FILES_CSS);
2223
2257
  }
2224
2258
  //#endregion
2225
2259
  //#region src/client/ui/Segmented.tsx
@@ -50177,8 +50211,12 @@ body[data-ds-dark-theme]{
50177
50211
  .dsh-tdt-sv-crumbs-menu-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
50178
50212
  /* 目录树:行内 ▸ 开关(内联展开/收起),点它只切展开、不导航。 */
50179
50213
  .dsh-tdt-sv-tree-toggle{appearance:none;background:0 0;border:none;flex:none;width:20px;height:20px;padding:0;margin:0;border-radius:var(--tdt-radius-sm,6px);cursor:pointer;color:var(--tdt-fg-2,rgba(128,128,128,.95));display:inline-flex;align-items:center;justify-content:center;transition:transform var(--tdt-dur,.15s) var(--tdt-ease,ease),background var(--tdt-dur,.15s) var(--tdt-ease,ease);}
50180
- .dsh-tdt-sv-tree-toggle:hover{background:var(--tdt-hover,rgba(38,49,72,.06));}
50181
50214
  .dsh-tdt-sv-tree-toggle-open{transform:rotate(90deg);}
50215
+ /* 目录树:行尾「选择」按钮(仅选择器形态,用户 2026-10-10)——平时藏起来,悬停或聚焦该行才出现,
50216
+ 点它才把整个文件夹选为附件;平时隐形但保留尺寸,出现时不会顶动行内布局。 */
50217
+ .dsh-tdt-sv-tree-act{flex:none;margin-left:auto;appearance:none;background:0 0;border:1px solid var(--tdt-border,rgba(128,128,128,.35));border-radius:var(--tdt-radius-sm,6px);padding:2px 8px;font-size:var(--tdt-font-sm);line-height:1.4;color:var(--tdt-fg-2,rgba(128,128,128,.95));cursor:pointer;opacity:0;pointer-events:none;transition:opacity var(--tdt-dur,.15s) var(--tdt-ease,ease);}
50218
+ .dsh-tdt-sv-tree-row:hover .dsh-tdt-sv-tree-act,.dsh-tdt-sv-tree-act:focus-visible{opacity:1;pointer-events:auto;}
50219
+ .dsh-tdt-sv-tree-act:hover{background:var(--tdt-hover,rgba(38,49,72,.06));color:var(--tdt-fg);}
50182
50220
  /* 内联展开子层:左缩进 + 淡竖线引导层级。 */
50183
50221
  .dsh-tdt-sv-tree-children{margin-left:9px;padding-left:7px;border-left:1px solid var(--tdt-border,rgba(128,128,128,.28));display:flex;flex-direction:column;}
50184
50222
  .dsh-tdt-sv-tree-loading,.dsh-tdt-sv-tree-err{padding:4px 8px 4px 36px;font-size:var(--tdt-font-sm);color:var(--tdt-fg-3,rgba(128,128,128,.8));}
@@ -52250,6 +52288,54 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
52250
52288
  }, (0, react$1.createElement)("span", null, t(expanded ? "deliverCollapse" : "deliverAll", { count: files.length })), expanded ? (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutlineRegular, {}) : (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {})) : null);
52251
52289
  }
52252
52290
  //#endregion
52291
+ //#region src/client/process-files.tsx
52292
+ /** 目录以尾斜杠表达(与 `normalizeOutputs` / `baseNameOf` 同一约定)。 */
52293
+ const isDirPath$1 = (path) => path.endsWith("/");
52294
+ function ProcessFiles(props) {
52295
+ const { paths, onOpen, className, t } = props;
52296
+ const [open, setOpen] = (0, react$1.useState)(false);
52297
+ ensureControlsStyle();
52298
+ const list = paths ?? [];
52299
+ if (list.length === 0) return null;
52300
+ return (0, react$1.createElement)("div", {
52301
+ className: className === void 0 ? "dsh-tdt-proc" : `dsh-tdt-proc ${className}`,
52302
+ "data-process-files": true
52303
+ }, (0, react$1.createElement)("button", {
52304
+ type: "button",
52305
+ className: "dsh-tdt-proc-head",
52306
+ "aria-expanded": open,
52307
+ onClick: () => {
52308
+ setOpen((value) => !value);
52309
+ }
52310
+ }, (0, react$1.createElement)("span", null, t("procFilesTitle", { count: list.length })), open ? (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutlineRegular, { size: 12 }) : (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { size: 12 })), open ? (0, react$1.createElement)("div", { className: "dsh-tdt-proc-list" }, list.map((path) => {
52311
+ const body = [isDirPath$1(path) ? (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconFolderCloseRegular, { size: 14 }) : (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
52312
+ path,
52313
+ size: 14
52314
+ }), (0, react$1.createElement)(MarqueeText, {
52315
+ text: baseNameOf(path),
52316
+ title: path,
52317
+ style: {
52318
+ maxWidth: "40ch",
52319
+ minWidth: 0
52320
+ }
52321
+ })];
52322
+ return onOpen === void 0 ? (0, react$1.createElement)("span", {
52323
+ key: path,
52324
+ className: "dsh-tdt-filechip dsh-tdt-filechip--inline",
52325
+ title: path,
52326
+ "data-noclick": true
52327
+ }, ...body) : (0, react$1.createElement)("button", {
52328
+ key: path,
52329
+ type: "button",
52330
+ className: "dsh-tdt-filechip dsh-tdt-filechip--inline",
52331
+ title: path,
52332
+ onClick: () => {
52333
+ onOpen(path);
52334
+ }
52335
+ }, ...body);
52336
+ })) : null);
52337
+ }
52338
+ //#endregion
52253
52339
  //#region src/client/task-file-context.tsx
52254
52340
  /**
52255
52341
  * 实例快照 → 本任务工作区 path(判上游目录是否跨区用)。
@@ -52734,7 +52820,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
52734
52820
  * @param groupPart - 过程分组侧('response' | 'reasoning')。
52735
52821
  * @returns 节点视图;null = 决策 28 过滤的噪音 kind。
52736
52822
  */
52737
- function renderKeyedNode(node, turnProcess, t, onBranchAt, fileOpen, groupPart, deliverFiles, lastTailTurn, host, attachedNames) {
52823
+ function renderKeyedNode(node, turnProcess, t, onBranchAt, fileOpen, groupPart, deliverFiles, lastTailTurn, host, attachedNames, processFiles) {
52738
52824
  switch (node.kind) {
52739
52825
  case "turn-trigger": return (0, react$1.createElement)(TurnTriggerNodeViewMirror, {
52740
52826
  data: node.data,
@@ -52748,14 +52834,21 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
52748
52834
  case "turn-tail": {
52749
52835
  const data = node.data;
52750
52836
  const turn = data?.turn ?? turnLocationOf(node)?.turn;
52751
- const tailSlot = deliverFiles !== void 0 && deliverFiles.length > 0 && turn !== void 0 && lastTailTurn !== void 0 && turn === lastTailTurn ? (0, react$1.createElement)(react$1.Fragment, null, host !== void 0 && host !== null && host !== "error" && !host.available ? (0, react$1.createElement)("span", {
52837
+ const atLastTail = turn !== void 0 && lastTailTurn !== void 0 && turn === lastTailTurn;
52838
+ const showGrid = deliverFiles !== void 0 && deliverFiles.length > 0 && atLastTail;
52839
+ const showProc = processFiles !== void 0 && processFiles.length > 0 && atLastTail;
52840
+ const tailSlot = showGrid || showProc ? (0, react$1.createElement)(react$1.Fragment, null, showGrid && host !== void 0 && host !== null && host !== "error" && !host.available ? (0, react$1.createElement)("span", {
52752
52841
  className: ocOr("Deliverables", "hostStatus", "dsh-tdt-sv-host-status"),
52753
52842
  "data-host-unavailable": true
52754
- }, t("presented.unavailable")) : null, (0, react$1.createElement)(DeliverablesGridMirror, {
52843
+ }, t("presented.unavailable")) : null, showGrid ? (0, react$1.createElement)(DeliverablesGridMirror, {
52755
52844
  files: deliverFiles,
52756
52845
  onOpen: fileOpen?.open,
52757
52846
  t
52758
- })) : null;
52847
+ }) : null, showProc ? (0, react$1.createElement)(ProcessFiles, {
52848
+ paths: processFiles,
52849
+ onOpen: fileOpen?.open,
52850
+ t
52851
+ }) : null) : null;
52759
52852
  return data === void 0 || data.closing === null || data.closing === void 0 ? null : (0, react$1.createElement)(TurnTailNodeViewMirror, {
52760
52853
  data,
52761
52854
  onBranchAt,
@@ -53133,7 +53226,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
53133
53226
  * forkSession / openHostSession 缺一即不渲染按钮(服务未就位时功能降级)。
53134
53227
  */
53135
53228
  function SessionViewModal(props) {
53136
- const { t, heading, sessionId, view, onClose, forkSession, openHostSession, workspaceFiles, onOpenFile, outputs, upstream, attached, workspacePath, headingTask, headingRest, taskId, onOpenTask } = props;
53229
+ const { t, heading, sessionId, view, onClose, forkSession, openHostSession, workspaceFiles, onOpenFile, outputs, processOutputs, upstream, attached, workspacePath, headingTask, headingRest, taskId, onOpenTask } = props;
53137
53230
  const tt = (0, react$1.useMemo)(() => interpolateTranslate(t), [t]);
53138
53231
  const attachedNames = (0, react$1.useMemo)(() => new Set((attached ?? []).filter((file) => file.path !== null).map((file) => file.name)), [attached]);
53139
53232
  const subscribe = (0, react$1.useMemo)(() => (onChange) => view.target.subscribe(onChange), [view]);
@@ -53234,14 +53327,15 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
53234
53327
  store
53235
53328
  ]);
53236
53329
  const host = usePresentedHost();
53237
- const renderNode = (0, react$1.useCallback)((node, turnProcess, groupPart) => renderKeyedNode(node, turnProcess, tt, onBranchAt, fileOpen, groupPart, deliverFiles, lastTailTurn, host, attachedNames), [
53330
+ const renderNode = (0, react$1.useCallback)((node, turnProcess, groupPart) => renderKeyedNode(node, turnProcess, tt, onBranchAt, fileOpen, groupPart, deliverFiles, lastTailTurn, host, attachedNames, processOutputs), [
53238
53331
  tt,
53239
53332
  onBranchAt,
53240
53333
  fileOpen,
53241
53334
  deliverFiles,
53242
53335
  lastTailTurn,
53243
53336
  host,
53244
- attachedNames
53337
+ attachedNames,
53338
+ processOutputs
53245
53339
  ]);
53246
53340
  const isTurnClosed = (0, react$1.useCallback)((turn) => (turns?.get(turn) ?? turns?.get(String(turn)))?.status !== "open", [turns]);
53247
53341
  const groupedView = (0, react$1.useMemo)(() => keyed ? buildProcessGroups(order, (key) => store?.get(key), isTurnClosed) : void 0, [
@@ -53922,7 +54016,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
53922
54016
  * ▾ 下拉条目构造(**picker 与 dock 共用同一段代码**,差别只在入参——用户 2026-09-29 点名提炼,
53923
54017
  * 严禁两处各写一份):
53924
54018
  * - 当前工作区排第一(打开文件夹图标 + 加粗),**其下路径紧跟**(每层一个右箭头位缩进),
53925
- * 再列其他工作区(关合图标 + 灰)——「打开哪个就跟在哪个下面」;
54019
+ * 再列其他工作区(关合图标 + 灰)——「打开哪个就跟在哪个下面」;点当前工作区 = 回到其根目录。
53926
54020
  * - 当前工作区未知(rootName 空或不在清单)时路径段置顶、全部工作区按「其他」罗列;
53927
54021
  * - workspaces 不传 = 不显示工作区段(旧形态兜底:根无箭头、其下逐层缩进)。
53928
54022
  */
@@ -53941,7 +54035,8 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
53941
54035
  },
53942
54036
  title: ws,
53943
54037
  onClick: () => {
53944
- if (!current) onPickWorkspace?.(ws);
54038
+ if (current) onLoadDir("");
54039
+ else onPickWorkspace?.(ws);
53945
54040
  }
53946
54041
  }, (0, react$1.createElement)("span", { style: {
53947
54042
  display: "inline-flex",
@@ -54422,7 +54517,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
54422
54517
  if (!(entry.type === "directory")) {
54423
54518
  const pick = () => {
54424
54519
  if (picker && onPick !== void 0) {
54425
- onPick(relativizeToRoot(childPath, sessionId));
54520
+ onPick(relativizeToRoot(childPath, sessionId), false);
54426
54521
  return;
54427
54522
  }
54428
54523
  setViewing(childPath);
@@ -54446,18 +54541,28 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
54446
54541
  }
54447
54542
  const cached = childCache[childPath];
54448
54543
  const isOpen = openDirs.has(childPath);
54544
+ const rowAction = () => {
54545
+ toggleDir(childPath);
54546
+ };
54547
+ /** 行尾动作按钮(stopPropagation 防触发展开):选择器选中整个文件夹;浏览则进入该目录。 */
54548
+ const actThisDir = (event) => {
54549
+ event.stopPropagation();
54550
+ if (picker === true) {
54551
+ if (onPick !== void 0) onPick(relativizeToRoot(childPath, sessionId), true);
54552
+ return;
54553
+ }
54554
+ loadDir(childPath);
54555
+ };
54449
54556
  return (0, react$1.createElement)(react$1.Fragment, { key: childPath }, (0, react$1.createElement)("div", {
54450
54557
  className: "dsh-tdt-sv-tree-row",
54451
54558
  role: "button",
54452
54559
  tabIndex: 0,
54453
54560
  title: childPath,
54454
- onClick: () => {
54455
- loadDir(childPath);
54456
- },
54561
+ onClick: rowAction,
54457
54562
  onKeyDown: (event) => {
54458
- if (event.key === "Enter" || event.key === " ") loadDir(childPath);
54563
+ if (event.key === "Enter" || event.key === " ") rowAction();
54459
54564
  }
54460
- }, tooled(isOpen ? t("explorerCollapse") : t("explorerExpand"), (0, react$1.createElement)("button", {
54565
+ }, (0, react$1.createElement)("button", {
54461
54566
  type: "button",
54462
54567
  className: "dsh-tdt-sv-tree-toggle" + (isOpen ? " dsh-tdt-sv-tree-toggle-open" : ""),
54463
54568
  "aria-expanded": isOpen,
@@ -54466,7 +54571,12 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
54466
54571
  event.stopPropagation();
54467
54572
  toggleDir(childPath);
54468
54573
  }
54469
- }, (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutlineRegular, { size: 16 }))), (0, react$1.createElement)("span", { className: "dsh-tdt-sv-tree-name" }, entry.name)), isOpen ? (0, react$1.createElement)("div", { className: "dsh-tdt-sv-tree-children" }, cached === void 0 || cached.status === "loading" ? null : cached.status === "error" ? (0, react$1.createElement)("div", { className: "dsh-tdt-sv-tree-err" }, t(cached.error.key, cached.error.params)) : (0, react$1.createElement)(react$1.Fragment, null, renderTree(cached.entries, childPath), cached.truncated ? (0, react$1.createElement)("div", { className: "dsh-tdt-sv-tree-truncated" }, t("explorerTruncated")) : null)) : null);
54574
+ }, (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutlineRegular, { size: 16 })), (0, react$1.createElement)("span", { className: "dsh-tdt-sv-tree-name" }, entry.name), picker === true && onPick === void 0 ? null : (0, react$1.createElement)("button", {
54575
+ type: "button",
54576
+ className: "dsh-tdt-sv-tree-act",
54577
+ "aria-label": picker === true ? t("editorPickerPickDir") : t("explorerEnter"),
54578
+ onClick: actThisDir
54579
+ }, t(picker === true ? "editorPickerSelect" : "explorerEnter"))), isOpen ? (0, react$1.createElement)("div", { className: "dsh-tdt-sv-tree-children" }, cached === void 0 || cached.status === "loading" ? null : cached.status === "error" ? (0, react$1.createElement)("div", { className: "dsh-tdt-sv-tree-err" }, t(cached.error.key, cached.error.params)) : (0, react$1.createElement)(react$1.Fragment, null, renderTree(cached.entries, childPath), cached.truncated ? (0, react$1.createElement)("div", { className: "dsh-tdt-sv-tree-truncated" }, t("explorerTruncated")) : null)) : null);
54470
54580
  });
54471
54581
  };
54472
54582
  const rootAbs = workspaceRoots.get(sessionId);
@@ -64592,7 +64702,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
64592
64702
  const absPath = item.path;
64593
64703
  const anchor = item.anchorSessionId;
64594
64704
  const key = item.key ?? item.name;
64595
- const icon = (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
64705
+ const icon = item.isDir === true ? (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconFolderCloseRegular, { size: 14 }) : (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
64596
64706
  path: item.name,
64597
64707
  size: 14
64598
64708
  });
@@ -64678,6 +64788,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
64678
64788
  const canOpenSession = sid !== null && onOpenSession !== void 0;
64679
64789
  const canOpenFile = sid !== null && onOpenFile !== void 0;
64680
64790
  const outputs = outputsOf(instance.outputs);
64791
+ const processOutputs = outputsOf(instance.process_outputs);
64681
64792
  const dur = durationMsOf(instance);
64682
64793
  const tokens = instance.token_in === null && instance.token_out === null ? null : formatTokenCount((instance.token_in ?? 0) + (instance.token_out ?? 0));
64683
64794
  const note = instance.note === null || instance.note === void 0 ? "" : instance.note;
@@ -64761,10 +64872,16 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
64761
64872
  onClick: () => {
64762
64873
  if (canOpenFile && onOpenFile !== void 0 && sid !== null) onOpenFile(sid, output);
64763
64874
  }
64764
- }, (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
64875
+ }, output.endsWith("/") ? (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconFolderCloseRegular, { size: 14 }) : (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
64765
64876
  path: output,
64766
64877
  size: 14
64767
- }), (0, react$1.createElement)("span", { className: "dsh-tdt-ellipsis" }, baseNameOf(output)))))));
64878
+ }), (0, react$1.createElement)("span", { className: "dsh-tdt-ellipsis" }, baseNameOf(output)))))), (0, react$1.createElement)(ProcessFiles, {
64879
+ paths: processOutputs,
64880
+ onOpen: canOpenFile && onOpenFile !== void 0 ? (path) => {
64881
+ onOpenFile(sid, path);
64882
+ } : void 0,
64883
+ t
64884
+ }));
64768
64885
  }
64769
64886
  //#endregion
64770
64887
  //#region src/client/event-subscribe.ts
@@ -65007,7 +65124,8 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
65007
65124
  name: item.name,
65008
65125
  key: item.id,
65009
65126
  path: resolved?.path ?? null,
65010
- anchorSessionId: resolved?.anchorSessionId ?? null
65127
+ anchorSessionId: resolved?.anchorSessionId ?? null,
65128
+ isDir: item.isDir
65011
65129
  };
65012
65130
  }),
65013
65131
  depends: draft.deps.map((dep) => {
@@ -65383,7 +65501,8 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
65383
65501
  name: item.name,
65384
65502
  kind: item.kind,
65385
65503
  ref: item.ref,
65386
- ...typeof item.workspace === "string" ? { workspace: item.workspace } : {}
65504
+ ...typeof item.workspace === "string" ? { workspace: item.workspace } : {},
65505
+ ...typeof item.isDir === "boolean" ? { isDir: item.isDir } : {}
65387
65506
  })) : [],
65388
65507
  versions: [],
65389
65508
  ...typeof definition.createdAt === "string" && definition.createdAt !== "" ? { createdAt: definition.createdAt } : {}
@@ -66250,6 +66369,12 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
66250
66369
  };
66251
66370
  }, [pickerOpen]);
66252
66371
  const [uploadError, setUploadError] = (0, react$1.useState)(null);
66372
+ /**
66373
+ * 刚上传、还没保存的附件:文件已在**服务端临时区**,但绝对路径只有服务端知道 ⇒ 由上传回包带回来存这里
66374
+ * (用户 2026-10-09:进了列表就要能点开预览,不能等保存)。
66375
+ * ⚠️ 只活组件内存,**绝不写进 draft** —— draft 会被序列化进任务定义 JSON,路径是服务端的、也不该留痕。
66376
+ */
66377
+ const [uploadPaths, setUploadPaths] = (0, react$1.useState)({});
66253
66378
  const [confirmDiscard, setConfirmDiscard] = (0, react$1.useState)(false);
66254
66379
  const initialDraftRef = (0, react$1.useRef)(draft);
66255
66380
  const dirty = stableStringify(draft) !== stableStringify(initialDraftRef.current);
@@ -66515,6 +66640,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
66515
66640
  if (sendable.length === 0) return;
66516
66641
  setUploading(true);
66517
66642
  const added = [];
66643
+ const nextPaths = {};
66518
66644
  let lastErr = null;
66519
66645
  for (const file of sendable) {
66520
66646
  const controller = new AbortController();
@@ -66533,8 +66659,10 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
66533
66659
  lastErr = typeof data?.error === "string" ? data.error : "upload-failed";
66534
66660
  continue;
66535
66661
  }
66662
+ const id = makeId();
66663
+ if (typeof data.path === "string" && data.path !== "") nextPaths[id] = data.path;
66536
66664
  added.push({
66537
- id: makeId(),
66665
+ id,
66538
66666
  name: data.name,
66539
66667
  kind: "upload",
66540
66668
  ref: data.ref
@@ -66547,8 +66675,43 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
66547
66675
  }
66548
66676
  setUploading(false);
66549
66677
  if (added.length > 0) patch({ attachments: [...draft.attachments, ...added] });
66678
+ if (Object.keys(nextPaths).length > 0) setUploadPaths((prev) => ({
66679
+ ...prev,
66680
+ ...nextPaths
66681
+ }));
66550
66682
  if (lastErr !== null) setUploadError(lastErr);
66551
66683
  };
66684
+ /**
66685
+ * 这一行的预览目标(路径 + 锚点会话)。三档递进,**服务端解析优先**(已保存任务的绝对路径最权威):
66686
+ * ① overview 命中(服务端把 ref 绝对化 + 配好锚点会话);
66687
+ * ② link 型当场算:`ref` 本来就是**工作区相对路径**,而 FileBrowser 的 path 就是同一口径
66688
+ * (相对会话 cwd = 工作区根)⇒ **零换算**,锚点取该工作区最近会话(与选择器同一份 workspaceAnchors);
66689
+ * ③ upload 型(还没保存):用上传回包带回的临时区绝对路径,锚点任一有会话的工作区即可
66690
+ * (宿主要的只是「某个有效会话」,与服务端 `attachmentsWithPaths` 的兜底同款)。
66691
+ * 算不出锚点 ⇒ 返回 null ⇒ 该行不可点:**没有会话的工作区宿主列不出文件,不给假入口**。
66692
+ */
66693
+ const previewTargetOf = (att) => {
66694
+ const anchors = workspaceAnchors ?? {};
66695
+ const hit = resolvedAttachments?.find((r) => r.name === att.name && r.kind === att.kind && r.path !== void 0 && r.anchorSessionId !== void 0);
66696
+ if (hit !== void 0) return {
66697
+ path: hit.path,
66698
+ anchorSessionId: hit.anchorSessionId
66699
+ };
66700
+ if (att.kind === "link") {
66701
+ const anchor = anchors[att.workspace !== void 0 && att.workspace !== "" ? att.workspace : draft.workspace];
66702
+ return anchor === void 0 || anchor === "" ? null : {
66703
+ path: att.ref,
66704
+ anchorSessionId: anchor
66705
+ };
66706
+ }
66707
+ const abs = uploadPaths[att.id];
66708
+ if (abs === void 0 || abs === "") return null;
66709
+ const anyAnchor = Object.values(anchors).find((item) => typeof item === "string" && item !== "");
66710
+ return anyAnchor === void 0 ? null : {
66711
+ path: abs,
66712
+ anchorSessionId: anyAnchor
66713
+ };
66714
+ };
66552
66715
  const attachmentsCard = (0, react$1.createElement)("div", { className: `dsh-tdt-ed-card${problemsByField("attachments") ? " dsh-tdt-ed-card--error" : ""}` }, (0, react$1.createElement)("div", { className: "dsh-tdt-ed-card-head" }, (0, react$1.createElement)("div", {
66553
66716
  className: "dsh-tdt-ed-label",
66554
66717
  style: {
@@ -66562,20 +66725,20 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
66562
66725
  gap: "6px",
66563
66726
  marginBottom: "10px"
66564
66727
  } }, draft.attachments.map((att) => {
66565
- const hit = resolvedAttachments?.find((r) => r.name === att.name && r.kind === att.kind && r.path !== void 0 && r.anchorSessionId !== void 0);
66566
- const canView = hit !== void 0 && onOpenFile !== void 0;
66728
+ const target = previewTargetOf(att);
66729
+ const canView = target !== null && onOpenFile !== void 0;
66567
66730
  return (0, react$1.createElement)("div", {
66568
66731
  key: att.id,
66569
66732
  className: `dsh-tdt-ed-attrow${canView ? " dsh-tdt-ed-attrow--view" : ""}`,
66570
66733
  title: canView ? t("editorAttachmentView") : void 0,
66571
66734
  onClick: canView ? () => {
66572
- onOpenFile?.(hit.anchorSessionId, hit.path);
66735
+ onOpenFile?.(target.anchorSessionId, target.path);
66573
66736
  } : void 0
66574
66737
  }, (0, react$1.createElement)("span", { style: {
66575
66738
  flex: "none",
66576
66739
  display: "flex",
66577
66740
  alignItems: "center"
66578
- } }, (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
66741
+ } }, att.isDir === true ? (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconFolderCloseRegular, { size: 16 }) : (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
66579
66742
  path: att.name,
66580
66743
  size: 16
66581
66744
  })), (0, react$1.createElement)("span", {
@@ -66601,6 +66764,11 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
66601
66764
  onClick: (event) => {
66602
66765
  event.stopPropagation();
66603
66766
  patch({ attachments: draft.attachments.filter((a) => a.id !== att.id) });
66767
+ setUploadPaths((prev) => {
66768
+ const next = { ...prev };
66769
+ delete next[att.id];
66770
+ return next;
66771
+ });
66604
66772
  },
66605
66773
  title: t("editorAttachmentRemove"),
66606
66774
  "aria-label": t("editorAttachmentRemove"),
@@ -67357,14 +67525,15 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
67357
67525
  setPickerOpen(false);
67358
67526
  },
67359
67527
  picker: true,
67360
- onPick: (p) => {
67528
+ onPick: (p, isDir) => {
67361
67529
  const name = p.slice(Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\")) + 1);
67362
67530
  addAttachment({
67363
67531
  id: makeId(),
67364
67532
  name,
67365
67533
  kind: "link",
67366
67534
  ref: p,
67367
- workspace: pickerWs
67535
+ workspace: pickerWs,
67536
+ isDir
67368
67537
  });
67369
67538
  setPickerOpen(false);
67370
67539
  },
@@ -67738,6 +67907,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
67738
67907
  minWidth: 0
67739
67908
  } }, (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconAlarmClockOutlineRegular, { size: 12 }), renderNextExec(row.scheduled_at, t))) : null;
67740
67909
  const outputs = outputsOf(row.outputs);
67910
+ const processOutputs = outputsOf(row.process_outputs);
67741
67911
  const sid = row.session_id;
67742
67912
  const canOpenSession = sid !== null && sid !== "";
67743
67913
  const canOpenFile = canOpenSession && openFile !== void 0;
@@ -67750,8 +67920,9 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
67750
67920
  const actual = clockLabelOf(row.dispatched_at, dayKey, crossFmt, t("recPrevDay"), t("recNextDay"));
67751
67921
  const deps = isPlanned ? plannedDeps ?? [] : resolvedDepsOf(snapshot);
67752
67922
  const hasOutputs = outputs.length > 0;
67923
+ const hasProcess = processOutputs.length > 0;
67753
67924
  const hasDeps = deps.length > 0;
67754
- const hasExpand = hasOutputs || hasDeps || !isPlanned;
67925
+ const hasExpand = hasOutputs || hasProcess || hasDeps || !isPlanned;
67755
67926
  /** token 三段之一:null 给占位(不编造 0)。 */
67756
67927
  const tokenPart = (v) => v === null ? "—" : formatTokenCount(v);
67757
67928
  /**
@@ -67865,7 +68036,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
67865
68036
  event.stopPropagation();
67866
68037
  if (canOpenFile) openFile?.(sid, path);
67867
68038
  }
67868
- }, (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
68039
+ }, path.endsWith("/") ? (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconFolderCloseRegular, { size: 16 }) : (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
67869
68040
  path,
67870
68041
  size: 16
67871
68042
  })))), outputs.length > 3 ? (0, react$1.createElement)("button", {
@@ -67894,7 +68065,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
67894
68065
  },
67895
68066
  "aria-expanded": open,
67896
68067
  style: { transform: open ? "rotate(180deg)" : "none" }
67897
- })) : null)), open && (hasOutputs || hasDeps || eventsError !== null || events !== null && events.length > 0) ? (0, react$1.createElement)("div", { className: "dsh-tdt-rec-exp" }, outputs.length === 0 ? null : (0, react$1.createElement)("div", { className: "dsh-tdt-rec-expouts" }, outputs.map((path) => (0, react$1.createElement)("button", {
68068
+ })) : null)), open && (hasOutputs || hasProcess || hasDeps || eventsError !== null || events !== null && events.length > 0) ? (0, react$1.createElement)("div", { className: "dsh-tdt-rec-exp" }, outputs.length === 0 ? null : (0, react$1.createElement)("div", { className: "dsh-tdt-rec-expouts" }, outputs.map((path) => (0, react$1.createElement)("button", {
67898
68069
  key: path,
67899
68070
  type: "button",
67900
68071
  title: path,
@@ -67903,7 +68074,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
67903
68074
  onClick: () => {
67904
68075
  if (canOpenFile) openFile?.(sid, path);
67905
68076
  }
67906
- }, (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
68077
+ }, path.endsWith("/") ? (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconFolderCloseRegular, { size: 14 }) : (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
67907
68078
  path,
67908
68079
  size: 14
67909
68080
  }), (0, react$1.createElement)(MarqueeText, {
@@ -67913,7 +68084,13 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
67913
68084
  maxWidth: "40ch",
67914
68085
  minWidth: 0
67915
68086
  }
67916
- })))), deps.length === 0 ? null : (0, react$1.createElement)("div", { className: "dsh-tdt-rec-depsec" }, (0, react$1.createElement)("div", { className: "dsh-tdt-rec-evtitle" }, t("listSectionDepends")), (0, react$1.createElement)("div", { className: "dsh-tdt-rec-depgrid" }, deps.map((dep, index) => {
68087
+ })))), (0, react$1.createElement)(ProcessFiles, {
68088
+ paths: processOutputs,
68089
+ onOpen: canOpenFile ? (path) => {
68090
+ openFile?.(sid, path);
68091
+ } : void 0,
68092
+ t
68093
+ }), deps.length === 0 ? null : (0, react$1.createElement)("div", { className: "dsh-tdt-rec-depsec" }, (0, react$1.createElement)("div", { className: "dsh-tdt-rec-evtitle" }, t("listSectionDepends")), (0, react$1.createElement)("div", { className: "dsh-tdt-rec-depgrid" }, deps.map((dep, index) => {
67917
68094
  const depOuts = Array.isArray(dep.outputs) ? dep.outputs : [];
67918
68095
  const depSid = dep.sessionId;
67919
68096
  const depHasSid = depSid !== null && depSid !== "";
@@ -67960,7 +68137,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
67960
68137
  event.stopPropagation();
67961
68138
  if (depCanOpen) openFile?.(depSid, path);
67962
68139
  }
67963
- }, (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
68140
+ }, path.endsWith("/") ? (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconFolderCloseRegular, { size: 16 }) : (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
67964
68141
  path,
67965
68142
  size: 16
67966
68143
  })))), depOuts.length > DEP_OUT_MAX ? (0, react$1.createElement)("button", {
@@ -68362,7 +68539,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
68362
68539
  *
68363
68540
  * ⚠️ 未来**没有实例行**(决策 31 懒建行:到点才 INSERT,不预建、不回看)⇒ 这里算出的是
68364
68541
  * 「按当前定义推算的计划」,任务改定义 / 停用后会随之变化 —— 是真实计算、不是模拟数据,
68365
- * UI 必须把它与「已发生」如实区分(AGENTS.md 第五条:正常功能一律真实取数,禁止模拟)。
68542
+ * UI 必须把它与「已发生」如实区分(仓库硬规则:正常功能一律真实取数,禁止模拟)。
68366
68543
  */
68367
68544
  function planEntriesByDay(tasks, from, to, now) {
68368
68545
  const out = /* @__PURE__ */ new Map();
@@ -69460,7 +69637,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
69460
69637
  /**
69461
69638
  * 任务卡片展开区三面板(决策 55):左下三个分段按钮(基础信息 / 执行记录 / 日志,默认基础信息),
69462
69639
  * 中间内容区三选一替换(统一最大高度滚动容器),右下按钮区(编辑任务 + 删除)。
69463
- * 数据全走 `client/query.ts` 真实取数(AGENTS.md 第五条,禁止 mock)。
69640
+ * 数据全走 `client/query.ts` 真实取数(仓库硬规则:禁止 mock)。
69464
69641
  */
69465
69642
  function TaskExpandPanel(props) {
69466
69643
  const { row, t, tt, scheduleLine, modelText, onEdit, onDelete, onRunNow, onOpenFile, onOpenSession, onViewTask, refresh } = props;
@@ -69723,7 +69900,8 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
69723
69900
  name: item.name,
69724
69901
  key: `${item.kind}:${item.name}`,
69725
69902
  path: item.path ?? null,
69726
- anchorSessionId: item.anchorSessionId ?? null
69903
+ anchorSessionId: item.anchorSessionId ?? null,
69904
+ isDir: item.isDir
69727
69905
  })),
69728
69906
  depends: row.depends.map((dep) => ({
69729
69907
  id: dep.id,
@@ -69879,7 +70057,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
69879
70057
  event.stopPropagation();
69880
70058
  if (canOpenFile && openFile !== void 0 && sid !== null) openFile(sid, output);
69881
70059
  }
69882
- }, (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
70060
+ }, output.endsWith("/") ? (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconFolderCloseRegular, { size: 16 }) : (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
69883
70061
  path: output,
69884
70062
  size: 16
69885
70063
  })))), outputs.length > 3 ? (0, react$1.createElement)("button", {
@@ -70208,7 +70386,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
70208
70386
  }) : null);
70209
70387
  }
70210
70388
  function TaskListView(props) {
70211
- const { t, rows, ready, onEdit, onViewTask, onDelete, onRunNow, onOpenFile, onOpenSession, onToggleEnabled, refresh, workspaces } = props;
70389
+ const { t, rows, ready, onEdit, onViewTask, onDelete, onRunNow, onOpenFile, onOpenSession, onToggleEnabled, refresh, workspaces, onCreate } = props;
70212
70390
  const tt = (0, react$1.useMemo)(() => interpolateTranslate(t), [t]);
70213
70391
  ensureTaskListStyle();
70214
70392
  ensureTaskInfoStyle();
@@ -70316,10 +70494,38 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
70316
70494
  size: "md",
70317
70495
  width: WS_WIDTH,
70318
70496
  marquee: true
70319
- }))), visible.length === 0 ? (0, react$1.createElement)("p", { style: {
70497
+ }))), visible.length === 0 ? rows.length === 0 && !ready ? null : rows.length === 0 ? (0, react$1.createElement)("div", { style: {
70498
+ display: "flex",
70499
+ flexDirection: "column",
70500
+ alignItems: "center",
70501
+ justifyContent: "center",
70502
+ margin: "24px auto 0",
70503
+ padding: "48px 32px",
70504
+ textAlign: "center",
70505
+ border: "1px dashed var(--tdt-border-heavy)",
70506
+ borderRadius: "var(--tdt-radius-md)",
70507
+ background: "var(--tdt-surface-1)"
70508
+ } }, (0, react$1.createElement)("div", { style: { color: "var(--tdt-fg-3)" } }, (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconAlarmClockOutlineRegular, { size: 40 })), (0, react$1.createElement)("div", { style: {
70509
+ marginTop: "12px",
70510
+ fontSize: "var(--tdt-font-lg)",
70511
+ fontWeight: 600
70512
+ } }, t("listEmptyTitle")), (0, react$1.createElement)("div", { style: {
70513
+ marginTop: "6px",
70514
+ maxWidth: "360px",
70515
+ fontSize: "var(--tdt-font-sm)",
70516
+ color: "var(--tdt-fg-2)",
70517
+ lineHeight: "var(--tdt-line-sm)"
70518
+ } }, t("listEmptyHint")), (0, react$1.createElement)(Button$2, {
70519
+ variant: "primary",
70520
+ size: "md",
70521
+ style: { marginTop: "16px" },
70522
+ onClick: () => {
70523
+ onCreate();
70524
+ }
70525
+ }, t("listEmptyAction"))) : (0, react$1.createElement)("p", { style: {
70320
70526
  ...metaStyle,
70321
70527
  marginTop: "8px"
70322
- } }, rows.length === 0 && !ready ? "" : rows.length === 0 ? t("listEmpty") : t("listEmptyFiltered")) : (0, react$1.createElement)("div", { style: { position: "relative" } }, visible.map((row) => (0, react$1.createElement)(TaskCard, {
70528
+ } }, t("listEmptyFiltered")) : (0, react$1.createElement)("div", { style: { position: "relative" } }, visible.map((row) => (0, react$1.createElement)(TaskCard, {
70323
70529
  key: row.id,
70324
70530
  row,
70325
70531
  t,
@@ -71365,7 +71571,10 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
71365
71571
  const avail = Math.min(Math.floor(window.innerWidth * PREVIEW_MAX_RATIO), window.innerWidth - 760 - reserved);
71366
71572
  return Math.min(Math.max(Math.round(value), PREVIEW_MIN), Math.max(PREVIEW_MIN, Math.floor(avail)));
71367
71573
  }
71368
- /** 实例行的产出物(决策 32③写回的 outputs 列:JSON 数组,兼容逗号串)。 */
71574
+ /**
71575
+ * 实例行的产出物(决策 32③写回的列:JSON 数组,兼容逗号串)。
71576
+ * 2026-10-10 起 `outputs`(主文件)与 `process_outputs`(过程文件)**同一形状**,共用这一个解析器。
71577
+ */
71369
71578
  function parseOutputs(raw) {
71370
71579
  if (typeof raw === "string" && raw.trim() !== "") {
71371
71580
  try {
@@ -71457,11 +71666,14 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
71457
71666
  const [saving, setSaving] = (0, react$1.useState)(false);
71458
71667
  const [failed, setFailed] = (0, react$1.useState)(null);
71459
71668
  const [failedKey, setFailedKey] = (0, react$1.useState)(0);
71460
- const [savedToast, setSavedToast] = (0, react$1.useState)(0);
71461
- const savedSeq = (0, react$1.useRef)(0);
71462
- const notifySaved = () => {
71463
- savedSeq.current += 1;
71464
- setSavedToast(savedSeq.current);
71669
+ const [successToast, setSuccessToast] = (0, react$1.useState)(null);
71670
+ const successSeq = (0, react$1.useRef)(0);
71671
+ const notify = (text) => {
71672
+ successSeq.current += 1;
71673
+ setSuccessToast({
71674
+ text,
71675
+ seq: successSeq.current
71676
+ });
71465
71677
  };
71466
71678
  const [invalidToast, setInvalidToast] = (0, react$1.useState)({
71467
71679
  on: false,
@@ -71729,7 +71941,8 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
71729
71941
  /**
71730
71942
  * 删除任务(决策 55,卡片右下角快捷删除):DELETE /tasks { id }。
71731
71943
  * 服务端语义:摘定义 + 整删任务目录(附件 / 版本 / 快照),实例 / 事件保留做审计;
71732
- * 成功后 overview.refresh() 让列表立刻少一行。失败走 viewErr 条(操作类失败留时间读)。
71944
+ * 成功后 overview.refresh() 让列表立刻少一行;若右侧正在编辑 / 查看该任务则一并关窗;
71945
+ * 失败走 viewErr 条(操作类失败留时间读)。
71733
71946
  */
71734
71947
  const deleteTask = async (id) => {
71735
71948
  try {
@@ -71745,6 +71958,8 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
71745
71958
  return message;
71746
71959
  }
71747
71960
  overview.refresh();
71961
+ if (editor?.id === id) setEditor(null);
71962
+ notify(t("editorTaskDeleted"));
71748
71963
  return null;
71749
71964
  } catch (error) {
71750
71965
  const message = error instanceof Error ? error.message : String(error);
@@ -71803,7 +72018,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
71803
72018
  }
71804
72019
  const missing = Array.isArray(body.missingAttachments) ? body.missingAttachments.filter((item) => typeof item === "string") : [];
71805
72020
  setEditor(null);
71806
- notifySaved();
72021
+ notify(t("editorTaskSaved"));
71807
72022
  if (typeof body.id === "string" && body.id !== "") overview.patchRow(body.id, rowPatchOf(definition));
71808
72023
  overview.refresh();
71809
72024
  if (missing.length > 0) setViewErr(`已保存,但以下附加文件已不在盘上,请重新上传:${missing.join("、")}`);
@@ -71829,6 +72044,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
71829
72044
  }
71830
72045
  setEditor(null);
71831
72046
  overview.refresh();
72047
+ notify(t("editorTaskDeleted"));
71832
72048
  } catch (error) {
71833
72049
  setEditorError(humanizeTaskError(error instanceof Error ? error.message : String(error)));
71834
72050
  }
@@ -72024,6 +72240,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
72024
72240
  view: target,
72025
72241
  didUnarchive,
72026
72242
  outputs: row === null ? void 0 : parseOutputs(row.outputs),
72243
+ processOutputs: row === null ? void 0 : parseOutputs(row.process_outputs),
72027
72244
  upstream: upstreamOf(row?.snapshot ?? null),
72028
72245
  attached: row === null ? [] : attachmentsOf(row.snapshot ?? null, row.attachmentPaths),
72029
72246
  workspacePath: workspacePathOf(row?.snapshot ?? null)
@@ -72127,7 +72344,8 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
72127
72344
  onToggleEnabled: toggleTaskEnabled,
72128
72345
  onRunNow: runTaskNow,
72129
72346
  onViewTask: openViewer,
72130
- workspaces: editorOptions.workspaces
72347
+ workspaces: editorOptions.workspaces,
72348
+ onCreate: openCreate
72131
72349
  }) : tab === "settings" ? (0, react$1.createElement)(SettingsPage, { t }) : data === void 0 ? (0, react$1.createElement)("div", null, (0, react$1.createElement)("p", { style: hintStyle }, hasRaw ? t("debugRaw") : t("debugEmpty")), hasRaw ? (0, react$1.createElement)("pre", { style: preStyle }, raw) : null) : null, (0, react$1.createElement)(BackToTop, null)), viewing !== null ? (0, react$1.createElement)(SessionViewModal, {
72132
72350
  t,
72133
72351
  heading: viewing.heading,
@@ -72138,6 +72356,7 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
72138
72356
  sessionId: viewing.sessionId,
72139
72357
  view: viewing.view,
72140
72358
  outputs: viewing.outputs,
72359
+ processOutputs: viewing.processOutputs,
72141
72360
  forkSession: forkSession ?? void 0,
72142
72361
  openHostSession: openHostSession ?? void 0,
72143
72362
  workspaceFiles: workspaceFiles ?? void 0,
@@ -72163,6 +72382,14 @@ button.dsh-tdt-sv-tfc-file:focus-visible{box-shadow:inset 0 0 0 2px var(--tdt-fo
72163
72382
  onDone: () => {
72164
72383
  setViewErr(null);
72165
72384
  }
72385
+ }) : null, successToast !== null ? (0, react$1.createElement)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
72386
+ key: successToast.seq,
72387
+ text: successToast.text,
72388
+ tone: "success",
72389
+ holdMs: 2500,
72390
+ onDone: () => {
72391
+ setSuccessToast(null);
72392
+ }
72166
72393
  }) : null, editor !== null ? (0, react$1.createElement)(TaskEditorDrawer, {
72167
72394
  key: editor.id,
72168
72395
  t,