@fonderie/react-courier-admin-screens 0.6.0 → 0.8.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.js CHANGED
@@ -1,11 +1,15 @@
1
1
  // src/screens/TemplateEditorScreen.tsx
2
+ import {
3
+ createAdminT,
4
+ formatAdminDate
5
+ } from "@fonderie/client";
2
6
  import {
3
7
  useTemplate,
4
8
  useTemplatePreview,
5
9
  useTemplateRevisions,
6
10
  useTemplates
7
11
  } from "@fonderie/react-courier-admin";
8
- import { useCallback, useEffect, useRef, useState } from "react";
12
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
9
13
  import { jsx, jsxs } from "react/jsx-runtime";
10
14
  var IMPLICIT = /* @__PURE__ */ new Set(["subject", "preheader", "brandName"]);
11
15
  var PREVIEW_DEBOUNCE_MS = 500;
@@ -13,10 +17,16 @@ function TemplateEditorScreen({
13
17
  client,
14
18
  type,
15
19
  locale,
16
- onSaved
20
+ onSaved,
21
+ system = false,
22
+ onDeleted,
23
+ onAddLocale,
24
+ uiLocale
17
25
  }) {
26
+ const t = useMemo(() => createAdminT(uiLocale), [uiLocale]);
18
27
  const { template, isLoading, error, refresh } = useTemplate(client, type, locale);
19
- const { saveTemplate } = useTemplates(client);
28
+ const { saveTemplate, removeTemplate } = useTemplates(client);
29
+ const [isDeleting, setIsDeleting] = useState(false);
20
30
  const { revisions, rollback } = useTemplateRevisions(client, type, locale);
21
31
  const [isSaving, setIsSaving] = useState(false);
22
32
  const [saveError, setSaveError] = useState(null);
@@ -39,13 +49,13 @@ function TemplateEditorScreen({
39
49
  try {
40
50
  const parsed = JSON.parse(sampleJson || "{}");
41
51
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
42
- setSampleError("Sample data must be a JSON object.");
52
+ setSampleError(t("templates.editor.sampleNotObject"));
43
53
  return;
44
54
  }
45
55
  data = parsed;
46
56
  setSampleError(null);
47
57
  } catch {
48
- setSampleError("Sample data is not valid JSON.");
58
+ setSampleError(t("templates.editor.sampleInvalid"));
49
59
  return;
50
60
  }
51
61
  const result = await renderPreview(
@@ -58,7 +68,7 @@ function TemplateEditorScreen({
58
68
  const seeded = { ...data, ...Object.fromEntries(missing.map((v) => [v, v])) };
59
69
  setSampleJson(JSON.stringify(seeded, null, 2));
60
70
  }
61
- }, [renderPreview, type, text, subject, html, locale, sampleJson]);
71
+ }, [renderPreview, type, text, subject, html, locale, sampleJson, t]);
62
72
  const runRef = useRef(run);
63
73
  runRef.current = run;
64
74
  const loadedType = template ? `${type}:${locale ?? ""}` : null;
@@ -86,20 +96,46 @@ function TemplateEditorScreen({
86
96
  setIsSaving(false);
87
97
  }
88
98
  };
89
- if (isLoading) return /* @__PURE__ */ jsx("p", { style: styles.status, children: "Loading template\u2026" });
99
+ const handleDelete = async () => {
100
+ const message = locale ? t("templates.editor.confirmDeleteLocale", { type, locale }) : t("templates.editor.confirmDelete", { type });
101
+ if (!window.confirm(message)) return;
102
+ setIsDeleting(true);
103
+ setSaveError(null);
104
+ try {
105
+ await removeTemplate(type, locale);
106
+ onDeleted?.();
107
+ } catch (err) {
108
+ setSaveError(err);
109
+ } finally {
110
+ setIsDeleting(false);
111
+ }
112
+ };
113
+ if (isLoading) return /* @__PURE__ */ jsx("p", { style: styles.status, children: t("templates.editor.loading") });
90
114
  if (error)
91
115
  return /* @__PURE__ */ jsx("p", { style: styles.error, role: "alert", children: error.explanation });
92
116
  return /* @__PURE__ */ jsxs("div", { style: styles.container, children: [
93
- /* @__PURE__ */ jsx("h1", { style: styles.title, children: type }),
117
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }, children: [
118
+ /* @__PURE__ */ jsx("h1", { style: styles.title, children: type }),
119
+ onAddLocale && !locale ? /* @__PURE__ */ jsx(
120
+ "button",
121
+ {
122
+ type: "button",
123
+ style: { ...styles.rollbackButton, marginLeft: "auto" },
124
+ onClick: () => onAddLocale(type),
125
+ children: t("templates.editor.addLocale")
126
+ }
127
+ ) : null
128
+ ] }),
94
129
  /* @__PURE__ */ jsxs("p", { style: styles.meta, children: [
95
- locale ?? "base",
130
+ /* @__PURE__ */ jsx("strong", { children: locale ?? t("templates.defaultLocale") }),
96
131
  " \xB7 v",
97
- template?.version ?? 1
132
+ template?.version ?? 1,
133
+ system ? ` \xB7 ${t("templates.editor.builtInNote")}` : ""
98
134
  ] }),
99
135
  /* @__PURE__ */ jsxs("div", { style: styles.split, children: [
100
136
  /* @__PURE__ */ jsxs("div", { style: styles.column, children: [
101
137
  /* @__PURE__ */ jsxs("form", { style: styles.form, onSubmit: handleSubmit, children: [
102
- /* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-subject", children: "Subject" }),
138
+ /* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-subject", children: t("templates.subject") }),
103
139
  /* @__PURE__ */ jsx(
104
140
  "input",
105
141
  {
@@ -109,7 +145,7 @@ function TemplateEditorScreen({
109
145
  onChange: (event) => setSubject(event.target.value)
110
146
  }
111
147
  ),
112
- /* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-html", children: "HTML body" }),
148
+ /* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-html", children: t("templates.htmlBody") }),
113
149
  /* @__PURE__ */ jsx(
114
150
  "textarea",
115
151
  {
@@ -120,7 +156,7 @@ function TemplateEditorScreen({
120
156
  rows: 8
121
157
  }
122
158
  ),
123
- /* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-text", children: "Plain-text body" }),
159
+ /* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-text", children: t("templates.textBody") }),
124
160
  /* @__PURE__ */ jsx(
125
161
  "textarea",
126
162
  {
@@ -141,9 +177,24 @@ function TemplateEditorScreen({
141
177
  onChange: (event) => setActive(event.target.checked)
142
178
  }
143
179
  ),
144
- "Active"
180
+ t("templates.editor.active")
145
181
  ] }),
146
- saveError && /* @__PURE__ */ jsx("p", { style: styles.error, role: "alert", children: saveError.explanation }),
182
+ saveError?.reason === "VERSION_CONFLICT" ? /* @__PURE__ */ jsxs("p", { style: styles.error, role: "alert", children: [
183
+ t("templates.editor.conflict"),
184
+ " ",
185
+ /* @__PURE__ */ jsx(
186
+ "button",
187
+ {
188
+ type: "button",
189
+ style: styles.rollbackButton,
190
+ onClick: () => {
191
+ setSaveError(null);
192
+ void refresh();
193
+ },
194
+ children: t("common.reload")
195
+ }
196
+ )
197
+ ] }) : saveError ? /* @__PURE__ */ jsx("p", { style: styles.error, role: "alert", children: saveError.explanation }) : null,
147
198
  /* @__PURE__ */ jsxs("div", { style: styles.saveRow, children: [
148
199
  /* @__PURE__ */ jsx(
149
200
  "button",
@@ -151,13 +202,29 @@ function TemplateEditorScreen({
151
202
  type: "submit",
152
203
  disabled: isSaving || !dirty,
153
204
  style: dirty ? styles.button : styles.buttonDisabled,
154
- children: isSaving ? "Saving\u2026" : "Save"
205
+ children: isSaving ? t("common.saving") : t("common.save")
155
206
  }
156
207
  ),
157
- !dirty && !isSaving && /* @__PURE__ */ jsx("span", { style: styles.meta, children: "No changes to save" })
208
+ !dirty && !isSaving && /* @__PURE__ */ jsx("span", { style: styles.meta, children: t("templates.editor.noChanges") }),
209
+ !system ? /* @__PURE__ */ jsx(
210
+ "button",
211
+ {
212
+ type: "button",
213
+ disabled: isDeleting,
214
+ onClick: () => void handleDelete(),
215
+ style: {
216
+ ...styles.rollbackButton,
217
+ height: 36,
218
+ marginLeft: "auto",
219
+ color: "var(--fonderie-danger,#e00)",
220
+ borderColor: "color-mix(in srgb, var(--fonderie-danger,#e00) 40%, transparent)"
221
+ },
222
+ children: isDeleting ? t("common.deleting") : t("common.delete")
223
+ }
224
+ ) : null
158
225
  ] })
159
226
  ] }),
160
- /* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-sample", children: "Sample data" }),
227
+ /* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-sample", children: t("templates.editor.sampleData") }),
161
228
  /* @__PURE__ */ jsx(
162
229
  "textarea",
163
230
  {
@@ -173,8 +240,8 @@ function TemplateEditorScreen({
173
240
  ] }),
174
241
  /* @__PURE__ */ jsxs("div", { style: styles.previewColumn, children: [
175
242
  /* @__PURE__ */ jsxs("div", { style: styles.previewHeader, children: [
176
- /* @__PURE__ */ jsx("span", { style: styles.label, children: "Preview" }),
177
- /* @__PURE__ */ jsx("span", { style: styles.meta, children: isPreviewing ? "Rendering\u2026" : "Live" })
243
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: t("templates.editor.preview") }),
244
+ /* @__PURE__ */ jsx("span", { style: styles.meta, children: isPreviewing ? t("templates.editor.rendering") : t("templates.editor.live") })
178
245
  ] }),
179
246
  previewError && /* @__PURE__ */ jsx("p", { style: styles.error, role: "alert", children: previewError.explanation }),
180
247
  preview?.subject && /* @__PURE__ */ jsx("p", { style: styles.previewSubject, children: preview.subject }),
@@ -185,26 +252,26 @@ function TemplateEditorScreen({
185
252
  /* @__PURE__ */ jsx(
186
253
  "iframe",
187
254
  {
188
- title: "Template preview",
255
+ title: t("templates.editor.previewTitle"),
189
256
  style: styles.previewFrame,
190
257
  sandbox: "",
191
258
  srcDoc: preview.html
192
259
  }
193
260
  )
194
- ) : preview ? /* @__PURE__ */ jsx("pre", { style: styles.previewText, children: preview.text }) : /* @__PURE__ */ jsx("p", { style: styles.meta, children: "Nothing rendered yet." })
261
+ ) : preview ? /* @__PURE__ */ jsx("pre", { style: styles.previewText, children: preview.text }) : /* @__PURE__ */ jsx("p", { style: styles.meta, children: t("templates.editor.nothingRendered") })
195
262
  ] })
196
263
  ] }),
197
264
  revisions.length > 0 && /* @__PURE__ */ jsxs("div", { style: styles.revisions, children: [
198
- /* @__PURE__ */ jsx("h2", { style: styles.subtitle, children: "History" }),
265
+ /* @__PURE__ */ jsx("h2", { style: styles.subtitle, children: t("templates.editor.history") }),
199
266
  /* @__PURE__ */ jsx("ul", { style: styles.list, children: revisions.map((rev) => /* @__PURE__ */ jsxs("li", { style: styles.row, children: [
200
267
  /* @__PURE__ */ jsxs("span", { children: [
201
268
  "v",
202
269
  rev.version,
203
270
  " \u2014 ",
204
- rev.actor ?? "unknown",
271
+ rev.actor ?? t("templates.editor.unknownActor"),
205
272
  " \u2014",
206
273
  " ",
207
- new Date(rev.createdAt).toLocaleString()
274
+ formatAdminDate(rev.createdAt, uiLocale)
208
275
  ] }),
209
276
  /* @__PURE__ */ jsx(
210
277
  "button",
@@ -212,7 +279,7 @@ function TemplateEditorScreen({
212
279
  type: "button",
213
280
  onClick: () => rollback(rev.version),
214
281
  style: styles.rollbackButton,
215
- children: "Roll back"
282
+ children: t("templates.editor.rollBack")
216
283
  }
217
284
  )
218
285
  ] }, rev.version)) })
@@ -388,14 +455,44 @@ var styles = {
388
455
  };
389
456
 
390
457
  // src/screens/TemplateListScreen.tsx
458
+ import {
459
+ createAdminT as createAdminT2
460
+ } from "@fonderie/client";
391
461
  import { useTemplates as useTemplates2 } from "@fonderie/react-courier-admin";
392
462
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
393
- function TemplateListScreen({ client, onSelectTemplate }) {
463
+ function TemplateListScreen({
464
+ client,
465
+ onSelectTemplate,
466
+ onCreateTemplate,
467
+ locale
468
+ }) {
469
+ const t = createAdminT2(locale);
394
470
  const { templates, isLoading, error } = useTemplates2(client);
395
471
  return /* @__PURE__ */ jsxs2("div", { style: styles2.container, children: [
396
- /* @__PURE__ */ jsx2("h1", { style: styles2.title, children: "Templates" }),
397
- /* @__PURE__ */ jsx2("p", { style: styles2.hint, children: "Every email the app sends. Open one to edit its copy and preview it live." }),
398
- isLoading ? /* @__PURE__ */ jsx2("p", { style: styles2.status, children: "Loading templates\u2026" }) : error ? /* @__PURE__ */ jsx2("p", { style: styles2.error, role: "alert", children: error.explanation }) : /* @__PURE__ */ jsx2("ul", { style: styles2.list, children: templates.map((template) => /* @__PURE__ */ jsx2("li", { style: styles2.row, children: /* @__PURE__ */ jsxs2(
472
+ /* @__PURE__ */ jsxs2(
473
+ "div",
474
+ {
475
+ style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16 },
476
+ children: [
477
+ /* @__PURE__ */ jsx2("h1", { style: styles2.title, children: t("templates.list.title") }),
478
+ onCreateTemplate ? /* @__PURE__ */ jsx2(
479
+ "button",
480
+ {
481
+ type: "button",
482
+ onClick: () => onCreateTemplate({
483
+ locales: [
484
+ ...new Set(templates.map((row) => row.locale).filter((l) => !!l))
485
+ ]
486
+ }),
487
+ style: styles2.newButton,
488
+ children: t("templates.list.newTemplate")
489
+ }
490
+ ) : null
491
+ ]
492
+ }
493
+ ),
494
+ /* @__PURE__ */ jsx2("p", { style: styles2.hint, children: t("templates.list.hint") }),
495
+ isLoading ? /* @__PURE__ */ jsx2("p", { style: styles2.status, children: t("templates.list.loading") }) : error ? /* @__PURE__ */ jsx2("p", { style: styles2.error, role: "alert", children: error.explanation }) : /* @__PURE__ */ jsx2("ul", { style: styles2.list, children: templates.map((template) => /* @__PURE__ */ jsx2("li", { style: styles2.row, children: /* @__PURE__ */ jsxs2(
399
496
  "button",
400
497
  {
401
498
  type: "button",
@@ -403,10 +500,11 @@ function TemplateListScreen({ client, onSelectTemplate }) {
403
500
  style: styles2.rowButton,
404
501
  children: [
405
502
  /* @__PURE__ */ jsx2("span", { style: styles2.type, children: template.type }),
406
- /* @__PURE__ */ jsx2("span", { style: styles2.locale, children: template.locale ?? "default locale" }),
503
+ /* @__PURE__ */ jsx2("span", { style: styles2.locale, children: template.locale ?? t("templates.defaultLocale") }),
504
+ template.system ? /* @__PURE__ */ jsx2("span", { style: styles2.builtIn, children: t("templates.builtInBadge") }) : null,
407
505
  /* @__PURE__ */ jsxs2("span", { style: template.active ? styles2.active : styles2.inactive, children: [
408
506
  /* @__PURE__ */ jsx2("span", { style: styles2.dot }),
409
- template.active ? "active" : "inactive"
507
+ template.active ? t("common.status.active") : t("common.status.inactive")
410
508
  ] })
411
509
  ]
412
510
  }
@@ -469,6 +567,29 @@ var styles2 = {
469
567
  },
470
568
  locale: { fontSize: 12.5, color: "var(--fonderie-text-muted,#5c5c5c)" },
471
569
  dot: { width: 6, height: 6, borderRadius: 999, background: "currentColor" },
570
+ builtIn: {
571
+ fontSize: 11,
572
+ fontWeight: 500,
573
+ padding: "1px 7px",
574
+ borderRadius: 999,
575
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
576
+ color: "var(--fonderie-text-muted,#5c5c5c)"
577
+ },
578
+ newButton: {
579
+ display: "inline-flex",
580
+ alignItems: "center",
581
+ height: 32,
582
+ boxSizing: "border-box",
583
+ borderRadius: 6,
584
+ padding: "0 12px",
585
+ fontSize: 13,
586
+ fontWeight: 500,
587
+ fontFamily: "inherit",
588
+ cursor: "pointer",
589
+ backgroundColor: "var(--fonderie-text,#171717)",
590
+ color: "var(--fonderie-surface,#fff)",
591
+ border: "1px solid var(--fonderie-text,#171717)"
592
+ },
472
593
  active: {
473
594
  ...pill,
474
595
  color: "var(--fonderie-accent-strong,#009767)",
@@ -480,7 +601,216 @@ var styles2 = {
480
601
  background: "color-mix(in srgb, var(--fonderie-text-muted,#5c5c5c) 10%, transparent)"
481
602
  }
482
603
  };
604
+
605
+ // src/screens/TemplateCreateScreen.tsx
606
+ import {
607
+ FonderieApiError,
608
+ createAdminT as createAdminT3
609
+ } from "@fonderie/client";
610
+ import { useEffect as useEffect2, useState as useState2 } from "react";
611
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
612
+ var TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;
613
+ var LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
614
+ function TemplateCreateScreen({
615
+ client,
616
+ type,
617
+ locales = [],
618
+ onCreated,
619
+ locale
620
+ }) {
621
+ const tr = createAdminT3(locale);
622
+ const addingLocale = type !== void 0;
623
+ const [newType, setNewType] = useState2("");
624
+ const [targetLocale, setTargetLocale] = useState2("");
625
+ const [subject, setSubject] = useState2("");
626
+ const [html, setHtml] = useState2("");
627
+ const [text, setText] = useState2("");
628
+ const [error, setError] = useState2(null);
629
+ const [busy, setBusy] = useState2(false);
630
+ useEffect2(() => {
631
+ if (!type) return;
632
+ client.getTemplate(type, null).then(({ result }) => {
633
+ setSubject(result.subject ?? "");
634
+ setHtml(result.html ?? "");
635
+ setText(result.text);
636
+ }).catch(() => void 0);
637
+ }, [client, type]);
638
+ const submit = async (e) => {
639
+ e.preventDefault();
640
+ setError(null);
641
+ const t = addingLocale ? type : newType.trim();
642
+ const l = targetLocale.trim() || null;
643
+ if (!TYPE_RE.test(t)) return setError(tr("templates.create.errorType"));
644
+ if (addingLocale && !l) return setError(tr("templates.create.errorLocaleRequired"));
645
+ if (l && !LOCALE_RE.test(l)) return setError(tr("templates.create.errorLocale"));
646
+ if (!text.trim()) return setError(tr("templates.create.errorText"));
647
+ setBusy(true);
648
+ try {
649
+ const exists = await client.getTemplate(t, l).then(
650
+ () => true,
651
+ (err) => !(err instanceof FonderieApiError && err.status === 404)
652
+ );
653
+ if (exists) {
654
+ setError(
655
+ l ? tr("templates.create.existsLocale", { type: t, locale: l }) : tr("templates.create.exists", { type: t })
656
+ );
657
+ return;
658
+ }
659
+ const input = { text, active: true };
660
+ if (subject) input.subject = subject;
661
+ if (html) input.html = html;
662
+ await client.setTemplate(t, input, l);
663
+ onCreated?.({ type: t, locale: l });
664
+ } catch (err) {
665
+ setError(err instanceof FonderieApiError ? err.explanation : String(err));
666
+ } finally {
667
+ setBusy(false);
668
+ }
669
+ };
670
+ return /* @__PURE__ */ jsxs3("div", { style: styles3.container, children: [
671
+ /* @__PURE__ */ jsx3("h1", { style: styles3.title, children: addingLocale ? tr("templates.create.titleLocale", { type }) : tr("templates.create.titleNew") }),
672
+ /* @__PURE__ */ jsx3("p", { style: styles3.meta, children: addingLocale ? tr("templates.create.hintLocale") : tr("templates.create.hintNew") }),
673
+ /* @__PURE__ */ jsxs3("form", { style: styles3.form, onSubmit: (e) => void submit(e), children: [
674
+ !addingLocale ? /* @__PURE__ */ jsxs3(Fragment, { children: [
675
+ /* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-type", children: tr("templates.create.type") }),
676
+ /* @__PURE__ */ jsx3(
677
+ "input",
678
+ {
679
+ id: "new-template-type",
680
+ style: styles3.input,
681
+ value: newType,
682
+ placeholder: "weekly-digest",
683
+ autoComplete: "off",
684
+ spellCheck: false,
685
+ onChange: (e) => setNewType(e.target.value)
686
+ }
687
+ )
688
+ ] }) : null,
689
+ /* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-locale", children: addingLocale ? tr("templates.create.locale") : tr("templates.create.localeOptional") }),
690
+ /* @__PURE__ */ jsx3(
691
+ "input",
692
+ {
693
+ id: "new-template-locale",
694
+ style: styles3.input,
695
+ value: targetLocale,
696
+ placeholder: "fr",
697
+ list: "new-template-locales",
698
+ autoComplete: "off",
699
+ spellCheck: false,
700
+ onChange: (e) => setTargetLocale(e.target.value)
701
+ }
702
+ ),
703
+ /* @__PURE__ */ jsx3("datalist", { id: "new-template-locales", children: [...new Set(locales)].map((l) => /* @__PURE__ */ jsx3("option", { value: l }, l)) }),
704
+ /* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-subject", children: tr("templates.subject") }),
705
+ /* @__PURE__ */ jsx3(
706
+ "input",
707
+ {
708
+ id: "new-template-subject",
709
+ style: styles3.input,
710
+ value: subject,
711
+ onChange: (e) => setSubject(e.target.value)
712
+ }
713
+ ),
714
+ /* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-html", children: tr("templates.htmlBody") }),
715
+ /* @__PURE__ */ jsx3(
716
+ "textarea",
717
+ {
718
+ id: "new-template-html",
719
+ style: { ...styles3.textarea, minHeight: 160 },
720
+ value: html,
721
+ onChange: (e) => setHtml(e.target.value)
722
+ }
723
+ ),
724
+ /* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-text", children: tr("templates.textBody") }),
725
+ /* @__PURE__ */ jsx3(
726
+ "textarea",
727
+ {
728
+ id: "new-template-text",
729
+ style: { ...styles3.textarea, minHeight: 100 },
730
+ value: text,
731
+ onChange: (e) => setText(e.target.value)
732
+ }
733
+ ),
734
+ error ? /* @__PURE__ */ jsx3("p", { style: styles3.error, role: "alert", children: error }) : null,
735
+ /* @__PURE__ */ jsx3("button", { type: "submit", disabled: busy, style: styles3.button, children: busy ? tr("templates.create.creating") : addingLocale ? tr("templates.create.submitLocale") : tr("templates.create.submitNew") })
736
+ ] })
737
+ ] });
738
+ }
739
+ var MONO = 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)';
740
+ var styles3 = {
741
+ container: { padding: "8px 40px 64px", maxWidth: 760, boxSizing: "border-box" },
742
+ title: {
743
+ fontSize: 22,
744
+ fontWeight: 600,
745
+ margin: "8px 0 4px",
746
+ letterSpacing: "var(--fonderie-tracking-display,-0.05em)",
747
+ lineHeight: 1.25
748
+ },
749
+ meta: {
750
+ fontSize: 13,
751
+ color: "var(--fonderie-text-muted,#5c5c5c)",
752
+ marginBottom: 20,
753
+ lineHeight: 1.5
754
+ },
755
+ form: {
756
+ display: "flex",
757
+ flexDirection: "column",
758
+ gap: 4,
759
+ background: "var(--fonderie-surface,#fff)",
760
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
761
+ borderRadius: "var(--fonderie-radius-lg,8px)",
762
+ padding: "6px 20px 20px"
763
+ },
764
+ label: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },
765
+ input: {
766
+ height: 36,
767
+ boxSizing: "border-box",
768
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
769
+ borderRadius: 6,
770
+ padding: "0 10px",
771
+ fontSize: 13.5,
772
+ fontFamily: "inherit",
773
+ background: "var(--fonderie-surface,#fff)",
774
+ color: "var(--fonderie-text,#171717)"
775
+ },
776
+ textarea: {
777
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
778
+ borderRadius: 6,
779
+ padding: 10,
780
+ fontSize: 13,
781
+ lineHeight: 1.55,
782
+ background: "var(--fonderie-surface,#fff)",
783
+ color: "var(--fonderie-text,#171717)",
784
+ fontFamily: MONO
785
+ },
786
+ error: {
787
+ color: "var(--fonderie-danger,#e00)",
788
+ background: "color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)",
789
+ borderRadius: 6,
790
+ padding: "8px 12px",
791
+ margin: "8px 0",
792
+ fontSize: 13.5
793
+ },
794
+ button: {
795
+ display: "inline-flex",
796
+ alignItems: "center",
797
+ height: 36,
798
+ boxSizing: "border-box",
799
+ backgroundColor: "var(--fonderie-text,#171717)",
800
+ color: "var(--fonderie-surface,#fff)",
801
+ padding: "0 16px",
802
+ borderRadius: 6,
803
+ border: "1px solid var(--fonderie-text,#171717)",
804
+ fontSize: 13.5,
805
+ fontWeight: 600,
806
+ fontFamily: "inherit",
807
+ cursor: "pointer",
808
+ alignSelf: "flex-start",
809
+ marginTop: 16
810
+ }
811
+ };
483
812
  export {
813
+ TemplateCreateScreen,
484
814
  TemplateEditorScreen,
485
815
  TemplateListScreen
486
816
  };