@drawnagency/primitives 0.1.110 → 0.1.112

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 (188) hide show
  1. package/dist/{chunk-3GAG6ZXB.js → chunk-3SZNUCXI.js} +8 -4
  2. package/dist/chunk-4I5OO5UB.js +785 -0
  3. package/dist/{chunk-ALMEC3XN.js → chunk-5KNXTMUC.js} +22 -18
  4. package/dist/{chunk-OUYB2GCW.js → chunk-BR4MIPTM.js} +1 -57
  5. package/dist/chunk-CXZ5YJG4.js +85 -0
  6. package/dist/{chunk-RA5IOFPY.js → chunk-DNY72JZN.js} +16 -38
  7. package/dist/{chunk-7MI3HCOF.js → chunk-EPWBPIV6.js} +10 -6
  8. package/dist/chunk-FV7JQRCN.js +94 -0
  9. package/dist/{chunk-UVIYI4J6.js → chunk-HSXMOS5Q.js} +15 -2
  10. package/dist/chunk-IIDKW7AV.js +41 -0
  11. package/dist/chunk-IINIZBQB.js +204 -0
  12. package/dist/chunk-MM7KZS5C.js +30 -0
  13. package/dist/{chunk-VCCNKTFD.js → chunk-OGGZ5AXT.js} +3 -31
  14. package/dist/{chunk-XQMDWXD6.js → chunk-P3ZNCHW7.js} +1 -1
  15. package/dist/chunk-QQ3S3P5P.js +38 -0
  16. package/dist/{chunk-ELQ4C677.js → chunk-TALRVOGO.js} +9 -44
  17. package/dist/chunk-VAYZJKVR.js +209 -0
  18. package/dist/{chunk-OTDA6KWL.js → chunk-VLPELTPI.js} +1 -1
  19. package/dist/{chunk-7WQJ57XG.js → chunk-VT3PYGVW.js} +68 -260
  20. package/dist/chunk-WUJ3XNFI.js +62 -0
  21. package/dist/{chunk-NXGIHSL5.js → chunk-Z2LO4PSQ.js} +11 -91
  22. package/dist/collab/rebase.js +1 -1
  23. package/dist/components/chat/ChatComposer.d.ts +25 -0
  24. package/dist/components/chat/ChatComposer.d.ts.map +1 -0
  25. package/dist/components/chat/ChatPanel.d.ts +5 -4
  26. package/dist/components/chat/ChatPanel.d.ts.map +1 -1
  27. package/dist/components/chat/markdown.d.ts +14 -0
  28. package/dist/components/chat/markdown.d.ts.map +1 -0
  29. package/dist/components/editor/index.js +16 -11
  30. package/dist/components/primitives/EditablePlainText.d.ts.map +1 -1
  31. package/dist/components/primitives/index.js +15 -10
  32. package/dist/components/sections/brand-guide.js +11 -9
  33. package/dist/components/sections/register-schemas.js +19 -14
  34. package/dist/components/sections/register.js +19 -14
  35. package/dist/components/shell/AudienceChips.d.ts +10 -0
  36. package/dist/components/shell/AudienceChips.d.ts.map +1 -0
  37. package/dist/components/shell/EditorShell.d.ts.map +1 -1
  38. package/dist/components/shell/SiteSettingsChatbot.d.ts.map +1 -1
  39. package/dist/components/shell/SiteSettingsDecks.d.ts +13 -0
  40. package/dist/components/shell/SiteSettingsDecks.d.ts.map +1 -0
  41. package/dist/components/shell/SiteSettingsModal.d.ts.map +1 -1
  42. package/dist/deck/DeckComponentView.d.ts +13 -0
  43. package/dist/deck/DeckComponentView.d.ts.map +1 -0
  44. package/dist/deck/DeckPlayer.d.ts +26 -0
  45. package/dist/deck/DeckPlayer.d.ts.map +1 -0
  46. package/dist/deck/DeckSlideView.d.ts +17 -0
  47. package/dist/deck/DeckSlideView.d.ts.map +1 -0
  48. package/dist/deck/DeckStage.d.ts +16 -0
  49. package/dist/deck/DeckStage.d.ts.map +1 -0
  50. package/dist/deck/components/DeckImage.d.ts +14 -0
  51. package/dist/deck/components/DeckImage.d.ts.map +1 -0
  52. package/dist/deck/components/DeckImageGrid.d.ts +27 -0
  53. package/dist/deck/components/DeckImageGrid.d.ts.map +1 -0
  54. package/dist/deck/components/DeckRichText.d.ts +22 -0
  55. package/dist/deck/components/DeckRichText.d.ts.map +1 -0
  56. package/dist/deck/components/DeckVideo.d.ts +15 -0
  57. package/dist/deck/components/DeckVideo.d.ts.map +1 -0
  58. package/dist/deck/editing.d.ts +50 -0
  59. package/dist/deck/editing.d.ts.map +1 -0
  60. package/dist/deck/flip.d.ts +11 -0
  61. package/dist/deck/flip.d.ts.map +1 -0
  62. package/dist/deck/index.d.ts +24 -0
  63. package/dist/deck/index.d.ts.map +1 -0
  64. package/dist/deck/index.js +53 -0
  65. package/dist/deck/layouts/CenteredTextLayout.d.ts +12 -0
  66. package/dist/deck/layouts/CenteredTextLayout.d.ts.map +1 -0
  67. package/dist/deck/layouts/ColumnSplitLayout.d.ts +14 -0
  68. package/dist/deck/layouts/ColumnSplitLayout.d.ts.map +1 -0
  69. package/dist/deck/layouts/FullBleedImageLayout.d.ts +14 -0
  70. package/dist/deck/layouts/FullBleedImageLayout.d.ts.map +1 -0
  71. package/dist/deck/media.d.ts +33 -0
  72. package/dist/deck/media.d.ts.map +1 -0
  73. package/dist/deck/tokens.d.ts +22 -0
  74. package/dist/deck/tokens.d.ts.map +1 -0
  75. package/dist/deck/useStageScale.d.ts +10 -0
  76. package/dist/deck/useStageScale.d.ts.map +1 -0
  77. package/dist/deck-builder/DeckBuilderApp.d.ts +30 -0
  78. package/dist/deck-builder/DeckBuilderApp.d.ts.map +1 -0
  79. package/dist/deck-builder/DeckBuilderTopBar.d.ts +24 -0
  80. package/dist/deck-builder/DeckBuilderTopBar.d.ts.map +1 -0
  81. package/dist/deck-builder/DeckChatPanel.d.ts +45 -0
  82. package/dist/deck-builder/DeckChatPanel.d.ts.map +1 -0
  83. package/dist/deck-builder/DeckEditor.d.ts +37 -0
  84. package/dist/deck-builder/DeckEditor.d.ts.map +1 -0
  85. package/dist/deck-builder/DeckLibrary.d.ts +22 -0
  86. package/dist/deck-builder/DeckLibrary.d.ts.map +1 -0
  87. package/dist/deck-builder/DeckSlideSurface.d.ts +31 -0
  88. package/dist/deck-builder/DeckSlideSurface.d.ts.map +1 -0
  89. package/dist/deck-builder/deck-api.d.ts +76 -0
  90. package/dist/deck-builder/deck-api.d.ts.map +1 -0
  91. package/dist/deck-builder/deck-chat-client.d.ts +44 -0
  92. package/dist/deck-builder/deck-chat-client.d.ts.map +1 -0
  93. package/dist/deck-builder/deck-chat-history.d.ts +10 -0
  94. package/dist/deck-builder/deck-chat-history.d.ts.map +1 -0
  95. package/dist/deck-builder/index.d.ts +13 -0
  96. package/dist/deck-builder/index.d.ts.map +1 -0
  97. package/dist/deck-builder/index.js +1505 -0
  98. package/dist/deck-builder/starter-deck.d.ts +23 -0
  99. package/dist/deck-builder/starter-deck.d.ts.map +1 -0
  100. package/dist/deck-builder/types.d.ts +29 -0
  101. package/dist/deck-builder/types.d.ts.map +1 -0
  102. package/dist/deck-builder/useSortableSlide.d.ts +29 -0
  103. package/dist/deck-builder/useSortableSlide.d.ts.map +1 -0
  104. package/dist/index.js +70 -63
  105. package/dist/lib/deck-access.d.ts +86 -0
  106. package/dist/lib/deck-access.d.ts.map +1 -0
  107. package/dist/lib/deck-access.js +58 -0
  108. package/dist/lib/deck-dexie.d.ts +88 -0
  109. package/dist/lib/deck-dexie.d.ts.map +1 -0
  110. package/dist/lib/deck-dexie.js +22 -0
  111. package/dist/lib/deck-edit.d.ts +67 -0
  112. package/dist/lib/deck-edit.d.ts.map +1 -0
  113. package/dist/lib/deck-media-assets.d.ts +27 -0
  114. package/dist/lib/deck-media-assets.d.ts.map +1 -0
  115. package/dist/lib/deck-media-assets.js +25 -0
  116. package/dist/lib/deck-media-refs.d.ts +35 -0
  117. package/dist/lib/deck-media-refs.d.ts.map +1 -0
  118. package/dist/lib/deck-media-refs.js +36 -0
  119. package/dist/lib/deck-tool.d.ts +25 -0
  120. package/dist/lib/deck-tool.d.ts.map +1 -0
  121. package/dist/lib/deck-tool.js +58 -0
  122. package/dist/lib/index-ops.js +2 -2
  123. package/dist/lib/index.js +17 -12
  124. package/dist/lib/nav-progress.d.ts +14 -0
  125. package/dist/lib/nav-progress.d.ts.map +1 -0
  126. package/dist/lib/nav.d.ts.map +1 -1
  127. package/dist/media/index.js +3 -3
  128. package/dist/schemas/deck-meta.d.ts +85 -0
  129. package/dist/schemas/deck-meta.d.ts.map +1 -0
  130. package/dist/schemas/deck-meta.js +64 -0
  131. package/dist/schemas/deck.d.ts +954 -0
  132. package/dist/schemas/deck.d.ts.map +1 -0
  133. package/dist/schemas/deck.js +65 -0
  134. package/dist/schemas/index.js +39 -37
  135. package/dist/schemas/site-config.d.ts +13 -1
  136. package/dist/schemas/site-config.d.ts.map +1 -1
  137. package/package.json +37 -1
  138. package/src/components/chat/ChatComposer.tsx +100 -0
  139. package/src/components/chat/ChatPanel.tsx +20 -69
  140. package/src/components/chat/markdown.ts +32 -0
  141. package/src/components/primitives/EditablePlainText.tsx +11 -1
  142. package/src/components/shell/AudienceChips.tsx +30 -0
  143. package/src/components/shell/EditorShell.tsx +12 -0
  144. package/src/components/shell/SiteSettingsChatbot.tsx +1 -18
  145. package/src/components/shell/SiteSettingsDecks.tsx +41 -0
  146. package/src/components/shell/SiteSettingsModal.tsx +11 -0
  147. package/src/deck/DeckComponentView.tsx +28 -0
  148. package/src/deck/DeckPlayer.tsx +166 -0
  149. package/src/deck/DeckSlideView.tsx +38 -0
  150. package/src/deck/DeckStage.tsx +39 -0
  151. package/src/deck/components/DeckImage.tsx +38 -0
  152. package/src/deck/components/DeckImageGrid.tsx +254 -0
  153. package/src/deck/components/DeckRichText.tsx +111 -0
  154. package/src/deck/components/DeckVideo.tsx +81 -0
  155. package/src/deck/editing.tsx +201 -0
  156. package/src/deck/flip.ts +70 -0
  157. package/src/deck/index.ts +30 -0
  158. package/src/deck/layouts/CenteredTextLayout.tsx +29 -0
  159. package/src/deck/layouts/ColumnSplitLayout.tsx +69 -0
  160. package/src/deck/layouts/FullBleedImageLayout.tsx +58 -0
  161. package/src/deck/media.tsx +46 -0
  162. package/src/deck/tokens.ts +57 -0
  163. package/src/deck/useStageScale.ts +44 -0
  164. package/src/deck-builder/DeckBuilderApp.tsx +572 -0
  165. package/src/deck-builder/DeckBuilderTopBar.tsx +62 -0
  166. package/src/deck-builder/DeckChatPanel.tsx +334 -0
  167. package/src/deck-builder/DeckEditor.tsx +258 -0
  168. package/src/deck-builder/DeckLibrary.tsx +195 -0
  169. package/src/deck-builder/DeckSlideSurface.tsx +55 -0
  170. package/src/deck-builder/deck-api.ts +177 -0
  171. package/src/deck-builder/deck-chat-client.ts +129 -0
  172. package/src/deck-builder/deck-chat-history.ts +62 -0
  173. package/src/deck-builder/index.ts +12 -0
  174. package/src/deck-builder/starter-deck.ts +86 -0
  175. package/src/deck-builder/types.ts +24 -0
  176. package/src/deck-builder/useSortableSlide.ts +102 -0
  177. package/src/lib/deck-access.ts +132 -0
  178. package/src/lib/deck-dexie.ts +190 -0
  179. package/src/lib/deck-edit.ts +196 -0
  180. package/src/lib/deck-media-assets.ts +57 -0
  181. package/src/lib/deck-media-refs.ts +67 -0
  182. package/src/lib/deck-tool.ts +95 -0
  183. package/src/lib/nav-progress.ts +108 -0
  184. package/src/lib/nav.ts +21 -1
  185. package/src/schemas/deck-meta.ts +88 -0
  186. package/src/schemas/deck.ts +478 -0
  187. package/src/schemas/index.ts +4 -0
  188. package/src/schemas/site-config.ts +24 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deck.d.ts","sourceRoot":"","sources":["../../src/schemas/deck.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import {
2
+ CenteredTextLayoutSchema,
3
+ ColumnSplitLayoutSchema,
4
+ DECK_COMPONENT_TYPES,
5
+ DECK_LAYOUT_TYPES,
6
+ DECK_MAX_SLIDES,
7
+ DECK_MEDIA_KINDS,
8
+ DECK_OVERLAY_PLACEMENTS,
9
+ DECK_SCHEMA_VERSION,
10
+ DECK_SPLIT_RATIOS,
11
+ DECK_STAGE_HEIGHT,
12
+ DECK_STAGE_WIDTH,
13
+ DECK_SURFACES,
14
+ DeckComponentSchema,
15
+ DeckIdSchema,
16
+ DeckImageGridItemSchema,
17
+ DeckImageGridSchema,
18
+ DeckImageSchema,
19
+ DeckLayoutSchema,
20
+ DeckMediaRefSchema,
21
+ DeckOverlayPanelSchema,
22
+ DeckRichTextSchema,
23
+ DeckSchema,
24
+ DeckSlideSchema,
25
+ DeckTextSpanSchema,
26
+ DeckVideoSchema,
27
+ FullBleedImageLayoutSchema,
28
+ PortalMediaRefSchema,
29
+ formatDeckIssues,
30
+ parseDeck,
31
+ serializeDeck
32
+ } from "../chunk-VAYZJKVR.js";
33
+ import "../chunk-JSBRDJBE.js";
34
+ export {
35
+ CenteredTextLayoutSchema,
36
+ ColumnSplitLayoutSchema,
37
+ DECK_COMPONENT_TYPES,
38
+ DECK_LAYOUT_TYPES,
39
+ DECK_MAX_SLIDES,
40
+ DECK_MEDIA_KINDS,
41
+ DECK_OVERLAY_PLACEMENTS,
42
+ DECK_SCHEMA_VERSION,
43
+ DECK_SPLIT_RATIOS,
44
+ DECK_STAGE_HEIGHT,
45
+ DECK_STAGE_WIDTH,
46
+ DECK_SURFACES,
47
+ DeckComponentSchema,
48
+ DeckIdSchema,
49
+ DeckImageGridItemSchema,
50
+ DeckImageGridSchema,
51
+ DeckImageSchema,
52
+ DeckLayoutSchema,
53
+ DeckMediaRefSchema,
54
+ DeckOverlayPanelSchema,
55
+ DeckRichTextSchema,
56
+ DeckSchema,
57
+ DeckSlideSchema,
58
+ DeckTextSpanSchema,
59
+ DeckVideoSchema,
60
+ FullBleedImageLayoutSchema,
61
+ PortalMediaRefSchema,
62
+ formatDeckIssues,
63
+ parseDeck,
64
+ serializeDeck
65
+ };
@@ -1,41 +1,6 @@
1
1
  import {
2
2
  MediaGridOptionsSchema
3
3
  } from "../chunk-B7VBSE5X.js";
4
- import {
5
- AudienceSchema,
6
- RoleSchema,
7
- SessionSchema,
8
- SiteUserSchema
9
- } from "../chunk-6P4CJCXB.js";
10
- import {
11
- AudienceColorSchema,
12
- AudienceNameSchema,
13
- NormalizedUsernameSchema,
14
- VIEWER_PASSWORD_MIN_LENGTH,
15
- ViewerCredentialSchema,
16
- ViewerPasswordSchema,
17
- ViewerUsernameSchema,
18
- normalizeUsername,
19
- slugifyAudienceName
20
- } from "../chunk-HBULBDGP.js";
21
- import {
22
- DEFAULT_LINK,
23
- LinkTargetSchema,
24
- LinkValueSchema,
25
- isSafeHref
26
- } from "../chunk-3ZRQ4G5F.js";
27
- import {
28
- DOCUMENT_BUCKET_MAX_BYTES,
29
- DOCUMENT_GIT_MAX_BYTES,
30
- DOCUMENT_KIND_TO_MIME,
31
- DOCUMENT_MANIFEST_PATH,
32
- DOCUMENT_MIME_TO_EXT,
33
- DocumentManifestEntrySchema,
34
- DocumentManifestSchema,
35
- documentBucketPath,
36
- documentFileUrl,
37
- documentGitPath
38
- } from "../chunk-BS5AQHZG.js";
39
4
  import {
40
5
  NavEntrySchema,
41
6
  NavFileSchema
@@ -45,10 +10,10 @@ import {
45
10
  getSectionSchema,
46
11
  getSectionSchemaForType
47
12
  } from "../chunk-DMCGN5AS.js";
48
- import "../chunk-FTPETWXE.js";
49
13
  import {
50
14
  ChatbotConfigSchema,
51
15
  ChatbotTaskSchema,
16
+ DeckBuilderConfigSchema,
52
17
  ImageManifestSchema,
53
18
  IndexSchema,
54
19
  MediaConfigSchema,
@@ -63,8 +28,26 @@ import {
63
28
  StatusSchema,
64
29
  VariantSchema,
65
30
  normalizeSiteIndex
66
- } from "../chunk-UVIYI4J6.js";
31
+ } from "../chunk-HSXMOS5Q.js";
67
32
  import "../chunk-4TZJZU2T.js";
33
+ import "../chunk-FTPETWXE.js";
34
+ import {
35
+ AudienceSchema,
36
+ RoleSchema,
37
+ SessionSchema,
38
+ SiteUserSchema
39
+ } from "../chunk-6P4CJCXB.js";
40
+ import {
41
+ AudienceColorSchema,
42
+ AudienceNameSchema,
43
+ NormalizedUsernameSchema,
44
+ VIEWER_PASSWORD_MIN_LENGTH,
45
+ ViewerCredentialSchema,
46
+ ViewerPasswordSchema,
47
+ ViewerUsernameSchema,
48
+ normalizeUsername,
49
+ slugifyAudienceName
50
+ } from "../chunk-HBULBDGP.js";
68
51
  import {
69
52
  FontNameSchema,
70
53
  HexColorSchema,
@@ -73,6 +56,24 @@ import {
73
56
  TextLineSchema
74
57
  } from "../chunk-GGXZZ77Z.js";
75
58
  import "../chunk-NSCT3AMV.js";
59
+ import {
60
+ DEFAULT_LINK,
61
+ LinkTargetSchema,
62
+ LinkValueSchema,
63
+ isSafeHref
64
+ } from "../chunk-3ZRQ4G5F.js";
65
+ import {
66
+ DOCUMENT_BUCKET_MAX_BYTES,
67
+ DOCUMENT_GIT_MAX_BYTES,
68
+ DOCUMENT_KIND_TO_MIME,
69
+ DOCUMENT_MANIFEST_PATH,
70
+ DOCUMENT_MIME_TO_EXT,
71
+ DocumentManifestEntrySchema,
72
+ DocumentManifestSchema,
73
+ documentBucketPath,
74
+ documentFileUrl,
75
+ documentGitPath
76
+ } from "../chunk-BS5AQHZG.js";
76
77
  import "../chunk-JSBRDJBE.js";
77
78
  export {
78
79
  AudienceColorSchema,
@@ -86,6 +87,7 @@ export {
86
87
  DOCUMENT_KIND_TO_MIME,
87
88
  DOCUMENT_MANIFEST_PATH,
88
89
  DOCUMENT_MIME_TO_EXT,
90
+ DeckBuilderConfigSchema,
89
91
  DocumentManifestEntrySchema,
90
92
  DocumentManifestSchema,
91
93
  FontNameSchema,
@@ -17,7 +17,7 @@ export declare const SectionMetaSchema: z.ZodObject<{
17
17
  access: z.ZodArray<z.ZodString>;
18
18
  }, z.core.$strip>;
19
19
  export type SectionMeta = z.infer<typeof SectionMetaSchema>;
20
- export declare const RESERVED_SLUGS: readonly ["edit", "api", "login", "set-password", "404", "present", "print", "audiences"];
20
+ export declare const RESERVED_SLUGS: readonly ["edit", "api", "login", "set-password", "404", "present", "print", "audiences", "decks"];
21
21
  export declare const PageStatusSchema: z.ZodEnum<{
22
22
  live: "live";
23
23
  archived: "archived";
@@ -454,6 +454,15 @@ export declare const ChatbotConfigSchema: z.ZodObject<{
454
454
  }, z.core.$strip>>>;
455
455
  }, z.core.$strip>;
456
456
  export type ChatbotConfig = z.infer<typeof ChatbotConfigSchema>;
457
+ /**
458
+ * Deck Builder config (ADR 0001/0002). An OBJECT rather than a bare audience
459
+ * array because `publicSharing` (#37) lands beside `audiences` — the same shape
460
+ * ChatbotConfigSchema has, for the same reason.
461
+ */
462
+ export declare const DeckBuilderConfigSchema: z.ZodObject<{
463
+ audiences: z.ZodDefault<z.ZodArray<z.ZodString>>;
464
+ }, z.core.$strip>;
465
+ export type DeckBuilderConfig = z.infer<typeof DeckBuilderConfigSchema>;
457
466
  export declare const SiteConfigSchema: z.ZodObject<{
458
467
  siteName: z.ZodDefault<z.ZodString>;
459
468
  primaryColor: z.ZodDefault<z.ZodString>;
@@ -516,6 +525,9 @@ export declare const SiteConfigSchema: z.ZodObject<{
516
525
  access: z.ZodDefault<z.ZodArray<z.ZodString>>;
517
526
  }, z.core.$strip>>>;
518
527
  }, z.core.$strip>>;
528
+ deckBuilder: z.ZodDefault<z.ZodObject<{
529
+ audiences: z.ZodDefault<z.ZodArray<z.ZodString>>;
530
+ }, z.core.$strip>>;
519
531
  }, z.core.$strip>;
520
532
  export type SiteConfig = z.infer<typeof SiteConfigSchema>;
521
533
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"site-config.d.ts","sourceRoot":"","sources":["../../src/schemas/site-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtC,eAAO,MAAM,YAAY;;;;;+FAExB,CAAC;AAEF,eAAO,MAAM,iBAAiB;;;;;;;;;iBAI5B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAQ5D,eAAO,MAAM,cAAc,2FAA4F,CAAC;AAWxH,eAAO,MAAM,gBAAgB;;;EAA+B,CAAC;AAE7D,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAUrB,CAAC;AAEH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAI9C,eAAO,MAAM,OAAO,QAA+B,CAAC;AAwCpD,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuEtB,CAAC;AAEL,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAAyE,CAAC;AAElG;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAC;AAEpF,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,CAE1D;AAMD,eAAO,MAAM,iBAAiB;;;;;iBAM5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;iBAiC5B,CAAC;AACL,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmE3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
1
+ {"version":3,"file":"site-config.d.ts","sourceRoot":"","sources":["../../src/schemas/site-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtC,eAAO,MAAM,YAAY;;;;;+FAExB,CAAC;AAEF,eAAO,MAAM,iBAAiB;;;;;;;;;iBAI5B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAQ5D,eAAO,MAAM,cAAc,oGAAqG,CAAC;AAWjI,eAAO,MAAM,gBAAgB;;;EAA+B,CAAC;AAE7D,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAUrB,CAAC;AAEH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAI9C,eAAO,MAAM,OAAO,QAA+B,CAAC;AAwCpD,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuEtB,CAAC;AAEL,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAAyE,CAAC;AAElG;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAC;AAEpF,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,CAE1D;AAMD,eAAO,MAAM,iBAAiB;;;;;iBAM5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;iBAiC5B,CAAC;AACL,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;GAIG;AACH,eAAO,MAAM,uBAAuB;;iBAWlC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsE3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawnagency/primitives",
3
- "version": "0.1.110",
3
+ "version": "0.1.112",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./package.json": "./package.json",
@@ -32,6 +32,14 @@
32
32
  "types": "./dist/schemas/documents.d.ts",
33
33
  "default": "./dist/schemas/documents.js"
34
34
  },
35
+ "./schemas/deck": {
36
+ "types": "./dist/schemas/deck.d.ts",
37
+ "default": "./dist/schemas/deck.js"
38
+ },
39
+ "./schemas/deck-meta": {
40
+ "types": "./dist/schemas/deck-meta.d.ts",
41
+ "default": "./dist/schemas/deck-meta.js"
42
+ },
35
43
  "./lib": {
36
44
  "types": "./dist/lib/index.d.ts",
37
45
  "default": "./dist/lib/index.js"
@@ -76,6 +84,26 @@
76
84
  "types": "./dist/lib/index-ops.d.ts",
77
85
  "default": "./dist/lib/index-ops.js"
78
86
  },
87
+ "./lib/deck-access": {
88
+ "types": "./dist/lib/deck-access.d.ts",
89
+ "default": "./dist/lib/deck-access.js"
90
+ },
91
+ "./lib/deck-media-refs": {
92
+ "types": "./dist/lib/deck-media-refs.d.ts",
93
+ "default": "./dist/lib/deck-media-refs.js"
94
+ },
95
+ "./lib/deck-tool": {
96
+ "types": "./dist/lib/deck-tool.d.ts",
97
+ "default": "./dist/lib/deck-tool.js"
98
+ },
99
+ "./lib/deck-dexie": {
100
+ "types": "./dist/lib/deck-dexie.d.ts",
101
+ "default": "./dist/lib/deck-dexie.js"
102
+ },
103
+ "./lib/deck-media-assets": {
104
+ "types": "./dist/lib/deck-media-assets.d.ts",
105
+ "default": "./dist/lib/deck-media-assets.js"
106
+ },
79
107
  "./lib/pdfjs-assets": {
80
108
  "types": "./dist/lib/pdfjs-assets.d.ts",
81
109
  "default": "./dist/lib/pdfjs-assets.js"
@@ -112,6 +140,14 @@
112
140
  "types": "./dist/document/index.d.ts",
113
141
  "default": "./dist/document/index.js"
114
142
  },
143
+ "./deck": {
144
+ "types": "./dist/deck/index.d.ts",
145
+ "default": "./dist/deck/index.js"
146
+ },
147
+ "./deck-builder": {
148
+ "types": "./dist/deck-builder/index.d.ts",
149
+ "default": "./dist/deck-builder/index.js"
150
+ },
115
151
  "./auth": {
116
152
  "types": "./dist/auth/index.d.ts",
117
153
  "default": "./dist/auth/index.js"
@@ -0,0 +1,100 @@
1
+ import { useEffect, useRef, type KeyboardEvent, type RefObject } from "react";
2
+ import { Square } from "lucide-react";
3
+ import { cn } from "../../lib/cn";
4
+
5
+ /**
6
+ * The composer every chat surface shares: an autosizing textarea, Enter-to-send
7
+ * with Shift+Enter for a newline, and a send button that becomes a stop button
8
+ * while a turn is in flight.
9
+ *
10
+ * Extracted for the Deck Builder's docked assistant (#35). Rendering it twice
11
+ * from one component is what keeps "Enter sends" from meaning two things on two
12
+ * panes of the same product.
13
+ */
14
+ export function ChatComposer({
15
+ value,
16
+ onChange,
17
+ onSend,
18
+ onStop,
19
+ busy,
20
+ placeholder,
21
+ disabled,
22
+ className,
23
+ textareaRef,
24
+ maxLength = 4000,
25
+ }: {
26
+ value: string;
27
+ onChange: (value: string) => void;
28
+ onSend: () => void;
29
+ /** Omitted where a turn cannot be interrupted; the button then just disables. */
30
+ onStop?: () => void;
31
+ busy: boolean;
32
+ placeholder: string;
33
+ disabled?: boolean;
34
+ className?: string;
35
+ /** Exposed so a panel can focus the composer (open, clear, suggestion click). */
36
+ textareaRef?: RefObject<HTMLTextAreaElement | null>;
37
+ maxLength?: number;
38
+ }) {
39
+ const ownRef = useRef<HTMLTextAreaElement>(null);
40
+ const ref = textareaRef ?? ownRef;
41
+
42
+ // Height tracks content up to the className's 4-line max-h cap, then scrolls
43
+ // internally. Keyed on `value` so the post-send clear snaps it back to one
44
+ // row. (field-sizing: content would do this in CSS alone, but it's still
45
+ // Chromium-only.)
46
+ useEffect(() => {
47
+ const el = ref.current;
48
+ if (!el) return;
49
+ el.style.height = "auto";
50
+ el.style.height = `${el.scrollHeight}px`;
51
+ }, [value, ref]);
52
+
53
+ function onKeyDown(e: KeyboardEvent<HTMLTextAreaElement>) {
54
+ if (e.key === "Enter" && !e.shiftKey) {
55
+ e.preventDefault();
56
+ onSend();
57
+ }
58
+ }
59
+
60
+ return (
61
+ <form
62
+ className={cn("flex shrink-0 items-end gap-2 border-t border-base-200 p-2", className)}
63
+ onSubmit={(e) => {
64
+ e.preventDefault();
65
+ onSend();
66
+ }}
67
+ >
68
+ <textarea
69
+ ref={ref}
70
+ rows={1}
71
+ value={value}
72
+ onChange={(e) => onChange(e.target.value)}
73
+ onKeyDown={onKeyDown}
74
+ placeholder={placeholder}
75
+ maxLength={maxLength}
76
+ disabled={disabled}
77
+ className="max-h-[5.625rem] min-w-0 flex-1 resize-none overflow-y-auto rounded-inner border border-base-200 bg-base px-2 py-1 text-sm text-base-contrast focus:bg-[color-mix(in_srgb,var(--color-base),var(--color-base-contrast)_5%)] focus:outline-none disabled:opacity-60"
78
+ />
79
+ {busy && onStop ? (
80
+ <button
81
+ type="button"
82
+ aria-label="Stop"
83
+ onClick={onStop}
84
+ className="flex cursor-pointer items-center rounded-inner bg-primary px-3 py-1.5 text-sm text-primary-contrast"
85
+ >
86
+ <Square size={12} fill="currentColor" aria-hidden="true" />
87
+ </button>
88
+ ) : (
89
+ <button
90
+ type="submit"
91
+ aria-label="Send"
92
+ disabled={busy || disabled}
93
+ className="rounded-inner bg-primary px-3 py-1 text-sm text-primary-contrast disabled:opacity-50"
94
+ >
95
+ →
96
+ </button>
97
+ )}
98
+ </form>
99
+ );
100
+ }
@@ -1,8 +1,9 @@
1
- import { useEffect, useRef, useState, type KeyboardEvent, type MouseEvent } from "react";
2
- import { marked } from "marked";
3
- import { ArrowDown, ChevronDown, ChevronRight, History, Pin, PinOff, SquarePen, Square, Trash2 } from "lucide-react";
1
+ import { useEffect, useRef, useState, type MouseEvent } from "react";
2
+ import { ArrowDown, ChevronDown, ChevronRight, History, Pin, PinOff, SquarePen, Trash2 } from "lucide-react";
4
3
  import { cn } from "../../lib/cn";
5
- import { ensureSanitizer, sanitizeHtml } from "../../lib/sanitize";
4
+ import { ensureSanitizer } from "../../lib/sanitize";
5
+ import { assistantHtml, MD_STYLES, USER_BUBBLE_CLASS } from "./markdown";
6
+ import { ChatComposer } from "./ChatComposer";
6
7
  import { resolveThinkingPhrases } from "../../lib/chatbot";
7
8
  import { lockBodyScroll } from "../../lib/scroll-lock";
8
9
  import { loadHistory, upsertConversation, setConversationPinned, removeConversation, type StoredConversation } from "./chat-history";
@@ -63,24 +64,10 @@ const ERROR_COPY: Record<string, string> = {
63
64
  };
64
65
  const RETRYABLE = new Set(["rate_limited", "upstream_error"]);
65
66
 
66
- /** Assistant markdown → sanitized HTML. Callers MUST gate on the sanitizer
67
- * having loaded (`mdReady`) — before then sanitizeHtml passes raw HTML
68
- * through, and model output is untrusted. Exported for tests. */
69
- export function assistantHtml(md: string): string {
70
- return sanitizeHtml(marked.parse(md, { async: false, gfm: true, breaks: true }) as string);
71
- }
72
-
73
- // Tailwind preflight makes h1–h6 inherit size/weight, so headings only need
74
- // weight + spacing to clamp to the message's text scale.
75
- const MD_STYLES =
76
- "[&_p:not(:first-child)]:mt-2 [&_ul]:mt-1 [&_ul]:list-disc [&_ul]:pl-4 " +
77
- "[&_ol]:mt-1 [&_ol]:list-decimal [&_ol]:pl-4 [&_li:not(:first-child)]:mt-1 " +
78
- "[&_a]:underline [&_a]:underline-offset-2 [&_a]:text-primary " +
79
- "[&_code]:rounded-inner [&_code]:bg-base-200 [&_code]:px-1 [&_code]:font-mono [&_code]:text-[0.8em] " +
80
- "[&_pre]:mt-2 [&_pre]:overflow-x-auto [&_pre]:rounded-inner [&_pre]:bg-base-200 [&_pre]:p-2 " +
81
- "[&_h1]:mt-2 [&_h1]:font-semibold [&_h2]:mt-2 [&_h2]:font-semibold " +
82
- "[&_h3]:mt-2 [&_h3]:font-semibold [&_h4]:mt-2 [&_h4]:font-semibold " +
83
- "[&_blockquote]:mt-2 [&_blockquote]:border-l-2 [&_blockquote]:border-base-200 [&_blockquote]:pl-2";
67
+ /** Re-exported from ./markdown, where the rendering rules now live so the deck
68
+ * assistant shares them. Kept here because tests and callers import it from
69
+ * this module. */
70
+ export { assistantHtml };
84
71
 
85
72
  /** Parse our /api/chat SSE frames out of a fetch body. Exported for tests. */
86
73
  export async function streamChat(
@@ -207,25 +194,6 @@ export default function ChatPanel({ open, closing, animateEnter, onClose, siteNa
207
194
  const listRef = useRef<HTMLDivElement>(null);
208
195
  const pinnedRef = useRef(true);
209
196
 
210
- // Autosize the composer: height tracks content up to the className's
211
- // 4-line max-h cap, then it scrolls internally. Keyed on `input` so the
212
- // post-send clear snaps it back to a single row. (field-sizing: content
213
- // would do this in CSS alone, but it's still Chromium-only.)
214
- useEffect(() => {
215
- const el = composerRef.current;
216
- if (!el) return;
217
- el.style.height = "auto";
218
- el.style.height = `${el.scrollHeight}px`;
219
- }, [input]);
220
-
221
- function onComposerKeyDown(e: KeyboardEvent<HTMLTextAreaElement>) {
222
- // Chat convention: Enter sends, Shift+Enter inserts a newline.
223
- if (e.key === "Enter" && !e.shiftKey) {
224
- e.preventDefault();
225
- void send();
226
- }
227
- }
228
-
229
197
  // The tasks prop can shrink live (the configurator's Preview-as filters it):
230
198
  // never keep a selected task that's no longer offered, or the next send would
231
199
  // assemble a task the previewed audience can't access.
@@ -589,7 +557,7 @@ export default function ChatPanel({ open, closing, animateEnter, onClose, siteNa
589
557
  )}
590
558
  {messages.map((m, i) =>
591
559
  m.role === "user" ? (
592
- <div key={i} className="ml-auto max-w-[85%] whitespace-pre-wrap rounded-outer bg-primary px-3 py-2 text-sm text-primary-contrast">
560
+ <div key={i} className={USER_BUBBLE_CLASS}>
593
561
  {m.content}
594
562
  </div>
595
563
  ) : m.content === "" ? (
@@ -657,33 +625,16 @@ export default function ChatPanel({ open, closing, animateEnter, onClose, siteNa
657
625
  )}
658
626
  </div>
659
627
  <p role="status" className="sr-only">{busy ? thinking : ""}</p>
660
- <form
661
- className={cn(
662
- "flex shrink-0 items-end gap-2 border-t border-base-200 p-2",
663
- mode === "sheet" && "pb-[max(0.5rem,env(safe-area-inset-bottom))]",
664
- )}
665
- onSubmit={(e) => { e.preventDefault(); void send(); }}
666
- >
667
- <textarea
668
- ref={composerRef}
669
- rows={1}
670
- value={input}
671
- onChange={(e) => setInput(e.target.value)}
672
- onKeyDown={onComposerKeyDown}
673
- placeholder={`Ask about ${siteName}…`}
674
- maxLength={4000}
675
- className="max-h-[5.625rem] min-w-0 flex-1 resize-none overflow-y-auto rounded-inner border border-base-200 bg-base px-2 py-1 text-sm text-base-contrast focus:outline-none focus:bg-[color-mix(in_srgb,var(--color-base),var(--color-base-contrast)_5%)]"
676
- />
677
- {busy ? (
678
- <button type="button" aria-label="Stop" onClick={stop}
679
- className="flex cursor-pointer items-center rounded-inner bg-primary px-3 py-1.5 text-sm text-primary-contrast">
680
- <Square size={12} fill="currentColor" aria-hidden="true" />
681
- </button>
682
- ) : (
683
- <button type="submit" aria-label="Send"
684
- className="rounded-inner bg-primary px-3 py-1 text-sm text-primary-contrast disabled:opacity-50">→</button>
685
- )}
686
- </form>
628
+ <ChatComposer
629
+ value={input}
630
+ onChange={setInput}
631
+ onSend={() => void send()}
632
+ onStop={stop}
633
+ busy={busy}
634
+ placeholder={`Ask about ${siteName}…`}
635
+ textareaRef={composerRef}
636
+ className={mode === "sheet" ? "pb-[max(0.5rem,env(safe-area-inset-bottom))]" : undefined}
637
+ />
687
638
  {isEditor && <p className="shrink-0 border-t border-base-200 px-3 py-1 text-[10px] text-base-contrast-light">Editor view — answers ground on published content.</p>}
688
639
  </div>
689
640
  </>
@@ -0,0 +1,32 @@
1
+ import { marked } from "marked";
2
+ import { sanitizeHtml } from "../../lib/sanitize";
3
+
4
+ /**
5
+ * The assistant-message treatment, shared by every chat surface (brand chat's
6
+ * ChatPanel and the Deck Builder's docked assistant). Extracted so a second
7
+ * chat pane is a second CALLER, not a second copy of the rendering rules —
8
+ * particularly the sanitizer gate below, which is a security property.
9
+ */
10
+
11
+ /** Assistant markdown → sanitized HTML. Callers MUST gate on the sanitizer
12
+ * having loaded (`mdReady`) — before then sanitizeHtml passes raw HTML
13
+ * through, and model output is untrusted. */
14
+ export function assistantHtml(md: string): string {
15
+ return sanitizeHtml(marked.parse(md, { async: false, gfm: true, breaks: true }) as string);
16
+ }
17
+
18
+ // Tailwind preflight makes h1–h6 inherit size/weight, so headings only need
19
+ // weight + spacing to clamp to the message's text scale.
20
+ export const MD_STYLES =
21
+ "[&_p:not(:first-child)]:mt-2 [&_ul]:mt-1 [&_ul]:list-disc [&_ul]:pl-4 " +
22
+ "[&_ol]:mt-1 [&_ol]:list-decimal [&_ol]:pl-4 [&_li:not(:first-child)]:mt-1 " +
23
+ "[&_a]:underline [&_a]:underline-offset-2 [&_a]:text-primary " +
24
+ "[&_code]:rounded-inner [&_code]:bg-base-200 [&_code]:px-1 [&_code]:font-mono [&_code]:text-[0.8em] " +
25
+ "[&_pre]:mt-2 [&_pre]:overflow-x-auto [&_pre]:rounded-inner [&_pre]:bg-base-200 [&_pre]:p-2 " +
26
+ "[&_h1]:mt-2 [&_h1]:font-semibold [&_h2]:mt-2 [&_h2]:font-semibold " +
27
+ "[&_h3]:mt-2 [&_h3]:font-semibold [&_h4]:mt-2 [&_h4]:font-semibold " +
28
+ "[&_blockquote]:mt-2 [&_blockquote]:border-l-2 [&_blockquote]:border-base-200 [&_blockquote]:pl-2";
29
+
30
+ /** The user's turn: the only side of a transcript that gets a filled bubble. */
31
+ export const USER_BUBBLE_CLASS =
32
+ "ml-auto max-w-[85%] whitespace-pre-wrap rounded-outer bg-primary px-3 py-2 text-sm text-primary-contrast";
@@ -29,6 +29,16 @@ export function EditablePlainText({
29
29
  ...htmlAttrs
30
30
  }: EditablePlainTextProps) {
31
31
  const ref = useRef<HTMLElement>(null);
32
+ // React must NOT own the text node while the field is being typed into. Every
33
+ // keystroke commits (see useEditablePlainText), the parent echoes the new
34
+ // value straight back, and React's children diff then rewrites the element's
35
+ // text — which destroys the live text node and collapses the caret to offset
36
+ // 0, so a heading comes out backwards. In edit mode we therefore render the
37
+ // text React knew at the moment editing began and hand ownership to the
38
+ // effect below, which writes only when the DOM has genuinely drifted.
39
+ const uncontrolled = useRef(value);
40
+ if (!isEditMode) uncontrolled.current = value;
41
+
32
42
  const {
33
43
  value: localValue,
34
44
  handleInput,
@@ -94,6 +104,6 @@ export function EditablePlainText({
94
104
  "data-placeholder": isEditMode && placeholder ? placeholder : undefined,
95
105
  ...htmlAttrs,
96
106
  },
97
- value,
107
+ isEditMode ? uncontrolled.current : value,
98
108
  );
99
109
  }
@@ -0,0 +1,30 @@
1
+ import { cn } from "../../lib/cn";
2
+ import { contrastColor } from "../../lib/contrast";
3
+ import type { Audience } from "../../auth/types";
4
+
5
+ /** Multi-select audience chips — the site-settings idiom for "who sees this".
6
+ * Shared by the chatbot and deck-builder opt-ins so the two gates, which have
7
+ * identical strict-opt-in semantics, cannot come to look like different rules. */
8
+ export function AudienceChips({
9
+ audiences,
10
+ selected,
11
+ onToggle,
12
+ }: {
13
+ audiences: Audience[];
14
+ selected: string[];
15
+ onToggle: (name: string) => void;
16
+ }) {
17
+ return (
18
+ <div className="flex flex-wrap gap-1">
19
+ {audiences.map((a) => (
20
+ <button key={a.name} type="button" onClick={() => onToggle(a.name)}
21
+ className={cn("rounded-full border px-2 py-0.5 text-xs",
22
+ selected.includes(a.name) ? "border-primary bg-primary text-primary-contrast" : "border-base-200 bg-base-accent text-base-contrast")}
23
+ style={a.color && selected.includes(a.name) ? { backgroundColor: a.color, borderColor: a.color, color: contrastColor(a.color) } : undefined}>
24
+ {a.displayName}
25
+ </button>
26
+ ))}
27
+ {audiences.length === 0 && <span className="text-xs text-base-contrast-light">No audiences configured</span>}
28
+ </div>
29
+ );
30
+ }
@@ -2025,6 +2025,18 @@ export default function EditorShell({
2025
2025
  </svg>
2026
2026
  </Fab>
2027
2027
  )}
2028
+ {/* Deck Builder (#34). Editors are super-admins over decks and are
2029
+ never gated by deckBuilder.audiences, so this is unconditional.
2030
+ An <a>, not a click handler: builder mode is its own route.
2031
+ bottom-52 clears the bottom-40 document FAB + gap. Taking over the
2032
+ editor top bar in place is #38 — this just goes there. */}
2033
+ <Fab href="/decks?next=%2Fedit" label="Build a deck" column="editor" className="bottom-52">
2034
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true">
2035
+ <rect x="3" y="4" width="18" height="12" rx="1" />
2036
+ <path d="M12 16v4" />
2037
+ <path d="M9 20h6" />
2038
+ </svg>
2039
+ </Fab>
2028
2040
  {showDocumentEditor && resolvedActivePage?.document?.enabled && (
2029
2041
  <Suspense fallback={<DocumentOverlayFallback />}>
2030
2042
  {/* Branch/history view (viewSections !== null → previewing the
@@ -1,8 +1,7 @@
1
1
  import { useEffect, useState } from "react";
2
2
  import { Button } from "../shared/Button";
3
3
  import { SubTabs } from "../shared/SubTabs";
4
- import { cn } from "../../lib/cn";
5
- import { contrastColor } from "../../lib/contrast";
4
+ import { AudienceChips } from "./AudienceChips";
6
5
  import ChatPanel from "../chat/ChatPanel";
7
6
  import type { SiteConfig, ChatbotConfig, ChatbotTask } from "../../schemas/site-config";
8
7
  import type { Audience } from "../../auth/types";
@@ -30,22 +29,6 @@ function slugify(label: string): string {
30
29
  return label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 32) || "task";
31
30
  }
32
31
 
33
- function AudienceChips({ audiences, selected, onToggle }: { audiences: Audience[]; selected: string[]; onToggle: (name: string) => void }) {
34
- return (
35
- <div className="flex flex-wrap gap-1">
36
- {audiences.map((a) => (
37
- <button key={a.name} type="button" onClick={() => onToggle(a.name)}
38
- className={cn("rounded-full border px-2 py-0.5 text-xs",
39
- selected.includes(a.name) ? "border-primary bg-primary text-primary-contrast" : "border-base-200 bg-base-accent text-base-contrast")}
40
- style={a.color && selected.includes(a.name) ? { backgroundColor: a.color, borderColor: a.color, color: contrastColor(a.color) } : undefined}>
41
- {a.displayName}
42
- </button>
43
- ))}
44
- {audiences.length === 0 && <span className="text-xs text-base-contrast-light">No audiences configured</span>}
45
- </div>
46
- );
47
- }
48
-
49
32
  export function SiteSettingsChatbot({ siteConfig, onSiteConfigChange, audiences, platformEnabled }: Props) {
50
33
  const chatbot = siteConfig.chatbot;
51
34
  const [newTaskLabel, setNewTaskLabel] = useState("");