doclific 0.2.0 → 0.2.2

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 (220) hide show
  1. package/LICENSE +17 -0
  2. package/dist/bin/doclific.js +36 -8
  3. package/package.json +11 -4
  4. package/readme.md +77 -0
  5. package/.gitattributes +0 -2
  6. package/.prettierignore +0 -5
  7. package/.prettierrc +0 -9
  8. package/.vscode/settings.json +0 -13
  9. package/frontend/components.json +0 -24
  10. package/frontend/eslint.config.js +0 -23
  11. package/frontend/index.html +0 -25
  12. package/frontend/package-lock.json +0 -15754
  13. package/frontend/public/logo.svg +0 -1
  14. package/frontend/src/App.tsx +0 -21
  15. package/frontend/src/components/app-sidebar.tsx +0 -393
  16. package/frontend/src/components/editor/editor-base-kit.tsx +0 -43
  17. package/frontend/src/components/editor/editor-kit.tsx +0 -93
  18. package/frontend/src/components/editor/plugins/align-base-kit.tsx +0 -16
  19. package/frontend/src/components/editor/plugins/align-kit.tsx +0 -18
  20. package/frontend/src/components/editor/plugins/autoformat-kit.tsx +0 -236
  21. package/frontend/src/components/editor/plugins/basic-blocks-base-kit.tsx +0 -35
  22. package/frontend/src/components/editor/plugins/basic-blocks-kit.tsx +0 -88
  23. package/frontend/src/components/editor/plugins/basic-marks-base-kit.tsx +0 -27
  24. package/frontend/src/components/editor/plugins/basic-marks-kit.tsx +0 -41
  25. package/frontend/src/components/editor/plugins/basic-nodes-kit.tsx +0 -6
  26. package/frontend/src/components/editor/plugins/block-menu-kit.tsx +0 -14
  27. package/frontend/src/components/editor/plugins/block-placeholder-kit.tsx +0 -17
  28. package/frontend/src/components/editor/plugins/block-selection-kit.tsx +0 -32
  29. package/frontend/src/components/editor/plugins/callout-base-kit.tsx +0 -7
  30. package/frontend/src/components/editor/plugins/callout-kit.tsx +0 -7
  31. package/frontend/src/components/editor/plugins/code-block-base-kit.tsx +0 -23
  32. package/frontend/src/components/editor/plugins/code-block-kit.tsx +0 -26
  33. package/frontend/src/components/editor/plugins/codebase-kit.tsx +0 -23
  34. package/frontend/src/components/editor/plugins/column-base-kit.tsx +0 -11
  35. package/frontend/src/components/editor/plugins/column-kit.tsx +0 -10
  36. package/frontend/src/components/editor/plugins/comment-base-kit.tsx +0 -7
  37. package/frontend/src/components/editor/plugins/comment-kit.tsx +0 -97
  38. package/frontend/src/components/editor/plugins/cursor-overlay-kit.tsx +0 -13
  39. package/frontend/src/components/editor/plugins/date-base-kit.tsx +0 -5
  40. package/frontend/src/components/editor/plugins/date-kit.tsx +0 -7
  41. package/frontend/src/components/editor/plugins/discussion-kit.tsx +0 -148
  42. package/frontend/src/components/editor/plugins/dnd-kit.tsx +0 -28
  43. package/frontend/src/components/editor/plugins/docx-kit.tsx +0 -6
  44. package/frontend/src/components/editor/plugins/emoji-kit.tsx +0 -13
  45. package/frontend/src/components/editor/plugins/excalidraw-kit.tsx +0 -9
  46. package/frontend/src/components/editor/plugins/exit-break-kit.tsx +0 -12
  47. package/frontend/src/components/editor/plugins/floating-toolbar-kit.tsx +0 -19
  48. package/frontend/src/components/editor/plugins/font-base-kit.tsx +0 -20
  49. package/frontend/src/components/editor/plugins/font-kit.tsx +0 -29
  50. package/frontend/src/components/editor/plugins/indent-base-kit.tsx +0 -19
  51. package/frontend/src/components/editor/plugins/indent-kit.tsx +0 -22
  52. package/frontend/src/components/editor/plugins/line-height-base-kit.tsx +0 -14
  53. package/frontend/src/components/editor/plugins/line-height-kit.tsx +0 -16
  54. package/frontend/src/components/editor/plugins/link-base-kit.tsx +0 -5
  55. package/frontend/src/components/editor/plugins/link-kit.tsx +0 -15
  56. package/frontend/src/components/editor/plugins/list-base-kit.tsx +0 -23
  57. package/frontend/src/components/editor/plugins/list-kit.tsx +0 -26
  58. package/frontend/src/components/editor/plugins/markdown-kit.tsx +0 -46
  59. package/frontend/src/components/editor/plugins/math-base-kit.tsx +0 -11
  60. package/frontend/src/components/editor/plugins/math-kit.tsx +0 -13
  61. package/frontend/src/components/editor/plugins/media-base-kit.tsx +0 -31
  62. package/frontend/src/components/editor/plugins/media-kit.tsx +0 -43
  63. package/frontend/src/components/editor/plugins/mention-base-kit.tsx +0 -7
  64. package/frontend/src/components/editor/plugins/mention-kit.tsx +0 -15
  65. package/frontend/src/components/editor/plugins/slash-kit.tsx +0 -18
  66. package/frontend/src/components/editor/plugins/suggestion-base-kit.tsx +0 -7
  67. package/frontend/src/components/editor/plugins/suggestion-kit.tsx +0 -90
  68. package/frontend/src/components/editor/plugins/table-base-kit.tsx +0 -20
  69. package/frontend/src/components/editor/plugins/table-kit.tsx +0 -22
  70. package/frontend/src/components/editor/plugins/toc-base-kit.tsx +0 -5
  71. package/frontend/src/components/editor/plugins/toc-kit.tsx +0 -14
  72. package/frontend/src/components/editor/plugins/toggle-base-kit.tsx +0 -7
  73. package/frontend/src/components/editor/plugins/toggle-kit.tsx +0 -11
  74. package/frontend/src/components/editor/transforms.ts +0 -194
  75. package/frontend/src/components/markdown-to-slate-demo.tsx +0 -50
  76. package/frontend/src/components/mode-toggle.tsx +0 -15
  77. package/frontend/src/components/theme-provider.tsx +0 -73
  78. package/frontend/src/components/ui/alert-dialog.tsx +0 -155
  79. package/frontend/src/components/ui/align-toolbar-button.tsx +0 -84
  80. package/frontend/src/components/ui/avatar.tsx +0 -51
  81. package/frontend/src/components/ui/block-context-menu.tsx +0 -199
  82. package/frontend/src/components/ui/block-discussion.tsx +0 -365
  83. package/frontend/src/components/ui/block-draggable.tsx +0 -512
  84. package/frontend/src/components/ui/block-list-static.tsx +0 -80
  85. package/frontend/src/components/ui/block-list.tsx +0 -87
  86. package/frontend/src/components/ui/block-selection.tsx +0 -42
  87. package/frontend/src/components/ui/block-suggestion.tsx +0 -473
  88. package/frontend/src/components/ui/blockquote-node-static.tsx +0 -11
  89. package/frontend/src/components/ui/blockquote-node.tsx +0 -13
  90. package/frontend/src/components/ui/button.tsx +0 -62
  91. package/frontend/src/components/ui/calendar.tsx +0 -218
  92. package/frontend/src/components/ui/callout-node-static.tsx +0 -36
  93. package/frontend/src/components/ui/callout-node.tsx +0 -63
  94. package/frontend/src/components/ui/caption.tsx +0 -63
  95. package/frontend/src/components/ui/checkbox.tsx +0 -30
  96. package/frontend/src/components/ui/code-block-node-static.tsx +0 -35
  97. package/frontend/src/components/ui/code-block-node.tsx +0 -287
  98. package/frontend/src/components/ui/code-node-static.tsx +0 -15
  99. package/frontend/src/components/ui/code-node.tsx +0 -17
  100. package/frontend/src/components/ui/codebase-snippet-node.tsx +0 -237
  101. package/frontend/src/components/ui/column-node-static.tsx +0 -29
  102. package/frontend/src/components/ui/column-node.tsx +0 -317
  103. package/frontend/src/components/ui/command.tsx +0 -182
  104. package/frontend/src/components/ui/comment-node-static.tsx +0 -15
  105. package/frontend/src/components/ui/comment-node.tsx +0 -45
  106. package/frontend/src/components/ui/comment-toolbar-button.tsx +0 -24
  107. package/frontend/src/components/ui/comment.tsx +0 -618
  108. package/frontend/src/components/ui/context-menu.tsx +0 -250
  109. package/frontend/src/components/ui/cursor-overlay.tsx +0 -66
  110. package/frontend/src/components/ui/date-node-static.tsx +0 -45
  111. package/frontend/src/components/ui/date-node.tsx +0 -93
  112. package/frontend/src/components/ui/dialog.tsx +0 -143
  113. package/frontend/src/components/ui/dropdown-menu.tsx +0 -255
  114. package/frontend/src/components/ui/dynamic-icon.tsx +0 -12
  115. package/frontend/src/components/ui/editor-static.tsx +0 -53
  116. package/frontend/src/components/ui/editor.tsx +0 -130
  117. package/frontend/src/components/ui/emoji-node.tsx +0 -69
  118. package/frontend/src/components/ui/emoji-toolbar-button.tsx +0 -628
  119. package/frontend/src/components/ui/equation-node-static.tsx +0 -98
  120. package/frontend/src/components/ui/equation-node.tsx +0 -235
  121. package/frontend/src/components/ui/equation-toolbar-button.tsx +0 -25
  122. package/frontend/src/components/ui/excalidraw-node.tsx +0 -36
  123. package/frontend/src/components/ui/export-toolbar-button.tsx +0 -174
  124. package/frontend/src/components/ui/file-selector.tsx +0 -339
  125. package/frontend/src/components/ui/floating-toolbar-buttons.tsx +0 -73
  126. package/frontend/src/components/ui/floating-toolbar.tsx +0 -85
  127. package/frontend/src/components/ui/font-color-toolbar-button.tsx +0 -831
  128. package/frontend/src/components/ui/font-size-toolbar-button.tsx +0 -152
  129. package/frontend/src/components/ui/heading-node-static.tsx +0 -68
  130. package/frontend/src/components/ui/heading-node.tsx +0 -58
  131. package/frontend/src/components/ui/highlight-node-static.tsx +0 -11
  132. package/frontend/src/components/ui/highlight-node.tsx +0 -13
  133. package/frontend/src/components/ui/history-toolbar-button.tsx +0 -50
  134. package/frontend/src/components/ui/hr-node-static.tsx +0 -20
  135. package/frontend/src/components/ui/hr-node.tsx +0 -33
  136. package/frontend/src/components/ui/import-toolbar-button.tsx +0 -97
  137. package/frontend/src/components/ui/indent-toolbar-button.tsx +0 -30
  138. package/frontend/src/components/ui/inline-combobox.tsx +0 -414
  139. package/frontend/src/components/ui/input.tsx +0 -21
  140. package/frontend/src/components/ui/insert-toolbar-button.tsx +0 -254
  141. package/frontend/src/components/ui/kbd-node-static.tsx +0 -15
  142. package/frontend/src/components/ui/kbd-node.tsx +0 -17
  143. package/frontend/src/components/ui/layout-header.tsx +0 -35
  144. package/frontend/src/components/ui/line-height-toolbar-button.tsx +0 -68
  145. package/frontend/src/components/ui/link-node-static.tsx +0 -21
  146. package/frontend/src/components/ui/link-node.tsx +0 -39
  147. package/frontend/src/components/ui/link-toolbar-button.tsx +0 -22
  148. package/frontend/src/components/ui/link-toolbar.tsx +0 -206
  149. package/frontend/src/components/ui/list-toolbar-button.tsx +0 -204
  150. package/frontend/src/components/ui/mark-toolbar-button.tsx +0 -19
  151. package/frontend/src/components/ui/media-audio-node-static.tsx +0 -17
  152. package/frontend/src/components/ui/media-audio-node.tsx +0 -39
  153. package/frontend/src/components/ui/media-embed-node.tsx +0 -136
  154. package/frontend/src/components/ui/media-file-node-static.tsx +0 -29
  155. package/frontend/src/components/ui/media-file-node.tsx +0 -47
  156. package/frontend/src/components/ui/media-image-node-static.tsx +0 -39
  157. package/frontend/src/components/ui/media-image-node.tsx +0 -80
  158. package/frontend/src/components/ui/media-placeholder-node.tsx +0 -249
  159. package/frontend/src/components/ui/media-preview-dialog.tsx +0 -152
  160. package/frontend/src/components/ui/media-toolbar-button.tsx +0 -225
  161. package/frontend/src/components/ui/media-toolbar.tsx +0 -115
  162. package/frontend/src/components/ui/media-upload-toast.tsx +0 -66
  163. package/frontend/src/components/ui/media-video-node-static.tsx +0 -30
  164. package/frontend/src/components/ui/media-video-node.tsx +0 -121
  165. package/frontend/src/components/ui/mention-node-static.tsx +0 -36
  166. package/frontend/src/components/ui/mention-node.tsx +0 -194
  167. package/frontend/src/components/ui/mode-toolbar-button.tsx +0 -123
  168. package/frontend/src/components/ui/more-toolbar-button.tsx +0 -80
  169. package/frontend/src/components/ui/paragraph-node-static.tsx +0 -13
  170. package/frontend/src/components/ui/paragraph-node.tsx +0 -15
  171. package/frontend/src/components/ui/popover.tsx +0 -46
  172. package/frontend/src/components/ui/resize-handle.tsx +0 -87
  173. package/frontend/src/components/ui/separator.tsx +0 -28
  174. package/frontend/src/components/ui/sheet.tsx +0 -139
  175. package/frontend/src/components/ui/sidebar.tsx +0 -726
  176. package/frontend/src/components/ui/skeleton.tsx +0 -13
  177. package/frontend/src/components/ui/slash-node.tsx +0 -233
  178. package/frontend/src/components/ui/sonner.tsx +0 -38
  179. package/frontend/src/components/ui/suggestion-node-static.tsx +0 -35
  180. package/frontend/src/components/ui/suggestion-node.tsx +0 -162
  181. package/frontend/src/components/ui/suggestion-toolbar-button.tsx +0 -25
  182. package/frontend/src/components/ui/table-icons.tsx +0 -862
  183. package/frontend/src/components/ui/table-node-static.tsx +0 -98
  184. package/frontend/src/components/ui/table-node.tsx +0 -656
  185. package/frontend/src/components/ui/table-toolbar-button.tsx +0 -264
  186. package/frontend/src/components/ui/toc-node-static.tsx +0 -92
  187. package/frontend/src/components/ui/toc-node.tsx +0 -55
  188. package/frontend/src/components/ui/toggle-node-static.tsx +0 -18
  189. package/frontend/src/components/ui/toggle-node.tsx +0 -36
  190. package/frontend/src/components/ui/toggle-toolbar-button.tsx +0 -22
  191. package/frontend/src/components/ui/toolbar.tsx +0 -387
  192. package/frontend/src/components/ui/tooltip.tsx +0 -59
  193. package/frontend/src/components/ui/turn-into-toolbar-button.tsx +0 -188
  194. package/frontend/src/hooks/use-debounce.ts +0 -18
  195. package/frontend/src/hooks/use-is-touch-device.ts +0 -24
  196. package/frontend/src/hooks/use-mobile.ts +0 -19
  197. package/frontend/src/hooks/use-mounted.ts +0 -11
  198. package/frontend/src/hooks/use-upload-file.ts +0 -128
  199. package/frontend/src/index.css +0 -128
  200. package/frontend/src/layout.tsx +0 -42
  201. package/frontend/src/lib/markdown-joiner-transform.ts +0 -239
  202. package/frontend/src/lib/orpc.ts +0 -13
  203. package/frontend/src/lib/uploadthing.ts +0 -19
  204. package/frontend/src/lib/utils.ts +0 -6
  205. package/frontend/src/main.tsx +0 -13
  206. package/frontend/src/pages/editor.tsx +0 -44
  207. package/frontend/src/types/docs.d.ts +0 -6
  208. package/frontend/src/types/global.d.ts +0 -9
  209. package/frontend/src/types/router.d.ts +0 -4
  210. package/frontend/tsconfig.app.json +0 -33
  211. package/frontend/tsconfig.json +0 -10
  212. package/frontend/tsconfig.node.json +0 -26
  213. package/frontend/vite.config.ts +0 -14
  214. package/src/bin/doclific.ts +0 -47
  215. package/src/core/codebase.ts +0 -39
  216. package/src/core/docs.ts +0 -90
  217. package/src/core/git.ts +0 -48
  218. package/src/server/index.ts +0 -55
  219. package/src/server/router.ts +0 -65
  220. package/tsconfig.json +0 -15
@@ -1,136 +0,0 @@
1
-
2
- import LiteYouTubeEmbed from 'react-lite-youtube-embed';
3
- import { Tweet } from 'react-tweet';
4
-
5
- import type { TMediaEmbedElement } from 'platejs';
6
- import type { PlateElementProps } from 'platejs/react';
7
-
8
- import { parseTwitterUrl, parseVideoUrl } from '@platejs/media';
9
- import { MediaEmbedPlugin, useMediaState } from '@platejs/media/react';
10
- import { ResizableProvider, useResizableValue } from '@platejs/resizable';
11
- import { PlateElement, withHOC } from 'platejs/react';
12
-
13
- import { cn } from '@/lib/utils';
14
-
15
- import { Caption, CaptionTextarea } from './caption';
16
- import { MediaToolbar } from './media-toolbar';
17
- import {
18
- mediaResizeHandleVariants,
19
- Resizable,
20
- ResizeHandle,
21
- } from './resize-handle';
22
-
23
- export const MediaEmbedElement = withHOC(
24
- ResizableProvider,
25
- function MediaEmbedElement(props: PlateElementProps<TMediaEmbedElement>) {
26
- const {
27
- align = 'center',
28
- embed,
29
- focused,
30
- isTweet,
31
- isVideo,
32
- isYoutube,
33
- readOnly,
34
- selected,
35
- } = useMediaState({
36
- urlParsers: [parseTwitterUrl, parseVideoUrl],
37
- });
38
- const width = useResizableValue('width');
39
- const provider = embed?.provider;
40
-
41
- return (
42
- <MediaToolbar plugin={MediaEmbedPlugin}>
43
- <PlateElement className="py-2.5" {...props}>
44
- <figure
45
- className="group relative m-0 w-full cursor-default"
46
- contentEditable={false}
47
- >
48
- <Resizable
49
- align={align}
50
- options={{
51
- align,
52
- maxWidth: isTweet ? 550 : '100%',
53
- minWidth: isTweet ? 300 : 100,
54
- }}
55
- >
56
- <ResizeHandle
57
- className={mediaResizeHandleVariants({ direction: 'left' })}
58
- options={{ direction: 'left' }}
59
- />
60
-
61
- {isVideo ? (
62
- isYoutube ? (
63
- <LiteYouTubeEmbed
64
- id={embed!.id!}
65
- title="youtube"
66
- wrapperClass={cn(
67
- 'rounded-sm',
68
- focused && selected && 'ring-2 ring-ring ring-offset-2',
69
- 'relative block cursor-pointer bg-black bg-center bg-cover [contain:content]',
70
- '[&.lyt-activated]:before:absolute [&.lyt-activated]:before:top-0 [&.lyt-activated]:before:h-[60px] [&.lyt-activated]:before:w-full [&.lyt-activated]:before:bg-top [&.lyt-activated]:before:bg-repeat-x [&.lyt-activated]:before:pb-[50px] [&.lyt-activated]:before:[transition:all_0.2s_cubic-bezier(0,_0,_0.2,_1)]',
71
- '[&.lyt-activated]:before:bg-[url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==)]',
72
- 'after:block after:pb-[var(--aspect-ratio)] after:content-[""]',
73
- '[&_>_iframe]:absolute [&_>_iframe]:top-0 [&_>_iframe]:left-0 [&_>_iframe]:size-full',
74
- '[&_>_.lty-playbtn]:z-1 [&_>_.lty-playbtn]:h-[46px] [&_>_.lty-playbtn]:w-[70px] [&_>_.lty-playbtn]:rounded-[14%] [&_>_.lty-playbtn]:bg-[#212121] [&_>_.lty-playbtn]:opacity-80 [&_>_.lty-playbtn]:[transition:all_0.2s_cubic-bezier(0,_0,_0.2,_1)]',
75
- '[&:hover_>_.lty-playbtn]:bg-[red] [&:hover_>_.lty-playbtn]:opacity-100',
76
- '[&_>_.lty-playbtn]:before:border-[transparent_transparent_transparent_#fff] [&_>_.lty-playbtn]:before:border-y-[11px] [&_>_.lty-playbtn]:before:border-r-0 [&_>_.lty-playbtn]:before:border-l-[19px] [&_>_.lty-playbtn]:before:content-[""]',
77
- '[&_>_.lty-playbtn]:absolute [&_>_.lty-playbtn]:top-1/2 [&_>_.lty-playbtn]:left-1/2 [&_>_.lty-playbtn]:[transform:translate3d(-50%,-50%,0)]',
78
- '[&_>_.lty-playbtn]:before:absolute [&_>_.lty-playbtn]:before:top-1/2 [&_>_.lty-playbtn]:before:left-1/2 [&_>_.lty-playbtn]:before:[transform:translate3d(-50%,-50%,0)]',
79
- '[&.lyt-activated]:cursor-[unset]',
80
- '[&.lyt-activated]:before:pointer-events-none [&.lyt-activated]:before:opacity-0',
81
- '[&.lyt-activated_>_.lty-playbtn]:pointer-events-none [&.lyt-activated_>_.lty-playbtn]:opacity-0!'
82
- )}
83
- />
84
- ) : (
85
- <div
86
- className={cn(
87
- provider === 'vimeo' && 'pb-[75%]',
88
- provider === 'youku' && 'pb-[56.25%]',
89
- provider === 'dailymotion' && 'pb-[56.0417%]',
90
- provider === 'coub' && 'pb-[51.25%]'
91
- )}
92
- >
93
- <iframe
94
- className={cn(
95
- 'absolute top-0 left-0 size-full rounded-sm',
96
- isVideo && 'border-0',
97
- focused && selected && 'ring-2 ring-ring ring-offset-2'
98
- )}
99
- title="embed"
100
- src={embed!.url}
101
- allowFullScreen
102
- />
103
- </div>
104
- )
105
- ) : null}
106
-
107
- {isTweet && (
108
- <div
109
- className={cn(
110
- '[&_.react-tweet-theme]:my-0',
111
- !readOnly &&
112
- selected &&
113
- '[&_.react-tweet-theme]:ring-2 [&_.react-tweet-theme]:ring-ring [&_.react-tweet-theme]:ring-offset-2'
114
- )}
115
- >
116
- <Tweet id={embed!.id!} />
117
- </div>
118
- )}
119
-
120
- <ResizeHandle
121
- className={mediaResizeHandleVariants({ direction: 'right' })}
122
- options={{ direction: 'right' }}
123
- />
124
- </Resizable>
125
-
126
- <Caption style={{ width }} align={align}>
127
- <CaptionTextarea placeholder="Write a caption..." />
128
- </Caption>
129
- </figure>
130
-
131
- {props.children}
132
- </PlateElement>
133
- </MediaToolbar>
134
- );
135
- }
136
- );
@@ -1,29 +0,0 @@
1
- import type { TFileElement } from 'platejs';
2
- import type { SlateElementProps } from 'platejs/static';
3
-
4
- import { FileUp } from 'lucide-react';
5
- import { SlateElement } from 'platejs/static';
6
-
7
- export function FileElementStatic(props: SlateElementProps<TFileElement>) {
8
- const { name, url } = props.element;
9
-
10
- return (
11
- <SlateElement className="my-px rounded-sm" {...props}>
12
- <a
13
- className="group relative m-0 flex cursor-pointer items-center rounded px-0.5 py-[3px] hover:bg-muted"
14
- contentEditable={false}
15
- download={name}
16
- href={url}
17
- rel="noopener noreferrer"
18
- role="button"
19
- target="_blank"
20
- >
21
- <div className="flex items-center gap-1 p-1">
22
- <FileUp className="size-5" />
23
- <div>{name}</div>
24
- </div>
25
- </a>
26
- {props.children}
27
- </SlateElement>
28
- );
29
- }
@@ -1,47 +0,0 @@
1
-
2
-
3
- import type { TFileElement } from 'platejs';
4
- import type { PlateElementProps } from 'platejs/react';
5
-
6
- import { useMediaState } from '@platejs/media/react';
7
- import { ResizableProvider } from '@platejs/resizable';
8
- import { FileUp } from 'lucide-react';
9
- import { PlateElement, useReadOnly, withHOC } from 'platejs/react';
10
-
11
- import { Caption, CaptionTextarea } from './caption';
12
-
13
- export const FileElement = withHOC(
14
- ResizableProvider,
15
- function FileElement(props: PlateElementProps<TFileElement>) {
16
- const readOnly = useReadOnly();
17
- const { name, unsafeUrl } = useMediaState();
18
-
19
- return (
20
- <PlateElement className="my-px rounded-sm" {...props}>
21
- <a
22
- className="group relative m-0 flex cursor-pointer items-center rounded px-0.5 py-[3px] hover:bg-muted"
23
- contentEditable={false}
24
- download={name}
25
- href={unsafeUrl}
26
- rel="noopener noreferrer"
27
- role="button"
28
- target="_blank"
29
- >
30
- <div className="flex items-center gap-1 p-1">
31
- <FileUp className="size-5" />
32
- <div>{name}</div>
33
- </div>
34
-
35
- <Caption align="left">
36
- <CaptionTextarea
37
- className="text-left"
38
- readOnly={readOnly}
39
- placeholder="Write a caption..."
40
- />
41
- </Caption>
42
- </a>
43
- {props.children}
44
- </PlateElement>
45
- );
46
- }
47
- );
@@ -1,39 +0,0 @@
1
- import type { TCaptionProps, TImageElement, TResizableProps } from 'platejs';
2
- import type { SlateElementProps } from 'platejs/static';
3
-
4
- import { NodeApi } from 'platejs';
5
- import { SlateElement } from 'platejs/static';
6
-
7
- import { cn } from '@/lib/utils';
8
-
9
- export function ImageElementStatic(
10
- props: SlateElementProps<TImageElement & TCaptionProps & TResizableProps>
11
- ) {
12
- const { align = 'center', caption, url, width } = props.element;
13
-
14
- return (
15
- <SlateElement {...props} className="py-2.5">
16
- <figure className="group relative m-0 inline-block" style={{ width }}>
17
- <div
18
- className="relative min-w-[92px] max-w-full"
19
- style={{ textAlign: align }}
20
- >
21
- <img
22
- className={cn(
23
- 'w-full max-w-full cursor-default object-cover px-0',
24
- 'rounded-sm'
25
- )}
26
- alt={(props.attributes as any).alt}
27
- src={url}
28
- />
29
- {caption && (
30
- <figcaption className="mx-auto mt-2 h-[24px] max-w-full">
31
- {NodeApi.string(caption[0])}
32
- </figcaption>
33
- )}
34
- </div>
35
- </figure>
36
- {props.children}
37
- </SlateElement>
38
- );
39
- }
@@ -1,80 +0,0 @@
1
-
2
-
3
- import type { TImageElement } from 'platejs';
4
- import type { PlateElementProps } from 'platejs/react';
5
-
6
- import { useDraggable } from '@platejs/dnd';
7
- import { Image, ImagePlugin, useMediaState } from '@platejs/media/react';
8
- import { ResizableProvider, useResizableValue } from '@platejs/resizable';
9
- import { PlateElement, withHOC } from 'platejs/react';
10
-
11
- import { cn } from '@/lib/utils';
12
-
13
- import { Caption, CaptionTextarea } from './caption';
14
- import { MediaToolbar } from './media-toolbar';
15
- import {
16
- mediaResizeHandleVariants,
17
- Resizable,
18
- ResizeHandle,
19
- } from './resize-handle';
20
-
21
- export const ImageElement = withHOC(
22
- ResizableProvider,
23
- function ImageElement(props: PlateElementProps<TImageElement>) {
24
- const { align = 'center', focused, readOnly, selected } = useMediaState();
25
- const width = useResizableValue('width');
26
-
27
- const { isDragging, handleRef } = useDraggable({
28
- element: props.element,
29
- });
30
-
31
- return (
32
- <MediaToolbar plugin={ImagePlugin}>
33
- <PlateElement {...props} className="py-2.5">
34
- <figure className="group relative m-0" contentEditable={false}>
35
- <Resizable
36
- align={align}
37
- options={{
38
- align,
39
- readOnly,
40
- }}
41
- >
42
- <ResizeHandle
43
- className={mediaResizeHandleVariants({ direction: 'left' })}
44
- options={{ direction: 'left' }}
45
- />
46
- <Image
47
- ref={handleRef}
48
- className={cn(
49
- 'block w-full max-w-full cursor-pointer object-cover px-0',
50
- 'rounded-sm',
51
- focused && selected && 'ring-2 ring-ring ring-offset-2',
52
- isDragging && 'opacity-50'
53
- )}
54
- alt={props.attributes.alt as string | undefined}
55
- />
56
- <ResizeHandle
57
- className={mediaResizeHandleVariants({
58
- direction: 'right',
59
- })}
60
- options={{ direction: 'right' }}
61
- />
62
- </Resizable>
63
-
64
- <Caption style={{ width }} align={align}>
65
- <CaptionTextarea
66
- readOnly={readOnly}
67
- onFocus={(e) => {
68
- e.preventDefault();
69
- }}
70
- placeholder="Write a caption..."
71
- />
72
- </Caption>
73
- </figure>
74
-
75
- {props.children}
76
- </PlateElement>
77
- </MediaToolbar>
78
- );
79
- }
80
- );
@@ -1,249 +0,0 @@
1
- import React from 'react';
2
-
3
- import type { TPlaceholderElement } from 'platejs';
4
- import type { PlateElementProps } from 'platejs/react';
5
-
6
- import {
7
- PlaceholderPlugin,
8
- PlaceholderProvider,
9
- updateUploadHistory,
10
- } from '@platejs/media/react';
11
- import { AudioLines, FileUp, Film, ImageIcon, Loader2Icon } from 'lucide-react';
12
- import { KEYS } from 'platejs';
13
- import { PlateElement, useEditorPlugin, withHOC } from 'platejs/react';
14
- import { useFilePicker } from 'use-file-picker';
15
-
16
- import { cn } from '@/lib/utils';
17
- import { useUploadFile } from '@/hooks/use-upload-file';
18
-
19
- const CONTENT: Record<
20
- string,
21
- {
22
- accept: string[];
23
- content: React.ReactNode;
24
- icon: React.ReactNode;
25
- }
26
- > = {
27
- [KEYS.audio]: {
28
- accept: ['audio/*'],
29
- content: 'Add an audio file',
30
- icon: <AudioLines />,
31
- },
32
- [KEYS.file]: {
33
- accept: ['*'],
34
- content: 'Add a file',
35
- icon: <FileUp />,
36
- },
37
- [KEYS.img]: {
38
- accept: ['image/*'],
39
- content: 'Add an image',
40
- icon: <ImageIcon />,
41
- },
42
- [KEYS.video]: {
43
- accept: ['video/*'],
44
- content: 'Add a video',
45
- icon: <Film />,
46
- },
47
- };
48
-
49
- export const PlaceholderElement = withHOC(
50
- PlaceholderProvider,
51
- function PlaceholderElement(props: PlateElementProps<TPlaceholderElement>) {
52
- const { editor, element } = props;
53
-
54
- const { api } = useEditorPlugin(PlaceholderPlugin);
55
-
56
- const { isUploading, progress, uploadedFile, uploadFile, uploadingFile } =
57
- useUploadFile();
58
-
59
- const loading = isUploading && uploadingFile;
60
-
61
- const currentContent = CONTENT[element.mediaType];
62
-
63
- const isImage = element.mediaType === KEYS.img;
64
-
65
- const imageRef = React.useRef<HTMLImageElement>(null);
66
-
67
- const { openFilePicker } = useFilePicker({
68
- accept: currentContent.accept,
69
- multiple: true,
70
- onFilesSelected: ({ plainFiles: updatedFiles }) => {
71
- const firstFile = updatedFiles[0];
72
- const restFiles = updatedFiles.slice(1);
73
-
74
- replaceCurrentPlaceholder(firstFile);
75
-
76
- if (restFiles.length > 0) {
77
- editor.getTransforms(PlaceholderPlugin).insert.media(restFiles);
78
- }
79
- },
80
- });
81
-
82
- const replaceCurrentPlaceholder = React.useCallback(
83
- (file: File) => {
84
- void uploadFile(file);
85
- api.placeholder.addUploadingFile(element.id as string, file);
86
- },
87
- [api.placeholder, element.id, uploadFile]
88
- );
89
-
90
- React.useEffect(() => {
91
- if (!uploadedFile) return;
92
-
93
- const path = editor.api.findPath(element);
94
-
95
- editor.tf.withoutSaving(() => {
96
- editor.tf.removeNodes({ at: path });
97
-
98
- const node = {
99
- children: [{ text: '' }],
100
- initialHeight: imageRef.current?.height,
101
- initialWidth: imageRef.current?.width,
102
- isUpload: true,
103
- name: element.mediaType === KEYS.file ? uploadedFile.name : '',
104
- placeholderId: element.id as string,
105
- type: element.mediaType!,
106
- url: uploadedFile.url,
107
- };
108
-
109
- editor.tf.insertNodes(node, { at: path });
110
-
111
- updateUploadHistory(editor, node);
112
- });
113
-
114
- api.placeholder.removeUploadingFile(element.id as string);
115
- // eslint-disable-next-line react-hooks/exhaustive-deps
116
- }, [uploadedFile, element.id]);
117
-
118
- // React dev mode will call React.useEffect twice
119
- const isReplaced = React.useRef(false);
120
-
121
- /** Paste and drop */
122
- React.useEffect(() => {
123
- if (isReplaced.current) return;
124
-
125
- isReplaced.current = true;
126
- const currentFiles = api.placeholder.getUploadingFile(
127
- element.id as string
128
- );
129
-
130
- if (!currentFiles) return;
131
-
132
- replaceCurrentPlaceholder(currentFiles);
133
-
134
- // eslint-disable-next-line react-hooks/exhaustive-deps
135
- }, [isReplaced]);
136
-
137
- return (
138
- <PlateElement className="my-1" {...props}>
139
- {(!loading || !isImage) && (
140
- <div
141
- className={cn(
142
- 'flex cursor-pointer select-none items-center rounded-sm bg-muted p-3 pr-9 hover:bg-primary/10'
143
- )}
144
- onClick={() => !loading && openFilePicker()}
145
- contentEditable={false}
146
- >
147
- <div className="relative mr-3 flex text-muted-foreground/80 [&_svg]:size-6">
148
- {currentContent.icon}
149
- </div>
150
- <div className="whitespace-nowrap text-muted-foreground text-sm">
151
- <div>
152
- {loading ? uploadingFile?.name : currentContent.content}
153
- </div>
154
-
155
- {loading && !isImage && (
156
- <div className="mt-1 flex items-center gap-1.5">
157
- <div>{formatBytes(uploadingFile?.size ?? 0)}</div>
158
- <div>–</div>
159
- <div className="flex items-center">
160
- <Loader2Icon className="mr-1 size-3.5 animate-spin text-muted-foreground" />
161
- {progress ?? 0}%
162
- </div>
163
- </div>
164
- )}
165
- </div>
166
- </div>
167
- )}
168
-
169
- {isImage && loading && (
170
- <ImageProgress
171
- file={uploadingFile}
172
- imageRef={imageRef}
173
- progress={progress}
174
- />
175
- )}
176
-
177
- {props.children}
178
- </PlateElement>
179
- );
180
- }
181
- );
182
-
183
- export function ImageProgress({
184
- className,
185
- file,
186
- imageRef,
187
- progress = 0,
188
- }: {
189
- file: File;
190
- className?: string;
191
- imageRef?: React.RefObject<HTMLImageElement | null>;
192
- progress?: number;
193
- }) {
194
- const [objectUrl, setObjectUrl] = React.useState<string | null>(null);
195
-
196
- React.useEffect(() => {
197
- const url = URL.createObjectURL(file);
198
- setObjectUrl(url);
199
-
200
- return () => {
201
- URL.revokeObjectURL(url);
202
- };
203
- }, [file]);
204
-
205
- if (!objectUrl) {
206
- return null;
207
- }
208
-
209
- return (
210
- <div className={cn('relative', className)} contentEditable={false}>
211
- <img
212
- ref={imageRef}
213
- className="h-auto w-full rounded-sm object-cover"
214
- alt={file.name}
215
- src={objectUrl}
216
- />
217
- {progress < 100 && (
218
- <div className="absolute right-1 bottom-1 flex items-center space-x-2 rounded-full bg-black/50 px-1 py-0.5">
219
- <Loader2Icon className="size-3.5 animate-spin text-muted-foreground" />
220
- <span className="font-medium text-white text-xs">
221
- {Math.round(progress)}%
222
- </span>
223
- </div>
224
- )}
225
- </div>
226
- );
227
- }
228
-
229
- function formatBytes(
230
- bytes: number,
231
- opts: {
232
- decimals?: number;
233
- sizeType?: 'accurate' | 'normal';
234
- } = {}
235
- ) {
236
- const { decimals = 0, sizeType = 'normal' } = opts;
237
-
238
- const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
239
- const accurateSizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB'];
240
-
241
- if (bytes === 0) return '0 Byte';
242
-
243
- const i = Math.floor(Math.log(bytes) / Math.log(1024));
244
-
245
- return `${(bytes / 1024 ** i).toFixed(decimals)} ${sizeType === 'accurate'
246
- ? (accurateSizes[i] ?? 'Bytest')
247
- : (sizes[i] ?? 'Bytes')
248
- }`;
249
- }