Compare commits

...

1 Commits

Author SHA1 Message Date
Kit Langton 5d729521d3 refactor(tui): remove redundant code (#40081) 2026-08-01 14:22:48 -04:00
10 changed files with 38 additions and 63 deletions
@@ -4,8 +4,6 @@ import { useDialog } from "../ui/dialog"
import { useData } from "../context/data"
import { For, Match, Switch, Show, createMemo } from "solid-js"
export type DialogStatusProps = {}
export function DialogStatus() {
const data = useData()
const theme = useTheme("elevated")
@@ -23,6 +23,7 @@ import { displayCharAt, mentionTriggerIndex } from "../../prompt/display"
import type { FileSystemEntry } from "@opencode-ai/client"
import { stringWidth } from "../../util/string-width"
import { parseFileLineRange, stripFileLineRange } from "../../prompt/parse"
import { moveSelection, revealSelectionOffset } from "../../ui/select-controller"
export type AutocompleteRef = {
onInput: (value: string) => void
@@ -501,22 +502,19 @@ export function Autocomplete(props: {
function move(direction: -1 | 1) {
if (!store.visible) return
if (!options().length) return
let next = store.selected + direction
if (next < 0) next = options().length - 1
if (next >= options().length) next = 0
moveTo(next)
moveTo(moveSelection(store.selected, { count: options().length, delta: direction, policy: "wrap" }))
}
function moveTo(next: number) {
setStore("selected", next)
if (!scroll) return
const viewportHeight = Math.min(height(), options().length)
const scrollBottom = scroll.scrollTop + viewportHeight
if (next < scroll.scrollTop) {
scroll.scrollBy(next - scroll.scrollTop)
} else if (next + 1 > scrollBottom) {
scroll.scrollBy(next + 1 - scrollBottom)
}
const offset = revealSelectionOffset(scroll.scrollTop, {
count: options().length,
limit: Math.min(height(), options().length),
selected: next,
})
if (offset === scroll.scrollTop) return
scroll.scrollBy(offset - scroll.scrollTop)
}
function select() {
@@ -8,7 +8,6 @@ import {
type KeyEvent,
} from "@opentui/core"
import { createEffect, createMemo, onMount, createSignal, onCleanup, on, Show, Switch, Match } from "solid-js"
import { registerOpencodeSpinner } from "../register-spinner"
import path from "path"
import { fileURLToPath } from "url"
import { useLocal } from "../../context/local"
@@ -55,8 +54,6 @@ import { Keymap, type KeymapCommand } from "../../context/keymap"
import { abbreviateHome } from "../../runtime"
import { PluginSlot } from "../../plugin/render"
registerOpencodeSpinner()
export type PromptProps = {
sessionID?: string
visible?: boolean
+4 -8
View File
@@ -65,6 +65,8 @@ function VerticalSessionTabs(props: { controller?: SessionTabsController; animat
const accent = () => theme.hue.accent[hueStep()]
const activeNumber = () => theme.hue.interactive[hueStep()]
const idleNumber = () => tint(theme.text.subdued, theme.background.default, 0.35)
const separatorUpperPulseColor = createMemo(() => tint(theme.background.default, theme.text.default, 0.04))
const separatorLowerPulseColor = createMemo(() => tint(theme.background.default, theme.text.default, 0.05))
const [hovered, setHovered] = createSignal<string>()
const [dragging, setDragging] = createSignal<string>()
const [preview, setPreview] = createSignal<{ sessionID: string; index: number }>()
@@ -199,12 +201,6 @@ function VerticalSessionTabs(props: { controller?: SessionTabsController; animat
}
const separatorUpperColor = createMemo(() => tint(theme.background.default, previousGlowHue(), 0.1))
const separatorLowerColor = createMemo(() => tint(theme.background.default, glowHue(), 0.12))
const separatorUpperPulseColor = createMemo(() =>
tint(theme.background.default, theme.text.default, 0.04),
)
const separatorLowerPulseColor = createMemo(() =>
tint(theme.background.default, theme.text.default, 0.05),
)
const titleColor = (index: number) => {
const color = glows()
? glowTextColor(foreground(), glowColor(), 1 + numberWidth() + index, width())
@@ -297,7 +293,7 @@ function VerticalSessionTabs(props: { controller?: SessionTabsController; animat
<box height={1} width="100%" flexDirection="row" position="relative">
<TabPulse
enabled={animations()}
active={status().busy && !status().attention}
active={runs()}
promptPulse={status().promptPulse}
complete={complete() && !status().attention}
glow={glows()}
@@ -350,7 +346,7 @@ function VerticalSessionTabs(props: { controller?: SessionTabsController; animat
<box height={1} width="100%" position="relative" flexDirection="row">
<TabPulse
enabled={animations()}
active={status().busy && !status().attention}
active={runs()}
promptPulse={status().promptPulse}
complete={complete() && !status().attention}
glow={glows()}
-2
View File
@@ -67,8 +67,6 @@ function initialRoute(value: unknown): Route | undefined {
}
}
export type RouteContext = ReturnType<typeof useRoute>
export function useRouteData<T extends Route["type"]>(type: T) {
const route = useRoute()
return route.data as Extract<Route, { type: typeof type }>
@@ -10,24 +10,18 @@ const money = new Intl.NumberFormat("en-US", {
export function PromptFooter(props: { context: Plugin.Context; sessionID?: string; mode: "normal" | "shell" }) {
const dimensions = useTerminalDimensions()
const activeSubagents = createMemo(() => {
const subagents = createMemo(() => {
if (!props.sessionID) return 0
return props.context.data.session
const count = props.context.data.session
.family(props.sessionID)
.filter((id) => id !== props.sessionID && props.context.data.session.status(id) === "running").length
})
const runningShells = createMemo(() => {
if (!props.sessionID) return 0
return props.context.data.shell
.list(props.context.location)
.filter((shell) => shell.metadata.sessionID === props.sessionID).length
})
const subagents = createMemo(() => {
const count = activeSubagents()
return count ? `${count} subagent${count === 1 ? "" : "s"}` : undefined
})
const shells = createMemo(() => {
const count = runningShells()
if (!props.sessionID) return 0
const count = props.context.data.shell
.list(props.context.location)
.filter((shell) => shell.metadata.sessionID === props.sessionID).length
return count ? `${count} shell${count === 1 ? "" : "s"}` : undefined
})
const status = createMemo(() => {
@@ -40,8 +34,10 @@ export function PromptFooter(props: { context: Plugin.Context; sessionID?: strin
session.revert?.messageID,
)
const cost = props.context.data.session.cost(props.sessionID)
return [usage ? formatContextUsage(usage.tokens, usage.percent) : undefined, cost > 0 ? money.format(cost) : undefined]
.filter((item): item is string => Boolean(item))
return [
usage ? formatContextUsage(usage.tokens, usage.percent) : undefined,
cost > 0 ? money.format(cost) : undefined,
].filter((item): item is string => Boolean(item))
})
const live = createMemo(() => Boolean(subagents() || shells()))
const shortcut = (id: string) => props.context.keymap.shortcuts(id)[0]
+1 -4
View File
@@ -90,10 +90,7 @@ export async function runPromptQueue(input: QueueInput): Promise<void> {
draining = (async () => {
try {
while (!state.closed && state.queue.length > 0) {
const prompt = state.queue.shift()
if (!prompt) {
continue
}
const prompt = state.queue.shift()!
if (prompt.mode !== "shell" && isNewCommand(prompt.text)) {
if (!input.onNewSession) {
+8 -6
View File
@@ -200,19 +200,21 @@ export function Session() {
const diffWrapMode = createMemo(() => config.diffs?.wrap ?? "word")
const groupExploration = createMemo(() => config.session?.grouping !== "none")
const tabRailWidth = createMemo(() =>
config.tabs?.enabled && config.tabs.vertical && sessionTabsFitVertically(dimensions().width)
? SESSION_SIDEBAR_WIDTH
: 0,
const availableWidth = createMemo(
() =>
dimensions().width -
(config.tabs?.enabled && config.tabs.vertical && sessionTabsFitVertically(dimensions().width)
? SESSION_SIDEBAR_WIDTH
: 0),
)
const wide = createMemo(() => dimensions().width - tabRailWidth() > 120)
const wide = createMemo(() => availableWidth() > 120)
const sidebarVisible = createMemo(() => {
if (session()?.parentID) return false
if (sidebarOpen()) return true
if (sidebar() === "auto" && wide()) return true
return false
})
const contentWidth = createMemo(() => dimensions().width - tabRailWidth() - (sidebarVisible() ? 42 : 0) - 4)
const contentWidth = createMemo(() => availableWidth() - (sidebarVisible() ? 42 : 0) - 4)
const models = createMemo(() => data.location.model.list(location()) ?? [])
const scrollAcceleration = createMemo(() => getScrollAcceleration(config))
+5 -1
View File
@@ -28,7 +28,11 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) {
position={props.overlay ? "absolute" : "relative"}
>
<scrollbox
ref={(scroll) => queueMicrotask(() => scroll.verticalScrollBar.resetVisibilityControl())}
ref={(scroll) =>
queueMicrotask(() => {
if (!scroll.isDestroyed) scroll.verticalScrollBar.resetVisibilityControl()
})
}
flexGrow={1}
scrollAcceleration={scrollAcceleration()}
verticalScrollbarOptions={{
+1 -12
View File
@@ -1,7 +1,7 @@
import { TextareaRenderable, TextAttributes } from "@opentui/core"
import { Keymap } from "../context/keymap"
import { useTheme } from "../context/theme"
import { useDialog, type DialogContext } from "./dialog"
import { useDialog } from "./dialog"
import { Show, createEffect, createSignal, onMount, type JSX } from "solid-js"
import { Spinner } from "../component/spinner"
@@ -112,14 +112,3 @@ export function DialogPrompt(props: DialogPromptProps) {
</box>
)
}
DialogPrompt.show = (dialog: DialogContext, title: string, options?: Omit<DialogPromptProps, "title">) => {
return new Promise<string | null>((resolve) => {
dialog.replace(
() => (
<DialogPrompt title={title} {...options} onConfirm={(value) => resolve(value)} onCancel={() => resolve(null)} />
),
() => resolve(null),
)
})
}