@fonderie/vue-courier-admin-screens 0.5.0 → 0.7.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/dist/index.cjs +515 -120
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +112 -2
- package/dist/index.d.ts +112 -2
- package/dist/index.js +517 -123
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -20,24 +20,36 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
TemplateCreateScreen: () => TemplateCreateScreen,
|
|
23
24
|
TemplateEditorScreen: () => TemplateEditorScreen,
|
|
24
25
|
TemplateListScreen: () => TemplateListScreen
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(index_exports);
|
|
27
28
|
|
|
28
|
-
// src/screens/
|
|
29
|
-
var
|
|
29
|
+
// src/screens/TemplateCreateScreen.ts
|
|
30
|
+
var import_client = require("@fonderie/client");
|
|
30
31
|
var import_vue = require("vue");
|
|
31
32
|
|
|
32
33
|
// src/styles.ts
|
|
33
34
|
var styles = {
|
|
34
|
-
container: { padding: "
|
|
35
|
+
container: { padding: "8px 40px 64px", maxWidth: "1400px", boxSizing: "border-box" },
|
|
35
36
|
// Two columns, always: editor left, rendered result right. Stacking the
|
|
36
37
|
// preview under a long form hid it below the fold.
|
|
37
|
-
split: {
|
|
38
|
+
split: {
|
|
39
|
+
display: "grid",
|
|
40
|
+
gridTemplateColumns: "minmax(0, 1fr) minmax(0, 1fr)",
|
|
41
|
+
gap: "24px",
|
|
42
|
+
alignItems: "start"
|
|
43
|
+
},
|
|
38
44
|
column: { minWidth: "0", display: "flex", flexDirection: "column" },
|
|
39
45
|
// Pinned while the form scrolls, so the preview stays in view as you edit.
|
|
40
|
-
previewColumn: {
|
|
46
|
+
previewColumn: {
|
|
47
|
+
minWidth: "0",
|
|
48
|
+
display: "flex",
|
|
49
|
+
flexDirection: "column",
|
|
50
|
+
position: "sticky",
|
|
51
|
+
top: "16px"
|
|
52
|
+
},
|
|
41
53
|
saveRow: { display: "flex", alignItems: "center", gap: "12px", marginTop: "16px" },
|
|
42
54
|
previewHeader: {
|
|
43
55
|
display: "flex",
|
|
@@ -51,7 +63,8 @@ var styles = {
|
|
|
51
63
|
height: "520px",
|
|
52
64
|
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
53
65
|
borderRadius: "8px",
|
|
54
|
-
background: "var(--fonderie-surface,#fff)"
|
|
66
|
+
background: "var(--fonderie-surface,#fff)",
|
|
67
|
+
boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))"
|
|
55
68
|
},
|
|
56
69
|
previewText: {
|
|
57
70
|
whiteSpace: "pre-wrap",
|
|
@@ -61,36 +74,117 @@ var styles = {
|
|
|
61
74
|
fontSize: "13px",
|
|
62
75
|
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)'
|
|
63
76
|
},
|
|
64
|
-
title: {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
title: {
|
|
78
|
+
fontSize: "22px",
|
|
79
|
+
fontWeight: 600,
|
|
80
|
+
margin: "8px 0 4px",
|
|
81
|
+
letterSpacing: "var(--fonderie-tracking-display,-0.05em)",
|
|
82
|
+
lineHeight: 1.25
|
|
83
|
+
},
|
|
84
|
+
subtitle: {
|
|
85
|
+
fontSize: "15px",
|
|
86
|
+
fontWeight: 600,
|
|
87
|
+
margin: "0 0 10px",
|
|
88
|
+
letterSpacing: "var(--fonderie-tracking-display,-0.05em)"
|
|
89
|
+
},
|
|
90
|
+
meta: { fontSize: "13px", color: "var(--fonderie-text-muted,#5c5c5c)", marginBottom: "20px" },
|
|
91
|
+
status: { padding: "24px 0", color: "var(--fonderie-text-muted,#5c5c5c)", fontSize: "13.5px" },
|
|
92
|
+
error: {
|
|
93
|
+
color: "var(--fonderie-danger,#e00)",
|
|
94
|
+
background: "color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)",
|
|
95
|
+
borderRadius: "6px",
|
|
96
|
+
padding: "8px 12px",
|
|
97
|
+
margin: "8px 0",
|
|
98
|
+
fontSize: "13.5px"
|
|
99
|
+
},
|
|
100
|
+
list: {
|
|
101
|
+
listStyle: "none",
|
|
102
|
+
padding: "0",
|
|
103
|
+
margin: "0",
|
|
104
|
+
background: "var(--fonderie-surface,#fff)",
|
|
105
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
106
|
+
borderRadius: "var(--fonderie-radius-lg,8px)",
|
|
107
|
+
overflow: "hidden",
|
|
108
|
+
boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))"
|
|
109
|
+
},
|
|
70
110
|
row: { borderBottom: "1px solid var(--fonderie-border-light,#f5f5f5)" },
|
|
71
111
|
rowButton: {
|
|
72
112
|
width: "100%",
|
|
73
113
|
display: "flex",
|
|
74
114
|
justifyContent: "space-between",
|
|
75
115
|
alignItems: "center",
|
|
76
|
-
|
|
116
|
+
gap: "16px",
|
|
117
|
+
padding: "13px 16px",
|
|
77
118
|
background: "none",
|
|
78
119
|
border: "none",
|
|
79
120
|
cursor: "pointer",
|
|
80
|
-
textAlign: "left"
|
|
121
|
+
textAlign: "left",
|
|
122
|
+
color: "var(--fonderie-text,#171717)",
|
|
123
|
+
fontFamily: "inherit"
|
|
124
|
+
},
|
|
125
|
+
type: {
|
|
126
|
+
fontSize: "13px",
|
|
127
|
+
fontWeight: 600,
|
|
128
|
+
flex: "1",
|
|
129
|
+
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)'
|
|
130
|
+
},
|
|
131
|
+
locale: { fontSize: "12.5px", color: "var(--fonderie-text-muted,#5c5c5c)" },
|
|
132
|
+
active: {
|
|
133
|
+
display: "inline-flex",
|
|
134
|
+
alignItems: "center",
|
|
135
|
+
gap: "6px",
|
|
136
|
+
borderRadius: "999px",
|
|
137
|
+
padding: "2px 9px 2px 8px",
|
|
138
|
+
fontSize: "12px",
|
|
139
|
+
fontWeight: 600,
|
|
140
|
+
lineHeight: 1.5,
|
|
141
|
+
minWidth: "64px",
|
|
142
|
+
color: "var(--fonderie-accent-strong,#009767)",
|
|
143
|
+
background: "color-mix(in srgb, var(--fonderie-accent-strong,#009767) 13%, transparent)"
|
|
144
|
+
},
|
|
145
|
+
inactive: {
|
|
146
|
+
display: "inline-flex",
|
|
147
|
+
alignItems: "center",
|
|
148
|
+
gap: "6px",
|
|
149
|
+
borderRadius: "999px",
|
|
150
|
+
padding: "2px 9px 2px 8px",
|
|
151
|
+
fontSize: "12px",
|
|
152
|
+
fontWeight: 600,
|
|
153
|
+
lineHeight: 1.5,
|
|
154
|
+
minWidth: "64px",
|
|
155
|
+
color: "var(--fonderie-text-muted,#5c5c5c)",
|
|
156
|
+
background: "color-mix(in srgb, var(--fonderie-text-muted,#5c5c5c) 10%, transparent)"
|
|
157
|
+
},
|
|
158
|
+
form: {
|
|
159
|
+
display: "flex",
|
|
160
|
+
flexDirection: "column",
|
|
161
|
+
gap: "4px",
|
|
162
|
+
background: "var(--fonderie-surface,#fff)",
|
|
163
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
164
|
+
borderRadius: "var(--fonderie-radius-lg,8px)",
|
|
165
|
+
padding: "6px 20px 20px",
|
|
166
|
+
boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))"
|
|
167
|
+
},
|
|
168
|
+
label: { fontSize: "13px", fontWeight: 500, marginTop: "14px", marginBottom: "2px" },
|
|
169
|
+
input: {
|
|
170
|
+
height: "36px",
|
|
171
|
+
boxSizing: "border-box",
|
|
172
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
173
|
+
borderRadius: "6px",
|
|
174
|
+
padding: "0 10px",
|
|
175
|
+
fontSize: "13.5px",
|
|
176
|
+
fontFamily: "inherit",
|
|
177
|
+
background: "var(--fonderie-surface,#fff)",
|
|
178
|
+
color: "var(--fonderie-text,#171717)"
|
|
81
179
|
},
|
|
82
|
-
type: { fontSize: "14px", fontWeight: 600, flex: "1" },
|
|
83
|
-
locale: { fontSize: "13px", color: "var(--fonderie-text-muted,#5c5c5c)", marginRight: "16px" },
|
|
84
|
-
active: { fontSize: "12px", color: "var(--fonderie-accent-strong,#009767)" },
|
|
85
|
-
inactive: { fontSize: "12px", color: "var(--fonderie-text-muted,#5c5c5c)" },
|
|
86
|
-
form: { display: "flex", flexDirection: "column", gap: "4px" },
|
|
87
|
-
label: { fontSize: "13px", fontWeight: 600, marginTop: "12px" },
|
|
88
|
-
input: { border: "1px solid var(--fonderie-border,#e0e0e0)", borderRadius: "8px", padding: "10px", fontSize: "14px" },
|
|
89
180
|
textarea: {
|
|
90
181
|
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
91
|
-
borderRadius: "
|
|
182
|
+
borderRadius: "6px",
|
|
92
183
|
padding: "10px",
|
|
93
|
-
fontSize: "
|
|
184
|
+
fontSize: "13px",
|
|
185
|
+
lineHeight: 1.55,
|
|
186
|
+
background: "var(--fonderie-surface,#fff)",
|
|
187
|
+
color: "var(--fonderie-text,#171717)",
|
|
94
188
|
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)'
|
|
95
189
|
},
|
|
96
190
|
checkboxLabel: {
|
|
@@ -101,58 +195,241 @@ var styles = {
|
|
|
101
195
|
fontSize: "14px"
|
|
102
196
|
},
|
|
103
197
|
button: {
|
|
198
|
+
display: "inline-flex",
|
|
199
|
+
alignItems: "center",
|
|
200
|
+
height: "36px",
|
|
201
|
+
boxSizing: "border-box",
|
|
104
202
|
backgroundColor: "var(--fonderie-text,#171717)",
|
|
105
203
|
color: "var(--fonderie-surface,#fff)",
|
|
106
|
-
padding: "
|
|
107
|
-
borderRadius: "
|
|
108
|
-
border: "
|
|
109
|
-
fontSize: "
|
|
204
|
+
padding: "0 16px",
|
|
205
|
+
borderRadius: "6px",
|
|
206
|
+
border: "1px solid var(--fonderie-text,#171717)",
|
|
207
|
+
fontSize: "13.5px",
|
|
110
208
|
fontWeight: 600,
|
|
111
|
-
|
|
112
|
-
alignSelf: "flex-start"
|
|
209
|
+
fontFamily: "inherit",
|
|
210
|
+
alignSelf: "flex-start",
|
|
211
|
+
cursor: "pointer"
|
|
113
212
|
},
|
|
114
213
|
buttonDisabled: {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
214
|
+
display: "inline-flex",
|
|
215
|
+
alignItems: "center",
|
|
216
|
+
height: "36px",
|
|
217
|
+
boxSizing: "border-box",
|
|
218
|
+
backgroundColor: "var(--fonderie-text,#171717)",
|
|
219
|
+
color: "var(--fonderie-surface,#fff)",
|
|
220
|
+
padding: "0 16px",
|
|
221
|
+
borderRadius: "6px",
|
|
222
|
+
border: "1px solid var(--fonderie-text,#171717)",
|
|
223
|
+
fontSize: "13.5px",
|
|
121
224
|
fontWeight: 600,
|
|
122
|
-
|
|
123
|
-
alignSelf: "flex-start"
|
|
225
|
+
fontFamily: "inherit",
|
|
226
|
+
alignSelf: "flex-start",
|
|
227
|
+
opacity: 0.45,
|
|
228
|
+
cursor: "not-allowed"
|
|
124
229
|
},
|
|
125
230
|
revisions: { marginTop: "32px" },
|
|
126
231
|
revisionRow: {
|
|
127
232
|
display: "flex",
|
|
128
233
|
justifyContent: "space-between",
|
|
129
234
|
alignItems: "center",
|
|
130
|
-
|
|
235
|
+
gap: "12px",
|
|
236
|
+
padding: "9px 16px",
|
|
131
237
|
borderBottom: "1px solid var(--fonderie-border-light,#f5f5f5)",
|
|
132
238
|
fontSize: "13px"
|
|
133
239
|
},
|
|
134
240
|
rollbackButton: {
|
|
135
|
-
|
|
241
|
+
display: "inline-flex",
|
|
242
|
+
alignItems: "center",
|
|
243
|
+
height: "28px",
|
|
244
|
+
boxSizing: "border-box",
|
|
245
|
+
background: "var(--fonderie-surface,#fff)",
|
|
246
|
+
color: "var(--fonderie-text,#171717)",
|
|
136
247
|
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
137
|
-
borderRadius: "
|
|
138
|
-
padding: "
|
|
139
|
-
fontSize: "
|
|
248
|
+
borderRadius: "6px",
|
|
249
|
+
padding: "0 10px",
|
|
250
|
+
fontSize: "12.5px",
|
|
251
|
+
fontWeight: 500,
|
|
252
|
+
fontFamily: "inherit",
|
|
140
253
|
cursor: "pointer"
|
|
141
|
-
}
|
|
254
|
+
},
|
|
255
|
+
// Template list: matches @fonderie/vue-admin-screens' shell look with local
|
|
256
|
+
// values — this package sits below the admin screens and cannot import them.
|
|
257
|
+
listContainer: { padding: "32px 40px 64px", maxWidth: "1160px", boxSizing: "border-box" },
|
|
258
|
+
listTitle: {
|
|
259
|
+
fontSize: "22px",
|
|
260
|
+
fontWeight: 600,
|
|
261
|
+
margin: "0",
|
|
262
|
+
letterSpacing: "var(--fonderie-tracking-display,-0.05em)",
|
|
263
|
+
lineHeight: 1.25
|
|
264
|
+
},
|
|
265
|
+
hint: { fontSize: "13.5px", color: "var(--fonderie-text-muted,#5c5c5c)", margin: "4px 0 24px" },
|
|
266
|
+
dot: { width: "6px", height: "6px", borderRadius: "999px", background: "currentColor" }
|
|
142
267
|
};
|
|
143
268
|
|
|
269
|
+
// src/screens/TemplateCreateScreen.ts
|
|
270
|
+
var TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;
|
|
271
|
+
var LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
|
|
272
|
+
var TemplateCreateScreen = (0, import_vue.defineComponent)({
|
|
273
|
+
name: "FonderieTemplateCreateScreen",
|
|
274
|
+
props: {
|
|
275
|
+
client: { type: Object, required: true },
|
|
276
|
+
/**
|
|
277
|
+
* Given ⇒ add a LOCALE of this existing template: the type is fixed and the
|
|
278
|
+
* form starts from its default-locale copy. Omitted ⇒ a brand-new template
|
|
279
|
+
* type (one the app sends that has no stored copy yet).
|
|
280
|
+
*/
|
|
281
|
+
type: { type: String, default: void 0 },
|
|
282
|
+
/** Locales already in use, offered as suggestions. */
|
|
283
|
+
locales: { type: Array, default: () => [] }
|
|
284
|
+
},
|
|
285
|
+
emits: {
|
|
286
|
+
/** What was created, so the caller can open it in the editor. */
|
|
287
|
+
created: (_created) => true
|
|
288
|
+
},
|
|
289
|
+
setup(props, { emit }) {
|
|
290
|
+
const addingLocale = props.type !== void 0;
|
|
291
|
+
const newType = (0, import_vue.ref)("");
|
|
292
|
+
const locale = (0, import_vue.ref)("");
|
|
293
|
+
const subject = (0, import_vue.ref)("");
|
|
294
|
+
const html = (0, import_vue.ref)("");
|
|
295
|
+
const text = (0, import_vue.ref)("");
|
|
296
|
+
const error = (0, import_vue.ref)(null);
|
|
297
|
+
const busy = (0, import_vue.ref)(false);
|
|
298
|
+
(0, import_vue.onMounted)(() => {
|
|
299
|
+
if (!props.type) return;
|
|
300
|
+
props.client.getTemplate(props.type, null).then(({ result }) => {
|
|
301
|
+
subject.value = result.subject ?? "";
|
|
302
|
+
html.value = result.html ?? "";
|
|
303
|
+
text.value = result.text;
|
|
304
|
+
}).catch(() => void 0);
|
|
305
|
+
});
|
|
306
|
+
async function submit(e) {
|
|
307
|
+
e.preventDefault();
|
|
308
|
+
error.value = null;
|
|
309
|
+
const t = addingLocale ? props.type : newType.value.trim();
|
|
310
|
+
const l = locale.value.trim() || null;
|
|
311
|
+
if (!TYPE_RE.test(t)) {
|
|
312
|
+
error.value = "Type: lowercase letters, digits, - and _ (e.g. weekly-digest).";
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
if (addingLocale && !l) {
|
|
316
|
+
error.value = "Choose the locale to add, e.g. fr or fr-CA.";
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (l && !LOCALE_RE.test(l)) {
|
|
320
|
+
error.value = "Locale: a language tag such as fr, es or fr-CA.";
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (!text.value.trim()) {
|
|
324
|
+
error.value = "The plain-text body is required \u2014 it is what every email client can show.";
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
busy.value = true;
|
|
328
|
+
try {
|
|
329
|
+
const exists = await props.client.getTemplate(t, l).then(
|
|
330
|
+
() => true,
|
|
331
|
+
(err) => !(err instanceof import_client.FonderieApiError && err.status === 404)
|
|
332
|
+
);
|
|
333
|
+
if (exists) {
|
|
334
|
+
error.value = `"${t}"${l ? ` (${l})` : ""} already exists \u2014 open it from the list to edit.`;
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const input = { text: text.value, active: true };
|
|
338
|
+
if (subject.value) input.subject = subject.value;
|
|
339
|
+
if (html.value) input.html = html.value;
|
|
340
|
+
await props.client.setTemplate(t, input, l);
|
|
341
|
+
emit("created", { type: t, locale: l });
|
|
342
|
+
} catch (err) {
|
|
343
|
+
error.value = err instanceof import_client.FonderieApiError ? err.explanation : String(err);
|
|
344
|
+
} finally {
|
|
345
|
+
busy.value = false;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const field = (id, label, model, opts = {}) => [
|
|
349
|
+
(0, import_vue.h)("label", { style: styles.label, for: id }, label),
|
|
350
|
+
(0, import_vue.h)(opts.textarea ? "textarea" : "input", {
|
|
351
|
+
id,
|
|
352
|
+
style: opts.textarea ? { ...styles.textarea, minHeight: opts.minHeight } : styles.input,
|
|
353
|
+
value: model.value,
|
|
354
|
+
autocomplete: "off",
|
|
355
|
+
spellcheck: false,
|
|
356
|
+
...opts.placeholder ? { placeholder: opts.placeholder } : {},
|
|
357
|
+
...opts.list ? { list: opts.list } : {},
|
|
358
|
+
onInput: (ev) => {
|
|
359
|
+
model.value = ev.target.value;
|
|
360
|
+
}
|
|
361
|
+
})
|
|
362
|
+
];
|
|
363
|
+
return () => (0, import_vue.h)("div", { style: { ...styles.container, maxWidth: "760px" } }, [
|
|
364
|
+
(0, import_vue.h)(
|
|
365
|
+
"h1",
|
|
366
|
+
{ style: styles.title },
|
|
367
|
+
addingLocale ? `Add a locale to ${props.type}` : "New template"
|
|
368
|
+
),
|
|
369
|
+
(0, import_vue.h)(
|
|
370
|
+
"p",
|
|
371
|
+
{ style: { ...styles.meta, lineHeight: 1.5 } },
|
|
372
|
+
addingLocale ? "Starts from the default copy. People whose locale matches receive this version; everyone else keeps the default." : "For an email your app sends that has no stored copy yet. The type must match what the app sends."
|
|
373
|
+
),
|
|
374
|
+
(0, import_vue.h)("form", { style: styles.form, onSubmit: (ev) => void submit(ev) }, [
|
|
375
|
+
...addingLocale ? [] : field("new-template-type", "Type", newType, { placeholder: "weekly-digest" }),
|
|
376
|
+
...field(
|
|
377
|
+
"new-template-locale",
|
|
378
|
+
addingLocale ? "Locale" : "Locale (optional \u2014 empty is the default)",
|
|
379
|
+
locale,
|
|
380
|
+
{ placeholder: "fr", list: "new-template-locales" }
|
|
381
|
+
),
|
|
382
|
+
(0, import_vue.h)(
|
|
383
|
+
"datalist",
|
|
384
|
+
{ id: "new-template-locales" },
|
|
385
|
+
[...new Set(props.locales)].map((l) => (0, import_vue.h)("option", { key: l, value: l }))
|
|
386
|
+
),
|
|
387
|
+
...field("new-template-subject", "Subject", subject),
|
|
388
|
+
...field("new-template-html", "HTML body", html, { textarea: true, minHeight: "160px" }),
|
|
389
|
+
...field("new-template-text", "Plain-text body", text, {
|
|
390
|
+
textarea: true,
|
|
391
|
+
minHeight: "100px"
|
|
392
|
+
}),
|
|
393
|
+
error.value ? (0, import_vue.h)("p", { style: styles.error, role: "alert" }, error.value) : null,
|
|
394
|
+
(0, import_vue.h)(
|
|
395
|
+
"button",
|
|
396
|
+
{
|
|
397
|
+
type: "submit",
|
|
398
|
+
disabled: busy.value,
|
|
399
|
+
style: { ...styles.button, marginTop: "16px" }
|
|
400
|
+
},
|
|
401
|
+
busy.value ? "Creating\u2026" : addingLocale ? "Add locale" : "Create template"
|
|
402
|
+
)
|
|
403
|
+
])
|
|
404
|
+
]);
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
|
|
144
408
|
// src/screens/TemplateEditorScreen.ts
|
|
409
|
+
var import_vue_courier_admin = require("@fonderie/vue-courier-admin");
|
|
410
|
+
var import_vue2 = require("vue");
|
|
145
411
|
var PREVIEW_DEBOUNCE_MS = 500;
|
|
146
412
|
var IMPLICIT = /* @__PURE__ */ new Set(["subject", "preheader", "brandName"]);
|
|
147
|
-
var TemplateEditorScreen = (0,
|
|
413
|
+
var TemplateEditorScreen = (0, import_vue2.defineComponent)({
|
|
148
414
|
name: "FonderieTemplateEditorScreen",
|
|
149
415
|
props: {
|
|
150
416
|
client: { type: Object, required: true },
|
|
151
417
|
type: { type: String, required: true },
|
|
152
|
-
locale: { type: String, default: null }
|
|
418
|
+
locale: { type: String, default: null },
|
|
419
|
+
/**
|
|
420
|
+
* A built-in email's default-locale row (the list's `system` flag): it can
|
|
421
|
+
* be edited and rolled back, never deleted — Delete is not offered.
|
|
422
|
+
*/
|
|
423
|
+
system: { type: Boolean, default: false },
|
|
424
|
+
/** Shows "+ Add locale" on the default locale (emits add-locale). */
|
|
425
|
+
allowAddLocale: { type: Boolean, default: false }
|
|
153
426
|
},
|
|
154
427
|
emits: {
|
|
155
|
-
saved: () => true
|
|
428
|
+
saved: () => true,
|
|
429
|
+
/** The template was deleted and no longer exists. */
|
|
430
|
+
deleted: () => true,
|
|
431
|
+
/** Create a translation of this template. */
|
|
432
|
+
"add-locale": (_type) => true
|
|
156
433
|
},
|
|
157
434
|
setup(props, { emit }) {
|
|
158
435
|
const { template, isLoading, error, refresh } = (0, import_vue_courier_admin.useTemplate)(
|
|
@@ -160,15 +437,16 @@ var TemplateEditorScreen = (0, import_vue.defineComponent)({
|
|
|
160
437
|
props.type,
|
|
161
438
|
props.locale
|
|
162
439
|
);
|
|
163
|
-
const { saveTemplate } = (0, import_vue_courier_admin.useTemplates)(props.client);
|
|
440
|
+
const { saveTemplate, removeTemplate } = (0, import_vue_courier_admin.useTemplates)(props.client);
|
|
441
|
+
const isDeleting = (0, import_vue2.ref)(false);
|
|
164
442
|
const { revisions, rollback } = (0, import_vue_courier_admin.useTemplateRevisions)(props.client, props.type, props.locale);
|
|
165
|
-
const isSaving = (0,
|
|
166
|
-
const saveError = (0,
|
|
167
|
-
const subject = (0,
|
|
168
|
-
const html = (0,
|
|
169
|
-
const text = (0,
|
|
170
|
-
const active = (0,
|
|
171
|
-
(0,
|
|
443
|
+
const isSaving = (0, import_vue2.ref)(false);
|
|
444
|
+
const saveError = (0, import_vue2.ref)(null);
|
|
445
|
+
const subject = (0, import_vue2.ref)("");
|
|
446
|
+
const html = (0, import_vue2.ref)("");
|
|
447
|
+
const text = (0, import_vue2.ref)("");
|
|
448
|
+
const active = (0, import_vue2.ref)(true);
|
|
449
|
+
(0, import_vue2.watch)(template, (tpl) => {
|
|
172
450
|
if (!tpl) return;
|
|
173
451
|
subject.value = tpl.subject ?? "";
|
|
174
452
|
html.value = tpl.html ?? "";
|
|
@@ -181,8 +459,8 @@ var TemplateEditorScreen = (0, import_vue.defineComponent)({
|
|
|
181
459
|
error: previewError,
|
|
182
460
|
renderPreview
|
|
183
461
|
} = (0, import_vue_courier_admin.useTemplatePreview)(props.client);
|
|
184
|
-
const sampleJson = (0,
|
|
185
|
-
const sampleError = (0,
|
|
462
|
+
const sampleJson = (0, import_vue2.ref)("{}");
|
|
463
|
+
const sampleError = (0, import_vue2.ref)(null);
|
|
186
464
|
async function run() {
|
|
187
465
|
let data = {};
|
|
188
466
|
try {
|
|
@@ -217,12 +495,12 @@ var TemplateEditorScreen = (0, import_vue.defineComponent)({
|
|
|
217
495
|
}
|
|
218
496
|
}
|
|
219
497
|
let previewTimer;
|
|
220
|
-
(0,
|
|
498
|
+
(0, import_vue2.watch)([template, subject, html, text, sampleJson], () => {
|
|
221
499
|
if (!template.value) return;
|
|
222
500
|
clearTimeout(previewTimer);
|
|
223
501
|
previewTimer = setTimeout(() => void run(), PREVIEW_DEBOUNCE_MS);
|
|
224
502
|
});
|
|
225
|
-
const dirty = (0,
|
|
503
|
+
const dirty = (0, import_vue2.computed)(() => {
|
|
226
504
|
const tpl = template.value;
|
|
227
505
|
return !!tpl && (subject.value !== (tpl.subject ?? "") || html.value !== (tpl.html ?? "") || text.value !== tpl.text || active.value !== tpl.active);
|
|
228
506
|
});
|
|
@@ -244,13 +522,28 @@ var TemplateEditorScreen = (0, import_vue.defineComponent)({
|
|
|
244
522
|
isSaving.value = false;
|
|
245
523
|
}
|
|
246
524
|
}
|
|
525
|
+
async function handleDelete() {
|
|
526
|
+
const which = props.locale ? `the ${props.locale} version of "${props.type}"` : `"${props.type}"`;
|
|
527
|
+
const fallback = props.locale ? " People in that locale will receive the default version." : "";
|
|
528
|
+
if (!window.confirm(`Delete ${which}?${fallback}`)) return;
|
|
529
|
+
isDeleting.value = true;
|
|
530
|
+
saveError.value = null;
|
|
531
|
+
try {
|
|
532
|
+
await removeTemplate(props.type, props.locale);
|
|
533
|
+
emit("deleted");
|
|
534
|
+
} catch (err) {
|
|
535
|
+
saveError.value = err;
|
|
536
|
+
} finally {
|
|
537
|
+
isDeleting.value = false;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
247
540
|
function renderRevision(rev) {
|
|
248
|
-
return (0,
|
|
249
|
-
(0,
|
|
541
|
+
return (0, import_vue2.h)("li", { key: rev.version, style: styles.revisionRow }, [
|
|
542
|
+
(0, import_vue2.h)(
|
|
250
543
|
"span",
|
|
251
544
|
`v${rev.version} \u2014 ${rev.actor ?? "unknown"} \u2014 ${new Date(rev.createdAt).toLocaleString()}`
|
|
252
545
|
),
|
|
253
|
-
(0,
|
|
546
|
+
(0, import_vue2.h)(
|
|
254
547
|
"button",
|
|
255
548
|
{ type: "button", style: styles.rollbackButton, onClick: () => rollback(rev.version) },
|
|
256
549
|
"Roll back"
|
|
@@ -258,21 +551,36 @@ var TemplateEditorScreen = (0, import_vue.defineComponent)({
|
|
|
258
551
|
]);
|
|
259
552
|
}
|
|
260
553
|
return () => {
|
|
261
|
-
if (isLoading.value) return (0,
|
|
554
|
+
if (isLoading.value) return (0, import_vue2.h)("p", { style: styles.status }, "Loading template\u2026");
|
|
262
555
|
if (error.value)
|
|
263
|
-
return (0,
|
|
264
|
-
return (0,
|
|
265
|
-
(0,
|
|
266
|
-
|
|
267
|
-
"
|
|
268
|
-
|
|
269
|
-
|
|
556
|
+
return (0, import_vue2.h)("p", { style: styles.error, role: "alert" }, error.value.explanation);
|
|
557
|
+
return (0, import_vue2.h)("div", { style: styles.container }, [
|
|
558
|
+
(0, import_vue2.h)(
|
|
559
|
+
"div",
|
|
560
|
+
{ style: { display: "flex", alignItems: "center", gap: "12px", flexWrap: "wrap" } },
|
|
561
|
+
[
|
|
562
|
+
(0, import_vue2.h)("h1", { style: styles.title }, props.type),
|
|
563
|
+
props.allowAddLocale && !props.locale ? (0, import_vue2.h)(
|
|
564
|
+
"button",
|
|
565
|
+
{
|
|
566
|
+
type: "button",
|
|
567
|
+
style: { ...styles.rollbackButton, marginLeft: "auto" },
|
|
568
|
+
onClick: () => emit("add-locale", props.type)
|
|
569
|
+
},
|
|
570
|
+
"+ Add locale"
|
|
571
|
+
) : null
|
|
572
|
+
]
|
|
270
573
|
),
|
|
271
|
-
(0,
|
|
272
|
-
(0,
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
574
|
+
(0, import_vue2.h)("p", { style: styles.meta }, [
|
|
575
|
+
(0, import_vue2.h)("strong", props.locale ?? "default locale"),
|
|
576
|
+
` \xB7 v${template.value?.version ?? 1}`,
|
|
577
|
+
props.system ? " \xB7 built-in email: edit or roll back, it cannot be deleted" : ""
|
|
578
|
+
]),
|
|
579
|
+
(0, import_vue2.h)("div", { style: styles.split }, [
|
|
580
|
+
(0, import_vue2.h)("div", { style: styles.column }, [
|
|
581
|
+
(0, import_vue2.h)("form", { style: styles.form, onSubmit: handleSubmit }, [
|
|
582
|
+
(0, import_vue2.h)("label", { style: styles.label, for: "template-subject" }, "Subject"),
|
|
583
|
+
(0, import_vue2.h)("input", {
|
|
276
584
|
id: "template-subject",
|
|
277
585
|
style: styles.input,
|
|
278
586
|
value: subject.value,
|
|
@@ -280,8 +588,8 @@ var TemplateEditorScreen = (0, import_vue.defineComponent)({
|
|
|
280
588
|
subject.value = e.target.value;
|
|
281
589
|
}
|
|
282
590
|
}),
|
|
283
|
-
(0,
|
|
284
|
-
(0,
|
|
591
|
+
(0, import_vue2.h)("label", { style: styles.label, for: "template-html" }, "HTML body"),
|
|
592
|
+
(0, import_vue2.h)("textarea", {
|
|
285
593
|
id: "template-html",
|
|
286
594
|
style: styles.textarea,
|
|
287
595
|
rows: 8,
|
|
@@ -290,8 +598,8 @@ var TemplateEditorScreen = (0, import_vue.defineComponent)({
|
|
|
290
598
|
html.value = e.target.value;
|
|
291
599
|
}
|
|
292
600
|
}),
|
|
293
|
-
(0,
|
|
294
|
-
(0,
|
|
601
|
+
(0, import_vue2.h)("label", { style: styles.label, for: "template-text" }, "Plain-text body"),
|
|
602
|
+
(0, import_vue2.h)("textarea", {
|
|
295
603
|
id: "template-text",
|
|
296
604
|
style: styles.textarea,
|
|
297
605
|
rows: 4,
|
|
@@ -301,8 +609,8 @@ var TemplateEditorScreen = (0, import_vue.defineComponent)({
|
|
|
301
609
|
text.value = e.target.value;
|
|
302
610
|
}
|
|
303
611
|
}),
|
|
304
|
-
(0,
|
|
305
|
-
(0,
|
|
612
|
+
(0, import_vue2.h)("label", { style: styles.checkboxLabel }, [
|
|
613
|
+
(0, import_vue2.h)("input", {
|
|
306
614
|
type: "checkbox",
|
|
307
615
|
checked: active.value,
|
|
308
616
|
onChange: (e) => {
|
|
@@ -311,9 +619,23 @@ var TemplateEditorScreen = (0, import_vue.defineComponent)({
|
|
|
311
619
|
}),
|
|
312
620
|
"Active"
|
|
313
621
|
]),
|
|
314
|
-
saveError.value ? (0,
|
|
315
|
-
|
|
316
|
-
(0,
|
|
622
|
+
saveError.value?.reason === "VERSION_CONFLICT" ? (0, import_vue2.h)("p", { style: styles.error, role: "alert" }, [
|
|
623
|
+
"Someone changed this template since you opened it. Reload to see their change, then edit again. ",
|
|
624
|
+
(0, import_vue2.h)(
|
|
625
|
+
"button",
|
|
626
|
+
{
|
|
627
|
+
type: "button",
|
|
628
|
+
style: styles.rollbackButton,
|
|
629
|
+
onClick: () => {
|
|
630
|
+
saveError.value = null;
|
|
631
|
+
void refresh();
|
|
632
|
+
}
|
|
633
|
+
},
|
|
634
|
+
"Reload"
|
|
635
|
+
)
|
|
636
|
+
]) : saveError.value ? (0, import_vue2.h)("p", { style: styles.error, role: "alert" }, saveError.value.explanation) : null,
|
|
637
|
+
(0, import_vue2.h)("div", { style: styles.saveRow }, [
|
|
638
|
+
(0, import_vue2.h)(
|
|
317
639
|
"button",
|
|
318
640
|
{
|
|
319
641
|
type: "submit",
|
|
@@ -322,11 +644,27 @@ var TemplateEditorScreen = (0, import_vue.defineComponent)({
|
|
|
322
644
|
},
|
|
323
645
|
isSaving.value ? "Saving\u2026" : "Save"
|
|
324
646
|
),
|
|
325
|
-
!dirty.value && !isSaving.value ? (0,
|
|
647
|
+
!dirty.value && !isSaving.value ? (0, import_vue2.h)("span", { style: styles.meta }, "No changes to save") : null,
|
|
648
|
+
!props.system ? (0, import_vue2.h)(
|
|
649
|
+
"button",
|
|
650
|
+
{
|
|
651
|
+
type: "button",
|
|
652
|
+
disabled: isDeleting.value,
|
|
653
|
+
onClick: () => void handleDelete(),
|
|
654
|
+
style: {
|
|
655
|
+
...styles.rollbackButton,
|
|
656
|
+
height: "36px",
|
|
657
|
+
marginLeft: "auto",
|
|
658
|
+
color: "var(--fonderie-danger,#e00)",
|
|
659
|
+
borderColor: "color-mix(in srgb, var(--fonderie-danger,#e00) 40%, transparent)"
|
|
660
|
+
}
|
|
661
|
+
},
|
|
662
|
+
isDeleting.value ? "Deleting\u2026" : "Delete"
|
|
663
|
+
) : null
|
|
326
664
|
])
|
|
327
665
|
]),
|
|
328
|
-
(0,
|
|
329
|
-
(0,
|
|
666
|
+
(0, import_vue2.h)("label", { style: styles.label, for: "template-sample" }, "Sample data"),
|
|
667
|
+
(0, import_vue2.h)("textarea", {
|
|
330
668
|
id: "template-sample",
|
|
331
669
|
style: styles.textarea,
|
|
332
670
|
rows: 6,
|
|
@@ -336,31 +674,31 @@ var TemplateEditorScreen = (0, import_vue.defineComponent)({
|
|
|
336
674
|
sampleJson.value = e.target.value;
|
|
337
675
|
}
|
|
338
676
|
}),
|
|
339
|
-
sampleError.value ? (0,
|
|
677
|
+
sampleError.value ? (0, import_vue2.h)("p", { style: styles.error, role: "alert" }, sampleError.value) : null
|
|
340
678
|
]),
|
|
341
|
-
(0,
|
|
342
|
-
(0,
|
|
343
|
-
(0,
|
|
344
|
-
(0,
|
|
679
|
+
(0, import_vue2.h)("div", { style: styles.previewColumn }, [
|
|
680
|
+
(0, import_vue2.h)("div", { style: styles.previewHeader }, [
|
|
681
|
+
(0, import_vue2.h)("span", { style: styles.label }, "Preview"),
|
|
682
|
+
(0, import_vue2.h)("span", { style: styles.meta }, isPreviewing.value ? "Rendering\u2026" : "Live")
|
|
345
683
|
]),
|
|
346
|
-
previewError.value ? (0,
|
|
347
|
-
preview.value?.subject ? (0,
|
|
684
|
+
previewError.value ? (0, import_vue2.h)("p", { style: styles.error, role: "alert" }, previewError.value.explanation) : null,
|
|
685
|
+
preview.value?.subject ? (0, import_vue2.h)("p", { style: styles.previewSubject }, preview.value.subject) : null,
|
|
348
686
|
preview.value?.html ? (
|
|
349
687
|
// sandbox= — no scripts, no same-origin. Operator-authored HTML
|
|
350
688
|
// must never execute in the dashboard's origin, where the admin
|
|
351
689
|
// token lives.
|
|
352
|
-
(0,
|
|
690
|
+
(0, import_vue2.h)("iframe", {
|
|
353
691
|
title: "Template preview",
|
|
354
692
|
style: styles.previewFrame,
|
|
355
693
|
sandbox: "",
|
|
356
694
|
srcdoc: preview.value.html
|
|
357
695
|
})
|
|
358
|
-
) : preview.value ? (0,
|
|
696
|
+
) : preview.value ? (0, import_vue2.h)("pre", { style: styles.previewText }, preview.value.text) : (0, import_vue2.h)("p", { style: styles.meta }, "Nothing rendered yet.")
|
|
359
697
|
])
|
|
360
698
|
]),
|
|
361
|
-
revisions.value.length > 0 ? (0,
|
|
362
|
-
(0,
|
|
363
|
-
(0,
|
|
699
|
+
revisions.value.length > 0 ? (0, import_vue2.h)("div", { style: styles.revisions }, [
|
|
700
|
+
(0, import_vue2.h)("h2", { style: styles.subtitle }, "History"),
|
|
701
|
+
(0, import_vue2.h)("ul", { style: styles.list }, revisions.value.map(renderRevision))
|
|
364
702
|
]) : null
|
|
365
703
|
]);
|
|
366
704
|
};
|
|
@@ -369,20 +707,48 @@ var TemplateEditorScreen = (0, import_vue.defineComponent)({
|
|
|
369
707
|
|
|
370
708
|
// src/screens/TemplateListScreen.ts
|
|
371
709
|
var import_vue_courier_admin2 = require("@fonderie/vue-courier-admin");
|
|
372
|
-
var
|
|
373
|
-
var
|
|
710
|
+
var import_vue3 = require("vue");
|
|
711
|
+
var builtIn = {
|
|
712
|
+
fontSize: "11px",
|
|
713
|
+
fontWeight: 500,
|
|
714
|
+
padding: "1px 7px",
|
|
715
|
+
borderRadius: "999px",
|
|
716
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
717
|
+
color: "var(--fonderie-text-muted,#5c5c5c)"
|
|
718
|
+
};
|
|
719
|
+
var newButton = {
|
|
720
|
+
display: "inline-flex",
|
|
721
|
+
alignItems: "center",
|
|
722
|
+
height: "32px",
|
|
723
|
+
boxSizing: "border-box",
|
|
724
|
+
borderRadius: "6px",
|
|
725
|
+
padding: "0 12px",
|
|
726
|
+
fontSize: "13px",
|
|
727
|
+
fontWeight: 500,
|
|
728
|
+
fontFamily: "inherit",
|
|
729
|
+
cursor: "pointer",
|
|
730
|
+
backgroundColor: "var(--fonderie-text,#171717)",
|
|
731
|
+
color: "var(--fonderie-surface,#fff)",
|
|
732
|
+
border: "1px solid var(--fonderie-text,#171717)"
|
|
733
|
+
};
|
|
734
|
+
var TemplateListScreen = (0, import_vue3.defineComponent)({
|
|
374
735
|
name: "FonderieTemplateListScreen",
|
|
375
736
|
props: {
|
|
376
|
-
client: { type: Object, required: true }
|
|
737
|
+
client: { type: Object, required: true },
|
|
738
|
+
/** Shows a "New template" button (emits create-template). */
|
|
739
|
+
allowCreate: { type: Boolean, default: false }
|
|
377
740
|
},
|
|
378
741
|
emits: {
|
|
379
|
-
|
|
742
|
+
/** The whole row — type AND locale, and whether it is built-in. */
|
|
743
|
+
"select-template": (_template) => true,
|
|
744
|
+
/** Receives the locales in use, to suggest. */
|
|
745
|
+
"create-template": (_context) => true
|
|
380
746
|
},
|
|
381
747
|
setup(props, { emit }) {
|
|
382
748
|
const { templates, isLoading, error } = (0, import_vue_courier_admin2.useTemplates)(props.client);
|
|
383
749
|
function renderRow(template) {
|
|
384
|
-
return (0,
|
|
385
|
-
(0,
|
|
750
|
+
return (0, import_vue3.h)("li", { key: `${template.type}:${template.locale ?? "base"}`, style: styles.row }, [
|
|
751
|
+
(0, import_vue3.h)(
|
|
386
752
|
"button",
|
|
387
753
|
{
|
|
388
754
|
type: "button",
|
|
@@ -390,30 +756,59 @@ var TemplateListScreen = (0, import_vue2.defineComponent)({
|
|
|
390
756
|
onClick: () => emit("select-template", template)
|
|
391
757
|
},
|
|
392
758
|
[
|
|
393
|
-
(0,
|
|
394
|
-
(0,
|
|
395
|
-
(0,
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
template.active ? "
|
|
399
|
-
)
|
|
759
|
+
(0, import_vue3.h)("span", { style: styles.type }, template.type),
|
|
760
|
+
(0, import_vue3.h)("span", { style: styles.locale }, template.locale ?? "default locale"),
|
|
761
|
+
template.system ? (0, import_vue3.h)("span", { style: builtIn }, "built-in") : null,
|
|
762
|
+
(0, import_vue3.h)("span", { style: template.active ? styles.active : styles.inactive }, [
|
|
763
|
+
(0, import_vue3.h)("span", { style: styles.dot }),
|
|
764
|
+
template.active ? "active" : "inactive"
|
|
765
|
+
])
|
|
400
766
|
]
|
|
401
767
|
)
|
|
402
768
|
]);
|
|
403
769
|
}
|
|
404
|
-
return () => {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
770
|
+
return () => (0, import_vue3.h)("div", { style: styles.listContainer }, [
|
|
771
|
+
(0, import_vue3.h)(
|
|
772
|
+
"div",
|
|
773
|
+
{
|
|
774
|
+
style: {
|
|
775
|
+
display: "flex",
|
|
776
|
+
alignItems: "center",
|
|
777
|
+
justifyContent: "space-between",
|
|
778
|
+
gap: "16px"
|
|
779
|
+
}
|
|
780
|
+
},
|
|
781
|
+
[
|
|
782
|
+
(0, import_vue3.h)("h1", { style: styles.listTitle }, "Templates"),
|
|
783
|
+
props.allowCreate ? (0, import_vue3.h)(
|
|
784
|
+
"button",
|
|
785
|
+
{
|
|
786
|
+
type: "button",
|
|
787
|
+
style: newButton,
|
|
788
|
+
onClick: () => emit("create-template", {
|
|
789
|
+
locales: [
|
|
790
|
+
...new Set(
|
|
791
|
+
templates.value.map((t) => t.locale).filter((l) => !!l)
|
|
792
|
+
)
|
|
793
|
+
]
|
|
794
|
+
})
|
|
795
|
+
},
|
|
796
|
+
"New template"
|
|
797
|
+
) : null
|
|
798
|
+
]
|
|
799
|
+
),
|
|
800
|
+
(0, import_vue3.h)(
|
|
801
|
+
"p",
|
|
802
|
+
{ style: styles.hint },
|
|
803
|
+
"Every email the app sends. Open one to edit its copy and preview it live."
|
|
804
|
+
),
|
|
805
|
+
isLoading.value ? (0, import_vue3.h)("p", { style: styles.status }, "Loading templates\u2026") : error.value ? (0, import_vue3.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : (0, import_vue3.h)("ul", { style: styles.list }, templates.value.map(renderRow))
|
|
806
|
+
]);
|
|
413
807
|
}
|
|
414
808
|
});
|
|
415
809
|
// Annotate the CommonJS export names for ESM import in node:
|
|
416
810
|
0 && (module.exports = {
|
|
811
|
+
TemplateCreateScreen,
|
|
417
812
|
TemplateEditorScreen,
|
|
418
813
|
TemplateListScreen
|
|
419
814
|
});
|