mirror of
https://github.com/tiennm99/programming-fengshui.git
synced 2026-09-02 08:21:02 +00:00
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
52 lines
1.9 KiB
JavaScript
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();
|
|
}
|