@valbuild/core 0.128.0 → 0.130.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 (36) hide show
  1. package/CHANGELOG.md +210 -0
  2. package/dist/declarations/src/index.d.ts +1 -1
  3. package/dist/declarations/src/preview.d.ts +65 -8
  4. package/dist/declarations/src/render.d.ts +26 -0
  5. package/dist/declarations/src/schema/array.d.ts +8 -5
  6. package/dist/declarations/src/schema/boolean.d.ts +8 -6
  7. package/dist/declarations/src/schema/code.d.ts +8 -6
  8. package/dist/declarations/src/schema/color.d.ts +8 -6
  9. package/dist/declarations/src/schema/date.d.ts +8 -6
  10. package/dist/declarations/src/schema/datetime.d.ts +8 -6
  11. package/dist/declarations/src/schema/discriminatedUnion.d.ts +8 -5
  12. package/dist/declarations/src/schema/enum.d.ts +8 -6
  13. package/dist/declarations/src/schema/file.d.ts +8 -6
  14. package/dist/declarations/src/schema/image.d.ts +8 -6
  15. package/dist/declarations/src/schema/index.d.ts +29 -1
  16. package/dist/declarations/src/schema/keyOf.d.ts +8 -6
  17. package/dist/declarations/src/schema/literal.d.ts +8 -6
  18. package/dist/declarations/src/schema/locale.d.ts +1 -2
  19. package/dist/declarations/src/schema/number.d.ts +8 -6
  20. package/dist/declarations/src/schema/object.d.ts +8 -5
  21. package/dist/declarations/src/schema/record.d.ts +8 -5
  22. package/dist/declarations/src/schema/richtext.d.ts +8 -6
  23. package/dist/declarations/src/schema/route.d.ts +8 -6
  24. package/dist/declarations/src/schema/settings.d.ts +10 -3
  25. package/dist/declarations/src/schema/string.d.ts +8 -11
  26. package/dist/declarations/src/source/settings.d.ts +117 -0
  27. package/dist/{index-c3ae637d.cjs.dev.js → index-27d57011.cjs.dev.js} +3451 -3142
  28. package/dist/{index-db41de3c.esm.js → index-71a59a88.esm.js} +3449 -3143
  29. package/dist/{index-053ccde2.cjs.prod.js → index-9e372bd7.cjs.prod.js} +3451 -3142
  30. package/dist/valbuild-core.cjs.dev.js +4 -1
  31. package/dist/valbuild-core.cjs.prod.js +4 -1
  32. package/dist/valbuild-core.esm.js +1 -1
  33. package/package.json +1 -1
  34. package/patch/dist/valbuild-core-patch.cjs.dev.js +1 -1
  35. package/patch/dist/valbuild-core-patch.cjs.prod.js +1 -1
  36. package/patch/dist/valbuild-core-patch.esm.js +2 -2
@@ -4,7 +4,7 @@ import { CustomValidateFunction, Schema, SchemaAssertResult, SerializedSchema }
4
4
  import { ModulePath, SourcePath } from "../val/index.js";
5
5
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
6
6
  import { ValModule } from "../index.js";
7
- import { ItemPreviewInput, PreviewItem, ReifiedPreview } from "../preview.js";
7
+ import { ItemPreviewInput, PreviewItem } from "../preview.js";
8
8
  import { FieldRender } from "../render.js";
9
9
  import { FilesetEntryMetadata } from "./fileset.js";
10
10
  export type FileOptions = {
@@ -40,10 +40,13 @@ export declare class FileSchema<Src extends FileSource | null> extends Schema<Sr
40
40
  /**
41
41
  * Describe this field.
42
42
  *
43
- * The description is shown next to the field's label in the Val editor, so
44
- * it is where you say what an editor needs to know but the field name cannot
45
- * carry. It also travels in the serialized schema, which is what the AI
46
- * assistant and the MCP tools read.
43
+ * The description is INPUT HELP: it is shown where this field's value is
44
+ * entered — beside its input in the Val editor, and for a record's key
45
+ * schema in every form that asks for a key — so it is where you say what an
46
+ * editor needs to know to fill it in RIGHT, which the field name cannot
47
+ * carry. It is not a name for the value: that is `.preview(...)`, and it is
48
+ * read somewhere else. The description also travels in the serialized
49
+ * schema, which is what the AI assistant and the MCP tools read.
47
50
  *
48
51
  * Pass `null` to clear a description set earlier.
49
52
  *
@@ -145,7 +148,6 @@ export declare class FileSchema<Src extends FileSource | null> extends Schema<Sr
145
148
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
146
149
  protected declaresItemPreview(): boolean;
147
150
  protected executeSerialize(): SerializedSchema;
148
- protected executePreview(): ReifiedPreview;
149
151
  }
150
152
  /**
151
153
  * A file picked from a gallery. Its mime type lives in the gallery, so the
@@ -3,7 +3,7 @@ import { GalleryImageSource, ImageSource } from "../source/media.js";
3
3
  import { ModulePath, SourcePath } from "../val/index.js";
4
4
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
5
5
  import { ValModule } from "../index.js";
6
- import { ItemPreviewInput, PreviewItem, ReifiedPreview } from "../preview.js";
6
+ import { ItemPreviewInput, PreviewItem } from "../preview.js";
7
7
  import { FieldRender } from "../render.js";
8
8
  import { AltSource, ImagesetEntryMetadata } from "./imageset.js";
9
9
  /**
@@ -85,10 +85,13 @@ export declare class ImageSchema<Src extends ImageSource | null> extends Schema<
85
85
  /**
86
86
  * Describe this field.
87
87
  *
88
- * The description is shown next to the field's label in the Val editor, so
89
- * it is where you say what an editor needs to know but the field name cannot
90
- * carry. It also travels in the serialized schema, which is what the AI
91
- * assistant and the MCP tools read.
88
+ * The description is INPUT HELP: it is shown where this field's value is
89
+ * entered — beside its input in the Val editor, and for a record's key
90
+ * schema in every form that asks for a key — so it is where you say what an
91
+ * editor needs to know to fill it in RIGHT, which the field name cannot
92
+ * carry. It is not a name for the value: that is `.preview(...)`, and it is
93
+ * read somewhere else. The description also travels in the serialized
94
+ * schema, which is what the AI assistant and the MCP tools read.
92
95
  *
93
96
  * Pass `null` to clear a description set earlier.
94
97
  *
@@ -219,7 +222,6 @@ export declare class ImageSchema<Src extends ImageSource | null> extends Schema<
219
222
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
220
223
  protected declaresItemPreview(): boolean;
221
224
  protected executeSerialize(): SerializedSchema;
222
- protected executePreview(): ReifiedPreview;
223
225
  }
224
226
  /**
225
227
  * An image picked from a gallery. Its dimensions and mime type live in the
@@ -270,13 +270,41 @@ export declare abstract class Schema<Src extends SelectorSource> {
270
270
  abstract hidden(isHidden?: boolean): Schema<Src>;
271
271
  protected abstract executeSerialize(): SerializedSchema;
272
272
  /**
273
+ * This value's preview, and every preview below it.
274
+ *
275
+ * The default is the whole of it for a LEAF: a leaf has nothing below it, so
276
+ * all it can contribute is {@link PreviewNode.self}. Containers override this
277
+ * and recurse; object and discriminated union add nothing of their own beyond
278
+ * the self they inherit here, and array and record add `rows`.
279
+ *
273
280
  * @param scope Which paths the caller needs a preview for. Absent means the
274
281
  * whole module, which is what every caller passed before scoping existed.
275
282
  * See {@link PreviewScope}: a container prunes recursion where nothing is
276
283
  * wanted, and previews a WINDOW when its own path is not wanted but some of
277
284
  * its items are — which is the single-visible-row case.
285
+ * @param selfIsReifiedByParent Set by an array or record on its DIRECT items,
286
+ * and by nothing else. Such an item's preview is the same closure the
287
+ * container is already running into {@link PreviewNode.rows}, so emitting a
288
+ * `self` for it as well would call the user's closure twice per row — which
289
+ * it did, and which the scoped-preview tests caught by counting. The flag
290
+ * does not travel further down: a grandchild is nobody's row.
291
+ */
292
+ protected executePreview(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: PreviewScope, selfIsReifiedByParent?: boolean): ReifiedPreview;
293
+ /**
294
+ * THIS value as a preview, at its own path — `{ self }` and nothing else.
295
+ *
296
+ * Every schema can produce one, which is the point: before this existed a
297
+ * value's preview was reified only by its CONTAINER, so a value with no
298
+ * container had none. A module root is exactly that, and `.preview(...)` on
299
+ * a module's own schema was therefore dead code — the studio showed the file
300
+ * name and nothing a developer wrote could change it. So is any field of an
301
+ * object, which reifies no rows.
302
+ *
303
+ * Gated on {@link PreviewScope.wants} rather than `wantsUnder`: a caller
304
+ * asking about a CONTAINER wants its rows, and computing a self for every
305
+ * descendant on the way past would run each item's closure twice.
278
306
  */
279
- protected abstract executePreview(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: PreviewScope): ReifiedPreview;
307
+ protected executeSelfPreview(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: PreviewScope): ReifiedPreview;
280
308
  /**
281
309
  * This value AS A PREVIEW — what a container's row, a reference dropdown or
282
310
  * a search hit shows for it. Runs the schema's own `preview` closure;
@@ -5,7 +5,7 @@ import { Source, SourceObject } from "../source/index.js";
5
5
  import { SourcePath } from "../val/index.js";
6
6
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
7
7
  import { RawString } from "./string.js";
8
- import { ItemPreviewInput, PreviewItem, ReifiedPreview } from "../preview.js";
8
+ import { ItemPreviewInput, PreviewItem } from "../preview.js";
9
9
  import { FieldRender } from "../render.js";
10
10
  export type SerializedKeyOfSchema = {
11
11
  type: "keyOf";
@@ -45,10 +45,13 @@ export declare class KeyOfSchema<Sel extends GenericSelector<SourceObject>, Src
45
45
  /**
46
46
  * Describe this field.
47
47
  *
48
- * The description is shown next to the field's label in the Val editor, so
49
- * it is where you say what an editor needs to know but the field name cannot
50
- * carry. It also travels in the serialized schema, which is what the AI
51
- * assistant and the MCP tools read.
48
+ * The description is INPUT HELP: it is shown where this field's value is
49
+ * entered — beside its input in the Val editor, and for a record's key
50
+ * schema in every form that asks for a key — so it is where you say what an
51
+ * editor needs to know to fill it in RIGHT, which the field name cannot
52
+ * carry. It is not a name for the value: that is `.preview(...)`, and it is
53
+ * read somewhere else. The description also travels in the serialized
54
+ * schema, which is what the AI assistant and the MCP tools read.
52
55
  *
53
56
  * Pass `null` to clear a description set earlier.
54
57
  *
@@ -117,7 +120,6 @@ export declare class KeyOfSchema<Sel extends GenericSelector<SourceObject>, Src
117
120
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
118
121
  protected declaresItemPreview(): boolean;
119
122
  protected executeSerialize(): SerializedSchema;
120
- protected executePreview(): ReifiedPreview;
121
123
  }
122
124
  export declare const keyOf: <Src extends GenericSelector<SourceObject> & ValModuleBrand>(valModule: Src) => KeyOfSchema<Src, KeyOfSelector<Src>>;
123
125
  export {};
@@ -1,5 +1,5 @@
1
1
  import { CustomValidateFunction, Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
- import { ItemPreviewInput, PreviewItem, ReifiedPreview } from "../preview.js";
2
+ import { ItemPreviewInput, PreviewItem } from "../preview.js";
3
3
  import { FieldRender } from "../render.js";
4
4
  import { SourcePath } from "../val/index.js";
5
5
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
@@ -31,10 +31,13 @@ export declare class LiteralSchema<Src extends string | null> extends Schema<Src
31
31
  /**
32
32
  * Describe this field.
33
33
  *
34
- * The description is shown next to the field's label in the Val editor, so
35
- * it is where you say what an editor needs to know but the field name cannot
36
- * carry. It also travels in the serialized schema, which is what the AI
37
- * assistant and the MCP tools read.
34
+ * The description is INPUT HELP: it is shown where this field's value is
35
+ * entered — beside its input in the Val editor, and for a record's key
36
+ * schema in every form that asks for a key — so it is where you say what an
37
+ * editor needs to know to fill it in RIGHT, which the field name cannot
38
+ * carry. It is not a name for the value: that is `.preview(...)`, and it is
39
+ * read somewhere else. The description also travels in the serialized
40
+ * schema, which is what the AI assistant and the MCP tools read.
38
41
  *
39
42
  * Pass `null` to clear a description set earlier.
40
43
  *
@@ -99,6 +102,5 @@ export declare class LiteralSchema<Src extends string | null> extends Schema<Src
99
102
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
100
103
  protected declaresItemPreview(): boolean;
101
104
  protected executeSerialize(): SerializedSchema;
102
- protected executePreview(): ReifiedPreview;
103
105
  }
104
106
  export declare const literal: <T extends string>(value: T) => LiteralSchema<T>;
@@ -1,5 +1,5 @@
1
1
  import { Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
- import { ItemPreviewInput, PreviewItem, ReifiedPreview } from "../preview.js";
2
+ import { ItemPreviewInput, PreviewItem } from "../preview.js";
3
3
  import { FieldRender } from "../render.js";
4
4
  import { SourcePath } from "../val/index.js";
5
5
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
@@ -115,7 +115,6 @@ export declare class LocaleSchema<Src extends string | null> extends Schema<Src>
115
115
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
116
116
  protected declaresItemPreview(): boolean;
117
117
  protected executeSerialize(): SerializedSchema;
118
- protected executePreview(): ReifiedPreview;
119
118
  }
120
119
  /**
121
120
  * Define one of the project's languages.
@@ -1,5 +1,5 @@
1
1
  import { CustomValidateFunction, Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
- import { ItemPreviewInput, PreviewItem, ReifiedPreview } from "../preview.js";
2
+ import { ItemPreviewInput, PreviewItem } from "../preview.js";
3
3
  import { FieldRender } from "../render.js";
4
4
  import { SourcePath } from "../val/index.js";
5
5
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
@@ -33,10 +33,13 @@ export declare class NumberSchema<Src extends number | null> extends Schema<Src>
33
33
  /**
34
34
  * Describe this field.
35
35
  *
36
- * The description is shown next to the field's label in the Val editor, so
37
- * it is where you say what an editor needs to know but the field name cannot
38
- * carry. It also travels in the serialized schema, which is what the AI
39
- * assistant and the MCP tools read.
36
+ * The description is INPUT HELP: it is shown where this field's value is
37
+ * entered — beside its input in the Val editor, and for a record's key
38
+ * schema in every form that asks for a key — so it is where you say what an
39
+ * editor needs to know to fill it in RIGHT, which the field name cannot
40
+ * carry. It is not a name for the value: that is `.preview(...)`, and it is
41
+ * read somewhere else. The description also travels in the serialized
42
+ * schema, which is what the AI assistant and the MCP tools read.
40
43
  *
41
44
  * Pass `null` to clear a description set earlier.
42
45
  *
@@ -115,7 +118,6 @@ export declare class NumberSchema<Src extends number | null> extends Schema<Src>
115
118
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
116
119
  protected declaresItemPreview(): boolean;
117
120
  protected executeSerialize(): SerializedSchema;
118
- protected executePreview(): ReifiedPreview;
119
121
  }
120
122
  export declare const number: (options?: NumberOptions) => NumberSchema<number>;
121
123
  export {};
@@ -53,10 +53,13 @@ export declare class ObjectSchema<Props extends ObjectSchemaProps, Src extends O
53
53
  /**
54
54
  * Describe this field.
55
55
  *
56
- * The description is shown next to the field's label in the Val editor, so
57
- * it is where you say what an editor needs to know but the field name cannot
58
- * carry. It also travels in the serialized schema, which is what the AI
59
- * assistant and the MCP tools read.
56
+ * The description is INPUT HELP: it is shown where this field's value is
57
+ * entered — beside its input in the Val editor, and for a record's key
58
+ * schema in every form that asks for a key — so it is where you say what an
59
+ * editor needs to know to fill it in RIGHT, which the field name cannot
60
+ * carry. It is not a name for the value: that is `.preview(...)`, and it is
61
+ * read somewhere else. The description also travels in the serialized
62
+ * schema, which is what the AI assistant and the MCP tools read.
60
63
  *
61
64
  * Pass `null` to clear a description set earlier.
62
65
  *
@@ -145,7 +148,7 @@ export declare class ObjectSchema<Props extends ObjectSchemaProps, Src extends O
145
148
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
146
149
  protected declaresItemPreview(): boolean;
147
150
  protected executeSerialize(): SerializedSchema;
148
- protected executePreview(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: PreviewScope): ReifiedPreview;
151
+ protected executePreview(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: PreviewScope, selfIsReifiedByParent?: boolean): ReifiedPreview;
149
152
  }
150
153
  export declare const object: <Props extends ObjectSchemaProps>(schema: Props) => ObjectSchema<Props, ObjectSchemaSrcOf<Props>>;
151
154
  export {};
@@ -111,10 +111,13 @@ RO extends boolean = false> extends Schema<Src> {
111
111
  /**
112
112
  * Describe this field.
113
113
  *
114
- * The description is shown next to the field's label in the Val editor, so
115
- * it is where you say what an editor needs to know but the field name cannot
116
- * carry. It also travels in the serialized schema, which is what the AI
117
- * assistant and the MCP tools read.
114
+ * The description is INPUT HELP: it is shown where this field's value is
115
+ * entered — beside its input in the Val editor, and for a record's key
116
+ * schema in every form that asks for a key — so it is where you say what an
117
+ * editor needs to know to fill it in RIGHT, which the field name cannot
118
+ * carry. It is not a name for the value: that is `.preview(...)`, and it is
119
+ * read somewhere else. The description also travels in the serialized
120
+ * schema, which is what the AI assistant and the MCP tools read.
118
121
  *
119
122
  * Pass `null` to clear a description set earlier.
120
123
  *
@@ -333,7 +336,7 @@ RO extends boolean = false> extends Schema<Src> {
333
336
  * defers).
334
337
  */
335
338
  validateJsonEntryContent(path: SourcePath, content: SelectorSource): ValidationErrors;
336
- protected executePreview(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: PreviewScope): ReifiedPreview;
339
+ protected executePreview(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: PreviewScope, selfIsReifiedByParent?: boolean): ReifiedPreview;
337
340
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
338
341
  protected declaresItemPreview(): boolean;
339
342
  /**
@@ -1,5 +1,5 @@
1
1
  import { CustomValidateFunction, Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
- import { ItemPreviewInput, PreviewItem, ReifiedPreview } from "../preview.js";
2
+ import { ItemPreviewInput, PreviewItem } from "../preview.js";
3
3
  import { FieldRender } from "../render.js";
4
4
  import { RichTextSource, RichTextOptions, SerializedRichTextOptions } from "../source/richtext.js";
5
5
  import { SourcePath } from "../val/index.js";
@@ -34,10 +34,13 @@ export declare class RichTextSchema<O extends RichTextOptions, Src extends RichT
34
34
  /**
35
35
  * Describe this field.
36
36
  *
37
- * The description is shown next to the field's label in the Val editor, so
38
- * it is where you say what an editor needs to know but the field name cannot
39
- * carry. It also travels in the serialized schema, which is what the AI
40
- * assistant and the MCP tools read.
37
+ * The description is INPUT HELP: it is shown where this field's value is
38
+ * entered — beside its input in the Val editor, and for a record's key
39
+ * schema in every form that asks for a key — so it is where you say what an
40
+ * editor needs to know to fill it in RIGHT, which the field name cannot
41
+ * carry. It is not a name for the value: that is `.preview(...)`, and it is
42
+ * read somewhere else. The description also travels in the serialized
43
+ * schema, which is what the AI assistant and the MCP tools read.
41
44
  *
42
45
  * Pass `null` to clear a description set earlier.
43
46
  *
@@ -138,7 +141,6 @@ export declare class RichTextSchema<O extends RichTextOptions, Src extends RichT
138
141
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
139
142
  protected declaresItemPreview(): boolean;
140
143
  protected executeSerialize(): SerializedSchema;
141
- protected executePreview(): ReifiedPreview;
142
144
  }
143
145
  export declare const richtext: <O extends RichTextOptions>(options?: O) => RichTextSchema<O, RichTextSource<O>>;
144
146
  export {};
@@ -1,6 +1,6 @@
1
1
  import { Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
2
  import { SourcePath } from "../val/index.js";
3
- import { ItemPreviewInput, PreviewItem, ReifiedPreview } from "../preview.js";
3
+ import { ItemPreviewInput, PreviewItem } from "../preview.js";
4
4
  import { FieldRender } from "../render.js";
5
5
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
6
6
  type RouteOptions = {
@@ -43,10 +43,13 @@ export declare class RouteSchema<Src extends string | null> extends Schema<Src>
43
43
  /**
44
44
  * Describe this field.
45
45
  *
46
- * The description is shown next to the field's label in the Val editor, so
47
- * it is where you say what an editor needs to know but the field name cannot
48
- * carry. It also travels in the serialized schema, which is what the AI
49
- * assistant and the MCP tools read.
46
+ * The description is INPUT HELP: it is shown where this field's value is
47
+ * entered — beside its input in the Val editor, and for a record's key
48
+ * schema in every form that asks for a key — so it is where you say what an
49
+ * editor needs to know to fill it in RIGHT, which the field name cannot
50
+ * carry. It is not a name for the value: that is `.preview(...)`, and it is
51
+ * read somewhere else. The description also travels in the serialized
52
+ * schema, which is what the AI assistant and the MCP tools read.
50
53
  *
51
54
  * Pass `null` to clear a description set earlier.
52
55
  *
@@ -135,7 +138,6 @@ export declare class RouteSchema<Src extends string | null> extends Schema<Src>
135
138
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
136
139
  protected declaresItemPreview(): boolean;
137
140
  protected executeSerialize(): SerializedSchema;
138
- protected executePreview(): ReifiedPreview;
139
141
  }
140
142
  export declare const route: <T extends string>(options?: Record<string, never>) => RouteSchema<T>;
141
143
  export {};
@@ -119,7 +119,7 @@ export declare class SettingsSchema<Src extends {
119
119
  hidden(): SettingsSchema<Src>;
120
120
  protected executeCustomValidateAt(): ValidationError[];
121
121
  protected executeSerialize(): SerializedSchema;
122
- protected executePreview(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: PreviewScope): ReifiedPreview;
122
+ protected executePreview(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: PreviewScope, selfIsReifiedByParent?: boolean): ReifiedPreview;
123
123
  }
124
124
  /**
125
125
  * Define the project's settings.
@@ -141,11 +141,18 @@ export declare class SettingsSchema<Src extends {
141
141
  * context: "Val is a CMS for developers. British English, and 'Val' is never 'VAL'.",
142
142
  * tone: "Plain and direct. No exclamation marks, sentence case in headings.",
143
143
  * },
144
+ * theme: {
145
+ * accent: "#2563eb",
146
+ * radius: "tight",
147
+ * logo: { path: "/public/val/brand/mark_a1b2c.png", width: 512, height: 512, mimeType: "image/png" },
148
+ * },
144
149
  * });
145
150
  * ```
146
151
  *
147
- * The Studio edits it under the cog at the foot of the left rail, and the
148
- * assistant is told `assistant.context` and `assistant.tone` on every message.
152
+ * The Studio edits it under the cog at the foot of the left rail. The assistant
153
+ * is told `assistant.context` and `assistant.tone` on every message, and
154
+ * `theme` restyles the Studio's own chrome — see {@link ThemeSettingsSource},
155
+ * and note that it is the CMS being restyled, not the site.
149
156
  *
150
157
  * `s.settings()` takes no arguments: the shape is Val's, which is what lets the
151
158
  * Studio render a UI built for each field rather than a generic form. A
@@ -1,5 +1,5 @@
1
1
  import { Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
- import { ItemPreviewInput, PreviewItem, ReifiedPreview } from "../preview.js";
2
+ import { ItemPreviewInput, PreviewItem } from "../preview.js";
3
3
  import { FieldRender } from "../render.js";
4
4
  import { SourcePath } from "../val/index.js";
5
5
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
@@ -69,10 +69,13 @@ export declare class StringSchema<Src extends string | null> extends Schema<Src>
69
69
  /**
70
70
  * Describe this field.
71
71
  *
72
- * The description is shown next to the field's label in the Val editor, so
73
- * it is where you say what an editor needs to know but the field name cannot
74
- * carry. It also travels in the serialized schema, which is what the AI
75
- * assistant and the MCP tools read.
72
+ * The description is INPUT HELP: it is shown where this field's value is
73
+ * entered — beside its input in the Val editor, and for a record's key
74
+ * schema in every form that asks for a key — so it is where you say what an
75
+ * editor needs to know to fill it in RIGHT, which the field name cannot
76
+ * carry. It is not a name for the value: that is `.preview(...)`, and it is
77
+ * read somewhere else. The description also travels in the serialized
78
+ * schema, which is what the AI assistant and the MCP tools read.
76
79
  *
77
80
  * Pass `null` to clear a description set earlier.
78
81
  *
@@ -216,12 +219,6 @@ export declare class StringSchema<Src extends string | null> extends Schema<Src>
216
219
  preview(select: ItemPreviewInput<Src>): StringSchema<Src>;
217
220
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
218
221
  protected declaresItemPreview(): boolean;
219
- /**
220
- * Nothing: a string has no items, so there is nothing to preview. Its layout
221
- * is a `render`, which travels in the serialized schema instead of through
222
- * this pipeline.
223
- */
224
- protected executePreview(): ReifiedPreview;
225
222
  }
226
223
  export declare const string: <T extends string>(options?: Record<string, never>) => StringSchema<T>;
227
224
  export {};
@@ -1,3 +1,4 @@
1
+ import { ImageSource } from "./media.js";
1
2
  /**
2
3
  * The source of the project's settings module — `s.settings()`.
3
4
  *
@@ -22,8 +23,124 @@
22
23
  */
23
24
  export type SettingsSource = {
24
25
  assistant?: AssistantSettingsSource;
26
+ theme?: ThemeSettingsSource;
25
27
  locales?: LocalesSettingsSource;
28
+ studio?: StudioSettingsSource;
26
29
  };
30
+ /**
31
+ * How the Studio behaves for the people editing this project.
32
+ *
33
+ * Not how it looks — that is {@link ThemeSettingsSource} — and not one person's
34
+ * own preferences, which stay in their browser. This is the project's word on
35
+ * what its editors are offered, which is why it is content: a team that has
36
+ * decided something about the way they work should not need a developer and a
37
+ * deploy to say so.
38
+ */
39
+ export type StudioSettingsSource = {
40
+ /**
41
+ * Whether editors are offered the guided tour of the Studio.
42
+ *
43
+ * Unset means yes, because the person this exists for is the one who has not
44
+ * answered any question yet. Setting it to `false` turns the offer off for
45
+ * everyone on the project, permanently and for good — which is the point:
46
+ * a team that finds it noisy can be rid of it once rather than each person
47
+ * dismissing it on each machine they use.
48
+ *
49
+ * It governs the OFFER, not the tour. Whoever wants it can still run it from
50
+ * Quick actions; what goes is the glowing button and the prompt on the empty
51
+ * editor. Whether a given person has already been through it is a fact about
52
+ * that browser and is not here.
53
+ */
54
+ tour?: boolean | null;
55
+ };
56
+ /**
57
+ * How the Studio looks in this project.
58
+ *
59
+ * Chrome, and only chrome: nothing here reaches a visitor to the site. It is
60
+ * the CMS that is being restyled, so that a project can make the tool it edits
61
+ * in feel like its own.
62
+ *
63
+ * This is deliberately NOT the light/dark switch. That one is per-person and
64
+ * per-machine (see `ValThemeProvider`, which keeps it in `localStorage`), and it
65
+ * has to stay that way — one editor working in a dark room is not a fact about
66
+ * the project. {@link ThemeSettingsSource.mode} is the project's DEFAULT for
67
+ * someone who has not chosen, which is a different statement.
68
+ */
69
+ export type ThemeSettingsSource = {
70
+ /**
71
+ * The one colour the Studio's chrome is built from, as a hex string.
72
+ *
73
+ * Unset means Val's own green. What it replaces is not a single value but the
74
+ * whole ten-step brand ramp — `--colors-brand-green-100` through `-1000` —
75
+ * because every brand token in `index.css` points into that ramp, and light
76
+ * and dark pick different steps out of it. One accent therefore drives both
77
+ * modes, with nothing to keep in sync.
78
+ *
79
+ * Any hex is allowed rather than a list of approved ones, and that is safe
80
+ * for a measured reason rather than an optimistic one: the ramp is generated
81
+ * by reusing green's LIGHTNESS at each step and changing only the hue, and
82
+ * WCAG contrast is almost entirely a function of lightness. See
83
+ * `accentRamp` in `@valbuild/shared`, whose tests hold every
84
+ * foreground/background pair the chrome renders to AA across the hue circle.
85
+ */
86
+ accent?: string | null;
87
+ /**
88
+ * How round the Studio's corners are.
89
+ *
90
+ * Named steps rather than a length, for two reasons: a number input invites
91
+ * `7px`, which nothing in the chrome is drawn around, and the scale is Val's
92
+ * decision rather than the project's. See {@link THEME_RADIUS_LENGTHS} for
93
+ * what each one is worth.
94
+ */
95
+ radius?: ThemeRadius | null;
96
+ /**
97
+ * The mode the Studio opens in for someone who has not picked one.
98
+ *
99
+ * A default, not a setting: an editor who has flicked the switch behind the
100
+ * account button keeps their choice, and this never overrides it.
101
+ */
102
+ mode?: "dark" | "light" | null;
103
+ /**
104
+ * The project's own mark, shown where Val's is in the Studio.
105
+ *
106
+ * The top of the left rail on desktop, and beside the menu button on mobile.
107
+ * NOT the launcher that floats on the project's own site: there the mark says
108
+ * "this is Val", and a project's logo on its own page says nothing at all —
109
+ * see {@link THEME_LOGO_DIRECTORY} and `architecture/logo.md`.
110
+ *
111
+ * A square-ish mark rather than a wordmark, because the slot it goes in is
112
+ * 32px wide. A wide image is contained rather than cropped, so nothing is
113
+ * cut off — it is simply small.
114
+ */
115
+ logo?: ImageSource | null;
116
+ };
117
+ /**
118
+ * Where an uploaded logo goes.
119
+ *
120
+ * Its own directory rather than the `/public/val` default, so that the one
121
+ * image a project uploads through the settings panel does not land in the
122
+ * middle of its content's media. `val list-unused-files` reads what is
123
+ * referenced rather than where it sits, so nothing depends on this beyond
124
+ * tidiness.
125
+ */
126
+ export declare const THEME_LOGO_DIRECTORY = "/public/val/brand";
127
+ /** @see {@link ThemeSettingsSource.radius} */
128
+ export type ThemeRadius = "square" | "tight" | "default" | "soft";
129
+ export declare const THEME_RADIUS_STEPS: readonly ThemeRadius[];
130
+ /**
131
+ * What each radius step is worth, as a `--radius` value.
132
+ *
133
+ * `--radius` is the only length in the Studio's chrome that is a token:
134
+ * `rounded-sm`, `rounded-md` and `rounded-lg` are all `calc(var(--radius) …)`
135
+ * in `tailwind.config.js`, which is around three hundred call sites moving on
136
+ * one value. `default` is what `index.css` declares, so selecting it and
137
+ * clearing the setting look the same — which is what makes this list the whole
138
+ * of the feature.
139
+ *
140
+ * `rounded-full` and the hand-written `rounded-t` / `-r` / `-b` cases do not
141
+ * follow, and that is visible at `soft`: a pill stays a pill.
142
+ */
143
+ export declare const THEME_RADIUS_LENGTHS: Record<ThemeRadius, string>;
27
144
  /**
28
145
  * The languages this project publishes.
29
146
  *