mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-15 17:08:21 -04:00
fix(tui): preserve background hint timing (#42577)
This commit is contained in:
@@ -1530,9 +1530,13 @@ function BackgroundToolHint(props: { messages: SessionMessageInfo[] }) {
|
||||
return name === "shell" || name === "subagent"
|
||||
})
|
||||
if (!current || !part) return
|
||||
return `${current.id}:${part.id}`
|
||||
return { key: `${current.id}:${part.id}`, started: part.time.ran ?? part.time.created }
|
||||
})
|
||||
const visible = createDelayedPresence(running, BACKGROUND_TOOL_HINT_DELAY)
|
||||
const visible = createDelayedPresence(
|
||||
running,
|
||||
(tool) => Math.max(0, BACKGROUND_TOOL_HINT_DELAY - (Date.now() - tool.started)),
|
||||
(previous, next) => previous.key === next.key && previous.started === next.started,
|
||||
)
|
||||
return (
|
||||
<Show when={visible() && shortcut()}>
|
||||
{(value) => (
|
||||
|
||||
@@ -1,14 +1,32 @@
|
||||
import { createEffect, createSignal, onCleanup, type Accessor } from "solid-js"
|
||||
import { createEffect, createMemo, createSignal, onCleanup, type Accessor } from "solid-js"
|
||||
|
||||
export function createDelayedPresence<T>(source: Accessor<T | undefined>, delay: number) {
|
||||
export function createDelayedPresence<T>(
|
||||
source: Accessor<T | undefined>,
|
||||
delay: number | ((value: T) => number),
|
||||
equals?: (previous: T, next: T) => boolean,
|
||||
) {
|
||||
const [visible, setVisible] = createSignal(false)
|
||||
const value = equals
|
||||
? createMemo(source, undefined, {
|
||||
equals: (previous, next) => {
|
||||
if (previous === undefined || next === undefined) return previous === next
|
||||
return equals(previous, next)
|
||||
},
|
||||
})
|
||||
: source
|
||||
|
||||
createEffect(() => {
|
||||
const value = source()
|
||||
const current = value()
|
||||
setVisible(false)
|
||||
if (value === undefined) return
|
||||
if (current === undefined) return
|
||||
|
||||
const timer = setTimeout(() => setVisible(true), delay)
|
||||
const remaining = typeof delay === "function" ? delay(current) : delay
|
||||
if (remaining <= 0) {
|
||||
setVisible(true)
|
||||
return
|
||||
}
|
||||
|
||||
const timer = setTimeout(() => setVisible(true), remaining)
|
||||
onCleanup(() => clearTimeout(timer))
|
||||
})
|
||||
|
||||
|
||||
@@ -53,3 +53,62 @@ test("cancels the delay when the value disappears or the owner is disposed", asy
|
||||
jest.useRealTimers()
|
||||
}
|
||||
})
|
||||
|
||||
test("uses the remaining delay for the current value", async () => {
|
||||
jest.useFakeTimers()
|
||||
const scope = createRoot((dispose) => {
|
||||
const [value, setValue] = createSignal<{ age: number }>()
|
||||
return {
|
||||
dispose,
|
||||
setValue,
|
||||
visible: createDelayedPresence(value, (current) => Math.max(0, 1_000 - current.age)),
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
scope.setValue({ age: 400 })
|
||||
await Promise.resolve()
|
||||
jest.advanceTimersByTime(599)
|
||||
expect(scope.visible()).toBe(false)
|
||||
jest.advanceTimersByTime(1)
|
||||
expect(scope.visible()).toBe(true)
|
||||
|
||||
scope.setValue({ age: 1_000 })
|
||||
await Promise.resolve()
|
||||
expect(scope.visible()).toBe(true)
|
||||
} finally {
|
||||
scope.dispose()
|
||||
jest.useRealTimers()
|
||||
}
|
||||
})
|
||||
|
||||
test("does not restart the delay for an equivalent value", async () => {
|
||||
jest.useFakeTimers()
|
||||
const scope = createRoot((dispose) => {
|
||||
const [value, setValue] = createSignal<{ id: string }>()
|
||||
return {
|
||||
dispose,
|
||||
setValue,
|
||||
visible: createDelayedPresence(value, 1_000, (previous, next) => previous.id === next.id),
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
scope.setValue({ id: "first" })
|
||||
await Promise.resolve()
|
||||
jest.advanceTimersByTime(500)
|
||||
scope.setValue({ id: "first" })
|
||||
await Promise.resolve()
|
||||
jest.advanceTimersByTime(499)
|
||||
expect(scope.visible()).toBe(false)
|
||||
jest.advanceTimersByTime(1)
|
||||
expect(scope.visible()).toBe(true)
|
||||
|
||||
scope.setValue({ id: "second" })
|
||||
await Promise.resolve()
|
||||
expect(scope.visible()).toBe(false)
|
||||
} finally {
|
||||
scope.dispose()
|
||||
jest.useRealTimers()
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user