mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-07-06 12:37:56 +02:00
Add files via upload
This commit is contained in:
+88
-1
@@ -33,6 +33,93 @@
|
||||
--c2-mono: 'SF Mono', 'Fira Code', 'JetBrains Mono', 'Cascadia Code', monospace;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] {
|
||||
--c2-accent: #60a5fa;
|
||||
--c2-accent-hover: #93c5fd;
|
||||
--c2-accent-dim: rgba(96, 165, 250, 0.14);
|
||||
--c2-accent-glow: rgba(96, 165, 250, 0.28);
|
||||
--c2-green: #34d399;
|
||||
--c2-green-dim: rgba(52, 211, 153, 0.14);
|
||||
--c2-red: #f87171;
|
||||
--c2-red-dim: rgba(248, 113, 113, 0.14);
|
||||
--c2-amber: #fbbf24;
|
||||
--c2-amber-dim: rgba(251, 191, 36, 0.14);
|
||||
--c2-purple: #a78bfa;
|
||||
--c2-purple-dim: rgba(167, 139, 250, 0.14);
|
||||
--c2-surface: #111827;
|
||||
--c2-surface-alt: #0b1120;
|
||||
--c2-border: #263244;
|
||||
--c2-border-hover: #3b4a63;
|
||||
--c2-text: #e5e7eb;
|
||||
--c2-text-dim: #a7b0c0;
|
||||
--c2-text-muted: #6b7280;
|
||||
--c2-shadow-sm: 0 1px 3px rgba(0,0,0,0.34);
|
||||
--c2-shadow-md: 0 8px 24px rgba(0,0,0,0.38);
|
||||
--c2-shadow-lg: 0 18px 48px rgba(0,0,0,0.45);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .c2-modal,
|
||||
html[data-theme="dark"] .c2-modal-content,
|
||||
html[data-theme="dark"] .c2-tab-panel--card,
|
||||
html[data-theme="dark"] .c2-session-detail,
|
||||
html[data-theme="dark"] .c2-payload-card,
|
||||
html[data-theme="dark"] .c2-profile-card,
|
||||
html[data-theme="dark"] .c2-event-card,
|
||||
html[data-theme="dark"] .c2-task-row,
|
||||
html[data-theme="dark"] .c2-listener-card {
|
||||
background: var(--c2-surface);
|
||||
color: var(--c2-text);
|
||||
border-color: var(--c2-border);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .c2-listener-card:hover,
|
||||
html[data-theme="dark"] .c2-payload-card:hover,
|
||||
html[data-theme="dark"] .c2-profile-card:hover {
|
||||
border-color: var(--c2-border-hover);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .c2-session-chip,
|
||||
html[data-theme="dark"] .c2-listener-pill,
|
||||
html[data-theme="dark"] .c2-task-type-badge,
|
||||
html[data-theme="dark"] .c2-tab-btn {
|
||||
background: var(--c2-surface-alt);
|
||||
border-color: var(--c2-border);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] #page-c2-listeners,
|
||||
html[data-theme="dark"] #page-c2-sessions,
|
||||
html[data-theme="dark"] #page-c2-tasks,
|
||||
html[data-theme="dark"] #page-c2-payloads,
|
||||
html[data-theme="dark"] #page-c2-events,
|
||||
html[data-theme="dark"] #page-c2-profiles,
|
||||
html[data-theme="dark"] #page-c2-listeners .page-content,
|
||||
html[data-theme="dark"] #page-c2-sessions .page-content,
|
||||
html[data-theme="dark"] #page-c2-tasks .page-content,
|
||||
html[data-theme="dark"] #page-c2-payloads .page-content,
|
||||
html[data-theme="dark"] #page-c2-events .page-content,
|
||||
html[data-theme="dark"] #page-c2-profiles .page-content,
|
||||
html[data-theme="dark"] .c2-session-layout,
|
||||
html[data-theme="dark"] .c2-session-main {
|
||||
background: var(--c2-surface-alt) !important;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .c2-session-sidebar-wrap,
|
||||
html[data-theme="dark"] .c2-sessions-toolbar,
|
||||
html[data-theme="dark"] .c2-session-sidebar {
|
||||
background: #0b1120 !important;
|
||||
border-color: var(--c2-border) !important;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .c2-session-main-empty {
|
||||
background: transparent !important;
|
||||
color: var(--c2-text-dim) !important;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .c2-session-main-empty__icon {
|
||||
background: var(--c2-accent-dim) !important;
|
||||
border-color: rgba(96, 165, 250, 0.35) !important;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Form Controls (scoped to C2 pages)
|
||||
============================================================================ */
|
||||
@@ -533,7 +620,7 @@
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
padding: 12px 16px 16px;
|
||||
background: linear-gradient(180deg, #f8fafc 0%, #ffffff 180px);
|
||||
background: linear-gradient(180deg, var(--c2-surface-alt) 0%, var(--c2-surface) 180px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
+2445
-4
File diff suppressed because it is too large
Load Diff
@@ -39,6 +39,15 @@
|
||||
"version": "Current version",
|
||||
"toggleSidebar": "Collapse/expand sidebar"
|
||||
},
|
||||
"theme": {
|
||||
"system": "System",
|
||||
"light": "Light",
|
||||
"dark": "Dark",
|
||||
"titleSystem": "Current: system theme. Click to switch to light.",
|
||||
"titleLight": "Current: light theme. Click to switch to dark.",
|
||||
"titleDark": "Current: dark theme. Click to switch to system.",
|
||||
"toggle": "Toggle theme"
|
||||
},
|
||||
"notifications": {
|
||||
"title": "Notifications",
|
||||
"empty": "No new events",
|
||||
|
||||
@@ -39,6 +39,15 @@
|
||||
"version": "当前版本",
|
||||
"toggleSidebar": "折叠/展开侧边栏"
|
||||
},
|
||||
"theme": {
|
||||
"system": "跟随系统",
|
||||
"light": "浅色",
|
||||
"dark": "暗色",
|
||||
"titleSystem": "当前:跟随系统主题。点击切换为浅色。",
|
||||
"titleLight": "当前:浅色主题。点击切换为暗色。",
|
||||
"titleDark": "当前:暗色主题。点击切换为跟随系统。",
|
||||
"toggle": "切换主题"
|
||||
},
|
||||
"notifications": {
|
||||
"title": "事件通知",
|
||||
"empty": "暂无新事件",
|
||||
|
||||
+29
-2
@@ -4210,6 +4210,7 @@ function renderAttackChain(chainData) {
|
||||
const nodeCount = chainData.nodes.length;
|
||||
const edgeCount = chainData.edges.length;
|
||||
const isComplexGraph = nodeCount > 15 || edgeCount > 25;
|
||||
const isDarkTheme = document.documentElement.getAttribute('data-theme') === 'dark';
|
||||
|
||||
// 优化节点标签:智能截断和换行
|
||||
chainData.nodes.forEach(node => {
|
||||
@@ -4313,6 +4314,29 @@ function renderAttackChain(chainData) {
|
||||
iconType = 'vulnerability';
|
||||
}
|
||||
|
||||
const labelTextColor = isDarkTheme ? '#E5E7EB' : '#0F172A';
|
||||
if (isDarkTheme) {
|
||||
typeColor = '#E5E7EB';
|
||||
bgGradientStart = '#111827';
|
||||
if (nodeType === 'target') {
|
||||
bgGradientEnd = '#1E1B4B';
|
||||
} else if (nodeType === 'action') {
|
||||
bgGradientEnd = accentColor === '#10B981' ? '#052E2B' : '#172033';
|
||||
} else if (nodeType === 'vulnerability') {
|
||||
if (riskScore >= 80) {
|
||||
bgGradientEnd = '#3F101C';
|
||||
} else if (riskScore >= 60) {
|
||||
bgGradientEnd = '#3B1D0D';
|
||||
} else if (riskScore >= 40) {
|
||||
bgGradientEnd = '#3A2A0A';
|
||||
} else {
|
||||
bgGradientEnd = '#063A36';
|
||||
}
|
||||
} else {
|
||||
bgGradientEnd = '#172033';
|
||||
}
|
||||
}
|
||||
|
||||
// 为每个节点生成图标 background-image(data URL)
|
||||
const iconSvg = _acBuildNodeIconDataUrl(iconType, accentColor, accentDark);
|
||||
|
||||
@@ -4345,6 +4369,7 @@ function renderAttackChain(chainData) {
|
||||
accentDark: accentDark,
|
||||
bgGradientStart: bgGradientStart,
|
||||
bgGradientEnd: bgGradientEnd,
|
||||
labelTextColor: labelTextColor,
|
||||
iconDataUrl: iconSvg,
|
||||
badgeText: badgeText,
|
||||
riskScore: riskScore,
|
||||
@@ -4444,7 +4469,9 @@ function renderAttackChain(chainData) {
|
||||
},
|
||||
'border-opacity': 0.5,
|
||||
// 文字样式
|
||||
'color': '#0f172a',
|
||||
'color': function(ele) {
|
||||
return ele.data('labelTextColor') || '#0f172a';
|
||||
},
|
||||
'font-size': function(ele) {
|
||||
return isComplexGraph ? '13px' : '14px';
|
||||
},
|
||||
@@ -5048,7 +5075,7 @@ function showNodeDetails(nodeData) {
|
||||
if (nodeData.metadata.ai_analysis) {
|
||||
html += `
|
||||
<div class="node-detail-item">
|
||||
<strong>AI分析:</strong> <div style="margin-top: 5px; padding: 8px; background: #f5f5f5; border-radius: 4px;">${escapeHtml(nodeData.metadata.ai_analysis)}</div>
|
||||
<strong>AI分析:</strong> <div class="node-detail-ai-analysis">${escapeHtml(nodeData.metadata.ai_analysis)}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
+53
-19
@@ -1707,7 +1707,7 @@ window.navigateToVulnerabilitiesWithFilter = navigateToVulnerabilitiesWithFilter
|
||||
|
||||
// 漏洞严重程度分布:半环形(donut)渲染
|
||||
// 几何参数固定,便于配合 viewBox 0 0 560 320 的 SVG 容器
|
||||
// 段间分隔由 CSS 的白色 stroke 完成,不再使用 gapRad
|
||||
// 段间分隔由 gapRad 几何间隙完成,不使用描边,避免浅色/暗色下白边或黑边过重
|
||||
var SEVERITY_DONUT_CFG = {
|
||||
// viewBox 0 0 480 260:整体保持紧凑,但环厚回到「黄金比例」附近,
|
||||
// 让弧带本身有视觉分量,又不像最早那版那样占太多空间。
|
||||
@@ -1717,7 +1717,7 @@ var SEVERITY_DONUT_CFG = {
|
||||
rOuter: 165,
|
||||
rInner: 115, // 环厚 = 50(介于原 90 和上一版 35 之间,自然且有质感)
|
||||
labelOffset: 14,
|
||||
gapRad: 0.012
|
||||
gapRad: 0.022
|
||||
};
|
||||
|
||||
// 三段渐变:[高光浅调, 中段饱和色, 深色边缘] —— 做出类似 3D 釉面的层次
|
||||
@@ -1759,28 +1759,50 @@ function severityLabel(id) {
|
||||
return SEVERITY_DEFAULT_LABELS[id] || id;
|
||||
}
|
||||
|
||||
function isDashboardDarkTheme() {
|
||||
return document.documentElement.getAttribute('data-theme') === 'dark';
|
||||
}
|
||||
|
||||
function ensureSeverityDonutDefs() {
|
||||
var defsEl = document.getElementById('dashboard-severity-donut-defs');
|
||||
if (!defsEl || defsEl.hasChildNodes()) return;
|
||||
if (!defsEl) return;
|
||||
var dark = isDashboardDarkTheme();
|
||||
var html = '';
|
||||
html += '<linearGradient id="donut-track-face" x1="0%" y1="0%" x2="0%" y2="100%">';
|
||||
html += '<stop offset="0%" stop-color="#f8fafc"/>';
|
||||
html += '<stop offset="55%" stop-color="#e8eef5"/>';
|
||||
html += '<stop offset="100%" stop-color="#dce5ef"/>';
|
||||
if (dark) {
|
||||
html += '<stop offset="0%" stop-color="#334155"/>';
|
||||
html += '<stop offset="55%" stop-color="#1e293b"/>';
|
||||
html += '<stop offset="100%" stop-color="#172033"/>';
|
||||
} else {
|
||||
html += '<stop offset="0%" stop-color="#f8fafc"/>';
|
||||
html += '<stop offset="55%" stop-color="#e8eef5"/>';
|
||||
html += '<stop offset="100%" stop-color="#dce5ef"/>';
|
||||
}
|
||||
html += '</linearGradient>';
|
||||
html += '<radialGradient id="donut-track-vignette" cx="50%" cy="85%" r="75%" fx="50%" fy="85%">';
|
||||
html += '<stop offset="0%" stop-color="#ffffff" stop-opacity="0.35"/>';
|
||||
html += '<stop offset="70%" stop-color="#ffffff" stop-opacity="0"/>';
|
||||
if (dark) {
|
||||
html += '<stop offset="0%" stop-color="#0f172a" stop-opacity="0.55"/>';
|
||||
html += '<stop offset="70%" stop-color="#0f172a" stop-opacity="0"/>';
|
||||
} else {
|
||||
html += '<stop offset="0%" stop-color="#ffffff" stop-opacity="0.35"/>';
|
||||
html += '<stop offset="70%" stop-color="#ffffff" stop-opacity="0"/>';
|
||||
}
|
||||
html += '</radialGradient>';
|
||||
html += '<radialGradient id="donut-inner-gloss" cx="35%" cy="75%" r="55%">';
|
||||
html += '<stop offset="0%" stop-color="#ffffff" stop-opacity="0.45"/>';
|
||||
html += '<stop offset="55%" stop-color="#ffffff" stop-opacity="0.08"/>';
|
||||
html += '<stop offset="100%" stop-color="#ffffff" stop-opacity="0"/>';
|
||||
if (dark) {
|
||||
html += '<stop offset="0%" stop-color="#94a3b8" stop-opacity="0.10"/>';
|
||||
html += '<stop offset="55%" stop-color="#94a3b8" stop-opacity="0.03"/>';
|
||||
html += '<stop offset="100%" stop-color="#94a3b8" stop-opacity="0"/>';
|
||||
} else {
|
||||
html += '<stop offset="0%" stop-color="#ffffff" stop-opacity="0.45"/>';
|
||||
html += '<stop offset="55%" stop-color="#ffffff" stop-opacity="0.08"/>';
|
||||
html += '<stop offset="100%" stop-color="#ffffff" stop-opacity="0"/>';
|
||||
}
|
||||
html += '</radialGradient>';
|
||||
html += '<filter id="donut-segment-soften" x="-18%" y="-18%" width="136%" height="136%" color-interpolation-filters="sRGB">';
|
||||
html += '<feGaussianBlur in="SourceAlpha" stdDeviation="0.8" result="blur"/>';
|
||||
html += '<feOffset dx="0" dy="1.5" in="blur" result="off"/>';
|
||||
html += '<feFlood flood-color="#0f172a" flood-opacity="0.13" result="flood"/>';
|
||||
html += '<feFlood flood-color="' + (dark ? '#000000' : '#0f172a') + '" flood-opacity="' + (dark ? '0.28' : '0.13') + '" result="flood"/>';
|
||||
html += '<feComposite in="flood" in2="off" operator="in" result="shadow"/>';
|
||||
html += '<feMerge><feMergeNode in="shadow"/><feMergeNode in="SourceGraphic"/></feMerge>';
|
||||
html += '</filter>';
|
||||
@@ -1812,13 +1834,11 @@ function renderSeverityDonut(bySeverity, total) {
|
||||
ensureSeverityDonutDefs();
|
||||
|
||||
// 背景轨迹(完整半环):双层填充营造凹槽 + 高光
|
||||
if (!trackEl.hasChildNodes()) {
|
||||
var trackPath = halfRingPath(cfg.cx, cfg.cy, cfg.rOuter, cfg.rInner);
|
||||
trackEl.innerHTML =
|
||||
'<path class="donut-track-shadow" d="' + trackPath + '"/>' +
|
||||
'<path class="donut-track" fill="url(#donut-track-face)" d="' + trackPath + '"/>' +
|
||||
'<path class="donut-track-vignette" fill="url(#donut-track-vignette)" d="' + trackPath + '"/>';
|
||||
}
|
||||
var trackPath = halfRingPath(cfg.cx, cfg.cy, cfg.rOuter, cfg.rInner);
|
||||
trackEl.innerHTML =
|
||||
'<path class="donut-track-shadow" d="' + trackPath + '"/>' +
|
||||
'<path class="donut-track" fill="url(#donut-track-face)" d="' + trackPath + '"/>' +
|
||||
'<path class="donut-track-vignette" fill="url(#donut-track-vignette)" d="' + trackPath + '"/>';
|
||||
|
||||
var ids = ['critical', 'high', 'medium', 'low', 'info'];
|
||||
var severities = ids.map(function (id) {
|
||||
@@ -2105,6 +2125,20 @@ function attachSeverityDonutInteractivity() {
|
||||
legend.addEventListener('click', severityLegendClick);
|
||||
legend.addEventListener('keydown', severityLegendKeydown);
|
||||
}
|
||||
if (!severityDonutState.themeObserver) {
|
||||
severityDonutState.themeObserver = new MutationObserver(function (mutations) {
|
||||
for (var i = 0; i < mutations.length; i++) {
|
||||
if (mutations[i].attributeName === 'data-theme') {
|
||||
renderSeverityDonut(severityDonutState.bySeverity, severityDonutState.total);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
severityDonutState.themeObserver.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['data-theme']
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
legend && legend.querySelectorAll('.dashboard-severity-legend-item').forEach(function (item) {
|
||||
|
||||
@@ -154,6 +154,9 @@
|
||||
}
|
||||
applyTranslations(document);
|
||||
updateLangLabel();
|
||||
if (typeof window.refreshThemeToggleLabel === 'function') {
|
||||
window.refreshThemeToggleLabel();
|
||||
}
|
||||
try {
|
||||
window.__locale = lang;
|
||||
} catch (e) { /* ignore */ }
|
||||
@@ -180,6 +183,9 @@
|
||||
await loadLanguageResources(initialLang);
|
||||
applyTranslations(document);
|
||||
updateLangLabel();
|
||||
if (typeof window.refreshThemeToggleLabel === 'function') {
|
||||
window.refreshThemeToggleLabel();
|
||||
}
|
||||
try {
|
||||
window.__locale = i18next.language || initialLang;
|
||||
} catch (e) { /* ignore */ }
|
||||
|
||||
@@ -650,7 +650,7 @@ async function viewSkill(skillId) {
|
||||
<div style="margin-bottom: 8px;"><strong>${escapeHtml(pathLabel)}</strong> ${escapeHtml(sumSkill.path || '')}</div>
|
||||
<div style="margin-bottom: 16px;"><strong>${escapeHtml(modTimeLabel)}</strong> ${escapeHtml(sumSkill.mod_time || '')}</div>
|
||||
<div style="margin-bottom: 8px;"><strong>${escapeHtml(contentLabel)}</strong> <span style="opacity:0.8;font-size:12px;">${escapeHtml(_t('skills.summaryHint'))}</span></div>
|
||||
<pre id="skill-view-body" style="background: #f5f5f5; padding: 16px; border-radius: 4px; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;">${escapeHtml(sumSkill.content || '')}</pre>
|
||||
<pre id="skill-view-body" style="padding: 16px; border-radius: 4px; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word; border: 1px solid var(--border-color);">${escapeHtml(sumSkill.content || '')}</pre>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn-secondary" data-skill-load-full>${escapeHtml(loadFullLabel)}</button>
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const STORAGE_KEY = 'cyberstrike-theme';
|
||||
const THEMES = ['system', 'light', 'dark'];
|
||||
const FALLBACK_LABELS = {
|
||||
system: '跟随系统',
|
||||
light: '浅色',
|
||||
dark: '暗色'
|
||||
};
|
||||
const FALLBACK_TITLES = {
|
||||
system: '当前:跟随系统主题。点击切换为浅色。',
|
||||
light: '当前:浅色主题。点击切换为暗色。',
|
||||
dark: '当前:暗色主题。点击切换为跟随系统。'
|
||||
};
|
||||
const TITLE_KEYS = {
|
||||
system: 'titleSystem',
|
||||
light: 'titleLight',
|
||||
dark: 'titleDark'
|
||||
};
|
||||
|
||||
const media = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
|
||||
|
||||
function themeText(key, fallback) {
|
||||
if (typeof window.t === 'function') {
|
||||
const value = window.t('theme.' + key);
|
||||
if (value && value !== 'theme.' + key) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function getLabel(preference) {
|
||||
return themeText(preference, FALLBACK_LABELS[preference] || FALLBACK_LABELS.system);
|
||||
}
|
||||
|
||||
function getTitle(preference) {
|
||||
const titleKey = TITLE_KEYS[preference] || TITLE_KEYS.system;
|
||||
return themeText(titleKey, FALLBACK_TITLES[preference] || FALLBACK_TITLES.system);
|
||||
}
|
||||
|
||||
function normalizePreference(value) {
|
||||
return THEMES.includes(value) ? value : 'system';
|
||||
}
|
||||
|
||||
function readPreference() {
|
||||
try {
|
||||
return normalizePreference(localStorage.getItem(STORAGE_KEY));
|
||||
} catch (err) {
|
||||
return 'system';
|
||||
}
|
||||
}
|
||||
|
||||
function resolveTheme(preference) {
|
||||
if (preference === 'dark' || preference === 'light') {
|
||||
return preference;
|
||||
}
|
||||
return media && media.matches ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
function updateButton(preference, resolved) {
|
||||
const btn = document.getElementById('theme-toggle-btn');
|
||||
const label = document.getElementById('theme-toggle-label');
|
||||
if (!btn) {
|
||||
return;
|
||||
}
|
||||
btn.dataset.themePreference = preference;
|
||||
btn.dataset.theme = resolved;
|
||||
const title = getTitle(preference);
|
||||
btn.title = title;
|
||||
btn.setAttribute('aria-label', title);
|
||||
if (label) {
|
||||
label.textContent = getLabel(preference);
|
||||
}
|
||||
}
|
||||
|
||||
function applyTheme(preference) {
|
||||
const normalized = normalizePreference(preference);
|
||||
const resolved = resolveTheme(normalized);
|
||||
const root = document.documentElement;
|
||||
root.setAttribute('data-theme-preference', normalized);
|
||||
root.setAttribute('data-theme', resolved);
|
||||
root.style.colorScheme = resolved;
|
||||
updateButton(normalized, resolved);
|
||||
}
|
||||
|
||||
function savePreference(preference) {
|
||||
const normalized = normalizePreference(preference);
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, normalized);
|
||||
} catch (err) {
|
||||
// Ignore storage failures; the current page can still apply the theme.
|
||||
}
|
||||
applyTheme(normalized);
|
||||
}
|
||||
|
||||
window.setThemePreference = savePreference;
|
||||
window.getThemePreference = readPreference;
|
||||
window.cycleThemePreference = function () {
|
||||
const current = readPreference();
|
||||
const next = THEMES[(THEMES.indexOf(current) + 1) % THEMES.length];
|
||||
savePreference(next);
|
||||
};
|
||||
window.refreshThemeToggleLabel = function () {
|
||||
applyTheme(readPreference());
|
||||
};
|
||||
|
||||
if (media) {
|
||||
const onSystemThemeChange = function () {
|
||||
if (readPreference() === 'system') {
|
||||
applyTheme('system');
|
||||
}
|
||||
};
|
||||
if (typeof media.addEventListener === 'function') {
|
||||
media.addEventListener('change', onSystemThemeChange);
|
||||
} else if (typeof media.addListener === 'function') {
|
||||
media.addListener(onSystemThemeChange);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('languagechange', function () {
|
||||
applyTheme(readPreference());
|
||||
});
|
||||
|
||||
function initTheme() {
|
||||
applyTheme(readPreference());
|
||||
if (window.i18nReady && typeof window.i18nReady.then === 'function') {
|
||||
window.i18nReady.then(function () {
|
||||
applyTheme(readPreference());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initTheme);
|
||||
} else {
|
||||
initTheme();
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user