Bots can now show a concept as a visual: create_graphic (with update_graphic, read_graphic and list_graphics, on every run, no computer needed) stores one HTML document in bot_graphics, per user like the conversation, and the chat renders its [graphic:ID] ref as an inline card sized to fit, with a large view and a page of its own (no rail). Graphic HTML is treated as hostile: it is only served at /graphics/<id> with a CSP sandbox allowing scripts under an origin of its own and no network, loading only the vendored Chart.js and Mermaid from /graphics-lib/. The document carries the app's theme vars and scrollbars, reports its height to the thread, and a frame that navigates away is closed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
47 lines
1.6 KiB
TypeScript
47 lines
1.6 KiB
TypeScript
import BunSQLite from "@moduletrace/bun-sqlite";
|
|
import AppError from "@/src/utils/app-error";
|
|
import { runGraphicsSql } from "@/src/utils/bot-graphic-sql";
|
|
import checkGraphicInput from "./check-graphic-input";
|
|
|
|
type Params = {
|
|
graphic_id: number | string;
|
|
/**
|
|
* The run's bot and user: only the graphics that bot made for that
|
|
* user are changed.
|
|
*/
|
|
bot_id: number | string;
|
|
user_id: number | string;
|
|
title?: string;
|
|
html?: string;
|
|
};
|
|
|
|
/**
|
|
* Replaces a graphic's HTML (and title, when given) and bumps its
|
|
* version. Every message naming it shows the new one. Returns its new
|
|
* version.
|
|
*/
|
|
export default async function updateBotGraphic({ graphic_id, bot_id, user_id, title, html }: Params) {
|
|
const checked = checkGraphicInput({ title, html });
|
|
|
|
if (!checked.title && !checked.html) throw new AppError(`Nothing to change: give new html or a title.`);
|
|
|
|
const reach = runGraphicsSql({ alias: "bot_graphics", user_id, bot_id });
|
|
|
|
const row = (
|
|
await BunSQLite.sql<{ version: number }>({
|
|
sql: `UPDATE bot_graphics SET
|
|
title = COALESCE(?, title),
|
|
html = COALESCE(?, html),
|
|
version = COALESCE(version, 1) + 1,
|
|
updated_at = ?
|
|
WHERE id = ? AND ${reach.sql}
|
|
RETURNING version`,
|
|
values: [checked.title ?? null, checked.html ?? null, Date.now(), Number(graphic_id), ...reach.values],
|
|
})
|
|
).payload?.[0];
|
|
|
|
if (!row) throw new AppError(`Graphic ${graphic_id} isn't one you made for this user.`);
|
|
|
|
return Number(row.version);
|
|
}
|