@mdfn/source 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/README.md +6 -0
- package/dist/index.cjs +216 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +39 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +187 -0
- package/dist/index.js.map +1 -0
- package/package.json +37 -0
package/README.md
ADDED
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
MDFN_SOURCE_VERSION: () => MDFN_SOURCE_VERSION,
|
|
24
|
+
createModeController: () => createModeController,
|
|
25
|
+
createPreview: () => createPreview,
|
|
26
|
+
createSourceEditor: () => createSourceEditor,
|
|
27
|
+
sourceInternals: () => sourceInternals
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(index_exports);
|
|
30
|
+
var import_commands = require("@codemirror/commands");
|
|
31
|
+
var import_lang_markdown = require("@codemirror/lang-markdown");
|
|
32
|
+
var import_language = require("@codemirror/language");
|
|
33
|
+
var import_lint = require("@codemirror/lint");
|
|
34
|
+
var import_search = require("@codemirror/search");
|
|
35
|
+
var import_state = require("@codemirror/state");
|
|
36
|
+
var import_view = require("@codemirror/view");
|
|
37
|
+
var import_core = require("@mdfn/core");
|
|
38
|
+
var import_render = require("@mdfn/render");
|
|
39
|
+
function diagnostics(controller) {
|
|
40
|
+
const length = controller.getState().markdown.length;
|
|
41
|
+
return controller.getState().diagnostics.map((entry) => ({
|
|
42
|
+
from: Math.max(0, Math.min(length, entry.source?.from ?? 0)),
|
|
43
|
+
to: Math.max(0, Math.min(length, entry.source?.to ?? entry.source?.from ?? 0)),
|
|
44
|
+
severity: entry.severity === "error" ? "error" : entry.severity === "warning" ? "warning" : "info",
|
|
45
|
+
message: entry.message,
|
|
46
|
+
source: entry.extension ?? "mdfn"
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
function createSourceEditor(options) {
|
|
50
|
+
if (typeof document === "undefined") throw new Error("MDFN_SOURCE_BROWSER_REQUIRED");
|
|
51
|
+
let syncing = false;
|
|
52
|
+
let readOnly = options.readOnly ?? false;
|
|
53
|
+
const readOnlyConfig = new import_state.Compartment();
|
|
54
|
+
const extensionKeys = Object.entries(options.controller.extensions.keymap).map(([key, command]) => ({ key, run: () => !readOnly && options.controller.run(command) }));
|
|
55
|
+
const extensions = [
|
|
56
|
+
options.lineNumbers === false ? [] : (0, import_view.lineNumbers)(),
|
|
57
|
+
(0, import_view.drawSelection)(),
|
|
58
|
+
(0, import_view.dropCursor)(),
|
|
59
|
+
(0, import_view.highlightActiveLine)(),
|
|
60
|
+
(0, import_language.bracketMatching)(),
|
|
61
|
+
(0, import_language.syntaxHighlighting)(import_language.defaultHighlightStyle, { fallback: true }),
|
|
62
|
+
(0, import_lang_markdown.markdown)({ base: import_lang_markdown.markdownLanguage }),
|
|
63
|
+
(0, import_lint.lintGutter)(),
|
|
64
|
+
(0, import_lint.linter)(() => diagnostics(options.controller), { delay: 100 }),
|
|
65
|
+
import_view.keymap.of([
|
|
66
|
+
...extensionKeys,
|
|
67
|
+
{ key: "Mod-z", run: () => !readOnly && options.controller.undo() },
|
|
68
|
+
{ key: "Mod-Shift-z", run: () => !readOnly && options.controller.redo() },
|
|
69
|
+
{ key: "Mod-y", run: () => !readOnly && options.controller.redo() },
|
|
70
|
+
import_commands.indentWithTab,
|
|
71
|
+
...import_commands.defaultKeymap,
|
|
72
|
+
...import_search.searchKeymap
|
|
73
|
+
]),
|
|
74
|
+
readOnlyConfig.of([
|
|
75
|
+
import_state.EditorState.readOnly.of(readOnly),
|
|
76
|
+
import_view.EditorView.editable.of(!readOnly)
|
|
77
|
+
]),
|
|
78
|
+
import_view.EditorView.contentAttributes.of({ "aria-label": options.ariaLabel ?? "Markdown source", "data-mdfn-editor": "source" }),
|
|
79
|
+
import_view.EditorView.domEventHandlers({
|
|
80
|
+
focus: () => {
|
|
81
|
+
options.onFocus?.();
|
|
82
|
+
return false;
|
|
83
|
+
},
|
|
84
|
+
blur: () => {
|
|
85
|
+
options.onBlur?.();
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}),
|
|
89
|
+
import_view.EditorView.updateListener.of((update) => {
|
|
90
|
+
if (syncing || !update.docChanged && !update.selectionSet) return;
|
|
91
|
+
let transaction = new import_core.Transaction().withSource("source");
|
|
92
|
+
if (update.docChanged) {
|
|
93
|
+
let current = options.controller.getState().markdown;
|
|
94
|
+
let offset = 0;
|
|
95
|
+
const corrections = [];
|
|
96
|
+
const pasted = update.transactions.some((entry) => entry.isUserEvent("input.paste"));
|
|
97
|
+
const rules = pasted ? options.controller.extensions.pasteRules : options.controller.extensions.inputRules;
|
|
98
|
+
update.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
|
|
99
|
+
const from = fromA + offset;
|
|
100
|
+
const to = toA + offset;
|
|
101
|
+
const rawInsert = inserted.toString();
|
|
102
|
+
const insert = (0, import_core.applyExtensionTextRules)(rawInsert, current, from, to, rules);
|
|
103
|
+
transaction = transaction.replaceSource(from, to, insert);
|
|
104
|
+
current = `${current.slice(0, from)}${insert}${current.slice(to)}`;
|
|
105
|
+
offset += insert.length - (toA - fromA);
|
|
106
|
+
if (insert !== rawInsert) corrections.push({ from: fromB, to: toB, insert });
|
|
107
|
+
});
|
|
108
|
+
if (corrections.length > 0) {
|
|
109
|
+
syncing = true;
|
|
110
|
+
update.view.dispatch({ changes: corrections });
|
|
111
|
+
syncing = false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const main = update.view.state.selection.main;
|
|
115
|
+
transaction = transaction.setSelection({ kind: "text", anchor: main.anchor, head: main.head });
|
|
116
|
+
try {
|
|
117
|
+
options.controller.dispatch(transaction);
|
|
118
|
+
} catch {
|
|
119
|
+
const canonical = options.controller.getState();
|
|
120
|
+
const current = update.view.state.doc.toString();
|
|
121
|
+
const selection = canonical.selection?.kind === "text" ? {
|
|
122
|
+
anchor: Math.min(canonical.selection.anchor, canonical.markdown.length),
|
|
123
|
+
head: Math.min(canonical.selection.head, canonical.markdown.length)
|
|
124
|
+
} : void 0;
|
|
125
|
+
syncing = true;
|
|
126
|
+
update.view.dispatch({
|
|
127
|
+
changes: { from: 0, to: current.length, insert: canonical.markdown },
|
|
128
|
+
...selection ? { selection } : {}
|
|
129
|
+
});
|
|
130
|
+
syncing = false;
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
];
|
|
134
|
+
const controllerSelection = options.controller.getState().selection;
|
|
135
|
+
const initialSelection = controllerSelection?.kind === "text" ? { anchor: Math.min(controllerSelection.anchor, options.controller.getState().markdown.length), head: Math.min(controllerSelection.head, options.controller.getState().markdown.length) } : void 0;
|
|
136
|
+
const view = new import_view.EditorView({
|
|
137
|
+
parent: options.target,
|
|
138
|
+
state: import_state.EditorState.create({ doc: options.controller.getState().markdown, selection: initialSelection, extensions })
|
|
139
|
+
});
|
|
140
|
+
const unsubscribe = options.controller.subscribe((change) => {
|
|
141
|
+
if (change.source === "source") return;
|
|
142
|
+
const current = view.state.doc.toString();
|
|
143
|
+
const selection = change.current.selection?.kind === "text" ? { anchor: change.current.selection.anchor, head: change.current.selection.head } : void 0;
|
|
144
|
+
const documentChanged = current !== change.current.markdown;
|
|
145
|
+
if (!documentChanged && !change.selectionChanged) return;
|
|
146
|
+
syncing = true;
|
|
147
|
+
view.dispatch({
|
|
148
|
+
...documentChanged ? { changes: { from: 0, to: current.length, insert: change.current.markdown } } : {},
|
|
149
|
+
...selection ? { selection } : {}
|
|
150
|
+
});
|
|
151
|
+
syncing = false;
|
|
152
|
+
});
|
|
153
|
+
return {
|
|
154
|
+
view,
|
|
155
|
+
focus() {
|
|
156
|
+
view.focus();
|
|
157
|
+
},
|
|
158
|
+
setReadOnly(value) {
|
|
159
|
+
readOnly = value;
|
|
160
|
+
view.dispatch({
|
|
161
|
+
effects: readOnlyConfig.reconfigure([
|
|
162
|
+
import_state.EditorState.readOnly.of(readOnly),
|
|
163
|
+
import_view.EditorView.editable.of(!readOnly)
|
|
164
|
+
])
|
|
165
|
+
});
|
|
166
|
+
},
|
|
167
|
+
destroy() {
|
|
168
|
+
unsubscribe();
|
|
169
|
+
view.destroy();
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function createPreview(controller, policy) {
|
|
174
|
+
const rendered = (0, import_render.renderHtml)(controller.getState().document, {
|
|
175
|
+
...policy,
|
|
176
|
+
extensions: policy?.extensions ?? controller.extensions
|
|
177
|
+
});
|
|
178
|
+
return { html: rendered.html, diagnostics: rendered.diagnostics };
|
|
179
|
+
}
|
|
180
|
+
function createModeController(initial = "visual") {
|
|
181
|
+
let mode = initial;
|
|
182
|
+
let destroyed = false;
|
|
183
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
184
|
+
return {
|
|
185
|
+
getMode() {
|
|
186
|
+
if (destroyed) throw new Error("MDFN_MODE_CONTROLLER_DESTROYED");
|
|
187
|
+
return mode;
|
|
188
|
+
},
|
|
189
|
+
setMode(next) {
|
|
190
|
+
if (destroyed) throw new Error("MDFN_MODE_CONTROLLER_DESTROYED");
|
|
191
|
+
if (next === mode) return;
|
|
192
|
+
mode = next;
|
|
193
|
+
listeners.forEach((listener) => listener(mode));
|
|
194
|
+
},
|
|
195
|
+
subscribe(listener) {
|
|
196
|
+
if (destroyed) throw new Error("MDFN_MODE_CONTROLLER_DESTROYED");
|
|
197
|
+
listeners.add(listener);
|
|
198
|
+
return () => listeners.delete(listener);
|
|
199
|
+
},
|
|
200
|
+
destroy() {
|
|
201
|
+
destroyed = true;
|
|
202
|
+
listeners.clear();
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
var MDFN_SOURCE_VERSION = "0.1.0";
|
|
207
|
+
var sourceInternals = Object.freeze({ smallestSourceChange: import_core.smallestSourceChange });
|
|
208
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
209
|
+
0 && (module.exports = {
|
|
210
|
+
MDFN_SOURCE_VERSION,
|
|
211
|
+
createModeController,
|
|
212
|
+
createPreview,
|
|
213
|
+
createSourceEditor,
|
|
214
|
+
sourceInternals
|
|
215
|
+
});
|
|
216
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { defaultKeymap, indentWithTab } from \"@codemirror/commands\";\nimport { markdown, markdownLanguage } from \"@codemirror/lang-markdown\";\nimport { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { linter, lintGutter, type Diagnostic as CodeMirrorDiagnostic } from \"@codemirror/lint\";\nimport { searchKeymap } from \"@codemirror/search\";\nimport { Compartment, EditorState as CodeMirrorState } from \"@codemirror/state\";\nimport { drawSelection, dropCursor, EditorView, highlightActiveLine, keymap, lineNumbers } from \"@codemirror/view\";\nimport { applyExtensionTextRules, smallestSourceChange, Transaction, type EditorController } from \"@mdfn/core\";\nimport { renderHtml, type RenderPolicy } from \"@mdfn/render\";\n\nexport type EditorMode = \"visual\" | \"source\" | \"split\" | \"preview\" | \"read-only\";\n\nexport interface SourceEditorOptions {\n readonly target: HTMLElement;\n readonly controller: EditorController;\n readonly readOnly?: boolean;\n readonly lineNumbers?: boolean;\n readonly ariaLabel?: string;\n readonly onFocus?: () => void;\n readonly onBlur?: () => void;\n}\n\nexport interface SourceEditor {\n readonly view: EditorView;\n focus(): void;\n setReadOnly(readOnly: boolean): void;\n destroy(): void;\n}\n\nfunction diagnostics(controller: EditorController): CodeMirrorDiagnostic[] {\n const length = controller.getState().markdown.length;\n return controller.getState().diagnostics.map((entry) => ({\n from: Math.max(0, Math.min(length, entry.source?.from ?? 0)),\n to: Math.max(0, Math.min(length, entry.source?.to ?? entry.source?.from ?? 0)),\n severity: entry.severity === \"error\" ? \"error\" : entry.severity === \"warning\" ? \"warning\" : \"info\",\n message: entry.message,\n source: entry.extension ?? \"mdfn\",\n }));\n}\n\nexport function createSourceEditor(options: SourceEditorOptions): SourceEditor {\n if (typeof document === \"undefined\") throw new Error(\"MDFN_SOURCE_BROWSER_REQUIRED\");\n let syncing = false;\n let readOnly = options.readOnly ?? false;\n const readOnlyConfig = new Compartment();\n const extensionKeys = Object.entries(options.controller.extensions.keymap).map(([key, command]) => ({ key, run: () => !readOnly && options.controller.run(command) }));\n const extensions = [\n options.lineNumbers === false ? [] : lineNumbers(),\n drawSelection(),\n dropCursor(),\n highlightActiveLine(),\n bracketMatching(),\n syntaxHighlighting(defaultHighlightStyle, { fallback: true }),\n markdown({ base: markdownLanguage }),\n lintGutter(),\n linter(() => diagnostics(options.controller), { delay: 100 }),\n keymap.of([\n ...extensionKeys,\n { key: \"Mod-z\", run: () => !readOnly && options.controller.undo() },\n { key: \"Mod-Shift-z\", run: () => !readOnly && options.controller.redo() },\n { key: \"Mod-y\", run: () => !readOnly && options.controller.redo() },\n indentWithTab,\n ...defaultKeymap,\n ...searchKeymap,\n ]),\n readOnlyConfig.of([\n CodeMirrorState.readOnly.of(readOnly),\n EditorView.editable.of(!readOnly),\n ]),\n EditorView.contentAttributes.of({ \"aria-label\": options.ariaLabel ?? \"Markdown source\", \"data-mdfn-editor\": \"source\" }),\n EditorView.domEventHandlers({\n focus: () => { options.onFocus?.(); return false; },\n blur: () => { options.onBlur?.(); return false; },\n }),\n EditorView.updateListener.of((update) => {\n if (syncing || (!update.docChanged && !update.selectionSet)) return;\n let transaction = new Transaction().withSource(\"source\");\n if (update.docChanged) {\n let current = options.controller.getState().markdown;\n let offset = 0;\n const corrections: Array<{ from: number; to: number; insert: string }> = [];\n const pasted = update.transactions.some((entry) => entry.isUserEvent(\"input.paste\"));\n const rules = pasted ? options.controller.extensions.pasteRules : options.controller.extensions.inputRules;\n update.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {\n const from = fromA + offset;\n const to = toA + offset;\n const rawInsert = inserted.toString();\n const insert = applyExtensionTextRules(rawInsert, current, from, to, rules);\n transaction = transaction.replaceSource(from, to, insert);\n current = `${current.slice(0, from)}${insert}${current.slice(to)}`;\n offset += insert.length - (toA - fromA);\n if (insert !== rawInsert) corrections.push({ from: fromB, to: toB, insert });\n });\n if (corrections.length > 0) {\n syncing = true;\n update.view.dispatch({ changes: corrections });\n syncing = false;\n }\n }\n const main = update.view.state.selection.main;\n transaction = transaction.setSelection({ kind: \"text\", anchor: main.anchor, head: main.head });\n try {\n options.controller.dispatch(transaction);\n } catch {\n const canonical = options.controller.getState();\n const current = update.view.state.doc.toString();\n const selection = canonical.selection?.kind === \"text\"\n ? {\n anchor: Math.min(canonical.selection.anchor, canonical.markdown.length),\n head: Math.min(canonical.selection.head, canonical.markdown.length),\n }\n : undefined;\n syncing = true;\n update.view.dispatch({\n changes: { from: 0, to: current.length, insert: canonical.markdown },\n ...(selection ? { selection } : {}),\n });\n syncing = false;\n }\n }),\n ];\n const controllerSelection = options.controller.getState().selection;\n const initialSelection = controllerSelection?.kind === \"text\"\n ? { anchor: Math.min(controllerSelection.anchor, options.controller.getState().markdown.length), head: Math.min(controllerSelection.head, options.controller.getState().markdown.length) }\n : undefined;\n const view = new EditorView({\n parent: options.target,\n state: CodeMirrorState.create({ doc: options.controller.getState().markdown, selection: initialSelection, extensions }),\n });\n const unsubscribe = options.controller.subscribe((change) => {\n if (change.source === \"source\") return;\n const current = view.state.doc.toString();\n const selection = change.current.selection?.kind === \"text\"\n ? { anchor: change.current.selection.anchor, head: change.current.selection.head }\n : undefined;\n const documentChanged = current !== change.current.markdown;\n if (!documentChanged && !change.selectionChanged) return;\n syncing = true;\n view.dispatch({\n ...(documentChanged ? { changes: { from: 0, to: current.length, insert: change.current.markdown } } : {}),\n ...(selection ? { selection } : {}),\n });\n syncing = false;\n });\n return {\n view,\n focus() { view.focus(); },\n setReadOnly(value) {\n readOnly = value;\n view.dispatch({\n effects: readOnlyConfig.reconfigure([\n CodeMirrorState.readOnly.of(readOnly),\n EditorView.editable.of(!readOnly),\n ]),\n });\n },\n destroy() { unsubscribe(); view.destroy(); },\n };\n}\n\nexport interface PreviewResult {\n readonly html: string;\n readonly diagnostics: ReturnType<typeof renderHtml>[\"diagnostics\"];\n}\n\nexport function createPreview(controller: EditorController, policy?: RenderPolicy): PreviewResult {\n const rendered = renderHtml(controller.getState().document, {\n ...policy,\n extensions: policy?.extensions ?? controller.extensions,\n });\n return { html: rendered.html, diagnostics: rendered.diagnostics };\n}\n\nexport interface ModeController {\n getMode(): EditorMode;\n setMode(mode: EditorMode): void;\n subscribe(listener: (mode: EditorMode) => void): () => void;\n destroy(): void;\n}\n\nexport function createModeController(initial: EditorMode = \"visual\"): ModeController {\n let mode = initial;\n let destroyed = false;\n const listeners = new Set<(mode: EditorMode) => void>();\n return {\n getMode() { if (destroyed) throw new Error(\"MDFN_MODE_CONTROLLER_DESTROYED\"); return mode; },\n setMode(next) { if (destroyed) throw new Error(\"MDFN_MODE_CONTROLLER_DESTROYED\"); if (next === mode) return; mode = next; listeners.forEach((listener) => listener(mode)); },\n subscribe(listener) { if (destroyed) throw new Error(\"MDFN_MODE_CONTROLLER_DESTROYED\"); listeners.add(listener); return () => listeners.delete(listener); },\n destroy() { destroyed = true; listeners.clear(); },\n };\n}\n\nexport const MDFN_SOURCE_VERSION = \"0.1.0\" as const;\n\nexport const sourceInternals = Object.freeze({ smallestSourceChange });\n"],"mappings":"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","names":["CodeMirrorState"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { EditorView } from '@codemirror/view';
|
|
2
|
+
import { EditorController, smallestSourceChange } from '@mdfn/core';
|
|
3
|
+
import { renderHtml, RenderPolicy } from '@mdfn/render';
|
|
4
|
+
|
|
5
|
+
type EditorMode = "visual" | "source" | "split" | "preview" | "read-only";
|
|
6
|
+
interface SourceEditorOptions {
|
|
7
|
+
readonly target: HTMLElement;
|
|
8
|
+
readonly controller: EditorController;
|
|
9
|
+
readonly readOnly?: boolean;
|
|
10
|
+
readonly lineNumbers?: boolean;
|
|
11
|
+
readonly ariaLabel?: string;
|
|
12
|
+
readonly onFocus?: () => void;
|
|
13
|
+
readonly onBlur?: () => void;
|
|
14
|
+
}
|
|
15
|
+
interface SourceEditor {
|
|
16
|
+
readonly view: EditorView;
|
|
17
|
+
focus(): void;
|
|
18
|
+
setReadOnly(readOnly: boolean): void;
|
|
19
|
+
destroy(): void;
|
|
20
|
+
}
|
|
21
|
+
declare function createSourceEditor(options: SourceEditorOptions): SourceEditor;
|
|
22
|
+
interface PreviewResult {
|
|
23
|
+
readonly html: string;
|
|
24
|
+
readonly diagnostics: ReturnType<typeof renderHtml>["diagnostics"];
|
|
25
|
+
}
|
|
26
|
+
declare function createPreview(controller: EditorController, policy?: RenderPolicy): PreviewResult;
|
|
27
|
+
interface ModeController {
|
|
28
|
+
getMode(): EditorMode;
|
|
29
|
+
setMode(mode: EditorMode): void;
|
|
30
|
+
subscribe(listener: (mode: EditorMode) => void): () => void;
|
|
31
|
+
destroy(): void;
|
|
32
|
+
}
|
|
33
|
+
declare function createModeController(initial?: EditorMode): ModeController;
|
|
34
|
+
declare const MDFN_SOURCE_VERSION: "0.1.0";
|
|
35
|
+
declare const sourceInternals: Readonly<{
|
|
36
|
+
smallestSourceChange: typeof smallestSourceChange;
|
|
37
|
+
}>;
|
|
38
|
+
|
|
39
|
+
export { type EditorMode, MDFN_SOURCE_VERSION, type ModeController, type PreviewResult, type SourceEditor, type SourceEditorOptions, createModeController, createPreview, createSourceEditor, sourceInternals };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { EditorView } from '@codemirror/view';
|
|
2
|
+
import { EditorController, smallestSourceChange } from '@mdfn/core';
|
|
3
|
+
import { renderHtml, RenderPolicy } from '@mdfn/render';
|
|
4
|
+
|
|
5
|
+
type EditorMode = "visual" | "source" | "split" | "preview" | "read-only";
|
|
6
|
+
interface SourceEditorOptions {
|
|
7
|
+
readonly target: HTMLElement;
|
|
8
|
+
readonly controller: EditorController;
|
|
9
|
+
readonly readOnly?: boolean;
|
|
10
|
+
readonly lineNumbers?: boolean;
|
|
11
|
+
readonly ariaLabel?: string;
|
|
12
|
+
readonly onFocus?: () => void;
|
|
13
|
+
readonly onBlur?: () => void;
|
|
14
|
+
}
|
|
15
|
+
interface SourceEditor {
|
|
16
|
+
readonly view: EditorView;
|
|
17
|
+
focus(): void;
|
|
18
|
+
setReadOnly(readOnly: boolean): void;
|
|
19
|
+
destroy(): void;
|
|
20
|
+
}
|
|
21
|
+
declare function createSourceEditor(options: SourceEditorOptions): SourceEditor;
|
|
22
|
+
interface PreviewResult {
|
|
23
|
+
readonly html: string;
|
|
24
|
+
readonly diagnostics: ReturnType<typeof renderHtml>["diagnostics"];
|
|
25
|
+
}
|
|
26
|
+
declare function createPreview(controller: EditorController, policy?: RenderPolicy): PreviewResult;
|
|
27
|
+
interface ModeController {
|
|
28
|
+
getMode(): EditorMode;
|
|
29
|
+
setMode(mode: EditorMode): void;
|
|
30
|
+
subscribe(listener: (mode: EditorMode) => void): () => void;
|
|
31
|
+
destroy(): void;
|
|
32
|
+
}
|
|
33
|
+
declare function createModeController(initial?: EditorMode): ModeController;
|
|
34
|
+
declare const MDFN_SOURCE_VERSION: "0.1.0";
|
|
35
|
+
declare const sourceInternals: Readonly<{
|
|
36
|
+
smallestSourceChange: typeof smallestSourceChange;
|
|
37
|
+
}>;
|
|
38
|
+
|
|
39
|
+
export { type EditorMode, MDFN_SOURCE_VERSION, type ModeController, type PreviewResult, type SourceEditor, type SourceEditorOptions, createModeController, createPreview, createSourceEditor, sourceInternals };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { defaultKeymap, indentWithTab } from "@codemirror/commands";
|
|
3
|
+
import { markdown, markdownLanguage } from "@codemirror/lang-markdown";
|
|
4
|
+
import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
|
|
5
|
+
import { linter, lintGutter } from "@codemirror/lint";
|
|
6
|
+
import { searchKeymap } from "@codemirror/search";
|
|
7
|
+
import { Compartment, EditorState as CodeMirrorState } from "@codemirror/state";
|
|
8
|
+
import { drawSelection, dropCursor, EditorView, highlightActiveLine, keymap, lineNumbers } from "@codemirror/view";
|
|
9
|
+
import { applyExtensionTextRules, smallestSourceChange, Transaction } from "@mdfn/core";
|
|
10
|
+
import { renderHtml } from "@mdfn/render";
|
|
11
|
+
function diagnostics(controller) {
|
|
12
|
+
const length = controller.getState().markdown.length;
|
|
13
|
+
return controller.getState().diagnostics.map((entry) => ({
|
|
14
|
+
from: Math.max(0, Math.min(length, entry.source?.from ?? 0)),
|
|
15
|
+
to: Math.max(0, Math.min(length, entry.source?.to ?? entry.source?.from ?? 0)),
|
|
16
|
+
severity: entry.severity === "error" ? "error" : entry.severity === "warning" ? "warning" : "info",
|
|
17
|
+
message: entry.message,
|
|
18
|
+
source: entry.extension ?? "mdfn"
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
function createSourceEditor(options) {
|
|
22
|
+
if (typeof document === "undefined") throw new Error("MDFN_SOURCE_BROWSER_REQUIRED");
|
|
23
|
+
let syncing = false;
|
|
24
|
+
let readOnly = options.readOnly ?? false;
|
|
25
|
+
const readOnlyConfig = new Compartment();
|
|
26
|
+
const extensionKeys = Object.entries(options.controller.extensions.keymap).map(([key, command]) => ({ key, run: () => !readOnly && options.controller.run(command) }));
|
|
27
|
+
const extensions = [
|
|
28
|
+
options.lineNumbers === false ? [] : lineNumbers(),
|
|
29
|
+
drawSelection(),
|
|
30
|
+
dropCursor(),
|
|
31
|
+
highlightActiveLine(),
|
|
32
|
+
bracketMatching(),
|
|
33
|
+
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
|
34
|
+
markdown({ base: markdownLanguage }),
|
|
35
|
+
lintGutter(),
|
|
36
|
+
linter(() => diagnostics(options.controller), { delay: 100 }),
|
|
37
|
+
keymap.of([
|
|
38
|
+
...extensionKeys,
|
|
39
|
+
{ key: "Mod-z", run: () => !readOnly && options.controller.undo() },
|
|
40
|
+
{ key: "Mod-Shift-z", run: () => !readOnly && options.controller.redo() },
|
|
41
|
+
{ key: "Mod-y", run: () => !readOnly && options.controller.redo() },
|
|
42
|
+
indentWithTab,
|
|
43
|
+
...defaultKeymap,
|
|
44
|
+
...searchKeymap
|
|
45
|
+
]),
|
|
46
|
+
readOnlyConfig.of([
|
|
47
|
+
CodeMirrorState.readOnly.of(readOnly),
|
|
48
|
+
EditorView.editable.of(!readOnly)
|
|
49
|
+
]),
|
|
50
|
+
EditorView.contentAttributes.of({ "aria-label": options.ariaLabel ?? "Markdown source", "data-mdfn-editor": "source" }),
|
|
51
|
+
EditorView.domEventHandlers({
|
|
52
|
+
focus: () => {
|
|
53
|
+
options.onFocus?.();
|
|
54
|
+
return false;
|
|
55
|
+
},
|
|
56
|
+
blur: () => {
|
|
57
|
+
options.onBlur?.();
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}),
|
|
61
|
+
EditorView.updateListener.of((update) => {
|
|
62
|
+
if (syncing || !update.docChanged && !update.selectionSet) return;
|
|
63
|
+
let transaction = new Transaction().withSource("source");
|
|
64
|
+
if (update.docChanged) {
|
|
65
|
+
let current = options.controller.getState().markdown;
|
|
66
|
+
let offset = 0;
|
|
67
|
+
const corrections = [];
|
|
68
|
+
const pasted = update.transactions.some((entry) => entry.isUserEvent("input.paste"));
|
|
69
|
+
const rules = pasted ? options.controller.extensions.pasteRules : options.controller.extensions.inputRules;
|
|
70
|
+
update.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
|
|
71
|
+
const from = fromA + offset;
|
|
72
|
+
const to = toA + offset;
|
|
73
|
+
const rawInsert = inserted.toString();
|
|
74
|
+
const insert = applyExtensionTextRules(rawInsert, current, from, to, rules);
|
|
75
|
+
transaction = transaction.replaceSource(from, to, insert);
|
|
76
|
+
current = `${current.slice(0, from)}${insert}${current.slice(to)}`;
|
|
77
|
+
offset += insert.length - (toA - fromA);
|
|
78
|
+
if (insert !== rawInsert) corrections.push({ from: fromB, to: toB, insert });
|
|
79
|
+
});
|
|
80
|
+
if (corrections.length > 0) {
|
|
81
|
+
syncing = true;
|
|
82
|
+
update.view.dispatch({ changes: corrections });
|
|
83
|
+
syncing = false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const main = update.view.state.selection.main;
|
|
87
|
+
transaction = transaction.setSelection({ kind: "text", anchor: main.anchor, head: main.head });
|
|
88
|
+
try {
|
|
89
|
+
options.controller.dispatch(transaction);
|
|
90
|
+
} catch {
|
|
91
|
+
const canonical = options.controller.getState();
|
|
92
|
+
const current = update.view.state.doc.toString();
|
|
93
|
+
const selection = canonical.selection?.kind === "text" ? {
|
|
94
|
+
anchor: Math.min(canonical.selection.anchor, canonical.markdown.length),
|
|
95
|
+
head: Math.min(canonical.selection.head, canonical.markdown.length)
|
|
96
|
+
} : void 0;
|
|
97
|
+
syncing = true;
|
|
98
|
+
update.view.dispatch({
|
|
99
|
+
changes: { from: 0, to: current.length, insert: canonical.markdown },
|
|
100
|
+
...selection ? { selection } : {}
|
|
101
|
+
});
|
|
102
|
+
syncing = false;
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
];
|
|
106
|
+
const controllerSelection = options.controller.getState().selection;
|
|
107
|
+
const initialSelection = controllerSelection?.kind === "text" ? { anchor: Math.min(controllerSelection.anchor, options.controller.getState().markdown.length), head: Math.min(controllerSelection.head, options.controller.getState().markdown.length) } : void 0;
|
|
108
|
+
const view = new EditorView({
|
|
109
|
+
parent: options.target,
|
|
110
|
+
state: CodeMirrorState.create({ doc: options.controller.getState().markdown, selection: initialSelection, extensions })
|
|
111
|
+
});
|
|
112
|
+
const unsubscribe = options.controller.subscribe((change) => {
|
|
113
|
+
if (change.source === "source") return;
|
|
114
|
+
const current = view.state.doc.toString();
|
|
115
|
+
const selection = change.current.selection?.kind === "text" ? { anchor: change.current.selection.anchor, head: change.current.selection.head } : void 0;
|
|
116
|
+
const documentChanged = current !== change.current.markdown;
|
|
117
|
+
if (!documentChanged && !change.selectionChanged) return;
|
|
118
|
+
syncing = true;
|
|
119
|
+
view.dispatch({
|
|
120
|
+
...documentChanged ? { changes: { from: 0, to: current.length, insert: change.current.markdown } } : {},
|
|
121
|
+
...selection ? { selection } : {}
|
|
122
|
+
});
|
|
123
|
+
syncing = false;
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
view,
|
|
127
|
+
focus() {
|
|
128
|
+
view.focus();
|
|
129
|
+
},
|
|
130
|
+
setReadOnly(value) {
|
|
131
|
+
readOnly = value;
|
|
132
|
+
view.dispatch({
|
|
133
|
+
effects: readOnlyConfig.reconfigure([
|
|
134
|
+
CodeMirrorState.readOnly.of(readOnly),
|
|
135
|
+
EditorView.editable.of(!readOnly)
|
|
136
|
+
])
|
|
137
|
+
});
|
|
138
|
+
},
|
|
139
|
+
destroy() {
|
|
140
|
+
unsubscribe();
|
|
141
|
+
view.destroy();
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function createPreview(controller, policy) {
|
|
146
|
+
const rendered = renderHtml(controller.getState().document, {
|
|
147
|
+
...policy,
|
|
148
|
+
extensions: policy?.extensions ?? controller.extensions
|
|
149
|
+
});
|
|
150
|
+
return { html: rendered.html, diagnostics: rendered.diagnostics };
|
|
151
|
+
}
|
|
152
|
+
function createModeController(initial = "visual") {
|
|
153
|
+
let mode = initial;
|
|
154
|
+
let destroyed = false;
|
|
155
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
156
|
+
return {
|
|
157
|
+
getMode() {
|
|
158
|
+
if (destroyed) throw new Error("MDFN_MODE_CONTROLLER_DESTROYED");
|
|
159
|
+
return mode;
|
|
160
|
+
},
|
|
161
|
+
setMode(next) {
|
|
162
|
+
if (destroyed) throw new Error("MDFN_MODE_CONTROLLER_DESTROYED");
|
|
163
|
+
if (next === mode) return;
|
|
164
|
+
mode = next;
|
|
165
|
+
listeners.forEach((listener) => listener(mode));
|
|
166
|
+
},
|
|
167
|
+
subscribe(listener) {
|
|
168
|
+
if (destroyed) throw new Error("MDFN_MODE_CONTROLLER_DESTROYED");
|
|
169
|
+
listeners.add(listener);
|
|
170
|
+
return () => listeners.delete(listener);
|
|
171
|
+
},
|
|
172
|
+
destroy() {
|
|
173
|
+
destroyed = true;
|
|
174
|
+
listeners.clear();
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
var MDFN_SOURCE_VERSION = "0.1.0";
|
|
179
|
+
var sourceInternals = Object.freeze({ smallestSourceChange });
|
|
180
|
+
export {
|
|
181
|
+
MDFN_SOURCE_VERSION,
|
|
182
|
+
createModeController,
|
|
183
|
+
createPreview,
|
|
184
|
+
createSourceEditor,
|
|
185
|
+
sourceInternals
|
|
186
|
+
};
|
|
187
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { defaultKeymap, indentWithTab } from \"@codemirror/commands\";\nimport { markdown, markdownLanguage } from \"@codemirror/lang-markdown\";\nimport { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { linter, lintGutter, type Diagnostic as CodeMirrorDiagnostic } from \"@codemirror/lint\";\nimport { searchKeymap } from \"@codemirror/search\";\nimport { Compartment, EditorState as CodeMirrorState } from \"@codemirror/state\";\nimport { drawSelection, dropCursor, EditorView, highlightActiveLine, keymap, lineNumbers } from \"@codemirror/view\";\nimport { applyExtensionTextRules, smallestSourceChange, Transaction, type EditorController } from \"@mdfn/core\";\nimport { renderHtml, type RenderPolicy } from \"@mdfn/render\";\n\nexport type EditorMode = \"visual\" | \"source\" | \"split\" | \"preview\" | \"read-only\";\n\nexport interface SourceEditorOptions {\n readonly target: HTMLElement;\n readonly controller: EditorController;\n readonly readOnly?: boolean;\n readonly lineNumbers?: boolean;\n readonly ariaLabel?: string;\n readonly onFocus?: () => void;\n readonly onBlur?: () => void;\n}\n\nexport interface SourceEditor {\n readonly view: EditorView;\n focus(): void;\n setReadOnly(readOnly: boolean): void;\n destroy(): void;\n}\n\nfunction diagnostics(controller: EditorController): CodeMirrorDiagnostic[] {\n const length = controller.getState().markdown.length;\n return controller.getState().diagnostics.map((entry) => ({\n from: Math.max(0, Math.min(length, entry.source?.from ?? 0)),\n to: Math.max(0, Math.min(length, entry.source?.to ?? entry.source?.from ?? 0)),\n severity: entry.severity === \"error\" ? \"error\" : entry.severity === \"warning\" ? \"warning\" : \"info\",\n message: entry.message,\n source: entry.extension ?? \"mdfn\",\n }));\n}\n\nexport function createSourceEditor(options: SourceEditorOptions): SourceEditor {\n if (typeof document === \"undefined\") throw new Error(\"MDFN_SOURCE_BROWSER_REQUIRED\");\n let syncing = false;\n let readOnly = options.readOnly ?? false;\n const readOnlyConfig = new Compartment();\n const extensionKeys = Object.entries(options.controller.extensions.keymap).map(([key, command]) => ({ key, run: () => !readOnly && options.controller.run(command) }));\n const extensions = [\n options.lineNumbers === false ? [] : lineNumbers(),\n drawSelection(),\n dropCursor(),\n highlightActiveLine(),\n bracketMatching(),\n syntaxHighlighting(defaultHighlightStyle, { fallback: true }),\n markdown({ base: markdownLanguage }),\n lintGutter(),\n linter(() => diagnostics(options.controller), { delay: 100 }),\n keymap.of([\n ...extensionKeys,\n { key: \"Mod-z\", run: () => !readOnly && options.controller.undo() },\n { key: \"Mod-Shift-z\", run: () => !readOnly && options.controller.redo() },\n { key: \"Mod-y\", run: () => !readOnly && options.controller.redo() },\n indentWithTab,\n ...defaultKeymap,\n ...searchKeymap,\n ]),\n readOnlyConfig.of([\n CodeMirrorState.readOnly.of(readOnly),\n EditorView.editable.of(!readOnly),\n ]),\n EditorView.contentAttributes.of({ \"aria-label\": options.ariaLabel ?? \"Markdown source\", \"data-mdfn-editor\": \"source\" }),\n EditorView.domEventHandlers({\n focus: () => { options.onFocus?.(); return false; },\n blur: () => { options.onBlur?.(); return false; },\n }),\n EditorView.updateListener.of((update) => {\n if (syncing || (!update.docChanged && !update.selectionSet)) return;\n let transaction = new Transaction().withSource(\"source\");\n if (update.docChanged) {\n let current = options.controller.getState().markdown;\n let offset = 0;\n const corrections: Array<{ from: number; to: number; insert: string }> = [];\n const pasted = update.transactions.some((entry) => entry.isUserEvent(\"input.paste\"));\n const rules = pasted ? options.controller.extensions.pasteRules : options.controller.extensions.inputRules;\n update.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {\n const from = fromA + offset;\n const to = toA + offset;\n const rawInsert = inserted.toString();\n const insert = applyExtensionTextRules(rawInsert, current, from, to, rules);\n transaction = transaction.replaceSource(from, to, insert);\n current = `${current.slice(0, from)}${insert}${current.slice(to)}`;\n offset += insert.length - (toA - fromA);\n if (insert !== rawInsert) corrections.push({ from: fromB, to: toB, insert });\n });\n if (corrections.length > 0) {\n syncing = true;\n update.view.dispatch({ changes: corrections });\n syncing = false;\n }\n }\n const main = update.view.state.selection.main;\n transaction = transaction.setSelection({ kind: \"text\", anchor: main.anchor, head: main.head });\n try {\n options.controller.dispatch(transaction);\n } catch {\n const canonical = options.controller.getState();\n const current = update.view.state.doc.toString();\n const selection = canonical.selection?.kind === \"text\"\n ? {\n anchor: Math.min(canonical.selection.anchor, canonical.markdown.length),\n head: Math.min(canonical.selection.head, canonical.markdown.length),\n }\n : undefined;\n syncing = true;\n update.view.dispatch({\n changes: { from: 0, to: current.length, insert: canonical.markdown },\n ...(selection ? { selection } : {}),\n });\n syncing = false;\n }\n }),\n ];\n const controllerSelection = options.controller.getState().selection;\n const initialSelection = controllerSelection?.kind === \"text\"\n ? { anchor: Math.min(controllerSelection.anchor, options.controller.getState().markdown.length), head: Math.min(controllerSelection.head, options.controller.getState().markdown.length) }\n : undefined;\n const view = new EditorView({\n parent: options.target,\n state: CodeMirrorState.create({ doc: options.controller.getState().markdown, selection: initialSelection, extensions }),\n });\n const unsubscribe = options.controller.subscribe((change) => {\n if (change.source === \"source\") return;\n const current = view.state.doc.toString();\n const selection = change.current.selection?.kind === \"text\"\n ? { anchor: change.current.selection.anchor, head: change.current.selection.head }\n : undefined;\n const documentChanged = current !== change.current.markdown;\n if (!documentChanged && !change.selectionChanged) return;\n syncing = true;\n view.dispatch({\n ...(documentChanged ? { changes: { from: 0, to: current.length, insert: change.current.markdown } } : {}),\n ...(selection ? { selection } : {}),\n });\n syncing = false;\n });\n return {\n view,\n focus() { view.focus(); },\n setReadOnly(value) {\n readOnly = value;\n view.dispatch({\n effects: readOnlyConfig.reconfigure([\n CodeMirrorState.readOnly.of(readOnly),\n EditorView.editable.of(!readOnly),\n ]),\n });\n },\n destroy() { unsubscribe(); view.destroy(); },\n };\n}\n\nexport interface PreviewResult {\n readonly html: string;\n readonly diagnostics: ReturnType<typeof renderHtml>[\"diagnostics\"];\n}\n\nexport function createPreview(controller: EditorController, policy?: RenderPolicy): PreviewResult {\n const rendered = renderHtml(controller.getState().document, {\n ...policy,\n extensions: policy?.extensions ?? controller.extensions,\n });\n return { html: rendered.html, diagnostics: rendered.diagnostics };\n}\n\nexport interface ModeController {\n getMode(): EditorMode;\n setMode(mode: EditorMode): void;\n subscribe(listener: (mode: EditorMode) => void): () => void;\n destroy(): void;\n}\n\nexport function createModeController(initial: EditorMode = \"visual\"): ModeController {\n let mode = initial;\n let destroyed = false;\n const listeners = new Set<(mode: EditorMode) => void>();\n return {\n getMode() { if (destroyed) throw new Error(\"MDFN_MODE_CONTROLLER_DESTROYED\"); return mode; },\n setMode(next) { if (destroyed) throw new Error(\"MDFN_MODE_CONTROLLER_DESTROYED\"); if (next === mode) return; mode = next; listeners.forEach((listener) => listener(mode)); },\n subscribe(listener) { if (destroyed) throw new Error(\"MDFN_MODE_CONTROLLER_DESTROYED\"); listeners.add(listener); return () => listeners.delete(listener); },\n destroy() { destroyed = true; listeners.clear(); },\n };\n}\n\nexport const MDFN_SOURCE_VERSION = \"0.1.0\" as const;\n\nexport const sourceInternals = Object.freeze({ smallestSourceChange });\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mdfn/source",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Source editor, diagnostics, and source-visual synchronization for mdfn",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.cjs",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js", "require": "./dist/index.cjs" } },
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --sourcemap --clean",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
14
|
+
"lint": "tsc --noEmit -p tsconfig.json",
|
|
15
|
+
"prepack": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@codemirror/commands": "^6.10.4",
|
|
19
|
+
"@codemirror/lang-markdown": "^6.5.2",
|
|
20
|
+
"@codemirror/language": "^6.12.4",
|
|
21
|
+
"@codemirror/lint": "^6.9.7",
|
|
22
|
+
"@codemirror/search": "^6.7.1",
|
|
23
|
+
"@codemirror/state": "^6.7.1",
|
|
24
|
+
"@codemirror/view": "^6.43.8",
|
|
25
|
+
"@mdfn/core": "0.1.0",
|
|
26
|
+
"@mdfn/markdown": "0.1.0",
|
|
27
|
+
"@mdfn/render": "0.1.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": { "jsdom": "^26.0.0", "tsup": "^8.0.0", "typescript": "^5.3.0", "vitest": "3.2.4" },
|
|
30
|
+
"files": ["dist", "README.md"],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"engines": { "node": ">=20 <25" },
|
|
33
|
+
"repository": { "type": "git", "url": "git+https://github.com/21nCo/super-functions.git", "directory": "mdfn/source" },
|
|
34
|
+
"publishConfig": { "access": "public" },
|
|
35
|
+
"sideEffects": false,
|
|
36
|
+
"mdfn": { "status": "stable-candidate", "layer": "browser", "schemaVersion": 1, "kernel": "codemirror" }
|
|
37
|
+
}
|