@drawnagency/primitives 0.1.118 → 0.1.120

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 (279) hide show
  1. package/dist/{chunk-O37AV27P.js → chunk-5JYHTHVR.js} +28 -0
  2. package/dist/{chunk-W72BPGR3.js → chunk-BZKHHUIN.js} +44 -8
  3. package/dist/{chunk-4NSOKEA7.js → chunk-HFHM4MNK.js} +12 -7
  4. package/dist/{chunk-CEFFP3CH.js → chunk-HZTPF6OK.js} +304 -83
  5. package/dist/{chunk-RIITGTV3.js → chunk-LQHBC7C6.js} +37 -2
  6. package/dist/{chunk-VY3IFGE3.js → chunk-QAMAIDXZ.js} +1 -1
  7. package/dist/{chunk-HRX3XJBU.js → chunk-RIN2QCYJ.js} +9 -11
  8. package/dist/{chunk-ZOTRHHUB.js → chunk-UNFPATDE.js} +8 -10
  9. package/dist/{chunk-ERLIL4RU.js → chunk-UZBFVVTC.js} +35 -37
  10. package/dist/{chunk-67P34WNW.js → chunk-ZKVQMIMJ.js} +3 -20
  11. package/dist/collab/rebase.js +4 -5
  12. package/dist/components/chat/ChatComposer.d.ts +0 -4
  13. package/dist/components/chat/ChatComposer.d.ts.map +1 -1
  14. package/dist/components/chat/ChatPanel.d.ts +2 -3
  15. package/dist/components/chat/ChatPanel.d.ts.map +1 -1
  16. package/dist/components/chat/markdown.d.ts +2 -3
  17. package/dist/components/chat/markdown.d.ts.map +1 -1
  18. package/dist/components/editor/AudienceIndicator.d.ts +3 -1
  19. package/dist/components/editor/AudienceIndicator.d.ts.map +1 -1
  20. package/dist/components/editor/IndicatorPill.d.ts +3 -1
  21. package/dist/components/editor/IndicatorPill.d.ts.map +1 -1
  22. package/dist/components/editor/PagesModal.d.ts +0 -2
  23. package/dist/components/editor/PagesModal.d.ts.map +1 -1
  24. package/dist/components/editor/SectionBulkEditModal.d.ts +25 -0
  25. package/dist/components/editor/SectionBulkEditModal.d.ts.map +1 -0
  26. package/dist/components/editor/StatusIndicator.d.ts +3 -1
  27. package/dist/components/editor/StatusIndicator.d.ts.map +1 -1
  28. package/dist/components/editor/index.js +28 -26
  29. package/dist/components/primitives/index.js +11 -15
  30. package/dist/components/sections/brand-guide.js +10 -13
  31. package/dist/components/sections/register-schemas.js +16 -21
  32. package/dist/components/sections/register.js +16 -21
  33. package/dist/components/shared/Popover.d.ts +1 -1
  34. package/dist/components/shared/Popover.d.ts.map +1 -1
  35. package/dist/components/shared/SectionSummaryRow.d.ts +1 -1
  36. package/dist/components/shell/AudienceChips.d.ts +1 -3
  37. package/dist/components/shell/AudienceChips.d.ts.map +1 -1
  38. package/dist/components/shell/EditorModal.d.ts.map +1 -1
  39. package/dist/components/shell/EditorShell.d.ts +0 -34
  40. package/dist/components/shell/EditorShell.d.ts.map +1 -1
  41. package/dist/components/shell/SiteSettingsModal.d.ts.map +1 -1
  42. package/dist/hooks/useSortableNavRow.d.ts +1 -1
  43. package/dist/hooks/useSortableRow.d.ts +1 -1
  44. package/dist/index.js +83 -90
  45. package/dist/lib/index-ops.js +5 -6
  46. package/dist/lib/index.js +27 -32
  47. package/dist/lib/pages.d.ts +0 -2
  48. package/dist/lib/pages.d.ts.map +1 -1
  49. package/dist/lib/sidecars.js +2 -2
  50. package/dist/lib/site-validation.js +13 -14
  51. package/dist/lib/storage-tier.js +4 -4
  52. package/dist/schemas/index.js +42 -45
  53. package/dist/schemas/site-config.d.ts +1 -277
  54. package/dist/schemas/site-config.d.ts.map +1 -1
  55. package/package.json +2 -42
  56. package/src/components/chat/ChatComposer.tsx +0 -4
  57. package/src/components/chat/ChatPanel.tsx +2 -3
  58. package/src/components/chat/markdown.ts +2 -3
  59. package/src/components/editor/AudienceIndicator.tsx +8 -4
  60. package/src/components/editor/IndicatorPill.tsx +4 -0
  61. package/src/components/editor/PagesModal.tsx +1 -15
  62. package/src/components/editor/SectionBulkEditModal.tsx +328 -0
  63. package/src/components/editor/StatusIndicator.tsx +7 -6
  64. package/src/components/shared/Popover.tsx +17 -4
  65. package/src/components/shared/SectionSummaryRow.tsx +1 -1
  66. package/src/components/shell/AudienceChips.tsx +1 -3
  67. package/src/components/shell/EditorModal.tsx +5 -2
  68. package/src/components/shell/EditorShell.tsx +15 -119
  69. package/src/components/shell/SiteSettingsModal.tsx +0 -11
  70. package/src/lib/pages.ts +0 -22
  71. package/src/schemas/index.ts +0 -4
  72. package/src/schemas/site-config.ts +4 -32
  73. package/dist/chunk-4TZJZU2T.js +0 -92
  74. package/dist/chunk-DIMDRBFK.js +0 -11
  75. package/dist/chunk-FV7JQRCN.js +0 -94
  76. package/dist/chunk-IIDKW7AV.js +0 -41
  77. package/dist/chunk-JCDZAFX2.js +0 -224
  78. package/dist/chunk-MM7KZS5C.js +0 -30
  79. package/dist/chunk-QQ3S3P5P.js +0 -38
  80. package/dist/chunk-TEOAKGZA.js +0 -784
  81. package/dist/chunk-VAYZJKVR.js +0 -209
  82. package/dist/components/editor/SectionOrderingModal.d.ts +0 -10
  83. package/dist/components/editor/SectionOrderingModal.d.ts.map +0 -1
  84. package/dist/components/shell/DocInspector.d.ts +0 -18
  85. package/dist/components/shell/DocInspector.d.ts.map +0 -1
  86. package/dist/components/shell/DocumentEditorOverlay.d.ts +0 -29
  87. package/dist/components/shell/DocumentEditorOverlay.d.ts.map +0 -1
  88. package/dist/components/shell/DocumentPresentOverlay.d.ts +0 -30
  89. package/dist/components/shell/DocumentPresentOverlay.d.ts.map +0 -1
  90. package/dist/components/shell/SiteSettingsDecks.d.ts +0 -13
  91. package/dist/components/shell/SiteSettingsDecks.d.ts.map +0 -1
  92. package/dist/deck/DeckComponentView.d.ts +0 -13
  93. package/dist/deck/DeckComponentView.d.ts.map +0 -1
  94. package/dist/deck/DeckPlayer.d.ts +0 -26
  95. package/dist/deck/DeckPlayer.d.ts.map +0 -1
  96. package/dist/deck/DeckSlideView.d.ts +0 -17
  97. package/dist/deck/DeckSlideView.d.ts.map +0 -1
  98. package/dist/deck/DeckStage.d.ts +0 -16
  99. package/dist/deck/DeckStage.d.ts.map +0 -1
  100. package/dist/deck/components/DeckImage.d.ts +0 -14
  101. package/dist/deck/components/DeckImage.d.ts.map +0 -1
  102. package/dist/deck/components/DeckImageGrid.d.ts +0 -27
  103. package/dist/deck/components/DeckImageGrid.d.ts.map +0 -1
  104. package/dist/deck/components/DeckRichText.d.ts +0 -22
  105. package/dist/deck/components/DeckRichText.d.ts.map +0 -1
  106. package/dist/deck/components/DeckVideo.d.ts +0 -15
  107. package/dist/deck/components/DeckVideo.d.ts.map +0 -1
  108. package/dist/deck/editing.d.ts +0 -50
  109. package/dist/deck/editing.d.ts.map +0 -1
  110. package/dist/deck/flip.d.ts +0 -11
  111. package/dist/deck/flip.d.ts.map +0 -1
  112. package/dist/deck/index.d.ts +0 -24
  113. package/dist/deck/index.d.ts.map +0 -1
  114. package/dist/deck/index.js +0 -51
  115. package/dist/deck/layouts/CenteredTextLayout.d.ts +0 -12
  116. package/dist/deck/layouts/CenteredTextLayout.d.ts.map +0 -1
  117. package/dist/deck/layouts/ColumnSplitLayout.d.ts +0 -14
  118. package/dist/deck/layouts/ColumnSplitLayout.d.ts.map +0 -1
  119. package/dist/deck/layouts/FullBleedImageLayout.d.ts +0 -14
  120. package/dist/deck/layouts/FullBleedImageLayout.d.ts.map +0 -1
  121. package/dist/deck/media.d.ts +0 -33
  122. package/dist/deck/media.d.ts.map +0 -1
  123. package/dist/deck/tokens.d.ts +0 -22
  124. package/dist/deck/tokens.d.ts.map +0 -1
  125. package/dist/deck/useStageScale.d.ts +0 -10
  126. package/dist/deck/useStageScale.d.ts.map +0 -1
  127. package/dist/deck-builder/DeckBuilderApp.d.ts +0 -30
  128. package/dist/deck-builder/DeckBuilderApp.d.ts.map +0 -1
  129. package/dist/deck-builder/DeckBuilderTopBar.d.ts +0 -24
  130. package/dist/deck-builder/DeckBuilderTopBar.d.ts.map +0 -1
  131. package/dist/deck-builder/DeckChatPanel.d.ts +0 -45
  132. package/dist/deck-builder/DeckChatPanel.d.ts.map +0 -1
  133. package/dist/deck-builder/DeckEditor.d.ts +0 -37
  134. package/dist/deck-builder/DeckEditor.d.ts.map +0 -1
  135. package/dist/deck-builder/DeckLibrary.d.ts +0 -22
  136. package/dist/deck-builder/DeckLibrary.d.ts.map +0 -1
  137. package/dist/deck-builder/DeckSlideSurface.d.ts +0 -31
  138. package/dist/deck-builder/DeckSlideSurface.d.ts.map +0 -1
  139. package/dist/deck-builder/deck-api.d.ts +0 -76
  140. package/dist/deck-builder/deck-api.d.ts.map +0 -1
  141. package/dist/deck-builder/deck-chat-client.d.ts +0 -44
  142. package/dist/deck-builder/deck-chat-client.d.ts.map +0 -1
  143. package/dist/deck-builder/deck-chat-history.d.ts +0 -10
  144. package/dist/deck-builder/deck-chat-history.d.ts.map +0 -1
  145. package/dist/deck-builder/index.d.ts +0 -13
  146. package/dist/deck-builder/index.d.ts.map +0 -1
  147. package/dist/deck-builder/index.js +0 -1498
  148. package/dist/deck-builder/starter-deck.d.ts +0 -23
  149. package/dist/deck-builder/starter-deck.d.ts.map +0 -1
  150. package/dist/deck-builder/types.d.ts +0 -29
  151. package/dist/deck-builder/types.d.ts.map +0 -1
  152. package/dist/deck-builder/useSortableSlide.d.ts +0 -29
  153. package/dist/deck-builder/useSortableSlide.d.ts.map +0 -1
  154. package/dist/document/assets/fonts/albra-grotesk-bold.woff +0 -0
  155. package/dist/document/assets/fonts/albra-grotesk-regular.woff +0 -0
  156. package/dist/document/assets/fonts/kapra-neue-expanded-medium.woff +0 -0
  157. package/dist/document/assets/fonts/kapra-neue-expanded-regular.woff +0 -0
  158. package/dist/document/components/DocCoverPage.d.ts +0 -11
  159. package/dist/document/components/DocCoverPage.d.ts.map +0 -1
  160. package/dist/document/components/DocPage.d.ts +0 -10
  161. package/dist/document/components/DocPage.d.ts.map +0 -1
  162. package/dist/document/components/DocRail.d.ts +0 -28
  163. package/dist/document/components/DocRail.d.ts.map +0 -1
  164. package/dist/document/components/DocSectionPage.d.ts +0 -12
  165. package/dist/document/components/DocSectionPage.d.ts.map +0 -1
  166. package/dist/document/components/DocViewer.d.ts +0 -21
  167. package/dist/document/components/DocViewer.d.ts.map +0 -1
  168. package/dist/document/components/Wordmark.d.ts +0 -4
  169. package/dist/document/components/Wordmark.d.ts.map +0 -1
  170. package/dist/document/components/measure.d.ts +0 -7
  171. package/dist/document/components/measure.d.ts.map +0 -1
  172. package/dist/document/components/useDocPages.d.ts +0 -12
  173. package/dist/document/components/useDocPages.d.ts.map +0 -1
  174. package/dist/document/engine/blocks.d.ts +0 -2
  175. package/dist/document/engine/blocks.d.ts.map +0 -1
  176. package/dist/document/engine/harvest.d.ts +0 -3
  177. package/dist/document/engine/harvest.d.ts.map +0 -1
  178. package/dist/document/engine/masonry.d.ts +0 -26
  179. package/dist/document/engine/masonry.d.ts.map +0 -1
  180. package/dist/document/engine/paginate.d.ts +0 -43
  181. package/dist/document/engine/paginate.d.ts.map +0 -1
  182. package/dist/document/engine/settings.d.ts +0 -91
  183. package/dist/document/engine/settings.d.ts.map +0 -1
  184. package/dist/document/engine/types.d.ts +0 -44
  185. package/dist/document/engine/types.d.ts.map +0 -1
  186. package/dist/document/index.d.ts +0 -13
  187. package/dist/document/index.d.ts.map +0 -1
  188. package/dist/document/index.js +0 -1030
  189. package/dist/document/styles/document.css +0 -217
  190. package/dist/lib/deck-access.d.ts +0 -86
  191. package/dist/lib/deck-access.d.ts.map +0 -1
  192. package/dist/lib/deck-access.js +0 -58
  193. package/dist/lib/deck-dexie.d.ts +0 -88
  194. package/dist/lib/deck-dexie.d.ts.map +0 -1
  195. package/dist/lib/deck-dexie.js +0 -22
  196. package/dist/lib/deck-edit.d.ts +0 -67
  197. package/dist/lib/deck-edit.d.ts.map +0 -1
  198. package/dist/lib/deck-media-assets.d.ts +0 -27
  199. package/dist/lib/deck-media-assets.d.ts.map +0 -1
  200. package/dist/lib/deck-media-assets.js +0 -25
  201. package/dist/lib/deck-media-refs.d.ts +0 -35
  202. package/dist/lib/deck-media-refs.d.ts.map +0 -1
  203. package/dist/lib/deck-media-refs.js +0 -36
  204. package/dist/lib/deck-tool.d.ts +0 -25
  205. package/dist/lib/deck-tool.d.ts.map +0 -1
  206. package/dist/lib/deck-tool.js +0 -58
  207. package/dist/lib/document-input.d.ts +0 -25
  208. package/dist/lib/document-input.d.ts.map +0 -1
  209. package/dist/schemas/deck-meta.d.ts +0 -85
  210. package/dist/schemas/deck-meta.d.ts.map +0 -1
  211. package/dist/schemas/deck-meta.js +0 -64
  212. package/dist/schemas/deck.d.ts +0 -954
  213. package/dist/schemas/deck.d.ts.map +0 -1
  214. package/dist/schemas/deck.js +0 -65
  215. package/src/components/editor/SectionOrderingModal.tsx +0 -80
  216. package/src/components/shell/DocInspector.tsx +0 -314
  217. package/src/components/shell/DocumentEditorOverlay.tsx +0 -179
  218. package/src/components/shell/DocumentPresentOverlay.tsx +0 -87
  219. package/src/components/shell/SiteSettingsDecks.tsx +0 -41
  220. package/src/deck/DeckComponentView.tsx +0 -28
  221. package/src/deck/DeckPlayer.tsx +0 -166
  222. package/src/deck/DeckSlideView.tsx +0 -38
  223. package/src/deck/DeckStage.tsx +0 -39
  224. package/src/deck/components/DeckImage.tsx +0 -38
  225. package/src/deck/components/DeckImageGrid.tsx +0 -254
  226. package/src/deck/components/DeckRichText.tsx +0 -111
  227. package/src/deck/components/DeckVideo.tsx +0 -81
  228. package/src/deck/editing.tsx +0 -201
  229. package/src/deck/flip.ts +0 -70
  230. package/src/deck/index.ts +0 -30
  231. package/src/deck/layouts/CenteredTextLayout.tsx +0 -29
  232. package/src/deck/layouts/ColumnSplitLayout.tsx +0 -69
  233. package/src/deck/layouts/FullBleedImageLayout.tsx +0 -58
  234. package/src/deck/media.tsx +0 -46
  235. package/src/deck/tokens.ts +0 -57
  236. package/src/deck/useStageScale.ts +0 -44
  237. package/src/deck-builder/DeckBuilderApp.tsx +0 -572
  238. package/src/deck-builder/DeckBuilderTopBar.tsx +0 -62
  239. package/src/deck-builder/DeckChatPanel.tsx +0 -334
  240. package/src/deck-builder/DeckEditor.tsx +0 -258
  241. package/src/deck-builder/DeckLibrary.tsx +0 -195
  242. package/src/deck-builder/DeckSlideSurface.tsx +0 -55
  243. package/src/deck-builder/deck-api.ts +0 -177
  244. package/src/deck-builder/deck-chat-client.ts +0 -129
  245. package/src/deck-builder/deck-chat-history.ts +0 -62
  246. package/src/deck-builder/index.ts +0 -12
  247. package/src/deck-builder/starter-deck.ts +0 -86
  248. package/src/deck-builder/types.ts +0 -24
  249. package/src/deck-builder/useSortableSlide.ts +0 -102
  250. package/src/document/assets/fonts/albra-grotesk-bold.woff +0 -0
  251. package/src/document/assets/fonts/albra-grotesk-regular.woff +0 -0
  252. package/src/document/assets/fonts/kapra-neue-expanded-medium.woff +0 -0
  253. package/src/document/assets/fonts/kapra-neue-expanded-regular.woff +0 -0
  254. package/src/document/components/DocCoverPage.tsx +0 -45
  255. package/src/document/components/DocPage.tsx +0 -40
  256. package/src/document/components/DocRail.tsx +0 -189
  257. package/src/document/components/DocSectionPage.tsx +0 -48
  258. package/src/document/components/DocViewer.tsx +0 -313
  259. package/src/document/components/Wordmark.tsx +0 -18
  260. package/src/document/components/measure.tsx +0 -133
  261. package/src/document/components/useDocPages.ts +0 -95
  262. package/src/document/engine/blocks.ts +0 -116
  263. package/src/document/engine/harvest.ts +0 -95
  264. package/src/document/engine/masonry.ts +0 -158
  265. package/src/document/engine/paginate.ts +0 -232
  266. package/src/document/engine/settings.ts +0 -125
  267. package/src/document/engine/types.ts +0 -64
  268. package/src/document/index.ts +0 -18
  269. package/src/document/styles/document.css +0 -217
  270. package/src/lib/deck-access.ts +0 -132
  271. package/src/lib/deck-dexie.ts +0 -190
  272. package/src/lib/deck-edit.ts +0 -196
  273. package/src/lib/deck-media-assets.ts +0 -57
  274. package/src/lib/deck-media-refs.ts +0 -67
  275. package/src/lib/deck-tool.ts +0 -95
  276. package/src/lib/document-input.ts +0 -57
  277. package/src/schemas/deck-meta.ts +0 -88
  278. package/src/schemas/deck.ts +0 -478
  279. package/dist/{chunk-DJABADOO.js → chunk-AJ2QOAZI.js} +3 -3
@@ -1,87 +0,0 @@
1
- import { useEffect, useMemo } from "react";
2
- import type { Page } from "../../schemas/site-config";
3
- import type { MediaManifest } from "../../media/types";
4
- import type { LoadedSection } from "../../lib/loader";
5
- import { DocViewer, documentOptionsSchema, type DocHost } from "../../document";
6
- import { buildDraftDocumentInput } from "../../lib/document-input";
7
- // Same sealed document stylesheet the editor overlay ships (see
8
- // DocumentEditorOverlay). Vitest stubs CSS imports, so it stays out of the jsdom
9
- // test graph regardless.
10
- import "../../document/styles/document.css";
11
-
12
- export interface DocumentPresentOverlayProps {
13
- /** The active page — its `document` block supplies cover/meta/section settings. */
14
- page: Page;
15
- /**
16
- * The sections currently on the canvas (already page-ordered and filtered to
17
- * the branch being previewed). The shell derives these from the same
18
- * `computeDisplaySections` call the history/branch canvas renders.
19
- */
20
- sections: LoadedSection[];
21
- manifest: MediaManifest;
22
- onClose: () => void;
23
- }
24
-
25
- /**
26
- * Read-only, full-viewport document PRESENTATION of the content currently being
27
- * viewed — used when the shell is previewing a non-draft version (the published
28
- * branch). It is `DocViewer` in `present` mode: no inspector, no update path.
29
- *
30
- * Product decision (Drew): history/branch views are the record — we present them
31
- * but never open the editor over them. Document SETTINGS come from the current
32
- * draft's `page.document`; the previewed branch's own document index is not
33
- * carried in the shell's `viewSections`, so cover/meta/exclusions here reflect
34
- * the draft, not the historical commit. This is an accepted approximation: we
35
- * are rendering historical *content* with the draft's presentation settings.
36
- */
37
- export function DocumentPresentOverlay({
38
- page,
39
- sections,
40
- manifest,
41
- onClose,
42
- }: DocumentPresentOverlayProps) {
43
- // page.document is optional on the type; the caller only mounts this when it
44
- // is enabled, but parse-with-defaults keeps us total either way.
45
- const doc = useMemo(() => documentOptionsSchema.parse(page.document ?? {}), [page.document]);
46
-
47
- const source = useMemo(
48
- () => buildDraftDocumentInput({ page, sections, manifest }),
49
- [page, sections, manifest],
50
- );
51
-
52
- // All media bytes come from /api/media — the same host shape the present route
53
- // and the editor overlay use.
54
- const host: DocHost = useMemo(
55
- () => ({
56
- variant: "preview",
57
- imageSrc: (image, variant) =>
58
- `/api/media/${image.imageId}/${variant === "print" ? image.printFile : image.previewFile}`,
59
- }),
60
- [],
61
- );
62
-
63
- // Escape closes, matching the editor overlay. DocViewer owns arrow paging on
64
- // its own window listener; Escape doesn't collide with it.
65
- useEffect(() => {
66
- const onKey = (e: KeyboardEvent) => {
67
- if (e.key !== "Escape") return;
68
- e.preventDefault();
69
- onClose();
70
- };
71
- window.addEventListener("keydown", onKey);
72
- return () => window.removeEventListener("keydown", onKey);
73
- }, [onClose]);
74
-
75
- return (
76
- <div
77
- className="fixed inset-0 z-50 flex flex-col bg-base"
78
- role="dialog"
79
- aria-modal="true"
80
- aria-label="Document presentation"
81
- >
82
- <div className="flex min-h-0 flex-1">
83
- <DocViewer source={source} document={doc} host={host} mode="present" onClose={onClose} />
84
- </div>
85
- </div>
86
- );
87
- }
@@ -1,41 +0,0 @@
1
- import { AudienceChips } from "./AudienceChips";
2
- import type { SiteConfig } from "../../schemas/site-config";
3
- import type { Audience } from "../../auth/types";
4
-
5
- interface Props {
6
- siteConfig: SiteConfig;
7
- onSiteConfigChange: (config: SiteConfig) => void;
8
- audiences: Audience[];
9
- }
10
-
11
- /** Deck Builder settings. One control today — the creation opt-in — because
12
- * that is the only `deckBuilder` key that exists; public sharing (#37) is the
13
- * next one and belongs on this tab. */
14
- export function SiteSettingsDecks({ siteConfig, onSiteConfigChange, audiences }: Props) {
15
- const selected = siteConfig.deckBuilder.audiences;
16
-
17
- function toggle(name: string) {
18
- const next = selected.includes(name) ? selected.filter((n) => n !== name) : [...selected, name];
19
- onSiteConfigChange({ ...siteConfig, deckBuilder: { ...siteConfig.deckBuilder, audiences: next } });
20
- }
21
-
22
- return (
23
- <div className="space-y-3">
24
- <div>
25
- <h3 className="mb-1 text-sm font-medium text-base-contrast">Who can build decks</h3>
26
- <p className="mb-2 text-xs text-base-contrast-light">
27
- Members of the selected audiences get the Decks button and can create and publish their own presentations.
28
- None selected means no viewer can start a new one. Editors always can.
29
- </p>
30
- <AudienceChips audiences={audiences} selected={selected} onToggle={toggle} />
31
- </div>
32
- <p className="text-xs text-base-contrast-light">
33
- Removing an audience here stops its members starting new decks. Decks they already own stay theirs — they can
34
- still open, edit and delete them — so remove the deck itself if that is what you want gone.
35
- </p>
36
- <p className="text-xs text-base-contrast-light">
37
- A deck is private to the person who made it. Sharing it with an audience, or by public link, comes later.
38
- </p>
39
- </div>
40
- );
41
- }
@@ -1,28 +0,0 @@
1
- import type { DeckTextSlot } from "../lib/deck-edit";
2
- import type { DeckComponent } from "../schemas/deck";
3
- import { DeckImage } from "./components/DeckImage";
4
- import { DeckImageGrid } from "./components/DeckImageGrid";
5
- import { DeckRichText } from "./components/DeckRichText";
6
- import { DeckVideo } from "./components/DeckVideo";
7
-
8
- interface Props {
9
- component: DeckComponent;
10
- /** The Layout slot this component fills — carried only so text leaves can
11
- * address themselves for inline editing (#39). Optional: a caller that omits
12
- * it gets the read-only render. */
13
- slot?: DeckTextSlot;
14
- }
15
-
16
- /** Dispatches a Deck Component onto its runtime implementation. */
17
- export function DeckComponentView({ component, slot }: Props) {
18
- switch (component.type) {
19
- case "richText":
20
- return <DeckRichText content={component} slot={slot} />;
21
- case "image":
22
- return <DeckImage content={component} />;
23
- case "imageGrid":
24
- return <DeckImageGrid content={component} />;
25
- case "video":
26
- return <DeckVideo content={component} />;
27
- }
28
- }
@@ -1,166 +0,0 @@
1
- import { ChevronLeft, ChevronRight } from "lucide-react";
2
- import { useCallback, useEffect, useMemo, useState } from "react";
3
- import { cn } from "../lib/cn";
4
- import type { Deck, DeckMediaAsset, DeckMediaResolver } from "../schemas/deck";
5
- import { DeckMediaProvider } from "./media";
6
- import { DeckStage } from "./DeckStage";
7
- import { DeckSlideView } from "./DeckSlideView";
8
-
9
- interface Props {
10
- deck: Deck;
11
- /** Slide to open on. Out-of-range values clamp into the deck. */
12
- initialSlide?: number;
13
- /** Window-level arrow/space/Home/End navigation. Off for thumbnails. */
14
- keyboard?: boolean;
15
- /** Turns media references into bytes. The Deck Builder injects the real one. */
16
- resolveMedia?: DeckMediaResolver;
17
- /**
18
- * Pre-resolved portal media, keyed by media id. The serializable form of the
19
- * same seam, for callers that cross an Astro island boundary — a function
20
- * prop cannot survive that trip. Ignored when `resolveMedia` is given.
21
- */
22
- mediaAssets?: Record<string, DeckMediaAsset>;
23
- className?: string;
24
- }
25
-
26
- function clamp(index: number, length: number): number {
27
- return Math.min(Math.max(index, 0), length - 1);
28
- }
29
-
30
- /** Arrow keys must not steal typing from a chat panel docked beside the stage. */
31
- function isTypingTarget(target: EventTarget | null): boolean {
32
- if (!(target instanceof HTMLElement)) return false;
33
- return target.isContentEditable || ["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName);
34
- }
35
-
36
- /**
37
- * A native button activates on Space *keyup*; the keydown only marks it active
38
- * and suppresses scrolling. Calling preventDefault on that keydown cancels the
39
- * activation — so Space on a focused "Previous slide" used to swallow the click
40
- * and step FORWARD instead. Enter needs no such guard: it has no case below, so
41
- * it never reaches preventDefault.
42
- */
43
- function isActivatableTarget(target: EventTarget | null): boolean {
44
- if (!(target instanceof HTMLElement)) return false;
45
- if (target.tagName === "BUTTON" || target.tagName === "SUMMARY") return true;
46
- if (target.tagName === "A" && target.hasAttribute("href")) return true;
47
- return target.getAttribute("role") === "button";
48
- }
49
-
50
- const NAV_BUTTON =
51
- "flex size-[38px] items-center justify-center rounded-[var(--radius-outer)] border border-base-200 text-base-contrast transition-colors hover:bg-base-accent disabled:cursor-not-allowed disabled:opacity-35 disabled:hover:bg-transparent";
52
-
53
- /**
54
- * The deck runtime's presentation surface: one 16:9 stage plus slide
55
- * navigation. Deliberately self-contained — the Deck Builder, the in-portal
56
- * deck view and the Public Link view all mount this same component, so slide
57
- * state lives here rather than in any one of those shells.
58
- */
59
- export function DeckPlayer({ deck, initialSlide = 0, keyboard = true, resolveMedia, mediaAssets, className }: Props) {
60
- const total = deck.slides.length;
61
- const [index, setIndex] = useState(() => clamp(initialSlide, total));
62
- // `Object.hasOwn`, not indexing: a media id is author-supplied, and a plain
63
- // object answers `"constructor"` and `"toString"` with inherited members that
64
- // are not assets at all. The schema's charset rules those ids out too — this
65
- // is the second lock on the same door. The `kind` check keeps the map form
66
- // honest the day a second media kind lands: it resolves portal ids, and says
67
- // so by refusing everything else rather than looking the id up anyway.
68
- const resolve = useMemo<DeckMediaResolver>(
69
- () =>
70
- resolveMedia ??
71
- ((ref) =>
72
- ref.kind === "portal" && mediaAssets !== undefined && Object.hasOwn(mediaAssets, ref.id)
73
- ? mediaAssets[ref.id]
74
- : null),
75
- [resolveMedia, mediaAssets],
76
- );
77
-
78
- const current = clamp(index, total);
79
- const slide = deck.slides[current];
80
-
81
- const goTo = useCallback((next: number) => setIndex(clamp(next, total)), [total]);
82
- // `prev` is clamped BEFORE the delta: the builder live-replaces the deck every
83
- // LLM turn, so a shrunk deck can leave the stored index past the end. Without
84
- // the inner clamp the first Previous press would land back on the slide
85
- // already showing — a visible no-op.
86
- const step = useCallback(
87
- (delta: number) => setIndex((prev) => clamp(clamp(prev, total) + delta, total)),
88
- [total],
89
- );
90
-
91
- useEffect(() => {
92
- if (!keyboard) return;
93
- const onKeyDown = (event: KeyboardEvent) => {
94
- if (event.metaKey || event.ctrlKey || event.altKey) return;
95
- if (isTypingTarget(event.target)) return;
96
- switch (event.key) {
97
- case " ":
98
- if (isActivatableTarget(event.target)) return;
99
- event.preventDefault();
100
- step(1);
101
- break;
102
- case "ArrowRight":
103
- case "PageDown":
104
- event.preventDefault();
105
- step(1);
106
- break;
107
- case "ArrowLeft":
108
- case "PageUp":
109
- event.preventDefault();
110
- step(-1);
111
- break;
112
- case "Home":
113
- event.preventDefault();
114
- setIndex(0);
115
- break;
116
- case "End":
117
- event.preventDefault();
118
- setIndex(total - 1);
119
- break;
120
- }
121
- };
122
- window.addEventListener("keydown", onKeyDown);
123
- return () => window.removeEventListener("keydown", onKeyDown);
124
- }, [keyboard, step, total]);
125
-
126
- return (
127
- <div data-deck-player={deck.id} className={cn("flex h-full min-h-0 w-full flex-col gap-4 bg-base p-4", className)}>
128
- <DeckStage className="flex-1">
129
- <DeckMediaProvider resolve={resolve}>
130
- <DeckSlideView slide={slide} />
131
- </DeckMediaProvider>
132
- </DeckStage>
133
-
134
- <nav aria-label={`${deck.title} slides`} className="flex shrink-0 items-center justify-center gap-4">
135
- <button type="button" className={NAV_BUTTON} onClick={() => step(-1)} disabled={current === 0} aria-label="Previous slide">
136
- <ChevronLeft className="size-5" aria-hidden="true" />
137
- </button>
138
-
139
- <ol className="flex items-center gap-2">
140
- {deck.slides.map((s, i) => (
141
- <li key={s.id}>
142
- <button
143
- type="button"
144
- onClick={() => goTo(i)}
145
- aria-current={i === current ? "true" : undefined}
146
- aria-label={s.title ? `Slide ${i + 1}: ${s.title}` : `Slide ${i + 1}`}
147
- className={cn(
148
- "size-[10px] rounded-full transition-colors",
149
- i === current ? "bg-primary" : "bg-base-300 hover:bg-base-contrast-light",
150
- )}
151
- />
152
- </li>
153
- ))}
154
- </ol>
155
-
156
- <button type="button" className={NAV_BUTTON} onClick={() => step(1)} disabled={current === total - 1} aria-label="Next slide">
157
- <ChevronRight className="size-5" aria-hidden="true" />
158
- </button>
159
-
160
- <p aria-live="polite" className="ml-2 text-sm tabular-nums text-base-contrast-light">
161
- {current + 1} / {total}
162
- </p>
163
- </nav>
164
- </div>
165
- );
166
- }
@@ -1,38 +0,0 @@
1
- import type { DeckSlide } from "../schemas/deck";
2
- import { CenteredTextLayout } from "./layouts/CenteredTextLayout";
3
- import { ColumnSplitLayout } from "./layouts/ColumnSplitLayout";
4
- import { FullBleedImageLayout } from "./layouts/FullBleedImageLayout";
5
-
6
- interface Props {
7
- slide: DeckSlide;
8
- }
9
-
10
- function LayoutView({ slide }: Props) {
11
- const { layout } = slide;
12
- switch (layout.type) {
13
- case "centeredText":
14
- return <CenteredTextLayout layout={layout} />;
15
- case "fullBleedImage":
16
- return <FullBleedImageLayout layout={layout} />;
17
- case "columnSplit":
18
- return <ColumnSplitLayout layout={layout} />;
19
- }
20
- }
21
-
22
- /**
23
- * Dispatches a slide onto its Layout. One slide uses exactly one Layout.
24
- *
25
- * Keyed by slide id, and that key is load-bearing rather than a lint nicety:
26
- * two slides sharing a Layout shape reconcile onto the SAME component
27
- * instances, so an expanded image-grid tile or a playing video on slide 1 was
28
- * inherited by slide 2 the moment you pressed the arrow key. The key forces the
29
- * subtree to remount, which is the only thing that makes a slide's interaction
30
- * state belong to that slide.
31
- */
32
- export function DeckSlideView({ slide }: Props) {
33
- return (
34
- <div key={slide.id} data-deck-slide={slide.id} className="size-full">
35
- <LayoutView slide={slide} />
36
- </div>
37
- );
38
- }
@@ -1,39 +0,0 @@
1
- import { useRef, type ReactNode } from "react";
2
- import { cn } from "../lib/cn";
3
- import { DECK_STAGE_HEIGHT, DECK_STAGE_WIDTH } from "../schemas/deck";
4
- import { useStageScale } from "./useStageScale";
5
-
6
- interface Props {
7
- children: ReactNode;
8
- /** Applied to the fit box, which is what the stage is scaled to fill. */
9
- className?: string;
10
- }
11
-
12
- /**
13
- * The 16:9 stage: a fixed logical canvas centered in — and scaled to fit — its
14
- * container, so slide contents are authored in px against one coordinate system
15
- * regardless of the viewport. Scaling by transform (rather than by a font-size
16
- * cascade) keeps every measurement, including line breaks, identical at every
17
- * size, which is what makes a screenshared deck look the same as its preview.
18
- */
19
- export function DeckStage({ children, className }: Props) {
20
- const fitRef = useRef<HTMLDivElement>(null);
21
- const scale = useStageScale(fitRef);
22
-
23
- return (
24
- <div ref={fitRef} className={cn("relative min-h-0 w-full overflow-hidden", className)} data-deck-fit="">
25
- <div
26
- data-deck-stage=""
27
- data-deck-scale={scale === null ? undefined : scale.toFixed(4)}
28
- className="absolute top-1/2 left-1/2 origin-center overflow-hidden rounded-[var(--radius-outer)] font-body"
29
- style={{
30
- width: DECK_STAGE_WIDTH,
31
- height: DECK_STAGE_HEIGHT,
32
- transform: scale === null ? "translate(-50%, -50%)" : `translate(-50%, -50%) scale(${scale})`,
33
- }}
34
- >
35
- {children}
36
- </div>
37
- </div>
38
- );
39
- }
@@ -1,38 +0,0 @@
1
- import type { DeckImage as DeckImageContent } from "../../schemas/deck";
2
- import { DeckMissingMedia, useDeckMedia } from "../media";
3
- import { surfaceInk } from "../tokens";
4
-
5
- interface Props {
6
- content: DeckImageContent;
7
- }
8
-
9
- /**
10
- * Deck Component — image.
11
- *
12
- * Fills its slot and crops. The caption is a solid brand block sitting on the
13
- * image's bottom edge rather than a gradient scrim: a scrim needs a literal
14
- * color to darken toward, and legibility here comes from the block.
15
- */
16
- export function DeckImage({ content }: Props) {
17
- const asset = useDeckMedia(content.media);
18
-
19
- return (
20
- <figure data-deck-component="image" className="relative size-full overflow-hidden bg-base-200">
21
- {asset === null ? (
22
- <DeckMissingMedia />
23
- ) : (
24
- <img src={asset.src} alt={asset.alt} className="size-full object-cover" />
25
- )}
26
-
27
- {content.caption !== undefined && (
28
- <figcaption
29
- data-deck-surface="brand"
30
- style={surfaceInk("brand")}
31
- className="absolute right-0 bottom-0 left-0 bg-primary px-[28px] py-[18px] text-[18px] tracking-[0.06em] text-[var(--deck-ink)] [text-transform:var(--nav-text-transform)]"
32
- >
33
- {content.caption}
34
- </figcaption>
35
- )}
36
- </figure>
37
- );
38
- }