From e75ab280412092f7c6c38ae0e319e06c1450a862 Mon Sep 17 00:00:00 2001 From: Benjamin Toby Date: Fri, 23 Oct 2026 17:58:08 +0100 Subject: [PATCH] Open a computer chip in the viewer instead of switching to it A computer a bot names in a reply is now a button that opens it in the shared computer viewer modal, the bot's own computer included (sunken and checked, as before). Switching stays in the header's computers modal, so the chip has one job. A computer the bot can no longer use stays a plain name. --- .../computer-ref-chip.render.test.tsx | 82 +++++++++++++++++++ .../[bot_id]/(partials)/computer-ref-chip.tsx | 44 +++++----- 2 files changed, 102 insertions(+), 24 deletions(-) create mode 100644 src/pages/admin/bots/[bot_id]/(partials)/computer-ref-chip.render.test.tsx diff --git a/src/pages/admin/bots/[bot_id]/(partials)/computer-ref-chip.render.test.tsx b/src/pages/admin/bots/[bot_id]/(partials)/computer-ref-chip.render.test.tsx new file mode 100644 index 0000000..80dc430 --- /dev/null +++ b/src/pages/admin/bots/[bot_id]/(partials)/computer-ref-chip.render.test.tsx @@ -0,0 +1,82 @@ +import { describe, expect, test } from "bun:test"; +import { renderToString } from "react-dom/server"; +import type { BUN_SQLITE_COBBLE_COMPUTERS } from "@/db/types/db"; +import type { BotComputers } from "../(hooks)/use-bot-computers"; +import { BotComputersContext } from "../(data)/bot-computers-context"; +import ComputerRefChip from "./computer-ref-chip"; + +const main: BUN_SQLITE_COBBLE_COMPUTERS = { id: 1, name: "Main computer" }; +const sandbox: BUN_SQLITE_COBBLE_COMPUTERS = { id: 2, name: "SEO box" }; + +type RenderProps = { + computer_id: number; + computers: BUN_SQLITE_COBBLE_COMPUTERS[]; +}; + +function render({ computer_id, computers }: RenderProps) { + const value = makeComputers({ computers }); + + const html = renderToString( + + + , + ); + + return { html, value }; +} + +type MakeComputersProps = { + computers: BUN_SQLITE_COBBLE_COMPUTERS[]; +}; + +/** + * A computers context recording what the chip asked for: the viewer it + * opened and the switches it made. + */ +function makeComputers({ computers }: MakeComputersProps) { + const switches: number[] = []; + const opened: { computer_id: number; mode: string }[] = []; + + const value: BotComputers = { + computers, + switchComputer: async ({ computer_id }) => { + switches.push(computer_id); + }, + openComputer: (params) => { + opened.push(params); + }, + closeComputer: () => {}, + }; + + return Object.assign(value, { switches, opened }); +} + +describe("ComputerRefChip", () => { + test("opens the viewer in its browser and switches nothing", async () => { + const { html, value } = render({ + computer_id: 2, + computers: [main, sandbox], + }); + + expect(html).toContain("SEO box"); + // The chip's only job is opening, so no switch affordance survives. + expect(html).not.toContain("Switch to"); + expect(value.switches).toHaveLength(0); + }); + + test("the computer the bot is on is still a chip, sunken with a check", () => { + const { html } = render({ computer_id: 1, computers: [main, sandbox] }); + + expect(html).toContain("Main computer"); + expect(html).toContain(" { + const { html } = render({ computer_id: 9, computers: [main] }); + + expect(html).toContain("computer 9"); + expect(html).not.toContain(" c.id == computer_id); - const computer = computers[index]; + const computer = computers.find((c) => c.id == computer_id); const name = computer?.name || `computer ${computer_id}`; - if (!computer) { + if (!computer?.id) { return {name}; } - if (index == 0) { - return ( - - - {name} - - - ); - } + const current = computers[0]?.id == computer.id; return ( ); }