@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.
- package/.devcontainer/Dockerfile +13 -0
- package/.devcontainer/devcontainer.json +52 -0
- package/.github/dependabot.yml +10 -0
- package/.github/workflows/pages.yml +42 -0
- package/.github/workflows/publish.yml +41 -0
- package/.vscode/settings.json +7 -0
- package/LICENSE +9 -0
- package/README.md +9 -0
- package/app/_component/editor.tsx +383 -0
- package/app/layout.tsx +46 -0
- package/app/page.tsx +11 -0
- package/app/r/registry.json/route.ts +22 -0
- package/components/editorx/editor.tsx +1794 -0
- package/components/editorx/extensions/floating-menu.tsx +376 -0
- package/components/editorx/extensions/floating-toolbar.tsx +97 -0
- package/components/editorx/extensions/image-placeholder.tsx +316 -0
- package/components/editorx/extensions/image.tsx +462 -0
- package/components/editorx/extensions/search-and-replace.tsx +438 -0
- package/components/editorx/rich-text-editor.tsx +383 -0
- package/components/editorx/tiptap.css +421 -0
- package/components/editorx/toolbars/alignment.tsx +126 -0
- package/components/editorx/toolbars/blockquote.tsx +47 -0
- package/components/editorx/toolbars/bold.tsx +48 -0
- package/components/editorx/toolbars/bullet-list.tsx +48 -0
- package/components/editorx/toolbars/code-block.tsx +47 -0
- package/components/editorx/toolbars/code.tsx +43 -0
- package/components/editorx/toolbars/color-and-highlight.tsx +215 -0
- package/components/editorx/toolbars/editor-toolbar.tsx +77 -0
- package/components/editorx/toolbars/hard-break.tsx +46 -0
- package/components/editorx/toolbars/headings.tsx +97 -0
- package/components/editorx/toolbars/horizontal-rule.tsx +42 -0
- package/components/editorx/toolbars/image-placeholder-toolbar.tsx +47 -0
- package/components/editorx/toolbars/italic.tsx +48 -0
- package/components/editorx/toolbars/link.tsx +130 -0
- package/components/editorx/toolbars/mobile-toolbar-group.tsx +76 -0
- package/components/editorx/toolbars/ordered-list.tsx +47 -0
- package/components/editorx/toolbars/redo.tsx +44 -0
- package/components/editorx/toolbars/strikethrough.tsx +48 -0
- package/components/editorx/toolbars/toolbar-provider.tsx +29 -0
- package/components/editorx/toolbars/underline.tsx +48 -0
- package/components/editorx/toolbars/undo.tsx +43 -0
- package/components/layout/theme-switcher.tsx +26 -0
- package/components/main-nav.tsx +24 -0
- package/components/mobile-nav.tsx +46 -0
- package/components/open-in-v0-button.tsx +38 -0
- package/components/page-header.tsx +30 -0
- package/components/site-footer.tsx +41 -0
- package/components/site-header.tsx +32 -0
- package/components/theme-provider.tsx +8 -0
- package/components/ui/button.tsx +57 -0
- package/components/ui/checkbox.tsx +30 -0
- package/components/ui/collapsible.tsx +11 -0
- package/components/ui/command.tsx +148 -0
- package/components/ui/dialog.tsx +122 -0
- package/components/ui/drawer.tsx +118 -0
- package/components/ui/dropdown-menu.tsx +201 -0
- package/components/ui/input.tsx +22 -0
- package/components/ui/label.tsx +26 -0
- package/components/ui/popover.tsx +33 -0
- package/components/ui/resizable.tsx +40 -0
- package/components/ui/scroll-area.tsx +42 -0
- package/components/ui/separator.tsx +31 -0
- package/components/ui/sheet.tsx +140 -0
- package/components/ui/sidebar.tsx +763 -0
- package/components/ui/skeleton.tsx +15 -0
- package/components/ui/spinner.tsx +29 -0
- package/components/ui/tabs.tsx +55 -0
- package/components/ui/toggle-group.tsx +61 -0
- package/components/ui/toggle.tsx +45 -0
- package/components/ui/tooltip.tsx +32 -0
- package/components.json +21 -0
- package/config/site.ts +15 -0
- package/eslint.config.mjs +20 -0
- package/hooks/use-character-limit.ts +28 -0
- package/hooks/use-copy-to-clipboard.ts +16 -0
- package/hooks/use-debounce.ts +17 -0
- package/hooks/use-image-upload.ts +97 -0
- package/hooks/use-media-querry.ts +18 -0
- package/hooks/use-mobile.tsx +19 -0
- package/images/editor.png +0 -0
- package/lib/content.ts +39 -0
- package/lib/cookie-client.ts +19 -0
- package/lib/localstorage-client.ts +19 -0
- package/lib/package.ts +144 -0
- package/lib/preferences-config.ts +72 -0
- package/lib/preferences-storage.ts +20 -0
- package/lib/theme-utils.ts +12 -0
- package/lib/theme.ts +50 -0
- package/lib/tiptap-utils.ts +45 -0
- package/lib/utils.ts +11 -0
- package/next-env.d.ts +6 -0
- package/next.config.mjs +11 -0
- package/package.json +92 -0
- package/postcss.config.mjs +8 -0
- package/prettier.config.mjs +15 -0
- package/public/android-chrome-192x192.png +0 -0
- package/public/android-chrome-512x512.png +0 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/next.svg +1 -0
- package/public/og.webp +0 -0
- package/public/r/editor-x.json +85 -0
- package/public/r/registry.json +93 -0
- package/public/site.webmanifest +19 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/registry/editor/components/editor.tsx +1794 -0
- package/registry/editor/components/extensions/floating-menu.tsx +376 -0
- package/registry/editor/components/extensions/floating-toolbar.tsx +97 -0
- package/registry/editor/components/extensions/image-placeholder.tsx +316 -0
- package/registry/editor/components/extensions/image.tsx +462 -0
- package/registry/editor/components/extensions/search-and-replace.tsx +438 -0
- package/registry/editor/components/rich-text-editor.tsx +383 -0
- package/registry/editor/components/tiptap.css +421 -0
- package/registry/editor/components/toolbars/alignment.tsx +126 -0
- package/registry/editor/components/toolbars/blockquote.tsx +47 -0
- package/registry/editor/components/toolbars/bold.tsx +48 -0
- package/registry/editor/components/toolbars/bullet-list.tsx +48 -0
- package/registry/editor/components/toolbars/code-block.tsx +47 -0
- package/registry/editor/components/toolbars/code.tsx +43 -0
- package/registry/editor/components/toolbars/color-and-highlight.tsx +215 -0
- package/registry/editor/components/toolbars/editor-toolbar.tsx +77 -0
- package/registry/editor/components/toolbars/hard-break.tsx +46 -0
- package/registry/editor/components/toolbars/headings.tsx +97 -0
- package/registry/editor/components/toolbars/horizontal-rule.tsx +42 -0
- package/registry/editor/components/toolbars/image-placeholder-toolbar.tsx +47 -0
- package/registry/editor/components/toolbars/italic.tsx +48 -0
- package/registry/editor/components/toolbars/link.tsx +130 -0
- package/registry/editor/components/toolbars/mobile-toolbar-group.tsx +76 -0
- package/registry/editor/components/toolbars/ordered-list.tsx +47 -0
- package/registry/editor/components/toolbars/redo.tsx +44 -0
- package/registry/editor/components/toolbars/strikethrough.tsx +48 -0
- package/registry/editor/components/toolbars/toolbar-provider.tsx +29 -0
- package/registry/editor/components/toolbars/underline.tsx +48 -0
- package/registry/editor/components/toolbars/undo.tsx +43 -0
- package/registry/editor/components/ui/button.tsx +57 -0
- package/registry/editor/components/ui/checkbox.tsx +30 -0
- package/registry/editor/components/ui/collapsible.tsx +11 -0
- package/registry/editor/components/ui/command.tsx +148 -0
- package/registry/editor/components/ui/dialog.tsx +122 -0
- package/registry/editor/components/ui/drawer.tsx +118 -0
- package/registry/editor/components/ui/dropdown-menu.tsx +201 -0
- package/registry/editor/components/ui/input.tsx +22 -0
- package/registry/editor/components/ui/label.tsx +26 -0
- package/registry/editor/components/ui/popover.tsx +33 -0
- package/registry/editor/components/ui/resizable.tsx +40 -0
- package/registry/editor/components/ui/scroll-area.tsx +42 -0
- package/registry/editor/components/ui/separator.tsx +31 -0
- package/registry/editor/components/ui/sheet.tsx +140 -0
- package/registry/editor/components/ui/sidebar.tsx +763 -0
- package/registry/editor/components/ui/skeleton.tsx +15 -0
- package/registry/editor/components/ui/spinner.tsx +29 -0
- package/registry/editor/components/ui/tabs.tsx +55 -0
- package/registry/editor/components/ui/toggle-group.tsx +61 -0
- package/registry/editor/components/ui/toggle.tsx +45 -0
- package/registry/editor/components/ui/tooltip.tsx +32 -0
- package/registry/editor/hooks/use-character-limit.ts +28 -0
- package/registry/editor/hooks/use-copy-to-clipboard.ts +16 -0
- package/registry/editor/hooks/use-debounce.ts +17 -0
- package/registry/editor/hooks/use-image-upload.ts +97 -0
- package/registry/editor/hooks/use-media-querry.ts +18 -0
- package/registry/editor/hooks/use-mobile.tsx +19 -0
- package/registry/editor/lib/content.ts +39 -0
- package/registry/editor/lib/cookie-client.ts +19 -0
- package/registry/editor/lib/localstorage-client.ts +19 -0
- package/registry/editor/lib/package.ts +144 -0
- package/registry/editor/lib/preferences-config.ts +72 -0
- package/registry/editor/lib/preferences-storage.ts +20 -0
- package/registry/editor/lib/theme-utils.ts +12 -0
- package/registry/editor/lib/theme.ts +50 -0
- package/registry/editor/lib/tiptap-utils.ts +45 -0
- package/registry/editor/lib/utils.ts +11 -0
- package/registry/editor/page.tsx +9 -0
- package/registry.json +93 -0
- package/reset.d.ts +1 -0
- package/scripts/generate-theme-presets.ts +128 -0
- package/scripts/postCreateCommand.sh +0 -0
- package/scripts/theme-boot.tsx +105 -0
- package/server/server-actions.ts +27 -0
- package/stores/preferences/preferences-provider.tsx +55 -0
- package/stores/preferences/preferences-store.ts +23 -0
- package/styles/globals.css +288 -0
- package/styles/presets/brutalist.css +89 -0
- package/styles/presets/soft-pop.css +89 -0
- package/styles/presets/tangerine.css +89 -0
- 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
package/next.config.mjs
ADDED
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,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
|
|
Binary file
|
|
Binary file
|
package/public/file.svg
ADDED
|
@@ -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>
|
package/public/globe.svg
ADDED
|
@@ -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>
|
package/public/next.svg
ADDED
|
@@ -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
|
+
}
|