Adjust some styles.

This commit is contained in:
fanlusky committed 2026-08-29 21:48:17 +08:00
1 parent 10aaa42626
commit ba1922e33f
15 files changed
+300 -81

No files matched your search

+1 -1
View File
@@ -17,4 +17,4 @@ skins/
每个皮肤目录包含 `horizontal_dark.css`、`horizontal_light.css`、`vertical_dark.css` 和 `vertical_light.css`。Server 根据基础皮肤、候选框布局和明暗模式组合资源路径,并以内联 `<style>` 注入共享 HTML;外部皮肤的 CSS 随后加载,用于覆盖基础皮肤。
新增或修改内置皮肤时,不要复制 HTML。只需建立独立皮肤目录并提供四份布局/主题 CSS。外部皮肤继续使用 `skin.json + skin.css + assets/`,不提供 HTML 或 JavaScript。
新增或修改内置皮肤时,不要复制 HTML。只需建立独立皮肤目录并提供四份布局/主题 CSS。外部皮肤继续使用 `skin.json + cand.css + assets/`,不提供 HTML 或 JavaScript。
@@ -29,7 +29,7 @@
margin-left: 0px;
background: #202020;
/* background: url("https://appassets/background.jpg"); */
padding: 2px;
padding: 2px 1px;
border-radius: 6px;
box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.5);
min-width: 7em;
@@ -70,6 +70,7 @@
.cand {
border-radius: 4px;
padding: 2px 4px 2px 0;
}
.hover-active .cand:hover {
@@ -89,10 +90,11 @@
}
.first-row-wrapper {
margin-left: 2px;
margin-left: 0;
}
.first {
position: relative;
background-color: #3e3e3eb9;
border-radius: 4px;
}
@@ -122,9 +124,9 @@
}
.cand .text {
padding-left: calc(0.1em + 1px);
padding-right: 2.5px;
line-height: 1.2;
padding-left: calc(0.2em + 3px);
padding-right: 4px;
line-height: 1.25;
}
.num {
@@ -29,7 +29,7 @@
margin-left: 0px;
background: #ffffff;
/* background: url("https://appassets/background.jpg"); */
padding: 2px;
padding: 2px 1px;
border-radius: 6px;
box-shadow: 4px 4px 12px rgba(0, 0, 0, 0.12);
min-width: 7em;
@@ -70,6 +70,7 @@
.cand {
border-radius: 4px;
padding: 2px 4px 2px 0;
}
.hover-active .cand:hover {
@@ -89,10 +90,11 @@
}
.first-row-wrapper {
margin-left: 2px;
margin-left: 0;
}
.first {
position: relative;
background-color: #e8e8e8;
border-radius: 4px;
}
@@ -122,9 +124,9 @@
}
.cand .text {
padding-left: calc(0.1em + 1px);
padding-right: 2.5px;
line-height: 1.2;
padding-left: calc(0.2em + 3px);
padding-right: 4px;
line-height: 1.25;
}
.num {
@@ -68,6 +68,8 @@
.cand {
border-radius: 4px;
padding-top: 1px;
padding-bottom: 1px;
}
.hover-active .cand:hover {
@@ -68,6 +68,8 @@
.cand {
border-radius: 4px;
padding-top: 1px;
padding-bottom: 1px;
}
.hover-active .cand:hover {
@@ -181,7 +181,8 @@
}
.cand { border-radius: 4px; }
.cursor { background: #07c160; }
.hover-active .cand:hover { background-color: rgba(7, 193, 96, .32); }
.hover-active .cand:not(.first):hover { background-color: rgba(7, 193, 96, .32); }
.hover-active .cand.first:hover { background-color: #07c160; }
.first {
background-color: #07c160;
border-radius: 5px;
@@ -179,7 +179,8 @@
}
.cand { border-radius: 4px; }
.cursor { background: #07c160; }
.hover-active .cand:hover { background-color: rgba(7, 193, 96, .14); }
.hover-active .cand:not(.first):hover { background-color: rgba(7, 193, 96, .14); }
.hover-active .cand.first:hover { background-color: #07c160; }
.first { background-color: #07c160; border-radius: 5px; }
.first::before { display: none; }
.first .text,
@@ -187,9 +187,14 @@
border-radius: 5px;
box-shadow: 0 6px 18px rgba(0, 0, 0, .42);
}
.cand { border-radius: 4px; }
.cand {
border-radius: 4px;
padding-top: 1px;
padding-bottom: 1px;
}
.cursor { background: #07c160; }
.hover-active .cand:hover { background-color: rgba(7, 193, 96, .32); }
.hover-active .cand:not(.first):hover { background-color: rgba(7, 193, 96, .32); }
.hover-active .cand.first:hover { background-color: #07c160; }
.first {
background-color: #07c160;
border-radius: 5px;
@@ -185,9 +185,14 @@
border-radius: 5px;
box-shadow: 0 5px 15px rgba(0, 0, 0, .14);
}
.cand { border-radius: 4px; }
.cand {
border-radius: 4px;
padding-top: 1px;
padding-bottom: 1px;
}
.cursor { background: #07c160; }
.hover-active .cand:hover { background-color: rgba(7, 193, 96, .14); }
.hover-active .cand:not(.first):hover { background-color: rgba(7, 193, 96, .14); }
.hover-active .cand.first:hover { background-color: #07c160; }
.first { background-color: #07c160; border-radius: 5px; }
.first::before { display: none; }
.first .text,
@@ -6,15 +6,17 @@ export type CandidateSkin = string;
type ExternalSkin = {
id: string; name: string; version: string; author?: string; description?: string;
base: string; preview?: string; layouts: string[]; themes: string[]; compatible: boolean;
base: string; stylesheet?: string; toolbarStylesheet?: string; preview?: string; layouts: string[]; themes: string[];
minWidthDip?: number; decorationTopDip?: number; decorationWidthDip?: number; compatible: boolean;
};
type SkinScanIssue = { folder: string; reason: string };
const BUILTIN_SKINS = ['fluent', 'wechat', 'graphite', 'willow_green'] as const;
type BuiltinSkin = typeof BUILTIN_SKINS[number];
const previewOverrides: Record<BuiltinSkin, SkinPreviewTheme | null> = {
const previewOverrides: Record<string, SkinPreviewTheme | null> = {
fluent: null, wechat: null, graphite: null, willow_green: null
};
const loadedExternalStyleIds = new Set<string>();
let activeTheme: SkinPreviewTheme = 'dark';
let activeSkin: CandidateSkin = 'fluent';
let externalSkins: ExternalSkin[] = [];
@@ -22,6 +24,8 @@ let scanIssues: SkinScanIssue[] = [];
let skinDirectory = '';
let catalogScanned = false;
let catalogRevision = 0;
let previewHorizontalHtml = '';
let previewVerticalHtml = '';
const SKIN_PREVIEW_PAGE_SIZE = 6;
function normalizeCandidateSkin(value: unknown): CandidateSkin {
@@ -32,12 +36,33 @@ function asBuiltinSkin(value: unknown): BuiltinSkin {
return BUILTIN_SKINS.includes(value as BuiltinSkin) ? value as BuiltinSkin : 'fluent';
}
function findExternalSkin(id: string): ExternalSkin | undefined {
return externalSkins.find((skin) => skin.id === id);
}
function builtinPreviewClass(skinId: string): string {
if (skinId === 'wechat') return 'skin-wechat';
if (skinId === 'graphite') return 'skin-graphite';
if (skinId === 'willow_green') return 'skin-willow-green';
const external = findExternalSkin(skinId);
return external ? builtinPreviewClass(external.base) : '';
}
function limitCandidatePreview(host: HTMLElement): void {
host.querySelectorAll<HTMLElement>('.row-wrapper').forEach((wrapper, index) => {
wrapper.style.display = index < SKIN_PREVIEW_PAGE_SIZE ? '' : 'none';
});
}
function wrapCandidateHtml(html: string): string {
return `<div class="containerParent">${html}</div>`;
}
function fillPreviewHost(host: HTMLElement, html: string): void {
host.innerHTML = wrapCandidateHtml(html);
limitCandidatePreview(host);
}
function fillToolbar(host: HTMLElement): void {
const source = new DOMParser().parseFromString(ftbHTML, 'text/html');
const statusBar = source.querySelector<HTMLElement>('.status-bar');
@@ -47,7 +72,69 @@ function fillToolbar(host: HTMLElement): void {
host.replaceChildren(statusBar);
}
function applySkinPreviewTheme(skin: BuiltinSkin, theme: SkinPreviewTheme): void {
function resourceUrl(id: string, relativePath: string): string {
return `https://candidate-skins/${encodeURIComponent(id)}/${relativePath.split('/').map(encodeURIComponent).join('/')}?v=${catalogRevision}`;
}
function rewriteSkinCssUrls(css: string, skinId: string): string {
return css.replace(/url\(\s*(['"]?)([^'")]+)\1\s*\)/gi, (full, _quote: string, url: string) => {
const trimmed = url.trim();
if (!trimmed || /^(data:|https?:|\/\/|\/)/i.test(trimmed)) return full;
const relative = trimmed.replace(/^\.\//, '');
if (relative.includes('..')) return full;
return `url("${resourceUrl(skinId, relative)}")`;
});
}
function applyDecorationVars(host: HTMLElement, skin: ExternalSkin): void {
host.style.setProperty('--msime-skin-min-width', `${skin.minWidthDip || 0}px`);
host.style.setProperty('--msime-skin-decoration-top', `${skin.decorationTopDip || 0}px`);
host.style.setProperty('--msime-skin-decoration-width', `${skin.decorationWidthDip || 0}px`);
}
function clearDecorationVars(host: HTMLElement): void {
host.style.removeProperty('--msime-skin-min-width');
host.style.removeProperty('--msime-skin-decoration-top');
host.style.removeProperty('--msime-skin-decoration-width');
}
async function injectScopedSkinCss(skin: ExternalSkin, stylesheet: string | undefined, styleId: string): Promise<void> {
if (!stylesheet || loadedExternalStyleIds.has(styleId)) return;
try {
const response = await fetch(resourceUrl(skin.id, stylesheet));
if (!response.ok) return;
const css = rewriteSkinCssUrls(await response.text(), skin.id).replace(/:root\b/g, ':scope');
const style = document.createElement('style');
style.id = styleId;
style.dataset.externalSkinStyle = skin.id;
style.textContent = `@scope ([data-external-skin-preview="${skin.id}"]) {\n${css}\n}`;
document.head.appendChild(style);
loadedExternalStyleIds.add(styleId);
} catch {
// Preview stays on the inherited built-in base if the stylesheet cannot load.
}
}
async function ensureExternalSkinStyle(skin: ExternalSkin): Promise<void> {
await Promise.all([
injectScopedSkinCss(skin, skin.stylesheet, `external-skin-style-${skin.id}`),
injectScopedSkinCss(skin, skin.toolbarStylesheet, `external-toolbar-style-${skin.id}`)
]);
}
function resetExternalSkinStyles(): void {
document.querySelectorAll('style[data-external-skin-style]').forEach((node) => node.remove());
loadedExternalStyleIds.clear();
}
function resolvedPreviewTheme(skinId: string, supported?: string[]): SkinPreviewTheme {
const override = previewOverrides[skinId];
if (override) return override;
if (!supported || supported.length === 0 || supported.includes(activeTheme)) return activeTheme;
return supported[0] === 'light' ? 'light' : 'dark';
}
function applyBuiltinCardTheme(skin: BuiltinSkin, theme: SkinPreviewTheme): void {
const card = document.querySelector<HTMLElement>(`[data-candidate-skin="${skin}"]`);
if (!card) return;
card.querySelectorAll<HTMLElement>('[data-skin-horizontal], [data-skin-vertical], [data-skin-toolbar]')
@@ -66,8 +153,18 @@ function applySkinPreviewTheme(skin: BuiltinSkin, theme: SkinPreviewTheme): void
});
}
function resolvedPreviewTheme(skin: BuiltinSkin): SkinPreviewTheme {
return previewOverrides[skin] ?? activeTheme;
function applyExternalCardTheme(skin: ExternalSkin): void {
const card = document.querySelector<HTMLElement>(`[data-candidate-skin="${skin.id}"]`);
if (!card) return;
const theme = resolvedPreviewTheme(skin.id, skin.themes);
card.querySelectorAll<HTMLElement>('[data-skin-horizontal], [data-skin-vertical], [data-skin-toolbar]')
.forEach((element) => {
element.classList.toggle('theme-light', theme === 'light');
element.classList.toggle('theme-dark', theme === 'dark');
});
card.querySelectorAll<HTMLButtonElement>('[data-skin-preview-switch]').forEach((button) => {
button.textContent = theme === 'light' ? '预览深色' : '预览浅色';
});
}
function syncSkinSwitches(): void {
@@ -78,20 +175,46 @@ function syncSkinSwitches(): void {
});
}
function ensureContainerParent(root: HTMLElement): void {
const box = root.querySelector<HTMLElement>(':scope > .container, .container');
if (!box || box.parentElement?.classList.contains('containerParent')) return;
if (box.parentElement !== root && !root.contains(box)) return;
const parent = document.createElement('div');
parent.className = 'containerParent';
box.replaceWith(parent);
parent.append(box);
}
function syncAppearancePreviews(): void {
const useWechat = activeSkin === 'wechat';
const useGraphite = activeSkin === 'graphite';
const useWillowGreen = activeSkin === 'willow_green';
const previewClass = builtinPreviewClass(activeSkin);
const external = findExternalSkin(activeSkin);
document.querySelectorAll<HTMLElement>('.cand-preview .candidate').forEach((element) => {
element.classList.toggle('skin-wechat', useWechat);
element.classList.toggle('skin-graphite', useGraphite);
element.classList.toggle('skin-willow-green', useWillowGreen);
element.classList.toggle('skin-wechat', previewClass === 'skin-wechat');
element.classList.toggle('skin-graphite', previewClass === 'skin-graphite');
element.classList.toggle('skin-willow-green', previewClass === 'skin-willow-green');
ensureContainerParent(element);
if (external) {
element.dataset.externalSkinPreview = external.id;
applyDecorationVars(element, external);
} else {
delete element.dataset.externalSkinPreview;
clearDecorationVars(element);
}
});
document.querySelectorAll<HTMLElement>('.ftb-preview-host:not([data-skin-toolbar])').forEach((element) => {
element.classList.toggle('skin-wechat', useWechat);
element.classList.toggle('skin-graphite', useGraphite);
element.classList.toggle('skin-willow-green', useWillowGreen);
element.classList.toggle('skin-wechat', previewClass === 'skin-wechat');
element.classList.toggle('skin-graphite', previewClass === 'skin-graphite');
element.classList.toggle('skin-willow-green', previewClass === 'skin-willow-green');
if (external) {
element.dataset.externalSkinPreview = external.id;
} else {
delete element.dataset.externalSkinPreview;
}
});
document.querySelectorAll<HTMLElement>('.cand-preview').forEach((element) => {
element.classList.toggle('has-skin-decoration', !!(external && (external.decorationTopDip || 0) > 0));
});
if (external) void ensureExternalSkinStyle(external);
}
function selectSkin(value: unknown, persist: boolean): void {
@@ -105,8 +228,17 @@ function selectSkin(value: unknown, persist: boolean): void {
}
}
function resourceUrl(id: string, relativePath: string): string {
return `https://candidate-skins/${encodeURIComponent(id)}/${relativePath.split('/').map(encodeURIComponent).join('/')}?v=${catalogRevision}`;
function bindSkinSwitch(element: HTMLElement, skinId: string, enabled: boolean): void {
element.dataset.skinSwitch = skinId;
element.setAttribute('role', 'switch');
element.tabIndex = enabled ? 0 : -1;
element.setAttribute('aria-disabled', String(!enabled));
if (!enabled) return;
const activate = () => selectSkin(skinId, true);
element.addEventListener('click', activate);
element.addEventListener('keydown', (event) => {
if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); activate(); }
});
}
function renderExternalSkins(): void {
@@ -119,20 +251,15 @@ function renderExternalSkins(): void {
directory.textContent = skinDirectory || '%LOCALAPPDATA%\\metasequoiaime\\skins';
list.replaceChildren(...externalSkins.map((skin) => {
const card = document.createElement('div');
card.className = `section external-skin-card${skin.compatible ? '' : ' is-incompatible'}`;
const previewClass = builtinPreviewClass(skin.base);
card.className = `section skin-theme-card external-skin-live-card${skin.compatible ? '' : ' is-incompatible'}${(skin.decorationTopDip || 0) > 0 ? ' has-skin-decoration' : ''}`;
card.dataset.candidateSkin = skin.id;
const preview = document.createElement('div');
preview.className = 'external-skin-preview';
if (skin.preview) {
const image = document.createElement('img');
image.src = resourceUrl(skin.id, skin.preview);
image.alt = '';
image.loading = 'lazy';
preview.append(image);
} else preview.textContent = '无预览图';
card.dataset.externalSkinPreview = skin.id;
applyDecorationVars(card, skin);
const info = document.createElement('div');
info.className = 'external-skin-info';
const header = document.createElement('div');
header.className = 'skin-theme-header';
const titles = document.createElement('div');
const title = document.createElement('div');
title.className = 'section-title';
title.textContent = skin.name;
@@ -143,26 +270,66 @@ function renderExternalSkins(): void {
description.className = 'skin-theme-description';
description.textContent = skin.compatible ? (skin.description || `基于 ${skin.base}`)
: `当前布局或明暗模式不受支持(${skin.layouts.join('/')},${skin.themes.join('/')})`;
info.append(title, meta, description);
titles.append(title, meta, description);
const actions = document.createElement('div');
actions.className = 'skin-theme-actions';
const toggle = document.createElement('div');
toggle.className = 'ftb-toggle-btn skin-theme-toggle';
toggle.dataset.skinSwitch = skin.id;
toggle.setAttribute('role', 'switch');
toggle.setAttribute('aria-checked', 'false');
toggle.tabIndex = skin.compatible ? 0 : -1;
toggle.setAttribute('aria-disabled', String(!skin.compatible));
const knob = document.createElement('div');
knob.className = 'ftb-toggle-knob';
toggle.append(knob);
if (skin.compatible) {
const activate = () => selectSkin(skin.id, true);
toggle.addEventListener('click', activate);
toggle.addEventListener('keydown', (event) => {
if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); activate(); }
});
}
card.append(preview, info, toggle);
bindSkinSwitch(toggle, skin.id, skin.compatible);
actions.append(toggle);
const switcher = document.createElement('button');
switcher.type = 'button';
switcher.className = 'skin-preview-switch';
switcher.dataset.skinPreviewSwitch = '';
switcher.title = '切换明暗预览';
switcher.textContent = '预览浅色';
switcher.addEventListener('click', () => {
previewOverrides[skin.id] = resolvedPreviewTheme(skin.id) === 'light' ? 'dark' : 'light';
applyExternalCardTheme(skin);
});
actions.append(switcher);
header.append(titles, actions);
const previews = document.createElement('div');
previews.className = 'skin-preview-list';
const horizontal = document.createElement('div');
horizontal.className = 'skin-preview-row skin-preview-horizontal';
const horizontalStage = document.createElement('div');
horizontalStage.className = 'skin-preview-stage';
const horizontalHost = document.createElement('div');
horizontalHost.className = `candidate wnd-h${previewClass ? ` ${previewClass}` : ''}`;
horizontalHost.dataset.skinHorizontal = '';
if (previewHorizontalHtml) fillPreviewHost(horizontalHost, previewHorizontalHtml);
horizontalStage.append(horizontalHost);
horizontal.append(horizontalStage);
const vertical = document.createElement('div');
vertical.className = 'skin-preview-row skin-preview-vertical';
const verticalStage = document.createElement('div');
verticalStage.className = 'skin-preview-stage';
const verticalHost = document.createElement('div');
verticalHost.className = `candidate wnd-v${previewClass ? ` ${previewClass}` : ''}`;
verticalHost.dataset.skinVertical = '';
if (previewVerticalHtml) fillPreviewHost(verticalHost, previewVerticalHtml);
verticalStage.append(verticalHost);
vertical.append(verticalStage);
const toolbar = document.createElement('div');
toolbar.className = 'skin-preview-row skin-preview-toolbar';
const toolbarStage = document.createElement('div');
toolbarStage.className = 'skin-preview-stage';
const toolbarHost = document.createElement('div');
toolbarHost.className = `ftb-preview-host${previewClass ? ` ${previewClass}` : ''}`;
toolbarHost.dataset.skinToolbar = '';
fillToolbar(toolbarHost);
toolbarStage.append(toolbarHost);
toolbar.append(toolbarStage);
previews.append(horizontal, vertical, toolbar);
card.append(header, previews);
void ensureExternalSkinStyle(skin).then(() => applyExternalCardTheme(skin));
return card;
}));
empty.textContent = catalogScanned ? '没有发现外部皮肤。' : '尚未扫描。点击“刷新皮肤”读取皮肤目录。';
@@ -181,19 +348,29 @@ function renderExternalSkins(): void {
details.append(label, entries);
diagnostics.append(details);
}
externalSkins.forEach(applyExternalCardTheme);
syncSkinSwitches();
syncAppearancePreviews();
}
export function applyCandidateSkinCatalog(
skins: unknown, issues: unknown, directory: unknown, scanned: unknown, revision: unknown
): void {
const nextRevision = typeof revision === 'number' && Number.isFinite(revision) ? revision : 0;
if (nextRevision !== catalogRevision) resetExternalSkinStyles();
externalSkins = Array.isArray(skins) ? skins.filter((skin): skin is ExternalSkin =>
!!skin && typeof skin === 'object' && typeof skin.id === 'string' && typeof skin.name === 'string') : [];
!!skin && typeof skin === 'object' && typeof skin.id === 'string' && typeof skin.name === 'string').map((skin) => ({
...skin,
base: typeof skin.base === 'string' ? skin.base : 'fluent',
layouts: Array.isArray(skin.layouts) ? skin.layouts : [],
themes: Array.isArray(skin.themes) ? skin.themes : [],
compatible: skin.compatible !== false
})) : [];
scanIssues = Array.isArray(issues) ? issues.filter((issue): issue is SkinScanIssue =>
!!issue && typeof issue === 'object' && typeof issue.folder === 'string' && typeof issue.reason === 'string') : [];
skinDirectory = typeof directory === 'string' ? directory : '';
catalogScanned = scanned === true;
catalogRevision = typeof revision === 'number' && Number.isFinite(revision) ? revision : 0;
catalogRevision = nextRevision;
renderExternalSkins();
}
@@ -203,38 +380,38 @@ export function syncSkinPreviewTheme(theme: SkinPreviewTheme): void {
activeTheme = theme;
BUILTIN_SKINS.forEach((skin) => {
previewOverrides[skin] = null;
applySkinPreviewTheme(skin, resolvedPreviewTheme(skin));
applyBuiltinCardTheme(skin, resolvedPreviewTheme(skin));
});
externalSkins.forEach((skin) => {
previewOverrides[skin.id] = null;
applyExternalCardTheme(skin);
});
}
export async function setupSkin(): Promise<void> {
const horizontalHtml = await loadHTML('/src/partials/candidate/candidate-wnd-h.html');
const verticalHtml = await loadHTML('/src/partials/candidate/candidate-wnd-v.html');
previewHorizontalHtml = await loadHTML('/src/partials/candidate/candidate-wnd-h.html');
previewVerticalHtml = await loadHTML('/src/partials/candidate/candidate-wnd-v.html');
document.querySelectorAll<HTMLElement>('[data-skin-horizontal]').forEach((host) => {
host.innerHTML = horizontalHtml; limitCandidatePreview(host);
host.innerHTML = previewHorizontalHtml; limitCandidatePreview(host);
});
document.querySelectorAll<HTMLElement>('[data-skin-vertical]').forEach((host) => {
host.innerHTML = verticalHtml; limitCandidatePreview(host);
host.innerHTML = previewVerticalHtml; limitCandidatePreview(host);
});
document.querySelectorAll<HTMLElement>('[data-skin-toolbar]').forEach(fillToolbar);
BUILTIN_SKINS.forEach((skin) => applySkinPreviewTheme(skin, resolvedPreviewTheme(skin)));
BUILTIN_SKINS.forEach((skin) => applyBuiltinCardTheme(skin, resolvedPreviewTheme(skin)));
syncSkinSwitches();
syncAppearancePreviews();
renderExternalSkins();
document.querySelectorAll<HTMLElement>('[data-skin-switch]').forEach((element) => {
if (!BUILTIN_SKINS.includes(element.dataset.skinSwitch as BuiltinSkin)) return;
const activate = () => selectSkin(element.dataset.skinSwitch, true);
element.addEventListener('click', activate);
element.addEventListener('keydown', (event) => {
if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); activate(); }
});
bindSkinSwitch(element, element.dataset.skinSwitch || 'fluent', true);
});
document.querySelectorAll<HTMLButtonElement>('[data-skin-preview-switch]').forEach((button) => {
button.addEventListener('click', () => {
const skin = asBuiltinSkin(button.closest<HTMLElement>('[data-candidate-skin]')?.dataset.candidateSkin);
previewOverrides[skin] = resolvedPreviewTheme(skin) === 'light' ? 'dark' : 'light';
applySkinPreviewTheme(skin, previewOverrides[skin]!);
applyBuiltinCardTheme(skin, previewOverrides[skin]!);
});
});
document.getElementById('refreshExternalSkins')?.addEventListener('click', () => {
@@ -80,7 +80,7 @@ export function applyDocumentTheme(theme: ResolvedTheme): void {
}
export function applyCandidatePreviewTheme(theme: ResolvedTheme): void {
document.querySelectorAll('.candidate:not(#skinCandidateHorizontal):not(#skinCandidateVertical)').forEach((element) => {
document.querySelectorAll('.cand-preview .candidate').forEach((element) => {
element.classList.toggle('theme-light', theme === 'light');
element.classList.toggle('theme-dark', theme === 'dark');
});
@@ -153,7 +153,7 @@ export function applyThemeConfig(config: ThemeConfig | undefined): void {
}
export function applyFtbPreviewTheme(theme: ResolvedTheme): void {
document.querySelectorAll('.ftb-preview-host:not(#skinToolbarPreview)').forEach((element) => {
document.querySelectorAll('.ftb-preview-host:not(#skinToolbarPreview):not([data-skin-toolbar])').forEach((element) => {
element.classList.toggle('theme-light', theme === 'light');
element.classList.toggle('theme-dark', theme === 'dark');
});
@@ -15,6 +15,14 @@
flex-direction: row;
justify-content: center;
align-items: center;
overflow: visible;
}
.cand-preview.has-skin-decoration {
min-height: 22rem;
padding: 16px;
}
.cand-preview .candidate {
margin-top: 0;
}
.candidate {
@@ -9,7 +9,7 @@
margin-top: 0px;
margin-left: 0px;
background: var(--cand-bg);
padding: 2px;
padding: 2px 1px;
border-radius: 6px;
box-shadow: var(--cand-shadow);
min-width: 7em;
@@ -44,6 +44,7 @@
.cand {
border-radius: 4px;
padding: 2px 4px 2px 0;
}
.cand:hover {
@@ -63,10 +64,11 @@
}
.first-row-wrapper {
margin-left: 2px;
margin-left: 0;
}
.first {
position: relative;
background-color: var(--cand-selected);
border-radius: 4px;
}
@@ -96,9 +98,9 @@
}
.cand .text {
padding-left: calc(0.1em + 1px);
padding-right: 2.5px;
line-height: 1.2;
padding-left: calc(0.2em + 3px);
padding-right: 4px;
line-height: 1.25;
}
.num {
@@ -36,6 +36,8 @@
.cand {
border-radius: 4px;
padding-top: 1px;
padding-bottom: 1px;
}
.cand:hover {
@@ -1,4 +1,7 @@
.skin-theme-card { padding: 0; overflow: hidden; }
.skin-theme-card.has-skin-decoration { overflow: visible; }
.skin-theme-card.has-skin-decoration .skin-preview-list,
.skin-theme-card.has-skin-decoration .skin-preview-stage { overflow: visible; }
.skin-theme-card + .skin-theme-card { margin-top: 18px; }
.skin-theme-header { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 20px 24px; }
.skin-theme-description { margin-top: 5px; color: var(--text-muted); font-size: 13px; }
@@ -60,8 +63,15 @@
}
.candidate.skin-wechat .container { padding: 2px; border-radius: 5px; }
.candidate.skin-wechat .cand { border-radius: 4px; }
.candidate.skin-wechat.wnd-v .cand {
padding-top: 1px;
padding-bottom: 1px;
}
.candidate.skin-wechat .first { border-radius: 5px; }
.candidate.skin-wechat .first::before { display: none; }
.candidate.skin-wechat .cand.first:hover {
background-color: var(--cand-selected);
}
.candidate.skin-wechat .first .text,
.candidate.skin-wechat .first .num,
.candidate.skin-wechat .first .cand-no { color: #fff; }
@@ -263,7 +273,7 @@
}
.external-skin-refresh { flex: 0 0 auto; height: 30px; margin-top: 2px; }
.external-skin-actions { display: flex; gap: 8px; flex: 0 0 auto; }
.external-skin-list { display: grid; gap: 12px; }
.external-skin-list { display: grid; gap: 18px; }
.external-skin-card {
display: grid; grid-template-columns: 112px minmax(0, 1fr) auto; align-items: center;
gap: 16px; min-height: 92px; padding: 14px 18px;