components-svelte 0.1.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 (242) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +103 -0
  3. package/dist/components/Search.svelte +320 -0
  4. package/dist/components/Search.svelte.d.ts +31 -0
  5. package/dist/components/SearchButton.svelte +82 -0
  6. package/dist/components/SearchButton.svelte.d.ts +13 -0
  7. package/dist/components/SearchProvider.svelte +87 -0
  8. package/dist/components/SearchProvider.svelte.d.ts +17 -0
  9. package/dist/components/accordion/Accordion.svelte +150 -0
  10. package/dist/components/accordion/Accordion.svelte.d.ts +25 -0
  11. package/dist/components/accordion/AccordionCover.svelte +68 -0
  12. package/dist/components/accordion/AccordionCover.svelte.d.ts +13 -0
  13. package/dist/components/accordion/AccordionGroup.svelte +23 -0
  14. package/dist/components/accordion/AccordionGroup.svelte.d.ts +8 -0
  15. package/dist/components/accordion/accordion-url-utils.d.ts +4 -0
  16. package/dist/components/accordion/accordion-url-utils.js +48 -0
  17. package/dist/components/accordion/index.d.ts +29 -0
  18. package/dist/components/accordion/index.js +6 -0
  19. package/dist/components/badge/Badge.svelte +152 -0
  20. package/dist/components/badge/Badge.svelte.d.ts +23 -0
  21. package/dist/components/badge/index.d.ts +1 -0
  22. package/dist/components/badge/index.js +2 -0
  23. package/dist/components/callout/Callout.svelte +317 -0
  24. package/dist/components/callout/Callout.svelte.d.ts +17 -0
  25. package/dist/components/callout/Check.svelte +9 -0
  26. package/dist/components/callout/Check.svelte.d.ts +4 -0
  27. package/dist/components/callout/Danger.svelte +9 -0
  28. package/dist/components/callout/Danger.svelte.d.ts +4 -0
  29. package/dist/components/callout/Info.svelte +9 -0
  30. package/dist/components/callout/Info.svelte.d.ts +4 -0
  31. package/dist/components/callout/Note.svelte +9 -0
  32. package/dist/components/callout/Note.svelte.d.ts +4 -0
  33. package/dist/components/callout/Tip.svelte +9 -0
  34. package/dist/components/callout/Tip.svelte.d.ts +4 -0
  35. package/dist/components/callout/Warning.svelte +9 -0
  36. package/dist/components/callout/Warning.svelte.d.ts +4 -0
  37. package/dist/components/callout/index.d.ts +8 -0
  38. package/dist/components/callout/index.js +8 -0
  39. package/dist/components/card/Card.svelte +172 -0
  40. package/dist/components/card/Card.svelte.d.ts +21 -0
  41. package/dist/components/card/index.d.ts +3 -0
  42. package/dist/components/card/index.js +2 -0
  43. package/dist/components/code-block/BaseCodeBlock.svelte +249 -0
  44. package/dist/components/code-block/BaseCodeBlock.svelte.d.ts +10 -0
  45. package/dist/components/code-block/CodeBlock.svelte +185 -0
  46. package/dist/components/code-block/CodeBlock.svelte.d.ts +5 -0
  47. package/dist/components/code-block/CodeFooter.svelte +34 -0
  48. package/dist/components/code-block/CodeFooter.svelte.d.ts +8 -0
  49. package/dist/components/code-block/CodeHeader.svelte +52 -0
  50. package/dist/components/code-block/CodeHeader.svelte.d.ts +11 -0
  51. package/dist/components/code-block/CopyToClipboardButton.svelte +124 -0
  52. package/dist/components/code-block/CopyToClipboardButton.svelte.d.ts +4 -0
  53. package/dist/components/code-block/index.d.ts +10 -0
  54. package/dist/components/code-block/index.js +5 -0
  55. package/dist/components/code-block/types.d.ts +85 -0
  56. package/dist/components/code-block/types.js +1 -0
  57. package/dist/components/code-group/CodeGroup.svelte +327 -0
  58. package/dist/components/code-group/CodeGroup.svelte.d.ts +24 -0
  59. package/dist/components/code-group/CodeGroupSelect.svelte +170 -0
  60. package/dist/components/code-group/CodeGroupSelect.svelte.d.ts +25 -0
  61. package/dist/components/code-group/CodeSelectDropdown.svelte +74 -0
  62. package/dist/components/code-group/CodeSelectDropdown.svelte.d.ts +9 -0
  63. package/dist/components/code-group/LanguageDropdown.svelte +134 -0
  64. package/dist/components/code-group/LanguageDropdown.svelte.d.ts +11 -0
  65. package/dist/components/code-group/LanguageIcon.svelte +24 -0
  66. package/dist/components/code-group/LanguageIcon.svelte.d.ts +8 -0
  67. package/dist/components/code-group/context.d.ts +14 -0
  68. package/dist/components/code-group/context.js +2 -0
  69. package/dist/components/code-group/index.d.ts +8 -0
  70. package/dist/components/code-group/index.js +4 -0
  71. package/dist/components/color/Color.svelte +34 -0
  72. package/dist/components/color/Color.svelte.d.ts +10 -0
  73. package/dist/components/color/ColorItem.svelte +140 -0
  74. package/dist/components/color/ColorItem.svelte.d.ts +10 -0
  75. package/dist/components/color/ColorRow.svelte +27 -0
  76. package/dist/components/color/ColorRow.svelte.d.ts +8 -0
  77. package/dist/components/color/index.d.ts +1 -0
  78. package/dist/components/color/index.js +8 -0
  79. package/dist/components/columns/Box.svelte +15 -0
  80. package/dist/components/columns/Box.svelte.d.ts +7 -0
  81. package/dist/components/columns/Columns.svelte +31 -0
  82. package/dist/components/columns/Columns.svelte.d.ts +10 -0
  83. package/dist/components/columns/constants.d.ts +5 -0
  84. package/dist/components/columns/constants.js +3 -0
  85. package/dist/components/columns/index.d.ts +5 -0
  86. package/dist/components/columns/index.js +3 -0
  87. package/dist/components/expandable/Expandable.svelte +182 -0
  88. package/dist/components/expandable/Expandable.svelte.d.ts +22 -0
  89. package/dist/components/expandable/index.d.ts +3 -0
  90. package/dist/components/expandable/index.js +2 -0
  91. package/dist/components/frame/Frame.svelte +118 -0
  92. package/dist/components/frame/Frame.svelte.d.ts +15 -0
  93. package/dist/components/frame/index.d.ts +3 -0
  94. package/dist/components/frame/index.js +2 -0
  95. package/dist/components/icon/Icon.svelte +129 -0
  96. package/dist/components/icon/Icon.svelte.d.ts +18 -0
  97. package/dist/components/icon/index.d.ts +2 -0
  98. package/dist/components/icon/index.js +1 -0
  99. package/dist/components/index.d.ts +22 -0
  100. package/dist/components/index.js +22 -0
  101. package/dist/components/mermaid/Mermaid.svelte +181 -0
  102. package/dist/components/mermaid/Mermaid.svelte.d.ts +11 -0
  103. package/dist/components/mermaid/ZoomControls.svelte +74 -0
  104. package/dist/components/mermaid/ZoomControls.svelte.d.ts +12 -0
  105. package/dist/components/mermaid/index.d.ts +7 -0
  106. package/dist/components/mermaid/index.js +4 -0
  107. package/dist/components/mermaid/use-pan-zoom.svelte.d.ts +11 -0
  108. package/dist/components/mermaid/use-pan-zoom.svelte.js +41 -0
  109. package/dist/components/panel/Panel.svelte +27 -0
  110. package/dist/components/panel/Panel.svelte.d.ts +9 -0
  111. package/dist/components/panel/index.d.ts +3 -0
  112. package/dist/components/panel/index.js +2 -0
  113. package/dist/components/property/DeprecatedPill.svelte +16 -0
  114. package/dist/components/property/DeprecatedPill.svelte.d.ts +6 -0
  115. package/dist/components/property/InfoPill.svelte +32 -0
  116. package/dist/components/property/InfoPill.svelte.d.ts +10 -0
  117. package/dist/components/property/ParamHead.svelte +188 -0
  118. package/dist/components/property/ParamHead.svelte.d.ts +22 -0
  119. package/dist/components/property/Property.svelte +123 -0
  120. package/dist/components/property/Property.svelte.d.ts +24 -0
  121. package/dist/components/property/RequiredPill.svelte +16 -0
  122. package/dist/components/property/RequiredPill.svelte.d.ts +6 -0
  123. package/dist/components/property/index.d.ts +11 -0
  124. package/dist/components/property/index.js +6 -0
  125. package/dist/components/search/index.d.ts +7 -0
  126. package/dist/components/search/index.js +4 -0
  127. package/dist/components/search/use-search.d.ts +9 -0
  128. package/dist/components/search/use-search.js +6 -0
  129. package/dist/components/steps/Step.svelte +314 -0
  130. package/dist/components/steps/Step.svelte.d.ts +26 -0
  131. package/dist/components/steps/Steps.svelte +51 -0
  132. package/dist/components/steps/Steps.svelte.d.ts +11 -0
  133. package/dist/components/steps/constants.d.ts +4 -0
  134. package/dist/components/steps/constants.js +2 -0
  135. package/dist/components/steps/context.d.ts +10 -0
  136. package/dist/components/steps/context.js +2 -0
  137. package/dist/components/steps/index.d.ts +32 -0
  138. package/dist/components/steps/index.js +6 -0
  139. package/dist/components/tabs/Tabs.svelte +254 -0
  140. package/dist/components/tabs/Tabs.svelte.d.ts +16 -0
  141. package/dist/components/tabs/TabsItem.svelte +56 -0
  142. package/dist/components/tabs/TabsItem.svelte.d.ts +15 -0
  143. package/dist/components/tabs/context.d.ts +20 -0
  144. package/dist/components/tabs/context.js +2 -0
  145. package/dist/components/tabs/index.d.ts +27 -0
  146. package/dist/components/tabs/index.js +5 -0
  147. package/dist/components/tile/Tile.svelte +124 -0
  148. package/dist/components/tile/Tile.svelte.d.ts +12 -0
  149. package/dist/components/tile/index.d.ts +2 -0
  150. package/dist/components/tile/index.js +1 -0
  151. package/dist/components/tooltip/Tooltip.svelte +170 -0
  152. package/dist/components/tooltip/Tooltip.svelte.d.ts +23 -0
  153. package/dist/components/tooltip/index.d.ts +2 -0
  154. package/dist/components/tooltip/index.js +1 -0
  155. package/dist/components/tree/Tree.svelte +226 -0
  156. package/dist/components/tree/Tree.svelte.d.ts +9 -0
  157. package/dist/components/tree/TreeFile.svelte +44 -0
  158. package/dist/components/tree/TreeFile.svelte.d.ts +6 -0
  159. package/dist/components/tree/TreeFolder.svelte +107 -0
  160. package/dist/components/tree/TreeFolder.svelte.d.ts +10 -0
  161. package/dist/components/tree/context.d.ts +3 -0
  162. package/dist/components/tree/context.js +7 -0
  163. package/dist/components/tree/index.d.ts +10 -0
  164. package/dist/components/tree/index.js +9 -0
  165. package/dist/components/tree/tree-level-context.d.ts +8 -0
  166. package/dist/components/tree/tree-level-context.js +7 -0
  167. package/dist/components/tree/use-tree-level.d.ts +3 -0
  168. package/dist/components/tree/use-tree-level.js +5 -0
  169. package/dist/components/tree/utils.d.ts +9 -0
  170. package/dist/components/tree/utils.js +86 -0
  171. package/dist/components/update/Update.svelte +210 -0
  172. package/dist/components/update/Update.svelte.d.ts +34 -0
  173. package/dist/components/update/index.d.ts +4 -0
  174. package/dist/components/update/index.js +3 -0
  175. package/dist/components/view/View.svelte +52 -0
  176. package/dist/components/view/View.svelte.d.ts +14 -0
  177. package/dist/components/view/index.d.ts +3 -0
  178. package/dist/components/view/index.js +2 -0
  179. package/dist/constants/brands.d.ts +2 -0
  180. package/dist/constants/brands.js +4 -0
  181. package/dist/constants/index.d.ts +6 -0
  182. package/dist/constants/index.js +6 -0
  183. package/dist/constants/selectors.d.ts +125 -0
  184. package/dist/constants/selectors.js +125 -0
  185. package/dist/hooks/index.d.ts +8 -0
  186. package/dist/hooks/index.js +6 -0
  187. package/dist/hooks/use-expandable-memory.svelte.d.ts +6 -0
  188. package/dist/hooks/use-expandable-memory.svelte.js +89 -0
  189. package/dist/hooks/use-expandable.svelte.d.ts +15 -0
  190. package/dist/hooks/use-expandable.svelte.js +53 -0
  191. package/dist/hooks/use-get-shiki-highlighted-html.svelte.d.ts +8 -0
  192. package/dist/hooks/use-get-shiki-highlighted-html.svelte.js +36 -0
  193. package/dist/hooks/use-has-hover.d.ts +2 -0
  194. package/dist/hooks/use-has-hover.js +10 -0
  195. package/dist/hooks/use-is-dark-theme.svelte.d.ts +3 -0
  196. package/dist/hooks/use-is-dark-theme.svelte.js +25 -0
  197. package/dist/hooks/use-isomorphic-layout-effect.d.ts +3 -0
  198. package/dist/hooks/use-isomorphic-layout-effect.js +6 -0
  199. package/dist/index.d.ts +2 -0
  200. package/dist/index.js +2 -0
  201. package/dist/styles.css +2 -0
  202. package/dist/utils/cn.d.ts +3 -0
  203. package/dist/utils/cn.js +7 -0
  204. package/dist/utils/copy-to-clipboard.d.ts +4 -0
  205. package/dist/utils/copy-to-clipboard.js +50 -0
  206. package/dist/utils/dedent.d.ts +1 -0
  207. package/dist/utils/dedent.js +125 -0
  208. package/dist/utils/enhance-video-props.d.ts +4 -0
  209. package/dist/utils/enhance-video-props.js +25 -0
  210. package/dist/utils/get-node-text.d.ts +2 -0
  211. package/dist/utils/get-node-text.js +26 -0
  212. package/dist/utils/icon-utils.d.ts +18 -0
  213. package/dist/utils/icon-utils.js +30 -0
  214. package/dist/utils/is-absolute-url.d.ts +4 -0
  215. package/dist/utils/is-absolute-url.js +11 -0
  216. package/dist/utils/is-remote-url.d.ts +2 -0
  217. package/dist/utils/is-remote-url.js +12 -0
  218. package/dist/utils/make-marker-ids-unique.d.ts +2 -0
  219. package/dist/utils/make-marker-ids-unique.js +36 -0
  220. package/dist/utils/shiki/code-styling.d.ts +13 -0
  221. package/dist/utils/shiki/code-styling.js +1 -0
  222. package/dist/utils/shiki/constants.d.ts +21 -0
  223. package/dist/utils/shiki/constants.js +116 -0
  224. package/dist/utils/shiki/get-code-styling.d.ts +9 -0
  225. package/dist/utils/shiki/get-code-styling.js +38 -0
  226. package/dist/utils/shiki/get-shiki-background-colors.d.ts +20 -0
  227. package/dist/utils/shiki/get-shiki-background-colors.js +56 -0
  228. package/dist/utils/shiki/index.d.ts +30 -0
  229. package/dist/utils/shiki/index.js +209 -0
  230. package/dist/utils/shiki/lib.d.ts +4 -0
  231. package/dist/utils/shiki/lib.js +49 -0
  232. package/dist/utils/shiki/shiki-themes.d.ts +2 -0
  233. package/dist/utils/shiki/shiki-themes.js +64 -0
  234. package/dist/utils/shiki/snippet-presets.d.ts +27 -0
  235. package/dist/utils/shiki/snippet-presets.js +44 -0
  236. package/dist/utils/shiki/worker-client.d.ts +9 -0
  237. package/dist/utils/shiki/worker-client.js +22 -0
  238. package/dist/utils/shiki/worker.d.ts +1 -0
  239. package/dist/utils/shiki/worker.js +134 -0
  240. package/dist/utils/slugify.d.ts +2 -0
  241. package/dist/utils/slugify.js +9 -0
  242. package/package.json +100 -0
@@ -0,0 +1,249 @@
1
+ <script lang="ts">
2
+ import CodeFooter from "./CodeFooter.svelte";
3
+ import type { CodeBlockProps } from "./types";
4
+ import { cn } from "../../utils/cn";
5
+ import {
6
+ getShikiHighlightedHtml,
7
+ type ShikiHighlightedHtmlArgs,
8
+ } from "../../utils/shiki";
9
+ import { useCalculateCodeLines } from "../../utils/shiki/lib";
10
+ import { getShikiBackgroundColors } from "../../utils/shiki/get-shiki-background-colors";
11
+
12
+ export type BaseCodeBlockProps = CodeBlockProps & {
13
+ isParentCodeGroup?: boolean;
14
+ shouldHighlight?: boolean;
15
+ forceExtract?: boolean;
16
+ codeString?: string;
17
+ };
18
+
19
+ const SMALL_EXPANDABLE_NUMBER_OF_LINES = 7;
20
+ const SMALL_EXPANDABLE_HEIGHT_OFFSET = 30;
21
+ const EXPANDED_HEIGHT_OFFSET = 40;
22
+ const SMALL_EXPANDABLE_CODE_BLOCK_HEIGHT = 45;
23
+ const DEFAULT_EXPANDABLE_CODE_BLOCK_HEIGHT = 190;
24
+
25
+ let {
26
+ expandable = false,
27
+ shouldHighlight = true,
28
+ children,
29
+ filename,
30
+ isParentCodeGroup,
31
+ isSmallText,
32
+ numberOfLines,
33
+ codeBlockTheme = "system",
34
+ codeBlockThemeObject = "system",
35
+ codeString = "",
36
+ class: className,
37
+ focus,
38
+ highlight,
39
+ language,
40
+ lang,
41
+ wrap,
42
+ lines,
43
+ }: BaseCodeBlockProps = $props();
44
+
45
+ function parseLineNumbers(value: string | undefined): number[] {
46
+ if (!value) {
47
+ return [];
48
+ }
49
+
50
+ try {
51
+ const parsed = JSON.parse(value);
52
+ return Array.isArray(parsed) ? parsed : [];
53
+ } catch {
54
+ return [];
55
+ }
56
+ }
57
+
58
+ let focusLines = $derived(parseLineNumbers(focus));
59
+ let highlightLines = $derived(parseLineNumbers(highlight));
60
+ let opts = $derived.by(() => {
61
+ const options: {
62
+ highlightedLines?: number[];
63
+ focusedLines?: number[];
64
+ } = {};
65
+
66
+ if (highlightLines.length > 0) {
67
+ options.highlightedLines = highlightLines;
68
+ }
69
+ if (focusLines.length > 0) {
70
+ options.focusedLines = focusLines;
71
+ }
72
+
73
+ return options;
74
+ });
75
+
76
+ let selectedLang = $derived(lang || language);
77
+ let html = $state<string | undefined>();
78
+ // Keep the request counter non-reactive. It guards stale async results but
79
+ // must not become a dependency of the highlighting effect that increments it.
80
+ let highlightRequest = 0;
81
+
82
+ $effect(() => {
83
+ const request: string | undefined | Promise<string | undefined> =
84
+ selectedLang
85
+ ? getShikiHighlightedHtml({
86
+ codeString,
87
+ codeBlockTheme: codeBlockThemeObject,
88
+ language: shouldHighlight ? selectedLang : "text",
89
+ opts,
90
+ } satisfies ShikiHighlightedHtmlArgs)
91
+ : getShikiHighlightedHtml({
92
+ codeString,
93
+ codeBlockTheme: codeBlockThemeObject,
94
+ className: shouldHighlight ? className : "lang-text",
95
+ opts,
96
+ } satisfies ShikiHighlightedHtmlArgs);
97
+
98
+ const requestId = highlightRequest + 1;
99
+ highlightRequest = requestId;
100
+
101
+ if (request instanceof Promise) {
102
+ html = undefined;
103
+ void request.then((result) => {
104
+ if (requestId === highlightRequest) {
105
+ html = result;
106
+ }
107
+ });
108
+ } else {
109
+ html = request;
110
+ }
111
+ });
112
+
113
+ let resolvedNumberOfLines = $derived(
114
+ useCalculateCodeLines(html, numberOfLines)
115
+ );
116
+ let isExpanded = $state(false);
117
+ let calculatedHeight = $state(0);
118
+ let contentRef = $state<HTMLDivElement>();
119
+
120
+ $effect(() => {
121
+ const currentContentRef = contentRef;
122
+ if (!currentContentRef || !expandable) {
123
+ return;
124
+ }
125
+
126
+ const rafId = requestAnimationFrame(() => {
127
+ if (contentRef === currentContentRef) {
128
+ calculatedHeight =
129
+ currentContentRef.scrollHeight +
130
+ (resolvedNumberOfLines &&
131
+ resolvedNumberOfLines < SMALL_EXPANDABLE_NUMBER_OF_LINES
132
+ ? SMALL_EXPANDABLE_HEIGHT_OFFSET
133
+ : EXPANDED_HEIGHT_OFFSET);
134
+ }
135
+ });
136
+
137
+ return () => cancelAnimationFrame(rafId);
138
+ });
139
+
140
+ function toggleExpanded() {
141
+ isExpanded = !isExpanded;
142
+ }
143
+
144
+ let height = $derived.by(() => {
145
+ if (expandable) {
146
+ if (isExpanded) {
147
+ return calculatedHeight + "px";
148
+ }
149
+ if (
150
+ resolvedNumberOfLines &&
151
+ resolvedNumberOfLines < SMALL_EXPANDABLE_NUMBER_OF_LINES
152
+ ) {
153
+ return SMALL_EXPANDABLE_CODE_BLOCK_HEIGHT + "px";
154
+ }
155
+ return DEFAULT_EXPANDABLE_CODE_BLOCK_HEIGHT + "px";
156
+ }
157
+ if (isParentCodeGroup) {
158
+ return "100%";
159
+ }
160
+ return "auto";
161
+ });
162
+
163
+ let isFocusEnabled = $derived(focusLines.length > 0);
164
+ let isHighlightEnabled = $derived(highlightLines.length > 0);
165
+ let shikiBackgroundColors = $derived(
166
+ getShikiBackgroundColors(codeBlockTheme, html, codeString)
167
+ );
168
+ let showFadeOverlay = $derived(
169
+ !(wrap || isHighlightEnabled || expandable) && focusLines.length === 0
170
+ );
171
+
172
+ function styleToString(
173
+ styles: Record<string, string | number | undefined>
174
+ ): string {
175
+ return Object.entries(styles)
176
+ .filter(([, value]) => value !== undefined)
177
+ .map(([key, value]) => key + ":" + value)
178
+ .join(";");
179
+ }
180
+
181
+ let rootStyle = $derived(
182
+ styleToString({
183
+ "font-variant-ligatures": "none",
184
+ height,
185
+ "background-color": shikiBackgroundColors?.light,
186
+ "--shiki-dark-bg": shikiBackgroundColors?.dark,
187
+ })
188
+ );
189
+ let fadeStyle = $derived(
190
+ styleToString({
191
+ "--fade-color-light": shikiBackgroundColors?.light,
192
+ "--fade-color-dark": shikiBackgroundColors?.dark,
193
+ })
194
+ );
195
+ </script>
196
+
197
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex: scrollable code blocks must be keyboard-focusable -->
198
+ <div
199
+ class={cn(
200
+ "children:!my-0 children:!shadow-none children:!bg-transparent relative h-full w-0 min-w-full max-w-full rounded-[14px] px-4 py-3.5 text-sm leading-6 dark:bg-codeblock",
201
+ "code-block-background overflow-x-auto transition-[height] duration-300 ease-in-out",
202
+ "**:outline-0 **:ring-0 **:focus:outline-0 **:focus:ring-0",
203
+ filename ? "rounded-[14px]" : "rounded-2xl",
204
+ codeBlockTheme === "system" ? "bg-white" : "bg-codeblock",
205
+ (expandable || isParentCodeGroup) && "overflow-auto",
206
+ expandable && (isExpanded ? "overflow-y-auto" : "overflow-y-hidden"),
207
+ wrap
208
+ ? "code-block-wrap overflow-x-hidden whitespace-pre-wrap"
209
+ : "overflow-x-auto",
210
+ lines && "has-line-numbers",
211
+ isFocusEnabled && "has-focused",
212
+ isHighlightEnabled && "has-highlighted",
213
+ codeBlockTheme === "system"
214
+ ? "scrollbar-code-system"
215
+ : "scrollbar-code-dark"
216
+ )}
217
+ data-component-part="code-block-root"
218
+ style={rootStyle}
219
+ tabindex={0}
220
+ >
221
+ <div
222
+ class={cn(
223
+ "font-mono",
224
+ wrap ? "whitespace-pre-wrap" : "whitespace-pre",
225
+ isParentCodeGroup && "h-full flex-none text-sm",
226
+ isSmallText ? "text-xs leading-[1.35rem]" : "leading-6"
227
+ )}
228
+ bind:this={contentRef}
229
+ data-component-part={isParentCodeGroup ? "code-group-tab-content" : undefined}
230
+ >
231
+ {#if html}
232
+ {@html html}
233
+ {:else if children}
234
+ {@render children()}
235
+ {/if}
236
+ </div>
237
+ </div>
238
+
239
+ {#if showFadeOverlay}
240
+ <div aria-hidden="true" data-fade-overlay style={fadeStyle}></div>
241
+ {/if}
242
+
243
+ {#if expandable}
244
+ <CodeFooter
245
+ isExpanded={isExpanded}
246
+ numberOfLines={resolvedNumberOfLines}
247
+ toggleExpanded={toggleExpanded}
248
+ />
249
+ {/if}
@@ -0,0 +1,10 @@
1
+ import type { CodeBlockProps } from "./types";
2
+ export type BaseCodeBlockProps = CodeBlockProps & {
3
+ isParentCodeGroup?: boolean;
4
+ shouldHighlight?: boolean;
5
+ forceExtract?: boolean;
6
+ codeString?: string;
7
+ };
8
+ declare const BaseCodeBlock: import("svelte").Component<BaseCodeBlockProps, {}, "">;
9
+ type BaseCodeBlock = ReturnType<typeof BaseCodeBlock>;
10
+ export default BaseCodeBlock;
@@ -0,0 +1,185 @@
1
+ <script lang="ts">
2
+ import { Classes } from "../../constants/selectors";
3
+ import { cn } from "../../utils/cn";
4
+ import { CODE_GROUP_CONTEXT, type CodeGroupContext } from "../code-group/context";
5
+ import { getContext, untrack } from "svelte";
6
+ import { getCodeString } from "../../utils/shiki/lib";
7
+ import BaseCodeBlock from "./BaseCodeBlock.svelte";
8
+ import CodeHeader from "./CodeHeader.svelte";
9
+ import CopyToClipboardButton from "./CopyToClipboardButton.svelte";
10
+ import type {
11
+ CodeBlockProps as CodeBlockPropsFromTypes,
12
+ CopyToClipboardButtonProps,
13
+ } from "./types";
14
+
15
+ export type CodeBlockProps = CodeBlockPropsFromTypes;
16
+
17
+ let {
18
+ filename,
19
+ children,
20
+ class: className,
21
+ icon,
22
+ lang,
23
+ language,
24
+ wrap,
25
+ lines,
26
+ expandable,
27
+ highlight,
28
+ focus,
29
+ numberOfLines,
30
+ hideAskAiButton,
31
+ isSmallText,
32
+ feedbackModalOpen,
33
+ anchorRef,
34
+ codeBlockTheme = "system",
35
+ codeBlockThemeObject = "system",
36
+ askAiButton,
37
+ feedbackButton,
38
+ copyButtonProps,
39
+ }: CodeBlockProps = $props();
40
+
41
+ let codeSourceRef = $state<HTMLDivElement>();
42
+ let extractedCode = $state("");
43
+ let rootRef = $state<HTMLDivElement>();
44
+ const codeGroupContext = getContext<CodeGroupContext | undefined>(CODE_GROUP_CONTEXT);
45
+ const codeGroupChildId = Symbol("code-group-child");
46
+
47
+ $effect(() => {
48
+ if (!codeSourceRef) {
49
+ return;
50
+ }
51
+
52
+ const nextCode = codeSourceRef.textContent ?? "";
53
+ if (nextCode !== extractedCode) {
54
+ extractedCode = nextCode;
55
+ }
56
+ });
57
+
58
+ let codeString = $derived(getCodeString(extractedCode, className, true));
59
+ let hasGrayBackgroundContainer = $derived(!!filename || !!icon);
60
+
61
+ // Register the CodeBlock with a parent CodeGroup — replaces React child inspection.
62
+ $effect(() => {
63
+ if (!codeGroupContext) {
64
+ return;
65
+ }
66
+
67
+ const child = {
68
+ id: codeGroupChildId,
69
+ filename,
70
+ icon,
71
+ lang,
72
+ class: className,
73
+ language,
74
+ wrap,
75
+ lines,
76
+ expandable,
77
+ highlight,
78
+ focus,
79
+ numberOfLines,
80
+ isSmallText,
81
+ feedbackModalOpen,
82
+ anchorRef,
83
+ codeBlockTheme,
84
+ codeBlockThemeObject,
85
+ askAiButton,
86
+ feedbackButton,
87
+ copyButtonProps,
88
+ children,
89
+ codeString,
90
+ };
91
+
92
+ // Keep the child subscribed to its own props without subscribing it to the
93
+ // parent's reactive child array while registration mutates that array.
94
+ untrack(() => codeGroupContext.register(child));
95
+
96
+ return () => untrack(() => codeGroupContext.unregister(codeGroupChildId));
97
+ });
98
+
99
+ $effect(() => {
100
+ if (anchorRef && rootRef) {
101
+ anchorRef.current = rootRef;
102
+ }
103
+ });
104
+ </script>
105
+
106
+ <div
107
+ aria-hidden="true"
108
+ class="hidden"
109
+ bind:this={codeSourceRef}
110
+ >
111
+ {#if children}{@render children()}{/if}
112
+ </div>
113
+
114
+ {#if !codeGroupContext}
115
+ <div
116
+ bind:this={rootRef}
117
+ class={cn(
118
+ Classes.CodeBlock,
119
+ "not-prose group relative mt-5 mb-8 rounded-2xl",
120
+ codeBlockTheme === "system" &&
121
+ "codeblock-light dark:twoslash-dark border border-stone-950/10 bg-stone-50 text-stone-950 dark:border-white/10 dark:bg-white/5 dark:text-stone-50",
122
+ codeBlockTheme === "dark" &&
123
+ "codeblock-dark twoslash-dark dark:twoslash-dark bg-codeblock text-stone-50 ring-1 ring-transparent dark:bg-white/5 dark:ring-white/[0.14]",
124
+ hasGrayBackgroundContainer
125
+ ? "p-0.5"
126
+ : "bg-transparent dark:bg-transparent",
127
+ feedbackModalOpen && "border border-primary dark:border-primary-light",
128
+ className
129
+ )}
130
+ {...(hasGrayBackgroundContainer
131
+ ? { "data-has-header": "" }
132
+ : { "data-has-floating-buttons": "" })}
133
+ >
134
+ {#if hasGrayBackgroundContainer}
135
+ <CodeHeader
136
+ codeBlockTheme={codeBlockTheme}
137
+ filename={filename}
138
+ icon={icon}
139
+ >
140
+ {#snippet children()}
141
+ {#if feedbackButton}{@render feedbackButton()}{/if}
142
+ <CopyToClipboardButton
143
+ codeBlockTheme={codeBlockTheme}
144
+ textToCopy={codeString}
145
+ {...(copyButtonProps as Omit<CopyToClipboardButtonProps, "textToCopy">)}
146
+ />
147
+ {#if askAiButton}{@render askAiButton()}{/if}
148
+ {/snippet}
149
+ </CodeHeader>
150
+ {:else}
151
+ <div
152
+ class="absolute top-3 right-4 flex items-center gap-1.5"
153
+ data-floating-buttons
154
+ >
155
+ {#if feedbackButton}{@render feedbackButton()}{/if}
156
+ <CopyToClipboardButton
157
+ codeBlockTheme={codeBlockTheme}
158
+ textToCopy={codeString}
159
+ {...(copyButtonProps as Omit<CopyToClipboardButtonProps, "textToCopy">)}
160
+ />
161
+ {#if askAiButton}{@render askAiButton()}{/if}
162
+ </div>
163
+ {/if}
164
+
165
+ <BaseCodeBlock
166
+ class={className}
167
+ codeBlockTheme={codeBlockTheme}
168
+ codeBlockThemeObject={codeBlockThemeObject}
169
+ codeString={codeString}
170
+ expandable={expandable}
171
+ filename={filename}
172
+ focus={focus}
173
+ forceExtract
174
+ highlight={highlight}
175
+ isSmallText={isSmallText}
176
+ lang={lang}
177
+ language={language}
178
+ lines={lines}
179
+ numberOfLines={numberOfLines}
180
+ wrap={wrap}
181
+ >
182
+ {#if children}{@render children()}{/if}
183
+ </BaseCodeBlock>
184
+ </div>
185
+ {/if}
@@ -0,0 +1,5 @@
1
+ import type { CodeBlockProps as CodeBlockPropsFromTypes } from "./types";
2
+ export type CodeBlockProps = CodeBlockPropsFromTypes;
3
+ declare const CodeBlock: import("svelte").Component<CodeBlockPropsFromTypes, {}, "">;
4
+ type CodeBlock = ReturnType<typeof CodeBlock>;
5
+ export default CodeBlock;
@@ -0,0 +1,34 @@
1
+ <script lang="ts">
2
+ import { Ellipsis } from "@lucide/svelte";
3
+
4
+ export type CodeFooterProps = {
5
+ numberOfLines: number | undefined;
6
+ isExpanded: boolean;
7
+ toggleExpanded: () => void;
8
+ };
9
+
10
+ let {
11
+ numberOfLines,
12
+ isExpanded,
13
+ toggleExpanded,
14
+ }: CodeFooterProps = $props();
15
+ </script>
16
+
17
+ {#if numberOfLines}
18
+ <div
19
+ class="flex items-center px-3 py-1 font-medium text-stone-500 text-xs hover:text-stone-600 dark:text-stone-400 dark:hover:text-stone-300"
20
+ data-component-part="code-block-footer"
21
+ >
22
+ <button
23
+ class="flex flex-1 items-center gap-1.5 py-1.5"
24
+ data-component-part="code-block-footer-button"
25
+ onclick={toggleExpanded}
26
+ type="button"
27
+ >
28
+ <Ellipsis class="size-3.5 shrink-0 text-stone-500 dark:text-stone-400" />
29
+ {isExpanded
30
+ ? "Collapse"
31
+ : "See all " + numberOfLines + " line" + (numberOfLines === 1 ? "" : "s")}
32
+ </button>
33
+ </div>
34
+ {/if}
@@ -0,0 +1,8 @@
1
+ export type CodeFooterProps = {
2
+ numberOfLines: number | undefined;
3
+ isExpanded: boolean;
4
+ toggleExpanded: () => void;
5
+ };
6
+ declare const CodeFooter: import("svelte").Component<CodeFooterProps, {}, "">;
7
+ type CodeFooter = ReturnType<typeof CodeFooter>;
8
+ export default CodeFooter;
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import Icon from "../icon/Icon.svelte";
4
+ import { Classes } from "../../constants/selectors";
5
+ import { cn } from "../../utils/cn";
6
+ import type { CodeBlockTheme } from "../../utils/shiki/code-styling";
7
+
8
+ export type CodeHeaderProps = {
9
+ filename?: string;
10
+ icon?: string;
11
+ codeBlockTheme?: CodeBlockTheme;
12
+ children?: Snippet;
13
+ };
14
+
15
+ let {
16
+ filename,
17
+ icon,
18
+ codeBlockTheme = "system",
19
+ children,
20
+ }: CodeHeaderProps = $props();
21
+ </script>
22
+
23
+ <div
24
+ class="flex rounded-t-[14px] py-1 pr-2.5 pl-4 font-medium text-stone-400 text-xs leading-6"
25
+ data-component-part="code-block-header"
26
+ >
27
+ <div
28
+ class={cn(
29
+ "flex min-w-0 grow-0 items-center gap-1.5 text-stone-700 dark:text-stone-300",
30
+ codeBlockTheme === "dark" && "text-stone-300"
31
+ )}
32
+ data-component-part="code-block-header-filename"
33
+ >
34
+ {#if icon}
35
+ <Icon
36
+ class={cn(
37
+ "size-3.5 shrink-0 bg-stone-500 dark:bg-stone-400",
38
+ Classes.CodeBlockIcon
39
+ )}
40
+ icon={icon}
41
+ iconType="regular"
42
+ overrideColor
43
+ />
44
+ {/if}
45
+ {#if filename}
46
+ <span class="mr-2 min-w-0 truncate">{filename}</span>
47
+ {/if}
48
+ </div>
49
+ <div class="flex flex-1 items-center justify-end gap-1.5">
50
+ {#if children}{@render children()}{/if}
51
+ </div>
52
+ </div>
@@ -0,0 +1,11 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { CodeBlockTheme } from "../../utils/shiki/code-styling";
3
+ export type CodeHeaderProps = {
4
+ filename?: string;
5
+ icon?: string;
6
+ codeBlockTheme?: CodeBlockTheme;
7
+ children?: Snippet;
8
+ };
9
+ declare const CodeHeader: import("svelte").Component<CodeHeaderProps, {}, "">;
10
+ type CodeHeader = ReturnType<typeof CodeHeader>;
11
+ export default CodeHeader;
@@ -0,0 +1,124 @@
1
+ <script lang="ts">
2
+ import { copyToClipboard } from "../../utils/copy-to-clipboard";
3
+ import type { CopyToClipboardButtonProps } from "./types";
4
+ import { cn } from "../../utils/cn";
5
+
6
+ const DEFAULT_COPY_BUTTON_ARIA_LABEL = "Copy the contents from the code block";
7
+ const DEFAULT_TOOLTIP_COPY_TEXT = "Copy";
8
+ const DEFAULT_TOOLTIP_COPIED_TEXT = "Copied!";
9
+
10
+ let {
11
+ textToCopy,
12
+ onCopied,
13
+ class: className,
14
+ showTooltip = true,
15
+ codeBlockTheme,
16
+ copyButtonAriaLabel = DEFAULT_COPY_BUTTON_ARIA_LABEL,
17
+ tooltipCopyText = DEFAULT_TOOLTIP_COPY_TEXT,
18
+ tooltipCopiedText = DEFAULT_TOOLTIP_COPIED_TEXT,
19
+ ...buttonProps
20
+ }: CopyToClipboardButtonProps = $props();
21
+
22
+ let isCopiedActive = $state(false);
23
+ let isDisabled = $state(false);
24
+ let trimmedTextToCopy = $derived(textToCopy.trim());
25
+
26
+ $effect(() => {
27
+ // Hide copy button if the browser does not support it
28
+ const canCopy =
29
+ typeof window === "undefined" ||
30
+ !!navigator.clipboard?.writeText ||
31
+ typeof document.execCommand === "function";
32
+
33
+ if (!canCopy) {
34
+ console.warn(
35
+ "The browser's Clipboard API and copy fallback are unavailable."
36
+ );
37
+ isDisabled = true;
38
+ } else {
39
+ isDisabled = false;
40
+ }
41
+ });
42
+
43
+ async function onCopy() {
44
+ const result = await copyToClipboard(trimmedTextToCopy);
45
+ if (onCopied) {
46
+ onCopied(result, trimmedTextToCopy);
47
+ }
48
+ if (result === "success") {
49
+ isCopiedActive = true;
50
+ setTimeout(() => {
51
+ isCopiedActive = false;
52
+ }, 2000);
53
+ }
54
+ }
55
+ </script>
56
+
57
+ {#if trimmedTextToCopy && !isDisabled}
58
+ <div class={cn("relative z-10 select-none", className)}>
59
+ <button
60
+ {...buttonProps}
61
+ aria-label={copyButtonAriaLabel}
62
+ class="peer group/copy-button flex size-[26px] items-center justify-center rounded-md backdrop-blur"
63
+ data-testid="copy-code-button"
64
+ onclick={onCopy}
65
+ type="button"
66
+ >
67
+ {#if isCopiedActive}
68
+ <svg
69
+ class={cn(
70
+ "size-4",
71
+ codeBlockTheme === "system" && "fill-primary dark:fill-primary-light",
72
+ codeBlockTheme === "dark" && "fill-primary-light"
73
+ )}
74
+ fill="none"
75
+ height="11"
76
+ role="img"
77
+ viewBox="0 0 16 11"
78
+ width="16"
79
+ >
80
+ <title>Copied</title>
81
+ <path d="M14.7813 1.21873C15.0751 1.51248 15.0751 1.98748 14.7813 2.2781L6.53135 10.5312C6.2376 10.825 5.7626 10.825 5.47197 10.5312L1.21885 6.28123C0.925098 5.98748 0.925098 5.51248 1.21885 5.22185C1.5126 4.93123 1.9876 4.9281 2.27822 5.22185L5.99697 8.9406L13.7188 1.21873C14.0126 0.924976 14.4876 0.924976 14.7782 1.21873H14.7813Z" />
82
+ </svg>
83
+ {:else}
84
+ <svg
85
+ class={cn(
86
+ "size-4",
87
+ codeBlockTheme === "system" &&
88
+ "text-stone-400 group-hover/copy-button:text-stone-500 dark:text-white/40 dark:group-hover/copy-button:text-white/60",
89
+ codeBlockTheme === "dark" &&
90
+ "text-white/40 group-hover/copy-button:text-white/60"
91
+ )}
92
+ fill="none"
93
+ height="18"
94
+ viewBox="0 0 18 18"
95
+ width="18"
96
+ >
97
+ <title>Copy</title>
98
+ <path
99
+ d="M14.25 5.25H7.25C6.14543 5.25 5.25 6.14543 5.25 7.25V14.25C5.25 15.3546 6.14543 16.25 7.25 16.25H14.25C15.3546 16.25 16.25 15.3546 16.25 14.25V7.25C16.25 6.14543 15.3546 5.25 14.25 5.25Z"
100
+ stroke="currentColor"
101
+ stroke-linecap="round"
102
+ stroke-linejoin="round"
103
+ stroke-width="1.5"
104
+ />
105
+ <path
106
+ d="M2.80103 11.998L1.77203 5.07397C1.61003 3.98097 2.36403 2.96397 3.45603 2.80197L10.38 1.77297C11.313 1.63397 12.19 2.16297 12.528 3.00097"
107
+ stroke="currentColor"
108
+ stroke-linecap="round"
109
+ stroke-linejoin="round"
110
+ stroke-width="1.5"
111
+ />
112
+ </svg>
113
+ {/if}
114
+ </button>
115
+ {#if showTooltip}
116
+ <div
117
+ aria-hidden="true"
118
+ class="absolute top-11 left-1/2 -translate-x-1/2 -translate-y-1/2 transform whitespace-nowrap rounded-lg bg-primary-dark px-1.5 py-0.5 text-tooltip-foreground text-xs opacity-0 peer-hover:opacity-100"
119
+ >
120
+ {isCopiedActive ? tooltipCopiedText : tooltipCopyText}
121
+ </div>
122
+ {/if}
123
+ </div>
124
+ {/if}
@@ -0,0 +1,4 @@
1
+ import type { CopyToClipboardButtonProps } from "./types";
2
+ declare const CopyToClipboardButton: import("svelte").Component<CopyToClipboardButtonProps, {}, "">;
3
+ type CopyToClipboardButton = ReturnType<typeof CopyToClipboardButton>;
4
+ export default CopyToClipboardButton;