Add theme options and timed agent dividers

This commit is contained in:
shiyue
2026-06-14 12:16:37 +08:00
parent 382c5accb7
commit 3a4006b7d3
6 changed files with 883 additions and 19 deletions

View File

@@ -2,10 +2,11 @@
(function () {
'use strict';
const ASSET_VERSION = '20260613-codexapp-tools2';
const ASSET_VERSION = '20260614-divider-time-selectfix';
const WS_URL = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}/ws`;
const RENDER_DEBOUNCE = 100;
const COMPOSER_SUGGESTION_DEBOUNCE = 120;
const DIVIDER_TIME_STORAGE_KEY = 'cc-web-show-divider-time';
const SLASH_COMMANDS = [
{ cmd: '/clear', desc: '清除当前会话' },
@@ -73,6 +74,42 @@
desc: '更明亮的留白和更克制的棕色强调,像编辑台一样安静。',
swatches: ['#f6f1e8', '#efe8dc', '#8b5e3c', '#2f4b45'],
},
{
value: 'sage',
label: 'Sage Console',
desc: '清透鼠尾草绿与石墨文字,适合长时间工作流。',
swatches: ['#f5f8f2', '#e6efdf', '#2f6f64', '#557ba3'],
},
{
value: 'ink',
label: 'Ink Focus',
desc: '浅灰纸面配靛蓝重点,信息密度更高也更冷静。',
swatches: ['#f6f7fb', '#e8edf5', '#3f5fb5', '#3f8f73'],
},
{
value: 'dawn',
label: 'Dawn Studio',
desc: '晨光米白配珊瑚红,保留温度但比暖纸更轻。',
swatches: ['#fff7f2', '#f3e8e1', '#b5524d', '#4f8a6b'],
},
{
value: 'carbon',
label: 'Carbon Mint',
desc: '石墨黑底配薄荷绿重点,夜间使用更稳。',
swatches: ['#0f1314', '#202829', '#67d8b2', '#9fb7ff'],
},
{
value: 'nocturne',
label: 'Nocturne Teal',
desc: '深海青黑配电光蓝,适合高专注会话。',
swatches: ['#081417', '#142b31', '#5ecdf5', '#f0c36a'],
},
{
value: 'cinder',
label: 'Cinder Rose',
desc: '炭黑底配低饱和玫瑰色,暗色里保留一点温度。',
swatches: ['#151112', '#2a2022', '#e68193', '#67c587'],
},
];
// --- State ---
@@ -98,6 +135,7 @@
let currentModel = 'opus';
let currentAgent = AGENT_LABELS[localStorage.getItem('cc-web-agent')] ? localStorage.getItem('cc-web-agent') : DEFAULT_AGENT;
let currentTheme = (document.documentElement.dataset.theme || localStorage.getItem('cc-web-theme') || 'washi');
let showAgentDividerTime = localStorage.getItem(DIVIDER_TIME_STORAGE_KEY) !== '0';
let codexConfigCache = null;
let loadedHistorySessionId = null;
let activeSessionLoad = null;
@@ -123,6 +161,7 @@
let noteDraftSeq = 0;
const pendingNotesByTarget = new Map();
const userMessageIndex = new Map();
document.documentElement.dataset.dividerTime = showAgentDividerTime ? 'show' : 'hide';
// --- DOM ---
const $ = (sel) => document.querySelector(sel);
@@ -447,6 +486,26 @@
refreshThemeSummaries();
}
function getDividerTimeSummary() {
return showAgentDividerTime ? '显示时间' : '不显示时间';
}
function refreshDividerTimeControls(root = document) {
root.querySelectorAll('[data-divider-time-summary]').forEach((node) => {
node.textContent = getDividerTimeSummary();
});
root.querySelectorAll('[data-divider-time-toggle]').forEach((node) => {
node.checked = showAgentDividerTime;
});
}
function applyDividerTimePreference(visible) {
showAgentDividerTime = !!visible;
document.documentElement.dataset.dividerTime = showAgentDividerTime ? 'show' : 'hide';
localStorage.setItem(DIVIDER_TIME_STORAGE_KEY, showAgentDividerTime ? '1' : '0');
refreshDividerTimeControls();
}
function buildThemePickerHtml(options = {}) {
const { showSectionTitle = true } = options;
return `
@@ -476,7 +535,7 @@
});
}
function buildThemeEntryHtml() {
function buildAppearanceSettingsHtml() {
return `
<div class="settings-section-title">外观</div>
<button class="settings-nav-card" type="button" data-open-theme-page>
@@ -486,9 +545,34 @@
</span>
<span class="settings-nav-card-arrow" aria-hidden="true"></span>
</button>
<label class="settings-toggle-row">
<span class="settings-toggle-copy">
<span class="settings-toggle-title">分隔线时间</span>
<span class="settings-toggle-meta">当前:<span data-divider-time-summary>${escapeHtml(getDividerTimeSummary())}</span></span>
</span>
<span class="settings-switch">
<input type="checkbox" data-divider-time-toggle ${showAgentDividerTime ? 'checked' : ''}>
<span class="settings-switch-track" aria-hidden="true">
<span class="settings-switch-thumb"></span>
</span>
</span>
</label>
`;
}
function mountAppearanceSettings(panel) {
const themePageBtn = panel.querySelector('[data-open-theme-page]');
if (themePageBtn) themePageBtn.addEventListener('click', openThemeSubpage);
const dividerTimeToggle = panel.querySelector('[data-divider-time-toggle]');
if (dividerTimeToggle) {
dividerTimeToggle.checked = showAgentDividerTime;
dividerTimeToggle.addEventListener('change', () => {
applyDividerTimePreference(dividerTimeToggle.checked);
});
}
refreshDividerTimeControls(panel);
}
function buildNotifyEntryHtml(config) {
const provider = config?.provider || 'off';
const providerLabel = PROVIDER_OPTIONS.find(o => o.value === provider)?.label || '关闭';
@@ -4319,7 +4403,7 @@
${title}
<div style="font-size:0.9em;color:var(--text-primary);margin-bottom:20px;line-height:1.7;word-break:break-word;white-space:pre-line">${escapeHtml(options.message || '')}</div>
<div style="display:flex;flex-direction:column;gap:8px">
<button id="simple-confirm-ok" style="width:100%;padding:10px;border:none;border-radius:10px;background:var(--accent);color:#fff;font-size:0.95em;font-weight:600;cursor:pointer;font-family:inherit">${escapeHtml(confirmText)}</button>
<button id="simple-confirm-ok" style="width:100%;padding:10px;border:none;border-radius:10px;background:var(--accent);color:var(--accent-ink, #fff);font-size:0.95em;font-weight:600;cursor:pointer;font-family:inherit">${escapeHtml(confirmText)}</button>
<button id="simple-confirm-cancel" style="width:100%;padding:9px;border:none;border-radius:10px;background:transparent;color:var(--text-muted);font-size:0.85em;cursor:pointer;font-family:inherit">${escapeHtml(cancelText)}</button>
</div>
`;
@@ -4355,7 +4439,7 @@
box.innerHTML = `
<div style="font-size:0.9em;color:var(--text-primary);margin-bottom:20px;line-height:1.7">${escapeHtml(getDeleteConfirmMessage(agent))}</div>
<div style="display:flex;flex-direction:column;gap:8px">
<button id="del-confirm-ok" style="width:100%;padding:10px;border:none;border-radius:10px;background:var(--accent);color:#fff;font-size:0.95em;font-weight:600;cursor:pointer;font-family:inherit">确认删除</button>
<button id="del-confirm-ok" style="width:100%;padding:10px;border:none;border-radius:10px;background:var(--accent);color:var(--accent-ink, #fff);font-size:0.95em;font-weight:600;cursor:pointer;font-family:inherit">确认删除</button>
<button id="del-confirm-skip" style="width:100%;padding:9px;border:1px solid var(--border-color);border-radius:10px;background:var(--bg-tertiary);color:var(--text-secondary);font-size:0.85em;cursor:pointer;font-family:inherit">确认且不再提示</button>
<button id="del-confirm-cancel" style="width:100%;padding:9px;border:none;border-radius:10px;background:transparent;color:var(--text-muted);font-size:0.85em;cursor:pointer;font-family:inherit">取消</button>
</div>
@@ -4588,7 +4672,7 @@
if (!currentSessionId || chatTitle.contentEditable === 'true') return;
const originalText = chatTitle.textContent;
chatTitle.contentEditable = 'true';
chatTitle.style.background = '#fff';
chatTitle.style.background = 'var(--surface-strong)';
chatTitle.style.outline = '1px solid var(--accent)';
chatTitle.style.borderRadius = '6px';
chatTitle.style.padding = '2px 8px';
@@ -5647,7 +5731,7 @@
<div class="settings-divider"></div>
${buildThemeEntryHtml()}
${buildAppearanceSettingsHtml()}
<div class="settings-divider"></div>
@@ -5665,8 +5749,7 @@
overlay.appendChild(panel);
document.body.appendChild(overlay);
const themePageBtn = panel.querySelector('[data-open-theme-page]');
if (themePageBtn) themePageBtn.addEventListener('click', openThemeSubpage);
mountAppearanceSettings(panel);
const notifyPageBtn = panel.querySelector('[data-open-notify-page]');
if (notifyPageBtn) notifyPageBtn.addEventListener('click', openNotifySubpage);
@@ -5934,7 +6017,7 @@
<div class="settings-divider"></div>
${buildThemeEntryHtml()}
${buildAppearanceSettingsHtml()}
<div class="settings-divider"></div>
@@ -5952,8 +6035,7 @@
overlay.appendChild(panel);
document.body.appendChild(overlay);
const themePageBtn = panel.querySelector('[data-open-theme-page]');
if (themePageBtn) themePageBtn.addEventListener('click', openThemeSubpage);
mountAppearanceSettings(panel);
const notifyPageBtn2 = panel.querySelector('[data-open-notify-page]');
if (notifyPageBtn2) notifyPageBtn2.addEventListener('click', openNotifySubpage);