| name | html-creator | ||||
|---|---|---|---|---|---|
| description | Create a standalone HTML page on a user-requested topic, upload it as a private GitHub gist via the local Emacs `gist-files` function, and open it through gisthost.github.io in the browser. Use when the user asks to "make an HTML page", "build a webpage about X", "share an HTML demo", or otherwise wants a one-off self-contained HTML artifact published as a gist. | ||||
| compatibility | Requires a running Emacs server with the `gist` package loaded and a working GitHub auth token for the gist API. Browser launching uses `browse-url`. | ||||
| allowed-tools | Bash(emacsclient:*), Write | ||||
| metadata |
|
Generate one self-contained HTML file on the topic the user specified, save it to a temp path, then publish it as a private GitHub gist via Emacs. After upload, open the rendered page through the gisthost proxy so the user sees the live result.
End-to-end flow:
- Author HTML at a temp path (
/tmp/<slug>.html). - Call
gist-filesthroughemacsclient --evalwithprivate=tand a callback that browses tohttps://gisthost.github.io/?<gist-id>. - Report the gisthost URL to the user.
- Single file. No external CSS/JS files. Inline
<style>and<script>. - CDN links allowed when topic needs a library (D3, Chart.js, Tailwind via CDN, etc.). Prefer pinned versions.
<!DOCTYPE html>,<html lang="en">,<meta charset="utf-8">,<meta name="viewport" content="width=device-width, initial-scale=1">.- Sensible
<title>based on topic. - Default styling readable on mobile + desktop. Dark-mode friendly via
prefers-color-schemewhen not contradicted by user request. - No tracking, no analytics, no external fonts unless user asked.
- Pure static. No build step.
- File name: short kebab-case slug derived from topic,
.htmlextension.
Source material (org-mode notes, Markdown, internal wikis) often carries links that resolve only on the author's machine. Published HTML must not contain any of them — gist viewers, mobile browsers, and other people's machines cannot follow them. Remove or rewrite:
file://URLs.- Relative paths:
./foo.org,../notes/bar.md,~/Notes/..., bareNotes/.... - Org-mode internal targets:
[[id:...]],[[*Heading]],[[#custom-id]],[[file:...]]. - Markdown footnote-style refs that point at undefined local anchors.
localhost,127.0.0.1, private IP ranges (10.x, 192.168.x, 172.16–31.x).emacsclient:,org-protocol:,x-callback-url:, or other scheme handlers only registered locally.- Intranet hostnames (no public DNS) — heuristic: single-label host
or
.local,.lan,.internal, company-private TLDs.
Rewrite rule: if the link text is meaningful, keep the text and drop
the <a> wrapper. If the link was the only signal, drop it entirely.
Never invent a public URL to replace a local one. Same goes for
<img src>, <link>, <script src> — only https:// URLs survive.
Goal: turn a document the user would skim into one they would actually
read. Pick an archetype, then commit to the affordances that archetype
implies. Prefer spatial layout over prose dumps. Use SVG for diagrams,
inline <script> for small interactions (≤ ~30 lines), CSS variables
for theming.
Style defaults when user did not specify:
- System font stack. No web fonts.
- Max content width ~72ch for prose, full-bleed for dashboards.
prefers-color-schemelight/dark variants via CSS variables.- Color-code semantics: ok/warn/error, severity, diff add/remove. Pick 3–5 accent colors, define as variables.
- Generous whitespace, sticky in-page nav when content > one screen.
- Copy-to-clipboard buttons on code blocks and exportable artifacts.
JS rules of thumb:
- No framework unless user asked. Vanilla DOM is fine for small UIs.
- Reach for libraries (via CDN, pinned) only when topic demands them:
- D3 / Chart.js for non-trivial charts.
- Mermaid for flowcharts when ad-hoc SVG would be tedious.
- Tailwind via CDN if user wants a polished marketing-style page.
- Arrow-key navigation, drag-and-drop, sliders, tabs, collapsibles all doable in under 50 lines — prefer those over heavier deps.
Match user's topic to an archetype, then bias the output toward its default affordances:
| Archetype | Affordances |
|---|---|
| Explainer / concept teardown | TL;DR box, collapsible sections, inline glossary on hover, SVG diagrams |
| Code review / PR summary | Inline diff with margin annotations, severity-tagged findings, jump links, color-coded changes |
| Module map / architecture | Boxes-and-arrows SVG, highlighted hot path, click-to-expand node details |
| Slide deck | Section-per-slide, arrow-key + space navigation, slide counter, full-screen mode |
| Design system / palette | Swatch grid, copy-able tokens, contrast badge |
| Post-mortem | Timeline, log excerpts in <pre>, follow-up checklist |
| Status report / dashboard | KPI cards, embedded chart, last-updated stamp |
| Triage board | Drag-and-drop columns, markdown export button |
| Feature-flag panel | Grouped toggles, dependency warnings, diff-copy button |
| Prompt tuner / playground | Split panel, live variable re-render, copy-prompt button |
| Animation sandbox | Isolated transition, duration/easing sliders |
| Single-file mini-app | index.html mindset: no deps, sparse styling, do one thing |
Two prompt shapes seen in the wild that produce good HTML output — use these as templates when the user gives only a topic and no structural hints:
-
Rich explainer:
"Explain {topic} in detail. Reformat it, expand confusing bits, go deep into how it works. Output HTML, neatly styled, using HTML/CSS/JavaScript to make the explanation rich, interactive, and as clear as possible."
-
Single-file mini-app:
"In a single index.html, no dependencies, sparse styling, create an app that {does X}."
If the user's request is vague, ask one short clarifying question about archetype (explainer vs. mini-app vs. report) before generating — a wrong-archetype draft wastes more time than a 5-second check.
Call gist-files with one-element filename list, private=t, callback that extracts gist id and browses gisthost:
emacsclient --eval "$(cat <<'EOF'
(gist-files
(list "/tmp/<slug>.html")
t
(lambda (gist)
(let ((id (oref gist :id)))
(browse-url (format "https://gisthost.github.io/?%s" id))
(kill-new (format "https://gisthost.github.io/?%s" id))
(message "html-creator: %s" id))))
EOF
)"Notes:
gist-filessignature:(filenames &optional private callback). Passtfor private.- Callback receives a
gh-gist-gistobject. Use(oref gist :id)for the gist id,(oref gist :html-url)for the raw gist page if needed. gisthost.github.iorenders the gist's HTML file directly, so the URL form ishttps://gisthost.github.io/?<id>— query string is the bare id, no path.- Call is async inside Emacs;
emacsclientreturns before the gist exists. The callback handlesbrowse-urlonce the API responds.
After firing the elisp call, tell the user:
- Topic / title of the HTML page.
- Local path of the temp file.
- The gisthost URL pattern (
https://gisthost.github.io/?<id>) — exact id arrives once Emacs's async callback runs; the user's browser will pop open with it. The id is also copied tokill-ringby the callback.
If the user wants the canonical gist page instead of the rendered preview, give them https://gist.github.com/<user>/<id> or (oref gist :html-url).
- Use the heredoc form (
emacsclient --eval "$(cat <<'EOF' ... EOF)") so the lambda body keeps its double quotes intact. - Avoid embedding the HTML body inside the elisp form — always go through a file path.
gist-filesreads the file itself. - Temp path must not contain shell metacharacters; stick to
/tmp/<slug>.htmlwith kebab-case slug.
- If emacsclient fails, ask user to
M-x server-start. *ERROR*: (void-function gist-files)→ package not loaded. Ask user to(require 'gist).- Auth failure → Emacs will prompt for a GitHub token in its own minibuffer; tell the user to check the Emacs frame.
browse-urlopens a non-default browser → not this skill's concern;browse-url-browser-functionis user-configured.