@fonderie/react-courier-admin-screens 0.9.0 → 0.10.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 CHANGED
@@ -718,10 +718,13 @@ var styles2 = {
718
718
 
719
719
  // src/screens/TemplateCreateScreen.tsx
720
720
  var import_client3 = require("@fonderie/client");
721
+ var import_react_courier_admin3 = require("@fonderie/react-courier-admin");
721
722
  var import_react2 = require("react");
722
723
  var import_jsx_runtime3 = require("react/jsx-runtime");
723
724
  var TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;
724
725
  var LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
726
+ var IMPLICIT2 = /* @__PURE__ */ new Set(["subject", "preheader", "brandName"]);
727
+ var PREVIEW_DEBOUNCE_MS2 = 500;
725
728
  function TemplateCreateScreen({
726
729
  client,
727
730
  type,
@@ -738,14 +741,48 @@ function TemplateCreateScreen({
738
741
  const [text, setText] = (0, import_react2.useState)("");
739
742
  const [error, setError] = (0, import_react2.useState)(null);
740
743
  const [busy, setBusy] = (0, import_react2.useState)(false);
744
+ const [source, setSource] = (0, import_react2.useState)(
745
+ null
746
+ );
747
+ const { preview, isPreviewing, error: previewError, renderPreview } = (0, import_react_courier_admin3.useTemplatePreview)(client);
748
+ const variables = (0, import_react2.useRef)([]);
741
749
  (0, import_react2.useEffect)(() => {
742
750
  if (!type) return;
743
751
  client.getTemplate(type, null).then(({ result }) => {
744
752
  setSubject(result.subject ?? "");
745
753
  setHtml(result.html ?? "");
746
754
  setText(result.text);
755
+ setSource({ subject: result.subject ?? "", html: result.html ?? "", text: result.text });
747
756
  }).catch(() => void 0);
748
757
  }, [client, type]);
758
+ const previewType = addingLocale ? type : newType.trim();
759
+ const previewLocale = LOCALE_RE.test(targetLocale.trim()) ? targetLocale.trim() : null;
760
+ (0, import_react2.useEffect)(() => {
761
+ if (!TYPE_RE.test(previewType) || !text.trim()) return;
762
+ const timer = setTimeout(async () => {
763
+ const input = { text, ...subject ? { subject } : {}, ...html ? { html } : {} };
764
+ const render = () => renderPreview(
765
+ previewType,
766
+ { ...input, data: Object.fromEntries(variables.current.map((v) => [v, v])) },
767
+ previewLocale
768
+ );
769
+ try {
770
+ const result = await render();
771
+ const used = result.variables.filter((v) => !IMPLICIT2.has(v));
772
+ if (used.some((v) => !variables.current.includes(v))) {
773
+ variables.current = used;
774
+ await render();
775
+ }
776
+ } catch {
777
+ }
778
+ }, PREVIEW_DEBOUNCE_MS2);
779
+ return () => clearTimeout(timer);
780
+ }, [renderPreview, previewType, previewLocale, subject, html, text]);
781
+ const untranslated = source ? [
782
+ subject && subject === source.subject ? tr("templates.subject") : null,
783
+ html && html === source.html ? tr("templates.htmlBody") : null,
784
+ text === source.text ? tr("templates.textBody") : null
785
+ ].filter((f) => f !== null) : [];
749
786
  const submit = async (e) => {
750
787
  e.preventDefault();
751
788
  setError(null);
@@ -781,75 +818,140 @@ function TemplateCreateScreen({
781
818
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles3.container, children: [
782
819
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { style: styles3.title, children: addingLocale ? tr("templates.create.titleLocale", { type }) : tr("templates.create.titleNew") }),
783
820
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.meta, children: addingLocale ? tr("templates.create.hintLocale") : tr("templates.create.hintNew") }),
784
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { style: styles3.form, onSubmit: (e) => void submit(e), children: [
785
- !addingLocale ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
786
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-type", children: tr("templates.create.type") }),
821
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles3.split, children: [
822
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { style: styles3.form, onSubmit: (e) => void submit(e), children: [
823
+ !addingLocale ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
824
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-type", children: tr("templates.create.type") }),
825
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
826
+ "input",
827
+ {
828
+ id: "new-template-type",
829
+ style: styles3.input,
830
+ value: newType,
831
+ placeholder: "weekly-digest",
832
+ autoComplete: "off",
833
+ spellCheck: false,
834
+ onChange: (e) => setNewType(e.target.value)
835
+ }
836
+ )
837
+ ] }) : null,
838
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-locale", children: addingLocale ? tr("templates.create.locale") : tr("templates.create.localeOptional") }),
787
839
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
788
840
  "input",
789
841
  {
790
- id: "new-template-type",
842
+ id: "new-template-locale",
791
843
  style: styles3.input,
792
- value: newType,
793
- placeholder: "weekly-digest",
844
+ value: targetLocale,
845
+ placeholder: "fr",
846
+ list: "new-template-locales",
794
847
  autoComplete: "off",
795
848
  spellCheck: false,
796
- onChange: (e) => setNewType(e.target.value)
849
+ onChange: (e) => setTargetLocale(e.target.value)
797
850
  }
798
- )
799
- ] }) : null,
800
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-locale", children: addingLocale ? tr("templates.create.locale") : tr("templates.create.localeOptional") }),
801
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
802
- "input",
803
- {
804
- id: "new-template-locale",
805
- style: styles3.input,
806
- value: targetLocale,
807
- placeholder: "fr",
808
- list: "new-template-locales",
809
- autoComplete: "off",
810
- spellCheck: false,
811
- onChange: (e) => setTargetLocale(e.target.value)
812
- }
813
- ),
814
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("datalist", { id: "new-template-locales", children: [...new Set(locales)].map((l) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: l }, l)) }),
815
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-subject", children: tr("templates.subject") }),
816
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
817
- "input",
818
- {
819
- id: "new-template-subject",
820
- style: styles3.input,
821
- value: subject,
822
- onChange: (e) => setSubject(e.target.value)
823
- }
824
- ),
825
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-html", children: tr("templates.htmlBody") }),
826
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
827
- "textarea",
828
- {
829
- id: "new-template-html",
830
- style: { ...styles3.textarea, minHeight: 160 },
831
- value: html,
832
- onChange: (e) => setHtml(e.target.value)
833
- }
834
- ),
835
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-text", children: tr("templates.textBody") }),
836
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
837
- "textarea",
838
- {
839
- id: "new-template-text",
840
- style: { ...styles3.textarea, minHeight: 100 },
841
- value: text,
842
- onChange: (e) => setText(e.target.value)
843
- }
844
- ),
845
- error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.error, role: "alert", children: error }) : null,
846
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "submit", disabled: busy, style: styles3.button, children: busy ? tr("templates.create.creating") : addingLocale ? tr("templates.create.submitLocale") : tr("templates.create.submitNew") })
851
+ ),
852
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("datalist", { id: "new-template-locales", children: [...new Set(locales)].map((l) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: l }, l)) }),
853
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-subject", children: tr("templates.subject") }),
854
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
855
+ "input",
856
+ {
857
+ id: "new-template-subject",
858
+ style: styles3.input,
859
+ value: subject,
860
+ onChange: (e) => setSubject(e.target.value)
861
+ }
862
+ ),
863
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-html", children: tr("templates.htmlBody") }),
864
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
865
+ "textarea",
866
+ {
867
+ id: "new-template-html",
868
+ style: { ...styles3.textarea, minHeight: 160 },
869
+ value: html,
870
+ onChange: (e) => setHtml(e.target.value)
871
+ }
872
+ ),
873
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-text", children: tr("templates.textBody") }),
874
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
875
+ "textarea",
876
+ {
877
+ id: "new-template-text",
878
+ style: { ...styles3.textarea, minHeight: 100 },
879
+ value: text,
880
+ onChange: (e) => setText(e.target.value)
881
+ }
882
+ ),
883
+ untranslated.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.notice, children: tr("templates.create.untranslated", { fields: untranslated.join(", ") }) }) : null,
884
+ error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.error, role: "alert", children: error }) : null,
885
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "submit", disabled: busy, style: styles3.button, children: busy ? tr("templates.create.creating") : addingLocale ? tr("templates.create.submitLocale") : tr("templates.create.submitNew") })
886
+ ] }),
887
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles3.previewColumn, children: [
888
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles3.previewHeader, children: [
889
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: styles3.label, children: tr("templates.editor.preview") }),
890
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: styles3.meta, children: isPreviewing ? tr("templates.editor.rendering") : tr("templates.editor.live") })
891
+ ] }),
892
+ previewError ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.error, role: "alert", children: previewError.explanation }) : null,
893
+ preview?.subject ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.previewSubject, children: preview.subject }) : null,
894
+ preview?.html ? (
895
+ // sandbox="" — no scripts, no same-origin: operator-authored HTML must
896
+ // never execute in the console's origin.
897
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
898
+ "iframe",
899
+ {
900
+ title: tr("templates.editor.previewTitle"),
901
+ style: styles3.previewFrame,
902
+ sandbox: "",
903
+ srcDoc: preview.html
904
+ }
905
+ )
906
+ ) : preview ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("pre", { style: styles3.previewText, children: preview.text }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.meta, children: tr("templates.editor.nothingRendered") })
907
+ ] })
847
908
  ] })
848
909
  ] });
849
910
  }
850
911
  var MONO = 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)';
851
912
  var styles3 = {
852
- container: { padding: "8px 40px 64px", maxWidth: 760, boxSizing: "border-box" },
913
+ container: { padding: "8px 40px 64px", maxWidth: 1400, boxSizing: "border-box" },
914
+ // The editor's layout: the form on the left, the render pinned on the right.
915
+ split: {
916
+ display: "grid",
917
+ gridTemplateColumns: "minmax(0, 1fr) minmax(0, 1fr)",
918
+ gap: 24,
919
+ alignItems: "start"
920
+ },
921
+ previewColumn: {
922
+ minWidth: 0,
923
+ display: "flex",
924
+ flexDirection: "column",
925
+ position: "sticky",
926
+ top: 16
927
+ },
928
+ previewHeader: { display: "flex", justifyContent: "space-between", alignItems: "center" },
929
+ previewSubject: { fontSize: 14, fontWeight: 600, margin: "8px 0" },
930
+ previewFrame: {
931
+ boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))",
932
+ width: "100%",
933
+ height: 520,
934
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
935
+ borderRadius: 8,
936
+ background: "var(--fonderie-surface,#fff)"
937
+ },
938
+ previewText: {
939
+ whiteSpace: "pre-wrap",
940
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
941
+ borderRadius: 8,
942
+ padding: 12,
943
+ fontSize: 13,
944
+ fontFamily: MONO
945
+ },
946
+ // The console's warning token (the shell's amber), readable in both themes.
947
+ notice: {
948
+ fontSize: 13,
949
+ color: "var(--fonderie-text,#171717)",
950
+ background: "color-mix(in srgb, var(--fonderie-warning,#f5a623) 16%, transparent)",
951
+ borderRadius: 6,
952
+ padding: "8px 12px",
953
+ margin: "12px 0 0"
954
+ },
853
955
  title: {
854
956
  fontSize: 22,
855
957
  fontWeight: 600,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/screens/TemplateEditorScreen.tsx","../src/screens/TemplateListScreen.tsx","../src/screens/TemplateCreateScreen.tsx"],"sourcesContent":["export type {\n\tITemplateCreateScreenProps,\n\tITemplateEditorScreenProps,\n\tITemplateListScreenProps,\n} from './screens';\nexport { TemplateCreateScreen, TemplateEditorScreen, TemplateListScreen } from './screens';\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\ttype FonderieApiError,\n\ttype ISetTemplateInput,\n\ttype ITemplateEntry,\n\tcreateAdminT,\n\tformatAdminDate,\n\tgroupTemplatesByType,\n\tmissingTemplateLocales,\n} from '@fonderie/client';\nimport {\n\tuseTemplate,\n\tuseTemplatePreview,\n\tuseTemplateRevisions,\n\tuseTemplates,\n} from '@fonderie/react-courier-admin';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\n// Implicit variables the layout injects — an operator never supplies these, so\n// offering them as fields would just be noise.\nconst IMPLICIT = new Set(['subject', 'preheader', 'brandName']);\n\nconst PREVIEW_DEBOUNCE_MS = 500;\n\nexport interface ITemplateEditorScreenProps {\n\tclient: CourierAdminClient;\n\ttype: string;\n\tlocale?: string | null;\n\tonSaved?: () => void;\n\t/**\n\t * A built-in email's default-locale row (the list's `system` flag): it can\n\t * be edited and rolled back, never deleted — Delete is not offered.\n\t */\n\tsystem?: boolean;\n\t/** Called after a delete; the template no longer exists. */\n\tonDeleted?: () => void;\n\t/**\n\t * Shows \"Add locale\": create a translation of this template. `locales` are\n\t * the ones the app uses elsewhere that this email lacks — suggest them first.\n\t */\n\tonAddLocale?: (type: string, context: { locales: string[] }) => void;\n\t/**\n\t * Shows a tab per locale this email exists in; receives the row to open.\n\t * Unsaved edits are confirmed before switching away.\n\t */\n\tonSelectLocale?: (template: ITemplateEntry) => void;\n\t/**\n\t * The CONSOLE's language (default English). Named `uiLocale` because\n\t * `locale` here is the template's own locale.\n\t */\n\tuiLocale?: AdminLocale | undefined;\n}\n\nexport function TemplateEditorScreen({\n\tclient,\n\ttype,\n\tlocale,\n\tonSaved,\n\tsystem = false,\n\tonDeleted,\n\tonAddLocale,\n\tonSelectLocale,\n\tuiLocale,\n}: ITemplateEditorScreenProps) {\n\t// Memoized: `run` below depends on it, and a new translator per render would\n\t// restart the preview debounce on every render.\n\tconst t = useMemo(() => createAdminT(uiLocale), [uiLocale]);\n\tconst { template, isLoading, error, refresh } = useTemplate(client, type, locale);\n\t// Saves go through the list hook (which re-fetches the template list after\n\t// each write); mounting it adds a template-list fetch to this single-template\n\t// editor — acceptable for an admin dashboard. Its isLoading/error track that\n\t// list fetch, so the save action keeps local state.\n\tconst { templates, saveTemplate, removeTemplate } = useTemplates(client);\n\t// This email's locales, for the tabs. The list is already fetched for saves.\n\tconst siblings = groupTemplatesByType(templates).find((g) => g.type === type)?.entries ?? [];\n\tconst [isDeleting, setIsDeleting] = useState(false);\n\tconst { revisions, rollback } = useTemplateRevisions(client, type, locale);\n\tconst [isSaving, setIsSaving] = useState(false);\n\tconst [saveError, setSaveError] = useState<FonderieApiError | null>(null);\n\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [active, setActive] = useState(true);\n\n\tconst { preview, isPreviewing, error: previewError, renderPreview } = useTemplatePreview(client);\n\t// The sample values, as JSON the operator can edit. Text rather than an\n\t// object so a half-typed value does not have to parse on every keystroke.\n\tconst [sampleJson, setSampleJson] = useState('{}');\n\tconst [sampleError, setSampleError] = useState<string | null>(null);\n\n\tuseEffect(() => {\n\t\tif (!template) return;\n\t\tsetSubject(template.subject ?? '');\n\t\tsetHtml(template.html ?? '');\n\t\tsetText(template.text);\n\t\tsetActive(template.active);\n\t}, [template]);\n\n\tconst run = useCallback(async () => {\n\t\tlet data: Record<string, unknown> = {};\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(sampleJson || '{}');\n\t\t\tif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\t\t\tsetSampleError(t('templates.editor.sampleNotObject'));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdata = parsed as Record<string, unknown>;\n\t\t\tsetSampleError(null);\n\t\t} catch {\n\t\t\tsetSampleError(t('templates.editor.sampleInvalid'));\n\t\t\treturn;\n\t\t}\n\t\tconst result = await renderPreview(\n\t\t\ttype,\n\t\t\t{ text, data, ...(subject ? { subject } : {}), ...(html ? { html } : {}) },\n\t\t\tlocale,\n\t\t);\n\t\t// The server reports which variables this content uses; seed the ones\n\t\t// that have no value yet with their own name, so the render shows where\n\t\t// each lands. Never overwrite something already typed.\n\t\tconst missing = result.variables.filter((v) => !IMPLICIT.has(v) && !(v in data));\n\t\tif (missing.length > 0) {\n\t\t\tconst seeded = { ...data, ...Object.fromEntries(missing.map((v) => [v, v])) };\n\t\t\tsetSampleJson(JSON.stringify(seeded, null, 2));\n\t\t}\n\t}, [renderPreview, type, text, subject, html, locale, sampleJson, t]);\n\n\t// The preview is always live: it renders once the template loads, then again\n\t// PREVIEW_DEBOUNCE_MS after the last edit to the content or the sample data.\n\t// Debounced, not per keystroke, so typing a paragraph costs one request.\n\t// `run` is a new function on every keystroke (it closes over the fields), so\n\t// the effect keys on the fields themselves and calls the latest `run`.\n\tconst runRef = useRef(run);\n\trunRef.current = run;\n\tconst loadedType = template ? `${type}:${locale ?? ''}` : null;\n\t// The fields ARE the trigger: each edit restarts the debounce. The effect\n\t// body reads the latest `run` through the ref, so it does not use them.\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: see above\n\tuseEffect(() => {\n\t\tif (!loadedType) return;\n\t\tconst timer = setTimeout(() => void runRef.current(), PREVIEW_DEBOUNCE_MS);\n\t\treturn () => clearTimeout(timer);\n\t}, [loadedType, subject, html, text, sampleJson]);\n\n\t// Saving what is already stored would only mint an identical version (the\n\t// server treats it as a no-op anyway), so Save waits for a real change.\n\tconst dirty =\n\t\t!!template &&\n\t\t(subject !== (template.subject ?? '') ||\n\t\t\thtml !== (template.html ?? '') ||\n\t\t\ttext !== template.text ||\n\t\t\tactive !== template.active);\n\n\tconst handleSubmit = async (event: FormEvent) => {\n\t\tevent.preventDefault();\n\t\tsetIsSaving(true);\n\t\tsetSaveError(null);\n\t\ttry {\n\t\t\tconst input: ISetTemplateInput = { text, active };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tif (template?.version !== undefined) input.ifVersion = template.version;\n\t\t\tawait saveTemplate(type, input, locale);\n\t\t\t// The list hook refreshes its own list; this screen renders the single\n\t\t\t// template, so re-read it too.\n\t\t\tawait refresh();\n\t\t\tonSaved?.();\n\t\t} catch (err) {\n\t\t\t// useTemplates normalizes every failure to FonderieApiError before throwing.\n\t\t\tsetSaveError(err as FonderieApiError);\n\t\t} finally {\n\t\t\tsetIsSaving(false);\n\t\t}\n\t};\n\n\tconst switchTo = (entry: ITemplateEntry) => {\n\t\tif (dirty && !window.confirm(t('templates.editor.discardChanges'))) return;\n\t\tonSelectLocale?.(entry);\n\t};\n\n\tconst handleDelete = async () => {\n\t\tconst message = locale\n\t\t\t? t('templates.editor.confirmDeleteLocale', { type, locale })\n\t\t\t: t('templates.editor.confirmDelete', { type });\n\t\tif (!window.confirm(message)) return;\n\t\tsetIsDeleting(true);\n\t\tsetSaveError(null);\n\t\ttry {\n\t\t\tawait removeTemplate(type, locale);\n\t\t\tonDeleted?.();\n\t\t} catch (err) {\n\t\t\tsetSaveError(err as FonderieApiError);\n\t\t} finally {\n\t\t\tsetIsDeleting(false);\n\t\t}\n\t};\n\n\tif (isLoading) return <p style={styles.status}>{t('templates.editor.loading')}</p>;\n\tif (error)\n\t\treturn (\n\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t{error.explanation}\n\t\t\t</p>\n\t\t);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>{type}</h1>\n\t\t\t{onSelectLocale || onAddLocale ? (\n\t\t\t\t<div style={styles.tabBar}>\n\t\t\t\t\t{onSelectLocale ? (\n\t\t\t\t\t\t// Each tab opens another stored row, so this is navigation, not an\n\t\t\t\t\t\t// ARIA tablist: plain buttons, the open one marked aria-current.\n\t\t\t\t\t\t<nav aria-label={t('templates.editor.locales')} style={styles.tabs}>\n\t\t\t\t\t\t\t{siblings.map((entry) => {\n\t\t\t\t\t\t\t\tconst selected = entry.locale === (locale ?? null);\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\tkey={entry.locale ?? ''}\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\taria-current={selected ? 'true' : undefined}\n\t\t\t\t\t\t\t\t\t\tonClick={() => (selected ? undefined : switchTo(entry))}\n\t\t\t\t\t\t\t\t\t\tstyle={selected ? styles.tabSelected : entry.active ? styles.tab : styles.tabInactive}\n\t\t\t\t\t\t\t\t\t\ttitle={entry.active ? undefined : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{entry.locale ?? t('templates.defaultChip')}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t</nav>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{onAddLocale ? (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tstyle={{ ...styles.rollbackButton, marginLeft: 'auto' }}\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tif (dirty && !window.confirm(t('templates.editor.discardChanges'))) return;\n\t\t\t\t\t\t\t\tonAddLocale(type, { locales: missingTemplateLocales(templates, type) });\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{t('templates.editor.addLocale')}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<p style={styles.meta}>\n\t\t\t\t<strong>{locale ?? t('templates.defaultLocale')}</strong> · v{template?.version ?? 1}\n\t\t\t\t{system ? ` · ${t('templates.editor.builtInNote')}` : ''}\n\t\t\t</p>\n\n\t\t\t<div style={styles.split}>\n\t\t\t\t<div style={styles.column}>\n\t\t\t\t\t<form style={styles.form} onSubmit={handleSubmit}>\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-subject\">\n\t\t\t\t\t\t\t{t('templates.subject')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tid=\"template-subject\"\n\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\tvalue={subject}\n\t\t\t\t\t\t\tonChange={(event) => setSubject(event.target.value)}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-html\">\n\t\t\t\t\t\t\t{t('templates.htmlBody')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\tid=\"template-html\"\n\t\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\t\tvalue={html}\n\t\t\t\t\t\t\tonChange={(event) => setHtml(event.target.value)}\n\t\t\t\t\t\t\trows={8}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-text\">\n\t\t\t\t\t\t\t{t('templates.textBody')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\tid=\"template-text\"\n\t\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\t\tvalue={text}\n\t\t\t\t\t\t\tonChange={(event) => setText(event.target.value)}\n\t\t\t\t\t\t\trows={4}\n\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.checkboxLabel}>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={active}\n\t\t\t\t\t\t\t\tonChange={(event) => setActive(event.target.checked)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t{t('templates.editor.active')}\n\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t{saveError?.reason === 'VERSION_CONFLICT' ? (\n\t\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t\t{t('templates.editor.conflict')}{' '}\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\tsetSaveError(null);\n\t\t\t\t\t\t\t\t\t\tvoid refresh();\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{t('common.reload')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : saveError ? (\n\t\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t\t{saveError.explanation}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : null}\n\n\t\t\t\t\t\t<div style={styles.saveRow}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tdisabled={isSaving || !dirty}\n\t\t\t\t\t\t\t\tstyle={dirty ? styles.button : styles.buttonDisabled}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isSaving ? t('common.saving') : t('common.save')}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{!dirty && !isSaving && (\n\t\t\t\t\t\t\t\t<span style={styles.meta}>{t('templates.editor.noChanges')}</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{!system ? (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tdisabled={isDeleting}\n\t\t\t\t\t\t\t\t\tonClick={() => void handleDelete()}\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t...styles.rollbackButton,\n\t\t\t\t\t\t\t\t\t\theight: 36,\n\t\t\t\t\t\t\t\t\t\tmarginLeft: 'auto',\n\t\t\t\t\t\t\t\t\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\t\t\t\t\t\t\t\t\tborderColor: 'color-mix(in srgb, var(--fonderie-danger,#e00) 40%, transparent)',\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{isDeleting ? t('common.deleting') : t('common.delete')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</form>\n\n\t\t\t\t\t<label style={styles.label} htmlFor=\"template-sample\">\n\t\t\t\t\t\t{t('templates.editor.sampleData')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tid=\"template-sample\"\n\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\tvalue={sampleJson}\n\t\t\t\t\t\tonChange={(event) => setSampleJson(event.target.value)}\n\t\t\t\t\t\trows={6}\n\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t/>\n\t\t\t\t\t{sampleError && (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{sampleError}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t<div style={styles.previewColumn}>\n\t\t\t\t\t<div style={styles.previewHeader}>\n\t\t\t\t\t\t<span style={styles.label}>{t('templates.editor.preview')}</span>\n\t\t\t\t\t\t<span style={styles.meta}>\n\t\t\t\t\t\t\t{isPreviewing ? t('templates.editor.rendering') : t('templates.editor.live')}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{previewError && (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{previewError.explanation}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t\t{preview?.subject && <p style={styles.previewSubject}>{preview.subject}</p>}\n\t\t\t\t\t{preview?.html ? (\n\t\t\t\t\t\t// sandbox=\"\" — no scripts, no same-origin. This is operator-authored\n\t\t\t\t\t\t// HTML and it must never execute in the dashboard's origin, which is\n\t\t\t\t\t\t// where the admin token lives.\n\t\t\t\t\t\t<iframe\n\t\t\t\t\t\t\ttitle={t('templates.editor.previewTitle')}\n\t\t\t\t\t\t\tstyle={styles.previewFrame}\n\t\t\t\t\t\t\tsandbox=\"\"\n\t\t\t\t\t\t\tsrcDoc={preview.html}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : preview ? (\n\t\t\t\t\t\t<pre style={styles.previewText}>{preview.text}</pre>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<p style={styles.meta}>{t('templates.editor.nothingRendered')}</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{revisions.length > 0 && (\n\t\t\t\t<div style={styles.revisions}>\n\t\t\t\t\t<h2 style={styles.subtitle}>{t('templates.editor.history')}</h2>\n\t\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t\t{revisions.map((rev) => (\n\t\t\t\t\t\t\t<li key={rev.version} style={styles.row}>\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\tv{rev.version} — {rev.actor ?? t('templates.editor.unknownActor')} —{' '}\n\t\t\t\t\t\t\t\t\t{formatAdminDate(rev.createdAt, uiLocale)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => rollback(rev.version)}\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{t('templates.editor.rollBack')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nconst tab: CSSProperties = {\n\theight: 32,\n\tpadding: '0 10px',\n\tbackground: 'none',\n\tborder: 'none',\n\tborderBottom: '2px solid transparent',\n\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\tfontSize: 13,\n\tfontFamily:\n\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\tcursor: 'pointer',\n};\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '8px 40px 64px', maxWidth: 1400, boxSizing: 'border-box' },\n\t// One tab per locale of this email, underlined like a document's tabs.\n\ttabBar: {\n\t\tdisplay: 'flex',\n\t\talignItems: 'flex-end',\n\t\tgap: 12,\n\t\tborderBottom: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tmargin: '4px 0 12px',\n\t\tpaddingBottom: 6,\n\t},\n\ttabs: { display: 'flex', flexWrap: 'wrap', gap: 4, marginBottom: -7 },\n\ttab,\n\ttabSelected: {\n\t\t...tab,\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontWeight: 600,\n\t\tborderBottomColor: 'var(--fonderie-text,#171717)',\n\t\tcursor: 'default',\n\t},\n\ttabInactive: { ...tab, opacity: 0.55, textDecoration: 'line-through' },\n\t// Two columns, always: the editor on the left, the rendered result on the\n\t// right. Stacking the preview under a long form hid it below the fold.\n\tsplit: {\n\t\tdisplay: 'grid',\n\t\tgridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)',\n\t\tgap: 24,\n\t\talignItems: 'start',\n\t},\n\tcolumn: { minWidth: 0, display: 'flex', flexDirection: 'column' },\n\t// Pinned while the form scrolls, so the preview stays in view as you edit.\n\tpreviewColumn: {\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tposition: 'sticky',\n\t\ttop: 16,\n\t},\n\tsaveRow: { display: 'flex', alignItems: 'center', gap: 12, marginTop: 16 },\n\tpreviewHeader: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tmarginTop: 12,\n\t},\n\tpreviewSubject: { fontSize: 14, fontWeight: 600, margin: '8px 0' },\n\tpreviewFrame: {\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t\twidth: '100%',\n\t\theight: 520,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t},\n\tpreviewText: {\n\t\twhiteSpace: 'pre-wrap',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tpadding: 12,\n\t\tfontSize: 13,\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: '8px 0 4px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\tmeta: { fontSize: 13, color: 'var(--fonderie-text-muted,#5c5c5c)', marginBottom: 20 },\n\tstatus: { padding: 24, textAlign: 'center', color: 'var(--fonderie-text-muted,#5c5c5c)' },\n\terror: {\n\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '8px 0',\n\t\tfontSize: 13.5,\n\t},\n\tform: {\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tgap: 4,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\tpadding: '6px 20px 20px',\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t},\n\tlabel: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },\n\tinput: {\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 13.5,\n\t\tfontFamily: 'inherit',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t},\n\ttextarea: {\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: 10,\n\t\tfontSize: 13,\n\t\tlineHeight: 1.55,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\tcheckboxLabel: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 12, fontSize: 14 },\n\tbutton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t},\n\tbuttonDisabled: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\talignSelf: 'flex-start',\n\t\topacity: 0.45,\n\t\tcursor: 'not-allowed',\n\t},\n\trevisions: { marginTop: 32 },\n\tsubtitle: {\n\t\tfontSize: 15,\n\t\tfontWeight: 600,\n\t\tmargin: '0 0 10px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t},\n\tlist: {\n\t\tlistStyle: 'none',\n\t\tpadding: 0,\n\t\tmargin: 0,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\toverflow: 'hidden',\n\t},\n\trow: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tgap: 12,\n\t\tpadding: '9px 16px',\n\t\tborderBottom: '1px solid var(--fonderie-border-light,#f5f5f5)',\n\t\tfontSize: 13,\n\t},\n\trollbackButton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 28,\n\t\tboxSizing: 'border-box',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 12.5,\n\t\tfontWeight: 500,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t},\n};\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\ttype ITemplateEntry,\n\tcreateAdminT,\n\tgroupTemplatesByType,\n} from '@fonderie/client';\nimport { useTemplates } from '@fonderie/react-courier-admin';\nimport type { CSSProperties } from 'react';\n\nexport interface ITemplateListScreenProps {\n\tclient: CourierAdminClient;\n\t/**\n\t * Receives the whole row — type AND locale, and whether it is built-in. The\n\t * list shows one row per email: clicking it opens the default locale, a\n\t * locale chip opens that locale.\n\t */\n\tonSelectTemplate?: (template: ITemplateEntry) => void;\n\t/** Shows a \"New template\" button. Receives the locales in use, to suggest. */\n\tonCreateTemplate?: (context: { locales: string[] }) => void;\n\t/** The console's language. Default English. */\n\tlocale?: AdminLocale | undefined;\n}\n\nexport function TemplateListScreen({\n\tclient,\n\tonSelectTemplate,\n\tonCreateTemplate,\n\tlocale,\n}: ITemplateListScreenProps) {\n\tconst t = createAdminT(locale);\n\tconst { templates, isLoading, error } = useTemplates(client);\n\tconst groups = groupTemplatesByType(templates);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<div\n\t\t\t\tstyle={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16 }}\n\t\t\t>\n\t\t\t\t<h1 style={styles.title}>{t('templates.list.title')}</h1>\n\t\t\t\t{onCreateTemplate ? (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\tonCreateTemplate({\n\t\t\t\t\t\t\t\tlocales: [\n\t\t\t\t\t\t\t\t\t...new Set(templates.map((row) => row.locale).filter((l): l is string => !!l)),\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstyle={styles.newButton}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t('templates.list.newTemplate')}\n\t\t\t\t\t</button>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t\t<p style={styles.hint}>{t('templates.list.hint')}</p>\n\t\t\t{isLoading ? (\n\t\t\t\t<p style={styles.status}>{t('templates.list.loading')}</p>\n\t\t\t) : error ? (\n\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t{error.explanation}\n\t\t\t\t</p>\n\t\t\t) : (\n\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t{groups.map((group) => (\n\t\t\t\t\t\t<li key={group.type} style={styles.row}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => onSelectTemplate?.(group.primary)}\n\t\t\t\t\t\t\t\tstyle={styles.rowButton}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span style={styles.type}>{group.type}</span>\n\t\t\t\t\t\t\t\t{group.system ? (\n\t\t\t\t\t\t\t\t\t<span style={styles.builtIn}>{t('templates.builtInBadge')}</span>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t<span style={group.primary.active ? styles.active : styles.inactive}>\n\t\t\t\t\t\t\t\t\t<span style={styles.dot} />\n\t\t\t\t\t\t\t\t\t{group.primary.active ? t('common.status.active') : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{/* Siblings of the row button, not inside it: a button cannot hold buttons. */}\n\t\t\t\t\t\t\t<span style={styles.chips}>\n\t\t\t\t\t\t\t\t{group.entries.map((entry) => {\n\t\t\t\t\t\t\t\t\tconst name = entry.locale ?? t('templates.defaultChip');\n\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\tkey={entry.locale ?? ''}\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => onSelectTemplate?.(entry)}\n\t\t\t\t\t\t\t\t\t\t\tstyle={entry.active ? styles.chip : styles.chipInactive}\n\t\t\t\t\t\t\t\t\t\t\taria-label={t('templates.list.openLocale', { type: group.type, locale: name })}\n\t\t\t\t\t\t\t\t\t\t\ttitle={entry.active ? undefined : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{name}\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t))}\n\t\t\t\t</ul>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// Matches @fonderie/react-admin-screens' shell look with local values: this\n// package sits below the admin screens and cannot import their blocks.\nconst pill: CSSProperties = {\n\tdisplay: 'inline-flex',\n\talignItems: 'center',\n\tgap: 6,\n\tborderRadius: 999,\n\tpadding: '2px 9px 2px 8px',\n\tfontSize: 12,\n\tfontWeight: 600,\n\tlineHeight: 1.5,\n\tminWidth: 64,\n};\n\n// One per locale the email exists in. Monospace: they are language tags.\nconst chip: CSSProperties = {\n\theight: 24,\n\tboxSizing: 'border-box',\n\tpadding: '0 8px',\n\tborderRadius: 6,\n\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\tbackground: 'var(--fonderie-surface,#fff)',\n\tcolor: 'var(--fonderie-text,#171717)',\n\tfontSize: 12,\n\tfontFamily:\n\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\tcursor: 'pointer',\n};\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '32px 40px 64px', maxWidth: 1160, boxSizing: 'border-box' },\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: 0,\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\thint: { fontSize: 13.5, color: 'var(--fonderie-text-muted,#5c5c5c)', margin: '4px 0 24px' },\n\tstatus: { padding: '24px 0', color: 'var(--fonderie-text-muted,#5c5c5c)', fontSize: 13.5 },\n\terror: { color: 'var(--fonderie-danger,#e00)', marginBottom: 12, fontSize: 14 },\n\tlist: {\n\t\tlistStyle: 'none',\n\t\tpadding: 0,\n\t\tmargin: 0,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\toverflow: 'hidden',\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t},\n\trow: {\n\t\tdisplay: 'flex',\n\t\talignItems: 'center',\n\t\tgap: 8,\n\t\tpaddingRight: 16,\n\t\tborderBottom: '1px solid var(--fonderie-border-light,#f5f5f5)',\n\t},\n\trowButton: {\n\t\tflex: 1,\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tgap: 16,\n\t\tpadding: '13px 16px',\n\t\tbackground: 'none',\n\t\tborder: 'none',\n\t\tcursor: 'pointer',\n\t\ttextAlign: 'left',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily: 'inherit',\n\t},\n\ttype: {\n\t\tfontSize: 13,\n\t\tfontWeight: 600,\n\t\tflex: 1,\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\tchips: { display: 'flex', flexWrap: 'wrap', justifyContent: 'flex-end', gap: 6 },\n\tchip,\n\tchipInactive: { ...chip, opacity: 0.5, textDecoration: 'line-through' },\n\tdot: { width: 6, height: 6, borderRadius: 999, background: 'currentColor' },\n\tbuiltIn: {\n\t\tfontSize: 11,\n\t\tfontWeight: 500,\n\t\tpadding: '1px 7px',\n\t\tborderRadius: 999,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t},\n\tnewButton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 32,\n\t\tboxSizing: 'border-box',\n\t\tborderRadius: 6,\n\t\tpadding: '0 12px',\n\t\tfontSize: 13,\n\t\tfontWeight: 500,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t},\n\tactive: {\n\t\t...pill,\n\t\tcolor: 'var(--fonderie-accent-strong,#009767)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-accent-strong,#009767) 13%, transparent)',\n\t},\n\tinactive: {\n\t\t...pill,\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-text-muted,#5c5c5c) 10%, transparent)',\n\t},\n};\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\tFonderieApiError,\n\ttype ISetTemplateInput,\n\tcreateAdminT,\n} from '@fonderie/client';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useEffect, useState } from 'react';\n\nexport interface ITemplateCreateScreenProps {\n\tclient: CourierAdminClient;\n\t/**\n\t * Given ⇒ add a LOCALE of this existing template: the type is fixed and the\n\t * form starts from its default-locale copy. Omitted ⇒ a brand-new template\n\t * type (one the app sends that has no stored copy yet).\n\t */\n\ttype?: string;\n\t/** Locales already in use, offered as suggestions. */\n\tlocales?: string[];\n\t/** Called with what was created, so the caller can open it in the editor. */\n\tonCreated?: (created: { type: string; locale: string | null }) => void;\n\t/** The console's language. Default English. */\n\tlocale?: AdminLocale | undefined;\n}\n\nconst TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;\nconst LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;\n\n// Creating is its own screen: the editor loads an existing row, and a new\n// template or locale has none yet. Once saved, the editor takes over.\nexport function TemplateCreateScreen({\n\tclient,\n\ttype,\n\tlocales = [],\n\tonCreated,\n\tlocale,\n}: ITemplateCreateScreenProps) {\n\tconst tr = createAdminT(locale);\n\tconst addingLocale = type !== undefined;\n\tconst [newType, setNewType] = useState('');\n\tconst [targetLocale, setTargetLocale] = useState('');\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [error, setError] = useState<string | null>(null);\n\tconst [busy, setBusy] = useState(false);\n\n\t// A new locale starts from the default copy, so translating is editing.\n\tuseEffect(() => {\n\t\tif (!type) return;\n\t\tclient\n\t\t\t.getTemplate(type, null)\n\t\t\t.then(({ result }) => {\n\t\t\t\tsetSubject(result.subject ?? '');\n\t\t\t\tsetHtml(result.html ?? '');\n\t\t\t\tsetText(result.text);\n\t\t\t})\n\t\t\t.catch(() => undefined);\n\t}, [client, type]);\n\n\tconst submit = async (e: FormEvent) => {\n\t\te.preventDefault();\n\t\tsetError(null);\n\t\tconst t = addingLocale ? (type as string) : newType.trim();\n\t\tconst l = targetLocale.trim() || null;\n\t\tif (!TYPE_RE.test(t)) return setError(tr('templates.create.errorType'));\n\t\tif (addingLocale && !l) return setError(tr('templates.create.errorLocaleRequired'));\n\t\tif (l && !LOCALE_RE.test(l)) return setError(tr('templates.create.errorLocale'));\n\t\tif (!text.trim()) return setError(tr('templates.create.errorText'));\n\t\tsetBusy(true);\n\t\ttry {\n\t\t\t// Refuse to overwrite: creating an existing row would replace it.\n\t\t\tconst exists = await client.getTemplate(t, l).then(\n\t\t\t\t() => true,\n\t\t\t\t(err: unknown) => !(err instanceof FonderieApiError && err.status === 404),\n\t\t\t);\n\t\t\tif (exists) {\n\t\t\t\tsetError(\n\t\t\t\t\tl\n\t\t\t\t\t\t? tr('templates.create.existsLocale', { type: t, locale: l })\n\t\t\t\t\t\t: tr('templates.create.exists', { type: t }),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst input: ISetTemplateInput = { text, active: true };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tawait client.setTemplate(t, input, l);\n\t\t\tonCreated?.({ type: t, locale: l });\n\t\t} catch (err) {\n\t\t\tsetError(err instanceof FonderieApiError ? err.explanation : String(err));\n\t\t} finally {\n\t\t\tsetBusy(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>\n\t\t\t\t{addingLocale\n\t\t\t\t\t? tr('templates.create.titleLocale', { type: type as string })\n\t\t\t\t\t: tr('templates.create.titleNew')}\n\t\t\t</h1>\n\t\t\t<p style={styles.meta}>\n\t\t\t\t{addingLocale ? tr('templates.create.hintLocale') : tr('templates.create.hintNew')}\n\t\t\t</p>\n\t\t\t<form style={styles.form} onSubmit={(e) => void submit(e)}>\n\t\t\t\t{!addingLocale ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-type\">\n\t\t\t\t\t\t\t{tr('templates.create.type')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tid=\"new-template-type\"\n\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\tvalue={newType}\n\t\t\t\t\t\t\tplaceholder=\"weekly-digest\"\n\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\tonChange={(e) => setNewType(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</>\n\t\t\t\t) : null}\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-locale\">\n\t\t\t\t\t{addingLocale ? tr('templates.create.locale') : tr('templates.create.localeOptional')}\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\tid=\"new-template-locale\"\n\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\tvalue={targetLocale}\n\t\t\t\t\tplaceholder=\"fr\"\n\t\t\t\t\tlist=\"new-template-locales\"\n\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\tspellCheck={false}\n\t\t\t\t\tonChange={(e) => setTargetLocale(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<datalist id=\"new-template-locales\">\n\t\t\t\t\t{[...new Set(locales)].map((l) => (\n\t\t\t\t\t\t<option key={l} value={l} />\n\t\t\t\t\t))}\n\t\t\t\t</datalist>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-subject\">\n\t\t\t\t\t{tr('templates.subject')}\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\tid=\"new-template-subject\"\n\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\tvalue={subject}\n\t\t\t\t\tonChange={(e) => setSubject(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-html\">\n\t\t\t\t\t{tr('templates.htmlBody')}\n\t\t\t\t</label>\n\t\t\t\t<textarea\n\t\t\t\t\tid=\"new-template-html\"\n\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 160 }}\n\t\t\t\t\tvalue={html}\n\t\t\t\t\tonChange={(e) => setHtml(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-text\">\n\t\t\t\t\t{tr('templates.textBody')}\n\t\t\t\t</label>\n\t\t\t\t<textarea\n\t\t\t\t\tid=\"new-template-text\"\n\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 100 }}\n\t\t\t\t\tvalue={text}\n\t\t\t\t\tonChange={(e) => setText(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t{error ? (\n\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t{error}\n\t\t\t\t\t</p>\n\t\t\t\t) : null}\n\t\t\t\t<button type=\"submit\" disabled={busy} style={styles.button}>\n\t\t\t\t\t{busy\n\t\t\t\t\t\t? tr('templates.create.creating')\n\t\t\t\t\t\t: addingLocale\n\t\t\t\t\t\t\t? tr('templates.create.submitLocale')\n\t\t\t\t\t\t\t: tr('templates.create.submitNew')}\n\t\t\t\t</button>\n\t\t\t</form>\n\t\t</div>\n\t);\n}\n\nconst MONO =\n\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)';\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '8px 40px 64px', maxWidth: 760, boxSizing: 'border-box' },\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: '8px 0 4px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\tmeta: {\n\t\tfontSize: 13,\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t\tmarginBottom: 20,\n\t\tlineHeight: 1.5,\n\t},\n\tform: {\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tgap: 4,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\tpadding: '6px 20px 20px',\n\t},\n\tlabel: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },\n\tinput: {\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 13.5,\n\t\tfontFamily: 'inherit',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t},\n\ttextarea: {\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: 10,\n\t\tfontSize: 13,\n\t\tlineHeight: 1.55,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily: MONO,\n\t},\n\terror: {\n\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '8px 0',\n\t\tfontSize: 13.5,\n\t},\n\tbutton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t\tmarginTop: 16,\n\t},\n};\n"],"mappings":"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","names":["import_client","import_react_courier_admin","import_jsx_runtime","styles","import_client","import_react","import_jsx_runtime","styles"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/screens/TemplateEditorScreen.tsx","../src/screens/TemplateListScreen.tsx","../src/screens/TemplateCreateScreen.tsx"],"sourcesContent":["export type {\n\tITemplateCreateScreenProps,\n\tITemplateEditorScreenProps,\n\tITemplateListScreenProps,\n} from './screens';\nexport { TemplateCreateScreen, TemplateEditorScreen, TemplateListScreen } from './screens';\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\ttype FonderieApiError,\n\ttype ISetTemplateInput,\n\ttype ITemplateEntry,\n\tcreateAdminT,\n\tformatAdminDate,\n\tgroupTemplatesByType,\n\tmissingTemplateLocales,\n} from '@fonderie/client';\nimport {\n\tuseTemplate,\n\tuseTemplatePreview,\n\tuseTemplateRevisions,\n\tuseTemplates,\n} from '@fonderie/react-courier-admin';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\n// Implicit variables the layout injects — an operator never supplies these, so\n// offering them as fields would just be noise.\nconst IMPLICIT = new Set(['subject', 'preheader', 'brandName']);\n\nconst PREVIEW_DEBOUNCE_MS = 500;\n\nexport interface ITemplateEditorScreenProps {\n\tclient: CourierAdminClient;\n\ttype: string;\n\tlocale?: string | null;\n\tonSaved?: () => void;\n\t/**\n\t * A built-in email's default-locale row (the list's `system` flag): it can\n\t * be edited and rolled back, never deleted — Delete is not offered.\n\t */\n\tsystem?: boolean;\n\t/** Called after a delete; the template no longer exists. */\n\tonDeleted?: () => void;\n\t/**\n\t * Shows \"Add locale\": create a translation of this template. `locales` are\n\t * the ones the app uses elsewhere that this email lacks — suggest them first.\n\t */\n\tonAddLocale?: (type: string, context: { locales: string[] }) => void;\n\t/**\n\t * Shows a tab per locale this email exists in; receives the row to open.\n\t * Unsaved edits are confirmed before switching away.\n\t */\n\tonSelectLocale?: (template: ITemplateEntry) => void;\n\t/**\n\t * The CONSOLE's language (default English). Named `uiLocale` because\n\t * `locale` here is the template's own locale.\n\t */\n\tuiLocale?: AdminLocale | undefined;\n}\n\nexport function TemplateEditorScreen({\n\tclient,\n\ttype,\n\tlocale,\n\tonSaved,\n\tsystem = false,\n\tonDeleted,\n\tonAddLocale,\n\tonSelectLocale,\n\tuiLocale,\n}: ITemplateEditorScreenProps) {\n\t// Memoized: `run` below depends on it, and a new translator per render would\n\t// restart the preview debounce on every render.\n\tconst t = useMemo(() => createAdminT(uiLocale), [uiLocale]);\n\tconst { template, isLoading, error, refresh } = useTemplate(client, type, locale);\n\t// Saves go through the list hook (which re-fetches the template list after\n\t// each write); mounting it adds a template-list fetch to this single-template\n\t// editor — acceptable for an admin dashboard. Its isLoading/error track that\n\t// list fetch, so the save action keeps local state.\n\tconst { templates, saveTemplate, removeTemplate } = useTemplates(client);\n\t// This email's locales, for the tabs. The list is already fetched for saves.\n\tconst siblings = groupTemplatesByType(templates).find((g) => g.type === type)?.entries ?? [];\n\tconst [isDeleting, setIsDeleting] = useState(false);\n\tconst { revisions, rollback } = useTemplateRevisions(client, type, locale);\n\tconst [isSaving, setIsSaving] = useState(false);\n\tconst [saveError, setSaveError] = useState<FonderieApiError | null>(null);\n\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [active, setActive] = useState(true);\n\n\tconst { preview, isPreviewing, error: previewError, renderPreview } = useTemplatePreview(client);\n\t// The sample values, as JSON the operator can edit. Text rather than an\n\t// object so a half-typed value does not have to parse on every keystroke.\n\tconst [sampleJson, setSampleJson] = useState('{}');\n\tconst [sampleError, setSampleError] = useState<string | null>(null);\n\n\tuseEffect(() => {\n\t\tif (!template) return;\n\t\tsetSubject(template.subject ?? '');\n\t\tsetHtml(template.html ?? '');\n\t\tsetText(template.text);\n\t\tsetActive(template.active);\n\t}, [template]);\n\n\tconst run = useCallback(async () => {\n\t\tlet data: Record<string, unknown> = {};\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(sampleJson || '{}');\n\t\t\tif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\t\t\tsetSampleError(t('templates.editor.sampleNotObject'));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdata = parsed as Record<string, unknown>;\n\t\t\tsetSampleError(null);\n\t\t} catch {\n\t\t\tsetSampleError(t('templates.editor.sampleInvalid'));\n\t\t\treturn;\n\t\t}\n\t\tconst result = await renderPreview(\n\t\t\ttype,\n\t\t\t{ text, data, ...(subject ? { subject } : {}), ...(html ? { html } : {}) },\n\t\t\tlocale,\n\t\t);\n\t\t// The server reports which variables this content uses; seed the ones\n\t\t// that have no value yet with their own name, so the render shows where\n\t\t// each lands. Never overwrite something already typed.\n\t\tconst missing = result.variables.filter((v) => !IMPLICIT.has(v) && !(v in data));\n\t\tif (missing.length > 0) {\n\t\t\tconst seeded = { ...data, ...Object.fromEntries(missing.map((v) => [v, v])) };\n\t\t\tsetSampleJson(JSON.stringify(seeded, null, 2));\n\t\t}\n\t}, [renderPreview, type, text, subject, html, locale, sampleJson, t]);\n\n\t// The preview is always live: it renders once the template loads, then again\n\t// PREVIEW_DEBOUNCE_MS after the last edit to the content or the sample data.\n\t// Debounced, not per keystroke, so typing a paragraph costs one request.\n\t// `run` is a new function on every keystroke (it closes over the fields), so\n\t// the effect keys on the fields themselves and calls the latest `run`.\n\tconst runRef = useRef(run);\n\trunRef.current = run;\n\tconst loadedType = template ? `${type}:${locale ?? ''}` : null;\n\t// The fields ARE the trigger: each edit restarts the debounce. The effect\n\t// body reads the latest `run` through the ref, so it does not use them.\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: see above\n\tuseEffect(() => {\n\t\tif (!loadedType) return;\n\t\tconst timer = setTimeout(() => void runRef.current(), PREVIEW_DEBOUNCE_MS);\n\t\treturn () => clearTimeout(timer);\n\t}, [loadedType, subject, html, text, sampleJson]);\n\n\t// Saving what is already stored would only mint an identical version (the\n\t// server treats it as a no-op anyway), so Save waits for a real change.\n\tconst dirty =\n\t\t!!template &&\n\t\t(subject !== (template.subject ?? '') ||\n\t\t\thtml !== (template.html ?? '') ||\n\t\t\ttext !== template.text ||\n\t\t\tactive !== template.active);\n\n\tconst handleSubmit = async (event: FormEvent) => {\n\t\tevent.preventDefault();\n\t\tsetIsSaving(true);\n\t\tsetSaveError(null);\n\t\ttry {\n\t\t\tconst input: ISetTemplateInput = { text, active };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tif (template?.version !== undefined) input.ifVersion = template.version;\n\t\t\tawait saveTemplate(type, input, locale);\n\t\t\t// The list hook refreshes its own list; this screen renders the single\n\t\t\t// template, so re-read it too.\n\t\t\tawait refresh();\n\t\t\tonSaved?.();\n\t\t} catch (err) {\n\t\t\t// useTemplates normalizes every failure to FonderieApiError before throwing.\n\t\t\tsetSaveError(err as FonderieApiError);\n\t\t} finally {\n\t\t\tsetIsSaving(false);\n\t\t}\n\t};\n\n\tconst switchTo = (entry: ITemplateEntry) => {\n\t\tif (dirty && !window.confirm(t('templates.editor.discardChanges'))) return;\n\t\tonSelectLocale?.(entry);\n\t};\n\n\tconst handleDelete = async () => {\n\t\tconst message = locale\n\t\t\t? t('templates.editor.confirmDeleteLocale', { type, locale })\n\t\t\t: t('templates.editor.confirmDelete', { type });\n\t\tif (!window.confirm(message)) return;\n\t\tsetIsDeleting(true);\n\t\tsetSaveError(null);\n\t\ttry {\n\t\t\tawait removeTemplate(type, locale);\n\t\t\tonDeleted?.();\n\t\t} catch (err) {\n\t\t\tsetSaveError(err as FonderieApiError);\n\t\t} finally {\n\t\t\tsetIsDeleting(false);\n\t\t}\n\t};\n\n\tif (isLoading) return <p style={styles.status}>{t('templates.editor.loading')}</p>;\n\tif (error)\n\t\treturn (\n\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t{error.explanation}\n\t\t\t</p>\n\t\t);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>{type}</h1>\n\t\t\t{onSelectLocale || onAddLocale ? (\n\t\t\t\t<div style={styles.tabBar}>\n\t\t\t\t\t{onSelectLocale ? (\n\t\t\t\t\t\t// Each tab opens another stored row, so this is navigation, not an\n\t\t\t\t\t\t// ARIA tablist: plain buttons, the open one marked aria-current.\n\t\t\t\t\t\t<nav aria-label={t('templates.editor.locales')} style={styles.tabs}>\n\t\t\t\t\t\t\t{siblings.map((entry) => {\n\t\t\t\t\t\t\t\tconst selected = entry.locale === (locale ?? null);\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\tkey={entry.locale ?? ''}\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\taria-current={selected ? 'true' : undefined}\n\t\t\t\t\t\t\t\t\t\tonClick={() => (selected ? undefined : switchTo(entry))}\n\t\t\t\t\t\t\t\t\t\tstyle={selected ? styles.tabSelected : entry.active ? styles.tab : styles.tabInactive}\n\t\t\t\t\t\t\t\t\t\ttitle={entry.active ? undefined : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{entry.locale ?? t('templates.defaultChip')}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t</nav>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{onAddLocale ? (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tstyle={{ ...styles.rollbackButton, marginLeft: 'auto' }}\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tif (dirty && !window.confirm(t('templates.editor.discardChanges'))) return;\n\t\t\t\t\t\t\t\tonAddLocale(type, { locales: missingTemplateLocales(templates, type) });\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{t('templates.editor.addLocale')}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<p style={styles.meta}>\n\t\t\t\t<strong>{locale ?? t('templates.defaultLocale')}</strong> · v{template?.version ?? 1}\n\t\t\t\t{system ? ` · ${t('templates.editor.builtInNote')}` : ''}\n\t\t\t</p>\n\n\t\t\t<div style={styles.split}>\n\t\t\t\t<div style={styles.column}>\n\t\t\t\t\t<form style={styles.form} onSubmit={handleSubmit}>\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-subject\">\n\t\t\t\t\t\t\t{t('templates.subject')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tid=\"template-subject\"\n\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\tvalue={subject}\n\t\t\t\t\t\t\tonChange={(event) => setSubject(event.target.value)}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-html\">\n\t\t\t\t\t\t\t{t('templates.htmlBody')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\tid=\"template-html\"\n\t\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\t\tvalue={html}\n\t\t\t\t\t\t\tonChange={(event) => setHtml(event.target.value)}\n\t\t\t\t\t\t\trows={8}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-text\">\n\t\t\t\t\t\t\t{t('templates.textBody')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\tid=\"template-text\"\n\t\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\t\tvalue={text}\n\t\t\t\t\t\t\tonChange={(event) => setText(event.target.value)}\n\t\t\t\t\t\t\trows={4}\n\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.checkboxLabel}>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={active}\n\t\t\t\t\t\t\t\tonChange={(event) => setActive(event.target.checked)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t{t('templates.editor.active')}\n\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t{saveError?.reason === 'VERSION_CONFLICT' ? (\n\t\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t\t{t('templates.editor.conflict')}{' '}\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\tsetSaveError(null);\n\t\t\t\t\t\t\t\t\t\tvoid refresh();\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{t('common.reload')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : saveError ? (\n\t\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t\t{saveError.explanation}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : null}\n\n\t\t\t\t\t\t<div style={styles.saveRow}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tdisabled={isSaving || !dirty}\n\t\t\t\t\t\t\t\tstyle={dirty ? styles.button : styles.buttonDisabled}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isSaving ? t('common.saving') : t('common.save')}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{!dirty && !isSaving && (\n\t\t\t\t\t\t\t\t<span style={styles.meta}>{t('templates.editor.noChanges')}</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{!system ? (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tdisabled={isDeleting}\n\t\t\t\t\t\t\t\t\tonClick={() => void handleDelete()}\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t...styles.rollbackButton,\n\t\t\t\t\t\t\t\t\t\theight: 36,\n\t\t\t\t\t\t\t\t\t\tmarginLeft: 'auto',\n\t\t\t\t\t\t\t\t\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\t\t\t\t\t\t\t\t\tborderColor: 'color-mix(in srgb, var(--fonderie-danger,#e00) 40%, transparent)',\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{isDeleting ? t('common.deleting') : t('common.delete')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</form>\n\n\t\t\t\t\t<label style={styles.label} htmlFor=\"template-sample\">\n\t\t\t\t\t\t{t('templates.editor.sampleData')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tid=\"template-sample\"\n\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\tvalue={sampleJson}\n\t\t\t\t\t\tonChange={(event) => setSampleJson(event.target.value)}\n\t\t\t\t\t\trows={6}\n\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t/>\n\t\t\t\t\t{sampleError && (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{sampleError}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t<div style={styles.previewColumn}>\n\t\t\t\t\t<div style={styles.previewHeader}>\n\t\t\t\t\t\t<span style={styles.label}>{t('templates.editor.preview')}</span>\n\t\t\t\t\t\t<span style={styles.meta}>\n\t\t\t\t\t\t\t{isPreviewing ? t('templates.editor.rendering') : t('templates.editor.live')}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{previewError && (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{previewError.explanation}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t\t{preview?.subject && <p style={styles.previewSubject}>{preview.subject}</p>}\n\t\t\t\t\t{preview?.html ? (\n\t\t\t\t\t\t// sandbox=\"\" — no scripts, no same-origin. This is operator-authored\n\t\t\t\t\t\t// HTML and it must never execute in the dashboard's origin, which is\n\t\t\t\t\t\t// where the admin token lives.\n\t\t\t\t\t\t<iframe\n\t\t\t\t\t\t\ttitle={t('templates.editor.previewTitle')}\n\t\t\t\t\t\t\tstyle={styles.previewFrame}\n\t\t\t\t\t\t\tsandbox=\"\"\n\t\t\t\t\t\t\tsrcDoc={preview.html}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : preview ? (\n\t\t\t\t\t\t<pre style={styles.previewText}>{preview.text}</pre>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<p style={styles.meta}>{t('templates.editor.nothingRendered')}</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{revisions.length > 0 && (\n\t\t\t\t<div style={styles.revisions}>\n\t\t\t\t\t<h2 style={styles.subtitle}>{t('templates.editor.history')}</h2>\n\t\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t\t{revisions.map((rev) => (\n\t\t\t\t\t\t\t<li key={rev.version} style={styles.row}>\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\tv{rev.version} — {rev.actor ?? t('templates.editor.unknownActor')} —{' '}\n\t\t\t\t\t\t\t\t\t{formatAdminDate(rev.createdAt, uiLocale)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => rollback(rev.version)}\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{t('templates.editor.rollBack')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nconst tab: CSSProperties = {\n\theight: 32,\n\tpadding: '0 10px',\n\tbackground: 'none',\n\tborder: 'none',\n\tborderBottom: '2px solid transparent',\n\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\tfontSize: 13,\n\tfontFamily:\n\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\tcursor: 'pointer',\n};\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '8px 40px 64px', maxWidth: 1400, boxSizing: 'border-box' },\n\t// One tab per locale of this email, underlined like a document's tabs.\n\ttabBar: {\n\t\tdisplay: 'flex',\n\t\talignItems: 'flex-end',\n\t\tgap: 12,\n\t\tborderBottom: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tmargin: '4px 0 12px',\n\t\tpaddingBottom: 6,\n\t},\n\ttabs: { display: 'flex', flexWrap: 'wrap', gap: 4, marginBottom: -7 },\n\ttab,\n\ttabSelected: {\n\t\t...tab,\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontWeight: 600,\n\t\tborderBottomColor: 'var(--fonderie-text,#171717)',\n\t\tcursor: 'default',\n\t},\n\ttabInactive: { ...tab, opacity: 0.55, textDecoration: 'line-through' },\n\t// Two columns, always: the editor on the left, the rendered result on the\n\t// right. Stacking the preview under a long form hid it below the fold.\n\tsplit: {\n\t\tdisplay: 'grid',\n\t\tgridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)',\n\t\tgap: 24,\n\t\talignItems: 'start',\n\t},\n\tcolumn: { minWidth: 0, display: 'flex', flexDirection: 'column' },\n\t// Pinned while the form scrolls, so the preview stays in view as you edit.\n\tpreviewColumn: {\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tposition: 'sticky',\n\t\ttop: 16,\n\t},\n\tsaveRow: { display: 'flex', alignItems: 'center', gap: 12, marginTop: 16 },\n\tpreviewHeader: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tmarginTop: 12,\n\t},\n\tpreviewSubject: { fontSize: 14, fontWeight: 600, margin: '8px 0' },\n\tpreviewFrame: {\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t\twidth: '100%',\n\t\theight: 520,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t},\n\tpreviewText: {\n\t\twhiteSpace: 'pre-wrap',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tpadding: 12,\n\t\tfontSize: 13,\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: '8px 0 4px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\tmeta: { fontSize: 13, color: 'var(--fonderie-text-muted,#5c5c5c)', marginBottom: 20 },\n\tstatus: { padding: 24, textAlign: 'center', color: 'var(--fonderie-text-muted,#5c5c5c)' },\n\terror: {\n\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '8px 0',\n\t\tfontSize: 13.5,\n\t},\n\tform: {\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tgap: 4,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\tpadding: '6px 20px 20px',\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t},\n\tlabel: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },\n\tinput: {\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 13.5,\n\t\tfontFamily: 'inherit',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t},\n\ttextarea: {\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: 10,\n\t\tfontSize: 13,\n\t\tlineHeight: 1.55,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\tcheckboxLabel: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 12, fontSize: 14 },\n\tbutton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t},\n\tbuttonDisabled: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\talignSelf: 'flex-start',\n\t\topacity: 0.45,\n\t\tcursor: 'not-allowed',\n\t},\n\trevisions: { marginTop: 32 },\n\tsubtitle: {\n\t\tfontSize: 15,\n\t\tfontWeight: 600,\n\t\tmargin: '0 0 10px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t},\n\tlist: {\n\t\tlistStyle: 'none',\n\t\tpadding: 0,\n\t\tmargin: 0,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\toverflow: 'hidden',\n\t},\n\trow: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tgap: 12,\n\t\tpadding: '9px 16px',\n\t\tborderBottom: '1px solid var(--fonderie-border-light,#f5f5f5)',\n\t\tfontSize: 13,\n\t},\n\trollbackButton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 28,\n\t\tboxSizing: 'border-box',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 12.5,\n\t\tfontWeight: 500,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t},\n};\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\ttype ITemplateEntry,\n\tcreateAdminT,\n\tgroupTemplatesByType,\n} from '@fonderie/client';\nimport { useTemplates } from '@fonderie/react-courier-admin';\nimport type { CSSProperties } from 'react';\n\nexport interface ITemplateListScreenProps {\n\tclient: CourierAdminClient;\n\t/**\n\t * Receives the whole row — type AND locale, and whether it is built-in. The\n\t * list shows one row per email: clicking it opens the default locale, a\n\t * locale chip opens that locale.\n\t */\n\tonSelectTemplate?: (template: ITemplateEntry) => void;\n\t/** Shows a \"New template\" button. Receives the locales in use, to suggest. */\n\tonCreateTemplate?: (context: { locales: string[] }) => void;\n\t/** The console's language. Default English. */\n\tlocale?: AdminLocale | undefined;\n}\n\nexport function TemplateListScreen({\n\tclient,\n\tonSelectTemplate,\n\tonCreateTemplate,\n\tlocale,\n}: ITemplateListScreenProps) {\n\tconst t = createAdminT(locale);\n\tconst { templates, isLoading, error } = useTemplates(client);\n\tconst groups = groupTemplatesByType(templates);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<div\n\t\t\t\tstyle={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16 }}\n\t\t\t>\n\t\t\t\t<h1 style={styles.title}>{t('templates.list.title')}</h1>\n\t\t\t\t{onCreateTemplate ? (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\tonCreateTemplate({\n\t\t\t\t\t\t\t\tlocales: [\n\t\t\t\t\t\t\t\t\t...new Set(templates.map((row) => row.locale).filter((l): l is string => !!l)),\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstyle={styles.newButton}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t('templates.list.newTemplate')}\n\t\t\t\t\t</button>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t\t<p style={styles.hint}>{t('templates.list.hint')}</p>\n\t\t\t{isLoading ? (\n\t\t\t\t<p style={styles.status}>{t('templates.list.loading')}</p>\n\t\t\t) : error ? (\n\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t{error.explanation}\n\t\t\t\t</p>\n\t\t\t) : (\n\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t{groups.map((group) => (\n\t\t\t\t\t\t<li key={group.type} style={styles.row}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => onSelectTemplate?.(group.primary)}\n\t\t\t\t\t\t\t\tstyle={styles.rowButton}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span style={styles.type}>{group.type}</span>\n\t\t\t\t\t\t\t\t{group.system ? (\n\t\t\t\t\t\t\t\t\t<span style={styles.builtIn}>{t('templates.builtInBadge')}</span>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t<span style={group.primary.active ? styles.active : styles.inactive}>\n\t\t\t\t\t\t\t\t\t<span style={styles.dot} />\n\t\t\t\t\t\t\t\t\t{group.primary.active ? t('common.status.active') : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{/* Siblings of the row button, not inside it: a button cannot hold buttons. */}\n\t\t\t\t\t\t\t<span style={styles.chips}>\n\t\t\t\t\t\t\t\t{group.entries.map((entry) => {\n\t\t\t\t\t\t\t\t\tconst name = entry.locale ?? t('templates.defaultChip');\n\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\tkey={entry.locale ?? ''}\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => onSelectTemplate?.(entry)}\n\t\t\t\t\t\t\t\t\t\t\tstyle={entry.active ? styles.chip : styles.chipInactive}\n\t\t\t\t\t\t\t\t\t\t\taria-label={t('templates.list.openLocale', { type: group.type, locale: name })}\n\t\t\t\t\t\t\t\t\t\t\ttitle={entry.active ? undefined : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{name}\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t))}\n\t\t\t\t</ul>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// Matches @fonderie/react-admin-screens' shell look with local values: this\n// package sits below the admin screens and cannot import their blocks.\nconst pill: CSSProperties = {\n\tdisplay: 'inline-flex',\n\talignItems: 'center',\n\tgap: 6,\n\tborderRadius: 999,\n\tpadding: '2px 9px 2px 8px',\n\tfontSize: 12,\n\tfontWeight: 600,\n\tlineHeight: 1.5,\n\tminWidth: 64,\n};\n\n// One per locale the email exists in. Monospace: they are language tags.\nconst chip: CSSProperties = {\n\theight: 24,\n\tboxSizing: 'border-box',\n\tpadding: '0 8px',\n\tborderRadius: 6,\n\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\tbackground: 'var(--fonderie-surface,#fff)',\n\tcolor: 'var(--fonderie-text,#171717)',\n\tfontSize: 12,\n\tfontFamily:\n\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\tcursor: 'pointer',\n};\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '32px 40px 64px', maxWidth: 1160, boxSizing: 'border-box' },\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: 0,\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\thint: { fontSize: 13.5, color: 'var(--fonderie-text-muted,#5c5c5c)', margin: '4px 0 24px' },\n\tstatus: { padding: '24px 0', color: 'var(--fonderie-text-muted,#5c5c5c)', fontSize: 13.5 },\n\terror: { color: 'var(--fonderie-danger,#e00)', marginBottom: 12, fontSize: 14 },\n\tlist: {\n\t\tlistStyle: 'none',\n\t\tpadding: 0,\n\t\tmargin: 0,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\toverflow: 'hidden',\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t},\n\trow: {\n\t\tdisplay: 'flex',\n\t\talignItems: 'center',\n\t\tgap: 8,\n\t\tpaddingRight: 16,\n\t\tborderBottom: '1px solid var(--fonderie-border-light,#f5f5f5)',\n\t},\n\trowButton: {\n\t\tflex: 1,\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tgap: 16,\n\t\tpadding: '13px 16px',\n\t\tbackground: 'none',\n\t\tborder: 'none',\n\t\tcursor: 'pointer',\n\t\ttextAlign: 'left',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily: 'inherit',\n\t},\n\ttype: {\n\t\tfontSize: 13,\n\t\tfontWeight: 600,\n\t\tflex: 1,\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\tchips: { display: 'flex', flexWrap: 'wrap', justifyContent: 'flex-end', gap: 6 },\n\tchip,\n\tchipInactive: { ...chip, opacity: 0.5, textDecoration: 'line-through' },\n\tdot: { width: 6, height: 6, borderRadius: 999, background: 'currentColor' },\n\tbuiltIn: {\n\t\tfontSize: 11,\n\t\tfontWeight: 500,\n\t\tpadding: '1px 7px',\n\t\tborderRadius: 999,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t},\n\tnewButton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 32,\n\t\tboxSizing: 'border-box',\n\t\tborderRadius: 6,\n\t\tpadding: '0 12px',\n\t\tfontSize: 13,\n\t\tfontWeight: 500,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t},\n\tactive: {\n\t\t...pill,\n\t\tcolor: 'var(--fonderie-accent-strong,#009767)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-accent-strong,#009767) 13%, transparent)',\n\t},\n\tinactive: {\n\t\t...pill,\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-text-muted,#5c5c5c) 10%, transparent)',\n\t},\n};\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\tFonderieApiError,\n\ttype ISetTemplateInput,\n\tcreateAdminT,\n} from '@fonderie/client';\nimport { useTemplatePreview } from '@fonderie/react-courier-admin';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useEffect, useRef, useState } from 'react';\n\nexport interface ITemplateCreateScreenProps {\n\tclient: CourierAdminClient;\n\t/**\n\t * Given ⇒ add a LOCALE of this existing template: the type is fixed and the\n\t * form starts from its default-locale copy. Omitted ⇒ a brand-new template\n\t * type (one the app sends that has no stored copy yet).\n\t */\n\ttype?: string;\n\t/** Locales already in use, offered as suggestions. */\n\tlocales?: string[];\n\t/** Called with what was created, so the caller can open it in the editor. */\n\tonCreated?: (created: { type: string; locale: string | null }) => void;\n\t/** The console's language. Default English. */\n\tlocale?: AdminLocale | undefined;\n}\n\nconst TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;\nconst LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;\n// Variables the layout injects — never sample values, as in the editor.\nconst IMPLICIT = new Set(['subject', 'preheader', 'brandName']);\nconst PREVIEW_DEBOUNCE_MS = 500;\n\n// Creating is its own screen: the editor loads an existing row, and a new\n// template or locale has none yet. Once saved, the editor takes over.\nexport function TemplateCreateScreen({\n\tclient,\n\ttype,\n\tlocales = [],\n\tonCreated,\n\tlocale,\n}: ITemplateCreateScreenProps) {\n\tconst tr = createAdminT(locale);\n\tconst addingLocale = type !== undefined;\n\tconst [newType, setNewType] = useState('');\n\tconst [targetLocale, setTargetLocale] = useState('');\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [error, setError] = useState<string | null>(null);\n\tconst [busy, setBusy] = useState(false);\n\t// The default copy this locale started from, to flag fields not yet translated.\n\tconst [source, setSource] = useState<{ subject: string; html: string; text: string } | null>(\n\t\tnull,\n\t);\n\tconst { preview, isPreviewing, error: previewError, renderPreview } = useTemplatePreview(client);\n\t// Each variable renders as its own name, so the preview shows where it lands.\n\t// Learned from the server's answer, not re-parsed here.\n\tconst variables = useRef<string[]>([]);\n\n\t// A new locale starts from the default copy, so translating is editing.\n\tuseEffect(() => {\n\t\tif (!type) return;\n\t\tclient\n\t\t\t.getTemplate(type, null)\n\t\t\t.then(({ result }) => {\n\t\t\t\tsetSubject(result.subject ?? '');\n\t\t\t\tsetHtml(result.html ?? '');\n\t\t\t\tsetText(result.text);\n\t\t\t\tsetSource({ subject: result.subject ?? '', html: result.html ?? '', text: result.text });\n\t\t\t})\n\t\t\t.catch(() => undefined);\n\t}, [client, type]);\n\n\t// Live preview, as in the editor: translating is where it matters most — a\n\t// half-translated email looks finished in a textarea and wrong in the render.\n\tconst previewType = addingLocale ? (type as string) : newType.trim();\n\tconst previewLocale = LOCALE_RE.test(targetLocale.trim()) ? targetLocale.trim() : null;\n\tuseEffect(() => {\n\t\tif (!TYPE_RE.test(previewType) || !text.trim()) return;\n\t\tconst timer = setTimeout(async () => {\n\t\t\tconst input = { text, ...(subject ? { subject } : {}), ...(html ? { html } : {}) };\n\t\t\tconst render = () =>\n\t\t\t\trenderPreview(\n\t\t\t\t\tpreviewType,\n\t\t\t\t\t{ ...input, data: Object.fromEntries(variables.current.map((v) => [v, v])) },\n\t\t\t\t\tpreviewLocale,\n\t\t\t\t);\n\t\t\ttry {\n\t\t\t\tconst result = await render();\n\t\t\t\tconst used = result.variables.filter((v) => !IMPLICIT.has(v));\n\t\t\t\t// A variable not seen before rendered empty: render once more with it.\n\t\t\t\tif (used.some((v) => !variables.current.includes(v))) {\n\t\t\t\t\tvariables.current = used;\n\t\t\t\t\tawait render();\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Shown through previewError.\n\t\t\t}\n\t\t}, PREVIEW_DEBOUNCE_MS);\n\t\treturn () => clearTimeout(timer);\n\t}, [renderPreview, previewType, previewLocale, subject, html, text]);\n\n\tconst untranslated = source\n\t\t? [\n\t\t\t\tsubject && subject === source.subject ? tr('templates.subject') : null,\n\t\t\t\thtml && html === source.html ? tr('templates.htmlBody') : null,\n\t\t\t\ttext === source.text ? tr('templates.textBody') : null,\n\t\t\t].filter((f): f is string => f !== null)\n\t\t: [];\n\n\tconst submit = async (e: FormEvent) => {\n\t\te.preventDefault();\n\t\tsetError(null);\n\t\tconst t = addingLocale ? (type as string) : newType.trim();\n\t\tconst l = targetLocale.trim() || null;\n\t\tif (!TYPE_RE.test(t)) return setError(tr('templates.create.errorType'));\n\t\tif (addingLocale && !l) return setError(tr('templates.create.errorLocaleRequired'));\n\t\tif (l && !LOCALE_RE.test(l)) return setError(tr('templates.create.errorLocale'));\n\t\tif (!text.trim()) return setError(tr('templates.create.errorText'));\n\t\tsetBusy(true);\n\t\ttry {\n\t\t\t// Refuse to overwrite: creating an existing row would replace it.\n\t\t\tconst exists = await client.getTemplate(t, l).then(\n\t\t\t\t() => true,\n\t\t\t\t(err: unknown) => !(err instanceof FonderieApiError && err.status === 404),\n\t\t\t);\n\t\t\tif (exists) {\n\t\t\t\tsetError(\n\t\t\t\t\tl\n\t\t\t\t\t\t? tr('templates.create.existsLocale', { type: t, locale: l })\n\t\t\t\t\t\t: tr('templates.create.exists', { type: t }),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst input: ISetTemplateInput = { text, active: true };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tawait client.setTemplate(t, input, l);\n\t\t\tonCreated?.({ type: t, locale: l });\n\t\t} catch (err) {\n\t\t\tsetError(err instanceof FonderieApiError ? err.explanation : String(err));\n\t\t} finally {\n\t\t\tsetBusy(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>\n\t\t\t\t{addingLocale\n\t\t\t\t\t? tr('templates.create.titleLocale', { type: type as string })\n\t\t\t\t\t: tr('templates.create.titleNew')}\n\t\t\t</h1>\n\t\t\t<p style={styles.meta}>\n\t\t\t\t{addingLocale ? tr('templates.create.hintLocale') : tr('templates.create.hintNew')}\n\t\t\t</p>\n\t\t\t<div style={styles.split}>\n\t\t\t\t<form style={styles.form} onSubmit={(e) => void submit(e)}>\n\t\t\t\t\t{!addingLocale ? (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-type\">\n\t\t\t\t\t\t\t\t{tr('templates.create.type')}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\tid=\"new-template-type\"\n\t\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\t\tvalue={newType}\n\t\t\t\t\t\t\t\tplaceholder=\"weekly-digest\"\n\t\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\t\tonChange={(e) => setNewType(e.target.value)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</>\n\t\t\t\t\t) : null}\n\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-locale\">\n\t\t\t\t\t\t{addingLocale ? tr('templates.create.locale') : tr('templates.create.localeOptional')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<input\n\t\t\t\t\t\tid=\"new-template-locale\"\n\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\tvalue={targetLocale}\n\t\t\t\t\t\tplaceholder=\"fr\"\n\t\t\t\t\t\tlist=\"new-template-locales\"\n\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\tonChange={(e) => setTargetLocale(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<datalist id=\"new-template-locales\">\n\t\t\t\t\t\t{[...new Set(locales)].map((l) => (\n\t\t\t\t\t\t\t<option key={l} value={l} />\n\t\t\t\t\t\t))}\n\t\t\t\t\t</datalist>\n\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-subject\">\n\t\t\t\t\t\t{tr('templates.subject')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<input\n\t\t\t\t\t\tid=\"new-template-subject\"\n\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\tvalue={subject}\n\t\t\t\t\t\tonChange={(e) => setSubject(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-html\">\n\t\t\t\t\t\t{tr('templates.htmlBody')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tid=\"new-template-html\"\n\t\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 160 }}\n\t\t\t\t\t\tvalue={html}\n\t\t\t\t\t\tonChange={(e) => setHtml(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-text\">\n\t\t\t\t\t\t{tr('templates.textBody')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tid=\"new-template-text\"\n\t\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 100 }}\n\t\t\t\t\t\tvalue={text}\n\t\t\t\t\t\tonChange={(e) => setText(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t{untranslated.length > 0 ? (\n\t\t\t\t\t\t<p style={styles.notice}>\n\t\t\t\t\t\t\t{tr('templates.create.untranslated', { fields: untranslated.join(', ') })}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{error ? (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{error}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t) : null}\n\t\t\t\t\t<button type=\"submit\" disabled={busy} style={styles.button}>\n\t\t\t\t\t\t{busy\n\t\t\t\t\t\t\t? tr('templates.create.creating')\n\t\t\t\t\t\t\t: addingLocale\n\t\t\t\t\t\t\t\t? tr('templates.create.submitLocale')\n\t\t\t\t\t\t\t\t: tr('templates.create.submitNew')}\n\t\t\t\t\t</button>\n\t\t\t\t</form>\n\n\t\t\t\t<div style={styles.previewColumn}>\n\t\t\t\t\t<div style={styles.previewHeader}>\n\t\t\t\t\t\t<span style={styles.label}>{tr('templates.editor.preview')}</span>\n\t\t\t\t\t\t<span style={styles.meta}>\n\t\t\t\t\t\t\t{isPreviewing ? tr('templates.editor.rendering') : tr('templates.editor.live')}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{previewError ? (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{previewError.explanation}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{preview?.subject ? <p style={styles.previewSubject}>{preview.subject}</p> : null}\n\t\t\t\t\t{preview?.html ? (\n\t\t\t\t\t\t// sandbox=\"\" — no scripts, no same-origin: operator-authored HTML must\n\t\t\t\t\t\t// never execute in the console's origin.\n\t\t\t\t\t\t<iframe\n\t\t\t\t\t\t\ttitle={tr('templates.editor.previewTitle')}\n\t\t\t\t\t\t\tstyle={styles.previewFrame}\n\t\t\t\t\t\t\tsandbox=\"\"\n\t\t\t\t\t\t\tsrcDoc={preview.html}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : preview ? (\n\t\t\t\t\t\t<pre style={styles.previewText}>{preview.text}</pre>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<p style={styles.meta}>{tr('templates.editor.nothingRendered')}</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nconst MONO =\n\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)';\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '8px 40px 64px', maxWidth: 1400, boxSizing: 'border-box' },\n\t// The editor's layout: the form on the left, the render pinned on the right.\n\tsplit: {\n\t\tdisplay: 'grid',\n\t\tgridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)',\n\t\tgap: 24,\n\t\talignItems: 'start',\n\t},\n\tpreviewColumn: {\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tposition: 'sticky',\n\t\ttop: 16,\n\t},\n\tpreviewHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' },\n\tpreviewSubject: { fontSize: 14, fontWeight: 600, margin: '8px 0' },\n\tpreviewFrame: {\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t\twidth: '100%',\n\t\theight: 520,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t},\n\tpreviewText: {\n\t\twhiteSpace: 'pre-wrap',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tpadding: 12,\n\t\tfontSize: 13,\n\t\tfontFamily: MONO,\n\t},\n\t// The console's warning token (the shell's amber), readable in both themes.\n\tnotice: {\n\t\tfontSize: 13,\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-warning,#f5a623) 16%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '12px 0 0',\n\t},\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: '8px 0 4px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\tmeta: {\n\t\tfontSize: 13,\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t\tmarginBottom: 20,\n\t\tlineHeight: 1.5,\n\t},\n\tform: {\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tgap: 4,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\tpadding: '6px 20px 20px',\n\t},\n\tlabel: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },\n\tinput: {\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 13.5,\n\t\tfontFamily: 'inherit',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t},\n\ttextarea: {\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: 10,\n\t\tfontSize: 13,\n\t\tlineHeight: 1.55,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily: MONO,\n\t},\n\terror: {\n\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '8px 0',\n\t\tfontSize: 13.5,\n\t},\n\tbutton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t\tmarginTop: 16,\n\t},\n};\n"],"mappings":"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","names":["import_client","import_react_courier_admin","import_jsx_runtime","styles","import_client","import_react_courier_admin","import_react","import_jsx_runtime","IMPLICIT","PREVIEW_DEBOUNCE_MS","styles"]}
package/dist/index.js CHANGED
@@ -706,10 +706,13 @@ import {
706
706
  FonderieApiError,
707
707
  createAdminT as createAdminT3
708
708
  } from "@fonderie/client";
709
- import { useEffect as useEffect2, useState as useState2 } from "react";
709
+ import { useTemplatePreview as useTemplatePreview2 } from "@fonderie/react-courier-admin";
710
+ import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
710
711
  import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
711
712
  var TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;
712
713
  var LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
714
+ var IMPLICIT2 = /* @__PURE__ */ new Set(["subject", "preheader", "brandName"]);
715
+ var PREVIEW_DEBOUNCE_MS2 = 500;
713
716
  function TemplateCreateScreen({
714
717
  client,
715
718
  type,
@@ -726,14 +729,48 @@ function TemplateCreateScreen({
726
729
  const [text, setText] = useState2("");
727
730
  const [error, setError] = useState2(null);
728
731
  const [busy, setBusy] = useState2(false);
732
+ const [source, setSource] = useState2(
733
+ null
734
+ );
735
+ const { preview, isPreviewing, error: previewError, renderPreview } = useTemplatePreview2(client);
736
+ const variables = useRef2([]);
729
737
  useEffect2(() => {
730
738
  if (!type) return;
731
739
  client.getTemplate(type, null).then(({ result }) => {
732
740
  setSubject(result.subject ?? "");
733
741
  setHtml(result.html ?? "");
734
742
  setText(result.text);
743
+ setSource({ subject: result.subject ?? "", html: result.html ?? "", text: result.text });
735
744
  }).catch(() => void 0);
736
745
  }, [client, type]);
746
+ const previewType = addingLocale ? type : newType.trim();
747
+ const previewLocale = LOCALE_RE.test(targetLocale.trim()) ? targetLocale.trim() : null;
748
+ useEffect2(() => {
749
+ if (!TYPE_RE.test(previewType) || !text.trim()) return;
750
+ const timer = setTimeout(async () => {
751
+ const input = { text, ...subject ? { subject } : {}, ...html ? { html } : {} };
752
+ const render = () => renderPreview(
753
+ previewType,
754
+ { ...input, data: Object.fromEntries(variables.current.map((v) => [v, v])) },
755
+ previewLocale
756
+ );
757
+ try {
758
+ const result = await render();
759
+ const used = result.variables.filter((v) => !IMPLICIT2.has(v));
760
+ if (used.some((v) => !variables.current.includes(v))) {
761
+ variables.current = used;
762
+ await render();
763
+ }
764
+ } catch {
765
+ }
766
+ }, PREVIEW_DEBOUNCE_MS2);
767
+ return () => clearTimeout(timer);
768
+ }, [renderPreview, previewType, previewLocale, subject, html, text]);
769
+ const untranslated = source ? [
770
+ subject && subject === source.subject ? tr("templates.subject") : null,
771
+ html && html === source.html ? tr("templates.htmlBody") : null,
772
+ text === source.text ? tr("templates.textBody") : null
773
+ ].filter((f) => f !== null) : [];
737
774
  const submit = async (e) => {
738
775
  e.preventDefault();
739
776
  setError(null);
@@ -769,75 +806,140 @@ function TemplateCreateScreen({
769
806
  return /* @__PURE__ */ jsxs3("div", { style: styles3.container, children: [
770
807
  /* @__PURE__ */ jsx3("h1", { style: styles3.title, children: addingLocale ? tr("templates.create.titleLocale", { type }) : tr("templates.create.titleNew") }),
771
808
  /* @__PURE__ */ jsx3("p", { style: styles3.meta, children: addingLocale ? tr("templates.create.hintLocale") : tr("templates.create.hintNew") }),
772
- /* @__PURE__ */ jsxs3("form", { style: styles3.form, onSubmit: (e) => void submit(e), children: [
773
- !addingLocale ? /* @__PURE__ */ jsxs3(Fragment, { children: [
774
- /* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-type", children: tr("templates.create.type") }),
809
+ /* @__PURE__ */ jsxs3("div", { style: styles3.split, children: [
810
+ /* @__PURE__ */ jsxs3("form", { style: styles3.form, onSubmit: (e) => void submit(e), children: [
811
+ !addingLocale ? /* @__PURE__ */ jsxs3(Fragment, { children: [
812
+ /* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-type", children: tr("templates.create.type") }),
813
+ /* @__PURE__ */ jsx3(
814
+ "input",
815
+ {
816
+ id: "new-template-type",
817
+ style: styles3.input,
818
+ value: newType,
819
+ placeholder: "weekly-digest",
820
+ autoComplete: "off",
821
+ spellCheck: false,
822
+ onChange: (e) => setNewType(e.target.value)
823
+ }
824
+ )
825
+ ] }) : null,
826
+ /* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-locale", children: addingLocale ? tr("templates.create.locale") : tr("templates.create.localeOptional") }),
775
827
  /* @__PURE__ */ jsx3(
776
828
  "input",
777
829
  {
778
- id: "new-template-type",
830
+ id: "new-template-locale",
779
831
  style: styles3.input,
780
- value: newType,
781
- placeholder: "weekly-digest",
832
+ value: targetLocale,
833
+ placeholder: "fr",
834
+ list: "new-template-locales",
782
835
  autoComplete: "off",
783
836
  spellCheck: false,
784
- onChange: (e) => setNewType(e.target.value)
837
+ onChange: (e) => setTargetLocale(e.target.value)
785
838
  }
786
- )
787
- ] }) : null,
788
- /* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-locale", children: addingLocale ? tr("templates.create.locale") : tr("templates.create.localeOptional") }),
789
- /* @__PURE__ */ jsx3(
790
- "input",
791
- {
792
- id: "new-template-locale",
793
- style: styles3.input,
794
- value: targetLocale,
795
- placeholder: "fr",
796
- list: "new-template-locales",
797
- autoComplete: "off",
798
- spellCheck: false,
799
- onChange: (e) => setTargetLocale(e.target.value)
800
- }
801
- ),
802
- /* @__PURE__ */ jsx3("datalist", { id: "new-template-locales", children: [...new Set(locales)].map((l) => /* @__PURE__ */ jsx3("option", { value: l }, l)) }),
803
- /* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-subject", children: tr("templates.subject") }),
804
- /* @__PURE__ */ jsx3(
805
- "input",
806
- {
807
- id: "new-template-subject",
808
- style: styles3.input,
809
- value: subject,
810
- onChange: (e) => setSubject(e.target.value)
811
- }
812
- ),
813
- /* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-html", children: tr("templates.htmlBody") }),
814
- /* @__PURE__ */ jsx3(
815
- "textarea",
816
- {
817
- id: "new-template-html",
818
- style: { ...styles3.textarea, minHeight: 160 },
819
- value: html,
820
- onChange: (e) => setHtml(e.target.value)
821
- }
822
- ),
823
- /* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-text", children: tr("templates.textBody") }),
824
- /* @__PURE__ */ jsx3(
825
- "textarea",
826
- {
827
- id: "new-template-text",
828
- style: { ...styles3.textarea, minHeight: 100 },
829
- value: text,
830
- onChange: (e) => setText(e.target.value)
831
- }
832
- ),
833
- error ? /* @__PURE__ */ jsx3("p", { style: styles3.error, role: "alert", children: error }) : null,
834
- /* @__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") })
839
+ ),
840
+ /* @__PURE__ */ jsx3("datalist", { id: "new-template-locales", children: [...new Set(locales)].map((l) => /* @__PURE__ */ jsx3("option", { value: l }, l)) }),
841
+ /* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-subject", children: tr("templates.subject") }),
842
+ /* @__PURE__ */ jsx3(
843
+ "input",
844
+ {
845
+ id: "new-template-subject",
846
+ style: styles3.input,
847
+ value: subject,
848
+ onChange: (e) => setSubject(e.target.value)
849
+ }
850
+ ),
851
+ /* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-html", children: tr("templates.htmlBody") }),
852
+ /* @__PURE__ */ jsx3(
853
+ "textarea",
854
+ {
855
+ id: "new-template-html",
856
+ style: { ...styles3.textarea, minHeight: 160 },
857
+ value: html,
858
+ onChange: (e) => setHtml(e.target.value)
859
+ }
860
+ ),
861
+ /* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-text", children: tr("templates.textBody") }),
862
+ /* @__PURE__ */ jsx3(
863
+ "textarea",
864
+ {
865
+ id: "new-template-text",
866
+ style: { ...styles3.textarea, minHeight: 100 },
867
+ value: text,
868
+ onChange: (e) => setText(e.target.value)
869
+ }
870
+ ),
871
+ untranslated.length > 0 ? /* @__PURE__ */ jsx3("p", { style: styles3.notice, children: tr("templates.create.untranslated", { fields: untranslated.join(", ") }) }) : null,
872
+ error ? /* @__PURE__ */ jsx3("p", { style: styles3.error, role: "alert", children: error }) : null,
873
+ /* @__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") })
874
+ ] }),
875
+ /* @__PURE__ */ jsxs3("div", { style: styles3.previewColumn, children: [
876
+ /* @__PURE__ */ jsxs3("div", { style: styles3.previewHeader, children: [
877
+ /* @__PURE__ */ jsx3("span", { style: styles3.label, children: tr("templates.editor.preview") }),
878
+ /* @__PURE__ */ jsx3("span", { style: styles3.meta, children: isPreviewing ? tr("templates.editor.rendering") : tr("templates.editor.live") })
879
+ ] }),
880
+ previewError ? /* @__PURE__ */ jsx3("p", { style: styles3.error, role: "alert", children: previewError.explanation }) : null,
881
+ preview?.subject ? /* @__PURE__ */ jsx3("p", { style: styles3.previewSubject, children: preview.subject }) : null,
882
+ preview?.html ? (
883
+ // sandbox="" — no scripts, no same-origin: operator-authored HTML must
884
+ // never execute in the console's origin.
885
+ /* @__PURE__ */ jsx3(
886
+ "iframe",
887
+ {
888
+ title: tr("templates.editor.previewTitle"),
889
+ style: styles3.previewFrame,
890
+ sandbox: "",
891
+ srcDoc: preview.html
892
+ }
893
+ )
894
+ ) : preview ? /* @__PURE__ */ jsx3("pre", { style: styles3.previewText, children: preview.text }) : /* @__PURE__ */ jsx3("p", { style: styles3.meta, children: tr("templates.editor.nothingRendered") })
895
+ ] })
835
896
  ] })
836
897
  ] });
837
898
  }
838
899
  var MONO = 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)';
839
900
  var styles3 = {
840
- container: { padding: "8px 40px 64px", maxWidth: 760, boxSizing: "border-box" },
901
+ container: { padding: "8px 40px 64px", maxWidth: 1400, boxSizing: "border-box" },
902
+ // The editor's layout: the form on the left, the render pinned on the right.
903
+ split: {
904
+ display: "grid",
905
+ gridTemplateColumns: "minmax(0, 1fr) minmax(0, 1fr)",
906
+ gap: 24,
907
+ alignItems: "start"
908
+ },
909
+ previewColumn: {
910
+ minWidth: 0,
911
+ display: "flex",
912
+ flexDirection: "column",
913
+ position: "sticky",
914
+ top: 16
915
+ },
916
+ previewHeader: { display: "flex", justifyContent: "space-between", alignItems: "center" },
917
+ previewSubject: { fontSize: 14, fontWeight: 600, margin: "8px 0" },
918
+ previewFrame: {
919
+ boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))",
920
+ width: "100%",
921
+ height: 520,
922
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
923
+ borderRadius: 8,
924
+ background: "var(--fonderie-surface,#fff)"
925
+ },
926
+ previewText: {
927
+ whiteSpace: "pre-wrap",
928
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
929
+ borderRadius: 8,
930
+ padding: 12,
931
+ fontSize: 13,
932
+ fontFamily: MONO
933
+ },
934
+ // The console's warning token (the shell's amber), readable in both themes.
935
+ notice: {
936
+ fontSize: 13,
937
+ color: "var(--fonderie-text,#171717)",
938
+ background: "color-mix(in srgb, var(--fonderie-warning,#f5a623) 16%, transparent)",
939
+ borderRadius: 6,
940
+ padding: "8px 12px",
941
+ margin: "12px 0 0"
942
+ },
841
943
  title: {
842
944
  fontSize: 22,
843
945
  fontWeight: 600,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/screens/TemplateEditorScreen.tsx","../src/screens/TemplateListScreen.tsx","../src/screens/TemplateCreateScreen.tsx"],"sourcesContent":["import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\ttype FonderieApiError,\n\ttype ISetTemplateInput,\n\ttype ITemplateEntry,\n\tcreateAdminT,\n\tformatAdminDate,\n\tgroupTemplatesByType,\n\tmissingTemplateLocales,\n} from '@fonderie/client';\nimport {\n\tuseTemplate,\n\tuseTemplatePreview,\n\tuseTemplateRevisions,\n\tuseTemplates,\n} from '@fonderie/react-courier-admin';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\n// Implicit variables the layout injects — an operator never supplies these, so\n// offering them as fields would just be noise.\nconst IMPLICIT = new Set(['subject', 'preheader', 'brandName']);\n\nconst PREVIEW_DEBOUNCE_MS = 500;\n\nexport interface ITemplateEditorScreenProps {\n\tclient: CourierAdminClient;\n\ttype: string;\n\tlocale?: string | null;\n\tonSaved?: () => void;\n\t/**\n\t * A built-in email's default-locale row (the list's `system` flag): it can\n\t * be edited and rolled back, never deleted — Delete is not offered.\n\t */\n\tsystem?: boolean;\n\t/** Called after a delete; the template no longer exists. */\n\tonDeleted?: () => void;\n\t/**\n\t * Shows \"Add locale\": create a translation of this template. `locales` are\n\t * the ones the app uses elsewhere that this email lacks — suggest them first.\n\t */\n\tonAddLocale?: (type: string, context: { locales: string[] }) => void;\n\t/**\n\t * Shows a tab per locale this email exists in; receives the row to open.\n\t * Unsaved edits are confirmed before switching away.\n\t */\n\tonSelectLocale?: (template: ITemplateEntry) => void;\n\t/**\n\t * The CONSOLE's language (default English). Named `uiLocale` because\n\t * `locale` here is the template's own locale.\n\t */\n\tuiLocale?: AdminLocale | undefined;\n}\n\nexport function TemplateEditorScreen({\n\tclient,\n\ttype,\n\tlocale,\n\tonSaved,\n\tsystem = false,\n\tonDeleted,\n\tonAddLocale,\n\tonSelectLocale,\n\tuiLocale,\n}: ITemplateEditorScreenProps) {\n\t// Memoized: `run` below depends on it, and a new translator per render would\n\t// restart the preview debounce on every render.\n\tconst t = useMemo(() => createAdminT(uiLocale), [uiLocale]);\n\tconst { template, isLoading, error, refresh } = useTemplate(client, type, locale);\n\t// Saves go through the list hook (which re-fetches the template list after\n\t// each write); mounting it adds a template-list fetch to this single-template\n\t// editor — acceptable for an admin dashboard. Its isLoading/error track that\n\t// list fetch, so the save action keeps local state.\n\tconst { templates, saveTemplate, removeTemplate } = useTemplates(client);\n\t// This email's locales, for the tabs. The list is already fetched for saves.\n\tconst siblings = groupTemplatesByType(templates).find((g) => g.type === type)?.entries ?? [];\n\tconst [isDeleting, setIsDeleting] = useState(false);\n\tconst { revisions, rollback } = useTemplateRevisions(client, type, locale);\n\tconst [isSaving, setIsSaving] = useState(false);\n\tconst [saveError, setSaveError] = useState<FonderieApiError | null>(null);\n\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [active, setActive] = useState(true);\n\n\tconst { preview, isPreviewing, error: previewError, renderPreview } = useTemplatePreview(client);\n\t// The sample values, as JSON the operator can edit. Text rather than an\n\t// object so a half-typed value does not have to parse on every keystroke.\n\tconst [sampleJson, setSampleJson] = useState('{}');\n\tconst [sampleError, setSampleError] = useState<string | null>(null);\n\n\tuseEffect(() => {\n\t\tif (!template) return;\n\t\tsetSubject(template.subject ?? '');\n\t\tsetHtml(template.html ?? '');\n\t\tsetText(template.text);\n\t\tsetActive(template.active);\n\t}, [template]);\n\n\tconst run = useCallback(async () => {\n\t\tlet data: Record<string, unknown> = {};\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(sampleJson || '{}');\n\t\t\tif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\t\t\tsetSampleError(t('templates.editor.sampleNotObject'));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdata = parsed as Record<string, unknown>;\n\t\t\tsetSampleError(null);\n\t\t} catch {\n\t\t\tsetSampleError(t('templates.editor.sampleInvalid'));\n\t\t\treturn;\n\t\t}\n\t\tconst result = await renderPreview(\n\t\t\ttype,\n\t\t\t{ text, data, ...(subject ? { subject } : {}), ...(html ? { html } : {}) },\n\t\t\tlocale,\n\t\t);\n\t\t// The server reports which variables this content uses; seed the ones\n\t\t// that have no value yet with their own name, so the render shows where\n\t\t// each lands. Never overwrite something already typed.\n\t\tconst missing = result.variables.filter((v) => !IMPLICIT.has(v) && !(v in data));\n\t\tif (missing.length > 0) {\n\t\t\tconst seeded = { ...data, ...Object.fromEntries(missing.map((v) => [v, v])) };\n\t\t\tsetSampleJson(JSON.stringify(seeded, null, 2));\n\t\t}\n\t}, [renderPreview, type, text, subject, html, locale, sampleJson, t]);\n\n\t// The preview is always live: it renders once the template loads, then again\n\t// PREVIEW_DEBOUNCE_MS after the last edit to the content or the sample data.\n\t// Debounced, not per keystroke, so typing a paragraph costs one request.\n\t// `run` is a new function on every keystroke (it closes over the fields), so\n\t// the effect keys on the fields themselves and calls the latest `run`.\n\tconst runRef = useRef(run);\n\trunRef.current = run;\n\tconst loadedType = template ? `${type}:${locale ?? ''}` : null;\n\t// The fields ARE the trigger: each edit restarts the debounce. The effect\n\t// body reads the latest `run` through the ref, so it does not use them.\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: see above\n\tuseEffect(() => {\n\t\tif (!loadedType) return;\n\t\tconst timer = setTimeout(() => void runRef.current(), PREVIEW_DEBOUNCE_MS);\n\t\treturn () => clearTimeout(timer);\n\t}, [loadedType, subject, html, text, sampleJson]);\n\n\t// Saving what is already stored would only mint an identical version (the\n\t// server treats it as a no-op anyway), so Save waits for a real change.\n\tconst dirty =\n\t\t!!template &&\n\t\t(subject !== (template.subject ?? '') ||\n\t\t\thtml !== (template.html ?? '') ||\n\t\t\ttext !== template.text ||\n\t\t\tactive !== template.active);\n\n\tconst handleSubmit = async (event: FormEvent) => {\n\t\tevent.preventDefault();\n\t\tsetIsSaving(true);\n\t\tsetSaveError(null);\n\t\ttry {\n\t\t\tconst input: ISetTemplateInput = { text, active };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tif (template?.version !== undefined) input.ifVersion = template.version;\n\t\t\tawait saveTemplate(type, input, locale);\n\t\t\t// The list hook refreshes its own list; this screen renders the single\n\t\t\t// template, so re-read it too.\n\t\t\tawait refresh();\n\t\t\tonSaved?.();\n\t\t} catch (err) {\n\t\t\t// useTemplates normalizes every failure to FonderieApiError before throwing.\n\t\t\tsetSaveError(err as FonderieApiError);\n\t\t} finally {\n\t\t\tsetIsSaving(false);\n\t\t}\n\t};\n\n\tconst switchTo = (entry: ITemplateEntry) => {\n\t\tif (dirty && !window.confirm(t('templates.editor.discardChanges'))) return;\n\t\tonSelectLocale?.(entry);\n\t};\n\n\tconst handleDelete = async () => {\n\t\tconst message = locale\n\t\t\t? t('templates.editor.confirmDeleteLocale', { type, locale })\n\t\t\t: t('templates.editor.confirmDelete', { type });\n\t\tif (!window.confirm(message)) return;\n\t\tsetIsDeleting(true);\n\t\tsetSaveError(null);\n\t\ttry {\n\t\t\tawait removeTemplate(type, locale);\n\t\t\tonDeleted?.();\n\t\t} catch (err) {\n\t\t\tsetSaveError(err as FonderieApiError);\n\t\t} finally {\n\t\t\tsetIsDeleting(false);\n\t\t}\n\t};\n\n\tif (isLoading) return <p style={styles.status}>{t('templates.editor.loading')}</p>;\n\tif (error)\n\t\treturn (\n\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t{error.explanation}\n\t\t\t</p>\n\t\t);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>{type}</h1>\n\t\t\t{onSelectLocale || onAddLocale ? (\n\t\t\t\t<div style={styles.tabBar}>\n\t\t\t\t\t{onSelectLocale ? (\n\t\t\t\t\t\t// Each tab opens another stored row, so this is navigation, not an\n\t\t\t\t\t\t// ARIA tablist: plain buttons, the open one marked aria-current.\n\t\t\t\t\t\t<nav aria-label={t('templates.editor.locales')} style={styles.tabs}>\n\t\t\t\t\t\t\t{siblings.map((entry) => {\n\t\t\t\t\t\t\t\tconst selected = entry.locale === (locale ?? null);\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\tkey={entry.locale ?? ''}\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\taria-current={selected ? 'true' : undefined}\n\t\t\t\t\t\t\t\t\t\tonClick={() => (selected ? undefined : switchTo(entry))}\n\t\t\t\t\t\t\t\t\t\tstyle={selected ? styles.tabSelected : entry.active ? styles.tab : styles.tabInactive}\n\t\t\t\t\t\t\t\t\t\ttitle={entry.active ? undefined : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{entry.locale ?? t('templates.defaultChip')}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t</nav>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{onAddLocale ? (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tstyle={{ ...styles.rollbackButton, marginLeft: 'auto' }}\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tif (dirty && !window.confirm(t('templates.editor.discardChanges'))) return;\n\t\t\t\t\t\t\t\tonAddLocale(type, { locales: missingTemplateLocales(templates, type) });\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{t('templates.editor.addLocale')}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<p style={styles.meta}>\n\t\t\t\t<strong>{locale ?? t('templates.defaultLocale')}</strong> · v{template?.version ?? 1}\n\t\t\t\t{system ? ` · ${t('templates.editor.builtInNote')}` : ''}\n\t\t\t</p>\n\n\t\t\t<div style={styles.split}>\n\t\t\t\t<div style={styles.column}>\n\t\t\t\t\t<form style={styles.form} onSubmit={handleSubmit}>\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-subject\">\n\t\t\t\t\t\t\t{t('templates.subject')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tid=\"template-subject\"\n\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\tvalue={subject}\n\t\t\t\t\t\t\tonChange={(event) => setSubject(event.target.value)}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-html\">\n\t\t\t\t\t\t\t{t('templates.htmlBody')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\tid=\"template-html\"\n\t\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\t\tvalue={html}\n\t\t\t\t\t\t\tonChange={(event) => setHtml(event.target.value)}\n\t\t\t\t\t\t\trows={8}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-text\">\n\t\t\t\t\t\t\t{t('templates.textBody')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\tid=\"template-text\"\n\t\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\t\tvalue={text}\n\t\t\t\t\t\t\tonChange={(event) => setText(event.target.value)}\n\t\t\t\t\t\t\trows={4}\n\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.checkboxLabel}>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={active}\n\t\t\t\t\t\t\t\tonChange={(event) => setActive(event.target.checked)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t{t('templates.editor.active')}\n\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t{saveError?.reason === 'VERSION_CONFLICT' ? (\n\t\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t\t{t('templates.editor.conflict')}{' '}\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\tsetSaveError(null);\n\t\t\t\t\t\t\t\t\t\tvoid refresh();\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{t('common.reload')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : saveError ? (\n\t\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t\t{saveError.explanation}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : null}\n\n\t\t\t\t\t\t<div style={styles.saveRow}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tdisabled={isSaving || !dirty}\n\t\t\t\t\t\t\t\tstyle={dirty ? styles.button : styles.buttonDisabled}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isSaving ? t('common.saving') : t('common.save')}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{!dirty && !isSaving && (\n\t\t\t\t\t\t\t\t<span style={styles.meta}>{t('templates.editor.noChanges')}</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{!system ? (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tdisabled={isDeleting}\n\t\t\t\t\t\t\t\t\tonClick={() => void handleDelete()}\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t...styles.rollbackButton,\n\t\t\t\t\t\t\t\t\t\theight: 36,\n\t\t\t\t\t\t\t\t\t\tmarginLeft: 'auto',\n\t\t\t\t\t\t\t\t\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\t\t\t\t\t\t\t\t\tborderColor: 'color-mix(in srgb, var(--fonderie-danger,#e00) 40%, transparent)',\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{isDeleting ? t('common.deleting') : t('common.delete')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</form>\n\n\t\t\t\t\t<label style={styles.label} htmlFor=\"template-sample\">\n\t\t\t\t\t\t{t('templates.editor.sampleData')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tid=\"template-sample\"\n\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\tvalue={sampleJson}\n\t\t\t\t\t\tonChange={(event) => setSampleJson(event.target.value)}\n\t\t\t\t\t\trows={6}\n\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t/>\n\t\t\t\t\t{sampleError && (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{sampleError}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t<div style={styles.previewColumn}>\n\t\t\t\t\t<div style={styles.previewHeader}>\n\t\t\t\t\t\t<span style={styles.label}>{t('templates.editor.preview')}</span>\n\t\t\t\t\t\t<span style={styles.meta}>\n\t\t\t\t\t\t\t{isPreviewing ? t('templates.editor.rendering') : t('templates.editor.live')}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{previewError && (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{previewError.explanation}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t\t{preview?.subject && <p style={styles.previewSubject}>{preview.subject}</p>}\n\t\t\t\t\t{preview?.html ? (\n\t\t\t\t\t\t// sandbox=\"\" — no scripts, no same-origin. This is operator-authored\n\t\t\t\t\t\t// HTML and it must never execute in the dashboard's origin, which is\n\t\t\t\t\t\t// where the admin token lives.\n\t\t\t\t\t\t<iframe\n\t\t\t\t\t\t\ttitle={t('templates.editor.previewTitle')}\n\t\t\t\t\t\t\tstyle={styles.previewFrame}\n\t\t\t\t\t\t\tsandbox=\"\"\n\t\t\t\t\t\t\tsrcDoc={preview.html}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : preview ? (\n\t\t\t\t\t\t<pre style={styles.previewText}>{preview.text}</pre>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<p style={styles.meta}>{t('templates.editor.nothingRendered')}</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{revisions.length > 0 && (\n\t\t\t\t<div style={styles.revisions}>\n\t\t\t\t\t<h2 style={styles.subtitle}>{t('templates.editor.history')}</h2>\n\t\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t\t{revisions.map((rev) => (\n\t\t\t\t\t\t\t<li key={rev.version} style={styles.row}>\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\tv{rev.version} — {rev.actor ?? t('templates.editor.unknownActor')} —{' '}\n\t\t\t\t\t\t\t\t\t{formatAdminDate(rev.createdAt, uiLocale)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => rollback(rev.version)}\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{t('templates.editor.rollBack')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nconst tab: CSSProperties = {\n\theight: 32,\n\tpadding: '0 10px',\n\tbackground: 'none',\n\tborder: 'none',\n\tborderBottom: '2px solid transparent',\n\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\tfontSize: 13,\n\tfontFamily:\n\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\tcursor: 'pointer',\n};\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '8px 40px 64px', maxWidth: 1400, boxSizing: 'border-box' },\n\t// One tab per locale of this email, underlined like a document's tabs.\n\ttabBar: {\n\t\tdisplay: 'flex',\n\t\talignItems: 'flex-end',\n\t\tgap: 12,\n\t\tborderBottom: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tmargin: '4px 0 12px',\n\t\tpaddingBottom: 6,\n\t},\n\ttabs: { display: 'flex', flexWrap: 'wrap', gap: 4, marginBottom: -7 },\n\ttab,\n\ttabSelected: {\n\t\t...tab,\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontWeight: 600,\n\t\tborderBottomColor: 'var(--fonderie-text,#171717)',\n\t\tcursor: 'default',\n\t},\n\ttabInactive: { ...tab, opacity: 0.55, textDecoration: 'line-through' },\n\t// Two columns, always: the editor on the left, the rendered result on the\n\t// right. Stacking the preview under a long form hid it below the fold.\n\tsplit: {\n\t\tdisplay: 'grid',\n\t\tgridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)',\n\t\tgap: 24,\n\t\talignItems: 'start',\n\t},\n\tcolumn: { minWidth: 0, display: 'flex', flexDirection: 'column' },\n\t// Pinned while the form scrolls, so the preview stays in view as you edit.\n\tpreviewColumn: {\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tposition: 'sticky',\n\t\ttop: 16,\n\t},\n\tsaveRow: { display: 'flex', alignItems: 'center', gap: 12, marginTop: 16 },\n\tpreviewHeader: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tmarginTop: 12,\n\t},\n\tpreviewSubject: { fontSize: 14, fontWeight: 600, margin: '8px 0' },\n\tpreviewFrame: {\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t\twidth: '100%',\n\t\theight: 520,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t},\n\tpreviewText: {\n\t\twhiteSpace: 'pre-wrap',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tpadding: 12,\n\t\tfontSize: 13,\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: '8px 0 4px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\tmeta: { fontSize: 13, color: 'var(--fonderie-text-muted,#5c5c5c)', marginBottom: 20 },\n\tstatus: { padding: 24, textAlign: 'center', color: 'var(--fonderie-text-muted,#5c5c5c)' },\n\terror: {\n\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '8px 0',\n\t\tfontSize: 13.5,\n\t},\n\tform: {\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tgap: 4,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\tpadding: '6px 20px 20px',\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t},\n\tlabel: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },\n\tinput: {\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 13.5,\n\t\tfontFamily: 'inherit',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t},\n\ttextarea: {\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: 10,\n\t\tfontSize: 13,\n\t\tlineHeight: 1.55,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\tcheckboxLabel: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 12, fontSize: 14 },\n\tbutton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t},\n\tbuttonDisabled: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\talignSelf: 'flex-start',\n\t\topacity: 0.45,\n\t\tcursor: 'not-allowed',\n\t},\n\trevisions: { marginTop: 32 },\n\tsubtitle: {\n\t\tfontSize: 15,\n\t\tfontWeight: 600,\n\t\tmargin: '0 0 10px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t},\n\tlist: {\n\t\tlistStyle: 'none',\n\t\tpadding: 0,\n\t\tmargin: 0,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\toverflow: 'hidden',\n\t},\n\trow: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tgap: 12,\n\t\tpadding: '9px 16px',\n\t\tborderBottom: '1px solid var(--fonderie-border-light,#f5f5f5)',\n\t\tfontSize: 13,\n\t},\n\trollbackButton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 28,\n\t\tboxSizing: 'border-box',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 12.5,\n\t\tfontWeight: 500,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t},\n};\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\ttype ITemplateEntry,\n\tcreateAdminT,\n\tgroupTemplatesByType,\n} from '@fonderie/client';\nimport { useTemplates } from '@fonderie/react-courier-admin';\nimport type { CSSProperties } from 'react';\n\nexport interface ITemplateListScreenProps {\n\tclient: CourierAdminClient;\n\t/**\n\t * Receives the whole row — type AND locale, and whether it is built-in. The\n\t * list shows one row per email: clicking it opens the default locale, a\n\t * locale chip opens that locale.\n\t */\n\tonSelectTemplate?: (template: ITemplateEntry) => void;\n\t/** Shows a \"New template\" button. Receives the locales in use, to suggest. */\n\tonCreateTemplate?: (context: { locales: string[] }) => void;\n\t/** The console's language. Default English. */\n\tlocale?: AdminLocale | undefined;\n}\n\nexport function TemplateListScreen({\n\tclient,\n\tonSelectTemplate,\n\tonCreateTemplate,\n\tlocale,\n}: ITemplateListScreenProps) {\n\tconst t = createAdminT(locale);\n\tconst { templates, isLoading, error } = useTemplates(client);\n\tconst groups = groupTemplatesByType(templates);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<div\n\t\t\t\tstyle={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16 }}\n\t\t\t>\n\t\t\t\t<h1 style={styles.title}>{t('templates.list.title')}</h1>\n\t\t\t\t{onCreateTemplate ? (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\tonCreateTemplate({\n\t\t\t\t\t\t\t\tlocales: [\n\t\t\t\t\t\t\t\t\t...new Set(templates.map((row) => row.locale).filter((l): l is string => !!l)),\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstyle={styles.newButton}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t('templates.list.newTemplate')}\n\t\t\t\t\t</button>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t\t<p style={styles.hint}>{t('templates.list.hint')}</p>\n\t\t\t{isLoading ? (\n\t\t\t\t<p style={styles.status}>{t('templates.list.loading')}</p>\n\t\t\t) : error ? (\n\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t{error.explanation}\n\t\t\t\t</p>\n\t\t\t) : (\n\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t{groups.map((group) => (\n\t\t\t\t\t\t<li key={group.type} style={styles.row}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => onSelectTemplate?.(group.primary)}\n\t\t\t\t\t\t\t\tstyle={styles.rowButton}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span style={styles.type}>{group.type}</span>\n\t\t\t\t\t\t\t\t{group.system ? (\n\t\t\t\t\t\t\t\t\t<span style={styles.builtIn}>{t('templates.builtInBadge')}</span>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t<span style={group.primary.active ? styles.active : styles.inactive}>\n\t\t\t\t\t\t\t\t\t<span style={styles.dot} />\n\t\t\t\t\t\t\t\t\t{group.primary.active ? t('common.status.active') : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{/* Siblings of the row button, not inside it: a button cannot hold buttons. */}\n\t\t\t\t\t\t\t<span style={styles.chips}>\n\t\t\t\t\t\t\t\t{group.entries.map((entry) => {\n\t\t\t\t\t\t\t\t\tconst name = entry.locale ?? t('templates.defaultChip');\n\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\tkey={entry.locale ?? ''}\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => onSelectTemplate?.(entry)}\n\t\t\t\t\t\t\t\t\t\t\tstyle={entry.active ? styles.chip : styles.chipInactive}\n\t\t\t\t\t\t\t\t\t\t\taria-label={t('templates.list.openLocale', { type: group.type, locale: name })}\n\t\t\t\t\t\t\t\t\t\t\ttitle={entry.active ? undefined : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{name}\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t))}\n\t\t\t\t</ul>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// Matches @fonderie/react-admin-screens' shell look with local values: this\n// package sits below the admin screens and cannot import their blocks.\nconst pill: CSSProperties = {\n\tdisplay: 'inline-flex',\n\talignItems: 'center',\n\tgap: 6,\n\tborderRadius: 999,\n\tpadding: '2px 9px 2px 8px',\n\tfontSize: 12,\n\tfontWeight: 600,\n\tlineHeight: 1.5,\n\tminWidth: 64,\n};\n\n// One per locale the email exists in. Monospace: they are language tags.\nconst chip: CSSProperties = {\n\theight: 24,\n\tboxSizing: 'border-box',\n\tpadding: '0 8px',\n\tborderRadius: 6,\n\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\tbackground: 'var(--fonderie-surface,#fff)',\n\tcolor: 'var(--fonderie-text,#171717)',\n\tfontSize: 12,\n\tfontFamily:\n\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\tcursor: 'pointer',\n};\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '32px 40px 64px', maxWidth: 1160, boxSizing: 'border-box' },\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: 0,\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\thint: { fontSize: 13.5, color: 'var(--fonderie-text-muted,#5c5c5c)', margin: '4px 0 24px' },\n\tstatus: { padding: '24px 0', color: 'var(--fonderie-text-muted,#5c5c5c)', fontSize: 13.5 },\n\terror: { color: 'var(--fonderie-danger,#e00)', marginBottom: 12, fontSize: 14 },\n\tlist: {\n\t\tlistStyle: 'none',\n\t\tpadding: 0,\n\t\tmargin: 0,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\toverflow: 'hidden',\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t},\n\trow: {\n\t\tdisplay: 'flex',\n\t\talignItems: 'center',\n\t\tgap: 8,\n\t\tpaddingRight: 16,\n\t\tborderBottom: '1px solid var(--fonderie-border-light,#f5f5f5)',\n\t},\n\trowButton: {\n\t\tflex: 1,\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tgap: 16,\n\t\tpadding: '13px 16px',\n\t\tbackground: 'none',\n\t\tborder: 'none',\n\t\tcursor: 'pointer',\n\t\ttextAlign: 'left',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily: 'inherit',\n\t},\n\ttype: {\n\t\tfontSize: 13,\n\t\tfontWeight: 600,\n\t\tflex: 1,\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\tchips: { display: 'flex', flexWrap: 'wrap', justifyContent: 'flex-end', gap: 6 },\n\tchip,\n\tchipInactive: { ...chip, opacity: 0.5, textDecoration: 'line-through' },\n\tdot: { width: 6, height: 6, borderRadius: 999, background: 'currentColor' },\n\tbuiltIn: {\n\t\tfontSize: 11,\n\t\tfontWeight: 500,\n\t\tpadding: '1px 7px',\n\t\tborderRadius: 999,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t},\n\tnewButton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 32,\n\t\tboxSizing: 'border-box',\n\t\tborderRadius: 6,\n\t\tpadding: '0 12px',\n\t\tfontSize: 13,\n\t\tfontWeight: 500,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t},\n\tactive: {\n\t\t...pill,\n\t\tcolor: 'var(--fonderie-accent-strong,#009767)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-accent-strong,#009767) 13%, transparent)',\n\t},\n\tinactive: {\n\t\t...pill,\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-text-muted,#5c5c5c) 10%, transparent)',\n\t},\n};\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\tFonderieApiError,\n\ttype ISetTemplateInput,\n\tcreateAdminT,\n} from '@fonderie/client';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useEffect, useState } from 'react';\n\nexport interface ITemplateCreateScreenProps {\n\tclient: CourierAdminClient;\n\t/**\n\t * Given ⇒ add a LOCALE of this existing template: the type is fixed and the\n\t * form starts from its default-locale copy. Omitted ⇒ a brand-new template\n\t * type (one the app sends that has no stored copy yet).\n\t */\n\ttype?: string;\n\t/** Locales already in use, offered as suggestions. */\n\tlocales?: string[];\n\t/** Called with what was created, so the caller can open it in the editor. */\n\tonCreated?: (created: { type: string; locale: string | null }) => void;\n\t/** The console's language. Default English. */\n\tlocale?: AdminLocale | undefined;\n}\n\nconst TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;\nconst LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;\n\n// Creating is its own screen: the editor loads an existing row, and a new\n// template or locale has none yet. Once saved, the editor takes over.\nexport function TemplateCreateScreen({\n\tclient,\n\ttype,\n\tlocales = [],\n\tonCreated,\n\tlocale,\n}: ITemplateCreateScreenProps) {\n\tconst tr = createAdminT(locale);\n\tconst addingLocale = type !== undefined;\n\tconst [newType, setNewType] = useState('');\n\tconst [targetLocale, setTargetLocale] = useState('');\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [error, setError] = useState<string | null>(null);\n\tconst [busy, setBusy] = useState(false);\n\n\t// A new locale starts from the default copy, so translating is editing.\n\tuseEffect(() => {\n\t\tif (!type) return;\n\t\tclient\n\t\t\t.getTemplate(type, null)\n\t\t\t.then(({ result }) => {\n\t\t\t\tsetSubject(result.subject ?? '');\n\t\t\t\tsetHtml(result.html ?? '');\n\t\t\t\tsetText(result.text);\n\t\t\t})\n\t\t\t.catch(() => undefined);\n\t}, [client, type]);\n\n\tconst submit = async (e: FormEvent) => {\n\t\te.preventDefault();\n\t\tsetError(null);\n\t\tconst t = addingLocale ? (type as string) : newType.trim();\n\t\tconst l = targetLocale.trim() || null;\n\t\tif (!TYPE_RE.test(t)) return setError(tr('templates.create.errorType'));\n\t\tif (addingLocale && !l) return setError(tr('templates.create.errorLocaleRequired'));\n\t\tif (l && !LOCALE_RE.test(l)) return setError(tr('templates.create.errorLocale'));\n\t\tif (!text.trim()) return setError(tr('templates.create.errorText'));\n\t\tsetBusy(true);\n\t\ttry {\n\t\t\t// Refuse to overwrite: creating an existing row would replace it.\n\t\t\tconst exists = await client.getTemplate(t, l).then(\n\t\t\t\t() => true,\n\t\t\t\t(err: unknown) => !(err instanceof FonderieApiError && err.status === 404),\n\t\t\t);\n\t\t\tif (exists) {\n\t\t\t\tsetError(\n\t\t\t\t\tl\n\t\t\t\t\t\t? tr('templates.create.existsLocale', { type: t, locale: l })\n\t\t\t\t\t\t: tr('templates.create.exists', { type: t }),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst input: ISetTemplateInput = { text, active: true };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tawait client.setTemplate(t, input, l);\n\t\t\tonCreated?.({ type: t, locale: l });\n\t\t} catch (err) {\n\t\t\tsetError(err instanceof FonderieApiError ? err.explanation : String(err));\n\t\t} finally {\n\t\t\tsetBusy(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>\n\t\t\t\t{addingLocale\n\t\t\t\t\t? tr('templates.create.titleLocale', { type: type as string })\n\t\t\t\t\t: tr('templates.create.titleNew')}\n\t\t\t</h1>\n\t\t\t<p style={styles.meta}>\n\t\t\t\t{addingLocale ? tr('templates.create.hintLocale') : tr('templates.create.hintNew')}\n\t\t\t</p>\n\t\t\t<form style={styles.form} onSubmit={(e) => void submit(e)}>\n\t\t\t\t{!addingLocale ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-type\">\n\t\t\t\t\t\t\t{tr('templates.create.type')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tid=\"new-template-type\"\n\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\tvalue={newType}\n\t\t\t\t\t\t\tplaceholder=\"weekly-digest\"\n\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\tonChange={(e) => setNewType(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</>\n\t\t\t\t) : null}\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-locale\">\n\t\t\t\t\t{addingLocale ? tr('templates.create.locale') : tr('templates.create.localeOptional')}\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\tid=\"new-template-locale\"\n\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\tvalue={targetLocale}\n\t\t\t\t\tplaceholder=\"fr\"\n\t\t\t\t\tlist=\"new-template-locales\"\n\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\tspellCheck={false}\n\t\t\t\t\tonChange={(e) => setTargetLocale(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<datalist id=\"new-template-locales\">\n\t\t\t\t\t{[...new Set(locales)].map((l) => (\n\t\t\t\t\t\t<option key={l} value={l} />\n\t\t\t\t\t))}\n\t\t\t\t</datalist>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-subject\">\n\t\t\t\t\t{tr('templates.subject')}\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\tid=\"new-template-subject\"\n\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\tvalue={subject}\n\t\t\t\t\tonChange={(e) => setSubject(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-html\">\n\t\t\t\t\t{tr('templates.htmlBody')}\n\t\t\t\t</label>\n\t\t\t\t<textarea\n\t\t\t\t\tid=\"new-template-html\"\n\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 160 }}\n\t\t\t\t\tvalue={html}\n\t\t\t\t\tonChange={(e) => setHtml(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-text\">\n\t\t\t\t\t{tr('templates.textBody')}\n\t\t\t\t</label>\n\t\t\t\t<textarea\n\t\t\t\t\tid=\"new-template-text\"\n\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 100 }}\n\t\t\t\t\tvalue={text}\n\t\t\t\t\tonChange={(e) => setText(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t{error ? (\n\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t{error}\n\t\t\t\t\t</p>\n\t\t\t\t) : null}\n\t\t\t\t<button type=\"submit\" disabled={busy} style={styles.button}>\n\t\t\t\t\t{busy\n\t\t\t\t\t\t? tr('templates.create.creating')\n\t\t\t\t\t\t: addingLocale\n\t\t\t\t\t\t\t? tr('templates.create.submitLocale')\n\t\t\t\t\t\t\t: tr('templates.create.submitNew')}\n\t\t\t\t</button>\n\t\t\t</form>\n\t\t</div>\n\t);\n}\n\nconst MONO =\n\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)';\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '8px 40px 64px', maxWidth: 760, boxSizing: 'border-box' },\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: '8px 0 4px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\tmeta: {\n\t\tfontSize: 13,\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t\tmarginBottom: 20,\n\t\tlineHeight: 1.5,\n\t},\n\tform: {\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tgap: 4,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\tpadding: '6px 20px 20px',\n\t},\n\tlabel: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },\n\tinput: {\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 13.5,\n\t\tfontFamily: 'inherit',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t},\n\ttextarea: {\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: 10,\n\t\tfontSize: 13,\n\t\tlineHeight: 1.55,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily: MONO,\n\t},\n\terror: {\n\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '8px 0',\n\t\tfontSize: 13.5,\n\t},\n\tbutton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t\tmarginTop: 16,\n\t},\n};\n"],"mappings":";AAAA;AAAA,EAMC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AAEP,SAAS,aAAa,WAAW,SAAS,QAAQ,gBAAgB;AAsL3C,cAYnB,YAZmB;AAlLvB,IAAM,WAAW,oBAAI,IAAI,CAAC,WAAW,aAAa,WAAW,CAAC;AAE9D,IAAM,sBAAsB;AA+BrB,SAAS,qBAAqB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAA+B;AAG9B,QAAM,IAAI,QAAQ,MAAM,aAAa,QAAQ,GAAG,CAAC,QAAQ,CAAC;AAC1D,QAAM,EAAE,UAAU,WAAW,OAAO,QAAQ,IAAI,YAAY,QAAQ,MAAM,MAAM;AAKhF,QAAM,EAAE,WAAW,cAAc,eAAe,IAAI,aAAa,MAAM;AAEvE,QAAM,WAAW,qBAAqB,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,GAAG,WAAW,CAAC;AAC3F,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,EAAE,WAAW,SAAS,IAAI,qBAAqB,QAAQ,MAAM,MAAM;AACzE,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,WAAW,YAAY,IAAI,SAAkC,IAAI;AAExE,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,EAAE;AACzC,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,EAAE;AACnC,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,EAAE;AACnC,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,IAAI;AAEzC,QAAM,EAAE,SAAS,cAAc,OAAO,cAAc,cAAc,IAAI,mBAAmB,MAAM;AAG/F,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,IAAI;AACjD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAwB,IAAI;AAElE,YAAU,MAAM;AACf,QAAI,CAAC,SAAU;AACf,eAAW,SAAS,WAAW,EAAE;AACjC,YAAQ,SAAS,QAAQ,EAAE;AAC3B,YAAQ,SAAS,IAAI;AACrB,cAAU,SAAS,MAAM;AAAA,EAC1B,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,MAAM,YAAY,YAAY;AACnC,QAAI,OAAgC,CAAC;AACrC,QAAI;AACH,YAAM,SAAkB,KAAK,MAAM,cAAc,IAAI;AACrD,UAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AACnE,uBAAe,EAAE,kCAAkC,CAAC;AACpD;AAAA,MACD;AACA,aAAO;AACP,qBAAe,IAAI;AAAA,IACpB,QAAQ;AACP,qBAAe,EAAE,gCAAgC,CAAC;AAClD;AAAA,IACD;AACA,UAAM,SAAS,MAAM;AAAA,MACpB;AAAA,MACA,EAAE,MAAM,MAAM,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,GAAI,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC,EAAG;AAAA,MACzE;AAAA,IACD;AAIA,UAAM,UAAU,OAAO,UAAU,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,KAAK,EAAE,KAAK,KAAK;AAC/E,QAAI,QAAQ,SAAS,GAAG;AACvB,YAAM,SAAS,EAAE,GAAG,MAAM,GAAG,OAAO,YAAY,QAAQ,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;AAC5E,oBAAc,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,IAC9C;AAAA,EACD,GAAG,CAAC,eAAe,MAAM,MAAM,SAAS,MAAM,QAAQ,YAAY,CAAC,CAAC;AAOpE,QAAM,SAAS,OAAO,GAAG;AACzB,SAAO,UAAU;AACjB,QAAM,aAAa,WAAW,GAAG,IAAI,IAAI,UAAU,EAAE,KAAK;AAI1D,YAAU,MAAM;AACf,QAAI,CAAC,WAAY;AACjB,UAAM,QAAQ,WAAW,MAAM,KAAK,OAAO,QAAQ,GAAG,mBAAmB;AACzE,WAAO,MAAM,aAAa,KAAK;AAAA,EAChC,GAAG,CAAC,YAAY,SAAS,MAAM,MAAM,UAAU,CAAC;AAIhD,QAAM,QACL,CAAC,CAAC,aACD,aAAa,SAAS,WAAW,OACjC,UAAU,SAAS,QAAQ,OAC3B,SAAS,SAAS,QAClB,WAAW,SAAS;AAEtB,QAAM,eAAe,OAAO,UAAqB;AAChD,UAAM,eAAe;AACrB,gBAAY,IAAI;AAChB,iBAAa,IAAI;AACjB,QAAI;AACH,YAAM,QAA2B,EAAE,MAAM,OAAO;AAChD,UAAI,QAAS,OAAM,UAAU;AAC7B,UAAI,KAAM,OAAM,OAAO;AACvB,UAAI,UAAU,YAAY,OAAW,OAAM,YAAY,SAAS;AAChE,YAAM,aAAa,MAAM,OAAO,MAAM;AAGtC,YAAM,QAAQ;AACd,gBAAU;AAAA,IACX,SAAS,KAAK;AAEb,mBAAa,GAAuB;AAAA,IACrC,UAAE;AACD,kBAAY,KAAK;AAAA,IAClB;AAAA,EACD;AAEA,QAAM,WAAW,CAAC,UAA0B;AAC3C,QAAI,SAAS,CAAC,OAAO,QAAQ,EAAE,iCAAiC,CAAC,EAAG;AACpE,qBAAiB,KAAK;AAAA,EACvB;AAEA,QAAM,eAAe,YAAY;AAChC,UAAM,UAAU,SACb,EAAE,wCAAwC,EAAE,MAAM,OAAO,CAAC,IAC1D,EAAE,kCAAkC,EAAE,KAAK,CAAC;AAC/C,QAAI,CAAC,OAAO,QAAQ,OAAO,EAAG;AAC9B,kBAAc,IAAI;AAClB,iBAAa,IAAI;AACjB,QAAI;AACH,YAAM,eAAe,MAAM,MAAM;AACjC,kBAAY;AAAA,IACb,SAAS,KAAK;AACb,mBAAa,GAAuB;AAAA,IACrC,UAAE;AACD,oBAAc,KAAK;AAAA,IACpB;AAAA,EACD;AAEA,MAAI,UAAW,QAAO,oBAAC,OAAE,OAAO,OAAO,QAAS,YAAE,0BAA0B,GAAE;AAC9E,MAAI;AACH,WACC,oBAAC,OAAE,OAAO,OAAO,OAAO,MAAK,SAC3B,gBAAM,aACR;AAGF,SACC,qBAAC,SAAI,OAAO,OAAO,WAClB;AAAA,wBAAC,QAAG,OAAO,OAAO,OAAQ,gBAAK;AAAA,IAC9B,kBAAkB,cAClB,qBAAC,SAAI,OAAO,OAAO,QACjB;AAAA;AAAA;AAAA;AAAA,QAGA,oBAAC,SAAI,cAAY,EAAE,0BAA0B,GAAG,OAAO,OAAO,MAC5D,mBAAS,IAAI,CAAC,UAAU;AACxB,gBAAM,WAAW,MAAM,YAAY,UAAU;AAC7C,iBACC;AAAA,YAAC;AAAA;AAAA,cAEA,MAAK;AAAA,cACL,gBAAc,WAAW,SAAS;AAAA,cAClC,SAAS,MAAO,WAAW,SAAY,SAAS,KAAK;AAAA,cACrD,OAAO,WAAW,OAAO,cAAc,MAAM,SAAS,OAAO,MAAM,OAAO;AAAA,cAC1E,OAAO,MAAM,SAAS,SAAY,EAAE,wBAAwB;AAAA,cAE3D,gBAAM,UAAU,EAAE,uBAAuB;AAAA;AAAA,YAPrC,MAAM,UAAU;AAAA,UAQtB;AAAA,QAEF,CAAC,GACF;AAAA,UACG;AAAA,MACH,cACA;AAAA,QAAC;AAAA;AAAA,UACA,MAAK;AAAA,UACL,OAAO,EAAE,GAAG,OAAO,gBAAgB,YAAY,OAAO;AAAA,UACtD,SAAS,MAAM;AACd,gBAAI,SAAS,CAAC,OAAO,QAAQ,EAAE,iCAAiC,CAAC,EAAG;AACpE,wBAAY,MAAM,EAAE,SAAS,uBAAuB,WAAW,IAAI,EAAE,CAAC;AAAA,UACvE;AAAA,UAEC,YAAE,4BAA4B;AAAA;AAAA,MAChC,IACG;AAAA,OACL,IACG;AAAA,IACJ,qBAAC,OAAE,OAAO,OAAO,MAChB;AAAA,0BAAC,YAAQ,oBAAU,EAAE,yBAAyB,GAAE;AAAA,MAAS;AAAA,MAAK,UAAU,WAAW;AAAA,MAClF,SAAS,SAAM,EAAE,8BAA8B,CAAC,KAAK;AAAA,OACvD;AAAA,IAEA,qBAAC,SAAI,OAAO,OAAO,OAClB;AAAA,2BAAC,SAAI,OAAO,OAAO,QAClB;AAAA,6BAAC,UAAK,OAAO,OAAO,MAAM,UAAU,cACnC;AAAA,8BAAC,WAAM,OAAO,OAAO,OAAO,SAAQ,oBAClC,YAAE,mBAAmB,GACvB;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACA,IAAG;AAAA,cACH,OAAO,OAAO;AAAA,cACd,OAAO;AAAA,cACP,UAAU,CAAC,UAAU,WAAW,MAAM,OAAO,KAAK;AAAA;AAAA,UACnD;AAAA,UAEA,oBAAC,WAAM,OAAO,OAAO,OAAO,SAAQ,iBAClC,YAAE,oBAAoB,GACxB;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACA,IAAG;AAAA,cACH,OAAO,OAAO;AAAA,cACd,OAAO;AAAA,cACP,UAAU,CAAC,UAAU,QAAQ,MAAM,OAAO,KAAK;AAAA,cAC/C,MAAM;AAAA;AAAA,UACP;AAAA,UAEA,oBAAC,WAAM,OAAO,OAAO,OAAO,SAAQ,iBAClC,YAAE,oBAAoB,GACxB;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACA,IAAG;AAAA,cACH,OAAO,OAAO;AAAA,cACd,OAAO;AAAA,cACP,UAAU,CAAC,UAAU,QAAQ,MAAM,OAAO,KAAK;AAAA,cAC/C,MAAM;AAAA,cACN,UAAQ;AAAA;AAAA,UACT;AAAA,UAEA,qBAAC,WAAM,OAAO,OAAO,eACpB;AAAA;AAAA,cAAC;AAAA;AAAA,gBACA,MAAK;AAAA,gBACL,SAAS;AAAA,gBACT,UAAU,CAAC,UAAU,UAAU,MAAM,OAAO,OAAO;AAAA;AAAA,YACpD;AAAA,YACC,EAAE,yBAAyB;AAAA,aAC7B;AAAA,UAEC,WAAW,WAAW,qBACtB,qBAAC,OAAE,OAAO,OAAO,OAAO,MAAK,SAC3B;AAAA,cAAE,2BAA2B;AAAA,YAAG;AAAA,YACjC;AAAA,cAAC;AAAA;AAAA,gBACA,MAAK;AAAA,gBACL,OAAO,OAAO;AAAA,gBACd,SAAS,MAAM;AACd,+BAAa,IAAI;AACjB,uBAAK,QAAQ;AAAA,gBACd;AAAA,gBAEC,YAAE,eAAe;AAAA;AAAA,YACnB;AAAA,aACD,IACG,YACH,oBAAC,OAAE,OAAO,OAAO,OAAO,MAAK,SAC3B,oBAAU,aACZ,IACG;AAAA,UAEJ,qBAAC,SAAI,OAAO,OAAO,SAClB;AAAA;AAAA,cAAC;AAAA;AAAA,gBACA,MAAK;AAAA,gBACL,UAAU,YAAY,CAAC;AAAA,gBACvB,OAAO,QAAQ,OAAO,SAAS,OAAO;AAAA,gBAErC,qBAAW,EAAE,eAAe,IAAI,EAAE,aAAa;AAAA;AAAA,YACjD;AAAA,YACC,CAAC,SAAS,CAAC,YACX,oBAAC,UAAK,OAAO,OAAO,MAAO,YAAE,4BAA4B,GAAE;AAAA,YAE3D,CAAC,SACD;AAAA,cAAC;AAAA;AAAA,gBACA,MAAK;AAAA,gBACL,UAAU;AAAA,gBACV,SAAS,MAAM,KAAK,aAAa;AAAA,gBACjC,OAAO;AAAA,kBACN,GAAG,OAAO;AAAA,kBACV,QAAQ;AAAA,kBACR,YAAY;AAAA,kBACZ,OAAO;AAAA,kBACP,aAAa;AAAA,gBACd;AAAA,gBAEC,uBAAa,EAAE,iBAAiB,IAAI,EAAE,eAAe;AAAA;AAAA,YACvD,IACG;AAAA,aACL;AAAA,WACD;AAAA,QAEA,oBAAC,WAAM,OAAO,OAAO,OAAO,SAAQ,mBAClC,YAAE,6BAA6B,GACjC;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACA,IAAG;AAAA,YACH,OAAO,OAAO;AAAA,YACd,OAAO;AAAA,YACP,UAAU,CAAC,UAAU,cAAc,MAAM,OAAO,KAAK;AAAA,YACrD,MAAM;AAAA,YACN,YAAY;AAAA;AAAA,QACb;AAAA,QACC,eACA,oBAAC,OAAE,OAAO,OAAO,OAAO,MAAK,SAC3B,uBACF;AAAA,SAEF;AAAA,MAEA,qBAAC,SAAI,OAAO,OAAO,eAClB;AAAA,6BAAC,SAAI,OAAO,OAAO,eAClB;AAAA,8BAAC,UAAK,OAAO,OAAO,OAAQ,YAAE,0BAA0B,GAAE;AAAA,UAC1D,oBAAC,UAAK,OAAO,OAAO,MAClB,yBAAe,EAAE,4BAA4B,IAAI,EAAE,uBAAuB,GAC5E;AAAA,WACD;AAAA,QACC,gBACA,oBAAC,OAAE,OAAO,OAAO,OAAO,MAAK,SAC3B,uBAAa,aACf;AAAA,QAEA,SAAS,WAAW,oBAAC,OAAE,OAAO,OAAO,gBAAiB,kBAAQ,SAAQ;AAAA,QACtE,SAAS;AAAA;AAAA;AAAA;AAAA,UAIT;AAAA,YAAC;AAAA;AAAA,cACA,OAAO,EAAE,+BAA+B;AAAA,cACxC,OAAO,OAAO;AAAA,cACd,SAAQ;AAAA,cACR,QAAQ,QAAQ;AAAA;AAAA,UACjB;AAAA,YACG,UACH,oBAAC,SAAI,OAAO,OAAO,aAAc,kBAAQ,MAAK,IAE9C,oBAAC,OAAE,OAAO,OAAO,MAAO,YAAE,kCAAkC,GAAE;AAAA,SAEhE;AAAA,OACD;AAAA,IAEC,UAAU,SAAS,KACnB,qBAAC,SAAI,OAAO,OAAO,WAClB;AAAA,0BAAC,QAAG,OAAO,OAAO,UAAW,YAAE,0BAA0B,GAAE;AAAA,MAC3D,oBAAC,QAAG,OAAO,OAAO,MAChB,oBAAU,IAAI,CAAC,QACf,qBAAC,QAAqB,OAAO,OAAO,KACnC;AAAA,6BAAC,UAAK;AAAA;AAAA,UACH,IAAI;AAAA,UAAQ;AAAA,UAAI,IAAI,SAAS,EAAE,+BAA+B;AAAA,UAAE;AAAA,UAAG;AAAA,UACpE,gBAAgB,IAAI,WAAW,QAAQ;AAAA,WACzC;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACA,MAAK;AAAA,YACL,SAAS,MAAM,SAAS,IAAI,OAAO;AAAA,YACnC,OAAO,OAAO;AAAA,YAEb,YAAE,2BAA2B;AAAA;AAAA,QAC/B;AAAA,WAXQ,IAAI,OAYb,CACA,GACF;AAAA,OACD;AAAA,KAEF;AAEF;AAEA,IAAM,MAAqB;AAAA,EAC1B,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,OAAO;AAAA,EACP,UAAU;AAAA,EACV,YACC;AAAA,EACD,QAAQ;AACT;AAEA,IAAM,SAAwC;AAAA,EAC7C,WAAW,EAAE,SAAS,iBAAiB,UAAU,MAAM,WAAW,aAAa;AAAA;AAAA,EAE/E,QAAQ;AAAA,IACP,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,eAAe;AAAA,EAChB;AAAA,EACA,MAAM,EAAE,SAAS,QAAQ,UAAU,QAAQ,KAAK,GAAG,cAAc,GAAG;AAAA,EACpE;AAAA,EACA,aAAa;AAAA,IACZ,GAAG;AAAA,IACH,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,mBAAmB;AAAA,IACnB,QAAQ;AAAA,EACT;AAAA,EACA,aAAa,EAAE,GAAG,KAAK,SAAS,MAAM,gBAAgB,eAAe;AAAA;AAAA;AAAA,EAGrE,OAAO;AAAA,IACN,SAAS;AAAA,IACT,qBAAqB;AAAA,IACrB,KAAK;AAAA,IACL,YAAY;AAAA,EACb;AAAA,EACA,QAAQ,EAAE,UAAU,GAAG,SAAS,QAAQ,eAAe,SAAS;AAAA;AAAA,EAEhE,eAAe;AAAA,IACd,UAAU;AAAA,IACV,SAAS;AAAA,IACT,eAAe;AAAA,IACf,UAAU;AAAA,IACV,KAAK;AAAA,EACN;AAAA,EACA,SAAS,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,IAAI,WAAW,GAAG;AAAA,EACzE,eAAe;AAAA,IACd,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,WAAW;AAAA,EACZ;AAAA,EACA,gBAAgB,EAAE,UAAU,IAAI,YAAY,KAAK,QAAQ,QAAQ;AAAA,EACjE,cAAc;AAAA,IACb,WAAW;AAAA,IACX,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,YAAY;AAAA,EACb;AAAA,EACA,aAAa;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YACC;AAAA,EACF;AAAA,EACA,OAAO;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,YAAY;AAAA,EACb;AAAA,EACA,MAAM,EAAE,UAAU,IAAI,OAAO,sCAAsC,cAAc,GAAG;AAAA,EACpF,QAAQ,EAAE,SAAS,IAAI,WAAW,UAAU,OAAO,qCAAqC;AAAA,EACxF,OAAO;AAAA,IACN,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,UAAU;AAAA,EACX;AAAA,EACA,MAAM;AAAA,IACL,SAAS;AAAA,IACT,eAAe;AAAA,IACf,KAAK;AAAA,IACL,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,WAAW;AAAA,EACZ;AAAA,EACA,OAAO,EAAE,UAAU,IAAI,YAAY,KAAK,WAAW,IAAI,cAAc,EAAE;AAAA,EACvE,OAAO;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,OAAO;AAAA,EACR;AAAA,EACA,UAAU;AAAA,IACT,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,YACC;AAAA,EACF;AAAA,EACA,eAAe,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,GAAG,WAAW,IAAI,UAAU,GAAG;AAAA,EAC5F,QAAQ;AAAA,IACP,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,EACZ;AAAA,EACA,gBAAgB;AAAA,IACf,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,EACT;AAAA,EACA,WAAW,EAAE,WAAW,GAAG;AAAA,EAC3B,UAAU;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,eAAe;AAAA,EAChB;AAAA,EACA,MAAM;AAAA,IACL,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,UAAU;AAAA,EACX;AAAA,EACA,KAAK;AAAA,IACJ,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,SAAS;AAAA,IACT,cAAc;AAAA,IACd,UAAU;AAAA,EACX;AAAA,EACA,gBAAgB;AAAA,IACf,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACT;AACD;;;AC/mBA;AAAA,EAIC,gBAAAA;AAAA,EACA,wBAAAC;AAAA,OACM;AACP,SAAS,gBAAAC,qBAAoB;AA6B1B,SAGC,OAAAC,MAHD,QAAAC,aAAA;AAZI,SAAS,mBAAmB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAA6B;AAC5B,QAAM,IAAIJ,cAAa,MAAM;AAC7B,QAAM,EAAE,WAAW,WAAW,MAAM,IAAIE,cAAa,MAAM;AAC3D,QAAM,SAASD,sBAAqB,SAAS;AAE7C,SACC,gBAAAG,MAAC,SAAI,OAAOC,QAAO,WAClB;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACA,OAAO,EAAE,SAAS,QAAQ,YAAY,UAAU,gBAAgB,iBAAiB,KAAK,GAAG;AAAA,QAEzF;AAAA,0BAAAD,KAAC,QAAG,OAAOE,QAAO,OAAQ,YAAE,sBAAsB,GAAE;AAAA,UACnD,mBACA,gBAAAF;AAAA,YAAC;AAAA;AAAA,cACA,MAAK;AAAA,cACL,SAAS,MACR,iBAAiB;AAAA,gBAChB,SAAS;AAAA,kBACR,GAAG,IAAI,IAAI,UAAU,IAAI,CAAC,QAAQ,IAAI,MAAM,EAAE,OAAO,CAAC,MAAmB,CAAC,CAAC,CAAC,CAAC;AAAA,gBAC9E;AAAA,cACD,CAAC;AAAA,cAEF,OAAOE,QAAO;AAAA,cAEb,YAAE,4BAA4B;AAAA;AAAA,UAChC,IACG;AAAA;AAAA;AAAA,IACL;AAAA,IACA,gBAAAF,KAAC,OAAE,OAAOE,QAAO,MAAO,YAAE,qBAAqB,GAAE;AAAA,IAChD,YACA,gBAAAF,KAAC,OAAE,OAAOE,QAAO,QAAS,YAAE,wBAAwB,GAAE,IACnD,QACH,gBAAAF,KAAC,OAAE,OAAOE,QAAO,OAAO,MAAK,SAC3B,gBAAM,aACR,IAEA,gBAAAF,KAAC,QAAG,OAAOE,QAAO,MAChB,iBAAO,IAAI,CAAC,UACZ,gBAAAD,MAAC,QAAoB,OAAOC,QAAO,KAClC;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACA,MAAK;AAAA,UACL,SAAS,MAAM,mBAAmB,MAAM,OAAO;AAAA,UAC/C,OAAOC,QAAO;AAAA,UAEd;AAAA,4BAAAF,KAAC,UAAK,OAAOE,QAAO,MAAO,gBAAM,MAAK;AAAA,YACrC,MAAM,SACN,gBAAAF,KAAC,UAAK,OAAOE,QAAO,SAAU,YAAE,wBAAwB,GAAE,IACvD;AAAA,YACJ,gBAAAD,MAAC,UAAK,OAAO,MAAM,QAAQ,SAASC,QAAO,SAASA,QAAO,UAC1D;AAAA,8BAAAF,KAAC,UAAK,OAAOE,QAAO,KAAK;AAAA,cACxB,MAAM,QAAQ,SAAS,EAAE,sBAAsB,IAAI,EAAE,wBAAwB;AAAA,eAC/E;AAAA;AAAA;AAAA,MACD;AAAA,MAEA,gBAAAF,KAAC,UAAK,OAAOE,QAAO,OAClB,gBAAM,QAAQ,IAAI,CAAC,UAAU;AAC7B,cAAM,OAAO,MAAM,UAAU,EAAE,uBAAuB;AACtD,eACC,gBAAAF;AAAA,UAAC;AAAA;AAAA,YAEA,MAAK;AAAA,YACL,SAAS,MAAM,mBAAmB,KAAK;AAAA,YACvC,OAAO,MAAM,SAASE,QAAO,OAAOA,QAAO;AAAA,YAC3C,cAAY,EAAE,6BAA6B,EAAE,MAAM,MAAM,MAAM,QAAQ,KAAK,CAAC;AAAA,YAC7E,OAAO,MAAM,SAAS,SAAY,EAAE,wBAAwB;AAAA,YAE3D;AAAA;AAAA,UAPI,MAAM,UAAU;AAAA,QAQtB;AAAA,MAEF,CAAC,GACF;AAAA,SAhCQ,MAAM,IAiCf,CACA,GACF;AAAA,KAEF;AAEF;AAIA,IAAM,OAAsB;AAAA,EAC3B,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,KAAK;AAAA,EACL,cAAc;AAAA,EACd,SAAS;AAAA,EACT,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,UAAU;AACX;AAGA,IAAM,OAAsB;AAAA,EAC3B,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,SAAS;AAAA,EACT,cAAc;AAAA,EACd,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,UAAU;AAAA,EACV,YACC;AAAA,EACD,QAAQ;AACT;AAEA,IAAMA,UAAwC;AAAA,EAC7C,WAAW,EAAE,SAAS,kBAAkB,UAAU,MAAM,WAAW,aAAa;AAAA,EAChF,OAAO;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,YAAY;AAAA,EACb;AAAA,EACA,MAAM,EAAE,UAAU,MAAM,OAAO,sCAAsC,QAAQ,aAAa;AAAA,EAC1F,QAAQ,EAAE,SAAS,UAAU,OAAO,sCAAsC,UAAU,KAAK;AAAA,EACzF,OAAO,EAAE,OAAO,+BAA+B,cAAc,IAAI,UAAU,GAAG;AAAA,EAC9E,MAAM;AAAA,IACL,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,UAAU;AAAA,IACV,WAAW;AAAA,EACZ;AAAA,EACA,KAAK;AAAA,IACJ,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,cAAc;AAAA,IACd,cAAc;AAAA,EACf;AAAA,EACA,WAAW;AAAA,IACV,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,OAAO;AAAA,IACP,YAAY;AAAA,EACb;AAAA,EACA,MAAM;AAAA,IACL,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,YACC;AAAA,EACF;AAAA,EACA,OAAO,EAAE,SAAS,QAAQ,UAAU,QAAQ,gBAAgB,YAAY,KAAK,EAAE;AAAA,EAC/E;AAAA,EACA,cAAc,EAAE,GAAG,MAAM,SAAS,KAAK,gBAAgB,eAAe;AAAA,EACtE,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,cAAc,KAAK,YAAY,eAAe;AAAA,EAC1E,SAAS;AAAA,IACR,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,OAAO;AAAA,EACR;AAAA,EACA,WAAW;AAAA,IACV,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,QAAQ;AAAA,EACT;AAAA,EACA,QAAQ;AAAA,IACP,GAAG;AAAA,IACH,OAAO;AAAA,IACP,YAAY;AAAA,EACb;AAAA,EACA,UAAU;AAAA,IACT,GAAG;AAAA,IACH,OAAO;AAAA,IACP,YAAY;AAAA,EACb;AACD;;;AChOA;AAAA,EAGC;AAAA,EAEA,gBAAAC;AAAA,OACM;AAEP,SAAS,aAAAC,YAAW,YAAAC,iBAAgB;AA2FjC,SAUE,UAVF,OAAAC,MAUE,QAAAC,aAVF;AAzEH,IAAM,UAAU;AAChB,IAAM,YAAY;AAIX,SAAS,qBAAqB;AAAA,EACpC;AAAA,EACA;AAAA,EACA,UAAU,CAAC;AAAA,EACX;AAAA,EACA;AACD,GAA+B;AAC9B,QAAM,KAAKJ,cAAa,MAAM;AAC9B,QAAM,eAAe,SAAS;AAC9B,QAAM,CAAC,SAAS,UAAU,IAAIE,UAAS,EAAE;AACzC,QAAM,CAAC,cAAc,eAAe,IAAIA,UAAS,EAAE;AACnD,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,EAAE;AACzC,QAAM,CAAC,MAAM,OAAO,IAAIA,UAAS,EAAE;AACnC,QAAM,CAAC,MAAM,OAAO,IAAIA,UAAS,EAAE;AACnC,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAwB,IAAI;AACtD,QAAM,CAAC,MAAM,OAAO,IAAIA,UAAS,KAAK;AAGtC,EAAAD,WAAU,MAAM;AACf,QAAI,CAAC,KAAM;AACX,WACE,YAAY,MAAM,IAAI,EACtB,KAAK,CAAC,EAAE,OAAO,MAAM;AACrB,iBAAW,OAAO,WAAW,EAAE;AAC/B,cAAQ,OAAO,QAAQ,EAAE;AACzB,cAAQ,OAAO,IAAI;AAAA,IACpB,CAAC,EACA,MAAM,MAAM,MAAS;AAAA,EACxB,GAAG,CAAC,QAAQ,IAAI,CAAC;AAEjB,QAAM,SAAS,OAAO,MAAiB;AACtC,MAAE,eAAe;AACjB,aAAS,IAAI;AACb,UAAM,IAAI,eAAgB,OAAkB,QAAQ,KAAK;AACzD,UAAM,IAAI,aAAa,KAAK,KAAK;AACjC,QAAI,CAAC,QAAQ,KAAK,CAAC,EAAG,QAAO,SAAS,GAAG,4BAA4B,CAAC;AACtE,QAAI,gBAAgB,CAAC,EAAG,QAAO,SAAS,GAAG,sCAAsC,CAAC;AAClF,QAAI,KAAK,CAAC,UAAU,KAAK,CAAC,EAAG,QAAO,SAAS,GAAG,8BAA8B,CAAC;AAC/E,QAAI,CAAC,KAAK,KAAK,EAAG,QAAO,SAAS,GAAG,4BAA4B,CAAC;AAClE,YAAQ,IAAI;AACZ,QAAI;AAEH,YAAM,SAAS,MAAM,OAAO,YAAY,GAAG,CAAC,EAAE;AAAA,QAC7C,MAAM;AAAA,QACN,CAAC,QAAiB,EAAE,eAAe,oBAAoB,IAAI,WAAW;AAAA,MACvE;AACA,UAAI,QAAQ;AACX;AAAA,UACC,IACG,GAAG,iCAAiC,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAC,IAC1D,GAAG,2BAA2B,EAAE,MAAM,EAAE,CAAC;AAAA,QAC7C;AACA;AAAA,MACD;AACA,YAAM,QAA2B,EAAE,MAAM,QAAQ,KAAK;AACtD,UAAI,QAAS,OAAM,UAAU;AAC7B,UAAI,KAAM,OAAM,OAAO;AACvB,YAAM,OAAO,YAAY,GAAG,OAAO,CAAC;AACpC,kBAAY,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAC;AAAA,IACnC,SAAS,KAAK;AACb,eAAS,eAAe,mBAAmB,IAAI,cAAc,OAAO,GAAG,CAAC;AAAA,IACzE,UAAE;AACD,cAAQ,KAAK;AAAA,IACd;AAAA,EACD;AAEA,SACC,gBAAAG,MAAC,SAAI,OAAOC,QAAO,WAClB;AAAA,oBAAAF,KAAC,QAAG,OAAOE,QAAO,OAChB,yBACE,GAAG,gCAAgC,EAAE,KAAqB,CAAC,IAC3D,GAAG,2BAA2B,GAClC;AAAA,IACA,gBAAAF,KAAC,OAAE,OAAOE,QAAO,MACf,yBAAe,GAAG,6BAA6B,IAAI,GAAG,0BAA0B,GAClF;AAAA,IACA,gBAAAD,MAAC,UAAK,OAAOC,QAAO,MAAM,UAAU,CAAC,MAAM,KAAK,OAAO,CAAC,GACtD;AAAA,OAAC,eACD,gBAAAD,MAAA,YACC;AAAA,wBAAAD,KAAC,WAAM,OAAOE,QAAO,OAAO,SAAQ,qBAClC,aAAG,uBAAuB,GAC5B;AAAA,QACA,gBAAAF;AAAA,UAAC;AAAA;AAAA,YACA,IAAG;AAAA,YACH,OAAOE,QAAO;AAAA,YACd,OAAO;AAAA,YACP,aAAY;AAAA,YACZ,cAAa;AAAA,YACb,YAAY;AAAA,YACZ,UAAU,CAAC,MAAM,WAAW,EAAE,OAAO,KAAK;AAAA;AAAA,QAC3C;AAAA,SACD,IACG;AAAA,MACJ,gBAAAF,KAAC,WAAM,OAAOE,QAAO,OAAO,SAAQ,uBAClC,yBAAe,GAAG,yBAAyB,IAAI,GAAG,iCAAiC,GACrF;AAAA,MACA,gBAAAF;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAOE,QAAO;AAAA,UACd,OAAO;AAAA,UACP,aAAY;AAAA,UACZ,MAAK;AAAA,UACL,cAAa;AAAA,UACb,YAAY;AAAA,UACZ,UAAU,CAAC,MAAM,gBAAgB,EAAE,OAAO,KAAK;AAAA;AAAA,MAChD;AAAA,MACA,gBAAAF,KAAC,cAAS,IAAG,wBACX,WAAC,GAAG,IAAI,IAAI,OAAO,CAAC,EAAE,IAAI,CAAC,MAC3B,gBAAAA,KAAC,YAAe,OAAO,KAAV,CAAa,CAC1B,GACF;AAAA,MACA,gBAAAA,KAAC,WAAM,OAAOE,QAAO,OAAO,SAAQ,wBAClC,aAAG,mBAAmB,GACxB;AAAA,MACA,gBAAAF;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAOE,QAAO;AAAA,UACd,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,WAAW,EAAE,OAAO,KAAK;AAAA;AAAA,MAC3C;AAAA,MACA,gBAAAF,KAAC,WAAM,OAAOE,QAAO,OAAO,SAAQ,qBAClC,aAAG,oBAAoB,GACzB;AAAA,MACA,gBAAAF;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAO,EAAE,GAAGE,QAAO,UAAU,WAAW,IAAI;AAAA,UAC5C,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA;AAAA,MACxC;AAAA,MACA,gBAAAF,KAAC,WAAM,OAAOE,QAAO,OAAO,SAAQ,qBAClC,aAAG,oBAAoB,GACzB;AAAA,MACA,gBAAAF;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAO,EAAE,GAAGE,QAAO,UAAU,WAAW,IAAI;AAAA,UAC5C,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA;AAAA,MACxC;AAAA,MACC,QACA,gBAAAF,KAAC,OAAE,OAAOE,QAAO,OAAO,MAAK,SAC3B,iBACF,IACG;AAAA,MACJ,gBAAAF,KAAC,YAAO,MAAK,UAAS,UAAU,MAAM,OAAOE,QAAO,QAClD,iBACE,GAAG,2BAA2B,IAC9B,eACC,GAAG,+BAA+B,IAClC,GAAG,4BAA4B,GACpC;AAAA,OACD;AAAA,KACD;AAEF;AAEA,IAAM,OACL;AACD,IAAMA,UAAwC;AAAA,EAC7C,WAAW,EAAE,SAAS,iBAAiB,UAAU,KAAK,WAAW,aAAa;AAAA,EAC9E,OAAO;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,YAAY;AAAA,EACb;AAAA,EACA,MAAM;AAAA,IACL,UAAU;AAAA,IACV,OAAO;AAAA,IACP,cAAc;AAAA,IACd,YAAY;AAAA,EACb;AAAA,EACA,MAAM;AAAA,IACL,SAAS;AAAA,IACT,eAAe;AAAA,IACf,KAAK;AAAA,IACL,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,EACV;AAAA,EACA,OAAO,EAAE,UAAU,IAAI,YAAY,KAAK,WAAW,IAAI,cAAc,EAAE;AAAA,EACvE,OAAO;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,OAAO;AAAA,EACR;AAAA,EACA,UAAU;AAAA,IACT,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,YAAY;AAAA,EACb;AAAA,EACA,OAAO;AAAA,IACN,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,UAAU;AAAA,EACX;AAAA,EACA,QAAQ;AAAA,IACP,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,WAAW;AAAA,EACZ;AACD;","names":["createAdminT","groupTemplatesByType","useTemplates","jsx","jsxs","styles","createAdminT","useEffect","useState","jsx","jsxs","styles"]}
1
+ {"version":3,"sources":["../src/screens/TemplateEditorScreen.tsx","../src/screens/TemplateListScreen.tsx","../src/screens/TemplateCreateScreen.tsx"],"sourcesContent":["import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\ttype FonderieApiError,\n\ttype ISetTemplateInput,\n\ttype ITemplateEntry,\n\tcreateAdminT,\n\tformatAdminDate,\n\tgroupTemplatesByType,\n\tmissingTemplateLocales,\n} from '@fonderie/client';\nimport {\n\tuseTemplate,\n\tuseTemplatePreview,\n\tuseTemplateRevisions,\n\tuseTemplates,\n} from '@fonderie/react-courier-admin';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\n// Implicit variables the layout injects — an operator never supplies these, so\n// offering them as fields would just be noise.\nconst IMPLICIT = new Set(['subject', 'preheader', 'brandName']);\n\nconst PREVIEW_DEBOUNCE_MS = 500;\n\nexport interface ITemplateEditorScreenProps {\n\tclient: CourierAdminClient;\n\ttype: string;\n\tlocale?: string | null;\n\tonSaved?: () => void;\n\t/**\n\t * A built-in email's default-locale row (the list's `system` flag): it can\n\t * be edited and rolled back, never deleted — Delete is not offered.\n\t */\n\tsystem?: boolean;\n\t/** Called after a delete; the template no longer exists. */\n\tonDeleted?: () => void;\n\t/**\n\t * Shows \"Add locale\": create a translation of this template. `locales` are\n\t * the ones the app uses elsewhere that this email lacks — suggest them first.\n\t */\n\tonAddLocale?: (type: string, context: { locales: string[] }) => void;\n\t/**\n\t * Shows a tab per locale this email exists in; receives the row to open.\n\t * Unsaved edits are confirmed before switching away.\n\t */\n\tonSelectLocale?: (template: ITemplateEntry) => void;\n\t/**\n\t * The CONSOLE's language (default English). Named `uiLocale` because\n\t * `locale` here is the template's own locale.\n\t */\n\tuiLocale?: AdminLocale | undefined;\n}\n\nexport function TemplateEditorScreen({\n\tclient,\n\ttype,\n\tlocale,\n\tonSaved,\n\tsystem = false,\n\tonDeleted,\n\tonAddLocale,\n\tonSelectLocale,\n\tuiLocale,\n}: ITemplateEditorScreenProps) {\n\t// Memoized: `run` below depends on it, and a new translator per render would\n\t// restart the preview debounce on every render.\n\tconst t = useMemo(() => createAdminT(uiLocale), [uiLocale]);\n\tconst { template, isLoading, error, refresh } = useTemplate(client, type, locale);\n\t// Saves go through the list hook (which re-fetches the template list after\n\t// each write); mounting it adds a template-list fetch to this single-template\n\t// editor — acceptable for an admin dashboard. Its isLoading/error track that\n\t// list fetch, so the save action keeps local state.\n\tconst { templates, saveTemplate, removeTemplate } = useTemplates(client);\n\t// This email's locales, for the tabs. The list is already fetched for saves.\n\tconst siblings = groupTemplatesByType(templates).find((g) => g.type === type)?.entries ?? [];\n\tconst [isDeleting, setIsDeleting] = useState(false);\n\tconst { revisions, rollback } = useTemplateRevisions(client, type, locale);\n\tconst [isSaving, setIsSaving] = useState(false);\n\tconst [saveError, setSaveError] = useState<FonderieApiError | null>(null);\n\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [active, setActive] = useState(true);\n\n\tconst { preview, isPreviewing, error: previewError, renderPreview } = useTemplatePreview(client);\n\t// The sample values, as JSON the operator can edit. Text rather than an\n\t// object so a half-typed value does not have to parse on every keystroke.\n\tconst [sampleJson, setSampleJson] = useState('{}');\n\tconst [sampleError, setSampleError] = useState<string | null>(null);\n\n\tuseEffect(() => {\n\t\tif (!template) return;\n\t\tsetSubject(template.subject ?? '');\n\t\tsetHtml(template.html ?? '');\n\t\tsetText(template.text);\n\t\tsetActive(template.active);\n\t}, [template]);\n\n\tconst run = useCallback(async () => {\n\t\tlet data: Record<string, unknown> = {};\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(sampleJson || '{}');\n\t\t\tif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\t\t\tsetSampleError(t('templates.editor.sampleNotObject'));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdata = parsed as Record<string, unknown>;\n\t\t\tsetSampleError(null);\n\t\t} catch {\n\t\t\tsetSampleError(t('templates.editor.sampleInvalid'));\n\t\t\treturn;\n\t\t}\n\t\tconst result = await renderPreview(\n\t\t\ttype,\n\t\t\t{ text, data, ...(subject ? { subject } : {}), ...(html ? { html } : {}) },\n\t\t\tlocale,\n\t\t);\n\t\t// The server reports which variables this content uses; seed the ones\n\t\t// that have no value yet with their own name, so the render shows where\n\t\t// each lands. Never overwrite something already typed.\n\t\tconst missing = result.variables.filter((v) => !IMPLICIT.has(v) && !(v in data));\n\t\tif (missing.length > 0) {\n\t\t\tconst seeded = { ...data, ...Object.fromEntries(missing.map((v) => [v, v])) };\n\t\t\tsetSampleJson(JSON.stringify(seeded, null, 2));\n\t\t}\n\t}, [renderPreview, type, text, subject, html, locale, sampleJson, t]);\n\n\t// The preview is always live: it renders once the template loads, then again\n\t// PREVIEW_DEBOUNCE_MS after the last edit to the content or the sample data.\n\t// Debounced, not per keystroke, so typing a paragraph costs one request.\n\t// `run` is a new function on every keystroke (it closes over the fields), so\n\t// the effect keys on the fields themselves and calls the latest `run`.\n\tconst runRef = useRef(run);\n\trunRef.current = run;\n\tconst loadedType = template ? `${type}:${locale ?? ''}` : null;\n\t// The fields ARE the trigger: each edit restarts the debounce. The effect\n\t// body reads the latest `run` through the ref, so it does not use them.\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: see above\n\tuseEffect(() => {\n\t\tif (!loadedType) return;\n\t\tconst timer = setTimeout(() => void runRef.current(), PREVIEW_DEBOUNCE_MS);\n\t\treturn () => clearTimeout(timer);\n\t}, [loadedType, subject, html, text, sampleJson]);\n\n\t// Saving what is already stored would only mint an identical version (the\n\t// server treats it as a no-op anyway), so Save waits for a real change.\n\tconst dirty =\n\t\t!!template &&\n\t\t(subject !== (template.subject ?? '') ||\n\t\t\thtml !== (template.html ?? '') ||\n\t\t\ttext !== template.text ||\n\t\t\tactive !== template.active);\n\n\tconst handleSubmit = async (event: FormEvent) => {\n\t\tevent.preventDefault();\n\t\tsetIsSaving(true);\n\t\tsetSaveError(null);\n\t\ttry {\n\t\t\tconst input: ISetTemplateInput = { text, active };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tif (template?.version !== undefined) input.ifVersion = template.version;\n\t\t\tawait saveTemplate(type, input, locale);\n\t\t\t// The list hook refreshes its own list; this screen renders the single\n\t\t\t// template, so re-read it too.\n\t\t\tawait refresh();\n\t\t\tonSaved?.();\n\t\t} catch (err) {\n\t\t\t// useTemplates normalizes every failure to FonderieApiError before throwing.\n\t\t\tsetSaveError(err as FonderieApiError);\n\t\t} finally {\n\t\t\tsetIsSaving(false);\n\t\t}\n\t};\n\n\tconst switchTo = (entry: ITemplateEntry) => {\n\t\tif (dirty && !window.confirm(t('templates.editor.discardChanges'))) return;\n\t\tonSelectLocale?.(entry);\n\t};\n\n\tconst handleDelete = async () => {\n\t\tconst message = locale\n\t\t\t? t('templates.editor.confirmDeleteLocale', { type, locale })\n\t\t\t: t('templates.editor.confirmDelete', { type });\n\t\tif (!window.confirm(message)) return;\n\t\tsetIsDeleting(true);\n\t\tsetSaveError(null);\n\t\ttry {\n\t\t\tawait removeTemplate(type, locale);\n\t\t\tonDeleted?.();\n\t\t} catch (err) {\n\t\t\tsetSaveError(err as FonderieApiError);\n\t\t} finally {\n\t\t\tsetIsDeleting(false);\n\t\t}\n\t};\n\n\tif (isLoading) return <p style={styles.status}>{t('templates.editor.loading')}</p>;\n\tif (error)\n\t\treturn (\n\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t{error.explanation}\n\t\t\t</p>\n\t\t);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>{type}</h1>\n\t\t\t{onSelectLocale || onAddLocale ? (\n\t\t\t\t<div style={styles.tabBar}>\n\t\t\t\t\t{onSelectLocale ? (\n\t\t\t\t\t\t// Each tab opens another stored row, so this is navigation, not an\n\t\t\t\t\t\t// ARIA tablist: plain buttons, the open one marked aria-current.\n\t\t\t\t\t\t<nav aria-label={t('templates.editor.locales')} style={styles.tabs}>\n\t\t\t\t\t\t\t{siblings.map((entry) => {\n\t\t\t\t\t\t\t\tconst selected = entry.locale === (locale ?? null);\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\tkey={entry.locale ?? ''}\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\taria-current={selected ? 'true' : undefined}\n\t\t\t\t\t\t\t\t\t\tonClick={() => (selected ? undefined : switchTo(entry))}\n\t\t\t\t\t\t\t\t\t\tstyle={selected ? styles.tabSelected : entry.active ? styles.tab : styles.tabInactive}\n\t\t\t\t\t\t\t\t\t\ttitle={entry.active ? undefined : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{entry.locale ?? t('templates.defaultChip')}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t</nav>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{onAddLocale ? (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tstyle={{ ...styles.rollbackButton, marginLeft: 'auto' }}\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tif (dirty && !window.confirm(t('templates.editor.discardChanges'))) return;\n\t\t\t\t\t\t\t\tonAddLocale(type, { locales: missingTemplateLocales(templates, type) });\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{t('templates.editor.addLocale')}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<p style={styles.meta}>\n\t\t\t\t<strong>{locale ?? t('templates.defaultLocale')}</strong> · v{template?.version ?? 1}\n\t\t\t\t{system ? ` · ${t('templates.editor.builtInNote')}` : ''}\n\t\t\t</p>\n\n\t\t\t<div style={styles.split}>\n\t\t\t\t<div style={styles.column}>\n\t\t\t\t\t<form style={styles.form} onSubmit={handleSubmit}>\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-subject\">\n\t\t\t\t\t\t\t{t('templates.subject')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tid=\"template-subject\"\n\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\tvalue={subject}\n\t\t\t\t\t\t\tonChange={(event) => setSubject(event.target.value)}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-html\">\n\t\t\t\t\t\t\t{t('templates.htmlBody')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\tid=\"template-html\"\n\t\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\t\tvalue={html}\n\t\t\t\t\t\t\tonChange={(event) => setHtml(event.target.value)}\n\t\t\t\t\t\t\trows={8}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-text\">\n\t\t\t\t\t\t\t{t('templates.textBody')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\tid=\"template-text\"\n\t\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\t\tvalue={text}\n\t\t\t\t\t\t\tonChange={(event) => setText(event.target.value)}\n\t\t\t\t\t\t\trows={4}\n\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.checkboxLabel}>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={active}\n\t\t\t\t\t\t\t\tonChange={(event) => setActive(event.target.checked)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t{t('templates.editor.active')}\n\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t{saveError?.reason === 'VERSION_CONFLICT' ? (\n\t\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t\t{t('templates.editor.conflict')}{' '}\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\tsetSaveError(null);\n\t\t\t\t\t\t\t\t\t\tvoid refresh();\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{t('common.reload')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : saveError ? (\n\t\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t\t{saveError.explanation}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : null}\n\n\t\t\t\t\t\t<div style={styles.saveRow}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tdisabled={isSaving || !dirty}\n\t\t\t\t\t\t\t\tstyle={dirty ? styles.button : styles.buttonDisabled}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isSaving ? t('common.saving') : t('common.save')}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{!dirty && !isSaving && (\n\t\t\t\t\t\t\t\t<span style={styles.meta}>{t('templates.editor.noChanges')}</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{!system ? (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tdisabled={isDeleting}\n\t\t\t\t\t\t\t\t\tonClick={() => void handleDelete()}\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t...styles.rollbackButton,\n\t\t\t\t\t\t\t\t\t\theight: 36,\n\t\t\t\t\t\t\t\t\t\tmarginLeft: 'auto',\n\t\t\t\t\t\t\t\t\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\t\t\t\t\t\t\t\t\tborderColor: 'color-mix(in srgb, var(--fonderie-danger,#e00) 40%, transparent)',\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{isDeleting ? t('common.deleting') : t('common.delete')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</form>\n\n\t\t\t\t\t<label style={styles.label} htmlFor=\"template-sample\">\n\t\t\t\t\t\t{t('templates.editor.sampleData')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tid=\"template-sample\"\n\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\tvalue={sampleJson}\n\t\t\t\t\t\tonChange={(event) => setSampleJson(event.target.value)}\n\t\t\t\t\t\trows={6}\n\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t/>\n\t\t\t\t\t{sampleError && (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{sampleError}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t<div style={styles.previewColumn}>\n\t\t\t\t\t<div style={styles.previewHeader}>\n\t\t\t\t\t\t<span style={styles.label}>{t('templates.editor.preview')}</span>\n\t\t\t\t\t\t<span style={styles.meta}>\n\t\t\t\t\t\t\t{isPreviewing ? t('templates.editor.rendering') : t('templates.editor.live')}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{previewError && (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{previewError.explanation}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t\t{preview?.subject && <p style={styles.previewSubject}>{preview.subject}</p>}\n\t\t\t\t\t{preview?.html ? (\n\t\t\t\t\t\t// sandbox=\"\" — no scripts, no same-origin. This is operator-authored\n\t\t\t\t\t\t// HTML and it must never execute in the dashboard's origin, which is\n\t\t\t\t\t\t// where the admin token lives.\n\t\t\t\t\t\t<iframe\n\t\t\t\t\t\t\ttitle={t('templates.editor.previewTitle')}\n\t\t\t\t\t\t\tstyle={styles.previewFrame}\n\t\t\t\t\t\t\tsandbox=\"\"\n\t\t\t\t\t\t\tsrcDoc={preview.html}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : preview ? (\n\t\t\t\t\t\t<pre style={styles.previewText}>{preview.text}</pre>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<p style={styles.meta}>{t('templates.editor.nothingRendered')}</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{revisions.length > 0 && (\n\t\t\t\t<div style={styles.revisions}>\n\t\t\t\t\t<h2 style={styles.subtitle}>{t('templates.editor.history')}</h2>\n\t\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t\t{revisions.map((rev) => (\n\t\t\t\t\t\t\t<li key={rev.version} style={styles.row}>\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\tv{rev.version} — {rev.actor ?? t('templates.editor.unknownActor')} —{' '}\n\t\t\t\t\t\t\t\t\t{formatAdminDate(rev.createdAt, uiLocale)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => rollback(rev.version)}\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{t('templates.editor.rollBack')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nconst tab: CSSProperties = {\n\theight: 32,\n\tpadding: '0 10px',\n\tbackground: 'none',\n\tborder: 'none',\n\tborderBottom: '2px solid transparent',\n\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\tfontSize: 13,\n\tfontFamily:\n\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\tcursor: 'pointer',\n};\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '8px 40px 64px', maxWidth: 1400, boxSizing: 'border-box' },\n\t// One tab per locale of this email, underlined like a document's tabs.\n\ttabBar: {\n\t\tdisplay: 'flex',\n\t\talignItems: 'flex-end',\n\t\tgap: 12,\n\t\tborderBottom: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tmargin: '4px 0 12px',\n\t\tpaddingBottom: 6,\n\t},\n\ttabs: { display: 'flex', flexWrap: 'wrap', gap: 4, marginBottom: -7 },\n\ttab,\n\ttabSelected: {\n\t\t...tab,\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontWeight: 600,\n\t\tborderBottomColor: 'var(--fonderie-text,#171717)',\n\t\tcursor: 'default',\n\t},\n\ttabInactive: { ...tab, opacity: 0.55, textDecoration: 'line-through' },\n\t// Two columns, always: the editor on the left, the rendered result on the\n\t// right. Stacking the preview under a long form hid it below the fold.\n\tsplit: {\n\t\tdisplay: 'grid',\n\t\tgridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)',\n\t\tgap: 24,\n\t\talignItems: 'start',\n\t},\n\tcolumn: { minWidth: 0, display: 'flex', flexDirection: 'column' },\n\t// Pinned while the form scrolls, so the preview stays in view as you edit.\n\tpreviewColumn: {\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tposition: 'sticky',\n\t\ttop: 16,\n\t},\n\tsaveRow: { display: 'flex', alignItems: 'center', gap: 12, marginTop: 16 },\n\tpreviewHeader: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tmarginTop: 12,\n\t},\n\tpreviewSubject: { fontSize: 14, fontWeight: 600, margin: '8px 0' },\n\tpreviewFrame: {\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t\twidth: '100%',\n\t\theight: 520,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t},\n\tpreviewText: {\n\t\twhiteSpace: 'pre-wrap',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tpadding: 12,\n\t\tfontSize: 13,\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: '8px 0 4px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\tmeta: { fontSize: 13, color: 'var(--fonderie-text-muted,#5c5c5c)', marginBottom: 20 },\n\tstatus: { padding: 24, textAlign: 'center', color: 'var(--fonderie-text-muted,#5c5c5c)' },\n\terror: {\n\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '8px 0',\n\t\tfontSize: 13.5,\n\t},\n\tform: {\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tgap: 4,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\tpadding: '6px 20px 20px',\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t},\n\tlabel: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },\n\tinput: {\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 13.5,\n\t\tfontFamily: 'inherit',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t},\n\ttextarea: {\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: 10,\n\t\tfontSize: 13,\n\t\tlineHeight: 1.55,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\tcheckboxLabel: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 12, fontSize: 14 },\n\tbutton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t},\n\tbuttonDisabled: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\talignSelf: 'flex-start',\n\t\topacity: 0.45,\n\t\tcursor: 'not-allowed',\n\t},\n\trevisions: { marginTop: 32 },\n\tsubtitle: {\n\t\tfontSize: 15,\n\t\tfontWeight: 600,\n\t\tmargin: '0 0 10px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t},\n\tlist: {\n\t\tlistStyle: 'none',\n\t\tpadding: 0,\n\t\tmargin: 0,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\toverflow: 'hidden',\n\t},\n\trow: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tgap: 12,\n\t\tpadding: '9px 16px',\n\t\tborderBottom: '1px solid var(--fonderie-border-light,#f5f5f5)',\n\t\tfontSize: 13,\n\t},\n\trollbackButton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 28,\n\t\tboxSizing: 'border-box',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 12.5,\n\t\tfontWeight: 500,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t},\n};\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\ttype ITemplateEntry,\n\tcreateAdminT,\n\tgroupTemplatesByType,\n} from '@fonderie/client';\nimport { useTemplates } from '@fonderie/react-courier-admin';\nimport type { CSSProperties } from 'react';\n\nexport interface ITemplateListScreenProps {\n\tclient: CourierAdminClient;\n\t/**\n\t * Receives the whole row — type AND locale, and whether it is built-in. The\n\t * list shows one row per email: clicking it opens the default locale, a\n\t * locale chip opens that locale.\n\t */\n\tonSelectTemplate?: (template: ITemplateEntry) => void;\n\t/** Shows a \"New template\" button. Receives the locales in use, to suggest. */\n\tonCreateTemplate?: (context: { locales: string[] }) => void;\n\t/** The console's language. Default English. */\n\tlocale?: AdminLocale | undefined;\n}\n\nexport function TemplateListScreen({\n\tclient,\n\tonSelectTemplate,\n\tonCreateTemplate,\n\tlocale,\n}: ITemplateListScreenProps) {\n\tconst t = createAdminT(locale);\n\tconst { templates, isLoading, error } = useTemplates(client);\n\tconst groups = groupTemplatesByType(templates);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<div\n\t\t\t\tstyle={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16 }}\n\t\t\t>\n\t\t\t\t<h1 style={styles.title}>{t('templates.list.title')}</h1>\n\t\t\t\t{onCreateTemplate ? (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\tonCreateTemplate({\n\t\t\t\t\t\t\t\tlocales: [\n\t\t\t\t\t\t\t\t\t...new Set(templates.map((row) => row.locale).filter((l): l is string => !!l)),\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstyle={styles.newButton}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t('templates.list.newTemplate')}\n\t\t\t\t\t</button>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t\t<p style={styles.hint}>{t('templates.list.hint')}</p>\n\t\t\t{isLoading ? (\n\t\t\t\t<p style={styles.status}>{t('templates.list.loading')}</p>\n\t\t\t) : error ? (\n\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t{error.explanation}\n\t\t\t\t</p>\n\t\t\t) : (\n\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t{groups.map((group) => (\n\t\t\t\t\t\t<li key={group.type} style={styles.row}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => onSelectTemplate?.(group.primary)}\n\t\t\t\t\t\t\t\tstyle={styles.rowButton}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span style={styles.type}>{group.type}</span>\n\t\t\t\t\t\t\t\t{group.system ? (\n\t\t\t\t\t\t\t\t\t<span style={styles.builtIn}>{t('templates.builtInBadge')}</span>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t<span style={group.primary.active ? styles.active : styles.inactive}>\n\t\t\t\t\t\t\t\t\t<span style={styles.dot} />\n\t\t\t\t\t\t\t\t\t{group.primary.active ? t('common.status.active') : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{/* Siblings of the row button, not inside it: a button cannot hold buttons. */}\n\t\t\t\t\t\t\t<span style={styles.chips}>\n\t\t\t\t\t\t\t\t{group.entries.map((entry) => {\n\t\t\t\t\t\t\t\t\tconst name = entry.locale ?? t('templates.defaultChip');\n\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\tkey={entry.locale ?? ''}\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => onSelectTemplate?.(entry)}\n\t\t\t\t\t\t\t\t\t\t\tstyle={entry.active ? styles.chip : styles.chipInactive}\n\t\t\t\t\t\t\t\t\t\t\taria-label={t('templates.list.openLocale', { type: group.type, locale: name })}\n\t\t\t\t\t\t\t\t\t\t\ttitle={entry.active ? undefined : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{name}\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t))}\n\t\t\t\t</ul>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// Matches @fonderie/react-admin-screens' shell look with local values: this\n// package sits below the admin screens and cannot import their blocks.\nconst pill: CSSProperties = {\n\tdisplay: 'inline-flex',\n\talignItems: 'center',\n\tgap: 6,\n\tborderRadius: 999,\n\tpadding: '2px 9px 2px 8px',\n\tfontSize: 12,\n\tfontWeight: 600,\n\tlineHeight: 1.5,\n\tminWidth: 64,\n};\n\n// One per locale the email exists in. Monospace: they are language tags.\nconst chip: CSSProperties = {\n\theight: 24,\n\tboxSizing: 'border-box',\n\tpadding: '0 8px',\n\tborderRadius: 6,\n\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\tbackground: 'var(--fonderie-surface,#fff)',\n\tcolor: 'var(--fonderie-text,#171717)',\n\tfontSize: 12,\n\tfontFamily:\n\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\tcursor: 'pointer',\n};\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '32px 40px 64px', maxWidth: 1160, boxSizing: 'border-box' },\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: 0,\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\thint: { fontSize: 13.5, color: 'var(--fonderie-text-muted,#5c5c5c)', margin: '4px 0 24px' },\n\tstatus: { padding: '24px 0', color: 'var(--fonderie-text-muted,#5c5c5c)', fontSize: 13.5 },\n\terror: { color: 'var(--fonderie-danger,#e00)', marginBottom: 12, fontSize: 14 },\n\tlist: {\n\t\tlistStyle: 'none',\n\t\tpadding: 0,\n\t\tmargin: 0,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\toverflow: 'hidden',\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t},\n\trow: {\n\t\tdisplay: 'flex',\n\t\talignItems: 'center',\n\t\tgap: 8,\n\t\tpaddingRight: 16,\n\t\tborderBottom: '1px solid var(--fonderie-border-light,#f5f5f5)',\n\t},\n\trowButton: {\n\t\tflex: 1,\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tgap: 16,\n\t\tpadding: '13px 16px',\n\t\tbackground: 'none',\n\t\tborder: 'none',\n\t\tcursor: 'pointer',\n\t\ttextAlign: 'left',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily: 'inherit',\n\t},\n\ttype: {\n\t\tfontSize: 13,\n\t\tfontWeight: 600,\n\t\tflex: 1,\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\tchips: { display: 'flex', flexWrap: 'wrap', justifyContent: 'flex-end', gap: 6 },\n\tchip,\n\tchipInactive: { ...chip, opacity: 0.5, textDecoration: 'line-through' },\n\tdot: { width: 6, height: 6, borderRadius: 999, background: 'currentColor' },\n\tbuiltIn: {\n\t\tfontSize: 11,\n\t\tfontWeight: 500,\n\t\tpadding: '1px 7px',\n\t\tborderRadius: 999,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t},\n\tnewButton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 32,\n\t\tboxSizing: 'border-box',\n\t\tborderRadius: 6,\n\t\tpadding: '0 12px',\n\t\tfontSize: 13,\n\t\tfontWeight: 500,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t},\n\tactive: {\n\t\t...pill,\n\t\tcolor: 'var(--fonderie-accent-strong,#009767)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-accent-strong,#009767) 13%, transparent)',\n\t},\n\tinactive: {\n\t\t...pill,\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-text-muted,#5c5c5c) 10%, transparent)',\n\t},\n};\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\tFonderieApiError,\n\ttype ISetTemplateInput,\n\tcreateAdminT,\n} from '@fonderie/client';\nimport { useTemplatePreview } from '@fonderie/react-courier-admin';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useEffect, useRef, useState } from 'react';\n\nexport interface ITemplateCreateScreenProps {\n\tclient: CourierAdminClient;\n\t/**\n\t * Given ⇒ add a LOCALE of this existing template: the type is fixed and the\n\t * form starts from its default-locale copy. Omitted ⇒ a brand-new template\n\t * type (one the app sends that has no stored copy yet).\n\t */\n\ttype?: string;\n\t/** Locales already in use, offered as suggestions. */\n\tlocales?: string[];\n\t/** Called with what was created, so the caller can open it in the editor. */\n\tonCreated?: (created: { type: string; locale: string | null }) => void;\n\t/** The console's language. Default English. */\n\tlocale?: AdminLocale | undefined;\n}\n\nconst TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;\nconst LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;\n// Variables the layout injects — never sample values, as in the editor.\nconst IMPLICIT = new Set(['subject', 'preheader', 'brandName']);\nconst PREVIEW_DEBOUNCE_MS = 500;\n\n// Creating is its own screen: the editor loads an existing row, and a new\n// template or locale has none yet. Once saved, the editor takes over.\nexport function TemplateCreateScreen({\n\tclient,\n\ttype,\n\tlocales = [],\n\tonCreated,\n\tlocale,\n}: ITemplateCreateScreenProps) {\n\tconst tr = createAdminT(locale);\n\tconst addingLocale = type !== undefined;\n\tconst [newType, setNewType] = useState('');\n\tconst [targetLocale, setTargetLocale] = useState('');\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [error, setError] = useState<string | null>(null);\n\tconst [busy, setBusy] = useState(false);\n\t// The default copy this locale started from, to flag fields not yet translated.\n\tconst [source, setSource] = useState<{ subject: string; html: string; text: string } | null>(\n\t\tnull,\n\t);\n\tconst { preview, isPreviewing, error: previewError, renderPreview } = useTemplatePreview(client);\n\t// Each variable renders as its own name, so the preview shows where it lands.\n\t// Learned from the server's answer, not re-parsed here.\n\tconst variables = useRef<string[]>([]);\n\n\t// A new locale starts from the default copy, so translating is editing.\n\tuseEffect(() => {\n\t\tif (!type) return;\n\t\tclient\n\t\t\t.getTemplate(type, null)\n\t\t\t.then(({ result }) => {\n\t\t\t\tsetSubject(result.subject ?? '');\n\t\t\t\tsetHtml(result.html ?? '');\n\t\t\t\tsetText(result.text);\n\t\t\t\tsetSource({ subject: result.subject ?? '', html: result.html ?? '', text: result.text });\n\t\t\t})\n\t\t\t.catch(() => undefined);\n\t}, [client, type]);\n\n\t// Live preview, as in the editor: translating is where it matters most — a\n\t// half-translated email looks finished in a textarea and wrong in the render.\n\tconst previewType = addingLocale ? (type as string) : newType.trim();\n\tconst previewLocale = LOCALE_RE.test(targetLocale.trim()) ? targetLocale.trim() : null;\n\tuseEffect(() => {\n\t\tif (!TYPE_RE.test(previewType) || !text.trim()) return;\n\t\tconst timer = setTimeout(async () => {\n\t\t\tconst input = { text, ...(subject ? { subject } : {}), ...(html ? { html } : {}) };\n\t\t\tconst render = () =>\n\t\t\t\trenderPreview(\n\t\t\t\t\tpreviewType,\n\t\t\t\t\t{ ...input, data: Object.fromEntries(variables.current.map((v) => [v, v])) },\n\t\t\t\t\tpreviewLocale,\n\t\t\t\t);\n\t\t\ttry {\n\t\t\t\tconst result = await render();\n\t\t\t\tconst used = result.variables.filter((v) => !IMPLICIT.has(v));\n\t\t\t\t// A variable not seen before rendered empty: render once more with it.\n\t\t\t\tif (used.some((v) => !variables.current.includes(v))) {\n\t\t\t\t\tvariables.current = used;\n\t\t\t\t\tawait render();\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Shown through previewError.\n\t\t\t}\n\t\t}, PREVIEW_DEBOUNCE_MS);\n\t\treturn () => clearTimeout(timer);\n\t}, [renderPreview, previewType, previewLocale, subject, html, text]);\n\n\tconst untranslated = source\n\t\t? [\n\t\t\t\tsubject && subject === source.subject ? tr('templates.subject') : null,\n\t\t\t\thtml && html === source.html ? tr('templates.htmlBody') : null,\n\t\t\t\ttext === source.text ? tr('templates.textBody') : null,\n\t\t\t].filter((f): f is string => f !== null)\n\t\t: [];\n\n\tconst submit = async (e: FormEvent) => {\n\t\te.preventDefault();\n\t\tsetError(null);\n\t\tconst t = addingLocale ? (type as string) : newType.trim();\n\t\tconst l = targetLocale.trim() || null;\n\t\tif (!TYPE_RE.test(t)) return setError(tr('templates.create.errorType'));\n\t\tif (addingLocale && !l) return setError(tr('templates.create.errorLocaleRequired'));\n\t\tif (l && !LOCALE_RE.test(l)) return setError(tr('templates.create.errorLocale'));\n\t\tif (!text.trim()) return setError(tr('templates.create.errorText'));\n\t\tsetBusy(true);\n\t\ttry {\n\t\t\t// Refuse to overwrite: creating an existing row would replace it.\n\t\t\tconst exists = await client.getTemplate(t, l).then(\n\t\t\t\t() => true,\n\t\t\t\t(err: unknown) => !(err instanceof FonderieApiError && err.status === 404),\n\t\t\t);\n\t\t\tif (exists) {\n\t\t\t\tsetError(\n\t\t\t\t\tl\n\t\t\t\t\t\t? tr('templates.create.existsLocale', { type: t, locale: l })\n\t\t\t\t\t\t: tr('templates.create.exists', { type: t }),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst input: ISetTemplateInput = { text, active: true };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tawait client.setTemplate(t, input, l);\n\t\t\tonCreated?.({ type: t, locale: l });\n\t\t} catch (err) {\n\t\t\tsetError(err instanceof FonderieApiError ? err.explanation : String(err));\n\t\t} finally {\n\t\t\tsetBusy(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>\n\t\t\t\t{addingLocale\n\t\t\t\t\t? tr('templates.create.titleLocale', { type: type as string })\n\t\t\t\t\t: tr('templates.create.titleNew')}\n\t\t\t</h1>\n\t\t\t<p style={styles.meta}>\n\t\t\t\t{addingLocale ? tr('templates.create.hintLocale') : tr('templates.create.hintNew')}\n\t\t\t</p>\n\t\t\t<div style={styles.split}>\n\t\t\t\t<form style={styles.form} onSubmit={(e) => void submit(e)}>\n\t\t\t\t\t{!addingLocale ? (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-type\">\n\t\t\t\t\t\t\t\t{tr('templates.create.type')}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\tid=\"new-template-type\"\n\t\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\t\tvalue={newType}\n\t\t\t\t\t\t\t\tplaceholder=\"weekly-digest\"\n\t\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\t\tonChange={(e) => setNewType(e.target.value)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</>\n\t\t\t\t\t) : null}\n\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-locale\">\n\t\t\t\t\t\t{addingLocale ? tr('templates.create.locale') : tr('templates.create.localeOptional')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<input\n\t\t\t\t\t\tid=\"new-template-locale\"\n\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\tvalue={targetLocale}\n\t\t\t\t\t\tplaceholder=\"fr\"\n\t\t\t\t\t\tlist=\"new-template-locales\"\n\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\tonChange={(e) => setTargetLocale(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<datalist id=\"new-template-locales\">\n\t\t\t\t\t\t{[...new Set(locales)].map((l) => (\n\t\t\t\t\t\t\t<option key={l} value={l} />\n\t\t\t\t\t\t))}\n\t\t\t\t\t</datalist>\n\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-subject\">\n\t\t\t\t\t\t{tr('templates.subject')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<input\n\t\t\t\t\t\tid=\"new-template-subject\"\n\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\tvalue={subject}\n\t\t\t\t\t\tonChange={(e) => setSubject(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-html\">\n\t\t\t\t\t\t{tr('templates.htmlBody')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tid=\"new-template-html\"\n\t\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 160 }}\n\t\t\t\t\t\tvalue={html}\n\t\t\t\t\t\tonChange={(e) => setHtml(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-text\">\n\t\t\t\t\t\t{tr('templates.textBody')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tid=\"new-template-text\"\n\t\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 100 }}\n\t\t\t\t\t\tvalue={text}\n\t\t\t\t\t\tonChange={(e) => setText(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t{untranslated.length > 0 ? (\n\t\t\t\t\t\t<p style={styles.notice}>\n\t\t\t\t\t\t\t{tr('templates.create.untranslated', { fields: untranslated.join(', ') })}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{error ? (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{error}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t) : null}\n\t\t\t\t\t<button type=\"submit\" disabled={busy} style={styles.button}>\n\t\t\t\t\t\t{busy\n\t\t\t\t\t\t\t? tr('templates.create.creating')\n\t\t\t\t\t\t\t: addingLocale\n\t\t\t\t\t\t\t\t? tr('templates.create.submitLocale')\n\t\t\t\t\t\t\t\t: tr('templates.create.submitNew')}\n\t\t\t\t\t</button>\n\t\t\t\t</form>\n\n\t\t\t\t<div style={styles.previewColumn}>\n\t\t\t\t\t<div style={styles.previewHeader}>\n\t\t\t\t\t\t<span style={styles.label}>{tr('templates.editor.preview')}</span>\n\t\t\t\t\t\t<span style={styles.meta}>\n\t\t\t\t\t\t\t{isPreviewing ? tr('templates.editor.rendering') : tr('templates.editor.live')}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{previewError ? (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{previewError.explanation}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{preview?.subject ? <p style={styles.previewSubject}>{preview.subject}</p> : null}\n\t\t\t\t\t{preview?.html ? (\n\t\t\t\t\t\t// sandbox=\"\" — no scripts, no same-origin: operator-authored HTML must\n\t\t\t\t\t\t// never execute in the console's origin.\n\t\t\t\t\t\t<iframe\n\t\t\t\t\t\t\ttitle={tr('templates.editor.previewTitle')}\n\t\t\t\t\t\t\tstyle={styles.previewFrame}\n\t\t\t\t\t\t\tsandbox=\"\"\n\t\t\t\t\t\t\tsrcDoc={preview.html}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : preview ? (\n\t\t\t\t\t\t<pre style={styles.previewText}>{preview.text}</pre>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<p style={styles.meta}>{tr('templates.editor.nothingRendered')}</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nconst MONO =\n\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)';\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '8px 40px 64px', maxWidth: 1400, boxSizing: 'border-box' },\n\t// The editor's layout: the form on the left, the render pinned on the right.\n\tsplit: {\n\t\tdisplay: 'grid',\n\t\tgridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)',\n\t\tgap: 24,\n\t\talignItems: 'start',\n\t},\n\tpreviewColumn: {\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tposition: 'sticky',\n\t\ttop: 16,\n\t},\n\tpreviewHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' },\n\tpreviewSubject: { fontSize: 14, fontWeight: 600, margin: '8px 0' },\n\tpreviewFrame: {\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t\twidth: '100%',\n\t\theight: 520,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t},\n\tpreviewText: {\n\t\twhiteSpace: 'pre-wrap',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tpadding: 12,\n\t\tfontSize: 13,\n\t\tfontFamily: MONO,\n\t},\n\t// The console's warning token (the shell's amber), readable in both themes.\n\tnotice: {\n\t\tfontSize: 13,\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-warning,#f5a623) 16%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '12px 0 0',\n\t},\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: '8px 0 4px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\tmeta: {\n\t\tfontSize: 13,\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t\tmarginBottom: 20,\n\t\tlineHeight: 1.5,\n\t},\n\tform: {\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tgap: 4,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\tpadding: '6px 20px 20px',\n\t},\n\tlabel: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },\n\tinput: {\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 13.5,\n\t\tfontFamily: 'inherit',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t},\n\ttextarea: {\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: 10,\n\t\tfontSize: 13,\n\t\tlineHeight: 1.55,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily: MONO,\n\t},\n\terror: {\n\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '8px 0',\n\t\tfontSize: 13.5,\n\t},\n\tbutton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t\tmarginTop: 16,\n\t},\n};\n"],"mappings":"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","names":["createAdminT","groupTemplatesByType","useTemplates","jsx","jsxs","styles","createAdminT","useTemplatePreview","useEffect","useRef","useState","jsx","jsxs","IMPLICIT","PREVIEW_DEBOUNCE_MS","styles"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fonderie/react-courier-admin-screens",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Pre-built React screens for the Fonderie courier admin API — template list, template editor with revision history and rollback.",
5
5
  "keywords": [
6
6
  "fonderiejs",