Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Computer Science Processes Database</title> | |
| <style> | |
| :root { | |
| --accent: #7c3aed; | |
| --accent-soft: color-mix(in srgb, var(--accent) 12%, white); | |
| --text: #172033; | |
| --muted: #5d6678; | |
| --border: #d9dee8; | |
| --bg: #f6f8fb; | |
| --card: #ffffff; | |
| } | |
| * { box-sizing: border-box; } | |
| body { | |
| margin: 0; | |
| font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; | |
| color: var(--text); | |
| background: var(--bg); | |
| } | |
| a { color: var(--accent); text-decoration: none; } | |
| a:hover { text-decoration: underline; } | |
| .hero { | |
| background: linear-gradient(135deg, var(--accent), #111827); | |
| color: white; | |
| padding: 42px 24px; | |
| } | |
| .hero-inner, main { max-width: 1280px; margin: 0 auto; } | |
| .eyebrow { text-transform: uppercase; letter-spacing: .12em; opacity: .8; font-size: .78rem; } | |
| h1 { margin: 8px 0 8px; font-size: clamp(2rem, 4vw, 3.5rem); line-height: 1; } | |
| .hero p { margin: 0; max-width: 850px; opacity: .9; font-size: 1.08rem; } | |
| main { padding: 24px; } | |
| .grid { display: grid; gap: 16px; } | |
| .stats { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); margin-bottom: 18px; } | |
| .card { | |
| background: var(--card); | |
| border: 1px solid var(--border); | |
| border-radius: 16px; | |
| box-shadow: 0 8px 28px rgba(15, 23, 42, .08); | |
| padding: 18px; | |
| } | |
| .stat-value { font-size: 2rem; font-weight: 800; color: var(--accent); } | |
| .stat-label { color: var(--muted); font-size: .92rem; margin-top: 4px; } | |
| .toolbar { | |
| display: grid; | |
| grid-template-columns: minmax(220px, 1fr) repeat(4, minmax(140px, 210px)); | |
| gap: 12px; | |
| margin: 18px 0; | |
| } | |
| input, select { | |
| width: 100%; | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 11px 12px; | |
| font: inherit; | |
| background: white; | |
| } | |
| .section-title { display: flex; justify-content: space-between; align-items: end; gap: 16px; margin: 24px 0 12px; } | |
| .section-title h2 { margin: 0; } | |
| .section-title p { margin: 4px 0 0; color: var(--muted); } | |
| .collections { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); } | |
| .collection-card { cursor: pointer; border-left: 5px solid var(--accent); } | |
| .collection-card.active { outline: 3px solid color-mix(in srgb, var(--accent) 22%, transparent); } | |
| .collection-card h3 { margin: 0 0 6px; } | |
| .collection-card p { color: var(--muted); margin: 0 0 12px; } | |
| .pill { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 6px; | |
| border: 1px solid var(--border); | |
| border-radius: 999px; | |
| padding: 4px 10px; | |
| background: var(--accent-soft); | |
| font-size: .84rem; | |
| color: #243044; | |
| } | |
| .lens { grid-template-columns: minmax(260px, 1.2fr) minmax(260px, 2fr); } | |
| .insights { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); } | |
| .frontier-links { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px; } | |
| .frontier-links a { | |
| display: inline-block; | |
| border: 1px solid var(--border); | |
| border-radius: 999px; | |
| padding: 8px 12px; | |
| background: white; | |
| } | |
| .table-wrap { | |
| overflow: auto; | |
| border: 1px solid var(--border); | |
| border-radius: 16px; | |
| background: white; | |
| } | |
| table { width: 100%; border-collapse: collapse; min-width: 1320px; } | |
| th, td { padding: 12px 14px; border-bottom: 1px solid var(--border); text-align: left; vertical-align: top; } | |
| th { background: #eef2f7; font-size: .84rem; text-transform: uppercase; letter-spacing: .04em; color: #435066; } | |
| tbody tr:hover { background: #f9fbff; } | |
| .name-cell { font-weight: 750; min-width: 260px; } | |
| .muted { color: var(--muted); } | |
| .error { border-left: 5px solid #b91c1c; } | |
| .hidden { display: none; } | |
| footer { color: var(--muted); padding: 28px 0; font-size: .9rem; } | |
| @media (max-width: 900px) { | |
| .toolbar, .lens { grid-template-columns: 1fr; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header class="hero"> | |
| <div class="hero-inner"> | |
| <div class="eyebrow">Programming Framework</div> | |
| <h1>Computer Science Processes</h1> | |
| <p>Algorithms, systems, networks, security, machine learning, and software workflows.</p> | |
| </div> | |
| </header> | |
| <main> | |
| <div id="error" class="card error hidden"></div> | |
| <section class="grid stats" id="stats"></section> | |
| <section class="card"> | |
| <div class="section-title"> | |
| <div> | |
| <h2 id="lensTitle">Discipline Lens</h2> | |
| <p id="lensDescription"></p> | |
| </div> | |
| <span class="pill" id="lastUpdated">Loading</span> | |
| </div> | |
| <div class="grid insights" id="insights"></div> | |
| <div class="frontier-links" id="frontierLinks"></div> | |
| </section> | |
| <section> | |
| <div class="section-title"> | |
| <div> | |
| <h2>Collections</h2> | |
| <p>Discipline-specific groupings generated from the current process catalog.</p> | |
| </div> | |
| <button class="pill" id="clearCollection" type="button">Show all</button> | |
| </div> | |
| <div class="grid collections" id="collections"></div> | |
| </section> | |
| <section class="card"> | |
| <div class="toolbar"> | |
| <input id="search" type="search" placeholder="Search by process, subcategory, collection, or keyword"> | |
| <select id="subcategoryFilter"></select> | |
| <select id="collectionFilter"></select> | |
| <select id="complexityFilter"></select> | |
| <select id="graphTypeFilter"></select> | |
| </div> | |
| <div class="section-title"> | |
| <div> | |
| <h2>Process Database Table</h2> | |
| <p><span id="visibleCount">0</span> matching processes.</p> | |
| </div> | |
| </div> | |
| <div class="table-wrap"> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Process</th> | |
| <th id="contextHeader">Context</th> | |
| <th>Category</th> | |
| <th>Complexity</th> | |
| <th>Nodes</th> | |
| <th>Edges</th> | |
| <th>Conditionals</th> | |
| <th>AND Gates</th> | |
| <th>OR Gates</th> | |
| <th>NOT Gates</th> | |
| <th>Loops</th> | |
| <th>Graph Type</th> | |
| <th>Collections</th> | |
| <th>Frontier</th> | |
| </tr> | |
| </thead> | |
| <tbody id="processRows"></tbody> | |
| </table> | |
| </div> | |
| </section> | |
| <footer> | |
| Static host: GCS. Data files: same-folder metadata.json, discipline-profile.json, and collections.json. | |
| </footer> | |
| </main> | |
| <script> | |
| const EMBEDDED_PROFILE = {"displayName": "Computer Science", "slug": "computer_science", "databaseDir": "computer-science-processes-database", "tableFile": "computer-science-database-table.html", "accent": "#7c3aed", "subtitle": "Algorithms, systems, networks, security, machine learning, and software workflows.", "lensTitle": "Computer Science Lens", "lensDescription": "Browse by algorithmic complexity, execution environment, invariants, and failure mode.", "facetLabels": {"subcategory": "Area", "collection": "Lens", "complexity": "Complexity"}, "insightCards": [{"label": "Best for", "value": "algorithms, systems, security, ML pipelines"}, {"label": "Research links", "value": "arXiv, Semantic Scholar, and Crossref"}, {"label": "Curation focus", "value": "separate theory, implementation, and operational concerns"}], "frontierSearches": [{"label": "arXiv", "urlTemplate": "https://arxiv.org/search/cs?query={query}&searchtype=all"}, {"label": "Crossref", "urlTemplate": "https://search.crossref.org/?q={query}+computer+science"}, {"label": "Semantic Scholar", "urlTemplate": "https://www.semanticscholar.org/search?q={query}"}], "contextColumnLabel": "System / Data Type", "defaultContext": "Computing system", "supportedGraphTypes": ["flowchart", "dependency_graph", "taxonomy", "state_transition", "influence_network"], "metricColumns": [{"id": "nodes", "label": "Nodes"}, {"id": "edges", "label": "Edges"}, {"id": "conditionals", "label": "Conditionals"}, {"id": "andGates", "label": "AND Gates"}, {"id": "orGates", "label": "OR Gates"}, {"id": "notGates", "label": "NOT Gates"}, {"id": "loops", "label": "Loops"}], "graphTypeLabels": {"flowchart": "Flowchart", "dependency_graph": "Dependency Graph", "taxonomy": "Taxonomy", "lineage_tree": "Lineage Tree", "influence_network": "Influence Network", "state_transition": "State Transition", "timeline": "Timeline"}, "featuredMaps": [{"label": "Whole of Computer Science", "url": "whole-of-computer-science.html", "description": "Computer Science is strongest as a layered abstraction stack with dependency and process links."}, {"label": "Computing Stack Map", "url": "computing-stack-map.html", "description": "Conceptual stack/subway map of computing layers and cross-cutting concerns."}], "generatedFrom": "scripts/processes/discipline_databases/enhanced_database_table.py", "collectionCount": 9}; | |
| let metadata = null; | |
| let profile = EMBEDDED_PROFILE; | |
| let collections = []; | |
| let selectedCollection = ""; | |
| function sameFolderUrl(file) { | |
| return new URL(file, window.location.href).href; | |
| } | |
| async function fetchJson(file, fallback) { | |
| try { | |
| const url = sameFolderUrl(file) + "?ts=" + Date.now(); | |
| const response = await fetch(url, { cache: "no-store" }); | |
| if (!response.ok) throw new Error("HTTP " + response.status); | |
| return await response.json(); | |
| } catch (error) { | |
| if (fallback !== undefined) return fallback; | |
| throw error; | |
| } | |
| } | |
| function getProcessCollections(process) { | |
| if (Array.isArray(process.collections)) return process.collections; | |
| if (Array.isArray(process.namedCollections)) return process.namedCollections; | |
| return []; | |
| } | |
| function applyCollectionMemberships() { | |
| const byProcess = new Map(); | |
| collections.forEach(collection => { | |
| (collection.processIds || []).forEach(processId => { | |
| if (!byProcess.has(processId)) byProcess.set(processId, new Set()); | |
| byProcess.get(processId).add(collection.id); | |
| }); | |
| }); | |
| (metadata.processes || []).forEach(process => { | |
| const existing = new Set(process.namedCollections || []); | |
| const generated = byProcess.get(process.id) || new Set(); | |
| process.collections = Array.from(new Set([...existing, ...generated])).sort(); | |
| }); | |
| } | |
| function normalizeText(value) { | |
| return String(value || "").toLowerCase(); | |
| } | |
| function processUrl(process) { | |
| const subcategory = process.subcategory || "processes"; | |
| return "processes/" + encodeURIComponent(subcategory) + "/" + encodeURIComponent(process.id) + ".html"; | |
| } | |
| function graphMetrics(process) { | |
| return process.graphMetrics || { | |
| nodes: process.nodes || 0, | |
| edges: process.edges || 0, | |
| conditionals: process.conditionals || 0, | |
| andGates: process.andGates || 0, | |
| orGates: process.orGates || 0, | |
| notGates: process.notGates || 0, | |
| loops: process.loops || 0 | |
| }; | |
| } | |
| function metricValue(process, key) { | |
| return Number(graphMetrics(process)[key] || 0).toLocaleString(); | |
| } | |
| function graphTypeLabel(graphType) { | |
| const labels = profile.graphTypeLabels || {}; | |
| const value = graphType || "flowchart"; | |
| return labels[value] || value.replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase()); | |
| } | |
| function frontierUrl(template, processName) { | |
| return template.replace("{query}", encodeURIComponent(processName)); | |
| } | |
| function renderStats() { | |
| const stats = metadata.statistics || {}; | |
| const processes = metadata.processes || []; | |
| const nodes = stats.totalNodes || processes.reduce((sum, p) => sum + Number(graphMetrics(p).nodes || 0), 0); | |
| const edges = stats.totalEdges || processes.reduce((sum, p) => sum + Number(graphMetrics(p).edges || 0), 0); | |
| const gates = stats.totalGates || processes.reduce((sum, p) => { | |
| const metrics = graphMetrics(p); | |
| return sum + Number(metrics.andGates || 0) + Number(metrics.orGates || 0) + Number(metrics.notGates || 0); | |
| }, 0); | |
| const subcats = Object.keys(metadata.subcategoryCounts || {}).length || metadata.subcategories || 0; | |
| document.getElementById("stats").innerHTML = [ | |
| ["Processes", metadata.totalProcesses || processes.length], | |
| ["Subcategories", subcats], | |
| ["Nodes", nodes], | |
| ["Edges", edges], | |
| ["Logic gates", gates], | |
| ["Collections", collections.length] | |
| ].map(([label, value]) => `<div class="card"><div class="stat-value">${Number(value || 0).toLocaleString()}</div><div class="stat-label">${label}</div></div>`).join(""); | |
| document.getElementById("lastUpdated").textContent = "Updated " + (metadata.lastUpdated || "unknown"); | |
| } | |
| function renderLens() { | |
| document.getElementById("lensTitle").textContent = profile.lensTitle || (profile.displayName + " Lens"); | |
| document.getElementById("lensDescription").textContent = profile.lensDescription || metadata.description || ""; | |
| document.getElementById("contextHeader").textContent = profile.contextColumnLabel || "Context"; | |
| document.getElementById("insights").innerHTML = (profile.insightCards || []).map(card => ` | |
| <div class="card"> | |
| <div class="stat-label">${card.label}</div> | |
| <div style="font-weight:700;margin-top:6px">${card.value}</div> | |
| </div> | |
| `).join(""); | |
| const mapLinks = (profile.featuredMaps || []).map(link => { | |
| return `<a href="${link.url}" target="_blank" rel="noopener noreferrer">${link.label}</a>`; | |
| }); | |
| const searchLinks = (profile.frontierSearches || []).map(link => { | |
| const sample = metadata.processes && metadata.processes[0] ? metadata.processes[0].name : profile.displayName; | |
| return `<a href="${frontierUrl(link.urlTemplate, sample)}" target="_blank" rel="noopener noreferrer">${link.label}</a>`; | |
| }); | |
| document.getElementById("frontierLinks").innerHTML = [...mapLinks, ...searchLinks].join(""); | |
| } | |
| function renderCollections() { | |
| document.getElementById("collections").innerHTML = collections.map(collection => ` | |
| <div class="card collection-card ${selectedCollection === collection.id ? "active" : ""}" data-id="${collection.id}"> | |
| <h3>${collection.label}</h3> | |
| <p>${collection.description || ""}</p> | |
| <span class="pill">${(collection.processIds || []).length} processes</span> | |
| </div> | |
| `).join(""); | |
| document.querySelectorAll(".collection-card").forEach(card => { | |
| card.addEventListener("click", () => { | |
| selectedCollection = card.dataset.id; | |
| document.getElementById("collectionFilter").value = selectedCollection; | |
| renderCollections(); | |
| renderRows(); | |
| }); | |
| }); | |
| } | |
| function populateFilters() { | |
| const processes = metadata.processes || []; | |
| const subcategories = [...new Set(processes.map(p => p.subcategory_name || p.subcategory).filter(Boolean))].sort(); | |
| const complexities = [...new Set(processes.map(p => p.complexity).filter(Boolean))].sort(); | |
| const graphTypes = [...new Set(processes.map(p => p.graphType || "flowchart").filter(Boolean))].sort(); | |
| document.getElementById("subcategoryFilter").innerHTML = `<option value="">All ${profile.facetLabels?.subcategory || "subcategories"}</option>` + subcategories.map(v => `<option value="${v}">${v}</option>`).join(""); | |
| document.getElementById("collectionFilter").innerHTML = `<option value="">All ${profile.facetLabels?.collection || "collections"}</option>` + collections.map(c => `<option value="${c.id}">${c.label}</option>`).join(""); | |
| document.getElementById("complexityFilter").innerHTML = `<option value="">All complexity levels</option>` + complexities.map(v => `<option value="${v}">${v}</option>`).join(""); | |
| document.getElementById("graphTypeFilter").innerHTML = `<option value="">All graph types</option>` + graphTypes.map(v => `<option value="${v}">${graphTypeLabel(v)}</option>`).join(""); | |
| } | |
| function filteredProcesses() { | |
| const q = normalizeText(document.getElementById("search").value); | |
| const subcategory = document.getElementById("subcategoryFilter").value; | |
| const collection = document.getElementById("collectionFilter").value; | |
| const complexity = document.getElementById("complexityFilter").value; | |
| const graphType = document.getElementById("graphTypeFilter").value; | |
| return (metadata.processes || []).filter(process => { | |
| const processCollections = getProcessCollections(process); | |
| const haystack = normalizeText([ | |
| process.name, | |
| process.domainContext, | |
| process.category, | |
| process.subcategory, | |
| process.subcategory_name, | |
| process.complexity, | |
| process.graphType, | |
| processCollections.join(" "), | |
| (process.keywords || []).join(" ") | |
| ].join(" ")); | |
| return (!q || haystack.includes(q)) | |
| && (!subcategory || (process.subcategory_name || process.subcategory) === subcategory) | |
| && (!collection || processCollections.includes(collection)) | |
| && (!complexity || process.complexity === complexity) | |
| && (!graphType || (process.graphType || "flowchart") === graphType); | |
| }); | |
| } | |
| function renderRows() { | |
| const rows = filteredProcesses(); | |
| document.getElementById("visibleCount").textContent = rows.length.toLocaleString(); | |
| document.getElementById("processRows").innerHTML = rows.map(process => { | |
| const processCollections = getProcessCollections(process); | |
| const collectionLabels = processCollections.map(id => (collections.find(c => c.id === id) || {label: id}).label); | |
| const primarySearch = (profile.frontierSearches || [])[0]; | |
| return ` | |
| <tr> | |
| <td class="name-cell"><a href="${processUrl(process)}">${process.name}</a></td> | |
| <td>${process.domainContext || ""}</td> | |
| <td>${process.category || process.subcategory_name || process.subcategory || ""}</td> | |
| <td>${process.complexity || ""}</td> | |
| <td>${metricValue(process, "nodes")}</td> | |
| <td>${metricValue(process, "edges")}</td> | |
| <td>${metricValue(process, "conditionals")}</td> | |
| <td>${metricValue(process, "andGates")}</td> | |
| <td>${metricValue(process, "orGates")}</td> | |
| <td>${metricValue(process, "notGates")}</td> | |
| <td>${metricValue(process, "loops")}</td> | |
| <td><span class="pill">${graphTypeLabel(process.graphType)}</span></td> | |
| <td>${collectionLabels.map(label => `<span class="pill">${label}</span>`).join(" ") || '<span class="muted">Unassigned</span>'}</td> | |
| <td>${primarySearch ? `<a href="${frontierUrl(primarySearch.urlTemplate, process.name)}" target="_blank" rel="noopener noreferrer">${primarySearch.label}</a>` : ""}</td> | |
| </tr> | |
| `; | |
| }).join(""); | |
| } | |
| function wireEvents() { | |
| ["search", "subcategoryFilter", "collectionFilter", "complexityFilter", "graphTypeFilter"].forEach(id => { | |
| document.getElementById(id).addEventListener("input", () => { | |
| selectedCollection = document.getElementById("collectionFilter").value; | |
| renderCollections(); | |
| renderRows(); | |
| }); | |
| }); | |
| document.getElementById("clearCollection").addEventListener("click", () => { | |
| selectedCollection = ""; | |
| document.getElementById("collectionFilter").value = ""; | |
| renderCollections(); | |
| renderRows(); | |
| }); | |
| } | |
| async function init() { | |
| try { | |
| profile = await fetchJson("discipline-profile.json", profile); | |
| metadata = await fetchJson("metadata.json"); | |
| metadata = await fetchJson("process-index.json", metadata); | |
| collections = await fetchJson("collections.json", []); | |
| applyCollectionMemberships(); | |
| renderStats(); | |
| renderLens(); | |
| renderCollections(); | |
| populateFilters(); | |
| wireEvents(); | |
| renderRows(); | |
| } catch (error) { | |
| const el = document.getElementById("error"); | |
| el.classList.remove("hidden"); | |
| el.innerHTML = `<strong>Could not load database metadata.</strong><br><span class="muted">${error.message}</span>`; | |
| } | |
| } | |
| init(); | |
| </script> | |
| </body> | |
| </html> | |