merge: maestro/tsk_EmZ6vjtVbrOk [tsk_EmZ6vjtVbrOk]

This commit is contained in:
maestro
2026-06-30 09:08:52 +08:00
3 changed files with 51 additions and 9 deletions
+3 -2
View File
@@ -11,9 +11,10 @@ function ArchiveRow({ item, t, onOpen }) {
borderBottom: '1px dashed var(--line-soft)',
background: hover ? 'var(--panel)' : 'transparent',
}}>
<span style={{ color: 'var(--muted)', fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{item.title}<span style={{ color: 'var(--faint)', fontSize: 10.5, marginLeft: 6 }}>{item.id}</span>
<span style={{ flex: '0 1 auto', minWidth: 0, color: 'var(--muted)', fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{item.title}
</span>
<span style={{ flex: 'none', color: 'var(--faint)', fontSize: 10.5 }}>{item.id}</span>
{item.subs ? <span style={{ flex: 'none', fontSize: 10.5, color: 'var(--faint)' }}>{t.archiveSubs.replace('{n}', item.subs)}</span> : null}
<span style={{ flex: 1, minWidth: 8 }}></span>
<ComplexityBadge complexity={item.cplx} />
+43 -2
View File
@@ -227,9 +227,10 @@ function TaskRow({ task, depth, ctx, expandedId, setExpandedId, openIds, toggleO
}}>
{kids.length ? '▶' : '·'}
</span>
<span style={{ fontWeight: 500, color: ctx ? 'var(--muted)' : 'var(--ink)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{task.title}<span style={{ color: 'var(--faint)', fontSize: 10.5, marginLeft: 6 }}>{task.id}</span>
<span style={{ flex: '0 1 auto', minWidth: 0, fontWeight: 500, color: ctx ? 'var(--muted)' : 'var(--ink)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{task.title}
</span>
<span style={{ flex: 'none', color: 'var(--faint)', fontSize: 10.5 }}>{task.id}</span>
<span style={{ flex: 1, minWidth: 8 }}></span>
{task.deps ? (
<span style={{ flex: 'none', fontSize: 10.5, letterSpacing: '.06em', color: 'var(--amber)', border: '1px dashed var(--amber-dim)', borderRadius: 3, padding: '1px 7px', lineHeight: 1.5, whiteSpace: 'nowrap' }}>
@@ -384,6 +385,46 @@ function TaskDetail({ task, t, byId, onJump, onTakeover, taskOps }) {
</div>
</div>
) : null}
{/* 审批记录:读 _raw.approvals,按记录列出闸名·通过/驳回 + 审批人 + 时间;驳回突出 reason。
安全:reason/actor/gate 全部走 React 文本节点(默认转义),绝不经 MaestroRichDoc / innerHTML,避免同源存储型 XSS。 */}
{(() => {
const approvals = (task._raw && task._raw.approvals) || [];
if (!approvals.length) return null;
return (
<div>
<div style={{ fontSize: 10.5, color: 'var(--muted)', letterSpacing: '.18em', marginBottom: 6 }}>{t.approvalsLabel}</div>
<div style={{ display: 'grid', gap: 6 }}>
{approvals.map((a, i) => {
const rej = a.action === 'reject';
const at = (a.at || '').replace('T', ' ').slice(0, 19);
const gate = (t.gates && t.gates[a.gate]) || a.gate;
return (
<div key={i} style={{
fontSize: 12, padding: '6px 10px', borderRadius: 4,
background: 'var(--bg-deep)', border: '1px solid var(--line-soft)',
borderLeft: '2px solid ' + (rej ? 'var(--red-dim)' : 'var(--green-dim)'),
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
<span style={{ color: rej ? 'var(--red)' : 'var(--green)', fontWeight: 700, flex: 'none' }}>
{gate} · {rej ? t.rejected : t.accepted}
</span>
<span style={{ color: 'var(--faint)', fontSize: 11 }}>{t.approver} {a.actor} · {at}</span>
</div>
{rej && a.reason ? (
<div style={{ marginTop: 5, padding: '5px 9px', borderRadius: 3,
background: 'rgba(240,100,100,.08)', borderLeft: '2px solid var(--red-dim)', color: 'var(--amber)' }}>
<span style={{ color: 'var(--red)', fontWeight: 600, marginRight: 6 }}>{t.rejectReason}</span>{a.reason}
</div>
) : a.reason ? (
<div style={{ marginTop: 4, color: 'var(--muted)' }}> {a.reason}</div>
) : null}
</div>
);
})}
</div>
</div>
);
})()}
</div>
);
}
+5 -5
View File
@@ -38,7 +38,7 @@ window.MAESTRO_I18N = {
fStatus: '状态', fAssignee: '执行者', assignees: { agent: '智能体', human: '人工' },
fullRead: '全屏阅读', depsLabel: 'DEPS · 依赖(全部完成才可执行)', depDone: '完成', depWait: '等待',
gateSection: '需你处理', gateNeedsHuman: '需人工', gRequeue: '↻ 重投', gTakeover: '⌨ 接管', gCancelTask: '⊘ 取消', accept: '✓ 通过', reject: '✕ 驳回', confirmReject: '确认驳回',
rejectPlaceholder: '改进意见(必填)',
rejectPlaceholder: '改进意见(必填)', rejectReason: '驳回原因',
taskSection: '任务树', newTask: '+ 新建任务', titleLabel: '标题', titlePlaceholder: '要做什么',
complexity: '复杂度', cplxAuto: '智能', parentTask: '父任务', topLevel: '(顶层)', priority: '优先级',
p0: 'P0 · 高', p1: 'P1 · 中', p2: 'P2 · 低', create: '创建任务', cancel: '取消',
@@ -92,7 +92,7 @@ window.MAESTRO_I18N = {
fStatus: 'Status', fAssignee: 'Assignee', assignees: { agent: 'Agent', human: 'Human' },
fullRead: 'Read fullscreen', depsLabel: 'DEPS (all must complete to run)', depDone: 'done', depWait: 'waiting',
gateSection: 'Needs you', gateNeedsHuman: 'Needs human', gRequeue: '↻ Requeue', gTakeover: '⌨ Take over', gCancelTask: '⊘ Cancel', accept: '✓ Accept', reject: '✕ Reject', confirmReject: 'Confirm reject',
rejectPlaceholder: 'Improvement feedback (required)',
rejectPlaceholder: 'Improvement feedback (required)', rejectReason: 'Reject reason',
taskSection: 'Task tree', newTask: '+ New task', titleLabel: 'Title', titlePlaceholder: 'What needs doing',
complexity: 'Complexity', cplxAuto: 'AUTO', parentTask: 'Parent', topLevel: '(top level)', priority: 'Priority',
p0: 'P0 · high', p1: 'P1 · medium', p2: 'P2 · low', create: 'Create task', cancel: 'Cancel',
@@ -146,7 +146,7 @@ window.MAESTRO_I18N = {
fStatus: 'Estado', fAssignee: 'Ejecutor', assignees: { agent: 'Agente', human: 'Humano' },
fullRead: 'Pantalla completa', depsLabel: 'DEPS (todas deben completarse)', depDone: 'hecha', depWait: 'esperando',
gateSection: 'Requiere acción', gateNeedsHuman: 'Manual', gRequeue: '↻ Reencolar', gTakeover: '⌨ Tomar', gCancelTask: '⊘ Cancelar', accept: '✓ Aceptar', reject: '✕ Rechazar', confirmReject: 'Confirmar rechazo',
rejectPlaceholder: 'Comentario de mejora (obligatorio)',
rejectPlaceholder: 'Comentario de mejora (obligatorio)', rejectReason: 'Motivo del rechazo',
taskSection: 'Árbol de tareas', newTask: '+ Nueva tarea', titleLabel: 'Título', titlePlaceholder: 'Qué hay que hacer',
complexity: 'Complejidad', cplxAuto: 'AUTO', parentTask: 'Padre', topLevel: '(raíz)', priority: 'Prioridad',
p0: 'P0 · alta', p1: 'P1 · media', p2: 'P2 · baja', create: 'Crear tarea', cancel: 'Cancelar',
@@ -200,7 +200,7 @@ window.MAESTRO_I18N = {
fStatus: 'ステータス', fAssignee: '担当', assignees: { agent: 'エージェント', human: '人手' },
fullRead: '全画面で読む', depsLabel: 'DEPS · 依存(全完了で実行可)', depDone: '完了', depWait: '待機',
gateSection: '要対応', gateNeedsHuman: '要人手', gRequeue: '↻ 再投入', gTakeover: '⌨ 引継ぎ', gCancelTask: '⊘ 取消', accept: '✓ 承認', reject: '✕ 却下', confirmReject: '却下を確定',
rejectPlaceholder: '改善フィードバック(必須)',
rejectPlaceholder: '改善フィードバック(必須)', rejectReason: '却下理由',
taskSection: 'タスクツリー', newTask: '+ 新規タスク', titleLabel: 'タイトル', titlePlaceholder: '何をしますか',
complexity: '複雑度', cplxAuto: '智能', parentTask: '親タスク', topLevel: '(トップ)', priority: '優先度',
p0: 'P0 · 高', p1: 'P1 · 中', p2: 'P2 · 低', create: 'タスク作成', cancel: 'キャンセル',
@@ -254,7 +254,7 @@ window.MAESTRO_I18N = {
fStatus: 'Statut', fAssignee: 'Exécutant', assignees: { agent: 'Agent', human: 'Humain' },
fullRead: 'Plein écran', depsLabel: 'DEPS (toutes requises)', depDone: 'terminée', depWait: 'en attente',
gateSection: 'À traiter', gateNeedsHuman: 'Manuel', gRequeue: '↻ Relancer', gTakeover: '⌨ Reprendre', gCancelTask: '⊘ Annuler', accept: '✓ Accepter', reject: '✕ Rejeter', confirmReject: 'Confirmer le rejet',
rejectPlaceholder: "Retour d'amélioration (obligatoire)",
rejectPlaceholder: "Retour d'amélioration (obligatoire)", rejectReason: 'Motif du rejet',
taskSection: 'Arbre des tâches', newTask: '+ Nouvelle tâche', titleLabel: 'Titre', titlePlaceholder: 'Que faut-il faire',
complexity: 'Complexité', cplxAuto: 'AUTO', parentTask: 'Parent', topLevel: '(racine)', priority: 'Priorité',
p0: 'P0 · haute', p1: 'P1 · moyenne', p2: 'P2 · basse', create: 'Créer la tâche', cancel: 'Annuler',