| name | preview-browser-skill |
| description | Render the last response (or a specified markdown file) as HTML and open in the default browser. Explicit invocation only — do not auto-trigger. |
| disable-model-invocation | true |
Preview in Browser
Render markdown to HTML and open it in the default browser.
Requires pandoc; jq is optional (fast path only). $SKILL_DIR below means this skill's base directory (stated when the skill is invoked). Work in /tmp when it exists, otherwise the platform temp dir — every snippet below starts with:
TMP=/tmp; [ -d "$TMP" ] || TMP="${TMPDIR:-${TEMP:-.}}"
Behavior
- If
$ARGUMENTS is a file path, render that file directly.
- If
$ARGUMENTS is empty, materialise your immediately preceding assistant message (the very last thing you said before this skill was invoked) verbatim at $TMP/preview-browser-skill.md and render that. Do not skip it, summarise it, or pick a different message. Two ways to materialise it:
Fast path (Claude Code with jq available; otherwise use the fallback)
Extract the message from the session transcript instead of re-typing it. Run this as your first action in the turn, before emitting any other text or tool calls:
TMP=/tmp; [ -d "$TMP" ] || TMP="${TMPDIR:-${TEMP:-.}}"
PROJ="$HOME/.claude/projects/$(pwd | sed 's/[^A-Za-z0-9]/-/g')"
T="$PROJ/${CLAUDE_SESSION_ID:-none}.jsonl"
[ -f "$T" ] || T=$(ls -t "$PROJ"/*.jsonl 2>/dev/null | head -1)
jq -rs '
def realuser: .type=="user" and ((.message.content|type) != "array" or .message.content[0].type? != "tool_result");
(map(if realuser then "u" else "x" end) | rindex("u")) as $u
| [.[0:$u][] | select(.type=="assistant" and .isSidechain != true) | .message.content[]? | select(.type=="text") | .text]
| last // empty
' "$T" > "$TMP/preview-browser-skill.md"
head -c 150 "$TMP/preview-browser-skill.md"
The transcript format is internal to Claude Code, and a concurrent session in the same project can be the newest file — so check that the head output matches how your previous message actually started. On mismatch, empty output, missing jq, or any other doubt, use the fallback. Never render a wrong or truncated extract.
Fallback (always correct)
Write the message verbatim to $TMP/preview-browser-skill.md with the Write tool.
Rendering
A single Bash call does all of the following:
- Stylesheet: copy the one shipped with this skill —
cp "$SKILL_DIR/preview-style.css" "$TMP/". For content-specific tweaks (accent colours, extra utility classes) append overrides to the copy; never edit the master in $SKILL_DIR.
- Title: short and descriptive, from the first heading or topic ("Preview" as fallback).
- Math: only if the source contains LaTeX math (
$$, \(, \[, or $...$ pairs) set FROM=gfm+tex_math_dollars MATH=--mathjax; otherwise FROM=gfm MATH= — the page loads noticeably faster without MathJax.
- Convert
[an: ...] annotation markers to <mark> and render (the relative --css keeps the HTML+CSS pair relocatable):
TMP=/tmp; [ -d "$TMP" ] || TMP="${TMPDIR:-${TEMP:-.}}"
cp "$SKILL_DIR/preview-style.css" "$TMP/"
sed 's/\[an: \([^]]*\)\]/<mark>\1<\/mark>/g' "$INPUT" \
| pandoc -f "$FROM" -t html -s $MATH --metadata title="$TITLE" \
--css preview-style.css -o "$TMP/preview-browser-skill.html"
- Open with the platform's opener, then tell the user the output path:
| Platform | Command |
|---|
| macOS | open "$TMP/preview-browser-skill.html" |
| Linux | xdg-open "$TMP/preview-browser-skill.html" |
| WSL | explorer.exe "$(wslpath -w "$TMP/preview-browser-skill.html")" |
| Windows (Git Bash) | cmd.exe //c start "" "$(cygpath -w "$TMP/preview-browser-skill.html")" |
Output filenames are stable, so each preview overwrites the previous one.