refactor: restyle pickers via tokens

This commit is contained in:
Shantur Rathore
2025-10-28 20:00:40 +00:00
parent 7267baf23d
commit 869b704a96
3 changed files with 131 additions and 33 deletions

View File

@@ -158,42 +158,43 @@ const FilePicker: Component<FilePickerProps> = (props) => {
<Show when={props.open}> <Show when={props.open}>
<div <div
ref={containerRef} ref={containerRef}
class="absolute bottom-full left-0 mb-2 w-full max-w-2xl rounded-lg border border-gray-300 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-900" class="dropdown-surface bottom-full left-0 mb-2 max-w-2xl rounded-lg"
style={{ "z-index": 100 }} style={{ "z-index": 100 }}
> >
<div ref={scrollContainerRef} class="max-h-96 overflow-y-auto"> <div ref={scrollContainerRef} class="dropdown-content max-h-96">
<Show <Show
when={!loading() && isInitialized()} when={!loading() && isInitialized()}
fallback={ fallback={
<div class="p-4 text-center text-sm text-gray-500"> <div class="dropdown-loading">
<div class="inline-block h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-blue-600"></div> <div class="spinner inline-block h-4 w-4 mr-2"></div>
<span class="ml-2">Loading files...</span> <span>Loading files...</span>
</div> </div>
} }
> >
<Show <Show
when={files().length > 0} when={files().length > 0}
fallback={<div class="p-4 text-center text-sm text-gray-500">No matching files</div>} fallback={<div class="dropdown-empty">No matching files</div>}
> >
<For each={files()}> <For each={files()}>
{(file, index) => ( {(file, index) => (
<div <div
data-file-selected={index() === selectedIndex()} data-file-selected={index() === selectedIndex()}
class={`cursor-pointer border-b border-gray-100 px-4 py-2 hover:bg-gray-50 dark:border-gray-800 dark:hover:bg-gray-800 ${ class={`dropdown-item border-b px-4 py-2 font-mono text-sm ${
index() === selectedIndex() ? "bg-blue-50 dark:bg-blue-900/20" : "" index() === selectedIndex() ? "dropdown-item-highlight" : ""
}`} }`}
style="border-color: var(--border-muted)"
onClick={() => handleSelect(file.path)} onClick={() => handleSelect(file.path)}
onMouseEnter={() => setSelectedIndex(index())} onMouseEnter={() => setSelectedIndex(index())}
> >
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span class="font-mono text-sm text-gray-900 dark:text-gray-100">{file.path}</span> <span>{file.path}</span>
<Show when={file.isGitFile && (file.added || file.removed)}> <Show when={file.isGitFile && (file.added || file.removed)}>
<div class="flex gap-2 text-xs"> <div class="flex gap-2">
<Show when={file.added}> <Show when={file.added}>
<span class="text-green-600 dark:text-green-400">+{file.added}</span> <span class="dropdown-diff-added">+{file.added}</span>
</Show> </Show>
<Show when={file.removed}> <Show when={file.removed}>
<span class="text-red-600 dark:text-red-400">-{file.removed}</span> <span class="dropdown-diff-removed">-{file.removed}</span>
</Show> </Show>
</div> </div>
</Show> </Show>
@@ -205,7 +206,7 @@ const FilePicker: Component<FilePickerProps> = (props) => {
</Show> </Show>
</div> </div>
<div class="border-t border-gray-200 p-2 text-xs text-gray-500 dark:border-gray-700"> <div class="dropdown-footer p-2">
<div class="flex items-center justify-between px-2"> <div class="flex items-center justify-between px-2">
<span> Navigate Enter Select Esc Close</span> <span> Navigate Enter Select Esc Close</span>
</div> </div>

View File

@@ -157,10 +157,10 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
<Show when={props.open}> <Show when={props.open}>
<div <div
ref={containerRef} ref={containerRef}
class="absolute bottom-full left-0 mb-1 w-full max-w-md rounded-md border border-gray-300 bg-white shadow-lg dark:border-gray-600 dark:bg-gray-800 z-50" class="dropdown-surface bottom-full left-0 mb-1 max-w-md"
> >
<div class="border-b border-gray-200 px-3 py-2 dark:border-gray-700"> <div class="dropdown-header">
<div class="text-xs font-medium text-gray-500 dark:text-gray-400"> <div class="dropdown-header-title">
Select Agent or File Select Agent or File
<Show when={loading()}> <Show when={loading()}>
<span class="ml-2">Loading...</span> <span class="ml-2">Loading...</span>
@@ -168,13 +168,13 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
</div> </div>
</div> </div>
<div ref={scrollContainerRef} class="max-h-60 overflow-y-auto"> <div ref={scrollContainerRef} class="dropdown-content max-h-60">
<Show when={agentCount() === 0 && fileCount() === 0}> <Show when={agentCount() === 0 && fileCount() === 0}>
<div class="px-3 py-4 text-center text-sm text-gray-500 dark:text-gray-400">No results found</div> <div class="dropdown-empty">No results found</div>
</Show> </Show>
<Show when={agentCount() > 0}> <Show when={agentCount() > 0}>
<div class="px-3 py-1.5 text-xs font-semibold text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-900/50"> <div class="dropdown-section-header">
AGENTS AGENTS
</div> </div>
<For each={filteredAgents()}> <For each={filteredAgents()}>
@@ -184,15 +184,15 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
) )
return ( return (
<div <div
class={`cursor-pointer px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 ${ class={`dropdown-item ${
itemIndex === selectedIndex() ? "bg-blue-50 dark:bg-blue-900/20" : "" itemIndex === selectedIndex() ? "dropdown-item-highlight" : ""
}`} }`}
data-picker-selected={itemIndex === selectedIndex()} data-picker-selected={itemIndex === selectedIndex()}
onClick={() => handleSelect({ type: "agent", agent })} onClick={() => handleSelect({ type: "agent", agent })}
> >
<div class="flex items-start gap-2"> <div class="flex items-start gap-2">
<svg <svg
class="h-4 w-4 mt-0.5 text-blue-600 dark:text-blue-400" class="dropdown-icon-accent h-4 w-4 mt-0.5"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke="currentColor" stroke="currentColor"
@@ -206,15 +206,15 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
</svg> </svg>
<div class="flex-1"> <div class="flex-1">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">{agent.name}</span> <span class="text-sm font-medium">{agent.name}</span>
<Show when={agent.mode === "subagent"}> <Show when={agent.mode === "subagent"}>
<span class="rounded bg-blue-50 px-1.5 py-0.5 text-xs font-normal text-blue-600 dark:bg-blue-500/20 dark:text-blue-400"> <span class="dropdown-badge">
subagent subagent
</span> </span>
</Show> </Show>
</div> </div>
<Show when={agent.description}> <Show when={agent.description}>
<div class="mt-0.5 text-xs text-gray-600 dark:text-gray-400"> <div class="mt-0.5 text-xs" style="color: var(--text-muted)">
{agent.description && agent.description.length > 80 {agent.description && agent.description.length > 80
? agent.description.slice(0, 80) + "..." ? agent.description.slice(0, 80) + "..."
: agent.description} : agent.description}
@@ -229,7 +229,7 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
</Show> </Show>
<Show when={fileCount() > 0}> <Show when={fileCount() > 0}>
<div class="px-3 py-1.5 text-xs font-semibold text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-900/50"> <div class="dropdown-section-header">
FILES FILES
</div> </div>
<For each={files()}> <For each={files()}>
@@ -238,8 +238,8 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
const isFolder = file.path.endsWith("/") const isFolder = file.path.endsWith("/")
return ( return (
<div <div
class={`cursor-pointer px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 ${ class={`dropdown-item py-1.5 ${
itemIndex === selectedIndex() ? "bg-blue-50 dark:bg-blue-900/20" : "" itemIndex === selectedIndex() ? "dropdown-item-highlight" : ""
}`} }`}
data-picker-selected={itemIndex === selectedIndex()} data-picker-selected={itemIndex === selectedIndex()}
onClick={() => handleSelect({ type: "file", file })} onClick={() => handleSelect({ type: "file", file })}
@@ -248,7 +248,7 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
<Show <Show
when={isFolder} when={isFolder}
fallback={ fallback={
<svg class="h-4 w-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg class="dropdown-icon h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path <path
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
@@ -258,7 +258,7 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
</svg> </svg>
} }
> >
<svg class="h-4 w-4 text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg class="dropdown-icon-accent h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path <path
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
@@ -267,7 +267,7 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
/> />
</svg> </svg>
</Show> </Show>
<span class="text-gray-900 dark:text-gray-100 truncate">{file.path}</span> <span class="truncate">{file.path}</span>
</div> </div>
</div> </div>
) )
@@ -276,8 +276,8 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
</Show> </Show>
</div> </div>
<div class="border-t border-gray-200 px-3 py-2 dark:border-gray-700"> <div class="dropdown-footer">
<div class="text-xs text-gray-500 dark:text-gray-400"> <div>
<span class="font-medium"></span> navigate <span class="font-medium">Enter</span> select {" "} <span class="font-medium"></span> navigate <span class="font-medium">Enter</span> select {" "}
<span class="font-medium">Esc</span> close <span class="font-medium">Esc</span> close
</div> </div>

View File

@@ -702,3 +702,100 @@
0%, 100% { opacity: 1; } 0%, 100% { opacity: 1; }
50% { opacity: 0.5; } 50% { opacity: 0.5; }
} }
/* Dropdown utilities */
.dropdown-surface {
@apply absolute w-full rounded-md shadow-lg z-50;
background-color: var(--surface-base);
border: 1px solid var(--border-base);
}
.dropdown-header {
@apply px-3 py-2 border-b;
border-color: var(--border-base);
background-color: var(--surface-secondary);
}
.dropdown-header-title {
@apply text-xs font-medium;
color: var(--text-muted);
}
.dropdown-section-header {
@apply px-3 py-1.5 text-xs font-semibold;
color: var(--text-muted);
background-color: var(--surface-secondary);
}
.dropdown-content {
@apply overflow-y-auto;
}
.dropdown-item {
@apply cursor-pointer px-3 py-2 transition-colors;
color: var(--text-primary);
}
.dropdown-item:hover {
background-color: var(--surface-hover);
}
.dropdown-item-highlight {
background-color: var(--accent-primary);
color: var(--text-inverted);
}
[data-theme="dark"] .dropdown-item-highlight {
background-color: rgba(0, 128, 255, 0.2);
color: var(--text-primary);
}
.dropdown-empty {
@apply px-3 py-4 text-center text-sm;
color: var(--text-muted);
}
.dropdown-loading {
@apply p-4 text-center text-sm;
color: var(--text-muted);
}
.dropdown-footer {
@apply border-t px-3 py-2 text-xs;
border-color: var(--border-base);
color: var(--text-muted);
}
.dropdown-badge {
@apply rounded px-1.5 py-0.5 text-xs font-normal;
background-color: var(--accent-primary);
color: var(--text-inverted);
}
[data-theme="dark"] .dropdown-badge {
background-color: rgba(0, 128, 255, 0.2);
color: var(--text-primary);
}
.dropdown-diff-added {
@apply text-xs;
color: var(--status-success);
}
.dropdown-diff-removed {
@apply text-xs;
color: var(--status-error);
}
.dropdown-icon {
@apply flex-shrink-0;
color: var(--text-muted);
}
.dropdown-icon-accent {
color: var(--accent-primary);
}
[data-theme="dark"] .dropdown-icon-accent {
color: var(--accent-primary);
}