neuphlo-editor 0.3.13 → 0.4.2

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/README.md CHANGED
@@ -1,27 +1,20 @@
1
- # Neuphlo Editor
1
+ Neuphlo Editor
2
+ ===============
2
3
 
3
- This repo is the `neuphlo-editor` package (TipTap preset + React wrapper).
4
+ Lightweight React wrapper around Tiptap with sensible defaults.
4
5
 
5
- ## Prerequisites
6
+ CSS integration
7
+ ----------------
6
8
 
7
- - Node 18+ (recommended) and npm 8+
8
- - React 19+ in your app
9
+ - Import global CSS once near your app root:
10
+ import 'neuphlo-editor/styles.css';
9
11
 
10
- ## Install & Run (local dev)
12
+ Highlight.js theme (optional)
13
+ -----------------------------
14
+ - We no longer inject a theme by default. If you want a default theme, also import:
15
+ import 'neuphlo-editor/highlight.css';
16
+ - Or import any other `highlight.js` theme you prefer.
11
17
 
12
- - Install dependencies: `npm install`
13
- - Start local dev (watch): `npm run dev`
14
- - The package builds in watch mode via `tsup`.
15
-
16
- ## Where Things Live
17
-
18
- - Source: `src`
19
- - Build output: `dist`
20
- - Exports: JS (ESM/CJS), types, and `styles.css`
21
-
22
- ## Using In Your App
23
-
24
- - Install the package in your app, then import:
25
- - `import { Editor } from 'neuphlo-editor/react'`
26
- - `import 'neuphlo-editor/styles.css'`
27
- - Or bring your own styles and set `className` on the component.
18
+ Notes
19
+ -----
20
+ - All styles are namespaced (e.g., `.nph-editor`) to avoid colliding with your app.
@@ -0,0 +1,366 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all2) => {
6
+ for (var name in all2)
7
+ __defProp(target, name, { get: all2[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
18
+
19
+ // src/headless/index.ts
20
+ import { useCurrentEditor as useCurrentEditor4 } from "@tiptap/react";
21
+
22
+ // src/headless/components/editor.tsx
23
+ import { EditorProvider } from "@tiptap/react";
24
+ import { forwardRef } from "react";
25
+ import { Provider } from "jotai";
26
+
27
+ // src/headless/utils/store.ts
28
+ var store_exports = {};
29
+ __export(store_exports, {
30
+ novelStore: () => novelStore
31
+ });
32
+ __reExport(store_exports, jotai_star);
33
+ import { createStore } from "jotai";
34
+ import * as jotai_star from "jotai";
35
+ var novelStore = createStore();
36
+
37
+ // src/headless/components/editor.tsx
38
+ import { jsx } from "react/jsx-runtime";
39
+ var EditorRoot = ({ children }) => {
40
+ return /* @__PURE__ */ jsx(Provider, { store: novelStore, children });
41
+ };
42
+ var EditorContent = forwardRef(
43
+ ({ className, children, initialContent, content, ...rest }, ref) => {
44
+ const effectiveContent = content ?? initialContent;
45
+ return /* @__PURE__ */ jsx("div", { ref, className, children: /* @__PURE__ */ jsx(EditorProvider, { ...rest, content: effectiveContent, children }) });
46
+ }
47
+ );
48
+ EditorContent.displayName = "EditorContent";
49
+
50
+ // src/headless/components/editor-bubble.tsx
51
+ import { useCurrentEditor } from "@tiptap/react";
52
+ import { BubbleMenu as BubbleMenuReact } from "@tiptap/react/menus";
53
+ import { jsx as jsx2 } from "react/jsx-runtime";
54
+ function EditorBubble({ className, children, options }) {
55
+ const { editor } = useCurrentEditor();
56
+ if (!editor) return null;
57
+ return /* @__PURE__ */ jsx2(BubbleMenuReact, { editor, options, children: /* @__PURE__ */ jsx2("div", { className, children }) });
58
+ }
59
+
60
+ // src/headless/components/editor-bubble-item.tsx
61
+ import { forwardRef as forwardRef2, isValidElement, cloneElement } from "react";
62
+ import { useCurrentEditor as useCurrentEditor2 } from "@tiptap/react";
63
+ import { jsx as jsx3 } from "react/jsx-runtime";
64
+ var EditorBubbleItem = forwardRef2(({ children, asChild, onSelect, ...rest }, ref) => {
65
+ const { editor } = useCurrentEditor2();
66
+ if (!editor) return null;
67
+ const handleClick = (e) => {
68
+ e.preventDefault();
69
+ onSelect?.(editor);
70
+ };
71
+ if (asChild && isValidElement(children)) {
72
+ const child = children;
73
+ const childOnClick = child.props?.onClick;
74
+ const mergedOnClick = (e) => {
75
+ childOnClick?.(e);
76
+ if (!e?.defaultPrevented) onSelect?.(editor);
77
+ };
78
+ return cloneElement(child, {
79
+ ...rest,
80
+ ref: child.ref ?? ref,
81
+ onClick: mergedOnClick
82
+ });
83
+ }
84
+ return /* @__PURE__ */ jsx3("div", { ref, ...rest, onClick: handleClick, children });
85
+ });
86
+ EditorBubbleItem.displayName = "EditorBubbleItem";
87
+
88
+ // src/headless/components/editor-command.tsx
89
+ import { useAtom, useSetAtom } from "jotai";
90
+ import { useEffect, forwardRef as forwardRef3 } from "react";
91
+ import { Command } from "cmdk";
92
+
93
+ // src/headless/utils/atoms.ts
94
+ import { atom } from "jotai";
95
+ var queryAtom = atom("");
96
+ var rangeAtom = atom(null);
97
+
98
+ // src/headless/components/editor-command.tsx
99
+ import tunnel from "tunnel-rat";
100
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
101
+ var commandTunnel = tunnel();
102
+ var EditorCommandOut = ({
103
+ query,
104
+ range
105
+ }) => {
106
+ const setQuery = useSetAtom(queryAtom, { store: novelStore });
107
+ const setRange = useSetAtom(rangeAtom, { store: novelStore });
108
+ useEffect(() => {
109
+ setQuery(query);
110
+ }, [query, setQuery]);
111
+ useEffect(() => {
112
+ setRange(range);
113
+ }, [range, setRange]);
114
+ useEffect(() => {
115
+ const navigationKeys = ["ArrowUp", "ArrowDown", "Enter"];
116
+ const onKeyDown = (e) => {
117
+ if (navigationKeys.includes(e.key)) {
118
+ e.preventDefault();
119
+ const commandRef = document.querySelector("#slash-command");
120
+ if (commandRef)
121
+ commandRef.dispatchEvent(
122
+ new KeyboardEvent("keydown", {
123
+ key: e.key,
124
+ cancelable: true,
125
+ bubbles: true
126
+ })
127
+ );
128
+ return false;
129
+ }
130
+ };
131
+ document.addEventListener("keydown", onKeyDown);
132
+ return () => {
133
+ document.removeEventListener("keydown", onKeyDown);
134
+ };
135
+ }, []);
136
+ return /* @__PURE__ */ jsx4(commandTunnel.Out, {});
137
+ };
138
+ var CommandAny = Command;
139
+ var EditorCommand = forwardRef3(
140
+ ({ children, className, ...rest }, ref) => {
141
+ const [query, setQuery] = useAtom(queryAtom);
142
+ return /* @__PURE__ */ jsx4(commandTunnel.In, { children: /* @__PURE__ */ jsxs(
143
+ CommandAny,
144
+ {
145
+ ref,
146
+ onKeyDown: (e) => {
147
+ e.stopPropagation();
148
+ },
149
+ id: "slash-command",
150
+ className,
151
+ ...rest,
152
+ children: [
153
+ /* @__PURE__ */ jsx4(
154
+ CommandAny.Input,
155
+ {
156
+ value: query,
157
+ onValueChange: setQuery,
158
+ style: { display: "none" }
159
+ }
160
+ ),
161
+ children
162
+ ]
163
+ }
164
+ ) });
165
+ }
166
+ );
167
+ var EditorCommandList = Command.List;
168
+ EditorCommand.displayName = "EditorCommand";
169
+
170
+ // src/headless/components/editor-command-item.tsx
171
+ import { forwardRef as forwardRef4 } from "react";
172
+ import { CommandEmpty, CommandItem } from "cmdk";
173
+ import { useCurrentEditor as useCurrentEditor3 } from "@tiptap/react";
174
+ import { useAtomValue } from "jotai";
175
+ import { jsx as jsx5 } from "react/jsx-runtime";
176
+ var CommandItemAny = CommandItem;
177
+ var EditorCommandItem = forwardRef4(({ children, onCommand, ...rest }, ref) => {
178
+ const { editor } = useCurrentEditor3();
179
+ const range = useAtomValue(rangeAtom);
180
+ if (!editor || !range) return null;
181
+ return /* @__PURE__ */ jsx5(
182
+ CommandItemAny,
183
+ {
184
+ ref,
185
+ ...rest,
186
+ onSelect: () => onCommand({ editor, range }),
187
+ children
188
+ }
189
+ );
190
+ });
191
+ EditorCommandItem.displayName = "EditorCommandItem";
192
+
193
+ // src/headless/extensions/index.ts
194
+ import { StarterKit } from "@tiptap/starter-kit";
195
+ import { Placeholder } from "@tiptap/extension-placeholder";
196
+
197
+ // src/headless/extensions/CodeBlock/CodeBlock.ts
198
+ import { CodeBlockLowlight } from "@tiptap/extension-code-block-lowlight";
199
+ import { all, createLowlight } from "lowlight";
200
+ var lowlight = createLowlight(all);
201
+ var CodeBlock = CodeBlockLowlight.configure({
202
+ lowlight
203
+ });
204
+
205
+ // src/headless/extensions/Link/Link.ts
206
+ import { mergeAttributes } from "@tiptap/core";
207
+ import TiptapLink from "@tiptap/extension-link";
208
+ import { Plugin } from "@tiptap/pm/state";
209
+ var Link = TiptapLink.extend({
210
+ inclusive: false,
211
+ parseHTML() {
212
+ return [
213
+ {
214
+ tag: 'a[href]:not([data-type="button"]):not([href *= "javascript:" i])'
215
+ }
216
+ ];
217
+ },
218
+ renderHTML({ HTMLAttributes }) {
219
+ return [
220
+ "a",
221
+ mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
222
+ class: "link"
223
+ }),
224
+ 0
225
+ ];
226
+ },
227
+ addProseMirrorPlugins() {
228
+ const { editor } = this;
229
+ return [
230
+ ...this.parent?.() || [],
231
+ new Plugin({
232
+ props: {
233
+ handleKeyDown: (view, event) => {
234
+ const { selection } = editor.state;
235
+ if (event.key === "Escape" && selection.empty !== true) {
236
+ editor.commands.focus(selection.to, { scrollIntoView: false });
237
+ }
238
+ return false;
239
+ }
240
+ }
241
+ })
242
+ ];
243
+ }
244
+ });
245
+
246
+ // src/headless/extensions/slash-command.tsx
247
+ import { ReactRenderer } from "@tiptap/react";
248
+ import Suggestion from "@tiptap/suggestion";
249
+ import { Extension } from "@tiptap/core";
250
+ var Command2 = Extension.create({
251
+ name: "slash-command",
252
+ addOptions() {
253
+ return {
254
+ suggestion: {
255
+ char: "/",
256
+ command: (ctx) => {
257
+ ctx.props.command({ editor: ctx.editor, range: ctx.range });
258
+ }
259
+ }
260
+ };
261
+ },
262
+ addProseMirrorPlugins() {
263
+ const base = this.options.suggestion ?? {};
264
+ return [
265
+ Suggestion({
266
+ editor: this.editor,
267
+ char: base.char ?? "/",
268
+ items: base.items ?? (() => ["/"]),
269
+ command: (ctx) => {
270
+ if (typeof ctx?.props?.command === "function") {
271
+ ctx.props.command({ editor: ctx.editor, range: ctx.range });
272
+ }
273
+ },
274
+ ...base
275
+ })
276
+ ];
277
+ }
278
+ });
279
+ var renderItems = (elementRef) => {
280
+ let component = null;
281
+ let container = null;
282
+ const destroy = () => {
283
+ component?.destroy();
284
+ component = null;
285
+ if (container) {
286
+ container.remove();
287
+ container = null;
288
+ }
289
+ };
290
+ const updatePosition = (clientRect) => {
291
+ if (!container || !clientRect) return;
292
+ const top = Math.round(clientRect.bottom + 8);
293
+ const left = Math.round(clientRect.left);
294
+ container.style.top = `${top}px`;
295
+ container.style.left = `${left}px`;
296
+ };
297
+ return {
298
+ onStart: (props) => {
299
+ const { selection } = props.editor.state;
300
+ const parentNode = selection.$from.node(selection.$from.depth);
301
+ const blockType = parentNode.type.name;
302
+ if (blockType === "codeBlock") return false;
303
+ component = new ReactRenderer(EditorCommandOut, {
304
+ props: {
305
+ query: props.query ?? "",
306
+ range: props.range
307
+ },
308
+ editor: props.editor
309
+ });
310
+ container = document.createElement("div");
311
+ container.style.position = "fixed";
312
+ container.style.zIndex = "9999";
313
+ container.style.minWidth = "240px";
314
+ (elementRef?.current ?? document.body).appendChild(container);
315
+ container.appendChild(component.element);
316
+ const rect = typeof props.clientRect === "function" ? props.clientRect() : null;
317
+ if (rect) updatePosition(rect);
318
+ },
319
+ onUpdate: (props) => {
320
+ component?.updateProps({
321
+ query: props.query ?? "",
322
+ range: props.range
323
+ });
324
+ const rect = typeof props.clientRect === "function" ? props.clientRect() : null;
325
+ if (rect) updatePosition(rect);
326
+ },
327
+ onKeyDown: ({ event }) => {
328
+ if (event.key === "Escape") {
329
+ destroy();
330
+ return true;
331
+ }
332
+ return false;
333
+ },
334
+ onExit: () => {
335
+ destroy();
336
+ }
337
+ };
338
+ };
339
+ var createSuggestionItems = (items) => items;
340
+ var handleCommandNavigation = (event) => {
341
+ if (["ArrowUp", "ArrowDown", "Enter"].includes(event.key)) {
342
+ const slashCommand = document.querySelector("#slash-command");
343
+ if (slashCommand) return true;
344
+ }
345
+ };
346
+
347
+ export {
348
+ EditorRoot,
349
+ EditorContent,
350
+ EditorBubble,
351
+ EditorBubbleItem,
352
+ EditorCommandOut,
353
+ EditorCommand,
354
+ EditorCommandList,
355
+ EditorCommandItem,
356
+ CodeBlock,
357
+ Link,
358
+ StarterKit,
359
+ Placeholder,
360
+ Command2 as Command,
361
+ renderItems,
362
+ createSuggestionItems,
363
+ handleCommandNavigation,
364
+ useCurrentEditor4 as useCurrentEditor
365
+ };
366
+ //# sourceMappingURL=chunk-M6GRNJJ6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/headless/index.ts","../src/headless/components/editor.tsx","../src/headless/utils/store.ts","../src/headless/components/editor-bubble.tsx","../src/headless/components/editor-bubble-item.tsx","../src/headless/components/editor-command.tsx","../src/headless/utils/atoms.ts","../src/headless/components/editor-command-item.tsx","../src/headless/extensions/index.ts","../src/headless/extensions/CodeBlock/CodeBlock.ts","../src/headless/extensions/Link/Link.ts","../src/headless/extensions/slash-command.tsx"],"sourcesContent":["export { useCurrentEditor as useEditor } from \"@tiptap/react\"\n\nexport {\n EditorRoot,\n EditorContent,\n type EditorContentProps,\n} from \"./components/editor\"\n\nexport { EditorBubble } from \"./components/editor-bubble\"\nexport { EditorBubbleItem } from \"./components/editor-bubble-item\"\nexport {\n EditorCommand,\n EditorCommandList,\n EditorCommandOut,\n} from \"./components/editor-command\"\nexport { EditorCommandItem } from \"./components/editor-command-item\"\n\nexport { Placeholder, StarterKit } from \"./extensions\"\nexport {\n Command as SlashCommand,\n renderItems as renderSlashCommandItems,\n createSuggestionItems,\n handleCommandNavigation,\n} from \"./extensions/slash-command\"\n// Path without extension to satisfy TS/tsup\n// (the file is at ./extensions/slash-command.tsx)\n// eslint-disable-next-line\n","import { EditorProvider } from \"@tiptap/react\"\nimport type { EditorProviderProps } from \"@tiptap/react\"\nimport { forwardRef } from \"react\"\nimport type { FC, ReactNode } from \"react\"\nimport { Provider } from \"jotai\"\nimport { novelStore } from \"../utils/store\"\n\nexport interface EditorRootProps {\n readonly children: ReactNode\n}\n\nexport const EditorRoot: FC<EditorRootProps> = ({ children }) => {\n return (\n <Provider store={novelStore as any}>\n {children as any}\n </Provider>\n )\n}\n\nexport type EditorContentProps = EditorProviderProps & {\n readonly children?: ReactNode\n readonly className?: string\n readonly initialContent?: any\n}\n\nexport const EditorContent = forwardRef<HTMLDivElement, EditorContentProps>(\n ({ className, children, initialContent, content, ...rest }, ref) => {\n const effectiveContent = content ?? initialContent\n return (\n <div ref={ref} className={className}>\n <EditorProvider {...rest} content={effectiveContent}>\n {children}\n </EditorProvider>\n </div>\n )\n }\n)\n\nEditorContent.displayName = \"EditorContent\"\n","import { createStore } from \"jotai\";\n\n// biome-ignore lint/suspicious/noExplicitAny: store is opaque to consumers\nexport const novelStore: any = createStore();\nexport * from \"jotai\";\n\n","import { useCurrentEditor } from \"@tiptap/react\";\nimport { BubbleMenu as BubbleMenuReact } from \"@tiptap/react/menus\";\nimport type { ReactNode } from \"react\";\n\nexport interface EditorBubbleProps {\n readonly className?: string;\n readonly children: ReactNode;\n readonly options?: Record<string, unknown>;\n}\n\nexport function EditorBubble({ className, children, options }: EditorBubbleProps) {\n const { editor } = useCurrentEditor();\n if (!editor) return null;\n\n return (\n <BubbleMenuReact editor={editor} options={options}>\n <div className={className}>{children}</div>\n </BubbleMenuReact>\n );\n}\n","import { forwardRef, isValidElement, cloneElement } from \"react\"\nimport type { ComponentPropsWithoutRef, ReactElement, ReactNode } from \"react\"\nimport { useCurrentEditor } from \"@tiptap/react\"\nimport type { Editor as TiptapEditor } from \"@tiptap/react\"\n\ninterface EditorBubbleItemProps {\n readonly children: ReactNode\n readonly asChild?: boolean\n readonly onSelect?: (editor: TiptapEditor) => void\n}\n\nexport const EditorBubbleItem = forwardRef<\n HTMLDivElement,\n EditorBubbleItemProps & Omit<ComponentPropsWithoutRef<\"div\">, \"onSelect\">\n>(({ children, asChild, onSelect, ...rest }, ref) => {\n const { editor } = useCurrentEditor()\n\n if (!editor) return null\n\n const handleClick = (e: React.MouseEvent) => {\n e.preventDefault()\n onSelect?.(editor)\n }\n\n if (asChild && isValidElement(children)) {\n const child = children as ReactElement<any>\n const childOnClick = (child.props as any)?.onClick as\n | ((e: any) => void)\n | undefined\n const mergedOnClick = (e: any) => {\n childOnClick?.(e)\n if (!e?.defaultPrevented) onSelect?.(editor)\n }\n\n return cloneElement(child, {\n ...rest,\n ref: (child as any).ref ?? ref,\n onClick: mergedOnClick,\n })\n }\n\n return (\n <div ref={ref} {...rest} onClick={handleClick}>\n {children}\n </div>\n )\n})\n\nEditorBubbleItem.displayName = \"EditorBubbleItem\"\n\nexport default EditorBubbleItem\n","import { useAtom, useSetAtom } from \"jotai\"\nimport { useEffect, forwardRef } from \"react\"\nimport { Command } from \"cmdk\"\nimport { queryAtom, rangeAtom } from \"../utils/atoms\"\nimport { novelStore } from \"../utils/store\"\nimport type { FC } from \"react\"\nimport type { Range } from \"@tiptap/core\"\nimport tunnel from \"tunnel-rat\"\n\nconst commandTunnel: any = (tunnel as any)()\n\ninterface EditorCommandOutProps {\n readonly query: string\n readonly range: Range\n}\n\nexport const EditorCommandOut: FC<EditorCommandOutProps> = ({\n query,\n range,\n}) => {\n const setQuery = useSetAtom(queryAtom, { store: novelStore })\n const setRange = useSetAtom(rangeAtom, { store: novelStore })\n\n useEffect(() => {\n setQuery(query)\n }, [query, setQuery])\n\n useEffect(() => {\n setRange(range)\n }, [range, setRange])\n\n useEffect(() => {\n const navigationKeys = [\"ArrowUp\", \"ArrowDown\", \"Enter\"]\n const onKeyDown = (e: KeyboardEvent) => {\n if (navigationKeys.includes(e.key)) {\n e.preventDefault()\n const commandRef = document.querySelector(\"#slash-command\")\n\n if (commandRef)\n commandRef.dispatchEvent(\n new KeyboardEvent(\"keydown\", {\n key: e.key,\n cancelable: true,\n bubbles: true,\n })\n )\n\n return false\n }\n }\n document.addEventListener(\"keydown\", onKeyDown)\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown)\n }\n }, [])\n\n return <commandTunnel.Out />\n}\n\nconst CommandAny: any = Command\nexport const EditorCommand = forwardRef<HTMLDivElement, any>(\n ({ children, className, ...rest }, ref) => {\n const [query, setQuery] = useAtom(queryAtom)\n\n return (\n <commandTunnel.In>\n <CommandAny\n ref={ref}\n onKeyDown={(e: any) => {\n e.stopPropagation()\n }}\n id=\"slash-command\"\n className={className}\n {...rest}\n >\n <CommandAny.Input\n value={query}\n onValueChange={setQuery}\n style={{ display: \"none\" }}\n />\n {children}\n </CommandAny>\n </commandTunnel.In>\n )\n }\n)\nexport const EditorCommandList: any = Command.List\n\nEditorCommand.displayName = \"EditorCommand\"\n","import { atom } from \"jotai\"\nimport type { Range } from \"@tiptap/core\"\n\nexport const queryAtom = atom(\"\")\nexport const rangeAtom = atom<Range | null>(null)\n\n","import { forwardRef } from \"react\"\nimport { CommandEmpty, CommandItem } from \"cmdk\"\nimport { useCurrentEditor } from \"@tiptap/react\"\nimport { useAtomValue } from \"jotai\"\nimport { rangeAtom } from \"../utils/atoms\"\nimport type { Editor, Range } from \"@tiptap/core\"\n\ninterface EditorCommandItemProps {\n readonly onCommand: ({\n editor,\n range,\n }: {\n editor: Editor\n range: Range\n }) => void\n}\n\nconst CommandItemAny: any = CommandItem\nconst CommandEmptyAny: any = CommandEmpty\n\nexport const EditorCommandItem = forwardRef<\n HTMLDivElement,\n EditorCommandItemProps & any\n>(({ children, onCommand, ...rest }, ref) => {\n const { editor } = useCurrentEditor()\n const range = useAtomValue(rangeAtom)\n\n if (!editor || !range) return null\n\n return (\n <CommandItemAny\n ref={ref}\n {...(rest as any)}\n onSelect={() => onCommand({ editor, range })}\n >\n {children}\n </CommandItemAny>\n )\n})\n\nEditorCommandItem.displayName = \"EditorCommandItem\"\n\nexport const EditorCommandEmpty: any = CommandEmptyAny\n\nexport default EditorCommandItem\n","export { StarterKit } from \"@tiptap/starter-kit\"\nexport { Placeholder } from \"@tiptap/extension-placeholder\"\n\n// Custom\nexport { CodeBlock } from \"./CodeBlock\"\nexport { Link } from \"./Link\"\n","import { CodeBlockLowlight } from \"@tiptap/extension-code-block-lowlight\"\nimport { all, createLowlight } from \"lowlight\"\n\nconst lowlight = createLowlight(all)\n\nexport const CodeBlock = CodeBlockLowlight.configure({\n lowlight,\n})\n","import { mergeAttributes } from \"@tiptap/core\"\nimport TiptapLink from \"@tiptap/extension-link\"\nimport { Plugin } from \"@tiptap/pm/state\"\nimport { EditorView } from \"@tiptap/pm/view\"\n\nexport const Link = TiptapLink.extend({\n inclusive: false,\n\n parseHTML() {\n return [\n {\n tag: 'a[href]:not([data-type=\"button\"]):not([href *= \"javascript:\" i])',\n },\n ]\n },\n\n renderHTML({ HTMLAttributes }: any) {\n return [\n \"a\",\n mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {\n class: \"link\",\n }),\n 0,\n ]\n },\n\n addProseMirrorPlugins() {\n const { editor } = this\n\n return [\n ...(((this as any).parent?.() as any[]) || []),\n new Plugin({\n props: {\n handleKeyDown: (view: EditorView, event: KeyboardEvent) => {\n const { selection } = editor.state\n\n if (event.key === \"Escape\" && selection.empty !== true) {\n editor.commands.focus(selection.to, { scrollIntoView: false })\n }\n\n return false\n },\n },\n }),\n ]\n },\n})\n\nexport default Link\n","import { ReactRenderer } from \"@tiptap/react\"\nimport Suggestion from \"@tiptap/suggestion\"\nimport { Extension } from \"@tiptap/core\"\nimport type { RefObject, ReactNode } from \"react\"\nimport { EditorCommandOut } from \"../components/editor-command\"\n\nexport const Command = Extension.create({\n name: \"slash-command\",\n addOptions() {\n return {\n suggestion: {\n char: \"/\",\n command: (ctx: any) => {\n ctx.props.command({ editor: ctx.editor, range: ctx.range })\n },\n } as any,\n }\n },\n addProseMirrorPlugins() {\n const base: any = this.options.suggestion ?? {}\n return [\n Suggestion({\n editor: this.editor,\n char: base.char ?? \"/\",\n items: base.items ?? (() => [\"/\"] as any),\n command: (ctx: any) => {\n if (typeof ctx?.props?.command === \"function\") {\n ctx.props.command({ editor: ctx.editor, range: ctx.range })\n }\n },\n ...base,\n }),\n ]\n },\n})\n\nexport const renderItems = (elementRef?: RefObject<Element> | null) => {\n let component: ReactRenderer | null = null\n let container: HTMLElement | null = null\n\n const destroy = () => {\n component?.destroy()\n component = null\n if (container) {\n container.remove()\n container = null\n }\n }\n\n const updatePosition = (clientRect?: DOMRect | null) => {\n if (!container || !clientRect) return\n const top = Math.round(clientRect.bottom + 8)\n const left = Math.round(clientRect.left)\n container.style.top = `${top}px`\n container.style.left = `${left}px`\n }\n\n return {\n onStart: (props: {\n editor: any\n clientRect: (() => DOMRect | null) | null\n query?: string\n range?: any\n }) => {\n const { selection } = props.editor.state\n const parentNode = selection.$from.node(selection.$from.depth)\n const blockType = parentNode.type.name\n if (blockType === \"codeBlock\") return false\n\n component = new ReactRenderer(EditorCommandOut, {\n props: {\n query: (props as any).query ?? \"\",\n range: (props as any).range,\n },\n editor: props.editor,\n })\n\n container = document.createElement(\"div\")\n container.style.position = \"fixed\"\n container.style.zIndex = \"9999\"\n container.style.minWidth = \"240px\"\n ;(elementRef?.current ?? document.body).appendChild(container)\n container.appendChild(component.element)\n\n const rect =\n typeof props.clientRect === \"function\" ? props.clientRect() : null\n if (rect) updatePosition(rect)\n },\n onUpdate: (props: {\n editor: any\n clientRect: (() => DOMRect | null) | null\n query?: string\n range?: any\n }) => {\n component?.updateProps({\n query: (props as any).query ?? \"\",\n range: (props as any).range,\n })\n const rect =\n typeof props.clientRect === \"function\" ? props.clientRect() : null\n if (rect) updatePosition(rect)\n },\n onKeyDown: ({ event }: { event: KeyboardEvent }) => {\n if (event.key === \"Escape\") {\n destroy()\n return true\n }\n return false\n },\n onExit: () => {\n destroy()\n },\n }\n}\n\nexport interface SuggestionItem {\n title: string\n description: string\n icon: ReactNode\n searchTerms?: string[]\n command?: (props: {\n editor: any\n range: { from: number; to: number }\n }) => void\n}\n\nexport const createSuggestionItems = (items: SuggestionItem[]) => items\n\nexport const handleCommandNavigation = (event: KeyboardEvent) => {\n if ([\"ArrowUp\", \"ArrowDown\", \"Enter\"].includes(event.key)) {\n const slashCommand = document.querySelector(\"#slash-command\")\n if (slashCommand) return true\n }\n}\n"],"mappings":"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","names":["useCurrentEditor","jsx","forwardRef","useCurrentEditor","jsx","forwardRef","jsx","forwardRef","forwardRef","useCurrentEditor","jsx","forwardRef","useCurrentEditor","jsx","Command"]}
@@ -0,0 +1,29 @@
1
+ import * as _tiptap_core from '@tiptap/core';
2
+ import * as react from 'react';
3
+ import { FC, ReactNode } from 'react';
4
+ import { EditorProviderProps } from '@tiptap/react';
5
+
6
+ interface EditorRootProps {
7
+ readonly children: ReactNode;
8
+ }
9
+ declare const EditorRoot: FC<EditorRootProps>;
10
+ type EditorContentProps = EditorProviderProps & {
11
+ readonly children?: ReactNode;
12
+ readonly className?: string;
13
+ readonly initialContent?: any;
14
+ };
15
+ declare const EditorContent: react.ForwardRefExoticComponent<{
16
+ children?: ReactNode;
17
+ slotBefore?: ReactNode;
18
+ slotAfter?: ReactNode;
19
+ editorContainerProps?: react.HTMLAttributes<HTMLDivElement>;
20
+ } & Partial<_tiptap_core.EditorOptions> & {
21
+ immediatelyRender?: boolean;
22
+ shouldRerenderOnTransaction?: boolean;
23
+ } & {
24
+ readonly children?: ReactNode;
25
+ readonly className?: string;
26
+ readonly initialContent?: any;
27
+ } & react.RefAttributes<HTMLDivElement>>;
28
+
29
+ export { EditorRoot as E, EditorContent as a, type EditorContentProps as b };
@@ -0,0 +1,29 @@
1
+ import * as _tiptap_core from '@tiptap/core';
2
+ import * as react from 'react';
3
+ import { FC, ReactNode } from 'react';
4
+ import { EditorProviderProps } from '@tiptap/react';
5
+
6
+ interface EditorRootProps {
7
+ readonly children: ReactNode;
8
+ }
9
+ declare const EditorRoot: FC<EditorRootProps>;
10
+ type EditorContentProps = EditorProviderProps & {
11
+ readonly children?: ReactNode;
12
+ readonly className?: string;
13
+ readonly initialContent?: any;
14
+ };
15
+ declare const EditorContent: react.ForwardRefExoticComponent<{
16
+ children?: ReactNode;
17
+ slotBefore?: ReactNode;
18
+ slotAfter?: ReactNode;
19
+ editorContainerProps?: react.HTMLAttributes<HTMLDivElement>;
20
+ } & Partial<_tiptap_core.EditorOptions> & {
21
+ immediatelyRender?: boolean;
22
+ shouldRerenderOnTransaction?: boolean;
23
+ } & {
24
+ readonly children?: ReactNode;
25
+ readonly className?: string;
26
+ readonly initialContent?: any;
27
+ } & react.RefAttributes<HTMLDivElement>>;
28
+
29
+ export { EditorRoot as E, EditorContent as a, type EditorContentProps as b };