diff options
Diffstat (limited to 'docs/superpowers/plans/2026-05-11-matrix-rain.md')
| -rw-r--r-- | docs/superpowers/plans/2026-05-11-matrix-rain.md | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/docs/superpowers/plans/2026-05-11-matrix-rain.md b/docs/superpowers/plans/2026-05-11-matrix-rain.md new file mode 100644 index 0000000..e7b38e1 --- /dev/null +++ b/docs/superpowers/plans/2026-05-11-matrix-rain.md @@ -0,0 +1,373 @@ +# Matrix Rain Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add matrix rain canvas animation to `packages.danix.xyz` headers, using the `danix2-hugo-theme` engine adapted for the header-scoped layout. + +**Architecture:** A single JS file at `.assets/matrix-rain.js` is served from the repo root and loaded via `<script defer>` in every generated `_header.html`. The script appends a canvas + gradient fade div to `.site-header` at runtime. CSS vars `--accent`, `--accent2`, `--bg` drive colors — no hardcoded values. + +**Tech Stack:** Vanilla JS (ES5-compatible IIFE), HTML Canvas API, Apache `.htaccess`, bash (`gen_web_hook.sh`) + +--- + +### Task 1: Add `--accent2` CSS variable and `position:relative` to `.site-header` + +**Files:** +- Modify: `gen_web_hook.sh` (CSS heredoc and `.site-header` rule) + +- [ ] **Step 1: Add `--accent2` to the `:root` block in the `CSS=` heredoc** + +In `gen_web_hook.sh`, find the `:root {` block inside the `CSS='<style>` heredoc (around line 37). Add `--accent2` after `--green`: + +```bash +# Before: + --green: #4ec97b; + +# After: + --green: #4ec97b; + --accent2: #4ec97b; +``` + +- [ ] **Step 2: Add `position: relative` to `.site-header`** + +In the same `CSS=` heredoc, find the `.site-header {` rule (around line 56). Add `position: relative;`: + +```css +.site-header { border-bottom: 1px solid var(--border); padding: 2rem 2.5rem 1.5rem; background: var(--bg-card); position: relative; overflow: hidden; } +``` + +(`overflow: hidden` clips the canvas to the header bounds.) + +- [ ] **Step 3: Verify the heredoc is syntactically intact** + +```bash +bash -n gen_web_hook.sh +``` + +Expected: no output (no syntax errors). + +- [ ] **Step 4: Commit** + +```bash +git add gen_web_hook.sh +git commit -m "style: add --accent2 CSS var and position:relative to .site-header" +``` + +--- + +### Task 2: Create `.assets/matrix-rain.js` + +**Files:** +- Create: `.assets/matrix-rain.js` + +This is the danix2 engine with canvas targeting changed from `#matrix-rain` (full-screen) to a dynamically created canvas appended to `.site-header` (65% width, right-aligned, with gradient fade div). + +- [ ] **Step 1: Create the `.assets/` directory and write the file** + +```bash +mkdir -p .assets +``` + +Write `.assets/matrix-rain.js` with the following content (danix2 engine, header-scoped): + +```javascript +// Matrix rain background effect — header-scoped build for packages.danix.xyz +// Derived from danix2-hugo-theme/assets/js/matrix-rain.js +(function() { + if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; + + var header = document.querySelector('.site-header'); + if (!header) return; + + // Canvas + var canvas = document.createElement('canvas'); + canvas.id = 'matrix-rain'; + canvas.style.cssText = 'position:absolute;top:0;right:0;height:100%;pointer-events:none;z-index:0;'; + header.appendChild(canvas); + + // Gradient fade div — covers left 75% of canvas area, left→transparent + var fade = document.createElement('div'); + fade.style.cssText = 'position:absolute;top:0;right:0;bottom:0;width:' + Math.floor(0.65 * 100) + '%;background:linear-gradient(to right,var(--bg-card) 0%,transparent 60%);pointer-events:none;z-index:1;'; + header.appendChild(fade); + + var ctx = canvas.getContext('2d'); + + var columns = []; + var frameCount = 0; + var colors = { accent: '#5c9cf5', accent2: '#4ec97b', bg: '#161b25', head: '#ffffff' }; + + var ASCII = Array.from({ length: 94 }, function(_, i) { return String.fromCharCode(33 + i); }); + var KATA = Array.from({ length: 96 }, function(_, i) { return String.fromCodePoint(0x30a0 + i); }); + var CHARS = shuffle([].concat(ASCII, KATA, KATA, KATA)); + + function shuffle(arr) { + for (var i = arr.length - 1; i > 0; i--) { + var j = Math.floor(Math.random() * (i + 1)); + var tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; + } + return arr; + } + + function hexToRgba(color, alpha) { + var rgbMatch = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/); + if (rgbMatch) return 'rgba(' + rgbMatch[1] + ',' + rgbMatch[2] + ',' + rgbMatch[3] + ',' + alpha + ')'; + var hex = color.replace('#', ''); + var r = parseInt(hex.substring(0, 2), 16); + var g = parseInt(hex.substring(2, 4), 16); + var b = parseInt(hex.substring(4, 6), 16); + return 'rgba(' + r + ',' + g + ',' + b + ',' + alpha + ')'; + } + + function sampleColors() { + var style = getComputedStyle(document.documentElement); + var isDark = !document.documentElement.classList.contains('theme-light'); + colors.accent = style.getPropertyValue('--accent').trim() || '#5c9cf5'; + colors.accent2 = style.getPropertyValue('--accent2').trim() || '#4ec97b'; + colors.bg = style.getPropertyValue('--bg-card').trim() || '#161b25'; + colors.head = isDark ? '#ffffff' : '#1a0533'; + } + + function resizeCanvas() { + canvas.width = Math.floor(header.offsetWidth * 0.65); + canvas.height = header.offsetHeight; + ctx.font = '14px "IBM Plex Mono", monospace'; + ctx.textBaseline = 'top'; + initColumns(); + } + + function initColumns() { + columns = []; + var columnWidth = 14; + var columnCount = Math.floor(canvas.width / columnWidth); + for (var i = 0; i < columnCount; i++) { + columns.push({ + x: i * columnWidth, + y: -Math.floor(Math.random() * 40), + speed: 2 + Math.floor(Math.random() * 3), + color: Math.random() < 0.6 ? 'accent2' : 'accent', + charIndex: Math.floor(Math.random() * CHARS.length), + length: 8 + Math.floor(Math.random() * 13) + }); + } + } + + function setupThemeObserver() { + var observer = new MutationObserver(function(mutations) { + for (var i = 0; i < mutations.length; i++) { + if (mutations[i].attributeName === 'class') { sampleColors(); break; } + } + }); + observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] }); + } + + function drawFrame() { + frameCount++; + ctx.fillStyle = hexToRgba(colors.bg, 0.085); + ctx.fillRect(0, 0, canvas.width, canvas.height); + + for (var ci = 0; ci < columns.length; ci++) { + var col = columns[ci]; + if (frameCount % col.speed !== 0) continue; + + ctx.fillStyle = colors[col.color]; + for (var i = 1; i <= col.length; i++) { + var trailY = (col.y - i) * 14; + if (trailY < 0) continue; + var trailCharIndex = (col.charIndex - i + CHARS.length) % CHARS.length; + ctx.fillText(CHARS[trailCharIndex], col.x, trailY); + } + + ctx.fillStyle = colors.head; + ctx.fillText(CHARS[col.charIndex % CHARS.length], col.x, col.y * 14); + + col.y++; + col.charIndex = (col.charIndex + 1) % CHARS.length; + + if (col.y * 14 > canvas.height + col.length * 14) { + col.y = -Math.floor(Math.random() * 20); + col.charIndex = Math.floor(Math.random() * CHARS.length); + col.color = Math.random() < 0.6 ? 'accent2' : 'accent'; + } + } + + requestAnimationFrame(drawFrame); + } + + sampleColors(); + resizeCanvas(); + setupThemeObserver(); + + var resizeTimer; + window.addEventListener('resize', function() { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(resizeCanvas, 150); + }); + + document.fonts.ready.then(function() { + requestAnimationFrame(drawFrame); + }); +})(); +``` + +- [ ] **Step 2: Commit** + +```bash +git add .assets/matrix-rain.js +git commit -m "feat: add matrix-rain.js (danix2 engine, header-scoped)" +``` + +--- + +### Task 3: Wire `<script>` tag into `write_header()` in `gen_web_hook.sh` + +**Files:** +- Modify: `gen_web_hook.sh` (`write_header()` function) + +- [ ] **Step 1: Add the `<script>` tag to `write_header()`** + +In `gen_web_hook.sh`, find `write_header()` (around line 241). Inside the `cat > "$dir/_header.html" << EOF` block, add the script tag in `<head>`, after the Google Fonts `<link>` and before `${CSS}`: + +```bash + <script src="/.assets/matrix-rain.js" defer></script> + ${CSS} +``` + +Full `<head>` block after change: + +```bash +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>danix Slackware Repository</title> + <link rel="preconnect" href="https://fonts.googleapis.com"> + <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&family=IBM+Plex+Sans:wght@300;400;600&display=swap" rel="stylesheet"> + <script src="/.assets/matrix-rain.js" defer></script> + ${CSS} +</head> +``` + +- [ ] **Step 2: Verify syntax** + +```bash +bash -n gen_web_hook.sh +``` + +Expected: no output. + +- [ ] **Step 3: Commit** + +```bash +git add gen_web_hook.sh +git commit -m "feat: load matrix-rain.js in generated _header.html" +``` + +--- + +### Task 4: Hide `.assets/` from Apache directory listing + +**Files:** +- Modify: `htaccess` + +- [ ] **Step 1: Add `IndexIgnore .assets` to `htaccess`** + +In `htaccess`, find the `IndexIgnore` line (around line 15). Append `.assets` to it: + +```apache +IndexIgnore _header.html _footer.html .htaccess .htpasswd \ + CHECKSUMS.md5 CHECKSUMS.md5.asc CHECKSUMS.md5.gz CHECKSUMS.md5.gz.asc \ + FILELIST.TXT MANIFEST.bz2 \ + PACKAGES.TXT PACKAGES.TXT.gz \ + ChangeLog.txt.gz GPG-KEY \ + .assets +``` + +- [ ] **Step 2: Commit** + +```bash +git add htaccess +git commit -m "chore: hide .assets dir from Apache autoindex" +``` + +--- + +### Task 5: Update CLAUDE.md + +**Files:** +- Modify: `CLAUDE.md` + +- [ ] **Step 1: Add `.assets/` and `--accent2` notes to CLAUDE.md** + +In the **CSS design system** section, update the CSS variables list to include `--accent2`: + +```markdown +- Colors: `--bg`, `--bg-card`, `--bg-hover`, `--border`, `--accent`, `--accent-dim`, `--accent2`, `--green`, `--text`, `--text-dim`, `--text-head` +``` + +Add a new **Static assets** section after the CSS section: + +```markdown +## Static assets + +`.assets/matrix-rain.js` — matrix rain canvas animation. Served from `$PKGREPO/.assets/`. Loaded via `<script defer>` in every `_header.html`. Hidden from Apache autoindex via `IndexIgnore .assets` in `.htaccess`. When updating, copy the adapted file to `$PKGREPO/.assets/matrix-rain.js` on the server — it is not regenerated by `gen_web_hook.sh`. +``` + +- [ ] **Step 2: Commit** + +```bash +git add CLAUDE.md +git commit -m "docs: document .assets/ dir and --accent2 var in CLAUDE.md" +``` + +--- + +### Task 6: Smoke-test the generated output + +**Files:** none (verification only) + +- [ ] **Step 1: Run `gen_web_hook.sh` against a test directory** + +```bash +mkdir -p /tmp/test-pkgrepo/audio/ffmpeg +echo "ffmpeg: FFmpeg multimedia framework" > /tmp/test-pkgrepo/audio/ffmpeg/ffmpeg-6.1-x86_64-1.txt +bash gen_web_hook.sh /tmp/test-pkgrepo +``` + +Expected output: +``` +gen_web_hook: Generating static web files in /tmp/test-pkgrepo ... +gen_web_hook: Written: /tmp/test-pkgrepo/_header.html +gen_web_hook: Written: /tmp/test-pkgrepo/_footer.html +gen_web_hook: Written: /tmp/test-pkgrepo/audio/_header.html +gen_web_hook: Written: /tmp/test-pkgrepo/audio/_footer.html +gen_web_hook: Written: /tmp/test-pkgrepo/audio/ffmpeg/_header.html +gen_web_hook: Written: /tmp/test-pkgrepo/audio/ffmpeg/_footer.html +gen_web_hook: Done. +``` + +- [ ] **Step 2: Verify `_header.html` contains expected elements** + +```bash +grep -c 'matrix-rain.js' /tmp/test-pkgrepo/_header.html +grep -c '\-\-accent2' /tmp/test-pkgrepo/_header.html +grep -c 'position: relative' /tmp/test-pkgrepo/_header.html +grep -c 'overflow: hidden' /tmp/test-pkgrepo/_header.html +``` + +Expected: each command prints `1`. + +- [ ] **Step 3: Validate HTML structure** + +```bash +grep -A2 'matrix-rain' /tmp/test-pkgrepo/_header.html +``` + +Expected output contains: +```html +<script src="/.assets/matrix-rain.js" defer></script> +``` + +- [ ] **Step 4: Clean up** + +```bash +rm -rf /tmp/test-pkgrepo +``` |
