autumnnote 1.6.1 → 1.6.3

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.
@@ -7282,15 +7282,13 @@ var Fullscreen = class {
7282
7282
  }
7283
7283
  };
7284
7284
  //#endregion
7285
- //#region src/js/module/LinkDialog.js
7285
+ //#region src/js/module/BaseDialog.js
7286
7286
  /**
7287
- * LinkDialog.js - Dialog for inserting / editing hyperlinks
7288
- * Inspired by Summernote's LinkDialog rewritten without jQuery
7287
+ * Shared lifecycle and shell-building logic for all modal dialogs.
7288
+ * Subclasses implement _buildDialog() for their specific form fields.
7289
7289
  */
7290
- var LinkDialog = class {
7291
- /**
7292
- * @param {import('../Context.js').Context} context
7293
- */
7290
+ var BaseDialog = class {
7291
+ /** @param {import('../Context.js').Context} context */
7294
7292
  constructor(context) {
7295
7293
  this.context = context;
7296
7294
  this.options = context.options;
@@ -7298,6 +7296,9 @@ var LinkDialog = class {
7298
7296
  this._dialog = null;
7299
7297
  this._disposers = [];
7300
7298
  this._savedRange = null;
7299
+ /** @type {HTMLElement|null} First focusable input; set by subclass in _buildDialog(). */
7300
+ this._firstInput = null;
7301
+ this._removeTrap = null;
7301
7302
  }
7302
7303
  initialize() {
7303
7304
  this._dialog = this._buildDialog();
@@ -7307,36 +7308,113 @@ var LinkDialog = class {
7307
7308
  destroy() {
7308
7309
  this._disposers.forEach((d) => d());
7309
7310
  this._disposers = [];
7310
- if (this._dialog && this._dialog.parentNode) this._dialog.remove();
7311
+ if (this._dialog?.parentNode) this._dialog.remove();
7311
7312
  this._dialog = null;
7312
7313
  }
7313
- /**
7314
- * Opens the link dialog.
7315
- * Pre-fills with the currently selected link if present.
7316
- */
7317
- show() {
7314
+ /** Saves the current selection range before opening the dialog. */
7315
+ _saveRange() {
7318
7316
  withSavedRange((range) => {
7319
7317
  this._savedRange = range;
7320
7318
  });
7321
- this._prefill();
7322
- this._open();
7323
7319
  }
7324
- _buildDialog() {
7325
- const L = this.context.locale.linkDialog;
7320
+ _open() {
7321
+ if (this._dialog) {
7322
+ this._dialog.style.display = "flex";
7323
+ this._removeTrap = trapFocus(this._dialog, () => this._close());
7324
+ setTimeout(() => this._firstInput && this._firstInput.focus(), 50);
7325
+ }
7326
+ }
7327
+ _close() {
7328
+ if (this._dialog) this._dialog.style.display = "none";
7329
+ if (this._removeTrap) {
7330
+ this._removeTrap();
7331
+ this._removeTrap = null;
7332
+ }
7333
+ this._savedRange = null;
7334
+ }
7335
+ /**
7336
+ * Builds the overlay + box + header shell common to all dialogs.
7337
+ * Also wires up draggable and overlay-click-to-close.
7338
+ * @param {string} ariaLabel
7339
+ * @param {string} iconHtml Raw SVG string for the dialog icon
7340
+ * @param {string} titleText
7341
+ * @returns {{ overlay: HTMLElement, box: HTMLElement }}
7342
+ */
7343
+ _buildDialogShell(ariaLabel, iconHtml, titleText) {
7326
7344
  const overlay = createElement("div", {
7327
7345
  class: "an-dialog-overlay",
7328
7346
  role: "dialog",
7329
7347
  "aria-modal": "true",
7330
- "aria-label": L.ariaLabel
7348
+ "aria-label": ariaLabel
7331
7349
  });
7332
7350
  const box = createElement("div", { class: "an-dialog-box" });
7333
7351
  const header = createElement("div", { class: "an-dialog-header" });
7334
7352
  const iconEl = createElement("span", { class: "an-dialog-icon" });
7335
- iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>`;
7336
- const title = createElement("h3", { class: "an-dialog-title" });
7337
- title.textContent = L.title;
7353
+ iconEl.innerHTML = iconHtml;
7354
+ const titleEl = createElement("h3", { class: "an-dialog-title" });
7355
+ titleEl.textContent = titleText;
7338
7356
  header.appendChild(iconEl);
7339
- header.appendChild(title);
7357
+ header.appendChild(titleEl);
7358
+ box.appendChild(header);
7359
+ overlay.appendChild(box);
7360
+ makeDraggable(header, box);
7361
+ const d = on(overlay, "click", (e) => {
7362
+ if (e.target === overlay) this._close();
7363
+ });
7364
+ this._disposers.push(d);
7365
+ return {
7366
+ overlay,
7367
+ box
7368
+ };
7369
+ }
7370
+ /**
7371
+ * Builds an action button row with a primary insert button and a cancel button.
7372
+ * Disposers are registered automatically.
7373
+ * @param {string} insertLabel
7374
+ * @param {string} cancelLabel
7375
+ * @param {() => void} onInsert
7376
+ * @returns {HTMLElement}
7377
+ */
7378
+ _buildButtonRow(insertLabel, cancelLabel, onInsert) {
7379
+ const btnRow = createElement("div", { class: "an-dialog-actions" });
7380
+ const insertBtn = createElement("button", {
7381
+ type: "button",
7382
+ class: "an-btn an-btn-primary"
7383
+ });
7384
+ insertBtn.textContent = insertLabel;
7385
+ const cancelBtn = createElement("button", {
7386
+ type: "button",
7387
+ class: "an-btn"
7388
+ });
7389
+ cancelBtn.textContent = cancelLabel;
7390
+ btnRow.appendChild(insertBtn);
7391
+ btnRow.appendChild(cancelBtn);
7392
+ const d1 = on(insertBtn, "click", onInsert);
7393
+ const d2 = on(cancelBtn, "click", () => this._close());
7394
+ this._disposers.push(d1, d2);
7395
+ return btnRow;
7396
+ }
7397
+ };
7398
+ //#endregion
7399
+ //#region src/js/module/LinkDialog.js
7400
+ /**
7401
+ * LinkDialog.js - Dialog for inserting / editing hyperlinks
7402
+ * Inspired by Summernote's LinkDialog — rewritten without jQuery
7403
+ */
7404
+ var ICON_SVG$3 = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>`;
7405
+ var LinkDialog = class extends BaseDialog {
7406
+ /**
7407
+ * Opens the link dialog.
7408
+ * Pre-fills with the currently selected link if present.
7409
+ */
7410
+ show() {
7411
+ this._saveRange();
7412
+ this._prefill();
7413
+ this._open();
7414
+ }
7415
+ _buildDialog() {
7416
+ const L = this.context.locale.linkDialog;
7417
+ const { overlay, box } = this._buildDialogShell(L.ariaLabel, ICON_SVG$3, L.title);
7340
7418
  const urlLabel = createElement("label", { class: "an-label" });
7341
7419
  urlLabel.textContent = L.url;
7342
7420
  const urlInput = createElement("input", {
@@ -7348,6 +7426,7 @@ var LinkDialog = class {
7348
7426
  autocomplete: "off"
7349
7427
  });
7350
7428
  this._urlInput = urlInput;
7429
+ this._firstInput = urlInput;
7351
7430
  const textLabel = createElement("label", { class: "an-label" });
7352
7431
  textLabel.textContent = L.displayText;
7353
7432
  const textInput = createElement("input", {
@@ -7368,27 +7447,8 @@ var LinkDialog = class {
7368
7447
  this._tabCheckbox = tabCheckbox;
7369
7448
  tabLabel.appendChild(tabCheckbox);
7370
7449
  tabLabel.appendChild(document.createTextNode(" " + L.openInNewTab));
7371
- const btnRow = createElement("div", { class: "an-dialog-actions" });
7372
- const insertBtn = createElement("button", {
7373
- type: "button",
7374
- class: "an-btn an-btn-primary"
7375
- });
7376
- insertBtn.textContent = L.insertBtn;
7377
- const cancelBtn = createElement("button", {
7378
- type: "button",
7379
- class: "an-btn"
7380
- });
7381
- cancelBtn.textContent = L.cancelBtn;
7382
- btnRow.appendChild(insertBtn);
7383
- btnRow.appendChild(cancelBtn);
7384
- box.append(header, urlLabel, urlInput, textLabel, textInput, tabLabel, btnRow);
7385
- overlay.appendChild(box);
7386
- makeDraggable(header, box);
7387
- const d1 = on(insertBtn, "click", () => this._onInsert());
7388
- const d2 = on(cancelBtn, "click", () => this._close());
7389
- const d3 = on(overlay, "click", (e) => {
7390
- if (e.target === overlay) this._close();
7391
- });
7450
+ const btnRow = this._buildButtonRow(L.insertBtn, L.cancelBtn, () => this._onInsert());
7451
+ box.append(urlLabel, urlInput, textLabel, textInput, tabLabel, btnRow);
7392
7452
  const d4 = on(urlInput, "keydown", (e) => {
7393
7453
  if (e.key === "Enter") {
7394
7454
  e.preventDefault();
@@ -7401,7 +7461,7 @@ var LinkDialog = class {
7401
7461
  this._onInsert();
7402
7462
  }
7403
7463
  });
7404
- this._disposers.push(d1, d2, d3, d4, d5);
7464
+ this._disposers.push(d4, d5);
7405
7465
  return overlay;
7406
7466
  }
7407
7467
  _prefill() {
@@ -7450,21 +7510,6 @@ var LinkDialog = class {
7450
7510
  this.context.invoke("editor.insertLink", url, text, newTab);
7451
7511
  this._close();
7452
7512
  }
7453
- _open() {
7454
- if (this._dialog) {
7455
- this._dialog.style.display = "flex";
7456
- this._removeTrap = trapFocus(this._dialog, () => this._close());
7457
- setTimeout(() => this._urlInput && this._urlInput.focus(), 50);
7458
- }
7459
- }
7460
- _close() {
7461
- if (this._dialog) this._dialog.style.display = "none";
7462
- if (this._removeTrap) {
7463
- this._removeTrap();
7464
- this._removeTrap = null;
7465
- }
7466
- this._savedRange = null;
7467
- }
7468
7513
  };
7469
7514
  //#endregion
7470
7515
  //#region src/js/module/ImageDialog.js
@@ -7472,33 +7517,10 @@ var LinkDialog = class {
7472
7517
  * ImageDialog.js - Dialog for inserting images (by URL or file upload)
7473
7518
  * Inspired by Summernote's ImageDialog — rewritten without jQuery
7474
7519
  */
7475
- var ImageDialog = class {
7476
- /**
7477
- * @param {import('../Context.js').Context} context
7478
- */
7479
- constructor(context) {
7480
- this.context = context;
7481
- this.options = context.options;
7482
- /** @type {HTMLElement|null} */
7483
- this._dialog = null;
7484
- this._disposers = [];
7485
- this._savedRange = null;
7486
- }
7487
- initialize() {
7488
- this._dialog = this._buildDialog();
7489
- document.body.appendChild(this._dialog);
7490
- return this;
7491
- }
7492
- destroy() {
7493
- this._disposers.forEach((d) => d());
7494
- this._disposers = [];
7495
- if (this._dialog && this._dialog.parentNode) this._dialog.remove();
7496
- this._dialog = null;
7497
- }
7520
+ var ICON_SVG$2 = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>`;
7521
+ var ImageDialog = class extends BaseDialog {
7498
7522
  show() {
7499
- withSavedRange((range) => {
7500
- this._savedRange = range;
7501
- });
7523
+ this._saveRange();
7502
7524
  this._urlInput.value = "";
7503
7525
  this._altInput.value = "";
7504
7526
  if (this._fileInput) this._fileInput.value = "";
@@ -7506,20 +7528,7 @@ var ImageDialog = class {
7506
7528
  }
7507
7529
  _buildDialog() {
7508
7530
  const L = this.context.locale.imageDialog;
7509
- const overlay = createElement("div", {
7510
- class: "an-dialog-overlay",
7511
- role: "dialog",
7512
- "aria-modal": "true",
7513
- "aria-label": L.ariaLabel
7514
- });
7515
- const box = createElement("div", { class: "an-dialog-box" });
7516
- const header = createElement("div", { class: "an-dialog-header" });
7517
- const iconEl = createElement("span", { class: "an-dialog-icon" });
7518
- iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>`;
7519
- const title = createElement("h3", { class: "an-dialog-title" });
7520
- title.textContent = L.title;
7521
- header.appendChild(iconEl);
7522
- header.appendChild(title);
7531
+ const { overlay, box } = this._buildDialogShell(L.ariaLabel, ICON_SVG$2, L.title);
7523
7532
  const urlLabel = createElement("label", { class: "an-label" });
7524
7533
  urlLabel.textContent = L.imageUrl;
7525
7534
  const urlInput = createElement("input", {
@@ -7529,6 +7538,7 @@ var ImageDialog = class {
7529
7538
  autocomplete: "off"
7530
7539
  });
7531
7540
  this._urlInput = urlInput;
7541
+ this._firstInput = urlInput;
7532
7542
  const altLabel = createElement("label", { class: "an-label" });
7533
7543
  altLabel.textContent = L.altText;
7534
7544
  const altInput = createElement("input", {
@@ -7538,7 +7548,7 @@ var ImageDialog = class {
7538
7548
  autocomplete: "off"
7539
7549
  });
7540
7550
  this._altInput = altInput;
7541
- box.append(header, urlLabel, urlInput, altLabel, altInput);
7551
+ box.append(urlLabel, urlInput, altLabel, altInput);
7542
7552
  const alignLabel = createElement("label", { class: "an-label" });
7543
7553
  alignLabel.textContent = L.alignment;
7544
7554
  const alignRow = createElement("div", { class: "an-align-row" });
@@ -7592,27 +7602,8 @@ var ImageDialog = class {
7592
7602
  this._disposers.push(d);
7593
7603
  box.append(fileLabel, fileInput, fileHint);
7594
7604
  }
7595
- const btnRow = createElement("div", { class: "an-dialog-actions" });
7596
- const insertBtn = createElement("button", {
7597
- type: "button",
7598
- class: "an-btn an-btn-primary"
7599
- });
7600
- insertBtn.textContent = L.insertBtn;
7601
- const cancelBtn = createElement("button", {
7602
- type: "button",
7603
- class: "an-btn"
7604
- });
7605
- cancelBtn.textContent = L.cancelBtn;
7606
- btnRow.appendChild(insertBtn);
7607
- btnRow.appendChild(cancelBtn);
7605
+ const btnRow = this._buildButtonRow(L.insertBtn, L.cancelBtn, () => this._onInsert());
7608
7606
  box.append(btnRow);
7609
- overlay.appendChild(box);
7610
- makeDraggable(header, box);
7611
- const d1 = on(insertBtn, "click", () => this._onInsert());
7612
- const d2 = on(cancelBtn, "click", () => this._close());
7613
- const d3 = on(overlay, "click", (e) => {
7614
- if (e.target === overlay) this._close();
7615
- });
7616
7607
  const d4 = on(urlInput, "keydown", (e) => {
7617
7608
  if (e.key === "Enter") {
7618
7609
  e.preventDefault();
@@ -7625,7 +7616,7 @@ var ImageDialog = class {
7625
7616
  this._onInsert();
7626
7617
  }
7627
7618
  });
7628
- this._disposers.push(d1, d2, d3, d4, d5);
7619
+ this._disposers.push(d4, d5);
7629
7620
  return overlay;
7630
7621
  }
7631
7622
  _onFileChange() {
@@ -7680,21 +7671,6 @@ var ImageDialog = class {
7680
7671
  this.context.invoke("editor.insertImage", src, alt, align);
7681
7672
  this._close();
7682
7673
  }
7683
- _open() {
7684
- if (this._dialog) {
7685
- this._dialog.style.display = "flex";
7686
- this._removeTrap = trapFocus(this._dialog, () => this._close());
7687
- setTimeout(() => this._urlInput && this._urlInput.focus(), 50);
7688
- }
7689
- }
7690
- _close() {
7691
- if (this._dialog) this._dialog.style.display = "none";
7692
- if (this._removeTrap) {
7693
- this._removeTrap();
7694
- this._removeTrap = null;
7695
- }
7696
- this._savedRange = null;
7697
- }
7698
7674
  };
7699
7675
  //#endregion
7700
7676
  //#region src/js/module/VideoDialog.js
@@ -7706,31 +7682,10 @@ var ImageDialog = class {
7706
7682
  * • Vimeo URLs → <iframe> embed
7707
7683
  * • Direct video URLs → <video> element (.mp4 / .webm / .ogg)
7708
7684
  */
7709
- var VideoDialog = class {
7710
- /** @param {import('../Context.js').Context} context */
7711
- constructor(context) {
7712
- this.context = context;
7713
- this.options = context.options;
7714
- /** @type {HTMLElement|null} */
7715
- this._dialog = null;
7716
- this._disposers = [];
7717
- this._savedRange = null;
7718
- }
7719
- initialize() {
7720
- this._dialog = this._buildDialog();
7721
- document.body.appendChild(this._dialog);
7722
- return this;
7723
- }
7724
- destroy() {
7725
- this._disposers.forEach((d) => d());
7726
- this._disposers = [];
7727
- if (this._dialog && this._dialog.parentNode) this._dialog.remove();
7728
- this._dialog = null;
7729
- }
7685
+ var ICON_SVG$1 = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2"/></svg>`;
7686
+ var VideoDialog = class extends BaseDialog {
7730
7687
  show() {
7731
- withSavedRange((range) => {
7732
- this._savedRange = range;
7733
- });
7688
+ this._saveRange();
7734
7689
  this._urlInput.value = "";
7735
7690
  this._widthInput.value = "560";
7736
7691
  this._hintEl.textContent = "";
@@ -7738,20 +7693,7 @@ var VideoDialog = class {
7738
7693
  }
7739
7694
  _buildDialog() {
7740
7695
  const L = this.context.locale.videoDialog;
7741
- const overlay = createElement("div", {
7742
- class: "an-dialog-overlay",
7743
- role: "dialog",
7744
- "aria-modal": "true",
7745
- "aria-label": L.ariaLabel
7746
- });
7747
- const box = createElement("div", { class: "an-dialog-box" });
7748
- const header = createElement("div", { class: "an-dialog-header" });
7749
- const iconEl = createElement("span", { class: "an-dialog-icon" });
7750
- iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2"/></svg>`;
7751
- const title = createElement("h3", { class: "an-dialog-title" });
7752
- title.textContent = L.title;
7753
- header.appendChild(iconEl);
7754
- header.appendChild(title);
7696
+ const { overlay, box } = this._buildDialogShell(L.ariaLabel, ICON_SVG$1, L.title);
7755
7697
  const urlLabel = createElement("label", { class: "an-label" });
7756
7698
  urlLabel.textContent = L.videoUrl;
7757
7699
  const urlInput = createElement("input", {
@@ -7761,6 +7703,7 @@ var VideoDialog = class {
7761
7703
  autocomplete: "off"
7762
7704
  });
7763
7705
  this._urlInput = urlInput;
7706
+ this._firstInput = urlInput;
7764
7707
  const hintEl = createElement("p", { class: "an-dialog-hint" });
7765
7708
  this._hintEl = hintEl;
7766
7709
  const widthLabel = createElement("label", { class: "an-label" });
@@ -7774,38 +7717,19 @@ var VideoDialog = class {
7774
7717
  value: "560"
7775
7718
  });
7776
7719
  this._widthInput = widthInput;
7777
- const btnRow = createElement("div", { class: "an-dialog-actions" });
7778
- const insertBtn = createElement("button", {
7779
- type: "button",
7780
- class: "an-btn an-btn-primary"
7781
- });
7782
- insertBtn.textContent = L.insertBtn;
7783
- const cancelBtn = createElement("button", {
7784
- type: "button",
7785
- class: "an-btn"
7786
- });
7787
- cancelBtn.textContent = L.cancelBtn;
7788
- btnRow.appendChild(insertBtn);
7789
- btnRow.appendChild(cancelBtn);
7790
- box.append(header, urlLabel, urlInput, hintEl, widthLabel, widthInput, btnRow);
7791
- overlay.appendChild(box);
7792
- makeDraggable(header, box);
7720
+ const btnRow = this._buildButtonRow(L.insertBtn, L.cancelBtn, () => this._onInsert());
7721
+ box.append(urlLabel, urlInput, hintEl, widthLabel, widthInput, btnRow);
7793
7722
  const d0 = on(urlInput, "input", () => {
7794
7723
  const info = this._parseVideoUrl(urlInput.value.trim());
7795
7724
  hintEl.textContent = info ? this.context.locale.videoDialog.detected(info.type) : urlInput.value ? this.context.locale.videoDialog.unknownFormat : "";
7796
7725
  });
7797
- const d1 = on(insertBtn, "click", () => this._onInsert());
7798
- const d2 = on(cancelBtn, "click", () => this._close());
7799
- const d3 = on(overlay, "click", (e) => {
7800
- if (e.target === overlay) this._close();
7801
- });
7802
7726
  const d4 = on(urlInput, "keydown", (e) => {
7803
7727
  if (e.key === "Enter") {
7804
7728
  e.preventDefault();
7805
7729
  this._onInsert();
7806
7730
  }
7807
7731
  });
7808
- this._disposers.push(d0, d1, d2, d3, d4);
7732
+ this._disposers.push(d0, d4);
7809
7733
  return overlay;
7810
7734
  }
7811
7735
  _onInsert() {
@@ -7825,21 +7749,6 @@ var VideoDialog = class {
7825
7749
  this.context.invoke("editor.insertVideo", html);
7826
7750
  this._close();
7827
7751
  }
7828
- _open() {
7829
- if (this._dialog) {
7830
- this._dialog.style.display = "flex";
7831
- this._removeTrap = trapFocus(this._dialog, () => this._close());
7832
- setTimeout(() => this._urlInput && this._urlInput.focus(), 50);
7833
- }
7834
- }
7835
- _close() {
7836
- if (this._dialog) this._dialog.style.display = "none";
7837
- if (this._removeTrap) {
7838
- this._removeTrap();
7839
- this._removeTrap = null;
7840
- }
7841
- this._savedRange = null;
7842
- }
7843
7752
  /**
7844
7753
  * Parses a video URL and returns { type, embedUrl } or null.
7845
7754
  * @param {string} url
@@ -12747,35 +12656,17 @@ var EMOJI_LIST = [
12747
12656
  "symbols"
12748
12657
  ]
12749
12658
  ];
12750
- var EmojiDialog = class {
12751
- /**
12752
- * @param {import('../Context.js').Context} context
12753
- */
12754
- constructor(context) {
12755
- this.context = context;
12756
- /** @type {HTMLElement|null} */
12757
- this._dialog = null;
12758
- this._disposers = [];
12759
- this._savedRange = null;
12760
- this._activeCat = "all";
12761
- }
12659
+ var EmojiDialog = class extends BaseDialog {
12660
+ _activeCat = "all";
12762
12661
  initialize() {
12763
12662
  return this;
12764
12663
  }
12765
- destroy() {
12766
- this._disposers.forEach((d) => d());
12767
- this._disposers = [];
12768
- if (this._dialog && this._dialog.parentNode) this._dialog.remove();
12769
- this._dialog = null;
12770
- }
12771
12664
  show() {
12772
12665
  if (!this._dialog) {
12773
12666
  this._dialog = this._buildDialog();
12774
12667
  document.body.appendChild(this._dialog);
12775
12668
  }
12776
- withSavedRange((range) => {
12777
- this._savedRange = range;
12778
- });
12669
+ this._saveRange();
12779
12670
  this._activeCat = "all";
12780
12671
  this._searchInput.value = "";
12781
12672
  this._updateCatTabs();
@@ -12844,6 +12735,7 @@ var EmojiDialog = class {
12844
12735
  grid.appendChild(cell);
12845
12736
  });
12846
12737
  this._grid = grid;
12738
+ this._firstInput = searchInput;
12847
12739
  const btnRow = createElement("div", { class: "an-dialog-actions" });
12848
12740
  const cancelBtn = createElement("button", {
12849
12741
  type: "button",
@@ -12933,21 +12825,6 @@ var EmojiDialog = class {
12933
12825
  editable.focus();
12934
12826
  this.context.invoke("editor.afterCommand");
12935
12827
  }
12936
- _open() {
12937
- if (this._dialog) {
12938
- this._dialog.style.display = "flex";
12939
- this._removeTrap = trapFocus(this._dialog, () => this._close());
12940
- setTimeout(() => this._searchInput?.focus(), 50);
12941
- }
12942
- }
12943
- _close() {
12944
- if (this._dialog) this._dialog.style.display = "none";
12945
- if (this._removeTrap) {
12946
- this._removeTrap();
12947
- this._removeTrap = null;
12948
- }
12949
- this._savedRange = null;
12950
- }
12951
12828
  };
12952
12829
  //#endregion
12953
12830
  //#region src/js/module/IconDialog.js
@@ -13205,19 +13082,9 @@ var ICON_LIST = [
13205
13082
  ["gift", "objects"],
13206
13083
  ["puzzle-piece", "objects"]
13207
13084
  ];
13208
- var IconDialog = class {
13209
- /**
13210
- * @param {import('../Context.js').Context} context
13211
- */
13212
- constructor(context) {
13213
- this.context = context;
13214
- /** @type {HTMLElement|null} */
13215
- this._dialog = null;
13216
- this._disposers = [];
13217
- this._savedRange = null;
13218
- this._selectedIcon = null;
13219
- this._activeCat = "all";
13220
- }
13085
+ var IconDialog = class extends BaseDialog {
13086
+ _selectedIcon = null;
13087
+ _activeCat = "all";
13221
13088
  initialize() {
13222
13089
  this._ensureFontAwesome();
13223
13090
  return this;
@@ -13241,20 +13108,12 @@ var IconDialog = class {
13241
13108
  link.referrerPolicy = "no-referrer";
13242
13109
  document.head.appendChild(link);
13243
13110
  }
13244
- destroy() {
13245
- this._disposers.forEach((d) => d());
13246
- this._disposers = [];
13247
- this._dialog?.remove();
13248
- this._dialog = null;
13249
- }
13250
13111
  show() {
13251
13112
  if (!this._dialog) {
13252
13113
  this._dialog = this._buildDialog();
13253
13114
  document.body.appendChild(this._dialog);
13254
13115
  }
13255
- withSavedRange((range) => {
13256
- this._savedRange = range;
13257
- });
13116
+ this._saveRange();
13258
13117
  this._selectedIcon = null;
13259
13118
  this._activeCat = "all";
13260
13119
  this._searchInput.value = "";
@@ -13295,6 +13154,7 @@ var IconDialog = class {
13295
13154
  autocomplete: "off"
13296
13155
  });
13297
13156
  this._searchInput = searchInput;
13157
+ this._firstInput = searchInput;
13298
13158
  const catBar = createElement("div", { class: "an-icon-cats" });
13299
13159
  const allTab = createElement("button", {
13300
13160
  type: "button",
@@ -13533,21 +13393,9 @@ var IconDialog = class {
13533
13393
  }
13534
13394
  this.context.invoke("editor.afterCommand");
13535
13395
  }
13536
- _open() {
13537
- if (this._dialog) {
13538
- this._dialog.style.display = "flex";
13539
- this._removeTrap = trapFocus(this._dialog, () => this._close());
13540
- setTimeout(() => this._searchInput?.focus(), 50);
13541
- }
13542
- }
13543
13396
  _close() {
13544
- if (this._dialog) this._dialog.style.display = "none";
13545
- if (this._removeTrap) {
13546
- this._removeTrap();
13547
- this._removeTrap = null;
13548
- }
13549
- this._savedRange = null;
13550
13397
  this._selectedIcon = null;
13398
+ super._close();
13551
13399
  }
13552
13400
  };
13553
13401
  //#endregion
@@ -14288,59 +14136,25 @@ var SHORTCUTS = [
14288
14136
  }]
14289
14137
  }
14290
14138
  ];
14291
- var ShortcutsDialog = class {
14292
- /** @param {import('../Context.js').Context} context */
14293
- constructor(context) {
14294
- this.context = context;
14295
- this._dialog = null;
14296
- this._closeBtn = null;
14297
- this._disposers = [];
14298
- }
14299
- initialize() {
14300
- this._dialog = this._buildDialog();
14301
- document.body.appendChild(this._dialog);
14302
- return this;
14303
- }
14304
- destroy() {
14305
- this._disposers.forEach((d) => d());
14306
- this._disposers = [];
14307
- this._dialog?.remove();
14308
- this._dialog = null;
14309
- }
14139
+ var ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="7" width="20" height="14" rx="2"/><path d="M6 11h.01M10 11h.01M14 11h.01M18 11h.01M6 15h.01M18 15h.01M10 15h4"/><path d="M7 3l5 4 5-4"/></svg>`;
14140
+ var ShortcutsDialog = class extends BaseDialog {
14310
14141
  show() {
14311
- if (this._dialog) {
14312
- this._dialog.style.display = "flex";
14313
- this._removeTrap = trapFocus(this._dialog, () => this._close());
14314
- setTimeout(() => this._closeBtn?.focus(), 50);
14315
- }
14316
- }
14317
- _close() {
14318
- if (this._dialog) this._dialog.style.display = "none";
14319
- if (this._removeTrap) {
14320
- this._removeTrap();
14321
- this._removeTrap = null;
14322
- }
14142
+ this._open();
14323
14143
  }
14324
14144
  _buildDialog() {
14325
- const overlay = createElement("div", {
14326
- class: "an-dialog-overlay",
14327
- role: "dialog",
14328
- "aria-modal": "true",
14329
- "aria-label": this.context.locale.shortcutsDialog.ariaLabel
14330
- });
14331
- const box = createElement("div", { class: "an-dialog-box an-shortcuts-box" });
14332
- const titleRow = createElement("div", { class: "an-icon-title-row" });
14333
- const title = createElement("h3", { class: "an-dialog-title" });
14334
- title.textContent = this.context.locale.shortcutsDialog.title;
14145
+ const L = this.context.locale.shortcutsDialog;
14146
+ const { overlay, box } = this._buildDialogShell(L.ariaLabel, ICON_SVG, L.title);
14147
+ box.classList.add("an-shortcuts-box");
14335
14148
  const closeBtn = createElement("button", {
14336
14149
  type: "button",
14337
14150
  class: "an-icon-close",
14338
- "aria-label": this.context.locale.shortcutsDialog.close
14151
+ "aria-label": L.close,
14152
+ style: "margin-left:auto"
14339
14153
  });
14340
14154
  closeBtn.textContent = "×";
14341
14155
  this._closeBtn = closeBtn;
14342
- titleRow.append(title, closeBtn);
14343
- box.appendChild(titleRow);
14156
+ this._firstInput = closeBtn;
14157
+ box.querySelector(".an-dialog-header").appendChild(closeBtn);
14344
14158
  (this.context.locale.shortcutsDialog.shortcuts || SHORTCUTS).forEach(({ category, items }) => {
14345
14159
  const catEl = createElement("div", { class: "an-shortcuts-cat" });
14346
14160
  catEl.textContent = category;
@@ -14357,12 +14171,8 @@ var ShortcutsDialog = class {
14357
14171
  });
14358
14172
  box.appendChild(table);
14359
14173
  });
14360
- overlay.appendChild(box);
14361
14174
  const d1 = on(closeBtn, "click", () => this._close());
14362
- const d2 = on(overlay, "click", (e) => {
14363
- if (e.target === overlay) this._close();
14364
- });
14365
- this._disposers.push(d1, d2);
14175
+ this._disposers.push(d1);
14366
14176
  return overlay;
14367
14177
  }
14368
14178
  };
@@ -14376,12 +14186,10 @@ var ShortcutsDialog = class {
14376
14186
  * for highlighting. Supports case-sensitive search, Prev/Next navigation and
14377
14187
  * single / replace-all replacement.
14378
14188
  */
14379
- var FindReplace = class {
14189
+ var FindReplace = class extends BaseDialog {
14380
14190
  /** @param {import('../Context.js').Context} context */
14381
14191
  constructor(context) {
14382
- this.context = context;
14383
- /** @type {HTMLElement|null} */
14384
- this._dialog = null;
14192
+ super(context);
14385
14193
  /** @type {HTMLInputElement|null} */
14386
14194
  this._findInput = null;
14387
14195
  /** @type {HTMLInputElement|null} */
@@ -14402,23 +14210,13 @@ var FindReplace = class {
14402
14210
  this._queryRegex = null;
14403
14211
  this._lastQuery = null;
14404
14212
  this._lastCaseSensitive = null;
14405
- this._disposers = [];
14406
- this._removeTrap = null;
14407
14213
  this._focusTimer = null;
14408
14214
  }
14409
- initialize() {
14410
- this._dialog = this._buildDialog();
14411
- document.body.appendChild(this._dialog);
14412
- return this;
14413
- }
14414
14215
  destroy() {
14415
14216
  clearTimeout(this._focusTimer);
14416
14217
  this._focusTimer = null;
14417
14218
  this._clearHighlights();
14418
- this._disposers.forEach((d) => d());
14419
- this._disposers = [];
14420
- if (this._dialog && this._dialog.parentNode) this._dialog.remove();
14421
- this._dialog = null;
14219
+ super.destroy();
14422
14220
  }
14423
14221
  /**
14424
14222
  * Opens the dialog in 'find' or 'replace' mode.
@@ -16333,7 +16131,7 @@ var Context = class {
16333
16131
  register("markdownShortcuts", MarkdownShortcuts);
16334
16132
  register("bubbleToolbar", BubbleToolbar);
16335
16133
  register("mention", Mention);
16336
- for (const [name, ModuleClass] of _customModules) register(name, ModuleClass);
16134
+ if (_customModules.size > 0) for (const [name, ModuleClass] of _customModules) register(name, ModuleClass);
16337
16135
  }
16338
16136
  /**
16339
16137
  * Registers and initialises a custom module on this instance.
@@ -16386,6 +16184,7 @@ var Context = class {
16386
16184
  });
16387
16185
  }
16388
16186
  _applyGlobalPlugins() {
16187
+ if (_globalPlugins.size === 0) return;
16389
16188
  for (const { plugin, options } of _globalPlugins.values()) this._installPlugin(plugin, options);
16390
16189
  }
16391
16190
  _bindEditorEvents(editable) {
@@ -16908,7 +16707,7 @@ var AutumnNote = {
16908
16707
  return this;
16909
16708
  },
16910
16709
  /** Library version */
16911
- version: "1.5.0"
16710
+ version: "1.6.3"
16912
16711
  };
16913
16712
  /**
16914
16713
  * @param {string|Element|NodeList|Element[]} selector