diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index fe1ec840..bcfdf46b 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -20,10 +20,10 @@ body: required: false - type: input - id: opencode-version + id: mimo-version attributes: - label: OpenCode version - description: What version of OpenCode are you using? + label: MiMo Code version + description: What version of MiMo Code are you using? validations: required: false diff --git a/packages/opencode/src/cli/cmd/tui/component/error-component.tsx b/packages/opencode/src/cli/cmd/tui/component/error-component.tsx index c74d3bbc..a8342bfd 100644 --- a/packages/opencode/src/cli/cmd/tui/component/error-component.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/error-component.tsx @@ -53,7 +53,7 @@ export function ErrorComponent(props: { ) } - issueURL.searchParams.set("opencode-version", InstallationVersion) + issueURL.searchParams.set("mimo-version", InstallationVersion) const copyIssueURL = () => { void Clipboard.copy(issueURL.toString()).then(() => { diff --git a/packages/opencode/src/cli/cmd/tui/ui/toast.tsx b/packages/opencode/src/cli/cmd/tui/ui/toast.tsx index 5b441ec0..a647100d 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/toast.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/toast.tsx @@ -1,5 +1,5 @@ -import { createContext, useContext, type ParentProps, Show } from "solid-js" -import { createStore } from "solid-js/store" +import { createContext, useContext, type ParentProps, Show, For } from "solid-js" +import { createStore, produce } from "solid-js/store" import { useTheme } from "@tui/context/theme" import { useTerminalDimensions } from "@opentui/solid" import { SplitBorder } from "../component/border" @@ -10,61 +10,100 @@ import { useLanguage } from "@tui/context/language" export type ToastOptions = z.infer +const MAX_TOASTS = 3 + +interface ToastEntry extends ToastOptions { + id: number +} + export function Toast() { const toast = useToast() const { theme } = useTheme() const dimensions = useTerminalDimensions() return ( - - {(current) => ( - - - - {current().title} + + {(entry, index) => { + const height = entry.title + ? (entry.message.length > 40 ? 4 : 3) + : (entry.message.length > 40 ? 3 : 2) + return ( + + + + {entry.title} + + + + {entry.message} - - - {current().message} - - - )} - + + ) + }} + ) } +let nextToastId = 0 + function init() { const [store, setStore] = createStore({ - currentToast: null as ToastOptions | null, + toasts: [] as ToastEntry[], }) const t = useLanguage().t - let timeoutHandle: NodeJS.Timeout | null = null + const timeoutHandles = new Map() + + function removeToast(id: number) { + const handle = timeoutHandles.get(id) + if (handle) { + clearTimeout(handle) + timeoutHandles.delete(id) + } + setStore(produce((draft: { toasts: ToastEntry[] }) => { + const idx = draft.toasts.findIndex(t => t.id === id) + if (idx !== -1) draft.toasts.splice(idx, 1) + })) + } const toast = { show(options: ToastOptions) { - const { duration = 5000, ...currentToast } = options - setStore("currentToast", currentToast) - if (timeoutHandle) clearTimeout(timeoutHandle) - timeoutHandle = setTimeout(() => { - setStore("currentToast", null) - }, duration).unref() + const { duration = 5000, ...toastOpts } = options + const id = nextToastId++ + const entry: ToastEntry = { ...toastOpts, id } + + // Evict oldest toasts if at capacity + const current = store.toasts + while (current.length >= MAX_TOASTS) { + const removed = current.shift()! + const handle = timeoutHandles.get(removed.id) + if (handle) { + clearTimeout(handle) + timeoutHandles.delete(removed.id) + } + } + current.push(entry) + setStore("toasts", [...current]) + + timeoutHandles.set(id, setTimeout(() => { + removeToast(id) + }, duration).unref()) }, error: (err: any) => { if (err instanceof Error) @@ -77,8 +116,11 @@ function init() { message: t("tui.toast.unknown_error"), }) }, + get toasts() { + return store.toasts + }, get currentToast(): ToastOptions | null { - return store.currentToast + return store.toasts[store.toasts.length - 1] ?? null }, } return toast