@drawnagency/primitives 0.1.118 → 0.1.119

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 (258) 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-CEFFP3CH.js → chunk-G6O3TYPJ.js} +297 -83
  4. package/dist/{chunk-4NSOKEA7.js → chunk-HFHM4MNK.js} +12 -7
  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-YKKGVSJH.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/PagesModal.d.ts +0 -2
  19. package/dist/components/editor/PagesModal.d.ts.map +1 -1
  20. package/dist/components/editor/index.js +15 -19
  21. package/dist/components/primitives/index.js +11 -15
  22. package/dist/components/sections/brand-guide.js +10 -13
  23. package/dist/components/sections/register-schemas.js +16 -21
  24. package/dist/components/sections/register.js +16 -21
  25. package/dist/components/shell/AudienceChips.d.ts +1 -3
  26. package/dist/components/shell/AudienceChips.d.ts.map +1 -1
  27. package/dist/components/shell/EditorShell.d.ts +0 -34
  28. package/dist/components/shell/EditorShell.d.ts.map +1 -1
  29. package/dist/components/shell/SiteSettingsModal.d.ts.map +1 -1
  30. package/dist/hooks/useSortableNavRow.d.ts +1 -1
  31. package/dist/hooks/useSortableRow.d.ts +1 -1
  32. package/dist/index.js +83 -90
  33. package/dist/lib/index-ops.js +5 -6
  34. package/dist/lib/index.js +27 -32
  35. package/dist/lib/pages.d.ts +0 -2
  36. package/dist/lib/pages.d.ts.map +1 -1
  37. package/dist/lib/sidecars.js +2 -2
  38. package/dist/lib/site-validation.js +13 -14
  39. package/dist/lib/storage-tier.js +4 -4
  40. package/dist/schemas/index.js +42 -45
  41. package/dist/schemas/site-config.d.ts +1 -277
  42. package/dist/schemas/site-config.d.ts.map +1 -1
  43. package/package.json +2 -42
  44. package/src/components/chat/ChatComposer.tsx +0 -4
  45. package/src/components/chat/ChatPanel.tsx +2 -3
  46. package/src/components/chat/markdown.ts +2 -3
  47. package/src/components/editor/PagesModal.tsx +1 -15
  48. package/src/components/editor/StatusIndicator.tsx +4 -4
  49. package/src/components/shell/AudienceChips.tsx +1 -3
  50. package/src/components/shell/EditorShell.tsx +5 -113
  51. package/src/components/shell/SiteSettingsModal.tsx +0 -11
  52. package/src/lib/pages.ts +0 -22
  53. package/src/schemas/index.ts +0 -4
  54. package/src/schemas/site-config.ts +4 -32
  55. package/dist/chunk-4TZJZU2T.js +0 -92
  56. package/dist/chunk-DIMDRBFK.js +0 -11
  57. package/dist/chunk-FV7JQRCN.js +0 -94
  58. package/dist/chunk-IIDKW7AV.js +0 -41
  59. package/dist/chunk-JCDZAFX2.js +0 -224
  60. package/dist/chunk-MM7KZS5C.js +0 -30
  61. package/dist/chunk-QQ3S3P5P.js +0 -38
  62. package/dist/chunk-TEOAKGZA.js +0 -784
  63. package/dist/chunk-VAYZJKVR.js +0 -209
  64. package/dist/components/shell/DocInspector.d.ts +0 -18
  65. package/dist/components/shell/DocInspector.d.ts.map +0 -1
  66. package/dist/components/shell/DocumentEditorOverlay.d.ts +0 -29
  67. package/dist/components/shell/DocumentEditorOverlay.d.ts.map +0 -1
  68. package/dist/components/shell/DocumentPresentOverlay.d.ts +0 -30
  69. package/dist/components/shell/DocumentPresentOverlay.d.ts.map +0 -1
  70. package/dist/components/shell/SiteSettingsDecks.d.ts +0 -13
  71. package/dist/components/shell/SiteSettingsDecks.d.ts.map +0 -1
  72. package/dist/deck/DeckComponentView.d.ts +0 -13
  73. package/dist/deck/DeckComponentView.d.ts.map +0 -1
  74. package/dist/deck/DeckPlayer.d.ts +0 -26
  75. package/dist/deck/DeckPlayer.d.ts.map +0 -1
  76. package/dist/deck/DeckSlideView.d.ts +0 -17
  77. package/dist/deck/DeckSlideView.d.ts.map +0 -1
  78. package/dist/deck/DeckStage.d.ts +0 -16
  79. package/dist/deck/DeckStage.d.ts.map +0 -1
  80. package/dist/deck/components/DeckImage.d.ts +0 -14
  81. package/dist/deck/components/DeckImage.d.ts.map +0 -1
  82. package/dist/deck/components/DeckImageGrid.d.ts +0 -27
  83. package/dist/deck/components/DeckImageGrid.d.ts.map +0 -1
  84. package/dist/deck/components/DeckRichText.d.ts +0 -22
  85. package/dist/deck/components/DeckRichText.d.ts.map +0 -1
  86. package/dist/deck/components/DeckVideo.d.ts +0 -15
  87. package/dist/deck/components/DeckVideo.d.ts.map +0 -1
  88. package/dist/deck/editing.d.ts +0 -50
  89. package/dist/deck/editing.d.ts.map +0 -1
  90. package/dist/deck/flip.d.ts +0 -11
  91. package/dist/deck/flip.d.ts.map +0 -1
  92. package/dist/deck/index.d.ts +0 -24
  93. package/dist/deck/index.d.ts.map +0 -1
  94. package/dist/deck/index.js +0 -51
  95. package/dist/deck/layouts/CenteredTextLayout.d.ts +0 -12
  96. package/dist/deck/layouts/CenteredTextLayout.d.ts.map +0 -1
  97. package/dist/deck/layouts/ColumnSplitLayout.d.ts +0 -14
  98. package/dist/deck/layouts/ColumnSplitLayout.d.ts.map +0 -1
  99. package/dist/deck/layouts/FullBleedImageLayout.d.ts +0 -14
  100. package/dist/deck/layouts/FullBleedImageLayout.d.ts.map +0 -1
  101. package/dist/deck/media.d.ts +0 -33
  102. package/dist/deck/media.d.ts.map +0 -1
  103. package/dist/deck/tokens.d.ts +0 -22
  104. package/dist/deck/tokens.d.ts.map +0 -1
  105. package/dist/deck/useStageScale.d.ts +0 -10
  106. package/dist/deck/useStageScale.d.ts.map +0 -1
  107. package/dist/deck-builder/DeckBuilderApp.d.ts +0 -30
  108. package/dist/deck-builder/DeckBuilderApp.d.ts.map +0 -1
  109. package/dist/deck-builder/DeckBuilderTopBar.d.ts +0 -24
  110. package/dist/deck-builder/DeckBuilderTopBar.d.ts.map +0 -1
  111. package/dist/deck-builder/DeckChatPanel.d.ts +0 -45
  112. package/dist/deck-builder/DeckChatPanel.d.ts.map +0 -1
  113. package/dist/deck-builder/DeckEditor.d.ts +0 -37
  114. package/dist/deck-builder/DeckEditor.d.ts.map +0 -1
  115. package/dist/deck-builder/DeckLibrary.d.ts +0 -22
  116. package/dist/deck-builder/DeckLibrary.d.ts.map +0 -1
  117. package/dist/deck-builder/DeckSlideSurface.d.ts +0 -31
  118. package/dist/deck-builder/DeckSlideSurface.d.ts.map +0 -1
  119. package/dist/deck-builder/deck-api.d.ts +0 -76
  120. package/dist/deck-builder/deck-api.d.ts.map +0 -1
  121. package/dist/deck-builder/deck-chat-client.d.ts +0 -44
  122. package/dist/deck-builder/deck-chat-client.d.ts.map +0 -1
  123. package/dist/deck-builder/deck-chat-history.d.ts +0 -10
  124. package/dist/deck-builder/deck-chat-history.d.ts.map +0 -1
  125. package/dist/deck-builder/index.d.ts +0 -13
  126. package/dist/deck-builder/index.d.ts.map +0 -1
  127. package/dist/deck-builder/index.js +0 -1498
  128. package/dist/deck-builder/starter-deck.d.ts +0 -23
  129. package/dist/deck-builder/starter-deck.d.ts.map +0 -1
  130. package/dist/deck-builder/types.d.ts +0 -29
  131. package/dist/deck-builder/types.d.ts.map +0 -1
  132. package/dist/deck-builder/useSortableSlide.d.ts +0 -29
  133. package/dist/deck-builder/useSortableSlide.d.ts.map +0 -1
  134. package/dist/document/assets/fonts/albra-grotesk-bold.woff +0 -0
  135. package/dist/document/assets/fonts/albra-grotesk-regular.woff +0 -0
  136. package/dist/document/assets/fonts/kapra-neue-expanded-medium.woff +0 -0
  137. package/dist/document/assets/fonts/kapra-neue-expanded-regular.woff +0 -0
  138. package/dist/document/components/DocCoverPage.d.ts +0 -11
  139. package/dist/document/components/DocCoverPage.d.ts.map +0 -1
  140. package/dist/document/components/DocPage.d.ts +0 -10
  141. package/dist/document/components/DocPage.d.ts.map +0 -1
  142. package/dist/document/components/DocRail.d.ts +0 -28
  143. package/dist/document/components/DocRail.d.ts.map +0 -1
  144. package/dist/document/components/DocSectionPage.d.ts +0 -12
  145. package/dist/document/components/DocSectionPage.d.ts.map +0 -1
  146. package/dist/document/components/DocViewer.d.ts +0 -21
  147. package/dist/document/components/DocViewer.d.ts.map +0 -1
  148. package/dist/document/components/Wordmark.d.ts +0 -4
  149. package/dist/document/components/Wordmark.d.ts.map +0 -1
  150. package/dist/document/components/measure.d.ts +0 -7
  151. package/dist/document/components/measure.d.ts.map +0 -1
  152. package/dist/document/components/useDocPages.d.ts +0 -12
  153. package/dist/document/components/useDocPages.d.ts.map +0 -1
  154. package/dist/document/engine/blocks.d.ts +0 -2
  155. package/dist/document/engine/blocks.d.ts.map +0 -1
  156. package/dist/document/engine/harvest.d.ts +0 -3
  157. package/dist/document/engine/harvest.d.ts.map +0 -1
  158. package/dist/document/engine/masonry.d.ts +0 -26
  159. package/dist/document/engine/masonry.d.ts.map +0 -1
  160. package/dist/document/engine/paginate.d.ts +0 -43
  161. package/dist/document/engine/paginate.d.ts.map +0 -1
  162. package/dist/document/engine/settings.d.ts +0 -91
  163. package/dist/document/engine/settings.d.ts.map +0 -1
  164. package/dist/document/engine/types.d.ts +0 -44
  165. package/dist/document/engine/types.d.ts.map +0 -1
  166. package/dist/document/index.d.ts +0 -13
  167. package/dist/document/index.d.ts.map +0 -1
  168. package/dist/document/index.js +0 -1030
  169. package/dist/document/styles/document.css +0 -217
  170. package/dist/lib/deck-access.d.ts +0 -86
  171. package/dist/lib/deck-access.d.ts.map +0 -1
  172. package/dist/lib/deck-access.js +0 -58
  173. package/dist/lib/deck-dexie.d.ts +0 -88
  174. package/dist/lib/deck-dexie.d.ts.map +0 -1
  175. package/dist/lib/deck-dexie.js +0 -22
  176. package/dist/lib/deck-edit.d.ts +0 -67
  177. package/dist/lib/deck-edit.d.ts.map +0 -1
  178. package/dist/lib/deck-media-assets.d.ts +0 -27
  179. package/dist/lib/deck-media-assets.d.ts.map +0 -1
  180. package/dist/lib/deck-media-assets.js +0 -25
  181. package/dist/lib/deck-media-refs.d.ts +0 -35
  182. package/dist/lib/deck-media-refs.d.ts.map +0 -1
  183. package/dist/lib/deck-media-refs.js +0 -36
  184. package/dist/lib/deck-tool.d.ts +0 -25
  185. package/dist/lib/deck-tool.d.ts.map +0 -1
  186. package/dist/lib/deck-tool.js +0 -58
  187. package/dist/lib/document-input.d.ts +0 -25
  188. package/dist/lib/document-input.d.ts.map +0 -1
  189. package/dist/schemas/deck-meta.d.ts +0 -85
  190. package/dist/schemas/deck-meta.d.ts.map +0 -1
  191. package/dist/schemas/deck-meta.js +0 -64
  192. package/dist/schemas/deck.d.ts +0 -954
  193. package/dist/schemas/deck.d.ts.map +0 -1
  194. package/dist/schemas/deck.js +0 -65
  195. package/src/components/shell/DocInspector.tsx +0 -314
  196. package/src/components/shell/DocumentEditorOverlay.tsx +0 -179
  197. package/src/components/shell/DocumentPresentOverlay.tsx +0 -87
  198. package/src/components/shell/SiteSettingsDecks.tsx +0 -41
  199. package/src/deck/DeckComponentView.tsx +0 -28
  200. package/src/deck/DeckPlayer.tsx +0 -166
  201. package/src/deck/DeckSlideView.tsx +0 -38
  202. package/src/deck/DeckStage.tsx +0 -39
  203. package/src/deck/components/DeckImage.tsx +0 -38
  204. package/src/deck/components/DeckImageGrid.tsx +0 -254
  205. package/src/deck/components/DeckRichText.tsx +0 -111
  206. package/src/deck/components/DeckVideo.tsx +0 -81
  207. package/src/deck/editing.tsx +0 -201
  208. package/src/deck/flip.ts +0 -70
  209. package/src/deck/index.ts +0 -30
  210. package/src/deck/layouts/CenteredTextLayout.tsx +0 -29
  211. package/src/deck/layouts/ColumnSplitLayout.tsx +0 -69
  212. package/src/deck/layouts/FullBleedImageLayout.tsx +0 -58
  213. package/src/deck/media.tsx +0 -46
  214. package/src/deck/tokens.ts +0 -57
  215. package/src/deck/useStageScale.ts +0 -44
  216. package/src/deck-builder/DeckBuilderApp.tsx +0 -572
  217. package/src/deck-builder/DeckBuilderTopBar.tsx +0 -62
  218. package/src/deck-builder/DeckChatPanel.tsx +0 -334
  219. package/src/deck-builder/DeckEditor.tsx +0 -258
  220. package/src/deck-builder/DeckLibrary.tsx +0 -195
  221. package/src/deck-builder/DeckSlideSurface.tsx +0 -55
  222. package/src/deck-builder/deck-api.ts +0 -177
  223. package/src/deck-builder/deck-chat-client.ts +0 -129
  224. package/src/deck-builder/deck-chat-history.ts +0 -62
  225. package/src/deck-builder/index.ts +0 -12
  226. package/src/deck-builder/starter-deck.ts +0 -86
  227. package/src/deck-builder/types.ts +0 -24
  228. package/src/deck-builder/useSortableSlide.ts +0 -102
  229. package/src/document/assets/fonts/albra-grotesk-bold.woff +0 -0
  230. package/src/document/assets/fonts/albra-grotesk-regular.woff +0 -0
  231. package/src/document/assets/fonts/kapra-neue-expanded-medium.woff +0 -0
  232. package/src/document/assets/fonts/kapra-neue-expanded-regular.woff +0 -0
  233. package/src/document/components/DocCoverPage.tsx +0 -45
  234. package/src/document/components/DocPage.tsx +0 -40
  235. package/src/document/components/DocRail.tsx +0 -189
  236. package/src/document/components/DocSectionPage.tsx +0 -48
  237. package/src/document/components/DocViewer.tsx +0 -313
  238. package/src/document/components/Wordmark.tsx +0 -18
  239. package/src/document/components/measure.tsx +0 -133
  240. package/src/document/components/useDocPages.ts +0 -95
  241. package/src/document/engine/blocks.ts +0 -116
  242. package/src/document/engine/harvest.ts +0 -95
  243. package/src/document/engine/masonry.ts +0 -158
  244. package/src/document/engine/paginate.ts +0 -232
  245. package/src/document/engine/settings.ts +0 -125
  246. package/src/document/engine/types.ts +0 -64
  247. package/src/document/index.ts +0 -18
  248. package/src/document/styles/document.css +0 -217
  249. package/src/lib/deck-access.ts +0 -132
  250. package/src/lib/deck-dexie.ts +0 -190
  251. package/src/lib/deck-edit.ts +0 -196
  252. package/src/lib/deck-media-assets.ts +0 -57
  253. package/src/lib/deck-media-refs.ts +0 -67
  254. package/src/lib/deck-tool.ts +0 -95
  255. package/src/lib/document-input.ts +0 -57
  256. package/src/schemas/deck-meta.ts +0 -88
  257. package/src/schemas/deck.ts +0 -478
  258. package/dist/{chunk-DJABADOO.js → chunk-AJ2QOAZI.js} +3 -3
@@ -1,86 +0,0 @@
1
- import { DECK_SCHEMA_VERSION, DeckSchema, type Deck } from "../schemas/deck";
2
-
3
- /**
4
- * A new deck's seed. Three slides using the two text-only Layouts, so a New
5
- * Deck is publishable and presentable before the author has said anything —
6
- * and so the starter never cites media that a given site may not have (a
7
- * reference the manifest cannot resolve renders as a gap, which is a poor
8
- * first impression of the runtime).
9
- *
10
- * Parsed rather than cast: this is the one deck the product itself authors, so
11
- * a schema change that breaks it should break a test, not a client's first
12
- * deck.
13
- */
14
- export function starterDeck(id: string, title: string): Deck {
15
- return DeckSchema.parse({
16
- schemaVersion: DECK_SCHEMA_VERSION,
17
- id,
18
- title,
19
- slides: [
20
- {
21
- id: "cover",
22
- title: "Cover",
23
- layout: {
24
- type: "centeredText",
25
- background: "brand",
26
- content: {
27
- type: "richText",
28
- kicker: "New deck",
29
- heading: title,
30
- subheading: "Rename this slide's heading, or ask the assistant to rewrite the whole deck.",
31
- },
32
- },
33
- },
34
- {
35
- id: "context",
36
- title: "Context",
37
- layout: {
38
- type: "centeredText",
39
- content: {
40
- type: "richText",
41
- kicker: "Context",
42
- heading: "What this deck is for",
43
- paragraphs: [
44
- "Every slide uses one of the portal's prebuilt Layouts, so it inherits the brand's type, color and spacing automatically.",
45
- ],
46
- },
47
- },
48
- },
49
- {
50
- id: "next",
51
- title: "Next",
52
- layout: {
53
- type: "centeredText",
54
- content: {
55
- type: "richText",
56
- kicker: "Next",
57
- heading: "Three things to try",
58
- bullets: [
59
- "Click a thumbnail below to move through the deck",
60
- "Publish when you want the deck kept for you",
61
- "Everything you change stays on this device until you do",
62
- ],
63
- },
64
- },
65
- },
66
- ],
67
- });
68
- }
69
-
70
- /**
71
- * A deck id for a new deck: a slug of the title plus a short random suffix.
72
- *
73
- * Random rather than sequential because the id is a directory name on a shared
74
- * branch that this browser cannot enumerate (a viewer only ever sees their own
75
- * decks), so "next free number" is not something the builder can know. The
76
- * charset is `DeckIdSchema`'s.
77
- */
78
- export function newDeckId(title: string): string {
79
- const slug = title
80
- .toLowerCase()
81
- .replace(/[^a-z0-9]+/g, "-")
82
- .replace(/^-+|-+$/g, "")
83
- .slice(0, 40);
84
- const suffix = Math.random().toString(36).slice(2, 8).replace(/[^a-z0-9]/g, "0").padEnd(6, "0");
85
- return slug ? `${slug}-${suffix}` : `deck-${suffix}`;
86
- }
@@ -1,24 +0,0 @@
1
- import type { Deck } from "../schemas/deck";
2
-
3
- /**
4
- * One row of the builder's deck grid: whatever this browser has staged, joined
5
- * to whatever the decks branch holds.
6
- *
7
- * The join is by deck id and both sides are optional, because the three states
8
- * are all real: a draft never published (published side absent), a deck
9
- * published from another device (draft side absent), and the normal case where
10
- * both exist and may differ.
11
- */
12
- export interface DeckGridEntry {
13
- id: string;
14
- title: string;
15
- slideCount: number;
16
- /** ISO timestamp behind the card's "… ago" line. */
17
- updatedAt: string;
18
- /** Present when this browser has the deck staged. */
19
- draft: { deck: Deck; baseVersion: string | null } | null;
20
- /** Present when the decks branch holds it. */
21
- published: { version: string } | null;
22
- /** The draft differs from what was published (or was never published). */
23
- unpublishedChanges: boolean;
24
- }
@@ -1,102 +0,0 @@
1
- import { useEffect, useRef, useState } from "react";
2
- import { computeReorderTarget } from "../lib/reorder";
3
-
4
- interface Options {
5
- index: number;
6
- onReorder: (fromIndex: number, toIndex: number) => void;
7
- /** False while a chat turn streams — the strip goes inert, like the stage. */
8
- enabled: boolean;
9
- }
10
-
11
- const DRAG_TYPE = "deck-filmstrip-slide";
12
-
13
- /**
14
- * Drag-to-reorder for one filmstrip thumbnail.
15
- *
16
- * A sibling of `useSortableRow` rather than an option on it: the filmstrip is
17
- * HORIZONTAL, so it reads left/right edges where every existing consumer reads
18
- * top/bottom, and widening that hook's `closestEdge` type would change the two
19
- * shipped editor modals that render `DropEdgeIndicator` from it. The Deck
20
- * Builder is a sealed subtree; a second small hook here costs less than a type
21
- * change under two surfaces this ticket is not touching.
22
- *
23
- * The pointer-math is NOT duplicated: `computeReorderTarget` is written in
24
- * before/after terms, so left maps to "top" and right to "bottom" and the
25
- * unit-tested decrement stays the one implementation. The whole atlaskit
26
- * adapter is imported lazily, as it is everywhere else — jsdom cannot run it,
27
- * which is why `moveDeckSlide` and the move affordances carry the coverage.
28
- */
29
- export function useSortableSlide<E extends HTMLElement>({ index, onReorder, enabled }: Options) {
30
- const ref = useRef<E>(null);
31
- // Held in a ref so the effect does not depend on it. `onReorder` is rebuilt
32
- // whenever the draft refreshes — i.e. after every single edit — and depending
33
- // on it tore down and re-registered the drag adapter on every thumbnail in
34
- // the strip each time.
35
- const onReorderRef = useRef(onReorder);
36
- onReorderRef.current = onReorder;
37
- const [isDragging, setIsDragging] = useState(false);
38
- const [closestEdge, setClosestEdge] = useState<"left" | "right" | null>(null);
39
-
40
- useEffect(() => {
41
- if (!enabled) return;
42
- const element = ref.current;
43
- if (!element) return;
44
-
45
- let cleanup: (() => void) | undefined;
46
- let cancelled = false;
47
-
48
- void Promise.all([
49
- import("@atlaskit/pragmatic-drag-and-drop/element/adapter"),
50
- import("@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge"),
51
- ]).then(([{ draggable, dropTargetForElements }, { attachClosestEdge, extractClosestEdge }]) => {
52
- if (cancelled) return;
53
-
54
- const edgeOf = (data: Record<string | symbol, unknown>): "left" | "right" | null => {
55
- const edge = extractClosestEdge(data);
56
- return edge === "left" || edge === "right" ? edge : null;
57
- };
58
-
59
- const cleanupDraggable = draggable({
60
- element,
61
- getInitialData: () => ({ dragType: DRAG_TYPE, index }),
62
- onGenerateDragPreview: () => {
63
- element.style.opacity = "0.4";
64
- requestAnimationFrame(() => {
65
- element.style.opacity = "";
66
- });
67
- },
68
- onDragStart: () => setIsDragging(true),
69
- onDrop: () => setIsDragging(false),
70
- });
71
-
72
- const cleanupDropTarget = dropTargetForElements({
73
- element,
74
- canDrop: ({ source }) => source.data.dragType === DRAG_TYPE,
75
- getData: ({ input, element: target }) =>
76
- attachClosestEdge({ index }, { input, element: target, allowedEdges: ["left", "right"] }),
77
- onDragEnter: ({ self }) => setClosestEdge(edgeOf(self.data)),
78
- onDrag: ({ self }) => setClosestEdge(edgeOf(self.data)),
79
- onDragLeave: () => setClosestEdge(null),
80
- onDrop: ({ source, self }) => {
81
- setClosestEdge(null);
82
- const fromIndex = source.data.index as number;
83
- const edge = edgeOf(self.data);
84
- const toIndex = computeReorderTarget(fromIndex, index, edge === "right" ? "bottom" : edge === "left" ? "top" : null);
85
- if (toIndex !== null) onReorderRef.current(fromIndex, toIndex);
86
- },
87
- });
88
-
89
- cleanup = () => {
90
- cleanupDraggable();
91
- cleanupDropTarget();
92
- };
93
- });
94
-
95
- return () => {
96
- cancelled = true;
97
- cleanup?.();
98
- };
99
- }, [index, enabled]);
100
-
101
- return { ref, isDragging, closestEdge };
102
- }
@@ -1,45 +0,0 @@
1
- import type { DocCover } from "../engine/settings";
2
- import type { DocImage, DocMeta } from "../engine/types";
3
- import { DocPage } from "./DocPage";
4
- import type { DocHost } from "./DocSectionPage";
5
- import { Wordmark } from "./Wordmark";
6
-
7
- // Absorbs the harness's TitleCardLayout inline instead of wrapping it: the
8
- // portal-native cover has no client-logo slot (the harness only ever passed
9
- // null) and cover text is edited in the inspector, never on-canvas, so there
10
- // is no EditableText/deck-context machinery here — this always renders the
11
- // harness's non-editable, null-logo branch.
12
- export function DocCoverPage({
13
- cover,
14
- meta,
15
- image,
16
- host,
17
- }: {
18
- cover: DocCover;
19
- meta: DocMeta;
20
- /** Resolved from source.images[cover.imageId] by the caller; null/undefined = solid cover. */
21
- image?: DocImage | null;
22
- host?: DocHost;
23
- }) {
24
- return (
25
- <DocPage meta={meta} bleed chrome={false}>
26
- <div className="layout-titlecard" style={{ background: cover.bgColor }}>
27
- <header className="layout-titlecard__masthead">
28
- <Wordmark className="layout-titlecard__wordmark" />
29
- <span className="layout-titlecard__rule" />
30
- </header>
31
- {image && host ? (
32
- <div className="layout-titlecard__image">
33
- <img src={host.imageSrc(image, host.variant)} alt={image.alt} />
34
- </div>
35
- ) : null}
36
- <div className="layout-titlecard__lockup" style={{ color: cover.accentColor }}>
37
- <h1 className="layout-titlecard__title">{cover.title}</h1>
38
- <span className="layout-titlecard__titlerule" style={{ background: cover.accentColor }} />
39
- <p className="layout-titlecard__label">{cover.label}</p>
40
- <p className="layout-titlecard__date">{cover.date || meta.dateLabel}</p>
41
- </div>
42
- </div>
43
- </DocPage>
44
- );
45
- }
@@ -1,40 +0,0 @@
1
- import type { ReactNode } from "react";
2
- import type { DocMeta } from "../engine/types";
3
- import { Wordmark } from "./Wordmark";
4
-
5
- // Re-exported so index.ts's `export { DocPage, type DocMeta } from
6
- // "./components/DocPage"` has a local name — the single definition still
7
- // lives in engine/types.ts.
8
- export type { DocMeta };
9
-
10
- export function DocPage({
11
- meta,
12
- bleed = false,
13
- chrome = true,
14
- children,
15
- }: {
16
- meta: DocMeta;
17
- bleed?: boolean;
18
- chrome?: boolean;
19
- children: ReactNode;
20
- }) {
21
- const crumb = [meta.client, meta.project, meta.dateLabel]
22
- .map((s) => s.trim().toUpperCase())
23
- .filter(Boolean)
24
- .join(" :: ");
25
-
26
- return (
27
- <section className="deck-page">
28
- {bleed ? <div className="deck-bleed">{children}</div> : null}
29
- {chrome ? (
30
- <header className="deck-chrome">
31
- <span className="deck-crumb">{crumb}</span>
32
- <div className="deck-wordmark-clip">
33
- <Wordmark className="deck-wordmark" />
34
- </div>
35
- </header>
36
- ) : null}
37
- {!bleed ? <div className="deck-body">{children}</div> : null}
38
- </section>
39
- );
40
- }
@@ -1,189 +0,0 @@
1
- import { useEffect, useRef, useState, type ReactNode } from "react";
2
- import type { DerivedPage } from "../engine/paginate";
3
- import type { DocCover } from "../engine/settings";
4
- import type { DocImage, DocMeta } from "../engine/types";
5
- import { DocCoverPage } from "./DocCoverPage";
6
- import { DocSectionPage, type DocHost } from "./DocSectionPage";
7
-
8
- // 17in @96dpi = 1632px wide; thumbnails render the real page scaled to fill the
9
- // rail width at a locked 17:11 aspect ratio (mirrors the harness PageRail).
10
- const PAGE_W = 1632;
11
- const PAGE_H = 1056;
12
-
13
- export type RailEntry =
14
- | { kind: "cover" }
15
- | { kind: "page"; page: DerivedPage }
16
- | { kind: "ghost"; chunkId: string; title: string; reason: "excluded" | "empty"; depth?: 1 };
17
-
18
- export function entryId(entry: RailEntry): string {
19
- if (entry.kind === "cover") return "cover";
20
- if (entry.kind === "page") return entry.page.id;
21
- return `ghost:${entry.chunkId}`;
22
- }
23
-
24
- // Rail nesting: sub-pages sit one step in per depth, continuations one more.
25
- const INDENT = ["", "pl-4", "pl-8"];
26
- const indentClass = (level: number) => INDENT[Math.min(level, INDENT.length - 1)] || undefined;
27
-
28
- function ThumbButton({
29
- id,
30
- selectedId,
31
- scale,
32
- label,
33
- indentLevel,
34
- onSelect,
35
- children,
36
- }: {
37
- id: string;
38
- selectedId: string;
39
- scale: number;
40
- label: string;
41
- indentLevel?: number;
42
- onSelect: (id: string) => void;
43
- children: ReactNode;
44
- }) {
45
- // Each thumbnail is a REAL page subtree — every image at print resolution,
46
- // scaled to ~11%. Mounting all of them cost a 40-page document 40 subtrees and
47
- // a fresh request for every image before the rail was even scrolled. The
48
- // aspect-ratio box below holds its size whether or not the page is mounted, so
49
- // deferring costs nothing in layout. Sticky once shown: re-unmounting would
50
- // re-request the images on every scroll back.
51
- const liRef = useRef<HTMLLIElement>(null);
52
- const [shown, setShown] = useState(typeof IntersectionObserver === "undefined");
53
- useEffect(() => {
54
- if (shown) return;
55
- const el = liRef.current;
56
- if (!el) return;
57
- const obs = new IntersectionObserver(
58
- (entries) => {
59
- if (entries.some((e) => e.isIntersecting)) {
60
- setShown(true);
61
- obs.disconnect();
62
- }
63
- },
64
- { rootMargin: "200% 0px" },
65
- );
66
- obs.observe(el);
67
- return () => obs.disconnect();
68
- }, [shown]);
69
-
70
- return (
71
- <li ref={liRef} className={indentClass(indentLevel ?? 0)}>
72
- <button
73
- type="button"
74
- onClick={() => onSelect(id)}
75
- className={`relative block w-full rounded border-2 bg-white ${
76
- id === selectedId ? "border-teal-600" : "border-neutral-200 hover:border-neutral-300"
77
- }`}
78
- >
79
- <span className="pointer-events-none block w-full overflow-hidden aspect-[17/11]">
80
- <span
81
- className="block origin-top-left"
82
- style={{ transform: `scale(${scale})`, width: PAGE_W, height: PAGE_H }}
83
- >
84
- {shown ? children : null}
85
- </span>
86
- </span>
87
- <span className="absolute bottom-0.5 left-1 max-w-[90%] truncate text-[10px] text-neutral-400">
88
- {label}
89
- </span>
90
- </button>
91
- </li>
92
- );
93
- }
94
-
95
- export function DocRail({
96
- entries,
97
- selectedId,
98
- meta,
99
- cover,
100
- coverImage,
101
- host,
102
- onSelect,
103
- onCollapse,
104
- }: {
105
- entries: RailEntry[];
106
- selectedId: string;
107
- meta: DocMeta;
108
- cover: DocCover;
109
- coverImage?: DocImage | null;
110
- host: DocHost;
111
- onSelect: (id: string) => void;
112
- onCollapse: () => void;
113
- }) {
114
- const listRef = useRef<HTMLOListElement>(null);
115
- const [scale, setScale] = useState(0.11);
116
- useEffect(() => {
117
- const el = listRef.current;
118
- if (!el || typeof ResizeObserver === "undefined") return;
119
- const obs = new ResizeObserver(() => {
120
- // content width = list width minus the button's 2px border on each side
121
- const w = el.clientWidth - 4;
122
- if (w > 0) setScale(w / PAGE_W);
123
- });
124
- obs.observe(el);
125
- return () => obs.disconnect();
126
- }, []);
127
-
128
- return (
129
- <nav
130
- data-testid="doc-rail"
131
- className="flex w-52 shrink-0 flex-col overflow-y-auto border-r border-neutral-200 bg-neutral-50 p-3"
132
- >
133
- <button
134
- type="button"
135
- aria-label="Collapse page rail"
136
- onClick={onCollapse}
137
- className="mb-2 flex w-full items-center justify-between rounded px-2 py-1.5 text-sm font-medium text-neutral-600 hover:bg-neutral-100 hover:text-neutral-900"
138
- >
139
- <span>Pages</span>
140
- <span aria-hidden="true" className="text-lg leading-none">‹</span>
141
- </button>
142
- <ol ref={listRef} className="flex flex-col gap-2">
143
- {entries.map((entry) => {
144
- const id = entryId(entry);
145
- if (entry.kind === "cover") {
146
- return (
147
- <ThumbButton key={id} id={id} selectedId={selectedId} scale={scale} label="Cover" onSelect={onSelect}>
148
- <DocCoverPage cover={cover} meta={meta} image={coverImage} host={host} />
149
- </ThumbButton>
150
- );
151
- }
152
- if (entry.kind === "page") {
153
- return (
154
- <ThumbButton
155
- key={id}
156
- id={id}
157
- selectedId={selectedId}
158
- scale={scale}
159
- label={entry.page.title}
160
- indentLevel={(entry.page.depth ?? 0) + (entry.page.cont ? 1 : 0)}
161
- onSelect={onSelect}
162
- >
163
- <DocSectionPage page={entry.page} meta={meta} host={host} />
164
- </ThumbButton>
165
- );
166
- }
167
- return (
168
- <li key={id} className={indentClass(entry.depth ?? 0)}>
169
- <button
170
- type="button"
171
- onClick={() => onSelect(id)}
172
- className={`flex w-full items-center justify-between gap-2 rounded border px-2 py-2 text-left text-xs ${
173
- id === selectedId
174
- ? "border-teal-600 bg-white"
175
- : "border-dashed border-neutral-300 bg-neutral-50 hover:border-neutral-400"
176
- }`}
177
- >
178
- <span className="truncate text-neutral-400">{entry.title}</span>
179
- <span className="shrink-0 rounded bg-neutral-200 px-1 text-[10px] uppercase tracking-wide text-neutral-500">
180
- {entry.reason}
181
- </span>
182
- </button>
183
- </li>
184
- );
185
- })}
186
- </ol>
187
- </nav>
188
- );
189
- }
@@ -1,48 +0,0 @@
1
- import type { DocImage, DocMeta } from "../engine/types";
2
- import type { DerivedPage } from "../engine/paginate";
3
- import { DocPage } from "./DocPage";
4
-
5
- export interface DocHost {
6
- imageSrc: (image: DocImage, variant: "preview" | "print") => string;
7
- variant: "preview" | "print";
8
- }
9
-
10
- const inch = (v: number) => `${v}in`;
11
-
12
- export function DocSectionPage({ page, meta, host }: { page: DerivedPage; meta: DocMeta; host: DocHost }) {
13
- return (
14
- <DocPage meta={meta}>
15
- <h2 className="pdoc-title">
16
- {page.kicker ? <span className="pdoc-kicker" aria-hidden="true">{page.kicker}</span> : null}
17
- {page.title}
18
- </h2>
19
- {page.textRegion ? (
20
- <div
21
- className="pdoc-text"
22
- style={{ left: inch(page.textRegion.xIn), top: inch(page.textRegion.yIn), width: inch(page.textRegion.wIn) }}
23
- >
24
- {page.textBlocks.map((b, i) => {
25
- const spacing = i > 0 ? { marginTop: inch(0.15) } : undefined;
26
- if (b.kind === "heading") {
27
- return b.level === 2 ? (
28
- <h3 key={i} className="pdoc-h2" style={spacing}>{b.text}</h3>
29
- ) : (
30
- <h4 key={i} className="pdoc-h3" style={spacing}>{b.text}</h4>
31
- );
32
- }
33
- return <div key={i} className="pdoc-prose" style={spacing} dangerouslySetInnerHTML={{ __html: b.html }} />;
34
- })}
35
- </div>
36
- ) : null}
37
- {page.imageBoxes.map((box, i) => (
38
- <div
39
- key={`${box.image.imageId}-${i}`}
40
- className="pdoc-img"
41
- style={{ left: inch(box.xIn), top: inch(box.yIn), width: inch(box.wIn), height: inch(box.hIn) }}
42
- >
43
- <img src={host.imageSrc(box.image, host.variant)} alt={box.image.alt} style={{ objectFit: box.fit }} />
44
- </div>
45
- ))}
46
- </DocPage>
47
- );
48
- }