@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,404 @@
1
+ import {
2
+ platformInstallClassName
3
+ } from "./chunk-h4k7yv6x.js";
4
+ import {
5
+ detectPlatform,
6
+ isKnownPlatformId,
7
+ isPlatformId,
8
+ matchDetectedPlatform,
9
+ platformLabel,
10
+ platformMark
11
+ } from "./chunk-wzvdn8ey.js";
12
+
13
+ // src/react/platform-install.tsx
14
+ import { useCallback, useEffect, useId, useRef, useState } from "react";
15
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
16
+ var copyResetMilliseconds = 2000;
17
+ function assertTargets(platforms, defaultPlatform) {
18
+ if (platforms.length === 0)
19
+ throw new RangeError("PlatformInstall needs at least one platform.");
20
+ const seen = new Set;
21
+ for (const target of platforms) {
22
+ if (!isPlatformId(target.id))
23
+ throw new RangeError(`Platform ids are lowercase slugs; received ${JSON.stringify(target.id)}.`);
24
+ if (seen.has(target.id))
25
+ throw new RangeError(`Duplicate platform id: ${target.id}.`);
26
+ seen.add(target.id);
27
+ if (target.label !== undefined ? target.label.trim() === "" : !isKnownPlatformId(target.id)) {
28
+ throw new RangeError(`Platform ${target.id} needs a label.`);
29
+ }
30
+ if (target.unavailable === true) {
31
+ if (target.unavailableNote === undefined || target.unavailableNote === null || target.unavailableNote === "") {
32
+ throw new RangeError(`Unavailable platform ${target.id} needs an unavailableNote.`);
33
+ }
34
+ if (target.command !== undefined && target.command.trim() === "")
35
+ throw new RangeError(`Platform ${target.id} has a blank command.`);
36
+ } else {
37
+ if (typeof target.command !== "string" || target.command.trim() === "")
38
+ throw new RangeError(`Platform ${target.id} needs a command.`);
39
+ }
40
+ for (const alternative of target.alternatives ?? []) {
41
+ if (alternative.label.trim() === "" || alternative.command.trim() === "") {
42
+ throw new RangeError(`Platform ${target.id} has an alternative without a label or command.`);
43
+ }
44
+ }
45
+ }
46
+ if (defaultPlatform !== undefined && !seen.has(defaultPlatform)) {
47
+ throw new RangeError(`defaultPlatform ${JSON.stringify(defaultPlatform)} is not one of the listed platforms.`);
48
+ }
49
+ }
50
+ function labelOf(target) {
51
+ return target.label ?? platformLabel(target.id);
52
+ }
53
+ function selectContents(element) {
54
+ if (element === null)
55
+ return;
56
+ try {
57
+ const selection = element.ownerDocument.defaultView?.getSelection();
58
+ if (selection === null || selection === undefined)
59
+ return;
60
+ const range = element.ownerDocument.createRange();
61
+ range.selectNodeContents(element);
62
+ selection.removeAllRanges();
63
+ selection.addRange(range);
64
+ } catch {}
65
+ }
66
+ async function writeClipboard(text, fallback) {
67
+ try {
68
+ if (typeof navigator !== "undefined" && navigator.clipboard !== undefined) {
69
+ await navigator.clipboard.writeText(text);
70
+ return true;
71
+ }
72
+ } catch {}
73
+ selectContents(fallback);
74
+ try {
75
+ return fallback?.ownerDocument.execCommand("copy") === true;
76
+ } catch {
77
+ return false;
78
+ }
79
+ }
80
+ function PlatformMarkSymbols({
81
+ ids,
82
+ symbolId
83
+ }) {
84
+ return /* @__PURE__ */ jsx("svg", {
85
+ "aria-hidden": "true",
86
+ className: platformInstallClassName(["markSymbols"]),
87
+ focusable: "false",
88
+ xmlns: "http://www.w3.org/2000/svg",
89
+ children: ids.map((id) => {
90
+ const mark = platformMark(id);
91
+ return /* @__PURE__ */ jsx("symbol", {
92
+ id: symbolId(id),
93
+ viewBox: mark.viewBox,
94
+ children: /* @__PURE__ */ jsx("path", {
95
+ d: mark.path
96
+ })
97
+ }, id);
98
+ })
99
+ });
100
+ }
101
+ function PlatformMarkUse({
102
+ platform,
103
+ symbolId
104
+ }) {
105
+ return /* @__PURE__ */ jsx("svg", {
106
+ "aria-hidden": "true",
107
+ className: platformInstallClassName(["icon"]),
108
+ "data-platform": platform,
109
+ fill: "currentColor",
110
+ focusable: "false",
111
+ viewBox: platformMark(platform).viewBox,
112
+ xmlns: "http://www.w3.org/2000/svg",
113
+ children: /* @__PURE__ */ jsx("use", {
114
+ href: `#${symbolId}`
115
+ })
116
+ });
117
+ }
118
+ function CopyGlyph({
119
+ copied
120
+ }) {
121
+ return /* @__PURE__ */ jsx("svg", {
122
+ "aria-hidden": "true",
123
+ className: platformInstallClassName(["copyIcon"]),
124
+ fill: "none",
125
+ focusable: "false",
126
+ stroke: "currentColor",
127
+ strokeLinecap: "round",
128
+ strokeLinejoin: "round",
129
+ strokeWidth: 2,
130
+ viewBox: "0 0 24 24",
131
+ children: copied ? /* @__PURE__ */ jsx("path", {
132
+ d: "M5 12.5l4.5 4.5L19 7.5"
133
+ }) : /* @__PURE__ */ jsxs(Fragment, {
134
+ children: [
135
+ /* @__PURE__ */ jsx("rect", {
136
+ height: "12",
137
+ rx: "2",
138
+ width: "12",
139
+ x: "8",
140
+ y: "8"
141
+ }),
142
+ /* @__PURE__ */ jsx("path", {
143
+ d: "M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2"
144
+ })
145
+ ]
146
+ })
147
+ });
148
+ }
149
+ function CommandBlock({
150
+ caption,
151
+ command,
152
+ copyKey,
153
+ onCopy,
154
+ status,
155
+ subject
156
+ }) {
157
+ const preRef = useRef(null);
158
+ const state = status?.key === copyKey ? status.ok ? "copied" : "failed" : "idle";
159
+ return /* @__PURE__ */ jsxs("div", {
160
+ className: platformInstallClassName(["command"]),
161
+ "data-copy-state": state,
162
+ children: [
163
+ /* @__PURE__ */ jsxs("div", {
164
+ className: platformInstallClassName(["commandBar", (caption === undefined || caption === "") && "commandBarEmpty"]),
165
+ children: [
166
+ /* @__PURE__ */ jsx("span", {
167
+ className: platformInstallClassName(["shell"]),
168
+ children: caption
169
+ }),
170
+ /* @__PURE__ */ jsxs("button", {
171
+ className: platformInstallClassName(["copy"]),
172
+ "data-copy-state": state,
173
+ onClick: () => onCopy(copyKey, command, subject, preRef.current),
174
+ type: "button",
175
+ children: [
176
+ /* @__PURE__ */ jsx(CopyGlyph, {
177
+ copied: state === "copied"
178
+ }),
179
+ /* @__PURE__ */ jsx("span", {
180
+ children: state === "copied" ? "Copied" : state === "failed" ? "Select to copy" : "Copy"
181
+ }),
182
+ /* @__PURE__ */ jsxs("span", {
183
+ className: platformInstallClassName(["status"]),
184
+ children: [
185
+ " ",
186
+ subject
187
+ ]
188
+ })
189
+ ]
190
+ })
191
+ ]
192
+ }),
193
+ /* @__PURE__ */ jsx("pre", {
194
+ "aria-label": subject,
195
+ className: platformInstallClassName(["pre"]),
196
+ ref: preRef,
197
+ tabIndex: 0,
198
+ children: /* @__PURE__ */ jsx("code", {
199
+ className: platformInstallClassName(["code"]),
200
+ children: command
201
+ })
202
+ })
203
+ ]
204
+ });
205
+ }
206
+ function PlatformInstall({
207
+ className,
208
+ defaultPlatform,
209
+ detect = true,
210
+ id,
211
+ label = "Platform",
212
+ platforms
213
+ }) {
214
+ assertTargets(platforms, defaultPlatform);
215
+ const [firstTarget] = platforms;
216
+ if (firstTarget === undefined)
217
+ throw new RangeError("PlatformInstall needs at least one platform.");
218
+ const generatedId = useId();
219
+ const baseId = id ?? `hraness-platform-install${generatedId.replaceAll(/[^A-Za-z0-9_-]/gu, "")}`;
220
+ const initial = defaultPlatform ?? firstTarget.id;
221
+ const [selected, setSelected] = useState(initial);
222
+ const [source, setSource] = useState("default");
223
+ const [status, setStatus] = useState(null);
224
+ const chosen = useRef(false);
225
+ const tabs = useRef(new Map);
226
+ const ids = platforms.map((target) => target.id);
227
+ const idKey = ids.join(`
228
+ `);
229
+ const symbolId = (platform) => `${baseId}-mark-${platform}`;
230
+ const current = ids.includes(selected) ? selected : initial;
231
+ useEffect(() => {
232
+ if (!detect || chosen.current)
233
+ return;
234
+ const match = matchDetectedPlatform(detectPlatform(globalThis.navigator), idKey.split(`
235
+ `));
236
+ if (match === null)
237
+ return;
238
+ setSelected(match);
239
+ setSource("detected");
240
+ }, [detect, idKey]);
241
+ useEffect(() => {
242
+ if (status === null)
243
+ return;
244
+ const timer = setTimeout(() => setStatus(null), copyResetMilliseconds);
245
+ return () => clearTimeout(timer);
246
+ }, [status]);
247
+ const choose = useCallback((next, focus) => {
248
+ chosen.current = true;
249
+ setSelected(next);
250
+ setSource("chosen");
251
+ if (focus)
252
+ tabs.current.get(next)?.focus();
253
+ }, []);
254
+ const copy = useCallback((key, command, subject, pre) => {
255
+ writeClipboard(command, pre).then((ok) => setStatus({
256
+ key,
257
+ ok,
258
+ subject
259
+ }));
260
+ }, []);
261
+ const onKeyDown = (event) => {
262
+ const index = ids.indexOf(current);
263
+ let next;
264
+ switch (event.key) {
265
+ case "ArrowRight":
266
+ next = (index + 1) % ids.length;
267
+ break;
268
+ case "ArrowLeft":
269
+ next = (index - 1 + ids.length) % ids.length;
270
+ break;
271
+ case "Home":
272
+ next = 0;
273
+ break;
274
+ case "End":
275
+ next = ids.length - 1;
276
+ break;
277
+ default:
278
+ return;
279
+ }
280
+ const target = ids[next];
281
+ if (target === undefined)
282
+ return;
283
+ event.preventDefault();
284
+ choose(target, true);
285
+ };
286
+ return /* @__PURE__ */ jsxs("div", {
287
+ className: platformInstallClassName(["root"], className),
288
+ "data-hraness-platform-install": "",
289
+ "data-selected-platform": current,
290
+ "data-selection-source": source,
291
+ id,
292
+ children: [
293
+ /* @__PURE__ */ jsx(PlatformMarkSymbols, {
294
+ ids,
295
+ symbolId
296
+ }),
297
+ /* @__PURE__ */ jsx("div", {
298
+ "aria-label": label,
299
+ className: platformInstallClassName(["tablist"]),
300
+ onKeyDown,
301
+ role: "tablist",
302
+ children: platforms.map((target) => {
303
+ const isSelected = target.id === current;
304
+ return /* @__PURE__ */ jsxs("button", {
305
+ "aria-controls": `${baseId}-panel-${target.id}`,
306
+ "aria-selected": isSelected,
307
+ className: platformInstallClassName(["tab", isSelected && "tabSelected"]),
308
+ "data-availability": target.unavailable === true ? "unavailable" : "available",
309
+ "data-platform": target.id,
310
+ id: `${baseId}-tab-${target.id}`,
311
+ onClick: () => choose(target.id, false),
312
+ ref: (element) => {
313
+ if (element === null)
314
+ tabs.current.delete(target.id);
315
+ else
316
+ tabs.current.set(target.id, element);
317
+ },
318
+ role: "tab",
319
+ tabIndex: isSelected ? 0 : -1,
320
+ type: "button",
321
+ children: [
322
+ /* @__PURE__ */ jsx(PlatformMarkUse, {
323
+ platform: target.id,
324
+ symbolId: symbolId(target.id)
325
+ }),
326
+ /* @__PURE__ */ jsx("span", {
327
+ className: platformInstallClassName(["tabLabel"]),
328
+ children: labelOf(target)
329
+ })
330
+ ]
331
+ }, target.id);
332
+ })
333
+ }),
334
+ platforms.map((target) => {
335
+ const name = labelOf(target);
336
+ const alternatives = target.alternatives ?? [];
337
+ return /* @__PURE__ */ jsx("div", {
338
+ "aria-labelledby": `${baseId}-tab-${target.id}`,
339
+ className: platformInstallClassName(["panel"]),
340
+ "data-availability": target.unavailable === true ? "unavailable" : "available",
341
+ "data-platform": target.id,
342
+ hidden: target.id !== current,
343
+ id: `${baseId}-panel-${target.id}`,
344
+ role: "tabpanel",
345
+ children: /* @__PURE__ */ jsxs("div", {
346
+ className: platformInstallClassName(["panelBody"]),
347
+ children: [
348
+ /* @__PURE__ */ jsxs("p", {
349
+ className: platformInstallClassName(["panelLabel"]),
350
+ children: [
351
+ /* @__PURE__ */ jsx(PlatformMarkUse, {
352
+ platform: target.id,
353
+ symbolId: symbolId(target.id)
354
+ }),
355
+ /* @__PURE__ */ jsx("span", {
356
+ children: name
357
+ })
358
+ ]
359
+ }),
360
+ target.unavailable === true ? /* @__PURE__ */ jsx("div", {
361
+ className: platformInstallClassName(["unavailable"]),
362
+ children: target.unavailableNote
363
+ }) : null,
364
+ target.command === undefined ? null : /* @__PURE__ */ jsx(CommandBlock, {
365
+ caption: target.shell,
366
+ command: target.command,
367
+ copyKey: `${target.id}:primary`,
368
+ onCopy: copy,
369
+ status,
370
+ subject: `${name} install command`
371
+ }),
372
+ alternatives.length === 0 ? null : /* @__PURE__ */ jsx("ul", {
373
+ "aria-label": `Other ways to install on ${name}`,
374
+ className: platformInstallClassName(["alternatives"]),
375
+ children: alternatives.map((alternative, index) => /* @__PURE__ */ jsx("li", {
376
+ children: /* @__PURE__ */ jsx(CommandBlock, {
377
+ caption: [alternative.label, alternative.shell].filter(Boolean).join(" · "),
378
+ command: alternative.command,
379
+ copyKey: `${target.id}:${index}`,
380
+ onCopy: copy,
381
+ status,
382
+ subject: `${name} ${alternative.label} command`
383
+ })
384
+ }, `${alternative.label}-${index}`))
385
+ }),
386
+ target.note === undefined ? null : /* @__PURE__ */ jsx("div", {
387
+ className: platformInstallClassName(["note"]),
388
+ children: target.note
389
+ })
390
+ ]
391
+ })
392
+ }, target.id);
393
+ }),
394
+ /* @__PURE__ */ jsx("p", {
395
+ "aria-live": "polite",
396
+ className: platformInstallClassName(["status"]),
397
+ role: "status",
398
+ children: status === null ? "" : status.ok ? `Copied the ${status.subject}.` : `Copying failed. The ${status.subject} is selected; copy it with your keyboard.`
399
+ })
400
+ ]
401
+ });
402
+ }
403
+
404
+ export { PlatformInstall };
@@ -0,0 +1,116 @@
1
+ // src/platforms.ts
2
+ var knownPlatformIds = ["macos", "linux", "windows"];
3
+ var platformIdPattern = /^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$/u;
4
+ function isPlatformId(value) {
5
+ return typeof value === "string" && platformIdPattern.test(value);
6
+ }
7
+ function isKnownPlatformId(value) {
8
+ return typeof value === "string" && knownPlatformIds.includes(value);
9
+ }
10
+ var platformLabels = {
11
+ linux: "Linux",
12
+ macos: "macOS",
13
+ windows: "Windows"
14
+ };
15
+ function platformLabel(id) {
16
+ return isKnownPlatformId(id) ? platformLabels[id] : id;
17
+ }
18
+ var marks = {
19
+ macos: {
20
+ viewBox: "0 0 24 24",
21
+ path: "M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701"
22
+ },
23
+ linux: {
24
+ viewBox: "0 0 24 24",
25
+ path: "M12.504 0c-.155 0-.315.008-.48.021-4.226.333-3.105 4.807-3.17 6.298-.076 1.092-.3 1.953-1.05 3.02-.885 1.051-2.127 2.75-2.716 4.521-.278.832-.41 1.684-.287 2.489a.424.424 0 00-.11.135c-.26.268-.45.6-.663.839-.199.199-.485.267-.797.4-.313.136-.658.269-.864.68-.09.189-.136.394-.132.602 0 .199.027.4.055.536.058.399.116.728.04.97-.249.68-.28 1.145-.106 1.484.174.334.535.47.94.601.81.2 1.91.135 2.774.6.926.466 1.866.67 2.616.47.526-.116.97-.464 1.208-.946.587-.003 1.23-.269 2.26-.334.699-.058 1.574.267 2.577.2.025.134.063.198.114.333l.003.003c.391.778 1.113 1.132 1.884 1.071.771-.06 1.592-.536 2.257-1.306.631-.765 1.683-1.084 2.378-1.503.348-.199.629-.469.649-.853.023-.4-.2-.811-.714-1.376v-.097l-.003-.003c-.17-.2-.25-.535-.338-.926-.085-.401-.182-.786-.492-1.046h-.003c-.059-.054-.123-.067-.188-.135a.357.357 0 00-.19-.064c.431-1.278.264-2.55-.173-3.694-.533-1.41-1.465-2.638-2.175-3.483-.796-1.005-1.576-1.957-1.56-3.368.026-2.152.236-6.133-3.544-6.139zm.529 3.405h.013c.213 0 .396.062.584.198.19.135.33.332.438.533.105.259.158.459.166.724 0-.02.006-.04.006-.06v.105a.086.086 0 01-.004-.021l-.004-.024a1.807 1.807 0 01-.15.706.953.953 0 01-.213.335.71.71 0 00-.088-.042c-.104-.045-.198-.064-.284-.133a1.312 1.312 0 00-.22-.066c.05-.06.146-.133.183-.198.053-.128.082-.264.088-.402v-.02a1.21 1.21 0 00-.061-.4c-.045-.134-.101-.2-.183-.333-.084-.066-.167-.132-.267-.132h-.016c-.093 0-.176.03-.262.132a.8.8 0 00-.205.334 1.18 1.18 0 00-.09.4v.019c.002.089.008.179.02.267-.193-.067-.438-.135-.607-.202a1.635 1.635 0 01-.018-.2v-.02a1.772 1.772 0 01.15-.768c.082-.22.232-.406.43-.533a.985.985 0 01.594-.2zm-2.962.059h.036c.142 0 .27.048.399.135.146.129.264.288.344.465.09.199.14.4.153.667v.004c.007.134.006.2-.002.266v.08c-.03.007-.056.018-.083.024-.152.055-.274.135-.393.2.012-.09.013-.18.003-.267v-.015c-.012-.133-.04-.2-.082-.333a.613.613 0 00-.166-.267.248.248 0 00-.183-.064h-.021c-.071.006-.13.04-.186.132a.552.552 0 00-.12.27.944.944 0 00-.023.33v.015c.012.135.037.2.08.334.046.134.098.2.166.268.01.009.02.018.034.024-.07.057-.117.07-.176.136a.304.304 0 01-.131.068 2.62 2.62 0 01-.275-.402 1.772 1.772 0 01-.155-.667 1.759 1.759 0 01.08-.668 1.43 1.43 0 01.283-.535c.128-.133.26-.2.418-.2zm1.37 1.706c.332 0 .733.065 1.216.399.293.2.523.269 1.052.468h.003c.255.136.405.266.478.399v-.131a.571.571 0 01.016.47c-.123.31-.516.643-1.063.842v.002c-.268.135-.501.333-.775.465-.276.135-.588.292-1.012.267a1.139 1.139 0 01-.448-.067 3.566 3.566 0 01-.322-.198c-.195-.135-.363-.332-.612-.465v-.005h-.005c-.4-.246-.616-.512-.686-.71-.07-.268-.005-.47.193-.6.224-.135.38-.271.483-.336.104-.074.143-.102.176-.131h.002v-.003c.169-.202.436-.47.839-.601.139-.036.294-.065.466-.065zm2.8 2.142c.358 1.417 1.196 3.475 1.735 4.473.286.534.855 1.659 1.102 3.024.156-.005.33.018.513.064.646-1.671-.546-3.467-1.089-3.966-.22-.2-.232-.335-.123-.335.59.534 1.365 1.572 1.646 2.757.13.535.16 1.104.021 1.67.067.028.135.06.205.067 1.032.534 1.413.938 1.23 1.537v-.043c-.06-.003-.12 0-.18 0h-.016c.151-.467-.182-.825-1.065-1.224-.915-.4-1.646-.336-1.77.465-.008.043-.013.066-.018.135-.068.023-.139.053-.209.064-.43.268-.662.669-.793 1.187-.13.533-.17 1.156-.205 1.869v.003c-.02.334-.17.838-.319 1.35-1.5 1.072-3.58 1.538-5.348.334a2.645 2.645 0 00-.402-.533 1.45 1.45 0 00-.275-.333c.182 0 .338-.03.465-.067a.615.615 0 00.314-.334c.108-.267 0-.697-.345-1.163-.345-.467-.931-.995-1.788-1.521-.63-.4-.986-.87-1.15-1.396-.165-.534-.143-1.085-.015-1.645.245-1.07.873-2.11 1.274-2.763.107-.065.037.135-.408.974-.396.751-1.14 2.497-.122 3.854a8.123 8.123 0 01.647-2.876c.564-1.278 1.743-3.504 1.836-5.268.048.036.217.135.289.202.218.133.38.333.59.465.21.201.477.335.876.335.039.003.075.006.11.006.412 0 .73-.134.997-.268.29-.134.52-.334.74-.4h.005c.467-.135.835-.402 1.044-.7zm2.185 8.958c.037.6.343 1.245.882 1.377.588.134 1.434-.333 1.791-.765l.211-.01c.315-.007.577.01.847.268l.003.003c.208.199.305.53.391.876.085.4.154.78.409 1.066.486.527.645.906.636 1.14l.003-.007v.018l-.003-.012c-.015.262-.185.396-.498.595-.63.401-1.746.712-2.457 1.57-.618.737-1.37 1.14-2.036 1.191-.664.053-1.237-.2-1.574-.898l-.005-.003c-.21-.4-.12-1.025.056-1.69.176-.668.428-1.344.463-1.897.037-.714.076-1.335.195-1.814.12-.465.308-.797.641-.984l.045-.022zm-10.814.049h.01c.053 0 .105.005.157.014.376.055.706.333 1.023.752l.91 1.664.003.003c.243.533.754 1.064 1.189 1.637.434.598.77 1.131.729 1.57v.006c-.057.744-.48 1.148-1.125 1.294-.645.135-1.52.002-2.395-.464-.968-.536-2.118-.469-2.857-.602-.369-.066-.61-.2-.723-.4-.11-.2-.113-.602.123-1.23v-.004l.002-.003c.117-.334.03-.752-.027-1.118-.055-.401-.083-.71.043-.94.16-.334.396-.4.69-.533.294-.135.64-.202.915-.47h.002v-.002c.256-.268.445-.601.668-.838.19-.201.38-.336.663-.336zm7.159-9.074c-.435.201-.945.535-1.488.535-.542 0-.97-.267-1.28-.466-.154-.134-.28-.268-.373-.335-.164-.134-.144-.333-.074-.333.109.016.129.134.199.2.096.066.215.2.36.333.292.2.68.467 1.167.467.485 0 1.053-.267 1.398-.466.195-.135.445-.334.648-.467.156-.136.149-.267.279-.267.128.016.034.134-.147.332a8.097 8.097 0 01-.69.468zm-1.082-1.583V5.64c-.006-.02.013-.042.029-.05.074-.043.18-.027.26.004.063 0 .16.067.15.135-.006.049-.085.066-.135.066-.055 0-.092-.043-.141-.068-.052-.018-.146-.008-.163-.065zm-.551 0c-.02.058-.113.049-.166.066-.047.025-.086.068-.14.068-.05 0-.13-.02-.136-.068-.01-.066.088-.133.15-.133.08-.031.184-.047.259-.005.019.009.036.03.03.05v.02h.003z"
26
+ },
27
+ windows: {
28
+ viewBox: "0 0 24 24",
29
+ path: "M1.5 1.5h10v10h-10zM12.5 1.5h10v10h-10zM1.5 12.5h10v10h-10zM12.5 12.5h10v10h-10z"
30
+ },
31
+ terminal: {
32
+ viewBox: "0 0 24 24",
33
+ path: "M4 3h16a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2zm0 2v14h16V5zm2.3 3.3 1.4-1.4 4.1 4.1-4.1 4.1-1.4-1.4 2.7-2.7zM12 15h6v2h-6z"
34
+ }
35
+ };
36
+ function platformMark(id) {
37
+ return isKnownPlatformId(id) ? marks[id] : marks.terminal;
38
+ }
39
+ function detectPlatform(navigatorLike) {
40
+ if (typeof navigatorLike !== "object" || navigatorLike === null)
41
+ return null;
42
+ const record = navigatorLike;
43
+ let hinted;
44
+ try {
45
+ const data = record.userAgentData;
46
+ if (typeof data === "object" && data !== null) {
47
+ const platform = data.platform;
48
+ if (typeof platform === "string")
49
+ hinted = platform;
50
+ }
51
+ } catch {
52
+ hinted = undefined;
53
+ }
54
+ const fromHint = hinted === undefined ? null : classifyHint(hinted);
55
+ if (fromHint !== null)
56
+ return fromHint;
57
+ const userAgent = stringField(record, "userAgent");
58
+ const fromAgent = userAgent === undefined ? null : classifyUserAgent(userAgent);
59
+ if (fromAgent !== null)
60
+ return fromAgent;
61
+ const legacy = stringField(record, "platform");
62
+ return legacy === undefined ? null : classifyUserAgent(legacy);
63
+ }
64
+ function stringField(record, key) {
65
+ try {
66
+ const value = record[key];
67
+ return typeof value === "string" ? value : undefined;
68
+ } catch {
69
+ return;
70
+ }
71
+ }
72
+ function classifyHint(platform) {
73
+ switch (platform.trim().toLowerCase()) {
74
+ case "macos":
75
+ return "macos";
76
+ case "windows":
77
+ return "windows";
78
+ case "linux":
79
+ return "linux";
80
+ case "chrome os":
81
+ case "chromeos":
82
+ return "chromeos";
83
+ case "android":
84
+ return "android";
85
+ case "ios":
86
+ return "ios";
87
+ default:
88
+ return null;
89
+ }
90
+ }
91
+ function classifyUserAgent(userAgent) {
92
+ if (/Windows|Win(?:32|64)\b/u.test(userAgent))
93
+ return "windows";
94
+ if (/iPhone|iPad|iPod/u.test(userAgent))
95
+ return "ios";
96
+ if (/Android/u.test(userAgent))
97
+ return "android";
98
+ if (/CrOS/u.test(userAgent))
99
+ return "chromeos";
100
+ if (/Mac OS X|Macintosh|MacIntel|\bMac\b/u.test(userAgent))
101
+ return "macos";
102
+ if (/Linux|X11/u.test(userAgent))
103
+ return "linux";
104
+ return null;
105
+ }
106
+ function matchDetectedPlatform(detected, available) {
107
+ if (detected === null)
108
+ return null;
109
+ if (available.includes(detected))
110
+ return detected;
111
+ if (detected === "chromeos" && available.includes("linux"))
112
+ return "linux";
113
+ return null;
114
+ }
115
+
116
+ export { knownPlatformIds, isPlatformId, isKnownPlatformId, platformLabel, platformMark, detectPlatform, matchDetectedPlatform };