@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,125 +0,0 @@
1
- import { z } from "zod";
2
-
3
- // Presentation settings for one link-heading chunk. This shape is designed as
4
- // if it were already a portal page's `document` options block — migrating it
5
- // into the portal later must be a data move, not a redesign.
6
- export const sectionSettingsSchema = z.object({
7
- excluded: z.boolean().default(false),
8
- textPlacement: z.enum(["top", "leftColumn"]).default("top"),
9
- // "right" is only meaningful with textPlacement "leftColumn"; settingsFor
10
- // normalizes the invalid combo.
11
- imagePlacement: z.enum(["fullWidth", "right"]).default("fullWidth"),
12
- // When true, text that overflows the first page is dropped instead of
13
- // flowing onto Cont. pages (images still paginate normally).
14
- dropTextOverflow: z.boolean().default(false),
15
- // Also start new pages at sub-heading boundaries. Only read from PARENT
16
- // entries (link_heading ids / __preamble); inert on sub-chunk entries.
17
- splitAt: z.enum(["none", "sub_heading"]).default("none"),
18
- imageLayout: z
19
- .discriminatedUnion("mode", [
20
- z.object({
21
- mode: z.literal("grid"),
22
- rows: z.union([z.literal(1), z.literal(2), z.literal(3)]).default(2),
23
- columns: z.union([z.literal(2), z.literal(3), z.literal(4)]).default(4),
24
- // "cover" crops each image to its cell; "contain" letterboxes the
25
- // natural image inside it.
26
- fit: z.enum(["cover", "contain"]).default("cover"),
27
- }),
28
- z.object({
29
- mode: z.literal("masonry"),
30
- flow: z.enum(["rows", "columns"]).default("rows"),
31
- columns: z
32
- .union([z.literal(2), z.literal(3), z.literal(4), z.literal(5), z.literal(6)])
33
- .default(3),
34
- // rows flow only: "auto" = greedy target-height fill (legacy behavior);
35
- // 1-4 forces an exact aspect-balanced row count.
36
- rows: z
37
- .union([z.literal("auto"), z.literal(1), z.literal(2), z.literal(3), z.literal(4)])
38
- .default("auto"),
39
- }),
40
- ])
41
- .default({ mode: "masonry", flow: "rows", columns: 3, rows: "auto" }),
42
- });
43
-
44
- export type SectionSettings = z.infer<typeof sectionSettingsSchema>;
45
-
46
- const DEFAULTS: SectionSettings = sectionSettingsSchema.parse({});
47
-
48
- /**
49
- * Field-by-field salvage. The whole-entry `safeParse` fallback was
50
- * all-or-nothing: ONE unrecognised value discarded every sibling setting, and
51
- * since the defaults have `excluded: false`, a section deliberately excluded
52
- * from the printed document silently reappeared in the output. `splitAt` was
53
- * lost the same way. An index written by a newer package version — or by an MCP
54
- * agent — is the realistic source of such a value.
55
- */
56
- function salvage(raw: unknown): { settings: SectionSettings; rejected: string[] } {
57
- const fast = sectionSettingsSchema.safeParse(raw ?? {});
58
- if (fast.success) return { settings: fast.data, rejected: [] };
59
- // Not an object at all (stale scalar, null): nothing to salvage.
60
- if (!raw || typeof raw !== "object") return { settings: DEFAULTS, rejected: [] };
61
-
62
- const src = raw as Record<string, unknown>;
63
- const out: Record<string, unknown> = {};
64
- const rejected: string[] = [];
65
- for (const [key, field] of Object.entries(sectionSettingsSchema.shape)) {
66
- // Every field carries a .default(), so an absent key parses to its default.
67
- const parsed = (field as z.ZodTypeAny).safeParse(src[key]);
68
- if (parsed.success) {
69
- out[key] = parsed.data;
70
- } else {
71
- rejected.push(key);
72
- out[key] = DEFAULTS[key as keyof SectionSettings];
73
- }
74
- }
75
- return { settings: out as SectionSettings, rejected };
76
- }
77
-
78
- // Overlay lookup: unknown/stale/malformed entries fall back to defaults —
79
- // settings for a deleted portal section must be inert, never fatal.
80
- export function settingsFor(
81
- all: Record<string, unknown> | undefined,
82
- chunkId: string,
83
- ): SectionSettings {
84
- const s = salvage(all?.[chunkId]).settings;
85
- if (s.textPlacement === "top" && s.imagePlacement === "right") {
86
- return { ...s, imagePlacement: "fullWidth" };
87
- }
88
- return s;
89
- }
90
-
91
- /** Setting names for `chunkId` that failed to parse and were reset to their
92
- * default. Surfaced as document warnings so a silent reset is visible. */
93
- export function settingsIssuesFor(
94
- all: Record<string, unknown> | undefined,
95
- chunkId: string,
96
- ): string[] {
97
- return salvage(all?.[chunkId]).rejected;
98
- }
99
-
100
- const documentMetaSchema = z.object({
101
- client: z.string().default(""),
102
- project: z.string().default(""),
103
- dateLabel: z.string().default(""),
104
- });
105
- const documentCoverSchema = z.object({
106
- bgColor: z.string().default("#333333"),
107
- accentColor: z.string().default("#EB9500"),
108
- title: z.string().default(""),
109
- label: z.string().default("BRAND GUIDELINES"),
110
- date: z.string().default(""),
111
- // Media-library image id shown between masthead and title lockup; null = solid cover.
112
- imageId: z.string().nullable().default(null),
113
- });
114
-
115
- export const documentOptionsSchema = z.object({
116
- enabled: z.boolean().default(false),
117
- // `.default({})` doesn't typecheck even though `{}` parses fine at
118
- // runtime — zod v4 infers the default's type from the object schema's
119
- // required input shape, not its post-default output shape.
120
- meta: documentMetaSchema.default(() => documentMetaSchema.parse({})),
121
- cover: documentCoverSchema.default(() => documentCoverSchema.parse({})),
122
- sections: z.record(z.string(), sectionSettingsSchema).default({}),
123
- });
124
- export type DocumentOptions = z.infer<typeof documentOptionsSchema>;
125
- export type DocCover = DocumentOptions["cover"];
@@ -1,64 +0,0 @@
1
- // Engine-local structural types for portal content. Deliberately NOT imported
2
- // from the rest of @drawnagency/primitives: the engine's contract is the
3
- // portal content SHAPE, and staying import-free keeps it portable into its
4
- // own package later.
5
- // (The fixture test in tests/document/fixture.test.ts pins the shape against
6
- // the real primitives schemas so drift is caught.)
7
-
8
- export const PREAMBLE_ID = "__preamble";
9
-
10
- // Page-split boundary level for one parent section. Sub-headings are the
11
- // deepest deck boundary (internal convention, 2026-07-19) — sub_sub_heading
12
- // sections always stay in-page as level-3 text blocks.
13
- export type SplitAt = "none" | "sub_heading";
14
-
15
- // Breadcrumb metadata shown in the page chrome. Single definition — the
16
- // harness carried this shape in two places (deck/DeckPage.tsx's `DeckMeta`
17
- // and lib/deck/schema.ts's zod-inferred `DeckMeta`); the engine only needs
18
- // the plain type, not a schema, so it lives here once.
19
- export interface DocMeta {
20
- client: string;
21
- project: string;
22
- dateLabel: string;
23
- }
24
-
25
- export interface DocSectionInput {
26
- id: string;
27
- type: string;
28
- content: Record<string, unknown>;
29
- options?: Record<string, unknown>;
30
- }
31
-
32
- // One displayable image, dimensions + pre-chosen repo filenames baked in by
33
- // the (scaffolding) fetch layer so the engine never touches the manifest.
34
- export interface DocImage {
35
- imageId: string;
36
- width: number;
37
- height: number;
38
- alt: string;
39
- previewFile: string; // e.g. "1080.webp" — editor variant
40
- printFile: string; // e.g. "1920.webp" — print/PDF variant
41
- }
42
-
43
- export type TextBlock =
44
- | { kind: "heading"; level: 2 | 3; text: string }
45
- | { kind: "prose"; html: string };
46
-
47
- // A page-producing chunk: a link_heading section (or the preamble), or — when
48
- // the parent's splitAt says so — a sub heading section.
49
- export interface Chunk {
50
- id: string; // boundary heading's section id, or PREAMBLE_ID
51
- title: string;
52
- textBlocks: TextBlock[];
53
- images: DocImage[];
54
- // Sub-chunk fields; absent on top-level chunks.
55
- parentId?: string;
56
- kicker?: string; // masthead context line — the parent link_heading title
57
- depth?: 1;
58
- }
59
-
60
- export interface DocSourceContent {
61
- pageTitle: string;
62
- sections: DocSectionInput[];
63
- images: Record<string, DocImage>;
64
- }
@@ -1,18 +0,0 @@
1
- // Public surface of the document engine + components. Sealed subtree: nothing
2
- // in document/ imports from the rest of primitives (see spec — extraction to
3
- // @drawnagency/document must stay a directory move).
4
- export * from "./engine/types";
5
- export * from "./engine/settings";
6
- export { splitProseBlocks } from "./engine/blocks";
7
- export { harvest } from "./engine/harvest";
8
- export * from "./engine/paginate";
9
- export { layoutMasonryPage, type MasonryOpts, type Sized } from "./engine/masonry";
10
-
11
- export { DocPage, type DocMeta } from "./components/DocPage";
12
- export { DocSectionPage, type DocHost } from "./components/DocSectionPage";
13
- export { DocCoverPage } from "./components/DocCoverPage";
14
- export { useDocPages } from "./components/useDocPages";
15
-
16
- // Task 3 — shared document shell (presentation + editing).
17
- export { DocViewer } from "./components/DocViewer";
18
- export { DocRail, entryId, type RailEntry } from "./components/DocRail";
@@ -1,217 +0,0 @@
1
- /* Document artifact rendering — physical units only. This file styles the
2
- artifact itself (editor canvas AND print route), so no Tailwind utilities
3
- and no viewport-relative units. 11×17 tabloid landscape = 17in × 11in. */
4
-
5
- /* ---- Drawn brand fonts ---------------------------------------------- */
6
- /* Albra Grotesk (sans body) · Kapra Neue Expanded (uppercase labels).
7
- Self-hosted, resolved relative to this file so bundlers can locate them
8
- without a public root. */
9
- @font-face {
10
- font-family: "Albra Grotesk";
11
- src: url("../assets/fonts/albra-grotesk-regular.woff") format("woff");
12
- font-weight: 400;
13
- font-style: normal;
14
- font-display: swap;
15
- }
16
- @font-face {
17
- font-family: "Albra Grotesk";
18
- src: url("../assets/fonts/albra-grotesk-bold.woff") format("woff");
19
- font-weight: 600;
20
- font-style: normal;
21
- font-display: swap;
22
- }
23
- @font-face {
24
- font-family: "Kapra Neue Expanded";
25
- src: url("../assets/fonts/kapra-neue-expanded-regular.woff") format("woff");
26
- font-weight: 400;
27
- font-style: normal;
28
- font-display: swap;
29
- }
30
- @font-face {
31
- font-family: "Kapra Neue Expanded";
32
- src: url("../assets/fonts/kapra-neue-expanded-medium.woff") format("woff");
33
- font-weight: 700;
34
- font-style: normal;
35
- font-display: swap;
36
- }
37
-
38
- /* ---- page chrome ------------------------------------------------------ */
39
-
40
- .deck-page {
41
- position: relative;
42
- width: 17in;
43
- height: 11in;
44
- background: #fff;
45
- color: #1a1a1a;
46
- overflow: hidden;
47
- }
48
-
49
- .deck-chrome {
50
- position: absolute;
51
- top: 0.5in;
52
- left: 0.6in;
53
- right: 0.6in;
54
- display: flex;
55
- align-items: flex-end;
56
- justify-content: space-between;
57
- border-bottom: 1px solid #1a1a1a;
58
- padding-bottom: 0.1in;
59
- z-index: 10;
60
- }
61
-
62
- .deck-crumb {
63
- font-family: "Kapra Neue Expanded", Helvetica, Arial, sans-serif;
64
- font-size: 9pt;
65
- letter-spacing: 0.12em;
66
- text-transform: uppercase;
67
- }
68
-
69
- /* The wordmark SVG's viewBox includes an under-swash flourish below the
70
- letters' baseline. The clip wrapper truncates the glyph so the hairline
71
- rule slices INTO the letter bottoms (per the reference deck): the cut
72
- falls at ~viewBox y=100.5 of 154.474 (0.407 / 0.625), removing the
73
- under-swash, the D's descender loop, and the rounded letter bottoms.
74
- The negative margin cancels .deck-chrome's padding-bottom so the cut
75
- edge lands exactly on the rule, while the breadcrumb keeps its normal
76
- gap above the rule. */
77
- .deck-wordmark-clip {
78
- height: 0.407in;
79
- overflow: hidden;
80
- margin-bottom: -0.1in;
81
- }
82
-
83
- .deck-wordmark {
84
- height: 0.625in;
85
- width: auto;
86
- display: block;
87
- }
88
-
89
- .deck-body {
90
- position: absolute;
91
- top: 1.35in;
92
- left: 0.6in;
93
- right: 0.6in;
94
- bottom: 0.6in;
95
- }
96
-
97
- /* Bleed layouts (cover) paint the whole page; chrome layers above via
98
- .deck-chrome's z-index — layering only, chrome treatment is untouched. */
99
- .deck-bleed {
100
- position: absolute;
101
- inset: 0;
102
- }
103
-
104
- /* ---- title card (document cover) --------------------------------------- */
105
-
106
- .layout-titlecard {
107
- position: absolute;
108
- inset: 0;
109
- color: #fff;
110
- }
111
- .layout-titlecard__masthead {
112
- position: absolute;
113
- top: 0.5in;
114
- left: 0.6in;
115
- right: 0.6in;
116
- display: flex;
117
- align-items: center;
118
- gap: 0.3in;
119
- }
120
- .layout-titlecard__wordmark { height: 0.4in; width: auto; color: #fff; flex: none; }
121
- .layout-titlecard__rule { flex: 1; height: 1px; background: #fff; }
122
- /* Optional cover image: the band between the masthead rule (~1.1in) and the
123
- title lockup (~3.2in from the page bottom). Image is 50% of the band's
124
- height, natural aspect ratio, left-aligned to the content margin, its
125
- center anchored at 45% of the band (top 20% + half of 50%). */
126
- .layout-titlecard__image {
127
- position: absolute;
128
- top: 1.1in;
129
- bottom: 3.2in;
130
- left: 0.6in;
131
- right: 0.6in;
132
- }
133
- .layout-titlecard__image img {
134
- position: absolute;
135
- top: 20%;
136
- left: 0;
137
- height: 50%;
138
- width: auto;
139
- max-width: 100%;
140
- object-fit: contain;
141
- }
142
- .layout-titlecard__lockup {
143
- position: absolute;
144
- left: 0.6in;
145
- bottom: 1.1in;
146
- right: 0.6in;
147
- }
148
- .layout-titlecard__title {
149
- font-family: "Kapra Neue Expanded", Helvetica, Arial, sans-serif;
150
- font-size: 40pt;
151
- letter-spacing: 0.14em;
152
- text-transform: uppercase;
153
- margin: 0 0 0.25in;
154
- }
155
- .layout-titlecard__titlerule { display: block; width: 100%; height: 1px; margin-bottom: 0.18in; }
156
- .layout-titlecard__label {
157
- font-family: "Kapra Neue Expanded", Helvetica, Arial, sans-serif;
158
- font-size: 10pt;
159
- letter-spacing: 0.12em;
160
- text-transform: uppercase;
161
- margin: 0;
162
- }
163
- .layout-titlecard__date {
164
- font-family: "Kapra Neue Expanded", Helvetica, Arial, sans-serif;
165
- font-size: 10pt;
166
- letter-spacing: 0.12em;
167
- text-transform: uppercase;
168
- margin: 0.04in 0 0;
169
- }
170
-
171
- /* ---- document pages ----------------------------------------------------- */
172
-
173
- .pdoc-title {
174
- font-family: "Kapra Neue Expanded", Helvetica, Arial, sans-serif;
175
- font-size: 22pt;
176
- letter-spacing: 0.04em;
177
- text-transform: uppercase;
178
- margin: 0;
179
- height: 0.9in; /* = DOC_GEOM.titleBandIn; keep in sync */
180
- }
181
- /* Sub-page context line inside the title band (uppercase inherits from
182
- .pdoc-title). Sized to sit with the title within the fixed 0.9in band. */
183
- .pdoc-kicker {
184
- display: block;
185
- font-size: 9pt;
186
- letter-spacing: 0.12em;
187
- color: #999999;
188
- margin-bottom: 0.05in;
189
- }
190
- .pdoc-text { position: absolute; }
191
- .pdoc-h2, .pdoc-h3 {
192
- font-family: "Kapra Neue Expanded", Helvetica, Arial, sans-serif;
193
- text-transform: uppercase;
194
- letter-spacing: 0.04em;
195
- line-height: 1.3;
196
- margin: 0;
197
- }
198
- .pdoc-h2 { font-size: 14pt; }
199
- .pdoc-h3 { font-size: 11pt; letter-spacing: 0.08em; }
200
- .pdoc-prose {
201
- font-family: "Albra Grotesk", Helvetica, Arial, sans-serif;
202
- font-size: 12.5pt;
203
- line-height: 1.55;
204
- margin: 0;
205
- }
206
- /* Self-contained prose: renders identically with or without Tailwind
207
- preflight. The editor loads preflight but the /print route and the
208
- hidden measurer do not, so pin every element the upstream sanitizer
209
- allows to zero margins and give lists explicit markers — otherwise
210
- measured heights (and page counts) diverge across surfaces. */
211
- .pdoc-prose :where(p, ul, ol, li, blockquote, h1, h2, h3, h4, h5, h6, pre, hr) { margin: 0; }
212
- .pdoc-prose ul { list-style: disc; }
213
- .pdoc-prose ol { list-style: decimal; }
214
- .pdoc-prose ul, .pdoc-prose ol { padding-left: 0.25in; }
215
- .pdoc-img { position: absolute; overflow: hidden; }
216
- .pdoc-img img { width: 100%; height: 100%; display: block; }
217
- .pdoc-measure { position: fixed; left: -10000px; top: 0; visibility: hidden; pointer-events: none; }
@@ -1,132 +0,0 @@
1
- import type { DeckOwner } from "../schemas/deck-meta";
2
-
3
- /**
4
- * Who is asking. Built from the request's identity — a signed audience cookie
5
- * (with the credential username when the site has named credentials) or an
6
- * editor session — and deliberately NOT an Astro `locals`: the decision below
7
- * is pure, so it is testable the way `audiencePasses` is.
8
- *
9
- * `audience` (no username) is the env-mode standalone case: a shared password
10
- * per audience means there is no person to attribute to (ADR 0002).
11
- */
12
- export type DeckIdentity =
13
- | { kind: "viewer"; username: string; audience: string }
14
- | { kind: "editor"; userId: string }
15
- | { kind: "audience"; audience: string };
16
-
17
- /** The owner to record on a deck this identity is creating. */
18
- export function deckOwnerFor(identity: DeckIdentity): DeckOwner {
19
- switch (identity.kind) {
20
- case "viewer":
21
- return { kind: "viewer", username: identity.username, audience: identity.audience };
22
- case "editor":
23
- return { kind: "editor", userId: identity.userId };
24
- case "audience":
25
- return { kind: "audience", audience: identity.audience };
26
- }
27
- }
28
-
29
- /**
30
- * Ownership. Credential-keyed for viewers (the username is the person-level
31
- * key — an audience change must not hand a deck to someone else, and must not
32
- * take it away from its author), user-id-keyed for editors, and audience-keyed
33
- * for the env-mode fallback.
34
- *
35
- * An audience-owned deck is owned by everyone in that audience, which is
36
- * exactly the attribution loss ADR 0002 accepts for env-mode portals. The
37
- * reverse does not hold: an identity with no username cannot claim a
38
- * credential-owned deck.
39
- */
40
- export function isDeckOwner(owner: DeckOwner, identity: DeckIdentity): boolean {
41
- switch (owner.kind) {
42
- case "viewer":
43
- return identity.kind === "viewer" && identity.username === owner.username;
44
- case "editor":
45
- return identity.kind === "editor" && identity.userId === owner.userId;
46
- case "audience":
47
- return (identity.kind === "viewer" || identity.kind === "audience") && identity.audience === owner.audience;
48
- }
49
- }
50
-
51
- /**
52
- * May this identity see and change this deck? Owner, or any editor — editors
53
- * are super-admins over every deck on the site regardless of audience config
54
- * (feasibility decision 9).
55
- *
56
- * One function for read and for write because today they are the same rule:
57
- * a deck is private to its owner (Deck Visibility). Audience grants and Public
58
- * Links (#36/#37) widen READ only, and that is when the two split.
59
- */
60
- export function canAccessDeck(owner: DeckOwner, identity: DeckIdentity): boolean {
61
- return identity.kind === "editor" || isDeckOwner(owner, identity);
62
- }
63
-
64
- /**
65
- * May this identity CREATE a deck? Editors always (super-admins); everyone else
66
- * only from a deck-enabled audience — `deckBuilder.audiences`, strict opt-in
67
- * exactly like `chatbot.audiences`, so an empty list means no viewer may.
68
- *
69
- * Creation rights are AUDIENCE-keyed while ownership of the result is
70
- * CREDENTIAL-keyed (`deckOwnerFor`); the two deliberately do not share a key.
71
- * An `audience` identity (env-mode standalone, shared password, no named
72
- * credential) passes when its audience is listed: that is a deliberate opt-in
73
- * by the editor, and the resulting deck is audience-owned — the attribution
74
- * loss ADR 0002 accepts, not an accident. What the list prevents is the DEFAULT
75
- * audience being enabled implicitly, i.e. anyone holding the site password
76
- * writing to the client's repo.
77
- */
78
- export function canCreateDeck(identity: DeckIdentity, deckAudiences: readonly string[]): boolean {
79
- if (identity.kind === "editor") return true;
80
- return deckAudiences.includes(identity.audience);
81
- }
82
-
83
- /** FNV-1a, twice, at different offsets — 16 hex chars with no crypto import and
84
- * no async. Partitioning only; see `deckDraftScope` for why that is enough. */
85
- function digest(input: string): string {
86
- const half = (offset: number): string => {
87
- let hash = offset;
88
- for (let i = 0; i < input.length; i++) {
89
- hash ^= input.charCodeAt(i);
90
- hash = Math.imul(hash, 0x01000193) >>> 0;
91
- }
92
- return hash.toString(16).padStart(8, "0");
93
- };
94
- return half(0x811c9dc5) + half(0x7fffffff);
95
- }
96
-
97
- /**
98
- * The Deck Draft partition key for an identity.
99
- *
100
- * A Deck Draft lives in one browser until it is published (ADR 0002), and a
101
- * browser PROFILE is shared between people far more often than an account is.
102
- * Keyed by site alone, two viewers signing into the same portal on one machine
103
- * saw each other's unpublished drafts in the grid, could open and read them,
104
- * and could publish another person's never-published draft under their own
105
- * ownership; a signed-out user's drafts simply waited for whoever came next.
106
- *
107
- * The partition mirrors `deckOwnerFor` exactly, so a draft can only ever be
108
- * published under the identity that staged it.
109
- *
110
- * DIGESTED, not spelled out, because the value becomes an IndexedDB database
111
- * NAME and any script on the origin can enumerate those — a raw username there
112
- * would disclose who last used the browser. This is NOT a security boundary:
113
- * IndexedDB is per-origin and isolates nothing between people sharing a
114
- * profile. It bounds accidents, not somebody with the keyboard.
115
- *
116
- * Switching identity leaves the previous identity's database in place,
117
- * untouched and unread. That is deliberate — the alternative is destroying a
118
- * colleague's unpublished work on sign-in — so orphaned drafts persist until
119
- * the browser's own storage eviction takes them.
120
- */
121
- export function deckDraftScope(identity: DeckIdentity): string {
122
- switch (identity.kind) {
123
- case "viewer":
124
- return digest(`viewer:${identity.username}@${identity.audience}`);
125
- case "editor":
126
- return digest(`editor:${identity.userId}`);
127
- case "audience":
128
- // Env-mode standalone: a shared password means there is no person to
129
- // separate, and ownership degrades the same way (ADR 0002).
130
- return digest(`audience:${identity.audience}`);
131
- }
132
- }