Sebastian hai 5 meses
pai
achega
38af99dcb4

+ 35 - 5
.opencode/plugins/tui-smoke.tsx

@@ -1,5 +1,5 @@
 /** @jsxImportSource @opentui/solid */
 /** @jsxImportSource @opentui/solid */
-import { useKeyboard, useTerminalDimensions } from "@opentui/solid"
+import { useKeyboard, useTerminalDimensions, type JSX } from "@opentui/solid"
 import { RGBA, VignetteEffect } from "@opentui/core"
 import { RGBA, VignetteEffect } from "@opentui/core"
 import type {
 import type {
   TuiKeybindSet,
   TuiKeybindSet,
@@ -615,7 +615,7 @@ const Modal = (props: {
   )
   )
 }
 }
 
 
-const home = (input: Cfg): TuiSlotPlugin => ({
+const home = (api: TuiPluginApi, input: Cfg) => ({
   slots: {
   slots: {
     home_logo(ctx) {
     home_logo(ctx) {
       const map = ctx.theme.current
       const map = ctx.theme.current
@@ -649,6 +649,36 @@ const home = (input: Cfg): TuiSlotPlugin => ({
         </box>
         </box>
       )
       )
     },
     },
+    home_prompt(ctx, value) {
+      const skin = look(ctx.theme.current)
+      type Prompt = (props: {
+        workspaceID?: string
+        hint?: JSX.Element
+        placeholders?: {
+          normal?: string[]
+          shell?: string[]
+        }
+      }) => JSX.Element
+      if (!("Prompt" in api.ui)) return null
+      const view = api.ui.Prompt
+      if (typeof view !== "function") return null
+      const Prompt = view as Prompt
+      const normal = [
+        `[SMOKE] route check for ${input.label}`,
+        "[SMOKE] confirm home_prompt slot override",
+        "[SMOKE] verify api.ui.Prompt rendering",
+      ]
+      const shell = ["printf '[SMOKE] home prompt\n'", "git status --short", "bun --version"]
+      const Hint = (
+        <box flexShrink={0} flexDirection="row" gap={1}>
+          <text fg={skin.muted}>
+            <span style={{ fg: skin.accent }}>•</span> smoke home prompt
+          </text>
+        </box>
+      )
+
+      return <Prompt workspaceID={value.workspace_id} hint={Hint} placeholders={{ normal, shell }} />
+    },
     home_bottom(ctx) {
     home_bottom(ctx) {
       const skin = look(ctx.theme.current)
       const skin = look(ctx.theme.current)
       const text = "extra content in the unified home bottom slot"
       const text = "extra content in the unified home bottom slot"
@@ -706,8 +736,8 @@ const block = (input: Cfg, order: number, title: string, text: string): TuiSlotP
   },
   },
 })
 })
 
 
-const slot = (input: Cfg): TuiSlotPlugin[] => [
-  home(input),
+const slot = (api: TuiPluginApi, input: Cfg): TuiSlotPlugin[] => [
+  home(api, input),
   block(input, 50, "Smoke above", "renders above internal sidebar blocks"),
   block(input, 50, "Smoke above", "renders above internal sidebar blocks"),
   block(input, 250, "Smoke between", "renders between internal sidebar blocks"),
   block(input, 250, "Smoke between", "renders between internal sidebar blocks"),
   block(input, 650, "Smoke below", "renders below internal sidebar blocks"),
   block(input, 650, "Smoke below", "renders below internal sidebar blocks"),
@@ -848,7 +878,7 @@ const tui: TuiPlugin = async (api, options, meta) => {
   ])
   ])
 
 
   reg(api, value, keys)
   reg(api, value, keys)
-  for (const item of slot(value)) {
+  for (const item of slot(api, value)) {
     api.slots.register(item)
     api.slots.register(item)
   }
   }
 }
 }

+ 14 - 14
bun.lock

@@ -338,8 +338,8 @@
         "@opencode-ai/sdk": "workspace:*",
         "@opencode-ai/sdk": "workspace:*",
         "@opencode-ai/util": "workspace:*",
         "@opencode-ai/util": "workspace:*",
         "@openrouter/ai-sdk-provider": "2.3.3",
         "@openrouter/ai-sdk-provider": "2.3.3",
-        "@opentui/core": "0.1.91",
-        "@opentui/solid": "0.1.91",
+        "@opentui/core": "0.1.92",
+        "@opentui/solid": "0.1.92",
         "@parcel/watcher": "2.5.1",
         "@parcel/watcher": "2.5.1",
         "@pierre/diffs": "catalog:",
         "@pierre/diffs": "catalog:",
         "@solid-primitives/event-bus": "1.1.2",
         "@solid-primitives/event-bus": "1.1.2",
@@ -428,16 +428,16 @@
         "zod": "catalog:",
         "zod": "catalog:",
       },
       },
       "devDependencies": {
       "devDependencies": {
-        "@opentui/core": "0.1.91",
-        "@opentui/solid": "0.1.91",
+        "@opentui/core": "0.1.92",
+        "@opentui/solid": "0.1.92",
         "@tsconfig/node22": "catalog:",
         "@tsconfig/node22": "catalog:",
         "@types/node": "catalog:",
         "@types/node": "catalog:",
         "@typescript/native-preview": "catalog:",
         "@typescript/native-preview": "catalog:",
         "typescript": "catalog:",
         "typescript": "catalog:",
       },
       },
       "peerDependencies": {
       "peerDependencies": {
-        "@opentui/core": ">=0.1.91",
-        "@opentui/solid": ">=0.1.91",
+        "@opentui/core": ">=0.1.92",
+        "@opentui/solid": ">=0.1.92",
       },
       },
       "optionalPeers": [
       "optionalPeers": [
         "@opentui/core",
         "@opentui/core",
@@ -1459,21 +1459,21 @@
 
 
     "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
     "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
 
 
-    "@opentui/core": ["@opentui/core@0.1.91", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.91", "@opentui/core-darwin-x64": "0.1.91", "@opentui/core-linux-arm64": "0.1.91", "@opentui/core-linux-x64": "0.1.91", "@opentui/core-win32-arm64": "0.1.91", "@opentui/core-win32-x64": "0.1.91", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-xkuBDChHix3lHESQZTWXnPi0c8aANtg0567te3Am2O9EB3V1afKYdOYRV7RrzC+VBNmkymD8dUN+jzLkEUnAEw=="],
+    "@opentui/core": ["@opentui/core@0.1.92", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.92", "@opentui/core-darwin-x64": "0.1.92", "@opentui/core-linux-arm64": "0.1.92", "@opentui/core-linux-x64": "0.1.92", "@opentui/core-win32-arm64": "0.1.92", "@opentui/core-win32-x64": "0.1.92", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-c+KdYAIH3M8n24RYaor+t7AQtKZ3l84L7xdP7DEaN4xtuYH8W08E6Gi+wUal4g+HSai3HS9irox68yFf0VPAxw=="],
 
 
-    "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.91", "", { "os": "darwin", "cpu": "arm64" }, "sha512-WlIMa832vyjHCJsteWtSDsTAOrOPw/LQjYXVPISwwKo5Puyyl9vWNsF+69eYEyFEh15u8JNNrOPK98nlXq8SOA=="],
+    "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.92", "", { "os": "darwin", "cpu": "arm64" }, "sha512-NX/qFRuc7My0pazyOrw9fdTXmU7omXcZzQuHcsaVnwssljaT52UYMrJ7mCKhSo69RhHw0lnGCymTorvz3XBdsA=="],
 
 
-    "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.91", "", { "os": "darwin", "cpu": "x64" }, "sha512-nFZgQrdGtEzf5GXg4YxtDzxHvSwAig2G4Qf6ySN6sU9f9eaB1NJNhOVYLNJHBVEs5qOamBee+nXYEtG6zInIFQ=="],
+    "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.92", "", { "os": "darwin", "cpu": "x64" }, "sha512-Zb4jn33hOf167llINKLniOabQIycs14LPOBZnQ6l4khbeeTPVJdG8gy9PhlAyIQygDKmRTFncVlP0RP+L6C7og=="],
 
 
-    "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.91", "", { "os": "linux", "cpu": "arm64" }, "sha512-vXAcHZaS3QzEXYyvM9KoE0juSOMPPPdNrV5Fo4HAbI5BXGCkMNQJoN0j0EzoO9xwfsO+EulRSHCLVTNkvI4n8Q=="],
+    "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.92", "", { "os": "linux", "cpu": "arm64" }, "sha512-4VA1A91OTMPJ3LkAyaxKEZVJsk5jIc3Kz0gV2vip8p2aGLPpYHHpkFZpXP/FyzsnJzoSGftBeA6ya1GKa5bkXg=="],
 
 
-    "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.91", "", { "os": "linux", "cpu": "x64" }, "sha512-rAJ9sOvvI9eoWHjVj6TLPDRqYPYISmfCm2TDxi67BO27+E7naJANHIIxMC7yhPAmwBof7plioL2lwl2UFXAoXw=="],
+    "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.92", "", { "os": "linux", "cpu": "x64" }, "sha512-tr7va8hfKS1uY+TBmulQBoBlwijzJk56K/U/L9/tbHfW7oJctqxPVwEFHIh1HDcOQ3/UhMMWGvMfeG6cFiK8/A=="],
 
 
-    "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.91", "", { "os": "win32", "cpu": "arm64" }, "sha512-teLe7uHvPnD/lOwTwZp2lUFfeT27dk6ZSLWk8hrhsAJ/Y0MyoaCUHAsg3nZ/p+I3pie5aZUR1f0vrJfaZ8ukJw=="],
+    "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.92", "", { "os": "win32", "cpu": "arm64" }, "sha512-34YM3uPtDjzUVeSnJWIK2J8mxyduzV7f3mYc4Hub0glNpUdM1jjzF2HvvvnrKK5ElzTsIcno3c3lOYT8yvG1Zg=="],
 
 
-    "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.91", "", { "os": "win32", "cpu": "x64" }, "sha512-Odx9S1NYp3I2jgy5aj5k3/wb3M+yChEK7k8UUxxFt4R37V1/um8n6Cxw4nfid6T2C45KDGJ/0BYe6lGugJlnSg=="],
+    "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.92", "", { "os": "win32", "cpu": "x64" }, "sha512-uk442kA2Vn0mmJHHqk5sPM+Zai/AN9sgl7egekhoEOUx2VK3gxftKsVlx2YVpCHTvTE/S+vnD2WpQaJk2SNjww=="],
 
 
-    "@opentui/solid": ["@opentui/solid@0.1.91", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.91", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.10", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.11" } }, "sha512-phqiOcmTgNy7aG7s3P6zyatrBc1f6DkuLDJmGqy6R9QuoS4Mn9MKdNQe6Ick03xRAZuaS6ZdG3kueNxIlUMTCA=="],
+    "@opentui/solid": ["@opentui/solid@0.1.92", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.92", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.10", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.11" } }, "sha512-0Sx1+6zRpmMJ5oDEY0JS9b9+eGd/Q0fPndNllrQNnp7w2FCjpXmvHdBdq+pFI6kFp01MHq2ZOkUU5zX5/9YMSQ=="],
 
 
     "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
     "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
 
 

+ 2 - 2
packages/opencode/package.json

@@ -102,8 +102,8 @@
     "@opencode-ai/sdk": "workspace:*",
     "@opencode-ai/sdk": "workspace:*",
     "@opencode-ai/util": "workspace:*",
     "@opencode-ai/util": "workspace:*",
     "@openrouter/ai-sdk-provider": "2.3.3",
     "@openrouter/ai-sdk-provider": "2.3.3",
-    "@opentui/core": "0.1.91",
-    "@opentui/solid": "0.1.91",
+    "@opentui/core": "0.1.92",
+    "@opentui/solid": "0.1.92",
     "@parcel/watcher": "2.5.1",
     "@parcel/watcher": "2.5.1",
     "@pierre/diffs": "catalog:",
     "@pierre/diffs": "catalog:",
     "@solid-primitives/event-bus": "1.1.2",
     "@solid-primitives/event-bus": "1.1.2",

+ 20 - 9
packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx

@@ -45,6 +45,10 @@ export type PromptProps = {
   ref?: (ref: PromptRef) => void
   ref?: (ref: PromptRef) => void
   hint?: JSX.Element
   hint?: JSX.Element
   showPlaceholder?: boolean
   showPlaceholder?: boolean
+  placeholders?: {
+    normal?: string[]
+    shell?: string[]
+  }
 }
 }
 
 
 export type PromptRef = {
 export type PromptRef = {
@@ -57,13 +61,16 @@ export type PromptRef = {
   submit(): void
   submit(): void
 }
 }
 
 
-const PLACEHOLDERS = ["Fix a TODO in the codebase", "What is the tech stack of this project?", "Fix broken tests"]
-const SHELL_PLACEHOLDERS = ["ls -la", "git status", "pwd"]
 const money = new Intl.NumberFormat("en-US", {
 const money = new Intl.NumberFormat("en-US", {
   style: "currency",
   style: "currency",
   currency: "USD",
   currency: "USD",
 })
 })
 
 
+function randomIndex(count: number) {
+  if (count <= 0) return 0
+  return Math.floor(Math.random() * count)
+}
+
 export function Prompt(props: PromptProps) {
 export function Prompt(props: PromptProps) {
   let input: TextareaRenderable
   let input: TextareaRenderable
   let anchor: BoxRenderable
   let anchor: BoxRenderable
@@ -83,6 +90,8 @@ export function Prompt(props: PromptProps) {
   const renderer = useRenderer()
   const renderer = useRenderer()
   const { theme, syntax } = useTheme()
   const { theme, syntax } = useTheme()
   const kv = useKV()
   const kv = useKV()
+  const list = createMemo(() => props.placeholders?.normal ?? [])
+  const shell = createMemo(() => props.placeholders?.shell ?? [])
 
 
   function promptModelWarning() {
   function promptModelWarning() {
     toast.show({
     toast.show({
@@ -152,7 +161,7 @@ export function Prompt(props: PromptProps) {
     interrupt: number
     interrupt: number
     placeholder: number
     placeholder: number
   }>({
   }>({
-    placeholder: Math.floor(Math.random() * PLACEHOLDERS.length),
+    placeholder: randomIndex(list().length),
     prompt: {
     prompt: {
       input: "",
       input: "",
       parts: [],
       parts: [],
@@ -166,7 +175,7 @@ export function Prompt(props: PromptProps) {
     on(
     on(
       () => props.sessionID,
       () => props.sessionID,
       () => {
       () => {
-        setStore("placeholder", Math.floor(Math.random() * PLACEHOLDERS.length))
+        setStore("placeholder", randomIndex(list().length))
       },
       },
       { defer: true },
       { defer: true },
     ),
     ),
@@ -801,12 +810,14 @@ export function Prompt(props: PromptProps) {
   })
   })
 
 
   const placeholderText = createMemo(() => {
   const placeholderText = createMemo(() => {
-    if (props.sessionID) return undefined
+    if (props.showPlaceholder === false) return undefined
     if (store.mode === "shell") {
     if (store.mode === "shell") {
-      const example = SHELL_PLACEHOLDERS[store.placeholder % SHELL_PLACEHOLDERS.length]
+      if (!shell().length) return undefined
+      const example = shell()[store.placeholder % shell().length]
       return `Run a command... "${example}"`
       return `Run a command... "${example}"`
     }
     }
-    return `Ask anything... "${PLACEHOLDERS[store.placeholder % PLACEHOLDERS.length]}"`
+    if (!list().length) return undefined
+    return `Ask anything... "${list()[store.placeholder % list().length]}"`
   })
   })
 
 
   const spinnerDef = createMemo(() => {
   const spinnerDef = createMemo(() => {
@@ -922,7 +933,7 @@ export function Prompt(props: PromptProps) {
                   }
                   }
                 }
                 }
                 if (e.name === "!" && input.visualCursor.offset === 0) {
                 if (e.name === "!" && input.visualCursor.offset === 0) {
-                  setStore("placeholder", Math.floor(Math.random() * SHELL_PLACEHOLDERS.length))
+                  setStore("placeholder", randomIndex(shell().length))
                   setStore("mode", "shell")
                   setStore("mode", "shell")
                   e.preventDefault()
                   e.preventDefault()
                   return
                   return
@@ -1097,7 +1108,7 @@ export function Prompt(props: PromptProps) {
           />
           />
         </box>
         </box>
         <box flexDirection="row" justifyContent="space-between">
         <box flexDirection="row" justifyContent="space-between">
-          <Show when={status().type !== "idle"} fallback={<text />}>
+          <Show when={status().type !== "idle"} fallback={props.hint ?? <text />}>
             <box
             <box
               flexDirection="row"
               flexDirection="row"
               gap={1}
               gap={1}

+ 14 - 0
packages/opencode/src/cli/cmd/tui/plugin/api.tsx

@@ -14,6 +14,7 @@ import { DialogAlert } from "../ui/dialog-alert"
 import { DialogConfirm } from "../ui/dialog-confirm"
 import { DialogConfirm } from "../ui/dialog-confirm"
 import { DialogPrompt } from "../ui/dialog-prompt"
 import { DialogPrompt } from "../ui/dialog-prompt"
 import { DialogSelect, type DialogSelectOption as SelectOption } from "../ui/dialog-select"
 import { DialogSelect, type DialogSelectOption as SelectOption } from "../ui/dialog-select"
+import { Prompt } from "../component/prompt"
 import type { useToast } from "../ui/toast"
 import type { useToast } from "../ui/toast"
 import { Installation } from "@/installation"
 import { Installation } from "@/installation"
 import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk/v2"
 import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk/v2"
@@ -287,6 +288,19 @@ export function createTuiApi(input: Input): TuiHostPluginApi {
           />
           />
         )
         )
       },
       },
+      Prompt(props) {
+        return (
+          <Prompt
+            workspaceID={props.workspaceID}
+            visible={props.visible}
+            disabled={props.disabled}
+            onSubmit={props.onSubmit}
+            hint={props.hint}
+            showPlaceholder={props.showPlaceholder}
+            placeholders={props.placeholders}
+          />
+        )
+      },
       toast(inputToast) {
       toast(inputToast) {
         input.toast.show({
         input.toast.show({
           title: inputToast.title,
           title: inputToast.title,

+ 18 - 9
packages/opencode/src/cli/cmd/tui/routes/home.tsx

@@ -15,6 +15,10 @@ import { TuiPluginRuntime } from "../plugin"
 
 
 // TODO: what is the best way to do this?
 // TODO: what is the best way to do this?
 let once = false
 let once = false
+const placeholder = {
+  normal: ["Fix a TODO in the codebase", "What is the tech stack of this project?", "Fix broken tests"],
+  shell: ["ls -la", "git status", "pwd"],
+}
 
 
 export function Home() {
 export function Home() {
   const sync = useSync()
   const sync = useSync()
@@ -49,11 +53,12 @@ export function Home() {
     </box>
     </box>
   )
   )
 
 
-  let prompt: PromptRef
+  let prompt: PromptRef | undefined
   const args = useArgs()
   const args = useArgs()
   const local = useLocal()
   const local = useLocal()
   onMount(() => {
   onMount(() => {
     if (once) return
     if (once) return
+    if (!prompt) return
     if (route.initialPrompt) {
     if (route.initialPrompt) {
       prompt.set(route.initialPrompt)
       prompt.set(route.initialPrompt)
       once = true
       once = true
@@ -69,6 +74,7 @@ export function Home() {
       () => sync.ready && local.model.ready,
       () => sync.ready && local.model.ready,
       (ready) => {
       (ready) => {
         if (!ready) return
         if (!ready) return
+        if (!prompt) return
         if (!args.prompt) return
         if (!args.prompt) return
         if (prompt.current?.input !== args.prompt) return
         if (prompt.current?.input !== args.prompt) return
         prompt.submit()
         prompt.submit()
@@ -89,14 +95,17 @@ export function Home() {
         </box>
         </box>
         <box height={1} minHeight={0} flexShrink={1} />
         <box height={1} minHeight={0} flexShrink={1} />
         <box width="100%" maxWidth={75} zIndex={1000} paddingTop={1} flexShrink={0}>
         <box width="100%" maxWidth={75} zIndex={1000} paddingTop={1} flexShrink={0}>
-          <Prompt
-            ref={(r) => {
-              prompt = r
-              promptRef.set(r)
-            }}
-            hint={Hint}
-            workspaceID={route.workspaceID}
-          />
+          <TuiPluginRuntime.Slot name="home_prompt" mode="replace" workspace_id={route.workspaceID}>
+            <Prompt
+              ref={(r) => {
+                prompt = r
+                promptRef.set(r)
+              }}
+              hint={Hint}
+              workspaceID={route.workspaceID}
+              placeholders={placeholder}
+            />
+          </TuiPluginRuntime.Slot>
         </box>
         </box>
         <TuiPluginRuntime.Slot name="home_bottom" />
         <TuiPluginRuntime.Slot name="home_bottom" />
         <box flexGrow={1} minHeight={0} />
         <box flexGrow={1} minHeight={0} />

+ 1 - 0
packages/opencode/test/fixture/tui-plugin.ts

@@ -231,6 +231,7 @@ export function createTuiPluginApi(opts: Opts = {}): HostPluginApi {
       DialogConfirm: () => null,
       DialogConfirm: () => null,
       DialogPrompt: () => null,
       DialogPrompt: () => null,
       DialogSelect: () => null,
       DialogSelect: () => null,
+      Prompt: () => null,
       toast: () => {},
       toast: () => {},
       dialog: {
       dialog: {
         replace: () => {
         replace: () => {

+ 4 - 4
packages/plugin/package.json

@@ -21,8 +21,8 @@
     "zod": "catalog:"
     "zod": "catalog:"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
-    "@opentui/core": ">=0.1.91",
-    "@opentui/solid": ">=0.1.91"
+    "@opentui/core": ">=0.1.92",
+    "@opentui/solid": ">=0.1.92"
   },
   },
   "peerDependenciesMeta": {
   "peerDependenciesMeta": {
     "@opentui/core": {
     "@opentui/core": {
@@ -33,8 +33,8 @@
     }
     }
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@opentui/core": "0.1.91",
-    "@opentui/solid": "0.1.91",
+    "@opentui/core": "0.1.92",
+    "@opentui/solid": "0.1.92",
     "@tsconfig/node22": "catalog:",
     "@tsconfig/node22": "catalog:",
     "@types/node": "catalog:",
     "@types/node": "catalog:",
     "typescript": "catalog:",
     "typescript": "catalog:",

+ 17 - 0
packages/plugin/src/tui.ts

@@ -135,6 +135,19 @@ export type TuiDialogSelectProps<Value = unknown> = {
   current?: Value
   current?: Value
 }
 }
 
 
+export type TuiPromptProps = {
+  workspaceID?: string
+  visible?: boolean
+  disabled?: boolean
+  onSubmit?: () => void
+  hint?: JSX.Element
+  showPlaceholder?: boolean
+  placeholders?: {
+    normal?: string[]
+    shell?: string[]
+  }
+}
+
 export type TuiToast = {
 export type TuiToast = {
   variant?: "info" | "success" | "warning" | "error"
   variant?: "info" | "success" | "warning" | "error"
   title?: string
   title?: string
@@ -279,6 +292,9 @@ export type TuiSidebarFileItem = {
 export type TuiSlotMap = {
 export type TuiSlotMap = {
   app: {}
   app: {}
   home_logo: {}
   home_logo: {}
+  home_prompt: {
+    workspace_id?: string
+  }
   home_bottom: {}
   home_bottom: {}
   sidebar_title: {
   sidebar_title: {
     session_id: string
     session_id: string
@@ -386,6 +402,7 @@ export type TuiPluginApi = {
     DialogConfirm: (props: TuiDialogConfirmProps) => JSX.Element
     DialogConfirm: (props: TuiDialogConfirmProps) => JSX.Element
     DialogPrompt: (props: TuiDialogPromptProps) => JSX.Element
     DialogPrompt: (props: TuiDialogPromptProps) => JSX.Element
     DialogSelect: <Value = unknown>(props: TuiDialogSelectProps<Value>) => JSX.Element
     DialogSelect: <Value = unknown>(props: TuiDialogSelectProps<Value>) => JSX.Element
+    Prompt: (props: TuiPromptProps) => JSX.Element
     toast: (input: TuiToast) => void
     toast: (input: TuiToast) => void
     dialog: TuiDialogStack
     dialog: TuiDialogStack
   }
   }