draftly 1.0.0-alpha.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +12 -0
  2. package/dist/chunk-3T55CBNZ.cjs +33 -0
  3. package/dist/chunk-3T55CBNZ.cjs.map +1 -0
  4. package/dist/chunk-5MC4T7JH.cjs +58 -0
  5. package/dist/chunk-5MC4T7JH.cjs.map +1 -0
  6. package/dist/{chunk-72ZYRGRT.cjs → chunk-BWJLMREN.cjs} +11 -9
  7. package/dist/chunk-BWJLMREN.cjs.map +1 -0
  8. package/dist/{chunk-KBQDZ5IW.cjs → chunk-CLW73JRX.cjs} +100 -75
  9. package/dist/chunk-CLW73JRX.cjs.map +1 -0
  10. package/dist/{chunk-DFQYXFOP.js → chunk-EEHILRG5.js} +26 -3
  11. package/dist/chunk-EEHILRG5.js.map +1 -0
  12. package/dist/{chunk-HPSMS2WB.js → chunk-I563H35S.js} +101 -75
  13. package/dist/chunk-I563H35S.js.map +1 -0
  14. package/dist/chunk-IAXF4SJL.js +55 -0
  15. package/dist/chunk-IAXF4SJL.js.map +1 -0
  16. package/dist/chunk-JF3WXXMJ.js +31 -0
  17. package/dist/chunk-JF3WXXMJ.js.map +1 -0
  18. package/dist/{chunk-N3WL3XPB.js → chunk-L2XSK57Y.js} +1761 -478
  19. package/dist/chunk-L2XSK57Y.js.map +1 -0
  20. package/dist/{chunk-KDEDLC3D.cjs → chunk-TBVZEK2H.cjs} +27 -2
  21. package/dist/chunk-TBVZEK2H.cjs.map +1 -0
  22. package/dist/{chunk-2B3A3VSQ.cjs → chunk-W5ALMXG2.cjs} +1808 -504
  23. package/dist/chunk-W5ALMXG2.cjs.map +1 -0
  24. package/dist/{chunk-CG4M4TC7.js → chunk-ZUI3GI3W.js} +7 -5
  25. package/dist/chunk-ZUI3GI3W.js.map +1 -0
  26. package/dist/{draftly-BLnx3uGX.d.cts → draftly-BBL-AdOl.d.cts} +5 -1
  27. package/dist/{draftly-BLnx3uGX.d.ts → draftly-BBL-AdOl.d.ts} +5 -1
  28. package/dist/editor/index.cjs +22 -14
  29. package/dist/editor/index.d.cts +2 -1
  30. package/dist/editor/index.d.ts +2 -1
  31. package/dist/editor/index.js +2 -2
  32. package/dist/index.cjs +65 -39
  33. package/dist/index.d.cts +6 -3
  34. package/dist/index.d.ts +6 -3
  35. package/dist/index.js +6 -4
  36. package/dist/lib/index.cjs +12 -0
  37. package/dist/lib/index.cjs.map +1 -0
  38. package/dist/lib/index.d.cts +16 -0
  39. package/dist/lib/index.d.ts +16 -0
  40. package/dist/lib/index.js +3 -0
  41. package/dist/lib/index.js.map +1 -0
  42. package/dist/plugins/index.cjs +27 -17
  43. package/dist/plugins/index.d.cts +144 -9
  44. package/dist/plugins/index.d.ts +144 -9
  45. package/dist/plugins/index.js +5 -3
  46. package/dist/preview/index.cjs +16 -11
  47. package/dist/preview/index.d.cts +19 -4
  48. package/dist/preview/index.d.ts +19 -4
  49. package/dist/preview/index.js +3 -2
  50. package/package.json +8 -1
  51. package/src/editor/draftly.ts +1 -0
  52. package/src/editor/plugin.ts +5 -1
  53. package/src/editor/theme.ts +1 -0
  54. package/src/editor/utils.ts +31 -0
  55. package/src/index.ts +5 -4
  56. package/src/lib/index.ts +2 -0
  57. package/src/lib/input-handler.ts +45 -0
  58. package/src/plugins/code-plugin.theme.ts +426 -0
  59. package/src/plugins/code-plugin.ts +810 -561
  60. package/src/plugins/emoji-plugin.ts +140 -0
  61. package/src/plugins/index.ts +63 -57
  62. package/src/plugins/inline-plugin.ts +305 -291
  63. package/src/plugins/math-plugin.ts +12 -0
  64. package/src/plugins/table-plugin.ts +900 -0
  65. package/src/preview/context.ts +4 -1
  66. package/src/preview/css-generator.ts +14 -1
  67. package/src/preview/index.ts +9 -1
  68. package/src/preview/preview.ts +2 -1
  69. package/src/preview/renderer.ts +21 -20
  70. package/src/preview/syntax-theme.ts +110 -0
  71. package/src/preview/types.ts +14 -0
  72. package/dist/chunk-2B3A3VSQ.cjs.map +0 -1
  73. package/dist/chunk-72ZYRGRT.cjs.map +0 -1
  74. package/dist/chunk-CG4M4TC7.js.map +0 -1
  75. package/dist/chunk-DFQYXFOP.js.map +0 -1
  76. package/dist/chunk-HPSMS2WB.js.map +0 -1
  77. package/dist/chunk-KBQDZ5IW.cjs.map +0 -1
  78. package/dist/chunk-KDEDLC3D.cjs.map +0 -1
  79. package/dist/chunk-N3WL3XPB.js.map +0 -1
@@ -1,12 +1,89 @@
1
- import { parser, GFM, Subscript, Superscript, Emoji } from '@lezer/markdown';
1
+ import { classHighlighter } from '@lezer/highlight';
2
+ import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
3
+ import { languages } from '@codemirror/language-data';
2
4
  import DOMPurify from 'dompurify';
3
- import { foldNodeProp } from '@codemirror/language';
4
5
 
5
- // src/preview/renderer.ts
6
- function createPreviewContext(doc, theme, renderChildren, sanitizeHtml = true) {
6
+ // src/preview/default-renderers.ts
7
+ function escapeHtml(text) {
8
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
9
+ }
10
+ var renderDocument = (_node, children) => {
11
+ return children;
12
+ };
13
+ var defaultRenderers = {
14
+ // Document structure
15
+ Document: renderDocument
16
+ };
17
+ var MAX_WALK_DEPTH = 8;
18
+ function generateSyntaxThemeCSS(syntaxTheme, _wrapperClass) {
19
+ if (!syntaxTheme) return "";
20
+ const styles = extractRuntimeHighlightStyles(syntaxTheme);
21
+ if (!styles.length) return "";
22
+ const cssChunks = [];
23
+ for (const style of styles) {
24
+ const rules = style.module?.getRules();
25
+ if (!rules) continue;
26
+ cssChunks.push(rules);
27
+ }
28
+ if (!cssChunks.length) return "";
29
+ return Array.from(new Set(cssChunks)).join("\n");
30
+ }
31
+ function resolveSyntaxHighlighters(syntaxTheme, includeLegacyClassHighlighter = true) {
32
+ const resolved = [];
33
+ if (includeLegacyClassHighlighter) {
34
+ resolved.push(classHighlighter);
35
+ }
36
+ const styles = extractRuntimeHighlightStyles(syntaxTheme);
37
+ for (const style of styles) {
38
+ if (typeof style.style === "function") {
39
+ resolved.push(style);
40
+ }
41
+ }
42
+ return Array.from(new Set(resolved));
43
+ }
44
+ function extractRuntimeHighlightStyles(input) {
45
+ if (!input) return [];
46
+ const values = Array.isArray(input) ? input : [input];
47
+ const styles = [];
48
+ const visited = /* @__PURE__ */ new WeakSet();
49
+ for (const value of values) {
50
+ walk(value, 0, visited, styles);
51
+ }
52
+ return styles;
53
+ }
54
+ function walk(value, depth, visited, out) {
55
+ if (value === null || value === void 0) return;
56
+ if (depth > MAX_WALK_DEPTH) return;
57
+ if (isRuntimeHighlightStyle(value)) {
58
+ out.push(value);
59
+ }
60
+ if (Array.isArray(value)) {
61
+ for (const item of value) {
62
+ walk(item, depth + 1, visited, out);
63
+ }
64
+ return;
65
+ }
66
+ if (typeof value !== "object") return;
67
+ if (visited.has(value)) return;
68
+ visited.add(value);
69
+ const keys = Object.getOwnPropertyNames(value);
70
+ for (const key of keys) {
71
+ try {
72
+ walk(value[key], depth + 1, visited, out);
73
+ } catch {
74
+ }
75
+ }
76
+ }
77
+ function isRuntimeHighlightStyle(value) {
78
+ if (!value || typeof value !== "object") return false;
79
+ const style = value;
80
+ return Array.isArray(style.specs) && typeof style.style === "function";
81
+ }
82
+ function createPreviewContext(doc, theme, renderChildren, sanitizeHtml = true, syntaxHighlighters = []) {
7
83
  return {
8
84
  doc,
9
85
  theme,
86
+ syntaxHighlighters,
10
87
  sliceDoc(from, to) {
11
88
  return doc.slice(from, to);
12
89
  },
@@ -21,34 +98,27 @@ function createPreviewContext(doc, theme, renderChildren, sanitizeHtml = true) {
21
98
  };
22
99
  }
23
100
 
24
- // src/preview/default-renderers.ts
25
- function escapeHtml(text) {
26
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
27
- }
28
- var renderDocument = (_node, children) => {
29
- return children;
30
- };
31
- var defaultRenderers = {
32
- // Document structure
33
- Document: renderDocument
34
- };
101
+ // src/preview/renderer.ts
35
102
  var PreviewRenderer = class {
36
103
  doc;
37
104
  theme;
38
105
  plugins;
39
106
  markdown;
40
107
  sanitizeHtml;
108
+ syntaxTheme;
41
109
  renderers;
42
110
  ctx;
43
111
  nodeToPlugins;
44
- constructor(doc, plugins = [], markdown, theme = "auto" /* AUTO */, sanitize = true) {
112
+ constructor(doc, plugins = [], markdown2, theme = "auto" /* AUTO */, sanitize = true, syntaxTheme) {
45
113
  this.doc = doc;
46
114
  this.theme = theme;
47
115
  this.plugins = plugins;
48
- this.markdown = markdown;
116
+ this.markdown = markdown2;
49
117
  this.sanitizeHtml = sanitize;
118
+ this.syntaxTheme = syntaxTheme;
50
119
  this.renderers = { ...defaultRenderers };
51
- this.ctx = createPreviewContext(doc, theme, this.renderChildren.bind(this), sanitize);
120
+ const syntaxHighlighters = resolveSyntaxHighlighters(this.syntaxTheme, true);
121
+ this.ctx = createPreviewContext(doc, theme, this.renderChildren.bind(this), sanitize, syntaxHighlighters);
52
122
  this.nodeToPlugins = this.buildNodePluginMap();
53
123
  }
54
124
  /**
@@ -75,21 +145,16 @@ var PreviewRenderer = class {
75
145
  ...this.markdown,
76
146
  ...this.plugins.map((p) => p.getMarkdownConfig()).filter((ext) => ext !== null)
77
147
  ];
78
- const baseParser = parser.configure([
79
- GFM,
80
- Subscript,
81
- Superscript,
82
- Emoji,
83
- {
84
- props: [
85
- foldNodeProp.add({
86
- Table: (tree2, state) => ({ from: state.doc.lineAt(tree2.from).to, to: tree2.to })
87
- })
88
- ]
89
- }
90
- ]);
91
- const parser$1 = extensions.length > 0 ? baseParser.configure(extensions) : baseParser;
92
- const tree = parser$1.parse(this.doc);
148
+ const markdownSupport = markdown({
149
+ base: markdownLanguage,
150
+ codeLanguages: languages,
151
+ extensions,
152
+ addKeymap: true,
153
+ completeHTMLTags: true,
154
+ pasteURLAsLink: true
155
+ });
156
+ const parser = markdownSupport.language.parser;
157
+ const tree = parser.parse(this.doc);
93
158
  return await this.renderNode(tree.topNode);
94
159
  }
95
160
  /**
@@ -138,45 +203,6 @@ var PreviewRenderer = class {
138
203
  }
139
204
  };
140
205
 
141
- // src/preview/preview.ts
142
- async function preview(markdown, config = {}) {
143
- const {
144
- plugins = [],
145
- markdown: markdownConfig = [],
146
- wrapperClass = "draftly-preview",
147
- wrapperTag = "article",
148
- sanitize = true,
149
- theme = "auto" /* AUTO */
150
- } = config;
151
- const renderer = new PreviewRenderer(markdown, plugins, markdownConfig, theme, sanitize);
152
- const content = await renderer.render();
153
- const classAttr = wrapperClass ? ` class="${wrapperClass}"` : "";
154
- return `<${wrapperTag}${classAttr}>
155
- ${content}</${wrapperTag}>`;
156
- }
157
-
158
- // src/preview/css-generator.ts
159
- var baseStyles = `.draftly-preview {
160
- padding: 0 0.5rem;
161
- }`;
162
- function generateCSS(config = {}) {
163
- const { plugins = [], theme = "auto" /* AUTO */, wrapperClass = "draftly-preview", includeBase = true } = config;
164
- const cssChunks = [];
165
- if (includeBase) {
166
- if (wrapperClass !== "draftly-preview") {
167
- cssChunks.push(baseStyles.replace(/\.draftly-preview/g, `.${wrapperClass}`));
168
- } else {
169
- cssChunks.push(baseStyles);
170
- }
171
- }
172
- for (const plugin of plugins) {
173
- const pluginCSS = plugin.getPreviewStyles(theme, wrapperClass);
174
- if (pluginCSS) cssChunks.push(`/* ${plugin.name} - ${plugin.version} */
175
- ` + pluginCSS);
176
- }
177
- return cssChunks.join("\n\n");
178
- }
179
-
180
- export { PreviewRenderer, defaultRenderers, escapeHtml, generateCSS, preview };
181
- //# sourceMappingURL=chunk-HPSMS2WB.js.map
182
- //# sourceMappingURL=chunk-HPSMS2WB.js.map
206
+ export { PreviewRenderer, defaultRenderers, escapeHtml, generateSyntaxThemeCSS };
207
+ //# sourceMappingURL=chunk-I563H35S.js.map
208
+ //# sourceMappingURL=chunk-I563H35S.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/preview/default-renderers.ts","../src/preview/syntax-theme.ts","../src/preview/context.ts","../src/preview/renderer.ts"],"names":["markdown"],"mappings":"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","file":"chunk-I563H35S.js","sourcesContent":["import { NodeRenderer, NodeRendererMap } from \"./types\";\r\n\r\n/**\r\n * Escape HTML special characters\r\n */\r\nexport function escapeHtml(text: string): string {\r\n return text\r\n .replace(/&/g, \"&amp;\")\r\n .replace(/</g, \"&lt;\")\r\n .replace(/>/g, \"&gt;\")\r\n .replace(/\"/g, \"&quot;\")\r\n .replace(/'/g, \"&#39;\");\r\n}\r\n\r\n// ============================================\r\n// DEFAULT RENDERERS\r\n// ============================================\r\n\r\nconst renderDocument: NodeRenderer = (_node, children) => {\r\n return children;\r\n};\r\n\r\n/**\r\n * Default node renderers for all markdown node types\r\n */\r\nexport const defaultRenderers: NodeRendererMap = {\r\n // Document structure\r\n Document: renderDocument,\r\n};\r\n","import { classHighlighter, Highlighter } from \"@lezer/highlight\";\r\nimport type { SyntaxThemeInput } from \"./types\";\r\n\r\ntype HighlightSpec = {\r\n tag?: unknown;\r\n class?: string;\r\n [key: string]: unknown;\r\n};\r\n\r\ntype RuntimeHighlightStyle = {\r\n specs?: HighlightSpec[];\r\n style?: (tags: readonly import(\"@lezer/highlight\").Tag[]) => string | null;\r\n module?: { getRules(): string } | null;\r\n};\r\n\r\nconst MAX_WALK_DEPTH = 8;\r\n\r\n/**\r\n * Extract syntax highlight CSS from resolved CodeMirror HighlightStyle modules.\r\n */\r\nexport function generateSyntaxThemeCSS(\r\n syntaxTheme: SyntaxThemeInput | SyntaxThemeInput[] | undefined,\r\n _wrapperClass: string\r\n): string {\r\n if (!syntaxTheme) return \"\";\r\n\r\n const styles = extractRuntimeHighlightStyles(syntaxTheme);\r\n if (!styles.length) return \"\";\r\n\r\n const cssChunks: string[] = [];\r\n\r\n for (const style of styles) {\r\n const rules = style.module?.getRules();\r\n if (!rules) continue;\r\n cssChunks.push(rules);\r\n }\r\n\r\n if (!cssChunks.length) return \"\";\r\n\r\n return Array.from(new Set(cssChunks))\r\n .join(\"\\n\");\r\n}\r\n\r\nexport function resolveSyntaxHighlighters(\r\n syntaxTheme: SyntaxThemeInput | SyntaxThemeInput[] | undefined,\r\n includeLegacyClassHighlighter: boolean = true\r\n): readonly Highlighter[] {\r\n const resolved: Highlighter[] = [];\r\n if (includeLegacyClassHighlighter) {\r\n resolved.push(classHighlighter);\r\n }\r\n\r\n const styles = extractRuntimeHighlightStyles(syntaxTheme);\r\n for (const style of styles) {\r\n if (typeof style.style === \"function\") {\r\n resolved.push(style as unknown as Highlighter);\r\n }\r\n }\r\n\r\n return Array.from(new Set(resolved));\r\n}\r\n\r\nfunction extractRuntimeHighlightStyles(input: SyntaxThemeInput | SyntaxThemeInput[] | undefined): RuntimeHighlightStyle[] {\r\n if (!input) return [];\r\n\r\n const values = Array.isArray(input) ? input : [input];\r\n const styles: RuntimeHighlightStyle[] = [];\r\n const visited = new WeakSet<object>();\r\n\r\n for (const value of values) {\r\n walk(value, 0, visited, styles);\r\n }\r\n\r\n return styles;\r\n}\r\n\r\nfunction walk(value: unknown, depth: number, visited: WeakSet<object>, out: RuntimeHighlightStyle[]): void {\r\n if (value === null || value === undefined) return;\r\n if (depth > MAX_WALK_DEPTH) return;\r\n\r\n if (isRuntimeHighlightStyle(value)) {\r\n out.push(value);\r\n }\r\n\r\n if (Array.isArray(value)) {\r\n for (const item of value) {\r\n walk(item, depth + 1, visited, out);\r\n }\r\n return;\r\n }\r\n\r\n if (typeof value !== \"object\") return;\r\n if (visited.has(value)) return;\r\n visited.add(value);\r\n\r\n const keys = Object.getOwnPropertyNames(value);\r\n for (const key of keys) {\r\n try {\r\n walk((value as Record<string, unknown>)[key], depth + 1, visited, out);\r\n } catch {\r\n // Ignore inaccessible properties\r\n }\r\n }\r\n}\r\n\r\nfunction isRuntimeHighlightStyle(value: unknown): value is RuntimeHighlightStyle {\r\n if (!value || typeof value !== \"object\") return false;\r\n const style = value as RuntimeHighlightStyle;\r\n return Array.isArray(style.specs) && typeof style.style === \"function\";\r\n}\r\n","import { SyntaxNode } from \"@lezer/common\";\r\nimport { Highlighter } from \"@lezer/highlight\";\r\nimport { ThemeEnum } from \"../editor/utils\";\r\nimport { PreviewContext } from \"./types\";\r\nimport DOMPurify from \"dompurify\";\r\n\r\n/**\r\n * Creates a PreviewContext for rendering\r\n */\r\nexport function createPreviewContext(\r\n doc: string,\r\n theme: ThemeEnum,\r\n renderChildren: (node: SyntaxNode) => Promise<string>,\r\n sanitizeHtml: boolean = true,\r\n syntaxHighlighters: readonly Highlighter[] = []\r\n): PreviewContext {\r\n return {\r\n doc,\r\n theme,\r\n syntaxHighlighters,\r\n\r\n sliceDoc(from: number, to: number): string {\r\n return doc.slice(from, to);\r\n },\r\n\r\n sanitize(html: string): string {\r\n if (!sanitizeHtml) return html;\r\n\r\n // DOMPurify works in browser; in Node, it needs jsdom\r\n if (typeof window !== \"undefined\") {\r\n return DOMPurify.sanitize(html);\r\n }\r\n\r\n // Server-side: return as-is (user should sanitize at application level)\r\n // or use isomorphic-dompurify in their setup\r\n return html;\r\n },\r\n\r\n renderChildren,\r\n };\r\n}\r\n","import { SyntaxNode } from \"@lezer/common\";\r\nimport { markdown, markdownLanguage } from \"@codemirror/lang-markdown\";\r\nimport { MarkdownConfig } from \"@lezer/markdown\";\r\nimport { languages } from \"@codemirror/language-data\";\r\n\r\nimport { DraftlyPlugin } from \"../editor/plugin\";\r\nimport { ThemeEnum } from \"../editor/utils\";\r\nimport { createPreviewContext } from \"./context\";\r\nimport { defaultRenderers, escapeHtml } from \"./default-renderers\";\r\nimport { resolveSyntaxHighlighters } from \"./syntax-theme\";\r\nimport { NodeRendererMap, PreviewContext } from \"./types\";\r\n\r\n/**\r\n * Renderer class that walks the syntax tree and produces HTML\r\n */\r\nexport class PreviewRenderer {\r\n private doc: string;\r\n private theme: ThemeEnum;\r\n private plugins: DraftlyPlugin[];\r\n private markdown: MarkdownConfig[];\r\n private sanitizeHtml: boolean;\r\n private syntaxTheme: import(\"./types\").SyntaxThemeInput | import(\"./types\").SyntaxThemeInput[] | undefined;\r\n private renderers: NodeRendererMap;\r\n private ctx: PreviewContext;\r\n private nodeToPlugins: Map<string, DraftlyPlugin[]>;\r\n\r\n constructor(\r\n doc: string,\r\n plugins: DraftlyPlugin[] = [],\r\n markdown: MarkdownConfig[],\r\n theme: ThemeEnum = ThemeEnum.AUTO,\r\n sanitize: boolean = true,\r\n syntaxTheme?: import(\"./types\").SyntaxThemeInput | import(\"./types\").SyntaxThemeInput[]\r\n ) {\r\n this.doc = doc;\r\n this.theme = theme;\r\n this.plugins = plugins;\r\n this.markdown = markdown;\r\n this.sanitizeHtml = sanitize;\r\n this.syntaxTheme = syntaxTheme;\r\n this.renderers = { ...defaultRenderers };\r\n\r\n const syntaxHighlighters = resolveSyntaxHighlighters(this.syntaxTheme, true);\r\n\r\n // Create context with reference to renderChildren\r\n this.ctx = createPreviewContext(doc, theme, this.renderChildren.bind(this), sanitize, syntaxHighlighters);\r\n\r\n // Build node-to-plugin map for O(1) lookup\r\n this.nodeToPlugins = this.buildNodePluginMap();\r\n }\r\n\r\n /**\r\n * Build a map from node names to plugins that handle them\r\n */\r\n private buildNodePluginMap(): Map<string, DraftlyPlugin[]> {\r\n const map = new Map<string, DraftlyPlugin[]>();\r\n for (const plugin of this.plugins) {\r\n if (plugin.renderToHTML && plugin.requiredNodes.length > 0) {\r\n for (const nodeName of plugin.requiredNodes) {\r\n const list = map.get(nodeName) || [];\r\n list.push(plugin);\r\n map.set(nodeName, list);\r\n }\r\n }\r\n }\r\n return map;\r\n }\r\n\r\n /**\r\n * Render the document to HTML\r\n */\r\n async render(): Promise<string> {\r\n // Collect markdown extensions from plugins\r\n const extensions = [\r\n ...this.markdown,\r\n ...this.plugins.map((p) => p.getMarkdownConfig()).filter((ext): ext is NonNullable<typeof ext> => ext !== null),\r\n ];\r\n\r\n // Build parser through @codemirror/lang-markdown to match editor behavior exactly\r\n const markdownSupport = markdown({\r\n base: markdownLanguage,\r\n codeLanguages: languages,\r\n extensions,\r\n addKeymap: true,\r\n completeHTMLTags: true,\r\n pasteURLAsLink: true,\r\n });\r\n const parser = markdownSupport.language.parser;\r\n\r\n // Parse the document\r\n const tree = parser.parse(this.doc);\r\n\r\n // Render from root\r\n return await this.renderNode(tree.topNode);\r\n }\r\n\r\n /**\r\n * Render a single node to HTML\r\n */\r\n private async renderNode(node: SyntaxNode): Promise<string> {\r\n // Get plugins that handle this node type (O(1) lookup)\r\n const plugins = this.nodeToPlugins.get(node.name);\r\n if (plugins) {\r\n for (const plugin of plugins) {\r\n const children = await this.renderChildren(node);\r\n const result = await plugin.renderToHTML!(node, children, this.ctx);\r\n if (result !== null) {\r\n return result;\r\n }\r\n }\r\n }\r\n\r\n // Use default renderer\r\n const renderer = this.renderers[node.name];\r\n if (renderer) {\r\n const children = await this.renderChildren(node);\r\n return renderer(node, children, this.ctx);\r\n }\r\n\r\n // Unknown node - render children or text\r\n if (node.firstChild) {\r\n return await this.renderChildren(node);\r\n }\r\n\r\n // Leaf node - return text content\r\n return this.ctx.sliceDoc(node.from, node.to);\r\n }\r\n\r\n /**\r\n * Render all children of a node, including text between nodes\r\n */\r\n private async renderChildren(node: SyntaxNode): Promise<string> {\r\n let result = \"\";\r\n let pos = node.from; // Track position to find text gaps\r\n let child = node.firstChild;\r\n\r\n while (child) {\r\n // Add any text between the last position and this child\r\n if (child.from > pos) {\r\n result += escapeHtml(this.ctx.sliceDoc(pos, child.from));\r\n }\r\n\r\n // Render the child node\r\n result += await this.renderNode(child);\r\n\r\n // Update position to end of this child\r\n pos = child.to;\r\n child = child.nextSibling;\r\n }\r\n\r\n // Add any trailing text after the last child\r\n if (pos < node.to) {\r\n result += escapeHtml(this.ctx.sliceDoc(pos, node.to));\r\n }\r\n\r\n return result;\r\n }\r\n}\r\n"]}
@@ -0,0 +1,55 @@
1
+ import { PreviewRenderer, generateSyntaxThemeCSS } from './chunk-I563H35S.js';
2
+
3
+ // src/preview/preview.ts
4
+ async function preview(markdown, config = {}) {
5
+ const {
6
+ plugins = [],
7
+ markdown: markdownConfig = [],
8
+ wrapperClass = "draftly-preview",
9
+ wrapperTag = "article",
10
+ sanitize = true,
11
+ theme = "auto" /* AUTO */,
12
+ syntaxTheme
13
+ } = config;
14
+ const renderer = new PreviewRenderer(markdown, plugins, markdownConfig, theme, sanitize, syntaxTheme);
15
+ const content = await renderer.render();
16
+ const classAttr = wrapperClass ? ` class="${wrapperClass}"` : "";
17
+ return `<${wrapperTag}${classAttr}>
18
+ ${content}</${wrapperTag}>`;
19
+ }
20
+
21
+ // src/preview/css-generator.ts
22
+ var baseStyles = `.draftly-preview {
23
+ padding: 0 0.5rem;
24
+ }`;
25
+ function generateCSS(config = {}) {
26
+ const {
27
+ plugins = [],
28
+ theme = "auto" /* AUTO */,
29
+ wrapperClass = "draftly-preview",
30
+ includeBase = true,
31
+ syntaxTheme
32
+ } = config;
33
+ const cssChunks = [];
34
+ if (includeBase) {
35
+ if (wrapperClass !== "draftly-preview") {
36
+ cssChunks.push(baseStyles.replace(/\.draftly-preview/g, `.${wrapperClass}`));
37
+ } else {
38
+ cssChunks.push(baseStyles);
39
+ }
40
+ }
41
+ const syntaxCSS = generateSyntaxThemeCSS(syntaxTheme, wrapperClass);
42
+ if (syntaxCSS) {
43
+ cssChunks.push("/* syntax-theme */\n" + syntaxCSS);
44
+ }
45
+ for (const plugin of plugins) {
46
+ const pluginCSS = plugin.getPreviewStyles(theme, wrapperClass);
47
+ if (pluginCSS) cssChunks.push(`/* ${plugin.name} - ${plugin.version} */
48
+ ` + pluginCSS);
49
+ }
50
+ return cssChunks.join("\n\n");
51
+ }
52
+
53
+ export { generateCSS, preview };
54
+ //# sourceMappingURL=chunk-IAXF4SJL.js.map
55
+ //# sourceMappingURL=chunk-IAXF4SJL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/preview/preview.ts","../src/preview/css-generator.ts"],"names":[],"mappings":";;;AAsBA,eAAsB,OAAA,CAAQ,QAAA,EAAkB,MAAA,GAAwB,EAAC,EAAoB;AAC3F,EAAA,MAAM;AAAA,IACJ,UAAU,EAAC;AAAA,IACX,QAAA,EAAU,iBAAiB,EAAC;AAAA,IAC5B,YAAA,GAAe,iBAAA;AAAA,IACf,UAAA,GAAa,SAAA;AAAA,IACb,QAAA,GAAW,IAAA;AAAA,IACX,KAAA,GAAA,MAAA;AAAA,IACA;AAAA,GACF,GAAI,MAAA;AAGJ,EAAA,MAAM,QAAA,GAAW,IAAI,eAAA,CAAgB,QAAA,EAAU,SAAS,cAAA,EAAgB,KAAA,EAAO,UAAU,WAAW,CAAA;AACpG,EAAA,MAAM,OAAA,GAAU,MAAM,QAAA,CAAS,MAAA,EAAO;AAGtC,EAAA,MAAM,SAAA,GAAY,YAAA,GAAe,CAAA,QAAA,EAAW,YAAY,CAAA,CAAA,CAAA,GAAM,EAAA;AAC9D,EAAA,OAAO,CAAA,CAAA,EAAI,UAAU,CAAA,EAAG,SAAS,CAAA;AAAA,EAAM,OAAO,KAAK,UAAU,CAAA,CAAA,CAAA;AAC/D;;;ACjCA,IAAM,UAAA,GAAa,CAAA;AAAA;AAAA,CAAA,CAAA;AAsBZ,SAAS,WAAA,CAAY,MAAA,GAA4B,EAAC,EAAW;AAClE,EAAA,MAAM;AAAA,IACJ,UAAU,EAAC;AAAA,IACX,KAAA,GAAA,MAAA;AAAA,IACA,YAAA,GAAe,iBAAA;AAAA,IACf,WAAA,GAAc,IAAA;AAAA,IACd;AAAA,GACF,GAAI,MAAA;AAEJ,EAAA,MAAM,YAAsB,EAAC;AAG7B,EAAA,IAAI,WAAA,EAAa;AAEf,IAAA,IAAI,iBAAiB,iBAAA,EAAmB;AACtC,MAAA,SAAA,CAAU,KAAK,UAAA,CAAW,OAAA,CAAQ,sBAAsB,CAAA,CAAA,EAAI,YAAY,EAAE,CAAC,CAAA;AAAA,IAC7E,CAAA,MAAO;AACL,MAAA,SAAA,CAAU,KAAK,UAAU,CAAA;AAAA,IAC3B;AAAA,EACF;AAGA,EAAA,MAAM,SAAA,GAAY,sBAAA,CAAuB,WAAA,EAAa,YAAY,CAAA;AAClE,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,SAAA,CAAU,IAAA,CAAK,yBAAyB,SAAS,CAAA;AAAA,EACnD;AAGA,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,gBAAA,CAAiB,KAAA,EAAO,YAAY,CAAA;AAC7D,IAAA,IAAI,SAAA,YAAqB,IAAA,CAAK,CAAA,GAAA,EAAM,OAAO,IAAI,CAAA,GAAA,EAAM,OAAO,OAAO,CAAA;AAAA,CAAA,GAAU,SAAS,CAAA;AAAA,EACxF;AAEA,EAAA,OAAO,SAAA,CAAU,KAAK,MAAM,CAAA;AAC9B","file":"chunk-IAXF4SJL.js","sourcesContent":["import { ThemeEnum } from \"../editor/utils\";\r\nimport { PreviewRenderer } from \"./renderer\";\r\nimport { PreviewConfig } from \"./types\";\r\n\r\n/**\r\n * Render markdown to semantic HTML\r\n *\r\n * @param markdown - Markdown string to render\r\n * @param config - Preview configuration\r\n * @returns HTML string\r\n *\r\n * @example\r\n * ```ts\r\n * import { preview } from 'draftly/preview';\r\n * import { HeadingPlugin, ListPlugin } from 'draftly/plugins';\r\n *\r\n * const html = preview('# Hello World', {\r\n * plugins: [new HeadingPlugin(), new ListPlugin()],\r\n * wrapperClass: 'draftly-preview',\r\n * });\r\n * ```\r\n */\r\nexport async function preview(markdown: string, config: PreviewConfig = {}): Promise<string> {\r\n const {\r\n plugins = [],\r\n markdown: markdownConfig = [],\r\n wrapperClass = \"draftly-preview\",\r\n wrapperTag = \"article\",\r\n sanitize = true,\r\n theme = ThemeEnum.AUTO,\r\n syntaxTheme,\r\n } = config;\r\n\r\n // Create renderer and generate HTML\r\n const renderer = new PreviewRenderer(markdown, plugins, markdownConfig, theme, sanitize, syntaxTheme);\r\n const content = await renderer.render();\r\n\r\n // Wrap in container\r\n const classAttr = wrapperClass ? ` class=\"${wrapperClass}\"` : \"\";\r\n return `<${wrapperTag}${classAttr}>\\n${content}</${wrapperTag}>`;\r\n}\r\n","import { ThemeEnum } from \"../editor/utils\";\r\nimport { GenerateCSSConfig } from \"./types\";\r\nimport { generateSyntaxThemeCSS } from \"./syntax-theme\";\r\n\r\n/**\r\n * Base CSS styles for preview rendering\r\n */\r\nconst baseStyles = `.draftly-preview {\r\n padding: 0 0.5rem;\r\n}`;\r\n\r\n/**\r\n * Generate CSS for preview rendering\r\n *\r\n * @param config - CSS generation configuration\r\n * @returns CSS string\r\n *\r\n * @example\r\n * ```ts\r\n * import { generateCSS } from 'draftly/preview';\r\n * import { HeadingPlugin, ListPlugin } from 'draftly/plugins';\r\n *\r\n * const css = generateCSS({\r\n * plugins: [new HeadingPlugin(), new ListPlugin()],\r\n * theme: ThemeEnum.AUTO,\r\n * includeBase: true,\r\n * });\r\n * ```\r\n */\r\nexport function generateCSS(config: GenerateCSSConfig = {}): string {\r\n const {\r\n plugins = [],\r\n theme = ThemeEnum.AUTO,\r\n wrapperClass = \"draftly-preview\",\r\n includeBase = true,\r\n syntaxTheme,\r\n } = config;\r\n\r\n const cssChunks: string[] = [];\r\n\r\n // Include base styles\r\n if (includeBase) {\r\n // Replace default wrapper class if custom one is provided\r\n if (wrapperClass !== \"draftly-preview\") {\r\n cssChunks.push(baseStyles.replace(/\\.draftly-preview/g, `.${wrapperClass}`));\r\n } else {\r\n cssChunks.push(baseStyles);\r\n }\r\n }\r\n\r\n // Collect syntax highlight styles (`tok-*` classes) from CodeMirror theme/extensions\r\n const syntaxCSS = generateSyntaxThemeCSS(syntaxTheme, wrapperClass);\r\n if (syntaxCSS) {\r\n cssChunks.push(\"/* syntax-theme */\\n\" + syntaxCSS);\r\n }\r\n\r\n // Collect styles from plugins\r\n for (const plugin of plugins) {\r\n const pluginCSS = plugin.getPreviewStyles(theme, wrapperClass);\r\n if (pluginCSS) cssChunks.push(`/* ${plugin.name} - ${plugin.version} */\\n` + pluginCSS);\r\n }\r\n\r\n return cssChunks.join(\"\\n\\n\");\r\n}\r\n"]}
@@ -0,0 +1,31 @@
1
+ import { EditorSelection } from '@codemirror/state';
2
+ import { EditorView } from '@codemirror/view';
3
+
4
+ // src/lib/input-handler.ts
5
+ function createWrapSelectionInputHandler(markersByInput) {
6
+ return EditorView.inputHandler.of((view, _from, _to, text) => {
7
+ const marker = markersByInput[text];
8
+ if (!marker) {
9
+ return false;
10
+ }
11
+ const ranges = view.state.selection.ranges;
12
+ if (ranges.length === 0 || ranges.some((range) => range.empty)) {
13
+ return false;
14
+ }
15
+ const changes = ranges.map((range) => ({
16
+ from: range.from,
17
+ to: range.to,
18
+ insert: `${marker}${view.state.sliceDoc(range.from, range.to)}${marker}`
19
+ })).reverse();
20
+ const nextRanges = ranges.map((range) => EditorSelection.range(range.from + marker.length, range.to + marker.length));
21
+ view.dispatch({
22
+ changes,
23
+ selection: EditorSelection.create(nextRanges, view.state.selection.mainIndex)
24
+ });
25
+ return true;
26
+ });
27
+ }
28
+
29
+ export { createWrapSelectionInputHandler };
30
+ //# sourceMappingURL=chunk-JF3WXXMJ.js.map
31
+ //# sourceMappingURL=chunk-JF3WXXMJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/input-handler.ts"],"names":[],"mappings":";;;;AAeO,SAAS,gCAAgC,cAAA,EAAmD;AACjG,EAAA,OAAO,WAAW,YAAA,CAAa,EAAA,CAAG,CAAC,IAAA,EAAM,KAAA,EAAO,KAAK,IAAA,KAAS;AAC5D,IAAA,MAAM,MAAA,GAAS,eAAe,IAAI,CAAA;AAClC,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,SAAA,CAAU,MAAA;AACpC,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,IAAK,MAAA,CAAO,KAAK,CAAC,KAAA,KAAU,KAAA,CAAM,KAAK,CAAA,EAAG;AAC9D,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,MAAM,OAAA,GAAU,MAAA,CACb,GAAA,CAAI,CAAC,KAAA,MAAW;AAAA,MACf,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,IAAI,KAAA,CAAM,EAAA;AAAA,MACV,MAAA,EAAQ,CAAA,EAAG,MAAM,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,QAAA,CAAS,KAAA,CAAM,IAAA,EAAM,KAAA,CAAM,EAAE,CAAC,GAAG,MAAM,CAAA;AAAA,KACxE,CAAE,EACD,OAAA,EAAQ;AAEX,IAAA,MAAM,UAAA,GAAa,MAAA,CAAO,GAAA,CAAI,CAAC,UAAU,eAAA,CAAgB,KAAA,CAAM,KAAA,CAAM,IAAA,GAAO,OAAO,MAAA,EAAQ,KAAA,CAAM,EAAA,GAAK,MAAA,CAAO,MAAM,CAAC,CAAA;AAEpH,IAAA,IAAA,CAAK,QAAA,CAAS;AAAA,MACZ,OAAA;AAAA,MACA,WAAW,eAAA,CAAgB,MAAA,CAAO,YAAY,IAAA,CAAK,KAAA,CAAM,UAAU,SAAS;AAAA,KAC7E,CAAA;AAED,IAAA,OAAO,IAAA;AAAA,EACT,CAAC,CAAA;AACH","file":"chunk-JF3WXXMJ.js","sourcesContent":["import { EditorSelection, Extension } from \"@codemirror/state\";\r\nimport { EditorView } from \"@codemirror/view\";\r\n\r\n/**\r\n * Mapping of typed input characters to surrounding markers.\r\n *\r\n * Example:\r\n * { \"*\": \"*\", \"=\": \"==\" }\r\n */\r\nexport type WrapSelectionMarkerMap = Record<string, string>;\r\n\r\n/**\r\n * Creates an input handler that wraps non-empty selections with markdown markers\r\n * when a mapped character is typed.\r\n */\r\nexport function createWrapSelectionInputHandler(markersByInput: WrapSelectionMarkerMap): Extension {\r\n return EditorView.inputHandler.of((view, _from, _to, text) => {\r\n const marker = markersByInput[text];\r\n if (!marker) {\r\n return false;\r\n }\r\n\r\n const ranges = view.state.selection.ranges;\r\n if (ranges.length === 0 || ranges.some((range) => range.empty)) {\r\n return false;\r\n }\r\n\r\n const changes = ranges\r\n .map((range) => ({\r\n from: range.from,\r\n to: range.to,\r\n insert: `${marker}${view.state.sliceDoc(range.from, range.to)}${marker}`,\r\n }))\r\n .reverse();\r\n\r\n const nextRanges = ranges.map((range) => EditorSelection.range(range.from + marker.length, range.to + marker.length));\r\n\r\n view.dispatch({\r\n changes,\r\n selection: EditorSelection.create(nextRanges, view.state.selection.mainIndex),\r\n });\r\n\r\n return true;\r\n });\r\n}\r\n"]}