@miethe/ui 0.2.0 → 0.6.0

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.
Files changed (192) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +863 -9
  3. package/dist/components/content-viewer/ArticleViewer.d.ts +42 -0
  4. package/dist/components/content-viewer/ArticleViewer.d.ts.map +1 -0
  5. package/dist/components/content-viewer/ArticleViewer.js +321 -0
  6. package/dist/components/content-viewer/ArticleViewer.js.map +1 -0
  7. package/dist/components/content-viewer/FrontmatterHeader.d.ts +32 -0
  8. package/dist/components/content-viewer/FrontmatterHeader.d.ts.map +1 -0
  9. package/dist/components/content-viewer/FrontmatterHeader.js +95 -0
  10. package/dist/components/content-viewer/FrontmatterHeader.js.map +1 -0
  11. package/dist/components/content-viewer/callouts/Callout.d.ts +43 -0
  12. package/dist/components/content-viewer/callouts/Callout.d.ts.map +1 -0
  13. package/dist/components/content-viewer/callouts/Callout.js +86 -0
  14. package/dist/components/content-viewer/callouts/Callout.js.map +1 -0
  15. package/dist/components/content-viewer/callouts/index.d.ts +2 -0
  16. package/dist/components/content-viewer/callouts/index.d.ts.map +1 -0
  17. package/dist/components/content-viewer/callouts/index.js +2 -0
  18. package/dist/components/content-viewer/callouts/index.js.map +1 -0
  19. package/dist/components/content-viewer/index.d.ts +21 -0
  20. package/dist/components/content-viewer/index.d.ts.map +1 -0
  21. package/dist/components/content-viewer/index.js +29 -0
  22. package/dist/components/content-viewer/index.js.map +1 -0
  23. package/dist/components/content-viewer/plugins/index.d.ts +5 -0
  24. package/dist/components/content-viewer/plugins/index.d.ts.map +1 -0
  25. package/dist/components/content-viewer/plugins/index.js +5 -0
  26. package/dist/components/content-viewer/plugins/index.js.map +1 -0
  27. package/dist/components/content-viewer/plugins/lowlightLoader.d.ts +63 -0
  28. package/dist/components/content-viewer/plugins/lowlightLoader.d.ts.map +1 -0
  29. package/dist/components/content-viewer/plugins/lowlightLoader.js +120 -0
  30. package/dist/components/content-viewer/plugins/lowlightLoader.js.map +1 -0
  31. package/dist/components/content-viewer/plugins/rehypeCodeHighlight.d.ts +44 -0
  32. package/dist/components/content-viewer/plugins/rehypeCodeHighlight.d.ts.map +1 -0
  33. package/dist/components/content-viewer/plugins/rehypeCodeHighlight.js +122 -0
  34. package/dist/components/content-viewer/plugins/rehypeCodeHighlight.js.map +1 -0
  35. package/dist/components/content-viewer/plugins/rehypeExternalLinks.d.ts +59 -0
  36. package/dist/components/content-viewer/plugins/rehypeExternalLinks.d.ts.map +1 -0
  37. package/dist/components/content-viewer/plugins/rehypeExternalLinks.js +79 -0
  38. package/dist/components/content-viewer/plugins/rehypeExternalLinks.js.map +1 -0
  39. package/dist/components/content-viewer/plugins/rehypeHeadingIds.d.ts +37 -0
  40. package/dist/components/content-viewer/plugins/rehypeHeadingIds.d.ts.map +1 -0
  41. package/dist/components/content-viewer/plugins/rehypeHeadingIds.js +82 -0
  42. package/dist/components/content-viewer/plugins/rehypeHeadingIds.js.map +1 -0
  43. package/dist/components/content-viewer/plugins/remarkCallouts.d.ts +39 -0
  44. package/dist/components/content-viewer/plugins/remarkCallouts.d.ts.map +1 -0
  45. package/dist/components/content-viewer/plugins/remarkCallouts.js +77 -0
  46. package/dist/components/content-viewer/plugins/remarkCallouts.js.map +1 -0
  47. package/dist/components/content-viewer/plugins/slugify.d.ts +24 -0
  48. package/dist/components/content-viewer/plugins/slugify.d.ts.map +1 -0
  49. package/dist/components/content-viewer/plugins/slugify.js +31 -0
  50. package/dist/components/content-viewer/plugins/slugify.js.map +1 -0
  51. package/dist/components/content-viewer/sanitize.d.ts +75 -0
  52. package/dist/components/content-viewer/sanitize.d.ts.map +1 -0
  53. package/dist/components/content-viewer/sanitize.js +252 -0
  54. package/dist/components/content-viewer/sanitize.js.map +1 -0
  55. package/dist/components/content-viewer/types.d.ts +315 -0
  56. package/dist/components/content-viewer/types.d.ts.map +1 -0
  57. package/dist/components/content-viewer/types.js +8 -0
  58. package/dist/components/content-viewer/types.js.map +1 -0
  59. package/dist/components/content-viewer/variants.d.ts +71 -0
  60. package/dist/components/content-viewer/variants.d.ts.map +1 -0
  61. package/dist/components/content-viewer/variants.js +105 -0
  62. package/dist/components/content-viewer/variants.js.map +1 -0
  63. package/dist/content-viewer/ContentPane.d.ts +44 -1
  64. package/dist/content-viewer/ContentPane.d.ts.map +1 -1
  65. package/dist/content-viewer/ContentPane.js +139 -5
  66. package/dist/content-viewer/ContentPane.js.map +1 -1
  67. package/dist/content-viewer/FileTree.d.ts +23 -1
  68. package/dist/content-viewer/FileTree.d.ts.map +1 -1
  69. package/dist/content-viewer/FileTree.js +20 -5
  70. package/dist/content-viewer/FileTree.js.map +1 -1
  71. package/dist/content-viewer/index.d.ts +2 -0
  72. package/dist/content-viewer/index.d.ts.map +1 -1
  73. package/dist/content-viewer/index.js +2 -0
  74. package/dist/content-viewer/index.js.map +1 -1
  75. package/dist/diff/DiffViewer.js +3 -3
  76. package/dist/diff/DiffViewer.js.map +1 -1
  77. package/dist/discovery/discovery-card.d.ts +25 -0
  78. package/dist/discovery/discovery-card.d.ts.map +1 -0
  79. package/dist/discovery/discovery-card.js +265 -0
  80. package/dist/discovery/discovery-card.js.map +1 -0
  81. package/dist/discovery/index.d.ts +3 -0
  82. package/dist/discovery/index.d.ts.map +1 -0
  83. package/dist/discovery/index.js +3 -0
  84. package/dist/discovery/index.js.map +1 -0
  85. package/dist/display/ContextInfoCard.d.ts +61 -0
  86. package/dist/display/ContextInfoCard.d.ts.map +1 -0
  87. package/dist/display/ContextInfoCard.js +45 -0
  88. package/dist/display/ContextInfoCard.js.map +1 -0
  89. package/dist/display/index.d.ts +2 -0
  90. package/dist/display/index.d.ts.map +1 -1
  91. package/dist/display/index.js +1 -0
  92. package/dist/display/index.js.map +1 -1
  93. package/dist/editor/CodeEditor.d.ts +39 -0
  94. package/dist/editor/CodeEditor.d.ts.map +1 -0
  95. package/dist/editor/CodeEditor.js +114 -0
  96. package/dist/editor/CodeEditor.js.map +1 -0
  97. package/dist/editor/MarkdownEditor.d.ts +3 -2
  98. package/dist/editor/MarkdownEditor.d.ts.map +1 -1
  99. package/dist/editor/MarkdownEditor.js +32 -80
  100. package/dist/editor/MarkdownEditor.js.map +1 -1
  101. package/dist/editor/SplitPreview.d.ts +10 -1
  102. package/dist/editor/SplitPreview.d.ts.map +1 -1
  103. package/dist/editor/SplitPreview.js +4 -2
  104. package/dist/editor/SplitPreview.js.map +1 -1
  105. package/dist/editor/codeLanguages.d.ts +28 -0
  106. package/dist/editor/codeLanguages.d.ts.map +1 -0
  107. package/dist/editor/codeLanguages.js +54 -0
  108. package/dist/editor/codeLanguages.js.map +1 -0
  109. package/dist/editor/index.d.ts +2 -0
  110. package/dist/editor/index.d.ts.map +1 -1
  111. package/dist/editor/index.js +6 -0
  112. package/dist/editor/index.js.map +1 -1
  113. package/dist/editor/theme.d.ts +16 -0
  114. package/dist/editor/theme.d.ts.map +1 -0
  115. package/dist/editor/theme.js +82 -0
  116. package/dist/editor/theme.js.map +1 -0
  117. package/dist/filters/filter-bar.d.ts +14 -0
  118. package/dist/filters/filter-bar.d.ts.map +1 -0
  119. package/dist/filters/filter-bar.js +47 -0
  120. package/dist/filters/filter-bar.js.map +1 -0
  121. package/dist/filters/filter-slot-config.d.ts +239 -0
  122. package/dist/filters/filter-slot-config.d.ts.map +1 -0
  123. package/dist/filters/filter-slot-config.js +24 -0
  124. package/dist/filters/filter-slot-config.js.map +1 -0
  125. package/dist/filters/index.d.ts +2 -0
  126. package/dist/filters/index.d.ts.map +1 -1
  127. package/dist/filters/index.js +1 -0
  128. package/dist/filters/index.js.map +1 -1
  129. package/dist/primitives/BatchReadinessPill.d.ts +22 -0
  130. package/dist/primitives/BatchReadinessPill.d.ts.map +1 -0
  131. package/dist/primitives/BatchReadinessPill.js +20 -0
  132. package/dist/primitives/BatchReadinessPill.js.map +1 -0
  133. package/dist/primitives/Card.d.ts +28 -0
  134. package/dist/primitives/Card.d.ts.map +1 -0
  135. package/dist/primitives/Card.js +30 -0
  136. package/dist/primitives/Card.js.map +1 -0
  137. package/dist/primitives/CollectionPicker.d.ts +47 -0
  138. package/dist/primitives/CollectionPicker.d.ts.map +1 -0
  139. package/dist/primitives/CollectionPicker.js +105 -0
  140. package/dist/primitives/CollectionPicker.js.map +1 -0
  141. package/dist/primitives/CreateEntityDialog.d.ts +144 -0
  142. package/dist/primitives/CreateEntityDialog.d.ts.map +1 -0
  143. package/dist/primitives/CreateEntityDialog.js +379 -0
  144. package/dist/primitives/CreateEntityDialog.js.map +1 -0
  145. package/dist/primitives/EffectiveStatusChips.d.ts +43 -0
  146. package/dist/primitives/EffectiveStatusChips.d.ts.map +1 -0
  147. package/dist/primitives/EffectiveStatusChips.js +23 -0
  148. package/dist/primitives/EffectiveStatusChips.js.map +1 -0
  149. package/dist/primitives/FormField.d.ts +29 -0
  150. package/dist/primitives/FormField.d.ts.map +1 -0
  151. package/dist/primitives/FormField.js +27 -0
  152. package/dist/primitives/FormField.js.map +1 -0
  153. package/dist/primitives/Label.d.ts +20 -0
  154. package/dist/primitives/Label.d.ts.map +1 -0
  155. package/dist/primitives/Label.js +21 -0
  156. package/dist/primitives/Label.js.map +1 -0
  157. package/dist/primitives/MismatchBadge.d.ts +34 -0
  158. package/dist/primitives/MismatchBadge.d.ts.map +1 -0
  159. package/dist/primitives/MismatchBadge.js +28 -0
  160. package/dist/primitives/MismatchBadge.js.map +1 -0
  161. package/dist/primitives/PlanningNodeTypeIcon.d.ts +33 -0
  162. package/dist/primitives/PlanningNodeTypeIcon.d.ts.map +1 -0
  163. package/dist/primitives/PlanningNodeTypeIcon.js +35 -0
  164. package/dist/primitives/PlanningNodeTypeIcon.js.map +1 -0
  165. package/dist/primitives/SecretField.d.ts +28 -0
  166. package/dist/primitives/SecretField.d.ts.map +1 -0
  167. package/dist/primitives/SecretField.js +65 -0
  168. package/dist/primitives/SecretField.js.map +1 -0
  169. package/dist/primitives/Spinner.d.ts +16 -0
  170. package/dist/primitives/Spinner.d.ts.map +1 -0
  171. package/dist/primitives/Spinner.js +34 -0
  172. package/dist/primitives/Spinner.js.map +1 -0
  173. package/dist/primitives/StatusChip.d.ts +17 -0
  174. package/dist/primitives/StatusChip.d.ts.map +1 -0
  175. package/dist/primitives/StatusChip.js +22 -0
  176. package/dist/primitives/StatusChip.js.map +1 -0
  177. package/dist/primitives/Switch.d.ts +32 -0
  178. package/dist/primitives/Switch.d.ts.map +1 -0
  179. package/dist/primitives/Switch.js +43 -0
  180. package/dist/primitives/Switch.js.map +1 -0
  181. package/dist/primitives/index.d.ts +28 -0
  182. package/dist/primitives/index.d.ts.map +1 -1
  183. package/dist/primitives/index.js +16 -0
  184. package/dist/primitives/index.js.map +1 -1
  185. package/dist/primitives/variants.d.ts +18 -0
  186. package/dist/primitives/variants.d.ts.map +1 -0
  187. package/dist/primitives/variants.js +33 -0
  188. package/dist/primitives/variants.js.map +1 -0
  189. package/dist/utils/type-colors.d.ts.map +1 -1
  190. package/dist/utils/type-colors.js +4 -0
  191. package/dist/utils/type-colors.js.map +1 -1
  192. package/package.json +40 -6
@@ -0,0 +1,252 @@
1
+ /**
2
+ * HTML sanitization for ArticleViewer — PU3-02 / PU3-03
3
+ *
4
+ * Two sanitization paths, selected via the `useDOMPurify` prop:
5
+ *
6
+ * 1. **Default (rehype-sanitize)** — unified pipeline:
7
+ * `rehypeParse → rehypeExternalLinks → rehypeSanitize(schema) → rehypeStringify`
8
+ * Uses GitHub's default allowlist (`defaultSchema`) with minor additions for
9
+ * callout div wrappers (class attr) and standard data attributes.
10
+ *
11
+ * 2. **Optional (DOMPurify)** — dynamically imports `isomorphic-dompurify`.
12
+ * `isomorphic-dompurify` is NOT a hard dependency; it must be installed by the
13
+ * consumer. If unavailable, falls back to path 1 with a console warning.
14
+ *
15
+ * Both paths:
16
+ * - Strip `<script>`, event handler attributes (`on*`), `javascript:` and unsafe
17
+ * `data:` URLs, `<iframe>`, `<object>`, `<embed>`, `<form>`, `<base>`.
18
+ * - Preserve safe HTML: headings, paragraphs, lists, blockquotes, tables,
19
+ * `<code>`, `<pre>`, `<a>` (http/https/mailto href), `<img>` (http/https src),
20
+ * `<div>`, `<span>` (with class), `<strong>`, `<em>`, `<del>`, `<details>`,
21
+ * `<summary>`, `<figure>`, `<figcaption>`, `<hr>`, `<br>`.
22
+ * - Apply external link hardening (`target="_blank" rel="noopener noreferrer"`).
23
+ *
24
+ * ## Custom schema rationale
25
+ *
26
+ * `rehype-sanitize`'s `defaultSchema` (mirroring GitHub's allowlist) is strict
27
+ * enough for most use cases. We extend it to allow:
28
+ * - `class` on `div` and `span` — callout wrappers use `data-callout-type` and
29
+ * Tailwind utility classes.
30
+ * - `data-callout-type` on `div` — semantic attribute written by the remark
31
+ * callout plugin.
32
+ * - `id` on headings — anchor links in compiled wiki documents.
33
+ * - `rel` and `target` on `a` — preserved after the external-link plugin sets
34
+ * them (rehype-sanitize would otherwise strip `target`).
35
+ *
36
+ * ## Allowlist vs blocklist
37
+ *
38
+ * We use an **allowlist** (default-deny) approach — only explicitly listed tags
39
+ * and attributes pass through. This is safer than blocklist-based sanitization
40
+ * which requires enumerating every possible XSS vector.
41
+ */
42
+ import { unified } from 'unified';
43
+ import rehypeParse from 'rehype-parse';
44
+ import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
45
+ import rehypeStringify from 'rehype-stringify';
46
+ import { rehypeExternalLinks } from './plugins/rehypeExternalLinks';
47
+ /**
48
+ * Cast `unified` to a simple callable so TypeScript doesn't balk at the
49
+ * complex overloaded union on `Processor.use()`. The actual runtime behaviour
50
+ * is identical — we just widen the type to avoid TS2349.
51
+ */
52
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
+ const makeProcessor = unified;
54
+ // ---------------------------------------------------------------------------
55
+ // Custom schema — extends GitHub's defaultSchema for Portal HTML
56
+ // ---------------------------------------------------------------------------
57
+ /**
58
+ * Extended sanitization schema for Portal-compiled HTML.
59
+ *
60
+ * Extends `defaultSchema` (GitHub's allowlist) to preserve:
61
+ * - `class` on `div`, `span` — callout wrappers + utility classes
62
+ * - `data-callout-type` on `div` — semantic callout attribute
63
+ * - `id` on `h1`–`h6` — anchor link targets in wiki documents
64
+ * - `target` on `a` — set by the external-link plugin (`_blank`)
65
+ * - `rel` on `a` — set by the external-link plugin (`noopener noreferrer`)
66
+ */
67
+ const defaultAttrs = (defaultSchema.attributes ?? {});
68
+ export const ARTICLE_VIEWER_SCHEMA = {
69
+ ...defaultSchema,
70
+ attributes: {
71
+ ...defaultAttrs,
72
+ // Allow class on div/span for callout wrappers and Tailwind utilities
73
+ div: [...(defaultAttrs['div'] ?? []), 'className', 'class', 'data-callout-type'],
74
+ span: [...(defaultAttrs['span'] ?? []), 'className', 'class'],
75
+ // Allow id on headings for anchor links
76
+ h1: [...(defaultAttrs['h1'] ?? []), 'id'],
77
+ h2: [...(defaultAttrs['h2'] ?? []), 'id'],
78
+ h3: [...(defaultAttrs['h3'] ?? []), 'id'],
79
+ h4: [...(defaultAttrs['h4'] ?? []), 'id'],
80
+ h5: [...(defaultAttrs['h5'] ?? []), 'id'],
81
+ h6: [...(defaultAttrs['h6'] ?? []), 'id'],
82
+ // Preserve target and rel set by rehypeExternalLinks (before sanitize runs)
83
+ a: [...(defaultAttrs['a'] ?? []), 'target', 'rel'],
84
+ },
85
+ };
86
+ // ---------------------------------------------------------------------------
87
+ // DOMPurify config (for the optional path)
88
+ // ---------------------------------------------------------------------------
89
+ /**
90
+ * DOMPurify config that mirrors our rehype-sanitize allowlist:
91
+ * - Permit safe tags only
92
+ * - Strip event handlers and javascript: / data: URLs
93
+ * - Preserve class, id, data-callout-type, target, rel
94
+ */
95
+ const DOMPURIFY_CONFIG = {
96
+ USE_PROFILES: { html: true },
97
+ ALLOWED_TAGS: [
98
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
99
+ 'p', 'br', 'hr',
100
+ 'ul', 'ol', 'li',
101
+ 'blockquote', 'pre', 'code',
102
+ 'table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td',
103
+ 'a', 'img',
104
+ 'strong', 'em', 'del', 's', 'u', 'sup', 'sub',
105
+ 'div', 'span', 'section', 'article', 'aside',
106
+ 'details', 'summary',
107
+ 'figure', 'figcaption',
108
+ 'dl', 'dt', 'dd',
109
+ 'input', // task list checkboxes (type=checkbox, disabled)
110
+ ],
111
+ ALLOWED_ATTR: [
112
+ 'class', 'id', 'data-callout-type',
113
+ 'href', 'src', 'alt', 'title',
114
+ 'target', 'rel',
115
+ 'type', 'checked', 'disabled', // for task list checkboxes
116
+ 'colspan', 'rowspan', 'align',
117
+ 'width', 'height',
118
+ 'lang', 'dir',
119
+ ],
120
+ FORBID_TAGS: ['script', 'style', 'iframe', 'frame', 'frameset', 'object', 'embed', 'form', 'base', 'meta', 'link'],
121
+ FORBID_ATTR: [],
122
+ ALLOW_DATA_ATTR: false,
123
+ FORCE_BODY: false,
124
+ };
125
+ // ---------------------------------------------------------------------------
126
+ // rehype-sanitize pipeline (default path) — PU3-02
127
+ // ---------------------------------------------------------------------------
128
+ /**
129
+ * Build a unified processor for HTML sanitization.
130
+ *
131
+ * Pipeline:
132
+ * 1. `rehypeParse` — parse HTML fragment to hast
133
+ * 2. `rehypeExternalLinks` — add target/rel to external links
134
+ * 3. `rehypeSanitize(ARTICLE_VIEWER_SCHEMA)` — strip XSS vectors
135
+ * 4. `rehypeStringify` — serialize back to HTML string
136
+ *
137
+ * We build fresh processors rather than sharing one instance to avoid vfile
138
+ * state leaking between calls (rehype-parse attaches vfile metadata).
139
+ */
140
+ function buildSanitizeProcessor() {
141
+ return makeProcessor()
142
+ .use(rehypeParse, { fragment: true })
143
+ .use(rehypeExternalLinks)
144
+ .use(rehypeSanitize, ARTICLE_VIEWER_SCHEMA)
145
+ .use(rehypeStringify);
146
+ }
147
+ /**
148
+ * Sanitize an HTML string using `rehype-sanitize@6`.
149
+ *
150
+ * Pipeline: rehypeParse → rehypeExternalLinks → rehypeSanitize → rehypeStringify
151
+ */
152
+ export function sanitizeWithRehype(html) {
153
+ const processor = buildSanitizeProcessor();
154
+ return processor.processSync(html).toString();
155
+ }
156
+ // ---------------------------------------------------------------------------
157
+ // DOMPurify optional path — PU3-03
158
+ // ---------------------------------------------------------------------------
159
+ /** Cache the dynamic import result so we only attempt it once per session */
160
+ let domPurifyModule = null;
161
+ let domPurifyLoadAttempted = false;
162
+ /**
163
+ * Attempt to load `isomorphic-dompurify` dynamically.
164
+ * Returns the module if available; `null` if not installed.
165
+ */
166
+ async function tryLoadDOMPurify() {
167
+ if (domPurifyLoadAttempted)
168
+ return domPurifyModule;
169
+ domPurifyLoadAttempted = true;
170
+ try {
171
+ // Dynamic import — isomorphic-dompurify is an optional peer dep with no bundled types.
172
+ // @ts-expect-error — optional package; not in node_modules at build time
173
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
174
+ const mod = await import(/* webpackIgnore: true */ 'isomorphic-dompurify');
175
+ const dp = mod.default ?? mod;
176
+ if (typeof dp?.sanitize === 'function') {
177
+ domPurifyModule = dp;
178
+ }
179
+ else {
180
+ console.warn('[ArticleViewer] isomorphic-dompurify loaded but has unexpected shape; falling back to rehype-sanitize');
181
+ }
182
+ }
183
+ catch {
184
+ // Package not installed — expected; fallback handled at call site
185
+ domPurifyModule = null;
186
+ }
187
+ return domPurifyModule;
188
+ }
189
+ /**
190
+ * Sanitize an HTML string, preferring DOMPurify if available (async).
191
+ *
192
+ * On first call it may do a dynamic import; subsequent calls use the cache.
193
+ * If DOMPurify is unavailable, falls back to `sanitizeWithRehype`.
194
+ */
195
+ export async function sanitizeWithDOMPurify(html) {
196
+ const dp = await tryLoadDOMPurify();
197
+ if (!dp) {
198
+ console.warn('[ArticleViewer] useDOMPurify=true requested but isomorphic-dompurify is not installed. ' +
199
+ 'Falling back to rehype-sanitize. ' +
200
+ 'Install isomorphic-dompurify to use the DOMPurify path: npm install isomorphic-dompurify');
201
+ return sanitizeWithRehype(html);
202
+ }
203
+ // Apply external link hardening before handing to DOMPurify
204
+ const withLinks = makeProcessor()
205
+ .use(rehypeParse, { fragment: true })
206
+ .use(rehypeExternalLinks)
207
+ .use(rehypeStringify)
208
+ .processSync(html);
209
+ return dp.sanitize(withLinks.toString(), DOMPURIFY_CONFIG);
210
+ }
211
+ // ---------------------------------------------------------------------------
212
+ // Synchronous sanitize dispatcher — used by the component render path
213
+ // ---------------------------------------------------------------------------
214
+ /**
215
+ * Synchronously sanitize `html`.
216
+ *
217
+ * When `useDOMPurify=true` and DOMPurify has already been loaded (warm cache),
218
+ * uses DOMPurify. If the cache is cold (first render), falls back to
219
+ * rehype-sanitize synchronously and triggers the async load in the background
220
+ * so subsequent renders can use DOMPurify.
221
+ */
222
+ export function sanitizeHtml(html, opts) {
223
+ if (opts.useDOMPurify) {
224
+ if (domPurifyModule) {
225
+ // Warm cache — use DOMPurify synchronously
226
+ const withLinks = makeProcessor()
227
+ .use(rehypeParse, { fragment: true })
228
+ .use(rehypeExternalLinks)
229
+ .use(rehypeStringify)
230
+ .processSync(html);
231
+ return domPurifyModule.sanitize(withLinks.toString(), DOMPURIFY_CONFIG);
232
+ }
233
+ // Cold cache — kick off the async load and fall through to rehype for this render
234
+ if (!domPurifyLoadAttempted) {
235
+ void tryLoadDOMPurify();
236
+ }
237
+ }
238
+ return sanitizeWithRehype(html);
239
+ }
240
+ /**
241
+ * Warm the DOMPurify cache. Call this once at app startup if you plan to use
242
+ * `useDOMPurify={true}` to avoid the rehype fallback on the first render.
243
+ */
244
+ export async function warmDOMPurifyCache() {
245
+ await tryLoadDOMPurify();
246
+ }
247
+ /** Exposed for testing — resets the module-level load state */
248
+ export function _resetDOMPurifyCache() {
249
+ domPurifyModule = null;
250
+ domPurifyLoadAttempted = false;
251
+ }
252
+ //# sourceMappingURL=sanitize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../../../src/components/content-viewer/sanitize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,315 @@
1
+ /**
2
+ * @miethe/ui — ArticleViewer type definitions
3
+ *
4
+ * All public types exported from the content-viewer component.
5
+ * No `any` types at module boundaries.
6
+ */
7
+ import type { ComponentType, ReactNode } from 'react';
8
+ /**
9
+ * Typography variant applied to the ArticleViewer.
10
+ *
11
+ * Each variant maps to a CSS class (`cv-variant-{name}`) that expects
12
+ * CSS custom properties at document root. Consumers (e.g., Portal) define
13
+ * the actual values in their `globals.css`; the component only applies
14
+ * the class. See the CSS-Variable Contract in the content-viewer README.
15
+ *
16
+ * - `"editorial"` — long-form reading; display serif headings, generous line-height
17
+ * - `"compact"` — dense information display; smaller type scale, tighter spacing
18
+ * - `"technical"` — code-heavy content; monospace body available, clear heading hierarchy
19
+ */
20
+ export type ArticleVariant = 'editorial' | 'compact' | 'technical';
21
+ /**
22
+ * Shape of the CSS-variable slot names for a single variant.
23
+ * Used by the variant utility to document and validate the contract.
24
+ * All slots are optional — missing variables fall back to browser defaults.
25
+ */
26
+ export interface VariantTokenShape {
27
+ /** CSS var for h1 font family, e.g. `--cv-editorial-h1-font` */
28
+ h1Font: string;
29
+ /** CSS var for h1 font size, e.g. `--cv-editorial-h1-size` */
30
+ h1Size: string;
31
+ /** CSS var for h2 font family */
32
+ h2Font: string;
33
+ /** CSS var for h2 font size */
34
+ h2Size: string;
35
+ /** CSS var for body font family */
36
+ bodyFont: string;
37
+ /** CSS var for body font size */
38
+ bodySize: string;
39
+ /** CSS var for body line-height */
40
+ bodyLineHeight: string;
41
+ /** CSS var for blockquote text color */
42
+ quoteColor: string;
43
+ /** CSS var for blockquote font-style (e.g. "italic") */
44
+ quoteFontStyle: string;
45
+ /** CSS var for `note` callout accent color */
46
+ calloutNoteAccent: string;
47
+ /** CSS var for `note` callout background */
48
+ calloutNoteBg: string;
49
+ /** CSS var for `reference` callout accent color */
50
+ calloutReferenceAccent: string;
51
+ /** CSS var for `reference` callout background */
52
+ calloutReferenceBg: string;
53
+ /** CSS var for `warning` callout accent color */
54
+ calloutWarningAccent: string;
55
+ /** CSS var for `warning` callout background */
56
+ calloutWarningBg: string;
57
+ /** CSS var for `info` callout accent color */
58
+ calloutInfoAccent: string;
59
+ /** CSS var for `info` callout background */
60
+ calloutInfoBg: string;
61
+ }
62
+ /**
63
+ * Visibility mode for the frontmatter header.
64
+ *
65
+ * - `"show"` — render FrontmatterHeader in expanded state
66
+ * - `"collapse"` — render FrontmatterHeader in collapsed state
67
+ * - `"hide"` — omit FrontmatterHeader entirely (default)
68
+ */
69
+ export type FrontmatterDisplayMode = 'show' | 'collapse' | 'hide';
70
+ /**
71
+ * Parsed YAML frontmatter object extracted from the markdown content.
72
+ * Values are typed as `unknown` at the boundary; use type guards before rendering.
73
+ */
74
+ export type FrontmatterData = Record<string, unknown>;
75
+ /**
76
+ * Props for the FrontmatterHeader component.
77
+ */
78
+ export interface FrontmatterHeaderProps {
79
+ /**
80
+ * Parsed YAML frontmatter key-value map.
81
+ * 1-level nesting is rendered as indented rows; deeper objects appear as JSON strings.
82
+ */
83
+ frontmatter: FrontmatterData;
84
+ /**
85
+ * Whether the header starts in collapsed state.
86
+ * @default false
87
+ */
88
+ isCollapsed?: boolean;
89
+ /**
90
+ * Callback invoked when the user toggles the collapsed state.
91
+ * Receives the new collapsed value (`true` = collapsed).
92
+ */
93
+ onToggleCollapse?: (collapsed: boolean) => void;
94
+ /** Additional CSS class names */
95
+ className?: string;
96
+ }
97
+ /**
98
+ * Custom component overrides for ArticleViewer sub-components.
99
+ * All keys are optional — omitting a key uses the default implementation.
100
+ */
101
+ export interface ArticleViewerComponents {
102
+ /**
103
+ * Override for `note` callout components.
104
+ */
105
+ note?: ComponentType<CalloutProps>;
106
+ /**
107
+ * Override for `reference` callout components.
108
+ */
109
+ reference?: ComponentType<CalloutProps>;
110
+ /**
111
+ * Override for `warning` callout components.
112
+ */
113
+ warning?: ComponentType<CalloutProps>;
114
+ /**
115
+ * Override for `info` callout components.
116
+ */
117
+ info?: ComponentType<CalloutProps>;
118
+ /**
119
+ * Override for the FrontmatterHeader component rendered above article content.
120
+ * Receives the same `FrontmatterHeaderProps` as the default implementation.
121
+ */
122
+ FrontmatterHeader?: ComponentType<FrontmatterHeaderProps>;
123
+ }
124
+ /** Supported callout directive types */
125
+ export type CalloutType = 'note' | 'reference' | 'warning' | 'info';
126
+ /** Props passed to each callout component */
127
+ export interface CalloutProps {
128
+ /** The directive type (note, reference, warning, info) */
129
+ type: CalloutType;
130
+ /** Rendered markdown content inside the callout */
131
+ children?: ReactNode;
132
+ /** Additional CSS class names */
133
+ className?: string;
134
+ }
135
+ /**
136
+ * Format of the incoming content string.
137
+ * - `"markdown"`: Always treat as markdown (render via remark pipeline)
138
+ * - `"html"`: Treat as pre-rendered HTML (renders as-is with dangerouslySetInnerHTML)
139
+ * - `"auto"`: Detect based on content heuristics (default)
140
+ */
141
+ export type ContentFormat = 'markdown' | 'html' | 'auto';
142
+ /**
143
+ * Map of callout type → custom component.
144
+ * Allows Portal (and other consumers) to override default callout renderers.
145
+ *
146
+ * @deprecated Prefer `ArticleViewerComponents` which also supports `FrontmatterHeader` override.
147
+ *
148
+ * @example
149
+ * ```tsx
150
+ * <ArticleViewer
151
+ * content={markdown}
152
+ * components={{
153
+ * note: MyCustomNoteCallout,
154
+ * warning: MyWarningBanner,
155
+ * }}
156
+ * />
157
+ * ```
158
+ */
159
+ export type CalloutComponents = Partial<Record<CalloutType, ComponentType<CalloutProps>>>;
160
+ /**
161
+ * Props for the ArticleViewer component.
162
+ *
163
+ * @example
164
+ * ```tsx
165
+ * <ArticleViewer
166
+ * content="# Hello\n\n::: note\nThis is a note\n:::"
167
+ * format="markdown"
168
+ * variant="editorial"
169
+ * frontmatter="show"
170
+ * onError={(err) => console.error(err)}
171
+ * />
172
+ * ```
173
+ */
174
+ export interface ArticleViewerProps {
175
+ /**
176
+ * The raw content string to render.
177
+ * For markdown, the full remark pipeline (GFM + directives) is applied.
178
+ * YAML frontmatter (if present) is extracted and not rendered as content.
179
+ */
180
+ content: string;
181
+ /**
182
+ * Format of the content. Defaults to `"auto"` which detects markdown
183
+ * by looking for common markdown patterns.
184
+ * @default "auto"
185
+ */
186
+ format?: ContentFormat;
187
+ /**
188
+ * Typography variant. Applies a CSS class (`cv-variant-{name}`) to the root
189
+ * element. Each variant relies on CSS custom properties at document root.
190
+ * See the CSS-Variable Contract in the content-viewer README for the full
191
+ * list of expected `--cv-{variant}-*` variables.
192
+ * @default undefined (no variant class applied)
193
+ */
194
+ variant?: ArticleVariant;
195
+ /**
196
+ * Controls visibility of the YAML frontmatter header above article content.
197
+ * - `"show"` — header rendered, expanded by default
198
+ * - `"collapse"` — header rendered, collapsed by default
199
+ * - `"hide"` — header omitted entirely
200
+ * Has no effect if the content has no frontmatter.
201
+ * @default "hide"
202
+ */
203
+ frontmatter?: FrontmatterDisplayMode;
204
+ /**
205
+ * Override map for sub-components rendered by ArticleViewer.
206
+ * - Callout keys (`note`, `reference`, `warning`, `info`): override default callout renderers
207
+ * - `FrontmatterHeader`: override the default frontmatter header component
208
+ *
209
+ * Any key not provided falls back to the default implementation.
210
+ */
211
+ components?: ArticleViewerComponents;
212
+ /**
213
+ * @deprecated Use `components` instead. Kept for single-component override
214
+ * compatibility. If `components.note` etc. are also provided, those take precedence.
215
+ */
216
+ calloutComponent?: ComponentType<CalloutProps>;
217
+ /**
218
+ * Whether to sanitize HTML content (applies to `format="html"` and auto-detected HTML).
219
+ *
220
+ * When `true` (default for HTML input), `rehype-sanitize@6` strips XSS vectors:
221
+ * `<script>`, event handlers (`onclick`, `onerror`, …), `javascript:` and unsafe
222
+ * `data:` URLs, `<iframe>`, `<object>`, `<embed>`, `<svg>` containing scripts,
223
+ * and CSS `expression()` / `javascript:` values.
224
+ *
225
+ * Set to `false` **only** when the HTML source is fully trusted (e.g. content
226
+ * compiled by the MeatyWiki engine through the Portal's controlled pipeline).
227
+ * Never set `false` for user-supplied or third-party content.
228
+ *
229
+ * Has no effect on markdown input (the remark pipeline never emits raw HTML
230
+ * unless `allowDangerousHtml` is explicitly set, which this component does not do).
231
+ *
232
+ * @default true (for format="html" / auto-detected HTML), false (for format="markdown")
233
+ */
234
+ sanitize?: boolean;
235
+ /**
236
+ * Whether to use `isomorphic-dompurify` as the HTML sanitizer instead of the
237
+ * default `rehype-sanitize`.
238
+ *
239
+ * `isomorphic-dompurify` is an **optional peer dependency** — it is NOT bundled
240
+ * with `@miethe/ui`. Consumers that want this path must install it separately:
241
+ * ```
242
+ * npm install isomorphic-dompurify
243
+ * ```
244
+ * If the package is unavailable at runtime, ArticleViewer logs a warning and
245
+ * falls back to `rehype-sanitize`. The `sanitize` prop must also be `true`
246
+ * for this prop to have any effect.
247
+ *
248
+ * @default false
249
+ */
250
+ useDOMPurify?: boolean;
251
+ /**
252
+ * Enable opt-in syntax highlighting for fenced code blocks.
253
+ *
254
+ * When `false` (default), code blocks render as styled monospace plain text —
255
+ * zero additional bundle cost.
256
+ *
257
+ * When `true`, the component dynamically imports `lowlight` on first use and
258
+ * applies highlight.js-compatible hast transformations to `pre > code` blocks.
259
+ * `lowlight` is an **optional peer dependency** (~15KB gzip); install separately:
260
+ * ```
261
+ * npm install lowlight
262
+ * ```
263
+ * On the first render with a cold cache the highlighter loads asynchronously;
264
+ * code renders as plain text until the next render. Call `warmHighlightCache()`
265
+ * at app startup to eliminate this delay.
266
+ *
267
+ * To style highlighted code, add a highlight.js CSS theme in your app:
268
+ * ```
269
+ * import 'highlight.js/styles/github.css';
270
+ * ```
271
+ *
272
+ * @default false
273
+ */
274
+ codeHighlight?: boolean;
275
+ /**
276
+ * Automatically generate `id` attributes on heading elements (h1–h6)
277
+ * using a GitHub-compatible slug algorithm.
278
+ *
279
+ * Generated IDs allow in-page anchor navigation (`#section-title`).
280
+ * The `id` attribute is preserved by the sanitization schema for HTML input.
281
+ *
282
+ * When `false`, headings are rendered without `id` attributes.
283
+ *
284
+ * @default true
285
+ */
286
+ generateHeadingIds?: boolean;
287
+ /**
288
+ * When `true`, suppress all content rendering and show an accessible
289
+ * skeleton/placeholder instead. The `onError` callback is NOT invoked
290
+ * while loading is active.
291
+ *
292
+ * @default false
293
+ */
294
+ isLoading?: boolean;
295
+ /**
296
+ * Render an accessible error message instead of content.
297
+ * Accepts either a `string` message or an `Error` object.
298
+ *
299
+ * Priority: `error` prop > error boundary caught error > normal render.
300
+ * Does not crash the component; the error boundary still catches child throws.
301
+ *
302
+ * @default undefined
303
+ */
304
+ error?: string | Error | null;
305
+ /**
306
+ * Callback invoked when a rendering error occurs.
307
+ * Receives the caught Error object.
308
+ */
309
+ onError?: (error: Error) => void;
310
+ /**
311
+ * Additional CSS class names applied to the root wrapper element.
312
+ */
313
+ className?: string;
314
+ }
315
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/content-viewer/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @miethe/ui — ArticleViewer type definitions
3
+ *
4
+ * All public types exported from the content-viewer component.
5
+ * No `any` types at module boundaries.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/components/content-viewer/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @miethe/ui — ArticleViewer CSS-variable variant system (PU2-05)
3
+ *
4
+ * Maps `ArticleVariant` values to Tailwind-compatible CSS class names.
5
+ * Each class is expected to read CSS custom properties from document root,
6
+ * which the consumer (e.g., Portal `globals.css`) must define.
7
+ *
8
+ * ## CSS-Variable Contract
9
+ *
10
+ * When variant="editorial", apply the class `cv-variant-editorial` and
11
+ * define these variables at `:root` (or a suitable ancestor):
12
+ *
13
+ * ```css
14
+ * :root {
15
+ * --cv-editorial-h1-font: Fraunces, Georgia, serif;
16
+ * --cv-editorial-h1-size: 2.25rem;
17
+ * --cv-editorial-h2-font: Fraunces, Georgia, serif;
18
+ * --cv-editorial-h2-size: 1.875rem;
19
+ * --cv-editorial-body-font: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
20
+ * --cv-editorial-body-size: 1rem;
21
+ * --cv-editorial-body-line-height: 1.75;
22
+ * --cv-editorial-quote-color: #64748b;
23
+ * --cv-editorial-quote-font-style: italic;
24
+ * --cv-callout-note-accent: #0ea5e9;
25
+ * --cv-callout-note-bg: #f0f9ff;
26
+ * --cv-callout-reference-accent: #64748b;
27
+ * --cv-callout-reference-bg: #f1f5f9;
28
+ * --cv-callout-warning-accent: #f59e0b;
29
+ * --cv-callout-warning-bg: #fffbeb;
30
+ * --cv-callout-info-accent: #0ea5e9;
31
+ * --cv-callout-info-bg: #f0f9ff;
32
+ * }
33
+ * ```
34
+ *
35
+ * Analogous `--cv-compact-*` and `--cv-technical-*` sets follow the same pattern.
36
+ * Missing variables are silently ignored — browser defaults apply.
37
+ *
38
+ * ## Light / Dark Mode
39
+ *
40
+ * Variables should be declared inside appropriate selectors at the consumer side:
41
+ *
42
+ * ```css
43
+ * :root { --cv-editorial-body-font: "Libre Baskerville", serif; }
44
+ * .dark { --cv-editorial-body-font: "Libre Baskerville", serif; }
45
+ * ```
46
+ *
47
+ * The component itself applies no color values — all theming is delegated to the consumer.
48
+ */
49
+ import type { ArticleVariant, VariantTokenShape } from './types';
50
+ /**
51
+ * Mapping from `ArticleVariant` → the CSS class applied to the root element.
52
+ * The class is expected to read CSS custom properties from document root.
53
+ */
54
+ export declare const VARIANT_CLASSES: Record<ArticleVariant, string>;
55
+ /**
56
+ * Returns the CSS custom property names expected for a given variant.
57
+ * Use this as documentation / tooling support — it does not read or set variables.
58
+ *
59
+ * @param variant - The `ArticleVariant` to query
60
+ * @returns An object whose values are the expected `--cv-*` variable names
61
+ */
62
+ export declare function getVariantTokenNames(variant: ArticleVariant): VariantTokenShape;
63
+ /**
64
+ * Returns the CSS class name to apply for a given variant,
65
+ * or `undefined` when no variant is specified.
66
+ *
67
+ * @param variant - Optional `ArticleVariant`
68
+ * @returns CSS class string, or `undefined`
69
+ */
70
+ export declare function variantClass(variant: ArticleVariant | undefined): string | undefined;
71
+ //# sourceMappingURL=variants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variants.d.ts","sourceRoot":"","sources":["../../../src/components/content-viewer/variants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAMjE;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAI1D,CAAC;AAMF;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,cAAc,GAAG,iBAAiB,CAqB/E;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGpF"}