@lab2view/vue-email-editor 0.1.2 → 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 +54 -10
- package/dist/AiChatPanel-CKHjYcuP.js +465 -0
- package/dist/{CodeEditor-CQ4mmQCG.js → CodeEditor-4dfVt7cm.js} +2 -2
- package/dist/{InlineTextEditor-CMQG9tEQ.js → InlineTextEditor-DvzsFt8K.js} +2 -2
- package/dist/email-editor.css +1 -1
- package/dist/email-editor.d.ts +58 -0
- package/dist/email-editor.js +41 -38
- package/dist/{index-Cplw1OsP.js → index-D2Vk6Cbv.js} +14890 -14252
- package/package.json +1 -1
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
|
|  |  |
|
|
31
31
|
|
|
32
|
+
| AI Chat & Preview |
|
|
33
|
+
|:---:|
|
|
34
|
+
|  |
|
|
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
|
-
-
|
|
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
|
|
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
|
|
455
|
+
## AI Template Generation
|
|
445
456
|
|
|
446
|
-
|
|
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
|
|
496
|
+
const res = await fetch('/api/ai/generate', {
|
|
453
497
|
method: 'POST',
|
|
454
498
|
body: JSON.stringify({ prompt, context }),
|
|
455
499
|
})
|
|
456
|
-
return (await
|
|
500
|
+
return (await res.json()).text
|
|
457
501
|
},
|
|
458
502
|
improveText: async (text, instruction) => {
|
|
459
|
-
const
|
|
503
|
+
const res = await fetch('/api/ai/improve', {
|
|
460
504
|
method: 'POST',
|
|
461
505
|
body: JSON.stringify({ text, instruction }),
|
|
462
506
|
})
|
|
463
|
-
return (await
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
491
|
+
//# sourceMappingURL=InlineTextEditor-DvzsFt8K.js.map
|