chore: migrate remaining ui to token utilities
This commit is contained in:
@@ -64,19 +64,17 @@ export default function AgentSelector(props: AgentSelectorProps) {
|
|||||||
itemComponent={(itemProps) => (
|
itemComponent={(itemProps) => (
|
||||||
<Select.Item
|
<Select.Item
|
||||||
item={itemProps.item}
|
item={itemProps.item}
|
||||||
class="px-3 py-2 cursor-pointer rounded outline-none transition-colors hover:bg-gray-100 focus:bg-gray-100 dark:hover:bg-gray-800 dark:focus:bg-gray-800"
|
class="selector-option"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col flex-1 min-w-0">
|
||||||
<Select.ItemLabel class="font-medium text-sm text-gray-900 dark:text-gray-100 flex items-center gap-2">
|
<Select.ItemLabel class="selector-option-label flex items-center gap-2">
|
||||||
<span>{itemProps.item.rawValue.name}</span>
|
<span>{itemProps.item.rawValue.name}</span>
|
||||||
<Show when={itemProps.item.rawValue.mode === "subagent"}>
|
<Show when={itemProps.item.rawValue.mode === "subagent"}>
|
||||||
<span class="neutral-badge">
|
<span class="neutral-badge">subagent</span>
|
||||||
subagent
|
|
||||||
</span>
|
|
||||||
</Show>
|
</Show>
|
||||||
</Select.ItemLabel>
|
</Select.ItemLabel>
|
||||||
<Show when={itemProps.item.rawValue.description}>
|
<Show when={itemProps.item.rawValue.description}>
|
||||||
<Select.ItemDescription class="text-xs text-gray-600 dark:text-gray-300">
|
<Select.ItemDescription class="selector-option-description">
|
||||||
{itemProps.item.rawValue.description.length > 50
|
{itemProps.item.rawValue.description.length > 50
|
||||||
? itemProps.item.rawValue.description.slice(0, 50) + "..."
|
? itemProps.item.rawValue.description.slice(0, 50) + "..."
|
||||||
: itemProps.item.rawValue.description}
|
: itemProps.item.rawValue.description}
|
||||||
@@ -92,11 +90,15 @@ export default function AgentSelector(props: AgentSelectorProps) {
|
|||||||
>
|
>
|
||||||
<Select.Value<Agent>>
|
<Select.Value<Agent>>
|
||||||
{(state) => (
|
{(state) => (
|
||||||
<span class="text-gray-700 dark:text-gray-200">Agent: {state.selectedOption()?.name ?? "None"}</span>
|
<div class="selector-trigger-label">
|
||||||
|
<span class="selector-trigger-primary">
|
||||||
|
Agent: {state.selectedOption()?.name ?? "None"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</Select.Value>
|
</Select.Value>
|
||||||
<Select.Icon>
|
<Select.Icon class="selector-trigger-icon">
|
||||||
<ChevronDown class="w-3 h-3 text-gray-500 dark:text-gray-300" />
|
<ChevronDown class="w-3 h-3" />
|
||||||
</Select.Icon>
|
</Select.Icon>
|
||||||
</Select.Trigger>
|
</Select.Trigger>
|
||||||
|
|
||||||
@@ -106,7 +108,7 @@ export default function AgentSelector(props: AgentSelectorProps) {
|
|||||||
</Select.Content>
|
</Select.Content>
|
||||||
</Select.Portal>
|
</Select.Portal>
|
||||||
</Select>
|
</Select>
|
||||||
<span class="text-xs text-gray-400 dark:text-gray-500">
|
<span class="hint">
|
||||||
<Kbd shortcut="cmd+shift+a" />
|
<Kbd shortcut="cmd+shift+a" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,21 +8,20 @@ interface EmptyStateProps {
|
|||||||
|
|
||||||
const EmptyState: Component<EmptyStateProps> = (props) => {
|
const EmptyState: Component<EmptyStateProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<div class="flex h-full w-full items-center justify-center" style="background-color: var(--surface-secondary);">
|
<div class="flex h-full w-full items-center justify-center bg-surface-secondary">
|
||||||
<div class="max-w-[500px] px-8 py-12 text-center">
|
<div class="max-w-[500px] px-8 py-12 text-center">
|
||||||
<div class="mb-8 flex justify-center">
|
<div class="mb-8 flex justify-center">
|
||||||
<Folder class="h-16 w-16 text-gray-400 dark:text-gray-600" />
|
<Folder class="h-16 w-16 icon-muted" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="mb-4 text-2xl font-semibold text-gray-900 dark:text-gray-100">Welcome to OpenCode Client</h1>
|
<h1 class="mb-4 text-2xl font-semibold text-primary">Welcome to OpenCode Client</h1>
|
||||||
|
|
||||||
<p class="mb-8 text-base text-gray-600 dark:text-gray-400">Select a folder to start coding with AI</p>
|
<p class="mb-8 text-base text-secondary">Select a folder to start coding with AI</p>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={props.onSelectFolder}
|
onClick={props.onSelectFolder}
|
||||||
disabled={props.isLoading}
|
disabled={props.isLoading}
|
||||||
class="mb-4 inline-flex items-center gap-2 rounded-lg px-6 py-3 text-base font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50 focus:outline-none focus:ring-2 focus:ring-offset-2 hover:opacity-90"
|
class="mb-4 button-primary"
|
||||||
style="background-color: var(--accent-primary); color: var(--text-inverted); ring-color: var(--accent-primary); ring-offset-color: var(--surface-base);"
|
|
||||||
>
|
>
|
||||||
{props.isLoading ? (
|
{props.isLoading ? (
|
||||||
<>
|
<>
|
||||||
@@ -34,11 +33,11 @@ const EmptyState: Component<EmptyStateProps> = (props) => {
|
|||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<p class="text-sm text-gray-500 dark:text-gray-500">
|
<p class="text-sm text-muted">
|
||||||
Keyboard shortcut: {navigator.platform.includes("Mac") ? "Cmd" : "Ctrl"}+N
|
Keyboard shortcut: {navigator.platform.includes("Mac") ? "Cmd" : "Ctrl"}+N
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="mt-6 space-y-1 text-sm text-gray-400 dark:text-gray-600">
|
<div class="mt-6 space-y-1 text-sm text-muted">
|
||||||
<p>Examples: ~/projects/my-app</p>
|
<p>Examples: ~/projects/my-app</p>
|
||||||
<p>You can have multiple instances of the same folder</p>
|
<p>You can have multiple instances of the same folder</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -52,9 +52,9 @@ const EnvironmentVariablesEditor: Component<EnvironmentVariablesEditorProps> = (
|
|||||||
return (
|
return (
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<div class="flex items-center gap-2 mb-3">
|
<div class="flex items-center gap-2 mb-3">
|
||||||
<Globe class="w-4 h-4 text-gray-500 dark:text-gray-400" />
|
<Globe class="w-4 h-4 icon-muted" />
|
||||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">Environment Variables</span>
|
<span class="text-sm font-medium text-secondary">Environment Variables</span>
|
||||||
<span class="text-xs text-gray-500 dark:text-gray-400">
|
<span class="text-xs text-muted">
|
||||||
({entries().length} variable{entries().length !== 1 ? "s" : ""})
|
({entries().length} variable{entries().length !== 1 ? "s" : ""})
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -66,12 +66,12 @@ const EnvironmentVariablesEditor: Component<EnvironmentVariablesEditorProps> = (
|
|||||||
{([key, value]) => (
|
{([key, value]) => (
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<div class="flex-1 flex items-center gap-2">
|
<div class="flex-1 flex items-center gap-2">
|
||||||
<Key class="w-3.5 h-3.5 text-gray-400 dark:text-gray-500 flex-shrink-0" />
|
<Key class="w-3.5 h-3.5 icon-muted flex-shrink-0" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={key}
|
value={key}
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
class="flex-1 px-2.5 py-1.5 text-sm bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded text-gray-500 dark:text-gray-400 cursor-not-allowed"
|
class="flex-1 px-2.5 py-1.5 text-sm bg-surface-secondary border border-base rounded text-muted cursor-not-allowed"
|
||||||
placeholder="Variable name"
|
placeholder="Variable name"
|
||||||
title="Variable name (read-only)"
|
title="Variable name (read-only)"
|
||||||
/>
|
/>
|
||||||
@@ -80,14 +80,14 @@ const EnvironmentVariablesEditor: Component<EnvironmentVariablesEditorProps> = (
|
|||||||
value={value}
|
value={value}
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
onInput={(e) => handleUpdateVariable(key, e.currentTarget.value)}
|
onInput={(e) => handleUpdateVariable(key, e.currentTarget.value)}
|
||||||
class="flex-1 px-2.5 py-1.5 text-sm bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed"
|
class="flex-1 px-2.5 py-1.5 text-sm bg-surface-base border border-base rounded text-primary focus-ring-accent disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
placeholder="Variable value"
|
placeholder="Variable value"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleRemoveVariable(key)}
|
onClick={() => handleRemoveVariable(key)}
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
class="p-1.5 text-gray-400 dark:text-gray-500 hover:text-red-600 dark:hover:text-red-400 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
class="p-1.5 icon-muted icon-danger-hover disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||||
title="Remove variable"
|
title="Remove variable"
|
||||||
>
|
>
|
||||||
<Trash2 class="w-3.5 h-3.5" />
|
<Trash2 class="w-3.5 h-3.5" />
|
||||||
@@ -99,16 +99,16 @@ const EnvironmentVariablesEditor: Component<EnvironmentVariablesEditorProps> = (
|
|||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
{/* Add new variable */}
|
{/* Add new variable */}
|
||||||
<div class="flex items-center gap-2 pt-2 border-t border-gray-200 dark:border-gray-600">
|
<div class="flex items-center gap-2 pt-2 border-t border-base">
|
||||||
<div class="flex-1 flex items-center gap-2">
|
<div class="flex-1 flex items-center gap-2">
|
||||||
<Key class="w-3.5 h-3.5 text-gray-400 dark:text-gray-500 flex-shrink-0" />
|
<Key class="w-3.5 h-3.5 icon-muted flex-shrink-0" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={newKey()}
|
value={newKey()}
|
||||||
onInput={(e) => setNewKey(e.currentTarget.value)}
|
onInput={(e) => setNewKey(e.currentTarget.value)}
|
||||||
onKeyPress={handleKeyPress}
|
onKeyPress={handleKeyPress}
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
class="flex-1 px-2.5 py-1.5 text-sm bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed"
|
class="flex-1 px-2.5 py-1.5 text-sm bg-surface-base border border-base rounded text-primary focus-ring-accent disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
placeholder="Variable name"
|
placeholder="Variable name"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
@@ -117,14 +117,14 @@ const EnvironmentVariablesEditor: Component<EnvironmentVariablesEditorProps> = (
|
|||||||
onInput={(e) => setNewValue(e.currentTarget.value)}
|
onInput={(e) => setNewValue(e.currentTarget.value)}
|
||||||
onKeyPress={handleKeyPress}
|
onKeyPress={handleKeyPress}
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
class="flex-1 px-2.5 py-1.5 text-sm bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed"
|
class="flex-1 px-2.5 py-1.5 text-sm bg-surface-base border border-base rounded text-primary focus-ring-accent disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
placeholder="Variable value"
|
placeholder="Variable value"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleAddVariable}
|
onClick={handleAddVariable}
|
||||||
disabled={props.disabled || !newKey().trim()}
|
disabled={props.disabled || !newKey().trim()}
|
||||||
class="p-1.5 text-gray-400 dark:text-gray-500 hover:text-blue-600 dark:hover:text-blue-400 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
class="p-1.5 icon-muted icon-accent-hover disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||||
title="Add variable"
|
title="Add variable"
|
||||||
>
|
>
|
||||||
<Plus class="w-3.5 h-3.5" />
|
<Plus class="w-3.5 h-3.5" />
|
||||||
@@ -132,12 +132,12 @@ const EnvironmentVariablesEditor: Component<EnvironmentVariablesEditorProps> = (
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Show when={entries().length === 0}>
|
<Show when={entries().length === 0}>
|
||||||
<div class="text-xs text-gray-500 dark:text-gray-400 text-center py-2">
|
<div class="text-xs text-muted text-center py-2">
|
||||||
No environment variables configured. Add variables above to customize the OpenCode environment.
|
No environment variables configured. Add variables above to customize the OpenCode environment.
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<div class="text-xs text-gray-500 dark:text-gray-400 mt-2">
|
<div class="text-xs text-muted mt-2">
|
||||||
These variables will be available in the OpenCode environment when starting instances.
|
These variables will be available in the OpenCode environment when starting instances.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ interface HintRowProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const HintRow: Component<HintRowProps> = (props) => {
|
const HintRow: Component<HintRowProps> = (props) => {
|
||||||
return <span class={`text-xs text-gray-500 dark:text-gray-400 ${props.class || ""}`}>{props.children}</span>
|
return <span class={`text-xs text-muted ${props.class || ""}`}>{props.children}</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HintRow
|
export default HintRow
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ const InstanceInfo: Component<InstanceInfoProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div class="panel-body space-y-3">
|
<div class="panel-body space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<div class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-1">Folder</div>
|
<div class="text-xs font-medium text-muted uppercase tracking-wide mb-1">Folder</div>
|
||||||
<div class="text-xs text-gray-900 dark:text-gray-100 font-mono break-all px-2 py-1.5 rounded border" style="background-color: var(--surface-secondary); border-color: var(--border-base); color: var(--text-primary);">
|
<div class="text-xs text-primary font-mono break-all px-2 py-1.5 rounded border bg-surface-secondary border-base">
|
||||||
{props.instance.folder}
|
{props.instance.folder}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -91,22 +91,23 @@ const InstanceInfo: Component<InstanceInfoProps> = (props) => {
|
|||||||
{(project) => (
|
{(project) => (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<div class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-1">
|
<div class="text-xs font-medium text-muted uppercase tracking-wide mb-1">
|
||||||
Project
|
Project
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs font-mono px-2 py-1.5 rounded border truncate" style="background-color: var(--surface-secondary); border-color: var(--border-base); color: var(--text-primary);">
|
<div class="text-xs font-mono px-2 py-1.5 rounded border truncate bg-surface-secondary border-base text-primary">
|
||||||
{project().id}
|
{project().id}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Show when={project().vcs}>
|
<Show when={project().vcs}>
|
||||||
<div>
|
<div>
|
||||||
<div class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-1">
|
<div class="text-xs font-medium text-muted uppercase tracking-wide mb-1">
|
||||||
Version Control
|
Version Control
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2 text-xs text-gray-900 dark:text-gray-100">
|
<div class="flex items-center gap-2 text-xs text-primary">
|
||||||
<svg
|
<svg
|
||||||
class="w-3.5 h-3.5 text-orange-600 dark:text-orange-500"
|
class="w-3.5 h-3.5"
|
||||||
|
style="color: var(--status-warning);"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
@@ -122,10 +123,10 @@ const InstanceInfo: Component<InstanceInfoProps> = (props) => {
|
|||||||
|
|
||||||
<Show when={metadata()?.version}>
|
<Show when={metadata()?.version}>
|
||||||
<div>
|
<div>
|
||||||
<div class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-1">
|
<div class="text-xs font-medium text-muted uppercase tracking-wide mb-1">
|
||||||
OpenCode Version
|
OpenCode Version
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs px-2 py-1.5 rounded border" style="background-color: var(--surface-secondary); border-color: var(--border-base); color: var(--text-primary);">
|
<div class="text-xs px-2 py-1.5 rounded border bg-surface-secondary border-base text-primary">
|
||||||
v{metadata()?.version}
|
v{metadata()?.version}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -133,10 +134,10 @@ const InstanceInfo: Component<InstanceInfoProps> = (props) => {
|
|||||||
|
|
||||||
<Show when={props.instance.binaryPath}>
|
<Show when={props.instance.binaryPath}>
|
||||||
<div>
|
<div>
|
||||||
<div class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-1">
|
<div class="text-xs font-medium text-muted uppercase tracking-wide mb-1">
|
||||||
Binary Path
|
Binary Path
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs font-mono break-all px-2 py-1.5 rounded border" style="background-color: var(--surface-secondary); border-color: var(--border-base); color: var(--text-primary);">
|
<div class="text-xs font-mono break-all px-2 py-1.5 rounded border bg-surface-secondary border-base text-primary">
|
||||||
{props.instance.binaryPath}
|
{props.instance.binaryPath}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -144,17 +145,17 @@ const InstanceInfo: Component<InstanceInfoProps> = (props) => {
|
|||||||
|
|
||||||
<Show when={props.instance.environmentVariables && Object.keys(props.instance.environmentVariables).length > 0}>
|
<Show when={props.instance.environmentVariables && Object.keys(props.instance.environmentVariables).length > 0}>
|
||||||
<div>
|
<div>
|
||||||
<div class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-1.5">
|
<div class="text-xs font-medium text-muted uppercase tracking-wide mb-1.5">
|
||||||
Environment Variables ({Object.keys(props.instance.environmentVariables!).length})
|
Environment Variables ({Object.keys(props.instance.environmentVariables!).length})
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<For each={Object.entries(props.instance.environmentVariables!)}>
|
<For each={Object.entries(props.instance.environmentVariables!)}>
|
||||||
{([key, value]) => (
|
{([key, value]) => (
|
||||||
<div class="flex items-center gap-2 px-2 py-1.5 rounded border" style="background-color: var(--surface-secondary); border-color: var(--border-base);">
|
<div class="flex items-center gap-2 px-2 py-1.5 rounded border bg-surface-secondary border-base">
|
||||||
<span class="text-xs font-mono font-medium flex-1" title={key} style="color: var(--text-primary);">
|
<span class="text-xs font-mono font-medium flex-1 text-primary" title={key}>
|
||||||
{key}
|
{key}
|
||||||
</span>
|
</span>
|
||||||
<span class="text-xs font-mono flex-1" title={value} style="color: var(--text-secondary);">
|
<span class="text-xs font-mono flex-1 text-secondary" title={value}>
|
||||||
{value}
|
{value}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -166,27 +167,23 @@ const InstanceInfo: Component<InstanceInfoProps> = (props) => {
|
|||||||
|
|
||||||
<Show when={!isLoadingMetadata() && mcpServers().length > 0}>
|
<Show when={!isLoadingMetadata() && mcpServers().length > 0}>
|
||||||
<div>
|
<div>
|
||||||
<div class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-1.5">
|
<div class="text-xs font-medium text-muted uppercase tracking-wide mb-1.5">
|
||||||
MCP Servers
|
MCP Servers
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-1.5">
|
<div class="space-y-1.5">
|
||||||
<For each={mcpServers()}>
|
<For each={mcpServers()}>
|
||||||
{(server) => (
|
{(server) => (
|
||||||
<div class="flex items-center justify-between px-2 py-1.5 rounded border" style="background-color: var(--surface-secondary); border-color: var(--border-base);">
|
<div class="flex items-center justify-between px-2 py-1.5 rounded border bg-surface-secondary border-base">
|
||||||
<span class="text-xs text-gray-900 dark:text-gray-100 font-medium truncate">{server.name}</span>
|
<span class="text-xs text-primary font-medium truncate">{server.name}</span>
|
||||||
<div class="flex items-center gap-1.5 flex-shrink-0">
|
<div class="flex items-center gap-1.5 flex-shrink-0">
|
||||||
<Show
|
<Show when={server.status === "running"}>
|
||||||
when={server.status === "running"}
|
<div class="status-dot ready animate-pulse" />
|
||||||
fallback={
|
</Show>
|
||||||
<Show
|
<Show when={server.status === "error"}>
|
||||||
when={server.status === "error"}
|
<div class="status-dot error" />
|
||||||
fallback={<div class="w-1.5 h-1.5 rounded-full bg-gray-400" />}
|
</Show>
|
||||||
>
|
<Show when={server.status === "stopped"}>
|
||||||
<div class="w-1.5 h-1.5 rounded-full bg-red-500" />
|
<div class="status-dot stopped" />
|
||||||
</Show>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div class="w-1.5 h-1.5 rounded-full bg-green-500 animate-pulse" />
|
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -197,9 +194,9 @@ const InstanceInfo: Component<InstanceInfoProps> = (props) => {
|
|||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<Show when={isLoadingMetadata()}>
|
<Show when={isLoadingMetadata()}>
|
||||||
<div class="text-xs text-gray-500 dark:text-gray-400 py-1">
|
<div class="text-xs text-muted py-1">
|
||||||
<div class="flex items-center gap-1.5">
|
<div class="flex items-center gap-1.5">
|
||||||
<svg class="animate-spin h-3 w-3" fill="none" viewBox="0 0 24 24">
|
<svg class="animate-spin h-3 w-3 icon-muted" fill="none" viewBox="0 0 24 24">
|
||||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
|
||||||
<path
|
<path
|
||||||
class="opacity-75"
|
class="opacity-75"
|
||||||
@@ -213,23 +210,23 @@ const InstanceInfo: Component<InstanceInfoProps> = (props) => {
|
|||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-1.5">Server</div>
|
<div class="text-xs font-medium text-muted uppercase tracking-wide mb-1.5">Server</div>
|
||||||
<div class="space-y-1 text-xs">
|
<div class="space-y-1 text-xs">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<span class="text-gray-600 dark:text-gray-400">Port:</span>
|
<span class="text-secondary">Port:</span>
|
||||||
<span class="text-gray-900 dark:text-gray-100 font-mono">{props.instance.port}</span>
|
<span class="text-primary font-mono">{props.instance.port}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<span class="text-gray-600 dark:text-gray-400">PID:</span>
|
<span class="text-secondary">PID:</span>
|
||||||
<span class="text-gray-900 dark:text-gray-100 font-mono">{props.instance.pid}</span>
|
<span class="text-primary font-mono">{props.instance.pid}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<span class="text-gray-600 dark:text-gray-400">Status:</span>
|
<span class="text-secondary">Status:</span>
|
||||||
<span
|
<span
|
||||||
class={`status-badge ${props.instance.status}`}
|
class={`status-badge ${props.instance.status}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class={`status-dot ${props.instance.status} ${props.instance.status === "ready" || props.instance.status === "starting" ? "animate-pulse" : ""}`}
|
class={`status-dot ${props.instance.status === "ready" ? "ready" : props.instance.status === "starting" ? "starting" : props.instance.status === "error" ? "error" : "stopped"} ${props.instance.status === "ready" || props.instance.status === "starting" ? "animate-pulse" : ""}`}
|
||||||
/>
|
/>
|
||||||
{props.instance.status}
|
{props.instance.status}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { Instance } from "../types/instance"
|
|||||||
import { getParentSessions, createSession, setActiveParentSession, agents } from "../stores/sessions"
|
import { getParentSessions, createSession, setActiveParentSession, agents } from "../stores/sessions"
|
||||||
import InstanceInfo from "./instance-info"
|
import InstanceInfo from "./instance-info"
|
||||||
|
|
||||||
|
|
||||||
interface InstanceWelcomeViewProps {
|
interface InstanceWelcomeViewProps {
|
||||||
instance: Instance
|
instance: Instance
|
||||||
}
|
}
|
||||||
@@ -146,14 +147,14 @@ const InstanceWelcomeView: Component<InstanceWelcomeViewProps> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex-1 flex flex-col overflow-hidden bg-gray-50 dark:bg-gray-950">
|
<div class="flex-1 flex flex-col overflow-hidden bg-surface-secondary">
|
||||||
<div class="flex-1 flex flex-col lg:flex-row gap-4 p-4 overflow-auto">
|
<div class="flex-1 flex flex-col lg:flex-row gap-4 p-4 overflow-auto">
|
||||||
<div class="flex-1 flex flex-col gap-4 min-h-0">
|
<div class="flex-1 flex flex-col gap-4 min-h-0">
|
||||||
<Show
|
<Show
|
||||||
when={parentSessions().length > 0}
|
when={parentSessions().length > 0}
|
||||||
fallback={
|
fallback={
|
||||||
<div class="bg-white dark:bg-gray-900 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6 text-center flex-shrink-0">
|
<div class="panel panel-empty-state flex-shrink-0">
|
||||||
<div class="text-gray-400 dark:text-gray-500 mb-2">
|
<div class="panel-empty-state-icon">
|
||||||
<svg class="w-12 h-12 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-12 h-12 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path
|
<path
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
@@ -163,83 +164,81 @@ const InstanceWelcomeView: Component<InstanceWelcomeViewProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-600 dark:text-gray-200 font-medium text-sm">No Previous Sessions</p>
|
<p class="panel-empty-state-title">No Previous Sessions</p>
|
||||||
<p class="text-xs text-gray-500 dark:text-gray-400">Create a new session below to get started</p>
|
<p class="panel-empty-state-description">Create a new session below to get started</p>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div class="bg-white dark:bg-gray-900 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden flex-shrink-0">
|
<div class="panel flex-shrink-0">
|
||||||
<div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900">
|
<div class="panel-header">
|
||||||
<h2 class="text-base font-semibold text-gray-900 dark:text-gray-100">Resume Session</h2>
|
<h2 class="panel-title">Resume Session</h2>
|
||||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
|
<p class="panel-subtitle">
|
||||||
{parentSessions().length} {parentSessions().length === 1 ? "session" : "sessions"} available
|
{parentSessions().length} {parentSessions().length === 1 ? "session" : "sessions"} available
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="max-h-[400px] overflow-y-auto">
|
<div class="panel-list">
|
||||||
<For each={parentSessions()}>
|
<For each={parentSessions()}>
|
||||||
{(session, index) => (
|
{(session, index) => (
|
||||||
<button
|
<div class="panel-list-item">
|
||||||
data-session-index={index()}
|
<button
|
||||||
class="w-full text-left px-4 py-2.5 border-b border-gray-100 dark:border-gray-800 transition-all group focus:outline-none hover:bg-blue-50 dark:hover:bg-blue-900/30"
|
data-session-index={index()}
|
||||||
classList={{
|
class="panel-list-item-content group"
|
||||||
"bg-blue-100 dark:bg-blue-900/50 ring-2 ring-blue-500 dark:ring-blue-400 ring-inset":
|
classList={{
|
||||||
focusMode() === "sessions" && selectedIndex() === index(),
|
"panel-list-item-highlight ring-accent-inset":
|
||||||
}}
|
focusMode() === "sessions" && selectedIndex() === index(),
|
||||||
onClick={() => handleSessionSelect(session.id)}
|
}}
|
||||||
onMouseEnter={() => {
|
onClick={() => handleSessionSelect(session.id)}
|
||||||
setFocusMode("sessions")
|
onMouseEnter={() => {
|
||||||
setSelectedIndex(index())
|
setFocusMode("sessions")
|
||||||
}}
|
setSelectedIndex(index())
|
||||||
>
|
}}
|
||||||
<div class="flex items-center justify-between gap-3">
|
>
|
||||||
<div class="flex-1 min-w-0">
|
<div class="flex items-center justify-between gap-3 w-full">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex-1 min-w-0">
|
||||||
<span
|
<div class="flex items-center gap-2">
|
||||||
class="text-sm font-medium text-gray-900 dark:text-gray-100 group-hover:text-blue-700 dark:group-hover:text-blue-300 truncate"
|
<span
|
||||||
classList={{
|
class="text-sm font-medium text-primary truncate transition-colors"
|
||||||
"text-blue-700 dark:text-blue-300":
|
classList={{
|
||||||
focusMode() === "sessions" && selectedIndex() === index(),
|
"text-accent":
|
||||||
}}
|
focusMode() === "sessions" && selectedIndex() === index(),
|
||||||
>
|
}}
|
||||||
{session.title || "Untitled Session"}
|
>
|
||||||
</span>
|
{session.title || "Untitled Session"}
|
||||||
</div>
|
</span>
|
||||||
<div class="flex items-center gap-3 text-xs text-gray-500 dark:text-gray-400 mt-0.5">
|
</div>
|
||||||
<span>{session.agent}</span>
|
<div class="flex items-center gap-3 text-xs text-muted mt-0.5">
|
||||||
<span>•</span>
|
<span>{session.agent}</span>
|
||||||
<span>{formatRelativeTime(session.time.updated)}</span>
|
<span>•</span>
|
||||||
|
<span>{formatRelativeTime(session.time.updated)}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Show when={focusMode() === "sessions" && selectedIndex() === index()}>
|
||||||
|
<kbd class="kbd flex-shrink-0">↵</kbd>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
<Show when={focusMode() === "sessions" && selectedIndex() === index()}>
|
</button>
|
||||||
<kbd class="kbd flex-shrink-0">
|
</div>
|
||||||
↵
|
|
||||||
</kbd>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<div class="bg-white dark:bg-gray-900 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden flex-shrink-0">
|
<div class="panel flex-shrink-0">
|
||||||
<div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900">
|
<div class="panel-header">
|
||||||
<h2 class="text-base font-semibold text-gray-900 dark:text-gray-100">Start New Session</h2>
|
<h2 class="panel-title">Start New Session</h2>
|
||||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
|
<p class="panel-subtitle">Create a fresh conversation with your chosen agent</p>
|
||||||
Create a fresh conversation with your chosen agent
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="p-4">
|
<div class="panel-body">
|
||||||
<Show
|
<Show
|
||||||
when={agentList().length > 0}
|
when={agentList().length > 0}
|
||||||
fallback={<div class="text-sm text-gray-500 dark:text-gray-400">Loading agents...</div>}
|
fallback={<div class="text-sm text-muted">Loading agents...</div>}
|
||||||
>
|
>
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1.5">Agent</label>
|
<label class="block text-xs font-medium text-secondary mb-1.5">Agent</label>
|
||||||
<select
|
<select
|
||||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-sm bg-white dark:bg-gray-800 dark:text-gray-100 hover:border-gray-400 dark:hover:border-gray-500 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 transition-all"
|
class="selector-input w-full"
|
||||||
value={selectedAgent()}
|
value={selectedAgent()}
|
||||||
onChange={(e) => setSelectedAgent(e.currentTarget.value)}
|
onChange={(e) => setSelectedAgent(e.currentTarget.value)}
|
||||||
>
|
>
|
||||||
@@ -255,14 +254,15 @@ const InstanceWelcomeView: Component<InstanceWelcomeViewProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="w-full px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 dark:hover:bg-blue-500 disabled:bg-gray-300 dark:disabled:bg-gray-600 disabled:cursor-not-allowed transition-all font-medium flex items-center justify-between text-sm relative group focus:outline-none focus:ring-2 focus:ring-blue-500/40"
|
type="button"
|
||||||
|
class="selector-button selector-button-primary w-full flex items-center justify-between gap-2 font-medium"
|
||||||
onClick={handleNewSession}
|
onClick={handleNewSession}
|
||||||
disabled={isCreating() || agentList().length === 0}
|
disabled={isCreating() || agentList().length === 0}
|
||||||
>
|
>
|
||||||
<Show
|
<Show
|
||||||
when={!isCreating()}
|
when={!isCreating()}
|
||||||
fallback={
|
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">
|
<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" />
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
|
||||||
<path
|
<path
|
||||||
@@ -272,18 +272,16 @@ const InstanceWelcomeView: Component<InstanceWelcomeViewProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
Creating...
|
Creating...
|
||||||
</>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-2 flex-1 justify-center">
|
<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">
|
<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" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||||
</svg>
|
</svg>
|
||||||
<span>Create Session</span>
|
<span>Create Session</span>
|
||||||
</div>
|
</div>
|
||||||
<kbd class="kbd flex-shrink-0">
|
<kbd class="kbd flex-shrink-0">Cmd+Enter</kbd>
|
||||||
Cmd+Enter
|
|
||||||
</kbd>
|
|
||||||
</Show>
|
</Show>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -299,8 +297,8 @@ const InstanceWelcomeView: Component<InstanceWelcomeViewProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="px-4 py-2 bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700 flex-shrink-0">
|
<div class="panel-footer">
|
||||||
<div class="flex items-center justify-center flex-wrap gap-3 text-xs text-gray-500 dark:text-gray-400">
|
<div class="panel-footer-hints">
|
||||||
<div class="flex items-center gap-1.5">
|
<div class="flex items-center gap-1.5">
|
||||||
<kbd class="kbd">↑</kbd>
|
<kbd class="kbd">↑</kbd>
|
||||||
<kbd class="kbd">↓</kbd>
|
<kbd class="kbd">↓</kbd>
|
||||||
@@ -331,3 +329,4 @@ const InstanceWelcomeView: Component<InstanceWelcomeViewProps> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default InstanceWelcomeView
|
export default InstanceWelcomeView
|
||||||
|
|
||||||
|
|||||||
@@ -354,10 +354,10 @@ const OpenCodeBinarySelector: Component<OpenCodeBinarySelectorProps> = (props) =
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={(e) => handleRemoveBinary(binary.path, e)}
|
onClick={(e) => handleRemoveBinary(binary.path, e)}
|
||||||
class="opacity-0 group-hover:opacity-100 p-1 hover:bg-red-100 dark:hover:bg-red-900/30 rounded transition-all"
|
class="remove-binary-button"
|
||||||
title="Remove binary"
|
title="Remove binary"
|
||||||
>
|
>
|
||||||
<Trash2 class="w-3.5 h-3.5 text-gray-400 dark:text-gray-500 hover:text-red-600 dark:hover:text-red-400" />
|
<Trash2 class="w-3.5 h-3.5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -64,39 +64,38 @@ const SessionPicker: Component<SessionPickerProps> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<Dialog open={props.open} onOpenChange={(open) => !open && handleCancel()}>
|
<Dialog open={props.open} onOpenChange={(open) => !open && handleCancel()}>
|
||||||
<Dialog.Portal>
|
<Dialog.Portal>
|
||||||
<Dialog.Overlay class="fixed inset-0 bg-black/50 z-50" />
|
<Dialog.Overlay class="modal-overlay" />
|
||||||
<div class="fixed inset-0 z-50 flex items-center justify-center p-4">
|
<div class="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||||
<Dialog.Content class="modal-surface w-full max-w-lg p-6">
|
<Dialog.Content class="modal-surface w-full max-w-lg p-6">
|
||||||
<Dialog.Title class="text-xl font-semibold mb-4" style="color: var(--text-primary);">
|
<Dialog.Title class="text-xl font-semibold text-primary mb-4">
|
||||||
OpenCode • {instance()?.folder.split("/").pop()}
|
OpenCode • {instance()?.folder.split("/").pop()}
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
|
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<Show
|
<Show
|
||||||
when={parentSessions().length > 0}
|
when={parentSessions().length > 0}
|
||||||
fallback={
|
fallback={<div class="text-center py-4 text-sm text-muted">No previous sessions</div>}
|
||||||
<div class="text-center py-4 text-gray-500 dark:text-gray-400 text-sm">No previous sessions</div>
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
<h3 class="text-sm font-medium text-secondary mb-2">
|
||||||
Resume a session ({parentSessions().length}):
|
Resume a session ({parentSessions().length}):
|
||||||
</h3>
|
</h3>
|
||||||
<div class="space-y-1 max-h-[400px] overflow-y-auto">
|
<div class="space-y-1 max-h-[400px] overflow-y-auto">
|
||||||
<For each={parentSessions()}>
|
<For each={parentSessions()}>
|
||||||
{(session) => (
|
{(session) => (
|
||||||
<button
|
<button
|
||||||
class="w-full text-left px-3 py-2 rounded transition-colors group hover:bg-gray-100 dark:hover:bg-gray-800"
|
type="button"
|
||||||
|
class="selector-option w-full text-left"
|
||||||
onClick={() => handleSessionSelect(session.id)}
|
onClick={() => handleSessionSelect(session.id)}
|
||||||
>
|
>
|
||||||
<div class="flex justify-between items-start">
|
<div class="selector-option-content">
|
||||||
<span class="text-sm text-gray-900 dark:text-gray-100 truncate flex-1">
|
<span class="selector-option-label truncate">
|
||||||
{session.title || "Untitled"}
|
{session.title || "Untitled"}
|
||||||
</span>
|
</span>
|
||||||
<span class="text-xs text-gray-500 dark:text-gray-400 ml-2 flex-shrink-0">
|
|
||||||
{formatRelativeTime(session.time.updated)}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
<span class="selector-badge-time flex-shrink-0">
|
||||||
|
{formatRelativeTime(session.time.updated)}
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
@@ -106,22 +105,22 @@ const SessionPicker: Component<SessionPickerProps> = (props) => {
|
|||||||
|
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div class="absolute inset-0 flex items-center">
|
<div class="absolute inset-0 flex items-center">
|
||||||
<div class="w-full border-t border-gray-300 dark:border-gray-700" />
|
<div class="w-full border-t border-base" />
|
||||||
</div>
|
</div>
|
||||||
<div class="relative flex justify-center text-sm">
|
<div class="relative flex justify-center text-sm">
|
||||||
<span class="px-2" style="background-color: var(--surface-base); color: var(--text-muted);">or</span>
|
<span class="px-2 bg-surface-base text-muted">or</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-sm font-medium mb-2" style="color: var(--text-secondary);">Start new session:</h3>
|
<h3 class="text-sm font-medium text-secondary mb-2">Start new session:</h3>
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<Show
|
<Show
|
||||||
when={agentList().length > 0}
|
when={agentList().length > 0}
|
||||||
fallback={<div class="text-sm text-gray-500 dark:text-gray-400">Loading agents...</div>}
|
fallback={<div class="text-sm text-muted">Loading agents...</div>}
|
||||||
>
|
>
|
||||||
<select
|
<select
|
||||||
class="selector-input"
|
class="selector-input w-full"
|
||||||
value={selectedAgent()}
|
value={selectedAgent()}
|
||||||
onChange={(e) => setSelectedAgent(e.currentTarget.value)}
|
onChange={(e) => setSelectedAgent(e.currentTarget.value)}
|
||||||
>
|
>
|
||||||
@@ -130,7 +129,7 @@ const SessionPicker: Component<SessionPickerProps> = (props) => {
|
|||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="selector-button-primary w-full"
|
class="selector-button selector-button-primary w-full"
|
||||||
onClick={handleNewSession}
|
onClick={handleNewSession}
|
||||||
disabled={isCreating() || agentList().length === 0}
|
disabled={isCreating() || agentList().length === 0}
|
||||||
>
|
>
|
||||||
@@ -142,7 +141,8 @@ const SessionPicker: Component<SessionPickerProps> = (props) => {
|
|||||||
|
|
||||||
<div class="mt-6 flex justify-end">
|
<div class="mt-6 flex justify-end">
|
||||||
<button
|
<button
|
||||||
class="px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 transition-colors"
|
type="button"
|
||||||
|
class="selector-button selector-button-secondary"
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
|
|||||||
@@ -1,6 +1,83 @@
|
|||||||
/* Reusable component utilities using tokens */
|
/* Reusable component utilities using tokens */
|
||||||
|
|
||||||
|
/* Base token utility helpers */
|
||||||
|
.text-primary {
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-secondary {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-muted {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-inverted {
|
||||||
|
color: var(--text-inverted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-accent {
|
||||||
|
color: var(--accent-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-surface-base {
|
||||||
|
background-color: var(--surface-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-surface-secondary {
|
||||||
|
background-color: var(--surface-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-surface-muted {
|
||||||
|
background-color: var(--surface-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-base {
|
||||||
|
border-color: var(--border-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-muted {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-accent {
|
||||||
|
color: var(--accent-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-danger-hover:hover {
|
||||||
|
color: var(--status-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-accent-hover:hover {
|
||||||
|
color: var(--accent-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ring-accent-inset {
|
||||||
|
box-shadow: inset 0 0 0 2px var(--accent-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-primary {
|
||||||
|
@apply inline-flex items-center justify-center gap-2 rounded-lg px-6 py-3 text-base font-medium transition-colors;
|
||||||
|
background-color: var(--accent-primary);
|
||||||
|
color: var(--text-inverted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-primary:hover:not(:disabled) {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-primary:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 2px var(--surface-base), 0 0 0 4px var(--accent-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-primary:disabled {
|
||||||
|
@apply cursor-not-allowed opacity-50;
|
||||||
|
}
|
||||||
|
|
||||||
/* Message item base styles */
|
/* Message item base styles */
|
||||||
|
|
||||||
.message-item-base {
|
.message-item-base {
|
||||||
@apply flex flex-col gap-2 p-3 rounded-lg w-full;
|
@apply flex flex-col gap-2 p-3 rounded-lg w-full;
|
||||||
}
|
}
|
||||||
@@ -960,18 +1037,28 @@
|
|||||||
background-color: var(--surface-hover);
|
background-color: var(--surface-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
.selector-option-highlighted {
|
.selector-option[data-highlighted],
|
||||||
|
.selector-option[data-focused] {
|
||||||
background-color: rgba(0, 102, 255, 0.1);
|
background-color: rgba(0, 102, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] .selector-option-highlighted {
|
[data-theme="dark"] .selector-option[data-highlighted],
|
||||||
|
[data-theme="dark"] .selector-option[data-focused] {
|
||||||
background-color: rgba(0, 128, 255, 0.2);
|
background-color: rgba(0, 128, 255, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.selector-option[data-selected] {
|
||||||
|
background-color: rgba(0, 102, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
.selector-option-selected {
|
.selector-option-selected {
|
||||||
background-color: rgba(0, 102, 255, 0.15);
|
background-color: rgba(0, 102, 255, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .selector-option[data-selected] {
|
||||||
|
background-color: rgba(0, 128, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
[data-theme="dark"] .selector-option-selected {
|
[data-theme="dark"] .selector-option-selected {
|
||||||
background-color: rgba(0, 128, 255, 0.25);
|
background-color: rgba(0, 128, 255, 0.25);
|
||||||
}
|
}
|
||||||
@@ -990,6 +1077,28 @@
|
|||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.selector-option .remove-binary-button {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-option:hover .remove-binary-button {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-binary-button {
|
||||||
|
@apply p-1 rounded transition-all;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-binary-button:hover {
|
||||||
|
background-color: rgba(239, 68, 68, 0.1);
|
||||||
|
color: var(--status-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .remove-binary-button:hover {
|
||||||
|
background-color: rgba(239, 68, 68, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
.selector-option-indicator {
|
.selector-option-indicator {
|
||||||
@apply flex-shrink-0 mt-0.5;
|
@apply flex-shrink-0 mt-0.5;
|
||||||
color: var(--accent-primary);
|
color: var(--accent-primary);
|
||||||
@@ -1106,6 +1215,12 @@
|
|||||||
@apply opacity-50 cursor-not-allowed;
|
@apply opacity-50 cursor-not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.focus-ring-accent:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: transparent;
|
||||||
|
box-shadow: 0 0 0 2px var(--accent-primary);
|
||||||
|
}
|
||||||
|
|
||||||
.selector-empty-state {
|
.selector-empty-state {
|
||||||
@apply p-4 text-center text-sm;
|
@apply p-4 text-center text-sm;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
|
|||||||
Reference in New Issue
Block a user