@hraness/design-kit 0.31.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 (345) hide show
  1. package/ARTICLE_COPY.md +208 -0
  2. package/HERO_FIELDS.md +55 -0
  3. package/ICONS.md +56 -0
  4. package/LANTERN_MATERIAL.md +60 -0
  5. package/LICENSE +21 -0
  6. package/MARKETING_COPY.md +49 -0
  7. package/MARKETING_PRESET.md +108 -0
  8. package/PALETTES.md +90 -0
  9. package/PAPER_THEME.md +128 -0
  10. package/README.md +745 -0
  11. package/STATUS_PAGES.md +67 -0
  12. package/STYLE.md +245 -0
  13. package/WRITING.md +72 -0
  14. package/dist/browser/index.js +2345 -0
  15. package/dist/chunk-44djxk16.js +6872 -0
  16. package/dist/chunk-5gtx3pza.js +9 -0
  17. package/dist/chunk-77391vmq.js +454 -0
  18. package/dist/chunk-7b1586eb.js +1209 -0
  19. package/dist/chunk-8834fh4n.js +6 -0
  20. package/dist/chunk-9t8xyqte.js +870 -0
  21. package/dist/chunk-cejpzyfh.js +446 -0
  22. package/dist/chunk-eh71jz57.js +429 -0
  23. package/dist/chunk-evb02bc1.js +516 -0
  24. package/dist/chunk-h4k7yv6x.js +368 -0
  25. package/dist/chunk-he8eznb1.js +760 -0
  26. package/dist/chunk-mr1vdcjq.js +404 -0
  27. package/dist/chunk-wzvdn8ey.js +116 -0
  28. package/dist/fonts/nebula-sans/social-fonts.generated.js +53 -0
  29. package/dist/icons.js +48 -0
  30. package/dist/index.js +237 -0
  31. package/dist/launch.js +41 -0
  32. package/dist/mockups/client.js +422 -0
  33. package/dist/mockups/index.js +1850 -0
  34. package/dist/portfolio.js +1549 -0
  35. package/dist/provider-marks.js +19 -0
  36. package/dist/react/charts.js +11 -0
  37. package/dist/react/hero-backdrop.js +8 -0
  38. package/dist/react/index.js +7252 -0
  39. package/dist/react/platform-install.js +10 -0
  40. package/dist/react/server.js +137 -0
  41. package/dist/stylex-manifest.json +1 -0
  42. package/dist/stylex.css +5392 -0
  43. package/dist/syntax-highlighting.js +15 -0
  44. package/dist/testing.js +250 -0
  45. package/package.json +377 -0
  46. package/portfolio-inventory.json +31 -0
  47. package/scripts/check-lantern-material-snapshot.d.mts +26 -0
  48. package/scripts/check-lantern-material-snapshot.mjs +126 -0
  49. package/scripts/check-marketing-snapshot.d.mts +12 -0
  50. package/scripts/check-marketing-snapshot.mjs +58 -0
  51. package/scripts/lantern-material-snapshot.ts +101 -0
  52. package/scripts/marketing-textures.ts +48 -0
  53. package/scripts/paper-theme-snapshot.ts +97 -0
  54. package/scripts/product-marketing-snapshot.ts +149 -0
  55. package/src/appearance-menu.css +266 -0
  56. package/src/appearance.ts +32 -0
  57. package/src/article-html.ts +269 -0
  58. package/src/article.ts +686 -0
  59. package/src/browser/appearance-menu.ts +604 -0
  60. package/src/browser/artifact-share.ts +164 -0
  61. package/src/browser/design-palette.ts +197 -0
  62. package/src/browser/foil.ts +124 -0
  63. package/src/browser/hero-light.ts +11 -0
  64. package/src/browser/index.ts +29 -0
  65. package/src/browser/status-field.ts +432 -0
  66. package/src/browser/status-page.ts +100 -0
  67. package/src/browser/sticky-offset.ts +82 -0
  68. package/src/browser/theme-color-sync.ts +213 -0
  69. package/src/charts.css +7 -0
  70. package/src/compiler-components.css +34 -0
  71. package/src/compiler-foundation.css +18 -0
  72. package/src/compiler-palettes.css +7 -0
  73. package/src/compiler-tokens.css +342 -0
  74. package/src/components.css +3 -0
  75. package/src/design-gallery.css +431 -0
  76. package/src/effects.css +14 -0
  77. package/src/fonts/geist-mono/GeistMono[wght].woff2 +0 -0
  78. package/src/fonts/geist-mono/OFL.txt +93 -0
  79. package/src/fonts/geist-mono/PROVENANCE.md +12 -0
  80. package/src/fonts/instrument-serif/OFL.txt +93 -0
  81. package/src/fonts/instrument-serif/UPSTREAM.md +14 -0
  82. package/src/fonts/instrument-serif/instrument-serif-latin-400.woff2 +0 -0
  83. package/src/fonts/nebula-sans/LICENSE.txt +96 -0
  84. package/src/fonts/nebula-sans/NebulaSans-Black.woff2 +0 -0
  85. package/src/fonts/nebula-sans/NebulaSans-BlackItalic.woff2 +0 -0
  86. package/src/fonts/nebula-sans/NebulaSans-Bold.otf +0 -0
  87. package/src/fonts/nebula-sans/NebulaSans-Bold.woff2 +0 -0
  88. package/src/fonts/nebula-sans/NebulaSans-BoldItalic.woff2 +0 -0
  89. package/src/fonts/nebula-sans/NebulaSans-Book.otf +0 -0
  90. package/src/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
  91. package/src/fonts/nebula-sans/NebulaSans-BookItalic.woff2 +0 -0
  92. package/src/fonts/nebula-sans/NebulaSans-Light.woff2 +0 -0
  93. package/src/fonts/nebula-sans/NebulaSans-LightItalic.woff2 +0 -0
  94. package/src/fonts/nebula-sans/NebulaSans-Medium.woff2 +0 -0
  95. package/src/fonts/nebula-sans/NebulaSans-MediumItalic.woff2 +0 -0
  96. package/src/fonts/nebula-sans/NebulaSans-Semibold.woff2 +0 -0
  97. package/src/fonts/nebula-sans/NebulaSans-SemiboldItalic.woff2 +0 -0
  98. package/src/fonts/nebula-sans/PROVENANCE.md +14 -0
  99. package/src/fonts/nebula-sans/social-fonts.generated.ts +3873 -0
  100. package/src/fonts.css +106 -0
  101. package/src/icons/act60/act60.svg +5 -0
  102. package/src/icons/act60/application.svg +9 -0
  103. package/src/icons/act60/chapter-compare.svg +3 -0
  104. package/src/icons/act60/decree-residence.svg +6 -0
  105. package/src/icons/act60/estimate.svg +7 -0
  106. package/src/icons/act60/export-services.svg +4 -0
  107. package/src/icons/act60/investor.svg +13 -0
  108. package/src/icons/act60/presence.svg +7 -0
  109. package/src/icons/aicharts/aicharts.svg +6 -0
  110. package/src/icons/aicharts/cost-compare.svg +4 -0
  111. package/src/icons/aicharts/task-audio.svg +7 -0
  112. package/src/icons/aicharts/task-coding.svg +6 -0
  113. package/src/icons/aicharts/task-images.svg +3 -0
  114. package/src/icons/aicharts/task-reasoning.svg +9 -0
  115. package/src/icons/aicharts/task-research.svg +5 -0
  116. package/src/icons/aicharts/task-video.svg +4 -0
  117. package/src/icons/manifest.json +1371 -0
  118. package/src/icons/platonik/conversation.svg +3 -0
  119. package/src/icons/platonik/creature.svg +7 -0
  120. package/src/icons/platonik/economy.svg +4 -0
  121. package/src/icons/platonik/keep-light.svg +6 -0
  122. package/src/icons/platonik/platonik.svg +7 -0
  123. package/src/icons/platonik/spark-route.svg +15 -0
  124. package/src/icons/receipts/act60-retry.receipt.json +888 -0
  125. package/src/icons/receipts/act60.receipt.json +1366 -0
  126. package/src/icons/receipts/aicharts.receipt.json +1546 -0
  127. package/src/icons/receipts/platonik.receipt.json +1011 -0
  128. package/src/icons/receipts/roughday.receipt.json +770 -0
  129. package/src/icons/receipts/shared.receipt.json +1296 -0
  130. package/src/icons/receipts/slopcamera.receipt.json +1495 -0
  131. package/src/icons/receipts/soundfish-retry.receipt.json +460 -0
  132. package/src/icons/receipts/soundfish.receipt.json +1709 -0
  133. package/src/icons/receipts/sponge.receipt.json +1743 -0
  134. package/src/icons/receipts/stripe-history-retry.receipt.json +145 -0
  135. package/src/icons/receipts/stripe-history.receipt.json +1202 -0
  136. package/src/icons/receipts/wordcell.receipt.json +1924 -0
  137. package/src/icons/roughday/finance.svg +7 -0
  138. package/src/icons/roughday/roughday.svg +6 -0
  139. package/src/icons/roughday/technology.svg +4 -0
  140. package/src/icons/roughday/world.svg +4 -0
  141. package/src/icons/sets/act60.json +63 -0
  142. package/src/icons/sets/aicharts.json +63 -0
  143. package/src/icons/sets/platonik.json +39 -0
  144. package/src/icons/sets/roughday.json +25 -0
  145. package/src/icons/sets/shared.json +12 -0
  146. package/src/icons/sets/slopcamera.json +47 -0
  147. package/src/icons/sets/soundfish.json +43 -0
  148. package/src/icons/sets/sponge.json +51 -0
  149. package/src/icons/sets/stripe-history.json +39 -0
  150. package/src/icons/sets/wordcell.json +51 -0
  151. package/src/icons/shared/agent-skill.svg +4 -0
  152. package/src/icons/shared/cli.svg +5 -0
  153. package/src/icons/shared/privacy.svg +3 -0
  154. package/src/icons/shared/research.svg +7 -0
  155. package/src/icons/shared/sdk.svg +3 -0
  156. package/src/icons/slopcamera/diagram.svg +3 -0
  157. package/src/icons/slopcamera/direct-scene.svg +5 -0
  158. package/src/icons/slopcamera/edit-video.svg +17 -0
  159. package/src/icons/slopcamera/mcp.svg +3 -0
  160. package/src/icons/slopcamera/native-world.svg +3 -0
  161. package/src/icons/slopcamera/slopcamera.svg +11 -0
  162. package/src/icons/soundfish/agent-ui.svg +7 -0
  163. package/src/icons/soundfish/collaborate.svg +5 -0
  164. package/src/icons/soundfish/compose.svg +3 -0
  165. package/src/icons/soundfish/hear.svg +6 -0
  166. package/src/icons/soundfish/human-ui.svg +6 -0
  167. package/src/icons/soundfish/share.svg +3 -0
  168. package/src/icons/soundfish/soundfish.svg +6 -0
  169. package/src/icons/sponge/api.svg +3 -0
  170. package/src/icons/sponge/documents.svg +3 -0
  171. package/src/icons/sponge/library.svg +3 -0
  172. package/src/icons/sponge/one-question.svg +5 -0
  173. package/src/icons/sponge/people-direct.svg +5 -0
  174. package/src/icons/sponge/read.svg +3 -0
  175. package/src/icons/sponge/save.svg +4 -0
  176. package/src/icons/sponge/sponge.svg +7 -0
  177. package/src/icons/stripe-history/company-history.svg +3 -0
  178. package/src/icons/stripe-history/evidence.svg +4 -0
  179. package/src/icons/stripe-history/independence.svg +7 -0
  180. package/src/icons/stripe-history/publications.svg +11 -0
  181. package/src/icons/stripe-history/sources.svg +6 -0
  182. package/src/icons/stripe-history/stripe-history.svg +3 -0
  183. package/src/icons/wordcell/backlinks.svg +6 -0
  184. package/src/icons/wordcell/capture.svg +6 -0
  185. package/src/icons/wordcell/git-provenance.svg +4 -0
  186. package/src/icons/wordcell/kb.svg +4 -0
  187. package/src/icons/wordcell/markdown.svg +4 -0
  188. package/src/icons/wordcell/scopes.svg +3 -0
  189. package/src/icons/wordcell/search.svg +7 -0
  190. package/src/icons.generated.ts +734 -0
  191. package/src/icons.ts +51 -0
  192. package/src/index.ts +275 -0
  193. package/src/lantern-material.css +213 -0
  194. package/src/launch.ts +624 -0
  195. package/src/marketing-assets/UPSTREAM.md +18 -0
  196. package/src/marketing-assets/cells.svg +1 -0
  197. package/src/marketing-assets/grain.svg +1 -0
  198. package/src/marketing-forced-colors.css +12 -0
  199. package/src/mockups/client.tsx +415 -0
  200. package/src/mockups/core.tsx +318 -0
  201. package/src/mockups/frames.tsx +368 -0
  202. package/src/mockups/index.ts +14 -0
  203. package/src/mockups/surfaces.tsx +681 -0
  204. package/src/mockups.css +2607 -0
  205. package/src/palette-appearance.ts +45 -0
  206. package/src/palette-bridge.css +124 -0
  207. package/src/palette-color.ts +41 -0
  208. package/src/palette-system.css +721 -0
  209. package/src/palette-themes.ts +23 -0
  210. package/src/palette-tokens.stylex.ts +519 -0
  211. package/src/palettes.css +3 -0
  212. package/src/palettes.ts +161 -0
  213. package/src/paper-theme.css +244 -0
  214. package/src/plain-publication.css +1241 -0
  215. package/src/plain-site.css +397 -0
  216. package/src/platforms.ts +151 -0
  217. package/src/portfolio.generated.json +1595 -0
  218. package/src/portfolio.generated.ts +1596 -0
  219. package/src/portfolio.ts +226 -0
  220. package/src/product-marketing-foundation.css +433 -0
  221. package/src/product-marketing-preset.css +261 -0
  222. package/src/product-marketing.css +2186 -0
  223. package/src/provider-marks.generated.ts +112 -0
  224. package/src/provider-marks.ts +143 -0
  225. package/src/react/animated-rail-stage.stylex.ts +15 -0
  226. package/src/react/animated-rail-stage.tsx +51 -0
  227. package/src/react/app-shell.stylex.ts +104 -0
  228. package/src/react/app-shell.tsx +121 -0
  229. package/src/react/article.tsx +602 -0
  230. package/src/react/aurora-dots-background.tsx +34 -0
  231. package/src/react/charts.stylex.ts +563 -0
  232. package/src/react/charts.tsx +610 -0
  233. package/src/react/chat.stylex.ts +34 -0
  234. package/src/react/chat.tsx +155 -0
  235. package/src/react/design-gallery.tsx +943 -0
  236. package/src/react/design-palette.stylex.ts +124 -0
  237. package/src/react/design-palette.tsx +196 -0
  238. package/src/react/design-theme-context.tsx +41 -0
  239. package/src/react/effects.stylex.ts +325 -0
  240. package/src/react/fader.stylex.ts +71 -0
  241. package/src/react/fader.tsx +151 -0
  242. package/src/react/foil-card-math.ts +100 -0
  243. package/src/react/foil-card-surface.stylex.ts +326 -0
  244. package/src/react/foil-card-surface.tsx +742 -0
  245. package/src/react/foil-mark.tsx +35 -0
  246. package/src/react/foil.stylex.ts +163 -0
  247. package/src/react/haptics.ts +228 -0
  248. package/src/react/hero-backdrop.tsx +22 -0
  249. package/src/react/index.ts +38 -0
  250. package/src/react/keyboard-shortcuts.ts +228 -0
  251. package/src/react/lantern-material-gallery.tsx +166 -0
  252. package/src/react/lantern-material.stylex.ts +39 -0
  253. package/src/react/launch-beats.tsx +73 -0
  254. package/src/react/navigation-rail.stylex.ts +130 -0
  255. package/src/react/navigation-rail.tsx +228 -0
  256. package/src/react/particle-halo.tsx +115 -0
  257. package/src/react/phaser-dots.tsx +501 -0
  258. package/src/react/platform-icons.tsx +94 -0
  259. package/src/react/platform-install.stylex.ts +385 -0
  260. package/src/react/platform-install.tsx +389 -0
  261. package/src/react/playback-transport.stylex.ts +14 -0
  262. package/src/react/playback-transport.tsx +112 -0
  263. package/src/react/procedural-backdrop.tsx +192 -0
  264. package/src/react/procedural-recipe.ts +323 -0
  265. package/src/react/product-marketing.stylex.ts +4469 -0
  266. package/src/react/product-marketing.tsx +1611 -0
  267. package/src/react/production-data-preview-notice.stylex.ts +40 -0
  268. package/src/react/production-data-preview-notice.tsx +34 -0
  269. package/src/react/provider-mark.stylex.ts +132 -0
  270. package/src/react/provider-mark.tsx +120 -0
  271. package/src/react/relative-time.tsx +117 -0
  272. package/src/react/route-state.stylex.ts +39 -0
  273. package/src/react/route-state.tsx +350 -0
  274. package/src/react/server.ts +11 -0
  275. package/src/react/social-kit-panel.tsx +103 -0
  276. package/src/react/sticky-offset.tsx +33 -0
  277. package/src/react/surfaces.stylex.ts +179 -0
  278. package/src/react/surfaces.tsx +330 -0
  279. package/src/react/syntax-code.tsx +33 -0
  280. package/src/react/theme-color-sync.ts +1 -0
  281. package/src/react/theme-resolution.ts +11 -0
  282. package/src/react/theme.stylex.ts +276 -0
  283. package/src/react/theme.tsx +402 -0
  284. package/src/reading.css +72 -0
  285. package/src/relative-time.ts +185 -0
  286. package/src/reset.css +2 -0
  287. package/src/site-shell.css +16 -0
  288. package/src/status-page-html.ts +63 -0
  289. package/src/status-page.css +276 -0
  290. package/src/status-page.ts +323 -0
  291. package/src/styles.css +21 -0
  292. package/src/syntax-code-lexers.ts +303 -0
  293. package/src/syntax-highlighting.css +78 -0
  294. package/src/syntax-highlighting.ts +477 -0
  295. package/src/syntax-tokens.ts +33 -0
  296. package/src/testing.ts +307 -0
  297. package/src/tokens.css +3 -0
  298. package/src/typography.css +86 -0
  299. package/vendor/evilcharts/LICENSE +21 -0
  300. package/vendor/evilcharts/UPSTREAM.md +14 -0
  301. package/vendor/platform-marks/LICENSE +30 -0
  302. package/vendor/platform-marks/UPSTREAM.md +15 -0
  303. package/vendor/platform-marks/apple.svg +1 -0
  304. package/vendor/platform-marks/linux.svg +1 -0
  305. package/vendor/provider-marks/CRUSH-LICENSE.md +134 -0
  306. package/vendor/provider-marks/LICENSE +21 -0
  307. package/vendor/provider-marks/UPSTREAM.md +27 -0
  308. package/vendor/provider-marks/aider.svg +3 -0
  309. package/vendor/provider-marks/crush-heartbit.svg +8 -0
  310. package/vendor/provider-marks/lobehub/alibabacloud-color.svg +1 -0
  311. package/vendor/provider-marks/lobehub/alibabacloud.svg +1 -0
  312. package/vendor/provider-marks/lobehub/amp-color.svg +1 -0
  313. package/vendor/provider-marks/lobehub/amp.svg +1 -0
  314. package/vendor/provider-marks/lobehub/anthropic.svg +1 -0
  315. package/vendor/provider-marks/lobehub/claude-color.svg +1 -0
  316. package/vendor/provider-marks/lobehub/claude.svg +1 -0
  317. package/vendor/provider-marks/lobehub/claudecode-color.svg +1 -0
  318. package/vendor/provider-marks/lobehub/claudecode.svg +1 -0
  319. package/vendor/provider-marks/lobehub/codex-color.svg +1 -0
  320. package/vendor/provider-marks/lobehub/codex.svg +1 -0
  321. package/vendor/provider-marks/lobehub/cursor.svg +1 -0
  322. package/vendor/provider-marks/lobehub/deepseek-color.svg +1 -0
  323. package/vendor/provider-marks/lobehub/deepseek.svg +1 -0
  324. package/vendor/provider-marks/lobehub/devin-color.svg +1 -0
  325. package/vendor/provider-marks/lobehub/devin.svg +1 -0
  326. package/vendor/provider-marks/lobehub/gemini-color.svg +1 -0
  327. package/vendor/provider-marks/lobehub/gemini.svg +1 -0
  328. package/vendor/provider-marks/lobehub/geminicli-color.svg +1 -0
  329. package/vendor/provider-marks/lobehub/geminicli.svg +1 -0
  330. package/vendor/provider-marks/lobehub/goose.svg +1 -0
  331. package/vendor/provider-marks/lobehub/meta-color.svg +1 -0
  332. package/vendor/provider-marks/lobehub/meta.svg +1 -0
  333. package/vendor/provider-marks/lobehub/mistral-color.svg +1 -0
  334. package/vendor/provider-marks/lobehub/mistral.svg +1 -0
  335. package/vendor/provider-marks/lobehub/moonshot.svg +1 -0
  336. package/vendor/provider-marks/lobehub/nvidia-color.svg +1 -0
  337. package/vendor/provider-marks/lobehub/nvidia.svg +1 -0
  338. package/vendor/provider-marks/lobehub/openai.svg +1 -0
  339. package/vendor/provider-marks/lobehub/opencode.svg +1 -0
  340. package/vendor/provider-marks/lobehub/perplexity-color.svg +1 -0
  341. package/vendor/provider-marks/lobehub/perplexity.svg +1 -0
  342. package/vendor/provider-marks/lobehub/qwen-color.svg +1 -0
  343. package/vendor/provider-marks/lobehub/qwen.svg +1 -0
  344. package/vendor/provider-marks/lobehub/xai.svg +1 -0
  345. package/vendor/provider-marks/lobehub/zai.svg +1 -0
@@ -0,0 +1,477 @@
1
+ import { highlight } from "sugar-high";
2
+ import { highlightCodeLexer } from "./syntax-code-lexers.js";
3
+ import { escapeHtml, token, type SyntaxToken } from "./syntax-tokens.js";
4
+
5
+ export const syntaxLanguages = [
6
+ "css",
7
+ "html",
8
+ "json",
9
+ "lean",
10
+ "markdown",
11
+ "rust",
12
+ "shell",
13
+ "text",
14
+ "tla",
15
+ "toml",
16
+ "typescript",
17
+ "yaml",
18
+ ] as const;
19
+
20
+ export type SyntaxLanguage = (typeof syntaxLanguages)[number];
21
+
22
+ /** Larger blocks remain escaped plain text rather than entering a tokenizer. */
23
+ export const maximumSyntaxCharacters = 128 * 1024;
24
+
25
+ export interface HighlightedCode {
26
+ readonly className: `syntax-code language-${SyntaxLanguage}`;
27
+ readonly html: string;
28
+ readonly language: SyntaxLanguage;
29
+ }
30
+
31
+ export interface HighlightCodeOptions {
32
+ /** Keep the default inline output, or use the shared stylesheet for strict CSP. */
33
+ readonly styles?: "inline" | "classes";
34
+ }
35
+
36
+ function classOnlySyntax(html: string): string {
37
+ // Sugar High owns this finite serialization. Strip only an exact matching
38
+ // token color; a new or mismatched style must be reviewed before admission.
39
+ const result = html.replace(
40
+ /<span class="sh__token--(class|comment|entity|identifier|jsxliterals|keyword|property|sign|space|string)" style="color:var\(--sh-\1\)">/gu,
41
+ '<span class="sh__token--$1">',
42
+ );
43
+ if (/<[A-Za-z][^<>]*\sstyle\s*=/iu.test(result)) {
44
+ throw new Error("Unrecognized inline style in class-only syntax output.");
45
+ }
46
+ return result;
47
+ }
48
+
49
+ const shellKeywords = new Set([
50
+ "case",
51
+ "do",
52
+ "done",
53
+ "elif",
54
+ "else",
55
+ "esac",
56
+ "fi",
57
+ "for",
58
+ "function",
59
+ "if",
60
+ "in",
61
+ "select",
62
+ "then",
63
+ "time",
64
+ "until",
65
+ "while",
66
+ ]);
67
+
68
+ const shellKeywordsFollowedByCommand = new Set([
69
+ "do",
70
+ "elif",
71
+ "if",
72
+ "then",
73
+ "until",
74
+ "while",
75
+ ]);
76
+
77
+ function languageToken(input: string): string {
78
+ const tokens = input.trim().toLowerCase().split(/\s+/u);
79
+ const languageClass = tokens.find((token) => token.startsWith("language-"));
80
+ return (languageClass ?? tokens[0] ?? "").replace(/^language-/u, "");
81
+ }
82
+
83
+ /**
84
+ * Parses Markdown info strings and DOM class names from an untrusted boundary
85
+ * into the finite language set supported by the design kit.
86
+ */
87
+ export function resolveSyntaxLanguage(input: unknown): SyntaxLanguage {
88
+ if (typeof input !== "string") return "text";
89
+
90
+ switch (languageToken(input)) {
91
+ case "css":
92
+ return "css";
93
+ case "htm":
94
+ case "html":
95
+ case "xml":
96
+ return "html";
97
+ case "json":
98
+ case "jsonc":
99
+ return "json";
100
+ case "lean":
101
+ case "lean4":
102
+ return "lean";
103
+ case "rust":
104
+ case "rs":
105
+ return "rust";
106
+ case "tla":
107
+ case "tla+":
108
+ case "tlaplus":
109
+ return "tla";
110
+ case "toml":
111
+ return "toml";
112
+ case "yaml":
113
+ case "yml":
114
+ return "yaml";
115
+ case "markdown":
116
+ case "md":
117
+ case "mdx":
118
+ return "markdown";
119
+ case "bash":
120
+ case "console":
121
+ case "sh":
122
+ case "shell":
123
+ case "zsh":
124
+ return "shell";
125
+ case "javascript":
126
+ case "js":
127
+ case "jsx":
128
+ case "ts":
129
+ case "tsx":
130
+ case "typescript":
131
+ return "typescript";
132
+ case "plaintext":
133
+ case "text":
134
+ case "txt":
135
+ default:
136
+ return "text";
137
+ }
138
+ }
139
+
140
+ /** Conservative, deterministic defaults for code without a language hint. */
141
+ export function inferSyntaxLanguage(code: string): SyntaxLanguage {
142
+ if (code.length > maximumSyntaxCharacters) return "text";
143
+ const trimmed = code.trim();
144
+ if (trimmed === "") return "text";
145
+ if (trimmed[0] === "{" || trimmed[0] === "[") {
146
+ try {
147
+ const value: unknown = JSON.parse(trimmed);
148
+ if (value !== null && typeof value === "object") return "json";
149
+ } catch { /* Other recognizable syntax may also begin with a bracket. */ }
150
+ }
151
+ // Examine only a bounded first line. Unknown commands and prose stay plain.
152
+ const first = (trimmed.split("\n", 1)[0] ?? "").slice(0, 2048);
153
+ if (/^#!\s*(?:\/bin\/(?:ba|z)?sh|\/usr\/bin\/env\s+(?:ba|z)?sh)(?:\s|$)/u.test(first)
154
+ || /^(?:\$\s+)?(?:bun|npm|pnpm|yarn)\s+(?:add|install|run|test|build|remove|update|exec|dlx|create|init|ci|publish|pack)\b/u.test(first)
155
+ || /^(?:\$\s+)?(?:bunx|npx)\s+(?:skills\s+add\b|create-[\w-]+\b|@[\w-]+\/[\w-]+\b|[\w-]+\s+--?[A-Za-z])/u.test(first)
156
+ || /^(?:\$\s+)?git\s+(?:clone|status|diff|log|show|fetch|pull|push|checkout|switch|add|commit|branch|tag)\b/u.test(first)
157
+ || /^(?:\$\s+)?curl\s+(?:--?[A-Za-z]|https?:\/\/)/u.test(first)) return "shell";
158
+ if (/^(?:export\s+)?(?:const|let|var)\s+[A-Za-z_$][\w$]*(?:\s*:[^=]+)?\s*=/u.test(first)
159
+ || /^import\s+(?:[\w*{].*\s+from\s+)?["']/u.test(first)
160
+ || /^(?:export\s+)?(?:async\s+)?function\s+[A-Za-z_$][\w$]*\s*\(/u.test(first)) return "typescript";
161
+ if (/^#{1,6}\s+\S/u.test(first) || /^`{3,}\w*/u.test(first)) return "markdown";
162
+ if (/^<!doctype\s+html\b/iu.test(first) || /^<([a-z][\w:-]*)(?:\s[^<>]*|)>(?:[^]*?)<\/\1\s*>/iu.test(trimmed)) return "html";
163
+ const opening = trimmed.indexOf("{");
164
+ if (opening > 0 && opening < 2048 && /^[.#]?[a-zA-Z_][\w.# :>+~,-]*$/u.test(trimmed.slice(0, opening).trimEnd())) {
165
+ const declaration = trimmed.slice(opening + 1, opening + 2049).trimStart();
166
+ const colon = declaration.indexOf(":");
167
+ if (colon > 0 && /^-{0,2}[a-zA-Z][a-zA-Z-]*$/u.test(declaration.slice(0, colon).trimEnd())
168
+ && /^[^{};]+[;}]/u.test(declaration.slice(colon + 1).trimStart())) return "css";
169
+ }
170
+ return "text";
171
+ }
172
+
173
+ function tokenHtml(kind: SyntaxToken, html: string): string {
174
+ return `<span class="syntax-token syntax-token--${kind}">${html}</span>`;
175
+ }
176
+
177
+ function highlightMarkdownInline(value: string): string {
178
+ let html = "";
179
+ let cursor = 0;
180
+
181
+ for (const match of value.matchAll(/`[^`\n]+`/gu)) {
182
+ const index = match.index;
183
+ const inlineCode = match[0];
184
+ html += escapeHtml(value.slice(cursor, index));
185
+ html += token("inline", inlineCode);
186
+ cursor = index + inlineCode.length;
187
+ }
188
+
189
+ return html + escapeHtml(value.slice(cursor));
190
+ }
191
+
192
+ interface MarkdownLineParts {
193
+ readonly content: string;
194
+ readonly indentation: string;
195
+ readonly marker: string;
196
+ readonly separator: string;
197
+ }
198
+
199
+ /** Exact ECMAScript `\s` code-unit set used by the former matchers. */
200
+ function isMarkdownWhitespace(codeUnit: number): boolean {
201
+ return codeUnit === 0x0009
202
+ || codeUnit === 0x000a
203
+ || codeUnit === 0x000b
204
+ || codeUnit === 0x000c
205
+ || codeUnit === 0x000d
206
+ || codeUnit === 0x0020
207
+ || codeUnit === 0x00a0
208
+ || codeUnit === 0x1680
209
+ || (codeUnit >= 0x2000 && codeUnit <= 0x200a)
210
+ || codeUnit === 0x2028
211
+ || codeUnit === 0x2029
212
+ || codeUnit === 0x202f
213
+ || codeUnit === 0x205f
214
+ || codeUnit === 0x3000
215
+ || codeUnit === 0xfeff;
216
+ }
217
+
218
+ function isMarkdownLineTerminator(codeUnit: number): boolean {
219
+ return codeUnit === 0x000a
220
+ || codeUnit === 0x000d
221
+ || codeUnit === 0x2028
222
+ || codeUnit === 0x2029;
223
+ }
224
+
225
+ function isAsciiDigit(codeUnit: number): boolean {
226
+ return codeUnit >= 0x0030 && codeUnit <= 0x0039;
227
+ }
228
+
229
+ function markdownLineParts(
230
+ line: string,
231
+ kind: "fence" | "heading" | "list",
232
+ ): MarkdownLineParts | null {
233
+ let markerStart = 0;
234
+ while (
235
+ markerStart < line.length
236
+ && isMarkdownWhitespace(line.charCodeAt(markerStart))
237
+ ) {
238
+ markerStart += 1;
239
+ }
240
+
241
+ let markerEnd = markerStart;
242
+ if (kind === "fence") {
243
+ const fence = line.charCodeAt(markerStart);
244
+ if (fence !== 0x0060 && fence !== 0x007e) return null;
245
+ while (markerEnd < line.length && line.charCodeAt(markerEnd) === fence) {
246
+ markerEnd += 1;
247
+ }
248
+ if (markerEnd - markerStart < 3) return null;
249
+ } else if (kind === "heading") {
250
+ while (
251
+ markerEnd < line.length
252
+ && markerEnd - markerStart < 6
253
+ && line.charCodeAt(markerEnd) === 0x0023
254
+ ) {
255
+ markerEnd += 1;
256
+ }
257
+ if (markerEnd === markerStart) return null;
258
+ } else {
259
+ const first = line.charCodeAt(markerStart);
260
+ if (first === 0x002a || first === 0x002b || first === 0x002d) {
261
+ markerEnd += 1;
262
+ } else if (isAsciiDigit(first)) {
263
+ while (markerEnd < line.length && isAsciiDigit(line.charCodeAt(markerEnd))) {
264
+ markerEnd += 1;
265
+ }
266
+ if (line.charCodeAt(markerEnd) !== 0x002e) return null;
267
+ markerEnd += 1;
268
+ } else {
269
+ return null;
270
+ }
271
+ }
272
+
273
+ const separatorStart = markerEnd;
274
+ if (kind !== "fence") {
275
+ while (
276
+ markerEnd < line.length
277
+ && isMarkdownWhitespace(line.charCodeAt(markerEnd))
278
+ ) {
279
+ markerEnd += 1;
280
+ }
281
+ if (markerEnd === separatorStart) return null;
282
+ }
283
+
284
+ // One final pass preserves `.` without dotAll: content containing any line
285
+ // terminator was never a legacy match, even when the terminator was last.
286
+ for (let cursor = markerEnd; cursor < line.length; cursor += 1) {
287
+ if (isMarkdownLineTerminator(line.charCodeAt(cursor))) return null;
288
+ }
289
+
290
+ return {
291
+ content: line.slice(markerEnd),
292
+ indentation: line.slice(0, markerStart),
293
+ marker: line.slice(markerStart, separatorStart),
294
+ separator: line.slice(separatorStart, markerEnd),
295
+ };
296
+ }
297
+
298
+ function highlightMarkdownLine(line: string): string {
299
+ const fence = markdownLineParts(line, "fence");
300
+ if (fence !== null) {
301
+ return `${escapeHtml(fence.indentation)}${token("marker", fence.marker)}${token("keyword", fence.content)}`;
302
+ }
303
+
304
+ const heading = markdownLineParts(line, "heading");
305
+ if (heading !== null) {
306
+ return `${escapeHtml(heading.indentation)}${token("marker", heading.marker)}${escapeHtml(heading.separator)}${tokenHtml("heading", highlightMarkdownInline(heading.content))}`;
307
+ }
308
+
309
+ const listItem = markdownLineParts(line, "list");
310
+ if (listItem !== null) {
311
+ return `${escapeHtml(listItem.indentation)}${token("marker", listItem.marker)}${escapeHtml(listItem.separator)}${highlightMarkdownInline(listItem.content)}`;
312
+ }
313
+
314
+ const quote = /^(\s*)(>)(\s?)(.*)$/u.exec(line);
315
+ if (quote !== null) {
316
+ return `${escapeHtml(quote[1] ?? "")}${token("marker", quote[2] ?? "")}${escapeHtml(quote[3] ?? "")}${highlightMarkdownInline(quote[4] ?? "")}`;
317
+ }
318
+
319
+ return highlightMarkdownInline(line);
320
+ }
321
+
322
+ function highlightMarkdown(value: string): string {
323
+ return value.split("\n").map(highlightMarkdownLine).join("\n");
324
+ }
325
+
326
+ function isShellOperator(character: string): boolean {
327
+ return character === "&"
328
+ || character === "("
329
+ || character === ")"
330
+ || character === ";"
331
+ || character === "<"
332
+ || character === ">"
333
+ || character === "|";
334
+ }
335
+
336
+ function isShellWhitespace(character: string): boolean {
337
+ return /\s/u.test(character);
338
+ }
339
+
340
+ function isEscaped(line: string, index: number): boolean {
341
+ let backslashes = 0;
342
+ for (let cursor = index - 1; cursor >= 0 && line[cursor] === "\\"; cursor -= 1) {
343
+ backslashes += 1;
344
+ }
345
+ return backslashes % 2 === 1;
346
+ }
347
+
348
+ function highlightShellLine(line: string): string {
349
+ let cursor = 0;
350
+ let expectsCommand = true;
351
+ let html = "";
352
+
353
+ while (cursor < line.length) {
354
+ const character = line[cursor] ?? "";
355
+
356
+ if (isShellWhitespace(character)) {
357
+ html += escapeHtml(character);
358
+ cursor += 1;
359
+ continue;
360
+ }
361
+
362
+ if (character === "#") {
363
+ html += token("comment", line.slice(cursor));
364
+ break;
365
+ }
366
+
367
+ if (character === "'" || character === '"') {
368
+ const quote = character;
369
+ let end = cursor + 1;
370
+ while (end < line.length) {
371
+ const next = line[end] ?? "";
372
+ end += 1;
373
+ if (next === quote && (quote === "'" || !isEscaped(line, end - 1))) break;
374
+ }
375
+ html += token("string", line.slice(cursor, end));
376
+ cursor = end;
377
+ expectsCommand = false;
378
+ continue;
379
+ }
380
+
381
+ if (character === "$") {
382
+ if (line.slice(0, cursor).trim() === "" && /\s/u.test(line[cursor + 1] ?? "")) {
383
+ html += token("operator", character);
384
+ cursor += 1;
385
+ continue;
386
+ }
387
+ const variable = /^\$(?:\{[^}\n]*\}|[A-Za-z_][A-Za-z0-9_]*|[?$!#*@0-9-])/u.exec(
388
+ line.slice(cursor),
389
+ )?.[0] ?? "$";
390
+ html += token("variable", variable);
391
+ cursor += variable.length;
392
+ expectsCommand = false;
393
+ continue;
394
+ }
395
+
396
+ if (isShellOperator(character)) {
397
+ const pair = line.slice(cursor, cursor + 2);
398
+ const operator = pair === "&&" || pair === "||" || pair === ">>" || pair === "<<"
399
+ ? pair
400
+ : character;
401
+ html += token("operator", operator);
402
+ cursor += operator.length;
403
+ expectsCommand = operator === ";" || operator === "&&" || operator === "||" || operator === "|";
404
+ continue;
405
+ }
406
+
407
+ let end = cursor + 1;
408
+ while (end < line.length) {
409
+ const next = line[end] ?? "";
410
+ if (
411
+ isShellWhitespace(next)
412
+ || isShellOperator(next)
413
+ || next === "$"
414
+ || next === "'"
415
+ || next === '"'
416
+ ) {
417
+ break;
418
+ }
419
+ end += 1;
420
+ }
421
+
422
+ const word = line.slice(cursor, end);
423
+ if (shellKeywords.has(word)) {
424
+ html += token("keyword", word);
425
+ expectsCommand = shellKeywordsFollowedByCommand.has(word);
426
+ } else if (word.startsWith("-")) {
427
+ html += token("flag", word);
428
+ expectsCommand = false;
429
+ } else if (expectsCommand && !word.includes("=")) {
430
+ html += token("command", word);
431
+ expectsCommand = false;
432
+ } else if (/^[A-Za-z_][A-Za-z0-9_]*=/u.test(word)) {
433
+ html += token("variable", word);
434
+ } else {
435
+ html += escapeHtml(word);
436
+ expectsCommand = false;
437
+ }
438
+ cursor = end;
439
+ }
440
+
441
+ return html;
442
+ }
443
+
444
+ function highlightShell(value: string): string {
445
+ return value.split("\n").map(highlightShellLine).join("\n");
446
+ }
447
+
448
+ export function highlightCode(
449
+ code: string,
450
+ languageHint?: string,
451
+ options: HighlightCodeOptions = {},
452
+ ): HighlightedCode {
453
+ const styles = options.styles ?? "inline";
454
+ if (styles !== "inline" && styles !== "classes") {
455
+ throw new Error("Syntax styles must be inline or classes.");
456
+ }
457
+ const language = code.length > maximumSyntaxCharacters
458
+ ? "text"
459
+ : languageHint === undefined || languageHint.trim() === "" || languageHint === "auto"
460
+ ? inferSyntaxLanguage(code)
461
+ : resolveSyntaxLanguage(languageHint);
462
+ const html = language === "text"
463
+ ? escapeHtml(code)
464
+ : language === "markdown"
465
+ ? highlightMarkdown(code)
466
+ : language === "shell"
467
+ ? highlightShell(code)
468
+ : language === "rust" || language === "toml" || language === "yaml" || language === "lean" || language === "tla"
469
+ ? highlightCodeLexer(code, language)
470
+ : highlight(code);
471
+
472
+ return Object.freeze({
473
+ className: `syntax-code language-${language}`,
474
+ html: styles === "classes" ? classOnlySyntax(html) : html,
475
+ language,
476
+ });
477
+ }
@@ -0,0 +1,33 @@
1
+ /** Fixed token roles shared by the dependency-free language lexers. */
2
+ export type SyntaxToken =
3
+ | "command"
4
+ | "comment"
5
+ | "flag"
6
+ | "function"
7
+ | "heading"
8
+ | "inline"
9
+ | "keyword"
10
+ | "marker"
11
+ | "number"
12
+ | "operator"
13
+ | "property"
14
+ | "string"
15
+ | "type"
16
+ | "variable";
17
+
18
+ export function escapeHtml(value: string): string {
19
+ return value.replace(/[&<>"']/gu, (character) => {
20
+ switch (character) {
21
+ case "&": return "&amp;";
22
+ case "<": return "&lt;";
23
+ case ">": return "&gt;";
24
+ case '"': return "&quot;";
25
+ case "'": return "&#39;";
26
+ default: return character;
27
+ }
28
+ });
29
+ }
30
+
31
+ export function token(kind: SyntaxToken, value: string): string {
32
+ return `<span class="syntax-token syntax-token--${kind}">${escapeHtml(value)}</span>`;
33
+ }