Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions .github/workflows/website.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ name: Website quality
on:
push:
branches: [master]
paths: ["website/**", ".github/workflows/website.yml"]
paths: ["website/**", "src/**", "agent/**", "style.css", "package*.json", "scripts/agent/**", ".github/workflows/website.yml"]
pull_request:
paths: ["website/**", ".github/workflows/website.yml"]
paths: ["website/**", "src/**", "agent/**", "style.css", "package*.json", "scripts/agent/**", ".github/workflows/website.yml"]
workflow_dispatch:

permissions:
contents: read

jobs:
quality:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
defaults:
run:
working-directory: website
Expand All @@ -24,10 +24,22 @@ jobs:
node-version: 22
cache: npm
cache-dependency-path: website/package-lock.json
- run: npm ci --ignore-scripts
working-directory: .
- run: npm run check
working-directory: .
- run: npm test -- tests/agent/showcase.test.ts
working-directory: .
- run: npm run build && npm run test:templates
working-directory: .
- run: npm ci
- run: npm run check
- run: npx playwright install --with-deps chromium
- run: npm test
- uses: actions/upload-artifact@v4
with:
name: static-showcase
path: website/dist/
- uses: actions/upload-artifact@v4
if: failure()
with:
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## Unreleased

- Add a unified website showcase with six live use cases, Product/UX/Engineering/QE
examples for workspace deletion, billing, search, security and import validation, a deterministic agent walkthrough, and
inspectable plans, actual source, canonical framework examples and verification scope.
- Add sticky `offsetX`/`offsetY` and arrow `labelOffsetX`/`labelOffsetY` pixel nudges
after automatic placement and before viewport clamping. Defaults preserve existing
placement. Core grows by 38 bytes gzip to 6.83 KiB; the 7 KiB guard is unchanged.
- Generate updated option contracts and snippets; add evidence-based handoff guidance
within existing skills without changing routing or adding a skill.
- Make playground code match its preview, including seed, motion and placement;
add reset and use canonical framework templates. Test the site against checkout
source, validate bundled plans and add desktop/mobile interaction and visual checks.

## 0.1.0 — 2026-09-06

- Add agent authoring tools alongside the typed runtime API with typed annotation plans, generated
Expand Down
30 changes: 24 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,22 @@ Hand-sketched annotations for live UI, built for coding agents and developers.
Add circles, highlights, arrows, notes, and proofreader marks while your
application keeps its controls and layout.

Agent and developer first: work with your coding agent through project skills,
validated plans and framework examples, or write annotations directly with the
typed JavaScript API. Both workflows use the same primitives and lifecycle.
[Live showcase](https://stetkit.com/use-cases) · [Agent workflow](https://stetkit.com/agent-workflow) · [Agent setup](docs/agent-usage.md)

**Developer:** `circle(button)` adds emphasis while your button keeps working.
**Agent:** “Annotate what changed and explain the risky parts.” A coding agent
can turn that request into a validated plan, framework code and a visual handoff.

Try [a dangerous settings action](https://stetkit.com/use-cases/workspace-deletion),
[an implementation handoff](https://stetkit.com/use-cases/security-handoff), or
[a reproducible form review](https://stetkit.com/use-cases/form-review).
Each has a working interface, annotation toggle, actual plan/source and explicit
verification scope. [Tutorials](https://stetkit.com/use-cases/guided-tutorial) and
[live documentation](https://stetkit.com/use-cases/live-documentation) work without an agent.

Distinct live examples show Product billing intent, UX search recovery and QE import checks.
Stet supplies the annotation layer; your existing tools own requirements, design
and tests. No collaboration backend, accounts or model API required.

The API is under active development and may change
before 1.0. Pin an exact version if you need predictable upgrades.
Expand Down Expand Up @@ -80,9 +93,13 @@ annotation.destroy(); // remove the annotation and its subscriptions
Still by default. Seeded when you need repeatability. Optional hover resketching
and stroke boil honor reduced-motion preferences.

To explore locally: `npm install`, then `python -m http.server 4173` and open
[the live demo](http://localhost:4173/examples/vanilla/) or
[the visual specimens](http://localhost:4173/examples/visual/).
To explore this checkout locally, run `npm ci`, `npm --prefix website ci`, then
`npm --prefix website run dev`. Open `/use-cases` or `/agent-workflow`.
The [playground](https://stetkit.com/playground) exposes real options and copyable code;
framework integration lives in [Docs](https://stetkit.com/docs).
See [website setup](website/README.md) for production builds and tests. Hosted
links show the deployed site; this checkout's additions appear after deployment.
The [fixed visual specimens](examples/visual/) remain the regression matrix.

## Dependencies and framework support

Expand All @@ -107,6 +124,7 @@ development dependencies and are not installed as dependencies in your app.
- [Explanation: how stet marks live UI](docs/explanation.md)
- [API reference](docs/reference.md)
- [Framework examples](examples/)
- [Use cases and visual handoff](docs/visual-handoff.md)
- [Agent usage](docs/agent-usage.md) and [architecture](docs/agent-architecture.md)
- [GitHub Packages and release process](docs/releases.md)

Expand Down
4 changes: 4 additions & 0 deletions agent/annotation-plan.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ export type AnnotationPlan = {
"options"?: {
"label"?: string;
"curvature"?: number;
"labelOffsetX"?: number;
"labelOffsetY"?: number;
"seed"?: number;
"roughness"?: number;
"boil"?: number;
Expand All @@ -108,6 +110,8 @@ export type AnnotationPlan = {
"options": {
"text": string;
"side"?: "auto" | "top" | "right" | "bottom" | "left";
"offsetX"?: number;
"offsetY"?: number;
"seed"?: number;
"roughness"?: number;
"boil"?: number;
Expand Down
18 changes: 17 additions & 1 deletion agent/capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,12 @@
"curvature": {
"type": "number"
},
"labelOffsetX": {
"type": "number"
},
"labelOffsetY": {
"type": "number"
},
"seed": {
"type": "number"
},
Expand Down Expand Up @@ -318,6 +324,8 @@
"defaults": {
"label": "absent",
"curvature": 0.16,
"labelOffsetX": 0,
"labelOffsetY": 0,
"seed": "random uint32",
"roughness": 1,
"boil": 0,
Expand Down Expand Up @@ -356,6 +364,12 @@
"left"
]
},
"offsetX": {
"type": "number"
},
"offsetY": {
"type": "number"
},
"seed": {
"type": "number"
},
Expand Down Expand Up @@ -391,6 +405,8 @@
},
"defaults": {
"side": "auto",
"offsetX": 0,
"offsetY": 0,
"seed": "random uint32",
"roughness": 1,
"boil": 0,
Expand Down Expand Up @@ -482,7 +498,7 @@
"motion": "Still by default; reduced motion disables boil and hover resketch live. Fixed seed, dimensions and options reproduce geometry.",
"lifecycle": "DOM Elements in the current document and document.body must exist. Options are snapshots. Handles expose refresh(), resketch(seed?), destroy().",
"targeting": "Targets are source-editing evidence, not executable selectors. Prove uniqueness and readiness in application code. Arrow order is from, to.",
"placement": "Body overlays; resize/scroll/font tracking. Movement without resize needs refresh(). No collision engine, cross-document targets, top-layer dialogs or transformed/zoomed body/html support. Partial clipping and very long notes need visual review.",
"placement": "Body overlays; resize/scroll/font tracking. Movement without resize needs refresh(). No collision engine, cross-document targets, top-layer dialogs or transformed/zoomed body/html support. Sticky offsetX/offsetY and arrow labelOffsetX/labelOffsetY nudge automatic placement before viewport clamping; they do not avoid obstacles. Partial clipping and very long notes need visual review.",
"styling": "Import @funsaized/stet/style.css once; preserve target layout and semantics. CSS variables theme the overlay."
},
"tools": {
Expand Down
4 changes: 2 additions & 2 deletions agent/catalog.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const primitives = {
sticky: { type: 'StickyOptions', targets: ['target'], padding: 14, purpose: 'Explain with concise paper-note text', meaningfulText: 'text; describes target' },
mark: { type: 'StetOptions', targets: ['target'], padding: 4, purpose: 'Check right or cross wrong; kind is a separate core argument', meaningfulText: 'description' },
};
export const defaults = { seed: 'random uint32', roughness: 1, boil: 0, stroke: 'CSS token', fill: 'CSS token', width: 'CSS token', resketchOnHover: false, description: 'absent', label: 'absent', curvature: 0.16, side: 'auto' };
export const defaults = { seed: 'random uint32', roughness: 1, boil: 0, stroke: 'CSS token', fill: 'CSS token', width: 'CSS token', resketchOnHover: false, description: 'absent', label: 'absent', curvature: 0.16, side: 'auto', offsetX: 0, offsetY: 0, labelOffsetX: 0, labelOffsetY: 0 };
export const strategies = ['ref', 'id', 'data-attribute', 'source', 'stet-attribute', 'css'];
export const tools = { claude: '.claude/skills', cursor: '.agents/skills', opencode: '.agents/skills', codex: '.agents/skills' };
export const constraints = {
Expand All @@ -23,6 +23,6 @@ export const constraints = {
motion: 'Still by default; reduced motion disables boil and hover resketch live. Fixed seed, dimensions and options reproduce geometry.',
lifecycle: 'DOM Elements in the current document and document.body must exist. Options are snapshots. Handles expose refresh(), resketch(seed?), destroy().',
targeting: 'Targets are source-editing evidence, not executable selectors. Prove uniqueness and readiness in application code. Arrow order is from, to.',
placement: 'Body overlays; resize/scroll/font tracking. Movement without resize needs refresh(). No collision engine, cross-document targets, top-layer dialogs or transformed/zoomed body/html support. Partial clipping and very long notes need visual review.',
placement: 'Body overlays; resize/scroll/font tracking. Movement without resize needs refresh(). No collision engine, cross-document targets, top-layer dialogs or transformed/zoomed body/html support. Sticky offsetX/offsetY and arrow labelOffsetX/labelOffsetY nudge automatic placement before viewport clamping; they do not avoid obstacles. Partial clipping and very long notes need visual review.',
styling: 'Import @funsaized/stet/style.css once; preserve target layout and semantics. CSS variables theme the overlay.',
};
2 changes: 1 addition & 1 deletion agent/patterns.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function attachMarks(target: Element, destination: Element | null | undefined, e
const destroy = () => { for (const handle of handles.splice(0).reverse()) handle.destroy(); };
try {
if (enabled) {
${arrowOnly ? '' : ' handles.push(circle(target, { seed: 42, description: "Review this action before continuing." }));\n handles.push(sticky(target, { seed: 43, text: "Read the consequences before continuing." }));\n'} if (destination) handles.push(arrow(target, destination, { seed: 44, label: "Consequences are explained here." }));
${arrowOnly ? '' : ' handles.push(circle(target, { seed: 42, description: "Review this action before continuing." }));\n handles.push(sticky(target, { seed: 43, text: "Read the consequences before continuing." }));\n'} if (destination) handles.push(arrow(target, destination, { seed: 44, label: "Consequences are explained here.", labelOffsetY: -12 }));
}
return destroy;
} catch (error) { destroy(); throw error; }
Expand Down
12 changes: 12 additions & 0 deletions agent/schemas/annotation-plan.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,12 @@
"curvature": {
"type": "number"
},
"labelOffsetX": {
"type": "number"
},
"labelOffsetY": {
"type": "number"
},
"seed": {
"type": "number"
},
Expand Down Expand Up @@ -625,6 +631,12 @@
"left"
]
},
"offsetX": {
"type": "number"
},
"offsetY": {
"type": "number"
},
"seed": {
"type": "number"
},
Expand Down
18 changes: 17 additions & 1 deletion agent/schemas/capabilities.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,12 @@
"curvature": {
"type": "number"
},
"labelOffsetX": {
"type": "number"
},
"labelOffsetY": {
"type": "number"
},
"seed": {
"type": "number"
},
Expand Down Expand Up @@ -322,6 +328,8 @@
"defaults": {
"label": "absent",
"curvature": 0.16,
"labelOffsetX": 0,
"labelOffsetY": 0,
"seed": "random uint32",
"roughness": 1,
"boil": 0,
Expand Down Expand Up @@ -360,6 +368,12 @@
"left"
]
},
"offsetX": {
"type": "number"
},
"offsetY": {
"type": "number"
},
"seed": {
"type": "number"
},
Expand Down Expand Up @@ -395,6 +409,8 @@
},
"defaults": {
"side": "auto",
"offsetX": 0,
"offsetY": 0,
"seed": "random uint32",
"roughness": 1,
"boil": 0,
Expand Down Expand Up @@ -486,7 +502,7 @@
"motion": "Still by default; reduced motion disables boil and hover resketch live. Fixed seed, dimensions and options reproduce geometry.",
"lifecycle": "DOM Elements in the current document and document.body must exist. Options are snapshots. Handles expose refresh(), resketch(seed?), destroy().",
"targeting": "Targets are source-editing evidence, not executable selectors. Prove uniqueness and readiness in application code. Arrow order is from, to.",
"placement": "Body overlays; resize/scroll/font tracking. Movement without resize needs refresh(). No collision engine, cross-document targets, top-layer dialogs or transformed/zoomed body/html support. Partial clipping and very long notes need visual review.",
"placement": "Body overlays; resize/scroll/font tracking. Movement without resize needs refresh(). No collision engine, cross-document targets, top-layer dialogs or transformed/zoomed body/html support. Sticky offsetX/offsetY and arrow labelOffsetX/labelOffsetY nudge automatic placement before viewport clamping; they do not avoid obstacles. Partial clipping and very long notes need visual review.",
"styling": "Import @funsaized/stet/style.css once; preserve target layout and semantics. CSS variables theme the overlay."
},
"tools": {
Expand Down
5 changes: 5 additions & 0 deletions agent/skills/stet-explain-ui/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ Remove redundant notes before trying to fit several overlays into a narrow layou
Use the [base Stet workflow](../stet/SKILL.md) to inspect installed facts, plan,
validate and implement in the detected framework. Verify a reader can understand
the action while the original controls retain their behavior.

When explaining completed implementation work, use the
[handoff guidance](../stet/references/handoff.md) to separate actual changes,
verified behavior and follow-ups. This does not substitute for implementation
or verification.
1 change: 1 addition & 0 deletions agent/skills/stet/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,5 @@ Local edits cause a conflict; reconcile them instead of deleting project config.
| Vue directives | [vue](references/vue.md) |
| Svelte actions | [svelte](references/svelte.md) |
| Angular directives | [angular](references/angular.md) |
| Explanation of completed implementation | [handoff](references/handoff.md) |
| Errors and verification failures | [troubleshooting](references/troubleshooting.md) |
20 changes: 20 additions & 0 deletions agent/skills/stet/references/handoff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Explain an implementation handoff

Use after implementation, when the requested explanation is about what changed.
Inspect the actual diff and working controls. Separate implemented behavior,
checks you actually ran, and follow-up work. A passing build does not establish
interaction behavior or visual quality; an annotation is never test evidence.

Annotate meaningful changes only. Use concise delivery copy such as “Exact-name
confirmation added”; reserve “verified” for behavior supported by your recorded
checks. Put untested claims and remaining work in the handoff text instead of
turning them into green checks. Keep safety warnings in the application.

Preserve the new controls and their layout. Prefer a focal circle or underline
when a note would repeat native copy. Group cleanup in the framework's existing
lifecycle. Provide the plan, changed source and actual verification results so a
human can review the working UI with marks on or off.

This is an explanation of completed work. A request to find defects uses the
review workflow; a request to present product value uses showcase. A handoff
request alone does not authorize unrelated implementation or deployment.
6 changes: 5 additions & 1 deletion agent/skills/stet/references/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@
| Invisible/misplaced chrome | Check CSS import, target visibility, lifecycle and supported placement context | Scroll/resize and inspect in browser |
| Drift after app movement | Call retained core handle.refresh(); inspect adapter lifecycle if applicable | Seed unchanged; overlay follows target |
| Stale annotation | Destroy manual handles; align target and adapter mount lifetimes | Navigate/toggle; no stale overlay/description |
| Text overlaps UI | Shorten copy, reduce marks, try a supported side; inspect responsive state | Text readable, controls unobscured |
| Text overlaps UI | Shorten copy and reduce marks first. Inspect installed support for sticky offsetX/offsetY or arrow labelOffsetX/labelOffsetY; nudge after browser inspection. Curvature adjusts the path, not obstacle routing | Text readable, controls unobscured |
| Skill update conflict | Preserve and reconcile named local files; rerun update | No unrelated config changed |

Exit codes: 1 invalid plan; 2 usage; 3 file/JSON error; 4 installation conflict.
JSON failure output contains errors with path/code/message. Recovery must address
that cause. A successful validation or build is not visual verification. If a
browser cannot run, state that limitation and the exact checks that did run.

Placement nudges use viewport x/y pixels (positive right/down), after automatic
placement and before viewport clamping. They do not detect collisions or force a
preferred side. Recheck at narrow widths; remove redundant labels when possible.
Loading
Loading