This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/server/pages/templates/base.html
gobha c687002015 Changeset 0.37.11 (#223)
Co-authored-by: gobha <jasafpro@gmail.com>
Co-committed-by: gobha <jasafpro@gmail.com>
2026-03-22 00:57:49 +00:00

203 lines
13 KiB
HTML

{{define "base.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, interactive-widget=resizes-content">
<title>{{block "title" .}}Chat Switchboard{{end}}</title>
<link rel="icon" type="image/svg+xml" href="{{.BasePath}}/favicon.svg?v={{.Version}}">
<link rel="icon" type="image/x-icon" href="{{.BasePath}}/favicon.ico?v={{.Version}}">
{{/* Decomposed from monolithic styles.css (v0.22.9) */}}
<link rel="stylesheet" href="{{.BasePath}}/css/variables.css?v={{.Version}}">
<link rel="stylesheet" href="{{.BasePath}}/css/layout.css?v={{.Version}}">
<link rel="stylesheet" href="{{.BasePath}}/css/primitives.css?v={{.Version}}">
<link rel="stylesheet" href="{{.BasePath}}/css/modals.css?v={{.Version}}">
{{/* chat.css removed in v0.37.10 — replaced by sw-chat-surface.css + sw-chat-pane.css */}}
{{/* panels.css, pane-container.css, chat-pane.css removed in v0.37.10 */}}
<link rel="stylesheet" href="{{.BasePath}}/css/surfaces.css?v={{.Version}}">
<link rel="stylesheet" href="{{.BasePath}}/css/splash.css?v={{.Version}}">
<link rel="stylesheet" href="{{.BasePath}}/css/sw-primitives.css?v={{.Version}}">
<link rel="stylesheet" href="{{.BasePath}}/css/sw-chat-pane.css?v={{.Version}}">
<link rel="stylesheet" href="{{.BasePath}}/css/sw-notes-pane.css?v={{.Version}}">
<link rel="stylesheet" href="{{.BasePath}}/css/user-menu.css?v={{.Version}}">
<link rel="stylesheet" href="{{.BasePath}}/css/tool-grants.css?v={{.Version}}">
<link rel="stylesheet" href="{{.BasePath}}/css/workflow.css?v={{.Version}}">
<link rel="stylesheet" href="{{.BasePath}}/css/admin-surfaces.css?v={{.Version}}">
<link rel="stylesheet" href="{{.BasePath}}/css/extension-surface.css?v={{.Version}}">
{{if eq .Surface "chat"}}{{template "css-chat" .}}{{end}}
{{if eq .Surface "notes"}}{{template "css-notes" .}}{{end}}
{{/* v0.27.0: Extension surface CSS — loaded from /surfaces/{id}/css/main.css */}}
{{if and .Manifest (eq .Manifest.Source "extension")}}
<link rel="stylesheet" href="{{.BasePath}}/surfaces/{{.Surface}}/css/main.css?v={{.Version}}">
{{end}}
<style>
:root {
--banner-h: 28px;
}
body { margin: 0; background: var(--bg); color: var(--text); display: flex; flex-direction: column; height: 100vh; overflow: hidden; }
.surface { flex: 1; min-height: 0; overflow: hidden; }
.surface-inner { width: 100%; height: 100%; transform-origin: top left; }
.banner { flex-shrink: 0; }
</style>
<meta name="theme-color" content="#0e0e10" id="metaThemeColor">
<script>
// Early theme application — prevents flash of wrong theme on all surfaces.
// Read from Theme API key (switchboard_theme) as authoritative source.
(function() {
try {
var mode = localStorage.getItem('switchboard_theme') || 'system';
var resolved = mode;
if (mode === 'system') resolved = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', resolved);
var meta = document.getElementById('metaThemeColor');
if (meta) meta.content = resolved === 'light' ? '#f7f7fa' : '#0e0e10';
} catch(e) {}
})();
</script>
</head>
<body data-surface="{{.Surface}}" data-base-path="{{.BasePath}}">
{{if .Banner.Visible}}
<div class="banner banner-top active" style="background:{{.Banner.Background}};color:{{.Banner.Color}};height:var(--banner-h);line-height:var(--banner-h);text-align:center;font-size:12px;font-weight:600;overflow:hidden;">
{{.Banner.Text}}
</div>
{{end}}
<div class="surface" id="surface">
<div class="surface-inner" id="surfaceInner">
{{if eq .Surface "chat"}}{{template "surface-chat" .}}
{{else if eq .Surface "admin"}}{{template "surface-admin" .}}
{{else if eq .Surface "team-admin"}}{{template "surface-team-admin" .}}
{{else if eq .Surface "notes"}}{{template "surface-notes" .}}
{{else if eq .Surface "settings"}}{{template "surface-settings" .}}
{{else if and .Manifest (eq .Manifest.Source "extension")}}{{template "surface-extension" .}}
{{else}}<div style="padding:20px">Unknown surface: {{.Surface}}</div>
{{end}}
</div>
</div>
{{if .Banner.Visible}}
<div class="banner banner-bottom active" style="background:{{.Banner.Background}};color:{{.Banner.Color}};height:var(--banner-h);line-height:var(--banner-h);text-align:center;font-size:12px;font-weight:600;overflow:hidden;">
{{.Banner.Text}}
</div>
{{end}}
<script nonce="{{.CSPNonce}}">
window.__BASE__ = '{{.BasePath}}';
window.__VERSION__ = '{{.Version}}';
window.__ENV__ = '{{.Environment}}';
window.__BRANDING__ = {};
window.__SURFACE__ = '{{.Surface}}';
window.__PAGE_DATA__ = {{.Data | toJSON}};
window.__USER__ = {{.User | toJSON}};
{{if .Manifest}}window.__MANIFEST__ = {{.Manifest | toJSON}};{{end}}
</script>
<script type="module" nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/sb.js?v={{.Version}}"></script>
<script type="module" nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/app-state.js?v={{.Version}}"></script>
{{/* api.js removed in v0.37.10. Extension surfaces using Switchboard.init()
should migrate to the Preact SDK (sw/sdk/). */}}
<script type="module" nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/events.js?v={{.Version}}"></script>
<script type="module" nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/ui-primitives.js?v={{.Version}}"></script>
<script nonce="{{.CSPNonce}}" src="{{.BasePath}}/vendor/marked.min.js"></script>
<script nonce="{{.CSPNonce}}" src="{{.BasePath}}/vendor/purify.min.js"></script>
<script type="module" nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/ui-format.js?v={{.Version}}"></script>
<script type="module" nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/virtual-scroll.js?v={{.Version}}"></script>
{{/* ui-core.js + pages.js removed in v0.37.10 */}}
{{/* v0.25.0: Component scripts — available on all surfaces */}}
<script type="module" nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/user-menu.js?v={{.Version}}"></script>
<script type="module" nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/model-selector.js?v={{.Version}}"></script>
<script type="module" nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/file-tree.js?v={{.Version}}"></script>
<script type="module" nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/code-editor.js?v={{.Version}}"></script>
{{/* chat-pane.js + pane-container.js removed in v0.37.10 — see sw/components/chat-pane/ */}}
<script type="module" nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/drag-resize.js?v={{.Version}}"></script>
{{/* v0.28.5: SDK — composition layer over globals. Must load after all
primitives/components it wraps, before extensions.js. */}}
<script type="module" nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/switchboard-sdk.js?v={{.Version}}"></script>
{{/* ── Universal init: SDK boot for ALL surfaces ── */}}
<script type="module" nonce="{{.CSPNonce}}">
// v0.28.5: SDK handles theme, appearance, and UserMenu hydration.
// Surfaces that load app.js call Switchboard.init() again (idempotent).
Switchboard.init();
// v0.28.0: Universal logout — available on every surface.
// Chat/editor/notes load app.js which overrides this with a
// richer version (showConfirm, App.chats cleanup, etc.).
// Extension surfaces get this baseline version.
function handleLogout() {
if (!confirm('Sign out?')) return;
if (typeof Events !== 'undefined') { Events.disconnect(); Events.clear(); }
if (typeof API !== 'undefined' && API.logout) API.logout();
location.reload();
}
// Register fallback — app.js re-registers with a richer version.
sb.register('handleLogout', handleLogout);
</script>
{{if eq .Surface "chat"}}{{template "scripts-chat" .}}{{end}}
{{if eq .Surface "admin"}}{{template "scripts-admin" .}}{{end}}
{{if eq .Surface "team-admin"}}{{template "scripts-team-admin" .}}{{end}}
{{if eq .Surface "notes"}}{{template "scripts-notes" .}}{{end}}
{{if eq .Surface "settings"}}{{template "scripts-settings" .}}{{end}}
{{/* v0.27.0: Extension surface JS — loaded from /surfaces/{id}/js/main.js */}}
{{if and .Manifest (eq .Manifest.Source "extension")}}
<script type="module" nonce="{{.CSPNonce}}" src="{{.BasePath}}/surfaces/{{.Surface}}/js/main.js?v={{.Version}}"></script>
{{end}}
{{/* v0.28.5: UserMenu hydration moved into Switchboard.init() (switchboard-sdk.js).
The SDK handles create/bind/setUser for all non-chat surfaces automatically. */}}
{{/* ── Debug Log Modal (all surfaces) ───── */}}
<div id="debugModal" class="modal-overlay">
<div class="modal modal-wide">
<div class="modal-header">
<h2>Debug Log</h2>
<button class="modal-close" onclick="sb.call('closeModal','debugModal')"></button>
</div>
<div class="modal-tabs">
<button class="debug-tab active" data-tab="console" onclick="sb.call('switchDebugTab','console')">Console <span id="debugConsoleCount" class="badge">0</span></button>
<button class="debug-tab" data-tab="network" onclick="sb.call('switchDebugTab','network')">Network <span id="debugNetworkCount" class="badge">0</span></button>
<button class="debug-tab" data-tab="state" onclick="sb.call('switchDebugTab','state')">State</button>
<button class="debug-tab" data-tab="repl" onclick="sb.call('switchDebugTab','repl')">REPL</button>
</div>
<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;flex:1;overflow-y:auto;min-height:0;"></div>
</div>
<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;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;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>
</div>
<div class="modal-footer" style="display:flex;justify-content:space-between;align-items:center;">
<div style="display:flex;gap:8px;">
<button class="btn-danger btn-small" onclick="sb.call('runDebugDiagnostics')">Diagnostics</button>
<button class="btn-danger btn-small" onclick="sb.call('purgeCache')">Purge Cache</button>
</div>
<div style="display:flex;gap:8px;">
<button class="btn-secondary btn-small" onclick="sb.call('clearDebugLog')">Clear</button>
<button class="btn-secondary btn-small" onclick="sb.call('copyDebugLog')">Copy</button>
<button class="btn-secondary btn-small" onclick="sb.call('exportDebugLog')">Export</button>
</div>
</div>
</div>
</div>
<script type="module" nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/debug.js?v={{.Version}}"></script>
<script type="module" nonce="{{.CSPNonce}}" src="{{.BasePath}}/js/repl.js?v={{.Version}}"></script>
<div id="toastContainer" class="toast-container"></div>
</body>
</html>
{{end}}