What does Preview: Selectable-Comment HTML Page do?
Turn any plan, proposal, draft, or finished document into an HTML page where readers can select text → leave a comment, then open it on the user's computer for them (or their client) to review. After they finish, they click "Copy All" and the comments and checkbox results become a markdown snippet that can be pasted back into the conversation for action.
Where to Find This Skill's Files
The folder containing this file is the skill folder (hereafter written as $SKILL_DIR). Set it before running any commands:
SKILL_DIR="${CLAUDE_SKILL_DIR:-$HOME/.claude/skills/preview}"
[ -f "$SKILL_DIR/scripts/md-to-preview.py" ] || SKILL_DIR="$(cd "$(dirname "$(find "$HOME/.claude/skills" .claude/skills -path '*/preview/scripts/md-to-preview.py' 2>/dev/null | head -1)")/.." && pwd)"
echo "$SKILL_DIR"
- Requires only Python 3.9+, using the standard library – no pip installs.
- On macOS use
python3; on Windows ifpython3is not available, usepythonorpyinstead. - If the skill folder cannot be found, stop and tell the user to ensure the
previewfolder is located under~/.claude/skills/; do not write a separate comment implementation.
| File | Purpose |
|---|---|
scripts/md-to-preview.py | Main generator: converts markdown to preview page |
scripts/convert/inject.py | For already-laid-out HTML: adds comment functionality only |
scripts/convert/visuals.py, flow.py, tables.py | Visual components, flowcharts, table conversion (called automatically by the generator) |
assets/preview-comment-overlay.html | Text-selection comment interface |
assets/preview-tokens.css | Colors, fonts, spacing; modify this to change appearance |
assets/fonts/ | Bundled fonts: Noto Sans TC, Geist, Geist Mono (SIL OFL licensed) |
assets/example.md | Full example: a fictional project proposal that demonstrates all components |
Rule 1: Comments Are Always Selection-Based
When the user says "add comments" or "a version with comments," they mean a page where you can select any text → leave a comment, like Google Docs – not an input box next to each paragraph. This skill's comment interface is selection-based; use it directly, do not build an alternative.
Rule 2: Rewrite First, Then Generate
First, Determine the Page Type
| Type | Example | How to Handle |
|---|---|---|
| Explanatory | Plans, proposals, reports, comparisons, pages requiring a decision | Rewrite according to the rules below into a readable version before generating; do not feed working drafts directly into the generator |
| Original-text review | Contracts, finished copy, transcripts, client-provided text | Do not alter a single word; generate with --no-lint; at most add a > [!Conclusion] at the top explaining what this page is for |
Writing Rules for Explanatory Content
- First screen says it all: Start with
> [!Conclusion]stating the conclusion in one sentence, then> [!Decision Needed]listing what the reader needs to do. Put background later; the reader can decide without scrolling down. - Say each thing once: If the conclusion box already said it, do not rephrase it in the body. If a diagram or table already said it, do not restate it in a paragraph below. Do not write a "summary" section.
- Plain language: Use the words the reader normally speaks. The first time you introduce a jargon term, use an analogy or add a
> [!Plain Talk]line of explanation. - Draw, don't write: First ask "what relationship is this describing?" then pick the component from the table below. At most three lines of pure text per block; every
##section must have at least one diagram component or table. - Be brief: One sentence, one point. Remove filler phrases like "in other words," "that is," "it is worth noting."
- Remove repetition, not information: Numbers, dates, names, consequences – every single one must remain, and must not be altered.
Visual Component Quick Reference
Write a few lines of short syntax; the generator draws the diagram. Use full-width | as separator, → for arrows. See assets/example.md for complete examples.
| What It Describes | Component | Syntax |
|---|---|---|
| Old vs new, before vs after | Comparison | ````Comparison: first line Before → After, then per line Item|Old → New` |
| System composition, data flow | Architecture | ````Architecture: one line per layer Layer|Box, Box; inside a layer use A → B; you can insert a line ↓ Description` between layers |
| What happens when | Timeline | ````Timeline: per line Time|Title|Description; prefix with ✅for done,👉` for current |
| Sequence, steps, cause-effect chain | Steps | ````Steps: per line Title|Description`, auto-numbered; up to 4 steps side‑by‑side, 5+ stacked vertically |
| Key numbers | Numbers | ````Numbers: per line Number Unit|Label|Small note`; a space between the number and unit makes the unit superscript |
| Options, roles, parallel items | Cards | Cards` (or Options, ````Roles): per line Title|Line|Line; prefix with ★ to mark as recommended |
| Multi-column comparison | Table | Standard md table. If the header simultaneously contains "Current/Old" and "New/After/Suggested," the old columns auto-gray, new columns auto-light-green |
| Branching flow | Flow cards | ASCII or mermaid flowchart (graph/flowchart TD, LR); if conversion fails, the original is preserved |
| Conclusion, decision needed, caution, risk | Callout boxes | > [!Conclusion] Title, next lines > Content. Use: Conclusion, Decision Needed, Caution, Risk, Complete, Plain Talk, Explanation |
| Status | Color badges | Inline {Green:Complete} {Yellow:Waiting} {Red:Blocked} {Blue:In Progress} {Gray:Won't Do} |
| Key phrase in a sentence | Highlight | ==Keyword==, at most one per paragraph |
- Colors have fixed meanings: Blue = conclusion & information, Green = done & after-change, Yellow = caution & waiting, Red = risk & blocked. Only one "Decision Needed" box per page.
- If the syntax is wrong, it will not break the page; it will fall back to a gray code block. Check the generated page to see if anything that should be a diagram is still gray.
- Use Steps or Timeline for sequences or cause-effect relationships; use Cards only when items are independent.
- Text inside diagrams is real text and can be selected for comments.
Layout Inspection
The generator will automatically print a "Layout Inspection" report: missing conclusion box, overly long all-text sections, too few visual components, redundant phrases, wrong dashes, repeated information, etc. If a warning appears, fix the markdown and re-run until you see "Layout Inspection: Passed." For original-text review pages, use --no-lint.
Save Resources
- Always use the short syntax for diagrams – do not hand-code HTML or SVG, and do not generate external images.
- To change content, edit the markdown and re-run; do not modify the output HTML directly.
- For very long lists, use
--fold "3"to collapse them.
Rule 3: Always Run the Generator
- Preview pages must be generated using
scripts/md-to-preview.py(from markdown) orscripts/convert/inject.py(from already-laid-out HTML). Do not write CSS by hand or build your own comment functionality. - To change the appearance, edit
assets/preview-tokens.cssand re-run the generator.
Execution Flow
Step 1: Confirm Source and Identity
Determine from the conversation; if uncertain, ask:
- What is to be previewed? (Content just written in the conversation, or a file provided by the user)
- Who will be commenting? Default identity is
me,guest. If it is for the client, set it to e.g.client,me(first is the default identity).
If the source is content from the conversation, save it as a markdown file in the user's current working directory (or in a preview folder on the desktop if none exists). Name the file with a Chinese description of the content, e.g. proposal-sample-client.md.
Step 2: Generate the Page
Source is markdown (most common):
python3 "$SKILL_DIR/scripts/md-to-preview.py" "<source>.md" "<output>.html" \
--identities "me,guest" --badges "draft,2026-10-19"
| Parameter | Effect |
|---|---|
--identities | Comma-separated list of identities; first is default |
--badges | Gray badges below the title, comma-separated; omit to hide |
--key | Name used to store comments in the browser; must be unique per page; defaults to output filename |
--title | Document title; defaults to the first # heading in the markdown |
--fold "3,4" | Collapse headings at the specified levels into expandable sections; for long pages |
--no-lint | Skip layout inspection (for contracts, finished copy, etc.) |
--no-overlay | Clean version without comment functionality |
--icon | Character or emoji for the tab icon; defaults to first character of the title |
--fonts copy/link/none | Font handling; default is copy |
Fonts: By default, a preview-fonts/ folder is placed next to the output HTML. 🚨 When sharing with others, bundle the HTML and the preview-fonts/ folder together – sending only the HTML will cause a fallback to system fonts (still usable, just less attractive).
Images: A standalone  line in the markdown becomes an image with a caption; the path is relative to the output HTML.
Source is already laid-out HTML (e.g., a finished proposal page): Do not alter the layout; just add comment functionality.
python3 "$SKILL_DIR/scripts/convert/inject.py" "<page>.html" -o "<page>-review.html" --identities "me,client"
-omust be a new file; the tool will not overwrite the source.- If the page already has comments and you need a newer version, add
--replace.
Step 3: Open for the User
Open with the system default browser:
# macOS
open "<output>.html"
# Windows (Claude's terminal is Git Bash, use start; PowerShell use Start-Process "<output>.html" )
start "" "<output>.html"
# Linux
xdg-open "<output>.html"
Tell the user three things:
- Select any text – a "Add Comment" button will appear.
- Checkboxes can be clicked directly to toggle.
- When done, click "Copy All" in the top-right corner and paste the content back into the conversation.
Step 4 (Optional): Send to Client for Review
- Zip the HTML together with the adjacent
preview-fonts/folder and send it to the client. Ask them to unzip and double-click the HTML to open. - Set the identity to the client's role, e.g.,
--identities "client,me". - Comments are stored in the client's own browser; they are not automatically synced back. Ask the client to click "Copy All" and send the text via email or message.
Checkbox Confirmation
When you need the user to decide on each item in a list and the options are fixed, make it checkboxes – do not ask them to type a response. For example: a list of features to implement or skip, a set of assets to select, a pre-launch checklist. For just one or two options, or when a rationale is needed, ask directly or use text-selection comments.
How to Write It
In the markdown, write a task list; the generator will convert it into clickable checkboxes:
### Must Do
- [x] Order form (after customer fills in, automatically enters database)
- [ ] Scheduling calendar (each employee sees only their own shifts)
- [ ]unchecked,- [x]checked by default.- Place each group under its own heading (
##or###): when exporting, the grouping is based on the nearest preceding heading. - Each item must have a name plus a description so it can be understood independently – do not write just "yes" or "no."
- Do not have two identical option texts under the same heading.
What Happens on the User's Side
- Checkbox state is stored in the browser; it survives a refresh. Clicking "Clear" clears only comments, not checkboxes.
- When clicking "Copy All", a prefix block is added:
## Check Results (2/5 checked)
### Scope Confirmation › Must Do
- [x] Order form (after customer fills in, automatically enters database)
- [ ] Scheduling calendar (each employee sees only their own shifts)
When Receiving Check Results
- First, save the check results verbatim as a file; then follow it exactly – do not retype from memory.
- Unchecked items mean "don't do it" or "keep as is" – do not make assumptions.
- If the same group contains conflicting checked options, ask the user to clarify.
- For actions that affect the outside world (sending emails, deleting files, going live), first compile the final list and only execute after the user says "proceed."
How to Apply Received "Comment Summaries"
When the user clicks "Copy All" and pastes the result back, it will contain: for each comment, the selected original text (quote), location (section heading), surrounding context, and the comment itself.
Interpret Each Comment
| Comment Appearance | Interpretation |
|---|---|
| Only a new piece of text, no verb | Replace: swap the quoted text with the comment text |
| "Delete" or "Remove" | Delete the quoted text (only the quoted range) |
| "Add" or "Insert" or "Add another one below" | Add: insert content near the quoted location |
| Descriptive ("Could we…?", "Would like…") | Modify according to the request |
| Ambiguous, could be interpreted two ways | Choose the least invasive change; report clearly which option you chose |
- Use the "location" and "surrounding context" to find the correct spot before editing. If the same text appears multiple times on the page, use the context to confirm you hit exactly one occurrence.
- Comments in the current round may overlap with previous ones; first compare to see which have already been applied.
Apply, Re-run, Re-open
- Edit the markdown source, then re-run the generator using the same command and the same
--key. Do not edit the output HTML directly (it will be overwritten the next run). Using the same key ensures old comments are preserved. - Re-open the page for the user to inspect.
- Report: list each comment and what changed. For ambiguous ones, note your decision.
Technical Notes (for Claude, not to be shared with the user)
- Comment anchoring uses "selected text + surrounding context." Small content changes can still re-match; if not found, mark it as "original text changed."
- Highlighting uses the browser's CSS Custom Highlight API, which does not alter the page structure. Supported by recent versions of Chrome, Edge, Safari. Unsupported browsers will simply lack the yellow highlight; sidebar comments still work normally.
- Comments and checkboxes are stored in the browser's
localStorage, keyed by the unique--keyper page. Otherwise, different pages' comments would mix. - After the comment interface loads, it exposes
window.PreviewComments:.refresh()to reposition highlights,.data()to retrieve all comments,.exportMd()to return the exported markdown. - The page's own popups must have a
z-indexbelow 10400, otherwise the comment button will be obscured.
