@drawnagency/primitives 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (517) hide show
  1. package/README.md +28 -0
  2. package/dist/auth/cookies.d.ts +8 -0
  3. package/dist/auth/cookies.d.ts.map +1 -0
  4. package/dist/auth/cookies.js +44 -0
  5. package/dist/auth/errors.d.ts +8 -0
  6. package/dist/auth/errors.d.ts.map +1 -0
  7. package/dist/auth/errors.js +10 -0
  8. package/dist/auth/index.d.ts +5 -0
  9. package/dist/auth/index.d.ts.map +1 -0
  10. package/dist/auth/index.js +3 -0
  11. package/dist/auth/security.d.ts +13 -0
  12. package/dist/auth/security.d.ts.map +1 -0
  13. package/dist/auth/security.js +48 -0
  14. package/dist/auth/types.d.ts +64 -0
  15. package/dist/auth/types.d.ts.map +1 -0
  16. package/dist/auth/types.js +1 -0
  17. package/dist/components/brandguide/ColorSwatchSettings.d.ts +8 -0
  18. package/dist/components/brandguide/ColorSwatchSettings.d.ts.map +1 -0
  19. package/dist/components/brandguide/ColorSwatchSettings.js +10 -0
  20. package/dist/components/brandguide/Colors.d.ts +15 -0
  21. package/dist/components/brandguide/Colors.d.ts.map +1 -0
  22. package/dist/components/brandguide/Colors.js +79 -0
  23. package/dist/components/brandguide/DoDontList.d.ts +16 -0
  24. package/dist/components/brandguide/DoDontList.d.ts.map +1 -0
  25. package/dist/components/brandguide/DoDontList.js +22 -0
  26. package/dist/components/brandguide/DoDontMediaGrid.d.ts +16 -0
  27. package/dist/components/brandguide/DoDontMediaGrid.d.ts.map +1 -0
  28. package/dist/components/brandguide/DoDontMediaGrid.js +5 -0
  29. package/dist/components/editor/AudiencePicker.d.ts +9 -0
  30. package/dist/components/editor/AudiencePicker.d.ts.map +1 -0
  31. package/dist/components/editor/AudiencePicker.js +24 -0
  32. package/dist/components/editor/DeleteButton.d.ts +6 -0
  33. package/dist/components/editor/DeleteButton.d.ts.map +1 -0
  34. package/dist/components/editor/DeleteButton.js +6 -0
  35. package/dist/components/editor/DragHandle.d.ts +2 -0
  36. package/dist/components/editor/DragHandle.d.ts.map +1 -0
  37. package/dist/components/editor/DragHandle.js +8 -0
  38. package/dist/components/editor/InsertButton.d.ts +7 -0
  39. package/dist/components/editor/InsertButton.d.ts.map +1 -0
  40. package/dist/components/editor/InsertButton.js +7 -0
  41. package/dist/components/editor/SectionWrapper.d.ts +3 -0
  42. package/dist/components/editor/SectionWrapper.d.ts.map +1 -0
  43. package/dist/components/editor/SectionWrapper.js +135 -0
  44. package/dist/components/editor/SettingsButton.d.ts +6 -0
  45. package/dist/components/editor/SettingsButton.d.ts.map +1 -0
  46. package/dist/components/editor/SettingsButton.js +6 -0
  47. package/dist/components/editor/SettingsForm.d.ts +13 -0
  48. package/dist/components/editor/SettingsForm.d.ts.map +1 -0
  49. package/dist/components/editor/SettingsForm.js +64 -0
  50. package/dist/components/editor/StatusBadge.d.ts +7 -0
  51. package/dist/components/editor/StatusBadge.d.ts.map +1 -0
  52. package/dist/components/editor/StatusBadge.js +10 -0
  53. package/dist/components/editor/StatusPicker.d.ts +9 -0
  54. package/dist/components/editor/StatusPicker.d.ts.map +1 -0
  55. package/dist/components/editor/StatusPicker.js +30 -0
  56. package/dist/components/editor/index.d.ts +8 -0
  57. package/dist/components/editor/index.d.ts.map +1 -0
  58. package/dist/components/editor/index.js +7 -0
  59. package/dist/components/primitives/CustomParagraph.d.ts +2 -0
  60. package/dist/components/primitives/CustomParagraph.d.ts.map +1 -0
  61. package/dist/components/primitives/CustomParagraph.js +24 -0
  62. package/dist/components/primitives/EditableGrid.d.ts +19 -0
  63. package/dist/components/primitives/EditableGrid.d.ts.map +1 -0
  64. package/dist/components/primitives/EditableGrid.js +90 -0
  65. package/dist/components/primitives/EditableList.d.ts +15 -0
  66. package/dist/components/primitives/EditableList.d.ts.map +1 -0
  67. package/dist/components/primitives/EditableList.js +54 -0
  68. package/dist/components/primitives/EditablePlainText.d.ts +12 -0
  69. package/dist/components/primitives/EditablePlainText.d.ts.map +1 -0
  70. package/dist/components/primitives/EditablePlainText.js +52 -0
  71. package/dist/components/primitives/EditableRichText.d.ts +11 -0
  72. package/dist/components/primitives/EditableRichText.d.ts.map +1 -0
  73. package/dist/components/primitives/EditableRichText.js +86 -0
  74. package/dist/components/primitives/HeadingSection.d.ts +10 -0
  75. package/dist/components/primitives/HeadingSection.d.ts.map +1 -0
  76. package/dist/components/primitives/HeadingSection.js +7 -0
  77. package/dist/components/primitives/IconPicker.d.ts +9 -0
  78. package/dist/components/primitives/IconPicker.d.ts.map +1 -0
  79. package/dist/components/primitives/IconPicker.js +21 -0
  80. package/dist/components/primitives/LinkPopover.d.ts +8 -0
  81. package/dist/components/primitives/LinkPopover.d.ts.map +1 -0
  82. package/dist/components/primitives/LinkPopover.js +48 -0
  83. package/dist/components/primitives/MediaSettingsForms.d.ts +19 -0
  84. package/dist/components/primitives/MediaSettingsForms.d.ts.map +1 -0
  85. package/dist/components/primitives/MediaSettingsForms.js +42 -0
  86. package/dist/components/primitives/ResolvedMedia.d.ts +8 -0
  87. package/dist/components/primitives/ResolvedMedia.d.ts.map +1 -0
  88. package/dist/components/primitives/ResolvedMedia.js +9 -0
  89. package/dist/components/primitives/RichTextToolbar.d.ts +9 -0
  90. package/dist/components/primitives/RichTextToolbar.d.ts.map +1 -0
  91. package/dist/components/primitives/RichTextToolbar.js +26 -0
  92. package/dist/components/primitives/tiptap-presets.d.ts +4 -0
  93. package/dist/components/primitives/tiptap-presets.d.ts.map +1 -0
  94. package/dist/components/primitives/tiptap-presets.js +44 -0
  95. package/dist/components/primitives/useEditableCollection.d.ts +19 -0
  96. package/dist/components/primitives/useEditableCollection.d.ts.map +1 -0
  97. package/dist/components/primitives/useEditableCollection.js +61 -0
  98. package/dist/components/primitives/useEditablePlainText.d.ts +14 -0
  99. package/dist/components/primitives/useEditablePlainText.d.ts.map +1 -0
  100. package/dist/components/primitives/useEditablePlainText.js +27 -0
  101. package/dist/components/primitives/useEditableRichText.d.ts +16 -0
  102. package/dist/components/primitives/useEditableRichText.d.ts.map +1 -0
  103. package/dist/components/primitives/useEditableRichText.js +52 -0
  104. package/dist/components/sections/Button/CTAButton.d.ts +11 -0
  105. package/dist/components/sections/Button/CTAButton.d.ts.map +1 -0
  106. package/dist/components/sections/Button/CTAButton.js +18 -0
  107. package/dist/components/sections/Button/index.d.ts +11 -0
  108. package/dist/components/sections/Button/index.d.ts.map +1 -0
  109. package/dist/components/sections/Button/index.js +28 -0
  110. package/dist/components/sections/Colors/index.d.ts +22 -0
  111. package/dist/components/sections/Colors/index.d.ts.map +1 -0
  112. package/dist/components/sections/Colors/index.js +34 -0
  113. package/dist/components/sections/DoDontList/index.d.ts +21 -0
  114. package/dist/components/sections/DoDontList/index.d.ts.map +1 -0
  115. package/dist/components/sections/DoDontList/index.js +33 -0
  116. package/dist/components/sections/DoDontMediaGrid/index.d.ts +55 -0
  117. package/dist/components/sections/DoDontMediaGrid/index.d.ts.map +1 -0
  118. package/dist/components/sections/DoDontMediaGrid/index.js +41 -0
  119. package/dist/components/sections/IconList/IconList.d.ts +20 -0
  120. package/dist/components/sections/IconList/IconList.d.ts.map +1 -0
  121. package/dist/components/sections/IconList/IconList.js +131 -0
  122. package/dist/components/sections/IconList/IconListSettings.d.ts +11 -0
  123. package/dist/components/sections/IconList/IconListSettings.d.ts.map +1 -0
  124. package/dist/components/sections/IconList/IconListSettings.js +22 -0
  125. package/dist/components/sections/IconList/index.d.ts +17 -0
  126. package/dist/components/sections/IconList/index.d.ts.map +1 -0
  127. package/dist/components/sections/IconList/index.js +27 -0
  128. package/dist/components/sections/LinkHeading/index.d.ts +8 -0
  129. package/dist/components/sections/LinkHeading/index.d.ts.map +1 -0
  130. package/dist/components/sections/LinkHeading/index.js +15 -0
  131. package/dist/components/sections/MediaGrid/MediaGrid.d.ts +17 -0
  132. package/dist/components/sections/MediaGrid/MediaGrid.d.ts.map +1 -0
  133. package/dist/components/sections/MediaGrid/MediaGrid.js +62 -0
  134. package/dist/components/sections/MediaGrid/index.d.ts +55 -0
  135. package/dist/components/sections/MediaGrid/index.d.ts.map +1 -0
  136. package/dist/components/sections/MediaGrid/index.js +35 -0
  137. package/dist/components/sections/Prose/Prose.d.ts +8 -0
  138. package/dist/components/sections/Prose/Prose.d.ts.map +1 -0
  139. package/dist/components/sections/Prose/Prose.js +11 -0
  140. package/dist/components/sections/Prose/index.d.ts +8 -0
  141. package/dist/components/sections/Prose/index.d.ts.map +1 -0
  142. package/dist/components/sections/Prose/index.js +15 -0
  143. package/dist/components/sections/SectionLayout.d.ts +11 -0
  144. package/dist/components/sections/SectionLayout.d.ts.map +1 -0
  145. package/dist/components/sections/SectionLayout.js +15 -0
  146. package/dist/components/sections/SplitContent/SplitContent.d.ts +11 -0
  147. package/dist/components/sections/SplitContent/SplitContent.d.ts.map +1 -0
  148. package/dist/components/sections/SplitContent/SplitContent.js +31 -0
  149. package/dist/components/sections/SplitContent/SplitContentSettings.d.ts +9 -0
  150. package/dist/components/sections/SplitContent/SplitContentSettings.d.ts.map +1 -0
  151. package/dist/components/sections/SplitContent/SplitContentSettings.js +17 -0
  152. package/dist/components/sections/SplitContent/index.d.ts +13 -0
  153. package/dist/components/sections/SplitContent/index.d.ts.map +1 -0
  154. package/dist/components/sections/SplitContent/index.js +27 -0
  155. package/dist/components/sections/SubHeading/index.d.ts +9 -0
  156. package/dist/components/sections/SubHeading/index.d.ts.map +1 -0
  157. package/dist/components/sections/SubHeading/index.js +18 -0
  158. package/dist/components/sections/SubSubHeading/index.d.ts +9 -0
  159. package/dist/components/sections/SubSubHeading/index.d.ts.map +1 -0
  160. package/dist/components/sections/SubSubHeading/index.js +18 -0
  161. package/dist/components/sections/ViewRenderer.d.ts +8 -0
  162. package/dist/components/sections/ViewRenderer.d.ts.map +1 -0
  163. package/dist/components/sections/ViewRenderer.js +13 -0
  164. package/dist/components/sections/register-schemas.d.ts +2 -0
  165. package/dist/components/sections/register-schemas.d.ts.map +1 -0
  166. package/dist/components/sections/register-schemas.js +15 -0
  167. package/dist/components/sections/register.d.ts +2 -0
  168. package/dist/components/sections/register.d.ts.map +1 -0
  169. package/dist/components/sections/register.js +15 -0
  170. package/dist/components/shared/Button.d.ts +15 -0
  171. package/dist/components/shared/Button.d.ts.map +1 -0
  172. package/dist/components/shared/Button.js +27 -0
  173. package/dist/components/shared/Checkbox.d.ts +14 -0
  174. package/dist/components/shared/Checkbox.d.ts.map +1 -0
  175. package/dist/components/shared/Checkbox.js +10 -0
  176. package/dist/components/shared/ColorPicker.d.ts +9 -0
  177. package/dist/components/shared/ColorPicker.d.ts.map +1 -0
  178. package/dist/components/shared/ColorPicker.js +5 -0
  179. package/dist/components/shared/ErrorBoundary.d.ts +24 -0
  180. package/dist/components/shared/ErrorBoundary.d.ts.map +1 -0
  181. package/dist/components/shared/ErrorBoundary.js +30 -0
  182. package/dist/components/shared/FontPicker.d.ts +8 -0
  183. package/dist/components/shared/FontPicker.d.ts.map +1 -0
  184. package/dist/components/shared/FontPicker.js +190 -0
  185. package/dist/components/shared/FormLabel.d.ts +8 -0
  186. package/dist/components/shared/FormLabel.d.ts.map +1 -0
  187. package/dist/components/shared/FormLabel.js +5 -0
  188. package/dist/components/shared/IconButton.d.ts +12 -0
  189. package/dist/components/shared/IconButton.d.ts.map +1 -0
  190. package/dist/components/shared/IconButton.js +16 -0
  191. package/dist/components/shared/Input.d.ts +8 -0
  192. package/dist/components/shared/Input.d.ts.map +1 -0
  193. package/dist/components/shared/Input.js +8 -0
  194. package/dist/components/shared/Navigation.d.ts +9 -0
  195. package/dist/components/shared/Navigation.d.ts.map +1 -0
  196. package/dist/components/shared/Navigation.js +71 -0
  197. package/dist/components/shared/PasswordInput.d.ts +13 -0
  198. package/dist/components/shared/PasswordInput.d.ts.map +1 -0
  199. package/dist/components/shared/PasswordInput.js +11 -0
  200. package/dist/components/shared/Popover.d.ts +12 -0
  201. package/dist/components/shared/Popover.d.ts.map +1 -0
  202. package/dist/components/shared/Popover.js +33 -0
  203. package/dist/components/shared/PopoverItem.d.ts +7 -0
  204. package/dist/components/shared/PopoverItem.d.ts.map +1 -0
  205. package/dist/components/shared/PopoverItem.js +6 -0
  206. package/dist/components/shared/Select.d.ts +16 -0
  207. package/dist/components/shared/Select.d.ts.map +1 -0
  208. package/dist/components/shared/Select.js +9 -0
  209. package/dist/components/shared/Textarea.d.ts +8 -0
  210. package/dist/components/shared/Textarea.d.ts.map +1 -0
  211. package/dist/components/shared/Textarea.js +8 -0
  212. package/dist/components/shared/Toggle.d.ts +10 -0
  213. package/dist/components/shared/Toggle.d.ts.map +1 -0
  214. package/dist/components/shared/Toggle.js +5 -0
  215. package/dist/components/shared/Tooltip.d.ts +12 -0
  216. package/dist/components/shared/Tooltip.d.ts.map +1 -0
  217. package/dist/components/shared/Tooltip.js +8 -0
  218. package/dist/components/shared/icons.d.ts +17 -0
  219. package/dist/components/shared/icons.d.ts.map +1 -0
  220. package/dist/components/shared/icons.js +23 -0
  221. package/dist/components/shell/AudienceAddForm.d.ts +11 -0
  222. package/dist/components/shell/AudienceAddForm.d.ts.map +1 -0
  223. package/dist/components/shell/AudienceAddForm.js +43 -0
  224. package/dist/components/shell/AudienceRow.d.ts +15 -0
  225. package/dist/components/shell/AudienceRow.d.ts.map +1 -0
  226. package/dist/components/shell/AudienceRow.js +74 -0
  227. package/dist/components/shell/EditorContext.d.ts +14 -0
  228. package/dist/components/shell/EditorContext.d.ts.map +1 -0
  229. package/dist/components/shell/EditorContext.js +24 -0
  230. package/dist/components/shell/EditorLoginForm.d.ts +13 -0
  231. package/dist/components/shell/EditorLoginForm.d.ts.map +1 -0
  232. package/dist/components/shell/EditorLoginForm.js +46 -0
  233. package/dist/components/shell/EditorModal.d.ts +13 -0
  234. package/dist/components/shell/EditorModal.d.ts.map +1 -0
  235. package/dist/components/shell/EditorModal.js +43 -0
  236. package/dist/components/shell/EditorModalContext.d.ts +16 -0
  237. package/dist/components/shell/EditorModalContext.d.ts.map +1 -0
  238. package/dist/components/shell/EditorModalContext.js +20 -0
  239. package/dist/components/shell/EditorShell.d.ts +22 -0
  240. package/dist/components/shell/EditorShell.d.ts.map +1 -0
  241. package/dist/components/shell/EditorShell.js +483 -0
  242. package/dist/components/shell/MediaLibraryContext.d.ts +14 -0
  243. package/dist/components/shell/MediaLibraryContext.d.ts.map +1 -0
  244. package/dist/components/shell/MediaLibraryContext.js +5 -0
  245. package/dist/components/shell/MediaLibraryModal.d.ts +14 -0
  246. package/dist/components/shell/MediaLibraryModal.d.ts.map +1 -0
  247. package/dist/components/shell/MediaLibraryModal.js +145 -0
  248. package/dist/components/shell/ProcessingIndicator.d.ts +7 -0
  249. package/dist/components/shell/ProcessingIndicator.d.ts.map +1 -0
  250. package/dist/components/shell/ProcessingIndicator.js +15 -0
  251. package/dist/components/shell/SectionSkeleton.d.ts +9 -0
  252. package/dist/components/shell/SectionSkeleton.d.ts.map +1 -0
  253. package/dist/components/shell/SectionSkeleton.js +22 -0
  254. package/dist/components/shell/SectionTypePicker.d.ts +14 -0
  255. package/dist/components/shell/SectionTypePicker.d.ts.map +1 -0
  256. package/dist/components/shell/SectionTypePicker.js +15 -0
  257. package/dist/components/shell/SiteSettingsDisplay.d.ts +8 -0
  258. package/dist/components/shell/SiteSettingsDisplay.d.ts.map +1 -0
  259. package/dist/components/shell/SiteSettingsDisplay.js +28 -0
  260. package/dist/components/shell/SiteSettingsModal.d.ts +24 -0
  261. package/dist/components/shell/SiteSettingsModal.d.ts.map +1 -0
  262. package/dist/components/shell/SiteSettingsModal.js +40 -0
  263. package/dist/components/shell/SiteSettingsUsers.d.ts +9 -0
  264. package/dist/components/shell/SiteSettingsUsers.d.ts.map +1 -0
  265. package/dist/components/shell/SiteSettingsUsers.js +87 -0
  266. package/dist/components/shell/SiteSettingsViewerAccess.d.ts +9 -0
  267. package/dist/components/shell/SiteSettingsViewerAccess.d.ts.map +1 -0
  268. package/dist/components/shell/SiteSettingsViewerAccess.js +94 -0
  269. package/dist/components/shell/ViewerLoginForm.d.ts +10 -0
  270. package/dist/components/shell/ViewerLoginForm.d.ts.map +1 -0
  271. package/dist/components/shell/ViewerLoginForm.js +40 -0
  272. package/dist/data/google-fonts.json +7718 -0
  273. package/dist/hooks/index.d.ts +7 -0
  274. package/dist/hooks/index.d.ts.map +1 -0
  275. package/dist/hooks/index.js +6 -0
  276. package/dist/hooks/useActiveHeadings.d.ts +7 -0
  277. package/dist/hooks/useActiveHeadings.d.ts.map +1 -0
  278. package/dist/hooks/useActiveHeadings.js +99 -0
  279. package/dist/hooks/useEditorPersistence.d.ts +13 -0
  280. package/dist/hooks/useEditorPersistence.d.ts.map +1 -0
  281. package/dist/hooks/useEditorPersistence.js +73 -0
  282. package/dist/hooks/useEditorPublish.d.ts +24 -0
  283. package/dist/hooks/useEditorPublish.d.ts.map +1 -0
  284. package/dist/hooks/useEditorPublish.js +145 -0
  285. package/dist/hooks/useFocusTrap.d.ts +3 -0
  286. package/dist/hooks/useFocusTrap.d.ts.map +1 -0
  287. package/dist/hooks/useFocusTrap.js +51 -0
  288. package/dist/hooks/useMediaPipeline.d.ts +65 -0
  289. package/dist/hooks/useMediaPipeline.d.ts.map +1 -0
  290. package/dist/hooks/useMediaPipeline.js +253 -0
  291. package/dist/hooks/useResolvedMedia.d.ts +9 -0
  292. package/dist/hooks/useResolvedMedia.d.ts.map +1 -0
  293. package/dist/hooks/useResolvedMedia.js +39 -0
  294. package/dist/index.d.ts +5 -0
  295. package/dist/index.d.ts.map +1 -0
  296. package/dist/index.js +4 -0
  297. package/dist/lib/cn.d.ts +3 -0
  298. package/dist/lib/cn.d.ts.map +1 -0
  299. package/dist/lib/cn.js +5 -0
  300. package/dist/lib/contrast.d.ts +2 -0
  301. package/dist/lib/contrast.d.ts.map +1 -0
  302. package/dist/lib/contrast.js +11 -0
  303. package/dist/lib/dexie.d.ts +41 -0
  304. package/dist/lib/dexie.d.ts.map +1 -0
  305. package/dist/lib/dexie.js +236 -0
  306. package/dist/lib/events.d.ts +12 -0
  307. package/dist/lib/events.d.ts.map +1 -0
  308. package/dist/lib/events.js +15 -0
  309. package/dist/lib/google-fonts.d.ts +2 -0
  310. package/dist/lib/google-fonts.d.ts.map +1 -0
  311. package/dist/lib/google-fonts.js +11 -0
  312. package/dist/lib/grid.d.ts +2 -0
  313. package/dist/lib/grid.d.ts.map +1 -0
  314. package/dist/lib/grid.js +7 -0
  315. package/dist/lib/icons.d.ts +9 -0
  316. package/dist/lib/icons.d.ts.map +1 -0
  317. package/dist/lib/icons.js +27 -0
  318. package/dist/lib/index.d.ts +13 -0
  319. package/dist/lib/index.d.ts.map +1 -0
  320. package/dist/lib/index.js +12 -0
  321. package/dist/lib/loader.d.ts +28 -0
  322. package/dist/lib/loader.d.ts.map +1 -0
  323. package/dist/lib/loader.js +57 -0
  324. package/dist/lib/nav.d.ts +15 -0
  325. package/dist/lib/nav.d.ts.map +1 -0
  326. package/dist/lib/nav.js +58 -0
  327. package/dist/lib/registry.d.ts +110 -0
  328. package/dist/lib/registry.d.ts.map +1 -0
  329. package/dist/lib/registry.js +64 -0
  330. package/dist/lib/safeRedirect.d.ts +7 -0
  331. package/dist/lib/safeRedirect.d.ts.map +1 -0
  332. package/dist/lib/safeRedirect.js +11 -0
  333. package/dist/lib/sanitize.d.ts +2 -0
  334. package/dist/lib/sanitize.d.ts.map +1 -0
  335. package/dist/lib/sanitize.js +6 -0
  336. package/dist/lib/timestamp.d.ts +2 -0
  337. package/dist/lib/timestamp.d.ts.map +1 -0
  338. package/dist/lib/timestamp.js +28 -0
  339. package/dist/media/github.d.ts +3 -0
  340. package/dist/media/github.d.ts.map +1 -0
  341. package/dist/media/github.js +60 -0
  342. package/dist/media/index.d.ts +8 -0
  343. package/dist/media/index.d.ts.map +1 -0
  344. package/dist/media/index.js +9 -0
  345. package/dist/media/queue.d.ts +74 -0
  346. package/dist/media/queue.d.ts.map +1 -0
  347. package/dist/media/queue.js +116 -0
  348. package/dist/media/resolve.d.ts +14 -0
  349. package/dist/media/resolve.d.ts.map +1 -0
  350. package/dist/media/resolve.js +50 -0
  351. package/dist/media/types.d.ts +42 -0
  352. package/dist/media/types.d.ts.map +1 -0
  353. package/dist/media/types.js +1 -0
  354. package/dist/media/utils.d.ts +7 -0
  355. package/dist/media/utils.d.ts.map +1 -0
  356. package/dist/media/utils.js +41 -0
  357. package/dist/media/videoPoster.d.ts +6 -0
  358. package/dist/media/videoPoster.d.ts.map +1 -0
  359. package/dist/media/videoPoster.js +44 -0
  360. package/dist/media/worker.d.ts +36 -0
  361. package/dist/media/worker.d.ts.map +1 -0
  362. package/dist/media/worker.js +73 -0
  363. package/dist/schemas/audience.d.ts +12 -0
  364. package/dist/schemas/audience.d.ts.map +1 -0
  365. package/dist/schemas/audience.js +19 -0
  366. package/dist/schemas/auth.d.ts +36 -0
  367. package/dist/schemas/auth.d.ts.map +1 -0
  368. package/dist/schemas/auth.js +22 -0
  369. package/dist/schemas/index.d.ts +8 -0
  370. package/dist/schemas/index.d.ts.map +1 -0
  371. package/dist/schemas/index.js +7 -0
  372. package/dist/schemas/media-grid-options.d.ts +8 -0
  373. package/dist/schemas/media-grid-options.d.ts.map +1 -0
  374. package/dist/schemas/media-grid-options.js +7 -0
  375. package/dist/schemas/media.d.ts +63 -0
  376. package/dist/schemas/media.d.ts.map +1 -0
  377. package/dist/schemas/media.js +28 -0
  378. package/dist/schemas/sections.d.ts +12 -0
  379. package/dist/schemas/sections.d.ts.map +1 -0
  380. package/dist/schemas/sections.js +12 -0
  381. package/dist/schemas/shared.d.ts +98 -0
  382. package/dist/schemas/shared.d.ts.map +1 -0
  383. package/dist/schemas/shared.js +71 -0
  384. package/dist/schemas/site-config.d.ts +48 -0
  385. package/dist/schemas/site-config.d.ts.map +1 -0
  386. package/dist/schemas/site-config.js +26 -0
  387. package/package.json +57 -0
  388. package/src/auth/cookies.ts +51 -0
  389. package/src/auth/errors.ts +10 -0
  390. package/src/auth/index.ts +15 -0
  391. package/src/auth/security.ts +44 -0
  392. package/src/auth/types.ts +61 -0
  393. package/src/components/brandguide/ColorSwatchSettings.tsx +34 -0
  394. package/src/components/brandguide/Colors.tsx +277 -0
  395. package/src/components/brandguide/DoDontList.tsx +67 -0
  396. package/src/components/brandguide/DoDontMediaGrid.tsx +19 -0
  397. package/src/components/editor/AudiencePicker.tsx +103 -0
  398. package/src/components/editor/DeleteButton.tsx +18 -0
  399. package/src/components/editor/DragHandle.tsx +18 -0
  400. package/src/components/editor/InsertButton.tsx +22 -0
  401. package/src/components/editor/SectionWrapper.tsx +279 -0
  402. package/src/components/editor/SettingsButton.tsx +17 -0
  403. package/src/components/editor/SettingsForm.tsx +159 -0
  404. package/src/components/editor/StatusBadge.tsx +30 -0
  405. package/src/components/editor/StatusPicker.tsx +86 -0
  406. package/src/components/editor/index.ts +7 -0
  407. package/src/components/primitives/CustomParagraph.ts +25 -0
  408. package/src/components/primitives/EditableGrid.tsx +260 -0
  409. package/src/components/primitives/EditableList.tsx +160 -0
  410. package/src/components/primitives/EditablePlainText.tsx +99 -0
  411. package/src/components/primitives/EditableRichText.tsx +132 -0
  412. package/src/components/primitives/HeadingSection.tsx +25 -0
  413. package/src/components/primitives/IconPicker.tsx +60 -0
  414. package/src/components/primitives/LinkPopover.tsx +109 -0
  415. package/src/components/primitives/MediaSettingsForms.tsx +109 -0
  416. package/src/components/primitives/ResolvedMedia.tsx +36 -0
  417. package/src/components/primitives/RichTextToolbar.tsx +149 -0
  418. package/src/components/primitives/tiptap-presets.ts +50 -0
  419. package/src/components/primitives/useEditableCollection.ts +104 -0
  420. package/src/components/primitives/useEditablePlainText.ts +49 -0
  421. package/src/components/primitives/useEditableRichText.ts +74 -0
  422. package/src/components/sections/Button/CTAButton.tsx +50 -0
  423. package/src/components/sections/Button/index.tsx +37 -0
  424. package/src/components/sections/Colors/index.tsx +45 -0
  425. package/src/components/sections/DoDontList/index.tsx +43 -0
  426. package/src/components/sections/DoDontMediaGrid/index.tsx +53 -0
  427. package/src/components/sections/IconList/IconList.tsx +462 -0
  428. package/src/components/sections/IconList/IconListSettings.tsx +81 -0
  429. package/src/components/sections/IconList/index.tsx +36 -0
  430. package/src/components/sections/LinkHeading/index.tsx +24 -0
  431. package/src/components/sections/MediaGrid/MediaGrid.tsx +219 -0
  432. package/src/components/sections/MediaGrid/index.tsx +47 -0
  433. package/src/components/sections/Prose/Prose.tsx +34 -0
  434. package/src/components/sections/Prose/index.tsx +18 -0
  435. package/src/components/sections/SectionLayout.tsx +31 -0
  436. package/src/components/sections/SplitContent/SplitContent.tsx +77 -0
  437. package/src/components/sections/SplitContent/SplitContentSettings.tsx +42 -0
  438. package/src/components/sections/SplitContent/index.tsx +36 -0
  439. package/src/components/sections/SubHeading/index.tsx +27 -0
  440. package/src/components/sections/SubSubHeading/index.tsx +27 -0
  441. package/src/components/sections/ViewRenderer.tsx +29 -0
  442. package/src/components/sections/register-schemas.ts +16 -0
  443. package/src/components/sections/register.ts +16 -0
  444. package/src/components/shared/Button.tsx +77 -0
  445. package/src/components/shared/Checkbox.tsx +73 -0
  446. package/src/components/shared/ColorPicker.tsx +28 -0
  447. package/src/components/shared/ErrorBoundary.tsx +92 -0
  448. package/src/components/shared/FontPicker.tsx +300 -0
  449. package/src/components/shared/FormLabel.tsx +18 -0
  450. package/src/components/shared/IconButton.tsx +45 -0
  451. package/src/components/shared/Input.tsx +34 -0
  452. package/src/components/shared/Navigation.tsx +196 -0
  453. package/src/components/shared/PasswordInput.tsx +64 -0
  454. package/src/components/shared/Popover.tsx +61 -0
  455. package/src/components/shared/PopoverItem.tsx +24 -0
  456. package/src/components/shared/Select.tsx +60 -0
  457. package/src/components/shared/Textarea.tsx +34 -0
  458. package/src/components/shared/Toggle.tsx +35 -0
  459. package/src/components/shared/Tooltip.tsx +28 -0
  460. package/src/components/shared/icons.tsx +28 -0
  461. package/src/components/shell/AudienceAddForm.tsx +94 -0
  462. package/src/components/shell/AudienceRow.tsx +171 -0
  463. package/src/components/shell/EditorContext.tsx +44 -0
  464. package/src/components/shell/EditorLoginForm.tsx +130 -0
  465. package/src/components/shell/EditorModal.tsx +93 -0
  466. package/src/components/shell/EditorModalContext.tsx +40 -0
  467. package/src/components/shell/EditorShell.tsx +910 -0
  468. package/src/components/shell/MediaLibraryContext.tsx +19 -0
  469. package/src/components/shell/MediaLibraryModal.tsx +371 -0
  470. package/src/components/shell/ProcessingIndicator.tsx +84 -0
  471. package/src/components/shell/SectionSkeleton.tsx +54 -0
  472. package/src/components/shell/SectionTypePicker.tsx +47 -0
  473. package/src/components/shell/SiteSettingsDisplay.tsx +78 -0
  474. package/src/components/shell/SiteSettingsModal.tsx +129 -0
  475. package/src/components/shell/SiteSettingsUsers.tsx +190 -0
  476. package/src/components/shell/SiteSettingsViewerAccess.tsx +171 -0
  477. package/src/components/shell/ViewerLoginForm.tsx +95 -0
  478. package/src/data/google-fonts.json +7718 -0
  479. package/src/env.d.ts +3 -0
  480. package/src/hooks/index.ts +6 -0
  481. package/src/hooks/useActiveHeadings.ts +120 -0
  482. package/src/hooks/useEditorPersistence.ts +103 -0
  483. package/src/hooks/useEditorPublish.ts +187 -0
  484. package/src/hooks/useFocusTrap.ts +57 -0
  485. package/src/hooks/useMediaPipeline.ts +309 -0
  486. package/src/hooks/useResolvedMedia.ts +54 -0
  487. package/src/index.ts +4 -0
  488. package/src/lib/cn.ts +6 -0
  489. package/src/lib/contrast.ts +11 -0
  490. package/src/lib/dexie.ts +347 -0
  491. package/src/lib/events.ts +23 -0
  492. package/src/lib/google-fonts.ts +10 -0
  493. package/src/lib/grid.ts +7 -0
  494. package/src/lib/icons.ts +58 -0
  495. package/src/lib/index.ts +35 -0
  496. package/src/lib/loader.ts +81 -0
  497. package/src/lib/nav.ts +64 -0
  498. package/src/lib/registry.ts +202 -0
  499. package/src/lib/safeRedirect.ts +11 -0
  500. package/src/lib/sanitize.ts +6 -0
  501. package/src/lib/timestamp.ts +31 -0
  502. package/src/media/github.ts +72 -0
  503. package/src/media/index.ts +12 -0
  504. package/src/media/queue.ts +166 -0
  505. package/src/media/resolve.ts +63 -0
  506. package/src/media/types.ts +58 -0
  507. package/src/media/utils.ts +48 -0
  508. package/src/media/videoPoster.ts +53 -0
  509. package/src/media/worker.ts +122 -0
  510. package/src/schemas/audience.ts +28 -0
  511. package/src/schemas/auth.ts +31 -0
  512. package/src/schemas/index.ts +7 -0
  513. package/src/schemas/media-grid-options.ts +8 -0
  514. package/src/schemas/media.ts +40 -0
  515. package/src/schemas/sections.ts +24 -0
  516. package/src/schemas/shared.ts +96 -0
  517. package/src/schemas/site-config.ts +38 -0
@@ -0,0 +1,60 @@
1
+ import { useEffect, useRef } from "react";
2
+ import { cn } from "../../lib/cn";
3
+ import { curatedIcons } from "../../lib/icons";
4
+
5
+ interface IconPickerProps {
6
+ selected: string | null;
7
+ onSelect: (iconId: string | null) => void;
8
+ onClose: () => void;
9
+ showRemove?: boolean;
10
+ }
11
+
12
+ export function IconPicker({ selected, onSelect, onClose, showRemove = true }: IconPickerProps) {
13
+ const panelRef = useRef<HTMLDivElement>(null);
14
+
15
+ useEffect(() => {
16
+ function handleMouseDown(e: MouseEvent) {
17
+ if (panelRef.current && !panelRef.current.contains(e.target as Node)) {
18
+ onClose();
19
+ }
20
+ }
21
+ document.addEventListener("mousedown", handleMouseDown);
22
+ return () => document.removeEventListener("mousedown", handleMouseDown);
23
+ }, [onClose]);
24
+
25
+ return (
26
+ <div
27
+ ref={panelRef}
28
+ className="absolute z-50 w-56 rounded-lg border border-base-200 bg-base p-2 shadow-lg"
29
+ >
30
+ <div className="grid grid-cols-5 gap-1">
31
+ {curatedIcons.map((entry) => {
32
+ const Icon = entry.icon;
33
+ const isSelected = selected === entry.id;
34
+ return (
35
+ <button
36
+ key={entry.id}
37
+ aria-label={entry.label}
38
+ className={cn(
39
+ "cursor-pointer flex h-9 w-9 items-center justify-center rounded transition-colors",
40
+ "text-base-contrast-light hover:bg-base-accent hover:text-base-contrast",
41
+ isSelected && "ring-2 ring-primary bg-base-accent text-primary",
42
+ )}
43
+ onClick={() => onSelect(entry.id)}
44
+ >
45
+ <Icon size={18} />
46
+ </button>
47
+ );
48
+ })}
49
+ </div>
50
+ {showRemove && (
51
+ <button
52
+ className="cursor-pointer mt-2 w-full rounded px-2 py-1.5 text-center text-xs text-base-contrast-light hover:bg-base-accent hover:text-base-contrast"
53
+ onClick={() => onSelect(null)}
54
+ >
55
+ Remove icon
56
+ </button>
57
+ )}
58
+ </div>
59
+ );
60
+ }
@@ -0,0 +1,109 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import type { Editor } from "@tiptap/core";
3
+ import { Button } from "../shared/Button";
4
+
5
+ interface LinkPopoverProps {
6
+ editor: Editor;
7
+ onClose: () => void;
8
+ }
9
+
10
+ export function LinkPopover({ editor, onClose }: LinkPopoverProps) {
11
+ const existingAttrs = editor.getAttributes("link");
12
+ const [url, setUrl] = useState<string>(existingAttrs.href ?? "");
13
+ const [openInNewTab, setOpenInNewTab] = useState<boolean>(
14
+ existingAttrs.target === "_blank"
15
+ );
16
+ const inputRef = useRef<HTMLInputElement>(null);
17
+ const hasLink = Boolean(existingAttrs.href);
18
+
19
+ useEffect(() => {
20
+ inputRef.current?.focus();
21
+ }, []);
22
+
23
+ const handleApply = () => {
24
+ if (url.trim()) {
25
+ editor
26
+ .chain()
27
+ .focus()
28
+ .extendMarkRange("link")
29
+ .setLink({ href: url.trim(), target: openInNewTab ? "_blank" : null })
30
+ .run();
31
+ }
32
+ onClose();
33
+ };
34
+
35
+ const handleRemove = () => {
36
+ editor.chain().focus().extendMarkRange("link").unsetLink().run();
37
+ onClose();
38
+ };
39
+
40
+ const handleKeyDown = (e: React.KeyboardEvent) => {
41
+ if (e.key === "Enter") {
42
+ e.preventDefault();
43
+ handleApply();
44
+ } else if (e.key === "Escape") {
45
+ e.preventDefault();
46
+ onClose();
47
+ }
48
+ };
49
+
50
+ return (
51
+ <div
52
+ className="flex flex-col gap-2 rounded bg-base-accent p-3 shadow-lg"
53
+ onMouseDown={(e) => e.stopPropagation()}
54
+ >
55
+ <input
56
+ ref={inputRef}
57
+ type="url"
58
+ value={url}
59
+ onChange={(e) => setUrl(e.target.value)}
60
+ onKeyDown={handleKeyDown}
61
+ placeholder="https://example.com"
62
+ className="rounded border border-base-contrast/20 bg-base px-2 py-1 text-sm text-base-contrast placeholder:text-base-contrast/40 focus:outline-none focus:ring-1 focus:ring-primary"
63
+ />
64
+ <label className="flex cursor-pointer items-center gap-2 text-sm text-base-contrast">
65
+ <input
66
+ type="checkbox"
67
+ checked={openInNewTab}
68
+ onChange={(e) => setOpenInNewTab(e.target.checked)}
69
+ className="accent-primary"
70
+ />
71
+ Open in new tab
72
+ </label>
73
+ <div className="flex gap-2">
74
+ <Button
75
+ variant="brand"
76
+ size="sm"
77
+ onMouseDown={(e) => {
78
+ e.preventDefault();
79
+ handleApply();
80
+ }}
81
+ >
82
+ Apply
83
+ </Button>
84
+ {hasLink && (
85
+ <Button
86
+ variant="secondary"
87
+ size="sm"
88
+ onMouseDown={(e) => {
89
+ e.preventDefault();
90
+ handleRemove();
91
+ }}
92
+ >
93
+ Remove
94
+ </Button>
95
+ )}
96
+ <Button
97
+ variant="secondary"
98
+ size="sm"
99
+ onMouseDown={(e) => {
100
+ e.preventDefault();
101
+ onClose();
102
+ }}
103
+ >
104
+ Cancel
105
+ </Button>
106
+ </div>
107
+ </div>
108
+ );
109
+ }
@@ -0,0 +1,109 @@
1
+ import { useState, useRef } from "react";
2
+ import { cn } from "../../lib/cn";
3
+ import { Check, X } from "lucide-react";
4
+ import { Checkbox } from "../shared/Checkbox";
5
+ import { Select } from "../shared/Select";
6
+
7
+ export function ImageSettingsForm({
8
+ border,
9
+ objectFit,
10
+ onChange,
11
+ }: {
12
+ border?: boolean;
13
+ objectFit?: "cover" | "contain";
14
+ onChange: (update: { border?: boolean; objectFit?: "cover" | "contain" }) => void;
15
+ }) {
16
+ const [itemBorder, setItemBorder] = useState(border);
17
+ const [fit, setFit] = useState(objectFit);
18
+
19
+ return (
20
+ <div className="space-y-4">
21
+ <Select
22
+ label="Object fit"
23
+ value={fit ?? ""}
24
+ onChange={(v) => {
25
+ const val = (v || undefined) as "cover" | "contain" | undefined;
26
+ setFit(val);
27
+ onChange({ border: itemBorder, objectFit: val });
28
+ }}
29
+ options={[
30
+ { value: "", label: "Default (inherit from grid)" },
31
+ { value: "contain", label: "Contain" },
32
+ { value: "cover", label: "Crop to fill" },
33
+ ]}
34
+ />
35
+ <Checkbox
36
+ checked={itemBorder ?? false}
37
+ onChange={(v) => {
38
+ const val = v || undefined;
39
+ setItemBorder(val);
40
+ onChange({ border: val, objectFit: fit });
41
+ }}
42
+ label="Override border settings"
43
+ />
44
+ </div>
45
+ );
46
+ }
47
+
48
+ export function DoDontImageSettingsForm({
49
+ border,
50
+ objectFit,
51
+ doDont: initialDoDont,
52
+ onChange,
53
+ }: {
54
+ border?: boolean;
55
+ objectFit?: "cover" | "contain";
56
+ doDont: "do" | "dont";
57
+ onChange: (update: { border?: boolean; objectFit?: "cover" | "contain"; doDont: "do" | "dont" }) => void;
58
+ }) {
59
+ const [doDont, setDoDont] = useState<"do" | "dont">(initialDoDont);
60
+ const latestRef = useRef<{ border?: boolean; objectFit?: "cover" | "contain" }>({ border, objectFit });
61
+
62
+ const handleBaseChange = (updated: { border?: boolean; objectFit?: "cover" | "contain" }) => {
63
+ latestRef.current = updated;
64
+ onChange({ ...updated, doDont });
65
+ };
66
+
67
+ return (
68
+ <div className="space-y-4">
69
+ <ImageSettingsForm
70
+ border={border}
71
+ objectFit={objectFit}
72
+ onChange={handleBaseChange}
73
+ />
74
+ <hr className="border-base-200" />
75
+ <div className="space-y-2">
76
+ <button
77
+ className={cn(
78
+ "cursor-pointer flex w-full items-center gap-3 rounded-md border px-3 py-2.5 text-sm transition-colors",
79
+ doDont === "do"
80
+ ? "border-green-600 bg-base-accent text-base-contrast"
81
+ : "border-base-200 text-base-contrast-light hover:bg-base-accent",
82
+ )}
83
+ onClick={() => {
84
+ setDoDont("do");
85
+ onChange({ ...latestRef.current, doDont: "do" });
86
+ }}
87
+ >
88
+ <Check size={16} className="text-green-600" />
89
+ Do
90
+ </button>
91
+ <button
92
+ className={cn(
93
+ "cursor-pointer flex w-full items-center gap-3 rounded-md border px-3 py-2.5 text-sm transition-colors",
94
+ doDont === "dont"
95
+ ? "border-red-600 bg-base-accent text-base-contrast"
96
+ : "border-base-200 text-base-contrast-light hover:bg-base-accent",
97
+ )}
98
+ onClick={() => {
99
+ setDoDont("dont");
100
+ onChange({ ...latestRef.current, doDont: "dont" });
101
+ }}
102
+ >
103
+ <X size={16} className="text-red-600" />
104
+ Don't
105
+ </button>
106
+ </div>
107
+ </div>
108
+ );
109
+ }
@@ -0,0 +1,36 @@
1
+ import { cn } from "../../lib/cn";
2
+ import { ImageIcon } from "lucide-react";
3
+ import { useResolvedMedia } from "../../hooks/useResolvedMedia";
4
+
5
+ interface ResolvedMediaProps {
6
+ imageId: string | undefined;
7
+ className?: string;
8
+ imgClassName?: string;
9
+ }
10
+
11
+ export function ResolvedMedia({
12
+ imageId,
13
+ className,
14
+ imgClassName,
15
+ }: ResolvedMediaProps) {
16
+ const { src, srcset, poster, alt, kind } = useResolvedMedia(imageId);
17
+
18
+ const mediaClass = cn("h-full w-full", imgClassName);
19
+
20
+ return (
21
+ <div className={className}>
22
+ {src ? (
23
+ kind === "video" ? (
24
+ <video src={src} poster={poster} muted playsInline loop autoPlay className={mediaClass} />
25
+ ) : (
26
+ <img src={src} srcSet={srcset} alt={alt} className={mediaClass} />
27
+ )
28
+ ) : (
29
+ <div className="flex aspect-video h-full w-full flex-col items-center justify-center gap-1 rounded bg-base-accent text-base-contrast-light/50">
30
+ <ImageIcon size={24} />
31
+ <span className="text-sm">No Image</span>
32
+ </div>
33
+ )}
34
+ </div>
35
+ );
36
+ }
@@ -0,0 +1,149 @@
1
+ import { useState } from "react";
2
+ import type { Editor } from "@tiptap/core";
3
+ import { cn } from "../../lib/cn";
4
+ import { LinkPopover } from "./LinkPopover";
5
+ import type { PresetName } from "./tiptap-presets";
6
+
7
+ interface RichTextToolbarProps {
8
+ editor: Editor;
9
+ preset: PresetName;
10
+ }
11
+
12
+ interface ToolbarButtonProps {
13
+ isActive?: boolean;
14
+ onClick: () => void;
15
+ title: string;
16
+ children: React.ReactNode;
17
+ }
18
+
19
+ function ToolbarButton({ isActive, onClick, title, children }: ToolbarButtonProps) {
20
+ return (
21
+ <button
22
+ type="button"
23
+ title={title}
24
+ aria-label={title}
25
+ aria-pressed={isActive}
26
+ onMouseDown={(e) => {
27
+ e.preventDefault();
28
+ onClick();
29
+ }}
30
+ className={cn(
31
+ "cursor-pointer rounded px-2 py-1 text-sm font-medium transition-colors",
32
+ isActive
33
+ ? "bg-primary text-primary-contrast"
34
+ : "bg-base-accent text-base-contrast hover:opacity-80"
35
+ )}
36
+ >
37
+ {children}
38
+ </button>
39
+ );
40
+ }
41
+
42
+ function Separator() {
43
+ return <div className="mx-1 h-5 w-px bg-base-contrast/20" />;
44
+ }
45
+
46
+ export function RichTextToolbar({ editor, preset }: RichTextToolbarProps) {
47
+ const [showLinkPopover, setShowLinkPopover] = useState(false);
48
+
49
+ const paragraphClass = editor.getAttributes("paragraph").class as string | undefined;
50
+
51
+ const isLargeActive = paragraphClass === "large";
52
+ const isLeadInActive = paragraphClass === "lead-in";
53
+
54
+ const toggleParagraphClass = (cls: string) => {
55
+ const next = paragraphClass === cls ? null : cls;
56
+ editor.chain().focus().updateAttributes("paragraph", { class: next }).run();
57
+ };
58
+
59
+ return (
60
+ <div
61
+ data-testid="rich-text-toolbar"
62
+ className="flex items-center gap-1 rounded border border-base-contrast/10 bg-base-accent p-1 shadow-md"
63
+ >
64
+ {/* Inline formatting group */}
65
+ <ToolbarButton
66
+ isActive={editor.isActive("bold")}
67
+ onClick={() => editor.chain().focus().toggleBold().run()}
68
+ title="Bold"
69
+ >
70
+ <strong>B</strong>
71
+ </ToolbarButton>
72
+ <ToolbarButton
73
+ isActive={editor.isActive("italic")}
74
+ onClick={() => editor.chain().focus().toggleItalic().run()}
75
+ title="Italic"
76
+ >
77
+ <em>I</em>
78
+ </ToolbarButton>
79
+ <ToolbarButton
80
+ isActive={editor.isActive("underline")}
81
+ onClick={() => editor.chain().focus().toggleUnderline().run()}
82
+ title="Underline"
83
+ >
84
+ <span className="underline">U</span>
85
+ </ToolbarButton>
86
+
87
+ {preset === "rich" && (
88
+ <>
89
+ <Separator />
90
+
91
+ {/* Link */}
92
+ <div className="relative">
93
+ <ToolbarButton
94
+ isActive={editor.isActive("link") || showLinkPopover}
95
+ onClick={() => setShowLinkPopover((prev) => !prev)}
96
+ title="Link"
97
+ >
98
+ 🔗
99
+ </ToolbarButton>
100
+ {showLinkPopover && (
101
+ <div className="absolute left-0 top-full z-50 mt-1">
102
+ <LinkPopover
103
+ editor={editor}
104
+ onClose={() => setShowLinkPopover(false)}
105
+ />
106
+ </div>
107
+ )}
108
+ </div>
109
+
110
+ <Separator />
111
+
112
+ {/* Lists */}
113
+ <ToolbarButton
114
+ isActive={editor.isActive("bulletList")}
115
+ onClick={() => editor.chain().focus().toggleBulletList().run()}
116
+ title="Bullet List"
117
+ >
118
+ •≡
119
+ </ToolbarButton>
120
+ <ToolbarButton
121
+ isActive={editor.isActive("orderedList")}
122
+ onClick={() => editor.chain().focus().toggleOrderedList().run()}
123
+ title="Ordered List"
124
+ >
125
+ 1≡
126
+ </ToolbarButton>
127
+
128
+ <Separator />
129
+
130
+ {/* Paragraph variants */}
131
+ <ToolbarButton
132
+ isActive={isLargeActive}
133
+ onClick={() => toggleParagraphClass("large")}
134
+ title="Large Paragraph"
135
+ >
136
+ Lg
137
+ </ToolbarButton>
138
+ <ToolbarButton
139
+ isActive={isLeadInActive}
140
+ onClick={() => toggleParagraphClass("lead-in")}
141
+ title="Lead-In"
142
+ >
143
+ Li
144
+ </ToolbarButton>
145
+ </>
146
+ )}
147
+ </div>
148
+ );
149
+ }
@@ -0,0 +1,50 @@
1
+ import type { Extensions } from "@tiptap/core";
2
+ import StarterKit from "@tiptap/starter-kit";
3
+ import Underline from "@tiptap/extension-underline";
4
+ import Link from "@tiptap/extension-link";
5
+ import { CustomParagraph } from "./CustomParagraph";
6
+
7
+ /**
8
+ * Basic preset: inline formatting only.
9
+ * Bold, italic, underline. No lists, links, or structural elements.
10
+ */
11
+ const basic: Extensions = [
12
+ StarterKit.configure({
13
+ paragraph: false,
14
+ heading: false,
15
+ blockquote: false,
16
+ bulletList: false,
17
+ orderedList: false,
18
+ listItem: false,
19
+ codeBlock: false,
20
+ horizontalRule: false,
21
+ code: false,
22
+ strike: false,
23
+ }),
24
+ CustomParagraph,
25
+ Underline,
26
+ ];
27
+
28
+ /**
29
+ * Rich preset: full prose editing.
30
+ * Everything in basic + links, bullet lists, ordered lists,
31
+ * large paragraph and lead-in variants (via CustomParagraph).
32
+ */
33
+ const rich: Extensions = [
34
+ StarterKit.configure({
35
+ paragraph: false,
36
+ heading: false,
37
+ blockquote: false,
38
+ codeBlock: false,
39
+ horizontalRule: false,
40
+ code: false,
41
+ strike: false,
42
+ }),
43
+ CustomParagraph,
44
+ Underline,
45
+ Link.configure({ openOnClick: false }),
46
+ ];
47
+
48
+ export type PresetName = "basic" | "rich";
49
+
50
+ export const presets: Record<PresetName, Extensions> = { basic, rich };
@@ -0,0 +1,104 @@
1
+ import { useState, useCallback, useRef, useEffect } from "react";
2
+
3
+ interface WrappedItem<T> {
4
+ id: string;
5
+ data: T;
6
+ }
7
+
8
+ interface UseEditableCollectionOptions<T> {
9
+ items: T[];
10
+ onChange: (items: T[]) => void;
11
+ createItem: () => T;
12
+ }
13
+
14
+ interface UseEditableCollectionReturn<T> {
15
+ wrappedItems: WrappedItem<T>[];
16
+ onReorder: (fromIndex: number, toIndex: number) => void;
17
+ onAdd: () => void;
18
+ onInsert: (index: number) => void;
19
+ onRemove: (id: string) => void;
20
+ }
21
+
22
+ let nextId = 0;
23
+ function uid(): string {
24
+ return typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
25
+ ? crypto.randomUUID()
26
+ : `_id_${nextId++}_${Date.now()}`;
27
+ }
28
+
29
+ function wrapItems<T>(items: T[]): WrappedItem<T>[] {
30
+ return items.map((data) => ({ id: uid(), data }));
31
+ }
32
+
33
+ export function useEditableCollection<T>({
34
+ items,
35
+ onChange,
36
+ createItem,
37
+ }: UseEditableCollectionOptions<T>): UseEditableCollectionReturn<T> {
38
+ const [wrappedItems, setWrappedItems] = useState<WrappedItem<T>[]>(() =>
39
+ wrapItems(items)
40
+ );
41
+ const prevItemsRef = useRef(items);
42
+
43
+ useEffect(() => {
44
+ if (items === prevItemsRef.current) return;
45
+ prevItemsRef.current = items;
46
+
47
+ setWrappedItems((prev) => {
48
+ return items.map((data, i) => {
49
+ if (i < prev.length) {
50
+ return { id: prev[i].id, data };
51
+ }
52
+ return { id: uid(), data };
53
+ });
54
+ });
55
+ }, [items]);
56
+
57
+ const onReorder = useCallback(
58
+ (fromIndex: number, toIndex: number) => {
59
+ setWrappedItems((prev) => {
60
+ const next = [...prev];
61
+ const [moved] = next.splice(fromIndex, 1);
62
+ next.splice(toIndex, 0, moved);
63
+ onChange(next.map((w) => w.data));
64
+ return next;
65
+ });
66
+ },
67
+ [onChange]
68
+ );
69
+
70
+ const onAdd = useCallback(() => {
71
+ const newItem = createItem();
72
+ setWrappedItems((prev) => {
73
+ const next = [...prev, { id: uid(), data: newItem }];
74
+ onChange(next.map((w) => w.data));
75
+ return next;
76
+ });
77
+ }, [createItem, onChange]);
78
+
79
+ const onInsert = useCallback(
80
+ (index: number) => {
81
+ const newItem = createItem();
82
+ setWrappedItems((prev) => {
83
+ const next = [...prev];
84
+ next.splice(index, 0, { id: uid(), data: newItem });
85
+ onChange(next.map((w) => w.data));
86
+ return next;
87
+ });
88
+ },
89
+ [createItem, onChange],
90
+ );
91
+
92
+ const onRemove = useCallback(
93
+ (id: string) => {
94
+ setWrappedItems((prev) => {
95
+ const next = prev.filter((w) => w.id !== id);
96
+ onChange(next.map((w) => w.data));
97
+ return next;
98
+ });
99
+ },
100
+ [onChange]
101
+ );
102
+
103
+ return { wrappedItems, onReorder, onAdd, onInsert, onRemove };
104
+ }
@@ -0,0 +1,49 @@
1
+ import { useState, useEffect, useRef, useCallback } from "react";
2
+
3
+ interface UseEditablePlainTextOptions {
4
+ value: string;
5
+ onChange: (value: string) => void;
6
+ multiline?: boolean;
7
+ }
8
+
9
+ interface UseEditablePlainTextReturn {
10
+ value: string;
11
+ handleInput: (text: string) => void;
12
+ handleBlur: () => void;
13
+ shouldPreventEnter: boolean;
14
+ }
15
+
16
+ export function useEditablePlainText({
17
+ value: propValue,
18
+ onChange,
19
+ multiline = false,
20
+ }: UseEditablePlainTextOptions): UseEditablePlainTextReturn {
21
+ const [value, setValue] = useState(propValue);
22
+ const lastPropValue = useRef(propValue);
23
+
24
+ // Sync with external prop changes
25
+ useEffect(() => {
26
+ if (propValue !== lastPropValue.current) {
27
+ setValue(propValue);
28
+ lastPropValue.current = propValue;
29
+ }
30
+ }, [propValue]);
31
+
32
+ const handleInput = useCallback((text: string) => {
33
+ setValue(text);
34
+ }, []);
35
+
36
+ const handleBlur = useCallback(() => {
37
+ if (value !== propValue) {
38
+ onChange(value);
39
+ lastPropValue.current = value;
40
+ }
41
+ }, [value, propValue, onChange]);
42
+
43
+ return {
44
+ value,
45
+ handleInput,
46
+ handleBlur,
47
+ shouldPreventEnter: !multiline,
48
+ };
49
+ }