@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,368 @@
1
+ import type { CSSProperties, ReactNode } from "react";
2
+
3
+ import {
4
+ MockupGlyph,
5
+ MockupRoot,
6
+ SampleText,
7
+ WindowLights,
8
+ type MockupOptOut,
9
+ type MockupRootProps,
10
+ } from "./core.js";
11
+
12
+ /** Hosts a mockup may show in an address bar: reserved example names only (RFC 2606 and RFC 6761). */
13
+ export const MOCKUP_EXAMPLE_HOST = /(^|\.)(example(\.(com|net|org))?|test|invalid|localhost)$/u;
14
+
15
+ /** Splits a mockup address into host and path, and rejects hosts that could belong to someone. */
16
+ export function mockupAddress(url: string): Readonly<{ host: string; path: string }> {
17
+ const trimmed = url.trim().replace(/^[a-z]+:\/\//iu, "");
18
+ const slash = trimmed.indexOf("/");
19
+ const host = (slash < 0 ? trimmed : trimmed.slice(0, slash)).toLowerCase();
20
+ const path = slash < 0 ? "" : trimmed.slice(slash);
21
+ if (!MOCKUP_EXAMPLE_HOST.test(host)) {
22
+ throw new RangeError(`Mockup address ${JSON.stringify(url)} must use a reserved example host such as example.com or site.example.`);
23
+ }
24
+ return { host, path: path === "/" ? "" : path };
25
+ }
26
+
27
+ function pageStyle(height: number | undefined): CSSProperties | undefined {
28
+ if (height === undefined) return undefined;
29
+ if (!(height > 0)) throw new RangeError("A mockup page height must be positive.");
30
+ return { "--hkm-page-height": `${height}px` } as CSSProperties;
31
+ }
32
+
33
+ function optOutProps(optOut: MockupOptOut | undefined): { optOut?: MockupOptOut } {
34
+ return optOut === undefined ? {} : { optOut };
35
+ }
36
+
37
+ /**
38
+ * A neutral browser window: window buttons, history controls, an address
39
+ * field on a reserved example host, an optional toolbar slot, and a page area.
40
+ * With `height`, the page area is a window onto a longer page; `fade` (on by
41
+ * default then) says it keeps going.
42
+ */
43
+ export function BrowserFrame({
44
+ children,
45
+ fade,
46
+ height,
47
+ toolbar,
48
+ url,
49
+ ...root
50
+ }: MockupRootProps & Readonly<{ url: string; toolbar?: ReactNode; height?: number; fade?: boolean; children: ReactNode }>) {
51
+ return (
52
+ <MockupRoot {...root} kind="browser">
53
+ <BrowserWindow {...(fade === undefined ? {} : { fade })} {...(height === undefined ? {} : { height })} toolbar={toolbar} url={url}>
54
+ {children}
55
+ </BrowserWindow>
56
+ </MockupRoot>
57
+ );
58
+ }
59
+
60
+ /** The browser chrome without a root, for surfaces that compose it inside their own root. */
61
+ export function BrowserWindow({
62
+ children,
63
+ fade,
64
+ height,
65
+ toolbar,
66
+ url,
67
+ }: Readonly<{ url: string; toolbar?: ReactNode; height?: number; fade?: boolean; children: ReactNode }>) {
68
+ const { host, path } = mockupAddress(url);
69
+ const fades = fade ?? height !== undefined;
70
+ return (
71
+ <div className="hkm-window">
72
+ <div aria-hidden="true" className="hkm-browser-bar">
73
+ <WindowLights />
74
+ <span className="hkm-browser-nav">
75
+ <MockupGlyph name="back" size={16} />
76
+ <MockupGlyph name="forward" size={16} />
77
+ <MockupGlyph name="reload" size={15} />
78
+ </span>
79
+ <span className="hkm-address">
80
+ <MockupGlyph name="lock" size={12} />
81
+ <span className="hkm-address-host">{host}</span>
82
+ {path === "" ? null : <span className="hkm-address-path">{path}</span>}
83
+ </span>
84
+ <span className="hkm-browser-tools">{toolbar}</span>
85
+ </div>
86
+ <div className="hkm-page" data-hkm-fade={fades ? "" : undefined} style={pageStyle(height)}>
87
+ {children}
88
+ </div>
89
+ </div>
90
+ );
91
+ }
92
+
93
+ /** One line in a terminal mockup. */
94
+ export type TerminalLine = Readonly<{
95
+ kind: "input" | "output" | "comment";
96
+ text: string;
97
+ tone?: "ok" | "warn" | "error" | "muted";
98
+ /** A beat id a film or step-through can key on; rendered as `data-hkm-beat`. */
99
+ beat?: string;
100
+ }>;
101
+
102
+ const TERMINAL_KINDS = new Set(["input", "output", "comment"]);
103
+
104
+ /**
105
+ * A terminal window with a prompt, typed commands, output, and comments.
106
+ * Commands and output are sample text; keep them to commands the product ships.
107
+ */
108
+ export function TerminalFrame({
109
+ fade,
110
+ height,
111
+ lines,
112
+ prompt = "$",
113
+ title = "Terminal",
114
+ ...root
115
+ }: MockupRootProps & Readonly<{ title?: string; prompt?: string; lines: readonly TerminalLine[]; height?: number; fade?: boolean }>) {
116
+ for (const line of lines) {
117
+ if (!TERMINAL_KINDS.has(line.kind)) throw new TypeError(`Unknown terminal line kind ${JSON.stringify(line.kind)}.`);
118
+ }
119
+ const fades = fade ?? height !== undefined;
120
+ return (
121
+ <MockupRoot {...root} kind="terminal">
122
+ <div className="hkm-window">
123
+ <div aria-hidden="true" className="hkm-title-bar">
124
+ <WindowLights />
125
+ <span className="hkm-title">{title}</span>
126
+ <span />
127
+ </div>
128
+ <div className="hkm-page hkm-terminal-body" data-hkm-fade={fades ? "" : undefined} style={pageStyle(height)}>
129
+ <div className="hkm-terminal-lines">
130
+ {lines.map((line, index) => (
131
+ <div className="hkm-terminal-line" data-hkm-beat={line.beat} data-hkm-line={line.kind} data-hkm-tone={line.tone} key={index}>
132
+ {line.kind === "input" ? <span className="hkm-terminal-prompt">{prompt}</span> : null}
133
+ {line.kind === "comment" ? <span className="hkm-terminal-prompt">#</span> : null}
134
+ <SampleText {...optOutProps(root.optOut)}>{line.text}</SampleText>
135
+ </div>
136
+ ))}
137
+ <div aria-hidden="true" className="hkm-terminal-line" data-hkm-line="input">
138
+ <span className="hkm-terminal-prompt">{prompt}</span>
139
+ <span className="hkm-caret" />
140
+ </div>
141
+ </div>
142
+ </div>
143
+ </div>
144
+ </MockupRoot>
145
+ );
146
+ }
147
+
148
+ /**
149
+ * A desktop app window: title bar, optional toolbar, optional translucent
150
+ * sidebar, and content.
151
+ */
152
+ export function MacWindow({
153
+ children,
154
+ sidebar,
155
+ title,
156
+ toolbar,
157
+ ...root
158
+ }: MockupRootProps & Readonly<{ title?: string; sidebar?: ReactNode; toolbar?: ReactNode; children: ReactNode }>) {
159
+ return (
160
+ <MockupRoot {...root} kind="app-window">
161
+ <div className="hkm-window" data-hkm-sidebar={sidebar === undefined ? undefined : ""}>
162
+ {sidebar === undefined ? null : (
163
+ <div className="hkm-app-sidebar">
164
+ <WindowLights />
165
+ <div className="hkm-app-sidebar-body">{sidebar}</div>
166
+ </div>
167
+ )}
168
+ <div className="hkm-app-main">
169
+ <div className="hkm-title-bar hkm-app-bar">
170
+ {sidebar === undefined ? <WindowLights /> : <span />}
171
+ <span className="hkm-title">{title}</span>
172
+ <span className="hkm-app-tools">{toolbar}</span>
173
+ </div>
174
+ <div className="hkm-app-content">{children}</div>
175
+ </div>
176
+ </div>
177
+ </MockupRoot>
178
+ );
179
+ }
180
+
181
+ /** One row in a menu bar popover. */
182
+ export type MenuBarItem = Readonly<{ id: string; label: string; detail?: string; tone?: "ok" | "warn" | "error"; selected?: boolean }>;
183
+
184
+ /**
185
+ * A slice of a desktop menu bar with a status item open. `mark` is the status
186
+ * item's glyph; pass the product's own mark, or leave it for a neutral dot.
187
+ */
188
+ export function MenuBarPopover({
189
+ footer,
190
+ items,
191
+ mark,
192
+ title,
193
+ ...root
194
+ }: MockupRootProps & Readonly<{ items: readonly MenuBarItem[]; title?: string; mark?: ReactNode; footer?: string }>) {
195
+ const ids = new Set<string>();
196
+ for (const item of items) {
197
+ if (ids.has(item.id)) throw new RangeError(`MenuBarPopover item ids must be unique: ${item.id}`);
198
+ ids.add(item.id);
199
+ }
200
+ return (
201
+ <MockupRoot {...root} kind="menubar">
202
+ <div aria-hidden="true" className="hkm-menubar">
203
+ <span className="hkm-menubar-spacer" />
204
+ <MockupGlyph name="search" size={14} />
205
+ <span className="hkm-menubar-item" data-hkm-open="">{mark ?? <span className="hkm-menubar-dot" />}</span>
206
+ <span className="hkm-menubar-clock">9:41</span>
207
+ </div>
208
+ <div className="hkm-popover">
209
+ {title === undefined ? null : <div className="hkm-popover-title">{title}</div>}
210
+ <ul className="hkm-popover-list">
211
+ {items.map((item) => (
212
+ <li className="hkm-popover-item" data-hkm-selected={item.selected === true ? "" : undefined} data-hkm-tone={item.tone} key={item.id}>
213
+ <span className="hkm-popover-dot" />
214
+ <span className="hkm-popover-text">
215
+ <span className="hkm-popover-label"><SampleText {...optOutProps(root.optOut)}>{item.label}</SampleText></span>
216
+ {item.detail === undefined ? null : <span className="hkm-popover-detail"><SampleText {...optOutProps(root.optOut)}>{item.detail}</SampleText></span>}
217
+ </span>
218
+ </li>
219
+ ))}
220
+ </ul>
221
+ {footer === undefined ? null : <div className="hkm-popover-footer">{footer}</div>}
222
+ </div>
223
+ </MockupRoot>
224
+ );
225
+ }
226
+
227
+ /**
228
+ * A generic modern phone: metal edge, bezel, side buttons, a camera island,
229
+ * status bar, and home indicator. No logos, carrier names, or real numbers.
230
+ * The screen is 390 by 844 points and scales with the root's width. Children
231
+ * sit in a scrollable body that clears the status bar and home indicator.
232
+ */
233
+ export function PhoneFrame({
234
+ children,
235
+ screenHeight,
236
+ statusTime,
237
+ width,
238
+ ...root
239
+ }: MockupRootProps & Readonly<{ statusTime?: string; width?: number; screenHeight?: number; children: ReactNode }>) {
240
+ return (
241
+ <MockupRoot {...root} kind="phone" style={phoneStyle(width, screenHeight)}>
242
+ <PhoneShell {...(statusTime === undefined ? {} : { statusTime })}>
243
+ <div className="hkm-phone-body">{children}</div>
244
+ </PhoneShell>
245
+ </MockupRoot>
246
+ );
247
+ }
248
+
249
+ /** Size variables for a phone root. */
250
+ export function phoneStyle(width: number | undefined, screenHeight: number | undefined): CSSProperties | undefined {
251
+ const style: Record<string, string> = {};
252
+ if (width !== undefined) {
253
+ if (!(width > 0)) throw new RangeError("A phone mockup width must be positive.");
254
+ style["--hkm-phone-width"] = `${width}px`;
255
+ }
256
+ if (screenHeight !== undefined) {
257
+ if (!(screenHeight >= 200 && screenHeight <= 844)) throw new RangeError("A phone mockup screenHeight must be 200 to 844 points.");
258
+ style["--hkm-phone-height"] = String(screenHeight + 22);
259
+ }
260
+ return Object.keys(style).length === 0 ? undefined : (style as CSSProperties);
261
+ }
262
+
263
+ /** The phone device without a root, for surfaces that compose it inside their own root. */
264
+ export function PhoneShell({ children, statusTime = "9:41" }: Readonly<{ statusTime?: string; children: ReactNode }>) {
265
+ return (
266
+ <div className="hkm-device">
267
+ <span aria-hidden="true" className="hkm-device-button" data-hkm-button="action" />
268
+ <span aria-hidden="true" className="hkm-device-button" data-hkm-button="volume-up" />
269
+ <span aria-hidden="true" className="hkm-device-button" data-hkm-button="volume-down" />
270
+ <span aria-hidden="true" className="hkm-device-button" data-hkm-button="power" />
271
+ <div className="hkm-bezel">
272
+ <div className="hkm-screen">
273
+ <div className="hkm-screen-content">{children}</div>
274
+ <div aria-hidden="true" className="hkm-status-bar">
275
+ <span className="hkm-status-time">{statusTime}</span>
276
+ <span className="hkm-status-glyphs">
277
+ <svg className="hkm-status-cell" viewBox="0 0 19 12">
278
+ <rect height="4.5" rx="0.9" width="3.2" x="0" y="7.5" />
279
+ <rect height="6.8" rx="0.9" width="3.2" x="5.1" y="5.2" />
280
+ <rect height="9.3" rx="0.9" width="3.2" x="10.2" y="2.7" />
281
+ <rect height="12" rx="0.9" width="3.2" x="15.3" y="0" />
282
+ </svg>
283
+ <svg className="hkm-status-wifi" viewBox="0 0 17 12.3">
284
+ <path d="M8.5 2.6c2.4 0 4.6.9 6.2 2.4.2.2.5.2.7 0l1.2-1.2c.2-.2.2-.5 0-.7C14.5 1.2 11.6 0 8.5 0S2.5 1.2.4 3.1c-.2.2-.2.5 0 .7L1.6 5c.2.2.5.2.7 0C3.9 3.5 6.1 2.6 8.5 2.6Z" />
285
+ <path d="M8.5 6.4c1.3 0 2.5.5 3.5 1.3.2.2.5.2.7 0l1.2-1.2c.2-.2.2-.5 0-.7-1.4-1.3-3.3-2-5.4-2s-4 .7-5.4 2c-.2.2-.2.5 0 .7l1.2 1.2c.2.2.5.2.7 0 1-.8 2.2-1.3 3.5-1.3Z" />
286
+ <path d="M11.1 9.4c.2-.2.2-.5 0-.7-.7-.6-1.6-1-2.6-1s-1.9.4-2.6 1c-.2.2-.2.5 0 .7l2.2 2.2c.2.2.5.2.7 0l2.3-2.2Z" />
287
+ </svg>
288
+ <svg className="hkm-status-battery" viewBox="0 0 27.4 13">
289
+ <rect fill="none" height="12" rx="3.8" stroke="currentColor" strokeOpacity="0.35" width="24" x="0.5" y="0.5" />
290
+ <rect height="9" rx="2.5" width="21" x="2" y="2" />
291
+ <path d="M26 4.4v4.2c.8-.3 1.4-1.1 1.4-2.1S26.8 4.7 26 4.4Z" fillOpacity="0.4" />
292
+ </svg>
293
+ </span>
294
+ </div>
295
+ <span aria-hidden="true" className="hkm-island" />
296
+ <span aria-hidden="true" className="hkm-home-indicator" />
297
+ </div>
298
+ </div>
299
+ </div>
300
+ );
301
+ }
302
+
303
+ /** One turn in a coding-agent session. */
304
+ export type AgentTurn = Readonly<{
305
+ role: "user" | "agent" | "tool";
306
+ text: string;
307
+ /** For a tool turn: the tool's neutral name, such as "Run tests" or "Edit file". */
308
+ tool?: string;
309
+ status?: "ok" | "warn" | "error" | "running";
310
+ beat?: string;
311
+ }>;
312
+
313
+ /**
314
+ * A neutral coding-agent session. `generic-cli` draws a terminal agent with
315
+ * tool-call blocks; `generic-chat` draws a chat panel with a composer. Neither
316
+ * copies a vendor's chrome, name, or mark.
317
+ */
318
+ export function AgentSession({
319
+ agent,
320
+ fade,
321
+ height,
322
+ title,
323
+ turns,
324
+ ...root
325
+ }: MockupRootProps & Readonly<{ agent: "generic-cli" | "generic-chat"; turns: readonly AgentTurn[]; title?: string; height?: number; fade?: boolean }>) {
326
+ if (agent !== "generic-cli" && agent !== "generic-chat") throw new TypeError(`Unknown agent chrome ${JSON.stringify(agent)}.`);
327
+ const fades = fade ?? height !== undefined;
328
+ const sample = optOutProps(root.optOut);
329
+ return (
330
+ <MockupRoot {...root} kind="agent">
331
+ <div className="hkm-window" data-hkm-agent={agent}>
332
+ <div aria-hidden="true" className="hkm-title-bar">
333
+ <WindowLights />
334
+ <span className="hkm-title">{title ?? (agent === "generic-cli" ? "Coding agent" : "Assistant")}</span>
335
+ <span />
336
+ </div>
337
+ <div className="hkm-page hkm-agent-body" data-hkm-fade={fades ? "" : undefined} style={pageStyle(height)}>
338
+ <ol className="hkm-agent-turns">
339
+ {turns.map((turn, index) => (
340
+ <li className="hkm-agent-turn" data-hkm-beat={turn.beat} data-hkm-role={turn.role} data-hkm-tone={turn.status} key={index}>
341
+ {turn.role === "tool" ? (
342
+ <span className="hkm-agent-tool">
343
+ <span className="hkm-agent-tool-name">
344
+ <span className="hkm-agent-status" />
345
+ {turn.tool ?? "Tool"}
346
+ </span>
347
+ <span className="hkm-agent-tool-output"><SampleText {...sample}>{turn.text}</SampleText></span>
348
+ </span>
349
+ ) : (
350
+ <>
351
+ <span aria-hidden="true" className="hkm-agent-marker">{turn.role === "user" ? "›" : <MockupGlyph name="sparkle" size={14} />}</span>
352
+ <span className="hkm-agent-text"><SampleText {...sample}>{turn.text}</SampleText></span>
353
+ </>
354
+ )}
355
+ </li>
356
+ ))}
357
+ </ol>
358
+ </div>
359
+ {agent === "generic-chat" ? (
360
+ <div aria-hidden="true" className="hkm-agent-composer">
361
+ <span className="hkm-agent-composer-field">Ask for a change</span>
362
+ <span className="hkm-agent-composer-send"><MockupGlyph name="send" size={14} /></span>
363
+ </div>
364
+ ) : null}
365
+ </div>
366
+ </MockupRoot>
367
+ );
368
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Code-built mockups: neutral browser, terminal, phone, desktop, chat, feed,
3
+ * inbox, and article frames for product pages, launch posts, and films.
4
+ *
5
+ * Server-safe plain React with one stylesheet, `@hraness/design-kit/mockups.css`.
6
+ * Nothing here uses StyleX, React Aria, hooks, or a client boundary, so a
7
+ * film build can render it with `renderToStaticMarkup` under plain Bun.
8
+ * Interactive shells live in `@hraness/design-kit/mockups/client`.
9
+ */
10
+ // `export *` rather than named re-exports: Bun 1.3 emitted named re-exports
11
+ // from these side-effect-free modules with no bindings.
12
+ export * from "./core.js";
13
+ export * from "./frames.js";
14
+ export * from "./surfaces.js";