ligarb 0.8.2 → 0.9.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bbc72fa10dd440a8c1da528db02f314735cae034beed2ae921ba3112caee7fbd
4
- data.tar.gz: 14a5b2b990c3bb507f02982c7d9b823f9525a7964b4bd37c69e243316788820a
3
+ metadata.gz: fff2a914d9209b69c1b9c35d4133accfe0d5dcc4e272779959baa9c3bba64cf0
4
+ data.tar.gz: da08e338979034ba836c2321adb4820f91231e942a6bedb0e7ffb31128271c52
5
5
  SHA512:
6
- metadata.gz: 528b2af5e5039875052fba85d6765ce8489109bd726ad51732294746b04891bd6c7cd7927ebb98d09ccf97ac4dd3de6107111418691f855760f7e89db9e4f73b
7
- data.tar.gz: 6749c352bf7da3170bdbc9605422c8e7c4b59770f3f57dfa030bf96f07d47034e8f95c953815293e3267be477760e95379752cd2e082f550bbd2ca1a7251a1c2
6
+ metadata.gz: 6880ee104849e3904dad11583a8b635dc3bf105acea215df197ab4acbe1dcffa432b5c45c0c471d84424f22c7f287c2b11dc553f987c99d2613948d1f01eb766
7
+ data.tar.gz: c230438379c7ff16c808b38835fe0ea764b88433e109ce0573caba710163ec3efddb86975251faa145525176946a543b70eb44670fc55718998fade9098bf7c7
@@ -0,0 +1,116 @@
1
+ // Build-time mermaid syntax checker for ligarb.
2
+ //
3
+ // Usage: node mermaid_check.mjs <path/to/mermaid.min.js>
4
+ // stdin: JSON array of {"id": <any>, "text": <mermaid source>}
5
+ // stdout: JSON array of {"id": <any>, "error": <message or null>,
6
+ // "kind": "syntax" | "environment"}
7
+ //
8
+ // Loads the browser UMD bundle of mermaid in Node by stubbing just enough
9
+ // of the DOM (mermaid.parse() only parses; it never renders, but DOMPurify
10
+ // refuses to initialize without something that looks like a document).
11
+ //
12
+ // The DOM stub is intentionally minimal, so it cannot satisfy DOMPurify when a
13
+ // node label contains HTML (e.g. "A[1<br>2]"): sanitizing real markup needs a
14
+ // real DOM tree to walk, which the stub does not provide. That surfaces as a
15
+ // generic JS error (e.g. TypeError "Right-hand side of 'instanceof'..."), NOT a
16
+ // diagram syntax error. classifyError() tells the two apart so callers only
17
+ // warn about genuine mermaid problems and not these harness limitations.
18
+
19
+ const noop = () => {};
20
+
21
+ // Catch-all stub: any property access returns another stub, so the bundle's
22
+ // incidental DOM touches during initialization succeed silently.
23
+ function makeStub(name, overrides = {}) {
24
+ const target = function () {};
25
+ Object.assign(target, overrides);
26
+ return new Proxy(target, {
27
+ get(t, prop) {
28
+ if (prop === Symbol.toPrimitive) return () => "";
29
+ if (prop === "toString") return () => "";
30
+ if (typeof prop === "symbol") return undefined;
31
+ if (!(prop in t)) t[prop] = makeStub(name + "." + String(prop));
32
+ return t[prop];
33
+ },
34
+ set(t, prop, v) {
35
+ t[prop] = v;
36
+ return true;
37
+ },
38
+ apply() {
39
+ return makeStub(name + "()");
40
+ },
41
+ construct() {
42
+ return makeStub("new " + name);
43
+ },
44
+ });
45
+ }
46
+
47
+ globalThis.window = globalThis;
48
+ // nodeType 9 = DOCUMENT_NODE; DOMPurify checks it to decide it has a real DOM.
49
+ globalThis.document = makeStub("document", { nodeType: 9 });
50
+ globalThis.navigator = { userAgent: "node" };
51
+ globalThis.addEventListener = noop;
52
+ globalThis.location = { href: "http://localhost/", protocol: "http:" };
53
+ globalThis.Element = function Element() {};
54
+ globalThis.HTMLTemplateElement = function HTMLTemplateElement() {};
55
+ globalThis.Node = function Node() {};
56
+ globalThis.NodeFilter = { SHOW_ELEMENT: 1, SHOW_TEXT: 4, SHOW_COMMENT: 128 };
57
+ globalThis.NamedNodeMap = function NamedNodeMap() {};
58
+ globalThis.HTMLFormElement = function HTMLFormElement() {};
59
+ globalThis.DOMParser = function DOMParser() {
60
+ return makeStub("domparser");
61
+ };
62
+
63
+ const { readFileSync } = await import("fs");
64
+ const vm = await import("vm");
65
+
66
+ const mermaidPath = process.argv[2];
67
+ if (!mermaidPath) {
68
+ console.error("usage: node mermaid_check.mjs <mermaid.min.js>");
69
+ process.exit(2);
70
+ }
71
+
72
+ // The bundle starts with "use strict" + top-level `var`, so indirect eval
73
+ // would not create the global binding it expects; a classic script does.
74
+ vm.runInThisContext(readFileSync(mermaidPath, "utf8"), {
75
+ filename: "mermaid.min.js",
76
+ });
77
+
78
+ const mermaid = globalThis.mermaid;
79
+ if (!mermaid || typeof mermaid.parse !== "function") {
80
+ console.error("mermaid.parse is not available after loading the bundle");
81
+ process.exit(2);
82
+ }
83
+
84
+ // Decide whether a thrown error is a genuine mermaid diagram problem
85
+ // ("syntax") or an artifact of our minimal DOM stub ("environment").
86
+ //
87
+ // - jison grammar errors carry a structured `.hash` -> syntax
88
+ // - mermaid's typed errors (e.g. UnknownDiagramError) -> syntax
89
+ // - generic JS runtime errors (TypeError/ReferenceError/RangeError/EvalError)
90
+ // with no hash come from the DOM stub -> environment
91
+ // - anything else is reported as syntax, erring toward visibility
92
+ function classifyError(e) {
93
+ if (e && e.hash !== undefined) return "syntax";
94
+ const name = e && e.name;
95
+ if (name && name.endsWith("DiagramError")) return "syntax";
96
+ if (["TypeError", "ReferenceError", "RangeError", "EvalError"].includes(name)) {
97
+ return "environment";
98
+ }
99
+ return "syntax";
100
+ }
101
+
102
+ const blocks = JSON.parse(readFileSync(0, "utf8"));
103
+ const results = [];
104
+ for (const block of blocks) {
105
+ try {
106
+ await mermaid.parse(block.text);
107
+ results.push({ id: block.id, error: null, kind: "syntax" });
108
+ } catch (e) {
109
+ results.push({
110
+ id: block.id,
111
+ error: String(e && e.message ? e.message : e),
112
+ kind: classifyError(e),
113
+ });
114
+ }
115
+ }
116
+ console.log(JSON.stringify(results));
data/assets/style.css CHANGED
@@ -515,7 +515,14 @@ mark.search-highlight {
515
515
  align-items: flex-start;
516
516
  }
517
517
 
518
- .theme-toggle {
518
+ .sidebar-header-actions {
519
+ display: flex;
520
+ gap: 0.35rem;
521
+ flex-shrink: 0;
522
+ }
523
+
524
+ .theme-toggle,
525
+ .sidebar-collapse {
519
526
  background: none;
520
527
  border: 1px solid var(--color-border);
521
528
  border-radius: 4px;
@@ -527,11 +534,30 @@ mark.search-highlight {
527
534
  flex-shrink: 0;
528
535
  }
529
536
 
530
- .theme-toggle:hover {
537
+ .theme-toggle:hover,
538
+ .sidebar-collapse:hover {
531
539
  color: var(--color-text);
532
540
  background: var(--color-sidebar-hover);
533
541
  }
534
542
 
543
+ /* Persistent collapse on wide screens: hide the sidebar and let content
544
+ reclaim the full width. The floating toggle reappears to bring it back.
545
+ Scoped to wide viewports so it never fights the off-canvas behavior. */
546
+ @media (min-width: 901px) {
547
+ .sidebar-collapsed .sidebar {
548
+ transform: translateX(-100%);
549
+ transition: transform 0.3s ease;
550
+ }
551
+
552
+ .sidebar-collapsed .content {
553
+ margin-left: 0;
554
+ }
555
+
556
+ .sidebar-collapsed .sidebar-toggle {
557
+ display: block;
558
+ }
559
+ }
560
+
535
561
  /* === Part / Appendix TOC === */
536
562
  .toc-part {
537
563
  margin-top: 0.5rem;
@@ -643,7 +669,9 @@ mark.search-highlight {
643
669
  }
644
670
 
645
671
  /* === Responsive === */
646
- @media (max-width: 768px) {
672
+ /* Off-canvas sidebar for narrow viewports. The 900px breakpoint covers iPad
673
+ portrait (810–834px), which would otherwise keep the fixed desktop sidebar. */
674
+ @media (max-width: 900px) {
647
675
  .sidebar {
648
676
  transform: translateX(-100%);
649
677
  transition: transform 0.3s ease;
@@ -657,6 +685,12 @@ mark.search-highlight {
657
685
  display: block;
658
686
  }
659
687
 
688
+ /* The in-header collapse button is only meaningful in the fixed desktop
689
+ layout; off-canvas already hides the sidebar via the floating toggle. */
690
+ .sidebar-collapse {
691
+ display: none;
692
+ }
693
+
660
694
  .content {
661
695
  margin-left: 0;
662
696
  padding: 2rem 1.5rem;