diff --git a/packages/tui/src/ui/toast.tsx b/packages/tui/src/ui/toast.tsx index 0d3bf1a8501..8935be3eab8 100644 --- a/packages/tui/src/ui/toast.tsx +++ b/packages/tui/src/ui/toast.tsx @@ -1,7 +1,7 @@ import { createContext, createSignal, onCleanup, useContext, type ParentProps, Show } from "solid-js" import { createStore } from "solid-js/store" import { useTheme } from "../context/theme" -import { useTerminalDimensions } from "@opentui/solid" +import { useRenderer, useTerminalDimensions } from "@opentui/solid" import { SplitBorder } from "./border" import { TextAttributes } from "@opentui/core" import { tint } from "../theme/color" @@ -25,6 +25,7 @@ function ToastSurface(props: { }) { const theme = useTheme("overlay") const dimensions = useTerminalDimensions() + const renderer = useRenderer() const [hovered, setHovered] = createSignal(false) const hover = (value: boolean) => { setHovered(value) @@ -44,7 +45,10 @@ function ToastSurface(props: { customBorderChars={SplitBorder.customBorderChars} onMouseOver={() => hover(true)} onMouseOut={() => hover(false)} - onMouseUp={props.onActivate} + onMouseUp={() => { + if (renderer.getSelection()?.getSelectedText()) return + props.onActivate() + }} > { clear() - paused = false const next = store.queue[0] setStore("queue", (queue) => queue.slice(1)) setStore("currentToast", next ?? null) - if (next) start(next.duration) + if (!next) { + paused = false + return + } + remaining = next.duration + if (!paused) start(next.duration) } const toast = { diff --git a/packages/tui/test/cli/tui/toast.test.tsx b/packages/tui/test/cli/tui/toast.test.tsx index fda5ae5e686..6b04f68c27e 100644 --- a/packages/tui/test/cli/tui/toast.test.tsx +++ b/packages/tui/test/cli/tui/toast.test.tsx @@ -12,7 +12,7 @@ function captureToast(setToast: (toast: ToastContext) => void) { } } -test("clicking runs an action and advances a queued toast", async () => { +test("activation runs an action and keeps a queued toast paused", async () => { let toast: ToastContext | undefined const Capture = captureToast((value) => (toast = value)) const app = await testRender(() => ( @@ -30,7 +30,7 @@ test("clicking runs an action and advances a queued toast", async () => { action: { label: "Open plugins", run: () => (activated = true) }, }) toast!.pause() - toast!.show({ message: "Copied", variant: "success" }) + toast!.show({ message: "Copied", variant: "success", duration: 5 }) expect(toast!.currentToast?.message).toBe("Plugin failed") expect(toast!.pending).toBe(1) @@ -40,12 +40,19 @@ test("clicking runs an action and advances a queued toast", async () => { expect(activated).toBe(true) expect(toast!.currentToast?.message).toBe("Copied") expect(toast!.pending).toBe(0) + + await Bun.sleep(10) + expect(toast!.currentToast?.message).toBe("Copied") + + toast!.resume() + await Bun.sleep(10) + expect(toast!.currentToast).toBeNull() } finally { app.renderer.destroy() } }) -test("clicking a toast without an action dismisses it", async () => { +test("activation dismisses a toast without an action", async () => { let toast: ToastContext | undefined const Capture = captureToast((value) => (toast = value)) const app = await testRender(() => ( @@ -56,7 +63,7 @@ test("clicking a toast without an action dismisses it", async () => { try { await app.waitFor(() => toast !== undefined) - toast!.show({ message: "Copied", variant: "success" }) + toast!.show({ message: "Copied", variant: "success", duration: 5 }) toast!.activate() expect(toast!.currentToast).toBeNull() } finally {