fix: render TUI pickers as overlays
This commit is contained in:
@@ -22,6 +22,7 @@
|
|||||||
### Fixes
|
### Fixes
|
||||||
- Mac: pass auth token/password to dashboard URL for authenticated access. (#918) — thanks @rahthakor.
|
- Mac: pass auth token/password to dashboard URL for authenticated access. (#918) — thanks @rahthakor.
|
||||||
- UI: use application-defined WebSocket close code (browser compatibility). (#918) — thanks @rahthakor.
|
- UI: use application-defined WebSocket close code (browser compatibility). (#918) — thanks @rahthakor.
|
||||||
|
- TUI: render picker overlays via the overlay stack so /models and /settings display. (#921) — thanks @grizzdank.
|
||||||
- Gateway/Dev: ensure `pnpm gateway:dev` always uses the dev profile config + state (`~/.clawdbot-dev`).
|
- Gateway/Dev: ensure `pnpm gateway:dev` always uses the dev profile config + state (`~/.clawdbot-dev`).
|
||||||
- macOS: fix cron preview/testing payload to use `channel` key. (#867) — thanks @wes-davis.
|
- macOS: fix cron preview/testing payload to use `channel` key. (#867) — thanks @wes-davis.
|
||||||
- Telegram: honor `channels.telegram.timeoutSeconds` for grammY API requests. (#863) — thanks @Snaver.
|
- Telegram: honor `channels.telegram.timeoutSeconds` for grammY API requests. (#863) — thanks @Snaver.
|
||||||
|
|||||||
60
src/tui/tui-overlays.test.ts
Normal file
60
src/tui/tui-overlays.test.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import type { Component } from "@mariozechner/pi-tui";
|
||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import { createOverlayHandlers } from "./tui-overlays.js";
|
||||||
|
|
||||||
|
class DummyComponent implements Component {
|
||||||
|
render() {
|
||||||
|
return ["dummy"];
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidate() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("createOverlayHandlers", () => {
|
||||||
|
it("routes overlays through the TUI overlay stack", () => {
|
||||||
|
const showOverlay = vi.fn();
|
||||||
|
const hideOverlay = vi.fn();
|
||||||
|
const setFocus = vi.fn();
|
||||||
|
let open = false;
|
||||||
|
|
||||||
|
const host = {
|
||||||
|
showOverlay: (component: Component) => {
|
||||||
|
open = true;
|
||||||
|
showOverlay(component);
|
||||||
|
},
|
||||||
|
hideOverlay: () => {
|
||||||
|
open = false;
|
||||||
|
hideOverlay();
|
||||||
|
},
|
||||||
|
hasOverlay: () => open,
|
||||||
|
setFocus,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { openOverlay, closeOverlay } = createOverlayHandlers(host, new DummyComponent());
|
||||||
|
const overlay = new DummyComponent();
|
||||||
|
|
||||||
|
openOverlay(overlay);
|
||||||
|
expect(showOverlay).toHaveBeenCalledWith(overlay);
|
||||||
|
|
||||||
|
closeOverlay();
|
||||||
|
expect(hideOverlay).toHaveBeenCalledTimes(1);
|
||||||
|
expect(setFocus).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("restores focus when closing without an overlay", () => {
|
||||||
|
const setFocus = vi.fn();
|
||||||
|
const host = {
|
||||||
|
showOverlay: vi.fn(),
|
||||||
|
hideOverlay: vi.fn(),
|
||||||
|
hasOverlay: () => false,
|
||||||
|
setFocus,
|
||||||
|
};
|
||||||
|
const fallback = new DummyComponent();
|
||||||
|
|
||||||
|
const { closeOverlay } = createOverlayHandlers(host, fallback);
|
||||||
|
closeOverlay();
|
||||||
|
|
||||||
|
expect(setFocus).toHaveBeenCalledWith(fallback);
|
||||||
|
});
|
||||||
|
});
|
||||||
19
src/tui/tui-overlays.ts
Normal file
19
src/tui/tui-overlays.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import type { Component, TUI } from "@mariozechner/pi-tui";
|
||||||
|
|
||||||
|
type OverlayHost = Pick<TUI, "showOverlay" | "hideOverlay" | "hasOverlay" | "setFocus">;
|
||||||
|
|
||||||
|
export function createOverlayHandlers(host: OverlayHost, fallbackFocus: Component) {
|
||||||
|
const openOverlay = (component: Component) => {
|
||||||
|
host.showOverlay(component);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeOverlay = () => {
|
||||||
|
if (host.hasOverlay()) {
|
||||||
|
host.hideOverlay();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
host.setFocus(fallbackFocus);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { openOverlay, closeOverlay };
|
||||||
|
}
|
||||||
@@ -1,11 +1,4 @@
|
|||||||
import {
|
import { CombinedAutocompleteProvider, Container, ProcessTerminal, Text, TUI } from "@mariozechner/pi-tui";
|
||||||
CombinedAutocompleteProvider,
|
|
||||||
type Component,
|
|
||||||
Container,
|
|
||||||
ProcessTerminal,
|
|
||||||
Text,
|
|
||||||
TUI,
|
|
||||||
} from "@mariozechner/pi-tui";
|
|
||||||
import { resolveDefaultAgentId } from "../agents/agent-scope.js";
|
import { resolveDefaultAgentId } from "../agents/agent-scope.js";
|
||||||
import { loadConfig } from "../config/config.js";
|
import { loadConfig } from "../config/config.js";
|
||||||
import {
|
import {
|
||||||
@@ -22,6 +15,7 @@ import { editorTheme, theme } from "./theme/theme.js";
|
|||||||
import { createCommandHandlers } from "./tui-command-handlers.js";
|
import { createCommandHandlers } from "./tui-command-handlers.js";
|
||||||
import { createEventHandlers } from "./tui-event-handlers.js";
|
import { createEventHandlers } from "./tui-event-handlers.js";
|
||||||
import { formatTokens } from "./tui-formatters.js";
|
import { formatTokens } from "./tui-formatters.js";
|
||||||
|
import { createOverlayHandlers } from "./tui-overlays.js";
|
||||||
import { createSessionActions } from "./tui-session-actions.js";
|
import { createSessionActions } from "./tui-session-actions.js";
|
||||||
import type {
|
import type {
|
||||||
AgentSummary,
|
AgentSummary,
|
||||||
@@ -188,10 +182,8 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
const footer = new Text("", 1, 0);
|
const footer = new Text("", 1, 0);
|
||||||
const chatLog = new ChatLog();
|
const chatLog = new ChatLog();
|
||||||
const editor = new CustomEditor(editorTheme);
|
const editor = new CustomEditor(editorTheme);
|
||||||
const overlay = new Container();
|
|
||||||
const root = new Container();
|
const root = new Container();
|
||||||
root.addChild(header);
|
root.addChild(header);
|
||||||
root.addChild(overlay);
|
|
||||||
root.addChild(chatLog);
|
root.addChild(chatLog);
|
||||||
root.addChild(status);
|
root.addChild(status);
|
||||||
root.addChild(footer);
|
root.addChild(footer);
|
||||||
@@ -300,16 +292,7 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeOverlay = () => {
|
const { openOverlay, closeOverlay } = createOverlayHandlers(tui, editor);
|
||||||
overlay.clear();
|
|
||||||
tui.setFocus(editor);
|
|
||||||
};
|
|
||||||
|
|
||||||
const openOverlay = (component: Component) => {
|
|
||||||
overlay.clear();
|
|
||||||
overlay.addChild(component);
|
|
||||||
tui.setFocus(component);
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialSessionAgentId = (() => {
|
const initialSessionAgentId = (() => {
|
||||||
if (!initialSessionInput) return null;
|
if (!initialSessionInput) return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user