@joekytc/dsh-swarm 0.1.0

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 (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +504 -0
  3. package/README.zh-CN.md +452 -0
  4. package/client/BoardCard.tsx +41 -0
  5. package/client/ConnectionBanner.tsx +16 -0
  6. package/client/KanbanBoard.tsx +168 -0
  7. package/client/KanbanTab.tsx +26 -0
  8. package/client/RenameModal.tsx +38 -0
  9. package/client/TaskDrawer.tsx +212 -0
  10. package/client/WorkflowRail.tsx +175 -0
  11. package/client/board-store.ts +196 -0
  12. package/client/css.d.ts +4 -0
  13. package/client/index.ts +31 -0
  14. package/client/kanban.css +653 -0
  15. package/client/useKanbanBoard.ts +7 -0
  16. package/client/workflow-model.ts +229 -0
  17. package/cordis.patch.yml +88 -0
  18. package/lib/client.js +1205 -0
  19. package/lib/config.d.ts +46 -0
  20. package/lib/config.js +43 -0
  21. package/lib/dispatcher/agent-runner.d.ts +23 -0
  22. package/lib/dispatcher/agent-runner.js +429 -0
  23. package/lib/dispatcher/chain-auditor.d.ts +47 -0
  24. package/lib/dispatcher/chain-auditor.js +194 -0
  25. package/lib/dispatcher/dispatcher.d.ts +50 -0
  26. package/lib/dispatcher/dispatcher.js +280 -0
  27. package/lib/dispatcher/event-waker.d.ts +13 -0
  28. package/lib/dispatcher/event-waker.js +24 -0
  29. package/lib/dispatcher/git-credentials.d.ts +33 -0
  30. package/lib/dispatcher/git-credentials.js +78 -0
  31. package/lib/dispatcher/merge-gate.d.ts +28 -0
  32. package/lib/dispatcher/merge-gate.js +74 -0
  33. package/lib/dispatcher/model-candidates.d.ts +13 -0
  34. package/lib/dispatcher/model-candidates.js +31 -0
  35. package/lib/dispatcher/session-events.d.ts +28 -0
  36. package/lib/dispatcher/session-events.js +33 -0
  37. package/lib/dispatcher/target-repo.d.ts +15 -0
  38. package/lib/dispatcher/target-repo.js +42 -0
  39. package/lib/dispatcher/v-orchestrator.d.ts +74 -0
  40. package/lib/dispatcher/v-orchestrator.js +452 -0
  41. package/lib/dispatcher/watchdog.d.ts +15 -0
  42. package/lib/dispatcher/watchdog.js +30 -0
  43. package/lib/dispatcher/workspace-attach.d.ts +40 -0
  44. package/lib/dispatcher/workspace-attach.js +112 -0
  45. package/lib/domain/delivery-contract.d.ts +18 -0
  46. package/lib/domain/delivery-contract.js +80 -0
  47. package/lib/domain/delivery-evidence.d.ts +12 -0
  48. package/lib/domain/delivery-evidence.js +41 -0
  49. package/lib/domain/event-store.d.ts +20 -0
  50. package/lib/domain/event-store.js +53 -0
  51. package/lib/domain/kanban-service.d.ts +94 -0
  52. package/lib/domain/kanban-service.js +430 -0
  53. package/lib/domain/permissions.d.ts +7 -0
  54. package/lib/domain/permissions.js +54 -0
  55. package/lib/domain/planning-checklist.d.ts +22 -0
  56. package/lib/domain/planning-checklist.js +81 -0
  57. package/lib/domain/prefetch-manifest.d.ts +21 -0
  58. package/lib/domain/prefetch-manifest.js +73 -0
  59. package/lib/domain/projection.d.ts +3 -0
  60. package/lib/domain/projection.js +165 -0
  61. package/lib/domain/review-evidence.d.ts +13 -0
  62. package/lib/domain/review-evidence.js +76 -0
  63. package/lib/domain/state-machine.d.ts +4 -0
  64. package/lib/domain/state-machine.js +32 -0
  65. package/lib/domain/task-parents.d.ts +17 -0
  66. package/lib/domain/task-parents.js +39 -0
  67. package/lib/domain/tdd-classify.d.ts +5 -0
  68. package/lib/domain/tdd-classify.js +20 -0
  69. package/lib/domain/types.d.ts +142 -0
  70. package/lib/domain/types.js +2 -0
  71. package/lib/index.d.ts +5 -0
  72. package/lib/index.js +56 -0
  73. package/lib/roles/preset-installer.d.ts +8 -0
  74. package/lib/roles/preset-installer.js +53 -0
  75. package/lib/roles/toolsets.d.ts +62 -0
  76. package/lib/roles/toolsets.js +332 -0
  77. package/lib/roles/wiki-worker.d.ts +20 -0
  78. package/lib/roles/wiki-worker.js +44 -0
  79. package/lib/routes/kanban-http.d.ts +6 -0
  80. package/lib/routes/kanban-http.js +159 -0
  81. package/lib/routes/kanban-sse.d.ts +8 -0
  82. package/lib/routes/kanban-sse.js +58 -0
  83. package/lib/routes/planning-driver.d.ts +16 -0
  84. package/lib/routes/planning-driver.js +52 -0
  85. package/lib/routes/prefix-router.d.ts +29 -0
  86. package/lib/routes/prefix-router.js +28 -0
  87. package/lib/services/kanban-provider.d.ts +16 -0
  88. package/lib/services/kanban-provider.js +14 -0
  89. package/lib/tools/kanban-tools.d.ts +11 -0
  90. package/lib/tools/kanban-tools.js +169 -0
  91. package/lib/tools/main-session-tools.d.ts +20 -0
  92. package/lib/tools/main-session-tools.js +170 -0
  93. package/lib/tools/planning-tools.d.ts +32 -0
  94. package/lib/tools/planning-tools.js +102 -0
  95. package/lib/tools/prefetch-tools.d.ts +5 -0
  96. package/lib/tools/prefetch-tools.js +59 -0
  97. package/lib/tools/spec-card-tools.d.ts +4 -0
  98. package/lib/tools/spec-card-tools.js +75 -0
  99. package/lib/tools/wiki-tools.d.ts +4 -0
  100. package/lib/tools/wiki-tools.js +57 -0
  101. package/lib/wiki/kb-linkage.d.ts +9 -0
  102. package/lib/wiki/kb-linkage.js +87 -0
  103. package/lib/wiki/page-path.d.ts +6 -0
  104. package/lib/wiki/page-path.js +28 -0
  105. package/lib/wiki/wiki-vault-client.d.ts +28 -0
  106. package/lib/wiki/wiki-vault-client.js +48 -0
  107. package/package.json +83 -0
  108. package/personas/kanban-d/agent.cordis.yml +157 -0
  109. package/personas/kanban-d/preset.yml +2 -0
  110. package/personas/kanban-dt/agent.cordis.yml +71 -0
  111. package/personas/kanban-dt/preset.yml +2 -0
  112. package/personas/kanban-p/agent.cordis.yml +66 -0
  113. package/personas/kanban-p/preset.yml +2 -0
  114. package/personas/kanban-pt/agent.cordis.yml +47 -0
  115. package/personas/kanban-pt/preset.yml +2 -0
  116. package/personas/kanban-v/agent.cordis.yml +47 -0
  117. package/personas/kanban-v/preset.yml +2 -0
  118. package/personas/kanban-w/agent.cordis.yml +47 -0
  119. package/personas/kanban-w/preset.yml +2 -0
  120. package/personas/persona-d.md +26 -0
  121. package/personas/persona-dt.md +18 -0
  122. package/personas/persona-p.md +13 -0
  123. package/personas/persona-pt.md +13 -0
  124. package/personas/persona-v.md +18 -0
  125. package/personas/persona-w.md +12 -0
@@ -0,0 +1,653 @@
1
+ /* T32:卡片/连接线等局部过渡保持 120-180ms;prefers-reduced-motion 全局关闭 */
2
+ .dsh-kb-tab {
3
+ /* DSH 设计令牌(浅/深色随 body[data-ds-dark-theme] 自动切换)——禁止硬编码颜色 */
4
+ --dsh-kb-surface: var(--dsw-alias-bg-layer-1);
5
+ --dsh-kb-surface-elev: var(--dsw-alias-bg-layer-3);
6
+ --dsh-kb-text-primary: var(--dsw-alias-label-primary);
7
+ --dsh-kb-text-secondary: var(--dsw-alias-label-secondary);
8
+ --dsh-kb-text-tertiary: var(--dsw-alias-label-tertiary);
9
+ --dsh-kb-border: var(--dsw-alias-border-l2);
10
+ --dsh-kb-hover: var(--dsw-alias-interactive-bg-hover);
11
+ --dsh-kb-mask: var(--dsw-alias-bg-mask-3);
12
+ --dsh-kb-input: var(--dsw-specific-input-major);
13
+ --dsh-kb-inline-code: var(--dsw-alias-markdown-inline-code);
14
+ /* 大圆角契约(对齐 DSH 卡片 16px),后续 UI 一律复用 */
15
+ --dsh-kb-radius-lg: 16px;
16
+ --dsh-kb-radius-md: 10px;
17
+ --dsh-kb-radius-sm: 8px;
18
+ /* 状态色 → DSH 语义令牌 */
19
+ --dsh-kb-complete: var(--dsw-alias-state-success-primary);
20
+ --dsh-kb-active: var(--dsw-alias-state-business-primary);
21
+ --dsh-kb-blocked: var(--dsw-alias-state-error-primary);
22
+ --dsh-kb-pending: currentColor;
23
+ /* 角色色:DSH 有等价令牌则复用;p/pt/dt 无 DSH 等价色,保留原值 */
24
+ --dsh-kb-v: var(--dsw-alias-state-business-primary);
25
+ --dsh-kb-p: #a855f7;
26
+ --dsh-kb-w: var(--dsw-alias-state-success-primary);
27
+ --dsh-kb-d: var(--dsw-alias-state-warn-primary);
28
+ --dsh-kb-pt: #6366f1; /* 计划评审:靛/浅蓝 */
29
+ --dsh-kb-dt: #ec4899; /* 实现评审:玫红 */
30
+ height: 100%;
31
+ width: 100%;
32
+ min-width: 715px; /* 看板最小宽度 */
33
+ max-width: 780px; /* 看板最大宽度 */
34
+ margin-inline: auto;
35
+ display: flex;
36
+ flex-direction: column;
37
+ color: inherit;
38
+ background: var(--dsw-alias-bg-base);
39
+ pointer-events: auto;
40
+ font-size: 13px;
41
+ line-height: 1.45;
42
+ overflow: hidden;
43
+ }
44
+
45
+ .dsh-kb-banner {
46
+ flex: 0 0 auto;
47
+ padding: 6px 12px;
48
+ font-size: 12px;
49
+ border-bottom: 1px solid var(--dsh-kb-border);
50
+ }
51
+ .dsh-kb-banner--reconnecting,
52
+ .dsh-kb-banner--loading {
53
+ color: var(--dsh-kb-active);
54
+ }
55
+ .dsh-kb-banner--error {
56
+ color: var(--dsh-kb-blocked);
57
+ }
58
+
59
+ .dsh-kb-tab-body {
60
+ flex: 1 1 auto;
61
+ min-height: 0;
62
+ overflow: auto;
63
+ padding: 8px;
64
+ }
65
+
66
+ .dsh-kb-loading {
67
+ flex: 1 1 auto;
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ color: currentColor;
72
+ opacity: 0.7;
73
+ }
74
+
75
+ .dsh-kb-toolbar {
76
+ position: sticky;
77
+ top: 0;
78
+ z-index: 1;
79
+ padding-bottom: 8px;
80
+ }
81
+ .dsh-kb-toolbar input {
82
+ width: 100%;
83
+ box-sizing: border-box;
84
+ padding: 6px 10px;
85
+ border: 1px solid var(--dsh-kb-border);
86
+ border-radius: var(--dsh-kb-radius-md);
87
+ background: var(--dsh-kb-input);
88
+ color: var(--dsh-kb-text-primary);
89
+ }
90
+
91
+ .dsh-kb-rail {
92
+ display: flex;
93
+ flex-direction: column;
94
+ }
95
+ /* 状态筛选 chip 组:执行中/阻塞/失败/已完成(多选并集;空=默认视图) */
96
+ .dsh-kb-filters {
97
+ display: flex;
98
+ flex-wrap: wrap;
99
+ gap: 6px;
100
+ padding: 0 0 8px;
101
+ }
102
+ .dsh-kb-filter {
103
+ display: inline-flex;
104
+ align-items: center;
105
+ padding: 2px 10px;
106
+ border: 1px solid var(--dsw-alias-border-l2);
107
+ border-radius: 999px;
108
+ background: transparent;
109
+ color: currentColor;
110
+ font: inherit;
111
+ cursor: pointer;
112
+ user-select: none;
113
+ transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease;
114
+ }
115
+ .dsh-kb-filter:hover {
116
+ background: var(--dsh-kb-hover);
117
+ }
118
+ .dsh-kb-filter--active {
119
+ border-color: var(--dsh-kb-active);
120
+ color: var(--dsh-kb-active);
121
+ background: color-mix(in srgb, var(--dsh-kb-active) 10%, transparent);
122
+ }
123
+
124
+ .dsh-kb-chain {
125
+ margin-bottom: 10px;
126
+ }
127
+ .dsh-kb-chain__title {
128
+ display: flex;
129
+ align-items: center;
130
+ gap: 8px;
131
+ box-sizing: border-box;
132
+ width: 100%;
133
+ padding: 6px 8px;
134
+ border: 0;
135
+ border-radius: var(--dsh-kb-radius-sm);
136
+ background: transparent;
137
+ color: var(--dsh-kb-text-primary);
138
+ font: inherit;
139
+ text-align: left;
140
+ cursor: pointer;
141
+ transition: background-color 150ms ease;
142
+ }
143
+ .dsh-kb-chain__title:hover {
144
+ background: var(--dsh-kb-hover);
145
+ }
146
+ /* 每条链路前的折叠箭头(与 DSH 折叠交互一致:细 chevron,展开时旋转,150ms 过渡) */
147
+ .dsh-kb-chain__chevron {
148
+ flex: 0 0 auto;
149
+ width: 7px;
150
+ height: 7px;
151
+ border-top: 1.5px solid currentColor;
152
+ border-right: 1.5px solid currentColor;
153
+ transform: rotate(45deg);
154
+ opacity: 0.7;
155
+ transition: transform 150ms ease;
156
+ }
157
+ .dsh-kb-chain__title[aria-expanded='true'] .dsh-kb-chain__chevron {
158
+ transform: rotate(135deg);
159
+ }
160
+ .dsh-kb-chain__name {
161
+ flex: 1 1 auto;
162
+ overflow: hidden;
163
+ text-overflow: ellipsis;
164
+ white-space: nowrap;
165
+ }
166
+ .dsh-kb-chain__meta {
167
+ flex: 0 0 auto;
168
+ white-space: nowrap;
169
+ opacity: 0.7;
170
+ }
171
+ .dsh-kb-chain__warning {
172
+ padding: 4px 8px;
173
+ margin: 2px 0 6px;
174
+ border-left: 3px solid var(--dsh-kb-blocked);
175
+ color: var(--dsh-kb-blocked);
176
+ overflow: hidden;
177
+ text-overflow: ellipsis;
178
+ white-space: nowrap;
179
+ }
180
+ /* D23:链完成验收核对警告行(completed 链未确认前阻塞最终汇报) */
181
+ .dsh-kb-chain__audit {
182
+ display: flex;
183
+ align-items: center;
184
+ gap: 8px;
185
+ white-space: normal;
186
+ }
187
+ .dsh-kb-chain__audit span {
188
+ flex: 1 1 auto;
189
+ overflow: hidden;
190
+ }
191
+ .dsh-kb-audit-confirm {
192
+ flex: 0 0 auto;
193
+ border: 1px solid var(--dsh-kb-blocked);
194
+ border-radius: var(--dsh-kb-radius-sm);
195
+ background: transparent;
196
+ color: var(--dsh-kb-blocked);
197
+ padding: 2px 10px;
198
+ cursor: pointer;
199
+ font: inherit;
200
+ transition: background-color 150ms ease, color 150ms ease;
201
+ }
202
+ .dsh-kb-audit-confirm:hover {
203
+ background: var(--dsh-kb-blocked);
204
+ color: #fff;
205
+ }
206
+
207
+ .dsh-kb-nodes {
208
+ list-style: none;
209
+ margin: 0;
210
+ padding: 0 0 0 12px;
211
+ display: flex;
212
+ flex-direction: column;
213
+ gap: 0;
214
+ }
215
+ .dsh-kb-node {
216
+ position: relative;
217
+ padding-left: 12px;
218
+ }
219
+ .dsh-kb-node::before {
220
+ content: '';
221
+ position: absolute;
222
+ left: 0;
223
+ top: 0;
224
+ bottom: 0;
225
+ width: 2px;
226
+ background: var(--dsh-kb-pending);
227
+ transition: background-color 150ms ease;
228
+ }
229
+ .dsh-kb-node--complete::before { background: var(--dsh-kb-complete); }
230
+ .dsh-kb-node--active::before { background: var(--dsh-kb-active); }
231
+ .dsh-kb-node--pending::before {
232
+ background: repeating-linear-gradient(to bottom, currentColor 0 3px, transparent 3px 6px);
233
+ }
234
+ .dsh-kb-node--blocked::before {
235
+ background: repeating-linear-gradient(to bottom, var(--dsh-kb-blocked) 0 4px, transparent 4px 8px);
236
+ }
237
+
238
+ .dsh-kb-task {
239
+ display: grid;
240
+ grid-template-columns: 22px 1fr auto;
241
+ grid-template-rows: auto auto;
242
+ gap: 2px 8px;
243
+ width: 100%;
244
+ box-sizing: border-box;
245
+ padding: 6px 8px;
246
+ margin: 4px 0;
247
+ border: 1px solid transparent;
248
+ border-radius: var(--dsh-kb-radius-lg);
249
+ background: transparent;
250
+ color: var(--dsh-kb-text-primary);
251
+ font: inherit;
252
+ text-align: left;
253
+ cursor: pointer;
254
+ transition: border-color 150ms ease, background-color 150ms ease, opacity 150ms ease;
255
+ }
256
+ .dsh-kb-task:hover {
257
+ background: var(--dsh-kb-hover);
258
+ }
259
+ .dsh-kb-task[data-selected],
260
+ .dsh-kb-task--active {
261
+ border-color: var(--dsh-kb-active);
262
+ }
263
+ /* T32:完成任务保留在轨道但视觉降权;状态色不变、不整卡染色 */
264
+ .dsh-kb-task--complete {
265
+ opacity: 0.72;
266
+ }
267
+ .dsh-kb-task__title {
268
+ grid-column: 2;
269
+ overflow: hidden;
270
+ text-overflow: ellipsis;
271
+ white-space: nowrap;
272
+ }
273
+ .dsh-kb-task__status {
274
+ grid-column: 3;
275
+ color: currentColor;
276
+ opacity: 0.75;
277
+ }
278
+ .dsh-kb-task__meta {
279
+ grid-column: 2 / 4;
280
+ opacity: 0.6;
281
+ overflow: hidden;
282
+ text-overflow: ellipsis;
283
+ white-space: nowrap;
284
+ }
285
+ .dsh-kb-task__warn {
286
+ grid-column: 2 / 4;
287
+ display: inline-flex;
288
+ align-items: center;
289
+ gap: 4px;
290
+ color: var(--dsh-kb-blocked);
291
+ overflow: hidden;
292
+ text-overflow: ellipsis;
293
+ white-space: nowrap;
294
+ }
295
+ .dsh-kb-task__warn svg {
296
+ flex: 0 0 auto;
297
+ }
298
+ .dsh-kb-task__warn span {
299
+ overflow: hidden;
300
+ text-overflow: ellipsis;
301
+ white-space: nowrap;
302
+ }
303
+ .dsh-kb-task--complete .dsh-kb-task__status { color: var(--dsh-kb-complete); }
304
+ .dsh-kb-task--blocked .dsh-kb-task__status { color: var(--dsh-kb-blocked); }
305
+ /* D17:选中任务所在链路的上游/当前/下游路径卡高亮;不改变连接线语义,选中卡保持强边框 */
306
+ .dsh-kb-task--related:not(.dsh-kb-task--active) {
307
+ border-color: color-mix(in srgb, var(--dsh-kb-active) 35%, transparent);
308
+ background: color-mix(in srgb, var(--dsh-kb-active) 6%, transparent);
309
+ }
310
+
311
+ .dsh-kb-empty {
312
+ padding: 24px 12px;
313
+ text-align: center;
314
+ color: var(--dsh-kb-text-secondary);
315
+ border: 1px dashed var(--dsh-kb-border);
316
+ border-radius: var(--dsh-kb-radius-lg);
317
+ }
318
+
319
+ .dsh-kb-profile {
320
+ grid-row: 1 / 3;
321
+ align-self: start;
322
+ width: 20px;
323
+ height: 20px;
324
+ border-radius: var(--dsh-kb-radius-sm);
325
+ display: inline-flex;
326
+ align-items: center;
327
+ justify-content: center;
328
+ font-size: 11px;
329
+ font-weight: 700;
330
+ color: #fff;
331
+ }
332
+ .dsh-kb-profile--v { background: var(--dsh-kb-v); }
333
+ .dsh-kb-profile--p { background: var(--dsh-kb-p); }
334
+ .dsh-kb-profile--w { background: var(--dsh-kb-w); }
335
+ .dsh-kb-profile--d { background: var(--dsh-kb-d); }
336
+ .dsh-kb-profile--pt { background: var(--dsh-kb-pt); }
337
+ .dsh-kb-profile--dt { background: var(--dsh-kb-dt); }
338
+
339
+ /* T27:任务卡弹窗(原位详情)——DSH 面板风格:大圆角 + 抬升表面 + 浅/深色令牌 */
340
+ .dsh-kb-detail {
341
+ display: flex;
342
+ flex-direction: column;
343
+ min-height: calc(100% - 16px);
344
+ margin: 8px;
345
+ padding: 12px 16px;
346
+ box-sizing: border-box;
347
+ border: 1px solid var(--dsh-kb-border);
348
+ border-radius: var(--dsh-kb-radius-lg);
349
+ background: var(--dsh-kb-surface);
350
+ color: var(--dsh-kb-text-primary);
351
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
352
+ }
353
+ .dsh-kb-detail__header {
354
+ display: flex;
355
+ align-items: center;
356
+ gap: 8px;
357
+ padding-bottom: 8px;
358
+ border-bottom: 1px solid var(--dsh-kb-border);
359
+ }
360
+ .dsh-kb-detail__header > button:not(.dsh-kb-unread) {
361
+ flex: 0 0 auto;
362
+ width: 28px;
363
+ height: 28px;
364
+ border: 1px solid var(--dsh-kb-border);
365
+ border-radius: var(--dsh-kb-radius-md);
366
+ background: transparent;
367
+ color: var(--dsh-kb-text-primary);
368
+ cursor: pointer;
369
+ font: inherit;
370
+ line-height: 1;
371
+ transition: background-color 150ms ease;
372
+ }
373
+ .dsh-kb-detail__header > button:not(.dsh-kb-unread):hover {
374
+ background: var(--dsh-kb-hover);
375
+ }
376
+ .dsh-kb-detail__identity {
377
+ flex: 1 1 auto;
378
+ min-width: 0;
379
+ }
380
+ .dsh-kb-detail__identity strong,
381
+ .dsh-kb-detail__identity span {
382
+ display: block;
383
+ overflow: hidden;
384
+ text-overflow: ellipsis;
385
+ white-space: nowrap;
386
+ }
387
+ .dsh-kb-detail__actions {
388
+ display: flex;
389
+ gap: 4px;
390
+ flex: 0 0 auto;
391
+ }
392
+ .dsh-kb-detail__actions button {
393
+ border: 1px solid var(--dsh-kb-border);
394
+ border-radius: var(--dsh-kb-radius-md);
395
+ background: transparent;
396
+ color: var(--dsh-kb-text-primary);
397
+ padding: 4px 10px;
398
+ cursor: pointer;
399
+ font: inherit;
400
+ transition: border-color 150ms ease, color 150ms ease, background-color 150ms ease;
401
+ }
402
+ .dsh-kb-detail__actions button:hover {
403
+ background: var(--dsh-kb-hover);
404
+ }
405
+ .dsh-kb-detail__actions button[data-confirming='true'] {
406
+ border-color: var(--dsh-kb-blocked);
407
+ color: var(--dsh-kb-blocked);
408
+ }
409
+ .dsh-kb-detail__actions button:disabled {
410
+ opacity: 0.5;
411
+ cursor: default;
412
+ }
413
+ .dsh-kb-action-form {
414
+ display: inline-flex;
415
+ align-items: center;
416
+ gap: 4px;
417
+ }
418
+ .dsh-kb-action-form input {
419
+ width: 140px;
420
+ padding: 3px 8px;
421
+ border: 1px solid var(--dsh-kb-border);
422
+ border-radius: var(--dsh-kb-radius-sm);
423
+ background: var(--dsh-kb-input);
424
+ color: var(--dsh-kb-text-primary);
425
+ font: inherit;
426
+ font-size: 12px;
427
+ }
428
+ .dsh-kb-unread {
429
+ flex: 0 0 auto;
430
+ border: 1px solid var(--dsh-kb-active);
431
+ border-radius: 999px;
432
+ background: color-mix(in srgb, var(--dsh-kb-active) 12%, transparent);
433
+ color: var(--dsh-kb-active);
434
+ padding: 2px 8px;
435
+ font: inherit;
436
+ font-size: 12px;
437
+ white-space: nowrap;
438
+ cursor: pointer;
439
+ }
440
+ .dsh-kb-action-error {
441
+ display: flex;
442
+ align-items: center;
443
+ gap: 8px;
444
+ padding: 6px 8px;
445
+ margin: 4px 0;
446
+ border-left: 3px solid var(--dsh-kb-blocked);
447
+ color: var(--dsh-kb-blocked);
448
+ font-size: 12px;
449
+ }
450
+ .dsh-kb-action-error span {
451
+ flex: 1 1 auto;
452
+ overflow: hidden;
453
+ text-overflow: ellipsis;
454
+ white-space: nowrap;
455
+ }
456
+ .dsh-kb-action-error button {
457
+ flex: 0 0 auto;
458
+ border: 1px solid currentColor;
459
+ border-radius: var(--dsh-kb-radius-sm);
460
+ background: transparent;
461
+ color: inherit;
462
+ padding: 1px 6px;
463
+ cursor: pointer;
464
+ font: inherit;
465
+ }
466
+ .dsh-kb-detail [role='tablist'] {
467
+ display: flex;
468
+ gap: 4px;
469
+ padding: 8px 0;
470
+ border-bottom: 1px solid var(--dsh-kb-border);
471
+ }
472
+ .dsh-kb-detail [role='tab'] {
473
+ border: 0;
474
+ border-radius: var(--dsh-kb-radius-sm);
475
+ background: transparent;
476
+ color: var(--dsh-kb-text-secondary);
477
+ padding: 4px 10px;
478
+ cursor: pointer;
479
+ transition: background-color 150ms ease, color 150ms ease;
480
+ }
481
+ .dsh-kb-detail [role='tab']:hover {
482
+ color: var(--dsh-kb-text-primary);
483
+ background: var(--dsh-kb-hover);
484
+ }
485
+ .dsh-kb-detail [role='tab'][aria-selected='true'] {
486
+ background: var(--dsh-kb-hover);
487
+ color: var(--dsh-kb-text-primary);
488
+ }
489
+ .dsh-kb-detail [role='tabpanel'] {
490
+ padding: 8px 0;
491
+ overflow: auto;
492
+ }
493
+ .dsh-kb-detail h4 {
494
+ margin: 8px 0 4px;
495
+ font-size: 12px;
496
+ text-transform: none;
497
+ color: var(--dsh-kb-text-secondary);
498
+ }
499
+ .dsh-kb-detail code {
500
+ background: var(--dsh-kb-inline-code);
501
+ border-radius: var(--dsh-kb-radius-sm);
502
+ padding: 1px 4px;
503
+ }
504
+ .dsh-kb-detail dl {
505
+ display: grid;
506
+ grid-template-columns: auto 1fr;
507
+ gap: 4px 8px;
508
+ margin: 8px 0;
509
+ }
510
+ .dsh-kb-detail dt { opacity: 0.7; color: var(--dsh-kb-text-tertiary); }
511
+ .dsh-kb-detail dd { margin: 0; }
512
+ .dsh-kb-detail time { opacity: 0.6; font-size: 12px; margin-inline-end: 4px; }
513
+
514
+ .dsh-kb-spec-attachments {
515
+ list-style: none;
516
+ margin: 4px 0;
517
+ padding: 0;
518
+ display: flex;
519
+ flex-direction: column;
520
+ gap: 4px;
521
+ }
522
+ .dsh-kb-spec-attachments li {
523
+ display: flex;
524
+ align-items: baseline;
525
+ gap: 6px;
526
+ overflow: hidden;
527
+ text-overflow: ellipsis;
528
+ white-space: nowrap;
529
+ }
530
+ .dsh-kb-spec-attachment__kind {
531
+ font-size: 11px;
532
+ border: 1px solid var(--dsh-kb-border);
533
+ border-radius: var(--dsh-kb-radius-sm);
534
+ padding: 0 4px;
535
+ opacity: 0.8;
536
+ }
537
+
538
+ @media (prefers-reduced-motion: reduce) {
539
+ .dsh-kb-tab * {
540
+ transition-duration: 0ms !important;
541
+ }
542
+ }
543
+
544
+ /* 整链删除按钮:默认隐藏,hover 需求标题行时出现(GUI 仅 human,二次确认) */
545
+ .dsh-kb-chain__delete {
546
+ flex: 0 0 auto;
547
+ border: 0;
548
+ background: transparent;
549
+ color: currentColor;
550
+ opacity: 0;
551
+ font: inherit;
552
+ line-height: 1;
553
+ padding: 2px 4px;
554
+ cursor: pointer;
555
+ transition: opacity 150ms ease, color 150ms ease;
556
+ }
557
+ .dsh-kb-chain__title:hover .dsh-kb-chain__delete,
558
+ .dsh-kb-chain__delete:focus-visible { opacity: 0.7; }
559
+ .dsh-kb-chain__delete:hover { opacity: 1; color: var(--dsh-kb-blocked); }
560
+ /* 删除二次确认弹窗(复用 rename 弹窗外壳/圆角契约) */
561
+ .dsh-kb-delete-modal__text {
562
+ color: var(--dsh-kb-text-secondary);
563
+ font-size: 13px;
564
+ line-height: 1.5;
565
+ }
566
+ .dsh-kb-delete-modal__error {
567
+ color: var(--dsh-kb-blocked);
568
+ font-size: 12px;
569
+ }
570
+ .dsh-kb-rename-modal__actions .dsh-kb-delete-confirm {
571
+ border-color: transparent;
572
+ background: var(--dsw-alias-state-error-primary);
573
+ color: #fff;
574
+ }
575
+ /* T7:链标题改名铅笔按钮 + 轻量弹窗(GUI 仅 human;角色卡不可改名) */
576
+ .dsh-kb-chain__rename {
577
+ flex: 0 0 auto;
578
+ border: 0;
579
+ background: transparent;
580
+ color: currentColor;
581
+ opacity: 0.55;
582
+ font: inherit;
583
+ line-height: 1;
584
+ padding: 2px 4px;
585
+ cursor: pointer;
586
+ }
587
+ .dsh-kb-chain__rename:hover { opacity: 1; }
588
+ .dsh-kb-task__status-row {
589
+ display: flex;
590
+ align-items: center;
591
+ gap: 4px;
592
+ }
593
+ .dsh-kb-rename-overlay {
594
+ position: fixed;
595
+ inset: 0;
596
+ z-index: 1000;
597
+ background: var(--dsh-kb-mask);
598
+ display: flex;
599
+ align-items: center;
600
+ justify-content: center;
601
+ }
602
+ .dsh-kb-rename-modal {
603
+ display: flex;
604
+ flex-direction: column;
605
+ gap: 8px;
606
+ min-width: 280px;
607
+ padding: 16px;
608
+ border: 1px solid var(--dsh-kb-border);
609
+ border-radius: var(--dsh-kb-radius-lg);
610
+ background: var(--dsh-kb-surface-elev);
611
+ color: var(--dsh-kb-text-primary);
612
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
613
+ }
614
+ .dsh-kb-rename-modal__label {
615
+ color: var(--dsh-kb-text-primary);
616
+ font-size: 13px;
617
+ font-weight: 600;
618
+ }
619
+ .dsh-kb-rename-modal__input {
620
+ padding: 6px 10px;
621
+ border: 1px solid var(--dsh-kb-border);
622
+ border-radius: var(--dsh-kb-radius-md);
623
+ background: var(--dsh-kb-input);
624
+ color: var(--dsh-kb-text-primary);
625
+ font: inherit;
626
+ }
627
+ .dsh-kb-rename-modal__input:focus {
628
+ outline: none;
629
+ border-color: var(--dsh-kb-active);
630
+ }
631
+ .dsh-kb-rename-modal__actions {
632
+ display: flex;
633
+ justify-content: flex-end;
634
+ gap: 8px;
635
+ }
636
+ .dsh-kb-rename-modal__actions button {
637
+ border: 1px solid var(--dsh-kb-border);
638
+ border-radius: var(--dsh-kb-radius-md);
639
+ background: transparent;
640
+ color: var(--dsh-kb-text-primary);
641
+ font: inherit;
642
+ padding: 4px 14px;
643
+ cursor: pointer;
644
+ transition: background-color 150ms ease;
645
+ }
646
+ .dsh-kb-rename-modal__actions button:hover {
647
+ background: var(--dsh-kb-hover);
648
+ }
649
+ .dsh-kb-rename-modal__actions .dsh-kb-rename-save {
650
+ border-color: transparent;
651
+ background: var(--dsw-alias-button-primary-fill);
652
+ color: var(--dsw-alias-label-primary-foreground);
653
+ }
@@ -0,0 +1,7 @@
1
+ import { useSyncExternalStore } from 'react';
2
+ import type { BoardStore } from './board-store.js';
3
+
4
+ /** 看板外部 store 的 React 桥(T24):只订阅快照,不复制领域状态机。 */
5
+ export function useKanbanBoard(store: BoardStore) {
6
+ return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
7
+ }