@open-cloud-initiative/editor-x 0.0.1

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 (190) hide show
  1. package/.devcontainer/Dockerfile +13 -0
  2. package/.devcontainer/devcontainer.json +52 -0
  3. package/.github/dependabot.yml +10 -0
  4. package/.github/workflows/pages.yml +42 -0
  5. package/.github/workflows/publish.yml +41 -0
  6. package/.vscode/settings.json +7 -0
  7. package/LICENSE +9 -0
  8. package/README.md +9 -0
  9. package/app/_component/editor.tsx +383 -0
  10. package/app/layout.tsx +46 -0
  11. package/app/page.tsx +11 -0
  12. package/app/r/registry.json/route.ts +22 -0
  13. package/components/editorx/editor.tsx +1794 -0
  14. package/components/editorx/extensions/floating-menu.tsx +376 -0
  15. package/components/editorx/extensions/floating-toolbar.tsx +97 -0
  16. package/components/editorx/extensions/image-placeholder.tsx +316 -0
  17. package/components/editorx/extensions/image.tsx +462 -0
  18. package/components/editorx/extensions/search-and-replace.tsx +438 -0
  19. package/components/editorx/rich-text-editor.tsx +383 -0
  20. package/components/editorx/tiptap.css +421 -0
  21. package/components/editorx/toolbars/alignment.tsx +126 -0
  22. package/components/editorx/toolbars/blockquote.tsx +47 -0
  23. package/components/editorx/toolbars/bold.tsx +48 -0
  24. package/components/editorx/toolbars/bullet-list.tsx +48 -0
  25. package/components/editorx/toolbars/code-block.tsx +47 -0
  26. package/components/editorx/toolbars/code.tsx +43 -0
  27. package/components/editorx/toolbars/color-and-highlight.tsx +215 -0
  28. package/components/editorx/toolbars/editor-toolbar.tsx +77 -0
  29. package/components/editorx/toolbars/hard-break.tsx +46 -0
  30. package/components/editorx/toolbars/headings.tsx +97 -0
  31. package/components/editorx/toolbars/horizontal-rule.tsx +42 -0
  32. package/components/editorx/toolbars/image-placeholder-toolbar.tsx +47 -0
  33. package/components/editorx/toolbars/italic.tsx +48 -0
  34. package/components/editorx/toolbars/link.tsx +130 -0
  35. package/components/editorx/toolbars/mobile-toolbar-group.tsx +76 -0
  36. package/components/editorx/toolbars/ordered-list.tsx +47 -0
  37. package/components/editorx/toolbars/redo.tsx +44 -0
  38. package/components/editorx/toolbars/strikethrough.tsx +48 -0
  39. package/components/editorx/toolbars/toolbar-provider.tsx +29 -0
  40. package/components/editorx/toolbars/underline.tsx +48 -0
  41. package/components/editorx/toolbars/undo.tsx +43 -0
  42. package/components/layout/theme-switcher.tsx +26 -0
  43. package/components/main-nav.tsx +24 -0
  44. package/components/mobile-nav.tsx +46 -0
  45. package/components/open-in-v0-button.tsx +38 -0
  46. package/components/page-header.tsx +30 -0
  47. package/components/site-footer.tsx +41 -0
  48. package/components/site-header.tsx +32 -0
  49. package/components/theme-provider.tsx +8 -0
  50. package/components/ui/button.tsx +57 -0
  51. package/components/ui/checkbox.tsx +30 -0
  52. package/components/ui/collapsible.tsx +11 -0
  53. package/components/ui/command.tsx +148 -0
  54. package/components/ui/dialog.tsx +122 -0
  55. package/components/ui/drawer.tsx +118 -0
  56. package/components/ui/dropdown-menu.tsx +201 -0
  57. package/components/ui/input.tsx +22 -0
  58. package/components/ui/label.tsx +26 -0
  59. package/components/ui/popover.tsx +33 -0
  60. package/components/ui/resizable.tsx +40 -0
  61. package/components/ui/scroll-area.tsx +42 -0
  62. package/components/ui/separator.tsx +31 -0
  63. package/components/ui/sheet.tsx +140 -0
  64. package/components/ui/sidebar.tsx +763 -0
  65. package/components/ui/skeleton.tsx +15 -0
  66. package/components/ui/spinner.tsx +29 -0
  67. package/components/ui/tabs.tsx +55 -0
  68. package/components/ui/toggle-group.tsx +61 -0
  69. package/components/ui/toggle.tsx +45 -0
  70. package/components/ui/tooltip.tsx +32 -0
  71. package/components.json +21 -0
  72. package/config/site.ts +15 -0
  73. package/eslint.config.mjs +20 -0
  74. package/hooks/use-character-limit.ts +28 -0
  75. package/hooks/use-copy-to-clipboard.ts +16 -0
  76. package/hooks/use-debounce.ts +17 -0
  77. package/hooks/use-image-upload.ts +97 -0
  78. package/hooks/use-media-querry.ts +18 -0
  79. package/hooks/use-mobile.tsx +19 -0
  80. package/images/editor.png +0 -0
  81. package/lib/content.ts +39 -0
  82. package/lib/cookie-client.ts +19 -0
  83. package/lib/localstorage-client.ts +19 -0
  84. package/lib/package.ts +144 -0
  85. package/lib/preferences-config.ts +72 -0
  86. package/lib/preferences-storage.ts +20 -0
  87. package/lib/theme-utils.ts +12 -0
  88. package/lib/theme.ts +50 -0
  89. package/lib/tiptap-utils.ts +45 -0
  90. package/lib/utils.ts +11 -0
  91. package/next-env.d.ts +6 -0
  92. package/next.config.mjs +11 -0
  93. package/package.json +92 -0
  94. package/postcss.config.mjs +8 -0
  95. package/prettier.config.mjs +15 -0
  96. package/public/android-chrome-192x192.png +0 -0
  97. package/public/android-chrome-512x512.png +0 -0
  98. package/public/apple-touch-icon.png +0 -0
  99. package/public/favicon-16x16.png +0 -0
  100. package/public/favicon-32x32.png +0 -0
  101. package/public/favicon.ico +0 -0
  102. package/public/file.svg +1 -0
  103. package/public/globe.svg +1 -0
  104. package/public/next.svg +1 -0
  105. package/public/og.webp +0 -0
  106. package/public/r/editor-x.json +85 -0
  107. package/public/r/registry.json +93 -0
  108. package/public/site.webmanifest +19 -0
  109. package/public/vercel.svg +1 -0
  110. package/public/window.svg +1 -0
  111. package/registry/editor/components/editor.tsx +1794 -0
  112. package/registry/editor/components/extensions/floating-menu.tsx +376 -0
  113. package/registry/editor/components/extensions/floating-toolbar.tsx +97 -0
  114. package/registry/editor/components/extensions/image-placeholder.tsx +316 -0
  115. package/registry/editor/components/extensions/image.tsx +462 -0
  116. package/registry/editor/components/extensions/search-and-replace.tsx +438 -0
  117. package/registry/editor/components/rich-text-editor.tsx +383 -0
  118. package/registry/editor/components/tiptap.css +421 -0
  119. package/registry/editor/components/toolbars/alignment.tsx +126 -0
  120. package/registry/editor/components/toolbars/blockquote.tsx +47 -0
  121. package/registry/editor/components/toolbars/bold.tsx +48 -0
  122. package/registry/editor/components/toolbars/bullet-list.tsx +48 -0
  123. package/registry/editor/components/toolbars/code-block.tsx +47 -0
  124. package/registry/editor/components/toolbars/code.tsx +43 -0
  125. package/registry/editor/components/toolbars/color-and-highlight.tsx +215 -0
  126. package/registry/editor/components/toolbars/editor-toolbar.tsx +77 -0
  127. package/registry/editor/components/toolbars/hard-break.tsx +46 -0
  128. package/registry/editor/components/toolbars/headings.tsx +97 -0
  129. package/registry/editor/components/toolbars/horizontal-rule.tsx +42 -0
  130. package/registry/editor/components/toolbars/image-placeholder-toolbar.tsx +47 -0
  131. package/registry/editor/components/toolbars/italic.tsx +48 -0
  132. package/registry/editor/components/toolbars/link.tsx +130 -0
  133. package/registry/editor/components/toolbars/mobile-toolbar-group.tsx +76 -0
  134. package/registry/editor/components/toolbars/ordered-list.tsx +47 -0
  135. package/registry/editor/components/toolbars/redo.tsx +44 -0
  136. package/registry/editor/components/toolbars/strikethrough.tsx +48 -0
  137. package/registry/editor/components/toolbars/toolbar-provider.tsx +29 -0
  138. package/registry/editor/components/toolbars/underline.tsx +48 -0
  139. package/registry/editor/components/toolbars/undo.tsx +43 -0
  140. package/registry/editor/components/ui/button.tsx +57 -0
  141. package/registry/editor/components/ui/checkbox.tsx +30 -0
  142. package/registry/editor/components/ui/collapsible.tsx +11 -0
  143. package/registry/editor/components/ui/command.tsx +148 -0
  144. package/registry/editor/components/ui/dialog.tsx +122 -0
  145. package/registry/editor/components/ui/drawer.tsx +118 -0
  146. package/registry/editor/components/ui/dropdown-menu.tsx +201 -0
  147. package/registry/editor/components/ui/input.tsx +22 -0
  148. package/registry/editor/components/ui/label.tsx +26 -0
  149. package/registry/editor/components/ui/popover.tsx +33 -0
  150. package/registry/editor/components/ui/resizable.tsx +40 -0
  151. package/registry/editor/components/ui/scroll-area.tsx +42 -0
  152. package/registry/editor/components/ui/separator.tsx +31 -0
  153. package/registry/editor/components/ui/sheet.tsx +140 -0
  154. package/registry/editor/components/ui/sidebar.tsx +763 -0
  155. package/registry/editor/components/ui/skeleton.tsx +15 -0
  156. package/registry/editor/components/ui/spinner.tsx +29 -0
  157. package/registry/editor/components/ui/tabs.tsx +55 -0
  158. package/registry/editor/components/ui/toggle-group.tsx +61 -0
  159. package/registry/editor/components/ui/toggle.tsx +45 -0
  160. package/registry/editor/components/ui/tooltip.tsx +32 -0
  161. package/registry/editor/hooks/use-character-limit.ts +28 -0
  162. package/registry/editor/hooks/use-copy-to-clipboard.ts +16 -0
  163. package/registry/editor/hooks/use-debounce.ts +17 -0
  164. package/registry/editor/hooks/use-image-upload.ts +97 -0
  165. package/registry/editor/hooks/use-media-querry.ts +18 -0
  166. package/registry/editor/hooks/use-mobile.tsx +19 -0
  167. package/registry/editor/lib/content.ts +39 -0
  168. package/registry/editor/lib/cookie-client.ts +19 -0
  169. package/registry/editor/lib/localstorage-client.ts +19 -0
  170. package/registry/editor/lib/package.ts +144 -0
  171. package/registry/editor/lib/preferences-config.ts +72 -0
  172. package/registry/editor/lib/preferences-storage.ts +20 -0
  173. package/registry/editor/lib/theme-utils.ts +12 -0
  174. package/registry/editor/lib/theme.ts +50 -0
  175. package/registry/editor/lib/tiptap-utils.ts +45 -0
  176. package/registry/editor/lib/utils.ts +11 -0
  177. package/registry/editor/page.tsx +9 -0
  178. package/registry.json +93 -0
  179. package/reset.d.ts +1 -0
  180. package/scripts/generate-theme-presets.ts +128 -0
  181. package/scripts/postCreateCommand.sh +0 -0
  182. package/scripts/theme-boot.tsx +105 -0
  183. package/server/server-actions.ts +27 -0
  184. package/stores/preferences/preferences-provider.tsx +55 -0
  185. package/stores/preferences/preferences-store.ts +23 -0
  186. package/styles/globals.css +288 -0
  187. package/styles/presets/brutalist.css +89 -0
  188. package/styles/presets/soft-pop.css +89 -0
  189. package/styles/presets/tangerine.css +89 -0
  190. package/tsconfig.json +50 -0
@@ -0,0 +1,421 @@
1
+
2
+ :root {
3
+ /* Color System */
4
+ --editor-text-default: hsl(240 10% 3.9%);
5
+ --editor-text-gray: hsl(240 3.8% 46.1%);
6
+ --editor-text-brown: hsl(25 95% 53%);
7
+ --editor-text-orange: hsl(24 95% 53%);
8
+ --editor-text-yellow: hsl(48 96% 53%);
9
+ --editor-text-green: hsl(142 71% 45%);
10
+ --editor-text-blue: hsl(221 83% 53%);
11
+ --editor-text-purple: hsl(269 97% 85%);
12
+ --editor-text-pink: hsl(336 80% 58%);
13
+ --editor-text-red: hsl(0 84% 60%);
14
+
15
+ /* Background Colors */
16
+ --editor-bg-default: hsl(0 0% 100%);
17
+ --editor-bg-subtle: hsl(0 0% 98%);
18
+ --editor-bg-muted: hsl(240 5% 96%);
19
+
20
+ /* Highlight Colors */
21
+ --editor-highlight-default: hsl(0 0% 98%);
22
+ --editor-highlight-gray: hsl(240 5% 96%);
23
+ --editor-highlight-brown: hsl(43 96% 96%);
24
+ --editor-highlight-orange: hsl(33 100% 96%);
25
+ --editor-highlight-yellow: hsl(54 100% 96%);
26
+ --editor-highlight-green: hsl(142 71% 96%);
27
+ --editor-highlight-blue: hsl(217 91% 96%);
28
+ --editor-highlight-purple: hsl(269 97% 96%);
29
+ --editor-highlight-pink: hsl(336 80% 96%);
30
+ --editor-highlight-red: hsl(0 84% 96%);
31
+
32
+ /* Border Colors */
33
+ --editor-border-default: hsl(240 5% 88%);
34
+ --editor-border-strong: hsl(240 5% 65%);
35
+
36
+ /* Spacing System */
37
+ --editor-spacing-1: 0.25rem;
38
+ --editor-spacing-2: 0.5rem;
39
+ --editor-spacing-3: 0.75rem;
40
+ --editor-spacing-4: 1rem;
41
+ --editor-spacing-6: 1.5rem;
42
+ --editor-spacing-8: 2rem;
43
+ --editor-spacing-12: 3rem;
44
+ --editor-spacing-16: 4rem;
45
+
46
+ /* Typography */
47
+ --editor-font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
48
+ --editor-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
49
+ --editor-font-serif: Georgia, Cambria, "Times New Roman", Times, serif;
50
+
51
+ /* Animation */
52
+ --editor-transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
53
+ --editor-transition-normal: 200ms cubic-bezier(0.4, 0, 0.2, 1);
54
+ --editor-transition-slow: 300ms cubic-bezier(0.4, 0, 0.2, 1);
55
+
56
+ /* Shadows */
57
+ --editor-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
58
+ --editor-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
59
+ --editor-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
60
+ }
61
+
62
+ /* Dark Mode Custom Properties */
63
+ .dark {
64
+ --editor-text-default: hsl(0 0% 98%);
65
+ --editor-text-gray: hsl(240 5% 64.9%);
66
+ --editor-text-brown: hsl(25 95% 53%);
67
+ --editor-text-orange: hsl(24 95% 53%);
68
+ --editor-text-yellow: hsl(48 96% 53%);
69
+ --editor-text-green: hsl(142 71% 45%);
70
+ --editor-text-blue: hsl(221 83% 53%);
71
+ --editor-text-purple: hsl(269 97% 85%);
72
+ --editor-text-pink: hsl(336 80% 58%);
73
+ --editor-text-red: hsl(0 84% 60%);
74
+
75
+ --editor-bg-default: hsl(240 10% 3.9%);
76
+ --editor-bg-subtle: hsl(240 3.7% 15.9%);
77
+ --editor-bg-muted: hsl(240 5% 26%);
78
+
79
+ --editor-highlight-default: hsl(240 3.7% 15.9%);
80
+ --editor-highlight-gray: hsl(240 5% 26%);
81
+ --editor-highlight-brown: hsl(43 96% 10%);
82
+ --editor-highlight-orange: hsl(33 100% 10%);
83
+ --editor-highlight-yellow: hsl(54 100% 10%);
84
+ --editor-highlight-green: hsl(142 71% 10%);
85
+ --editor-highlight-blue: hsl(217 91% 10%);
86
+ --editor-highlight-purple: hsl(269 97% 10%);
87
+ --editor-highlight-pink: hsl(336 80% 10%);
88
+ --editor-highlight-red: hsl(0 84% 10%);
89
+
90
+ --editor-border-default: hsl(240 5% 26%);
91
+ --editor-border-strong: hsl(240 5% 64.9%);
92
+ }
93
+
94
+ /* Core Editor Styles */
95
+ .ProseMirror {
96
+ caret-color: var(--editor-text-default);
97
+ outline: none;
98
+ padding: var(--editor-spacing-16) var(--editor-spacing-8);
99
+ margin: 0 auto;
100
+ max-width: 90ch;
101
+ font-family: var(--editor-font-sans);
102
+ position: relative;
103
+ /* background-color: var(--editor-bg-default); */
104
+ color: var(--editor-text-default);
105
+ transition: all var(--editor-transition-normal);
106
+ min-height: 100vh;
107
+ text-rendering: optimizeLegibility;
108
+ -webkit-font-smoothing: antialiased;
109
+ -moz-osx-font-smoothing: grayscale;
110
+ }
111
+
112
+ .ProseMirror:focus {
113
+ outline: none;
114
+ box-shadow: none;
115
+ }
116
+
117
+ .ProseMirror .selection,
118
+ .ProseMirror *::selection {
119
+ background-color: var(--editor-highlight-blue);
120
+ /* color: var(--editor-text-default); */
121
+ }
122
+
123
+ .ProseMirror > .react-renderer {
124
+ margin: var(--editor-spacing-12) 0;
125
+ transition: all var(--editor-transition-normal);
126
+ }
127
+
128
+ .ProseMirror > .react-renderer:first-child {
129
+ margin-top: 0;
130
+ }
131
+
132
+ .ProseMirror > .react-renderer:last-child {
133
+ margin-bottom: 0;
134
+ }
135
+
136
+ /* Typography Styles */
137
+ .ProseMirror p {
138
+ line-height: 1.75;
139
+ margin: var(--editor-spacing-4) 0;
140
+ color: var(--editor-text-default);
141
+ font-size: 1.125rem;
142
+ }
143
+
144
+ .ProseMirror > p {
145
+ margin: var(--editor-spacing-6) 0;
146
+ }
147
+
148
+ .ProseMirror h1,
149
+ .ProseMirror h2,
150
+ .ProseMirror h3,
151
+ .ProseMirror h4 {
152
+ font-family: var(--editor-font-sans);
153
+ font-weight: 700;
154
+ letter-spacing: -0.025em;
155
+ color: var(--editor-text-default);
156
+ scroll-margin-top: var(--editor-spacing-16);
157
+ line-height: 1.2;
158
+ }
159
+
160
+ .ProseMirror h1 {
161
+ font-size: 2.5rem;
162
+ margin: var(--editor-spacing-8) 0 var(--editor-spacing-4);
163
+ }
164
+
165
+ .ProseMirror h2 {
166
+ font-size: 2rem;
167
+ margin: var(--editor-spacing-8) 0 var(--editor-spacing-4);
168
+ }
169
+
170
+ .ProseMirror h3 {
171
+ font-size: 1.5rem;
172
+ margin: var(--editor-spacing-6) 0 var(--editor-spacing-3);
173
+ }
174
+
175
+ .ProseMirror h4 {
176
+ font-size: 1.25rem;
177
+ margin: var(--editor-spacing-4) 0 var(--editor-spacing-2);
178
+ }
179
+
180
+ .ProseMirror a {
181
+ color: var(--editor-text-blue);
182
+ cursor: pointer;
183
+ text-decoration: underline;
184
+ text-decoration-thickness: 0.1em;
185
+ text-underline-offset: 0.2em;
186
+ transition: all var(--editor-transition-fast);
187
+ }
188
+
189
+ .ProseMirror a:hover {
190
+ color: var(--editor-text-blue);
191
+ text-decoration-thickness: 0.2em;
192
+ }
193
+
194
+ .ProseMirror code {
195
+ font-family: var(--editor-font-mono);
196
+ font-size: 0.9em;
197
+ background-color: var(--editor-bg-muted);
198
+ padding: 0.2em 0.4em;
199
+ border-radius: 4px;
200
+ color: var(--editor-text-default);
201
+ border: 1px solid var(--editor-border-default);
202
+ }
203
+
204
+ .ProseMirror pre {
205
+ margin: var(--editor-spacing-6) 0;
206
+ padding: var(--editor-spacing-4);
207
+ background-color: var(--editor-bg-subtle);
208
+ border-radius: 8px;
209
+ overflow-x: auto;
210
+ border: 1px solid var(--editor-border-default);
211
+ }
212
+
213
+ .ProseMirror pre code {
214
+ background-color: transparent;
215
+ padding: 0;
216
+ border: none;
217
+ font-size: 0.875rem;
218
+ line-height: 1.7;
219
+ color: var(--editor-text-default);
220
+ }
221
+
222
+ .ProseMirror blockquote {
223
+ margin: var(--editor-spacing-6) 0;
224
+ padding: var(--editor-spacing-4) var(--editor-spacing-6);
225
+ border-left: 4px solid var(--editor-border-strong);
226
+ font-style: italic;
227
+ color: var(--editor-text-gray);
228
+ background-color: var(--editor-bg-subtle);
229
+ border-radius: 0 8px 8px 0;
230
+ }
231
+
232
+ /* Lists */
233
+ .ProseMirror ul,
234
+ .ProseMirror ol {
235
+ margin: var(--editor-spacing-4) 0;
236
+ padding-left: var(--editor-spacing-6);
237
+ }
238
+
239
+ .ProseMirror li {
240
+ margin: var(--editor-spacing-2) 0;
241
+ padding-left: var(--editor-spacing-2);
242
+ }
243
+
244
+ .ProseMirror ul {
245
+ list-style-type: disc;
246
+ }
247
+
248
+ .ProseMirror ul ul {
249
+ list-style-type: circle;
250
+ }
251
+
252
+ .ProseMirror ul ul ul {
253
+ list-style-type: square;
254
+ }
255
+
256
+ .ProseMirror ol {
257
+ list-style-type: decimal;
258
+ }
259
+
260
+ .ProseMirror ol ol {
261
+ list-style-type: lower-alpha;
262
+ }
263
+
264
+ .ProseMirror ol ol ol {
265
+ list-style-type: lower-roman;
266
+ }
267
+
268
+ /* Tables */
269
+ .ProseMirror table {
270
+ width: 100%;
271
+ border-collapse: separate;
272
+ border-spacing: 0;
273
+ margin: var(--editor-spacing-6) 0;
274
+ border: 1px solid var(--editor-border-default);
275
+ border-radius: 8px;
276
+ overflow: hidden;
277
+ }
278
+
279
+ .ProseMirror th {
280
+ background-color: var(--editor-bg-subtle);
281
+ font-weight: 600;
282
+ text-align: left;
283
+ padding: var(--editor-spacing-3) var(--editor-spacing-4);
284
+ border-bottom: 2px solid var(--editor-border-default);
285
+ }
286
+
287
+ .ProseMirror td {
288
+ padding: var(--editor-spacing-3) var(--editor-spacing-4);
289
+ border-bottom: 1px solid var(--editor-border-default);
290
+ transition: background-color var(--editor-transition-fast);
291
+ }
292
+
293
+ .ProseMirror tr:last-child td {
294
+ border-bottom: none;
295
+ }
296
+
297
+ .ProseMirror tr:hover td {
298
+ background-color: var(--editor-bg-subtle);
299
+ }
300
+
301
+ /* Images */
302
+ .ProseMirror img {
303
+ max-width: 100%;
304
+ height: auto;
305
+ border-radius: 8px;
306
+ border: 1px solid var(--editor-border-default);
307
+ box-shadow: var(--editor-shadow-sm);
308
+ transition: all var(--editor-transition-normal);
309
+ display: block;
310
+ margin: var(--editor-spacing-1) auto;
311
+ }
312
+
313
+ .ProseMirror img:hover {
314
+ box-shadow: var(--editor-shadow-lg);
315
+ transform: translateY(-2px);
316
+ }
317
+
318
+ /* Horizontal Rule */
319
+ .ProseMirror hr {
320
+ margin: var(--editor-spacing-8) 0;
321
+ border: none;
322
+ border-top: 2px solid var(--editor-border-default);
323
+ }
324
+
325
+ /* Floating Menu & Toolbar */
326
+ .floating-menu {
327
+ background-color: var(--editor-bg-default);
328
+ border: 1px solid var(--editor-border-default);
329
+ box-shadow: var(--editor-shadow-lg);
330
+ border-radius: 8px;
331
+ padding: var(--editor-spacing-1);
332
+ display: flex;
333
+ gap: var(--editor-spacing-1);
334
+ align-items: center;
335
+ animation: fadeIn var(--editor-transition-normal);
336
+ backdrop-filter: blur(8px);
337
+ }
338
+
339
+ .toolbar-button {
340
+ display: inline-flex;
341
+ align-items: center;
342
+ justify-content: center;
343
+ border-radius: 6px;
344
+ font-size: 0.875rem;
345
+ font-weight: 500;
346
+ height: 2.25rem;
347
+ padding: 0 var(--editor-spacing-3);
348
+ transition: all var(--editor-transition-fast);
349
+ background-color: transparent;
350
+ color: var(--editor-text-default);
351
+ border: 1px solid transparent;
352
+ }
353
+
354
+ .toolbar-button:hover {
355
+ background-color: var(--editor-bg-subtle);
356
+ color: var(--editor-text-default);
357
+ }
358
+
359
+ .toolbar-button:focus-visible {
360
+ outline: none;
361
+ box-shadow: 0 0 0 2px var(--editor-border-strong);
362
+ }
363
+
364
+ .toolbar-button:disabled {
365
+ opacity: 0.5;
366
+ cursor: not-allowed;
367
+ }
368
+
369
+ .toolbar-button.active {
370
+ background-color: var(--editor-bg-muted);
371
+ color: var(--editor-text-blue);
372
+ }
373
+
374
+ /* Placeholder Styles
375
+ .ProseMirror p.is-editor-empty:first-child::before {
376
+ content: "Start writing or press '/' for commands...";
377
+ color: var(--editor-text-gray);
378
+ pointer-events: none;
379
+ float: left;
380
+ height: 0;
381
+ } */
382
+
383
+ /* Mobile Optimizations */
384
+ @media (max-width: 640px) {
385
+ .ProseMirror {
386
+ padding: var(--editor-spacing-8) var(--editor-spacing-4);
387
+ }
388
+
389
+ .ProseMirror h1 { font-size: 2rem; }
390
+ .ProseMirror h2 { font-size: 1.75rem; }
391
+ .ProseMirror h3 { font-size: 1.5rem; }
392
+ .ProseMirror h4 { font-size: 1.25rem; }
393
+ .ProseMirror p { font-size: 1rem; }
394
+ }
395
+
396
+ /* Animations */
397
+ @keyframes fadeIn {
398
+ from { opacity: 0; transform: translateY(-8px); }
399
+ to { opacity: 1; transform: translateY(0); }
400
+ }
401
+
402
+ /* Print Styles */
403
+ @media print {
404
+ .ProseMirror {
405
+ padding: 0;
406
+ max-width: none;
407
+ }
408
+
409
+ .floating-menu,
410
+ .toolbar-button {
411
+ display: none;
412
+ }
413
+ }
414
+
415
+ .is-editor-empty::before {
416
+ color: var(--editor-text-gray);
417
+ content: attr(data-placeholder);
418
+ float: left;
419
+ height: 0;
420
+ pointer-events: none;
421
+ }
@@ -0,0 +1,126 @@
1
+ 'use client'
2
+
3
+ import { useMediaQuery } from 'hooks/use-media-querry'
4
+ import { AlignCenter, AlignJustify, AlignLeft, AlignRight, Check, ChevronDown } from 'lucide-react'
5
+ import { MobileToolbarGroup, MobileToolbarItem } from './mobile-toolbar-group'
6
+
7
+ import { Button } from 'components/ui/button'
8
+ import {
9
+ DropdownMenu,
10
+ DropdownMenuContent,
11
+ DropdownMenuGroup,
12
+ DropdownMenuItem,
13
+ DropdownMenuTrigger,
14
+ } from 'components/ui/dropdown-menu'
15
+ import { Tooltip, TooltipContent, TooltipTrigger } from 'components/ui/tooltip'
16
+ import { useToolbar } from './toolbar-provider'
17
+
18
+ export const AlignmentTooolbar = () => {
19
+ const { editor } = useToolbar()
20
+ const isMobile = useMediaQuery('(max-width: 640px)')
21
+ const handleAlign = (value: string) => {
22
+ editor?.chain().focus().setTextAlign(value).run()
23
+ }
24
+
25
+ const isDisabled = editor?.isActive('image') ?? editor?.isActive('video') ?? false
26
+
27
+ const currentTextAlign = () => {
28
+ if (editor?.isActive({ textAlign: 'left' })) {
29
+ return 'left'
30
+ }
31
+ if (editor?.isActive({ textAlign: 'center' })) {
32
+ return 'center'
33
+ }
34
+ if (editor?.isActive({ textAlign: 'right' })) {
35
+ return 'right'
36
+ }
37
+ if (editor?.isActive({ textAlign: 'justify' })) {
38
+ return 'justify'
39
+ }
40
+
41
+ return 'left'
42
+ }
43
+
44
+ const alignmentOptions = [
45
+ {
46
+ name: 'Left Align',
47
+ value: 'left',
48
+ icon: <AlignLeft className="h-4 w-4" />,
49
+ },
50
+ {
51
+ name: 'Center Align',
52
+ value: 'center',
53
+ icon: <AlignCenter className="h-4 w-4" />,
54
+ },
55
+ {
56
+ name: 'Right Align',
57
+ value: 'right',
58
+ icon: <AlignRight className="h-4 w-4" />,
59
+ },
60
+ {
61
+ name: 'Justify Align',
62
+ value: 'justify',
63
+ icon: <AlignJustify className="h-4 w-4" />,
64
+ },
65
+ ]
66
+
67
+ const findIndex = (value: string) => {
68
+ return alignmentOptions.findIndex((option) => option.value === value)
69
+ }
70
+
71
+ if (isMobile) {
72
+ return (
73
+ <MobileToolbarGroup label={alignmentOptions[findIndex(currentTextAlign())]?.name ?? 'Left Align'}>
74
+ {alignmentOptions.map((option, index) => (
75
+ <MobileToolbarItem
76
+ key={index}
77
+ onClick={() => handleAlign(option.value)}
78
+ active={currentTextAlign() === option.value}
79
+ >
80
+ <span className="mr-2">{option.icon}</span>
81
+ {option.name}
82
+ </MobileToolbarItem>
83
+ ))}
84
+ </MobileToolbarGroup>
85
+ )
86
+ }
87
+
88
+ return (
89
+ <DropdownMenu>
90
+ <Tooltip>
91
+ <TooltipTrigger asChild>
92
+ <DropdownMenuTrigger disabled={isDisabled} asChild>
93
+ <Button variant="ghost" size="sm" className="h-8 w-max font-normal" type="button">
94
+ <span className="mr-2">{alignmentOptions[findIndex(currentTextAlign())]?.icon}</span>
95
+ {alignmentOptions[findIndex(currentTextAlign())]?.name}
96
+ <ChevronDown className="ml-2 h-4 w-4" />
97
+ </Button>
98
+ </DropdownMenuTrigger>
99
+ </TooltipTrigger>
100
+ <TooltipContent>Text Alignment</TooltipContent>
101
+ </Tooltip>
102
+ <DropdownMenuContent
103
+ loop
104
+ onCloseAutoFocus={(e) => {
105
+ e.preventDefault()
106
+ }}
107
+ >
108
+ <DropdownMenuGroup className=" w-40">
109
+ {alignmentOptions.map((option, index) => (
110
+ <DropdownMenuItem
111
+ onSelect={() => {
112
+ handleAlign(option.value)
113
+ }}
114
+ key={index}
115
+ >
116
+ <span className="mr-2">{option.icon}</span>
117
+ {option.name}
118
+
119
+ {option.value === currentTextAlign() && <Check className="ml-auto h-4 w-4" />}
120
+ </DropdownMenuItem>
121
+ ))}
122
+ </DropdownMenuGroup>
123
+ </DropdownMenuContent>
124
+ </DropdownMenu>
125
+ )
126
+ }
@@ -0,0 +1,47 @@
1
+ 'use client'
2
+
3
+ import { TextQuote } from 'lucide-react'
4
+ import React from 'react'
5
+
6
+ import { cn } from '@/lib/utils'
7
+ import { Button, type ButtonProps } from 'components/ui/button'
8
+ import { Tooltip, TooltipContent, TooltipTrigger } from 'components/ui/tooltip'
9
+ import { useToolbar } from './toolbar-provider'
10
+
11
+ const BlockquoteToolbar = React.forwardRef<HTMLButtonElement, ButtonProps>(
12
+ ({ className, onClick, children, ...props }, ref) => {
13
+ const { editor } = useToolbar()
14
+ return (
15
+ <Tooltip>
16
+ <TooltipTrigger asChild>
17
+ <Button
18
+ variant="ghost"
19
+ size="icon"
20
+ type="button"
21
+ className={cn(
22
+ 'h-8 w-8 p-0 sm:h-9 sm:w-9',
23
+ editor?.isActive('blockquote') && 'bg-accent',
24
+ className,
25
+ )}
26
+ onClick={(e) => {
27
+ editor?.chain().focus().toggleBlockquote().run()
28
+ onClick?.(e)
29
+ }}
30
+ disabled={!editor?.can().chain().focus().toggleBlockquote().run()}
31
+ ref={ref}
32
+ {...props}
33
+ >
34
+ {children ?? <TextQuote className="h-4 w-4" />}
35
+ </Button>
36
+ </TooltipTrigger>
37
+ <TooltipContent>
38
+ <span>Blockquote</span>
39
+ </TooltipContent>
40
+ </Tooltip>
41
+ )
42
+ },
43
+ )
44
+
45
+ BlockquoteToolbar.displayName = 'BlockquoteToolbar'
46
+
47
+ export { BlockquoteToolbar }
@@ -0,0 +1,48 @@
1
+ 'use client'
2
+
3
+ import { BoldIcon } from 'lucide-react'
4
+ import React from 'react'
5
+
6
+ import { cn } from '@/lib/utils'
7
+ import { Button, type ButtonProps } from 'components/ui/button'
8
+ import { Tooltip, TooltipContent, TooltipTrigger } from 'components/ui/tooltip'
9
+ import { useToolbar } from './toolbar-provider'
10
+ // import type { Extension } from "@tiptap/core";
11
+ // import type { StarterKitOptions } from "@tiptap/starter-kit";
12
+
13
+ // type StarterKitExtensions = Extension<StarterKitOptions>;
14
+
15
+ const BoldToolbar = React.forwardRef<HTMLButtonElement, ButtonProps>(
16
+ ({ className, onClick, children, ...props }, ref) => {
17
+ const { editor } = useToolbar()
18
+ return (
19
+ <Tooltip>
20
+ <TooltipTrigger asChild>
21
+ <Button
22
+ variant="ghost"
23
+ size="icon"
24
+ type="button"
25
+ className={cn('h-8 w-8 p-0 sm:h-9 sm:w-9', editor?.isActive('bold') && 'bg-accent', className)}
26
+ onClick={(e) => {
27
+ editor?.chain().focus().toggleBold().run()
28
+ onClick?.(e)
29
+ }}
30
+ disabled={!editor?.can().chain().focus().toggleBold().run()}
31
+ ref={ref}
32
+ {...props}
33
+ >
34
+ {children ?? <BoldIcon className="h-4 w-4" />}
35
+ </Button>
36
+ </TooltipTrigger>
37
+ <TooltipContent>
38
+ <span>Bold</span>
39
+ <span className="ml-1 text-xs text-gray-11">(cmd + b)</span>
40
+ </TooltipContent>
41
+ </Tooltip>
42
+ )
43
+ },
44
+ )
45
+
46
+ BoldToolbar.displayName = 'BoldToolbar'
47
+
48
+ export { BoldToolbar }
@@ -0,0 +1,48 @@
1
+ 'use client'
2
+
3
+ import { List } from 'lucide-react'
4
+ import React from 'react'
5
+
6
+ import { cn } from '@/lib/utils'
7
+ import { Button, type ButtonProps } from 'components/ui/button'
8
+ import { Tooltip, TooltipContent, TooltipTrigger } from 'components/ui/tooltip'
9
+ import { useToolbar } from './toolbar-provider'
10
+
11
+ const BulletListToolbar = React.forwardRef<HTMLButtonElement, ButtonProps>(
12
+ ({ className, onClick, children, ...props }, ref) => {
13
+ const { editor } = useToolbar()
14
+
15
+ return (
16
+ <Tooltip>
17
+ <TooltipTrigger asChild>
18
+ <Button
19
+ variant="ghost"
20
+ size="icon"
21
+ type="button"
22
+ className={cn(
23
+ 'h-8 w-8 p-0 sm:h-9 sm:w-9',
24
+ editor?.isActive('bulletList') && 'bg-accent',
25
+ className,
26
+ )}
27
+ onClick={(e) => {
28
+ editor?.chain().focus().toggleBulletList().run()
29
+ onClick?.(e)
30
+ }}
31
+ disabled={!editor?.can().chain().focus().toggleBulletList().run()}
32
+ ref={ref}
33
+ {...props}
34
+ >
35
+ {children ?? <List className="h-4 w-4" />}
36
+ </Button>
37
+ </TooltipTrigger>
38
+ <TooltipContent>
39
+ <span>Bullet list</span>
40
+ </TooltipContent>
41
+ </Tooltip>
42
+ )
43
+ },
44
+ )
45
+
46
+ BulletListToolbar.displayName = 'BulletListToolbar'
47
+
48
+ export { BulletListToolbar }