vestrycms 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +348 -2
  3. package/dist/config/fields.d.ts +30 -0
  4. package/dist/config/fields.js +75 -0
  5. package/dist/config/index.d.ts +9 -0
  6. package/dist/config/index.js +18 -0
  7. package/dist/config/types.d.ts +403 -0
  8. package/dist/config/types.js +10 -0
  9. package/dist/core/api.d.ts +67 -0
  10. package/dist/core/api.js +538 -0
  11. package/dist/core/auth.d.ts +1561 -0
  12. package/dist/core/auth.js +234 -0
  13. package/dist/core/cache.d.ts +27 -0
  14. package/dist/core/cache.js +52 -0
  15. package/dist/core/entries.d.ts +19 -0
  16. package/dist/core/entries.js +127 -0
  17. package/dist/core/index-types.d.ts +20 -0
  18. package/dist/core/index-types.js +1 -0
  19. package/dist/core/index.d.ts +13 -0
  20. package/dist/core/index.js +13 -0
  21. package/dist/core/mail.d.ts +28 -0
  22. package/dist/core/mail.js +62 -0
  23. package/dist/core/paths.d.ts +35 -0
  24. package/dist/core/paths.js +148 -0
  25. package/dist/core/roles.d.ts +24 -0
  26. package/dist/core/roles.js +15 -0
  27. package/dist/core/service.d.ts +102 -0
  28. package/dist/core/service.js +478 -0
  29. package/dist/core/source.d.ts +82 -0
  30. package/dist/core/source.js +75 -0
  31. package/dist/core/sources/github-app.d.ts +26 -0
  32. package/dist/core/sources/github-app.js +161 -0
  33. package/dist/core/sources/github.d.ts +40 -0
  34. package/dist/core/sources/github.js +278 -0
  35. package/dist/core/sources/local.d.ts +16 -0
  36. package/dist/core/sources/local.js +98 -0
  37. package/dist/core/values.d.ts +22 -0
  38. package/dist/core/values.js +123 -0
  39. package/dist/env.d.ts +19 -0
  40. package/dist/index.d.ts +18 -0
  41. package/dist/index.js +167 -0
  42. package/dist/integration/build-index.d.ts +8 -0
  43. package/dist/integration/build-index.js +67 -0
  44. package/dist/integration/client-deps.d.ts +10 -0
  45. package/dist/integration/client-deps.js +43 -0
  46. package/dist/integration/dev-middleware.d.ts +6 -0
  47. package/dist/integration/dev-middleware.js +48 -0
  48. package/dist/integration/formatter.d.ts +7 -0
  49. package/dist/integration/formatter.js +56 -0
  50. package/dist/mdx/expressions.d.ts +14 -0
  51. package/dist/mdx/expressions.js +141 -0
  52. package/dist/mdx/index.d.ts +4 -0
  53. package/dist/mdx/index.js +4 -0
  54. package/dist/mdx/parse.d.ts +6 -0
  55. package/dist/mdx/parse.js +378 -0
  56. package/dist/mdx/serialize.d.ts +9 -0
  57. package/dist/mdx/serialize.js +396 -0
  58. package/dist/mdx/types.d.ts +40 -0
  59. package/dist/mdx/types.js +6 -0
  60. package/dist/pages/admin.astro +27 -0
  61. package/dist/pages/api.d.ts +3 -0
  62. package/dist/pages/api.js +31 -0
  63. package/dist/ui/App.d.ts +4 -0
  64. package/dist/ui/App.js +70 -0
  65. package/dist/ui/api.d.ts +83 -0
  66. package/dist/ui/api.js +97 -0
  67. package/dist/ui/auth-client.d.ts +27 -0
  68. package/dist/ui/auth-client.js +29 -0
  69. package/dist/ui/components/AssetPicker.d.ts +27 -0
  70. package/dist/ui/components/AssetPicker.js +97 -0
  71. package/dist/ui/components/Icon.d.ts +11 -0
  72. package/dist/ui/components/Icon.js +46 -0
  73. package/dist/ui/components/Layout.d.ts +15 -0
  74. package/dist/ui/components/Layout.js +87 -0
  75. package/dist/ui/components/editor/EditorContext.d.ts +15 -0
  76. package/dist/ui/components/editor/EditorContext.js +9 -0
  77. package/dist/ui/components/editor/NodeViews.d.ts +4 -0
  78. package/dist/ui/components/editor/NodeViews.js +127 -0
  79. package/dist/ui/components/editor/RichTextEditor.d.ts +24 -0
  80. package/dist/ui/components/editor/RichTextEditor.js +253 -0
  81. package/dist/ui/components/editor/extensions.d.ts +20 -0
  82. package/dist/ui/components/editor/extensions.js +156 -0
  83. package/dist/ui/components/fields/CodeInput.d.ts +10 -0
  84. package/dist/ui/components/fields/CodeInput.js +8 -0
  85. package/dist/ui/components/fields/CodeMirrorInput.d.ts +2 -0
  86. package/dist/ui/components/fields/CodeMirrorInput.js +44 -0
  87. package/dist/ui/components/fields/FieldRenderer.d.ts +41 -0
  88. package/dist/ui/components/fields/FieldRenderer.js +299 -0
  89. package/dist/ui/components/fields/ImageTools.d.ts +31 -0
  90. package/dist/ui/components/fields/ImageTools.js +296 -0
  91. package/dist/ui/components/fields/MarkdownField.d.ts +7 -0
  92. package/dist/ui/components/fields/MarkdownField.js +16 -0
  93. package/dist/ui/components/fields/TreeInput.d.ts +13 -0
  94. package/dist/ui/components/fields/TreeInput.js +221 -0
  95. package/dist/ui/components/ui.d.ts +63 -0
  96. package/dist/ui/components/ui.js +113 -0
  97. package/dist/ui/context.d.ts +17 -0
  98. package/dist/ui/context.js +13 -0
  99. package/dist/ui/lib/format.d.ts +5 -0
  100. package/dist/ui/lib/format.js +11 -0
  101. package/dist/ui/lib/utils.d.ts +24 -0
  102. package/dist/ui/lib/utils.js +255 -0
  103. package/dist/ui/main.d.ts +1 -0
  104. package/dist/ui/main.js +11 -0
  105. package/dist/ui/pages/Assets.d.ts +1 -0
  106. package/dist/ui/pages/Assets.js +69 -0
  107. package/dist/ui/pages/CollectionList.d.ts +6 -0
  108. package/dist/ui/pages/CollectionList.js +115 -0
  109. package/dist/ui/pages/Dashboard.d.ts +1 -0
  110. package/dist/ui/pages/Dashboard.js +57 -0
  111. package/dist/ui/pages/EntryEditor.d.ts +3 -0
  112. package/dist/ui/pages/EntryEditor.js +313 -0
  113. package/dist/ui/pages/Login.d.ts +6 -0
  114. package/dist/ui/pages/Login.js +88 -0
  115. package/dist/ui/pages/Users.d.ts +1 -0
  116. package/dist/ui/pages/Users.js +102 -0
  117. package/dist/ui/styles.css +2536 -0
  118. package/package.json +121 -3
@@ -0,0 +1,253 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { TableKit } from "@tiptap/extension-table";
3
+ import Subscript from "@tiptap/extension-subscript";
4
+ import Superscript from "@tiptap/extension-superscript";
5
+ import { Placeholder } from "@tiptap/extensions";
6
+ import { EditorContent, useEditor, useEditorState, } from "@tiptap/react";
7
+ import StarterKit from "@tiptap/starter-kit";
8
+ import { Bold, Code, Code2, Eye, Heading1, Heading2, Heading3, Image as ImageIcon, Italic, Link as LinkIcon, List, ListOrdered, Minus, Plus, Quote, Redo2, Strikethrough, Subscript as SubIcon, Superscript as SupIcon, Table as TableIcon, Undo2, Unlink, } from "lucide-react";
9
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
10
+ import { parseMdx, serializeMdx } from "../../../mdx/index.js";
11
+ import { cx, defaultValues } from "../../lib/utils.js";
12
+ import { AssetPicker } from "../AssetPicker.js";
13
+ import { Icon } from "../Icon.js";
14
+ import { CodeInput } from "../fields/CodeInput.js";
15
+ import { Button, Menu, Modal } from "../ui.js";
16
+ import { EditorEnvContext, useEditorEnv, } from "./EditorContext.js";
17
+ import { MdxComponent, MdxEsm, MdxExpression, MdxImage, MdxInlineComponent, MdxInlineExpression, RawHtml, RawInlineHtml, } from "./extensions.js";
18
+ /**
19
+ * Tiptap editor that reads and writes MDX. The document is parsed once on
20
+ * load; edits are serialized back (debounced) and reported through `onChange`.
21
+ * A source mode exposes the raw MDX in CodeMirror; switching back re-parses.
22
+ */
23
+ export function RichTextEditor(props) {
24
+ return (_jsx(EditorEnvContext.Provider, { value: props.env, children: _jsx(EditorInner, { ...props }) }));
25
+ }
26
+ function EditorInner({ value, onChange, components, env, format = "mdx", placeholder, compact, flush, onUpload, }) {
27
+ const [mode, setMode] = useState("visual");
28
+ const [parseError, setParseError] = useState(null);
29
+ const importsRef = useRef({});
30
+ const lastEmitted = useRef(value);
31
+ const registry = useMemo(() => {
32
+ const out = {};
33
+ for (const [name, c] of Object.entries(env.components)) {
34
+ out[name] = {
35
+ import: c.import,
36
+ namedImport: c.namedImport,
37
+ inline: c.inline,
38
+ children: c.children,
39
+ };
40
+ }
41
+ return out;
42
+ }, [env.components]);
43
+ const parse = useCallback((mdx) => {
44
+ try {
45
+ const parsed = parseMdx(mdx, { format });
46
+ importsRef.current = parsed.imports;
47
+ setParseError(null);
48
+ return parsed.doc;
49
+ }
50
+ catch (e) {
51
+ setParseError(e.message);
52
+ return null;
53
+ }
54
+ }, [format]);
55
+ const serialize = useCallback((doc) => serializeMdx(doc, {
56
+ format,
57
+ components: registry,
58
+ existingImports: importsRef.current,
59
+ }), [format, registry]);
60
+ const initialDoc = useMemo(() => parse(value), []); // eslint-disable-line react-hooks/exhaustive-deps
61
+ const extensions = useMemo(() => [
62
+ StarterKit.configure({
63
+ heading: { levels: [1, 2, 3, 4] },
64
+ link: { openOnClick: false, autolink: true, defaultProtocol: "https" },
65
+ codeBlock: { languageClassPrefix: "language-" },
66
+ dropcursor: { color: "var(--v-primary)", width: 2 },
67
+ }),
68
+ Superscript,
69
+ Subscript,
70
+ MdxImage.configure({
71
+ resolveSrc: (src) => env.resolveImage?.(src) ?? src,
72
+ }),
73
+ TableKit.configure({ table: { resizable: false } }),
74
+ Placeholder.configure({ placeholder: placeholder ?? "Start writing…" }),
75
+ MdxComponent,
76
+ MdxInlineComponent,
77
+ MdxExpression,
78
+ MdxInlineExpression,
79
+ MdxEsm,
80
+ RawHtml,
81
+ RawInlineHtml,
82
+ ], [env, placeholder]);
83
+ const timer = useRef(null);
84
+ const editor = useEditor({
85
+ extensions,
86
+ content: initialDoc ?? { type: "doc", content: [{ type: "paragraph" }] },
87
+ editorProps: { attributes: { class: "v-prose", spellcheck: "true" } },
88
+ onUpdate: ({ editor }) => {
89
+ if (timer.current)
90
+ clearTimeout(timer.current);
91
+ timer.current = setTimeout(() => {
92
+ const mdx = serialize(editor.getJSON());
93
+ lastEmitted.current = mdx;
94
+ onChange(mdx);
95
+ }, 250);
96
+ },
97
+ });
98
+ // External value changes (e.g. after a reload) → re-parse.
99
+ useEffect(() => {
100
+ if (!editor || mode !== "visual")
101
+ return;
102
+ if (value === lastEmitted.current)
103
+ return;
104
+ const doc = parse(value);
105
+ if (doc) {
106
+ lastEmitted.current = value;
107
+ editor.commands.setContent(doc, { emitUpdate: false });
108
+ }
109
+ }, [value, editor, mode, parse]);
110
+ useEffect(() => () => {
111
+ if (timer.current)
112
+ clearTimeout(timer.current);
113
+ }, []);
114
+ const switchMode = (next) => {
115
+ if (next === mode || !editor)
116
+ return;
117
+ if (next === "source") {
118
+ if (timer.current) {
119
+ clearTimeout(timer.current);
120
+ timer.current = null;
121
+ const mdx = serialize(editor.getJSON());
122
+ lastEmitted.current = mdx;
123
+ onChange(mdx);
124
+ }
125
+ setMode("source");
126
+ }
127
+ else {
128
+ const doc = parse(value);
129
+ if (!doc)
130
+ return;
131
+ lastEmitted.current = value;
132
+ editor.commands.setContent(doc, { emitUpdate: false });
133
+ setMode("visual");
134
+ }
135
+ };
136
+ return (_jsxs("div", { className: cx("v-rte", compact && "v-rte--compact", flush && "v-rte--flush"), children: [editor && (_jsx(Toolbar, { editor: editor, mode: mode, onMode: switchMode, components: components ?? Object.keys(env.components), onUpload: onUpload, format: format })), mode === "visual" ? (_jsx(EditorContent, { editor: editor })) : (_jsxs("div", { className: "v-source-editor", children: [parseError && (_jsxs("div", { className: "v-alert v-alert--error", style: { margin: 10 }, children: ["Could not parse MDX: ", parseError] })), _jsx(CodeInput, { value: value, language: "markdown", minHeight: "320px", onChange: (v) => {
137
+ lastEmitted.current = v;
138
+ onChange(v);
139
+ } })] }))] }));
140
+ }
141
+ function Toolbar({ editor, mode, onMode, components, onUpload, format, }) {
142
+ const env = useEditorEnv();
143
+ const [linkOpen, setLinkOpen] = useState(false);
144
+ const [imageOpen, setImageOpen] = useState(false);
145
+ const state = useEditorState({
146
+ editor,
147
+ selector: ({ editor }) => ({
148
+ bold: editor.isActive("bold"),
149
+ italic: editor.isActive("italic"),
150
+ strike: editor.isActive("strike"),
151
+ code: editor.isActive("code"),
152
+ sup: editor.isActive("superscript"),
153
+ sub: editor.isActive("subscript"),
154
+ link: editor.isActive("link"),
155
+ h1: editor.isActive("heading", { level: 1 }),
156
+ h2: editor.isActive("heading", { level: 2 }),
157
+ h3: editor.isActive("heading", { level: 3 }),
158
+ bullet: editor.isActive("bulletList"),
159
+ ordered: editor.isActive("orderedList"),
160
+ quote: editor.isActive("blockquote"),
161
+ codeBlock: editor.isActive("codeBlock"),
162
+ table: editor.isActive("table"),
163
+ canUndo: editor.can().undo(),
164
+ canRedo: editor.can().redo(),
165
+ }),
166
+ });
167
+ const disabled = mode === "source";
168
+ const tool = (label, active, icon, run, isDisabled = false) => (_jsx("button", { type: "button", className: cx("v-rte__tool", active && "is-active"), title: label, "aria-label": label, disabled: disabled || isDisabled, onMouseDown: (e) => e.preventDefault(), onClick: run, children: icon }));
169
+ const insertComponent = (name) => {
170
+ const cfg = env.components[name];
171
+ const props = defaultValues(cfg?.props ?? {});
172
+ if (cfg?.inline) {
173
+ editor
174
+ .chain()
175
+ .focus()
176
+ .insertContent({
177
+ type: "mdxInlineComponent",
178
+ attrs: { name, props, hasChildren: Boolean(cfg.children) },
179
+ content: cfg.children ? [{ type: "text", text: "text" }] : [],
180
+ })
181
+ .run();
182
+ return;
183
+ }
184
+ const node = {
185
+ type: "mdxComponent",
186
+ attrs: { name, props, hasChildren: Boolean(cfg?.children) },
187
+ content: cfg?.children ? [{ type: "paragraph" }] : [],
188
+ };
189
+ editor.chain().focus().insertContent(node).run();
190
+ };
191
+ return (_jsxs("div", { className: "v-rte__toolbar", children: [tool("Undo", false, _jsx(Undo2, {}), () => editor.chain().focus().undo().run(), !state.canUndo), tool("Redo", false, _jsx(Redo2, {}), () => editor.chain().focus().redo().run(), !state.canRedo), _jsx("span", { className: "v-rte__sep" }), tool("Heading 1", state.h1, _jsx(Heading1, {}), () => editor.chain().focus().toggleHeading({ level: 1 }).run()), tool("Heading 2", state.h2, _jsx(Heading2, {}), () => editor.chain().focus().toggleHeading({ level: 2 }).run()), tool("Heading 3", state.h3, _jsx(Heading3, {}), () => editor.chain().focus().toggleHeading({ level: 3 }).run()), _jsx("span", { className: "v-rte__sep" }), tool("Bold", state.bold, _jsx(Bold, {}), () => editor.chain().focus().toggleBold().run()), tool("Italic", state.italic, _jsx(Italic, {}), () => editor.chain().focus().toggleItalic().run()), tool("Strikethrough", state.strike, _jsx(Strikethrough, {}), () => editor.chain().focus().toggleStrike().run()), tool("Inline code", state.code, _jsx(Code, {}), () => editor.chain().focus().toggleCode().run()), format === "mdx" &&
192
+ tool("Superscript", state.sup, _jsx(SupIcon, {}), () => editor.chain().focus().toggleSuperscript().run()), format === "mdx" &&
193
+ tool("Subscript", state.sub, _jsx(SubIcon, {}), () => editor.chain().focus().toggleSubscript().run()), state.link
194
+ ? tool("Remove link", true, _jsx(Unlink, {}), () => editor.chain().focus().unsetLink().run())
195
+ : tool("Link", false, _jsx(LinkIcon, {}), () => setLinkOpen(true)), _jsx("span", { className: "v-rte__sep" }), tool("Bullet list", state.bullet, _jsx(List, {}), () => editor.chain().focus().toggleBulletList().run()), tool("Numbered list", state.ordered, _jsx(ListOrdered, {}), () => editor.chain().focus().toggleOrderedList().run()), tool("Quote", state.quote, _jsx(Quote, {}), () => editor.chain().focus().toggleBlockquote().run()), tool("Code block", state.codeBlock, _jsx(Code2, {}), () => editor.chain().focus().toggleCodeBlock().run()), tool("Divider", false, _jsx(Minus, {}), () => editor.chain().focus().setHorizontalRule().run()), tool("Image", false, _jsx(ImageIcon, {}), () => setImageOpen(true), !env.imageDirectory), _jsx(Menu, { trigger: ({ toggle }) => (_jsx("button", { type: "button", className: cx("v-rte__tool", state.table && "is-active"), title: "Table", disabled: disabled, onMouseDown: (e) => e.preventDefault(), onClick: toggle, children: _jsx(TableIcon, {}) })), children: (close) => (_jsxs(_Fragment, { children: [_jsx("button", { className: "v-menu__item", onClick: () => {
196
+ editor
197
+ .chain()
198
+ .focus()
199
+ .insertTable({ rows: 3, cols: 3, withHeaderRow: true })
200
+ .run();
201
+ close();
202
+ }, children: "Insert table" }), state.table && (_jsxs(_Fragment, { children: [_jsx("button", { className: "v-menu__item", onClick: () => {
203
+ editor.chain().focus().addRowAfter().run();
204
+ close();
205
+ }, children: "Add row below" }), _jsx("button", { className: "v-menu__item", onClick: () => {
206
+ editor.chain().focus().addColumnAfter().run();
207
+ close();
208
+ }, children: "Add column after" }), _jsx("button", { className: "v-menu__item", onClick: () => {
209
+ editor.chain().focus().deleteRow().run();
210
+ close();
211
+ }, children: "Delete row" }), _jsx("button", { className: "v-menu__item", onClick: () => {
212
+ editor.chain().focus().deleteColumn().run();
213
+ close();
214
+ }, children: "Delete column" }), _jsx("button", { className: "v-menu__item is-danger", onClick: () => {
215
+ editor.chain().focus().deleteTable().run();
216
+ close();
217
+ }, children: "Delete table" })] }))] })) }), format === "mdx" && components.length > 0 && (_jsxs(_Fragment, { children: [_jsx("span", { className: "v-rte__sep" }), _jsx(Menu, { trigger: ({ toggle }) => (_jsxs("button", { type: "button", className: "v-rte__insert", disabled: disabled, onMouseDown: (e) => e.preventDefault(), onClick: toggle, children: [_jsx(Plus, {}), " Insert"] })), children: (close) => (_jsxs(_Fragment, { children: [_jsx("div", { className: "v-menu__heading", children: "Components" }), components.map((name) => {
218
+ const c = env.components[name];
219
+ return (_jsxs("button", { className: "v-menu__item", onClick: () => {
220
+ insertComponent(name);
221
+ close();
222
+ }, children: [_jsx(Icon, { name: c?.icon ?? "box" }), _jsxs("span", { children: [c?.label ?? name, c?.description && _jsx("small", { children: c.description })] })] }, name));
223
+ })] })) })] })), _jsx("span", { className: "v-rte__spacer" }), _jsxs("div", { className: "v-segment", style: { height: 30 }, children: [_jsxs("button", { type: "button", className: cx(mode === "visual" && "is-active"), style: { height: 28 }, onClick: () => onMode("visual"), title: "Visual editor", children: [_jsx(Eye, { size: 14, style: { marginRight: 4, verticalAlign: -2 } }), " Visual"] }), _jsxs("button", { type: "button", className: cx(mode === "source" && "is-active"), style: { height: 28 }, onClick: () => onMode("source"), title: "Edit MDX source", children: [_jsx(Code2, { size: 14, style: { marginRight: 4, verticalAlign: -2 } }), " ", "Source"] })] }), linkOpen && (_jsx(LinkDialog, { editor: editor, onClose: () => setLinkOpen(false) })), imageOpen && env.imageDirectory && (_jsx(AssetPicker, { directory: env.imageDirectory, accept: ["jpg", "jpeg", "png", "gif", "webp", "avif", "svg"], title: "Insert image", deferUploads: Boolean(onUpload), onClose: () => setImageOpen(false), onPick: (path, upload) => {
224
+ if (upload)
225
+ onUpload?.(upload);
226
+ editor
227
+ .chain()
228
+ .focus()
229
+ .setImage({ src: env.toDocPath(path), alt: "" })
230
+ .run();
231
+ setImageOpen(false);
232
+ } }))] }));
233
+ }
234
+ function LinkDialog({ editor, onClose, }) {
235
+ const [href, setHref] = useState(() => editor.getAttributes("link").href ?? "");
236
+ const apply = () => {
237
+ const url = href.trim();
238
+ if (!url)
239
+ editor.chain().focus().extendMarkRange("link").unsetLink().run();
240
+ else
241
+ editor
242
+ .chain()
243
+ .focus()
244
+ .extendMarkRange("link")
245
+ .setLink({ href: url })
246
+ .run();
247
+ onClose();
248
+ };
249
+ return (_jsxs(Modal, { title: "Link", onClose: onClose, footer: _jsxs(_Fragment, { children: [_jsx(Button, { onClick: onClose, children: "Cancel" }), _jsx(Button, { variant: "primary", onClick: apply, children: "Apply" })] }), children: [_jsx("label", { className: "v-field__label", htmlFor: "v-link-url", children: "URL" }), _jsx("input", { id: "v-link-url", className: "v-input", autoFocus: true, value: href, placeholder: "https://example.com or /visit", onChange: (e) => setHref(e.target.value), onKeyDown: (e) => {
250
+ if (e.key === "Enter")
251
+ apply();
252
+ } })] }));
253
+ }
@@ -0,0 +1,20 @@
1
+ import { Node } from "@tiptap/core";
2
+ /** A block-level MDX component, e.g. `<Highlights items={[...]} />`. */
3
+ export declare const MdxComponent: Node<any, any>;
4
+ /** An inline MDX component, e.g. `<Abbr title="...">WELS</Abbr>`. */
5
+ export declare const MdxInlineComponent: Node<any, any>;
6
+ /** `{expression}` at block level. */
7
+ export declare const MdxExpression: Node<any, any>;
8
+ /** `export const x = ...` and other ESM that isn't a known import. */
9
+ export declare const MdxEsm: Node<any, any>;
10
+ /** Raw HTML blocks (plain Markdown files). */
11
+ export declare const RawHtml: Node<any, any>;
12
+ export declare const MdxInlineExpression: Node<any, any>;
13
+ export declare const RawInlineHtml: Node<any, any>;
14
+ /**
15
+ * Markdown images are inline. `resolveSrc` maps the stored path (relative to
16
+ * the entry file, or a public URL) to something the browser can load.
17
+ */
18
+ export declare const MdxImage: Node<{
19
+ resolveSrc: (src: string) => string;
20
+ } & Record<string, unknown>, any>;
@@ -0,0 +1,156 @@
1
+ import { Node, mergeAttributes } from "@tiptap/core";
2
+ import Image from "@tiptap/extension-image";
3
+ import { ReactNodeViewRenderer } from "@tiptap/react";
4
+ import { ComponentNodeView, InlineComponentNodeView, RawBlockNodeView, } from "./NodeViews.js";
5
+ /** A block-level MDX component, e.g. `<Highlights items={[...]} />`. */
6
+ export const MdxComponent = Node.create({
7
+ name: "mdxComponent",
8
+ group: "block",
9
+ content: "block*",
10
+ defining: true,
11
+ isolating: true,
12
+ draggable: true,
13
+ selectable: true,
14
+ addAttributes() {
15
+ return {
16
+ name: { default: "" },
17
+ props: { default: {} },
18
+ spreads: { default: [] },
19
+ hasChildren: { default: false },
20
+ };
21
+ },
22
+ parseHTML() {
23
+ return [{ tag: "div[data-mdx-component]" }];
24
+ },
25
+ renderHTML({ HTMLAttributes, node }) {
26
+ return [
27
+ "div",
28
+ mergeAttributes(HTMLAttributes, {
29
+ "data-mdx-component": node.attrs.name,
30
+ }),
31
+ 0,
32
+ ];
33
+ },
34
+ addNodeView() {
35
+ return ReactNodeViewRenderer(ComponentNodeView, {
36
+ contentDOMElementTag: "div",
37
+ });
38
+ },
39
+ });
40
+ /** An inline MDX component, e.g. `<Abbr title="...">WELS</Abbr>`. */
41
+ export const MdxInlineComponent = Node.create({
42
+ name: "mdxInlineComponent",
43
+ group: "inline",
44
+ inline: true,
45
+ content: "inline*",
46
+ selectable: true,
47
+ addAttributes() {
48
+ return {
49
+ name: { default: "" },
50
+ props: { default: {} },
51
+ spreads: { default: [] },
52
+ hasChildren: { default: false },
53
+ };
54
+ },
55
+ parseHTML() {
56
+ return [{ tag: "span[data-mdx-inline-component]" }];
57
+ },
58
+ renderHTML({ HTMLAttributes, node }) {
59
+ return [
60
+ "span",
61
+ mergeAttributes(HTMLAttributes, {
62
+ "data-mdx-inline-component": node.attrs.name,
63
+ class: "v-inline-component",
64
+ "data-name": node.attrs.name,
65
+ }),
66
+ 0,
67
+ ];
68
+ },
69
+ addNodeView() {
70
+ return ReactNodeViewRenderer(InlineComponentNodeView, {
71
+ contentDOMElementTag: "span",
72
+ });
73
+ },
74
+ });
75
+ function rawBlock(name, kind) {
76
+ return Node.create({
77
+ name,
78
+ group: "block",
79
+ atom: true,
80
+ draggable: true,
81
+ selectable: true,
82
+ addAttributes() {
83
+ return { value: { default: "" }, kind: { default: kind } };
84
+ },
85
+ parseHTML() {
86
+ return [{ tag: `div[data-${name}]` }];
87
+ },
88
+ renderHTML({ HTMLAttributes, node }) {
89
+ return [
90
+ "div",
91
+ mergeAttributes(HTMLAttributes, { [`data-${name}`]: "" }),
92
+ node.attrs.value,
93
+ ];
94
+ },
95
+ addNodeView() {
96
+ return ReactNodeViewRenderer(RawBlockNodeView);
97
+ },
98
+ });
99
+ }
100
+ /** `{expression}` at block level. */
101
+ export const MdxExpression = rawBlock("mdxExpression", "expression");
102
+ /** `export const x = ...` and other ESM that isn't a known import. */
103
+ export const MdxEsm = rawBlock("mdxEsm", "esm");
104
+ /** Raw HTML blocks (plain Markdown files). */
105
+ export const RawHtml = rawBlock("rawHtml", "html");
106
+ function rawInline(name) {
107
+ return Node.create({
108
+ name,
109
+ group: "inline",
110
+ inline: true,
111
+ atom: true,
112
+ selectable: true,
113
+ addAttributes() {
114
+ return { value: { default: "" } };
115
+ },
116
+ parseHTML() {
117
+ return [{ tag: `span[data-${name}]` }];
118
+ },
119
+ renderHTML({ HTMLAttributes, node }) {
120
+ return [
121
+ "span",
122
+ mergeAttributes(HTMLAttributes, {
123
+ [`data-${name}`]: "",
124
+ class: "v-inline-expression",
125
+ title: "Click to edit",
126
+ }),
127
+ name === "mdxInlineExpression"
128
+ ? `{${node.attrs.value}}`
129
+ : node.attrs.value,
130
+ ];
131
+ },
132
+ });
133
+ }
134
+ export const MdxInlineExpression = rawInline("mdxInlineExpression");
135
+ export const RawInlineHtml = rawInline("rawInlineHtml");
136
+ /**
137
+ * Markdown images are inline. `resolveSrc` maps the stored path (relative to
138
+ * the entry file, or a public URL) to something the browser can load.
139
+ */
140
+ export const MdxImage = Image.extend({
141
+ addOptions() {
142
+ return {
143
+ ...this.parent?.(),
144
+ inline: true,
145
+ allowBase64: false,
146
+ resolveSrc: (src) => src,
147
+ };
148
+ },
149
+ renderHTML({ HTMLAttributes }) {
150
+ const src = String(HTMLAttributes.src ?? "");
151
+ return [
152
+ "img",
153
+ mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, { src: this.options.resolveSrc(src), "data-src": src }),
154
+ ];
155
+ },
156
+ });
@@ -0,0 +1,10 @@
1
+ export interface CodeInputProps {
2
+ value: string;
3
+ onChange: (v: string) => void;
4
+ language?: "markdown" | "yaml" | "json" | "javascript" | "text";
5
+ rows?: number;
6
+ readOnly?: boolean;
7
+ minHeight?: string;
8
+ }
9
+ /** Code editor (CodeMirror, loaded on demand) with a textarea fallback while it loads. */
10
+ export declare function CodeInput(props: CodeInputProps): import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Suspense, lazy } from "react";
3
+ const CodeMirrorInput = lazy(() => import("./CodeMirrorInput.js"));
4
+ /** Code editor (CodeMirror, loaded on demand) with a textarea fallback while it loads. */
5
+ export function CodeInput(props) {
6
+ const fallback = (_jsx("textarea", { className: "v-textarea v-input--mono", rows: props.rows ?? 6, value: props.value, readOnly: props.readOnly, onChange: (e) => props.onChange(e.target.value), spellCheck: false }));
7
+ return (_jsx(Suspense, { fallback: fallback, children: _jsx(CodeMirrorInput, { ...props }) }));
8
+ }
@@ -0,0 +1,2 @@
1
+ import type { CodeInputProps } from "./CodeInput.js";
2
+ export default function CodeMirrorInput({ value, onChange, language, rows, readOnly, minHeight, }: CodeInputProps): import("react").JSX.Element;
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import CodeMirror from "@uiw/react-codemirror";
3
+ import { markdown } from "@codemirror/lang-markdown";
4
+ import { yaml } from "@codemirror/lang-yaml";
5
+ import { EditorView } from "@codemirror/view";
6
+ import { useMemo } from "react";
7
+ const theme = EditorView.theme({
8
+ "&": {
9
+ fontSize: "13px",
10
+ border: "1px solid var(--v-border-strong)",
11
+ borderRadius: "6px",
12
+ background: "#fff",
13
+ },
14
+ "&.cm-focused": {
15
+ outline: "none",
16
+ borderColor: "var(--v-primary)",
17
+ boxShadow: "0 0 0 3px var(--v-primary-ring)",
18
+ },
19
+ ".cm-scroller": { fontFamily: "var(--v-mono)", lineHeight: "1.55" },
20
+ ".cm-gutters": {
21
+ background: "#f9fafb",
22
+ borderRight: "1px solid var(--v-border)",
23
+ color: "#9ca3af",
24
+ },
25
+ ".cm-activeLineGutter": { background: "#f3f4f6" },
26
+ ".cm-activeLine": { background: "rgba(37, 99, 235, 0.04)" },
27
+ ".cm-content": { padding: "8px 0" },
28
+ });
29
+ export default function CodeMirrorInput({ value, onChange, language = "text", rows, readOnly, minHeight, }) {
30
+ const extensions = useMemo(() => {
31
+ const ext = [theme, EditorView.lineWrapping];
32
+ if (language === "markdown")
33
+ ext.push(markdown());
34
+ if (language === "yaml")
35
+ ext.push(yaml());
36
+ return ext;
37
+ }, [language]);
38
+ return (_jsx(CodeMirror, { value: value, onChange: onChange, extensions: extensions, readOnly: readOnly, editable: !readOnly, minHeight: minHeight ?? (rows ? `${rows * 1.55 * 13 + 16}px` : "120px"), basicSetup: {
39
+ lineNumbers: language !== "text",
40
+ foldGutter: false,
41
+ highlightActiveLine: true,
42
+ autocompletion: false,
43
+ } }));
44
+ }
@@ -0,0 +1,41 @@
1
+ import { type ReactNode } from "react";
2
+ import type { Field, Schema } from "../../../config/types.js";
3
+ import { type ValidationError } from "../../lib/utils.js";
4
+ import { type PendingUpload } from "../AssetPicker.js";
5
+ export interface FieldContext {
6
+ /** Collects files picked through image/file fields so they're committed with the entry. */
7
+ addUpload?: (upload: PendingUpload) => void;
8
+ pendingUploads?: Record<string, PendingUpload>;
9
+ /** Marks the entry as new so slug fields auto-fill. */
10
+ isNew?: boolean;
11
+ /** Values of sibling fields (for slug `from`). */
12
+ siblings?: Record<string, unknown>;
13
+ }
14
+ export interface FieldProps<F extends Field = Field> {
15
+ fieldKey: string;
16
+ path: string;
17
+ field: F;
18
+ value: unknown;
19
+ onChange: (value: unknown) => void;
20
+ errors?: ValidationError[];
21
+ ctx?: FieldContext;
22
+ /** Omit the label (e.g. when the surrounding card already names the field). */
23
+ hideLabel?: boolean;
24
+ }
25
+ /** Renders a schema as a responsive grid of fields. */
26
+ export declare function FieldSet({ schema, values, onChange, errors, filter, ctx, pathPrefix, hideLabels, }: {
27
+ schema: Schema;
28
+ values: Record<string, unknown>;
29
+ onChange: (values: Record<string, unknown>) => void;
30
+ errors?: ValidationError[];
31
+ filter?: (key: string, field: Field) => boolean;
32
+ ctx?: FieldContext;
33
+ pathPrefix?: string;
34
+ /** Keys whose label should be omitted. */
35
+ hideLabels?: string[];
36
+ }): import("react").JSX.Element | null;
37
+ export declare function FieldRenderer(props: FieldProps): import("react").JSX.Element;
38
+ export declare function FieldGroup({ title, children, }: {
39
+ title: ReactNode;
40
+ children: ReactNode;
41
+ }): import("react").JSX.Element;