diff --git a/web/static/css/style.css b/web/static/css/style.css index 648b8275..2e14c712 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -1144,12 +1144,18 @@ header { .hitl-config-input { width: 100%; height: 38px; - border: 1px solid var(--border-color); + border: 1px solid #e2e8f0; border-radius: 8px; background: #fff; - padding: 0 10px; + padding: 0 12px; font-size: 14px; color: var(--text-primary); + transition: border-color 0.15s, box-shadow 0.15s; +} +.hitl-config-input:focus { + outline: none; + border-color: var(--accent-color, #0066ff); + box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.1); } .hitl-pending-list { @@ -1159,43 +1165,133 @@ header { } .hitl-pending-item { - border: 1px solid rgba(99, 102, 241, 0.25); + border: 1px solid #dbeafe; border-radius: 10px; - padding: 12px; - background: rgba(15, 23, 42, 0.45); + padding: 16px; + background: #f8fbff; + transition: box-shadow 0.2s; +} +.hitl-pending-item:hover { + box-shadow: 0 2px 12px rgba(0, 102, 255, 0.08); } .hitl-pending-item-header { display: flex; justify-content: space-between; align-items: center; + margin-bottom: 10px; +} + +.hitl-pending-item-title { + display: flex; + align-items: center; + gap: 8px; +} + +.hitl-tool-badge { + display: inline-flex; + align-items: center; + padding: 3px 10px; + border-radius: 6px; + font-size: 13px; + font-weight: 600; + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + background: var(--accent-color, #0066ff); + color: #fff; +} + +.hitl-mode-tag { + display: inline-flex; + align-items: center; + padding: 2px 8px; + border-radius: 10px; + font-size: 11px; + font-weight: 500; + background: #e0e7ff; + color: #4338ca; +} +.hitl-mode-tag--review_edit { + background: #fef3c7; + color: #92400e; +} + +.hitl-pending-meta { + font-size: 12px; + color: #94a3b8; margin-bottom: 8px; + word-break: break-all; +} + +.hitl-pending-payload { + white-space: pre-wrap; + word-break: break-all; + max-height: 160px; + overflow: auto; + margin: 0 0 4px 0; + padding: 10px 12px; + border-radius: 8px; + background: #fff; + border: 1px solid #e2e8f0; + font-size: 12px; + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + color: #334155; + line-height: 1.5; +} + +.hitl-pending-item-header-right { + display: flex; + align-items: center; + gap: 8px; +} + +.hitl-dismiss-btn { + background: none; + border: none; + cursor: pointer; + font-size: 18px; + line-height: 1; + color: var(--text-secondary, #64748b); + padding: 2px 6px; + border-radius: 4px; + transition: background 0.15s, color 0.15s; +} +.hitl-dismiss-btn:hover { + background: rgba(239, 68, 68, 0.1); + color: #ef4444; } .hitl-pending-actions { display: flex; gap: 8px; - margin-top: 10px; + margin-top: 12px; + padding-top: 12px; + border-top: 1px solid #e2e8f0; } .hitl-edit-args { width: 100%; min-height: 76px; margin-top: 8px; - border: 1px solid rgba(148, 163, 184, 0.35); + border: 1px solid #e2e8f0; border-radius: 8px; background: #ffffff; color: #1f2937; - padding: 8px; + padding: 10px 12px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; + transition: border-color 0.15s, box-shadow 0.15s; +} +.hitl-edit-args:focus { + outline: none; + border-color: var(--accent-color, #0066ff); + box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.1); } .hitl-input-help { margin-top: 6px; margin-bottom: 6px; font-size: 12px; - color: #64748b; + color: var(--accent-color, #0066ff); line-height: 1.4; } diff --git a/web/static/js/hitl.js b/web/static/js/hitl.js index d7c9c97e..5334b41b 100644 --- a/web/static/js/hitl.js +++ b/web/static/js/hitl.js @@ -282,21 +282,29 @@ async function refreshHitlPending() { const preview = payload.length > 280 ? (payload.slice(0, 280) + '...') : payload; const mode = String(item.mode || '').trim().toLowerCase(); const allowEdit = mode === 'review_edit'; + var escId = escapeHtml(String(item.id || '')); + var qId = JSON.stringify(String(item.id || '')).replace(/"/g, '"'); + var qConv = JSON.stringify(String(item.conversationId || '')).replace(/"/g, '"'); return ( '
' + '
' + - '' + escapeHtml(item.toolName || '-') + '' + - '' + escapeHtml(item.mode || '-') + '' + + '
' + + '' + escapeHtml(item.toolName || '-') + '' + + '' + escapeHtml(item.mode || '-') + '' + '
' + - '
conversation: ' + escapeHtml(item.conversationId || '-') + '
' + - '
' + escapeHtml(preview) + '
' + + '' + + '
' + + '
会话:' + escapeHtml(item.conversationId || '-') + '
' + + '
' + escapeHtml(preview) + '
' + (allowEdit - ? ('
审查编辑模式:可填写 JSON 对象覆盖参数,示例:{"command":"ls -la"}
' + - '') + ? ('
审查编辑模式:可填写 JSON 对象覆盖参数。示例:{"command":"ls -la"}
' + + '') : '
审批模式:仅通过/拒绝,不支持改参。
') + + '
备注(可选):建议写审批依据。
' + + '' + '
' + - '' + - '' + + '' + + '' + '
' + '
' ); @@ -307,7 +315,8 @@ async function refreshHitlPending() { } async function submitHitlDecision(interruptId, decision, conversationIdOpt) { - const comment = prompt('审批备注(可选)') || ''; + const commentBox = document.getElementById('hitl-comment-' + interruptId); + const comment = (commentBox && commentBox.value) ? commentBox.value.trim() : ''; let editedArguments = null; const editBox = document.getElementById('hitl-edit-' + interruptId); if (editBox && editBox.value && editBox.value.trim()) { @@ -332,7 +341,7 @@ async function submitHitlDecisionWithPayload(interruptId, decision, comment, edi if (!resp.ok) { const errText = await readHitlApiError(resp); if (resp.status === 409 && (errText.indexOf('already resolved') >= 0 || errText.indexOf('not found') >= 0)) { - refreshHitlPending(); + await dismissHitlItem(interruptId, true); return true; } alert('提交失败:' + errText); @@ -363,9 +372,24 @@ async function readHitlApiError(resp) { } } +async function dismissHitlItem(interruptId, silent) { + try { + await hitlApiFetch('/api/hitl/dismiss', { + method: 'POST', + credentials: 'same-origin', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ interruptId: interruptId }) + }); + } catch (e) { + if (!silent) { console.warn('dismissHitlItem', e); } + } + refreshHitlPending(); +} + window.refreshHitlPending = refreshHitlPending; window.submitHitlDecision = submitHitlDecision; window.submitHitlDecisionWithPayload = submitHitlDecisionWithPayload; +window.dismissHitlItem = dismissHitlItem; window.followAgentRunAfterHitlDecision = followAgentRunAfterHitlDecision; window.addEventListener('hitl-interrupt', function () { diff --git a/web/templates/index.html b/web/templates/index.html index 069838d7..65213f21 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -117,10 +117,9 @@