From a92f44eb3d0ee725597d5ee2cf824dfc48260597 Mon Sep 17 00:00:00 2001 From: My Name is Tito Date: Thu, 22 Oct 2026 11:31:34 +1300 Subject: [PATCH] fix: mount force-input hint through TUI renderer --- .changeset/fbf210b6.md | 5 +++ README.md | 8 +++-- .../__tests__/plugin.test.ts | 10 ++---- .../src/{index.ts => index.tsx} | 36 ++++++++++--------- .../opencode-force-input/tsdown.config.ts | 2 +- 5 files changed, 33 insertions(+), 28 deletions(-) create mode 100644 .changeset/fbf210b6.md rename packages/opencode-force-input/src/{index.ts => index.tsx} (87%) diff --git a/.changeset/fbf210b6.md b/.changeset/fbf210b6.md new file mode 100644 index 0000000..893ede2 --- /dev/null +++ b/.changeset/fbf210b6.md @@ -0,0 +1,5 @@ +--- +"@mynameistito/opencode-force-input": patch +--- + +Mount footer UI through OpenTUI's renderer and register force-submit bindings once during setup diff --git a/README.md b/README.md index 564586d..7025c23 100644 --- a/README.md +++ b/README.md @@ -49,18 +49,20 @@ The documentation site source lives in [`apps/web/docs/`](apps/web/docs/). Start ## Local Development -Build the workspace and load the source entrypoints from `~/.config/opencode/cli.json`: +Build the packages you want to test, then add their package directories to your TUI config (`~/.config/opencode/cli.json` on this machine). Preserve the rest of your config and use absolute paths for your checkout: ```jsonc { "$schema": "https://opencode.ai/v2/cli.json", "plugins": [ - "/path/to/opencode-plugins/packages/opencode-force-input/src/index.ts", - "/path/to/opencode-plugins/packages/opencode-usage-limits/src/index.ts", + "/path/to/opencode-plugins/packages/opencode-force-input", + "/path/to/opencode-plugins/packages/opencode-usage-limits", ], } ``` +Restart OpenCode after editing the config. In a session, confirm the idle `⏎ send` hint appears, start a prompt and confirm it changes to the `steer` hint, then press `Ctrl+Enter` to verify the active run is interrupted and the prompt is submitted. + ## Development ```powershell diff --git a/packages/opencode-force-input/__tests__/plugin.test.ts b/packages/opencode-force-input/__tests__/plugin.test.ts index c2be82d..18d7f54 100644 --- a/packages/opencode-force-input/__tests__/plugin.test.ts +++ b/packages/opencode-force-input/__tests__/plugin.test.ts @@ -53,7 +53,7 @@ describe("force submit", () => { ]); }); - it("mounts a layer for each prompt footer render and dispatches force-submit", async () => { + it("registers one layer and dispatches force-submit from the prompt footer", async () => { const claims: SlotClaim[] = []; const layers: (() => KeymapLayer)[] = []; const dispatched: string[] = []; @@ -92,18 +92,14 @@ describe("force submit", () => { } const renderSetup = await testRender( - () => { - claim.render({ mode: "normal", showDetails: false }); - - return claim.render({ mode: "normal", showDetails: false }); - }, + () => claim.render({ mode: "normal", showDetails: false }), { height: 3, width: 70 } ); try { await renderSetup.flush(); - expect(layers).toHaveLength(2); + expect(layers).toHaveLength(1); const [layer] = layers; if (!layer) { diff --git a/packages/opencode-force-input/src/index.ts b/packages/opencode-force-input/src/index.tsx similarity index 87% rename from packages/opencode-force-input/src/index.ts rename to packages/opencode-force-input/src/index.tsx index befeaac..64bddb5 100644 --- a/packages/opencode-force-input/src/index.ts +++ b/packages/opencode-force-input/src/index.tsx @@ -1,6 +1,6 @@ +/* @jsxImportSource @opentui/solid */ import { Plugin } from "@opencode/plugin/tui"; import type { Context } from "@opencode/plugin/tui/context"; -import { createComponent } from "@opentui/solid"; import type { JSX } from "@opentui/solid"; import { ForceHint } from "./force-hint.tsx"; @@ -87,19 +87,17 @@ interface PromptFooterContributionProps { * same `prompt.footer.status` claim, so the binding and its affordance follow * TUI context changes together. */ -const promptFooterContribution = ( +const PromptFooterContribution = ( props: PromptFooterContributionProps -): JSX.Element => { - registerForceSubmitLayer(props.context.keymap); - - return createComponent(ForceHint, { - data: props.context.data, - enabled: hintEnabled(props.context.options), - mode: props.slot.mode, - sessionID: props.slot.sessionID, - theme: props.context.theme, - }); -}; +): JSX.Element => ( + +); /** * Initializes the OpenCode v2 TUI plugin and registers the composer hint. @@ -107,12 +105,16 @@ const promptFooterContribution = ( * @param context - Plugin context surfaces required by the setup lifecycle. * @returns A disposer that unregisters the UI slot when the plugin is stopped. */ -export const setup = (context: ForceSubmitContext): (() => void) => - context.ui.slot({ +export const setup = (context: ForceSubmitContext): (() => void) => { + registerForceSubmitLayer(context.keymap); + + return context.ui.slot({ append: "prompt.footer.status", - render: (slot) => - createComponent(promptFooterContribution, { context, slot }), + render: (slot) => ( + + ), }); +}; /** OpenCode v2 plugin entrypoint registering the force-input feature. */ export default Plugin.define({ diff --git a/packages/opencode-force-input/tsdown.config.ts b/packages/opencode-force-input/tsdown.config.ts index c7424a6..79353de 100644 --- a/packages/opencode-force-input/tsdown.config.ts +++ b/packages/opencode-force-input/tsdown.config.ts @@ -17,7 +17,7 @@ export default defineConfig({ dts: { sourcemap: true, }, - entry: ["src/index.ts"], + entry: ["src/index.tsx"], format: ["esm"], outDir: "dist", sourcemap: true,