From 2de24aea7167cfd40e712f4e4b02e7d258424b8f Mon Sep 17 00:00:00 2001 From: Meysam Parvizi Date: Sun, 11 Oct 2026 20:41:33 +0200 Subject: [PATCH] feat(explorer): show embedded system definitions on the landing panel and as a topic --- README.md | 4 ++++ explorer/app.js | 32 ++++++++++++++++++++++++++++---- explorer/map.json | 2 +- explorer/parser.js | 37 ++++++++++++++++++++++++++++++++++++- explorer/parser.test.mjs | 13 ++++++++++++- explorer/styles.css | 27 +++++++++++++++++++++++++++ 6 files changed, 108 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9537fb2..ed1d358 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ Embedded engineering demands a solid understanding of hardware functionality as ## ❓ What is an Embedded System? +### 📖 Embedded System Definitions + +Standards, books and glossaries word it differently, but the common idea is a computer built into a larger product to perform a dedicated function, usually with tight limits on cost, size, power and reliability. + ▶️ [ISO/IEC/IEEE 24765 Standard:](https://www.iso.org/obp/ui/en/#iso:std:iso-iec-ieee:24765:ed-2:v1:en) > **computer system** that is **part of a larger system** and **performs some of the requirements of that system**. For example, a computer system diff --git a/explorer/app.js b/explorer/app.js index 6297fc5..e3ff1af 100644 --- a/explorer/app.js +++ b/explorer/app.js @@ -54,9 +54,10 @@ function store(key, value) { // ---------- colors ---------- function assignHues(data) { - const [first, ...rest] = data.groups; - const start = rest.length ? first.topics : []; - const learning = rest.length ? rest.flatMap((g) => g.topics) : data.root.children; + // Every section before the last one holds getting-started topics; the last holds the learning resources. + const learningGroup = data.groups.length > 1 ? data.groups[data.groups.length - 1] : null; + const start = learningGroup ? data.groups.slice(0, -1).flatMap((g) => g.topics) : []; + const learning = learningGroup ? learningGroup.topics : data.root.children; start.forEach((t, i) => { t.hue = 168 + (start.length > 1 ? (i * 40) / (start.length - 1) : 0); }); learning.forEach((t, i) => { t.hue = (214 + (i * 300) / Math.max(1, learning.length)) % 360; }); data.root.children.forEach((top, index) => { @@ -134,6 +135,15 @@ function description(blocks) { const p = h('p'); p.innerHTML = renderInline(block.text); wrap.append(p); + } else if (block.type === 'quote') { + const quote = h('blockquote'); + for (const text of block.paragraphs) { + const p = h('p'); + p.innerHTML = renderInline(text); + quote.append(p); + } + wrap.append(h('figure', { class: 'quote' }, quote, h('figcaption', {}, + h('a', { href: block.url, target: '_blank', rel: 'noopener noreferrer' }, block.source)))); } else { const ul = h('ul'); for (const item of block.items) { @@ -300,16 +310,30 @@ function areaSummary() { tile(['HARDWARE'], count((b) => b.areas.length === 1 && b.areas[0] === 'HARDWARE'), 'Hardware only')); } +// The summary and the first quotation stay visible; the other quotations fold away to keep the landing panel short. +function foldedDefinitions(blocks) { + const firstQuote = blocks.findIndex((b) => b.type === 'quote'); + const rest = blocks.slice(firstQuote + 1); + return [ + description(blocks.slice(0, firstQuote + 1)), + rest.length ? h('details', { class: 'more' }, h('summary', {}, `${rest.length} more definitions`), description(rest)) : null, + ]; +} + function overview(root) { const { stats, groups } = state.data; + // A section whose topic quotes sources (README definitions) is shown in full instead of as a chip. + const quoted = (g) => g.topics.find((t) => t.description.some((b) => b.type === 'quote')); + const definitions = groups.find((g) => quoted(g)); return [ section(null, description(root.description)), + definitions ? section(definitions.title, ...foldedDefinitions(quoted(definitions).description)) : null, section(null, h('div', { class: 'stats' }, h('div', { class: 'stat' }, h('b', {}, stats.topics), h('span', {}, 'topics')), h('div', { class: 'stat' }, h('b', {}, stats.resources), h('span', {}, 'resources')), h('div', { class: 'stat' }, h('b', {}, state.plan ? state.plan.connections.filter((c) => c.reason).length : stats.links), h('span', {}, 'connections')))), state.plan ? section('Diagram topics by area', areaSummary()) : null, - ...groups.map((g) => section(g.title, topicChips(g.topics))), + ...groups.filter((g) => !quoted(g)).map((g) => section(g.title, topicChips(g.topics))), section('How to explore', h('ul', { class: 'howto' }, h('li', {}, 'Map follows the roadmap diagram: topics sit in Software, Hardware or both, and are colored by learning priority, a general guide that varies with your specialty and role.'), h('li', {}, 'Click Required, Recommended or Optional in the legend above the map to highlight topics of that priority.'), diff --git a/explorer/map.json b/explorer/map.json index 186df05..9356b26 100644 --- a/explorer/map.json +++ b/explorer/map.json @@ -488,7 +488,7 @@ "Microcontrollers": ["rtos", "mcu"], "Interfaces & Protocols": ["basic", "wireless", "highSpeed", "industrial", "cellular", "network", "automotive", "display"] }, - "offMap": ["Don't Know Where to Start!"], + "offMap": ["What is an Embedded System?", "Don't Know Where to Start!"], "readme": { "ADC / DAC": ["ADC", "DAC"], "Buildroot / Yocto": ["Buildroot", "Yocto"], diff --git a/explorer/parser.js b/explorer/parser.js index 735fa47..8eee73e 100644 --- a/explorer/parser.js +++ b/explorer/parser.js @@ -19,6 +19,8 @@ const URL_PART = String.raw`https?:\/\/[^\s()]+(?:\([^\s()]*\)[^\s()]*)*`; const HEADING = /^(#{1,6})\s+(.+)$/; const RESOURCE = new RegExp(String.raw`^\s*[-*]\s+\[(.+)\]\((${URL_PART})\)(.*)$`, 'u'); const HEADING_LINK = new RegExp(String.raw`^\[(.+)\]\((${URL_PART})\)$`, 'u'); +// A "▶️ [Source](url)" line introduces the blockquote that follows it, a sourced quotation such as a definition. +const QUOTE_SOURCE = new RegExp(String.raw`^\s*\u25B6\uFE0F?\s*\[(.+)\]\((${URL_PART})\)\s*$`, 'u'); const BULLET = /^\s*[-*]\s+(.+)$/; const SKIP = /^(\s*>|