@rtkelly-labs/design-system 0.0.0-bootstrap.0 → 0.16.0-dev.0.37dedac

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 (284) hide show
  1. package/CHANGELOG.md +347 -0
  2. package/LICENSE +21 -0
  3. package/README.md +317 -2
  4. package/dist/components/AlertDialog.js +95 -0
  5. package/dist/components/AlertDialog.js.map +1 -0
  6. package/dist/components/AppShell.js +261 -0
  7. package/dist/components/AppShell.js.map +1 -0
  8. package/dist/components/AsciiDivider.js +8 -0
  9. package/dist/components/AsciiDivider.js.map +1 -0
  10. package/dist/components/AsciiGauge.js +130 -0
  11. package/dist/components/AsciiGauge.js.map +1 -0
  12. package/dist/components/Avatar.js +87 -0
  13. package/dist/components/Avatar.js.map +1 -0
  14. package/dist/components/Badge.js +25 -0
  15. package/dist/components/Badge.js.map +1 -0
  16. package/dist/components/BarChart.js +288 -0
  17. package/dist/components/BarChart.js.map +1 -0
  18. package/dist/components/BracketText.js +23 -0
  19. package/dist/components/BracketText.js.map +1 -0
  20. package/dist/components/BulletChart.js +50 -0
  21. package/dist/components/BulletChart.js.map +1 -0
  22. package/dist/components/Button.js +130 -0
  23. package/dist/components/Button.js.map +1 -0
  24. package/dist/components/Card.js +181 -0
  25. package/dist/components/Card.js.map +1 -0
  26. package/dist/components/ChartTooltip.js +54 -0
  27. package/dist/components/ChartTooltip.js.map +1 -0
  28. package/dist/components/Checkbox.js +55 -0
  29. package/dist/components/Checkbox.js.map +1 -0
  30. package/dist/components/ColophonCard.js +122 -0
  31. package/dist/components/ColophonCard.js.map +1 -0
  32. package/dist/components/DataTable.js +299 -0
  33. package/dist/components/DataTable.js.map +1 -0
  34. package/dist/components/Divider.js +42 -0
  35. package/dist/components/Divider.js.map +1 -0
  36. package/dist/components/Drawer.js +81 -0
  37. package/dist/components/Drawer.js.map +1 -0
  38. package/dist/components/EmptyState.js +39 -0
  39. package/dist/components/EmptyState.js.map +1 -0
  40. package/dist/components/ErrorSummary.js +122 -0
  41. package/dist/components/ErrorSummary.js.map +1 -0
  42. package/dist/components/Fieldset.js +37 -0
  43. package/dist/components/Fieldset.js.map +1 -0
  44. package/dist/components/Glyph.js +60 -0
  45. package/dist/components/Glyph.js.map +1 -0
  46. package/dist/components/Input.js +111 -0
  47. package/dist/components/Input.js.map +1 -0
  48. package/dist/components/LinkProvider.js +76 -0
  49. package/dist/components/LinkProvider.js.map +1 -0
  50. package/dist/components/Menu.js +144 -0
  51. package/dist/components/Menu.js.map +1 -0
  52. package/dist/components/MetadataGrid.js +184 -0
  53. package/dist/components/MetadataGrid.js.map +1 -0
  54. package/dist/components/MobileNav.js +120 -0
  55. package/dist/components/MobileNav.js.map +1 -0
  56. package/dist/components/Modal.js +79 -0
  57. package/dist/components/Modal.js.map +1 -0
  58. package/dist/components/NerdIcon.js +177 -0
  59. package/dist/components/NerdIcon.js.map +1 -0
  60. package/dist/components/NoteBlock.js +84 -0
  61. package/dist/components/NoteBlock.js.map +1 -0
  62. package/dist/components/PageHeader.js +34 -0
  63. package/dist/components/PageHeader.js.map +1 -0
  64. package/dist/components/PageTitle.js +21 -0
  65. package/dist/components/PageTitle.js.map +1 -0
  66. package/dist/components/Pagination.js +96 -0
  67. package/dist/components/Pagination.js.map +1 -0
  68. package/dist/components/Popover.js +87 -0
  69. package/dist/components/Popover.js.map +1 -0
  70. package/dist/components/Progress.js +83 -0
  71. package/dist/components/Progress.js.map +1 -0
  72. package/dist/components/RadioGroup.js +74 -0
  73. package/dist/components/RadioGroup.js.map +1 -0
  74. package/dist/components/ReportDocument.js +75 -0
  75. package/dist/components/ReportDocument.js.map +1 -0
  76. package/dist/components/SectionContainer.js +18 -0
  77. package/dist/components/SectionContainer.js.map +1 -0
  78. package/dist/components/Select.js +199 -0
  79. package/dist/components/Select.js.map +1 -0
  80. package/dist/components/SiteFooter.js +29 -0
  81. package/dist/components/SiteFooter.js.map +1 -0
  82. package/dist/components/SiteHeader.js +59 -0
  83. package/dist/components/SiteHeader.js.map +1 -0
  84. package/dist/components/SiteNav.js +141 -0
  85. package/dist/components/SiteNav.js.map +1 -0
  86. package/dist/components/Skeleton.js +51 -0
  87. package/dist/components/Skeleton.js.map +1 -0
  88. package/dist/components/SocialIcon.js +77 -0
  89. package/dist/components/SocialIcon.js.map +1 -0
  90. package/dist/components/Sparkline.js +104 -0
  91. package/dist/components/Sparkline.js.map +1 -0
  92. package/dist/components/Spinner.js +65 -0
  93. package/dist/components/Spinner.js.map +1 -0
  94. package/dist/components/StatCard.js +53 -0
  95. package/dist/components/StatCard.js.map +1 -0
  96. package/dist/components/StatusPage.js +99 -0
  97. package/dist/components/StatusPage.js.map +1 -0
  98. package/dist/components/Swatch.js +55 -0
  99. package/dist/components/Swatch.js.map +1 -0
  100. package/dist/components/Switch.js +53 -0
  101. package/dist/components/Switch.js.map +1 -0
  102. package/dist/components/TLDR.js +53 -0
  103. package/dist/components/TLDR.js.map +1 -0
  104. package/dist/components/Table.js +155 -0
  105. package/dist/components/Table.js.map +1 -0
  106. package/dist/components/Tabs.js +325 -0
  107. package/dist/components/Tabs.js.map +1 -0
  108. package/dist/components/Tag.js +19 -0
  109. package/dist/components/Tag.js.map +1 -0
  110. package/dist/components/TelemetryGauge.js +112 -0
  111. package/dist/components/TelemetryGauge.js.map +1 -0
  112. package/dist/components/ThemeProvider.js +87 -0
  113. package/dist/components/ThemeProvider.js.map +1 -0
  114. package/dist/components/TickerTape.js +196 -0
  115. package/dist/components/TickerTape.js.map +1 -0
  116. package/dist/components/Toast.js +200 -0
  117. package/dist/components/Toast.js.map +1 -0
  118. package/dist/components/Tooltip.js +69 -0
  119. package/dist/components/Tooltip.js.map +1 -0
  120. package/dist/components/TrackCard.js +206 -0
  121. package/dist/components/TrackCard.js.map +1 -0
  122. package/dist/components/blog/BlogPost.js +116 -0
  123. package/dist/components/blog/BlogPost.js.map +1 -0
  124. package/dist/components/blog/LoremIpsumPost.js +62 -0
  125. package/dist/components/blog/LoremIpsumPost.js.map +1 -0
  126. package/dist/components/blog/author.js +25 -0
  127. package/dist/components/blog/author.js.map +1 -0
  128. package/dist/components/booleanControl.js +50 -0
  129. package/dist/components/booleanControl.js.map +1 -0
  130. package/dist/components/dialogPopupRef.js +28 -0
  131. package/dist/components/dialogPopupRef.js.map +1 -0
  132. package/dist/components/dialogSurface.js +75 -0
  133. package/dist/components/dialogSurface.js.map +1 -0
  134. package/dist/components/docs/AnchorHeading.js +80 -0
  135. package/dist/components/docs/AnchorHeading.js.map +1 -0
  136. package/dist/components/docs/Breadcrumbs.js +26 -0
  137. package/dist/components/docs/Breadcrumbs.js.map +1 -0
  138. package/dist/components/docs/CodeBlock.js +63 -0
  139. package/dist/components/docs/CodeBlock.js.map +1 -0
  140. package/dist/components/docs/CodeTabs.js +71 -0
  141. package/dist/components/docs/CodeTabs.js.map +1 -0
  142. package/dist/components/docs/DocPager.js +28 -0
  143. package/dist/components/docs/DocPager.js.map +1 -0
  144. package/dist/components/docs/DocsHeader.js +124 -0
  145. package/dist/components/docs/DocsHeader.js.map +1 -0
  146. package/dist/components/docs/DocsLayout.js +61 -0
  147. package/dist/components/docs/DocsLayout.js.map +1 -0
  148. package/dist/components/docs/DocsLinkProvider.js +25 -0
  149. package/dist/components/docs/DocsLinkProvider.js.map +1 -0
  150. package/dist/components/docs/DocsSidebar.js +147 -0
  151. package/dist/components/docs/DocsSidebar.js.map +1 -0
  152. package/dist/components/docs/Prose.js +56 -0
  153. package/dist/components/docs/Prose.js.map +1 -0
  154. package/dist/components/docs/TableOfContents.js +71 -0
  155. package/dist/components/docs/TableOfContents.js.map +1 -0
  156. package/dist/components/docs/codeTabsStore.js +75 -0
  157. package/dist/components/docs/codeTabsStore.js.map +1 -0
  158. package/dist/components/docs/figures/ActivityGrid.js +116 -0
  159. package/dist/components/docs/figures/ActivityGrid.js.map +1 -0
  160. package/dist/components/docs/figures/BeforeAfter.js +64 -0
  161. package/dist/components/docs/figures/BeforeAfter.js.map +1 -0
  162. package/dist/components/docs/figures/ChangeSummary.js +74 -0
  163. package/dist/components/docs/figures/ChangeSummary.js.map +1 -0
  164. package/dist/components/docs/figures/Decision.js +34 -0
  165. package/dist/components/docs/figures/Decision.js.map +1 -0
  166. package/dist/components/docs/figures/DrawingSheet.js +37 -0
  167. package/dist/components/docs/figures/DrawingSheet.js.map +1 -0
  168. package/dist/components/docs/figures/FigureFrame.js +226 -0
  169. package/dist/components/docs/figures/FigureFrame.js.map +1 -0
  170. package/dist/components/docs/figures/FileChangeTree.js +134 -0
  171. package/dist/components/docs/figures/FileChangeTree.js.map +1 -0
  172. package/dist/components/docs/figures/FlowDiagram.js +82 -0
  173. package/dist/components/docs/figures/FlowDiagram.js.map +1 -0
  174. package/dist/components/docs/figures/GanttChart.js +79 -0
  175. package/dist/components/docs/figures/GanttChart.js.map +1 -0
  176. package/dist/components/docs/figures/IndexedSection.js +45 -0
  177. package/dist/components/docs/figures/IndexedSection.js.map +1 -0
  178. package/dist/components/docs/figures/MetricStrip.js +23 -0
  179. package/dist/components/docs/figures/MetricStrip.js.map +1 -0
  180. package/dist/components/docs/figures/Steps.js +58 -0
  181. package/dist/components/docs/figures/Steps.js.map +1 -0
  182. package/dist/components/docs/figures/SwatchCompare.js +42 -0
  183. package/dist/components/docs/figures/SwatchCompare.js.map +1 -0
  184. package/dist/components/docs/figures/TermGrid.js +22 -0
  185. package/dist/components/docs/figures/TermGrid.js.map +1 -0
  186. package/dist/components/docs/figures/Terminal.js +37 -0
  187. package/dist/components/docs/figures/Terminal.js.map +1 -0
  188. package/dist/components/docs/figures/Timeline.js +61 -0
  189. package/dist/components/docs/figures/Timeline.js.map +1 -0
  190. package/dist/components/docs/figures/TokenTable.js +42 -0
  191. package/dist/components/docs/figures/TokenTable.js.map +1 -0
  192. package/dist/components/docs/figures/TreeDiagram.js +76 -0
  193. package/dist/components/docs/figures/TreeDiagram.js.map +1 -0
  194. package/dist/components/docs/figures/UptimeStrip.js +68 -0
  195. package/dist/components/docs/figures/UptimeStrip.js.map +1 -0
  196. package/dist/components/docs/figures/index.js +24 -0
  197. package/dist/components/docs/figures/index.js.map +1 -0
  198. package/dist/components/docs/index.js +15 -0
  199. package/dist/components/docs/index.js.map +1 -0
  200. package/dist/components/docs/mdxComponents.js +108 -0
  201. package/dist/components/docs/mdxComponents.js.map +1 -0
  202. package/dist/components/experiments/DesignSandbox.js +206 -0
  203. package/dist/components/experiments/DesignSandbox.js.map +1 -0
  204. package/dist/components/experiments/ExperimentsView.js +138 -0
  205. package/dist/components/experiments/ExperimentsView.js.map +1 -0
  206. package/dist/components/fieldFrame.js +200 -0
  207. package/dist/components/fieldFrame.js.map +1 -0
  208. package/dist/components/floatingSurface.js +35 -0
  209. package/dist/components/floatingSurface.js.map +1 -0
  210. package/dist/components/marketing/CTASection.js +47 -0
  211. package/dist/components/marketing/CTASection.js.map +1 -0
  212. package/dist/components/marketing/FeatureGrid.js +49 -0
  213. package/dist/components/marketing/FeatureGrid.js.map +1 -0
  214. package/dist/components/marketing/Hero.js +44 -0
  215. package/dist/components/marketing/Hero.js.map +1 -0
  216. package/dist/components/marketing/PricingGrid.js +65 -0
  217. package/dist/components/marketing/PricingGrid.js.map +1 -0
  218. package/dist/components/marketing/SectionGrid.js +51 -0
  219. package/dist/components/marketing/SectionGrid.js.map +1 -0
  220. package/dist/components/paginationRange.js +28 -0
  221. package/dist/components/paginationRange.js.map +1 -0
  222. package/dist/components/portalTheme.js +14 -0
  223. package/dist/components/portalTheme.js.map +1 -0
  224. package/dist/components/saas/SaasLandingPage.js +201 -0
  225. package/dist/components/saas/SaasLandingPage.js.map +1 -0
  226. package/dist/components/siteNavContext.js +24 -0
  227. package/dist/components/siteNavContext.js.map +1 -0
  228. package/dist/components/slides/Slide.js +84 -0
  229. package/dist/components/slides/Slide.js.map +1 -0
  230. package/dist/components/slides/SlideDeck.js +228 -0
  231. package/dist/components/slides/SlideDeck.js.map +1 -0
  232. package/dist/components/themeInitScript.js +16 -0
  233. package/dist/components/themeInitScript.js.map +1 -0
  234. package/dist/components/themeScope.js +9 -0
  235. package/dist/components/themeScope.js.map +1 -0
  236. package/dist/components/toastLifetime.js +21 -0
  237. package/dist/components/toastLifetime.js.map +1 -0
  238. package/dist/fonts/DSSymbols-OFL.txt +98 -0
  239. package/dist/fonts/DSSymbols-Regular.woff2 +0 -0
  240. package/dist/fonts/SymbolsNerdFontMono-Regular.woff2 +0 -0
  241. package/dist/hooks/useActiveHeading.js +59 -0
  242. package/dist/hooks/useActiveHeading.js.map +1 -0
  243. package/dist/hooks/useCopyToClipboard.js +58 -0
  244. package/dist/hooks/useCopyToClipboard.js.map +1 -0
  245. package/dist/index.d.ts +6394 -0
  246. package/dist/index.js +117 -0
  247. package/dist/index.js.map +1 -0
  248. package/dist/lib/accentClasses.js +87 -0
  249. package/dist/lib/accentClasses.js.map +1 -0
  250. package/dist/lib/recipe.js +33 -0
  251. package/dist/lib/recipe.js.map +1 -0
  252. package/dist/lib/slug.js +39 -0
  253. package/dist/lib/slug.js.map +1 -0
  254. package/dist/lib/theme.js +76 -0
  255. package/dist/lib/theme.js.map +1 -0
  256. package/dist/lib/tokenRules.js +51 -0
  257. package/dist/lib/tokenRules.js.map +1 -0
  258. package/dist/prose.css +766 -0
  259. package/dist/styles.css +203 -0
  260. package/dist/theme/ansi.js +77 -0
  261. package/dist/theme/ansi.js.map +1 -0
  262. package/dist/theme/contrast.js +342 -0
  263. package/dist/theme/contrast.js.map +1 -0
  264. package/dist/theme/levels.js +234 -0
  265. package/dist/theme/levels.js.map +1 -0
  266. package/dist/theme/media.js +115 -0
  267. package/dist/theme/media.js.map +1 -0
  268. package/dist/theme/recommended.js +105 -0
  269. package/dist/theme/recommended.js.map +1 -0
  270. package/dist/theme.css +538 -0
  271. package/dist/tokens.js +1 -0
  272. package/dist/tokens.js.map +1 -0
  273. package/package.json +209 -6
  274. package/skills/design-system-usage/SKILL.md +75 -0
  275. package/terminal/rtk-midnight.alacritty.toml +33 -0
  276. package/terminal/rtk-midnight.ghostty.conf +24 -0
  277. package/terminal/rtk-midnight.itermcolors +167 -0
  278. package/terminal/rtk-midnight.windows-terminal.json +23 -0
  279. package/terminal/rtk-sketch.alacritty.toml +33 -0
  280. package/terminal/rtk-sketch.ghostty.conf +24 -0
  281. package/terminal/rtk-sketch.itermcolors +167 -0
  282. package/terminal/rtk-sketch.windows-terminal.json +23 -0
  283. package/tokens/palette.midnight.tokens.json +477 -0
  284. package/tokens/palette.sketch.tokens.json +477 -0
@@ -0,0 +1,56 @@
1
+ // src/components/docs/Prose.tsx
2
+ import { recipe } from "../../lib/recipe.js";
3
+ import { jsx } from "react/jsx-runtime";
4
+ var prose = recipe({
5
+ base: [
6
+ "prose prose-ladder max-w-[var(--docs-content-max)]",
7
+ // `prose.css` promises to work without Tailwind's preflight, so the
8
+ // border-box reset cannot be assumed here.
9
+ "box-border [&_*]:box-border"
10
+ ],
11
+ variants: {
12
+ /** The house style. Off only for a preview that wants plugin defaults. */
13
+ brutalist: {
14
+ true: [
15
+ // Headings: display face, uppercase, heavy — and a hierarchy of rules
16
+ // rather than a hierarchy of sizes alone.
17
+ "prose-headings:font-display prose-headings:font-extrabold prose-headings:uppercase prose-headings:tracking-[0.01em]",
18
+ "prose-h1:border-b-4 prose-h1:border-double prose-h1:border-edge-strong prose-h1:pb-3",
19
+ "prose-h2:border-b-2 prose-h2:border-solid prose-h2:border-edge-strong prose-h2:pb-1.5",
20
+ "prose-h3:text-accent-primary prose-h4:text-accent-secondary",
21
+ // Links invert on hover rather than changing shade — the same move
22
+ // Button makes, so the article and the chrome agree.
23
+ "prose-a:underline prose-a:decoration-2 prose-a:underline-offset-[3px]",
24
+ "prose-a:hover:bg-accent-primary prose-a:hover:text-content-inverse prose-a:hover:no-underline",
25
+ // Square markers, native rather than drawn: `list-[square]` is a real
26
+ // marker, so it takes --tw-prose-bullets and needs no ::before.
27
+ "prose-ul:list-[square]",
28
+ "prose-ol:marker:font-mono prose-ol:marker:font-bold",
29
+ // Inline code as a hard-edged chip. `pre > code` is untouched — the code
30
+ // block owns that.
31
+ "prose-code:border prose-code:border-edge-default prose-code:bg-surface-raised",
32
+ "prose-code:px-[0.35rem] prose-code:py-[0.1rem] prose-code:before:content-none prose-code:after:content-none",
33
+ "prose-blockquote:border-l-4 prose-blockquote:bg-surface-raised prose-blockquote:px-5 prose-blockquote:py-3",
34
+ "prose-hr:border-t-2 prose-hr:border-dashed prose-hr:opacity-50",
35
+ "prose-img:border-2 prose-img:border-edge-strong",
36
+ "prose-kbd:border-2 prose-kbd:border-edge-strong prose-kbd:shadow-hard-sm",
37
+ // Tables are data, so they read in mono at a tighter size, with every
38
+ // cell ruled rather than only the header.
39
+ "prose-table:border-2 prose-table:border-edge-strong prose-table:font-mono prose-table:text-[0.85rem]",
40
+ "prose-thead:bg-surface-raised",
41
+ "prose-th:border-2 prose-th:border-edge-strong prose-th:uppercase prose-th:tracking-[0.04em] prose-th:text-accent-primary",
42
+ "prose-td:border-2 prose-td:border-edge-default prose-td:align-top"
43
+ ]
44
+ }
45
+ },
46
+ defaultVariants: {
47
+ brutalist: true
48
+ }
49
+ });
50
+ function Prose({ children, brutalist = true, className = "", ...rest }) {
51
+ return /* @__PURE__ */ jsx("div", { "data-slot": "prose", className: prose({ brutalist, class: className }), ...rest, children });
52
+ }
53
+ export {
54
+ Prose
55
+ };
56
+ //# sourceMappingURL=Prose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/docs/Prose.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from 'react';\nimport { recipe } from '../../lib/recipe';\n\n/**\n * Typography scope for compiled Markdown/MDX output.\n *\n * Everything a Markdown pipeline emits — `ul`, `table`, `blockquote`, inline\n * `code` — arrives as bare HTML with no class names, so there is nothing for a\n * utility to attach to. `@tailwindcss/typography` exists for precisely that,\n * and `prose-ladder` (in `prose.css`) points its `--tw-prose-*` variables at\n * this system's role tokens, so prose follows the theme ladder with no per-level\n * rules of its own.\n *\n * The recipe below is the **delta** between the plugin's defaults and this\n * system: display headings in uppercase, rules under h1 and h2, hard-edged code\n * chips, mono tables, square list markers. Element modifiers (`prose-h1:`,\n * `prose-table:`) are the plugin's own mechanism for exactly this, so the\n * styling lives on the element as utilities and no descendant CSS is authored.\n *\n * ## Nesting chrome inside an article\n *\n * A breadcrumb above the page title and a pager below the body both read as\n * part of the article, so they get rendered inside this scope — and used to need\n * ~70 declarations of reset in `prose.css` to stop them inheriting list markers\n * and the prose link treatment. The plugin has a designed escape hatch for that:\n * every selector it generates excludes `.not-prose` subtrees. Chrome carries\n * that class and the whole reset block goes away.\n */\n\nconst prose = recipe({\n base: [\n 'prose prose-ladder max-w-[var(--docs-content-max)]',\n // `prose.css` promises to work without Tailwind's preflight, so the\n // border-box reset cannot be assumed here.\n 'box-border [&_*]:box-border',\n ],\n variants: {\n /** The house style. Off only for a preview that wants plugin defaults. */\n brutalist: {\n true: [\n // Headings: display face, uppercase, heavy — and a hierarchy of rules\n // rather than a hierarchy of sizes alone.\n 'prose-headings:font-display prose-headings:font-extrabold prose-headings:uppercase prose-headings:tracking-[0.01em]',\n 'prose-h1:border-b-4 prose-h1:border-double prose-h1:border-edge-strong prose-h1:pb-3',\n 'prose-h2:border-b-2 prose-h2:border-solid prose-h2:border-edge-strong prose-h2:pb-1.5',\n 'prose-h3:text-accent-primary prose-h4:text-accent-secondary',\n\n // Links invert on hover rather than changing shade — the same move\n // Button makes, so the article and the chrome agree.\n 'prose-a:underline prose-a:decoration-2 prose-a:underline-offset-[3px]',\n 'prose-a:hover:bg-accent-primary prose-a:hover:text-content-inverse prose-a:hover:no-underline',\n\n // Square markers, native rather than drawn: `list-[square]` is a real\n // marker, so it takes --tw-prose-bullets and needs no ::before.\n 'prose-ul:list-[square]',\n 'prose-ol:marker:font-mono prose-ol:marker:font-bold',\n\n // Inline code as a hard-edged chip. `pre > code` is untouched — the code\n // block owns that.\n 'prose-code:border prose-code:border-edge-default prose-code:bg-surface-raised',\n 'prose-code:px-[0.35rem] prose-code:py-[0.1rem] prose-code:before:content-none prose-code:after:content-none',\n\n 'prose-blockquote:border-l-4 prose-blockquote:bg-surface-raised prose-blockquote:px-5 prose-blockquote:py-3',\n 'prose-hr:border-t-2 prose-hr:border-dashed prose-hr:opacity-50',\n 'prose-img:border-2 prose-img:border-edge-strong',\n 'prose-kbd:border-2 prose-kbd:border-edge-strong prose-kbd:shadow-hard-sm',\n\n // Tables are data, so they read in mono at a tighter size, with every\n // cell ruled rather than only the header.\n 'prose-table:border-2 prose-table:border-edge-strong prose-table:font-mono prose-table:text-[0.85rem]',\n 'prose-thead:bg-surface-raised',\n 'prose-th:border-2 prose-th:border-edge-strong prose-th:uppercase prose-th:tracking-[0.04em] prose-th:text-accent-primary',\n 'prose-td:border-2 prose-td:border-edge-default prose-td:align-top',\n ],\n },\n },\n defaultVariants: {\n brutalist: true,\n },\n});\n\nexport interface ProseProps extends Omit<HTMLAttributes<HTMLDivElement>, 'className'> {\n children: ReactNode;\n /**\n * Apply the house typography on top of the plugin's defaults. Turn off only\n * to preview what the tokens alone produce.\n */\n brutalist?: boolean;\n className?: string;\n}\n\nexport function Prose({ children, brutalist = true, className = '', ...rest }: ProseProps) {\n return (\n <div data-slot=\"prose\" className={prose({ brutalist, class: className })} {...rest}>\n {children}\n </div>\n );\n}\n"],"mappings":";AACA,SAAS,cAAc;AA4FnB;AAhEJ,IAAM,QAAQ,OAAO;AAAA,EACnB,MAAM;AAAA,IACJ;AAAA;AAAA;AAAA,IAGA;AAAA,EACF;AAAA,EACA,UAAU;AAAA;AAAA,IAER,WAAW;AAAA,MACT,MAAM;AAAA;AAAA;AAAA,QAGJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA;AAAA,QAIA;AAAA,QACA;AAAA;AAAA;AAAA,QAIA;AAAA,QACA;AAAA;AAAA;AAAA,QAIA;AAAA,QACA;AAAA,QAEA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA;AAAA,QAIA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA,IACf,WAAW;AAAA,EACb;AACF,CAAC;AAYM,SAAS,MAAM,EAAE,UAAU,YAAY,MAAM,YAAY,IAAI,GAAG,KAAK,GAAe;AACzF,SACE,oBAAC,SAAI,aAAU,SAAQ,WAAW,MAAM,EAAE,WAAW,OAAO,UAAU,CAAC,GAAI,GAAG,MAC3E,UACH;AAEJ;","names":[]}
@@ -0,0 +1,71 @@
1
+ "use client";
2
+
3
+ // src/components/docs/TableOfContents.tsx
4
+ import { useCallback, useMemo } from "react";
5
+ import { useActiveHeading } from "../../hooks/useActiveHeading.js";
6
+ import { cn } from "../../lib/recipe.js";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ function TableOfContents({
9
+ toc,
10
+ fromDepth = 2,
11
+ toDepth = 3,
12
+ label = "ON THIS PAGE",
13
+ spy = true,
14
+ className = ""
15
+ }) {
16
+ const visible = useMemo(
17
+ () => toc.filter((e) => e.depth >= fromDepth && e.depth <= toDepth),
18
+ [toc, fromDepth, toDepth]
19
+ );
20
+ const ids = useMemo(() => visible.map((e) => e.id), [visible]);
21
+ const active = useActiveHeading(ids, { enabled: spy });
22
+ const onClick = useCallback((event, id) => {
23
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
24
+ event.preventDefault();
25
+ const target = document.getElementById(id);
26
+ if (!target) return;
27
+ target.scrollIntoView({ behavior: "smooth" });
28
+ window.history.replaceState(null, "", `#${id}`);
29
+ target.setAttribute("tabindex", "-1");
30
+ target.focus({ preventScroll: true });
31
+ }, []);
32
+ if (visible.length === 0) return null;
33
+ return /* @__PURE__ */ jsxs("nav", { "data-slot": "table-of-contents", className: cn("not-prose", className), "aria-label": "Table of contents", children: [
34
+ label && /* @__PURE__ */ jsxs("p", { className: "docs-toc-label", children: [
35
+ "[ ",
36
+ label,
37
+ " ]"
38
+ ] }),
39
+ /* @__PURE__ */ jsx("ul", { className: "docs-toc-list", children: visible.map((entry) => /* @__PURE__ */ jsx(
40
+ "li",
41
+ {
42
+ style: { paddingLeft: `${(entry.depth - fromDepth) * 0.75}rem` },
43
+ children: /* @__PURE__ */ jsx(
44
+ "a",
45
+ {
46
+ href: `#${entry.id}`,
47
+ onClick: (event) => onClick(event, entry.id),
48
+ className: "docs-toc-link",
49
+ "data-active": active === entry.id ? "true" : void 0,
50
+ ...active === entry.id ? { "aria-current": "location" } : {},
51
+ children: entry.title
52
+ }
53
+ )
54
+ },
55
+ entry.id
56
+ )) })
57
+ ] });
58
+ }
59
+ function collectHeadings(container, selector = "h2, h3, h4") {
60
+ if (!container) return [];
61
+ return Array.from(container.querySelectorAll(selector)).filter((el) => el.id).map((el) => ({
62
+ id: el.id,
63
+ title: el.textContent?.replace(/COPY LINK|COPIED/g, "").trim() ?? "",
64
+ depth: Number(el.tagName.slice(1))
65
+ }));
66
+ }
67
+ export {
68
+ TableOfContents,
69
+ collectHeadings
70
+ };
71
+ //# sourceMappingURL=TableOfContents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/docs/TableOfContents.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useMemo } from 'react';\nimport type { MouseEvent } from 'react';\nimport { useActiveHeading } from '../../hooks/useActiveHeading';\nimport { cn } from '../../lib/recipe';\n\nexport interface TocEntry {\n /** Anchor id, without the leading `#`. */\n id: string;\n title: string;\n /** Heading level, 1–6. */\n depth: number;\n}\n\nexport interface TableOfContentsProps {\n /** Flat, document-ordered heading list. Nesting is derived from `depth`. */\n toc: readonly TocEntry[];\n /** Shallowest heading level to include. Defaults to 2 — `h1` is the page title. */\n fromDepth?: number;\n /** Deepest heading level to include. Defaults to 3. */\n toDepth?: number;\n /** Heading above the list. Pass `null` to omit it. */\n label?: string | null;\n /** Disable scroll-spy — useful in Storybook and other non-scrolling frames. */\n spy?: boolean;\n className?: string;\n}\n\n/**\n * Right-rail contents list with scroll-spy.\n *\n * Anchor clicks are intercepted so the hash lands in the address bar via\n * `replaceState` instead of `pushState`: scrubbing a long page through the\n * contents list would otherwise stack a history entry per section and turn the\n * browser back button into a section-by-section rewind.\n */\nexport function TableOfContents({\n toc,\n fromDepth = 2,\n toDepth = 3,\n label = 'ON THIS PAGE',\n spy = true,\n className = '',\n}: TableOfContentsProps) {\n const visible = useMemo(\n () => toc.filter((e) => e.depth >= fromDepth && e.depth <= toDepth),\n [toc, fromDepth, toDepth],\n );\n\n const ids = useMemo(() => visible.map((e) => e.id), [visible]);\n const active = useActiveHeading(ids, { enabled: spy });\n\n const onClick = useCallback((event: MouseEvent<HTMLAnchorElement>, id: string) => {\n if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;\n event.preventDefault();\n\n const target = document.getElementById(id);\n if (!target) return;\n\n target.scrollIntoView({ behavior: 'smooth' });\n window.history.replaceState(null, '', `#${id}`);\n\n // scrollIntoView does not move focus, so a keyboard user would stay\n // parked in the contents list. Focus the heading without a second scroll.\n target.setAttribute('tabindex', '-1');\n target.focus({ preventScroll: true });\n }, []);\n\n if (visible.length === 0) return null;\n\n return (\n <nav data-slot=\"table-of-contents\" className={cn('not-prose', className)} aria-label=\"Table of contents\">\n {label && <p className=\"docs-toc-label\">[ {label} ]</p>}\n <ul className=\"docs-toc-list\">\n {visible.map((entry) => (\n <li\n key={entry.id}\n style={{ paddingLeft: `${(entry.depth - fromDepth) * 0.75}rem` }}\n >\n <a\n href={`#${entry.id}`}\n onClick={(event) => onClick(event, entry.id)}\n className=\"docs-toc-link\"\n data-active={active === entry.id ? 'true' : undefined}\n {...(active === entry.id ? { 'aria-current': 'location' as const } : {})}\n >\n {entry.title}\n </a>\n </li>\n ))}\n </ul>\n </nav>\n );\n}\n\n/**\n * Build a {@link TocEntry} list by reading headings out of the rendered DOM.\n *\n * Use when the content pipeline does not already hand you a TOC (a build-time\n * `remark` plugin is cheaper and works during SSR). Headings without an `id`\n * are skipped rather than assigned one — a generated id here would not match\n * the anchors elsewhere on the page.\n */\nexport function collectHeadings(\n container: ParentNode | null | undefined,\n selector = 'h2, h3, h4',\n): TocEntry[] {\n if (!container) return [];\n\n return Array.from(container.querySelectorAll<HTMLHeadingElement>(selector))\n .filter((el) => el.id)\n .map((el) => ({\n id: el.id,\n title: el.textContent?.replace(/COPY LINK|COPIED/g, '').trim() ?? '',\n depth: Number(el.tagName.slice(1)),\n }));\n}\n"],"mappings":";;;AAEA,SAAS,aAAa,eAAe;AAErC,SAAS,wBAAwB;AACjC,SAAS,UAAU;AAoEH,SAOJ,KAPI;AApCT,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,YAAY;AACd,GAAyB;AACvB,QAAM,UAAU;AAAA,IACd,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,SAAS,OAAO;AAAA,IAClE,CAAC,KAAK,WAAW,OAAO;AAAA,EAC1B;AAEA,QAAM,MAAM,QAAQ,MAAM,QAAQ,IAAI,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC;AAC7D,QAAM,SAAS,iBAAiB,KAAK,EAAE,SAAS,IAAI,CAAC;AAErD,QAAM,UAAU,YAAY,CAAC,OAAsC,OAAe;AAChF,QAAI,MAAM,WAAW,MAAM,WAAW,MAAM,YAAY,MAAM,OAAQ;AACtE,UAAM,eAAe;AAErB,UAAM,SAAS,SAAS,eAAe,EAAE;AACzC,QAAI,CAAC,OAAQ;AAEb,WAAO,eAAe,EAAE,UAAU,SAAS,CAAC;AAC5C,WAAO,QAAQ,aAAa,MAAM,IAAI,IAAI,EAAE,EAAE;AAI9C,WAAO,aAAa,YAAY,IAAI;AACpC,WAAO,MAAM,EAAE,eAAe,KAAK,CAAC;AAAA,EACtC,GAAG,CAAC,CAAC;AAEL,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,SACE,qBAAC,SAAI,aAAU,qBAAoB,WAAW,GAAG,aAAa,SAAS,GAAG,cAAW,qBAClF;AAAA,aAAS,qBAAC,OAAE,WAAU,kBAAiB;AAAA;AAAA,MAAG;AAAA,MAAM;AAAA,OAAE;AAAA,IACnD,oBAAC,QAAG,WAAU,iBACX,kBAAQ,IAAI,CAAC,UACZ;AAAA,MAAC;AAAA;AAAA,QAEC,OAAO,EAAE,aAAa,IAAI,MAAM,QAAQ,aAAa,IAAI,MAAM;AAAA,QAE/D;AAAA,UAAC;AAAA;AAAA,YACC,MAAM,IAAI,MAAM,EAAE;AAAA,YAClB,SAAS,CAAC,UAAU,QAAQ,OAAO,MAAM,EAAE;AAAA,YAC3C,WAAU;AAAA,YACV,eAAa,WAAW,MAAM,KAAK,SAAS;AAAA,YAC3C,GAAI,WAAW,MAAM,KAAK,EAAE,gBAAgB,WAAoB,IAAI,CAAC;AAAA,YAErE,gBAAM;AAAA;AAAA,QACT;AAAA;AAAA,MAXK,MAAM;AAAA,IAYb,CACD,GACH;AAAA,KACF;AAEJ;AAUO,SAAS,gBACd,WACA,WAAW,cACC;AACZ,MAAI,CAAC,UAAW,QAAO,CAAC;AAExB,SAAO,MAAM,KAAK,UAAU,iBAAqC,QAAQ,CAAC,EACvE,OAAO,CAAC,OAAO,GAAG,EAAE,EACpB,IAAI,CAAC,QAAQ;AAAA,IACZ,IAAI,GAAG;AAAA,IACP,OAAO,GAAG,aAAa,QAAQ,qBAAqB,EAAE,EAAE,KAAK,KAAK;AAAA,IAClE,OAAO,OAAO,GAAG,QAAQ,MAAM,CAAC,CAAC;AAAA,EACnC,EAAE;AACN;","names":[]}
@@ -0,0 +1,75 @@
1
+ "use client";
2
+
3
+ // src/components/docs/codeTabsStore.ts
4
+ import { useCallback, useEffect, useState, useSyncExternalStore } from "react";
5
+ var STORAGE_PREFIX = "ds-code-tabs:";
6
+ var selections = /* @__PURE__ */ new Map();
7
+ var listeners = /* @__PURE__ */ new Set();
8
+ function emit() {
9
+ for (const listener of listeners) listener();
10
+ }
11
+ function subscribe(listener) {
12
+ listeners.add(listener);
13
+ return () => {
14
+ listeners.delete(listener);
15
+ };
16
+ }
17
+ function subscribeNothing() {
18
+ return () => {
19
+ };
20
+ }
21
+ function selectInGroup(group, label) {
22
+ if (selections.get(group) === label) return;
23
+ selections.set(group, label);
24
+ try {
25
+ window.localStorage.setItem(STORAGE_PREFIX + group, label);
26
+ } catch {
27
+ }
28
+ emit();
29
+ }
30
+ function restoreGroup(group) {
31
+ if (selections.has(group)) return;
32
+ try {
33
+ const stored = window.localStorage.getItem(STORAGE_PREFIX + group);
34
+ if (stored) {
35
+ selections.set(group, stored);
36
+ emit();
37
+ }
38
+ } catch {
39
+ }
40
+ }
41
+ function resetCodeTabGroups() {
42
+ selections.clear();
43
+ try {
44
+ for (let i = window.localStorage.length - 1; i >= 0; i -= 1) {
45
+ const key = window.localStorage.key(i);
46
+ if (key?.startsWith(STORAGE_PREFIX)) window.localStorage.removeItem(key);
47
+ }
48
+ } catch {
49
+ }
50
+ emit();
51
+ }
52
+ function useTabGroup(group, fallback) {
53
+ const shared = useSyncExternalStore(
54
+ group ? subscribe : subscribeNothing,
55
+ () => group ? selections.get(group) : void 0,
56
+ () => void 0
57
+ );
58
+ const [local, setLocal] = useState(fallback);
59
+ useEffect(() => {
60
+ if (group) restoreGroup(group);
61
+ }, [group]);
62
+ const select = useCallback(
63
+ (label) => {
64
+ if (group) selectInGroup(group, label);
65
+ else setLocal(label);
66
+ },
67
+ [group]
68
+ );
69
+ return [group ? shared ?? fallback : local, select];
70
+ }
71
+ export {
72
+ resetCodeTabGroups,
73
+ useTabGroup
74
+ };
75
+ //# sourceMappingURL=codeTabsStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/docs/codeTabsStore.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useState, useSyncExternalStore } from 'react';\n\n/**\n * Group sync for {@link CodeTabs}.\n *\n * The behaviour every developer documentation portal has and no tab widget\n * does by default: pick `pnpm` on one code block and *every* block on the page\n * in the same group switches with it, because a reader picks a package manager\n * once, not once per snippet.\n *\n * The selection lives in a module-level store rather than a React context so\n * blocks do not have to share a provider — MDX bodies mount them at arbitrary\n * depths and there is no component in a document to hang a provider off. The\n * choice is also persisted, so it survives navigating to the next page.\n */\n\nconst STORAGE_PREFIX = 'ds-code-tabs:';\n\nconst selections = new Map<string, string>();\nconst listeners = new Set<() => void>();\n\nfunction emit() {\n for (const listener of listeners) listener();\n}\n\nfunction subscribe(listener: () => void) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\n/** A group of one: an ungrouped block never notifies and never listens. */\nfunction subscribeNothing() {\n return () => {};\n}\n\nfunction selectInGroup(group: string, label: string) {\n if (selections.get(group) === label) return;\n selections.set(group, label);\n try {\n window.localStorage.setItem(STORAGE_PREFIX + group, label);\n } catch {\n // Private windows and blocked site data both throw here. A reader who\n // cannot persist the choice still gets it for the length of the page.\n }\n emit();\n}\n\nfunction restoreGroup(group: string) {\n if (selections.has(group)) return;\n try {\n const stored = window.localStorage.getItem(STORAGE_PREFIX + group);\n if (stored) {\n selections.set(group, stored);\n emit();\n }\n } catch {\n // As above.\n }\n}\n\n/** Test seam: forget every selection, in memory and in storage. */\nexport function resetCodeTabGroups() {\n selections.clear();\n try {\n for (let i = window.localStorage.length - 1; i >= 0; i -= 1) {\n const key = window.localStorage.key(i);\n if (key?.startsWith(STORAGE_PREFIX)) window.localStorage.removeItem(key);\n }\n } catch {\n // No storage, nothing to forget.\n }\n emit();\n}\n\n/**\n * The selected label for a group, and a setter.\n *\n * `fallback` is the first tab, and it is also the value the server renders:\n * the store starts empty on both sides, so the first client render matches the\n * HTML and the stored choice is applied in an effect afterwards. Restoring\n * during render instead is what produces a hydration mismatch.\n */\nexport function useTabGroup(\n group: string | undefined,\n fallback: string,\n): readonly [string, (label: string) => void] {\n const shared = useSyncExternalStore(\n group ? subscribe : subscribeNothing,\n () => (group ? selections.get(group) : undefined),\n () => undefined,\n );\n const [local, setLocal] = useState(fallback);\n\n useEffect(() => {\n if (group) restoreGroup(group);\n }, [group]);\n\n const select = useCallback(\n (label: string) => {\n if (group) selectInGroup(group, label);\n else setLocal(label);\n },\n [group],\n );\n\n return [group ? (shared ?? fallback) : local, select] as const;\n}\n"],"mappings":";;;AAEA,SAAS,aAAa,WAAW,UAAU,4BAA4B;AAgBvE,IAAM,iBAAiB;AAEvB,IAAM,aAAa,oBAAI,IAAoB;AAC3C,IAAM,YAAY,oBAAI,IAAgB;AAEtC,SAAS,OAAO;AACd,aAAW,YAAY,UAAW,UAAS;AAC7C;AAEA,SAAS,UAAU,UAAsB;AACvC,YAAU,IAAI,QAAQ;AACtB,SAAO,MAAM;AACX,cAAU,OAAO,QAAQ;AAAA,EAC3B;AACF;AAGA,SAAS,mBAAmB;AAC1B,SAAO,MAAM;AAAA,EAAC;AAChB;AAEA,SAAS,cAAc,OAAe,OAAe;AACnD,MAAI,WAAW,IAAI,KAAK,MAAM,MAAO;AACrC,aAAW,IAAI,OAAO,KAAK;AAC3B,MAAI;AACF,WAAO,aAAa,QAAQ,iBAAiB,OAAO,KAAK;AAAA,EAC3D,QAAQ;AAAA,EAGR;AACA,OAAK;AACP;AAEA,SAAS,aAAa,OAAe;AACnC,MAAI,WAAW,IAAI,KAAK,EAAG;AAC3B,MAAI;AACF,UAAM,SAAS,OAAO,aAAa,QAAQ,iBAAiB,KAAK;AACjE,QAAI,QAAQ;AACV,iBAAW,IAAI,OAAO,MAAM;AAC5B,WAAK;AAAA,IACP;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AAGO,SAAS,qBAAqB;AACnC,aAAW,MAAM;AACjB,MAAI;AACF,aAAS,IAAI,OAAO,aAAa,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;AAC3D,YAAM,MAAM,OAAO,aAAa,IAAI,CAAC;AACrC,UAAI,KAAK,WAAW,cAAc,EAAG,QAAO,aAAa,WAAW,GAAG;AAAA,IACzE;AAAA,EACF,QAAQ;AAAA,EAER;AACA,OAAK;AACP;AAUO,SAAS,YACd,OACA,UAC4C;AAC5C,QAAM,SAAS;AAAA,IACb,QAAQ,YAAY;AAAA,IACpB,MAAO,QAAQ,WAAW,IAAI,KAAK,IAAI;AAAA,IACvC,MAAM;AAAA,EACR;AACA,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,QAAQ;AAE3C,YAAU,MAAM;AACd,QAAI,MAAO,cAAa,KAAK;AAAA,EAC/B,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,SAAS;AAAA,IACb,CAAC,UAAkB;AACjB,UAAI,MAAO,eAAc,OAAO,KAAK;AAAA,UAChC,UAAS,KAAK;AAAA,IACrB;AAAA,IACA,CAAC,KAAK;AAAA,EACR;AAEA,SAAO,CAAC,QAAS,UAAU,WAAY,OAAO,MAAM;AACtD;","names":[]}
@@ -0,0 +1,116 @@
1
+ // src/components/docs/figures/ActivityGrid.tsx
2
+ import * as React from "react";
3
+ import { cn } from "../../../lib/recipe.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ var DAY_MS = 864e5;
6
+ var MAX_DAYS = 3660;
7
+ var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
8
+ var DEFAULT_GLYPHS = ["\xB7", "\u2591", "\u2592", "\u2593", "\u2588"];
9
+ function glyphsOf(glyphs) {
10
+ if (Array.isArray(glyphs)) return glyphs.length ? glyphs : DEFAULT_GLYPHS;
11
+ if (glyphs === "ascii") return [".", "-", "=", "#", "@"];
12
+ if (glyphs === "hash") return [".", ":", "+", "#", "\u2588"];
13
+ if (glyphs === "bar") return ["\u2581", "\u2582", "\u2583", "\u2585", "\u2588"];
14
+ return DEFAULT_GLYPHS;
15
+ }
16
+ function intensity(level, max) {
17
+ return level <= 0 || max <= 0 ? 0 : Math.max(1, Math.round(Math.min(1, level / max) * 4));
18
+ }
19
+ function colorClass(level, palette = "mono") {
20
+ if (!level) return "text-content-muted";
21
+ if (palette === "mono") return level <= 2 ? "text-content-secondary" : "text-content-primary";
22
+ return level === 1 ? "text-accent-secondary" : level === 2 && palette === "multi" ? "text-accent-tertiary" : "text-accent-primary";
23
+ }
24
+ function renderGlyph(level, glyphs) {
25
+ return glyphs[Math.round(Math.max(0, Math.min(4, level)) / 4 * (glyphs.length - 1))] ?? glyphs[0] ?? "\xB7";
26
+ }
27
+ function parseDate(value) {
28
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
29
+ if (!match) return null;
30
+ const [, y, m, d] = match;
31
+ const date = /* @__PURE__ */ new Date(0);
32
+ date.setUTCHours(0, 0, 0, 0);
33
+ date.setUTCFullYear(Number(y), Number(m) - 1, Number(d));
34
+ return date.getUTCFullYear() === Number(y) && date.getUTCMonth() === Number(m) - 1 && date.getUTCDate() === Number(d) ? date.getTime() : null;
35
+ }
36
+ function iso(time) {
37
+ return new Date(time).toISOString().slice(0, 10);
38
+ }
39
+ function dailyCounts(data) {
40
+ const counts = /* @__PURE__ */ new Map();
41
+ for (const item of data) {
42
+ if (parseDate(item.date) === null) continue;
43
+ const count = Number.isFinite(item.count) ? Math.max(0, item.count) : 0;
44
+ counts.set(item.date, (counts.get(item.date) ?? 0) + count);
45
+ }
46
+ return Array.from(counts, ([date, count]) => ({ date, count }));
47
+ }
48
+ function makeWeeks(data, weekStartsOn) {
49
+ const valid = data.map((item) => ({ item, time: parseDate(item.date) })).filter((entry) => entry.time !== null);
50
+ if (!valid.length) return [];
51
+ const min = valid.reduce((earliest, entry) => Math.min(earliest, entry.time), Infinity);
52
+ const max = valid.reduce((latest, entry) => Math.max(latest, entry.time), -Infinity);
53
+ const span = Math.floor((max - min) / DAY_MS) + 1;
54
+ if (span > MAX_DAYS) return [];
55
+ const first = min - (new Date(min).getUTCDay() - weekStartsOn + 7) % 7 * DAY_MS;
56
+ const last = max + (weekStartsOn + 6 - new Date(max).getUTCDay() + 7) % 7 * DAY_MS;
57
+ const counts = new Map(valid.map(({ item }) => [item.date, Math.max(0, Number.isFinite(item.count) ? item.count : 0)]));
58
+ const weeks = [];
59
+ for (let time = first; time <= last; time += 7 * DAY_MS) {
60
+ weeks.push(Array.from({ length: 7 }, (_, offset) => {
61
+ const dateTime = time + offset * DAY_MS;
62
+ const date = iso(dateTime);
63
+ const inRange = dateTime >= min && dateTime <= max;
64
+ return { date, count: inRange ? counts.get(date) ?? 0 : 0, inRange };
65
+ }));
66
+ }
67
+ return weeks;
68
+ }
69
+ function Legend({ glyphs, palette }) {
70
+ return /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-content-muted", children: [
71
+ /* @__PURE__ */ jsx("span", { children: "Less" }),
72
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "flex select-none", children: glyphs.map((glyph, index) => /* @__PURE__ */ jsx("span", { className: cn("w-[1ch] text-center", colorClass(Math.round(index / Math.max(glyphs.length - 1, 1) * 4), palette)), children: glyph }, `${glyph}-${index}`)) }),
73
+ /* @__PURE__ */ jsx("span", { children: "More" })
74
+ ] });
75
+ }
76
+ var ActivityGrid = React.forwardRef(function ActivityGrid2({ data, weekStartsOn = 0, max, legend = true, glyphs, palette, className, ...rest }, ref) {
77
+ const valid = dailyCounts(data);
78
+ const weeks = makeWeeks(valid, weekStartsOn);
79
+ const total = valid.reduce((sum, item) => sum + item.count, 0);
80
+ const summary = `${total.toLocaleString("en-US")} contributions across ${valid.length} dates`;
81
+ const glyphSet = glyphsOf(glyphs);
82
+ const peak = max == null ? valid.reduce((largest, item) => Math.max(largest, item.count), 0) : Math.max(0, max);
83
+ const weekdayLabels = weekStartsOn === 1 ? ["Mon", "", "Wed", "", "Fri", "", ""] : ["", "Tue", "", "Thu", "", "Sat", ""];
84
+ if (!valid.length || !weeks.length) return /* @__PURE__ */ jsxs("section", { "data-slot": "activity-grid", ref, "aria-label": "Activity grid", ...rest, className: cn("font-mono text-sm text-content-primary", className), children: [
85
+ /* @__PURE__ */ jsx("p", { className: "text-content-muted", children: valid.length ? "Date range is too large to display." : "No activity dates to display." }),
86
+ /* @__PURE__ */ jsx("p", { className: "sr-only", children: summary })
87
+ ] });
88
+ return /* @__PURE__ */ jsxs("section", { "data-slot": "activity-grid", ref, "aria-label": "Activity grid", ...rest, className: cn("flex w-fit max-w-full min-w-0 flex-col gap-3 font-mono text-sm text-content-primary", className), children: [
89
+ /* @__PURE__ */ jsxs("p", { className: "sr-only", children: [
90
+ summary,
91
+ ", from ",
92
+ valid.reduce((a, b) => a.date < b.date ? a : b).date,
93
+ " to ",
94
+ valid.reduce((a, b) => a.date > b.date ? a : b).date,
95
+ "."
96
+ ] }),
97
+ /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", role: "img", "aria-label": summary, children: /* @__PURE__ */ jsxs("div", { className: "min-w-max", children: [
98
+ /* @__PURE__ */ jsx("div", { className: "flex pl-[4ch]", "aria-hidden": "true", children: weeks.map((week, i) => {
99
+ const firstOfMonth = week.find((cell) => cell.inRange && cell.date.slice(-2) === "01");
100
+ return /* @__PURE__ */ jsx("span", { className: "w-[2ch] shrink-0 text-content-muted", children: firstOfMonth ? MONTHS[Number(firstOfMonth.date.slice(5, 7)) - 1] : "" }, i);
101
+ }) }),
102
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
103
+ /* @__PURE__ */ jsx("div", { className: "flex w-[3ch] shrink-0 flex-col", "aria-hidden": "true", children: weekdayLabels.map((label, i) => /* @__PURE__ */ jsx("span", { className: "h-[1.2em] text-[0.7em] text-content-muted", children: label }, i)) }),
104
+ /* @__PURE__ */ jsx("div", { className: "flex gap-1", children: weeks.map((week) => /* @__PURE__ */ jsx("div", { className: "flex flex-col", children: week.map((cell) => {
105
+ const level = cell.inRange ? intensity(cell.count, peak) : 0;
106
+ return /* @__PURE__ */ jsx("span", { "aria-hidden": "true", title: cell.inRange ? `${cell.date}: ${cell.count} contributions` : void 0, className: cn("flex h-[1.2em] w-[2ch] items-center justify-center leading-none", cell.inRange ? colorClass(level, palette) : "text-transparent"), children: cell.inRange ? renderGlyph(level, glyphSet) : glyphSet[0] ?? "\xB7" }, cell.date);
107
+ }) }, week[0].date)) })
108
+ ] })
109
+ ] }) }),
110
+ legend ? /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(Legend, { glyphs: glyphSet, palette }) }) : null
111
+ ] });
112
+ });
113
+ export {
114
+ ActivityGrid
115
+ };
116
+ //# sourceMappingURL=ActivityGrid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/docs/figures/ActivityGrid.tsx"],"sourcesContent":["/**\n * Adapted from mdxcn by Keshav Bagaade, MIT licensed.\n * Source: https://github.com/keshav-exe/mdxcn/blob/2928126ba146ebbae9c2351deca27d27bb40e744/registry/default/graph-activity/graph-activity.tsx\n * The local port removes motion and graph variables in favour of deterministic\n * rendering and the design system's semantic token classes.\n */\n\nimport * as React from 'react';\nimport { cn } from '../../../lib/recipe';\n\nconst DAY_MS = 86_400_000;\nconst MAX_DAYS = 3660;\nconst MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] as const;\nconst DEFAULT_GLYPHS = ['·', '░', '▒', '▓', '█'];\ntype Glyphs = 'shade' | 'ascii' | 'hash' | 'bar' | readonly string[];\ntype FigurePalette = 'mono' | 'duo' | 'multi';\n\nfunction glyphsOf(glyphs?: Glyphs): readonly string[] {\n if (Array.isArray(glyphs)) return glyphs.length ? glyphs : DEFAULT_GLYPHS;\n if (glyphs === 'ascii') return ['.', '-', '=', '#', '@'];\n if (glyphs === 'hash') return ['.', ':', '+', '#', '█'];\n if (glyphs === 'bar') return ['▁', '▂', '▃', '▅', '█'];\n return DEFAULT_GLYPHS;\n}\nfunction intensity(level: number, max: number): number { return level <= 0 || max <= 0 ? 0 : Math.max(1, Math.round(Math.min(1, level / max) * 4)); }\nfunction colorClass(level: number, palette: FigurePalette = 'mono'): string {\n if (!level) return 'text-content-muted';\n if (palette === 'mono') return level <= 2 ? 'text-content-secondary' : 'text-content-primary';\n return level === 1 ? 'text-accent-secondary' : level === 2 && palette === 'multi' ? 'text-accent-tertiary' : 'text-accent-primary';\n}\nfunction renderGlyph(level: number, glyphs: readonly string[]): string { return glyphs[Math.round(Math.max(0, Math.min(4, level)) / 4 * (glyphs.length - 1))] ?? glyphs[0] ?? '·'; }\n\nexport interface ActivityDay {\n /** Calendar date in ISO `YYYY-MM-DD` form, interpreted in UTC. */\n date: string;\n /** Non-negative activity count for the date. */\n count: number;\n}\n\nexport interface ActivityGridProps extends React.HTMLAttributes<HTMLElement> {\n /** Dated counts used to derive the contribution grid. */\n data: ActivityDay[];\n /** First weekday in each column: Sunday (0) or Monday (1). */\n weekStartsOn?: 0 | 1;\n /** Optional upper bound for intensity scaling. */\n max?: number;\n /** Render the less/more glyph legend. */\n legend?: boolean;\n /** Glyph set name or custom glyph sequence. */\n glyphs?: Glyphs;\n /** Accent palette used for intensity levels. */\n palette?: FigurePalette;\n /** Additional classes for the renderer root. */\n className?: string;\n}\n\ninterface Cell { date: string; count: number; inRange: boolean }\n\nfunction parseDate(value: string): number | null {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(value);\n if (!match) return null;\n const [, y, m, d] = match;\n const date = new Date(0);\n date.setUTCHours(0, 0, 0, 0);\n date.setUTCFullYear(Number(y), Number(m) - 1, Number(d));\n return date.getUTCFullYear() === Number(y) && date.getUTCMonth() === Number(m) - 1 && date.getUTCDate() === Number(d) ? date.getTime() : null;\n}\n\nfunction iso(time: number): string { return new Date(time).toISOString().slice(0, 10); }\n\nfunction dailyCounts(data: ActivityDay[]): ActivityDay[] {\n const counts = new Map<string, number>();\n for (const item of data) {\n if (parseDate(item.date) === null) continue;\n const count = Number.isFinite(item.count) ? Math.max(0, item.count) : 0;\n counts.set(item.date, (counts.get(item.date) ?? 0) + count);\n }\n return Array.from(counts, ([date, count]) => ({ date, count }));\n}\n\nfunction makeWeeks(data: ActivityDay[], weekStartsOn: 0 | 1): Cell[][] {\n const valid = data.map((item) => ({ item, time: parseDate(item.date) })).filter((entry): entry is { item: ActivityDay; time: number } => entry.time !== null);\n if (!valid.length) return [];\n const min = valid.reduce((earliest, entry) => Math.min(earliest, entry.time), Infinity);\n const max = valid.reduce((latest, entry) => Math.max(latest, entry.time), -Infinity);\n const span = Math.floor((max - min) / DAY_MS) + 1;\n if (span > MAX_DAYS) return [];\n const first = min - ((new Date(min).getUTCDay() - weekStartsOn + 7) % 7) * DAY_MS;\n const last = max + ((weekStartsOn + 6 - new Date(max).getUTCDay() + 7) % 7) * DAY_MS;\n const counts = new Map(valid.map(({ item }) => [item.date, Math.max(0, Number.isFinite(item.count) ? item.count : 0)]));\n const weeks: Cell[][] = [];\n for (let time = first; time <= last; time += 7 * DAY_MS) {\n weeks.push(Array.from({ length: 7 }, (_, offset) => {\n const dateTime = time + offset * DAY_MS;\n const date = iso(dateTime);\n const inRange = dateTime >= min && dateTime <= max;\n return { date, count: inRange ? counts.get(date) ?? 0 : 0, inRange };\n }));\n }\n return weeks;\n}\n\nfunction Legend({ glyphs, palette }: { glyphs: readonly string[]; palette?: FigurePalette }) {\n return <p className=\"flex items-center gap-2 text-content-muted\"><span>Less</span><span aria-hidden=\"true\" className=\"flex select-none\">{glyphs.map((glyph, index) => <span className={cn('w-[1ch] text-center', colorClass(Math.round(index / Math.max(glyphs.length - 1, 1) * 4), palette))} key={`${glyph}-${index}`}>{glyph}</span>)}</span><span>More</span></p>;\n}\n\n/** Renders dated counts as a bounded, UTC-stable contribution grid. */\nexport const ActivityGrid = React.forwardRef<HTMLElement, ActivityGridProps>(function ActivityGrid({ data, weekStartsOn = 0, max, legend = true, glyphs, palette, className, ...rest }, ref) {\n const valid = dailyCounts(data);\n const weeks = makeWeeks(valid, weekStartsOn);\n const total = valid.reduce((sum, item) => sum + item.count, 0);\n const summary = `${total.toLocaleString('en-US')} contributions across ${valid.length} dates`;\n const glyphSet = glyphsOf(glyphs);\n const peak = max == null ? valid.reduce((largest, item) => Math.max(largest, item.count), 0) : Math.max(0, max);\n const weekdayLabels = weekStartsOn === 1 ? ['Mon', '', 'Wed', '', 'Fri', '', ''] : ['', 'Tue', '', 'Thu', '', 'Sat', ''];\n\n if (!valid.length || !weeks.length) return <section data-slot=\"activity-grid\" ref={ref} aria-label=\"Activity grid\" {...rest} className={cn('font-mono text-sm text-content-primary', className)}><p className=\"text-content-muted\">{valid.length ? 'Date range is too large to display.' : 'No activity dates to display.'}</p><p className=\"sr-only\">{summary}</p></section>;\n\n return <section data-slot=\"activity-grid\" ref={ref} aria-label=\"Activity grid\" {...rest} className={cn('flex w-fit max-w-full min-w-0 flex-col gap-3 font-mono text-sm text-content-primary', className)}>\n <p className=\"sr-only\">{summary}, from {valid.reduce((a, b) => a.date < b.date ? a : b).date} to {valid.reduce((a, b) => a.date > b.date ? a : b).date}.</p>\n <div className=\"overflow-x-auto\" role=\"img\" aria-label={summary}>\n <div className=\"min-w-max\">\n <div className=\"flex pl-[4ch]\" aria-hidden=\"true\">{weeks.map((week, i) => { const firstOfMonth = week.find((cell) => cell.inRange && cell.date.slice(-2) === '01'); return <span className=\"w-[2ch] shrink-0 text-content-muted\" key={i}>{firstOfMonth ? MONTHS[Number(firstOfMonth.date.slice(5, 7)) - 1] : ''}</span>; })}</div>\n <div className=\"flex gap-1\"><div className=\"flex w-[3ch] shrink-0 flex-col\" aria-hidden=\"true\">{weekdayLabels.map((label, i) => <span className=\"h-[1.2em] text-[0.7em] text-content-muted\" key={i}>{label}</span>)}</div><div className=\"flex gap-1\">{weeks.map((week) => <div className=\"flex flex-col\" key={week[0].date}>{week.map((cell) => { const level = cell.inRange ? intensity(cell.count, peak) : 0; return <span aria-hidden=\"true\" title={cell.inRange ? `${cell.date}: ${cell.count} contributions` : undefined} className={cn('flex h-[1.2em] w-[2ch] items-center justify-center leading-none', cell.inRange ? colorClass(level, palette) : 'text-transparent')} key={cell.date}>{cell.inRange ? renderGlyph(level, glyphSet) : (glyphSet[0] ?? '·')}</span>; })}</div>)}</div></div>\n </div>\n </div>\n {legend ? <div className=\"flex justify-end\"><Legend glyphs={glyphSet} palette={palette} /></div> : null}\n </section>;\n});\n"],"mappings":"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","names":["ActivityGrid"]}
@@ -0,0 +1,64 @@
1
+ // src/components/docs/figures/BeforeAfter.tsx
2
+ import * as React from "react";
3
+ import {
4
+ childItems,
5
+ defineAsciiItem,
6
+ AsciiFrameBody,
7
+ itemText,
8
+ listItems,
9
+ numberOf,
10
+ splitLabel,
11
+ textOf
12
+ } from "./FigureFrame.js";
13
+ import { cn } from "../../../lib/recipe.js";
14
+ import { jsx, jsxs } from "react/jsx-runtime";
15
+ var Slope = defineAsciiItem("Slope");
16
+ function format(value) {
17
+ return value.toLocaleString("en-US", { maximumFractionDigits: Number.isInteger(value) ? 0 : 1 });
18
+ }
19
+ function displayOf(value) {
20
+ return typeof value === "number" ? format(value) : value.trim();
21
+ }
22
+ function itemsOf(children) {
23
+ const listed = listItems(children).map((item) => {
24
+ const { label, rest } = splitLabel(itemText(item));
25
+ const [from = "", to = ""] = rest.split(/\s*(?:→|->|—>|=>)\s*/);
26
+ return { label, from, to };
27
+ });
28
+ if (listed.length > 0) return listed;
29
+ return childItems(children, Slope).map((entry) => ({ ...entry, label: entry.label ?? textOf(entry.children) }));
30
+ }
31
+ var BeforeAfter = React.forwardRef(function BeforeAfter2({ label = "Before and after", fromLabel, toLabel, items: itemsProp, children, palette, className, ...rest }, ref) {
32
+ const items = (itemsProp ?? itemsOf(children)).map((entry) => ({
33
+ label: entry.label ?? "",
34
+ from: numberOf(entry.from),
35
+ to: numberOf(entry.to),
36
+ fromDisplay: displayOf(entry.from),
37
+ toDisplay: displayOf(entry.to)
38
+ }));
39
+ return /* @__PURE__ */ jsxs(AsciiFrameBody, { "data-slot": "before-after", ref, ...rest, className: cn("flex flex-col gap-3", className), children: [
40
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_6.5rem_2rem_6.5rem] items-end gap-x-3", children: [
41
+ /* @__PURE__ */ jsx("span", {}),
42
+ /* @__PURE__ */ jsx("span", { className: "text-right text-content-muted", children: fromLabel }),
43
+ /* @__PURE__ */ jsx("span", {}),
44
+ /* @__PURE__ */ jsx("span", { className: "text-right text-content-muted", children: toLabel })
45
+ ] }),
46
+ /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-2", "aria-label": label, children: items.map((row, index) => {
47
+ const up = row.to > row.from;
48
+ const down = row.to < row.from;
49
+ const tone = up ? "text-accent-primary" : down ? palette === "multi" ? "text-accent-secondary" : "text-content-secondary" : "text-content-primary";
50
+ return /* @__PURE__ */ jsxs("li", { className: "grid grid-cols-[minmax(0,1fr)_6.5rem_2rem_6.5rem] items-baseline gap-x-3", "aria-label": `${row.label} from ${row.fromDisplay} to ${row.toDisplay}`, children: [
51
+ /* @__PURE__ */ jsx("span", { className: "truncate text-content-primary", children: row.label }),
52
+ /* @__PURE__ */ jsx("span", { className: "text-right tabular-nums text-content-muted", children: row.fromDisplay }),
53
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("select-none text-center", tone), children: up || down ? "\u2192" : "\u2013" }),
54
+ /* @__PURE__ */ jsx("span", { className: cn("text-right tabular-nums", tone), children: row.toDisplay })
55
+ ] }, `${row.label}-${index}`);
56
+ }) }),
57
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: items.length === 0 ? "Empty comparison." : `${items.length} before and after comparison${items.length === 1 ? "" : "s"}.` })
58
+ ] });
59
+ });
60
+ export {
61
+ BeforeAfter,
62
+ Slope
63
+ };
64
+ //# sourceMappingURL=BeforeAfter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/docs/figures/BeforeAfter.tsx"],"sourcesContent":["/**\n * Adapted from mdxcn by Keshav Bagaade, MIT licensed.\n * Source: https://github.com/keshav-exe/mdxcn/blob/2928126ba146ebbae9c2351deca27d27bb40e744/registry/default/graph-slope/graph-slope.tsx\n * The local port removes motion and graph variables while preserving the\n * before/after data API and Markdown-friendly child syntax.\n */\n\nimport type { ReactNode } from 'react';\nimport * as React from 'react';\nimport {\n childItems,\n defineAsciiItem,\n AsciiFrameBody,\n itemText,\n listItems,\n numberOf,\n splitLabel,\n textOf,\n type FigurePalette,\n} from './FigureFrame';\nimport { cn } from '../../../lib/recipe';\n\nexport interface SlopeItem {\n /** Falls back to the child text for `<Slope from={160} to={142}>read</Slope>`. */\n label?: string;\n from: number | string;\n to: number | string;\n}\n\nexport interface BeforeAfterProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Accessible name; the enclosing FigureFrame owns the caption. */\n label?: string;\n /** Header for the before-value column. */\n fromLabel: string;\n /** Header for the after-value column. */\n toLabel: string;\n /** Data form. If omitted, the component reads Markdown or `<Slope>` children. */\n items?: SlopeItem[];\n /** Markdown list or `<Slope>` content used to derive items. */\n children?: ReactNode;\n /** Accent palette used for directional change. */\n palette?: FigurePalette;\n /** Additional classes for this comparison. */\n className?: string;\n}\n\n/** MDX-only slope item, for example `<Slope from={8200} to={12400}>docs</Slope>`. */\nexport const Slope = defineAsciiItem<SlopeItem>('Slope');\n\ntype SlopeRow = { label: string; from: number; to: number; fromDisplay: string; toDisplay: string };\n\nfunction format(value: number): string {\n return value.toLocaleString('en-US', { maximumFractionDigits: Number.isInteger(value) ? 0 : 1 });\n}\n\nfunction displayOf(value: number | string): string {\n return typeof value === 'number' ? format(value) : value.trim();\n}\n\nfunction itemsOf(children: ReactNode): SlopeItem[] {\n const listed = listItems(children).map((item) => {\n const { label, rest } = splitLabel(itemText(item));\n const [from = '', to = ''] = rest.split(/\\s*(?:→|->|—>|=>)\\s*/);\n return { label, from, to };\n });\n if (listed.length > 0) return listed;\n return childItems(children, Slope).map((entry) => ({ ...entry, label: entry.label ?? textOf(entry.children) }));\n}\n\n/** Renders paired values with directional change and an accessible row label. */\nexport const BeforeAfter = React.forwardRef<HTMLDivElement, BeforeAfterProps>(function BeforeAfter({ label = 'Before and after', fromLabel, toLabel, items: itemsProp, children, palette, className, ...rest }, ref) {\n const items: SlopeRow[] = (itemsProp ?? itemsOf(children)).map((entry) => ({\n label: entry.label ?? '',\n from: numberOf(entry.from),\n to: numberOf(entry.to),\n fromDisplay: displayOf(entry.from),\n toDisplay: displayOf(entry.to),\n }));\n\n return (\n <AsciiFrameBody data-slot=\"before-after\" ref={ref} {...rest} className={cn('flex flex-col gap-3', className)}>\n <div className=\"grid grid-cols-[minmax(0,1fr)_6.5rem_2rem_6.5rem] items-end gap-x-3\">\n <span />\n <span className=\"text-right text-content-muted\">{fromLabel}</span>\n <span />\n <span className=\"text-right text-content-muted\">{toLabel}</span>\n </div>\n <ul className=\"flex flex-col gap-2\" aria-label={label}>\n {items.map((row, index) => {\n const up = row.to > row.from;\n const down = row.to < row.from;\n const tone = up ? 'text-accent-primary' : down ? palette === 'multi' ? 'text-accent-secondary' : 'text-content-secondary' : 'text-content-primary';\n return (\n <li className=\"grid grid-cols-[minmax(0,1fr)_6.5rem_2rem_6.5rem] items-baseline gap-x-3\" key={`${row.label}-${index}`} aria-label={`${row.label} from ${row.fromDisplay} to ${row.toDisplay}`}>\n <span className=\"truncate text-content-primary\">{row.label}</span>\n <span className=\"text-right tabular-nums text-content-muted\">{row.fromDisplay}</span>\n <span aria-hidden=\"true\" className={cn('select-none text-center', tone)}>{up || down ? '→' : '–'}</span>\n <span className={cn('text-right tabular-nums', tone)}>{row.toDisplay}</span>\n </li>\n );\n })}\n </ul>\n <span className=\"sr-only\">{items.length === 0 ? 'Empty comparison.' : `${items.length} before and after comparison${items.length === 1 ? '' : 's'}.`}</span>\n </AsciiFrameBody>\n );\n});\n"],"mappings":";AAQA,YAAY,WAAW;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,UAAU;AA6DX,SACE,KADF;AAlCD,IAAM,QAAQ,gBAA2B,OAAO;AAIvD,SAAS,OAAO,OAAuB;AACrC,SAAO,MAAM,eAAe,SAAS,EAAE,uBAAuB,OAAO,UAAU,KAAK,IAAI,IAAI,EAAE,CAAC;AACjG;AAEA,SAAS,UAAU,OAAgC;AACjD,SAAO,OAAO,UAAU,WAAW,OAAO,KAAK,IAAI,MAAM,KAAK;AAChE;AAEA,SAAS,QAAQ,UAAkC;AACjD,QAAM,SAAS,UAAU,QAAQ,EAAE,IAAI,CAAC,SAAS;AAC/C,UAAM,EAAE,OAAO,KAAK,IAAI,WAAW,SAAS,IAAI,CAAC;AACjD,UAAM,CAAC,OAAO,IAAI,KAAK,EAAE,IAAI,KAAK,MAAM,sBAAsB;AAC9D,WAAO,EAAE,OAAO,MAAM,GAAG;AAAA,EAC3B,CAAC;AACD,MAAI,OAAO,SAAS,EAAG,QAAO;AAC9B,SAAO,WAAW,UAAU,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,GAAG,OAAO,OAAO,MAAM,SAAS,OAAO,MAAM,QAAQ,EAAE,EAAE;AAChH;AAGO,IAAM,cAAoB,iBAA6C,SAASA,aAAY,EAAE,QAAQ,oBAAoB,WAAW,SAAS,OAAO,WAAW,UAAU,SAAS,WAAW,GAAG,KAAK,GAAG,KAAK;AACnN,QAAM,SAAqB,aAAa,QAAQ,QAAQ,GAAG,IAAI,CAAC,WAAW;AAAA,IACzE,OAAO,MAAM,SAAS;AAAA,IACtB,MAAM,SAAS,MAAM,IAAI;AAAA,IACzB,IAAI,SAAS,MAAM,EAAE;AAAA,IACrB,aAAa,UAAU,MAAM,IAAI;AAAA,IACjC,WAAW,UAAU,MAAM,EAAE;AAAA,EAC/B,EAAE;AAEF,SACI,qBAAC,kBAAe,aAAU,gBAAe,KAAW,GAAG,MAAM,WAAW,GAAG,uBAAuB,SAAS,GACzG;AAAA,yBAAC,SAAI,WAAU,uEACb;AAAA,0BAAC,UAAK;AAAA,MACN,oBAAC,UAAK,WAAU,iCAAiC,qBAAU;AAAA,MAC3D,oBAAC,UAAK;AAAA,MACN,oBAAC,UAAK,WAAU,iCAAiC,mBAAQ;AAAA,OAC3D;AAAA,IACA,oBAAC,QAAG,WAAU,uBAAsB,cAAY,OAC7C,gBAAM,IAAI,CAAC,KAAK,UAAU;AACzB,YAAM,KAAK,IAAI,KAAK,IAAI;AACxB,YAAM,OAAO,IAAI,KAAK,IAAI;AAC1B,YAAM,OAAO,KAAK,wBAAwB,OAAO,YAAY,UAAU,0BAA0B,2BAA2B;AAC5H,aACE,qBAAC,QAAG,WAAU,4EAAyG,cAAY,GAAG,IAAI,KAAK,SAAS,IAAI,WAAW,OAAO,IAAI,SAAS,IACzL;AAAA,4BAAC,UAAK,WAAU,iCAAiC,cAAI,OAAM;AAAA,QAC3D,oBAAC,UAAK,WAAU,8CAA8C,cAAI,aAAY;AAAA,QAC9E,oBAAC,UAAK,eAAY,QAAO,WAAW,GAAG,2BAA2B,IAAI,GAAI,gBAAM,OAAO,WAAM,UAAI;AAAA,QACjG,oBAAC,UAAK,WAAW,GAAG,2BAA2B,IAAI,GAAI,cAAI,WAAU;AAAA,WAJuB,GAAG,IAAI,KAAK,IAAI,KAAK,EAKnH;AAAA,IAEJ,CAAC,GACH;AAAA,IACA,oBAAC,UAAK,WAAU,WAAW,gBAAM,WAAW,IAAI,sBAAsB,GAAG,MAAM,MAAM,+BAA+B,MAAM,WAAW,IAAI,KAAK,GAAG,KAAI;AAAA,KACvJ;AAEN,CAAC;","names":["BeforeAfter"]}
@@ -0,0 +1,74 @@
1
+ // src/components/docs/figures/ChangeSummary.tsx
2
+ import * as React from "react";
3
+ import {
4
+ childItems,
5
+ defineAsciiItem,
6
+ AsciiFrameBody,
7
+ AsciiFrameRule,
8
+ hasHost,
9
+ itemText,
10
+ listItems,
11
+ splitLabel,
12
+ textOf
13
+ } from "./FigureFrame.js";
14
+ import { cn } from "../../../lib/recipe.js";
15
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
16
+ var Line = defineAsciiItem("Line");
17
+ function signOf(value) {
18
+ if (/^\+/.test(value.trim())) return "add";
19
+ if (/^[−-]/.test(value.trim())) return "remove";
20
+ return void 0;
21
+ }
22
+ function diffFromList(children) {
23
+ return listItems(children).map((item) => {
24
+ const { label, rest } = splitLabel(itemText(item));
25
+ const content = item.props.children;
26
+ return {
27
+ label,
28
+ value: rest.replace(/^[+−-]\s*/, "") || rest,
29
+ sign: signOf(rest),
30
+ total: hasHost(content, ["strong", "b"])
31
+ };
32
+ });
33
+ }
34
+ function signClass(palette, sign) {
35
+ if (sign === "add") return "text-accent-primary";
36
+ if (sign === "remove") return palette === "multi" ? "text-accent-secondary" : "text-content-secondary";
37
+ return "text-content-primary";
38
+ }
39
+ function lineText(row) {
40
+ return `${row.label ?? ""} ${row.value}`.trim();
41
+ }
42
+ var ChangeSummary = React.forwardRef(function ChangeSummary2({ label = "Change summary", rows: rowsProp, footer: footerProp, children, palette, className, ...rest }, ref) {
43
+ const listed = diffFromList(children);
44
+ const tagged = childItems(children, Line).map((entry) => ({ ...entry, label: entry.label ?? textOf(entry.children) }));
45
+ const lines = listed.length > 0 ? listed : tagged;
46
+ const rows = (rowsProp ?? lines.filter((entry) => !entry.total)).map((entry) => ({ ...entry, label: entry.label ?? "" }));
47
+ const footerLine = footerProp ?? lines.find((entry) => entry.total);
48
+ const footer = footerLine ? { ...footerLine, label: footerLine.label ?? "" } : void 0;
49
+ return /* @__PURE__ */ jsxs(AsciiFrameBody, { "data-slot": "change-summary", ref, ...rest, className: cn("flex flex-col gap-3", className), children: [
50
+ /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-2", "aria-label": label, children: rows.map((row, index) => {
51
+ const sign = row.sign ?? "keep";
52
+ const tone = signClass(palette, sign);
53
+ return /* @__PURE__ */ jsxs("li", { className: "grid grid-cols-[1.25rem_minmax(0,1fr)_8ch] items-baseline gap-x-3", "aria-label": `${sign}: ${lineText(row)}`, children: [
54
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("select-none text-center", sign === "keep" ? "text-content-muted" : tone), children: sign === "keep" ? " " : sign === "add" ? "+" : "-" }),
55
+ /* @__PURE__ */ jsx("span", { className: tone, children: row.label }),
56
+ /* @__PURE__ */ jsx("span", { className: cn("text-right tabular-nums", tone), children: row.value })
57
+ ] }, `${lineText(row)}-${index}`);
58
+ }) }),
59
+ footer ? /* @__PURE__ */ jsxs(Fragment, { children: [
60
+ /* @__PURE__ */ jsx(AsciiFrameRule, {}),
61
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[1.25rem_minmax(0,1fr)_8ch] items-baseline gap-x-3", "aria-label": `total: ${lineText(footer)}`, children: [
62
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "text-content-muted", children: " " }),
63
+ /* @__PURE__ */ jsx("span", { className: "text-content-primary", children: footer.label }),
64
+ /* @__PURE__ */ jsx("span", { className: "text-right tabular-nums text-content-primary", children: footer.value })
65
+ ] })
66
+ ] }) : null,
67
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: rows.length === 0 && !footer ? "Empty diff." : `${rows.length} changed row${rows.length === 1 ? "" : "s"}${footer ? `, total ${lineText(footer)}` : ""}.` })
68
+ ] });
69
+ });
70
+ export {
71
+ ChangeSummary,
72
+ Line
73
+ };
74
+ //# sourceMappingURL=ChangeSummary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/docs/figures/ChangeSummary.tsx"],"sourcesContent":["/**\n * Adapted from mdxcn by Keshav Bagaade, MIT licensed.\n * Source: https://github.com/keshav-exe/mdxcn/blob/2928126ba146ebbae9c2351deca27d27bb40e744/registry/default/graph-diff/graph-diff.tsx\n * The local port removes motion and graph variables while retaining the\n * source component's Markdown and `<Line>` data forms.\n */\n\nimport type { ReactNode } from 'react';\nimport * as React from 'react';\nimport {\n childItems,\n defineAsciiItem,\n AsciiFrameBody,\n AsciiFrameRule,\n hasHost,\n itemText,\n listItems,\n splitLabel,\n textOf,\n type FigurePalette,\n} from './FigureFrame';\nimport { cn } from '../../../lib/recipe';\n\nexport type DiffSign = 'add' | 'remove' | 'keep';\n\nexport interface DiffRow {\n /** Falls back to the child text for `<Line value=\"31 kb\">app</Line>`. */\n label?: string;\n value: string;\n sign?: DiffSign;\n}\n\nexport interface DiffLineProps extends DiffRow {\n /** Draw the row under a rule as the total. */\n total?: boolean;\n}\n\nexport interface ChangeSummaryProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Accessible name; the enclosing FigureFrame owns the caption. */\n label?: string;\n /** Data form. If omitted, the component reads Markdown or `<Line>` children. */\n rows?: DiffRow[];\n /** Optional typed total shown below the row rule. */\n footer?: DiffRow;\n /** Markdown list or `<Line>` content used to derive rows. */\n children?: ReactNode;\n /** Accent palette used for additions and removals. */\n palette?: FigurePalette;\n /** Additional classes for this summary. */\n className?: string;\n}\n\n/** MDX-only diff item, for example `<Line sign=\"add\" value=\"31 kb\">app</Line>`. */\nexport const Line = defineAsciiItem<DiffLineProps>('Line');\n\nfunction signOf(value: string): DiffSign | undefined {\n if (/^\\+/.test(value.trim())) return 'add';\n if (/^[−-]/.test(value.trim())) return 'remove';\n return undefined;\n}\n\nfunction diffFromList(children: ReactNode): DiffLineProps[] {\n return listItems(children).map((item) => {\n const { label, rest } = splitLabel(itemText(item));\n const content = (item.props as { children?: ReactNode }).children;\n return {\n label,\n value: rest.replace(/^[+−-]\\s*/, '') || rest,\n sign: signOf(rest),\n total: hasHost(content, ['strong', 'b']),\n };\n });\n}\n\nfunction signClass(palette: FigurePalette | undefined, sign: DiffSign): string {\n if (sign === 'add') return 'text-accent-primary';\n if (sign === 'remove') return palette === 'multi' ? 'text-accent-secondary' : 'text-content-secondary';\n return 'text-content-primary';\n}\n\nfunction lineText(row: DiffRow): string {\n return `${row.label ?? ''} ${row.value}`.trim();\n}\n\n/** Renders additions, removals, and unchanged rows with a readable text summary. */\nexport const ChangeSummary = React.forwardRef<HTMLDivElement, ChangeSummaryProps>(function ChangeSummary({ label = 'Change summary', rows: rowsProp, footer: footerProp, children, palette, className, ...rest }, ref) {\n const listed = diffFromList(children);\n const tagged = childItems(children, Line).map((entry) => ({ ...entry, label: entry.label ?? textOf(entry.children) }));\n const lines = listed.length > 0 ? listed : tagged;\n const rows = (rowsProp ?? lines.filter((entry) => !entry.total)).map((entry) => ({ ...entry, label: entry.label ?? '' }));\n const footerLine = footerProp ?? lines.find((entry) => entry.total);\n const footer = footerLine ? { ...footerLine, label: footerLine.label ?? '' } : undefined;\n\n return (\n <AsciiFrameBody data-slot=\"change-summary\" ref={ref} {...rest} className={cn('flex flex-col gap-3', className)}>\n <ul className=\"flex flex-col gap-2\" aria-label={label}>\n {rows.map((row, index) => {\n const sign = row.sign ?? 'keep';\n const tone = signClass(palette, sign);\n return (\n <li className=\"grid grid-cols-[1.25rem_minmax(0,1fr)_8ch] items-baseline gap-x-3\" key={`${lineText(row)}-${index}`} aria-label={`${sign}: ${lineText(row)}`}>\n <span aria-hidden=\"true\" className={cn('select-none text-center', sign === 'keep' ? 'text-content-muted' : tone)}>{sign === 'keep' ? ' ' : sign === 'add' ? '+' : '-'}</span>\n <span className={tone}>{row.label}</span>\n <span className={cn('text-right tabular-nums', tone)}>{row.value}</span>\n </li>\n );\n })}\n </ul>\n {footer ? <><AsciiFrameRule /><div className=\"grid grid-cols-[1.25rem_minmax(0,1fr)_8ch] items-baseline gap-x-3\" aria-label={`total: ${lineText(footer)}`}><span aria-hidden=\"true\" className=\"text-content-muted\"> </span><span className=\"text-content-primary\">{footer.label}</span><span className=\"text-right tabular-nums text-content-primary\">{footer.value}</span></div></> : null}\n <span className=\"sr-only\">{rows.length === 0 && !footer ? 'Empty diff.' : `${rows.length} changed row${rows.length === 1 ? '' : 's'}${footer ? `, total ${lineText(footer)}` : ''}.`}</span>\n </AsciiFrameBody>\n );\n});\n"],"mappings":"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","names":["ChangeSummary"]}
@@ -0,0 +1,34 @@
1
+ // src/components/docs/figures/Decision.tsx
2
+ import * as React from "react";
3
+ import { cn } from "../../../lib/recipe.js";
4
+ import { childItems, defineAsciiItem } from "./FigureFrame.js";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ var Option = defineAsciiItem("Option");
7
+ var Decision = React.forwardRef(function Decision2({ question, context, options: optionsProp, recommendation, children, className, ...rest }, ref) {
8
+ const options = optionsProp ?? childItems(children, Option);
9
+ const picked = options.find((o) => o.picked);
10
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "decision", ref, role: "group", "aria-label": question, ...rest, className: cn("border-[3px] border-intent-warning bg-surface-raised shadow-hard-md", className), children: [
11
+ /* @__PURE__ */ jsxs("div", { className: "border-b-2 border-intent-warning px-4 py-3", children: [
12
+ /* @__PURE__ */ jsx("p", { className: "m-0 font-display text-base font-extrabold text-content-primary", children: question }),
13
+ context && /* @__PURE__ */ jsx("p", { className: "m-0 mt-1 font-mono text-xs text-content-muted", children: context })
14
+ ] }),
15
+ /* @__PURE__ */ jsx("div", { className: "grid sm:grid-cols-2", children: options.map((o, i) => /* @__PURE__ */ jsxs("div", { "data-slot": "decision-option", className: cn("flex flex-col gap-2 border-b border-edge-subtle px-4 py-3 sm:odd:border-r", o.picked && "bg-surface-sunken"), children: [
16
+ /* @__PURE__ */ jsxs("p", { className: "m-0 flex items-baseline justify-between gap-2 font-display text-sm font-bold text-content-primary", children: [
17
+ /* @__PURE__ */ jsx("span", { children: o.title }),
18
+ o.picked && /* @__PURE__ */ jsx("span", { className: "border-2 border-intent-success px-1 font-mono text-[0.6rem] uppercase text-intent-success", children: "Recommended" })
19
+ ] }),
20
+ o.children && /* @__PURE__ */ jsx("div", { className: "text-sm text-content-secondary [&_p]:m-0", children: o.children }),
21
+ o.cost && /* @__PURE__ */ jsxs("p", { className: "m-0 font-mono text-xs text-content-muted", children: [
22
+ "cost: ",
23
+ /* @__PURE__ */ jsx("span", { children: o.cost })
24
+ ] })
25
+ ] }, `${o.title}-${i}`)) }),
26
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: options.length === 0 ? "No options." : `${options.length} option${options.length === 1 ? "" : "s"}${picked ? `, recommended: ${picked.title}` : ""}.` }),
27
+ recommendation && /* @__PURE__ */ jsx("div", { className: "px-4 py-3 text-sm text-content-primary [&_p]:m-0", children: recommendation })
28
+ ] });
29
+ });
30
+ export {
31
+ Decision,
32
+ Option
33
+ };
34
+ //# sourceMappingURL=Decision.js.map