mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-02 00:13:39 -04:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ced6747737 | |||
| 5d729521d3 |
@@ -301,7 +301,6 @@ describe("ShellTool", () => {
|
||||
},
|
||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]().then(() => undefined)),
|
||||
),
|
||||
{ timeout: 15_000 },
|
||||
)
|
||||
|
||||
it.live("rejects a workdir that stops being a directory during approval", () =>
|
||||
@@ -473,7 +472,6 @@ describe("ShellTool", () => {
|
||||
},
|
||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]().then(() => undefined)),
|
||||
),
|
||||
{ timeout: 15_000 },
|
||||
)
|
||||
|
||||
it.live(
|
||||
@@ -540,7 +538,7 @@ describe("ShellTool", () => {
|
||||
(tmp) => {
|
||||
reset()
|
||||
return withSession(tmp.path, (registry) =>
|
||||
executeTool(registry, call({ command: timeoutOutputCommand, timeout: isWindows ? 3_000 : 50 })),
|
||||
executeTool(registry, call({ command: timeoutOutputCommand, timeout: isWindows ? 500 : 50 })),
|
||||
).pipe(
|
||||
Effect.andThen((settled) =>
|
||||
Effect.sync(() => {
|
||||
@@ -559,7 +557,6 @@ describe("ShellTool", () => {
|
||||
},
|
||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]().then(() => undefined)),
|
||||
),
|
||||
{ timeout: 15_000 },
|
||||
)
|
||||
|
||||
it.live("returns the shell id for a background command", () =>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { RGBA, ScrollBoxRenderable, TextAttributes } from "@opentui/core"
|
||||
import { For, Show, createComputed, createEffect, createMemo, createSignal, untrack } from "solid-js"
|
||||
import { For, Show, createComputed, createEffect, createMemo, createSignal, onCleanup, untrack } from "solid-js"
|
||||
import { useTerminalDimensions } from "@opentui/solid"
|
||||
import { useConfig } from "../config"
|
||||
import { useSessionTabs } from "../context/session-tabs"
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
import { createAnimatable, spring, tween } from "../ui/animation"
|
||||
import { Locale } from "../util/locale"
|
||||
import { stringWidth } from "../util/string-width"
|
||||
import { TabPulse, unreadGlowIntensity } from "./tab-pulse"
|
||||
import { TabPulse, TabPulseTimeline, unreadGlowIntensity } from "./tab-pulse"
|
||||
import { tint } from "../theme/color"
|
||||
import { SESSION_SIDEBAR_WIDTH } from "../ui/layout"
|
||||
import { projectName } from "../util/project"
|
||||
@@ -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 }>()
|
||||
@@ -95,6 +97,14 @@ function VerticalSessionTabs(props: { controller?: SessionTabsController; animat
|
||||
),
|
||||
)
|
||||
const itemStatus = (tab: SessionTab) => statuses().get(tab.sessionID)!
|
||||
const pulseTimelines = new Map<string, TabPulseTimeline>()
|
||||
const pulseTimeline = (sessionID: string) => {
|
||||
const existing = pulseTimelines.get(sessionID)
|
||||
if (existing) return existing
|
||||
const created = new TabPulseTimeline()
|
||||
pulseTimelines.set(sessionID, created)
|
||||
return created
|
||||
}
|
||||
let rail: { screenY: number } | undefined
|
||||
let scroll: ScrollBoxRenderable | undefined
|
||||
|
||||
@@ -130,6 +140,8 @@ function VerticalSessionTabs(props: { controller?: SessionTabsController; animat
|
||||
<box flexShrink={0} flexDirection="column" gap={1}>
|
||||
<For each={items()}>
|
||||
{(tab, index) => {
|
||||
const timeline = pulseTimeline(tab.sessionID)
|
||||
onCleanup(() => pulseTimelines.delete(tab.sessionID))
|
||||
const selected = () => activeID() === tab.sessionID
|
||||
const status = createMemo(() => itemStatus(tab))
|
||||
const [sweepLevel, setSweepLevel] = createSignal(0)
|
||||
@@ -183,6 +195,10 @@ function VerticalSessionTabs(props: { controller?: SessionTabsController; animat
|
||||
const detailColor = createMemo(() => tint(theme.text.subdued, pulseBackground(), 0.35))
|
||||
const glows = () => status().glows
|
||||
const previous = createMemo(() => items()[index() - 1])
|
||||
const previousTimeline = () => {
|
||||
const tab = previous()
|
||||
return tab ? pulseTimeline(tab.sessionID) : undefined
|
||||
}
|
||||
const previousStatus = createMemo(() => {
|
||||
const tab = previous()
|
||||
return tab
|
||||
@@ -199,12 +215,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())
|
||||
@@ -248,24 +258,30 @@ function VerticalSessionTabs(props: { controller?: SessionTabsController; animat
|
||||
top={-1}
|
||||
edge="above"
|
||||
enabled={animations()}
|
||||
active={runs()}
|
||||
outerActive={previousRuns()}
|
||||
promptPulse={status().promptPulse}
|
||||
outerPromptPulse={previousStatus().promptPulse}
|
||||
complete={complete() && !status().attention}
|
||||
outerComplete={previousStatus().complete && !previousStatus().attention}
|
||||
glow={glows()}
|
||||
outerGlow={previousGlows()}
|
||||
breathe={status().attention}
|
||||
outerBreathe={previousStatus().attention}
|
||||
color={separatorLowerPulseColor()}
|
||||
outerColor={separatorUpperPulseColor()}
|
||||
glowColor={separatorLowerColor()}
|
||||
outerGlowColor={separatorUpperColor()}
|
||||
glowTail={8}
|
||||
outerGlowTail={5}
|
||||
completionColor={separatorLowerColor()}
|
||||
outerCompletionColor={separatorUpperColor()}
|
||||
layer={{
|
||||
timeline,
|
||||
active: runs(),
|
||||
promptPulse: status().promptPulse,
|
||||
complete: complete() && !status().attention,
|
||||
glow: glows(),
|
||||
breathe: status().attention,
|
||||
color: separatorLowerPulseColor(),
|
||||
glowColor: separatorLowerColor(),
|
||||
glowTail: 8,
|
||||
completionColor: separatorLowerColor(),
|
||||
}}
|
||||
edgeLayer={{
|
||||
timeline: previousTimeline(),
|
||||
active: previousRuns(),
|
||||
promptPulse: previousStatus().promptPulse,
|
||||
complete: previousStatus().complete && !previousStatus().attention,
|
||||
glow: previousGlows(),
|
||||
breathe: previousStatus().attention,
|
||||
color: separatorUpperPulseColor(),
|
||||
glowColor: separatorUpperColor(),
|
||||
glowTail: 5,
|
||||
completionColor: separatorUpperColor(),
|
||||
}}
|
||||
backgroundColor={theme.background.default}
|
||||
/>
|
||||
<Show when={index() === items().length - 1}>
|
||||
@@ -273,38 +289,41 @@ function VerticalSessionTabs(props: { controller?: SessionTabsController; animat
|
||||
top={2}
|
||||
edge="below"
|
||||
enabled={animations()}
|
||||
active={runs()}
|
||||
outerActive={false}
|
||||
promptPulse={status().promptPulse}
|
||||
outerPromptPulse={0}
|
||||
complete={complete() && !status().attention}
|
||||
outerComplete={false}
|
||||
glow={glows()}
|
||||
outerGlow={false}
|
||||
breathe={status().attention}
|
||||
outerBreathe={false}
|
||||
color={tint(theme.background.default, theme.text.default, 0.04)}
|
||||
outerColor={tint(theme.background.default, theme.text.default, 0.006)}
|
||||
glowColor={tint(theme.background.default, glowHue(), 0.1)}
|
||||
outerGlowColor={theme.background.default}
|
||||
glowTail={8}
|
||||
outerGlowTail={5}
|
||||
completionColor={tint(theme.background.default, glowHue(), 0.1)}
|
||||
outerCompletionColor={theme.background.default}
|
||||
layer={{
|
||||
timeline,
|
||||
active: runs(),
|
||||
promptPulse: status().promptPulse,
|
||||
complete: complete() && !status().attention,
|
||||
glow: glows(),
|
||||
breathe: status().attention,
|
||||
color: tint(theme.background.default, theme.text.default, 0.04),
|
||||
glowColor: tint(theme.background.default, glowHue(), 0.1),
|
||||
glowTail: 8,
|
||||
completionColor: tint(theme.background.default, glowHue(), 0.1),
|
||||
}}
|
||||
edgeLayer={{
|
||||
color: tint(theme.background.default, theme.text.default, 0.006),
|
||||
glowColor: theme.background.default,
|
||||
glowTail: 5,
|
||||
completionColor: theme.background.default,
|
||||
}}
|
||||
backgroundColor={theme.background.default}
|
||||
/>
|
||||
</Show>
|
||||
<box height={1} width="100%" flexDirection="row" position="relative">
|
||||
<TabPulse
|
||||
enabled={animations()}
|
||||
active={status().busy && !status().attention}
|
||||
promptPulse={status().promptPulse}
|
||||
complete={complete() && !status().attention}
|
||||
glow={glows()}
|
||||
breathe={status().attention}
|
||||
color={pulseColor()}
|
||||
glowColor={glowColor()}
|
||||
completionColor={glowColor()}
|
||||
layer={{
|
||||
timeline,
|
||||
active: runs(),
|
||||
promptPulse: status().promptPulse,
|
||||
complete: complete() && !status().attention,
|
||||
glow: glows(),
|
||||
breathe: status().attention,
|
||||
color: pulseColor(),
|
||||
glowColor: glowColor(),
|
||||
completionColor: glowColor(),
|
||||
}}
|
||||
backgroundColor={pulseBackground()}
|
||||
onLevel={setSweepLevel}
|
||||
/>
|
||||
@@ -350,15 +369,18 @@ function VerticalSessionTabs(props: { controller?: SessionTabsController; animat
|
||||
<box height={1} width="100%" position="relative" flexDirection="row">
|
||||
<TabPulse
|
||||
enabled={animations()}
|
||||
active={status().busy && !status().attention}
|
||||
promptPulse={status().promptPulse}
|
||||
complete={complete() && !status().attention}
|
||||
glow={glows()}
|
||||
breathe={status().attention}
|
||||
color={detailPulseColor()}
|
||||
glowColor={detailGlowColor()}
|
||||
glowTail={10}
|
||||
completionColor={detailGlowColor()}
|
||||
layer={{
|
||||
timeline,
|
||||
active: runs(),
|
||||
promptPulse: status().promptPulse,
|
||||
complete: complete() && !status().attention,
|
||||
glow: glows(),
|
||||
breathe: status().attention,
|
||||
color: detailPulseColor(),
|
||||
glowColor: detailGlowColor(),
|
||||
glowTail: 10,
|
||||
completionColor: detailGlowColor(),
|
||||
}}
|
||||
backgroundColor={pulseBackground()}
|
||||
/>
|
||||
<box zIndex={1} width="100%" flexDirection="row" paddingLeft={numberWidth() + 1} paddingRight={2}>
|
||||
@@ -655,15 +677,17 @@ function HorizontalSessionTabs(props: { controller?: SessionTabsController; anim
|
||||
>
|
||||
<TabPulse
|
||||
enabled={animations()}
|
||||
active={status().busy && !status().attention}
|
||||
promptPulse={status().promptPulse}
|
||||
complete={status().complete && !status().attention}
|
||||
glow={glows()}
|
||||
breathe={status().attention}
|
||||
color={pulseColor()}
|
||||
glowColor={glowColor()}
|
||||
flashColor={flashColor()}
|
||||
completionColor={accent()}
|
||||
layer={{
|
||||
active: status().busy && !status().attention,
|
||||
promptPulse: status().promptPulse,
|
||||
complete: status().complete && !status().attention,
|
||||
glow: glows(),
|
||||
breathe: status().attention,
|
||||
color: pulseColor(),
|
||||
glowColor: glowColor(),
|
||||
flashColor: flashColor(),
|
||||
completionColor: accent(),
|
||||
}}
|
||||
backgroundColor={background()}
|
||||
onLevel={setSweepLevel}
|
||||
/>
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
import { OptimizedBuffer, Renderable, RGBA, type RenderableOptions, type RenderContext } from "@opentui/core"
|
||||
import { extend } from "@opentui/solid"
|
||||
|
||||
export type TabPulseState = {
|
||||
active?: boolean
|
||||
promptPulse?: number
|
||||
complete?: boolean
|
||||
glow?: boolean
|
||||
breathe?: boolean
|
||||
}
|
||||
|
||||
export type TabPulseLayer = TabPulseState & {
|
||||
timeline?: TabPulseTimeline
|
||||
color: RGBA
|
||||
glowColor?: RGBA
|
||||
glowTail?: number
|
||||
flashColor?: RGBA
|
||||
completionColor?: RGBA
|
||||
}
|
||||
|
||||
type TabPulseOptions = RenderableOptions<TabPulseRenderable> & {
|
||||
edge?: "above" | "below"
|
||||
enabled?: boolean
|
||||
active?: boolean
|
||||
outerActive?: boolean
|
||||
promptPulse?: number
|
||||
outerPromptPulse?: number
|
||||
complete?: boolean
|
||||
outerComplete?: boolean
|
||||
glow?: boolean
|
||||
outerGlow?: boolean
|
||||
breathe?: boolean
|
||||
outerBreathe?: boolean
|
||||
color?: RGBA
|
||||
outerColor?: RGBA
|
||||
glowColor?: RGBA
|
||||
outerGlowColor?: RGBA
|
||||
glowTail?: number
|
||||
outerGlowTail?: number
|
||||
flashColor?: RGBA
|
||||
outerFlashColor?: RGBA
|
||||
completionColor?: RGBA
|
||||
outerCompletionColor?: RGBA
|
||||
layer?: TabPulseLayer
|
||||
edgeLayer?: TabPulseLayer
|
||||
backgroundColor?: RGBA
|
||||
/** Reports the running sweep's intensity at the tab number's cell, quantized; 0 when idle. */
|
||||
onLevel?: (level: number) => void
|
||||
@@ -310,71 +309,163 @@ class PulseState {
|
||||
}
|
||||
}
|
||||
|
||||
export class TabPulseTimeline {
|
||||
private state: PulseState
|
||||
private frame = -1
|
||||
|
||||
constructor(options: TabPulseState = {}, enabled = true) {
|
||||
this.state = new PulseState({
|
||||
enabled,
|
||||
active: options.active ?? false,
|
||||
promptPulse: options.promptPulse ?? 0,
|
||||
complete: options.complete ?? false,
|
||||
glow: options.glow ?? false,
|
||||
breathe: options.breathe ?? false,
|
||||
})
|
||||
}
|
||||
|
||||
set(options: TabPulseState, enabled: boolean) {
|
||||
return [
|
||||
this.state.setEnabled(enabled),
|
||||
// Stopping a run arms completion; apply complete afterward so an atomic idle update can consume it.
|
||||
this.state.setActive(options.active ?? false),
|
||||
this.state.setPromptPulse(options.promptPulse ?? 0),
|
||||
this.state.setComplete(options.complete ?? false),
|
||||
this.state.setGlow(options.glow ?? false),
|
||||
this.state.setBreathe(options.breathe ?? false),
|
||||
].some(Boolean)
|
||||
}
|
||||
|
||||
setEnabled(value: boolean) {
|
||||
return this.state.setEnabled(value)
|
||||
}
|
||||
|
||||
advance(deltaTime: number, frame: number) {
|
||||
if (frame === this.frame) return
|
||||
this.frame = frame
|
||||
this.state.advance(deltaTime)
|
||||
}
|
||||
|
||||
get live() {
|
||||
return this.state.live
|
||||
}
|
||||
|
||||
get running() {
|
||||
return this.state.running
|
||||
}
|
||||
|
||||
get completion() {
|
||||
return this.state.completion
|
||||
}
|
||||
|
||||
get flash() {
|
||||
return this.state.flash
|
||||
}
|
||||
|
||||
get glowLevel() {
|
||||
return this.state.glowLevel
|
||||
}
|
||||
|
||||
fronts(width: number) {
|
||||
return this.state.fronts(width)
|
||||
}
|
||||
}
|
||||
|
||||
class PulseLayer {
|
||||
timeline: TabPulseTimeline
|
||||
private source: TabPulseTimeline | undefined
|
||||
private enabled: boolean
|
||||
color: RGBA
|
||||
glowColor: RGBA
|
||||
glowTail: number
|
||||
flashColor: RGBA
|
||||
completionColor: RGBA
|
||||
|
||||
constructor(options: TabPulseLayer, enabled: boolean) {
|
||||
this.enabled = enabled
|
||||
this.source = options.timeline
|
||||
this.timeline = options.timeline ?? new TabPulseTimeline(options, enabled)
|
||||
if (options.timeline) options.timeline.set(options, enabled)
|
||||
this.color = options.color
|
||||
this.glowColor = options.glowColor ?? options.color
|
||||
this.glowTail = options.glowTail ?? GLOW_TAIL
|
||||
this.flashColor = options.flashColor ?? options.color
|
||||
this.completionColor = options.completionColor ?? options.color
|
||||
}
|
||||
|
||||
set(options: TabPulseLayer) {
|
||||
const sourceChanged = options.timeline !== this.source
|
||||
if (sourceChanged) {
|
||||
this.source = options.timeline
|
||||
this.timeline = options.timeline ?? new TabPulseTimeline(options, this.enabled)
|
||||
}
|
||||
const stateChanged = sourceChanged
|
||||
? (options.timeline?.set(options, this.enabled) ?? false)
|
||||
: this.timeline.set(options, this.enabled)
|
||||
const color = options.color
|
||||
const glowColor = options.glowColor ?? color
|
||||
const glowTail = options.glowTail ?? GLOW_TAIL
|
||||
const flashColor = options.flashColor ?? color
|
||||
const completionColor = options.completionColor ?? color
|
||||
const changed = [
|
||||
sourceChanged,
|
||||
stateChanged,
|
||||
!color.equals(this.color),
|
||||
!glowColor.equals(this.glowColor),
|
||||
glowTail !== this.glowTail,
|
||||
!flashColor.equals(this.flashColor),
|
||||
!completionColor.equals(this.completionColor),
|
||||
].some(Boolean)
|
||||
this.color = color
|
||||
this.glowColor = glowColor
|
||||
this.glowTail = glowTail
|
||||
this.flashColor = flashColor
|
||||
this.completionColor = completionColor
|
||||
return changed
|
||||
}
|
||||
|
||||
setEnabled(value: boolean) {
|
||||
this.enabled = value
|
||||
return this.timeline.setEnabled(value)
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_LAYER: TabPulseLayer = { color: RGBA.defaultForeground() }
|
||||
|
||||
class TabPulseRenderable extends Renderable {
|
||||
private _enabled: boolean
|
||||
private inner: PulseState
|
||||
private outer: PulseState
|
||||
private _color: RGBA
|
||||
private _outerColor: RGBA
|
||||
private _glowColor: RGBA
|
||||
private _outerGlowColor: RGBA
|
||||
private _glowTail: number
|
||||
private _outerGlowTail: number
|
||||
private primary: PulseLayer
|
||||
private adjacent: PulseLayer
|
||||
private primaryAssigned: boolean
|
||||
private adjacentAssigned: boolean
|
||||
private _edge: "above" | "below" | undefined
|
||||
private _flashColor: RGBA
|
||||
private _outerFlashColor: RGBA
|
||||
private _completionColor: RGBA
|
||||
private _outerCompletionColor: RGBA
|
||||
private _backgroundColor: RGBA
|
||||
private renderColor = RGBA.fromInts(0, 0, 0)
|
||||
private outerRenderColor = RGBA.fromInts(0, 0, 0)
|
||||
private primaryRenderColor = RGBA.fromInts(0, 0, 0)
|
||||
private adjacentRenderColor = RGBA.fromInts(0, 0, 0)
|
||||
private _onLevel: ((level: number) => void) | undefined
|
||||
private lastLevel = 0
|
||||
|
||||
constructor(ctx: RenderContext, options: TabPulseOptions = {}) {
|
||||
const enabled = options.enabled ?? true
|
||||
const active = options.active ?? false
|
||||
const glow = options.glow ?? false
|
||||
const breathe = options.breathe ?? false
|
||||
const primary = options.layer ?? DEFAULT_LAYER
|
||||
const adjacent = options.edgeLayer ?? DEFAULT_LAYER
|
||||
const edge = options.edge
|
||||
const outerActive = options.outerActive ?? active
|
||||
const outerGlow = options.outerGlow ?? glow
|
||||
const outerBreathe = options.outerBreathe ?? breathe
|
||||
super(ctx, {
|
||||
...options,
|
||||
height: 1,
|
||||
live:
|
||||
enabled &&
|
||||
(active || (glow && breathe) || (edge !== undefined && (outerActive || (outerGlow && outerBreathe)))),
|
||||
((primary.active ?? false) ||
|
||||
((primary.glow ?? false) && (primary.breathe ?? false)) ||
|
||||
(edge !== undefined &&
|
||||
((adjacent.active ?? false) || ((adjacent.glow ?? false) && (adjacent.breathe ?? false))))),
|
||||
})
|
||||
this._enabled = enabled
|
||||
this.inner = new PulseState({
|
||||
enabled,
|
||||
active,
|
||||
promptPulse: options.promptPulse ?? 0,
|
||||
complete: options.complete ?? false,
|
||||
glow,
|
||||
breathe,
|
||||
})
|
||||
this.outer = new PulseState({
|
||||
enabled: enabled && edge !== undefined,
|
||||
active: outerActive,
|
||||
promptPulse: options.outerPromptPulse ?? options.promptPulse ?? 0,
|
||||
complete: options.outerComplete ?? options.complete ?? false,
|
||||
glow: outerGlow,
|
||||
breathe: outerBreathe,
|
||||
})
|
||||
this._color = options.color ?? RGBA.defaultForeground()
|
||||
this._outerColor = options.outerColor ?? this._color
|
||||
this._glowColor = options.glowColor ?? this._color
|
||||
this._outerGlowColor = options.outerGlowColor ?? this._glowColor
|
||||
this._glowTail = options.glowTail ?? GLOW_TAIL
|
||||
this._outerGlowTail = options.outerGlowTail ?? this._glowTail
|
||||
this.primary = new PulseLayer(primary, enabled)
|
||||
this.adjacent = new PulseLayer(adjacent, enabled && edge !== undefined)
|
||||
this.primaryAssigned = options.layer !== undefined
|
||||
this.adjacentAssigned = options.edgeLayer !== undefined
|
||||
this._edge = edge
|
||||
this._flashColor = options.flashColor ?? this._color
|
||||
this._outerFlashColor = options.outerFlashColor ?? options.flashColor ?? this._outerColor
|
||||
this._completionColor = options.completionColor ?? this._color
|
||||
this._outerCompletionColor = options.outerCompletionColor ?? options.completionColor ?? this._outerColor
|
||||
this._backgroundColor = options.backgroundColor ?? RGBA.defaultBackground()
|
||||
this._onLevel = options.onLevel
|
||||
}
|
||||
@@ -388,129 +479,42 @@ class TabPulseRenderable extends Renderable {
|
||||
const quantized = Math.round(value * 32) / 32
|
||||
if (quantized === this.lastLevel) return
|
||||
this.lastLevel = quantized
|
||||
this._onLevel?.(quantized)
|
||||
this._onLevel(quantized)
|
||||
}
|
||||
|
||||
set enabled(value: boolean) {
|
||||
if (value === this._enabled) return
|
||||
this._enabled = value
|
||||
this.inner.setEnabled(value)
|
||||
this.outer.setEnabled(value && this._edge !== undefined)
|
||||
this.live = this.inner.live || this.outer.live
|
||||
this.requestRender()
|
||||
this.primary.setEnabled(value)
|
||||
this.adjacent.setEnabled(value && this._edge !== undefined)
|
||||
this.changed()
|
||||
}
|
||||
|
||||
set active(value: boolean) {
|
||||
if (this.inner.setActive(value)) this.changed()
|
||||
set layer(value: TabPulseLayer) {
|
||||
if (!this.primaryAssigned) {
|
||||
this.primaryAssigned = true
|
||||
this.primary = new PulseLayer(value, this._enabled)
|
||||
this.changed()
|
||||
return
|
||||
}
|
||||
if (this.primary.set(value)) this.changed()
|
||||
}
|
||||
|
||||
set outerActive(value: boolean) {
|
||||
if (this.outer.setActive(value)) this.changed()
|
||||
}
|
||||
|
||||
set promptPulse(value: number) {
|
||||
if (this.inner.setPromptPulse(value)) this.changed()
|
||||
}
|
||||
|
||||
set outerPromptPulse(value: number) {
|
||||
if (this.outer.setPromptPulse(value)) this.changed()
|
||||
}
|
||||
|
||||
set complete(value: boolean) {
|
||||
if (this.inner.setComplete(value)) this.changed()
|
||||
}
|
||||
|
||||
set outerComplete(value: boolean) {
|
||||
if (this.outer.setComplete(value)) this.changed()
|
||||
}
|
||||
|
||||
set glow(value: boolean) {
|
||||
if (this.inner.setGlow(value)) this.changed()
|
||||
}
|
||||
|
||||
set outerGlow(value: boolean) {
|
||||
if (this.outer.setGlow(value)) this.changed()
|
||||
}
|
||||
|
||||
set breathe(value: boolean) {
|
||||
if (this.inner.setBreathe(value)) this.changed()
|
||||
}
|
||||
|
||||
set outerBreathe(value: boolean) {
|
||||
if (this.outer.setBreathe(value)) this.changed()
|
||||
}
|
||||
|
||||
private changed() {
|
||||
this.live = this.inner.live || this.outer.live
|
||||
this.requestRender()
|
||||
}
|
||||
|
||||
set color(value: RGBA) {
|
||||
if (value.equals(this._color)) return
|
||||
this._color = value
|
||||
this.requestRender()
|
||||
}
|
||||
|
||||
set glowColor(value: RGBA) {
|
||||
if (value.equals(this._glowColor)) return
|
||||
this._glowColor = value
|
||||
this.requestRender()
|
||||
}
|
||||
|
||||
set outerColor(value: RGBA) {
|
||||
if (value.equals(this._outerColor)) return
|
||||
this._outerColor = value
|
||||
this.requestRender()
|
||||
}
|
||||
|
||||
set outerGlowColor(value: RGBA) {
|
||||
if (value.equals(this._outerGlowColor)) return
|
||||
this._outerGlowColor = value
|
||||
this.requestRender()
|
||||
}
|
||||
|
||||
set glowTail(value: number) {
|
||||
if (value === this._glowTail) return
|
||||
this._glowTail = value
|
||||
this.requestRender()
|
||||
}
|
||||
|
||||
set outerGlowTail(value: number) {
|
||||
if (value === this._outerGlowTail) return
|
||||
this._outerGlowTail = value
|
||||
this.requestRender()
|
||||
set edgeLayer(value: TabPulseLayer) {
|
||||
if (!this.adjacentAssigned) {
|
||||
this.adjacentAssigned = true
|
||||
this.adjacent = new PulseLayer(value, this._enabled && this._edge !== undefined)
|
||||
this.changed()
|
||||
return
|
||||
}
|
||||
if (this.adjacent.set(value)) this.changed()
|
||||
}
|
||||
|
||||
set edge(value: "above" | "below" | undefined) {
|
||||
if (value === this._edge) return
|
||||
this._edge = value
|
||||
this.outer.setEnabled(this._enabled && value !== undefined)
|
||||
this.live = this.inner.live || this.outer.live
|
||||
this.requestRender()
|
||||
}
|
||||
|
||||
set flashColor(value: RGBA) {
|
||||
if (value.equals(this._flashColor)) return
|
||||
this._flashColor = value
|
||||
this.requestRender()
|
||||
}
|
||||
|
||||
set outerFlashColor(value: RGBA) {
|
||||
if (value.equals(this._outerFlashColor)) return
|
||||
this._outerFlashColor = value
|
||||
this.requestRender()
|
||||
}
|
||||
|
||||
set completionColor(value: RGBA) {
|
||||
if (value.equals(this._completionColor)) return
|
||||
this._completionColor = value
|
||||
this.requestRender()
|
||||
}
|
||||
|
||||
set outerCompletionColor(value: RGBA) {
|
||||
if (value.equals(this._outerCompletionColor)) return
|
||||
this._outerCompletionColor = value
|
||||
this.requestRender()
|
||||
this.adjacent.setEnabled(this._enabled && value !== undefined)
|
||||
this.changed()
|
||||
}
|
||||
|
||||
set backgroundColor(value: RGBA) {
|
||||
@@ -519,38 +523,43 @@ class TabPulseRenderable extends Renderable {
|
||||
this.requestRender()
|
||||
}
|
||||
|
||||
private changed() {
|
||||
this.live = this.primary.timeline.live || this.adjacent.timeline.live
|
||||
this.requestRender()
|
||||
}
|
||||
|
||||
protected override onUpdate(deltaTime: number): void {
|
||||
if (!this._enabled) return
|
||||
this.inner.advance(deltaTime)
|
||||
this.outer.advance(deltaTime)
|
||||
this.live = this.inner.live || this.outer.live
|
||||
this.primary.timeline.advance(deltaTime, this.ctx.frameId)
|
||||
this.adjacent.timeline.advance(deltaTime, this.ctx.frameId)
|
||||
this.live = this.primary.timeline.live || this.adjacent.timeline.live
|
||||
}
|
||||
|
||||
protected override renderSelf(buffer: OptimizedBuffer): void {
|
||||
if (!this.visible || this.isDestroyed || this.width <= 0) return
|
||||
const running = this.inner.running
|
||||
const completion = this.inner.completion
|
||||
const flash = this.inner.flash
|
||||
const glowLevel = this.inner.glowLevel
|
||||
const outerRunning = this.outer.running
|
||||
const outerCompletion = this.outer.completion
|
||||
const outerFlash = this.outer.flash
|
||||
const outerGlowLevel = this.outer.glowLevel
|
||||
const running = this.primary.timeline.running
|
||||
const completion = this.primary.timeline.completion
|
||||
const flash = this.primary.timeline.flash
|
||||
const glowLevel = this.primary.timeline.glowLevel
|
||||
const adjacentRunning = this.adjacent.timeline.running
|
||||
const adjacentCompletion = this.adjacent.timeline.completion
|
||||
const adjacentFlash = this.adjacent.timeline.flash
|
||||
const adjacentGlowLevel = this.adjacent.timeline.glowLevel
|
||||
if (
|
||||
glowLevel === 0 &&
|
||||
running === 0 &&
|
||||
completion === 0 &&
|
||||
flash === 0 &&
|
||||
outerGlowLevel === 0 &&
|
||||
outerRunning === 0 &&
|
||||
outerCompletion === 0 &&
|
||||
outerFlash === 0
|
||||
adjacentGlowLevel === 0 &&
|
||||
adjacentRunning === 0 &&
|
||||
adjacentCompletion === 0 &&
|
||||
adjacentFlash === 0
|
||||
) {
|
||||
this.emitLevel(0)
|
||||
return
|
||||
}
|
||||
const [front, secondFront] = this.inner.fronts(this.width)
|
||||
const [outerFront, outerSecondFront] = this.outer.fronts(this.width)
|
||||
const [front, secondFront] = this.primary.timeline.fronts(this.width)
|
||||
const [adjacentFront, adjacentSecondFront] = this.adjacent.timeline.fronts(this.width)
|
||||
if (this._onLevel)
|
||||
this.emitLevel(
|
||||
running === 0
|
||||
@@ -558,8 +567,8 @@ class TabPulseRenderable extends Renderable {
|
||||
: Math.max(intensityAt(1, front, RUN_HEAD, RUN_TAIL), intensityAt(1, secondFront, RUN_HEAD, RUN_TAIL)) *
|
||||
running,
|
||||
)
|
||||
const glowTail = Math.min(this._glowTail, Math.max(1, this.width - 2))
|
||||
const outerGlowTail = Math.min(this._outerGlowTail, Math.max(1, this.width - 2))
|
||||
const glowTail = Math.min(this.primary.glowTail, Math.max(1, this.width - 2))
|
||||
const adjacentGlowTail = Math.min(this.adjacent.glowTail, Math.max(1, this.width - 2))
|
||||
for (let index = 0; index < this.width; index++) {
|
||||
// Skip per-cell sweep and glow math when that stage is idle, e.g. a steady breathing glow.
|
||||
const sweep =
|
||||
@@ -571,49 +580,49 @@ class TabPulseRenderable extends Renderable {
|
||||
) *
|
||||
0.14 *
|
||||
running
|
||||
const outerSweep =
|
||||
outerRunning === 0
|
||||
const adjacentSweep =
|
||||
adjacentRunning === 0
|
||||
? 0
|
||||
: Math.max(
|
||||
intensityAt(index, outerFront, RUN_HEAD, RUN_TAIL),
|
||||
intensityAt(index, outerSecondFront, RUN_HEAD, RUN_TAIL),
|
||||
intensityAt(index, adjacentFront, RUN_HEAD, RUN_TAIL),
|
||||
intensityAt(index, adjacentSecondFront, RUN_HEAD, RUN_TAIL),
|
||||
) *
|
||||
0.14 *
|
||||
outerRunning
|
||||
adjacentRunning
|
||||
blendTabPulseColor(
|
||||
this.renderColor,
|
||||
this.primaryRenderColor,
|
||||
this._backgroundColor,
|
||||
this._glowColor,
|
||||
this._color,
|
||||
this._flashColor,
|
||||
this._completionColor,
|
||||
this.primary.glowColor,
|
||||
this.primary.color,
|
||||
this.primary.flashColor,
|
||||
this.primary.completionColor,
|
||||
glowLevel === 0 ? 0 : glowIntensityAt(index, glowTail) * GLOW_OPACITY * glowLevel,
|
||||
sweep,
|
||||
flash,
|
||||
completion,
|
||||
)
|
||||
if (!this._edge) {
|
||||
buffer.setCell(this.screenX + index, this.screenY, " ", DEFAULT_FOREGROUND, this.renderColor)
|
||||
buffer.setCell(this.screenX + index, this.screenY, " ", DEFAULT_FOREGROUND, this.primaryRenderColor)
|
||||
continue
|
||||
}
|
||||
blendTabPulseColor(
|
||||
this.outerRenderColor,
|
||||
this.adjacentRenderColor,
|
||||
this._backgroundColor,
|
||||
this._outerGlowColor,
|
||||
this._outerColor,
|
||||
this._outerFlashColor,
|
||||
this._outerCompletionColor,
|
||||
outerGlowLevel === 0 ? 0 : glowIntensityAt(index, outerGlowTail) * GLOW_OPACITY * outerGlowLevel,
|
||||
outerSweep,
|
||||
outerFlash,
|
||||
outerCompletion,
|
||||
this.adjacent.glowColor,
|
||||
this.adjacent.color,
|
||||
this.adjacent.flashColor,
|
||||
this.adjacent.completionColor,
|
||||
adjacentGlowLevel === 0 ? 0 : glowIntensityAt(index, adjacentGlowTail) * GLOW_OPACITY * adjacentGlowLevel,
|
||||
adjacentSweep,
|
||||
adjacentFlash,
|
||||
adjacentCompletion,
|
||||
)
|
||||
buffer.setCell(
|
||||
this.screenX + index,
|
||||
this.screenY,
|
||||
this._edge === "above" ? "▄" : "▀",
|
||||
this.renderColor,
|
||||
this.outerRenderColor,
|
||||
this.primaryRenderColor,
|
||||
this.adjacentRenderColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -632,26 +641,8 @@ export function TabPulse(props: {
|
||||
width?: number
|
||||
edge?: "above" | "below"
|
||||
enabled?: boolean
|
||||
active: boolean
|
||||
outerActive?: boolean
|
||||
promptPulse?: number
|
||||
outerPromptPulse?: number
|
||||
complete?: boolean
|
||||
outerComplete?: boolean
|
||||
glow?: boolean
|
||||
outerGlow?: boolean
|
||||
breathe?: boolean
|
||||
outerBreathe?: boolean
|
||||
color: RGBA
|
||||
outerColor?: RGBA
|
||||
glowColor?: RGBA
|
||||
outerGlowColor?: RGBA
|
||||
glowTail?: number
|
||||
outerGlowTail?: number
|
||||
flashColor?: RGBA
|
||||
outerFlashColor?: RGBA
|
||||
completionColor?: RGBA
|
||||
outerCompletionColor?: RGBA
|
||||
layer: TabPulseLayer
|
||||
edgeLayer?: TabPulseLayer
|
||||
backgroundColor: RGBA
|
||||
onLevel?: (level: number) => void
|
||||
}) {
|
||||
@@ -663,26 +654,8 @@ export function TabPulse(props: {
|
||||
zIndex={0}
|
||||
width={props.width ?? "100%"}
|
||||
enabled={props.enabled ?? true}
|
||||
active={props.active}
|
||||
outerActive={props.outerActive ?? props.active}
|
||||
promptPulse={props.promptPulse ?? 0}
|
||||
outerPromptPulse={props.outerPromptPulse ?? props.promptPulse ?? 0}
|
||||
complete={props.complete ?? false}
|
||||
outerComplete={props.outerComplete ?? props.complete ?? false}
|
||||
glow={props.glow ?? false}
|
||||
outerGlow={props.outerGlow ?? props.glow ?? false}
|
||||
breathe={props.breathe ?? false}
|
||||
outerBreathe={props.outerBreathe ?? props.breathe ?? false}
|
||||
color={props.color}
|
||||
outerColor={props.outerColor ?? props.color}
|
||||
glowColor={props.glowColor ?? props.color}
|
||||
outerGlowColor={props.outerGlowColor ?? props.glowColor ?? props.color}
|
||||
glowTail={props.glowTail ?? GLOW_TAIL}
|
||||
outerGlowTail={props.outerGlowTail ?? props.glowTail ?? GLOW_TAIL}
|
||||
flashColor={props.flashColor ?? props.color}
|
||||
outerFlashColor={props.outerFlashColor ?? props.flashColor ?? props.outerColor ?? props.color}
|
||||
completionColor={props.completionColor ?? props.color}
|
||||
outerCompletionColor={props.outerCompletionColor ?? props.completionColor ?? props.outerColor ?? props.color}
|
||||
layer={props.layer}
|
||||
edgeLayer={props.edge === undefined ? undefined : (props.edgeLayer ?? props.layer)}
|
||||
backgroundColor={props.backgroundColor}
|
||||
onLevel={props.onLevel}
|
||||
/>
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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,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),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -20,10 +20,12 @@ test("a prompt pulse restarts the neutral edge flash while the tab remains busy"
|
||||
() => (
|
||||
<box width={8} height={1} backgroundColor={background}>
|
||||
<TabPulse
|
||||
active={true}
|
||||
promptPulse={promptPulse()}
|
||||
color={background}
|
||||
flashColor={flash}
|
||||
layer={{
|
||||
active: true,
|
||||
promptPulse: promptPulse(),
|
||||
color: background,
|
||||
flashColor: flash,
|
||||
}}
|
||||
backgroundColor={background}
|
||||
/>
|
||||
</box>
|
||||
|
||||
Reference in New Issue
Block a user