dsh-taskboard 0.5.5 → 0.6.1

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 (42) hide show
  1. package/README.md +26 -2
  2. package/lib/client.js +2663 -767
  3. package/lib/host/execution.js +3 -0
  4. package/lib/host/execution.js.map +1 -1
  5. package/lib/host/routes.js +31 -1
  6. package/lib/host/routes.js.map +1 -1
  7. package/lib/host/session-sync.js +210 -10
  8. package/lib/host/session-sync.js.map +1 -1
  9. package/lib/host/store.js +9 -2
  10. package/lib/host/store.js.map +1 -1
  11. package/lib/index.js +100 -1
  12. package/lib/index.js.map +1 -1
  13. package/lib/shared/api.js.map +1 -1
  14. package/lib/shared/protocol.js +19 -1
  15. package/lib/shared/protocol.js.map +1 -1
  16. package/package.json +75 -75
  17. package/src/client/api.ts +8 -0
  18. package/src/client/board/AlertModal.tsx +3 -1
  19. package/src/client/board/ImportModal.tsx +26 -24
  20. package/src/client/board/SettingsModal.tsx +66 -26
  21. package/src/client/board/SlashPromptInput.tsx +340 -0
  22. package/src/client/board/TaskBoard.tsx +53 -49
  23. package/src/client/board/TaskCard.tsx +33 -21
  24. package/src/client/board/TaskDetail.tsx +169 -104
  25. package/src/client/board/TaskFormModal.tsx +254 -202
  26. package/src/client/board/TemplateManager.tsx +32 -29
  27. package/src/client/board/labels.ts +36 -27
  28. package/src/client/controller.ts +62 -2
  29. package/src/client/i18n/en.ts +455 -0
  30. package/src/client/i18n/runtime.ts +155 -0
  31. package/src/client/i18n/zh.ts +460 -0
  32. package/src/client/index.ts +182 -42
  33. package/src/client/sidebar-entry.ts +13 -3
  34. package/src/client/styles.ts +133 -0
  35. package/src/host/execution.ts +13 -0
  36. package/src/host/routes.ts +49 -1
  37. package/src/host/session-sync.ts +334 -14
  38. package/src/host/store.ts +15 -1
  39. package/src/index.ts +115 -1
  40. package/src/shared/api.ts +47 -0
  41. package/src/shared/protocol.ts +41 -0
  42. package/src/shared/version.ts +1 -1
@@ -15,7 +15,8 @@ import { useState } from 'react'
15
15
  import type { BoardController } from '../controller.ts'
16
16
  import type { TaskRecord } from '../../shared/protocol.ts'
17
17
  import { fmtTime, isStaleClaim } from './format.ts'
18
- import { OUTCOME_LABEL, URGENCY_LABEL } from './labels.ts'
18
+ import { OUTCOME_KEYS, URGENCY_KEYS } from './labels.ts'
19
+ import { useT } from '../i18n/runtime.ts'
19
20
 
20
21
  /** dataTransfer type carrying the dragged task id. */
21
22
  export const DRAG_TYPE = 'application/x-dsh-atb-task'
@@ -35,6 +36,7 @@ function shortId(id: string | undefined): string {
35
36
  * @param onAlert - show an alert message (replaces native alert).
36
37
  */
37
38
  export function TaskCard({ task, controller, draggable = false, now, onAlert }: { task: TaskRecord; controller: BoardController; draggable?: boolean; now?: number; onAlert?: (msg: string) => void }) {
39
+ const t = useT()
38
40
  const [rejectOpen, setRejectOpen] = useState(false)
39
41
  const [note, setNote] = useState('')
40
42
  const last = task.executions.length > 0 ? task.executions[task.executions.length - 1] : undefined
@@ -63,7 +65,7 @@ export function TaskCard({ task, controller, draggable = false, now, onAlert }:
63
65
  // Block drag if a session is still executing this task
64
66
  if (running !== undefined) {
65
67
  e.preventDefault()
66
- const msg = `该任务正由会话执行中(${task.title}),不能拖动`
68
+ const msg = t('card.drag.running', { title: task.title })
67
69
  if (onAlert !== undefined) onAlert(msg)
68
70
  else alert(msg)
69
71
  return
@@ -85,53 +87,63 @@ export function TaskCard({ task, controller, draggable = false, now, onAlert }:
85
87
  >
86
88
  <div className="dsh-atb-card-title">{task.title}</div>
87
89
  <div className="dsh-atb-card-meta">
88
- <span className="dsh-atb-badge">{URGENCY_LABEL[task.urgency]}</span>
89
- {task.blocked && <span className="dsh-atb-badge" data-kind="blocked">受阻</span>}
90
- {stale && <span className="dsh-atb-badge" data-kind="stale">⏱ 认领超时</span>}
90
+ <span className="dsh-atb-badge">{t(URGENCY_KEYS[task.urgency])}</span>
91
+ {task.blocked && <span className="dsh-atb-badge" data-kind="blocked">{t('shared.blocked')}</span>}
92
+ {stale && <span className="dsh-atb-badge" data-kind="stale">{t('card.badge.stale')}</span>}
91
93
  {task.execution.mode === 'scheduled' && (
92
94
  <span className="dsh-atb-badge" data-kind="scheduled">⏰ {fmtTime(task.execution.nextRunAt)}</span>
93
95
  )}
94
96
  {task.model !== undefined && (
95
97
  <span
96
98
  className="dsh-atb-badge"
97
- title={`固定模型: ${task.model.provider}/${task.model.model}${task.model.reasoningEffort !== undefined ? ` · 思考强度: ${task.model.reasoningEffort}` : ''}`}
99
+ title={t('card.badge.modelTitle', { model: task.model.provider + '/' + task.model.model }) + (task.model.reasoningEffort !== undefined ? t('card.badge.modelEffort', { effort: task.model.reasoningEffort }) : '')}
98
100
  >
99
101
  {task.model.model}{task.model.reasoningEffort !== undefined ? ` (${task.model.reasoningEffort})` : ''}
100
102
  </span>
101
103
  )}
104
+ {task.permission === 'read-only' && (
105
+ <span className="dsh-atb-badge" data-kind="blocked" title={t('card.badge.permReadOnlyTitle')}>
106
+ {t('card.badge.permReadOnly')}
107
+ </span>
108
+ )}
109
+ {task.permission === 'danger-full-access' && (
110
+ <span className="dsh-atb-badge" data-kind="urgent" title={t('card.badge.permFullTitle')}>
111
+ {t('card.badge.permFull')}
112
+ </span>
113
+ )}
102
114
  {task.checklist !== undefined && task.checklist.length > 0 && (
103
115
  <span
104
116
  className="dsh-atb-badge"
105
117
  data-kind={task.status === 'in_review' && task.checklist.some(i => !i.checked) ? 'blocked' : 'checklist'}
106
- title={task.status === 'in_review' && task.checklist.some(i => !i.checked) ? '待验收:清单未全部勾选' : '验收清单进度'}
118
+ title={task.status === 'in_review' && task.checklist.some(i => !i.checked) ? t('card.badge.checklistReview') : t('card.badge.checklist')}
107
119
  >
108
120
  ☑ {task.checklist.filter(i => i.checked).length}/{task.checklist.length}
109
121
  </span>
110
122
  )}
111
- {task.status === 'done' && <span className="dsh-atb-badge" data-kind="done">完成</span>}
123
+ {task.status === 'done' && <span className="dsh-atb-badge" data-kind="done">{t('status.pill.done')}</span>}
112
124
  {last !== undefined && (
113
125
  <span className="dsh-atb-badge" data-kind={last.outcome === 'running' ? 'running' : last.outcome}>
114
- {OUTCOME_LABEL[last.outcome] ?? last.outcome}
126
+ {t(OUTCOME_KEYS[last.outcome] ?? last.outcome)}
115
127
  </span>
116
128
  )}
117
129
  {targetSessionId !== undefined && (
118
130
  <button
119
131
  type="button"
120
132
  className="dsh-atb-card-session"
121
- title={`点击一键跳转到会话:${targetSessionId}`}
133
+ title={t('card.session.jumpTitle', { id: targetSessionId })}
122
134
  onClick={(e) => {
123
135
  e.stopPropagation()
124
136
  void controller.openSession(targetSessionId).then(result => {
125
137
  if (result === 'missing') {
126
- const msg = `该会话已被删除(${shortId(targetSessionId)}),无法打开`
138
+ const msg = t('card.session.missing', { id: shortId(targetSessionId) })
127
139
  if (onAlert !== undefined) onAlert(msg)
128
140
  else alert(msg)
129
141
  } else if (result === 'archived') {
130
- const msg = `该会话已归档(${shortId(targetSessionId)}),已从会话列表隐藏`
142
+ const msg = t('card.session.archived', { id: shortId(targetSessionId) })
131
143
  if (onAlert !== undefined) onAlert(msg)
132
144
  else alert(msg)
133
145
  } else if (result === 'unavailable') {
134
- const msg = `会话导航不可用,会话 ID:${targetSessionId}`
146
+ const msg = t('card.session.unavailable', { id: targetSessionId })
135
147
  if (onAlert !== undefined) onAlert(msg)
136
148
  else alert(msg)
137
149
  }
@@ -142,7 +154,7 @@ export function TaskCard({ task, controller, draggable = false, now, onAlert }:
142
154
  </button>
143
155
  )}
144
156
  {task.comments.length > 0 && <span>💬 {task.comments.length}</span>}
145
- {task.trashedAt !== undefined && <span className="dsh-atb-badge" data-kind="trashed">待清除</span>}
157
+ {task.trashedAt !== undefined && <span className="dsh-atb-badge" data-kind="trashed">{t('card.badge.pendingPurge')}</span>}
146
158
  <span style={{ marginLeft: 'auto' }}>{fmtTime(task.updatedAt)}</span>
147
159
  </div>
148
160
  {reviewing && (rejectOpen
@@ -151,7 +163,7 @@ export function TaskCard({ task, controller, draggable = false, now, onAlert }:
151
163
  <input
152
164
  className="dsh-atb-input dsh-atb-quick-note"
153
165
  value={note}
154
- placeholder="退回原因(可选,agent 开工前会读)…"
166
+ placeholder={t('card.reject.placeholder')}
155
167
  autoFocus
156
168
  spellCheck={false}
157
169
  onChange={e => setNote(e.target.value)}
@@ -160,8 +172,8 @@ export function TaskCard({ task, controller, draggable = false, now, onAlert }:
160
172
  else if (e.key === 'Escape') { setRejectOpen(false); setNote('') }
161
173
  }}
162
174
  />
163
- <button type="button" className="dsh-atb-quickbtn" data-act="reject-confirm" onClick={submitReject}>退回待办</button>
164
- <button type="button" className="dsh-atb-quickbtn" data-act="reject-cancel" onClick={() => { setRejectOpen(false); setNote('') }}>取消</button>
175
+ <button type="button" className="dsh-atb-quickbtn" data-act="reject-confirm" onClick={submitReject}>{t('card.reject.confirm')}</button>
176
+ <button type="button" className="dsh-atb-quickbtn" data-act="reject-cancel" onClick={() => { setRejectOpen(false); setNote('') }}>{t('shared.cancel')}</button>
165
177
  </div>
166
178
  )
167
179
  : (
@@ -170,19 +182,19 @@ export function TaskCard({ task, controller, draggable = false, now, onAlert }:
170
182
  type="button"
171
183
  className="dsh-atb-quickbtn"
172
184
  data-act="done"
173
- title="验收完成:移至已完成"
185
+ title={t('card.action.doneTitle')}
174
186
  onClick={() => void controller.move(task.id, task.version, 'done')}
175
187
  >
176
- ✓ 完成
188
+ {t('card.action.done')}
177
189
  </button>
178
190
  <button
179
191
  type="button"
180
192
  className="dsh-atb-quickbtn"
181
193
  data-act="reject"
182
- title="退回待办,可附退回原因"
194
+ title={t('card.action.rejectTitle')}
183
195
  onClick={() => setRejectOpen(true)}
184
196
  >
185
- ✗ 退回
197
+ {t('card.action.reject')}
186
198
  </button>
187
199
  </div>
188
200
  ))}