refactor(explorer): single TITLE constant; show the map title only in print/PDF/PNG

This commit is contained in:
Meysam Parvizi
2026-10-11 17:39:04 +02:00
parent edaf33ad61
commit 4d4b0a3059
6 changed files with 25 additions and 11 deletions
+5 -4
View File
@@ -1,7 +1,7 @@
import { parseRoadmap, renderInline, TYPES } from './parser.js';
import { linkDiagram } from './diagram.js';
import { createMapView } from './mapview.js';
import { CREDITS, IMPORTANCE, areaDots, areaLabel, h, plural } from './dom.js';
import { CREDITS, IMPORTANCE, TITLE, areaDots, areaLabel, h, plural } from './dom.js';
const SOURCES = [
'README.md',
@@ -97,7 +97,7 @@ function navigate(node, { push = true, resource = null, reveal = false } = {}) {
syncOutline();
if (resource) revealResource(resource);
const root = state.data.root;
document.title = node === root ? 'Embedded Systems Engineering Roadmap · Explorer' : `${node.title} · Embedded Systems Engineering Roadmap`;
document.title = node === root ? `${TITLE} · Explorer` : `${node.title} · ${TITLE}`;
if (push) {
const hash = node === root ? '#/' : `#/${node.slug}`;
if (location.hash !== hash) history.pushState(null, '', hash);
@@ -344,7 +344,7 @@ function renderPanel(node) {
crumbs(node),
h('h2', { class: 'panel-title' },
node.icon ? h('span', { class: 'title-icon', 'aria-hidden': 'true' }, node.icon) : node === root ? null : swatch(node),
node === root ? 'Embedded Systems Engineering Roadmap' : node.title),
node === root ? 'About this roadmap' : node.title),
node === root ? null : h('div', { class: 'meta' },
node.areas && node.areas.length ? h('span', { class: 'pill area-pill' }, areaDots(node.areas), areaLabel(node.areas)) : null,
node.importance ? h('span', { class: `pill imp-pill ${node.importance}` }, IMPORTANCE[node.importance]) : null,
@@ -589,7 +589,7 @@ async function loadMarkdown() {
const response = await fetch(source, { cache: 'no-cache' });
if (!response.ok) continue;
const text = await response.text();
if (text.includes('# Embedded Systems Engineering Roadmap')) return text;
if (text.includes(`# ${TITLE}`)) return text;
} catch {
// Try the next source.
}
@@ -674,6 +674,7 @@ function wireGlobal() {
}
async function init() {
document.getElementById('brand-title').textContent = TITLE;
applyTheme(store('roadmap-theme') || (window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'));
let markdown;
const diagramRequest = loadMap();
+3
View File
@@ -2,6 +2,9 @@
export const SVG_NS = 'http://www.w3.org/2000/svg';
// The roadmap's name; also the README's top heading.
export const TITLE = 'Embedded Systems Engineering Roadmap';
export const plural = (n, word) => `${n} ${word}${n === 1 ? '' : 's'}`;
export const CREDITS = {
+1 -1
View File
@@ -17,7 +17,7 @@
<a class="brand" href="#/" aria-label="Roadmap overview">
<img class="brand-mark" src="assets/logo.svg" alt="" width="36" height="36">
<span class="brand-text">
<strong>Embedded Systems Engineering Roadmap</strong>
<strong id="brand-title"></strong>
<span>Interactive explorer</span>
</span>
</a>
+6
View File
@@ -228,6 +228,12 @@
letter-spacing: -0.01em;
}
@media not print {
.page-title {
display: none;
}
}
.legend-text {
fill: var(--text);
font-size: 13px;
+8 -5
View File
@@ -1,6 +1,6 @@
// Interactive roadmap diagram: Software / Hardware / Soft skills areas with their cross-sections,
// importance colors, filters and connections between topics. Laid out by layout.js to fit A4.
import { AREAS, CREDITS, GITHUB_MARK, IMPORTANCE, areaDots, areaLabel, h, plural, svg } from './dom.js';
import { AREAS, CREDITS, GITHUB_MARK, IMPORTANCE, TITLE, areaDots, areaLabel, h, plural, svg } from './dom.js';
import { SPACE, buildLayout } from './layout.js';
const MIN_ZOOM = 0.6;
@@ -151,8 +151,11 @@ export function createMapView({ root, plan, data, build, onSelect }) {
rect({ x: 0, y: 0, w: layout.width, h: layout.height }, { class: 'page-bg' }, layers.page);
// Title and legend, so the diagram also stands on its own when printed.
// On screen the header already names the roadmap, so the title only appears in print, PDF and PNG.
const legendY = layout.titleY + 22;
const screenTop = legendY - 24;
const title = svg('text', { x: 24, y: layout.titleY, class: 'page-title' }, layers.labels);
title.textContent = 'Embedded Systems Engineering Roadmap';
title.textContent = TITLE;
const legend = svg('g', { class: 'page-legend' }, layers.labels);
const legendItems = [
...Object.entries(IMPORTANCE).map(([level, label]) => ({ kind: 'imp', cls: level, label })),
@@ -162,7 +165,7 @@ export function createMapView({ root, plan, data, build, onSelect }) {
{ kind: 'area', cls: 'ss', label: 'Soft skills' },
];
for (const item of legendItems) {
const y = layout.titleY + 22;
const y = legendY;
const cls = item.kind === 'imp' ? `legend-imp ${item.cls}` : item.kind === 'area' ? `legend-area ${item.cls}` : 'legend-hatch';
const key = rect({ x: 0, y, w: 22, h: 13 }, { rx: item.kind === 'imp' ? 3 : 4, class: cls }, legend);
const t = svg('text', { x: 0, y: y + 11, class: 'legend-text' }, legend);
@@ -456,7 +459,7 @@ export function createMapView({ root, plan, data, build, onSelect }) {
const slackX = Math.max(0, page.w - v.w);
const slackY = Math.max(0, page.h - v.h);
v.x = slackX ? Math.min(Math.max(v.x, -40), slackX + 40) : (page.w - v.w) / 2;
v.y = slackY ? Math.min(Math.max(v.y, -40), slackY + 40) : (page.h - v.h) / 2;
v.y = slackY ? Math.min(Math.max(v.y, screenTop - 40), slackY + 40) : (page.h - v.h) / 2;
state.view = v;
chart.setAttribute('viewBox', `${v.x.toFixed(1)} ${v.y.toFixed(1)} ${v.w.toFixed(1)} ${v.h.toFixed(1)}`);
if (size) zoomLevel.textContent = `${Math.round((size.w / v.w) * 100)}%`;
@@ -466,7 +469,7 @@ export function createMapView({ root, plan, data, build, onSelect }) {
const size = canvasSize();
const width = size ? Math.max(page.w + 40, size.w / MAX_SCALE) : page.w;
fitMode = 'width';
setView({ x: (page.w - width) / 2, y: -20, w: width });
setView({ x: (page.w - width) / 2, y: screenTop - 20, w: width });
fitButton.title = 'Show the whole page';
}
+2 -1
View File
@@ -1,5 +1,6 @@
// Turns the roadmap README into a topic tree with descriptions, resources and cross-links.
// Pure module: no DOM access, so it runs in the browser and under `node --test`.
import { TITLE } from './dom.js';
export const TYPES = {
book: { icon: '📘', label: 'Book' },
@@ -87,7 +88,7 @@ function makeNode(fields) {
export function parseRoadmap(markdown) {
const lines = markdown.replace(/\r\n?/g, '\n').split('\n');
const root = makeNode({ id: 'root', slug: '', title: 'Embedded Systems Engineering Roadmap' });
const root = makeNode({ id: 'root', slug: '', title: TITLE });
const groups = [];
const stack = [];
let target = root;