@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
package/lib/package.ts ADDED
@@ -0,0 +1,144 @@
1
+ import { promises as fs, readdirSync } from 'node:fs'
2
+ import { readFile } from 'node:fs/promises'
3
+ import { join } from 'node:path'
4
+ import postcss from 'postcss'
5
+ import postcssNested from 'postcss-nested'
6
+ import type { RegistryItem } from 'shadcn/schema'
7
+
8
+ type PackageJson = {
9
+ name: string
10
+ version: string
11
+ description: string
12
+ dependencies?: Record<string, string>
13
+ devDependencies?: Record<string, string>
14
+ }
15
+
16
+ export const getPackage = async (packageName: string) => {
17
+ const packageDir = join(process.cwd())
18
+ const packagePath = join(packageDir, 'package.json')
19
+ const packageJson = JSON.parse(await readFile(packagePath, 'utf-8')) as PackageJson
20
+
21
+ const kiboDependencies = Object.keys(packageJson.dependencies || {}).filter(
22
+ (dep) => dep.startsWith('@repo') && dep !== '@repo/shadcn-ui',
23
+ )
24
+
25
+ const dependencies = Object.keys(packageJson.dependencies || {}).filter(
26
+ (dep) => !['react', 'react-dom', '@repo/shadcn-ui', ...kiboDependencies].includes(dep),
27
+ )
28
+
29
+ const devDependencies = Object.keys(packageJson.devDependencies || {}).filter(
30
+ (dep) => !['@repo/typescript-config', '@types/react', '@types/react-dom', 'typescript'].includes(dep),
31
+ )
32
+
33
+ const packageFiles = readdirSync(packageDir, { withFileTypes: true })
34
+ const tsxFiles = packageFiles.filter((file) => file.isFile() && file.name.endsWith('.tsx'))
35
+
36
+ const cssFiles = packageFiles.filter((file) => file.isFile() && file.name.endsWith('.css'))
37
+
38
+ const files: RegistryItem['files'] = []
39
+
40
+ for (const file of tsxFiles) {
41
+ const filePath = join(packageDir, file.name)
42
+ const content = await fs.readFile(filePath, 'utf-8')
43
+
44
+ files.push({
45
+ type: 'registry:ui',
46
+ path: file.name,
47
+ content,
48
+ target: `components/${packageName}/${file.name}`,
49
+ })
50
+ }
51
+
52
+ const registryDependencies =
53
+ files
54
+ .map((f) => f.content)
55
+ .join('\n')
56
+ .match(/@\/components\/ui\/([a-z-]+)/g)
57
+ ?.map((path) => path.split('/').pop())
58
+ .filter((name): name is string => !!name) || []
59
+
60
+ const css: RegistryItem['css'] = {}
61
+
62
+ for (const file of cssFiles) {
63
+ const contents = await fs.readFile(join(packageDir, file.name), 'utf-8')
64
+
65
+ // Process CSS with PostCSS to handle nested selectors
66
+ const processed = await postcss([postcssNested]).process(contents, {
67
+ from: undefined,
68
+ })
69
+
70
+ // Parse the processed CSS and convert to JSON structure
71
+ const ast = postcss.parse(processed.css)
72
+
73
+ ast.walkAtRules('layer', (atRule) => {
74
+ const layerName = `@layer ${atRule.params}`
75
+ css[layerName] = {}
76
+
77
+ // First pass: process non-media rules
78
+ atRule.walkRules((rule) => {
79
+ // Skip rules that are inside media queries
80
+ if (rule.parent && rule.parent.type === 'atrule' && (rule.parent as any).name === 'media') {
81
+ return
82
+ }
83
+
84
+ const selector = rule.selector
85
+ const ruleObj: Record<string, string> = {}
86
+
87
+ // Process all declarations
88
+ rule.walkDecls((decl) => {
89
+ ruleObj[decl.prop] = decl.value
90
+ })
91
+
92
+ if (Object.keys(ruleObj).length > 0) {
93
+ css[layerName][selector] = ruleObj
94
+ }
95
+ })
96
+
97
+ // Second pass: process media query rules as top-level entries
98
+ atRule.walkAtRules('media', (mediaRule) => {
99
+ const mediaQuery = `@media ${mediaRule.params}`
100
+
101
+ // Create a top-level media query entry if it doesn't exist
102
+ if (!css[layerName][mediaQuery]) {
103
+ css[layerName][mediaQuery] = {}
104
+ }
105
+
106
+ mediaRule.walkRules((rule) => {
107
+ const selector = rule.selector
108
+ const mediaObj: Record<string, string> = {}
109
+
110
+ rule.walkDecls((decl) => {
111
+ mediaObj[decl.prop] = decl.value
112
+ })
113
+
114
+ if (Object.keys(mediaObj).length > 0) {
115
+ // Store the selector inside the media query
116
+ css[layerName][mediaQuery][selector] = mediaObj
117
+ }
118
+ })
119
+ })
120
+ })
121
+ }
122
+
123
+ let type: RegistryItem['type'] = 'registry:ui'
124
+
125
+ if (!Object.keys(files).length && Object.keys(css).length) {
126
+ type = 'registry:style'
127
+ }
128
+
129
+ const response: RegistryItem = {
130
+ $schema: 'https://ui.shadcn.com/schema/registry-item.json',
131
+ name: packageName,
132
+ type,
133
+ title: packageName,
134
+ description: packageJson.description,
135
+ author: 'Sebastian Döll (@katallaxie)',
136
+ dependencies,
137
+ devDependencies,
138
+ registryDependencies,
139
+ files,
140
+ css,
141
+ }
142
+
143
+ return response
144
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * How each preference should be saved.
3
+ *
4
+ * "client-cookie" → write cookie on the browser only.
5
+ * "server-cookie" → write cookie through a Server Action.
6
+ * "localStorage" → save only on the client (non-layout stuff).
7
+ * "none" → no saving, resets on reload.
8
+ *
9
+ * Layout-critical prefs (sidebar_variant / sidebar_collapsible)
10
+ * must stay consistent during SSR → so they can’t use localStorage.
11
+ * Others are flexible and can use any persistence.
12
+ */
13
+
14
+ import type { ThemeMode, ThemePreset } from './theme'
15
+
16
+ export type PreferencePersistence = 'none' | 'client-cookie' | 'server-cookie' | 'localStorage'
17
+
18
+ /**
19
+ * All available preference keys and their value types.
20
+ */
21
+ export type PreferenceValueMap = {
22
+ theme_mode: ThemeMode
23
+ theme_preset: ThemePreset
24
+ }
25
+
26
+ export type PreferenceKey = keyof PreferenceValueMap
27
+
28
+ /**
29
+ * Layout-critical keys → these affect SSR UI (sidebar shape)
30
+ * so they must be accessible on the server.
31
+ */
32
+ export const LAYOUT_CRITICAL_KEYS = [] as const
33
+ export type LayoutCriticalKey = (typeof LAYOUT_CRITICAL_KEYS)[number]
34
+
35
+ /**
36
+ * Everything else is non-critical and can be read from the client.
37
+ */
38
+ export type NonCriticalKey = Exclude<PreferenceKey, LayoutCriticalKey>
39
+
40
+ /**
41
+ * Layout-critical cannot use "localStorage" because SSR needs the value.
42
+ * So remove it from allowed persistence types for those keys.
43
+ */
44
+ type LayoutCriticalPersistence = Exclude<PreferencePersistence, 'localStorage'>
45
+
46
+ /**
47
+ * Final config:
48
+ * - layout-critical keys → restricted persistence
49
+ * - non-critical keys → can use any persistence
50
+ */
51
+ type PreferencePersistenceConfig = {
52
+ [K in LayoutCriticalKey]: LayoutCriticalPersistence
53
+ } & {
54
+ [K in NonCriticalKey]: PreferencePersistence
55
+ }
56
+
57
+ /**
58
+ * Default preference values on first load.
59
+ */
60
+ export const PREFERENCE_DEFAULTS: PreferenceValueMap = {
61
+ theme_mode: 'light',
62
+ theme_preset: 'default',
63
+ }
64
+
65
+ /**
66
+ * How each preference is persisted.
67
+ * You can change these per-key.
68
+ */
69
+ export const PREFERENCE_PERSISTENCE: PreferencePersistenceConfig = {
70
+ theme_mode: 'client-cookie',
71
+ theme_preset: 'client-cookie',
72
+ }
@@ -0,0 +1,20 @@
1
+ 'use client'
2
+
3
+ import { setLocalStorageValue } from './localstorage-client'
4
+ import { PREFERENCE_PERSISTENCE, type PreferenceKey } from './preferences-config'
5
+
6
+ export async function persistPreference(key: PreferenceKey, value: string) {
7
+ const mode = PREFERENCE_PERSISTENCE[key]
8
+
9
+ switch (mode) {
10
+ case 'none':
11
+ return
12
+
13
+ case 'localStorage':
14
+ setLocalStorageValue(key, value)
15
+ return
16
+
17
+ default:
18
+ return
19
+ }
20
+ }
@@ -0,0 +1,12 @@
1
+ export function applyThemeMode(value: 'light' | 'dark') {
2
+ const doc = document.documentElement
3
+ doc.classList.add('disable-transitions')
4
+ doc.classList.toggle('dark', value === 'dark')
5
+ requestAnimationFrame(() => {
6
+ doc.classList.remove('disable-transitions')
7
+ })
8
+ }
9
+
10
+ export function applyThemePreset(value: string) {
11
+ document.documentElement.setAttribute('data-theme-preset', value)
12
+ }
package/lib/theme.ts ADDED
@@ -0,0 +1,50 @@
1
+ export const THEME_MODE_OPTIONS = [
2
+ { label: 'Light', value: 'light' },
3
+ { label: 'Dark', value: 'dark' },
4
+ ] as const
5
+
6
+ export const THEME_MODE_VALUES = THEME_MODE_OPTIONS.map((o) => o.value)
7
+ export type ThemeMode = (typeof THEME_MODE_VALUES)[number]
8
+
9
+ // --- generated:themePresets:start ---
10
+
11
+ export const THEME_PRESET_OPTIONS = [
12
+ {
13
+ label: 'Default',
14
+ value: 'default',
15
+ primary: {
16
+ light: 'oklch(0.205 0 0)',
17
+ dark: 'oklch(0.922 0 0)',
18
+ },
19
+ },
20
+ {
21
+ label: 'Brutalist',
22
+ value: 'brutalist',
23
+ primary: {
24
+ light: 'oklch(0.6489 0.237 26.9728)',
25
+ dark: 'oklch(0.7044 0.1872 23.1858)',
26
+ },
27
+ },
28
+ {
29
+ label: 'Soft Pop',
30
+ value: 'soft-pop',
31
+ primary: {
32
+ light: 'oklch(0.5106 0.2301 276.9656)',
33
+ dark: 'oklch(0.6801 0.1583 276.9349)',
34
+ },
35
+ },
36
+ {
37
+ label: 'Tangerine',
38
+ value: 'tangerine',
39
+ primary: {
40
+ light: 'oklch(0.64 0.17 36.44)',
41
+ dark: 'oklch(0.64 0.17 36.44)',
42
+ },
43
+ },
44
+ ] as const
45
+
46
+ export const THEME_PRESET_VALUES = THEME_PRESET_OPTIONS.map((p) => p.value)
47
+
48
+ export type ThemePreset = (typeof THEME_PRESET_OPTIONS)[number]['value']
49
+
50
+ // --- generated:themePresets:end ---
@@ -0,0 +1,45 @@
1
+ import { type Editor } from "@tiptap/core";
2
+
3
+ export const NODE_HANDLES_SELECTED_STYLE_CLASSNAME =
4
+ "node-handles-selected-style";
5
+
6
+ export function isValidUrl(url: string) {
7
+ return /^https?:\/\/\S+$/.test(url);
8
+ }
9
+
10
+ export const duplicateContent = (editor: Editor) => {
11
+ const { view } = editor;
12
+ const { state } = view;
13
+ const { selection } = state;
14
+
15
+ editor
16
+ .chain()
17
+ .insertContentAt(
18
+ selection.to,
19
+ /* eslint-disable */
20
+ // @ts-nocheck
21
+ selection.content().content.firstChild?.toJSON(),
22
+ {
23
+ updateSelection: true,
24
+ }
25
+ )
26
+ .focus(selection.to)
27
+ .run();
28
+ };
29
+
30
+ export function getUrlFromString(str: string) {
31
+ if (isValidUrl(str)) {
32
+ return str;
33
+ }
34
+ try {
35
+ if (str.includes(".") && !str.includes(" ")) {
36
+ return new URL(`https://${str}`).toString();
37
+ }
38
+ } catch {
39
+ return null;
40
+ }
41
+ }
42
+
43
+ export function absoluteUrl(path: string) {
44
+ return `${process.env.NEXT_PUBLIC_APP_URL}${path}`;
45
+ }
package/lib/utils.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { clsx, type ClassValue } from 'clsx'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ // pnpx shadcn add dropdown-menu tooltip scroll-area popover separator dropdown-menu label input drawer checkbox separator command tabs dropdown-menu
5
+ export function cn(...inputs: ClassValue[]) {
6
+ return twMerge(clsx(inputs))
7
+ }
8
+
9
+ export function absoluteUrl(path: string) {
10
+ return `${process.env.NEXT_PUBLIC_APP_URL}${path}`
11
+ }
package/next-env.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+ import "./.next/types/routes.d.ts";
4
+
5
+ // NOTE: This file should not be edited
6
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
@@ -0,0 +1,11 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ reactStrictMode: true,
4
+ output: 'export',
5
+ basePath: process.env.BASE_PATH ?? '',
6
+ images: {
7
+ unoptimized: true,
8
+ },
9
+ }
10
+
11
+ export default nextConfig
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "@open-cloud-initiative/editor-x",
3
+ "version": "0.0.1",
4
+ "scripts": {
5
+ "dev": "next dev --turbopack",
6
+ "build": "next build",
7
+ "start": "next start",
8
+ "lint": "next lint",
9
+ "export": "next export",
10
+ "registry:build": "shadcn build"
11
+ },
12
+ "dependencies": {
13
+ "@floating-ui/dom": "^1.7.4",
14
+ "@radix-ui/react-checkbox": "^1.1.3",
15
+ "@radix-ui/react-collapsible": "^1.1.2",
16
+ "@radix-ui/react-dialog": "^1.1.5",
17
+ "@radix-ui/react-dropdown-menu": "^2.1.5",
18
+ "@radix-ui/react-label": "^2.1.1",
19
+ "@radix-ui/react-popover": "^1.1.5",
20
+ "@radix-ui/react-scroll-area": "^1.2.2",
21
+ "@radix-ui/react-separator": "^1.1.1",
22
+ "@radix-ui/react-slot": "^1.2.4",
23
+ "@radix-ui/react-tabs": "^1.1.2",
24
+ "@radix-ui/react-toggle": "^1.1.1",
25
+ "@radix-ui/react-toggle-group": "^1.1.1",
26
+ "@radix-ui/react-tooltip": "^1.1.7",
27
+ "@tiptap/core": "^3.15.3",
28
+ "@tiptap/extension-code-block-lowlight": "^3.15.3",
29
+ "@tiptap/extension-color": "^3.15.3",
30
+ "@tiptap/extension-floating-menu": "^3.15.3",
31
+ "@tiptap/extension-heading": "^3.15.3",
32
+ "@tiptap/extension-image": "^3.15.3",
33
+ "@tiptap/extension-link": "^3.15.3",
34
+ "@tiptap/extension-list": "^3.15.3",
35
+ "@tiptap/extension-placeholder": "^3.15.3",
36
+ "@tiptap/extension-subscript": "^3.15.3",
37
+ "@tiptap/extension-superscript": "^3.15.3",
38
+ "@tiptap/extension-table": "^3.15.3",
39
+ "@tiptap/extension-text-align": "^3.15.3",
40
+ "@tiptap/extension-text-style": "^3.15.3",
41
+ "@tiptap/extension-typography": "^3.15.3",
42
+ "@tiptap/extension-underline": "^3.15.3",
43
+ "@tiptap/markdown": "^3.15.3",
44
+ "@tiptap/pm": "^3.15.3",
45
+ "@tiptap/react": "^3.15.3",
46
+ "@tiptap/starter-kit": "^3.15.3",
47
+ "@tiptap/suggestion": "^3.15.3",
48
+ "class-variance-authority": "^0.7.1",
49
+ "clsx": "^2.1.1",
50
+ "cmdk": "1.1.1",
51
+ "fuse.js": "^7.1.0",
52
+ "lowlight": "^3.3.0",
53
+ "lucide-react": "^0.562.0",
54
+ "next": "16.1.1",
55
+ "next-themes": "^0.4.6",
56
+ "postcss-nested": "^7.0.2",
57
+ "react": "^19.2.3",
58
+ "react-dom": "^19.2.3",
59
+ "react-resizable-panels": "^3.0.6",
60
+ "shadcn": "3.6.3",
61
+ "tailwind-merge": "^3.4.0",
62
+ "tailwindcss-animate": "^1.0.7",
63
+ "tippy.js": "^6.3.7",
64
+ "vaul": "^1.1.2",
65
+ "zustand": "^5.0.9"
66
+ },
67
+ "devDependencies": {
68
+ "@eslint/eslintrc": "^3",
69
+ "@tailwindcss/postcss": "^4.1.18",
70
+ "@tiptap/extension-highlight": "^3.15.3",
71
+ "@total-typescript/ts-reset": "^0.6.1",
72
+ "@types/node": "^25.0.3",
73
+ "@types/react": "^19",
74
+ "@types/react-dom": "^19",
75
+ "@typescript-eslint/parser": "^8.51.0",
76
+ "babel-plugin-react-compiler": "^1.0.0",
77
+ "eslint": "^9.39.2",
78
+ "eslint-config-google": "^0.14.0",
79
+ "eslint-config-next": "^16.1.1",
80
+ "eslint-config-prettier": "^10.1.8",
81
+ "eslint-import-resolver-typescript": "^4.4.4",
82
+ "eslint-plugin-import": "^2.26.0",
83
+ "eslint-plugin-prettier": "^5.5.4",
84
+ "postcss": "^8.5.6",
85
+ "prettier": "^3.7.4",
86
+ "prettier-eslint": "^16.4.2",
87
+ "prettier-plugin-tailwindcss": "^0.7.2",
88
+ "tailwindcss": "^4.1.5",
89
+ "tw-animate-css": "^1.4.0",
90
+ "typescript": "^5"
91
+ }
92
+ }
@@ -0,0 +1,8 @@
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: {
4
+ '@tailwindcss/postcss': {},
5
+ },
6
+ }
7
+
8
+ export default config
@@ -0,0 +1,15 @@
1
+ // prettier.config.js, .prettierrc.js, prettier.config.mjs, or .prettierrc.mjs
2
+
3
+ /**
4
+ * @see https://prettier.io/docs/configuration
5
+ * @type {import("prettier").Config}
6
+ */
7
+ const config = {
8
+ semi: false,
9
+ trailingComma: "all",
10
+ singleQuote: true,
11
+ printWidth: 120,
12
+ tabWidth: 4
13
+ };
14
+
15
+ export default config;
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1 @@
1
+ <svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
package/public/og.webp ADDED
Binary file
@@ -0,0 +1,85 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "editor-x",
4
+ "title": "editor-x",
5
+ "author": "Sebastian Döll (@katallaxie)",
6
+ "dependencies": [
7
+ "@floating-ui/dom",
8
+ "@radix-ui/react-checkbox",
9
+ "@radix-ui/react-collapsible",
10
+ "@radix-ui/react-dialog",
11
+ "@radix-ui/react-dropdown-menu",
12
+ "@radix-ui/react-label",
13
+ "@radix-ui/react-popover",
14
+ "@radix-ui/react-scroll-area",
15
+ "@radix-ui/react-separator",
16
+ "@radix-ui/react-slot",
17
+ "@radix-ui/react-tabs",
18
+ "@radix-ui/react-toggle",
19
+ "@radix-ui/react-toggle-group",
20
+ "@radix-ui/react-tooltip",
21
+ "@tiptap/core",
22
+ "@tiptap/extension-code-block-lowlight",
23
+ "@tiptap/extension-color",
24
+ "@tiptap/extension-floating-menu",
25
+ "@tiptap/extension-heading",
26
+ "@tiptap/extension-image",
27
+ "@tiptap/extension-link",
28
+ "@tiptap/extension-list",
29
+ "@tiptap/extension-placeholder",
30
+ "@tiptap/extension-subscript",
31
+ "@tiptap/extension-superscript",
32
+ "@tiptap/extension-table",
33
+ "@tiptap/extension-text-align",
34
+ "@tiptap/extension-text-style",
35
+ "@tiptap/extension-typography",
36
+ "@tiptap/extension-underline",
37
+ "@tiptap/markdown",
38
+ "@tiptap/pm",
39
+ "@tiptap/react",
40
+ "@tiptap/starter-kit",
41
+ "@tiptap/suggestion",
42
+ "class-variance-authority",
43
+ "clsx",
44
+ "cmdk",
45
+ "fuse.js",
46
+ "lowlight",
47
+ "lucide-react",
48
+ "next",
49
+ "next-themes",
50
+ "postcss-nested",
51
+ "react-resizable-panels",
52
+ "shadcn",
53
+ "tailwind-merge",
54
+ "tailwindcss-animate",
55
+ "tippy.js",
56
+ "vaul",
57
+ "zustand"
58
+ ],
59
+ "devDependencies": [
60
+ "@eslint/eslintrc",
61
+ "@tailwindcss/postcss",
62
+ "@tiptap/extension-highlight",
63
+ "@total-typescript/ts-reset",
64
+ "@types/node",
65
+ "@typescript-eslint/parser",
66
+ "babel-plugin-react-compiler",
67
+ "eslint",
68
+ "eslint-config-google",
69
+ "eslint-config-next",
70
+ "eslint-config-prettier",
71
+ "eslint-import-resolver-typescript",
72
+ "eslint-plugin-import",
73
+ "eslint-plugin-prettier",
74
+ "postcss",
75
+ "prettier",
76
+ "prettier-eslint",
77
+ "prettier-plugin-tailwindcss",
78
+ "tailwindcss",
79
+ "tw-animate-css"
80
+ ],
81
+ "registryDependencies": [],
82
+ "files": [],
83
+ "css": {},
84
+ "type": "registry:ui"
85
+ }