@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
@@ -24,6 +24,13 @@ export interface PluginSettingsCardProps<TKey extends string = string> {
24
24
  descriptionKey: TKey;
25
25
  /** Optional rich header description rendered instead of the plain t(descriptionKey) text (the plain text stays as the tooltip). */
26
26
  descriptionNode?: ReactNode;
27
+ /**
28
+ * Optional leading mark identifying the plugin. A card without one keeps the
29
+ * plain official header shape, so the chrome stays a readable sibling of the
30
+ * built-in PluginCard wherever a family plugin lands in the official bundle
31
+ * seat.
32
+ */
33
+ icon?: ReactNode;
27
34
  /** The card's form state: availability, writability, and what a save would do. */
28
35
  state: CardShell;
29
36
  /** Write every staged edit. */
@@ -3,9 +3,10 @@
3
3
  * idle-sleep protection. Registers into the `web-ui.plugin.item` child slot
4
4
  * the Web UI plugin group renders, bound to the `task-board` namespace.
5
5
  */
6
- import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
6
+ import type { InjectFace, PropsLocale, PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
7
7
  import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
8
8
  import type { ConfigForm } from '@deepseek-ai/dsh-client-ui-settings/client';
9
+ import type { TaskBoardExtensionDispatch } from '../core/extension.ts';
9
10
  import { type CardActions, type CardShell, type FieldState as CardFieldState } from './settings-form.ts';
10
11
  /** The task-board fields this card edits (the namespace's full schema). */
11
12
  export interface TaskBoardSettings {
@@ -17,6 +18,20 @@ export interface TaskBoardSettings {
17
18
  preventIdleSleep?: boolean;
18
19
  /** Subtask depth limit (1..3); 1 means a single level of subtasks. */
19
20
  maxSubtaskDepth?: number;
21
+ /** Roster-poll cadence in seconds while the board has work to reconcile. */
22
+ sessionPollSeconds?: number;
23
+ /**
24
+ * GLOBAL native /goal switch (default off): the master control over whether
25
+ * this board starts a run with dsh's built-in /goal at all. Frozen per
26
+ * execution.
27
+ */
28
+ goalRunEnabled?: boolean;
29
+ /** Goal acceptance for this board's executions (default on). */
30
+ goalVerification?: boolean;
31
+ /** Judge model route for goal acceptance; blank inherits the host default. */
32
+ goalVerificationModel?: string;
33
+ /** Judge reasoning effort; blank inherits the host default level. */
34
+ goalVerificationReasoningEffort?: string;
20
35
  }
21
36
  /** What the task-board card renders. */
22
37
  export interface TaskBoardSettingsCardState extends CardShell {
@@ -28,6 +43,16 @@ export interface TaskBoardSettingsCardState extends CardShell {
28
43
  preventIdleSleep: CardFieldState;
29
44
  /** Subtask depth limit field. */
30
45
  maxSubtaskDepth: CardFieldState;
46
+ /** Roster-poll cadence field. */
47
+ sessionPollSeconds: CardFieldState;
48
+ /** GLOBAL native /goal switch. */
49
+ goalRunEnabled: CardFieldState;
50
+ /** Goal-acceptance switch. */
51
+ goalVerification: CardFieldState;
52
+ /** Judge model route field. */
53
+ goalVerificationModel: CardFieldState;
54
+ /** Judge reasoning-effort field. */
55
+ goalVerificationReasoningEffort: CardFieldState;
31
56
  }
32
57
  /** The registration-side face the card's slot entry injects. */
33
58
  export interface TaskBoardSettingsCardFace extends CardActions {
@@ -35,13 +60,16 @@ export interface TaskBoardSettingsCardFace extends CardActions {
35
60
  /** Card snapshot bound by the renderer as useTaskBoardSettingsCard. */
36
61
  taskBoardSettingsCard: SnapshotStore<TaskBoardSettingsCardState>;
37
62
  };
63
+ /** Deliver one provider action over the board's same-origin channel. */
64
+ dispatch: TaskBoardExtensionDispatch;
38
65
  }
39
66
  /** Bridges the `task-board` settings form onto the card's staged form. */
40
67
  export declare class TaskBoardSettingsCardController {
68
+ private readonly dispatch;
41
69
  private readonly form;
42
70
  private readonly store;
43
71
  /** @param scope - the bound configuration form for the `task-board` namespace. */
44
- constructor(scope: ConfigForm<TaskBoardSettings>);
72
+ constructor(scope: ConfigForm<TaskBoardSettings>, dispatch: TaskBoardExtensionDispatch);
45
73
  private projection;
46
74
  /**
47
75
  * Build the face the card's slot registration injects.
@@ -55,7 +83,7 @@ export declare class TaskBoardSettingsCardController {
55
83
  dispose(): void;
56
84
  }
57
85
  /** Props the renderer binds for the task-board card. */
58
- export type TaskBoardSettingsCardProps = PropsRuntime<'web-ui.plugin.item'> & PropsLocale<'task-board'> & InjectFace<TaskBoardSettingsCardFace>;
86
+ export type TaskBoardSettingsCardProps = PropsRuntime<'web-ui.plugin.item'> & PropsLocale<'task-board'> & InjectFace<TaskBoardSettingsCardFace> & PropsRenderSlots<'task-board.settings.section'>;
59
87
  /**
60
88
  * Render the task-board card.
61
89
  * @param props - locale copy, the card snapshot, and its form actions.
@@ -0,0 +1,3 @@
1
+ export declare function ClampedMarkdown({ source }: {
2
+ source: string;
3
+ }): import("react").JSX.Element;
@@ -1,3 +1,4 @@
1
+ import { type OverlayPhase } from './overlay.tsx';
1
2
  /** Confirm overlay props. */
2
3
  export interface ConfirmDialogProps {
3
4
  title: string;
@@ -5,8 +6,10 @@ export interface ConfirmDialogProps {
5
6
  confirmLabel: string;
6
7
  /** Render the confirm button in the danger style. */
7
8
  danger?: boolean;
9
+ /** Which leg of the enter/exit motion pair the surface is on. */
10
+ phase?: OverlayPhase;
8
11
  onCancel: () => void;
9
12
  onConfirm: () => void;
10
13
  }
11
14
  /** Small confirm overlay. */
12
- export declare function ConfirmDialog({ title, message, confirmLabel, danger, onCancel, onConfirm }: ConfirmDialogProps): import("react").JSX.Element;
15
+ export declare function ConfirmDialog({ title, message, confirmLabel, danger, phase, onCancel, onConfirm }: ConfirmDialogProps): import("react").JSX.Element;
@@ -1,14 +1,17 @@
1
1
  import type { BoardController } from '../../core/controller.ts';
2
2
  import { type TaskRecord } from '../../core/tasks.ts';
3
+ import type { OverlayPhase } from './overlay.tsx';
3
4
  /** Edit-task form overlay. */
4
- export declare function EditTaskModal({ controller, task, onClose }: {
5
+ export declare function EditTaskModal({ controller, task, onClose, phase }: {
5
6
  controller: BoardController;
6
7
  task: TaskRecord;
7
8
  onClose: () => void;
9
+ phase?: OverlayPhase;
8
10
  }): import("react").JSX.Element;
9
11
  /** Edit-tags modal: edit labels only, shown for tasks after first execution. */
10
- export declare function EditTagsModal({ controller, task, onClose }: {
12
+ export declare function EditTagsModal({ controller, task, onClose, phase }: {
11
13
  controller: BoardController;
12
14
  task: TaskRecord;
13
15
  onClose: () => void;
16
+ phase?: OverlayPhase;
14
17
  }): import("react").JSX.Element;
@@ -1,11 +1,14 @@
1
1
  import type { BoardController } from '../../core/controller.ts';
2
- import type { TaskRecord } from '../../core/tasks.ts';
2
+ import { type TaskRecord } from '../../core/tasks.ts';
3
+ import { type OverlayPhase } from './overlay.tsx';
3
4
  /** Link-subtask overlay props. */
4
5
  export interface LinkSubtaskModalProps {
5
6
  controller: BoardController;
6
7
  /** The task the picked card becomes a subtask of. */
7
8
  parent: TaskRecord;
8
9
  onClose: () => void;
10
+ /** Which leg of the enter/exit motion pair the surface is on. */
11
+ phase?: OverlayPhase;
9
12
  }
10
13
  /** Link-subtask overlay. */
11
- export declare function LinkSubtaskModal({ controller, parent, onClose }: LinkSubtaskModalProps): import("react").JSX.Element;
14
+ export declare function LinkSubtaskModal({ controller, parent, onClose, phase }: LinkSubtaskModalProps): import("react").JSX.Element;
@@ -1,5 +1,6 @@
1
1
  import type { BoardController } from '../../core/controller.ts';
2
2
  import { type TaskRecord } from '../../core/tasks.ts';
3
+ import type { OverlayPhase } from './overlay.tsx';
3
4
  export interface NewTaskModalProps {
4
5
  controller: BoardController;
5
6
  onClose: () => void;
@@ -18,6 +19,8 @@ export interface NewTaskModalProps {
18
19
  * carries the parent link; unset targets inherit again at run time.
19
20
  */
20
21
  parentTask?: TaskRecord;
22
+ /** Which leg of the enter/exit motion pair the surface is on. */
23
+ phase?: OverlayPhase;
21
24
  }
22
25
  /** New-task form overlay. */
23
- export declare function NewTaskModal({ controller, onClose, initialTask, defaultWorkspaceId, onDuplicateSuccess, parentTask }: NewTaskModalProps): import("react").JSX.Element;
26
+ export declare function NewTaskModal({ controller, onClose, initialTask, defaultWorkspaceId, onDuplicateSuccess, parentTask, phase }: NewTaskModalProps): import("react").JSX.Element;
@@ -0,0 +1,26 @@
1
+ import type { BoardController } from '../../core/controller.ts';
2
+ import { type TaskRecord } from '../../core/tasks.ts';
3
+ import { type OverlayPhase } from './overlay.tsx';
4
+ /** One label as the manager lists it: its definition plus how many tasks carry it. */
5
+ export interface TagUsage {
6
+ name: string;
7
+ promptPrefix?: string;
8
+ /** Tasks carrying the label right now. */
9
+ count: number;
10
+ }
11
+ /**
12
+ * Labels in use, in board order (first occurrence wins, exactly like the
13
+ * filter row), each with its usage count.
14
+ * @param tasks - the whole ledger (board and archive).
15
+ * @returns one row per label.
16
+ */
17
+ export declare function tagUsage(tasks: readonly TaskRecord[]): TagUsage[];
18
+ /** Label-manager overlay props. */
19
+ export interface TagManagerModalProps {
20
+ controller: BoardController;
21
+ onClose: () => void;
22
+ /** Which leg of the enter/exit motion pair the surface is on. */
23
+ phase?: OverlayPhase;
24
+ }
25
+ /** Label-manager overlay. */
26
+ export declare function TagManagerModal({ controller, onClose, phase }: TagManagerModalProps): import("react").JSX.Element;
@@ -2,7 +2,14 @@ import { type BoardController } from '../../core/controller.ts';
2
2
  import { type TaskRecord } from '../../core/tasks.ts';
3
3
  /** Sentinel option value of the project row's "register a new project" entry. */
4
4
  export declare const NEW_PROJECT_VALUE = "__dsh_new_project__";
5
- /** Case-insensitive title/description/tag/freeze-snapshot match. */
5
+ /**
6
+ * Case-insensitive title/description/tag/freeze-snapshot/provider-payload match.
7
+ *
8
+ * A leading `#` is dropped from the needle. Providers render their own
9
+ * identifiers with that prefix (`#1758`) and the board attaches no meaning to
10
+ * it: stripping it keeps the identifier searchable exactly as it is written on
11
+ * screen, without the board learning any provider's vocabulary.
12
+ */
6
13
  export declare function matchesFilter(task: TaskRecord, filter: string): boolean;
7
14
  /**
8
15
  * Whether a task carries every selected label (issue #1521). Multi-select is
@@ -11,6 +18,8 @@ export declare function matchesFilter(task: TaskRecord, filter: string): boolean
11
18
  * back in when a second label is added.
12
19
  */
13
20
  export declare function matchesTagFilter(task: TaskRecord, selected: readonly string[]): boolean;
21
+ /** Cards a grouped column renders before the oldest group folds. */
22
+ export declare const COLUMN_FOLD_THRESHOLD = 30;
14
23
  /** Board component; subscribes to the controller snapshot. */
15
24
  export declare function TaskBoard({ controller }: {
16
25
  controller: BoardController;
@@ -2,11 +2,24 @@ import type { TaskRecord } from '../../core/tasks.ts';
2
2
  /** Compact relative/absolute time label. */
3
3
  export declare function formatHostTimestamp(ms: number, timeZone?: string): string;
4
4
  export declare function formatTime(ms: number, timeZone?: string): string;
5
- declare function TaskCardInner({ task, pending, timeZone, onClick, subtaskCount, isSubtask, subtasksDone, subtasksRunning, subtasksFailed, }: {
5
+ /**
6
+ * Card time label: relative within a day, otherwise the calendar date read in
7
+ * the Host time zone (the same zone the tooltip and the detail view use), so a
8
+ * card never shows a browser-local date beside a Host-zone tooltip.
9
+ * @param ms - the instant.
10
+ * @param timeZone - Host IANA zone; absent uses the browser zone.
11
+ * @param now - the current instant.
12
+ */
13
+ export declare function formatCardTime(ms: number, timeZone?: string, now?: number): string;
14
+ declare function TaskCardInner({ task, pending, timeZone, onClick, onOpenSession, compact, subtaskCount, isSubtask, subtasksDone, subtasksRunning, subtasksFailed, }: {
6
15
  task: TaskRecord;
7
16
  pending: boolean;
8
17
  timeZone?: string;
9
18
  onClick: () => void;
19
+ /** Open an execution session; absent renders the session mark inert. */
20
+ onOpenSession?: (sessionId: string) => void;
21
+ /** Single-line density used by long settled columns. */
22
+ compact?: boolean;
10
23
  /** Direct subtasks of this card, shown as a badge (0 hides it). */
11
24
  subtaskCount?: number;
12
25
  /** Whether this card is itself a subtask. */
@@ -1,7 +1,9 @@
1
1
  import type { BoardController } from '../../core/controller.ts';
2
2
  import { type TaskRecord } from '../../core/tasks.ts';
3
+ import { type OverlayPhase } from './overlay.tsx';
3
4
  /** Task detail overlay. */
4
- export declare function TaskDetail({ controller, task }: {
5
+ export declare function TaskDetail({ controller, task, phase }: {
5
6
  controller: BoardController;
6
7
  task: TaskRecord;
8
+ phase?: OverlayPhase;
7
9
  }): import("react").JSX.Element;
@@ -6,8 +6,9 @@
6
6
  */
7
7
  import { type ReactNode } from 'react';
8
8
  import { type TaskTag } from '../../core/tasks.ts';
9
+ import { type OverlayPhase } from './overlay.tsx';
9
10
  /** Modal overlay: closes on backdrop press, submits through the form. */
10
- export declare function ModalShell({ ariaLabel, title, error, pending, submitLabel, onSubmit, onClose, secondaryAction, children, }: {
11
+ export declare function ModalShell({ ariaLabel, title, error, pending, submitLabel, onSubmit, onClose, secondaryAction, phase, children, }: {
11
12
  ariaLabel: string;
12
13
  title: string;
13
14
  error: string | undefined;
@@ -20,6 +21,8 @@ export declare function ModalShell({ ariaLabel, title, error, pending, submitLab
20
21
  label: string;
21
22
  onSubmit: () => void;
22
23
  };
24
+ /** Which leg of the enter/exit motion pair the surface is on. */
25
+ phase?: OverlayPhase;
23
26
  children: ReactNode;
24
27
  }): import("react").JSX.Element;
25
28
  /**
@@ -0,0 +1,23 @@
1
+ /**
2
+ * One execution's goal-acceptance report.
3
+ *
4
+ * Rendered inside the execution-history row, so a rerun or a scheduled run
5
+ * keeps its OWN report and a later cycle never overwrites an earlier verdict.
6
+ * It shows what the board decided and the evidence it decided on: the verdict,
7
+ * the judge route and reasoning level, the acceptance count, the total and
8
+ * per-criterion scores against their threshold, the findings the judge located,
9
+ * the evidence scope with its truncation, and the token usage.
10
+ *
11
+ * Deliberately adds no new `data-dsh-part` value: the part enum is owned by the
12
+ * cross-repository semantic-attribute contract, and this report reuses the
13
+ * execution row's existing markup instead of extending that enum.
14
+ */
15
+ import { type ExecutionVerification } from '../../core/verification.ts';
16
+ /**
17
+ * Render one execution's acceptance report.
18
+ * @param props - the execution's persisted acceptance block.
19
+ * @returns the report, or nothing when acceptance never applied.
20
+ */
21
+ export declare function VerificationReport({ verification }: {
22
+ verification: ExecutionVerification;
23
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Card view model: the pure derivations a task card renders from its record.
3
+ *
4
+ * Everything here is a function of the task record (plus the clock and the
5
+ * Host time zone for the recency groups), so the card stays a thin renderer and
6
+ * every rule is unit-testable without mounting React. Nothing here writes the
7
+ * record: the type prefix, the tone and the time label are presentation only.
8
+ *
9
+ * @module @linxin666/dsh-client-ui-task-board/client/board/card-view
10
+ */
11
+ import { type ExecutionOutcome, type ExecutionRecord, type TaskRecord, type TaskStatus } from '../../core/tasks.ts';
12
+ import type { TaskBoardKey } from '../locales.ts';
13
+ /** A title split into its bracketed type prefix and the remaining text. */
14
+ export interface TitleParts {
15
+ /** Bracketed type prefix such as `Bug` from `[Bug]: ...`; absent when none. */
16
+ kind?: string;
17
+ /** Title text without the prefix (the whole title when there is no prefix). */
18
+ text: string;
19
+ }
20
+ /**
21
+ * Split an issue-form style type prefix off a title. The prefix becomes a
22
+ * badge, so the clamped title lines carry the text the reader scans for. A
23
+ * title that is only a prefix keeps its full text.
24
+ * @param summary - the plain-text title.
25
+ * @returns the prefix (when present) and the remaining title text.
26
+ */
27
+ export declare function splitTitleKind(summary: string): TitleParts;
28
+ /** Stripe tone of a card: the state of its latest execution. */
29
+ export type CardTone = 'running' | ExecutionOutcome;
30
+ /** What the card reports about its latest execution. */
31
+ export interface CardOutcome {
32
+ /** Stripe tone; absent on an archived card or a card that never ran. */
33
+ tone?: CardTone;
34
+ /** The latest execution record, when the card ran at least once. */
35
+ latest?: ExecutionRecord;
36
+ /**
37
+ * The column disagrees with the latest settled run: a card in Done whose last
38
+ * run did not succeed, or a card in Failed whose last run succeeded. The
39
+ * column was then set by hand or by a provider (a closed issue), and the
40
+ * card says so instead of letting a red run count read as a contradiction.
41
+ */
42
+ declared: boolean;
43
+ }
44
+ /**
45
+ * Derive the card's execution outcome.
46
+ * @param task - the task record.
47
+ * @param archived - whether the card renders in the archive view.
48
+ */
49
+ export declare function cardOutcome(task: TaskRecord, archived: boolean): CardOutcome;
50
+ /** Locale key of the latest settled result shown before the run count. */
51
+ export declare const RESULT_KEY: Record<ExecutionOutcome, TaskBoardKey>;
52
+ /** The one time a card shows, and what that time means. */
53
+ export interface CardTimeline {
54
+ /** Locale key with a `{time}` placeholder. */
55
+ key: TaskBoardKey;
56
+ /** The instant (ms epoch). */
57
+ at: number;
58
+ }
59
+ /**
60
+ * Pick the time that tells cards in the same column apart. `updatedAt` moves
61
+ * on every provider sync, so a column of synced cards would all read
62
+ * "updated 4m"; each column instead shows the instant its cards differ by:
63
+ * planning columns the creation time, an executing card its start, settled
64
+ * columns the latest run's settlement, the archive the archive time.
65
+ * @param task - the task record.
66
+ * @param archived - whether the card renders in the archive view.
67
+ */
68
+ export declare function cardTimeline(task: TaskRecord, archived: boolean): CardTimeline;
69
+ /** Recency bucket of a settled card. */
70
+ export type RecencyGroup = 'today' | 'week' | 'earlier';
71
+ /** Group order, newest first. */
72
+ export declare const RECENCY_GROUPS: readonly RecencyGroup[];
73
+ /** Locale key of each recency group header. */
74
+ export declare const RECENCY_KEY: Record<RecencyGroup, TaskBoardKey>;
75
+ /**
76
+ * Bucket an instant: the same Host calendar day as now, the last seven days, or
77
+ * earlier.
78
+ * @param at - the instant to bucket.
79
+ * @param now - the current instant.
80
+ * @param timeZone - Host IANA zone the calendar day is read in.
81
+ */
82
+ export declare function recencyGroup(at: number, now: number, timeZone?: string): RecencyGroup;
83
+ /** One recency group and its items, newest first. */
84
+ export interface RecencyBucket<T> {
85
+ group: RecencyGroup;
86
+ items: T[];
87
+ }
88
+ /**
89
+ * Sort items newest first and split them into recency groups; empty groups are
90
+ * omitted.
91
+ * @param items - the items to group.
92
+ * @param at - the instant each item is ordered and bucketed by.
93
+ * @param now - the current instant.
94
+ * @param timeZone - Host IANA zone.
95
+ */
96
+ export declare function groupByRecency<T>(items: readonly T[], at: (item: T) => number, now: number, timeZone?: string): RecencyBucket<T>[];
97
+ /** Columns that render compact by default: the long settled column. */
98
+ export declare const DEFAULT_COMPACT_COLUMNS: readonly TaskStatus[];
99
+ /** Browser storage key of the per-column density choice. */
100
+ export declare const COMPACT_COLUMNS_KEY = "dsh.taskBoard.compactColumns.v1";
101
+ /**
102
+ * Read the columns the user set to compact. A missing, unreadable or malformed
103
+ * value falls back to the default rather than failing the board.
104
+ * @param storage - browser storage (absent outside a browser).
105
+ */
106
+ export declare function readCompactColumns(storage: Pick<Storage, 'getItem'> | undefined): TaskStatus[];
107
+ /**
108
+ * Persist the compact columns; a storage failure (quota, privacy mode) only
109
+ * loses the preference.
110
+ * @param storage - browser storage (absent outside a browser).
111
+ * @param columns - the compact columns.
112
+ */
113
+ export declare function writeCompactColumns(storage: Pick<Storage, 'setItem'> | undefined, columns: readonly TaskStatus[]): void;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The board's own vector glyphs.
3
+ *
4
+ * Every control glyph the board draws comes from here, so one action always
5
+ * has one metaphor and one stroke recipe: a 16-unit viewBox, `currentColor`,
6
+ * 1.3 stroke, round caps and joins — the same recipe the sidebar panel glyph
7
+ * (`native-panel.tsx`) uses, which is why the board's chrome and its sidebar
8
+ * row read as one product. Icon-only controls pass a localized `title` and an
9
+ * accessible name at the call site; the glyph itself is always decorative.
10
+ *
11
+ * @module @linxin666/dsh-client-ui-task-board/client/board/icons
12
+ */
13
+ import type { ReactElement } from 'react';
14
+ /** Props of one board glyph: the shared recipe plus an explicit pixel size. */
15
+ export interface BoardIconProps {
16
+ /** Rendered square edge in CSS pixels. */
17
+ size?: number;
18
+ }
19
+ /** Leave the board: the back-to-chat chevron. */
20
+ export declare function IconChevronLeft(props: BoardIconProps): ReactElement;
21
+ /** Dismiss a transient surface. */
22
+ export declare function IconClose(props: BoardIconProps): ReactElement;
23
+ /** Add: create a task, a label row, or a subtask. */
24
+ export declare function IconPlus(props: BoardIconProps): ReactElement;
25
+ /** Open the execution's session transcript. */
26
+ export declare function IconSession(props: BoardIconProps): ReactElement;
27
+ /** Switch a column to single-line cards. */
28
+ export declare function IconCompactRows(props: BoardIconProps): ReactElement;
29
+ /** Switch a column back to full cards. */
30
+ export declare function IconExpandRows(props: BoardIconProps): ReactElement;
31
+ /** A scheduled run: the recurrence mark on a card. */
32
+ export declare function IconClock(props: BoardIconProps): ReactElement;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Shared behavior of the board's transient surfaces (the task detail overlay
3
+ * and every modal that opens above it).
4
+ *
5
+ * Two concerns live here because they are one contract:
6
+ *
7
+ * - `usePresence` keeps a surface mounted through its exit leg. The board's
8
+ * overlays are mounted by a boolean at the call site; the moment that
9
+ * boolean flips the node would vanish, so the enter animation had no
10
+ * reverse. Presence turns the boolean into `open` -> `closing` -> gone and
11
+ * paints the phase on the surface, which is what the CSS transition pair
12
+ * reads. Dimensions never change across the pair, so nothing around the
13
+ * overlay reflows.
14
+ * - `useDialog` owns the keyboard and focus contract of a modal surface:
15
+ * focus moves into the dialog when it opens and returns to the control that
16
+ * opened it when it closes, Tab cycles inside it, and Escape asks the owner
17
+ * to close.
18
+ *
19
+ * @module @linxin666/dsh-client-ui-task-board/client/board/overlay
20
+ */
21
+ import { type MouseEvent } from 'react';
22
+ /**
23
+ * Exit duration in milliseconds. It matches `--dsh-tb-motion-close` in
24
+ * `board.module.css`; under `prefers-reduced-motion` the stylesheet keeps the
25
+ * same duration and drops only the spatial travel, so the unmount delay stays
26
+ * valid for both motion settings.
27
+ */
28
+ export declare const OVERLAY_EXIT_MS = 140;
29
+ /** Lifecycle phase of one transient surface. */
30
+ export type OverlayPhase = 'open' | 'closing';
31
+ /** Presence of one transient surface. */
32
+ export interface OverlayPresence {
33
+ /** Whether the surface should render at all (true through the exit leg). */
34
+ mounted: boolean;
35
+ /** Which leg of the motion pair the surface is on. */
36
+ phase: OverlayPhase;
37
+ }
38
+ /**
39
+ * Keep a surface mounted until its exit transition has run.
40
+ *
41
+ * `open=false` starts the exit leg immediately (the surface paints
42
+ * `data-state="closing"`) and unmounts it one exit duration later. Re-opening
43
+ * during the exit leg cancels the unmount, so a fast toggle never leaves a
44
+ * half-faded surface behind.
45
+ * @param open - the owner's mount request.
46
+ * @param exitMs - exit duration; defaults to {@link OVERLAY_EXIT_MS}.
47
+ * @returns the mount decision and the phase to paint.
48
+ */
49
+ export declare function usePresence(open: boolean, exitMs?: number): OverlayPresence;
50
+ /** The dialog's keyboard/focus handle. */
51
+ export interface DialogHandle<T extends HTMLElement> {
52
+ /** Callback ref for the dialog element itself (it must accept focus). */
53
+ attach: (node: T | null) => void;
54
+ /** Backdrop press handler: closes only on a press of the backdrop itself. */
55
+ onMouseDown: (event: MouseEvent<HTMLElement>) => void;
56
+ }
57
+ /**
58
+ * Bind a modal surface's keyboard and focus contract.
59
+ *
60
+ * Focus enters the dialog on mount (its first focusable control, or the dialog
61
+ * itself) and returns to the control that opened it on unmount. Tab wraps
62
+ * inside the dialog, and Escape asks the owner to close — but only while the
63
+ * surface is on its enter leg, so a repeat press during the exit cannot queue
64
+ * a second close.
65
+ * @param onClose - the owner's close request.
66
+ * @param phase - the surface's current motion phase.
67
+ * @returns the ref to attach and the backdrop press handler.
68
+ */
69
+ export declare function useDialog<T extends HTMLElement>(onClose: () => void, phase: OverlayPhase): DialogHandle<T>;
@@ -1,4 +1,19 @@
1
1
  import type { TaskStatus } from '../../core/tasks.ts';
2
+ import { type ExecutionVerification, type VerificationPhase } from '../../core/verification.ts';
2
3
  import type { TaskBoardKey } from '../locales.ts';
3
4
  /** Task status → locale key (board column titles and the detail badge). */
4
5
  export declare const STATUS_KEY: Record<TaskStatus, TaskBoardKey>;
6
+ /**
7
+ * Acceptance phase to the label the running column shows: executing,
8
+ * verifying, or fixing a failed acceptance. The card keeps one running label;
9
+ * this decides which one.
10
+ */
11
+ export declare const VERIFICATION_PHASE_KEY: Record<VerificationPhase, TaskBoardKey>;
12
+ /**
13
+ * The acceptance label for one open execution, or undefined when acceptance
14
+ * does not govern it (the switch was off at start, the run never became a goal
15
+ * run, or a teammate is covered by its Lead).
16
+ * @param verification - the execution's acceptance state.
17
+ * @returns the locale key to render, or undefined for the historical label.
18
+ */
19
+ export declare function verificationRunningKey(verification: ExecutionVerification | undefined): TaskBoardKey | undefined;
@@ -0,0 +1,19 @@
1
+ /** Readable summary without changing the task source or creating DOM HTML. */
2
+ export declare function markdownToPlainText(source: string): string;
3
+ /**
4
+ * Card excerpt: the plain text a reader can tell cards apart by.
5
+ *
6
+ * Issue-form bodies open with identical boilerplate (a duplicate-search
7
+ * checkbox, a plugin field, an issue-type field), so the plain-text prefix of
8
+ * every synced card read the same. The excerpt drops section headings, lists
9
+ * made only of checkboxes, raw HTML, rules and empty-field placeholders; when a
10
+ * summary-like section exists its body wins. A description that loses
11
+ * everything to those rules falls back to its full plain text, so no card goes
12
+ * blank. The stored description is never changed.
13
+ * @param source - the task description (Markdown).
14
+ */
15
+ export declare function markdownExcerpt(source: string): string;
16
+ /** Markdown rendered as React nodes: raw HTML and automatic image loads are disabled. */
17
+ export declare function TaskMarkdown({ source }: {
18
+ source: string;
19
+ }): import("react").JSX.Element;
@@ -12,6 +12,7 @@ import type { Context as ClientContext } from '@deepseek-ai/cordis';
12
12
  import type { ConfigForm, ConfigForms } from '@deepseek-ai/dsh-client-ui-settings/client';
13
13
  import { type TaskBoardSettings } from './TaskBoardSettingsCard.tsx';
14
14
  import { type TaskBoardKey } from './locales.ts';
15
+ import { type TaskBoardCardDecorationProps, type TaskBoardDetailSectionProps, type TaskBoardSettingsSectionProps } from '../core/extension.ts';
15
16
  /** Domain-owned description of one settings namespace a family card binds. */
16
17
  export interface SettingsFormSpec<T> {
17
18
  /** Settings namespace the card edits. */
@@ -50,6 +51,24 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
50
51
  scope: 'root';
51
52
  owner: SettingsPluginItemOwnerProps;
52
53
  };
54
+ /** Provider task-detail section; the board declares and renders it. */
55
+ 'task-board.detail.section': {
56
+ kind: 'list';
57
+ scope: 'root';
58
+ owner: TaskBoardDetailSectionProps;
59
+ };
60
+ /** Provider settings section; the board declares and renders it. */
61
+ 'task-board.settings.section': {
62
+ kind: 'list';
63
+ scope: 'root';
64
+ owner: TaskBoardSettingsSectionProps;
65
+ };
66
+ /** Provider card decoration; the board declares and renders it. */
67
+ 'task-board.card.decoration': {
68
+ kind: 'list';
69
+ scope: 'root';
70
+ owner: TaskBoardCardDecorationProps;
71
+ };
53
72
  }
54
73
  }
55
74
  /** Owner share of a plugin card (the section supplies nothing). */
@@ -69,11 +88,10 @@ declare module '@deepseek-ai/cordis' {
69
88
  }
70
89
  /**
71
90
  * Required services (fiber inject waiting — the runtime must be up first).
72
- * The generated remote faces are probed at use time instead of injected:
73
- * `remote.agentPresets` only registers on 0.1.2-alpha.2 hosts (the
74
- * api-remotes contribution), so a hard wait would pend the entry forever
75
- * on hosts below that cohort, which serve the same roster through the
76
- * connection RPC face.
91
+ * The generated remote namespaces are not all hard injects: `remote.session`
92
+ * is one, while `remote.agentPresets` is reached through a scoped inject at
93
+ * use time (see mountUi), because the namespace mounts with the official
94
+ * api-remotes assembly applied beside this entry rather than before it.
77
95
  */
78
96
  export declare const inject: string[];
79
97
  /**