autumnnote 1.0.3 → 1.0.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autumnnote",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "A modern, lightweight WYSIWYG editor — built with vanilla JavaScript, no jQuery required.",
5
5
  "main": "dist/autumnnote.umd.js",
6
6
  "module": "dist/autumnnote.es.js",
package/src/js/Context.js CHANGED
@@ -32,6 +32,7 @@ import { IconDialog } from './module/IconDialog.js';
32
32
  import { ContextMenu } from './module/ContextMenu.js';
33
33
  import { ShortcutsDialog } from './module/ShortcutsDialog.js';
34
34
  import { FindReplace } from './module/FindReplace.js';
35
+ import { ImageCropOverlay } from './module/ImageCropOverlay.js';
35
36
 
36
37
  /** Module registry shared across all Context instances (populated via AutumnNote.registerModule). */
37
38
  export const _customModules = new Map();
@@ -107,8 +108,8 @@ export class Context {
107
108
  _registerModules() {
108
109
  const register = (name, ModuleClass) => {
109
110
  const instance = new ModuleClass(this);
110
- instance.initialize();
111
111
  this._modules.set(name, instance);
112
+ instance.initialize();
112
113
  };
113
114
 
114
115
  register('editor', Editor);
@@ -133,6 +134,7 @@ export class Context {
133
134
  register('iconDialog', IconDialog);
134
135
  register('shortcutsDialog', ShortcutsDialog);
135
136
  register('findReplace', FindReplace);
137
+ register('imageCropOverlay', ImageCropOverlay);
136
138
 
137
139
  // Custom modules registered via AutumnNote.registerModule()
138
140
  for (const [name, ModuleClass] of _customModules) {
@@ -155,6 +157,9 @@ export class Context {
155
157
  }
156
158
 
157
159
  _bindEditorEvents(editable) {
160
+ // Keep the original textarea/input value in sync immediately on every input.
161
+ // This guarantees form.submit() sees fresh data even before debounced change.
162
+ const d0 = on(editable, 'input', () => this._syncToTarget());
158
163
  const d1 = on(editable, 'focus', () => {
159
164
  this.layoutInfo.container.classList.add('an-focused');
160
165
  if (typeof this.options.onFocus === 'function') {
@@ -170,7 +175,7 @@ export class Context {
170
175
  });
171
176
  // Sync textarea/input value on every change so form.submit() always gets fresh content
172
177
  const d3 = this.on('change', () => this._syncToTarget());
173
- this._disposers.push(d1, d2, d3);
178
+ this._disposers.push(d0, d1, d2, d3);
174
179
 
175
180
  // Auto-save to localStorage on every change
176
181
  if (this.options.autoSave && this.options.autoSaveKey) {
@@ -313,8 +313,8 @@ export function isInlineCode() {
313
313
 
314
314
  /**
315
315
  * Toggles a task-list at the cursor.
316
- * If inside a checklist <li>, converts it back to a <p>.
317
- * Otherwise inserts a new <ul class="an-checklist"> with one item.
316
+ * If inside a checklist <li>, converts it (and any other selected items) back to <p> elements.
317
+ * Otherwise inserts a new <ul class="an-checklist"> with one item per selected line.
318
318
  */
319
319
  export function toggleChecklist() {
320
320
  const sel = window.getSelection();
@@ -322,59 +322,56 @@ export function toggleChecklist() {
322
322
  const range = sel.getRangeAt(0);
323
323
  let container = range.commonAncestorContainer;
324
324
  if (container.nodeType === 3) container = container.parentElement;
325
- const li = container && container.closest ? container.closest('.an-checklist li') : null;
326
- if (li) {
327
- // Exit checklist — convert item to a <p>
328
- const ul = li.closest('.an-checklist');
329
- const text = Array.from(li.childNodes)
330
- .filter((n) => !(n.nodeType === 1 && n.tagName === 'INPUT'))
331
- .map((n) => n.textContent).join('').replace(/\u00a0/g, ' ').trim();
332
- const p = document.createElement('p');
333
- p.textContent = text || '\u00a0';
334
- ul.parentNode.insertBefore(p, ul.nextSibling);
335
- ul.removeChild(li);
336
- if (ul.children.length === 0) ul.remove();
337
- const nr = document.createRange();
338
- // Point into the text node (or first child) rather than the element node
339
- // so the cursor is at a well-defined text position, not element-offset 0.
340
- const startNode = p.firstChild || p;
341
- nr.setStart(startNode, 0);
342
- nr.collapse(true);
343
- sel.removeAllRanges();
344
- sel.addRange(nr);
345
- } else {
346
- // Use a temporary marker attribute so the new <li> can be found reliably
347
- // even when execCommand('insertHTML') places the cursor outside the list.
348
- // Chrome often moves the caret to a generated <p> after the <ul> rather
349
- // than staying inside the <li>, making a selection-based lookup fail.
350
- const MARKER = 'data-an-new-cli';
351
- execCommand('insertHTML',
352
- `<ul class="an-checklist"><li ${MARKER}><input type="checkbox" contenteditable="false"></li></ul>`);
353
- const newLi = document.querySelector(`[${MARKER}]`);
354
- if (newLi) {
355
- newLi.removeAttribute(MARKER);
356
- const cbEl = newLi.querySelector('input[type="checkbox"]');
357
- if (cbEl) {
358
- // Use \u200B (zero-width space) instead of an empty string.
359
- // Chrome does not reliably honour a Selection pointing into an empty
360
- // text node and may silently normalise it to an element-level offset,
361
- // rendering the caret before the absolutely-positioned checkbox.
362
- // \u200B is invisible/zero-width and is stripped by getHTML().
363
- let textNode = cbEl.nextSibling;
364
- if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {
365
- textNode = document.createTextNode('\u200B');
366
- newLi.appendChild(textNode);
367
- } else if (!textNode.textContent) {
368
- textNode.textContent = '\u200B';
369
- }
325
+
326
+ const ul = container.closest && container.closest('.an-checklist');
327
+ if (ul) {
328
+ // If selection covers multiple <li>, convert them all
329
+ const selectedLis = Array.from(ul.querySelectorAll('li')).filter((li) =>
330
+ sel.containsNode(li, true),
331
+ );
332
+ if (selectedLis.length > 0) {
333
+ let firstP = null;
334
+ selectedLis.forEach((li) => {
335
+ const text = Array.from(li.childNodes)
336
+ .filter((n) => !(n.nodeType === 1 && n.tagName === 'INPUT'))
337
+ .map((n) => n.textContent)
338
+ .join('')
339
+ .replace(/\u00a0/g, ' ')
340
+ .trim();
341
+ const p = document.createElement('p');
342
+ p.textContent = text || '\u00a0';
343
+ ul.parentNode.insertBefore(p, ul);
344
+ if (!firstP) firstP = p;
345
+ ul.removeChild(li);
346
+ });
347
+ if (ul.children.length === 0) ul.remove();
348
+ // Move caret to first converted paragraph
349
+ if (firstP) {
370
350
  const nr = document.createRange();
371
- nr.setStart(textNode, 0);
351
+ nr.setStart(firstP.firstChild || firstP, 0);
372
352
  nr.collapse(true);
373
353
  sel.removeAllRanges();
374
354
  sel.addRange(nr);
375
355
  }
356
+ return;
376
357
  }
377
358
  }
359
+
360
+ // Otherwise: insert new checklist from selected text
361
+ const text = sel.toString();
362
+ const lines = text.split(/\r?\n/).filter((l) => l.trim().length > 0);
363
+ if (lines.length === 0) return;
364
+ const items = lines
365
+ .map(
366
+ (l) =>
367
+ `<li><input type="checkbox" contenteditable="false">${l || '\u200B'}</li>`,
368
+ )
369
+ .join('');
370
+ document.execCommand(
371
+ 'insertHTML',
372
+ false,
373
+ `<ul class="an-checklist">${items}</ul>`,
374
+ );
378
375
  }
379
376
 
380
377
  /**
package/src/js/index.js CHANGED
@@ -99,7 +99,7 @@ const AutumnNote = {
99
99
  registerModule(name, ModuleClass) { _customModules.set(name, ModuleClass); },
100
100
 
101
101
  /** Library version */
102
- version: '1.0.1',
102
+ version: '1.0.4',
103
103
  };
104
104
 
105
105
  // ---------------------------------------------------------------------------
@@ -1,12 +1,12 @@
1
- /**
2
- * index.umd.js — UMD entry point for AutumnNote
3
- *
4
- * Re-exports only the default export so the UMD global is the factory object
5
- * directly, enabling the script-tag usage documented in the README:
6
- *
7
- * <script src="dist/autumnnote.umd.js"></script>
8
- * <script>
9
- * const editor = AutumnNote.create('#my-editor');
10
- * </script>
11
- */
12
- export { default } from './index.js';
1
+ /**
2
+ * index.umd.js — UMD entry point for AutumnNote
3
+ *
4
+ * Re-exports only the default export so the UMD global is the factory object
5
+ * directly, enabling the script-tag usage documented in the README:
6
+ *
7
+ * <script src="dist/autumnnote.umd.js"></script>
8
+ * <script>
9
+ * const editor = AutumnNote.create('#my-editor');
10
+ * </script>
11
+ */
12
+ export { default } from './index.js';