@8btc/mditor 0.0.18 → 0.0.19

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.
@@ -64,21 +64,31 @@ class WYSIWYG {
64
64
  divElement.classList.add("vditor--linenumber");
65
65
  }
66
66
 
67
+ // 构建选择浮窗的操作按钮HTML
68
+ const aiButtonHtml = vditor.options.ai?.enable
69
+ ? '<button type="button" data-action="ai" aria-label="AI编辑" class="vditor-selection-popover__btn">✨ AI编辑</button>'
70
+ : '';
71
+
72
+ // 构建AI输入区域HTML
73
+ const aiInputHtml = vditor.options.ai?.enable
74
+ ? `<div class="vditor-selection-popover__input">
75
+ <textarea placeholder="请输入您想修改的内容"></textarea>
76
+ <button class="vditor-selection-popover__send">
77
+ <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" class="lucide lucide-sparkles-icon lucide-sparkles"><path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z"/><path d="M20 2v4"/><path d="M22 4h-4"/><circle cx="4" cy="20" r="2"/></svg>
78
+ </button>
79
+ </div>`
80
+ : '';
81
+
67
82
  divElement.innerHTML = `<pre class="vditor-reset" placeholder="${vditor.options.placeholder}"
68
83
  contenteditable="true" spellcheck="false"></pre>
69
84
  <div class="vditor-panel vditor-panel--none"></div>
70
85
  <div class="vditor-selection-popover">
71
86
  <div class="vditor-selection-popover__actions">
72
- <button type="button" data-action="ai" aria-label="AI编辑" class="vditor-selection-popover__btn">✨ AI编辑</button>
87
+ ${aiButtonHtml}
73
88
  <button type="button" data-action="cut" aria-label="剪切" class="vditor-selection-popover__btn">剪切</button>
74
89
  <button type="button" data-action="copy" aria-label="复制" class="vditor-selection-popover__btn">复制</button>
75
90
  </div>
76
- <div class="vditor-selection-popover__input">
77
- <textarea placeholder="请输入您想修改的内容"></textarea>
78
- <button class="vditor-selection-popover__send">
79
- <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" class="lucide lucide-sparkles-icon lucide-sparkles"><path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z"/><path d="M20 2v4"/><path d="M22 4h-4"/><circle cx="4" cy="20" r="2"/></svg>
80
- </button>
81
- </div>
91
+ ${aiInputHtml}
82
92
  </div>`;
83
93
 
84
94
  this.element = divElement.firstElementChild as HTMLPreElement;
@@ -94,14 +104,14 @@ class WYSIWYG {
94
104
 
95
105
  this.bindEvent(vditor);
96
106
 
97
- // 绑定发送按钮事件
98
- if (this.popoverSendBtn) {
107
+ // 绑定发送按钮事件(仅在AI启用时)
108
+ if (this.popoverSendBtn && vditor.options.ai?.enable) {
99
109
  this.popoverSendBtn.setAttribute("disabled", "disabled");
100
110
 
101
111
  this.popoverSendBtn.onclick = (event) => {
102
112
  event.stopPropagation();
103
- if (vditor.options.ai) {
104
- vditor.options.ai({
113
+ if (vditor.options.ai?.callback) {
114
+ vditor.options.ai.callback({
105
115
  value: this.popoverInput.value,
106
116
  content: this.selectionContent,
107
117
  lines: this.selectionLines,
@@ -109,35 +119,36 @@ class WYSIWYG {
109
119
  }
110
120
  this.hideSelectionPopover();
111
121
  };
112
- // 阻止点击 popover 内任何区域时导致编辑器失去焦点
113
- // 但不影响 textarea 获取焦点和按钮点击
114
- this.selectPopover.addEventListener("mousedown", (event) => {
115
- const target = event.target as HTMLElement;
116
- // 如果点击的是 textarea 或 button,允许默认行为
117
- if (
118
- target.tagName === "TEXTAREA" ||
119
- target.tagName === "BUTTON"
120
- ) {
121
- return;
122
- }
123
- // 其他情况阻止默认行为,防止编辑器失去焦点
124
- event.preventDefault();
125
- event.stopPropagation();
126
- });
122
+ }
127
123
 
128
- // 输入验证
129
- if (this.popoverInput) {
130
- this.popoverInput.addEventListener("input", () => {
131
- if (this.popoverInput.value.trim().length > 0) {
132
- this.popoverSendBtn.removeAttribute("disabled");
133
- } else {
134
- this.popoverSendBtn.setAttribute(
135
- "disabled",
136
- "disabled"
137
- );
138
- }
139
- });
124
+ // 阻止点击 popover 内任何区域时导致编辑器失去焦点
125
+ // 但不影响 textarea 获取焦点和按钮点击
126
+ this.selectPopover.addEventListener("mousedown", (event) => {
127
+ const target = event.target as HTMLElement;
128
+ // 如果点击的是 textarea 或 button,允许默认行为
129
+ if (
130
+ target.tagName === "TEXTAREA" ||
131
+ target.tagName === "BUTTON"
132
+ ) {
133
+ return;
140
134
  }
135
+ // 其他情况阻止默认行为,防止编辑器失去焦点
136
+ event.preventDefault();
137
+ event.stopPropagation();
138
+ });
139
+
140
+ // 输入验证(仅在AI启用时)
141
+ if (this.popoverInput && vditor.options.ai?.enable) {
142
+ this.popoverInput.addEventListener("input", () => {
143
+ if (this.popoverInput.value.trim().length > 0) {
144
+ this.popoverSendBtn.removeAttribute("disabled");
145
+ } else {
146
+ this.popoverSendBtn.setAttribute(
147
+ "disabled",
148
+ "disabled"
149
+ );
150
+ }
151
+ });
141
152
  }
142
153
 
143
154
  focusEvent(vditor, this.element);
@@ -149,42 +160,32 @@ class WYSIWYG {
149
160
  copyEvent(vditor, this.element, this.copy);
150
161
  cutEvent(vditor, this.element, this.copy);
151
162
 
152
- // 选择浮窗按钮事件绑定
153
- // const aiBtn = this.selectPopover.querySelector(
154
- // '[data-action="ai"]'
155
- // ) as HTMLButtonElement | null;
156
- // if (aiBtn) {
157
- // /**
158
- // * AI编辑按钮占位事件
159
- // * - 当前仅输出日志,不执行编辑逻辑
160
- // */
161
- // aiBtn.onclick = () => {
162
- // console.log("[Selection AI Edit] clicked");
163
- // };
164
- // }
165
- const copyBtn = this.selectPopover.querySelector(
166
- '[data-action="copy"]'
167
- ) as HTMLButtonElement | null;
168
- if (copyBtn) {
169
- /**
170
- * 复制按钮点击处理
171
- * - 优先使用 Clipboard API;降级为 execCommand('copy')
172
- */
173
- copyBtn.onclick = () => {
174
- this.copySelection(vditor);
175
- };
176
- }
177
- const cutBtn = this.selectPopover.querySelector(
178
- '[data-action="cut"]'
179
- ) as HTMLButtonElement | null;
180
- if (cutBtn) {
181
- /**
182
- * 剪切按钮点击处理
183
- * - 将选区复制到剪贴板后删除,并接入撤销栈
184
- */
185
- cutBtn.onclick = () => {
186
- this.cutSelection(vditor);
187
- };
163
+ // 选择浮窗按钮事件绑定(仅在AI启用时)
164
+ if (vditor.options.ai?.enable) {
165
+ const copyBtn = this.selectPopover.querySelector(
166
+ '[data-action="copy"]'
167
+ ) as HTMLButtonElement | null;
168
+ if (copyBtn) {
169
+ /**
170
+ * 复制按钮点击处理
171
+ * - 优先使用 Clipboard API;降级为 execCommand('copy')
172
+ */
173
+ copyBtn.onclick = () => {
174
+ this.copySelection(vditor);
175
+ };
176
+ }
177
+ const cutBtn = this.selectPopover.querySelector(
178
+ '[data-action="cut"]'
179
+ ) as HTMLButtonElement | null;
180
+ if (cutBtn) {
181
+ /**
182
+ * 剪切按钮点击处理
183
+ * - 将选区复制到剪贴板后删除,并接入撤销栈
184
+ */
185
+ cutBtn.onclick = () => {
186
+ this.cutSelection(vditor);
187
+ };
188
+ }
188
189
  }
189
190
 
190
191
  // 评论按钮仅在启用时注册(避免选择浮窗结构变化导致报错)
@@ -374,8 +375,14 @@ class WYSIWYG {
374
375
  /**
375
376
  * 显示选择浮窗(定位于选区右上)
376
377
  * - 当未选中文本或选区不在编辑器内时不显示
378
+ * - 当AI功能未启用时不显示
377
379
  */
378
380
  public showSelectionPopover() {
381
+ // 如果AI功能未启用,不显示选择浮窗
382
+ if (!this.vditor.options.ai?.enable) {
383
+ return;
384
+ }
385
+
379
386
  if (getSelection().rangeCount === 0) {
380
387
  return;
381
388
  }