@refrakt-md/svelte 0.4.0 → 0.5.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@refrakt-md/svelte",
3
3
  "description": "Svelte renderer for refrakt.md content",
4
- "version": "0.4.0",
4
+ "version": "0.5.1",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -27,7 +27,8 @@
27
27
  ],
28
28
  "dependencies": {
29
29
  "@markdoc/markdoc": "0.4.0",
30
- "@refrakt-md/types": "0.4.0"
30
+ "@refrakt-md/behaviors": "0.5.1",
31
+ "@refrakt-md/types": "0.5.1"
31
32
  },
32
33
  "peerDependencies": {
33
34
  "svelte": "^5.0.0"
@@ -24,6 +24,25 @@
24
24
  return result;
25
25
  }
26
26
 
27
+ function escapeAttr(str: string): string {
28
+ return str.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
29
+ }
30
+
31
+ /** Serialize an SVG tag tree to HTML string. Using {@html} avoids SVG namespace
32
+ * issues that occur when <svelte:element> creates child SVG elements (path, circle, etc.)
33
+ * outside the SVG namespace context during client-side navigation. */
34
+ function svgToHtml(tag: SerializedTag): string {
35
+ const attrs = Object.entries(htmlAttrs(tag.attributes))
36
+ .map(([k, v]) => ` ${k}="${escapeAttr(v)}"`)
37
+ .join('');
38
+ const children = tag.children.map(child => {
39
+ if (typeof child === 'string') return escapeAttr(child);
40
+ if (isTag(child)) return svgToHtml(child);
41
+ return '';
42
+ }).join('');
43
+ return `<${tag.name}${attrs}>${children}</${tag.name}>`;
44
+ }
45
+
27
46
  const globalOverrides = getElementOverrides();
28
47
  const merged = $derived(overrides
29
48
  ? { ...globalOverrides, ...overrides }
@@ -55,6 +74,8 @@
55
74
  <Renderer node={child} overrides={merged} />
56
75
  {/each}
57
76
  </ElementOverride>
77
+ {:else if node.name === 'svg'}
78
+ {@html svgToHtml(node)}
58
79
  {:else}
59
80
  <svelte:element this={node.name} {...htmlAttrs(node.attributes)}>
60
81
  {#each node.children as child}
@@ -1,8 +1,9 @@
1
1
  <script lang="ts">
2
2
  import type { SvelteTheme } from './theme.js';
3
3
  import { setRegistry, setElementOverrides } from './context.js';
4
- import { setContext } from 'svelte';
4
+ import { setContext, tick } from 'svelte';
5
5
  import { matchRouteRule } from './route-rules.js';
6
+ import { initRuneBehaviors } from '@refrakt-md/behaviors';
6
7
 
7
8
  interface OgMeta {
8
9
  title?: string;
@@ -50,6 +51,23 @@
50
51
  // Pick layout via route rules (reactive so layout updates on client-side navigation)
51
52
  const layoutName = $derived(matchRouteRule(page.url, theme.manifest.routeRules));
52
53
  const Layout = $derived(theme.layouts[layoutName] ?? theme.layouts['default']);
54
+
55
+ // Initialize rune behaviors after render, re-run on navigation.
56
+ // The {#key page.url} block in the template ensures full DOM recreation on
57
+ // navigation, so behaviors always run on fresh DOM and old behavior-modified
58
+ // elements are simply discarded (no cleanup/restore conflicts with Svelte).
59
+ $effect(() => {
60
+ void page.url; // re-run when page changes
61
+ let cleanup: (() => void) | undefined;
62
+ let active = true;
63
+ tick().then(() => {
64
+ if (active) cleanup = initRuneBehaviors();
65
+ });
66
+ return () => {
67
+ active = false;
68
+ cleanup?.();
69
+ };
70
+ });
53
71
  </script>
54
72
 
55
73
  <svelte:head>
@@ -82,13 +100,16 @@
82
100
  {/if}
83
101
  </svelte:head>
84
102
 
85
- {#if Layout}
86
- <Layout
87
- title={page.title}
88
- description={page.description}
89
- frontmatter={page.frontmatter}
90
- regions={page.regions}
91
- renderable={page.renderable}
92
- pages={page.pages}
93
- />
94
- {/if}
103
+ {#key page.url}
104
+ {#if Layout}
105
+ <Layout
106
+ title={page.title}
107
+ description={page.description}
108
+ frontmatter={page.frontmatter}
109
+ regions={page.regions}
110
+ renderable={page.renderable}
111
+ pages={page.pages}
112
+ url={page.url}
113
+ />
114
+ {/if}
115
+ {/key}