@nuasite/cms 0.18.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editor.js +44697 -26834
- package/package.json +23 -21
- package/src/build-processor.ts +4 -1
- package/src/collection-scanner.ts +425 -48
- package/src/dev-middleware.ts +26 -203
- package/src/editor/api.ts +1 -22
- package/src/editor/components/ai-chat.tsx +3 -3
- package/src/editor/components/ai-tooltip.tsx +2 -1
- package/src/editor/components/block-editor.tsx +13 -108
- package/src/editor/components/collections-browser.tsx +168 -205
- package/src/editor/components/component-card.tsx +49 -0
- package/src/editor/components/confirm-dialog.tsx +34 -47
- package/src/editor/components/create-page-modal.tsx +529 -101
- package/src/editor/components/delete-page-dialog.tsx +100 -0
- package/src/editor/components/fields.tsx +175 -0
- package/src/editor/components/frontmatter-fields.tsx +281 -70
- package/src/editor/components/frontmatter-sidebar.tsx +223 -0
- package/src/editor/components/highlight-overlay.ts +3 -2
- package/src/editor/components/markdown-editor-overlay.tsx +131 -85
- package/src/editor/components/markdown-inline-editor.tsx +74 -5
- package/src/editor/components/mdx-block-view.tsx +102 -0
- package/src/editor/components/mdx-component-picker.tsx +123 -0
- package/src/editor/components/mdx-props-editor.tsx +94 -0
- package/src/editor/components/media-library.tsx +373 -100
- package/src/editor/components/modal-shell.tsx +87 -0
- package/src/editor/components/prop-editor.tsx +52 -0
- package/src/editor/components/redirect-countdown.tsx +3 -1
- package/src/editor/components/redirects-manager.tsx +269 -0
- package/src/editor/components/reference-picker.tsx +203 -0
- package/src/editor/components/seo-editor.tsx +285 -303
- package/src/editor/components/toast/toast-container.tsx +2 -1
- package/src/editor/components/toolbar.tsx +177 -46
- package/src/editor/constants.ts +26 -0
- package/src/editor/editor.ts +112 -0
- package/src/editor/fetch.ts +62 -0
- package/src/editor/index.tsx +19 -1
- package/src/editor/markdown-api.ts +105 -156
- package/src/editor/milkdown-mdx-plugin.tsx +269 -0
- package/src/editor/signals.ts +206 -13
- package/src/editor/types.ts +52 -1
- package/src/handlers/api-routes.ts +251 -0
- package/src/handlers/component-ops.ts +2 -18
- package/src/handlers/markdown-ops.ts +202 -47
- package/src/handlers/page-ops.ts +229 -0
- package/src/handlers/redirect-ops.ts +163 -0
- package/src/handlers/source-writer.ts +157 -1
- package/src/html-processor.ts +14 -2
- package/src/index.ts +76 -2
- package/src/manifest-writer.ts +19 -1
- package/src/media/contember.ts +2 -1
- package/src/media/local.ts +66 -28
- package/src/media/project-images.ts +81 -0
- package/src/media/s3.ts +32 -11
- package/src/media/types.ts +24 -2
- package/src/shared.ts +27 -0
- package/src/source-finder/collection-finder.ts +219 -41
- package/src/source-finder/index.ts +7 -1
- package/src/source-finder/search-index.ts +178 -36
- package/src/source-finder/snippet-utils.ts +423 -3
- package/src/tsconfig.json +0 -2
- package/src/types.ts +111 -2
- package/src/utils.ts +40 -4
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { useState } from 'preact/hooks'
|
|
2
|
+
import { getComponentDefinitions } from '../manifest'
|
|
3
|
+
import { manifest, mdxComponentPickerOpen } from '../signals'
|
|
4
|
+
import { ComponentCard, getDefaultProps } from './component-card'
|
|
5
|
+
import { CancelButton, ModalBackdrop, ModalHeader } from './modal-shell'
|
|
6
|
+
import { PropEditor } from './prop-editor'
|
|
7
|
+
|
|
8
|
+
export interface MdxComponentPickerProps {
|
|
9
|
+
onInsert: (componentName: string, props: Record<string, string>) => void
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function MdxComponentPicker({ onInsert }: MdxComponentPickerProps) {
|
|
13
|
+
const isOpen = mdxComponentPickerOpen.value
|
|
14
|
+
const [selectedComponent, setSelectedComponent] = useState<string | null>(null)
|
|
15
|
+
const [propValues, setPropValues] = useState<Record<string, string>>({})
|
|
16
|
+
const [searchQuery, setSearchQuery] = useState('')
|
|
17
|
+
|
|
18
|
+
if (!isOpen) return null
|
|
19
|
+
|
|
20
|
+
const componentDefinitions = getComponentDefinitions(manifest.value)
|
|
21
|
+
|
|
22
|
+
const resetSelection = () => {
|
|
23
|
+
setSelectedComponent(null)
|
|
24
|
+
setPropValues({})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const close = () => {
|
|
28
|
+
mdxComponentPickerOpen.value = false
|
|
29
|
+
resetSelection()
|
|
30
|
+
setSearchQuery('')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const handleSelectComponent = (name: string) => {
|
|
34
|
+
const def = componentDefinitions[name]
|
|
35
|
+
if (!def) return
|
|
36
|
+
setSelectedComponent(name)
|
|
37
|
+
setPropValues(getDefaultProps(def))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const handleConfirmInsert = () => {
|
|
41
|
+
if (selectedComponent) {
|
|
42
|
+
onInsert(selectedComponent, propValues)
|
|
43
|
+
close()
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const mdxAllowList = manifest.value?.mdxComponents
|
|
48
|
+
const filteredDefs = Object.values(componentDefinitions).filter((def) => {
|
|
49
|
+
if (mdxAllowList && !mdxAllowList.includes(def.name)) return false
|
|
50
|
+
return !searchQuery || def.name.toLowerCase().includes(searchQuery.toLowerCase())
|
|
51
|
+
|| def.description?.toLowerCase().includes(searchQuery.toLowerCase())
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<ModalBackdrop onClose={close} maxWidth="max-w-md" extraClass="max-h-[80vh] flex flex-col overflow-hidden">
|
|
56
|
+
{selectedComponent
|
|
57
|
+
? (
|
|
58
|
+
<>
|
|
59
|
+
<ModalHeader title={`Configure ${selectedComponent}`} onBack={resetSelection} onClose={close} />
|
|
60
|
+
<div class="p-5 overflow-y-auto flex-1">
|
|
61
|
+
<div class="px-4 py-3 bg-white/10 rounded-cms-md mb-4 text-[13px] text-white">
|
|
62
|
+
Inserting <strong>{selectedComponent}</strong> at cursor position
|
|
63
|
+
</div>
|
|
64
|
+
{(() => {
|
|
65
|
+
const selectedDef = componentDefinitions[selectedComponent]
|
|
66
|
+
if (!selectedDef || selectedDef.props.length === 0) {
|
|
67
|
+
return (
|
|
68
|
+
<div class="text-white/50 text-[13px]">
|
|
69
|
+
This component has no configurable props.
|
|
70
|
+
</div>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
return selectedDef.props.map((prop) => (
|
|
74
|
+
<PropEditor
|
|
75
|
+
key={prop.name}
|
|
76
|
+
prop={prop}
|
|
77
|
+
value={propValues[prop.name] || ''}
|
|
78
|
+
onChange={(value) => setPropValues((prev) => ({ ...prev, [prop.name]: value }))}
|
|
79
|
+
/>
|
|
80
|
+
))
|
|
81
|
+
})()}
|
|
82
|
+
</div>
|
|
83
|
+
<div class="px-5 py-4 border-t border-white/10 flex gap-2 justify-end">
|
|
84
|
+
<CancelButton onClick={resetSelection} label="Back" />
|
|
85
|
+
<button
|
|
86
|
+
onClick={handleConfirmInsert}
|
|
87
|
+
class="px-4 py-2.5 bg-cms-primary text-cms-primary-text rounded-cms-pill cursor-pointer hover:bg-cms-primary-hover transition-all font-medium"
|
|
88
|
+
>
|
|
89
|
+
Insert
|
|
90
|
+
</button>
|
|
91
|
+
</div>
|
|
92
|
+
</>
|
|
93
|
+
)
|
|
94
|
+
: (
|
|
95
|
+
<>
|
|
96
|
+
<ModalHeader title="Insert Component" onClose={close} />
|
|
97
|
+
<div class="px-5 pt-4">
|
|
98
|
+
<input
|
|
99
|
+
type="text"
|
|
100
|
+
value={searchQuery}
|
|
101
|
+
onInput={(e) => setSearchQuery((e.target as HTMLInputElement).value)}
|
|
102
|
+
placeholder="Search components..."
|
|
103
|
+
class="w-full px-4 py-2.5 bg-white/10 border border-white/20 text-[13px] text-white placeholder:text-white/40 outline-none focus:border-white/40 focus:ring-1 focus:ring-white/10 transition-all rounded-cms-md"
|
|
104
|
+
/>
|
|
105
|
+
</div>
|
|
106
|
+
<div class="p-5 overflow-y-auto flex-1">
|
|
107
|
+
{filteredDefs.length === 0
|
|
108
|
+
? (
|
|
109
|
+
<div class="text-center text-white/50 py-8">
|
|
110
|
+
{searchQuery ? 'No components match your search.' : 'No components available.'}
|
|
111
|
+
</div>
|
|
112
|
+
)
|
|
113
|
+
: (
|
|
114
|
+
<div class="flex flex-col gap-2">
|
|
115
|
+
{filteredDefs.map((def) => <ComponentCard key={def.name} def={def} onClick={() => handleSelectComponent(def.name)} />)}
|
|
116
|
+
</div>
|
|
117
|
+
)}
|
|
118
|
+
</div>
|
|
119
|
+
</>
|
|
120
|
+
)}
|
|
121
|
+
</ModalBackdrop>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { useEffect, useState } from 'preact/hooks'
|
|
2
|
+
import { clampPanelPosition, Z_INDEX } from '../constants'
|
|
3
|
+
import { getComponentDefinition } from '../manifest'
|
|
4
|
+
import { closeMdxPropsEditor, manifest, mdxPropsEditorState } from '../signals'
|
|
5
|
+
import { MdxComponentIcon } from './mdx-block-view'
|
|
6
|
+
import { CancelButton, CloseButton } from './modal-shell'
|
|
7
|
+
import { PropEditor } from './prop-editor'
|
|
8
|
+
|
|
9
|
+
export function MdxPropsEditor({
|
|
10
|
+
onUpdateProps,
|
|
11
|
+
}: {
|
|
12
|
+
onUpdateProps: (nodePos: number, props: Record<string, string>) => void
|
|
13
|
+
}) {
|
|
14
|
+
const state = mdxPropsEditorState.value
|
|
15
|
+
const isVisible = state.isOpen && state.componentName !== null && state.nodePos !== null
|
|
16
|
+
|
|
17
|
+
const definition = isVisible ? getComponentDefinition(manifest.value, state.componentName!) : undefined
|
|
18
|
+
const [propValues, setPropValues] = useState<Record<string, string>>(state.props)
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (isVisible) setPropValues(state.props)
|
|
22
|
+
}, [state.nodePos, state.componentName, state.props, isVisible])
|
|
23
|
+
|
|
24
|
+
if (!isVisible) return null
|
|
25
|
+
|
|
26
|
+
const panelStyle = state.cursorPos ? clampPanelPosition(state.cursorPos, 360) : {}
|
|
27
|
+
|
|
28
|
+
const handleSave = () => {
|
|
29
|
+
if (state.nodePos !== null) {
|
|
30
|
+
onUpdateProps(state.nodePos, propValues)
|
|
31
|
+
closeMdxPropsEditor()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<>
|
|
37
|
+
<div
|
|
38
|
+
data-cms-ui
|
|
39
|
+
onClick={closeMdxPropsEditor}
|
|
40
|
+
style={{ zIndex: Z_INDEX.SELECTION }}
|
|
41
|
+
class="fixed inset-0"
|
|
42
|
+
/>
|
|
43
|
+
|
|
44
|
+
<div
|
|
45
|
+
data-cms-ui
|
|
46
|
+
onClick={(e: MouseEvent) => e.stopPropagation()}
|
|
47
|
+
class="fixed w-90 bg-cms-dark shadow-[0_8px_32px_rgba(0,0,0,0.4)] font-sans text-sm overflow-hidden flex flex-col rounded-cms-xl border border-white/10"
|
|
48
|
+
style={{ ...panelStyle, zIndex: Z_INDEX.MODAL }}
|
|
49
|
+
>
|
|
50
|
+
<div class="px-5 py-4 flex justify-between items-center border-b border-white/10">
|
|
51
|
+
<div class="flex items-center gap-2">
|
|
52
|
+
<MdxComponentIcon />
|
|
53
|
+
<span class="font-semibold text-white">{state.componentName}</span>
|
|
54
|
+
</div>
|
|
55
|
+
<CloseButton onClick={closeMdxPropsEditor} />
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<div class="p-5 overflow-y-auto flex-1">
|
|
59
|
+
{definition
|
|
60
|
+
? (
|
|
61
|
+
definition.props.map((prop) => (
|
|
62
|
+
<PropEditor
|
|
63
|
+
key={prop.name}
|
|
64
|
+
prop={prop}
|
|
65
|
+
value={propValues[prop.name] || ''}
|
|
66
|
+
onChange={(value) => setPropValues((prev) => ({ ...prev, [prop.name]: value }))}
|
|
67
|
+
/>
|
|
68
|
+
))
|
|
69
|
+
)
|
|
70
|
+
: (
|
|
71
|
+
<div class="text-white/50 text-[13px]">
|
|
72
|
+
<div class="mb-3">Unknown component — props not editable.</div>
|
|
73
|
+
<div class="font-mono text-[11px] text-white/30 bg-white/5 p-3 rounded-cms-md break-all">
|
|
74
|
+
{Object.entries(propValues).map(([k, v]) => <div key={k}>{k}="{v}"</div>)}
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
{definition && (
|
|
81
|
+
<div class="px-5 py-4 border-t border-white/10 flex gap-2 justify-end">
|
|
82
|
+
<CancelButton onClick={closeMdxPropsEditor} />
|
|
83
|
+
<button
|
|
84
|
+
onClick={handleSave}
|
|
85
|
+
class="px-4 py-2.5 bg-cms-primary text-cms-primary-text rounded-cms-pill cursor-pointer hover:bg-cms-primary-hover transition-all font-medium"
|
|
86
|
+
>
|
|
87
|
+
Save
|
|
88
|
+
</button>
|
|
89
|
+
</div>
|
|
90
|
+
)}
|
|
91
|
+
</div>
|
|
92
|
+
</>
|
|
93
|
+
)
|
|
94
|
+
}
|