From 8ad4768476acd1f3285ad710f09ef2e4d9e1ec14 Mon Sep 17 00:00:00 2001 From: oliver Date: Tue, 11 Aug 2026 03:39:04 +0800 Subject: [PATCH] Polish Admin/Chat UI: utilities, chat tokens, denser topbar. Replace high-traffic inline styles with shared utility classes, retokenize chat chrome off white-mist hardcodes, and align the admin topbar to NetX 48px density. Co-authored-by: Cursor --- interfaces/admin/static/app.js | 239 ++++++++++----------- interfaces/admin/static/chat.js | 120 +++++------ interfaces/admin/static/index.html | 6 +- interfaces/admin/static/styles.css | 120 ++++++++++- interfaces/admin/static/theme-deepseek.css | 54 ++--- 5 files changed, 311 insertions(+), 228 deletions(-) diff --git a/interfaces/admin/static/app.js b/interfaces/admin/static/app.js index 20cf22a2..7d08307b 100644 --- a/interfaces/admin/static/app.js +++ b/interfaces/admin/static/app.js @@ -2231,7 +2231,7 @@ async function renderStack() { .map((rid) => { const item = promptsMap[rid] && typeof promptsMap[rid] === "object" ? promptsMap[rid] : {}; const finalSystem = String(item.system_prompt || ""); - return el("div", { class: "card", style: "margin-top:10px;" }, [ + return el("div", { class: "card u-mt-10" }, [ el("div", { class: "card__title", text: `role: ${rid}` }), el("div", {}, [ el("div", { class: "muted", text: "system_prompt" }), @@ -2285,7 +2285,7 @@ async function renderStack() { el("div", { class: "card" }, [ el("div", { class: "card__title", text: t("stack.title") }), el("div", { class: "row" }, [btnUp, btnDown]), - el("div", { style: "height:10px" }), + el("div", { class: "u-h-10" }), el("table", { class: "table" }, [ el("thead", {}, [el("tr", {}, [el("th", { text: t("table.pid") }), el("th", { text: t("table.service") }), el("th", { text: "端口" }), el("th", { text: t("table.status") })])]), el("tbody", {}, items), @@ -2934,7 +2934,7 @@ async function renderUserManagement() { await loadAccounts(); const tenantsListHint = allTenants.length > 1 - ? el("div", { class: "muted", style: "margin-top:8px", text: t("tenants.allTenantsHint") }) + ? el("div", { class: "muted u-mt-8", text: t("tenants.allTenantsHint") }) : el("div", {}); return el("div", {}, [ @@ -2942,7 +2942,7 @@ async function renderUserManagement() { el("div", { class: "card__title", text: t("tenants.title") }), el("div", { class: "row" }, [createName, btnCreateTenant]), el("div", { class: "row" }, [el("label", { text: t("tenants.select") }), selector]), - el("div", { class: "muted", style: "margin-top:6px", text: t("tenants.scopeHint") }), + el("div", { class: "muted u-mt-6", text: t("tenants.scopeHint") }), el("table", { class: "table" }, [ el("thead", {}, [el("tr", {}, [ el("th", { text: t("tenants.colTenantName") }), @@ -4004,7 +4004,7 @@ async function renderModels() { const bindingWrap = el("div", {}); const opsAiSpecialistSelect = el("select", { class: "input", disabled: !canConfigureBindings }); const opsAiProfileSelect = el("select", { class: "input", disabled: !canConfigureBindings }); - const modelsGrantsLinkRow = el("div", { class: "muted", style: "display:none" }); + const modelsGrantsLinkRow = el("div", { class: "muted u-hidden" }); const newName = el("input", { class: "input", placeholder: t("models.createNamePlaceholder") }); const newMode = el("select", { class: "input", disabled: !canMutateProfiles }, [ el("option", { value: "openai", text: t("models.mode.openai") }), @@ -4110,7 +4110,7 @@ async function renderModels() { const secretsStatusMsg = el("div", { class: "muted", text: "" }); const secretsMigrateBtn = el("button", { class: "btn", text: t("secrets.migrateBtn") }); - const secretsCard = el("div", { class: "card", style: "display:none" }, [ + const secretsCard = el("div", { class: "card u-hidden" }, [ el("div", { class: "card__title", text: t("secrets.migrateTitle") }), el("div", { class: "muted", text: t("secrets.migrateHint") }), el("div", { class: "row" }, [secretsMigrateBtn]), @@ -4788,7 +4788,7 @@ async function renderModels() { el("div", { class: "row" }, [btnDelete]), ], { id: "models-api" }), renderSectionCard("Experts", "Runtime registry and workspace prompt files are split into two sections below.", [ - el("div", { class: "card", style: "margin:8px 0;padding:10px;" }, [ + el("div", { class: "card u-pad-block" }, [ el("div", { class: "card__title", text: "Runtime Expert Registry" }), el("div", { class: "row" }, [el("label", { text: "Existing" }), expertsSelect]), el("div", { class: "row" }, [btnExpertDelete]), @@ -4800,7 +4800,7 @@ async function renderModels() { el("div", { class: "row" }, [el("label", { text: "Name(zh)" }), expertNameZh]), el("div", { class: "row" }, [el("label", { text: "Role" }), expertRoleSel]), ]), - el("div", { class: "card", style: "margin:8px 0;padding:10px;" }, [ + el("div", { class: "card u-pad-block" }, [ el("div", { class: "card__title", text: "Workspace Prompt Files" }), el("div", { class: "muted", text: "SOUL.md or ROLE_SYSTEM.md is required." }), el("div", { class: "row" }, [el("label", { text: "SOUL.md" }), expertSoul]), @@ -4809,7 +4809,7 @@ async function renderModels() { ]), expertsStatus, ], { id: "models-experts" }), - el("div", { class: "card section-card", id: "models-eval", style: "display:none" }, [evalDetails]), + el("div", { class: "card section-card u-hidden", id: "models-eval" }, [evalDetails]), ]); } @@ -5119,44 +5119,39 @@ async function renderPlugins() { const preflightFixWrap = el("div"); const toolPolicyStatus = el("div", { class: "muted", text: "" }); const turnMaxWorkersInput = el("input", { - class: "input", + class: "input u-max-w-120", type: "number", min: "1", max: "32", value: String(Number(toolPolicy.turn_max_tool_workers || 8)), - style: "max-width:120px", }); const turnMaxRoundsInput = el("input", { - class: "input", + class: "input u-max-w-120", type: "number", min: "1", max: "300", value: String(Number(toolPolicy.turn_max_tool_rounds || 100)), - style: "max-width:120px", }); const turnMaxCtxInput = el("input", { - class: "input", + class: "input u-max-w-120", type: "number", min: "10", max: "400", value: String(Number(toolPolicy.turn_max_context_messages || 80)), - style: "max-width:120px", }); const sseQueueMaxsizeInput = el("input", { - class: "input", + class: "input u-max-w-140", type: "number", min: "200", max: "50000", value: String(Number(toolPolicy.sse_queue_maxsize || 2000)), - style: "max-width:140px", }); const toolLogMaxCharsInput = el("input", { - class: "input", + class: "input u-max-w-140", type: "number", min: "20000", max: "2000000", value: String(Number(toolPolicy.tool_log_max_chars || 200000)), - style: "max-width:140px", }); const enableMcpToolsCb = el("input", { type: "checkbox" }); enableMcpToolsCb.checked = !!toolPolicy.enable_mcp_tools; @@ -5169,12 +5164,11 @@ async function renderPlugins() { const chatShowTtftDebugCb = el("input", { type: "checkbox" }); chatShowTtftDebugCb.checked = !!toolPolicy.chat_show_ttft_debug; const toolLlmMessageMaxCharsInput = el("input", { - class: "input", + class: "input u-max-w-140", type: "number", min: "0", max: "500000", value: String(Number(toolPolicy.tool_llm_message_max_chars ?? 0)), - style: "max-width:140px", }); const mcpFilesystemExtraRootsInput = el("input", { class: "input", @@ -5194,20 +5188,18 @@ async function renderPlugins() { const oclawRetryCodesStrictModeCb = el("input", { type: "checkbox" }); oclawRetryCodesStrictModeCb.checked = !!toolPolicy.oclaw_retry_codes_strict_mode; const wecomLongconnWorkersInput = el("input", { - class: "input", + class: "input u-max-w-120", type: "number", min: "1", max: "8", value: String(Number(toolPolicy.wecom_longconn_workers || 2)), - style: "max-width:120px", }); const wecomLongconnInboundQueueInput = el("input", { - class: "input", + class: "input u-max-w-140", type: "number", min: "20", max: "5000", value: String(Number(toolPolicy.wecom_longconn_inbound_queue_maxsize || 200)), - style: "max-width:140px", }); const saveToolPolicyBtn = el("button", { class: "btn", @@ -5735,7 +5727,7 @@ async function renderPlugins() { } }, }); - const cliInstallModal = el("div", { class: "session-monitor-modal", style: "display:none;" }); + const cliInstallModal = el("div", { class: "session-monitor-modal u-hidden" }); const cliInstallModalTitle = el("div", { class: "card__title", text: "MCP CLI install" }); const cliInstallModalBody = el("pre", { class: "pre", text: "" }); const closeCliInstallModal = () => { @@ -5757,7 +5749,7 @@ async function renderPlugins() { el("div", { class: "card session-monitor-modal__card", style: "width:min(720px,96vw);" }, [ cliInstallModalTitle, cliInstallModalBody, - el("div", { class: "row", style: "justify-content:flex-end;margin-top:10px;" }, [ + el("div", { class: "row u-row-end" }, [ el("button", { class: "btn", text: "Close", onclick: closeCliInstallModal }), ]), ]), @@ -5916,13 +5908,13 @@ async function renderPlugins() { healthBtn.className = "chat-sess-menu-item"; syncBtn.className = "chat-sess-menu-item"; const actionMenuBtn = el("button", { - class: "chat-sess-more", + class: "chat-sess-more u-overlay-fixed", text: "⋯", title: "MCP actions", onclick: (ev) => { ev.stopPropagation(); closeMcpActionMenu(); - const menu = el("div", { class: "chat-sess-menu-pop", style: "position:fixed;z-index:250;" }, [ + const menu = el("div", { class: "chat-sess-menu-pop" }, [ toggleBtn, healthBtn, syncBtn, @@ -6353,7 +6345,7 @@ pip install git+https://github.com/philschmid/code-sandbox-mcp.git && python -m ]), ]), usageSummaryPager.wrap, - el("div", { class: "muted", text: "Recent MCP Calls", style: "margin-top:10px;" }), + el("div", { class: "muted u-mt-10", text: "Recent MCP Calls" }), el("div", { class: "table-wrap" }, [ el("table", { class: "table table--compact" }, [ el("thead", {}, [el("tr", {}, [ @@ -6568,7 +6560,7 @@ async function renderSessionMonitor() { const detailStatus = el("div", { class: "muted", text: t("sessionMonitor.noMessages") }); const detailPager = el("div", { class: "row" }); const detailRoleFilter = el("select", { class: "input" }); - const detailModal = el("div", { class: "session-monitor-modal", style: "display:none;" }); + const detailModal = el("div", { class: "session-monitor-modal u-hidden" }); const detailModalCard = el("div", { class: "session-monitor-modal__card" }); const totalsWrap = el("div", { class: "row" }); let selectedUserId = ""; @@ -6818,7 +6810,7 @@ async function renderSessionMonitor() { onclick: (ev) => { ev.stopPropagation(); closeSessionMenu(); - const menu = el("div", { class: "chat-sess-menu-pop", style: "position:fixed;z-index:250;" }, [ + const menu = el("div", { class: "chat-sess-menu-pop u-overlay-fixed" }, [ el("button", { class: "chat-sess-menu-item", text: t("sessionMonitor.viewDetail"), @@ -7257,10 +7249,10 @@ async function renderWorkspacePaths() { el("div", { class: "muted", text: t("workspacePaths.help") }), ]; if (selfService) { - cardParts.push(el("div", { class: "muted", style: "margin-top:6px;", text: t("workspacePaths.selfOnly") })); + cardParts.push(el("div", { class: "muted u-mt-6", text: t("workspacePaths.selfOnly") })); } cardParts.push( - el("div", { style: "height:10px" }), + el("div", { class: "u-h-10" }), el("div", { class: "row" }, [ el("label", { text: t("workspacePaths.tenant") }), tenantSel, @@ -7275,14 +7267,14 @@ async function renderWorkspacePaths() { allowAnyCb, el("span", { text: t("workspacePaths.allowAny") }), ]), - el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("workspacePaths.allowAnyHint") }), + el("div", { class: "muted u-muted-block", text: t("workspacePaths.allowAnyHint") }), el("label", { class: "row", style: "align-items:center;gap:8px;margin-top:10px;" }, [ allowHighToolsCb, el("span", { text: t("workspacePaths.allowHighTools") }), ]), - el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("workspacePaths.allowHighToolsHint") }), + el("div", { class: "muted u-muted-block", text: t("workspacePaths.allowHighToolsHint") }), el("div", { class: "row", style: "margin-top:10px;gap:8px;" }, [loadBtn, saveBtn]), - el("div", { class: "muted", style: "margin-top:8px;" }, [el("span", { text: t("workspacePaths.status") + ": " }), status]), + el("div", { class: "muted u-mt-8" }, [el("span", { text: t("workspacePaths.status") + ": " }), status]), ); return el("div", { class: "card" }, cardParts); } @@ -7488,16 +7480,16 @@ async function renderAttachments() { inputRow(t("attachments.toolModeMinRows"), toolMinRowsInput), inputRow(t("attachments.toolModeMaxBytes"), toolMaxBytesInput), inputRow(t("attachments.sqlTimeoutMs"), sqlTimeoutInput), - el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("attachments.sqlTimeoutHint") }), + el("div", { class: "muted u-muted-block", text: t("attachments.sqlTimeoutHint") }), ], { id: "attach-table" }), renderSectionCard("图像与视频策略", "", [ inputRow(t("attachments.imageReplayCapChars"), imageReplayCapInput), - el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("attachments.imageReplayCapHint") }), + el("div", { class: "muted u-muted-block", text: t("attachments.imageReplayCapHint") }), inputRow(t("attachments.videoReplayCapChars"), videoReplayCapInput), - el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("attachments.videoReplayCapHint") }), + el("div", { class: "muted u-muted-block", text: t("attachments.videoReplayCapHint") }), inputRow(t("attachments.videoTranscriptChunkSize"), videoTranscriptChunkSizeInput), inputRow(t("attachments.videoTranscriptChunkOverlap"), videoTranscriptChunkOverlapInput), - el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("attachments.videoTranscriptChunkHint") }), + el("div", { class: "muted u-muted-block", text: t("attachments.videoTranscriptChunkHint") }), ], { id: "attach-media" }), ]), renderSectionCard("压缩包预算策略", "", [ @@ -7505,8 +7497,8 @@ async function renderAttachments() { inputRow(t("attachments.archiveMaxFileCount"), archiveMaxFileCountInput), inputRow(t("attachments.archiveMaxEntryBytes"), archiveMaxEntryBytesInput), inputRow(t("attachments.archiveMaxTotalBytes"), archiveMaxTotalBytesInput), - el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("attachments.archivePolicyHint") }), - el("div", { style: "height:8px" }), + el("div", { class: "muted u-muted-block", text: t("attachments.archivePolicyHint") }), + el("div", { class: "u-h-8" }), status, ], { id: "attach-archive" }), ]); @@ -7518,10 +7510,8 @@ async function renderProfile() { const metaUser = el("div", { class: "muted pre", text: "" }); const metaIds = el("div", { class: "muted pre", text: "" }); const avatarPreview = el("img", { - class: "profile-avatar-preview", + class: "profile-avatar-preview u-hidden u-avatar-chip", alt: "", - style: - "display:none;width:48px;height:48px;border-radius:999px;object-fit:contain;border:2px solid rgba(255,255,255,0.12);background:rgba(255,255,255,0.08);padding:5px;box-sizing:border-box;", }); const avatarRow = el("div", { class: "row", style: "align-items:center;gap:16px;flex-wrap:wrap;" }, [ el("div", {}, [avatarPreview]), @@ -7530,7 +7520,7 @@ async function renderProfile() { el("div", { class: "muted", style: "margin-bottom:8px;font-size:12px;line-height:1.4;", text: t("profile.avatarHint") }), ]), ]); - const fileInput = el("input", { type: "file", accept: "image/png,image/jpeg,image/jpg,image/webp,image/gif", style: "display:none" }); + const fileInput = el("input", { class: "u-hidden", type: "file", accept: "image/png,image/jpeg,image/jpg,image/webp,image/gif" }); const chooseBtn = el("button", { class: "btn", type: "button", text: t("profile.chooseImage") }); const removeBtn = el("button", { class: "btn", type: "button", text: t("profile.removeAvatar") }); const saveBtn = el("button", { class: "btn btn--primary", type: "button", text: t("profile.save") }); @@ -7649,14 +7639,14 @@ async function renderProfile() { el("div", { class: "card__title", text: t("title.profile") }), el("div", { class: "muted", style: "margin-bottom:10px;line-height:1.45;", text: t("profile.help") }), el("div", { class: "row" }, [el("label", { text: t("profile.displayName") }), dnInput]), - el("div", { style: "height:8px" }), + el("div", { class: "u-h-8" }), avatarRow, el("div", { class: "row", style: "margin-top:10px;gap:8px;flex-wrap:wrap;" }, [fileInput, chooseBtn, removeBtn]), el("div", { class: "row", style: "margin-top:12px;gap:8px;flex-wrap:wrap;" }, [saveBtn, chatBtn]), - el("div", { style: "height:8px" }), + el("div", { class: "u-h-8" }), el("div", { style: "margin-top:14px" }, [metaUser]), - el("div", { style: "margin-top:6px" }, [metaIds]), - el("div", { class: "muted", style: "margin-top:10px;" }, [status]), + el("div", { class: "u-mt-6" }, [metaIds]), + el("div", { class: "muted u-mt-10" }, [status]), ]); return el("div", {}, [profileCard]); } @@ -7775,19 +7765,19 @@ async function renderSkills() { const btnMarketSearch = el("button", { class: "btn", text: "Search", onclick: async () => await loadMarket(marketQ.value) }); const btnMarketLatest = el("button", { class: "btn", text: "Latest", onclick: async () => await loadMarket("") }); - const marketBox = el("details", { style: "margin:10px 0 14px 0;" }, [ - el("summary", { text: "Skill market (ClawHub / CocoLoop)", style: "cursor:pointer;user-select:none;" }), - el("div", { style: "height:8px" }), - el("div", { class: "row", style: "gap:8px;flex-wrap:wrap;margin-bottom:8px;" }, [marketQ, marketLimitInp, btnMarketSearch, btnMarketLatest]), + const marketBox = el("details", { class: "u-spacer-v" }, [ + el("summary", { class: "u-drag", text: "Skill market (ClawHub / CocoLoop)" }), + el("div", { class: "u-h-8" }), + el("div", { class: "row u-row-wrap-mb" }, [marketQ, marketLimitInp, btnMarketSearch, btnMarketLatest]), marketStatus, - el("div", { style: "height:8px" }), + el("div", { class: "u-h-8" }), el("div", { class: "table-wrap" }, [ el("table", { class: "table table--compact" }, [ el("thead", {}, [el("tr", {}, [el("th", { text: "slug" }), el("th", { text: "name" }), el("th", { text: "version" }), el("th", { text: "description" }), el("th", { text: "action" })])]), marketTbody, ]), ]), - el("div", { style: "height:8px" }), + el("div", { class: "u-h-8" }), el("div", { class: "muted", text: "Detail" }), marketDetailPre, ]); @@ -7840,13 +7830,13 @@ async function renderSkills() { const namesCell = names.length ? el("details", {}, [ el("summary", { text: previewText || "(empty)" }), - el("div", { class: "muted", style: "margin-top:4px;white-space:normal;line-height:1.5;", text: names.join(", ") }), + el("div", { class: "muted u-mt-4 u-wrap-lh", text: names.join(", ") }), ]) : el("span", { class: "muted", text: "-" }); const docsOnlyCell = docsOnlyNames.length ? el("details", {}, [ el("summary", { text: docsOnlyNames.slice(0, 4).join(", ") }), - el("div", { class: "muted", style: "margin-top:4px;white-space:normal;line-height:1.5;", text: docsOnlyNames.join(", ") }), + el("div", { class: "muted u-mt-4 u-wrap-lh", text: docsOnlyNames.join(", ") }), ]) : el("span", { class: "muted", text: "-" }); skillEffectiveTbody.appendChild( @@ -7902,7 +7892,7 @@ async function renderSkills() { else prev.delete(nm); draft[current] = Array.from(prev); }); - skillBindingListWrap.appendChild(el("label", { class: "row", style: "gap:6px;align-items:center;" }, [cb, el("span", { text: nm })])); + skillBindingListWrap.appendChild(el("label", { class: "row u-row-center" }, [cb, el("span", { text: nm })])); }); }; const loadSkillBinding = async () => { @@ -7977,11 +7967,10 @@ async function renderSkills() { } }, }); - const skillBindingBox = el("details", { style: "margin:10px 0 14px 0;" }, [ - el("summary", { text: "Skill role binding (specialists)", style: "cursor:pointer;user-select:none;" }), + const skillBindingBox = el("details", { class: "u-spacer-v" }, [ + el("summary", { class: "u-drag", text: "Skill role binding (specialists)" }), el("div", { - class: "muted", - style: "margin:8px 0;line-height:1.5;", + class: "muted u-muted-block-lg", text: "When enabled, the model skills catalog is filtered per role: each role sees only skills bound to that role, plus skills under skills/_workspace/public/. If nothing is bound yet, non-public skills are hidden until you assign them.", }), @@ -7992,19 +7981,19 @@ async function renderSkills() { skillBindingEnabledCb, el("span", { text: "Enable role binding (AIA_SKILL_ROLE_BINDING_ENABLED)" }), ]), - el("div", { class: "row", style: "gap:8px;flex-wrap:wrap;margin-top:8px;align-items:center;" }, [ + el("div", { class: "row u-row-wrap-mt" }, [ el("label", { text: "Role" }), skillRoleSelect, btnSaveSkillBinding, ]), - el("div", { class: "muted", style: "margin:8px 0 4px 0;", text: "Role binding dashboard" }), + el("div", { class: "muted u-label-mt", text: "Role binding dashboard" }), el("div", { class: "table-wrap" }, [ el("table", { class: "table table--compact" }, [ el("thead", {}, [el("tr", {}, [el("th", { text: "role" }), el("th", { text: "direct skills" }), el("th", { text: "effective" })])]), skillBindingDashTbody, ]), ]), - el("div", { class: "muted", style: "margin:8px 0 4px 0;", text: "Effective skills dashboard (binding + MCP + tools)" }), + el("div", { class: "muted u-label-mt", text: "Effective skills dashboard (binding + MCP + tools)" }), el("div", { class: "table-wrap" }, [ el("table", { class: "table table--compact" }, [ el("thead", {}, [el("tr", {}, [ @@ -8079,11 +8068,11 @@ async function renderSkills() { text: "Preview", onclick: async () => await loadInternalToolsPreview(internalRoleSelect.value), }); - const internalToolsBox = el("details", { style: "margin:10px 0 14px 0;" }, [ - el("summary", { text: "Internal tools preview (public + expert)", style: "cursor:pointer;user-select:none;" }), - el("div", { class: "muted", style: "margin:8px 0;line-height:1.5;", text: "Preview dynamically loaded builtin tools for a role (public shared + expert-scoped)." }), + const internalToolsBox = el("details", { class: "u-spacer-v" }, [ + el("summary", { class: "u-drag", text: "Internal tools preview (public + expert)" }), + el("div", { class: "muted u-muted-block-lg", text: "Preview dynamically loaded builtin tools for a role (public shared + expert-scoped)." }), internalToolsStatus, - el("div", { class: "row", style: "gap:8px;flex-wrap:wrap;margin-top:8px;align-items:center;" }, [ + el("div", { class: "row u-row-wrap-mt" }, [ el("label", { text: "Role" }), internalRoleSelect, btnInternalPreview, @@ -8101,7 +8090,7 @@ async function renderSkills() { }, }), ]), - el("div", { class: "table-wrap", style: "margin-top:8px" }, [ + el("div", { class: "table-wrap u-mt-8" }, [ el("table", { class: "table table--compact" }, [ el("thead", {}, [el("tr", {}, [ el("th", { text: "source" }), @@ -8114,7 +8103,7 @@ async function renderSkills() { internalToolsTbody, ]), ]), - el("div", { class: "muted", style: "margin-top:8px", text: "Skipped modules / factory errors" }), + el("div", { class: "muted u-mt-8", text: "Skipped modules / factory errors" }), internalSkippedPre, ]); @@ -8127,7 +8116,7 @@ async function renderSkills() { const llmModeBadge = el("span", { class: "badge badge--mode-restricted", text: "restricted" }); const llmDiffPre = el("pre", { class: "muted pre", text: "" }); const llmDiffTbody = el("tbody"); - const llmDiffTableWrap = el("div", { class: "table-wrap", style: "margin-top:8px" }, [ + const llmDiffTableWrap = el("div", { class: "table-wrap u-mt-8" }, [ el("table", { class: "table table--compact" }, [ el("thead", {}, [el("tr", {}, [ el("th", { text: "type" }), @@ -8215,16 +8204,16 @@ async function renderSkills() { selfCheckStatus.textContent = `export failed: ${String(e && e.message ? e.message : e)}`; } }; - const selfCheckBox = el("details", { style: "margin:10px 0 14px 0;" }, [ - el("summary", { text: "Tools self-check", style: "cursor:pointer;user-select:none;" }), - el("div", { class: "muted", style: "margin:8px 0;line-height:1.5;", text: "Run one-shot diagnostics across roles for internal/wired tools, role mode and permanent bans." }), + const selfCheckBox = el("details", { class: "u-spacer-v" }, [ + el("summary", { class: "u-drag", text: "Tools self-check" }), + el("div", { class: "muted u-muted-block-lg", text: "Run one-shot diagnostics across roles for internal/wired tools, role mode and permanent bans." }), selfCheckStatus, - el("div", { class: "row", style: "gap:8px;align-items:center;margin-top:8px;" }, [ + el("div", { class: "row u-row-center-mt" }, [ el("button", { class: "btn btn--primary", text: "Run self-check", onclick: runSelfCheck }), el("button", { class: "btn", text: "Export self-check JSON", onclick: exportSelfCheckJson }), selfCheckSummary, ]), - el("div", { class: "table-wrap", style: "margin-top:8px" }, [ + el("div", { class: "table-wrap u-mt-8" }, [ el("table", { class: "table table--compact" }, [ el("thead", {}, [el("tr", {}, [ el("th", { text: "role" }), @@ -8335,14 +8324,14 @@ async function renderSkills() { skillHealthStatus.textContent = `export failed: ${String(e && e.message ? e.message : e)}`; } }; - const skillHealthBox = el("details", { style: "margin:10px 0 14px 0;" }, [ - el("summary", { text: "Skills health self-check", style: "cursor:pointer;user-select:none;" }), - el("div", { class: "muted", style: "margin:8px 0;line-height:1.5;", text: "Run executable skill diagnostics and show failure code distribution." }), + const skillHealthBox = el("details", { class: "u-spacer-v" }, [ + el("summary", { class: "u-drag", text: "Skills health self-check" }), + el("div", { class: "muted u-muted-block-lg", text: "Run executable skill diagnostics and show failure code distribution." }), skillHealthStatus, - el("div", { class: "row", style: "gap:8px;align-items:center;margin-top:8px;" }, [ + el("div", { class: "row u-row-center-mt" }, [ el("button", { class: "btn btn--primary", text: "Run skills self-check", onclick: runSkillHealthCheck }), el("button", { class: "btn", text: "Export skills self-check JSON", onclick: exportSkillHealthJson }), - el("label", { class: "row", style: "gap:6px;align-items:center;" }, [ + el("label", { class: "row u-row-center" }, [ skillHealthFailedOnlyCb, el("span", { class: "muted", text: "Only failed items" }), ]), @@ -8350,14 +8339,14 @@ async function renderSkills() { ]), el("div", { class: "row", style: "gap:12px;align-items:flex-start;flex-wrap:wrap;margin-top:8px;" }, [ el("div", { class: "table-wrap", style: "min-width:280px;flex:1;" }, [ - el("div", { class: "muted", style: "margin:0 0 6px 0;", text: "classification_counts" }), + el("div", { class: "muted u-mb-6", text: "classification_counts" }), el("table", { class: "table table--compact" }, [ el("thead", {}, [el("tr", {}, [el("th", { text: "code" }), el("th", { text: "count" })])]), skillHealthClassifyTbody, ]), ]), el("div", { class: "table-wrap", style: "min-width:360px;flex:2;" }, [ - el("div", { class: "muted", style: "margin:0 0 6px 0;", text: "execution_checks (top 30)" }), + el("div", { class: "muted u-mb-6", text: "execution_checks (top 30)" }), el("table", { class: "table table--compact" }, [ el("thead", {}, [el("tr", {}, [el("th", { text: "skill" }), el("th", { text: "status" }), el("th", { text: "error_code" }), el("th", { text: "output_truncated" }), el("th", { text: "action" })])]), skillHealthExecTbody, @@ -8475,11 +8464,11 @@ async function renderSkills() { const p0 = a.parameters ? JSON.stringify(a.parameters, null, 2) : ""; const p1 = b.parameters ? JSON.stringify(b.parameters, null, 2) : ""; const box = el("details", {}, [ - el("summary", { text: "compare", style: "cursor:pointer;user-select:none;" }), - el("div", { class: "muted", style: "margin-top:6px;" }, [el("div", { text: "raw.description" }), el("pre", { class: "muted pre", text: d0 })]), - el("div", { class: "muted", style: "margin-top:6px;" }, [el("div", { text: "wired.description" }), el("pre", { class: "muted pre", text: d1 })]), - el("div", { class: "muted", style: "margin-top:6px;" }, [el("div", { text: "raw.parameters" }), el("pre", { class: "muted pre", text: p0 })]), - el("div", { class: "muted", style: "margin-top:6px;" }, [el("div", { text: "wired.parameters" }), el("pre", { class: "muted pre", text: p1 })]), + el("summary", { class: "u-drag", text: "compare" }), + el("div", { class: "muted u-mt-6" }, [el("div", { text: "raw.description" }), el("pre", { class: "muted pre", text: d0 })]), + el("div", { class: "muted u-mt-6" }, [el("div", { text: "wired.description" }), el("pre", { class: "muted pre", text: d1 })]), + el("div", { class: "muted u-mt-6" }, [el("div", { text: "raw.parameters" }), el("pre", { class: "muted pre", text: p0 })]), + el("div", { class: "muted u-mt-6" }, [el("div", { text: "wired.parameters" }), el("pre", { class: "muted pre", text: p1 })]), ]); return box; }; @@ -8534,11 +8523,11 @@ async function renderSkills() { btnLLMToggle.textContent = next === "wired" ? "Toggle raw/wired" : "Toggle raw/wired"; }, }); - const llmToolsBox = el("details", { style: "margin:10px 0 14px 0;" }, [ - el("summary", { text: "LLM tools preview (after wire policy)", style: "cursor:pointer;user-select:none;" }), - el("div", { class: "muted", style: "margin:8px 0;line-height:1.5;", text: "Preview the final tools injected to the model for a role (internal + MCP + role_mode + MCP specialist binding)." }), + const llmToolsBox = el("details", { class: "u-spacer-v" }, [ + el("summary", { class: "u-drag", text: "LLM tools preview (after wire policy)" }), + el("div", { class: "muted u-muted-block-lg", text: "Preview the final tools injected to the model for a role (internal + MCP + role_mode + MCP specialist binding)." }), llmToolsStatus, - el("div", { class: "row", style: "gap:8px;flex-wrap:wrap;margin-top:8px;align-items:center;" }, [ + el("div", { class: "row u-row-wrap-mt" }, [ el("label", { text: "Role" }), llmRoleSelect, btnLLMPreview, @@ -8558,21 +8547,21 @@ async function renderSkills() { }), el("span", { class: "muted", text: "role_mode:" }), llmModeBadge, - el("label", { class: "row", style: "gap:6px;align-items:center;" }, [ + el("label", { class: "row u-row-center" }, [ llmTracePlanCb, el("span", { class: "muted", text: "Trace exposure plan" }), ]), el("button", { class: "btn", text: "Save trace setting", onclick: saveExposureTraceSetting }), ]), - el("div", { class: "table-wrap", style: "margin-top:8px" }, [ + el("div", { class: "table-wrap u-mt-8" }, [ el("table", { class: "table table--compact" }, [ el("thead", {}, [el("tr", {}, [el("th", { text: "name" }), el("th", { text: "description" }), el("th", { text: "kind" }), el("th", { text: "parameters" })])]), llmToolsTbody, ]), ]), - el("div", { class: "muted", style: "margin-top:8px", text: "Diff / diagnostics" }), + el("div", { class: "muted u-mt-8", text: "Diff / diagnostics" }), llmRemovedPre, - el("div", { class: "muted", style: "margin-top:8px", text: "raw vs wired diff (added/removed/changed)" }), + el("div", { class: "muted u-mt-8", text: "raw vs wired diff (added/removed/changed)" }), llmDiffPre, llmDiffTableWrap, ]); @@ -8587,10 +8576,10 @@ async function renderSkills() { } skillActionMenuEl = null; }; - const skillTestRunModal = el("div", { class: "session-monitor-modal", style: "display:none;" }); + const skillTestRunModal = el("div", { class: "session-monitor-modal u-hidden" }); const skillTestRunTitle = el("div", { class: "card__title", text: "Skill test run" }); const skillTestRunArgs = el("textarea", { class: "input", rows: "8", style: "width:100%;min-width:0;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;", placeholder: "{}" }); - const skillInstallModal = el("div", { class: "session-monitor-modal", style: "display:none;" }); + const skillInstallModal = el("div", { class: "session-monitor-modal u-hidden" }); const skillInstallTitle = el("div", { class: "card__title", text: "Skill installation" }); const skillInstallMsg = el("div", { class: "muted", style: "line-height:1.5;" }); const closeSkillInstallModal = () => { @@ -8614,7 +8603,7 @@ async function renderSkills() { const skillInstallCard = el("div", { class: "card session-monitor-modal__card", style: "width:min(560px,96vw);" }, [ skillInstallTitle, skillInstallMsg, - el("div", { class: "row", style: "justify-content:flex-end;margin-top:10px;" }, [ + el("div", { class: "row u-row-end" }, [ el("button", { class: "btn", text: "Close", onclick: closeSkillInstallModal }), ]), ]); @@ -8660,7 +8649,7 @@ async function renderSkills() { skillTestRunTitle, el("div", { class: "muted", style: "margin-bottom:8px;", text: "Input JSON args passed to the executable skill runtime." }), skillTestRunArgs, - el("div", { class: "row", style: "gap:8px;justify-content:flex-end;margin-top:10px;" }, [skillTestRunCancelBtn, skillTestRunRunBtn]), + el("div", { class: "row u-row-end" }, [skillTestRunCancelBtn, skillTestRunRunBtn]), ]); skillTestRunModal.appendChild(skillTestRunCard); const retryableOnlyCb = el("input", { type: "checkbox" }); @@ -8768,13 +8757,13 @@ async function renderSkills() { }, }); const actionMenuBtn = el("button", { - class: "chat-sess-more", + class: "chat-sess-more u-overlay-fixed", text: "⋯", title: "Skill actions", onclick: (ev) => { ev.stopPropagation(); closeSkillActionMenu(); - const menu = el("div", { class: "chat-sess-menu-pop", style: "position:fixed;z-index:250;" }, [ + const menu = el("div", { class: "chat-sess-menu-pop" }, [ toggleBtn, testRunBtn, repairDepsBtn, @@ -9022,7 +9011,7 @@ async function renderSkills() { return el("div", { class: "card" }, [ el("div", { class: "card__title", text: t("title.skills") }), el("div", { class: "row", style: "gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:8px;" }, [ - el("label", { class: "row", style: "gap:6px;align-items:center;" }, [skillPromptModeCb, el("span", { text: "Prompt mode (inject SKILL.md)" })]), + el("label", { class: "row u-row-center" }, [skillPromptModeCb, el("span", { text: "Prompt mode (inject SKILL.md)" })]), el("label", { class: "row", style: "gap:6px;align-items:center;flex-wrap:wrap;" }, [ el("span", { text: "Market (AIA_SKILL_MARKET_PROVIDER)" }), skillMarketProviderSelect, @@ -9032,25 +9021,25 @@ async function renderSkills() { ]), docsHint, marketBox, - el("details", { style: "margin:10px 0 14px 0;" }, [ - el("summary", { text: "Skill installation (local / registry)", style: "cursor:pointer;user-select:none;" }), - el("div", { style: "height:8px" }), + el("details", { class: "u-spacer-v" }, [ + el("summary", { class: "u-drag", text: "Skill installation (local / registry)" }), + el("div", { class: "u-h-8" }), el("div", { class: "muted", style: "margin-bottom:8px;line-height:1.5;" }, [ el("div", { text: "Local dir: 目录内必须包含 SKILL.md(支持绝对路径)。" }), el("div", { text: "Registry: zip/tar 的 URL(支持 file:// 本地 URI)。" }), ]), - el("div", { class: "row", style: "gap:8px;flex-wrap:wrap;margin-bottom:8px;" }, [regInp, btnInstallRegistry]), - el("div", { class: "row", style: "gap:8px;flex-wrap:wrap;margin-bottom:8px;" }, [localDirInp, btnInstallLocal]), + el("div", { class: "row u-row-wrap-mb" }, [regInp, btnInstallRegistry]), + el("div", { class: "row u-row-wrap-mb" }, [localDirInp, btnInstallLocal]), ]), skillBindingBox, selfCheckBox, skillHealthBox, internalToolsBox, llmToolsBox, - el("div", { class: "row", style: "gap:8px;flex-wrap:wrap;margin-bottom:8px;" }, [nameInp, descInp]), + el("div", { class: "row u-row-wrap-mb" }, [nameInp, descInp]), el("div", { style: "margin-bottom:8px;" }, [bodyInp]), - el("div", { class: "row", style: "gap:8px;flex-wrap:wrap;margin-bottom:8px;" }, [btnCreate]), - el("div", { class: "row", style: "gap:8px;flex-wrap:wrap;margin-bottom:8px;" }, [btnRefresh, btnRepairDepsAll]), + el("div", { class: "row u-row-wrap-mb" }, [btnCreate]), + el("div", { class: "row u-row-wrap-mb" }, [btnRefresh, btnRepairDepsAll]), el("div", { class: "table-wrap" }, [ el("table", { class: "table table--compact" }, [ el("thead", {}, [el("tr", {}, [ @@ -9065,10 +9054,10 @@ async function renderSkills() { tbody, ]), ]), - el("div", { style: "height:10px" }), + el("div", { class: "u-h-10" }), el("div", { class: "row", style: "gap:8px;align-items:center;" }, [ el("div", { class: "muted", text: "Recent skill install audit" }), - el("label", { class: "row", style: "gap:6px;align-items:center;" }, [ + el("label", { class: "row u-row-center" }, [ retryableOnlyCb, el("span", { class: "muted", text: "Only retryable failures" }), ]), @@ -9135,7 +9124,7 @@ async function renderScheduledJobs() { const runsBox = el("pre", { class: "code", style: "max-height:220px;overflow:auto;white-space:pre-wrap;" }); const msg = el("div", { class: "muted", text: "" }); - const editModal = el("div", { class: "session-monitor-modal", style: "display:none;" }); + const editModal = el("div", { class: "session-monitor-modal u-hidden" }); const editTitle = el("div", { class: "card__title", text: t("scheduledJobs.editTitle") }); const editNameInput = el("input", { class: "input", placeholder: "name" }); const editKindInput = el("select", {}, [ @@ -9206,11 +9195,11 @@ async function renderScheduledJobs() { editTitle, editWeixinInfo, editNameInput, - el("div", { class: "row", style: "gap:8px;" }, [editKindInput, editExprInput]), + el("div", { class: "row u-gap-8" }, [editKindInput, editExprInput]), editPromptInput, editSpecialistInput, editWaChatInput, - el("div", { class: "row", style: "gap:8px;justify-content:flex-end;margin-top:10px;" }, [ + el("div", { class: "row u-row-end" }, [ el("button", { class: "btn", text: t("scheduledJobs.cancel"), onclick: closeEditModal }), editSaveBtn, ]), @@ -9246,13 +9235,13 @@ async function renderScheduledJobs() { for (const job of resp.items || []) { const jobId = String(job.id || ""); const btnMore = el("button", { - class: "chat-sess-more", + class: "chat-sess-more u-overlay-fixed", text: "⋯", title: t("scheduledJobs.menuTitle"), onclick: (ev) => { ev.stopPropagation(); closeScheduledJobMenu(); - const menu = el("div", { class: "chat-sess-menu-pop", style: "position:fixed;z-index:250;" }, [ + const menu = el("div", { class: "chat-sess-menu-pop" }, [ el("button", { class: "chat-sess-menu-item", text: t("scheduledJobs.viewRuns"), @@ -9391,7 +9380,7 @@ async function renderScheduledJobs() { el("div", { class: "card__title", text: t("scheduledJobs.createTitle") }), weixinInfo, nameInput, - el("div", { class: "row", style: "gap:8px;" }, [kindInput, exprInput]), + el("div", { class: "row u-gap-8" }, [kindInput, exprInput]), promptInput, specialistInput, waChatInput, @@ -9427,10 +9416,10 @@ async function renderScheduledJobs() { el("colgroup", {}, [ el("col", { style: "width:14%" }), el("col", { style: "width:14%" }), - el("col", { style: "width:12%" }), + el("col", { class: "u-w-12p" }), el("col", { style: "width:8%" }), - el("col", { style: "width:12%" }), - el("col", { style: "width:12%" }), + el("col", { class: "u-w-12p" }), + el("col", { class: "u-w-12p" }), el("col", { style: "width:8%" }), el("col", { style: "width:10%" }), el("col", { style: "width:10%" }), diff --git a/interfaces/admin/static/chat.js b/interfaces/admin/static/chat.js index de4ea0d2..f1223c1f 100644 --- a/interfaces/admin/static/chat.js +++ b/interfaces/admin/static/chat.js @@ -982,12 +982,10 @@ async function openBackgroundJobsPanel() { } const backdrop = el("div", { - class: "chat-confirm-backdrop chat-jobs-backdrop", - style: "z-index:9998;", + class: "chat-confirm-backdrop chat-jobs-backdrop u-z-modal", }); const card = el("div", { - class: "chat-confirm-card chat-jobs-card", - style: "max-width:920px;width:94vw;max-height:82vh;display:flex;flex-direction:column;gap:10px;", + class: "chat-confirm-card chat-jobs-card u-jobs-card", }); const title = el("div", { class: "card__title", text: t("chat.jobsTitle") }); const closeBtn = el("button", { type: "button", class: "btn", text: t("chat.jobsClose") }); @@ -997,15 +995,14 @@ async function openBackgroundJobsPanel() { class: "btn btn--danger", text: t("chat.jobsKillAll"), }); - const head = el("div", { class: "row", style: "gap:8px;justify-content:space-between;align-items:center;flex-wrap:wrap;" }, [ + const head = el("div", { class: "row u-row-between" }, [ title, - el("div", { class: "row", style: "gap:8px;" }, [refreshBtn, killAllBtn, closeBtn]), + el("div", { class: "row u-gap-8" }, [refreshBtn, killAllBtn, closeBtn]), ]); - const hint = el("div", { class: "muted", style: "font-size:12px;line-height:1.45;", text: t("chat.jobsHint") }); - const summary = el("div", { class: "muted", style: "font-size:12px;" }); + const hint = el("div", { class: "muted u-hint", text: t("chat.jobsHint") }); + const summary = el("div", { class: "muted u-fs-12" }); const list = el("div", { - class: "chat-jobs-list", - style: "overflow:auto;flex:1;min-height:220px;max-height:58vh;display:flex;flex-direction:column;gap:8px;", + class: "chat-jobs-list u-jobs-list", }); const close = () => { @@ -1059,7 +1056,7 @@ async function openBackgroundJobsPanel() { }), ], ); - const actions = el("div", { class: "chat-jobs-row__actions row", style: "gap:8px;" }); + const actions = el("div", { class: "chat-jobs-row__actions row u-gap-8" }); if (runningJob) { const killBtn = el("button", { type: "button", @@ -1163,16 +1160,14 @@ async function openWikiPreviewModal({ sessionId, path }) { const p = String(path || "").replace(/\\/g, "/").replace(/^\//, ""); if (!sid || !p) return; const backdrop = el("div", { - class: "chat-confirm-backdrop", - style: "z-index:9998;", + class: "chat-confirm-backdrop u-z-modal", }); const card = el("div", { - class: "chat-confirm-card", - style: "max-width:980px;width:92vw;max-height:78vh;overflow:auto;", + class: "chat-confirm-card u-profile-card", }); const title = el("div", { class: "card__title", text: t("chat.wikiPreviewTitle", { path: p }) }); const closeBtn = el("button", { type: "button", class: "btn", text: t("chat.wikiPreviewClose") }); - const head = el("div", { class: "row", style: "gap:8px;justify-content:space-between;align-items:center;" }, [ + const head = el("div", { class: "row u-row-between" }, [ title, closeBtn, ]); @@ -1250,30 +1245,25 @@ async function openDispatchLabelsEditor(statusEl) { ? JSON.stringify(cfg.overrides, null, 2) : ""; const backdrop = el("div", { - class: "chat-confirm-backdrop", - style: "z-index:9999;", + class: "chat-confirm-backdrop u-z-modal-top", }); const card = el("div", { - class: "card", - style: "width:min(860px,96vw);max-height:86vh;overflow:auto;padding:12px;", + class: "card u-json-card", }); const title = el("div", { class: "card__title", text: t("chat.dispatchLabelsTitle") }); const tip = el("div", { class: "muted", text: t("chat.dispatchLabelsPrompt") }); - const err = el("div", { class: "muted", style: "color:#dc2626;" }); + const err = el("div", { class: "muted u-text-danger" }); const ta = el("textarea", { - class: "input", - style: "width:100%;min-height:320px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;", + class: "input u-mono-area", }); ta.value = initText; const previewTitle = el("div", { class: "muted", text: t("chat.dispatchLabelsEffectivePreview") }); - const diffOnlyWrap = el("label", { class: "muted", style: "display:flex;gap:6px;align-items:center;cursor:pointer;" }); + const diffOnlyWrap = el("label", { class: "muted u-check-row" }); const diffOnlyCb = el("input", { type: "checkbox" }); diffOnlyWrap.appendChild(diffOnlyCb); diffOnlyWrap.appendChild(el("span", { text: t("chat.dispatchLabelsDiffOnly") })); const preview = el("textarea", { - class: "input", - style: - "width:100%;min-height:220px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;", + class: "input u-mono-area-sm", readonly: "readonly", }); const btnSave = el("button", { type: "button", class: "btn btn--primary", text: t("chat.dispatchLabelsSave") }); @@ -1282,7 +1272,7 @@ async function openDispatchLabelsEditor(statusEl) { const btnExport = el("button", { type: "button", class: "btn", text: t("chat.dispatchLabelsExport") }); const btnImport = el("button", { type: "button", class: "btn", text: t("chat.dispatchLabelsImport") }); const btnCancel = el("button", { type: "button", class: "btn", text: t("chat.dispatchLabelsCancel") }); - const fileInput = el("input", { type: "file", accept: "application/json,.json", style: "display:none" }); + const fileInput = el("input", { class: "u-hidden", type: "file", accept: "application/json,.json" }); const close = () => backdrop.remove(); const saveCurrent = async () => { const raw = String(ta.value || "").trim(); @@ -1395,16 +1385,16 @@ async function openDispatchLabelsEditor(statusEl) { card.appendChild(title); card.appendChild(tip); - card.appendChild(el("div", { style: "height:8px;" })); + card.appendChild(el("div", { class: "u-h-8" })); card.appendChild(ta); - card.appendChild(el("div", { style: "height:8px;" })); + card.appendChild(el("div", { class: "u-h-8" })); card.appendChild(previewTitle); card.appendChild(diffOnlyWrap); card.appendChild(preview); - card.appendChild(el("div", { style: "height:8px;" })); + card.appendChild(el("div", { class: "u-h-8" })); card.appendChild(err); card.appendChild( - el("div", { class: "row", style: "gap:8px;justify-content:flex-end;margin-top:8px;" }, [ + el("div", { class: "row u-row-end-sm" }, [ btnDefaults, btnImport, btnExport, @@ -1583,8 +1573,7 @@ function clearChatPageBlockers() { function attachChatMenuDismiss(menu) { const scrim = el("div", { - class: "chat-menu-scrim", - style: "position:fixed;inset:0;z-index:250;background:transparent;", + class: "chat-menu-scrim u-overlay-scrim", }); const closeAll = () => { dismissChatMenus(); @@ -2707,10 +2696,9 @@ async function renderAttachmentsEl(raw) { if (String(typ || "") === "video_ref" && mime.toLowerCase().startsWith("video/")) { card.appendChild( el("video", { - class: "chat-att-video", + class: "chat-att-video u-media-preview", controls: true, preload: "metadata", - style: "max-width:100%;max-height:420px;margin-top:8px;border-radius:8px;", src: url, }), ); @@ -2721,10 +2709,9 @@ async function renderAttachmentsEl(raw) { if (remote && String(typ || "") === "video_ref" && mime.toLowerCase().startsWith("video/")) { card.appendChild( el("video", { - class: "chat-att-video", + class: "chat-att-video u-media-preview", controls: true, preload: "metadata", - style: "max-width:100%;max-height:420px;margin-top:8px;border-radius:8px;", src: remote, }), ); @@ -3012,11 +2999,10 @@ function syncAuthUserLabel() { ]; items.push(el("div", { class: "chat-sess-menu-sep" })); items.push( - el("div", { class: "muted", style: "padding:6px 10px 2px;font-size:12px;", text: t("theme.label") }), + el("div", { class: "muted u-menu-label", text: t("theme.label") }), ); const themeSelMenu = el("select", { - class: "input", - style: "width:100%;margin:4px 8px 8px;max-width:calc(100% - 16px);", + class: "input u-menu-select", }); try { (window.OclawAdminTheme && window.OclawAdminTheme.THEMES ? window.OclawAdminTheme.THEMES : ["netx"]).forEach((tid) => { @@ -3033,8 +3019,8 @@ function syncAuthUserLabel() { const bridge = window.__chatUserMenuPrefs; if (bridge && typeof bridge === "object") { items.push(el("div", { class: "chat-sess-menu-sep" })); - items.push(el("div", { class: "muted", style: "padding:6px 10px 2px;font-size:12px;", text: t("chat.modeLabel") })); - const modeSel = el("select", { class: "input", style: "width:100%;margin:4px 8px 8px;max-width:calc(100% - 16px);" }); + items.push(el("div", { class: "muted u-menu-label", text: t("chat.modeLabel") })); + const modeSel = el("select", { class: "input u-menu-select" }); try { const rows = Array.isArray(bridge.getModeOptions && bridge.getModeOptions()) ? bridge.getModeOptions() : []; rows.forEach((r) => modeSel.appendChild(el("option", { value: String(r.value || ""), text: String(r.label || r.value || "") }))); @@ -3052,7 +3038,7 @@ function syncAuthUserLabel() { } }); items.push(modeSel); - const reasonWrap = el("label", { class: "switch-wrap", style: "margin:2px 8px 8px;" }, [ + const reasonWrap = el("label", { class: "switch-wrap u-switch-menu" }, [ el("input", { type: "checkbox", class: "switch-input" }), el("span", { class: "switch-slider" }), el("span", { class: "muted", text: t("chat.tools") }), @@ -3087,8 +3073,7 @@ function syncAuthUserLabel() { }), ); const menu = el("div", { - class: "chat-sess-menu-pop", - style: "position:fixed;min-width:220px;z-index:300;", + class: "chat-sess-menu-pop u-pop-menu", }, items); const rect = moreBtn.getBoundingClientRect(); attachChatMenuDismiss(menu); @@ -3162,7 +3147,7 @@ async function renderLogin() { text: t("auth.login"), onclick: doLogin, }); - const card = el("div", { class: "card", style: "max-width:440px;width:100%" }, [ + const card = el("div", { class: "card u-modal-card-sm" }, [ el("div", { class: "card__title", text: t("auth.login") }), el("div", { class: "chat-login-fields" }, [username, password]), el("div", { class: "row chat-login-actions" }, [btn]), @@ -3271,7 +3256,7 @@ async function renderChatUi() { rows: "1", placeholder: t("chat.placeholder"), }); - const fileInput = el("input", { type: "file", multiple: "multiple", style: "display:none", accept: "*/*" }); + const fileInput = el("input", { class: "u-hidden", type: "file", multiple: "multiple", accept: "*/*" }); const attachBtn = el("button", { type: "button", class: "chat-composer-iconbtn", @@ -3301,16 +3286,14 @@ async function renderChatUi() { let specialistCatalog = []; /** Hidden: mode is global-only (⋯ menu). Kept for specialist option list in `publishUserMenuPrefsBridge`. */ const modeSelect = el("select", { - class: "input", - style: "display:none;", + class: "input u-hidden", "aria-hidden": "true", tabIndex: -1, }); let globalMenuModeValue = String(localStorage.getItem(CHAT_USER_MENU_MODE_KEY) || MAIN_MODE_VALUE).toLowerCase(); if (globalMenuModeValue === "comprehensive" || globalMenuModeValue === "main") globalMenuModeValue = MAIN_MODE_VALUE; const modelSelect = el("select", { - class: "input", - style: "min-width:150px;max-width:240px;padding:6px 8px;", + class: "input u-select-mode", title: t("chat.activeModelLabel"), }); let modelSelectNameToId = new Map(); @@ -3322,8 +3305,7 @@ async function renderChatUi() { normalizeExecutionMode(v) === EXECUTION_MODE_PLAN ? t("chat.execModePlan") : t("chat.execModeAgent"); let currentExecutionMode = normalizeExecutionMode(localStorage.getItem(CHAT_EXECUTION_MODE_KEY) || EXECUTION_MODE_AGENT); const execSelect = el("select", { - class: "input", - style: "min-width:96px;max-width:140px;padding:6px 8px;", + class: "input u-select-exec", }); const refreshExecutionSelect = () => { const prev = normalizeExecutionMode(execSelect.value || currentExecutionMode); @@ -3344,7 +3326,7 @@ async function renderChatUi() { }); refreshExecutionSelect(); execSelect.value = currentExecutionMode; - const execSelectWrap = el("span", { class: "chat-exec-mode-wrap", style: "display:inline-flex;align-items:center;" }, [ + const execSelectWrap = el("span", { class: "chat-exec-mode-wrap u-inline-flex" }, [ execSelect, ]); const refreshExecUi = () => { @@ -3584,13 +3566,12 @@ async function renderChatUi() { }; await refreshActiveModelText(); refreshExecUi(); - const composerMetaBar = el("div", { class: "row", style: "gap:8px;padding:2px 4px 6px;align-items:center;" }, [ + const composerMetaBar = el("div", { class: "row u-gap-8 u-composer-meta" }, [ execSelectWrap, modelSelect, el("button", { type: "button", - class: "btn", - style: "padding:4px 8px;min-height:auto;font-size:12px;", + class: "btn u-btn-compact", text: t("chat.compressHistory"), onclick: async () => { const sid = String(activeId || ""); @@ -3668,7 +3649,7 @@ async function renderChatUi() { if (ev.target === backdrop) close(false); }); card.appendChild(text); - card.appendChild(el("div", { class: "row", style: "gap:8px;justify-content:flex-end;margin-top:10px;" }, [btnCancel, btnOk])); + card.appendChild(el("div", { class: "row u-row-end" }, [btnCancel, btnOk])); backdrop.appendChild(card); document.body.appendChild(backdrop); }); @@ -3678,10 +3659,9 @@ async function renderChatUi() { const card = el("div", { class: "chat-confirm-card" }); const text = el("div", { class: "chat-confirm-text", text: String(message || "") }); const input = el("input", { - class: "input", + class: "input u-w-full-mt-10", type: "text", value: String(initialValue || ""), - style: "width:100%;margin-top:10px;", }); const btnCancel = el("button", { type: "button", class: "btn", text: t("chat.dispatchLabelsCancel") }); const btnOk = el("button", { type: "button", class: "btn btn--primary", text: t("chat.dispatchLabelsSave") }); @@ -3705,7 +3685,7 @@ async function renderChatUi() { }); card.appendChild(text); card.appendChild(input); - card.appendChild(el("div", { class: "row", style: "gap:8px;justify-content:flex-end;margin-top:10px;" }, [btnCancel, btnOk])); + card.appendChild(el("div", { class: "row u-row-end" }, [btnCancel, btnOk])); backdrop.appendChild(card); document.body.appendChild(backdrop); setTimeout(() => { @@ -3773,13 +3753,12 @@ async function renderChatUi() { const node = showToast(msg.text, { kind: msg.kind, ttlMs: 6500 }); const acts = Array.isArray(msg.actions) ? msg.actions : []; if (acts.length) { - const btnRow = el("div", { class: "row", style: "gap:8px;flex-wrap:wrap;margin-top:8px;" }); + const btnRow = el("div", { class: "row u-row-wrap-mt-loose" }); for (const a of acts) { btnRow.appendChild( el("button", { type: "button", - class: "btn", - style: "padding:4px 8px;min-height:auto;font-size:12px;", + class: "btn u-btn-compact", text: String(a.label || "View"), onclick: (e) => { e.stopPropagation(); @@ -3972,7 +3951,7 @@ async function renderChatUi() { loadMoreWrap.innerHTML = ""; if (!sessions.length) { sessionsListEl.appendChild( - el("div", { class: "muted", style: "padding:10px 0 10px 8px", text: t("chat.noSessions") }), + el("div", { class: "muted u-empty-pad", text: t("chat.noSessions") }), ); return; } @@ -4025,8 +4004,7 @@ async function renderChatUi() { } if (sessions.length < sessionTotal) { const lm = el("button", { - class: "btn", - style: "width:100%;margin-top:8px", + class: "btn u-w-full-mt-8", text: t("chat.loadMore"), onclick: async () => { try { @@ -5687,7 +5665,7 @@ document.body.addEventListener("click", (e) => { if (!_statusReasonPairs.length) return; document.querySelectorAll(".chat-sess-menu-pop").forEach((n) => n.remove()); const items = _statusReasonPairs.map(([k, n]) => `${reasonLabel(k)}: ${n}`).join("\n"); - const pop = el("div", { class: "chat-sess-menu-pop", style: "position:fixed;max-width:420px;white-space:pre-wrap;line-height:1.4;" }, [ + const pop = el("div", { class: "chat-sess-menu-pop u-pop-tip" }, [ el("div", { class: "chat-sess-menu-item", text: t("chat.dynamicAllReasonsTitle") }), el("div", { class: "chat-sess-menu-item", text: items || "-" }), ]); @@ -5745,7 +5723,7 @@ async function boot() { } catch (err) { mount( el("div", { class: "chat-app--login" }, [ - el("div", { class: "card", style: "max-width:520px;width:100%" }, [ + el("div", { class: "card u-modal-card" }, [ el("div", { class: "card__title", text: t("common.error") }), el("div", { class: "pre", text: String(err) }), ]), @@ -5817,7 +5795,7 @@ document.addEventListener("keydown", (ev) => { boot().catch((err) => { mount( el("div", { class: "chat-app--login" }, [ - el("div", { class: "card", style: "max-width:520px;width:100%" }, [ + el("div", { class: "card u-modal-card" }, [ el("div", { class: "card__title", text: t("common.error") }), el("div", { class: "pre", text: String(err) }), ]), diff --git a/interfaces/admin/static/index.html b/interfaces/admin/static/index.html index 953321de..4061c0f0 100644 --- a/interfaces/admin/static/index.html +++ b/interfaces/admin/static/index.html @@ -95,9 +95,9 @@
Runtime
-