@lab2view/vue-email-editor 0.1.1 → 0.2.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/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  <p align="center">
17
17
  A professional, extensible drag-and-drop email editor built with <strong>Vue 3</strong> and <strong>MJML</strong>.<br/>
18
- Design responsive HTML emails visually with 43 pre-built blocks, a plugin system, full i18n support, and a complete imperative API.<br/>
18
+ Design responsive HTML emails visually with AI-powered template generation, 43 pre-built blocks, a plugin system, full i18n support, and a complete imperative API.<br/>
19
19
  <strong>Free and open-source alternative to Unlayer, Beefree, and Stripo.</strong>
20
20
  </p>
21
21
 
@@ -29,6 +29,10 @@
29
29
  |:---:|:---:|
30
30
  | ![Image properties and styling](src/assets/capture_blocs_styes_image_1.png) | ![Document tree layers view](src/assets/capture_layers_1.png) |
31
31
 
32
+ | AI Chat & Preview |
33
+ |:---:|
34
+ | ![AI chat panel with template preview](src/assets/capture_ia_chat_preview_1.png) |
35
+
32
36
  ## Features
33
37
 
34
38
  ### Visual Drag & Drop Builder
@@ -75,9 +79,16 @@ Powered by [TipTap](https://tiptap.dev), with inline formatting:
75
79
  - 6 operators: equals, not equals, contains, not contains, exists, not exists
76
80
  - Exports as HTML conditional comments for ESP processing
77
81
 
82
+ ### AI Template Generation
83
+ - Describe an email in plain language and get a complete, production-ready template
84
+ - Built-in AI chat panel with multi-turn conversation for iterative refinement
85
+ - Live HTML preview of the generated template before applying
86
+ - Streaming support for real-time generation feedback
87
+ - Automatic JSON repair for robust parsing of AI responses with auto-retry
88
+ - BYOAI (Bring Your Own AI) — plug in OpenAI, Anthropic, or any LLM
89
+
78
90
  ### AI Text Generation
79
- - BYOAI (Bring Your Own AI) pattern plug in any AI provider
80
- - Generate, improve, shorten, expand, and translate text
91
+ - Inline text generation, improvement, shortening, expansion, and translation
81
92
  - Custom prompt input for freeform generation
82
93
  - Non-blocking async generation with loading state
83
94
 
@@ -423,7 +434,7 @@ import {
423
434
  | `theme` | `Partial<ThemeConfig>` | `DEFAULT_THEME` | Visual customization |
424
435
  | `plugins` | `Plugin[]` | `[]` | Editor extensions |
425
436
  | `mergeTags` | `MergeTag[]` | — | Dynamic variable tags |
426
- | `aiProvider` | `AiProvider` | — | AI text generation callbacks |
437
+ | `aiProvider` | `AiProvider` | — | AI template generation and inline text callbacks |
427
438
  | `onImageUpload` | `(file: File) => Promise<{ url }>` | — | Image upload handler |
428
439
  | `onBrowseAssets` | `() => Promise<string \| null>` | — | Asset browser handler |
429
440
 
@@ -441,26 +452,59 @@ Insert dynamic content with merge tag variables:
441
452
  />
442
453
  ```
443
454
 
444
- ## AI Text Generation
455
+ ## AI Template Generation
445
456
 
446
- Bring your own AI provider for inline text generation:
457
+ Generate complete email templates from natural language prompts. The AI chat panel supports multi-turn conversations — describe your email, preview the result, then refine it through follow-up messages before applying.
447
458
 
448
459
  ```vue
449
460
  <EmailEditor
450
461
  :ai-provider="{
462
+ // Full template generation via chat (enables the AI sidebar tab)
463
+ generateTemplate: async (messages, systemPrompt) => {
464
+ const response = await fetch('/api/ai/chat', {
465
+ method: 'POST',
466
+ body: JSON.stringify({
467
+ messages: [
468
+ { role: 'system', content: systemPrompt },
469
+ ...messages,
470
+ ],
471
+ }),
472
+ })
473
+ return (await response.json()).content
474
+ },
475
+
476
+ // Optional: streaming for real-time generation feedback
477
+ async *generateTemplateStream(messages, systemPrompt) {
478
+ const response = await fetch('/api/ai/chat/stream', {
479
+ method: 'POST',
480
+ body: JSON.stringify({
481
+ messages: [{ role: 'system', content: systemPrompt }, ...messages],
482
+ stream: true,
483
+ }),
484
+ })
485
+ const reader = response.body.getReader()
486
+ const decoder = new TextDecoder()
487
+ while (true) {
488
+ const { done, value } = await reader.read()
489
+ if (done) break
490
+ yield decoder.decode(value)
491
+ }
492
+ },
493
+
494
+ // Inline text generation (generate, improve, shorten, expand, translate)
451
495
  generateText: async (prompt, context) => {
452
- const response = await fetch('/api/ai/generate', {
496
+ const res = await fetch('/api/ai/generate', {
453
497
  method: 'POST',
454
498
  body: JSON.stringify({ prompt, context }),
455
499
  })
456
- return (await response.json()).text
500
+ return (await res.json()).text
457
501
  },
458
502
  improveText: async (text, instruction) => {
459
- const response = await fetch('/api/ai/improve', {
503
+ const res = await fetch('/api/ai/improve', {
460
504
  method: 'POST',
461
505
  body: JSON.stringify({ text, instruction }),
462
506
  })
463
- return (await response.json()).text
507
+ return (await res.json()).text
464
508
  },
465
509
  }"
466
510
  />
@@ -0,0 +1,465 @@
1
+ import { ref as y, defineComponent as te, inject as B, computed as K, watch as J, openBlock as o, createElementBlock as r, createElementVNode as i, normalizeClass as Y, toDisplayString as _, unref as l, createCommentVNode as k, createVNode as v, createTextVNode as A, withDirectives as V, Fragment as I, renderList as R, vShow as ae, vModelText as se, nextTick as ne } from "vue";
2
+ import { p as W, A as H, c as ie, d as le, e as ce, a as oe, D as re, E as ue, b as _e, _ as p } from "./index-D2Vk6Cbv.js";
3
+ function de(w) {
4
+ const c = w.trim();
5
+ return !!(c.startsWith("{") || /```(?:json)?\s*\n?\s*\{/.test(c) || c.includes('"version"') && c.includes('"body"') && c.includes('"mj-body"') || c.includes('"headAttributes"') && c.includes('"mj-body"'));
6
+ }
7
+ function X(w) {
8
+ return w.body.children.length > 0;
9
+ }
10
+ function he(w, c, N, f) {
11
+ const n = y([]), T = y(!1), g = y(null), d = y(null), h = y(""), m = y("");
12
+ let b = "", S;
13
+ function C() {
14
+ return ie({
15
+ mergeTags: f == null ? void 0 : f.mergeTags,
16
+ promptPrefix: f == null ? void 0 : f.promptPrefix,
17
+ promptSuffix: f == null ? void 0 : f.promptSuffix
18
+ });
19
+ }
20
+ function P() {
21
+ const a = [...n.value], e = d.value ?? c.value;
22
+ if (X(e)) {
23
+ const t = JSON.stringify(e, null, 2), u = {
24
+ role: "user",
25
+ content: `[${d.value ? "CURRENT_PREVIEW_TEMPLATE_JSON — This is the template you just generated that is being previewed. The user wants to refine THIS template. Make ONLY the requested changes and return the complete updated JSON." : "CURRENT_TEMPLATE_JSON — This is the template currently loaded in the editor. When I ask for changes, modify this and return the complete updated JSON. When I ask for a completely new template, ignore this and create from scratch."}]
26
+
27
+ ${t}`
28
+ };
29
+ a.splice(a.length - 1, 0, u);
30
+ }
31
+ return a;
32
+ }
33
+ function $(a) {
34
+ const e = a.body.children.length, t = x(a.body, "mj-image"), s = x(a.body, "mj-button"), u = [`${e} section${e !== 1 ? "s" : ""}`];
35
+ return t > 0 && u.push(`${t} image${t !== 1 ? "s" : ""}`), s > 0 && u.push(`${s} button${s !== 1 ? "s" : ""}`), u.join(", ");
36
+ }
37
+ function x(a, e) {
38
+ let t = a.type === e ? 1 : 0;
39
+ if (a.children)
40
+ for (const s of a.children)
41
+ t += x(s, e);
42
+ return t;
43
+ }
44
+ async function z(a, e) {
45
+ if (w.generateTemplateStream) {
46
+ let t = "";
47
+ for await (const s of w.generateTemplateStream(a, e))
48
+ t += s, m.value = t;
49
+ return t;
50
+ } else
51
+ return await w.generateTemplate(a, e);
52
+ }
53
+ async function E(a, e) {
54
+ d.value = a;
55
+ const t = le(a), s = await ce(t);
56
+ h.value = s.html;
57
+ const u = $(a);
58
+ n.value.push({
59
+ role: "assistant",
60
+ content: e ? `Template updated (${u}). Review the changes and apply or discard.` : `Template created (${u}). Apply it to load it in the editor.`
61
+ });
62
+ }
63
+ async function L(a, e) {
64
+ if (!w.generateTemplate && !w.generateTemplateStream) return;
65
+ b = a, S = e, g.value = null, m.value = "";
66
+ const t = { role: "user", content: a };
67
+ e && e.length > 0 && (t.attachments = e), n.value.push(t), T.value = !0;
68
+ try {
69
+ const s = C(), u = !!d.value || X(c.value), M = P();
70
+ d.value = null;
71
+ const U = await z(M, s);
72
+ if (de(U))
73
+ try {
74
+ const D = W(U);
75
+ await E(D, u);
76
+ } catch (D) {
77
+ if (D instanceof H) {
78
+ m.value = "";
79
+ const q = {
80
+ role: "user",
81
+ content: "Your previous response could not be parsed. Please re-send ONLY the raw JSON EmailDocument, starting with { and ending with }. No text, no code fences, no markdown — just the JSON object."
82
+ };
83
+ n.value.push(q);
84
+ const Z = P(), Q = await z(Z, s);
85
+ n.value.pop();
86
+ const ee = W(Q);
87
+ await E(ee, u);
88
+ } else
89
+ throw D;
90
+ }
91
+ else
92
+ n.value.push({
93
+ role: "assistant",
94
+ content: U.trim()
95
+ });
96
+ } catch (s) {
97
+ s instanceof H || s instanceof Error ? g.value = s.message : g.value = "An unexpected error occurred", n.value.pop();
98
+ } finally {
99
+ T.value = !1, m.value = "";
100
+ }
101
+ }
102
+ function O() {
103
+ d.value && (N(d.value), d.value = null, h.value = "");
104
+ }
105
+ function F() {
106
+ d.value = null, h.value = "";
107
+ }
108
+ function G() {
109
+ n.value = [], g.value = null, d.value = null, h.value = "", m.value = "", b = "";
110
+ }
111
+ async function j() {
112
+ b && (g.value = null, await L(b, S));
113
+ }
114
+ return {
115
+ messages: n,
116
+ isGenerating: T,
117
+ error: g,
118
+ lastGeneratedTemplate: d,
119
+ previewHtml: h,
120
+ streamBuffer: m,
121
+ sendMessage: L,
122
+ applyTemplate: O,
123
+ discardTemplate: F,
124
+ clearConversation: G,
125
+ retry: j
126
+ };
127
+ }
128
+ const me = { class: "ebb-ai-chat" }, be = {
129
+ key: 0,
130
+ class: "ebb-ai-chat__tabs"
131
+ }, ve = {
132
+ key: 1,
133
+ class: "ebb-ai-chat__preview"
134
+ }, pe = ["srcdoc", "title"], fe = { class: "ebb-ai-chat__actions" }, ge = {
135
+ key: 0,
136
+ class: "ebb-ai-chat__welcome"
137
+ }, ye = { class: "ebb-ai-chat__welcome-icon" }, we = { class: "ebb-ai-chat__welcome-title" }, Te = { class: "ebb-ai-chat__welcome-hint" }, ke = { class: "ebb-ai-chat__suggestions" }, Ce = ["onClick"], Ee = {
138
+ key: 0,
139
+ class: "ebb-ai-chat__attachments"
140
+ }, Ae = ["src", "alt"], Se = {
141
+ key: 1,
142
+ class: "ebb-ai-chat__attachment-file"
143
+ }, $e = { class: "ebb-ai-chat__msg-text" }, xe = {
144
+ key: 1,
145
+ class: "ebb-ai-chat__actions"
146
+ }, Me = {
147
+ key: 2,
148
+ class: "ebb-ai-chat__thinking"
149
+ }, Ne = {
150
+ key: 3,
151
+ class: "ebb-ai-chat__error"
152
+ }, Pe = { class: "ebb-ai-chat__error-text" }, ze = { class: "ebb-ai-chat__input-area" }, Le = {
153
+ key: 0,
154
+ class: "ebb-ai-chat__pending"
155
+ }, je = ["src", "alt"], De = {
156
+ key: 1,
157
+ class: "ebb-ai-chat__pending-file"
158
+ }, Ie = { class: "ebb-ai-chat__pending-name" }, Re = ["onClick"], Oe = {
159
+ key: 1,
160
+ class: "ebb-ai-chat__attach-error"
161
+ }, Fe = { class: "ebb-ai-chat__input-row" }, Ge = ["placeholder", "disabled"], Ue = { class: "ebb-ai-chat__input-buttons" }, Be = ["title", "disabled"], Je = ["disabled"], Ye = {
162
+ key: 2,
163
+ class: "ebb-ai-chat__footer"
164
+ }, Ke = 10 * 1024 * 1024, Ve = "image/png,image/jpeg,image/gif,image/webp,image/svg+xml,application/pdf", Xe = /* @__PURE__ */ te({
165
+ __name: "AiChatPanel",
166
+ setup(w) {
167
+ const c = B(oe, re), N = B(ue), f = B(_e), n = he(f.aiProvider, N.document, N.replaceDocument, {
168
+ mergeTags: f.mergeTags
169
+ }), T = y(""), g = y(), d = y(), h = y([]), m = y(null), b = y("chat"), S = K(() => n.messages.value.length > 0), C = K(() => !!n.lastGeneratedTemplate.value), P = [
170
+ "Create a professional newsletter",
171
+ "Design a welcome email for a SaaS product",
172
+ "Promotional email with 30% discount",
173
+ "Event invitation with modern design"
174
+ ];
175
+ async function $() {
176
+ const a = T.value.trim();
177
+ if (!a || n.isGenerating.value) return;
178
+ const e = h.value.length > 0 ? [...h.value] : void 0;
179
+ T.value = "", h.value = [], m.value = null, await n.sendMessage(a, e), E();
180
+ }
181
+ function x(a) {
182
+ T.value = a, $();
183
+ }
184
+ function z(a) {
185
+ a.key === "Enter" && !a.shiftKey && (a.preventDefault(), $());
186
+ }
187
+ function E() {
188
+ ne(() => {
189
+ g.value && (g.value.scrollTop = g.value.scrollHeight);
190
+ });
191
+ }
192
+ function L() {
193
+ var a;
194
+ (a = d.value) == null || a.click();
195
+ }
196
+ async function O(a) {
197
+ const e = a.target, t = e.files;
198
+ if (!(!t || t.length === 0)) {
199
+ m.value = null;
200
+ for (const s of Array.from(t)) {
201
+ if (s.size > Ke) {
202
+ m.value = c.ai_chat_file_too_large;
203
+ continue;
204
+ }
205
+ try {
206
+ const u = await F(s);
207
+ h.value.push({
208
+ mimeType: s.type,
209
+ data: u,
210
+ name: s.name
211
+ });
212
+ } catch {
213
+ m.value = "Failed to read file";
214
+ }
215
+ }
216
+ e.value = "";
217
+ }
218
+ }
219
+ function F(a) {
220
+ return new Promise((e, t) => {
221
+ const s = new FileReader();
222
+ s.onload = () => {
223
+ const M = s.result.split(",")[1];
224
+ e(M);
225
+ }, s.onerror = t, s.readAsDataURL(a);
226
+ });
227
+ }
228
+ function G(a) {
229
+ h.value.splice(a, 1);
230
+ }
231
+ function j(a) {
232
+ return a.mimeType.startsWith("image/") ? `data:${a.mimeType};base64,${a.data}` : null;
233
+ }
234
+ return J(() => n.messages.value.length, E), J(() => n.streamBuffer.value, E), J(() => n.lastGeneratedTemplate.value, (a) => {
235
+ b.value = a ? "preview" : "chat";
236
+ }), (a, e) => (o(), r("div", me, [
237
+ C.value ? (o(), r("div", be, [
238
+ i("button", {
239
+ class: Y(["ebb-ai-chat__tab", { "ebb-ai-chat__tab--active": b.value === "chat" }]),
240
+ onClick: e[0] || (e[0] = (t) => b.value = "chat")
241
+ }, _(l(c).ai_chat), 3),
242
+ i("button", {
243
+ class: Y(["ebb-ai-chat__tab", { "ebb-ai-chat__tab--active": b.value === "preview" }]),
244
+ onClick: e[1] || (e[1] = (t) => b.value = "preview")
245
+ }, _(l(c).ai_chat_preview), 3)
246
+ ])) : k("", !0),
247
+ C.value && b.value === "preview" ? (o(), r("div", ve, [
248
+ i("iframe", {
249
+ class: "ebb-ai-chat__preview-iframe",
250
+ srcdoc: l(n).previewHtml.value,
251
+ sandbox: "allow-same-origin",
252
+ title: l(c).ai_chat_preview_hint
253
+ }, null, 8, pe),
254
+ i("div", fe, [
255
+ i("button", {
256
+ class: "ebb-ai-chat__action-apply",
257
+ onClick: e[2] || (e[2] = (t) => l(n).applyTemplate())
258
+ }, [
259
+ v(p, {
260
+ name: "Check",
261
+ size: 13
262
+ }),
263
+ A(" " + _(l(c).ai_chat_apply), 1)
264
+ ]),
265
+ e[9] || (e[9] = i("span", { class: "ebb-ai-chat__actions-sep" }, null, -1)),
266
+ i("button", {
267
+ class: "ebb-ai-chat__action-discard",
268
+ onClick: e[3] || (e[3] = (t) => l(n).discardTemplate())
269
+ }, [
270
+ v(p, {
271
+ name: "X",
272
+ size: 13
273
+ }),
274
+ A(" " + _(l(c).ai_chat_discard), 1)
275
+ ])
276
+ ])
277
+ ])) : k("", !0),
278
+ V(i("div", {
279
+ ref_key: "messagesContainer",
280
+ ref: g,
281
+ class: "ebb-ai-chat__messages"
282
+ }, [
283
+ !S.value && !l(n).isGenerating.value ? (o(), r("div", ge, [
284
+ i("div", ye, [
285
+ v(p, {
286
+ name: "Sparkles",
287
+ size: 20
288
+ })
289
+ ]),
290
+ i("p", we, _(l(c).ai_chat_welcome), 1),
291
+ i("p", Te, _(l(c).ai_chat_welcome_hint), 1),
292
+ i("div", ke, [
293
+ (o(), r(I, null, R(P, (t) => i("button", {
294
+ key: t,
295
+ class: "ebb-ai-chat__suggestion",
296
+ onClick: (s) => x(t)
297
+ }, _(t), 9, Ce)), 64))
298
+ ])
299
+ ])) : k("", !0),
300
+ (o(!0), r(I, null, R(l(n).messages.value, (t, s) => (o(), r("div", {
301
+ key: s,
302
+ class: Y(["ebb-ai-chat__msg", t.role === "user" ? "ebb-ai-chat__msg--user" : "ebb-ai-chat__msg--assistant"])
303
+ }, [
304
+ t.attachments && t.attachments.length > 0 ? (o(), r("div", Ee, [
305
+ (o(!0), r(I, null, R(t.attachments, (u, M) => (o(), r("div", {
306
+ key: M,
307
+ class: "ebb-ai-chat__attachment-thumb"
308
+ }, [
309
+ u.mimeType.startsWith("image/") ? (o(), r("img", {
310
+ key: 0,
311
+ src: `data:${u.mimeType};base64,${u.data}`,
312
+ alt: u.name || "Attachment",
313
+ class: "ebb-ai-chat__attachment-img"
314
+ }, null, 8, Ae)) : (o(), r("div", Se, [
315
+ v(p, {
316
+ name: "FileText",
317
+ size: 14
318
+ }),
319
+ i("span", null, _(u.name || "Document"), 1)
320
+ ]))
321
+ ]))), 128))
322
+ ])) : k("", !0),
323
+ i("span", $e, _(t.content), 1)
324
+ ], 2))), 128)),
325
+ C.value && b.value === "chat" ? (o(), r("div", xe, [
326
+ i("button", {
327
+ class: "ebb-ai-chat__action-apply",
328
+ onClick: e[4] || (e[4] = (t) => l(n).applyTemplate())
329
+ }, [
330
+ v(p, {
331
+ name: "Check",
332
+ size: 13
333
+ }),
334
+ A(" " + _(l(c).ai_chat_apply), 1)
335
+ ]),
336
+ e[10] || (e[10] = i("span", { class: "ebb-ai-chat__actions-sep" }, null, -1)),
337
+ i("button", {
338
+ class: "ebb-ai-chat__action-discard",
339
+ onClick: e[5] || (e[5] = (t) => l(n).discardTemplate())
340
+ }, [
341
+ v(p, {
342
+ name: "X",
343
+ size: 13
344
+ }),
345
+ A(" " + _(l(c).ai_chat_discard), 1)
346
+ ])
347
+ ])) : k("", !0),
348
+ l(n).isGenerating.value ? (o(), r("div", Me, [
349
+ e[11] || (e[11] = i("span", { class: "ebb-ai-chat__spinner" }, null, -1)),
350
+ i("span", null, _(l(c).ai_chat_thinking), 1)
351
+ ])) : k("", !0),
352
+ l(n).error.value ? (o(), r("div", Ne, [
353
+ v(p, {
354
+ name: "AlertCircle",
355
+ size: 14
356
+ }),
357
+ i("span", Pe, _(l(n).error.value), 1),
358
+ i("button", {
359
+ class: "ebb-ai-chat__error-retry",
360
+ onClick: e[6] || (e[6] = (t) => l(n).retry())
361
+ }, [
362
+ v(p, {
363
+ name: "RotateCcw",
364
+ size: 12
365
+ }),
366
+ A(" " + _(l(c).ai_chat_retry), 1)
367
+ ])
368
+ ])) : k("", !0)
369
+ ], 512), [
370
+ [ae, !C.value || b.value === "chat"]
371
+ ]),
372
+ i("div", ze, [
373
+ h.value.length > 0 ? (o(), r("div", Le, [
374
+ (o(!0), r(I, null, R(h.value, (t, s) => (o(), r("div", {
375
+ key: s,
376
+ class: "ebb-ai-chat__pending-item"
377
+ }, [
378
+ j(t) ? (o(), r("img", {
379
+ key: 0,
380
+ src: j(t),
381
+ alt: t.name || "Preview",
382
+ class: "ebb-ai-chat__pending-img"
383
+ }, null, 8, je)) : (o(), r("div", De, [
384
+ v(p, {
385
+ name: "FileText",
386
+ size: 14
387
+ })
388
+ ])),
389
+ i("span", Ie, _(t.name), 1),
390
+ i("button", {
391
+ class: "ebb-ai-chat__pending-remove",
392
+ onClick: (u) => G(s)
393
+ }, [
394
+ v(p, {
395
+ name: "X",
396
+ size: 10
397
+ })
398
+ ], 8, Re)
399
+ ]))), 128))
400
+ ])) : k("", !0),
401
+ m.value ? (o(), r("div", Oe, _(m.value), 1)) : k("", !0),
402
+ i("div", Fe, [
403
+ V(i("textarea", {
404
+ "onUpdate:modelValue": e[7] || (e[7] = (t) => T.value = t),
405
+ class: "ebb-ai-chat__textarea",
406
+ placeholder: l(c).ai_chat_placeholder,
407
+ rows: "1",
408
+ disabled: l(n).isGenerating.value,
409
+ onKeydown: z
410
+ }, null, 40, Ge), [
411
+ [se, T.value]
412
+ ]),
413
+ i("div", Ue, [
414
+ i("button", {
415
+ class: "ebb-ai-chat__icon-btn",
416
+ title: l(c).ai_chat_attach,
417
+ disabled: l(n).isGenerating.value,
418
+ onClick: L
419
+ }, [
420
+ v(p, {
421
+ name: "ImagePlus",
422
+ size: 14
423
+ })
424
+ ], 8, Be),
425
+ i("button", {
426
+ class: "ebb-ai-chat__icon-btn ebb-ai-chat__icon-btn--send",
427
+ disabled: !T.value.trim() || l(n).isGenerating.value,
428
+ onClick: $
429
+ }, [
430
+ v(p, {
431
+ name: "ArrowUp",
432
+ size: 14
433
+ })
434
+ ], 8, Je)
435
+ ])
436
+ ]),
437
+ S.value ? (o(), r("div", Ye, [
438
+ i("button", {
439
+ class: "ebb-ai-chat__new-btn",
440
+ onClick: e[8] || (e[8] = (t) => l(n).clearConversation())
441
+ }, [
442
+ v(p, {
443
+ name: "Plus",
444
+ size: 12
445
+ }),
446
+ A(" " + _(l(c).ai_chat_new), 1)
447
+ ])
448
+ ])) : k("", !0),
449
+ i("input", {
450
+ ref_key: "fileInput",
451
+ ref: d,
452
+ type: "file",
453
+ accept: Ve,
454
+ multiple: "",
455
+ style: { display: "none" },
456
+ onChange: O
457
+ }, null, 544)
458
+ ])
459
+ ]));
460
+ }
461
+ });
462
+ export {
463
+ Xe as default
464
+ };
465
+ //# sourceMappingURL=AiChatPanel-CKHjYcuP.js.map
@@ -1,6 +1,6 @@
1
1
  import { defineComponent as u, inject as f, ref as p, watch as h, onMounted as _, onBeforeUnmount as g, openBlock as E, createElementBlock as M } from "vue";
2
2
  import { E as j, o as x, l as C, h as k, k as v, d as y, i as w, x as D, a as c } from "./codemirror-CWpT3Qh8.js";
3
- import { E as S, m as b, d as B } from "./index-CT-stdGQ.js";
3
+ import { E as S, m as b, d as B } from "./index-D2Vk6Cbv.js";
4
4
  const A = /* @__PURE__ */ u({
5
5
  __name: "CodeEditor",
6
6
  setup(T) {
@@ -66,4 +66,4 @@ const A = /* @__PURE__ */ u({
66
66
  export {
67
67
  A as default
68
68
  };
69
- //# sourceMappingURL=CodeEditor-DMzmmIKl.js.map
69
+ //# sourceMappingURL=CodeEditor-4dfVt7cm.js.map
@@ -1,6 +1,6 @@
1
1
  import { defineComponent as F, inject as S, ref as A, computed as R, openBlock as c, createElementBlock as g, withModifiers as $, createElementVNode as a, normalizeClass as p, createVNode as r, createCommentVNode as h, Fragment as w, renderList as N, toDisplayString as y, createTextVNode as M, withDirectives as Y, withKeys as j, vModelText as G, markRaw as W, watch as q, nextTick as J, onBeforeUnmount as Q, normalizeStyle as V, unref as E, createBlock as X } from "vue";
2
2
  import { T as Z, i as ee, a as te, c as ne, d as ie, e as ae, u as le, E as re } from "./tiptap-gdUcLDLI.js";
3
- import { a as oe, D as se, b as O, _ as o, M as be } from "./index-CT-stdGQ.js";
3
+ import { a as oe, D as se, b as O, _ as o, M as be } from "./index-D2Vk6Cbv.js";
4
4
  const ue = ["aria-pressed", "title", "aria-label"], de = ["aria-pressed", "title", "aria-label"], ce = ["aria-pressed", "title", "aria-label"], me = ["aria-pressed", "title", "aria-label"], ge = ["title", "aria-label"], _e = ["title", "aria-label"], ve = ["aria-pressed", "title", "aria-label"], fe = ["aria-pressed", "title", "aria-label"], pe = ["aria-pressed", "title", "aria-label"], ke = ["title"], xe = ["aria-label"], he = { class: "ebb-inline-toolbar__merge-wrap" }, ye = ["title", "aria-label", "aria-expanded"], Te = {
5
5
  key: 0,
6
6
  class: "ebb-merge-menu__category"
@@ -488,4 +488,4 @@ Text: ` + e), u && t.editor) {
488
488
  export {
489
489
  Re as default
490
490
  };
491
- //# sourceMappingURL=InlineTextEditor-CrX1sdh1.js.map
491
+ //# sourceMappingURL=InlineTextEditor-DvzsFt8K.js.map