@linxin666/dsh-client-ui-task-board 0.4.3 → 0.4.4
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 +17 -10
- package/README.zh.md +17 -10
- package/lib/client.js +1350 -552
- package/lib/client.js.map +1 -1
- package/lib/index.js +655 -134
- package/lib/types/client/board/TaskForm.d.ts +19 -1
- package/lib/types/client/board/preset-label.d.ts +22 -0
- package/lib/types/client/host-api.d.ts +24 -1
- package/lib/types/client/locales.d.ts +38 -2
- package/lib/types/client/schedule-zone.d.ts +47 -0
- package/lib/types/core/controller.d.ts +1 -0
- package/lib/types/core/degraded-reason.d.ts +46 -0
- package/lib/types/core/package-name.d.ts +9 -0
- package/lib/types/core/schedule.d.ts +95 -20
- package/lib/types/core/subtask.d.ts +30 -11
- package/lib/types/core/tasks.d.ts +26 -2
- package/lib/types/core/use-cases/task-create.d.ts +2 -1
- package/lib/types/core/use-cases/task-schedule.d.ts +8 -4
- package/lib/types/core/use-cases/task-update.d.ts +1 -1
- package/lib/types/host-ledger.d.ts +56 -4
- package/lib/types/host-runner.d.ts +76 -3
- package/lib/types/host-service.d.ts +26 -0
- package/lib/types/protocol.d.ts +9 -3
- package/package.json +25 -25
- package/src/client/board/LinkSubtaskModal.tsx +21 -11
- package/src/client/board/NewTaskModal.tsx +271 -150
- package/src/client/board/TaskCard.tsx +24 -5
- package/src/client/board/TaskDetail.tsx +83 -33
- package/src/client/board/TaskForm.tsx +48 -3
- package/src/client/board/preset-label.ts +44 -0
- package/src/client/board.module.css +114 -5
- package/src/client/host-api.ts +109 -6
- package/src/client/index.ts +5 -1
- package/src/client/locales.ts +82 -8
- package/src/client/schedule-zone.ts +135 -0
- package/src/core/controller.ts +7 -2
- package/src/core/degraded-reason.ts +81 -0
- package/src/core/package-name.ts +9 -0
- package/src/core/schedule.ts +211 -55
- package/src/core/store.ts +10 -1
- package/src/core/subtask.ts +77 -22
- package/src/core/tasks.ts +38 -4
- package/src/core/use-cases/task-create.ts +14 -2
- package/src/core/use-cases/task-schedule.ts +17 -6
- package/src/core/use-cases/task-update.ts +4 -1
- package/src/host/agent-tools.ts +40 -8
- package/src/host-ledger.ts +203 -23
- package/src/host-runner.ts +164 -14
- package/src/host-service.ts +94 -17
- package/src/protocol.ts +27 -8
- package/lib/types/host/run-guarded.d.ts +0 -39
- package/src/host/run-guarded.ts +0 -76
package/lib/client.js
CHANGED
|
@@ -159,6 +159,7 @@ window.__ModuleLoader__.load({
|
|
|
159
159
|
model: normalizeTargetId(input.model),
|
|
160
160
|
reuseSession: input.reuseSession === true ? true : void 0,
|
|
161
161
|
teamRun: input.teamRun === true ? true : void 0,
|
|
162
|
+
goalRun: input.goalRun === false ? false : void 0,
|
|
162
163
|
...input.freeze === void 0 ? {} : { freeze: freezeOf(input.freeze, now) },
|
|
163
164
|
...input.handover === void 0 ? {} : { handover: {
|
|
164
165
|
...input.handover,
|
|
@@ -186,11 +187,14 @@ window.__ModuleLoader__.load({
|
|
|
186
187
|
const schedule = {
|
|
187
188
|
enabled: current?.enabled ?? false,
|
|
188
189
|
cron: current?.cron ?? "",
|
|
190
|
+
...current?.timeZone === void 0 ? {} : { timeZone: current.timeZone },
|
|
189
191
|
nextRunAt: current?.nextRunAt,
|
|
190
192
|
lastTriggeredAt: current?.lastTriggeredAt
|
|
191
193
|
};
|
|
192
194
|
if ("enabled" in patch) schedule.enabled = patch.enabled ?? false;
|
|
193
195
|
if ("cron" in patch) schedule.cron = patch.cron ?? "";
|
|
196
|
+
if ("timeZone" in patch) if (patch.timeZone === void 0) delete schedule.timeZone;
|
|
197
|
+
else schedule.timeZone = patch.timeZone;
|
|
194
198
|
if ("nextRunAt" in patch) schedule.nextRunAt = patch.nextRunAt;
|
|
195
199
|
if ("lastTriggeredAt" in patch) schedule.lastTriggeredAt = patch.lastTriggeredAt;
|
|
196
200
|
return {
|
|
@@ -222,6 +226,22 @@ window.__ModuleLoader__.load({
|
|
|
222
226
|
function isExecutionOutcome(value) {
|
|
223
227
|
return value === "succeeded" || value === "failed" || value === "cancelled";
|
|
224
228
|
}
|
|
229
|
+
/** Build the reusable index for a task list (ledger order preserved). */
|
|
230
|
+
function buildLineageIndex(tasks) {
|
|
231
|
+
const byId = /* @__PURE__ */ new Map();
|
|
232
|
+
const childrenByParent = /* @__PURE__ */ new Map();
|
|
233
|
+
for (const task of tasks) {
|
|
234
|
+
byId.set(task.id, task);
|
|
235
|
+
if (task.parentId === void 0) continue;
|
|
236
|
+
const siblings = childrenByParent.get(task.parentId);
|
|
237
|
+
if (siblings === void 0) childrenByParent.set(task.parentId, [task]);
|
|
238
|
+
else siblings.push(task);
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
byId,
|
|
242
|
+
childrenByParent
|
|
243
|
+
};
|
|
244
|
+
}
|
|
225
245
|
function indexOf(tasks) {
|
|
226
246
|
return new Map(tasks.map((task) => [task.id, task]));
|
|
227
247
|
}
|
|
@@ -231,8 +251,8 @@ window.__ModuleLoader__.load({
|
|
|
231
251
|
* under a larger limit survives that limit being lowered, and restore and
|
|
232
252
|
* inheritance must still see all of it.
|
|
233
253
|
*/
|
|
234
|
-
function ancestorChain(tasks, task) {
|
|
235
|
-
const index = indexOf(tasks);
|
|
254
|
+
function ancestorChain(tasks, task, lineage) {
|
|
255
|
+
const index = lineage?.byId ?? indexOf(tasks);
|
|
236
256
|
const chain = [];
|
|
237
257
|
const seen = /* @__PURE__ */ new Set([task.id]);
|
|
238
258
|
let current = task.parentId === void 0 ? void 0 : index.get(task.parentId);
|
|
@@ -245,29 +265,33 @@ window.__ModuleLoader__.load({
|
|
|
245
265
|
return chain;
|
|
246
266
|
}
|
|
247
267
|
/** Root-to-task depth: a root task is 0, its subtask 1, and so on. */
|
|
248
|
-
function taskDepth(tasks, id) {
|
|
249
|
-
const task = indexOf(tasks).get(id);
|
|
250
|
-
return task === void 0 ? 0 : ancestorChain(tasks, task).length;
|
|
268
|
+
function taskDepth(tasks, id, lineage) {
|
|
269
|
+
const task = (lineage?.byId ?? indexOf(tasks)).get(id);
|
|
270
|
+
return task === void 0 ? 0 : ancestorChain(tasks, task, lineage).length;
|
|
251
271
|
}
|
|
252
272
|
/** Direct subtasks of a task, in ledger order. */
|
|
253
|
-
function directSubtasks(tasks, id) {
|
|
273
|
+
function directSubtasks(tasks, id, lineage) {
|
|
274
|
+
if (lineage !== void 0) return [...lineage.childrenByParent.get(id) ?? []];
|
|
254
275
|
return tasks.filter((task) => task.parentId === id);
|
|
255
276
|
}
|
|
256
277
|
/**
|
|
257
278
|
* Descendants of a task in breadth-first order, bounded by maxSubtaskDepth
|
|
258
279
|
* total depth. The visited set keeps a malformed ledger from looping.
|
|
259
280
|
*/
|
|
260
|
-
function descendantTasks(tasks, id, maxSubtaskDepth) {
|
|
281
|
+
function descendantTasks(tasks, id, maxSubtaskDepth, lineage) {
|
|
261
282
|
const visited = /* @__PURE__ */ new Set([id]);
|
|
262
283
|
const found = [];
|
|
263
284
|
let frontier = [id];
|
|
264
285
|
for (let depth = 1; depth <= maxSubtaskDepth && frontier.length > 0; depth += 1) {
|
|
265
286
|
const next = [];
|
|
266
|
-
for (const parentId of frontier)
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
287
|
+
for (const parentId of frontier) {
|
|
288
|
+
const children = lineage === void 0 ? tasks.filter((task) => task.parentId === parentId) : lineage.childrenByParent.get(parentId) ?? [];
|
|
289
|
+
for (const task of children) {
|
|
290
|
+
if (visited.has(task.id)) continue;
|
|
291
|
+
visited.add(task.id);
|
|
292
|
+
found.push(task);
|
|
293
|
+
next.push(task.id);
|
|
294
|
+
}
|
|
271
295
|
}
|
|
272
296
|
frontier = next;
|
|
273
297
|
}
|
|
@@ -278,13 +302,13 @@ window.__ModuleLoader__.load({
|
|
|
278
302
|
* The visited set makes the walk terminate on a cyclic hand-edited ledger
|
|
279
303
|
* instead of recursing until the stack overflows.
|
|
280
304
|
*/
|
|
281
|
-
function subtreeHeight(tasks, id, visited = /* @__PURE__ */ new Set()) {
|
|
305
|
+
function subtreeHeight(tasks, id, visited = /* @__PURE__ */ new Set(), lineage) {
|
|
282
306
|
if (visited.has(id)) return 0;
|
|
283
307
|
const seen = /* @__PURE__ */ new Set([...visited, id]);
|
|
284
|
-
const children = directSubtasks(tasks, id);
|
|
308
|
+
const children = directSubtasks(tasks, id, lineage);
|
|
285
309
|
if (children.length === 0) return 0;
|
|
286
310
|
let height = 0;
|
|
287
|
-
for (const child of children) height = Math.max(height, 1 + subtreeHeight(tasks, child.id, seen));
|
|
311
|
+
for (const child of children) height = Math.max(height, 1 + subtreeHeight(tasks, child.id, seen, lineage));
|
|
288
312
|
return Math.min(height, 3);
|
|
289
313
|
}
|
|
290
314
|
/**
|
|
@@ -294,8 +318,8 @@ window.__ModuleLoader__.load({
|
|
|
294
318
|
* and the resulting depth (parent depth + 1 + the child own subtree height)
|
|
295
319
|
* must stay within maxSubtaskDepth.
|
|
296
320
|
*/
|
|
297
|
-
function checkParentLink(tasks, childId, parentId, maxSubtaskDepth) {
|
|
298
|
-
if (tasks.find((task) => task.id === childId) === void 0) return {
|
|
321
|
+
function checkParentLink(tasks, childId, parentId, maxSubtaskDepth, lineage) {
|
|
322
|
+
if ((lineage === void 0 ? tasks.find((task) => task.id === childId) : lineage.byId.get(childId)) === void 0) return {
|
|
299
323
|
ok: false,
|
|
300
324
|
reason: "unknown-task"
|
|
301
325
|
};
|
|
@@ -304,7 +328,7 @@ window.__ModuleLoader__.load({
|
|
|
304
328
|
ok: false,
|
|
305
329
|
reason: "self-parent"
|
|
306
330
|
};
|
|
307
|
-
const parent = tasks.find((task) => task.id === parentId);
|
|
331
|
+
const parent = lineage === void 0 ? tasks.find((task) => task.id === parentId) : lineage.byId.get(parentId);
|
|
308
332
|
if (parent === void 0) return {
|
|
309
333
|
ok: false,
|
|
310
334
|
reason: "unknown-parent"
|
|
@@ -313,11 +337,11 @@ window.__ModuleLoader__.load({
|
|
|
313
337
|
ok: false,
|
|
314
338
|
reason: "archived-parent"
|
|
315
339
|
};
|
|
316
|
-
if (descendantTasks(tasks, childId, 3).some((task) => task.id === parentId)) return {
|
|
340
|
+
if (descendantTasks(tasks, childId, 3, lineage).some((task) => task.id === parentId)) return {
|
|
317
341
|
ok: false,
|
|
318
342
|
reason: "cycle"
|
|
319
343
|
};
|
|
320
|
-
if (taskDepth(tasks, parentId) + 1 + subtreeHeight(tasks, childId) > maxSubtaskDepth) return {
|
|
344
|
+
if (taskDepth(tasks, parentId, lineage) + 1 + subtreeHeight(tasks, childId, /* @__PURE__ */ new Set(), lineage) > maxSubtaskDepth) return {
|
|
321
345
|
ok: false,
|
|
322
346
|
reason: "depth-exceeded"
|
|
323
347
|
};
|
|
@@ -424,6 +448,112 @@ window.__ModuleLoader__.load({
|
|
|
424
448
|
[1, 12],
|
|
425
449
|
[0, 7]
|
|
426
450
|
];
|
|
451
|
+
/** IANA `Area/Location`, plus the bare `UTC` the platform reports. */
|
|
452
|
+
const IANA_ZONE = /^[A-Za-z][A-Za-z0-9_+.-]*(?:\/[A-Za-z0-9_+.-]+)+$/;
|
|
453
|
+
/** Cached formatters: constructing an Intl formatter dominates the cost of a resolve. */
|
|
454
|
+
const FORMATTERS = /* @__PURE__ */ new Map();
|
|
455
|
+
function zoneFormatter(timeZone) {
|
|
456
|
+
let formatter = FORMATTERS.get(timeZone);
|
|
457
|
+
if (formatter === void 0) {
|
|
458
|
+
formatter = new Intl.DateTimeFormat("en-US", {
|
|
459
|
+
timeZone,
|
|
460
|
+
hourCycle: "h23",
|
|
461
|
+
year: "numeric",
|
|
462
|
+
month: "2-digit",
|
|
463
|
+
day: "2-digit",
|
|
464
|
+
hour: "2-digit",
|
|
465
|
+
minute: "2-digit"
|
|
466
|
+
});
|
|
467
|
+
FORMATTERS.set(timeZone, formatter);
|
|
468
|
+
}
|
|
469
|
+
return formatter;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* The wall-clock reading of one instant in an explicit zone. Reading through
|
|
473
|
+
* Intl (rather than the process `Date` accessors) is what makes the result
|
|
474
|
+
* independent of the Host's own TZ.
|
|
475
|
+
* @param timeZone - explicit IANA zone.
|
|
476
|
+
* @param epochMs - instant to read.
|
|
477
|
+
* @returns the zone-local calendar and clock fields.
|
|
478
|
+
*/
|
|
479
|
+
function partsInZone(timeZone, epochMs) {
|
|
480
|
+
const fields = {};
|
|
481
|
+
for (const part of zoneFormatter(timeZone).formatToParts(new Date(epochMs))) if (part.type !== "literal") fields[part.type] = part.value;
|
|
482
|
+
return {
|
|
483
|
+
year: Number(fields.year),
|
|
484
|
+
month: Number(fields.month),
|
|
485
|
+
day: Number(fields.day),
|
|
486
|
+
hour: Number(fields.hour),
|
|
487
|
+
minute: Number(fields.minute)
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
/** The zone's UTC offset in effect at one instant, in milliseconds. */
|
|
491
|
+
function offsetAt(timeZone, epochMs) {
|
|
492
|
+
const parts = partsInZone(timeZone, epochMs);
|
|
493
|
+
return Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute) - epochMs;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Resolve a wall clock in an explicit zone to its instant.
|
|
497
|
+
*
|
|
498
|
+
* The offset is probed a day either side of the naive guess so both DST
|
|
499
|
+
* transitions are covered, then every candidate offset whose inverse maps back
|
|
500
|
+
* to the requested wall clock is kept. No survivor means the wall clock does
|
|
501
|
+
* not exist (a spring-forward gap); several means it is ambiguous (a fall-back
|
|
502
|
+
* overlap), and the earliest instant wins so the repeated clock is taken once.
|
|
503
|
+
* @param timeZone - explicit IANA zone.
|
|
504
|
+
* @param year - full calendar year.
|
|
505
|
+
* @param month - 1-12.
|
|
506
|
+
* @param day - day of month.
|
|
507
|
+
* @param hour - 0-23.
|
|
508
|
+
* @param minute - 0-59.
|
|
509
|
+
* @returns the instant in ms epoch, or undefined when the wall clock is skipped.
|
|
510
|
+
*/
|
|
511
|
+
function zonedEpoch(timeZone, year, month, day, hour, minute) {
|
|
512
|
+
const naive = Date.UTC(year, month - 1, day, hour, minute);
|
|
513
|
+
const offsets = /* @__PURE__ */ new Set([
|
|
514
|
+
offsetAt(timeZone, naive - 864e5),
|
|
515
|
+
offsetAt(timeZone, naive),
|
|
516
|
+
offsetAt(timeZone, naive + 864e5)
|
|
517
|
+
]);
|
|
518
|
+
let earliest;
|
|
519
|
+
for (const offset of offsets) {
|
|
520
|
+
const candidate = naive - offset;
|
|
521
|
+
const parts = partsInZone(timeZone, candidate);
|
|
522
|
+
if (parts.year !== year || parts.month !== month || parts.day !== day) continue;
|
|
523
|
+
if (parts.hour !== hour || parts.minute !== minute) continue;
|
|
524
|
+
if (earliest === void 0 || candidate < earliest) earliest = candidate;
|
|
525
|
+
}
|
|
526
|
+
return earliest;
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* The Host process's IANA zone, used for schedules that store no zone of their
|
|
530
|
+
* own (a ledger written before zones were persisted).
|
|
531
|
+
* @returns a zone name Intl accepts, never empty.
|
|
532
|
+
*/
|
|
533
|
+
function resolveHostTimeZone() {
|
|
534
|
+
try {
|
|
535
|
+
const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
536
|
+
return typeof zone === "string" && zone !== "" ? zone : "UTC";
|
|
537
|
+
} catch {
|
|
538
|
+
return "UTC";
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Whether a value names a zone this runtime can actually resolve. A stored
|
|
543
|
+
* zone is trusted only after it round-trips through Intl, so a typo or a zone
|
|
544
|
+
* that this ICU build does not know can never arm a schedule.
|
|
545
|
+
* @param value - candidate zone name.
|
|
546
|
+
* @returns true when the name is usable.
|
|
547
|
+
*/
|
|
548
|
+
function isValidTimeZone(value) {
|
|
549
|
+
if (value.trim() !== value || value === "") return false;
|
|
550
|
+
if (value !== "UTC" && !IANA_ZONE.test(value)) return false;
|
|
551
|
+
try {
|
|
552
|
+
return new Intl.DateTimeFormat("en-US", { timeZone: value }).resolvedOptions().timeZone !== void 0;
|
|
553
|
+
} catch {
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
427
557
|
/**
|
|
428
558
|
* Parse a 5-field cron expression.
|
|
429
559
|
* @returns the match sets, or null when the expression is invalid.
|
|
@@ -446,8 +576,8 @@ window.__ModuleLoader__.load({
|
|
|
446
576
|
days: sets[2],
|
|
447
577
|
months: sets[3],
|
|
448
578
|
weekdays,
|
|
449
|
-
|
|
450
|
-
|
|
579
|
+
dayStarred: fields[2].startsWith("*"),
|
|
580
|
+
weekdayStarred: fields[4].startsWith("*")
|
|
451
581
|
};
|
|
452
582
|
}
|
|
453
583
|
/** Whether the expression parses. */
|
|
@@ -455,52 +585,57 @@ window.__ModuleLoader__.load({
|
|
|
455
585
|
return parseCron(expr) !== null;
|
|
456
586
|
}
|
|
457
587
|
/**
|
|
458
|
-
* Compute the next matching instant after `fromMs`
|
|
459
|
-
*
|
|
460
|
-
* of the matching minute's start, or undefined
|
|
461
|
-
* can never match (for example `0 0 30 2 *`).
|
|
462
|
-
* a full leap cycle, so a valid February 29
|
|
463
|
-
* every non-leap year.
|
|
588
|
+
* Compute the next matching instant after `fromMs` strictly greater than it,
|
|
589
|
+
* interpreting the expression's wall clock in `timeZone` (the Host zone when
|
|
590
|
+
* omitted). Returns the ms epoch of the matching minute's start, or undefined
|
|
591
|
+
* when the calendar constraint can never match (for example `0 0 30 2 *`).
|
|
592
|
+
* The five-year horizon includes a full leap cycle, so a valid February 29
|
|
593
|
+
* schedule remains reachable from every non-leap year.
|
|
464
594
|
*
|
|
465
595
|
* Walks candidate year/month/day/hour/minute values straight from the parsed
|
|
466
596
|
* field sets instead of scanning every minute: a sparse expression such as
|
|
467
597
|
* `0 0 29 2 *` used to iterate ~1.5M wall-clock minutes before reaching the
|
|
468
|
-
* next leap day.
|
|
469
|
-
*
|
|
470
|
-
*
|
|
598
|
+
* next leap day. A candidate wall clock that the zone skips (spring-forward
|
|
599
|
+
* gap) is passed over, which is what makes the gap semantics fall out of the
|
|
600
|
+
* candidate walk rather than needing a separate rule.
|
|
601
|
+
* @param expr - 5-field cron expression.
|
|
602
|
+
* @param fromMs - exclusive lower bound, ms epoch.
|
|
603
|
+
* @param timeZone - explicit IANA zone; defaults to the Host zone.
|
|
604
|
+
* @returns the next trigger instant, or undefined when unreachable.
|
|
471
605
|
*/
|
|
472
|
-
function nextRunAtMs(expr, fromMs) {
|
|
606
|
+
function nextRunAtMs(expr, fromMs, timeZone) {
|
|
473
607
|
const schedule = parseCron(expr);
|
|
474
608
|
if (schedule === null) return void 0;
|
|
475
609
|
if (!hasPossibleCalendarDay(schedule)) return void 0;
|
|
476
|
-
const
|
|
610
|
+
const zone = timeZone ?? resolveHostTimeZone();
|
|
611
|
+
const start = partsInZone(zone, fromMs);
|
|
477
612
|
const limitMs = fromMs + 5 * 366 * 24 * 60 * 60 * 1e3;
|
|
478
613
|
const sortedMinutes = [...schedule.minutes].sort((a, b) => a - b);
|
|
479
614
|
const sortedHours = [...schedule.hours].sort((a, b) => a - b);
|
|
480
615
|
const sortedMonths = [...schedule.months].sort((a, b) => a - b);
|
|
481
|
-
let year =
|
|
482
|
-
let month =
|
|
483
|
-
let day =
|
|
484
|
-
let hour =
|
|
485
|
-
let minute =
|
|
486
|
-
while (
|
|
616
|
+
let year = start.year;
|
|
617
|
+
let month = start.month;
|
|
618
|
+
let day = start.day;
|
|
619
|
+
let hour = start.hour;
|
|
620
|
+
let minute = start.minute + 1;
|
|
621
|
+
while (Date.UTC(year, month - 1, 1) <= limitMs + 864e5) {
|
|
487
622
|
for (const candidateMonth of sortedMonths) {
|
|
488
623
|
if (candidateMonth < month) continue;
|
|
489
|
-
const daysInMonth = new Date(year, candidateMonth, 0).
|
|
624
|
+
const daysInMonth = new Date(Date.UTC(year, candidateMonth, 0)).getUTCDate();
|
|
490
625
|
const dayStart = candidateMonth === month ? day : 1;
|
|
491
626
|
for (let candidateDay = dayStart; candidateDay <= daysInMonth; candidateDay += 1) {
|
|
492
|
-
if (!dayCandidate(schedule,
|
|
627
|
+
if (!dayCandidate(schedule, year, candidateMonth, candidateDay)) continue;
|
|
493
628
|
const hourStart = candidateMonth === month && candidateDay === day ? hour : 0;
|
|
494
629
|
for (const candidateHour of sortedHours) {
|
|
495
630
|
if (candidateHour < hourStart) continue;
|
|
496
631
|
const minuteStart = candidateMonth === month && candidateDay === day && candidateHour === hour ? minute : 0;
|
|
497
632
|
for (const candidateMinute of sortedMinutes) {
|
|
498
633
|
if (candidateMinute < minuteStart) continue;
|
|
499
|
-
const
|
|
500
|
-
|
|
634
|
+
const time = zonedEpoch(zone, year, candidateMonth, candidateDay, candidateHour, candidateMinute);
|
|
635
|
+
if (time === void 0) continue;
|
|
501
636
|
if (time <= fromMs) continue;
|
|
502
637
|
if (time > limitMs) return void 0;
|
|
503
|
-
|
|
638
|
+
return time;
|
|
504
639
|
}
|
|
505
640
|
}
|
|
506
641
|
}
|
|
@@ -512,17 +647,20 @@ window.__ModuleLoader__.load({
|
|
|
512
647
|
minute = 0;
|
|
513
648
|
}
|
|
514
649
|
}
|
|
515
|
-
/**
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
650
|
+
/**
|
|
651
|
+
* The Vixie day gate shared by the candidate walk. Both fields restricted is
|
|
652
|
+
* OR; any other combination is AND, because a starred field matches every
|
|
653
|
+
* value and so cannot widen the result.
|
|
654
|
+
*/
|
|
655
|
+
function dayCandidate(schedule, year, month, day) {
|
|
656
|
+
const dayMatches = schedule.days.has(day);
|
|
657
|
+
const weekdayMatches = schedule.weekdays.has(new Date(Date.UTC(year, month - 1, day)).getUTCDay());
|
|
658
|
+
if (!schedule.dayStarred && !schedule.weekdayStarred) return dayMatches || weekdayMatches;
|
|
659
|
+
return dayMatches && weekdayMatches;
|
|
522
660
|
}
|
|
523
661
|
/** Reject impossible month/day pairs without spending the multi-year scan. */
|
|
524
662
|
function hasPossibleCalendarDay(schedule) {
|
|
525
|
-
if (schedule.
|
|
663
|
+
if (schedule.dayStarred || !schedule.weekdayStarred) return true;
|
|
526
664
|
const maximumDay = /* @__PURE__ */ new Map([
|
|
527
665
|
[1, 31],
|
|
528
666
|
[2, 29],
|
|
@@ -573,13 +711,6 @@ window.__ModuleLoader__.load({
|
|
|
573
711
|
}
|
|
574
712
|
return true;
|
|
575
713
|
}
|
|
576
|
-
/** Day/weekday OR semantics: a restricted day field alone gates, and vice versa. */
|
|
577
|
-
function matches(schedule, date) {
|
|
578
|
-
if (!schedule.minutes.has(date.getMinutes())) return false;
|
|
579
|
-
if (!schedule.hours.has(date.getHours())) return false;
|
|
580
|
-
if (!schedule.months.has(date.getMonth() + 1)) return false;
|
|
581
|
-
return dayCandidate(schedule, date);
|
|
582
|
-
}
|
|
583
714
|
function isDigits(value) {
|
|
584
715
|
return /^\d+$/.test(value);
|
|
585
716
|
}
|
|
@@ -605,8 +736,9 @@ window.__ModuleLoader__.load({
|
|
|
605
736
|
* @param now - clock instant (ms epoch).
|
|
606
737
|
* @param id - minted task id.
|
|
607
738
|
* @param maxSubtaskDepth - deployment subtask depth limit.
|
|
739
|
+
* @param hostTimeZone - zone a rule that stores no zone of its own follows.
|
|
608
740
|
*/
|
|
609
|
-
function applyCreateTask(tasks, input, now, id, maxSubtaskDepth = 1) {
|
|
741
|
+
function applyCreateTask(tasks, input, now, id, maxSubtaskDepth = 1, hostTimeZone = resolveHostTimeZone()) {
|
|
610
742
|
if (input.title.trim() === "") return {
|
|
611
743
|
task: void 0,
|
|
612
744
|
tasks,
|
|
@@ -641,10 +773,18 @@ window.__ModuleLoader__.load({
|
|
|
641
773
|
const requested = input.schedule;
|
|
642
774
|
if (requested?.enabled === true && requested.cron.trim() !== "" && isValidCron(requested.cron)) {
|
|
643
775
|
const cron = requested.cron.trim();
|
|
644
|
-
|
|
776
|
+
const requestedZone = requested.timeZone;
|
|
777
|
+
if (requestedZone !== void 0 && !isValidTimeZone(requestedZone)) return {
|
|
778
|
+
task: void 0,
|
|
779
|
+
tasks,
|
|
780
|
+
error: "invalid schedule time zone"
|
|
781
|
+
};
|
|
782
|
+
const nextRunAt = nextRunAtMs(cron, now, requestedZone ?? hostTimeZone);
|
|
783
|
+
if (nextRunAt !== void 0) task = withSchedule(task, {
|
|
645
784
|
enabled: true,
|
|
646
785
|
cron,
|
|
647
|
-
|
|
786
|
+
timeZone: requestedZone,
|
|
787
|
+
nextRunAt
|
|
648
788
|
}, now);
|
|
649
789
|
}
|
|
650
790
|
return {
|
|
@@ -682,15 +822,17 @@ window.__ModuleLoader__.load({
|
|
|
682
822
|
* the core cron parser (schedule.ts) and the withSchedule transition.
|
|
683
823
|
*/
|
|
684
824
|
/**
|
|
685
|
-
* Set an on-board task's schedule rule. A blank or invalid cron, or
|
|
686
|
-
* archived task, is rejected (state untouched); an enabled
|
|
687
|
-
* next run instant immediately
|
|
825
|
+
* Set an on-board task's schedule rule. A blank or invalid cron, an unknown or
|
|
826
|
+
* archived task, or an unusable zone is rejected (state untouched); an enabled
|
|
827
|
+
* rule computes the next run instant immediately in the rule's own zone, a
|
|
828
|
+
* disabled one carries no next-run instant.
|
|
688
829
|
* @param tasks - current ledger.
|
|
689
830
|
* @param id - the task to schedule.
|
|
690
831
|
* @param patch - rule fields to change (absent fields keep their current value).
|
|
691
832
|
* @param now - clock instant (ms epoch).
|
|
833
|
+
* @param hostTimeZone - zone a rule with no stored zone follows.
|
|
692
834
|
*/
|
|
693
|
-
function applySetSchedule(tasks, id, patch, now) {
|
|
835
|
+
function applySetSchedule(tasks, id, patch, now, hostTimeZone) {
|
|
694
836
|
const task = tasks.find((candidate) => candidate.id === id);
|
|
695
837
|
if (task === void 0 || task.archivedAt !== void 0) return {
|
|
696
838
|
tasks,
|
|
@@ -702,8 +844,13 @@ window.__ModuleLoader__.load({
|
|
|
702
844
|
tasks,
|
|
703
845
|
applied: false
|
|
704
846
|
};
|
|
847
|
+
const requestedZone = patch.timeZone === void 0 ? current?.timeZone : patch.timeZone ?? void 0;
|
|
848
|
+
if (requestedZone !== void 0 && !isValidTimeZone(requestedZone)) return {
|
|
849
|
+
tasks,
|
|
850
|
+
applied: false
|
|
851
|
+
};
|
|
705
852
|
const enabled = patch.enabled ?? current?.enabled ?? false;
|
|
706
|
-
const nextRunAt = enabled ? nextRunAtMs(cron, now) : void 0;
|
|
853
|
+
const nextRunAt = enabled ? nextRunAtMs(cron, now, requestedZone ?? hostTimeZone) : void 0;
|
|
707
854
|
if (enabled && nextRunAt === void 0) return {
|
|
708
855
|
tasks,
|
|
709
856
|
applied: false
|
|
@@ -712,6 +859,7 @@ window.__ModuleLoader__.load({
|
|
|
712
859
|
tasks: tasks.map((candidate) => candidate.id === id ? withSchedule(candidate, {
|
|
713
860
|
enabled,
|
|
714
861
|
cron,
|
|
862
|
+
timeZone: requestedZone ?? void 0,
|
|
715
863
|
nextRunAt
|
|
716
864
|
}, now) : candidate),
|
|
717
865
|
applied: true
|
|
@@ -867,6 +1015,7 @@ window.__ModuleLoader__.load({
|
|
|
867
1015
|
if ("permission" in patch && patch.permission !== void 0 && patch.permission !== task.permission || "handover" in patch) next.permissionConfirmedAt = void 0;
|
|
868
1016
|
if ("reuseSession" in patch) next.reuseSession = patch.reuseSession === true ? true : void 0;
|
|
869
1017
|
if ("teamRun" in patch) next.teamRun = patch.teamRun === true ? true : void 0;
|
|
1018
|
+
if ("goalRun" in patch) next.goalRun = patch.goalRun === false ? false : void 0;
|
|
870
1019
|
if (workspaceId !== void 0 || "workspaceId" in patch) next.workspaceId = workspaceId;
|
|
871
1020
|
if (mode !== void 0 || "mode" in patch) next.mode = mode;
|
|
872
1021
|
if (permission !== void 0 || "permission" in patch) next.permission = permission;
|
|
@@ -1217,7 +1366,8 @@ window.__ModuleLoader__.load({
|
|
|
1217
1366
|
* @returns true when applied, false when rejected (invalid cron / unknown task).
|
|
1218
1367
|
*/
|
|
1219
1368
|
setSchedule(id, patch) {
|
|
1220
|
-
const
|
|
1369
|
+
const hostTimeZone = this.hostState?.scheduler.timeZone ?? resolveHostTimeZone();
|
|
1370
|
+
const { tasks, applied } = applySetSchedule(this.tasks, id, patch, this.now(), hostTimeZone);
|
|
1221
1371
|
if (!applied) return false;
|
|
1222
1372
|
if (this.deps.transport !== void 0) {
|
|
1223
1373
|
this.commitRemote({
|
|
@@ -1744,6 +1894,7 @@ window.__ModuleLoader__.load({
|
|
|
1744
1894
|
if (record.mode !== void 0 && typeof record.mode !== "string") return false;
|
|
1745
1895
|
if (record.permission !== void 0 && typeof record.permission !== "string") return false;
|
|
1746
1896
|
if (record.reuseSession !== void 0 && typeof record.reuseSession !== "boolean") return false;
|
|
1897
|
+
if (record.goalRun !== void 0 && typeof record.goalRun !== "boolean") return false;
|
|
1747
1898
|
if (!Array.isArray(record.executions)) return false;
|
|
1748
1899
|
for (const execution of record.executions) {
|
|
1749
1900
|
if (typeof execution !== "object" || execution === null) return false;
|
|
@@ -1774,9 +1925,11 @@ window.__ModuleLoader__.load({
|
|
|
1774
1925
|
const rule = schedule;
|
|
1775
1926
|
if (typeof rule.cron !== "string") return void 0;
|
|
1776
1927
|
if (rule.cron.trim() === "" || !isValidCron(rule.cron)) return void 0;
|
|
1928
|
+
const timeZone = typeof rule.timeZone === "string" && isValidTimeZone(rule.timeZone) ? rule.timeZone : void 0;
|
|
1777
1929
|
return {
|
|
1778
1930
|
enabled: rule.enabled === true,
|
|
1779
1931
|
cron: rule.cron,
|
|
1932
|
+
...timeZone === void 0 ? {} : { timeZone },
|
|
1780
1933
|
nextRunAt: typeof rule.nextRunAt === "number" ? rule.nextRunAt : void 0,
|
|
1781
1934
|
lastTriggeredAt: typeof rule.lastTriggeredAt === "number" ? rule.lastTriggeredAt : void 0
|
|
1782
1935
|
};
|
|
@@ -1861,6 +2014,7 @@ window.__ModuleLoader__.load({
|
|
|
1861
2014
|
task.archivedAt = typeof row.archivedAt === "number" && Number.isFinite(row.archivedAt) ? row.archivedAt : void 0;
|
|
1862
2015
|
task.permission = isTaskPermission(row.permission) ? row.permission : void 0;
|
|
1863
2016
|
task.reuseSession = row.reuseSession === true ? true : void 0;
|
|
2017
|
+
task.goalRun = row.goalRun === false ? false : void 0;
|
|
1864
2018
|
task.freeze = normalizeFreeze(row.freeze);
|
|
1865
2019
|
task.handover = normalizeHandover(row.handover);
|
|
1866
2020
|
task.tags = normalizeTags(row.tags);
|
|
@@ -1978,8 +2132,11 @@ window.__ModuleLoader__.load({
|
|
|
1978
2132
|
"board.updated": "更新于",
|
|
1979
2133
|
"board.created": "创建于",
|
|
1980
2134
|
"board.hostError": "Host 操作失败:{error}",
|
|
1981
|
-
"board.hostError.notMounted": "
|
|
2135
|
+
"board.hostError.notMounted": "任务看板的后台接口没有挂载:宿主没有加载这个插件(可能未安装、未启用,或该行启动失败)。重启 DSH 服务后再试",
|
|
2136
|
+
"board.hostError.ledgerLocked": "任务看板的账本已被另一个 DSH 进程占用(占用者 pid {pid}),本实例没有注册后台接口:关闭那个 DSH 实例,或等它退出后再点重试——反复重启本实例无效(宿主原因:{detail})",
|
|
2137
|
+
"board.hostError.degraded": "任务看板这一行在启动时降级,后台接口没有注册(宿主原因:{detail})",
|
|
1982
2138
|
"board.hostError.unauthorized": "登录状态已失效,请刷新页面后重试",
|
|
2139
|
+
"board.hostError.forbidden": "当前环境未通过同源校验:桌面外壳或反向代理可能未传递浏览器标记,也没有携带宿主认证凭据",
|
|
1983
2140
|
"board.hostError.locked": "任务看板的后台数据被占用:{detail}",
|
|
1984
2141
|
"board.hostError.timeout": "后台 {seconds} 秒内没有响应,请稍后重试",
|
|
1985
2142
|
"board.hostError.unreachable": "连接不上本机 DSH 服务,请确认它仍在运行",
|
|
@@ -2004,6 +2161,19 @@ window.__ModuleLoader__.load({
|
|
|
2004
2161
|
"new.aiParseFailed": "解析失败:{error}",
|
|
2005
2162
|
"board.retryHost": "重试连接 Host",
|
|
2006
2163
|
"board.hostMeta": "Host 时区 {timeZone} · revision {revision}",
|
|
2164
|
+
"new.section.parse": "AI 解析",
|
|
2165
|
+
"new.section.content": "任务内容",
|
|
2166
|
+
"new.section.labels": "标签",
|
|
2167
|
+
"new.section.execution": "执行设置",
|
|
2168
|
+
"new.section.run": "运行方式",
|
|
2169
|
+
"new.section.handover": "交接与续跑",
|
|
2170
|
+
"new.section.schedule": "定时运行",
|
|
2171
|
+
"new.summary.none": "未设置",
|
|
2172
|
+
"new.summary.labelCount": "{count} 个标签",
|
|
2173
|
+
"new.summary.filled": "已填写",
|
|
2174
|
+
"new.summary.scheduleOff": "未启用",
|
|
2175
|
+
"new.summary.multiRound": "多轮 /goal",
|
|
2176
|
+
"new.summary.singleRound": "单轮",
|
|
2007
2177
|
"new.title": "标题",
|
|
2008
2178
|
"new.titlePlaceholder": "一句话描述要做什么",
|
|
2009
2179
|
"new.description": "描述",
|
|
@@ -2095,13 +2265,31 @@ window.__ModuleLoader__.load({
|
|
|
2095
2265
|
"detail.schedule.invalid": "Cron 表达式无效",
|
|
2096
2266
|
"detail.schedule.notScheduled": "尚未排程",
|
|
2097
2267
|
"detail.schedule.dueSoon": "即将运行",
|
|
2268
|
+
"detail.schedule.timeZone": "时区",
|
|
2269
|
+
"detail.schedule.timeZoneHost": "Host 时区({timeZone})",
|
|
2270
|
+
"detail.schedule.timeZoneHostUnknown": "Host 时区(未知)",
|
|
2271
|
+
"detail.schedule.timeZoneHint": "Cron 的墙上时间按此时区解读;夏令时跳变会跳过不存在的时刻,重复的时刻只触发一次。",
|
|
2272
|
+
"detail.schedule.timeZoneSearch": "搜索时区…",
|
|
2273
|
+
"detail.schedule.timeZoneNone": "没有匹配的时区",
|
|
2274
|
+
"detail.schedule.nextRunOverdue": "已逾期 {duration}",
|
|
2275
|
+
"detail.schedule.duration.days": "{count} 天",
|
|
2276
|
+
"detail.schedule.duration.hours": "{count} 小时",
|
|
2277
|
+
"detail.schedule.duration.minutes": "{count} 分钟",
|
|
2278
|
+
"detail.schedule.duration.seconds": "{count} 秒",
|
|
2098
2279
|
"card.scheduled": "定时",
|
|
2099
2280
|
"new.workspace": "工作区",
|
|
2100
|
-
"new.
|
|
2281
|
+
"new.agentPreset": "Agent 预设",
|
|
2101
2282
|
"new.permission": "权限",
|
|
2102
2283
|
"new.model": "模型",
|
|
2103
2284
|
"exec.workspace.recent": "最近使用(默认)",
|
|
2104
|
-
"exec.mode.
|
|
2285
|
+
"exec.mode.inherit": "继承(部署默认)",
|
|
2286
|
+
"exec.mode.inheritWith": "继承(部署默认:{preset})",
|
|
2287
|
+
"exec.mode.builtinGroup": "内置预设",
|
|
2288
|
+
"exec.mode.customGroup": "自定义预设",
|
|
2289
|
+
"preset.builtin.standard": "标准",
|
|
2290
|
+
"preset.builtin.ptc": "PTC(程序化工具调用)",
|
|
2291
|
+
"preset.builtin.minimal": "极简",
|
|
2292
|
+
"preset.builtin.cordis": "Cordis",
|
|
2105
2293
|
"exec.mode.defaultSuffix": "(默认)",
|
|
2106
2294
|
"exec.mode.brokenSuffix": "(不可用)",
|
|
2107
2295
|
"exec.mode.removed": "(已移除)",
|
|
@@ -2117,8 +2305,10 @@ window.__ModuleLoader__.load({
|
|
|
2117
2305
|
"exec.teamRunHint": "开启后:执行本任务只启动一个 Leader 会话,每个子任务由它派生为 teammate 并在该会话内并行工作(子任务自身的权限钉住不适用)。关闭则每个成员各开独立会话。",
|
|
2118
2306
|
"exec.teamRunUnavailable": "当前部署未提供 Agent Teams 服务,无法启用。",
|
|
2119
2307
|
"exec.reuseSessionHint": "开启后,本任务的后续执行在上一次会话里继续(该会话空闲且仍存在时),不再每次新建对话;每次复用时都会重新应用上面钉住的权限与模型。",
|
|
2308
|
+
"exec.goalRun": "以 dsh 内置的 /goal 开始执行任务",
|
|
2309
|
+
"exec.goalRunHint": "默认开启:执行时把任务目标作为持久目标交给 dsh 内置的 /goal,会话会自动续跑多轮,直到 agent 标记目标完成;看板在该目标真正结束后才结算本次执行。关闭则只执行一轮普通对话。",
|
|
2120
2310
|
"detail.executionSettings": "执行设置",
|
|
2121
|
-
"exec.hint": "
|
|
2311
|
+
"exec.hint": "执行时生效:工作区决定执行会话落在哪个工作区;Agent 预设决定会话的组合(工具面与提示词段);权限经 /permission 命令应用到会话。留空则继承部署默认。",
|
|
2122
2312
|
"settings.title": "任务看板",
|
|
2123
2313
|
"settings.description": "控制 Host 任务看板、agent 播报与运行期间的系统空闲睡眠保护。",
|
|
2124
2314
|
"settings.enabled": "启用任务看板",
|
|
@@ -2200,8 +2390,11 @@ window.__ModuleLoader__.load({
|
|
|
2200
2390
|
"board.updated": "Updated",
|
|
2201
2391
|
"board.created": "Created",
|
|
2202
2392
|
"board.hostError": "Host action failed: {error}",
|
|
2203
|
-
"board.hostError.notMounted": "The task board Host API is not mounted: the Host
|
|
2393
|
+
"board.hostError.notMounted": "The task board Host API is not mounted: the Host did not load this plugin (not installed, disabled, or its row failed to start). Restart the DSH service and try again",
|
|
2394
|
+
"board.hostError.ledgerLocked": "Another DSH process (pid {pid}) holds the task board ledger, so this instance registered no Host API: close that DSH instance, or wait for it to exit and retry. Restarting this instance cannot help (reason: {detail})",
|
|
2395
|
+
"board.hostError.degraded": "The task board row degraded during start, so its Host API was not registered: {detail}",
|
|
2204
2396
|
"board.hostError.unauthorized": "This session is no longer signed in; reload the page and try again",
|
|
2397
|
+
"board.hostError.forbidden": "The request did not pass the same-origin fence: the desktop shell or reverse proxy may not have forwarded browser credentials",
|
|
2205
2398
|
"board.hostError.locked": "The task board ledger is locked: {detail}",
|
|
2206
2399
|
"board.hostError.timeout": "The Host did not answer within {seconds}s; try again shortly",
|
|
2207
2400
|
"board.hostError.unreachable": "Cannot reach the local DSH service; make sure it is still running",
|
|
@@ -2226,6 +2419,19 @@ window.__ModuleLoader__.load({
|
|
|
2226
2419
|
"new.aiParseFailed": "Parsing failed: {error}",
|
|
2227
2420
|
"board.retryHost": "Retry Host connection",
|
|
2228
2421
|
"board.hostMeta": "Host time zone {timeZone} · revision {revision}",
|
|
2422
|
+
"new.section.parse": "AI Parse",
|
|
2423
|
+
"new.section.content": "Task Content",
|
|
2424
|
+
"new.section.labels": "Tags",
|
|
2425
|
+
"new.section.execution": "Execution Settings",
|
|
2426
|
+
"new.section.run": "Run Mode",
|
|
2427
|
+
"new.section.handover": "Handover",
|
|
2428
|
+
"new.section.schedule": "Scheduled Runs",
|
|
2429
|
+
"new.summary.none": "Not set",
|
|
2430
|
+
"new.summary.labelCount": "{count} tags",
|
|
2431
|
+
"new.summary.filled": "Filled",
|
|
2432
|
+
"new.summary.scheduleOff": "Off",
|
|
2433
|
+
"new.summary.multiRound": "multi-round /goal",
|
|
2434
|
+
"new.summary.singleRound": "single round",
|
|
2229
2435
|
"new.title": "Title",
|
|
2230
2436
|
"new.titlePlaceholder": "What should be done, in one line",
|
|
2231
2437
|
"new.description": "Description",
|
|
@@ -2317,13 +2523,31 @@ window.__ModuleLoader__.load({
|
|
|
2317
2523
|
"detail.schedule.invalid": "Invalid cron expression",
|
|
2318
2524
|
"detail.schedule.notScheduled": "Not scheduled yet",
|
|
2319
2525
|
"detail.schedule.dueSoon": "Due soon",
|
|
2526
|
+
"detail.schedule.timeZone": "Time zone",
|
|
2527
|
+
"detail.schedule.timeZoneHost": "Host time zone ({timeZone})",
|
|
2528
|
+
"detail.schedule.timeZoneHostUnknown": "Host time zone (unknown)",
|
|
2529
|
+
"detail.schedule.timeZoneHint": "The cron wall clock is read in this zone; daylight-saving gaps are skipped and a repeated time fires once.",
|
|
2530
|
+
"detail.schedule.timeZoneSearch": "Search time zones…",
|
|
2531
|
+
"detail.schedule.timeZoneNone": "No matching time zone",
|
|
2532
|
+
"detail.schedule.nextRunOverdue": "Overdue by {duration}",
|
|
2533
|
+
"detail.schedule.duration.days": "{count}d",
|
|
2534
|
+
"detail.schedule.duration.hours": "{count}h",
|
|
2535
|
+
"detail.schedule.duration.minutes": "{count}m",
|
|
2536
|
+
"detail.schedule.duration.seconds": "{count}s",
|
|
2320
2537
|
"card.scheduled": "scheduled",
|
|
2321
2538
|
"new.workspace": "Workspace",
|
|
2322
|
-
"new.
|
|
2539
|
+
"new.agentPreset": "Agent preset",
|
|
2323
2540
|
"new.permission": "Permission",
|
|
2324
2541
|
"new.model": "Model",
|
|
2325
2542
|
"exec.workspace.recent": "Most recent (default)",
|
|
2326
|
-
"exec.mode.
|
|
2543
|
+
"exec.mode.inherit": "Inherit (deployment default)",
|
|
2544
|
+
"exec.mode.inheritWith": "Inherit (deployment default: {preset})",
|
|
2545
|
+
"exec.mode.builtinGroup": "Built-in presets",
|
|
2546
|
+
"exec.mode.customGroup": "Custom presets",
|
|
2547
|
+
"preset.builtin.standard": "Standard",
|
|
2548
|
+
"preset.builtin.ptc": "PTC (programmatic tool calling)",
|
|
2549
|
+
"preset.builtin.minimal": "Minimal",
|
|
2550
|
+
"preset.builtin.cordis": "Cordis",
|
|
2327
2551
|
"exec.mode.defaultSuffix": " (default)",
|
|
2328
2552
|
"exec.mode.brokenSuffix": " (unavailable)",
|
|
2329
2553
|
"exec.mode.removed": " (removed)",
|
|
@@ -2339,8 +2563,10 @@ window.__ModuleLoader__.load({
|
|
|
2339
2563
|
"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.",
|
|
2340
2564
|
"exec.teamRunUnavailable": "This deployment serves no Agent Teams service, so team runs cannot be enabled.",
|
|
2341
2565
|
"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.",
|
|
2566
|
+
"exec.goalRun": "Start the run with dsh's built-in /goal",
|
|
2567
|
+
"exec.goalRunHint": "On by default: the run arms dsh built-in /goal with the task objective, so the session keeps working automatic continuation rounds until the agent marks the goal complete, and the board settles the execution only when that goal really ends. Off: one plain turn.",
|
|
2342
2568
|
"detail.executionSettings": "Execution Settings",
|
|
2343
|
-
"exec.hint": "Applied when the task runs: the workspace decides where the execution session lands; the
|
|
2569
|
+
"exec.hint": "Applied when the task runs: the workspace decides where the execution session lands; the agent preset composes the session (tool face and prompt sections); the permission is applied through the /permission command. Blank inherits the deployment default.",
|
|
2344
2570
|
"settings.title": "Task Board",
|
|
2345
2571
|
"settings.description": "Configure the Host task board, agent announcement, and idle-system-sleep protection while work is pending.",
|
|
2346
2572
|
"settings.enabled": "Enable the task board",
|
|
@@ -2423,7 +2649,7 @@ window.__ModuleLoader__.load({
|
|
|
2423
2649
|
}
|
|
2424
2650
|
//#endregion
|
|
2425
2651
|
//#region \0dsh-css:packages/dsh-task-board/src/client/board.module.css.mjs
|
|
2426
|
-
const css$1 = "[data-dsh-taskboard-view]{box-sizing:border-box;background:var(--dsw-alias-bg-base);flex-direction:column;width:100%;min-width:0;height:100%;min-height:0;display:flex;container:_7D6uKa_task-board-view/inline-size}._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_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_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_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_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_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_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}";
|
|
2652
|
+
const css$1 = "[data-dsh-taskboard-view]{box-sizing:border-box;background:var(--dsw-alias-bg-base);flex-direction:column;width:100%;min-width:0;height:100%;min-height:0;display:flex;container:_7D6uKa_task-board-view/inline-size}._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:10px;padding:18px;display:flex;overflow:hidden}._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_modalBody{flex-direction:column;gap:8px;min-height:0;display:flex;overflow-y:auto}._7D6uKa_modalBody>*{flex:none}._7D6uKa_formSection{border:1px solid var(--dsw-alias-separator-primary);border-radius:10px;flex-direction:column;display:flex;overflow:hidden}._7D6uKa_formSectionHeader{width:100%;color:var(--dsw-alias-label-primary);text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;font-family:inherit;font-size:12.5px;font-weight:600;display:flex}._7D6uKa_formSectionHeader:hover{background:var(--dsw-alias-bg-mask-1)}._7D6uKa_formSectionChevron{border-top:4px solid #0000;border-bottom:4px solid #0000;border-left:5px solid var(--dsw-alias-label-tertiary);flex:none;width:0;height:0;margin-left:1px;transition:transform .12s}._7D6uKa_formSection[data-open=true] ._7D6uKa_formSectionChevron{transform:rotate(90deg)}._7D6uKa_formSectionTitle{flex:none}._7D6uKa_formSectionSummary{min-width:0;color:var(--dsw-alias-label-tertiary);text-align:right;text-overflow:ellipsis;white-space:nowrap;flex:auto;font-weight:400;overflow:hidden}._7D6uKa_formSectionBody{border-top:1px solid var(--dsw-alias-separator-primary);flex-direction:column;gap:8px;padding:8px 10px 10px;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_scheduleZone{color:var(--dsw-alias-label-secondary);align-items:center;gap:8px;font-size:12.5px;display:flex}._7D6uKa_scheduleZone select{flex:1;min-width:0}._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_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_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,._7D6uKa_formSectionHeader:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}._7D6uKa_primaryButton,._7D6uKa_ghostButton,._7D6uKa_dangerButton,._7D6uKa_iconButton,._7D6uKa_linkButton,._7D6uKa_search,._7D6uKa_input,._7D6uKa_select,._7D6uKa_schedulePreset,._7D6uKa_scheduleToggle input,._7D6uKa_formSectionHeader,._7D6uKa_formSectionChevron{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_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_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_card,._7D6uKa_primaryButton,._7D6uKa_ghostButton,._7D6uKa_dangerButton,._7D6uKa_iconButton,._7D6uKa_linkButton,._7D6uKa_search,._7D6uKa_input,._7D6uKa_select,._7D6uKa_schedulePreset,._7D6uKa_scheduleToggle input,._7D6uKa_formSectionHeader,._7D6uKa_formSectionChevron{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}";
|
|
2427
2653
|
const tagId$1 = "@linxin666/dsh-client-ui-task-board/packages/dsh-task-board/src/client/board.module.css";
|
|
2428
2654
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
2429
2655
|
const tag = document.createElement("style");
|
|
@@ -2479,12 +2705,19 @@ window.__ModuleLoader__.load({
|
|
|
2479
2705
|
"fieldHint": "_7D6uKa_fieldHint",
|
|
2480
2706
|
"fieldLabel": "_7D6uKa_fieldLabel",
|
|
2481
2707
|
"formError": "_7D6uKa_formError",
|
|
2708
|
+
"formSection": "_7D6uKa_formSection",
|
|
2709
|
+
"formSectionBody": "_7D6uKa_formSectionBody",
|
|
2710
|
+
"formSectionChevron": "_7D6uKa_formSectionChevron",
|
|
2711
|
+
"formSectionHeader": "_7D6uKa_formSectionHeader",
|
|
2712
|
+
"formSectionSummary": "_7D6uKa_formSectionSummary",
|
|
2713
|
+
"formSectionTitle": "_7D6uKa_formSectionTitle",
|
|
2482
2714
|
"ghostButton": "_7D6uKa_ghostButton",
|
|
2483
2715
|
"iconButton": "_7D6uKa_iconButton",
|
|
2484
2716
|
"input": "_7D6uKa_input",
|
|
2485
2717
|
"linkButton": "_7D6uKa_linkButton",
|
|
2486
2718
|
"modal": "_7D6uKa_modal",
|
|
2487
2719
|
"modalBackdrop": "_7D6uKa_modalBackdrop",
|
|
2720
|
+
"modalBody": "_7D6uKa_modalBody",
|
|
2488
2721
|
"modalFooter": "_7D6uKa_modalFooter",
|
|
2489
2722
|
"modalTitle": "_7D6uKa_modalTitle",
|
|
2490
2723
|
"moveRow": "_7D6uKa_moveRow",
|
|
@@ -2503,6 +2736,7 @@ window.__ModuleLoader__.load({
|
|
|
2503
2736
|
"schedulePreset": "_7D6uKa_schedulePreset",
|
|
2504
2737
|
"scheduleRow": "_7D6uKa_scheduleRow",
|
|
2505
2738
|
"scheduleToggle": "_7D6uKa_scheduleToggle",
|
|
2739
|
+
"scheduleZone": "_7D6uKa_scheduleZone",
|
|
2506
2740
|
"search": "_7D6uKa_search",
|
|
2507
2741
|
"select": "_7D6uKa_select",
|
|
2508
2742
|
"statusBadge": "_7D6uKa_statusBadge",
|
|
@@ -2539,7 +2773,128 @@ window.__ModuleLoader__.load({
|
|
|
2539
2773
|
}
|
|
2540
2774
|
];
|
|
2541
2775
|
//#endregion
|
|
2776
|
+
//#region src/client/schedule-zone.ts
|
|
2777
|
+
/**
|
|
2778
|
+
* Shared scheduling helpers for the board's schedule editors (the new-task
|
|
2779
|
+
* dialog and the task detail panel): the time-zone picker inventory, the
|
|
2780
|
+
* relative next-run wording, and the combined absolute + relative label.
|
|
2781
|
+
*
|
|
2782
|
+
* Everything here is browser-side presentation built on the same pure engine
|
|
2783
|
+
* the Host schedules with (`core/schedule.ts`), so the preview a user sees is
|
|
2784
|
+
* computed by the code that will actually arm the rule.
|
|
2785
|
+
*/
|
|
2786
|
+
/**
|
|
2787
|
+
* The runtime's zone inventory, read once. `Intl.supportedValuesOf` is not
|
|
2788
|
+
* available in every engine, so a missing inventory degrades to `UTC` rather
|
|
2789
|
+
* than offering a list that cannot resolve.
|
|
2790
|
+
*
|
|
2791
|
+
* The inventory is deliberately NOT truncated. A capped list silently omits
|
|
2792
|
+
* whole regions, and because a `<select>` whose value matches no option falls
|
|
2793
|
+
* back to its first option, an omitted zone would make the control show — and
|
|
2794
|
+
* then save — a different zone than the rule actually stores.
|
|
2795
|
+
*/
|
|
2796
|
+
let inventoryCache;
|
|
2797
|
+
function zoneInventory() {
|
|
2798
|
+
if (inventoryCache !== void 0) return inventoryCache;
|
|
2799
|
+
let inventory = [];
|
|
2800
|
+
try {
|
|
2801
|
+
inventory = typeof Intl.supportedValuesOf === "function" ? Intl.supportedValuesOf("timeZone") : [];
|
|
2802
|
+
} catch {
|
|
2803
|
+
inventory = [];
|
|
2804
|
+
}
|
|
2805
|
+
inventoryCache = inventory.length > 0 ? inventory : ["UTC"];
|
|
2806
|
+
return inventoryCache;
|
|
2807
|
+
}
|
|
2808
|
+
/**
|
|
2809
|
+
* Zones offered by the picker: the Host zone first (the default, and the entry
|
|
2810
|
+
* a rule with no stored zone follows), then the runtime's own inventory.
|
|
2811
|
+
*
|
|
2812
|
+
* The Host entry is always present, even before the Host snapshot reports a
|
|
2813
|
+
* zone: it is the entry that stores no zone, so dropping it would leave a rule
|
|
2814
|
+
* unable to express "follow the Host" and would let the control silently
|
|
2815
|
+
* display an unrelated zone.
|
|
2816
|
+
*
|
|
2817
|
+
* A stored zone the inventory does not list (a rule written on another machine
|
|
2818
|
+
* or under a different ICU build) is appended, so opening the editor can never
|
|
2819
|
+
* silently rewrite the rule's zone.
|
|
2820
|
+
* @param hostTimeZone - zone the Host reported for this deployment, when known.
|
|
2821
|
+
* @param storedTimeZone - zone the edited rule already stores, when any.
|
|
2822
|
+
* @returns the de-duplicated choice list.
|
|
2823
|
+
*/
|
|
2824
|
+
function zoneChoices(hostTimeZone, storedTimeZone) {
|
|
2825
|
+
const host = hostTimeZone !== void 0 && isValidTimeZone(hostTimeZone) ? hostTimeZone : void 0;
|
|
2826
|
+
const choices = [];
|
|
2827
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2828
|
+
choices.push({
|
|
2829
|
+
id: "",
|
|
2830
|
+
label: host === void 0 ? t("detail.schedule.timeZoneHostUnknown") : t("detail.schedule.timeZoneHost", { timeZone: host })
|
|
2831
|
+
});
|
|
2832
|
+
if (host !== void 0) seen.add(host);
|
|
2833
|
+
for (const zone of zoneInventory()) {
|
|
2834
|
+
if (seen.has(zone) || !isValidTimeZone(zone)) continue;
|
|
2835
|
+
seen.add(zone);
|
|
2836
|
+
choices.push({
|
|
2837
|
+
id: zone,
|
|
2838
|
+
label: zone
|
|
2839
|
+
});
|
|
2840
|
+
}
|
|
2841
|
+
if (storedTimeZone !== void 0 && !seen.has(storedTimeZone) && isValidTimeZone(storedTimeZone)) choices.push({
|
|
2842
|
+
id: storedTimeZone,
|
|
2843
|
+
label: storedTimeZone
|
|
2844
|
+
});
|
|
2845
|
+
return choices;
|
|
2846
|
+
}
|
|
2847
|
+
/** Compact duration parts, largest unit first, with at most two units kept. */
|
|
2848
|
+
function durationParts(ms) {
|
|
2849
|
+
const total = Math.max(0, Math.floor(ms / 1e3));
|
|
2850
|
+
const days = Math.floor(total / 86400);
|
|
2851
|
+
const hours = Math.floor(total % 86400 / 3600);
|
|
2852
|
+
const minutes = Math.floor(total % 3600 / 60);
|
|
2853
|
+
const seconds = total % 60;
|
|
2854
|
+
const parts = [];
|
|
2855
|
+
if (days > 0) parts.push(t("detail.schedule.duration.days", { count: String(days) }));
|
|
2856
|
+
if (hours > 0) parts.push(t("detail.schedule.duration.hours", { count: String(hours) }));
|
|
2857
|
+
if (minutes > 0) parts.push(t("detail.schedule.duration.minutes", { count: String(minutes) }));
|
|
2858
|
+
if (parts.length < 2 && (seconds > 0 || parts.length === 0)) parts.push(t("detail.schedule.duration.seconds", { count: String(seconds) }));
|
|
2859
|
+
return parts.slice(0, 2).join(" ");
|
|
2860
|
+
}
|
|
2861
|
+
/**
|
|
2862
|
+
* The relative half of a next-run label: how far away the instant is, or how
|
|
2863
|
+
* far past it the board is running. `now` is passed in rather than sampled so
|
|
2864
|
+
* callers can share one clock reading across a row, and tests stay
|
|
2865
|
+
* deterministic.
|
|
2866
|
+
* @param targetMs - the instant being described.
|
|
2867
|
+
* @param now - the reference instant.
|
|
2868
|
+
* @returns localized relative wording.
|
|
2869
|
+
*/
|
|
2870
|
+
function relativeTimeLabel(targetMs, now) {
|
|
2871
|
+
const delta = targetMs - now;
|
|
2872
|
+
if (delta < 0) return t("detail.schedule.nextRunOverdue", { duration: durationParts(-delta) });
|
|
2873
|
+
return `${durationParts(delta)}`;
|
|
2874
|
+
}
|
|
2875
|
+
/**
|
|
2876
|
+
* The full next-run label: the absolute wall clock in the schedule's own zone,
|
|
2877
|
+
* plus the relative distance in parentheses. Showing both matters because a
|
|
2878
|
+
* rule's zone can differ from the reader's, so the wall clock alone is
|
|
2879
|
+
* ambiguous and the distance alone loses the actual moment.
|
|
2880
|
+
* @param targetMs - the instant being described.
|
|
2881
|
+
* @param timeZone - zone to render the absolute half in.
|
|
2882
|
+
* @param formatAbsolute - formatter for the absolute half (keeps Intl caching in the caller).
|
|
2883
|
+
* @param now - the reference instant.
|
|
2884
|
+
* @param translate - translate function, bound so this module stays dependency-light.
|
|
2885
|
+
* @returns the combined label.
|
|
2886
|
+
*/
|
|
2887
|
+
function nextRunLabel(targetMs, timeZone, formatAbsolute, now) {
|
|
2888
|
+
return `${formatAbsolute(targetMs, timeZone)} (${relativeTimeLabel(targetMs, now)})`;
|
|
2889
|
+
}
|
|
2890
|
+
//#endregion
|
|
2542
2891
|
//#region src/client/board/TaskForm.tsx
|
|
2892
|
+
/**
|
|
2893
|
+
* Shared task-modal pieces: the overlay shell (backdrop, form, title, error,
|
|
2894
|
+
* footer) and the title/description/prompt field trio used by both the
|
|
2895
|
+
* NewTaskModal and the EditTaskModal. State stays in the owning modal; these
|
|
2896
|
+
* are controlled components.
|
|
2897
|
+
*/
|
|
2543
2898
|
/** DOM id shared by the tag-name inputs and their datalist (one board at a time). */
|
|
2544
2899
|
const TAG_NAME_LIST_ID = "dsh-task-board-tag-names";
|
|
2545
2900
|
/** Modal overlay: closes on backdrop press, submits through the form. */
|
|
@@ -2562,7 +2917,10 @@ window.__ModuleLoader__.load({
|
|
|
2562
2917
|
className: board_module_css_default.modalTitle,
|
|
2563
2918
|
children: title
|
|
2564
2919
|
}),
|
|
2565
|
-
|
|
2920
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2921
|
+
className: board_module_css_default.modalBody,
|
|
2922
|
+
children
|
|
2923
|
+
}),
|
|
2566
2924
|
error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2567
2925
|
className: board_module_css_default.formError,
|
|
2568
2926
|
children: error
|
|
@@ -2595,6 +2953,48 @@ window.__ModuleLoader__.load({
|
|
|
2595
2953
|
})
|
|
2596
2954
|
});
|
|
2597
2955
|
}
|
|
2956
|
+
/**
|
|
2957
|
+
* One collapsible region of a task form (create/edit dialog UX): the form
|
|
2958
|
+
* groups its configuration into a few regions instead of stacking every field
|
|
2959
|
+
* in one long column, so a normal-sized dialog needs no scrollbar. A collapsed
|
|
2960
|
+
* region keeps a one-line summary of the values it holds, so nothing is hidden
|
|
2961
|
+
* without a trace; a region holding a blocking error is forced open by its
|
|
2962
|
+
* owner (forceOpen) rather than reporting the error out of sight.
|
|
2963
|
+
*/
|
|
2964
|
+
function CollapsibleSection({ title, summary, defaultOpen = false, forceOpen = false, children }) {
|
|
2965
|
+
const [open, setOpen] = (0, react.useState)(defaultOpen);
|
|
2966
|
+
const expanded = forceOpen || open;
|
|
2967
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2968
|
+
className: board_module_css_default.formSection,
|
|
2969
|
+
"data-dsh-part": "form-section",
|
|
2970
|
+
"data-open": expanded ? "true" : void 0,
|
|
2971
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2972
|
+
type: "button",
|
|
2973
|
+
className: board_module_css_default.formSectionHeader,
|
|
2974
|
+
"aria-expanded": expanded,
|
|
2975
|
+
onClick: () => {
|
|
2976
|
+
setOpen((value) => !value);
|
|
2977
|
+
},
|
|
2978
|
+
children: [
|
|
2979
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2980
|
+
className: board_module_css_default.formSectionChevron,
|
|
2981
|
+
"aria-hidden": "true"
|
|
2982
|
+
}),
|
|
2983
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2984
|
+
className: board_module_css_default.formSectionTitle,
|
|
2985
|
+
children: title
|
|
2986
|
+
}),
|
|
2987
|
+
!expanded && summary !== void 0 && summary !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2988
|
+
className: board_module_css_default.formSectionSummary,
|
|
2989
|
+
children: summary
|
|
2990
|
+
})
|
|
2991
|
+
]
|
|
2992
|
+
}), expanded && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2993
|
+
className: board_module_css_default.formSectionBody,
|
|
2994
|
+
children
|
|
2995
|
+
})]
|
|
2996
|
+
});
|
|
2997
|
+
}
|
|
2598
2998
|
/** Title + description + prompt fields shared by the new and edit task forms. */
|
|
2599
2999
|
function TaskContentFields({ title, description, prompt, onTitleChange, onDescriptionChange, onPromptChange }) {
|
|
2600
3000
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
@@ -2618,7 +3018,7 @@ window.__ModuleLoader__.load({
|
|
|
2618
3018
|
children: t("new.description")
|
|
2619
3019
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
2620
3020
|
className: board_module_css_default.input,
|
|
2621
|
-
rows:
|
|
3021
|
+
rows: 2,
|
|
2622
3022
|
value: description,
|
|
2623
3023
|
placeholder: t("new.descriptionPlaceholder"),
|
|
2624
3024
|
onChange: (event) => onDescriptionChange(event.target.value)
|
|
@@ -2728,6 +3128,166 @@ window.__ModuleLoader__.load({
|
|
|
2728
3128
|
return normalizeTags(tags) ?? [];
|
|
2729
3129
|
}
|
|
2730
3130
|
//#endregion
|
|
3131
|
+
//#region src/client/board/TaskCard.tsx
|
|
3132
|
+
/**
|
|
3133
|
+
* Task card: the board's column item. Clicking opens the task detail — it
|
|
3134
|
+
* never executes anything directly (detail holds the Run button).
|
|
3135
|
+
*
|
|
3136
|
+
* Memoized: the card re-renders only when its own task record changes, so a
|
|
3137
|
+
* status/filter update on one card (or scrolling) never re-renders every
|
|
3138
|
+
* card on the board. The per-card onClick is built with a stable task reference
|
|
3139
|
+
* by the board, so the memo boundary is effective.
|
|
3140
|
+
*/
|
|
3141
|
+
/**
|
|
3142
|
+
* Built formatters, keyed by time zone ('' = the browser's own). Constructing
|
|
3143
|
+
* an Intl.DateTimeFormat is orders of magnitude more expensive than formatting
|
|
3144
|
+
* with one, and the board renders dozens of timestamps per SSE frame; the
|
|
3145
|
+
* cached formatter keeps that cost off the render path.
|
|
3146
|
+
*/
|
|
3147
|
+
const HOST_TIMESTAMP_FORMATS = /* @__PURE__ */ new Map();
|
|
3148
|
+
function hostTimestampFormat(timeZone) {
|
|
3149
|
+
const key = timeZone ?? "";
|
|
3150
|
+
const cached = HOST_TIMESTAMP_FORMATS.get(key);
|
|
3151
|
+
if (cached !== void 0) return cached;
|
|
3152
|
+
const format = new Intl.DateTimeFormat(void 0, {
|
|
3153
|
+
dateStyle: "medium",
|
|
3154
|
+
timeStyle: "medium",
|
|
3155
|
+
...timeZone === void 0 ? {} : { timeZone }
|
|
3156
|
+
});
|
|
3157
|
+
HOST_TIMESTAMP_FORMATS.set(key, format);
|
|
3158
|
+
return format;
|
|
3159
|
+
}
|
|
3160
|
+
/** Compact relative/absolute time label. */
|
|
3161
|
+
function formatHostTimestamp(ms, timeZone) {
|
|
3162
|
+
try {
|
|
3163
|
+
return hostTimestampFormat(timeZone).format(new Date(ms));
|
|
3164
|
+
} catch {
|
|
3165
|
+
return new Date(ms).toISOString();
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
function formatTime(ms, timeZone) {
|
|
3169
|
+
const date = new Date(ms);
|
|
3170
|
+
const minutes = Math.floor((Date.now() - ms) / 6e4);
|
|
3171
|
+
if (minutes < 1) return t("time.justNow");
|
|
3172
|
+
if (minutes < 60) return `${minutes}m`;
|
|
3173
|
+
if (minutes < 1440) return `${Math.floor(minutes / 60)}h`;
|
|
3174
|
+
if (timeZone !== void 0) return formatHostTimestamp(ms, timeZone);
|
|
3175
|
+
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
3176
|
+
}
|
|
3177
|
+
function TaskCardInner({ task, pending, timeZone, onClick, subtaskCount = 0, isSubtask = false, subtasksDone = 0, subtasksRunning = 0, subtasksFailed = 0 }) {
|
|
3178
|
+
const latest = task.executions[task.executions.length - 1];
|
|
3179
|
+
const runs = task.executions.length;
|
|
3180
|
+
const archived = task.archivedAt !== void 0;
|
|
3181
|
+
const isDraggable = !archived && task.status !== "running" && !pending;
|
|
3182
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3183
|
+
type: "button",
|
|
3184
|
+
className: board_module_css_default.card,
|
|
3185
|
+
"data-status": archived ? "archived" : task.status,
|
|
3186
|
+
"data-dsh-part": "card",
|
|
3187
|
+
"data-pending": pending || void 0,
|
|
3188
|
+
draggable: isDraggable,
|
|
3189
|
+
onDragStart: isDraggable ? (event) => {
|
|
3190
|
+
event.dataTransfer.setData("text/plain", task.id);
|
|
3191
|
+
event.dataTransfer.effectAllowed = "move";
|
|
3192
|
+
} : void 0,
|
|
3193
|
+
onClick,
|
|
3194
|
+
title: task.description !== "" ? task.description : task.title,
|
|
3195
|
+
children: [
|
|
3196
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3197
|
+
className: board_module_css_default.cardTitle,
|
|
3198
|
+
children: task.title
|
|
3199
|
+
}),
|
|
3200
|
+
task.tags !== void 0 && task.tags.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3201
|
+
className: board_module_css_default.cardTags,
|
|
3202
|
+
children: task.tags.map((tag) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3203
|
+
className: board_module_css_default.cardTag,
|
|
3204
|
+
"data-tag-tone": tagTone(tag.name),
|
|
3205
|
+
"data-dsh-part": "tag-badge",
|
|
3206
|
+
"data-tag-hint": tag.promptPrefix === void 0 ? void 0 : tag.promptPrefix,
|
|
3207
|
+
title: tag.promptPrefix === void 0 ? tag.name : tag.promptPrefix,
|
|
3208
|
+
children: tag.name
|
|
3209
|
+
}, tag.name))
|
|
3210
|
+
}),
|
|
3211
|
+
task.description !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3212
|
+
className: board_module_css_default.cardExcerpt,
|
|
3213
|
+
children: task.description
|
|
3214
|
+
}),
|
|
3215
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3216
|
+
className: board_module_css_default.cardMeta,
|
|
3217
|
+
children: [
|
|
3218
|
+
isSubtask && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3219
|
+
className: board_module_css_default.cardSubtask,
|
|
3220
|
+
"data-dsh-part": "subtask-badge",
|
|
3221
|
+
children: t("card.subtask")
|
|
3222
|
+
}),
|
|
3223
|
+
subtaskCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3224
|
+
className: board_module_css_default.cardSubtask,
|
|
3225
|
+
"data-dsh-part": "subtask-count",
|
|
3226
|
+
"data-tone": subtasksFailed > 0 ? "failed" : subtasksRunning > 0 ? "running" : subtasksDone === subtaskCount ? "done" : void 0,
|
|
3227
|
+
title: t("card.subtasksBreakdown", {
|
|
3228
|
+
total: String(subtaskCount),
|
|
3229
|
+
done: String(subtasksDone),
|
|
3230
|
+
running: String(subtasksRunning),
|
|
3231
|
+
failed: String(subtasksFailed)
|
|
3232
|
+
}),
|
|
3233
|
+
children: [
|
|
3234
|
+
t("card.subtasks", { count: String(subtaskCount) }),
|
|
3235
|
+
subtasksFailed > 0 ? " · " + t("card.subtasksFailed", { count: String(subtasksFailed) }) : "",
|
|
3236
|
+
subtasksFailed === 0 && subtasksRunning > 0 ? " · " + t("card.subtasksRunning", { count: String(subtasksRunning) }) : ""
|
|
3237
|
+
]
|
|
3238
|
+
}),
|
|
3239
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3240
|
+
className: board_module_css_default.cardTime,
|
|
3241
|
+
children: [
|
|
3242
|
+
t("board.updated"),
|
|
3243
|
+
" ",
|
|
3244
|
+
formatTime(task.updatedAt)
|
|
3245
|
+
]
|
|
3246
|
+
}),
|
|
3247
|
+
task.freeze !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3248
|
+
className: board_module_css_default.cardSchedule,
|
|
3249
|
+
title: task.freeze.goal,
|
|
3250
|
+
children: t("card.frozen")
|
|
3251
|
+
}),
|
|
3252
|
+
!archived && task.schedule?.enabled === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3253
|
+
className: board_module_css_default.cardSchedule,
|
|
3254
|
+
title: task.schedule.nextRunAt !== void 0 ? `${t("card.scheduled")} · ${formatHostTimestamp(task.schedule.nextRunAt, timeZone)}` : t("card.scheduled"),
|
|
3255
|
+
children: t("card.scheduled")
|
|
3256
|
+
}),
|
|
3257
|
+
latest !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3258
|
+
className: board_module_css_default.cardRun,
|
|
3259
|
+
"data-result": archived ? void 0 : latest.result,
|
|
3260
|
+
children: [
|
|
3261
|
+
runs,
|
|
3262
|
+
" ",
|
|
3263
|
+
t("board.runs")
|
|
3264
|
+
]
|
|
3265
|
+
}),
|
|
3266
|
+
latest?.sessionId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3267
|
+
className: board_module_css_default.cardSession,
|
|
3268
|
+
title: latest.sessionId,
|
|
3269
|
+
children: "⌁"
|
|
3270
|
+
}),
|
|
3271
|
+
!archived && (task.status === "running" || pending) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3272
|
+
className: board_module_css_default.cardSpinner,
|
|
3273
|
+
"aria-hidden": "true"
|
|
3274
|
+
})
|
|
3275
|
+
]
|
|
3276
|
+
}),
|
|
3277
|
+
!archived && pending && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3278
|
+
className: board_module_css_default.cardRunningLabel,
|
|
3279
|
+
children: [t("board.pending"), "…"]
|
|
3280
|
+
}),
|
|
3281
|
+
!archived && latest !== void 0 && executionLabel(latest) === "running" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3282
|
+
className: board_module_css_default.cardRunningLabel,
|
|
3283
|
+
children: [latest.ownResult === void 0 ? t("detail.result.running") : t("detail.subtasks.waiting"), "…"]
|
|
3284
|
+
})
|
|
3285
|
+
]
|
|
3286
|
+
});
|
|
3287
|
+
}
|
|
3288
|
+
/** Memoized card: re-renders only when the card's own task record changes. */
|
|
3289
|
+
const TaskCard = (0, react.memo)(TaskCardInner);
|
|
3290
|
+
//#endregion
|
|
2731
3291
|
//#region src/client/board/parse-model-pref.ts
|
|
2732
3292
|
/**
|
|
2733
3293
|
* The model the user last picked for AI parsing, remembered per browser so the
|
|
@@ -2759,6 +3319,35 @@ window.__ModuleLoader__.load({
|
|
|
2759
3319
|
} catch {}
|
|
2760
3320
|
}
|
|
2761
3321
|
//#endregion
|
|
3322
|
+
//#region src/client/board/preset-label.ts
|
|
3323
|
+
/** Built-in preset id -> display-name key (display sugar only). */
|
|
3324
|
+
const BUILTIN_PRESET_LABELS = {
|
|
3325
|
+
standard: "preset.builtin.standard",
|
|
3326
|
+
ptc: "preset.builtin.ptc",
|
|
3327
|
+
minimal: "preset.builtin.minimal",
|
|
3328
|
+
cordis: "preset.builtin.cordis"
|
|
3329
|
+
};
|
|
3330
|
+
/** Whether one roster row is a shipped built-in preset. */
|
|
3331
|
+
function isBuiltinPreset(id) {
|
|
3332
|
+
return BUILTIN_PRESET_LABELS[id] !== void 0;
|
|
3333
|
+
}
|
|
3334
|
+
/** Display label of one preset row: built-in label, roster name, or raw id. */
|
|
3335
|
+
function presetLabel(preset) {
|
|
3336
|
+
const builtin = BUILTIN_PRESET_LABELS[preset.id];
|
|
3337
|
+
if (builtin !== void 0 && (preset.name ?? "").trim() === "") return t(builtin);
|
|
3338
|
+
return preset.name ?? preset.id;
|
|
3339
|
+
}
|
|
3340
|
+
/**
|
|
3341
|
+
* Label of the empty ("inherit") choice: names the preset a run actually lands
|
|
3342
|
+
* on when the card pins nothing, which is the deployment default the roster
|
|
3343
|
+
* marks with `isDefault`.
|
|
3344
|
+
*/
|
|
3345
|
+
function inheritPresetLabel(presets) {
|
|
3346
|
+
const fallback = presets.find((preset) => preset.isDefault);
|
|
3347
|
+
if (fallback === void 0) return t("exec.mode.inherit");
|
|
3348
|
+
return t("exec.mode.inheritWith", { preset: presetLabel(fallback) });
|
|
3349
|
+
}
|
|
3350
|
+
//#endregion
|
|
2762
3351
|
//#region src/client/board/NewTaskModal.tsx
|
|
2763
3352
|
/**
|
|
2764
3353
|
* New-task modal: title + description + the prompt that execution will send.
|
|
@@ -2776,8 +3365,10 @@ window.__ModuleLoader__.load({
|
|
|
2776
3365
|
const [model, setModel] = (0, react.useState)(initialTask?.model ?? parentTask?.model ?? "");
|
|
2777
3366
|
const inheritedPermission = parentTask === void 0 ? void 0 : effectiveTaskPermission(parentTask);
|
|
2778
3367
|
const [reuseSession, setReuseSession] = (0, react.useState)(initialTask?.reuseSession ?? false);
|
|
3368
|
+
const [goalRun, setGoalRun] = (0, react.useState)(initialTask?.goalRun ?? true);
|
|
2779
3369
|
const [scheduleEnabled, setScheduleEnabled] = (0, react.useState)(initialTask?.schedule?.enabled ?? false);
|
|
2780
3370
|
const [scheduleCron, setScheduleCron] = (0, react.useState)(initialTask?.schedule?.cron ?? "");
|
|
3371
|
+
const [scheduleZone, setScheduleZone] = (0, react.useState)(initialTask?.schedule?.timeZone ?? "");
|
|
2781
3372
|
const [scheduleError, setScheduleError] = (0, react.useState)(void 0);
|
|
2782
3373
|
const [freezeText, setFreezeText] = (0, react.useState)("");
|
|
2783
3374
|
const [freezeError, setFreezeError] = (0, react.useState)(void 0);
|
|
@@ -2876,10 +3467,12 @@ window.__ModuleLoader__.load({
|
|
|
2876
3467
|
permission: permission === "" ? void 0 : permission,
|
|
2877
3468
|
model: model === "" ? void 0 : model,
|
|
2878
3469
|
...reuseSession ? { reuseSession: true } : {},
|
|
3470
|
+
...goalRun ? {} : { goalRun: false },
|
|
2879
3471
|
...tagList.length > 0 ? { tags: tagList } : {},
|
|
2880
3472
|
schedule: scheduleEnabled ? {
|
|
2881
3473
|
enabled: true,
|
|
2882
|
-
cron: scheduleCron.trim()
|
|
3474
|
+
cron: scheduleCron.trim(),
|
|
3475
|
+
...scheduleZone === "" ? {} : { timeZone: scheduleZone }
|
|
2883
3476
|
} : void 0
|
|
2884
3477
|
});
|
|
2885
3478
|
if (task === void 0) {
|
|
@@ -2894,9 +3487,30 @@ window.__ModuleLoader__.load({
|
|
|
2894
3487
|
}
|
|
2895
3488
|
onClose();
|
|
2896
3489
|
};
|
|
2897
|
-
|
|
2898
|
-
const
|
|
3490
|
+
const hostTimeZone = controller.getSnapshot().host?.scheduler.timeZone;
|
|
3491
|
+
const scheduleTimeZone = scheduleZone === "" ? hostTimeZone : scheduleZone;
|
|
3492
|
+
const scheduleNextRun = scheduleEnabled && scheduleCron.trim() !== "" && isValidCron(scheduleCron) ? nextRunAtMs(scheduleCron, Date.now(), scheduleTimeZone) : void 0;
|
|
2899
3493
|
const modalTitle = parentTask !== void 0 ? t("new.subtaskTitle") : isDuplicate ? t("new.duplicateTitle") : t("board.new");
|
|
3494
|
+
const builtinPresets = options.presets.filter((preset) => isBuiltinPreset(preset.id));
|
|
3495
|
+
const customPresets = options.presets.filter((preset) => !isBuiltinPreset(preset.id));
|
|
3496
|
+
const inheritLabel = inheritPresetLabel(options.presets);
|
|
3497
|
+
const selectedPreset = options.presets.find((preset) => preset.id === mode);
|
|
3498
|
+
const modeLabel = mode === "" ? inheritLabel : selectedPreset === void 0 ? mode : presetLabel(selectedPreset);
|
|
3499
|
+
const workspaceLabel = workspaceId === "" ? t("exec.workspace.recent") : options.workspaces.find((item) => item.workspaceId === workspaceId)?.title ?? workspaceId;
|
|
3500
|
+
const permissionLabel = permission === "" ? inheritedPermission === void 0 ? t("exec.permission.default") : t("exec.permission.inheritParent", { permission: t(`exec.permission.${inheritedPermission}`) }) : t(`exec.permission.${permission}`);
|
|
3501
|
+
const modelLabel = model === "" ? t("exec.model.default") : (options.models ?? []).find((item) => item.id === model)?.name ?? model;
|
|
3502
|
+
const executionSummary = [
|
|
3503
|
+
workspaceLabel,
|
|
3504
|
+
modeLabel,
|
|
3505
|
+
permissionLabel,
|
|
3506
|
+
...model === "" ? [] : [modelLabel]
|
|
3507
|
+
].join(" · ");
|
|
3508
|
+
const tagCount = cleanTags(tags).length;
|
|
3509
|
+
const labelsSummary = tagCount === 0 ? t("new.summary.none") : t("new.summary.labelCount", { count: String(tagCount) });
|
|
3510
|
+
const runSummary = [goalRun ? t("new.summary.multiRound") : t("new.summary.singleRound"), ...reuseSession ? [t("exec.reuseSession")] : []].join(" · ");
|
|
3511
|
+
const handoverSummary = freezeText.trim() !== "" || handoverText.trim() !== "" ? t("new.summary.filled") : t("new.summary.none");
|
|
3512
|
+
const scheduleSummary = !scheduleEnabled ? t("new.summary.scheduleOff") : scheduleCron.trim() === "" ? t("new.summary.none") : scheduleCron.trim();
|
|
3513
|
+
const parseSummary = parseText.trim() === "" ? t("new.summary.none") : t("new.summary.filled");
|
|
2900
3514
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ModalShell, {
|
|
2901
3515
|
ariaLabel: modalTitle,
|
|
2902
3516
|
title: modalTitle,
|
|
@@ -2914,68 +3528,73 @@ window.__ModuleLoader__.load({
|
|
|
2914
3528
|
}
|
|
2915
3529
|
},
|
|
2916
3530
|
children: [
|
|
2917
|
-
canParse && /* @__PURE__ */ (0, react_jsx_runtime.
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
3531
|
+
canParse && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CollapsibleSection, {
|
|
3532
|
+
title: t("new.section.parse"),
|
|
3533
|
+
summary: parseSummary,
|
|
3534
|
+
forceOpen: parseError !== void 0,
|
|
3535
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3536
|
+
className: board_module_css_default.aiParse,
|
|
3537
|
+
"data-dsh-part": "ai-parse",
|
|
3538
|
+
children: [
|
|
3539
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3540
|
+
className: board_module_css_default.fieldLabel,
|
|
3541
|
+
children: t("new.aiParse")
|
|
3542
|
+
}),
|
|
3543
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3544
|
+
className: board_module_css_default.fieldHint,
|
|
3545
|
+
children: t("new.aiParseHint")
|
|
3546
|
+
}),
|
|
3547
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
3548
|
+
className: board_module_css_default.input,
|
|
3549
|
+
rows: 3,
|
|
3550
|
+
value: parseText,
|
|
3551
|
+
placeholder: t("new.aiParsePlaceholder"),
|
|
3552
|
+
spellCheck: false,
|
|
3553
|
+
onChange: (event) => {
|
|
3554
|
+
setParseText(event.target.value);
|
|
3555
|
+
setParseError(void 0);
|
|
3556
|
+
}
|
|
3557
|
+
}),
|
|
3558
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3559
|
+
className: board_module_css_default.aiParseRow,
|
|
3560
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3561
|
+
className: board_module_css_default.select,
|
|
3562
|
+
value: parseModel,
|
|
3563
|
+
"aria-label": t("new.aiParseModel"),
|
|
3564
|
+
onChange: (event) => {
|
|
3565
|
+
setParseModel(event.target.value);
|
|
3566
|
+
writeParseModelPreference(event.target.value);
|
|
3567
|
+
},
|
|
3568
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3569
|
+
value: "",
|
|
3570
|
+
children: t("exec.model.default")
|
|
3571
|
+
}), parseModels.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3572
|
+
value: option.id,
|
|
3573
|
+
children: option.name ?? option.id
|
|
3574
|
+
}, option.id))]
|
|
3575
|
+
}), parsePending ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3576
|
+
type: "button",
|
|
3577
|
+
className: board_module_css_default.ghostButton,
|
|
3578
|
+
onClick: () => {
|
|
3579
|
+
parseAbort.current?.abort();
|
|
3580
|
+
},
|
|
3581
|
+
children: t("new.aiParseCancel")
|
|
3582
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3583
|
+
type: "button",
|
|
3584
|
+
className: board_module_css_default.primaryButton,
|
|
3585
|
+
disabled: parseText.trim() === "",
|
|
3586
|
+
onClick: () => {
|
|
3587
|
+
runParse();
|
|
3588
|
+
},
|
|
3589
|
+
children: t("new.aiParseRun")
|
|
3590
|
+
})]
|
|
3591
|
+
}),
|
|
3592
|
+
parseError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3593
|
+
className: board_module_css_default.formError,
|
|
3594
|
+
children: parseError
|
|
3595
|
+
})
|
|
3596
|
+
]
|
|
3597
|
+
})
|
|
2979
3598
|
}),
|
|
2980
3599
|
parentTask !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
2981
3600
|
className: board_module_css_default.detailText,
|
|
@@ -2988,409 +3607,333 @@ window.__ModuleLoader__.load({
|
|
|
2988
3607
|
t("new.subtaskInherit")
|
|
2989
3608
|
]
|
|
2990
3609
|
}),
|
|
2991
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2992
|
-
title,
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
}),
|
|
3002
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TaskTagFields, {
|
|
3003
|
-
tags,
|
|
3004
|
-
knownTags: collectKnownTags(controller.getSnapshot().tasks),
|
|
3005
|
-
onChange: setTags
|
|
3006
|
-
}),
|
|
3007
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3008
|
-
className: board_module_css_default.field,
|
|
3009
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3010
|
-
className: board_module_css_default.fieldLabel,
|
|
3011
|
-
children: t("new.freeze")
|
|
3012
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
3013
|
-
className: board_module_css_default.input,
|
|
3014
|
-
rows: 4,
|
|
3015
|
-
value: freezeText,
|
|
3016
|
-
placeholder: t("new.freezePlaceholder"),
|
|
3017
|
-
spellCheck: false,
|
|
3018
|
-
onChange: (event) => {
|
|
3019
|
-
setFreezeText(event.target.value);
|
|
3020
|
-
setFreezeError(void 0);
|
|
3021
|
-
}
|
|
3022
|
-
})]
|
|
3023
|
-
}),
|
|
3024
|
-
freezeError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3025
|
-
className: board_module_css_default.formError,
|
|
3026
|
-
children: freezeError
|
|
3027
|
-
}),
|
|
3028
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3029
|
-
className: board_module_css_default.field,
|
|
3030
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3031
|
-
className: board_module_css_default.fieldLabel,
|
|
3032
|
-
children: t("new.handover")
|
|
3033
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
3034
|
-
className: board_module_css_default.input,
|
|
3035
|
-
rows: 3,
|
|
3036
|
-
value: handoverText,
|
|
3037
|
-
placeholder: t("new.handoverPlaceholder"),
|
|
3038
|
-
spellCheck: false,
|
|
3039
|
-
onChange: (event) => {
|
|
3040
|
-
setHandoverText(event.target.value);
|
|
3041
|
-
}
|
|
3042
|
-
})]
|
|
3043
|
-
}),
|
|
3044
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3045
|
-
className: board_module_css_default.field,
|
|
3046
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3047
|
-
className: board_module_css_default.fieldLabel,
|
|
3048
|
-
children: t("new.workspace")
|
|
3049
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3050
|
-
className: board_module_css_default.select,
|
|
3051
|
-
value: workspaceId,
|
|
3052
|
-
onChange: (event) => {
|
|
3053
|
-
setWorkspaceId(event.target.value);
|
|
3054
|
-
},
|
|
3055
|
-
children: [
|
|
3056
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3057
|
-
value: "",
|
|
3058
|
-
children: t("exec.workspace.recent")
|
|
3059
|
-
}),
|
|
3060
|
-
!workspaceKnown && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3061
|
-
value: workspaceId,
|
|
3062
|
-
children: [workspaceId, t("exec.mode.removed")]
|
|
3063
|
-
}),
|
|
3064
|
-
options.workspaces.map((workspace) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3065
|
-
value: workspace.workspaceId,
|
|
3066
|
-
children: workspace.title
|
|
3067
|
-
}, workspace.workspaceId))
|
|
3068
|
-
]
|
|
3069
|
-
})]
|
|
3070
|
-
}),
|
|
3071
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3072
|
-
className: board_module_css_default.field,
|
|
3073
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3074
|
-
className: board_module_css_default.fieldLabel,
|
|
3075
|
-
children: t("new.mode")
|
|
3076
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3077
|
-
className: board_module_css_default.select,
|
|
3078
|
-
value: mode,
|
|
3079
|
-
onChange: (event) => {
|
|
3080
|
-
setMode(event.target.value);
|
|
3081
|
-
},
|
|
3082
|
-
children: [
|
|
3083
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3084
|
-
value: "",
|
|
3085
|
-
children: t("exec.mode.default")
|
|
3086
|
-
}),
|
|
3087
|
-
!modeKnown && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3088
|
-
value: mode,
|
|
3089
|
-
children: [mode, t("exec.mode.removed")]
|
|
3090
|
-
}),
|
|
3091
|
-
options.presets.map((preset) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3092
|
-
value: preset.id,
|
|
3093
|
-
disabled: preset.broken !== void 0,
|
|
3094
|
-
children: [
|
|
3095
|
-
preset.name ?? preset.id,
|
|
3096
|
-
preset.isDefault ? t("exec.mode.defaultSuffix") : "",
|
|
3097
|
-
preset.broken !== void 0 ? t("exec.mode.brokenSuffix") : ""
|
|
3098
|
-
]
|
|
3099
|
-
}, preset.id))
|
|
3100
|
-
]
|
|
3101
|
-
})]
|
|
3102
|
-
}),
|
|
3103
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3104
|
-
className: board_module_css_default.field,
|
|
3105
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3106
|
-
className: board_module_css_default.fieldLabel,
|
|
3107
|
-
children: t("new.permission")
|
|
3108
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3109
|
-
className: board_module_css_default.select,
|
|
3110
|
-
value: permission,
|
|
3111
|
-
onChange: (event) => {
|
|
3112
|
-
setPermission(event.target.value);
|
|
3113
|
-
},
|
|
3114
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3115
|
-
value: "",
|
|
3116
|
-
children: inheritedPermission === void 0 ? t("exec.permission.default") : t("exec.permission.inheritParent", { permission: t(`exec.permission.${inheritedPermission}`) })
|
|
3117
|
-
}), TASK_PERMISSIONS.map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3118
|
-
value: id,
|
|
3119
|
-
children: t(`exec.permission.${id}`)
|
|
3120
|
-
}, id))]
|
|
3121
|
-
})]
|
|
3122
|
-
}),
|
|
3123
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3124
|
-
className: board_module_css_default.field,
|
|
3125
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3126
|
-
className: board_module_css_default.fieldLabel,
|
|
3127
|
-
children: t("new.model")
|
|
3128
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3129
|
-
className: board_module_css_default.select,
|
|
3130
|
-
value: model,
|
|
3131
|
-
onChange: (event) => {
|
|
3132
|
-
setModel(event.target.value);
|
|
3610
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CollapsibleSection, {
|
|
3611
|
+
title: t("new.section.content"),
|
|
3612
|
+
defaultOpen: true,
|
|
3613
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TaskContentFields, {
|
|
3614
|
+
title,
|
|
3615
|
+
description,
|
|
3616
|
+
prompt,
|
|
3617
|
+
onTitleChange: (value) => {
|
|
3618
|
+
setTitle(value);
|
|
3619
|
+
setError(void 0);
|
|
3133
3620
|
},
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
children: item.name ?? item.id
|
|
3146
|
-
}, item.id))
|
|
3147
|
-
]
|
|
3621
|
+
onDescriptionChange: setDescription,
|
|
3622
|
+
onPromptChange: setPrompt
|
|
3623
|
+
}), isDuplicate && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3624
|
+
className: board_module_css_default.checkboxLabel,
|
|
3625
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3626
|
+
type: "checkbox",
|
|
3627
|
+
checked: archiveOriginal,
|
|
3628
|
+
onChange: (event) => {
|
|
3629
|
+
setArchiveOriginal(event.target.checked);
|
|
3630
|
+
}
|
|
3631
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("new.archiveOriginal") })]
|
|
3148
3632
|
})]
|
|
3149
3633
|
}),
|
|
3150
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("exec.reuseSession") })]
|
|
3159
|
-
}),
|
|
3160
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3161
|
-
className: board_module_css_default.detailText,
|
|
3162
|
-
children: t("exec.reuseSessionHint")
|
|
3634
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CollapsibleSection, {
|
|
3635
|
+
title: t("new.section.labels"),
|
|
3636
|
+
summary: labelsSummary,
|
|
3637
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TaskTagFields, {
|
|
3638
|
+
tags,
|
|
3639
|
+
knownTags: collectKnownTags(controller.getSnapshot().tasks),
|
|
3640
|
+
onChange: setTags
|
|
3641
|
+
})
|
|
3163
3642
|
}),
|
|
3164
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(
|
|
3165
|
-
|
|
3643
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CollapsibleSection, {
|
|
3644
|
+
title: t("new.section.execution"),
|
|
3645
|
+
summary: executionSummary,
|
|
3166
3646
|
children: [
|
|
3167
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("detail.schedule") }),
|
|
3168
3647
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3169
|
-
className: board_module_css_default.
|
|
3170
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
3171
|
-
|
|
3172
|
-
|
|
3648
|
+
className: board_module_css_default.field,
|
|
3649
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3650
|
+
className: board_module_css_default.fieldLabel,
|
|
3651
|
+
children: t("new.workspace")
|
|
3652
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3653
|
+
className: board_module_css_default.select,
|
|
3654
|
+
value: workspaceId,
|
|
3173
3655
|
onChange: (event) => {
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
}
|
|
3177
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("detail.schedule.enable") })]
|
|
3178
|
-
}),
|
|
3179
|
-
scheduleEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
3180
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3181
|
-
className: board_module_css_default.scheduleRow,
|
|
3182
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3183
|
-
className: `${board_module_css_default.input} ${board_module_css_default.scheduleInput}${scheduleError !== void 0 ? ` ${board_module_css_default.scheduleInputInvalid}` : ""}`,
|
|
3184
|
-
value: scheduleCron,
|
|
3185
|
-
placeholder: "0 9 * * *",
|
|
3186
|
-
spellCheck: false,
|
|
3187
|
-
"aria-label": t("detail.schedule.cron"),
|
|
3188
|
-
onChange: (event) => {
|
|
3189
|
-
setScheduleCron(event.target.value);
|
|
3190
|
-
setScheduleError(void 0);
|
|
3191
|
-
}
|
|
3192
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3193
|
-
className: board_module_css_default.schedulePreset,
|
|
3194
|
-
value: "",
|
|
3195
|
-
"aria-label": t("detail.schedule.presets"),
|
|
3196
|
-
onChange: (event) => {
|
|
3197
|
-
if (event.target.value === "") return;
|
|
3198
|
-
setScheduleCron(event.target.value);
|
|
3199
|
-
setScheduleError(void 0);
|
|
3200
|
-
},
|
|
3201
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3202
|
-
value: "",
|
|
3203
|
-
children: [t("detail.schedule.presets"), "…"]
|
|
3204
|
-
}), SCHEDULE_PRESETS.map((preset) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3205
|
-
value: preset.cron,
|
|
3206
|
-
children: t(preset.label)
|
|
3207
|
-
}, preset.cron))]
|
|
3208
|
-
})]
|
|
3209
|
-
}),
|
|
3210
|
-
scheduleError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3211
|
-
className: board_module_css_default.formError,
|
|
3212
|
-
children: scheduleError
|
|
3213
|
-
}),
|
|
3214
|
-
scheduleError === void 0 && scheduleNextRun !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
3215
|
-
className: board_module_css_default.scheduleMeta,
|
|
3656
|
+
setWorkspaceId(event.target.value);
|
|
3657
|
+
},
|
|
3216
3658
|
children: [
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3659
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3660
|
+
value: "",
|
|
3661
|
+
children: t("exec.workspace.recent")
|
|
3662
|
+
}),
|
|
3663
|
+
!workspaceKnown && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3664
|
+
value: workspaceId,
|
|
3665
|
+
children: [workspaceId, t("exec.mode.removed")]
|
|
3666
|
+
}),
|
|
3667
|
+
options.workspaces.map((workspace) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3668
|
+
value: workspace.workspaceId,
|
|
3669
|
+
children: workspace.title
|
|
3670
|
+
}, workspace.workspaceId))
|
|
3220
3671
|
]
|
|
3221
|
-
})
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3672
|
+
})]
|
|
3673
|
+
}),
|
|
3674
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3675
|
+
className: board_module_css_default.field,
|
|
3676
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3677
|
+
className: board_module_css_default.fieldLabel,
|
|
3678
|
+
children: t("new.agentPreset")
|
|
3679
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3680
|
+
className: board_module_css_default.select,
|
|
3681
|
+
value: mode,
|
|
3682
|
+
onChange: (event) => {
|
|
3683
|
+
setMode(event.target.value);
|
|
3684
|
+
},
|
|
3685
|
+
children: [
|
|
3686
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3687
|
+
value: "",
|
|
3688
|
+
children: inheritLabel
|
|
3689
|
+
}),
|
|
3690
|
+
!modeKnown && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3691
|
+
value: mode,
|
|
3692
|
+
children: [mode, t("exec.mode.removed")]
|
|
3693
|
+
}),
|
|
3694
|
+
builtinPresets.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("optgroup", {
|
|
3695
|
+
label: t("exec.mode.builtinGroup"),
|
|
3696
|
+
children: builtinPresets.map((preset) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3697
|
+
value: preset.id,
|
|
3698
|
+
disabled: preset.broken !== void 0,
|
|
3699
|
+
children: [
|
|
3700
|
+
presetLabel(preset),
|
|
3701
|
+
preset.isDefault ? t("exec.mode.defaultSuffix") : "",
|
|
3702
|
+
preset.broken !== void 0 ? t("exec.mode.brokenSuffix") : ""
|
|
3703
|
+
]
|
|
3704
|
+
}, preset.id))
|
|
3705
|
+
}),
|
|
3706
|
+
customPresets.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("optgroup", {
|
|
3707
|
+
label: t("exec.mode.customGroup"),
|
|
3708
|
+
children: customPresets.map((preset) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3709
|
+
value: preset.id,
|
|
3710
|
+
disabled: preset.broken !== void 0,
|
|
3711
|
+
children: [
|
|
3712
|
+
presetLabel(preset),
|
|
3713
|
+
preset.isDefault ? t("exec.mode.defaultSuffix") : "",
|
|
3714
|
+
preset.broken !== void 0 ? t("exec.mode.brokenSuffix") : ""
|
|
3715
|
+
]
|
|
3716
|
+
}, preset.id))
|
|
3717
|
+
})
|
|
3718
|
+
]
|
|
3719
|
+
})]
|
|
3720
|
+
}),
|
|
3721
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3722
|
+
className: board_module_css_default.field,
|
|
3723
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3724
|
+
className: board_module_css_default.fieldLabel,
|
|
3725
|
+
children: t("new.permission")
|
|
3726
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3727
|
+
className: board_module_css_default.select,
|
|
3728
|
+
value: permission,
|
|
3729
|
+
onChange: (event) => {
|
|
3730
|
+
setPermission(event.target.value);
|
|
3731
|
+
},
|
|
3732
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3733
|
+
value: "",
|
|
3734
|
+
children: inheritedPermission === void 0 ? t("exec.permission.default") : t("exec.permission.inheritParent", { permission: t(`exec.permission.${inheritedPermission}`) })
|
|
3735
|
+
}), TASK_PERMISSIONS.map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3736
|
+
value: id,
|
|
3737
|
+
children: t(`exec.permission.${id}`)
|
|
3738
|
+
}, id))]
|
|
3739
|
+
})]
|
|
3740
|
+
}),
|
|
3741
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3742
|
+
className: board_module_css_default.field,
|
|
3743
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3744
|
+
className: board_module_css_default.fieldLabel,
|
|
3745
|
+
children: t("new.model")
|
|
3746
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3747
|
+
className: board_module_css_default.select,
|
|
3748
|
+
value: model,
|
|
3749
|
+
onChange: (event) => {
|
|
3750
|
+
setModel(event.target.value);
|
|
3751
|
+
},
|
|
3752
|
+
children: [
|
|
3753
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3754
|
+
value: "",
|
|
3755
|
+
children: t("exec.model.default")
|
|
3756
|
+
}),
|
|
3757
|
+
!modelKnown && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3758
|
+
value: model,
|
|
3759
|
+
children: [model, t("exec.model.unknown")]
|
|
3760
|
+
}),
|
|
3761
|
+
options.models?.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3762
|
+
value: item.id,
|
|
3763
|
+
children: item.name ?? item.id
|
|
3764
|
+
}, item.id))
|
|
3765
|
+
]
|
|
3766
|
+
})]
|
|
3767
|
+
})
|
|
3768
|
+
]
|
|
3314
3769
|
}),
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3770
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CollapsibleSection, {
|
|
3771
|
+
title: t("new.section.run"),
|
|
3772
|
+
summary: runSummary,
|
|
3773
|
+
children: [
|
|
3774
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3775
|
+
className: board_module_css_default.scheduleToggle,
|
|
3776
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3777
|
+
type: "checkbox",
|
|
3778
|
+
checked: reuseSession,
|
|
3779
|
+
onChange: (event) => {
|
|
3780
|
+
setReuseSession(event.target.checked);
|
|
3781
|
+
}
|
|
3782
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("exec.reuseSession") })]
|
|
3783
|
+
}),
|
|
3784
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3785
|
+
className: board_module_css_default.detailText,
|
|
3786
|
+
children: t("exec.reuseSessionHint")
|
|
3787
|
+
}),
|
|
3788
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3789
|
+
className: board_module_css_default.scheduleToggle,
|
|
3790
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3791
|
+
type: "checkbox",
|
|
3792
|
+
checked: goalRun,
|
|
3793
|
+
onChange: (event) => {
|
|
3794
|
+
setGoalRun(event.target.checked);
|
|
3795
|
+
}
|
|
3796
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("exec.goalRun") })]
|
|
3797
|
+
}),
|
|
3798
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3799
|
+
className: board_module_css_default.detailText,
|
|
3800
|
+
children: t("exec.goalRunHint")
|
|
3801
|
+
})
|
|
3802
|
+
]
|
|
3318
3803
|
}),
|
|
3319
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(
|
|
3320
|
-
|
|
3804
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CollapsibleSection, {
|
|
3805
|
+
title: t("new.section.handover"),
|
|
3806
|
+
summary: handoverSummary,
|
|
3807
|
+
forceOpen: freezeError !== void 0,
|
|
3321
3808
|
children: [
|
|
3322
|
-
|
|
3323
|
-
className: board_module_css_default.
|
|
3324
|
-
|
|
3325
|
-
|
|
3809
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3810
|
+
className: board_module_css_default.field,
|
|
3811
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3812
|
+
className: board_module_css_default.fieldLabel,
|
|
3813
|
+
children: t("new.freeze")
|
|
3814
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
3815
|
+
className: board_module_css_default.input,
|
|
3816
|
+
rows: 4,
|
|
3817
|
+
value: freezeText,
|
|
3818
|
+
placeholder: t("new.freezePlaceholder"),
|
|
3819
|
+
spellCheck: false,
|
|
3820
|
+
onChange: (event) => {
|
|
3821
|
+
setFreezeText(event.target.value);
|
|
3822
|
+
setFreezeError(void 0);
|
|
3823
|
+
}
|
|
3824
|
+
})]
|
|
3326
3825
|
}),
|
|
3327
|
-
|
|
3328
|
-
className: board_module_css_default.
|
|
3329
|
-
|
|
3330
|
-
"data-tone": subtasksFailed > 0 ? "failed" : subtasksRunning > 0 ? "running" : subtasksDone === subtaskCount ? "done" : void 0,
|
|
3331
|
-
title: t("card.subtasksBreakdown", {
|
|
3332
|
-
total: String(subtaskCount),
|
|
3333
|
-
done: String(subtasksDone),
|
|
3334
|
-
running: String(subtasksRunning),
|
|
3335
|
-
failed: String(subtasksFailed)
|
|
3336
|
-
}),
|
|
3337
|
-
children: [
|
|
3338
|
-
t("card.subtasks", { count: String(subtaskCount) }),
|
|
3339
|
-
subtasksFailed > 0 ? " · " + t("card.subtasksFailed", { count: String(subtasksFailed) }) : "",
|
|
3340
|
-
subtasksFailed === 0 && subtasksRunning > 0 ? " · " + t("card.subtasksRunning", { count: String(subtasksRunning) }) : ""
|
|
3341
|
-
]
|
|
3826
|
+
freezeError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3827
|
+
className: board_module_css_default.formError,
|
|
3828
|
+
children: freezeError
|
|
3342
3829
|
}),
|
|
3343
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
3344
|
-
className: board_module_css_default.
|
|
3345
|
-
children: [
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3830
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3831
|
+
className: board_module_css_default.field,
|
|
3832
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3833
|
+
className: board_module_css_default.fieldLabel,
|
|
3834
|
+
children: t("new.handover")
|
|
3835
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
3836
|
+
className: board_module_css_default.input,
|
|
3837
|
+
rows: 3,
|
|
3838
|
+
value: handoverText,
|
|
3839
|
+
placeholder: t("new.handoverPlaceholder"),
|
|
3840
|
+
spellCheck: false,
|
|
3841
|
+
onChange: (event) => {
|
|
3842
|
+
setHandoverText(event.target.value);
|
|
3843
|
+
}
|
|
3844
|
+
})]
|
|
3845
|
+
})
|
|
3846
|
+
]
|
|
3847
|
+
}),
|
|
3848
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CollapsibleSection, {
|
|
3849
|
+
title: t("new.section.schedule"),
|
|
3850
|
+
summary: scheduleSummary,
|
|
3851
|
+
forceOpen: scheduleError !== void 0,
|
|
3852
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3853
|
+
className: board_module_css_default.scheduleToggle,
|
|
3854
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3855
|
+
type: "checkbox",
|
|
3856
|
+
checked: scheduleEnabled,
|
|
3857
|
+
onChange: (event) => {
|
|
3858
|
+
setScheduleEnabled(event.target.checked);
|
|
3859
|
+
if (!event.target.checked) setScheduleError(void 0);
|
|
3860
|
+
}
|
|
3861
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("detail.schedule.enable") })]
|
|
3862
|
+
}), scheduleEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
3863
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3864
|
+
className: board_module_css_default.scheduleRow,
|
|
3865
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3866
|
+
className: `${board_module_css_default.input} ${board_module_css_default.scheduleInput}${scheduleError !== void 0 ? ` ${board_module_css_default.scheduleInputInvalid}` : ""}`,
|
|
3867
|
+
value: scheduleCron,
|
|
3868
|
+
placeholder: "0 9 * * *",
|
|
3869
|
+
spellCheck: false,
|
|
3870
|
+
"aria-label": t("detail.schedule.cron"),
|
|
3871
|
+
onChange: (event) => {
|
|
3872
|
+
setScheduleCron(event.target.value);
|
|
3873
|
+
setScheduleError(void 0);
|
|
3874
|
+
}
|
|
3875
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3876
|
+
className: board_module_css_default.schedulePreset,
|
|
3877
|
+
value: "",
|
|
3878
|
+
"aria-label": t("detail.schedule.presets"),
|
|
3879
|
+
onChange: (event) => {
|
|
3880
|
+
if (event.target.value === "") return;
|
|
3881
|
+
setScheduleCron(event.target.value);
|
|
3882
|
+
setScheduleError(void 0);
|
|
3883
|
+
},
|
|
3884
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3885
|
+
value: "",
|
|
3886
|
+
children: [t("detail.schedule.presets"), "…"]
|
|
3887
|
+
}), SCHEDULE_PRESETS.map((preset) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3888
|
+
value: preset.cron,
|
|
3889
|
+
children: t(preset.label)
|
|
3890
|
+
}, preset.cron))]
|
|
3891
|
+
})]
|
|
3350
3892
|
}),
|
|
3351
|
-
|
|
3352
|
-
className: board_module_css_default.
|
|
3353
|
-
|
|
3354
|
-
children: t("card.frozen")
|
|
3893
|
+
scheduleError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3894
|
+
className: board_module_css_default.formError,
|
|
3895
|
+
children: scheduleError
|
|
3355
3896
|
}),
|
|
3356
|
-
|
|
3357
|
-
className: board_module_css_default.
|
|
3358
|
-
|
|
3359
|
-
|
|
3897
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3898
|
+
className: board_module_css_default.scheduleZone,
|
|
3899
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("detail.schedule.timeZone") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
3900
|
+
className: board_module_css_default.schedulePreset,
|
|
3901
|
+
value: scheduleZone,
|
|
3902
|
+
"aria-label": t("detail.schedule.timeZone"),
|
|
3903
|
+
title: t("detail.schedule.timeZoneHint"),
|
|
3904
|
+
onChange: (event) => {
|
|
3905
|
+
setScheduleZone(event.target.value);
|
|
3906
|
+
setScheduleError(void 0);
|
|
3907
|
+
},
|
|
3908
|
+
children: zoneChoices(hostTimeZone, initialTask?.schedule?.timeZone).map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3909
|
+
value: choice.id,
|
|
3910
|
+
children: choice.label
|
|
3911
|
+
}, choice.id === "" ? "__host" : choice.id))
|
|
3912
|
+
})]
|
|
3360
3913
|
}),
|
|
3361
|
-
|
|
3362
|
-
className: board_module_css_default.
|
|
3363
|
-
"data-result": archived ? void 0 : latest.result,
|
|
3914
|
+
scheduleError === void 0 && scheduleNextRun !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
3915
|
+
className: board_module_css_default.scheduleMeta,
|
|
3364
3916
|
children: [
|
|
3365
|
-
|
|
3917
|
+
t("detail.schedule.nextRun"),
|
|
3366
3918
|
" ",
|
|
3367
|
-
|
|
3919
|
+
nextRunLabel(scheduleNextRun, scheduleTimeZone, formatHostTimestamp, Date.now())
|
|
3368
3920
|
]
|
|
3369
|
-
}),
|
|
3370
|
-
latest?.sessionId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3371
|
-
className: board_module_css_default.cardSession,
|
|
3372
|
-
title: latest.sessionId,
|
|
3373
|
-
children: "⌁"
|
|
3374
|
-
}),
|
|
3375
|
-
!archived && (task.status === "running" || pending) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3376
|
-
className: board_module_css_default.cardSpinner,
|
|
3377
|
-
"aria-hidden": "true"
|
|
3378
3921
|
})
|
|
3379
|
-
]
|
|
3380
|
-
}),
|
|
3381
|
-
!archived && pending && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3382
|
-
className: board_module_css_default.cardRunningLabel,
|
|
3383
|
-
children: [t("board.pending"), "…"]
|
|
3384
|
-
}),
|
|
3385
|
-
!archived && latest !== void 0 && executionLabel(latest) === "running" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3386
|
-
className: board_module_css_default.cardRunningLabel,
|
|
3387
|
-
children: [latest.ownResult === void 0 ? t("detail.result.running") : t("detail.subtasks.waiting"), "…"]
|
|
3922
|
+
] })]
|
|
3388
3923
|
})
|
|
3389
3924
|
]
|
|
3390
3925
|
});
|
|
3391
3926
|
}
|
|
3392
|
-
|
|
3393
|
-
|
|
3927
|
+
//#endregion
|
|
3928
|
+
//#region src/client/board/status-key.ts
|
|
3929
|
+
/** Task status → locale key (board column titles and the detail badge). */
|
|
3930
|
+
const STATUS_KEY = {
|
|
3931
|
+
backlog: "board.status.backlog",
|
|
3932
|
+
todo: "board.status.todo",
|
|
3933
|
+
running: "board.status.running",
|
|
3934
|
+
done: "board.status.done",
|
|
3935
|
+
failed: "board.status.failed"
|
|
3936
|
+
};
|
|
3394
3937
|
//#endregion
|
|
3395
3938
|
//#region src/client/board/ConfirmDialog.tsx
|
|
3396
3939
|
/**
|
|
@@ -3545,7 +4088,15 @@ window.__ModuleLoader__.load({
|
|
|
3545
4088
|
const [error, setError] = (0, react.useState)(void 0);
|
|
3546
4089
|
const snapshot = controller.getSnapshot();
|
|
3547
4090
|
const limit = snapshot.host?.maxSubtaskDepth ?? 1;
|
|
3548
|
-
const
|
|
4091
|
+
const tasks = snapshot.tasks;
|
|
4092
|
+
const candidates = (0, react.useMemo)(() => {
|
|
4093
|
+
const lineage = buildLineageIndex(tasks);
|
|
4094
|
+
return tasks.filter((task) => task.archivedAt === void 0 && task.status !== "running" && task.parentId === void 0 && task.id !== parent.id && checkParentLink(tasks, task.id, parent.id, limit, lineage).ok);
|
|
4095
|
+
}, [
|
|
4096
|
+
tasks,
|
|
4097
|
+
parent.id,
|
|
4098
|
+
limit
|
|
4099
|
+
]);
|
|
3549
4100
|
const link = async (taskId) => {
|
|
3550
4101
|
setPending(true);
|
|
3551
4102
|
setError(void 0);
|
|
@@ -3677,15 +4228,8 @@ window.__ModuleLoader__.load({
|
|
|
3677
4228
|
});
|
|
3678
4229
|
}
|
|
3679
4230
|
/** The execution-target editor: workspace / mode / permission pickers. */
|
|
3680
|
-
function ExecutionSettingsSection({ controller, task, pending }) {
|
|
3681
|
-
const
|
|
3682
|
-
const teamRunOffered = () => controller.getSnapshot().host?.teamRunAvailable === true;
|
|
3683
|
-
const [teamRunAvailable, setTeamRunAvailable] = (0, react.useState)(teamRunOffered());
|
|
3684
|
-
(0, react.useEffect)(() => controller.subscribe(() => {
|
|
3685
|
-
const snapshot = controller.getSnapshot();
|
|
3686
|
-
setOptions(snapshot.executionOptions);
|
|
3687
|
-
setTeamRunAvailable(snapshot.host?.teamRunAvailable === true);
|
|
3688
|
-
}), [controller]);
|
|
4231
|
+
function ExecutionSettingsSection({ controller, task, pending, executionOptions, teamRunAvailable }) {
|
|
4232
|
+
const options = executionOptions;
|
|
3689
4233
|
const workspaceId = task.workspaceId ?? "";
|
|
3690
4234
|
const mode = task.mode ?? "";
|
|
3691
4235
|
const permission = task.permission ?? "";
|
|
@@ -3733,7 +4277,7 @@ window.__ModuleLoader__.load({
|
|
|
3733
4277
|
className: board_module_css_default.field,
|
|
3734
4278
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3735
4279
|
className: board_module_css_default.fieldLabel,
|
|
3736
|
-
children: t("new.
|
|
4280
|
+
children: t("new.agentPreset")
|
|
3737
4281
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3738
4282
|
className: board_module_css_default.select,
|
|
3739
4283
|
value: mode,
|
|
@@ -3744,7 +4288,7 @@ window.__ModuleLoader__.load({
|
|
|
3744
4288
|
children: [
|
|
3745
4289
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3746
4290
|
value: "",
|
|
3747
|
-
children:
|
|
4291
|
+
children: inheritPresetLabel(options.presets)
|
|
3748
4292
|
}),
|
|
3749
4293
|
!modeKnown && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3750
4294
|
value: mode,
|
|
@@ -3754,7 +4298,7 @@ window.__ModuleLoader__.load({
|
|
|
3754
4298
|
value: preset.id,
|
|
3755
4299
|
disabled: preset.broken !== void 0,
|
|
3756
4300
|
children: [
|
|
3757
|
-
preset
|
|
4301
|
+
presetLabel(preset),
|
|
3758
4302
|
preset.isDefault ? t("exec.mode.defaultSuffix") : "",
|
|
3759
4303
|
preset.broken !== void 0 ? t("exec.mode.brokenSuffix") : ""
|
|
3760
4304
|
]
|
|
@@ -3826,6 +4370,21 @@ window.__ModuleLoader__.load({
|
|
|
3826
4370
|
className: board_module_css_default.detailText,
|
|
3827
4371
|
children: t("exec.reuseSessionHint")
|
|
3828
4372
|
}),
|
|
4373
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4374
|
+
className: board_module_css_default.scheduleToggle,
|
|
4375
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4376
|
+
type: "checkbox",
|
|
4377
|
+
checked: task.goalRun !== false,
|
|
4378
|
+
disabled: pending,
|
|
4379
|
+
onChange: (event) => {
|
|
4380
|
+
controller.updateTask(task.id, { goalRun: event.target.checked });
|
|
4381
|
+
}
|
|
4382
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("exec.goalRun") })]
|
|
4383
|
+
}),
|
|
4384
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4385
|
+
className: board_module_css_default.detailText,
|
|
4386
|
+
children: t("exec.goalRunHint")
|
|
4387
|
+
}),
|
|
3829
4388
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3830
4389
|
className: board_module_css_default.scheduleToggle,
|
|
3831
4390
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
@@ -3844,7 +4403,7 @@ window.__ModuleLoader__.load({
|
|
|
3844
4403
|
]
|
|
3845
4404
|
});
|
|
3846
4405
|
}
|
|
3847
|
-
/** The scheduled-runs editor: enable toggle, cron input + presets, next-run info. */
|
|
4406
|
+
/** The scheduled-runs editor: enable toggle, cron input + presets, zone, next-run info. */
|
|
3848
4407
|
function ScheduleSection({ controller, task, pending }) {
|
|
3849
4408
|
const schedule = task.schedule;
|
|
3850
4409
|
const [cron, setCron] = (0, react.useState)(schedule?.cron ?? "0 9 * * *");
|
|
@@ -3852,17 +4411,21 @@ window.__ModuleLoader__.load({
|
|
|
3852
4411
|
const [nextRunAt, setNextRunAt] = (0, react.useState)(schedule?.nextRunAt);
|
|
3853
4412
|
const [lastTriggeredAt, setLastTriggeredAt] = (0, react.useState)(schedule?.lastTriggeredAt);
|
|
3854
4413
|
const [error, setError] = (0, react.useState)(void 0);
|
|
3855
|
-
const
|
|
4414
|
+
const hostTimeZone = controller.getSnapshot().host?.scheduler.timeZone;
|
|
4415
|
+
const [zone, setZone] = (0, react.useState)(schedule?.timeZone ?? "");
|
|
4416
|
+
const now = Date.now();
|
|
3856
4417
|
(0, react.useEffect)(() => {
|
|
3857
4418
|
setCron(schedule?.cron ?? "0 9 * * *");
|
|
3858
4419
|
setEnabled(schedule?.enabled ?? false);
|
|
3859
4420
|
setNextRunAt(schedule?.nextRunAt);
|
|
3860
4421
|
setLastTriggeredAt(schedule?.lastTriggeredAt);
|
|
4422
|
+
setZone(schedule?.timeZone ?? "");
|
|
3861
4423
|
setError(void 0);
|
|
3862
4424
|
}, [
|
|
3863
4425
|
task.id,
|
|
3864
4426
|
schedule?.enabled,
|
|
3865
4427
|
schedule?.cron,
|
|
4428
|
+
schedule?.timeZone,
|
|
3866
4429
|
schedule?.nextRunAt,
|
|
3867
4430
|
schedule?.lastTriggeredAt
|
|
3868
4431
|
]);
|
|
@@ -3877,6 +4440,15 @@ window.__ModuleLoader__.load({
|
|
|
3877
4440
|
setError(void 0);
|
|
3878
4441
|
controller.setSchedule(task.id, { cron: trimmed });
|
|
3879
4442
|
};
|
|
4443
|
+
/**
|
|
4444
|
+
* Change the rule's zone. `''` clears the stored zone, which is how a user
|
|
4445
|
+
* returns a rule to following the Host zone.
|
|
4446
|
+
*/
|
|
4447
|
+
const changeZone = (value) => {
|
|
4448
|
+
setZone(value);
|
|
4449
|
+
setError(void 0);
|
|
4450
|
+
controller.setSchedule(task.id, { timeZone: value === "" ? null : value });
|
|
4451
|
+
};
|
|
3880
4452
|
/** Arm/disarm the schedule (arming first persists the edited cron). */
|
|
3881
4453
|
const toggleEnabled = (next) => {
|
|
3882
4454
|
const trimmed = cron.trim();
|
|
@@ -3896,8 +4468,10 @@ window.__ModuleLoader__.load({
|
|
|
3896
4468
|
setError(void 0);
|
|
3897
4469
|
controller.setSchedule(task.id, { cron: preset });
|
|
3898
4470
|
};
|
|
3899
|
-
const
|
|
3900
|
-
const
|
|
4471
|
+
const effectiveZone = zone === "" ? hostTimeZone : zone;
|
|
4472
|
+
const nextLabel = !enabled || nextRunAt === void 0 ? t("detail.schedule.notScheduled") : nextRunAt <= now ? t("detail.schedule.dueSoon") : nextRunLabel(nextRunAt, effectiveZone, formatHostTimestamp, now);
|
|
4473
|
+
const lastLabel = lastTriggeredAt === void 0 ? "—" : formatHostTimestamp(lastTriggeredAt, effectiveZone);
|
|
4474
|
+
const zones = zoneChoices(hostTimeZone, schedule?.timeZone);
|
|
3901
4475
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3902
4476
|
className: board_module_css_default.detailSection,
|
|
3903
4477
|
children: [
|
|
@@ -3949,6 +4523,23 @@ window.__ModuleLoader__.load({
|
|
|
3949
4523
|
}, preset.cron))]
|
|
3950
4524
|
})]
|
|
3951
4525
|
}),
|
|
4526
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4527
|
+
className: board_module_css_default.scheduleZone,
|
|
4528
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("detail.schedule.timeZone") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
4529
|
+
className: board_module_css_default.schedulePreset,
|
|
4530
|
+
value: zone,
|
|
4531
|
+
disabled: pending,
|
|
4532
|
+
"aria-label": t("detail.schedule.timeZone"),
|
|
4533
|
+
title: t("detail.schedule.timeZoneHint"),
|
|
4534
|
+
onChange: (event) => {
|
|
4535
|
+
changeZone(event.target.value);
|
|
4536
|
+
},
|
|
4537
|
+
children: zones.map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4538
|
+
value: choice.id,
|
|
4539
|
+
children: choice.label
|
|
4540
|
+
}, choice.id === "" ? "__host" : choice.id))
|
|
4541
|
+
})]
|
|
4542
|
+
}),
|
|
3952
4543
|
error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3953
4544
|
className: board_module_css_default.formError,
|
|
3954
4545
|
children: error
|
|
@@ -3973,9 +4564,7 @@ window.__ModuleLoader__.load({
|
|
|
3973
4564
|
* and the actions that grow or prune the tree. Every gate here mirrors the
|
|
3974
4565
|
* Host lineage gate for affordance only; the Host re-checks the action.
|
|
3975
4566
|
*/
|
|
3976
|
-
function SubtaskSection({ controller, task, pending, archived }) {
|
|
3977
|
-
const [snapshot, setSnapshot] = (0, react.useState)(controller.getSnapshot());
|
|
3978
|
-
(0, react.useEffect)(() => controller.subscribe(() => setSnapshot(controller.getSnapshot())), [controller]);
|
|
4567
|
+
function SubtaskSection({ controller, task, pending, archived, snapshot }) {
|
|
3979
4568
|
const [showAdd, setShowAdd] = (0, react.useState)(false);
|
|
3980
4569
|
const [showLink, setShowLink] = (0, react.useState)(false);
|
|
3981
4570
|
const tasks = snapshot.tasks;
|
|
@@ -4174,7 +4763,8 @@ window.__ModuleLoader__.load({
|
|
|
4174
4763
|
controller,
|
|
4175
4764
|
task: current,
|
|
4176
4765
|
pending,
|
|
4177
|
-
archived
|
|
4766
|
+
archived,
|
|
4767
|
+
snapshot
|
|
4178
4768
|
}),
|
|
4179
4769
|
current.tags !== void 0 && current.tags.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4180
4770
|
className: board_module_css_default.detailSection,
|
|
@@ -4246,9 +4836,9 @@ window.__ModuleLoader__.load({
|
|
|
4246
4836
|
": ",
|
|
4247
4837
|
current.handover.workspaceId ?? t("exec.workspace.recent"),
|
|
4248
4838
|
" · ",
|
|
4249
|
-
t("new.
|
|
4839
|
+
t("new.agentPreset"),
|
|
4250
4840
|
": ",
|
|
4251
|
-
current.handover.mode ?? t("exec.mode.
|
|
4841
|
+
current.handover.mode ?? t("exec.mode.inherit"),
|
|
4252
4842
|
" · ",
|
|
4253
4843
|
t("new.permission"),
|
|
4254
4844
|
": ",
|
|
@@ -4302,7 +4892,9 @@ window.__ModuleLoader__.load({
|
|
|
4302
4892
|
!archived && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExecutionSettingsSection, {
|
|
4303
4893
|
controller,
|
|
4304
4894
|
task: current,
|
|
4305
|
-
pending
|
|
4895
|
+
pending,
|
|
4896
|
+
executionOptions: snapshot.executionOptions,
|
|
4897
|
+
teamRunAvailable: snapshot.host?.teamRunAvailable === true
|
|
4306
4898
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScheduleSection, {
|
|
4307
4899
|
controller,
|
|
4308
4900
|
task: current,
|
|
@@ -5876,6 +6468,71 @@ window.__ModuleLoader__.load({
|
|
|
5876
6468
|
});
|
|
5877
6469
|
}
|
|
5878
6470
|
//#endregion
|
|
6471
|
+
//#region src/core/degraded-reason.ts
|
|
6472
|
+
/**
|
|
6473
|
+
* The one degraded record naming `plugin`, or undefined when that row is not
|
|
6474
|
+
* degraded (or the payload does not come from the shell's own shape). A
|
|
6475
|
+
* plugin may appear once: the shell keys the ledger by package name and
|
|
6476
|
+
* refreshes the record on every new failure.
|
|
6477
|
+
* @param body - parsed JSON body of the health route.
|
|
6478
|
+
* @param plugin - the real plugin package name to look for.
|
|
6479
|
+
* @returns the degraded row, or undefined when the ledger does not name it.
|
|
6480
|
+
*/
|
|
6481
|
+
function degradedRowOf(body, plugin) {
|
|
6482
|
+
if (typeof body !== "object" || body === null) return void 0;
|
|
6483
|
+
const ledger = body;
|
|
6484
|
+
if (ledger.ok !== true || !Array.isArray(ledger.degraded)) return void 0;
|
|
6485
|
+
for (const entry of ledger.degraded) {
|
|
6486
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
6487
|
+
const record = entry;
|
|
6488
|
+
if (record.plugin !== plugin) continue;
|
|
6489
|
+
const stage = record.stage === "import" || record.stage === "shape" || record.stage === "start" ? record.stage : void 0;
|
|
6490
|
+
if (stage === void 0) continue;
|
|
6491
|
+
return {
|
|
6492
|
+
plugin,
|
|
6493
|
+
stage,
|
|
6494
|
+
reason: typeof record.reason === "string" ? record.reason : ""
|
|
6495
|
+
};
|
|
6496
|
+
}
|
|
6497
|
+
}
|
|
6498
|
+
/** The ledger owner pid a task-board lock reason names, when it names one. */
|
|
6499
|
+
const OWNED_BY_PROCESS_RE = /already owned by process\s+(\d+)/;
|
|
6500
|
+
/**
|
|
6501
|
+
* The marker the ledger appends when it could NOT confirm the recorded PID
|
|
6502
|
+
* belongs to the lock's real owner: the PID was reused by an unrelated process
|
|
6503
|
+
* after a crash, and the reason then carries the manual-removal step instead.
|
|
6504
|
+
* That reason must be rendered verbatim — telling the user to close "the other
|
|
6505
|
+
* DSH instance" would send them after a process that never touched the board.
|
|
6506
|
+
*/
|
|
6507
|
+
const UNCONFIRMED_OWNER_MARKER = "reused after a crash";
|
|
6508
|
+
/**
|
|
6509
|
+
* Extract the pid of the LIVE process a ledger-lock failure blames, when the
|
|
6510
|
+
* reason identifies one. The task board's lock refusal is the one degraded
|
|
6511
|
+
* reason that carries an owner the user can act on, so the panel renders the
|
|
6512
|
+
* pid in its own sentence and adds the guidance that actually applies.
|
|
6513
|
+
* @param reason - the shell's one-line failure reason.
|
|
6514
|
+
* @returns the confirmed owner pid, or undefined when the reason names none or
|
|
6515
|
+
* warns that the PID may have been reused (its own text owns the recovery).
|
|
6516
|
+
*/
|
|
6517
|
+
function ledgerOwnerPid(reason) {
|
|
6518
|
+
if (reason.includes(UNCONFIRMED_OWNER_MARKER)) return void 0;
|
|
6519
|
+
const match = OWNED_BY_PROCESS_RE.exec(reason);
|
|
6520
|
+
if (match === null) return void 0;
|
|
6521
|
+
const pid = Number(match[1]);
|
|
6522
|
+
return Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
6523
|
+
}
|
|
6524
|
+
//#endregion
|
|
6525
|
+
//#region src/core/package-name.ts
|
|
6526
|
+
/**
|
|
6527
|
+
* npm identity of this plugin. The dsh-web-all degraded ledger is keyed by the
|
|
6528
|
+
* real plugin package name of a family row, and this plugin's browser half
|
|
6529
|
+
* must name its OWN row there to learn why its Host API is missing (issue
|
|
6530
|
+
* #1730): the served bundle cannot derive the name, because a family row
|
|
6531
|
+
* mounts the package from a versioned profile path. The host half spells the
|
|
6532
|
+
* same name in its single-mount guard (src/index.ts); the two move together.
|
|
6533
|
+
*/
|
|
6534
|
+
const TASK_BOARD_PACKAGE = "@linxin666/dsh-client-ui-task-board";
|
|
6535
|
+
//#endregion
|
|
5879
6536
|
//#region src/protocol.ts
|
|
5880
6537
|
const TASK_BOARD_API_PREFIX = "/api/task-board";
|
|
5881
6538
|
/** Whether a decoded reply carries the three draft strings the form accepts. */
|
|
@@ -5914,6 +6571,90 @@ window.__ModuleLoader__.load({
|
|
|
5914
6571
|
this.name = "HostApiError";
|
|
5915
6572
|
}
|
|
5916
6573
|
};
|
|
6574
|
+
/**
|
|
6575
|
+
* Same-origin family health route served by the dsh-web-all shell: the
|
|
6576
|
+
* degraded ledger, in which a family row that failed during apply appears WITH
|
|
6577
|
+
* its reason. DOCUMENT-RELATIVE for the same reason as the board's own routes
|
|
6578
|
+
* (issue #1707). A 404 here means the family shell is absent or older than the
|
|
6579
|
+
* route, which is not itself a failure of the board.
|
|
6580
|
+
*/
|
|
6581
|
+
const FAMILY_DEGRADED_ROUTE = "api/dsh-web-all/degraded";
|
|
6582
|
+
/** Health-route ceiling: an unresponsive family shell must not hold the board's first paint. */
|
|
6583
|
+
const DEGRADED_LOOKUP_TIMEOUT_MS = 2e3;
|
|
6584
|
+
/**
|
|
6585
|
+
* The failure text for a row this browser knows failed during start.
|
|
6586
|
+
*
|
|
6587
|
+
* A ledger lock is the one class with an owner the user can act on, so its
|
|
6588
|
+
* reason is unpacked into the pid plus what to do about it; every other
|
|
6589
|
+
* degraded row is reported with the shell's own reason verbatim. The raw
|
|
6590
|
+
* reason rides along either way, because it is the only text that names the
|
|
6591
|
+
* lock file or the recovery step when the pid is not the whole story.
|
|
6592
|
+
* @param reason - the shell's one-line failure reason (never empty).
|
|
6593
|
+
*/
|
|
6594
|
+
function degradedFailureMessage(reason) {
|
|
6595
|
+
const pid = ledgerOwnerPid(reason);
|
|
6596
|
+
if (pid === void 0) return {
|
|
6597
|
+
failure: "degraded",
|
|
6598
|
+
message: t("board.hostError.degraded", { detail: reason })
|
|
6599
|
+
};
|
|
6600
|
+
return {
|
|
6601
|
+
failure: "ledger-locked",
|
|
6602
|
+
message: t("board.hostError.ledgerLocked", {
|
|
6603
|
+
pid: String(pid),
|
|
6604
|
+
detail: reason
|
|
6605
|
+
})
|
|
6606
|
+
};
|
|
6607
|
+
}
|
|
6608
|
+
/**
|
|
6609
|
+
* The degraded record the family shell holds for THIS board, or undefined when
|
|
6610
|
+
* the row is healthy, the shell is absent, or the answer is unusable. The
|
|
6611
|
+
* lookup is best-effort by contract: any uncertainty keeps the caller's own
|
|
6612
|
+
* failure text instead of replacing it with a weaker one.
|
|
6613
|
+
* @param fetchImpl - fetch implementation (injected for tests).
|
|
6614
|
+
* @returns the ledger's reason for this row, or undefined when it names none.
|
|
6615
|
+
*/
|
|
6616
|
+
async function fetchDegradedReason(fetchImpl) {
|
|
6617
|
+
const controller = new AbortController();
|
|
6618
|
+
const timer = globalThis.setTimeout(() => {
|
|
6619
|
+
controller.abort();
|
|
6620
|
+
}, DEGRADED_LOOKUP_TIMEOUT_MS);
|
|
6621
|
+
try {
|
|
6622
|
+
const response = await fetchImpl(FAMILY_DEGRADED_ROUTE, {
|
|
6623
|
+
signal: controller.signal,
|
|
6624
|
+
headers: { accept: "application/json" },
|
|
6625
|
+
cache: "no-store"
|
|
6626
|
+
});
|
|
6627
|
+
if (!response.ok) return void 0;
|
|
6628
|
+
return degradedRowOf(await response.json(), TASK_BOARD_PACKAGE)?.reason;
|
|
6629
|
+
} catch {
|
|
6630
|
+
return;
|
|
6631
|
+
} finally {
|
|
6632
|
+
globalThis.clearTimeout(timer);
|
|
6633
|
+
}
|
|
6634
|
+
}
|
|
6635
|
+
/**
|
|
6636
|
+
* Name the real reason this board's routes are missing (issue #1730).
|
|
6637
|
+
*
|
|
6638
|
+
* The core webserver answers an unmounted route with a bare 404, which cannot
|
|
6639
|
+
* distinguish "this deployment never loaded the plugin" from "this row
|
|
6640
|
+
* degraded during start" — a second DSH process holding `ledger-v2.lock` is
|
|
6641
|
+
* the case that matters, because the panel's advice ("restart the service")
|
|
6642
|
+
* is then false: the lock belongs to the other process, and restarting cannot
|
|
6643
|
+
* win it. The family health route knows the difference, so the 404 is
|
|
6644
|
+
* disambiguated there before anything is shown.
|
|
6645
|
+
* @param fallback - copy for the plain "routes are not there" failure, which
|
|
6646
|
+
* is route-specific (the board's state route and the AI-parse route phrase
|
|
6647
|
+
* their own absence); it is also what is shown when the ledger names nothing.
|
|
6648
|
+
* @param fetchImpl - fetch implementation (injected for tests).
|
|
6649
|
+
* @returns the HostApiError to throw for a missing board route.
|
|
6650
|
+
*/
|
|
6651
|
+
async function missingRouteFailure(fallback = t("board.hostError.notMounted"), fetchImpl = fetch) {
|
|
6652
|
+
const absent = () => new HostApiError("not-mounted", fallback, 404);
|
|
6653
|
+
const reason = await fetchDegradedReason(fetchImpl);
|
|
6654
|
+
if (reason === void 0 || reason === "") return absent();
|
|
6655
|
+
const described = degradedFailureMessage(reason);
|
|
6656
|
+
return new HostApiError(described.failure, described.message, 404);
|
|
6657
|
+
}
|
|
5917
6658
|
function uuid() {
|
|
5918
6659
|
return globalThis.crypto?.randomUUID?.() ?? `browser-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
5919
6660
|
}
|
|
@@ -5938,12 +6679,13 @@ window.__ModuleLoader__.load({
|
|
|
5938
6679
|
return parsed;
|
|
5939
6680
|
}
|
|
5940
6681
|
if (hostError !== void 0) {
|
|
5941
|
-
if (hostError === "forbidden") throw new HostApiError("
|
|
6682
|
+
if (hostError === "forbidden") throw new HostApiError("forbidden", t("board.hostError.forbidden"), response.status);
|
|
5942
6683
|
if (response.status === 503 || /lock/i.test(hostError)) throw new HostApiError("locked", t("board.hostError.locked", { detail: hostError }), response.status);
|
|
5943
6684
|
throw new HostApiError("rejected", hostError, response.status);
|
|
5944
6685
|
}
|
|
5945
|
-
if (response.status === 404) throw
|
|
5946
|
-
if (response.status ===
|
|
6686
|
+
if (response.status === 404) throw await missingRouteFailure();
|
|
6687
|
+
if (response.status === 403) throw new HostApiError("forbidden", t("board.hostError.forbidden"), 403);
|
|
6688
|
+
if (response.status === 401) throw new HostApiError("unauthorized", t("board.hostError.unauthorized"), 401);
|
|
5947
6689
|
throw new HostApiError("unexpected", t("board.hostError.unexpected", { status: String(response.status) }), response.status);
|
|
5948
6690
|
}
|
|
5949
6691
|
var HttpTaskBoardHostTransport = class {
|
|
@@ -6042,8 +6784,9 @@ window.__ModuleLoader__.load({
|
|
|
6042
6784
|
if (record !== void 0 && isTaskParseDraft(record.draft)) return record.draft;
|
|
6043
6785
|
throw new HostApiError("unexpected", t("new.aiParseFailed", { error: t("board.hostError.unexpected", { status: String(response.status) }) }), response.status);
|
|
6044
6786
|
}
|
|
6045
|
-
if (response.status === 404) throw
|
|
6046
|
-
if (response.status ===
|
|
6787
|
+
if (response.status === 404) throw await missingRouteFailure(t("new.aiParseUnavailable"));
|
|
6788
|
+
if (response.status === 403) throw new HostApiError("forbidden", t("board.hostError.forbidden"), 403);
|
|
6789
|
+
if (response.status === 401) throw new HostApiError("unauthorized", t("board.hostError.unauthorized"), 401);
|
|
6047
6790
|
const code = typeof record?.code === "string" ? record.code : void 0;
|
|
6048
6791
|
if (code === "no-model") throw new HostApiError("rejected", t("new.aiParseNoModel"), response.status);
|
|
6049
6792
|
if (code === "timeout") throw new HostApiError("timeout", t("new.aiParseTimeout", { seconds: String(TASK_PARSE_TIMEOUT_SECONDS) }), response.status);
|
|
@@ -6085,7 +6828,7 @@ window.__ModuleLoader__.load({
|
|
|
6085
6828
|
/** The building package's version, when the bundle carries it. */
|
|
6086
6829
|
function bakedVersion() {
|
|
6087
6830
|
try {
|
|
6088
|
-
return "0.4.
|
|
6831
|
+
return "0.4.4";
|
|
6089
6832
|
} catch {
|
|
6090
6833
|
return;
|
|
6091
6834
|
}
|
|
@@ -6407,6 +7150,44 @@ window.__ModuleLoader__.load({
|
|
|
6407
7150
|
"layout"
|
|
6408
7151
|
];
|
|
6409
7152
|
/**
|
|
7153
|
+
* Read the agent-preset roster through whichever face the running host
|
|
7154
|
+
* serves: the generated api-remotes face (`remote.agentPresets`,
|
|
7155
|
+
* 0.1.2-alpha.2) or the connection RPC face
|
|
7156
|
+
* (`connection.api.agentPresets`, hosts below that cohort). Answers
|
|
7157
|
+
* undefined when the host serves neither, so the caller leaves the picker
|
|
7158
|
+
* options untouched instead of erroring.
|
|
7159
|
+
*/
|
|
7160
|
+
async function readPresetRoster(ctx, remote) {
|
|
7161
|
+
let remotes;
|
|
7162
|
+
try {
|
|
7163
|
+
remotes = remote.agentPresets;
|
|
7164
|
+
} catch {
|
|
7165
|
+
remotes = void 0;
|
|
7166
|
+
}
|
|
7167
|
+
if (remotes !== void 0) {
|
|
7168
|
+
const response = await remotes.list();
|
|
7169
|
+
if (!response.ok) return {
|
|
7170
|
+
ok: false,
|
|
7171
|
+
presets: []
|
|
7172
|
+
};
|
|
7173
|
+
return {
|
|
7174
|
+
ok: true,
|
|
7175
|
+
presets: response.value.presets
|
|
7176
|
+
};
|
|
7177
|
+
}
|
|
7178
|
+
const legacy = ctx.get("connection").api?.agentPresets;
|
|
7179
|
+
if (legacy === void 0) return void 0;
|
|
7180
|
+
const response = await legacy.list({});
|
|
7181
|
+
if (!response.result.ok || response.result.value === void 0) return {
|
|
7182
|
+
ok: false,
|
|
7183
|
+
presets: []
|
|
7184
|
+
};
|
|
7185
|
+
return {
|
|
7186
|
+
ok: true,
|
|
7187
|
+
presets: response.result.value.presets ?? []
|
|
7188
|
+
};
|
|
7189
|
+
}
|
|
7190
|
+
/**
|
|
6410
7191
|
* Mount the task board.
|
|
6411
7192
|
* @param ctx - client root context (services: sessions, workspaces).
|
|
6412
7193
|
*/
|
|
@@ -6472,6 +7253,21 @@ window.__ModuleLoader__.load({
|
|
|
6472
7253
|
controller.setWorkspaceCreator(async (path) => {
|
|
6473
7254
|
return { workspaceId: (await workspaces.create({ path })).workspaceId };
|
|
6474
7255
|
});
|
|
7256
|
+
const pushPresetOptions = async () => {
|
|
7257
|
+
try {
|
|
7258
|
+
const roster = await readPresetRoster(ctx, remote);
|
|
7259
|
+
if (roster === void 0 || !roster.ok) return;
|
|
7260
|
+
controller.setExecutionOptions({ presets: roster.presets.map((preset) => ({
|
|
7261
|
+
id: preset.id,
|
|
7262
|
+
name: preset.name,
|
|
7263
|
+
description: preset.description,
|
|
7264
|
+
broken: preset.broken,
|
|
7265
|
+
isDefault: preset.isDefault
|
|
7266
|
+
})) });
|
|
7267
|
+
} catch (error) {
|
|
7268
|
+
console.error("[dsh-task-board] agent preset roster read failed", error);
|
|
7269
|
+
}
|
|
7270
|
+
};
|
|
6475
7271
|
const pushModelOptions = async () => {
|
|
6476
7272
|
try {
|
|
6477
7273
|
let models = [];
|
|
@@ -6527,8 +7323,10 @@ window.__ModuleLoader__.load({
|
|
|
6527
7323
|
console.error("[dsh-task-board] model options read failed", error);
|
|
6528
7324
|
}
|
|
6529
7325
|
};
|
|
7326
|
+
pushPresetOptions();
|
|
6530
7327
|
pushModelOptions();
|
|
6531
7328
|
disposers.push(ctx.on("connection/reset", () => {
|
|
7329
|
+
pushPresetOptions();
|
|
6532
7330
|
pushModelOptions();
|
|
6533
7331
|
}));
|
|
6534
7332
|
try {
|