autumnnote 1.9.1 → 1.11.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/autumnnote.css +40 -0
- package/dist/autumnnote.es.js +382 -52
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +382 -52
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/js/core/markdown.js +361 -55
- package/src/js/index.js +1 -1
- package/src/js/module/Clipboard.js +69 -11
- package/src/styles/autumnnote.scss +23 -0
- package/types/index.d.ts +1 -1
|
@@ -194,6 +194,20 @@ export class Clipboard {
|
|
|
194
194
|
return doc.body.innerHTML;
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Checks whether an HTML payload has no semantic markup beyond plain
|
|
199
|
+
* wrapper elements (e.g. a bare <div>/<p>). Used to decide whether a
|
|
200
|
+
* markdown-shaped plain-text paste should win over an accompanying HTML
|
|
201
|
+
* payload that isn't actually carrying any real rich-text formatting.
|
|
202
|
+
* @param {string} html
|
|
203
|
+
* @returns {boolean}
|
|
204
|
+
*/
|
|
205
|
+
_isTriviallyPlainHtml(html) {
|
|
206
|
+
const doc = new DOMParser().parseFromString(`<body>${html}</body>`, 'text/html');
|
|
207
|
+
const SIGNIFICANT = 'a,img,table,ul,ol,li,blockquote,pre,code,h1,h2,h3,h4,h5,h6,strong,b,em,i,u,s,del,strike,hr,br';
|
|
208
|
+
return !doc.body.querySelector(SIGNIFICANT);
|
|
209
|
+
}
|
|
210
|
+
|
|
197
211
|
/**
|
|
198
212
|
* Forces the next paste operation to strip all HTML formatting.
|
|
199
213
|
* Called by Editor when Ctrl+Shift+V is pressed.
|
|
@@ -219,6 +233,9 @@ export class Clipboard {
|
|
|
219
233
|
const size = Math.max(text.length, html.length);
|
|
220
234
|
if (size > maxBytes) {
|
|
221
235
|
event.preventDefault();
|
|
236
|
+
const message = `Pasted content (${size} bytes) exceeds the ${this.options.maxPasteSize ?? 5} MB paste size limit.`;
|
|
237
|
+
this.context.triggerEvent('pasteError', { size, maxBytes, message });
|
|
238
|
+
console.warn(`[AutumnNote] ${message}`);
|
|
222
239
|
return;
|
|
223
240
|
}
|
|
224
241
|
}
|
|
@@ -257,13 +274,20 @@ export class Clipboard {
|
|
|
257
274
|
return;
|
|
258
275
|
}
|
|
259
276
|
|
|
260
|
-
// 3. Markdown paste —
|
|
261
|
-
|
|
277
|
+
// 3. Markdown paste — when there's no HTML on the clipboard, or the
|
|
278
|
+
// accompanying HTML has no semantic markup (e.g. some terminal/clipboard
|
|
279
|
+
// tools put both a markdown-shaped text/plain and a trivial <div>-wrapped
|
|
280
|
+
// text/html on the clipboard). Real rich-text sources (Word, Docs, etc.)
|
|
281
|
+
// always have semantic tags after cleaning, so this is unaffected.
|
|
282
|
+
if (this.options.markdownPaste !== false) {
|
|
283
|
+
const hasHtml = clipboardData.types.includes('text/html');
|
|
284
|
+
const html = hasHtml ? clipboardData.getData('text/html') : '';
|
|
285
|
+
const htmlTriviallyPlain = !hasHtml || this._isTriviallyPlainHtml(html);
|
|
262
286
|
const text = clipboardData.getData('text/plain');
|
|
263
|
-
if (text && isMarkdown(text)) {
|
|
287
|
+
if (text && htmlTriviallyPlain && isMarkdown(text)) {
|
|
264
288
|
event.preventDefault();
|
|
265
|
-
const
|
|
266
|
-
execCommand('insertHTML',
|
|
289
|
+
const converted = sanitiseHTML(markdownToHTML(text));
|
|
290
|
+
execCommand('insertHTML', converted);
|
|
267
291
|
this.context.invoke('editor.afterCommand');
|
|
268
292
|
return;
|
|
269
293
|
}
|
|
@@ -307,14 +331,48 @@ export class Clipboard {
|
|
|
307
331
|
if (!dt?.files?.length) return;
|
|
308
332
|
|
|
309
333
|
const imageFiles = Array.from(dt.files).filter((f) => f.type.startsWith('image/'));
|
|
310
|
-
if (imageFiles.length
|
|
334
|
+
if (imageFiles.length > 0) {
|
|
335
|
+
event.preventDefault();
|
|
336
|
+
event.stopPropagation();
|
|
337
|
+
// Place the caret at the drop coordinates before inserting
|
|
338
|
+
this._placeCaretAtPoint(event.clientX, event.clientY);
|
|
339
|
+
this._insertImageFiles(imageFiles);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
311
342
|
|
|
312
|
-
|
|
313
|
-
|
|
343
|
+
if (this.options.markdownPaste !== false) {
|
|
344
|
+
const mdFile = Array.from(dt.files).find((f) => /\.md$/i.test(f.name) || f.type === 'text/markdown');
|
|
345
|
+
if (mdFile) {
|
|
346
|
+
event.preventDefault();
|
|
347
|
+
event.stopPropagation();
|
|
348
|
+
this._placeCaretAtPoint(event.clientX, event.clientY);
|
|
349
|
+
this._insertMarkdownFile(mdFile);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
314
353
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
354
|
+
/**
|
|
355
|
+
* Reads a dropped `.md` File and inserts it converted to HTML at the
|
|
356
|
+
* current caret. Skips the isMarkdown() heuristic — an explicit `.md`
|
|
357
|
+
* extension/MIME type is an unambiguous signal, unlike pasted plain text.
|
|
358
|
+
* @param {File} file
|
|
359
|
+
*/
|
|
360
|
+
_insertMarkdownFile(file) {
|
|
361
|
+
const maxBytes = (this.options.maxPasteSize ?? 5) * 1024 * 1024;
|
|
362
|
+
if (maxBytes > 0 && file.size > maxBytes) {
|
|
363
|
+
const message = `Dropped file "${file.name}" (${file.size} bytes) exceeds the ${this.options.maxPasteSize ?? 5} MB paste size limit.`;
|
|
364
|
+
this.context.triggerEvent('pasteError', { size: file.size, maxBytes, message });
|
|
365
|
+
console.warn(`[AutumnNote] ${message}`);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const reader = new FileReader();
|
|
369
|
+
reader.onload = (e) => {
|
|
370
|
+
const html = sanitiseHTML(markdownToHTML(/** @type {string} */ (e.target.result) || ''));
|
|
371
|
+
execCommand('insertHTML', html);
|
|
372
|
+
this.context.invoke('editor.afterCommand');
|
|
373
|
+
};
|
|
374
|
+
reader.onerror = () => console.warn('[AutumnNote] Failed to read dropped markdown file', file.name);
|
|
375
|
+
reader.readAsText(file);
|
|
318
376
|
}
|
|
319
377
|
|
|
320
378
|
// ---------------------------------------------------------------------------
|
|
@@ -459,8 +459,24 @@
|
|
|
459
459
|
// Content styles
|
|
460
460
|
p { margin: 0 0 0.75em; }
|
|
461
461
|
h1, h2, h3, h4, h5, h6 { margin: 0.5em 0; line-height: 1.3; }
|
|
462
|
+
h1 { font-size: 2em; }
|
|
463
|
+
h2 { font-size: 1.5em; }
|
|
464
|
+
h3 { font-size: 1.25em; }
|
|
465
|
+
h4 { font-size: 1.1em; }
|
|
466
|
+
h5 { font-size: 1em; }
|
|
467
|
+
h6 { font-size: 0.875em; }
|
|
462
468
|
ul, ol { margin: 0 0 0.75em 0; padding-left: 1.5em; list-style-position: inside; }
|
|
463
469
|
li { margin-bottom: 0.25em; }
|
|
470
|
+
|
|
471
|
+
// Loose lists (markdown paste wraps item text in <p>): keep the list marker
|
|
472
|
+
// on the same line as the first paragraph (list-style-position: inside puts
|
|
473
|
+
// it in the inline flow), and use tighter margins than standalone <p>.
|
|
474
|
+
li > p {
|
|
475
|
+
margin: 0 0 0.5em;
|
|
476
|
+
&:first-child { display: inline; }
|
|
477
|
+
&:last-child { margin-bottom: 0; }
|
|
478
|
+
}
|
|
479
|
+
li:has(> p) { margin-bottom: 0.6em; }
|
|
464
480
|
blockquote {
|
|
465
481
|
margin: 0.5em 0 0.5em 1em;
|
|
466
482
|
padding: 0.5em 1em;
|
|
@@ -475,6 +491,9 @@
|
|
|
475
491
|
}
|
|
476
492
|
pre { padding: 0.75em 1em; overflow-x: auto; }
|
|
477
493
|
code { padding: 0.1em 0.3em; }
|
|
494
|
+
// Inside <pre> the wrapper already paints the background — reset the inner
|
|
495
|
+
// <code> so a plain (non-Prism) code fence isn't double-tinted.
|
|
496
|
+
pre > code { background: transparent; padding: 0; }
|
|
478
497
|
|
|
479
498
|
// Prism-highlighted blocks — <pre> gets class="language-*" mirrored from <code>
|
|
480
499
|
// Set dark background here as fallback for before Prism CSS loads from CDN.
|
|
@@ -1981,6 +2000,8 @@ $an-video-accent: #8b5cf6; // violet-500
|
|
|
1981
2000
|
color: #cdd6f4;
|
|
1982
2001
|
}
|
|
1983
2002
|
|
|
2003
|
+
pre:not([class*='language-']) > code { background: transparent; }
|
|
2004
|
+
|
|
1984
2005
|
.an-code-line-numbers::before {
|
|
1985
2006
|
background: rgba(255, 255, 255, 0.05);
|
|
1986
2007
|
border-right-color: rgba(255, 255, 255, 0.1);
|
|
@@ -2447,6 +2468,8 @@ $an-video-accent: #8b5cf6; // violet-500
|
|
|
2447
2468
|
color: #cdd6f4;
|
|
2448
2469
|
}
|
|
2449
2470
|
|
|
2471
|
+
pre:not([class*='language-']) > code { background: transparent; }
|
|
2472
|
+
|
|
2450
2473
|
hr { border-color: #3f3f5f; }
|
|
2451
2474
|
|
|
2452
2475
|
table, .an-table {
|