@valbuild/shared 0.127.0 → 0.129.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/CHANGELOG.md +71 -0
- package/client/dist/valbuild-shared-client.cjs.dev.js +3 -1
- package/client/dist/valbuild-shared-client.cjs.prod.js +3 -1
- package/client/dist/valbuild-shared-client.esm.js +1 -1
- package/dist/declarations/src/internal/aiTools/aiImageToolPatches.d.ts +1 -1
- package/dist/declarations/src/internal/index.d.ts +1 -0
- package/dist/declarations/src/internal/theme/accentRamp.d.ts +120 -0
- package/dist/declarations/src/internal/valCanvasProtocol.d.ts +44 -0
- package/dist/declarations/src/internal/zod/SerializedSchema.d.ts +3 -3
- package/dist/{getSitemapTree-a6cb37cb.esm.js → getSitemapTree-878d3888.esm.js} +48 -1
- package/dist/{getSitemapTree-b320d033.cjs.prod.js → getSitemapTree-99839b02.cjs.prod.js} +49 -0
- package/dist/{getSitemapTree-d1977d3e.cjs.dev.js → getSitemapTree-99c97c62.cjs.dev.js} +49 -0
- package/internal/dist/valbuild-shared-internal.cjs.dev.js +564 -11
- package/internal/dist/valbuild-shared-internal.cjs.prod.js +564 -11
- package/internal/dist/valbuild-shared-internal.esm.js +554 -13
- package/package.json +2 -2
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var core = require('@valbuild/core');
|
|
6
|
-
var getSitemapTree = require('../../dist/getSitemapTree-
|
|
6
|
+
var getSitemapTree = require('../../dist/getSitemapTree-99c97c62.cjs.dev.js');
|
|
7
7
|
var zod = require('zod');
|
|
8
8
|
var zodValidationError = require('zod-validation-error');
|
|
9
9
|
var FlexSearch = require('flexsearch');
|
|
@@ -897,10 +897,34 @@ var PatchBlock = zod.z.object({
|
|
|
897
897
|
parentRef: ParentRef$1
|
|
898
898
|
});
|
|
899
899
|
|
|
900
|
+
function _objectWithoutPropertiesLoose(r, e) {
|
|
901
|
+
if (null == r) return {};
|
|
902
|
+
var t = {};
|
|
903
|
+
for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
|
|
904
|
+
if (-1 !== e.indexOf(n)) continue;
|
|
905
|
+
t[n] = r[n];
|
|
906
|
+
}
|
|
907
|
+
return t;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
function _objectWithoutProperties(e, t) {
|
|
911
|
+
if (null == e) return {};
|
|
912
|
+
var o,
|
|
913
|
+
r,
|
|
914
|
+
i = _objectWithoutPropertiesLoose(e, t);
|
|
915
|
+
if (Object.getOwnPropertySymbols) {
|
|
916
|
+
var n = Object.getOwnPropertySymbols(e);
|
|
917
|
+
for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]);
|
|
918
|
+
}
|
|
919
|
+
return i;
|
|
920
|
+
}
|
|
921
|
+
|
|
900
922
|
var SourcePath = zod.z.string().transform(function (path) {
|
|
901
923
|
return path;
|
|
902
924
|
});
|
|
903
925
|
|
|
926
|
+
var _excluded = ["directory"];
|
|
927
|
+
|
|
904
928
|
// A render is static config, so unlike a `preview` it travels WHOLE — this
|
|
905
929
|
// is the field the editor reads the layout from. See `core/src/render.ts`.
|
|
906
930
|
// Every field can carry `{ as: "inline" }`, and that is all a render says.
|
|
@@ -1008,11 +1032,35 @@ var ImageEncodeOption = zod.z.union([zod.z.literal(false), zod.z.object({
|
|
|
1008
1032
|
maxWidth: zod.z.number().optional(),
|
|
1009
1033
|
maxHeight: zod.z.number().optional()
|
|
1010
1034
|
})]);
|
|
1011
|
-
|
|
1012
|
-
|
|
1035
|
+
/**
|
|
1036
|
+
* Accept the pre-rename `directory` key and read it as `dir`.
|
|
1037
|
+
*
|
|
1038
|
+
* Serialized schemas are not only computed from live code: a commit record
|
|
1039
|
+
* stores the schema each module was under (`StoredModuleVersion`), and the
|
|
1040
|
+
* history view parses those back. Commits made before `directory` became `dir`
|
|
1041
|
+
* therefore still carry the old key, and dropping it would show a historical
|
|
1042
|
+
* gallery with no directory at all — silently, since the rest of the schema
|
|
1043
|
+
* parses fine.
|
|
1044
|
+
*
|
|
1045
|
+
* Only fills `dir` when it is absent, so a schema carrying both (which should
|
|
1046
|
+
* not happen) keeps the current key.
|
|
1047
|
+
*/
|
|
1048
|
+
var readLegacyDirectoryAsDir = function readLegacyDirectoryAsDir(value) {
|
|
1049
|
+
if (!value || getSitemapTree._typeof(value) !== "object" || Array.isArray(value) || !("directory" in value) || "dir" in value) {
|
|
1050
|
+
return value;
|
|
1051
|
+
}
|
|
1052
|
+
var _ref = value,
|
|
1053
|
+
directory = _ref.directory,
|
|
1054
|
+
rest = _objectWithoutProperties(_ref, _excluded);
|
|
1055
|
+
return getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, rest), {}, {
|
|
1056
|
+
dir: directory
|
|
1057
|
+
});
|
|
1058
|
+
};
|
|
1059
|
+
var ImageOptions = zod.z.preprocess(readLegacyDirectoryAsDir, zod.z.object({
|
|
1060
|
+
dir: zod.z.string().optional(),
|
|
1013
1061
|
accept: zod.z.string().optional(),
|
|
1014
1062
|
encode: ImageEncodeOption.optional()
|
|
1015
|
-
});
|
|
1063
|
+
}));
|
|
1016
1064
|
var SerializedImageSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
|
|
1017
1065
|
type: zod.z.literal("image"),
|
|
1018
1066
|
options: ImageOptions.optional(),
|
|
@@ -1051,7 +1099,7 @@ var SerializedRichTextSchema = zod.z.object(getSitemapTree._objectSpread2(getSit
|
|
|
1051
1099
|
opt: zod.z["boolean"]()
|
|
1052
1100
|
}));
|
|
1053
1101
|
var SerializedRecordSchema = zod.z.lazy(function () {
|
|
1054
|
-
return zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
|
|
1102
|
+
return zod.z.preprocess(readLegacyDirectoryAsDir, zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
|
|
1055
1103
|
type: zod.z.literal("record"),
|
|
1056
1104
|
item: SerializedSchema,
|
|
1057
1105
|
opt: zod.z["boolean"](),
|
|
@@ -1059,12 +1107,12 @@ var SerializedRecordSchema = zod.z.lazy(function () {
|
|
|
1059
1107
|
mediaType: zod.z.union([zod.z.literal("files"), zod.z.literal("images")]).optional(),
|
|
1060
1108
|
// Optional legacy gallery metadata
|
|
1061
1109
|
accept: zod.z.string().optional(),
|
|
1062
|
-
|
|
1110
|
+
dir: zod.z.string().optional(),
|
|
1063
1111
|
remote: zod.z["boolean"]().optional(),
|
|
1064
1112
|
encode: ImageEncodeOption.optional(),
|
|
1065
1113
|
alt: SerializedSchema.optional(),
|
|
1066
1114
|
moduleMetadata: zod.z.record(zod.z.string(), zod.z.record(zod.z.string(), zod.z.any())).optional()
|
|
1067
|
-
})).passthrough();
|
|
1115
|
+
})).passthrough());
|
|
1068
1116
|
});
|
|
1069
1117
|
var SerializedKeyOfSchema = zod.z.lazy(function () {
|
|
1070
1118
|
return zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
|
|
@@ -4704,7 +4752,7 @@ function getSourceAt(source, path) {
|
|
|
4704
4752
|
* Walks `schema` along `path`, returning a structured result:
|
|
4705
4753
|
* - `leaf`: walked the full path; resolved schema at the end.
|
|
4706
4754
|
* - `richtext`: hit a richtext mid-walk; richtext is the resolved schema.
|
|
4707
|
-
* - `gallery-traversed`: hit an `s.
|
|
4755
|
+
* - `gallery-traversed`: hit an `s.imageset()` record and walked past it (i.e. the
|
|
4708
4756
|
* path tries to address an entry inside the gallery — a clue the AI should
|
|
4709
4757
|
* have used the gallery tool instead).
|
|
4710
4758
|
* - `unresolved`: the path could not be walked.
|
|
@@ -4835,7 +4883,7 @@ function buildImageGalleryPatch(args, moduleSchema) {
|
|
|
4835
4883
|
op: "add",
|
|
4836
4884
|
path: [args.filePath],
|
|
4837
4885
|
value: getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, args.metadata), {}, {
|
|
4838
|
-
// `alt` is a REQUIRED field of a gallery entry (`s.
|
|
4886
|
+
// `alt` is a REQUIRED field of a gallery entry (`s.imageset()` gives it a
|
|
4839
4887
|
// nullable string schema), so an entry written without one is invalid
|
|
4840
4888
|
// the moment it lands — and the caller that had no alt to give is the
|
|
4841
4889
|
// common case, not the odd one. Null is what the Studio's own upload
|
|
@@ -4898,10 +4946,10 @@ function buildRemoveImageGalleryEntryPatch(args, moduleSchema, moduleSource) {
|
|
|
4898
4946
|
var GALLERY_REDIRECT = {
|
|
4899
4947
|
kind: "wrong-tool",
|
|
4900
4948
|
suggestedTool: "add_session_image_to_gallery",
|
|
4901
|
-
reason: "Destination is a media gallery (s.
|
|
4949
|
+
reason: "Destination is a media gallery (s.imageset() / s.fileset()). Gallery entries are keyed by file path and carry a file on disk, so they cannot be created with a plain patch. Use add_session_image_to_gallery."
|
|
4902
4950
|
};
|
|
4903
4951
|
|
|
4904
|
-
/** An `s.
|
|
4952
|
+
/** An `s.imageset()` / `s.fileset()` record: a gallery, not an ordinary record. */
|
|
4905
4953
|
function isGallery(schema) {
|
|
4906
4954
|
return schema.type === "record" && schema.mediaType !== undefined;
|
|
4907
4955
|
}
|
|
@@ -6071,6 +6119,501 @@ function performSearch(searchIndex, query) {
|
|
|
6071
6119
|
};
|
|
6072
6120
|
}
|
|
6073
6121
|
|
|
6122
|
+
/**
|
|
6123
|
+
* The Studio's chrome, generated from one colour.
|
|
6124
|
+
*
|
|
6125
|
+
* `s.settings()`'s `theme.accent` is a single hex, and what it has to replace is
|
|
6126
|
+
* a TEN-STEP RAMP: `index.css` declares `--colors-brand-green-100` through
|
|
6127
|
+
* `-1000` and every brand token points into it, picking different steps in light
|
|
6128
|
+
* and in dark (a tinted surface is step 200 in light and 800 in dark; the text
|
|
6129
|
+
* on it is 1000 and 100). So the accent is not a colour to place, it is a ramp
|
|
6130
|
+
* to build — which is also why one accent drives both modes with nothing to keep
|
|
6131
|
+
* in sync.
|
|
6132
|
+
*
|
|
6133
|
+
* ## Why an arbitrary hex is safe
|
|
6134
|
+
*
|
|
6135
|
+
* The obvious worry is `contrast.test.ts`, which holds every foreground /
|
|
6136
|
+
* background pair the chrome renders to WCAG AA. A hex typed by an editor never
|
|
6137
|
+
* goes near that test.
|
|
6138
|
+
*
|
|
6139
|
+
* It holds anyway, because of how the ramp is built: **green's lightness at each
|
|
6140
|
+
* step is reused unchanged, and only the hue moves.** WCAG relative luminance is
|
|
6141
|
+
* almost entirely a function of lightness, so a ramp that keeps green's L
|
|
6142
|
+
* profile keeps green's contrast relationships. Chroma is scaled to the accent's
|
|
6143
|
+
* own saturation and then reduced — never L — until each step fits sRGB.
|
|
6144
|
+
*
|
|
6145
|
+
* "Almost entirely" is doing real work in that sentence: at equal OKLCH
|
|
6146
|
+
* lightness a saturated yellow has a higher relative luminance than a blue, so
|
|
6147
|
+
* the ratios do move a little. {@link BRAND_CONTRAST_PAIRS} is the list of pairs
|
|
6148
|
+
* that have to survive it, and `accentRamp.test.ts` runs them over the hue
|
|
6149
|
+
* circle rather than trusting the argument.
|
|
6150
|
+
*
|
|
6151
|
+
* ## What is NOT generated
|
|
6152
|
+
*
|
|
6153
|
+
* With no accent set, the hand-tuned ramp in `index.css` is used as it is. This
|
|
6154
|
+
* module reproduces it to within a step or two, and re-deriving a value somebody
|
|
6155
|
+
* tuned by eye is a silent regression rather than a simplification.
|
|
6156
|
+
*/
|
|
6157
|
+
|
|
6158
|
+
/** The steps `index.css` declares, smallest (lightest) first. */
|
|
6159
|
+
var RAMP_STEPS = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000];
|
|
6160
|
+
/**
|
|
6161
|
+
* Val's own brand ramp, and the lightness profile every generated ramp borrows.
|
|
6162
|
+
*
|
|
6163
|
+
* A copy of what `index.css` declares, because this package cannot read the UI
|
|
6164
|
+
* package's stylesheet — so the two are held together by a test instead:
|
|
6165
|
+
* `contrast.test.ts` resolves the ramp out of the real CSS and asserts it equals
|
|
6166
|
+
* this. If that test fails, the stylesheet moved and this did not.
|
|
6167
|
+
*/
|
|
6168
|
+
var VAL_GREEN_RAMP = {
|
|
6169
|
+
100: "#dcfae6",
|
|
6170
|
+
200: "#abefc6",
|
|
6171
|
+
300: "#75e0a7",
|
|
6172
|
+
400: "#47cd89",
|
|
6173
|
+
500: "#17b26a",
|
|
6174
|
+
600: "#079455",
|
|
6175
|
+
700: "#067647",
|
|
6176
|
+
800: "#085d3a",
|
|
6177
|
+
900: "#074d31",
|
|
6178
|
+
1000: "#053321"
|
|
6179
|
+
};
|
|
6180
|
+
|
|
6181
|
+
/**
|
|
6182
|
+
* The step the accent itself becomes, and therefore the step the whole ramp's
|
|
6183
|
+
* chroma is scaled against.
|
|
6184
|
+
*
|
|
6185
|
+
* 500 rather than 600: it is the most saturated step in the green ramp, so an
|
|
6186
|
+
* accent anchored there is reproduced about as faithfully as the ramp allows,
|
|
6187
|
+
* and the steps either side of it stay in the same relationship to it that
|
|
6188
|
+
* green's do.
|
|
6189
|
+
*/
|
|
6190
|
+
var ANCHOR_STEP = 500;
|
|
6191
|
+
|
|
6192
|
+
/**
|
|
6193
|
+
* The step drawn on the CUSTOMER's page, as an outline around every editable
|
|
6194
|
+
* element.
|
|
6195
|
+
*
|
|
6196
|
+
* 600 is the step `index.css` pins the page-selection tokens to, for a reason
|
|
6197
|
+
* worth keeping: it clears 3:1 against both white and black, so the outlines
|
|
6198
|
+
* stay visible whatever the site behind them looks like. A generated ramp
|
|
6199
|
+
* inherits that because it inherits green-600's lightness — and
|
|
6200
|
+
* `accentRamp.test.ts` checks it on both grounds rather than assuming.
|
|
6201
|
+
*/
|
|
6202
|
+
var PAGE_SELECTION_STEP = 600;
|
|
6203
|
+
|
|
6204
|
+
// ---------------------------------------------------------------------------
|
|
6205
|
+
// sRGB <-> OKLCH
|
|
6206
|
+
//
|
|
6207
|
+
// Written out rather than taken from a dependency: it is thirty lines, it runs
|
|
6208
|
+
// on every Studio render that has an accent, and `s.color()` already carries
|
|
6209
|
+
// its own copy of the neighbouring maths (`schema/colorFormat.ts`) for the same
|
|
6210
|
+
// reason.
|
|
6211
|
+
// ---------------------------------------------------------------------------
|
|
6212
|
+
|
|
6213
|
+
/** OKLCH: lightness 0-1, chroma, hue in degrees. */
|
|
6214
|
+
|
|
6215
|
+
/**
|
|
6216
|
+
* Every hex form `s.color({ format: "hex" })` accepts.
|
|
6217
|
+
*
|
|
6218
|
+
* Three and four digits as well as six and eight, because that is what the
|
|
6219
|
+
* schema validates — `detectColorFormat` calls anything starting with `#` hex,
|
|
6220
|
+
* and `#fff` parses. A generator that took only six digits left a
|
|
6221
|
+
* schema-VALID accent doing nothing at all: no ramp, no error, and a Studio
|
|
6222
|
+
* still on Val green with a colour sitting in the settings module.
|
|
6223
|
+
*/
|
|
6224
|
+
var HEX = /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
6225
|
+
function srgbToLinear(c) {
|
|
6226
|
+
return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
6227
|
+
}
|
|
6228
|
+
function linearToSrgb(c) {
|
|
6229
|
+
return c <= 0.0031308 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
|
|
6230
|
+
}
|
|
6231
|
+
|
|
6232
|
+
/**
|
|
6233
|
+
* `null` for anything that is not a hex colour.
|
|
6234
|
+
*
|
|
6235
|
+
* Alpha is read and discarded. The ramp is a set of opaque surfaces and text
|
|
6236
|
+
* colours, and `s.color()` rejects a non-opaque value anyway unless the field
|
|
6237
|
+
* enabled alpha — which `theme.accent` does not. So `#0f08` is the same accent
|
|
6238
|
+
* as `#0f0`, rather than nothing at all.
|
|
6239
|
+
*/
|
|
6240
|
+
function hexToRgb(hex) {
|
|
6241
|
+
var trimmed = hex.trim();
|
|
6242
|
+
if (!HEX.test(trimmed)) {
|
|
6243
|
+
return null;
|
|
6244
|
+
}
|
|
6245
|
+
var digits = trimmed.slice(1);
|
|
6246
|
+
var _short = digits.length <= 4;
|
|
6247
|
+
var channel = function channel(index) {
|
|
6248
|
+
var slice = _short ? digits[index].repeat(2) : digits.slice(index * 2, index * 2 + 2);
|
|
6249
|
+
return parseInt(slice, 16) / 255;
|
|
6250
|
+
};
|
|
6251
|
+
return [channel(0), channel(1), channel(2)];
|
|
6252
|
+
}
|
|
6253
|
+
function rgbToHex(rgb) {
|
|
6254
|
+
return "#" + rgb.map(function (c) {
|
|
6255
|
+
return Math.round(Math.min(1, Math.max(0, c)) * 255).toString(16).padStart(2, "0");
|
|
6256
|
+
}).join("");
|
|
6257
|
+
}
|
|
6258
|
+
function rgbToOklch(rgb) {
|
|
6259
|
+
var r = srgbToLinear(rgb[0]);
|
|
6260
|
+
var g = srgbToLinear(rgb[1]);
|
|
6261
|
+
var b = srgbToLinear(rgb[2]);
|
|
6262
|
+
var l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
|
6263
|
+
var m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
|
6264
|
+
var s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
|
6265
|
+
var okL = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s;
|
|
6266
|
+
var okA = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s;
|
|
6267
|
+
var okB = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s;
|
|
6268
|
+
return [okL, Math.hypot(okA, okB), Math.atan2(okB, okA) * 180 / Math.PI];
|
|
6269
|
+
}
|
|
6270
|
+
function oklchToRgb(lch) {
|
|
6271
|
+
var okA = lch[1] * Math.cos(lch[2] * Math.PI / 180);
|
|
6272
|
+
var okB = lch[1] * Math.sin(lch[2] * Math.PI / 180);
|
|
6273
|
+
var l = Math.pow(lch[0] + 0.3963377774 * okA + 0.2158037573 * okB, 3);
|
|
6274
|
+
var m = Math.pow(lch[0] - 0.1055613458 * okA - 0.0638541728 * okB, 3);
|
|
6275
|
+
var s = Math.pow(lch[0] - 0.0894841775 * okA - 1.291485548 * okB, 3);
|
|
6276
|
+
return [linearToSrgb(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s), linearToSrgb(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s), linearToSrgb(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s)];
|
|
6277
|
+
}
|
|
6278
|
+
|
|
6279
|
+
/** A hair of slack, so a colour that is exactly at the edge is not clamped. */
|
|
6280
|
+
var GAMUT_EPSILON = 0.0005;
|
|
6281
|
+
function inGamut(rgb) {
|
|
6282
|
+
return rgb.every(function (c) {
|
|
6283
|
+
return c >= -GAMUT_EPSILON && c <= 1 + GAMUT_EPSILON;
|
|
6284
|
+
});
|
|
6285
|
+
}
|
|
6286
|
+
|
|
6287
|
+
/**
|
|
6288
|
+
* Bring a colour inside sRGB by reducing its CHROMA, never its lightness.
|
|
6289
|
+
*
|
|
6290
|
+
* That restriction is the whole reason the contrast argument holds: clipping
|
|
6291
|
+
* RGB channels (which is what naive conversion does) moves lightness, and a
|
|
6292
|
+
* step that has drifted lighter is a step whose contrast guarantee is gone.
|
|
6293
|
+
* Twenty-four bisections is well past single-byte precision.
|
|
6294
|
+
*/
|
|
6295
|
+
function clampChromaToGamut(lch) {
|
|
6296
|
+
if (inGamut(oklchToRgb(lch))) {
|
|
6297
|
+
return lch;
|
|
6298
|
+
}
|
|
6299
|
+
var lo = 0;
|
|
6300
|
+
var hi = lch[1];
|
|
6301
|
+
for (var i = 0; i < 24; i++) {
|
|
6302
|
+
var mid = (lo + hi) / 2;
|
|
6303
|
+
if (inGamut(oklchToRgb([lch[0], mid, lch[2]]))) {
|
|
6304
|
+
lo = mid;
|
|
6305
|
+
} else {
|
|
6306
|
+
hi = mid;
|
|
6307
|
+
}
|
|
6308
|
+
}
|
|
6309
|
+
return [lch[0], lo, lch[2]];
|
|
6310
|
+
}
|
|
6311
|
+
var GREEN_PROFILE = RAMP_STEPS.map(function (step) {
|
|
6312
|
+
var rgb = hexToRgb(VAL_GREEN_RAMP[step]);
|
|
6313
|
+
if (rgb === null) {
|
|
6314
|
+
// Unreachable: the ramp above is literal hex. Thrown rather than defaulted
|
|
6315
|
+
// because a broken profile would produce a plausible-looking wrong ramp.
|
|
6316
|
+
throw new Error("Val's green ramp holds a non-hex value at step ".concat(step));
|
|
6317
|
+
}
|
|
6318
|
+
return {
|
|
6319
|
+
step: step,
|
|
6320
|
+
lch: rgbToOklch(rgb)
|
|
6321
|
+
};
|
|
6322
|
+
});
|
|
6323
|
+
|
|
6324
|
+
/**
|
|
6325
|
+
* The ten-step ramp for one accent, or `null` if it is not a six-digit hex.
|
|
6326
|
+
*
|
|
6327
|
+
* `null` rather than a fallback: the caller knows whether it is holding a value
|
|
6328
|
+
* an editor picked (in which case the schema already validated it) or one that
|
|
6329
|
+
* came out of a hand-edited settings file, and only the caller can decide
|
|
6330
|
+
* whether "ignore it" or "report it" is right.
|
|
6331
|
+
*/
|
|
6332
|
+
function accentRamp(accent) {
|
|
6333
|
+
var rgb = hexToRgb(accent);
|
|
6334
|
+
if (rgb === null) {
|
|
6335
|
+
return null;
|
|
6336
|
+
}
|
|
6337
|
+
var _rgbToOklch = rgbToOklch(rgb),
|
|
6338
|
+
_rgbToOklch2 = _slicedToArray(_rgbToOklch, 3),
|
|
6339
|
+
accentChroma = _rgbToOklch2[1],
|
|
6340
|
+
accentHue = _rgbToOklch2[2];
|
|
6341
|
+
var anchor = GREEN_PROFILE.find(function (entry) {
|
|
6342
|
+
return entry.step === ANCHOR_STEP;
|
|
6343
|
+
});
|
|
6344
|
+
if (anchor === undefined || anchor.lch[1] === 0) {
|
|
6345
|
+
throw new Error("Val's green ramp has no chroma at its anchor step");
|
|
6346
|
+
}
|
|
6347
|
+
var chromaScale = accentChroma / anchor.lch[1];
|
|
6348
|
+
var at = function at(step) {
|
|
6349
|
+
var entry = GREEN_PROFILE.find(function (candidate) {
|
|
6350
|
+
return candidate.step === step;
|
|
6351
|
+
});
|
|
6352
|
+
if (entry === undefined) {
|
|
6353
|
+
throw new Error("Val's green ramp has no step ".concat(step));
|
|
6354
|
+
}
|
|
6355
|
+
// Green's lightness and chroma, the accent's hue. Green's own hue is the
|
|
6356
|
+
// one thing a generated ramp does not keep.
|
|
6357
|
+
var _entry$lch = _slicedToArray(entry.lch, 2),
|
|
6358
|
+
lightness = _entry$lch[0],
|
|
6359
|
+
chroma = _entry$lch[1];
|
|
6360
|
+
return rgbToHex(oklchToRgb(clampChromaToGamut([lightness, chroma * chromaScale, accentHue])));
|
|
6361
|
+
};
|
|
6362
|
+
// Written out rather than reduced into, so the type says every step is
|
|
6363
|
+
// present instead of a cast saying so.
|
|
6364
|
+
return {
|
|
6365
|
+
100: at(100),
|
|
6366
|
+
200: at(200),
|
|
6367
|
+
300: at(300),
|
|
6368
|
+
400: at(400),
|
|
6369
|
+
500: at(500),
|
|
6370
|
+
600: at(600),
|
|
6371
|
+
700: at(700),
|
|
6372
|
+
800: at(800),
|
|
6373
|
+
900: at(900),
|
|
6374
|
+
1000: at(1000)
|
|
6375
|
+
};
|
|
6376
|
+
}
|
|
6377
|
+
|
|
6378
|
+
/**
|
|
6379
|
+
* The custom properties that restyle the chrome, ready to put on a `style`
|
|
6380
|
+
* attribute.
|
|
6381
|
+
*
|
|
6382
|
+
* Custom properties on the element rather than a generated stylesheet: nothing
|
|
6383
|
+
* has to be escaped, an inline custom property beats `@layer base` without a
|
|
6384
|
+
* specificity fight, and a subtree that should keep the untouched palette
|
|
6385
|
+
* simply is not given the object. See `useValThemeStyle` for where they go —
|
|
6386
|
+
* every place the Studio stamps `data-mode`, because the theme travels with it.
|
|
6387
|
+
*
|
|
6388
|
+
* Empty when nothing is set, which is what makes "no theme" cost nothing.
|
|
6389
|
+
*/
|
|
6390
|
+
function themeCustomProperties(theme) {
|
|
6391
|
+
var properties = {};
|
|
6392
|
+
if (theme.accent) {
|
|
6393
|
+
var ramp = accentRamp(theme.accent);
|
|
6394
|
+
if (ramp !== null) {
|
|
6395
|
+
var _iterator = getSitemapTree._createForOfIteratorHelper(RAMP_STEPS),
|
|
6396
|
+
_step;
|
|
6397
|
+
try {
|
|
6398
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
6399
|
+
var step = _step.value;
|
|
6400
|
+
properties["--colors-brand-green-".concat(step)] = ramp[step];
|
|
6401
|
+
}
|
|
6402
|
+
/*
|
|
6403
|
+
* The page-selection trio, written OUT rather than left to follow the
|
|
6404
|
+
* ramp — and the first of them is the one that has to be said out loud,
|
|
6405
|
+
* because it looks like it should follow and does not.
|
|
6406
|
+
*
|
|
6407
|
+
* `index.css` declares `--bg-page-selection: var(--colors-brand-green-600)`,
|
|
6408
|
+
* so overriding the ramp looks like enough. It is not: **a `var()` inside
|
|
6409
|
+
* a custom property is substituted where the property is DECLARED, not
|
|
6410
|
+
* where it is used.** That declaration lives in the light block, whose
|
|
6411
|
+
* selector is `:host, :root, *[data-mode="light"]` — so on a
|
|
6412
|
+
* `data-mode="dark"` element the rule does not match, nothing is declared
|
|
6413
|
+
* there, and what the element inherits from `:host` is the value already
|
|
6414
|
+
* substituted to green. An override of the ramp on the element cannot
|
|
6415
|
+
* reach backwards into it.
|
|
6416
|
+
*
|
|
6417
|
+
* The brand tokens escape this only because the DARK block re-declares
|
|
6418
|
+
* them: `--bg-brand-primary: var(--colors-brand-green-800)` is computed
|
|
6419
|
+
* on the themed element itself, so it resolves against the override. The
|
|
6420
|
+
* page-selection tokens are deliberately declared once and never flipped
|
|
6421
|
+
* with the theme (the page underneath is not a Val surface), which is
|
|
6422
|
+
* exactly what puts them outside that mechanism.
|
|
6423
|
+
*
|
|
6424
|
+
* So: anything that depends on the ramp and is not re-declared per mode
|
|
6425
|
+
* has to be written here. `brandDerivedTokens.test.ts` scans the
|
|
6426
|
+
* stylesheet and fails if another one appears.
|
|
6427
|
+
*/
|
|
6428
|
+
} catch (err) {
|
|
6429
|
+
_iterator.e(err);
|
|
6430
|
+
} finally {
|
|
6431
|
+
_iterator.f();
|
|
6432
|
+
}
|
|
6433
|
+
properties["--bg-page-selection"] = ramp[PAGE_SELECTION_STEP];
|
|
6434
|
+
var selection = hexToRgb(ramp[PAGE_SELECTION_STEP]);
|
|
6435
|
+
if (selection !== null) {
|
|
6436
|
+
var channels = selection.map(function (c) {
|
|
6437
|
+
return Math.round(c * 255);
|
|
6438
|
+
}).join(", ");
|
|
6439
|
+
properties["--bg-page-selection-fill"] = "rgba(".concat(channels, ", 0.12)");
|
|
6440
|
+
properties["--bg-page-selection-soft"] = "rgba(".concat(channels, ", 0.4)");
|
|
6441
|
+
}
|
|
6442
|
+
}
|
|
6443
|
+
}
|
|
6444
|
+
if (theme.radius) {
|
|
6445
|
+
properties["--radius"] = theme.radius;
|
|
6446
|
+
}
|
|
6447
|
+
return properties;
|
|
6448
|
+
}
|
|
6449
|
+
|
|
6450
|
+
/**
|
|
6451
|
+
* WCAG relative luminance of a six-digit hex.
|
|
6452
|
+
*
|
|
6453
|
+
* Exported so there is one implementation of the maths that decides whether a
|
|
6454
|
+
* theme is legible; `contrast.test.ts` had its own copy, and two copies of a
|
|
6455
|
+
* threshold is how a guarantee quietly stops being one.
|
|
6456
|
+
*/
|
|
6457
|
+
function relativeLuminance(hex) {
|
|
6458
|
+
var rgb = hexToRgb(hex);
|
|
6459
|
+
if (rgb === null) {
|
|
6460
|
+
throw new Error("Not a six-digit hex colour: ".concat(hex));
|
|
6461
|
+
}
|
|
6462
|
+
var _rgb$map = rgb.map(srgbToLinear),
|
|
6463
|
+
_rgb$map2 = _slicedToArray(_rgb$map, 3),
|
|
6464
|
+
r = _rgb$map2[0],
|
|
6465
|
+
g = _rgb$map2[1],
|
|
6466
|
+
b = _rgb$map2[2];
|
|
6467
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
6468
|
+
}
|
|
6469
|
+
|
|
6470
|
+
/** WCAG contrast ratio between two six-digit hex colours, 1 to 21. */
|
|
6471
|
+
function contrastRatio(a, b) {
|
|
6472
|
+
var la = relativeLuminance(a);
|
|
6473
|
+
var lb = relativeLuminance(b);
|
|
6474
|
+
var hi = Math.max(la, lb);
|
|
6475
|
+
var lo = Math.min(la, lb);
|
|
6476
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
6477
|
+
}
|
|
6478
|
+
|
|
6479
|
+
/** WCAG AA for body text. */
|
|
6480
|
+
var AA_TEXT = 4.5;
|
|
6481
|
+
/** WCAG AA for large text and for UI components such as a focus ring. */
|
|
6482
|
+
var AA_LARGE = 3;
|
|
6483
|
+
|
|
6484
|
+
/**
|
|
6485
|
+
* Where a brand pair's background comes from.
|
|
6486
|
+
*
|
|
6487
|
+
* A `RampStep` is a step of the brand ramp, so it moves with the accent. A hex
|
|
6488
|
+
* is a neutral surface that does not — the canvas and the floating panels are
|
|
6489
|
+
* grey in every theme, and a ring has to be legible against them.
|
|
6490
|
+
*/
|
|
6491
|
+
|
|
6492
|
+
/**
|
|
6493
|
+
* Every foreground / background pair the chrome renders with BRAND colours.
|
|
6494
|
+
*
|
|
6495
|
+
* The list is data rather than a test body because two different things have to
|
|
6496
|
+
* be held to it, and one table is the only way they can agree:
|
|
6497
|
+
*
|
|
6498
|
+
* - `accentRamp.test.ts` runs it over ramps generated across the hue circle,
|
|
6499
|
+
* which is what makes an arbitrary accent safe.
|
|
6500
|
+
* - `contrast.test.ts` runs it over the ramp resolved out of the real
|
|
6501
|
+
* `index.css`, which is what stops someone nudging Val's own green past AA.
|
|
6502
|
+
*
|
|
6503
|
+
* The neutral pairs — muted text on a panel, a hairline on the canvas — stay in
|
|
6504
|
+
* `contrast.test.ts`. They have nothing to do with the accent, and a theme
|
|
6505
|
+
* cannot move them.
|
|
6506
|
+
*/
|
|
6507
|
+
var BRAND_CONTRAST_PAIRS = [
|
|
6508
|
+
// Light: `--bg-brand-primary` is step 200, with `--fg-brand-primary` (1000)
|
|
6509
|
+
// and `--fg-brand-primary-alt` (900) on it.
|
|
6510
|
+
{
|
|
6511
|
+
what: "fg-brand-primary on bg-brand-primary",
|
|
6512
|
+
mode: "light",
|
|
6513
|
+
fg: 1000,
|
|
6514
|
+
bg: 200,
|
|
6515
|
+
min: AA_TEXT
|
|
6516
|
+
}, {
|
|
6517
|
+
what: "fg-brand-primary-alt on bg-brand-primary",
|
|
6518
|
+
mode: "light",
|
|
6519
|
+
fg: 900,
|
|
6520
|
+
bg: 200,
|
|
6521
|
+
min: AA_TEXT
|
|
6522
|
+
},
|
|
6523
|
+
// Light: `--bg-brand-secondary` is the filled step 800.
|
|
6524
|
+
{
|
|
6525
|
+
what: "fg-brand-secondary on bg-brand-secondary",
|
|
6526
|
+
mode: "light",
|
|
6527
|
+
fg: 100,
|
|
6528
|
+
bg: 800,
|
|
6529
|
+
min: AA_TEXT
|
|
6530
|
+
}, {
|
|
6531
|
+
what: "fg-brand-secondary-alt on bg-brand-secondary",
|
|
6532
|
+
mode: "light",
|
|
6533
|
+
fg: 200,
|
|
6534
|
+
bg: 800,
|
|
6535
|
+
min: AA_TEXT
|
|
6536
|
+
},
|
|
6537
|
+
// Light: `--border-focus` is step 600, and it has to read on both neutral
|
|
6538
|
+
// surfaces a focused control can sit on.
|
|
6539
|
+
{
|
|
6540
|
+
what: "border-focus on bg-float",
|
|
6541
|
+
mode: "light",
|
|
6542
|
+
fg: 600,
|
|
6543
|
+
bg: "#fcfcfc",
|
|
6544
|
+
min: AA_LARGE
|
|
6545
|
+
}, {
|
|
6546
|
+
what: "border-focus on bg-canvas",
|
|
6547
|
+
mode: "light",
|
|
6548
|
+
fg: 600,
|
|
6549
|
+
bg: "#f4f4f5",
|
|
6550
|
+
min: AA_LARGE
|
|
6551
|
+
},
|
|
6552
|
+
// The outlines drawn on the customer's own page. Not a Val surface at all,
|
|
6553
|
+
// which is why the grounds are pure white and pure black: the site behind
|
|
6554
|
+
// them can be anything.
|
|
6555
|
+
{
|
|
6556
|
+
what: "bg-page-selection on a white site",
|
|
6557
|
+
mode: "light",
|
|
6558
|
+
fg: 600,
|
|
6559
|
+
bg: "#ffffff",
|
|
6560
|
+
min: AA_LARGE
|
|
6561
|
+
}, {
|
|
6562
|
+
what: "bg-page-selection on a black site",
|
|
6563
|
+
mode: "light",
|
|
6564
|
+
fg: 600,
|
|
6565
|
+
bg: "#000000",
|
|
6566
|
+
min: AA_LARGE
|
|
6567
|
+
},
|
|
6568
|
+
// Dark inverts which step is the surface and which is the text.
|
|
6569
|
+
{
|
|
6570
|
+
what: "fg-brand-primary on bg-brand-primary",
|
|
6571
|
+
mode: "dark",
|
|
6572
|
+
fg: 100,
|
|
6573
|
+
bg: 800,
|
|
6574
|
+
min: AA_TEXT
|
|
6575
|
+
}, {
|
|
6576
|
+
what: "fg-brand-primary-alt on bg-brand-primary",
|
|
6577
|
+
mode: "dark",
|
|
6578
|
+
fg: 200,
|
|
6579
|
+
bg: 800,
|
|
6580
|
+
min: AA_TEXT
|
|
6581
|
+
}, {
|
|
6582
|
+
what: "fg-brand-secondary on bg-brand-secondary",
|
|
6583
|
+
mode: "dark",
|
|
6584
|
+
fg: 1000,
|
|
6585
|
+
bg: 200,
|
|
6586
|
+
min: AA_TEXT
|
|
6587
|
+
}, {
|
|
6588
|
+
what: "fg-brand-secondary-alt on bg-brand-secondary",
|
|
6589
|
+
mode: "dark",
|
|
6590
|
+
fg: 900,
|
|
6591
|
+
bg: 200,
|
|
6592
|
+
min: AA_TEXT
|
|
6593
|
+
},
|
|
6594
|
+
// Dark's focus ring lightens rather than mirroring: step 600 is nearly
|
|
6595
|
+
// invisible against the dark canvas.
|
|
6596
|
+
{
|
|
6597
|
+
what: "border-focus on bg-canvas",
|
|
6598
|
+
mode: "dark",
|
|
6599
|
+
fg: 400,
|
|
6600
|
+
bg: "#08080a",
|
|
6601
|
+
min: AA_LARGE
|
|
6602
|
+
}, {
|
|
6603
|
+
what: "border-focus on bg-float",
|
|
6604
|
+
mode: "dark",
|
|
6605
|
+
fg: 400,
|
|
6606
|
+
bg: "#131316",
|
|
6607
|
+
min: AA_LARGE
|
|
6608
|
+
}];
|
|
6609
|
+
|
|
6610
|
+
/** Resolve one side of a {@link BRAND_CONTRAST_PAIRS} entry against a ramp. */
|
|
6611
|
+
function resolvePairGround(ground, ramp) {
|
|
6612
|
+
return typeof ground === "number" ? ramp[ground] : ground;
|
|
6613
|
+
}
|
|
6614
|
+
|
|
6615
|
+
exports.DEFAULT_CANVAS_SELECTION = getSitemapTree.DEFAULT_CANVAS_SELECTION;
|
|
6616
|
+
exports.DEFAULT_CANVAS_SELECTION_SOFT = getSitemapTree.DEFAULT_CANVAS_SELECTION_SOFT;
|
|
6074
6617
|
exports.VAL_AI_SESSION_STORAGE_KEY = getSitemapTree.VAL_AI_SESSION_STORAGE_KEY;
|
|
6075
6618
|
exports.VAL_CANVAS_MESSAGE = getSitemapTree.VAL_CANVAS_MESSAGE;
|
|
6076
6619
|
exports.VAL_CANVAS_PARAM = getSitemapTree.VAL_CANVAS_PARAM;
|
|
@@ -6087,7 +6630,10 @@ exports.isValCanvasPageMessage = getSitemapTree.isValCanvasPageMessage;
|
|
|
6087
6630
|
exports.isValCanvasStudioMessage = getSitemapTree.isValCanvasStudioMessage;
|
|
6088
6631
|
exports.parseRoutePattern = getSitemapTree.parseRoutePattern;
|
|
6089
6632
|
exports.withValCanvasParam = getSitemapTree.withValCanvasParam;
|
|
6633
|
+
exports.AA_LARGE = AA_LARGE;
|
|
6634
|
+
exports.AA_TEXT = AA_TEXT;
|
|
6090
6635
|
exports.Api = Api;
|
|
6636
|
+
exports.BRAND_CONTRAST_PAIRS = BRAND_CONTRAST_PAIRS;
|
|
6091
6637
|
exports.BinaryFileRef = BinaryFileRef;
|
|
6092
6638
|
exports.CodeOptions = CodeOptions;
|
|
6093
6639
|
exports.ColorOptions = ColorOptions;
|
|
@@ -6112,6 +6658,7 @@ exports.PatchBlock = PatchBlock;
|
|
|
6112
6658
|
exports.PatchGroup = PatchGroup;
|
|
6113
6659
|
exports.PatchId = PatchId;
|
|
6114
6660
|
exports.PatchJSON = PatchJSON;
|
|
6661
|
+
exports.RAMP_STEPS = RAMP_STEPS;
|
|
6115
6662
|
exports.RemirrorBr = RemirrorBr;
|
|
6116
6663
|
exports.RemirrorBulletList = RemirrorBulletList;
|
|
6117
6664
|
exports.RemirrorHeading = RemirrorHeading;
|
|
@@ -6149,10 +6696,12 @@ exports.SerializedSettingsSchema = SerializedSettingsSchema;
|
|
|
6149
6696
|
exports.SerializedStringSchema = SerializedStringSchema;
|
|
6150
6697
|
exports.SharedValConfig = SharedValConfig;
|
|
6151
6698
|
exports.VAL_ENABLE_COOKIE_NAME = VAL_ENABLE_COOKIE_NAME;
|
|
6699
|
+
exports.VAL_GREEN_RAMP = VAL_GREEN_RAMP;
|
|
6152
6700
|
exports.VAL_SESSION_COOKIE = VAL_SESSION_COOKIE;
|
|
6153
6701
|
exports.VAL_STATE_COOKIE = VAL_STATE_COOKIE;
|
|
6154
6702
|
exports.ValCommit = ValCommit;
|
|
6155
6703
|
exports.ValDeployment = ValDeployment;
|
|
6704
|
+
exports.accentRamp = accentRamp;
|
|
6156
6705
|
exports.buildDuplicatePatch = buildDuplicatePatch;
|
|
6157
6706
|
exports.buildEmptyAtPathPatch = buildEmptyAtPathPatch;
|
|
6158
6707
|
exports.buildFileRefValue = buildFileRefValue;
|
|
@@ -6162,6 +6711,7 @@ exports.buildSearchIndex = buildSearchIndex;
|
|
|
6162
6711
|
exports.checkCompatibility = checkCompatibility;
|
|
6163
6712
|
exports.chooseEncoded = chooseEncoded;
|
|
6164
6713
|
exports.concatModulePath = concatModulePath;
|
|
6714
|
+
exports.contrastRatio = contrastRatio;
|
|
6165
6715
|
exports.createRegExpFromPattern = createRegExpFromPattern;
|
|
6166
6716
|
exports.createSearchIndex = createSearchIndex;
|
|
6167
6717
|
exports.createValClient = createValClient;
|
|
@@ -6193,15 +6743,18 @@ exports.localeAt = localeAt;
|
|
|
6193
6743
|
exports.newestCommitSha = newestCommitSha;
|
|
6194
6744
|
exports.partitionValidationErrors = partitionValidationErrors;
|
|
6195
6745
|
exports.performSearch = performSearch;
|
|
6746
|
+
exports.relativeLuminance = relativeLuminance;
|
|
6196
6747
|
exports.remirrorToRichTextSource = remirrorToRichTextSource;
|
|
6197
6748
|
exports.removeModule = removeModule;
|
|
6198
6749
|
exports.resolveEncodeSettings = resolveEncodeSettings;
|
|
6750
|
+
exports.resolvePairGround = resolvePairGround;
|
|
6199
6751
|
exports.resolveSchemaSourceFixForError = resolveSchemaSourceFixForError;
|
|
6200
6752
|
exports.resolveSchemaSourceFixes = resolveSchemaSourceFixes;
|
|
6201
6753
|
exports.resolveSerializedSchemaAtPath = resolveSerializedSchemaAtPath;
|
|
6202
6754
|
exports.richTextToRemirror = richTextToRemirror;
|
|
6203
6755
|
exports.safeParsePatch = safeParsePatch;
|
|
6204
6756
|
exports.sourcePathOfChild = sourcePathOfChild;
|
|
6757
|
+
exports.themeCustomProperties = themeCustomProperties;
|
|
6205
6758
|
exports.toolNames = toolNames;
|
|
6206
6759
|
exports.traverseSchemaSource = traverseSchemaSource;
|
|
6207
6760
|
exports.urlOf = urlOf;
|