doclific 0.1.0

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 (231) hide show
  1. package/.gitattributes +2 -0
  2. package/.prettierignore +5 -0
  3. package/.prettierrc +9 -0
  4. package/.vscode/settings.json +13 -0
  5. package/dist/bin/doclific.d.ts +3 -0
  6. package/dist/bin/doclific.d.ts.map +1 -0
  7. package/dist/bin/doclific.js +11 -0
  8. package/dist/core/codebase.js +31 -0
  9. package/dist/core/docs.js +75 -0
  10. package/dist/core/git.js +47 -0
  11. package/dist/server/index.d.ts +2 -0
  12. package/dist/server/index.d.ts.map +1 -0
  13. package/dist/server/index.js +46 -0
  14. package/dist/server/router.d.ts +9 -0
  15. package/dist/server/router.d.ts.map +1 -0
  16. package/dist/server/router.js +55 -0
  17. package/frontend/README.md +73 -0
  18. package/frontend/components.json +24 -0
  19. package/frontend/eslint.config.js +23 -0
  20. package/frontend/index.html +25 -0
  21. package/frontend/package-lock.json +15754 -0
  22. package/frontend/package.json +122 -0
  23. package/frontend/public/logo.svg +1 -0
  24. package/frontend/src/App.tsx +21 -0
  25. package/frontend/src/components/app-sidebar.tsx +393 -0
  26. package/frontend/src/components/editor/editor-base-kit.tsx +43 -0
  27. package/frontend/src/components/editor/editor-kit.tsx +93 -0
  28. package/frontend/src/components/editor/plugins/align-base-kit.tsx +16 -0
  29. package/frontend/src/components/editor/plugins/align-kit.tsx +18 -0
  30. package/frontend/src/components/editor/plugins/autoformat-kit.tsx +236 -0
  31. package/frontend/src/components/editor/plugins/basic-blocks-base-kit.tsx +35 -0
  32. package/frontend/src/components/editor/plugins/basic-blocks-kit.tsx +88 -0
  33. package/frontend/src/components/editor/plugins/basic-marks-base-kit.tsx +27 -0
  34. package/frontend/src/components/editor/plugins/basic-marks-kit.tsx +41 -0
  35. package/frontend/src/components/editor/plugins/basic-nodes-kit.tsx +6 -0
  36. package/frontend/src/components/editor/plugins/block-menu-kit.tsx +14 -0
  37. package/frontend/src/components/editor/plugins/block-placeholder-kit.tsx +17 -0
  38. package/frontend/src/components/editor/plugins/block-selection-kit.tsx +32 -0
  39. package/frontend/src/components/editor/plugins/callout-base-kit.tsx +7 -0
  40. package/frontend/src/components/editor/plugins/callout-kit.tsx +7 -0
  41. package/frontend/src/components/editor/plugins/code-block-base-kit.tsx +23 -0
  42. package/frontend/src/components/editor/plugins/code-block-kit.tsx +26 -0
  43. package/frontend/src/components/editor/plugins/codebase-kit.tsx +23 -0
  44. package/frontend/src/components/editor/plugins/column-base-kit.tsx +11 -0
  45. package/frontend/src/components/editor/plugins/column-kit.tsx +10 -0
  46. package/frontend/src/components/editor/plugins/comment-base-kit.tsx +7 -0
  47. package/frontend/src/components/editor/plugins/comment-kit.tsx +97 -0
  48. package/frontend/src/components/editor/plugins/cursor-overlay-kit.tsx +13 -0
  49. package/frontend/src/components/editor/plugins/date-base-kit.tsx +5 -0
  50. package/frontend/src/components/editor/plugins/date-kit.tsx +7 -0
  51. package/frontend/src/components/editor/plugins/discussion-kit.tsx +148 -0
  52. package/frontend/src/components/editor/plugins/dnd-kit.tsx +28 -0
  53. package/frontend/src/components/editor/plugins/docx-kit.tsx +6 -0
  54. package/frontend/src/components/editor/plugins/emoji-kit.tsx +13 -0
  55. package/frontend/src/components/editor/plugins/excalidraw-kit.tsx +9 -0
  56. package/frontend/src/components/editor/plugins/exit-break-kit.tsx +12 -0
  57. package/frontend/src/components/editor/plugins/floating-toolbar-kit.tsx +19 -0
  58. package/frontend/src/components/editor/plugins/font-base-kit.tsx +20 -0
  59. package/frontend/src/components/editor/plugins/font-kit.tsx +29 -0
  60. package/frontend/src/components/editor/plugins/indent-base-kit.tsx +19 -0
  61. package/frontend/src/components/editor/plugins/indent-kit.tsx +22 -0
  62. package/frontend/src/components/editor/plugins/line-height-base-kit.tsx +14 -0
  63. package/frontend/src/components/editor/plugins/line-height-kit.tsx +16 -0
  64. package/frontend/src/components/editor/plugins/link-base-kit.tsx +5 -0
  65. package/frontend/src/components/editor/plugins/link-kit.tsx +15 -0
  66. package/frontend/src/components/editor/plugins/list-base-kit.tsx +23 -0
  67. package/frontend/src/components/editor/plugins/list-kit.tsx +26 -0
  68. package/frontend/src/components/editor/plugins/markdown-kit.tsx +46 -0
  69. package/frontend/src/components/editor/plugins/math-base-kit.tsx +11 -0
  70. package/frontend/src/components/editor/plugins/math-kit.tsx +13 -0
  71. package/frontend/src/components/editor/plugins/media-base-kit.tsx +31 -0
  72. package/frontend/src/components/editor/plugins/media-kit.tsx +43 -0
  73. package/frontend/src/components/editor/plugins/mention-base-kit.tsx +7 -0
  74. package/frontend/src/components/editor/plugins/mention-kit.tsx +15 -0
  75. package/frontend/src/components/editor/plugins/slash-kit.tsx +18 -0
  76. package/frontend/src/components/editor/plugins/suggestion-base-kit.tsx +7 -0
  77. package/frontend/src/components/editor/plugins/suggestion-kit.tsx +90 -0
  78. package/frontend/src/components/editor/plugins/table-base-kit.tsx +20 -0
  79. package/frontend/src/components/editor/plugins/table-kit.tsx +22 -0
  80. package/frontend/src/components/editor/plugins/toc-base-kit.tsx +5 -0
  81. package/frontend/src/components/editor/plugins/toc-kit.tsx +14 -0
  82. package/frontend/src/components/editor/plugins/toggle-base-kit.tsx +7 -0
  83. package/frontend/src/components/editor/plugins/toggle-kit.tsx +11 -0
  84. package/frontend/src/components/editor/transforms.ts +194 -0
  85. package/frontend/src/components/markdown-to-slate-demo.tsx +50 -0
  86. package/frontend/src/components/mode-toggle.tsx +15 -0
  87. package/frontend/src/components/theme-provider.tsx +73 -0
  88. package/frontend/src/components/ui/alert-dialog.tsx +155 -0
  89. package/frontend/src/components/ui/align-toolbar-button.tsx +84 -0
  90. package/frontend/src/components/ui/avatar.tsx +51 -0
  91. package/frontend/src/components/ui/block-context-menu.tsx +199 -0
  92. package/frontend/src/components/ui/block-discussion.tsx +365 -0
  93. package/frontend/src/components/ui/block-draggable.tsx +512 -0
  94. package/frontend/src/components/ui/block-list-static.tsx +80 -0
  95. package/frontend/src/components/ui/block-list.tsx +87 -0
  96. package/frontend/src/components/ui/block-selection.tsx +42 -0
  97. package/frontend/src/components/ui/block-suggestion.tsx +473 -0
  98. package/frontend/src/components/ui/blockquote-node-static.tsx +11 -0
  99. package/frontend/src/components/ui/blockquote-node.tsx +13 -0
  100. package/frontend/src/components/ui/button.tsx +62 -0
  101. package/frontend/src/components/ui/calendar.tsx +218 -0
  102. package/frontend/src/components/ui/callout-node-static.tsx +36 -0
  103. package/frontend/src/components/ui/callout-node.tsx +63 -0
  104. package/frontend/src/components/ui/caption.tsx +63 -0
  105. package/frontend/src/components/ui/checkbox.tsx +30 -0
  106. package/frontend/src/components/ui/code-block-node-static.tsx +35 -0
  107. package/frontend/src/components/ui/code-block-node.tsx +287 -0
  108. package/frontend/src/components/ui/code-node-static.tsx +15 -0
  109. package/frontend/src/components/ui/code-node.tsx +17 -0
  110. package/frontend/src/components/ui/codebase-snippet-node.tsx +237 -0
  111. package/frontend/src/components/ui/column-node-static.tsx +29 -0
  112. package/frontend/src/components/ui/column-node.tsx +317 -0
  113. package/frontend/src/components/ui/command.tsx +182 -0
  114. package/frontend/src/components/ui/comment-node-static.tsx +15 -0
  115. package/frontend/src/components/ui/comment-node.tsx +45 -0
  116. package/frontend/src/components/ui/comment-toolbar-button.tsx +24 -0
  117. package/frontend/src/components/ui/comment.tsx +618 -0
  118. package/frontend/src/components/ui/context-menu.tsx +250 -0
  119. package/frontend/src/components/ui/cursor-overlay.tsx +66 -0
  120. package/frontend/src/components/ui/date-node-static.tsx +45 -0
  121. package/frontend/src/components/ui/date-node.tsx +93 -0
  122. package/frontend/src/components/ui/dialog.tsx +143 -0
  123. package/frontend/src/components/ui/dropdown-menu.tsx +255 -0
  124. package/frontend/src/components/ui/dynamic-icon.tsx +12 -0
  125. package/frontend/src/components/ui/editor-static.tsx +53 -0
  126. package/frontend/src/components/ui/editor.tsx +130 -0
  127. package/frontend/src/components/ui/emoji-node.tsx +69 -0
  128. package/frontend/src/components/ui/emoji-toolbar-button.tsx +628 -0
  129. package/frontend/src/components/ui/equation-node-static.tsx +98 -0
  130. package/frontend/src/components/ui/equation-node.tsx +235 -0
  131. package/frontend/src/components/ui/equation-toolbar-button.tsx +25 -0
  132. package/frontend/src/components/ui/excalidraw-node.tsx +36 -0
  133. package/frontend/src/components/ui/export-toolbar-button.tsx +174 -0
  134. package/frontend/src/components/ui/file-selector.tsx +339 -0
  135. package/frontend/src/components/ui/floating-toolbar-buttons.tsx +73 -0
  136. package/frontend/src/components/ui/floating-toolbar.tsx +85 -0
  137. package/frontend/src/components/ui/font-color-toolbar-button.tsx +831 -0
  138. package/frontend/src/components/ui/font-size-toolbar-button.tsx +152 -0
  139. package/frontend/src/components/ui/heading-node-static.tsx +68 -0
  140. package/frontend/src/components/ui/heading-node.tsx +58 -0
  141. package/frontend/src/components/ui/highlight-node-static.tsx +11 -0
  142. package/frontend/src/components/ui/highlight-node.tsx +13 -0
  143. package/frontend/src/components/ui/history-toolbar-button.tsx +50 -0
  144. package/frontend/src/components/ui/hr-node-static.tsx +20 -0
  145. package/frontend/src/components/ui/hr-node.tsx +33 -0
  146. package/frontend/src/components/ui/import-toolbar-button.tsx +97 -0
  147. package/frontend/src/components/ui/indent-toolbar-button.tsx +30 -0
  148. package/frontend/src/components/ui/inline-combobox.tsx +414 -0
  149. package/frontend/src/components/ui/input.tsx +21 -0
  150. package/frontend/src/components/ui/insert-toolbar-button.tsx +254 -0
  151. package/frontend/src/components/ui/kbd-node-static.tsx +15 -0
  152. package/frontend/src/components/ui/kbd-node.tsx +17 -0
  153. package/frontend/src/components/ui/layout-header.tsx +35 -0
  154. package/frontend/src/components/ui/line-height-toolbar-button.tsx +68 -0
  155. package/frontend/src/components/ui/link-node-static.tsx +21 -0
  156. package/frontend/src/components/ui/link-node.tsx +39 -0
  157. package/frontend/src/components/ui/link-toolbar-button.tsx +22 -0
  158. package/frontend/src/components/ui/link-toolbar.tsx +206 -0
  159. package/frontend/src/components/ui/list-toolbar-button.tsx +204 -0
  160. package/frontend/src/components/ui/mark-toolbar-button.tsx +19 -0
  161. package/frontend/src/components/ui/media-audio-node-static.tsx +17 -0
  162. package/frontend/src/components/ui/media-audio-node.tsx +39 -0
  163. package/frontend/src/components/ui/media-embed-node.tsx +136 -0
  164. package/frontend/src/components/ui/media-file-node-static.tsx +29 -0
  165. package/frontend/src/components/ui/media-file-node.tsx +47 -0
  166. package/frontend/src/components/ui/media-image-node-static.tsx +39 -0
  167. package/frontend/src/components/ui/media-image-node.tsx +80 -0
  168. package/frontend/src/components/ui/media-placeholder-node.tsx +249 -0
  169. package/frontend/src/components/ui/media-preview-dialog.tsx +152 -0
  170. package/frontend/src/components/ui/media-toolbar-button.tsx +225 -0
  171. package/frontend/src/components/ui/media-toolbar.tsx +115 -0
  172. package/frontend/src/components/ui/media-upload-toast.tsx +66 -0
  173. package/frontend/src/components/ui/media-video-node-static.tsx +30 -0
  174. package/frontend/src/components/ui/media-video-node.tsx +121 -0
  175. package/frontend/src/components/ui/mention-node-static.tsx +36 -0
  176. package/frontend/src/components/ui/mention-node.tsx +194 -0
  177. package/frontend/src/components/ui/mode-toolbar-button.tsx +123 -0
  178. package/frontend/src/components/ui/more-toolbar-button.tsx +80 -0
  179. package/frontend/src/components/ui/paragraph-node-static.tsx +13 -0
  180. package/frontend/src/components/ui/paragraph-node.tsx +15 -0
  181. package/frontend/src/components/ui/popover.tsx +46 -0
  182. package/frontend/src/components/ui/resize-handle.tsx +87 -0
  183. package/frontend/src/components/ui/separator.tsx +28 -0
  184. package/frontend/src/components/ui/sheet.tsx +139 -0
  185. package/frontend/src/components/ui/sidebar.tsx +726 -0
  186. package/frontend/src/components/ui/skeleton.tsx +13 -0
  187. package/frontend/src/components/ui/slash-node.tsx +233 -0
  188. package/frontend/src/components/ui/sonner.tsx +38 -0
  189. package/frontend/src/components/ui/suggestion-node-static.tsx +35 -0
  190. package/frontend/src/components/ui/suggestion-node.tsx +162 -0
  191. package/frontend/src/components/ui/suggestion-toolbar-button.tsx +25 -0
  192. package/frontend/src/components/ui/table-icons.tsx +862 -0
  193. package/frontend/src/components/ui/table-node-static.tsx +98 -0
  194. package/frontend/src/components/ui/table-node.tsx +656 -0
  195. package/frontend/src/components/ui/table-toolbar-button.tsx +264 -0
  196. package/frontend/src/components/ui/toc-node-static.tsx +92 -0
  197. package/frontend/src/components/ui/toc-node.tsx +55 -0
  198. package/frontend/src/components/ui/toggle-node-static.tsx +18 -0
  199. package/frontend/src/components/ui/toggle-node.tsx +36 -0
  200. package/frontend/src/components/ui/toggle-toolbar-button.tsx +22 -0
  201. package/frontend/src/components/ui/toolbar.tsx +387 -0
  202. package/frontend/src/components/ui/tooltip.tsx +59 -0
  203. package/frontend/src/components/ui/turn-into-toolbar-button.tsx +188 -0
  204. package/frontend/src/hooks/use-debounce.ts +18 -0
  205. package/frontend/src/hooks/use-is-touch-device.ts +24 -0
  206. package/frontend/src/hooks/use-mobile.ts +19 -0
  207. package/frontend/src/hooks/use-mounted.ts +11 -0
  208. package/frontend/src/hooks/use-upload-file.ts +128 -0
  209. package/frontend/src/index.css +128 -0
  210. package/frontend/src/layout.tsx +42 -0
  211. package/frontend/src/lib/markdown-joiner-transform.ts +239 -0
  212. package/frontend/src/lib/orpc.ts +13 -0
  213. package/frontend/src/lib/uploadthing.ts +19 -0
  214. package/frontend/src/lib/utils.ts +6 -0
  215. package/frontend/src/main.tsx +13 -0
  216. package/frontend/src/pages/editor.tsx +44 -0
  217. package/frontend/src/types/docs.d.ts +6 -0
  218. package/frontend/src/types/global.d.ts +9 -0
  219. package/frontend/src/types/router.d.ts +4 -0
  220. package/frontend/tsconfig.app.json +33 -0
  221. package/frontend/tsconfig.json +10 -0
  222. package/frontend/tsconfig.node.json +26 -0
  223. package/frontend/vite.config.ts +14 -0
  224. package/package.json +30 -0
  225. package/src/bin/doclific.ts +17 -0
  226. package/src/core/codebase.ts +39 -0
  227. package/src/core/docs.ts +90 -0
  228. package/src/core/git.ts +48 -0
  229. package/src/server/index.ts +55 -0
  230. package/src/server/router.ts +65 -0
  231. package/tsconfig.json +15 -0
@@ -0,0 +1,831 @@
1
+ 'use client';
2
+ /* eslint-disable react-hooks/refs -- Ref usage for color picker component refs */
3
+
4
+ import React from 'react';
5
+
6
+ import type {
7
+ DropdownMenuItemProps,
8
+ DropdownMenuProps,
9
+ } from '@radix-ui/react-dropdown-menu';
10
+
11
+ import { useComposedRef } from '@udecode/cn';
12
+ import debounce from 'lodash/debounce.js';
13
+ import { EraserIcon, PlusIcon } from 'lucide-react';
14
+ import { useEditorRef, useEditorSelector } from 'platejs/react';
15
+
16
+ import { buttonVariants } from '@/components/ui/button';
17
+ import {
18
+ DropdownMenu,
19
+ DropdownMenuContent,
20
+ DropdownMenuItem,
21
+ DropdownMenuTrigger,
22
+ } from '@/components/ui/dropdown-menu';
23
+ import {
24
+ Tooltip,
25
+ TooltipContent,
26
+ TooltipProvider,
27
+ TooltipTrigger,
28
+ } from '@/components/ui/tooltip';
29
+ import { cn } from '@/lib/utils';
30
+
31
+ import { ToolbarButton, ToolbarMenuGroup } from './toolbar';
32
+
33
+ export function FontColorToolbarButton({
34
+ children,
35
+ nodeType,
36
+ tooltip,
37
+ }: {
38
+ nodeType: string;
39
+ tooltip?: string;
40
+ } & DropdownMenuProps) {
41
+ const editor = useEditorRef();
42
+
43
+ const selectionDefined = useEditorSelector(
44
+ (editor) => !!editor.selection,
45
+ []
46
+ );
47
+
48
+ const color = useEditorSelector(
49
+ (editor) => editor.api.mark(nodeType) as string,
50
+ [nodeType]
51
+ );
52
+
53
+ const [selectedColor, setSelectedColor] = React.useState<string>();
54
+ const [open, setOpen] = React.useState(false);
55
+
56
+ const onToggle = React.useCallback(
57
+ (value = !open) => {
58
+ setOpen(value);
59
+ },
60
+ [open, setOpen]
61
+ );
62
+
63
+ const updateColor = React.useCallback(
64
+ (value: string) => {
65
+ if (editor.selection) {
66
+ setSelectedColor(value);
67
+
68
+ editor.tf.select(editor.selection);
69
+ editor.tf.focus();
70
+
71
+ editor.tf.addMarks({ [nodeType]: value });
72
+ }
73
+ },
74
+ [editor, nodeType]
75
+ );
76
+
77
+ const updateColorAndClose = React.useCallback(
78
+ (value: string) => {
79
+ updateColor(value);
80
+ onToggle();
81
+ },
82
+ [onToggle, updateColor]
83
+ );
84
+
85
+ const clearColor = React.useCallback(() => {
86
+ if (editor.selection) {
87
+ editor.tf.select(editor.selection);
88
+ editor.tf.focus();
89
+
90
+ if (selectedColor) {
91
+ editor.tf.removeMarks(nodeType);
92
+ }
93
+
94
+ onToggle();
95
+ }
96
+ }, [editor, selectedColor, onToggle, nodeType]);
97
+
98
+ React.useEffect(() => {
99
+ if (selectionDefined) {
100
+ setSelectedColor(color);
101
+ }
102
+ }, [color, selectionDefined]);
103
+
104
+ return (
105
+ <DropdownMenu
106
+ open={open}
107
+ onOpenChange={(value) => {
108
+ setOpen(value);
109
+ }}
110
+ modal={false}
111
+ >
112
+ <DropdownMenuTrigger asChild>
113
+ <ToolbarButton pressed={open} tooltip={tooltip}>
114
+ {children}
115
+ </ToolbarButton>
116
+ </DropdownMenuTrigger>
117
+
118
+ <DropdownMenuContent align="start">
119
+ <ColorPicker
120
+ color={selectedColor || color}
121
+ clearColor={clearColor}
122
+ colors={DEFAULT_COLORS}
123
+ customColors={DEFAULT_CUSTOM_COLORS}
124
+ updateColor={updateColorAndClose}
125
+ updateCustomColor={updateColor}
126
+ />
127
+ </DropdownMenuContent>
128
+ </DropdownMenu>
129
+ );
130
+ }
131
+
132
+ function PureColorPicker({
133
+ className,
134
+ clearColor,
135
+ color,
136
+ colors,
137
+ customColors,
138
+ updateColor,
139
+ updateCustomColor,
140
+ ...props
141
+ }: React.ComponentProps<'div'> & {
142
+ colors: TColor[];
143
+ customColors: TColor[];
144
+ clearColor: () => void;
145
+ updateColor: (color: string) => void;
146
+ updateCustomColor: (color: string) => void;
147
+ color?: string;
148
+ }) {
149
+ return (
150
+ <div className={cn('flex flex-col', className)} {...props}>
151
+ <ToolbarMenuGroup label="Custom Colors">
152
+ <ColorCustom
153
+ color={color}
154
+ className="px-2"
155
+ colors={colors}
156
+ customColors={customColors}
157
+ updateColor={updateColor}
158
+ updateCustomColor={updateCustomColor}
159
+ />
160
+ </ToolbarMenuGroup>
161
+ <ToolbarMenuGroup label="Default Colors">
162
+ <ColorDropdownMenuItems
163
+ color={color}
164
+ className="px-2"
165
+ colors={colors}
166
+ updateColor={updateColor}
167
+ />
168
+ </ToolbarMenuGroup>
169
+ {color && (
170
+ <ToolbarMenuGroup>
171
+ <DropdownMenuItem className="p-2" onClick={clearColor}>
172
+ <EraserIcon />
173
+ <span>Clear</span>
174
+ </DropdownMenuItem>
175
+ </ToolbarMenuGroup>
176
+ )}
177
+ </div>
178
+ );
179
+ }
180
+
181
+ const ColorPicker = React.memo(
182
+ PureColorPicker,
183
+ (prev, next) =>
184
+ prev.color === next.color &&
185
+ prev.colors === next.colors &&
186
+ prev.customColors === next.customColors
187
+ );
188
+
189
+ function ColorCustom({
190
+ className,
191
+ color,
192
+ colors,
193
+ customColors,
194
+ updateColor,
195
+ updateCustomColor,
196
+ ...props
197
+ }: {
198
+ colors: TColor[];
199
+ customColors: TColor[];
200
+ updateColor: (color: string) => void;
201
+ updateCustomColor: (color: string) => void;
202
+ color?: string;
203
+ } & React.ComponentPropsWithoutRef<'div'>) {
204
+ const [customColor, setCustomColor] = React.useState<string>();
205
+ const [value, setValue] = React.useState<string>(color || '#000000');
206
+
207
+ React.useEffect(() => {
208
+ if (
209
+ !color ||
210
+ customColors.some((c) => c.value === color) ||
211
+ colors.some((c) => c.value === color)
212
+ ) {
213
+ return;
214
+ }
215
+
216
+ setCustomColor(color);
217
+ }, [color, colors, customColors]);
218
+
219
+ const computedColors = React.useMemo(
220
+ () =>
221
+ customColor
222
+ ? [
223
+ ...customColors,
224
+ {
225
+ isBrightColor: false,
226
+ name: '',
227
+ value: customColor,
228
+ },
229
+ ]
230
+ : customColors,
231
+ [customColor, customColors]
232
+ );
233
+
234
+ // eslint-disable-next-line react-hooks/exhaustive-deps
235
+ const updateCustomColorDebounced = React.useCallback(
236
+ debounce(updateCustomColor, 100),
237
+ [updateCustomColor]
238
+ );
239
+
240
+ return (
241
+ <div className={cn('relative flex flex-col gap-4', className)} {...props}>
242
+ <ColorDropdownMenuItems
243
+ color={color}
244
+ colors={computedColors}
245
+ updateColor={updateColor}
246
+ >
247
+ <ColorInput
248
+ value={value}
249
+ onChange={(e) => {
250
+ setValue(e.target.value);
251
+ updateCustomColorDebounced(e.target.value);
252
+ }}
253
+ >
254
+ <DropdownMenuItem
255
+ className={cn(
256
+ buttonVariants({
257
+ size: 'icon',
258
+ variant: 'outline',
259
+ }),
260
+ 'absolute top-1 right-2 bottom-2 flex size-8 items-center justify-center rounded-full'
261
+ )}
262
+ onSelect={(e) => {
263
+ e.preventDefault();
264
+ }}
265
+ >
266
+ <span className="sr-only">Custom</span>
267
+ <PlusIcon />
268
+ </DropdownMenuItem>
269
+ </ColorInput>
270
+ </ColorDropdownMenuItems>
271
+ </div>
272
+ );
273
+ }
274
+
275
+ function ColorInput({
276
+ children,
277
+ className,
278
+ value = '#000000',
279
+ ...props
280
+ }: React.ComponentProps<'input'>) {
281
+ const inputRef = React.useRef<HTMLInputElement | null>(null);
282
+
283
+ return (
284
+ <div className="flex flex-col items-center">
285
+ {React.Children.map(children, (child) => {
286
+ if (!child) return child;
287
+
288
+ return React.cloneElement(
289
+ child as React.ReactElement<{
290
+ onClick: () => void;
291
+ }>,
292
+ {
293
+ onClick: () => inputRef.current?.click(),
294
+ }
295
+ );
296
+ })}
297
+ <input
298
+ {...props}
299
+ ref={useComposedRef(props.ref, inputRef)}
300
+ className={cn('size-0 overflow-hidden border-0 p-0', className)}
301
+ value={value}
302
+ type="color"
303
+ />
304
+ </div>
305
+ );
306
+ }
307
+
308
+ type TColor = {
309
+ isBrightColor: boolean;
310
+ name: string;
311
+ value: string;
312
+ };
313
+
314
+ function ColorDropdownMenuItem({
315
+ className,
316
+ isBrightColor,
317
+ isSelected,
318
+ name,
319
+ updateColor,
320
+ value,
321
+ ...props
322
+ }: {
323
+ isBrightColor: boolean;
324
+ isSelected: boolean;
325
+ value: string;
326
+ updateColor: (color: string) => void;
327
+ name?: string;
328
+ } & DropdownMenuItemProps) {
329
+ const content = (
330
+ <DropdownMenuItem
331
+ className={cn(
332
+ buttonVariants({
333
+ size: 'icon',
334
+ variant: 'outline',
335
+ }),
336
+ 'my-1 flex size-6 items-center justify-center rounded-full border border-muted border-solid p-0 transition-all hover:scale-125',
337
+ !isBrightColor && 'border-transparent',
338
+ isSelected && 'border-2 border-primary',
339
+ className
340
+ )}
341
+ style={{ backgroundColor: value }}
342
+ onSelect={(e) => {
343
+ e.preventDefault();
344
+ updateColor(value);
345
+ }}
346
+ {...props}
347
+ />
348
+ );
349
+
350
+ return name ? (
351
+ <Tooltip>
352
+ <TooltipTrigger>{content}</TooltipTrigger>
353
+ <TooltipContent className="mb-1 capitalize">{name}</TooltipContent>
354
+ </Tooltip>
355
+ ) : (
356
+ content
357
+ );
358
+ }
359
+
360
+ export function ColorDropdownMenuItems({
361
+ className,
362
+ color,
363
+ colors,
364
+ updateColor,
365
+ ...props
366
+ }: {
367
+ colors: TColor[];
368
+ updateColor: (color: string) => void;
369
+ color?: string;
370
+ } & React.ComponentProps<'div'>) {
371
+ return (
372
+ <div
373
+ className={cn(
374
+ 'grid grid-cols-[repeat(10,1fr)] place-items-center gap-x-1',
375
+ className
376
+ )}
377
+ {...props}
378
+ >
379
+ <TooltipProvider>
380
+ {colors.map(({ isBrightColor, name, value }) => (
381
+ <ColorDropdownMenuItem
382
+ name={name}
383
+ key={name ?? value}
384
+ value={value}
385
+ isBrightColor={isBrightColor}
386
+ isSelected={color === value}
387
+ updateColor={updateColor}
388
+ />
389
+ ))}
390
+ {props.children}
391
+ </TooltipProvider>
392
+ </div>
393
+ );
394
+ }
395
+
396
+ export const DEFAULT_COLORS = [
397
+ {
398
+ isBrightColor: false,
399
+ name: 'black',
400
+ value: '#000000',
401
+ },
402
+ {
403
+ isBrightColor: false,
404
+ name: 'dark grey 4',
405
+ value: '#434343',
406
+ },
407
+ {
408
+ isBrightColor: false,
409
+ name: 'dark grey 3',
410
+ value: '#666666',
411
+ },
412
+ {
413
+ isBrightColor: false,
414
+ name: 'dark grey 2',
415
+ value: '#999999',
416
+ },
417
+ {
418
+ isBrightColor: false,
419
+ name: 'dark grey 1',
420
+ value: '#B7B7B7',
421
+ },
422
+ {
423
+ isBrightColor: false,
424
+ name: 'grey',
425
+ value: '#CCCCCC',
426
+ },
427
+ {
428
+ isBrightColor: false,
429
+ name: 'light grey 1',
430
+ value: '#D9D9D9',
431
+ },
432
+ {
433
+ isBrightColor: true,
434
+ name: 'light grey 2',
435
+ value: '#EFEFEF',
436
+ },
437
+ {
438
+ isBrightColor: true,
439
+ name: 'light grey 3',
440
+ value: '#F3F3F3',
441
+ },
442
+ {
443
+ isBrightColor: true,
444
+ name: 'white',
445
+ value: '#FFFFFF',
446
+ },
447
+ {
448
+ isBrightColor: false,
449
+ name: 'red berry',
450
+ value: '#980100',
451
+ },
452
+ {
453
+ isBrightColor: false,
454
+ name: 'red',
455
+ value: '#FE0000',
456
+ },
457
+ {
458
+ isBrightColor: false,
459
+ name: 'orange',
460
+ value: '#FE9900',
461
+ },
462
+ {
463
+ isBrightColor: true,
464
+ name: 'yellow',
465
+ value: '#FEFF00',
466
+ },
467
+ {
468
+ isBrightColor: false,
469
+ name: 'green',
470
+ value: '#00FF00',
471
+ },
472
+ {
473
+ isBrightColor: false,
474
+ name: 'cyan',
475
+ value: '#00FFFF',
476
+ },
477
+ {
478
+ isBrightColor: false,
479
+ name: 'cornflower blue',
480
+ value: '#4B85E8',
481
+ },
482
+ {
483
+ isBrightColor: false,
484
+ name: 'blue',
485
+ value: '#1300FF',
486
+ },
487
+ {
488
+ isBrightColor: false,
489
+ name: 'purple',
490
+ value: '#9900FF',
491
+ },
492
+ {
493
+ isBrightColor: false,
494
+ name: 'magenta',
495
+ value: '#FF00FF',
496
+ },
497
+
498
+ {
499
+ isBrightColor: false,
500
+ name: 'light red berry 3',
501
+ value: '#E6B8AF',
502
+ },
503
+ {
504
+ isBrightColor: false,
505
+ name: 'light red 3',
506
+ value: '#F4CCCC',
507
+ },
508
+ {
509
+ isBrightColor: true,
510
+ name: 'light orange 3',
511
+ value: '#FCE4CD',
512
+ },
513
+ {
514
+ isBrightColor: true,
515
+ name: 'light yellow 3',
516
+ value: '#FFF2CC',
517
+ },
518
+ {
519
+ isBrightColor: true,
520
+ name: 'light green 3',
521
+ value: '#D9EAD3',
522
+ },
523
+ {
524
+ isBrightColor: false,
525
+ name: 'light cyan 3',
526
+ value: '#D0DFE3',
527
+ },
528
+ {
529
+ isBrightColor: false,
530
+ name: 'light cornflower blue 3',
531
+ value: '#C9DAF8',
532
+ },
533
+ {
534
+ isBrightColor: true,
535
+ name: 'light blue 3',
536
+ value: '#CFE1F3',
537
+ },
538
+ {
539
+ isBrightColor: true,
540
+ name: 'light purple 3',
541
+ value: '#D9D2E9',
542
+ },
543
+ {
544
+ isBrightColor: true,
545
+ name: 'light magenta 3',
546
+ value: '#EAD1DB',
547
+ },
548
+
549
+ {
550
+ isBrightColor: false,
551
+ name: 'light red berry 2',
552
+ value: '#DC7E6B',
553
+ },
554
+ {
555
+ isBrightColor: false,
556
+ name: 'light red 2',
557
+ value: '#EA9999',
558
+ },
559
+ {
560
+ isBrightColor: false,
561
+ name: 'light orange 2',
562
+ value: '#F9CB9C',
563
+ },
564
+ {
565
+ isBrightColor: true,
566
+ name: 'light yellow 2',
567
+ value: '#FFE598',
568
+ },
569
+ {
570
+ isBrightColor: false,
571
+ name: 'light green 2',
572
+ value: '#B7D6A8',
573
+ },
574
+ {
575
+ isBrightColor: false,
576
+ name: 'light cyan 2',
577
+ value: '#A1C4C9',
578
+ },
579
+ {
580
+ isBrightColor: false,
581
+ name: 'light cornflower blue 2',
582
+ value: '#A4C2F4',
583
+ },
584
+ {
585
+ isBrightColor: false,
586
+ name: 'light blue 2',
587
+ value: '#9FC5E8',
588
+ },
589
+ {
590
+ isBrightColor: false,
591
+ name: 'light purple 2',
592
+ value: '#B5A7D5',
593
+ },
594
+ {
595
+ isBrightColor: false,
596
+ name: 'light magenta 2',
597
+ value: '#D5A6BD',
598
+ },
599
+
600
+ {
601
+ isBrightColor: false,
602
+ name: 'light red berry 1',
603
+ value: '#CC4125',
604
+ },
605
+ {
606
+ isBrightColor: false,
607
+ name: 'light red 1',
608
+ value: '#E06666',
609
+ },
610
+ {
611
+ isBrightColor: false,
612
+ name: 'light orange 1',
613
+ value: '#F6B26B',
614
+ },
615
+ {
616
+ isBrightColor: false,
617
+ name: 'light yellow 1',
618
+ value: '#FFD966',
619
+ },
620
+ {
621
+ isBrightColor: false,
622
+ name: 'light green 1',
623
+ value: '#93C47D',
624
+ },
625
+ {
626
+ isBrightColor: false,
627
+ name: 'light cyan 1',
628
+ value: '#76A5AE',
629
+ },
630
+ {
631
+ isBrightColor: false,
632
+ name: 'light cornflower blue 1',
633
+ value: '#6C9EEB',
634
+ },
635
+ {
636
+ isBrightColor: false,
637
+ name: 'light blue 1',
638
+ value: '#6FA8DC',
639
+ },
640
+ {
641
+ isBrightColor: false,
642
+ name: 'light purple 1',
643
+ value: '#8D7CC3',
644
+ },
645
+ {
646
+ isBrightColor: false,
647
+ name: 'light magenta 1',
648
+ value: '#C27BA0',
649
+ },
650
+
651
+ {
652
+ isBrightColor: false,
653
+ name: 'dark red berry 1',
654
+ value: '#A61B00',
655
+ },
656
+ {
657
+ isBrightColor: false,
658
+ name: 'dark red 1',
659
+ value: '#CC0000',
660
+ },
661
+ {
662
+ isBrightColor: false,
663
+ name: 'dark orange 1',
664
+ value: '#E59138',
665
+ },
666
+ {
667
+ isBrightColor: false,
668
+ name: 'dark yellow 1',
669
+ value: '#F1C231',
670
+ },
671
+ {
672
+ isBrightColor: false,
673
+ name: 'dark green 1',
674
+ value: '#6AA74F',
675
+ },
676
+ {
677
+ isBrightColor: false,
678
+ name: 'dark cyan 1',
679
+ value: '#45818E',
680
+ },
681
+ {
682
+ isBrightColor: false,
683
+ name: 'dark cornflower blue 1',
684
+ value: '#3B78D8',
685
+ },
686
+ {
687
+ isBrightColor: false,
688
+ name: 'dark blue 1',
689
+ value: '#3E84C6',
690
+ },
691
+ {
692
+ isBrightColor: false,
693
+ name: 'dark purple 1',
694
+ value: '#664EA6',
695
+ },
696
+ {
697
+ isBrightColor: false,
698
+ name: 'dark magenta 1',
699
+ value: '#A64D78',
700
+ },
701
+
702
+ {
703
+ isBrightColor: false,
704
+ name: 'dark red berry 2',
705
+ value: '#84200D',
706
+ },
707
+ {
708
+ isBrightColor: false,
709
+ name: 'dark red 2',
710
+ value: '#990001',
711
+ },
712
+ {
713
+ isBrightColor: false,
714
+ name: 'dark orange 2',
715
+ value: '#B45F05',
716
+ },
717
+ {
718
+ isBrightColor: false,
719
+ name: 'dark yellow 2',
720
+ value: '#BF9002',
721
+ },
722
+ {
723
+ isBrightColor: false,
724
+ name: 'dark green 2',
725
+ value: '#38761D',
726
+ },
727
+ {
728
+ isBrightColor: false,
729
+ name: 'dark cyan 2',
730
+ value: '#124F5C',
731
+ },
732
+ {
733
+ isBrightColor: false,
734
+ name: 'dark cornflower blue 2',
735
+ value: '#1155CB',
736
+ },
737
+ {
738
+ isBrightColor: false,
739
+ name: 'dark blue 2',
740
+ value: '#0C5394',
741
+ },
742
+ {
743
+ isBrightColor: false,
744
+ name: 'dark purple 2',
745
+ value: '#351C75',
746
+ },
747
+ {
748
+ isBrightColor: false,
749
+ name: 'dark magenta 2',
750
+ value: '#741B47',
751
+ },
752
+
753
+ {
754
+ isBrightColor: false,
755
+ name: 'dark red berry 3',
756
+ value: '#5B0F00',
757
+ },
758
+ {
759
+ isBrightColor: false,
760
+ name: 'dark red 3',
761
+ value: '#660000',
762
+ },
763
+ {
764
+ isBrightColor: false,
765
+ name: 'dark orange 3',
766
+ value: '#783F04',
767
+ },
768
+ {
769
+ isBrightColor: false,
770
+ name: 'dark yellow 3',
771
+ value: '#7E6000',
772
+ },
773
+ {
774
+ isBrightColor: false,
775
+ name: 'dark green 3',
776
+ value: '#274E12',
777
+ },
778
+ {
779
+ isBrightColor: false,
780
+ name: 'dark cyan 3',
781
+ value: '#0D343D',
782
+ },
783
+ {
784
+ isBrightColor: false,
785
+ name: 'dark cornflower blue 3',
786
+ value: '#1B4487',
787
+ },
788
+ {
789
+ isBrightColor: false,
790
+ name: 'dark blue 3',
791
+ value: '#083763',
792
+ },
793
+ {
794
+ isBrightColor: false,
795
+ name: 'dark purple 3',
796
+ value: '#1F124D',
797
+ },
798
+ {
799
+ isBrightColor: false,
800
+ name: 'dark magenta 3',
801
+ value: '#4C1130',
802
+ },
803
+ ];
804
+
805
+ const DEFAULT_CUSTOM_COLORS = [
806
+ {
807
+ isBrightColor: false,
808
+ name: 'dark orange 3',
809
+ value: '#783F04',
810
+ },
811
+ {
812
+ isBrightColor: false,
813
+ name: 'dark grey 3',
814
+ value: '#666666',
815
+ },
816
+ {
817
+ isBrightColor: false,
818
+ name: 'dark grey 2',
819
+ value: '#999999',
820
+ },
821
+ {
822
+ isBrightColor: false,
823
+ name: 'light cornflower blue 1',
824
+ value: '#6C9EEB',
825
+ },
826
+ {
827
+ isBrightColor: false,
828
+ name: 'dark magenta 3',
829
+ value: '#4C1130',
830
+ },
831
+ ];