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}>
<div
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 }}
>
<div ref={scrollContainerRef} class="max-h-96 overflow-y-auto">
<div ref={scrollContainerRef} class="dropdown-content max-h-96">
<Show
when={!loading() && isInitialized()}
fallback={
<div class="p-4 text-center text-sm text-gray-500">
<div class="inline-block h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-blue-600"></div>
<span class="ml-2">Loading files...</span>
<div class="dropdown-loading">
<div class="spinner inline-block h-4 w-4 mr-2"></div>
<span>Loading files...</span>
</div>
}
>
<Show
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()}>
{(file, index) => (
<div
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 ${
index() === selectedIndex() ? "bg-blue-50 dark:bg-blue-900/20" : ""
class={`dropdown-item border-b px-4 py-2 font-mono text-sm ${
index() === selectedIndex() ? "dropdown-item-highlight" : ""
}`}
style="border-color: var(--border-muted)"
onClick={() => handleSelect(file.path)}
onMouseEnter={() => setSelectedIndex(index())}
>
<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)}>
<div class="flex gap-2 text-xs">
<div class="flex gap-2">
<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 when={file.removed}>
<span class="text-red-600 dark:text-red-400">-{file.removed}</span>
<span class="dropdown-diff-removed">-{file.removed}</span>
</Show>
</div>
</Show>
@@ -205,7 +206,7 @@ const FilePicker: Component<FilePickerProps> = (props) => {
</Show>
</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">
<span> Navigate Enter Select Esc Close</span>
</div>

View File

@@ -157,10 +157,10 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
<Show when={props.open}>
<div
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="text-xs font-medium text-gray-500 dark:text-gray-400">
<div class="dropdown-header">
<div class="dropdown-header-title">
Select Agent or File
<Show when={loading()}>
<span class="ml-2">Loading...</span>
@@ -168,13 +168,13 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
</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}>
<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 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
</div>
<For each={filteredAgents()}>
@@ -184,15 +184,15 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
)
return (
<div
class={`cursor-pointer px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 ${
itemIndex === selectedIndex() ? "bg-blue-50 dark:bg-blue-900/20" : ""
class={`dropdown-item ${
itemIndex === selectedIndex() ? "dropdown-item-highlight" : ""
}`}
data-picker-selected={itemIndex === selectedIndex()}
onClick={() => handleSelect({ type: "agent", agent })}
>
<div class="flex items-start gap-2">
<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"
viewBox="0 0 24 24"
stroke="currentColor"
@@ -206,15 +206,15 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
</svg>
<div class="flex-1">
<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"}>
<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
</span>
</Show>
</div>
<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.slice(0, 80) + "..."
: agent.description}
@@ -229,7 +229,7 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
</Show>
<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
</div>
<For each={files()}>
@@ -238,8 +238,8 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
const isFolder = file.path.endsWith("/")
return (
<div
class={`cursor-pointer px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 ${
itemIndex === selectedIndex() ? "bg-blue-50 dark:bg-blue-900/20" : ""
class={`dropdown-item py-1.5 ${
itemIndex === selectedIndex() ? "dropdown-item-highlight" : ""
}`}
data-picker-selected={itemIndex === selectedIndex()}
onClick={() => handleSelect({ type: "file", file })}
@@ -248,7 +248,7 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
<Show
when={isFolder}
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
stroke-linecap="round"
stroke-linejoin="round"
@@ -258,7 +258,7 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
</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
stroke-linecap="round"
stroke-linejoin="round"
@@ -267,7 +267,7 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
/>
</svg>
</Show>
<span class="text-gray-900 dark:text-gray-100 truncate">{file.path}</span>
<span class="truncate">{file.path}</span>
</div>
</div>
)
@@ -276,8 +276,8 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
</Show>
</div>
<div class="border-t border-gray-200 px-3 py-2 dark:border-gray-700">
<div class="text-xs text-gray-500 dark:text-gray-400">
<div class="dropdown-footer">
<div>
<span class="font-medium"></span> navigate <span class="font-medium">Enter</span> select {" "}
<span class="font-medium">Esc</span> close
</div>

View File

@@ -701,4 +701,101 @@
@keyframes pulse {
0%, 100% { opacity: 1; }
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);
}