@jhits/plugin-blog 0.0.14 → 0.0.16

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 (208) hide show
  1. package/package.json +5 -4
  2. package/src/api/categories.ts +43 -0
  3. package/src/api/check-title.ts +60 -0
  4. package/src/api/config-handler.ts +76 -0
  5. package/src/api/handler.ts +418 -0
  6. package/src/api/index.ts +33 -0
  7. package/src/api/route.ts +116 -0
  8. package/src/api/router.ts +128 -0
  9. package/src/api-server.ts +11 -0
  10. package/src/config.ts +161 -0
  11. package/src/hooks/index.d.ts +8 -0
  12. package/src/hooks/index.d.ts.map +1 -0
  13. package/src/hooks/index.ts +9 -0
  14. package/src/hooks/useBlog.d.ts +31 -0
  15. package/src/hooks/useBlog.d.ts.map +1 -0
  16. package/src/hooks/useBlog.ts +85 -0
  17. package/src/hooks/useBlogs.d.ts +39 -0
  18. package/src/hooks/useBlogs.d.ts.map +1 -0
  19. package/src/hooks/useBlogs.ts +123 -0
  20. package/src/hooks/useCategories.d.ts +9 -0
  21. package/src/hooks/useCategories.d.ts.map +1 -0
  22. package/src/hooks/useCategories.ts +76 -0
  23. package/src/index.server.ts +14 -0
  24. package/src/index.tsx +335 -0
  25. package/src/init.tsx +63 -0
  26. package/src/lib/blocks/BlockRenderer.d.ts +54 -0
  27. package/src/lib/blocks/BlockRenderer.d.ts.map +1 -0
  28. package/src/lib/blocks/BlockRenderer.tsx +141 -0
  29. package/src/lib/blocks/index.ts +6 -0
  30. package/src/lib/config-storage.d.ts +30 -0
  31. package/src/lib/config-storage.d.ts.map +1 -0
  32. package/src/lib/config-storage.ts +65 -0
  33. package/src/lib/index.ts +9 -0
  34. package/src/lib/layouts/blocks/ColumnsBlock.d.ts +25 -0
  35. package/src/lib/layouts/blocks/ColumnsBlock.d.ts.map +1 -0
  36. package/src/lib/layouts/blocks/ColumnsBlock.tsx +298 -0
  37. package/src/lib/layouts/blocks/ColumnsBlock.tsx.tmp +81 -0
  38. package/src/lib/layouts/blocks/SectionBlock.d.ts +25 -0
  39. package/src/lib/layouts/blocks/SectionBlock.d.ts.map +1 -0
  40. package/src/lib/layouts/blocks/SectionBlock.tsx +104 -0
  41. package/src/lib/layouts/blocks/index.ts +8 -0
  42. package/src/lib/layouts/index.d.ts +23 -0
  43. package/src/lib/layouts/index.d.ts.map +1 -0
  44. package/src/lib/layouts/index.ts +52 -0
  45. package/src/lib/layouts/registerLayoutBlocks.d.ts +9 -0
  46. package/src/lib/layouts/registerLayoutBlocks.d.ts.map +1 -0
  47. package/src/lib/layouts/registerLayoutBlocks.ts +64 -0
  48. package/src/lib/mappers/apiMapper.d.ts +66 -0
  49. package/src/lib/mappers/apiMapper.d.ts.map +1 -0
  50. package/src/lib/mappers/apiMapper.ts +254 -0
  51. package/src/lib/migration/index.ts +6 -0
  52. package/src/lib/migration/mapper.ts +140 -0
  53. package/src/lib/rich-text/RichTextEditor.d.ts +45 -0
  54. package/src/lib/rich-text/RichTextEditor.d.ts.map +1 -0
  55. package/src/lib/rich-text/RichTextEditor.tsx +826 -0
  56. package/src/lib/rich-text/RichTextPreview.d.ts +16 -0
  57. package/src/lib/rich-text/RichTextPreview.d.ts.map +1 -0
  58. package/src/lib/rich-text/RichTextPreview.tsx +210 -0
  59. package/src/lib/rich-text/index.d.ts +9 -0
  60. package/src/lib/rich-text/index.d.ts.map +1 -0
  61. package/src/lib/rich-text/index.ts +10 -0
  62. package/src/lib/utils/blockHelpers.d.ts +23 -0
  63. package/src/lib/utils/blockHelpers.d.ts.map +1 -0
  64. package/src/lib/utils/blockHelpers.ts +72 -0
  65. package/src/lib/utils/configValidation.d.ts +23 -0
  66. package/src/lib/utils/configValidation.d.ts.map +1 -0
  67. package/src/lib/utils/configValidation.ts +137 -0
  68. package/src/lib/utils/index.ts +8 -0
  69. package/src/lib/utils/slugify.ts +79 -0
  70. package/src/registry/BlockRegistry.d.ts +62 -0
  71. package/src/registry/BlockRegistry.d.ts.map +1 -0
  72. package/src/registry/BlockRegistry.ts +139 -0
  73. package/src/registry/index.d.ts +6 -0
  74. package/src/registry/index.d.ts.map +1 -0
  75. package/src/registry/index.ts +11 -0
  76. package/src/state/EditorContext.d.ts +45 -0
  77. package/src/state/EditorContext.d.ts.map +1 -0
  78. package/src/state/EditorContext.tsx +283 -0
  79. package/src/state/index.d.ts +7 -0
  80. package/src/state/index.d.ts.map +1 -0
  81. package/src/state/index.ts +8 -0
  82. package/src/state/reducer.d.ts +11 -0
  83. package/src/state/reducer.d.ts.map +1 -0
  84. package/src/state/reducer.ts +694 -0
  85. package/src/state/types.d.ts +162 -0
  86. package/src/state/types.d.ts.map +1 -0
  87. package/src/state/types.ts +160 -0
  88. package/src/types/block.d.ts +221 -0
  89. package/src/types/block.d.ts.map +1 -0
  90. package/src/types/block.ts +269 -0
  91. package/src/types/index.d.ts +8 -0
  92. package/src/types/index.d.ts.map +1 -0
  93. package/src/types/index.ts +17 -0
  94. package/src/types/post.d.ts +136 -0
  95. package/src/types/post.d.ts.map +1 -0
  96. package/src/types/post.ts +169 -0
  97. package/src/utils/client.d.ts +48 -0
  98. package/src/utils/client.d.ts.map +1 -0
  99. package/src/utils/client.ts +122 -0
  100. package/src/utils/index.ts +7 -0
  101. package/src/views/CanvasEditor/BlockWrapper.d.ts +16 -0
  102. package/src/views/CanvasEditor/BlockWrapper.d.ts.map +1 -0
  103. package/src/views/CanvasEditor/BlockWrapper.tsx +522 -0
  104. package/src/views/CanvasEditor/CanvasEditorView.d.ts +14 -0
  105. package/src/views/CanvasEditor/CanvasEditorView.d.ts.map +1 -0
  106. package/src/views/CanvasEditor/CanvasEditorView.tsx +337 -0
  107. package/src/views/CanvasEditor/EditorBody.d.ts +22 -0
  108. package/src/views/CanvasEditor/EditorBody.d.ts.map +1 -0
  109. package/src/views/CanvasEditor/EditorBody.tsx +665 -0
  110. package/src/views/CanvasEditor/EditorHeader.d.ts +18 -0
  111. package/src/views/CanvasEditor/EditorHeader.d.ts.map +1 -0
  112. package/src/views/CanvasEditor/EditorHeader.tsx +268 -0
  113. package/src/views/CanvasEditor/LayoutContainer.d.ts +17 -0
  114. package/src/views/CanvasEditor/LayoutContainer.d.ts.map +1 -0
  115. package/src/views/CanvasEditor/LayoutContainer.tsx +322 -0
  116. package/src/views/CanvasEditor/SaveConfirmationModal.d.ts +13 -0
  117. package/src/views/CanvasEditor/SaveConfirmationModal.d.ts.map +1 -0
  118. package/src/views/CanvasEditor/SaveConfirmationModal.tsx +233 -0
  119. package/src/views/CanvasEditor/components/CustomBlockItem.d.ts +14 -0
  120. package/src/views/CanvasEditor/components/CustomBlockItem.d.ts.map +1 -0
  121. package/src/views/CanvasEditor/components/CustomBlockItem.tsx +92 -0
  122. package/src/views/CanvasEditor/components/EditorCanvas.d.ts +29 -0
  123. package/src/views/CanvasEditor/components/EditorCanvas.d.ts.map +1 -0
  124. package/src/views/CanvasEditor/components/EditorCanvas.tsx +160 -0
  125. package/src/views/CanvasEditor/components/EditorLibrary.d.ts +7 -0
  126. package/src/views/CanvasEditor/components/EditorLibrary.d.ts.map +1 -0
  127. package/src/views/CanvasEditor/components/EditorLibrary.tsx +122 -0
  128. package/src/views/CanvasEditor/components/EditorSidebar.d.ts +13 -0
  129. package/src/views/CanvasEditor/components/EditorSidebar.d.ts.map +1 -0
  130. package/src/views/CanvasEditor/components/EditorSidebar.tsx +181 -0
  131. package/src/views/CanvasEditor/components/ErrorBanner.d.ts +6 -0
  132. package/src/views/CanvasEditor/components/ErrorBanner.d.ts.map +1 -0
  133. package/src/views/CanvasEditor/components/ErrorBanner.tsx +31 -0
  134. package/src/views/CanvasEditor/components/FeaturedMediaSection.d.ts +25 -0
  135. package/src/views/CanvasEditor/components/FeaturedMediaSection.d.ts.map +1 -0
  136. package/src/views/CanvasEditor/components/FeaturedMediaSection.tsx +341 -0
  137. package/src/views/CanvasEditor/components/LibraryItem.d.ts +14 -0
  138. package/src/views/CanvasEditor/components/LibraryItem.d.ts.map +1 -0
  139. package/src/views/CanvasEditor/components/LibraryItem.tsx +80 -0
  140. package/src/views/CanvasEditor/components/PrivacySettingsSection.d.ts +15 -0
  141. package/src/views/CanvasEditor/components/PrivacySettingsSection.d.ts.map +1 -0
  142. package/src/views/CanvasEditor/components/PrivacySettingsSection.tsx +212 -0
  143. package/src/views/CanvasEditor/components/index.d.ts +21 -0
  144. package/src/views/CanvasEditor/components/index.d.ts.map +1 -0
  145. package/src/views/CanvasEditor/components/index.ts +28 -0
  146. package/src/views/CanvasEditor/hooks/index.d.ts +10 -0
  147. package/src/views/CanvasEditor/hooks/index.d.ts.map +1 -0
  148. package/src/views/CanvasEditor/hooks/index.ts +10 -0
  149. package/src/views/CanvasEditor/hooks/useHeroBlock.d.ts +8 -0
  150. package/src/views/CanvasEditor/hooks/useHeroBlock.d.ts.map +1 -0
  151. package/src/views/CanvasEditor/hooks/useHeroBlock.ts +103 -0
  152. package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.d.ts +3 -0
  153. package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.d.ts.map +1 -0
  154. package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.ts +142 -0
  155. package/src/views/CanvasEditor/hooks/usePostLoader.d.ts +5 -0
  156. package/src/views/CanvasEditor/hooks/usePostLoader.d.ts.map +1 -0
  157. package/src/views/CanvasEditor/hooks/usePostLoader.ts +39 -0
  158. package/src/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts +2 -0
  159. package/src/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts.map +1 -0
  160. package/src/views/CanvasEditor/hooks/useRegisteredBlocks.ts +55 -0
  161. package/src/views/CanvasEditor/hooks/useUnsavedChanges.d.ts +25 -0
  162. package/src/views/CanvasEditor/hooks/useUnsavedChanges.d.ts.map +1 -0
  163. package/src/views/CanvasEditor/hooks/useUnsavedChanges.ts +339 -0
  164. package/src/views/CanvasEditor/index.d.ts +16 -0
  165. package/src/views/CanvasEditor/index.d.ts.map +1 -0
  166. package/src/views/CanvasEditor/index.ts +16 -0
  167. package/src/views/PostManager/EmptyState.d.ts +10 -0
  168. package/src/views/PostManager/EmptyState.d.ts.map +1 -0
  169. package/src/views/PostManager/EmptyState.tsx +42 -0
  170. package/src/views/PostManager/PostActionsMenu.d.ts +12 -0
  171. package/src/views/PostManager/PostActionsMenu.d.ts.map +1 -0
  172. package/src/views/PostManager/PostActionsMenu.tsx +112 -0
  173. package/src/views/PostManager/PostCards.d.ts +15 -0
  174. package/src/views/PostManager/PostCards.d.ts.map +1 -0
  175. package/src/views/PostManager/PostCards.tsx +197 -0
  176. package/src/views/PostManager/PostFilters.d.ts +16 -0
  177. package/src/views/PostManager/PostFilters.d.ts.map +1 -0
  178. package/src/views/PostManager/PostFilters.tsx +95 -0
  179. package/src/views/PostManager/PostManagerView.d.ts +11 -0
  180. package/src/views/PostManager/PostManagerView.d.ts.map +1 -0
  181. package/src/views/PostManager/PostManagerView.tsx +289 -0
  182. package/src/views/PostManager/PostStats.d.ts +11 -0
  183. package/src/views/PostManager/PostStats.d.ts.map +1 -0
  184. package/src/views/PostManager/PostStats.tsx +81 -0
  185. package/src/views/PostManager/PostTable.d.ts +15 -0
  186. package/src/views/PostManager/PostTable.d.ts.map +1 -0
  187. package/src/views/PostManager/PostTable.tsx +230 -0
  188. package/src/views/PostManager/index.d.ts +12 -0
  189. package/src/views/PostManager/index.d.ts.map +1 -0
  190. package/src/views/PostManager/index.ts +15 -0
  191. package/src/views/Preview/PreviewBridgeView.d.ts +12 -0
  192. package/src/views/Preview/PreviewBridgeView.d.ts.map +1 -0
  193. package/src/views/Preview/PreviewBridgeView.tsx +64 -0
  194. package/src/views/Preview/index.d.ts +6 -0
  195. package/src/views/Preview/index.d.ts.map +1 -0
  196. package/src/views/Preview/index.ts +7 -0
  197. package/src/views/Settings/SettingsView.d.ts +10 -0
  198. package/src/views/Settings/SettingsView.d.ts.map +1 -0
  199. package/src/views/Settings/SettingsView.tsx +298 -0
  200. package/src/views/Settings/index.d.ts +6 -0
  201. package/src/views/Settings/index.d.ts.map +1 -0
  202. package/src/views/Settings/index.ts +7 -0
  203. package/src/views/SlugSEO/SlugSEOManagerView.d.ts +12 -0
  204. package/src/views/SlugSEO/SlugSEOManagerView.d.ts.map +1 -0
  205. package/src/views/SlugSEO/SlugSEOManagerView.tsx +94 -0
  206. package/src/views/SlugSEO/index.d.ts +6 -0
  207. package/src/views/SlugSEO/index.d.ts.map +1 -0
  208. package/src/views/SlugSEO/index.ts +7 -0
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Block Registry
3
+ * Dynamic registry for all block types in the system
4
+ * Multi-Tenant Architecture: Blocks are provided by client applications
5
+ *
6
+ * The registry is a singleton that starts empty and is populated by client apps
7
+ */
8
+
9
+ import {
10
+ BlockTypeDefinition,
11
+ BlockRegistry as IBlockRegistry,
12
+ ClientBlockDefinition
13
+ } from '../types/block';
14
+
15
+ /**
16
+ * Dynamic Block Registry Implementation
17
+ * No default blocks - all blocks must be registered by client applications
18
+ */
19
+ class BlockRegistryImpl implements IBlockRegistry {
20
+ private _types: Map<string, BlockTypeDefinition> = new Map();
21
+
22
+ get types(): Map<string, BlockTypeDefinition> {
23
+ return this._types;
24
+ }
25
+
26
+ /**
27
+ * Register a single block type
28
+ */
29
+ register(definition: BlockTypeDefinition): void {
30
+ // Validate that components are provided
31
+ if (!definition.components || !definition.components.Edit || !definition.components.Preview) {
32
+ throw new Error(
33
+ `Block type "${definition.type}" must provide both Edit and Preview components. ` +
34
+ `Use registerClientBlocks() for client-provided blocks.`
35
+ );
36
+ }
37
+
38
+ // Silently overwrite if already registered (expected in React 18 Strict Mode)
39
+ this._types.set(definition.type, definition);
40
+ }
41
+
42
+ /**
43
+ * Register multiple client blocks at once
44
+ * This is the primary method for client applications to register their blocks
45
+ */
46
+ registerClientBlocks(definitions: ClientBlockDefinition[]): void {
47
+ for (const def of definitions) {
48
+ // Validate required fields
49
+ if (!def.type || !def.name || !def.components) {
50
+ console.error('Invalid block definition:', def);
51
+ throw new Error(
52
+ 'Block definition must have: type, name, and components (with Edit and Preview)'
53
+ );
54
+ }
55
+
56
+ // Validate components
57
+ if (!def.components.Edit || !def.components.Preview) {
58
+ throw new Error(
59
+ `Block "${def.type}" must provide both Edit and Preview components in the components object`
60
+ );
61
+ }
62
+
63
+ // Convert ClientBlockDefinition to BlockTypeDefinition
64
+ const blockDefinition: BlockTypeDefinition = {
65
+ type: def.type,
66
+ name: def.name,
67
+ description: def.description,
68
+ icon: def.icon || def.components.Icon,
69
+ defaultData: def.defaultData,
70
+ validate: def.validate,
71
+ isContainer: def.isContainer,
72
+ allowedChildren: def.allowedChildren,
73
+ category: def.category || 'custom',
74
+ components: def.components,
75
+ };
76
+
77
+ this.register(blockDefinition);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Get block type definition by type string
83
+ */
84
+ get(type: string): BlockTypeDefinition | undefined {
85
+ return this._types.get(type);
86
+ }
87
+
88
+ /**
89
+ * Get all registered block types
90
+ */
91
+ getAll(): BlockTypeDefinition[] {
92
+ return Array.from(this._types.values());
93
+ }
94
+
95
+ /**
96
+ * Get block types filtered by category
97
+ */
98
+ getByCategory(category: BlockTypeDefinition['category']): BlockTypeDefinition[] {
99
+ return this.getAll().filter(block => block.category === category);
100
+ }
101
+
102
+ /**
103
+ * Check if a block type is registered
104
+ */
105
+ has(type: string): boolean {
106
+ return this._types.has(type);
107
+ }
108
+
109
+ /**
110
+ * Unregister a block type (useful for testing or dynamic loading)
111
+ */
112
+ unregister(type: string): boolean {
113
+ return this._types.delete(type);
114
+ }
115
+
116
+ /**
117
+ * Clear all registered block types
118
+ * Useful for testing or resetting the registry
119
+ */
120
+ clear(): void {
121
+ this._types.clear();
122
+ }
123
+
124
+ /**
125
+ * Get count of registered blocks
126
+ */
127
+ getCount(): number {
128
+ return this._types.size;
129
+ }
130
+ }
131
+
132
+ // Singleton instance - starts empty, populated by client apps
133
+ export const blockRegistry = new BlockRegistryImpl();
134
+
135
+ /**
136
+ * NOTE: No default blocks are registered automatically.
137
+ * Client applications must call registerClientBlocks() to populate the registry.
138
+ * This ensures the editor is a true "shell" without hardcoded blocks.
139
+ */
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Registry exports
3
+ */
4
+ export { blockRegistry } from './BlockRegistry';
5
+ export type { ClientBlockDefinition, BlockTypeDefinition, BlockRegistry, } from '../types/block';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EACR,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,GAChB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Registry exports
3
+ */
4
+
5
+ export { blockRegistry } from './BlockRegistry';
6
+ export type {
7
+ ClientBlockDefinition,
8
+ BlockTypeDefinition,
9
+ BlockRegistry,
10
+ } from '../types/block';
11
+
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Editor Context
3
+ * React Context for managing editor state
4
+ * Multi-Tenant: Accepts custom blocks from client applications
5
+ */
6
+ import React from 'react';
7
+ import { EditorContextValue, EditorState } from './types';
8
+ import { Block } from '../types/block';
9
+ import { ClientBlockDefinition } from '../types/block';
10
+ /**
11
+ * Editor Provider Props
12
+ */
13
+ export interface EditorProviderProps {
14
+ children: React.ReactNode;
15
+ /** Initial state (optional) */
16
+ initialState?: Partial<EditorState>;
17
+ /** Callback when save is triggered */
18
+ onSave?: (state: EditorState, heroBlock?: Block | null) => Promise<void>;
19
+ /**
20
+ * Custom blocks from client application
21
+ * These blocks will be registered in the BlockRegistry on mount
22
+ */
23
+ customBlocks?: ClientBlockDefinition[];
24
+ /** Enable dark mode for content area and wrappers (default: true) */
25
+ darkMode?: boolean;
26
+ /** Background colors for the editor */
27
+ backgroundColors?: {
28
+ /** Background color for light mode (REQUIRED) */
29
+ light: string;
30
+ /** Background color for dark mode (optional) */
31
+ dark?: string;
32
+ };
33
+ }
34
+ /**
35
+ * Editor Provider
36
+ * Provides editor state and actions to child components
37
+ * Automatically registers client-provided blocks on mount
38
+ */
39
+ export declare function EditorProvider({ children, initialState, onSave, customBlocks, darkMode, backgroundColors }: EditorProviderProps): import("react/jsx-runtime").JSX.Element;
40
+ /**
41
+ * Hook to access editor context
42
+ * @throws Error if used outside EditorProvider
43
+ */
44
+ export declare function useEditor(): EditorContextValue;
45
+ //# sourceMappingURL=EditorContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditorContext.d.ts","sourceRoot":"","sources":["EditorContext.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAmG,MAAM,OAAO,CAAC;AAExH,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAoC,MAAM,SAAS,CAAC;AAC5F,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAGvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAMvD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,+BAA+B;IAC/B,YAAY,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IACpC,sCAAsC;IACtC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,KAAK,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE;;;OAGG;IACH,YAAY,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACvC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,gBAAgB,CAAC,EAAE;QACf,iDAAiD;QACjD,KAAK,EAAE,MAAM,CAAC;QACd,gDAAgD;QAChD,IAAI,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACL;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC3B,QAAQ,EACR,YAAY,EACZ,MAAM,EACN,YAAiB,EACjB,QAAe,EACf,gBAAgB,EACnB,EAAE,mBAAmB,2CAqNrB;AAED;;;GAGG;AACH,wBAAgB,SAAS,IAAI,kBAAkB,CAM9C"}
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Editor Context
3
+ * React Context for managing editor state
4
+ * Multi-Tenant: Accepts custom blocks from client applications
5
+ */
6
+
7
+ 'use client';
8
+
9
+ import React, { createContext, useContext, useReducer, useCallback, useMemo, useEffect, useRef, useState } from 'react';
10
+ import { editorReducer } from './reducer';
11
+ import { EditorContextValue, EditorState, EditorAction, initialEditorState } from './types';
12
+ import { Block } from '../types/block';
13
+ import { BlogPost } from '../types/post';
14
+ import { blockRegistry } from '../registry/BlockRegistry';
15
+ import { ClientBlockDefinition } from '../types/block';
16
+ import { registerLayoutBlocks } from '../lib/layouts/registerLayoutBlocks';
17
+
18
+ // Create the context
19
+ const EditorContext = createContext<EditorContextValue | null>(null);
20
+
21
+ /**
22
+ * Editor Provider Props
23
+ */
24
+ export interface EditorProviderProps {
25
+ children: React.ReactNode;
26
+ /** Initial state (optional) */
27
+ initialState?: Partial<EditorState>;
28
+ /** Callback when save is triggered */
29
+ onSave?: (state: EditorState, heroBlock?: Block | null) => Promise<void>;
30
+ /**
31
+ * Custom blocks from client application
32
+ * These blocks will be registered in the BlockRegistry on mount
33
+ */
34
+ customBlocks?: ClientBlockDefinition[];
35
+ /** Enable dark mode for content area and wrappers (default: true) */
36
+ darkMode?: boolean;
37
+ /** Background colors for the editor */
38
+ backgroundColors?: {
39
+ /** Background color for light mode (REQUIRED) */
40
+ light: string;
41
+ /** Background color for dark mode (optional) */
42
+ dark?: string;
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Editor Provider
48
+ * Provides editor state and actions to child components
49
+ * Automatically registers client-provided blocks on mount
50
+ */
51
+ export function EditorProvider({
52
+ children,
53
+ initialState,
54
+ onSave,
55
+ customBlocks = [],
56
+ darkMode = true,
57
+ backgroundColors
58
+ }: EditorProviderProps) {
59
+ // Register core layout blocks on mount
60
+ useEffect(() => {
61
+ registerLayoutBlocks();
62
+ }, []);
63
+
64
+ // Register client blocks on mount
65
+ useEffect(() => {
66
+ if (customBlocks && customBlocks.length > 0) {
67
+ try {
68
+ blockRegistry.registerClientBlocks(customBlocks);
69
+ } catch (error) {
70
+ console.error('[EditorContext] Failed to register custom blocks:', error);
71
+ }
72
+ }
73
+ }, [customBlocks]);
74
+
75
+ const [state, dispatch] = useReducer(
76
+ editorReducer,
77
+ { ...initialEditorState, ...initialState }
78
+ );
79
+
80
+ // Use a ref to always have access to the latest state in callbacks
81
+ const stateRef = useRef(state);
82
+ stateRef.current = state;
83
+
84
+ // History state for undo/redo
85
+ const [history, setHistory] = useState<EditorState[]>([]);
86
+ const [historyIndex, setHistoryIndex] = useState(-1);
87
+ const isRestoringRef = useRef(false);
88
+ const MAX_HISTORY = 50; // Limit history to prevent memory issues
89
+
90
+ // Save current state to history after state changes (but not during undo/redo)
91
+ // Debounce history updates to avoid excessive re-renders
92
+ const historyTimeoutRef = useRef<NodeJS.Timeout | null>(null);
93
+
94
+ useEffect(() => {
95
+ if (isRestoringRef.current) {
96
+ isRestoringRef.current = false;
97
+ return;
98
+ }
99
+
100
+ // Clear existing timeout
101
+ if (historyTimeoutRef.current) {
102
+ clearTimeout(historyTimeoutRef.current);
103
+ }
104
+
105
+ // Debounce history updates to reduce re-renders
106
+ historyTimeoutRef.current = setTimeout(() => {
107
+ // Save current state to history
108
+ setHistory(prev => {
109
+ const newHistory = [...prev];
110
+ // Remove any future history if we're not at the end
111
+ if (historyIndex < newHistory.length - 1) {
112
+ newHistory.splice(historyIndex + 1);
113
+ }
114
+ // Add current state
115
+ newHistory.push({ ...state });
116
+ // Limit history size
117
+ if (newHistory.length > MAX_HISTORY) {
118
+ newHistory.shift();
119
+ return newHistory;
120
+ }
121
+ return newHistory;
122
+ });
123
+ setHistoryIndex(prev => {
124
+ const newIndex = prev + 1;
125
+ return newIndex >= MAX_HISTORY ? MAX_HISTORY - 1 : newIndex;
126
+ });
127
+ }, 300); // Debounce by 300ms
128
+
129
+ return () => {
130
+ if (historyTimeoutRef.current) {
131
+ clearTimeout(historyTimeoutRef.current);
132
+ }
133
+ };
134
+ }, [state.blocks, state.title, state.slug, state.seo, state.metadata, state.status, historyIndex]);
135
+
136
+ // Helper: Add a new block (supports nested containers)
137
+ const addBlock = useCallback((type: string, index?: number, containerId?: string) => {
138
+ const blockDefinition = blockRegistry.get(type);
139
+ if (!blockDefinition) {
140
+ console.warn(`Block type "${type}" not found in registry. Available types:`,
141
+ blockRegistry.getAll().map(b => b.type).join(', '));
142
+ return;
143
+ }
144
+
145
+ const newBlock: Block = {
146
+ id: `block-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
147
+ type,
148
+ data: { ...blockDefinition.defaultData },
149
+ };
150
+
151
+ dispatch({ type: 'ADD_BLOCK', payload: { block: newBlock, index, containerId } });
152
+ }, []);
153
+
154
+ // Helper: Update a block
155
+ const updateBlock = useCallback((id: string, data: Partial<Block['data']>) => {
156
+ dispatch({ type: 'UPDATE_BLOCK', payload: { id, data } });
157
+ }, []);
158
+
159
+ // Helper: Delete a block
160
+ const deleteBlock = useCallback((id: string) => {
161
+ dispatch({ type: 'DELETE_BLOCK', payload: { id } });
162
+ }, []);
163
+
164
+ // Helper: Duplicate a block
165
+ const duplicateBlock = useCallback((id: string) => {
166
+ dispatch({ type: 'DUPLICATE_BLOCK', payload: { id } });
167
+ }, []);
168
+
169
+ // Helper: Move a block (supports nested containers)
170
+ const moveBlock = useCallback((id: string, newIndex: number, containerId?: string) => {
171
+ dispatch({ type: 'MOVE_BLOCK', payload: { id, newIndex, containerId } });
172
+ }, []);
173
+
174
+ // Helper: Load a post
175
+ const loadPost = useCallback((post: BlogPost) => {
176
+ dispatch({ type: 'LOAD_POST', payload: post });
177
+ }, []);
178
+
179
+ // Helper: Reset editor
180
+ const resetEditor = useCallback(() => {
181
+ dispatch({ type: 'RESET_EDITOR' });
182
+ }, []);
183
+
184
+ // Helper: Save
185
+ // Uses stateRef to always get the latest state, avoiding stale closure issues
186
+ const save = useCallback(async (heroBlock?: Block | null) => {
187
+ if (onSave) {
188
+ // Use stateRef.current to get the absolute latest state
189
+ // This ensures we don't have stale closure issues with React state updates
190
+ await onSave(stateRef.current, heroBlock);
191
+ dispatch({ type: 'MARK_CLEAN' });
192
+ }
193
+ }, [onSave]);
194
+
195
+ // Helper: Undo
196
+ const undo = useCallback(() => {
197
+ if (historyIndex > 0 && history.length > 0) {
198
+ const previousState = history[historyIndex - 1];
199
+ if (previousState) {
200
+ isRestoringRef.current = true;
201
+ setHistoryIndex(prev => prev - 1);
202
+ dispatch({ type: 'LOAD_POST', payload: {
203
+ id: previousState.postId || '',
204
+ title: previousState.title,
205
+ slug: previousState.slug,
206
+ blocks: previousState.blocks,
207
+ seo: previousState.seo,
208
+ publication: {
209
+ status: previousState.status,
210
+ authorId: undefined,
211
+ },
212
+ metadata: previousState.metadata,
213
+ createdAt: new Date().toISOString(),
214
+ updatedAt: new Date().toISOString(),
215
+ } });
216
+ }
217
+ }
218
+ }, [history, historyIndex, dispatch]);
219
+
220
+ // Helper: Redo
221
+ const redo = useCallback(() => {
222
+ if (historyIndex < history.length - 1) {
223
+ const nextState = history[historyIndex + 1];
224
+ if (nextState) {
225
+ isRestoringRef.current = true;
226
+ setHistoryIndex(prev => prev + 1);
227
+ dispatch({ type: 'LOAD_POST', payload: {
228
+ id: nextState.postId || '',
229
+ title: nextState.title,
230
+ slug: nextState.slug,
231
+ blocks: nextState.blocks,
232
+ seo: nextState.seo,
233
+ publication: {
234
+ status: nextState.status,
235
+ authorId: undefined,
236
+ },
237
+ metadata: nextState.metadata,
238
+ createdAt: new Date().toISOString(),
239
+ updatedAt: new Date().toISOString(),
240
+ } });
241
+ }
242
+ }
243
+ }, [history, historyIndex, dispatch]);
244
+
245
+ // Memoize the context value
246
+ const value = useMemo<EditorContextValue>(
247
+ () => ({
248
+ state,
249
+ dispatch,
250
+ darkMode,
251
+ backgroundColors,
252
+ helpers: {
253
+ addBlock,
254
+ updateBlock,
255
+ deleteBlock,
256
+ duplicateBlock,
257
+ moveBlock,
258
+ loadPost,
259
+ resetEditor,
260
+ save,
261
+ undo,
262
+ redo,
263
+ },
264
+ canUndo: historyIndex > 0 && history.length > 0,
265
+ canRedo: historyIndex < history.length - 1,
266
+ }),
267
+ [state, dispatch, darkMode, backgroundColors, addBlock, updateBlock, deleteBlock, duplicateBlock, moveBlock, loadPost, resetEditor, save, undo, redo, historyIndex, history.length]
268
+ );
269
+
270
+ return <EditorContext.Provider value={value}>{children}</EditorContext.Provider>;
271
+ }
272
+
273
+ /**
274
+ * Hook to access editor context
275
+ * @throws Error if used outside EditorProvider
276
+ */
277
+ export function useEditor(): EditorContextValue {
278
+ const context = useContext(EditorContext);
279
+ if (!context) {
280
+ throw new Error('useEditor must be used within an EditorProvider');
281
+ }
282
+ return context;
283
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * State management exports
3
+ */
4
+ export * from './types';
5
+ export * from './reducer';
6
+ export * from './EditorContext';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * State management exports
3
+ */
4
+
5
+ export * from './types';
6
+ export * from './reducer';
7
+ export * from './EditorContext';
8
+
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Editor Reducer
3
+ * Pure function that handles state transitions
4
+ */
5
+ import { EditorState, EditorAction } from './types';
6
+ /**
7
+ * Editor Reducer
8
+ * Handles all state transitions for the editor
9
+ */
10
+ export declare function editorReducer(state: EditorState, action: EditorAction): EditorState;
11
+ //# sourceMappingURL=reducer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["reducer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAsB,MAAM,SAAS,CAAC;AAqUxE;;;GAGG;AACH,wBAAgB,aAAa,CACzB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,YAAY,GACrB,WAAW,CAmWb"}