From cf063fd8aa904012d25f9e06a5c31d501ed4fb3a Mon Sep 17 00:00:00 2001 From: Matteo Benedetto Date: Wed, 23 Sep 2026 10:50:28 +0200 Subject: [PATCH] Replace fake TUI buttons with usage instructions --- README.md | 4 ++-- index.ts | 30 +++++++++++------------------- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 97f57cc..8cc0ac3 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # pi-bash-code-actions -A Pi extension that detects fenced `bash`, `sh`, `shell`, and `zsh` blocks in assistant replies and adds actions to the transcript: +A Pi extension that detects fenced `bash`, `sh`, `shell`, and `zsh` blocks in assistant replies and shows an informational note in the transcript. Use `/bash-blocks` to choose an action: - **Copy** the block to the system clipboard (`wl-copy`, `xclip`, or `xsel` on Linux; `pbcopy` on macOS; `clip` on Windows). - **Open terminal** with the block loaded as a temporary script. It prefers Konsole, then `$TERMINAL`, GNOME Terminal, and `x-terminal-emulator`. - **Execute** after showing the code in a Pi confirmation dialog. Execution takes place in the newly opened terminal. -The `/bash-blocks` command lets you select a detected block and choose an action. Existing session history is scanned when the extension starts. +The transcript note is informational; it is not clickable. The `/bash-blocks` command lets you select a detected block and choose an action. Existing session history is scanned when the extension starts. ## Safety diff --git a/index.ts b/index.ts index a0916a4..ed8bbc8 100644 --- a/index.ts +++ b/index.ts @@ -4,13 +4,12 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import type { AssistantMessage } from "@earendil-works/pi-ai"; import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent"; -import { Box, MouseRegion, Text, type TuiMouseEvent } from "@earendil-works/pi-tui"; +import { Text } from "@earendil-works/pi-tui"; const TYPE = "bash-code-actions"; const LANGS = new Set(["bash", "sh", "shell", "zsh"]); type Block = { index: number; language: string; code: string }; type Data = { sourceEntryId: string; cwd: string; blocks: Block[] }; -let current: ExtensionContext | undefined; function body(message: unknown): string { const parts = (message as AssistantMessage | undefined)?.content; @@ -74,20 +73,14 @@ async function action(kind: "copy" | "open" | "execute", block: Block, data: Dat function preview(code: string): string { const line = code.split("\n").find(s => s.trim())?.trim() || "(vuoto)"; return line.length > 65 ? `${line.slice(0, 62)}...` : line; } -function button(label: string, kind: "copy" | "open" | "execute", block: Block, data: Data, theme: any): MouseRegion { - return new MouseRegion(new Text(theme.fg(kind === "execute" ? "warning" : "accent", `[ ${label} ]`), 1, 0), (e: TuiMouseEvent) => { - if (e.type !== "click" || e.button !== "left") return undefined; - if (current) void action(kind, block, data, current); return { handled: true, render: false }; - }); -} -function render(data: Data, theme: any): Box { - const box = new Box(1, 0, s => theme.bg("customMessageBg", s)); - box.addChild(new Text(theme.fg("muted", "bash code actions"), 1, 0)); - for (const b of data.blocks) { - box.addChild(new Text(theme.fg("dim", `${b.index}. ${preview(b.code)}`), 1, 0)); - box.addChild(button("copia", "copy", b, data, theme)); box.addChild(button("terminale", "open", b, data, theme)); box.addChild(button("esegui", "execute", b, data, theme)); - } - return box; +function render(data: Data, theme: any): Text { + const count = data.blocks.length; + const lines = [ + theme.fg("muted", `${count} blocco${count === 1 ? "" : "chi"} shell rilevat${count === 1 ? "o" : "i"}.`), + theme.fg("dim", "Usa /bash-blocks per copiare, aprire nel terminale o eseguire con conferma."), + ...data.blocks.map(b => theme.fg("dim", `${b.index}. ${preview(b.code)}`)), + ]; + return new Text(lines.join("\n"), 1, 0); } function known(ctx: ExtensionContext): Data[] { return ctx.sessionManager.getBranch().filter((e: any) => e.type === "custom" && e.customType === TYPE).map((e: any) => e.data).filter((d: any) => d?.blocks); @@ -102,10 +95,9 @@ async function recover(pi: ExtensionAPI, ctx: ExtensionContext): Promise { } export default function (pi: ExtensionAPI) { pi.registerEntryRenderer(TYPE, (entry, _opts, theme) => { const d = entry.data as Data | undefined; return d?.blocks?.length ? render(d, theme) : undefined; }); - pi.on("session_start", async (_e, ctx) => { current = ctx; await recover(pi, ctx); }); - pi.on("session_shutdown", () => { current = undefined; }); + pi.on("session_start", async (_e, ctx) => { await recover(pi, ctx); }); pi.on("turn_end", (e, ctx) => { - current = ctx; if (e.message.role !== "assistant") return; + if (e.message.role !== "assistant") return; const blocks = parse(body(e.message)); if (blocks.length) pi.appendEntry(TYPE, { sourceEntryId: e.messageEntryId, cwd: ctx.cwd, blocks } satisfies Data); }); pi.registerCommand("bash-blocks", { description: "Gestisce i blocchi shell della sessione", handler: async (_args, ctx) => {