feat(desktop): session router, interrupt agent, visual cleanup

This commit is contained in:
Adam
2025-11-05 11:55:31 -06:00
parent 69a499f807
commit d525fbf829
21 changed files with 1259 additions and 1145 deletions

View File

@@ -11,20 +11,21 @@ export type CodeProps<T = {}> = FileOptions<T> & {
export function Code<T>(props: CodeProps<T>) {
let container!: HTMLDivElement
const [local, others] = splitProps(props, ["file", "class", "classList", "annotations"])
const file = () => local.file
createEffect(() => {
const instance = new File<T>({
theme: { dark: "oc-1-dark", light: "oc-1-light" }, // or any Shiki theme
overflow: "wrap", // or 'scroll'
themeType: "system", // 'system', 'light', or 'dark'
disableFileHeader: true,
disableLineNumbers: false, // optional
// lang: 'typescript', // optional - auto-detected from filename if not provided
...others,
})
container.innerHTML = ""
instance.render({
file: file(),
file: local.file,
lineAnnotations: local.annotations,
containerWrapper: container,
})

View File

@@ -154,6 +154,7 @@ export function Diff<T>(props: DiffProps<T>) {
...others,
})
container.innerHTML = ""
instance.render({
oldFile: local.before,
newFile: local.after,

View File

@@ -150,6 +150,7 @@ const newIcons = {
"code-lines": `<path d="M2.08325 3.75H11.2499M14.5833 3.75H17.9166M2.08325 10L7.08325 10M10.4166 10L17.9166 10M2.08325 16.25L8.74992 16.25M12.0833 16.25L17.9166 16.25" stroke="currentColor" stroke-linecap="square" stroke-linejoin="round"/>`,
"square-arrow-top-right": `<path d="M7.91675 2.9165H2.91675V17.0832H17.0834V12.0832M12.0834 2.9165H17.0834V7.9165M9.58342 10.4165L16.6667 3.33317" stroke="currentColor" stroke-linecap="square"/>`,
"circle-ban-sign": `<path d="M15.3675 4.63087L4.55742 15.441M17.9163 9.9987C17.9163 14.371 14.3719 17.9154 9.99967 17.9154C7.81355 17.9154 5.83438 17.0293 4.40175 15.5966C2.96911 14.164 2.08301 12.1848 2.08301 9.9987C2.08301 5.62644 5.62742 2.08203 9.99967 2.08203C12.1858 2.08203 14.165 2.96813 15.5976 4.40077C17.0302 5.8334 17.9163 7.81257 17.9163 9.9987Z" stroke="currentColor" stroke-linecap="round"/>`,
stop: `<rect x="6" y="6" width="8" height="8" fill="currentColor"/>`,
}
export interface IconProps extends ComponentProps<"svg"> {

View File

@@ -2,22 +2,37 @@ import { TextField as Kobalte } from "@kobalte/core/text-field"
import { Show, splitProps } from "solid-js"
import type { ComponentProps } from "solid-js"
export interface InputProps extends ComponentProps<typeof Kobalte> {
export interface InputProps
extends ComponentProps<typeof Kobalte.Input>,
Pick<ComponentProps<typeof Kobalte>, "value" | "onChange" | "onKeyDown"> {
label?: string
hideLabel?: boolean
description?: string
}
export function Input(props: InputProps) {
const [local, others] = splitProps(props, ["class", "label", "hideLabel", "description", "placeholder"])
const [local, others] = splitProps(props, [
"class",
"label",
"hideLabel",
"description",
"value",
"onChange",
"onKeyDown",
])
return (
<Kobalte {...others} data-component="input">
<Kobalte
data-component="input"
value={local.value}
onChange={local.onChange}
onKeyDown={local.onKeyDown}
>
<Show when={local.label}>
<Kobalte.Label data-slot="label" classList={{ "sr-only": local.hideLabel }}>
{local.label}
</Kobalte.Label>
</Show>
<Kobalte.Input data-slot="input" class={local.class} placeholder={local.placeholder} />
<Kobalte.Input {...others} data-slot="input" class={local.class} />
<Show when={local.description}>
<Kobalte.Description data-slot="description">{local.description}</Kobalte.Description>
</Show>

View File

@@ -62,7 +62,13 @@ export function List<T>(props: ListProps<T>) {
})
return (
<VList data-component="list" ref={setVirtualizer} data={props.data} onKeyDown={handleKey} class={props.class}>
<VList
data-component="list"
ref={setVirtualizer}
data={props.data}
onKeyDown={handleKey}
class={props.class}
>
{(item) => (
<button
data-slot="item"

View File

@@ -21,15 +21,16 @@ export function SelectDialog<T>(props: SelectDialogProps<T>) {
mouseActive: false,
})
const { filter, grouped, flat, reset, clear, active, setActive, onKeyDown, onInput } = useFilteredList<T>({
items: others.items,
key: others.key,
filterKeys: others.filterKeys,
current: others.current,
groupBy: others.groupBy,
sortBy: others.sortBy,
sortGroupsBy: others.sortGroupsBy,
})
const { filter, grouped, flat, reset, clear, active, setActive, onKeyDown, onInput } =
useFilteredList<T>({
items: others.items,
key: others.key,
filterKeys: others.filterKeys,
current: others.current,
groupBy: others.groupBy,
sortBy: others.sortBy,
sortGroupsBy: others.sortGroupsBy,
})
createEffect(() => {
filter()
@@ -117,7 +118,8 @@ export function SelectDialog<T>(props: SelectDialogProps<T>) {
fallback={
<div data-slot="empty-state">
<div data-slot="message">
{props.emptyMessage ?? "No search results"} for <span data-slot="filter">&quot;{filter()}&quot;</span>
{props.emptyMessage ?? "No search results"} for{" "}
<span data-slot="filter">&quot;{filter()}&quot;</span>
</div>
</div>
}

View File

@@ -1,14 +1,14 @@
[data-component="select"] {
[data-slot="trigger"] {
[data-slot="select-trigger"] {
padding: 0 4px 0 8px;
box-shadow: none;
[data-slot="value"] {
[data-slot="select-trigger-value"] {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
[data-slot="icon"] {
[data-slot="select-trigger-icon"] {
width: 16px;
height: 16px;
flex-shrink: 0;
@@ -38,7 +38,7 @@
animation: select-open 0.15s ease-out;
}
[data-slot="list"] {
[data-slot="select-content-list"] {
overflow-y: auto;
max-height: 12rem;
white-space: nowrap;
@@ -55,7 +55,7 @@
/* [data-slot="section"] { */
/* } */
[data-slot="item"] {
[data-slot="select-item"] {
position: relative;
display: flex;
align-items: center;
@@ -85,7 +85,7 @@
background-color: var(--surface-raised-base);
pointer-events: none;
}
[data-slot="item-indicator"] {
[data-slot="select-item-indicator"] {
margin-left: auto;
width: 16px;
height: 16px;

View File

@@ -41,17 +41,19 @@ export function Select<T>(props: SelectProps<T> & ButtonProps) {
)}
itemComponent={(itemProps) => (
<Kobalte.Item
data-slot="item"
data-slot="select-item"
classList={{
...(props.classList ?? {}),
[props.class ?? ""]: !!props.class,
}}
{...itemProps}
>
<Kobalte.ItemLabel data-slot="item-label">
{props.label ? props.label(itemProps.item.rawValue) : (itemProps.item.rawValue as string)}
<Kobalte.ItemLabel data-slot="select-item-label">
{props.label
? props.label(itemProps.item.rawValue)
: (itemProps.item.rawValue as string)}
</Kobalte.ItemLabel>
<Kobalte.ItemIndicator data-slot="item-indicator">
<Kobalte.ItemIndicator data-slot="select-item-indicator">
<Icon name="check-small" size="small" />
</Kobalte.ItemIndicator>
</Kobalte.Item>
@@ -61,7 +63,7 @@ export function Select<T>(props: SelectProps<T> & ButtonProps) {
}}
>
<Kobalte.Trigger
data-slot="trigger"
data-slot="select-trigger"
as={Button}
size={props.size}
variant={props.variant}
@@ -70,7 +72,7 @@ export function Select<T>(props: SelectProps<T> & ButtonProps) {
[props.class ?? ""]: !!props.class,
}}
>
<Kobalte.Value<T> data-slot="value">
<Kobalte.Value<T> data-slot="select-trigger-value">
{(state) => {
const selected = state.selectedOption() ?? props.current
if (!selected) return props.placeholder || ""
@@ -78,7 +80,7 @@ export function Select<T>(props: SelectProps<T> & ButtonProps) {
return selected as string
}}
</Kobalte.Value>
<Kobalte.Icon data-slot="icon">
<Kobalte.Icon data-slot="select-trigger-icon">
<Icon name="chevron-down" size="small" />
</Kobalte.Icon>
</Kobalte.Trigger>
@@ -90,7 +92,7 @@ export function Select<T>(props: SelectProps<T> & ButtonProps) {
}}
data-component="select-content"
>
<Kobalte.Listbox data-slot="list" />
<Kobalte.Listbox data-slot="select-content-list" />
</Kobalte.Content>
</Kobalte.Portal>
</Kobalte>