@wix/himalaya-cli 0.1044.0 → 0.1046.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 (29) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/wix-platform/src/index.d.ts +2 -2
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-platform.mjs +6 -67
  3. package/dist/cli.mjs +308 -172
  4. package/dist/preview-assets.generated.bin +0 -0
  5. package/guides/designing-himi-screens.md +5 -1
  6. package/guides/sdk-reference.json +4 -4
  7. package/guides/wix-platform-usage.md +1 -1
  8. package/package.json +2 -2
  9. package/stdlib/design-kits/README.md +8 -0
  10. package/templates/beauty-salon/tokens.json +5 -0
  11. package/templates/beauty-salon-owner/tokens.json +5 -0
  12. package/templates/community-club/tokens.json +5 -0
  13. package/templates/community-club-owner/tokens.json +5 -0
  14. package/templates/events-meetups/tokens.json +5 -0
  15. package/templates/events-meetups-owner/tokens.json +5 -0
  16. package/templates/gym-fitness/tokens.json +5 -0
  17. package/templates/gym-fitness-owner/tokens.json +5 -0
  18. package/templates/nonprofit-cause/tokens.json +5 -0
  19. package/templates/nonprofit-cause-owner/tokens.json +5 -0
  20. package/templates/podcast-creator/tokens.json +5 -0
  21. package/templates/podcast-creator-owner/tokens.json +5 -0
  22. package/templates/portfolio-creative/tokens.json +5 -0
  23. package/templates/portfolio-creative-owner/tokens.json +5 -0
  24. package/templates/restaurant/tokens.json +5 -0
  25. package/templates/restaurant-owner/tokens.json +5 -0
  26. package/templates/retail-store/tokens.json +5 -0
  27. package/templates/retail-store-owner/tokens.json +5 -0
  28. package/templates/yoga-studio/tokens.json +5 -0
  29. package/templates/yoga-studio-owner/tokens.json +5 -0
@@ -19,7 +19,7 @@ export interface ExperimentResult {
19
19
  variant: string;
20
20
  }
21
21
  export declare const experiments: {
22
- /** Conduct a Petri experiment; returns the resolved variant (or `fallback`). */
22
+ /** The app's served variant for `spec` (`himi flags`), or `fallback` when none is assigned. */
23
23
  conduct(spec: string, fallback?: string): Promise<ExperimentResult>;
24
24
  /** Boolean convenience — true when the variant is exactly "true". */
25
25
  isEnabled(spec: string): Promise<boolean>;
@@ -32,7 +32,7 @@ export declare const experiments: {
32
32
  }>;
33
33
  };
34
34
  export declare const flags: {
35
- /** Whether a Petri feature-toggle spec is ON. */
35
+ /** Whether the app's served flag `spec` is ON (variant `on`/`true`/`new`), else `fallback`. */
36
36
  isEnabled(spec: string, fallback?: boolean): Promise<boolean>;
37
37
  /** A toggle's raw value (variant string) for non-boolean specs. */
38
38
  value(spec: string, fallback?: string): Promise<string>;
@@ -62,40 +62,15 @@ function flights() {
62
62
  function dropConductFlights() {
63
63
  flights()?.inflight.clear();
64
64
  }
65
- var conductedReal = (value) => ({ value, cacheable: true });
66
- var conductedSeam = (value) => ({ value, cacheable: false });
67
- async function l2Get(ns, spec) {
68
- try {
69
- const r = await call("conductedGet", { door: ns, spec });
70
- return {
71
- value: r?.hit && typeof r.value === "string" && r.value.length > 0 ? r.value : null,
72
- scope: typeof r?.scope === "string" && r.scope.length > 0 ? r.scope : null
73
- };
74
- } catch {
75
- return { value: null, scope: null };
76
- }
77
- }
78
- async function l2Put(ns, spec, value, scope) {
79
- try {
80
- await call("conductedPut", { door: ns, spec, value, scope });
81
- } catch {
82
- }
83
- }
84
65
  async function conductCached(ns, spec, fallback, resolve) {
85
66
  const f = flights();
86
- if (!f) return (await resolve()).value;
67
+ if (!f) return resolve();
87
68
  const flightKey = JSON.stringify([ns, spec, fallback]);
88
69
  const pending = f.inflight.get(flightKey);
89
70
  if (pending) return pending;
90
71
  const task = (async () => {
91
72
  try {
92
- const { value: cached, scope } = await l2Get(ns, spec);
93
- if (cached !== null) return cached;
94
- const { value, cacheable } = await resolve();
95
- if (cacheable && value !== null && value.length > 0 && scope !== null) {
96
- await l2Put(ns, spec, value, scope);
97
- }
98
- return value;
73
+ return await resolve();
99
74
  } finally {
100
75
  f.inflight.delete(flightKey);
101
76
  }
@@ -103,31 +78,16 @@ async function conductCached(ns, spec, fallback, resolve) {
103
78
  f.inflight.set(flightKey, task);
104
79
  return task;
105
80
  }
106
- var PETRI_CONDUCT = "/_api/wix-petri-webapp/v1/conductExperiment";
107
81
  async function resolveExperiment(spec, fallback) {
108
- if (isMemberReal("experiments")) {
109
- try {
110
- const r2 = await realWix("POST", PETRI_CONDUCT, { key: spec, fallbackValue: fallback });
111
- if (typeof r2?.value === "string") return conductedReal(r2.value);
112
- } catch {
113
- }
114
- }
115
82
  const r = await call("experimentsConduct", { spec, fallback });
116
- return conductedSeam(typeof r?.variant === "string" ? r.variant : null);
83
+ return typeof r?.variant === "string" ? r.variant : null;
117
84
  }
118
85
  async function resolveFlag(spec, fallback) {
119
- if (isMemberReal("flags")) {
120
- try {
121
- const r2 = await realWix("POST", PETRI_CONDUCT, { key: spec, fallbackValue: String(fallback) });
122
- if (typeof r2?.value === "string") return conductedReal(r2.value);
123
- } catch {
124
- }
125
- }
126
86
  const r = await call("flagsIsEnabled", { spec, fallback });
127
- return conductedSeam(typeof r?.enabled === "boolean" ? String(r.enabled) : null);
87
+ return typeof r?.enabled === "boolean" ? String(r.enabled) : null;
128
88
  }
129
89
  var experiments = {
130
- /** Conduct a Petri experiment; returns the resolved variant (or `fallback`). */
90
+ /** The app's served variant for `spec` (`himi flags`), or `fallback` when none is assigned. */
131
91
  async conduct(spec, fallback = "false") {
132
92
  const resolved = await conductCached("experiments", spec, fallback, () => resolveExperiment(spec, fallback));
133
93
  return { spec, variant: resolved ?? fallback };
@@ -146,7 +106,7 @@ var experiments = {
146
106
  }
147
107
  };
148
108
  var flags = {
149
- /** Whether a Petri feature-toggle spec is ON. */
109
+ /** Whether the app's served flag `spec` is ON (variant `on`/`true`/`new`), else `fallback`. */
150
110
  async isEnabled(spec, fallback = false) {
151
111
  const resolved = await conductCached("flags", spec, String(fallback), () => resolveFlag(spec, fallback));
152
112
  if (resolved === null) return fallback;
@@ -186,25 +146,11 @@ var i18n = {
186
146
  var ping = {
187
147
  /** Register the device's push token with Wix Ping. */
188
148
  async register() {
189
- if (isMemberReal("ping")) {
190
- try {
191
- const r2 = await realWix("POST", "/_api/ping-webapp/v1/registrations", {});
192
- if (r2 && typeof r2.registered === "boolean") return { registered: r2.registered, token: r2.token ?? "" };
193
- } catch {
194
- }
195
- }
196
149
  const r = await call("pingRegister");
197
150
  return { registered: r?.registered ?? false, token: r?.token ?? "" };
198
151
  },
199
152
  /** Current Ping registration status (no network). */
200
153
  async status() {
201
- if (isMemberReal("ping")) {
202
- try {
203
- const r2 = await realWix("GET", "/_api/ping-webapp/v1/registrations");
204
- if (r2 && typeof r2.registered === "boolean") return { registered: r2.registered, token: r2.token ?? "" };
205
- } catch {
206
- }
207
- }
208
154
  const r = await call("pingStatus");
209
155
  return { registered: r?.registered ?? false, token: r?.token ?? "" };
210
156
  }
@@ -212,13 +158,6 @@ var ping = {
212
158
  var deeplink = {
213
159
  /** Resolve a deferred (post-install) deep link via Wix Jot. Null when none. */
214
160
  async resolveDeferred() {
215
- if (isMemberReal("deeplink")) {
216
- try {
217
- const r2 = await realWix("GET", "/_api/app-link-server/v1/deferred-deep-link");
218
- if (r2) return { link: r2.link && r2.link.length > 0 ? r2.link : null };
219
- } catch {
220
- }
221
- }
222
161
  const r = await call("deeplinkResolveDeferred");
223
162
  return { link: r?.link && r.link.length > 0 ? r.link : null };
224
163
  }
package/dist/cli.mjs CHANGED
@@ -13197,8 +13197,17 @@ function hexToRgb2(hex2) {
13197
13197
  return [int >> 16 & 255, int >> 8 & 255, int & 255];
13198
13198
  }
13199
13199
  function rgba(color, alpha2) {
13200
- const rgb = hexToRgb2(color);
13201
- return rgb ? `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${clamp(alpha2)})` : color;
13200
+ let h = color.trim().replace(/^#/, "");
13201
+ let own3 = 1;
13202
+ if (/^[\da-f]{4}$/i.test(h) || /^[\da-f]{8}$/i.test(h)) {
13203
+ const a = h.length === 4 ? h[3] + h[3] : h.slice(6, 8);
13204
+ own3 = parseInt(a, 16) / 255;
13205
+ h = h.length === 4 ? h.slice(0, 3) : h.slice(0, 6);
13206
+ }
13207
+ const rgb = hexToRgb2(`#${h}`);
13208
+ if (!rgb) return color;
13209
+ const composed = own3 === 1 ? clamp(alpha2) : Math.round(clamp(alpha2) * own3 * 1e3) / 1e3;
13210
+ return `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${composed})`;
13202
13211
  }
13203
13212
  function normalizeSpring(spring, durationMs) {
13204
13213
  return {
@@ -18821,6 +18830,204 @@ ${cookieHeader}`);
18821
18830
  }
18822
18831
  });
18823
18832
 
18833
+ // ../../core/ts/src/flags-engine.ts
18834
+ function bucket(key2) {
18835
+ const digest2 = sha1(key2);
18836
+ const n = (digest2[0] << 24 | digest2[1] << 16 | digest2[2] << 8 | digest2[3]) >>> 0;
18837
+ return n % 1e4;
18838
+ }
18839
+ function attrValue(ctx, attribute) {
18840
+ switch (attribute) {
18841
+ case "platform":
18842
+ return ctx.platform;
18843
+ case "appVersion":
18844
+ return ctx.appVersion;
18845
+ case "locale":
18846
+ return ctx.locale;
18847
+ case "appId":
18848
+ return ctx.appId;
18849
+ case "userId":
18850
+ return ctx.userId;
18851
+ default:
18852
+ return ctx.custom?.[attribute];
18853
+ }
18854
+ }
18855
+ function clauseMatches(ctx, c) {
18856
+ const v = attrValue(ctx, c.attribute);
18857
+ switch (c.op) {
18858
+ case "eq":
18859
+ return v !== void 0 && c.values.includes(v);
18860
+ case "neq":
18861
+ return v === void 0 || !c.values.includes(v);
18862
+ case "in":
18863
+ return v !== void 0 && c.values.includes(v);
18864
+ case "contains":
18865
+ return v !== void 0 && c.values.some((needle) => v.includes(needle));
18866
+ default:
18867
+ return false;
18868
+ }
18869
+ }
18870
+ function ruleMatches(ctx, rule) {
18871
+ return rule.clauses.length > 0 && rule.clauses.every((c) => clauseMatches(ctx, c));
18872
+ }
18873
+ function variantForBucket(def, b) {
18874
+ const total = def.variants.reduce((s, v) => s + Math.max(0, v.weight), 0);
18875
+ if (total <= 0) return def.defaultVariant;
18876
+ const target = b / 1e4 * total;
18877
+ let acc = 0;
18878
+ for (const v of def.variants) {
18879
+ acc += Math.max(0, v.weight);
18880
+ if (target < acc) return v.key;
18881
+ }
18882
+ return def.variants[def.variants.length - 1].key;
18883
+ }
18884
+ function evaluate(def, ctx, override) {
18885
+ const base = { key: def.key, experimentId: def.experiment?.id };
18886
+ if (override) return { ...base, variant: override, reason: "override" };
18887
+ if (!def.enabled) return { ...base, variant: def.defaultVariant, reason: "disabled" };
18888
+ for (const rule of def.targeting ?? []) {
18889
+ if (ruleMatches(ctx, rule)) return { ...base, variant: rule.variant, reason: "targeting" };
18890
+ }
18891
+ const id = ctx.userId ?? ctx.sessionId;
18892
+ const seed2 = def.seed ?? def.key;
18893
+ const rollout = def.rolloutPercentage ?? 100;
18894
+ if (rollout < 100) {
18895
+ const gate = bucket(`${id}:${seed2}:rollout`);
18896
+ if (gate >= rollout * 100) return { ...base, variant: def.defaultVariant, reason: "default" };
18897
+ }
18898
+ const variant = variantForBucket(def, bucket(`${id}:${seed2}`));
18899
+ return { ...base, variant, reason: "rollout" };
18900
+ }
18901
+ function evaluateAll(defs, ctx, overrides) {
18902
+ const out = {};
18903
+ for (const def of defs) out[def.key] = evaluate(def, ctx, overrides?.[def.key]);
18904
+ return out;
18905
+ }
18906
+ function utf8(input) {
18907
+ return new TextEncoder().encode(input);
18908
+ }
18909
+ function sha1(input) {
18910
+ const bytes2 = typeof input === "string" ? utf8(input) : input;
18911
+ const bitLen = bytes2.length * 8;
18912
+ const padded = new Uint8Array(((bytes2.length + 9 + 63) / 64 | 0) * 64);
18913
+ padded.set(bytes2);
18914
+ padded[bytes2.length] = 128;
18915
+ const view = new DataView(padded.buffer);
18916
+ view.setUint32(padded.length - 8, Math.floor(bitLen / 4294967296));
18917
+ view.setUint32(padded.length - 4, bitLen >>> 0);
18918
+ let h0 = 1732584193, h1 = 4023233417, h2 = 2562383102, h3 = 271733878, h4 = 3285377520;
18919
+ const w = new Uint32Array(80);
18920
+ for (let off = 0; off < padded.length; off += 64) {
18921
+ for (let i = 0; i < 16; i++) w[i] = view.getUint32(off + i * 4);
18922
+ for (let i = 16; i < 80; i++) {
18923
+ const v = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16];
18924
+ w[i] = (v << 1 | v >>> 31) >>> 0;
18925
+ }
18926
+ let a = h0, b = h1, c = h2, d = h3, e = h4;
18927
+ for (let i = 0; i < 80; i++) {
18928
+ let f;
18929
+ let k;
18930
+ if (i < 20) {
18931
+ f = b & c | ~b & d;
18932
+ k = 1518500249;
18933
+ } else if (i < 40) {
18934
+ f = b ^ c ^ d;
18935
+ k = 1859775393;
18936
+ } else if (i < 60) {
18937
+ f = b & c | b & d | c & d;
18938
+ k = 2400959708;
18939
+ } else {
18940
+ f = b ^ c ^ d;
18941
+ k = 3395469782;
18942
+ }
18943
+ const t = ((a << 5 | a >>> 27) >>> 0) + (f >>> 0) + e + k + w[i] >>> 0;
18944
+ e = d;
18945
+ d = c;
18946
+ c = (b << 30 | b >>> 2) >>> 0;
18947
+ b = a;
18948
+ a = t;
18949
+ }
18950
+ h0 = h0 + a >>> 0;
18951
+ h1 = h1 + b >>> 0;
18952
+ h2 = h2 + c >>> 0;
18953
+ h3 = h3 + d >>> 0;
18954
+ h4 = h4 + e >>> 0;
18955
+ }
18956
+ const out = new Uint8Array(20);
18957
+ const outView = new DataView(out.buffer);
18958
+ outView.setUint32(0, h0);
18959
+ outView.setUint32(4, h1);
18960
+ outView.setUint32(8, h2);
18961
+ outView.setUint32(12, h3);
18962
+ outView.setUint32(16, h4);
18963
+ return out;
18964
+ }
18965
+ var init_flags_engine = __esm({
18966
+ "../../core/ts/src/flags-engine.ts"() {
18967
+ }
18968
+ });
18969
+
18970
+ // ../../core/serve-runtime/src/flag-assignments.ts
18971
+ function bad(error) {
18972
+ return new Response(JSON.stringify({ error }), { status: 400, headers: { "content-type": JSON_CT, "cache-control": "no-store" } });
18973
+ }
18974
+ function flagAssignmentsBody(app, defs, ctx, overrides, ttlSeconds = FLAG_ASSIGNMENTS_TTL_SECONDS) {
18975
+ const evalCtx = {
18976
+ sessionId: ctx.id,
18977
+ appId: app,
18978
+ ...ctx.platform ? { platform: ctx.platform } : {},
18979
+ ...ctx.appVersion ? { appVersion: ctx.appVersion } : {},
18980
+ ...ctx.locale ? { locale: ctx.locale } : {}
18981
+ };
18982
+ const byKey = new Map(defs.map((d) => [d.key, d]));
18983
+ const assignments = {};
18984
+ for (const [key2, a] of Object.entries(evaluateAll(defs, evalCtx, overrides))) {
18985
+ const value = byKey.get(key2)?.variants.find((v) => v.key === a.variant)?.value;
18986
+ assignments[key2] = {
18987
+ variant: a.variant,
18988
+ reason: a.reason,
18989
+ ...a.experimentId ? { experimentId: a.experimentId } : {},
18990
+ ...value !== void 0 ? { value } : {}
18991
+ };
18992
+ }
18993
+ return { app, id: ctx.id, assignments, ttlSeconds };
18994
+ }
18995
+ function flagAssignmentsResponse(request, app, defs, overrides, ttlSeconds = FLAG_ASSIGNMENTS_TTL_SECONDS) {
18996
+ if (request.method !== "GET" && request.method !== "HEAD") {
18997
+ return new Response(JSON.stringify({ error: "method_not_allowed" }), { status: 405, headers: { "content-type": JSON_CT } });
18998
+ }
18999
+ const url = new URL(request.url);
19000
+ const id = url.searchParams.get("id") ?? "";
19001
+ if (!id) return bad("missing_id");
19002
+ if (id.length > MAX_ID_LENGTH) return bad("id_too_long");
19003
+ const q = (k) => url.searchParams.get(k) || void 0;
19004
+ const body = JSON.stringify(flagAssignmentsBody(app, defs.filter(isEvaluable), {
19005
+ id,
19006
+ platform: q("platform"),
19007
+ appVersion: q("appVersion"),
19008
+ locale: q("locale")
19009
+ }, overrides, ttlSeconds));
19010
+ const etag = strongEtag(body);
19011
+ const headers2 = { "content-type": JSON_CT, etag, "cache-control": `private, max-age=${Math.min(ttlSeconds, 60)}` };
19012
+ if (request.headers.get("if-none-match") === etag) return new Response(null, { status: 304, headers: headers2 });
19013
+ return new Response(request.method === "HEAD" ? null : body, { status: 200, headers: headers2 });
19014
+ }
19015
+ function isEvaluable(d) {
19016
+ const x = d;
19017
+ return !!x && typeof x.key === "string" && Array.isArray(x.variants) && typeof x.defaultVariant === "string";
19018
+ }
19019
+ var FLAG_ASSIGNMENTS_PATH, FLAG_ASSIGNMENTS_TTL_SECONDS, MAX_ID_LENGTH, JSON_CT;
19020
+ var init_flag_assignments = __esm({
19021
+ "../../core/serve-runtime/src/flag-assignments.ts"() {
19022
+ init_flags_engine();
19023
+ init_contract();
19024
+ FLAG_ASSIGNMENTS_PATH = /^\/v1\/flags\/([^/]+)\/assignments$/;
19025
+ FLAG_ASSIGNMENTS_TTL_SECONDS = 300;
19026
+ MAX_ID_LENGTH = 256;
19027
+ JSON_CT = "application/json; charset=utf-8";
19028
+ }
19029
+ });
19030
+
18824
19031
  // ../../core/dev-server/src/images.ts
18825
19032
  import { createHash as createHash11 } from "node:crypto";
18826
19033
  function svgFor(productId, title, sku) {
@@ -24966,6 +25173,7 @@ function withTheme(ctx, theme) {
24966
25173
  const base = ctx.tokens;
24967
25174
  const colors = { ...base.colors ?? {} };
24968
25175
  if (theme.colors) for (const [k, v] of Object.entries(theme.colors)) colors[k] = v;
25176
+ if (theme.colors && "accentPrimary" in theme.colors && !("accentText" in theme.colors)) delete colors.accentText;
24969
25177
  const typography = { ...base.typography ?? {} };
24970
25178
  if (theme.typography) for (const [k, v] of Object.entries(theme.typography)) typography[k] = v;
24971
25179
  const kit = { ...base.kit ?? {} };
@@ -26143,6 +26351,9 @@ function actionClick(ctx, action) {
26143
26351
  ctx.onAction(resolveItemDeep2(action, ctx));
26144
26352
  };
26145
26353
  }
26354
+ function accentTextColor(ctx) {
26355
+ return ctx.color("accentText", ctx.color("accentPrimary", "#116DFF"));
26356
+ }
26146
26357
  function actionTextButton(ctx, action, label2, color, extra) {
26147
26358
  if (action == null) return null;
26148
26359
  const onClick = actionClick(ctx, action);
@@ -29033,7 +29244,7 @@ var init_renderNode = __esm({
29033
29244
  e?.stopPropagation?.();
29034
29245
  ctx.onAction(resolveItemDeep2(props.action, ctx));
29035
29246
  } : void 0;
29036
- const fg = primary ? C("textOnAccent", "#fff") : accent;
29247
+ const fg = primary ? C("textOnAccent", "#fff") : accentTextColor(ctx);
29037
29248
  const pillRadius = props.cornerRadius != null ? props.cornerRadius : ctx.radius("pill", 24);
29038
29249
  const vPad = props.iconOnly ? 11 : props.size === "sm" ? 8 : props.size === "lg" ? 16 : 13;
29039
29250
  const hPadBase = props.iconOnly ? 11 : props.size === "sm" ? 13 : props.size === "lg" ? 22 : 16;
@@ -29102,14 +29313,14 @@ var init_renderNode = __esm({
29102
29313
  fontSize: f.remSize,
29103
29314
  background: primary ? accent : "transparent",
29104
29315
  color: fg,
29105
- border: outlined ? `1px solid ${accent}` : "none",
29316
+ border: outlined ? `1px solid ${fg}` : "none",
29106
29317
  cursor: disabled ? "default" : "pointer",
29107
29318
  opacity: disabled ? 0.55 : 1,
29108
29319
  boxSizing: "border-box",
29109
29320
  ...wrap
29110
29321
  }, rowStretch, "fit-content"),
29111
29322
  children: [
29112
- props.loading && /* @__PURE__ */ jsx6("span", { style: { width: glyph, height: glyph, borderRadius: "50%", border: `2px solid ${primary ? "rgba(255,255,255,.4)" : "rgba(17,109,255,.35)"}`, borderTopColor: fg, animation: ctx.empty ? void 0 : "himalaya-spin .7s linear infinite" } }),
29323
+ props.loading && /* @__PURE__ */ jsx6("span", { style: { width: glyph, height: glyph, borderRadius: "50%", border: `2px solid ${rgba(fg, primary ? 0.4 : 0.35)}`, borderTopColor: fg, animation: ctx.empty ? void 0 : "himalaya-spin .7s linear infinite" } }),
29113
29324
  !props.loading && resolvedLeadingIcon && ic(String(resolvedLeadingIcon)),
29114
29325
  !props.iconOnly && (resolvedTitle != null && resolvedTitle !== "" ? String(resolvedTitle) : txt(props.title, ctx) || "Button"),
29115
29326
  !props.loading && !props.iconOnly && resolvedTrailingIcon && ic(String(resolvedTrailingIcon))
@@ -32018,143 +32229,6 @@ var init_flags = __esm({
32018
32229
  }
32019
32230
  });
32020
32231
 
32021
- // ../../core/ts/src/flags-engine.ts
32022
- function bucket(key2) {
32023
- const digest2 = sha1(key2);
32024
- const n = (digest2[0] << 24 | digest2[1] << 16 | digest2[2] << 8 | digest2[3]) >>> 0;
32025
- return n % 1e4;
32026
- }
32027
- function attrValue(ctx, attribute) {
32028
- switch (attribute) {
32029
- case "platform":
32030
- return ctx.platform;
32031
- case "appVersion":
32032
- return ctx.appVersion;
32033
- case "locale":
32034
- return ctx.locale;
32035
- case "appId":
32036
- return ctx.appId;
32037
- case "userId":
32038
- return ctx.userId;
32039
- default:
32040
- return ctx.custom?.[attribute];
32041
- }
32042
- }
32043
- function clauseMatches(ctx, c) {
32044
- const v = attrValue(ctx, c.attribute);
32045
- switch (c.op) {
32046
- case "eq":
32047
- return v !== void 0 && c.values.includes(v);
32048
- case "neq":
32049
- return v === void 0 || !c.values.includes(v);
32050
- case "in":
32051
- return v !== void 0 && c.values.includes(v);
32052
- case "contains":
32053
- return v !== void 0 && c.values.some((needle) => v.includes(needle));
32054
- default:
32055
- return false;
32056
- }
32057
- }
32058
- function ruleMatches(ctx, rule) {
32059
- return rule.clauses.length > 0 && rule.clauses.every((c) => clauseMatches(ctx, c));
32060
- }
32061
- function variantForBucket(def, b) {
32062
- const total = def.variants.reduce((s, v) => s + Math.max(0, v.weight), 0);
32063
- if (total <= 0) return def.defaultVariant;
32064
- const target = b / 1e4 * total;
32065
- let acc = 0;
32066
- for (const v of def.variants) {
32067
- acc += Math.max(0, v.weight);
32068
- if (target < acc) return v.key;
32069
- }
32070
- return def.variants[def.variants.length - 1].key;
32071
- }
32072
- function evaluate(def, ctx, override) {
32073
- const base = { key: def.key, experimentId: def.experiment?.id };
32074
- if (override) return { ...base, variant: override, reason: "override" };
32075
- if (!def.enabled) return { ...base, variant: def.defaultVariant, reason: "disabled" };
32076
- for (const rule of def.targeting ?? []) {
32077
- if (ruleMatches(ctx, rule)) return { ...base, variant: rule.variant, reason: "targeting" };
32078
- }
32079
- const id = ctx.userId ?? ctx.sessionId;
32080
- const seed2 = def.seed ?? def.key;
32081
- const rollout = def.rolloutPercentage ?? 100;
32082
- if (rollout < 100) {
32083
- const gate = bucket(`${id}:${seed2}:rollout`);
32084
- if (gate >= rollout * 100) return { ...base, variant: def.defaultVariant, reason: "default" };
32085
- }
32086
- const variant = variantForBucket(def, bucket(`${id}:${seed2}`));
32087
- return { ...base, variant, reason: "rollout" };
32088
- }
32089
- function evaluateAll(defs, ctx, overrides) {
32090
- const out = {};
32091
- for (const def of defs) out[def.key] = evaluate(def, ctx, overrides?.[def.key]);
32092
- return out;
32093
- }
32094
- function utf8(input) {
32095
- return new TextEncoder().encode(input);
32096
- }
32097
- function sha1(input) {
32098
- const bytes2 = typeof input === "string" ? utf8(input) : input;
32099
- const bitLen = bytes2.length * 8;
32100
- const padded = new Uint8Array(((bytes2.length + 9 + 63) / 64 | 0) * 64);
32101
- padded.set(bytes2);
32102
- padded[bytes2.length] = 128;
32103
- const view = new DataView(padded.buffer);
32104
- view.setUint32(padded.length - 8, Math.floor(bitLen / 4294967296));
32105
- view.setUint32(padded.length - 4, bitLen >>> 0);
32106
- let h0 = 1732584193, h1 = 4023233417, h2 = 2562383102, h3 = 271733878, h4 = 3285377520;
32107
- const w = new Uint32Array(80);
32108
- for (let off = 0; off < padded.length; off += 64) {
32109
- for (let i = 0; i < 16; i++) w[i] = view.getUint32(off + i * 4);
32110
- for (let i = 16; i < 80; i++) {
32111
- const v = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16];
32112
- w[i] = (v << 1 | v >>> 31) >>> 0;
32113
- }
32114
- let a = h0, b = h1, c = h2, d = h3, e = h4;
32115
- for (let i = 0; i < 80; i++) {
32116
- let f;
32117
- let k;
32118
- if (i < 20) {
32119
- f = b & c | ~b & d;
32120
- k = 1518500249;
32121
- } else if (i < 40) {
32122
- f = b ^ c ^ d;
32123
- k = 1859775393;
32124
- } else if (i < 60) {
32125
- f = b & c | b & d | c & d;
32126
- k = 2400959708;
32127
- } else {
32128
- f = b ^ c ^ d;
32129
- k = 3395469782;
32130
- }
32131
- const t = ((a << 5 | a >>> 27) >>> 0) + (f >>> 0) + e + k + w[i] >>> 0;
32132
- e = d;
32133
- d = c;
32134
- c = (b << 30 | b >>> 2) >>> 0;
32135
- b = a;
32136
- a = t;
32137
- }
32138
- h0 = h0 + a >>> 0;
32139
- h1 = h1 + b >>> 0;
32140
- h2 = h2 + c >>> 0;
32141
- h3 = h3 + d >>> 0;
32142
- h4 = h4 + e >>> 0;
32143
- }
32144
- const out = new Uint8Array(20);
32145
- const outView = new DataView(out.buffer);
32146
- outView.setUint32(0, h0);
32147
- outView.setUint32(4, h1);
32148
- outView.setUint32(8, h2);
32149
- outView.setUint32(12, h3);
32150
- outView.setUint32(16, h4);
32151
- return out;
32152
- }
32153
- var init_flags_engine = __esm({
32154
- "../../core/ts/src/flags-engine.ts"() {
32155
- }
32156
- });
32157
-
32158
32232
  // ../../core/ts/src/launch-manifest.ts
32159
32233
  import Ajv2 from "ajv";
32160
32234
  var directiveObject, LAUNCH_MANIFEST_SCHEMA;
@@ -36220,6 +36294,23 @@ function createDevServer(opts = {}) {
36220
36294
  const k = readKit(kitM[1]);
36221
36295
  return k ? sendJson(res, 200, k, { "access-control-allow-origin": "*" }) : sendJson(res, 404, { error: "unknown_kit", id: kitM[1] }, { "access-control-allow-origin": "*" });
36222
36296
  }
36297
+ const flagAssignM = /^\/v1\/flags\/([^/?]+)\/assignments(?:\?|$)/.exec(url);
36298
+ if (flagAssignM && (req.method === "GET" || req.method === "HEAD")) {
36299
+ await state.flagSync.refresh(state.app?.dir, state.flagStore, Date.now());
36300
+ const ours = flagAssignM[1] === state.appName;
36301
+ const answer = flagAssignmentsResponse(
36302
+ new Request(`http://dev-server${url}`, {
36303
+ method: req.method,
36304
+ headers: typeof req.headers["if-none-match"] === "string" ? { "if-none-match": req.headers["if-none-match"] } : {}
36305
+ }),
36306
+ flagAssignM[1],
36307
+ ours ? state.flagStore.list() : [],
36308
+ ours ? currentFlagConfig(state)?.overrides : void 0
36309
+ );
36310
+ res.writeHead(answer.status, { ...Object.fromEntries(answer.headers), "access-control-allow-origin": "*" });
36311
+ res.end(answer.status === 200 && req.method === "GET" ? await answer.text() : void 0);
36312
+ return;
36313
+ }
36223
36314
  if (url.startsWith("/v1/revocations") && req.method === "GET") {
36224
36315
  let revoked = [];
36225
36316
  if (state.app) {
@@ -36442,6 +36533,7 @@ var init_src6 = __esm({
36442
36533
  init_wix_gateway();
36443
36534
  init_wix_member_login();
36444
36535
  init_wix_bridge();
36536
+ init_flag_assignments();
36445
36537
  init_images();
36446
36538
  init_l10n();
36447
36539
  init_l10n_catalog();
@@ -49454,7 +49546,7 @@ var init_wix_member_auth = __esm({
49454
49546
 
49455
49547
  // ../../core/serve/src/server.ts
49456
49548
  function json3(status, body, headers2 = {}) {
49457
- return new Response(JSON.stringify(body), { status, headers: { "content-type": JSON_CT, ...headers2 } });
49549
+ return new Response(JSON.stringify(body), { status, headers: { "content-type": JSON_CT2, ...headers2 } });
49458
49550
  }
49459
49551
  function isCookieAuthorizedPath(path2) {
49460
49552
  return path2 === "/v1/__webwix/call" || path2 === "/v1/owner" || path2.startsWith("/v1/owner/") || path2 === "/v1/__webwix/identity" || path2 === "/v1/__webwix/login" || path2 === "/v1/__webwix/register" || path2 === "/v1/__webwix/logout" || path2 === MEMBER_CALLBACK_PATH;
@@ -49614,7 +49706,7 @@ ${releaseId}`;
49614
49706
  if (request.headers.get("if-none-match") === etag) {
49615
49707
  return new Response(null, { status: 304, headers: { etag, "cache-control": cache, ...vary } });
49616
49708
  }
49617
- return new Response(body, { status: 200, headers: { "content-type": JSON_CT, etag, "cache-control": cache, ...vary } });
49709
+ return new Response(body, { status: 200, headers: { "content-type": JSON_CT2, etag, "cache-control": cache, ...vary } });
49618
49710
  }
49619
49711
  async function authorizePinnedRead(request, url, app, releaseId, screenId) {
49620
49712
  const denied = await checkPinnedRead({
@@ -49809,7 +49901,7 @@ ${releaseId}`;
49809
49901
  });
49810
49902
  }
49811
49903
  if ((request.method === "GET" || request.method === "HEAD") && path2 === "/v1/__webwix/identity") {
49812
- const headers2 = { "content-type": JSON_CT, "cache-control": "no-store" };
49904
+ const headers2 = { "content-type": JSON_CT2, "cache-control": "no-store" };
49813
49905
  if (request.method === "HEAD") return new Response(null, { status: 200, headers: headers2 });
49814
49906
  const webwixDirect = opts.webwixDirect ? await opts.webwixDirect() === true : opts.webwixBridge !== false;
49815
49907
  if (memberAuthOn()) {
@@ -50037,7 +50129,7 @@ ${releaseId}`;
50037
50129
  }
50038
50130
  return new Response(listBody, {
50039
50131
  status: 200,
50040
- headers: { "content-type": JSON_CT, ...listHeaders }
50132
+ headers: { "content-type": JSON_CT2, ...listHeaders }
50041
50133
  });
50042
50134
  }
50043
50135
  if (path2 === "/v1/templates") {
@@ -50094,7 +50186,7 @@ ${releaseId}`;
50094
50186
  }
50095
50187
  return new Response(bytes2, {
50096
50188
  status: 200,
50097
- headers: { "content-type": JSON_CT, etag, ...cache, "x-himalaya-app": app }
50189
+ headers: { "content-type": JSON_CT2, etag, ...cache, "x-himalaya-app": app }
50098
50190
  });
50099
50191
  }
50100
50192
  if (path2 === "/v1/authoring-docs" || /^\/v1\/authoring-docs\/(spec|mobile-ux)$/.test(path2)) {
@@ -50359,7 +50451,7 @@ ${releaseId}`;
50359
50451
  if (request.headers.get("if-none-match") === etag2) return new Response(null, { status: 304, headers: headers2 });
50360
50452
  const body2 = await pinnedBody(app, meta2.descriptorHash, () => store.getScreen(app, screenId));
50361
50453
  if (!body2) return json3(404, { error: "screen_not_found", screenId });
50362
- return new Response(body2, { status: 200, headers: { "content-type": JSON_CT, ...headers2, "x-himalaya-app": app } });
50454
+ return new Response(body2, { status: 200, headers: { "content-type": JSON_CT2, ...headers2, "x-himalaya-app": app } });
50363
50455
  }
50364
50456
  const m = await manifest(app);
50365
50457
  const meta = m?.screens[screenId];
@@ -50372,7 +50464,7 @@ ${releaseId}`;
50372
50464
  if (!body) return json3(404, { error: "screen_not_found", screenId });
50373
50465
  return new Response(body, {
50374
50466
  status: 200,
50375
- headers: { "content-type": JSON_CT, etag, "cache-control": DOC_CACHE_CONTROL, "x-himalaya-app": app }
50467
+ headers: { "content-type": JSON_CT2, etag, "cache-control": DOC_CACHE_CONTROL, "x-himalaya-app": app }
50376
50468
  });
50377
50469
  }
50378
50470
  if (path2 === "/v1/widgets") {
@@ -50400,7 +50492,7 @@ ${releaseId}`;
50400
50492
  if (!body) return json3(404, { error: "widget_not_found", widgetId });
50401
50493
  return new Response(body, {
50402
50494
  status: 200,
50403
- headers: { "content-type": JSON_CT, etag, "cache-control": cache, ...vary, "x-himalaya-app": app }
50495
+ headers: { "content-type": JSON_CT2, etag, "cache-control": cache, ...vary, "x-himalaya-app": app }
50404
50496
  });
50405
50497
  }
50406
50498
  return json3(404, { error: "not_found" });
@@ -50427,7 +50519,7 @@ ${releaseId}`;
50427
50519
  return withCors(request, response);
50428
50520
  };
50429
50521
  }
50430
- var JSON_CT;
50522
+ var JSON_CT2;
50431
50523
  var init_server = __esm({
50432
50524
  "../../core/serve/src/server.ts"() {
50433
50525
  init_sha256();
@@ -50438,7 +50530,7 @@ var init_server = __esm({
50438
50530
  init_package_share();
50439
50531
  init_wix_bridge();
50440
50532
  init_wix_member_auth();
50441
- JSON_CT = "application/json; charset=utf-8";
50533
+ JSON_CT2 = "application/json; charset=utf-8";
50442
50534
  }
50443
50535
  });
50444
50536
 
@@ -50619,6 +50711,8 @@ function makeRunHandler(opts) {
50619
50711
  { "cache-control": "max-age=30" }
50620
50712
  );
50621
50713
  }
50714
+ const flagAssign = FLAG_ASSIGNMENTS_PATH.exec(path2);
50715
+ if (flagAssign) return flagAssignmentsResponse(request, flagAssign[1], []);
50622
50716
  if (path2.startsWith("/v1/flags/")) {
50623
50717
  return json4(200, { app: opts.app, definitions: {} }, { "cache-control": "no-store" });
50624
50718
  }
@@ -50690,6 +50784,7 @@ var json4;
50690
50784
  var init_run_handler = __esm({
50691
50785
  "../../core/serve/src/run-handler.ts"() {
50692
50786
  init_server();
50787
+ init_flag_assignments();
50693
50788
  init_preview2();
50694
50789
  init_himi_package();
50695
50790
  json4 = (status, body, headers2 = {}) => new Response(JSON.stringify(body), {
@@ -50828,21 +50923,21 @@ var init_preview_assets_generated = __esm({
50828
50923
  "../../packages/serve/src/preview-assets.generated.ts"() {
50829
50924
  init_preview_assets_format();
50830
50925
  PREVIEW_ASSET_MANIFEST = [
50831
- { key: "assets/index-i1HVt_7j.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1051636, sha256: "cfb282567ed9de1491cb9d91f16c329e6560a6809da79cca4be3b35bc5b4b161" },
50832
- { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1051644, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
50833
- { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1056254, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
50834
- { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1544838, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
50835
- { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1635762, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
50836
- { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1726746, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
50837
- { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1817726, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
50838
- { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1908598, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
50839
- { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1999578, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
50840
- { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2108214, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
50841
- { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2217298, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
50842
- { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2326346, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
50843
- { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2434046, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
50844
- { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2522154, length: 4947, sha256: "3c696c7f99affdf0305da31e2e6a6355a33d87ce9676a62ddea33babcd95f978" },
50845
- { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2527101, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
50926
+ { key: "assets/index-DT6bRpY_.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1055972, sha256: "3a52c8395a6509cfe2cae8036024b987cd3361095ff5450bfd7a9efa0123850b" },
50927
+ { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1055980, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
50928
+ { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1060590, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
50929
+ { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1549174, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
50930
+ { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1640098, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
50931
+ { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1731082, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
50932
+ { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1822062, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
50933
+ { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1912934, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
50934
+ { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 2003914, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
50935
+ { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2112550, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
50936
+ { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2221634, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
50937
+ { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2330682, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
50938
+ { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2438382, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
50939
+ { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2526490, length: 4947, sha256: "a9dcd2ec33b7ea5cee46b0a1f37f926678dd6939b442e3d50bff0101f0fe63ba" },
50940
+ { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2531437, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
50846
50941
  ];
50847
50942
  }
50848
50943
  });
@@ -54487,6 +54582,35 @@ function tuneHighContrastAccent(brand, surface) {
54487
54582
  }
54488
54583
  return candidate;
54489
54584
  }
54585
+ function deriveAccentText(accent, surfaces) {
54586
+ const all = (typeof surfaces === "string" ? [surfaces] : [...surfaces]).map((s) => s.slice(0, 7));
54587
+ const page = all[0];
54588
+ const worst = (color) => Math.min(...all.map((s) => contrastRatio3(color, s)));
54589
+ const opaque = accent.length === 9 ? mix2(accent.slice(0, 7), page, channel2(accent, 7) / 255) : accent;
54590
+ if (worst(opaque) >= ACCENT_TEXT_MIN_CONTRAST) return opaque;
54591
+ const pole = worst("#FFFFFF") >= worst("#000000") ? "#FFFFFF" : "#000000";
54592
+ for (let step = 1; step <= 100; step++) {
54593
+ const candidate = mix2(pole, opaque, step / 100);
54594
+ if (worst(candidate) >= ACCENT_TEXT_MIN_CONTRAST) return candidate;
54595
+ }
54596
+ if (all.length > 1) {
54597
+ const side2 = (hex2) => relativeLuminance2(hex2) > MID_GREY_LUMINANCE;
54598
+ const sameSide = all.filter((s) => side2(s) === side2(page));
54599
+ return deriveAccentText(accent, sameSide.length < all.length ? sameSide : [page]);
54600
+ }
54601
+ return pole;
54602
+ }
54603
+ function textSurfacesOf(colors) {
54604
+ const out = { light: [], dark: [], highContrast: [] };
54605
+ for (const name of TEXT_BACKDROP_TOKENS) {
54606
+ const value = colors[name];
54607
+ for (const arm of ["light", "dark", "highContrast"]) {
54608
+ const hex2 = typeof value === "string" ? value : value && typeof value === "object" && !Array.isArray(value) ? value[arm] ?? value.light : void 0;
54609
+ if (typeof hex2 === "string" && hex2.startsWith("#") && hex2.length >= 7) out[arm].push(hex2);
54610
+ }
54611
+ }
54612
+ return out;
54613
+ }
54490
54614
  function stripBrandFromIdiom(tokens) {
54491
54615
  const idiom = tokens.idiom;
54492
54616
  if (!idiom || typeof idiom !== "object" || Array.isArray(idiom)) return;
@@ -54507,7 +54631,7 @@ function stripBrandFromIdiom(tokens) {
54507
54631
  }
54508
54632
  }
54509
54633
  }
54510
- function deriveAccentFamily(primary, secondary, surfaces) {
54634
+ function deriveAccentFamily(primary, secondary, surfaces, textSurfaces = {}) {
54511
54635
  const primaryDark = tuneDarkAccent(primary, surfaces.dark, surfaces.light);
54512
54636
  const secondaryDark = tuneDarkAccent(secondary, surfaces.dark, surfaces.light);
54513
54637
  const primaryHigh = tuneHighContrastAccent(primary, surfaces.highContrast);
@@ -54524,10 +54648,15 @@ function deriveAccentFamily(primary, secondary, surfaces) {
54524
54648
  light: textOnArm(primary),
54525
54649
  dark: textOnArm(primaryDark),
54526
54650
  highContrast: textOn(primaryHigh)
54651
+ },
54652
+ accentText: {
54653
+ light: deriveAccentText(primary, [surfaces.light, ...textSurfaces.light ?? []]),
54654
+ dark: deriveAccentText(primaryDark, [surfaces.dark, ...textSurfaces.dark ?? []]),
54655
+ highContrast: deriveAccentText(primaryHigh, [surfaces.highContrast, ...textSurfaces.highContrast ?? []])
54527
54656
  }
54528
54657
  };
54529
54658
  }
54530
- var ACCENT_SURFACE_MIN_DELTA, ACCENT_SURFACE_LIGHT_WEIGHT, SECONDARY_SHADE_WEIGHT, SECONDARY_MIN_LSTAR, DARK_ARM_MIN_LIFT, DARK_ARM_TARGET_CONTRAST, DARK_ARM_FLOOR_CONTRAST, DARK_ARM_LSTAR_CAP, HIGH_CONTRAST_TARGET, BRAND_COLOR_TOKENS, BRAND_GLOW_SHADOW;
54659
+ var ACCENT_SURFACE_MIN_DELTA, ACCENT_SURFACE_LIGHT_WEIGHT, SECONDARY_SHADE_WEIGHT, SECONDARY_MIN_LSTAR, DARK_ARM_MIN_LIFT, DARK_ARM_TARGET_CONTRAST, DARK_ARM_FLOOR_CONTRAST, DARK_ARM_LSTAR_CAP, HIGH_CONTRAST_TARGET, ACCENT_TEXT_MIN_CONTRAST, MID_GREY_LUMINANCE, TEXT_BACKDROP_TOKENS, BRAND_COLOR_TOKENS, BRAND_GLOW_SHADOW;
54531
54660
  var init_brand_color_math = __esm({
54532
54661
  "../../core/ts/src/brand-color-math.ts"() {
54533
54662
  ACCENT_SURFACE_MIN_DELTA = 1.08;
@@ -54539,7 +54668,10 @@ var init_brand_color_math = __esm({
54539
54668
  DARK_ARM_FLOOR_CONTRAST = 3;
54540
54669
  DARK_ARM_LSTAR_CAP = 80;
54541
54670
  HIGH_CONTRAST_TARGET = 8.5;
54542
- BRAND_COLOR_TOKENS = ["accentPrimary", "accentSecondary", "accentSurface", "textOnAccent"];
54671
+ ACCENT_TEXT_MIN_CONTRAST = 4.5;
54672
+ MID_GREY_LUMINANCE = 0.179;
54673
+ TEXT_BACKDROP_TOKENS = ["surfaceSecondary", "surfaceElevated", "surfaceSubtle", "surfaceTertiary"];
54674
+ BRAND_COLOR_TOKENS = ["accentPrimary", "accentSecondary", "accentSurface", "textOnAccent", "accentText"];
54543
54675
  BRAND_GLOW_SHADOW = "glow";
54544
54676
  }
54545
54677
  });
@@ -54575,7 +54707,9 @@ function withDerivedAccents(tokensOverlay, wst) {
54575
54707
  light: lightArm2(surfacePrimary) ?? "#FFFFFF",
54576
54708
  dark: armOf(surfacePrimary, "dark") ?? "#0D1117",
54577
54709
  highContrast: armOf(surfacePrimary, "highContrast") ?? "#000000"
54578
- });
54710
+ // #3275 — the crawl knows only the site's LIGHT surfaces; a light card judged against a dark
54711
+ // arm would fight that arm's page, and `--apply` re-derives every arm after the merge anyway.
54712
+ }, { light: textSurfacesOf(tokensOverlay.colors ?? {}).light });
54579
54713
  const keepCrawledLight = (derived, existing) => {
54580
54714
  const crawled = lightArm2(existing);
54581
54715
  return crawled ? { ...derived, light: crawled } : derived;
@@ -54584,6 +54718,7 @@ function withDerivedAccents(tokensOverlay, wst) {
54584
54718
  colors.accentSecondary = keepCrawledLight(family.accentSecondary, tokensOverlay.colors?.accentSecondary);
54585
54719
  colors.accentSurface = keepCrawledLight(family.accentSurface, tokensOverlay.colors?.accentSurface);
54586
54720
  colors.textOnAccent = keepCrawledLight(family.textOnAccent, tokensOverlay.colors?.textOnAccent);
54721
+ colors.accentText = keepCrawledLight(family.accentText, tokensOverlay.colors?.accentText);
54587
54722
  return { ...tokensOverlay, colors };
54588
54723
  }
54589
54724
  function mergedArm(colors, name, arm, fallback) {
@@ -54606,11 +54741,12 @@ function alignAccentsToSurfaces(mergedColors, overlayColors) {
54606
54741
  light: mergedArm(colors, "surfacePrimary", "light", "#FFFFFF"),
54607
54742
  dark: mergedArm(colors, "surfacePrimary", "dark", "#0D1117"),
54608
54743
  highContrast: mergedArm(colors, "surfacePrimary", "highContrast", "#000000")
54609
- });
54744
+ }, textSurfacesOf(colors));
54610
54745
  colors.accentPrimary = family.accentPrimary;
54611
54746
  colors.accentSecondary = family.accentSecondary;
54612
54747
  colors.accentSurface = family.accentSurface;
54613
54748
  colors.textOnAccent = family.textOnAccent;
54749
+ colors.accentText = family.accentText;
54614
54750
  return colors;
54615
54751
  }
54616
54752
  function deriveBranding(input) {
Binary file
@@ -83,7 +83,11 @@ family whose faces could not be fetched, which would otherwise ship as its fallb
83
83
  ### Token discipline — every value is a `TokenRef`, never a literal
84
84
  Colors, gradients, shadows, materials, motions are named tokens (`{ token: "accentPrimary" }`), never
85
85
  hex. `himi validate` rejects unknown names; a literal hex is a design smell even when it "works." Use the **semantic** roles for meaning:
86
- `success` / `warning` / `danger` for status, `textOnAccent` for text sitting on an accent surface.
86
+ `success` / `warning` / `danger` for status, `textOnAccent` for text sitting on an accent surface,
87
+ and `accentText` for accent-COLOURED text on the page (links, "See all"). `accentPrimary` is the
88
+ accent FILL; many brand accents fail 4.5:1 as text, so brand derivation writes a same-hue
89
+ `accentText` that passes, and secondary/tertiary/link/plain Button labels already use it. It is
90
+ optional: every renderer falls back to `accentPrimary` when a tokens.json has none.
87
91
 
88
92
  ### Copy is configuration — never hardcoded in the descriptor
89
93
  User-facing text is white-labelable **configuration**, exactly like a color token — a template/app
@@ -3463,13 +3463,13 @@
3463
3463
  "name": "experiments",
3464
3464
  "kind": "const",
3465
3465
  "jsdoc": "",
3466
- "decl": "export const experiments = {\n /** Conduct a Petri experiment; returns the resolved variant (or `fallback`). */\n async conduct(spec: string, fallback = \"false\"): Promise<ExperimentResult> {\n const resolved = await conductCached(\"experiments\", spec, fallback, () => resolveExperiment(spec, fallback));\n return { spec, variant: resolved ?? fallback };\n },\n /** Boolean convenience — true when the variant is exactly \"true\". */\n async isEnabled(spec: string): Promise<boolean> {\n return (await experiments.conduct(spec, \"false\")).variant === \"true\";\n },\n /** Report A/B exposure for `spec`/`variant` — emits the platform's\n * `experiment_exposed` telemetry so outcome events attribute to the variant.\n * (Symphony relies on server-side conduction; this is the generalized app-side\n * exposure hook.) */\n async exposure(spec: string, variant: string): Promise<{ ok: boolean }> {\n const r = await call<{ ok?: boolean }>(\"experimentsExposure\", { spec, variant });\n return { ok: r?.ok ?? false };\n },\n};"
3466
+ "decl": "export const experiments = {\n /** The app's served variant for `spec` (`himi flags`), or `fallback` when none is assigned. */\n async conduct(spec: string, fallback = \"false\"): Promise<ExperimentResult> {\n const resolved = await conductCached(\"experiments\", spec, fallback, () => resolveExperiment(spec, fallback));\n return { spec, variant: resolved ?? fallback };\n },\n /** Boolean convenience — true when the variant is exactly \"true\". */\n async isEnabled(spec: string): Promise<boolean> {\n return (await experiments.conduct(spec, \"false\")).variant === \"true\";\n },\n /** Report A/B exposure for `spec`/`variant` — emits the platform's\n * `experiment_exposed` telemetry so outcome events attribute to the variant.\n * (Symphony relies on server-side conduction; this is the generalized app-side\n * exposure hook.) */\n async exposure(spec: string, variant: string): Promise<{ ok: boolean }> {\n const r = await call<{ ok?: boolean }>(\"experimentsExposure\", { spec, variant });\n return { ok: r?.ok ?? false };\n },\n};"
3467
3467
  },
3468
3468
  {
3469
3469
  "name": "flags",
3470
3470
  "kind": "const",
3471
3471
  "jsdoc": "",
3472
- "decl": "export const flags = {\n /** Whether a Petri feature-toggle spec is ON. */\n async isEnabled(spec: string, fallback = false): Promise<boolean> {\n const resolved = await conductCached(\"flags\", spec, String(fallback), () => resolveFlag(spec, fallback));\n if (resolved === null) return fallback;\n return resolved === \"true\" || resolved === \"new\";\n },\n /** A toggle's raw value (variant string) for non-boolean specs. */\n async value(spec: string, fallback = \"\"): Promise<string> {\n const r = await call<{ value?: string }>(\"flagsValue\", { spec, fallback });\n return r?.value ?? fallback;\n },\n /** All known toggle states (dev/diagnostic). */\n async all(): Promise<Record<string, string>> {\n const r = await call<{ flags?: Record<string, string> }>(\"flagsAll\");\n return r?.flags && typeof r.flags === \"object\" ? r.flags : {};\n },\n};"
3472
+ "decl": "export const flags = {\n /** Whether the app's served flag `spec` is ON (variant `on`/`true`/`new`), else `fallback`. */\n async isEnabled(spec: string, fallback = false): Promise<boolean> {\n const resolved = await conductCached(\"flags\", spec, String(fallback), () => resolveFlag(spec, fallback));\n if (resolved === null) return fallback;\n return resolved === \"true\" || resolved === \"new\";\n },\n /** A toggle's raw value (variant string) for non-boolean specs. */\n async value(spec: string, fallback = \"\"): Promise<string> {\n const r = await call<{ value?: string }>(\"flagsValue\", { spec, fallback });\n return r?.value ?? fallback;\n },\n /** All known toggle states (dev/diagnostic). */\n async all(): Promise<Record<string, string>> {\n const r = await call<{ flags?: Record<string, string> }>(\"flagsAll\");\n return r?.flags && typeof r.flags === \"object\" ? r.flags : {};\n },\n};"
3473
3473
  },
3474
3474
  {
3475
3475
  "name": "i18n",
@@ -3487,13 +3487,13 @@
3487
3487
  "name": "ping",
3488
3488
  "kind": "const",
3489
3489
  "jsdoc": "",
3490
- "decl": "export const ping = {\n /** Register the device's push token with Wix Ping. */\n async register(): Promise<PingRegistration> {\n if (isMemberReal(\"ping\")) {\n try {\n const r = await realWix<{ registered?: boolean; token?: string }>(\"POST\", \"/_api/ping-webapp/v1/registrations\", {});\n if (r && typeof r.registered === \"boolean\") return { registered: r.registered, token: r.token ?? \"\" };\n } catch { /* fall through to the dev seam */ }\n }\n const r = await call<{ registered?: boolean; token?: string }>(\"pingRegister\");\n return { registered: r?.registered ?? false, token: r?.token ?? \"\" };\n },\n /** Current Ping registration status (no network). */\n async status(): Promise<PingRegistration> {\n if (isMemberReal(\"ping\")) {\n try {\n const r = await realWix<{ registered?: boolean; token?: string }>(\"GET\", \"/_api/ping-webapp/v1/registrations\");\n if (r && typeof r.registered === \"boolean\") return { registered: r.registered, token: r.token ?? \"\" };\n } catch { /* fall through to the dev seam */ }\n }\n const r = await call<{ registered?: boolean; token?: string }>(\"pingStatus\");\n return { registered: r?.registered ?? false, token: r?.token ?? \"\" };\n },\n};"
3490
+ "decl": "export const ping = {\n /** Register the device's push token with Wix Ping. */\n async register(): Promise<PingRegistration> {\n const r = await call<{ registered?: boolean; token?: string }>(\"pingRegister\");\n return { registered: r?.registered ?? false, token: r?.token ?? \"\" };\n },\n /** Current Ping registration status (no network). */\n async status(): Promise<PingRegistration> {\n const r = await call<{ registered?: boolean; token?: string }>(\"pingStatus\");\n return { registered: r?.registered ?? false, token: r?.token ?? \"\" };\n },\n};"
3491
3491
  },
3492
3492
  {
3493
3493
  "name": "deeplink",
3494
3494
  "kind": "const",
3495
3495
  "jsdoc": "",
3496
- "decl": "export const deeplink = {\n /** Resolve a deferred (post-install) deep link via Wix Jot. Null when none. */\n async resolveDeferred(): Promise<{ link: string | null }> {\n if (isMemberReal(\"deeplink\")) {\n try {\n const r = await realWix<{ link?: string }>(\"GET\", \"/_api/app-link-server/v1/deferred-deep-link\");\n if (r) return { link: r.link && r.link.length > 0 ? r.link : null };\n } catch { /* fall through to the dev seam */ }\n }\n const r = await call<{ link?: string }>(\"deeplinkResolveDeferred\");\n return { link: r?.link && r.link.length > 0 ? r.link : null };\n },\n};"
3496
+ "decl": "export const deeplink = {\n /** Resolve a deferred (post-install) deep link via Wix Jot. Null when none. */\n async resolveDeferred(): Promise<{ link: string | null }> {\n const r = await call<{ link?: string }>(\"deeplinkResolveDeferred\");\n return { link: r?.link && r.link.length > 0 ? r.link : null };\n },\n};"
3497
3497
  },
3498
3498
  {
3499
3499
  "name": "SignedInstanceStatus",
@@ -26,7 +26,7 @@ import { bi, experiments, flags, i18n, auth, monitoring } from "@himalaya/wix-pl
26
26
  await bi.pageView("home");
27
27
  await bi.click("cta_start", "home");
28
28
 
29
- // experiments (Petri) / flags
29
+ // experiments / flags — the app's served flags (`himi flags`), never Wix Petri
30
30
  const variant = await experiments.conduct("specs.ai", "false"); // → { spec, variant }
31
31
  if (await flags.isEnabled("specs.newCheckout")) { /* … */ }
32
32
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1044.0",
3
+ "version": "0.1046.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -65,5 +65,5 @@
65
65
  "artifactId": "himalaya-cli"
66
66
  }
67
67
  },
68
- "falconPackageHash": "7c2534e837382316a3416a0e6225572fbc86b02c8b5b35cea3d7530b"
68
+ "falconPackageHash": "920a07437cf238c0bf1e34b799a4fe38573b57c6da92541fc2836079"
69
69
  }
@@ -126,6 +126,14 @@ elevation subtle (≤ 2:1 vs the page, dark arms stepping *lighter* as elevation
126
126
  rises); accents ≥ 3:1 on the page with textOnAccent ≥ 4.5:1; semantic colors
127
127
  ≥ 3:1 on the page and on their own surfaces.
128
128
 
129
+ `accentText` (#3275) is deliberately NOT in the ladder. It is the accent's text
130
+ arm, which non-filled button labels paint and which falls back to `accentPrimary`
131
+ when absent. Brand application derives it per arm: the brand itself when it already
132
+ clears 4.5:1 on `surfacePrimary`, else the smallest same-hue step that does
133
+ (`deriveAccentText` in `core/ts/src/brand-color-math.ts`). A kit never has to ship
134
+ it, and `core/ts/tests/brand-tokens.test.ts` checks that every kit × brand result
135
+ clears 4.5:1.
136
+
129
137
  Deliberate judgment calls (the 2026-07 color reconsideration):
130
138
 
131
139
  - **playful** — the hot-pink `textPrimary` failed 7:1 everywhere; legibility
@@ -94,6 +94,11 @@
94
94
  "light": "#E3E3DF",
95
95
  "dark": "#262626",
96
96
  "highContrast": "#FFFFFF"
97
+ },
98
+ "accentText": {
99
+ "light": "#9C5853",
100
+ "dark": "#BA837E",
101
+ "highContrast": "#CA9E9B"
97
102
  }
98
103
  },
99
104
  "spacing": {
@@ -90,6 +90,11 @@
90
90
  "dangerSurface": {
91
91
  "light": "#F6E9E7",
92
92
  "dark": "#241312"
93
+ },
94
+ "accentText": {
95
+ "light": "#8E3D6B",
96
+ "dark": "#B47D9C",
97
+ "highContrast": "#C49AB2"
93
98
  }
94
99
  },
95
100
  "spacing": {
@@ -98,6 +98,11 @@
98
98
  "pushShadow": {
99
99
  "light": "#D61F6E",
100
100
  "dark": "#7E1044"
101
+ },
102
+ "accentText": {
103
+ "light": "#6C3FC4",
104
+ "dark": "#9E80D8",
105
+ "highContrast": "#B39BE0"
101
106
  }
102
107
  },
103
108
  "spacing": {
@@ -94,6 +94,11 @@
94
94
  "pushShadow": {
95
95
  "light": "#D61F6E",
96
96
  "dark": "#7E1044"
97
+ },
98
+ "accentText": {
99
+ "light": "#4C4BC4",
100
+ "dark": "#8787D7",
101
+ "highContrast": "#A2A1E0"
97
102
  }
98
103
  },
99
104
  "spacing": {
@@ -94,6 +94,11 @@
94
94
  "light": "#DCE2F5",
95
95
  "dark": "#262C42",
96
96
  "highContrast": "#FFFFFF"
97
+ },
98
+ "accentText": {
99
+ "light": "#5B3E96",
100
+ "dark": "#9987BE",
101
+ "highContrast": "#B0A2CD"
97
102
  }
98
103
  },
99
104
  "spacing": {
@@ -90,6 +90,11 @@
90
90
  "dangerSurface": {
91
91
  "light": "#FBE5E7",
92
92
  "dark": "#2B131A"
93
+ },
94
+ "accentText": {
95
+ "light": "#1A6BA1",
96
+ "dark": "#5594BE",
97
+ "highContrast": "#7FAECE"
93
98
  }
94
99
  },
95
100
  "spacing": {
@@ -94,6 +94,11 @@
94
94
  "light": "#000000",
95
95
  "dark": "#FFFFFF",
96
96
  "highContrast": "#FFFFFF"
97
+ },
98
+ "accentText": {
99
+ "light": "#A84A29",
100
+ "dark": "#CF7A5D",
101
+ "highContrast": "#DA9982"
97
102
  }
98
103
  },
99
104
  "spacing": {
@@ -90,6 +90,11 @@
90
90
  "dangerSurface": {
91
91
  "light": "#FFD6D6",
92
92
  "dark": "#291111"
93
+ },
94
+ "accentText": {
95
+ "light": "#B43F0F",
96
+ "dark": "#DE6D3F",
97
+ "highContrast": "#E69371"
93
98
  }
94
99
  },
95
100
  "spacing": {
@@ -94,6 +94,11 @@
94
94
  "light": "#CAC4D0",
95
95
  "dark": "#49454F",
96
96
  "highContrast": "#FFFFFF"
97
+ },
98
+ "accentText": {
99
+ "light": "#0F6B5C",
100
+ "dark": "#5E9C92",
101
+ "highContrast": "#82B2AA"
97
102
  }
98
103
  },
99
104
  "spacing": {
@@ -90,6 +90,11 @@
90
90
  "dangerSurface": {
91
91
  "light": "#F9DEDC",
92
92
  "dark": "#33191E"
93
+ },
94
+ "accentText": {
95
+ "light": "#2B7349",
96
+ "dark": "#5F9A77",
97
+ "highContrast": "#82B195"
93
98
  }
94
99
  },
95
100
  "spacing": {
@@ -94,6 +94,11 @@
94
94
  "light": "#262B36",
95
95
  "dark": "#262B36",
96
96
  "highContrast": "#FFFFFF"
97
+ },
98
+ "accentText": {
99
+ "light": "#8B6FFF",
100
+ "dark": "#957CFF",
101
+ "highContrast": "#AE9AFF"
97
102
  }
98
103
  },
99
104
  "spacing": {
@@ -90,6 +90,11 @@
90
90
  "dangerSurface": {
91
91
  "light": "#221214",
92
92
  "dark": "#221214"
93
+ },
94
+ "accentText": {
95
+ "light": "#9474E1",
96
+ "dark": "#9A7CE2",
97
+ "highContrast": "#B49DEA"
93
98
  }
94
99
  },
95
100
  "spacing": {
@@ -94,6 +94,11 @@
94
94
  "light": "#E3E3DF",
95
95
  "dark": "#262626",
96
96
  "highContrast": "#FFFFFF"
97
+ },
98
+ "accentText": {
99
+ "light": "#AE501F",
100
+ "dark": "#C57F5A",
101
+ "highContrast": "#D39E82"
97
102
  }
98
103
  },
99
104
  "spacing": {
@@ -90,6 +90,11 @@
90
90
  "dangerSurface": {
91
91
  "light": "#F6E9E7",
92
92
  "dark": "#241312"
93
+ },
94
+ "accentText": {
95
+ "light": "#945639",
96
+ "dark": "#B4836C",
97
+ "highContrast": "#C49D8A"
93
98
  }
94
99
  },
95
100
  "spacing": {
@@ -94,6 +94,11 @@
94
94
  "light": "#262B36",
95
95
  "dark": "#262B36",
96
96
  "highContrast": "#FFFFFF"
97
+ },
98
+ "accentText": {
99
+ "light": "#C36E65",
100
+ "dark": "#C87B74",
101
+ "highContrast": "#D49892"
97
102
  }
98
103
  },
99
104
  "spacing": {
@@ -98,6 +98,11 @@
98
98
  "glossBottom": {
99
99
  "light": "#AEB6C0",
100
100
  "dark": "#1B1F26"
101
+ },
102
+ "accentText": {
103
+ "light": "#922637",
104
+ "dark": "#D1929B",
105
+ "highContrast": "#D499A2"
101
106
  }
102
107
  },
103
108
  "spacing": {
@@ -94,6 +94,11 @@
94
94
  "light": "#CAC4D0",
95
95
  "dark": "#49454F",
96
96
  "highContrast": "#FFFFFF"
97
+ },
98
+ "accentText": {
99
+ "light": "#AB512B",
100
+ "dark": "#C67E5F",
101
+ "highContrast": "#D29981"
97
102
  }
98
103
  },
99
104
  "spacing": {
@@ -90,6 +90,11 @@
90
90
  "dangerSurface": {
91
91
  "light": "#FEF2F2",
92
92
  "dark": "#241214"
93
+ },
94
+ "accentText": {
95
+ "light": "#16697A",
96
+ "dark": "#5F98A4",
97
+ "highContrast": "#86B1BA"
93
98
  }
94
99
  },
95
100
  "spacing": {
@@ -102,6 +102,11 @@
102
102
  "neumoDark": {
103
103
  "light": "#AEB5C0",
104
104
  "dark": "#17191D"
105
+ },
106
+ "accentText": {
107
+ "light": "#566856",
108
+ "dark": "#92A093",
109
+ "highContrast": "#9FABA0"
105
110
  }
106
111
  },
107
112
  "spacing": {
@@ -98,6 +98,11 @@
98
98
  "neumoDark": {
99
99
  "light": "#AEB5C0",
100
100
  "dark": "#17191D"
101
+ },
102
+ "accentText": {
103
+ "light": "#536A47",
104
+ "dark": "#8CA380",
105
+ "highContrast": "#9CB092"
101
106
  }
102
107
  },
103
108
  "spacing": {