notra-editor 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.
- package/dist/index.cjs +183 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +25 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.mjs +145 -0
- package/dist/index.mjs.map +1 -0
- package/dist/themes/default/editor.css +76 -0
- package/dist/themes/default/reader.css +14 -0
- package/dist/themes/default/shared.css +431 -0
- package/package.json +70 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
NotraEditor: () => NotraEditor,
|
|
34
|
+
NotraReader: () => NotraReader
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
|
|
38
|
+
// src/notra-editor.tsx
|
|
39
|
+
var import_react3 = require("@tiptap/react");
|
|
40
|
+
|
|
41
|
+
// src/hooks/use-markdown-editor.ts
|
|
42
|
+
var import_react = require("@tiptap/react");
|
|
43
|
+
var import_react2 = require("react");
|
|
44
|
+
|
|
45
|
+
// src/extensions/shared.ts
|
|
46
|
+
var import_extension_list = require("@tiptap/extension-list");
|
|
47
|
+
var import_starter_kit = __toESM(require("@tiptap/starter-kit"), 1);
|
|
48
|
+
var starterKitBaseConfig = {
|
|
49
|
+
heading: { levels: [1, 2, 3, 4, 5, 6] },
|
|
50
|
+
link: {
|
|
51
|
+
openOnClick: false,
|
|
52
|
+
autolink: true
|
|
53
|
+
},
|
|
54
|
+
// Disable StarterKit's built-in list handling; use @tiptap/extension-list instead
|
|
55
|
+
bulletList: false,
|
|
56
|
+
orderedList: false,
|
|
57
|
+
listItem: false,
|
|
58
|
+
listKeymap: false
|
|
59
|
+
};
|
|
60
|
+
var sharedExtensions = [
|
|
61
|
+
import_starter_kit.default.configure({
|
|
62
|
+
...starterKitBaseConfig,
|
|
63
|
+
dropcursor: false,
|
|
64
|
+
gapcursor: false,
|
|
65
|
+
undoRedo: false,
|
|
66
|
+
trailingNode: false
|
|
67
|
+
}),
|
|
68
|
+
import_extension_list.ListKit
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
// src/extensions/editor.ts
|
|
72
|
+
var import_extension_list2 = require("@tiptap/extension-list");
|
|
73
|
+
var import_starter_kit2 = __toESM(require("@tiptap/starter-kit"), 1);
|
|
74
|
+
var import_tiptap_markdown = require("tiptap-markdown");
|
|
75
|
+
var editorExtensions = [
|
|
76
|
+
import_starter_kit2.default.configure(starterKitBaseConfig),
|
|
77
|
+
import_extension_list2.ListKit,
|
|
78
|
+
import_tiptap_markdown.Markdown.configure({
|
|
79
|
+
html: false,
|
|
80
|
+
transformPastedText: true,
|
|
81
|
+
transformCopiedText: true
|
|
82
|
+
})
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
// src/hooks/use-markdown-editor.ts
|
|
86
|
+
function getMarkdown(storage) {
|
|
87
|
+
return storage.markdown.getMarkdown();
|
|
88
|
+
}
|
|
89
|
+
function useMarkdownEditor({
|
|
90
|
+
value,
|
|
91
|
+
onChange,
|
|
92
|
+
editable = true
|
|
93
|
+
}) {
|
|
94
|
+
const externalValue = (0, import_react2.useRef)(value);
|
|
95
|
+
const onChangeRef = (0, import_react2.useRef)(onChange);
|
|
96
|
+
onChangeRef.current = onChange;
|
|
97
|
+
const editor = (0, import_react.useEditor)({
|
|
98
|
+
extensions: editorExtensions,
|
|
99
|
+
editable,
|
|
100
|
+
content: value,
|
|
101
|
+
onUpdate({ editor: editor2 }) {
|
|
102
|
+
const md = getMarkdown(
|
|
103
|
+
editor2.storage
|
|
104
|
+
);
|
|
105
|
+
externalValue.current = md;
|
|
106
|
+
onChangeRef.current(md);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
(0, import_react2.useEffect)(() => {
|
|
110
|
+
if (!editor) return;
|
|
111
|
+
if (value === externalValue.current) return;
|
|
112
|
+
externalValue.current = value;
|
|
113
|
+
editor.commands.setContent(value);
|
|
114
|
+
}, [value, editor]);
|
|
115
|
+
(0, import_react2.useEffect)(() => {
|
|
116
|
+
if (!editor) return;
|
|
117
|
+
editor.setEditable(editable);
|
|
118
|
+
}, [editable, editor]);
|
|
119
|
+
return { editor };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/notra-editor.tsx
|
|
123
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
124
|
+
function NotraEditor({
|
|
125
|
+
value,
|
|
126
|
+
onChange,
|
|
127
|
+
placeholder,
|
|
128
|
+
readOnly = false,
|
|
129
|
+
className
|
|
130
|
+
}) {
|
|
131
|
+
const { editor } = useMarkdownEditor({
|
|
132
|
+
value,
|
|
133
|
+
onChange,
|
|
134
|
+
placeholder,
|
|
135
|
+
editable: !readOnly
|
|
136
|
+
});
|
|
137
|
+
const classNames = ["notra", "notra-editor", className].filter(Boolean).join(" ");
|
|
138
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: classNames, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react3.EditorContent, { editor }) });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/notra-reader.tsx
|
|
142
|
+
var import_react4 = require("@tiptap/static-renderer/pm/react");
|
|
143
|
+
|
|
144
|
+
// src/utils/markdown-to-json.ts
|
|
145
|
+
var import_core = require("@tiptap/core");
|
|
146
|
+
var import_tiptap_markdown2 = require("tiptap-markdown");
|
|
147
|
+
var parserExtensions = [
|
|
148
|
+
...sharedExtensions,
|
|
149
|
+
import_tiptap_markdown2.Markdown.configure({ html: false })
|
|
150
|
+
];
|
|
151
|
+
var parserEditor = null;
|
|
152
|
+
function getParserEditor() {
|
|
153
|
+
if (!parserEditor) {
|
|
154
|
+
parserEditor = new import_core.Editor({
|
|
155
|
+
extensions: parserExtensions,
|
|
156
|
+
content: ""
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
return parserEditor;
|
|
160
|
+
}
|
|
161
|
+
function markdownToJSON(markdown) {
|
|
162
|
+
const editor = getParserEditor();
|
|
163
|
+
editor.commands.setContent(markdown);
|
|
164
|
+
return editor.getJSON();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/notra-reader.tsx
|
|
168
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
169
|
+
function NotraReader({ content, className }) {
|
|
170
|
+
const json = markdownToJSON(content);
|
|
171
|
+
const rendered = (0, import_react4.renderToReactElement)({
|
|
172
|
+
extensions: sharedExtensions,
|
|
173
|
+
content: json
|
|
174
|
+
});
|
|
175
|
+
const classNames = ["notra", "notra-reader", className].filter(Boolean).join(" ");
|
|
176
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: classNames, children: rendered });
|
|
177
|
+
}
|
|
178
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
179
|
+
0 && (module.exports = {
|
|
180
|
+
NotraEditor,
|
|
181
|
+
NotraReader
|
|
182
|
+
});
|
|
183
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/notra-editor.tsx","../src/hooks/use-markdown-editor.ts","../src/extensions/shared.ts","../src/extensions/editor.ts","../src/notra-reader.tsx","../src/utils/markdown-to-json.ts"],"sourcesContent":["export { NotraEditor } from './notra-editor';\nexport type { NotraEditorProps } from './notra-editor';\n\nexport { NotraReader } from './notra-reader';\nexport type { NotraReaderProps } from './notra-reader';\n","import { EditorContent } from '@tiptap/react';\n\nimport { useMarkdownEditor } from './hooks/use-markdown-editor';\n\nexport interface NotraEditorProps {\n\t/** Markdown content (source of truth) */\n\tvalue: string;\n\t/** Called when content changes, receives updated Markdown */\n\tonChange: (value: string) => void;\n\t/** Placeholder text shown when editor is empty */\n\tplaceholder?: string;\n\t/** Disable editing */\n\treadOnly?: boolean;\n\t/** Additional CSS class on the wrapper element */\n\tclassName?: string;\n}\n\nexport function NotraEditor({\n\tvalue,\n\tonChange,\n\tplaceholder,\n\treadOnly = false,\n\tclassName\n}: NotraEditorProps) {\n\tconst { editor } = useMarkdownEditor({\n\t\tvalue,\n\t\tonChange,\n\t\tplaceholder,\n\t\teditable: !readOnly\n\t});\n\n\tconst classNames = ['notra', 'notra-editor', className]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\treturn (\n\t\t<div className={classNames}>\n\t\t\t<EditorContent editor={editor} />\n\t\t</div>\n\t);\n}\n","import { useEditor } from '@tiptap/react';\nimport { useEffect, useRef } from 'react';\n\nimport { editorExtensions } from '../extensions';\n\nimport type { MarkdownStorage } from 'tiptap-markdown';\n\nexport interface UseMarkdownEditorOptions {\n\tvalue: string;\n\tonChange: (value: string) => void;\n\tplaceholder?: string;\n\teditable?: boolean;\n}\n\nfunction getMarkdown(storage: Record<string, unknown>): string {\n\treturn (storage.markdown as MarkdownStorage).getMarkdown();\n}\n\nexport function useMarkdownEditor({\n\tvalue,\n\tonChange,\n\teditable = true\n}: UseMarkdownEditorOptions) {\n\tconst externalValue = useRef(value);\n\tconst onChangeRef = useRef(onChange);\n\n\tonChangeRef.current = onChange;\n\n\tconst editor = useEditor({\n\t\textensions: editorExtensions,\n\t\teditable,\n\t\tcontent: value,\n\t\tonUpdate({ editor }) {\n\t\t\tconst md = getMarkdown(\n\t\t\t\teditor.storage as unknown as Record<string, unknown>\n\t\t\t);\n\n\t\t\texternalValue.current = md;\n\t\t\tonChangeRef.current(md);\n\t\t}\n\t});\n\n\tuseEffect(() => {\n\t\tif (!editor) return;\n\n\t\tif (value === externalValue.current) return;\n\n\t\texternalValue.current = value;\n\t\teditor.commands.setContent(value);\n\t}, [value, editor]);\n\n\tuseEffect(() => {\n\t\tif (!editor) return;\n\n\t\teditor.setEditable(editable);\n\t}, [editable, editor]);\n\n\treturn { editor };\n}\n","import { ListKit } from '@tiptap/extension-list';\nimport StarterKit, { type StarterKitOptions } from '@tiptap/starter-kit';\n\n// Shared StarterKit config: content nodes/marks, no lists (use ListKit instead)\nexport const starterKitBaseConfig: Partial<StarterKitOptions> = {\n\theading: { levels: [1, 2, 3, 4, 5, 6] },\n\tlink: {\n\t\topenOnClick: false,\n\t\tautolink: true\n\t},\n\t// Disable StarterKit's built-in list handling; use @tiptap/extension-list instead\n\tbulletList: false,\n\torderedList: false,\n\tlistItem: false,\n\tlistKeymap: false\n};\n\n// Content model extensions — shared by editor and reader\n// No interactive features (dropcursor, gapcursor, undoRedo, trailingNode)\nexport const sharedExtensions = [\n\tStarterKit.configure({\n\t\t...starterKitBaseConfig,\n\t\tdropcursor: false,\n\t\tgapcursor: false,\n\t\tundoRedo: false,\n\t\ttrailingNode: false\n\t}),\n\tListKit\n];\n","import { ListKit } from '@tiptap/extension-list';\nimport StarterKit from '@tiptap/starter-kit';\nimport { Markdown } from 'tiptap-markdown';\n\nimport { starterKitBaseConfig } from './shared';\n\n// Editor extensions = shared content model + interactive features + Markdown\nexport const editorExtensions = [\n\tStarterKit.configure(starterKitBaseConfig),\n\tListKit,\n\tMarkdown.configure({\n\t\thtml: false,\n\t\ttransformPastedText: true,\n\t\ttransformCopiedText: true\n\t})\n];\n","import { renderToReactElement } from '@tiptap/static-renderer/pm/react';\n\nimport { sharedExtensions } from './extensions';\nimport { markdownToJSON } from './utils/markdown-to-json';\n\nexport interface NotraReaderProps {\n\t/** Markdown content to render */\n\tcontent: string;\n\t/** Additional CSS class on the wrapper element */\n\tclassName?: string;\n}\n\nexport function NotraReader({ content, className }: NotraReaderProps) {\n\tconst json = markdownToJSON(content);\n\n\tconst rendered = renderToReactElement({\n\t\textensions: sharedExtensions,\n\t\tcontent: json\n\t});\n\n\tconst classNames = ['notra', 'notra-reader', className]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\treturn <div className={classNames}>{rendered}</div>;\n}\n","import { Editor } from '@tiptap/core';\nimport { Markdown } from 'tiptap-markdown';\n\nimport { sharedExtensions } from '../extensions';\n\n// Parser needs shared content model + Markdown for markdown→JSON conversion\n// No clipboard features needed (transformPastedText/transformCopiedText are editor-only)\nconst parserExtensions = [\n\t...sharedExtensions,\n\tMarkdown.configure({ html: false })\n];\n\nlet parserEditor: Editor | null = null;\n\nfunction getParserEditor(): Editor {\n\tif (!parserEditor) {\n\t\tparserEditor = new Editor({\n\t\t\textensions: parserExtensions,\n\t\t\tcontent: ''\n\t\t});\n\t}\n\n\treturn parserEditor;\n}\n\n/**\n * Convert a Markdown string to Tiptap-compatible JSON (ProseMirror document).\n * Uses a singleton headless Tiptap editor for parsing.\n */\nexport function markdownToJSON(markdown: string): Record<string, unknown> {\n\tconst editor = getParserEditor();\n\n\teditor.commands.setContent(markdown);\n\n\treturn editor.getJSON() as Record<string, unknown>;\n}\n"],"mappings":"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","names":["import_react","import_react","StarterKit","import_extension_list","import_starter_kit","StarterKit","editor","import_react","import_tiptap_markdown","import_jsx_runtime"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface NotraEditorProps {
|
|
4
|
+
/** Markdown content (source of truth) */
|
|
5
|
+
value: string;
|
|
6
|
+
/** Called when content changes, receives updated Markdown */
|
|
7
|
+
onChange: (value: string) => void;
|
|
8
|
+
/** Placeholder text shown when editor is empty */
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
/** Disable editing */
|
|
11
|
+
readOnly?: boolean;
|
|
12
|
+
/** Additional CSS class on the wrapper element */
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
declare function NotraEditor({ value, onChange, placeholder, readOnly, className }: NotraEditorProps): react_jsx_runtime.JSX.Element;
|
|
16
|
+
|
|
17
|
+
interface NotraReaderProps {
|
|
18
|
+
/** Markdown content to render */
|
|
19
|
+
content: string;
|
|
20
|
+
/** Additional CSS class on the wrapper element */
|
|
21
|
+
className?: string;
|
|
22
|
+
}
|
|
23
|
+
declare function NotraReader({ content, className }: NotraReaderProps): react_jsx_runtime.JSX.Element;
|
|
24
|
+
|
|
25
|
+
export { NotraEditor, type NotraEditorProps, NotraReader, type NotraReaderProps };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface NotraEditorProps {
|
|
4
|
+
/** Markdown content (source of truth) */
|
|
5
|
+
value: string;
|
|
6
|
+
/** Called when content changes, receives updated Markdown */
|
|
7
|
+
onChange: (value: string) => void;
|
|
8
|
+
/** Placeholder text shown when editor is empty */
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
/** Disable editing */
|
|
11
|
+
readOnly?: boolean;
|
|
12
|
+
/** Additional CSS class on the wrapper element */
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
declare function NotraEditor({ value, onChange, placeholder, readOnly, className }: NotraEditorProps): react_jsx_runtime.JSX.Element;
|
|
16
|
+
|
|
17
|
+
interface NotraReaderProps {
|
|
18
|
+
/** Markdown content to render */
|
|
19
|
+
content: string;
|
|
20
|
+
/** Additional CSS class on the wrapper element */
|
|
21
|
+
className?: string;
|
|
22
|
+
}
|
|
23
|
+
declare function NotraReader({ content, className }: NotraReaderProps): react_jsx_runtime.JSX.Element;
|
|
24
|
+
|
|
25
|
+
export { NotraEditor, type NotraEditorProps, NotraReader, type NotraReaderProps };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// src/notra-editor.tsx
|
|
2
|
+
import { EditorContent } from "@tiptap/react";
|
|
3
|
+
|
|
4
|
+
// src/hooks/use-markdown-editor.ts
|
|
5
|
+
import { useEditor } from "@tiptap/react";
|
|
6
|
+
import { useEffect, useRef } from "react";
|
|
7
|
+
|
|
8
|
+
// src/extensions/shared.ts
|
|
9
|
+
import { ListKit } from "@tiptap/extension-list";
|
|
10
|
+
import StarterKit from "@tiptap/starter-kit";
|
|
11
|
+
var starterKitBaseConfig = {
|
|
12
|
+
heading: { levels: [1, 2, 3, 4, 5, 6] },
|
|
13
|
+
link: {
|
|
14
|
+
openOnClick: false,
|
|
15
|
+
autolink: true
|
|
16
|
+
},
|
|
17
|
+
// Disable StarterKit's built-in list handling; use @tiptap/extension-list instead
|
|
18
|
+
bulletList: false,
|
|
19
|
+
orderedList: false,
|
|
20
|
+
listItem: false,
|
|
21
|
+
listKeymap: false
|
|
22
|
+
};
|
|
23
|
+
var sharedExtensions = [
|
|
24
|
+
StarterKit.configure({
|
|
25
|
+
...starterKitBaseConfig,
|
|
26
|
+
dropcursor: false,
|
|
27
|
+
gapcursor: false,
|
|
28
|
+
undoRedo: false,
|
|
29
|
+
trailingNode: false
|
|
30
|
+
}),
|
|
31
|
+
ListKit
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// src/extensions/editor.ts
|
|
35
|
+
import { ListKit as ListKit2 } from "@tiptap/extension-list";
|
|
36
|
+
import StarterKit2 from "@tiptap/starter-kit";
|
|
37
|
+
import { Markdown } from "tiptap-markdown";
|
|
38
|
+
var editorExtensions = [
|
|
39
|
+
StarterKit2.configure(starterKitBaseConfig),
|
|
40
|
+
ListKit2,
|
|
41
|
+
Markdown.configure({
|
|
42
|
+
html: false,
|
|
43
|
+
transformPastedText: true,
|
|
44
|
+
transformCopiedText: true
|
|
45
|
+
})
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
// src/hooks/use-markdown-editor.ts
|
|
49
|
+
function getMarkdown(storage) {
|
|
50
|
+
return storage.markdown.getMarkdown();
|
|
51
|
+
}
|
|
52
|
+
function useMarkdownEditor({
|
|
53
|
+
value,
|
|
54
|
+
onChange,
|
|
55
|
+
editable = true
|
|
56
|
+
}) {
|
|
57
|
+
const externalValue = useRef(value);
|
|
58
|
+
const onChangeRef = useRef(onChange);
|
|
59
|
+
onChangeRef.current = onChange;
|
|
60
|
+
const editor = useEditor({
|
|
61
|
+
extensions: editorExtensions,
|
|
62
|
+
editable,
|
|
63
|
+
content: value,
|
|
64
|
+
onUpdate({ editor: editor2 }) {
|
|
65
|
+
const md = getMarkdown(
|
|
66
|
+
editor2.storage
|
|
67
|
+
);
|
|
68
|
+
externalValue.current = md;
|
|
69
|
+
onChangeRef.current(md);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (!editor) return;
|
|
74
|
+
if (value === externalValue.current) return;
|
|
75
|
+
externalValue.current = value;
|
|
76
|
+
editor.commands.setContent(value);
|
|
77
|
+
}, [value, editor]);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (!editor) return;
|
|
80
|
+
editor.setEditable(editable);
|
|
81
|
+
}, [editable, editor]);
|
|
82
|
+
return { editor };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/notra-editor.tsx
|
|
86
|
+
import { jsx } from "react/jsx-runtime";
|
|
87
|
+
function NotraEditor({
|
|
88
|
+
value,
|
|
89
|
+
onChange,
|
|
90
|
+
placeholder,
|
|
91
|
+
readOnly = false,
|
|
92
|
+
className
|
|
93
|
+
}) {
|
|
94
|
+
const { editor } = useMarkdownEditor({
|
|
95
|
+
value,
|
|
96
|
+
onChange,
|
|
97
|
+
placeholder,
|
|
98
|
+
editable: !readOnly
|
|
99
|
+
});
|
|
100
|
+
const classNames = ["notra", "notra-editor", className].filter(Boolean).join(" ");
|
|
101
|
+
return /* @__PURE__ */ jsx("div", { className: classNames, children: /* @__PURE__ */ jsx(EditorContent, { editor }) });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/notra-reader.tsx
|
|
105
|
+
import { renderToReactElement } from "@tiptap/static-renderer/pm/react";
|
|
106
|
+
|
|
107
|
+
// src/utils/markdown-to-json.ts
|
|
108
|
+
import { Editor } from "@tiptap/core";
|
|
109
|
+
import { Markdown as Markdown2 } from "tiptap-markdown";
|
|
110
|
+
var parserExtensions = [
|
|
111
|
+
...sharedExtensions,
|
|
112
|
+
Markdown2.configure({ html: false })
|
|
113
|
+
];
|
|
114
|
+
var parserEditor = null;
|
|
115
|
+
function getParserEditor() {
|
|
116
|
+
if (!parserEditor) {
|
|
117
|
+
parserEditor = new Editor({
|
|
118
|
+
extensions: parserExtensions,
|
|
119
|
+
content: ""
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return parserEditor;
|
|
123
|
+
}
|
|
124
|
+
function markdownToJSON(markdown) {
|
|
125
|
+
const editor = getParserEditor();
|
|
126
|
+
editor.commands.setContent(markdown);
|
|
127
|
+
return editor.getJSON();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/notra-reader.tsx
|
|
131
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
132
|
+
function NotraReader({ content, className }) {
|
|
133
|
+
const json = markdownToJSON(content);
|
|
134
|
+
const rendered = renderToReactElement({
|
|
135
|
+
extensions: sharedExtensions,
|
|
136
|
+
content: json
|
|
137
|
+
});
|
|
138
|
+
const classNames = ["notra", "notra-reader", className].filter(Boolean).join(" ");
|
|
139
|
+
return /* @__PURE__ */ jsx2("div", { className: classNames, children: rendered });
|
|
140
|
+
}
|
|
141
|
+
export {
|
|
142
|
+
NotraEditor,
|
|
143
|
+
NotraReader
|
|
144
|
+
};
|
|
145
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/notra-editor.tsx","../src/hooks/use-markdown-editor.ts","../src/extensions/shared.ts","../src/extensions/editor.ts","../src/notra-reader.tsx","../src/utils/markdown-to-json.ts"],"sourcesContent":["import { EditorContent } from '@tiptap/react';\n\nimport { useMarkdownEditor } from './hooks/use-markdown-editor';\n\nexport interface NotraEditorProps {\n\t/** Markdown content (source of truth) */\n\tvalue: string;\n\t/** Called when content changes, receives updated Markdown */\n\tonChange: (value: string) => void;\n\t/** Placeholder text shown when editor is empty */\n\tplaceholder?: string;\n\t/** Disable editing */\n\treadOnly?: boolean;\n\t/** Additional CSS class on the wrapper element */\n\tclassName?: string;\n}\n\nexport function NotraEditor({\n\tvalue,\n\tonChange,\n\tplaceholder,\n\treadOnly = false,\n\tclassName\n}: NotraEditorProps) {\n\tconst { editor } = useMarkdownEditor({\n\t\tvalue,\n\t\tonChange,\n\t\tplaceholder,\n\t\teditable: !readOnly\n\t});\n\n\tconst classNames = ['notra', 'notra-editor', className]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\treturn (\n\t\t<div className={classNames}>\n\t\t\t<EditorContent editor={editor} />\n\t\t</div>\n\t);\n}\n","import { useEditor } from '@tiptap/react';\nimport { useEffect, useRef } from 'react';\n\nimport { editorExtensions } from '../extensions';\n\nimport type { MarkdownStorage } from 'tiptap-markdown';\n\nexport interface UseMarkdownEditorOptions {\n\tvalue: string;\n\tonChange: (value: string) => void;\n\tplaceholder?: string;\n\teditable?: boolean;\n}\n\nfunction getMarkdown(storage: Record<string, unknown>): string {\n\treturn (storage.markdown as MarkdownStorage).getMarkdown();\n}\n\nexport function useMarkdownEditor({\n\tvalue,\n\tonChange,\n\teditable = true\n}: UseMarkdownEditorOptions) {\n\tconst externalValue = useRef(value);\n\tconst onChangeRef = useRef(onChange);\n\n\tonChangeRef.current = onChange;\n\n\tconst editor = useEditor({\n\t\textensions: editorExtensions,\n\t\teditable,\n\t\tcontent: value,\n\t\tonUpdate({ editor }) {\n\t\t\tconst md = getMarkdown(\n\t\t\t\teditor.storage as unknown as Record<string, unknown>\n\t\t\t);\n\n\t\t\texternalValue.current = md;\n\t\t\tonChangeRef.current(md);\n\t\t}\n\t});\n\n\tuseEffect(() => {\n\t\tif (!editor) return;\n\n\t\tif (value === externalValue.current) return;\n\n\t\texternalValue.current = value;\n\t\teditor.commands.setContent(value);\n\t}, [value, editor]);\n\n\tuseEffect(() => {\n\t\tif (!editor) return;\n\n\t\teditor.setEditable(editable);\n\t}, [editable, editor]);\n\n\treturn { editor };\n}\n","import { ListKit } from '@tiptap/extension-list';\nimport StarterKit, { type StarterKitOptions } from '@tiptap/starter-kit';\n\n// Shared StarterKit config: content nodes/marks, no lists (use ListKit instead)\nexport const starterKitBaseConfig: Partial<StarterKitOptions> = {\n\theading: { levels: [1, 2, 3, 4, 5, 6] },\n\tlink: {\n\t\topenOnClick: false,\n\t\tautolink: true\n\t},\n\t// Disable StarterKit's built-in list handling; use @tiptap/extension-list instead\n\tbulletList: false,\n\torderedList: false,\n\tlistItem: false,\n\tlistKeymap: false\n};\n\n// Content model extensions — shared by editor and reader\n// No interactive features (dropcursor, gapcursor, undoRedo, trailingNode)\nexport const sharedExtensions = [\n\tStarterKit.configure({\n\t\t...starterKitBaseConfig,\n\t\tdropcursor: false,\n\t\tgapcursor: false,\n\t\tundoRedo: false,\n\t\ttrailingNode: false\n\t}),\n\tListKit\n];\n","import { ListKit } from '@tiptap/extension-list';\nimport StarterKit from '@tiptap/starter-kit';\nimport { Markdown } from 'tiptap-markdown';\n\nimport { starterKitBaseConfig } from './shared';\n\n// Editor extensions = shared content model + interactive features + Markdown\nexport const editorExtensions = [\n\tStarterKit.configure(starterKitBaseConfig),\n\tListKit,\n\tMarkdown.configure({\n\t\thtml: false,\n\t\ttransformPastedText: true,\n\t\ttransformCopiedText: true\n\t})\n];\n","import { renderToReactElement } from '@tiptap/static-renderer/pm/react';\n\nimport { sharedExtensions } from './extensions';\nimport { markdownToJSON } from './utils/markdown-to-json';\n\nexport interface NotraReaderProps {\n\t/** Markdown content to render */\n\tcontent: string;\n\t/** Additional CSS class on the wrapper element */\n\tclassName?: string;\n}\n\nexport function NotraReader({ content, className }: NotraReaderProps) {\n\tconst json = markdownToJSON(content);\n\n\tconst rendered = renderToReactElement({\n\t\textensions: sharedExtensions,\n\t\tcontent: json\n\t});\n\n\tconst classNames = ['notra', 'notra-reader', className]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\treturn <div className={classNames}>{rendered}</div>;\n}\n","import { Editor } from '@tiptap/core';\nimport { Markdown } from 'tiptap-markdown';\n\nimport { sharedExtensions } from '../extensions';\n\n// Parser needs shared content model + Markdown for markdown→JSON conversion\n// No clipboard features needed (transformPastedText/transformCopiedText are editor-only)\nconst parserExtensions = [\n\t...sharedExtensions,\n\tMarkdown.configure({ html: false })\n];\n\nlet parserEditor: Editor | null = null;\n\nfunction getParserEditor(): Editor {\n\tif (!parserEditor) {\n\t\tparserEditor = new Editor({\n\t\t\textensions: parserExtensions,\n\t\t\tcontent: ''\n\t\t});\n\t}\n\n\treturn parserEditor;\n}\n\n/**\n * Convert a Markdown string to Tiptap-compatible JSON (ProseMirror document).\n * Uses a singleton headless Tiptap editor for parsing.\n */\nexport function markdownToJSON(markdown: string): Record<string, unknown> {\n\tconst editor = getParserEditor();\n\n\teditor.commands.setContent(markdown);\n\n\treturn editor.getJSON() as Record<string, unknown>;\n}\n"],"mappings":"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","names":["ListKit","StarterKit","StarterKit","ListKit","editor","Markdown","Markdown","jsx"]}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/* =====================
|
|
2
|
+
Editor-specific Variables
|
|
3
|
+
===================== */
|
|
4
|
+
.notra-editor {
|
|
5
|
+
--notra-cursor-color: rgba(98, 41, 255, 1);
|
|
6
|
+
--notra-selection-color: rgba(157, 138, 255, 0.2);
|
|
7
|
+
--notra-placeholder-color: rgba(40, 44, 51, 0.42);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* =====================
|
|
11
|
+
Editor Container
|
|
12
|
+
===================== */
|
|
13
|
+
.notra-editor .tiptap.ProseMirror {
|
|
14
|
+
white-space: pre-wrap;
|
|
15
|
+
outline: none;
|
|
16
|
+
caret-color: var(--notra-cursor-color);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* =====================
|
|
20
|
+
Selection
|
|
21
|
+
===================== */
|
|
22
|
+
.notra-editor .tiptap.ProseMirror ::selection {
|
|
23
|
+
background-color: var(--notra-selection-color);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.notra-editor .tiptap.ProseMirror .selection {
|
|
27
|
+
display: inline;
|
|
28
|
+
background-color: var(--notra-selection-color);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.notra-editor .tiptap.ProseMirror .selection::selection {
|
|
32
|
+
background: transparent;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.notra-editor
|
|
36
|
+
.tiptap.ProseMirror
|
|
37
|
+
.ProseMirror-selectednode:not(img):not(pre):not(.react-renderer) {
|
|
38
|
+
border-radius: var(--notra-radius);
|
|
39
|
+
background-color: var(--notra-selection-color);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* =====================
|
|
43
|
+
Placeholder
|
|
44
|
+
===================== */
|
|
45
|
+
.notra-editor .tiptap.ProseMirror > * {
|
|
46
|
+
position: relative;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.notra-editor
|
|
50
|
+
.tiptap.ProseMirror
|
|
51
|
+
.is-empty[data-placeholder]:has(
|
|
52
|
+
> .ProseMirror-trailingBreak:only-child
|
|
53
|
+
)::before {
|
|
54
|
+
content: attr(data-placeholder);
|
|
55
|
+
pointer-events: none;
|
|
56
|
+
height: 0;
|
|
57
|
+
position: absolute;
|
|
58
|
+
width: 100%;
|
|
59
|
+
text-align: inherit;
|
|
60
|
+
left: 0;
|
|
61
|
+
right: 0;
|
|
62
|
+
color: var(--notra-placeholder-color);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* =====================
|
|
66
|
+
Drop Cursor
|
|
67
|
+
===================== */
|
|
68
|
+
.notra-editor .prosemirror-dropcursor-block,
|
|
69
|
+
.notra-editor .prosemirror-dropcursor-inline {
|
|
70
|
+
background: var(--notra-cursor-color) !important;
|
|
71
|
+
border-radius: 0.25rem;
|
|
72
|
+
margin-left: -1px;
|
|
73
|
+
margin-right: -1px;
|
|
74
|
+
width: 100%;
|
|
75
|
+
height: 0.188rem;
|
|
76
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* =====================
|
|
2
|
+
Reader-specific Styles
|
|
3
|
+
===================== */
|
|
4
|
+
.notra-reader {
|
|
5
|
+
font-family: var(--notra-font-body);
|
|
6
|
+
font-size: var(--notra-font-size);
|
|
7
|
+
line-height: var(--notra-line-height);
|
|
8
|
+
color: var(--notra-color-text);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* Ensure native selection in reader mode */
|
|
12
|
+
.notra-reader *::selection {
|
|
13
|
+
background-color: highlight;
|
|
14
|
+
}
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
/* =====================
|
|
2
|
+
CSS Custom Properties
|
|
3
|
+
===================== */
|
|
4
|
+
.notra {
|
|
5
|
+
--notra-font-body: 'DM Sans', system-ui, -apple-system, sans-serif;
|
|
6
|
+
--notra-font-mono: 'JetBrains Mono NL', ui-monospace, 'SF Mono', monospace;
|
|
7
|
+
--notra-font-size: 1rem;
|
|
8
|
+
--notra-line-height: 1.6;
|
|
9
|
+
|
|
10
|
+
/* Light mode colors */
|
|
11
|
+
--notra-color-text: rgba(29, 30, 32, 0.98);
|
|
12
|
+
--notra-color-bg: #ffffff;
|
|
13
|
+
--notra-color-border: rgba(37, 39, 45, 0.1);
|
|
14
|
+
--notra-color-link: rgba(98, 41, 255, 1);
|
|
15
|
+
|
|
16
|
+
/* Code */
|
|
17
|
+
--notra-code-bg: rgba(15, 22, 36, 0.05);
|
|
18
|
+
--notra-code-text: rgba(35, 37, 42, 0.87);
|
|
19
|
+
--notra-code-border: rgba(37, 39, 45, 0.1);
|
|
20
|
+
--notra-codeblock-bg: rgba(56, 56, 56, 0.04);
|
|
21
|
+
--notra-codeblock-text: rgba(30, 32, 36, 0.95);
|
|
22
|
+
--notra-codeblock-border: rgba(37, 39, 45, 0.1);
|
|
23
|
+
|
|
24
|
+
/* Blockquote */
|
|
25
|
+
--notra-blockquote-bar: rgba(29, 30, 32, 0.98);
|
|
26
|
+
|
|
27
|
+
/* Horizontal rule */
|
|
28
|
+
--notra-hr-color: rgba(37, 39, 45, 0.1);
|
|
29
|
+
|
|
30
|
+
/* Task list */
|
|
31
|
+
--notra-checklist-bg: rgba(15, 22, 36, 0.05);
|
|
32
|
+
--notra-checklist-bg-active: rgba(29, 30, 32, 0.98);
|
|
33
|
+
--notra-checklist-border: rgba(37, 39, 45, 0.1);
|
|
34
|
+
--notra-checklist-border-active: rgba(29, 30, 32, 0.98);
|
|
35
|
+
--notra-checklist-check-color: #ffffff;
|
|
36
|
+
--notra-checklist-text-active: rgba(52, 55, 60, 0.64);
|
|
37
|
+
|
|
38
|
+
--notra-radius: 6px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* =====================
|
|
42
|
+
Base Typography
|
|
43
|
+
===================== */
|
|
44
|
+
.notra .tiptap,
|
|
45
|
+
.notra-reader {
|
|
46
|
+
font-family: var(--notra-font-body);
|
|
47
|
+
font-size: var(--notra-font-size);
|
|
48
|
+
line-height: var(--notra-line-height);
|
|
49
|
+
color: var(--notra-color-text);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* =====================
|
|
53
|
+
Paragraphs
|
|
54
|
+
===================== */
|
|
55
|
+
.notra .tiptap p:not(:first-child):not(td p):not(th p),
|
|
56
|
+
.notra-reader p:not(:first-child) {
|
|
57
|
+
font-size: 1rem;
|
|
58
|
+
line-height: 1.6;
|
|
59
|
+
font-weight: normal;
|
|
60
|
+
margin-top: 20px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* =====================
|
|
64
|
+
Headings
|
|
65
|
+
===================== */
|
|
66
|
+
.notra .tiptap h1,
|
|
67
|
+
.notra .tiptap h2,
|
|
68
|
+
.notra .tiptap h3,
|
|
69
|
+
.notra .tiptap h4,
|
|
70
|
+
.notra .tiptap h5,
|
|
71
|
+
.notra .tiptap h6,
|
|
72
|
+
.notra-reader h1,
|
|
73
|
+
.notra-reader h2,
|
|
74
|
+
.notra-reader h3,
|
|
75
|
+
.notra-reader h4,
|
|
76
|
+
.notra-reader h5,
|
|
77
|
+
.notra-reader h6 {
|
|
78
|
+
position: relative;
|
|
79
|
+
color: inherit;
|
|
80
|
+
font-style: inherit;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.notra .tiptap > h1:first-child,
|
|
84
|
+
.notra .tiptap > h2:first-child,
|
|
85
|
+
.notra .tiptap > h3:first-child,
|
|
86
|
+
.notra .tiptap > h4:first-child,
|
|
87
|
+
.notra-reader > h1:first-child,
|
|
88
|
+
.notra-reader > h2:first-child,
|
|
89
|
+
.notra-reader > h3:first-child,
|
|
90
|
+
.notra-reader > h4:first-child {
|
|
91
|
+
margin-top: 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.notra .tiptap h1,
|
|
95
|
+
.notra-reader h1 {
|
|
96
|
+
font-size: 1.5em;
|
|
97
|
+
font-weight: 700;
|
|
98
|
+
margin-top: 3em;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.notra .tiptap h2,
|
|
102
|
+
.notra-reader h2 {
|
|
103
|
+
font-size: 1.25em;
|
|
104
|
+
font-weight: 700;
|
|
105
|
+
margin-top: 2.5em;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.notra .tiptap h3,
|
|
109
|
+
.notra-reader h3 {
|
|
110
|
+
font-size: 1.125em;
|
|
111
|
+
font-weight: 600;
|
|
112
|
+
margin-top: 2em;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.notra .tiptap h4,
|
|
116
|
+
.notra-reader h4 {
|
|
117
|
+
font-size: 1em;
|
|
118
|
+
font-weight: 600;
|
|
119
|
+
margin-top: 2em;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.notra .tiptap h5,
|
|
123
|
+
.notra-reader h5 {
|
|
124
|
+
font-size: 0.875em;
|
|
125
|
+
font-weight: 600;
|
|
126
|
+
margin-top: 1.5em;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.notra .tiptap h6,
|
|
130
|
+
.notra-reader h6 {
|
|
131
|
+
font-size: 0.75em;
|
|
132
|
+
font-weight: 600;
|
|
133
|
+
margin-top: 1.5em;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* =====================
|
|
137
|
+
Lists — Common
|
|
138
|
+
===================== */
|
|
139
|
+
.notra .tiptap ol,
|
|
140
|
+
.notra .tiptap ul,
|
|
141
|
+
.notra-reader ol,
|
|
142
|
+
.notra-reader ul {
|
|
143
|
+
margin-top: 1.5em;
|
|
144
|
+
margin-bottom: 1.5em;
|
|
145
|
+
padding-left: 1.5em;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.notra .tiptap ol:first-child,
|
|
149
|
+
.notra .tiptap ul:first-child,
|
|
150
|
+
.notra-reader ol:first-child,
|
|
151
|
+
.notra-reader ul:first-child {
|
|
152
|
+
margin-top: 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.notra .tiptap ol:last-child,
|
|
156
|
+
.notra .tiptap ul:last-child,
|
|
157
|
+
.notra-reader ol:last-child,
|
|
158
|
+
.notra-reader ul:last-child {
|
|
159
|
+
margin-bottom: 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.notra .tiptap ol ol,
|
|
163
|
+
.notra .tiptap ol ul,
|
|
164
|
+
.notra .tiptap ul ol,
|
|
165
|
+
.notra .tiptap ul ul,
|
|
166
|
+
.notra-reader ol ol,
|
|
167
|
+
.notra-reader ol ul,
|
|
168
|
+
.notra-reader ul ol,
|
|
169
|
+
.notra-reader ul ul {
|
|
170
|
+
margin-top: 0;
|
|
171
|
+
margin-bottom: 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.notra .tiptap li p,
|
|
175
|
+
.notra-reader li p {
|
|
176
|
+
margin-top: 0;
|
|
177
|
+
line-height: 1.6;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* =====================
|
|
181
|
+
Ordered Lists — Nested styles
|
|
182
|
+
===================== */
|
|
183
|
+
.notra .tiptap ol,
|
|
184
|
+
.notra-reader ol {
|
|
185
|
+
list-style: decimal;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.notra .tiptap ol ol,
|
|
189
|
+
.notra-reader ol ol {
|
|
190
|
+
list-style: lower-alpha;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.notra .tiptap ol ol ol,
|
|
194
|
+
.notra-reader ol ol ol {
|
|
195
|
+
list-style: lower-roman;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* =====================
|
|
199
|
+
Unordered Lists — Nested styles
|
|
200
|
+
===================== */
|
|
201
|
+
.notra .tiptap ul:not([data-type='taskList']),
|
|
202
|
+
.notra-reader ul:not([data-type='taskList']) {
|
|
203
|
+
list-style: disc;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.notra .tiptap ul:not([data-type='taskList']) ul,
|
|
207
|
+
.notra-reader ul:not([data-type='taskList']) ul {
|
|
208
|
+
list-style: circle;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.notra .tiptap ul:not([data-type='taskList']) ul ul,
|
|
212
|
+
.notra-reader ul:not([data-type='taskList']) ul ul {
|
|
213
|
+
list-style: square;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* =====================
|
|
217
|
+
Task Lists
|
|
218
|
+
===================== */
|
|
219
|
+
.notra .tiptap ul[data-type='taskList'],
|
|
220
|
+
.notra-reader ul[data-type='taskList'] {
|
|
221
|
+
padding-left: 0.25em;
|
|
222
|
+
list-style: none;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.notra .tiptap ul[data-type='taskList'] li,
|
|
226
|
+
.notra-reader ul[data-type='taskList'] li {
|
|
227
|
+
display: flex;
|
|
228
|
+
flex-direction: row;
|
|
229
|
+
align-items: flex-start;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.notra .tiptap ul[data-type='taskList'] li[data-checked='true'] > div > p,
|
|
233
|
+
.notra-reader ul[data-type='taskList'] li[data-checked='true'] > div > p {
|
|
234
|
+
opacity: 0.5;
|
|
235
|
+
text-decoration: line-through;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.notra .tiptap ul[data-type='taskList'] li label,
|
|
239
|
+
.notra-reader ul[data-type='taskList'] li label {
|
|
240
|
+
position: relative;
|
|
241
|
+
padding-top: 0.375rem;
|
|
242
|
+
padding-right: 0.5rem;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.notra .tiptap ul[data-type='taskList'] li label input[type='checkbox'],
|
|
246
|
+
.notra-reader ul[data-type='taskList'] li label input[type='checkbox'] {
|
|
247
|
+
position: absolute;
|
|
248
|
+
opacity: 0;
|
|
249
|
+
width: 0;
|
|
250
|
+
height: 0;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.notra .tiptap ul[data-type='taskList'] li label span,
|
|
254
|
+
.notra-reader ul[data-type='taskList'] li label span {
|
|
255
|
+
display: block;
|
|
256
|
+
width: 1em;
|
|
257
|
+
height: 1em;
|
|
258
|
+
border: 1px solid var(--notra-checklist-border);
|
|
259
|
+
border-radius: 0.25rem;
|
|
260
|
+
position: relative;
|
|
261
|
+
cursor: pointer;
|
|
262
|
+
background-color: var(--notra-checklist-bg);
|
|
263
|
+
transition:
|
|
264
|
+
background-color 80ms ease-out,
|
|
265
|
+
border-color 80ms ease-out;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.notra .tiptap ul[data-type='taskList'] li label span::before,
|
|
269
|
+
.notra-reader ul[data-type='taskList'] li label span::before {
|
|
270
|
+
content: '';
|
|
271
|
+
position: absolute;
|
|
272
|
+
left: 50%;
|
|
273
|
+
top: 50%;
|
|
274
|
+
transform: translate(-50%, -50%);
|
|
275
|
+
width: 0.75em;
|
|
276
|
+
height: 0.75em;
|
|
277
|
+
background-color: var(--notra-checklist-check-color);
|
|
278
|
+
opacity: 0;
|
|
279
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='currentColor' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M21.4142 4.58579C22.1953 5.36683 22.1953 6.63317 21.4142 7.41421L10.4142 18.4142C9.63317 19.1953 8.36684 19.1953 7.58579 18.4142L2.58579 13.4142C1.80474 12.6332 1.80474 11.3668 2.58579 10.5858C3.36683 9.80474 4.63317 9.80474 5.41421 10.5858L9 14.1716L18.5858 4.58579C19.3668 3.80474 20.6332 3.80474 21.4142 4.58579Z' fill='currentColor'/%3E%3C/svg%3E")
|
|
280
|
+
center / contain no-repeat;
|
|
281
|
+
mask: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='currentColor' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M21.4142 4.58579C22.1953 5.36683 22.1953 6.63317 21.4142 7.41421L10.4142 18.4142C9.63317 19.1953 8.36684 19.1953 7.58579 18.4142L2.58579 13.4142C1.80474 12.6332 1.80474 11.3668 2.58579 10.5858C3.36683 9.80474 4.63317 9.80474 5.41421 10.5858L9 14.1716L18.5858 4.58579C19.3668 3.80474 20.6332 3.80474 21.4142 4.58579Z' fill='currentColor'/%3E%3C/svg%3E")
|
|
282
|
+
center / contain no-repeat;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.notra
|
|
286
|
+
.tiptap
|
|
287
|
+
ul[data-type='taskList']
|
|
288
|
+
li
|
|
289
|
+
label
|
|
290
|
+
input[type='checkbox']:checked
|
|
291
|
+
+ span,
|
|
292
|
+
.notra-reader
|
|
293
|
+
ul[data-type='taskList']
|
|
294
|
+
li
|
|
295
|
+
label
|
|
296
|
+
input[type='checkbox']:checked
|
|
297
|
+
+ span {
|
|
298
|
+
background: var(--notra-checklist-bg-active);
|
|
299
|
+
border-color: var(--notra-checklist-border-active);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.notra
|
|
303
|
+
.tiptap
|
|
304
|
+
ul[data-type='taskList']
|
|
305
|
+
li
|
|
306
|
+
label
|
|
307
|
+
input[type='checkbox']:checked
|
|
308
|
+
+ span::before,
|
|
309
|
+
.notra-reader
|
|
310
|
+
ul[data-type='taskList']
|
|
311
|
+
li
|
|
312
|
+
label
|
|
313
|
+
input[type='checkbox']:checked
|
|
314
|
+
+ span::before {
|
|
315
|
+
opacity: 1;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.notra .tiptap ul[data-type='taskList'] li div,
|
|
319
|
+
.notra-reader ul[data-type='taskList'] li div {
|
|
320
|
+
flex: 1 1 0%;
|
|
321
|
+
min-width: 0;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/* =====================
|
|
325
|
+
Code — Inline
|
|
326
|
+
===================== */
|
|
327
|
+
.notra .tiptap code,
|
|
328
|
+
.notra-reader code {
|
|
329
|
+
background-color: var(--notra-code-bg);
|
|
330
|
+
color: var(--notra-code-text);
|
|
331
|
+
border: 1px solid var(--notra-code-border);
|
|
332
|
+
font-family: var(--notra-font-mono);
|
|
333
|
+
font-size: 0.875em;
|
|
334
|
+
line-height: 1.4;
|
|
335
|
+
border-radius: var(--notra-radius);
|
|
336
|
+
padding: 0.1em 0.2em;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* =====================
|
|
340
|
+
Code — Block
|
|
341
|
+
===================== */
|
|
342
|
+
.notra .tiptap pre,
|
|
343
|
+
.notra-reader pre {
|
|
344
|
+
background-color: var(--notra-codeblock-bg);
|
|
345
|
+
color: var(--notra-codeblock-text);
|
|
346
|
+
border: 1px solid var(--notra-codeblock-border);
|
|
347
|
+
margin-top: 1.5em;
|
|
348
|
+
margin-bottom: 1.5em;
|
|
349
|
+
padding: 1em;
|
|
350
|
+
font-size: 1rem;
|
|
351
|
+
border-radius: var(--notra-radius);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.notra .tiptap pre code,
|
|
355
|
+
.notra-reader pre code {
|
|
356
|
+
background-color: transparent;
|
|
357
|
+
border: none;
|
|
358
|
+
border-radius: 0;
|
|
359
|
+
color: inherit;
|
|
360
|
+
padding: 0;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/* =====================
|
|
364
|
+
Blockquote
|
|
365
|
+
===================== */
|
|
366
|
+
.notra .tiptap blockquote,
|
|
367
|
+
.notra-reader blockquote {
|
|
368
|
+
position: relative;
|
|
369
|
+
padding-left: 1em;
|
|
370
|
+
padding-top: 0.375em;
|
|
371
|
+
padding-bottom: 0.375em;
|
|
372
|
+
margin: 1.5rem 0;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.notra .tiptap blockquote p,
|
|
376
|
+
.notra-reader blockquote p {
|
|
377
|
+
margin-top: 0;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.notra .tiptap blockquote::before,
|
|
381
|
+
.notra-reader blockquote::before {
|
|
382
|
+
position: absolute;
|
|
383
|
+
bottom: 0;
|
|
384
|
+
left: 0;
|
|
385
|
+
top: 0;
|
|
386
|
+
height: 100%;
|
|
387
|
+
width: 0.25em;
|
|
388
|
+
background-color: var(--notra-blockquote-bar);
|
|
389
|
+
content: '';
|
|
390
|
+
border-radius: 0;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/* =====================
|
|
394
|
+
Horizontal Rule
|
|
395
|
+
===================== */
|
|
396
|
+
.notra .tiptap hr,
|
|
397
|
+
.notra-reader hr {
|
|
398
|
+
border: none;
|
|
399
|
+
height: 1px;
|
|
400
|
+
background-color: var(--notra-hr-color);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.notra .tiptap [data-type='horizontalRule'],
|
|
404
|
+
.notra-reader [data-type='horizontalRule'] {
|
|
405
|
+
margin-top: 2.25em;
|
|
406
|
+
margin-bottom: 2.25em;
|
|
407
|
+
padding-top: 0.75rem;
|
|
408
|
+
padding-bottom: 0.75rem;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/* =====================
|
|
412
|
+
Links
|
|
413
|
+
===================== */
|
|
414
|
+
.notra .tiptap a,
|
|
415
|
+
.notra-reader a {
|
|
416
|
+
color: var(--notra-color-link);
|
|
417
|
+
text-decoration: underline;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/* =====================
|
|
421
|
+
Inline Text Decoration
|
|
422
|
+
===================== */
|
|
423
|
+
.notra .tiptap a span,
|
|
424
|
+
.notra-reader a span {
|
|
425
|
+
text-decoration: underline;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.notra .tiptap s span,
|
|
429
|
+
.notra-reader s span {
|
|
430
|
+
text-decoration: line-through;
|
|
431
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "notra-editor",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "A Markdown-first rich text editor for React",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"editor",
|
|
9
|
+
"markdown",
|
|
10
|
+
"rich-text",
|
|
11
|
+
"tiptap",
|
|
12
|
+
"react",
|
|
13
|
+
"notion"
|
|
14
|
+
],
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"default": "./dist/index.mjs"
|
|
20
|
+
},
|
|
21
|
+
"require": {
|
|
22
|
+
"types": "./dist/index.d.cts",
|
|
23
|
+
"default": "./dist/index.cjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"./themes/default/shared.css": "./dist/themes/default/shared.css",
|
|
27
|
+
"./themes/default/editor.css": "./dist/themes/default/editor.css",
|
|
28
|
+
"./themes/default/reader.css": "./dist/themes/default/reader.css"
|
|
29
|
+
},
|
|
30
|
+
"main": "./dist/index.cjs",
|
|
31
|
+
"module": "./dist/index.mjs",
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
38
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@tiptap/core": "^3.22.4",
|
|
42
|
+
"@tiptap/extension-link": "^3.22.4",
|
|
43
|
+
"@tiptap/extension-list": "^3.22.4",
|
|
44
|
+
"@tiptap/pm": "^3.22.4",
|
|
45
|
+
"@tiptap/react": "^3.22.4",
|
|
46
|
+
"@tiptap/starter-kit": "^3.22.4",
|
|
47
|
+
"@tiptap/static-renderer": "^3.22.4",
|
|
48
|
+
"tiptap-markdown": "^0.8.10"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
52
|
+
"@testing-library/react": "^16.3.0",
|
|
53
|
+
"@types/react": "19.2.2",
|
|
54
|
+
"@types/react-dom": "19.2.2",
|
|
55
|
+
"jsdom": "^26.1.0",
|
|
56
|
+
"react": "^19.2.0",
|
|
57
|
+
"react-dom": "^19.2.0",
|
|
58
|
+
"tsup": "^8.4.0",
|
|
59
|
+
"typescript": "^5.8.0",
|
|
60
|
+
"vitest": "^4.0.18"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsup",
|
|
64
|
+
"dev": "tsup --watch",
|
|
65
|
+
"test": "vitest run --passWithNoTests",
|
|
66
|
+
"test:watch": "vitest",
|
|
67
|
+
"pub:beta": "pnpm build && pnpm publish --tag beta --access public",
|
|
68
|
+
"pub:release": "pnpm build && pnpm publish --access public"
|
|
69
|
+
}
|
|
70
|
+
}
|