diff --git a/review_agent/file_summary/views.py b/review_agent/file_summary/views.py index 3fe4120..cb701cf 100644 --- a/review_agent/file_summary/views.py +++ b/review_agent/file_summary/views.py @@ -148,6 +148,14 @@ def conversation_list(request): ) +@require_http_methods(["DELETE"]) +@login_required +def conversation_detail(request, conversation_id: int): + conversation = _conversation_for_user(request.user, conversation_id) + conversation.delete() + return JsonResponse({"ok": True, "conversation_id": conversation_id}) + + @require_http_methods(["GET"]) @login_required def attachment_download(request, conversation_id: int, attachment_id: int): diff --git a/review_agent/urls.py b/review_agent/urls.py index a2b1d24..dfe648c 100644 --- a/review_agent/urls.py +++ b/review_agent/urls.py @@ -6,6 +6,7 @@ from .file_summary.views import ( attachments, batch_events, batch_status, + conversation_detail, conversation_list, conversation_messages, export_download, @@ -35,6 +36,11 @@ urlpatterns = [ conversation_list, name="review_agent_conversation_list", ), + path( + "api/review-agent/conversations//", + conversation_detail, + name="review_agent_conversation_detail", + ), path( "api/review-agent/conversations//attachments/", attachments, diff --git a/static/css/login.css b/static/css/login.css index fa4ded9..e21ca9b 100644 --- a/static/css/login.css +++ b/static/css/login.css @@ -147,7 +147,7 @@ input:focus { gap: 24px; padding: 18px; min-height: 0; - overflow-y: auto; + overflow: hidden; background: linear-gradient(180deg, var(--sidebar) 0%, var(--sidebar-strong) 100%); border-right: 1px solid var(--line); transition: width 180ms ease, padding 180ms ease, transform 180ms ease; @@ -259,19 +259,47 @@ input:focus { text-transform: uppercase; } +.sidebar-group { + display: flex; + min-height: 0; + flex: 1; + flex-direction: column; +} + .history-list { display: grid; + align-content: start; gap: 8px; + min-height: 0; + overflow-y: auto; + padding-right: 4px; + scrollbar-width: thin; + scrollbar-color: #c4cfdd transparent; +} + +.history-list::-webkit-scrollbar { + width: 8px; +} + +.history-list::-webkit-scrollbar-track { + background: transparent; +} + +.history-list::-webkit-scrollbar-thumb { + border-radius: 999px; + background: #c4cfdd; } .history-item { + position: relative; display: grid; - gap: 4px; - padding: 14px; + grid-template-columns: minmax(0, 1fr) 28px; + align-items: center; + gap: 8px; + padding: 10px 8px 10px 14px; border: 1px solid var(--line); border-radius: 14px; color: var(--text); - text-decoration: none; background: rgba(255, 255, 255, 0.82); } @@ -281,7 +309,18 @@ input:focus { background: #edf4ff; } +.history-link { + display: grid; + min-width: 0; + gap: 4px; + color: inherit; + text-decoration: none; +} + .history-title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; font-size: 14px; font-weight: 600; } @@ -291,6 +330,41 @@ input:focus { font-size: 12px; } +.history-item .history-delete { + appearance: none; + -webkit-appearance: none; + display: inline-grid; + place-items: center; + flex: 0 0 28px; + width: 28px; + min-width: 28px; + height: 28px; + min-height: 28px; + padding: 0; + border: 1px solid transparent; + border-radius: 8px; + background: transparent; + color: #93a0af; + cursor: pointer; + font: inherit; + font-size: 18px; + line-height: 1; + opacity: 0; + transition: opacity 140ms ease, background 140ms ease, color 140ms ease, border-color 140ms ease; +} + +.history-item:hover .history-delete, +.history-item.active .history-delete, +.history-item .history-delete:focus-visible { + opacity: 1; +} + +.history-item .history-delete:hover { + border-color: #fecdd3; + background: var(--danger-bg); + color: var(--danger-text); +} + .history-empty { padding: 16px 14px; border: 1px dashed var(--line-strong); @@ -800,11 +874,13 @@ input:focus { .workspace[data-sidebar-state="collapsed"] .search-form, .workspace[data-sidebar-state="collapsed"] .sidebar-label, .workspace[data-sidebar-state="collapsed"] .history-title, -.workspace[data-sidebar-state="collapsed"] .history-meta { +.workspace[data-sidebar-state="collapsed"] .history-meta, +.workspace[data-sidebar-state="collapsed"] .history-delete { display: none; } .workspace[data-sidebar-state="collapsed"] .history-item { + grid-template-columns: minmax(0, 1fr); place-items: center; padding: 12px; } @@ -1422,6 +1498,639 @@ input:focus { margin: 0; } +.knowledge-page { + display: grid; + align-content: start; + gap: 12px; + height: calc(100vh - 60px); + min-height: 0; + overflow-y: auto; + padding: 16px 24px 20px; + background: var(--bg); +} + +.knowledge-hero, +.knowledge-status-panel, +.knowledge-grid, +.knowledge-content, +.knowledge-workbench, +.knowledge-summary-row, +.knowledge-main-grid, +.knowledge-secondary-grid, +.knowledge-panel { + width: min(1440px, 100%); + margin: 0 auto; +} + +.knowledge-hero-actions { + display: flex; + align-items: center; + gap: 10px; +} + +.knowledge-hero h1 { + margin: 2px 0; + font-size: 22px; +} + +.knowledge-hero p { + margin: 0; + color: var(--muted); + font-size: 13px; +} + +.knowledge-status { + display: inline-flex; + align-items: center; + min-height: 34px; + padding: 0 12px; + border-radius: 999px; + font-size: 13px; + font-weight: 700; + white-space: nowrap; +} + +.knowledge-status.status-ready { + background: #ecfdf3; + color: #047857; +} + +.knowledge-status.status-thin { + background: #fff7ed; + color: #c2410c; +} + +.knowledge-status.status-missing { + background: #fff1f2; + color: var(--danger-text); +} + +.knowledge-summary-row { + display: grid; + grid-template-columns: repeat(4, minmax(120px, 1fr)); + gap: 0; + overflow: hidden; + border: 1px solid var(--line); + border-radius: 8px; + background: #ffffff; +} + +.knowledge-summary-item { + display: grid; + gap: 4px; + min-height: 68px; + padding: 12px 14px; + border-right: 1px solid var(--line); + background: #ffffff; +} + +.knowledge-summary-item:last-child { + border-right: 0; +} + +.knowledge-summary-item span { + color: var(--muted); + font-size: 12px; + font-weight: 700; +} + +.knowledge-summary-item strong { + font-size: 22px; + line-height: 1.1; +} + +.knowledge-summary-item small { + color: var(--muted); + font-size: 12px; +} + +.knowledge-status-message { + grid-column: 1 / -1; + margin: 0; + padding: 10px 12px; + border-radius: 8px; + background: #f8fbff; + color: #344054; + font-size: 13px; + line-height: 1.6; +} + +.knowledge-grid, +.knowledge-main-grid, +.knowledge-secondary-grid { + display: grid; + gap: 12px; +} + +.knowledge-content { + display: grid; + gap: 12px; +} + +.knowledge-workbench { + grid-template-columns: minmax(320px, 420px) minmax(0, 1fr); + align-items: start; +} + +.knowledge-main-grid { + grid-template-columns: minmax(300px, 360px) minmax(0, 1fr); + align-items: start; +} + +.knowledge-secondary-grid { + grid-template-columns: minmax(340px, 0.8fr) minmax(0, 1.2fr); + align-items: start; +} + +.knowledge-left-stack, +.knowledge-right-stack, +.knowledge-left-rail, +.knowledge-right-display { + display: grid; + gap: 12px; + min-width: 0; +} + +.knowledge-panel { + display: grid; + gap: 10px; +} + +.knowledge-panel h2 { + margin: 0; + font-size: 16px; +} + +.knowledge-system-panel { + display: grid; + gap: 12px; +} + +.knowledge-system-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; +} + +.knowledge-system-header h2 { + margin: 0 0 4px; + font-size: 16px; +} + +.knowledge-system-header p { + margin: 0; + color: #344054; + font-size: 13px; + line-height: 1.6; +} + +.knowledge-system-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 14px; +} + +.knowledge-document-form { + display: grid; + gap: 10px; +} + +.knowledge-document-form label { + display: grid; + gap: 6px; +} + +.knowledge-document-form label span { + color: #344054; + font-size: 13px; + font-weight: 700; +} + +.knowledge-document-form input[type="text"], +.knowledge-document-form input[type="file"], +.knowledge-document-form textarea { + width: 100%; + min-height: 36px; + padding: 8px 10px; + border: 1px solid var(--line); + border-radius: 8px; + background: #ffffff; + color: var(--text); + font: inherit; +} + +.knowledge-upload-dropzone { + min-height: 156px; + cursor: pointer; +} + +.knowledge-upload-dropzone strong { + color: var(--text); + font-size: 16px; +} + +.knowledge-document-form textarea { + resize: vertical; + line-height: 1.6; +} + +.knowledge-document-form input:focus, +.knowledge-document-form textarea:focus, +.knowledge-search-form input:focus { + border-color: var(--accent); + box-shadow: 0 0 0 3px rgba(58, 114, 216, 0.14); + outline: none; +} + +.knowledge-checkbox { + display: flex !important; + grid-template-columns: auto 1fr; + align-items: center; + gap: 8px !important; +} + +.knowledge-checkbox input { + width: 16px; + height: 16px; +} + +.knowledge-form-actions, +.knowledge-toolbar-actions, +.knowledge-inline-actions { + display: flex; + align-items: center; + gap: 8px; +} + +.knowledge-inline-actions { + justify-content: space-between; +} + +.knowledge-inline-actions .knowledge-checkbox { + min-height: 34px; +} + +.knowledge-form-actions button, +.knowledge-toolbar-actions button, +.knowledge-inline-actions button { + min-height: 34px; + padding: 0 12px; + border: 1px solid var(--line); + border-radius: 8px; + background: #ffffff; + color: var(--accent); + cursor: pointer; + font: inherit; + font-size: 13px; + font-weight: 700; +} + +.knowledge-form-actions button[type="submit"], +.knowledge-inline-actions button { + border: 0; + background: var(--accent); + color: #ffffff; +} + +.knowledge-toolbar-actions button:disabled { + color: var(--muted); + cursor: not-allowed; + opacity: 0.68; +} + +.knowledge-definition-list { + display: grid; + gap: 8px; + margin: 0; +} + +.knowledge-definition-list div { + display: grid; + grid-template-columns: 120px minmax(0, 1fr); + gap: 10px; + padding: 8px 0; + border-top: 1px solid var(--line); +} + +.knowledge-definition-list dt { + color: var(--muted); + font-size: 12px; + font-weight: 700; +} + +.knowledge-definition-list dd { + margin: 0; + overflow-wrap: anywhere; + color: var(--text); + font-size: 13px; +} + +.knowledge-command-box { + display: grid; + gap: 8px; + padding: 10px; + border: 1px solid var(--line); + border-radius: 8px; + background: #f8fbff; +} + +.knowledge-command-box strong { + font-size: 13px; +} + +.knowledge-command-box code { + display: block; + overflow-wrap: anywhere; + color: #1f2a37; + font-size: 12px; + line-height: 1.5; +} + +.knowledge-severity-list { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.knowledge-severity-list span { + display: inline-flex; + align-items: center; + min-height: 28px; + padding: 0 10px; + border-radius: 999px; + background: #eaf2ff; + color: var(--accent); + font-size: 12px; + font-weight: 700; +} + +.knowledge-search-form { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 8px; +} + +.knowledge-search-form input { + min-height: 36px; + padding: 0 12px; + border: 1px solid var(--line); + border-radius: 8px; + color: var(--text); + font: inherit; +} + +.knowledge-search-form button { + min-height: 36px; + padding: 0 14px; + border: 0; + border-radius: 8px; + background: var(--accent); + color: #ffffff; + cursor: pointer; + font: inherit; + font-weight: 700; +} + +.knowledge-search-results { + display: grid; + gap: 10px; +} + +.knowledge-panel-note { + margin: 0; + color: var(--muted); + font-size: 13px; + line-height: 1.6; +} + +.knowledge-compact-stats { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0; + overflow: hidden; + margin: 0; + border: 1px solid var(--line); + border-radius: 8px; +} + +.knowledge-compact-stats div { + display: grid; + gap: 4px; + padding: 10px; + border-right: 1px solid var(--line); +} + +.knowledge-compact-stats div:last-child { + border-right: 0; +} + +.knowledge-compact-stats dt { + color: var(--muted); + font-size: 12px; + font-weight: 700; +} + +.knowledge-compact-stats dd { + margin: 0; + color: var(--text); + font-size: 18px; + font-weight: 800; + line-height: 1; +} + +.knowledge-result { + display: grid; + gap: 8px; + padding: 12px; + border: 1px solid var(--line); + border-radius: 8px; + background: var(--panel-soft); +} + +.knowledge-result header { + display: flex; + justify-content: space-between; + gap: 12px; +} + +.knowledge-result header strong { + font-size: 13px; +} + +.knowledge-result header span, +.knowledge-result em { + overflow-wrap: anywhere; + color: var(--muted); + font-size: 12px; +} + +.knowledge-result p, +.knowledge-search-error { + margin: 0; + color: #344054; + font-size: 13px; + line-height: 1.7; + overflow-wrap: anywhere; +} + +.knowledge-search-error { + padding: 10px 12px; + border-radius: 8px; + background: #fff1f2; + color: var(--danger-text); +} + +.knowledge-source-table th:first-child, +.knowledge-source-table td:first-child, +.knowledge-document-table th:first-child, +.knowledge-document-table td:first-child, +.knowledge-source-table th:nth-child(3), +.knowledge-source-table td:nth-child(3), +.knowledge-source-table th:nth-child(4), +.knowledge-source-table td:nth-child(4), +.knowledge-source-table th:nth-child(5), +.knowledge-source-table td:nth-child(5), +.knowledge-document-table th:nth-child(4), +.knowledge-document-table td:nth-child(4), +.knowledge-document-table th:nth-child(5), +.knowledge-document-table td:nth-child(5), +.knowledge-document-table th:nth-child(6), +.knowledge-document-table td:nth-child(6) { + white-space: nowrap; +} + +.knowledge-page .summary-subheading h3 { + color: var(--text); + font-size: 16px; + line-height: 1.3; +} + +.knowledge-page input[type="text"], +.knowledge-page input[type="search"], +.knowledge-page textarea { + width: 100%; + min-height: 38px; + padding: 0 12px; + border: 1px solid var(--line); + border-radius: 8px; + background: #ffffff; + color: var(--text); + font: inherit; + font-size: 14px; + outline: none; +} + +.knowledge-page textarea { + min-height: 44px; + padding-top: 9px; + padding-bottom: 9px; + resize: vertical; + line-height: 1.5; +} + +.knowledge-page input[type="text"]:focus, +.knowledge-page input[type="search"]:focus, +.knowledge-page textarea:focus { + border-color: var(--accent); + box-shadow: 0 0 0 3px rgba(58, 114, 216, 0.14); +} + +.knowledge-page button { + min-height: 34px; + padding: 0 12px; + border: 1px solid var(--line); + border-radius: 8px; + background: #ffffff; + color: var(--accent); + cursor: pointer; + font: inherit; + font-size: 13px; + font-weight: 700; +} + +.knowledge-page button[type="submit"], +.knowledge-inline-actions button { + border-color: var(--accent); + background: var(--accent); + color: #ffffff; +} + +.knowledge-page button:hover:not(:disabled) { + border-color: var(--accent); + background: #eaf2ff; +} + +.knowledge-page button[type="submit"]:hover:not(:disabled), +.knowledge-inline-actions button:hover:not(:disabled) { + background: var(--accent-dark); + color: #ffffff; +} + +.knowledge-page button:disabled { + border-color: var(--line); + background: #f3f6fb; + color: var(--muted); + cursor: not-allowed; + opacity: 1; +} + +.knowledge-page .panel-empty { + margin: 0; + padding: 18px 16px; + border: 1px dashed var(--line); + border-radius: 8px; + background: #fbfdff; + color: var(--muted); + font-size: 13px; + line-height: 1.6; + text-align: center; +} + +.knowledge-upload-panel .summary-subheading span { + color: var(--muted); + font-size: 12px; + white-space: nowrap; +} + +.knowledge-upload-dropzone { + background: #f7faff; + text-align: center; +} + +.knowledge-upload-dropzone:hover { + border-color: var(--accent); + background: #eef5ff; +} + +.knowledge-parse-panel .knowledge-status { + min-height: 28px; + border-radius: 8px; + font-size: 12px; +} + +.knowledge-document-list-panel, +.knowledge-source-panel { + min-height: 152px; +} + +.knowledge-right-display .attachment-table th, +.knowledge-right-display .attachment-table td { + padding-top: 11px; + padding-bottom: 11px; +} + +.knowledge-document-list-panel .summary-subheading h3, +.knowledge-source-panel .summary-subheading h3 { + max-width: none; + white-space: nowrap; +} + +.knowledge-document-table th:nth-child(5), +.knowledge-document-table td:nth-child(5) { + white-space: nowrap; +} + @media (max-width: 640px) { .tabbar { overflow-x: auto; @@ -1510,9 +2219,80 @@ input:focus { grid-template-columns: 1fr; } + .knowledge-workbench { + grid-template-columns: 1fr; + } + .attachment-search { width: 100%; } + + .knowledge-page { + height: auto; + min-height: calc(100vh - 60px); + padding: 12px; + } + + .knowledge-hero { + align-items: stretch; + flex-direction: column; + } + + .knowledge-status-panel, + .knowledge-summary-row, + .knowledge-grid, + .knowledge-main-grid, + .knowledge-secondary-grid, + .knowledge-system-grid, + .knowledge-search-form { + grid-template-columns: 1fr; + } + + .knowledge-summary-item { + border-right: 0; + border-bottom: 1px solid var(--line); + } + + .knowledge-summary-item:last-child { + border-bottom: 0; + } + + .knowledge-hero-actions { + align-items: stretch; + flex-direction: column; + } + + .knowledge-toolbar-actions, + .knowledge-form-actions, + .knowledge-inline-actions { + align-items: stretch; + flex-direction: column; + } + + .knowledge-toolbar-actions .attachment-search, + .knowledge-toolbar-actions button, + .knowledge-form-actions button, + .knowledge-inline-actions button { + width: 100%; + } + + .knowledge-compact-stats { + grid-template-columns: 1fr; + } + + .knowledge-compact-stats div { + border-right: 0; + border-bottom: 1px solid var(--line); + } + + .knowledge-compact-stats div:last-child { + border-bottom: 0; + } + + .knowledge-definition-list div { + grid-template-columns: 1fr; + gap: 4px; + } } @keyframes pulse-caret { diff --git a/static/js/app.js b/static/js/app.js index 58e1230..015a1f5 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -26,6 +26,13 @@ return; } + function getCsrfToken() { + if (!composer) { + return ""; + } + return new FormData(composer).get("csrfmiddlewaretoken") || ""; + } + function isMobile() { return window.matchMedia("(max-width: 980px)").matches; } @@ -415,19 +422,60 @@ empty.remove(); } - var item = document.createElement("a"); + var item = document.createElement("div"); item.className = "history-item active"; item.setAttribute("data-conversation-id", conversationId); - item.href = "/?conversation=" + conversationId; + item.setAttribute("data-delete-url", "/api/review-agent/conversations/" + conversationId + "/"); item.innerHTML = - '' + + '' + escapeHtml(encodedTitle) + '' + meta + - ""; + ''; list.prepend(item); } + async function deleteConversation(item) { + if (!item) { + return; + } + var url = item.getAttribute("data-delete-url"); + var conversationId = item.getAttribute("data-conversation-id"); + if (!url || !conversationId) { + return; + } + var titleNode = item.querySelector(".history-title"); + var title = titleNode ? titleNode.textContent.trim() : "这个对话"; + if (!window.confirm('确定删除对话“' + title + '”?')) { + return; + } + var response = await fetch(url, { + method: "DELETE", + headers: { + "X-CSRFToken": getCsrfToken(), + }, + }); + if (!response.ok) { + throw new Error("删除对话失败"); + } + var isCurrent = currentConversationId() === conversationId; + item.remove(); + var list = document.querySelector(".history-list"); + if (list && !list.querySelector(".history-item")) { + var empty = document.createElement("div"); + empty.className = "history-empty"; + empty.innerHTML = "

暂无会话记录

点击上方“新对话”开始审核。"; + list.appendChild(empty); + } + if (isCurrent) { + window.location.href = "/"; + } + } + function setConversationTitle(title) { if (!title) { return; @@ -1167,6 +1215,25 @@ }); } + function bindConversationDeleteButtons() { + var list = document.querySelector(".history-list"); + if (!list) { + return; + } + list.addEventListener("click", function (event) { + var button = event.target.closest("[data-conversation-delete]"); + if (!button) { + return; + } + event.preventDefault(); + event.stopPropagation(); + var item = button.closest(".history-item"); + deleteConversation(item).catch(function () { + window.alert("删除对话失败,请稍后重试。"); + }); + }); + } + syncNodeRailVisibility(); syncLatestMessageIdFromDom(); bindNodeAnchorClicks(); @@ -1176,6 +1243,7 @@ bindConditionConfirmForms(); bindRectificationActionButtons(); bindPromptTemplateButtons(); + bindConversationDeleteButtons(); refreshRunningWorkflowCards(); if (chatScroll) { diff --git a/templates/base.html b/templates/base.html index a6064cd..adeff1b 100644 --- a/templates/base.html +++ b/templates/base.html @@ -5,7 +5,7 @@ {% block title %}DEMO-AGENT V2{% endblock %} - + {% block content %}{% endblock %} diff --git a/templates/home.html b/templates/home.html index ef75d33..82d1c7c 100644 --- a/templates/home.html +++ b/templates/home.html @@ -10,8 +10,8 @@ @@ -72,14 +72,26 @@