twintrinsic 0.0.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.
Files changed (212) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +150 -0
  3. package/dist/App/App.svelte +54 -0
  4. package/dist/App/App.svelte.d.ts +65 -0
  5. package/dist/Section.svelte +25 -0
  6. package/dist/Section.svelte.d.ts +34 -0
  7. package/dist/actions/clickOutside.d.ts +9 -0
  8. package/dist/actions/clickOutside.js +19 -0
  9. package/dist/actions/index.d.ts +1 -0
  10. package/dist/actions/index.js +1 -0
  11. package/dist/components/Accordion/Accordion.svelte +75 -0
  12. package/dist/components/Accordion/Accordion.svelte.d.ts +39 -0
  13. package/dist/components/Accordion/AccordionItem.svelte +150 -0
  14. package/dist/components/Accordion/AccordionItem.svelte.d.ts +30 -0
  15. package/dist/components/App/App.story.md +8 -0
  16. package/dist/components/App/App.story.svelte +170 -0
  17. package/dist/components/App/App.story.svelte.d.ts +22 -0
  18. package/dist/components/App/App.svelte +77 -0
  19. package/dist/components/App/App.svelte.d.ts +66 -0
  20. package/dist/components/App/Split.svelte +346 -0
  21. package/dist/components/App/Split.svelte.d.ts +54 -0
  22. package/dist/components/App/index.d.ts +2 -0
  23. package/dist/components/App/index.js +3 -0
  24. package/dist/components/AppHeader/AppHeader.svelte +439 -0
  25. package/dist/components/AppHeader/AppHeader.svelte.d.ts +24 -0
  26. package/dist/components/Avatar/Avatar.svelte +300 -0
  27. package/dist/components/Avatar/Avatar.svelte.d.ts +48 -0
  28. package/dist/components/Avatar/AvatarGroup.svelte +185 -0
  29. package/dist/components/Avatar/AvatarGroup.svelte.d.ts +46 -0
  30. package/dist/components/Badge/Badge.svelte +186 -0
  31. package/dist/components/Badge/Badge.svelte.d.ts +51 -0
  32. package/dist/components/BottomBar/BottomBar.svelte +146 -0
  33. package/dist/components/BottomBar/BottomBar.svelte.d.ts +38 -0
  34. package/dist/components/Breadcrumb/Breadcrumb.svelte +77 -0
  35. package/dist/components/Breadcrumb/Breadcrumb.svelte.d.ts +42 -0
  36. package/dist/components/Breadcrumb/BreadcrumbItem.svelte +171 -0
  37. package/dist/components/Breadcrumb/BreadcrumbItem.svelte.d.ts +38 -0
  38. package/dist/components/Button/Button.svelte +252 -0
  39. package/dist/components/Button/Button.svelte.d.ts +80 -0
  40. package/dist/components/Button/ButtonGroup.svelte +127 -0
  41. package/dist/components/Button/ButtonGroup.svelte.d.ts +44 -0
  42. package/dist/components/Card/Card.svelte +152 -0
  43. package/dist/components/Card/Card.svelte.d.ts +55 -0
  44. package/dist/components/Carousel/Carousel.svelte +461 -0
  45. package/dist/components/Carousel/Carousel.svelte.d.ts +79 -0
  46. package/dist/components/Carousel/CarouselItem.svelte +149 -0
  47. package/dist/components/Carousel/CarouselItem.svelte.d.ts +35 -0
  48. package/dist/components/Chip/Chip.svelte +288 -0
  49. package/dist/components/Chip/Chip.svelte.d.ts +71 -0
  50. package/dist/components/Chip/ChipGroup.svelte +190 -0
  51. package/dist/components/Chip/ChipGroup.svelte.d.ts +71 -0
  52. package/dist/components/CodeBlock/CodeBlock.svelte +356 -0
  53. package/dist/components/CodeBlock/CodeBlock.svelte.d.ts +44 -0
  54. package/dist/components/CodeBlock/index.d.ts +1 -0
  55. package/dist/components/CodeBlock/index.js +1 -0
  56. package/dist/components/CodeBlockSpeed/CodeBlockSpeed.svelte +145 -0
  57. package/dist/components/CodeBlockSpeed/CodeBlockSpeed.svelte.d.ts +44 -0
  58. package/dist/components/CodeEditor/CodeEditor.svelte +229 -0
  59. package/dist/components/CodeEditor/CodeEditor.svelte.d.ts +23 -0
  60. package/dist/components/Combobox/Combobox.svelte +279 -0
  61. package/dist/components/Combobox/Combobox.svelte.d.ts +34 -0
  62. package/dist/components/Container/Container.svelte +45 -0
  63. package/dist/components/Container/Container.svelte.d.ts +36 -0
  64. package/dist/components/DataTable/DataTable.svelte +879 -0
  65. package/dist/components/DataTable/DataTable.svelte.d.ts +102 -0
  66. package/dist/components/Form/AutoComplete.svelte +357 -0
  67. package/dist/components/Form/AutoComplete.svelte.d.ts +73 -0
  68. package/dist/components/Form/Calendar.svelte +429 -0
  69. package/dist/components/Form/Calendar.svelte.d.ts +53 -0
  70. package/dist/components/Form/Checkbox.svelte +196 -0
  71. package/dist/components/Form/Checkbox.svelte.d.ts +50 -0
  72. package/dist/components/Form/ColorPicker.svelte +396 -0
  73. package/dist/components/Form/ColorPicker.svelte.d.ts +43 -0
  74. package/dist/components/Form/Combobox.svelte +645 -0
  75. package/dist/components/Form/Combobox.svelte.d.ts +93 -0
  76. package/dist/components/Form/Dropdown.svelte +773 -0
  77. package/dist/components/Form/Dropdown.svelte.d.ts +81 -0
  78. package/dist/components/Form/FileUpload.svelte +796 -0
  79. package/dist/components/Form/FileUpload.svelte.d.ts +78 -0
  80. package/dist/components/Form/FloatLabel.svelte +245 -0
  81. package/dist/components/Form/FloatLabel.svelte.d.ts +44 -0
  82. package/dist/components/Form/Form.svelte +281 -0
  83. package/dist/components/Form/Form.svelte.d.ts +54 -0
  84. package/dist/components/Form/FormField.svelte +218 -0
  85. package/dist/components/Form/FormField.svelte.d.ts +47 -0
  86. package/dist/components/Form/Input.svelte +340 -0
  87. package/dist/components/Form/Input.svelte.d.ts +79 -0
  88. package/dist/components/Form/InputSwitch.svelte +189 -0
  89. package/dist/components/Form/InputSwitch.svelte.d.ts +46 -0
  90. package/dist/components/Form/InvalidState.svelte +97 -0
  91. package/dist/components/Form/InvalidState.svelte.d.ts +37 -0
  92. package/dist/components/Form/Knob.svelte +537 -0
  93. package/dist/components/Form/Knob.svelte.d.ts +78 -0
  94. package/dist/components/Form/ListInput.svelte +469 -0
  95. package/dist/components/Form/ListInput.svelte.d.ts +70 -0
  96. package/dist/components/Form/Listbox.svelte +513 -0
  97. package/dist/components/Form/Listbox.svelte.d.ts +74 -0
  98. package/dist/components/Form/NumberInput.svelte +452 -0
  99. package/dist/components/Form/NumberInput.svelte.d.ts +82 -0
  100. package/dist/components/Form/Radio.svelte +192 -0
  101. package/dist/components/Form/Radio.svelte.d.ts +53 -0
  102. package/dist/components/Form/RadioGroup.svelte +155 -0
  103. package/dist/components/Form/RadioGroup.svelte.d.ts +48 -0
  104. package/dist/components/Form/Rating.svelte +380 -0
  105. package/dist/components/Form/Rating.svelte.d.ts +64 -0
  106. package/dist/components/Form/Select.svelte +436 -0
  107. package/dist/components/Form/Select.svelte.d.ts +49 -0
  108. package/dist/components/Form/SelectGroup.svelte +34 -0
  109. package/dist/components/Form/SelectGroup.svelte.d.ts +33 -0
  110. package/dist/components/Form/Slider.svelte +622 -0
  111. package/dist/components/Form/Slider.svelte.d.ts +73 -0
  112. package/dist/components/Form/Switch.svelte +192 -0
  113. package/dist/components/Form/Switch.svelte.d.ts +46 -0
  114. package/dist/components/Form/TextInput.svelte +274 -0
  115. package/dist/components/Form/TextInput.svelte.d.ts +74 -0
  116. package/dist/components/Form/Textarea.svelte +207 -0
  117. package/dist/components/Form/Textarea.svelte.d.ts +62 -0
  118. package/dist/components/Icon/Icon.svelte +140 -0
  119. package/dist/components/Icon/Icon.svelte.d.ts +25 -0
  120. package/dist/components/Icon/index.d.ts +1 -0
  121. package/dist/components/Icon/index.js +1 -0
  122. package/dist/components/Lazy/Lazy.svelte +158 -0
  123. package/dist/components/Lazy/Lazy.svelte.d.ts +42 -0
  124. package/dist/components/Masonry/Masonry.svelte +299 -0
  125. package/dist/components/Masonry/Masonry.svelte.d.ts +55 -0
  126. package/dist/components/Menu/Menu/Menu.svelte +65 -0
  127. package/dist/components/Menu/Menu/Menu.svelte.d.ts +17 -0
  128. package/dist/components/Menu/Menu/MenuItem.svelte +90 -0
  129. package/dist/components/Menu/Menu/MenuItem.svelte.d.ts +27 -0
  130. package/dist/components/Modal/Modal.svelte +334 -0
  131. package/dist/components/Modal/Modal.svelte.d.ts +55 -0
  132. package/dist/components/Panel/Card.svelte +141 -0
  133. package/dist/components/Panel/Card.svelte.d.ts +52 -0
  134. package/dist/components/Panel/Hero/Hero.story.md +9 -0
  135. package/dist/components/Panel/Hero/Hero.story.svelte +49 -0
  136. package/dist/components/Panel/Hero/Hero.story.svelte.d.ts +21 -0
  137. package/dist/components/Panel/Hero/Hero.svelte +24 -0
  138. package/dist/components/Panel/Hero/Hero.svelte.d.ts +32 -0
  139. package/dist/components/Panel/LazyPanel.svelte +110 -0
  140. package/dist/components/Panel/LazyPanel.svelte.d.ts +46 -0
  141. package/dist/components/Panel/Panel.svelte +205 -0
  142. package/dist/components/Panel/Panel.svelte.d.ts +23 -0
  143. package/dist/components/Progress/Progress.svelte +220 -0
  144. package/dist/components/Progress/Progress.svelte.d.ts +61 -0
  145. package/dist/components/Separator/Separator.svelte +109 -0
  146. package/dist/components/Separator/Separator.svelte.d.ts +35 -0
  147. package/dist/components/Sidebar/Sidebar.svelte +213 -0
  148. package/dist/components/Sidebar/Sidebar.svelte.d.ts +60 -0
  149. package/dist/components/Skeleton/Skeleton.svelte +170 -0
  150. package/dist/components/Skeleton/Skeleton.svelte.d.ts +48 -0
  151. package/dist/components/Stepper/Stepper.svelte +111 -0
  152. package/dist/components/Stepper/Stepper.svelte.d.ts +54 -0
  153. package/dist/components/Stepper/StepperStep.svelte +369 -0
  154. package/dist/components/Stepper/StepperStep.svelte.d.ts +63 -0
  155. package/dist/components/Table/Table.svelte +167 -0
  156. package/dist/components/Table/Table.svelte.d.ts +56 -0
  157. package/dist/components/Table/TableBody.svelte +41 -0
  158. package/dist/components/Table/TableBody.svelte.d.ts +33 -0
  159. package/dist/components/Table/TableCell.svelte +76 -0
  160. package/dist/components/Table/TableCell.svelte.d.ts +36 -0
  161. package/dist/components/Table/TableHead.svelte +41 -0
  162. package/dist/components/Table/TableHead.svelte.d.ts +32 -0
  163. package/dist/components/Table/TableHeader.svelte +148 -0
  164. package/dist/components/Table/TableHeader.svelte.d.ts +42 -0
  165. package/dist/components/Table/TableRow.svelte +99 -0
  166. package/dist/components/Table/TableRow.svelte.d.ts +40 -0
  167. package/dist/components/Tabs/Tab.svelte +145 -0
  168. package/dist/components/Tabs/Tab.svelte.d.ts +36 -0
  169. package/dist/components/Tabs/TabList.svelte +60 -0
  170. package/dist/components/Tabs/TabList.svelte.d.ts +32 -0
  171. package/dist/components/Tabs/TabPanel.svelte +118 -0
  172. package/dist/components/Tabs/TabPanel.svelte.d.ts +38 -0
  173. package/dist/components/Tabs/Tabs.svelte +287 -0
  174. package/dist/components/Tabs/Tabs.svelte.d.ts +50 -0
  175. package/dist/components/Tag/Tag.svelte +260 -0
  176. package/dist/components/Tag/Tag.svelte.d.ts +54 -0
  177. package/dist/components/Tag/TagGroup.svelte +147 -0
  178. package/dist/components/Tag/TagGroup.svelte.d.ts +62 -0
  179. package/dist/components/ThemeToggle/ThemeToggle.svelte +93 -0
  180. package/dist/components/ThemeToggle/ThemeToggle.svelte.d.ts +12 -0
  181. package/dist/components/Timeline/Timeline.svelte +144 -0
  182. package/dist/components/Timeline/Timeline.svelte.d.ts +48 -0
  183. package/dist/components/Timeline/TimelineItem.svelte +391 -0
  184. package/dist/components/Timeline/TimelineItem.svelte.d.ts +63 -0
  185. package/dist/components/Toast/Toast.svelte +313 -0
  186. package/dist/components/Toast/Toast.svelte.d.ts +44 -0
  187. package/dist/components/Toast/toastStore.d.ts +40 -0
  188. package/dist/components/Toast/toastStore.js +293 -0
  189. package/dist/components/Tooltip/Tooltip.svelte +282 -0
  190. package/dist/components/Tooltip/Tooltip.svelte.d.ts +55 -0
  191. package/dist/components/Tree/Tree.svelte +129 -0
  192. package/dist/components/Tree/Tree.svelte.d.ts +61 -0
  193. package/dist/components/Tree/TreeNode.svelte +332 -0
  194. package/dist/components/Tree/TreeNode.svelte.d.ts +55 -0
  195. package/dist/components/icons/TwintrinsicLogo.svelte +73 -0
  196. package/dist/components/icons/TwintrinsicLogo.svelte.d.ts +17 -0
  197. package/dist/components/icons/twintrinsic-source.svg +73 -0
  198. package/dist/components/icons/twintrinsic.svg +38 -0
  199. package/dist/docs/EventsTable.svelte +86 -0
  200. package/dist/docs/EventsTable.svelte.d.ts +27 -0
  201. package/dist/docs/PropsTable.svelte +103 -0
  202. package/dist/docs/PropsTable.svelte.d.ts +28 -0
  203. package/dist/docs/index.d.ts +2 -0
  204. package/dist/docs/index.js +2 -0
  205. package/dist/helpers/detectLanguage.d.ts +6 -0
  206. package/dist/helpers/detectLanguage.js +60 -0
  207. package/dist/helpers/index.d.ts +1 -0
  208. package/dist/helpers/index.js +1 -0
  209. package/dist/index.d.ts +86 -0
  210. package/dist/index.js +94 -0
  211. package/dist/twintrinsic.css +347 -0
  212. package/package.json +98 -0
@@ -0,0 +1,190 @@
1
+ <!--
2
+ @component
3
+ ChipGroup - A container for managing multiple Chip components.
4
+ Provides consistent spacing, layout options, and keyboard navigation.
5
+
6
+ Usage:
7
+ ```svelte
8
+ <ChipGroup>
9
+ <Chip>Chip 1</Chip>
10
+ <Chip>Chip 2</Chip>
11
+ <Chip>Chip 3</Chip>
12
+ </ChipGroup>
13
+
14
+ <ChipGroup variant="primary" removable>
15
+ <Chip>JavaScript</Chip>
16
+ <Chip>TypeScript</Chip>
17
+ <Chip>Svelte</Chip>
18
+ </ChipGroup>
19
+
20
+ <ChipGroup
21
+ items={['Red', 'Green', 'Blue']}
22
+ let:item
23
+ selectable
24
+ onselect={handleSelect}
25
+ >
26
+ <Chip>{item}</Chip>
27
+ </ChipGroup>
28
+ ```
29
+ -->
30
+ <script>
31
+ import { setContext } from "svelte"
32
+
33
+ const {
34
+ /** @type {string} - Additional CSS classes */
35
+ class: className = "",
36
+
37
+ /** @type {string} - HTML id for accessibility */
38
+ id = crypto.randomUUID(),
39
+
40
+ /** @type {string} - Visual style variant passed to all chips */
41
+ variant = "default",
42
+
43
+ /** @type {string} - Size passed to all chips (sm, md, lg) */
44
+ size = "md",
45
+
46
+ /** @type {boolean} - Whether all chips are removable */
47
+ removable = false,
48
+
49
+ /** @type {boolean} - Whether all chips are clickable */
50
+ clickable = false,
51
+
52
+ /** @type {boolean} - Whether all chips are selectable */
53
+ selectable = false,
54
+
55
+ /** @type {boolean} - Whether multiple chips can be selected */
56
+ multiple = false,
57
+
58
+ /** @type {boolean} - Whether all chips are disabled */
59
+ disabled = false,
60
+
61
+ /** @type {boolean} - Whether all chips use outline style */
62
+ outline = false,
63
+
64
+ /** @type {string} - Direction of the chip group (horizontal, vertical) */
65
+ direction = "horizontal",
66
+
67
+ /** @type {Array} - Items to render as chips */
68
+ items = [],
69
+
70
+ /** @type {Array} - Selected items or indices */
71
+ selected = [],
72
+
73
+ /** @type {string} - ARIA label for the chip group */
74
+ ariaLabel = "Chip group",
75
+
76
+ /** @type {(event: CustomEvent) => void} - Select event handler */
77
+ onselect,
78
+ /** @type {(event: CustomEvent) => void} - Remove event handler */
79
+ onremove,
80
+
81
+ children,
82
+ } = $props()
83
+
84
+ // Component state
85
+ let selectedItems = $state(Array.isArray(selected) ? [...selected] : [])
86
+
87
+ // Update selected items when prop changes
88
+ $effect(() => {
89
+ selectedItems = Array.isArray(selected) ? [...selected] : []
90
+ })
91
+
92
+ // Provide context for child chips
93
+ $effect(() => {
94
+ setContext("chipGroup", {
95
+ variant,
96
+ size,
97
+ removable,
98
+ clickable,
99
+ selectable,
100
+ multiple,
101
+ disabled,
102
+ outline,
103
+ isSelected: (item) => selectedItems.includes(item),
104
+ toggleSelection: (item) => {
105
+ if (selectable) {
106
+ if (selectedItems.includes(item)) {
107
+ // Remove item if already selected
108
+ selectedItems = selectedItems.filter((i) => i !== item)
109
+ } else {
110
+ // Add item if not selected
111
+ if (multiple) {
112
+ selectedItems = [...selectedItems, item]
113
+ } else {
114
+ selectedItems = [item]
115
+ }
116
+ }
117
+ onselect?.(new CustomEvent("select", { detail: { selected: selectedItems } }))
118
+ }
119
+ },
120
+ })
121
+ })
122
+
123
+ /**
124
+ * Handles removing a chip
125
+ * @param {number} index - Index of the chip to remove
126
+ */
127
+ function handleRemove(index) {
128
+ if (items.length > 0) {
129
+ const newItems = [...items]
130
+ const removedItem = newItems.splice(index, 1)[0]
131
+ onremove?.(new CustomEvent("remove", { detail: { item: removedItem, index } }))
132
+ }
133
+ }
134
+ </script>
135
+
136
+ <div
137
+ {id}
138
+ class="
139
+ chip-group
140
+ chip-group-{direction}
141
+ {className}
142
+ "
143
+ role={selectable ? 'listbox' : 'group'}
144
+ aria-label={ariaLabel}
145
+ aria-multiselectable={selectable && multiple ? true : undefined}
146
+ >
147
+ {#if items.length > 0}
148
+ {#each items as item, index}
149
+ <div class="chip-group-item">
150
+ <svelte:component
151
+ this={children?.item}
152
+ {item}
153
+ {index}
154
+ variant={variant}
155
+ size={size}
156
+ removable={removable}
157
+ clickable={clickable || selectable}
158
+ disabled={disabled}
159
+ selected={selectedItems.includes(item)}
160
+ outline={outline}
161
+ onclick={() => selectable && !disabled && setContext('chipGroup').toggleSelection(item)}
162
+ onremove={() => handleRemove(index)}
163
+ />
164
+ </div>
165
+ {/each}
166
+ {:else}
167
+ {@render children?.()}
168
+ {/if}
169
+ </div>
170
+
171
+ <style>
172
+ @reference "../../twintrinsic.css";
173
+
174
+ .chip-group {
175
+ @apply flex flex-wrap;
176
+ @apply gap-2;
177
+ }
178
+
179
+ .chip-group-horizontal {
180
+ @apply flex-row;
181
+ }
182
+
183
+ .chip-group-vertical {
184
+ @apply flex-col;
185
+ }
186
+
187
+ .chip-group-item {
188
+ @apply flex-none;
189
+ }
190
+ </style>
@@ -0,0 +1,71 @@
1
+ export default ChipGroup;
2
+ type ChipGroup = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ /**
7
+ * ChipGroup - A container for managing multiple Chip components.
8
+ * Provides consistent spacing, layout options, and keyboard navigation.
9
+ *
10
+ * Usage:
11
+ * ```svelte
12
+ * <ChipGroup>
13
+ * <Chip>Chip 1</Chip>
14
+ * <Chip>Chip 2</Chip>
15
+ * <Chip>Chip 3</Chip>
16
+ * </ChipGroup>
17
+ *
18
+ * <ChipGroup variant="primary" removable>
19
+ * <Chip>JavaScript</Chip>
20
+ * <Chip>TypeScript</Chip>
21
+ * <Chip>Svelte</Chip>
22
+ * </ChipGroup>
23
+ *
24
+ * <ChipGroup
25
+ * items={['Red', 'Green', 'Blue']}
26
+ * let:item
27
+ * selectable
28
+ * onselect={handleSelect}
29
+ * >
30
+ * <Chip>{item}</Chip>
31
+ * </ChipGroup>
32
+ * ```
33
+ */
34
+ declare const ChipGroup: import("svelte").Component<{
35
+ class?: string;
36
+ id?: any;
37
+ variant?: string;
38
+ size?: string;
39
+ removable?: boolean;
40
+ clickable?: boolean;
41
+ selectable?: boolean;
42
+ multiple?: boolean;
43
+ disabled?: boolean;
44
+ outline?: boolean;
45
+ direction?: string;
46
+ items?: any[];
47
+ selected?: any[];
48
+ ariaLabel?: string;
49
+ onselect: any;
50
+ onremove: any;
51
+ children: any;
52
+ }, {}, "">;
53
+ type $$ComponentProps = {
54
+ class?: string;
55
+ id?: any;
56
+ variant?: string;
57
+ size?: string;
58
+ removable?: boolean;
59
+ clickable?: boolean;
60
+ selectable?: boolean;
61
+ multiple?: boolean;
62
+ disabled?: boolean;
63
+ outline?: boolean;
64
+ direction?: string;
65
+ items?: any[];
66
+ selected?: any[];
67
+ ariaLabel?: string;
68
+ onselect: any;
69
+ onremove: any;
70
+ children: any;
71
+ };
@@ -0,0 +1,356 @@
1
+ <!--
2
+ @component
3
+ CodeBlock - A component for displaying code snippets with syntax highlighting and copy functionality.
4
+
5
+ Features:
6
+ - Syntax highlighting with Prism.js
7
+ - Automatic language detection
8
+ - Dynamic language loading via Prism.js autoloader plugin
9
+ - Copy to clipboard functionality
10
+ - Support for multiple CDNs (unpkg, esm.sh, jsdelivr)
11
+
12
+ > NOTE: The slot needs to be outdented as far as possible so the child contents' first line isn't indented.
13
+
14
+ Usage:
15
+ ```svelte
16
+ <CodeBlock language="javascript">
17
+ const hello = 'world';
18
+ console.log(hello);
19
+ </CodeBlock>
20
+
21
+ <CodeBlock>
22
+ &lt;!-- Auto-detects language --&gt;
23
+ const hello = 'world';
24
+ </CodeBlock>
25
+
26
+ <CodeBlock language="python" cdn="jsdelivr">
27
+ print('hello world')
28
+ </CodeBlock>
29
+
30
+ <CodeBlock cdn="esm.sh" languagesPath="https://custom.cdn/prismjs/components/">
31
+ &lt;!-- Custom CDN and language path --&gt;
32
+ const code = 'example';
33
+ </CodeBlock>
34
+ ```
35
+ -->
36
+ <script lang="ts">
37
+ import Prism from "prismjs";
38
+ import { onDestroy, onMount } from "svelte";
39
+ import { detectLanguage } from "../../helpers";
40
+ // import "prism-svelte";
41
+ import "prismjs/plugins/autoloader/prism-autoloader";
42
+
43
+ const {
44
+ /** @type {string} - The language for syntax highlighting */
45
+ language = "",
46
+ /** @type {string} - Additional CSS classes */
47
+ class: className = "",
48
+ /** @type { "unpkg" | "esm.sh" | "jsdelivr" | string } - CDN to use for autoloader, or custom path to prism components folder*/
49
+ pluginSource = "unpkg",
50
+ /** @type {string[]} - List of plugin names (e.g., "autoloader") or full paths to load */
51
+ plugins = [],
52
+ /** @type {import('svelte').Snippet} - The code to display */
53
+ children = ""
54
+ } = $props()
55
+
56
+ let code = $state("")
57
+ let copied = $state(false)
58
+ let copyTimeout = $state()
59
+ let codeElement = $state()
60
+
61
+
62
+ /**
63
+ * Get CDN URL for a plugin
64
+ * @param {string} plugin - Plugin name or full path
65
+ * @param {string} pluginSource - CDN name
66
+ * @returns {string} Full URL to plugin
67
+ */
68
+ function getPluginUrl(plugin, pluginSource) {
69
+ // If it's a full path (contains :// or starts with /), return as-is
70
+ if (plugin.includes("://") || plugin.startsWith("/")) {
71
+ return plugin;
72
+ }
73
+
74
+ // Otherwise, it's a plugin name - construct URL based on CDN
75
+ const pluginName = plugin.startsWith("prism-") ? plugin : `prism-${plugin}`;
76
+
77
+ switch (pluginSource) {
78
+ case "esm.sh":
79
+ return `https://esm.sh/prismjs@1/plugins/${plugin}/${pluginName}.min.js`;
80
+ case "jsdelivr":
81
+ return `https://cdn.jsdelivr.net/npm/prismjs@1/plugins/${plugin}/${pluginName}.min.js`;
82
+ case "unpkg":
83
+ default:
84
+ return `https://unpkg.com/prismjs@1/plugins/${plugin}/${pluginName}.min.js`;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Load plugins via script tags
90
+ * @param {string[]} pluginList - List of plugin names or paths
91
+ */
92
+ async function loadPlugins(pluginList) {
93
+ for (const plugin of pluginList) {
94
+ const url = getPluginUrl(plugin, pluginSource);
95
+ await new Promise<void>((resolve, reject) => {
96
+ const script = document.createElement("script");
97
+ script.src = url;
98
+ script.onload = () => resolve();
99
+ script.onerror = () => reject(new Error(`Failed to load plugin: ${url}`));
100
+ document.head.appendChild(script);
101
+ });
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Get languages path for autoloader
107
+ * @param {string} pluginSource - CDN name or path to a custom cdn
108
+ * @returns {string} Path to language grammars
109
+ */
110
+ function getLanguagesPath(pluginSource) {
111
+
112
+ switch (pluginSource) {
113
+ case "esm.sh":
114
+ return "https://esm.sh/prismjs@1/components/"
115
+ case "jsdelivr":
116
+ return "https://cdn.jsdelivr.net/npm/prismjs@1/components/"
117
+ case "unpkg":
118
+ return "https://unpkg.com/prismjs@1/components/"
119
+ default:
120
+ return pluginSource;
121
+ }
122
+ }
123
+
124
+ // Initialize plugins and highlight code
125
+ onMount(async () => {
126
+ if (plugins.length > 0) {
127
+ await loadPlugins(plugins);
128
+ }
129
+ highlightCode()
130
+ })
131
+
132
+ /**
133
+ * determine the path to the language components based on the plugin source.
134
+ * @param {string} language - The language to determine the path for
135
+ * @returns {[string, boolean]} The path to the language components and whether to use minified files
136
+ */
137
+ function determineComponentsDirectory(language) {
138
+ if(language === "svelte") {
139
+ switch (pluginSource) {
140
+ case "esm.sh":
141
+ return ["https://esm.sh/prism-svelte", true]
142
+ case "jsdelivr":
143
+ return ["https://cdn.jsdelivr.net/npm/prism-svelte", true]
144
+ case "unpkg":
145
+ return ["https://unpkg.com/prism-svelte", true]
146
+ default:
147
+ return [getLanguagesPath(pluginSource), false];
148
+ }
149
+ }
150
+ return [getLanguagesPath(pluginSource), false];
151
+ }
152
+
153
+ /**
154
+ * Highlight code with language detection
155
+ */
156
+ async function highlightCode() {
157
+ if (!codeElement) return
158
+
159
+ code = codeElement.textContent || ""
160
+
161
+ // Auto-detect language if not specified
162
+ const detectedLang = language || detectLanguage(code);
163
+
164
+ const [languagesPath, isFullPath] = determineComponentsDirectory(detectedLang);
165
+
166
+ // If language not already loaded, load it
167
+ if (detectedLang && !Prism.languages[detectedLang]) {
168
+ if (isFullPath) {
169
+ // For full paths (like prism-svelte), load the script directly
170
+ await new Promise<void>((resolve, reject) => {
171
+ const script = document.createElement('script');
172
+ script.src = languagesPath;
173
+ script.onload = () => resolve();
174
+ script.onerror = () => reject(new Error(`Failed to load ${languagesPath}`));
175
+ document.head.appendChild(script);
176
+ });
177
+ } else if (Prism.plugins?.autoloader) {
178
+ // For standard paths, use autoloader with custom languages_path
179
+ Prism.plugins.autoloader.languages_path = languagesPath;
180
+ await new Promise<void>((resolve) => {
181
+ Prism.plugins.autoloader.loadLanguages(detectedLang, () => {
182
+ resolve()
183
+ })
184
+ })
185
+ }
186
+ }
187
+
188
+ // Highlight code
189
+ if (detectedLang) {
190
+ const grammar = Prism.languages[detectedLang]
191
+ if (grammar) {
192
+ const highlighted = Prism.highlight(code, grammar, detectedLang)
193
+ codeElement.innerHTML = highlighted
194
+ }
195
+ }
196
+ }
197
+
198
+ // Clean up copy timeout
199
+ onDestroy(() => {
200
+ if (copyTimeout) {
201
+ clearTimeout(copyTimeout)
202
+ }
203
+ })
204
+
205
+ /**
206
+ * Copy code to clipboard
207
+ */
208
+ async function copyCode() {
209
+ if (copied) return
210
+
211
+ try {
212
+ await navigator.clipboard.writeText(code)
213
+ copied = true
214
+
215
+ copyTimeout = setTimeout(() => {
216
+ copied = false
217
+ }, 2000)
218
+ } catch (error) {
219
+ console.error("Failed to copy code:", error)
220
+ }
221
+ }
222
+ </script>
223
+
224
+ <div class="code-block {className}">
225
+ <div class="code-header">
226
+ {#if language}
227
+ <div class="code-language">{language}</div>
228
+ {/if}
229
+ <button
230
+ type="button"
231
+ class="code-copy"
232
+ onclick={copyCode}
233
+ aria-label={copied ? 'Copied!' : 'Copy code'}
234
+ >
235
+ {#if copied}
236
+ <svg viewBox="0 0 24 24" width="16" height="16">
237
+ <path
238
+ fill="currentColor"
239
+ d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"
240
+ />
241
+ </svg>
242
+ <span>Copied!</span>
243
+ {:else}
244
+ <svg viewBox="0 0 24 24" width="16" height="16">
245
+ <path
246
+ fill="currentColor"
247
+ d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"
248
+ />
249
+ </svg>
250
+ <span>Copy</span>
251
+ {/if}
252
+ </button>
253
+ </div>
254
+
255
+ <pre class="code-pre {plugins.join(' ')}"><code
256
+ bind:this={codeElement}
257
+ class="language-{language || 'javascript'}"
258
+ >{@render children?.()}</code></pre>
259
+ </div>
260
+
261
+ <style>
262
+ @reference '../../twintrinsic.css';
263
+
264
+ .code-block {
265
+ @apply relative my-4 rounded-lg overflow-hidden;
266
+ @apply bg-surface border border-border;
267
+ }
268
+
269
+ .code-header {
270
+ @apply flex items-center justify-between px-4 py-2;
271
+ @apply bg-surface border-b border-border;
272
+ }
273
+
274
+ .code-language {
275
+ @apply text-xs font-mono text-muted;
276
+ }
277
+
278
+ .code-copy {
279
+ @apply flex items-center gap-2 px-2 py-1;
280
+ @apply text-xs font-medium text-muted;
281
+ @apply rounded hover:bg-hover;
282
+ @apply transition-colors duration-150;
283
+ @apply focus:outline-none focus:ring-2 focus:ring-primary/50;
284
+ }
285
+
286
+ .code-pre {
287
+ @apply m-0 p-4 overflow-x-auto;
288
+ @apply font-mono text-sm;
289
+ @apply bg-surface dark:bg-surface;
290
+ }
291
+
292
+ :global(.code-pre .token.comment),
293
+ :global(.code-pre .token.prolog),
294
+ :global(.code-pre .token.doctype),
295
+ :global(.code-pre .token.cdata) {
296
+ @apply text-muted dark:text-muted;
297
+ }
298
+
299
+ :global(.code-pre .token.punctuation) {
300
+ @apply text-primary-600 dark:text-primary-400;
301
+ }
302
+
303
+ :global(.code-pre .token.property),
304
+ :global(.code-pre .token.tag),
305
+ :global(.code-pre .token.boolean),
306
+ :global(.code-pre .token.number),
307
+ :global(.code-pre .token.constant),
308
+ :global(.code-pre .token.symbol) {
309
+ @apply text-primary-700 dark:text-primary-300;
310
+ }
311
+
312
+ :global(.code-pre .token.selector),
313
+ :global(.code-pre .token.attr-name),
314
+ :global(.code-pre .token.string),
315
+ :global(.code-pre .token.char),
316
+ :global(.code-pre .token.builtin) {
317
+ @apply text-success;
318
+ }
319
+
320
+ :global(.code-pre .token.operator),
321
+ :global(.code-pre .token.entity),
322
+ :global(.code-pre .token.url),
323
+ :global(.code-pre .language-css .token.string),
324
+ :global(.code-pre .style .token.string) {
325
+ @apply text-warning;
326
+ }
327
+
328
+ :global(.code-pre .token.atrule),
329
+ :global(.code-pre .token.attr-value),
330
+ :global(.code-pre .token.keyword) {
331
+ @apply text-info;
332
+ }
333
+
334
+ :global(.code-pre .token.function),
335
+ :global(.code-pre .token.class-name) {
336
+ @apply text-error;
337
+ }
338
+
339
+ :global(.code-pre .token.regex),
340
+ :global(.code-pre .token.important) {
341
+ @apply text-warning;
342
+ }
343
+
344
+ :global(.code-pre .token.important),
345
+ :global(.code-pre .token.bold) {
346
+ @apply font-bold;
347
+ }
348
+
349
+ :global(.code-pre .token.italic) {
350
+ @apply italic;
351
+ }
352
+
353
+ :global(.code-pre .token.entity) {
354
+ @apply cursor-help;
355
+ }
356
+ </style>
@@ -0,0 +1,44 @@
1
+ import "prismjs/plugins/autoloader/prism-autoloader";
2
+ /**
3
+ * CodeBlock - A component for displaying code snippets with syntax highlighting and copy functionality.
4
+ *
5
+ * Features:
6
+ * - Syntax highlighting with Prism.js
7
+ * - Automatic language detection
8
+ * - Dynamic language loading via Prism.js autoloader plugin
9
+ * - Copy to clipboard functionality
10
+ * - Support for multiple CDNs (unpkg, esm.sh, jsdelivr)
11
+ *
12
+ * > NOTE: The slot needs to be outdented as far as possible so the child contents' first line isn't indented.
13
+ *
14
+ * Usage:
15
+ * ```svelte
16
+ * <CodeBlock language="javascript">
17
+ * const hello = 'world';
18
+ * console.log(hello);
19
+ * </CodeBlock>
20
+ *
21
+ * <CodeBlock>
22
+ * &lt;!-- Auto-detects language --&gt;
23
+ * const hello = 'world';
24
+ * </CodeBlock>
25
+ *
26
+ * <CodeBlock language="python" cdn="jsdelivr">
27
+ * print('hello world')
28
+ * </CodeBlock>
29
+ *
30
+ * <CodeBlock cdn="esm.sh" languagesPath="https://custom.cdn/prismjs/components/">
31
+ * &lt;!-- Custom CDN and language path --&gt;
32
+ * const code = 'example';
33
+ * </CodeBlock>
34
+ * ```
35
+ */
36
+ declare const CodeBlock: import("svelte").Component<{
37
+ language?: string;
38
+ class?: string;
39
+ pluginSource?: string;
40
+ plugins?: any[];
41
+ children?: string;
42
+ }, {}, "">;
43
+ type CodeBlock = ReturnType<typeof CodeBlock>;
44
+ export default CodeBlock;
@@ -0,0 +1 @@
1
+ export { default as CodeBlock } from "./CodeBlock.svelte";
@@ -0,0 +1 @@
1
+ export { default as CodeBlock } from "./CodeBlock.svelte";