πͺ½ Artifact Preview v4.3 β Live Preview + Smart History π
One-line install. Instant visual feedback. Every version safely saved. Zero "where did my work go?" π₯
What This Is
This skill shows the human what you built β a UI, a dashboard, a page, a result. Visually. Instantly.
It is not for agents to read content from. The Chrome window is for human eyes only. Extract data directly from the source if needed.
β οΈ INCOMPATIBLE with chrome-devtools. Never use both simultaneously.
π One-Line Install
curl -fsSL https://raw.githubusercontent.com/ChuckSRQ/awesome-hermes-skills/v4.3/artifact-preview/install.sh | bash
Server starts automatically. You're ready to go. β¨
How to Use
Step 1 β Generate complete, polished, self-contained HTML/CSS/JS (see Design Standards below).
Step 2 β Save + trigger reload:
cd ~/artifact-preview
cat > artifact.html << 'ENDOFHTML'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Your Clear Title Here</title>
</head>
<body>
<!-- your complete content -->
</body>
</html>
ENDOFHTML
# Saves + archives + live reload β instant! β‘
curl -X POST http://localhost:8765/update \
-H "Content-Type: text/html; charset=utf-8" \
--data-binary @artifact.html -s -o /dev/null
Step 3 β Open the preview:
bash ~/artifact-preview/open-chrome.sh # auto mode = magic πͺ
Pro tip: Always include <title> and <h1> β history labels depend on them.
β οΈ Static asset trap β images and subdirectories: If your artifact HTML references subdirectories or image files (e.g. <img src="slides/slide-01.jpg">), those files MUST be inside ~/artifact-preview/ β the server only serves from its own directory. Files elsewhere on the filesystem are invisible even if they exist. Always copy assets to ~/artifact-preview/ before POSTing.
β οΈ Pitfall β tilde in curl @ syntax: curl --data-binary @~/path fails because ~ doesn't expand through curl's @ filename operator. Always cd to the directory first, or copy to a temp file with absolute path first:
cp ~/artifact-preview/history/myfile.html /tmp/myfile.html
curl -X POST http://localhost:8765/update \
-H "Content-Type: text/html; charset=utf-8" \
--data-binary @/tmp/myfile.html -s -o /dev/null
β οΈ NEVER POST test strings to /update with @filepath. If you POST "test" (or any short string) using --data-binary "test" or @/path/to/small-file, the server writes that content directly to artifact.html on disk β overwriting whatever was there. Subsequent @artifact.html POSTs will then re-upload the corrupted content. Symptom: POST returns HTTP 200 but the page still shows the old/corrupted content. Fix: re-write the correct content to disk with write_file, then POST with @filepath.
π¨ Design Standards
- β Complete, interactive, beautiful β not skeletons π
- β Self-contained single HTML file
- β
Light-first:
#F8F7F4warm white background,#8B5CF6violet accent at ~10% - β Instrument Sans font, WCAG 4.5:1 contrast
π₯ Recommended Workflow (Copy-Paste This)
cd ~/artifact-preview
cat > artifact.html << 'ENDOFHTML'
[your complete, polished HTML/CSS/JS here]
ENDOFHTML
# Triggers save + archive + toast + live reload β‘
curl -X POST http://localhost:8765/update \
-H "Content-Type: text/html; charset=utf-8" \
--data-binary @artifact.html -s -o /dev/null
# Open β auto mode is pure magic πͺ
bash ~/artifact-preview/open-chrome.sh
Viewing a file from history (not a new artifact)? Copy it to artifact.html first, then open:
cp ~/artifact-preview/history/<filename.html> ~/artifact-preview/artifact.html
bash ~/artifact-preview/open-chrome.sh portrait # or horizontal / full
π¦ History β Never Lose Work π
Every /update archives a new entry. Newest appear at top. Up to 15 kept.
- Save β overwrites
artifact.html+ archives β live preview updates - Save as New β archives as new entry β does NOT overwrite
artifact.html
Click any entry in the Recent dropdown to load it. Click "β Current β Live" to go back.
πͺ Launch Modes
bash ~/artifact-preview/open-chrome.sh # auto-detect β MUST use bash, not open
bash ~/artifact-preview/open-chrome.sh portrait # phone 480Γ960 (9:16) π±
bash ~/artifact-preview/open-chrome.sh horizontal # monitor 1280Γ720 (16:9) πΊ
bash ~/artifact-preview/open-chrome.sh full # maximized π₯οΈ
β οΈ Always use bash ~/artifact-preview/open-chrome.sh β NOT open ~/artifact-preview/open-chrome.sh. The open command on macOS treats the script path as a file to open in the default app, not as a shell script to execute. bash runs it properly with arguments passed through.
β¨οΈ Keyboard Shortcuts
| Shortcut | Action |
|---|---|
ββ§E |
Toggle HTML editor βοΈ |
ββ§S |
Save from editor πΎ |
ββ§R |
Refresh preview π |
π‘ Server Commands
# Start server (runs in background)
cd ~/artifact-preview && python3 server.py &
# Stop server
PIDS=$(lsof -ti :8765 2>/dev/null) && [ -n "$PIDS" ] && kill $PIDS 2>/dev/null || true
# Verify running
curl -s -o /dev/null -w "%{http_code}" http://localhost:8765/
# β 200 β
Endpoints
| Endpoint | Method | What it does |
|---|---|---|
/update |
POST | Save artifact.html + archive + live reload β‘ |
/ |
GET | Preview UI |
/artifact.html |
GET | Raw artifact HTML |
/artifacts.json |
GET | History manifest |
/events |
GET | SSE stream for live reload + history updates |
/history/<filename> |
GET | Serve archived artifact |
π οΈ Troubleshooting
Server address already in use (Errno 48)?
The lsof -ti :8765 | xargs kill pattern can silently fail if the process is zombie or orphaned. Use this instead:
# Find the actual PID holding the port
lsof -i :8765
# Example output: COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
# Python 9273 carlosmac 4u IPv4 0xeddad3b3e14e8540 0t0 TCP *:ultraseek-http (LISTEN)
# Force-kill it
kill -9 <PID>
# Verify port is free
lsof -i :8765 # should return nothing
# Restart fresh
cd ~/artifact-preview && python3 server.py &
sleep 3
curl -s -o /dev/null -w "%{http_code}" http://localhost:8765/
# β 200 β
Or verify server is already running (Errno 48 means it's already up):
curl -s -o /dev/null -w "%{http_code}" http://localhost:8765/
# β 200 means running β
Server appears alive (port 8765 responds) but POST returns empty / curl error 52?
The server process is hung β it accepts connections but can't process requests. Symptoms: curl -X POST http://localhost:8765/update returns "Empty reply from server" (curl error 52) even though GET / returns 200. Fix: kill and restart from scratch:
# Kill hung server
PIDS=$(lsof -ti :8765 2>/dev/null) && [ -n "$PIDS" ] && kill $PIDS
sleep 1
# Restart fresh
cd ~/artifact-preview && python3 server.py &
sleep 2
curl -s -o /dev/null -w "%{http_code}" http://localhost:8765/
# β 200 β
Preview not updating?
Click Refresh in toolbar (instant via SSE) or restart: PIDS=$(lsof -ti :8765 2>/dev/null) && [ -n "$PIDS" ] && kill $PIDS 2>/dev/null || true && cd ~/artifact-preview && python3 server.py &
Content clipping β card shows only half:
Fix CSS overflow: body { overflow: auto }, #container { overflow: visible }, #preview-card { overflow: visible }, #artifact-frame { overflow: auto }. Don't add height: 100% to #preview-card.
Chrome window not opening (macOS)? Grant Automation permissions: System Settings β Privacy & Security β Automation β Terminal β Google Chrome β
History dropdown empty after manual file copy?
Terminal writes don't auto-archive. Use the editor Save button (POSTs to /update) or manually POST to /update.
History stops saving across server restarts β POSTs return 200 but nothing is archived?
If history/ exists but artifacts.json does NOT, _archive_artifact() silently fails. The server appears to work (POST returns 200) but nothing gets saved. Symptoms: ls history/ is empty or stale, artifacts.json is missing or outdated.
Fix: Kill the old server and start fresh:
# Find and kill old server
OLD_PID=$(lsof -ti :8765 2>/dev/null)
[ -n "$OLD_PID" ] && kill $OLD_PID
sleep 1
# Restart β fresh server writes artifacts.json correctly
cd ~/artifact-preview && python3 server.py &
sleep 2
curl -s -o /dev/null -w "%{http_code}" http://localhost:8765/
# β 200 β
π What's New in v4.3 π₯
- Fixed: SSE live-reload β the preview UI now correctly listens for
reloadevents and refreshes the artifact iframe instantly after every POST - Fixed: Server-side broadcast reliability β verified that
event: reloadfires from/eventsSSE endpoint after every/updatePOST - Fixed: Chrome startup reliability β profile picker suppressed on cold start, no duplicate tabs when Chrome already running, window activation fixed
- Fixed: Dual-panel history recall β switching to a past artifact via the Recent dropdown now updates BOTH the preview iframe AND the code panel simultaneously
- Improved: Precise window sizing β Portrait 480Γ960 (9:16), Horizontal 1280Γ720 (16:9), all content visible without scrolling
π What's New in v4.2 π₯
- Newest versions at top of Recent dropdown
- Auto-save to history on server startup
- "β Saved to history" toast notifications
- "Save as New" button for safe variations
- Pinned "β Current β Live" in dropdown