371 lines
12 KiB
HTML
371 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Design Spec Samples Catalog | Dynamic Registry</title>
|
|
<style>
|
|
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;700&family=Inter:wght@400;500;700&family=JetBrains+Mono:wght@400;500&display=swap');
|
|
|
|
:root {
|
|
--bg-canvas: #0d0f12;
|
|
--bg-surface: #121417;
|
|
--bg-elevated: #1c1f26;
|
|
--border-color: #2b303b;
|
|
--text-primary: #e0e6f0;
|
|
--text-muted: #8a95a5;
|
|
--accent-cyan: #00f0ff;
|
|
--accent-green: #00e676;
|
|
--accent-amber: #ffb700;
|
|
}
|
|
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
|
|
body {
|
|
background-color: var(--bg-canvas);
|
|
color: var(--text-primary);
|
|
font-family: 'Inter', sans-serif;
|
|
line-height: 1.6;
|
|
max-width: 860px;
|
|
margin: 40px auto;
|
|
padding: 0 20px;
|
|
}
|
|
|
|
/* Header Identity */
|
|
.catalog-header {
|
|
border-bottom: 1px solid var(--border-color);
|
|
padding-bottom: 16px;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.catalog-title {
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 1.6rem;
|
|
font-weight: 700;
|
|
color: var(--text-primary);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
.status-dot {
|
|
width: 10px;
|
|
height: 10px;
|
|
background-color: var(--accent-cyan);
|
|
display: inline-block;
|
|
}
|
|
|
|
.catalog-meta {
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 0.82rem;
|
|
color: var(--text-muted);
|
|
margin-top: 6px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
/* Search Filter Input Bar */
|
|
.filter-bar {
|
|
margin-bottom: 24px;
|
|
display: flex;
|
|
gap: 12px;
|
|
}
|
|
|
|
.search-input {
|
|
background-color: var(--bg-surface);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 4px;
|
|
color: var(--text-primary);
|
|
font-family: 'JetBrains Mono', monospace;
|
|
font-size: 0.85rem;
|
|
padding: 10px 14px;
|
|
width: 100%;
|
|
outline: none;
|
|
}
|
|
|
|
.search-input:focus {
|
|
border-color: var(--accent-cyan);
|
|
}
|
|
|
|
/* Dynamic Catalog Cards List */
|
|
.samples-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
}
|
|
|
|
.sample-card {
|
|
background-color: var(--bg-surface);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 4px;
|
|
padding: 16px;
|
|
transition: none; /* Instant snap */
|
|
}
|
|
|
|
.sample-card:hover {
|
|
border-color: var(--accent-cyan);
|
|
background-color: var(--bg-elevated);
|
|
}
|
|
|
|
.sample-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.sample-link {
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 1.05rem;
|
|
font-weight: 700;
|
|
color: var(--text-primary);
|
|
text-decoration: none;
|
|
transition: none;
|
|
}
|
|
|
|
.sample-link:hover {
|
|
color: var(--accent-cyan);
|
|
}
|
|
|
|
.badge {
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 0.72rem;
|
|
padding: 2px 6px;
|
|
border-radius: 2px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.badge-html { background-color: rgba(0, 240, 255, 0.15); color: var(--accent-cyan); }
|
|
.badge-md { background-color: rgba(255, 183, 0, 0.15); color: var(--accent-amber); }
|
|
|
|
.sample-filename {
|
|
font-family: 'JetBrains Mono', monospace;
|
|
font-size: 0.78rem;
|
|
color: var(--text-muted);
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.sample-desc {
|
|
font-size: 0.88rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
/* Footer */
|
|
.catalog-footer {
|
|
margin-top: 40px;
|
|
padding-top: 16px;
|
|
border-top: 1px solid var(--border-color);
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 0.78rem;
|
|
color: var(--text-muted);
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<header class="catalog-header">
|
|
<h1 class="catalog-title">
|
|
<span class="status-dot"></span>
|
|
DESIGN SPEC SAMPLES CATALOG
|
|
</h1>
|
|
<div class="catalog-meta">
|
|
<span>PATH: /samples/</span>
|
|
<span id="file-counter">SCANNING DIRECTORY...</span>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Filter Input -->
|
|
<div class="filter-bar">
|
|
<input type="text" id="search-box" class="search-input" placeholder="Search samples by name, keyword, or filename... ($ grep -i)" oninput="filterSamples()">
|
|
</div>
|
|
|
|
<!-- Dynamic Catalog Container -->
|
|
<main id="samples-container" class="samples-list">
|
|
<!-- Rendered dynamically by JavaScript -->
|
|
</main>
|
|
|
|
<footer class="catalog-footer">
|
|
<div>Unified Design Language Compliant</div>
|
|
<div>POSIX / Terminal Minimalist Spec</div>
|
|
</footer>
|
|
|
|
<script>
|
|
// Known files catalog with fallback metadata definitions
|
|
const knownFiles = [
|
|
{
|
|
file: 'unified-design-specification.md',
|
|
title: 'Unified Design Specification (Master Brief)',
|
|
type: 'md',
|
|
desc: 'The master specification uniting Homelab Services Directory, Technical Blog/Notes, and Resume/Portfolio into a dark slate (#0d0f12), dual-typography (IBM Plex Mono + Inter), instant snap interaction language.'
|
|
},
|
|
{
|
|
file: 'sample-dashboard.html',
|
|
title: 'Sample 8: Terminal Workspace Dashboard (3-Tile Split)',
|
|
type: 'html',
|
|
desc: 'A 3-tile modal editor workspace layout (Neovim/Tmux style) featuring live homelab services, resume bio, article log stream, and interactive vim command bar.'
|
|
},
|
|
{
|
|
file: 'sample-command-center.html',
|
|
title: 'Sample 9: Command Center (Zed/Ghostty Tabbed Utility)',
|
|
type: 'html',
|
|
desc: 'A tabbed single-pane utility layout inspired by Zed Editor and Ghostty. Features keyboard navigation hotkeys [1], [2], [3] for Services, Blog, and Resume tabs.'
|
|
},
|
|
{
|
|
file: 'sample-dual-pane-explorer.html',
|
|
title: 'Sample 10: Dual-Pane Tree Explorer (Helix File Tree)',
|
|
type: 'html',
|
|
desc: 'A split-screen file-tree explorer featuring a left tree sidebar, document view container, and an interactive ASCII daemon mascot (sys-daemon) reacting to item selection.'
|
|
},
|
|
{
|
|
file: 'homepage.html',
|
|
title: 'Sample 1: Developer Homepage (Terminal Minimalist Spec)',
|
|
type: 'html',
|
|
desc: 'A dark-mode native developer homepage modeled after modern, high-density terminal setups and IDEs. Adheres strictly to zero glassmorphism and solid color layouts.'
|
|
},
|
|
{
|
|
file: 'split-panel.html',
|
|
title: 'Sample 2: Split Panel Layout (ASCII Animation & Scrollable Tiles)',
|
|
type: 'html',
|
|
desc: 'A dashboard layout with a sticky site nav header, left panel with animated terminal ASCII character, and right panel with scrollable developer project cards.'
|
|
},
|
|
{
|
|
file: 'companion.html',
|
|
title: 'Sample 3: Companion Layout (Interactive Daemon & Minimal Cards)',
|
|
type: 'html',
|
|
desc: 'A 50/50 vertical split layout with content cards on the left and a persistent, reactive ASCII companion on the right with discrete state updates.'
|
|
},
|
|
{
|
|
file: 'document-flow.html',
|
|
title: 'Sample 4: Document Flow Layout (Scroll-Tracker Daemon)',
|
|
type: 'html',
|
|
desc: 'A continuous document reading flow removing 4-sided bounding boxes, featuring a left pane with an IntersectionObserver scroll-tracker daemon.'
|
|
},
|
|
{
|
|
file: 'portfolio.html',
|
|
title: 'Sample 5: River Rooks Portfolio (Single-Page Business Card)',
|
|
type: 'html',
|
|
desc: 'A single-column digital business card landing page conforming to portfolio-spec.md with terminal stdout block, active stack tags, and piped footer.'
|
|
},
|
|
{
|
|
file: 'homelab.html',
|
|
title: 'Sample 6: Homelab Lander & Infrastructure Directory',
|
|
type: 'html',
|
|
desc: 'A public-facing lander for self-hosted homelab infrastructure featuring category filters, service cards, telemetry, and ASCII network topology routing map.'
|
|
},
|
|
{
|
|
file: 'public-services.html',
|
|
title: 'Sample 7: Public Services & Applications Portal',
|
|
type: 'html',
|
|
desc: 'A quick-access launcher portal for self-hosted web tools (Git forge, CyberChef, Stirling PDF, media) with search, filters, and access level badges.'
|
|
},
|
|
{
|
|
file: 'design-specification.md',
|
|
title: 'Design Specification (Primary Technical Brief)',
|
|
type: 'md',
|
|
desc: 'Primary specification document detailing technical web architecture guidelines, typography rules, color palette, and global anti-pattern constraints.'
|
|
},
|
|
{
|
|
file: 'portfolio-spec.md',
|
|
title: 'Portfolio Specification (Landing Page Brief)',
|
|
type: 'md',
|
|
desc: 'Specification sheet for the single-page under-construction portfolio card layout.'
|
|
},
|
|
{
|
|
file: 'sample3-spec-sheet.md',
|
|
title: 'Sample 3 Specification (Companion Layout Brief)',
|
|
type: 'md',
|
|
desc: 'Specification sheet detailing the split-screen ASCII companion interaction suppression policy.'
|
|
}
|
|
];
|
|
|
|
let allItems = [];
|
|
|
|
async function initCatalog() {
|
|
allItems = [...knownFiles];
|
|
|
|
// Attempt dynamic discovery via fetch directory listing if web server supports autoindex
|
|
try {
|
|
const response = await fetch('./');
|
|
if (response.ok) {
|
|
const htmlText = await textResponse(response);
|
|
const parser = new DOMParser();
|
|
const doc = parser.parseFromString(htmlText, 'text/html');
|
|
const links = Array.from(doc.querySelectorAll('a'))
|
|
.map(a => a.getAttribute('href'))
|
|
.filter(href => href && (href.endsWith('.html') || href.endsWith('.md')) && href !== 'index.html');
|
|
|
|
// Merge dynamically discovered files if any are missing from knownFiles
|
|
links.forEach(href => {
|
|
const cleanName = href.replace('./', '');
|
|
if (!allItems.some(item => item.file === cleanName)) {
|
|
const isHtml = cleanName.endsWith('.html');
|
|
allItems.push({
|
|
file: cleanName,
|
|
title: cleanName,
|
|
type: isHtml ? 'html' : 'md',
|
|
desc: `Dynamically discovered ${isHtml ? 'HTML sample' : 'Markdown specification'} file.`
|
|
});
|
|
}
|
|
});
|
|
}
|
|
} catch (err) {
|
|
console.log('Directory autoindex fetch skipped, rendering fallback manifest.');
|
|
}
|
|
|
|
renderCatalog(allItems);
|
|
}
|
|
|
|
async function textResponse(res) {
|
|
return await res.text();
|
|
}
|
|
|
|
function renderCatalog(items) {
|
|
const container = document.getElementById('samples-container');
|
|
const counter = document.getElementById('file-counter');
|
|
|
|
counter.innerText = `${items.length} FILES INDEXED`;
|
|
container.innerHTML = '';
|
|
|
|
if (items.length === 0) {
|
|
container.innerHTML = `<div style="color: var(--text-muted); font-family: monospace;">No matching sample files found.</div>`;
|
|
return;
|
|
}
|
|
|
|
items.forEach(item => {
|
|
const card = document.createElement('div');
|
|
card.className = 'sample-card';
|
|
|
|
const badgeClass = item.type === 'html' ? 'badge-html' : 'badge-md';
|
|
const badgeLabel = item.type === 'html' ? 'HTML SAMPLE' : 'MARKDOWN SPEC';
|
|
|
|
card.innerHTML = `
|
|
<div class="sample-header">
|
|
<a href="${item.file}" class="sample-link">${item.title}</a>
|
|
<span class="badge ${badgeClass}">${badgeLabel}</span>
|
|
</div>
|
|
<div class="sample-filename">file:///samples/${item.file}</div>
|
|
<div class="sample-desc">${item.desc}</div>
|
|
`;
|
|
|
|
container.appendChild(card);
|
|
});
|
|
}
|
|
|
|
function filterSamples() {
|
|
const query = document.getElementById('search-box').value.toLowerCase().trim();
|
|
const filtered = allItems.filter(item =>
|
|
item.title.toLowerCase().includes(query) ||
|
|
item.file.toLowerCase().includes(query) ||
|
|
item.desc.toLowerCase().includes(query)
|
|
);
|
|
renderCatalog(filtered);
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', initCatalog);
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|