Agent skill
LC Algo Demo
36 pages, no generator
The visualizers are 36 hand-written pages plus
common.js and style.css, copied wholesale by the build. Nothing
compiles them and nothing templates them, so every shared behaviour is held together by
convention — which means a page that reimplements one looks fine until the theme
changes. /lc-algo-demo writes the 37th against the contract.
1 neighbour matched kadane.html โ one array walk, a structure beside it
2 helpers VIZ.bar ยท VIZ.pointer ยท VIZ.readout
three drawing helpers NOT written
3 write algo_demo/monotonic-stack.html
draw(i, stack, popped)
draw = VIZ.repaintable(draw)
addEventListener('resize', draw.repaint)
4 index card under "Stack & Queue":
"Next greater element in one pass. LC 496, 503, 739, 84."
5 exercise trace i=3 h=2
h[3] < stack top โ pop
outcome one highlight span at the end
theme switch mid-run โ highlight survived
resize mid-run โ highlight survived
6 assumed --viz-swap reused for "on the stack". Correct me.
Anything that should look the same on all 37 pages belongs in common.js or style.css, never in a page's inline script.
Why a skill and not a habit
Three things you get for free, and must not rebuild
Each of these is already done once, for every page. Re-doing one in a page's inline script is invisible in review and wrong the moment something moves.
Canvases read colours from VIZ — the --viz-* tokens — so both themes and any repaint stay in one place. A literal #3b82f6 is fine in one theme and wrong in the other.
Rule: never hardcode a colour.
getContext('2d') is wrapped once: the backing store is scaled to devicePixelRatio behind width/height accessors, and the font setter rewrites the generic families to the site's face.
Cost of rebuilding it: a double-scaled canvas.
A draw() that takes arguments needs VIZ.repaintable. Handed straight to addEventListener it receives the resize Event as its first drawing argument; called with reset arguments it throws the highlight away.
And a theme switch fires a resize. Either way the picture reverts mid-run.
One pass, no branches
Six steps, in order
Pick one to see what it does and the rule that step exists to enforce.
What the page must obey
The shared contract
The skeleton, the trace's three shapes, and the one wrapper that is easy to get wrong.
<script src="../nav.js"></script> <- before common.js, always
<script src="common.js"></script>
...
<div id="site-nav" data-page="visualizer" data-base="../"></div>
<script>CSNav.mount();</script>
<main class="container">
<div class="breadcrumbs">โฆ<span class="current">Your Algorithm</span></div>
<div class="page-header">โฆ</div>
<div class="viz-wrapper">
<div class="viz-canvas-area"><canvas id="canvas"></canvas></div>
<div class="legend">โฆ</div> <- dots use var(--viz-*)
<div class="viz-controls">โฆ</div> <- 300px column: controls ONLY
</div>
<section class="viz-trace"> <- full width, below the canvas
โฆ<div class="viz-log" id="log"></div>
</section>
</main>
The trace was once in the 300px control column, where L=4 R=24 sum=28
wrapped three times and four steps filled the box. Put it back there and the trace
stops being readable.
createLogger(id)'s contract is still clear() and
log(html, cls). What it does with the message depends on its shape:
| Written | Rendered as |
|---|---|
| log('L=4 R=24 sum=28') | a numbered step |
| log(' sum > target โ move R') | the reason, indented and tucked under the step above it |
| log('--- Iteration 2 ---') | a phase heading |
| log('<span class="highlight">Found!</span>') | the run's outcome |
| log('') | ignored — rows are already spaced |
The outcome test is the message being entirely one highlight span. Pages also
use that span mid-sentence to pick out a value —
Process node <span class="highlight">1</span> dist=7 — and
counting those as outcomes flags nine rows in ten, which marks nothing. The logger
also auto-decorates name=value pairs and arrows, so write them plainly.
function draw(highlight, phase) { ... }
// wrong - draw gets the resize Event as `highlight`
window.addEventListener('resize', draw);
// wrong - resets the arguments, throwing the highlight away
window.addEventListener('resize', function () { draw(); });
// right
draw = VIZ.repaintable(draw);
window.addEventListener('resize', draw.repaint);
A theme switch fires a resize, so both wrong forms have the same symptom: flip the theme mid-run and the picture reverts to its starting state. That is the check in step 5, and it is the one that catches this.
Arguments are inferred, not interrogated
How to call it
/lc-algo-demo monotonic-stack
/lc-algo-demo trie
add a visualizer for Dijkstra
animate the sliding window
the trace is unreadable on the segment tree page
| Left out | What happens |
|---|---|
| Which page to copy | Matched on the shape of the thing being drawn — array walk, grid, tree, graph — not on the topic name. |
| Index section | Chosen from the existing section-labels, and named in the report. |
| Palette tokens | Reused from a page that means the same thing by them, rather than invented. |
| The algorithm | The one thing it will ask for. |
The guardrails
What it will not do
- Hardcode a colour.Every canvas colour comes from
VIZ, so both themes and any future repaint stay in one place. - Re-scale the canvas.
getContext('2d')already does it for every page. Doing it again double-scales. - Move the trace into the control column.It lived there once. Four steps filled the box and every readout wrapped three times.
- Copy a shared behaviour into a page.It goes in
common.jsorstyle.css, wherealgo-demo.test.jscan see it — a page's inline script is untestable by construction. - Add an external
<script>tag.e2e-check.jsfails the build on one, on every page. - Commit or push unless asked.It writes files and stops.
One markdown file, no dependencies
Install
SKILL.md is the whole recipe — nothing to build and no network calls,
so the same source runs on any agent that takes a system prompt. Pick yours.
Drop the skill directory into your user-level skills folder and it loads in every repo:
git clone --depth 1 https://github.com/yennanliu/CS_basics.git /tmp/cs_basics
mkdir -p ~/.claude/skills
cp -r /tmp/cs_basics/.claude/skills/lc-algo-demo ~/.claude/skills/
Already installed inside this repo at .claude/skills/lc-algo-demo/. The
directory name is the command.
Zip the directory, then Customize → Skills → + → + Create skill → Upload a skill:
cd .claude/skills && zip -r lc-algo-demo.zip lc-algo-demo
Leave the YAML frontmatter intact — description is what Claude
matches your request against.
Codex reads AGENTS.md at the repo root automatically. Point it at the skill:
## Adding an algorithm visualizer
When asked to add a visualizer, animate an algorithm, or change something
that should look the same on every visualizer, follow
`.claude/skills/lc-algo-demo/SKILL.md`.
A pointer, not a copy — one source of truth means a fix reaches every agent at once.
Same shape in GEMINI.md, or point at it for a single session:
gemini -p "Follow the recipe in .claude/skills/lc-algo-demo/SKILL.md. \
Add a monotonic stack visualizer."
Paste SKILL.md in as the system prompt. For Cursor or Windsurf, put the
Codex pointer above into a rule file
(.cursor/rules/lc-algo-demo.mdc or the editor's equivalent).
curl -sL https://raw.githubusercontent.com/yennanliu/CS_basics/master/.claude/skills/lc-algo-demo/SKILL.md
This one is the most repo-specific of the family: the contract it enforces is
this common.js's. Away from here, the transferable part is the
rule — shared behaviour in one shared file, never copied into a page.
Under the hood
What is inside
- SKILL.md The whole recipe — the five prime directives, the step trace's three shapes, the six steps, the page skeleton, the do-not list, and a worked run.
Gated in CI by
check_skills.py,
and the shared file it defends is gated by site/test/algo-demo.test.js,
which evaluates the shipped common.js in jsdom.
The rest of the loop
Where it fits
A visualizer is for the algorithm you cannot yet see running in your head. The cheatsheets hold the template, /lc-cheatsheet files what you learned, and /lc-python files the solution — this one builds the picture that makes the template obvious.