Files
programming-fengshui/js/main.js
T
tiennm99 5efccffede feat: add classic/modern dual-mode with HSL color classifier
Classic mode reproduces the 2018 toidicodedao image and its 5 hardcoded
element cards verbatim. Modern mode renders a data-driven grid built from
GitHub Linguist colors classified into Ngũ Hành elements via a deterministic
HSL rule set. Toggle persists via URL hash; both panels coexist in DOM with
a CSS opacity fade. Includes a collapsed debug panel listing skipped (no
color) and borderline (near hue boundary) entries.

- data/github-colors.json: vendored from ozh/github-colors (722 entries)
- js/classify-element.js: pure HSL classifier (664 lang sample, 22/22 fixtures pass)
- js/mode-toggle.js: tablist with hash persistence + arrow-key nav
- js/render-elements.js: chip rendering with YIQ contrast + debug panel
- js/main.js: fetch + classify + render entry point
- style.css: segmented toggle, panel fade, chip pill, debug-panel, mobile
2026-04-27 09:19:18 +07:00

52 lines
1.9 KiB
JavaScript

import { classify } from './classify-element.js';
import { renderGrid, renderError, renderDebugPanel, isBorderline } from './render-elements.js';
const HEX_RE = /^#[0-9a-fA-F]{6}$/;
const LEGEND_TEXT =
'Phân loại theo tông màu HSL: đỏ/tím/cam đậm → HOẢ, xanh lá/cyan → MỘC, xanh dương → THUỶ, vàng/nâu → THỔ, trắng/xám sáng → KIM.';
async function init() {
const gridEl = document.getElementById('element-grid');
const legendEl = document.querySelector('#panel-modern .legend');
const elementsSection = document.querySelector('#panel-modern .elements');
const debugEl = document.getElementById('debug-panel');
try {
const res = await fetch('./data/github-colors.json');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
const buckets = { kim: [], moc: [], thuy: [], hoa: [], tho: [] };
const skipped = [];
const borderline = [];
for (const [name, entry] of Object.entries(data)) {
const color = entry && entry.color;
if (!color || !HEX_RE.test(color)) {
skipped.push({ name });
continue;
}
const element = classify(color);
buckets[element].push({ name, color });
if (isBorderline(color)) borderline.push({ name, color, element });
}
for (const key of Object.keys(buckets)) {
buckets[key].sort((a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }));
}
renderGrid(buckets, gridEl);
if (legendEl) legendEl.textContent = LEGEND_TEXT;
renderDebugPanel({ skipped, borderline }, debugEl);
} catch (err) {
console.error('[programming-fengshui] failed to render modern grid:', err);
renderError(`Không tải được dữ liệu màu (${err.message}). Mở qua HTTP server thay vì file://.`, elementsSection);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}