From 683d234d805e4d1097751d3cd583117856e41de5 Mon Sep 17 00:00:00 2001 From: Akshar Patel <123344143+AksharP5@users.noreply.github.com> Date: Thu, 5 Feb 2026 21:11:08 -0500 Subject: [PATCH] feat(tui): highlight esc label on hover in dialog (#12383) --- .../cli/cmd/tui/component/dialog-provider.tsx | 14 +++++++++++--- .../src/cli/cmd/tui/component/dialog-status.tsx | 16 ++++++++++++---- .../opencode/src/cli/cmd/tui/ui/dialog-alert.tsx | 15 ++++++++++++--- .../src/cli/cmd/tui/ui/dialog-confirm.tsx | 16 ++++++++++++---- .../src/cli/cmd/tui/ui/dialog-export-options.tsx | 16 ++++++++++++---- .../opencode/src/cli/cmd/tui/ui/dialog-help.tsx | 15 ++++++++++++--- .../src/cli/cmd/tui/ui/dialog-prompt.tsx | 16 ++++++++++++---- .../src/cli/cmd/tui/ui/dialog-select.tsx | 16 ++++++++++++---- 8 files changed, 95 insertions(+), 29 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx index 93e76cbdf..f8be5577b 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx @@ -124,6 +124,7 @@ function AutoMethod(props: AutoMethodProps) { const dialog = useDialog() const sync = useSync() const toast = useToast() + const [hover, setHover] = createSignal(false) useKeyboard((evt) => { if (evt.name === "c" && !evt.ctrl && !evt.meta) { @@ -154,9 +155,16 @@ function AutoMethod(props: AutoMethodProps) { {props.title} - dialog.clear()}> - esc - + setHover(true)} + onMouseOut={() => setHover(false)} + onMouseUp={() => dialog.clear()} + > + esc + diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-status.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-status.tsx index 3e6e30951..e2ab579a9 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-status.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-status.tsx @@ -2,7 +2,7 @@ import { TextAttributes } from "@opentui/core" import { useTheme } from "../context/theme" import { useDialog } from "@tui/ui/dialog" import { useSync } from "@tui/context/sync" -import { For, Match, Switch, Show, createMemo } from "solid-js" +import { For, Match, Switch, Show, createMemo, createSignal } from "solid-js" import { Installation } from "@/installation" export type DialogStatusProps = {} @@ -11,6 +11,7 @@ export function DialogStatus() { const sync = useSync() const { theme } = useTheme() const dialog = useDialog() + const [hover, setHover] = createSignal(false) const enabledFormatters = createMemo(() => sync.data.formatter.filter((f) => f.enabled)) @@ -45,9 +46,16 @@ export function DialogStatus() { Status - dialog.clear()}> - esc - + setHover(true)} + onMouseOut={() => setHover(false)} + onMouseUp={() => dialog.clear()} + > + esc + OpenCode v{Installation.VERSION} 0} fallback={No MCP Servers}> diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-alert.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-alert.tsx index 642c73b48..8b4b61476 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-alert.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-alert.tsx @@ -2,6 +2,7 @@ import { TextAttributes } from "@opentui/core" import { useTheme } from "../context/theme" import { useDialog, type DialogContext } from "./dialog" import { useKeyboard } from "@opentui/solid" +import { createSignal } from "solid-js" export type DialogAlertProps = { title: string @@ -12,6 +13,7 @@ export type DialogAlertProps = { export function DialogAlert(props: DialogAlertProps) { const dialog = useDialog() const { theme } = useTheme() + const [hover, setHover] = createSignal(false) useKeyboard((evt) => { if (evt.name === "return") { @@ -25,9 +27,16 @@ export function DialogAlert(props: DialogAlertProps) { {props.title} - dialog.clear()}> - esc - + setHover(true)} + onMouseOut={() => setHover(false)} + onMouseUp={() => dialog.clear()} + > + esc + {props.message} diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx index b86bd4325..7d9b74cde 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx @@ -2,7 +2,7 @@ import { TextAttributes } from "@opentui/core" import { useTheme } from "../context/theme" import { useDialog, type DialogContext } from "./dialog" import { createStore } from "solid-js/store" -import { For } from "solid-js" +import { createSignal, For } from "solid-js" import { useKeyboard } from "@opentui/solid" import { Locale } from "@/util/locale" @@ -16,6 +16,7 @@ export type DialogConfirmProps = { export function DialogConfirm(props: DialogConfirmProps) { const dialog = useDialog() const { theme } = useTheme() + const [hover, setHover] = createSignal(false) const [store, setStore] = createStore({ active: "confirm" as "confirm" | "cancel", }) @@ -37,9 +38,16 @@ export function DialogConfirm(props: DialogConfirmProps) { {props.title} - dialog.clear()}> - esc - + setHover(true)} + onMouseOut={() => setHover(false)} + onMouseUp={() => dialog.clear()} + > + esc + {props.message} diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-export-options.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-export-options.tsx index 1e8d09bb0..957467c5d 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-export-options.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-export-options.tsx @@ -2,7 +2,7 @@ import { TextareaRenderable, TextAttributes } from "@opentui/core" import { useTheme } from "../context/theme" import { useDialog, type DialogContext } from "./dialog" import { createStore } from "solid-js/store" -import { onMount, Show, type JSX } from "solid-js" +import { createSignal, onMount, Show, type JSX } from "solid-js" import { useKeyboard } from "@opentui/solid" export type DialogExportOptionsProps = { @@ -25,6 +25,7 @@ export function DialogExportOptions(props: DialogExportOptionsProps) { const dialog = useDialog() const { theme } = useTheme() let textarea: TextareaRenderable + const [hover, setHover] = createSignal(false) const [store, setStore] = createStore({ thinking: props.defaultThinking, toolDetails: props.defaultToolDetails, @@ -80,9 +81,16 @@ export function DialogExportOptions(props: DialogExportOptionsProps) { Export Options - dialog.clear()}> - esc - + setHover(true)} + onMouseOut={() => setHover(false)} + onMouseUp={() => dialog.clear()} + > + esc + diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-help.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-help.tsx index 4e4527930..f56347d4a 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-help.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-help.tsx @@ -3,11 +3,13 @@ import { useTheme } from "@tui/context/theme" import { useDialog } from "./dialog" import { useKeyboard } from "@opentui/solid" import { useKeybind } from "@tui/context/keybind" +import { createSignal } from "solid-js" export function DialogHelp() { const dialog = useDialog() const { theme } = useTheme() const keybind = useKeybind() + const [hover, setHover] = createSignal(false) useKeyboard((evt) => { if (evt.name === "return" || evt.name === "escape") { @@ -21,9 +23,16 @@ export function DialogHelp() { Help - dialog.clear()}> - esc/enter - + setHover(true)} + onMouseOut={() => setHover(false)} + onMouseUp={() => dialog.clear()} + > + esc/enter + diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx index b1b05a0f1..03814e17d 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx @@ -1,7 +1,7 @@ import { TextareaRenderable, TextAttributes } from "@opentui/core" import { useTheme } from "../context/theme" import { useDialog, type DialogContext } from "./dialog" -import { onMount, type JSX } from "solid-js" +import { createSignal, onMount, type JSX } from "solid-js" import { useKeyboard } from "@opentui/solid" export type DialogPromptProps = { @@ -17,6 +17,7 @@ export function DialogPrompt(props: DialogPromptProps) { const dialog = useDialog() const { theme } = useTheme() let textarea: TextareaRenderable + const [hover, setHover] = createSignal(false) useKeyboard((evt) => { if (evt.name === "return") { @@ -39,9 +40,16 @@ export function DialogPrompt(props: DialogPromptProps) { {props.title} - dialog.clear()}> - esc - + setHover(true)} + onMouseOut={() => setHover(false)} + onMouseUp={() => dialog.clear()} + > + esc + {props.description} diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx index 490a10072..7792900bc 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx @@ -1,7 +1,7 @@ import { InputRenderable, RGBA, ScrollBoxRenderable, TextAttributes } from "@opentui/core" import { useTheme, selectedForeground } from "@tui/context/theme" import { entries, filter, flatMap, groupBy, pipe, take } from "remeda" -import { batch, createEffect, createMemo, For, Show, type JSX, on } from "solid-js" +import { batch, createEffect, createMemo, createSignal, For, Show, type JSX, on } from "solid-js" import { createStore } from "solid-js/store" import { useKeyboard, useTerminalDimensions } from "@opentui/solid" import * as fuzzysort from "fuzzysort" @@ -49,6 +49,7 @@ export type DialogSelectRef = { export function DialogSelect(props: DialogSelectProps) { const dialog = useDialog() const { theme } = useTheme() + const [hover, setHover] = createSignal(false) const [store, setStore] = createStore({ selected: 0, filter: "", @@ -226,9 +227,16 @@ export function DialogSelect(props: DialogSelectProps) { {props.title} - dialog.clear()}> - esc - + setHover(true)} + onMouseOut={() => setHover(false)} + onMouseUp={() => dialog.clear()} + > + esc +