@cloudvoyant/helix-svelte 0.15.0 → 0.16.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.
@@ -0,0 +1,92 @@
1
+ <!-- libs/helix-svelte/src/Mermaid.svelte -->
2
+ <!-- Closely based on: diffbook Mermaid (packages/diffbook-ui/src/components/Mermaid.tsx placeholder +
3
+ apps/book/src/components/article-body.tsx client renderer), mirrored from @cloudvoyant/helix-react -->
4
+ <script lang="ts">
5
+ import { Ark } from '@ark-ui/svelte/factory';
6
+ import {
7
+ mermaidRootBase,
8
+ mermaidSourceBase,
9
+ mermaidSvgBase,
10
+ mermaidLoadingBase,
11
+ renderMermaidSource,
12
+ mermaidSvgAspectRatio,
13
+ cn,
14
+ } from '@cloudvoyant/helix';
15
+ import type { HTMLAttributes } from 'svelte/elements';
16
+
17
+ type Props = {
18
+ /** Mermaid diagram source. */
19
+ code: string;
20
+ /** Pre-rendered SVG markup. When provided the component renders it immediately and never
21
+ * imports `mermaid` client-side — the server-side rendering path. The SVG should carry a
22
+ * `viewBox` so its aspect ratio can be reserved (no layout shift). */
23
+ svg?: string;
24
+ /** Extra classes for the root container. */
25
+ class?: string;
26
+ } & HTMLAttributes<HTMLDivElement>;
27
+
28
+ let { code, svg = undefined, class: className = '', ...rest }: Props = $props();
29
+
30
+ type Status = 'loading' | 'done' | 'error';
31
+ let status = $state<Status>(svg !== undefined ? 'done' : 'loading');
32
+ let rendered = $state<string | null>(svg ?? null);
33
+
34
+ $effect(() => {
35
+ if (svg !== undefined) return; // prerendered — nothing to load client-side
36
+ let cancelled = false;
37
+ status = 'loading';
38
+ rendered = null;
39
+ renderMermaidSource(code)
40
+ .then((markup) => {
41
+ if (!cancelled) {
42
+ rendered = markup;
43
+ status = 'done';
44
+ }
45
+ })
46
+ .catch(() => {
47
+ if (!cancelled) status = 'error';
48
+ });
49
+ return () => {
50
+ cancelled = true;
51
+ };
52
+ });
53
+
54
+ const aspectRatio = $derived(rendered !== null ? mermaidSvgAspectRatio(rendered) : undefined);
55
+ const classes = $derived(cn(mermaidRootBase, className));
56
+ // Svelte style is a string; append aspect-ratio last so the diagram's ratio wins over
57
+ // any caller style and the swap never shifts layout (matches the React wrapper).
58
+ const styles = $derived(
59
+ aspectRatio !== undefined
60
+ ? `${rest.style ? `${rest.style.trim().replace(/;?\s*$/, '')};` : ''}aspect-ratio: ${aspectRatio};`
61
+ : rest.style,
62
+ );
63
+ </script>
64
+
65
+ <Ark
66
+ as="div"
67
+ {...rest}
68
+ data-mermaid-code={JSON.stringify(code)}
69
+ data-mermaid-src={rendered !== null ? code : undefined}
70
+ data-mermaid-state={status}
71
+ style={styles}
72
+ class={classes}
73
+ >
74
+ {#if rendered !== null}
75
+ <div class={mermaidSvgBase}>{@html rendered}</div>
76
+ {:else if status === 'error'}
77
+ <pre class={mermaidSourceBase}>{code}</pre>
78
+ {:else}
79
+ <div class={mermaidLoadingBase} role="status" aria-label="Rendering diagram" data-mermaid-loading>
80
+ <span
81
+ aria-hidden="true"
82
+ class="h-5 w-5 shrink-0 animate-spin rounded-full border-2 border-muted-foreground/25 border-t-muted-foreground"></span>
83
+ <span class="text-sm text-muted-foreground">Rendering diagram</span>
84
+ </div>
85
+ <noscript>
86
+ <!-- Only active with JS disabled: hide the loading spinner, which can never resolve,
87
+ and show the raw source instead. -->
88
+ <style>[data-mermaid-loading]{display:none}</style>
89
+ <pre class={mermaidSourceBase}>{code}</pre>
90
+ </noscript>
91
+ {/if}
92
+ </Ark>
@@ -0,0 +1,14 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ type Props = {
3
+ /** Mermaid diagram source. */
4
+ code: string;
5
+ /** Pre-rendered SVG markup. When provided the component renders it immediately and never
6
+ * imports `mermaid` client-side — the server-side rendering path. The SVG should carry a
7
+ * `viewBox` so its aspect ratio can be reserved (no layout shift). */
8
+ svg?: string;
9
+ /** Extra classes for the root container. */
10
+ class?: string;
11
+ } & HTMLAttributes<HTMLDivElement>;
12
+ declare const Mermaid: import("svelte").Component<Props, {}, "">;
13
+ type Mermaid = ReturnType<typeof Mermaid>;
14
+ export default Mermaid;
package/dist/index.d.ts CHANGED
@@ -39,3 +39,4 @@ export * from './switch';
39
39
  export * from './select';
40
40
  export * from './combobox';
41
41
  export * from './tags-input';
42
+ export { default as Mermaid } from './Mermaid.svelte';
package/dist/index.js CHANGED
@@ -40,3 +40,4 @@ export * from './switch';
40
40
  export * from './select';
41
41
  export * from './combobox';
42
42
  export * from './tags-input';
43
+ export { default as Mermaid } from './Mermaid.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudvoyant/helix-svelte",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "helix Svelte UI components",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,15 +23,22 @@
23
23
  "access": "public"
24
24
  },
25
25
  "peerDependencies": {
26
- "svelte": "^5.56.9"
26
+ "svelte": "^5.56.9",
27
+ "mermaid": "^11.16.0"
28
+ },
29
+ "peerDependenciesMeta": {
30
+ "mermaid": {
31
+ "optional": true
32
+ }
27
33
  },
28
34
  "dependencies": {
29
35
  "@ark-ui/svelte": "^5.23.1",
30
- "@cloudvoyant/helix": "0.15.0"
36
+ "@cloudvoyant/helix": "0.16.0"
31
37
  },
32
38
  "devDependencies": {
33
39
  "@sveltejs/package": "^2.5.8",
34
40
  "@sveltejs/vite-plugin-svelte": "^4.0.0",
41
+ "mermaid": "^11.16.0",
35
42
  "prettier-plugin-svelte": "^4.1.1",
36
43
  "svelte": "^5.56.9",
37
44
  "svelte-check": "^4.7.6",