Changeset 0.25.3 (#163)

This commit is contained in:
2026-03-09 01:54:06 +00:00
parent 6c484fa7f8
commit 2f7a0fb027
11 changed files with 235 additions and 74 deletions

View File

@@ -34,16 +34,18 @@
.surface { height: var(--surface-h); overflow: hidden; }
.banner { flex-shrink: 0; }
</style>
<meta name="theme-color" content="#0e0e10">
<meta name="theme-color" content="#0e0e10" id="metaThemeColor">
<script>
// Early theme application — prevents flash of wrong theme on all surfaces
// Early theme application — prevents flash of wrong theme on all surfaces.
// Read from Theme API key (switchboard_theme) as authoritative source.
(function() {
try {
var p = JSON.parse(localStorage.getItem('cs-appearance') || '{}');
var mode = p.theme || 'system';
var mode = localStorage.getItem('switchboard_theme') || 'system';
var resolved = mode;
if (mode === 'system') resolved = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
if (resolved === 'light') document.documentElement.setAttribute('data-theme', 'light');
document.documentElement.setAttribute('data-theme', resolved);
var meta = document.getElementById('metaThemeColor');
if (meta) meta.content = resolved === 'light' ? '#f7f7fa' : '#0e0e10';
} catch(e) {}
})();
</script>
@@ -104,6 +106,14 @@
<script nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/drag-resize.js?v={{.Version}}"></script>
<script nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/pane-container.js?v={{.Version}}"></script>
{{/* ── Universal init: theme + appearance for ALL surfaces ── */}}
<script nonce="{{.CSPNonce}}">
// Theme must init on every surface, not just chat.
if (typeof Theme !== 'undefined') Theme.init();
// Appearance (zoom + font size) must restore on every surface.
if (typeof UI !== 'undefined' && UI.restoreAppearance) UI.restoreAppearance();
</script>
{{if eq .Surface "chat"}}{{template "scripts-chat" .}}{{end}}
{{if eq .Surface "admin"}}{{template "scripts-admin" .}}{{end}}
{{if eq .Surface "editor"}}{{template "scripts-editor" .}}{{end}}
@@ -112,7 +122,7 @@
{{/* ── Debug Log Modal (all surfaces) ───── */}}
<div id="debugModal" class="modal-overlay">
<div class="modal-content modal-lg">
<div class="modal modal-wide">
<div class="modal-header">
<h2>Debug Log</h2>
<button class="modal-close" onclick="closeModal('debugModal')"></button>
@@ -123,23 +133,23 @@
<button class="debug-tab" data-tab="state" onclick="switchDebugTab('state')">State</button>
<button class="debug-tab" data-tab="repl" onclick="switchDebugTab('repl')">REPL</button>
</div>
<div class="modal-body" style="padding:0;height:400px;overflow:hidden;">
<div id="debugConsoleTab" class="debug-tab-content" style="display:block;height:100%;overflow-y:auto;">
<div style="padding:8px;display:flex;gap:8px;align-items:center;">
<div class="modal-body" style="padding:0;overflow:hidden;display:flex;flex-direction:column;min-height:0;">
<div id="debugConsoleTab" class="debug-tab-content" style="display:flex;flex-direction:column;flex:1;min-height:0;">
<div style="padding:8px;display:flex;gap:8px;align-items:center;flex-shrink:0;">
<label><input type="checkbox" id="debugFilterErrors"> Errors only</label>
<label><input type="checkbox" id="debugAutoScroll" checked> Auto-scroll</label>
</div>
<div id="debugConsoleContent" class="debug-content" style="font-family:monospace;font-size:12px;padding:8px;"></div>
<div id="debugConsoleContent" class="debug-content" style="font-family:monospace;font-size:12px;padding:8px;flex:1;overflow-y:auto;min-height:0;"></div>
</div>
<div id="debugNetworkTab" class="debug-tab-content" style="display:none;height:100%;overflow-y:auto;">
<div id="debugNetworkTab" class="debug-tab-content" style="display:none;flex:1;min-height:0;overflow-y:auto;">
<div id="debugNetworkContent" class="debug-content" style="font-family:monospace;font-size:12px;padding:8px;"></div>
</div>
<div id="debugStateTab" class="debug-tab-content" style="display:none;height:100%;overflow-y:auto;">
<div id="debugStateTab" class="debug-tab-content" style="display:none;flex:1;min-height:0;overflow-y:auto;">
<div id="debugStateContent" class="debug-content" style="font-family:monospace;font-size:12px;padding:8px;white-space:pre-wrap;"></div>
</div>
<div id="debugReplTab" class="debug-tab-content" style="display:none;height:100%;display:flex;flex-direction:column;">
<div id="replOutput" style="flex:1;overflow-y:auto;font-family:monospace;font-size:12px;padding:8px;"></div>
<div style="border-top:1px solid var(--border);padding:8px;">
<div id="debugReplTab" class="debug-tab-content" style="display:none;flex:1;min-height:0;flex-direction:column;">
<div id="replOutput" style="flex:1;overflow-y:auto;font-family:monospace;font-size:12px;padding:8px;min-height:0;"></div>
<div style="border-top:1px solid var(--border);padding:8px;flex-shrink:0;">
<input type="text" id="replInput" placeholder="Type expression…" style="width:100%;font-family:monospace;font-size:12px;">
</div>
</div>

View File

@@ -9,7 +9,7 @@
{{/* Top Bar */}}
<div class="admin-topbar">
<a href="{{.BasePath}}/" class="admin-topbar-back" onclick="if(history.length>1){history.back();return false;}">
<a href="{{.BasePath}}/" class="admin-topbar-back" id="adminBackBtn">>
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
Back
</a>
@@ -220,4 +220,52 @@
<script nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/notifications.js?v={{.Version}}"></script>
<script nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/admin-scaffold.js?v={{.Version}}"></script>
<script nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/admin-surfaces.js?v={{.Version}}"></script>
<script nonce="{{.CSPNonce}}">
document.addEventListener('DOMContentLoaded', () => {
// ── Admin navigation: don't pollute browser history ──
// Same pattern as settings surface: stash return URL on entry,
// tab/nav switches use location.replace(), back goes directly out.
const RETURN_KEY = 'sb_admin_return';
if (!sessionStorage.getItem(RETURN_KEY)) {
const ref = document.referrer;
const base = (window.__BASE__ || '') + '/';
if (ref && ref.startsWith(location.origin) && !ref.includes('/admin')) {
sessionStorage.setItem(RETURN_KEY, ref);
} else {
sessionStorage.setItem(RETURN_KEY, location.origin + base);
}
}
const backBtn = document.getElementById('adminBackBtn');
if (backBtn) {
backBtn.addEventListener('click', (e) => {
e.preventDefault();
const returnURL = sessionStorage.getItem(RETURN_KEY);
sessionStorage.removeItem(RETURN_KEY);
location.href = returnURL || (window.__BASE__ || '') + '/';
});
}
// Category tabs: replace history instead of pushing
document.querySelectorAll('.admin-cat-btn').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
location.replace(link.href);
});
});
// Nav links are dynamically rebuilt by admin-scaffold.js —
// use event delegation on the nav container
const adminNav = document.getElementById('adminNav');
if (adminNav) {
adminNav.addEventListener('click', (e) => {
const link = e.target.closest('.admin-nav-link');
if (link && link.href) {
e.preventDefault();
location.replace(link.href);
}
});
}
});
</script>
{{end}}

View File

@@ -9,7 +9,7 @@
{{/* Top Bar */}}
<div class="settings-topbar">
<a href="{{.BasePath}}/" class="settings-topbar-back" onclick="if(history.length>1){history.back();return false;}">
<a href="{{.BasePath}}/" class="settings-topbar-back" id="settingsBackBtn">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
Back
</a>
@@ -98,7 +98,7 @@
<h3>UI Scale</h3>
<div class="form-group">
<div style="display:flex;align-items:center;gap:8px;">
<input type="range" id="settingsScale" min="80" max="120" step="5" value="100" style="flex:1;">
<input type="range" id="settingsScale" min="80" max="175" step="5" value="100" style="flex:1;">
<span id="scaleValue" style="font-size:12px;color:var(--text-2);font-family:var(--mono);min-width:32px;">100%</span>
</div>
</div>
@@ -170,6 +170,40 @@
const section = document.getElementById('settingsSection')?.dataset.section;
if (!section) return;
// ── Settings navigation: don't pollute browser history ──
// On first settings page load, stash the URL the user came from.
// Tab switches use location.replace() so they don't stack in history.
// Back button navigates directly to the stored return URL.
const RETURN_KEY = 'sb_settings_return';
if (!sessionStorage.getItem(RETURN_KEY)) {
// Prefer referrer if it's on the same origin and not another settings page
const ref = document.referrer;
const base = (window.__BASE__ || '') + '/';
if (ref && ref.startsWith(location.origin) && !ref.includes('/settings')) {
sessionStorage.setItem(RETURN_KEY, ref);
} else {
sessionStorage.setItem(RETURN_KEY, location.origin + base);
}
}
const backBtn = document.getElementById('settingsBackBtn');
if (backBtn) {
backBtn.addEventListener('click', (e) => {
e.preventDefault();
const returnURL = sessionStorage.getItem(RETURN_KEY);
sessionStorage.removeItem(RETURN_KEY);
location.href = returnURL || (window.__BASE__ || '') + '/';
});
}
// Tab links: replace current history entry instead of pushing
document.querySelectorAll('.settings-nav-link').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
location.replace(link.href);
});
});
// Show BYOK nav if enabled
const pageData = window.__PAGE_DATA__;
if (pageData && pageData.BYOKEnabled) {