@jhits/plugin-blog 0.0.9 → 0.0.11

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 (276) hide show
  1. package/package.json +58 -59
  2. package/src/api/categories.ts +0 -43
  3. package/src/api/check-title.ts +0 -60
  4. package/src/api/config-handler.ts +0 -76
  5. package/src/api/handler.ts +0 -418
  6. package/src/api/index.ts +0 -33
  7. package/src/api/route.ts +0 -116
  8. package/src/api/router.ts +0 -128
  9. package/src/api-server.ts +0 -11
  10. package/src/config.ts +0 -161
  11. package/src/hooks/index.d.ts +0 -8
  12. package/src/hooks/index.d.ts.map +0 -1
  13. package/src/hooks/index.js +0 -7
  14. package/src/hooks/index.ts +0 -9
  15. package/src/hooks/useBlog.d.ts +0 -31
  16. package/src/hooks/useBlog.d.ts.map +0 -1
  17. package/src/hooks/useBlog.js +0 -57
  18. package/src/hooks/useBlog.ts +0 -85
  19. package/src/hooks/useBlogs.d.ts +0 -39
  20. package/src/hooks/useBlogs.d.ts.map +0 -1
  21. package/src/hooks/useBlogs.js +0 -82
  22. package/src/hooks/useBlogs.ts +0 -123
  23. package/src/hooks/useCategories.d.ts +0 -9
  24. package/src/hooks/useCategories.d.ts.map +0 -1
  25. package/src/hooks/useCategories.js +0 -70
  26. package/src/hooks/useCategories.ts +0 -76
  27. package/src/index.d.ts +0 -55
  28. package/src/index.d.ts.map +0 -1
  29. package/src/index.js +0 -228
  30. package/src/index.server.ts +0 -14
  31. package/src/index.tsx +0 -335
  32. package/src/init.d.ts +0 -40
  33. package/src/init.d.ts.map +0 -1
  34. package/src/init.js +0 -41
  35. package/src/init.tsx +0 -63
  36. package/src/lib/blocks/BlockRenderer.d.ts +0 -54
  37. package/src/lib/blocks/BlockRenderer.d.ts.map +0 -1
  38. package/src/lib/blocks/BlockRenderer.js +0 -54
  39. package/src/lib/blocks/BlockRenderer.tsx +0 -141
  40. package/src/lib/blocks/index.ts +0 -6
  41. package/src/lib/config-storage.d.ts +0 -30
  42. package/src/lib/config-storage.d.ts.map +0 -1
  43. package/src/lib/config-storage.js +0 -31
  44. package/src/lib/config-storage.ts +0 -65
  45. package/src/lib/index.ts +0 -9
  46. package/src/lib/layouts/blocks/ColumnsBlock.d.ts +0 -25
  47. package/src/lib/layouts/blocks/ColumnsBlock.d.ts.map +0 -1
  48. package/src/lib/layouts/blocks/ColumnsBlock.js +0 -182
  49. package/src/lib/layouts/blocks/ColumnsBlock.tsx +0 -298
  50. package/src/lib/layouts/blocks/ColumnsBlock.tsx.tmp +0 -81
  51. package/src/lib/layouts/blocks/SectionBlock.d.ts +0 -25
  52. package/src/lib/layouts/blocks/SectionBlock.d.ts.map +0 -1
  53. package/src/lib/layouts/blocks/SectionBlock.js +0 -44
  54. package/src/lib/layouts/blocks/SectionBlock.tsx +0 -104
  55. package/src/lib/layouts/blocks/index.ts +0 -8
  56. package/src/lib/layouts/index.d.ts +0 -23
  57. package/src/lib/layouts/index.d.ts.map +0 -1
  58. package/src/lib/layouts/index.js +0 -45
  59. package/src/lib/layouts/index.ts +0 -52
  60. package/src/lib/layouts/registerLayoutBlocks.d.ts +0 -9
  61. package/src/lib/layouts/registerLayoutBlocks.d.ts.map +0 -1
  62. package/src/lib/layouts/registerLayoutBlocks.js +0 -60
  63. package/src/lib/layouts/registerLayoutBlocks.ts +0 -64
  64. package/src/lib/mappers/apiMapper.d.ts +0 -66
  65. package/src/lib/mappers/apiMapper.d.ts.map +0 -1
  66. package/src/lib/mappers/apiMapper.js +0 -191
  67. package/src/lib/mappers/apiMapper.ts +0 -254
  68. package/src/lib/migration/index.ts +0 -6
  69. package/src/lib/migration/mapper.ts +0 -140
  70. package/src/lib/rich-text/RichTextEditor.d.ts +0 -45
  71. package/src/lib/rich-text/RichTextEditor.d.ts.map +0 -1
  72. package/src/lib/rich-text/RichTextEditor.js +0 -564
  73. package/src/lib/rich-text/RichTextEditor.tsx +0 -826
  74. package/src/lib/rich-text/RichTextPreview.d.ts +0 -16
  75. package/src/lib/rich-text/RichTextPreview.d.ts.map +0 -1
  76. package/src/lib/rich-text/RichTextPreview.js +0 -144
  77. package/src/lib/rich-text/RichTextPreview.tsx +0 -210
  78. package/src/lib/rich-text/index.d.ts +0 -9
  79. package/src/lib/rich-text/index.d.ts.map +0 -1
  80. package/src/lib/rich-text/index.js +0 -6
  81. package/src/lib/rich-text/index.ts +0 -10
  82. package/src/lib/utils/blockHelpers.d.ts +0 -23
  83. package/src/lib/utils/blockHelpers.d.ts.map +0 -1
  84. package/src/lib/utils/blockHelpers.js +0 -65
  85. package/src/lib/utils/blockHelpers.ts +0 -72
  86. package/src/lib/utils/configValidation.d.ts +0 -23
  87. package/src/lib/utils/configValidation.d.ts.map +0 -1
  88. package/src/lib/utils/configValidation.js +0 -113
  89. package/src/lib/utils/configValidation.ts +0 -137
  90. package/src/lib/utils/index.ts +0 -8
  91. package/src/lib/utils/slugify.ts +0 -79
  92. package/src/registry/BlockRegistry.d.ts +0 -62
  93. package/src/registry/BlockRegistry.d.ts.map +0 -1
  94. package/src/registry/BlockRegistry.js +0 -112
  95. package/src/registry/BlockRegistry.ts +0 -139
  96. package/src/registry/index.d.ts +0 -6
  97. package/src/registry/index.d.ts.map +0 -1
  98. package/src/registry/index.js +0 -4
  99. package/src/registry/index.ts +0 -11
  100. package/src/state/EditorContext.d.ts +0 -45
  101. package/src/state/EditorContext.d.ts.map +0 -1
  102. package/src/state/EditorContext.js +0 -215
  103. package/src/state/EditorContext.tsx +0 -283
  104. package/src/state/index.d.ts +0 -7
  105. package/src/state/index.d.ts.map +0 -1
  106. package/src/state/index.js +0 -6
  107. package/src/state/index.ts +0 -8
  108. package/src/state/reducer.d.ts +0 -11
  109. package/src/state/reducer.d.ts.map +0 -1
  110. package/src/state/reducer.js +0 -443
  111. package/src/state/reducer.ts +0 -694
  112. package/src/state/types.d.ts +0 -162
  113. package/src/state/types.d.ts.map +0 -1
  114. package/src/state/types.js +0 -27
  115. package/src/state/types.ts +0 -160
  116. package/src/types/block.d.ts +0 -221
  117. package/src/types/block.d.ts.map +0 -1
  118. package/src/types/block.js +0 -6
  119. package/src/types/block.ts +0 -269
  120. package/src/types/index.d.ts +0 -8
  121. package/src/types/index.d.ts.map +0 -1
  122. package/src/types/index.js +0 -5
  123. package/src/types/index.ts +0 -17
  124. package/src/types/post.d.ts +0 -136
  125. package/src/types/post.d.ts.map +0 -1
  126. package/src/types/post.js +0 -5
  127. package/src/types/post.ts +0 -169
  128. package/src/utils/client.d.ts +0 -48
  129. package/src/utils/client.d.ts.map +0 -1
  130. package/src/utils/client.js +0 -77
  131. package/src/utils/client.ts +0 -122
  132. package/src/utils/index.ts +0 -7
  133. package/src/views/CanvasEditor/BlockWrapper.d.ts +0 -16
  134. package/src/views/CanvasEditor/BlockWrapper.d.ts.map +0 -1
  135. package/src/views/CanvasEditor/BlockWrapper.js +0 -276
  136. package/src/views/CanvasEditor/BlockWrapper.tsx +0 -522
  137. package/src/views/CanvasEditor/CanvasEditorView.d.ts +0 -14
  138. package/src/views/CanvasEditor/CanvasEditorView.d.ts.map +0 -1
  139. package/src/views/CanvasEditor/CanvasEditorView.js +0 -209
  140. package/src/views/CanvasEditor/CanvasEditorView.tsx +0 -337
  141. package/src/views/CanvasEditor/EditorBody.d.ts +0 -22
  142. package/src/views/CanvasEditor/EditorBody.d.ts.map +0 -1
  143. package/src/views/CanvasEditor/EditorBody.js +0 -505
  144. package/src/views/CanvasEditor/EditorBody.tsx +0 -665
  145. package/src/views/CanvasEditor/EditorHeader.d.ts +0 -18
  146. package/src/views/CanvasEditor/EditorHeader.d.ts.map +0 -1
  147. package/src/views/CanvasEditor/EditorHeader.js +0 -101
  148. package/src/views/CanvasEditor/EditorHeader.tsx +0 -268
  149. package/src/views/CanvasEditor/LayoutContainer.d.ts +0 -17
  150. package/src/views/CanvasEditor/LayoutContainer.d.ts.map +0 -1
  151. package/src/views/CanvasEditor/LayoutContainer.js +0 -222
  152. package/src/views/CanvasEditor/LayoutContainer.tsx +0 -322
  153. package/src/views/CanvasEditor/SaveConfirmationModal.d.ts +0 -13
  154. package/src/views/CanvasEditor/SaveConfirmationModal.d.ts.map +0 -1
  155. package/src/views/CanvasEditor/SaveConfirmationModal.js +0 -78
  156. package/src/views/CanvasEditor/SaveConfirmationModal.tsx +0 -233
  157. package/src/views/CanvasEditor/components/CustomBlockItem.d.ts +0 -14
  158. package/src/views/CanvasEditor/components/CustomBlockItem.d.ts.map +0 -1
  159. package/src/views/CanvasEditor/components/CustomBlockItem.js +0 -44
  160. package/src/views/CanvasEditor/components/CustomBlockItem.tsx +0 -92
  161. package/src/views/CanvasEditor/components/EditorCanvas.d.ts +0 -29
  162. package/src/views/CanvasEditor/components/EditorCanvas.d.ts.map +0 -1
  163. package/src/views/CanvasEditor/components/EditorCanvas.js +0 -32
  164. package/src/views/CanvasEditor/components/EditorCanvas.tsx +0 -160
  165. package/src/views/CanvasEditor/components/EditorLibrary.d.ts +0 -7
  166. package/src/views/CanvasEditor/components/EditorLibrary.d.ts.map +0 -1
  167. package/src/views/CanvasEditor/components/EditorLibrary.js +0 -25
  168. package/src/views/CanvasEditor/components/EditorLibrary.tsx +0 -122
  169. package/src/views/CanvasEditor/components/EditorSidebar.d.ts +0 -13
  170. package/src/views/CanvasEditor/components/EditorSidebar.d.ts.map +0 -1
  171. package/src/views/CanvasEditor/components/EditorSidebar.js +0 -20
  172. package/src/views/CanvasEditor/components/EditorSidebar.tsx +0 -181
  173. package/src/views/CanvasEditor/components/ErrorBanner.d.ts +0 -6
  174. package/src/views/CanvasEditor/components/ErrorBanner.d.ts.map +0 -1
  175. package/src/views/CanvasEditor/components/ErrorBanner.js +0 -8
  176. package/src/views/CanvasEditor/components/ErrorBanner.tsx +0 -31
  177. package/src/views/CanvasEditor/components/FeaturedMediaSection.d.ts +0 -25
  178. package/src/views/CanvasEditor/components/FeaturedMediaSection.d.ts.map +0 -1
  179. package/src/views/CanvasEditor/components/FeaturedMediaSection.js +0 -182
  180. package/src/views/CanvasEditor/components/FeaturedMediaSection.tsx +0 -341
  181. package/src/views/CanvasEditor/components/LibraryItem.d.ts +0 -14
  182. package/src/views/CanvasEditor/components/LibraryItem.d.ts.map +0 -1
  183. package/src/views/CanvasEditor/components/LibraryItem.js +0 -43
  184. package/src/views/CanvasEditor/components/LibraryItem.tsx +0 -80
  185. package/src/views/CanvasEditor/components/PrivacySettingsSection.d.ts +0 -15
  186. package/src/views/CanvasEditor/components/PrivacySettingsSection.d.ts.map +0 -1
  187. package/src/views/CanvasEditor/components/PrivacySettingsSection.js +0 -63
  188. package/src/views/CanvasEditor/components/PrivacySettingsSection.tsx +0 -212
  189. package/src/views/CanvasEditor/components/index.d.ts +0 -21
  190. package/src/views/CanvasEditor/components/index.d.ts.map +0 -1
  191. package/src/views/CanvasEditor/components/index.js +0 -12
  192. package/src/views/CanvasEditor/components/index.ts +0 -28
  193. package/src/views/CanvasEditor/hooks/index.d.ts +0 -10
  194. package/src/views/CanvasEditor/hooks/index.d.ts.map +0 -1
  195. package/src/views/CanvasEditor/hooks/index.js +0 -9
  196. package/src/views/CanvasEditor/hooks/index.ts +0 -10
  197. package/src/views/CanvasEditor/hooks/useHeroBlock.d.ts +0 -8
  198. package/src/views/CanvasEditor/hooks/useHeroBlock.d.ts.map +0 -1
  199. package/src/views/CanvasEditor/hooks/useHeroBlock.js +0 -79
  200. package/src/views/CanvasEditor/hooks/useHeroBlock.ts +0 -103
  201. package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.d.ts +0 -3
  202. package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.d.ts.map +0 -1
  203. package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.js +0 -114
  204. package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.ts +0 -142
  205. package/src/views/CanvasEditor/hooks/usePostLoader.d.ts +0 -5
  206. package/src/views/CanvasEditor/hooks/usePostLoader.d.ts.map +0 -1
  207. package/src/views/CanvasEditor/hooks/usePostLoader.js +0 -32
  208. package/src/views/CanvasEditor/hooks/usePostLoader.ts +0 -39
  209. package/src/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts +0 -2
  210. package/src/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts.map +0 -1
  211. package/src/views/CanvasEditor/hooks/useRegisteredBlocks.js +0 -47
  212. package/src/views/CanvasEditor/hooks/useRegisteredBlocks.ts +0 -55
  213. package/src/views/CanvasEditor/hooks/useUnsavedChanges.d.ts +0 -25
  214. package/src/views/CanvasEditor/hooks/useUnsavedChanges.d.ts.map +0 -1
  215. package/src/views/CanvasEditor/hooks/useUnsavedChanges.js +0 -285
  216. package/src/views/CanvasEditor/hooks/useUnsavedChanges.ts +0 -339
  217. package/src/views/CanvasEditor/index.d.ts +0 -16
  218. package/src/views/CanvasEditor/index.d.ts.map +0 -1
  219. package/src/views/CanvasEditor/index.js +0 -9
  220. package/src/views/CanvasEditor/index.ts +0 -16
  221. package/src/views/PostManager/EmptyState.d.ts +0 -10
  222. package/src/views/PostManager/EmptyState.d.ts.map +0 -1
  223. package/src/views/PostManager/EmptyState.js +0 -12
  224. package/src/views/PostManager/EmptyState.tsx +0 -42
  225. package/src/views/PostManager/PostActionsMenu.d.ts +0 -12
  226. package/src/views/PostManager/PostActionsMenu.d.ts.map +0 -1
  227. package/src/views/PostManager/PostActionsMenu.js +0 -58
  228. package/src/views/PostManager/PostActionsMenu.tsx +0 -112
  229. package/src/views/PostManager/PostCards.d.ts +0 -15
  230. package/src/views/PostManager/PostCards.d.ts.map +0 -1
  231. package/src/views/PostManager/PostCards.js +0 -79
  232. package/src/views/PostManager/PostCards.tsx +0 -197
  233. package/src/views/PostManager/PostFilters.d.ts +0 -16
  234. package/src/views/PostManager/PostFilters.d.ts.map +0 -1
  235. package/src/views/PostManager/PostFilters.js +0 -10
  236. package/src/views/PostManager/PostFilters.tsx +0 -95
  237. package/src/views/PostManager/PostManagerView.d.ts +0 -11
  238. package/src/views/PostManager/PostManagerView.d.ts.map +0 -1
  239. package/src/views/PostManager/PostManagerView.js +0 -174
  240. package/src/views/PostManager/PostManagerView.tsx +0 -289
  241. package/src/views/PostManager/PostStats.d.ts +0 -11
  242. package/src/views/PostManager/PostStats.d.ts.map +0 -1
  243. package/src/views/PostManager/PostStats.js +0 -46
  244. package/src/views/PostManager/PostStats.tsx +0 -81
  245. package/src/views/PostManager/PostTable.d.ts +0 -15
  246. package/src/views/PostManager/PostTable.d.ts.map +0 -1
  247. package/src/views/PostManager/PostTable.js +0 -79
  248. package/src/views/PostManager/PostTable.tsx +0 -230
  249. package/src/views/PostManager/index.d.ts +0 -12
  250. package/src/views/PostManager/index.d.ts.map +0 -1
  251. package/src/views/PostManager/index.js +0 -11
  252. package/src/views/PostManager/index.ts +0 -15
  253. package/src/views/Preview/PreviewBridgeView.d.ts +0 -12
  254. package/src/views/Preview/PreviewBridgeView.d.ts.map +0 -1
  255. package/src/views/Preview/PreviewBridgeView.js +0 -11
  256. package/src/views/Preview/PreviewBridgeView.tsx +0 -64
  257. package/src/views/Preview/index.d.ts +0 -6
  258. package/src/views/Preview/index.d.ts.map +0 -1
  259. package/src/views/Preview/index.js +0 -4
  260. package/src/views/Preview/index.ts +0 -7
  261. package/src/views/Settings/SettingsView.d.ts +0 -10
  262. package/src/views/Settings/SettingsView.d.ts.map +0 -1
  263. package/src/views/Settings/SettingsView.js +0 -111
  264. package/src/views/Settings/SettingsView.tsx +0 -298
  265. package/src/views/Settings/index.d.ts +0 -6
  266. package/src/views/Settings/index.d.ts.map +0 -1
  267. package/src/views/Settings/index.js +0 -4
  268. package/src/views/Settings/index.ts +0 -7
  269. package/src/views/SlugSEO/SlugSEOManagerView.d.ts +0 -12
  270. package/src/views/SlugSEO/SlugSEOManagerView.d.ts.map +0 -1
  271. package/src/views/SlugSEO/SlugSEOManagerView.js +0 -11
  272. package/src/views/SlugSEO/SlugSEOManagerView.tsx +0 -94
  273. package/src/views/SlugSEO/index.d.ts +0 -6
  274. package/src/views/SlugSEO/index.d.ts.map +0 -1
  275. package/src/views/SlugSEO/index.js +0 -4
  276. package/src/views/SlugSEO/index.ts +0 -7
@@ -1,341 +0,0 @@
1
- 'use client';
2
-
3
- import React, { useState, useCallback, useEffect } from 'react';
4
- import { createPortal } from 'react-dom';
5
- import { Image as ImageIcon, Plus, X } from 'lucide-react';
6
- import { ImagePicker, Image } from '@jhits/plugin-images';
7
- import type { ImageMetadata } from '@jhits/plugin-images';
8
- import type { Block } from '../../../types/block';
9
-
10
- export interface FeaturedImage {
11
- // Store only the semantic ID - plugin-images handles everything else
12
- id?: string;
13
- alt?: string;
14
- // Transform values (stored locally for UI, but plugin-images API is source of truth)
15
- brightness?: number;
16
- blur?: number;
17
- scale?: number;
18
- positionX?: number;
19
- positionY?: number;
20
- // Indicates if this is a custom featured image (not synced from hero)
21
- isCustom?: boolean;
22
- }
23
-
24
- export interface FeaturedMediaSectionProps {
25
- featuredImage?: FeaturedImage;
26
- heroBlock?: Block | null; // Hero block to get default image from
27
- slug?: string; // Blog post slug for semantic ID
28
- onUpdate: (image: FeaturedImage | undefined) => void;
29
- }
30
-
31
- /**
32
- * Featured Media Section Component
33
- * Handles featured image selection - completely independent from hero image
34
- * Featured image is a thumbnail used for blog post cards
35
- * Hero image is separate and managed in the hero block
36
- */
37
- export function FeaturedMediaSection({
38
- featuredImage,
39
- heroBlock,
40
- slug,
41
- onUpdate,
42
- }: FeaturedMediaSectionProps) {
43
- const [showImagePicker, setShowImagePicker] = useState(false);
44
- const [openEditorDirectly, setOpenEditorDirectly] = useState(false);
45
- const [mounted, setMounted] = useState(false);
46
-
47
- // Handle SSR - ensure we only render portal on client
48
- useEffect(() => {
49
- setMounted(true);
50
- }, []);
51
-
52
- // Create semantic ID for this featured image - plugin-images will handle everything
53
- const semanticId = slug ? `blog-featured-${slug}` : `blog-featured-${Date.now()}`;
54
-
55
- // Use semantic ID from featuredImage if it exists, otherwise use generated one
56
- // IMPORTANT: Always use the actual id from featuredImage if available, otherwise the semanticId
57
- // This ensures the id is stable and doesn't change on re-renders
58
- const imageId = featuredImage?.id || semanticId;
59
-
60
- // Ensure featuredImage always has an id when it exists
61
- // This prevents the "missing featured image" issue on save
62
- useEffect(() => {
63
- if (featuredImage && !featuredImage.id) {
64
- // If featuredImage exists but has no id, set it to the semanticId
65
- onUpdate({
66
- ...featuredImage,
67
- id: semanticId,
68
- });
69
- }
70
- }, [featuredImage, semanticId, onUpdate]);
71
-
72
- // Get transform values from featuredImage or use defaults
73
- const brightness = featuredImage?.brightness ?? 100;
74
- const blur = featuredImage?.blur ?? 0;
75
- const scale = featuredImage?.scale ?? 1.0;
76
- const positionX = featuredImage?.positionX ?? 0;
77
- const positionY = featuredImage?.positionY ?? 0;
78
-
79
- // Handle image selection - create initial mapping and update blog metadata with semantic ID
80
- // Plugin-images Image component will automatically resolve the semantic ID when it renders
81
- const handleImageChange = useCallback(async (image: ImageMetadata | null) => {
82
- if (image) {
83
- // Extract filename from image URL for reference
84
- const isUploadedImage = image.url.startsWith('/api/uploads/');
85
- let filename = image.filename;
86
-
87
- if (!filename && isUploadedImage) {
88
- // Extract filename from URL if not provided
89
- filename = image.url.split('/api/uploads/')[1]?.split('?')[0] || image.id;
90
- } else if (!filename) {
91
- // For external URLs, use the image ID or extract from URL
92
- filename = image.id || image.url.split('/').pop()?.split('?')[0] || `external-${Date.now()}`;
93
- }
94
-
95
- // Create initial mapping in plugin-images API immediately
96
- // This ensures the semantic ID resolves correctly
97
- try {
98
- const saveData = {
99
- id: imageId,
100
- filename: filename,
101
- scale: 1.0,
102
- positionX: 0,
103
- positionY: 0,
104
- brightness: 100,
105
- blur: 0,
106
- };
107
-
108
- await fetch('/api/plugin-images/resolve', {
109
- method: 'POST',
110
- headers: { 'Content-Type': 'application/json' },
111
- body: JSON.stringify(saveData),
112
- });
113
- } catch (error) {
114
- console.error('[FeaturedMediaSection] Failed to create initial mapping:', error);
115
- // Continue anyway - the mapping might be created later
116
- }
117
-
118
- // Update blog metadata with semantic ID
119
- onUpdate({
120
- id: imageId,
121
- alt: image.alt || image.filename,
122
- brightness: 100,
123
- blur: 0,
124
- scale: 1.0,
125
- positionX: 0,
126
- positionY: 0,
127
- isCustom: true,
128
- } as FeaturedImage);
129
- } else {
130
- // If removed, set to undefined
131
- onUpdate(undefined);
132
- }
133
- setShowImagePicker(false);
134
- }, [imageId, onUpdate]);
135
-
136
- // Handle editor save from ImagePicker - save to plugin-images API
137
- const handleEditorSave = useCallback(async (
138
- finalScale: number,
139
- finalPositionX: number,
140
- finalPositionY: number,
141
- finalBrightness?: number,
142
- finalBlur?: number
143
- ) => {
144
- if (!featuredImage?.id) return;
145
-
146
- // Reset the auto-open flag immediately to prevent reopening
147
- setOpenEditorDirectly(false);
148
-
149
- // Get the actual filename from the API (resolve the semantic ID)
150
- let filename = imageId; // Fallback to semantic ID
151
- try {
152
- const response = await fetch(`/api/plugin-images/resolve?id=${encodeURIComponent(imageId)}`);
153
- if (response.ok) {
154
- const data = await response.json();
155
- filename = data.filename || imageId;
156
- }
157
- } catch (error) {
158
- console.error('Failed to resolve filename:', error);
159
- }
160
-
161
- // Normalize position values
162
- const normalizedPositionX = finalPositionX === -50 ? 0 : finalPositionX;
163
- const normalizedPositionY = finalPositionY === -50 ? 0 : finalPositionY;
164
- const finalBrightnessValue = finalBrightness ?? brightness;
165
- const finalBlurValue = finalBlur ?? blur;
166
-
167
- // Save to plugin-images API
168
- try {
169
- const saveData = {
170
- id: imageId,
171
- filename: filename,
172
- scale: finalScale,
173
- positionX: normalizedPositionX,
174
- positionY: normalizedPositionY,
175
- brightness: finalBrightnessValue,
176
- blur: finalBlurValue,
177
- };
178
-
179
- const response = await fetch('/api/plugin-images/resolve', {
180
- method: 'POST',
181
- headers: { 'Content-Type': 'application/json' },
182
- body: JSON.stringify(saveData),
183
- });
184
-
185
- if (response.ok) {
186
- // Update local featured image data - ensure id is preserved
187
- onUpdate({
188
- ...featuredImage,
189
- id: featuredImage.id || imageId, // Ensure id is always preserved
190
- scale: finalScale,
191
- positionX: normalizedPositionX,
192
- positionY: normalizedPositionY,
193
- brightness: finalBrightnessValue,
194
- blur: finalBlurValue,
195
- });
196
-
197
- // Dispatch event to notify Image components
198
- window.dispatchEvent(new CustomEvent('image-mapping-updated', {
199
- detail: saveData
200
- }));
201
- }
202
- } catch (error) {
203
- console.error('Failed to save image transform:', error);
204
- }
205
- }, [imageId, featuredImage, brightness, blur, onUpdate]);
206
-
207
- return (
208
- <section>
209
- <div className="flex items-center gap-3 mb-6">
210
- <ImageIcon size={14} className="text-neutral-500 dark:text-neutral-400" />
211
- <label className="text-[10px] uppercase tracking-[0.2em] text-neutral-500 dark:text-neutral-400 font-black">
212
- Featured Media
213
- </label>
214
- </div>
215
- {featuredImage?.id ? (
216
- <div className="relative group">
217
- {/* Use Image component from plugin-images - it handles everything automatically */}
218
- {/* Blog component only handles the design/styling */}
219
- <div className="relative aspect-[16/10] bg-dashboard-bg rounded-3xl overflow-hidden border border-dashboard-border group/image">
220
- <Image
221
- id={imageId}
222
- alt={featuredImage?.alt || 'Featured image'}
223
- fill
224
- className="object-cover w-full h-full"
225
- editable={false} // Disable Image component's overlay - we'll use our own
226
- {...({
227
- brightness,
228
- blur,
229
- scale,
230
- positionX,
231
- positionY,
232
- } as any)}
233
- />
234
- {/* Custom edit overlay that opens ImagePicker editor */}
235
- <button
236
- onClick={() => {
237
- setOpenEditorDirectly(true);
238
- setShowImagePicker(true);
239
- }}
240
- className="absolute inset-0 z-30 flex items-center justify-center opacity-0 group-hover/image:opacity-100 transition-all duration-300 bg-neutral-900/40 dark:bg-neutral-900/60 backdrop-blur-[2px]"
241
- >
242
- <div className="flex items-center gap-2 px-4 py-2 bg-white dark:bg-neutral-800 rounded-full shadow-xl">
243
- <ImageIcon size={14} className="text-primary" />
244
- <span className="text-[10px] font-bold uppercase tracking-widest">Edit</span>
245
- </div>
246
- </button>
247
- </div>
248
- <div className="mt-2 flex items-center gap-3">
249
- <button
250
- onClick={() => setShowImagePicker(true)}
251
- className="text-[10px] text-neutral-600 dark:text-neutral-400 hover:text-primary font-bold uppercase tracking-wider"
252
- >
253
- Change Image
254
- </button>
255
- <span className="text-[10px] text-neutral-400">•</span>
256
- <button
257
- onClick={() => onUpdate(undefined)}
258
- className="text-[10px] text-red-500 dark:text-red-400 hover:text-red-700 dark:hover:text-red-300 font-bold uppercase tracking-wider"
259
- >
260
- Remove Image
261
- </button>
262
- </div>
263
- </div>
264
- ) : (
265
- <div
266
- className="group relative aspect-[16/10] bg-dashboard-bg rounded-3xl border-2 border-dashed border-dashboard-border flex flex-col items-center justify-center text-neutral-400 dark:text-neutral-500 hover:bg-dashboard-card hover:border-primary cursor-pointer transition-all duration-300"
267
- onClick={() => setShowImagePicker(true)}
268
- >
269
- <Plus size={24} strokeWidth={1} className="mb-3 group-hover:scale-110 transition-transform" />
270
- <span className="text-[9px] font-black uppercase tracking-widest">Assign Image</span>
271
- </div>
272
- )}
273
-
274
- {/* Image Picker Modal */}
275
- {showImagePicker && mounted && createPortal(
276
- <div className="fixed inset-0 z-[100] flex items-center justify-center bg-black/50 backdrop-blur-sm" onClick={() => {
277
- setShowImagePicker(false);
278
- setOpenEditorDirectly(false); // Reset flag when closing
279
- }}>
280
- <div className="bg-white dark:bg-neutral-900 rounded-2xl w-full max-w-2xl mx-4 p-6 shadow-2xl max-h-[90vh] overflow-y-auto" onClick={(e) => e.stopPropagation()}>
281
- <div className="flex items-center justify-between mb-6">
282
- <h3 className="text-lg font-bold text-neutral-900 dark:text-neutral-100">
283
- {openEditorDirectly ? 'Edit Featured Image' : 'Select Featured Image'}
284
- </h3>
285
- <button
286
- onClick={() => {
287
- setShowImagePicker(false);
288
- setOpenEditorDirectly(false); // Reset flag when closing
289
- }}
290
- className="p-2 hover:bg-dashboard-bg dark:hover:bg-neutral-800 rounded-lg transition-colors text-neutral-700 dark:text-neutral-300 hover:text-neutral-900 dark:hover:text-white"
291
- aria-label="Close"
292
- >
293
- <X size={20} className="transition-colors" />
294
- </button>
295
- </div>
296
- <ImagePicker
297
- value={featuredImage?.id ? imageId : undefined}
298
- onChange={handleImageChange}
299
- brightness={brightness}
300
- blur={blur}
301
- {...({
302
- scale,
303
- positionX,
304
- positionY,
305
- } as any)}
306
- onBrightnessChange={(val) => {
307
- // Update local state only - don't trigger save
308
- if (featuredImage) {
309
- onUpdate({
310
- ...featuredImage,
311
- id: featuredImage.id || imageId, // Ensure id is preserved
312
- brightness: val,
313
- });
314
- }
315
- }}
316
- onBlurChange={(val) => {
317
- // Update local state only - don't trigger save
318
- if (featuredImage) {
319
- onUpdate({
320
- ...featuredImage,
321
- id: featuredImage.id || imageId, // Ensure id is preserved
322
- blur: val,
323
- });
324
- }
325
- }}
326
- onEditorSave={handleEditorSave}
327
- darkMode={false}
328
- showEffects={true} // Enable effects so editor can be used
329
- aspectRatio="16/10" // Thumbnail aspect ratio for blog cards
330
- borderRadius="rounded-3xl"
331
- objectFit="cover" // Cover for thumbnails
332
- objectPosition="center"
333
- />
334
- </div>
335
- </div>,
336
- document.body
337
- )}
338
- </section>
339
- );
340
- }
341
-
@@ -1,14 +0,0 @@
1
- import React from 'react';
2
- export interface LibraryItemProps {
3
- icon: React.ReactNode;
4
- label: string;
5
- blockType: string;
6
- description?: string;
7
- onAddBlock?: (blockType: string) => void;
8
- }
9
- /**
10
- * Reusable Library Item Component
11
- * Makes blocks draggable from the library and clickable to add at bottom
12
- */
13
- export declare function LibraryItem({ icon, label, blockType, description, onAddBlock }: LibraryItemProps): import("react/jsx-runtime").JSX.Element;
14
- //# sourceMappingURL=LibraryItem.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"LibraryItem.d.ts","sourceRoot":"","sources":["LibraryItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA2B,MAAM,OAAO,CAAC;AAEhD,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EACxB,IAAI,EACJ,KAAK,EACL,SAAS,EACT,WAAW,EACX,UAAU,EACb,EAAE,gBAAgB,2CAwDlB"}
@@ -1,43 +0,0 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import React, { useState, useRef } from 'react';
4
- /**
5
- * Reusable Library Item Component
6
- * Makes blocks draggable from the library and clickable to add at bottom
7
- */
8
- export function LibraryItem({ icon, label, blockType, description, onAddBlock }) {
9
- const [hasDragged, setHasDragged] = useState(false);
10
- const mouseDownRef = useRef(null);
11
- const handleDragStart = (e) => {
12
- e.dataTransfer.setData('block-type', blockType);
13
- e.dataTransfer.effectAllowed = 'move';
14
- setHasDragged(true); // Mark as dragged when drag starts
15
- };
16
- const handleMouseDown = (e) => {
17
- // Track mouse position on mousedown
18
- mouseDownRef.current = { x: e.clientX, y: e.clientY };
19
- setHasDragged(false); // Reset drag state
20
- };
21
- const handleMouseMove = (e) => {
22
- // If mouse moved more than 5px, consider it a drag
23
- if (mouseDownRef.current) {
24
- const dx = Math.abs(e.clientX - mouseDownRef.current.x);
25
- const dy = Math.abs(e.clientY - mouseDownRef.current.y);
26
- if (dx > 5 || dy > 5) {
27
- setHasDragged(true);
28
- }
29
- }
30
- };
31
- const handleClick = (e) => {
32
- // Only add block if we didn't drag
33
- if (!hasDragged && onAddBlock) {
34
- e.preventDefault();
35
- e.stopPropagation();
36
- onAddBlock(blockType);
37
- }
38
- // Reset state
39
- mouseDownRef.current = null;
40
- setTimeout(() => setHasDragged(false), 100);
41
- };
42
- return (_jsxs("div", { draggable: true, onDragStart: handleDragStart, onMouseDown: handleMouseDown, onMouseMove: handleMouseMove, onClick: handleClick, className: "flex flex-col items-center justify-center p-5 rounded-2xl border border-dashboard-border bg-dashboard-card hover:border-primary hover:shadow-xl hover:shadow-primary/5 transition-all cursor-pointer group", children: [_jsx("div", { className: "text-neutral-400 dark:text-neutral-500 group-hover:text-primary dark:group-hover:text-primary mb-3 transition-colors duration-300", children: React.cloneElement(icon, { strokeWidth: 1.5 }) }), _jsx("span", { className: "text-[9px] font-black uppercase tracking-[0.15em] text-neutral-600 dark:text-neutral-400 group-hover:text-neutral-950 dark:group-hover:text-white transition-colors", children: label })] }));
43
- }
@@ -1,80 +0,0 @@
1
- 'use client';
2
-
3
- import React, { useState, useRef } from 'react';
4
-
5
- export interface LibraryItemProps {
6
- icon: React.ReactNode;
7
- label: string;
8
- blockType: string;
9
- description?: string;
10
- onAddBlock?: (blockType: string) => void;
11
- }
12
-
13
- /**
14
- * Reusable Library Item Component
15
- * Makes blocks draggable from the library and clickable to add at bottom
16
- */
17
- export function LibraryItem({
18
- icon,
19
- label,
20
- blockType,
21
- description,
22
- onAddBlock
23
- }: LibraryItemProps) {
24
- const [hasDragged, setHasDragged] = useState(false);
25
- const mouseDownRef = useRef<{ x: number; y: number } | null>(null);
26
-
27
- const handleDragStart = (e: React.DragEvent) => {
28
- e.dataTransfer.setData('block-type', blockType);
29
- e.dataTransfer.effectAllowed = 'move';
30
- setHasDragged(true); // Mark as dragged when drag starts
31
- };
32
-
33
- const handleMouseDown = (e: React.MouseEvent) => {
34
- // Track mouse position on mousedown
35
- mouseDownRef.current = { x: e.clientX, y: e.clientY };
36
- setHasDragged(false); // Reset drag state
37
- };
38
-
39
- const handleMouseMove = (e: React.MouseEvent) => {
40
- // If mouse moved more than 5px, consider it a drag
41
- if (mouseDownRef.current) {
42
- const dx = Math.abs(e.clientX - mouseDownRef.current.x);
43
- const dy = Math.abs(e.clientY - mouseDownRef.current.y);
44
- if (dx > 5 || dy > 5) {
45
- setHasDragged(true);
46
- }
47
- }
48
- };
49
-
50
- const handleClick = (e: React.MouseEvent) => {
51
- // Only add block if we didn't drag
52
- if (!hasDragged && onAddBlock) {
53
- e.preventDefault();
54
- e.stopPropagation();
55
- onAddBlock(blockType);
56
- }
57
- // Reset state
58
- mouseDownRef.current = null;
59
- setTimeout(() => setHasDragged(false), 100);
60
- };
61
-
62
- return (
63
- <div
64
- draggable
65
- onDragStart={handleDragStart}
66
- onMouseDown={handleMouseDown}
67
- onMouseMove={handleMouseMove}
68
- onClick={handleClick}
69
- className="flex flex-col items-center justify-center p-5 rounded-2xl border border-dashboard-border bg-dashboard-card hover:border-primary hover:shadow-xl hover:shadow-primary/5 transition-all cursor-pointer group"
70
- >
71
- <div className="text-neutral-400 dark:text-neutral-500 group-hover:text-primary dark:group-hover:text-primary mb-3 transition-colors duration-300">
72
- {React.cloneElement(icon as React.ReactElement, { strokeWidth: 1.5 } as any)}
73
- </div>
74
- <span className="text-[9px] font-black uppercase tracking-[0.15em] text-neutral-600 dark:text-neutral-400 group-hover:text-neutral-950 dark:group-hover:text-white transition-colors">
75
- {label}
76
- </span>
77
- </div>
78
- );
79
- }
80
-
@@ -1,15 +0,0 @@
1
- export interface PrivacySettings {
2
- isPrivate?: boolean;
3
- password?: string;
4
- sharedWithUsers?: string[];
5
- }
6
- export interface PrivacySettingsSectionProps {
7
- privacy?: PrivacySettings;
8
- onUpdate: (privacy: PrivacySettings) => void;
9
- }
10
- /**
11
- * Privacy Settings Section Component
12
- * Handles privacy settings: private, password-protected, share with users
13
- */
14
- export declare function PrivacySettingsSection({ privacy, onUpdate, }: PrivacySettingsSectionProps): import("react/jsx-runtime").JSX.Element;
15
- //# sourceMappingURL=PrivacySettingsSection.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PrivacySettingsSection.d.ts","sourceRoot":"","sources":["PrivacySettingsSection.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,2BAA2B;IACxC,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,QAAQ,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;CAChD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,EACnC,OAAO,EACP,QAAQ,GACX,EAAE,2BAA2B,2CA2L7B"}
@@ -1,63 +0,0 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useEffect } from 'react';
4
- import { Shield, Key, Users } from 'lucide-react';
5
- /**
6
- * Privacy Settings Section Component
7
- * Handles privacy settings: private, password-protected, share with users
8
- */
9
- export function PrivacySettingsSection({ privacy, onUpdate, }) {
10
- var _a;
11
- const [users, setUsers] = useState([]);
12
- const [loadingUsers, setLoadingUsers] = useState(false);
13
- const [showPasswordInput, setShowPasswordInput] = useState(false);
14
- const [passwordValue, setPasswordValue] = useState((privacy === null || privacy === void 0 ? void 0 : privacy.password) || '');
15
- const [showUserSelector, setShowUserSelector] = useState(false);
16
- // Fetch users from plugin-users
17
- useEffect(() => {
18
- const fetchUsers = async () => {
19
- try {
20
- setLoadingUsers(true);
21
- const res = await fetch('/api/users');
22
- const data = await res.json();
23
- if (Array.isArray(data)) {
24
- setUsers(data);
25
- }
26
- }
27
- catch (err) {
28
- console.error('Failed to load users', err);
29
- }
30
- finally {
31
- setLoadingUsers(false);
32
- }
33
- };
34
- fetchUsers();
35
- }, []);
36
- const handlePrivacyToggle = (isPrivate) => {
37
- onUpdate(Object.assign(Object.assign(Object.assign({}, privacy), { isPrivate }), (isPrivate ? {} : { password: undefined, sharedWithUsers: undefined })));
38
- };
39
- const handlePasswordChange = (password) => {
40
- setPasswordValue(password);
41
- onUpdate(Object.assign(Object.assign({}, privacy), { password: password || undefined }));
42
- };
43
- const handleUserToggle = (userId) => {
44
- const currentUsers = (privacy === null || privacy === void 0 ? void 0 : privacy.sharedWithUsers) || [];
45
- const newUsers = currentUsers.includes(userId)
46
- ? currentUsers.filter(id => id !== userId)
47
- : [...currentUsers, userId];
48
- onUpdate(Object.assign(Object.assign({}, privacy), { sharedWithUsers: newUsers.length > 0 ? newUsers : undefined }));
49
- };
50
- return (_jsxs("section", { className: "pt-8 border-t border-neutral-200 dark:border-neutral-800", children: [_jsxs("div", { className: "flex items-center gap-3 mb-6", children: [_jsx(Shield, { size: 14, className: "text-neutral-500 dark:text-neutral-400" }), _jsx("label", { className: "text-[10px] uppercase tracking-[0.2em] text-neutral-500 dark:text-neutral-400 font-black", children: "Privacy Settings" })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("label", { className: "text-[10px] text-neutral-700 dark:text-neutral-300 font-bold block mb-1", children: "Make Private" }), _jsx("p", { className: "text-[9px] text-neutral-500 dark:text-neutral-400", children: "Hide from public view" })] }), _jsx("button", { onClick: () => handlePrivacyToggle(!(privacy === null || privacy === void 0 ? void 0 : privacy.isPrivate)), className: `relative w-12 h-6 rounded-full transition-colors ${(privacy === null || privacy === void 0 ? void 0 : privacy.isPrivate) ? 'bg-primary' : 'bg-neutral-300 dark:bg-neutral-700'}`, children: _jsx("div", { className: `absolute top-1 left-1 w-4 h-4 bg-white rounded-full transition-transform ${(privacy === null || privacy === void 0 ? void 0 : privacy.isPrivate) ? 'translate-x-6' : 'translate-x-0'}` }) })] }), (privacy === null || privacy === void 0 ? void 0 : privacy.isPrivate) && (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("label", { className: "text-[10px] text-neutral-700 dark:text-neutral-300 font-bold block mb-1", children: "Password Protection" }), _jsx("p", { className: "text-[9px] text-neutral-500 dark:text-neutral-400", children: "Require password to view" })] }), _jsx("button", { onClick: () => {
51
- setShowPasswordInput(!showPasswordInput);
52
- if (!showPasswordInput && !privacy.password) {
53
- setPasswordValue('');
54
- }
55
- }, className: `p-1.5 rounded-lg transition-colors ${privacy.password || showPasswordInput
56
- ? 'bg-primary/10 text-primary'
57
- : 'bg-neutral-100 dark:bg-neutral-800 text-neutral-400'}`, children: _jsx(Key, { size: 14 }) })] }), (showPasswordInput || privacy.password) && (_jsx("input", { type: "password", value: passwordValue, onChange: (e) => handlePasswordChange(e.target.value), placeholder: "Enter password", className: "w-full px-3 py-2 text-xs bg-white dark:bg-neutral-900/50 border border-neutral-300 dark:border-neutral-700 rounded-lg outline-none focus:border-primary transition-all dark:text-neutral-100" }))] })), (privacy === null || privacy === void 0 ? void 0 : privacy.isPrivate) && (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("label", { className: "text-[10px] text-neutral-700 dark:text-neutral-300 font-bold block mb-1", children: "Share with Users" }), _jsx("p", { className: "text-[9px] text-neutral-500 dark:text-neutral-400", children: "Grant access to specific users" })] }), _jsx("button", { onClick: () => setShowUserSelector(!showUserSelector), className: `p-1.5 rounded-lg transition-colors ${(((_a = privacy.sharedWithUsers) === null || _a === void 0 ? void 0 : _a.length) || 0) > 0 || showUserSelector
58
- ? 'bg-primary/10 text-primary'
59
- : 'bg-neutral-100 dark:bg-neutral-800 text-neutral-400'}`, children: _jsx(Users, { size: 14 }) })] }), showUserSelector && (_jsx("div", { className: "bg-dashboard-bg rounded-lg p-3 border border-dashboard-border max-h-48 overflow-y-auto", children: loadingUsers ? (_jsx("p", { className: "text-[10px] text-neutral-500 dark:text-neutral-400", children: "Loading users..." })) : users.length === 0 ? (_jsx("p", { className: "text-[10px] text-neutral-500 dark:text-neutral-400", children: "No users found" })) : (_jsx("div", { className: "space-y-2", children: users.map((user) => {
60
- var _a;
61
- return (_jsxs("label", { className: "flex items-center gap-2 cursor-pointer hover:bg-dashboard-bg p-2 rounded transition-colors", children: [_jsx("input", { type: "checkbox", checked: ((_a = privacy.sharedWithUsers) === null || _a === void 0 ? void 0 : _a.includes(user._id)) || false, onChange: () => handleUserToggle(user._id), className: "rounded border-neutral-300 dark:border-neutral-700 text-primary focus:ring-primary" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("p", { className: "text-[10px] font-bold text-neutral-700 dark:text-neutral-300 truncate", children: user.name }), _jsx("p", { className: "text-[9px] text-neutral-500 dark:text-neutral-400 truncate", children: user.email })] })] }, user._id));
62
- }) })) })), privacy.sharedWithUsers && privacy.sharedWithUsers.length > 0 && (_jsxs("p", { className: "text-[9px] text-neutral-500 dark:text-neutral-400", children: ["Shared with ", privacy.sharedWithUsers.length, " user", privacy.sharedWithUsers.length !== 1 ? 's' : ''] }))] }))] })] }));
63
- }