From 36123b4c6cffce461c005c104622e9b0ba7313b8 Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Sat, 18 Jul 2026 19:50:56 -0400 Subject: [PATCH] docs(markup): clarify native note composition --- .changeset/teach-stml-note-composition.md | 5 ++ src/ui/lib/stml/guide.test.ts | 15 ++++- src/ui/lib/stml/guide.ts | 76 +++++++++++------------ 3 files changed, 57 insertions(+), 39 deletions(-) create mode 100644 .changeset/teach-stml-note-composition.md diff --git a/.changeset/teach-stml-note-composition.md b/.changeset/teach-stml-note-composition.md new file mode 100644 index 00000000..53efd047 --- /dev/null +++ b/.changeset/teach-stml-note-composition.md @@ -0,0 +1,5 @@ +--- +"hunkdiff": patch +--- + +Teach STML authors to compose within Hunk's native note frame while preserving focused inset boxes. diff --git a/src/ui/lib/stml/guide.test.ts b/src/ui/lib/stml/guide.test.ts index 2c22768c..5fe1384d 100644 --- a/src/ui/lib/stml/guide.test.ts +++ b/src/ui/lib/stml/guide.test.ts @@ -3,7 +3,7 @@ import { STML_GUIDE, stmlGuideSnippets } from "./guide"; import { layoutStml, STML_REFERENCE_WIDTH } from "./layout"; describe("STML guide", () => { - test("contains a copy-paste snippet for every core pattern", () => { + test("contains a copy-paste snippet for every core mechanic", () => { const snippets = stmlGuideSnippets(); expect(snippets.length).toBeGreaterThanOrEqual(8); @@ -14,6 +14,19 @@ describe("STML guide", () => { expect(STML_GUIDE).toContain(`${STML_REFERENCE_WIDTH}`); }); + test("teaches agents that STML composes inside Hunk's existing note frame", () => { + expect(STML_GUIDE).toContain("Hunk supplies the note's outer frame"); + expect(STML_GUIDE).toContain("Sibling and nested boxes are supported"); + expect(STML_GUIDE).toContain(" { + expect(STML_GUIDE).toContain("## Syntax examples"); + expect(STML_GUIDE).toContain("demonstrate mechanics, not preferred layouts"); + expect(STML_GUIDE).toContain("combine, omit, repeat, and nest"); + expect(STML_GUIDE).not.toContain("## Patterns"); + }); + test("every snippet lays out cleanly at the reference width", () => { for (const snippet of stmlGuideSnippets()) { const { lines, errors } = layoutStml(snippet, STML_REFERENCE_WIDTH); diff --git a/src/ui/lib/stml/guide.ts b/src/ui/lib/stml/guide.ts index 21418723..739613a5 100644 --- a/src/ui/lib/stml/guide.ts +++ b/src/ui/lib/stml/guide.ts @@ -14,11 +14,11 @@ export const STML_GUIDE = `# STML — terminal markup for Hunk agent notes Experimental: the tag and color vocabulary may change between releases. Markup degrades to plain text, so worst case a note loses polish, not content. -Small HTML-like markup rendered as real terminal UI inside note cards: +Small HTML-like markup rendered as real terminal UI inside agent notes: boxes, rows, badges, gauges, lists, code blocks. Sources (--summary stays as the plain-text fallback): - hunk session comment add ... --markup '...' + hunk session comment add ... --markup 'formatted note body' comment apply items: { "markup": "...", ... } agent-context sidecar: annotations[].markup @@ -28,12 +28,15 @@ Preview from a file or stdin: ## Ground rules +- Hunk supplies the note's outer frame, author, and source location. STML is + the note body; use borders for useful inner hierarchy rather than duplicating + that frame around the whole body. Sibling and nested boxes are supported. - Width follows the live session: stack ≈ full pane, split ≈ half, big terminal = big note. \`hunk session context --json\` reports \`noteMarkupWidth\`; comment responses echo \`markupWidth\`. Preview with \`hunk markup render - --width \`. Unknown? Design for ~${STML_REFERENCE_WIDTH} cols — it holds up wider, and users resize/switch layouts anytime. -- No chart tag: gauges are block chars (█ ░) in color spans (pattern below). +- No chart tag: gauges are block chars (█ ░) in color spans (glyph-run example below). - Bad markup degrades instead of crashing and produces render notes (in comment responses and on \`markup render\` stderr) — fix what they flag. - Entities work: → → ✓ ✓ & &. @@ -49,88 +52,85 @@ row: gap. list: marker. spacer: size. code: title. Colors: theme tokens accent success warning danger info muted subtle heading (preferred — they follow the user's theme), ANSI names, or #hex. -## Patterns +## Syntax examples -Status line: +These fragments demonstrate mechanics, not preferred layouts. STML is an +open composition grammar: combine, omit, repeat, and nest elements as the +explanation requires. + +Inline styles: \`\`\`stml -PASS 34 tests · TODO add jitter · reviewed by fable +label bold italic dim \`\`\` -Titled card: +Bordered grouping: \`\`\`stml - - Retries cap at 3 attempts; the last error is rethrown. - + + grouped detail + \`\`\` -Scorecard row: +Responsive siblings: \`\`\`stml - - 34 pass - - - RISK unbounded delay - + first region + second region \`\`\` -Gauges (fixed bar budget ~20 cells, filled/empty split, label at the end): +Colored glyph runs: \`\`\`stml -coverage ████████████████░░░░ 80% -p95 ███████░░░░░░░░░░░░░ 340ms +████████████░░░░░░░░ 60% \`\`\` -Pipeline (the
drops each arrow to the boxes' middle row): +Rows with connectors (the
vertically centers each arrow): \`\`\`stml - parse + first
detail

- render + second
detail
\`\`\` -Checklist: +List structure: \`\`\`stml - DONE bounded retry loop - TODO add jitter - RISK delayMs grows unbounded + first item + second item \`\`\` -Key-value block: +Fixed-width columns: \`\`\`stml - attempts
base delay
- 3 max
100ms
+ label
status
+ value
ready
\`\`\` -Code suggestion (verbatim; clips, never wraps): +Verbatim block (clips, never wraps): \`\`\`stml - -const backoff = (attempt: number) => - 100 * 2 ** (attempt - 1); + +const value = compute(); \`\`\` -Keyboard hints: +Keyboard token: \`\`\`stml -press a to toggle notes + key \`\`\` -A note reads best as a callout, not a page: verdict first, a couple of -blocks of evidence, semantic colors used for meaning. +Use STML when its layout makes the note clearer than plain text, and add +structure where it helps communicate the note. `; /** Extract the fenced \`\`\`stml snippets from the guide, in order. */