Compare commits

...
Author SHA1 Message Date
arkonandClaude Fable 5 0809f59f0f chore: version packages — Codeman 1.0.0
Bumps aicodeman 0.9.14 → 1.0.0 (theme skins + first stable release).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 23:30:34 +02:00
arkonandClaude Fable 5 eda95adaa9 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>
2026-06-13 23:21:47 +02:00
12 changed files with 485 additions and 87 deletions
+19
View File
@@ -1,5 +1,24 @@
# aicodeman # aicodeman
## 1.0.0
### Major Changes
- # Codeman 1.0.0 🎉
The first stable release of Codeman — and it comes with a fresh new look.
**New: theme skins.** Codeman now ships a built-in skin switcher (App Settings → Display → Appearance):
- **OG Codeman** — the original look, preserved exactly.
- **Daylight Green** — a fresh emerald-on-slate theme.
- **Daylight Blue** — bright sky-blue on lifted slate (the new default).
Skins apply instantly, persist per device (with a pre-paint script so there's no flash on load), and re-theme any open terminals live. The system is built on `html[data-skin]` design tokens and self-hosted Manrope (UI) + JetBrains Mono (terminal) fonts — no external CDN, CSP-safe.
**1.0.0 milestone.** This marks the start of the stable 1.x line: the CLI, documented environment variables, and the `{ success, data }` HTTP/SSE API envelope follow semantic versioning (see `docs/versioning-policy.md`).
**Thank you to everyone who helped build Codeman.** This release is dedicated to all of our contributors for their work on the project: Ark0N, Aamer Akhter (@aakhter), Tenggan Zhang (@TeigenZhang), zhouyuan / @sunnyzhouy, jaypark, Marco Migozzi, Skúli Arnlaugsson, Aaron Fields, Loïc Sculier, and Noah Waldner (@noahwaldner). 💙
## 0.9.14 ## 0.9.14
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -56,7 +56,7 @@ When user says "COM":
CI runs `npm run check:lockfile` on every push/PR, so lockfile drift fails the build even if the `version-packages` script is bypassed. CI runs `npm run check:lockfile` on every push/PR, so lockfile drift fails the build even if the `version-packages` script is bypassed.
**Version**: 0.9.14 (must match `package.json`) **Version**: 1.0.0 (must match `package.json`)
## Project Overview ## Project Overview
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "aicodeman", "name": "aicodeman",
"version": "0.9.14", "version": "1.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "aicodeman", "name": "aicodeman",
"version": "0.9.14", "version": "1.0.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"workspaces": [ "workspaces": [
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "aicodeman", "name": "aicodeman",
"version": "0.9.14", "version": "1.0.0",
"description": "Mission control for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence", "description": "Mission control for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",
+2
View File
@@ -580,6 +580,7 @@ class CodemanApp {
const _kbSettings = this.loadAppSettingsFromStorage(); const _kbSettings = this.loadAppSettingsFromStorage();
if (_kbSettings.extendedKeyboardBar) KeyboardAccessoryBar.setMode('extended'); if (_kbSettings.extendedKeyboardBar) KeyboardAccessoryBar.setMode('extended');
this.applyHeaderVisibilitySettings(); this.applyHeaderVisibilitySettings();
this.applySkin();
this.applyTabWrapSettings(); this.applyTabWrapSettings();
this.applyMonitorVisibility(); this.applyMonitorVisibility();
// Remove mobile-init class now that JS has applied visibility settings. // 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) // Load server-stored settings (async, re-applies visibility after load)
this.loadAppSettingsFromServer(settingsPromise).then(() => { this.loadAppSettingsFromServer(settingsPromise).then(() => {
this.applyHeaderVisibilitySettings(); this.applyHeaderVisibilitySettings();
this.applySkin();
this.applyTabWrapSettings(); this.applyTabWrapSettings();
this.applyMonitorVisibility(); this.applyMonitorVisibility();
}); });
Binary file not shown.
Binary file not shown.
+22 -10
View File
@@ -8,7 +8,7 @@
<base href="/"> <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="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="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="google" content="notranslate">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
@@ -41,15 +41,17 @@
<script defer src="vendor/marked.min.js"></script> <script defer src="vendor/marked.min.js"></script>
<!-- Synchronous mobile detection — runs before first paint to prevent panel flash --> <!-- 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> <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) --> <!-- Inline critical CSS for instant skeleton paint (before styles.css loads) -->
<style> <style>
.loading-skeleton{display:flex;flex-direction:column;height:100vh;height:100dvh;background:#09090b} .loading-skeleton{display:flex;flex-direction:column;height:100vh;height:100dvh;background:#11151c}
.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-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:#60a5fa;font-size:14px;font-weight:700;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;opacity:.7} .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-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-tab{width:80px;height:24px;background:rgba(255,255,255,0.04);border-radius:6px}
.skeleton-terminal{flex:1;background:#0d0d0d} .skeleton-terminal{flex:1;background:#161b23}
.skeleton-toolbar{height:42px;background:rgba(19,19,22,0.85);border-top:1px solid rgba(255,255,255,0.06)} .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} .app-loaded .loading-skeleton{display:none}
</style> </style>
</head> </head>
@@ -178,8 +180,8 @@
<svg viewBox="0 0 36 36" class="ralph-ring-svg"> <svg viewBox="0 0 36 36" class="ralph-ring-svg">
<defs> <defs>
<linearGradient id="ralphGradientMini" x1="0%" y1="0%" x2="100%" y2="100%"> <linearGradient id="ralphGradientMini" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#3b82f6" /> <stop offset="0%" stop-color="#3ec8ee" />
<stop offset="100%" stop-color="#22c55e" /> <stop offset="100%" stop-color="#2b8fd9" />
</linearGradient> </linearGradient>
</defs> </defs>
<circle class="ralph-ring-bg" cx="18" cy="18" r="15.9" /> <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"> <svg viewBox="0 0 100 100" class="ralph-ring-svg-large">
<defs> <defs>
<linearGradient id="ralphGradient" x1="0%" y1="0%" x2="100%" y2="100%"> <linearGradient id="ralphGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#3b82f6" /> <stop offset="0%" stop-color="#3ec8ee" />
<stop offset="100%" stop-color="#22c55e" /> <stop offset="100%" stop-color="#2b8fd9" />
</linearGradient> </linearGradient>
</defs> </defs>
<circle class="ralph-ring-track" cx="50" cy="50" r="42" /> <circle class="ralph-ring-track" cx="50" cy="50" r="42" />
@@ -918,6 +920,16 @@
<!-- Display Tab --> <!-- Display Tab -->
<div class="modal-tab-content" id="settings-display"> <div class="modal-tab-content" id="settings-display">
<div class="settings-grid"> <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 --> <!-- Input Section -->
<div class="settings-section-header">Input</div> <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."> <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.">
+1 -23
View File
@@ -1550,29 +1550,7 @@ Object.assign(CodemanApp.prototype, {
} }
const terminal = new Terminal({ const terminal = new Terminal({
theme: { theme: { ...window.codemanCurrentXtermTheme() },
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',
},
fontFamily: '"Fira Code", "Cascadia Code", "JetBrains Mono", "SF Mono", Monaco, monospace', fontFamily: '"Fira Code", "Cascadia Code", "JetBrains Mono", "SF Mono", Monaco, monospace',
fontSize: 12, fontSize: 12,
lineHeight: 1.2, lineHeight: 1.2,
+24 -1
View File
@@ -309,6 +309,7 @@ Object.assign(CodemanApp.prototype, {
document.getElementById('appSettingsShowCost').checked = settings.showCost ?? defaults.showCost ?? false; document.getElementById('appSettingsShowCost').checked = settings.showCost ?? defaults.showCost ?? false;
document.getElementById('appSettingsShowLifecycleLog').checked = settings.showLifecycleLog ?? defaults.showLifecycleLog ?? true; document.getElementById('appSettingsShowLifecycleLog').checked = settings.showLifecycleLog ?? defaults.showLifecycleLog ?? true;
document.getElementById('appSettingsShowResponseViewer').checked = settings.showResponseViewer ?? defaults.showResponseViewer ?? false; 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('appSettingsShowMonitor').checked = settings.showMonitor ?? defaults.showMonitor ?? false;
document.getElementById('appSettingsShowProjectInsights').checked = settings.showProjectInsights ?? defaults.showProjectInsights ?? false; document.getElementById('appSettingsShowProjectInsights').checked = settings.showProjectInsights ?? defaults.showProjectInsights ?? false;
document.getElementById('appSettingsShowFileBrowser').checked = settings.showFileBrowser ?? defaults.showFileBrowser ?? false; document.getElementById('appSettingsShowFileBrowser').checked = settings.showFileBrowser ?? defaults.showFileBrowser ?? false;
@@ -1377,6 +1378,7 @@ Object.assign(CodemanApp.prototype, {
cjkInputEnabled: document.getElementById('appSettingsCjkInput').checked, cjkInputEnabled: document.getElementById('appSettingsCjkInput').checked,
extendedKeyboardBar: document.getElementById('appSettingsExtendedKeyboardBar').checked, extendedKeyboardBar: document.getElementById('appSettingsExtendedKeyboardBar').checked,
tabTwoRows: document.getElementById('appSettingsTabTwoRows').checked, tabTwoRows: document.getElementById('appSettingsTabTwoRows').checked,
skin: document.getElementById('appSettingsSkin').value,
// Claude CLI settings // Claude CLI settings
claudeMode: document.getElementById('appSettingsClaudeMode').value, claudeMode: document.getElementById('appSettingsClaudeMode').value,
allowedTools: document.getElementById('appSettingsAllowedTools').value.trim(), allowedTools: document.getElementById('appSettingsAllowedTools').value.trim(),
@@ -1491,6 +1493,7 @@ Object.assign(CodemanApp.prototype, {
// Apply header visibility immediately // Apply header visibility immediately
this.applyHeaderVisibilitySettings(); this.applyHeaderVisibilitySettings();
this.applySkin();
this.applyTabWrapSettings(); this.applyTabWrapSettings();
this._updateTokensImmediate(); // Re-render token display (picks up showCost change) this._updateTokensImmediate(); // Re-render token display (picks up showCost change)
this.applyMonitorVisibility(); this.applyMonitorVisibility();
@@ -1505,7 +1508,7 @@ Object.assign(CodemanApp.prototype, {
// Save to server (includes notification prefs for cross-browser persistence) // Save to server (includes notification prefs for cross-browser persistence)
// Strip device-specific keys — localEchoEnabled/cjkInputEnabled are per-platform // 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 { try {
const res = await this._apiPut('/api/settings', { const res = await this._apiPut('/api/settings', {
...serverSettings, ...serverSettings,
@@ -1664,6 +1667,7 @@ Object.assign(CodemanApp.prototype, {
ralphTrackerEnabled: false, ralphTrackerEnabled: false,
tabTwoRows: false, tabTwoRows: false,
cjkInputEnabled: false, cjkInputEnabled: false,
skin: 'daylight-blue',
}; };
} }
// Desktop defaults - rely on ?? operators in apply functions // 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() { applyHeaderVisibilitySettings() {
const settings = this.loadAppSettingsFromStorage(); const settings = this.loadAppSettingsFromStorage();
const defaults = this.getDefaultSettings(); const defaults = this.getDefaultSettings();
@@ -1944,6 +1966,7 @@ Object.assign(CodemanApp.prototype, {
'showLifecycleLog', 'showResponseViewer', 'showLifecycleLog', 'showResponseViewer',
'showMonitor', 'showProjectInsights', 'showFileBrowser', 'showSubagents', 'showMonitor', 'showProjectInsights', 'showFileBrowser', 'showSubagents',
'subagentActiveTabOnly', 'tabTwoRows', 'localEchoEnabled', 'cjkInputEnabled', 'extendedKeyboardBar', 'subagentActiveTabOnly', 'tabTwoRows', 'localEchoEnabled', 'cjkInputEnabled', 'extendedKeyboardBar',
'skin',
]); ]);
// Merge settings: non-display keys always sync from server, // Merge settings: non-display keys always sync from server,
// display keys only seed from server when localStorage has no value // display keys only seed from server when localStorage has no value
+373 -26
View File
@@ -1,36 +1,65 @@
/* Codeman - Terminal-First UI */ /* 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 { :root {
--bg-dark: #09090b; /* Carbon Aurora · Daylight — deep-but-not-black slate, surfaces step UP into the light */
--bg-card: #131316; --bg-dark: #11151c;
--bg-input: #1a1a1f; --bg-card: #1b222c;
--bg-hover: #1f1f26; --bg-input: #202833;
--border: #232329; --bg-hover: #2a323d;
--border-light: #2e2e38; --border: #2b333f;
--text: #ececf0; --border-light: #3a4350;
--text-dim: #8b8b97; --text: #f3f6fa;
--text-muted: #52525e; --text-dim: #98a2b1;
--accent: #3b82f6; --text-muted: #717b8c;
--accent-hover: #60a5fa; /* SINGLE cohesive accent — optimistic emerald → teal (replaces the old blue) */
--green: #22c55e; --accent: #38b6f0;
--yellow: #eab308; --accent-hover: #3ec8ee;
--red: #ef4444; /* 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; --header-height: 36px;
--toolbar-height: 42px; --toolbar-height: 42px;
--glass-bg: rgba(19, 19, 22, 0.85); --glass-bg: rgba(31, 38, 48, 0.85);
--glass-border: rgba(255, 255, 255, 0.06); --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); --subtle-shadow: 0 -1px 3px rgba(0, 0, 0, 0.3), 0 -4px 16px rgba(0, 0, 0, 0.15);
--btn-radius: 6px; --btn-radius: 6px;
--transition-smooth: 0.2s cubic-bezier(0.4, 0, 0.2, 1); --transition-smooth: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
/* Session color palette */ /* Session color palette — retoned toward the Daylight system */
--session-red: #ef4444; --session-red: #e06363;
--session-orange: #f97316; --session-orange: #e8a45c;
--session-yellow: #eab308; --session-yellow: #f0c25a;
--session-green: #22c55e; --session-green: #44b993;
--session-blue: #3b82f6; --session-blue: #2b8fd9;
--session-purple: #a855f7; --session-purple: #a98fe0;
--session-pink: #ec4899; --session-pink: #e07db0;
/* Safe area insets for notched devices (iPhone X+, Dynamic Island) */ /* Safe area insets for notched devices (iPhone X+, Dynamic Island) */
--safe-area-top: env(safe-area-inset-top, 0px); --safe-area-top: env(safe-area-inset-top, 0px);
@@ -40,6 +69,78 @@
/* Touch target minimum (iOS Human Interface Guidelines) */ /* Touch target minimum (iOS Human Interface Guidelines) */
--touch-target-min: 44px; --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; } * { box-sizing: border-box; margin: 0; padding: 0; }
@@ -129,7 +230,7 @@ textarea:focus-visible {
} }
body { body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-family: var(--ui-font);
background: var(--bg-dark); background: var(--bg-dark);
color: var(--text); color: var(--text);
height: 100vh; height: 100vh;
@@ -2097,7 +2198,7 @@ body.solo-mode .btn-lifecycle-log {
.terminal-container { .terminal-container {
flex: 1; flex: 1;
background: #0d0d0d; background: var(--term-bg, #161b23);
overflow: hidden; overflow: hidden;
position: relative; position: relative;
/* Performance: isolate layout and paint operations. /* Performance: isolate layout and paint operations.
@@ -3828,6 +3929,22 @@ body.solo-mode .btn-lifecycle-log {
text-overflow: ellipsis; 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 { .settings-item-multiline {
align-items: center; align-items: center;
} }
@@ -9057,3 +9174,233 @@ body.touch-device.cjk-input-visible .main {
pointer-events: none; pointer-events: none;
z-index: 100; 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); }
}
+40 -23
View File
@@ -28,11 +28,27 @@
return isTerminalQueryResponse(data); 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 = { global.CodemanTerminalInput = {
isTerminalQueryResponse, isTerminalQueryResponse,
shouldSuppressTerminalQueryResponse, shouldSuppressTerminalQueryResponse,
USER_SCROLL_STICKY_SUPPRESS_MS, USER_SCROLL_STICKY_SUPPRESS_MS,
}; };
global.CODEMAN_XTERM_THEMES = CODEMAN_XTERM_THEMES;
global.codemanCurrentXtermTheme = currentXtermTheme;
})(window); })(window);
Object.assign(CodemanApp.prototype, { 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; const scrollback = Number.isFinite(stored) && stored > 0 ? Math.max(stored, DEFAULT_SCROLLBACK) : DEFAULT_SCROLLBACK;
this.terminal = new Terminal({ this.terminal = new Terminal({
theme: { theme: { ...window.codemanCurrentXtermTheme() },
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',
},
fontFamily: '"Fira Code", "Cascadia Code", "JetBrains Mono", "SF Mono", Monaco, monospace', 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) // Use smaller font on mobile to fit more columns (prevents wrapping of Claude's status line)
fontSize: MobileDetection.getDeviceType() === 'mobile' ? 10 : 14, fontSize: MobileDetection.getDeviceType() === 'mobile' ? 10 : 14,
@@ -2182,4 +2176,27 @@ Object.assign(CodemanApp.prototype, {
document.getElementById('dirDisplay').textContent = value || 'No directory'; document.getElementById('dirDisplay').textContent = value || 'No directory';
}, 100); }, 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 {}
}
}
}
},
}); });