stack-site-builder 1.23.1 → 1.23.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -11,6 +11,38 @@ content schema, while a consuming site supplies only content, taxonomy data and
11
11
  config. Sites track the theme with `pnpm up stack-site-builder`, so each release
12
12
  here is a plain version bump they pull in.
13
13
 
14
+ ## [1.23.2] - 2026-08-06
15
+
16
+ ### Fixed
17
+
18
+ - **Wide tables scroll instead of cramming their cells** — a table wider than its
19
+ column had no way out: it needs `display: table` for real column sizing, which
20
+ makes `overflow-x` on the table itself a no-op, so every cell was squeezed into
21
+ a stack of wrapped lines (a 3-column table with 1561px of natural content
22
+ rendered 920px wide and 3.6x its natural height, breaking code samples
23
+ mid-token). Every rendered table now sits in a `.aas-table-scroll` box that
24
+ takes the scrolling while the table keeps its natural widths — on both render
25
+ paths: `rehypeTableScroll` for content collections, `withTableScroll`
26
+ (`lib/md-tables.ts`) for the MarkdownIt paths behind READMEs and sample
27
+ descriptions. In a reading column the wrapper shrinks to the table and centers,
28
+ so a narrow table keeps its own width instead of being stretched; on slides a
29
+ wide table slides sideways. A table that arrives without a wrapper (raw HTML in
30
+ MDX, say) still behaves exactly as before.
31
+ - **Slide diagrams render full-size** — the generic caps (`pre` 62vh, svg 52vh)
32
+ don't know how much room the slide fill rules actually handed the diagram — on
33
+ a full-height slide that box is ~78vh — so a tall flowchart was scaled to fit
34
+ 52vh and the unused width was letterboxed as empty margins either side.
35
+ Diagrams now size against the box they were given (measured on a 1958x2000
36
+ flowchart: 0.34 -> 0.75 scale). `compact`, `.aas-split.scroll` and `scroll-x`
37
+ manage their own overflow and are excluded, so they render exactly as before.
38
+ - **Mermaid subgraph titles stop overlapping** — mermaid draws labels as real
39
+ HTML in a `<foreignObject>` and sizes nodes and cluster frames from the
40
+ measured box; mermaid 11 wraps label text in a `<p>`, which inherited the
41
+ page's paragraph typography (26px/41px on a slide) while mermaid reserves only
42
+ ~25px for a cluster title, so every subgraph title overlapped the first node
43
+ inside it by ~16px. Label typography is pinned back to mermaid's own so its
44
+ measurements hold.
45
+
14
46
  ## [1.23.1] - 2026-07-29
15
47
 
16
48
  ### Fixed
@@ -433,6 +465,7 @@ catalog sites from a thin content-only repository.
433
465
  - **Standalone development setup** — a devcontainer and a minimal `playground/`
434
466
  consuming site for developing and previewing the theme on its own.
435
467
 
468
+ [1.23.2]: https://github.com/CodeComposeStudio/stack-site-builder/compare/v1.23.1...v1.23.2
436
469
  [1.23.1]: https://github.com/CodeComposeStudio/stack-site-builder/compare/v1.23.0...v1.23.1
437
470
  [1.23.0]: https://github.com/CodeComposeStudio/stack-site-builder/compare/v1.22.0...v1.23.0
438
471
  [1.19.3]: https://github.com/CodeCompose7/stack-site-builder/compare/v1.19.2...v1.19.3
package/markdown.mjs CHANGED
@@ -94,6 +94,32 @@ function remarkMermaid() {
94
94
  return (/** @type {any} */ tree) => walk(tree);
95
95
  }
96
96
 
97
+ // Wrap every table in a horizontal scroll box. A table can't scroll itself: the
98
+ // moment it needs `display: table` for real column sizing (and a rounded frame),
99
+ // `overflow-x` on the table is dead, so a table wider than its column has no way
100
+ // out but to squeeze every cell into a stack of wrapped lines — a 120px-tall
101
+ // table becomes 430px of cramped text. The wrapper takes the scrolling, the
102
+ // table keeps its natural widths (see `.aas-table-scroll` in global.css).
103
+ function rehypeTableScroll() {
104
+ /** @param {any} node */
105
+ const walk = (node) => {
106
+ if (!node.children) return;
107
+ node.children.forEach((/** @type {any} */ child, /** @type {number} */ i) => {
108
+ if (child.type === 'element' && child.tagName === 'table') {
109
+ node.children[i] = {
110
+ type: 'element',
111
+ tagName: 'div',
112
+ properties: { className: ['aas-table-scroll'] },
113
+ children: [child],
114
+ };
115
+ } else {
116
+ walk(child);
117
+ }
118
+ });
119
+ };
120
+ return (/** @type {any} */ tree) => walk(tree);
121
+ }
122
+
97
123
  // Slide directives (needs remarkDirective, which runs first). Two are handled:
98
124
  //
99
125
  // :::cols columns, separated by `---`:
@@ -405,6 +431,7 @@ export function aasMarkdown({ glossary, locales = ['en', 'ko'], defaultLocale =
405
431
  rehypePlugins: [
406
432
  rehypeSlug,
407
433
  rehypeHeadingAnchors,
434
+ rehypeTableScroll,
408
435
  [rehypeExternalLinks, { target: '_blank', rel: ['noopener', 'noreferrer'] }],
409
436
  ],
410
437
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stack-site-builder",
3
3
  "type": "module",
4
- "version": "1.23.1",
4
+ "version": "1.23.2",
5
5
  "license": "MIT",
6
6
  "description": "The engine behind the awesome-*-stack catalog sites: an Astro theme with the catalog/concepts/articles/slides/samples routes, components, styles and markdown pipeline. Sites provide content, taxonomy data and config.",
7
7
  "repository": {
@@ -0,0 +1,21 @@
1
+ import type MarkdownIt from 'markdown-it';
2
+
3
+ /**
4
+ * Wrap every rendered table in a `.aas-table-scroll` box, matching what
5
+ * `rehypeTableScroll` (markdown.mjs) does for content collections. A table
6
+ * cannot scroll itself — it needs `display: table` for real column sizing,
7
+ * which makes `overflow-x` on the table a no-op — so without the wrapper a
8
+ * README table wider than its column has no escape but to wrap every cell.
9
+ *
10
+ * Only needed on MarkdownIt instances that RENDER (READMEs, sample
11
+ * descriptions); the ones that merely `parse()` for metadata can skip it.
12
+ */
13
+ export function withTableScroll(md: MarkdownIt): MarkdownIt {
14
+ const base = md.renderer.rules.table_open ?? ((t, i, o, _e, s) => s.renderToken(t, i, o));
15
+ md.renderer.rules.table_open = (tokens, idx, opts, env, self) =>
16
+ `<div class="aas-table-scroll">${base(tokens, idx, opts, env, self)}`;
17
+ const baseClose = md.renderer.rules.table_close ?? ((t, i, o, _e, s) => s.renderToken(t, i, o));
18
+ md.renderer.rules.table_close = (tokens, idx, opts, env, self) =>
19
+ `${baseClose(tokens, idx, opts, env, self)}</div>`;
20
+ return md;
21
+ }
@@ -3,6 +3,7 @@ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
3
3
  import { join, relative } from 'node:path';
4
4
  import { createHighlighter, type Highlighter } from 'shiki';
5
5
  import MarkdownIt from 'markdown-it';
6
+ import { withTableScroll } from './md-tables';
6
7
  import { site } from '@aas-data/site';
7
8
 
8
9
  const SAMPLES_DIR = 'samples';
@@ -240,17 +241,19 @@ export async function renderProjects(folders: string[], lang: string): Promise<R
240
241
  const hl = await getHighlighter();
241
242
  const escapeHtml = (s: string) =>
242
243
  s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
243
- const md = new MarkdownIt({
244
- html: false,
245
- linkify: true,
246
- highlight: (code, info) => {
247
- const lang = (info || '').trim() || 'text';
248
- // Hand mermaid blocks to the client-side MermaidLoader instead of Shiki,
249
- // so README diagrams render as graphics rather than highlighted text.
250
- if (lang === 'mermaid') return `<pre class="mermaid">${escapeHtml(code)}</pre>`;
251
- return highlight(hl, code, lang);
252
- },
253
- });
244
+ const md = withTableScroll(
245
+ new MarkdownIt({
246
+ html: false,
247
+ linkify: true,
248
+ highlight: (code, info) => {
249
+ const lang = (info || '').trim() || 'text';
250
+ // Hand mermaid blocks to the client-side MermaidLoader instead of Shiki,
251
+ // so README diagrams render as graphics rather than highlighted text.
252
+ if (lang === 'mermaid') return `<pre class="mermaid">${escapeHtml(code)}</pre>`;
253
+ return highlight(hl, code, lang);
254
+ },
255
+ }),
256
+ );
254
257
  // README links open in a new tab.
255
258
  const baseLink = md.renderer.rules.link_open ?? ((t, i, o, _e, s) => s.renderToken(t, i, o));
256
259
  md.renderer.rules.link_open = (tokens, idx, opts, env, self) => {
@@ -1,7 +1,8 @@
1
1
  import { codeToHtml } from 'shiki';
2
2
  import MarkdownIt from 'markdown-it';
3
+ import { withTableScroll } from './md-tables';
3
4
 
4
- const md = new MarkdownIt({ html: false, linkify: true });
5
+ const md = withTableScroll(new MarkdownIt({ html: false, linkify: true }));
5
6
 
6
7
  export interface SampleHeading {
7
8
  slug: string;
@@ -379,8 +379,9 @@ html[lang='ko'] h1 {
379
379
  }
380
380
  /* Doc tables: a touch larger, centered (shrink-to-content), roomier cells, and
381
381
  a full-row hover highlight. `fit-content` + `margin-inline: auto` centers the
382
- table block while `max-width`/overflow (from the base .prose rule) keep it
383
- scrollable if it ever outgrows the column. */
382
+ table block. When the table sits in a `.aas-table-scroll` wrapper (the normal
383
+ case see below) the centering moves to the wrapper and the table keeps its
384
+ natural column widths instead. */
384
385
  .aas-reading.aas-reading :where(table) {
385
386
  display: table;
386
387
  width: fit-content;
@@ -727,6 +728,18 @@ summary {
727
728
  margin-inline: auto;
728
729
  }
729
730
 
731
+ /* Mermaid draws its labels as real HTML inside <foreignObject> and sizes nodes
732
+ and subgraph frames from the measured box. Mermaid 11 wraps label text in a
733
+ <p>, which then inherits the PAGE's paragraph typography — on a slide that is
734
+ 26px/41px, while mermaid only reserves ~25px for a subgraph title, so every
735
+ subgraph title overlapped the first node inside it by ~16px. Pin label
736
+ typography back to mermaid's own so its measurements hold. */
737
+ .mermaid foreignObject p {
738
+ margin: 0;
739
+ font-size: inherit;
740
+ line-height: inherit;
741
+ }
742
+
730
743
  /* Mermaid theme: rounded corners + soft pastel fills, derived from the site
731
744
  accent so it adapts to light/dark. `!important` overrides Mermaid's own
732
745
  id-scoped <style> block. Applies wherever a diagram renders (prose + samples). */
@@ -794,7 +807,10 @@ summary {
794
807
  }
795
808
 
796
809
 
797
- /* Markdown tables — without these, columns collapse together. */
810
+ /* Markdown tables — without these, columns collapse together. This is also the
811
+ fallback for a table that reaches the page WITHOUT the scroll wrapper below
812
+ (raw HTML in MDX, say): `display: block` + `overflow-x` at least keeps it
813
+ from pushing the page sideways. */
798
814
  .prose :where(table) {
799
815
  width: 100%;
800
816
  border-collapse: collapse;
@@ -803,6 +819,39 @@ summary {
803
819
  display: block;
804
820
  overflow-x: auto;
805
821
  }
822
+
823
+ /* Horizontal scroll box wrapped around every rendered table — by
824
+ `rehypeTableScroll` (markdown.mjs) for content collections, and by
825
+ `withTableScroll` (lib/md-tables.ts) for READMEs and sample descriptions.
826
+ A table cannot scroll itself: it needs `display: table` for real column
827
+ sizing, which makes `overflow-x` on the table a no-op, so a table wider than
828
+ its column had no escape but to wrap every cell — a 120px-tall table became
829
+ 430px of cramped text. The wrapper takes the scrolling; the table gets its
830
+ natural column widths back (`max-content`, floored at the column width so a
831
+ narrow table still fills it). */
832
+ .aas-table-scroll {
833
+ max-width: 100%;
834
+ overflow-x: auto;
835
+ overscroll-behavior-x: contain;
836
+ margin: 1.25rem 0;
837
+ }
838
+ .aas-table-scroll > table {
839
+ display: table;
840
+ width: max-content;
841
+ min-width: 100%;
842
+ margin: 0;
843
+ }
844
+ /* In a reading column the wrapper shrinks to the table and centers, so a table
845
+ narrower than the column keeps its own width rather than being stretched. */
846
+ .aas-reading.aas-reading .aas-table-scroll {
847
+ width: fit-content;
848
+ margin-inline: auto;
849
+ }
850
+ .aas-reading.aas-reading .aas-table-scroll > table {
851
+ min-width: 0;
852
+ max-width: none;
853
+ margin-inline: 0;
854
+ }
806
855
  .prose :where(th, td) {
807
856
  border: 1px solid var(--aas-border);
808
857
  padding: 0.55rem 0.85rem;
@@ -1271,13 +1320,25 @@ summary {
1271
1320
  color: var(--aas-muted);
1272
1321
  }
1273
1322
 
1274
- /* Tables */
1323
+ /* Tables. Same deal as prose tables: the `.aas-table-scroll` wrapper does the
1324
+ scrolling, so a wide table slides sideways instead of cramming its cells. */
1325
+ .aas-slide-inner .aas-table-scroll {
1326
+ width: 100%;
1327
+ margin: 0.5em 0;
1328
+ }
1275
1329
  .aas-slide-inner table {
1276
1330
  width: 100%;
1277
1331
  border-collapse: collapse;
1278
1332
  font-size: clamp(0.9rem, 1.5vw, 1.25rem);
1279
1333
  margin: 0.5em 0;
1280
1334
  }
1335
+ /* Declared after `.aas-slide-inner table` so the wrapped form wins: full column
1336
+ width normally, natural width (scrolling) once the table outgrows the slide. */
1337
+ .aas-slide-inner .aas-table-scroll > table {
1338
+ width: max-content;
1339
+ min-width: 100%;
1340
+ margin: 0;
1341
+ }
1281
1342
  .aas-slide-inner th,
1282
1343
  .aas-slide-inner td {
1283
1344
  text-align: left;
@@ -1528,6 +1589,38 @@ summary {
1528
1589
  /* 3b) Code stays pinned to the top of its filled box — that's the default block
1529
1590
  flow of a tall <pre>, so nothing extra is needed. */
1530
1591
 
1592
+ /* 3c) Size the diagram against the box it was just given, not a fixed viewport
1593
+ fraction. The generic caps (`pre` 62vh, svg 52vh) don't know how much room
1594
+ the fill rules actually handed over — on a full-height slide that box is
1595
+ ~78vh, so a tall flowchart rendered at a THIRD of its size with the unused
1596
+ height letterboxed as empty margins either side. The box already has a
1597
+ definite height here, so `100%` resolves against it; the svg's viewBox
1598
+ does the rest (it scales to fit and centers itself). Excludes the layouts
1599
+ that deliberately manage their own overflow: `compact` (natural size),
1600
+ `.aas-split.scroll` (vertical scroll box) and `scroll-x` (horizontal). */
1601
+ .aas-slide:not(.compact):not(.scroll-x) .aas-slide-inner > .aas-diagram > pre.mermaid,
1602
+ .aas-slide:not(.compact):not(.scroll-x)
1603
+ .aas-slide-inner
1604
+ > .aas-split:not(.scroll)
1605
+ > .aas-diagram
1606
+ > pre.mermaid {
1607
+ flex: 1 1 0;
1608
+ min-height: 0;
1609
+ max-height: 100%;
1610
+ width: 100%;
1611
+ }
1612
+ .aas-slide:not(.compact):not(.scroll-x) .aas-slide-inner > .aas-diagram > pre.mermaid > svg,
1613
+ .aas-slide:not(.compact):not(.scroll-x)
1614
+ .aas-slide-inner
1615
+ > .aas-split:not(.scroll)
1616
+ > .aas-diagram
1617
+ > pre.mermaid
1618
+ > svg {
1619
+ width: 100%;
1620
+ height: 100%;
1621
+ max-height: 100%;
1622
+ }
1623
+
1531
1624
  /* Scrollable diagram (`<div class="aas-split scroll">`): the diagram column is a
1532
1625
  fixed-height scroll box (opted out of the fill rules above) and the diagram
1533
1626
  fits the column width, so a tall/complex flowchart scrolls vertically next to