feat: reusable diagram templates — save a board, reuse it by id in future sessions#179
Merged
Conversation
…ture sessions Decouples a diagram's structure/styling from its data so a good diagram can be SAVED as a reusable template (a `tpl_…` id) and reused in independent/future sessions for new data. Reuse is AI-driven: a template stores the working diagram as a concrete EXAMPLE plus natural-language HINTS, and a later agent fetches it, reads the hints, authors fresh data, and pushes — there is no deterministic server-side "apply". Templates are a GLOBAL library, distinct from the shipped recipes: recipes are generic authoring-time starting patterns; templates are the user's own saved diagrams, addressed by id at runtime. - TemplateStore (mirrors ShareStore): each template is a Payload row in the reserved, never-servable `__templates__` collection, so it rides the existing persistence backend — reusable by id across sessions/containers with no new persistence code. - Server: POST /w/<wsid>/template (save a board — tokenless + wsid-gated, like clear/share, since the UI has no push token), GET /templates, GET/DELETE /template/<id>. __templates__ is blocked from being served. - CLI: `termchart template save|list|get|delete`. `get` prints the HINTS first, then the example content, so a consuming agent is guided before it adapts. - Viewer UI: a Templates button → a popover to save the current board (name + hints prefilled with a WHAT/DATA SLOTS/ADAPT/KEEP scaffold) and browse the library (copy id, view hints inline, delete). Hidden on the read-only demo. - Docs: a "Reusable templates (≠ recipes)" section + reuse workflow in the diagram-recipes SKILL and the diagram-remote command, with the hints scaffold. Tests: template-store.test (incl. a cross-session persistence round-trip — hydrate a fresh store from the saved rows and resolve the template by id), CLI template.test (8), and template.e2e (save in the UI → library → GET api → adapt+reuse → delete; __templates__ not servable), chained into the e2e CI.
ivanmkc
added a commit
that referenced
this pull request
Jun 11, 2026
…l GET/DELETE) (#180) Code review of #179 found the template library's read/delete routes were ORIGIN-rooted and ungated: they sat above the /w/<wsid> dispatch with no wsid and no token. Because GET /templates returned every template id+name, any anonymous caller (the public demo shares the deployment's origin) could enumerate the whole global library and DELETE /template/<id> to wipe it — the "unguessable id gates it" comment was false. Fix: move list/get/delete under /w/<wsid>/ so the unguessable workspace id is the capability (consistent with clear/share), and exclude the read-only demo (isReadOnlyWsid) so the public showcase can't enumerate or mutate the library. Create was already wsid-scoped + demo-blocked. The library stays GLOBAL (any of your workspaces sees every template — cross-session reuse by id is preserved); only the access path is now gated. - server.ts: remove the top-level /templates and /template/<id> routes; add GET /w/<wsid>/templates and GET|DELETE /w/<wsid>/template/<id>, both demo-excluded; fix the misleading comment. - cli/template.ts: list/get/delete now use ${base} (…/w/<wsid>) instead of deriving the origin — simpler and matches the new routes. - client/viewer.ts: the Templates popover fetches ${apiBase}/templates and /template/<id>. Tests: server.test.ts gains a "template library" suite (create/list/get/delete, missing-board 404, 405, demo 403/404, origin-rooted routes gone, __templates__ not servable); CLI test asserts the wsid-scoped paths; template.e2e asserts the origin routes are gone + demo can't enumerate. Full chain: server 76, CLI 8, e2e rich 62 / board-sort 12 / template 14. Co-authored-by: Ivan Cheung <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Decouples a diagram's structure/styling from its data so a good diagram can be saved as a reusable template (a
tpl_…id) and reused in independent/future sessions for new data.Reuse is AI-driven (per the chosen model): a template stores the working diagram as a concrete example plus natural-language hints, and a later agent does
template get <id>, reads the hints, authors fresh data, and pushes. No deterministic server-side "apply".Templates vs recipes
Complementary, not overlapping:
How it works
TemplateStore(mirrorsShareStore): each template is aPayloadrow in the reserved, never-servable__templates__collection, so it rides the existing persistence backend — reusable by id across sessions/containers with zero new persistence code.POST /w/<wsid>/template(save a board — tokenless + wsid-gated, like clear/share, since the UI holds no push token), globalGET /templates,GET /template/<id>,DELETE /template/<id>.__templates__blocked from serving.termchart template save|list|get|delete.getprints HINTS first, then the example content.WHAT / DATA SLOTS / ADAPT / KEEPscaffold) and browse the library (copy id, inline hints, delete). Hidden on the read-only demo so the public showcase doesn't expose the library.diagram-remotecommand.Verification
template-store.testwith a cross-session persistence round-trip — hydrate a fresh store from the saved rows, resolve the template by id → proves reuse in a new session), CLI 171 (incl. 8templatetests).test:e2e:nobuild→ rich 62/62, board-sort 12/12, new template 11/11 (save in the UI → library →GETapi → adapt + reuse renders → delete;__templates__not servable). Verified locally + screenshotted the popover.Notes
templatecommands reach users on the (user-gated) npm republish, and the recipe/skill docs on the (user-gated) plugin bump.