@cloudvoyant/helix-svelte 0.16.0 → 0.17.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 (63) hide show
  1. package/dist/Chart.svelte +52 -0
  2. package/dist/Chart.svelte.d.ts +22 -0
  3. package/dist/CopyButton.svelte +58 -0
  4. package/dist/CopyButton.svelte.d.ts +12 -0
  5. package/dist/Figure.svelte +32 -0
  6. package/dist/Figure.svelte.d.ts +14 -0
  7. package/dist/InlineLaTeX.svelte +34 -0
  8. package/dist/InlineLaTeX.svelte.d.ts +9 -0
  9. package/dist/LaTeX.svelte +34 -0
  10. package/dist/LaTeX.svelte.d.ts +9 -0
  11. package/dist/Manim.svelte +119 -0
  12. package/dist/Manim.svelte.d.ts +12 -0
  13. package/dist/Notice.svelte +87 -0
  14. package/dist/Notice.svelte.d.ts +12 -0
  15. package/dist/PrevNext.svelte +47 -0
  16. package/dist/PrevNext.svelte.d.ts +15 -0
  17. package/dist/Reveal.svelte +40 -0
  18. package/dist/Reveal.svelte.d.ts +9 -0
  19. package/dist/YouTube.svelte +85 -0
  20. package/dist/YouTube.svelte.d.ts +13 -0
  21. package/dist/YoutubeTimestampAt.svelte +20 -0
  22. package/dist/YoutubeTimestampAt.svelte.d.ts +10 -0
  23. package/dist/YoutubeTimestamps.svelte +100 -0
  24. package/dist/YoutubeTimestamps.svelte.d.ts +11 -0
  25. package/dist/code-block/CodeBlock.svelte +67 -0
  26. package/dist/code-block/CodeBlock.svelte.d.ts +17 -0
  27. package/dist/code-block/CodeBlockContent.svelte +19 -0
  28. package/dist/code-block/CodeBlockContent.svelte.d.ts +8 -0
  29. package/dist/code-block/CodeBlockContext.svelte.d.ts +11 -0
  30. package/dist/code-block/CodeBlockContext.svelte.js +7 -0
  31. package/dist/code-block/CodeBlockCopyButton.svelte +19 -0
  32. package/dist/code-block/CodeBlockCopyButton.svelte.d.ts +8 -0
  33. package/dist/code-block/CodeBlockHeader.svelte +15 -0
  34. package/dist/code-block/CodeBlockHeader.svelte.d.ts +9 -0
  35. package/dist/code-block/CodeBlockTitle.svelte +15 -0
  36. package/dist/code-block/CodeBlockTitle.svelte.d.ts +9 -0
  37. package/dist/code-block/CodeRenderer.svelte +78 -0
  38. package/dist/code-block/CodeRenderer.svelte.d.ts +12 -0
  39. package/dist/code-block/LanguageTabsCodeBlock.svelte +119 -0
  40. package/dist/code-block/LanguageTabsCodeBlock.svelte.d.ts +12 -0
  41. package/dist/code-block/MultiFileCodeBlock.svelte +75 -0
  42. package/dist/code-block/MultiFileCodeBlock.svelte.d.ts +12 -0
  43. package/dist/code-block/index.d.ts +7 -0
  44. package/dist/code-block/index.js +8 -0
  45. package/dist/index.d.ts +15 -0
  46. package/dist/index.js +15 -0
  47. package/dist/questions/MultipleChoiceQuestion.svelte +167 -0
  48. package/dist/questions/MultipleChoiceQuestion.svelte.d.ts +19 -0
  49. package/dist/questions/NumericQuestion.svelte +139 -0
  50. package/dist/questions/NumericQuestion.svelte.d.ts +20 -0
  51. package/dist/questions/Quiz.svelte +119 -0
  52. package/dist/questions/Quiz.svelte.d.ts +11 -0
  53. package/dist/questions/SingleChoiceQuestion.svelte +141 -0
  54. package/dist/questions/SingleChoiceQuestion.svelte.d.ts +19 -0
  55. package/dist/questions/index.d.ts +4 -0
  56. package/dist/questions/index.js +5 -0
  57. package/dist/radio-group/RadioGroup.svelte +16 -0
  58. package/dist/radio-group/RadioGroup.svelte.d.ts +7 -0
  59. package/dist/radio-group/RadioGroupItem.svelte +26 -0
  60. package/dist/radio-group/RadioGroupItem.svelte.d.ts +7 -0
  61. package/dist/radio-group/index.d.ts +2 -0
  62. package/dist/radio-group/index.js +3 -0
  63. package/package.json +12 -3
@@ -0,0 +1,85 @@
1
+ <!-- libs/helix-svelte/src/YouTube.svelte -->
2
+ <!-- Closely based on: diffbook YouTube, mirrored from @cloudvoyant/helix-react -->
3
+ <script lang="ts">
4
+ import { Ark } from '@ark-ui/svelte/factory';
5
+ import { getContext, setContext } from 'svelte';
6
+ import {
7
+ youtubeRootBase,
8
+ youtubeTitleBase,
9
+ youtubeFacadeBase,
10
+ youtubePlayButtonBase,
11
+ youtubePlayButtonInnerBase,
12
+ youtubeDescriptionBase,
13
+ youtubeChaptersBase,
14
+ youtubeChaptersSummaryBase,
15
+ youtubeChaptersChevronBase,
16
+ youtubeChaptersListBase,
17
+ youtubeChapterButtonBase,
18
+ youtubeChapterTimeBase,
19
+ youtubeChapterLabelBase,
20
+ extractYouTubeId,
21
+ formatTime,
22
+ parseYouTubeChapters,
23
+ fetchYouTubeMetadata,
24
+ cn,
25
+ } from '@cloudvoyant/helix';
26
+ import type { YouTubeChapter } from '@cloudvoyant/helix';
27
+ import type { HTMLAttributes } from 'svelte/elements';
28
+ import type { Snippet } from 'svelte';
29
+
30
+ type Props = {
31
+ url: string;
32
+ title?: string;
33
+ description?: string;
34
+ poster?: string;
35
+ class?: string;
36
+ children?: Snippet;
37
+ } & HTMLAttributes<HTMLDivElement>;
38
+
39
+ let { url, title, description, poster, class: className = '', children, ...rest }: Props = $props();
40
+
41
+ const videoId = $derived(extractYouTubeId(url));
42
+ const posterUrl = $derived(poster ?? `https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`);
43
+
44
+ let playing = $state(false);
45
+ let startAt = $state<number | null>(null);
46
+
47
+ const seek = (t: number) => {
48
+ startAt = t;
49
+ playing = true;
50
+ };
51
+
52
+ setContext('helix-youtube', { url, videoId, seek });
53
+
54
+ const src = $derived(
55
+ `https://www.youtube-nocookie.com/embed/${videoId}?autoplay=1${startAt != null ? `&start=${startAt}` : ''}`,
56
+ );
57
+
58
+ const classes = $derived(cn(youtubeRootBase, className));
59
+ </script>
60
+
61
+ <Ark as="div" data-youtube-id={videoId} class={classes} {...rest}>
62
+ {#if title}<Ark as="h3" class={youtubeTitleBase}>{title}</Ark>{/if}
63
+ {#if playing}
64
+ <iframe
65
+ {src}
66
+ title={title ?? `YouTube video ${videoId}`}
67
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
68
+ allowfullscreen
69
+ class="aspect-video w-full rounded-md border border-border"
70
+ ></iframe>
71
+ {:else}
72
+ <Ark as="button" type="button" class={youtubeFacadeBase} onclick={() => (playing = true)} aria-label={title ? `Play video: ${title}` : 'Play video'}>
73
+ <Ark as="img" src={posterUrl} alt={title ?? `YouTube video ${videoId}`} loading="lazy" class="h-full w-full object-cover" />
74
+ <Ark as="span" class={youtubePlayButtonBase} aria-hidden="true">
75
+ <Ark as="span" class={youtubePlayButtonInnerBase}>
76
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="h-8 w-8" aria-hidden="true">
77
+ <path d="M8 5v14l11-7z"></path>
78
+ </svg>
79
+ </Ark>
80
+ </Ark>
81
+ </Ark>
82
+ {/if}
83
+ {#if description}<Ark as="p" class={youtubeDescriptionBase}>{description}</Ark>{/if}
84
+ {#if children}{@render children()}{/if}
85
+ </Ark>
@@ -0,0 +1,13 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import type { Snippet } from 'svelte';
3
+ type Props = {
4
+ url: string;
5
+ title?: string;
6
+ description?: string;
7
+ poster?: string;
8
+ class?: string;
9
+ children?: Snippet;
10
+ } & HTMLAttributes<HTMLDivElement>;
11
+ declare const YouTube: import("svelte").Component<Props, {}, "">;
12
+ type YouTube = ReturnType<typeof YouTube>;
13
+ export default YouTube;
@@ -0,0 +1,20 @@
1
+ <!-- libs/helix-svelte/src/YoutubeTimestampAt.svelte -->
2
+ <script lang="ts">
3
+ import { Ark } from '@ark-ui/svelte/factory';
4
+ import { getContext } from 'svelte';
5
+ import { youtubeChapterButtonBase, youtubeChapterTimeBase, youtubeChapterLabelBase, formatTime } from '@cloudvoyant/helix';
6
+ import type { HTMLAttributes } from 'svelte/elements';
7
+ import type { Snippet } from 'svelte';
8
+
9
+ type Props = { t: number; children?: Snippet; class?: string } & HTMLAttributes<HTMLElement>;
10
+ let { t, children, class: className = '', ...rest }: Props = $props();
11
+
12
+ const ctx = getContext<{ seek: (t: number) => void }>('helix-youtube');
13
+ </script>
14
+
15
+ <Ark as="li" class={className} {...rest}>
16
+ <Ark as="button" type="button" onclick={() => ctx.seek(t)} class={youtubeChapterButtonBase}>
17
+ <Ark as="span" class={youtubeChapterTimeBase}>{formatTime(t)}</Ark>
18
+ <Ark as="span" class={youtubeChapterLabelBase}>{#if children}{@render children()}{/if}</Ark>
19
+ </Ark>
20
+ </Ark>
@@ -0,0 +1,10 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import type { Snippet } from 'svelte';
3
+ type Props = {
4
+ t: number;
5
+ children?: Snippet;
6
+ class?: string;
7
+ } & HTMLAttributes<HTMLElement>;
8
+ declare const YoutubeTimestampAt: import("svelte").Component<Props, {}, "">;
9
+ type YoutubeTimestampAt = ReturnType<typeof YoutubeTimestampAt>;
10
+ export default YoutubeTimestampAt;
@@ -0,0 +1,100 @@
1
+ <!-- libs/helix-svelte/src/YoutubeTimestamps.svelte -->
2
+ <!-- Composed timestamps list, auto-inferred from web metadata or overridden manually via
3
+ YoutubeTimestampAt children. Mirrored from @cloudvoyant/helix-react. -->
4
+ <script lang="ts">
5
+ import { Ark } from '@ark-ui/svelte/factory';
6
+ import { getContext } from 'svelte';
7
+ import {
8
+ youtubeChaptersBase,
9
+ youtubeChaptersSummaryBase,
10
+ youtubeChaptersChevronBase,
11
+ youtubeChaptersListBase,
12
+ youtubeChapterButtonBase,
13
+ youtubeChapterTimeBase,
14
+ youtubeChapterLabelBase,
15
+ formatTime,
16
+ parseYouTubeChapters,
17
+ fetchYouTubeMetadata,
18
+ cn,
19
+ } from '@cloudvoyant/helix';
20
+ import type { YouTubeChapter } from '@cloudvoyant/helix';
21
+ import type { HTMLAttributes } from 'svelte/elements';
22
+ import type { Snippet } from 'svelte';
23
+
24
+ type Props = {
25
+ chapters?: YouTubeChapter[];
26
+ children?: Snippet;
27
+ class?: string;
28
+ } & HTMLAttributes<HTMLDivElement>;
29
+
30
+ let { chapters = undefined, children, class: className = '', ...rest }: Props = $props();
31
+
32
+ const ctx = getContext<{ url: string; seek: (t: number) => void }>('helix-youtube');
33
+
34
+ let inferred = $state<YouTubeChapter[]>([]);
35
+ let loading = $state(chapters === undefined);
36
+
37
+ $effect(() => {
38
+ if (chapters !== undefined) {
39
+ loading = false;
40
+ return;
41
+ }
42
+ let cancelled = false;
43
+ loading = true;
44
+ fetchYouTubeMetadata(ctx.url)
45
+ .then((meta) => {
46
+ if (!cancelled) {
47
+ inferred = meta.description ? parseYouTubeChapters(meta.description) : [];
48
+ loading = false;
49
+ }
50
+ })
51
+ .catch(() => {
52
+ if (!cancelled) loading = false;
53
+ });
54
+ return () => {
55
+ cancelled = true;
56
+ };
57
+ });
58
+
59
+ const resolved = $derived(chapters ?? inferred);
60
+
61
+ const seek = (t: number) => ctx.seek(t);
62
+
63
+ const classes = $derived(cn(youtubeChaptersBase, className));
64
+ </script>
65
+
66
+ {#if loading || resolved.length > 0 || children}
67
+ <Ark as="div" class={classes} {...rest}>
68
+ <details class="group" open>
69
+ <Ark as="summary" class={youtubeChaptersSummaryBase}>
70
+ <svg
71
+ class={youtubeChaptersChevronBase}
72
+ xmlns="http://www.w3.org/2000/svg"
73
+ viewBox="0 0 24 24"
74
+ fill="none"
75
+ stroke="currentColor"
76
+ stroke-width="2"
77
+ stroke-linecap="round"
78
+ stroke-linejoin="round"
79
+ aria-hidden="true"
80
+ >
81
+ <path d="m9 18 6-6-6-6"></path>
82
+ </svg>
83
+ <span>Timestamps</span>
84
+ </Ark>
85
+ <Ark as="nav" aria-label="Video timestamps" class={youtubeChaptersListBase}>
86
+ <Ark as="ul" class="space-y-1">
87
+ {#each resolved as ch (ch.t)}
88
+ <Ark as="li">
89
+ <Ark as="button" type="button" onclick={() => seek(ch.t)} class={youtubeChapterButtonBase}>
90
+ <Ark as="span" class={youtubeChapterTimeBase}>{formatTime(ch.t)}</Ark>
91
+ <Ark as="span" class={youtubeChapterLabelBase}>{ch.label}</Ark>
92
+ </Ark>
93
+ </Ark>
94
+ {/each}
95
+ {#if children}{@render children()}{/if}
96
+ </Ark>
97
+ </Ark>
98
+ </details>
99
+ </Ark>
100
+ {/if}
@@ -0,0 +1,11 @@
1
+ import type { YouTubeChapter } from '@cloudvoyant/helix';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import type { Snippet } from 'svelte';
4
+ type Props = {
5
+ chapters?: YouTubeChapter[];
6
+ children?: Snippet;
7
+ class?: string;
8
+ } & HTMLAttributes<HTMLDivElement>;
9
+ declare const YoutubeTimestamps: import("svelte").Component<Props, {}, "">;
10
+ type YoutubeTimestamps = ReturnType<typeof YoutubeTimestamps>;
11
+ export default YoutubeTimestamps;
@@ -0,0 +1,67 @@
1
+ <!-- libs/helix-svelte/src/code-block/CodeBlock.svelte -->
2
+ <!-- Closely based on: diffbook code-block family, rebuilt as a composable API, mirrored from @cloudvoyant/helix-react -->
3
+ <script lang="ts">
4
+ import { Ark } from '@ark-ui/svelte/factory';
5
+ import CodeBlockHeader from './CodeBlockHeader.svelte';
6
+ import CodeBlockTitle from './CodeBlockTitle.svelte';
7
+ import CodeBlockContent from './CodeBlockContent.svelte';
8
+ import CodeBlockCopyButton from './CodeBlockCopyButton.svelte';
9
+ import { codeBlockRootBase, cn } from '@cloudvoyant/helix';
10
+ import { setContext } from 'svelte';
11
+ import { CODE_BLOCK_KEY, type CodeBlockContextValue } from './CodeBlockContext.svelte';
12
+ import type { HTMLAttributes } from 'svelte/elements';
13
+ import type { Snippet } from 'svelte';
14
+
15
+ type Props = {
16
+ code: string;
17
+ language?: string;
18
+ filename?: string;
19
+ html?: string;
20
+ showLineNumbers?: boolean;
21
+ scrollable?: boolean;
22
+ maxHeight?: number;
23
+ highlightLines?: number[];
24
+ class?: string;
25
+ children?: Snippet;
26
+ } & HTMLAttributes<HTMLDivElement>;
27
+
28
+ let {
29
+ code,
30
+ language = 'tsx',
31
+ filename,
32
+ html = undefined,
33
+ showLineNumbers = false,
34
+ scrollable = false,
35
+ maxHeight = 400,
36
+ highlightLines = undefined,
37
+ class: className = '',
38
+ children,
39
+ ...rest
40
+ }: Props = $props();
41
+
42
+ setContext<CodeBlockContextValue>(CODE_BLOCK_KEY, {
43
+ code,
44
+ language,
45
+ html,
46
+ showLineNumbers,
47
+ scrollable,
48
+ maxHeight,
49
+ highlightLines,
50
+ });
51
+
52
+ const classes = $derived(cn(codeBlockRootBase, className));
53
+ </script>
54
+
55
+ <Ark as="div" data-code-block class={classes} {...rest}>
56
+ {#if children}
57
+ {@render children()}
58
+ {:else}
59
+ <CodeBlockHeader>
60
+ <CodeBlockTitle>{filename ?? language}</CodeBlockTitle>
61
+ <div class="ml-auto">
62
+ <CodeBlockCopyButton />
63
+ </div>
64
+ </CodeBlockHeader>
65
+ <CodeBlockContent />
66
+ {/if}
67
+ </Ark>
@@ -0,0 +1,17 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import type { Snippet } from 'svelte';
3
+ type Props = {
4
+ code: string;
5
+ language?: string;
6
+ filename?: string;
7
+ html?: string;
8
+ showLineNumbers?: boolean;
9
+ scrollable?: boolean;
10
+ maxHeight?: number;
11
+ highlightLines?: number[];
12
+ class?: string;
13
+ children?: Snippet;
14
+ } & HTMLAttributes<HTMLDivElement>;
15
+ declare const CodeBlock: import("svelte").Component<Props, {}, "">;
16
+ type CodeBlock = ReturnType<typeof CodeBlock>;
17
+ export default CodeBlock;
@@ -0,0 +1,19 @@
1
+ <!-- libs/helix-svelte/src/code-block/CodeBlockContent.svelte -->
2
+ <script lang="ts">
3
+ import { Ark } from '@ark-ui/svelte/factory';
4
+ import CodeRenderer from './CodeRenderer.svelte';
5
+ import { codeBlockContext } from './CodeBlockContext.svelte';
6
+ import type { HTMLAttributes } from 'svelte/elements';
7
+
8
+ type Props = { html?: string; class?: string } & HTMLAttributes<HTMLDivElement>;
9
+ let { html = undefined, class: className = '', ...rest }: Props = $props();
10
+
11
+ const ctx = codeBlockContext();
12
+ if (!ctx) {
13
+ throw new Error('CodeBlockContent must be rendered inside a CodeBlock.');
14
+ }
15
+ </script>
16
+
17
+ <Ark as="div" class={className} {...rest}>
18
+ <CodeRenderer {...ctx} html={html ?? ctx.html} />
19
+ </Ark>
@@ -0,0 +1,8 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ type Props = {
3
+ html?: string;
4
+ class?: string;
5
+ } & HTMLAttributes<HTMLDivElement>;
6
+ declare const CodeBlockContent: import("svelte").Component<Props, {}, "">;
7
+ type CodeBlockContent = ReturnType<typeof CodeBlockContent>;
8
+ export default CodeBlockContent;
@@ -0,0 +1,11 @@
1
+ export interface CodeBlockContextValue {
2
+ code: string;
3
+ language: string;
4
+ html?: string;
5
+ showLineNumbers: boolean;
6
+ scrollable: boolean;
7
+ maxHeight: number;
8
+ highlightLines?: number[];
9
+ }
10
+ export declare const CODE_BLOCK_KEY: unique symbol;
11
+ export declare function codeBlockContext(): CodeBlockContextValue | undefined;
@@ -0,0 +1,7 @@
1
+ // libs/helix-svelte/src/code-block/CodeBlockContext.svelte.ts
2
+ // Shared code-block context so composite parts (Title/Content/CopyButton) read the block's props.
3
+ import { getContext } from 'svelte';
4
+ export const CODE_BLOCK_KEY = Symbol('helix-code-block');
5
+ export function codeBlockContext() {
6
+ return getContext(CODE_BLOCK_KEY);
7
+ }
@@ -0,0 +1,19 @@
1
+ <!-- libs/helix-svelte/src/code-block/CodeBlockCopyButton.svelte -->
2
+ <script lang="ts">
3
+ import { Ark } from '@ark-ui/svelte/factory';
4
+ import CopyButton from '../CopyButton.svelte';
5
+ import { codeBlockContext } from './CodeBlockContext.svelte';
6
+ import type { HTMLAttributes } from 'svelte/elements';
7
+
8
+ type Props = { label?: string; class?: string } & HTMLAttributes<HTMLDivElement>;
9
+ let { label = 'Copy code', class: className = '', ...rest }: Props = $props();
10
+
11
+ const ctx = codeBlockContext();
12
+ if (!ctx) {
13
+ throw new Error('CodeBlockCopyButton must be rendered inside a CodeBlock.');
14
+ }
15
+ </script>
16
+
17
+ <Ark as="div" class={className} {...rest}>
18
+ <CopyButton value={ctx.code} {label} />
19
+ </Ark>
@@ -0,0 +1,8 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ type Props = {
3
+ label?: string;
4
+ class?: string;
5
+ } & HTMLAttributes<HTMLDivElement>;
6
+ declare const CodeBlockCopyButton: import("svelte").Component<Props, {}, "">;
7
+ type CodeBlockCopyButton = ReturnType<typeof CodeBlockCopyButton>;
8
+ export default CodeBlockCopyButton;
@@ -0,0 +1,15 @@
1
+ <!-- libs/helix-svelte/src/code-block/CodeBlockHeader.svelte -->
2
+ <script lang="ts">
3
+ import { Ark } from '@ark-ui/svelte/factory';
4
+ import { codeBlockHeaderBase, cn } from '@cloudvoyant/helix';
5
+ import type { HTMLAttributes } from 'svelte/elements';
6
+ import type { Snippet } from 'svelte';
7
+
8
+ type Props = { children?: Snippet; class?: string } & HTMLAttributes<HTMLDivElement>;
9
+ let { children, class: className = '', ...rest }: Props = $props();
10
+ const classes = $derived(cn(codeBlockHeaderBase, className));
11
+ </script>
12
+
13
+ <Ark as="div" class={classes} {...rest}>
14
+ {#if children}{@render children()}{/if}
15
+ </Ark>
@@ -0,0 +1,9 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import type { Snippet } from 'svelte';
3
+ type Props = {
4
+ children?: Snippet;
5
+ class?: string;
6
+ } & HTMLAttributes<HTMLDivElement>;
7
+ declare const CodeBlockHeader: import("svelte").Component<Props, {}, "">;
8
+ type CodeBlockHeader = ReturnType<typeof CodeBlockHeader>;
9
+ export default CodeBlockHeader;
@@ -0,0 +1,15 @@
1
+ <!-- libs/helix-svelte/src/code-block/CodeBlockTitle.svelte -->
2
+ <script lang="ts">
3
+ import { Ark } from '@ark-ui/svelte/factory';
4
+ import { codeBlockTitleBase, cn } from '@cloudvoyant/helix';
5
+ import type { HTMLAttributes } from 'svelte/elements';
6
+ import type { Snippet } from 'svelte';
7
+
8
+ type Props = { children?: Snippet; class?: string } & HTMLAttributes<HTMLSpanElement>;
9
+ let { children, class: className = '', ...rest }: Props = $props();
10
+ const classes = $derived(cn(codeBlockTitleBase, className));
11
+ </script>
12
+
13
+ <Ark as="span" class={classes} {...rest}>
14
+ {#if children}{@render children()}{/if}
15
+ </Ark>
@@ -0,0 +1,9 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import type { Snippet } from 'svelte';
3
+ type Props = {
4
+ children?: Snippet;
5
+ class?: string;
6
+ } & HTMLAttributes<HTMLSpanElement>;
7
+ declare const CodeBlockTitle: import("svelte").Component<Props, {}, "">;
8
+ type CodeBlockTitle = ReturnType<typeof CodeBlockTitle>;
9
+ export default CodeBlockTitle;
@@ -0,0 +1,78 @@
1
+ <!-- libs/helix-svelte/src/code-block/CodeRenderer.svelte -->
2
+ <!-- Shared shiki renderer for the code-block family, mirrored from @cloudvoyant/helix-react -->
3
+ <script lang="ts">
4
+ import {
5
+ codeBlockBodyBase,
6
+ codeBlockBodyScrollableBase,
7
+ codeBlockContentBase,
8
+ codeBlockLineNumbersBase,
9
+ codeBlockHighlightBase,
10
+ codeBlockLoadingBase,
11
+ codeBlockSpinnerBase,
12
+ injectCodeBlockStyles,
13
+ renderCodeToHtml,
14
+ cn,
15
+ } from '@cloudvoyant/helix';
16
+
17
+ type Props = {
18
+ code: string;
19
+ language: string;
20
+ html?: string;
21
+ showLineNumbers: boolean;
22
+ scrollable: boolean;
23
+ maxHeight: number;
24
+ highlightLines?: number[];
25
+ };
26
+
27
+ let {
28
+ code,
29
+ language,
30
+ html = undefined,
31
+ showLineNumbers,
32
+ scrollable,
33
+ maxHeight,
34
+ highlightLines = undefined,
35
+ }: Props = $props();
36
+
37
+ let rendered = $state<string | null>(html ?? null);
38
+
39
+ const highlightKey = $derived(highlightLines?.join(',') ?? '');
40
+
41
+ // The scoped `.hx-cb`/`.cbln`/`.cbhl` styles must be present for both the client
42
+ // highlight path and the prerendered `html` path, so inject them on mount (idempotent).
43
+ $effect(() => {
44
+ injectCodeBlockStyles();
45
+ });
46
+
47
+ $effect(() => {
48
+ if (html !== undefined) return;
49
+ let cancelled = false;
50
+ rendered = null;
51
+ renderCodeToHtml(code, language, highlightLines).then((h) => {
52
+ if (!cancelled) rendered = h;
53
+ });
54
+ return () => {
55
+ cancelled = true;
56
+ };
57
+ });
58
+
59
+ const classes = $derived(cn(codeBlockBodyBase, scrollable && codeBlockBodyScrollableBase));
60
+ const contentClasses = $derived(
61
+ cn(
62
+ codeBlockContentBase,
63
+ showLineNumbers && codeBlockLineNumbersBase,
64
+ highlightLines?.length && codeBlockHighlightBase,
65
+ ),
66
+ );
67
+ const styles = $derived(scrollable ? `max-height: ${maxHeight}px;` : undefined);
68
+ </script>
69
+
70
+ <div class={classes} style={styles}>
71
+ {#if rendered !== null}
72
+ <div class={contentClasses}>{@html rendered}</div>
73
+ {:else}
74
+ <div class={codeBlockLoadingBase}>
75
+ <div class={codeBlockSpinnerBase}></div>
76
+ </div>
77
+ {/if}
78
+ </div>
@@ -0,0 +1,12 @@
1
+ type Props = {
2
+ code: string;
3
+ language: string;
4
+ html?: string;
5
+ showLineNumbers: boolean;
6
+ scrollable: boolean;
7
+ maxHeight: number;
8
+ highlightLines?: number[];
9
+ };
10
+ declare const CodeRenderer: import("svelte").Component<Props, {}, "">;
11
+ type CodeRenderer = ReturnType<typeof CodeRenderer>;
12
+ export default CodeRenderer;