@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,119 @@
1
+ <!-- libs/helix-svelte/src/questions/Quiz.svelte -->
2
+ <!-- Closely based on: diffbook Quiz, mirrored from @cloudvoyant/helix-react -->
3
+ <script lang="ts">
4
+ import { Ark } from '@ark-ui/svelte/factory';
5
+ import SingleChoiceQuestion from './SingleChoiceQuestion.svelte';
6
+ import MultipleChoiceQuestion from './MultipleChoiceQuestion.svelte';
7
+ import NumericQuestion from './NumericQuestion.svelte';
8
+ import {
9
+ quizRootBase,
10
+ quizHeaderBase,
11
+ quizTitleBase,
12
+ quizScoreBase,
13
+ quizResultBase,
14
+ quizResultTitleBase,
15
+ quizResultTextBase,
16
+ quizSubmitButtonBase,
17
+ quizResetButtonBase,
18
+ cn,
19
+ } from '@cloudvoyant/helix';
20
+ import type { Question } from '@cloudvoyant/helix';
21
+ import type { HTMLAttributes } from 'svelte/elements';
22
+
23
+ type Props = {
24
+ id: string;
25
+ title?: string;
26
+ questions: Question[];
27
+ class?: string;
28
+ } & HTMLAttributes<HTMLDivElement>;
29
+
30
+ let { id, title, questions, class: className = '', ...rest }: Props = $props();
31
+
32
+ let answers = $state<Record<string, { answered: boolean; correct: boolean }>>({});
33
+ let submitted = $state(false);
34
+ let resetKey = $state(0);
35
+
36
+ const total = $derived(questions.length);
37
+ const allAnswered = $derived(questions.length > 0 && questions.every((q) => answers[q.id]?.answered === true));
38
+ const score = $derived(questions.filter((q) => answers[q.id]?.correct === true).length);
39
+ const pct = $derived(total > 0 ? Math.round((score / total) * 100) : 0);
40
+
41
+ function handleAnswerChange(questionId: string) {
42
+ return (state: { answered: boolean; correct: boolean }) => {
43
+ answers = { ...answers, [questionId]: state };
44
+ };
45
+ }
46
+
47
+ function handleReset() {
48
+ answers = {};
49
+ submitted = false;
50
+ resetKey += 1;
51
+ }
52
+ </script>
53
+
54
+ <div data-quiz-id={id} class={cn(quizRootBase, className)} {...rest}>
55
+ <div class={quizHeaderBase}>
56
+ {#if title}<Ark as="h3" class={quizTitleBase}>{title}</Ark>{/if}
57
+ </div>
58
+ {#each questions as q (q.id)}
59
+ {#key `${q.id}-${resetKey}`}
60
+ {#if q.type === 'single'}
61
+ <SingleChoiceQuestion
62
+ id={q.id}
63
+ prompt={q.prompt}
64
+ choices={q.choices}
65
+ correct={q.correct}
66
+ explanation={q.explanation}
67
+ quiz
68
+ {submitted}
69
+ onAnswerChange={handleAnswerChange(q.id)}
70
+ />
71
+ {:else if q.type === 'multiple'}
72
+ <MultipleChoiceQuestion
73
+ id={q.id}
74
+ prompt={q.prompt}
75
+ choices={q.choices}
76
+ correct={q.correct}
77
+ explanation={q.explanation}
78
+ quiz
79
+ {submitted}
80
+ onAnswerChange={handleAnswerChange(q.id)}
81
+ />
82
+ {:else if q.type === 'numeric'}
83
+ <NumericQuestion
84
+ id={q.id}
85
+ prompt={q.prompt}
86
+ answer={q.answer}
87
+ tolerance={q.tolerance}
88
+ unit={q.unit}
89
+ explanation={q.explanation}
90
+ quiz
91
+ {submitted}
92
+ onAnswerChange={handleAnswerChange(q.id)}
93
+ />
94
+ {/if}
95
+ {/key}
96
+ {/each}
97
+ <div class="mt-4 flex items-center gap-3">
98
+ {#if !submitted}
99
+ <Ark as="button" type="button" onclick={() => (submitted = true)} disabled={!allAnswered} class={quizSubmitButtonBase}>
100
+ Submit
101
+ </Ark>
102
+ {:else}
103
+ <Ark as="button" type="button" onclick={handleReset} class={quizResetButtonBase}>
104
+ Reset
105
+ </Ark>
106
+ {/if}
107
+ <Ark as="span" role="status" class={quizScoreBase}>
108
+ {submitted ? `${score}/${total} (${pct}%)` : `${total} question${total === 1 ? '' : 's'}`}
109
+ </Ark>
110
+ </div>
111
+ {#if submitted}
112
+ <div class={quizResultBase}>
113
+ <div aria-live="polite" role="status" class="text-center">
114
+ <p class={quizResultTitleBase}>{score}/{total} ({pct}%)</p>
115
+ <p class={quizResultTextBase}>{pct >= 70 ? 'Well done!' : 'Keep studying and try again.'}</p>
116
+ </div>
117
+ </div>
118
+ {/if}
119
+ </div>
@@ -0,0 +1,11 @@
1
+ import type { Question } from '@cloudvoyant/helix';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ type Props = {
4
+ id: string;
5
+ title?: string;
6
+ questions: Question[];
7
+ class?: string;
8
+ } & HTMLAttributes<HTMLDivElement>;
9
+ declare const Quiz: import("svelte").Component<Props, {}, "">;
10
+ type Quiz = ReturnType<typeof Quiz>;
11
+ export default Quiz;
@@ -0,0 +1,141 @@
1
+ <!-- libs/helix-svelte/src/questions/SingleChoiceQuestion.svelte -->
2
+ <!-- Closely based on: diffbook Quiz, mirrored from @cloudvoyant/helix-react -->
3
+ <script lang="ts">
4
+ import { Ark } from '@ark-ui/svelte/factory';
5
+ import RadioGroup from '../radio-group/RadioGroup.svelte';
6
+ import RadioGroupItem from '../radio-group/RadioGroupItem.svelte';
7
+ import {
8
+ questionCardBase,
9
+ questionPromptBase,
10
+ questionGroupBase,
11
+ questionOptionBase,
12
+ questionOptionCorrectBase,
13
+ questionOptionIncorrectBase,
14
+ questionCheckButtonBase,
15
+ feedbackCorrectBase,
16
+ feedbackIncorrectBase,
17
+ feedbackExplanationBase,
18
+ cn,
19
+ } from '@cloudvoyant/helix';
20
+ import type { HTMLAttributes } from 'svelte/elements';
21
+
22
+ type Props = {
23
+ id: string;
24
+ prompt: string;
25
+ choices: string[];
26
+ correct: number;
27
+ explanation?: string;
28
+ quiz?: boolean;
29
+ submitted?: boolean;
30
+ onAnswerChange?: (state: { answered: boolean; correct: boolean }) => void;
31
+ onGraded?: (correct: boolean) => void;
32
+ class?: string;
33
+ } & HTMLAttributes<HTMLDivElement>;
34
+
35
+ let {
36
+ prompt,
37
+ choices,
38
+ correct,
39
+ explanation = undefined,
40
+ quiz = false,
41
+ submitted = false,
42
+ onAnswerChange,
43
+ onGraded,
44
+ class: className = '',
45
+ ...rest
46
+ }: Props = $props();
47
+
48
+ let selected = $state<number | null>(null);
49
+ let result = $state<boolean | null>(null);
50
+
51
+ const displayResult = $derived(quiz ? (submitted ? selected === correct : null) : result);
52
+
53
+ function handleCheck() {
54
+ if (selected === null) return;
55
+ const isCorrect = selected === correct;
56
+ result = isCorrect;
57
+ onGraded?.(isCorrect);
58
+ }
59
+ </script>
60
+
61
+ <div class={cn(questionCardBase, className)} {...rest}>
62
+ <p class={questionPromptBase}>{prompt}</p>
63
+ <RadioGroup
64
+ orientation="vertical"
65
+ value={selected !== null ? String(selected) : null}
66
+ onValueChange={(details) => {
67
+ if (quiz ? submitted : result !== null) return;
68
+ selected = details.value != null ? Number(details.value) : null;
69
+ if (quiz) {
70
+ const has = selected !== null;
71
+ onAnswerChange?.({ answered: has, correct: has && selected === correct });
72
+ }
73
+ }}
74
+ disabled={quiz ? submitted : result !== null}
75
+ class={questionGroupBase}
76
+ >
77
+ {#each choices as choice, i (i)}
78
+ <RadioGroupItem
79
+ value={String(i)}
80
+ class={cn(
81
+ questionOptionBase,
82
+ displayResult !== null && i === correct && questionOptionCorrectBase,
83
+ displayResult !== null && i === selected && i !== correct && questionOptionIncorrectBase,
84
+ )}
85
+ >
86
+ {choice}
87
+ </RadioGroupItem>
88
+ {/each}
89
+ </RadioGroup>
90
+ {#if !quiz && result === null}
91
+ <Ark as="button" type="button" onclick={handleCheck} disabled={selected === null} class={questionCheckButtonBase}>
92
+ Check
93
+ </Ark>
94
+ {/if}
95
+ {#if displayResult !== null}
96
+ <div aria-live="polite" role="status" class="mt-3">
97
+ <div
98
+ class={cn(
99
+ 'flex items-center gap-2 text-sm font-medium',
100
+ displayResult ? feedbackCorrectBase : feedbackIncorrectBase,
101
+ )}
102
+ >
103
+ {#if displayResult}
104
+ <svg
105
+ xmlns="http://www.w3.org/2000/svg"
106
+ viewBox="0 0 24 24"
107
+ fill="none"
108
+ stroke="currentColor"
109
+ stroke-width="2"
110
+ stroke-linecap="round"
111
+ stroke-linejoin="round"
112
+ aria-hidden="true"
113
+ class="size-4 shrink-0"
114
+ >
115
+ <circle cx="12" cy="12" r="10"></circle>
116
+ <path d="m9 12 2 2 4-4"></path>
117
+ </svg>
118
+ <span>Correct</span>
119
+ {:else}
120
+ <svg
121
+ xmlns="http://www.w3.org/2000/svg"
122
+ viewBox="0 0 24 24"
123
+ fill="none"
124
+ stroke="currentColor"
125
+ stroke-width="2"
126
+ stroke-linecap="round"
127
+ stroke-linejoin="round"
128
+ aria-hidden="true"
129
+ class="size-4 shrink-0"
130
+ >
131
+ <circle cx="12" cy="12" r="10"></circle>
132
+ <path d="m15 9-6 6"></path>
133
+ <path d="m9 9 6 6"></path>
134
+ </svg>
135
+ <span>Incorrect</span>
136
+ {/if}
137
+ </div>
138
+ {#if explanation}<p class={feedbackExplanationBase}>{explanation}</p>{/if}
139
+ </div>
140
+ {/if}
141
+ </div>
@@ -0,0 +1,19 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ type Props = {
3
+ id: string;
4
+ prompt: string;
5
+ choices: string[];
6
+ correct: number;
7
+ explanation?: string;
8
+ quiz?: boolean;
9
+ submitted?: boolean;
10
+ onAnswerChange?: (state: {
11
+ answered: boolean;
12
+ correct: boolean;
13
+ }) => void;
14
+ onGraded?: (correct: boolean) => void;
15
+ class?: string;
16
+ } & HTMLAttributes<HTMLDivElement>;
17
+ declare const SingleChoiceQuestion: import("svelte").Component<Props, {}, "">;
18
+ type SingleChoiceQuestion = ReturnType<typeof SingleChoiceQuestion>;
19
+ export default SingleChoiceQuestion;
@@ -0,0 +1,4 @@
1
+ export { default as SingleChoiceQuestion } from './SingleChoiceQuestion.svelte';
2
+ export { default as MultipleChoiceQuestion } from './MultipleChoiceQuestion.svelte';
3
+ export { default as NumericQuestion } from './NumericQuestion.svelte';
4
+ export { default as Quiz } from './Quiz.svelte';
@@ -0,0 +1,5 @@
1
+ // libs/helix-svelte/src/questions/index.ts
2
+ export { default as SingleChoiceQuestion } from './SingleChoiceQuestion.svelte';
3
+ export { default as MultipleChoiceQuestion } from './MultipleChoiceQuestion.svelte';
4
+ export { default as NumericQuestion } from './NumericQuestion.svelte';
5
+ export { default as Quiz } from './Quiz.svelte';
@@ -0,0 +1,16 @@
1
+ <!-- libs/helix-svelte/src/radio-group/RadioGroup.svelte -->
2
+ <!-- Closely based on: Shark UI radio group (@ark-ui/svelte/radio-group), mirrored from @cloudvoyant/helix-react -->
3
+ <script lang="ts">
4
+ import { RadioGroupRoot, type RadioGroupRootProps } from '@ark-ui/svelte/radio-group';
5
+ import { radioGroupRootBase, cn } from '@cloudvoyant/helix';
6
+
7
+ type Props = RadioGroupRootProps & { class?: string };
8
+
9
+ let { class: className = '', children, ...rest }: Props = $props();
10
+
11
+ const classes = $derived(cn(radioGroupRootBase, className));
12
+ </script>
13
+
14
+ <RadioGroupRoot class={classes} {...rest}>
15
+ {@render children?.()}
16
+ </RadioGroupRoot>
@@ -0,0 +1,7 @@
1
+ import { type RadioGroupRootProps } from '@ark-ui/svelte/radio-group';
2
+ type Props = RadioGroupRootProps & {
3
+ class?: string;
4
+ };
5
+ declare const RadioGroup: import("svelte").Component<Props, {}, "">;
6
+ type RadioGroup = ReturnType<typeof RadioGroup>;
7
+ export default RadioGroup;
@@ -0,0 +1,26 @@
1
+ <!-- libs/helix-svelte/src/radio-group/RadioGroupItem.svelte -->
2
+ <!-- Closely based on: Shark UI radio group (@ark-ui/svelte/radio-group), mirrored from @cloudvoyant/helix-react -->
3
+ <script lang="ts">
4
+ import {
5
+ RadioGroupItem as ArkRadioGroupItem,
6
+ RadioGroupItemControl,
7
+ RadioGroupItemText,
8
+ RadioGroupItemHiddenInput,
9
+ type RadioGroupItemProps,
10
+ } from '@ark-ui/svelte/radio-group';
11
+ import { radioGroupItemControlBase, radioGroupItemTextBase, cn } from '@cloudvoyant/helix';
12
+
13
+ type Props = RadioGroupItemProps & { class?: string };
14
+
15
+ let { class: className = '', children, ...rest }: Props = $props();
16
+
17
+ const classes = $derived(cn('flex cursor-pointer items-center gap-2.5', className));
18
+ </script>
19
+
20
+ <ArkRadioGroupItem class={classes} {...rest}>
21
+ <RadioGroupItemControl class={radioGroupItemControlBase}>
22
+ <span class="size-1.5 rounded-full bg-current hidden [[data-state=checked]>&]:block"></span>
23
+ </RadioGroupItemControl>
24
+ <RadioGroupItemText class={radioGroupItemTextBase}>{@render children?.()}</RadioGroupItemText>
25
+ <RadioGroupItemHiddenInput />
26
+ </ArkRadioGroupItem>
@@ -0,0 +1,7 @@
1
+ import { type RadioGroupItemProps } from '@ark-ui/svelte/radio-group';
2
+ type Props = RadioGroupItemProps & {
3
+ class?: string;
4
+ };
5
+ declare const RadioGroupItem: import("svelte").Component<Props, {}, "">;
6
+ type RadioGroupItem = ReturnType<typeof RadioGroupItem>;
7
+ export default RadioGroupItem;
@@ -0,0 +1,2 @@
1
+ export { default as RadioGroup } from './RadioGroup.svelte';
2
+ export { default as RadioGroupItem } from './RadioGroupItem.svelte';
@@ -0,0 +1,3 @@
1
+ // libs/helix-svelte/src/radio-group/index.ts
2
+ export { default as RadioGroup } from './RadioGroup.svelte';
3
+ export { default as RadioGroupItem } from './RadioGroupItem.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudvoyant/helix-svelte",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "helix Svelte UI components",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,22 +24,31 @@
24
24
  },
25
25
  "peerDependencies": {
26
26
  "svelte": "^5.56.9",
27
- "mermaid": "^11.16.0"
27
+ "mermaid": "^11.16.0",
28
+ "shiki": "^3.0.0"
28
29
  },
29
30
  "peerDependenciesMeta": {
30
31
  "mermaid": {
31
32
  "optional": true
33
+ },
34
+ "shiki": {
35
+ "optional": true
32
36
  }
33
37
  },
34
38
  "dependencies": {
35
39
  "@ark-ui/svelte": "^5.23.1",
36
- "@cloudvoyant/helix": "0.16.0"
40
+ "@tanstack/charts": "^0.16.0",
41
+ "@cloudvoyant/helix": "0.17.0"
37
42
  },
38
43
  "devDependencies": {
39
44
  "@sveltejs/package": "^2.5.8",
40
45
  "@sveltejs/vite-plugin-svelte": "^4.0.0",
46
+ "@types/katex": "^0.16.8",
47
+ "katex": "^0.16.11",
48
+ "manim-web": "^0.3.24",
41
49
  "mermaid": "^11.16.0",
42
50
  "prettier-plugin-svelte": "^4.1.1",
51
+ "shiki": "^3.0.0",
43
52
  "svelte": "^5.56.9",
44
53
  "svelte-check": "^4.7.6",
45
54
  "typescript": "^5.5.0"