betterstart-cli 0.0.1

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 (369) hide show
  1. package/README.md +5 -0
  2. package/dist/assets/adapters/next/integrations/mailchimp/actions/mailchimp.ts +38 -0
  3. package/dist/assets/adapters/next/integrations/mailchimp/integration.ts +33 -0
  4. package/dist/assets/adapters/next/integrations/r2/actions/r2.ts +77 -0
  5. package/dist/assets/adapters/next/integrations/r2/integration.ts +39 -0
  6. package/dist/assets/adapters/next/integrations/resend/actions/resend.ts +138 -0
  7. package/dist/assets/adapters/next/integrations/resend/integration.ts +36 -0
  8. package/dist/assets/adapters/next/plugins/blog/plugin.ts +17 -0
  9. package/dist/assets/adapters/next/plugins/blog/schemas/posts.json +169 -0
  10. package/dist/assets/adapters/next/templates/init/admin-globals.css +677 -0
  11. package/dist/assets/adapters/next/templates/init/api/auth-route.ts +3 -0
  12. package/dist/assets/adapters/next/templates/init/api/upload-route.ts +132 -0
  13. package/dist/assets/adapters/next/templates/init/components/layouts/admin-header.tsx +32 -0
  14. package/dist/assets/adapters/next/templates/init/components/layouts/admin-nav-link.tsx +23 -0
  15. package/dist/assets/adapters/next/templates/init/components/layouts/admin-providers.tsx +33 -0
  16. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-branding-rsc.tsx +8 -0
  17. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-branding-skeleton.tsx +10 -0
  18. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-nav-link-skeleton.tsx +11 -0
  19. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-nav-link.tsx +12 -0
  20. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-user-menu-skeleton.tsx +15 -0
  21. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-user-menu.tsx +90 -0
  22. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar.tsx +78 -0
  23. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sign-out.tsx +44 -0
  24. package/dist/assets/adapters/next/templates/init/components/layouts/content-skeleton.tsx +44 -0
  25. package/dist/assets/adapters/next/templates/init/components/layouts/sidebar-branding.tsx +41 -0
  26. package/dist/assets/adapters/next/templates/init/components/shared/data-table/data-table-pagination.tsx +139 -0
  27. package/dist/assets/adapters/next/templates/init/components/shared/data-table/data-table.tsx +236 -0
  28. package/dist/assets/adapters/next/templates/init/components/shared/delete-dialog.tsx +67 -0
  29. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/copyable-code-block.tsx +104 -0
  30. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/dev-mode-code-mirror.tsx +68 -0
  31. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/dev-mode-types.ts +75 -0
  32. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/lifecycle-hooks-tab.tsx +111 -0
  33. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/plain-code-fallback.tsx +11 -0
  34. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/snippets-tab.tsx +125 -0
  35. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode-integrate.tsx +108 -0
  36. package/dist/assets/adapters/next/templates/init/components/shared/entity-filters-bar.tsx +184 -0
  37. package/dist/assets/adapters/next/templates/init/components/shared/entity-metadata.tsx +93 -0
  38. package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-version-item.tsx +55 -0
  39. package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-version-restore-dialog.tsx +80 -0
  40. package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-versions-button.tsx +74 -0
  41. package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-versions-current-row.tsx +48 -0
  42. package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-versions-drawer.tsx +79 -0
  43. package/dist/assets/adapters/next/templates/init/components/shared/media/edit-media-dialog-content.tsx +222 -0
  44. package/dist/assets/adapters/next/templates/init/components/shared/media/edit-media-dialog.tsx +56 -0
  45. package/dist/assets/adapters/next/templates/init/components/shared/media/media-delete-dialog.tsx +83 -0
  46. package/dist/assets/adapters/next/templates/init/components/shared/media/media-delete-drawer.tsx +148 -0
  47. package/dist/assets/adapters/next/templates/init/components/shared/media/media-empty-state.tsx +45 -0
  48. package/dist/assets/adapters/next/templates/init/components/shared/media/media-filters-bar.tsx +129 -0
  49. package/dist/assets/adapters/next/templates/init/components/shared/media/media-gallery-dialog.tsx +182 -0
  50. package/dist/assets/adapters/next/templates/init/components/shared/media/media-grid-item.tsx +56 -0
  51. package/dist/assets/adapters/next/templates/init/components/shared/media/media-grid-pagination.tsx +114 -0
  52. package/dist/assets/adapters/next/templates/init/components/shared/media/media-grid.tsx +44 -0
  53. package/dist/assets/adapters/next/templates/init/components/shared/media/media-preview.tsx +69 -0
  54. package/dist/assets/adapters/next/templates/init/components/shared/media/media-url-importer.tsx +139 -0
  55. package/dist/assets/adapters/next/templates/init/components/shared/page-header.tsx +46 -0
  56. package/dist/assets/adapters/next/templates/init/components/shared/search-input.tsx +88 -0
  57. package/dist/assets/adapters/next/templates/init/components/shared/sort-indicator.tsx +24 -0
  58. package/dist/assets/adapters/next/templates/init/components/shared/sort-order-dialog.tsx +242 -0
  59. package/dist/assets/adapters/next/templates/init/components/shared/sort-order-drag-overlay-item.tsx +15 -0
  60. package/dist/assets/adapters/next/templates/init/components/shared/sort-order-item.tsx +32 -0
  61. package/dist/assets/adapters/next/templates/init/components/shared/sort-order-types.ts +9 -0
  62. package/dist/assets/adapters/next/templates/init/data/navigation.ts +43 -0
  63. package/dist/assets/adapters/next/templates/init/drizzle.config.ts +36 -0
  64. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-blockquote.ts +251 -0
  65. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-code-block.ts +258 -0
  66. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-color-highlight.ts +347 -0
  67. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor-media-insertion.ts +59 -0
  68. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor-mobile-toolbar.ts +17 -0
  69. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor-slash-menu.ts +116 -0
  70. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor-source-mode.tsx +638 -0
  71. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor-table-add-controls.ts +174 -0
  72. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor.ts +288 -0
  73. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-heading-dropdown-menu.ts +127 -0
  74. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-heading.ts +269 -0
  75. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-is-breakpoint.ts +32 -0
  76. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-link-popover.ts +278 -0
  77. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-list-dropdown-menu.ts +199 -0
  78. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-list.ts +290 -0
  79. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-mark.ts +199 -0
  80. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-menu-navigation.ts +221 -0
  81. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-tiptap-editor.ts +46 -0
  82. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-undo-redo.ts +169 -0
  83. package/dist/assets/adapters/next/templates/init/hooks/use-admin-theme.tsx +74 -0
  84. package/dist/assets/adapters/next/templates/init/hooks/use-copy-to-clipboard.ts +48 -0
  85. package/dist/assets/adapters/next/templates/init/hooks/use-dev-mode-integration.ts +43 -0
  86. package/dist/assets/adapters/next/templates/init/hooks/use-entity-versions.ts +32 -0
  87. package/dist/assets/adapters/next/templates/init/hooks/use-failed-image-url.ts +21 -0
  88. package/dist/assets/adapters/next/templates/init/hooks/use-local-storage.ts +46 -0
  89. package/dist/assets/adapters/next/templates/init/hooks/use-media-filters-bar.ts +72 -0
  90. package/dist/assets/adapters/next/templates/init/hooks/use-media.ts +102 -0
  91. package/dist/assets/adapters/next/templates/init/hooks/use-mobile.ts +19 -0
  92. package/dist/assets/adapters/next/templates/init/hooks/use-page-boundary-blur-visibility.ts +59 -0
  93. package/dist/assets/adapters/next/templates/init/hooks/use-page-scroll-threshold.ts +27 -0
  94. package/dist/assets/adapters/next/templates/init/hooks/use-table-utils.ts +315 -0
  95. package/dist/assets/adapters/next/templates/init/hooks/use-upload.ts +321 -0
  96. package/dist/assets/adapters/next/templates/init/hooks/use-users.ts +12 -0
  97. package/dist/assets/adapters/next/templates/init/lib/actions/auth/auth.ts +58 -0
  98. package/dist/assets/adapters/next/templates/init/lib/actions/auth/client.ts +12 -0
  99. package/dist/assets/adapters/next/templates/init/lib/actions/auth/middleware.ts +46 -0
  100. package/dist/assets/adapters/next/templates/init/lib/actions/email/form-delivery.ts +72 -0
  101. package/dist/assets/adapters/next/templates/init/lib/actions/email/index.ts +4 -0
  102. package/dist/assets/adapters/next/templates/init/lib/actions/email/is-email-delivery-configured.ts +5 -0
  103. package/dist/assets/adapters/next/templates/init/lib/actions/email/none.ts +24 -0
  104. package/dist/assets/adapters/next/templates/init/lib/actions/email/provider.ts +6 -0
  105. package/dist/assets/adapters/next/templates/init/lib/actions/email/send-email.ts +6 -0
  106. package/dist/assets/adapters/next/templates/init/lib/actions/email/send-password-reset-email.ts +10 -0
  107. package/dist/assets/adapters/next/templates/init/lib/actions/email/types.ts +25 -0
  108. package/dist/assets/adapters/next/templates/init/lib/actions/entity-versions/get-entity-versions.ts +48 -0
  109. package/dist/assets/adapters/next/templates/init/lib/actions/entity-versions/index.ts +10 -0
  110. package/dist/assets/adapters/next/templates/init/lib/actions/entity-versions/internal-create-entity-version.ts +43 -0
  111. package/dist/assets/adapters/next/templates/init/lib/actions/entity-versions/internal-delete-entity-versions.ts +19 -0
  112. package/dist/assets/adapters/next/templates/init/lib/actions/entity-versions/types.ts +26 -0
  113. package/dist/assets/adapters/next/templates/init/lib/actions/forms/get-all-form-settings.ts +21 -0
  114. package/dist/assets/adapters/next/templates/init/lib/actions/forms/get-form-settings.ts +27 -0
  115. package/dist/assets/adapters/next/templates/init/lib/actions/forms/index.ts +9 -0
  116. package/dist/assets/adapters/next/templates/init/lib/actions/forms/test-form-webhook.ts +40 -0
  117. package/dist/assets/adapters/next/templates/init/lib/actions/forms/types.ts +26 -0
  118. package/dist/assets/adapters/next/templates/init/lib/actions/forms/upsert-form-settings.ts +40 -0
  119. package/dist/assets/adapters/next/templates/init/lib/actions/media/create-media.ts +39 -0
  120. package/dist/assets/adapters/next/templates/init/lib/actions/media/delete-media-bulk.ts +29 -0
  121. package/dist/assets/adapters/next/templates/init/lib/actions/media/delete-media.ts +22 -0
  122. package/dist/assets/adapters/next/templates/init/lib/actions/media/get-media-by-id.ts +18 -0
  123. package/dist/assets/adapters/next/templates/init/lib/actions/media/get-media-by-ids.ts +25 -0
  124. package/dist/assets/adapters/next/templates/init/lib/actions/media/get-media.ts +71 -0
  125. package/dist/assets/adapters/next/templates/init/lib/actions/media/index.ts +14 -0
  126. package/dist/assets/adapters/next/templates/init/lib/actions/media/types.ts +31 -0
  127. package/dist/assets/adapters/next/templates/init/lib/actions/media/update-media.ts +35 -0
  128. package/dist/assets/adapters/next/templates/init/lib/actions/profile/index.ts +4 -0
  129. package/dist/assets/adapters/next/templates/init/lib/actions/profile/invalidate-users-cache.ts +14 -0
  130. package/dist/assets/adapters/next/templates/init/lib/actions/profile/is-email-configured.ts +7 -0
  131. package/dist/assets/adapters/next/templates/init/lib/actions/profile/types.ts +4 -0
  132. package/dist/assets/adapters/next/templates/init/lib/actions/profile/update-email.ts +57 -0
  133. package/dist/assets/adapters/next/templates/init/lib/actions/storage/index.ts +2 -0
  134. package/dist/assets/adapters/next/templates/init/lib/actions/storage/local.ts +31 -0
  135. package/dist/assets/adapters/next/templates/init/lib/actions/storage/provider.ts +6 -0
  136. package/dist/assets/adapters/next/templates/init/lib/actions/storage/save-upload.ts +11 -0
  137. package/dist/assets/adapters/next/templates/init/lib/actions/storage/types.ts +18 -0
  138. package/dist/assets/adapters/next/templates/init/lib/actions/upload/index.ts +9 -0
  139. package/dist/assets/adapters/next/templates/init/lib/actions/upload/types.ts +17 -0
  140. package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-file.ts +11 -0
  141. package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-files.ts +92 -0
  142. package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-image-from-url.ts +22 -0
  143. package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-media-from-url.ts +133 -0
  144. package/dist/assets/adapters/next/templates/init/lib/actions/users/create-user.ts +55 -0
  145. package/dist/assets/adapters/next/templates/init/lib/actions/users/delete-user.ts +24 -0
  146. package/dist/assets/adapters/next/templates/init/lib/actions/users/get-users.ts +49 -0
  147. package/dist/assets/adapters/next/templates/init/lib/actions/users/index.ts +12 -0
  148. package/dist/assets/adapters/next/templates/init/lib/actions/users/types.ts +43 -0
  149. package/dist/assets/adapters/next/templates/init/lib/actions/users/update-user-role.ts +28 -0
  150. package/dist/assets/adapters/next/templates/init/lib/db/client.ts +13 -0
  151. package/dist/assets/adapters/next/templates/init/lib/db/core/schema.ts +160 -0
  152. package/dist/assets/adapters/next/templates/init/lib/db/schema.ts +1 -0
  153. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/index.ts +19 -0
  154. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/register.local.ts +2 -0
  155. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/register.ts +9 -0
  156. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/registry.ts +55 -0
  157. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/runner.ts +51 -0
  158. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/types.ts +39 -0
  159. package/dist/assets/adapters/next/templates/init/pages/account-layout.tsx +11 -0
  160. package/dist/assets/adapters/next/templates/init/pages/account-shell-rsc.tsx +30 -0
  161. package/dist/assets/adapters/next/templates/init/pages/admin-layout.tsx +24 -0
  162. package/dist/assets/adapters/next/templates/init/pages/auth-gate-rsc.tsx +6 -0
  163. package/dist/assets/adapters/next/templates/init/pages/authenticated-layout.tsx +18 -0
  164. package/dist/assets/adapters/next/templates/init/pages/dashboard-page.tsx +121 -0
  165. package/dist/assets/adapters/next/templates/init/pages/forgot-password-form.tsx +124 -0
  166. package/dist/assets/adapters/next/templates/init/pages/forgot-password-page-skeleton.tsx +24 -0
  167. package/dist/assets/adapters/next/templates/init/pages/forgot-password-page.tsx +21 -0
  168. package/dist/assets/adapters/next/templates/init/pages/login-form.tsx +131 -0
  169. package/dist/assets/adapters/next/templates/init/pages/login-page-rsc.tsx +14 -0
  170. package/dist/assets/adapters/next/templates/init/pages/login-page-skeleton.tsx +26 -0
  171. package/dist/assets/adapters/next/templates/init/pages/login-page.tsx +21 -0
  172. package/dist/assets/adapters/next/templates/init/pages/media/media-page-content.tsx +273 -0
  173. package/dist/assets/adapters/next/templates/init/pages/media/media-page-skeleton.tsx +7 -0
  174. package/dist/assets/adapters/next/templates/init/pages/media/media-page.tsx +11 -0
  175. package/dist/assets/adapters/next/templates/init/pages/minimal-account-shell.tsx +25 -0
  176. package/dist/assets/adapters/next/templates/init/pages/profile/profile-form.tsx +281 -0
  177. package/dist/assets/adapters/next/templates/init/pages/profile/profile-page.tsx +31 -0
  178. package/dist/assets/adapters/next/templates/init/pages/reset-password-form.tsx +161 -0
  179. package/dist/assets/adapters/next/templates/init/pages/reset-password-page-skeleton.tsx +26 -0
  180. package/dist/assets/adapters/next/templates/init/pages/reset-password-page.tsx +21 -0
  181. package/dist/assets/adapters/next/templates/init/pages/users/columns.tsx +170 -0
  182. package/dist/assets/adapters/next/templates/init/pages/users/create-user-dialog.tsx +221 -0
  183. package/dist/assets/adapters/next/templates/init/pages/users/delete-user-dialog.tsx +172 -0
  184. package/dist/assets/adapters/next/templates/init/pages/users/edit-role-dialog.tsx +91 -0
  185. package/dist/assets/adapters/next/templates/init/pages/users/users-page-content.tsx +25 -0
  186. package/dist/assets/adapters/next/templates/init/pages/users/users-page-skeleton.tsx +7 -0
  187. package/dist/assets/adapters/next/templates/init/pages/users/users-page.tsx +11 -0
  188. package/dist/assets/adapters/next/templates/init/pages/users/users-table.tsx +221 -0
  189. package/dist/assets/adapters/next/templates/init/types/auth.ts +71 -0
  190. package/dist/assets/adapters/next/templates/init/types/index.ts +108 -0
  191. package/dist/assets/adapters/next/templates/init/types/navigation.ts +11 -0
  192. package/dist/assets/adapters/next/templates/init/types/table-meta.ts +14 -0
  193. package/dist/assets/adapters/next/templates/init/utils/auth/roles.ts +17 -0
  194. package/dist/assets/adapters/next/templates/init/utils/date/date.ts +90 -0
  195. package/dist/assets/adapters/next/templates/init/utils/dev-mode/code-block-height.ts +9 -0
  196. package/dist/assets/adapters/next/templates/init/utils/editor/content-editor-rich-extensions.ts +824 -0
  197. package/dist/assets/adapters/next/templates/init/utils/editor/content-editor.ts +316 -0
  198. package/dist/assets/adapters/next/templates/init/utils/editor/editor-view.ts +19 -0
  199. package/dist/assets/adapters/next/templates/init/utils/editor/markdown.ts +542 -0
  200. package/dist/assets/adapters/next/templates/init/utils/editor/node-attrs.ts +25 -0
  201. package/dist/assets/adapters/next/templates/init/utils/editor/slash-commands.ts +148 -0
  202. package/dist/assets/adapters/next/templates/init/utils/editor/source-media.ts +11 -0
  203. package/dist/assets/adapters/next/templates/init/utils/editor/table-add-controls.ts +91 -0
  204. package/dist/assets/adapters/next/templates/init/utils/editor/table-bubble.ts +172 -0
  205. package/dist/assets/adapters/next/templates/init/utils/editor/table-input.ts +5 -0
  206. package/dist/assets/adapters/next/templates/init/utils/editor/task-item.ts +19 -0
  207. package/dist/assets/adapters/next/templates/init/utils/editor/tiptap.ts +991 -0
  208. package/dist/assets/adapters/next/templates/init/utils/email/form-delivery.ts +104 -0
  209. package/dist/assets/adapters/next/templates/init/utils/media/fallback.ts +37 -0
  210. package/dist/assets/adapters/next/templates/init/utils/media/media.ts +91 -0
  211. package/dist/assets/adapters/next/templates/init/utils/media/query.ts +96 -0
  212. package/dist/assets/adapters/next/templates/init/utils/navigation/order.ts +6 -0
  213. package/dist/assets/adapters/next/templates/init/utils/navigation/sidebar.ts +26 -0
  214. package/dist/assets/adapters/next/templates/init/utils/page/boundary.ts +32 -0
  215. package/dist/assets/adapters/next/templates/init/utils/seo/seo.ts +90 -0
  216. package/dist/assets/adapters/next/templates/init/utils/shared/cn.ts +6 -0
  217. package/dist/assets/adapters/next/templates/init/utils/storage/local.ts +9 -0
  218. package/dist/assets/adapters/next/templates/init/utils/table/table.ts +10 -0
  219. package/dist/assets/adapters/next/templates/init/utils/text/text.ts +4 -0
  220. package/dist/assets/adapters/next/templates/init/utils/theme/system.ts +6 -0
  221. package/dist/assets/adapters/next/templates/init/utils/upload/remote.ts +55 -0
  222. package/dist/assets/adapters/next/templates/init/utils/upload/upload.ts +26 -0
  223. package/dist/assets/adapters/next/templates/init/utils/user/user.ts +11 -0
  224. package/dist/assets/adapters/next/templates/init/utils/validation/validation.ts +114 -0
  225. package/dist/assets/adapters/next/templates/init/utils/webhook/webhook.ts +28 -0
  226. package/dist/assets/shared-assets/react-admin/custom/content-editor/editor-toolbar.tsx +25 -0
  227. package/dist/assets/shared-assets/react-admin/custom/content-editor/horizontal-rule-button.tsx +22 -0
  228. package/dist/assets/shared-assets/react-admin/custom/content-editor/index.tsx +142 -0
  229. package/dist/assets/shared-assets/react-admin/custom/content-editor/main-toolbar-content.tsx +118 -0
  230. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-bubble-menu.tsx +80 -0
  231. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-button.tsx +22 -0
  232. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-editor-controls.tsx +117 -0
  233. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-popover-button.tsx +59 -0
  234. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-popover.tsx +6 -0
  235. package/dist/assets/shared-assets/react-admin/custom/content-editor/media-gallery-block.tsx +31 -0
  236. package/dist/assets/shared-assets/react-admin/custom/content-editor/mobile-toolbar-content.tsx +56 -0
  237. package/dist/assets/shared-assets/react-admin/custom/content-editor/mode-toggle-button.tsx +29 -0
  238. package/dist/assets/shared-assets/react-admin/custom/content-editor/remove-table-part-icon.tsx +17 -0
  239. package/dist/assets/shared-assets/react-admin/custom/content-editor/selection-bubble-menu.tsx +105 -0
  240. package/dist/assets/shared-assets/react-admin/custom/content-editor/slash-command-menu.tsx +65 -0
  241. package/dist/assets/shared-assets/react-admin/custom/content-editor/source-mode-dropdown-button.tsx +52 -0
  242. package/dist/assets/shared-assets/react-admin/custom/content-editor/source-mode.tsx +360 -0
  243. package/dist/assets/shared-assets/react-admin/custom/content-editor/table-add-controls.tsx +46 -0
  244. package/dist/assets/shared-assets/react-admin/custom/content-editor/table-bubble-menu.tsx +290 -0
  245. package/dist/assets/shared-assets/react-admin/custom/content-editor/table-button.tsx +116 -0
  246. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-extension/node-background-extension.ts +138 -0
  247. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/horizontal-rule-node/horizontal-rule-node-extension.ts +10 -0
  248. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/media-gallery-placeholder-node/index.tsx +1 -0
  249. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/media-gallery-placeholder-node/media-gallery-placeholder-node-extension.ts +117 -0
  250. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/media-gallery-placeholder-node/media-gallery-placeholder-node.tsx +63 -0
  251. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/removable-image-node/index.tsx +1 -0
  252. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/removable-image-node/removable-image-node-extension.ts +11 -0
  253. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/removable-image-node/removable-image-node.tsx +168 -0
  254. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/task-item-node/task-item-node-extension.tsx +142 -0
  255. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/blockquote-button/blockquote-button.tsx +114 -0
  256. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/blockquote-button/index.tsx +1 -0
  257. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/code-block-button/code-block-button.tsx +112 -0
  258. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/code-block-button/index.tsx +1 -0
  259. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-button/color-highlight-button.tsx +185 -0
  260. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-button/index.tsx +1 -0
  261. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover-button.tsx +40 -0
  262. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover-content.tsx +130 -0
  263. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover.tsx +98 -0
  264. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/highlight-color-button.tsx +75 -0
  265. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/highlight-colors.ts +24 -0
  266. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/index.tsx +1 -0
  267. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/source-color-highlight-popover.tsx +65 -0
  268. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/control-options.ts +27 -0
  269. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/heading-dropdown-menu/heading-dropdown-menu-item.tsx +35 -0
  270. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/heading-dropdown-menu/heading-dropdown-menu.tsx +119 -0
  271. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/heading-dropdown-menu/index.tsx +1 -0
  272. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/index.tsx +1 -0
  273. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-button.tsx +39 -0
  274. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-content.tsx +13 -0
  275. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-control-popover.tsx +90 -0
  276. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-main.tsx +96 -0
  277. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-popover.tsx +121 -0
  278. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/list-dropdown-menu/index.tsx +1 -0
  279. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/list-dropdown-menu/list-dropdown-menu-item.tsx +44 -0
  280. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/list-dropdown-menu/list-dropdown-menu.tsx +115 -0
  281. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/mark-button/index.tsx +1 -0
  282. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/mark-button/mark-button.tsx +117 -0
  283. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/undo-redo-button/index.tsx +1 -0
  284. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/undo-redo-button/undo-redo-button.tsx +115 -0
  285. package/dist/assets/shared-assets/react-admin/custom/date-picker.tsx +70 -0
  286. package/dist/assets/shared-assets/react-admin/custom/date-range-picker.tsx +216 -0
  287. package/dist/assets/shared-assets/react-admin/custom/dynamic-list-field.tsx +111 -0
  288. package/dist/assets/shared-assets/react-admin/custom/gallery-field.tsx +124 -0
  289. package/dist/assets/shared-assets/react-admin/custom/gallery-thumbnail.tsx +39 -0
  290. package/dist/assets/shared-assets/react-admin/custom/icon-picker.tsx +377 -0
  291. package/dist/assets/shared-assets/react-admin/custom/icons-column-skeleton.tsx +16 -0
  292. package/dist/assets/shared-assets/react-admin/custom/icons-data.ts +8 -0
  293. package/dist/assets/shared-assets/react-admin/custom/logo.tsx +115 -0
  294. package/dist/assets/shared-assets/react-admin/custom/media-gallery-field.tsx +182 -0
  295. package/dist/assets/shared-assets/react-admin/custom/page-skeleton.tsx +11 -0
  296. package/dist/assets/shared-assets/react-admin/custom/placeholder-card.tsx +34 -0
  297. package/dist/assets/shared-assets/react-admin/custom/placeholder.tsx +25 -0
  298. package/dist/assets/shared-assets/react-admin/custom/progressive-blur.tsx +62 -0
  299. package/dist/assets/shared-assets/react-admin/custom/sortable-gallery-item.tsx +68 -0
  300. package/dist/assets/shared-assets/react-admin/custom/upload-dropzone.tsx +107 -0
  301. package/dist/assets/shared-assets/react-admin/dependencies.ts +73 -0
  302. package/dist/assets/shared-assets/react-admin/schema.json +1670 -0
  303. package/dist/assets/shared-assets/react-admin/ui/accordion.tsx +86 -0
  304. package/dist/assets/shared-assets/react-admin/ui/alert-dialog.tsx +178 -0
  305. package/dist/assets/shared-assets/react-admin/ui/alert.tsx +72 -0
  306. package/dist/assets/shared-assets/react-admin/ui/aspect-ratio.tsx +9 -0
  307. package/dist/assets/shared-assets/react-admin/ui/avatar.tsx +95 -0
  308. package/dist/assets/shared-assets/react-admin/ui/badge.tsx +48 -0
  309. package/dist/assets/shared-assets/react-admin/ui/breadcrumb.tsx +99 -0
  310. package/dist/assets/shared-assets/react-admin/ui/button-group.tsx +76 -0
  311. package/dist/assets/shared-assets/react-admin/ui/button.tsx +66 -0
  312. package/dist/assets/shared-assets/react-admin/ui/calendar.tsx +184 -0
  313. package/dist/assets/shared-assets/react-admin/ui/card.tsx +94 -0
  314. package/dist/assets/shared-assets/react-admin/ui/carousel.tsx +239 -0
  315. package/dist/assets/shared-assets/react-admin/ui/chart.tsx +336 -0
  316. package/dist/assets/shared-assets/react-admin/ui/checkbox.tsx +28 -0
  317. package/dist/assets/shared-assets/react-admin/ui/collapsible.tsx +21 -0
  318. package/dist/assets/shared-assets/react-admin/ui/combobox.tsx +272 -0
  319. package/dist/assets/shared-assets/react-admin/ui/command.tsx +180 -0
  320. package/dist/assets/shared-assets/react-admin/ui/context-menu.tsx +243 -0
  321. package/dist/assets/shared-assets/react-admin/ui/dialog.tsx +141 -0
  322. package/dist/assets/shared-assets/react-admin/ui/direction.tsx +20 -0
  323. package/dist/assets/shared-assets/react-admin/ui/drawer.tsx +119 -0
  324. package/dist/assets/shared-assets/react-admin/ui/dropdown-menu.tsx +253 -0
  325. package/dist/assets/shared-assets/react-admin/ui/empty.tsx +93 -0
  326. package/dist/assets/shared-assets/react-admin/ui/field.tsx +234 -0
  327. package/dist/assets/shared-assets/react-admin/ui/form.tsx +172 -0
  328. package/dist/assets/shared-assets/react-admin/ui/hover-card.tsx +37 -0
  329. package/dist/assets/shared-assets/react-admin/ui/input-group.tsx +134 -0
  330. package/dist/assets/shared-assets/react-admin/ui/input-otp.tsx +85 -0
  331. package/dist/assets/shared-assets/react-admin/ui/input.tsx +18 -0
  332. package/dist/assets/shared-assets/react-admin/ui/item.tsx +180 -0
  333. package/dist/assets/shared-assets/react-admin/ui/kbd.tsx +26 -0
  334. package/dist/assets/shared-assets/react-admin/ui/label.tsx +20 -0
  335. package/dist/assets/shared-assets/react-admin/ui/menubar.tsx +259 -0
  336. package/dist/assets/shared-assets/react-admin/ui/native-select.tsx +54 -0
  337. package/dist/assets/shared-assets/react-admin/ui/navigation-menu.tsx +159 -0
  338. package/dist/assets/shared-assets/react-admin/ui/pagination.tsx +111 -0
  339. package/dist/assets/shared-assets/react-admin/ui/popover.tsx +75 -0
  340. package/dist/assets/shared-assets/react-admin/ui/progress.tsx +30 -0
  341. package/dist/assets/shared-assets/react-admin/ui/radio-group.tsx +43 -0
  342. package/dist/assets/shared-assets/react-admin/ui/resizable.tsx +41 -0
  343. package/dist/assets/shared-assets/react-admin/ui/scroll-area.tsx +54 -0
  344. package/dist/assets/shared-assets/react-admin/ui/select.tsx +183 -0
  345. package/dist/assets/shared-assets/react-admin/ui/separator.tsx +27 -0
  346. package/dist/assets/shared-assets/react-admin/ui/sheet.tsx +129 -0
  347. package/dist/assets/shared-assets/react-admin/ui/sidebar.tsx +688 -0
  348. package/dist/assets/shared-assets/react-admin/ui/skeleton.tsx +13 -0
  349. package/dist/assets/shared-assets/react-admin/ui/slider.tsx +53 -0
  350. package/dist/assets/shared-assets/react-admin/ui/sonner.tsx +45 -0
  351. package/dist/assets/shared-assets/react-admin/ui/spinner.tsx +15 -0
  352. package/dist/assets/shared-assets/react-admin/ui/switch.tsx +32 -0
  353. package/dist/assets/shared-assets/react-admin/ui/table.tsx +101 -0
  354. package/dist/assets/shared-assets/react-admin/ui/tabs.tsx +79 -0
  355. package/dist/assets/shared-assets/react-admin/ui/textarea.tsx +17 -0
  356. package/dist/assets/shared-assets/react-admin/ui/toggle-group.tsx +85 -0
  357. package/dist/assets/shared-assets/react-admin/ui/toggle.tsx +45 -0
  358. package/dist/assets/shared-assets/react-admin/ui/tooltip.tsx +51 -0
  359. package/dist/chunk-MUZQCVQA.js +306 -0
  360. package/dist/chunk-MUZQCVQA.js.map +1 -0
  361. package/dist/cli.d.ts +2 -0
  362. package/dist/cli.js +23437 -0
  363. package/dist/cli.js.map +1 -0
  364. package/dist/index.d.ts +90 -0
  365. package/dist/index.js +8 -0
  366. package/dist/index.js.map +1 -0
  367. package/dist/template-reader-YKWE2C7O.js +13 -0
  368. package/dist/template-reader-YKWE2C7O.js.map +1 -0
  369. package/package.json +74 -0
@@ -0,0 +1,199 @@
1
+ 'use client'
2
+
3
+ // --- Hooks ---
4
+ import { useTiptapEditor } from '@admin/hooks/content-editor/use-tiptap-editor'
5
+ // --- Lib ---
6
+ import { isNodeInSchema } from '@admin/utils/editor/tiptap'
7
+ import type { Editor } from '@tiptap/react'
8
+ // --- Icons ---
9
+ import { ListIcon, ListOrderedIcon, ListTodoIcon } from 'lucide-react'
10
+ import { useEffect, useMemo, useState } from 'react'
11
+
12
+ import { canToggleList, isListActive, type ListType, listIcons } from './use-list'
13
+
14
+ /**
15
+ * Configuration for the list dropdown menu functionality
16
+ */
17
+ export interface UseListDropdownMenuConfig {
18
+ /**
19
+ * The Tiptap editor instance.
20
+ */
21
+ editor?: Editor | null
22
+ /**
23
+ * The list types to display in the dropdown.
24
+ * @default ["bulletList", "orderedList", "taskList"]
25
+ */
26
+ types?: ListType[]
27
+ /**
28
+ * Whether the dropdown should be hidden when no list types are available
29
+ * @default false
30
+ */
31
+ hideWhenUnavailable?: boolean
32
+ }
33
+
34
+ export interface ListOption {
35
+ label: string
36
+ type: ListType
37
+ icon: React.ElementType
38
+ }
39
+
40
+ export const listOptions: ListOption[] = [
41
+ {
42
+ label: 'Bullet List',
43
+ type: 'bulletList',
44
+ icon: ListIcon
45
+ },
46
+ {
47
+ label: 'Ordered List',
48
+ type: 'orderedList',
49
+ icon: ListOrderedIcon
50
+ },
51
+ {
52
+ label: 'Task List',
53
+ type: 'taskList',
54
+ icon: ListTodoIcon
55
+ }
56
+ ]
57
+
58
+ export function canToggleAnyList(editor: Editor | null, listTypes: ListType[]): boolean {
59
+ if (!editor || !editor.isEditable) return false
60
+ return listTypes.some((type) => canToggleList(editor, type))
61
+ }
62
+
63
+ export function isAnyListActive(editor: Editor | null, listTypes: ListType[]): boolean {
64
+ if (!editor || !editor.isEditable) return false
65
+ return listTypes.some((type) => isListActive(editor, type))
66
+ }
67
+
68
+ export function getFilteredListOptions(availableTypes: ListType[]): typeof listOptions {
69
+ return listOptions.filter((option) => !option.type || availableTypes.includes(option.type))
70
+ }
71
+
72
+ export function shouldShowListDropdown(params: {
73
+ editor: Editor | null
74
+ listTypes: ListType[]
75
+ hideWhenUnavailable: boolean
76
+ listInSchema: boolean
77
+ canToggleAny: boolean
78
+ }): boolean {
79
+ const { editor, hideWhenUnavailable, listInSchema, canToggleAny } = params
80
+
81
+ if (!editor) return false
82
+
83
+ if (!hideWhenUnavailable) {
84
+ return true
85
+ }
86
+
87
+ if (!listInSchema) return false
88
+
89
+ if (!editor.isActive('code')) {
90
+ return canToggleAny
91
+ }
92
+
93
+ return true
94
+ }
95
+
96
+ /**
97
+ * Gets the currently active list type from the available types
98
+ */
99
+ export function getActiveListType(
100
+ editor: Editor | null,
101
+ availableTypes: ListType[]
102
+ ): ListType | undefined {
103
+ if (!editor || !editor.isEditable) return undefined
104
+ return availableTypes.find((type) => isListActive(editor, type))
105
+ }
106
+
107
+ /**
108
+ * Custom hook that provides list dropdown menu functionality for Tiptap editor
109
+ *
110
+ * @example
111
+ * ```tsx
112
+ * // Simple usage
113
+ * function MyListDropdown() {
114
+ * const {
115
+ * isVisible,
116
+ * activeType,
117
+ * isAnyActive,
118
+ * canToggleAny,
119
+ * filteredLists,
120
+ * } = useListDropdownMenu()
121
+ *
122
+ * if (!isVisible) return null
123
+ *
124
+ * return (
125
+ * <DropdownMenu>
126
+ * // dropdown content
127
+ * </DropdownMenu>
128
+ * )
129
+ * }
130
+ *
131
+ * // Advanced usage with configuration
132
+ * function MyAdvancedListDropdown() {
133
+ * const {
134
+ * isVisible,
135
+ * activeType,
136
+ * } = useListDropdownMenu({
137
+ * editor: myEditor,
138
+ * types: ["bulletList", "orderedList"],
139
+ * hideWhenUnavailable: true,
140
+ * })
141
+ *
142
+ * // component implementation
143
+ * }
144
+ * ```
145
+ */
146
+ export function useListDropdownMenu(config?: UseListDropdownMenuConfig) {
147
+ const {
148
+ editor: providedEditor,
149
+ types = ['bulletList', 'orderedList', 'taskList'],
150
+ hideWhenUnavailable = false
151
+ } = config || {}
152
+
153
+ const { editor } = useTiptapEditor(providedEditor)
154
+ const [isVisible, setIsVisible] = useState(true)
155
+
156
+ const listInSchema = types.some((type) => isNodeInSchema(type, editor))
157
+
158
+ const filteredLists = useMemo(() => getFilteredListOptions(types), [types])
159
+
160
+ const canToggleAny = canToggleAnyList(editor, types)
161
+ const isAnyActive = isAnyListActive(editor, types)
162
+ const activeType = getActiveListType(editor, types)
163
+ const activeList = filteredLists.find((option) => option.type === activeType)
164
+
165
+ useEffect(() => {
166
+ if (!editor) return
167
+
168
+ const handleSelectionUpdate = () => {
169
+ setIsVisible(
170
+ shouldShowListDropdown({
171
+ editor,
172
+ listTypes: types,
173
+ hideWhenUnavailable,
174
+ listInSchema,
175
+ canToggleAny
176
+ })
177
+ )
178
+ }
179
+
180
+ handleSelectionUpdate()
181
+
182
+ editor.on('selectionUpdate', handleSelectionUpdate)
183
+
184
+ return () => {
185
+ editor.off('selectionUpdate', handleSelectionUpdate)
186
+ }
187
+ }, [canToggleAny, editor, hideWhenUnavailable, listInSchema, types])
188
+
189
+ return {
190
+ isVisible,
191
+ activeType,
192
+ isActive: isAnyActive,
193
+ canToggle: canToggleAny,
194
+ types,
195
+ filteredLists,
196
+ label: 'List',
197
+ Icon: activeList ? listIcons[activeList.type] : ListIcon
198
+ }
199
+ }
@@ -0,0 +1,290 @@
1
+ 'use client'
2
+
3
+ // --- Hooks ---
4
+ import { useTiptapEditor } from '@admin/hooks/content-editor/use-tiptap-editor'
5
+ // --- Lib ---
6
+ import {
7
+ findNodePosition,
8
+ getSelectedBlockNodes,
9
+ isNodeInSchema,
10
+ isNodeTypeSelected,
11
+ isValidPosition,
12
+ selectionWithinConvertibleTypes
13
+ } from '@admin/utils/editor/tiptap'
14
+ import { NodeSelection, TextSelection } from '@tiptap/pm/state'
15
+ import type { Editor } from '@tiptap/react'
16
+ // --- Icons ---
17
+ import { ListIcon, ListOrderedIcon, ListTodoIcon } from 'lucide-react'
18
+ import { useCallback, useEffect, useState } from 'react'
19
+
20
+ export type ListType = 'bulletList' | 'orderedList' | 'taskList'
21
+
22
+ /**
23
+ * Configuration for the list functionality
24
+ */
25
+ export interface UseListConfig {
26
+ /**
27
+ * The Tiptap editor instance.
28
+ */
29
+ editor?: Editor | null
30
+ /**
31
+ * The type of list to toggle.
32
+ */
33
+ type: ListType
34
+ /**
35
+ * Whether the button should hide when list is not available.
36
+ * @default false
37
+ */
38
+ hideWhenUnavailable?: boolean
39
+ /**
40
+ * Callback function called after a successful toggle.
41
+ */
42
+ onToggled?: () => void
43
+ }
44
+
45
+ export const listIcons = {
46
+ bulletList: ListIcon,
47
+ orderedList: ListOrderedIcon,
48
+ taskList: ListTodoIcon
49
+ }
50
+
51
+ export const listLabels: Record<ListType, string> = {
52
+ bulletList: 'Bullet List',
53
+ orderedList: 'Ordered List',
54
+ taskList: 'Task List'
55
+ }
56
+
57
+ export const LIST_SHORTCUT_KEYS: Record<ListType, string> = {
58
+ bulletList: 'mod+shift+8',
59
+ orderedList: 'mod+shift+7',
60
+ taskList: 'mod+shift+9'
61
+ }
62
+
63
+ /**
64
+ * Checks if a list can be toggled in the current editor state
65
+ */
66
+ export function canToggleList(
67
+ editor: Editor | null,
68
+ type: ListType,
69
+ turnInto: boolean = true
70
+ ): boolean {
71
+ if (!editor || !editor.isEditable) return false
72
+ if (!isNodeInSchema(type, editor) || isNodeTypeSelected(editor, ['image'])) return false
73
+
74
+ if (!turnInto) {
75
+ switch (type) {
76
+ case 'bulletList':
77
+ return editor.can().toggleBulletList()
78
+ case 'orderedList':
79
+ return editor.can().toggleOrderedList()
80
+ case 'taskList':
81
+ return editor.can().toggleList('taskList', 'taskItem')
82
+ default:
83
+ return false
84
+ }
85
+ }
86
+
87
+ // Ensure selection is in nodes we're allowed to convert
88
+ if (
89
+ !selectionWithinConvertibleTypes(editor, [
90
+ 'paragraph',
91
+ 'heading',
92
+ 'bulletList',
93
+ 'orderedList',
94
+ 'taskList',
95
+ 'blockquote',
96
+ 'codeBlock'
97
+ ])
98
+ )
99
+ return false
100
+
101
+ // Either we can set list directly on the selection,
102
+ // or we can clear formatting/nodes to arrive at a list.
103
+ switch (type) {
104
+ case 'bulletList':
105
+ return editor.can().toggleBulletList() || editor.can().clearNodes()
106
+ case 'orderedList':
107
+ return editor.can().toggleOrderedList() || editor.can().clearNodes()
108
+ case 'taskList':
109
+ return editor.can().toggleList('taskList', 'taskItem') || editor.can().clearNodes()
110
+ default:
111
+ return false
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Checks if list is currently active
117
+ */
118
+ export function isListActive(editor: Editor | null, type: ListType): boolean {
119
+ if (!editor || !editor.isEditable) return false
120
+
121
+ switch (type) {
122
+ case 'bulletList':
123
+ return editor.isActive('bulletList')
124
+ case 'orderedList':
125
+ return editor.isActive('orderedList')
126
+ case 'taskList':
127
+ return editor.isActive('taskList')
128
+ default:
129
+ return false
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Toggles list in the editor
135
+ */
136
+ export function toggleList(editor: Editor | null, type: ListType): boolean {
137
+ if (!editor || !editor.isEditable) return false
138
+ if (!canToggleList(editor, type)) return false
139
+
140
+ try {
141
+ const view = editor.view
142
+ let state = view.state
143
+ let tr = state.tr
144
+
145
+ const blocks = getSelectedBlockNodes(editor)
146
+
147
+ // In case a selection contains multiple blocks, we only allow
148
+ // toggling to nide if there's exactly one block selected
149
+ // we also dont block the canToggle since it will fall back to the bottom logic
150
+ const isPossibleToTurnInto =
151
+ selectionWithinConvertibleTypes(editor, [
152
+ 'paragraph',
153
+ 'heading',
154
+ 'bulletList',
155
+ 'orderedList',
156
+ 'taskList',
157
+ 'blockquote',
158
+ 'codeBlock'
159
+ ]) && blocks.length === 1
160
+
161
+ // No selection, find the the cursor position
162
+ if (
163
+ (state.selection.empty || state.selection instanceof TextSelection) &&
164
+ isPossibleToTurnInto
165
+ ) {
166
+ const pos = findNodePosition({
167
+ editor,
168
+ node: state.selection.$anchor.node(1)
169
+ })?.pos
170
+ if (!isValidPosition(pos)) return false
171
+
172
+ tr = tr.setSelection(NodeSelection.create(state.doc, pos))
173
+ view.dispatch(tr)
174
+ state = view.state
175
+ }
176
+
177
+ const selection = state.selection
178
+
179
+ let chain = editor.chain().focus()
180
+
181
+ // Handle NodeSelection
182
+ if (selection instanceof NodeSelection) {
183
+ const firstChild = selection.node.firstChild?.firstChild
184
+ const lastChild = selection.node.lastChild?.lastChild
185
+
186
+ const from = firstChild ? selection.from + firstChild.nodeSize : selection.from + 1
187
+
188
+ const to = lastChild ? selection.to - lastChild.nodeSize : selection.to - 1
189
+
190
+ const resolvedFrom = state.doc.resolve(from)
191
+ const resolvedTo = state.doc.resolve(to)
192
+
193
+ chain = chain.setTextSelection(TextSelection.between(resolvedFrom, resolvedTo)).clearNodes()
194
+ }
195
+
196
+ if (editor.isActive(type)) {
197
+ // Unwrap list
198
+ chain.liftListItem('listItem').lift('bulletList').lift('orderedList').lift('taskList').run()
199
+ } else {
200
+ // Wrap in specific list type
201
+ const toggleMap: Record<ListType, () => typeof chain> = {
202
+ bulletList: () => chain.toggleBulletList(),
203
+ orderedList: () => chain.toggleOrderedList(),
204
+ taskList: () => chain.toggleList('taskList', 'taskItem')
205
+ }
206
+
207
+ const toggle = toggleMap[type]
208
+ if (!toggle) return false
209
+
210
+ toggle().run()
211
+ }
212
+
213
+ editor.chain().focus().selectTextblockEnd().run()
214
+
215
+ return true
216
+ } catch {
217
+ return false
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Determines if the list button should be shown
223
+ */
224
+ export function shouldShowButton(props: {
225
+ editor: Editor | null
226
+ type: ListType
227
+ hideWhenUnavailable: boolean
228
+ }): boolean {
229
+ const { editor, type, hideWhenUnavailable } = props
230
+
231
+ if (!editor || !editor.isEditable) return false
232
+
233
+ if (!hideWhenUnavailable) {
234
+ return true
235
+ }
236
+
237
+ if (!isNodeInSchema(type, editor)) return false
238
+
239
+ if (!editor.isActive('code')) {
240
+ return canToggleList(editor, type)
241
+ }
242
+
243
+ return true
244
+ }
245
+
246
+ /** Provides list commands for ContentEditor controls. */
247
+ export function useList(config: UseListConfig) {
248
+ const { editor: providedEditor, type, hideWhenUnavailable = false, onToggled } = config
249
+
250
+ const { editor } = useTiptapEditor(providedEditor)
251
+ const [isVisible, setIsVisible] = useState<boolean>(true)
252
+ const canToggle = canToggleList(editor, type)
253
+ const isActive = isListActive(editor, type)
254
+
255
+ useEffect(() => {
256
+ if (!editor) return
257
+
258
+ const handleSelectionUpdate = () => {
259
+ setIsVisible(shouldShowButton({ editor, type, hideWhenUnavailable }))
260
+ }
261
+
262
+ handleSelectionUpdate()
263
+
264
+ editor.on('selectionUpdate', handleSelectionUpdate)
265
+
266
+ return () => {
267
+ editor.off('selectionUpdate', handleSelectionUpdate)
268
+ }
269
+ }, [editor, type, hideWhenUnavailable])
270
+
271
+ const handleToggle = useCallback(() => {
272
+ if (!editor) return false
273
+
274
+ const success = toggleList(editor, type)
275
+ if (success) {
276
+ onToggled?.()
277
+ }
278
+ return success
279
+ }, [editor, type, onToggled])
280
+
281
+ return {
282
+ isVisible,
283
+ isActive,
284
+ handleToggle,
285
+ canToggle,
286
+ label: listLabels[type],
287
+ shortcutKeys: LIST_SHORTCUT_KEYS[type],
288
+ Icon: listIcons[type]
289
+ }
290
+ }
@@ -0,0 +1,199 @@
1
+ 'use client'
2
+
3
+ // --- Hooks ---
4
+ import { useTiptapEditor } from '@admin/hooks/content-editor/use-tiptap-editor'
5
+ // --- Lib ---
6
+ import { isMarkInSchema, isNodeTypeSelected } from '@admin/utils/editor/tiptap'
7
+ import type { Editor } from '@tiptap/react'
8
+ // --- Icons ---
9
+ import {
10
+ BoldIcon,
11
+ Code2Icon,
12
+ ItalicIcon,
13
+ StrikethroughIcon as StrikeIcon,
14
+ UnderlineIcon
15
+ } from 'lucide-react'
16
+ import { useCallback, useEffect, useState } from 'react'
17
+
18
+ export type Mark = 'bold' | 'italic' | 'strike' | 'code' | 'underline'
19
+
20
+ /**
21
+ * Configuration for the mark functionality
22
+ */
23
+ export interface UseMarkConfig {
24
+ /**
25
+ * The Tiptap editor instance.
26
+ */
27
+ editor?: Editor | null
28
+ /**
29
+ * The type of mark to toggle
30
+ */
31
+ type: Mark
32
+ /**
33
+ * Whether the button should hide when mark is not available.
34
+ * @default false
35
+ */
36
+ hideWhenUnavailable?: boolean
37
+ /**
38
+ * Callback function called after a successful mark toggle.
39
+ */
40
+ onToggled?: () => void
41
+ }
42
+
43
+ export const markIcons = {
44
+ bold: BoldIcon,
45
+ italic: ItalicIcon,
46
+ underline: UnderlineIcon,
47
+ strike: StrikeIcon,
48
+ code: Code2Icon
49
+ }
50
+
51
+ export const MARK_SHORTCUT_KEYS: Record<Mark, string> = {
52
+ bold: 'mod+b',
53
+ italic: 'mod+i',
54
+ underline: 'mod+u',
55
+ strike: 'mod+shift+s',
56
+ code: 'mod+e'
57
+ }
58
+
59
+ /**
60
+ * Checks if a mark can be toggled in the current editor state
61
+ */
62
+ export function canToggleMark(editor: Editor | null, type: Mark): boolean {
63
+ if (!editor || !editor.isEditable) return false
64
+ if (!isMarkInSchema(type, editor) || isNodeTypeSelected(editor, ['image'])) return false
65
+
66
+ return editor.can().toggleMark(type)
67
+ }
68
+
69
+ /**
70
+ * Checks if a mark is currently active
71
+ */
72
+ export function isMarkActive(editor: Editor | null, type: Mark): boolean {
73
+ if (!editor || !editor.isEditable) return false
74
+ return editor.isActive(type)
75
+ }
76
+
77
+ /**
78
+ * Toggles a mark in the editor
79
+ */
80
+ export function toggleMark(editor: Editor | null, type: Mark): boolean {
81
+ if (!editor || !editor.isEditable) return false
82
+ if (!canToggleMark(editor, type)) return false
83
+
84
+ return editor.chain().focus().toggleMark(type).run()
85
+ }
86
+
87
+ /**
88
+ * Determines if the mark button should be shown
89
+ */
90
+ export function shouldShowButton(props: {
91
+ editor: Editor | null
92
+ type: Mark
93
+ hideWhenUnavailable: boolean
94
+ }): boolean {
95
+ const { editor, type, hideWhenUnavailable } = props
96
+
97
+ if (!editor || !editor.isEditable) return false
98
+
99
+ if (!hideWhenUnavailable) {
100
+ return true
101
+ }
102
+
103
+ if (!isMarkInSchema(type, editor)) return false
104
+
105
+ if (!editor.isActive('code')) {
106
+ return canToggleMark(editor, type)
107
+ }
108
+
109
+ return true
110
+ }
111
+
112
+ /**
113
+ * Gets the formatted mark name
114
+ */
115
+ export function getFormattedMarkName(type: Mark): string {
116
+ return type.charAt(0).toUpperCase() + type.slice(1)
117
+ }
118
+
119
+ /**
120
+ * Custom hook that provides mark functionality for Tiptap editor
121
+ *
122
+ * @example
123
+ * ```tsx
124
+ * // Simple usage
125
+ * function MySimpleBoldButton() {
126
+ * const { isVisible, handleMark } = useMark({ type: "bold" })
127
+ *
128
+ * if (!isVisible) return null
129
+ *
130
+ * return <button onClick={handleMark}>Bold</button>
131
+ * }
132
+ *
133
+ * // Advanced usage with configuration
134
+ * function MyAdvancedItalicButton() {
135
+ * const { isVisible, handleMark, label, isActive } = useMark({
136
+ * editor: myEditor,
137
+ * type: "italic",
138
+ * hideWhenUnavailable: true,
139
+ * onToggled: () => console.log('Mark toggled!')
140
+ * })
141
+ *
142
+ * if (!isVisible) return null
143
+ *
144
+ * return (
145
+ * <MyButton
146
+ * onClick={handleMark}
147
+ * aria-pressed={isActive}
148
+ * aria-label={label}
149
+ * >
150
+ * Italic
151
+ * </MyButton>
152
+ * )
153
+ * }
154
+ * ```
155
+ */
156
+ export function useMark(config: UseMarkConfig) {
157
+ const { editor: providedEditor, type, hideWhenUnavailable = false, onToggled } = config
158
+
159
+ const { editor } = useTiptapEditor(providedEditor)
160
+ const [isVisible, setIsVisible] = useState<boolean>(true)
161
+ const canToggle = canToggleMark(editor, type)
162
+ const isActive = isMarkActive(editor, type)
163
+
164
+ useEffect(() => {
165
+ if (!editor) return
166
+
167
+ const handleSelectionUpdate = () => {
168
+ setIsVisible(shouldShowButton({ editor, type, hideWhenUnavailable }))
169
+ }
170
+
171
+ handleSelectionUpdate()
172
+
173
+ editor.on('selectionUpdate', handleSelectionUpdate)
174
+
175
+ return () => {
176
+ editor.off('selectionUpdate', handleSelectionUpdate)
177
+ }
178
+ }, [editor, type, hideWhenUnavailable])
179
+
180
+ const handleMark = useCallback(() => {
181
+ if (!editor) return false
182
+
183
+ const success = toggleMark(editor, type)
184
+ if (success) {
185
+ onToggled?.()
186
+ }
187
+ return success
188
+ }, [editor, type, onToggled])
189
+
190
+ return {
191
+ isVisible,
192
+ isActive,
193
+ handleMark,
194
+ canToggle,
195
+ label: getFormattedMarkName(type),
196
+ shortcutKeys: MARK_SHORTCUT_KEYS[type],
197
+ Icon: markIcons[type]
198
+ }
199
+ }