refactor: restructure session sidebar layout

This commit is contained in:
Shantur Rathore
2025-10-29 22:01:17 +00:00
parent c3504266fa
commit 7542110120
6 changed files with 227 additions and 100 deletions

View File

@@ -53,7 +53,7 @@ export default function AgentSelector(props: AgentSelectorProps) {
}
return (
<div class="flex items-center gap-2">
<div class="sidebar-selector">
<Select
value={availableAgents().find((a) => a.name === props.currentAgent)}
onChange={handleChange}
@@ -108,7 +108,7 @@ export default function AgentSelector(props: AgentSelectorProps) {
</Select.Content>
</Select.Portal>
</Select>
<span class="hint">
<span class="hint sidebar-selector-hint">
<Kbd shortcut="cmd+shift+a" />
</span>
</div>

View File

@@ -63,7 +63,7 @@ export default function ModelSelector(props: ModelSelectorProps) {
})
return (
<div class="flex items-center gap-2">
<div class="sidebar-selector">
<Combobox<FlatModel>
value={currentModelValue()}
onChange={handleChange}
@@ -97,14 +97,14 @@ export default function ModelSelector(props: ModelSelectorProps) {
</Combobox.Item>
)}
>
<Combobox.Control class="relative" data-model-selector-control>
<Combobox.Control class="relative w-full" data-model-selector-control>
<Combobox.Input class="sr-only" data-model-selector />
<Combobox.Trigger
ref={triggerRef}
class="selector-trigger"
>
<div class="selector-trigger-label">
<span class="selector-trigger-primary">
<div class="selector-trigger-label selector-trigger-label--stacked">
<span class="selector-trigger-primary selector-trigger-primary--align-left">
Model: {currentModelValue()?.name ?? "None"}
</span>
{currentModelValue() && (
@@ -132,7 +132,7 @@ export default function ModelSelector(props: ModelSelectorProps) {
</Combobox.Content>
</Combobox.Portal>
</Combobox>
<span class="hint">
<span class="hint sidebar-selector-hint">
<Kbd shortcut="cmd+shift+m" />
</span>
</div>

View File

@@ -1,6 +1,4 @@
import { createSignal, Show, onMount, For, onCleanup } from "solid-js"
import AgentSelector from "./agent-selector"
import ModelSelector from "./model-selector"
import UnifiedPicker from "./unified-picker"
import { addToHistory, getHistory } from "../stores/message-history"
import { getAttachments, addAttachment, clearAttachments, removeAttachment } from "../stores/attachments"
@@ -17,10 +15,6 @@ interface PromptInputProps {
sessionId: string
onSend: (prompt: string, attachments: Attachment[]) => Promise<void>
disabled?: boolean
agent: string
model: { providerId: string; modelId: string }
onAgentChange: (agent: string) => Promise<void>
onModelChange: (model: { providerId: string; modelId: string }) => Promise<void>
escapeInDebounce?: boolean
}
@@ -730,37 +724,25 @@ export default function PromptInput(props: PromptInputProps) {
</button>
</div>
<div class="prompt-input-hints">
<HintRow>
<Show
when={props.escapeInDebounce}
fallback={
<>
<Kbd>Enter</Kbd> to send <Kbd>Shift+Enter</Kbd> for new line <Kbd>@</Kbd> for files/agents {" "}
<Kbd></Kbd> for history
<Show when={attachments().length > 0}>
<span class="ml-2 text-xs" style="color: var(--text-muted);"> {attachments().length} file(s) attached</span>
</Show>
</>
}
>
<span class="font-medium" style="color: var(--status-warning);">
Press <Kbd>Esc</Kbd> again to abort session
</span>
</Show>
</HintRow>
<div class="flex items-center gap-2">
<AgentSelector
instanceId={props.instanceId}
sessionId={props.sessionId}
currentAgent={props.agent}
onAgentChange={props.onAgentChange}
/>
<ModelSelector
instanceId={props.instanceId}
sessionId={props.sessionId}
currentModel={props.model}
onModelChange={props.onModelChange}
/>
<div class="flex justify-end">
<HintRow>
<Show
when={props.escapeInDebounce}
fallback={
<>
<Kbd>Enter</Kbd> to send <Kbd>Shift+Enter</Kbd> for new line <Kbd>@</Kbd> for files/agents {" "}
<Kbd></Kbd> for history
<Show when={attachments().length > 0}>
<span class="ml-2 text-xs" style="color: var(--text-muted);"> {attachments().length} file(s) attached</span>
</Show>
</>
}
>
<span class="font-medium" style="color: var(--status-warning);">
Press <Kbd>Esc</Kbd> again to abort session
</span>
</Show>
</HintRow>
</div>
</div>
</div>

View File

@@ -1,4 +1,4 @@
import { Component, For, Show, createSignal, createEffect, onCleanup, onMount, createMemo } from "solid-js"
import { Component, For, Show, createSignal, createEffect, onCleanup, onMount, createMemo, JSX } from "solid-js"
import type { Session } from "../types/session"
import { MessageSquare, Info, Plus, X } from "lucide-solid"
import KeyboardHint from "./keyboard-hint"
@@ -21,6 +21,11 @@ interface SessionListProps {
onSelect: (sessionId: string) => void
onClose: (sessionId: string) => void
onNew: () => void
showHeader?: boolean
showFooter?: boolean
headerContent?: JSX.Element
footerContent?: JSX.Element
onWidthChange?: (width: number) => void
}
const MIN_WIDTH = 200
@@ -57,6 +62,10 @@ const SessionList: Component<SessionListProps> = (props) => {
window.localStorage.setItem(STORAGE_KEY, width.toString())
})
createEffect(() => {
props.onWidthChange?.(sidebarWidth())
})
const clampWidth = (width: number) => Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, width))
const removeMouseListeners = () => {
@@ -194,14 +203,18 @@ const SessionList: Component<SessionListProps> = (props) => {
aria-hidden="true"
/>
<div class="session-list-header p-3 border-b border-base">
<div class="flex items-center justify-between gap-3">
<h3 class="text-sm font-semibold text-primary">Sessions</h3>
<KeyboardHint
shortcuts={[keyboardRegistry.get("session-prev")!, keyboardRegistry.get("session-next")!].filter(Boolean)}
/>
<Show when={props.showHeader !== false}>
<div class="session-list-header p-3 border-b border-base">
{props.headerContent ?? (
<div class="flex items-center justify-between gap-3">
<h3 class="text-sm font-semibold text-primary">Sessions</h3>
<KeyboardHint
shortcuts={[keyboardRegistry.get("session-prev")!, keyboardRegistry.get("session-next")!].filter(Boolean)}
/>
</div>
)}
</div>
</div>
</Show>
<div class="session-list flex-1 overflow-y-auto">
<div class="session-section">
@@ -289,17 +302,21 @@ const SessionList: Component<SessionListProps> = (props) => {
</Show>
</div>
<div class="session-list-footer p-3 border-t border-base">
<button
class="session-new-button w-full flex items-center gap-2 px-3 py-2 rounded-md transition-colors text-sm font-medium"
onClick={props.onNew}
title="New session (Cmd/Ctrl+T)"
aria-label="New session"
>
<Plus class="w-4 h-4" />
<span>New Session</span>
</button>
</div>
<Show when={props.showFooter !== false}>
<div class="session-list-footer p-3 border-t border-base">
{props.footerContent ?? (
<button
class="session-new-button w-full flex items-center gap-2 px-3 py-2 rounded-md transition-colors text-sm font-medium"
onClick={props.onNew}
title="New session (Cmd/Ctrl+T)"
aria-label="New session"
>
<Plus class="w-4 h-4" />
<span>New Session</span>
</button>
)}
</div>
</Show>
</div>
)
}