@miethe/ui 0.3.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 (168) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +776 -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/Card.d.ts +28 -0
  130. package/dist/primitives/Card.d.ts.map +1 -0
  131. package/dist/primitives/Card.js +30 -0
  132. package/dist/primitives/Card.js.map +1 -0
  133. package/dist/primitives/CollectionPicker.d.ts +47 -0
  134. package/dist/primitives/CollectionPicker.d.ts.map +1 -0
  135. package/dist/primitives/CollectionPicker.js +105 -0
  136. package/dist/primitives/CollectionPicker.js.map +1 -0
  137. package/dist/primitives/CreateEntityDialog.d.ts +144 -0
  138. package/dist/primitives/CreateEntityDialog.d.ts.map +1 -0
  139. package/dist/primitives/CreateEntityDialog.js +379 -0
  140. package/dist/primitives/CreateEntityDialog.js.map +1 -0
  141. package/dist/primitives/FormField.d.ts +29 -0
  142. package/dist/primitives/FormField.d.ts.map +1 -0
  143. package/dist/primitives/FormField.js +27 -0
  144. package/dist/primitives/FormField.js.map +1 -0
  145. package/dist/primitives/Label.d.ts +20 -0
  146. package/dist/primitives/Label.d.ts.map +1 -0
  147. package/dist/primitives/Label.js +21 -0
  148. package/dist/primitives/Label.js.map +1 -0
  149. package/dist/primitives/SecretField.d.ts +28 -0
  150. package/dist/primitives/SecretField.d.ts.map +1 -0
  151. package/dist/primitives/SecretField.js +65 -0
  152. package/dist/primitives/SecretField.js.map +1 -0
  153. package/dist/primitives/Spinner.d.ts +16 -0
  154. package/dist/primitives/Spinner.d.ts.map +1 -0
  155. package/dist/primitives/Spinner.js +34 -0
  156. package/dist/primitives/Spinner.js.map +1 -0
  157. package/dist/primitives/Switch.d.ts +32 -0
  158. package/dist/primitives/Switch.d.ts.map +1 -0
  159. package/dist/primitives/Switch.js +43 -0
  160. package/dist/primitives/Switch.js.map +1 -0
  161. package/dist/primitives/index.d.ts +16 -0
  162. package/dist/primitives/index.d.ts.map +1 -1
  163. package/dist/primitives/index.js +9 -0
  164. package/dist/primitives/index.js.map +1 -1
  165. package/dist/utils/type-colors.d.ts.map +1 -1
  166. package/dist/utils/type-colors.js +4 -0
  167. package/dist/utils/type-colors.js.map +1 -1
  168. package/package.json +40 -6
@@ -0,0 +1,79 @@
1
+ /**
2
+ * rehypeExternalLinks — shared rehype plugin for external link hardening (PU3-04)
3
+ *
4
+ * Adds `target="_blank"` and `rel="noopener noreferrer"` to every `<a>` element
5
+ * whose `href` is an absolute URL with an http, https, or mailto scheme.
6
+ *
7
+ * This plugin is intentionally written as a pure unified/rehype tree visitor so
8
+ * it works on both the markdown pipeline (via ReactMarkdown's `rehypePlugins`)
9
+ * and the HTML pipeline (unified + rehypeParse + this plugin + rehype-sanitize
10
+ * + rehypeStringify). A single implementation avoids per-component duplication.
11
+ *
12
+ * Scheme detection uses a strict allowlist: `http:`, `https:`, `mailto:`.
13
+ * Protocol-relative URLs (`//example.com`) are also treated as external.
14
+ *
15
+ * Note: The markdown path also uses the `components.a` override in
16
+ * `buildComponentMap()` for link hardening because ReactMarkdown does not
17
+ * thread rehype node attributes through to rendered anchors. The `components.a`
18
+ * override is kept for the markdown path; this plugin is the canonical
19
+ * implementation used for the HTML path and can be tested in isolation.
20
+ */
21
+ import { visit } from 'unist-util-visit';
22
+ // ---------------------------------------------------------------------------
23
+ // Helper
24
+ // ---------------------------------------------------------------------------
25
+ // Schemes treated as "external" — anything else (relative, #anchor, data:, etc.)
26
+ // is left unmodified.
27
+ const EXTERNAL_SCHEMES = new Set(['http:', 'https:', 'mailto:']);
28
+ /**
29
+ * Returns true if `href` should be treated as an external link.
30
+ * Protocol-relative URLs (starting with `//`) are also considered external.
31
+ */
32
+ export function isExternalHref(href) {
33
+ if (href.startsWith('//'))
34
+ return true;
35
+ try {
36
+ const url = new URL(href);
37
+ return EXTERNAL_SCHEMES.has(url.protocol);
38
+ }
39
+ catch {
40
+ // Not an absolute URL — treat as relative/internal
41
+ return false;
42
+ }
43
+ }
44
+ // ---------------------------------------------------------------------------
45
+ // Plugin
46
+ // ---------------------------------------------------------------------------
47
+ /**
48
+ * Unified/rehype plugin that adds `target="_blank"` and
49
+ * `rel="noopener noreferrer"` to external `<a>` elements.
50
+ *
51
+ * Usage (HTML pipeline):
52
+ * ```ts
53
+ * unified()
54
+ * .use(rehypeParse, { fragment: true })
55
+ * .use(rehypeExternalLinks)
56
+ * .use(rehypeSanitize) // sanitize AFTER link attrs are set (allowlist includes rel/target)
57
+ * .use(rehypeStringify)
58
+ * .process(htmlString);
59
+ * ```
60
+ */
61
+ export const rehypeExternalLinks = () => {
62
+ return (tree) => {
63
+ visit(tree, 'element', (node) => {
64
+ const el = node;
65
+ if (el.tagName !== 'a')
66
+ return;
67
+ const href = el.properties?.['href'];
68
+ if (typeof href !== 'string')
69
+ return;
70
+ if (isExternalHref(href)) {
71
+ el.properties = el.properties ?? {};
72
+ el.properties['target'] = '_blank';
73
+ el.properties['rel'] = 'noopener noreferrer';
74
+ }
75
+ });
76
+ };
77
+ };
78
+ export default rehypeExternalLinks;
79
+ //# sourceMappingURL=rehypeExternalLinks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rehypeExternalLinks.js","sourceRoot":"","sources":["../../../../src/components/content-viewer/plugins/rehypeExternalLinks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAwBzC,8EAA8E;AAC9E,SAAS;AACT,8EAA8E;AAE9E,iFAAiF;AACjF,sBAAsB;AACtB,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;AAEjE;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1B,OAAO,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,mDAAmD;QACnD,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,SAAS;AACT,8EAA8E;AAE9E;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAyB,GAA0B,EAAE;IACnF,OAAO,CAAC,IAAc,EAAE,EAAE;QACxB,KAAK,CAAC,IAAmC,EAAE,SAAS,EAAE,CAAC,IAAc,EAAE,EAAE;YACvE,MAAM,EAAE,GAAG,IAAmB,CAAC;YAC/B,IAAI,EAAE,CAAC,OAAO,KAAK,GAAG;gBAAE,OAAO;YAE/B,MAAM,IAAI,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,MAAM,CAAC,CAAC;YACrC,IAAI,OAAO,IAAI,KAAK,QAAQ;gBAAE,OAAO;YAErC,IAAI,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzB,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,UAAU,IAAI,EAAE,CAAC;gBACpC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;gBACnC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,qBAAqB,CAAC;YAC/C,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * rehypeHeadingIds — auto-generate `id` attributes on heading elements.
3
+ *
4
+ * Implements OQ-UCV-D / A-UCV-08 (generateHeadingIds prop, default true).
5
+ *
6
+ * Algorithm:
7
+ * 1. Walk the hast tree for h1–h6 elements.
8
+ * 2. Extract plain text content by recursively collecting Text node values.
9
+ * 3. Slugify the text using a GitHub-compatible slug algorithm:
10
+ * - Lowercase
11
+ * - Replace non-alphanumeric (non-space) chars with nothing
12
+ * - Replace spaces with hyphens
13
+ * - Strip leading/trailing hyphens
14
+ * 4. Deduplicate: if the slug already exists, append `-1`, `-2`, … until unique.
15
+ * 5. Set `id` on the element (skipping elements that already have an `id`).
16
+ *
17
+ * The deduplication counter is per-document (plugin instance). Each page render
18
+ * creates a fresh plugin instance via `createHeadingIdsPlugin()`.
19
+ *
20
+ * @module rehypeHeadingIds
21
+ */
22
+ import type { Root } from 'hast';
23
+ export { slugify } from './slugify';
24
+ /**
25
+ * Create a rehype plugin that adds `id` attributes to h1–h6 elements.
26
+ *
27
+ * A fresh plugin instance (and thus a fresh deduplication map) must be created
28
+ * for each render to avoid cross-render contamination.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * const headingIdsPlugin = createHeadingIdsPlugin();
33
+ * // pass to ReactMarkdown rehypePlugins
34
+ * ```
35
+ */
36
+ export declare function createHeadingIdsPlugin(): () => (tree: Root) => void;
37
+ //# sourceMappingURL=rehypeHeadingIds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rehypeHeadingIds.d.ts","sourceRoot":"","sources":["../../../../src/components/content-viewer/plugins/rehypeHeadingIds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAA8C,MAAM,MAAM,CAAC;AAI7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2BpC;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAwBnE"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * rehypeHeadingIds — auto-generate `id` attributes on heading elements.
3
+ *
4
+ * Implements OQ-UCV-D / A-UCV-08 (generateHeadingIds prop, default true).
5
+ *
6
+ * Algorithm:
7
+ * 1. Walk the hast tree for h1–h6 elements.
8
+ * 2. Extract plain text content by recursively collecting Text node values.
9
+ * 3. Slugify the text using a GitHub-compatible slug algorithm:
10
+ * - Lowercase
11
+ * - Replace non-alphanumeric (non-space) chars with nothing
12
+ * - Replace spaces with hyphens
13
+ * - Strip leading/trailing hyphens
14
+ * 4. Deduplicate: if the slug already exists, append `-1`, `-2`, … until unique.
15
+ * 5. Set `id` on the element (skipping elements that already have an `id`).
16
+ *
17
+ * The deduplication counter is per-document (plugin instance). Each page render
18
+ * creates a fresh plugin instance via `createHeadingIdsPlugin()`.
19
+ *
20
+ * @module rehypeHeadingIds
21
+ */
22
+ import { visit } from 'unist-util-visit';
23
+ import { slugify } from './slugify';
24
+ export { slugify } from './slugify';
25
+ // ---------------------------------------------------------------------------
26
+ // Slug helpers
27
+ // ---------------------------------------------------------------------------
28
+ const HEADING_TAGS = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']);
29
+ /**
30
+ * Extract plain text from a hast node by recursively collecting text nodes.
31
+ */
32
+ function extractText(nodes) {
33
+ let text = '';
34
+ for (const node of nodes) {
35
+ if (node.type === 'text') {
36
+ text += node.value;
37
+ }
38
+ else if ('children' in node && Array.isArray(node.children)) {
39
+ text += extractText(node.children);
40
+ }
41
+ }
42
+ return text;
43
+ }
44
+ // ---------------------------------------------------------------------------
45
+ // Plugin factory
46
+ // ---------------------------------------------------------------------------
47
+ /**
48
+ * Create a rehype plugin that adds `id` attributes to h1–h6 elements.
49
+ *
50
+ * A fresh plugin instance (and thus a fresh deduplication map) must be created
51
+ * for each render to avoid cross-render contamination.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * const headingIdsPlugin = createHeadingIdsPlugin();
56
+ * // pass to ReactMarkdown rehypePlugins
57
+ * ```
58
+ */
59
+ export function createHeadingIdsPlugin() {
60
+ return function rehypeHeadingIdsPlugin() {
61
+ return function transformer(tree) {
62
+ const seen = new Map();
63
+ visit(tree, 'element', (node) => {
64
+ if (!HEADING_TAGS.has(node.tagName))
65
+ return;
66
+ // Skip elements that already have an id
67
+ if (node.properties?.id)
68
+ return;
69
+ const rawText = extractText(node.children);
70
+ const base = slugify(rawText);
71
+ if (!base)
72
+ return;
73
+ // Deduplicate
74
+ const count = seen.get(base) ?? 0;
75
+ const id = count === 0 ? base : `${base}-${count}`;
76
+ seen.set(base, count + 1);
77
+ node.properties = { ...node.properties, id };
78
+ });
79
+ };
80
+ };
81
+ }
82
+ //# sourceMappingURL=rehypeHeadingIds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rehypeHeadingIds.js","sourceRoot":"","sources":["../../../../src/components/content-viewer/plugins/rehypeHeadingIds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,8EAA8E;AAC9E,eAAe;AACf,8EAA8E;AAE9E,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AAEnE;;GAEG;AACH,SAAS,WAAW,CAAC,KAAkD;IACrE,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACzB,IAAI,IAAK,IAAa,CAAC,KAAK,CAAC;QAC/B,CAAC;aAAM,IAAI,UAAU,IAAI,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9D,IAAI,IAAI,WAAW,CAAC,IAAI,CAAC,QAAyC,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO,SAAS,sBAAsB;QACpC,OAAO,SAAS,WAAW,CAAC,IAAU;YACpC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;YAEvC,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,IAAa,EAAE,EAAE;gBACvC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC;oBAAE,OAAO;gBAE5C,wCAAwC;gBACxC,IAAI,IAAI,CAAC,UAAU,EAAE,EAAE;oBAAE,OAAO;gBAEhC,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,QAAyC,CAAC,CAAC;gBAC5E,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;gBAC9B,IAAI,CAAC,IAAI;oBAAE,OAAO;gBAElB,cAAc;gBACd,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAClC,MAAM,EAAE,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,KAAK,EAAE,CAAC;gBACnD,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;gBAE1B,IAAI,CAAC,UAAU,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,EAAE,EAAE,EAAE,CAAC;YAC/C,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * remarkCallouts — custom remark plugin
3
+ *
4
+ * Transforms `:::type` container directives produced by `remark-directive`
5
+ * into custom HAST elements that ReactMarkdown's `components` map can render.
6
+ *
7
+ * Directive syntax:
8
+ * ```markdown
9
+ * ::: note
10
+ * Content here.
11
+ * :::
12
+ * ```
13
+ *
14
+ * The plugin visits `containerDirective` nodes whose `name` matches one of
15
+ * the recognised callout types (note, reference, warning, info). It rewrites
16
+ * the node in-place so that the mdast → hast bridge produces a custom element
17
+ * name (e.g. `callout-note`) which ReactMarkdown maps to a JSX component.
18
+ *
19
+ * Implementation note: remark-directive (v2) produces mdast nodes of type
20
+ * `containerDirective`. We traverse the tree manually to avoid importing
21
+ * `unist-util-visit` (not in the package's direct deps) while keeping the
22
+ * plugin zero-dependency beyond what remark-directive already provides.
23
+ */
24
+ /** Recognised callout directive names */
25
+ declare const CALLOUT_TYPES: Set<string>;
26
+ /**
27
+ * Remark plugin: walk the tree, find container directives that match a known
28
+ * callout type, and annotate them with hast properties so ReactMarkdown maps
29
+ * them to the correct callout component.
30
+ *
31
+ * ReactMarkdown uses `node.data.hName` as the element tag name and
32
+ * `node.data.hProperties` as the element's props. By setting `hName` to
33
+ * `"callout-{type}"` we can register a component for each type in the
34
+ * `components` map without name collisions with standard HTML elements.
35
+ */
36
+ declare function remarkCallouts(): (tree: Record<string, unknown>) => void;
37
+ export default remarkCallouts;
38
+ export { CALLOUT_TYPES };
39
+ //# sourceMappingURL=remarkCallouts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remarkCallouts.d.ts","sourceRoot":"","sources":["../../../../src/components/content-viewer/plugins/remarkCallouts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,yCAAyC;AACzC,QAAA,MAAM,aAAa,aAA4D,CAAC;AAyBhF;;;;;;;;;GASG;AACH,iBAAS,cAAc,KACb,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAAI,CAsB7C;AAED,eAAe,cAAc,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,CAAC"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * remarkCallouts — custom remark plugin
3
+ *
4
+ * Transforms `:::type` container directives produced by `remark-directive`
5
+ * into custom HAST elements that ReactMarkdown's `components` map can render.
6
+ *
7
+ * Directive syntax:
8
+ * ```markdown
9
+ * ::: note
10
+ * Content here.
11
+ * :::
12
+ * ```
13
+ *
14
+ * The plugin visits `containerDirective` nodes whose `name` matches one of
15
+ * the recognised callout types (note, reference, warning, info). It rewrites
16
+ * the node in-place so that the mdast → hast bridge produces a custom element
17
+ * name (e.g. `callout-note`) which ReactMarkdown maps to a JSX component.
18
+ *
19
+ * Implementation note: remark-directive (v2) produces mdast nodes of type
20
+ * `containerDirective`. We traverse the tree manually to avoid importing
21
+ * `unist-util-visit` (not in the package's direct deps) while keeping the
22
+ * plugin zero-dependency beyond what remark-directive already provides.
23
+ */
24
+ /** Recognised callout directive names */
25
+ const CALLOUT_TYPES = new Set(['note', 'reference', 'warning', 'info']);
26
+ /**
27
+ * Minimal recursive visitor — walks the mdast tree and invokes `visitor`
28
+ * for every node whose `type` matches `nodeType`.
29
+ */
30
+ function visitAll(tree, nodeType, visitor) {
31
+ if (!tree || typeof tree !== 'object')
32
+ return;
33
+ if (tree['type'] === nodeType) {
34
+ visitor(tree);
35
+ }
36
+ const children = tree['children'];
37
+ if (Array.isArray(children)) {
38
+ for (const child of children) {
39
+ visitAll(child, nodeType, visitor);
40
+ }
41
+ }
42
+ }
43
+ /**
44
+ * Remark plugin: walk the tree, find container directives that match a known
45
+ * callout type, and annotate them with hast properties so ReactMarkdown maps
46
+ * them to the correct callout component.
47
+ *
48
+ * ReactMarkdown uses `node.data.hName` as the element tag name and
49
+ * `node.data.hProperties` as the element's props. By setting `hName` to
50
+ * `"callout-{type}"` we can register a component for each type in the
51
+ * `components` map without name collisions with standard HTML elements.
52
+ */
53
+ function remarkCallouts() {
54
+ return (tree) => {
55
+ visitAll(tree, 'containerDirective', (node) => {
56
+ const name = node['name']?.toLowerCase();
57
+ if (!name || !CALLOUT_TYPES.has(name))
58
+ return;
59
+ const calloutType = name;
60
+ // Ensure data bag exists
61
+ if (!node['data'] || typeof node['data'] !== 'object') {
62
+ node['data'] = {};
63
+ }
64
+ const data = node['data'];
65
+ // Map to a custom element name that ReactMarkdown's components prop can target.
66
+ // Using lowercase kebab-case avoids conflicts with standard HTML elements.
67
+ data['hName'] = `callout-${calloutType}`;
68
+ data['hProperties'] = {
69
+ ...(data['hProperties'] ?? {}),
70
+ 'data-callout-type': calloutType,
71
+ };
72
+ });
73
+ };
74
+ }
75
+ export default remarkCallouts;
76
+ export { CALLOUT_TYPES };
77
+ //# sourceMappingURL=remarkCallouts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remarkCallouts.js","sourceRoot":"","sources":["../../../../src/components/content-viewer/plugins/remarkCallouts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,yCAAyC;AACzC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAS,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;AAEhF;;;GAGG;AACH,SAAS,QAAQ,CACf,IAA6B,EAC7B,QAAgB,EAChB,OAAgD;IAEhD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO;IAE9C,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;IAClC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;YAC7B,QAAQ,CAAC,KAAgC,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,cAAc;IACrB,OAAO,CAAC,IAA6B,EAAQ,EAAE;QAC7C,QAAQ,CAAC,IAAI,EAAE,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE;YAC5C,MAAM,IAAI,GAAI,IAAI,CAAC,MAAM,CAAwB,EAAE,WAAW,EAAE,CAAC;YACjE,IAAI,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,OAAO;YAE9C,MAAM,WAAW,GAAG,IAAmB,CAAC;YAExC,yBAAyB;YACzB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,QAAQ,EAAE,CAAC;gBACtD,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;YACpB,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAA4B,CAAC;YAErD,gFAAgF;YAChF,2EAA2E;YAC3E,IAAI,CAAC,OAAO,CAAC,GAAG,WAAW,WAAW,EAAE,CAAC;YACzC,IAAI,CAAC,aAAa,CAAC,GAAG;gBACpB,GAAG,CAAE,IAAI,CAAC,aAAa,CAA6B,IAAI,EAAE,CAAC;gBAC3D,mBAAmB,EAAE,WAAW;aACjC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED,eAAe,cAAc,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * GitHub-compatible heading slug utility.
3
+ *
4
+ * Exported separately from rehypeHeadingIds so consumers can:
5
+ * 1. Import slugify without pulling in the full hast/unist dependency graph.
6
+ * 2. Test the slug algorithm in Jest without ESM transformation issues.
7
+ *
8
+ * Algorithm:
9
+ * - Lowercase the text
10
+ * - Remove characters that are not word chars ([\w]), hyphens, or spaces
11
+ * - Replace whitespace runs with a single hyphen
12
+ * - Collapse multiple consecutive hyphens
13
+ * - Strip leading and trailing hyphens
14
+ */
15
+ /**
16
+ * Produce a GitHub-compatible URL slug from a heading text string.
17
+ *
18
+ * @example
19
+ * slugify('Hello World') // → 'hello-world'
20
+ * slugify('API Reference') // → 'api-reference'
21
+ * slugify('What is @miethe?') // → 'what-is-miethe'
22
+ */
23
+ export declare function slugify(text: string): string;
24
+ //# sourceMappingURL=slugify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slugify.d.ts","sourceRoot":"","sources":["../../../../src/components/content-viewer/plugins/slugify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO5C"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * GitHub-compatible heading slug utility.
3
+ *
4
+ * Exported separately from rehypeHeadingIds so consumers can:
5
+ * 1. Import slugify without pulling in the full hast/unist dependency graph.
6
+ * 2. Test the slug algorithm in Jest without ESM transformation issues.
7
+ *
8
+ * Algorithm:
9
+ * - Lowercase the text
10
+ * - Remove characters that are not word chars ([\w]), hyphens, or spaces
11
+ * - Replace whitespace runs with a single hyphen
12
+ * - Collapse multiple consecutive hyphens
13
+ * - Strip leading and trailing hyphens
14
+ */
15
+ /**
16
+ * Produce a GitHub-compatible URL slug from a heading text string.
17
+ *
18
+ * @example
19
+ * slugify('Hello World') // → 'hello-world'
20
+ * slugify('API Reference') // → 'api-reference'
21
+ * slugify('What is @miethe?') // → 'what-is-miethe'
22
+ */
23
+ export function slugify(text) {
24
+ return text
25
+ .toLowerCase()
26
+ .replace(/[^\w\s-]/g, '') // remove non-word, non-space, non-hyphen chars
27
+ .replace(/[\s]+/g, '-') // spaces → hyphens
28
+ .replace(/-{2,}/g, '-') // collapse multiple consecutive hyphens
29
+ .replace(/^-+|-+$/g, ''); // strip leading/trailing hyphens
30
+ }
31
+ //# sourceMappingURL=slugify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slugify.js","sourceRoot":"","sources":["../../../../src/components/content-viewer/plugins/slugify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,IAAI;SACR,WAAW,EAAE;SACb,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAG,+CAA+C;SAC1E,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAM,mBAAmB;SAC/C,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAM,wCAAwC;SACpE,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAI,iCAAiC;AAClE,CAAC"}
@@ -0,0 +1,75 @@
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 type { Options as SanitizeSchema } from 'rehype-sanitize';
43
+ export declare const ARTICLE_VIEWER_SCHEMA: SanitizeSchema;
44
+ /**
45
+ * Sanitize an HTML string using `rehype-sanitize@6`.
46
+ *
47
+ * Pipeline: rehypeParse → rehypeExternalLinks → rehypeSanitize → rehypeStringify
48
+ */
49
+ export declare function sanitizeWithRehype(html: string): string;
50
+ /**
51
+ * Sanitize an HTML string, preferring DOMPurify if available (async).
52
+ *
53
+ * On first call it may do a dynamic import; subsequent calls use the cache.
54
+ * If DOMPurify is unavailable, falls back to `sanitizeWithRehype`.
55
+ */
56
+ export declare function sanitizeWithDOMPurify(html: string): Promise<string>;
57
+ /**
58
+ * Synchronously sanitize `html`.
59
+ *
60
+ * When `useDOMPurify=true` and DOMPurify has already been loaded (warm cache),
61
+ * uses DOMPurify. If the cache is cold (first render), falls back to
62
+ * rehype-sanitize synchronously and triggers the async load in the background
63
+ * so subsequent renders can use DOMPurify.
64
+ */
65
+ export declare function sanitizeHtml(html: string, opts: {
66
+ useDOMPurify: boolean;
67
+ }): string;
68
+ /**
69
+ * Warm the DOMPurify cache. Call this once at app startup if you plan to use
70
+ * `useDOMPurify={true}` to avoid the rehype fallback on the first render.
71
+ */
72
+ export declare function warmDOMPurifyCache(): Promise<void>;
73
+ /** Exposed for testing — resets the module-level load state */
74
+ export declare function _resetDOMPurifyCache(): void;
75
+ //# sourceMappingURL=sanitize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../../src/components/content-viewer/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAMH,OAAO,KAAK,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAoCjE,eAAO,MAAM,qBAAqB,EAAE,cAiBnC,CAAC;AAmEF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGvD;AAqCD;;;;;GAKG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBzE;AAMD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IAAE,YAAY,EAAE,OAAO,CAAA;CAAE,GAC9B,MAAM,CAuBR;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAExD;AAED,+DAA+D;AAC/D,wBAAgB,oBAAoB,IAAI,IAAI,CAG3C"}