@linxin666/dsh-client-ui-task-board 0.4.0 → 0.4.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/README.i18n.yaml +2 -2
- package/README.md +24 -0
- package/README.zh.md +24 -0
- package/lib/client.js +917 -81
- package/lib/client.js.map +1 -1
- package/lib/index.js +1774 -1420
- package/lib/types/client/TaskBoardSettingsCard.d.ts +4 -0
- package/lib/types/client/board/LinkSubtaskModal.d.ts +11 -0
- package/lib/types/client/board/NewTaskModal.d.ts +7 -1
- package/lib/types/client/board/TaskCard.d.ts +11 -1
- package/lib/types/client/locales.d.ts +33 -0
- package/lib/types/core/controller.d.ts +30 -1
- package/lib/types/core/subtask.d.ts +122 -0
- package/lib/types/core/tasks.d.ts +46 -3
- package/lib/types/core/use-cases/task-archive.d.ts +10 -13
- package/lib/types/core/use-cases/task-create.d.ts +13 -4
- package/lib/types/core/use-cases/task-delete.d.ts +7 -1
- package/lib/types/core/use-cases/task-parent.d.ts +21 -0
- package/lib/types/core/use-cases/task-update.d.ts +1 -1
- package/lib/types/host/agent-tools.d.ts +27 -0
- package/lib/types/host-ledger.d.ts +88 -4
- package/lib/types/host-runner.d.ts +39 -1
- package/lib/types/host-service.d.ts +47 -0
- package/lib/types/index.d.ts +83 -5
- package/lib/types/protocol.d.ts +14 -0
- package/package.json +23 -21
- package/src/client/TaskBoardSettingsCard.tsx +48 -3
- package/src/client/board/LinkSubtaskModal.tsx +82 -0
- package/src/client/board/NewTaskModal.tsx +40 -6
- package/src/client/board/TaskBoard.tsx +88 -8
- package/src/client/board/TaskCard.tsx +52 -2
- package/src/client/board/TaskDetail.tsx +124 -1
- package/src/client/board.module.css +96 -0
- package/src/client/host-api.ts +14 -4
- package/src/client/index.ts +11 -1
- package/src/client/locales.ts +70 -2
- package/src/core/controller.ts +69 -8
- package/src/core/store.ts +15 -0
- package/src/core/subtask.ts +286 -0
- package/src/core/tasks.ts +50 -2
- package/src/core/use-cases/task-archive.ts +46 -12
- package/src/core/use-cases/task-create.ts +42 -7
- package/src/core/use-cases/task-delete.ts +11 -1
- package/src/core/use-cases/task-parent.ts +64 -0
- package/src/core/use-cases/task-update.ts +3 -1
- package/src/host/agent-tools.ts +643 -0
- package/src/host-ledger.ts +323 -45
- package/src/host-runner.ts +141 -12
- package/src/host-service.ts +127 -10
- package/src/index.ts +181 -9
- package/src/protocol.ts +26 -2
package/lib/client.js
CHANGED
|
@@ -149,6 +149,7 @@ window.__ModuleLoader__.load({
|
|
|
149
149
|
title: input.title.trim(),
|
|
150
150
|
description: input.description.trim(),
|
|
151
151
|
prompt: input.prompt.trim(),
|
|
152
|
+
parentId: normalizeTargetId(input.parentId),
|
|
152
153
|
status: "todo",
|
|
153
154
|
createdAt: now,
|
|
154
155
|
updatedAt: now,
|
|
@@ -158,6 +159,7 @@ window.__ModuleLoader__.load({
|
|
|
158
159
|
permission: isTaskPermission(input.permission) ? input.permission : void 0,
|
|
159
160
|
model: normalizeTargetId(input.model),
|
|
160
161
|
reuseSession: input.reuseSession === true ? true : void 0,
|
|
162
|
+
teamRun: input.teamRun === true ? true : void 0,
|
|
161
163
|
...input.freeze === void 0 ? {} : { freeze: freezeOf(input.freeze, now) },
|
|
162
164
|
...input.handover === void 0 ? {} : { handover: {
|
|
163
165
|
...input.handover,
|
|
@@ -205,20 +207,173 @@ window.__ModuleLoader__.load({
|
|
|
205
207
|
if (execution.result === "cancelled") return "cancelled";
|
|
206
208
|
return "running";
|
|
207
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Clamp a configured depth into the supported range. Anything unusable
|
|
212
|
+
* (absent, non-numeric, not finite) falls back to the default, so a bad
|
|
213
|
+
* configuration narrows the tree instead of lifting the guard.
|
|
214
|
+
*/
|
|
215
|
+
function normalizeSubtaskDepth(value) {
|
|
216
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 1;
|
|
217
|
+
const depth = Math.trunc(value);
|
|
218
|
+
if (depth < 1) return 1;
|
|
219
|
+
if (depth > 3) return 3;
|
|
220
|
+
return depth;
|
|
221
|
+
}
|
|
222
|
+
/** Whether an unknown value is a settled execution outcome. */
|
|
223
|
+
function isExecutionOutcome(value) {
|
|
224
|
+
return value === "succeeded" || value === "failed" || value === "cancelled";
|
|
225
|
+
}
|
|
226
|
+
function indexOf(tasks) {
|
|
227
|
+
return new Map(tasks.map((task) => [task.id, task]));
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Nearest-first ancestor chain of a task. The walk is cycle-safe and bounded
|
|
231
|
+
* by the ledger size rather than by the deployment maximum: a chain stored
|
|
232
|
+
* under a larger limit survives that limit being lowered, and restore and
|
|
233
|
+
* inheritance must still see all of it.
|
|
234
|
+
*/
|
|
235
|
+
function ancestorChain(tasks, task) {
|
|
236
|
+
const index = indexOf(tasks);
|
|
237
|
+
const chain = [];
|
|
238
|
+
const seen = /* @__PURE__ */ new Set([task.id]);
|
|
239
|
+
let current = task.parentId === void 0 ? void 0 : index.get(task.parentId);
|
|
240
|
+
while (current !== void 0 && chain.length < tasks.length) {
|
|
241
|
+
if (seen.has(current.id)) break;
|
|
242
|
+
seen.add(current.id);
|
|
243
|
+
chain.push(current);
|
|
244
|
+
current = current.parentId === void 0 ? void 0 : index.get(current.parentId);
|
|
245
|
+
}
|
|
246
|
+
return chain;
|
|
247
|
+
}
|
|
248
|
+
/** Root-to-task depth: a root task is 0, its subtask 1, and so on. */
|
|
249
|
+
function taskDepth(tasks, id) {
|
|
250
|
+
const task = indexOf(tasks).get(id);
|
|
251
|
+
return task === void 0 ? 0 : ancestorChain(tasks, task).length;
|
|
252
|
+
}
|
|
253
|
+
/** Direct subtasks of a task, in ledger order. */
|
|
254
|
+
function directSubtasks(tasks, id) {
|
|
255
|
+
return tasks.filter((task) => task.parentId === id);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Descendants of a task in breadth-first order, bounded by maxSubtaskDepth
|
|
259
|
+
* total depth. The visited set keeps a malformed ledger from looping.
|
|
260
|
+
*/
|
|
261
|
+
function descendantTasks(tasks, id, maxSubtaskDepth) {
|
|
262
|
+
const visited = /* @__PURE__ */ new Set([id]);
|
|
263
|
+
const found = [];
|
|
264
|
+
let frontier = [id];
|
|
265
|
+
for (let depth = 1; depth <= maxSubtaskDepth && frontier.length > 0; depth += 1) {
|
|
266
|
+
const next = [];
|
|
267
|
+
for (const parentId of frontier) for (const task of tasks) {
|
|
268
|
+
if (task.parentId !== parentId || visited.has(task.id)) continue;
|
|
269
|
+
visited.add(task.id);
|
|
270
|
+
found.push(task);
|
|
271
|
+
next.push(task.id);
|
|
272
|
+
}
|
|
273
|
+
frontier = next;
|
|
274
|
+
}
|
|
275
|
+
return found;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Height of a task's own subtree (0 for a leaf), bounded by the hard maximum.
|
|
279
|
+
* The visited set makes the walk terminate on a cyclic hand-edited ledger
|
|
280
|
+
* instead of recursing until the stack overflows.
|
|
281
|
+
*/
|
|
282
|
+
function subtreeHeight(tasks, id, visited = /* @__PURE__ */ new Set()) {
|
|
283
|
+
if (visited.has(id)) return 0;
|
|
284
|
+
const seen = /* @__PURE__ */ new Set([...visited, id]);
|
|
285
|
+
const children = directSubtasks(tasks, id);
|
|
286
|
+
if (children.length === 0) return 0;
|
|
287
|
+
let height = 0;
|
|
288
|
+
for (const child of children) height = Math.max(height, 1 + subtreeHeight(tasks, child.id, seen));
|
|
289
|
+
return Math.min(height, 3);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Whether childId may be attached under parentId. A null parent detaches and
|
|
293
|
+
* is always allowed for a known task. Otherwise the link must name an
|
|
294
|
+
* on-board task that is neither the child itself nor one of its descendants,
|
|
295
|
+
* and the resulting depth (parent depth + 1 + the child own subtree height)
|
|
296
|
+
* must stay within maxSubtaskDepth.
|
|
297
|
+
*/
|
|
298
|
+
function checkParentLink(tasks, childId, parentId, maxSubtaskDepth) {
|
|
299
|
+
if (tasks.find((task) => task.id === childId) === void 0) return {
|
|
300
|
+
ok: false,
|
|
301
|
+
reason: "unknown-task"
|
|
302
|
+
};
|
|
303
|
+
if (parentId === null || parentId === "") return { ok: true };
|
|
304
|
+
if (parentId === childId) return {
|
|
305
|
+
ok: false,
|
|
306
|
+
reason: "self-parent"
|
|
307
|
+
};
|
|
308
|
+
const parent = tasks.find((task) => task.id === parentId);
|
|
309
|
+
if (parent === void 0) return {
|
|
310
|
+
ok: false,
|
|
311
|
+
reason: "unknown-parent"
|
|
312
|
+
};
|
|
313
|
+
if (parent.archivedAt !== void 0) return {
|
|
314
|
+
ok: false,
|
|
315
|
+
reason: "archived-parent"
|
|
316
|
+
};
|
|
317
|
+
if (descendantTasks(tasks, childId, 3).some((task) => task.id === parentId)) return {
|
|
318
|
+
ok: false,
|
|
319
|
+
reason: "cycle"
|
|
320
|
+
};
|
|
321
|
+
if (taskDepth(tasks, parentId) + 1 + subtreeHeight(tasks, childId) > maxSubtaskDepth) return {
|
|
322
|
+
ok: false,
|
|
323
|
+
reason: "depth-exceeded"
|
|
324
|
+
};
|
|
325
|
+
return { ok: true };
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* The permission a task would actually run under: its handover bundle's pin
|
|
329
|
+
* wins over the plain pin field, exactly as the runner resolves it.
|
|
330
|
+
*/
|
|
331
|
+
function effectiveTaskPermission(task) {
|
|
332
|
+
return task.handover?.permission ?? task.permission;
|
|
333
|
+
}
|
|
208
334
|
//#endregion
|
|
209
335
|
//#region src/core/use-cases/task-archive.ts
|
|
210
336
|
/**
|
|
211
|
-
* Archive
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
337
|
+
* Archive/restore task use case: move a task off the main board and back,
|
|
338
|
+
* whatever its status but running. The task keeps its status, execution
|
|
339
|
+
* history, and transcript references, while archiving disarms any schedule so
|
|
340
|
+
* it cannot create more execution records until the user restores and
|
|
341
|
+
* re-enables it.
|
|
342
|
+
*
|
|
343
|
+
* Subtask trees move as a unit: archiving takes the whole subtree (an on-board
|
|
344
|
+
* subtask under an archived parent would be unreachable from its parent) and
|
|
345
|
+
* refuses the whole group while any member is running. Restoring brings the
|
|
346
|
+
* task, its ancestors and its subtree back together.
|
|
215
347
|
*/
|
|
216
|
-
|
|
348
|
+
/** The archive/restore id set: the task, its subtree, and (for restore) its ancestors. */
|
|
349
|
+
function groupIds(tasks, id, maxSubtaskDepth, withAncestors) {
|
|
350
|
+
const target = tasks.find((task) => task.id === id);
|
|
351
|
+
const ids = /* @__PURE__ */ new Set([id]);
|
|
352
|
+
for (const task of descendantTasks(tasks, id, normalizeSubtaskDepth(maxSubtaskDepth))) ids.add(task.id);
|
|
353
|
+
if (withAncestors && target !== void 0) for (const ancestor of ancestorChain(tasks, target)) ids.add(ancestor.id);
|
|
354
|
+
return ids;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Archive one task and its subtasks: only a running member keeps the group on
|
|
358
|
+
* the board (its runner still owns its lifecycle until the execution settles);
|
|
359
|
+
* any other status can be archived. Archiving disarms schedules; an
|
|
360
|
+
* already-archived task is a no-op.
|
|
361
|
+
*/
|
|
362
|
+
function applyArchiveTask(tasks, id, now, maxSubtaskDepth = 1) {
|
|
363
|
+
const target = tasks.find((task) => task.id === id);
|
|
364
|
+
if (target === void 0 || target.archivedAt !== void 0) return {
|
|
365
|
+
tasks,
|
|
366
|
+
archived: false
|
|
367
|
+
};
|
|
368
|
+
const ids = groupIds(tasks, id, maxSubtaskDepth, false);
|
|
369
|
+
if (tasks.some((task) => ids.has(task.id) && !ARCHIVABLE_STATUSES.includes(task.status))) return {
|
|
370
|
+
tasks,
|
|
371
|
+
archived: false
|
|
372
|
+
};
|
|
217
373
|
let applied = false;
|
|
218
374
|
return {
|
|
219
375
|
tasks: tasks.map((task) => {
|
|
220
|
-
if (task.id
|
|
221
|
-
if (!ARCHIVABLE_STATUSES.includes(task.status)) return task;
|
|
376
|
+
if (!ids.has(task.id) || task.archivedAt !== void 0) return task;
|
|
222
377
|
applied = true;
|
|
223
378
|
const schedule = task.schedule === void 0 ? void 0 : {
|
|
224
379
|
...task.schedule,
|
|
@@ -235,12 +390,21 @@ window.__ModuleLoader__.load({
|
|
|
235
390
|
archived: applied
|
|
236
391
|
};
|
|
237
392
|
}
|
|
238
|
-
/**
|
|
239
|
-
|
|
393
|
+
/**
|
|
394
|
+
* Restore one task back onto the main board, together with its ancestors (an
|
|
395
|
+
* on-board subtask must not point at an archived parent) and its subtasks.
|
|
396
|
+
*/
|
|
397
|
+
function applyRestoreTask(tasks, id, now, maxSubtaskDepth = 1) {
|
|
398
|
+
const target = tasks.find((task) => task.id === id);
|
|
399
|
+
if (target === void 0 || target.archivedAt === void 0) return {
|
|
400
|
+
tasks,
|
|
401
|
+
archived: false
|
|
402
|
+
};
|
|
403
|
+
const ids = groupIds(tasks, id, maxSubtaskDepth, true);
|
|
240
404
|
let applied = false;
|
|
241
405
|
return {
|
|
242
406
|
tasks: tasks.map((task) => {
|
|
243
|
-
if (task.id
|
|
407
|
+
if (!ids.has(task.id) || task.archivedAt === void 0) return task;
|
|
244
408
|
applied = true;
|
|
245
409
|
const { archivedAt: _archived, ...rest } = task;
|
|
246
410
|
return {
|
|
@@ -424,23 +588,57 @@ window.__ModuleLoader__.load({
|
|
|
424
588
|
//#region src/core/use-cases/task-create.ts
|
|
425
589
|
/**
|
|
426
590
|
* Create-task use case: mint a new task from user input, rejecting a blank
|
|
427
|
-
* title
|
|
428
|
-
* orchestrates those), so it is
|
|
591
|
+
* title and any parent link the lineage gate refuses. Pure ledger transition
|
|
592
|
+
* (no persistence or notify - the controller orchestrates those), so it is
|
|
593
|
+
* unit-testable without any runtime face.
|
|
429
594
|
*/
|
|
430
595
|
/**
|
|
431
596
|
* Apply a create against the current ledger. Returns the new task and the
|
|
432
|
-
* appended ledger, or the unchanged ledger when the title is blank
|
|
597
|
+
* appended ledger, or the unchanged ledger when the title is blank or the
|
|
598
|
+
* requested parent link is refused.
|
|
599
|
+
*
|
|
600
|
+
* A subtask inherits the execution targets its parent already pins (workspace,
|
|
601
|
+
* agent preset, permission, model) unless the form supplied its own; the
|
|
602
|
+
* parent human permission confirmation travels with an inherited permission,
|
|
603
|
+
* so a subtask of an already-confirmed elevated card is runnable at once.
|
|
433
604
|
* @param tasks - current ledger.
|
|
434
|
-
* @param input - raw user input (title/description/prompt + optional schedule).
|
|
605
|
+
* @param input - raw user input (title/description/prompt + optional schedule/parent).
|
|
435
606
|
* @param now - clock instant (ms epoch).
|
|
436
607
|
* @param id - minted task id.
|
|
608
|
+
* @param maxSubtaskDepth - deployment subtask depth limit.
|
|
437
609
|
*/
|
|
438
|
-
function applyCreateTask(tasks, input, now, id) {
|
|
610
|
+
function applyCreateTask(tasks, input, now, id, maxSubtaskDepth = 1) {
|
|
439
611
|
if (input.title.trim() === "") return {
|
|
440
612
|
task: void 0,
|
|
441
|
-
tasks
|
|
613
|
+
tasks,
|
|
614
|
+
error: "title is required"
|
|
442
615
|
};
|
|
443
|
-
|
|
616
|
+
const limit = normalizeSubtaskDepth(maxSubtaskDepth);
|
|
617
|
+
let parent;
|
|
618
|
+
if (input.parentId !== void 0) {
|
|
619
|
+
parent = tasks.find((task) => task.id === input.parentId);
|
|
620
|
+
if (parent === void 0) return {
|
|
621
|
+
task: void 0,
|
|
622
|
+
tasks,
|
|
623
|
+
error: "parent task not found"
|
|
624
|
+
};
|
|
625
|
+
if (parent.archivedAt !== void 0) return {
|
|
626
|
+
task: void 0,
|
|
627
|
+
tasks,
|
|
628
|
+
error: "parent task is archived"
|
|
629
|
+
};
|
|
630
|
+
if (taskDepth(tasks, parent.id) + 1 > limit) return {
|
|
631
|
+
task: void 0,
|
|
632
|
+
tasks,
|
|
633
|
+
error: `subtask depth limit (${limit}) exceeded`
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
let task = createTask(parent === void 0 ? input : {
|
|
637
|
+
...input,
|
|
638
|
+
workspaceId: input.workspaceId ?? parent.workspaceId,
|
|
639
|
+
mode: input.mode ?? parent.mode,
|
|
640
|
+
model: input.model ?? parent.model
|
|
641
|
+
}, now, id);
|
|
444
642
|
const requested = input.schedule;
|
|
445
643
|
if (requested?.enabled === true && requested.cron.trim() !== "" && isValidCron(requested.cron)) {
|
|
446
644
|
const cron = requested.cron.trim();
|
|
@@ -465,9 +663,15 @@ window.__ModuleLoader__.load({
|
|
|
465
663
|
* @param id - the task to remove.
|
|
466
664
|
*/
|
|
467
665
|
function applyDeleteTask(tasks, selectedTaskId, id) {
|
|
666
|
+
if (tasks.some((task) => task.parentId === id)) return {
|
|
667
|
+
tasks,
|
|
668
|
+
selectionCleared: false,
|
|
669
|
+
blocked: true
|
|
670
|
+
};
|
|
468
671
|
return {
|
|
469
672
|
tasks: tasks.filter((task) => task.id !== id),
|
|
470
|
-
selectionCleared: selectedTaskId === id
|
|
673
|
+
selectionCleared: selectedTaskId === id,
|
|
674
|
+
blocked: false
|
|
471
675
|
};
|
|
472
676
|
}
|
|
473
677
|
//#endregion
|
|
@@ -531,6 +735,70 @@ window.__ModuleLoader__.load({
|
|
|
531
735
|
}, now) : task);
|
|
532
736
|
}
|
|
533
737
|
//#endregion
|
|
738
|
+
//#region src/core/use-cases/task-parent.ts
|
|
739
|
+
/**
|
|
740
|
+
* Parent-link use case: attach an existing on-board task under another as a
|
|
741
|
+
* subtask, or detach it back to the root. The lineage gate is the same one
|
|
742
|
+
* creation uses (unknown or archived parent, self, cycle, depth), so the two
|
|
743
|
+
* ways into the tree cannot disagree. Pure ledger transition.
|
|
744
|
+
*/
|
|
745
|
+
/** Host error text per refusal reason (the wire carries the same strings). */
|
|
746
|
+
const REJECTION_TEXT = {
|
|
747
|
+
"unknown-task": "task not found",
|
|
748
|
+
"unknown-parent": "parent task not found",
|
|
749
|
+
"archived-parent": "parent task is archived",
|
|
750
|
+
"self-parent": "a task cannot be its own parent",
|
|
751
|
+
"cycle": "a task cannot be attached under its own subtask",
|
|
752
|
+
"depth-exceeded": "subtask depth limit exceeded"
|
|
753
|
+
};
|
|
754
|
+
/**
|
|
755
|
+
* Attach or detach one task. A null (or blank) parent detaches, which is always
|
|
756
|
+
* allowed for a known on-board task; attaching validates the whole chain so a
|
|
757
|
+
* cyclic or too-deep ledger can never be stored.
|
|
758
|
+
* @param tasks - current ledger.
|
|
759
|
+
* @param id - the task being moved.
|
|
760
|
+
* @param parentId - the new parent, or null to detach.
|
|
761
|
+
* @param now - clock instant (ms epoch).
|
|
762
|
+
* @param maxSubtaskDepth - deployment subtask depth limit.
|
|
763
|
+
*/
|
|
764
|
+
function applySetParent(tasks, id, parentId, now, maxSubtaskDepth = 1) {
|
|
765
|
+
const task = tasks.find((candidate) => candidate.id === id);
|
|
766
|
+
if (task === void 0) return {
|
|
767
|
+
tasks,
|
|
768
|
+
applied: false,
|
|
769
|
+
error: REJECTION_TEXT["unknown-task"]
|
|
770
|
+
};
|
|
771
|
+
if (task.archivedAt !== void 0) return {
|
|
772
|
+
tasks,
|
|
773
|
+
applied: false,
|
|
774
|
+
error: "archived task is read-only"
|
|
775
|
+
};
|
|
776
|
+
if (task.status === "running" || task.executions.some((execution) => execution.endedAt === void 0)) return {
|
|
777
|
+
tasks,
|
|
778
|
+
applied: false,
|
|
779
|
+
error: "running task cannot be re-parented"
|
|
780
|
+
};
|
|
781
|
+
const check = checkParentLink(tasks, id, parentId, normalizeSubtaskDepth(maxSubtaskDepth));
|
|
782
|
+
if (!check.ok) return {
|
|
783
|
+
tasks,
|
|
784
|
+
applied: false,
|
|
785
|
+
error: REJECTION_TEXT[check.reason ?? "unknown-task"]
|
|
786
|
+
};
|
|
787
|
+
const nextParentId = parentId === null || parentId === "" ? void 0 : parentId;
|
|
788
|
+
if (nextParentId === task.parentId) return {
|
|
789
|
+
tasks: [...tasks],
|
|
790
|
+
applied: true
|
|
791
|
+
};
|
|
792
|
+
return {
|
|
793
|
+
tasks: tasks.map((candidate) => candidate.id === id ? {
|
|
794
|
+
...candidate,
|
|
795
|
+
parentId: nextParentId,
|
|
796
|
+
updatedAt: now
|
|
797
|
+
} : candidate),
|
|
798
|
+
applied: true
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
//#endregion
|
|
534
802
|
//#region src/core/use-cases/task-update.ts
|
|
535
803
|
/**
|
|
536
804
|
* Update-task use case: apply an editable-field patch (title/description/
|
|
@@ -599,6 +867,7 @@ window.__ModuleLoader__.load({
|
|
|
599
867
|
if ("tags" in patch) next.tags = tagsPatch == null ? void 0 : normalizeTags(tagsPatch);
|
|
600
868
|
if ("permission" in patch && patch.permission !== void 0 && patch.permission !== task.permission || "handover" in patch) next.permissionConfirmedAt = void 0;
|
|
601
869
|
if ("reuseSession" in patch) next.reuseSession = patch.reuseSession === true ? true : void 0;
|
|
870
|
+
if ("teamRun" in patch) next.teamRun = patch.teamRun === true ? true : void 0;
|
|
602
871
|
if (workspaceId !== void 0 || "workspaceId" in patch) next.workspaceId = workspaceId;
|
|
603
872
|
if (mode !== void 0 || "mode" in patch) next.mode = mode;
|
|
604
873
|
if (permission !== void 0 || "permission" in patch) next.permission = permission;
|
|
@@ -746,7 +1015,7 @@ window.__ModuleLoader__.load({
|
|
|
746
1015
|
}
|
|
747
1016
|
createTask(input) {
|
|
748
1017
|
const id = this.uuid();
|
|
749
|
-
const { task, tasks } = applyCreateTask(this.tasks, input, this.now(), id);
|
|
1018
|
+
const { task, tasks } = applyCreateTask(this.tasks, input, this.now(), id, this.mirrorDepth());
|
|
750
1019
|
if (task === void 0) return void 0;
|
|
751
1020
|
this.tasks = [...tasks];
|
|
752
1021
|
this.persistAndNotify();
|
|
@@ -756,7 +1025,7 @@ window.__ModuleLoader__.load({
|
|
|
756
1025
|
async createTaskConfirmed(input) {
|
|
757
1026
|
if (this.deps.transport === void 0) return this.createTask(input);
|
|
758
1027
|
const id = this.uuid();
|
|
759
|
-
if (applyCreateTask(this.tasks, input, this.now(), id).task === void 0) return void 0;
|
|
1028
|
+
if (applyCreateTask(this.tasks, input, this.now(), id, this.mirrorDepth()).task === void 0) return void 0;
|
|
760
1029
|
return await this.commitRemote({
|
|
761
1030
|
kind: "create",
|
|
762
1031
|
id,
|
|
@@ -850,7 +1119,7 @@ window.__ModuleLoader__.load({
|
|
|
850
1119
|
* @returns true when applied.
|
|
851
1120
|
*/
|
|
852
1121
|
archiveTask(id) {
|
|
853
|
-
const { tasks, archived } = applyArchiveTask(this.tasks, id, this.now());
|
|
1122
|
+
const { tasks, archived } = applyArchiveTask(this.tasks, id, this.now(), this.mirrorDepth());
|
|
854
1123
|
if (!archived) return false;
|
|
855
1124
|
if (this.deps.transport !== void 0) {
|
|
856
1125
|
this.commitRemote({
|
|
@@ -863,9 +1132,30 @@ window.__ModuleLoader__.load({
|
|
|
863
1132
|
this.persistAndNotify();
|
|
864
1133
|
return true;
|
|
865
1134
|
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Attach an existing task under a parent (or detach it with a null parent)
|
|
1137
|
+
* through the Host. The lineage gate — parent exists and is on-board, no
|
|
1138
|
+
* cycle, depth within the deployment limit — belongs to the Host; a refusal
|
|
1139
|
+
* surfaces through the transport error like every other rejected action.
|
|
1140
|
+
* @returns true when the link was accepted by the authority.
|
|
1141
|
+
*/
|
|
1142
|
+
async setParent(id, parentId) {
|
|
1143
|
+
if (this.deps.transport === void 0) {
|
|
1144
|
+
const result = applySetParent(this.tasks, id, parentId, this.now(), this.mirrorDepth());
|
|
1145
|
+
if (!result.applied) return false;
|
|
1146
|
+
this.tasks = [...result.tasks];
|
|
1147
|
+
this.persistAndNotify();
|
|
1148
|
+
return true;
|
|
1149
|
+
}
|
|
1150
|
+
return await this.commitRemote({
|
|
1151
|
+
kind: "set-parent",
|
|
1152
|
+
taskId: id,
|
|
1153
|
+
parentId
|
|
1154
|
+
}, id);
|
|
1155
|
+
}
|
|
866
1156
|
/** Restore an archived task back onto the board (same status column). */
|
|
867
1157
|
restoreTask(id) {
|
|
868
|
-
const { tasks, archived } = applyRestoreTask(this.tasks, id, this.now());
|
|
1158
|
+
const { tasks, archived } = applyRestoreTask(this.tasks, id, this.now(), this.mirrorDepth());
|
|
869
1159
|
if (!archived) return false;
|
|
870
1160
|
if (this.deps.transport !== void 0) {
|
|
871
1161
|
this.commitRemote({
|
|
@@ -1057,6 +1347,7 @@ window.__ModuleLoader__.load({
|
|
|
1057
1347
|
onRemoteEvent(event) {
|
|
1058
1348
|
if (event !== void 0 && this.hostState !== void 0 && event.revision === this.hostState.revision && typeof event.scheduler === "object" && event.scheduler !== null && typeof event.power === "object" && event.power !== null) {
|
|
1059
1349
|
this.hostState = {
|
|
1350
|
+
...this.hostState,
|
|
1060
1351
|
revision: event.revision,
|
|
1061
1352
|
scheduler: event.scheduler,
|
|
1062
1353
|
power: event.power
|
|
@@ -1066,6 +1357,35 @@ window.__ModuleLoader__.load({
|
|
|
1066
1357
|
}
|
|
1067
1358
|
this.refreshRemote();
|
|
1068
1359
|
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Deployment subtask depth limit the browser mirrors from the last Host
|
|
1362
|
+
* snapshot; the fallback is the deployment default for the legacy path (no
|
|
1363
|
+
* transport) and for the window before the first snapshot arrives.
|
|
1364
|
+
*/
|
|
1365
|
+
mirrorDepth() {
|
|
1366
|
+
return this.hostState?.maxSubtaskDepth ?? 1;
|
|
1367
|
+
}
|
|
1368
|
+
/**
|
|
1369
|
+
* Project a Host snapshot onto the browser's mirror. SSE frames and partial
|
|
1370
|
+
* snapshots carry only the volatile subset (revision/scheduler/power), so the
|
|
1371
|
+
* deployment constants the UI reads — the session-default permission the
|
|
1372
|
+
* confirmation banner compares against and the subtask depth limit — are
|
|
1373
|
+
* carried over from the last full snapshot instead of being dropped by a
|
|
1374
|
+
* heartbeat frame.
|
|
1375
|
+
*/
|
|
1376
|
+
mirrorOf(snapshot) {
|
|
1377
|
+
const sessionDefaultPermission = snapshot.sessionDefaultPermission ?? this.hostState?.sessionDefaultPermission;
|
|
1378
|
+
const maxSubtaskDepth = snapshot.maxSubtaskDepth ?? this.hostState?.maxSubtaskDepth;
|
|
1379
|
+
const teamRunAvailable = snapshot.teamRunAvailable ?? this.hostState?.teamRunAvailable;
|
|
1380
|
+
return {
|
|
1381
|
+
revision: snapshot.revision,
|
|
1382
|
+
scheduler: snapshot.scheduler,
|
|
1383
|
+
power: snapshot.power,
|
|
1384
|
+
...sessionDefaultPermission === void 0 ? {} : { sessionDefaultPermission },
|
|
1385
|
+
...maxSubtaskDepth === void 0 ? {} : { maxSubtaskDepth },
|
|
1386
|
+
...teamRunAvailable === void 0 ? {} : { teamRunAvailable }
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1069
1389
|
async refreshRemote(preserveError) {
|
|
1070
1390
|
const transport = this.deps.transport;
|
|
1071
1391
|
if (transport === void 0) return true;
|
|
@@ -1085,11 +1405,7 @@ window.__ModuleLoader__.load({
|
|
|
1085
1405
|
acceptRemote(snapshot) {
|
|
1086
1406
|
if (this.hostState?.scheduler.ledgerId === snapshot.scheduler.ledgerId && this.hostState !== void 0 && snapshot.revision < this.hostState.revision) return false;
|
|
1087
1407
|
this.tasks = [...snapshot.tasks];
|
|
1088
|
-
this.hostState =
|
|
1089
|
-
revision: snapshot.revision,
|
|
1090
|
-
scheduler: snapshot.scheduler,
|
|
1091
|
-
power: snapshot.power
|
|
1092
|
-
};
|
|
1408
|
+
this.hostState = this.mirrorOf(snapshot);
|
|
1093
1409
|
this.transportError = void 0;
|
|
1094
1410
|
if (this.selectedTaskId !== void 0 && !this.tasks.some((task) => task.id === this.selectedTaskId)) this.selectedTaskId = void 0;
|
|
1095
1411
|
if (!this.archiveView && this.selectedTaskId !== void 0 && this.tasks.find((task) => task.id === this.selectedTaskId)?.archivedAt !== void 0) this.selectedTaskId = void 0;
|
|
@@ -1387,6 +1703,7 @@ window.__ModuleLoader__.load({
|
|
|
1387
1703
|
if (typeof record.prompt !== "string") return false;
|
|
1388
1704
|
if (typeof record.createdAt !== "number") return false;
|
|
1389
1705
|
if (typeof record.updatedAt !== "number") return false;
|
|
1706
|
+
if (record.parentId !== void 0 && typeof record.parentId !== "string") return false;
|
|
1390
1707
|
if (record.workspaceId !== void 0 && typeof record.workspaceId !== "string") return false;
|
|
1391
1708
|
if (record.mode !== void 0 && typeof record.mode !== "string") return false;
|
|
1392
1709
|
if (record.permission !== void 0 && typeof record.permission !== "string") return false;
|
|
@@ -1496,6 +1813,13 @@ window.__ModuleLoader__.load({
|
|
|
1496
1813
|
status: normalizeStatus(row.status)
|
|
1497
1814
|
};
|
|
1498
1815
|
task.schedule = normalizeSchedule(row.schedule);
|
|
1816
|
+
task.parentId = normalizeTargetId(row.parentId);
|
|
1817
|
+
task.executions = task.executions.map((execution) => ({
|
|
1818
|
+
...execution,
|
|
1819
|
+
runGroupId: normalizeTargetId(execution.runGroupId),
|
|
1820
|
+
ownResult: isExecutionOutcome(execution.ownResult) ? execution.ownResult : void 0,
|
|
1821
|
+
ownError: typeof execution.ownError === "string" ? execution.ownError : void 0
|
|
1822
|
+
}));
|
|
1499
1823
|
task.workspaceId = normalizeTargetId(row.workspaceId);
|
|
1500
1824
|
task.mode = normalizeTargetId(row.mode);
|
|
1501
1825
|
task.archivedAt = typeof row.archivedAt === "number" && Number.isFinite(row.archivedAt) ? row.archivedAt : void 0;
|
|
@@ -1746,19 +2070,23 @@ window.__ModuleLoader__.load({
|
|
|
1746
2070
|
"exec.mode.brokenSuffix": "(不可用)",
|
|
1747
2071
|
"exec.mode.removed": "(已移除)",
|
|
1748
2072
|
"exec.permission.default": "会话默认",
|
|
2073
|
+
"exec.permission.inheritParent": "继承父任务({permission})",
|
|
1749
2074
|
"exec.permission.read-only": "只读",
|
|
1750
2075
|
"exec.permission.workspace-write": "工作区可写",
|
|
1751
2076
|
"exec.permission.danger-full-access": "完全访问",
|
|
1752
2077
|
"exec.model.default": "宿主默认(agent-default-model)",
|
|
1753
2078
|
"exec.model.unknown": "(未知模型/回退默认)",
|
|
1754
2079
|
"exec.reuseSession": "在同一对话继续",
|
|
2080
|
+
"exec.teamRun": "用 Agent Team 执行(Leader/teammate)",
|
|
2081
|
+
"exec.teamRunHint": "开启后:执行本任务只启动一个 Leader 会话,每个子任务由它派生为 teammate 并在该会话内并行工作(子任务自身的权限钉住不适用)。关闭则每个成员各开独立会话。",
|
|
2082
|
+
"exec.teamRunUnavailable": "当前部署未提供 Agent Teams 服务,无法启用。",
|
|
1755
2083
|
"exec.reuseSessionHint": "开启后,本任务的后续执行在上一次会话里继续(该会话空闲且仍存在时),不再每次新建对话;每次复用时都会重新应用上面钉住的权限与模型。",
|
|
1756
2084
|
"detail.executionSettings": "执行设置",
|
|
1757
2085
|
"exec.hint": "执行时生效:工作区决定执行会话落在哪个工作区;模式决定会话的 agent 预设;权限经 /permission 命令应用到会话。留空则使用运行时默认。",
|
|
1758
2086
|
"settings.title": "任务看板",
|
|
1759
2087
|
"settings.description": "控制 Host 任务看板、agent 播报与运行期间的系统空闲睡眠保护。",
|
|
1760
2088
|
"settings.enabled": "启用任务看板",
|
|
1761
|
-
"settings.enabledHint": "
|
|
2089
|
+
"settings.enabledHint": "关闭后隐藏侧边栏入口与看板视图,并注销 task_board_* agent 工具;开启时任何会话都可以通过这些工具读写看板、子任务与定时计划。",
|
|
1762
2090
|
"settings.announceToAgent": "向 agent 播报任务看板",
|
|
1763
2091
|
"settings.announceToAgentHint": "开启:每条 agent 系统提示都会包含本看板的说明;关闭:不播报,agent 仅在用户主动提及时了解看板。",
|
|
1764
2092
|
"settings.preventIdleSleep": "阻止系统空闲睡眠",
|
|
@@ -1781,7 +2109,36 @@ window.__ModuleLoader__.load({
|
|
|
1781
2109
|
"settings.discard": "放弃",
|
|
1782
2110
|
"settings.unsaved": "未保存",
|
|
1783
2111
|
"settings.saveFailed": "部署未接受这些值,已保留供你修改。",
|
|
1784
|
-
"settings.invalidNumber": "请输入数字,留空则使用默认值。"
|
|
2112
|
+
"settings.invalidNumber": "请输入数字,留空则使用默认值。",
|
|
2113
|
+
"settings.maxSubtaskDepth": "子任务深度上限",
|
|
2114
|
+
"settings.maxSubtaskDepthHint": "默认 1:一个任务只允许一层子任务,子任务不能再创建或关联子任务。最大 3。执行父任务会并发执行它的整棵子任务树,层级越深,一次执行开启的会话越多。",
|
|
2115
|
+
"settings.maxSubtaskDepthOption": "{depth} 层",
|
|
2116
|
+
"detail.parent": "父任务",
|
|
2117
|
+
"detail.parent.open": "打开父任务",
|
|
2118
|
+
"detail.subtasks": "子任务",
|
|
2119
|
+
"detail.subtasks.empty": "还没有子任务",
|
|
2120
|
+
"detail.subtasks.add": "添加子任务",
|
|
2121
|
+
"detail.subtasks.link": "关联现有任务",
|
|
2122
|
+
"detail.subtasks.detach": "解除关联",
|
|
2123
|
+
"detail.subtasks.runningLock": "子任务正在执行,结算后才能解除关联或改挂父任务",
|
|
2124
|
+
"detail.subtasks.runHint": "执行本任务会同时并发执行它的 {count} 个子任务;子任务失败不影响其它子任务继续执行。",
|
|
2125
|
+
"detail.subtasks.waiting": "等待子任务结算",
|
|
2126
|
+
"detail.subtasks.depthLimit": "当前子任务深度上限为 {depth} 层,子任务不能再创建或关联子任务。",
|
|
2127
|
+
"detail.subtasks.hostOnly": "连接 Host 后才能创建或关联子任务。",
|
|
2128
|
+
"new.subtaskTitle": "新建子任务",
|
|
2129
|
+
"new.subtaskInherit": "未单独选择的执行设置继承父任务(工作区、模式、权限、模型),可在此覆盖。",
|
|
2130
|
+
"link.subtask.title": "关联现有任务",
|
|
2131
|
+
"link.subtask.hint": "只能关联没有父任务的在看板任务;关联后它成为本任务的子任务。",
|
|
2132
|
+
"link.subtask.empty": "没有可关联的任务",
|
|
2133
|
+
"link.subtask.confirm": "关联",
|
|
2134
|
+
"card.subtask": "子任务",
|
|
2135
|
+
"card.subtasks": "{count} 子任务",
|
|
2136
|
+
"card.subtasksFailed": "{count} 失败",
|
|
2137
|
+
"card.subtasksRunning": "{count} 运行中",
|
|
2138
|
+
"card.subtasksBreakdown": "子任务 {total}:已完成 {done},运行中 {running},失败 {failed}",
|
|
2139
|
+
"board.hideSubtasks": "隐藏子任务",
|
|
2140
|
+
"board.showSubtasks": "显示子任务",
|
|
2141
|
+
"board.subtaskFilterHint": "看板默认只显示父任务;搜索或按标签筛选时会自动展开子任务。"
|
|
1785
2142
|
};
|
|
1786
2143
|
/** en dictionary, complete against the zh key set. */
|
|
1787
2144
|
const en = {
|
|
@@ -1935,19 +2292,23 @@ window.__ModuleLoader__.load({
|
|
|
1935
2292
|
"exec.mode.brokenSuffix": " (unavailable)",
|
|
1936
2293
|
"exec.mode.removed": " (removed)",
|
|
1937
2294
|
"exec.permission.default": "Session default",
|
|
2295
|
+
"exec.permission.inheritParent": "Inherit parent ({permission})",
|
|
1938
2296
|
"exec.permission.read-only": "Read-only",
|
|
1939
2297
|
"exec.permission.workspace-write": "Workspace Write",
|
|
1940
2298
|
"exec.permission.danger-full-access": "Full Access",
|
|
1941
2299
|
"exec.model.default": "Host default (agent-default-model)",
|
|
1942
2300
|
"exec.model.unknown": " (unknown / fallback to default)",
|
|
1943
2301
|
"exec.reuseSession": "Continue in the same conversation",
|
|
2302
|
+
"exec.teamRun": "Run with an Agent Team (Lead + teammates)",
|
|
2303
|
+
"exec.teamRunHint": "On: running this task starts one Lead session and spawns a teammate per subtask inside it, working in parallel (a subtask permission pin does not apply). Off: every member gets its own independent session.",
|
|
2304
|
+
"exec.teamRunUnavailable": "This deployment serves no Agent Teams service, so team runs cannot be enabled.",
|
|
1944
2305
|
"exec.reuseSessionHint": "When on, later runs continue in the previous session (when that session is idle and still exists) instead of starting a new conversation each time; the pinned permission and model above are re-applied on every reuse.",
|
|
1945
2306
|
"detail.executionSettings": "Execution Settings",
|
|
1946
2307
|
"exec.hint": "Applied when the task runs: the workspace decides where the execution session lands; the mode composes the session's agent preset; the permission is applied through the /permission command. Blank = runtime default.",
|
|
1947
2308
|
"settings.title": "Task Board",
|
|
1948
2309
|
"settings.description": "Configure the Host task board, agent announcement, and idle-system-sleep protection while work is pending.",
|
|
1949
2310
|
"settings.enabled": "Enable the task board",
|
|
1950
|
-
"settings.enabledHint": "When off, the sidebar entry and board view are hidden.",
|
|
2311
|
+
"settings.enabledHint": "When off, the sidebar entry and board view are hidden and the task_board_* agent tools are unregistered; while on, any session can read and drive the board, its subtasks and its schedules through those tools.",
|
|
1951
2312
|
"settings.announceToAgent": "Announce the task board to agents",
|
|
1952
2313
|
"settings.announceToAgentHint": "On: every agent system prompt includes a note about this board. Off: no announcement; agents learn about the board only when you mention it.",
|
|
1953
2314
|
"settings.preventIdleSleep": "Prevent idle system sleep",
|
|
@@ -1970,7 +2331,36 @@ window.__ModuleLoader__.load({
|
|
|
1970
2331
|
"settings.discard": "Discard",
|
|
1971
2332
|
"settings.unsaved": "Unsaved",
|
|
1972
2333
|
"settings.saveFailed": "The deployment did not accept these values; they were left for you to correct.",
|
|
1973
|
-
"settings.invalidNumber": "Enter a number, or leave blank to use the default."
|
|
2334
|
+
"settings.invalidNumber": "Enter a number, or leave blank to use the default.",
|
|
2335
|
+
"settings.maxSubtaskDepth": "Subtask depth limit",
|
|
2336
|
+
"settings.maxSubtaskDepthHint": "Default 1: a task may carry one level of subtasks, and a subtask cannot create or link further subtasks. Maximum 3. Running a task also runs its whole subtask tree concurrently, and every extra level opens more sessions per run.",
|
|
2337
|
+
"settings.maxSubtaskDepthOption": "{depth} levels",
|
|
2338
|
+
"detail.parent": "Parent task",
|
|
2339
|
+
"detail.parent.open": "Open parent task",
|
|
2340
|
+
"detail.subtasks": "Subtasks",
|
|
2341
|
+
"detail.subtasks.empty": "No subtasks yet",
|
|
2342
|
+
"detail.subtasks.add": "Add subtask",
|
|
2343
|
+
"detail.subtasks.link": "Link existing task",
|
|
2344
|
+
"detail.subtasks.detach": "Detach",
|
|
2345
|
+
"detail.subtasks.runningLock": "This subtask is running; it can be detached or re-linked once it settles",
|
|
2346
|
+
"detail.subtasks.runHint": "Running this task also runs its {count} subtasks concurrently; one subtask failing does not stop the others.",
|
|
2347
|
+
"detail.subtasks.waiting": "Waiting for subtasks",
|
|
2348
|
+
"detail.subtasks.depthLimit": "The subtask depth limit is {depth}; a subtask cannot create or link further subtasks.",
|
|
2349
|
+
"detail.subtasks.hostOnly": "Connect to the Host to create or link subtasks.",
|
|
2350
|
+
"new.subtaskTitle": "New Subtask",
|
|
2351
|
+
"new.subtaskInherit": "Execution settings left unset inherit the parent (workspace, mode, permission, model); override any of them here.",
|
|
2352
|
+
"link.subtask.title": "Link an existing task",
|
|
2353
|
+
"link.subtask.hint": "Only an on-board task without a parent can be linked; it then becomes a subtask of this task.",
|
|
2354
|
+
"link.subtask.empty": "No task can be linked",
|
|
2355
|
+
"link.subtask.confirm": "Link",
|
|
2356
|
+
"card.subtask": "subtask",
|
|
2357
|
+
"card.subtasks": "{count} subtasks",
|
|
2358
|
+
"card.subtasksFailed": "{count} failed",
|
|
2359
|
+
"card.subtasksRunning": "{count} running",
|
|
2360
|
+
"card.subtasksBreakdown": "Subtasks {total}: {done} done, {running} running, {failed} failed",
|
|
2361
|
+
"board.hideSubtasks": "Hide subtasks",
|
|
2362
|
+
"board.showSubtasks": "Show subtasks",
|
|
2363
|
+
"board.subtaskFilterHint": "The board shows parent tasks only; a text or label filter reveals the subtasks automatically."
|
|
1974
2364
|
};
|
|
1975
2365
|
/** Active dictionary, picked by the document language at call time. */
|
|
1976
2366
|
function dictionary() {
|
|
@@ -1997,7 +2387,7 @@ window.__ModuleLoader__.load({
|
|
|
1997
2387
|
}
|
|
1998
2388
|
//#endregion
|
|
1999
2389
|
//#region \0dsh-css:packages/dsh-task-board/src/client/board.module.css.mjs
|
|
2000
|
-
const css$1 = "[data-pane=conversation],[class*=centerCol]{position:relative}[data-dsh-taskboard-view]{z-index:60;background:var(--dsw-alias-bg-base);display:none;position:absolute;inset:0;container:_7D6uKa_task-board-view/inline-size}html[data-dsh-taskboard-active]:not([data-dsh-ssh-active]) [data-dsh-taskboard-view]{display:block}html[data-dsh-taskboard-active]:not([data-dsh-ssh-active]) [data-pane=conversation]>:not([data-dsh-taskboard-view]),html[data-dsh-taskboard-active]:not([data-dsh-ssh-active]) [class*=centerCol]>:not([data-dsh-taskboard-view]){display:none!important}._7D6uKa_entry{box-sizing:border-box;min-height:36px;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;text-align:left;white-space:nowrap;background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;margin:0 2px;padding:7px 8px;font-size:14px;line-height:22px;display:flex}._7D6uKa_entry:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}._7D6uKa_entry[data-active]{background:var(--dsw-alias-interactive-bg-active);color:var(--dsw-alias-label-primary);font-weight:600}._7D6uKa_entryIcon{flex:none;justify-content:center;align-items:center;width:16px;height:16px;display:inline-flex}._7D6uKa_entryIcon svg{width:16px;height:16px;display:block}._7D6uKa_entryLabel{text-overflow:ellipsis;overflow:hidden}[data-dsh-frame][data-sidebar-collapsed] ._7D6uKa_entry,[data-sidebar-collapsed] ._7D6uKa_entry{border-radius:12px;justify-content:center;width:36px;height:36px;margin:0 auto 12px;padding:0}[data-dsh-frame][data-sidebar-collapsed] ._7D6uKa_entryIcon,[data-sidebar-collapsed] ._7D6uKa_entryIcon,[data-dsh-frame][data-sidebar-collapsed] ._7D6uKa_entryIcon svg,[data-sidebar-collapsed] ._7D6uKa_entryIcon svg{width:18px;height:18px}[data-dsh-frame][data-sidebar-collapsed] ._7D6uKa_entryLabel,[data-sidebar-collapsed] ._7D6uKa_entryLabel{display:none}._7D6uKa_board{box-sizing:border-box;background:var(--dsw-alias-bg-base);min-width:0;height:100%;min-height:0;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);flex-direction:column;gap:12px;padding:14px 16px 16px;display:flex}._7D6uKa_boardHeader{flex:none;align-items:center;gap:10px;display:flex}._7D6uKa_boardTitle{color:var(--dsw-alias-label-primary);white-space:nowrap;margin:0;font-size:16px;font-weight:700}._7D6uKa_backButton{align-items:center;gap:4px;display:inline-flex}._7D6uKa_search{min-width:120px;color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;flex:0 260px;padding:6px 10px;font-size:13px}._7D6uKa_search::placeholder{color:var(--dsw-alias-label-tertiary)}._7D6uKa_columns{overscroll-behavior-inline:contain;scrollbar-color:var(--dsw-alias-border-l3) var(--dsw-alias-interactive-bg-hover);scrollbar-width:thin;flex:1;grid-auto-columns:minmax(220px,1fr);grid-auto-flow:column;gap:12px;min-height:0;padding-bottom:6px;display:grid;overflow:auto hidden}._7D6uKa_columns::-webkit-scrollbar{height:10px}._7D6uKa_columns::-webkit-scrollbar-track{background:var(--dsw-alias-interactive-bg-hover);border-radius:999px}._7D6uKa_columns::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l3);background-clip:content-box;border:2px solid #0000;border-radius:999px}._7D6uKa_columns::-webkit-scrollbar-thumb:hover{background:var(--dsw-alias-border-l4);background-clip:content-box}._7D6uKa_column{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:12px;flex-direction:column;min-height:0;display:flex;overflow:hidden}._7D6uKa_columnHeader{flex:none;align-items:center;gap:6px;padding:10px 12px;display:flex}._7D6uKa_columnTitle{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:13px;font-weight:700;overflow:hidden}._7D6uKa_columnCount{min-width:0;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-interactive-bg-hover);border-radius:999px;flex:none;padding:1px 8px;font-size:12px}._7D6uKa_statusDot{border-radius:50%;flex:none;width:8px;height:8px}._7D6uKa_statusDot[data-status=backlog]{background:var(--dsw-alias-label-tertiary)}._7D6uKa_statusDot[data-status=todo]{background:var(--dsw-alias-state-business-primary)}._7D6uKa_statusDot[data-status=running]{background:var(--dsw-alias-state-warn-primary)}._7D6uKa_statusDot[data-status=done]{background:var(--dsw-alias-state-success-primary)}._7D6uKa_statusDot[data-status=failed]{background:var(--dsw-alias-state-error-primary)}._7D6uKa_cards{flex-direction:column;flex:1;gap:8px;min-height:0;padding:2px 8px 10px;display:flex;overflow-y:auto}._7D6uKa_columnEmpty{text-align:center;color:var(--dsw-alias-label-tertiary);padding:24px 8px;font-size:12px}._7D6uKa_card{text-align:left;background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;color:var(--dsw-alias-label-primary);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;font-family:inherit;transition:box-shadow .12s,border-color .12s,transform .12s;display:flex}._7D6uKa_card:hover{box-shadow:var(--dsw-shadow-lv2);border-color:var(--dsw-alias-border-l3);transform:translateY(-1px)}._7D6uKa_card[data-status=running]{border-color:var(--dsw-alias-state-warn-primary)}._7D6uKa_cardTitle{-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:13px;font-weight:600;line-height:1.35;display:-webkit-box;overflow:hidden}._7D6uKa_cardExcerpt{color:var(--dsw-alias-label-secondary);-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12px;line-height:1.4;display:-webkit-box;overflow:hidden}._7D6uKa_cardMeta{color:var(--dsw-alias-label-tertiary);align-items:center;gap:8px;font-size:11px;display:flex}._7D6uKa_cardTime{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}._7D6uKa_cardSchedule{white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover);border-radius:999px;flex:none;padding:2px 6px;font-size:12px;line-height:1}._7D6uKa_cardRun{flex:none}._7D6uKa_cardRun[data-result=failed]{color:var(--dsw-alias-state-error-primary)}._7D6uKa_cardRun[data-result=succeeded]{color:var(--dsw-alias-state-success-primary)}._7D6uKa_cardSession{color:var(--dsw-alias-state-business-primary);flex:none}._7D6uKa_cardRunningLabel{color:var(--dsw-alias-state-warn-primary);font-size:11px}._7D6uKa_cardSpinner{border:2px solid var(--dsw-alias-state-warn-primary);border-top-color:#0000;border-radius:50%;flex:none;width:10px;height:10px;animation:.8s linear infinite _7D6uKa_dshTbSpin}@keyframes _7D6uKa_dshTbSpin{to{transform:rotate(360deg)}}._7D6uKa_primaryButton{color:var(--dsw-alias-label-primary-foreground);background:var(--dsw-alias-button-info-fill);cursor:pointer;white-space:nowrap;border:none;border-radius:8px;padding:6px 14px;font-size:13px;font-weight:600}._7D6uKa_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-info-hover)}._7D6uKa_primaryButton:disabled{opacity:.5;cursor:default}._7D6uKa_ghostButton{color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;white-space:nowrap;background:0 0;border-radius:8px;padding:5px 12px;font-size:12px}._7D6uKa_ghostButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}._7D6uKa_ghostButton:disabled{opacity:.45;cursor:default}._7D6uKa_dangerButton{color:#fff;background:var(--dsw-alias-state-error-primary);cursor:pointer;white-space:nowrap;border:none;border-radius:8px;padding:6px 14px;font-size:13px;font-weight:600}._7D6uKa_dangerButton:hover:not(:disabled){filter:brightness(1.08)}._7D6uKa_dangerButton:active:not(:disabled){filter:brightness(.94)}._7D6uKa_dangerButton:disabled{opacity:.5;cursor:default}._7D6uKa_iconButton{width:26px;height:26px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:0;font-size:13px;display:inline-flex}._7D6uKa_iconButton:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}._7D6uKa_linkButton{color:var(--dsw-alias-state-business-primary);cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:0;font-size:12px}._7D6uKa_linkButton:hover{text-decoration:underline}._7D6uKa_modalBackdrop{z-index:1300;background:var(--dsw-alias-bg-mask-1);justify-content:center;align-items:center;display:flex;position:fixed;inset:0}._7D6uKa_modal{background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);width:min(520px,100vw - 48px);max-height:calc(100vh - 96px);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);border-radius:14px;flex-direction:column;gap:12px;padding:18px;display:flex;overflow-y:auto}._7D6uKa_modalTitle{margin:0;font-size:15px;font-weight:700}._7D6uKa_confirmMessage{color:var(--dsw-alias-label-secondary);white-space:pre-wrap;overflow-wrap:anywhere;margin:0;font-size:13px;line-height:1.5}._7D6uKa_modalFooter{justify-content:flex-end;gap:10px;margin-top:4px;display:flex}._7D6uKa_field{flex-direction:column;gap:5px;display:flex}._7D6uKa_fieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}._7D6uKa_input{color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);resize:vertical;border-radius:8px;outline:none;padding:7px 10px;font-family:inherit;font-size:13px}._7D6uKa_input:focus{border-color:var(--dsw-alias-state-business-primary)}._7D6uKa_select{color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;max-width:100%;padding:7px 10px;font-family:inherit;font-size:13px}._7D6uKa_input::placeholder{color:var(--dsw-alias-label-tertiary)}._7D6uKa_formError{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px}._7D6uKa_detail{background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);width:min(640px,100vw - 48px);max-height:calc(100vh - 80px);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);border-radius:14px;flex-direction:column;display:flex;overflow:hidden}._7D6uKa_detailHeader{border-bottom:1px solid var(--dsw-alias-separator-primary);flex:none;align-items:center;gap:10px;padding:14px 18px;display:flex}._7D6uKa_detailTitle{overflow-wrap:anywhere;flex:1;margin:0;font-size:15px;font-weight:700}._7D6uKa_statusBadge{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:2px 10px;font-size:12px}._7D6uKa_statusBadge[data-status=running]{color:var(--dsw-alias-state-warn-primary);border-color:var(--dsw-alias-state-warn-primary)}._7D6uKa_statusBadge[data-status=done]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}._7D6uKa_statusBadge[data-status=failed]{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}._7D6uKa_detailBody{flex-direction:column;flex:1;gap:16px;padding:14px 18px;display:flex;overflow-y:auto}._7D6uKa_detailSection{flex-direction:column;gap:6px;display:flex}._7D6uKa_detailSection h4{color:var(--dsw-alias-label-tertiary);text-transform:none;margin:0;font-size:12px;font-weight:700}._7D6uKa_detailText{color:var(--dsw-alias-label-primary);white-space:pre-wrap;overflow-wrap:anywhere;margin:0;font-size:13px;line-height:1.55}._7D6uKa_scheduleToggle{color:var(--dsw-alias-label-primary);cursor:pointer;user-select:none;align-items:center;gap:8px;font-size:13px;display:flex}._7D6uKa_scheduleToggle input{accent-color:var(--dsw-alias-state-business-primary)}._7D6uKa_scheduleRow{align-items:center;gap:8px;display:flex}._7D6uKa_scheduleInput{min-width:0;font-family:var(--dsw-font-markdown-code-block-small);flex:1;font-size:12.5px}._7D6uKa_scheduleInputInvalid,._7D6uKa_scheduleInputInvalid:focus{border-color:var(--dsw-alias-state-error-primary)}._7D6uKa_schedulePreset{color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;flex:none;padding:7px 8px;font-size:12.5px}._7D6uKa_scheduleMeta{color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;margin:0;font-size:12px}._7D6uKa_promptBlock{font-size:12.5px;line-height:1.5;font-family:var(--dsw-font-markdown-code-block-small);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-markdown-code-block);border:1px solid var(--dsw-alias-border-l1);white-space:pre-wrap;overflow-wrap:anywhere;border-radius:8px;max-height:240px;margin:0;padding:10px 12px;overflow-y:auto}._7D6uKa_executionList{flex-direction:column;gap:8px;margin:0;padding:0;list-style:none;display:flex}._7D6uKa_executionRow{border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-wrap:wrap;align-items:center;gap:10px;padding:8px 10px;display:flex}._7D6uKa_executionBadge{color:var(--dsw-alias-state-warn-primary);background:var(--dsw-alias-state-warn-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:600}._7D6uKa_executionBadge[data-result=succeeded]{color:var(--dsw-alias-state-success-primary);background:0 0}._7D6uKa_executionBadge[data-result=failed]{color:var(--dsw-alias-state-error-primary);background:0 0}._7D6uKa_executionBadge[data-result=cancelled]{color:var(--dsw-alias-label-tertiary);background:0 0}._7D6uKa_executionTimes{color:var(--dsw-alias-label-secondary);font-size:12px}._7D6uKa_executionError{width:100%;color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;font-size:12px}._7D6uKa_moveRow{flex-wrap:wrap;gap:8px;display:flex}._7D6uKa_detailFooter{border-top:1px solid var(--dsw-alias-separator-primary);flex:none;align-items:center;gap:10px;padding:12px 18px;display:flex}._7D6uKa_detailMeta{color:var(--dsw-alias-label-tertiary);margin-left:auto;font-size:11px}@container _7D6uKa_task-board-view (width<=768px){._7D6uKa_board{gap:10px;padding:10px}._7D6uKa_boardHeader{flex-wrap:wrap;align-items:center;gap:8px}._7D6uKa_backButton{flex:none;order:1}._7D6uKa_boardTitle{flex:auto;order:2}._7D6uKa_boardHeader>._7D6uKa_detailMeta{flex:1 0 100%;order:3;margin-left:0}._7D6uKa_search{flex:1 0 100%;order:4;min-width:0}._7D6uKa_boardHeader>button:not(._7D6uKa_backButton){flex:1 1 0;order:5;min-width:0}._7D6uKa_columns{scroll-snap-type:inline mandatory;scrollbar-width:none;-webkit-overflow-scrolling:touch;grid-auto-columns:86cqw;gap:10px;padding-inline:2px 14cqw;scroll-padding-inline:2px}._7D6uKa_columns::-webkit-scrollbar{display:none}._7D6uKa_column{scroll-snap-align:start;scroll-snap-stop:always}}@container _7D6uKa_task-board-view (width<=720px){._7D6uKa_boardHeader>._7D6uKa_detailMeta{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}}@container _7D6uKa_task-board-view (width<=600px){._7D6uKa_board{padding-inline:8px}}@media (width<=768px){[data-dsh-taskboard-view]{height:100dvh}._7D6uKa_entry,._7D6uKa_card,._7D6uKa_primaryButton,._7D6uKa_ghostButton,._7D6uKa_dangerButton,._7D6uKa_iconButton,._7D6uKa_linkButton,._7D6uKa_search,._7D6uKa_input,._7D6uKa_select,._7D6uKa_schedulePreset,._7D6uKa_scheduleToggle{min-height:44px}._7D6uKa_search,._7D6uKa_input,._7D6uKa_select,._7D6uKa_schedulePreset{box-sizing:border-box;font-size:16px}._7D6uKa_modalBackdrop{justify-content:stretch;align-items:stretch;width:100vw;height:100dvh}._7D6uKa_modal,._7D6uKa_detail{box-sizing:border-box;border:0;border-radius:0;width:100vw;height:100dvh;max-height:none}._7D6uKa_modal{padding-top:max(16px, env(safe-area-inset-top));padding-right:max(16px, env(safe-area-inset-right));padding-bottom:max(16px, env(safe-area-inset-bottom));padding-left:max(16px, env(safe-area-inset-left))}._7D6uKa_modalFooter{z-index:1;background:var(--dsw-alias-bg-base);flex-wrap:wrap;padding-top:8px;position:sticky;bottom:0}._7D6uKa_modalFooter>button{flex:120px}._7D6uKa_detailHeader{padding-top:max(12px, env(safe-area-inset-top));padding-right:max(14px, env(safe-area-inset-right));padding-left:max(14px, env(safe-area-inset-left));flex-wrap:wrap}._7D6uKa_detailTitle{min-width:0}._7D6uKa_detailBody{overscroll-behavior-y:contain;padding-right:max(14px, env(safe-area-inset-right));padding-left:max(14px, env(safe-area-inset-left))}._7D6uKa_detailFooter{padding-right:max(14px, env(safe-area-inset-right));padding-bottom:max(12px, env(safe-area-inset-bottom));padding-left:max(14px, env(safe-area-inset-left));flex-wrap:wrap}._7D6uKa_detailFooter>button{flex:96px}._7D6uKa_detailFooter>._7D6uKa_detailMeta{text-align:end;flex:1 0 100%;margin-left:0}._7D6uKa_scheduleRow{flex-direction:column;align-items:stretch}._7D6uKa_schedulePreset{width:100%}}._7D6uKa_entry:focus-visible,._7D6uKa_card:focus-visible,._7D6uKa_primaryButton:focus-visible,._7D6uKa_ghostButton:focus-visible,._7D6uKa_dangerButton:focus-visible,._7D6uKa_iconButton:focus-visible,._7D6uKa_linkButton:focus-visible,._7D6uKa_search:focus-visible,._7D6uKa_input:focus-visible,._7D6uKa_select:focus-visible,._7D6uKa_schedulePreset:focus-visible,._7D6uKa_scheduleToggle input:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}._7D6uKa_entry,._7D6uKa_primaryButton,._7D6uKa_ghostButton,._7D6uKa_dangerButton,._7D6uKa_iconButton,._7D6uKa_linkButton,._7D6uKa_search,._7D6uKa_input,._7D6uKa_select,._7D6uKa_schedulePreset,._7D6uKa_scheduleToggle input{transition:background-color .12s,color .12s,border-color .12s,outline-color .12s,box-shadow .12s,transform .12s}._7D6uKa_card:active{box-shadow:var(--dsw-shadow-lv1);transform:translateY(0)}._7D6uKa_entry:active,._7D6uKa_primaryButton:active:not(:disabled),._7D6uKa_ghostButton:active:not(:disabled),._7D6uKa_dangerButton:active:not(:disabled),._7D6uKa_iconButton:active:not(:disabled),._7D6uKa_linkButton:active:not(:disabled){transform:translateY(1px)}._7D6uKa_entry[data-active]:hover{background:var(--dsw-specific-sidebar-nav-item-active)}._7D6uKa_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}._7D6uKa_linkButton:hover:not(:disabled){text-decoration:underline}._7D6uKa_iconButton:disabled,._7D6uKa_linkButton:disabled{opacity:.45;cursor:default}._7D6uKa_search:focus,._7D6uKa_select:focus,._7D6uKa_schedulePreset:focus{border-color:var(--dsw-alias-state-business-primary)}._7D6uKa_scheduleToggle input{margin:0}@media (prefers-reduced-motion:reduce){._7D6uKa_entry,._7D6uKa_card,._7D6uKa_primaryButton,._7D6uKa_ghostButton,._7D6uKa_dangerButton,._7D6uKa_iconButton,._7D6uKa_linkButton,._7D6uKa_search,._7D6uKa_input,._7D6uKa_select,._7D6uKa_schedulePreset,._7D6uKa_scheduleToggle input{transition:none}._7D6uKa_cardSpinner{animation:none}}._7D6uKa_cardTags{flex-wrap:wrap;gap:4px;display:flex}._7D6uKa_cardTag{border:1px solid var(--dsh-task-tag-border);background:var(--dsh-task-tag-fill);max-width:100%;color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;border-radius:999px;padding:0 7px;font-size:10px;line-height:16px;overflow:hidden}._7D6uKa_tagFilter{flex-wrap:wrap;align-items:center;gap:6px;margin:0 0 10px;display:flex}._7D6uKa_tagFilterLabel{color:var(--dsw-alias-label-tertiary);font-size:11px}._7D6uKa_tagChip{border:1px solid var(--dsh-task-tag-border);color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border-radius:999px;padding:1px 9px;font-family:inherit;font-size:11px;line-height:18px}._7D6uKa_tagChip[data-active=true]{background:var(--dsh-task-tag-fill);color:var(--dsw-alias-label-primary)}._7D6uKa_cardTag[data-tag-tone=\"0\"],._7D6uKa_tagChip[data-tag-tone=\"0\"]{--dsh-task-tag-fill:#4e93e82e;--dsh-task-tag-border:#4e93e866}._7D6uKa_cardTag[data-tag-tone=\"1\"],._7D6uKa_tagChip[data-tag-tone=\"1\"]{--dsh-task-tag-fill:#2ea36a2e;--dsh-task-tag-border:#2ea36a66}._7D6uKa_cardTag[data-tag-tone=\"2\"],._7D6uKa_tagChip[data-tag-tone=\"2\"]{--dsh-task-tag-fill:#d08a2a2e;--dsh-task-tag-border:#d08a2a66}._7D6uKa_cardTag[data-tag-tone=\"3\"],._7D6uKa_tagChip[data-tag-tone=\"3\"]{--dsh-task-tag-fill:#b456c82e;--dsh-task-tag-border:#b456c866}._7D6uKa_cardTag[data-tag-tone=\"4\"],._7D6uKa_tagChip[data-tag-tone=\"4\"]{--dsh-task-tag-fill:#cf5f7a2e;--dsh-task-tag-border:#cf5f7a66}._7D6uKa_cardTag[data-tag-tone=\"5\"],._7D6uKa_tagChip[data-tag-tone=\"5\"]{--dsh-task-tag-fill:#4a9fb52e;--dsh-task-tag-border:#4a9fb566}._7D6uKa_fieldHint{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.4}._7D6uKa_tagRow{align-items:center;gap:6px;display:flex}._7D6uKa_tagRow ._7D6uKa_input{flex:1 1 0;min-width:0}._7D6uKa_tagRow ._7D6uKa_ghostButton{flex:none}._7D6uKa_tagAddButton{align-self:flex-start}._7D6uKa_projectFilter{flex:none;align-items:center;gap:6px;display:flex}._7D6uKa_projectFilterLabel{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}._7D6uKa_projectDialog{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;flex-direction:column;flex:none;gap:8px;margin-bottom:8px;padding:10px 12px;display:flex}._7D6uKa_projectDialogActions{justify-content:flex-end;gap:8px;display:flex}._7D6uKa_aiParse{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}._7D6uKa_aiParseRow{align-items:center;gap:8px;display:flex}._7D6uKa_aiParseRow ._7D6uKa_select{flex:1 1 0;min-width:0}._7D6uKa_aiParseRow ._7D6uKa_ghostButton,._7D6uKa_aiParseRow ._7D6uKa_primaryButton{flex:none}";
|
|
2390
|
+
const css$1 = "[data-pane=conversation],[class*=centerCol]{position:relative}[data-dsh-taskboard-view]{z-index:60;background:var(--dsw-alias-bg-base);display:none;position:absolute;inset:0;container:_7D6uKa_task-board-view/inline-size}html[data-dsh-taskboard-active]:not([data-dsh-ssh-active]) [data-dsh-taskboard-view]{display:block}html[data-dsh-taskboard-active]:not([data-dsh-ssh-active]) [data-pane=conversation]>:not([data-dsh-taskboard-view]),html[data-dsh-taskboard-active]:not([data-dsh-ssh-active]) [class*=centerCol]>:not([data-dsh-taskboard-view]){display:none!important}._7D6uKa_entry{box-sizing:border-box;min-height:36px;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;text-align:left;white-space:nowrap;background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;margin:0 2px;padding:7px 8px;font-size:14px;line-height:22px;display:flex}._7D6uKa_entry:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}._7D6uKa_entry[data-active]{background:var(--dsw-alias-interactive-bg-active);color:var(--dsw-alias-label-primary);font-weight:600}._7D6uKa_entryIcon{flex:none;justify-content:center;align-items:center;width:16px;height:16px;display:inline-flex}._7D6uKa_entryIcon svg{width:16px;height:16px;display:block}._7D6uKa_entryLabel{text-overflow:ellipsis;overflow:hidden}[data-dsh-frame][data-sidebar-collapsed] ._7D6uKa_entry,[data-sidebar-collapsed] ._7D6uKa_entry{border-radius:12px;justify-content:center;width:36px;height:36px;margin:0 auto 12px;padding:0}[data-dsh-frame][data-sidebar-collapsed] ._7D6uKa_entryIcon,[data-sidebar-collapsed] ._7D6uKa_entryIcon,[data-dsh-frame][data-sidebar-collapsed] ._7D6uKa_entryIcon svg,[data-sidebar-collapsed] ._7D6uKa_entryIcon svg{width:18px;height:18px}[data-dsh-frame][data-sidebar-collapsed] ._7D6uKa_entryLabel,[data-sidebar-collapsed] ._7D6uKa_entryLabel{display:none}._7D6uKa_board{box-sizing:border-box;background:var(--dsw-alias-bg-base);min-width:0;height:100%;min-height:0;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);flex-direction:column;gap:12px;padding:14px 16px 16px;display:flex}._7D6uKa_boardHeader{flex:none;align-items:center;gap:10px;display:flex}._7D6uKa_boardTitle{color:var(--dsw-alias-label-primary);white-space:nowrap;margin:0;font-size:16px;font-weight:700}._7D6uKa_backButton{align-items:center;gap:4px;display:inline-flex}._7D6uKa_search{min-width:120px;color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;flex:0 260px;padding:6px 10px;font-size:13px}._7D6uKa_search::placeholder{color:var(--dsw-alias-label-tertiary)}._7D6uKa_columns{overscroll-behavior-inline:contain;scrollbar-color:var(--dsw-alias-border-l3) var(--dsw-alias-interactive-bg-hover);scrollbar-width:thin;flex:1;grid-auto-columns:minmax(220px,1fr);grid-auto-flow:column;gap:12px;min-height:0;padding-bottom:6px;display:grid;overflow:auto hidden}._7D6uKa_columns::-webkit-scrollbar{height:10px}._7D6uKa_columns::-webkit-scrollbar-track{background:var(--dsw-alias-interactive-bg-hover);border-radius:999px}._7D6uKa_columns::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l3);background-clip:content-box;border:2px solid #0000;border-radius:999px}._7D6uKa_columns::-webkit-scrollbar-thumb:hover{background:var(--dsw-alias-border-l4);background-clip:content-box}._7D6uKa_column{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:12px;flex-direction:column;min-height:0;display:flex;overflow:hidden}._7D6uKa_columnHeader{flex:none;align-items:center;gap:6px;padding:10px 12px;display:flex}._7D6uKa_columnTitle{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:13px;font-weight:700;overflow:hidden}._7D6uKa_columnCount{min-width:0;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-interactive-bg-hover);border-radius:999px;flex:none;padding:1px 8px;font-size:12px}._7D6uKa_statusDot{border-radius:50%;flex:none;width:8px;height:8px}._7D6uKa_statusDot[data-status=backlog]{background:var(--dsw-alias-label-tertiary)}._7D6uKa_statusDot[data-status=todo]{background:var(--dsw-alias-state-business-primary)}._7D6uKa_statusDot[data-status=running]{background:var(--dsw-alias-state-warn-primary)}._7D6uKa_statusDot[data-status=done]{background:var(--dsw-alias-state-success-primary)}._7D6uKa_statusDot[data-status=failed]{background:var(--dsw-alias-state-error-primary)}._7D6uKa_cards{flex-direction:column;flex:1;gap:8px;min-height:0;padding:2px 8px 10px;display:flex;overflow-y:auto}._7D6uKa_columnEmpty{text-align:center;color:var(--dsw-alias-label-tertiary);padding:24px 8px;font-size:12px}._7D6uKa_card{text-align:left;background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;color:var(--dsw-alias-label-primary);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;font-family:inherit;transition:box-shadow .12s,border-color .12s,transform .12s;display:flex}._7D6uKa_card:hover{box-shadow:var(--dsw-shadow-lv2);border-color:var(--dsw-alias-border-l3);transform:translateY(-1px)}._7D6uKa_card[data-status=running]{border-color:var(--dsw-alias-state-warn-primary)}._7D6uKa_cardTitle{-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:13px;font-weight:600;line-height:1.35;display:-webkit-box;overflow:hidden}._7D6uKa_cardExcerpt{color:var(--dsw-alias-label-secondary);-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12px;line-height:1.4;display:-webkit-box;overflow:hidden}._7D6uKa_cardMeta{color:var(--dsw-alias-label-tertiary);align-items:center;gap:8px;font-size:11px;display:flex}._7D6uKa_cardTime{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}._7D6uKa_cardSchedule{white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover);border-radius:999px;flex:none;padding:2px 6px;font-size:12px;line-height:1}._7D6uKa_cardRun{flex:none}._7D6uKa_cardRun[data-result=failed]{color:var(--dsw-alias-state-error-primary)}._7D6uKa_cardRun[data-result=succeeded]{color:var(--dsw-alias-state-success-primary)}._7D6uKa_cardSession{color:var(--dsw-alias-state-business-primary);flex:none}._7D6uKa_cardRunningLabel{color:var(--dsw-alias-state-warn-primary);font-size:11px}._7D6uKa_cardSpinner{border:2px solid var(--dsw-alias-state-warn-primary);border-top-color:#0000;border-radius:50%;flex:none;width:10px;height:10px;animation:.8s linear infinite _7D6uKa_dshTbSpin}@keyframes _7D6uKa_dshTbSpin{to{transform:rotate(360deg)}}._7D6uKa_primaryButton{color:var(--dsw-alias-label-primary-foreground);background:var(--dsw-alias-button-info-fill);cursor:pointer;white-space:nowrap;border:none;border-radius:8px;padding:6px 14px;font-size:13px;font-weight:600}._7D6uKa_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-info-hover)}._7D6uKa_primaryButton:disabled{opacity:.5;cursor:default}._7D6uKa_ghostButton{color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;white-space:nowrap;background:0 0;border-radius:8px;padding:5px 12px;font-size:12px}._7D6uKa_ghostButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}._7D6uKa_ghostButton:disabled{opacity:.45;cursor:default}._7D6uKa_dangerButton{color:#fff;background:var(--dsw-alias-state-error-primary);cursor:pointer;white-space:nowrap;border:none;border-radius:8px;padding:6px 14px;font-size:13px;font-weight:600}._7D6uKa_dangerButton:hover:not(:disabled){filter:brightness(1.08)}._7D6uKa_dangerButton:active:not(:disabled){filter:brightness(.94)}._7D6uKa_dangerButton:disabled{opacity:.5;cursor:default}._7D6uKa_iconButton{width:26px;height:26px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:0;font-size:13px;display:inline-flex}._7D6uKa_iconButton:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}._7D6uKa_linkButton{color:var(--dsw-alias-state-business-primary);cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:0;font-size:12px}._7D6uKa_linkButton:hover{text-decoration:underline}._7D6uKa_modalBackdrop{z-index:1300;background:var(--dsw-alias-bg-mask-1);justify-content:center;align-items:center;display:flex;position:fixed;inset:0}._7D6uKa_modal{background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);width:min(520px,100vw - 48px);max-height:calc(100vh - 96px);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);border-radius:14px;flex-direction:column;gap:12px;padding:18px;display:flex;overflow-y:auto}._7D6uKa_modalTitle{margin:0;font-size:15px;font-weight:700}._7D6uKa_confirmMessage{color:var(--dsw-alias-label-secondary);white-space:pre-wrap;overflow-wrap:anywhere;margin:0;font-size:13px;line-height:1.5}._7D6uKa_modalFooter{justify-content:flex-end;gap:10px;margin-top:4px;display:flex}._7D6uKa_field{flex-direction:column;gap:5px;display:flex}._7D6uKa_fieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}._7D6uKa_input{color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);resize:vertical;border-radius:8px;outline:none;padding:7px 10px;font-family:inherit;font-size:13px}._7D6uKa_input:focus{border-color:var(--dsw-alias-state-business-primary)}._7D6uKa_select{color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;max-width:100%;padding:7px 10px;font-family:inherit;font-size:13px}._7D6uKa_input::placeholder{color:var(--dsw-alias-label-tertiary)}._7D6uKa_formError{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px}._7D6uKa_detail{background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);width:min(640px,100vw - 48px);max-height:calc(100vh - 80px);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);border-radius:14px;flex-direction:column;display:flex;overflow:hidden}._7D6uKa_detailHeader{border-bottom:1px solid var(--dsw-alias-separator-primary);flex:none;align-items:center;gap:10px;padding:14px 18px;display:flex}._7D6uKa_detailTitle{overflow-wrap:anywhere;flex:1;margin:0;font-size:15px;font-weight:700}._7D6uKa_statusBadge{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:2px 10px;font-size:12px}._7D6uKa_statusBadge[data-status=running]{color:var(--dsw-alias-state-warn-primary);border-color:var(--dsw-alias-state-warn-primary)}._7D6uKa_statusBadge[data-status=done]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}._7D6uKa_statusBadge[data-status=failed]{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}._7D6uKa_detailBody{flex-direction:column;flex:1;gap:16px;padding:14px 18px;display:flex;overflow-y:auto}._7D6uKa_detailSection{flex-direction:column;gap:6px;display:flex}._7D6uKa_detailSection h4{color:var(--dsw-alias-label-tertiary);text-transform:none;margin:0;font-size:12px;font-weight:700}._7D6uKa_detailText{color:var(--dsw-alias-label-primary);white-space:pre-wrap;overflow-wrap:anywhere;margin:0;font-size:13px;line-height:1.55}._7D6uKa_scheduleToggle{color:var(--dsw-alias-label-primary);cursor:pointer;user-select:none;align-items:center;gap:8px;font-size:13px;display:flex}._7D6uKa_scheduleToggle input{accent-color:var(--dsw-alias-state-business-primary)}._7D6uKa_scheduleRow{align-items:center;gap:8px;display:flex}._7D6uKa_scheduleInput{min-width:0;font-family:var(--dsw-font-markdown-code-block-small);flex:1;font-size:12.5px}._7D6uKa_scheduleInputInvalid,._7D6uKa_scheduleInputInvalid:focus{border-color:var(--dsw-alias-state-error-primary)}._7D6uKa_schedulePreset{color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;flex:none;padding:7px 8px;font-size:12.5px}._7D6uKa_scheduleMeta{color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;margin:0;font-size:12px}._7D6uKa_promptBlock{font-size:12.5px;line-height:1.5;font-family:var(--dsw-font-markdown-code-block-small);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-markdown-code-block);border:1px solid var(--dsw-alias-border-l1);white-space:pre-wrap;overflow-wrap:anywhere;border-radius:8px;max-height:240px;margin:0;padding:10px 12px;overflow-y:auto}._7D6uKa_executionList{flex-direction:column;gap:8px;margin:0;padding:0;list-style:none;display:flex}._7D6uKa_executionRow{border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-wrap:wrap;align-items:center;gap:10px;padding:8px 10px;display:flex}._7D6uKa_executionBadge{color:var(--dsw-alias-state-warn-primary);background:var(--dsw-alias-state-warn-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:600}._7D6uKa_executionBadge[data-result=succeeded]{color:var(--dsw-alias-state-success-primary);background:0 0}._7D6uKa_executionBadge[data-result=failed]{color:var(--dsw-alias-state-error-primary);background:0 0}._7D6uKa_executionBadge[data-result=cancelled]{color:var(--dsw-alias-label-tertiary);background:0 0}._7D6uKa_executionTimes{color:var(--dsw-alias-label-secondary);font-size:12px}._7D6uKa_executionError{width:100%;color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;font-size:12px}._7D6uKa_moveRow{flex-wrap:wrap;gap:8px;display:flex}._7D6uKa_detailFooter{border-top:1px solid var(--dsw-alias-separator-primary);flex:none;align-items:center;gap:10px;padding:12px 18px;display:flex}._7D6uKa_detailMeta{color:var(--dsw-alias-label-tertiary);margin-left:auto;font-size:11px}@container _7D6uKa_task-board-view (width<=768px){._7D6uKa_board{gap:10px;padding:10px}._7D6uKa_boardHeader{flex-wrap:wrap;align-items:center;gap:8px}._7D6uKa_backButton{flex:none;order:1}._7D6uKa_boardTitle{flex:auto;order:2}._7D6uKa_boardHeader>._7D6uKa_detailMeta{flex:1 0 100%;order:3;margin-left:0}._7D6uKa_search{flex:1 0 100%;order:4;min-width:0}._7D6uKa_boardHeader>button:not(._7D6uKa_backButton){flex:1 1 0;order:5;min-width:0}._7D6uKa_columns{scroll-snap-type:inline mandatory;scrollbar-width:none;-webkit-overflow-scrolling:touch;grid-auto-columns:86cqw;gap:10px;padding-inline:2px 14cqw;scroll-padding-inline:2px}._7D6uKa_columns::-webkit-scrollbar{display:none}._7D6uKa_column{scroll-snap-align:start;scroll-snap-stop:always}}@container _7D6uKa_task-board-view (width<=720px){._7D6uKa_boardHeader>._7D6uKa_detailMeta{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}}@container _7D6uKa_task-board-view (width<=600px){._7D6uKa_board{padding-inline:8px}}@media (width<=768px){[data-dsh-taskboard-view]{height:100dvh}._7D6uKa_entry,._7D6uKa_card,._7D6uKa_primaryButton,._7D6uKa_ghostButton,._7D6uKa_dangerButton,._7D6uKa_iconButton,._7D6uKa_linkButton,._7D6uKa_search,._7D6uKa_input,._7D6uKa_select,._7D6uKa_schedulePreset,._7D6uKa_scheduleToggle{min-height:44px}._7D6uKa_search,._7D6uKa_input,._7D6uKa_select,._7D6uKa_schedulePreset{box-sizing:border-box;font-size:16px}._7D6uKa_modalBackdrop{justify-content:stretch;align-items:stretch;width:100vw;height:100dvh}._7D6uKa_modal,._7D6uKa_detail{box-sizing:border-box;border:0;border-radius:0;width:100vw;height:100dvh;max-height:none}._7D6uKa_modal{padding-top:max(16px, env(safe-area-inset-top));padding-right:max(16px, env(safe-area-inset-right));padding-bottom:max(16px, env(safe-area-inset-bottom));padding-left:max(16px, env(safe-area-inset-left))}._7D6uKa_modalFooter{z-index:1;background:var(--dsw-alias-bg-base);flex-wrap:wrap;padding-top:8px;position:sticky;bottom:0}._7D6uKa_modalFooter>button{flex:120px}._7D6uKa_detailHeader{padding-top:max(12px, env(safe-area-inset-top));padding-right:max(14px, env(safe-area-inset-right));padding-left:max(14px, env(safe-area-inset-left));flex-wrap:wrap}._7D6uKa_detailTitle{min-width:0}._7D6uKa_detailBody{overscroll-behavior-y:contain;padding-right:max(14px, env(safe-area-inset-right));padding-left:max(14px, env(safe-area-inset-left))}._7D6uKa_detailFooter{padding-right:max(14px, env(safe-area-inset-right));padding-bottom:max(12px, env(safe-area-inset-bottom));padding-left:max(14px, env(safe-area-inset-left));flex-wrap:wrap}._7D6uKa_detailFooter>button{flex:96px}._7D6uKa_detailFooter>._7D6uKa_detailMeta{text-align:end;flex:1 0 100%;margin-left:0}._7D6uKa_scheduleRow{flex-direction:column;align-items:stretch}._7D6uKa_schedulePreset{width:100%}}._7D6uKa_entry:focus-visible,._7D6uKa_card:focus-visible,._7D6uKa_primaryButton:focus-visible,._7D6uKa_ghostButton:focus-visible,._7D6uKa_dangerButton:focus-visible,._7D6uKa_iconButton:focus-visible,._7D6uKa_linkButton:focus-visible,._7D6uKa_search:focus-visible,._7D6uKa_input:focus-visible,._7D6uKa_select:focus-visible,._7D6uKa_schedulePreset:focus-visible,._7D6uKa_scheduleToggle input:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}._7D6uKa_entry,._7D6uKa_primaryButton,._7D6uKa_ghostButton,._7D6uKa_dangerButton,._7D6uKa_iconButton,._7D6uKa_linkButton,._7D6uKa_search,._7D6uKa_input,._7D6uKa_select,._7D6uKa_schedulePreset,._7D6uKa_scheduleToggle input{transition:background-color .12s,color .12s,border-color .12s,outline-color .12s,box-shadow .12s,transform .12s}._7D6uKa_card:active{box-shadow:var(--dsw-shadow-lv1);transform:translateY(0)}._7D6uKa_entry:active,._7D6uKa_primaryButton:active:not(:disabled),._7D6uKa_ghostButton:active:not(:disabled),._7D6uKa_dangerButton:active:not(:disabled),._7D6uKa_iconButton:active:not(:disabled),._7D6uKa_linkButton:active:not(:disabled){transform:translateY(1px)}._7D6uKa_entry[data-active]:hover{background:var(--dsw-specific-sidebar-nav-item-active)}._7D6uKa_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}._7D6uKa_linkButton:hover:not(:disabled){text-decoration:underline}._7D6uKa_iconButton:disabled,._7D6uKa_linkButton:disabled{opacity:.45;cursor:default}._7D6uKa_search:focus,._7D6uKa_select:focus,._7D6uKa_schedulePreset:focus{border-color:var(--dsw-alias-state-business-primary)}._7D6uKa_scheduleToggle input{margin:0}@media (prefers-reduced-motion:reduce){._7D6uKa_entry,._7D6uKa_card,._7D6uKa_primaryButton,._7D6uKa_ghostButton,._7D6uKa_dangerButton,._7D6uKa_iconButton,._7D6uKa_linkButton,._7D6uKa_search,._7D6uKa_input,._7D6uKa_select,._7D6uKa_schedulePreset,._7D6uKa_scheduleToggle input{transition:none}._7D6uKa_cardSpinner{animation:none}}._7D6uKa_cardTags{flex-wrap:wrap;gap:4px;display:flex}._7D6uKa_cardTag{border:1px solid var(--dsh-task-tag-border);background:var(--dsh-task-tag-fill);max-width:100%;color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;border-radius:999px;padding:0 7px;font-size:10px;line-height:16px;overflow:hidden}._7D6uKa_tagFilter{flex-wrap:wrap;align-items:center;gap:6px;margin:0 0 10px;display:flex}._7D6uKa_tagFilterLabel{color:var(--dsw-alias-label-tertiary);font-size:11px}._7D6uKa_tagChip{border:1px solid var(--dsh-task-tag-border);color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border-radius:999px;padding:1px 9px;font-family:inherit;font-size:11px;line-height:18px}._7D6uKa_tagChip[data-active=true]{background:var(--dsh-task-tag-fill);color:var(--dsw-alias-label-primary)}._7D6uKa_cardTag[data-tag-tone=\"0\"],._7D6uKa_tagChip[data-tag-tone=\"0\"]{--dsh-task-tag-fill:#4e93e82e;--dsh-task-tag-border:#4e93e866}._7D6uKa_cardTag[data-tag-tone=\"1\"],._7D6uKa_tagChip[data-tag-tone=\"1\"]{--dsh-task-tag-fill:#2ea36a2e;--dsh-task-tag-border:#2ea36a66}._7D6uKa_cardTag[data-tag-tone=\"2\"],._7D6uKa_tagChip[data-tag-tone=\"2\"]{--dsh-task-tag-fill:#d08a2a2e;--dsh-task-tag-border:#d08a2a66}._7D6uKa_cardTag[data-tag-tone=\"3\"],._7D6uKa_tagChip[data-tag-tone=\"3\"]{--dsh-task-tag-fill:#b456c82e;--dsh-task-tag-border:#b456c866}._7D6uKa_cardTag[data-tag-tone=\"4\"],._7D6uKa_tagChip[data-tag-tone=\"4\"]{--dsh-task-tag-fill:#cf5f7a2e;--dsh-task-tag-border:#cf5f7a66}._7D6uKa_cardTag[data-tag-tone=\"5\"],._7D6uKa_tagChip[data-tag-tone=\"5\"]{--dsh-task-tag-fill:#4a9fb52e;--dsh-task-tag-border:#4a9fb566}._7D6uKa_fieldHint{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.4}._7D6uKa_tagRow{align-items:center;gap:6px;display:flex}._7D6uKa_tagRow ._7D6uKa_input{flex:1 1 0;min-width:0}._7D6uKa_tagRow ._7D6uKa_ghostButton{flex:none}._7D6uKa_tagAddButton{align-self:flex-start}._7D6uKa_projectFilter{flex:none;align-items:center;gap:6px;display:flex}._7D6uKa_projectFilterLabel{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}._7D6uKa_projectDialog{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;flex-direction:column;flex:none;gap:8px;margin-bottom:8px;padding:10px 12px;display:flex}._7D6uKa_projectDialogActions{justify-content:flex-end;gap:8px;display:flex}._7D6uKa_aiParse{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}._7D6uKa_aiParseRow{align-items:center;gap:8px;display:flex}._7D6uKa_aiParseRow ._7D6uKa_select{flex:1 1 0;min-width:0}._7D6uKa_aiParseRow ._7D6uKa_ghostButton,._7D6uKa_aiParseRow ._7D6uKa_primaryButton{flex:none}._7D6uKa_cardSubtask{border:1px solid var(--dsw-alias-border-l2);max-width:100%;color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;background:0 0;border-radius:999px;padding:0 7px;font-size:10px;line-height:16px;overflow:hidden}._7D6uKa_cardSubtask[data-tone=failed]{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}._7D6uKa_cardSubtask[data-tone=running]{color:var(--dsw-alias-state-warn-primary);border-color:var(--dsw-alias-state-warn-primary)}._7D6uKa_cardSubtask[data-tone=done]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}._7D6uKa_subtaskList{flex-direction:column;gap:6px;margin:0;padding:0;list-style:none;display:flex}._7D6uKa_subtaskRow{border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-wrap:wrap;align-items:center;gap:10px;padding:6px 10px;display:flex}._7D6uKa_subtaskRow ._7D6uKa_linkButton:first-child{text-align:left;overflow-wrap:anywhere;flex:auto;min-width:0}._7D6uKa_subtaskAddRow{flex-wrap:wrap;gap:8px;display:flex}._7D6uKa_pickList{flex-direction:column;gap:6px;max-height:320px;margin:0;padding:0;list-style:none;display:flex;overflow-y:auto}._7D6uKa_pickRow{border:1px solid var(--dsw-alias-border-l1);border-radius:8px;align-items:center;gap:10px;padding:6px 10px;display:flex}._7D6uKa_pickTitle{min-width:0;color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;flex:auto;font-size:13px}";
|
|
2001
2391
|
const tagId$1 = "@linxin666/dsh-client-ui-task-board/packages/dsh-task-board/src/client/board.module.css";
|
|
2002
2392
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
2003
2393
|
const tag = document.createElement("style");
|
|
@@ -2021,6 +2411,7 @@ window.__ModuleLoader__.load({
|
|
|
2021
2411
|
"cardSchedule": "_7D6uKa_cardSchedule",
|
|
2022
2412
|
"cardSession": "_7D6uKa_cardSession",
|
|
2023
2413
|
"cardSpinner": "_7D6uKa_cardSpinner",
|
|
2414
|
+
"cardSubtask": "_7D6uKa_cardSubtask",
|
|
2024
2415
|
"cardTag": "_7D6uKa_cardTag",
|
|
2025
2416
|
"cardTags": "_7D6uKa_cardTags",
|
|
2026
2417
|
"cardTime": "_7D6uKa_cardTime",
|
|
@@ -2064,6 +2455,9 @@ window.__ModuleLoader__.load({
|
|
|
2064
2455
|
"modalFooter": "_7D6uKa_modalFooter",
|
|
2065
2456
|
"modalTitle": "_7D6uKa_modalTitle",
|
|
2066
2457
|
"moveRow": "_7D6uKa_moveRow",
|
|
2458
|
+
"pickList": "_7D6uKa_pickList",
|
|
2459
|
+
"pickRow": "_7D6uKa_pickRow",
|
|
2460
|
+
"pickTitle": "_7D6uKa_pickTitle",
|
|
2067
2461
|
"primaryButton": "_7D6uKa_primaryButton",
|
|
2068
2462
|
"projectDialog": "_7D6uKa_projectDialog",
|
|
2069
2463
|
"projectDialogActions": "_7D6uKa_projectDialogActions",
|
|
@@ -2080,6 +2474,9 @@ window.__ModuleLoader__.load({
|
|
|
2080
2474
|
"select": "_7D6uKa_select",
|
|
2081
2475
|
"statusBadge": "_7D6uKa_statusBadge",
|
|
2082
2476
|
"statusDot": "_7D6uKa_statusDot",
|
|
2477
|
+
"subtaskAddRow": "_7D6uKa_subtaskAddRow",
|
|
2478
|
+
"subtaskList": "_7D6uKa_subtaskList",
|
|
2479
|
+
"subtaskRow": "_7D6uKa_subtaskRow",
|
|
2083
2480
|
"tagAddButton": "_7D6uKa_tagAddButton",
|
|
2084
2481
|
"tagChip": "_7D6uKa_tagChip",
|
|
2085
2482
|
"tagFilter": "_7D6uKa_tagFilter",
|
|
@@ -2335,15 +2732,16 @@ window.__ModuleLoader__.load({
|
|
|
2335
2732
|
* Creates through the Host and closes only after the Host confirms it.
|
|
2336
2733
|
*/
|
|
2337
2734
|
/** New-task form overlay. */
|
|
2338
|
-
function NewTaskModal({ controller, onClose, initialTask, defaultWorkspaceId, onDuplicateSuccess }) {
|
|
2735
|
+
function NewTaskModal({ controller, onClose, initialTask, defaultWorkspaceId, onDuplicateSuccess, parentTask }) {
|
|
2339
2736
|
const isDuplicate = initialTask !== void 0;
|
|
2340
2737
|
const [title, setTitle] = (0, react.useState)(initialTask?.title ?? "");
|
|
2341
2738
|
const [description, setDescription] = (0, react.useState)(initialTask?.description ?? "");
|
|
2342
2739
|
const [prompt, setPrompt] = (0, react.useState)(initialTask?.prompt ?? "");
|
|
2343
|
-
const [workspaceId, setWorkspaceId] = (0, react.useState)(initialTask?.workspaceId ?? defaultWorkspaceId ?? "");
|
|
2344
|
-
const [mode, setMode] = (0, react.useState)(initialTask?.mode ?? "");
|
|
2740
|
+
const [workspaceId, setWorkspaceId] = (0, react.useState)(initialTask?.workspaceId ?? parentTask?.workspaceId ?? defaultWorkspaceId ?? "");
|
|
2741
|
+
const [mode, setMode] = (0, react.useState)(initialTask?.mode ?? parentTask?.mode ?? "");
|
|
2345
2742
|
const [permission, setPermission] = (0, react.useState)(initialTask?.permission ?? "");
|
|
2346
|
-
const [model, setModel] = (0, react.useState)(initialTask?.model ?? "");
|
|
2743
|
+
const [model, setModel] = (0, react.useState)(initialTask?.model ?? parentTask?.model ?? "");
|
|
2744
|
+
const inheritedPermission = parentTask === void 0 ? void 0 : effectiveTaskPermission(parentTask);
|
|
2347
2745
|
const [reuseSession, setReuseSession] = (0, react.useState)(initialTask?.reuseSession ?? false);
|
|
2348
2746
|
const [scheduleEnabled, setScheduleEnabled] = (0, react.useState)(initialTask?.schedule?.enabled ?? false);
|
|
2349
2747
|
const [scheduleCron, setScheduleCron] = (0, react.useState)(initialTask?.schedule?.cron ?? "");
|
|
@@ -2363,6 +2761,9 @@ window.__ModuleLoader__.load({
|
|
|
2363
2761
|
const [parseError, setParseError] = (0, react.useState)(void 0);
|
|
2364
2762
|
const parseAbort = (0, react.useRef)(void 0);
|
|
2365
2763
|
const parseModels = options.models ?? [];
|
|
2764
|
+
const workspaceKnown = workspaceId === "" || options.workspaces.some((item) => item.workspaceId === workspaceId);
|
|
2765
|
+
const modeKnown = mode === "" || options.presets.some((item) => item.id === mode);
|
|
2766
|
+
const modelKnown = model === "" || parseModels.some((item) => item.id === model);
|
|
2366
2767
|
(0, react.useEffect)(() => controller.subscribe(() => setOptions(controller.getSnapshot().executionOptions)), [controller]);
|
|
2367
2768
|
(0, react.useEffect)(() => {
|
|
2368
2769
|
if (parseModel === "" || parseModels.length === 0) return;
|
|
@@ -2434,6 +2835,7 @@ window.__ModuleLoader__.load({
|
|
|
2434
2835
|
title,
|
|
2435
2836
|
description,
|
|
2436
2837
|
prompt,
|
|
2838
|
+
...parentTask === void 0 ? {} : { parentId: parentTask.id },
|
|
2437
2839
|
freeze,
|
|
2438
2840
|
handover,
|
|
2439
2841
|
workspaceId: workspaceId === "" ? void 0 : workspaceId,
|
|
@@ -2461,7 +2863,7 @@ window.__ModuleLoader__.load({
|
|
|
2461
2863
|
};
|
|
2462
2864
|
/** Next-run preview for a valid armed cron (creation-time only). */
|
|
2463
2865
|
const scheduleNextRun = scheduleEnabled && scheduleCron.trim() !== "" && isValidCron(scheduleCron) ? nextRunAtMs(scheduleCron, Date.now()) : void 0;
|
|
2464
|
-
const modalTitle = isDuplicate ? t("new.duplicateTitle") : t("board.new");
|
|
2866
|
+
const modalTitle = parentTask !== void 0 ? t("new.subtaskTitle") : isDuplicate ? t("new.duplicateTitle") : t("board.new");
|
|
2465
2867
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ModalShell, {
|
|
2466
2868
|
ariaLabel: modalTitle,
|
|
2467
2869
|
title: modalTitle,
|
|
@@ -2542,6 +2944,17 @@ window.__ModuleLoader__.load({
|
|
|
2542
2944
|
})
|
|
2543
2945
|
]
|
|
2544
2946
|
}),
|
|
2947
|
+
parentTask !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
2948
|
+
className: board_module_css_default.detailText,
|
|
2949
|
+
"data-dsh-part": "subtask-inherit",
|
|
2950
|
+
children: [
|
|
2951
|
+
t("detail.parent"),
|
|
2952
|
+
": ",
|
|
2953
|
+
parentTask.title,
|
|
2954
|
+
" · ",
|
|
2955
|
+
t("new.subtaskInherit")
|
|
2956
|
+
]
|
|
2957
|
+
}),
|
|
2545
2958
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TaskContentFields, {
|
|
2546
2959
|
title,
|
|
2547
2960
|
description,
|
|
@@ -2606,13 +3019,20 @@ window.__ModuleLoader__.load({
|
|
|
2606
3019
|
onChange: (event) => {
|
|
2607
3020
|
setWorkspaceId(event.target.value);
|
|
2608
3021
|
},
|
|
2609
|
-
children: [
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
3022
|
+
children: [
|
|
3023
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3024
|
+
value: "",
|
|
3025
|
+
children: t("exec.workspace.recent")
|
|
3026
|
+
}),
|
|
3027
|
+
!workspaceKnown && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3028
|
+
value: workspaceId,
|
|
3029
|
+
children: [workspaceId, t("exec.mode.removed")]
|
|
3030
|
+
}),
|
|
3031
|
+
options.workspaces.map((workspace) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3032
|
+
value: workspace.workspaceId,
|
|
3033
|
+
children: workspace.title
|
|
3034
|
+
}, workspace.workspaceId))
|
|
3035
|
+
]
|
|
2616
3036
|
})]
|
|
2617
3037
|
}),
|
|
2618
3038
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
@@ -2626,18 +3046,25 @@ window.__ModuleLoader__.load({
|
|
|
2626
3046
|
onChange: (event) => {
|
|
2627
3047
|
setMode(event.target.value);
|
|
2628
3048
|
},
|
|
2629
|
-
children: [
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
3049
|
+
children: [
|
|
3050
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3051
|
+
value: "",
|
|
3052
|
+
children: t("exec.mode.default")
|
|
3053
|
+
}),
|
|
3054
|
+
!modeKnown && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3055
|
+
value: mode,
|
|
3056
|
+
children: [mode, t("exec.mode.removed")]
|
|
3057
|
+
}),
|
|
3058
|
+
options.presets.map((preset) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3059
|
+
value: preset.id,
|
|
3060
|
+
disabled: preset.broken !== void 0,
|
|
3061
|
+
children: [
|
|
3062
|
+
preset.name ?? preset.id,
|
|
3063
|
+
preset.isDefault ? t("exec.mode.defaultSuffix") : "",
|
|
3064
|
+
preset.broken !== void 0 ? t("exec.mode.brokenSuffix") : ""
|
|
3065
|
+
]
|
|
3066
|
+
}, preset.id))
|
|
3067
|
+
]
|
|
2641
3068
|
})]
|
|
2642
3069
|
}),
|
|
2643
3070
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
@@ -2653,7 +3080,7 @@ window.__ModuleLoader__.load({
|
|
|
2653
3080
|
},
|
|
2654
3081
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
2655
3082
|
value: "",
|
|
2656
|
-
children: t("exec.permission.default")
|
|
3083
|
+
children: inheritedPermission === void 0 ? t("exec.permission.default") : t("exec.permission.inheritParent", { permission: t(`exec.permission.${inheritedPermission}`) })
|
|
2657
3084
|
}), TASK_PERMISSIONS.map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
2658
3085
|
value: id,
|
|
2659
3086
|
children: t(`exec.permission.${id}`)
|
|
@@ -2671,13 +3098,20 @@ window.__ModuleLoader__.load({
|
|
|
2671
3098
|
onChange: (event) => {
|
|
2672
3099
|
setModel(event.target.value);
|
|
2673
3100
|
},
|
|
2674
|
-
children: [
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
3101
|
+
children: [
|
|
3102
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3103
|
+
value: "",
|
|
3104
|
+
children: t("exec.model.default")
|
|
3105
|
+
}),
|
|
3106
|
+
!modelKnown && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3107
|
+
value: model,
|
|
3108
|
+
children: [model, t("exec.model.unknown")]
|
|
3109
|
+
}),
|
|
3110
|
+
options.models?.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3111
|
+
value: item.id,
|
|
3112
|
+
children: item.name ?? item.id
|
|
3113
|
+
}, item.id))
|
|
3114
|
+
]
|
|
2681
3115
|
})]
|
|
2682
3116
|
}),
|
|
2683
3117
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
@@ -2811,7 +3245,7 @@ window.__ModuleLoader__.load({
|
|
|
2811
3245
|
if (timeZone !== void 0) return formatHostTimestamp(ms, timeZone);
|
|
2812
3246
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
2813
3247
|
}
|
|
2814
|
-
function TaskCardInner({ task, pending, timeZone, onClick }) {
|
|
3248
|
+
function TaskCardInner({ task, pending, timeZone, onClick, subtaskCount = 0, isSubtask = false, subtasksDone = 0, subtasksRunning = 0, subtasksFailed = 0 }) {
|
|
2815
3249
|
const latest = task.executions[task.executions.length - 1];
|
|
2816
3250
|
const runs = task.executions.length;
|
|
2817
3251
|
const archived = task.archivedAt !== void 0;
|
|
@@ -2852,6 +3286,27 @@ window.__ModuleLoader__.load({
|
|
|
2852
3286
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2853
3287
|
className: board_module_css_default.cardMeta,
|
|
2854
3288
|
children: [
|
|
3289
|
+
isSubtask && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3290
|
+
className: board_module_css_default.cardSubtask,
|
|
3291
|
+
"data-dsh-part": "subtask-badge",
|
|
3292
|
+
children: t("card.subtask")
|
|
3293
|
+
}),
|
|
3294
|
+
subtaskCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3295
|
+
className: board_module_css_default.cardSubtask,
|
|
3296
|
+
"data-dsh-part": "subtask-count",
|
|
3297
|
+
"data-tone": subtasksFailed > 0 ? "failed" : subtasksRunning > 0 ? "running" : subtasksDone === subtaskCount ? "done" : void 0,
|
|
3298
|
+
title: t("card.subtasksBreakdown", {
|
|
3299
|
+
total: String(subtaskCount),
|
|
3300
|
+
done: String(subtasksDone),
|
|
3301
|
+
running: String(subtasksRunning),
|
|
3302
|
+
failed: String(subtasksFailed)
|
|
3303
|
+
}),
|
|
3304
|
+
children: [
|
|
3305
|
+
t("card.subtasks", { count: String(subtaskCount) }),
|
|
3306
|
+
subtasksFailed > 0 ? " · " + t("card.subtasksFailed", { count: String(subtasksFailed) }) : "",
|
|
3307
|
+
subtasksFailed === 0 && subtasksRunning > 0 ? " · " + t("card.subtasksRunning", { count: String(subtasksRunning) }) : ""
|
|
3308
|
+
]
|
|
3309
|
+
}),
|
|
2855
3310
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2856
3311
|
className: board_module_css_default.cardTime,
|
|
2857
3312
|
children: [
|
|
@@ -2896,7 +3351,7 @@ window.__ModuleLoader__.load({
|
|
|
2896
3351
|
}),
|
|
2897
3352
|
!archived && latest !== void 0 && executionLabel(latest) === "running" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2898
3353
|
className: board_module_css_default.cardRunningLabel,
|
|
2899
|
-
children: [t("detail.result.running"), "…"]
|
|
3354
|
+
children: [latest.ownResult === void 0 ? t("detail.result.running") : t("detail.subtasks.waiting"), "…"]
|
|
2900
3355
|
})
|
|
2901
3356
|
]
|
|
2902
3357
|
});
|
|
@@ -3044,6 +3499,95 @@ window.__ModuleLoader__.load({
|
|
|
3044
3499
|
});
|
|
3045
3500
|
}
|
|
3046
3501
|
//#endregion
|
|
3502
|
+
//#region src/client/board/LinkSubtaskModal.tsx
|
|
3503
|
+
/**
|
|
3504
|
+
* Link-subtask modal: attach an existing on-board root task under the open
|
|
3505
|
+
* task. The candidate list mirrors the Host lineage gate (checkParentLink), so
|
|
3506
|
+
* a link the Host would refuse is not offered in the first place; the Host
|
|
3507
|
+
* still re-checks the action at submit time.
|
|
3508
|
+
*/
|
|
3509
|
+
/** Link-subtask overlay. */
|
|
3510
|
+
function LinkSubtaskModal({ controller, parent, onClose }) {
|
|
3511
|
+
const [pending, setPending] = (0, react.useState)(false);
|
|
3512
|
+
const [error, setError] = (0, react.useState)(void 0);
|
|
3513
|
+
const snapshot = controller.getSnapshot();
|
|
3514
|
+
const limit = snapshot.host?.maxSubtaskDepth ?? 1;
|
|
3515
|
+
const candidates = snapshot.tasks.filter((task) => task.archivedAt === void 0 && task.status !== "running" && task.parentId === void 0 && task.id !== parent.id && checkParentLink(snapshot.tasks, task.id, parent.id, limit).ok);
|
|
3516
|
+
const link = async (taskId) => {
|
|
3517
|
+
setPending(true);
|
|
3518
|
+
setError(void 0);
|
|
3519
|
+
if (await controller.setParent(taskId, parent.id)) {
|
|
3520
|
+
onClose();
|
|
3521
|
+
return;
|
|
3522
|
+
}
|
|
3523
|
+
setPending(false);
|
|
3524
|
+
setError(controller.getSnapshot().transportError ?? t("new.required"));
|
|
3525
|
+
};
|
|
3526
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3527
|
+
className: board_module_css_default.modalBackdrop,
|
|
3528
|
+
onMouseDown: (event) => {
|
|
3529
|
+
if (event.target === event.currentTarget) onClose();
|
|
3530
|
+
},
|
|
3531
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3532
|
+
className: board_module_css_default.modal,
|
|
3533
|
+
role: "dialog",
|
|
3534
|
+
"aria-label": t("link.subtask.title"),
|
|
3535
|
+
children: [
|
|
3536
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
3537
|
+
className: board_module_css_default.modalTitle,
|
|
3538
|
+
children: t("link.subtask.title")
|
|
3539
|
+
}),
|
|
3540
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3541
|
+
className: board_module_css_default.fieldHint,
|
|
3542
|
+
children: t("link.subtask.hint")
|
|
3543
|
+
}),
|
|
3544
|
+
candidates.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3545
|
+
className: board_module_css_default.detailText,
|
|
3546
|
+
children: t("link.subtask.empty")
|
|
3547
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
3548
|
+
className: board_module_css_default.pickList,
|
|
3549
|
+
children: candidates.map((task) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
3550
|
+
className: board_module_css_default.pickRow,
|
|
3551
|
+
children: [
|
|
3552
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3553
|
+
className: board_module_css_default.pickTitle,
|
|
3554
|
+
children: task.title
|
|
3555
|
+
}),
|
|
3556
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3557
|
+
className: board_module_css_default.statusBadge,
|
|
3558
|
+
"data-status": task.status,
|
|
3559
|
+
children: t(STATUS_KEY[task.status])
|
|
3560
|
+
}),
|
|
3561
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3562
|
+
type: "button",
|
|
3563
|
+
className: board_module_css_default.primaryButton,
|
|
3564
|
+
disabled: pending,
|
|
3565
|
+
onClick: () => {
|
|
3566
|
+
link(task.id);
|
|
3567
|
+
},
|
|
3568
|
+
children: t("link.subtask.confirm")
|
|
3569
|
+
})
|
|
3570
|
+
]
|
|
3571
|
+
}, task.id))
|
|
3572
|
+
}),
|
|
3573
|
+
error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3574
|
+
className: board_module_css_default.formError,
|
|
3575
|
+
children: error
|
|
3576
|
+
}),
|
|
3577
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("footer", {
|
|
3578
|
+
className: board_module_css_default.modalFooter,
|
|
3579
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3580
|
+
type: "button",
|
|
3581
|
+
className: board_module_css_default.ghostButton,
|
|
3582
|
+
onClick: onClose,
|
|
3583
|
+
children: t("new.cancel")
|
|
3584
|
+
})
|
|
3585
|
+
})
|
|
3586
|
+
]
|
|
3587
|
+
})
|
|
3588
|
+
});
|
|
3589
|
+
}
|
|
3590
|
+
//#endregion
|
|
3047
3591
|
//#region src/client/board/TaskDetail.tsx
|
|
3048
3592
|
/**
|
|
3049
3593
|
* Task detail: the full view of one task — content, prompt, execution
|
|
@@ -3102,7 +3646,13 @@ window.__ModuleLoader__.load({
|
|
|
3102
3646
|
/** The execution-target editor: workspace / mode / permission pickers. */
|
|
3103
3647
|
function ExecutionSettingsSection({ controller, task, pending }) {
|
|
3104
3648
|
const [options, setOptions] = (0, react.useState)(controller.getSnapshot().executionOptions);
|
|
3105
|
-
|
|
3649
|
+
const teamRunOffered = () => controller.getSnapshot().host?.teamRunAvailable === true;
|
|
3650
|
+
const [teamRunAvailable, setTeamRunAvailable] = (0, react.useState)(teamRunOffered());
|
|
3651
|
+
(0, react.useEffect)(() => controller.subscribe(() => {
|
|
3652
|
+
const snapshot = controller.getSnapshot();
|
|
3653
|
+
setOptions(snapshot.executionOptions);
|
|
3654
|
+
setTeamRunAvailable(snapshot.host?.teamRunAvailable === true);
|
|
3655
|
+
}), [controller]);
|
|
3106
3656
|
const workspaceId = task.workspaceId ?? "";
|
|
3107
3657
|
const mode = task.mode ?? "";
|
|
3108
3658
|
const permission = task.permission ?? "";
|
|
@@ -3242,6 +3792,21 @@ window.__ModuleLoader__.load({
|
|
|
3242
3792
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3243
3793
|
className: board_module_css_default.detailText,
|
|
3244
3794
|
children: t("exec.reuseSessionHint")
|
|
3795
|
+
}),
|
|
3796
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3797
|
+
className: board_module_css_default.scheduleToggle,
|
|
3798
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3799
|
+
type: "checkbox",
|
|
3800
|
+
checked: task.teamRun === true,
|
|
3801
|
+
disabled: pending || !teamRunAvailable,
|
|
3802
|
+
onChange: (event) => {
|
|
3803
|
+
controller.updateTask(task.id, { teamRun: event.target.checked });
|
|
3804
|
+
}
|
|
3805
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("exec.teamRun") })]
|
|
3806
|
+
}),
|
|
3807
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3808
|
+
className: board_module_css_default.detailText,
|
|
3809
|
+
children: teamRunAvailable ? t("exec.teamRunHint") : t("exec.teamRunUnavailable")
|
|
3245
3810
|
})
|
|
3246
3811
|
]
|
|
3247
3812
|
});
|
|
@@ -3370,6 +3935,125 @@ window.__ModuleLoader__.load({
|
|
|
3370
3935
|
]
|
|
3371
3936
|
});
|
|
3372
3937
|
}
|
|
3938
|
+
/**
|
|
3939
|
+
* The subtask block: the parent link, the direct subtasks with their status,
|
|
3940
|
+
* and the actions that grow or prune the tree. Every gate here mirrors the
|
|
3941
|
+
* Host lineage gate for affordance only; the Host re-checks the action.
|
|
3942
|
+
*/
|
|
3943
|
+
function SubtaskSection({ controller, task, pending, archived }) {
|
|
3944
|
+
const [snapshot, setSnapshot] = (0, react.useState)(controller.getSnapshot());
|
|
3945
|
+
(0, react.useEffect)(() => controller.subscribe(() => setSnapshot(controller.getSnapshot())), [controller]);
|
|
3946
|
+
const [showAdd, setShowAdd] = (0, react.useState)(false);
|
|
3947
|
+
const [showLink, setShowLink] = (0, react.useState)(false);
|
|
3948
|
+
const tasks = snapshot.tasks;
|
|
3949
|
+
const parent = task.parentId === void 0 ? void 0 : tasks.find((candidate) => candidate.id === task.parentId);
|
|
3950
|
+
const children = directSubtasks(tasks, task.id);
|
|
3951
|
+
const limit = snapshot.host?.maxSubtaskDepth ?? 1;
|
|
3952
|
+
const canAddChild = taskDepth(tasks, task.id) + 1 <= limit;
|
|
3953
|
+
const editable = !archived && !pending;
|
|
3954
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3955
|
+
className: board_module_css_default.detailSection,
|
|
3956
|
+
"data-dsh-part": "subtasks",
|
|
3957
|
+
children: [
|
|
3958
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("detail.subtasks") }),
|
|
3959
|
+
parent !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
3960
|
+
className: board_module_css_default.detailText,
|
|
3961
|
+
children: [
|
|
3962
|
+
t("detail.parent"),
|
|
3963
|
+
":",
|
|
3964
|
+
" ",
|
|
3965
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3966
|
+
type: "button",
|
|
3967
|
+
className: board_module_css_default.linkButton,
|
|
3968
|
+
title: t("detail.parent.open"),
|
|
3969
|
+
onClick: () => {
|
|
3970
|
+
controller.openTask(parent.id);
|
|
3971
|
+
},
|
|
3972
|
+
children: parent.title
|
|
3973
|
+
})
|
|
3974
|
+
]
|
|
3975
|
+
}),
|
|
3976
|
+
children.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3977
|
+
className: board_module_css_default.detailText,
|
|
3978
|
+
children: t("detail.subtasks.empty")
|
|
3979
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
3980
|
+
className: board_module_css_default.subtaskList,
|
|
3981
|
+
children: children.map((child) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
3982
|
+
className: board_module_css_default.subtaskRow,
|
|
3983
|
+
children: [
|
|
3984
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3985
|
+
type: "button",
|
|
3986
|
+
className: board_module_css_default.linkButton,
|
|
3987
|
+
onClick: () => {
|
|
3988
|
+
controller.openTask(child.id);
|
|
3989
|
+
},
|
|
3990
|
+
children: child.title
|
|
3991
|
+
}),
|
|
3992
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3993
|
+
className: board_module_css_default.statusBadge,
|
|
3994
|
+
"data-status": child.status,
|
|
3995
|
+
children: t(STATUS_KEY[child.status])
|
|
3996
|
+
}),
|
|
3997
|
+
!archived && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3998
|
+
type: "button",
|
|
3999
|
+
className: board_module_css_default.linkButton,
|
|
4000
|
+
disabled: pending || child.status === "running",
|
|
4001
|
+
title: child.status === "running" ? t("detail.subtasks.runningLock") : void 0,
|
|
4002
|
+
onClick: () => {
|
|
4003
|
+
controller.setParent(child.id, null);
|
|
4004
|
+
},
|
|
4005
|
+
children: t("detail.subtasks.detach")
|
|
4006
|
+
})
|
|
4007
|
+
]
|
|
4008
|
+
}, child.id))
|
|
4009
|
+
}),
|
|
4010
|
+
children.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4011
|
+
className: board_module_css_default.detailText,
|
|
4012
|
+
children: t("detail.subtasks.runHint", { count: String(children.length) })
|
|
4013
|
+
}),
|
|
4014
|
+
!archived && (canAddChild ? controller.isHostBacked() ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4015
|
+
className: board_module_css_default.subtaskAddRow,
|
|
4016
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4017
|
+
type: "button",
|
|
4018
|
+
className: board_module_css_default.ghostButton,
|
|
4019
|
+
disabled: !editable,
|
|
4020
|
+
onClick: () => {
|
|
4021
|
+
setShowAdd(true);
|
|
4022
|
+
},
|
|
4023
|
+
children: ["+ ", t("detail.subtasks.add")]
|
|
4024
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4025
|
+
type: "button",
|
|
4026
|
+
className: board_module_css_default.ghostButton,
|
|
4027
|
+
disabled: !editable,
|
|
4028
|
+
onClick: () => {
|
|
4029
|
+
setShowLink(true);
|
|
4030
|
+
},
|
|
4031
|
+
children: t("detail.subtasks.link")
|
|
4032
|
+
})]
|
|
4033
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4034
|
+
className: board_module_css_default.detailMeta,
|
|
4035
|
+
children: t("detail.subtasks.hostOnly")
|
|
4036
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4037
|
+
className: board_module_css_default.detailMeta,
|
|
4038
|
+
children: t("detail.subtasks.depthLimit", { depth: String(limit) })
|
|
4039
|
+
})),
|
|
4040
|
+
showAdd && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NewTaskModal, {
|
|
4041
|
+
controller,
|
|
4042
|
+
parentTask: task,
|
|
4043
|
+
onClose: () => {
|
|
4044
|
+
setShowAdd(false);
|
|
4045
|
+
}
|
|
4046
|
+
}),
|
|
4047
|
+
showLink && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LinkSubtaskModal, {
|
|
4048
|
+
controller,
|
|
4049
|
+
parent: task,
|
|
4050
|
+
onClose: () => {
|
|
4051
|
+
setShowLink(false);
|
|
4052
|
+
}
|
|
4053
|
+
})
|
|
4054
|
+
]
|
|
4055
|
+
});
|
|
4056
|
+
}
|
|
3373
4057
|
/** Task detail overlay. */
|
|
3374
4058
|
function TaskDetail({ controller, task }) {
|
|
3375
4059
|
const [confirmDelete, setConfirmDelete] = (0, react.useState)(false);
|
|
@@ -3393,6 +4077,7 @@ window.__ModuleLoader__.load({
|
|
|
3393
4077
|
const transportError = snapshot.transportError;
|
|
3394
4078
|
const timeZone = snapshot.host?.scheduler.timeZone;
|
|
3395
4079
|
const permissionPending = requiresPermissionConfirmation(current, snapshot.host?.sessionDefaultPermission);
|
|
4080
|
+
const subtaskChildren = directSubtasks(snapshot.tasks, current.id);
|
|
3396
4081
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3397
4082
|
className: board_module_css_default.modalBackdrop,
|
|
3398
4083
|
onMouseDown: (event) => {
|
|
@@ -3452,6 +4137,12 @@ window.__ModuleLoader__.load({
|
|
|
3452
4137
|
children: current.description !== "" ? current.description : "—"
|
|
3453
4138
|
})]
|
|
3454
4139
|
}),
|
|
4140
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SubtaskSection, {
|
|
4141
|
+
controller,
|
|
4142
|
+
task: current,
|
|
4143
|
+
pending,
|
|
4144
|
+
archived
|
|
4145
|
+
}),
|
|
3455
4146
|
current.tags !== void 0 && current.tags.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3456
4147
|
className: board_module_css_default.detailSection,
|
|
3457
4148
|
"data-dsh-part": "tags",
|
|
@@ -3656,6 +4347,7 @@ window.__ModuleLoader__.load({
|
|
|
3656
4347
|
type: "button",
|
|
3657
4348
|
className: board_module_css_default.primaryButton,
|
|
3658
4349
|
disabled: running || pending,
|
|
4350
|
+
title: subtaskChildren.length > 0 ? t("detail.subtasks.runHint", { count: String(subtaskChildren.length) }) : void 0,
|
|
3659
4351
|
onClick: () => {
|
|
3660
4352
|
controller.rerunTask(current.id).then(() => {
|
|
3661
4353
|
if (controller.getSnapshot().transportError === void 0) controller.closeTask();
|
|
@@ -3772,14 +4464,19 @@ window.__ModuleLoader__.load({
|
|
|
3772
4464
|
* re-renders only when its own task changes — not when a sibling card status,
|
|
3773
4465
|
* the filter, or the selection moves.
|
|
3774
4466
|
*/
|
|
3775
|
-
const MemoTaskCard = (0, react.memo)(function MemoTaskCard({ task, pending, timeZone, onOpen }) {
|
|
4467
|
+
const MemoTaskCard = (0, react.memo)(function MemoTaskCard({ task, pending, timeZone, onOpen, subtaskCount, isSubtask, subtasksDone, subtasksRunning, subtasksFailed }) {
|
|
3776
4468
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TaskCard, {
|
|
3777
4469
|
task,
|
|
3778
4470
|
pending,
|
|
3779
4471
|
timeZone,
|
|
3780
4472
|
onClick: (0, react.useCallback)(() => {
|
|
3781
4473
|
onOpen(task.id);
|
|
3782
|
-
}, [task.id, onOpen])
|
|
4474
|
+
}, [task.id, onOpen]),
|
|
4475
|
+
subtaskCount,
|
|
4476
|
+
isSubtask,
|
|
4477
|
+
subtasksDone,
|
|
4478
|
+
subtasksRunning,
|
|
4479
|
+
subtasksFailed
|
|
3783
4480
|
});
|
|
3784
4481
|
});
|
|
3785
4482
|
/** Board component; subscribes to the controller snapshot. */
|
|
@@ -3788,6 +4485,7 @@ window.__ModuleLoader__.load({
|
|
|
3788
4485
|
(0, react.useEffect)(() => controller.subscribe(() => setSnapshot(controller.getSnapshot())), [controller]);
|
|
3789
4486
|
const [filter, setFilter] = (0, react.useState)("");
|
|
3790
4487
|
const [tagFilter, setTagFilter] = (0, react.useState)([]);
|
|
4488
|
+
const [hideSubtasks, setHideSubtasks] = (0, react.useState)(true);
|
|
3791
4489
|
const [showNew, setShowNew] = (0, react.useState)(false);
|
|
3792
4490
|
const [projectId, setProjectId] = (0, react.useState)("");
|
|
3793
4491
|
const [showNewProject, setShowNewProject] = (0, react.useState)(false);
|
|
@@ -3797,7 +4495,26 @@ window.__ModuleLoader__.load({
|
|
|
3797
4495
|
const selected = selectedTaskOf(snapshot);
|
|
3798
4496
|
const archiveView = snapshot.archiveView;
|
|
3799
4497
|
const knownTags = collectKnownTags(snapshot.tasks);
|
|
3800
|
-
const
|
|
4498
|
+
const onBoard = snapshot.tasks.filter((task) => archiveView ? task.archivedAt !== void 0 : task.archivedAt === void 0);
|
|
4499
|
+
const subtaskCounts = /* @__PURE__ */ new Map();
|
|
4500
|
+
const subtaskRollup = /* @__PURE__ */ new Map();
|
|
4501
|
+
for (const task of onBoard) {
|
|
4502
|
+
if (task.parentId === void 0) continue;
|
|
4503
|
+
subtaskCounts.set(task.parentId, (subtaskCounts.get(task.parentId) ?? 0) + 1);
|
|
4504
|
+
const rollup = subtaskRollup.get(task.parentId) ?? {
|
|
4505
|
+
done: 0,
|
|
4506
|
+
running: 0,
|
|
4507
|
+
failed: 0
|
|
4508
|
+
};
|
|
4509
|
+
if (task.status === "done") rollup.done += 1;
|
|
4510
|
+
else if (task.status === "running") rollup.running += 1;
|
|
4511
|
+
else if (task.status === "failed") rollup.failed += 1;
|
|
4512
|
+
subtaskRollup.set(task.parentId, rollup);
|
|
4513
|
+
}
|
|
4514
|
+
const hasSubtasks = subtaskCounts.size > 0;
|
|
4515
|
+
const searchActive = filter.trim() !== "" || tagFilter.length > 0;
|
|
4516
|
+
const hidingSubtasks = hideSubtasks && !searchActive;
|
|
4517
|
+
const visible = onBoard.filter((task) => (projectId === "" || task.workspaceId === projectId) && (!hidingSubtasks || task.parentId === void 0) && matchesFilter(task, filter) && matchesTagFilter(task, tagFilter));
|
|
3801
4518
|
const projects = snapshot.executionOptions.workspaces;
|
|
3802
4519
|
const canCreateProject = snapshot.canCreateWorkspace === true;
|
|
3803
4520
|
const submitNewProject = async () => {
|
|
@@ -3899,6 +4616,17 @@ window.__ModuleLoader__.load({
|
|
|
3899
4616
|
},
|
|
3900
4617
|
"aria-label": t("board.search")
|
|
3901
4618
|
}),
|
|
4619
|
+
hasSubtasks && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4620
|
+
type: "button",
|
|
4621
|
+
className: hidingSubtasks ? board_module_css_default.primaryButton : board_module_css_default.ghostButton,
|
|
4622
|
+
"data-dsh-part": "subtask-filter",
|
|
4623
|
+
"aria-pressed": hidingSubtasks,
|
|
4624
|
+
title: t("board.subtaskFilterHint"),
|
|
4625
|
+
onClick: () => {
|
|
4626
|
+
setHideSubtasks((value) => !value);
|
|
4627
|
+
},
|
|
4628
|
+
children: hidingSubtasks ? t("board.showSubtasks") : t("board.hideSubtasks")
|
|
4629
|
+
}),
|
|
3902
4630
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3903
4631
|
type: "button",
|
|
3904
4632
|
className: archiveView ? board_module_css_default.primaryButton : board_module_css_default.ghostButton,
|
|
@@ -4033,7 +4761,12 @@ window.__ModuleLoader__.load({
|
|
|
4033
4761
|
task,
|
|
4034
4762
|
pending: snapshot.pendingTaskIds.includes(task.id),
|
|
4035
4763
|
timeZone: snapshot.host?.scheduler.timeZone,
|
|
4036
|
-
onOpen: openTask
|
|
4764
|
+
onOpen: openTask,
|
|
4765
|
+
subtaskCount: subtaskCounts.get(task.id) ?? 0,
|
|
4766
|
+
isSubtask: task.parentId !== void 0,
|
|
4767
|
+
subtasksDone: subtaskRollup.get(task.id)?.done ?? 0,
|
|
4768
|
+
subtasksRunning: subtaskRollup.get(task.id)?.running ?? 0,
|
|
4769
|
+
subtasksFailed: subtaskRollup.get(task.id)?.failed ?? 0
|
|
4037
4770
|
}, task.id)), visible.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4038
4771
|
className: board_module_css_default.columnEmpty,
|
|
4039
4772
|
children: tagFilter.length > 0 ? t("board.tagEmpty") : t("archive.empty")
|
|
@@ -4080,7 +4813,12 @@ window.__ModuleLoader__.load({
|
|
|
4080
4813
|
task,
|
|
4081
4814
|
pending: snapshot.pendingTaskIds.includes(task.id),
|
|
4082
4815
|
timeZone: snapshot.host?.scheduler.timeZone,
|
|
4083
|
-
onOpen: openTask
|
|
4816
|
+
onOpen: openTask,
|
|
4817
|
+
subtaskCount: subtaskCounts.get(task.id) ?? 0,
|
|
4818
|
+
isSubtask: task.parentId !== void 0,
|
|
4819
|
+
subtasksDone: subtaskRollup.get(task.id)?.done ?? 0,
|
|
4820
|
+
subtasksRunning: subtaskRollup.get(task.id)?.running ?? 0,
|
|
4821
|
+
subtasksFailed: subtaskRollup.get(task.id)?.failed ?? 0
|
|
4084
4822
|
}, task.id)), tasks.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4085
4823
|
className: board_module_css_default.columnEmpty,
|
|
4086
4824
|
children: tagFilter.length > 0 ? t("board.tagEmpty") : t("board.empty")
|
|
@@ -4896,6 +5634,49 @@ window.__ModuleLoader__.load({
|
|
|
4896
5634
|
]
|
|
4897
5635
|
});
|
|
4898
5636
|
}
|
|
5637
|
+
/** A staged enumerated field rendered as a select. */
|
|
5638
|
+
function ChoiceField(props) {
|
|
5639
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5640
|
+
className: settings_card_module_css_default.field,
|
|
5641
|
+
children: [
|
|
5642
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5643
|
+
className: settings_card_module_css_default.head,
|
|
5644
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
5645
|
+
className: settings_card_module_css_default.label,
|
|
5646
|
+
htmlFor: props.id,
|
|
5647
|
+
children: props.label
|
|
5648
|
+
}), props.overridden ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5649
|
+
className: settings_card_module_css_default.badges,
|
|
5650
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5651
|
+
className: settings_card_module_css_default.badge,
|
|
5652
|
+
children: props.overriddenLabel
|
|
5653
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5654
|
+
type: "button",
|
|
5655
|
+
className: settings_card_module_css_default.reset,
|
|
5656
|
+
disabled: props.disabled,
|
|
5657
|
+
onClick: props.onReset,
|
|
5658
|
+
children: props.resetLabel
|
|
5659
|
+
})]
|
|
5660
|
+
}) : null]
|
|
5661
|
+
}),
|
|
5662
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectField, {
|
|
5663
|
+
id: props.id,
|
|
5664
|
+
options: [{
|
|
5665
|
+
value: "",
|
|
5666
|
+
label: props.inheritLabel
|
|
5667
|
+
}, ...props.choices],
|
|
5668
|
+
value: props.text,
|
|
5669
|
+
disabled: props.disabled,
|
|
5670
|
+
invalid: props.invalid,
|
|
5671
|
+
onEdit: props.onEdit
|
|
5672
|
+
}),
|
|
5673
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5674
|
+
className: props.invalid ? settings_card_module_css_default.invalid : settings_card_module_css_default.hint,
|
|
5675
|
+
children: props.invalid ? props.invalidLabel : props.hint
|
|
5676
|
+
})
|
|
5677
|
+
]
|
|
5678
|
+
});
|
|
5679
|
+
}
|
|
4899
5680
|
//#endregion
|
|
4900
5681
|
//#region \0dsh-store-engine
|
|
4901
5682
|
const platform = ["@deepseek-ai/dsh-client", "-store"].join("");
|
|
@@ -5181,6 +5962,28 @@ window.__ModuleLoader__.load({
|
|
|
5181
5962
|
};
|
|
5182
5963
|
//#endregion
|
|
5183
5964
|
//#region src/client/TaskBoardSettingsCard.tsx
|
|
5965
|
+
/** The depth choices the card offers, derived from the supported range. */
|
|
5966
|
+
const SUBTASK_DEPTH_CHOICES = Array.from({ length: 3 }, (_, index) => String(1 + index));
|
|
5967
|
+
/**
|
|
5968
|
+
* The depth field: a choice among the supported levels whose draft text is a
|
|
5969
|
+
* number, because the Host schema (`maxSubtaskDepth`) is numeric. A draft
|
|
5970
|
+
* outside the range blocks the save instead of staging a value the Host
|
|
5971
|
+
* refuses.
|
|
5972
|
+
*/
|
|
5973
|
+
function subtaskDepthField() {
|
|
5974
|
+
return {
|
|
5975
|
+
field: "maxSubtaskDepth",
|
|
5976
|
+
format: (value) => typeof value === "number" && Number.isInteger(value) ? String(value) : "",
|
|
5977
|
+
parse: (text) => {
|
|
5978
|
+
const trimmed = text.trim();
|
|
5979
|
+
if (trimmed === "") return { kind: "clear" };
|
|
5980
|
+
return SUBTASK_DEPTH_CHOICES.includes(trimmed) ? {
|
|
5981
|
+
kind: "set",
|
|
5982
|
+
value: Number(trimmed)
|
|
5983
|
+
} : void 0;
|
|
5984
|
+
}
|
|
5985
|
+
};
|
|
5986
|
+
}
|
|
5184
5987
|
/** Bridges the `task-board` settings form onto the card's staged form. */
|
|
5185
5988
|
var TaskBoardSettingsCardController = class {
|
|
5186
5989
|
form;
|
|
@@ -5190,7 +5993,8 @@ window.__ModuleLoader__.load({
|
|
|
5190
5993
|
this.form = new CardForm(scope, [
|
|
5191
5994
|
booleanField("enabled"),
|
|
5192
5995
|
booleanField("announceToAgent"),
|
|
5193
|
-
booleanField("preventIdleSleep")
|
|
5996
|
+
booleanField("preventIdleSleep"),
|
|
5997
|
+
subtaskDepthField()
|
|
5194
5998
|
]);
|
|
5195
5999
|
this.store = this.form.bind(() => this.projection());
|
|
5196
6000
|
}
|
|
@@ -5199,7 +6003,8 @@ window.__ModuleLoader__.load({
|
|
|
5199
6003
|
...this.form.shell(),
|
|
5200
6004
|
enabled: this.form.field("enabled"),
|
|
5201
6005
|
announceToAgent: this.form.field("announceToAgent"),
|
|
5202
|
-
preventIdleSleep: this.form.field("preventIdleSleep")
|
|
6006
|
+
preventIdleSleep: this.form.field("preventIdleSleep"),
|
|
6007
|
+
maxSubtaskDepth: this.form.field("maxSubtaskDepth")
|
|
5203
6008
|
};
|
|
5204
6009
|
}
|
|
5205
6010
|
/**
|
|
@@ -5232,7 +6037,7 @@ window.__ModuleLoader__.load({
|
|
|
5232
6037
|
const [power, setPower] = (0, react.useState)();
|
|
5233
6038
|
(0, react.useEffect)(() => {
|
|
5234
6039
|
let live = true;
|
|
5235
|
-
const events = new EventSource("
|
|
6040
|
+
const events = new EventSource("api/task-board/events");
|
|
5236
6041
|
events.onmessage = (message) => {
|
|
5237
6042
|
try {
|
|
5238
6043
|
const frame = JSON.parse(message.data);
|
|
@@ -5309,6 +6114,24 @@ window.__ModuleLoader__.load({
|
|
|
5309
6114
|
props.resetField("preventIdleSleep");
|
|
5310
6115
|
}
|
|
5311
6116
|
}),
|
|
6117
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChoiceField, {
|
|
6118
|
+
id: "settings-task-board-subtask-depth",
|
|
6119
|
+
label: t("settings.maxSubtaskDepth"),
|
|
6120
|
+
hint: t("settings.maxSubtaskDepthHint"),
|
|
6121
|
+
inheritLabel: t("settings.inherit"),
|
|
6122
|
+
choices: SUBTASK_DEPTH_CHOICES.map((value) => ({
|
|
6123
|
+
value,
|
|
6124
|
+
label: t("settings.maxSubtaskDepthOption", { depth: value })
|
|
6125
|
+
})),
|
|
6126
|
+
...fieldProps,
|
|
6127
|
+
...state.maxSubtaskDepth,
|
|
6128
|
+
onEdit: (text) => {
|
|
6129
|
+
props.edit("maxSubtaskDepth", text);
|
|
6130
|
+
},
|
|
6131
|
+
onReset: () => {
|
|
6132
|
+
props.resetField("maxSubtaskDepth");
|
|
6133
|
+
}
|
|
6134
|
+
}),
|
|
5312
6135
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("settings.powerStatus", {
|
|
5313
6136
|
platform: power?.platform ?? t("settings.powerUnknown"),
|
|
5314
6137
|
phase: power?.phase ?? t("settings.powerUnknown"),
|
|
@@ -5331,6 +6154,15 @@ window.__ModuleLoader__.load({
|
|
|
5331
6154
|
}
|
|
5332
6155
|
//#endregion
|
|
5333
6156
|
//#region src/client/host-api.ts
|
|
6157
|
+
/**
|
|
6158
|
+
* Client-side base for the board's routes: DOCUMENT-RELATIVE (issue #1707).
|
|
6159
|
+
*
|
|
6160
|
+
* The host registers `TASK_BOARD_API_PREFIX` root-absolute; the page is served
|
|
6161
|
+
* with `<base href="./">`, so the browser half must drop the leading slash or
|
|
6162
|
+
* a sub-path deployment escapes its entry directory. Derived from the shared
|
|
6163
|
+
* constant so host and client cannot drift.
|
|
6164
|
+
*/
|
|
6165
|
+
const CLIENT_API_PREFIX = TASK_BOARD_API_PREFIX.slice(1);
|
|
5334
6166
|
const IMPORT_MARKER = "dsh.taskBoard.v2.hostImported";
|
|
5335
6167
|
const SOURCE_KEY = "dsh.taskBoard.v2.sourceId";
|
|
5336
6168
|
const IMPORT_REQUEST_KEY = "dsh.taskBoard.v2.importRequestId";
|
|
@@ -5412,7 +6244,7 @@ window.__ModuleLoader__.load({
|
|
|
5412
6244
|
return initial;
|
|
5413
6245
|
}
|
|
5414
6246
|
async state() {
|
|
5415
|
-
return await this.request(`${
|
|
6247
|
+
return await this.request(`${CLIENT_API_PREFIX}/state`, { cache: "no-store" });
|
|
5416
6248
|
}
|
|
5417
6249
|
async action(action, initiator) {
|
|
5418
6250
|
return await this.post(uuid(), action, initiator);
|
|
@@ -5423,7 +6255,7 @@ window.__ModuleLoader__.load({
|
|
|
5423
6255
|
action,
|
|
5424
6256
|
...initiator === void 0 || initiator === "" ? {} : { initiator }
|
|
5425
6257
|
};
|
|
5426
|
-
return await this.request(`${
|
|
6258
|
+
return await this.request(`${CLIENT_API_PREFIX}/action`, {
|
|
5427
6259
|
method: "POST",
|
|
5428
6260
|
headers: { "content-type": "application/json" },
|
|
5429
6261
|
body: JSON.stringify(envelope)
|
|
@@ -5455,7 +6287,7 @@ window.__ModuleLoader__.load({
|
|
|
5455
6287
|
async parseDraft(request, signal) {
|
|
5456
6288
|
let response;
|
|
5457
6289
|
try {
|
|
5458
|
-
response = await fetch(`${
|
|
6290
|
+
response = await fetch(`${CLIENT_API_PREFIX}/parse`, {
|
|
5459
6291
|
method: "POST",
|
|
5460
6292
|
headers: { "content-type": "application/json" },
|
|
5461
6293
|
body: JSON.stringify(request),
|
|
@@ -5486,7 +6318,7 @@ window.__ModuleLoader__.load({
|
|
|
5486
6318
|
throw new HostApiError("rejected", t("new.aiParseFailed", { error: typeof record?.error === "string" && record.error !== "" ? record.error : String(response.status) }), response.status);
|
|
5487
6319
|
}
|
|
5488
6320
|
subscribe(listener) {
|
|
5489
|
-
const events = new EventSource(`${
|
|
6321
|
+
const events = new EventSource(`${CLIENT_API_PREFIX}/events`);
|
|
5490
6322
|
let lastStreamErrorNotify = 0;
|
|
5491
6323
|
events.onmessage = (message) => {
|
|
5492
6324
|
try {
|
|
@@ -5521,7 +6353,7 @@ window.__ModuleLoader__.load({
|
|
|
5521
6353
|
/** The building package's version, when the bundle carries it. */
|
|
5522
6354
|
function bakedVersion() {
|
|
5523
6355
|
try {
|
|
5524
|
-
return "0.4.
|
|
6356
|
+
return "0.4.2";
|
|
5525
6357
|
} catch {
|
|
5526
6358
|
return;
|
|
5527
6359
|
}
|
|
@@ -5890,7 +6722,11 @@ window.__ModuleLoader__.load({
|
|
|
5890
6722
|
if (snapshot.status === "ready" ? snapshot.value?.enabled ?? true : snapshot.status === "unavailable") mountUi();
|
|
5891
6723
|
else uiDisposer?.();
|
|
5892
6724
|
};
|
|
5893
|
-
settingsForm.subscribe(syncEnabled);
|
|
6725
|
+
const unsubscribeSettings = settingsForm.subscribe(syncEnabled);
|
|
6726
|
+
ctx.effect(() => () => {
|
|
6727
|
+
unsubscribeSettings();
|
|
6728
|
+
uiDisposer?.();
|
|
6729
|
+
}, "task-board: DOM surfaces");
|
|
5894
6730
|
syncEnabled();
|
|
5895
6731
|
}
|
|
5896
6732
|
/**
|