autumnnote 1.1.1 → 1.2.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Autumn Note
2
2
 
3
- <p align="center"><img src="image/banner.png" width="120" alt="AutumnNote Banner"/></p>
3
+ <p align="center"><img src="public/image/banner.png" width="120" alt="AutumnNote Banner"/></p>
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/autumnnote?label=version&color=blue&logo=npm)](https://www.npmjs.com/package/autumnnote)
6
6
  [![npm downloads](https://img.shields.io/npm/dw/autumnnote?label=downloads%2Fweek&logo=npm&color=cb3837)](https://www.npmjs.com/package/autumnnote)
@@ -88,7 +88,7 @@ Right-click inside the editor opens a context menu with: **Undo**, **Redo**, **C
88
88
  - **Auto-fallback** — unknown codes or missing keys fall back to English
89
89
 
90
90
  ### UI
91
- - **Toolbar** — fully configurable button groups; overflow strategy: `wrap` (default) or `scroll`; FontAwesome icons with built-in SVG fallback
91
+ - **Toolbar** — fully configurable button groups; overflow strategy: `wrap` (default) or `scroll`; on viewports ≤ 640 px the toolbar automatically switches to a single horizontally-scrollable row regardless of the `toolbarOverflow` setting; FontAwesome icons with built-in SVG fallback
92
92
  - **Sticky toolbar** — `stickyToolbar: true` pins the toolbar to the viewport top; configurable offset for fixed nav bars
93
93
  - **Dark / light theme** — `theme: 'dark'` or `theme: 'light'` (default); full SCSS variable coverage
94
94
  - **Image resizer** — drag handle on selected image to resize proportionally
@@ -100,7 +100,7 @@ Right-click inside the editor opens a context menu with: **Undo**, **Redo**, **C
100
100
  - **Read-only mode** — `readOnly: true` renders a non-editable preview with toolbar hidden; toggle at runtime via `editor.setDisabled()`
101
101
  - **Auto-save** — `autoSave: true` persists content to `localStorage` on every change; key configurable via `autoSaveKey`
102
102
  - **Auto-save restore** — when `autoSave` and `autoSaveRestore` are both `true`, a dismissible banner prompts the user to restore or discard a previously saved draft on load; configurable age window via `autoSaveRestoreTimeout`
103
- - **Bubble toolbar** — `bubbleToolbar: true` shows a compact floating toolbar above selected text with quick-access buttons (bold, italic, underline, strikethrough, link, text colour, remove format, inline code); button set configurable via `bubbleToolbarItems`
103
+ - **Bubble toolbar** — `bubbleToolbar: true` shows a compact floating toolbar above selected text; default buttons: bold, italic, underline, strikethrough, link, **text colour**, **highlight colour**, remove format, inline code; each colour button displays a live colour-strip indicator and opens an inline colour palette (matching the context menu); button set configurable via `bubbleToolbarItems`
104
104
  - **Markdown shortcuts** — `markdownShortcuts: true` (default) converts Markdown syntax typed in the editor into HTML in real time: `# ` → H1–H3, `> ` → blockquote, `- ` / `* ` → unordered list, `1. ` → ordered list, `[ ] ` → checklist, `---` → HR, ` ``` ` → code block; inline: `**bold**`, `*italic*`, `~~strikethrough~~`, `` `code` ``
105
105
  - **Custom focus ring** — `focusColor` accepts any CSS colour string to override the default blue focus ring
106
106
  - **Spellcheck** — browser spellcheck enabled by default (`spellcheck: true`)
@@ -68,6 +68,7 @@
68
68
  background: #f9fafb;
69
69
  border-bottom: 1px solid #d1d5db;
70
70
  user-select: none;
71
+ border-radius: 6px 6px 0 0;
71
72
  }
72
73
 
73
74
  .an-btn-group {
@@ -140,9 +141,7 @@
140
141
 
141
142
  .an-table-picker-popup {
142
143
  display: none;
143
- position: absolute;
144
- top: calc(100% + 4px);
145
- left: 0;
144
+ position: fixed;
146
145
  background: #ffffff;
147
146
  border: 1px solid #d1d5db;
148
147
  border-radius: 6px;
@@ -654,6 +653,11 @@
654
653
  font-size: 11px;
655
654
  color: #6b7280;
656
655
  user-select: none;
656
+ border-radius: 0 0 6px 6px;
657
+ }
658
+
659
+ .an-container > .an-editable:last-child {
660
+ border-radius: 0 0 6px 6px;
657
661
  }
658
662
 
659
663
  .an-status-info {
@@ -1725,6 +1729,30 @@
1725
1729
  border-radius: 2px;
1726
1730
  }
1727
1731
 
1732
+ @media (max-width: 640px) {
1733
+ .an-toolbar {
1734
+ flex-wrap: nowrap;
1735
+ overflow-x: auto;
1736
+ scrollbar-width: none;
1737
+ -ms-overflow-style: none;
1738
+ }
1739
+ .an-toolbar::-webkit-scrollbar {
1740
+ display: none;
1741
+ }
1742
+ .an-select {
1743
+ min-width: 72px;
1744
+ max-width: 90px;
1745
+ font-size: 11px;
1746
+ }
1747
+ .an-select-style {
1748
+ min-width: 62px;
1749
+ max-width: 76px;
1750
+ }
1751
+ .an-select-narrow {
1752
+ min-width: 50px;
1753
+ max-width: 66px;
1754
+ }
1755
+ }
1728
1756
  .an-dir-rtl .an-toolbar {
1729
1757
  direction: rtl;
1730
1758
  }
@@ -1910,6 +1938,27 @@
1910
1938
  background: #dbeafe;
1911
1939
  color: #3b82f6;
1912
1940
  }
1941
+ .an-bubble-btn.an-bubble-btn--color {
1942
+ flex-direction: column;
1943
+ gap: 1px;
1944
+ padding: 4px 0 3px;
1945
+ }
1946
+ .an-bubble-btn.an-bubble-btn--color .an-bubble-btn-svg {
1947
+ display: flex;
1948
+ align-items: center;
1949
+ justify-content: center;
1950
+ line-height: 0;
1951
+ }
1952
+ .an-bubble-btn.an-bubble-btn--color .an-bubble-btn-svg svg {
1953
+ display: block;
1954
+ }
1955
+ .an-bubble-btn.an-bubble-btn--color .an-bubble-color-strip {
1956
+ display: block;
1957
+ width: 14px;
1958
+ height: 3px;
1959
+ border-radius: 1px;
1960
+ pointer-events: none;
1961
+ }
1913
1962
  .an-theme-dark .an-bubble-btn {
1914
1963
  color: #cdd6f4;
1915
1964
  }
@@ -1921,6 +1970,21 @@
1921
1970
  color: #93c5fd;
1922
1971
  }
1923
1972
 
1973
+ .an-bubble-color-picker {
1974
+ position: fixed;
1975
+ z-index: 10041;
1976
+ background: #ffffff;
1977
+ border: 1px solid #d1d5db;
1978
+ border-radius: 8px;
1979
+ box-shadow: 0 8px 28px rgba(0, 0, 0, 0.14);
1980
+ padding: 4px 0;
1981
+ min-width: 160px;
1982
+ }
1983
+ .an-theme-dark .an-bubble-color-picker {
1984
+ background: #24273a;
1985
+ border-color: #3f3f5f;
1986
+ }
1987
+
1924
1988
  .an-mention-dropdown {
1925
1989
  display: none;
1926
1990
  position: fixed;
@@ -1455,6 +1455,7 @@ var defaultOptions = {
1455
1455
  "underline",
1456
1456
  "link",
1457
1457
  "foreColor",
1458
+ "hiliteColor",
1458
1459
  "removeFormat"
1459
1460
  ],
1460
1461
  mention: null
@@ -5838,7 +5839,18 @@ var Toolbar = class {
5838
5839
  };
5839
5840
  const openPopup = () => {
5840
5841
  isOpen = true;
5842
+ const rect = btn.getBoundingClientRect();
5843
+ popup.style.visibility = "hidden";
5841
5844
  popup.style.display = "block";
5845
+ const pw = popup.offsetWidth;
5846
+ const ph = popup.offsetHeight;
5847
+ let left = rect.left;
5848
+ let top = rect.bottom + 4;
5849
+ if (left + pw > window.innerWidth - 8) left = Math.max(8, window.innerWidth - pw - 8);
5850
+ if (top + ph > window.innerHeight - 8) top = rect.top - ph - 4;
5851
+ popup.style.left = `${left}px`;
5852
+ popup.style.top = `${top}px`;
5853
+ popup.style.visibility = "";
5842
5854
  btn.setAttribute("aria-expanded", "true");
5843
5855
  };
5844
5856
  const closePopup = () => {
@@ -5870,9 +5882,11 @@ var Toolbar = class {
5870
5882
  const d5 = on(document, "click", () => {
5871
5883
  if (isOpen) closePopup();
5872
5884
  });
5873
- this._disposers.push(d1, d2, d3, d4, d5);
5885
+ this._disposers.push(d1, d2, d3, d4, d5, () => {
5886
+ if (popup.parentNode) popup.parentNode.removeChild(popup);
5887
+ });
5874
5888
  wrap.appendChild(btn);
5875
- wrap.appendChild(popup);
5889
+ document.body.appendChild(popup);
5876
5890
  return wrap;
5877
5891
  }
5878
5892
  /**
@@ -12969,7 +12983,7 @@ var ICONS = {
12969
12983
  noColor: `<svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="4" y1="4" x2="20" y2="20"/><line x1="20" y1="4" x2="4" y2="20"/></svg>`,
12970
12984
  back: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"/></svg>`
12971
12985
  };
12972
- var COLOR_PRESETS = [
12986
+ var COLOR_PRESETS$1 = [
12973
12987
  "#000000",
12974
12988
  "#434343",
12975
12989
  "#666666",
@@ -13257,7 +13271,7 @@ var ContextMenu = class {
13257
13271
  }
13258
13272
  if (it.colorPalette) {
13259
13273
  const palette = createElement("div", { class: "an-context-color-palette" });
13260
- COLOR_PRESETS.forEach((color) => {
13274
+ COLOR_PRESETS$1.forEach((color) => {
13261
13275
  const sw = createElement("div", {
13262
13276
  class: "an-context-color-swatch",
13263
13277
  title: color,
@@ -14916,9 +14930,35 @@ var MarkdownShortcuts = class {
14916
14930
  * controlled by `bubbleToolbarItems` (array of button name strings).
14917
14931
  *
14918
14932
  * Built-in names: 'bold', 'italic', 'underline', 'strikethrough',
14919
- * 'link', 'foreColor', 'removeFormat', 'inlineCode'.
14933
+ * 'link', 'foreColor', 'hiliteColor', 'removeFormat', 'inlineCode'.
14920
14934
  * Any name not found in the built-in map is silently ignored.
14921
14935
  */
14936
+ var COLOR_PRESETS = [
14937
+ "#000000",
14938
+ "#434343",
14939
+ "#666666",
14940
+ "#999999",
14941
+ "#b7b7b7",
14942
+ "#cccccc",
14943
+ "#efefef",
14944
+ "#ffffff",
14945
+ "#ff0000",
14946
+ "#ff9900",
14947
+ "#ffff00",
14948
+ "#00ff00",
14949
+ "#00ffff",
14950
+ "#4a86e8",
14951
+ "#9900ff",
14952
+ "#ff00ff",
14953
+ "#f4cccc",
14954
+ "#fce5cd",
14955
+ "#fff2cc",
14956
+ "#d9ead3",
14957
+ "#d0e0e3",
14958
+ "#c9daf8",
14959
+ "#d9d2e9",
14960
+ "#ead1dc"
14961
+ ];
14922
14962
  var _S = "stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"";
14923
14963
  var _svg = (p) => `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" ${_S} style="display:block">${p}</svg>`;
14924
14964
  var _ICONS = {
@@ -14928,6 +14968,7 @@ var _ICONS = {
14928
14968
  strikethrough: _svg("<path d=\"M17.3 12H6.7\"/><path d=\"M10 6.5C10 5.1 11.1 4 12.5 4c1.4 0 2.5 1.1 2.5 2.5 0 .8-.4 1.5-1 2\"/><path d=\"M14 17.5C14 19 12.9 20 11.5 20 10.1 20 9 18.9 9 17.5c0-.8.4-1.5 1-2\"/>"),
14929
14969
  link: _svg("<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\"/>"),
14930
14970
  foreColor: _svg("<path d=\"M4 20L12 4L20 20\"/><line x1=\"7.5\" y1=\"14\" x2=\"16.5\" y2=\"14\"/>"),
14971
+ hiliteColor: _svg("<path d=\"M3 21v-4l9-9 4 4-9 9z\"/><path d=\"M12 8l4 4\"/><line x1=\"3\" y1=\"21\" x2=\"21\" y2=\"21\"/>"),
14931
14972
  removeFormat: _svg("<path d=\"m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21\"/><path d=\"M22 21H7\"/><path d=\"m5 11 9 9\"/>"),
14932
14973
  inlineCode: _svg("<path d=\"M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1\"/><path d=\"M16 3h1a2 2 0 0 1 2 2v5c0 1.1.9 2 2 2a2 2 0 0 1-2 2v5a2 2 0 0 1-2 2h-1\"/>")
14933
14974
  };
@@ -14937,11 +14978,13 @@ var _ACTIONS = {
14937
14978
  underline: (ctx) => ctx.invoke("editor.underline"),
14938
14979
  strikethrough: (ctx) => ctx.invoke("editor.strikethrough"),
14939
14980
  link: (ctx) => ctx.invoke("linkDialog.show"),
14940
- foreColor: (ctx) => {
14941
- const color = window.prompt("Text color (hex or name):", "#000000");
14942
- if (color) ctx.invoke("editor.foreColor", color);
14981
+ removeFormat: (ctx) => {
14982
+ const editable = ctx.layoutInfo && ctx.layoutInfo.editable;
14983
+ if (!editable) return;
14984
+ editable.focus();
14985
+ document.execCommand("removeFormat");
14986
+ ctx.invoke("editor.afterCommand");
14943
14987
  },
14944
- removeFormat: (ctx) => ctx.invoke("editor.removeFormat"),
14945
14988
  inlineCode: (ctx) => ctx.invoke("editor.inlineCode")
14946
14989
  };
14947
14990
  var _ACTIVE = {
@@ -14950,6 +14993,10 @@ var _ACTIVE = {
14950
14993
  underline: () => document.queryCommandState("underline"),
14951
14994
  strikethrough: () => document.queryCommandState("strikeThrough")
14952
14995
  };
14996
+ var _COLOR_TYPES = {
14997
+ foreColor: true,
14998
+ hiliteColor: true
14999
+ };
14953
15000
  var BubbleToolbar = class {
14954
15001
  /** @param {import('../Context.js').Context} context */
14955
15002
  constructor(context) {
@@ -14957,6 +15004,10 @@ var BubbleToolbar = class {
14957
15004
  this.options = context.options;
14958
15005
  /** @type {HTMLElement|null} */
14959
15006
  this._el = null;
15007
+ /** @type {HTMLElement|null} */
15008
+ this._picker = null;
15009
+ this._pickerType = null;
15010
+ this._savedRange = null;
14960
15011
  this._visible = false;
14961
15012
  this._rafId = null;
14962
15013
  this._contextMenuOpen = false;
@@ -14969,6 +15020,7 @@ var BubbleToolbar = class {
14969
15020
  initialize() {
14970
15021
  if (!this.options.bubbleToolbar) return this;
14971
15022
  this._build();
15023
+ this._buildColorPicker();
14972
15024
  const d1 = on(document, "selectionchange", this._onSelectionChange);
14973
15025
  const d2 = on(document, "mousedown", this._onMousedown);
14974
15026
  const d3 = on(document, "keydown", this._onKeydown);
@@ -14986,6 +15038,8 @@ var BubbleToolbar = class {
14986
15038
  destroy() {
14987
15039
  if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
14988
15040
  this._el = null;
15041
+ if (this._picker && this._picker.parentNode) this._picker.parentNode.removeChild(this._picker);
15042
+ this._picker = null;
14989
15043
  this._disposers.forEach((d) => d());
14990
15044
  this._disposers = [];
14991
15045
  cancelAnimationFrame(this._rafId);
@@ -15001,25 +15055,41 @@ var BubbleToolbar = class {
15001
15055
  "underline",
15002
15056
  "link",
15003
15057
  "foreColor",
15058
+ "hiliteColor",
15004
15059
  "removeFormat"
15005
15060
  ];
15006
15061
  for (const name of items) {
15007
- if (!_ICONS[name] || !_ACTIONS[name]) continue;
15062
+ if (!_ICONS[name]) continue;
15008
15063
  const btn = document.createElement("button");
15009
15064
  btn.type = "button";
15010
- btn.className = "an-bubble-btn";
15011
15065
  btn.dataset.name = name;
15012
15066
  btn.setAttribute("aria-label", name);
15013
- btn.innerHTML = _ICONS[name];
15067
+ if (_COLOR_TYPES[name]) {
15068
+ btn.className = "an-bubble-btn an-bubble-btn--color";
15069
+ const iconWrap = document.createElement("span");
15070
+ iconWrap.className = "an-bubble-btn-svg";
15071
+ iconWrap.innerHTML = _ICONS[name];
15072
+ const strip = document.createElement("span");
15073
+ strip.className = "an-bubble-color-strip";
15074
+ strip.style.background = name === "foreColor" ? "#000000" : "transparent";
15075
+ btn.appendChild(iconWrap);
15076
+ btn.appendChild(strip);
15077
+ } else {
15078
+ btn.className = "an-bubble-btn";
15079
+ btn.innerHTML = _ICONS[name];
15080
+ }
15014
15081
  btn.addEventListener("mousedown", (e) => {
15015
15082
  e.preventDefault();
15016
15083
  });
15017
15084
  btn.addEventListener("click", (e) => {
15018
15085
  e.preventDefault();
15019
15086
  e.stopPropagation();
15087
+ if (_COLOR_TYPES[name]) {
15088
+ this._openColorPicker(name, btn);
15089
+ return;
15090
+ }
15020
15091
  this.context.invoke("editor.focus");
15021
- _ACTIONS[name](this.context);
15022
- this.context.invoke("editor.afterCommand");
15092
+ if (_ACTIONS[name]) _ACTIONS[name](this.context);
15023
15093
  this._syncActive();
15024
15094
  });
15025
15095
  el.appendChild(btn);
@@ -15027,6 +15097,100 @@ var BubbleToolbar = class {
15027
15097
  document.body.appendChild(el);
15028
15098
  this._el = el;
15029
15099
  }
15100
+ _buildColorPicker() {
15101
+ const picker = document.createElement("div");
15102
+ picker.className = "an-bubble-color-picker";
15103
+ picker.style.display = "none";
15104
+ picker.addEventListener("mousedown", (e) => e.preventDefault());
15105
+ const palette = document.createElement("div");
15106
+ palette.className = "an-context-color-palette";
15107
+ COLOR_PRESETS.forEach((color) => {
15108
+ const sw = document.createElement("div");
15109
+ sw.className = "an-context-color-swatch";
15110
+ sw.title = color;
15111
+ sw.style.background = color;
15112
+ sw.setAttribute("role", "button");
15113
+ sw.setAttribute("aria-label", color);
15114
+ sw.addEventListener("click", (e) => {
15115
+ e.stopPropagation();
15116
+ this._applyColor(this._pickerType, color);
15117
+ });
15118
+ palette.appendChild(sw);
15119
+ });
15120
+ const noColorBtn = document.createElement("div");
15121
+ noColorBtn.className = "an-context-color-swatch an-context-color-none";
15122
+ noColorBtn.title = "No highlight";
15123
+ noColorBtn.setAttribute("role", "button");
15124
+ noColorBtn.setAttribute("aria-label", "No highlight");
15125
+ noColorBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="4" y1="4" x2="20" y2="20"/><line x1="20" y1="4" x2="4" y2="20"/></svg>`;
15126
+ noColorBtn.addEventListener("click", (e) => {
15127
+ e.stopPropagation();
15128
+ this._applyColor("hiliteColor", "transparent");
15129
+ });
15130
+ const customRow = document.createElement("div");
15131
+ customRow.className = "an-context-color-custom";
15132
+ const colorInput = document.createElement("input");
15133
+ colorInput.type = "color";
15134
+ colorInput.value = "#000000";
15135
+ colorInput.title = "Custom color";
15136
+ colorInput.addEventListener("mousedown", (e) => e.stopPropagation());
15137
+ colorInput.addEventListener("change", () => {
15138
+ this._applyColor(this._pickerType, colorInput.value);
15139
+ });
15140
+ const customLabel = document.createElement("span");
15141
+ customLabel.textContent = "Custom…";
15142
+ customRow.appendChild(colorInput);
15143
+ customRow.appendChild(customLabel);
15144
+ picker.appendChild(palette);
15145
+ picker.appendChild(customRow);
15146
+ document.body.appendChild(picker);
15147
+ this._picker = picker;
15148
+ this._picker._paletteEl = palette;
15149
+ this._picker._noColorBtn = noColorBtn;
15150
+ this._picker._colorInput = colorInput;
15151
+ }
15152
+ _openColorPicker(type, anchorBtn) {
15153
+ const sel = window.getSelection();
15154
+ if (sel && sel.rangeCount > 0) this._savedRange = sel.getRangeAt(0).cloneRange();
15155
+ this._pickerType = type;
15156
+ const palette = this._picker._paletteEl;
15157
+ const noColorBtn = this._picker._noColorBtn;
15158
+ if (type === "hiliteColor") {
15159
+ if (!palette.contains(noColorBtn)) palette.appendChild(noColorBtn);
15160
+ } else if (palette.contains(noColorBtn)) palette.removeChild(noColorBtn);
15161
+ this._picker._colorInput.value = type === "foreColor" ? "#000000" : "#ffff00";
15162
+ this._picker.style.display = "block";
15163
+ const pw = this._picker.offsetWidth;
15164
+ const ph = this._picker.offsetHeight;
15165
+ const toolbarRect = this._el.getBoundingClientRect();
15166
+ let top = toolbarRect.top - ph - 6;
15167
+ if (top < 8) top = toolbarRect.bottom + 6;
15168
+ let left = anchorBtn.getBoundingClientRect().left;
15169
+ left = Math.max(8, Math.min(left, window.innerWidth - pw - 8));
15170
+ this._picker.style.left = `${left}px`;
15171
+ this._picker.style.top = `${top}px`;
15172
+ }
15173
+ _closeColorPicker() {
15174
+ if (this._picker) this._picker.style.display = "none";
15175
+ this._pickerType = null;
15176
+ }
15177
+ /** Restore the saved selection, apply execCommand, update the color strip, then close the picker. */
15178
+ _applyColor(type, color) {
15179
+ const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
15180
+ if (!editable || !this._savedRange) return;
15181
+ editable.focus();
15182
+ const sel = window.getSelection();
15183
+ sel.removeAllRanges();
15184
+ sel.addRange(this._savedRange.cloneRange());
15185
+ document.execCommand(type, false, color);
15186
+ this.context.invoke("editor.afterCommand");
15187
+ const name = type === "hiliteColor" ? "hiliteColor" : "foreColor";
15188
+ const btn = this._el && this._el.querySelector(`[data-name="${name}"]`);
15189
+ const strip = btn && btn.querySelector(".an-bubble-color-strip");
15190
+ if (strip) strip.style.background = color === "transparent" ? "transparent" : color;
15191
+ this._closeColorPicker();
15192
+ this._syncActive();
15193
+ }
15030
15194
  _show(rect) {
15031
15195
  if (!this._el) return;
15032
15196
  const el = this._el;
@@ -15043,12 +15207,14 @@ var BubbleToolbar = class {
15043
15207
  el.style.left = `${left}px`;
15044
15208
  el.style.visibility = "";
15045
15209
  this._syncActive();
15210
+ this._syncColorStrips();
15046
15211
  this._visible = true;
15047
15212
  }
15048
15213
  _hide() {
15049
15214
  if (!this._el) return;
15050
15215
  this._el.style.display = "none";
15051
15216
  this._visible = false;
15217
+ this._closeColorPicker();
15052
15218
  }
15053
15219
  _syncActive() {
15054
15220
  if (!this._el) return;
@@ -15057,10 +15223,30 @@ var BubbleToolbar = class {
15057
15223
  btn.classList.toggle("an-active", !!(activeFn && activeFn()));
15058
15224
  });
15059
15225
  }
15226
+ /** Read the current selection's color and update the color-strip indicators. */
15227
+ _syncColorStrips() {
15228
+ if (!this._el) return;
15229
+ const sel = window.getSelection();
15230
+ if (!sel || !sel.rangeCount) return;
15231
+ let node = sel.getRangeAt(0).startContainer;
15232
+ if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
15233
+ if (!node) return;
15234
+ const cs = window.getComputedStyle(node);
15235
+ const foreBtn = this._el.querySelector("[data-name=\"foreColor\"]");
15236
+ const foreStrip = foreBtn && foreBtn.querySelector(".an-bubble-color-strip");
15237
+ if (foreStrip) foreStrip.style.background = cs.color || "#000000";
15238
+ const hiliteBtn = this._el.querySelector("[data-name=\"hiliteColor\"]");
15239
+ const hiliteStrip = hiliteBtn && hiliteBtn.querySelector(".an-bubble-color-strip");
15240
+ if (hiliteStrip) {
15241
+ const bg = cs.backgroundColor;
15242
+ hiliteStrip.style.background = !bg || bg === "rgba(0, 0, 0, 0)" || bg === "transparent" ? "transparent" : bg;
15243
+ }
15244
+ }
15060
15245
  _onSelectionChange() {
15061
15246
  cancelAnimationFrame(this._rafId);
15062
15247
  this._rafId = requestAnimationFrame(() => {
15063
15248
  if (this._contextMenuOpen) return;
15249
+ if (this._picker && this._picker.style.display !== "none") return;
15064
15250
  const sel = window.getSelection();
15065
15251
  if (!sel || sel.isCollapsed || !sel.rangeCount) {
15066
15252
  this._hide();
@@ -15085,11 +15271,15 @@ var BubbleToolbar = class {
15085
15271
  _onMousedown(e) {
15086
15272
  if (!this._visible) return;
15087
15273
  if (this._el && this._el.contains(e.target)) return;
15274
+ if (this._picker && this._picker.contains(e.target)) return;
15088
15275
  if (this.context.layoutInfo.editable.contains(e.target)) return;
15089
15276
  this._hide();
15090
15277
  }
15091
15278
  _onKeydown(e) {
15092
- if (e.key === "Escape" && this._visible) this._hide();
15279
+ if (e.key === "Escape") {
15280
+ if (this._picker && this._picker.style.display !== "none") this._closeColorPicker();
15281
+ else if (this._visible) this._hide();
15282
+ }
15093
15283
  }
15094
15284
  _onContextMenu() {
15095
15285
  this._hide();