@cosmicdrift/kumiko-document-designer 0.363.0 → 0.365.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.
Files changed (90) hide show
  1. package/dist/constants.d.ts +3 -0
  2. package/dist/constants.js +4 -0
  3. package/dist/i18n.js +208 -50
  4. package/dist/web/cells.js +4 -3
  5. package/dist/web/editor-header.d.ts +37 -0
  6. package/dist/web/editor-header.js +43 -0
  7. package/dist/web/editor-locale.d.ts +1 -0
  8. package/dist/web/editor-locale.js +15 -0
  9. package/dist/web/editor-shell.d.ts +40 -0
  10. package/dist/web/editor-shell.js +36 -0
  11. package/dist/web/i18n.js +35 -59
  12. package/dist/web/layout-editor-body.d.ts +2 -2
  13. package/dist/web/layout-editor-body.js +101 -52
  14. package/dist/web/layout-editor.js +14 -7
  15. package/dist/web/layout-html.d.ts +2 -0
  16. package/dist/web/layout-html.js +13 -1
  17. package/dist/web/layout-meta.d.ts +29 -0
  18. package/dist/web/layout-meta.js +32 -0
  19. package/dist/web/layout-rails.d.ts +15 -5
  20. package/dist/web/layout-rails.js +61 -68
  21. package/dist/web/layout-types.d.ts +4 -0
  22. package/dist/web/layout-users.d.ts +1 -0
  23. package/dist/web/layout-users.js +4 -1
  24. package/dist/web/mail-dialogs.d.ts +1 -6
  25. package/dist/web/mail-dialogs.js +2 -2
  26. package/dist/web/mail-draft.d.ts +8 -6
  27. package/dist/web/mail-draft.js +45 -11
  28. package/dist/web/mail-edit-center.d.ts +3 -1
  29. package/dist/web/mail-edit-center.js +5 -4
  30. package/dist/web/mail-editor-body.d.ts +0 -1
  31. package/dist/web/mail-editor-body.js +60 -45
  32. package/dist/web/mail-editor.js +9 -14
  33. package/dist/web/mail-footer.d.ts +3 -14
  34. package/dist/web/mail-footer.js +12 -13
  35. package/dist/web/mail-left-rail.d.ts +9 -4
  36. package/dist/web/mail-left-rail.js +18 -15
  37. package/dist/web/mail-missing-locale.d.ts +10 -0
  38. package/dist/web/mail-missing-locale.js +45 -0
  39. package/dist/web/mail-origin-bar.d.ts +3 -5
  40. package/dist/web/mail-origin-bar.js +26 -50
  41. package/dist/web/mail-preview-side.d.ts +12 -5
  42. package/dist/web/mail-preview-side.js +14 -5
  43. package/dist/web/mail-preview.d.ts +9 -5
  44. package/dist/web/mail-preview.js +24 -13
  45. package/dist/web/mail-right-rail.d.ts +8 -0
  46. package/dist/web/mail-right-rail.js +65 -14
  47. package/dist/web/mail-types.d.ts +10 -1
  48. package/dist/web/mail-upload-modal.js +12 -2
  49. package/dist/web/mail-versions.js +3 -3
  50. package/dist/web/origin-bar.d.ts +11 -2
  51. package/dist/web/origin-bar.js +46 -24
  52. package/dist/web/overrides-drawer.d.ts +3 -2
  53. package/dist/web/overrides-drawer.js +87 -11
  54. package/dist/web/overview-row.d.ts +2 -0
  55. package/dist/web/overview-row.js +2 -1
  56. package/dist/web/page-canvas.d.ts +2 -1
  57. package/dist/web/page-canvas.js +23 -13
  58. package/dist/web/pdf-footer.d.ts +13 -0
  59. package/dist/web/pdf-footer.js +34 -0
  60. package/dist/web/pdf-left-rail.d.ts +9 -0
  61. package/dist/web/pdf-left-rail.js +16 -0
  62. package/dist/web/pdf-meta.d.ts +3 -5
  63. package/dist/web/pdf-meta.js +10 -16
  64. package/dist/web/pdf-preview.js +21 -8
  65. package/dist/web/pdf-template-editor.js +91 -76
  66. package/dist/web/rail.d.ts +13 -1
  67. package/dist/web/rail.js +15 -7
  68. package/dist/web/relative-time.d.ts +1 -0
  69. package/dist/web/relative-time.js +18 -0
  70. package/dist/web/settings-panel.d.ts +11 -5
  71. package/dist/web/settings-panel.js +24 -13
  72. package/dist/web/status-label.d.ts +7 -0
  73. package/dist/web/status-label.js +8 -0
  74. package/dist/web/types.d.ts +23 -0
  75. package/dist/web/use-debounced-value.d.ts +1 -0
  76. package/dist/web/use-debounced-value.js +11 -0
  77. package/dist/web/use-insert-targets.d.ts +2 -2
  78. package/dist/web/use-insert-targets.js +4 -1
  79. package/dist/web/use-mail-persistence.d.ts +2 -2
  80. package/dist/web/use-mail-persistence.js +4 -4
  81. package/dist/web/use-own-address.d.ts +5 -0
  82. package/dist/web/use-own-address.js +14 -0
  83. package/dist/web/used-by-label.d.ts +1 -0
  84. package/dist/web/used-by-label.js +11 -0
  85. package/package.json +8 -8
  86. package/src/changes.json +48 -0
  87. package/dist/web/mail-header.d.ts +0 -23
  88. package/dist/web/mail-header.js +0 -16
  89. package/dist/web/placeholder-panel.d.ts +0 -21
  90. package/dist/web/placeholder-panel.js +0 -35
@@ -8,6 +8,9 @@ export declare const TEMPLATES_NAV_ID: "templates";
8
8
  export declare const PLATFORM_TEMPLATES_NAV_ID: "platform-templates";
9
9
  export declare const BRANDING_SCREEN_QN: "managed-pages:screen:branding-settings";
10
10
  export declare const BRANDING_QUERY_QN: "managed-pages:query:branding";
11
+ export declare const USER_ME_QUERY_QN: "user:query:user:me";
12
+ export declare const PROFILE_FEATURE_NAME: "user-profile";
13
+ export declare const PROFILE_SCREEN_ID: "profile";
11
14
  export declare const DEFAULT_LAYOUT_SLUG: "default";
12
15
  export type TemplateEditorKind = "mail" | "document-pdf" | "mail-layout" | "pdf-layout";
13
16
  export declare const TEMPLATE_EDITOR_SCREENS: Partial<Record<TemplateEditorKind, string>>;
package/dist/constants.js CHANGED
@@ -10,6 +10,10 @@ export const TEMPLATES_NAV_ID = "templates";
10
10
  export const PLATFORM_TEMPLATES_NAV_ID = "platform-templates";
11
11
  export const BRANDING_SCREEN_QN = "managed-pages:screen:branding-settings";
12
12
  export const BRANDING_QUERY_QN = "managed-pages:query:branding";
13
+ // Pinned like the branding names: the user features' constants are not exported to the web barrel.
14
+ export const USER_ME_QUERY_QN = "user:query:user:me";
15
+ export const PROFILE_FEATURE_NAME = "user-profile";
16
+ export const PROFILE_SCREEN_ID = "profile";
13
17
  export const DEFAULT_LAYOUT_SLUG = "default";
14
18
  // Short screen id per template kind. A kind without an entry gets no "Edit" row action.
15
19
  // The mail editor adds `mail` here and declares its screen in this feature, because the
package/dist/i18n.js CHANGED
@@ -29,6 +29,8 @@ export const DOCUMENT_DESIGNER_I18N = {
29
29
  "documentDesigner.kind.layout": { en: "Layout", de: "Layout" },
30
30
  "documentDesigner.source.system": { en: "System", de: "System" },
31
31
  "documentDesigner.source.tenant": { en: "Tenant", de: "Mandant" },
32
+ "documentDesigner.origin.system": { en: "System template", de: "System-Vorlage" },
33
+ "documentDesigner.origin.override": { en: "Customised", de: "Mandanten-Anpassung" },
32
34
  "documentDesigner.status.active": { en: "Active", de: "Aktiv" },
33
35
  "documentDesigner.status.draft": { en: "Draft", de: "Entwurf" },
34
36
  "documentDesigner.status.archived": { en: "Archived", de: "Archiviert" },
@@ -77,19 +79,55 @@ export const DOCUMENT_DESIGNER_I18N = {
77
79
  "documentDesigner.appearance.title": { en: "Appearance", de: "Darstellung" },
78
80
  "documentDesigner.appearance.width": { en: "Width", de: "Breite" },
79
81
  "documentDesigner.appearance.font": { en: "Font", de: "Schrift" },
80
- "documentDesigner.appearance.systemFont": {
81
- en: "System font (Helvetica, Arial)",
82
- de: "Systemschrift (Helvetica, Arial)",
83
- },
84
82
  "documentDesigner.appearance.background": { en: "Outer background", de: "Hintergrund außen" },
85
- "documentDesigner.appearance.backgroundValue": { en: "#f5f5f5", de: "#f5f5f5" },
86
- "documentDesigner.appearance.pageFormat": { en: "Page format", de: "Seitenformat" },
87
- "documentDesigner.appearance.hint": {
88
- en: "The appearance is fixed. Custom fonts and colours will follow.",
89
- de: "Darstellung ist fest vorgegeben. Eigene Schriften und Farben kommen später.",
83
+ "documentDesigner.appearance.font.sans": {
84
+ en: "System font (Arial, Helvetica)",
85
+ de: "Systemschrift (Arial, Helvetica)",
86
+ },
87
+ "documentDesigner.appearance.font.serif": {
88
+ en: "Serif font (Georgia)",
89
+ de: "Serifenschrift (Georgia)",
90
+ },
91
+ "documentDesigner.appearance.fontHint": {
92
+ en: "Custom fonts in mails come later. PDF uses the bundled framework font.",
93
+ de: "Eigene Schriften in Mails kommen sp\u00e4ter. PDF nutzt die geb\u00fcndelte Schrift des Frameworks.",
94
+ },
95
+ "documentDesigner.appearance.backgroundInvalid": {
96
+ en: "Enter a colour like #f3f2ee.",
97
+ de: "Eine Farbe wie #f3f2ee angeben.",
98
+ },
99
+ "documentDesigner.appearance.accentLine": {
100
+ en: "Accent line at the top",
101
+ de: "Akzentlinie oben",
102
+ },
103
+ "documentDesigner.appearance.accentLine.branding": {
104
+ en: "Accent colour from branding",
105
+ de: "Akzentfarbe aus Branding",
106
+ },
107
+ "documentDesigner.appearance.accentLine.none": { en: "None", de: "Keine" },
108
+ "documentDesigner.appearance.buttonStyle": { en: "Button style", de: "Button-Stil" },
109
+ "documentDesigner.appearance.buttonStyle.filled-rounded": {
110
+ en: "Filled, rounded",
111
+ de: "Gef\u00fcllt, abgerundet",
112
+ },
113
+ "documentDesigner.appearance.buttonStyle.filled-square": {
114
+ en: "Filled, square",
115
+ de: "Gef\u00fcllt, eckig",
116
+ },
117
+ "documentDesigner.appearance.buttonStyle.outline": { en: "Outline", de: "Umriss" },
118
+ "documentDesigner.appearance.fromMjml": {
119
+ en: "The appearance comes from this layout's MJML source.",
120
+ de: "Die Darstellung kommt aus der MJML-Quelle dieses Layouts.",
90
121
  },
91
122
  "documentDesigner.mjml.title": { en: "MJML source", de: "MJML-Quelle" },
92
- "documentDesigner.editor.crumb": { en: "Templates", de: "Vorlagen" },
123
+ "documentDesigner.mjml.hint": {
124
+ en: "Platform admins can edit the MJML source directly. Tenants only see the draft mode.",
125
+ de: "Plattform-Admins k\u00f6nnen die MJML-Quelle direkt bearbeiten. Mandanten sehen nur den Entwurfs-Modus.",
126
+ },
127
+ "documentDesigner.renderer.title": { en: "Renderer", de: "Renderer" },
128
+ "documentDesigner.renderer.mjml": { en: "MJML", de: "MJML" },
129
+ "documentDesigner.renderer.chromium": { en: "Chromium (PDF)", de: "Chromium (PDF)" },
130
+ "documentDesigner.renderer.active": { en: "active", de: "aktiv" },
93
131
  "documentDesigner.editor.title.mail": { en: "Mail layout", de: "Mail-Layout" },
94
132
  "documentDesigner.editor.title.pdf": { en: "Letterhead", de: "Briefpapier" },
95
133
  "documentDesigner.editor.kindSwitch": { en: "Layout kind", de: "Layout-Art" },
@@ -136,8 +174,8 @@ export const DOCUMENT_DESIGNER_I18N = {
136
174
  de: "System-Layout v{version} · {active} von {total} Mandanten haben eine eigene Fassung",
137
175
  },
138
176
  "documentDesigner.editor.diff.open": {
139
- en: "Difference to the system layout",
140
- de: "Unterschied zum System-Layout",
177
+ en: "Show difference",
178
+ de: "Unterschied anzeigen",
141
179
  },
142
180
  "documentDesigner.editor.diff.title": {
143
181
  en: "Difference to the system layout",
@@ -148,8 +186,8 @@ export const DOCUMENT_DESIGNER_I18N = {
148
186
  de: "Ihr Layout entspricht dem System-Layout.",
149
187
  },
150
188
  "documentDesigner.editor.reset.open": {
151
- en: "Reset to system layout",
152
- de: "Auf System-Layout zurücksetzen",
189
+ en: "Reset",
190
+ de: "Zurücksetzen",
153
191
  },
154
192
  "documentDesigner.editor.reset.title": {
155
193
  en: "Reset to system layout?",
@@ -161,6 +199,10 @@ export const DOCUMENT_DESIGNER_I18N = {
161
199
  },
162
200
  "documentDesigner.editor.reset.confirm": { en: "Reset", de: "Zurücksetzen" },
163
201
  "documentDesigner.editor.region.header": { en: "Header", de: "Kopf" },
202
+ "documentDesigner.editor.logoCaption": {
203
+ en: "Logo · from branding, max. 180 px wide",
204
+ de: "Logo · aus Branding, max. 180 px breit",
205
+ },
164
206
  "documentDesigner.editor.region.content": { en: "Template content", de: "Inhalt der Vorlage" },
165
207
  "documentDesigner.editor.region.signature": { en: "Signature area", de: "Signatur-Bereich" },
166
208
  "documentDesigner.editor.region.footer": { en: "Footer", de: "Fuß" },
@@ -223,10 +265,6 @@ export const DOCUMENT_DESIGNER_I18N = {
223
265
  en: "Preview",
224
266
  de: "Vorschau",
225
267
  },
226
- "documentDesigner.mail.header.testMail": {
227
- en: "Send test mail to me",
228
- de: "Test-Mail an mich",
229
- },
230
268
  "documentDesigner.mail.language.de": {
231
269
  en: "German",
232
270
  de: "Deutsch",
@@ -240,8 +278,8 @@ export const DOCUMENT_DESIGNER_I18N = {
240
278
  de: "Spanisch",
241
279
  },
242
280
  "documentDesigner.mail.origin.overrides": {
243
- en: 'Overrides the system template "{title}" v{version}.',
244
- de: "Überschreibt die System-Vorlage „{title}“ v{version}.",
281
+ en: "Overrides the system template v{version}.",
282
+ de: "Überschreibt die System-Vorlage v{version}.",
245
283
  },
246
284
  "documentDesigner.mail.origin.tenantOnly": {
247
285
  en: "This template exists only for your tenant.",
@@ -260,8 +298,8 @@ export const DOCUMENT_DESIGNER_I18N = {
260
298
  de: "Archiviert.",
261
299
  },
262
300
  "documentDesigner.mail.origin.reset": {
263
- en: "Reset to system template",
264
- de: "Auf System-Vorlage zurücksetzen",
301
+ en: "Reset",
302
+ de: "Zurücksetzen",
265
303
  },
266
304
  "documentDesigner.mail.origin.system": {
267
305
  en: "You are viewing the system template v{version}. Saving creates a customisation for your tenant.",
@@ -272,21 +310,37 @@ export const DOCUMENT_DESIGNER_I18N = {
272
310
  de: "System-Vorlage v{version}. Speichern ändert sie für alle Mandanten ohne eigene Anpassung.",
273
311
  },
274
312
  "documentDesigner.mail.origin.missing": {
275
- en: "There is no own version for {language}. Recipients with language {locale} get the {fallback} version.",
276
- de: "Für {language} gibt es keine eigene Fassung. Empfänger mit Sprache {locale} bekommen die Fassung {fallback}.",
313
+ en: "There is no version for {language} yet.",
314
+ de: "Für {language} gibt es noch keine Fassung.",
315
+ },
316
+ "documentDesigner.mail.missing.text": {
317
+ en: "There is no version for {language} yet. Recipients with language {locale} get the {fallbackAdjective} version.",
318
+ de: "Für {language} gibt es noch keine Fassung. Empfänger mit Sprache {locale} bekommen die {fallbackAdjective} Fassung.",
319
+ },
320
+ "documentDesigner.mail.missing.textNoFallback": {
321
+ en: "There is no version for {language} yet.",
322
+ de: "Für {language} gibt es noch keine Fassung.",
277
323
  },
278
- "documentDesigner.mail.origin.missingNoFallback": {
279
- en: "There is no own version for {language}.",
280
- de: "Für {language} gibt es keine eigene Fassung.",
324
+ "documentDesigner.mail.missing.copy": {
325
+ en: "Copy from {language}",
326
+ de: "Aus {language} kopieren",
327
+ },
328
+ "documentDesigner.mail.languageAdjective.de": {
329
+ en: "German",
330
+ de: "deutsche",
331
+ },
332
+ "documentDesigner.mail.languageAdjective.en": {
333
+ en: "English",
334
+ de: "englische",
335
+ },
336
+ "documentDesigner.mail.languageAdjective.es": {
337
+ en: "Spanish",
338
+ de: "spanische",
281
339
  },
282
340
  "documentDesigner.mail.origin.copyNote": {
283
341
  en: "Copying a system version does not bring images or attachments along; upload them again afterwards.",
284
342
  de: "Beim Kopieren einer System-Fassung kommen Bilder und Anhänge nicht mit; laden Sie sie danach neu hoch.",
285
343
  },
286
- "documentDesigner.mail.origin.copyFrom": {
287
- en: "Copy from {fallback}",
288
- de: "Aus {fallback} kopieren",
289
- },
290
344
  "documentDesigner.mail.origin.createBlank": {
291
345
  en: "Start empty",
292
346
  de: "Leer anlegen",
@@ -323,6 +377,14 @@ export const DOCUMENT_DESIGNER_I18N = {
323
377
  en: "System placeholders",
324
378
  de: "System-Platzhalter",
325
379
  },
380
+ "documentDesigner.mail.system.layout": {
381
+ en: "Layout",
382
+ de: "Layout",
383
+ },
384
+ "documentDesigner.mail.system.hint": {
385
+ en: "Filled in by the platform. Imprint and unsubscribe link come from the layout.",
386
+ de: "Füllt die Plattform. Impressum und Abmeldelink liefert das Layout.",
387
+ },
326
388
  "documentDesigner.mail.resource.title": {
327
389
  en: "Resources",
328
390
  de: "Ressourcen",
@@ -352,8 +414,8 @@ export const DOCUMENT_DESIGNER_I18N = {
352
414
  de: "Entfernen",
353
415
  },
354
416
  "documentDesigner.mail.resource.caption": {
355
- en: "{key} · embedded as CID",
356
- de: "{key} · als CID eingebettet",
417
+ en: "{key} · {size} · embedded as CID",
418
+ de: "{key} · {size} · als CID eingebettet",
357
419
  },
358
420
  "documentDesigner.mail.resource.hint": {
359
421
  en: "Inline images are sent as CID. Attachments from PDF templates are generated when the mail is sent.",
@@ -407,6 +469,10 @@ export const DOCUMENT_DESIGNER_I18N = {
407
469
  en: "Preview text",
408
470
  de: "Vorschautext",
409
471
  },
472
+ "documentDesigner.mail.meta.previewTextPlaceholder": {
473
+ en: "Short line shown next to the subject in the inbox",
474
+ de: "Kurze Zeile, die im Posteingang neben dem Betreff erscheint",
475
+ },
410
476
  "documentDesigner.mail.props.title": {
411
477
  en: "Properties",
412
478
  de: "Eigenschaften",
@@ -415,6 +481,10 @@ export const DOCUMENT_DESIGNER_I18N = {
415
481
  en: "Layout",
416
482
  de: "Layout",
417
483
  },
484
+ "documentDesigner.mail.props.layoutBuiltIn": {
485
+ en: "Standard layout (built in)",
486
+ de: "Standard-Layout (eingebaut)",
487
+ },
418
488
  "documentDesigner.mail.props.layoutTenant": {
419
489
  en: "{title} (tenant customisation v{version})",
420
490
  de: "{title} (Mandanten-Anpassung v{version})",
@@ -431,6 +501,18 @@ export const DOCUMENT_DESIGNER_I18N = {
431
501
  en: "Default sender of the tenant",
432
502
  de: "Standard-Absender des Mandanten",
433
503
  },
504
+ "documentDesigner.mail.props.senderPlaceholder": {
505
+ en: "Name shown to recipients",
506
+ de: "Name, den Empfänger sehen",
507
+ },
508
+ "documentDesigner.mail.props.senderHint": {
509
+ en: "The address comes from the platform's mail delivery.",
510
+ de: "Die Adresse kommt vom Mail-Versand der Plattform.",
511
+ },
512
+ "documentDesigner.mail.props.replyTo": {
513
+ en: "Reply to",
514
+ de: "Antwort an",
515
+ },
434
516
  "documentDesigner.mail.props.category": {
435
517
  en: "Category",
436
518
  de: "Kategorie",
@@ -447,6 +529,62 @@ export const DOCUMENT_DESIGNER_I18N = {
447
529
  en: 'Transactional mails need no unsubscribe link. With "Marketing" the layout must provide one.',
448
530
  de: "Transaktionale Mails brauchen keinen Abmeldelink. Bei „Marketing“ muss das Layout den Abmeldelink liefern.",
449
531
  },
532
+ "documentDesigner.mail.chrome.title": {
533
+ en: "Header and footer",
534
+ de: "Kopf und Fuß",
535
+ },
536
+ "documentDesigner.mail.chrome.headerChip": {
537
+ en: "Label in the header",
538
+ de: "Kennzeichen im Kopf",
539
+ },
540
+ "documentDesigner.mail.chrome.headerChipPlaceholder": {
541
+ en: "e.g. New incident",
542
+ de: "z. B. Neuer Incident",
543
+ },
544
+ "documentDesigner.mail.chrome.tone": {
545
+ en: "Tone",
546
+ de: "Farbton",
547
+ },
548
+ "documentDesigner.mail.chrome.toneAccent": {
549
+ en: "Accent colour (default)",
550
+ de: "Akzentfarbe (Standard)",
551
+ },
552
+ "documentDesigner.mail.chrome.toneInfo": {
553
+ en: "Info (blue grey)",
554
+ de: "Info (blaugrau)",
555
+ },
556
+ "documentDesigner.mail.chrome.toneSuccess": {
557
+ en: "Success (green)",
558
+ de: "Erfolg (grün)",
559
+ },
560
+ "documentDesigner.mail.chrome.toneWarning": {
561
+ en: "Warning (amber)",
562
+ de: "Warnung (gelb)",
563
+ },
564
+ "documentDesigner.mail.chrome.toneDanger": {
565
+ en: "Danger (red)",
566
+ de: "Gefahr (rot)",
567
+ },
568
+ "documentDesigner.mail.chrome.toneNeutral": {
569
+ en: "Neutral (grey)",
570
+ de: "Neutral (grau)",
571
+ },
572
+ "documentDesigner.mail.chrome.toneFromPlaceholder": {
573
+ en: "From placeholder: {value}",
574
+ de: "Aus Platzhalter: {value}",
575
+ },
576
+ "documentDesigner.mail.chrome.footerNote": {
577
+ en: "Note in the footer",
578
+ de: "Hinweis im Fuß",
579
+ },
580
+ "documentDesigner.mail.chrome.footerNotePlaceholder": {
581
+ en: "e.g. why the recipient gets this mail",
582
+ de: "z. B. warum der Empfänger diese Mail bekommt",
583
+ },
584
+ "documentDesigner.mail.chrome.hint": {
585
+ en: "The label sits right of the logo; the tone colours it and the line at the top. The note stands above the website and unsubscribe links. Layouts with their own MJML code show neither.",
586
+ de: "Das Kennzeichen steht rechts neben dem Logo, der Farbton färbt es und die Linie oben. Der Hinweis steht über den Links zur Website und zum Abmelden. Layouts mit eigenem MJML-Code zeigen beides nicht.",
587
+ },
450
588
  "documentDesigner.mail.attachments.title": {
451
589
  en: "Attachments",
452
590
  de: "Anhänge",
@@ -467,6 +605,10 @@ export const DOCUMENT_DESIGNER_I18N = {
467
605
  en: "File",
468
606
  de: "Datei",
469
607
  },
608
+ "documentDesigner.mail.attachments.hint": {
609
+ en: "PDF templates are generated when the mail is sent; files go out as uploaded.",
610
+ de: "PDF-Vorlagen werden beim Versand erzeugt, Dateien gehen so mit, wie sie hochgeladen wurden.",
611
+ },
470
612
  "documentDesigner.mail.pdfLink.title": {
471
613
  en: "Attachment from PDF template",
472
614
  de: "Anhang aus PDF-Vorlage",
@@ -511,6 +653,10 @@ export const DOCUMENT_DESIGNER_I18N = {
511
653
  en: "v{version} {when}, {name}",
512
654
  de: "v{version} {when}, {name}",
513
655
  },
656
+ "documentDesigner.mail.versions.hint": {
657
+ en: "Every save creates a new version. The active one is what recipients get.",
658
+ de: "Jedes Speichern legt eine neue Version an. Aktiv ist die Version, die Empfänger bekommen.",
659
+ },
514
660
  "documentDesigner.mail.footer.missing": {
515
661
  en: 'Required placeholder "{names}" is missing in subject, preview text and content. Publishing is blocked.',
516
662
  de: "Pflicht-Platzhalter „{names}“ fehlt in Betreff, Vorschautext und Inhalt. Veröffentlichen ist gesperrt.",
@@ -539,17 +685,9 @@ export const DOCUMENT_DESIGNER_I18N = {
539
685
  en: "Discard",
540
686
  de: "Verwerfen",
541
687
  },
542
- "documentDesigner.mail.activeSave.title": {
543
- en: "Save as draft?",
544
- de: "Als Entwurf speichern?",
545
- },
546
- "documentDesigner.mail.activeSave.description": {
547
- en: "Saving as a draft takes the active customisation offline until you publish. Publish instead?",
548
- de: "Speichern als Entwurf nimmt die aktive Anpassung offline, bis Sie veröffentlichen. Stattdessen veröffentlichen?",
549
- },
550
- "documentDesigner.mail.activeSave.confirm": {
551
- en: "Save as draft",
552
- de: "Als Entwurf speichern",
688
+ "documentDesigner.origin.draftBesideActive": {
689
+ en: "Version {active} is sent, your draft (version {draft}) is not published yet.",
690
+ de: "Verschickt wird Version {active}, Ihr Entwurf (Version {draft}) ist noch nicht veröffentlicht.",
553
691
  },
554
692
  "documentDesigner.mail.leave.title": {
555
693
  en: "Unsaved changes",
@@ -568,8 +706,8 @@ export const DOCUMENT_DESIGNER_I18N = {
568
706
  de: "Verwerfen",
569
707
  },
570
708
  "documentDesigner.mail.preview.bar": {
571
- en: 'Preview with sample values from the placeholder schema. Rendered through "{layout}".',
572
- de: "Vorschau mit Beispielwerten aus dem Platzhalter-Schema. Gerendert über „{layout}“.",
709
+ en: 'Preview with sample values from the placeholder schema. Rendered through "Mail layout v{version}", renderer: MJML.',
710
+ de: "Vorschau mit Beispielwerten aus dem Platzhalter-Schema. Gerendert über „Mail-Layout v{version}“, Renderer: MJML.",
573
711
  },
574
712
  "documentDesigner.mail.preview.barPlain": {
575
713
  en: "Preview with sample values from the placeholder schema.",
@@ -611,6 +749,14 @@ export const DOCUMENT_DESIGNER_I18N = {
611
749
  en: "Mail preview",
612
750
  de: "Mail-Vorschau",
613
751
  },
752
+ "documentDesigner.mail.preview.from": {
753
+ en: "From",
754
+ de: "Von",
755
+ },
756
+ "documentDesigner.mail.preview.to": {
757
+ en: "To",
758
+ de: "An",
759
+ },
614
760
  "documentDesigner.mail.preview.samples": {
615
761
  en: "Sample values",
616
762
  de: "Beispielwerte",
@@ -631,6 +777,10 @@ export const DOCUMENT_DESIGNER_I18N = {
631
777
  en: "Checks",
632
778
  de: "Prüfung",
633
779
  },
780
+ "documentDesigner.mail.preview.checksHint": {
781
+ en: "What the server found while rendering this mail.",
782
+ de: "Das hat der Server beim Rendern dieser Mail gefunden.",
783
+ },
634
784
  "documentDesigner.mail.testMail.title": {
635
785
  en: "Test mail",
636
786
  de: "Test-Mail",
@@ -643,13 +793,21 @@ export const DOCUMENT_DESIGNER_I18N = {
643
793
  en: "Your address",
644
794
  de: "Ihre Adresse",
645
795
  },
646
- "documentDesigner.mail.testMail.prefix": {
647
- en: "Subject with [TEST] prefix",
796
+ "documentDesigner.mail.testMail.changeAddress": {
797
+ en: "Change address",
798
+ de: "Adresse ändern",
799
+ },
800
+ "documentDesigner.mail.testMail.prefixSubject": {
801
+ en: "Prefix the subject with [TEST]",
648
802
  de: "Betreff mit Präfix [TEST]",
649
803
  },
650
804
  "documentDesigner.mail.testMail.attachments": {
651
- en: "Attachments are sent along",
652
- de: "Anhänge werden mitgesendet",
805
+ en: "Send attachments along",
806
+ de: "Anhang mitsenden",
807
+ },
808
+ "documentDesigner.mail.testMail.noAttachments": {
809
+ en: "This template has no attachments",
810
+ de: "Diese Vorlage hat keine Anhänge",
653
811
  },
654
812
  "documentDesigner.mail.testMail.send": {
655
813
  en: "Send test mail",
package/dist/web/cells.js CHANGED
@@ -6,14 +6,15 @@ import { formatWhen, useQuery, useTranslation, } from "@cosmicdrift/kumiko-rende
6
6
  import { StatusBadge } from "@cosmicdrift/kumiko-renderer-web";
7
7
  import { parseOverviewRow } from "./overview-row.js";
8
8
  import { statusLabel, statusTone } from "./status-label.js";
9
- import { usedByLabel } from "./used-by-label.js";
9
+ import { templateTitle, usedByLabel } from "./used-by-label.js";
10
10
  const EMPTY_MARK = "–";
11
11
  function withRow(row, render) {
12
12
  const parsed = parseOverviewRow(row);
13
13
  return parsed === null ? EMPTY_MARK : render(parsed);
14
14
  }
15
15
  export function TitleCell({ row }) {
16
- return withRow(row, (r) => (_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: r.title }), _jsx("span", { className: "block max-w-40 truncate font-mono text-xs text-muted-foreground", title: r.slug, children: r.slug })] })));
16
+ const t = useTranslation();
17
+ return withRow(row, (r) => (_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: templateTitle(t, r) }), _jsx("span", { className: "block max-w-40 truncate font-mono text-xs text-muted-foreground", title: r.slug, children: r.slug })] })));
17
18
  }
18
19
  const KIND_KEY = {
19
20
  mail: "documentDesigner.kind.mail",
@@ -44,7 +45,7 @@ export function createLocalesCell(appLocales) {
44
45
  }
45
46
  export function StatusCell({ row }) {
46
47
  const t = useTranslation();
47
- return withRow(row, (r) => (_jsx(StatusBadge, { tone: statusTone(r.status), testId: "document-designer-status", children: statusLabel(t, r.status, r.version) })));
48
+ return withRow(row, (r) => (_jsxs("span", { className: "flex flex-col items-start gap-1", children: [_jsx(StatusBadge, { tone: statusTone(r.status), testId: "document-designer-status", children: statusLabel(t, r.status, r.version) }), r.status === "active" && r.draftVersion !== null && (_jsx(StatusBadge, { tone: statusTone("draft"), testId: "document-designer-draft-status", children: statusLabel(t, "draft", r.draftVersion) }))] })));
48
49
  }
49
50
  export function UsedByCell({ row }) {
50
51
  const t = useTranslation();
@@ -0,0 +1,37 @@
1
+ import type { ReactNode } from "react";
2
+ export type EditorMode = "edit" | "preview";
3
+ /** "override" is a tenant row that replaces a system version; "tenant" has no system counterpart. */
4
+ export type EditorOrigin = "system" | "override" | "tenant";
5
+ export declare function originOf(isSystemRow: boolean, hasSystemVersion: boolean): EditorOrigin;
6
+ export declare function OriginBadge({ origin }: {
7
+ readonly origin: EditorOrigin;
8
+ }): ReactNode;
9
+ export declare function EditorModeSwitch({ testIdPrefix, mode, onMode, }: {
10
+ readonly testIdPrefix: string;
11
+ readonly mode: EditorMode;
12
+ readonly onMode: (mode: EditorMode) => void;
13
+ }): ReactNode;
14
+ export declare function EditorLocaleSwitch({ testIdPrefix, locales, locale, onLocale, }: {
15
+ readonly testIdPrefix: string;
16
+ readonly locales: readonly string[];
17
+ readonly locale: string;
18
+ readonly onLocale: (locale: string) => void;
19
+ }): ReactNode;
20
+ export type StoredVersion = {
21
+ readonly status: string;
22
+ readonly version: number;
23
+ };
24
+ export type EditorSaveActions = {
25
+ readonly busy: boolean;
26
+ readonly canSave: boolean;
27
+ readonly canPublish: boolean;
28
+ readonly onSave: () => void;
29
+ readonly onPublish: () => void;
30
+ };
31
+ export declare function EditorHeader({ title, stored, actions, children, }: {
32
+ readonly title: string;
33
+ readonly stored: StoredVersion | null;
34
+ readonly actions: EditorSaveActions;
35
+ /** View controls left of the save actions (mode or kind switch). */
36
+ readonly children: ReactNode;
37
+ }): ReactNode;
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // @runtime client
3
+ import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
4
+ import { ModeSwitch, StatusBadge } from "@cosmicdrift/kumiko-renderer-web";
5
+ import { asRowStatus, statusLabel, statusTone } from "./status-label.js";
6
+ const ORIGIN_LABEL = {
7
+ system: "documentDesigner.origin.system",
8
+ override: "documentDesigner.origin.override",
9
+ tenant: "documentDesigner.source.tenant",
10
+ };
11
+ export function originOf(isSystemRow, hasSystemVersion) {
12
+ return isSystemRow ? "system" : hasSystemVersion ? "override" : "tenant";
13
+ }
14
+ export function OriginBadge({ origin }) {
15
+ const t = useTranslation();
16
+ return (_jsx(StatusBadge, { tone: origin === "system" ? "muted" : "accent", testId: "document-designer-editor-origin", children: t(ORIGIN_LABEL[origin]) }));
17
+ }
18
+ export function EditorModeSwitch({ testIdPrefix, mode, onMode, }) {
19
+ const t = useTranslation();
20
+ return (_jsx(ModeSwitch, { value: mode, onChange: onMode, ariaLabel: t("documentDesigner.mail.header.modeSwitch"), testId: `${testIdPrefix}-mode-switch`, options: [
21
+ { value: "edit", label: t("documentDesigner.mail.header.edit") },
22
+ { value: "preview", label: t("documentDesigner.mail.header.preview") },
23
+ ] }));
24
+ }
25
+ export function EditorLocaleSwitch({ testIdPrefix, locales, locale, onLocale, }) {
26
+ const t = useTranslation();
27
+ return (_jsx(ModeSwitch, { value: locale, onChange: onLocale, ariaLabel: t("documentDesigner.mail.header.localeSwitch"), testId: `${testIdPrefix}-locale-switch`, options: locales.map((code) => ({ value: code, label: code })) }));
28
+ }
29
+ function StoredStatus({ stored }) {
30
+ const t = useTranslation();
31
+ const status = asRowStatus(stored.status);
32
+ return (_jsx(StatusBadge, { tone: statusTone(status), testId: "document-designer-editor-status", children: statusLabel(t, status, stored.version) }));
33
+ }
34
+ // Title, status and the save/publish actions live in the shell header. The origin badge, the
35
+ // locale switch and the secondary action sit in the info bar below: at 1280px next to the
36
+ // sidebar, assistant and theme toggle the header has no room for them and the title would clip.
37
+ export function EditorHeader({ title, stored, actions, children, }) {
38
+ const t = useTranslation();
39
+ const { Button, PageHeader } = usePrimitives();
40
+ if (PageHeader === undefined)
41
+ return null;
42
+ return (_jsx(PageHeader, { title: title, status: stored !== null && _jsx(StoredStatus, { stored: stored }), actions: _jsxs("span", { className: "flex items-center gap-2 [&_button]:px-2.5 [&_button]:text-sm", children: [children, _jsx(Button, { variant: "secondary", size: "sm", disabled: !actions.canSave, loading: actions.busy, onClick: actions.onSave, testId: "document-designer-header-save", children: t("documentDesigner.editor.save") }), _jsx(Button, { size: "sm", disabled: !actions.canPublish, onClick: actions.onPublish, testId: "document-designer-header-publish", children: t("documentDesigner.editor.publish") })] }) }));
43
+ }
@@ -0,0 +1 @@
1
+ export declare function defaultLocaleOf(uiLocale: string, appLocales: readonly string[], rowLocales: readonly string[]): string;
@@ -0,0 +1,15 @@
1
+ // @runtime client
2
+ import { DEFAULT_TEMPLATE_LOCALES } from "../constants.js";
3
+ // The editor opens in the language the user works in when the template has it
4
+ // (or, for a template without any version yet, when the app offers it).
5
+ export function defaultLocaleOf(uiLocale, appLocales, rowLocales) {
6
+ const available = rowLocales.length > 0 ? rowLocales : appLocales;
7
+ const uiLanguage = uiLocale.split("-")[0] ?? uiLocale;
8
+ const fromUi = [uiLocale, uiLanguage].find((candidate) => available.includes(candidate));
9
+ if (fromUi !== undefined)
10
+ return fromUi;
11
+ const preferred = appLocales[0] ?? DEFAULT_TEMPLATE_LOCALES[0] ?? "de";
12
+ if (rowLocales.length === 0 || rowLocales.includes(preferred))
13
+ return preferred;
14
+ return rowLocales[0] ?? preferred;
15
+ }
@@ -0,0 +1,40 @@
1
+ import type { ReactNode } from "react";
2
+ import { type EditorOrigin } from "./editor-header.js";
3
+ export type InfoBarTone = "active" | "muted" | "warn";
4
+ export type FooterTone = "bad" | "warn" | "ok";
5
+ export declare function EditorFrame({ testId, children, }: {
6
+ readonly testId: string;
7
+ readonly children: ReactNode;
8
+ }): ReactNode;
9
+ export declare function EditorInfoBar({ tone, testId, origin, controls, links, overlays, children, }: {
10
+ readonly tone: InfoBarTone;
11
+ readonly testId: string;
12
+ readonly origin?: EditorOrigin | null;
13
+ /** Right side, before the links: locale switch and the editor's secondary action. */
14
+ readonly controls?: ReactNode;
15
+ readonly links?: ReactNode;
16
+ readonly overlays?: ReactNode;
17
+ readonly children: ReactNode;
18
+ }): ReactNode;
19
+ export declare function EditorBody({ left, center, right, testId, }: {
20
+ readonly left?: ReactNode;
21
+ readonly center: ReactNode;
22
+ readonly right: ReactNode;
23
+ readonly testId?: string;
24
+ }): ReactNode;
25
+ export declare function EditorFooter({ testIdPrefix, message, saveError, modifiedAt, dirty, busy, canSave, canPublish, onDiscard, onSave, onPublish, }: {
26
+ readonly testIdPrefix: string;
27
+ readonly message: {
28
+ readonly tone: FooterTone;
29
+ readonly text: string;
30
+ } | null;
31
+ readonly saveError: string | null;
32
+ readonly modifiedAt: string | null;
33
+ readonly dirty: boolean;
34
+ readonly busy: boolean;
35
+ readonly canSave: boolean;
36
+ readonly canPublish: boolean;
37
+ readonly onDiscard: () => void;
38
+ readonly onSave: () => void;
39
+ readonly onPublish: () => void;
40
+ }): ReactNode;
@@ -0,0 +1,36 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // @runtime client
3
+ import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
4
+ import { cn } from "@cosmicdrift/kumiko-renderer-web";
5
+ import { OriginBadge } from "./editor-header.js";
6
+ import { formatRelativeWhen } from "./relative-time.js";
7
+ const INFO_BAR_TONE = {
8
+ active: "bg-primary/10",
9
+ muted: "bg-muted",
10
+ warn: "bg-status-warn-surface",
11
+ };
12
+ const FOOTER_TONE = {
13
+ ok: { text: "text-status-ok", glyph: "✓" },
14
+ warn: { text: "text-status-warn", glyph: "!" },
15
+ bad: { text: "text-status-bad", glyph: "!" },
16
+ };
17
+ // Height below the shell header: the rails and the center scroll on their own.
18
+ export function EditorFrame({ testId, children, }) {
19
+ return (_jsx("div", { className: "flex h-[calc(100svh-var(--shell-header-height))] min-h-[600px] flex-col", "data-testid": testId, children: children }));
20
+ }
21
+ export function EditorInfoBar({ tone, testId, origin, controls, links, overlays, children, }) {
22
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: cn("flex items-start gap-4 border-b px-6 py-2 text-sm", INFO_BAR_TONE[tone]), "data-testid": testId, "data-variant": tone === "warn" ? "warn" : undefined, children: [_jsxs("span", { className: "min-w-0 flex-1", children: [origin != null && _jsx(OriginBadge, { origin: origin }), " ", children] }), controls !== undefined && (_jsx("div", { className: "flex shrink-0 items-center gap-2", children: controls })), links !== undefined && _jsx("div", { className: "flex shrink-0 items-center gap-4", children: links })] }), overlays] }));
23
+ }
24
+ export function EditorBody({ left, center, right, testId, }) {
25
+ return (_jsxs("div", { className: cn("grid min-h-0 flex-1", left === undefined
26
+ ? "grid-cols-[minmax(0,1fr)_280px]"
27
+ : "grid-cols-[240px_minmax(0,1fr)_280px]"), "data-testid": testId, children: [left !== undefined && (_jsx("aside", { className: "min-h-0 overflow-y-auto border-r bg-background", children: left })), _jsx("div", { className: "flex min-h-0 min-w-0 flex-col overflow-hidden bg-muted", children: center }), _jsx("aside", { className: "min-h-0 overflow-y-auto border-l bg-background", children: right })] }));
28
+ }
29
+ export function EditorFooter({ testIdPrefix, message, saveError, modifiedAt, dirty, busy, canSave, canPublish, onDiscard, onSave, onPublish, }) {
30
+ const t = useTranslation();
31
+ const { Banner, Button, Text } = usePrimitives();
32
+ const tone = message === null ? null : FOOTER_TONE[message.tone];
33
+ return (_jsxs("div", { className: "flex items-center gap-4 border-t bg-background px-6 py-3", "data-testid": `${testIdPrefix}-footer`, children: [_jsxs("div", { className: "min-w-0 flex-1 text-sm", children: [saveError !== null ? (_jsx(Banner, { variant: "error", testId: `${testIdPrefix}-save-error`, children: saveError })) : (_jsx("span", { className: cn(tone === null ? "text-muted-foreground" : tone.text), "data-testid": `${testIdPrefix}-footer-message`, "data-tone": message?.tone, children: message === null
34
+ ? t("documentDesigner.editor.footer.checking")
35
+ : `${tone?.glyph ?? ""} ${message.text}` })), modifiedAt !== null && (_jsxs(_Fragment, { children: [" ", _jsx(Text, { variant: "muted", testId: `${testIdPrefix}-saved-at`, children: t("documentDesigner.mail.footer.savedAt", { when: formatRelativeWhen(modifiedAt) }) })] }))] }), _jsxs("div", { className: "flex shrink-0 items-center gap-3", children: [_jsx(Button, { variant: "ghost", disabled: !dirty, onClick: onDiscard, testId: "document-designer-discard", children: t("documentDesigner.editor.discard") }), _jsx(Button, { variant: "secondary", disabled: !canSave, loading: busy, onClick: onSave, testId: "document-designer-save", children: t("documentDesigner.editor.save") }), _jsx(Button, { disabled: !canPublish, onClick: onPublish, testId: "document-designer-publish", children: t("documentDesigner.editor.publish") })] })] }));
36
+ }