From 884df671a1bd744d3bc004cd8a6f2b5838d24b7b Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Mon, 11 May 2026 10:17:53 +0200 Subject: feat: initial commit — Apache autoindex theme with matrix rain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - gen_web_hook.sh: generates _header.html/_footer.html per repo/category/package dir - .assets/matrix-rain.js: danix2-engine matrix rain, header-scoped (65% width, right-aligned) - Category headers include gradient accent pill with category name - htaccess: autoindex config, MIME types, cache headers, IndexIgnore .assets - vhost.conf: Apache VirtualHost template (values masked for public repo) - CLAUDE.md: repo architecture docs Co-Authored-By: Claude Sonnet 4.6 --- .assets/matrix-rain.js | 136 ++++++++ CLAUDE.md | 49 +++ docs/superpowers/plans/2026-05-11-matrix-rain.md | 373 +++++++++++++++++++++ .../specs/2026-05-11-matrix-rain-design.md | 71 ++++ gen_web_hook.sh | 342 +++++++++++++++++++ htaccess | 43 +++ vhost.conf | 33 ++ 7 files changed, 1047 insertions(+) create mode 100644 .assets/matrix-rain.js create mode 100644 CLAUDE.md create mode 100644 docs/superpowers/plans/2026-05-11-matrix-rain.md create mode 100644 docs/superpowers/specs/2026-05-11-matrix-rain-design.md create mode 100644 gen_web_hook.sh create mode 100644 htaccess create mode 100644 vhost.conf diff --git a/.assets/matrix-rain.js b/.assets/matrix-rain.js new file mode 100644 index 0000000..e22ada2 --- /dev/null +++ b/.assets/matrix-rain.js @@ -0,0 +1,136 @@ +// 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 portion of canvas area, left→transparent + var fade = document.createElement('div'); + fade.style.cssText = 'position:absolute;top:0;right:0;bottom:0;width:65%;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); + }); +})(); diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..2e61988 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,49 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this repo is + +Web frontend for `packages.danix.xyz` — an Apache autoindex-based Slackware package repository. Apache serves directory listings; this repo provides the HTML chrome injected around them. + +## How header/footer injection works + +Apache's `HeaderName` / `ReadmeName` directives (in `.htaccess`) point to `_header.html` and `_footer.html`. These static files are generated by `gen_web_hook.sh`, which walks `$PKGREPO/{category}/{package}/` and writes a pair at each level. Run standalone or as a slackrepo `HOOK_FINISH` hook. + +## Repository directory structure (on server) + +``` +/var/www/pkgs/ ← REPO_ROOT (depth 0) + category/ ← depth 1 + pkgname/ ← depth 2 + pkgname-ver.txz + pkgname-ver.txt ← parsed for title/description/Homepage + pkgname-ver.meta ← parsed for compressed/uncompressed sizes +``` + +## CSS design system + +CSS variables defined on `:root`: + +- Colors: `--bg`, `--bg-card`, `--bg-hover`, `--border`, `--accent`, `--accent-dim`, `--accent2`, `--green`, `--text`, `--text-dim`, `--text-head` +- Fonts: `--mono` (IBM Plex Mono), `--sans` (IBM Plex Sans) — loaded from Google Fonts + +## Static assets + +`.assets/matrix-rain.js` — matrix rain canvas animation. Served from `$PKGREPO/.assets/`. Loaded via ` + ${CSS} +``` + +Full `` block after change: + +```bash + + + + danix Slackware Repository + + + + ${CSS} + +``` + +- [ ] **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 ` +``` + +- [ ] **Step 4: Clean up** + +```bash +rm -rf /tmp/test-pkgrepo +``` diff --git a/docs/superpowers/specs/2026-05-11-matrix-rain-design.md b/docs/superpowers/specs/2026-05-11-matrix-rain-design.md new file mode 100644 index 0000000..17d8401 --- /dev/null +++ b/docs/superpowers/specs/2026-05-11-matrix-rain-design.md @@ -0,0 +1,71 @@ +# Matrix Rain — Design Spec +**Date:** 2026-05-11 +**Repo:** repo-html-structure (`packages.danix.xyz`) + +## Goal + +Add the matrix rain canvas animation to the `_header.html` of the Slackware package repository, matching the implementation in `danix2-hugo-theme` as closely as possible for brand consistency. + +## Architecture + +### New file: `.assets/matrix-rain.js` + +Served from `$PKGREPO/.assets/matrix-rain.js` (i.e. `https://packages.danix.xyz/.assets/matrix-rain.js`). + +Source: `danix2-hugo-theme/assets/js/matrix-rain.js` with minimal adaptations (see below). No logic changes — rendering engine, trail model, CSS var integration, font-ready wait, debounced resize, MutationObserver for theme switching all carried over verbatim. + +**Adaptations from danix2 version:** + +| danix2 | This repo | +|--------|-----------| +| Canvas = `#matrix-rain`, full window width/height | Canvas appended to `.site-header`, width = 65% of header width, height = header height | +| No fade overlay | Gradient fade `
` over left 75% of canvas (matches cgit approach) | +| `canvas.width = window.innerWidth` | `canvas.width = Math.floor(header.offsetWidth * 0.65)` | +| `canvas.height = window.innerHeight` | `canvas.height = header.offsetHeight` | +| Resize: `resizeCanvas()` uses `window.innerWidth/Height` | Resize: recalculate from `header.offsetWidth/Height` | +| Canvas positioned full-screen fixed | Canvas `position:absolute; top:0; right:0; height:100%; pointer-events:none; z-index:0` | + +Everything else (character set, trail rendering, color sampling, `hexToRgba`, `sampleColors`, `MutationObserver`, `document.fonts.ready`, debounced resize) is identical. + +### CSS variable addition: `--accent2` + +Add to `:root` in the `CSS=` heredoc in `gen_web_hook.sh`: + +```css +--accent2: #4ec97b; +``` + +This matches the existing `--green` value and aligns the var name with danix2-hugo-theme. + +The script references `--accent2` (green rain) and `--accent` (purple rain) — both already present in this repo's palette. + +### Header structure change (in `gen_web_hook.sh` → `write_header()`) + +Add `position: relative` to `.site-header` so the absolutely-positioned canvas is contained within it. + +Add `` to the `` of every generated `_header.html`. + +The canvas and fade `
` are injected by the JS itself (matching cgit pattern) — no HTML changes needed beyond the ` + ${CSS} + + + +
+EOF + log "Written: $dir/_header.html" +} + +# ── Write _header.html for a category directory (includes pill) ─────────────── +write_category_header() { + local dir="$1" + local cat_name + cat_name=$(basename "$dir") + cat > "$dir/_header.html" << EOF + + + + + + danix Slackware Repository + + + + ${CSS} + + + +
+EOF + log "Written: $dir/_header.html" +} + +# ── Generate category _footer.html (just the signature) ────────────────────── +generate_category_footer() { + local cat_dir="$1" + { echo '
'; footer_sig; } > "$cat_dir/_footer.html" + log "Written: $cat_dir/_footer.html" +} + +# ── Main ────────────────────────────────────────────────────────────────────── +write_header "$PKGREPO" +generate_root_footer + +for category in "$PKGREPO"/*/; do + [ -d "$category" ] || continue + write_category_header "$category" + generate_category_footer "$category" + for pkg in "$category"*/; do + [ -d "$pkg" ] || continue + write_header "$pkg" + generate_package_footer "$pkg" + done +done + +log "Done." diff --git a/htaccess b/htaccess new file mode 100644 index 0000000..bb92f15 --- /dev/null +++ b/htaccess @@ -0,0 +1,43 @@ +# danix Slackware package repository + +Options +Indexes + +# ── Autoindex options ───────────────────────────────────────────────────────── +IndexOptions FancyIndexing HTMLTable IgnoreCase SuppressDescription SuppressHTMLPreamble NameWidth=* FoldersFirst ScanHTMLTitles + +IndexOrderDefault Ascending Name + +# ── Header / Footer (static HTML, generated by gen_web_hook) ───────────────── +HeaderName _header.html +ReadmeName _footer.html + +# ── Hide internal files from the listing ───────────────────────────────────── +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 + +# ── Icons ───────────────────────────────────────────────────────────────────── +AddIconByType (DIR,/icons-pkg/dir.svg) httpd/unix-directory +AddIcon /icons-pkg/dir.svg ^^DIRECTORY^^ +AddIcon /icons-pkg/package.svg .txz +AddIcon /icons-pkg/signature.svg .asc +AddIcon /icons-pkg/checksum.svg .md5 .sha256 +AddIcon /icons-pkg/compressed.svg .gz .bz2 +AddIcon /icons-pkg/rss.svg .rss +AddIcon /icons-pkg/file.svg .txt .lst .meta .dep +DefaultIcon /icons-pkg/file.svg + +# ── MIME types for Slackware-specific extensions ───────────────────────────── +AddType application/octet-stream .txz .dep +AddType text/plain .txt .asc .lst .meta .md5 .sha256 + +# ── Cache control ───────────────────────────────────────────────────────────── + + Header set Cache-Control "public, max-age=86400" + + + Header set Cache-Control "public, max-age=3600" + diff --git a/vhost.conf b/vhost.conf new file mode 100644 index 0000000..03fef31 --- /dev/null +++ b/vhost.conf @@ -0,0 +1,33 @@ + + ServerName YOUR_DOMAIN + DocumentRoot /path/to/pkgrepo + + SSLEngine On + + ErrorLog "/var/log/apache2/packages_error_log" + CustomLog "/var/log/apache2/packages_access_log" common + + + Require all granted + Options +Indexes + AllowOverride All + + + # All IndexOptions, HeaderName, ReadmeName are managed in .htaccess + + Include /etc/letsencrypt/options-ssl-apache.conf + SSLCertificateFile /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem + SSLCertificateKeyFile /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem + + +# ── Required Apache modules ─────────────────────────────────────────────────── +# Check what's active: apache2ctl -M | grep -E 'autoindex|headers|php|proxy' +# +# a2enmod autoindex <- directory listings +# a2enmod headers <- Cache-Control headers in .htaccess +# +# PHP -- pick one: +# mod_php: a2enmod php8.4 +# php-fpm: a2enmod proxy_fcgi setenvif && a2enconf php8.4-fpm +# +# systemctl reload apache2 \ No newline at end of file -- cgit v1.2.3