fix: align new-session styling and improve dark theme startup

This commit is contained in:
Shantur Rathore
2025-10-29 00:44:51 +00:00
parent 317d076117
commit 30992fbf48
6 changed files with 205 additions and 48 deletions

View File

@@ -1,4 +1,4 @@
import { app, BrowserWindow, dialog, ipcMain } from "electron"
import { app, BrowserWindow, dialog, ipcMain, nativeTheme } from "electron"
import { join } from "path"
import { createApplicationMenu } from "./menu"
import { setupInstanceIPC } from "./ipc"
@@ -10,11 +10,15 @@ setupStorageIPC()
let mainWindow: BrowserWindow | null = null
function createWindow() {
const prefersDark = nativeTheme.shouldUseDarkColors
const backgroundColor = prefersDark ? "#1a1a1a" : "#ffffff"
mainWindow = new BrowserWindow({
width: 1400,
height: 900,
minWidth: 800,
minHeight: 600,
backgroundColor,
webPreferences: {
preload: join(__dirname, "../preload/index.js"),
contextIsolation: true,

View File

@@ -231,11 +231,8 @@ const InstanceWelcomeView: Component<InstanceWelcomeViewProps> = (props) => {
<p class="panel-subtitle">Create a fresh conversation with your chosen agent</p>
</div>
<div class="panel-body">
<Show
when={agentList().length > 0}
fallback={<div class="text-sm text-muted">Loading agents...</div>}
>
<div class="space-y-3">
<div class="space-y-3">
<Show when={agentList().length > 0}>
<div>
<label class="block text-xs font-medium text-secondary mb-1.5">Agent</label>
<select
@@ -253,40 +250,36 @@ const InstanceWelcomeView: Component<InstanceWelcomeViewProps> = (props) => {
</For>
</select>
</div>
</Show>
<button
type="button"
class="selector-button selector-button-primary w-full flex items-center justify-between gap-2 font-medium"
onClick={handleNewSession}
disabled={isCreating() || agentList().length === 0}
>
<Show
when={!isCreating()}
fallback={
<div class="flex items-center gap-2 w-full justify-center text-sm">
<svg class="animate-spin h-4 w-4" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
Creating...
</div>
}
>
<div class="flex items-center gap-2 flex-1 justify-center text-sm">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
<span>Create Session</span>
</div>
<kbd class="kbd flex-shrink-0">Cmd+Enter</kbd>
</Show>
</button>
</div>
</Show>
<button
type="button"
class="button-primary w-full flex items-center justify-center text-sm disabled:cursor-not-allowed"
onClick={handleNewSession}
disabled={isCreating() || agentList().length === 0}
>
<div class="flex items-center gap-2">
{isCreating() ? (
<svg class="animate-spin h-4 w-4" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
) : (
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
)}
<span>{agentList().length === 0 ? "Loading agents..." : "Create Session"}</span>
</div>
<kbd class="kbd ml-2">
Cmd+Enter
</kbd>
</button>
</div>
</div>
</div>
</div>

View File

@@ -129,11 +129,38 @@ const SessionPicker: Component<SessionPickerProps> = (props) => {
</Show>
<button
class="selector-button selector-button-primary w-full"
class="button-primary w-full flex items-center justify-center text-sm disabled:cursor-not-allowed"
onClick={handleNewSession}
disabled={isCreating() || agentList().length === 0}
>
{isCreating() ? "Creating..." : "Start"}
<div class="flex items-center gap-2">
<Show
when={!isCreating()}
fallback={
<svg class="animate-spin h-4 w-4" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
}
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
</Show>
<Show
when={!isCreating()}
fallback={<span>Creating...</span>}
>
<span>{agentList().length === 0 ? "Loading agents..." : "Create Session"}</span>
</Show>
</div>
<kbd class="kbd ml-2">
Cmd+Enter
</kbd>
</button>
</div>
</div>

View File

@@ -4,6 +4,37 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OpenCode Client</title>
<style>
:root {
color-scheme: light dark;
}
html,
body {
background-color: #ffffff;
color: #1a1a1a;
}
@media (prefers-color-scheme: dark) {
html,
body {
background-color: #1a1a1a;
color: #e0e0e0;
}
}
</style>
<script>
;(function () {
try {
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
if (prefersDark) {
document.documentElement.setAttribute('data-theme', 'dark')
} else {
document.documentElement.removeAttribute('data-theme')
}
} catch (error) {
console.warn('Failed to apply initial theme', error)
}
})()
</script>
</head>
<body>
<div id="root"></div>

View File

@@ -76,6 +76,22 @@
@apply cursor-not-allowed opacity-50;
}
/* Ensure Tailwind base reset on [type="button"] doesn't remove accent styles */
button.button-primary {
background-color: var(--accent-primary);
color: var(--text-inverted);
}
[data-theme="dark"] button.button-primary {
background-color: #3f3f46;
color: #f5f6f8;
}
[data-theme="dark"] button.button-primary:hover:not(:disabled) {
background-color: #52525b;
opacity: 1;
}
.button-secondary {
@apply inline-flex items-center justify-center gap-2 rounded-lg px-6 py-3 text-base font-medium transition-colors;
background-color: var(--surface-secondary);
@@ -378,12 +394,23 @@
background-color: var(--surface-hover);
}
[data-theme="dark"] .tab-active {
background-color: #3f3f46;
color: #f5f6f8;
}
[data-theme="dark"] .tab-active:hover {
background-color: #52525b;
}
[data-theme="dark"] .tab-inactive {
background-color: var(--surface-muted);
background-color: #2a2a31;
color: #d4d4d8;
}
[data-theme="dark"] .tab-inactive:hover {
background-color: var(--surface-hover);
background-color: #3f3f46;
color: #f5f6f8;
}
.tab-label {
@@ -410,6 +437,15 @@
background-color: var(--surface-hover);
}
[data-theme="dark"] .new-tab-button {
background-color: #3f3f46;
color: #f5f6f8;
}
[data-theme="dark"] .new-tab-button:hover {
background-color: #52525b;
}
.new-tab-button:focus-visible {
@apply ring-2 ring-offset-1;
ring-color: var(--accent-primary);
@@ -1201,12 +1237,16 @@
}
.selector-button {
@apply px-3 py-1.5 text-sm rounded transition-colors cursor-pointer;
@apply px-3 py-1.5 text-sm rounded transition-colors cursor-pointer w-full inline-flex items-center justify-center font-medium;
background-color: var(--surface-secondary);
color: var(--text-primary);
border: 1px solid var(--border-base);
}
.selector-button-primary {
background-color: var(--accent-primary);
color: var(--text-inverted);
background-color: var(--accent-primary) !important;
color: var(--text-inverted) !important;
border: 1px solid var(--accent-primary) !important;
}
.selector-button-primary:hover:not(:disabled) {
@@ -1374,7 +1414,7 @@
}
.panel-section-header {
@apply w-full px-4 py-3 flex items-center justify-between transition-colors cursor-pointer;
@apply w-full px-4 py-3 flex items-center justify-center transition-colors cursor-pointer gap-2;
background-color: var(--surface-secondary);
}

View File

@@ -58,6 +58,68 @@
--line-height-relaxed: 1.6;
}
@media (prefers-color-scheme: dark) {
:root {
/* Surface tokens */
--surface-base: #1a1a1a;
--surface-secondary: #2a2a2a;
--surface-muted: #212529;
--surface-code: #1a1a1a;
--surface-hover: #3a3a3a;
/* Border tokens */
--border-base: #3a3a3a;
--border-secondary: #3a3a3a;
--border-muted: #3a3a3a;
/* Text tokens */
--text-primary: #e0e0e0;
--text-secondary: #999999;
--text-muted: #999999;
--text-inverted: #1a1a1a;
/* Accent tokens */
--accent-primary: #0080ff;
--accent-hover: #0066cc;
/* Status tokens */
--status-success: #4caf50;
--status-error: #f44336;
--status-warning: #ff9800;
/* Message-specific tokens */
--message-user-bg: #1a2332;
--message-user-border: #42a5f5;
--message-assistant-bg: #251a2e;
--message-assistant-border: #ba68c8;
--message-tool-bg: #212529;
--message-tool-border: #adb5bd;
/* Typography tokens (same as light theme) */
--font-family-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
--font-family-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
/* Font weights */
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
/* Font sizes */
--font-size-xs: 11px;
--font-size-sm: 12px;
--font-size-base: 14px;
--font-size-lg: 16px;
--font-size-xl: 18px;
--font-size-2xl: 20px;
/* Line heights */
--line-height-tight: 1.25;
--line-height-normal: 1.5;
--line-height-relaxed: 1.6;
}
}
[data-theme="dark"] {
/* Surface tokens */
--surface-base: #1a1a1a;
@@ -116,4 +178,4 @@
--line-height-tight: 1.25;
--line-height-normal: 1.5;
--line-height-relaxed: 1.6;
}
}