@linxin666/dsh-client-ui-task-board 0.1.17 → 0.1.18
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 +3 -3
- package/README.zh.md +3 -3
- package/lib/client.js +377 -115
- package/lib/client.js.map +1 -1
- package/lib/types/client/PluginSettingsCard.d.ts +11 -0
- package/lib/types/client/locales.d.ts +7 -0
- package/lib/types/client/schedule-presets.d.ts +10 -0
- package/lib/types/core/controller.d.ts +27 -0
- package/lib/types/core/execution.d.ts +16 -0
- package/lib/types/core/scheduler.d.ts +7 -0
- package/lib/types/core/tasks.d.ts +16 -0
- package/lib/types/core/use-cases/task-archive.d.ts +22 -0
- package/lib/types/core/use-cases/task-create.d.ts +1 -6
- package/package.json +1 -1
- package/src/client/PluginSettingsCard.tsx +50 -47
- package/src/client/board/NewTaskModal.tsx +68 -0
- package/src/client/board/TaskBoard.tsx +48 -19
- package/src/client/board/TaskDetail.tsx +27 -8
- package/src/client/index.ts +3 -0
- package/src/client/locales.ts +14 -0
- package/src/client/schedule-presets.ts +13 -0
- package/src/client/settings-card.module.css +9 -0
- package/src/core/controller.ts +54 -0
- package/src/core/execution.ts +41 -2
- package/src/core/scheduler.ts +15 -0
- package/src/core/store.ts +1 -0
- package/src/core/tasks.ts +15 -0
- package/src/core/use-cases/task-archive.ts +48 -0
- package/src/core/use-cases/task-create.ts +12 -3
package/lib/client.js
CHANGED
|
@@ -9,6 +9,8 @@ window.__ModuleLoader__.load({
|
|
|
9
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
10
|
let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
|
|
11
11
|
//#region src/core/tasks.ts
|
|
12
|
+
/** Statuses a settled task may be archived from. */
|
|
13
|
+
const ARCHIVABLE_STATUSES = ["done", "failed"];
|
|
12
14
|
/** Permission presets a task may pin on its execution session (the `/permission <id>` ids). */
|
|
13
15
|
const TASK_PERMISSIONS = [
|
|
14
16
|
"read-only",
|
|
@@ -166,44 +168,42 @@ window.__ModuleLoader__.load({
|
|
|
166
168
|
return "running";
|
|
167
169
|
}
|
|
168
170
|
//#endregion
|
|
169
|
-
//#region src/core/use-cases/task-
|
|
171
|
+
//#region src/core/use-cases/task-archive.ts
|
|
170
172
|
/**
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
173
|
+
* Archive one task: only settled statuses (done/failed) can be archived;
|
|
174
|
+
* a running or not-yet-settled task stays on the board (its runner still
|
|
175
|
+
* owns its lifecycle). Already-archived tasks are a no-op.
|
|
174
176
|
*/
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
* appended ledger, or the unchanged ledger when the title is blank.
|
|
178
|
-
* @param tasks - current ledger.
|
|
179
|
-
* @param input - raw user input (title/description/prompt).
|
|
180
|
-
* @param now - clock instant (ms epoch).
|
|
181
|
-
* @param id - minted task id.
|
|
182
|
-
*/
|
|
183
|
-
function applyCreateTask(tasks, input, now, id) {
|
|
184
|
-
if (input.title.trim() === "") return {
|
|
185
|
-
task: void 0,
|
|
186
|
-
tasks
|
|
187
|
-
};
|
|
188
|
-
const task = createTask(input, now, id);
|
|
177
|
+
function applyArchiveTask(tasks, id, now) {
|
|
178
|
+
let applied = false;
|
|
189
179
|
return {
|
|
190
|
-
task
|
|
191
|
-
|
|
180
|
+
tasks: tasks.map((task) => {
|
|
181
|
+
if (task.id !== id || task.archivedAt !== void 0) return task;
|
|
182
|
+
if (!ARCHIVABLE_STATUSES.includes(task.status)) return task;
|
|
183
|
+
applied = true;
|
|
184
|
+
return {
|
|
185
|
+
...task,
|
|
186
|
+
archivedAt: now,
|
|
187
|
+
updatedAt: now
|
|
188
|
+
};
|
|
189
|
+
}),
|
|
190
|
+
archived: applied
|
|
192
191
|
};
|
|
193
192
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
* Apply a delete across the ledger. The selection (a task id) is cleared when
|
|
198
|
-
* it matches the removed task, so the UI never lingers on a vanished detail.
|
|
199
|
-
* @param tasks - current ledger.
|
|
200
|
-
* @param selectedTaskId - the currently selected task id (may be undefined).
|
|
201
|
-
* @param id - the task to remove.
|
|
202
|
-
*/
|
|
203
|
-
function applyDeleteTask(tasks, selectedTaskId, id) {
|
|
193
|
+
/** Restore one task back onto the main board (clears the archive marker). */
|
|
194
|
+
function applyRestoreTask(tasks, id, now) {
|
|
195
|
+
let applied = false;
|
|
204
196
|
return {
|
|
205
|
-
tasks: tasks.
|
|
206
|
-
|
|
197
|
+
tasks: tasks.map((task) => {
|
|
198
|
+
if (task.id !== id || task.archivedAt === void 0) return task;
|
|
199
|
+
applied = true;
|
|
200
|
+
const { archivedAt: _archived, ...rest } = task;
|
|
201
|
+
return {
|
|
202
|
+
...rest,
|
|
203
|
+
updatedAt: now
|
|
204
|
+
};
|
|
205
|
+
}),
|
|
206
|
+
archived: applied
|
|
207
207
|
};
|
|
208
208
|
}
|
|
209
209
|
//#endregion
|
|
@@ -308,6 +308,56 @@ window.__ModuleLoader__.load({
|
|
|
308
308
|
return /^\d+$/.test(value);
|
|
309
309
|
}
|
|
310
310
|
//#endregion
|
|
311
|
+
//#region src/core/use-cases/task-create.ts
|
|
312
|
+
/**
|
|
313
|
+
* Create-task use case: mint a new task from user input, rejecting a blank
|
|
314
|
+
* title. Pure ledger transition (no persistence or notify — the controller
|
|
315
|
+
* orchestrates those), so it is unit-testable without any runtime face.
|
|
316
|
+
*/
|
|
317
|
+
/**
|
|
318
|
+
* Apply a create against the current ledger. Returns the new task and the
|
|
319
|
+
* appended ledger, or the unchanged ledger when the title is blank.
|
|
320
|
+
* @param tasks - current ledger.
|
|
321
|
+
* @param input - raw user input (title/description/prompt + optional schedule).
|
|
322
|
+
* @param now - clock instant (ms epoch).
|
|
323
|
+
* @param id - minted task id.
|
|
324
|
+
*/
|
|
325
|
+
function applyCreateTask(tasks, input, now, id) {
|
|
326
|
+
if (input.title.trim() === "") return {
|
|
327
|
+
task: void 0,
|
|
328
|
+
tasks
|
|
329
|
+
};
|
|
330
|
+
let task = createTask(input, now, id);
|
|
331
|
+
const requested = input.schedule;
|
|
332
|
+
if (requested?.enabled === true && requested.cron.trim() !== "" && isValidCron(requested.cron)) {
|
|
333
|
+
const cron = requested.cron.trim();
|
|
334
|
+
task = withSchedule(task, {
|
|
335
|
+
enabled: true,
|
|
336
|
+
cron,
|
|
337
|
+
nextRunAt: nextRunAtMs(cron, now)
|
|
338
|
+
}, now);
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
task,
|
|
342
|
+
tasks: [...tasks, task]
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
//#endregion
|
|
346
|
+
//#region src/core/use-cases/task-delete.ts
|
|
347
|
+
/**
|
|
348
|
+
* Apply a delete across the ledger. The selection (a task id) is cleared when
|
|
349
|
+
* it matches the removed task, so the UI never lingers on a vanished detail.
|
|
350
|
+
* @param tasks - current ledger.
|
|
351
|
+
* @param selectedTaskId - the currently selected task id (may be undefined).
|
|
352
|
+
* @param id - the task to remove.
|
|
353
|
+
*/
|
|
354
|
+
function applyDeleteTask(tasks, selectedTaskId, id) {
|
|
355
|
+
return {
|
|
356
|
+
tasks: tasks.filter((task) => task.id !== id),
|
|
357
|
+
selectionCleared: selectedTaskId === id
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
//#endregion
|
|
311
361
|
//#region src/core/use-cases/task-schedule.ts
|
|
312
362
|
/**
|
|
313
363
|
* Schedule use case: arm/disarm a task's cron rule and roll a rule forward.
|
|
@@ -436,6 +486,7 @@ window.__ModuleLoader__.load({
|
|
|
436
486
|
deps;
|
|
437
487
|
tasks = [];
|
|
438
488
|
boardOpen = false;
|
|
489
|
+
archiveView = false;
|
|
439
490
|
selectedTaskId;
|
|
440
491
|
executionOptions = {
|
|
441
492
|
workspaces: [],
|
|
@@ -476,6 +527,7 @@ window.__ModuleLoader__.load({
|
|
|
476
527
|
return {
|
|
477
528
|
tasks: this.tasks,
|
|
478
529
|
boardOpen: this.boardOpen,
|
|
530
|
+
archiveView: this.archiveView,
|
|
479
531
|
selectedTaskId: this.selectedTaskId,
|
|
480
532
|
executionOptions: this.executionOptions
|
|
481
533
|
};
|
|
@@ -501,6 +553,18 @@ window.__ModuleLoader__.load({
|
|
|
501
553
|
if (this.boardOpen) this.closeBoard();
|
|
502
554
|
else this.openBoard();
|
|
503
555
|
}
|
|
556
|
+
/**
|
|
557
|
+
* Switch between the kanban columns and the archive view. Leaving the
|
|
558
|
+
* archive view with an archived task still selected closes the selection —
|
|
559
|
+
* the detail overlay must not linger over a task that is off-board.
|
|
560
|
+
*/
|
|
561
|
+
toggleArchiveView() {
|
|
562
|
+
this.archiveView = !this.archiveView;
|
|
563
|
+
if (!this.archiveView && this.selectedTaskId !== void 0) {
|
|
564
|
+
if (this.tasks.find((task) => task.id === this.selectedTaskId)?.archivedAt !== void 0) this.selectedTaskId = void 0;
|
|
565
|
+
}
|
|
566
|
+
this.notify();
|
|
567
|
+
}
|
|
504
568
|
openTask(id) {
|
|
505
569
|
if (this.tasks.some((task) => task.id === id)) {
|
|
506
570
|
this.selectedTaskId = id;
|
|
@@ -545,6 +609,27 @@ window.__ModuleLoader__.load({
|
|
|
545
609
|
this.persistAndNotify();
|
|
546
610
|
}
|
|
547
611
|
/**
|
|
612
|
+
* Archive a settled task (done/failed). Running or on-board-unsettled
|
|
613
|
+
* tasks are refused so the runner keeps exclusive ownership of their
|
|
614
|
+
* lifecycle.
|
|
615
|
+
* @returns true when applied.
|
|
616
|
+
*/
|
|
617
|
+
archiveTask(id) {
|
|
618
|
+
const { tasks, archived } = applyArchiveTask(this.tasks, id, this.now());
|
|
619
|
+
if (!archived) return false;
|
|
620
|
+
this.tasks = [...tasks];
|
|
621
|
+
this.persistAndNotify();
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
/** Restore an archived task back onto the board (same status column). */
|
|
625
|
+
restoreTask(id) {
|
|
626
|
+
const { tasks, archived } = applyRestoreTask(this.tasks, id, this.now());
|
|
627
|
+
if (!archived) return false;
|
|
628
|
+
this.tasks = [...tasks];
|
|
629
|
+
this.persistAndNotify();
|
|
630
|
+
return true;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
548
633
|
* Update a task's schedule rule. A blank or invalid cron expression is
|
|
549
634
|
* rejected (returns false, state untouched). When the rule ends up enabled
|
|
550
635
|
* the next run instant is computed immediately; a disabled rule carries no
|
|
@@ -571,6 +656,17 @@ window.__ModuleLoader__.load({
|
|
|
571
656
|
this.persistAndNotify();
|
|
572
657
|
}
|
|
573
658
|
/**
|
|
659
|
+
* Reload the ledger from the persisted store without notifying subscribers.
|
|
660
|
+
* The scheduler calls this before every tick so a task deleted in another
|
|
661
|
+
* tab (or a stale in-memory copy) can never be fired from this tab: the
|
|
662
|
+
* fire decision and the subsequent roll-forward both run on the freshest
|
|
663
|
+
* persisted truth. Deliberately silent — same-origin external changes still
|
|
664
|
+
* reach subscribers through the storage-event subscription.
|
|
665
|
+
*/
|
|
666
|
+
reloadFromStore() {
|
|
667
|
+
this.tasks = this.deps.store.load();
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
574
670
|
* Jump to an execution's session transcript. Selecting the session changes
|
|
575
671
|
* `current`, which closes the board (the conversation view takes over).
|
|
576
672
|
* @param sessionId - the execution session to open.
|
|
@@ -729,6 +825,15 @@ window.__ModuleLoader__.load({
|
|
|
729
825
|
*/
|
|
730
826
|
var ExecutionService = class {
|
|
731
827
|
env;
|
|
828
|
+
/**
|
|
829
|
+
* In-flight preset switches keyed by `sessionId\u0000mode`. The session
|
|
830
|
+
* list mirror can lag behind a just-applied switch, so concurrent runs
|
|
831
|
+
* against one blank session would otherwise each issue the same select
|
|
832
|
+
* RPC (an amplified storm); sharing one in-flight call keeps the wire to
|
|
833
|
+
* a single request. The settled entry is forgotten so a later run can
|
|
834
|
+
* switch again.
|
|
835
|
+
*/
|
|
836
|
+
presetSwitches = /* @__PURE__ */ new Map();
|
|
732
837
|
/** @param env - the runtime faces (real or fake). */
|
|
733
838
|
constructor(env) {
|
|
734
839
|
this.env = env;
|
|
@@ -785,12 +890,13 @@ window.__ModuleLoader__.load({
|
|
|
785
890
|
return false;
|
|
786
891
|
}
|
|
787
892
|
if (summary?.agentPreset === mode) return true;
|
|
788
|
-
|
|
893
|
+
const presets = this.env.presets;
|
|
894
|
+
if (presets === void 0) {
|
|
789
895
|
settleFailed(`this deployment does not support agent presets (task asks for ${mode})`);
|
|
790
896
|
return false;
|
|
791
897
|
}
|
|
792
898
|
try {
|
|
793
|
-
const result = await this.
|
|
899
|
+
const result = await this.switchPreset(presets, sessionId, mode);
|
|
794
900
|
if (!result.ok) {
|
|
795
901
|
if (presetAlreadyRuns(result.error, mode)) {
|
|
796
902
|
this.env.sessions.noteAgentPreset?.(sessionId, mode);
|
|
@@ -807,6 +913,22 @@ window.__ModuleLoader__.load({
|
|
|
807
913
|
return true;
|
|
808
914
|
}
|
|
809
915
|
/**
|
|
916
|
+
* One in-flight `select` per (session, preset): concurrent runs against
|
|
917
|
+
* the same blank session share the same wire call instead of each issuing
|
|
918
|
+
* a duplicate RPC. The entry is removed once the call settles, so a later
|
|
919
|
+
* run (after a shared failure, say) issues a fresh switch.
|
|
920
|
+
*/
|
|
921
|
+
switchPreset(presets, sessionId, mode) {
|
|
922
|
+
const key = `${sessionId}\u0000${mode}`;
|
|
923
|
+
const inflight = this.presetSwitches.get(key);
|
|
924
|
+
if (inflight !== void 0) return inflight;
|
|
925
|
+
const attempt = presets.select(sessionId, mode).finally(() => {
|
|
926
|
+
if (this.presetSwitches.get(key) === attempt) this.presetSwitches.delete(key);
|
|
927
|
+
});
|
|
928
|
+
this.presetSwitches.set(key, attempt);
|
|
929
|
+
return attempt;
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
810
932
|
* Apply the task-pinned permission preset through the `/permission <id>`
|
|
811
933
|
* slash command. No-op when the task pins none; fails the run when the
|
|
812
934
|
* admission is rejected or no command claimed the line.
|
|
@@ -969,6 +1091,11 @@ window.__ModuleLoader__.load({
|
|
|
969
1091
|
* a task still running at its due instant is skipped by the controller's
|
|
970
1092
|
* runTask guard and simply waits for the next cron match.
|
|
971
1093
|
*
|
|
1094
|
+
* A deleted task can never fire again from a stale in-memory copy: every
|
|
1095
|
+
* tick first re-reads the persisted ledger through the optional `refresh`
|
|
1096
|
+
* hook (the controller reloads its store), so the fire decision and the
|
|
1097
|
+
* subsequent roll-forward both run on the freshest persisted truth.
|
|
1098
|
+
*
|
|
972
1099
|
* The ticker is controlled: `start` arms a single interval guarded by an
|
|
973
1100
|
* idempotence check (a second start while running is a no-op, so wiring can
|
|
974
1101
|
* never leak a duplicate timer), and `stop`/`dispose` clear the timer and
|
|
@@ -1043,6 +1170,7 @@ window.__ModuleLoader__.load({
|
|
|
1043
1170
|
async tick() {
|
|
1044
1171
|
if (this.disposed) return;
|
|
1045
1172
|
if (this.deps.ready !== void 0 && !this.deps.ready()) return;
|
|
1173
|
+
this.deps.refresh?.();
|
|
1046
1174
|
const now = this.deps.now();
|
|
1047
1175
|
for (const task of this.deps.tasks()) {
|
|
1048
1176
|
const schedule = task.schedule;
|
|
@@ -1161,6 +1289,7 @@ window.__ModuleLoader__.load({
|
|
|
1161
1289
|
task.schedule = normalizeSchedule(row.schedule);
|
|
1162
1290
|
task.workspaceId = normalizeTargetId(row.workspaceId);
|
|
1163
1291
|
task.mode = normalizeTargetId(row.mode);
|
|
1292
|
+
task.archivedAt = typeof row.archivedAt === "number" && Number.isFinite(row.archivedAt) ? row.archivedAt : void 0;
|
|
1164
1293
|
task.permission = isTaskPermission(row.permission) ? row.permission : void 0;
|
|
1165
1294
|
tasks.push(task);
|
|
1166
1295
|
}
|
|
@@ -1260,6 +1389,10 @@ window.__ModuleLoader__.load({
|
|
|
1260
1389
|
"board.search": "筛选任务…",
|
|
1261
1390
|
"board.empty": "这个状态还没有任务",
|
|
1262
1391
|
"board.filterAll": "全部",
|
|
1392
|
+
"board.archive": "归档",
|
|
1393
|
+
"board.archiveView": "归档 ({count})",
|
|
1394
|
+
"board.backToBoard": "返回看板",
|
|
1395
|
+
"archive.empty": "没有已归档的任务",
|
|
1263
1396
|
"board.status": "状态",
|
|
1264
1397
|
"board.status.backlog": "待规划",
|
|
1265
1398
|
"board.status.todo": "待办",
|
|
@@ -1287,6 +1420,9 @@ window.__ModuleLoader__.load({
|
|
|
1287
1420
|
"detail.run": "执行",
|
|
1288
1421
|
"detail.rerun": "重新执行",
|
|
1289
1422
|
"detail.delete": "删除",
|
|
1423
|
+
"detail.archive": "归档",
|
|
1424
|
+
"detail.restore": "恢复",
|
|
1425
|
+
"detail.archivedAt": "已归档 · {time}",
|
|
1290
1426
|
"detail.viewSession": "查看会话",
|
|
1291
1427
|
"detail.noSession": "暂无会话",
|
|
1292
1428
|
"detail.executionStarted": "已启动",
|
|
@@ -1364,6 +1500,10 @@ window.__ModuleLoader__.load({
|
|
|
1364
1500
|
"board.search": "Filter tasks…",
|
|
1365
1501
|
"board.empty": "No tasks in this column",
|
|
1366
1502
|
"board.filterAll": "All",
|
|
1503
|
+
"board.archive": "Archive",
|
|
1504
|
+
"board.archiveView": "Archived ({count})",
|
|
1505
|
+
"board.backToBoard": "Back to board",
|
|
1506
|
+
"archive.empty": "No archived tasks",
|
|
1367
1507
|
"board.status": "Status",
|
|
1368
1508
|
"board.status.backlog": "Backlog",
|
|
1369
1509
|
"board.status.todo": "To Do",
|
|
@@ -1391,6 +1531,9 @@ window.__ModuleLoader__.load({
|
|
|
1391
1531
|
"detail.run": "Run",
|
|
1392
1532
|
"detail.rerun": "Run Again",
|
|
1393
1533
|
"detail.delete": "Delete",
|
|
1534
|
+
"detail.archive": "Archive",
|
|
1535
|
+
"detail.restore": "Restore",
|
|
1536
|
+
"detail.archivedAt": "Archived · {time}",
|
|
1394
1537
|
"detail.viewSession": "View Session",
|
|
1395
1538
|
"detail.noSession": "No session",
|
|
1396
1539
|
"detail.executionStarted": "Started",
|
|
@@ -1546,6 +1689,27 @@ window.__ModuleLoader__.load({
|
|
|
1546
1689
|
"statusDot": "_7D6uKa_statusDot"
|
|
1547
1690
|
};
|
|
1548
1691
|
//#endregion
|
|
1692
|
+
//#region src/client/schedule-presets.ts
|
|
1693
|
+
/** Common scheduled-run presets (cron → locale label). */
|
|
1694
|
+
const SCHEDULE_PRESETS = [
|
|
1695
|
+
{
|
|
1696
|
+
cron: "0 9 * * *",
|
|
1697
|
+
label: "detail.schedule.preset.daily9"
|
|
1698
|
+
},
|
|
1699
|
+
{
|
|
1700
|
+
cron: "0 * * * *",
|
|
1701
|
+
label: "detail.schedule.preset.hourly"
|
|
1702
|
+
},
|
|
1703
|
+
{
|
|
1704
|
+
cron: "*/10 * * * *",
|
|
1705
|
+
label: "detail.schedule.preset.tenMin"
|
|
1706
|
+
},
|
|
1707
|
+
{
|
|
1708
|
+
cron: "0 9 * * 1",
|
|
1709
|
+
label: "detail.schedule.preset.weeklyMon9"
|
|
1710
|
+
}
|
|
1711
|
+
];
|
|
1712
|
+
//#endregion
|
|
1549
1713
|
//#region src/client/board/NewTaskModal.tsx
|
|
1550
1714
|
/**
|
|
1551
1715
|
* New-task modal: title + description + the prompt that execution will send.
|
|
@@ -1559,23 +1723,39 @@ window.__ModuleLoader__.load({
|
|
|
1559
1723
|
const [workspaceId, setWorkspaceId] = (0, react.useState)("");
|
|
1560
1724
|
const [mode, setMode] = (0, react.useState)("");
|
|
1561
1725
|
const [permission, setPermission] = (0, react.useState)("");
|
|
1726
|
+
const [scheduleEnabled, setScheduleEnabled] = (0, react.useState)(false);
|
|
1727
|
+
const [scheduleCron, setScheduleCron] = (0, react.useState)("");
|
|
1728
|
+
const [scheduleError, setScheduleError] = (0, react.useState)(void 0);
|
|
1562
1729
|
const [error, setError] = (0, react.useState)(void 0);
|
|
1563
1730
|
const [options, setOptions] = (0, react.useState)(controller.getSnapshot().executionOptions);
|
|
1564
1731
|
(0, react.useEffect)(() => controller.subscribe(() => setOptions(controller.getSnapshot().executionOptions)), [controller]);
|
|
1565
1732
|
const submit = () => {
|
|
1733
|
+
if (scheduleEnabled) {
|
|
1734
|
+
const cron = scheduleCron.trim();
|
|
1735
|
+
if (cron === "" || !isValidCron(cron)) {
|
|
1736
|
+
setScheduleError(t("detail.schedule.invalid"));
|
|
1737
|
+
return;
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1566
1740
|
if (controller.createTask({
|
|
1567
1741
|
title,
|
|
1568
1742
|
description,
|
|
1569
1743
|
prompt,
|
|
1570
1744
|
workspaceId: workspaceId === "" ? void 0 : workspaceId,
|
|
1571
1745
|
mode: mode === "" ? void 0 : mode,
|
|
1572
|
-
permission: permission === "" ? void 0 : permission
|
|
1746
|
+
permission: permission === "" ? void 0 : permission,
|
|
1747
|
+
schedule: scheduleEnabled ? {
|
|
1748
|
+
enabled: true,
|
|
1749
|
+
cron: scheduleCron.trim()
|
|
1750
|
+
} : void 0
|
|
1573
1751
|
}) === void 0) {
|
|
1574
1752
|
setError(t("new.required"));
|
|
1575
1753
|
return;
|
|
1576
1754
|
}
|
|
1577
1755
|
onClose();
|
|
1578
1756
|
};
|
|
1757
|
+
/** Next-run preview for a valid armed cron (creation-time only). */
|
|
1758
|
+
const scheduleNextRun = scheduleEnabled && scheduleCron.trim() !== "" && isValidCron(scheduleCron) ? nextRunAtMs(scheduleCron, Date.now()) : void 0;
|
|
1579
1759
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1580
1760
|
className: board_module_css_default.modalBackdrop,
|
|
1581
1761
|
onMouseDown: (event) => {
|
|
@@ -1705,6 +1885,67 @@ window.__ModuleLoader__.load({
|
|
|
1705
1885
|
}, id))]
|
|
1706
1886
|
})]
|
|
1707
1887
|
}),
|
|
1888
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1889
|
+
className: board_module_css_default.detailSection,
|
|
1890
|
+
children: [
|
|
1891
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("detail.schedule") }),
|
|
1892
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1893
|
+
className: board_module_css_default.scheduleToggle,
|
|
1894
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1895
|
+
type: "checkbox",
|
|
1896
|
+
checked: scheduleEnabled,
|
|
1897
|
+
onChange: (event) => {
|
|
1898
|
+
setScheduleEnabled(event.target.checked);
|
|
1899
|
+
if (!event.target.checked) setScheduleError(void 0);
|
|
1900
|
+
}
|
|
1901
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("detail.schedule.enable") })]
|
|
1902
|
+
}),
|
|
1903
|
+
scheduleEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1904
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1905
|
+
className: board_module_css_default.scheduleRow,
|
|
1906
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1907
|
+
className: `${board_module_css_default.input} ${board_module_css_default.scheduleInput}${scheduleError !== void 0 ? ` ${board_module_css_default.scheduleInputInvalid}` : ""}`,
|
|
1908
|
+
value: scheduleCron,
|
|
1909
|
+
placeholder: "0 9 * * *",
|
|
1910
|
+
spellCheck: false,
|
|
1911
|
+
"aria-label": t("detail.schedule.cron"),
|
|
1912
|
+
onChange: (event) => {
|
|
1913
|
+
setScheduleCron(event.target.value);
|
|
1914
|
+
setScheduleError(void 0);
|
|
1915
|
+
}
|
|
1916
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1917
|
+
className: board_module_css_default.schedulePreset,
|
|
1918
|
+
value: "",
|
|
1919
|
+
"aria-label": t("detail.schedule.presets"),
|
|
1920
|
+
onChange: (event) => {
|
|
1921
|
+
if (event.target.value === "") return;
|
|
1922
|
+
setScheduleCron(event.target.value);
|
|
1923
|
+
setScheduleError(void 0);
|
|
1924
|
+
},
|
|
1925
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
1926
|
+
value: "",
|
|
1927
|
+
children: [t("detail.schedule.presets"), "…"]
|
|
1928
|
+
}), SCHEDULE_PRESETS.map((preset) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1929
|
+
value: preset.cron,
|
|
1930
|
+
children: t(preset.label)
|
|
1931
|
+
}, preset.cron))]
|
|
1932
|
+
})]
|
|
1933
|
+
}),
|
|
1934
|
+
scheduleError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1935
|
+
className: board_module_css_default.formError,
|
|
1936
|
+
children: scheduleError
|
|
1937
|
+
}),
|
|
1938
|
+
scheduleError === void 0 && scheduleNextRun !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1939
|
+
className: board_module_css_default.scheduleMeta,
|
|
1940
|
+
children: [
|
|
1941
|
+
t("detail.schedule.nextRun"),
|
|
1942
|
+
" ",
|
|
1943
|
+
new Date(scheduleNextRun).toLocaleString()
|
|
1944
|
+
]
|
|
1945
|
+
})
|
|
1946
|
+
] })
|
|
1947
|
+
]
|
|
1948
|
+
}),
|
|
1708
1949
|
error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1709
1950
|
className: board_module_css_default.formError,
|
|
1710
1951
|
children: error
|
|
@@ -1912,25 +2153,6 @@ window.__ModuleLoader__.load({
|
|
|
1912
2153
|
]
|
|
1913
2154
|
});
|
|
1914
2155
|
}
|
|
1915
|
-
/** Common scheduled-run presets (cron → locale label). */
|
|
1916
|
-
const SCHEDULE_PRESETS = [
|
|
1917
|
-
{
|
|
1918
|
-
cron: "0 9 * * *",
|
|
1919
|
-
label: "detail.schedule.preset.daily9"
|
|
1920
|
-
},
|
|
1921
|
-
{
|
|
1922
|
-
cron: "0 * * * *",
|
|
1923
|
-
label: "detail.schedule.preset.hourly"
|
|
1924
|
-
},
|
|
1925
|
-
{
|
|
1926
|
-
cron: "*/10 * * * *",
|
|
1927
|
-
label: "detail.schedule.preset.tenMin"
|
|
1928
|
-
},
|
|
1929
|
-
{
|
|
1930
|
-
cron: "0 9 * * 1",
|
|
1931
|
-
label: "detail.schedule.preset.weeklyMon9"
|
|
1932
|
-
}
|
|
1933
|
-
];
|
|
1934
2156
|
/** The execution-target editor: workspace / mode / permission pickers. */
|
|
1935
2157
|
function ExecutionSettingsSection({ controller, task }) {
|
|
1936
2158
|
const [options, setOptions] = (0, react.useState)(controller.getSnapshot().executionOptions);
|
|
@@ -2260,6 +2482,23 @@ window.__ModuleLoader__.load({
|
|
|
2260
2482
|
},
|
|
2261
2483
|
children: current.executions.length === 0 ? t("detail.run") : t("detail.rerun")
|
|
2262
2484
|
}),
|
|
2485
|
+
current.archivedAt !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2486
|
+
type: "button",
|
|
2487
|
+
className: board_module_css_default.primaryButton,
|
|
2488
|
+
onClick: () => {
|
|
2489
|
+
controller.restoreTask(current.id);
|
|
2490
|
+
controller.closeTask();
|
|
2491
|
+
},
|
|
2492
|
+
children: t("detail.restore")
|
|
2493
|
+
}) : (current.status === "done" || current.status === "failed") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2494
|
+
type: "button",
|
|
2495
|
+
className: board_module_css_default.ghostButton,
|
|
2496
|
+
onClick: () => {
|
|
2497
|
+
controller.archiveTask(current.id);
|
|
2498
|
+
controller.closeTask();
|
|
2499
|
+
},
|
|
2500
|
+
children: t("detail.archive")
|
|
2501
|
+
}),
|
|
2263
2502
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2264
2503
|
type: "button",
|
|
2265
2504
|
className: board_module_css_default.dangerButton,
|
|
@@ -2273,7 +2512,8 @@ window.__ModuleLoader__.load({
|
|
|
2273
2512
|
children: [
|
|
2274
2513
|
t("board.created"),
|
|
2275
2514
|
" ",
|
|
2276
|
-
formatTime(current.createdAt)
|
|
2515
|
+
formatTime(current.createdAt),
|
|
2516
|
+
current.archivedAt !== void 0 && ` · ${t("detail.archivedAt", { time: formatTime(current.archivedAt) })}`
|
|
2277
2517
|
]
|
|
2278
2518
|
})
|
|
2279
2519
|
]
|
|
@@ -2337,7 +2577,8 @@ window.__ModuleLoader__.load({
|
|
|
2337
2577
|
const [filter, setFilter] = (0, react.useState)("");
|
|
2338
2578
|
const [showNew, setShowNew] = (0, react.useState)(false);
|
|
2339
2579
|
const selected = selectedTaskOf(snapshot);
|
|
2340
|
-
const
|
|
2580
|
+
const archiveView = snapshot.archiveView;
|
|
2581
|
+
const visible = snapshot.tasks.filter((task) => (archiveView ? task.archivedAt !== void 0 : task.archivedAt === void 0) && matchesFilter(task, filter));
|
|
2341
2582
|
const openTask = (0, react.useCallback)((id) => {
|
|
2342
2583
|
controller.openTask(id);
|
|
2343
2584
|
}, [controller]);
|
|
@@ -2362,6 +2603,14 @@ window.__ModuleLoader__.load({
|
|
|
2362
2603
|
},
|
|
2363
2604
|
"aria-label": t("board.search")
|
|
2364
2605
|
}),
|
|
2606
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2607
|
+
type: "button",
|
|
2608
|
+
className: archiveView ? board_module_css_default.primaryButton : board_module_css_default.ghostButton,
|
|
2609
|
+
onClick: () => {
|
|
2610
|
+
controller.toggleArchiveView();
|
|
2611
|
+
},
|
|
2612
|
+
children: archiveView ? t("board.backToBoard") : t("board.archiveView", { count: String(snapshot.tasks.filter((task) => task.archivedAt !== void 0).length) })
|
|
2613
|
+
}),
|
|
2365
2614
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2366
2615
|
type: "button",
|
|
2367
2616
|
className: board_module_css_default.primaryButton,
|
|
@@ -2382,7 +2631,29 @@ window.__ModuleLoader__.load({
|
|
|
2382
2631
|
}),
|
|
2383
2632
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2384
2633
|
className: board_module_css_default.columns,
|
|
2385
|
-
children:
|
|
2634
|
+
children: archiveView ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2635
|
+
className: board_module_css_default.column,
|
|
2636
|
+
"data-status": "archived",
|
|
2637
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
2638
|
+
className: board_module_css_default.columnHeader,
|
|
2639
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
2640
|
+
className: board_module_css_default.columnTitle,
|
|
2641
|
+
children: t("board.archive")
|
|
2642
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2643
|
+
className: board_module_css_default.columnCount,
|
|
2644
|
+
children: visible.length
|
|
2645
|
+
})]
|
|
2646
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2647
|
+
className: board_module_css_default.cards,
|
|
2648
|
+
children: [visible.map((task) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoTaskCard, {
|
|
2649
|
+
task,
|
|
2650
|
+
onOpen: openTask
|
|
2651
|
+
}, task.id)), visible.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2652
|
+
className: board_module_css_default.columnEmpty,
|
|
2653
|
+
children: t("archive.empty")
|
|
2654
|
+
})]
|
|
2655
|
+
})]
|
|
2656
|
+
}) : COLUMNS.map((column) => {
|
|
2386
2657
|
const tasks = visible.filter((task) => task.status === column.status);
|
|
2387
2658
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2388
2659
|
className: board_module_css_default.column,
|
|
@@ -2622,7 +2893,7 @@ window.__ModuleLoader__.load({
|
|
|
2622
2893
|
}
|
|
2623
2894
|
//#endregion
|
|
2624
2895
|
//#region \0dsh-css:packages/dsh-task-board/src/client/settings-card.module.css.mjs
|
|
2625
|
-
const css = ".Jh0q7G_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.Jh0q7G_card:hover{border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.Jh0q7G_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.Jh0q7G_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.Jh0q7G_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.Jh0q7G_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.Jh0q7G_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Jh0q7G_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.Jh0q7G_chevronOpen{transform:rotate(180deg)}.Jh0q7G_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.Jh0q7G_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.Jh0q7G_notExposed{color:var(--dsw-alias-state-warn-primary);margin:12px 0 0;font-size:12px;line-height:1.5}.Jh0q7G_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.Jh0q7G_failed{min-width:0;color:var(--dsw-alias-label-error);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:12px;line-height:1.5;overflow:hidden}.Jh0q7G_discard,.Jh0q7G_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.Jh0q7G_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.Jh0q7G_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.Jh0q7G_discard:disabled,.Jh0q7G_save:disabled{opacity:.4;cursor:default}.Jh0q7G_discard:focus-visible,.Jh0q7G_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.Jh0q7G_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.Jh0q7G_field+.Jh0q7G_field{border-top:1px solid var(--dsw-alias-border-l2)}.Jh0q7G_head{align-items:center;gap:8px;display:flex}.Jh0q7G_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.Jh0q7G_badges{align-items:center;gap:8px;display:inline-flex}.Jh0q7G_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Jh0q7G_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.Jh0q7G_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.Jh0q7G_reset:disabled{cursor:default}.Jh0q7G_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.Jh0q7G_input,.Jh0q7G_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Jh0q7G_input:focus-visible,.Jh0q7G_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.Jh0q7G_input:disabled,.Jh0q7G_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.Jh0q7G_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Jh0q7G_inputInvalid:focus-visible{outline:2px solid var(--dsw-alias-label-error);outline-offset:1px;border-color:var(--dsw-alias-label-error)}.Jh0q7G_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.Jh0q7G_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){.Jh0q7G_card,.Jh0q7G_header,.Jh0q7G_chevron,.Jh0q7G_chevronOpen,.Jh0q7G_discard,.Jh0q7G_save{transition:none}}";
|
|
2896
|
+
const css = ".Jh0q7G_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.Jh0q7G_card:hover{border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.Jh0q7G_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.Jh0q7G_headerStatic{border-radius:12px;align-items:center;gap:12px;width:100%;padding:14px 16px;display:flex}.Jh0q7G_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.Jh0q7G_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.Jh0q7G_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.Jh0q7G_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Jh0q7G_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.Jh0q7G_chevronOpen{transform:rotate(180deg)}.Jh0q7G_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.Jh0q7G_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.Jh0q7G_notExposed{color:var(--dsw-alias-state-warn-primary);margin:12px 0 0;font-size:12px;line-height:1.5}.Jh0q7G_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.Jh0q7G_failed{min-width:0;color:var(--dsw-alias-label-error);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:12px;line-height:1.5;overflow:hidden}.Jh0q7G_discard,.Jh0q7G_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.Jh0q7G_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.Jh0q7G_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.Jh0q7G_discard:disabled,.Jh0q7G_save:disabled{opacity:.4;cursor:default}.Jh0q7G_discard:focus-visible,.Jh0q7G_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.Jh0q7G_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.Jh0q7G_field+.Jh0q7G_field{border-top:1px solid var(--dsw-alias-border-l2)}.Jh0q7G_head{align-items:center;gap:8px;display:flex}.Jh0q7G_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.Jh0q7G_badges{align-items:center;gap:8px;display:inline-flex}.Jh0q7G_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Jh0q7G_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.Jh0q7G_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.Jh0q7G_reset:disabled{cursor:default}.Jh0q7G_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.Jh0q7G_input,.Jh0q7G_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Jh0q7G_input:focus-visible,.Jh0q7G_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.Jh0q7G_input:disabled,.Jh0q7G_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.Jh0q7G_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Jh0q7G_inputInvalid:focus-visible{outline:2px solid var(--dsw-alias-label-error);outline-offset:1px;border-color:var(--dsw-alias-label-error)}.Jh0q7G_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.Jh0q7G_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){.Jh0q7G_card,.Jh0q7G_header,.Jh0q7G_chevron,.Jh0q7G_chevronOpen,.Jh0q7G_discard,.Jh0q7G_save{transition:none}}";
|
|
2626
2897
|
const tagId = "@linxin666/dsh-client-ui-task-board/settings-card.module.css";
|
|
2627
2898
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
2628
2899
|
const tag = document.createElement("style");
|
|
@@ -2647,6 +2918,7 @@ window.__ModuleLoader__.load({
|
|
|
2647
2918
|
"head": "Jh0q7G_head",
|
|
2648
2919
|
"headText": "Jh0q7G_headText",
|
|
2649
2920
|
"header": "Jh0q7G_header",
|
|
2921
|
+
"headerStatic": "Jh0q7G_headerStatic",
|
|
2650
2922
|
"hint": "Jh0q7G_hint",
|
|
2651
2923
|
"input": "Jh0q7G_input",
|
|
2652
2924
|
"inputInvalid": "Jh0q7G_inputInvalid",
|
|
@@ -2676,24 +2948,42 @@ window.__ModuleLoader__.load({
|
|
|
2676
2948
|
* @returns the card, or nothing while the namespace is still loading.
|
|
2677
2949
|
*/
|
|
2678
2950
|
function PluginSettingsCard(props) {
|
|
2679
|
-
const [open, setOpen] = (0, react.useState)(
|
|
2680
|
-
const { state } = props;
|
|
2951
|
+
const [open, setOpen] = (0, react.useState)(props.defaultOpen ?? true);
|
|
2952
|
+
const { state, alwaysOpen } = props;
|
|
2681
2953
|
if (!state.available) return null;
|
|
2682
2954
|
const title = props.t(props.titleKey);
|
|
2683
2955
|
const description = props.t(props.descriptionKey);
|
|
2684
2956
|
const blocked = !state.dirty || state.invalid || state.saving;
|
|
2685
|
-
const
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
className: settings_card_module_css_default.
|
|
2691
|
-
"
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
},
|
|
2696
|
-
|
|
2957
|
+
const expanded = alwaysOpen === true || open;
|
|
2958
|
+
const cardClass = expanded ? `${settings_card_module_css_default.cardOpen} ${settings_card_module_css_default.card}` : settings_card_module_css_default.card;
|
|
2959
|
+
const header = alwaysOpen === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2960
|
+
className: settings_card_module_css_default.headerStatic,
|
|
2961
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2962
|
+
className: settings_card_module_css_default.headText,
|
|
2963
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2964
|
+
className: settings_card_module_css_default.name,
|
|
2965
|
+
title,
|
|
2966
|
+
children: title
|
|
2967
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2968
|
+
className: settings_card_module_css_default.description,
|
|
2969
|
+
title: description,
|
|
2970
|
+
children: description
|
|
2971
|
+
})]
|
|
2972
|
+
}), state.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2973
|
+
className: settings_card_module_css_default.pending,
|
|
2974
|
+
title: props.t("settings.unsaved"),
|
|
2975
|
+
children: props.t("settings.unsaved")
|
|
2976
|
+
}) : null]
|
|
2977
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2978
|
+
type: "button",
|
|
2979
|
+
className: settings_card_module_css_default.header,
|
|
2980
|
+
"aria-expanded": open,
|
|
2981
|
+
"aria-label": `${props.t(open ? "settings.collapse" : "settings.expand")}: ${title}`,
|
|
2982
|
+
onClick: () => {
|
|
2983
|
+
setOpen(!open);
|
|
2984
|
+
},
|
|
2985
|
+
children: [
|
|
2986
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2697
2987
|
className: settings_card_module_css_default.headText,
|
|
2698
2988
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2699
2989
|
className: settings_card_module_css_default.name,
|
|
@@ -2704,7 +2994,13 @@ window.__ModuleLoader__.load({
|
|
|
2704
2994
|
title: description,
|
|
2705
2995
|
children: description
|
|
2706
2996
|
})]
|
|
2707
|
-
}),
|
|
2997
|
+
}),
|
|
2998
|
+
state.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2999
|
+
className: settings_card_module_css_default.pending,
|
|
3000
|
+
title: props.t("settings.unsaved"),
|
|
3001
|
+
children: props.t("settings.unsaved")
|
|
3002
|
+
}) : null,
|
|
3003
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
2708
3004
|
width: "14",
|
|
2709
3005
|
height: "14",
|
|
2710
3006
|
viewBox: "0 0 14 14",
|
|
@@ -2715,8 +3011,12 @@ window.__ModuleLoader__.load({
|
|
|
2715
3011
|
d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z",
|
|
2716
3012
|
fill: "currentColor"
|
|
2717
3013
|
})
|
|
2718
|
-
})
|
|
2719
|
-
|
|
3014
|
+
})
|
|
3015
|
+
]
|
|
3016
|
+
});
|
|
3017
|
+
if (!state.exposed) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
3018
|
+
className: cardClass,
|
|
3019
|
+
children: [header, expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2720
3020
|
className: settings_card_module_css_default.body,
|
|
2721
3021
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2722
3022
|
className: settings_card_module_css_default.notExposed,
|
|
@@ -2727,46 +3027,7 @@ window.__ModuleLoader__.load({
|
|
|
2727
3027
|
});
|
|
2728
3028
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
2729
3029
|
className: cardClass,
|
|
2730
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
2731
|
-
type: "button",
|
|
2732
|
-
className: settings_card_module_css_default.header,
|
|
2733
|
-
"aria-expanded": open,
|
|
2734
|
-
"aria-label": `${props.t(open ? "settings.collapse" : "settings.expand")}: ${title}`,
|
|
2735
|
-
onClick: () => {
|
|
2736
|
-
setOpen(!open);
|
|
2737
|
-
},
|
|
2738
|
-
children: [
|
|
2739
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2740
|
-
className: settings_card_module_css_default.headText,
|
|
2741
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2742
|
-
className: settings_card_module_css_default.name,
|
|
2743
|
-
title,
|
|
2744
|
-
children: title
|
|
2745
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2746
|
-
className: settings_card_module_css_default.description,
|
|
2747
|
-
title: description,
|
|
2748
|
-
children: description
|
|
2749
|
-
})]
|
|
2750
|
-
}),
|
|
2751
|
-
state.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2752
|
-
className: settings_card_module_css_default.pending,
|
|
2753
|
-
title: props.t("settings.unsaved"),
|
|
2754
|
-
children: props.t("settings.unsaved")
|
|
2755
|
-
}) : null,
|
|
2756
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
2757
|
-
width: "14",
|
|
2758
|
-
height: "14",
|
|
2759
|
-
viewBox: "0 0 14 14",
|
|
2760
|
-
fill: "none",
|
|
2761
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
2762
|
-
className: open ? `${settings_card_module_css_default.chevron} ${settings_card_module_css_default.chevronOpen}` : settings_card_module_css_default.chevron,
|
|
2763
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
2764
|
-
d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z",
|
|
2765
|
-
fill: "currentColor"
|
|
2766
|
-
})
|
|
2767
|
-
})
|
|
2768
|
-
]
|
|
2769
|
-
}), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3030
|
+
children: [header, expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2770
3031
|
className: settings_card_module_css_default.body,
|
|
2771
3032
|
children: [
|
|
2772
3033
|
!state.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -3291,6 +3552,7 @@ window.__ModuleLoader__.load({
|
|
|
3291
3552
|
controller.start();
|
|
3292
3553
|
const scheduler = new SchedulerService({
|
|
3293
3554
|
tasks: () => controller.getSnapshot().tasks,
|
|
3555
|
+
refresh: () => controller.reloadFromStore(),
|
|
3294
3556
|
now: () => Date.now(),
|
|
3295
3557
|
runTask: (id) => controller.runTask(id),
|
|
3296
3558
|
applySchedule: (id, nextRunAt, lastTriggeredAt) => controller.applyScheduleNextRun(id, nextRunAt, lastTriggeredAt),
|