feat(desktop): 复杂主题色与外观调整
浅/深外观、素色模式、8 色色盘、语言列表 UI 与设置表单样式优化。
This commit is contained in:
1 parent
852ab63d43
commit
562b47f3b6
3 files changed
+328
-70
No files matched your search
@@ -24,7 +24,8 @@ const I18N = {
|
||||
'ctx.pin': '置顶', 'ctx.unpin': '取消置顶', 'ctx.del': '删除',
|
||||
'common.close': '关闭', 'common.more': '更多', 'common.optional': '选填',
|
||||
'modal.preset': '预设功能', 'modal.addModel': '添加模型', 'modal.editModel': '编辑模型', 'modal.settings': '配置',
|
||||
'set.theme': '主题色', 'set.lang': '语言', 'set.model': '模型', 'set.addModel': '添加模型',
|
||||
'set.appearance': '外观', 'set.plainUi': '素色', 'set.theme': '颜色', 'set.lang': '语言', 'set.model': '模型', 'set.addModel': '添加模型',
|
||||
'appearance.light': '浅色', 'appearance.dark': '深色',
|
||||
'set.noModels': '暂无模型,点击下方添加',
|
||||
'lang.zh': '简体中文', 'lang.en': 'English',
|
||||
'model.name': '备注', 'model.namePh': '会显示在模型列表',
|
||||
@@ -35,7 +36,8 @@ const I18N = {
|
||||
'model.retries': '重试 (次)', 'model.connTimeout': '连接超时 (s)', 'model.readTimeout': '读取超时 (s)',
|
||||
'model.save': '保存', 'common.cancel': '取消', 'common.edit': '编辑', 'common.delete': '删除',
|
||||
'err.modelSave': '保存失败', 'err.modelRequired': '请填写模型、API Key 和 API 地址',
|
||||
'err.modelDelete': '删除失败', 'confirm.modelDelete': '确定删除该模型配置?',
|
||||
'err.modelDelete': '删除失败', 'err.modelDeleteLast': '至少保留一个模型',
|
||||
'confirm.modelDelete': '确定删除该模型配置?',
|
||||
'page.channels.title': '消息通道', 'page.channels.sub': '把 hub.pyw 管理的各 imbot 接入搬进来:每行一个渠道',
|
||||
'page.status.title': '状态面板', 'page.status.sub': 'hub.pyw 管理的后台进程/服务,集中查看与启停',
|
||||
'page.collab.title': '协作动态', 'page.collab.sub': 'subagent / Hive worker 的实时状态与产出',
|
||||
@@ -83,7 +85,8 @@ const I18N = {
|
||||
'ctx.pin': 'Pin', 'ctx.unpin': 'Unpin', 'ctx.del': 'Delete',
|
||||
'common.close': 'Close', 'common.more': 'More', 'common.optional': 'Optional',
|
||||
'modal.preset': 'Presets', 'modal.addModel': 'Add model', 'modal.editModel': 'Edit model', 'modal.settings': 'Settings',
|
||||
'set.theme': 'Theme color', 'set.lang': 'Language', 'set.model': 'Model', 'set.addModel': 'Add model',
|
||||
'set.appearance': 'Appearance', 'set.plainUi': 'Plain', 'set.theme': 'Color', 'set.lang': 'Language', 'set.model': 'Model', 'set.addModel': 'Add model',
|
||||
'appearance.light': 'Light', 'appearance.dark': 'Dark',
|
||||
'set.noModels': 'No models yet — add one below',
|
||||
'lang.zh': '简体中文', 'lang.en': 'English',
|
||||
'model.name': 'Note', 'model.namePh': 'Shown in the model list',
|
||||
@@ -94,7 +97,8 @@ const I18N = {
|
||||
'model.retries': 'Retries (×)', 'model.connTimeout': 'Connect (s)', 'model.readTimeout': 'Read (s)',
|
||||
'model.save': 'Save', 'common.cancel': 'Cancel', 'common.edit': 'Edit', 'common.delete': 'Delete',
|
||||
'err.modelSave': 'Save failed', 'err.modelRequired': 'Model, API Key and base URL are required',
|
||||
'err.modelDelete': 'Delete failed', 'confirm.modelDelete': 'Delete this model profile?',
|
||||
'err.modelDelete': 'Delete failed', 'err.modelDeleteLast': 'At least one model is required',
|
||||
'confirm.modelDelete': 'Delete this model profile?',
|
||||
'page.channels.title': 'Channels', 'page.channels.sub': 'imbot channels managed by hub.pyw — one row per channel',
|
||||
'page.status.title': 'Status', 'page.status.sub': 'Background processes/services managed by hub.pyw',
|
||||
'page.collab.title': 'Collaboration', 'page.collab.sub': 'Live state & output of subagents / Hive workers',
|
||||
@@ -125,9 +129,28 @@ const I18N = {
|
||||
'presetPrompt.mine': 'Collect this week\'s git commits and write a weekly report.',
|
||||
},
|
||||
};
|
||||
let lang = (localStorage.getItem('ga_lang') === 'en') ? 'en' : 'zh';
|
||||
const LANGS = ['zh', 'en'];
|
||||
let lang = LANGS.includes(localStorage.getItem('ga_lang')) ? localStorage.getItem('ga_lang') : 'zh';
|
||||
let theme = localStorage.getItem('ga_theme') || '1';
|
||||
const STORE = { lang: 'ga_lang', theme: 'ga_theme', llmNo: 'ga_llm_no' };
|
||||
const STORE = { lang: 'ga_lang', theme: 'ga_theme', appearance: 'ga_appearance', plain: 'ga_plain', llmNo: 'ga_llm_no' };
|
||||
const APPEARANCE_IDS = ['light', 'dark'];
|
||||
const LEGACY_STYLE = { classic: ['light', true], tinted: ['light', false], dark: ['dark', false] };
|
||||
|
||||
function migrateAppearance() {
|
||||
let app = localStorage.getItem(STORE.appearance);
|
||||
let plain = localStorage.getItem(STORE.plain) === '1';
|
||||
const legacy = localStorage.getItem('ga_style');
|
||||
if (!app && legacy && LEGACY_STYLE[legacy]) {
|
||||
[app, plain] = LEGACY_STYLE[legacy];
|
||||
localStorage.setItem(STORE.appearance, app);
|
||||
if (plain) localStorage.setItem(STORE.plain, '1');
|
||||
else localStorage.removeItem(STORE.plain);
|
||||
localStorage.removeItem('ga_style');
|
||||
}
|
||||
if (!APPEARANCE_IDS.includes(app)) app = 'light';
|
||||
return { appearance: app, plain: plain && app === 'light' };
|
||||
}
|
||||
let { appearance, plain: plainUi } = migrateAppearance();
|
||||
const bridgeHost = () => `${location.protocol}//${location.hostname}:14168`;
|
||||
async function bridgeFetch(path, opts = {}) {
|
||||
const headers = { ...(opts.headers || {}) };
|
||||
@@ -156,26 +179,67 @@ function applyI18n() {
|
||||
el.setAttribute('placeholder', el.hasAttribute('data-optional-ph') ? optionalPh(phKey) : t(phKey));
|
||||
});
|
||||
document.querySelectorAll('[data-i18n-title]').forEach(el => { el.setAttribute('title', t(el.dataset.i18nTitle)); });
|
||||
populateLangSelect();
|
||||
renderLangList();
|
||||
}
|
||||
function populateLangSelect() {
|
||||
if (!langSel) return;
|
||||
langSel.innerHTML = '';
|
||||
['zh', 'en'].forEach(code => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = code; opt.textContent = t('lang.' + code);
|
||||
langSel.appendChild(opt);
|
||||
function renderLangList() {
|
||||
const box = document.getElementById('lang-list');
|
||||
if (!box) return;
|
||||
box.innerHTML = '';
|
||||
LANGS.forEach(code => {
|
||||
const row = document.createElement('label');
|
||||
row.className = 'model-row' + (lang === code ? ' sel' : '');
|
||||
row.innerHTML = `<input type="radio" name="lang-pick"${lang === code ? ' checked' : ''}><span>${escapeHtml(t('lang.' + code))}</span>`;
|
||||
row.addEventListener('click', (e) => { e.preventDefault(); selectLang(code); });
|
||||
box.appendChild(row);
|
||||
});
|
||||
langSel.value = lang;
|
||||
}
|
||||
function selectLang(code) {
|
||||
if (!LANGS.includes(code) || lang === code) return;
|
||||
lang = code;
|
||||
localStorage.setItem(STORE.lang, lang);
|
||||
applyI18n();
|
||||
renderSessionList();
|
||||
refreshStatusLabel();
|
||||
updateModelChip();
|
||||
renderSettingsModels();
|
||||
}
|
||||
function applyTheme(id) {
|
||||
theme = String(id || '1');
|
||||
const n = parseInt(id, 10);
|
||||
theme = (n >= 1 && n <= 8) ? String(n) : '1';
|
||||
document.documentElement.dataset.theme = theme;
|
||||
localStorage.setItem(STORE.theme, theme);
|
||||
document.querySelectorAll('#theme-swatches .swatch').forEach(el => {
|
||||
el.classList.toggle('sel', el.dataset.theme === theme);
|
||||
});
|
||||
}
|
||||
function syncPlainSwitch() {
|
||||
const row = document.getElementById('plain-ui-row');
|
||||
const sw = document.getElementById('plain-ui-switch');
|
||||
if (!row || !sw) return;
|
||||
const show = appearance === 'light';
|
||||
row.hidden = !show;
|
||||
sw.setAttribute('aria-checked', plainUi ? 'true' : 'false');
|
||||
}
|
||||
function applyAppearance(nextApp, nextPlain) {
|
||||
appearance = APPEARANCE_IDS.includes(nextApp) ? nextApp : 'light';
|
||||
if (appearance === 'light') {
|
||||
plainUi = !!nextPlain;
|
||||
if (plainUi) localStorage.setItem(STORE.plain, '1');
|
||||
else localStorage.removeItem(STORE.plain);
|
||||
} else {
|
||||
plainUi = false;
|
||||
}
|
||||
localStorage.setItem(STORE.appearance, appearance);
|
||||
document.documentElement.dataset.appearance = appearance;
|
||||
if (plainUi) document.documentElement.dataset.plain = '1';
|
||||
else delete document.documentElement.dataset.plain;
|
||||
document.querySelectorAll('#appearance-seg .appear-card').forEach(el => {
|
||||
const on = el.dataset.appearance === appearance;
|
||||
el.classList.toggle('sel', on);
|
||||
el.setAttribute('aria-checked', on ? 'true' : 'false');
|
||||
});
|
||||
syncPlainSwitch();
|
||||
}
|
||||
|
||||
/* ═══════════════ 侧边栏导航 ═══════════════ */
|
||||
const nav = document.getElementById('nav');
|
||||
@@ -324,7 +388,6 @@ bindResize(rpResize, rpPanel, -1, 160, 400); // 右栏:cursor 左移 → 增宽
|
||||
bindResize(sbResize, sbPanel, +1, 180, 360); // 左栏:cursor 右移 → 增宽
|
||||
const modelChip = document.getElementById('model-chip');
|
||||
const modelNameEl= modelChip ? modelChip.querySelector('.model-name') : null;
|
||||
const langSel = document.getElementById('lang-select');
|
||||
|
||||
let msgsEl = null;
|
||||
function ensureMsgs() {
|
||||
@@ -735,7 +798,8 @@ async function deleteModel(id, name) {
|
||||
}
|
||||
renderSettingsModels();
|
||||
} catch (ex) {
|
||||
alert(ex.message || t('err.modelDelete'));
|
||||
const msg = ex.message || '';
|
||||
alert(msg.includes('last profile') ? t('err.modelDeleteLast') : (msg || t('err.modelDelete')));
|
||||
}
|
||||
}
|
||||
function renderSettingsModels() {
|
||||
@@ -767,8 +831,9 @@ function renderSettingsModels() {
|
||||
function openSettings() {
|
||||
openModal('settings-modal');
|
||||
renderSettingsModels();
|
||||
populateLangSelect();
|
||||
renderLangList();
|
||||
applyTheme(theme);
|
||||
applyAppearance(appearance, plainUi);
|
||||
}
|
||||
async function loadModelProfiles() {
|
||||
try {
|
||||
@@ -801,14 +866,16 @@ if (themeSwatches) themeSwatches.addEventListener('click', (e) => {
|
||||
const sw = e.target.closest('.swatch[data-theme]');
|
||||
if (sw) applyTheme(sw.dataset.theme);
|
||||
});
|
||||
if (langSel) langSel.addEventListener('change', () => {
|
||||
lang = (langSel.value === 'en') ? 'en' : 'zh';
|
||||
localStorage.setItem(STORE.lang, lang);
|
||||
applyI18n();
|
||||
renderSessionList();
|
||||
refreshStatusLabel();
|
||||
updateModelChip();
|
||||
renderSettingsModels();
|
||||
const appearanceSeg = document.getElementById('appearance-seg');
|
||||
if (appearanceSeg) appearanceSeg.addEventListener('click', (e) => {
|
||||
const btn = e.target.closest('.appear-card[data-appearance]');
|
||||
if (!btn) return;
|
||||
const isLight = btn.dataset.appearance === 'light';
|
||||
applyAppearance(btn.dataset.appearance, isLight && localStorage.getItem(STORE.plain) === '1');
|
||||
});
|
||||
const plainUiSwitch = document.getElementById('plain-ui-switch');
|
||||
if (plainUiSwitch) plainUiSwitch.addEventListener('click', () => {
|
||||
if (appearance === 'light') applyAppearance('light', !plainUi);
|
||||
});
|
||||
async function loadBridgeConfig() {
|
||||
try {
|
||||
@@ -1005,6 +1072,7 @@ if(tokResetBtn)tokResetBtn.addEventListener('click',()=>{if(tokSince)tokSince.va
|
||||
nav.addEventListener('click',(e)=>{const item=e.target.closest('.nav-item');if(item&&item.dataset.page==='token')loadTokenPage();});
|
||||
|
||||
/* ═══════════════ 启动 ═══════════════ */
|
||||
applyAppearance(appearance, plainUi);
|
||||
applyTheme(theme);
|
||||
applyI18n();
|
||||
updateModelChip();
|
||||
|
||||
@@ -6,6 +6,22 @@
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<!-- DEV 脚手架(标注模式):删本行 + 下方 annot.js 一行即可彻底移除 -->
|
||||
<link rel="stylesheet" href="annot.css">
|
||||
<script>
|
||||
(function(){
|
||||
var t=localStorage.getItem('ga_theme'); if(t) document.documentElement.dataset.theme=t;
|
||||
var legacy=localStorage.getItem('ga_style');
|
||||
var app=localStorage.getItem('ga_appearance');
|
||||
if(!app&&legacy){
|
||||
if(legacy==='dark') app='dark';
|
||||
else{ app='light'; if(legacy==='classic') localStorage.setItem('ga_plain','1'); }
|
||||
localStorage.setItem('ga_appearance',app);
|
||||
localStorage.removeItem('ga_style');
|
||||
}
|
||||
app=app||'light';
|
||||
document.documentElement.dataset.appearance=app;
|
||||
if(app==='light'&&localStorage.getItem('ga_plain')==='1') document.documentElement.dataset.plain='1';
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -337,19 +353,41 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body set-body">
|
||||
<div>
|
||||
<div class="set-sec-t" data-i18n="set.theme"></div>
|
||||
<div class="swatches" id="theme-swatches">
|
||||
<span class="swatch sel" data-theme="1" role="button" tabindex="0"></span>
|
||||
<span class="swatch" data-theme="2" role="button" tabindex="0"></span>
|
||||
<span class="swatch" data-theme="3" role="button" tabindex="0"></span>
|
||||
<span class="swatch" data-theme="4" role="button" tabindex="0"></span>
|
||||
<span class="swatch" data-theme="5" role="button" tabindex="0"></span>
|
||||
<div class="set-block">
|
||||
<div class="set-sec-t" data-i18n="set.appearance"></div>
|
||||
<div class="appear-cards" id="appearance-seg" role="radiogroup">
|
||||
<button class="appear-card sel" type="button" data-appearance="light" role="radio" aria-checked="true">
|
||||
<span class="appear-preview appear-preview-light" aria-hidden="true"></span>
|
||||
<span class="appear-name" data-i18n="appearance.light"></span>
|
||||
</button>
|
||||
<button class="appear-card" type="button" data-appearance="dark" role="radio" aria-checked="false">
|
||||
<span class="appear-preview appear-preview-dark" aria-hidden="true"></span>
|
||||
<span class="appear-name" data-i18n="appearance.dark"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="set-toggle-row" id="plain-ui-row" hidden>
|
||||
<span class="set-toggle-label" data-i18n="set.plainUi"></span>
|
||||
<button class="set-switch" type="button" id="plain-ui-switch" role="switch" aria-checked="false">
|
||||
<span class="set-switch-knob" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="set-block">
|
||||
<div class="set-sec-t" data-i18n="set.theme"></div>
|
||||
<div class="color-grid" id="theme-swatches">
|
||||
<span class="swatch sel" data-theme="1" role="button" tabindex="0" aria-label="1"></span>
|
||||
<span class="swatch" data-theme="2" role="button" tabindex="0" aria-label="2"></span>
|
||||
<span class="swatch" data-theme="3" role="button" tabindex="0" aria-label="3"></span>
|
||||
<span class="swatch" data-theme="4" role="button" tabindex="0" aria-label="4"></span>
|
||||
<span class="swatch" data-theme="5" role="button" tabindex="0" aria-label="5"></span>
|
||||
<span class="swatch" data-theme="6" role="button" tabindex="0" aria-label="6"></span>
|
||||
<span class="swatch" data-theme="7" role="button" tabindex="0" aria-label="7"></span>
|
||||
<span class="swatch" data-theme="8" role="button" tabindex="0" aria-label="8"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="set-block">
|
||||
<div class="set-sec-t" data-i18n="set.lang"></div>
|
||||
<select class="set-select" id="lang-select"></select>
|
||||
<div class="model-list lang-list" id="lang-list"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="set-sec-t" data-i18n="set.model"></div>
|
||||
|
||||
@@ -5,12 +5,15 @@
|
||||
|
||||
:root{
|
||||
/* —— 基础面 —— */
|
||||
--bg:#f4f5f7;
|
||||
--panel:#ffffff; /* 三大卡片底 */
|
||||
--card:#ffffff; /* 卡片内的小块:pill/列表行/输入/菜单/按钮 */
|
||||
--field-bg:#fafbfc; /* 表单输入底 */
|
||||
--line:#e9eaee;
|
||||
--line-soft:#f0f1f4; /* 浅分隔 / hover 底 */
|
||||
--bg-base:#f4f5f7;
|
||||
--bg:var(--bg-base);
|
||||
--panel:#ffffff;
|
||||
--card:#ffffff;
|
||||
--field-bg:#fafbfc;
|
||||
--line-base:#e9eaee;
|
||||
--line:var(--line-base);
|
||||
--line-soft-base:#f0f1f4;
|
||||
--line-soft:var(--line-soft-base);
|
||||
--line-strong:#d4d8e0; /* hover 描边 */
|
||||
--hover-2:#eceef2; /* 次级 hover 底 */
|
||||
--hover-dim:#0000000f;
|
||||
@@ -35,12 +38,15 @@
|
||||
--brand1:#ffb35c;
|
||||
--brand2:#ff7a59;
|
||||
|
||||
/* —— 配置弹窗:可选主题色板(骨架占位)—— */
|
||||
/* —— 配置弹窗:主题色板(mac 8 色)—— */
|
||||
--swatch-1:#1f6f53;
|
||||
--swatch-2:#7c5cff;
|
||||
--swatch-3:#2f9e54;
|
||||
--swatch-3:#007aff;
|
||||
--swatch-4:#ff7a59;
|
||||
--swatch-5:#1f2430;
|
||||
--swatch-5:#ff2d55;
|
||||
--swatch-6:#ff3b30;
|
||||
--swatch-7:#ffcc00;
|
||||
--swatch-8:#8e8e93;
|
||||
|
||||
/* —— 状态:成功 / 危险 / 关闭 —— */
|
||||
--ok:#39a85a;
|
||||
@@ -66,12 +72,78 @@
|
||||
font-family:-apple-system,"Segoe UI","Microsoft YaHei",system-ui,sans-serif;
|
||||
}
|
||||
|
||||
/* —— 主题色板(切主题只改 accent 组)—— */
|
||||
html[data-theme="1"]{ --blue:var(--swatch-1); --blue-bg:#e7f2ed; --blue-soft:#f1f8f4; }
|
||||
html[data-theme="2"]{ --blue:var(--swatch-2); --blue-bg:#f0ecff; --blue-soft:#f7f5ff; }
|
||||
html[data-theme="3"]{ --blue:var(--swatch-3); --blue-bg:#e9f7ee; --blue-soft:#f1fbf4; }
|
||||
html[data-theme="4"]{ --blue:var(--swatch-4); --blue-bg:#fff0eb; --blue-soft:#fff6f2; }
|
||||
html[data-theme="5"]{ --blue:var(--swatch-5); --blue-bg:#eceef2; --blue-soft:#f4f5f7; }
|
||||
/* —— 主题色板(素色:仅 accent 浅底)—— */
|
||||
html[data-theme="1"]{ --blue:var(--swatch-1); --blue-bg:color-mix(in srgb, var(--blue) 14%, var(--panel)); --blue-soft:color-mix(in srgb, var(--blue) 7%, var(--panel)); }
|
||||
html[data-theme="2"]{ --blue:var(--swatch-2); --blue-bg:color-mix(in srgb, var(--blue) 14%, var(--panel)); --blue-soft:color-mix(in srgb, var(--blue) 7%, var(--panel)); }
|
||||
html[data-theme="3"]{ --blue:var(--swatch-3); --blue-bg:color-mix(in srgb, var(--blue) 14%, var(--panel)); --blue-soft:color-mix(in srgb, var(--blue) 7%, var(--panel)); }
|
||||
html[data-theme="4"]{ --blue:var(--swatch-4); --blue-bg:color-mix(in srgb, var(--blue) 14%, var(--panel)); --blue-soft:color-mix(in srgb, var(--blue) 7%, var(--panel)); }
|
||||
html[data-theme="5"]{ --blue:var(--swatch-5); --blue-bg:color-mix(in srgb, var(--blue) 14%, var(--panel)); --blue-soft:color-mix(in srgb, var(--blue) 7%, var(--panel)); }
|
||||
html[data-theme="6"]{ --blue:var(--swatch-6); --blue-bg:color-mix(in srgb, var(--blue) 14%, var(--panel)); --blue-soft:color-mix(in srgb, var(--blue) 7%, var(--panel)); }
|
||||
html[data-theme="7"]{ --blue:var(--swatch-7); --blue-bg:color-mix(in srgb, var(--blue) 14%, var(--panel)); --blue-soft:color-mix(in srgb, var(--blue) 7%, var(--panel)); --on-accent:#1f2430; }
|
||||
html[data-theme="8"]{ --blue:var(--swatch-8); --blue-bg:color-mix(in srgb, var(--blue) 14%, var(--panel)); --blue-soft:color-mix(in srgb, var(--blue) 7%, var(--panel)); --on-accent:#ffffff; }
|
||||
|
||||
/* logo 渐变随主题色 */
|
||||
html[data-theme]{
|
||||
--brand1:color-mix(in srgb, var(--blue) 40%, #ffc978);
|
||||
--brand2:var(--blue);
|
||||
}
|
||||
|
||||
/* —— 外观:浅色(accent 轻 tint 页面/边框)—— */
|
||||
html[data-appearance="light"]:not([data-plain="1"])[data-theme]{
|
||||
--blue-bg:color-mix(in srgb, var(--blue) 20%, var(--panel));
|
||||
--blue-soft:color-mix(in srgb, var(--blue) 11%, var(--panel));
|
||||
--bg:color-mix(in srgb, var(--blue) 7%, var(--bg-base));
|
||||
--line:color-mix(in srgb, var(--blue) 16%, var(--line-base));
|
||||
--line-soft:color-mix(in srgb, var(--blue) 10%, var(--line-soft-base));
|
||||
}
|
||||
|
||||
/* —— 外观:深色 —— */
|
||||
html[data-appearance="dark"]{
|
||||
--swatch-7:#ffd60a;
|
||||
--swatch-8:#e6edf3;
|
||||
--bg-base:#0d1117;
|
||||
--bg:#0d1117;
|
||||
--panel:#161b22;
|
||||
--card:#161b22;
|
||||
--field-bg:#0d1117;
|
||||
--line-base:#30363d;
|
||||
--line:#30363d;
|
||||
--line-soft-base:#21262d;
|
||||
--line-soft:#21262d;
|
||||
--line-strong:#484f58;
|
||||
--hover-2:#21262d;
|
||||
--hover-dim:#ffffff0f;
|
||||
--txt:#e6edf3;
|
||||
--txt-strong:#ffffff;
|
||||
--txt-2:#c9d1d9;
|
||||
--txt-3:#afb8c1;
|
||||
--muted:#7d8590;
|
||||
--muted2:#656d76;
|
||||
--placeholder:#484f58;
|
||||
--icon:#7d8590;
|
||||
--off-bg:#21262d;
|
||||
--switch-off:#484f58;
|
||||
--dot-idle:#484f58;
|
||||
--ok:#3fb950;
|
||||
--ok-strong:#46c35a;
|
||||
--ok-bg:#12261e;
|
||||
--ok-ring:#3fb95033;
|
||||
--danger:#f85149;
|
||||
--danger-dot:#f85149;
|
||||
--danger-text:#ff7b72;
|
||||
--danger-bg:#2d1515;
|
||||
--danger-ring:#f8514938;
|
||||
--overlay:#010409a6;
|
||||
--shadow:0 1px 2px #01040966, 0 8px 24px #01040966;
|
||||
--shadow-pop:0 8px 24px #01040999;
|
||||
--shadow-modal:0 18px 50px #010409cc;
|
||||
}
|
||||
html[data-appearance="dark"][data-theme]{
|
||||
--blue-bg:color-mix(in srgb, var(--blue) 22%, var(--panel));
|
||||
--blue-soft:color-mix(in srgb, var(--blue) 12%, var(--panel));
|
||||
}
|
||||
html[data-appearance="dark"][data-theme="7"],
|
||||
html[data-appearance="dark"][data-theme="8"]{ --on-accent:#0d1117; }
|
||||
|
||||
html,body{ height:100%; }
|
||||
body{ background:var(--bg); color:var(--txt); font-size:13px; overflow:hidden; }
|
||||
@@ -116,27 +188,103 @@ body{ background:var(--bg); color:var(--txt); font-size:13px; overflow:hidden; }
|
||||
.modal-body{ flex:1; min-height:340px; overflow:auto; }
|
||||
|
||||
/* 配置弹窗内的骨架控件(未实现逻辑)*/
|
||||
.set-body{ min-height:0; padding:18px; display:flex; flex-direction:column; gap:20px; }
|
||||
.set-body{ min-height:0; padding:18px; display:flex; flex-direction:column; gap:22px; }
|
||||
.set-block{ display:flex; flex-direction:column; gap:0; }
|
||||
.preset-pop{ min-height:0; padding:18px; display:flex; flex-direction:column; gap:14px; }
|
||||
.set-sec-t{ font-size:12px; font-weight:700; color:var(--txt-2); margin-bottom:10px; }
|
||||
.swatches{ display:flex; gap:10px; flex-wrap:wrap; }
|
||||
|
||||
/* 外观:mac 式预览卡片 */
|
||||
.appear-cards{ display:grid; grid-template-columns:1fr 1fr; gap:10px; }
|
||||
.appear-card{
|
||||
display:flex; flex-direction:column; align-items:center; gap:8px;
|
||||
padding:10px 8px 9px; border:2px solid var(--line); border-radius:10px;
|
||||
background:var(--field-bg); cursor:pointer; font-family:inherit; transition:.15s;
|
||||
}
|
||||
.appear-card:hover{ border-color:var(--line-strong); background:var(--line-soft); }
|
||||
.appear-card.sel{
|
||||
border-color:var(--blue); background:var(--blue-bg);
|
||||
box-shadow:0 0 0 1px color-mix(in srgb, var(--blue) 25%, transparent);
|
||||
}
|
||||
.appear-preview{
|
||||
width:100%; height:44px; border-radius:7px; border:1px solid var(--line);
|
||||
position:relative; overflow:hidden;
|
||||
}
|
||||
.appear-preview-light{
|
||||
background:linear-gradient(180deg, #f4f5f7 0%, #f4f5f7 42%, #ffffff 42%, #ffffff 100%);
|
||||
}
|
||||
.appear-preview-light::after{
|
||||
content:''; position:absolute; left:10%; top:52%; width:55%; height:7px;
|
||||
border-radius:4px; background:#e9eaee;
|
||||
}
|
||||
.appear-preview-dark{
|
||||
background:linear-gradient(180deg, #0d1117 0%, #0d1117 42%, #161b22 42%, #161b22 100%);
|
||||
border-color:#30363d;
|
||||
}
|
||||
.appear-preview-dark::after{
|
||||
content:''; position:absolute; left:10%; top:52%; width:55%; height:7px;
|
||||
border-radius:4px; background:#30363d;
|
||||
}
|
||||
.appear-name{ font-size:12px; font-weight:600; color:var(--txt-2); }
|
||||
.appear-card.sel .appear-name{ color:var(--blue); }
|
||||
|
||||
/* 素色:mac/iOS 设置行 + Toggle Switch */
|
||||
.set-toggle-row{
|
||||
display:flex; align-items:center; justify-content:space-between; gap:12px;
|
||||
margin-top:10px; padding:10px 12px;
|
||||
border:1px solid var(--line); border-radius:10px; background:var(--field-bg);
|
||||
}
|
||||
.set-toggle-row[hidden]{ display:none; }
|
||||
.set-toggle-label{ font-size:13px; font-weight:600; color:var(--txt-2); }
|
||||
.set-switch{
|
||||
position:relative; flex:0 0 auto; width:44px; height:26px;
|
||||
border:none; border-radius:999px; background:var(--switch-off);
|
||||
cursor:pointer; transition:background .2s; padding:0;
|
||||
}
|
||||
.set-switch[aria-checked="true"]{ background:var(--blue); }
|
||||
.set-switch-knob{
|
||||
position:absolute; top:2px; left:2px; width:22px; height:22px;
|
||||
border-radius:50%; background:var(--card);
|
||||
box-shadow:0 1px 3px var(--hover-dim); transition:transform .2s;
|
||||
}
|
||||
.set-switch[aria-checked="true"] .set-switch-knob{ transform:translateX(18px); }
|
||||
.set-switch:focus-visible{ outline:2px solid var(--blue); outline-offset:2px; }
|
||||
|
||||
.lang-list{ margin-bottom:0; }
|
||||
|
||||
/* 颜色:mac 式 4×2 色盘 */
|
||||
.color-grid{
|
||||
display:grid; grid-template-columns:repeat(4, 1fr); gap:12px 10px;
|
||||
justify-items:center; max-width:220px;
|
||||
}
|
||||
.swatch{
|
||||
width:26px; height:26px; border-radius:50%; cursor:pointer;
|
||||
border:2px solid var(--card); box-shadow:0 0 0 1px var(--line);
|
||||
position:relative; width:32px; height:32px; border-radius:50%; cursor:pointer;
|
||||
background:var(--swatch-color, var(--swatch-1));
|
||||
border:2px solid color-mix(in srgb, var(--panel) 70%, transparent);
|
||||
box-shadow:0 1px 2px var(--hover-dim);
|
||||
transition:transform .12s, box-shadow .12s;
|
||||
}
|
||||
.swatch:hover{ transform:scale(1.08); }
|
||||
.swatch:focus-visible{ outline:2px solid var(--blue); outline-offset:2px; }
|
||||
.swatch.sel{
|
||||
box-shadow:0 0 0 2px var(--panel), 0 0 0 4px var(--blue);
|
||||
transform:scale(1.04);
|
||||
}
|
||||
.swatch.sel::after{
|
||||
content:''; position:absolute; inset:0; border-radius:50%;
|
||||
background:center/14px no-repeat url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
|
||||
}
|
||||
html[data-theme="7"] .swatch[data-theme="7"].sel::after,
|
||||
html[data-appearance="dark"] .swatch[data-theme="8"].sel::after{
|
||||
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231f2430' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
|
||||
}
|
||||
.swatch.sel{ box-shadow:0 0 0 2px var(--blue); }
|
||||
.swatch[data-theme="1"]{ --swatch-color:var(--swatch-1); }
|
||||
.swatch[data-theme="2"]{ --swatch-color:var(--swatch-2); }
|
||||
.swatch[data-theme="3"]{ --swatch-color:var(--swatch-3); }
|
||||
.swatch[data-theme="4"]{ --swatch-color:var(--swatch-4); }
|
||||
.swatch[data-theme="5"]{ --swatch-color:var(--swatch-5); }
|
||||
.set-select{
|
||||
width:100%; border:1px solid var(--line); border-radius:8px; padding:8px 10px;
|
||||
font-size:13px; font-family:inherit; background:var(--field-bg); color:var(--txt-2);
|
||||
cursor:pointer;
|
||||
}
|
||||
.set-select:focus{ outline:none; border-color:var(--blue); }
|
||||
.swatch[data-theme="6"]{ --swatch-color:var(--swatch-6); }
|
||||
.swatch[data-theme="7"]{ --swatch-color:var(--swatch-7); }
|
||||
.swatch[data-theme="8"]{ --swatch-color:var(--swatch-8); }
|
||||
.model-list{ display:flex; flex-direction:column; gap:4px; margin-bottom:10px; max-height:160px; overflow:auto; }
|
||||
.model-row{
|
||||
display:flex; align-items:center; gap:10px; padding:8px 10px;
|
||||
@@ -147,15 +295,19 @@ body{ background:var(--bg); color:var(--txt); font-size:13px; overflow:hidden; }
|
||||
.model-row.sel{ background:var(--blue-bg); border-color:var(--blue); color:var(--blue); font-weight:600; }
|
||||
.model-row input{ accent-color:var(--blue); flex:0 0 auto; pointer-events:none; }
|
||||
.model-row-name{ flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||
.model-row-actions{ display:flex; gap:2px; flex:0 0 auto; opacity:0; transition:opacity .12s; }
|
||||
.model-row:hover .model-row-actions, .model-row.sel .model-row-actions{ opacity:1; }
|
||||
.model-act{
|
||||
width:26px; height:26px; border:none; background:transparent; cursor:pointer;
|
||||
color:var(--muted); border-radius:6px; display:flex; align-items:center; justify-content:center; padding:0;
|
||||
.model-row-actions{
|
||||
display:flex; gap:2px; flex:0 0 auto; opacity:0; transition:opacity .12s;
|
||||
}
|
||||
.model-act:hover{ background:var(--line-soft); color:var(--txt-2); }
|
||||
.model-row:hover .model-row-actions,
|
||||
.model-row.sel .model-row-actions{ opacity:1; }
|
||||
.model-act{
|
||||
width:28px; height:28px; border:none; background:transparent; cursor:pointer;
|
||||
color:var(--muted); border-radius:6px; display:flex; align-items:center; justify-content:center;
|
||||
padding:0; flex:0 0 auto;
|
||||
}
|
||||
.model-act:hover{ background:var(--hover-dim); color:var(--txt-2); }
|
||||
.model-act-del:hover{ color:var(--danger-text); }
|
||||
.model-act svg{ width:13px; height:13px; pointer-events:none; }
|
||||
.model-act svg{ width:14px; height:14px; pointer-events:none; }
|
||||
.set-empty{ font-size:12px; color:var(--muted); padding:6px 0; }
|
||||
.set-btn{
|
||||
width:100%; display:flex; align-items:center; gap:8px;
|
||||
|
||||
Reference in new issue
Block a user