@misoto22/folio 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/dist/agent/Accordion.md +1 -1
  2. package/dist/agent/Actions.md +1 -1
  3. package/dist/agent/Alert.md +1 -1
  4. package/dist/agent/AppShell.md +1 -1
  5. package/dist/agent/ArchitectureFigure.md +1 -1
  6. package/dist/agent/AreaChart.md +1 -1
  7. package/dist/agent/Article.md +1 -1
  8. package/dist/agent/AspectRatio.md +1 -1
  9. package/dist/agent/Avatar.md +1 -1
  10. package/dist/agent/Badge.md +1 -1
  11. package/dist/agent/BarChart.md +1 -1
  12. package/dist/agent/BarList.md +1 -1
  13. package/dist/agent/BigNumber.md +1 -1
  14. package/dist/agent/BoxPlot.md +1 -1
  15. package/dist/agent/Breadcrumb.md +1 -1
  16. package/dist/agent/BulletChart.md +1 -1
  17. package/dist/agent/Button.md +1 -1
  18. package/dist/agent/Calendar.md +1 -1
  19. package/dist/agent/CalendarHeatmap.md +1 -1
  20. package/dist/agent/Card.md +1 -1
  21. package/dist/agent/Checkbox.md +1 -1
  22. package/dist/agent/Code.md +1 -1
  23. package/dist/agent/CodeBlock.md +1 -1
  24. package/dist/agent/Collapsible.md +1 -1
  25. package/dist/agent/Collection.md +1 -1
  26. package/dist/agent/ColorPicker.md +1 -1
  27. package/dist/agent/Combobox.md +1 -1
  28. package/dist/agent/Command.md +2 -1
  29. package/dist/agent/ComposedChart.md +1 -1
  30. package/dist/agent/Contact.md +1 -1
  31. package/dist/agent/Content.md +1 -1
  32. package/dist/agent/ContextMenu.md +1 -1
  33. package/dist/agent/Conversation.md +1 -1
  34. package/dist/agent/DataflowFigure.md +1 -1
  35. package/dist/agent/DatePicker.md +1 -1
  36. package/dist/agent/DescriptionList.md +1 -1
  37. package/dist/agent/Diagram.md +1 -1
  38. package/dist/agent/DiagramCanvas.md +1 -1
  39. package/dist/agent/DiagramExportMenu.md +1 -1
  40. package/dist/agent/DiagramInspector.md +1 -1
  41. package/dist/agent/DiagramLegend.md +1 -1
  42. package/dist/agent/DiagramMinimap.md +1 -1
  43. package/dist/agent/DiagramToolbar.md +1 -1
  44. package/dist/agent/Dialog.md +1 -1
  45. package/dist/agent/DropdownMenu.md +1 -1
  46. package/dist/agent/EmptyState.md +1 -1
  47. package/dist/agent/ErrorState.md +1 -1
  48. package/dist/agent/Evidence.md +1 -1
  49. package/dist/agent/Facet.md +1 -1
  50. package/dist/agent/Field.md +1 -1
  51. package/dist/agent/FigureBand.md +1 -1
  52. package/dist/agent/FloatingIconButton.md +1 -1
  53. package/dist/agent/FunnelChart.md +1 -1
  54. package/dist/agent/Heading.md +1 -1
  55. package/dist/agent/Heatmap.md +1 -1
  56. package/dist/agent/Histogram.md +1 -1
  57. package/dist/agent/Input.md +1 -1
  58. package/dist/agent/Kbd.md +1 -1
  59. package/dist/agent/LifecycleFigure.md +1 -1
  60. package/dist/agent/LineChart.md +1 -1
  61. package/dist/agent/LinkArrow.md +1 -1
  62. package/dist/agent/Markdown.md +1 -1
  63. package/dist/agent/Media.md +1 -1
  64. package/dist/agent/Metric.md +1 -1
  65. package/dist/agent/Metrics.md +1 -1
  66. package/dist/agent/Music.md +1 -1
  67. package/dist/agent/NativeSelect.md +1 -1
  68. package/dist/agent/NavItem.md +1 -1
  69. package/dist/agent/NumberField.md +1 -1
  70. package/dist/agent/PageHeader.md +1 -1
  71. package/dist/agent/Pagination.md +1 -1
  72. package/dist/agent/PieChart.md +1 -1
  73. package/dist/agent/Popover.md +1 -1
  74. package/dist/agent/Portfolio.md +1 -1
  75. package/dist/agent/PortfolioIndex.md +1 -1
  76. package/dist/agent/Progress.md +1 -1
  77. package/dist/agent/RadarChart.md +1 -1
  78. package/dist/agent/RadialChart.md +1 -1
  79. package/dist/agent/RadioGroup.md +1 -1
  80. package/dist/agent/Reading.md +10 -1
  81. package/dist/agent/Recovery.md +3 -3
  82. package/dist/agent/SankeyChart.md +1 -1
  83. package/dist/agent/ScatterChart.md +1 -1
  84. package/dist/agent/ScrollArea.md +1 -1
  85. package/dist/agent/SearchPalette.md +1 -1
  86. package/dist/agent/SearchableMenu.md +1 -1
  87. package/dist/agent/Select.md +1 -1
  88. package/dist/agent/Separator.md +1 -1
  89. package/dist/agent/SequenceFigure.md +1 -1
  90. package/dist/agent/Sheet.md +1 -1
  91. package/dist/agent/Sidebar.md +1 -1
  92. package/dist/agent/SiteNavigation.md +1 -1
  93. package/dist/agent/SiteShell.md +1 -1
  94. package/dist/agent/Skeleton.md +1 -1
  95. package/dist/agent/Slider.md +1 -1
  96. package/dist/agent/Sparkline.md +1 -1
  97. package/dist/agent/Spinner.md +1 -1
  98. package/dist/agent/StatusDot.md +1 -1
  99. package/dist/agent/StatusPill.md +1 -1
  100. package/dist/agent/Steps.md +1 -1
  101. package/dist/agent/Switch.md +1 -1
  102. package/dist/agent/Table.md +1 -1
  103. package/dist/agent/Tabs.md +1 -1
  104. package/dist/agent/Tag.md +1 -1
  105. package/dist/agent/Text.md +1 -1
  106. package/dist/agent/Textarea.md +1 -1
  107. package/dist/agent/Timeline.md +6 -2
  108. package/dist/agent/Timestamp.md +1 -1
  109. package/dist/agent/Toast.md +1 -1
  110. package/dist/agent/ToggleGroup.md +1 -1
  111. package/dist/agent/Toolbar.md +1 -1
  112. package/dist/agent/Tooltip.md +1 -1
  113. package/dist/agent/TreemapChart.md +1 -1
  114. package/dist/agent/WaterfallChart.md +1 -1
  115. package/dist/agent/Workbench.md +1 -1
  116. package/dist/agent/WorkflowFigure.md +1 -1
  117. package/dist/agent/catalog.json +10 -1
  118. package/dist/agent/index.md +2 -1
  119. package/dist/article.css +1 -1
  120. package/dist/components/Button/Button.js +1 -1
  121. package/dist/components/Button/Button.js.map +1 -1
  122. package/dist/components/Command/Command.d.ts +3 -1
  123. package/dist/components/Command/Command.js +3 -2
  124. package/dist/components/Command/Command.js.map +1 -1
  125. package/dist/components/FigureBand/FigureBand.js +1 -1
  126. package/dist/components/FigureBand/FigureBand.js.map +1 -1
  127. package/dist/components/Pagination/Pagination.js +2 -2
  128. package/dist/components/Pagination/Pagination.js.map +1 -1
  129. package/dist/components/Table/Table.js +1 -1
  130. package/dist/components/Table/Table.js.map +1 -1
  131. package/dist/components/Tag/Tag.js +1 -1
  132. package/dist/components/Tag/Tag.js.map +1 -1
  133. package/dist/components/Text/Text.js +2 -2
  134. package/dist/components/Text/Text.js.map +1 -1
  135. package/dist/components/ToggleGroup/ToggleGroup.js +2 -2
  136. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
  137. package/dist/keyframes.css +22 -0
  138. package/dist/semantic.css +6 -4
  139. package/dist/styles.css +1 -1
  140. package/dist/themes.css +22 -5
  141. package/dist/tokens.css +27 -0
  142. package/dist/tokens.d.ts +6 -0
  143. package/dist/tokens.js +35 -1
  144. package/dist/tokens.json +35 -1
  145. package/dist/website/Media/Media.js +2 -2
  146. package/dist/website/Media/Media.js.map +1 -1
  147. package/dist/website/Reading/Reading.d.ts +7 -1
  148. package/dist/website/Reading/Reading.js +4 -0
  149. package/dist/website/Reading/Reading.js.map +1 -1
  150. package/dist/website/Recovery/Recovery.d.ts +10 -2
  151. package/dist/website/Recovery/Recovery.js +36 -2
  152. package/dist/website/Recovery/Recovery.js.map +1 -1
  153. package/dist/website/SearchPalette/SearchPalette.js +2 -0
  154. package/dist/website/SearchPalette/SearchPalette.js.map +1 -1
  155. package/dist/website/Timeline/Timeline.d.ts +11 -2
  156. package/dist/website/Timeline/Timeline.js +7 -1
  157. package/dist/website/Timeline/Timeline.js.map +1 -1
  158. package/dist/website/Workbench/theme.d.ts +2 -1
  159. package/dist/website/Workbench/theme.js +2 -1
  160. package/dist/website/Workbench/theme.js.map +1 -1
  161. package/dist/website/index.d.ts +2 -2
  162. package/dist/website-collection.css +2 -2
  163. package/dist/website-contact.css +9 -3
  164. package/dist/website-conversation.css +5 -5
  165. package/dist/website-evidence.css +1 -1
  166. package/dist/website-media.css +11 -5
  167. package/dist/website-metrics.css +1 -1
  168. package/dist/website-music.css +1 -1
  169. package/dist/website-reading.css +18 -7
  170. package/dist/website-recovery.css +9 -1
  171. package/dist/website-search.css +28 -14
  172. package/dist/website-timeline.css +9 -3
  173. package/package.json +1 -1
  174. package/skills/folio-design/rules/tokens.md +1 -0
package/dist/tokens.css CHANGED
@@ -79,6 +79,11 @@
79
79
  --rule: #edebe6;
80
80
  --rule-2: #dbdbd6;
81
81
  --rule-hard: var(--ink);
82
+ /* The hard rule as a section DIVIDER — under a band heading, above a form,
83
+ between groups. Its own name so the rules axis can take dividers away
84
+ without touching the hard edges controls draw (a slider track, a table
85
+ head, a quotation bar). */
86
+ --rule-divider: var(--rule-hard);
82
87
 
83
88
  /* Historic role names, kept so a consumer written against the previous
84
89
  palette keeps resolving. Their hue is now ink; the role remains "the
@@ -191,6 +196,11 @@
191
196
  --rule: rgba(244, 244, 242, 0.14);
192
197
  --rule-2: rgba(244, 244, 242, 0.26);
193
198
  --rule-hard: var(--ink);
199
+ /* The hard rule as a section DIVIDER — under a band heading, above a form,
200
+ between groups. Its own name so the rules axis can take dividers away
201
+ without touching the hard edges controls draw (a slider track, a table
202
+ head, a quotation bar). */
203
+ --rule-divider: var(--rule-hard);
194
204
 
195
205
  --red: var(--clay);
196
206
  --red-ink: var(--clay);
@@ -279,6 +289,15 @@
279
289
 
280
290
  }
281
291
 
292
+ /* Device hardware. A phone frame is drawn in the colours of the object, not
293
+ the page: a dark bezel and a black island read as an iPhone in light and
294
+ dark mode alike, so these do not flip with `data-mode`. */
295
+ :root {
296
+ --device-bezel: #1d1d1f;
297
+ --device-rim: #4a4a4d;
298
+ --device-island: #000;
299
+ }
300
+
282
301
  /* Display voice. How loud a page's display type and labels are, read by the
283
302
  website compositions instead of literal values so the voice axis in
284
303
  `themes.css` can quiet them. Declared on the root only — never on a fluid
@@ -289,6 +308,9 @@
289
308
  --label-font: var(--font-mono);
290
309
  --label-case: uppercase;
291
310
  --label-tracking-scale: 1;
311
+ /* Running text: the White Reset sets it light; a quiet voice sets it at a
312
+ regular weight to match its regular-weight headings. */
313
+ --body-weight: 300;
292
314
  }
293
315
 
294
316
  /* The fluid ramp, computed wherever `--fluid` is set.
@@ -359,6 +381,11 @@
359
381
  --radius: calc(8px * var(--radius-factor)); /* inputs, code, small plates */
360
382
  --radius-lg: calc(12px * var(--radius-factor)); /* cards, dialogs, menu panels */
361
383
  --radius-pill: calc(999px * var(--radius-factor)); /* capsules and counters */
384
+ /* The corner every pressable control shares — a button, an icon button, a
385
+ launcher. A role rather than a step, so a theme can make controls capsules
386
+ without re-shaping the inputs and plates that also sit on `--radius`. The
387
+ `data-controls` axis in themes.css is the only thing that moves it. */
388
+ --radius-control: var(--radius);
362
389
 
363
390
  /* The nesting law, applied to the two insets this system actually uses. */
364
391
  --radius-row: max(0px, calc(var(--radius-lg) - 0.375rem)); /* a row in a 6px-padded panel */
package/dist/tokens.d.ts CHANGED
@@ -26,6 +26,7 @@ export type TokenName =
26
26
  | 'bar'
27
27
  | 'bar-2'
28
28
  | 'bar-h'
29
+ | 'body-weight'
29
30
  | 'border-color'
30
31
  | 'border-subtle'
31
32
  | 'brand-plate'
@@ -58,6 +59,9 @@ export type TokenName =
58
59
  | 'danger'
59
60
  | 'danger-soft'
60
61
  | 'danger-wash'
62
+ | 'device-bezel'
63
+ | 'device-island'
64
+ | 'device-rim'
61
65
  | 'diagram-band'
62
66
  | 'diagram-chip'
63
67
  | 'diagram-ink'
@@ -140,6 +144,7 @@ export type TokenName =
140
144
  | 'photo-scrim'
141
145
  | 'photo-shadow'
142
146
  | 'radius'
147
+ | 'radius-control'
143
148
  | 'radius-factor'
144
149
  | 'radius-frame'
145
150
  | 'radius-gate'
@@ -156,6 +161,7 @@ export type TokenName =
156
161
  | 'row'
157
162
  | 'rule'
158
163
  | 'rule-2'
164
+ | 'rule-divider'
159
165
  | 'rule-hard'
160
166
  | 'sans'
161
167
  | 'scrim'
package/dist/tokens.js CHANGED
@@ -137,6 +137,13 @@ export const TOKENS = {
137
137
  "light": "var(--ink)",
138
138
  "dark": "var(--ink)"
139
139
  },
140
+ "rule-divider": {
141
+ "layer": "tokens",
142
+ "category": "colour",
143
+ "light": "var(--rule-hard)",
144
+ "comment": "The hard rule as a section DIVIDER — under a band heading, above a form, between groups. Its own name so the rules axis can take dividers away without touching the hard edges controls draw (a slider track, a table head, a quotation bar).",
145
+ "dark": "var(--rule-hard)"
146
+ },
140
147
  "red": {
141
148
  "layer": "tokens",
142
149
  "category": "colour",
@@ -294,6 +301,21 @@ export const TOKENS = {
294
301
  "category": "space",
295
302
  "light": "52px"
296
303
  },
304
+ "device-bezel": {
305
+ "layer": "tokens",
306
+ "category": "colour",
307
+ "light": "#1d1d1f"
308
+ },
309
+ "device-rim": {
310
+ "layer": "tokens",
311
+ "category": "colour",
312
+ "light": "#4a4a4d"
313
+ },
314
+ "device-island": {
315
+ "layer": "tokens",
316
+ "category": "colour",
317
+ "light": "#000"
318
+ },
297
319
  "display-scale": {
298
320
  "layer": "tokens",
299
321
  "category": "other",
@@ -319,6 +341,12 @@ export const TOKENS = {
319
341
  "category": "other",
320
342
  "light": "1"
321
343
  },
344
+ "body-weight": {
345
+ "layer": "tokens",
346
+ "category": "other",
347
+ "light": "300",
348
+ "comment": "Running text: the White Reset sets it light; a quiet voice sets it at a regular weight to match its regular-weight headings."
349
+ },
322
350
  "fs-title": {
323
351
  "layer": "tokens",
324
352
  "category": "type",
@@ -405,6 +433,12 @@ export const TOKENS = {
405
433
  "light": "calc(999px * var(--radius-factor))",
406
434
  "comment": "capsules and counters"
407
435
  },
436
+ "radius-control": {
437
+ "layer": "tokens",
438
+ "category": "radius",
439
+ "light": "var(--radius)",
440
+ "comment": "The corner every pressable control shares — a button, an icon button, a launcher. A role rather than a step, so a theme can make controls capsules without re-shaping the inputs and plates that also sit on `--radius`. The `data-controls` axis in themes.css is the only thing that moves it."
441
+ },
408
442
  "radius-row": {
409
443
  "layer": "tokens",
410
444
  "category": "radius",
@@ -777,7 +811,7 @@ export const TOKENS = {
777
811
  "layer": "semantic",
778
812
  "category": "other",
779
813
  "light": "var(--accent)",
780
- "comment": "The fill of the one primary action on a screen. It follows the accent unless `data-action` says otherwise: a preset that keeps its accent for links and the current state can hand the primary button back to ink, without a checked box or the current page losing the accent."
814
+ "comment": "The SOLID fill of a primary action or a chosen option — the primary button, the sliding pill of a segmented control, the active filter chip, the current page. It follows the accent unless `data-action` says otherwise: a preset that keeps its accent for links can hand these large fills back to ink, while small marks (a checked box, a switch, a slider's range) keep the accent."
781
815
  },
782
816
  "action-foreground": {
783
817
  "layer": "semantic",
package/dist/tokens.json CHANGED
@@ -136,6 +136,13 @@
136
136
  "light": "var(--ink)",
137
137
  "dark": "var(--ink)"
138
138
  },
139
+ "rule-divider": {
140
+ "layer": "tokens",
141
+ "category": "colour",
142
+ "light": "var(--rule-hard)",
143
+ "comment": "The hard rule as a section DIVIDER — under a band heading, above a form, between groups. Its own name so the rules axis can take dividers away without touching the hard edges controls draw (a slider track, a table head, a quotation bar).",
144
+ "dark": "var(--rule-hard)"
145
+ },
139
146
  "red": {
140
147
  "layer": "tokens",
141
148
  "category": "colour",
@@ -293,6 +300,21 @@
293
300
  "category": "space",
294
301
  "light": "52px"
295
302
  },
303
+ "device-bezel": {
304
+ "layer": "tokens",
305
+ "category": "colour",
306
+ "light": "#1d1d1f"
307
+ },
308
+ "device-rim": {
309
+ "layer": "tokens",
310
+ "category": "colour",
311
+ "light": "#4a4a4d"
312
+ },
313
+ "device-island": {
314
+ "layer": "tokens",
315
+ "category": "colour",
316
+ "light": "#000"
317
+ },
296
318
  "display-scale": {
297
319
  "layer": "tokens",
298
320
  "category": "other",
@@ -318,6 +340,12 @@
318
340
  "category": "other",
319
341
  "light": "1"
320
342
  },
343
+ "body-weight": {
344
+ "layer": "tokens",
345
+ "category": "other",
346
+ "light": "300",
347
+ "comment": "Running text: the White Reset sets it light; a quiet voice sets it at a regular weight to match its regular-weight headings."
348
+ },
321
349
  "fs-title": {
322
350
  "layer": "tokens",
323
351
  "category": "type",
@@ -404,6 +432,12 @@
404
432
  "light": "calc(999px * var(--radius-factor))",
405
433
  "comment": "capsules and counters"
406
434
  },
435
+ "radius-control": {
436
+ "layer": "tokens",
437
+ "category": "radius",
438
+ "light": "var(--radius)",
439
+ "comment": "The corner every pressable control shares — a button, an icon button, a launcher. A role rather than a step, so a theme can make controls capsules without re-shaping the inputs and plates that also sit on `--radius`. The `data-controls` axis in themes.css is the only thing that moves it."
440
+ },
407
441
  "radius-row": {
408
442
  "layer": "tokens",
409
443
  "category": "radius",
@@ -776,7 +810,7 @@
776
810
  "layer": "semantic",
777
811
  "category": "other",
778
812
  "light": "var(--accent)",
779
- "comment": "The fill of the one primary action on a screen. It follows the accent unless `data-action` says otherwise: a preset that keeps its accent for links and the current state can hand the primary button back to ink, without a checked box or the current page losing the accent."
813
+ "comment": "The SOLID fill of a primary action or a chosen option — the primary button, the sliding pill of a segmented control, the active filter chip, the current page. It follows the accent unless `data-action` says otherwise: a preset that keeps its accent for links can hand these large fills back to ink, while small marks (a checked box, a switch, a slider's range) keep the accent."
780
814
  },
781
815
  "action-foreground": {
782
816
  "layer": "semantic",
@@ -52,7 +52,7 @@ function MediaGalleryItem({ children, title, index, location, orientation, class
52
52
  ] }) });
53
53
  }
54
54
  function MediaGallerySkeleton({ label, count = 6 }) {
55
- return /* @__PURE__ */ jsxs(SkeletonPage, { label, "aria-label": label, children: [
55
+ return /* @__PURE__ */ jsx("div", { className: "folio-loading-page", children: /* @__PURE__ */ jsxs(SkeletonPage, { label, "aria-label": label, children: [
56
56
  /* @__PURE__ */ jsxs("header", { className: "folio-media-masthead", children: [
57
57
  /* @__PURE__ */ jsx(SkeletonLine, { className: "w-24" }),
58
58
  /* @__PURE__ */ jsx(SkeletonBlock, { className: "mt-4 h-9 w-[min(560px,90%)]" }),
@@ -63,7 +63,7 @@ function MediaGallerySkeleton({ label, count = 6 }) {
63
63
  /* @__PURE__ */ jsx(SkeletonBlock, { className: "folio-media-gallery-mount" }),
64
64
  /* @__PURE__ */ jsx(SkeletonLine, { className: "mt-3 w-3/4" })
65
65
  ] }, index)) }) })
66
- ] });
66
+ ] }) });
67
67
  }
68
68
  function MediaDetailLayout({ title, titleId, headingLevel = 1, category, notesLabel, orientation, backLink, index, media, light, actions, metadata, location, pager, related, className, ...rest }) {
69
69
  const Heading = `h${headingLevel}`;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/website/Media/Media.tsx"],"sourcesContent":["import { Slot } from '@radix-ui/react-slot'\nimport { RiAddLine, RiSubtractLine } from '@remixicon/react'\nimport type { ComponentPropsWithRef, HTMLAttributes, ReactElement, ReactNode } from 'react'\nimport { Badge } from '../../components/Badge/Badge'\nimport { Button, type ButtonProps } from '../../components/Button/Button'\nimport { DescriptionList, type DescriptionListItem } from '../../components/DescriptionList/DescriptionList'\nimport { EmptyState } from '../../components/EmptyState/EmptyState'\nimport { SkeletonBlock, SkeletonLine, SkeletonPage } from '../../components/Skeleton/Skeleton'\nimport { cn } from '../../lib/cn'\nimport { replaceSlotContent } from '../../lib/slot-content'\n\nexport interface MediaMastheadProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {\n eyebrow?: ReactNode\n title: ReactNode\n description?: ReactNode\n /** The title level when this page composition sits inside another document. */\n headingLevel?: 1 | 2 | 3 | 4 | 5 | 6\n}\n\n/** The quiet opening to an image collection, before its controls and prints. */\nexport function MediaMasthead({ eyebrow, title, description, headingLevel = 1, className, ...rest }: MediaMastheadProps) {\n const Heading = `h${headingLevel}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'\n\n return (\n <header className={cn('folio-media-masthead', className)} {...rest}>\n {eyebrow && <p className=\"folio-media-eyebrow\">{eyebrow}</p>}\n <Heading className=\"folio-media-masthead-title\">{title}</Heading>\n {description && <p className=\"folio-media-description\">{description}</p>}\n </header>\n )\n}\n\nexport interface PhotographFeatureProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {\n image: ReactNode\n title: ReactNode\n eyebrow?: ReactNode\n description?: ReactNode\n caption?: ReactNode\n action?: ReactNode\n}\n\n/** A photograph and an editorial introduction, with the original image ratio. */\nexport function PhotographFeature({ image, title, eyebrow, description, caption, action, className, ...rest }: PhotographFeatureProps) {\n return (\n <section className={cn('folio-photograph-feature', className)} {...rest}>\n <div className=\"folio-photograph-feature-copy\">\n {eyebrow && <p className=\"folio-media-eyebrow\">{eyebrow}</p>}\n <h2 className=\"folio-photograph-feature-title\">{title}</h2>\n {description && <div className=\"folio-media-description\">{description}</div>}\n {action && <div className=\"folio-photograph-feature-action\">{action}</div>}\n </div>\n <figure className=\"folio-photograph-feature-figure\">\n <div className=\"folio-photograph-feature-image\">{image}</div>\n {caption && <figcaption className=\"folio-photograph-feature-caption\">{caption}</figcaption>}\n </figure>\n </section>\n )\n}\n\nexport interface MediaCollectionProps extends HTMLAttributes<HTMLDivElement> {\n masthead: ReactNode\n controls?: ReactNode\n collectionId?: string\n footer?: ReactNode\n}\n\n/** Page-level gallery composition; the host owns its filters and collection. */\nexport function MediaCollection({ masthead, controls, collectionId, footer, children, className, ...rest }: MediaCollectionProps) {\n return (\n <div className={cn('folio-media-gallery-page', className)} {...rest}>\n {masthead}\n {controls && <div className=\"folio-media-container\">{controls}</div>}\n <section id={collectionId} className=\"folio-media-gallery-content\">{children}</section>\n {footer}\n </div>\n )\n}\n\nexport interface MediaGalleryProps extends HTMLAttributes<HTMLUListElement> {\n children?: ReactNode\n /** Supply localized empty copy when the collection may have no children. */\n emptyTitle?: ReactNode\n emptyAction?: ReactNode\n}\n\n/** Level mounts keep unlike aspect ratios aligned without cropping the prints. */\nexport function MediaGallery({ children, emptyTitle, emptyAction, className, ...rest }: MediaGalleryProps) {\n return (\n <div className=\"folio-media-container\">\n {emptyTitle ? <EmptyState title={emptyTitle} action={emptyAction} /> : (\n <ul role=\"list\" className={cn('folio-media-gallery', className)} {...rest}>{children}</ul>\n )}\n </div>\n )\n}\n\nexport interface MediaGalleryItemProps extends Omit<HTMLAttributes<HTMLLIElement>, 'title'> {\n /** A host link containing its image. The package supplies the print layout. */\n children: ReactElement\n title: ReactNode\n index?: ReactNode\n location?: ReactNode\n orientation?: 'portrait' | 'landscape' | 'square'\n}\n\nexport function MediaGalleryItem({ children, title, index, location, orientation, className, ...rest }: MediaGalleryItemProps) {\n return (\n <li className={cn('folio-media-gallery-item', className)} {...rest}>\n <figure>\n <Slot className=\"folio-media-gallery-link\" data-photo-orientation={orientation}>{children}</Slot>\n <figcaption className=\"folio-media-gallery-caption\">\n {index != null && <span className=\"folio-media-gallery-index\" aria-hidden=\"true\">{index}</span>}\n <span className=\"folio-media-gallery-title\">{title}</span>\n {location && <span className=\"folio-media-gallery-location\">{location}</span>}\n </figcaption>\n </figure>\n </li>\n )\n}\n\nexport interface MediaGallerySkeletonProps {\n label: string\n count?: number\n}\n\nexport function MediaGallerySkeleton({ label, count = 6 }: MediaGallerySkeletonProps) {\n return (\n <SkeletonPage label={label} aria-label={label}>\n <header className=\"folio-media-masthead\">\n <SkeletonLine className=\"w-24\" />\n <SkeletonBlock className=\"mt-4 h-9 w-[min(560px,90%)]\" />\n <SkeletonBlock className=\"mt-2.5 h-9 w-[min(380px,70%)]\" />\n </header>\n <div className=\"folio-media-container\"><SkeletonLine className=\"h-11 w-full\" /></div>\n <section data-loading-content className=\"folio-media-gallery-content\">\n <MediaGallery>\n {Array.from({ length: count }, (_, index) => (\n <li key={index}>\n <SkeletonBlock className=\"folio-media-gallery-mount\" />\n <SkeletonLine className=\"mt-3 w-3/4\" />\n </li>\n ))}\n </MediaGallery>\n </section>\n </SkeletonPage>\n )\n}\n\nexport interface MediaDetailLayoutProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {\n title: ReactNode\n titleId: string\n /** The title level when this page composition sits inside another document. */\n headingLevel?: 1 | 2 | 3 | 4 | 5 | 6\n category?: ReactNode\n notesLabel: string\n orientation: 'portrait' | 'landscape' | 'square'\n backLink: ReactElement\n index?: ReactNode\n media: ReactNode\n light?: ReactNode\n actions?: ReactNode\n metadata?: ReactNode\n location?: ReactNode\n pager?: ReactNode\n related?: ReactNode\n}\n\n/** Portraits share a spread with their notes; landscapes lead a full-width spread. */\nexport function MediaDetailLayout({ title, titleId, headingLevel = 1, category, notesLabel, orientation, backLink, index, media, light, actions, metadata, location, pager, related, className, ...rest }: MediaDetailLayoutProps) {\n const Heading = `h${headingLevel}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'\n\n return (\n <div className=\"folio-media-detail-page\">\n <article className={cn('folio-media-detail', className)} aria-labelledby={titleId} data-photo-detail {...rest}>\n <header className=\"folio-media-index-bar\">\n <Button asChild variant=\"ghost\">{backLink}</Button>\n {index && <span className=\"folio-media-index\">{index}</span>}\n </header>\n <div className=\"folio-media-spread\" data-photo-spread={orientation === 'landscape' ? 'wide' : 'tall'}>\n <div className=\"folio-media-well\" data-photo-media>{media}</div>\n <aside className=\"folio-media-ledger\" aria-label={notesLabel} data-photo-ledger>\n <div className=\"folio-media-ledger-narrative\">\n <div className=\"folio-media-ledger-lead\">\n {category && <p className=\"folio-media-eyebrow\">{category}</p>}\n <Heading id={titleId} className=\"folio-media-detail-title\">{title}</Heading>\n </div>\n {light}\n {actions && <div className=\"folio-media-actions\" data-photo-actions>{actions}</div>}\n </div>\n <div className=\"folio-media-ledger-data\">{metadata}{location}</div>\n </aside>\n </div>\n {pager}\n {related}\n </article>\n </div>\n )\n}\n\nexport interface MediaMetadataProps extends HTMLAttributes<HTMLDListElement> {\n items: DescriptionListItem[]\n}\n\nexport function MediaMetadata({ items, className, ...rest }: MediaMetadataProps) {\n return <DescriptionList items={items} className={cn('folio-media-metadata', className)} data-photo-specs {...rest} />\n}\n\nexport interface MediaPagerProps extends HTMLAttributes<HTMLElement> {\n label: string\n previous?: ReactNode\n next?: ReactNode\n}\n\nexport function MediaPager({ label, previous, next, className, ...rest }: MediaPagerProps) {\n if (!previous && !next) return null\n return <nav aria-label={label} className={cn('folio-media-pager', className)} data-photo-pager {...rest}>{previous || <span />}{next || <span />}</nav>\n}\n\nexport interface MediaPagerItemProps {\n link: ReactElement\n direction: 'previous' | 'next'\n label: ReactNode\n title: ReactNode\n image?: ReactNode\n}\n\nexport function MediaPagerItem({ link, direction, label, title, image }: MediaPagerItemProps) {\n return (\n <Slot className=\"folio-media-pager-link\" data-direction={direction}>\n {/** Slot composes the host link while retaining its routing behavior. */}\n {replaceSlotContent(link, <>\n {image && direction === 'previous' && <span className=\"folio-media-pager-image\">{image}</span>}\n <span className=\"folio-media-pager-copy\">\n <span className=\"folio-media-pager-label\">{label}</span>{' '}\n <strong className=\"folio-media-pager-title\">{title}</strong>\n </span>\n {image && direction === 'next' && <span className=\"folio-media-pager-image\">{image}</span>}\n </>)}\n </Slot>\n )\n}\n\nexport interface MediaThumbnailStripProps extends HTMLAttributes<HTMLElement> {\n heading: ReactNode\n}\n\nexport function MediaThumbnailStrip({ heading, children, className, ...rest }: MediaThumbnailStripProps) {\n return <section className={cn('folio-media-thumbnails', className)} data-photo-related {...rest}><h2>{heading}</h2><div className=\"folio-media-thumbnail-list\">{children}</div></section>\n}\n\nexport interface MediaThumbnailProps {\n children: ReactElement\n}\n\nexport function MediaThumbnail({ children }: MediaThumbnailProps) {\n return <Slot className=\"folio-media-thumbnail\">{children}</Slot>\n}\n\nexport interface MediaLightNoteLine {\n id: string\n icon?: ReactNode\n text: ReactNode\n detail?: ReactNode\n}\n\nexport interface MediaLightNoteProps {\n label: ReactNode\n lines: MediaLightNoteLine[]\n}\n\n/** The host supplies observations and formatted labels; this is presentation only. */\nexport function MediaLightNote({ label, lines }: MediaLightNoteProps) {\n return <div className=\"folio-media-light\"><Badge tone=\"outline\">{label}</Badge>{lines.map((line) => <div className=\"folio-media-light-line\" key={line.id}>{line.icon}<span>{line.text}</span>{line.detail && <span className=\"folio-media-light-detail\">{line.detail}</span>}</div>)}</div>\n}\n\nexport interface MediaMapSectionProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {\n title: ReactNode\n hint?: ReactNode\n /** An optional legacy anchor, owned and named by the host. */\n aliasId?: string\n map: ReactNode\n preview: ReactNode\n}\n\nexport function MediaMapSection({ title, hint, aliasId, map, preview, className, ...rest }: MediaMapSectionProps) {\n return <section className={cn('folio-media-map-section', className)} {...rest}><div className=\"folio-media-container\">{aliasId && <span id={aliasId} className=\"folio-media-anchor\" aria-hidden=\"true\" />}<header><h2>{title}</h2>{hint && <p>{hint}</p>}</header><div className=\"folio-media-map-grid\">{map}{preview}</div></div></section>\n}\n\nexport interface MediaMapFrameProps extends HTMLAttributes<HTMLDivElement> {\n label?: ReactNode\n hint?: ReactNode\n navigation?: ReactNode\n compact?: boolean\n}\n\nexport function MediaMapFrame({ label, hint, navigation, compact = false, children, className, ...rest }: MediaMapFrameProps) {\n return <div className={cn('folio-media-map-frame', compact && 'folio-media-map-frame-compact', className)} {...rest}>{label && <span className=\"folio-media-map-label\">{label}</span>}<div className=\"folio-media-map-stage\">{children}{hint && <div className=\"folio-media-map-hint\">{hint}</div>}{navigation && <div className=\"folio-media-map-navigation\">{navigation}</div>}</div></div>\n}\n\nexport interface MediaMapNavigationProps {\n zoomInLabel: string\n zoomOutLabel: string\n onZoomIn: () => void\n onZoomOut: () => void\n}\n\nexport function MediaMapNavigation({ zoomInLabel, zoomOutLabel, onZoomIn, onZoomOut }: MediaMapNavigationProps) {\n return <><Button variant=\"secondary\" iconOnly aria-label={zoomInLabel} onClick={onZoomIn}><RiAddLine size={18} aria-hidden=\"true\" /></Button><Button variant=\"secondary\" iconOnly aria-label={zoomOutLabel} onClick={onZoomOut}><RiSubtractLine size={18} aria-hidden=\"true\" /></Button></>\n}\n\nexport type MediaMapCanvasProps = ComponentPropsWithRef<'div'>\n\n/** A sized provider container. Map creation, themes and geographic math stay in the host. */\nexport function MediaMapCanvas({ className, ...rest }: MediaMapCanvasProps) {\n return <div className={cn('folio-media-map-canvas', className)} {...rest} />\n}\n\nexport interface MediaMapPreviewProps {\n title: ReactNode\n image?: ReactNode\n action?: ReactNode\n place?: ReactNode\n coordinates?: ReactNode\n children?: ReactNode\n}\n\nexport function MediaMapPreview({ title, image, action, place, coordinates, children }: MediaMapPreviewProps) {\n return <aside className=\"folio-media-map-preview\"><div className=\"folio-media-map-preview-image\">{image}{action && <div className=\"folio-media-map-preview-action\">{action}</div>}</div><div className=\"folio-media-map-preview-copy\"><h3>{title}</h3>{place && <p className=\"folio-media-map-place\">{place}</p>}{coordinates && <div className=\"folio-media-map-coordinates\">{coordinates}</div>}{children}</div></aside>\n}\n\nexport interface MediaMapArea {\n id: string\n label: ReactNode\n count: ReactNode\n}\n\nexport interface MediaMapAreaListProps {\n items: MediaMapArea[]\n value?: string\n onValueChange: (value: string) => void\n}\n\nexport function MediaMapAreaList({ items, value, onValueChange }: MediaMapAreaListProps) {\n return (\n <div className=\"folio-media-map-areas\">\n {items.map((item) => (\n <Button key={item.id} variant=\"ghost\" aria-pressed={value === item.id} onClick={() => onValueChange(item.id)} className=\"folio-media-map-area\">\n <span className=\"folio-media-map-area-name\"><span aria-hidden=\"true\" />{item.label}</span>{' '}\n <span className=\"folio-media-map-area-count\">{item.count}</span>\n </Button>\n ))}\n </div>\n )\n}\n\nexport interface MediaMapMarkerProps extends Omit<Extract<ButtonProps, { href?: undefined }>, 'children' | 'iconOnly'> {\n label: string\n active?: boolean\n}\n\n/** A keyboard-operable 44px marker for a host's map-provider portal. */\nexport function MediaMapMarker({ label, active = false, className, ...rest }: MediaMapMarkerProps) {\n return <Button {...rest} variant=\"ghost\" iconOnly aria-label={label} aria-pressed={active} className={cn('folio-media-map-marker', className)} style={{ width: 44, height: 44, ...rest.style }}><MediaMapPin active={active} /></Button>\n}\n\nexport interface MediaMapPinProps {\n active?: boolean\n}\n\nexport function MediaMapPin({ active = false }: MediaMapPinProps) {\n return <span className=\"folio-media-map-pin\" data-active={active || undefined} aria-hidden=\"true\"><span /></span>\n}\n\nexport function MediaMapSkeleton() {\n return <div className=\"folio-media-map-skeleton folio-media-container\" aria-hidden=\"true\"><SkeletonBlock className=\"folio-media-map-canvas\" /></div>\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/website/Media/Media.tsx"],"sourcesContent":["import { Slot } from '@radix-ui/react-slot'\nimport { RiAddLine, RiSubtractLine } from '@remixicon/react'\nimport type { ComponentPropsWithRef, HTMLAttributes, ReactElement, ReactNode } from 'react'\nimport { Badge } from '../../components/Badge/Badge'\nimport { Button, type ButtonProps } from '../../components/Button/Button'\nimport { DescriptionList, type DescriptionListItem } from '../../components/DescriptionList/DescriptionList'\nimport { EmptyState } from '../../components/EmptyState/EmptyState'\nimport { SkeletonBlock, SkeletonLine, SkeletonPage } from '../../components/Skeleton/Skeleton'\nimport { cn } from '../../lib/cn'\nimport { replaceSlotContent } from '../../lib/slot-content'\n\nexport interface MediaMastheadProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {\n eyebrow?: ReactNode\n title: ReactNode\n description?: ReactNode\n /** The title level when this page composition sits inside another document. */\n headingLevel?: 1 | 2 | 3 | 4 | 5 | 6\n}\n\n/** The quiet opening to an image collection, before its controls and prints. */\nexport function MediaMasthead({ eyebrow, title, description, headingLevel = 1, className, ...rest }: MediaMastheadProps) {\n const Heading = `h${headingLevel}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'\n\n return (\n <header className={cn('folio-media-masthead', className)} {...rest}>\n {eyebrow && <p className=\"folio-media-eyebrow\">{eyebrow}</p>}\n <Heading className=\"folio-media-masthead-title\">{title}</Heading>\n {description && <p className=\"folio-media-description\">{description}</p>}\n </header>\n )\n}\n\nexport interface PhotographFeatureProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {\n image: ReactNode\n title: ReactNode\n eyebrow?: ReactNode\n description?: ReactNode\n caption?: ReactNode\n action?: ReactNode\n}\n\n/** A photograph and an editorial introduction, with the original image ratio. */\nexport function PhotographFeature({ image, title, eyebrow, description, caption, action, className, ...rest }: PhotographFeatureProps) {\n return (\n <section className={cn('folio-photograph-feature', className)} {...rest}>\n <div className=\"folio-photograph-feature-copy\">\n {eyebrow && <p className=\"folio-media-eyebrow\">{eyebrow}</p>}\n <h2 className=\"folio-photograph-feature-title\">{title}</h2>\n {description && <div className=\"folio-media-description\">{description}</div>}\n {action && <div className=\"folio-photograph-feature-action\">{action}</div>}\n </div>\n <figure className=\"folio-photograph-feature-figure\">\n <div className=\"folio-photograph-feature-image\">{image}</div>\n {caption && <figcaption className=\"folio-photograph-feature-caption\">{caption}</figcaption>}\n </figure>\n </section>\n )\n}\n\nexport interface MediaCollectionProps extends HTMLAttributes<HTMLDivElement> {\n masthead: ReactNode\n controls?: ReactNode\n collectionId?: string\n footer?: ReactNode\n}\n\n/** Page-level gallery composition; the host owns its filters and collection. */\nexport function MediaCollection({ masthead, controls, collectionId, footer, children, className, ...rest }: MediaCollectionProps) {\n return (\n <div className={cn('folio-media-gallery-page', className)} {...rest}>\n {masthead}\n {controls && <div className=\"folio-media-container\">{controls}</div>}\n <section id={collectionId} className=\"folio-media-gallery-content\">{children}</section>\n {footer}\n </div>\n )\n}\n\nexport interface MediaGalleryProps extends HTMLAttributes<HTMLUListElement> {\n children?: ReactNode\n /** Supply localized empty copy when the collection may have no children. */\n emptyTitle?: ReactNode\n emptyAction?: ReactNode\n}\n\n/** Level mounts keep unlike aspect ratios aligned without cropping the prints. */\nexport function MediaGallery({ children, emptyTitle, emptyAction, className, ...rest }: MediaGalleryProps) {\n return (\n <div className=\"folio-media-container\">\n {emptyTitle ? <EmptyState title={emptyTitle} action={emptyAction} /> : (\n <ul role=\"list\" className={cn('folio-media-gallery', className)} {...rest}>{children}</ul>\n )}\n </div>\n )\n}\n\nexport interface MediaGalleryItemProps extends Omit<HTMLAttributes<HTMLLIElement>, 'title'> {\n /** A host link containing its image. The package supplies the print layout. */\n children: ReactElement\n title: ReactNode\n index?: ReactNode\n location?: ReactNode\n orientation?: 'portrait' | 'landscape' | 'square'\n}\n\nexport function MediaGalleryItem({ children, title, index, location, orientation, className, ...rest }: MediaGalleryItemProps) {\n return (\n <li className={cn('folio-media-gallery-item', className)} {...rest}>\n <figure>\n <Slot className=\"folio-media-gallery-link\" data-photo-orientation={orientation}>{children}</Slot>\n <figcaption className=\"folio-media-gallery-caption\">\n {index != null && <span className=\"folio-media-gallery-index\" aria-hidden=\"true\">{index}</span>}\n <span className=\"folio-media-gallery-title\">{title}</span>\n {location && <span className=\"folio-media-gallery-location\">{location}</span>}\n </figcaption>\n </figure>\n </li>\n )\n}\n\nexport interface MediaGallerySkeletonProps {\n label: string\n count?: number\n}\n\nexport function MediaGallerySkeleton({ label, count = 6 }: MediaGallerySkeletonProps) {\n return (\n <div className=\"folio-loading-page\">\n <SkeletonPage label={label} aria-label={label}>\n <header className=\"folio-media-masthead\">\n <SkeletonLine className=\"w-24\" />\n <SkeletonBlock className=\"mt-4 h-9 w-[min(560px,90%)]\" />\n <SkeletonBlock className=\"mt-2.5 h-9 w-[min(380px,70%)]\" />\n </header>\n <div className=\"folio-media-container\"><SkeletonLine className=\"h-11 w-full\" /></div>\n <section data-loading-content className=\"folio-media-gallery-content\">\n <MediaGallery>\n {Array.from({ length: count }, (_, index) => (\n <li key={index}>\n <SkeletonBlock className=\"folio-media-gallery-mount\" />\n <SkeletonLine className=\"mt-3 w-3/4\" />\n </li>\n ))}\n </MediaGallery>\n </section>\n </SkeletonPage>\n </div>\n )\n}\n\nexport interface MediaDetailLayoutProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {\n title: ReactNode\n titleId: string\n /** The title level when this page composition sits inside another document. */\n headingLevel?: 1 | 2 | 3 | 4 | 5 | 6\n category?: ReactNode\n notesLabel: string\n orientation: 'portrait' | 'landscape' | 'square'\n backLink: ReactElement\n index?: ReactNode\n media: ReactNode\n light?: ReactNode\n actions?: ReactNode\n metadata?: ReactNode\n location?: ReactNode\n pager?: ReactNode\n related?: ReactNode\n}\n\n/** Portraits share a spread with their notes; landscapes lead a full-width spread. */\nexport function MediaDetailLayout({ title, titleId, headingLevel = 1, category, notesLabel, orientation, backLink, index, media, light, actions, metadata, location, pager, related, className, ...rest }: MediaDetailLayoutProps) {\n const Heading = `h${headingLevel}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'\n\n return (\n <div className=\"folio-media-detail-page\">\n <article className={cn('folio-media-detail', className)} aria-labelledby={titleId} data-photo-detail {...rest}>\n <header className=\"folio-media-index-bar\">\n <Button asChild variant=\"ghost\">{backLink}</Button>\n {index && <span className=\"folio-media-index\">{index}</span>}\n </header>\n <div className=\"folio-media-spread\" data-photo-spread={orientation === 'landscape' ? 'wide' : 'tall'}>\n <div className=\"folio-media-well\" data-photo-media>{media}</div>\n <aside className=\"folio-media-ledger\" aria-label={notesLabel} data-photo-ledger>\n <div className=\"folio-media-ledger-narrative\">\n <div className=\"folio-media-ledger-lead\">\n {category && <p className=\"folio-media-eyebrow\">{category}</p>}\n <Heading id={titleId} className=\"folio-media-detail-title\">{title}</Heading>\n </div>\n {light}\n {actions && <div className=\"folio-media-actions\" data-photo-actions>{actions}</div>}\n </div>\n <div className=\"folio-media-ledger-data\">{metadata}{location}</div>\n </aside>\n </div>\n {pager}\n {related}\n </article>\n </div>\n )\n}\n\nexport interface MediaMetadataProps extends HTMLAttributes<HTMLDListElement> {\n items: DescriptionListItem[]\n}\n\nexport function MediaMetadata({ items, className, ...rest }: MediaMetadataProps) {\n return <DescriptionList items={items} className={cn('folio-media-metadata', className)} data-photo-specs {...rest} />\n}\n\nexport interface MediaPagerProps extends HTMLAttributes<HTMLElement> {\n label: string\n previous?: ReactNode\n next?: ReactNode\n}\n\nexport function MediaPager({ label, previous, next, className, ...rest }: MediaPagerProps) {\n if (!previous && !next) return null\n return <nav aria-label={label} className={cn('folio-media-pager', className)} data-photo-pager {...rest}>{previous || <span />}{next || <span />}</nav>\n}\n\nexport interface MediaPagerItemProps {\n link: ReactElement\n direction: 'previous' | 'next'\n label: ReactNode\n title: ReactNode\n image?: ReactNode\n}\n\nexport function MediaPagerItem({ link, direction, label, title, image }: MediaPagerItemProps) {\n return (\n <Slot className=\"folio-media-pager-link\" data-direction={direction}>\n {/** Slot composes the host link while retaining its routing behavior. */}\n {replaceSlotContent(link, <>\n {image && direction === 'previous' && <span className=\"folio-media-pager-image\">{image}</span>}\n <span className=\"folio-media-pager-copy\">\n <span className=\"folio-media-pager-label\">{label}</span>{' '}\n <strong className=\"folio-media-pager-title\">{title}</strong>\n </span>\n {image && direction === 'next' && <span className=\"folio-media-pager-image\">{image}</span>}\n </>)}\n </Slot>\n )\n}\n\nexport interface MediaThumbnailStripProps extends HTMLAttributes<HTMLElement> {\n heading: ReactNode\n}\n\nexport function MediaThumbnailStrip({ heading, children, className, ...rest }: MediaThumbnailStripProps) {\n return <section className={cn('folio-media-thumbnails', className)} data-photo-related {...rest}><h2>{heading}</h2><div className=\"folio-media-thumbnail-list\">{children}</div></section>\n}\n\nexport interface MediaThumbnailProps {\n children: ReactElement\n}\n\nexport function MediaThumbnail({ children }: MediaThumbnailProps) {\n return <Slot className=\"folio-media-thumbnail\">{children}</Slot>\n}\n\nexport interface MediaLightNoteLine {\n id: string\n icon?: ReactNode\n text: ReactNode\n detail?: ReactNode\n}\n\nexport interface MediaLightNoteProps {\n label: ReactNode\n lines: MediaLightNoteLine[]\n}\n\n/** The host supplies observations and formatted labels; this is presentation only. */\nexport function MediaLightNote({ label, lines }: MediaLightNoteProps) {\n return <div className=\"folio-media-light\"><Badge tone=\"outline\">{label}</Badge>{lines.map((line) => <div className=\"folio-media-light-line\" key={line.id}>{line.icon}<span>{line.text}</span>{line.detail && <span className=\"folio-media-light-detail\">{line.detail}</span>}</div>)}</div>\n}\n\nexport interface MediaMapSectionProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {\n title: ReactNode\n hint?: ReactNode\n /** An optional legacy anchor, owned and named by the host. */\n aliasId?: string\n map: ReactNode\n preview: ReactNode\n}\n\nexport function MediaMapSection({ title, hint, aliasId, map, preview, className, ...rest }: MediaMapSectionProps) {\n return <section className={cn('folio-media-map-section', className)} {...rest}><div className=\"folio-media-container\">{aliasId && <span id={aliasId} className=\"folio-media-anchor\" aria-hidden=\"true\" />}<header><h2>{title}</h2>{hint && <p>{hint}</p>}</header><div className=\"folio-media-map-grid\">{map}{preview}</div></div></section>\n}\n\nexport interface MediaMapFrameProps extends HTMLAttributes<HTMLDivElement> {\n label?: ReactNode\n hint?: ReactNode\n navigation?: ReactNode\n compact?: boolean\n}\n\nexport function MediaMapFrame({ label, hint, navigation, compact = false, children, className, ...rest }: MediaMapFrameProps) {\n return <div className={cn('folio-media-map-frame', compact && 'folio-media-map-frame-compact', className)} {...rest}>{label && <span className=\"folio-media-map-label\">{label}</span>}<div className=\"folio-media-map-stage\">{children}{hint && <div className=\"folio-media-map-hint\">{hint}</div>}{navigation && <div className=\"folio-media-map-navigation\">{navigation}</div>}</div></div>\n}\n\nexport interface MediaMapNavigationProps {\n zoomInLabel: string\n zoomOutLabel: string\n onZoomIn: () => void\n onZoomOut: () => void\n}\n\nexport function MediaMapNavigation({ zoomInLabel, zoomOutLabel, onZoomIn, onZoomOut }: MediaMapNavigationProps) {\n return <><Button variant=\"secondary\" iconOnly aria-label={zoomInLabel} onClick={onZoomIn}><RiAddLine size={18} aria-hidden=\"true\" /></Button><Button variant=\"secondary\" iconOnly aria-label={zoomOutLabel} onClick={onZoomOut}><RiSubtractLine size={18} aria-hidden=\"true\" /></Button></>\n}\n\nexport type MediaMapCanvasProps = ComponentPropsWithRef<'div'>\n\n/** A sized provider container. Map creation, themes and geographic math stay in the host. */\nexport function MediaMapCanvas({ className, ...rest }: MediaMapCanvasProps) {\n return <div className={cn('folio-media-map-canvas', className)} {...rest} />\n}\n\nexport interface MediaMapPreviewProps {\n title: ReactNode\n image?: ReactNode\n action?: ReactNode\n place?: ReactNode\n coordinates?: ReactNode\n children?: ReactNode\n}\n\nexport function MediaMapPreview({ title, image, action, place, coordinates, children }: MediaMapPreviewProps) {\n return <aside className=\"folio-media-map-preview\"><div className=\"folio-media-map-preview-image\">{image}{action && <div className=\"folio-media-map-preview-action\">{action}</div>}</div><div className=\"folio-media-map-preview-copy\"><h3>{title}</h3>{place && <p className=\"folio-media-map-place\">{place}</p>}{coordinates && <div className=\"folio-media-map-coordinates\">{coordinates}</div>}{children}</div></aside>\n}\n\nexport interface MediaMapArea {\n id: string\n label: ReactNode\n count: ReactNode\n}\n\nexport interface MediaMapAreaListProps {\n items: MediaMapArea[]\n value?: string\n onValueChange: (value: string) => void\n}\n\nexport function MediaMapAreaList({ items, value, onValueChange }: MediaMapAreaListProps) {\n return (\n <div className=\"folio-media-map-areas\">\n {items.map((item) => (\n <Button key={item.id} variant=\"ghost\" aria-pressed={value === item.id} onClick={() => onValueChange(item.id)} className=\"folio-media-map-area\">\n <span className=\"folio-media-map-area-name\"><span aria-hidden=\"true\" />{item.label}</span>{' '}\n <span className=\"folio-media-map-area-count\">{item.count}</span>\n </Button>\n ))}\n </div>\n )\n}\n\nexport interface MediaMapMarkerProps extends Omit<Extract<ButtonProps, { href?: undefined }>, 'children' | 'iconOnly'> {\n label: string\n active?: boolean\n}\n\n/** A keyboard-operable 44px marker for a host's map-provider portal. */\nexport function MediaMapMarker({ label, active = false, className, ...rest }: MediaMapMarkerProps) {\n return <Button {...rest} variant=\"ghost\" iconOnly aria-label={label} aria-pressed={active} className={cn('folio-media-map-marker', className)} style={{ width: 44, height: 44, ...rest.style }}><MediaMapPin active={active} /></Button>\n}\n\nexport interface MediaMapPinProps {\n active?: boolean\n}\n\nexport function MediaMapPin({ active = false }: MediaMapPinProps) {\n return <span className=\"folio-media-map-pin\" data-active={active || undefined} aria-hidden=\"true\"><span /></span>\n}\n\nexport function MediaMapSkeleton() {\n return <div className=\"folio-media-map-skeleton folio-media-container\" aria-hidden=\"true\"><SkeletonBlock className=\"folio-media-map-canvas\" /></div>\n}\n"],"mappings":"AAwBI,SAgN4B,UA/Md,KADd;AAxBJ,SAAS,YAAY;AACrB,SAAS,WAAW,sBAAsB;AAE1C,SAAS,aAAa;AACtB,SAAS,cAAgC;AACzC,SAAS,uBAAiD;AAC1D,SAAS,kBAAkB;AAC3B,SAAS,eAAe,cAAc,oBAAoB;AAC1D,SAAS,UAAU;AACnB,SAAS,0BAA0B;AAW5B,SAAS,cAAc,EAAE,SAAS,OAAO,aAAa,eAAe,GAAG,WAAW,GAAG,KAAK,GAAuB;AACvH,QAAM,UAAU,IAAI,YAAY;AAEhC,SACE,qBAAC,YAAO,WAAW,GAAG,wBAAwB,SAAS,GAAI,GAAG,MAC3D;AAAA,eAAW,oBAAC,OAAE,WAAU,uBAAuB,mBAAQ;AAAA,IACxD,oBAAC,WAAQ,WAAU,8BAA8B,iBAAM;AAAA,IACtD,eAAe,oBAAC,OAAE,WAAU,2BAA2B,uBAAY;AAAA,KACtE;AAEJ;AAYO,SAAS,kBAAkB,EAAE,OAAO,OAAO,SAAS,aAAa,SAAS,QAAQ,WAAW,GAAG,KAAK,GAA2B;AACrI,SACE,qBAAC,aAAQ,WAAW,GAAG,4BAA4B,SAAS,GAAI,GAAG,MACjE;AAAA,yBAAC,SAAI,WAAU,iCACZ;AAAA,iBAAW,oBAAC,OAAE,WAAU,uBAAuB,mBAAQ;AAAA,MACxD,oBAAC,QAAG,WAAU,kCAAkC,iBAAM;AAAA,MACrD,eAAe,oBAAC,SAAI,WAAU,2BAA2B,uBAAY;AAAA,MACrE,UAAU,oBAAC,SAAI,WAAU,mCAAmC,kBAAO;AAAA,OACtE;AAAA,IACA,qBAAC,YAAO,WAAU,mCAChB;AAAA,0BAAC,SAAI,WAAU,kCAAkC,iBAAM;AAAA,MACtD,WAAW,oBAAC,gBAAW,WAAU,oCAAoC,mBAAQ;AAAA,OAChF;AAAA,KACF;AAEJ;AAUO,SAAS,gBAAgB,EAAE,UAAU,UAAU,cAAc,QAAQ,UAAU,WAAW,GAAG,KAAK,GAAyB;AAChI,SACE,qBAAC,SAAI,WAAW,GAAG,4BAA4B,SAAS,GAAI,GAAG,MAC5D;AAAA;AAAA,IACA,YAAY,oBAAC,SAAI,WAAU,yBAAyB,oBAAS;AAAA,IAC9D,oBAAC,aAAQ,IAAI,cAAc,WAAU,+BAA+B,UAAS;AAAA,IAC5E;AAAA,KACH;AAEJ;AAUO,SAAS,aAAa,EAAE,UAAU,YAAY,aAAa,WAAW,GAAG,KAAK,GAAsB;AACzG,SACE,oBAAC,SAAI,WAAU,yBACZ,uBAAa,oBAAC,cAAW,OAAO,YAAY,QAAQ,aAAa,IAChE,oBAAC,QAAG,MAAK,QAAO,WAAW,GAAG,uBAAuB,SAAS,GAAI,GAAG,MAAO,UAAS,GAEzF;AAEJ;AAWO,SAAS,iBAAiB,EAAE,UAAU,OAAO,OAAO,UAAU,aAAa,WAAW,GAAG,KAAK,GAA0B;AAC7H,SACE,oBAAC,QAAG,WAAW,GAAG,4BAA4B,SAAS,GAAI,GAAG,MAC5D,+BAAC,YACC;AAAA,wBAAC,QAAK,WAAU,4BAA2B,0BAAwB,aAAc,UAAS;AAAA,IAC1F,qBAAC,gBAAW,WAAU,+BACnB;AAAA,eAAS,QAAQ,oBAAC,UAAK,WAAU,6BAA4B,eAAY,QAAQ,iBAAM;AAAA,MACxF,oBAAC,UAAK,WAAU,6BAA6B,iBAAM;AAAA,MAClD,YAAY,oBAAC,UAAK,WAAU,gCAAgC,oBAAS;AAAA,OACxE;AAAA,KACF,GACF;AAEJ;AAOO,SAAS,qBAAqB,EAAE,OAAO,QAAQ,EAAE,GAA8B;AACpF,SACE,oBAAC,SAAI,WAAU,sBACb,+BAAC,gBAAa,OAAc,cAAY,OACtC;AAAA,yBAAC,YAAO,WAAU,wBAChB;AAAA,0BAAC,gBAAa,WAAU,QAAO;AAAA,MAC/B,oBAAC,iBAAc,WAAU,+BAA8B;AAAA,MACvD,oBAAC,iBAAc,WAAU,iCAAgC;AAAA,OAC3D;AAAA,IACA,oBAAC,SAAI,WAAU,yBAAwB,8BAAC,gBAAa,WAAU,eAAc,GAAE;AAAA,IAC/E,oBAAC,aAAQ,wBAAoB,MAAC,WAAU,+BACtC,8BAAC,gBACE,gBAAM,KAAK,EAAE,QAAQ,MAAM,GAAG,CAAC,GAAG,UACjC,qBAAC,QACC;AAAA,0BAAC,iBAAc,WAAU,6BAA4B;AAAA,MACrD,oBAAC,gBAAa,WAAU,cAAa;AAAA,SAF9B,KAGT,CACD,GACH,GACF;AAAA,KACF,GACF;AAEJ;AAsBO,SAAS,kBAAkB,EAAE,OAAO,SAAS,eAAe,GAAG,UAAU,YAAY,aAAa,UAAU,OAAO,OAAO,OAAO,SAAS,UAAU,UAAU,OAAO,SAAS,WAAW,GAAG,KAAK,GAA2B;AACjO,QAAM,UAAU,IAAI,YAAY;AAEhC,SACE,oBAAC,SAAI,WAAU,2BACb,+BAAC,aAAQ,WAAW,GAAG,sBAAsB,SAAS,GAAG,mBAAiB,SAAS,qBAAiB,MAAE,GAAG,MACvG;AAAA,yBAAC,YAAO,WAAU,yBAChB;AAAA,0BAAC,UAAO,SAAO,MAAC,SAAQ,SAAS,oBAAS;AAAA,MACzC,SAAS,oBAAC,UAAK,WAAU,qBAAqB,iBAAM;AAAA,OACvD;AAAA,IACA,qBAAC,SAAI,WAAU,sBAAqB,qBAAmB,gBAAgB,cAAc,SAAS,QAC5F;AAAA,0BAAC,SAAI,WAAU,oBAAmB,oBAAgB,MAAE,iBAAM;AAAA,MAC1D,qBAAC,WAAM,WAAU,sBAAqB,cAAY,YAAY,qBAAiB,MAC7E;AAAA,6BAAC,SAAI,WAAU,gCACb;AAAA,+BAAC,SAAI,WAAU,2BACZ;AAAA,wBAAY,oBAAC,OAAE,WAAU,uBAAuB,oBAAS;AAAA,YAC1D,oBAAC,WAAQ,IAAI,SAAS,WAAU,4BAA4B,iBAAM;AAAA,aACpE;AAAA,UACC;AAAA,UACA,WAAW,oBAAC,SAAI,WAAU,uBAAsB,sBAAkB,MAAE,mBAAQ;AAAA,WAC/E;AAAA,QACA,qBAAC,SAAI,WAAU,2BAA2B;AAAA;AAAA,UAAU;AAAA,WAAS;AAAA,SAC/D;AAAA,OACF;AAAA,IACC;AAAA,IACA;AAAA,KACH,GACF;AAEJ;AAMO,SAAS,cAAc,EAAE,OAAO,WAAW,GAAG,KAAK,GAAuB;AAC/E,SAAO,oBAAC,mBAAgB,OAAc,WAAW,GAAG,wBAAwB,SAAS,GAAG,oBAAgB,MAAE,GAAG,MAAM;AACrH;AAQO,SAAS,WAAW,EAAE,OAAO,UAAU,MAAM,WAAW,GAAG,KAAK,GAAoB;AACzF,MAAI,CAAC,YAAY,CAAC,KAAM,QAAO;AAC/B,SAAO,qBAAC,SAAI,cAAY,OAAO,WAAW,GAAG,qBAAqB,SAAS,GAAG,oBAAgB,MAAE,GAAG,MAAO;AAAA,gBAAY,oBAAC,UAAK;AAAA,IAAI,QAAQ,oBAAC,UAAK;AAAA,KAAG;AACnJ;AAUO,SAAS,eAAe,EAAE,MAAM,WAAW,OAAO,OAAO,MAAM,GAAwB;AAC5F,SACE,oBAAC,QAAK,WAAU,0BAAyB,kBAAgB,WAEtD,6BAAmB,MAAM,iCACvB;AAAA,aAAS,cAAc,cAAc,oBAAC,UAAK,WAAU,2BAA2B,iBAAM;AAAA,IACvF,qBAAC,UAAK,WAAU,0BACd;AAAA,0BAAC,UAAK,WAAU,2BAA2B,iBAAM;AAAA,MAAQ;AAAA,MACzD,oBAAC,YAAO,WAAU,2BAA2B,iBAAM;AAAA,OACrD;AAAA,IACC,SAAS,cAAc,UAAU,oBAAC,UAAK,WAAU,2BAA2B,iBAAM;AAAA,KACrF,CAAG,GACL;AAEJ;AAMO,SAAS,oBAAoB,EAAE,SAAS,UAAU,WAAW,GAAG,KAAK,GAA6B;AACvG,SAAO,qBAAC,aAAQ,WAAW,GAAG,0BAA0B,SAAS,GAAG,sBAAkB,MAAE,GAAG,MAAM;AAAA,wBAAC,QAAI,mBAAQ;AAAA,IAAK,oBAAC,SAAI,WAAU,8BAA8B,UAAS;AAAA,KAAM;AACjL;AAMO,SAAS,eAAe,EAAE,SAAS,GAAwB;AAChE,SAAO,oBAAC,QAAK,WAAU,yBAAyB,UAAS;AAC3D;AAeO,SAAS,eAAe,EAAE,OAAO,MAAM,GAAwB;AACpE,SAAO,qBAAC,SAAI,WAAU,qBAAoB;AAAA,wBAAC,SAAM,MAAK,WAAW,iBAAM;AAAA,IAAS,MAAM,IAAI,CAAC,SAAS,qBAAC,SAAI,WAAU,0BAAwC;AAAA,WAAK;AAAA,MAAK,oBAAC,UAAM,eAAK,MAAK;AAAA,MAAQ,KAAK,UAAU,oBAAC,UAAK,WAAU,4BAA4B,eAAK,QAAO;AAAA,SAApH,KAAK,EAAuH,CAAM;AAAA,KAAE;AACvR;AAWO,SAAS,gBAAgB,EAAE,OAAO,MAAM,SAAS,KAAK,SAAS,WAAW,GAAG,KAAK,GAAyB;AAChH,SAAO,oBAAC,aAAQ,WAAW,GAAG,2BAA2B,SAAS,GAAI,GAAG,MAAM,+BAAC,SAAI,WAAU,yBAAyB;AAAA,eAAW,oBAAC,UAAK,IAAI,SAAS,WAAU,sBAAqB,eAAY,QAAO;AAAA,IAAG,qBAAC,YAAO;AAAA,0BAAC,QAAI,iBAAM;AAAA,MAAM,QAAQ,oBAAC,OAAG,gBAAK;AAAA,OAAK;AAAA,IAAS,qBAAC,SAAI,WAAU,wBAAwB;AAAA;AAAA,MAAK;AAAA,OAAQ;AAAA,KAAM,GAAM;AACpU;AASO,SAAS,cAAc,EAAE,OAAO,MAAM,YAAY,UAAU,OAAO,UAAU,WAAW,GAAG,KAAK,GAAuB;AAC5H,SAAO,qBAAC,SAAI,WAAW,GAAG,yBAAyB,WAAW,iCAAiC,SAAS,GAAI,GAAG,MAAO;AAAA,aAAS,oBAAC,UAAK,WAAU,yBAAyB,iBAAM;AAAA,IAAQ,qBAAC,SAAI,WAAU,yBAAyB;AAAA;AAAA,MAAU,QAAQ,oBAAC,SAAI,WAAU,wBAAwB,gBAAK;AAAA,MAAQ,cAAc,oBAAC,SAAI,WAAU,8BAA8B,sBAAW;AAAA,OAAO;AAAA,KAAM;AACzX;AASO,SAAS,mBAAmB,EAAE,aAAa,cAAc,UAAU,UAAU,GAA4B;AAC9G,SAAO,iCAAE;AAAA,wBAAC,UAAO,SAAQ,aAAY,UAAQ,MAAC,cAAY,aAAa,SAAS,UAAU,8BAAC,aAAU,MAAM,IAAI,eAAY,QAAO,GAAE;AAAA,IAAS,oBAAC,UAAO,SAAQ,aAAY,UAAQ,MAAC,cAAY,cAAc,SAAS,WAAW,8BAAC,kBAAe,MAAM,IAAI,eAAY,QAAO,GAAE;AAAA,KAAS;AAC1R;AAKO,SAAS,eAAe,EAAE,WAAW,GAAG,KAAK,GAAwB;AAC1E,SAAO,oBAAC,SAAI,WAAW,GAAG,0BAA0B,SAAS,GAAI,GAAG,MAAM;AAC5E;AAWO,SAAS,gBAAgB,EAAE,OAAO,OAAO,QAAQ,OAAO,aAAa,SAAS,GAAyB;AAC5G,SAAO,qBAAC,WAAM,WAAU,2BAA0B;AAAA,yBAAC,SAAI,WAAU,iCAAiC;AAAA;AAAA,MAAO,UAAU,oBAAC,SAAI,WAAU,kCAAkC,kBAAO;AAAA,OAAO;AAAA,IAAM,qBAAC,SAAI,WAAU,gCAA+B;AAAA,0BAAC,QAAI,iBAAM;AAAA,MAAM,SAAS,oBAAC,OAAE,WAAU,yBAAyB,iBAAM;AAAA,MAAM,eAAe,oBAAC,SAAI,WAAU,+BAA+B,uBAAY;AAAA,MAAQ;AAAA,OAAS;AAAA,KAAM;AACpZ;AAcO,SAAS,iBAAiB,EAAE,OAAO,OAAO,cAAc,GAA0B;AACvF,SACE,oBAAC,SAAI,WAAU,yBACZ,gBAAM,IAAI,CAAC,SACV,qBAAC,UAAqB,SAAQ,SAAQ,gBAAc,UAAU,KAAK,IAAI,SAAS,MAAM,cAAc,KAAK,EAAE,GAAG,WAAU,wBACtH;AAAA,yBAAC,UAAK,WAAU,6BAA4B;AAAA,0BAAC,UAAK,eAAY,QAAO;AAAA,MAAG,KAAK;AAAA,OAAM;AAAA,IAAQ;AAAA,IAC3F,oBAAC,UAAK,WAAU,8BAA8B,eAAK,OAAM;AAAA,OAF9C,KAAK,EAGlB,CACD,GACH;AAEJ;AAQO,SAAS,eAAe,EAAE,OAAO,SAAS,OAAO,WAAW,GAAG,KAAK,GAAwB;AACjG,SAAO,oBAAC,UAAQ,GAAG,MAAM,SAAQ,SAAQ,UAAQ,MAAC,cAAY,OAAO,gBAAc,QAAQ,WAAW,GAAG,0BAA0B,SAAS,GAAG,OAAO,EAAE,OAAO,IAAI,QAAQ,IAAI,GAAG,KAAK,MAAM,GAAG,8BAAC,eAAY,QAAgB,GAAE;AACjO;AAMO,SAAS,YAAY,EAAE,SAAS,MAAM,GAAqB;AAChE,SAAO,oBAAC,UAAK,WAAU,uBAAsB,eAAa,UAAU,QAAW,eAAY,QAAO,8BAAC,UAAK,GAAE;AAC5G;AAEO,SAAS,mBAAmB;AACjC,SAAO,oBAAC,SAAI,WAAU,kDAAiD,eAAY,QAAO,8BAAC,iBAAc,WAAU,0BAAyB,GAAE;AAChJ;","names":[]}
@@ -128,6 +128,12 @@ interface PreviewPairProps {
128
128
  desktop: ReactNode;
129
129
  mobile?: ReactNode;
130
130
  }
131
+ interface DeviceRowProps {
132
+ children: ReactNode;
133
+ label?: string;
134
+ }
135
+ /** Two or three phones side by side, the middle one raised a step, wrapping to a column on a narrow screen. */
136
+ declare function DeviceRow({ children, label }: DeviceRowProps): react.JSX.Element;
131
137
  declare function PreviewPair({ desktop, mobile }: PreviewPairProps): react.JSX.Element;
132
138
  interface CommandTab {
133
139
  id: string;
@@ -200,4 +206,4 @@ interface CommandSnippetProps {
200
206
  }
201
207
  declare function CommandSnippet({ code, pieces, copyLabel, copiedLabel, className }: CommandSnippetProps): react.JSX.Element;
202
208
 
203
- export { ActionBand, type ActionBandProps, type ArchitectureColumnNode, ArchitectureColumns, type ArchitectureColumnsProps, AuthorPanel, type AuthorPanelProps, type CommandPiece, CommandSnippet, type CommandSnippetProps, type CommandTab, CommandTabs, type CommandTabsProps, ContentFigure, type ContentFigureProps, DetailMasthead, type DetailMastheadProps, DevicePreview, type DevicePreviewProps, FeaturePairs, type FeaturePairsProps, MediaPlayback, type MediaPlaybackProps, MobileOutline, type MobileOutlineProps, type OutlineItem, PreviewPair, type PreviewPairProps, ReadingFigure, type ReadingFigureProps, ReadingForm, type ReadingFormProps, ReadingLayout, type ReadingLayoutProps, ReadingLead, type ReadingLeadProps, type ReadingMetadataItem, ReadingPage, type ReadingPageProps, type ReadingPagerItem, ReadingProgress, ReadingSection, type ReadingSectionProps, ReadingSurface, type ReadingSurfaceProps, RecordPager, type RecordPagerProps, TableOfContents, type TableOfContentsProps, TokenLine, type TokenLineProps, isSelfNumbered, numberToc };
209
+ export { ActionBand, type ActionBandProps, type ArchitectureColumnNode, ArchitectureColumns, type ArchitectureColumnsProps, AuthorPanel, type AuthorPanelProps, type CommandPiece, CommandSnippet, type CommandSnippetProps, type CommandTab, CommandTabs, type CommandTabsProps, ContentFigure, type ContentFigureProps, DetailMasthead, type DetailMastheadProps, DevicePreview, type DevicePreviewProps, DeviceRow, type DeviceRowProps, FeaturePairs, type FeaturePairsProps, MediaPlayback, type MediaPlaybackProps, MobileOutline, type MobileOutlineProps, type OutlineItem, PreviewPair, type PreviewPairProps, ReadingFigure, type ReadingFigureProps, ReadingForm, type ReadingFormProps, ReadingLayout, type ReadingLayoutProps, ReadingLead, type ReadingLeadProps, type ReadingMetadataItem, ReadingPage, type ReadingPageProps, type ReadingPagerItem, ReadingProgress, ReadingSection, type ReadingSectionProps, ReadingSurface, type ReadingSurfaceProps, RecordPager, type RecordPagerProps, TableOfContents, type TableOfContentsProps, TokenLine, type TokenLineProps, isSelfNumbered, numberToc };
@@ -259,6 +259,9 @@ function DevicePreview({ kind, label, media, caption }) {
259
259
  caption && /* @__PURE__ */ jsx("figcaption", { children: caption })
260
260
  ] });
261
261
  }
262
+ function DeviceRow({ children, label }) {
263
+ return /* @__PURE__ */ jsx("div", { className: "folio-device-row", role: label ? "group" : void 0, "aria-label": label, children });
264
+ }
262
265
  function PreviewPair({ desktop, mobile }) {
263
266
  return /* @__PURE__ */ jsxs("div", { className: "folio-preview-pair", "data-mobile": Boolean(mobile) || void 0, children: [
264
267
  desktop,
@@ -336,6 +339,7 @@ export {
336
339
  ContentFigure,
337
340
  DetailMasthead,
338
341
  DevicePreview,
342
+ DeviceRow,
339
343
  FeaturePairs,
340
344
  MediaPlayback,
341
345
  MobileOutline,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/website/Reading/Reading.tsx"],"sourcesContent":["'use client'\n\nimport { useEffect, useRef, useState, type HTMLAttributes, type ReactNode } from 'react'\nimport { Slot } from '@radix-ui/react-slot'\nimport { RiListUnordered, RiPlayFill, RiPauseFill } from '@remixicon/react'\nimport { Button } from '../../components/Button/Button'\nimport { StatusDot } from '../../components/StatusDot/StatusDot'\nimport { Sheet, SheetContent, SheetTrigger } from '../../components/Sheet/Sheet'\nimport { CodeBlock } from '../../components/CodeBlock/CodeBlock'\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '../../components/Tabs/Tabs'\nimport { cn } from '../../lib/cn'\nimport { replaceSlotContent } from '../../lib/slot-content'\nimport type { CollectionLink } from '../Collection/Collection'\n\nexport interface ReadingMetadataItem { label: string; value: ReactNode; live?: boolean; dateTime?: string }\nexport interface DetailMastheadProps { backLink?: ReactNode; title: string; dek?: ReactNode; context?: ReactNode; aside?: ReactNode; actions?: ReactNode; metadata?: ReadingMetadataItem[]; width?: 'page' | 'fill' }\nexport function DetailMasthead({ backLink, title, dek, context, aside, actions, metadata = [], width = 'page' }: DetailMastheadProps) {\n const stacked = title.length <= 28 || typeof dek !== 'string' || dek.length > 280 || Boolean(aside)\n return <header className=\"folio-detail-masthead\" data-width={width} data-stacked={stacked || undefined} data-display={title.length <= 16 || undefined}>\n {backLink && <div className=\"folio-detail-back\">{backLink}</div>}{context && <p className=\"folio-detail-context\">{context}</p>}\n <div className=\"folio-detail-opening\" data-aside={Boolean(aside) || undefined} data-stacked={stacked || undefined}>{aside ? <div className=\"folio-detail-copy\"><h1>{title}</h1>{dek && <p className=\"folio-detail-dek\">{dek}</p>}</div> : <><h1>{title}</h1>{dek && <p className=\"folio-detail-dek\">{dek}</p>}</>}{aside && <aside>{aside}</aside>}</div>\n {(metadata.length > 0 || actions) && <div className=\"folio-detail-metabar\"><dl>{metadata.map((item) => <div key={item.label}><dt>{item.label}</dt><dd>{item.live && <StatusDot size=\"sm\" pulse={false} />}{item.dateTime ? <time dateTime={item.dateTime}>{item.value}</time> : item.value}</dd></div>)}</dl>{actions && <div className=\"folio-detail-actions\">{actions}</div>}</div>}\n </header>\n}\nexport interface ReadingLayoutProps extends HTMLAttributes<HTMLDivElement> { outline?: ReactNode }\nexport function ReadingLayout({ children, outline, className, ...rest }: ReadingLayoutProps) { return <div className={cn('folio-reading-layout', className)} {...rest}><div className=\"folio-reading-body\">{children}</div>{outline}</div> }\nexport interface ReadingSectionProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> { title?: ReactNode; caption?: ReactNode; lead?: ReactNode; compact?: boolean; seamless?: boolean }\n/** `seamless` lets a structured child continue directly from the section rule without drawing a second seam. */\nexport function ReadingSection({ title, caption, lead, compact, seamless, children, className, ...rest }: ReadingSectionProps) {\n return <section className={cn('folio-reading-section', className)} data-compact={compact || undefined} data-seamless={seamless || undefined} {...rest}>{(title || caption) && <div className=\"folio-reading-heading\"><h2>{title}</h2>{caption && <span>{caption}</span>}</div>}{lead && <p className=\"folio-reading-lead\">{lead}</p>}{children}</section>\n}\nexport interface ReadingLeadProps { children: ReactNode; muted?: ReactNode; description?: ReactNode }\nexport function ReadingLead({ children, muted, description }: ReadingLeadProps) { return <div className=\"folio-reading-lede\"><p>{children}{muted && <span> {muted}</span>}</p>{description && <p>{description}</p>}</div> }\nexport interface ReadingPagerItem { link: CollectionLink; direction: ReactNode; title: ReactNode; media?: ReactNode }\nexport interface RecordPagerProps { label: string; previous?: ReadingPagerItem; next?: ReadingPagerItem }\nexport function RecordPager({ label, previous, next }: RecordPagerProps) {\n if (!previous && !next) return null\n const cell = (item: ReadingPagerItem | undefined, direction: 'prev' | 'next') => item ? <Slot className=\"folio-record-pager-link\" data-direction={direction}>{replaceSlotContent(item.link, <>{item.media && <span className=\"folio-record-pager-media\">{item.media}</span>}<span><span className=\"folio-record-pager-direction\">{item.direction}</span>{' '}<strong>{item.title}</strong></span></>)}</Slot> : <span />\n return <nav className=\"folio-record-pager\" aria-label={label}>{cell(previous, 'prev')}{cell(next, 'next')}</nav>\n}\nexport interface OutlineItem { id: string; label: string; level: number }\nconst SELF_NUMBERED = /^\\s*\\d+[a-z]?\\s*[.)]\\s+/i\nexport function isSelfNumbered(items: OutlineItem[]): boolean {\n if (!items.length) return false\n const top = Math.min(...items.map((item) => item.level))\n const headings = items.filter((item) => item.level === top)\n return headings.length >= 2 && headings.filter((item) => SELF_NUMBERED.test(item.label)).length * 2 > headings.length\n}\nexport function numberToc(items: OutlineItem[]): string[] {\n const top = items.length ? Math.min(...items.map((item) => item.level)) : 0\n let major = 0; let minor = 0\n return items.map((item) => item.level <= top ? (minor = 0, String(++major)) : `${major}.${++minor}`)\n}\nexport interface TableOfContentsProps { items: OutlineItem[]; label: string; numbering?: 'auto' | 'none'; onNavigate?: (id: string) => void; expanded?: boolean }\nexport function TableOfContents({ items, label, numbering = 'auto', onNavigate, expanded = false }: TableOfContentsProps) {\n const [activeId, setActiveId] = useState(items[0]?.id ?? '')\n const pinnedId = useRef<string | null>(null)\n const releaseTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const numbers = numberToc(items)\n const showNumbers = numbering === 'auto' && !isSelfNumbered(items)\n const top = items.length ? Math.min(...items.map((item) => item.level)) : 0\n useEffect(() => () => clearTimeout(releaseTimer.current), [])\n useEffect(() => {\n const sections = items.map((item) => document.getElementById(item.id)).filter((item): item is HTMLElement => Boolean(item))\n const first = sections[0]\n if (!first) return\n const update = () => {\n let current = first\n for (const section of sections) if (section.getBoundingClientRect().top <= window.innerHeight * .28) current = section\n if (pinnedId.current && current.id !== pinnedId.current) return\n pinnedId.current = null\n setActiveId(current.id)\n }\n window.addEventListener('scroll', update, { passive: true }); window.addEventListener('resize', update); update()\n return () => { window.removeEventListener('scroll', update); window.removeEventListener('resize', update) }\n }, [items])\n const groups: { item: OutlineItem; index: number; subs: { item: OutlineItem; index: number }[] }[] = []\n items.forEach((item, index) => { const last = groups.at(-1); if (item.level <= top || !last) groups.push({ item, index, subs: [] }); else last.subs.push({ item, index }) })\n if (!items.length) return null\n const link = (item: OutlineItem, index: number, isOpen?: boolean) => <a href={`#${item.id}`} data-toc-id={item.id} aria-current={activeId === item.id ? 'true' : undefined} aria-expanded={isOpen} onClick={(event) => {\n pinnedId.current = item.id; clearTimeout(releaseTimer.current); releaseTimer.current = setTimeout(() => { pinnedId.current = null }, 1200); setActiveId(item.id)\n if (onNavigate) { event.preventDefault(); onNavigate(item.id) }\n }}>{showNumbers && <span aria-hidden>{numbers[index]}</span>}{item.label}</a>\n return <nav className=\"folio-toc\" aria-label={label}><div><p>{label}</p><ul>{groups.map((group) => {\n const open = expanded || group.item.id === activeId || group.subs.some(({ item }) => item.id === activeId)\n return <li key={group.item.id}>{link(group.item, group.index, group.subs.length ? open : undefined)}{group.subs.length > 0 && open && <ul>{group.subs.map(({ item, index }) => <li key={item.id}>{link(item, index)}</li>)}</ul>}</li>\n })}</ul></div></nav>\n}\nexport interface MobileOutlineProps { items: OutlineItem[]; title: string; openLabel: string; closeLabel: string }\nexport function MobileOutline({ items, title, openLabel, closeLabel }: MobileOutlineProps) {\n const [open, setOpen] = useState(false)\n if (!items.length) return null\n return <Sheet open={open} onOpenChange={setOpen}><SheetTrigger asChild><Button variant=\"secondary\" iconOnly aria-label={openLabel} className=\"folio-outline-trigger\"><RiListUnordered size={18} aria-hidden=\"true\" /></Button></SheetTrigger><SheetContent title={title} closeLabel={closeLabel} side=\"bottom\" className=\"folio-outline-sheet\" aria-describedby={undefined}><TableOfContents items={items} label={title} expanded onNavigate={(id) => {\n setOpen(false)\n requestAnimationFrame(() => { const target = document.getElementById(id); if (target) { target.scrollIntoView({ behavior: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth', block: 'start' }); window.history.replaceState(null, '', `#${id}`) } })\n }} /></SheetContent></Sheet>\n}\nexport function ReadingProgress() {\n const [progress, setProgress] = useState(0)\n useEffect(() => {\n const update = () => { const doc = document.documentElement; const max = doc.scrollHeight - doc.clientHeight; setProgress(max > 0 ? Math.min(100, Math.max(0, doc.scrollTop / max * 100)) : 0) }\n update(); window.addEventListener('scroll', update, { passive: true }); window.addEventListener('resize', update)\n return () => { window.removeEventListener('scroll', update); window.removeEventListener('resize', update) }\n }, [])\n return <div className=\"folio-reading-progress\" aria-hidden><div style={{ width: `${progress}%` }} /></div>\n}\nexport interface AuthorPanelProps { avatar?: ReactNode; name: string; role?: ReactNode; bio?: ReactNode; links?: ReactNode }\nexport function AuthorPanel({ avatar, name, role, bio, links }: AuthorPanelProps) { return <aside className=\"folio-author-panel\">{avatar && <div className=\"folio-author-avatar\" aria-hidden>{avatar}</div>}<div><h2>{name}</h2>{role && <div className=\"folio-author-role\">{role}</div>}{bio && <p>{bio}</p>}{links && <div className=\"folio-author-links\">{links}</div>}</div></aside> }\nexport interface ReadingFigureProps { media: ReactNode; captionStart?: ReactNode; captionEnd?: ReactNode; width?: 'page' | 'fill'; ratio?: string; fit?: 'cover' | 'contain' }\n/** A reading image can preserve its source framing with `fit=\"contain\"`, rather than silently cropping it to a house ratio. */\nexport function ReadingFigure({ media, captionStart, captionEnd, width = 'page', ratio = '16 / 8', fit = 'cover' }: ReadingFigureProps) { return <figure className=\"folio-reading-figure\" data-width={width} data-fit={fit}><div style={{ aspectRatio: ratio }}>{media}</div>{(captionStart || captionEnd) && <figcaption><span>{captionStart}</span><span>{captionEnd}</span></figcaption>}</figure> }\nexport interface MediaPlaybackProps { src: string; poster?: string; label: string; playLabel: string; pauseLabel: string; fallback: ReactNode }\nexport function MediaPlayback({ src, poster, label, playLabel, pauseLabel, fallback }: MediaPlaybackProps) {\n const [reduceMotion, setReduceMotion] = useState(true)\n const [playing, setPlaying] = useState(false)\n const ref = useRef<HTMLVideoElement>(null)\n useEffect(() => { if (typeof window.matchMedia !== 'function') return; const query = window.matchMedia('(prefers-reduced-motion: reduce)'); const update = () => setReduceMotion(query.matches); update(); query.addEventListener('change', update); return () => query.removeEventListener('change', update) }, [])\n if (reduceMotion) return fallback\n return <div className=\"folio-media-playback\"><video ref={ref} src={src} poster={poster} aria-label={label} autoPlay muted loop playsInline preload=\"metadata\" onPlay={() => setPlaying(true)} onPause={() => setPlaying(false)} /><Button type=\"button\" variant=\"secondary\" iconOnly aria-label={playing ? pauseLabel : playLabel} onClick={() => { if (playing) ref.current?.pause(); else void ref.current?.play().catch(() => {}) }}>{playing ? <RiPauseFill size={18} aria-hidden /> : <RiPlayFill size={18} aria-hidden />}</Button></div>\n}\nexport interface ActionBandProps { title: ReactNode; description?: ReactNode; actions?: ReactNode; eyebrow?: ReactNode }\n/** A terminal project invitation with one strong entry action and quieter supporting actions. */\nexport function ActionBand({ title, description, actions, eyebrow }: ActionBandProps) { return <section className=\"folio-action-band\">{eyebrow && <span className=\"folio-action-band__eyebrow\">{eyebrow}</span>}<div className=\"folio-action-band__copy\"><h2>{title}</h2>{description && <p>{description}</p>}</div>{actions && <div className=\"folio-action-band__actions\">{actions}</div>}</section> }\nexport interface FeaturePairsProps { items: { id?: string; label?: ReactNode; title?: ReactNode; description: ReactNode }[] }\nexport function FeaturePairs({ items }: FeaturePairsProps) { return <div className=\"folio-feature-pairs\">{items.map((item, index) => <div key={item.id ?? index}>{item.label && <span>{item.label}</span>}{item.title && <h3>{item.title}</h3>}<div className=\"folio-feature-description\">{item.description}</div></div>)}</div> }\nexport interface DevicePreviewProps { kind: 'browser' | 'phone'; label?: string; media: ReactNode; caption?: ReactNode }\nexport function DevicePreview({ kind, label, media, caption }: DevicePreviewProps) { return <figure className=\"folio-device-preview\" data-kind={kind}><div>{kind === 'browser' && <div className=\"folio-browser-bar\"><span aria-hidden>● ● ●</span><span>{label}</span></div>}<div className=\"folio-device-screen\">{media}</div></div>{caption && <figcaption>{caption}</figcaption>}</figure> }\nexport interface PreviewPairProps { desktop: ReactNode; mobile?: ReactNode }\nexport function PreviewPair({ desktop, mobile }: PreviewPairProps) { return <div className=\"folio-preview-pair\" data-mobile={Boolean(mobile) || undefined}>{desktop}{mobile}</div> }\nexport interface CommandTab { id: string; name: string; commands: string[]; then?: ReactNode; note?: ReactNode; link?: ReactNode }\nexport interface CommandTabsProps { label: string; tabs: CommandTab[]; copyLabel: string; copiedLabel: string; footer?: ReactNode }\nexport function CommandTabs({ label, tabs, copyLabel, copiedLabel, footer }: CommandTabsProps) {\n const first = tabs[0]\n if (!first) return null\n return <div className=\"folio-command-tabs\"><Tabs defaultValue={first.id}><TabsList aria-label={label}>{tabs.map((tab) => <TabsTrigger value={tab.id} key={tab.id}>{tab.name}</TabsTrigger>)}</TabsList>{tabs.map((tab) => <TabsContent value={tab.id} key={tab.id}>{tab.commands.map((command) => <CodeBlock key={command} code={command} copyLabel={copyLabel} copiedLabel={copiedLabel} />)}{tab.then && <p>{tab.then}</p>}{tab.note && <p>{tab.note}</p>}{tab.link}</TabsContent>)}</Tabs>{footer && <div className=\"folio-command-footer\">{footer}</div>}</div>\n}\nexport interface ReadingSurfaceProps extends HTMLAttributes<HTMLDivElement> { numbered?: boolean }\nexport function ReadingSurface({ numbered, children, className, ...rest }: ReadingSurfaceProps) { return <div className={cn('folio-reading-surface', className)} data-folio-article data-numbered={numbered || undefined} {...rest}>{children}</div> }\nexport interface ReadingFormProps extends React.FormHTMLAttributes<HTMLFormElement> { inline?: boolean }\nexport function ReadingForm({ inline, className, ...props }: ReadingFormProps) { return <form className={cn('folio-reading-form', className)} data-inline={inline || undefined} {...props} /> }\nexport interface ArchitectureColumnNode { id: string; title: string; description?: string }\nexport interface ArchitectureColumnsProps { columns: { title: string; connected?: boolean; nodes: ArchitectureColumnNode[] }[]; foundation?: ArchitectureColumnNode[] }\nexport function ArchitectureColumns({ columns, foundation }: ArchitectureColumnsProps) {\n return <div className=\"folio-architecture-columns\"><div>{columns.map((column, index) => <div key={index} data-connected={Boolean(foundation?.length && column.connected) || undefined}><h3>{column.title}</h3>{column.nodes.map((node) => <div className=\"folio-architecture-node\" key={node.id}><strong>{node.title}</strong>{node.description && <span>{node.description}</span>}</div>)}</div>)}</div>{foundation && foundation.length > 0 && <div className=\"folio-architecture-foundation\">{foundation.map((node) => <div key={node.id}><strong>{node.title}</strong>{node.description && <span>{node.description}</span>}</div>)}</div>}</div>\n}\nexport interface TokenLineProps { items: { label: ReactNode; strong?: boolean; icon?: ReactNode }[]; label?: ReactNode; separator?: string }\nexport function TokenLine({ items, label, separator = '·' }: TokenLineProps) {\n return <div className=\"folio-token-line\" data-flow={separator !== '·' || undefined}>\n {label && <span>{label}</span>}\n <ul role=\"list\" aria-label={typeof label === 'string' ? label : undefined}>\n {items.map((item, index) => <li key={index}>\n {index > 0 && <span aria-hidden className=\"folio-token-separator\">{separator}</span>}\n {item.icon && <span className=\"folio-token-icon\" aria-hidden>{item.icon}</span>}{item.strong ? <strong>{item.label}</strong> : item.label}\n </li>)}\n </ul>\n </div>\n}\nexport interface ContentFigureProps { children: ReactNode; caption?: ReactNode; wide?: boolean }\nexport function ContentFigure({ children, caption, wide }: ContentFigureProps) { return <figure className=\"folio-content-figure\" data-wide={wide || undefined}>{children}{caption && <figcaption>{caption}</figcaption>}</figure> }\nexport interface ReadingPageProps extends HTMLAttributes<HTMLElement> { children: ReactNode }\nexport function ReadingPage({ children, className, ...props }: ReadingPageProps) { return <section className={cn('folio-reading-page', className)} {...props}>{children}</section> }\nexport interface CommandPiece { text: string; emphasis?: 'strong' | 'normal' | 'muted' }\nexport interface CommandSnippetProps { code: string; pieces?: CommandPiece[]; copyLabel: string; copiedLabel: string; className?: string }\nexport function CommandSnippet({ code, pieces, copyLabel, copiedLabel, className }: CommandSnippetProps) {\n const escape = (value: string) => value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;').replace(/'/g, '&#39;')\n // Only use the highlighting when it describes the exact source being copied.\n const highlighted = pieces?.map((piece) => piece.text).join('') === code ? pieces : undefined\n const html = highlighted ? `<pre><code>${highlighted.map((piece) => `<span class=\"folio-command-${piece.emphasis ?? 'normal'}\">${escape(piece.text)}</span>`).join('')}</code></pre>` : undefined\n return <div className={cn('folio-command-snippet', className)}><CodeBlock code={code} html={html} copyLabel={copyLabel} copiedLabel={copiedLabel} /></div>\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/website/Reading/Reading.tsx"],"sourcesContent":["'use client'\n\nimport { useEffect, useRef, useState, type HTMLAttributes, type ReactNode } from 'react'\nimport { Slot } from '@radix-ui/react-slot'\nimport { RiListUnordered, RiPlayFill, RiPauseFill } from '@remixicon/react'\nimport { Button } from '../../components/Button/Button'\nimport { StatusDot } from '../../components/StatusDot/StatusDot'\nimport { Sheet, SheetContent, SheetTrigger } from '../../components/Sheet/Sheet'\nimport { CodeBlock } from '../../components/CodeBlock/CodeBlock'\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '../../components/Tabs/Tabs'\nimport { cn } from '../../lib/cn'\nimport { replaceSlotContent } from '../../lib/slot-content'\nimport type { CollectionLink } from '../Collection/Collection'\n\nexport interface ReadingMetadataItem { label: string; value: ReactNode; live?: boolean; dateTime?: string }\nexport interface DetailMastheadProps { backLink?: ReactNode; title: string; dek?: ReactNode; context?: ReactNode; aside?: ReactNode; actions?: ReactNode; metadata?: ReadingMetadataItem[]; width?: 'page' | 'fill' }\nexport function DetailMasthead({ backLink, title, dek, context, aside, actions, metadata = [], width = 'page' }: DetailMastheadProps) {\n const stacked = title.length <= 28 || typeof dek !== 'string' || dek.length > 280 || Boolean(aside)\n return <header className=\"folio-detail-masthead\" data-width={width} data-stacked={stacked || undefined} data-display={title.length <= 16 || undefined}>\n {backLink && <div className=\"folio-detail-back\">{backLink}</div>}{context && <p className=\"folio-detail-context\">{context}</p>}\n <div className=\"folio-detail-opening\" data-aside={Boolean(aside) || undefined} data-stacked={stacked || undefined}>{aside ? <div className=\"folio-detail-copy\"><h1>{title}</h1>{dek && <p className=\"folio-detail-dek\">{dek}</p>}</div> : <><h1>{title}</h1>{dek && <p className=\"folio-detail-dek\">{dek}</p>}</>}{aside && <aside>{aside}</aside>}</div>\n {(metadata.length > 0 || actions) && <div className=\"folio-detail-metabar\"><dl>{metadata.map((item) => <div key={item.label}><dt>{item.label}</dt><dd>{item.live && <StatusDot size=\"sm\" pulse={false} />}{item.dateTime ? <time dateTime={item.dateTime}>{item.value}</time> : item.value}</dd></div>)}</dl>{actions && <div className=\"folio-detail-actions\">{actions}</div>}</div>}\n </header>\n}\nexport interface ReadingLayoutProps extends HTMLAttributes<HTMLDivElement> { outline?: ReactNode }\nexport function ReadingLayout({ children, outline, className, ...rest }: ReadingLayoutProps) { return <div className={cn('folio-reading-layout', className)} {...rest}><div className=\"folio-reading-body\">{children}</div>{outline}</div> }\nexport interface ReadingSectionProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> { title?: ReactNode; caption?: ReactNode; lead?: ReactNode; compact?: boolean; seamless?: boolean }\n/** `seamless` lets a structured child continue directly from the section rule without drawing a second seam. */\nexport function ReadingSection({ title, caption, lead, compact, seamless, children, className, ...rest }: ReadingSectionProps) {\n return <section className={cn('folio-reading-section', className)} data-compact={compact || undefined} data-seamless={seamless || undefined} {...rest}>{(title || caption) && <div className=\"folio-reading-heading\"><h2>{title}</h2>{caption && <span>{caption}</span>}</div>}{lead && <p className=\"folio-reading-lead\">{lead}</p>}{children}</section>\n}\nexport interface ReadingLeadProps { children: ReactNode; muted?: ReactNode; description?: ReactNode }\nexport function ReadingLead({ children, muted, description }: ReadingLeadProps) { return <div className=\"folio-reading-lede\"><p>{children}{muted && <span> {muted}</span>}</p>{description && <p>{description}</p>}</div> }\nexport interface ReadingPagerItem { link: CollectionLink; direction: ReactNode; title: ReactNode; media?: ReactNode }\nexport interface RecordPagerProps { label: string; previous?: ReadingPagerItem; next?: ReadingPagerItem }\nexport function RecordPager({ label, previous, next }: RecordPagerProps) {\n if (!previous && !next) return null\n const cell = (item: ReadingPagerItem | undefined, direction: 'prev' | 'next') => item ? <Slot className=\"folio-record-pager-link\" data-direction={direction}>{replaceSlotContent(item.link, <>{item.media && <span className=\"folio-record-pager-media\">{item.media}</span>}<span><span className=\"folio-record-pager-direction\">{item.direction}</span>{' '}<strong>{item.title}</strong></span></>)}</Slot> : <span />\n return <nav className=\"folio-record-pager\" aria-label={label}>{cell(previous, 'prev')}{cell(next, 'next')}</nav>\n}\nexport interface OutlineItem { id: string; label: string; level: number }\nconst SELF_NUMBERED = /^\\s*\\d+[a-z]?\\s*[.)]\\s+/i\nexport function isSelfNumbered(items: OutlineItem[]): boolean {\n if (!items.length) return false\n const top = Math.min(...items.map((item) => item.level))\n const headings = items.filter((item) => item.level === top)\n return headings.length >= 2 && headings.filter((item) => SELF_NUMBERED.test(item.label)).length * 2 > headings.length\n}\nexport function numberToc(items: OutlineItem[]): string[] {\n const top = items.length ? Math.min(...items.map((item) => item.level)) : 0\n let major = 0; let minor = 0\n return items.map((item) => item.level <= top ? (minor = 0, String(++major)) : `${major}.${++minor}`)\n}\nexport interface TableOfContentsProps { items: OutlineItem[]; label: string; numbering?: 'auto' | 'none'; onNavigate?: (id: string) => void; expanded?: boolean }\nexport function TableOfContents({ items, label, numbering = 'auto', onNavigate, expanded = false }: TableOfContentsProps) {\n const [activeId, setActiveId] = useState(items[0]?.id ?? '')\n const pinnedId = useRef<string | null>(null)\n const releaseTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const numbers = numberToc(items)\n const showNumbers = numbering === 'auto' && !isSelfNumbered(items)\n const top = items.length ? Math.min(...items.map((item) => item.level)) : 0\n useEffect(() => () => clearTimeout(releaseTimer.current), [])\n useEffect(() => {\n const sections = items.map((item) => document.getElementById(item.id)).filter((item): item is HTMLElement => Boolean(item))\n const first = sections[0]\n if (!first) return\n const update = () => {\n let current = first\n for (const section of sections) if (section.getBoundingClientRect().top <= window.innerHeight * .28) current = section\n if (pinnedId.current && current.id !== pinnedId.current) return\n pinnedId.current = null\n setActiveId(current.id)\n }\n window.addEventListener('scroll', update, { passive: true }); window.addEventListener('resize', update); update()\n return () => { window.removeEventListener('scroll', update); window.removeEventListener('resize', update) }\n }, [items])\n const groups: { item: OutlineItem; index: number; subs: { item: OutlineItem; index: number }[] }[] = []\n items.forEach((item, index) => { const last = groups.at(-1); if (item.level <= top || !last) groups.push({ item, index, subs: [] }); else last.subs.push({ item, index }) })\n if (!items.length) return null\n const link = (item: OutlineItem, index: number, isOpen?: boolean) => <a href={`#${item.id}`} data-toc-id={item.id} aria-current={activeId === item.id ? 'true' : undefined} aria-expanded={isOpen} onClick={(event) => {\n pinnedId.current = item.id; clearTimeout(releaseTimer.current); releaseTimer.current = setTimeout(() => { pinnedId.current = null }, 1200); setActiveId(item.id)\n if (onNavigate) { event.preventDefault(); onNavigate(item.id) }\n }}>{showNumbers && <span aria-hidden>{numbers[index]}</span>}{item.label}</a>\n return <nav className=\"folio-toc\" aria-label={label}><div><p>{label}</p><ul>{groups.map((group) => {\n const open = expanded || group.item.id === activeId || group.subs.some(({ item }) => item.id === activeId)\n return <li key={group.item.id}>{link(group.item, group.index, group.subs.length ? open : undefined)}{group.subs.length > 0 && open && <ul>{group.subs.map(({ item, index }) => <li key={item.id}>{link(item, index)}</li>)}</ul>}</li>\n })}</ul></div></nav>\n}\nexport interface MobileOutlineProps { items: OutlineItem[]; title: string; openLabel: string; closeLabel: string }\nexport function MobileOutline({ items, title, openLabel, closeLabel }: MobileOutlineProps) {\n const [open, setOpen] = useState(false)\n if (!items.length) return null\n return <Sheet open={open} onOpenChange={setOpen}><SheetTrigger asChild><Button variant=\"secondary\" iconOnly aria-label={openLabel} className=\"folio-outline-trigger\"><RiListUnordered size={18} aria-hidden=\"true\" /></Button></SheetTrigger><SheetContent title={title} closeLabel={closeLabel} side=\"bottom\" className=\"folio-outline-sheet\" aria-describedby={undefined}><TableOfContents items={items} label={title} expanded onNavigate={(id) => {\n setOpen(false)\n requestAnimationFrame(() => { const target = document.getElementById(id); if (target) { target.scrollIntoView({ behavior: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth', block: 'start' }); window.history.replaceState(null, '', `#${id}`) } })\n }} /></SheetContent></Sheet>\n}\nexport function ReadingProgress() {\n const [progress, setProgress] = useState(0)\n useEffect(() => {\n const update = () => { const doc = document.documentElement; const max = doc.scrollHeight - doc.clientHeight; setProgress(max > 0 ? Math.min(100, Math.max(0, doc.scrollTop / max * 100)) : 0) }\n update(); window.addEventListener('scroll', update, { passive: true }); window.addEventListener('resize', update)\n return () => { window.removeEventListener('scroll', update); window.removeEventListener('resize', update) }\n }, [])\n return <div className=\"folio-reading-progress\" aria-hidden><div style={{ width: `${progress}%` }} /></div>\n}\nexport interface AuthorPanelProps { avatar?: ReactNode; name: string; role?: ReactNode; bio?: ReactNode; links?: ReactNode }\nexport function AuthorPanel({ avatar, name, role, bio, links }: AuthorPanelProps) { return <aside className=\"folio-author-panel\">{avatar && <div className=\"folio-author-avatar\" aria-hidden>{avatar}</div>}<div><h2>{name}</h2>{role && <div className=\"folio-author-role\">{role}</div>}{bio && <p>{bio}</p>}{links && <div className=\"folio-author-links\">{links}</div>}</div></aside> }\nexport interface ReadingFigureProps { media: ReactNode; captionStart?: ReactNode; captionEnd?: ReactNode; width?: 'page' | 'fill'; ratio?: string; fit?: 'cover' | 'contain' }\n/** A reading image can preserve its source framing with `fit=\"contain\"`, rather than silently cropping it to a house ratio. */\nexport function ReadingFigure({ media, captionStart, captionEnd, width = 'page', ratio = '16 / 8', fit = 'cover' }: ReadingFigureProps) { return <figure className=\"folio-reading-figure\" data-width={width} data-fit={fit}><div style={{ aspectRatio: ratio }}>{media}</div>{(captionStart || captionEnd) && <figcaption><span>{captionStart}</span><span>{captionEnd}</span></figcaption>}</figure> }\nexport interface MediaPlaybackProps { src: string; poster?: string; label: string; playLabel: string; pauseLabel: string; fallback: ReactNode }\nexport function MediaPlayback({ src, poster, label, playLabel, pauseLabel, fallback }: MediaPlaybackProps) {\n const [reduceMotion, setReduceMotion] = useState(true)\n const [playing, setPlaying] = useState(false)\n const ref = useRef<HTMLVideoElement>(null)\n useEffect(() => { if (typeof window.matchMedia !== 'function') return; const query = window.matchMedia('(prefers-reduced-motion: reduce)'); const update = () => setReduceMotion(query.matches); update(); query.addEventListener('change', update); return () => query.removeEventListener('change', update) }, [])\n if (reduceMotion) return fallback\n return <div className=\"folio-media-playback\"><video ref={ref} src={src} poster={poster} aria-label={label} autoPlay muted loop playsInline preload=\"metadata\" onPlay={() => setPlaying(true)} onPause={() => setPlaying(false)} /><Button type=\"button\" variant=\"secondary\" iconOnly aria-label={playing ? pauseLabel : playLabel} onClick={() => { if (playing) ref.current?.pause(); else void ref.current?.play().catch(() => {}) }}>{playing ? <RiPauseFill size={18} aria-hidden /> : <RiPlayFill size={18} aria-hidden />}</Button></div>\n}\nexport interface ActionBandProps { title: ReactNode; description?: ReactNode; actions?: ReactNode; eyebrow?: ReactNode }\n/** A terminal project invitation with one strong entry action and quieter supporting actions. */\nexport function ActionBand({ title, description, actions, eyebrow }: ActionBandProps) { return <section className=\"folio-action-band\">{eyebrow && <span className=\"folio-action-band__eyebrow\">{eyebrow}</span>}<div className=\"folio-action-band__copy\"><h2>{title}</h2>{description && <p>{description}</p>}</div>{actions && <div className=\"folio-action-band__actions\">{actions}</div>}</section> }\nexport interface FeaturePairsProps { items: { id?: string; label?: ReactNode; title?: ReactNode; description: ReactNode }[] }\nexport function FeaturePairs({ items }: FeaturePairsProps) { return <div className=\"folio-feature-pairs\">{items.map((item, index) => <div key={item.id ?? index}>{item.label && <span>{item.label}</span>}{item.title && <h3>{item.title}</h3>}<div className=\"folio-feature-description\">{item.description}</div></div>)}</div> }\nexport interface DevicePreviewProps { kind: 'browser' | 'phone'; label?: string; media: ReactNode; caption?: ReactNode }\nexport function DevicePreview({ kind, label, media, caption }: DevicePreviewProps) { return <figure className=\"folio-device-preview\" data-kind={kind}><div>{kind === 'browser' && <div className=\"folio-browser-bar\"><span aria-hidden>● ● ●</span><span>{label}</span></div>}<div className=\"folio-device-screen\">{media}</div></div>{caption && <figcaption>{caption}</figcaption>}</figure> }\nexport interface PreviewPairProps { desktop: ReactNode; mobile?: ReactNode }\nexport interface DeviceRowProps { children: ReactNode; label?: string }\n/** Two or three phones side by side, the middle one raised a step, wrapping to a column on a narrow screen. */\nexport function DeviceRow({ children, label }: DeviceRowProps) { return <div className=\"folio-device-row\" role={label ? 'group' : undefined} aria-label={label}>{children}</div> }\nexport function PreviewPair({ desktop, mobile }: PreviewPairProps) { return <div className=\"folio-preview-pair\" data-mobile={Boolean(mobile) || undefined}>{desktop}{mobile}</div> }\nexport interface CommandTab { id: string; name: string; commands: string[]; then?: ReactNode; note?: ReactNode; link?: ReactNode }\nexport interface CommandTabsProps { label: string; tabs: CommandTab[]; copyLabel: string; copiedLabel: string; footer?: ReactNode }\nexport function CommandTabs({ label, tabs, copyLabel, copiedLabel, footer }: CommandTabsProps) {\n const first = tabs[0]\n if (!first) return null\n return <div className=\"folio-command-tabs\"><Tabs defaultValue={first.id}><TabsList aria-label={label}>{tabs.map((tab) => <TabsTrigger value={tab.id} key={tab.id}>{tab.name}</TabsTrigger>)}</TabsList>{tabs.map((tab) => <TabsContent value={tab.id} key={tab.id}>{tab.commands.map((command) => <CodeBlock key={command} code={command} copyLabel={copyLabel} copiedLabel={copiedLabel} />)}{tab.then && <p>{tab.then}</p>}{tab.note && <p>{tab.note}</p>}{tab.link}</TabsContent>)}</Tabs>{footer && <div className=\"folio-command-footer\">{footer}</div>}</div>\n}\nexport interface ReadingSurfaceProps extends HTMLAttributes<HTMLDivElement> { numbered?: boolean }\nexport function ReadingSurface({ numbered, children, className, ...rest }: ReadingSurfaceProps) { return <div className={cn('folio-reading-surface', className)} data-folio-article data-numbered={numbered || undefined} {...rest}>{children}</div> }\nexport interface ReadingFormProps extends React.FormHTMLAttributes<HTMLFormElement> { inline?: boolean }\nexport function ReadingForm({ inline, className, ...props }: ReadingFormProps) { return <form className={cn('folio-reading-form', className)} data-inline={inline || undefined} {...props} /> }\nexport interface ArchitectureColumnNode { id: string; title: string; description?: string }\nexport interface ArchitectureColumnsProps { columns: { title: string; connected?: boolean; nodes: ArchitectureColumnNode[] }[]; foundation?: ArchitectureColumnNode[] }\nexport function ArchitectureColumns({ columns, foundation }: ArchitectureColumnsProps) {\n return <div className=\"folio-architecture-columns\"><div>{columns.map((column, index) => <div key={index} data-connected={Boolean(foundation?.length && column.connected) || undefined}><h3>{column.title}</h3>{column.nodes.map((node) => <div className=\"folio-architecture-node\" key={node.id}><strong>{node.title}</strong>{node.description && <span>{node.description}</span>}</div>)}</div>)}</div>{foundation && foundation.length > 0 && <div className=\"folio-architecture-foundation\">{foundation.map((node) => <div key={node.id}><strong>{node.title}</strong>{node.description && <span>{node.description}</span>}</div>)}</div>}</div>\n}\nexport interface TokenLineProps { items: { label: ReactNode; strong?: boolean; icon?: ReactNode }[]; label?: ReactNode; separator?: string }\nexport function TokenLine({ items, label, separator = '·' }: TokenLineProps) {\n return <div className=\"folio-token-line\" data-flow={separator !== '·' || undefined}>\n {label && <span>{label}</span>}\n <ul role=\"list\" aria-label={typeof label === 'string' ? label : undefined}>\n {items.map((item, index) => <li key={index}>\n {index > 0 && <span aria-hidden className=\"folio-token-separator\">{separator}</span>}\n {item.icon && <span className=\"folio-token-icon\" aria-hidden>{item.icon}</span>}{item.strong ? <strong>{item.label}</strong> : item.label}\n </li>)}\n </ul>\n </div>\n}\nexport interface ContentFigureProps { children: ReactNode; caption?: ReactNode; wide?: boolean }\nexport function ContentFigure({ children, caption, wide }: ContentFigureProps) { return <figure className=\"folio-content-figure\" data-wide={wide || undefined}>{children}{caption && <figcaption>{caption}</figcaption>}</figure> }\nexport interface ReadingPageProps extends HTMLAttributes<HTMLElement> { children: ReactNode }\nexport function ReadingPage({ children, className, ...props }: ReadingPageProps) { return <section className={cn('folio-reading-page', className)} {...props}>{children}</section> }\nexport interface CommandPiece { text: string; emphasis?: 'strong' | 'normal' | 'muted' }\nexport interface CommandSnippetProps { code: string; pieces?: CommandPiece[]; copyLabel: string; copiedLabel: string; className?: string }\nexport function CommandSnippet({ code, pieces, copyLabel, copiedLabel, className }: CommandSnippetProps) {\n const escape = (value: string) => value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;').replace(/'/g, '&#39;')\n // Only use the highlighting when it describes the exact source being copied.\n const highlighted = pieces?.map((piece) => piece.text).join('') === code ? pieces : undefined\n const html = highlighted ? `<pre><code>${highlighted.map((piece) => `<span class=\"folio-command-${piece.emphasis ?? 'normal'}\">${escape(piece.text)}</span>`).join('')}</code></pre>` : undefined\n return <div className={cn('folio-command-snippet', className)}><CodeBlock code={code} html={html} copyLabel={copyLabel} copiedLabel={copiedLabel} /></div>\n}\n"],"mappings":"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","names":[]}
@@ -30,10 +30,18 @@ interface LoadingRecordsProps {
30
30
  declare function LoadingRecords({ rows, media }: LoadingRecordsProps): react.JSX.Element;
31
31
  interface WebsiteLoadingProps extends LoadingRecordsProps, LoadingRouteIntroProps {
32
32
  label: string;
33
- layout?: 'records' | 'gallery' | 'media' | 'metrics' | 'identity';
33
+ layout?: 'records' | 'gallery' | 'media' | 'metrics' | 'identity' | 'article' | 'photograph';
34
34
  filters?: boolean;
35
35
  }
36
- /** Named loading layouts for the website's reusable page shapes. */
36
+ /**
37
+ * Named loading layouts for the website's reusable page shapes.
38
+ *
39
+ * Every layout waits a beat before it shows and then fades in, so a route that
40
+ * resolves within that beat goes straight from the old page to the new one
41
+ * instead of flashing grey bars for a frame. A detail route needs its own
42
+ * `article` or `photograph` shape: inheriting the list skeleton above it
43
+ * promises a layout the page then abandons.
44
+ */
37
45
  declare function WebsiteLoading({ label, layout, titleClassName, rows, media, filters }: WebsiteLoadingProps): react.JSX.Element;
38
46
 
39
47
  export { LoadingRecords, type LoadingRecordsProps, LoadingRouteIntro, type LoadingRouteIntroProps, RecoveryLinks, type RecoveryLinksProps, RecoveryState, type RecoveryStateProps, WebsiteLoading, type WebsiteLoadingProps };