diff --git a/AGENTS.md b/AGENTS.md index 269795d1..5af4a7b2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -215,7 +215,7 @@ No need to explicitly document the telemetry behaviors. Nothing (Tailwind, DaisyUI, Mermaid, layout safety CSS) is auto-injected into artifacts; agents choose a design direction via the single-sourced `DESIGN_PRIORITY_RULE` (see AXI integration above). The `lavish-axi design` Mermaid snippet chooses dark or light rendering from the effective artifact page background and re-renders when a page-theme or OS appearance change alters that appearance, so do not hardcode one Mermaid theme. - For rendered Mermaid SVGs outside `.mermaid` containers, the injected SDK retains dependency-free viewBox pan (drag) and zoom (wheel) in explore mode, then freezes it in annotation mode so a click resolves cleanly to one node instead of panning. It enhances on load and `DOMContentLoaded` and re-runs through a throttled `MutationObserver` because Mermaid renders asynchronously and can re-render. Enhancement touches only the live SVG's `viewBox` and listeners, never the saved artifact, so the diagram still renders identically when opened directly. Node detection, label extraction, and target validation live in `src/mermaid-node.js` so they are unit-testable and shared with the server. -- Any helper `createArtifactSdk` calls must reach the browser through `serializeModuleHelpers` in `createSdkJs`, which turns every export of a shared module (`src/mermaid-node.js`, `src/table-cell.js`) into a same-scope `const`. A module-private function called from the SDK closure compiles fine and only `ReferenceError`s on the first click, so put new helpers in one of those wholesale-serialized modules and export them; a helper may then reference only its own arguments, browser globals, or its sibling exports - never a module-level constant, which is not serialized. Those modules must export functions and nothing else: only functions survive `toString()`, so `serializeModuleHelpers` throws on any other export rather than shipping a `Set` or `RegExp` that would arrive as an empty `{}`. `test/artifact-sdk-bundle.test.js` boots the served bundle and drives a real click, which is what catches an unreachable helper; the module-level unit tests cannot. +- Any helper `createArtifactSdk` calls must reach the browser as a same-scope `const` in `createSdkJs`. `src/artifact-sdk.js`'s own exports (everything except `createArtifactSdk` itself) are serialized automatically by iterating `Object.entries` of the module - a new exported helper needs no hand-kept entry in `src/server.js`. Helpers imported from the other shared modules (`src/mermaid-node.js`, `src/table-cell.js`) still go through `serializeModuleHelpers`, which turns every export of those modules into a same-scope `const`; a module-private function called from the SDK closure compiles fine and only `ReferenceError`s on the first click, so put new cross-module helpers in one of those wholesale-serialized modules and export them. Either way, a helper may reference only its own arguments, browser globals, or its sibling exports - never a module-level constant, which is not serialized. `serializeModuleHelpers` throws if `src/mermaid-node.js`/`src/table-cell.js` export anything but a function, since only functions survive `toString()`; the automatic `artifact-sdk.js` path additionally accepts non-function exports (JSON-stringified) for constants like `MODE_TOGGLE_HOTKEY_KEY`. `test/artifact-sdk-bundle.test.js` boots the served bundle and drives a real click, which is what catches an unreachable helper; the module-level unit tests cannot. - Table-cell annotations attach `src/table-cell.js`'s semantic row/column names as `target` only. The clicked element's own `selector`, `tag`, and `text` keep describing that element, because the on-screen highlight outlines exactly what was clicked. Both coordinates stay silent rather than name a row or column they cannot prove, because a confidently wrong name reads as authoritative and is worse than none: a rowspan is clipped to its own row group, so only one starting in an earlier row of that group (including `rowspan="0"`, which runs to the end of it) makes a row's DOM order stop being its rendered order, and that suppresses the row's positional heading - only a declared `scope="row"` heading survives it - while the column label needs the header row unshifted the same way, plus a row whose colspans sum to the header's and a cell that does not straddle a grouped header. A grouped header's `
Hello bright world
"); + + const store = new SessionStore(stateFile); + const session = await store.upsertSession(artifact, "http://localhost:4387/session/test"); + const target = { + type: "text-range", + text: "bright", + selector: "p#intro", + start: { selector: "p#intro", path: [0], offset: 6 }, + end: { selector: "p#intro", path: [0], offset: 12 }, + }; + await store.queuePrompts(session.key, { + prompts: [ + { id: "ann-2", uid: "", prompt: "Punch this up", selector: "p#intro", tag: "text", text: "bright", target }, + ], + }); + + const stored = await store.findByKey(session.key); + assert.equal(stored.annotations.length, 1); + assert.deepEqual(stored.annotations[0].target, target); + } finally { + await rm(dir, { recursive: true, force: true }); + } +}); + test("queued text selection prompts preserve range anchors", async () => { const dir = await mkdtemp(path.join(tmpdir(), "lavish-store-")); try {