@linxin666/dsh-client-ui-task-board 0.4.3 → 0.4.5

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 (128) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +56 -18
  3. package/README.zh.md +56 -18
  4. package/lib/client.js +6242 -958
  5. package/lib/client.js.map +1 -1
  6. package/lib/index.js +3475 -234
  7. package/lib/types/client/PluginSettingsCard.d.ts +7 -0
  8. package/lib/types/client/TaskBoardSettingsCard.d.ts +19 -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 +23 -2
  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/preset-label.d.ts +22 -0
  24. package/lib/types/client/board/status-key.d.ts +15 -0
  25. package/lib/types/client/board/task-markdown.d.ts +19 -0
  26. package/lib/types/client/board-card-seat.d.ts +34 -0
  27. package/lib/types/client/host-api.d.ts +24 -1
  28. package/lib/types/client/index.d.ts +19 -0
  29. package/lib/types/client/locales.d.ts +141 -3
  30. package/lib/types/client/native-panel.d.ts +6 -20
  31. package/lib/types/client/schedule-zone.d.ts +47 -0
  32. package/lib/types/client/seats.d.ts +18 -0
  33. package/lib/types/client/service.d.ts +32 -0
  34. package/lib/types/client/settings-form.d.ts +16 -0
  35. package/lib/types/core/controller.d.ts +38 -1
  36. package/lib/types/core/degraded-reason.d.ts +46 -0
  37. package/lib/types/core/extension.d.ts +283 -0
  38. package/lib/types/core/handover.d.ts +13 -1
  39. package/lib/types/core/issue-form.d.ts +16 -0
  40. package/lib/types/core/package-name.d.ts +9 -0
  41. package/lib/types/core/schedule.d.ts +95 -20
  42. package/lib/types/core/subtask.d.ts +30 -11
  43. package/lib/types/core/tasks.d.ts +110 -5
  44. package/lib/types/core/use-cases/task-create.d.ts +2 -1
  45. package/lib/types/core/use-cases/task-schedule.d.ts +8 -4
  46. package/lib/types/core/use-cases/task-tag.d.ts +48 -0
  47. package/lib/types/core/use-cases/task-update.d.ts +5 -3
  48. package/lib/types/core/verification.d.ts +386 -0
  49. package/lib/types/core/workspace-target.d.ts +41 -0
  50. package/lib/types/host/agent-tools.d.ts +1 -1
  51. package/lib/types/host/extension-registry.d.ts +111 -0
  52. package/lib/types/host/llm-dispatch.d.ts +63 -0
  53. package/lib/types/host/verification-gate.d.ts +71 -0
  54. package/lib/types/host/verification-runner.d.ts +108 -0
  55. package/lib/types/host/workspace-evidence.d.ts +53 -0
  56. package/lib/types/host-ledger.d.ts +107 -11
  57. package/lib/types/host-runner.d.ts +99 -4
  58. package/lib/types/host-service.d.ts +114 -2
  59. package/lib/types/index.d.ts +80 -26
  60. package/lib/types/protocol.d.ts +53 -3
  61. package/lib/types/tool-surface.d.ts +71 -0
  62. package/package.json +27 -26
  63. package/src/client/PluginSettingsCard.tsx +9 -0
  64. package/src/client/TaskBoardSettingsCard.tsx +304 -65
  65. package/src/client/board/ClampedMarkdown.tsx +69 -0
  66. package/src/client/board/ConfirmDialog.tsx +7 -3
  67. package/src/client/board/EditTaskModal.tsx +5 -2
  68. package/src/client/board/LinkSubtaskModal.tsx +30 -15
  69. package/src/client/board/NewTaskModal.tsx +288 -151
  70. package/src/client/board/TagManagerModal.tsx +235 -0
  71. package/src/client/board/TaskBoard.tsx +259 -103
  72. package/src/client/board/TaskCard.tsx +238 -86
  73. package/src/client/board/TaskDetail.tsx +154 -59
  74. package/src/client/board/TaskForm.tsx +61 -6
  75. package/src/client/board/VerificationReport.tsx +203 -0
  76. package/src/client/board/card-view.ts +220 -0
  77. package/src/client/board/icons.tsx +89 -0
  78. package/src/client/board/overlay.tsx +189 -0
  79. package/src/client/board/preset-label.ts +44 -0
  80. package/src/client/board/status-key.ts +27 -0
  81. package/src/client/board/task-markdown.module.css +137 -0
  82. package/src/client/board/task-markdown.tsx +106 -0
  83. package/src/client/board-card-seat.ts +102 -0
  84. package/src/client/board-settings.module.css +66 -0
  85. package/src/client/board.module.css +602 -68
  86. package/src/client/host-api.ts +109 -6
  87. package/src/client/index.ts +48 -6
  88. package/src/client/locales.ts +290 -12
  89. package/src/client/native-panel.tsx +18 -2
  90. package/src/client/schedule-zone.ts +135 -0
  91. package/src/client/seats.tsx +58 -0
  92. package/src/client/service.ts +94 -0
  93. package/src/client/settings-card.module.css +31 -5
  94. package/src/client/settings-form.ts +27 -1
  95. package/src/core/controller.ts +101 -5
  96. package/src/core/degraded-reason.ts +81 -0
  97. package/src/core/extension.ts +376 -0
  98. package/src/core/freeze-snapshot.ts +2 -2
  99. package/src/core/handover.ts +16 -1
  100. package/src/core/issue-form.ts +19 -0
  101. package/src/core/package-name.ts +9 -0
  102. package/src/core/schedule.ts +211 -55
  103. package/src/core/store.ts +26 -1
  104. package/src/core/subtask.ts +77 -22
  105. package/src/core/tasks.ts +138 -10
  106. package/src/core/use-cases/task-create.ts +14 -2
  107. package/src/core/use-cases/task-parent.ts +3 -2
  108. package/src/core/use-cases/task-schedule.ts +17 -6
  109. package/src/core/use-cases/task-tag.ts +110 -0
  110. package/src/core/use-cases/task-update.ts +13 -4
  111. package/src/core/verification.ts +826 -0
  112. package/src/core/workspace-target.ts +59 -0
  113. package/src/host/agent-tools.ts +58 -24
  114. package/src/host/extension-registry.ts +509 -0
  115. package/src/host/llm-dispatch.ts +87 -0
  116. package/src/host/verification-gate.ts +331 -0
  117. package/src/host/verification-runner.ts +535 -0
  118. package/src/host/workspace-evidence.ts +176 -0
  119. package/src/host-ai.ts +15 -9
  120. package/src/host-ledger.ts +342 -51
  121. package/src/host-routes.ts +15 -2
  122. package/src/host-runner.ts +219 -20
  123. package/src/host-service.ts +346 -26
  124. package/src/index.ts +204 -16
  125. package/src/protocol.ts +122 -9
  126. package/src/tool-surface.ts +95 -0
  127. package/lib/types/host/run-guarded.d.ts +0 -39
  128. package/src/host/run-guarded.ts +0 -76
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Label manager: the board's label row can only shrink through here.
3
+ *
4
+ * The filter row is the union of every label in use across the ledger, so a
5
+ * label created once — by an experiment, a pasted task, an import — stayed on
6
+ * the board forever: there was no way to rename a typo, merge two labels that
7
+ * mean the same thing, or remove one nobody uses. This dialog is that way out.
8
+ * It lists the labels with their usage counts and offers the two ledger-wide
9
+ * transitions, both of which go through the controller so the Host ledger stays
10
+ * the authority (and a shared board stays consistent for everyone watching it).
11
+ *
12
+ * Renaming onto a name already in use merges the two labels; the dialog says so
13
+ * before the edit is submitted. Deleting is confirmed first, and the
14
+ * confirmation names how many tasks the label is removed from.
15
+ */
16
+ import { useMemo, useState } from 'react'
17
+ import type { BoardController } from '../../core/controller.ts'
18
+ import { TAG_NAME_MAX_LENGTH, collectKnownTags, type TaskRecord } from '../../core/tasks.ts'
19
+ import { t } from '../locales.ts'
20
+ import css from '../board.module.css'
21
+ import { ConfirmDialog } from './ConfirmDialog.tsx'
22
+ import { IconClose } from './icons.tsx'
23
+ import { useDialog, usePresence, type OverlayPhase } from './overlay.tsx'
24
+
25
+ /** One label as the manager lists it: its definition plus how many tasks carry it. */
26
+ export interface TagUsage {
27
+ name: string
28
+ promptPrefix?: string
29
+ /** Tasks carrying the label right now. */
30
+ count: number
31
+ }
32
+
33
+ /**
34
+ * Labels in use, in board order (first occurrence wins, exactly like the
35
+ * filter row), each with its usage count.
36
+ * @param tasks - the whole ledger (board and archive).
37
+ * @returns one row per label.
38
+ */
39
+ export function tagUsage(tasks: readonly TaskRecord[]): TagUsage[] {
40
+ const counts = new Map<string, number>()
41
+ for (const task of tasks) {
42
+ for (const tag of task.tags ?? []) counts.set(tag.name, (counts.get(tag.name) ?? 0) + 1)
43
+ }
44
+ return collectKnownTags(tasks).map(tag => ({
45
+ name: tag.name,
46
+ ...(tag.promptPrefix === undefined ? {} : { promptPrefix: tag.promptPrefix }),
47
+ count: counts.get(tag.name) ?? 0,
48
+ }))
49
+ }
50
+
51
+ /** Label-manager overlay props. */
52
+ export interface TagManagerModalProps {
53
+ controller: BoardController
54
+ onClose: () => void
55
+ /** Which leg of the enter/exit motion pair the surface is on. */
56
+ phase?: OverlayPhase
57
+ }
58
+
59
+ /** Label-manager overlay. */
60
+ export function TagManagerModal({ controller, onClose, phase = 'open' }: TagManagerModalProps) {
61
+ /** The label whose row is being renamed, and the draft name. */
62
+ const [editing, setEditing] = useState<string | undefined>(undefined)
63
+ const [draft, setDraft] = useState('')
64
+ const [busy, setBusy] = useState(false)
65
+ const [error, setError] = useState<string | undefined>(undefined)
66
+ const [confirmDelete, setConfirmDelete] = useState<string | undefined>(undefined)
67
+ const confirmPresence = usePresence(confirmDelete !== undefined)
68
+ // Escape and a backdrop press peel one layer at a time: an open row editor
69
+ // first, then the dialog — so a half-typed rename is never lost to the same
70
+ // key that closes the surface under it.
71
+ const dismiss = (): void => {
72
+ if (editing !== undefined) {
73
+ setEditing(undefined)
74
+ setError(undefined)
75
+ return
76
+ }
77
+ onClose()
78
+ }
79
+ const dialog = useDialog<HTMLDivElement>(dismiss, phase)
80
+ const snapshot = controller.getSnapshot()
81
+ const rows = useMemo(() => tagUsage(snapshot.tasks), [snapshot.tasks])
82
+ const deleting = rows.find(row => row.name === confirmDelete)
83
+ /** The label the typed name would merge into, when it is already in use. */
84
+ const mergesInto = editing === undefined ? undefined : rows.find(row => row.name === draft.trim() && row.name !== editing)
85
+
86
+ const startRename = (row: TagUsage): void => {
87
+ setEditing(row.name)
88
+ setDraft(row.name)
89
+ setError(undefined)
90
+ }
91
+
92
+ const submitRename = async (from: string): Promise<void> => {
93
+ const to = draft.trim()
94
+ if (to === '' || to === from) {
95
+ setEditing(undefined)
96
+ return
97
+ }
98
+ setBusy(true)
99
+ setError(undefined)
100
+ const accepted = await controller.renameTag(from, to)
101
+ setBusy(false)
102
+ if (accepted) {
103
+ setEditing(undefined)
104
+ return
105
+ }
106
+ setError(controller.getSnapshot().transportError ?? t('tags.renameFailed'))
107
+ }
108
+
109
+ const remove = async (name: string): Promise<void> => {
110
+ setBusy(true)
111
+ setError(undefined)
112
+ const accepted = await controller.deleteTag(name)
113
+ setBusy(false)
114
+ if (!accepted) setError(controller.getSnapshot().transportError ?? t('tags.deleteFailed'))
115
+ }
116
+
117
+ return (
118
+ <>
119
+ <div className={css.modalBackdrop} data-state={phase} onMouseDown={dialog.onMouseDown}>
120
+ <div
121
+ ref={dialog.attach}
122
+ className={css.modal}
123
+ role="dialog"
124
+ aria-modal="true"
125
+ aria-label={t('tags.title')}
126
+ tabIndex={-1}
127
+ >
128
+ <header className={css.modalHeader}>
129
+ <h2 className={css.modalTitle}>{t('tags.title')}</h2>
130
+ <button
131
+ type="button"
132
+ className={css.iconButton}
133
+ aria-label={t('detail.close')}
134
+ title={t('detail.close')}
135
+ onClick={onClose}
136
+ >
137
+ <IconClose size={16} />
138
+ </button>
139
+ </header>
140
+
141
+ <div className={css.modalBody}>
142
+ <p className={css.fieldHint}>{t('tags.hint')}</p>
143
+ {rows.length === 0
144
+ ? <p className={css.detailText}>{t('tags.empty')}</p>
145
+ : (
146
+ <ul className={css.tagManageList}>
147
+ {rows.map(row => (
148
+ <li key={row.name} className={css.tagManageRow}>
149
+ {editing === row.name
150
+ ? (
151
+ <>
152
+ <input
153
+ className={css.input + ' ' + css.tagManageInput}
154
+ value={draft}
155
+ maxLength={TAG_NAME_MAX_LENGTH}
156
+ spellCheck={false}
157
+ aria-label={t('tags.renameLabel', { name: row.name })}
158
+ onChange={event => { setDraft(event.target.value); setError(undefined) }}
159
+ onKeyDown={event => {
160
+ if (event.key === 'Enter') void submitRename(row.name)
161
+ }}
162
+ />
163
+ <button
164
+ type="button"
165
+ className={css.primaryButton}
166
+ disabled={busy || draft.trim() === ''}
167
+ onClick={() => { void submitRename(row.name) }}
168
+ >
169
+ {t('tags.save')}
170
+ </button>
171
+ <button
172
+ type="button"
173
+ className={css.ghostButton}
174
+ disabled={busy}
175
+ onClick={() => { setEditing(undefined); setError(undefined) }}
176
+ >
177
+ {t('new.cancel')}
178
+ </button>
179
+ </>
180
+ )
181
+ : (
182
+ <>
183
+ <span className={css.tagManageName}>{row.name}</span>
184
+ <span className={css.tagManageCount}>{t('tags.usage', { count: String(row.count) })}</span>
185
+ <button
186
+ type="button"
187
+ className={css.ghostButton}
188
+ disabled={busy}
189
+ onClick={() => { startRename(row) }}
190
+ >
191
+ {t('tags.rename')}
192
+ </button>
193
+ <button
194
+ type="button"
195
+ className={css.ghostButton}
196
+ disabled={busy}
197
+ onClick={() => { setConfirmDelete(row.name) }}
198
+ >
199
+ {t('tags.delete')}
200
+ </button>
201
+ </>
202
+ )}
203
+ {editing === row.name && mergesInto !== undefined && (
204
+ <span className={css.tagManageMerge}>{t('tags.mergeHint', { name: mergesInto.name })}</span>
205
+ )}
206
+ </li>
207
+ ))}
208
+ </ul>
209
+ )}
210
+ {error !== undefined && <p className={css.formError}>{error}</p>}
211
+ </div>
212
+
213
+ <footer className={css.modalFooter}>
214
+ <button type="button" className={css.ghostButton} onClick={onClose}>{t('tags.done')}</button>
215
+ </footer>
216
+ </div>
217
+ </div>
218
+
219
+ {confirmPresence.mounted && deleting !== undefined && (
220
+ <ConfirmDialog
221
+ title={t('tags.deleteTitle')}
222
+ message={t('tags.deleteConfirm', { name: deleting.name, count: String(deleting.count) })}
223
+ confirmLabel={t('tags.deleteOk')}
224
+ danger
225
+ phase={confirmPresence.phase}
226
+ onCancel={() => { setConfirmDelete(undefined) }}
227
+ onConfirm={() => {
228
+ setConfirmDelete(undefined)
229
+ void remove(deleting.name)
230
+ }}
231
+ />
232
+ )}
233
+ </>
234
+ )
235
+ }