mirror of
https://github.com/sst/opencode.git
synced 2025-12-23 10:11:41 +00:00
opentui: feat: Implement POST /tui/toast-show (#3604)
This commit is contained in:
parent
2d0bc37a65
commit
54e2b1f399
6 changed files with 53 additions and 103 deletions
|
|
@ -25,6 +25,7 @@ import { DialogAlert } from "./ui/dialog-alert"
|
|||
import { ToastProvider, useToast } from "./ui/toast"
|
||||
import { ExitProvider } from "./context/exit"
|
||||
import type { SessionRoute } from "./context/route"
|
||||
import { TuiEvent } from "./event"
|
||||
|
||||
export function tui(input: {
|
||||
url: string
|
||||
|
|
@ -119,7 +120,7 @@ function App() {
|
|||
await sync.session.sync(data.sessionID).catch(() => {
|
||||
toast.show({
|
||||
message: `Session not found: ${data.sessionID}`,
|
||||
type: "error",
|
||||
variant: "error",
|
||||
})
|
||||
return route.navigate({ type: "home" })
|
||||
})
|
||||
|
|
@ -231,10 +232,19 @@ function App() {
|
|||
}
|
||||
})
|
||||
|
||||
event.on("tui.command.execute", (evt) => {
|
||||
event.on(TuiEvent.CommandExecute.type, (evt) => {
|
||||
command.trigger(evt.properties.command)
|
||||
})
|
||||
|
||||
event.on(TuiEvent.ToastShow.type, (evt) => {
|
||||
toast.show({
|
||||
title: evt.properties.title,
|
||||
message: evt.properties.message,
|
||||
variant: evt.properties.variant,
|
||||
duration: evt.properties.duration,
|
||||
})
|
||||
})
|
||||
|
||||
return (
|
||||
<box
|
||||
width={dimensions().width}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import { Editor } from "@tui/util/editor"
|
|||
import { useExit } from "../../context/exit"
|
||||
import { Clipboard } from "../../util/clipboard"
|
||||
import type { FilePart } from "@opencode-ai/sdk"
|
||||
import { TuiEvent } from "../../event"
|
||||
|
||||
export type PromptProps = {
|
||||
sessionID?: string
|
||||
|
|
@ -162,7 +163,7 @@ export function Prompt(props: PromptProps) {
|
|||
]
|
||||
})
|
||||
|
||||
sdk.event.on("tui.prompt.append", (evt) => {
|
||||
sdk.event.on(TuiEvent.PromptAppend.type, (evt) => {
|
||||
setStore(
|
||||
"prompt",
|
||||
produce((draft) => {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import { Global } from "@/global"
|
|||
import { iife } from "@/util/iife"
|
||||
import { createSimpleContext } from "./helper"
|
||||
import { useToast } from "../ui/toast"
|
||||
import type { Provider } from "@opencode-ai/sdk"
|
||||
|
||||
export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
||||
name: "Local",
|
||||
|
|
@ -40,7 +39,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||
const [providerID, modelID] = props.initialModel.split("/")
|
||||
if (!providerID || !modelID)
|
||||
return toast.show({
|
||||
type: "warning",
|
||||
variant: "warning",
|
||||
message: `Invalid model format: ${props.initialModel}`,
|
||||
duration: 3000,
|
||||
})
|
||||
|
|
@ -60,7 +59,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||
})
|
||||
else
|
||||
toast.show({
|
||||
type: "warning",
|
||||
variant: "warning",
|
||||
message: `Agent ${value.name}'s configured model ${value.model.providerID}/${value.model.modelID} is not valid`,
|
||||
duration: 3000,
|
||||
})
|
||||
|
|
@ -93,7 +92,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||
set(name: string) {
|
||||
if (!agents().some((x) => x.name === name))
|
||||
return toast.show({
|
||||
type: "warning",
|
||||
variant: "warning",
|
||||
message: `Agent not found: ${name}`,
|
||||
duration: 3000,
|
||||
})
|
||||
|
|
@ -211,7 +210,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||
if (!isModelValid(model)) {
|
||||
toast.show({
|
||||
message: `Model ${model.providerID}/${model.modelID} is not valid`,
|
||||
type: "warning",
|
||||
variant: "warning",
|
||||
duration: 3000,
|
||||
})
|
||||
return
|
||||
|
|
@ -274,4 +273,4 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||
}
|
||||
return result
|
||||
},
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Bus } from "@/bus"
|
||||
import z from "zod"
|
||||
import { Schema as ToastSchema } from "./ui/toast"
|
||||
|
||||
export const TuiEvent = {
|
||||
PromptAppend: Bus.event("tui.prompt.append", z.object({ text: z.string() })),
|
||||
|
|
@ -29,10 +30,6 @@ export const TuiEvent = {
|
|||
),
|
||||
ToastShow: Bus.event(
|
||||
"tui.toast.show",
|
||||
z.object({
|
||||
title: z.string().optional(),
|
||||
message: z.string(),
|
||||
variant: z.enum(["info", "success", "warning", "error"]),
|
||||
}),
|
||||
ToastSchema,
|
||||
),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,17 @@ import { createContext, useContext, type ParentProps, Show } from "solid-js"
|
|||
import { createStore } from "solid-js/store"
|
||||
import { useTheme } from "@tui/context/theme"
|
||||
import { SplitBorder } from "../component/border"
|
||||
import { TextAttributes } from "@opentui/core"
|
||||
import z from "zod"
|
||||
|
||||
export interface ToastOptions {
|
||||
message: string | null
|
||||
duration?: number
|
||||
type: "info" | "success" | "warning" | "error"
|
||||
}
|
||||
export const Schema = z.object({
|
||||
title: z.string().optional(),
|
||||
message: z.string(),
|
||||
variant: z.enum(["info", "success", "warning", "error"]),
|
||||
duration: z.number().default(5000).optional().describe("Duration in milliseconds"),
|
||||
})
|
||||
|
||||
export type ToastOptions = z.infer<typeof Schema>
|
||||
|
||||
export function Toast() {
|
||||
const toast = useToast()
|
||||
|
|
@ -19,7 +24,7 @@ export function Toast() {
|
|||
<box
|
||||
position="absolute"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
alignItems="flex-start"
|
||||
top={2}
|
||||
right={2}
|
||||
paddingLeft={2}
|
||||
|
|
@ -27,10 +32,13 @@ export function Toast() {
|
|||
paddingTop={1}
|
||||
paddingBottom={1}
|
||||
backgroundColor={theme.backgroundPanel}
|
||||
borderColor={theme[current().type]}
|
||||
borderColor={theme[current().variant]}
|
||||
border={["left", "right"]}
|
||||
customBorderChars={SplitBorder.customBorderChars}
|
||||
>
|
||||
<Show when={current().title}>
|
||||
<text attributes={TextAttributes.BOLD} marginBottom={1}>{current().title}</text>
|
||||
</Show>
|
||||
<text>{current().message}</text>
|
||||
</box>
|
||||
)}
|
||||
|
|
@ -47,12 +55,13 @@ function init() {
|
|||
|
||||
return {
|
||||
show(options: ToastOptions) {
|
||||
const { duration, ...currentToast } = options
|
||||
const parsedOptions = Schema.parse(options)
|
||||
const { duration, ...currentToast } = parsedOptions
|
||||
setStore("currentToast", currentToast)
|
||||
if (timeoutHandle) clearTimeout(timeoutHandle)
|
||||
timeoutHandle = setTimeout(() => {
|
||||
setStore("currentToast", null)
|
||||
}, duration ?? 5000).unref()
|
||||
}, duration).unref()
|
||||
},
|
||||
get currentToast(): ToastOptions | null {
|
||||
return store.currentToast
|
||||
|
|
|
|||
|
|
@ -18,10 +18,6 @@ export type KeybindsConfig = {
|
|||
* Leader key for keybind combinations
|
||||
*/
|
||||
leader?: string
|
||||
/**
|
||||
* Show help dialog
|
||||
*/
|
||||
app_help?: string
|
||||
/**
|
||||
* Exit the application
|
||||
*/
|
||||
|
|
@ -34,18 +30,6 @@ export type KeybindsConfig = {
|
|||
* List available themes
|
||||
*/
|
||||
theme_list?: string
|
||||
/**
|
||||
* Create/update AGENTS.md
|
||||
*/
|
||||
project_init?: string
|
||||
/**
|
||||
* Toggle tool details
|
||||
*/
|
||||
tool_details?: string
|
||||
/**
|
||||
* Toggle thinking blocks
|
||||
*/
|
||||
thinking_blocks?: string
|
||||
/**
|
||||
* Toggle sidebar
|
||||
*/
|
||||
|
|
@ -86,14 +70,6 @@ export type KeybindsConfig = {
|
|||
* Compact the session
|
||||
*/
|
||||
session_compact?: string
|
||||
/**
|
||||
* Cycle to next child session
|
||||
*/
|
||||
session_child_cycle?: string
|
||||
/**
|
||||
* Cycle to previous child session
|
||||
*/
|
||||
session_child_cycle_reverse?: string
|
||||
/**
|
||||
* Scroll messages up by one page
|
||||
*/
|
||||
|
|
@ -138,14 +114,6 @@ export type KeybindsConfig = {
|
|||
* List available commands
|
||||
*/
|
||||
command_list?: string
|
||||
/**
|
||||
* Next recent model
|
||||
*/
|
||||
model_cycle_recent?: string
|
||||
/**
|
||||
* Previous recent model
|
||||
*/
|
||||
model_cycle_recent_reverse?: string
|
||||
/**
|
||||
* List agents
|
||||
*/
|
||||
|
|
@ -174,54 +142,6 @@ export type KeybindsConfig = {
|
|||
* Insert newline in input
|
||||
*/
|
||||
input_newline?: string
|
||||
/**
|
||||
* @deprecated use agent_cycle. Next mode
|
||||
*/
|
||||
switch_mode?: string
|
||||
/**
|
||||
* @deprecated use agent_cycle_reverse. Previous mode
|
||||
*/
|
||||
switch_mode_reverse?: string
|
||||
/**
|
||||
* @deprecated use agent_cycle. Next agent
|
||||
*/
|
||||
switch_agent?: string
|
||||
/**
|
||||
* @deprecated use agent_cycle_reverse. Previous agent
|
||||
*/
|
||||
switch_agent_reverse?: string
|
||||
/**
|
||||
* @deprecated Currently not available. List files
|
||||
*/
|
||||
file_list?: string
|
||||
/**
|
||||
* @deprecated Close file
|
||||
*/
|
||||
file_close?: string
|
||||
/**
|
||||
* @deprecated Search file
|
||||
*/
|
||||
file_search?: string
|
||||
/**
|
||||
* @deprecated Split/unified diff
|
||||
*/
|
||||
file_diff_toggle?: string
|
||||
/**
|
||||
* @deprecated Navigate to previous message
|
||||
*/
|
||||
messages_previous?: string
|
||||
/**
|
||||
* @deprecated Navigate to next message
|
||||
*/
|
||||
messages_next?: string
|
||||
/**
|
||||
* @deprecated Toggle layout
|
||||
*/
|
||||
messages_layout_toggle?: string
|
||||
/**
|
||||
* @deprecated use messages_undo. Revert message
|
||||
*/
|
||||
messages_revert?: string
|
||||
}
|
||||
|
||||
export type AgentConfig = {
|
||||
|
|
@ -442,6 +362,9 @@ export type Config = {
|
|||
options?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
headers?: {
|
||||
[key: string]: string
|
||||
}
|
||||
provider?: {
|
||||
npm: string
|
||||
}
|
||||
|
|
@ -1009,6 +932,9 @@ export type Model = {
|
|||
options: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
headers?: {
|
||||
[key: string]: string
|
||||
}
|
||||
provider?: {
|
||||
npm: string
|
||||
}
|
||||
|
|
@ -1157,6 +1083,10 @@ export type EventTuiToastShow = {
|
|||
title?: string
|
||||
message: string
|
||||
variant: "info" | "success" | "warning" | "error"
|
||||
/**
|
||||
* Duration in milliseconds
|
||||
*/
|
||||
duration?: number
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2728,6 +2658,10 @@ export type TuiShowToastData = {
|
|||
title?: string
|
||||
message: string
|
||||
variant: "info" | "success" | "warning" | "error"
|
||||
/**
|
||||
* Duration in milliseconds
|
||||
*/
|
||||
duration?: number
|
||||
}
|
||||
path?: never
|
||||
query?: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue