@u-krupaveho-kraba/form-editor-schemas 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  export * from "./schemas/campaignItem.js";
2
2
  export * from "./schemas/theme.js";
3
+ export * from "./schemas/editorTheme.js";
3
4
  export * from "./schemas/fileManager.js";
5
+ export * from "./schemas/notification.js";
4
6
  export * from "./schemas/options.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@u-krupaveho-kraba/form-editor-schemas",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Zod schemas for the Topol Form Editor's host-facing init/save contract",
5
5
  "author": "Topol.io",
6
6
  "license": "ISC",
@@ -24,7 +24,7 @@
24
24
  "access": "public"
25
25
  },
26
26
  "peerDependencies": {
27
- "@u-krupaveho-kraba/form-schemas": "^0.4.0",
27
+ "@u-krupaveho-kraba/form-schemas": "^0.5.0",
28
28
  "zod": "^4.1.11"
29
29
  }
30
30
  }
@@ -0,0 +1,102 @@
1
+ import * as z from "zod/mini";
2
+ /**
3
+ * The Host-settable look of the **editor's own UI chrome** -- the accent
4
+ * colour on buttons, active states and the canvas selection outline, the
5
+ * gray ramp every panel and toolbar is built from, the corner radii and the
6
+ * UI font.
7
+ *
8
+ * Not to be confused with `themes` (`./theme.ts`), which is the Brand Kit /
9
+ * Theme Panel library that styles *the form being built* (ADR 0022). The
10
+ * two are unrelated concerns that happen to share the word "theme": this
11
+ * one never reaches a published form, and `themes` never touches the
12
+ * editor's chrome.
13
+ *
14
+ * Deliberately the same shape as email-editor's and LPE's `theme` option
15
+ * (`apps/email-editor/src/schemas/theme/themeSchema.ts`), so a Host
16
+ * embedding more than one editor passes one object to all of them and they
17
+ * match. Applied as CSS custom properties on `<html>` by `setTheme()`
18
+ * (`@topol/utils`) -- no recompile, no store.
19
+ *
20
+ * The one key dropped from email-editor's shape is `canvasColor`, which
21
+ * sets `--c-email-canvas`: that is the paper colour behind an *email*
22
+ * template, a var no form-editor stylesheet reads. Everything else is kept
23
+ * even where form-editor happens not to read the var today, so that this
24
+ * and email-editor's option stay one shape rather than two that drift.
25
+ */
26
+ export const EditorThemeSchema = z.object({
27
+ /**
28
+ * Which built-in gray ramp and accent to start from before any `colors`
29
+ * override. Defaults to `"dark"`, which is how the editor has always
30
+ * looked; `"light"` is the same ramp inverted.
31
+ *
32
+ * **`"light"` is accepted but not yet audited in the Form Editor**
33
+ * (ticket 196). The mechanism works -- the ramp inverts, the accent
34
+ * swaps -- but the Form Editor's own chrome was authored against the
35
+ * dark ramp only, so some utilities mean "a light surface" and get a
36
+ * dark one. Measured examples: the element-tile label drops to 3.28:1
37
+ * (below WCAG AA), and the band framing the canvas (`bg-gray-100`)
38
+ * inverts from light to dark. Kept in the contract rather than rejected
39
+ * so a Host can pass one theme object to every editor it embeds, per
40
+ * this schema's whole purpose.
41
+ */
42
+ preset: z.optional(z.enum(["light", "dark"])),
43
+ /**
44
+ * Corner radii for the editor's own controls, as CSS lengths ("4px",
45
+ * "0.5rem"). `small`/`large` also set `--b-radius-small`/`--b-radius-large`
46
+ * for `@topol/ui`'s older components.
47
+ */
48
+ borderRadius: z.optional(z.object({
49
+ extraSmall: z.optional(z.string()),
50
+ small: z.optional(z.string()),
51
+ large: z.optional(z.string()),
52
+ extraLarge: z.optional(z.string()),
53
+ })),
54
+ /**
55
+ * The editor UI's font. `url` is injected as a stylesheet link (a Google
56
+ * Fonts href, say) and `family` becomes `--font-family-primary`; a
57
+ * `family` with no comma gets a `, sans-serif` fallback appended.
58
+ */
59
+ font: z.optional(z.object({
60
+ family: z.optional(z.string()),
61
+ url: z.optional(z.string()),
62
+ })),
63
+ /**
64
+ * Individual overrides on top of `preset`. The numeric keys are the gray
65
+ * ramp, `"900"` being the darkest surface under `preset: "dark"`.
66
+ *
67
+ * The primary/accent knob most Hosts want is `active` -- it drives
68
+ * `--c-accent-1`, which is the editor's buttons, active states, focus
69
+ * outlines and the canvas selection outline.
70
+ */
71
+ colors: z.optional(z.object({
72
+ "900": z.optional(z.string()),
73
+ "800": z.optional(z.string()),
74
+ "700": z.optional(z.string()),
75
+ "600": z.optional(z.string()),
76
+ "500": z.optional(z.string()),
77
+ "400": z.optional(z.string()),
78
+ "300": z.optional(z.string()),
79
+ "200": z.optional(z.string()),
80
+ "100": z.optional(z.string()),
81
+ "50": z.optional(z.string()),
82
+ "10": z.optional(z.string()),
83
+ white: z.optional(z.string()),
84
+ primary: z.optional(z.string()),
85
+ "primary-light": z.optional(z.string()),
86
+ "primary-light-2": z.optional(z.string()),
87
+ "primary-dark": z.optional(z.string()),
88
+ secondary: z.optional(z.string()),
89
+ "secondary-light": z.optional(z.string()),
90
+ "secondary-light-2": z.optional(z.string()),
91
+ error: z.optional(z.string()),
92
+ "error-light": z.optional(z.string()),
93
+ success: z.optional(z.string()),
94
+ "success-light": z.optional(z.string()),
95
+ /** The editor's primary accent -- `--c-accent-1`. */
96
+ active: z.optional(z.string()),
97
+ "active-2": z.optional(z.string()),
98
+ "active-3": z.optional(z.string()),
99
+ "locked-structure": z.optional(z.string()),
100
+ "locked-content": z.optional(z.string()),
101
+ })),
102
+ });
@@ -0,0 +1,36 @@
1
+ import * as z from "zod/mini";
2
+ /**
3
+ * A transient, app-level message -- the Form Editor's notifications, declared
4
+ * here rather than app-locally because a Host can receive them: with
5
+ * `disableAlerts`, every notification is routed to the Host's `onAlert`
6
+ * instead of being rendered in the editor, so this shape crosses the wire.
7
+ *
8
+ * Deliberately identical to the Email Editor's and Landing Page Editor's
9
+ * `INotification`, minus two fields:
10
+ *
11
+ * - `expectSideEffect` is set at four call sites across those two apps and
12
+ * read at none. Carrying a field nothing consumes into a *new* published
13
+ * contract would mean hosts writing against something that does nothing.
14
+ * - `extarnalUse` (sic) means "render in-editor even when alerts are
15
+ * disabled". It has real consumers there -- init/auth failures a Host may
16
+ * not be listening for yet -- but none here, and it would arrive with its
17
+ * typo preserved or with a silent rename. When the Form Editor grows a
18
+ * notification that must survive `disableAlerts`, add it then, spelled
19
+ * correctly.
20
+ */
21
+ export const NotificationTypeSchema = z.enum(["info", "success", "error"]);
22
+ export const EditorNotificationSchema = z.object({
23
+ /** Optional bold first line. Rendered above `text` when present. */
24
+ title: z.optional(z.string()),
25
+ /** The message itself, already translated -- callers own i18n. */
26
+ text: z.string(),
27
+ type: NotificationTypeSchema,
28
+ /** Suppresses the auto-dismiss timer; the notification stays until closed. */
29
+ persistent: z.optional(z.boolean()),
30
+ /**
31
+ * Assigned by the editor when the notification is created, not by the
32
+ * caller -- optional here because it only exists once a notification is
33
+ * live. A Host receiving one through `onAlert` always sees it set.
34
+ */
35
+ id: z.optional(z.string()),
36
+ });
@@ -1,6 +1,7 @@
1
1
  import * as z from "zod/mini";
2
2
  import { FormThemeSchema, InputFieldSchema, PreferenceSchema, } from "@u-krupaveho-kraba/form-schemas";
3
3
  import { CampaignItemSchema, } from "./campaignItem.js";
4
+ import { EditorThemeSchema } from "./editorTheme.js";
4
5
  import { FileManagerOptionsSchema } from "./fileManager.js";
5
6
  import { ThemeGroupSchema, } from "./theme.js";
6
7
  /**
@@ -61,6 +62,17 @@ export const EditorCallbacksSchema = z.object({
61
62
  onCreatePreference: z.optional(callback()),
62
63
  /** Fired when the editor fails to initialize, or when a payload the Host sent fails validation (ADR 0029 decision 3). */
63
64
  onError: z.optional(callback()),
65
+ /**
66
+ * Receives every notification the editor would otherwise have rendered
67
+ * itself, so a Host can surface them in its own UI -- same `{ notification }`
68
+ * argument shape LPE's `onAlert` uses, so a Host already embedding that
69
+ * editor can reuse its handler.
70
+ *
71
+ * Only called when `disableAlerts` is on. Unlike every other optional
72
+ * callback here, its *presence* is not the affordance: the editor keeps
73
+ * rendering its own notifications until a Host explicitly asks it to stop.
74
+ */
75
+ onAlert: z.optional(callback()),
64
76
  /** Fired when the user clicks the WindowBar's close button (requires `"close"` in `windowBar`). The Host is responsible for tearing down/hiding the editor -- the child does not attempt its own teardown. */
65
77
  onEditorClose: z.optional(callback()),
66
78
  /**
@@ -164,6 +176,17 @@ export const TopolFormEditorOptionsSchema = z
164
176
  language: z.optional(EditorLanguageSchema),
165
177
  /** Per-key overrides for editor UI strings, keyed by dot-path (e.g. "controls.autosaves.title"). Applied once at bootstrap, merged into whichever locale ends up active. */
166
178
  textOverride: z.optional(z.record(z.string(), z.string())),
179
+ /**
180
+ * The look of the editor's own UI chrome -- accent colour, gray ramp,
181
+ * corner radii, UI font. Same option name and shape as email-editor's
182
+ * and LPE's `theme`, so a Host embedding several editors passes one
183
+ * object to all of them. Omit it and the editor looks exactly as it
184
+ * always has (`preset: "dark"`).
185
+ *
186
+ * Note the neighbour below: `theme` is the *editor*, `themes` is the
187
+ * *form being built*. They are unrelated (see `./editorTheme.ts`).
188
+ */
189
+ theme: z.optional(EditorThemeSchema),
167
190
  /** Accepts the legacy flat shape for backward compatibility; a Host App is encouraged to move to `ThemeGroup[]` to supply locked/Brand Kit themes. */
168
191
  themes: z.optional(z.union([z.array(FormThemeSchema), z.array(ThemeGroupSchema)])),
169
192
  previewUrl: z.optional(z.string()),
@@ -214,5 +237,15 @@ export const TopolFormEditorOptionsSchema = z
214
237
  ])),
215
238
  comments: z.optional(CommentsConfigSchema),
216
239
  autosaves: z.optional(AutosaveConfigSchema),
240
+ /**
241
+ * Stops the editor rendering its own notifications and routes them to
242
+ * `callbacks.onAlert` instead, for a Host that wants them in its own UI
243
+ * (same option name and meaning as LPE's `disableAlerts`).
244
+ *
245
+ * Honoured only when `onAlert` is actually wired up -- a Host that sets
246
+ * this without it would otherwise silence the editor into a black hole,
247
+ * which is strictly worse than the notifications it asked to be rid of.
248
+ */
249
+ disableAlerts: z.optional(z.boolean()),
217
250
  })
218
251
  .check(z.refine(onlyOneOfItemAndItems, ITEM_AND_ITEMS_EXCLUSIVE_MESSAGE));
package/types/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export * from "./schemas/campaignItem.js";
2
2
  export * from "./schemas/theme.js";
3
+ export * from "./schemas/editorTheme.js";
3
4
  export * from "./schemas/fileManager.js";
5
+ export * from "./schemas/notification.js";
4
6
  export * from "./schemas/options.js";