{/* Flat overlay: stroke + elevated surface do the separating, no shadow. */}
-
{selected.size} selected
+
{k.nSelected(selected.size)}
setMenu(open ? 'move' : null)} open={menu === 'move'}>
@@ -988,7 +1001,7 @@ function SelectionBar({
.map(name => (
bulk.mutate({ status: name })}>
- {columnMeta(name).label}
+ {columnLabel(k, name)}
))}
@@ -997,7 +1010,7 @@ function SelectionBar({
setMenu(open ? 'assign' : null)} open={menu === 'assign'}>
@@ -1013,13 +1026,13 @@ function SelectionBar({
))}
bulk.mutate({ assignee: '', reclaim_first: true })}>
- Unassign
+ {k.unassignAction}
-
-
diff --git a/apps/desktop/src/plugins/kanban/drawer.tsx b/apps/desktop/src/plugins/kanban/drawer.tsx
index a5b3909850e..79a0f13fb1f 100644
--- a/apps/desktop/src/plugins/kanban/drawer.tsx
+++ b/apps/desktop/src/plugins/kanban/drawer.tsx
@@ -46,8 +46,6 @@ import {
uploadAttachment
} from './api'
import {
- columnMeta,
- COMPLEXITY_LABEL,
type Diagnostic,
type DiagnosticAction,
type KanbanAttachment,
@@ -60,15 +58,18 @@ import {
ago,
Avatar,
Callout,
+ columnLabel,
duration,
errText,
isLockedTarget,
- LOCKED_COLUMNS,
+ type KanbanText,
+ lockedReason,
ScrollFade,
Section,
shortId,
StatusMenu,
- useDefaultAssignee
+ useDefaultAssignee,
+ useKanban
} from './ui'
/**
@@ -78,7 +79,7 @@ import {
* prose with the payload folded in; unknown kinds fall back to kind + compact
* key=value detail so new backend events still say something.
*/
-function eventText(event: KanbanEvent): { detail?: string; label: string } {
+function eventText(event: KanbanEvent, k: KanbanText): { detail?: string; label: string } {
let p: Record
= {}
if (typeof event.payload === 'string' && event.payload) {
@@ -100,69 +101,62 @@ function eventText(event: KanbanEvent): { detail?: string; label: string } {
const col = (key: string) => {
const value = str(key)
- return value ? columnMeta(value).label : null
+ return value ? columnLabel(k, value) : null
}
switch (event.kind) {
- case 'created': {
- const where = col('status')
- const assignee = str('assignee')
-
- return {
- label: `created${where ? ` in ${where}` : ''}${assignee ? ` · assigned to ${assignee}` : ''}`
- }
- }
-
+ case 'created':
+ return { label: k.evtCreated(col('status') ?? '', str('assignee') ?? '') }
case 'status': {
const reason = str('reason')
return {
- label: `moved to ${col('status') ?? '?'}`,
- detail: reason === 'parent_reopened' ? `parent ${str('parent') ?? ''} reopened` : (reason ?? undefined)
+ label: k.evtMovedTo(col('status') ?? '?'),
+ detail: reason === 'parent_reopened' ? k.evtParentReopened(str('parent') ?? '') : (reason ?? undefined)
}
}
case 'assigned': {
const assignee = str('assignee')
- return { label: assignee ? `assigned to ${assignee}` : 'unassigned' }
+ return { label: assignee ? k.evtAssignedTo(assignee) : k.evtUnassigned }
}
case 'commented':
- return { label: `comment by ${str('author') ?? 'someone'}` }
+ return { label: k.evtCommentBy(str('author') ?? k.someone) }
case 'claimed':
- return { label: str('source_status') === 'review' ? 'claimed by a review agent' : 'claimed by a worker' }
+ return { label: str('source_status') === 'review' ? k.evtClaimedReview : k.evtClaimedWorker }
case 'spawned':
- return { label: 'worker started', detail: p.pid != null ? `pid ${p.pid}` : undefined }
+ return { label: k.evtWorkerStarted, detail: p.pid != null ? `pid ${p.pid}` : undefined }
case 'completed':
- return { label: 'completed' }
+ return { label: k.evtCompleted }
case 'blocked':
- return { label: 'blocked — needs human input', detail: str('reason') ?? undefined }
+ return { label: k.evtBlocked, detail: str('reason') ?? undefined }
case 'unblocked':
- return { label: `unblocked${col('status') ? ` → ${col('status')}` : ' → Ready'}` }
+ return { label: k.evtUnblocked(col('status') ?? '') }
case 'reclaimed':
- return { label: 'reclaimed — returned to the queue', detail: str('reason') ?? undefined }
+ return { label: k.evtReclaimed, detail: str('reason') ?? undefined }
case 'specified':
- return { label: 'spec written by the triage agent' }
+ return { label: k.evtSpecified }
case 'promoted':
- return { label: 'dependencies done — promoted to Ready' }
+ return { label: k.evtPromoted }
case 'scheduled':
- return { label: 'scheduled for later', detail: str('reason') ?? undefined }
+ return { label: k.evtScheduled, detail: str('reason') ?? undefined }
case 'archived':
- return { label: 'archived' }
+ return { label: k.evtArchived }
case 'reprioritized':
- return { label: `priority set to ${p.priority ?? '?'}` }
+ return { label: k.evtReprioritized(String(p.priority ?? '?')) }
default: {
const detail = Object.entries(p)
.filter(([, value]) => value != null && typeof value !== 'object')
@@ -187,12 +181,14 @@ function MetaRow({ children, label }: { children: ReactNode; label: string }) {
* backend's structured recovery actions as buttons. `reassign` is skipped —
* the Assignee control in the meta table IS that action, inline. */
function Diagnostics({ items, onReclaim }: { items: Diagnostic[]; onReclaim: () => void }) {
+ const k = useKanban()
+
const act = (action: DiagnosticAction) => {
if (action.kind === 'reclaim') {
onReclaim()
} else if (action.kind === 'cli_hint') {
void navigator.clipboard.writeText(String(action.payload?.command ?? action.label))
- host.notify({ kind: 'info', message: 'Command copied' })
+ host.notify({ kind: 'info', message: k.commandCopied })
}
}
@@ -243,6 +239,7 @@ function AssigneeMenu({
current: null | string | undefined
onReassign: (p: string) => void
}) {
+ const k = useKanban()
const { data: roster } = useQuery({ queryKey: PROFILES_KEY, queryFn: fetchProfiles, staleTime: 60_000 })
return (
@@ -258,7 +255,7 @@ function AssigneeMenu({
{current}
>
) : (
- unassigned
+ {k.unassigned}
)}
@@ -296,6 +293,7 @@ function CommentComposer({
pending: boolean
running?: boolean
}) {
+ const k = useKanban()
const [body, setBody] = useState('')
const submit = () => {
@@ -328,7 +326,7 @@ function CommentComposer({
submit()
}
}}
- placeholder={running ? 'Message the running worker…' : 'Add a comment…'}
+ placeholder={running ? k.messageWorker : k.addComment}
rows={1}
size="sm"
value={body}
@@ -340,17 +338,15 @@ function CommentComposer({
size="xs"
variant="secondary"
>
- {running ? 'Send' : 'Comment'}
+ {running ? k.send : k.comment}
{running && onRequeue && (