- Move all inline <script> blocks (layout head/theme, topbar dark toggle, foot, paste, new, history, mine, settings, admin, unlock) to external files under internal/web/static/. Page data reaches scripts via data-* attributes (data-paste-id, data-default-dark) instead of template vars. - Replace inline onclick handlers (copy, delete, stats toggle) with addEventListener wiring. - Convert inline style="" attributes to CSS utility classes; swatch colors are now set via CSSOM/DOM APIs instead of innerHTML strings. - script-src/style-src are now plain 'self'; img-src data: stays for the SVG data-URI backgrounds. Verified with headless chromium: zero CSP violations on all pages in dark and light presets, theme swatches, admin lock, tables and paste view render correctly.
This commit is contained in:
@@ -13,102 +13,11 @@
|
||||
</button>
|
||||
<h3>Theme</h3>
|
||||
<div class="theme-grid" id="theme-grid"></div>
|
||||
<h3 style="margin-top:18px">Editor</h3>
|
||||
<label class="toggle" for="wrap-setting" style="display:inline-flex"><input type="checkbox" id="wrap-setting"> Line wrap</label>
|
||||
<h3 class="mt18">Editor</h3>
|
||||
<label class="toggle toggle-inline" for="wrap-setting"><input type="checkbox" id="wrap-setting"> Line wrap</label>
|
||||
<p class="admin-link-row"><a class="admin-link" href="/admin">Admin</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
// #112: derive each preset's swatches from the real CSS variables in
|
||||
// app.css by temporarily applying data-preset, so they can never drift.
|
||||
// #127: 5 theme pairs, light swatches top row, dark bottom row.
|
||||
var pairs = [
|
||||
{ id: 'midnight', name: 'Midnight' },
|
||||
{ id: 'smooth', name: 'Smooth' },
|
||||
{ id: 'pastel-lavender', name: 'Pastel Lavender' },
|
||||
{ id: 'pastel-peach', name: 'Pastel Peach' },
|
||||
{ id: 'pastel-cloud', name: 'Pastel Cloud' }
|
||||
];
|
||||
var SWATCH_VARS = ['--bg', '--surface', '--surface-2', '--muted', '--accent'];
|
||||
|
||||
function presetColors(id) {
|
||||
var root = document.documentElement;
|
||||
var prev = root.getAttribute('data-preset');
|
||||
root.setAttribute('data-preset', id);
|
||||
var cs = getComputedStyle(root);
|
||||
var colors = SWATCH_VARS.map(function (v) { return cs.getPropertyValue(v).trim(); });
|
||||
if (prev === null) root.removeAttribute('data-preset'); else root.setAttribute('data-preset', prev);
|
||||
return colors;
|
||||
}
|
||||
|
||||
// current base pair + dark flag from the resolved data-preset.
|
||||
// "midnight" is itself the dark variant, so check dark ids first.
|
||||
function state() {
|
||||
var p = document.documentElement.dataset.preset || 'midnight';
|
||||
if (p === 'midnight' || /-dark$/.test(p)) {
|
||||
return { base: p === 'midnight' ? 'midnight' : p.replace(/-dark$/, ''), dark: true };
|
||||
}
|
||||
return { base: p === 'midnight-light' ? 'midnight' : p, dark: false };
|
||||
}
|
||||
|
||||
var grid = document.getElementById('theme-grid');
|
||||
var cards = {};
|
||||
// #132: midnight is dark-first (root preset = midnight = dark; its light
|
||||
// variant is midnight-light), the others are light-first. Resolve the
|
||||
// LIGHT and DARK preset ids generically so the light swatches always
|
||||
// render in the top row of every card.
|
||||
function lightPreset(id) {
|
||||
if (id === 'midnight') return 'midnight-light';
|
||||
return id; // light-first bases use themselves as the light variant
|
||||
}
|
||||
function darkPreset(id) {
|
||||
if (id === 'midnight') return 'midnight'; // root preset is midnight's dark
|
||||
return id + '-dark';
|
||||
}
|
||||
pairs.forEach(function (t) {
|
||||
var light = presetColors(lightPreset(t.id));
|
||||
var dark = presetColors(darkPreset(t.id));
|
||||
var btn = document.createElement('button');
|
||||
btn.type = 'button';
|
||||
btn.className = 'theme-card';
|
||||
btn.setAttribute('aria-pressed', 'false');
|
||||
btn.setAttribute('data-pair', t.id);
|
||||
btn.innerHTML = '<strong>' + t.name + '</strong>' +
|
||||
'<span class="swatches">' + light.map(function (c) {
|
||||
return '<span class="swatch" style="background:' + c + '"></span>';
|
||||
}).join('') + '</span>' +
|
||||
'<span class="swatches">' + dark.map(function (c) {
|
||||
return '<span class="swatch" style="background:' + c + '"></span>';
|
||||
}).join('') + '</span>';
|
||||
btn.addEventListener('click', function () {
|
||||
var dark = state().dark;
|
||||
document.documentElement.dataset.preset = dark ? t.id + '-dark' : t.id;
|
||||
try { localStorage.setItem('palette-theme', t.id); } catch (e) {}
|
||||
Object.keys(cards).forEach(function (k) { cards[k].setAttribute('aria-pressed', 'false'); });
|
||||
btn.setAttribute('aria-pressed', 'true');
|
||||
});
|
||||
cards[t.id] = btn;
|
||||
grid.appendChild(btn);
|
||||
});
|
||||
|
||||
function sync() {
|
||||
var s = state();
|
||||
Object.keys(cards).forEach(function (k) {
|
||||
cards[k].setAttribute('aria-pressed', k === s.base ? 'true' : 'false');
|
||||
});
|
||||
var dt = document.getElementById('settings-dark-toggle');
|
||||
if (dt) dt.setAttribute('aria-pressed', s.dark ? 'true' : 'false');
|
||||
}
|
||||
sync();
|
||||
// the topbar script runs before this button exists, so wire it here
|
||||
var dt = document.getElementById('settings-dark-toggle');
|
||||
dt.addEventListener('click', function () {
|
||||
var btns = document.querySelectorAll('.topbar .dark-toggle');
|
||||
if (btns.length) btns[0].click(); else document.dispatchEvent(new CustomEvent('palette-darkchange'));
|
||||
});
|
||||
document.addEventListener('palette-darkchange', sync);
|
||||
})();
|
||||
</script>
|
||||
<script src="/static/settings.js" defer></script>
|
||||
{{template "foot" .}}
|
||||
|
||||
Reference in New Issue
Block a user