feat(canvas): collapse canvas generation instructions into a clickable chip#2883
Conversation
…e chip The freeform canvas generation prompt dumped its authoring contract + publishing/data boilerplate inline, so the user message showed all of it as plain text. Wrap that boilerplate in a `<canvas_generation_instructions>` element and collapse it into a clickable chip (mirroring the channel CONTEXT.md treatment); clicking it opens a read-only snapshot in the right-side split. The user's own instruction now leads the visible message. The block is always stripped so the raw XML never leaks to flag-off viewers; the chip itself is gated behind project-bluebird. Generated-By: PostHog Code Task-Id: 933900d5-c8d2-449e-8ea9-79861613a103
|
React Doctor found no issues in the changed files. 🎉 Reviewed by React Doctor for commit |
|
Reviews (1): Last reviewed commit: "feat(canvas): collapse canvas generation..." | Re-trigger Greptile |
| const match = CANVAS_INSTRUCTIONS_REGEX.exec(content); | ||
| if (match?.index === undefined) return null; |
There was a problem hiding this comment.
The optional-chain guard
match?.index === undefined is testing for two conditions simultaneously — match being null and match.index being undefined — but index is never undefined on a non-null RegExpExecArray. A plain if (!match) is equivalent and clearer.
| const match = CANVAS_INSTRUCTIONS_REGEX.exec(content); | |
| if (match?.index === undefined) return null; | |
| const match = CANVAS_INSTRUCTIONS_REGEX.exec(content); | |
| if (!match) return null; |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| it("extracts the body and strips the element from the text", () => { | ||
| const content = | ||
| "What the user wants:\nadd a retention chart\n\n<canvas_generation_instructions>\nauthoring contract here\n</canvas_generation_instructions>"; | ||
| const result = extractCanvasInstructions(content); | ||
| expect(result).not.toBeNull(); | ||
| expect(result?.body).toBe("authoring contract here"); | ||
| expect(result?.stripped).toBe( | ||
| "What the user wants:\nadd a retention chart", | ||
| ); | ||
| expect(hasCanvasInstructions(content)).toBe(true); | ||
| }); | ||
|
|
||
| it("strips the element even when it is the only content", () => { | ||
| const result = extractCanvasInstructions( | ||
| "<canvas_generation_instructions>\nbody\n</canvas_generation_instructions>", | ||
| ); | ||
| expect(result?.body).toBe("body"); | ||
| expect(result?.stripped).toBe(""); | ||
| }); |
There was a problem hiding this comment.
The two extraction tests share exactly the same assertion shape (input →
body + stripped). Prefer a parameterized it.each table per the repo's simplicity rules — OnceAndOnlyOnce for the test structure, and easier to extend with edge cases.
| it("extracts the body and strips the element from the text", () => { | |
| const content = | |
| "What the user wants:\nadd a retention chart\n\n<canvas_generation_instructions>\nauthoring contract here\n</canvas_generation_instructions>"; | |
| const result = extractCanvasInstructions(content); | |
| expect(result).not.toBeNull(); | |
| expect(result?.body).toBe("authoring contract here"); | |
| expect(result?.stripped).toBe( | |
| "What the user wants:\nadd a retention chart", | |
| ); | |
| expect(hasCanvasInstructions(content)).toBe(true); | |
| }); | |
| it("strips the element even when it is the only content", () => { | |
| const result = extractCanvasInstructions( | |
| "<canvas_generation_instructions>\nbody\n</canvas_generation_instructions>", | |
| ); | |
| expect(result?.body).toBe("body"); | |
| expect(result?.stripped).toBe(""); | |
| }); | |
| it.each([ | |
| { | |
| label: "extracts the body and strips the element from the text", | |
| content: | |
| "What the user wants:\nadd a retention chart\n\n<canvas_generation_instructions>\nauthoring contract here\n</canvas_generation_instructions>", | |
| expectedBody: "authoring contract here", | |
| expectedStripped: "What the user wants:\nadd a retention chart", | |
| }, | |
| { | |
| label: "strips the element even when it is the only content", | |
| content: | |
| "<canvas_generation_instructions>\nbody\n</canvas_generation_instructions>", | |
| expectedBody: "body", | |
| expectedStripped: "", | |
| }, | |
| ])("$label", ({ content, expectedBody, expectedStripped }) => { | |
| const result = extractCanvasInstructions(content); | |
| expect(result).not.toBeNull(); | |
| expect(result?.body).toBe(expectedBody); | |
| expect(result?.stripped).toBe(expectedStripped); | |
| expect(hasCanvasInstructions(content)).toBe(true); | |
| }); |
Context Used: Do not attempt to comment on incorrect alphabetica... (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
…he prompt The instructions block opened with "Build a freeform React canvas …", which reads as a self-contained task and could lead the agent to under-weight the bare user instruction that now leads the message. Add a pointer in the header (inside the tag, so it stays hidden behind the chip) tying it back to the user's request. Generated-By: PostHog Code Task-Id: 933900d5-c8d2-449e-8ea9-79861613a103
There was a problem hiding this comment.
Clean UI feature that follows the existing channel-context chip pattern. The rename of openContextInSplit → openReadonlyTabInSplit is a safe internal refactor. The bot's inline comment about match?.index === undefined vs !match is a stylistic note — both are functionally equivalent since RegExpExecArray.index is always a number. Tests are included, the feature is flag-gated, and no data models or API contracts are broken.
|
Problem
When generating a freeform canvas, the agent's first user message dumped the entire canvas authoring contract + publishing/data rules inline as plain text — boilerplate the user never typed, drowning out their actual request.
Why: In a project-bluebird channel, those generation instructions should read like the CONTEXT.md info — a small chip, not a wall of prompt text — and open in the right panel when clicked.
Changes
<canvas_generation_instructions>element; the user's instruction now leads the visible message.UserMessagecollapses that element into a clickable "Canvas instructions" chip (mirroring the channel CONTEXT.md chip). Clicking it opens a read-only snapshot in the right-side split via a newcanvas-instructionstab. The block is always stripped so raw XML never leaks; the chip is gated behindproject-bluebird.openContextInSplittransform intoopenReadonlyTabInSplitso both chips share it.How did you test this?
@posthog/core+@posthog/uitypecheck clean.pnpm vitest run— newcanvasInstructions+freeformPrompttests and updatedUserMessagetests pass (10), plus existing core panel tests (6).Automatic notifications
Created with PostHog Code