design-html: match Pretext docs to the vendored bundle; fix cross-block server state (s08 H1, H2, H3, H4, H7, M6)

- H1-H3: walkLineRanges, layoutNextLine and layoutWithLines signatures and
  return shapes now match design-html/vendor/pretext.js; patterns 2-4 rewritten.
- H4: inline the ES-module bundle in <script type="module"> with page code in
  the same module; no window.Pretext global exists.
- H7: remember the printed server URL and PID; later blocks restate them.
- M6: drop the 2-3 line cap on the refinement summary.

Template-only commit: generated SKILL.md is expected red until the regeneration commit.
This commit is contained in:
garrytan committed 2026-10-02 23:55:06 +00:00
1 parent 0d9f77bd08
commit 2941980083
2 files changed
+36 -42

No files matched your search

+9 -5
View File
@@ -231,8 +231,9 @@ _ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
[ -n "$_PRETEXT_VENDOR" ] && echo "VENDOR: $_PRETEXT_VENDOR" || echo "VENDOR_MISSING"
```
- If `VENDOR` found: read the file and inline it in a `<script>` tag. The HTML file
is fully self-contained with zero network dependencies.
- If `VENDOR` found: inline the file in a `<script type="module">` and put the page's
Pretext code after it in that same script; the bundle ends in `export {...}` and sets
no global. The HTML file is fully self-contained with zero network dependencies.
- If `VENDOR_MISSING`: use CDN import as fallback:
`<script type="module">import { prepare, layout, prepareWithSegments, walkLineRanges, layoutNextLine, layoutWithLines } from 'https://esm.sh/@chenglou/pretext'</script>`
Add a comment: `<!-- FALLBACK: vendor/pretext.js missing, using CDN -->`
@@ -306,17 +307,20 @@ echo "SERVER: http://localhost:$_PORT/finalized.html"
echo "PID: $_SERVER_PID"
```
Remember the printed `SERVER:` URL and `PID:` value; each bash block is a fresh
shell, so later blocks restate them literally.
If python3 is not available, fall back to:
```bash
open <path-to-finalized.html>
```
Tell the user: "Live preview running at http://localhost:$_PORT/finalized.html.
Tell the user: "Live preview running at <SERVER URL>.
After each edit, just refresh the browser (Cmd+R) to see changes."
When the refinement loop ends (Step 4 exits), kill the server:
```bash
kill $_SERVER_PID 2>/dev/null || true
kill <PID> 2>/dev/null || true
```
---
@@ -377,7 +381,7 @@ LOOP:
5. Apply feedback using targeted Edit tool changes on the HTML file
(do NOT regenerate the entire file — surgical edits only)
6. Brief summary of what changed (2-3 lines max)
6. Briefly say what changed
7. If verification screenshots are available, re-take them to confirm the fix
+27 -37
View File
@@ -5,8 +5,8 @@ Pretext API usage patterns. Follow them exactly.
**Pattern 1: Basic height computation (Simple layout, Card/grid)**
```js
import { prepare, layout } from './pretext-inline.js'
// Or if inlined: const { prepare, layout } = window.Pretext
import { prepare, layout } from '@chenglou/pretext'
// Vanilla HTML: omit the import; this code follows the inlined bundle in the same <script type="module">
// 1. PREPARE — one-time, after fonts load
await document.fonts.ready
@@ -45,37 +45,26 @@ for (const el of elements) {
**Pattern 2: Shrinkwrap / tight-fit containers (Chat bubbles)**
```js
import { prepareWithSegments, walkLineRanges } from './pretext-inline.js'
import { prepareWithSegments, walkLineRanges } from '@chenglou/pretext'
// Find the tightest width that produces the same line count
function shrinkwrap(text, font, maxWidth, lineHeight) {
// Tight-fit width: lay out at maxWidth, then shrink the bubble to its widest line
function shrinkwrap(text, font, maxWidth) {
const segs = prepareWithSegments(text, font)
let bestWidth = maxWidth
walkLineRanges(segs, maxWidth, (lineCount, startIdx, endIdx) => {
// walkLineRanges calls back with progressively narrower widths
// The first call gives us the line count at maxWidth
// We want the narrowest width that still produces this line count
let widest = 0
const lineCount = walkLineRanges(segs, maxWidth, (line) => {
widest = Math.max(widest, line.width)
})
// Binary search for tightest width with same line count
const { lineCount: targetLines } = layout(prepare(text, font), maxWidth, lineHeight)
let lo = 0, hi = maxWidth
while (hi - lo > 1) {
const mid = (lo + hi) / 2
const { lineCount } = layout(prepare(text, font), mid, lineHeight)
if (lineCount === targetLines) hi = mid
else lo = mid
}
return hi
return { width: Math.ceil(widest), lineCount }
}
```
**Pattern 3: Text around obstacles (Editorial layout)**
```js
import { prepareWithSegments, layoutNextLine } from './pretext-inline.js'
import { prepareWithSegments, layoutNextLine } from '@chenglou/pretext'
function layoutAroundObstacles(text, font, containerWidth, lineHeight, obstacles) {
const segs = prepareWithSegments(text, font)
let state = null
let cursor = { segmentIndex: 0, graphemeIndex: 0 }
let y = 0
const lines = []
@@ -88,11 +77,11 @@ function layoutAroundObstacles(text, font, containerWidth, lineHeight, obstacles
}
}
const result = layoutNextLine(segs, state, availWidth, lineHeight)
const result = layoutNextLine(segs, cursor, availWidth)
if (!result) break
lines.push({ text: result.text, width: result.width, x: 0, y })
state = result.state
cursor = result.end
y += lineHeight
}
@@ -102,21 +91,21 @@ function layoutAroundObstacles(text, font, containerWidth, lineHeight, obstacles
**Pattern 4: Full line-by-line rendering (Complex editorial)**
```js
import { prepareWithSegments, layoutWithLines } from './pretext-inline.js'
import { prepareWithSegments, layoutWithLines } from '@chenglou/pretext'
const segs = prepareWithSegments(text, font)
const { lines, height } = layoutWithLines(segs, containerWidth, lineHeight)
// lines = [{ text, width, x, y }, ...]
// lines = [{ text, width, start, end }, ...] (no x/y: line i sits at y = i * lineHeight)
// Use for Canvas/SVG rendering or custom DOM positioning
for (const line of lines) {
lines.forEach((line, i) => {
const span = document.createElement('span')
span.textContent = line.text
span.style.position = 'absolute'
span.style.left = `${line.x}px`
span.style.top = `${line.y}px`
span.style.left = '0px'
span.style.top = `${i * lineHeight}px`
container.appendChild(span)
}
})
```
### Pretext API Reference
@@ -134,16 +123,17 @@ layout(prepared, maxWidth, lineHeight) → { height, lineCount }
prepareWithSegments(text, font) → handle
Like prepare() but enables line-level APIs below.
layoutWithLines(segs, maxWidth, lineHeight) → { lines: [{text, width, x, y}...], height }
Full line-by-line breakdown. For Canvas/SVG rendering.
layoutWithLines(segs, maxWidth, lineHeight) → { lineCount, height, lines: [{text, width, start, end}...] }
Full line-by-line breakdown (no x/y: line i is at y = i * lineHeight). For Canvas/SVG rendering.
walkLineRanges(segs, maxWidth, onLine) → void
Calls onLine(lineCount, startIdx, endIdx) for each possible layout.
Find minimum width for N lines. For tight-fit containers.
walkLineRanges(segs, maxWidth, onLine) → lineCount
Calls onLine({ width, start, end }) once per line at maxWidth (no text built).
The widest line's width is the tight-fit container width. For chat bubbles.
layoutNextLine(segs, state, maxWidth, lineHeight) → { text, width, state } | null
layoutNextLine(segs, cursor, maxWidth) → { text, width, start, end } | null
Iterator. Different maxWidth per line = text around obstacles.
Pass null as initial state. Returns null when text is exhausted.
Start with cursor { segmentIndex: 0, graphemeIndex: 0 }; pass result.end next.
Returns null when text is exhausted. Takes no lineHeight: advance y yourself.
clearCache() → void
Clears internal measurement caches. Use when cycling many fonts.