mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-02 13:39:41 +02:00
feat(ui): theme skins — OG Codeman, Daylight Green, Daylight Blue
Add a per-device skin switcher in App Settings → Display: - Three skins via html[data-skin]: og (original Codeman look), daylight-green, and daylight-blue (new default). Per-skin CSS-variable token blocks; the v1.0 "Carbon Aurora" component polish is scoped to non-og skins and parameterized so green/blue differ only by token values. - Self-hosted Manrope (UI) + JetBrains Mono (terminal) variable fonts, served from /fonts (no external CDN, CSP-safe via font-src 'self'). - Per-skin xterm terminal theme with live re-theming of open terminals on skin change; skin-aware --term-bg so the terminal background fills cleanly (fixes the variable-height gap above the toolbar). - Pre-paint inline script applies the saved skin before first paint (no flash); persisted per-device in localStorage + the settings blob, and kept out of the server settings payload (device-local). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -580,6 +580,7 @@ class CodemanApp {
|
||||
const _kbSettings = this.loadAppSettingsFromStorage();
|
||||
if (_kbSettings.extendedKeyboardBar) KeyboardAccessoryBar.setMode('extended');
|
||||
this.applyHeaderVisibilitySettings();
|
||||
this.applySkin();
|
||||
this.applyTabWrapSettings();
|
||||
this.applyMonitorVisibility();
|
||||
// Remove mobile-init class now that JS has applied visibility settings.
|
||||
@@ -637,6 +638,7 @@ class CodemanApp {
|
||||
// Load server-stored settings (async, re-applies visibility after load)
|
||||
this.loadAppSettingsFromServer(settingsPromise).then(() => {
|
||||
this.applyHeaderVisibilitySettings();
|
||||
this.applySkin();
|
||||
this.applyTabWrapSettings();
|
||||
this.applyMonitorVisibility();
|
||||
});
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+22
-10
@@ -8,7 +8,7 @@
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||
<meta name="description" content="Claude Code session manager with web interface">
|
||||
<meta name="theme-color" content="#0a0a0a">
|
||||
<meta name="theme-color" content="#11151c">
|
||||
<meta name="google" content="notranslate">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
@@ -41,15 +41,17 @@
|
||||
<script defer src="vendor/marked.min.js"></script>
|
||||
<!-- Synchronous mobile detection — runs before first paint to prevent panel flash -->
|
||||
<script>if(window.innerWidth<768||(('ontouchstart' in window||navigator.maxTouchPoints>0)&&window.innerWidth<1024))document.documentElement.classList.add('mobile-init');</script>
|
||||
<!-- Synchronous skin selection — runs before first paint to prevent theme flash -->
|
||||
<script>try{var s=localStorage.getItem('codeman:skin');if(s!=='og'&&s!=='daylight-green'&&s!=='daylight-blue')s='daylight-blue';document.documentElement.dataset.skin=s;window.__codemanSkin=s;}catch(e){document.documentElement.dataset.skin='daylight-blue';window.__codemanSkin='daylight-blue';}</script>
|
||||
<!-- Inline critical CSS for instant skeleton paint (before styles.css loads) -->
|
||||
<style>
|
||||
.loading-skeleton{display:flex;flex-direction:column;height:100vh;height:100dvh;background:#09090b}
|
||||
.skeleton-header{height:40px;background:rgba(19,19,22,0.85);border-bottom:1px solid rgba(255,255,255,0.06);display:flex;align-items:center;padding:0 12px}
|
||||
.skeleton-brand{color:#60a5fa;font-size:14px;font-weight:700;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;opacity:.7}
|
||||
.loading-skeleton{display:flex;flex-direction:column;height:100vh;height:100dvh;background:#11151c}
|
||||
.skeleton-header{height:40px;background:rgba(31,38,48,0.85);border-bottom:1px solid rgba(255,255,255,0.08);display:flex;align-items:center;padding:0 12px}
|
||||
.skeleton-brand{color:#38b6f0;font-size:14px;font-weight:700;font-family:'Manrope',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;opacity:.85}
|
||||
.skeleton-tabs{display:flex;gap:4px;margin-left:16px}
|
||||
.skeleton-tab{width:80px;height:24px;background:rgba(255,255,255,0.04);border-radius:6px}
|
||||
.skeleton-terminal{flex:1;background:#0d0d0d}
|
||||
.skeleton-toolbar{height:42px;background:rgba(19,19,22,0.85);border-top:1px solid rgba(255,255,255,0.06)}
|
||||
.skeleton-terminal{flex:1;background:#161b23}
|
||||
.skeleton-toolbar{height:42px;background:rgba(31,38,48,0.85);border-top:1px solid rgba(255,255,255,0.08)}
|
||||
.app-loaded .loading-skeleton{display:none}
|
||||
</style>
|
||||
</head>
|
||||
@@ -178,8 +180,8 @@
|
||||
<svg viewBox="0 0 36 36" class="ralph-ring-svg">
|
||||
<defs>
|
||||
<linearGradient id="ralphGradientMini" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#3b82f6" />
|
||||
<stop offset="100%" stop-color="#22c55e" />
|
||||
<stop offset="0%" stop-color="#3ec8ee" />
|
||||
<stop offset="100%" stop-color="#2b8fd9" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<circle class="ralph-ring-bg" cx="18" cy="18" r="15.9" />
|
||||
@@ -224,8 +226,8 @@
|
||||
<svg viewBox="0 0 100 100" class="ralph-ring-svg-large">
|
||||
<defs>
|
||||
<linearGradient id="ralphGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#3b82f6" />
|
||||
<stop offset="100%" stop-color="#22c55e" />
|
||||
<stop offset="0%" stop-color="#3ec8ee" />
|
||||
<stop offset="100%" stop-color="#2b8fd9" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<circle class="ralph-ring-track" cx="50" cy="50" r="42" />
|
||||
@@ -918,6 +920,16 @@
|
||||
<!-- Display Tab -->
|
||||
<div class="modal-tab-content" id="settings-display">
|
||||
<div class="settings-grid">
|
||||
<!-- Appearance Section -->
|
||||
<div class="settings-section-header">Appearance</div>
|
||||
<div class="settings-item settings-item-skin" title="Visual theme for this device (not synced)">
|
||||
<span class="settings-item-label">Skin</span>
|
||||
<select id="appSettingsSkin" class="form-select">
|
||||
<option value="daylight-blue">Daylight Blue</option>
|
||||
<option value="daylight-green">Daylight Green</option>
|
||||
<option value="og">OG Codeman</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Input Section -->
|
||||
<div class="settings-section-header">Input</div>
|
||||
<div class="settings-item settings-item-multiline" title="Shows typed characters instantly via overlay while forwarding keystrokes to the server in the background. Enables Tab completion, preserves input across tab switches, and protects against session crashes. Recommended for mobile and high-latency connections.">
|
||||
|
||||
@@ -1550,29 +1550,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
|
||||
const terminal = new Terminal({
|
||||
theme: {
|
||||
background: '#0d0d0d',
|
||||
foreground: '#e0e0e0',
|
||||
cursor: '#e0e0e0',
|
||||
cursorAccent: '#0d0d0d',
|
||||
selection: 'rgba(255, 255, 255, 0.3)',
|
||||
black: '#0d0d0d',
|
||||
red: '#ff6b6b',
|
||||
green: '#51cf66',
|
||||
yellow: '#ffd43b',
|
||||
blue: '#339af0',
|
||||
magenta: '#cc5de8',
|
||||
cyan: '#22b8cf',
|
||||
white: '#e0e0e0',
|
||||
brightBlack: '#495057',
|
||||
brightRed: '#ff8787',
|
||||
brightGreen: '#69db7c',
|
||||
brightYellow: '#ffe066',
|
||||
brightBlue: '#5c7cfa',
|
||||
brightMagenta: '#da77f2',
|
||||
brightCyan: '#66d9e8',
|
||||
brightWhite: '#ffffff',
|
||||
},
|
||||
theme: { ...window.codemanCurrentXtermTheme() },
|
||||
fontFamily: '"Fira Code", "Cascadia Code", "JetBrains Mono", "SF Mono", Monaco, monospace',
|
||||
fontSize: 12,
|
||||
lineHeight: 1.2,
|
||||
|
||||
@@ -309,6 +309,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
document.getElementById('appSettingsShowCost').checked = settings.showCost ?? defaults.showCost ?? false;
|
||||
document.getElementById('appSettingsShowLifecycleLog').checked = settings.showLifecycleLog ?? defaults.showLifecycleLog ?? true;
|
||||
document.getElementById('appSettingsShowResponseViewer').checked = settings.showResponseViewer ?? defaults.showResponseViewer ?? false;
|
||||
document.getElementById('appSettingsSkin').value = settings.skin ?? defaults.skin ?? 'daylight-blue';
|
||||
document.getElementById('appSettingsShowMonitor').checked = settings.showMonitor ?? defaults.showMonitor ?? false;
|
||||
document.getElementById('appSettingsShowProjectInsights').checked = settings.showProjectInsights ?? defaults.showProjectInsights ?? false;
|
||||
document.getElementById('appSettingsShowFileBrowser').checked = settings.showFileBrowser ?? defaults.showFileBrowser ?? false;
|
||||
@@ -1377,6 +1378,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
cjkInputEnabled: document.getElementById('appSettingsCjkInput').checked,
|
||||
extendedKeyboardBar: document.getElementById('appSettingsExtendedKeyboardBar').checked,
|
||||
tabTwoRows: document.getElementById('appSettingsTabTwoRows').checked,
|
||||
skin: document.getElementById('appSettingsSkin').value,
|
||||
// Claude CLI settings
|
||||
claudeMode: document.getElementById('appSettingsClaudeMode').value,
|
||||
allowedTools: document.getElementById('appSettingsAllowedTools').value.trim(),
|
||||
@@ -1491,6 +1493,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
|
||||
// Apply header visibility immediately
|
||||
this.applyHeaderVisibilitySettings();
|
||||
this.applySkin();
|
||||
this.applyTabWrapSettings();
|
||||
this._updateTokensImmediate(); // Re-render token display (picks up showCost change)
|
||||
this.applyMonitorVisibility();
|
||||
@@ -1505,7 +1508,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
|
||||
// Save to server (includes notification prefs for cross-browser persistence)
|
||||
// Strip device-specific keys — localEchoEnabled/cjkInputEnabled are per-platform
|
||||
const { localEchoEnabled: _leo, cjkInputEnabled: _cjk, extendedKeyboardBar: _ekb, ...serverSettings } = settings;
|
||||
const { localEchoEnabled: _leo, cjkInputEnabled: _cjk, extendedKeyboardBar: _ekb, skin: _skin, ...serverSettings } = settings;
|
||||
try {
|
||||
const res = await this._apiPut('/api/settings', {
|
||||
...serverSettings,
|
||||
@@ -1664,6 +1667,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
ralphTrackerEnabled: false,
|
||||
tabTwoRows: false,
|
||||
cjkInputEnabled: false,
|
||||
skin: 'daylight-blue',
|
||||
};
|
||||
}
|
||||
// Desktop defaults - rely on ?? operators in apply functions
|
||||
@@ -1701,6 +1705,24 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
},
|
||||
|
||||
// Apply the chosen skin live: sets the html[data-skin] attribute, syncs BOTH
|
||||
// localStorage locations (the standalone 'codeman:skin' key the pre-paint head
|
||||
// script reads + the app-settings blob field written by saveAppSettingsToStorage),
|
||||
// updates window.__codemanSkin, and re-themes any live terminals.
|
||||
applySkin() {
|
||||
const settings = this.loadAppSettingsFromStorage();
|
||||
const defaults = this.getDefaultSettings();
|
||||
const skin = settings.skin ?? defaults.skin ?? 'daylight-blue';
|
||||
document.documentElement.setAttribute('data-skin', skin);
|
||||
window.__codemanSkin = skin;
|
||||
try {
|
||||
localStorage.setItem('codeman:skin', skin);
|
||||
} catch (_e) {
|
||||
/* private mode */
|
||||
}
|
||||
if (typeof this.applyTerminalSkin === 'function') this.applyTerminalSkin(skin);
|
||||
},
|
||||
|
||||
applyHeaderVisibilitySettings() {
|
||||
const settings = this.loadAppSettingsFromStorage();
|
||||
const defaults = this.getDefaultSettings();
|
||||
@@ -1944,6 +1966,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
'showLifecycleLog', 'showResponseViewer',
|
||||
'showMonitor', 'showProjectInsights', 'showFileBrowser', 'showSubagents',
|
||||
'subagentActiveTabOnly', 'tabTwoRows', 'localEchoEnabled', 'cjkInputEnabled', 'extendedKeyboardBar',
|
||||
'skin',
|
||||
]);
|
||||
// Merge settings: non-display keys always sync from server,
|
||||
// display keys only seed from server when localStorage has no value
|
||||
|
||||
+373
-26
@@ -1,36 +1,65 @@
|
||||
/* Codeman - Terminal-First UI */
|
||||
|
||||
/* Self-hosted UI + mono fonts (Carbon Aurora · Daylight) — served from /fonts,
|
||||
no external CDN, CSP-safe (font-src 'self'). Variable woff2 cover all weights. */
|
||||
@font-face {
|
||||
font-family: 'Manrope';
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
font-weight: 400 800;
|
||||
src: url('fonts/manrope-variable.woff2') format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
font-weight: 400 700;
|
||||
src: url('fonts/jetbrains-mono-variable.woff2') format('woff2');
|
||||
}
|
||||
|
||||
:root {
|
||||
--bg-dark: #09090b;
|
||||
--bg-card: #131316;
|
||||
--bg-input: #1a1a1f;
|
||||
--bg-hover: #1f1f26;
|
||||
--border: #232329;
|
||||
--border-light: #2e2e38;
|
||||
--text: #ececf0;
|
||||
--text-dim: #8b8b97;
|
||||
--text-muted: #52525e;
|
||||
--accent: #3b82f6;
|
||||
--accent-hover: #60a5fa;
|
||||
--green: #22c55e;
|
||||
--yellow: #eab308;
|
||||
--red: #ef4444;
|
||||
/* Carbon Aurora · Daylight — deep-but-not-black slate, surfaces step UP into the light */
|
||||
--bg-dark: #11151c;
|
||||
--bg-card: #1b222c;
|
||||
--bg-input: #202833;
|
||||
--bg-hover: #2a323d;
|
||||
--border: #2b333f;
|
||||
--border-light: #3a4350;
|
||||
--text: #f3f6fa;
|
||||
--text-dim: #98a2b1;
|
||||
--text-muted: #717b8c;
|
||||
/* SINGLE cohesive accent — optimistic emerald → teal (replaces the old blue) */
|
||||
--accent: #38b6f0;
|
||||
--accent-hover: #3ec8ee;
|
||||
/* calmer secondary green for non-primary pills/dots so Run + ring stay focal */
|
||||
--green: #44b993;
|
||||
/* warm, quiet "working" amber (no second saturated hue) */
|
||||
--yellow: #f0c25a;
|
||||
--red: #e06363;
|
||||
/* Daylight accent extras — referenced by the v1.0 override block at end of file */
|
||||
--accent-d: #2487c9;
|
||||
--emerald: #3ec8ee;
|
||||
--teal: #2b8fd9;
|
||||
--accent-soft: #44b993;
|
||||
--accent-ink: #04223a;
|
||||
--warm: #e8c79a;
|
||||
--ring-glow: 0 0 12px -2px rgba(56, 182, 240, 0.55);
|
||||
--header-height: 36px;
|
||||
--toolbar-height: 42px;
|
||||
--glass-bg: rgba(19, 19, 22, 0.85);
|
||||
--glass-border: rgba(255, 255, 255, 0.06);
|
||||
--glass-bg: rgba(31, 38, 48, 0.85);
|
||||
--glass-border: rgba(255, 255, 255, 0.08);
|
||||
--subtle-shadow: 0 -1px 3px rgba(0, 0, 0, 0.3), 0 -4px 16px rgba(0, 0, 0, 0.15);
|
||||
--btn-radius: 6px;
|
||||
--transition-smooth: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
/* Session color palette */
|
||||
--session-red: #ef4444;
|
||||
--session-orange: #f97316;
|
||||
--session-yellow: #eab308;
|
||||
--session-green: #22c55e;
|
||||
--session-blue: #3b82f6;
|
||||
--session-purple: #a855f7;
|
||||
--session-pink: #ec4899;
|
||||
/* Session color palette — retoned toward the Daylight system */
|
||||
--session-red: #e06363;
|
||||
--session-orange: #e8a45c;
|
||||
--session-yellow: #f0c25a;
|
||||
--session-green: #44b993;
|
||||
--session-blue: #2b8fd9;
|
||||
--session-purple: #a98fe0;
|
||||
--session-pink: #e07db0;
|
||||
|
||||
/* Safe area insets for notched devices (iPhone X+, Dynamic Island) */
|
||||
--safe-area-top: env(safe-area-inset-top, 0px);
|
||||
@@ -40,6 +69,78 @@
|
||||
|
||||
/* Touch target minimum (iOS Human Interface Guidelines) */
|
||||
--touch-target-min: 44px;
|
||||
|
||||
/* UI font + accent/gradient/hover tokens referenced by the v1.0 override block.
|
||||
These :root values equal the daylight-blue palette so the no-attribute fallback
|
||||
matches current behavior. */
|
||||
--ui-font: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
--accent-rgb: 56, 182, 240;
|
||||
--accent-grad-a: #3ec8ee;
|
||||
--accent-grad-b: #2b8fd9;
|
||||
--run-hover-a: #5cc4f5;
|
||||
--run-hover-b: #3aa3e2;
|
||||
--gear-hover: #2a93d0;
|
||||
}
|
||||
|
||||
/* ===== Skin: Daylight Blue (current default) ===== */
|
||||
html[data-skin="daylight-blue"] {
|
||||
--bg-dark: #11151c; --bg-card: #1b222c; --bg-input: #202833; --bg-hover: #2a323d;
|
||||
--border: #2b333f; --border-light: #3a4350;
|
||||
--text: #f3f6fa; --text-dim: #98a2b1; --text-muted: #717b8c;
|
||||
--accent: #38b6f0; --accent-hover: #3ec8ee;
|
||||
--term-bg: #161b23;
|
||||
--green: #44b993; --yellow: #f0c25a; --red: #e06363;
|
||||
--glass-bg: rgba(31, 38, 48, 0.85); --glass-border: rgba(255, 255, 255, 0.08);
|
||||
--accent-d: #2487c9; --emerald: #3ec8ee; --teal: #2b8fd9;
|
||||
--accent-soft: #44b993; --accent-ink: #04223a;
|
||||
--accent-rgb: 56, 182, 240;
|
||||
--accent-grad-a: #3ec8ee; --accent-grad-b: #2b8fd9;
|
||||
--run-hover-a: #5cc4f5; --run-hover-b: #3aa3e2; --gear-hover: #2a93d0;
|
||||
--ring-glow: 0 0 12px -2px rgba(56, 182, 240, 0.55);
|
||||
--session-red: #e06363; --session-orange: #e8a45c; --session-yellow: #f0c25a;
|
||||
--session-green: #44b993; --session-blue: #2b8fd9; --session-purple: #a98fe0; --session-pink: #e07db0;
|
||||
--ui-font: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
}
|
||||
|
||||
/* ===== Skin: Daylight Green ===== */
|
||||
html[data-skin="daylight-green"] {
|
||||
--bg-dark: #11151c; --bg-card: #1b222c; --bg-input: #202833; --bg-hover: #2a323d;
|
||||
--border: #2b333f; --border-light: #3a4350;
|
||||
--text: #f3f6fa; --text-dim: #98a2b1; --text-muted: #717b8c;
|
||||
--accent: #2fd3aa; --accent-hover: #34d8a0;
|
||||
--term-bg: #161b23;
|
||||
--green: #44b993; --yellow: #f0c25a; --red: #e06363;
|
||||
--glass-bg: rgba(31, 38, 48, 0.85); --glass-border: rgba(255, 255, 255, 0.08);
|
||||
--accent-d: #19a987; --emerald: #34d8a0; --teal: #2bcbbb;
|
||||
--accent-soft: #44b993; --accent-ink: #062019;
|
||||
--accent-rgb: 47, 211, 170;
|
||||
--accent-grad-a: #34d8a0; --accent-grad-b: #2bcbbb;
|
||||
--run-hover-a: #3ee0aa; --run-hover-b: #34d4c4; --gear-hover: #1cbd97;
|
||||
--ring-glow: 0 0 12px -2px rgba(47, 211, 170, 0.55);
|
||||
--session-red: #e06363; --session-orange: #e8a45c; --session-yellow: #f0c25a;
|
||||
--session-green: #44b993; --session-blue: #2bcbbb; --session-purple: #a98fe0; --session-pink: #e07db0;
|
||||
--ui-font: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
}
|
||||
|
||||
/* ===== Skin: OG Codeman (original look — no Daylight polish, system font) ===== */
|
||||
html[data-skin="og"] {
|
||||
--bg-dark: #09090b; --bg-card: #131316; --bg-input: #1a1a1f; --bg-hover: #1f1f26;
|
||||
--border: #232329; --border-light: #2e2e38;
|
||||
--text: #ececf0; --text-dim: #8b8b97; --text-muted: #52525e;
|
||||
--accent: #3b82f6; --accent-hover: #60a5fa;
|
||||
--term-bg: #0d0d0d;
|
||||
--green: #22c55e; --yellow: #eab308; --red: #ef4444;
|
||||
--glass-bg: rgba(19, 19, 22, 0.85); --glass-border: rgba(255, 255, 255, 0.06);
|
||||
/* daylight-only extras left at sane neutral defaults (unused — override block does not apply to og) */
|
||||
--accent-d: #3b82f6; --emerald: #3b82f6; --teal: #2563eb;
|
||||
--accent-soft: #22c55e; --accent-ink: #0b1220;
|
||||
--accent-rgb: 59, 130, 246;
|
||||
--accent-grad-a: #3b82f6; --accent-grad-b: #2563eb;
|
||||
--run-hover-a: #60a5fa; --run-hover-b: #3b82f6; --gear-hover: #2563eb;
|
||||
--ring-glow: 0 0 12px -2px rgba(59, 130, 246, 0.55);
|
||||
--session-red: #ef4444; --session-orange: #f97316; --session-yellow: #eab308;
|
||||
--session-green: #22c55e; --session-blue: #3b82f6; --session-purple: #a855f7; --session-pink: #ec4899;
|
||||
--ui-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
@@ -129,7 +230,7 @@ textarea:focus-visible {
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
font-family: var(--ui-font);
|
||||
background: var(--bg-dark);
|
||||
color: var(--text);
|
||||
height: 100vh;
|
||||
@@ -2097,7 +2198,7 @@ body.solo-mode .btn-lifecycle-log {
|
||||
|
||||
.terminal-container {
|
||||
flex: 1;
|
||||
background: #0d0d0d;
|
||||
background: var(--term-bg, #161b23);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
/* Performance: isolate layout and paint operations.
|
||||
@@ -3828,6 +3929,22 @@ body.solo-mode .btn-lifecycle-log {
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* Skin picker: full-width row (not squeezed into a half-width grid cell) with an
|
||||
auto-width select, so the "Skin" label keeps its space instead of truncating. */
|
||||
.settings-item-skin {
|
||||
grid-column: 1 / -1;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.settings-item-skin .settings-item-label {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.settings-item-skin .form-select {
|
||||
width: auto;
|
||||
min-width: 220px;
|
||||
max-width: 320px;
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
.settings-item-multiline {
|
||||
align-items: center;
|
||||
}
|
||||
@@ -9057,3 +9174,233 @@ body.touch-device.cjk-input-visible .main {
|
||||
pointer-events: none;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
=== v1.0 Carbon Aurora · Daylight overrides ===
|
||||
Single appended block — trivially removable. Retones the hardcoded
|
||||
component colors that don't flow through :root: the active session
|
||||
tab (#00FF66), the banners' literal gradients, the Ralph Material
|
||||
palette, the Run/welcome/stop gradient buttons, the panels' glass,
|
||||
and the welcome title. Calms heavy glows; the ONLY real saturation/
|
||||
glow is reserved for the Run button + the progress ring.
|
||||
============================================================ */
|
||||
|
||||
html:not([data-skin="og"]) {
|
||||
/* ---- Brand wordmark: emerald, calm (kill the blue glow) ---- */
|
||||
.logo:hover {
|
||||
color: var(--accent-hover);
|
||||
text-shadow: none;
|
||||
filter: brightness(1.06);
|
||||
}
|
||||
|
||||
/* ---- Active session tab: crisp emerald (replaces neon #00FF66) ---- */
|
||||
.session-tab.active {
|
||||
background: rgba(var(--accent-rgb), 0.13) !important;
|
||||
border: 1px solid var(--accent) !important;
|
||||
color: var(--text) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.session-tab:hover {
|
||||
background: rgba(var(--accent-rgb), 0.06);
|
||||
border-color: rgba(var(--accent-rgb), 0.2);
|
||||
box-shadow: none;
|
||||
}
|
||||
.session-tab .tab-number {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
color: var(--text-dim);
|
||||
}
|
||||
.session-tab.active .tab-number {
|
||||
background: rgba(var(--accent-rgb), 0.2) !important;
|
||||
border-color: rgba(var(--accent-rgb), 0.45) !important;
|
||||
color: var(--accent-hover) !important;
|
||||
}
|
||||
/* per-color active tints: calm the glow, keep the identity hue as a quiet border */
|
||||
.session-tab.active[data-color] {
|
||||
box-shadow: none;
|
||||
}
|
||||
/* tab status dots: quiet, no halo */
|
||||
.session-tab .tab-status.idle,
|
||||
.session-tab .tab-status.busy {
|
||||
box-shadow: none;
|
||||
}
|
||||
.tab-glow { animation: none; box-shadow: none; }
|
||||
|
||||
/* ---- Timer banner: emerald-teal, flat ---- */
|
||||
.timer-banner {
|
||||
background: linear-gradient(90deg, rgba(31, 38, 48, 0.92), rgba(22, 27, 35, 0.92));
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
}
|
||||
.timer-value { color: var(--accent); }
|
||||
.timer-progress-fill { background: linear-gradient(90deg, var(--accent-grad-a), var(--accent-grad-b)); }
|
||||
|
||||
/* ---- Respawn banner: lifted slate, calm green hairline ---- */
|
||||
.respawn-banner {
|
||||
background: linear-gradient(90deg, rgba(31, 38, 48, 0.9), rgba(22, 27, 35, 0.9));
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
}
|
||||
.respawn-countdown-timer .respawn-timer-value,
|
||||
.action-type { color: var(--accent); }
|
||||
.respawn-timer-progress { background: var(--accent); }
|
||||
.respawn-action-log { border-left-color: rgba(var(--accent-rgb), 0.18); }
|
||||
.respawn-action-log::-webkit-scrollbar-thumb { background: rgba(var(--accent-rgb), 0.28); }
|
||||
|
||||
/* ---- Ralph panel: replace the blue->green ring gradient + Material badges ---- */
|
||||
.ralph-ring-text {
|
||||
background: linear-gradient(135deg, var(--accent-grad-a), var(--accent-grad-b));
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
}
|
||||
.ralph-ring-bg, .ralph-ring-track { stroke: rgba(255, 255, 255, 0.1); }
|
||||
/* Ralph status badges: collapse the off-token Material palette onto the system */
|
||||
.ralph-status-badge { color: var(--text-dim); }
|
||||
.ralph-status-dot { box-shadow: none; }
|
||||
.ralph-info { background: rgba(var(--accent-rgb), 0.05); }
|
||||
|
||||
/* ---- Bottom toolbar + neutral buttons ---- */
|
||||
.toolbar { box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.22), 0 -4px 14px rgba(0, 0, 0, 0.14); }
|
||||
.btn-toolbar {
|
||||
background: rgba(255, 255, 255, 0.045);
|
||||
border-color: rgba(255, 255, 255, 0.09);
|
||||
color: var(--text-dim);
|
||||
}
|
||||
.btn-toolbar:hover {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-color: rgba(255, 255, 255, 0.14);
|
||||
color: var(--text);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* ---- RUN button: THE focal saturated accent (flat emerald->teal fill) ---- */
|
||||
.btn-toolbar.btn-run,
|
||||
.btn-toolbar.btn-run.mode-claude {
|
||||
background: linear-gradient(135deg, var(--accent-grad-a), var(--accent-grad-b));
|
||||
border-color: var(--accent);
|
||||
color: var(--accent-ink);
|
||||
font-weight: 700;
|
||||
text-shadow: none;
|
||||
}
|
||||
.btn-toolbar.btn-run:hover,
|
||||
.btn-toolbar.btn-run.mode-claude:hover {
|
||||
background: linear-gradient(135deg, var(--run-hover-a), var(--run-hover-b));
|
||||
box-shadow: 0 0 14px -2px rgba(var(--accent-rgb), 0.5);
|
||||
}
|
||||
.btn-toolbar.btn-run.mode-opencode {
|
||||
background: linear-gradient(135deg, var(--accent-d), var(--accent-grad-b));
|
||||
border-color: var(--accent);
|
||||
color: var(--accent-ink);
|
||||
}
|
||||
.btn-toolbar.btn-run.mode-opencode:hover { box-shadow: 0 0 14px -2px rgba(var(--accent-rgb), 0.45); }
|
||||
.btn-toolbar.btn-run.mode-codex {
|
||||
background: linear-gradient(135deg, var(--accent-grad-b), #5cc6e8);
|
||||
border-color: var(--accent-grad-b);
|
||||
color: #061c20;
|
||||
}
|
||||
.btn-toolbar.btn-run.mode-codex:hover { box-shadow: 0 0 14px -2px rgba(43, 203, 187, 0.45); }
|
||||
.btn-toolbar.btn-run-gear {
|
||||
background: var(--accent-d);
|
||||
border-color: var(--accent);
|
||||
color: var(--accent-ink);
|
||||
}
|
||||
.btn-toolbar.btn-run-gear:hover { background: var(--gear-hover); box-shadow: none; }
|
||||
.run-mode-dot.claude { background: var(--accent); }
|
||||
.run-mode-dot.opencode { background: var(--accent-soft); }
|
||||
.run-mode-dot.codex { background: var(--accent-grad-b); }
|
||||
|
||||
/* ---- Shell button: quiet neutral with a calm green tint ---- */
|
||||
.btn-toolbar.btn-shell {
|
||||
background: rgba(68, 185, 147, 0.1);
|
||||
border: 1px solid rgba(68, 185, 147, 0.28);
|
||||
color: var(--green);
|
||||
text-shadow: none;
|
||||
}
|
||||
.btn-toolbar.btn-shell:hover {
|
||||
background: rgba(68, 185, 147, 0.16);
|
||||
border-color: rgba(68, 185, 147, 0.4);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* ---- Stop button: quiet neutral control (no rose glow) ---- */
|
||||
.btn-toolbar.btn-stop {
|
||||
background: rgba(255, 255, 255, 0.045);
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
color: var(--text-dim);
|
||||
text-shadow: none;
|
||||
}
|
||||
.btn-toolbar.btn-stop:hover {
|
||||
background: rgba(224, 99, 99, 0.14);
|
||||
border-color: rgba(224, 99, 99, 0.4);
|
||||
color: #f0a0a0;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn-toolbar.btn-stop.confirming {
|
||||
background: linear-gradient(135deg, #5a4a1a, #6b5620);
|
||||
border-color: rgba(240, 194, 90, 0.6);
|
||||
color: var(--yellow);
|
||||
}
|
||||
|
||||
/* ---- Case selector + add ---- */
|
||||
.toolbar-select {
|
||||
background: rgba(255, 255, 255, 0.045);
|
||||
border-color: rgba(255, 255, 255, 0.09);
|
||||
color: var(--text-dim);
|
||||
}
|
||||
.toolbar-select:hover { background: rgba(255, 255, 255, 0.07); border-color: rgba(255, 255, 255, 0.14); }
|
||||
.toolbar-select:focus {
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 1px rgba(var(--accent-rgb), 0.25);
|
||||
}
|
||||
.btn-case-add { color: var(--accent-soft); }
|
||||
|
||||
/* ---- Header system stats: cohesive accent meters ---- */
|
||||
.stat-bar-mem { background: linear-gradient(90deg, var(--accent), var(--accent-hover)); }
|
||||
.header-tokens.clickable:hover { background: rgba(var(--accent-rgb), 0.16); }
|
||||
|
||||
/* ---- Monitor + Subagents panels: lifted frosted slate ---- */
|
||||
.monitor-panel,
|
||||
.subagents-panel {
|
||||
background: rgba(31, 38, 48, 0.95);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
/* ---- Subagent progress ring: emerald, calm glow ---- */
|
||||
.subagent-ring-progress,
|
||||
.subagent-ring circle.progress { stroke: var(--accent); }
|
||||
|
||||
/* ---- PROGRESS RING (Ralph large): the one place a soft glow lives ---- */
|
||||
.ralph-ring-fill { filter: drop-shadow(0 0 6px rgba(var(--accent-rgb), 0.45)); }
|
||||
|
||||
/* ---- Welcome overlay: emerald title + Daylight buttons ---- */
|
||||
.welcome-title {
|
||||
background: linear-gradient(135deg, var(--accent-grad-a) 0%, var(--accent-grad-b) 50%, var(--accent-grad-a) 100%);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.welcome-btn-claude {
|
||||
background: linear-gradient(135deg, var(--accent-grad-a), var(--accent-grad-b));
|
||||
border-color: var(--accent);
|
||||
color: var(--accent-ink);
|
||||
}
|
||||
.welcome-btn-claude:hover { box-shadow: 0 0 28px -4px rgba(var(--accent-rgb), 0.3); }
|
||||
.welcome-btn-opencode {
|
||||
background: linear-gradient(135deg, var(--accent-d), var(--accent-grad-b));
|
||||
border-color: rgba(var(--accent-rgb), 0.4);
|
||||
color: var(--accent-ink);
|
||||
}
|
||||
.welcome-btn-opencode:hover { box-shadow: 0 0 28px -4px rgba(var(--accent-rgb), 0.25); }
|
||||
.welcome-btn-tunnel {
|
||||
background: linear-gradient(135deg, rgba(46, 55, 67, 0.9), rgba(56, 66, 79, 0.9));
|
||||
border-color: var(--border-light);
|
||||
color: var(--text);
|
||||
}
|
||||
.welcome-btn-tunnel:hover { box-shadow: none; }
|
||||
.welcome-btn-tunnel.active {
|
||||
background: linear-gradient(135deg, var(--accent-d), var(--accent-grad-a));
|
||||
border-color: var(--accent);
|
||||
color: var(--accent-ink);
|
||||
}
|
||||
|
||||
/* ---- Focus rings (driven by --accent, now emerald) stay accessible ---- */
|
||||
:focus-visible { outline-color: var(--accent); }
|
||||
}
|
||||
|
||||
@@ -28,11 +28,27 @@
|
||||
return isTerminalQueryResponse(data);
|
||||
}
|
||||
|
||||
// Per-skin xterm.js palettes. The 'daylight-blue' object equals the legacy hardcoded
|
||||
// theme, so default behavior is unchanged. Shared at module scope and exported on the
|
||||
// global so both terminal-ui.js (main terminal) and panels-ui.js (teammate terminals,
|
||||
// a separate IIFE) can read the current skin's palette.
|
||||
const CODEMAN_XTERM_THEMES = {
|
||||
og: { background: '#0d0d0d', foreground: '#e0e0e0', cursor: '#e0e0e0', cursorAccent: '#0d0d0d', selection: 'rgba(255,255,255,0.3)', black: '#0d0d0d', red: '#ff6b6b', green: '#51cf66', yellow: '#ffd43b', blue: '#339af0', magenta: '#cc5de8', cyan: '#22b8cf', white: '#e0e0e0', brightBlack: '#495057', brightRed: '#ff8787', brightGreen: '#69db7c', brightYellow: '#ffe066', brightBlue: '#5c7cfa', brightMagenta: '#da77f2', brightCyan: '#66d9e8', brightWhite: '#ffffff' },
|
||||
'daylight-green': { background: '#161b23', foreground: '#dfe6ef', cursor: '#2fd3aa', cursorAccent: '#161b23', selection: 'rgba(47,211,170,0.22)', black: '#161b23', red: '#ff8585', green: '#34d8a0', yellow: '#f0c25a', blue: '#5cc6e8', magenta: '#c79af2', cyan: '#2bcbbb', white: '#dfe6ef', brightBlack: '#5b6675', brightRed: '#ffa0a0', brightGreen: '#5fe6b8', brightYellow: '#ffd884', brightBlue: '#82d4ee', brightMagenta: '#d6b3f7', brightCyan: '#5ee0d4', brightWhite: '#f3f6fa' },
|
||||
'daylight-blue': { background: '#161b23', foreground: '#dfe6ef', cursor: '#38b6f0', cursorAccent: '#161b23', selection: 'rgba(56,182,240,0.22)', black: '#161b23', red: '#ff8585', green: '#34d8a0', yellow: '#f0c25a', blue: '#5cc6e8', magenta: '#c79af2', cyan: '#2bcbbb', white: '#dfe6ef', brightBlack: '#5b6675', brightRed: '#ffa0a0', brightGreen: '#5fe6b8', brightYellow: '#ffd884', brightBlue: '#82d4ee', brightMagenta: '#d6b3f7', brightCyan: '#5ee0d4', brightWhite: '#f3f6fa' },
|
||||
};
|
||||
function currentXtermTheme() {
|
||||
const skin = (typeof document !== 'undefined' && document.documentElement.dataset.skin) || 'daylight-blue';
|
||||
return CODEMAN_XTERM_THEMES[skin] || CODEMAN_XTERM_THEMES['daylight-blue'];
|
||||
}
|
||||
|
||||
global.CodemanTerminalInput = {
|
||||
isTerminalQueryResponse,
|
||||
shouldSuppressTerminalQueryResponse,
|
||||
USER_SCROLL_STICKY_SUPPRESS_MS,
|
||||
};
|
||||
global.CODEMAN_XTERM_THEMES = CODEMAN_XTERM_THEMES;
|
||||
global.codemanCurrentXtermTheme = currentXtermTheme;
|
||||
})(window);
|
||||
|
||||
Object.assign(CodemanApp.prototype, {
|
||||
@@ -47,29 +63,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
const scrollback = Number.isFinite(stored) && stored > 0 ? Math.max(stored, DEFAULT_SCROLLBACK) : DEFAULT_SCROLLBACK;
|
||||
|
||||
this.terminal = new Terminal({
|
||||
theme: {
|
||||
background: '#0d0d0d',
|
||||
foreground: '#e0e0e0',
|
||||
cursor: '#e0e0e0',
|
||||
cursorAccent: '#0d0d0d',
|
||||
selection: 'rgba(255, 255, 255, 0.3)',
|
||||
black: '#0d0d0d',
|
||||
red: '#ff6b6b',
|
||||
green: '#51cf66',
|
||||
yellow: '#ffd43b',
|
||||
blue: '#339af0',
|
||||
magenta: '#cc5de8',
|
||||
cyan: '#22b8cf',
|
||||
white: '#e0e0e0',
|
||||
brightBlack: '#495057',
|
||||
brightRed: '#ff8787',
|
||||
brightGreen: '#69db7c',
|
||||
brightYellow: '#ffe066',
|
||||
brightBlue: '#5c7cfa',
|
||||
brightMagenta: '#da77f2',
|
||||
brightCyan: '#66d9e8',
|
||||
brightWhite: '#ffffff',
|
||||
},
|
||||
theme: { ...window.codemanCurrentXtermTheme() },
|
||||
fontFamily: '"Fira Code", "Cascadia Code", "JetBrains Mono", "SF Mono", Monaco, monospace',
|
||||
// Use smaller font on mobile to fit more columns (prevents wrapping of Claude's status line)
|
||||
fontSize: MobileDetection.getDeviceType() === 'mobile' ? 10 : 14,
|
||||
@@ -2182,4 +2176,27 @@ Object.assign(CodemanApp.prototype, {
|
||||
document.getElementById('dirDisplay').textContent = value || 'No directory';
|
||||
}, 100);
|
||||
},
|
||||
|
||||
// Re-theme all live xterm terminals (main + teammate) to the given skin's palette.
|
||||
// Uses the xterm v5+ live setter (full object assignment triggers a repaint for both
|
||||
// DOM and WebGL renderers) plus a belt-and-suspenders refresh().
|
||||
applyTerminalSkin(skin) {
|
||||
const theme = { ...(window.CODEMAN_XTERM_THEMES[skin] || window.CODEMAN_XTERM_THEMES['daylight-blue']) };
|
||||
if (this.terminal) {
|
||||
this.terminal.options.theme = theme;
|
||||
try {
|
||||
this.terminal.refresh(0, this.terminal.rows - 1);
|
||||
} catch {}
|
||||
}
|
||||
if (this.teammateTerminals) {
|
||||
for (const [, entry] of this.teammateTerminals) {
|
||||
if (entry && entry.terminal) {
|
||||
entry.terminal.options.theme = { ...theme };
|
||||
try {
|
||||
entry.terminal.refresh(0, entry.terminal.rows - 1);
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user