@caltho/site-kit 0.2.9 → 0.2.11

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 (92) hide show
  1. package/README.md +63 -8
  2. package/dist/cli/check.js +31 -1
  3. package/dist/content/index.d.ts +318 -23
  4. package/dist/content/index.js +148 -15
  5. package/dist/content/look.d.ts +260 -9
  6. package/dist/content/look.js +386 -22
  7. package/dist/finish.js +9 -1
  8. package/dist/fonts/families/dm-sans/LICENSE +93 -0
  9. package/dist/fonts/families/dm-sans/dm-sans-latin-ext-wght-italic.woff2 +0 -0
  10. package/dist/fonts/families/dm-sans/dm-sans-latin-ext-wght-normal.woff2 +0 -0
  11. package/dist/fonts/families/dm-sans/dm-sans-latin-wght-italic.woff2 +0 -0
  12. package/dist/fonts/families/dm-sans/dm-sans-latin-wght-normal.woff2 +0 -0
  13. package/dist/fonts/families/fraunces/LICENSE +93 -0
  14. package/dist/fonts/families/fraunces/fraunces-latin-ext-wght-italic.woff2 +0 -0
  15. package/dist/fonts/families/fraunces/fraunces-latin-ext-wght-normal.woff2 +0 -0
  16. package/dist/fonts/families/fraunces/fraunces-latin-wght-italic.woff2 +0 -0
  17. package/dist/fonts/families/fraunces/fraunces-latin-wght-normal.woff2 +0 -0
  18. package/dist/fonts/families/inter/LICENSE +93 -0
  19. package/dist/fonts/families/inter/inter-latin-ext-wght-italic.woff2 +0 -0
  20. package/dist/fonts/families/inter/inter-latin-ext-wght-normal.woff2 +0 -0
  21. package/dist/fonts/families/inter/inter-latin-wght-italic.woff2 +0 -0
  22. package/dist/fonts/families/inter/inter-latin-wght-normal.woff2 +0 -0
  23. package/dist/fonts/families/lora/LICENSE +93 -0
  24. package/dist/fonts/families/lora/lora-latin-ext-wght-italic.woff2 +0 -0
  25. package/dist/fonts/families/lora/lora-latin-ext-wght-normal.woff2 +0 -0
  26. package/dist/fonts/families/lora/lora-latin-wght-italic.woff2 +0 -0
  27. package/dist/fonts/families/lora/lora-latin-wght-normal.woff2 +0 -0
  28. package/dist/fonts/families/manrope/LICENSE +93 -0
  29. package/dist/fonts/families/manrope/manrope-latin-ext-wght-normal.woff2 +0 -0
  30. package/dist/fonts/families/manrope/manrope-latin-wght-normal.woff2 +0 -0
  31. package/dist/fonts/families/newsreader/LICENSE +93 -0
  32. package/dist/fonts/families/newsreader/newsreader-latin-ext-wght-italic.woff2 +0 -0
  33. package/dist/fonts/families/newsreader/newsreader-latin-ext-wght-normal.woff2 +0 -0
  34. package/dist/fonts/families/newsreader/newsreader-latin-wght-italic.woff2 +0 -0
  35. package/dist/fonts/families/newsreader/newsreader-latin-wght-normal.woff2 +0 -0
  36. package/dist/fonts/families/outfit/LICENSE +93 -0
  37. package/dist/fonts/families/outfit/outfit-latin-ext-wght-normal.woff2 +0 -0
  38. package/dist/fonts/families/outfit/outfit-latin-wght-normal.woff2 +0 -0
  39. package/dist/fonts/families/playfair-display/LICENSE +93 -0
  40. package/dist/fonts/families/playfair-display/playfair-display-latin-ext-wght-italic.woff2 +0 -0
  41. package/dist/fonts/families/playfair-display/playfair-display-latin-ext-wght-normal.woff2 +0 -0
  42. package/dist/fonts/families/playfair-display/playfair-display-latin-wght-italic.woff2 +0 -0
  43. package/dist/fonts/families/playfair-display/playfair-display-latin-wght-normal.woff2 +0 -0
  44. package/dist/fonts/families/source-serif-4/LICENSE +93 -0
  45. package/dist/fonts/families/source-serif-4/source-serif-4-latin-ext-wght-italic.woff2 +0 -0
  46. package/dist/fonts/families/source-serif-4/source-serif-4-latin-ext-wght-normal.woff2 +0 -0
  47. package/dist/fonts/families/source-serif-4/source-serif-4-latin-wght-italic.woff2 +0 -0
  48. package/dist/fonts/families/source-serif-4/source-serif-4-latin-wght-normal.woff2 +0 -0
  49. package/dist/fonts/families/work-sans/LICENSE +93 -0
  50. package/dist/fonts/families/work-sans/work-sans-latin-ext-wght-italic.woff2 +0 -0
  51. package/dist/fonts/families/work-sans/work-sans-latin-ext-wght-normal.woff2 +0 -0
  52. package/dist/fonts/families/work-sans/work-sans-latin-wght-italic.woff2 +0 -0
  53. package/dist/fonts/families/work-sans/work-sans-latin-wght-normal.woff2 +0 -0
  54. package/dist/fonts/google.json +1 -0
  55. package/dist/fonts-google.d.ts +35 -0
  56. package/dist/fonts-google.js +70 -0
  57. package/dist/fonts.css +365 -0
  58. package/dist/frame.test.d.ts +1 -0
  59. package/dist/frame.test.js +33 -0
  60. package/dist/migrate.d.ts +45 -0
  61. package/dist/migrate.js +284 -0
  62. package/dist/next/block-mark.d.ts +7 -0
  63. package/dist/next/block-mark.js +16 -0
  64. package/dist/next/blocks.d.ts +11 -0
  65. package/dist/next/blocks.js +35 -0
  66. package/dist/next/data.d.ts +4 -0
  67. package/dist/next/data.js +4 -0
  68. package/dist/next/frame.d.ts +34 -0
  69. package/dist/next/frame.js +70 -0
  70. package/dist/next/index.d.ts +6 -2
  71. package/dist/next/index.js +7 -3
  72. package/dist/next/menu-group.d.ts +11 -0
  73. package/dist/next/menu-group.js +33 -0
  74. package/dist/next/parts/LookStyle.js +3 -1
  75. package/dist/next/preview.js +282 -25
  76. package/dist/next/sections.d.ts +21 -6
  77. package/dist/next/sections.js +167 -6
  78. package/dist/prepare.d.ts +5 -1
  79. package/dist/prepare.js +59 -1
  80. package/dist/preview-match.d.ts +8 -0
  81. package/dist/preview-match.js +33 -1
  82. package/dist/schema.d.ts +10 -0
  83. package/dist/schema.js +26 -0
  84. package/dist/sections/icons.d.ts +47 -0
  85. package/dist/sections/icons.js +51 -0
  86. package/dist/sections/index.d.ts +284 -2
  87. package/dist/sections/index.js +156 -2
  88. package/dist/sections/presets.d.ts +13 -0
  89. package/dist/sections/presets.js +300 -0
  90. package/dist/sections.css +1393 -9
  91. package/dist/sections.test.js +310 -8
  92. package/package.json +8 -3
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ export { ICON_NAMES, ICONS, type IconName } from "./icons.ts";
2
3
  /** Photos in a grid, each with a caption if it has one: their work, the place, the team at it. */
3
4
  export declare const gallery: z.ZodObject<{
4
5
  heading: z.ZodOptional<z.ZodString>;
@@ -30,11 +31,245 @@ export declare const slideshow: z.ZodObject<{
30
31
  }, z.core.$strip>>;
31
32
  shape: z.ZodDefault<z.ZodEnum<{
32
33
  square: "square";
33
- wide: "wide";
34
34
  landscape: "landscape";
35
+ wide: "wide";
35
36
  }>>;
36
37
  seconds: z.ZodDefault<z.ZodNumber>;
37
38
  }, z.core.$strip>;
39
+ /**
40
+ * A YouTube or Vimeo address as the privacy-friendly player it's shown in (YouTube's no-cookie one,
41
+ * Vimeo's with tracking off), or null for any other address. Watch, share, Shorts and embed
42
+ * addresses all work.
43
+ */
44
+ export declare function videoEmbed(url: string): {
45
+ src: string;
46
+ origin: string;
47
+ } | null;
48
+ /** Where a video part's players come from: a page showing one allows them in its frame-src. */
49
+ export declare const VIDEO_ORIGINS: readonly ["https://www.youtube-nocookie.com", "https://player.vimeo.com"];
50
+ export declare const parts: {
51
+ readonly heading: z.ZodObject<{
52
+ text: z.ZodString;
53
+ size: z.ZodDefault<z.ZodEnum<{
54
+ display: "display";
55
+ small: "small";
56
+ medium: "medium";
57
+ large: "large";
58
+ page: "page";
59
+ }>>;
60
+ font: z.ZodOptional<z.ZodEnum<{
61
+ heading: "heading";
62
+ text: "text";
63
+ }>>;
64
+ }, z.core.$strip>;
65
+ readonly text: z.ZodObject<{
66
+ text: z.ZodString;
67
+ font: z.ZodOptional<z.ZodEnum<{
68
+ heading: "heading";
69
+ text: "text";
70
+ }>>;
71
+ }, z.core.$strip>;
72
+ readonly photo: z.ZodObject<{
73
+ image: z.ZodOptional<z.ZodObject<{
74
+ src: z.ZodString;
75
+ alt: z.ZodString;
76
+ caption: z.ZodOptional<z.ZodString>;
77
+ }, z.core.$strip>>;
78
+ shape: z.ZodDefault<z.ZodEnum<{
79
+ square: "square";
80
+ landscape: "landscape";
81
+ portrait: "portrait";
82
+ "as-taken": "as-taken";
83
+ }>>;
84
+ }, z.core.$strip>;
85
+ readonly button: z.ZodObject<{
86
+ label: z.ZodString;
87
+ href: z.ZodString;
88
+ style: z.ZodDefault<z.ZodString>;
89
+ }, z.core.$strip>;
90
+ readonly list: z.ZodObject<{
91
+ items: z.ZodArray<z.ZodString>;
92
+ style: z.ZodDefault<z.ZodEnum<{
93
+ checks: "checks";
94
+ bullets: "bullets";
95
+ numbers: "numbers";
96
+ }>>;
97
+ }, z.core.$strip>;
98
+ readonly items: z.ZodObject<{
99
+ items: z.ZodArray<z.ZodObject<{
100
+ title: z.ZodString;
101
+ text: z.ZodOptional<z.ZodString>;
102
+ }, z.core.$strip>>;
103
+ font: z.ZodOptional<z.ZodEnum<{
104
+ heading: "heading";
105
+ text: "text";
106
+ }>>;
107
+ numbered: z.ZodDefault<z.ZodBoolean>;
108
+ lines: z.ZodDefault<z.ZodBoolean>;
109
+ }, z.core.$strip>;
110
+ readonly quote: z.ZodObject<{
111
+ text: z.ZodString;
112
+ who: z.ZodOptional<z.ZodString>;
113
+ font: z.ZodOptional<z.ZodEnum<{
114
+ heading: "heading";
115
+ text: "text";
116
+ }>>;
117
+ }, z.core.$strip>;
118
+ readonly stats: z.ZodObject<{
119
+ items: z.ZodArray<z.ZodObject<{
120
+ value: z.ZodString;
121
+ label: z.ZodString;
122
+ }, z.core.$strip>>;
123
+ }, z.core.$strip>;
124
+ readonly price: z.ZodObject<{
125
+ amount: z.ZodString;
126
+ per: z.ZodOptional<z.ZodString>;
127
+ note: z.ZodOptional<z.ZodString>;
128
+ }, z.core.$strip>;
129
+ readonly gallery: z.ZodObject<{
130
+ images: z.ZodArray<z.ZodObject<{
131
+ src: z.ZodString;
132
+ alt: z.ZodString;
133
+ caption: z.ZodOptional<z.ZodString>;
134
+ }, z.core.$strip>>;
135
+ columns: z.ZodDefault<z.ZodEnum<{
136
+ 2: "2";
137
+ 3: "3";
138
+ 4: "4";
139
+ }>>;
140
+ }, z.core.$strip>;
141
+ readonly slideshow: z.ZodObject<{
142
+ images: z.ZodArray<z.ZodObject<{
143
+ src: z.ZodString;
144
+ alt: z.ZodString;
145
+ caption: z.ZodOptional<z.ZodString>;
146
+ }, z.core.$strip>>;
147
+ seconds: z.ZodDefault<z.ZodNumber>;
148
+ }, z.core.$strip>;
149
+ readonly space: z.ZodObject<{
150
+ size: z.ZodDefault<z.ZodEnum<{
151
+ small: "small";
152
+ medium: "medium";
153
+ large: "large";
154
+ }>>;
155
+ }, z.core.$strip>;
156
+ readonly table: z.ZodObject<{
157
+ columns: z.ZodArray<z.ZodString>;
158
+ rows: z.ZodArray<z.ZodObject<{
159
+ label: z.ZodString;
160
+ cells: z.ZodArray<z.ZodString>;
161
+ }, z.core.$strip>>;
162
+ highlight: z.ZodOptional<z.ZodNumber>;
163
+ }, z.core.$strip>;
164
+ readonly icon: z.ZodObject<{
165
+ name: z.ZodEnum<{
166
+ card: "card";
167
+ check: "check";
168
+ phone: "phone";
169
+ file: "file";
170
+ home: "home";
171
+ play: "play";
172
+ x: "x";
173
+ search: "search";
174
+ link: "link";
175
+ calendar: "calendar";
176
+ clock: "clock";
177
+ users: "users";
178
+ user: "user";
179
+ chat: "chat";
180
+ mail: "mail";
181
+ "map-pin": "map-pin";
182
+ chart: "chart";
183
+ folder: "folder";
184
+ invoice: "invoice";
185
+ shield: "shield";
186
+ lock: "lock";
187
+ star: "star";
188
+ heart: "heart";
189
+ bolt: "bolt";
190
+ gear: "gear";
191
+ building: "building";
192
+ truck: "truck";
193
+ wrench: "wrench";
194
+ leaf: "leaf";
195
+ sun: "sun";
196
+ globe: "globe";
197
+ camera: "camera";
198
+ "arrow-right": "arrow-right";
199
+ plus: "plus";
200
+ minus: "minus";
201
+ info: "info";
202
+ warning: "warning";
203
+ list: "list";
204
+ grid: "grid";
205
+ tag: "tag";
206
+ award: "award";
207
+ }>;
208
+ size: z.ZodDefault<z.ZodEnum<{
209
+ small: "small";
210
+ medium: "medium";
211
+ large: "large";
212
+ }>>;
213
+ }, z.core.$strip>;
214
+ readonly logos: z.ZodObject<{
215
+ title: z.ZodOptional<z.ZodString>;
216
+ images: z.ZodArray<z.ZodObject<{
217
+ src: z.ZodString;
218
+ alt: z.ZodString;
219
+ }, z.core.$strip>>;
220
+ }, z.core.$strip>;
221
+ readonly video: z.ZodObject<{
222
+ url: z.ZodOptional<z.ZodString>;
223
+ title: z.ZodString;
224
+ }, z.core.$strip>;
225
+ };
226
+ export type PartType = keyof typeof parts;
227
+ /** A section of the business's own: columns of parts, laid out as chosen, in the site's design. */
228
+ export declare const own: z.ZodObject<{
229
+ heading: z.ZodOptional<z.ZodString>;
230
+ intro: z.ZodOptional<z.ZodString>;
231
+ layout: z.ZodDefault<z.ZodEnum<{
232
+ one: "one";
233
+ two: "two";
234
+ three: "three";
235
+ four: "four";
236
+ "wide-left": "wide-left";
237
+ "wide-right": "wide-right";
238
+ }>>;
239
+ align: z.ZodDefault<z.ZodEnum<{
240
+ left: "left";
241
+ center: "center";
242
+ }>>;
243
+ tone: z.ZodDefault<z.ZodEnum<{
244
+ soft: "soft";
245
+ plain: "plain";
246
+ brand: "brand";
247
+ dark: "dark";
248
+ }>>;
249
+ shape: z.ZodDefault<z.ZodEnum<{
250
+ card: "card";
251
+ band: "band";
252
+ }>>;
253
+ spacing: z.ZodDefault<z.ZodEnum<{
254
+ normal: "normal";
255
+ roomy: "roomy";
256
+ tight: "tight";
257
+ }>>;
258
+ background: z.ZodOptional<z.ZodObject<{
259
+ src: z.ZodString;
260
+ alt: z.ZodString;
261
+ }, z.core.$strip>>;
262
+ columns: z.ZodArray<z.ZodObject<{
263
+ card: z.ZodOptional<z.ZodBoolean>;
264
+ end: z.ZodOptional<z.ZodBoolean>;
265
+ place: z.ZodOptional<z.ZodEnum<{
266
+ top: "top";
267
+ middle: "middle";
268
+ bottom: "bottom";
269
+ }>>;
270
+ parts: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>, ...z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>[]], "type">>;
271
+ }, z.core.$strip>>;
272
+ }, z.core.$strip>;
38
273
  /** Every section the kit draws, by the type a block names. */
39
274
  export declare const sections: {
40
275
  readonly gallery: z.ZodObject<{
@@ -66,11 +301,58 @@ export declare const sections: {
66
301
  }, z.core.$strip>>;
67
302
  shape: z.ZodDefault<z.ZodEnum<{
68
303
  square: "square";
69
- wide: "wide";
70
304
  landscape: "landscape";
305
+ wide: "wide";
71
306
  }>>;
72
307
  seconds: z.ZodDefault<z.ZodNumber>;
73
308
  }, z.core.$strip>;
309
+ readonly own: z.ZodObject<{
310
+ heading: z.ZodOptional<z.ZodString>;
311
+ intro: z.ZodOptional<z.ZodString>;
312
+ layout: z.ZodDefault<z.ZodEnum<{
313
+ one: "one";
314
+ two: "two";
315
+ three: "three";
316
+ four: "four";
317
+ "wide-left": "wide-left";
318
+ "wide-right": "wide-right";
319
+ }>>;
320
+ align: z.ZodDefault<z.ZodEnum<{
321
+ left: "left";
322
+ center: "center";
323
+ }>>;
324
+ tone: z.ZodDefault<z.ZodEnum<{
325
+ soft: "soft";
326
+ plain: "plain";
327
+ brand: "brand";
328
+ dark: "dark";
329
+ }>>;
330
+ shape: z.ZodDefault<z.ZodEnum<{
331
+ card: "card";
332
+ band: "band";
333
+ }>>;
334
+ spacing: z.ZodDefault<z.ZodEnum<{
335
+ normal: "normal";
336
+ roomy: "roomy";
337
+ tight: "tight";
338
+ }>>;
339
+ background: z.ZodOptional<z.ZodObject<{
340
+ src: z.ZodString;
341
+ alt: z.ZodString;
342
+ }, z.core.$strip>>;
343
+ columns: z.ZodArray<z.ZodObject<{
344
+ card: z.ZodOptional<z.ZodBoolean>;
345
+ end: z.ZodOptional<z.ZodBoolean>;
346
+ place: z.ZodOptional<z.ZodEnum<{
347
+ top: "top";
348
+ middle: "middle";
349
+ bottom: "bottom";
350
+ }>>;
351
+ parts: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>, ...z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>[]], "type">>;
352
+ }, z.core.$strip>>;
353
+ }, z.core.$strip>;
74
354
  };
75
355
  export type KitSectionType = keyof typeof sections;
76
356
  export declare const isKitSection: (type: string) => type is KitSectionType;
357
+ /** Ready-made sections of the business's own, to add and then edit like any other (./presets.ts). */
358
+ export { presets, type OwnBlock, type Preset } from "./presets.ts";
@@ -2,8 +2,11 @@
2
2
  // its own (README, "Sections"): each one's fields here, drawn by KitSection (../next/sections.tsx) and
3
3
  // styled by sections.css, themed by the site's Look. A site takes them all into its blocks
4
4
  // (`defineSite({ ...blocks, ...sections })`); one of its own by the same name is kept over the kit's.
5
+ import { buttonStyleName } from "../content/look.js";
5
6
  import { z } from "zod";
6
- import { image, md, text } from "../content/index.js";
7
+ import { href, image, md, text } from "../content/index.js";
8
+ import { ICON_NAMES } from "./icons.js";
9
+ export { ICON_NAMES, ICONS } from "./icons.js";
7
10
  /** Photos in a grid, each with a caption if it has one: their work, the place, the team at it. */
8
11
  export const gallery = z
9
12
  .object({
@@ -31,6 +34,157 @@ export const slideshow = z
31
34
  seconds: z.number().int().min(3).max(15).default(5).meta({ title: "Seconds on each photo" }),
32
35
  })
33
36
  .meta({ title: "Slideshow", description: "Photos one at a time, fading from one to the next" });
37
+ /**
38
+ * A YouTube or Vimeo address as the privacy-friendly player it's shown in (YouTube's no-cookie one,
39
+ * Vimeo's with tracking off), or null for any other address. Watch, share, Shorts and embed
40
+ * addresses all work.
41
+ */
42
+ export function videoEmbed(url) {
43
+ const yt = /^https:\/\/(?:www\.|m\.)?(?:youtube\.com\/(?:watch\?(?:[^#]*&)?v=|shorts\/|embed\/|live\/)|youtu\.be\/)([\w-]{11})(?![\w-])/.exec(url.trim());
44
+ if (yt)
45
+ return { src: `${YOUTUBE}/embed/${yt[1]}`, origin: YOUTUBE };
46
+ const vm = /^https:\/\/(?:www\.)?(?:vimeo\.com\/|player\.vimeo\.com\/video\/)(\d+)(?:\/([\da-f]+))?(?![\w])/.exec(url.trim());
47
+ if (vm)
48
+ return { src: `${VIMEO}/video/${vm[1]}?${vm[2] ? `h=${vm[2]}&` : ""}dnt=1`, origin: VIMEO };
49
+ return null;
50
+ }
51
+ const YOUTUBE = "https://www.youtube-nocookie.com";
52
+ const VIMEO = "https://player.vimeo.com";
53
+ /** Where a video part's players come from: a page showing one allows them in its frame-src. */
54
+ export const VIDEO_ORIGINS = [YOUTUBE, VIMEO];
55
+ const VIDEO_HELP = "A YouTube or Vimeo address, like https://www.youtube.com/watch?v=… or https://vimeo.com/…";
56
+ // The same test as videoEmbed's, as a pattern CalOS can check with from the JSON Schema alone.
57
+ const videoUrl = z
58
+ .string()
59
+ .trim()
60
+ .regex(/^https:\/\/((www\.|m\.)?(youtube\.com\/(watch\?([^#]*&)?v=|shorts\/|embed\/|live\/)|youtu\.be\/)[\w-]{11}([^\w-].*)?|(www\.)?(vimeo\.com\/|player\.vimeo\.com\/video\/)\d+([^\w].*)?)$/, { error: VIDEO_HELP })
61
+ .meta({ title: "Video address", "x-error": VIDEO_HELP });
62
+ /** A table's cell: plain words, or "yes" / "no", drawn as a tick and a dash. */
63
+ const cell = z.string().trim().max(120);
64
+ /**
65
+ * The parts a section of the business's own is put together from in CalOS: each drawn in the site's
66
+ * own type, buttons and widths (the classes it gives KitSection), so what's built looks like the
67
+ * site, not a template. A fixed set, no colours or fonts of their own.
68
+ */
69
+ /** Which of the Look's fonts a part's words are in, where it isn't the usual (a heading in the text's font). */
70
+ const font = z.enum(["heading", "text"]).optional().meta({ title: "Font", "x-labels": { heading: "Headings' font", text: "Text font" } });
71
+ const photo = image.extend({ caption: text.optional().meta({ title: "Caption" }) });
72
+ export const parts = {
73
+ heading: z
74
+ .object({
75
+ text,
76
+ size: z
77
+ .enum(["display", "page", "large", "medium", "small"])
78
+ .default("medium")
79
+ .meta({ title: "Size", "x-labels": { display: "Display (the largest)", page: "Page heading", large: "Section heading", medium: "Medium", small: "Small" } }),
80
+ font,
81
+ })
82
+ .meta({ title: "Heading" }),
83
+ text: z.object({ text: md, font }).meta({ title: "Text" }),
84
+ photo: z.object({ image: photo.optional(), shape: z.enum(["as-taken", "landscape", "square", "portrait"]).default("as-taken").meta({ title: "Shape" }) }).meta({ title: "Photo" }),
85
+ button: z
86
+ .object({
87
+ label: text.meta({ title: "Label" }),
88
+ href,
89
+ // The theme's Main, Accent or Quiet, or one of the business's own button styles (Look › Buttons), by its slug.
90
+ style: buttonStyleName.default("main").meta({ title: "Style", "x-ui": "button-style" }),
91
+ })
92
+ .meta({ title: "Button" }),
93
+ list: z.object({ items: z.array(text).min(1).max(20).meta({ title: "Items" }), style: z.enum(["bullets", "checks", "numbers"]).default("bullets").meta({ title: "Style" }) }).meta({ title: "List" }),
94
+ // Things with a name and a line about each: services, steps, what's included. Numbered (01, 02…) or
95
+ // not, with a rule between them or none.
96
+ items: z
97
+ .object({
98
+ items: z
99
+ .array(z.object({ title: text.meta({ title: "Title" }), text: text.optional().meta({ title: "Line about it" }) }).meta({ title: "Item" }))
100
+ .min(1)
101
+ .max(12)
102
+ .meta({ title: "Items" }),
103
+ // Their titles in the headings' font, or the text's (a sans title under a serif heading).
104
+ font,
105
+ numbered: z.boolean().default(true).meta({ title: "Numbered (01, 02…)" }),
106
+ lines: z.boolean().default(true).meta({ title: "Lines between them" }),
107
+ })
108
+ .meta({ title: "Items with titles", description: "Services or steps: each a title and a line about it, numbered or not" }),
109
+ quote: z.object({ text: text.meta({ title: "Quote" }), who: text.optional().meta({ title: "Who said it" }), font }).meta({ title: "Quote" }),
110
+ stats: z.object({ items: z.array(z.object({ value: text.meta({ title: "Number" }), label: text.meta({ title: "What it is" }) })).min(1).max(6).meta({ title: "Facts" }) }).meta({ title: "Facts in a row", description: "8 guests · 4 cabins · 2 baths" }),
111
+ price: z.object({ amount: text.meta({ title: "Price" }), per: text.optional().meta({ title: "Per" }), note: text.optional().meta({ title: "Note" }) }).meta({ title: "Price" }),
112
+ gallery: z.object({ images: z.array(photo).min(1).max(30).meta({ title: "Photos" }), columns: z.enum(["2", "3", "4"]).default("3").meta({ title: "Photos across" }) }).meta({ title: "Photos in a grid" }),
113
+ slideshow: z.object({ images: z.array(photo).min(2).max(20).meta({ title: "Photos" }), seconds: z.number().int().min(3).max(15).default(5).meta({ title: "Seconds on each photo" }) }).meta({ title: "Slideshow" }),
114
+ space: z.object({ size: z.enum(["small", "medium", "large"]).default("medium").meta({ title: "Size" }) }).meta({ title: "Space" }),
115
+ // Rows of the same few things compared: plans, packages, what's included. The first column is the
116
+ // rows' labels (its heading may be ""); a cell "yes" or "no" is a tick or a dash.
117
+ table: z
118
+ .object({
119
+ columns: z.array(z.string().trim().max(60)).min(2).max(5).meta({ title: "Column headings", description: "The first is over the rows' labels and may be left empty" }),
120
+ rows: z
121
+ .array(z.object({ label: text.meta({ title: "Row" }), cells: z.array(cell).max(4).meta({ title: "Cells", description: "One for each column after the first: words, or yes or no for a tick or a dash" }) }).meta({ title: "Row" }))
122
+ .min(1)
123
+ .max(20)
124
+ .meta({ title: "Rows" }),
125
+ highlight: z.number().int().min(1).max(4).optional().meta({ title: "Column to pick out", description: "Which column (counting from 1 after the rows' labels) to pick out" }),
126
+ })
127
+ .meta({ title: "Table", description: "Rows compared across a few columns: plans, packages, what's included" }),
128
+ icon: z.object({ name: z.enum(ICON_NAMES).meta({ title: "Icon", "x-ui": "icon" }), size: z.enum(["small", "medium", "large"]).default("medium").meta({ title: "Size" }) }).meta({ title: "Icon" }),
129
+ logos: z
130
+ .object({ title: text.optional().meta({ title: "Line above" }), images: z.array(image).min(2).max(12).meta({ title: "Logos" }) })
131
+ .meta({ title: "Logos", description: "A row of the logos of clients, partners or memberships" }),
132
+ // Optional while the business finds its address: a quiet box in CalOS's preview, nothing on the site.
133
+ video: z.object({ url: videoUrl.optional(), title: text.meta({ title: "What the video is", description: "Said to someone using a screen reader" }) }).meta({ title: "Video", description: "A YouTube or Vimeo video" }),
134
+ };
135
+ /** What a part's own fields can't say alone: a table's rows each have a cell for every column, its headings are named, and the column it picks out is one of them. */
136
+ function partProblems(p, ctx) {
137
+ if (p.type !== "table")
138
+ return;
139
+ const t = p;
140
+ const n = t.columns.length - 1;
141
+ t.columns.forEach((c, i) => {
142
+ if (i > 0 && !c)
143
+ ctx.addIssue({ code: "custom", path: ["columns", i], message: "A column needs a heading" });
144
+ });
145
+ t.rows.forEach((r, i) => {
146
+ if (r.cells.length !== n)
147
+ ctx.addIssue({ code: "custom", path: ["rows", i, "cells"], message: `One cell for each of the ${n} column${n === 1 ? "" : "s"} after the first` });
148
+ });
149
+ if (t.highlight !== undefined && t.highlight > n)
150
+ ctx.addIssue({ code: "custom", path: ["highlight"], message: `The table has ${n} column${n === 1 ? "" : "s"} after the first` });
151
+ }
152
+ const part = z.discriminatedUnion("type", Object.keys(parts).map((type) => z.object({ type: z.literal(type) }).extend(parts[type].shape).meta(parts[type].meta()).superRefine(partProblems)));
153
+ /** A section of the business's own: columns of parts, laid out as chosen, in the site's design. */
154
+ export const own = z
155
+ .object({
156
+ // Its heading area: over its columns, in the site's section heading style.
157
+ heading: text.optional().meta({ title: "Heading" }),
158
+ intro: md.optional().meta({ title: "Intro" }),
159
+ layout: z.enum(["one", "two", "three", "four", "wide-left", "wide-right"]).default("one").meta({ title: "Columns" }),
160
+ align: z.enum(["left", "center"]).default("left").meta({ title: "Align" }),
161
+ // Plain: the page's own background; soft: a quiet tint of its text colour; brand: the brand
162
+ // colour; dark: the site's ink, words light on it. A photo behind (background) is under a scrim,
163
+ // words light over it, whatever the tone.
164
+ tone: z.enum(["plain", "soft", "brand", "dark"]).default("plain").meta({ title: "Background" }),
165
+ // On soft, brand or dark: a card inset within the content's width, in the theme's corners, or a
166
+ // band the page's full width. Plain has nothing to shape.
167
+ shape: z.enum(["card", "band"]).default("card").meta({ title: "Shape" }),
168
+ spacing: z.enum(["tight", "normal", "roomy"]).default("normal").meta({ title: "Space above and below" }),
169
+ background: image.optional().meta({ title: "Photo behind" }),
170
+ columns: z
171
+ .array(z
172
+ .object({
173
+ card: z.boolean().optional().meta({ title: "Drawn as a card" }),
174
+ // Against the far edge: a row's second half ("Where we work … Philadelphia, PA / Princeton, NJ").
175
+ end: z.boolean().optional().meta({ title: "Against the right edge" }),
176
+ // Where its parts sit when a column beside it is taller: words at the foot of a tall photo.
177
+ place: z.enum(["top", "middle", "bottom"]).optional().meta({ title: "Up and down", "x-labels": { top: "At the top", middle: "In the middle", bottom: "At the bottom" } }),
178
+ parts: z.array(part).max(12).meta({ title: "Parts" }),
179
+ })
180
+ .meta({ title: "Column" }))
181
+ .min(1)
182
+ .max(4)
183
+ .meta({ title: "Columns' parts" }),
184
+ })
185
+ .meta({ title: "Your own section", description: "Put together from headings, text, photos, buttons and more, in the site's design" });
34
186
  /** Every section the kit draws, by the type a block names. */
35
- export const sections = { gallery, slideshow };
187
+ export const sections = { gallery, slideshow, own };
36
188
  export const isKitSection = (type) => Object.prototype.hasOwnProperty.call(sections, type);
189
+ /** Ready-made sections of the business's own, to add and then edit like any other (./presets.ts). */
190
+ export { presets } from "./presets.js";
@@ -0,0 +1,13 @@
1
+ import type { z } from "zod";
2
+ import type { own } from "./index.ts";
3
+ /** A section of the business's own, as content holds it. */
4
+ export type OwnBlock = {
5
+ type: "own";
6
+ } & z.input<typeof own>;
7
+ export type Preset = {
8
+ key: string;
9
+ name: string;
10
+ description: string;
11
+ block: OwnBlock;
12
+ };
13
+ export declare const presets: Preset[];