let state = { settings: { activeProfileId: null, activePresetId: null }, profiles: [], presets: [], }; let editingProfileId = null; let activeSection = "api"; let lastImportedPresetId = null; const expandedPresetEntries = new Map(); const profilesListEl = document.getElementById("profiles-list"); const presetsListEl = document.getElementById("presets-list"); const importReportEl = document.getElementById("import-report"); const profileDialog = document.getElementById("profile-dialog"); const profileForm = document.getElementById("profile-form"); const profileDialogTitle = document.getElementById("profile-dialog-title"); const presetFileEl = document.getElementById("preset-file"); const panelTitleEl = document.getElementById("panel-title"); const panelSubtitleEl = document.getElementById("panel-subtitle"); const panelActionsEl = document.getElementById("panel-actions"); const sectionApiEl = document.getElementById("section-api"); const sectionPresetEl = document.getElementById("section-preset"); const activeSettingsBarEl = document.getElementById("active-settings-bar"); const settingsToastEl = document.getElementById("settings-toast"); async function api(path, options = {}) { const res = await fetch(path, { headers: { "Content-Type": "application/json" }, ...options, }); const data = await res.json(); if (!res.ok) throw new Error(data.error || "请求失败"); return data; } function escapeHtml(text) { return String(text) .replace(/&/g, "&") .replace(//g, ">"); } function maskKey(key) { if (!key) return "(未设置)"; if (key.length <= 8) return "****"; return `${key.slice(0, 4)}…${key.slice(-4)}`; } let toastTimer = null; function showToast(message, isError = false) { settingsToastEl.hidden = false; settingsToastEl.textContent = message; settingsToastEl.classList.toggle("error", isError); clearTimeout(toastTimer); toastTimer = setTimeout(() => { settingsToastEl.hidden = true; }, 4000); } function renderActiveBar() { const profile = state.profiles.find( (p) => p.id === state.settings.activeProfileId, ); const preset = state.presets.find( (p) => p.id === state.settings.activePresetId, ); activeSettingsBarEl.innerHTML = ` 当前生效 API: ${profile ? escapeHtml(`${profile.name} · ${profile.model}`) : "未选用"} 预设: ${preset ? escapeHtml(preset.name) : "未选用"} `; } function switchSection(section) { activeSection = section; document.querySelectorAll(".sidebar-nav-item").forEach((el) => { el.classList.toggle("active", el.dataset.section === section); }); sectionApiEl.classList.toggle("hidden", section !== "api"); sectionPresetEl.classList.toggle("hidden", section !== "preset"); renderPanelHeader(); } function renderPanelHeader() { panelActionsEl.innerHTML = ""; if (activeSection === "api") { panelTitleEl.textContent = "API 配置"; panelSubtitleEl.textContent = "保存后点击「选用」或「保存并选用」立即生效"; const btn = document.createElement("button"); btn.type = "button"; btn.className = "btn-primary"; btn.textContent = "新增配置"; btn.addEventListener("click", () => openProfileDialog()); panelActionsEl.appendChild(btn); } else { panelTitleEl.textContent = "预设 Preset"; panelSubtitleEl.textContent = "导入后点击「选用此预设」注入所有 LLM 请求"; const btn = document.createElement("button"); btn.type = "button"; btn.className = "btn-primary"; btn.textContent = "导入 JSON"; btn.addEventListener("click", () => presetFileEl.click()); panelActionsEl.appendChild(btn); } } function activateButtonHtml(active, id, action) { if (active) { return ``; } return ``; } function renderProfiles() { profilesListEl.innerHTML = ""; if (!state.profiles.length) { profilesListEl.innerHTML = '

暂无配置,点击右上角「新增配置」。

'; return; } for (const p of state.profiles) { const active = p.id === state.settings.activeProfileId; const card = document.createElement("div"); card.className = `config-card${active ? " active" : ""}`; card.innerHTML = `
${escapeHtml(p.name.charAt(0).toUpperCase())}
${escapeHtml(p.name)} ${active ? '当前' : ""}
${escapeHtml(p.model)} · ${escapeHtml(p.baseUrl)}
Key: ${escapeHtml(maskKey(p.apiKey))}
${activateButtonHtml(active, p.id, "activate-profile")}
`; profilesListEl.appendChild(card); } } function renderPresets() { presetsListEl.innerHTML = ""; if (!state.presets.length) { presetsListEl.innerHTML = '

暂无预设,点击右上角「导入 JSON」。

'; return; } for (const p of state.presets) { const active = p.id === state.settings.activePresetId; const expanded = expandedPresetEntries.get(p.id); const card = document.createElement("div"); card.className = `config-card${active ? " active" : ""}`; card.innerHTML = `
P
${escapeHtml(p.name)} ${active ? '当前' : ""}
${escapeHtml(p.source)} · 启用 ${p.enabledCount} 条 · 注入 ${p.injectingCount ?? "?"} 条 · ${escapeHtml(p.importedAt?.slice(0, 10) ?? "")}
${activateButtonHtml(active, p.id, "activate-preset")}
`; presetsListEl.appendChild(card); if (expanded) { renderPresetEntriesPanel(p.id, expanded); } } } function renderPresetEntriesPanel(presetId, data) { const panel = document.getElementById(`preset-entries-${presetId}`); if (!panel || !data) return; const gen = data.generation ?? {}; const genLines = Object.entries(gen) .filter(([, v]) => v !== undefined && v !== null) .map(([k, v]) => `${k}: ${v}`); const entriesHtml = data.entries .map((entry, idx) => { const roleClass = `role-${entry.role}`; const status = entry.willInject ? "" : entry.marker ? 'marker · 无内容' : '空内容 · 不注入'; return `
${idx + 1} ${escapeHtml(entry.role)} ${escapeHtml(entry.name)} ${status}
${entry.content ? `
${escapeHtml(entry.content)}
` : `

(无文本内容)

`}
`; }) .join(""); panel.innerHTML = `
${genLines.length ? `
生成参数 ${escapeHtml(genLines.join(" · "))}
` : ""}

共 ${data.entries.length} 条启用顺序,${data.injectingCount} 条会注入请求

${entriesHtml || '

无启用条目

'}
`; } async function togglePresetEntries(presetId) { if (expandedPresetEntries.has(presetId)) { expandedPresetEntries.delete(presetId); renderPresets(); return; } const data = await api(`/api/presets/${encodeURIComponent(presetId)}/entries`); expandedPresetEntries.set(presetId, data); renderPresets(); } async function loadAll() { const data = await api("/api/settings"); state.settings = data.settings; state.profiles = data.profiles; state.presets = data.presets; renderActiveBar(); renderProfiles(); renderPresets(); } function openProfileDialog(profile = null) { editingProfileId = profile?.id ?? null; profileDialogTitle.textContent = profile ? "编辑 API 配置" : "新增 API 配置"; profileForm.name.value = profile?.name ?? ""; profileForm.baseUrl.value = profile?.baseUrl ?? "https://api.deepseek.com"; profileForm.apiKey.value = profile?.apiKey ?? ""; profileForm.model.value = profile?.model ?? "deepseek-v4-pro"; profileDialog.showModal(); } async function saveProfile(activate) { const payload = { name: profileForm.name.value, baseUrl: profileForm.baseUrl.value, model: profileForm.model.value, activate, }; const apiKey = profileForm.apiKey.value.trim(); if (apiKey || !editingProfileId) { payload.apiKey = apiKey; } let result; if (editingProfileId) { result = await api(`/api/profiles/${editingProfileId}`, { method: "PUT", body: JSON.stringify(payload), }); if (activate) { result = await api(`/api/profiles/${editingProfileId}/activate`, { method: "POST", }); } } else { result = await api("/api/profiles", { method: "POST", body: JSON.stringify(payload), }); } profileDialog.close(); await loadAll(); if (activate) { const n = result.reloadedSessions ?? 0; showToast(`已选用并生效${n > 0 ? `,已更新 ${n} 个活跃会话` : ""}`); } else { showToast("已保存。点击「选用并生效」后才会用于 LLM 请求。"); } } async function activateProfile(id) { const result = await api(`/api/profiles/${id}/activate`, { method: "POST" }); await loadAll(); const n = result.reloadedSessions ?? 0; showToast(`API 配置已生效${n > 0 ? `(${n} 个会话已更新)` : ""}`); } async function activatePreset(id) { const result = await api(`/api/presets/${id}/activate`, { method: "POST" }); await loadAll(); const n = result.reloadedSessions ?? 0; showToast(`预设已生效${n > 0 ? `(${n} 个会话已更新)` : ""}`); } document.querySelectorAll(".sidebar-nav-item").forEach((btn) => { btn.addEventListener("click", () => switchSection(btn.dataset.section)); }); document.getElementById("profile-cancel").addEventListener("click", () => { profileDialog.close(); }); document.getElementById("profile-save").addEventListener("click", () => { saveProfile(false).catch((err) => showToast(err.message, true)); }); profileForm.addEventListener("submit", async (e) => { e.preventDefault(); try { await saveProfile(true); } catch (err) { showToast(err.message, true); } }); profilesListEl.addEventListener("click", async (e) => { const btn = e.target.closest("[data-action]"); if (!btn) return; const id = btn.dataset.id; const action = btn.dataset.action; try { if (action === "activate-profile") { await activateProfile(id); } else if (action === "edit-profile") { openProfileDialog(state.profiles.find((p) => p.id === id)); } else if (action === "test-profile") { const el = document.getElementById(`test-${id}`); el.textContent = "测试中…"; el.className = "test-result"; const result = await api(`/api/profiles/${id}/test`, { method: "POST" }); el.textContent = result.message; el.className = `test-result ${result.ok ? "ok" : "fail"}`; } else if (action === "delete-profile") { if (!confirm("确定删除此 API 配置?")) return; await api(`/api/profiles/${id}`, { method: "DELETE" }); await loadAll(); showToast("已删除"); } } catch (err) { showToast(err.message, true); } }); presetsListEl.addEventListener("click", async (e) => { const btn = e.target.closest("[data-action]"); if (!btn) return; const id = btn.dataset.id; const action = btn.dataset.action; try { if (action === "activate-preset") { await activatePreset(id); } else if (action === "toggle-preset-entries") { await togglePresetEntries(id); } else if (action === "delete-preset") { if (!confirm("确定删除此预设?")) return; await api(`/api/presets/${id}`, { method: "DELETE" }); await loadAll(); showToast("已删除"); } } catch (err) { showToast(err.message, true); } }); presetFileEl.addEventListener("change", async () => { const file = presetFileEl.files?.[0]; if (!file) return; try { const text = await file.text(); const raw = JSON.parse(text); const name = file.name.replace(/\.json$/i, ""); const report = await api("/api/presets/import", { method: "POST", body: JSON.stringify({ raw, name }), }); lastImportedPresetId = report.preset.id; importReportEl.hidden = false; importReportEl.innerHTML = `
${[ `已导入: ${report.preset.name}`, `条目: ${report.promptCount},启用: ${report.enabledCount}`, report.warnings.length ? `警告: ${report.warnings.join("; ")}` : "", ] .filter(Boolean) .join("
")}
`; document .getElementById("activate-imported-preset") ?.addEventListener("click", async () => { try { await activatePreset(lastImportedPresetId); } catch (err) { showToast(err.message, true); } }); await loadAll(); showToast("预设已导入,请点击「选用此预设并生效」"); } catch (err) { showToast(err.message, true); } finally { presetFileEl.value = ""; } }); renderPanelHeader(); loadAll().catch((err) => { showToast(`加载设置失败: ${err.message}`, true); });