@linxin666/dsh-client-ui-task-board 0.4.4 → 0.4.6

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.
Files changed (120) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +59 -20
  3. package/README.zh.md +59 -20
  4. package/lib/client.js +6208 -624
  5. package/lib/client.js.map +1 -1
  6. package/lib/index.js +4774 -493
  7. package/lib/types/client/PluginSettingsCard.d.ts +7 -0
  8. package/lib/types/client/TaskBoardSettingsCard.d.ts +31 -3
  9. package/lib/types/client/board/ClampedMarkdown.d.ts +3 -0
  10. package/lib/types/client/board/ConfirmDialog.d.ts +4 -1
  11. package/lib/types/client/board/EditTaskModal.d.ts +5 -2
  12. package/lib/types/client/board/LinkSubtaskModal.d.ts +5 -2
  13. package/lib/types/client/board/NewTaskModal.d.ts +4 -1
  14. package/lib/types/client/board/TagManagerModal.d.ts +26 -0
  15. package/lib/types/client/board/TaskBoard.d.ts +10 -1
  16. package/lib/types/client/board/TaskCard.d.ts +14 -1
  17. package/lib/types/client/board/TaskDetail.d.ts +3 -1
  18. package/lib/types/client/board/TaskForm.d.ts +4 -1
  19. package/lib/types/client/board/VerificationReport.d.ts +23 -0
  20. package/lib/types/client/board/card-view.d.ts +113 -0
  21. package/lib/types/client/board/icons.d.ts +32 -0
  22. package/lib/types/client/board/overlay.d.ts +69 -0
  23. package/lib/types/client/board/status-key.d.ts +15 -0
  24. package/lib/types/client/board/task-markdown.d.ts +19 -0
  25. package/lib/types/client/index.d.ts +23 -5
  26. package/lib/types/client/locales.d.ts +168 -1
  27. package/lib/types/client/native-panel.d.ts +6 -20
  28. package/lib/types/client/plugin-card-seat.d.ts +5 -0
  29. package/lib/types/client/schedule-zone.d.ts +50 -0
  30. package/lib/types/client/seats.d.ts +18 -0
  31. package/lib/types/client/service.d.ts +32 -0
  32. package/lib/types/client/settings-form.d.ts +16 -0
  33. package/lib/types/core/controller.d.ts +46 -11
  34. package/lib/types/core/extension.d.ts +283 -0
  35. package/lib/types/core/handover.d.ts +13 -1
  36. package/lib/types/core/issue-form.d.ts +16 -0
  37. package/lib/types/core/poll-cadence.d.ts +31 -0
  38. package/lib/types/core/tasks.d.ts +228 -17
  39. package/lib/types/core/use-cases/task-schedule.d.ts +68 -10
  40. package/lib/types/core/use-cases/task-tag.d.ts +48 -0
  41. package/lib/types/core/use-cases/task-update.d.ts +5 -3
  42. package/lib/types/core/verification-budget.d.ts +52 -0
  43. package/lib/types/core/verification.d.ts +753 -0
  44. package/lib/types/core/workspace-target.d.ts +41 -0
  45. package/lib/types/host/agent-tools.d.ts +1 -1
  46. package/lib/types/host/extension-registry.d.ts +111 -0
  47. package/lib/types/host/llm-dispatch.d.ts +63 -0
  48. package/lib/types/host/verification-gate.d.ts +95 -0
  49. package/lib/types/host/verification-runner.d.ts +139 -0
  50. package/lib/types/host/workspace-evidence.d.ts +53 -0
  51. package/lib/types/host-ledger.d.ts +133 -14
  52. package/lib/types/host-runner.d.ts +55 -10
  53. package/lib/types/host-service.d.ts +205 -11
  54. package/lib/types/index.d.ts +134 -26
  55. package/lib/types/protocol.d.ts +92 -10
  56. package/lib/types/tool-surface.d.ts +71 -0
  57. package/package.json +28 -31
  58. package/src/client/PluginSettingsCard.tsx +9 -0
  59. package/src/client/TaskBoardSettingsCard.tsx +378 -66
  60. package/src/client/board/ClampedMarkdown.tsx +69 -0
  61. package/src/client/board/ConfirmDialog.tsx +7 -3
  62. package/src/client/board/EditTaskModal.tsx +5 -2
  63. package/src/client/board/LinkSubtaskModal.tsx +12 -7
  64. package/src/client/board/NewTaskModal.tsx +168 -21
  65. package/src/client/board/TagManagerModal.tsx +235 -0
  66. package/src/client/board/TaskBoard.tsx +259 -103
  67. package/src/client/board/TaskCard.tsx +214 -81
  68. package/src/client/board/TaskDetail.tsx +289 -43
  69. package/src/client/board/TaskForm.tsx +13 -3
  70. package/src/client/board/VerificationReport.tsx +322 -0
  71. package/src/client/board/card-view.ts +220 -0
  72. package/src/client/board/icons.tsx +89 -0
  73. package/src/client/board/overlay.tsx +189 -0
  74. package/src/client/board/status-key.ts +30 -0
  75. package/src/client/board/task-markdown.module.css +137 -0
  76. package/src/client/board/task-markdown.tsx +106 -0
  77. package/src/client/board-settings.module.css +66 -0
  78. package/src/client/board.module.css +488 -63
  79. package/src/client/index.ts +82 -51
  80. package/src/client/locales.ts +343 -6
  81. package/src/client/native-panel.tsx +18 -2
  82. package/src/client/plugin-card-seat.ts +7 -0
  83. package/src/client/schedule-zone.ts +100 -2
  84. package/src/client/seats.tsx +58 -0
  85. package/src/client/service.ts +94 -0
  86. package/src/client/settings-card.module.css +31 -5
  87. package/src/client/settings-form.ts +27 -1
  88. package/src/core/controller.ts +105 -10
  89. package/src/core/extension.ts +376 -0
  90. package/src/core/freeze-snapshot.ts +2 -2
  91. package/src/core/handover.ts +16 -1
  92. package/src/core/issue-form.ts +19 -0
  93. package/src/core/poll-cadence.ts +41 -0
  94. package/src/core/store.ts +80 -15
  95. package/src/core/tasks.ts +293 -45
  96. package/src/core/use-cases/task-create.ts +30 -9
  97. package/src/core/use-cases/task-parent.ts +3 -2
  98. package/src/core/use-cases/task-schedule.ts +190 -25
  99. package/src/core/use-cases/task-tag.ts +110 -0
  100. package/src/core/use-cases/task-update.ts +13 -7
  101. package/src/core/verification-budget.ts +115 -0
  102. package/src/core/verification.ts +1594 -0
  103. package/src/core/workspace-target.ts +59 -0
  104. package/src/host/agent-tools.ts +125 -38
  105. package/src/host/extension-registry.ts +509 -0
  106. package/src/host/llm-dispatch.ts +87 -0
  107. package/src/host/verification-gate.ts +487 -0
  108. package/src/host/verification-runner.ts +702 -0
  109. package/src/host/workspace-evidence.ts +176 -0
  110. package/src/host-ai.ts +15 -9
  111. package/src/host-ledger.ts +468 -71
  112. package/src/host-routes.ts +15 -2
  113. package/src/host-runner.ts +106 -24
  114. package/src/host-service.ts +546 -40
  115. package/src/index.ts +305 -21
  116. package/src/protocol.ts +226 -28
  117. package/src/tool-surface.ts +95 -0
  118. package/lib/invariant.js +0 -6
  119. package/lib/types/invariant.d.ts +0 -3
  120. package/src/invariant.ts +0 -4
@@ -25,7 +25,6 @@ export declare const zh: {
25
25
  'board.status.failed': string;
26
26
  'board.runs': string;
27
27
  'board.pending': string;
28
- 'board.updated': string;
29
28
  'board.created': string;
30
29
  'board.hostError': string;
31
30
  'board.hostError.notMounted': string;
@@ -106,6 +105,22 @@ export declare const zh: {
106
105
  'board.tagFilter': string;
107
106
  'board.tagFilterClear': string;
108
107
  'board.tagEmpty': string;
108
+ 'board.tagManage': string;
109
+ 'tags.title': string;
110
+ 'tags.hint': string;
111
+ 'tags.empty': string;
112
+ 'tags.usage': string;
113
+ 'tags.rename': string;
114
+ 'tags.renameLabel': string;
115
+ 'tags.delete': string;
116
+ 'tags.deleteTitle': string;
117
+ 'tags.deleteConfirm': string;
118
+ 'tags.deleteOk': string;
119
+ 'tags.save': string;
120
+ 'tags.mergeHint': string;
121
+ 'tags.renameFailed': string;
122
+ 'tags.deleteFailed': string;
123
+ 'tags.done': string;
109
124
  'detail.permissionPending': string;
110
125
  'detail.permissionConfirm': string;
111
126
  'detail.permissionConfirmed': string;
@@ -121,6 +136,8 @@ export declare const zh: {
121
136
  'new.archiveOriginal': string;
122
137
  'detail.prompt': string;
123
138
  'detail.description': string;
139
+ 'detail.expand': string;
140
+ 'detail.collapse': string;
124
141
  'detail.execution': string;
125
142
  'detail.noExecution': string;
126
143
  'detail.run': string;
@@ -133,6 +150,7 @@ export declare const zh: {
133
150
  'detail.noSession': string;
134
151
  'detail.executionStarted': string;
135
152
  'detail.execution.initiator': string;
153
+ 'detail.execution.external': string;
136
154
  'detail.executionEnded': string;
137
155
  'detail.result.succeeded': string;
138
156
  'detail.result.failed': string;
@@ -144,6 +162,9 @@ export declare const zh: {
144
162
  'delete.cancel': string;
145
163
  'status.move.backlog': string;
146
164
  'status.move.todo': string;
165
+ 'status.move.running': string;
166
+ 'status.move.done': string;
167
+ 'status.move.failed': string;
147
168
  'exec.error.noWorkspace': string;
148
169
  'exec.error.promptRejected': string;
149
170
  'run.failed': string;
@@ -172,7 +193,34 @@ export declare const zh: {
172
193
  'detail.schedule.duration.hours': string;
173
194
  'detail.schedule.duration.minutes': string;
174
195
  'detail.schedule.duration.seconds': string;
196
+ 'detail.schedule.mode': string;
197
+ 'detail.schedule.mode.cron': string;
198
+ 'detail.schedule.mode.once': string;
199
+ 'detail.schedule.at': string;
200
+ 'detail.schedule.atHint': string;
201
+ 'detail.schedule.atInvalid': string;
202
+ 'detail.schedule.plannedAt': string;
203
+ 'detail.schedule.zoneOnceHint': string;
204
+ 'detail.schedule.maxRuns': string;
205
+ 'detail.schedule.maxRunsUnlimited': string;
206
+ 'detail.schedule.maxRunsValue': string;
207
+ 'detail.schedule.maxRunsCustom': string;
208
+ 'detail.schedule.maxRunsInvalid': string;
209
+ 'detail.schedule.runs': string;
210
+ 'detail.schedule.runsUnlimited': string;
211
+ 'detail.schedule.remaining': string;
212
+ 'detail.schedule.cancel': string;
213
+ 'detail.schedule.ended': string;
214
+ 'detail.schedule.skipped': string;
215
+ 'detail.schedule.stop.fired': string;
216
+ 'detail.schedule.stop.limit': string;
217
+ 'detail.schedule.stop.no-target': string;
218
+ 'detail.schedule.stop.missed': string;
219
+ 'detail.schedule.stop.busy': string;
220
+ 'detail.schedule.stop.permission': string;
221
+ 'detail.schedule.stop.launch-failed': string;
175
222
  'card.scheduled': string;
223
+ 'card.scheduledOnce': string;
176
224
  'new.workspace': string;
177
225
  'new.agentPreset': string;
178
226
  'new.permission': string;
@@ -203,11 +251,14 @@ export declare const zh: {
203
251
  'exec.reuseSessionHint': string;
204
252
  'exec.goalRun': string;
205
253
  'exec.goalRunHint': string;
254
+ 'exec.skipVerification': string;
255
+ 'exec.skipVerificationHint': string;
206
256
  'detail.executionSettings': string;
207
257
  'exec.hint': string;
208
258
  'settings.title': string;
209
259
  'settings.description': string;
210
260
  'settings.enabled': string;
261
+ 'settings.enabledCardHint': string;
211
262
  'settings.enabledHint': string;
212
263
  'settings.announceToAgent': string;
213
264
  'settings.announceToAgentHint': string;
@@ -232,9 +283,105 @@ export declare const zh: {
232
283
  'settings.unsaved': string;
233
284
  'settings.saveFailed': string;
234
285
  'settings.invalidNumber': string;
286
+ 'settings.sessionPollSeconds': string;
287
+ 'settings.sessionPollSecondsHint': string;
235
288
  'settings.maxSubtaskDepth': string;
236
289
  'settings.maxSubtaskDepthHint': string;
237
290
  'settings.maxSubtaskDepthOption': string;
291
+ 'settings.runBehaviorTitle': string;
292
+ 'settings.runBehaviorCardHint': string;
293
+ 'settings.goalRunGlobal': string;
294
+ 'settings.goalRunGlobalHint': string;
295
+ 'settings.goalRunGlobalDisabledTaskOption': string;
296
+ 'settings.goalVerificationTitle': string;
297
+ 'settings.goalVerification': string;
298
+ 'settings.goalVerificationCardHint': string;
299
+ 'settings.goalVerificationHint': string;
300
+ 'settings.goalVerificationModel': string;
301
+ 'settings.goalVerificationModelHint': string;
302
+ 'settings.goalVerificationModelInherit': string;
303
+ 'settings.goalVerificationModelInheritUnknown': string;
304
+ 'settings.goalVerificationEffort': string;
305
+ 'settings.goalVerificationEffortHint': string;
306
+ 'settings.goalVerificationEffortInherit': string;
307
+ 'settings.goalVerificationEffortInheritUnknown': string;
308
+ 'settings.goalVerificationResolved': string;
309
+ 'settings.goalVerificationResolvedModel': string;
310
+ 'settings.goalVerificationResolvedEffort': string;
311
+ 'settings.goalVerificationResolvedNoEffort': string;
312
+ 'settings.goalVerificationResolvedSource': string;
313
+ 'settings.goalVerificationSourceInherit': string;
314
+ 'settings.goalVerificationSourceExplicit': string;
315
+ 'settings.goalVerificationResolvedPreset': string;
316
+ 'settings.goalVerificationRouteMissing': string;
317
+ 'settings.goalVerificationEffortFallback': string;
318
+ 'settings.goalVerificationEffortFallbackNone': string;
319
+ 'settings.goalVerificationModelInvalid': string;
320
+ 'running.executing': string;
321
+ 'running.verifying': string;
322
+ 'running.repairing': string;
323
+ 'running.verificationPassed': string;
324
+ 'running.verificationFailed': string;
325
+ 'running.verificationInvalid': string;
326
+ 'verify.title': string;
327
+ 'verify.status.passed': string;
328
+ 'verify.status.failed': string;
329
+ 'verify.status.exception': string;
330
+ 'verify.status.verifying': string;
331
+ 'verify.status.pending': string;
332
+ 'verify.status.off': string;
333
+ 'verify.status.invalid': string;
334
+ 'verify.status.invalidHeld': string;
335
+ 'verify.attemptInvalid': string;
336
+ 'verify.invalidReason.missing-finding': string;
337
+ 'verify.invalidReason.unlocatable-quote': string;
338
+ 'verify.invalidReason.baseline-finding': string;
339
+ 'verify.invalidReason.vacuous-finding': string;
340
+ 'verify.invalidReason.insufficient-evidence': string;
341
+ 'verify.invalidReason.unknown': string;
342
+ 'verify.invalidCounts': string;
343
+ 'verify.invalidDetail': string;
344
+ 'verify.criterionFindings': string;
345
+ 'verify.criterionFinding': string;
346
+ 'verify.criterionFindingFull': string;
347
+ 'verify.location.task': string;
348
+ 'verify.location.trajectory': string;
349
+ 'verify.location.workspace': string;
350
+ 'verify.location.baseline': string;
351
+ 'verify.location.unknown': string;
352
+ 'verify.detailRemoved': string;
353
+ 'verify.cleanup.cleaned': string;
354
+ 'verify.cleanup.pending': string;
355
+ 'verify.cleanup.failed': string;
356
+ 'verify.cleanup.audit': string;
357
+ 'verify.cleanup.stage.quality': string;
358
+ 'verify.cleanup.stage.invalid': string;
359
+ 'verify.cleanup.stage.exception': string;
360
+ 'verify.cleanup.stage.budget': string;
361
+ 'verify.summary': string;
362
+ 'verify.criteria': string;
363
+ 'verify.criterion': string;
364
+ 'verify.rounds': string;
365
+ 'verify.judge': string;
366
+ 'verify.judgeNoEffort': string;
367
+ 'verify.judgeInherited': string;
368
+ 'verify.attempt': string;
369
+ 'verify.attemptException': string;
370
+ 'verify.counts': string;
371
+ 'verify.findings': string;
372
+ 'verify.noFindings': string;
373
+ 'verify.evidence': string;
374
+ 'verify.workspaceEvidence': string;
375
+ 'verify.usage': string;
376
+ 'verify.usageIncomplete': string;
377
+ 'verify.finalFailure': string;
378
+ 'verify.applicability.disabled': string;
379
+ 'verify.applicability.goalUnavailable': string;
380
+ 'verify.applicability.skipped': string;
381
+ 'verify.applicability.teamMember': string;
382
+ 'verify.applicability.goalDisabled': string;
383
+ 'verify.effortFallback': string;
384
+ 'verify.thresholdValue': string;
238
385
  'detail.parent': string;
239
386
  'detail.parent.open': string;
240
387
  'detail.subtasks': string;
@@ -258,6 +405,25 @@ export declare const zh: {
258
405
  'card.subtasksFailed': string;
259
406
  'card.subtasksRunning': string;
260
407
  'card.subtasksBreakdown': string;
408
+ 'card.result.succeeded': string;
409
+ 'card.result.failed': string;
410
+ 'card.result.cancelled': string;
411
+ 'card.declared': string;
412
+ 'card.declaredHint': string;
413
+ 'card.openSession': string;
414
+ 'card.tagsLabel': string;
415
+ 'card.time.created': string;
416
+ 'card.time.started': string;
417
+ 'card.time.settled': string;
418
+ 'card.time.archived': string;
419
+ 'card.time.updated': string;
420
+ 'board.density.compact': string;
421
+ 'board.density.comfortable': string;
422
+ 'board.group.today': string;
423
+ 'board.group.week': string;
424
+ 'board.group.earlier': string;
425
+ 'board.group.expand': string;
426
+ 'board.group.collapse': string;
261
427
  'board.hideSubtasks': string;
262
428
  'board.showSubtasks': string;
263
429
  'board.subtaskFilterHint': string;
@@ -265,6 +431,7 @@ export declare const zh: {
265
431
  /** en dictionary, complete against the zh key set. */
266
432
  export declare const en: Record<keyof typeof zh, string>;
267
433
  /** The dictionary key union. */
434
+ /** The dictionary key union. */
268
435
  export type TaskBoardKey = keyof typeof zh;
269
436
  /** The settings-card slice of the task-board dictionary. */
270
437
  export type SettingsCardKey = TaskBoardKey;
@@ -1,23 +1,7 @@
1
- /**
2
- * Native panel registration for the task board.
3
- *
4
- * The board is an official-style center-column panel, not a DOM takeover: it
5
- * contributes a row into the sidebar shell's own global panel list
6
- * (`sidebar.panellist`) and its page into the layout's keyed `main` slot. The
7
- * shell then owns the row box, the label, the active highlight, the collapsed
8
- * rail, the panel switch and the window-chrome interplay exactly as it does for
9
- * the shipped Plugins and Schedule pages, so the board renders through the same
10
- * container the desktop application's Plugins page uses.
11
- *
12
- * Both registrations go through `ctx.slots.inject`, which fires only once the
13
- * owning shell entry has declared the seat: load order between this plugin and
14
- * ui-layout / ui-sidebar therefore does not matter, and neither does a shell
15
- * that cannot serve the seats (the callback simply never runs).
16
- *
17
- * @module @linxin666/dsh-client-ui-task-board/client
18
- */
1
+ import type { ReactNode } from 'react';
19
2
  import type { Context as ClientContext } from '@deepseek-ai/cordis';
20
3
  import { TASK_BOARD_PANEL_ID, type BoardController } from '../core/controller.ts';
4
+ import { type SlotRenderBinding } from './seats.tsx';
21
5
  /** The panel id shared by the sidebar row and the main-slot occupant. */
22
6
  export { TASK_BOARD_PANEL_ID };
23
7
  /**
@@ -45,9 +29,11 @@ export declare function TaskBoardPanelIcon({ size }: {
45
29
  * @param props - the framework main-slot share plus this entry's injected face.
46
30
  * @returns the board page.
47
31
  */
48
- export declare function TaskBoardPanel({ controller }: {
32
+ export declare function TaskBoardPanel({ controller, renderSlot }: {
49
33
  controller: BoardController;
50
- }): React.ReactElement;
34
+ /** Child-slot renderer the registration declares (detail + card decoration). */
35
+ renderSlot?: SlotRenderBinding;
36
+ }): ReactNode;
51
37
  /**
52
38
  * Register the board's sidebar row and center-column page.
53
39
  *
@@ -78,6 +78,11 @@ export interface PluginCardSeat {
78
78
  label?: () => string;
79
79
  /** Locale namespace the card renders with. */
80
80
  locale: string;
81
+ /**
82
+ * Child slots this card declares and renders. Forwarded to whichever seat
83
+ * the host renders; absent for a card that declares none.
84
+ */
85
+ children?: Record<string, unknown>;
81
86
  /** Business-face factory of the registration. */
82
87
  inject?: () => object;
83
88
  /**
@@ -1,3 +1,5 @@
1
+ import { type ScheduleRule, type ScheduleStopReason } from '../core/tasks.ts';
2
+ import { type TaskBoardKey } from './locales.ts';
1
3
  /** One selectable zone, in the order the picker shows it. */
2
4
  export interface ZoneChoice {
3
5
  /** Canonical IANA id, or `''` for the Host-zone entry. */
@@ -32,6 +34,54 @@ export declare function zoneChoices(hostTimeZone: string | undefined, storedTime
32
34
  * @returns localized relative wording.
33
35
  */
34
36
  export declare function relativeTimeLabel(targetMs: number, now: number): string;
37
+ /**
38
+ * The `datetime-local` input value for an instant, read in an explicit zone:
39
+ * `YYYY-MM-DDTHH:mm`. Uses the shared engine's zone reader, so the editor
40
+ * shows the same wall clock the Host schedules by.
41
+ * @param epochMs - the instant to render.
42
+ * @param timeZone - zone to read it in; absent uses the Host zone.
43
+ * @returns the input value.
44
+ */
45
+ export declare function zonedInputValue(epochMs: number, timeZone?: string): string;
46
+ /**
47
+ * Parse a `datetime-local` input value back to an instant, read in an explicit
48
+ * zone. Returns undefined when the text is not a whole calendar wall clock or
49
+ * names one the zone skips (a spring-forward gap), so the editor can refuse an
50
+ * unusable instant instead of silently moving it.
51
+ * @param value - the input text.
52
+ * @param timeZone - zone to interpret it in; absent uses the Host zone.
53
+ * @returns the instant, or undefined when it is not a usable wall clock.
54
+ */
55
+ export declare function parseZonedInput(value: string, timeZone?: string): number | undefined;
56
+ /**
57
+ * The default instant a new one-shot editor opens on: the next whole hour,
58
+ * rendered in the zone the rule will be read in.
59
+ * @param now - reference instant.
60
+ * @param timeZone - zone the rule is read in.
61
+ * @returns the input value for a near-future default.
62
+ */
63
+ export declare function defaultZonedInputValue(now: number, timeZone?: string): string;
64
+ /**
65
+ * The editor's run-cap option for a stored cap: the two named presets, the
66
+ * unlimited entry, or a custom value the editor shows in its own field.
67
+ * @param maxRuns - the stored cap, when any.
68
+ * @returns the select option value.
69
+ */
70
+ export declare function runCapOption(maxRuns: number | undefined): string;
71
+ /** The locale key describing why a rule stopped or skipped an occurrence. */
72
+ export declare function stopReasonKey(reason: ScheduleStopReason): TaskBoardKey;
73
+ /**
74
+ * The run-budget line: how many scheduled executions the Host opened and how
75
+ * many remain. Derived from the counter and the cap rather than stored, so the
76
+ * display cannot drift from the ledger.
77
+ * @param rule - the schedule rule.
78
+ * @returns localized wording.
79
+ */
80
+ export declare function budgetLabel(rule: ScheduleRule): string;
81
+ /** The line describing a rule that stopped itself, or undefined while it may still fire. */
82
+ export declare function endedLabel(rule: ScheduleRule): string | undefined;
83
+ /** The line describing the last skipped occurrence, or undefined when none. */
84
+ export declare function skippedLabel(rule: ScheduleRule): string | undefined;
35
85
  /**
36
86
  * The full next-run label: the absolute wall clock in the schedule's own zone,
37
87
  * plus the relative distance in parentheses. Showing both matters because a
@@ -0,0 +1,18 @@
1
+ import type { ReactNode } from 'react';
2
+ import { type TaskBoardCardDecorationProps, type TaskBoardDetailSectionProps, type TaskBoardSettingsSectionProps } from '../core/extension.ts';
3
+ /** The three provider seats, adapted to plain render functions. */
4
+ export interface TaskBoardSeats {
5
+ detailSection: (props: TaskBoardDetailSectionProps) => ReactNode;
6
+ settingsSection: (props: TaskBoardSettingsSectionProps) => ReactNode;
7
+ cardDecoration: (props: TaskBoardCardDecorationProps) => ReactNode;
8
+ }
9
+ /** Nothing registered: every seat renders empty. */
10
+ export declare const EMPTY_TASK_BOARD_SEATS: TaskBoardSeats;
11
+ /** The framework `renderSlot` binding shape (key + owner props share). */
12
+ export type SlotRenderBinding = (key: string, owner: Record<string, unknown>) => ReactNode;
13
+ /** Adapt one framework renderSlot binding to the board's seat interface. */
14
+ export declare function seatsFromRenderSlot(renderSlot: SlotRenderBinding | undefined): TaskBoardSeats;
15
+ /** Provider the board's registered components wrap their subtree in. */
16
+ export declare const TaskBoardSeatsProvider: import("react").Provider<TaskBoardSeats>;
17
+ /** Read the current seat set (empty when no board registration is above). */
18
+ export declare function useTaskBoardSeats(): TaskBoardSeats;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The board's browser-half extension service (`taskBoard`).
3
+ *
4
+ * It gives a provider's browser half the client capability face from the
5
+ * contract: dispatch over the board's same-origin action channel, card
6
+ * visibility predicates, and a subscription to the board mirror. The board
7
+ * controller is attached and detached as the master switch mounts and unmounts
8
+ * the UI, so the service can be provided before any controller exists.
9
+ *
10
+ * @module dsh-task-board/client/service
11
+ */
12
+ import type { BoardController } from '../core/controller.ts';
13
+ import type { TaskBoardClientFace, TaskBoardClientMirror, TaskBoardExtensionActionRequest, TaskBoardVisibilityPredicate } from '../core/extension.ts';
14
+ export declare class TaskBoardClientService implements TaskBoardClientFace {
15
+ private controller;
16
+ private enabled;
17
+ private unsubscribeController;
18
+ /** Predicates registered before the controller exists, replayed on attach. */
19
+ private readonly pendingVisibility;
20
+ private readonly listeners;
21
+ /** Bind the live board controller (called when the UI mounts). */
22
+ attach(controller: BoardController): void;
23
+ /** Release the current controller (called when the UI unmounts). */
24
+ detach(): void;
25
+ /** Apply the board's master switch. */
26
+ setEnabled(enabled: boolean): void;
27
+ dispatch(request: TaskBoardExtensionActionRequest): Promise<boolean>;
28
+ registerVisibility(predicate: TaskBoardVisibilityPredicate): () => void;
29
+ subscribe(callback: (mirror: TaskBoardClientMirror) => void): () => void;
30
+ snapshot(): TaskBoardClientMirror;
31
+ private notify;
32
+ }
@@ -134,6 +134,8 @@ export declare class CardForm<T> {
134
134
  private readonly disposeForm;
135
135
  private disposed;
136
136
  private saving;
137
+ /** A save asked for while one was in flight; it runs once the first settles (#1754). */
138
+ private saveQueued;
137
139
  private failed;
138
140
  private failedReason;
139
141
  /** @param scope - the bound configuration form for this card's namespace. */
@@ -167,6 +169,20 @@ export declare class CardForm<T> {
167
169
  * the user can correct them instead of retyping.
168
170
  * @returns settlement after the mutation and the read-back.
169
171
  */
172
+ /**
173
+ * Run one save, and re-run it once if another was asked for while this one
174
+ * was still in flight.
175
+ *
176
+ * A save is a Host round trip that also drives the profile reconcile, and
177
+ * the Host runs that write inside one exclusive transaction. Answering a
178
+ * press that arrives mid-flight by returning immediately dropped the edit
179
+ * with no explanation, which is the "the save button stops working after a
180
+ * few rounds" report (#1754). Serializing instead means a save pressed while
181
+ * another is still settling runs against the settled state - which is what
182
+ * the operator meant by pressing it again.
183
+ * @returns settlement after the mutation and the read-back.
184
+ */
185
+ requestSave(): Promise<void>;
170
186
  save(): Promise<void>;
171
187
  /**
172
188
  * Every staged edit a save would write. An entry whose draft is not a value
@@ -13,8 +13,10 @@
13
13
  */
14
14
  import type { TaskStore } from './store.ts';
15
15
  import { type NewTaskInput, type TaskRecord, type TaskStatus } from './tasks.ts';
16
+ import { type SetSchedulePatch } from './use-cases/task-schedule.ts';
16
17
  import { type TaskUpdatePatch } from './use-cases/task-update.ts';
17
18
  import type { TaskBoardAction, TaskBoardEventPayload, TaskBoardParseDraft, TaskBoardParseRequest, TaskBoardSnapshot } from '../protocol.ts';
19
+ import type { TaskBoardExtensionActionRequest, TaskBoardVisibilityPredicate } from './extension.ts';
18
20
  export interface TaskBoardTransport {
19
21
  bootstrap(legacy: readonly TaskRecord[]): Promise<TaskBoardSnapshot>;
20
22
  state(): Promise<TaskBoardSnapshot>;
@@ -110,7 +112,7 @@ export interface ExecutionOptionsSnapshot {
110
112
  * (revision/scheduler/power); a full snapshot also carries the deployment
111
113
  * constants the UI reads (session default permission, subtask depth).
112
114
  */
113
- export type HostMirror = Pick<TaskBoardSnapshot, 'revision' | 'scheduler' | 'power' | 'sessionDefaultPermission' | 'maxSubtaskDepth' | 'teamRunAvailable'>;
115
+ export type HostMirror = Pick<TaskBoardSnapshot, 'revision' | 'scheduler' | 'power' | 'sessionDefaultPermission' | 'maxSubtaskDepth' | 'teamRunAvailable' | 'goalRunEnabled' | 'extensions'>;
114
116
  /** Immutable controller snapshot for UI subscriptions. */
115
117
  export interface ControllerSnapshot {
116
118
  tasks: readonly TaskRecord[];
@@ -127,6 +129,15 @@ export interface ControllerSnapshot {
127
129
  canParseTask?: boolean;
128
130
  transportError?: string;
129
131
  host?: HostMirror;
132
+ /**
133
+ * Provider card-visibility predicates, in registration order. A task shows
134
+ * only while every predicate accepts it; providers hide their own off-board
135
+ * cards here instead of the board hard-coding a provider rule. Optional so a
136
+ * hand-built snapshot (tests, a shell-less composition) defaults to visible.
137
+ */
138
+ visibility?: readonly TaskBoardVisibilityPredicate[];
139
+ /** Read-only summaries published by running extensions, keyed by extension id. */
140
+ extensions?: Readonly<Record<string, unknown>>;
130
141
  }
131
142
  /** The selected task (resolved from the ledger), or undefined. */
132
143
  export declare function selectedTaskOf(snapshot: ControllerSnapshot): TaskRecord | undefined;
@@ -150,6 +161,8 @@ export declare class BoardController {
150
161
  private readonly taskQueues;
151
162
  private transportError;
152
163
  private hostState;
164
+ /** Provider visibility predicates (immutable array; replaced on change). */
165
+ private visibility;
153
166
  private remoteSubscribed;
154
167
  private remoteInitialization;
155
168
  /** @param deps - store and the sessions navigation face. */
@@ -159,6 +172,16 @@ export declare class BoardController {
159
172
  /** Stop all subscriptions and drop retained state (idempotent). */
160
173
  dispose(): void;
161
174
  getSnapshot(): ControllerSnapshot;
175
+ /**
176
+ * Register one card-visibility predicate. The returned disposer removes it;
177
+ * both changes notify subscribers so the board re-renders.
178
+ */
179
+ registerVisibility(predicate: TaskBoardVisibilityPredicate): () => void;
180
+ /**
181
+ * Deliver one provider action through the board's same-origin channel. A
182
+ * board without a Host transport (the legacy in-memory path) refuses it.
183
+ */
184
+ dispatchExtension(request: TaskBoardExtensionActionRequest): Promise<boolean>;
162
185
  subscribe(fn: () => void): () => void;
163
186
  /** Whether production mutations are confirmed by the Host transport. */
164
187
  isHostBacked(): boolean;
@@ -251,19 +274,31 @@ export declare class BoardController {
251
274
  /** Restore an archived task back onto the board (same status column). */
252
275
  restoreTask(id: string): boolean;
253
276
  /**
254
- * Update a task's schedule rule. A blank or invalid cron expression is
255
- * rejected (returns false, state untouched). When the rule ends up enabled
256
- * the next run instant is computed immediately; a disabled rule carries no
257
- * next-run instant. Delegates the domain transition to the schedule use case.
277
+ * Rename one label across the whole ledger; a name already in use merges the
278
+ * two labels. Host-backed: the ledger owns the transition and confirms it, so
279
+ * the board's label row reflects whether the Host accepted the edit.
280
+ * @returns true when the edit was accepted by the authority.
281
+ */
282
+ renameTag(from: string, to: string): Promise<boolean>;
283
+ /**
284
+ * Remove one label from every task that carries it, board and archive alike —
285
+ * the board's label row is drawn from the whole ledger, so a label left on an
286
+ * archived card would come straight back.
287
+ * @returns true when the edit was accepted by the authority.
288
+ */
289
+ deleteTag(name: string): Promise<boolean>;
290
+ /**
291
+ * Update a task's schedule rule. An unusable plan (blank or invalid cron, a
292
+ * one-shot with no future instant, a non-positive run cap) is rejected
293
+ * (returns false, state untouched). When the rule ends up enabled the next
294
+ * run instant is computed immediately — a one-shot's next instant IS its
295
+ * planned instant — and a disabled rule carries none. Delegates the domain
296
+ * transition to the schedule use case.
258
297
  * @param id - the task to schedule.
259
298
  * @param patch - fields to change (absent fields keep their current value).
260
- * @returns true when applied, false when rejected (invalid cron / unknown task).
299
+ * @returns true when applied, false when rejected (unknown task / unusable plan).
261
300
  */
262
- setSchedule(id: string, patch: {
263
- enabled?: boolean;
264
- cron?: string;
265
- timeZone?: string | null;
266
- }): boolean;
301
+ setSchedule(id: string, patch: SetSchedulePatch): boolean;
267
302
  /**
268
303
  * Legacy pure-controller seam retained for migration-focused tests. The
269
304
  * production browser never rolls schedules; the Host ledger owns them.