@jhits/plugin-blog 0.0.18 → 0.0.20

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 (291) hide show
  1. package/dist/api/categories.d.ts.map +1 -1
  2. package/dist/api/categories.js +42 -38
  3. package/dist/api/handler.d.ts +1 -26
  4. package/dist/api/handler.d.ts.map +1 -1
  5. package/dist/api/handler.js +81 -500
  6. package/dist/api/router.d.ts +0 -5
  7. package/dist/api/router.d.ts.map +1 -1
  8. package/dist/api/router.js +8 -35
  9. package/dist/api/service.d.ts +80 -0
  10. package/dist/api/service.d.ts.map +1 -0
  11. package/dist/api/service.js +219 -0
  12. package/dist/hooks/useAutoSave.d.ts +10 -0
  13. package/dist/hooks/useAutoSave.d.ts.map +1 -0
  14. package/dist/hooks/useAutoSave.js +57 -0
  15. package/dist/hooks/useCategories.d.ts +1 -1
  16. package/dist/hooks/useCategories.d.ts.map +1 -1
  17. package/dist/hooks/useCategories.js +15 -46
  18. package/dist/index.d.ts +24 -31
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +44 -201
  21. package/dist/init.d.ts +20 -7
  22. package/dist/init.d.ts.map +1 -1
  23. package/dist/init.js +8 -7
  24. package/dist/lib/blocks/BlockRenderer.d.ts.map +1 -1
  25. package/dist/lib/layouts/blocks/ColumnsBlock.d.ts.map +1 -1
  26. package/dist/lib/layouts/blocks/ColumnsBlock.js +30 -113
  27. package/dist/lib/layouts/blocks/SectionBlock.d.ts.map +1 -1
  28. package/dist/lib/layouts/blocks/SectionBlock.js +9 -21
  29. package/dist/lib/layouts/index.d.ts +3 -3
  30. package/dist/lib/layouts/index.js +4 -4
  31. package/dist/lib/mappers/apiMapper.d.ts +10 -0
  32. package/dist/lib/mappers/apiMapper.d.ts.map +1 -1
  33. package/dist/lib/mappers/apiMapper.js +47 -32
  34. package/dist/lib/rich-text/RichTextEditor.d.ts +4 -2
  35. package/dist/lib/rich-text/RichTextEditor.d.ts.map +1 -1
  36. package/dist/lib/rich-text/RichTextEditor.js +12 -9
  37. package/dist/lib/utils/config-resolver.d.ts +28 -0
  38. package/dist/lib/utils/config-resolver.d.ts.map +1 -0
  39. package/dist/lib/utils/config-resolver.js +46 -0
  40. package/dist/lib/utils/tree.d.ts +29 -0
  41. package/dist/lib/utils/tree.d.ts.map +1 -0
  42. package/dist/lib/utils/tree.js +129 -0
  43. package/dist/state/EditorContext.d.ts +3 -25
  44. package/dist/state/EditorContext.d.ts.map +1 -1
  45. package/dist/state/EditorContext.js +124 -174
  46. package/dist/state/reducer.d.ts +1 -5
  47. package/dist/state/reducer.d.ts.map +1 -1
  48. package/dist/state/reducer.js +128 -521
  49. package/dist/state/types.d.ts +12 -1
  50. package/dist/state/types.d.ts.map +1 -1
  51. package/dist/types/block.d.ts +9 -0
  52. package/dist/types/block.d.ts.map +1 -1
  53. package/dist/types/post.d.ts +17 -1
  54. package/dist/types/post.d.ts.map +1 -1
  55. package/dist/views/CanvasEditor/BlockWrapper.d.ts +5 -6
  56. package/dist/views/CanvasEditor/BlockWrapper.d.ts.map +1 -1
  57. package/dist/views/CanvasEditor/BlockWrapper.js +56 -264
  58. package/dist/views/CanvasEditor/CanvasEditorView.d.ts +5 -3
  59. package/dist/views/CanvasEditor/CanvasEditorView.d.ts.map +1 -1
  60. package/dist/views/CanvasEditor/CanvasEditorView.js +55 -315
  61. package/dist/views/CanvasEditor/EditorBody.d.ts +6 -8
  62. package/dist/views/CanvasEditor/EditorBody.d.ts.map +1 -1
  63. package/dist/views/CanvasEditor/EditorBody.js +34 -482
  64. package/dist/views/CanvasEditor/EditorHeader.d.ts.map +1 -1
  65. package/dist/views/CanvasEditor/EditorHeader.js +27 -63
  66. package/dist/views/CanvasEditor/LayoutContainer.d.ts.map +1 -1
  67. package/dist/views/CanvasEditor/LayoutContainer.js +49 -70
  68. package/dist/views/CanvasEditor/components/CustomBlockItem.js +1 -1
  69. package/dist/views/CanvasEditor/components/EditorCanvas.d.ts +15 -3
  70. package/dist/views/CanvasEditor/components/EditorCanvas.d.ts.map +1 -1
  71. package/dist/views/CanvasEditor/components/EditorCanvas.js +40 -18
  72. package/dist/views/CanvasEditor/components/EditorLibrary.d.ts +5 -1
  73. package/dist/views/CanvasEditor/components/EditorLibrary.d.ts.map +1 -1
  74. package/dist/views/CanvasEditor/components/EditorLibrary.js +11 -7
  75. package/dist/views/CanvasEditor/components/EditorSidebar.d.ts.map +1 -1
  76. package/dist/views/CanvasEditor/components/EditorSidebar.js +32 -14
  77. package/dist/views/CanvasEditor/components/FeaturedMediaSection.d.ts +0 -6
  78. package/dist/views/CanvasEditor/components/FeaturedMediaSection.d.ts.map +1 -1
  79. package/dist/views/CanvasEditor/components/FeaturedMediaSection.js +17 -128
  80. package/dist/views/CanvasEditor/components/JSONInspector.d.ts +9 -0
  81. package/dist/views/CanvasEditor/components/JSONInspector.d.ts.map +1 -0
  82. package/dist/views/CanvasEditor/components/JSONInspector.js +56 -0
  83. package/dist/views/CanvasEditor/components/LibraryItem.js +2 -2
  84. package/dist/views/CanvasEditor/components/PrivacySettingsSection.d.ts +0 -4
  85. package/dist/views/CanvasEditor/components/PrivacySettingsSection.d.ts.map +1 -1
  86. package/dist/views/CanvasEditor/components/PrivacySettingsSection.js +6 -28
  87. package/dist/views/CanvasEditor/components/index.d.ts +2 -0
  88. package/dist/views/CanvasEditor/components/index.d.ts.map +1 -1
  89. package/dist/views/CanvasEditor/components/index.js +1 -0
  90. package/dist/views/CanvasEditor/hooks/useHeroBlock.d.ts.map +1 -1
  91. package/dist/views/CanvasEditor/hooks/useHeroBlock.js +15 -18
  92. package/dist/views/CanvasEditor/hooks/usePostLoader.d.ts +3 -0
  93. package/dist/views/CanvasEditor/hooks/usePostLoader.d.ts.map +1 -1
  94. package/dist/views/CanvasEditor/hooks/usePostLoader.js +12 -13
  95. package/dist/views/CanvasEditor/hooks/useUnsavedChanges.js +0 -4
  96. package/dist/views/PostManager/EmptyState.d.ts +1 -1
  97. package/dist/views/PostManager/EmptyState.js +4 -4
  98. package/dist/views/PostManager/FilterDropdown.d.ts +21 -0
  99. package/dist/views/PostManager/FilterDropdown.d.ts.map +1 -0
  100. package/dist/views/PostManager/FilterDropdown.js +28 -0
  101. package/dist/views/PostManager/LanguageFlags.d.ts.map +1 -1
  102. package/dist/views/PostManager/LanguageFlags.js +4 -1
  103. package/dist/views/PostManager/PostCards.d.ts.map +1 -1
  104. package/dist/views/PostManager/PostCards.js +23 -40
  105. package/dist/views/PostManager/PostFilters.d.ts.map +1 -1
  106. package/dist/views/PostManager/PostFilters.js +34 -3
  107. package/dist/views/PostManager/PostManagerView.d.ts +1 -2
  108. package/dist/views/PostManager/PostManagerView.d.ts.map +1 -1
  109. package/dist/views/PostManager/PostManagerView.js +30 -96
  110. package/dist/views/PostManager/PostStats.d.ts.map +1 -1
  111. package/dist/views/PostManager/PostStats.js +10 -10
  112. package/dist/views/PostManager/PostTable.d.ts.map +1 -1
  113. package/dist/views/PostManager/PostTable.js +23 -40
  114. package/dist/views/Settings/SettingsView.d.ts +1 -1
  115. package/dist/views/Settings/SettingsView.d.ts.map +1 -1
  116. package/dist/views/Settings/SettingsView.js +12 -39
  117. package/dist/views/SlugSEO/SlugSEOManagerView.d.ts.map +1 -1
  118. package/dist/views/SlugSEO/SlugSEOManagerView.js +2 -2
  119. package/package.json +42 -6
  120. package/src/api/categories.ts +48 -52
  121. package/src/api/handler.ts +87 -604
  122. package/src/api/router.ts +15 -65
  123. package/src/api/service.ts +241 -0
  124. package/src/hooks/useAutoSave.ts +64 -0
  125. package/src/hooks/useCategories.ts +19 -47
  126. package/src/index.tsx +79 -293
  127. package/src/init.tsx +24 -11
  128. package/src/lib/blocks/BlockRenderer.tsx +1 -0
  129. package/src/lib/layouts/blocks/ColumnsBlock.tsx +60 -173
  130. package/src/lib/layouts/blocks/SectionBlock.tsx +22 -26
  131. package/src/lib/layouts/index.ts +4 -4
  132. package/src/lib/mappers/apiMapper.ts +63 -32
  133. package/src/lib/rich-text/RichTextEditor.tsx +16 -9
  134. package/src/lib/utils/config-resolver.ts +64 -0
  135. package/src/lib/utils/tree.ts +150 -0
  136. package/src/state/EditorContext.tsx +153 -232
  137. package/src/state/reducer.ts +141 -606
  138. package/src/state/types.ts +14 -1
  139. package/src/types/block.ts +10 -0
  140. package/src/types/post.ts +19 -1
  141. package/src/views/CanvasEditor/BlockWrapper.tsx +130 -460
  142. package/src/views/CanvasEditor/CanvasEditorView.tsx +145 -420
  143. package/src/views/CanvasEditor/EditorBody.tsx +98 -610
  144. package/src/views/CanvasEditor/EditorHeader.tsx +176 -196
  145. package/src/views/CanvasEditor/LayoutContainer.tsx +74 -89
  146. package/src/views/CanvasEditor/components/CustomBlockItem.tsx +7 -8
  147. package/src/views/CanvasEditor/components/EditorCanvas.tsx +139 -84
  148. package/src/views/CanvasEditor/components/EditorLibrary.tsx +25 -10
  149. package/src/views/CanvasEditor/components/EditorSidebar.tsx +196 -127
  150. package/src/views/CanvasEditor/components/FeaturedMediaSection.tsx +78 -210
  151. package/src/views/CanvasEditor/components/JSONInspector.tsx +125 -0
  152. package/src/views/CanvasEditor/components/LibraryItem.tsx +5 -6
  153. package/src/views/CanvasEditor/components/PrivacySettingsSection.tsx +73 -124
  154. package/src/views/CanvasEditor/components/index.ts +2 -1
  155. package/src/views/CanvasEditor/hooks/useHeroBlock.ts +15 -18
  156. package/src/views/CanvasEditor/hooks/usePostLoader.ts +21 -13
  157. package/src/views/CanvasEditor/hooks/useUnsavedChanges.ts +4 -4
  158. package/src/views/PostManager/EmptyState.tsx +9 -10
  159. package/src/views/PostManager/FilterDropdown.tsx +95 -0
  160. package/src/views/PostManager/LanguageFlags.tsx +6 -2
  161. package/src/views/PostManager/PostCards.tsx +127 -133
  162. package/src/views/PostManager/PostFilters.tsx +73 -68
  163. package/src/views/PostManager/PostManagerView.tsx +132 -179
  164. package/src/views/PostManager/PostStats.tsx +21 -20
  165. package/src/views/PostManager/PostTable.tsx +137 -165
  166. package/src/views/Settings/SettingsView.tsx +64 -180
  167. package/src/views/SlugSEO/SlugSEOManagerView.tsx +59 -44
  168. package/src/hooks/index.d.ts +0 -8
  169. package/src/hooks/index.d.ts.map +0 -1
  170. package/src/hooks/useBlog.d.ts +0 -31
  171. package/src/hooks/useBlog.d.ts.map +0 -1
  172. package/src/hooks/useBlogs.d.ts +0 -39
  173. package/src/hooks/useBlogs.d.ts.map +0 -1
  174. package/src/hooks/useCategories.d.ts +0 -9
  175. package/src/hooks/useCategories.d.ts.map +0 -1
  176. package/src/lib/blocks/BlockRenderer.d.ts +0 -54
  177. package/src/lib/blocks/BlockRenderer.d.ts.map +0 -1
  178. package/src/lib/config-storage.d.ts +0 -30
  179. package/src/lib/config-storage.d.ts.map +0 -1
  180. package/src/lib/layouts/blocks/ColumnsBlock.d.ts +0 -25
  181. package/src/lib/layouts/blocks/ColumnsBlock.d.ts.map +0 -1
  182. package/src/lib/layouts/blocks/SectionBlock.d.ts +0 -25
  183. package/src/lib/layouts/blocks/SectionBlock.d.ts.map +0 -1
  184. package/src/lib/layouts/index.d.ts +0 -23
  185. package/src/lib/layouts/index.d.ts.map +0 -1
  186. package/src/lib/layouts/registerLayoutBlocks.d.ts +0 -9
  187. package/src/lib/layouts/registerLayoutBlocks.d.ts.map +0 -1
  188. package/src/lib/mappers/apiMapper.d.ts +0 -66
  189. package/src/lib/mappers/apiMapper.d.ts.map +0 -1
  190. package/src/lib/rich-text/RichTextEditor.d.ts +0 -45
  191. package/src/lib/rich-text/RichTextEditor.d.ts.map +0 -1
  192. package/src/lib/rich-text/RichTextPreview.d.ts +0 -16
  193. package/src/lib/rich-text/RichTextPreview.d.ts.map +0 -1
  194. package/src/lib/rich-text/index.d.ts +0 -9
  195. package/src/lib/rich-text/index.d.ts.map +0 -1
  196. package/src/lib/utils/blockHelpers.d.ts +0 -23
  197. package/src/lib/utils/blockHelpers.d.ts.map +0 -1
  198. package/src/lib/utils/configValidation.d.ts +0 -23
  199. package/src/lib/utils/configValidation.d.ts.map +0 -1
  200. package/src/registry/BlockRegistry.d.ts +0 -62
  201. package/src/registry/BlockRegistry.d.ts.map +0 -1
  202. package/src/registry/index.d.ts +0 -6
  203. package/src/registry/index.d.ts.map +0 -1
  204. package/src/state/EditorContext.d.ts +0 -45
  205. package/src/state/EditorContext.d.ts.map +0 -1
  206. package/src/state/index.d.ts +0 -7
  207. package/src/state/index.d.ts.map +0 -1
  208. package/src/state/reducer.d.ts +0 -11
  209. package/src/state/reducer.d.ts.map +0 -1
  210. package/src/state/types.d.ts +0 -162
  211. package/src/state/types.d.ts.map +0 -1
  212. package/src/types/block.d.ts +0 -221
  213. package/src/types/block.d.ts.map +0 -1
  214. package/src/types/index.d.ts +0 -8
  215. package/src/types/index.d.ts.map +0 -1
  216. package/src/types/post.d.ts +0 -136
  217. package/src/types/post.d.ts.map +0 -1
  218. package/src/utils/client.d.ts +0 -48
  219. package/src/utils/client.d.ts.map +0 -1
  220. package/src/views/CanvasEditor/BlockWrapper.d.ts +0 -16
  221. package/src/views/CanvasEditor/BlockWrapper.d.ts.map +0 -1
  222. package/src/views/CanvasEditor/CanvasEditorView.d.ts +0 -14
  223. package/src/views/CanvasEditor/CanvasEditorView.d.ts.map +0 -1
  224. package/src/views/CanvasEditor/EditorBody.d.ts +0 -22
  225. package/src/views/CanvasEditor/EditorBody.d.ts.map +0 -1
  226. package/src/views/CanvasEditor/EditorHeader.d.ts +0 -18
  227. package/src/views/CanvasEditor/EditorHeader.d.ts.map +0 -1
  228. package/src/views/CanvasEditor/LayoutContainer.d.ts +0 -17
  229. package/src/views/CanvasEditor/LayoutContainer.d.ts.map +0 -1
  230. package/src/views/CanvasEditor/SaveConfirmationModal.d.ts +0 -13
  231. package/src/views/CanvasEditor/SaveConfirmationModal.d.ts.map +0 -1
  232. package/src/views/CanvasEditor/components/CustomBlockItem.d.ts +0 -14
  233. package/src/views/CanvasEditor/components/CustomBlockItem.d.ts.map +0 -1
  234. package/src/views/CanvasEditor/components/EditorCanvas.d.ts +0 -29
  235. package/src/views/CanvasEditor/components/EditorCanvas.d.ts.map +0 -1
  236. package/src/views/CanvasEditor/components/EditorLibrary.d.ts +0 -7
  237. package/src/views/CanvasEditor/components/EditorLibrary.d.ts.map +0 -1
  238. package/src/views/CanvasEditor/components/EditorSidebar.d.ts +0 -13
  239. package/src/views/CanvasEditor/components/EditorSidebar.d.ts.map +0 -1
  240. package/src/views/CanvasEditor/components/ErrorBanner.d.ts +0 -6
  241. package/src/views/CanvasEditor/components/ErrorBanner.d.ts.map +0 -1
  242. package/src/views/CanvasEditor/components/FeaturedMediaSection.d.ts +0 -25
  243. package/src/views/CanvasEditor/components/FeaturedMediaSection.d.ts.map +0 -1
  244. package/src/views/CanvasEditor/components/LibraryItem.d.ts +0 -14
  245. package/src/views/CanvasEditor/components/LibraryItem.d.ts.map +0 -1
  246. package/src/views/CanvasEditor/components/PrivacySettingsSection.d.ts +0 -15
  247. package/src/views/CanvasEditor/components/PrivacySettingsSection.d.ts.map +0 -1
  248. package/src/views/CanvasEditor/components/index.d.ts +0 -21
  249. package/src/views/CanvasEditor/components/index.d.ts.map +0 -1
  250. package/src/views/CanvasEditor/hooks/index.d.ts +0 -10
  251. package/src/views/CanvasEditor/hooks/index.d.ts.map +0 -1
  252. package/src/views/CanvasEditor/hooks/useHeroBlock.d.ts +0 -8
  253. package/src/views/CanvasEditor/hooks/useHeroBlock.d.ts.map +0 -1
  254. package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.d.ts +0 -3
  255. package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.d.ts.map +0 -1
  256. package/src/views/CanvasEditor/hooks/usePostLoader.d.ts +0 -5
  257. package/src/views/CanvasEditor/hooks/usePostLoader.d.ts.map +0 -1
  258. package/src/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts +0 -2
  259. package/src/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts.map +0 -1
  260. package/src/views/CanvasEditor/hooks/useUnsavedChanges.d.ts +0 -25
  261. package/src/views/CanvasEditor/hooks/useUnsavedChanges.d.ts.map +0 -1
  262. package/src/views/CanvasEditor/index.d.ts +0 -16
  263. package/src/views/CanvasEditor/index.d.ts.map +0 -1
  264. package/src/views/PostManager/EmptyState.d.ts +0 -10
  265. package/src/views/PostManager/EmptyState.d.ts.map +0 -1
  266. package/src/views/PostManager/PostActionsMenu.d.ts +0 -12
  267. package/src/views/PostManager/PostActionsMenu.d.ts.map +0 -1
  268. package/src/views/PostManager/PostCards.d.ts +0 -15
  269. package/src/views/PostManager/PostCards.d.ts.map +0 -1
  270. package/src/views/PostManager/PostFilters.d.ts +0 -16
  271. package/src/views/PostManager/PostFilters.d.ts.map +0 -1
  272. package/src/views/PostManager/PostManagerView.d.ts +0 -11
  273. package/src/views/PostManager/PostManagerView.d.ts.map +0 -1
  274. package/src/views/PostManager/PostStats.d.ts +0 -11
  275. package/src/views/PostManager/PostStats.d.ts.map +0 -1
  276. package/src/views/PostManager/PostTable.d.ts +0 -15
  277. package/src/views/PostManager/PostTable.d.ts.map +0 -1
  278. package/src/views/PostManager/index.d.ts +0 -12
  279. package/src/views/PostManager/index.d.ts.map +0 -1
  280. package/src/views/Preview/PreviewBridgeView.d.ts +0 -12
  281. package/src/views/Preview/PreviewBridgeView.d.ts.map +0 -1
  282. package/src/views/Preview/index.d.ts +0 -6
  283. package/src/views/Preview/index.d.ts.map +0 -1
  284. package/src/views/Settings/SettingsView.d.ts +0 -10
  285. package/src/views/Settings/SettingsView.d.ts.map +0 -1
  286. package/src/views/Settings/index.d.ts +0 -6
  287. package/src/views/Settings/index.d.ts.map +0 -1
  288. package/src/views/SlugSEO/SlugSEOManagerView.d.ts +0 -12
  289. package/src/views/SlugSEO/SlugSEOManagerView.d.ts.map +0 -1
  290. package/src/views/SlugSEO/index.d.ts +0 -6
  291. package/src/views/SlugSEO/index.d.ts.map +0 -1
@@ -32,7 +32,7 @@ export function LayoutContainer({
32
32
  className = '',
33
33
  emptyLabel = 'Drop blocks here',
34
34
  }: LayoutContainerProps) {
35
- const { darkMode } = useEditor();
35
+ const { darkMode, helpers } = useEditor();
36
36
 
37
37
  // --- State ---
38
38
  const [dragOverIndex, setDragOverIndex] = useState<number | null>(null);
@@ -47,28 +47,37 @@ export function LayoutContainer({
47
47
 
48
48
  // --- Cleanup & Event Listeners ---
49
49
  useEffect(() => {
50
- const resetState = () => {
50
+ const resetState = (e: Event) => {
51
+ // Only reset if this is the container that was actually being dragged over
52
+ // or if it's a global dragend event
51
53
  setDropIndicatorPosition(null);
52
54
  setDragOverIndex(null);
53
55
  setDropAtEnd(false);
54
- dropAtEndRef.current = false; // Reset ref too
56
+ dropAtEndRef.current = false;
55
57
  setIsDragging(false);
56
- // Clear global dragged block ID on dragend (in case drag was cancelled)
57
- if (typeof window !== 'undefined') {
58
- (window as any).__DRAGGED_BLOCK_ID__ = null;
59
- }
60
58
  };
61
59
 
62
60
  const container = containerRef.current;
63
61
  if (container) {
64
62
  container.addEventListener('clear-drop-indicator', resetState);
65
- document.addEventListener('dragend', resetState);
63
+
64
+ // Listen for dragend on the document but ONLY if we are currently dragging
65
+ const handleGlobalDragEnd = () => {
66
+ if (isDragging) {
67
+ resetState(new Event('dragend'));
68
+ if (typeof window !== 'undefined') {
69
+ (window as any).__DRAGGED_BLOCK_ID__ = null;
70
+ }
71
+ }
72
+ };
73
+
74
+ document.addEventListener('dragend', handleGlobalDragEnd);
66
75
  return () => {
67
76
  container.removeEventListener('clear-drop-indicator', resetState);
68
- document.removeEventListener('dragend', resetState);
77
+ document.removeEventListener('dragend', handleGlobalDragEnd);
69
78
  };
70
79
  }
71
- }, []);
80
+ }, [isDragging]);
72
81
 
73
82
  // --- Drag & Drop Logic ---
74
83
 
@@ -152,67 +161,35 @@ export function LayoutContainer({
152
161
  e.preventDefault();
153
162
  e.stopPropagation();
154
163
 
155
- const blockId = e.dataTransfer.getData('block-id') || (window as any).__DRAGGED_BLOCK_ID__;
156
- const blockType = e.dataTransfer.getData('block-type');
164
+ // MATCHING KEYS: use blockId and blockType (not hyphenated)
165
+ const blockId = e.dataTransfer.getData('blockId') || (window as any).__DRAGGED_BLOCK_ID__ || e.dataTransfer.getData('text/plain');
166
+ const blockType = e.dataTransfer.getData('blockType');
157
167
 
158
- // Clear the global dragged block ID immediately to prevent it from being used for new blocks
168
+
169
+
170
+ // Clear the global dragged block ID immediately
159
171
  if (typeof window !== 'undefined') {
160
172
  (window as any).__DRAGGED_BLOCK_ID__ = null;
161
173
  }
162
174
 
163
- // Logic: index is null when dropping on the container background (appends to end)
164
- // When dropAtEnd is true, we want to place it AFTER the block at index, so targetIndex = index + 1
165
- // When dropAtEnd is false, we want to place it BEFORE the block at index, so targetIndex = index
166
- // Use ref to get the latest value (React state updates are async)
167
175
  const isDropAtEnd = dropAtEndRef.current;
168
176
  let targetIndex = index === null ? blocks.length : (isDropAtEnd ? index + 1 : index);
169
177
 
170
178
  if (blockId) {
171
179
  const currentIndex = blocks.findIndex(b => b.id === blockId);
172
180
  if (currentIndex !== -1) {
173
- // Moving within the same array - need to adjust for removal
181
+ // Moving within same container
174
182
  let finalMoveIndex = targetIndex;
175
-
176
183
  if (currentIndex < targetIndex) {
177
- // Moving forward: when we remove the item from currentIndex, everything after it shifts down by 1.
178
- if (isDropAtEnd) {
179
- // Dropping below: we want it at index + 1 in the original array
180
- // If currentIndex <= index: after removal, block at index stays at index, so we want index + 1 = targetIndex
181
- // If index < currentIndex < targetIndex: after removal, we still want index + 1, but since we removed
182
- // an item before targetIndex, the position targetIndex in original = targetIndex - 1 in new array
183
- if (index !== null && currentIndex <= index) {
184
- // Item is at or before target block - no adjustment needed
185
- finalMoveIndex = targetIndex;
186
- } else {
187
- // Item is after target block but before targetIndex - need to adjust
188
- finalMoveIndex = targetIndex - 1;
189
- }
190
- } else {
191
- // Dropping above: targetIndex = index means "before the block at index"
192
- // After removal, if currentIndex < index, the block at index shifts to index - 1,
193
- // so we want it at index - 1 in the new array.
194
- finalMoveIndex = targetIndex - 1;
195
- }
184
+ finalMoveIndex = targetIndex - 1;
196
185
  }
197
- // If currentIndex >= targetIndex, no adjustment needed (moving backward or same position)
198
-
199
- console.log('[LayoutContainer] Drop calculation:', {
200
- blockId,
201
- index,
202
- dropAtEnd: isDropAtEnd,
203
- currentIndex,
204
- targetIndex,
205
- finalMoveIndex,
206
- blocksCount: blocks.length
207
- });
208
-
209
186
  onBlockMove(blockId, Math.max(0, finalMoveIndex), containerId);
210
187
  } else {
211
- // Moving from another container - no adjustment needed
188
+ // Moving from different container
212
189
  onBlockMove(blockId, targetIndex, containerId);
213
190
  }
214
191
  } else if (blockType) {
215
- // Adding new block - use targetIndex as-is
192
+ // Adding new block
216
193
  onBlockAdd(blockType, targetIndex, containerId);
217
194
  }
218
195
 
@@ -230,11 +207,21 @@ export function LayoutContainer({
230
207
  <div
231
208
  ref={containerRef}
232
209
  data-layout-container={containerId}
233
- className={`relative flex flex-col min-h-[40px] transition-colors ${className}`}
234
- onDragOver={(e) => { e.preventDefault(); setIsDragging(true); }}
210
+ className={`relative flex flex-col min-h-[40px] transition-colors pb-2 ${className}`}
211
+ onDragEnter={(e) => {
212
+ e.preventDefault();
213
+ e.stopPropagation();
214
+ setIsDragging(true);
215
+ }}
216
+ onDragOver={(e) => {
217
+ e.preventDefault();
218
+ e.stopPropagation();
219
+ setIsDragging(true);
220
+ }}
235
221
  onDrop={(e) => handleDrop(e, null)}
236
222
  onDragLeave={(e) => {
237
223
  if (!e.currentTarget.contains(e.relatedTarget as Node)) {
224
+ setIsDragging(false);
238
225
  setDropIndicatorPosition(null);
239
226
  }
240
227
  }}
@@ -260,8 +247,16 @@ export function LayoutContainer({
260
247
  block={block}
261
248
  onUpdate={(data) => onBlockUpdate(block.id, data, containerId)}
262
249
  onDelete={() => onBlockDelete(block.id, containerId)}
250
+ onDuplicate={() => helpers.duplicateBlock(block.id)}
263
251
  onMoveUp={index > 0 ? () => onBlockMove(block.id, index - 1, containerId) : undefined}
264
252
  onMoveDown={index < blocks.length - 1 ? () => onBlockMove(block.id, index + 1, containerId) : undefined}
253
+
254
+ // Essential for nested rendering: pass children and handlers to the Edit component
255
+ childBlocks={Array.isArray(block.children) && typeof block.children[0] === 'object' ? (block.children as Block[]) : []}
256
+ onChildBlockAdd={(type, idx, cid) => onBlockAdd(type, idx ?? 0, cid || containerId)}
257
+ onChildBlockUpdate={(bid, data, cid) => onBlockUpdate(bid, data, cid || containerId)}
258
+ onChildBlockDelete={(bid, cid) => onBlockDelete(bid, cid || containerId)}
259
+ onChildBlockMove={(bid, idx, cid) => onBlockMove(bid, idx, cid || containerId)}
265
260
  />
266
261
  </div>
267
262
  ))
@@ -271,52 +266,42 @@ export function LayoutContainer({
271
266
  }
272
267
 
273
268
  /**
274
- * Visual Line that shows where the block will land
269
+ * Visual Components
275
270
  */
276
- function DropIndicator({ position, darkMode }: { position: any; darkMode: boolean }) {
271
+ function DropIndicator({ position, darkMode }: { position: { top: number, left: number, width: number }, darkMode: boolean }) {
277
272
  return (
278
273
  <div
279
- className="absolute z-50 pointer-events-none"
274
+ className="absolute z-50 pointer-events-none transition-all duration-150 ease-out"
280
275
  style={{
281
- top: `${position.top - 12}px`,
282
- left: `${position.left}px`,
283
- width: `${position.width}px`,
284
- height: '24px',
276
+ top: position.top,
277
+ left: position.left,
278
+ width: position.width,
279
+ height: '4px',
280
+ marginTop: '-2px', // Center on the line
285
281
  }}
286
282
  >
287
- <div className={`absolute inset-0 rounded-lg border border-dashed backdrop-blur-sm
288
- ${darkMode ? 'bg-primary/20 border-primary/40' : 'bg-primary/10 border-primary/30'}`}
289
- />
290
- <div className={`absolute top-1/2 left-0 right-0 h-0.5 transform -translate-y-1/2
291
- ${darkMode ? 'bg-primary' : 'bg-primary'}`}
292
- />
293
- <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
294
- <div className="w-6 h-6 rounded-full flex items-center justify-center bg-primary shadow-lg">
295
- <div className="w-2 h-2 rounded-full bg-white" />
296
- </div>
297
- </div>
283
+ <div className="w-full h-full bg-primary rounded-full shadow-[0_0_8px_rgba(var(--primary-rgb),0.5)] animate-pulse" />
284
+ <div className="absolute left-0 top-1/2 -translate-y-1/2 -translate-x-1 w-2 h-2 rounded-full bg-primary" />
285
+ <div className="absolute right-0 top-1/2 -translate-y-1/2 translate-x-1 w-2 h-2 rounded-full bg-primary" />
298
286
  </div>
299
287
  );
300
288
  }
301
289
 
302
- /**
303
- * Placeholder when the container is empty
304
- */
305
- function EmptyState({ isDragging, darkMode, label }: { isDragging: boolean; darkMode: boolean; label: string }) {
290
+ function EmptyState({ isDragging, darkMode, label }: { isDragging: boolean, darkMode: boolean, label: string }) {
306
291
  return (
307
- <div className={`flex flex-col items-center justify-center py-12 px-6 rounded-2xl border border-dashed transition-all
308
- ${darkMode
309
- ? isDragging ? 'border-primary/50 bg-primary/10' : 'border-neutral-700 bg-neutral-800/20'
310
- : isDragging ? 'border-primary/50 bg-primary/5' : 'border-neutral-200 bg-neutral-50/30'
311
- }`}
292
+ <div
293
+ className={`flex flex-col items-center justify-center py-10 rounded-2xl border-2 border-dashed transition-all duration-300 ${isDragging
294
+ ? 'border-primary bg-primary/5 scale-[0.98]'
295
+ : 'border-neutral-200 dark:border-neutral-800 bg-neutral-50/50 dark:bg-neutral-900/20'
296
+ }`}
312
297
  >
313
- <div className={`p-3 rounded-full mb-3 ${darkMode ? 'bg-neutral-800' : 'bg-neutral-100'}`}>
314
- <Plus size={20} className={isDragging ? 'text-primary' : 'text-neutral-400'} />
315
- </div>
316
- <p className={`text-xs font-black uppercase tracking-wider
317
- ${isDragging ? 'text-primary' : 'text-neutral-500'}`}>
318
- {isDragging ? 'Drop Block Here' : label}
319
- </p>
298
+ <Plus
299
+ size={24}
300
+ className={`mb-2 transition-colors ${isDragging ? 'text-primary' : 'text-neutral-300 dark:text-neutral-700'}`}
301
+ />
302
+ <span className={`text-[10px] font-bold uppercase tracking-widest ${isDragging ? 'text-primary' : 'text-neutral-400 dark:text-neutral-600'}`}>
303
+ {label}
304
+ </span>
320
305
  </div>
321
306
  );
322
- }
307
+ }
@@ -67,26 +67,25 @@ export function CustomBlockItem({
67
67
  onMouseDown={handleMouseDown}
68
68
  onMouseMove={handleMouseMove}
69
69
  onClick={handleClick}
70
- className="p-4 rounded-xl border border-dashboard-border bg-dashboard-bg hover:border-primary cursor-pointer transition-all group"
70
+ className="p-5 rounded-[1.5rem] border border-dashboard-border/50 bg-dashboard-card/30 backdrop-blur-sm hover:border-primary/40 hover:bg-primary/[0.02] hover:shadow-2xl hover:shadow-primary/10 transition-all duration-500 cursor-grab active:cursor-grabbing group"
71
71
  title={description}
72
72
  >
73
- <div className="flex items-center justify-between mb-2">
74
- <div className="flex items-center gap-2">
75
- <div className="text-neutral-500 dark:text-neutral-400 group-hover:text-primary dark:group-hover:text-primary transition-colors">
73
+ <div className="flex items-center justify-between mb-3">
74
+ <div className="flex items-center gap-3">
75
+ <div className="text-dashboard-text-secondary group-hover:text-primary transition-all duration-500 group-hover:scale-110">
76
76
  {icon}
77
77
  </div>
78
- <span className="text-[10px] font-bold uppercase tracking-wider text-neutral-700 dark:text-neutral-300 group-hover:text-neutral-950 dark:group-hover:text-white transition-colors">
78
+ <span className="text-[10px] font-black uppercase tracking-[0.2em] text-dashboard-text-secondary group-hover:text-dashboard-text transition-colors">
79
79
  {name}
80
80
  </span>
81
81
  </div>
82
- <GripVertical size={12} className="text-neutral-400 dark:text-neutral-500 group-hover:text-neutral-600 dark:group-hover:text-neutral-400" />
82
+ <GripVertical size={14} className="text-dashboard-text-secondary/30 group-hover:text-primary transition-colors" />
83
83
  </div>
84
84
  {description && (
85
- <p className="text-[9px] text-neutral-500 dark:text-neutral-400 leading-relaxed line-clamp-2">
85
+ <p className="text-[9px] text-dashboard-text-secondary/60 leading-relaxed line-clamp-2 italic font-medium">
86
86
  {description}
87
87
  </p>
88
88
  )}
89
89
  </div>
90
90
  );
91
91
  }
92
-
@@ -16,20 +16,31 @@ export interface EditorCanvasProps {
16
16
  siteId: string;
17
17
  locale: string;
18
18
  darkMode: boolean;
19
+ tags?: string[];
19
20
  backgroundColors?: {
20
21
  light: string;
21
22
  dark?: string;
22
23
  };
23
24
  featuredImage?: {
24
25
  id?: string; // Semantic ID - plugin-images handles transforms
26
+ src?: string; // Preview URL
25
27
  alt?: string;
26
28
  };
29
+ LayoutWrapper?: React.ComponentType<{
30
+ children: React.ReactNode;
31
+ header?: React.ReactNode;
32
+ footer?: React.ReactNode;
33
+ heroImage?: React.ReactNode;
34
+ isPreview?: boolean;
35
+ isWrapper?: boolean;
36
+ tags?: string[];
37
+ }>;
27
38
  onTitleChange: (title: string) => void;
28
39
  onHeroBlockUpdate: (data: Partial<Block['data']>) => void;
29
40
  onHeroBlockDelete: () => void;
30
41
  onBlockAdd: (type: string, index: number, containerId?: string) => void;
31
- onBlockUpdate: (id: string, data: Partial<Block['data']>) => void;
32
- onBlockDelete: (id: string) => void;
42
+ onBlockUpdate: (id: string, data: Partial<Block['data']>, containerId?: string) => void;
43
+ onBlockDelete: (id: string, containerId?: string) => void;
33
44
  onBlockMove: (id: string, newIndex: number, containerId?: string) => void;
34
45
  }
35
46
 
@@ -42,8 +53,10 @@ export function EditorCanvas({
42
53
  siteId,
43
54
  locale,
44
55
  darkMode,
56
+ tags = [],
45
57
  backgroundColors,
46
58
  featuredImage,
59
+ LayoutWrapper,
47
60
  onTitleChange,
48
61
  onHeroBlockUpdate,
49
62
  onHeroBlockDelete,
@@ -62,97 +75,139 @@ export function EditorCanvas({
62
75
  }
63
76
  }, [title]);
64
77
 
65
- return (
66
- <div
67
- className="flex-1 overflow-y-auto overflow-x-hidden pb-40 px-6 custom-scrollbar selection:bg-primary/20 dark:selection:bg-primary/30 min-h-0"
68
- style={{
69
- backgroundColor: backgroundColors
70
- ? (darkMode && backgroundColors.dark
71
- ? backgroundColors.dark
72
- : backgroundColors.light)
73
- : undefined,
74
- }}
75
- >
76
- <div className={`mx-auto transition-all duration-500 max-w-7xl w-full`}>
77
- {/* Hero Block - Only show editable version when NOT in preview mode */}
78
- {!isPreviewMode && heroBlockDefinition && heroBlock && (
79
- <div className="mb-12">
80
- <BlockWrapper
81
- block={heroBlock}
82
- onUpdate={onHeroBlockUpdate}
83
- onDelete={onHeroBlockDelete}
84
- onMoveUp={() => { }}
85
- onMoveDown={() => { }}
86
- allBlocks={[heroBlock]}
87
- />
88
- </div>
78
+ const renderPreviewContent = () => {
79
+ // Filter out hero block from content if it's already rendered in header
80
+ const mainContent = heroBlockDefinition && heroBlock
81
+ ? contentBlocks.filter(b => b.type !== 'hero')
82
+ : contentBlocks;
83
+
84
+ const commonContext = {
85
+ siteId,
86
+ locale,
87
+ fallbackImage: featuredImage ? {
88
+ id: featuredImage.id,
89
+ src: featuredImage.src,
90
+ alt: featuredImage.alt,
91
+ } : undefined,
92
+ };
93
+
94
+ return (
95
+ <div className="space-y-8">
96
+ {!heroBlockDefinition && title && (
97
+ <h1 className="text-5xl font-serif font-medium leading-tight mb-12" style={{ color: 'var(--color-forest, var(--color-neutral-950))' }}>
98
+ {title}
99
+ </h1>
89
100
  )}
90
101
 
91
- {isPreviewMode ? (
102
+ {mainContent.length > 0 ? (
92
103
  <div className="space-y-8">
93
- {heroBlockDefinition && heroBlock && (
104
+ {mainContent.map((block) => (
94
105
  <BlockRenderer
95
- block={heroBlock}
96
- context={{
97
- siteId,
98
- locale,
99
- // Pass featured image as fallback for hero block
100
- // Only pass id and alt - plugin-images handles transforms
101
- fallbackImage: featuredImage ? {
102
- id: featuredImage.id,
103
- alt: featuredImage.alt,
104
- } : undefined,
105
- }}
106
+ key={block.id}
107
+ block={block}
108
+ context={commonContext}
106
109
  />
107
- )}
108
-
109
- {!heroBlockDefinition && title && (
110
- <h1 className="text-5xl font-serif font-medium text-neutral-950 dark:text-white leading-tight mb-12">
111
- {title}
112
- </h1>
113
- )}
114
-
115
- {contentBlocks.length > 0 ? (
116
- <div className="space-y-8">
117
- {contentBlocks.map((block) => (
118
- <BlockRenderer
119
- key={block.id}
120
- block={block}
121
- context={{ siteId, locale }}
122
- />
123
- ))}
124
- </div>
125
- ) : (
126
- <div className="text-center py-20 text-neutral-400 dark:text-neutral-500">
127
- <p className="text-sm">No content blocks yet. Switch to Edit mode to add blocks.</p>
128
- </div>
129
- )}
110
+ ))}
130
111
  </div>
131
112
  ) : (
132
- <>
133
- {!heroBlockDefinition && (
134
- <div className="mb-12">
135
- <textarea
136
- ref={titleRef}
137
- rows={1}
138
- value={title}
139
- onChange={(e) => onTitleChange(e.target.value)}
140
- placeholder="The title of your story..."
141
- className="w-full bg-transparent border-none outline-none text-5xl font-serif font-medium placeholder:text-neutral-500 dark:placeholder:text-neutral-500 resize-none leading-tight transition-colors duration-300 text-neutral-950 dark:text-white"
142
- />
143
- </div>
144
- )}
113
+ <div className="text-center py-20 text-neutral-400 dark:text-neutral-500">
114
+ <p className="text-sm">No content blocks yet. Switch to Edit mode to add blocks.</p>
115
+ </div>
116
+ )}
117
+ </div>
118
+ );
119
+ };
120
+
121
+ const renderEditorContent = () => {
122
+ // Filter out hero block from content if it's already rendered in header
123
+ const mainBlocks = heroBlockDefinition && heroBlock
124
+ ? contentBlocks.filter(b => b.type !== 'hero')
125
+ : contentBlocks;
145
126
 
146
- <EditorBody
147
- blocks={contentBlocks}
148
- darkMode={darkMode}
149
- backgroundColors={backgroundColors}
150
- onBlockAdd={onBlockAdd}
151
- onBlockUpdate={onBlockUpdate}
152
- onBlockDelete={onBlockDelete}
153
- onBlockMove={onBlockMove}
127
+ return (
128
+ <div className="space-y-12">
129
+ {!heroBlockDefinition && (
130
+ <div className="mb-12">
131
+ <textarea
132
+ ref={titleRef}
133
+ rows={1}
134
+ value={title}
135
+ onChange={(e) => onTitleChange(e.target.value)}
136
+ placeholder="The title of your story..."
137
+ className="w-full bg-transparent border-none outline-none text-5xl md:text-7xl lg:text-9xl font-serif font-medium placeholder:text-neutral-500 dark:placeholder:text-neutral-500 resize-none leading-tight transition-colors duration-300 italic"
138
+ style={{ color: 'var(--color-forest, var(--color-neutral-950))' }}
154
139
  />
155
- </>
140
+ </div>
141
+ )}
142
+
143
+ <EditorBody
144
+ blocks={mainBlocks}
145
+ darkMode={darkMode}
146
+ backgroundColors={backgroundColors}
147
+ onBlockAdd={onBlockAdd}
148
+ onBlockUpdate={onBlockUpdate}
149
+ onBlockDelete={onBlockDelete}
150
+ onBlockMove={onBlockMove}
151
+ />
152
+ </div>
153
+ );
154
+ };
155
+
156
+ // Render Hero Block for Slot Injection
157
+ const heroElement = heroBlockDefinition && heroBlock ? (
158
+ isPreviewMode ? (
159
+ <BlockRenderer
160
+ block={heroBlock}
161
+ context={{
162
+ siteId,
163
+ locale,
164
+ fallbackImage: featuredImage ? {
165
+ id: featuredImage.id,
166
+ src: featuredImage.src,
167
+ alt: featuredImage.alt,
168
+ } : undefined,
169
+ }}
170
+ />
171
+ ) : (
172
+ <BlockWrapper
173
+ block={heroBlock}
174
+ onUpdate={onHeroBlockUpdate}
175
+ onDelete={onHeroBlockDelete}
176
+ onMoveUp={() => { }}
177
+ onMoveDown={() => { }}
178
+ />
179
+ )
180
+ ) : null;
181
+
182
+ return (
183
+ <div
184
+ data-theme={darkMode ? 'dark' : 'light'}
185
+ className="flex-1 overflow-y-auto overflow-x-hidden pb-40 px-4 lg:px-10 custom-scrollbar selection:bg-primary/20 dark:selection:bg-primary/30 min-h-0 transition-colors duration-300"
186
+ style={{
187
+ backgroundColor: darkMode
188
+ ? (backgroundColors?.dark || '#0c0c0c')
189
+ : (backgroundColors?.light || '#ffffff')
190
+ }}
191
+ >
192
+ <div className={`${!LayoutWrapper ? 'mx-auto transition-all duration-500 max-w-[1600px] w-full px-2 pt-8' : ''}`}>
193
+ {LayoutWrapper ? (
194
+ <div data-theme={darkMode ? 'dark' : 'light'} data-dashboard="false" className="theme-reset contents">
195
+ <LayoutWrapper
196
+ header={heroElement}
197
+ isPreview={true}
198
+ isWrapper={true}
199
+ tags={tags}
200
+ >
201
+ {isPreviewMode ? renderPreviewContent() : renderEditorContent()}
202
+ </LayoutWrapper>
203
+ </div>
204
+ ) : (
205
+ <div data-theme={darkMode ? 'dark' : 'light'} data-dashboard="false" className="theme-reset">
206
+ {heroElement}
207
+ <div className="max-w-[1600px] mx-auto px-4 sm:px-6 lg:px-8 py-6 prose prose-lg max-w-none prose-headings:font-serif">
208
+ {isPreviewMode ? renderPreviewContent() : renderEditorContent()}
209
+ </div>
210
+ </div>
156
211
  )}
157
212
  </div>
158
213
  </div>
@@ -7,16 +7,32 @@ import type { BlockTypeDefinition } from '../../../types/block';
7
7
 
8
8
  export interface EditorLibraryProps {
9
9
  registeredBlocks: BlockTypeDefinition[];
10
- onAddBlock: (type: string) => void;
10
+ onAddBlock: (blockType: string) => void;
11
11
  }
12
12
 
13
+ /**
14
+ * Editor Library Component
15
+ * Displays available blocks that can be added to the canvas
16
+ */
13
17
  export function EditorLibrary({ registeredBlocks, onAddBlock }: EditorLibraryProps) {
14
- // Get all registered blocks from state (excluding Hero block from sidebar)
15
- const allBlocks = registeredBlocks.filter(block => block.type !== 'hero');
16
- const textBlocks = allBlocks.filter(block => block.category === 'text');
17
- const customBlocks = allBlocks.filter(block => block.category === 'custom');
18
- const mediaBlocks = allBlocks.filter(block => block.category === 'media');
19
- const layoutBlocks = allBlocks.filter(block => block.category === 'layout');
18
+ // Categorize blocks
19
+ const allBlocks = registeredBlocks || [];
20
+
21
+ const textBlocks = allBlocks.filter(bl =>
22
+ bl.category === 'text' || bl.type === 'heading' || bl.type === 'paragraph' || bl.type === 'list'
23
+ );
24
+
25
+ const mediaBlocks = allBlocks.filter(bl =>
26
+ bl.category === 'media' || bl.type === 'image' || bl.type === 'gallery' || bl.type === 'video'
27
+ );
28
+
29
+ const layoutBlocks = allBlocks.filter(bl =>
30
+ bl.category === 'layout' || bl.type === 'section' || bl.type === 'columns' || bl.type === 'spacer'
31
+ );
32
+
33
+ const customBlocks = allBlocks.filter(bl =>
34
+ !textBlocks.includes(bl) && !mediaBlocks.includes(bl) && !layoutBlocks.includes(bl)
35
+ );
20
36
 
21
37
  return (
22
38
  <div className="p-6 w-72 min-w-0 max-w-full">
@@ -108,10 +124,9 @@ export function EditorLibrary({ registeredBlocks, onAddBlock }: EditorLibraryPro
108
124
  </div>
109
125
  )}
110
126
 
111
- {/* Empty State */}
112
127
  {allBlocks.length === 0 && (
113
- <div className="text-center py-12">
114
- <Library size={32} className="mx-auto text-neutral-300 dark:text-neutral-700 mb-4" />
128
+ <div className="flex flex-col items-center justify-center py-12 text-center">
129
+ <Library className="size-12 text-neutral-300 dark:text-neutral-700 mb-4" />
115
130
  <p className="text-xs text-neutral-500 dark:text-neutral-400">
116
131
  No blocks registered yet.
117
132
  </p>