@wix/himalaya-cli 0.1020.0 → 0.1021.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.
@@ -208,6 +208,13 @@ export interface PreviewSurface {
208
208
  * phone and tablet are always offered.
209
209
  */
210
210
  readonly platformKey: string | null;
211
+ /**
212
+ * RFC 0059 / #2927 — the native builds that draw this surface, which is what a null
213
+ * `platformKey` leaves implicit. The web host offers each one's idiom as a preview
214
+ * (`IDIOM_CONTEXTS`: `ios` wears `ios`, `android` wears `material`), and side by side.
215
+ * Absent ⇒ none: the surface previews in its own look only.
216
+ */
217
+ readonly idiomPlatforms?: readonly ("ios" | "android")[];
211
218
  readonly gate: SurfaceGate;
212
219
  }
213
220
  /**
@@ -9232,7 +9232,8 @@ var PREVIEW_SURFACES = [
9232
9232
  // iPhone 14/15 logical points
9233
9233
  orientations: ["portrait"],
9234
9234
  platformKey: null,
9235
- // iOS/Android are implicit
9235
+ // iOS/Android are implicit — named explicitly in idiomPlatforms
9236
+ idiomPlatforms: ["ios", "android"],
9236
9237
  gate: { kind: "none" }
9237
9238
  },
9238
9239
  {
@@ -9247,6 +9248,7 @@ var PREVIEW_SURFACES = [
9247
9248
  // iPad Pro 11" logical points
9248
9249
  orientations: ["portrait", "landscape"],
9249
9250
  platformKey: null,
9251
+ idiomPlatforms: ["ios", "android"],
9250
9252
  gate: { kind: "none" }
9251
9253
  },
9252
9254
  {
@@ -7661,7 +7661,8 @@ var PREVIEW_SURFACES = [
7661
7661
  // iPhone 14/15 logical points
7662
7662
  orientations: ["portrait"],
7663
7663
  platformKey: null,
7664
- // iOS/Android are implicit
7664
+ // iOS/Android are implicit — named explicitly in idiomPlatforms
7665
+ idiomPlatforms: ["ios", "android"],
7665
7666
  gate: { kind: "none" }
7666
7667
  },
7667
7668
  {
@@ -7676,6 +7677,7 @@ var PREVIEW_SURFACES = [
7676
7677
  // iPad Pro 11" logical points
7677
7678
  orientations: ["portrait", "landscape"],
7678
7679
  platformKey: null,
7680
+ idiomPlatforms: ["ios", "android"],
7679
7681
  gate: { kind: "none" }
7680
7682
  },
7681
7683
  {
package/dist/cli.mjs CHANGED
@@ -2127,7 +2127,8 @@ var init_preview_surfaces = __esm({
2127
2127
  // iPhone 14/15 logical points
2128
2128
  orientations: ["portrait"],
2129
2129
  platformKey: null,
2130
- // iOS/Android are implicit
2130
+ // iOS/Android are implicit — named explicitly in idiomPlatforms
2131
+ idiomPlatforms: ["ios", "android"],
2131
2132
  gate: { kind: "none" }
2132
2133
  },
2133
2134
  {
@@ -2142,6 +2143,7 @@ var init_preview_surfaces = __esm({
2142
2143
  // iPad Pro 11" logical points
2143
2144
  orientations: ["portrait", "landscape"],
2144
2145
  platformKey: null,
2146
+ idiomPlatforms: ["ios", "android"],
2145
2147
  gate: { kind: "none" }
2146
2148
  },
2147
2149
  {
@@ -12687,23 +12689,130 @@ function resolveIdiom(tokens, ctx = {}) {
12687
12689
  const { idiomPolicy: _applied, ...final } = merged;
12688
12690
  return final;
12689
12691
  }
12690
- var UNSAFE_KEYS;
12692
+ var IDIOM_CONTEXTS, UNSAFE_KEYS;
12691
12693
  var init_idiom = __esm({
12692
12694
  "../../core/ts/src/idiom.ts"() {
12693
12695
  "use strict";
12696
+ IDIOM_CONTEXTS = {
12697
+ ios: { native: ["ios"] },
12698
+ android: { native: ["material"] },
12699
+ mac: { native: ["desktop", "ios"], surface: "mac" }
12700
+ };
12694
12701
  UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
12695
12702
  }
12696
12703
  });
12697
12704
 
12705
+ // ../../core/web-renderer/src/idiom-controls.ts
12706
+ function pickRole(colors, role) {
12707
+ if (typeof role === "string") return role;
12708
+ return role.find((name) => colors?.[name] != null) ?? role[role.length - 1];
12709
+ }
12710
+ function controlIdiomFor(context) {
12711
+ for (const name of context?.native ?? []) {
12712
+ if (name !== "base" && Object.hasOwn(IDIOM_CONTROLS, name)) return name;
12713
+ }
12714
+ return "base";
12715
+ }
12716
+ function idiomControls(idiom) {
12717
+ return idiom != null && Object.hasOwn(IDIOM_CONTROLS, idiom) ? IDIOM_CONTROLS[idiom] : IDIOM_CONTROLS.base;
12718
+ }
12719
+ var STOCK_ROLES, FILLED_SEGMENT_ROLES, IDIOM_CONTROLS;
12720
+ var init_idiom_controls = __esm({
12721
+ "../../core/web-renderer/src/idiom-controls.ts"() {
12722
+ STOCK_ROLES = { onThumb: null, offTrack: "surfaceTertiary", offThumb: null, outline: null };
12723
+ FILLED_SEGMENT_ROLES = { outline: "border", inactive: "surfaceTertiary" };
12724
+ IDIOM_CONTROLS = {
12725
+ // The web's own look — today's values, unchanged.
12726
+ base: {
12727
+ font: { defaultStack: '"Wix Madefor Text", system-ui', system: null, bundled: null, faithfulOn: "any" },
12728
+ switch: { width: 51, height: 31, thumbOn: 27, thumbOff: 27, outline: 0, roles: STOCK_ROLES, thumbShadow: true },
12729
+ segmented: { height: 50, trackPadding: 3, trackRadius: null, segmentRadius: null, outlined: false, selectedCheck: false, roles: FILLED_SEGMENT_ROLES },
12730
+ sheet: { cornerRadius: 16, grabberWidth: 36, grabberHeight: 5 },
12731
+ navBar: { rootTitle: "inline", pushedTitle: "inline", back: "chevron", inlineTitleSize: 15, largeTitleSize: 15, hideWithStickyChrome: false },
12732
+ press: { button: "none", tappable: "none", highlightOpacity: 1, stateLayerOpacity: 0 }
12733
+ },
12734
+ // iOS: the stock UIKit / SwiftUI controls. The switch is MEASURED against
12735
+ // HimalayaDemoAppSnapshotTests' gallery_inputs golden (51×31, 27pt thumb, 2pt inset).
12736
+ ios: {
12737
+ // SF Pro reaches a page only as the Apple system font (`-apple-system`); it is not
12738
+ // redistributable as a webfont, so elsewhere this frame falls back and says so.
12739
+ font: { defaultStack: '-apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", sans-serif', system: '-apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", sans-serif', bundled: null, faithfulOn: "apple" },
12740
+ switch: { width: 51, height: 31, thumbOn: 27, thumbOff: 27, outline: 0, roles: STOCK_ROLES, thumbShadow: true },
12741
+ // UISegmentedControl: 32pt tall, a 2pt-inset selected segment inside a filled track.
12742
+ segmented: { height: 32, trackPadding: 2, trackRadius: 9, segmentRadius: 7, outlined: false, selectedCheck: false, roles: FILLED_SEGMENT_ROLES },
12743
+ // UISheetPresentationController: a 36×5 grabber and 10pt top corners (iOS 15–18).
12744
+ sheet: { cornerRadius: 10, grabberWidth: 36, grabberHeight: 5 },
12745
+ // NavigationStack: a large title on the root screen, inline once pushed, and NO bar when the
12746
+ // screen pins sticky chrome (HimalayaScreenHost.swift `RootTitleDisplayMode`,
12747
+ // `HideNavigationBarWhen(hasStickyChrome)`). UINavigationBar: 17pt semibold inline, 34pt
12748
+ // bold large.
12749
+ navBar: { rootTitle: "large", pushedTitle: "inline", back: "chevron", inlineTitleSize: 17, largeTitleSize: 34, hideWithStickyChrome: true },
12750
+ // A Button is `.buttonStyle(.borderless)`, which dims its content while pressed; any other
12751
+ // tappable is `.onTapGesture` (SDUIRenderer.swift `TapGestureIfPresent`), which shows
12752
+ // nothing. The dim is approximate — no golden captures a pressed state.
12753
+ press: { button: "highlight", tappable: "none", highlightOpacity: 0.3, stateLayerOpacity: 0 }
12754
+ },
12755
+ // Android: the stock Material 3 controls. The switch's ON state is MEASURED against the
12756
+ // Paparazzi gallery_inputs golden (52×32 track, 24dp thumb, 4dp inset); its OFF state —
12757
+ // 16dp thumb, 2dp outline — is Compose Material 3's SwitchTokens (no golden shows one).
12758
+ material: {
12759
+ // Roboto is the font catalog's variable face (100–900); the web host bundles that exact file
12760
+ // (index.html @font-face), so the frame is faithful on any OS, offline included.
12761
+ font: {
12762
+ defaultStack: "Roboto, sans-serif",
12763
+ system: "Roboto, sans-serif",
12764
+ bundled: { family: "Roboto", file: "fonts/Roboto-Variable.ttf", sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
12765
+ faithfulOn: "any"
12766
+ },
12767
+ switch: {
12768
+ width: 52,
12769
+ height: 32,
12770
+ thumbOn: 24,
12771
+ thumbOff: 16,
12772
+ outline: 2,
12773
+ thumbShadow: false,
12774
+ roles: {
12775
+ onThumb: "textOnAccent",
12776
+ offTrack: ["surfaceElevated", "surfaceSecondary", "surfacePrimary"],
12777
+ offThumb: ["border", "surfaceTertiary", "surfaceSecondary", "surfacePrimary"],
12778
+ outline: ["border", "surfaceTertiary", "surfaceSecondary", "surfacePrimary"]
12779
+ }
12780
+ },
12781
+ // SegmentedButton (SDUIComponents.kt `SingleChoiceSegmentedButtonRow`): 40dp, pill-ended
12782
+ // adjoining outlined buttons, no inset track.
12783
+ segmented: {
12784
+ height: 40,
12785
+ trackPadding: 0,
12786
+ trackRadius: "pill",
12787
+ segmentRadius: "pill",
12788
+ outlined: true,
12789
+ selectedCheck: true,
12790
+ roles: { outline: ["border", "surfaceTertiary", "surfaceSecondary", "surfacePrimary"], inactive: ["surfaceTertiary", "surfaceSecondary", "surfacePrimary"] }
12791
+ },
12792
+ // ModalBottomSheet + BottomSheetDefaults.DragHandle: a 32×4 handle, 28dp top corners.
12793
+ sheet: { cornerRadius: 28, grabberWidth: 32, grabberHeight: 4 },
12794
+ // HimalayaAppShell draws NO title bar — a floating ArrowBack IconButton, the title unused.
12795
+ // The floating control reserves a top band in the content's flow unless the screen is
12796
+ // immersive (ScreenView.kt `reservesBand`), so nothing slides under it.
12797
+ navBar: { rootTitle: "none", pushedTitle: "none", back: "arrow", inlineTitleSize: 0, largeTitleSize: 0, hideWithStickyChrome: false },
12798
+ // M3 Buttons and `Modifier.clickable` (every `onTap`, SDUIRenderer.kt) ripple. The web draws
12799
+ // the M3 pressed state layer (10% of the content colour), not the expanding circle.
12800
+ press: { button: "ripple", tappable: "ripple", highlightOpacity: 1, stateLayerOpacity: 0.1 }
12801
+ }
12802
+ };
12803
+ }
12804
+ });
12805
+
12698
12806
  // ../../core/web-renderer/src/tokens.ts
12699
12807
  function isSerifFamily(family) {
12700
12808
  return !!family && SERIF_FAMILY_RE.test(family);
12701
12809
  }
12702
- function fontStack(family) {
12703
- if (!family) return DEFAULT_FONT_STACK;
12704
- if (family === SYSTEM_FONT_FAMILY) return SYSTEM_FONT_STACK;
12810
+ function fontStack(family, idiom) {
12811
+ const fallback = idiom ? idiomControls(idiom).font.defaultStack : DEFAULT_FONT_STACK;
12812
+ if (!family) return fallback;
12813
+ if (family === SYSTEM_FONT_FAMILY) return (idiom ? idiomControls(idiom).font.system : null) ?? SYSTEM_FONT_STACK;
12705
12814
  if (isSerifFamily(family)) return `"${family}", serif`;
12706
- return `"${family}", ${DEFAULT_FONT_STACK}`;
12815
+ return `"${family}", ${fallback}`;
12707
12816
  }
12708
12817
  function clamp(n, lo = 0, hi = 1) {
12709
12818
  return Math.max(lo, Math.min(hi, n));
@@ -13054,6 +13163,7 @@ var REM_ROOT_PX, pxToRem, DEFAULT_FONT_STACK, SYSTEM_FONT_STACK, SERIF_FAMILY_RE
13054
13163
  var init_tokens2 = __esm({
13055
13164
  "../../core/web-renderer/src/tokens.ts"() {
13056
13165
  init_idiom();
13166
+ init_idiom_controls();
13057
13167
  init_tokens();
13058
13168
  REM_ROOT_PX = 16;
13059
13169
  pxToRem = (px) => `${px / REM_ROOT_PX}rem`;
@@ -17195,7 +17305,17 @@ async function wixVisitorCall(method, path2, body, token, fetchImpl = fetch, for
17195
17305
  });
17196
17306
  return { status: res.status, body: await res.text() };
17197
17307
  }
17198
- function normalizeWixForwardedHeaders(input) {
17308
+ function isChatWebTarget(path2) {
17309
+ if (!path2 || !/^https:\/\//i.test(path2)) return false;
17310
+ try {
17311
+ const url = new URL(path2);
17312
+ return url.host === "manage.wix.com" && url.pathname.startsWith("/_api/chat-web/");
17313
+ } catch {
17314
+ return false;
17315
+ }
17316
+ }
17317
+ function normalizeWixForwardedHeaders(input, path2) {
17318
+ const chatWeb = isChatWebTarget(path2);
17199
17319
  let value = input;
17200
17320
  if (typeof value === "string") {
17201
17321
  try {
@@ -17205,10 +17325,12 @@ function normalizeWixForwardedHeaders(input) {
17205
17325
  }
17206
17326
  }
17207
17327
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
17208
- const blocked = /* @__PURE__ */ new Set(["authorization", "cookie", "host", "content-length", "content-type", "connection"]);
17209
17328
  const out = {};
17210
17329
  for (const [name, headerValue] of Object.entries(value)) {
17211
- if (!/^[a-z0-9-]+$/i.test(name) || blocked.has(name.toLowerCase()) || typeof headerValue !== "string") continue;
17330
+ const lower = name.toLowerCase();
17331
+ const allowed2 = WIX_FORWARDABLE_HEADERS.has(lower) || chatWeb && lower === "chattoken";
17332
+ if (!/^[a-z0-9-]+$/i.test(name) || !allowed2 || typeof headerValue !== "string") continue;
17333
+ if (/[\r\n\0]/.test(headerValue)) continue;
17212
17334
  out[name] = headerValue;
17213
17335
  }
17214
17336
  return out;
@@ -17247,7 +17369,7 @@ function wixClientIdOf(appConfigJson) {
17247
17369
  if (typeof clientId !== "string" || !clientId) return { error: "no_client_id" };
17248
17370
  return { clientId };
17249
17371
  }
17250
- var WIX_API, ONE_APP_SESSION, OWNER_SCOPED_WIX_HOST, VisitorTokenCache, OWNER_REFRESH_MARGIN_MS, OWNER_RENEW_MIN_AGE_MS, OWNER_MINT_TIMEOUT_MS, OwnerInstanceCache, OWNER_SITES_QUERY_PATH, OWNER_SITES_COUNT_PATH, OwnerSitesCache;
17372
+ var WIX_API, ONE_APP_SESSION, OWNER_SCOPED_WIX_HOST, VisitorTokenCache, WIX_FORWARDABLE_HEADERS, OWNER_REFRESH_MARGIN_MS, OWNER_RENEW_MIN_AGE_MS, OWNER_MINT_TIMEOUT_MS, OwnerInstanceCache, OWNER_SITES_QUERY_PATH, OWNER_SITES_COUNT_PATH, OwnerSitesCache;
17251
17373
  var init_wix_bridge = __esm({
17252
17374
  "../../core/serve-runtime/src/wix-bridge.ts"() {
17253
17375
  init_sha256();
@@ -17284,6 +17406,22 @@ var init_wix_bridge = __esm({
17284
17406
  this.cache.delete(clientId);
17285
17407
  }
17286
17408
  };
17409
+ WIX_FORWARDABLE_HEADERS = /* @__PURE__ */ new Set([
17410
+ "x-wix-language",
17411
+ "x-wix-linguist",
17412
+ "wixlanguage",
17413
+ "accept-language",
17414
+ "x-wix-currency",
17415
+ "x-wix-consistent",
17416
+ "x-wix-client-api-revision",
17417
+ "x-wix-site-revision",
17418
+ "x-wix-bi-gateway",
17419
+ "x-wix-brand",
17420
+ "consent-policy",
17421
+ "commonconfig",
17422
+ "x-wix-client-artifact-id",
17423
+ "wix-site-id"
17424
+ ]);
17287
17425
  OWNER_REFRESH_MARGIN_MS = 6e4;
17288
17426
  OWNER_RENEW_MIN_AGE_MS = 5e3;
17289
17427
  OWNER_MINT_TIMEOUT_MS = 8e3;
@@ -22426,6 +22564,7 @@ var init_controls = __esm({
22426
22564
  "use strict";
22427
22565
  init_helpers();
22428
22566
  init_tokens2();
22567
+ init_idiom_controls();
22429
22568
  init_bindings2();
22430
22569
  init_icons();
22431
22570
  init_formBag();
@@ -22434,7 +22573,7 @@ var init_controls = __esm({
22434
22573
  const r = useResolvedScalar(typeof v === "string" ? v : void 0, ctx);
22435
22574
  return r != null ? String(r) : void 0;
22436
22575
  };
22437
- FONT = (ctx) => fontStack(ctx.font("body").family);
22576
+ FONT = (ctx) => fontStack(ctx.font("body").family, ctx.idiom);
22438
22577
  rowShareFloor = (style, rowStretch, floor) => rowStretch ? { ...style, minWidth: floor } : style;
22439
22578
  A11ySwitch = ({ props, ctx, wrap }) => {
22440
22579
  const C = ctx.color;
@@ -22463,11 +22602,18 @@ var init_controls = __esm({
22463
22602
  bag.register(fieldId, props.validation);
22464
22603
  return () => bag.unregister(fieldId);
22465
22604
  }, [fieldId, bag, ruleKey]);
22466
- const w = typeof props.width === "number" ? props.width : 51;
22467
- const h = typeof props.height === "number" ? props.height : 31;
22468
- const tSize = Math.max(0, h - 4);
22469
- const trackColor = on3 ? C(props.trackOn ?? "accentPrimary", "#116DFF") : C(props.trackOff ?? "surfaceTertiary", "#ddd");
22470
- const thumbColor = props.thumb ? C(props.thumb, "#fff") : "#fff";
22605
+ const hasAppearance = props.trackOn != null || props.trackOff != null || props.thumb != null || props.width != null || props.height != null;
22606
+ const shape = idiomControls(hasAppearance ? void 0 : ctx.idiom).switch;
22607
+ const w = typeof props.width === "number" ? props.width : shape.width;
22608
+ const h = typeof props.height === "number" ? props.height : shape.height;
22609
+ const tSize = hasAppearance ? Math.max(0, h - 4) : Math.max(0, on3 ? shape.thumbOn : shape.thumbOff);
22610
+ const roles = shape.roles;
22611
+ const role = (r) => pickRole(ctx.tokens.colors, r);
22612
+ const trackColor = on3 ? C(props.trackOn ?? "accentPrimary", "#116DFF") : C(props.trackOff ?? role(roles.offTrack), "#ddd");
22613
+ const thumbRole = on3 ? roles.onThumb : roles.offThumb;
22614
+ const thumbColor = props.thumb ? C(props.thumb, "#fff") : thumbRole ? C(role(thumbRole), "#fff") : "#fff";
22615
+ const outlineWidth = !on3 && roles.outline ? shape.outline : 0;
22616
+ const thumbInset = (h - tSize) / 2 - outlineWidth;
22471
22617
  const act = props.onChange ?? props.action;
22472
22618
  const canFire = !props.disabled && Boolean(ctx.onAction) && (inForm || Boolean(act));
22473
22619
  const fire = canFire ? () => {
@@ -22492,8 +22638,8 @@ var init_controls = __esm({
22492
22638
  "aria-label": props.label != null ? asText2(props.label) : void 0,
22493
22639
  disabled: props.disabled === true,
22494
22640
  onClick: fire,
22495
- style: { all: "unset", boxSizing: "border-box", width: w, height: h, borderRadius: 999, background: trackColor, position: "relative", flex: "none", cursor: fire ? "pointer" : "default", display: "inline-block", outlineOffset: 2 },
22496
- children: /* @__PURE__ */ jsx4("span", { "aria-hidden": true, style: { position: "absolute", top: 2, [on3 ? "right" : "left"]: 2, width: tSize, height: tSize, borderRadius: "50%", background: thumbColor, boxShadow: "0 1px 3px rgba(0,0,0,.3)" } })
22641
+ style: { all: "unset", boxSizing: "border-box", width: w, height: h, borderRadius: 999, background: trackColor, ...outlineWidth > 0 ? { border: `${outlineWidth}px solid ${C(role(roles.outline), "#79747e")}` } : {}, position: "relative", flex: "none", cursor: fire ? "pointer" : "default", display: "inline-block", outlineOffset: 2 },
22642
+ children: /* @__PURE__ */ jsx4("span", { "aria-hidden": true, style: { position: "absolute", top: thumbInset, [on3 ? "right" : "left"]: thumbInset, width: tSize, height: tSize, borderRadius: "50%", background: thumbColor, boxShadow: shape.thumbShadow ? "0 1px 3px rgba(0,0,0,.3)" : "none" } })
22497
22643
  }
22498
22644
  )
22499
22645
  ] });
@@ -22656,6 +22802,8 @@ var init_controls = __esm({
22656
22802
  };
22657
22803
  A11ySegmented = ({ props, ctx, wrap }) => {
22658
22804
  const C = ctx.color;
22805
+ const seg = idiomControls(ctx.idiom).segmented;
22806
+ const radius = (r, token, fallback) => r === null ? ctx.radius(token, fallback) : r === "pill" ? 999 : r;
22659
22807
  const optionsPath = props.optionsPath ?? props.options_path;
22660
22808
  const boundOpts = useBoundValue(ctx, typeof optionsPath === "string" && isBinding(optionsPath) ? optionsPath : "");
22661
22809
  const resolvedSel = useResolvedId(props.selectedId, ctx);
@@ -22675,9 +22823,15 @@ var init_controls = __esm({
22675
22823
  e.preventDefault();
22676
22824
  if (ni !== selIdx) select(ni);
22677
22825
  };
22678
- return /* @__PURE__ */ jsx4("div", { role: "radiogroup", onKeyDown, style: { display: "flex", background: C("surfaceTertiary", "#eee"), borderRadius: ctx.radius("md", 8), padding: 3, width: "100%", boxSizing: "border-box", ...wrap }, children: opts.map((o, i) => {
22826
+ return /* @__PURE__ */ jsx4("div", { role: "radiogroup", onKeyDown, style: { display: "flex", background: seg.outlined ? "transparent" : C("surfaceTertiary", "#eee"), borderRadius: radius(seg.trackRadius, "md", 8), padding: seg.trackPadding, width: "100%", boxSizing: "border-box", ...wrap }, children: opts.map((o, i) => {
22679
22827
  const on3 = o.id === selId;
22680
- return /* @__PURE__ */ jsx4(
22828
+ const outerR = radius(seg.segmentRadius, "sm", 6);
22829
+ const outlinedShape = seg.outlined ? {
22830
+ border: `1px solid ${C(pickRole(ctx.tokens.colors, seg.roles.outline), "#d8dee4")}`,
22831
+ marginLeft: i > 0 ? -1 : 0,
22832
+ borderRadius: `${i === 0 ? outerR : 0}px ${i === opts.length - 1 ? outerR : 0}px ${i === opts.length - 1 ? outerR : 0}px ${i === 0 ? outerR : 0}px`
22833
+ } : {};
22834
+ return /* @__PURE__ */ jsxs(
22681
22835
  "button",
22682
22836
  {
22683
22837
  type: "button",
@@ -22685,8 +22839,11 @@ var init_controls = __esm({
22685
22839
  "aria-checked": on3,
22686
22840
  tabIndex: on3 || selId == null && i === 0 ? 0 : -1,
22687
22841
  onClick: () => select(i),
22688
- style: { all: "unset", flex: 1, minWidth: 44, minHeight: 44, textAlign: "center", padding: "7px 4px", borderRadius: ctx.radius("sm", 6), fontFamily: FONT(ctx), fontWeight: 600, fontSize: 13, cursor: "pointer", boxSizing: "border-box", background: on3 ? C("surfacePrimary", "#fff") : "transparent", color: on3 ? C("textPrimary", "#111") : C("textSecondary", "#777"), boxShadow: on3 ? "0 1px 3px rgba(0,0,0,.12)" : "none" },
22689
- children: asText2(o.label ?? o.title ?? o.id) || `Option ${i + 1}`
22842
+ style: { all: "unset", flex: 1, minWidth: 44, minHeight: seg.height - 2 * seg.trackPadding, textAlign: "center", padding: "7px 4px", borderRadius: outerR, fontFamily: FONT(ctx), fontWeight: 600, fontSize: 13, cursor: "pointer", boxSizing: "border-box", background: on3 ? C("surfacePrimary", "#fff") : seg.outlined ? C(pickRole(ctx.tokens.colors, seg.roles.inactive), "#eee") : "transparent", color: on3 ? C("textPrimary", "#111") : C("textSecondary", "#777"), boxShadow: on3 && !seg.outlined ? "0 1px 3px rgba(0,0,0,.12)" : "none", ...outlinedShape },
22843
+ children: [
22844
+ on3 && seg.selectedCheck ? /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", children: "\u2713 " }) : null,
22845
+ asText2(o.label ?? o.title ?? o.id) || `Option ${i + 1}`
22846
+ ]
22690
22847
  },
22691
22848
  o.id ?? i
22692
22849
  );
@@ -23346,7 +23503,7 @@ function seededWidth(measured, seed2) {
23346
23503
  return measured || (typeof seed2 === "number" && seed2 > 0 ? seed2 : 0);
23347
23504
  }
23348
23505
  function makeCtx(tokens, scheme = "light", opts = {}) {
23349
- const tk = makeTokenResolvers(tokens, scheme);
23506
+ const tk = makeTokenResolvers(tokens, scheme, opts.idiomContext);
23350
23507
  const provider = opts.provider ?? null;
23351
23508
  return {
23352
23509
  ...tk,
@@ -23369,7 +23526,9 @@ function makeCtx(tokens, scheme = "light", opts = {}) {
23369
23526
  emitTelemetry: opts.emitTelemetry,
23370
23527
  assetBaseUrl: opts.assetBaseUrl,
23371
23528
  runtimeDegraded: opts.runtimeDegraded,
23372
- previewSurface: opts.previewSurface
23529
+ previewSurface: opts.previewSurface,
23530
+ idiomContext: opts.idiomContext,
23531
+ idiom: controlIdiomFor(opts.idiomContext)
23373
23532
  };
23374
23533
  }
23375
23534
  function resolveAssetUrl(url, base) {
@@ -23393,7 +23552,7 @@ function withTheme(ctx, theme) {
23393
23552
  radius: { ...base.radius ?? {}, ...theme.radius ?? {} },
23394
23553
  spacing: { ...base.spacing ?? {}, ...theme.spacing ?? {} }
23395
23554
  };
23396
- const themed = makeCtx(merged, ctx.scheme, { provider: ctx.provider, onAction: ctx.onAction, intents: ctx.intents, empty: ctx.empty, screenId: ctx.screenId, emitTelemetry: ctx.emitTelemetry, previewSurface: ctx.previewSurface });
23555
+ const themed = makeCtx(merged, ctx.scheme, { provider: ctx.provider, onAction: ctx.onAction, intents: ctx.intents, empty: ctx.empty, screenId: ctx.screenId, emitTelemetry: ctx.emitTelemetry, previewSurface: ctx.previewSurface, idiomContext: ctx.idiomContext });
23397
23556
  themed.itemScope = ctx.itemScope;
23398
23557
  return themed;
23399
23558
  }
@@ -23410,9 +23569,29 @@ function ensurePressStyles() {
23410
23569
  const el = document.createElement("style");
23411
23570
  el.textContent = // WS-N3 — the duration/easing read from per-node CSS vars (set when a
23412
23571
  // `pressMotion` token is present) with the original 160ms/standard-ease default.
23413
- '[data-himalaya-press]{transition:transform var(--hm-press-dur,160ms) var(--hm-press-ease,cubic-bezier(0.2,0,0,1)),filter var(--hm-press-dur,160ms) var(--hm-press-ease,cubic-bezier(0.2,0,0,1))}[data-himalaya-press="scale"]:active,[data-himalaya-press="auto"]:active,[data-himalaya-press="scaleHighlight"]:active{transform:scale(0.96)}[data-himalaya-press="scaleHighlight"]:active,[data-himalaya-press="highlight"]:active{filter:brightness(1.08)}[data-himalaya-press="lift"]:active{transform:translateY(-4px) scale(1.02)}@media (hover:hover){[data-himalaya-tap]{transition:filter 120ms ease}[data-himalaya-tap]:hover{filter:brightness(0.97)}[data-himalaya-press="scale"]:hover,[data-himalaya-press="auto"]:hover,[data-himalaya-press="scaleHighlight"]:hover{transform:scale(0.985)}[data-himalaya-press="lift"]:hover{transform:translateY(-2px) scale(1.01)}}';
23572
+ '[data-himalaya-press]{transition:transform var(--hm-press-dur,160ms) var(--hm-press-ease,cubic-bezier(0.2,0,0,1)),filter var(--hm-press-dur,160ms) var(--hm-press-ease,cubic-bezier(0.2,0,0,1))}[data-himalaya-press="scale"]:active,[data-himalaya-press="auto"]:active,[data-himalaya-press="scaleHighlight"]:active{transform:scale(0.96)}[data-himalaya-press="scaleHighlight"]:active,[data-himalaya-press="highlight"]:active{filter:brightness(1.08)}[data-himalaya-press="lift"]:active{transform:translateY(-4px) scale(1.02)}' + idiomPressRules() + // RFC 0051 track D — desktop hover analogs. Guarded by `(hover: hover)` so a
23573
+ // touch device never sees a sticky-hover state; pointer-only affordance for
23574
+ // the real web app. Tap-bearing nodes get a subtle dim (the hover analog of
23575
+ // pressFeedback's dim family); press-styled nodes preview their :active
23576
+ // recipe at reduced amplitude.
23577
+ '@media (hover:hover){[data-himalaya-tap]{transition:filter 120ms ease}[data-himalaya-tap]:hover{filter:brightness(0.97)}[data-himalaya-press="scale"]:hover,[data-himalaya-press="auto"]:hover,[data-himalaya-press="scaleHighlight"]:hover{transform:scale(0.985)}[data-himalaya-press="lift"]:hover{transform:translateY(-2px) scale(1.01)}}';
23414
23578
  document.head.appendChild(el);
23415
23579
  }
23580
+ function idiomPressRules() {
23581
+ let css = "";
23582
+ for (const [name, row] of Object.entries(IDIOM_CONTROLS)) {
23583
+ const layer = `color-mix(in srgb,currentColor ${Math.round(row.press.stateLayerOpacity * 100)}%,transparent)`;
23584
+ css += `[data-himalaya-idiom-press="${name}-highlight"]:active{opacity:${row.press.highlightOpacity}!important}`;
23585
+ css += `[data-himalaya-idiom-press="${name}-ripple"]:active{background-image:linear-gradient(${layer},${layer})!important}`;
23586
+ }
23587
+ return css;
23588
+ }
23589
+ function idiomPress(ctx, part) {
23590
+ const kind = idiomControls(ctx.idiom).press[part];
23591
+ if (kind === "none") return void 0;
23592
+ ensurePressStyles();
23593
+ return `${ctx.idiom}-${kind}`;
23594
+ }
23416
23595
  function ensureFocusStyles() {
23417
23596
  if (focusStylesInjected || typeof document === "undefined") return;
23418
23597
  focusStylesInjected = true;
@@ -24683,6 +24862,8 @@ function renderNodeInner(node, ctx) {
24683
24862
  }
24684
24863
  ensurePressStyles();
24685
24864
  extra["data-himalaya-tap"] = "1";
24865
+ const idiomPressed = idiomPress(ctx, "tappable");
24866
+ if (idiomPressed) extra["data-himalaya-idiom-press"] = idiomPressed;
24686
24867
  }
24687
24868
  const dtap = node?.modifiers?.onDoubleTap ?? node?.modifiers?.on_double_tap;
24688
24869
  if (dtap && ctx.onAction) {
@@ -25064,7 +25245,7 @@ function renderInner(node, ctx) {
25064
25245
  return /* @__PURE__ */ jsx6("div", { style: { fontFamily: bodyFont(ctx), lineHeight: 1.5, overflowWrap: "break-word", color: C(resolveScalarStatic(props.color, ctx) ?? "textPrimary", "#13171e"), ...wrap }, children: runs.map((r, i) => {
25065
25246
  const marks = Array.isArray(r.marks) ? r.marks : [];
25066
25247
  const f = ctx.font(r.style);
25067
- return /* @__PURE__ */ jsx6("span", { style: { fontSize: f.remSize, fontWeight: marks.includes("bold") ? 700 : f.weight, fontStyle: marks.includes("italic") ? "italic" : void 0, textDecoration: marks.includes("underline") ? "underline" : void 0, ...f.family ? { fontFamily: fontStack(f.family) } : {}, ...r.color ? { color: C(r.color) } : {}, ...marks.includes("code") ? { fontFamily: "ui-monospace, monospace", background: C("surfaceTertiary", "#eee"), padding: "1px 5px", borderRadius: 4 } : {} }, children: txt(r.text, ctx) }, i);
25248
+ return /* @__PURE__ */ jsx6("span", { style: { fontSize: f.remSize, fontWeight: marks.includes("bold") ? 700 : f.weight, fontStyle: marks.includes("italic") ? "italic" : void 0, textDecoration: marks.includes("underline") ? "underline" : void 0, ...f.family ? { fontFamily: fontStack(f.family, ctx.idiom) } : {}, ...r.color ? { color: C(r.color) } : {}, ...marks.includes("code") ? { fontFamily: "ui-monospace, monospace", background: C("surfaceTertiary", "#eee"), padding: "1px 5px", borderRadius: 4 } : {} }, children: txt(r.text, ctx) }, i);
25068
25249
  }) });
25069
25250
  }
25070
25251
  case "Image":
@@ -25105,8 +25286,8 @@ function renderInner(node, ctx) {
25105
25286
  props.leading && /* @__PURE__ */ jsx6("div", { style: { flex: "none", display: "flex" }, children: renderNode(props.leading, ctx) }),
25106
25287
  /* @__PURE__ */ jsxs3("div", { style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 2 }, children: [
25107
25288
  props.overline != null && /* @__PURE__ */ jsx6("span", { style: { fontFamily: bodyFont(ctx), fontSize: 11, fontWeight: 600, letterSpacing: 0.4, textTransform: "uppercase", color: C(props.overlineColor ?? "textSecondary", "#888"), ...ell }, children: txt(props.overline, ctx) }),
25108
- /* @__PURE__ */ jsx6("span", { style: { fontFamily: fontStack(titleF.family), fontSize: titleF.remSize, fontWeight: titleF.weight, color: C(props.titleColor ?? "textPrimary", "#13171e"), ...ell }, children: txt(props.title, ctx) ?? (ctx.empty ? "" : "") }),
25109
- props.subtitle != null && /* @__PURE__ */ jsx6("span", { style: { fontFamily: fontStack(subF.family), fontSize: subF.remSize, color: C(props.subtitleColor ?? "textSecondary", "#888"), ...ell }, children: txt(props.subtitle, ctx) })
25289
+ /* @__PURE__ */ jsx6("span", { style: { fontFamily: fontStack(titleF.family, ctx.idiom), fontSize: titleF.remSize, fontWeight: titleF.weight, color: C(props.titleColor ?? "textPrimary", "#13171e"), ...ell }, children: txt(props.title, ctx) ?? (ctx.empty ? "" : "") }),
25290
+ props.subtitle != null && /* @__PURE__ */ jsx6("span", { style: { fontFamily: fontStack(subF.family, ctx.idiom), fontSize: subF.remSize, color: C(props.subtitleColor ?? "textSecondary", "#888"), ...ell }, children: txt(props.subtitle, ctx) })
25110
25291
  ] }),
25111
25292
  props.trailing ? (
25112
25293
  // #1010 — the row's `onTap` lives on the outer flex div, and the trailing
@@ -25172,7 +25353,7 @@ function renderInner(node, ctx) {
25172
25353
  case "Label": {
25173
25354
  const f = ctx.font(props.style ?? "body");
25174
25355
  const col = C(resolveScalarStatic(props.color, ctx) ?? "textPrimary", "#13171e");
25175
- return /* @__PURE__ */ jsxs3("span", { style: { display: "inline-flex", alignItems: "center", gap: 6, fontFamily: fontStack(f.family), fontSize: f.remSize, fontWeight: f.weight, color: col, ...wrap }, children: [
25356
+ return /* @__PURE__ */ jsxs3("span", { style: { display: "inline-flex", alignItems: "center", gap: 6, fontFamily: fontStack(f.family, ctx.idiom), fontSize: f.remSize, fontWeight: f.weight, color: col, ...wrap }, children: [
25176
25357
  props.icon && /* @__PURE__ */ jsx6("span", { style: { display: "inline-grid", placeItems: "center", width: f.size, height: f.size }, children: iconSvg(props.icon, { size: f.size, color: col }) }),
25177
25358
  txt(props.text, ctx)
25178
25359
  ] });
@@ -25201,7 +25382,7 @@ function renderInner(node, ctx) {
25201
25382
  ensureMarqueeKeyframes();
25202
25383
  const f = ctx.font(props.style ?? "body");
25203
25384
  const dur = props.durationMs ?? 8e3;
25204
- return /* @__PURE__ */ jsx6("div", { style: { width: "100%", overflow: "hidden", whiteSpace: "nowrap", ...wrap }, children: /* @__PURE__ */ jsx6("span", { style: { display: "inline-block", paddingLeft: "100%", fontFamily: fontStack(f.family), fontSize: f.remSize, color: C(resolveScalarStatic(props.color, ctx) ?? "textPrimary", "#13171e"), animation: ctx.empty ? void 0 : `himalaya-marquee ${dur}ms linear infinite` }, children: txt(props.text, ctx) }) });
25385
+ return /* @__PURE__ */ jsx6("div", { style: { width: "100%", overflow: "hidden", whiteSpace: "nowrap", ...wrap }, children: /* @__PURE__ */ jsx6("span", { style: { display: "inline-block", paddingLeft: "100%", fontFamily: fontStack(f.family, ctx.idiom), fontSize: f.remSize, color: C(resolveScalarStatic(props.color, ctx) ?? "textPrimary", "#13171e"), animation: ctx.empty ? void 0 : `himalaya-marquee ${dur}ms linear infinite` }, children: txt(props.text, ctx) }) });
25205
25386
  }
25206
25387
  case "Timeline":
25207
25388
  return /* @__PURE__ */ jsx6(TimelineNode, { props, ctx, wrap });
@@ -25440,6 +25621,7 @@ var init_renderNode = __esm({
25440
25621
  "../../core/web-renderer/src/renderNode.tsx"() {
25441
25622
  "use strict";
25442
25623
  init_tokens2();
25624
+ init_idiom_controls();
25443
25625
  init_modifiers();
25444
25626
  init_helpers();
25445
25627
  init_bindings2();
@@ -25570,7 +25752,7 @@ var init_renderNode = __esm({
25570
25752
  return "center";
25571
25753
  }
25572
25754
  };
25573
- bodyFont = (ctx) => fontStack(ctx.font("body").family);
25755
+ bodyFont = (ctx) => fontStack(ctx.font("body").family, ctx.idiom);
25574
25756
  kids = (arr, ctx) => Array.isArray(arr) ? arr.map((n, i) => /* @__PURE__ */ jsx6(React5.Fragment, { children: renderNode(n, ctx) }, n?.id ?? i)) : null;
25575
25757
  rowKids = (arr, ctx) => Array.isArray(arr) ? arr.map((n, i) => /* @__PURE__ */ jsx6(React5.Fragment, { children: renderNode(n, wantsRowStretch(n) ? { ...ctx, rowStretch: true } : ctx) }, n?.id ?? i)) : null;
25576
25758
  staggeredKids = (arr, ctx, staggerMs) => Array.isArray(arr) ? arr.map((n, i) => {
@@ -27058,7 +27240,7 @@ var init_renderNode = __esm({
27058
27240
  const tAlign = props.textAlign === "center" ? "center" : props.textAlign === "trailing" ? "right" : props.textAlign === "justify" ? "justify" : props.textAlign === "leading" ? "left" : void 0;
27059
27241
  const tDecor = props.decoration === "strikethrough" ? "line-through" : props.decoration === "underline" ? "underline" : void 0;
27060
27242
  const ln = typeof props.lineSpacing === "number" ? pxToRem((f.line ?? 1.4) * f.size + props.lineSpacing) : f.line;
27061
- return /* @__PURE__ */ jsx6("div", { ...countAttrs, style: { fontFamily: fontStack(f.family), fontSize: f.remSize, fontWeight: f.weight, lineHeight: ln, letterSpacing: f.letterSpacing, color: C(resolveScalarStatic(props.color, ctx) ?? "textPrimary", "#13171e"), whiteSpace: "pre-wrap", overflowWrap: "anywhere", wordBreak: "break-word", minWidth: 0, ...tAlign ? { textAlign: tAlign } : {}, ...tDecor ? { textDecoration: tDecor } : {}, ...props.maxLines ? { display: "-webkit-box", WebkitLineClamp: props.maxLines, WebkitBoxOrient: "vertical", overflow: "hidden" } : {}, ...wrap }, children: content == null || content === "" ? collapse ? null : "\xA0" : content });
27243
+ return /* @__PURE__ */ jsx6("div", { ...countAttrs, style: { fontFamily: fontStack(f.family, ctx.idiom), fontSize: f.remSize, fontWeight: f.weight, lineHeight: ln, letterSpacing: f.letterSpacing, color: C(resolveScalarStatic(props.color, ctx) ?? "textPrimary", "#13171e"), whiteSpace: "pre-wrap", overflowWrap: "anywhere", wordBreak: "break-word", minWidth: 0, ...tAlign ? { textAlign: tAlign } : {}, ...tDecor ? { textDecoration: tDecor } : {}, ...props.maxLines ? { display: "-webkit-box", WebkitLineClamp: props.maxLines, WebkitBoxOrient: "vertical", overflow: "hidden" } : {}, ...wrap }, children: content == null || content === "" ? collapse ? null : "\xA0" : content });
27062
27244
  });
27063
27245
  ImageNode = withExtras("ImageNode", ({ props, ctx, wrap }) => {
27064
27246
  const C = ctx.color;
@@ -27162,6 +27344,7 @@ var init_renderNode = __esm({
27162
27344
  disabled,
27163
27345
  "aria-label": accessibleLabel,
27164
27346
  "aria-busy": props.loading === true ? true : void 0,
27347
+ "data-himalaya-idiom-press": idiomPress(ctx, "button"),
27165
27348
  onClick,
27166
27349
  style: rowShareFloor({
27167
27350
  // reset first, so every token-driven value below still wins
@@ -27204,7 +27387,7 @@ var init_renderNode = __esm({
27204
27387
  gap: 8,
27205
27388
  padding: pad2,
27206
27389
  borderRadius: props.iconOnly ? "50%" : pillRadius,
27207
- fontFamily: fontStack(f.family),
27390
+ fontFamily: fontStack(f.family, ctx.idiom),
27208
27391
  fontWeight: f.weight,
27209
27392
  fontSize: f.remSize,
27210
27393
  background: primary ? accent : "transparent",
@@ -27271,8 +27454,9 @@ var init_renderNode = __esm({
27271
27454
  const presented = props.presentedPath != null ? bound : props.presented;
27272
27455
  if (gated && presented !== true && presented !== "true") return null;
27273
27456
  const grabber = props.showGrabber !== false;
27274
- return /* @__PURE__ */ jsx6("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,.35)", zIndex: 50, display: "flex", flexDirection: "column", justifyContent: "flex-end", ...wrap }, children: /* @__PURE__ */ jsxs3("div", { role: "dialog", "aria-modal": "true", "aria-label": props.title != null ? sheetTitle : "Sheet", style: { width: "100%", background: C("surfacePrimary", "#fff"), borderRadius: "16px 16px 0 0", boxShadow: "0 -4px 24px rgba(0,0,0,.12)", overflow: "hidden" }, children: [
27275
- grabber && /* @__PURE__ */ jsx6("div", { style: { display: "flex", justifyContent: "center", padding: "8px 0 4px" }, children: /* @__PURE__ */ jsx6("div", { style: { width: 36, height: 5, borderRadius: 999, background: C("surfaceTertiary", "#d8dce2") } }) }),
27457
+ const sheet = idiomControls(ctx.idiom).sheet;
27458
+ return /* @__PURE__ */ jsx6("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,.35)", zIndex: 50, display: "flex", flexDirection: "column", justifyContent: "flex-end", ...wrap }, children: /* @__PURE__ */ jsxs3("div", { role: "dialog", "aria-modal": "true", "aria-label": props.title != null ? sheetTitle : "Sheet", style: { width: "100%", background: C("surfacePrimary", "#fff"), borderRadius: `${sheet.cornerRadius}px ${sheet.cornerRadius}px 0 0`, boxShadow: "0 -4px 24px rgba(0,0,0,.12)", overflow: "hidden" }, children: [
27459
+ grabber && /* @__PURE__ */ jsx6("div", { style: { display: "flex", justifyContent: "center", padding: "8px 0 4px" }, children: /* @__PURE__ */ jsx6("div", { style: { width: sheet.grabberWidth, height: sheet.grabberHeight, borderRadius: 999, background: C("surfaceTertiary", "#d8dce2") } }) }),
27276
27460
  /* @__PURE__ */ jsx6("div", { style: { padding: "8px 16px 16px" }, children: props.content ? renderNode(props.content, ctx) : null })
27277
27461
  ] }) });
27278
27462
  });
@@ -27356,7 +27540,7 @@ var init_renderNode = __esm({
27356
27540
  const capF = ctx.font("caption");
27357
27541
  return /* @__PURE__ */ jsx6("div", { style: { display: "inline-flex", flexDirection: "column", alignItems: "center", gap: 4, ...wrap }, children: /* @__PURE__ */ jsxs3("div", { style: { position: "relative", width: 72, height: 72 }, children: [
27358
27542
  /* @__PURE__ */ jsx6("div", { style: { width: 72, height: 72, borderRadius: "50%", background: `conic-gradient(${tint} ${pct * 360}deg, ${track} 0deg)`, WebkitMask: ringMask, mask: ringMask, ...gaugeAnim } }),
27359
- /* @__PURE__ */ jsx6("div", { style: { position: "absolute", inset: 0, display: "grid", placeItems: "center", fontFamily: fontStack(capF.family), fontSize: capF.remSize, fontWeight: capF.weight, color: C("textPrimary", "#13171e") }, children: txt(props.label, ctx) || `${Math.round(pct * 100)}%` })
27543
+ /* @__PURE__ */ jsx6("div", { style: { position: "absolute", inset: 0, display: "grid", placeItems: "center", fontFamily: fontStack(capF.family, ctx.idiom), fontSize: capF.remSize, fontWeight: capF.weight, color: C("textPrimary", "#13171e") }, children: txt(props.label, ctx) || `${Math.round(pct * 100)}%` })
27360
27544
  ] }) });
27361
27545
  });
27362
27546
  RATING_METRICS = {
@@ -28216,7 +28400,7 @@ var init_renderNode = __esm({
28216
28400
  /* @__PURE__ */ jsxs3("div", { style: { position: "relative", height: Math.min(props.expandedHeight ?? 120, 160), borderRadius: ctx.radius("md", 10), overflow: "hidden", background: imageUrl ? void 0 : `linear-gradient(135deg, ${C("accentPrimary", "#116DFF")}, ${C("accentSecondary", "#0E5FE3")})` }, children: [
28217
28401
  imageUrl && /* @__PURE__ */ jsx6("img", { src: imageUrl, alt: "", style: { width: "100%", height: "100%", objectFit: "cover" } }),
28218
28402
  /* @__PURE__ */ jsxs3("div", { style: { position: "absolute", left: 14, bottom: 12, color: "#fff", textShadow: "0 1px 4px rgba(0,0,0,.5)" }, children: [
28219
- /* @__PURE__ */ jsx6("div", { style: { fontFamily: ctx.font("title1").family ? fontStack(ctx.font("title1").family) : '"Wix Madefor Display", system-ui', fontWeight: 700, fontSize: 20 }, children: txt(props.title, ctx) }),
28403
+ /* @__PURE__ */ jsx6("div", { style: { fontFamily: ctx.font("title1").family || ctx.idiom && ctx.idiom !== "base" ? fontStack(ctx.font("title1").family, ctx.idiom) : '"Wix Madefor Display", system-ui', fontWeight: 700, fontSize: 20 }, children: txt(props.title, ctx) }),
28220
28404
  props.subtitle && /* @__PURE__ */ jsx6("div", { style: { fontSize: 13, opacity: 0.9 }, children: txt(props.subtitle, ctx) })
28221
28405
  ] })
28222
28406
  ] }),
@@ -28834,7 +29018,8 @@ function renderDescriptorToHtml(payload, tokens, scheme = "light", provider = nu
28834
29018
  const ctx = makeCtx(tokens, scheme, {
28835
29019
  provider,
28836
29020
  ...typeof width === "number" && Number.isFinite(width) && width > 0 ? { sizeClass: sizeClassFromWidth(width), containerWidth: width } : {},
28837
- ...rtl ? { isRTL: true } : {}
29021
+ ...rtl ? { isRTL: true } : {},
29022
+ ...opts?.idiomContext ? { idiomContext: opts.idiomContext } : {}
28838
29023
  });
28839
29024
  const FOCUS_STYLE_MARKUP = `<style data-himalaya-focus-ring>${FOCUS_RING_CSS}</style>`;
28840
29025
  const withFocusRing = (markup) => markup ? FOCUS_STYLE_MARKUP + markup : markup;
@@ -28925,16 +29110,19 @@ __export(src_exports, {
28925
29110
  Bound: () => Bound,
28926
29111
  BoundList: () => BoundList,
28927
29112
  FakeServiceProvider: () => FakeServiceProvider,
29113
+ IDIOM_CONTROLS: () => IDIOM_CONTROLS,
28928
29114
  WebAppRegistry: () => WebAppRegistry,
28929
29115
  WebRenderer: () => WebRenderer,
28930
29116
  WebScreenRegistry: () => WebScreenRegistry,
28931
29117
  asText: () => asText2,
28932
29118
  avatarCircle: () => avatarCircle,
29119
+ controlIdiomFor: () => controlIdiomFor,
28933
29120
  controlText: () => controlText,
28934
29121
  fontStack: () => fontStack,
28935
29122
  hasIcon: () => hasIcon,
28936
29123
  iconGlyph: () => iconGlyph,
28937
29124
  iconSvg: () => iconSvg,
29125
+ idiomControls: () => idiomControls,
28938
29126
  isBinding: () => isBinding,
28939
29127
  isNodeLike: () => isNodeLike,
28940
29128
  isSerifFamily: () => isSerifFamily,
@@ -28952,6 +29140,7 @@ var init_src3 = __esm({
28952
29140
  init_renderNode();
28953
29141
  init_renderToHtml();
28954
29142
  init_tokens2();
29143
+ init_idiom_controls();
28955
29144
  init_modifiers();
28956
29145
  init_bindings2();
28957
29146
  init_FakeServiceProvider();
@@ -29158,7 +29347,7 @@ function composeStickyTree(payload, header, footer) {
29158
29347
  if (footer) children.push(footer);
29159
29348
  return { id: "screen", type: "ScreenChrome", children };
29160
29349
  }
29161
- async function renderDescriptorWeb(descriptor, tokens, scheme, wantPng, services = null, width, isRTL) {
29350
+ async function renderDescriptorWeb(descriptor, tokens, scheme, wantPng, services = null, width, isRTL, idiomContext) {
29162
29351
  const desc = descriptor;
29163
29352
  const payload = desc.payload;
29164
29353
  if (!payload || typeof payload !== "object" || !payload.body) {
@@ -29193,7 +29382,7 @@ async function renderDescriptorWeb(descriptor, tokens, scheme, wantPng, services
29193
29382
  provider,
29194
29383
  width,
29195
29384
  stickyHeader || stickyFooter ? { stickyHeader, stickyFooter } : void 0,
29196
- isRTL ? { isRTL: true } : void 0
29385
+ isRTL || idiomContext ? { ...isRTL ? { isRTL: true } : {}, ...idiomContext ? { idiomContext } : {} } : void 0
29197
29386
  );
29198
29387
  } catch (err) {
29199
29388
  return {
@@ -29207,7 +29396,7 @@ async function renderDescriptorWeb(descriptor, tokens, scheme, wantPng, services
29207
29396
  const nativeOnly = collectNativeOnly(renderTree);
29208
29397
  if (nativeOnly.length) result.nativeOnly = nativeOnly;
29209
29398
  if (wantPng) {
29210
- const png = await renderPng(html, scheme, width, isRTL, tokens);
29399
+ const png = await renderPng(html, scheme, width, isRTL, tokens, idiomContext);
29211
29400
  if (png) result.pngBase64 = png;
29212
29401
  else result.pngUnavailable = true;
29213
29402
  }
@@ -29224,8 +29413,8 @@ async function withRetry2(op, attempts = 2) {
29224
29413
  }
29225
29414
  throw lastErr;
29226
29415
  }
29227
- async function renderPng(html, scheme, width, isRTL, tokens) {
29228
- const out = await renderPngBatch([{ html, scheme, width, isRTL, tokens }]);
29416
+ async function renderPng(html, scheme, width, isRTL, tokens, idiomContext) {
29417
+ const out = await renderPngBatch([{ html, scheme, width, isRTL, tokens, idiomContext }]);
29229
29418
  return out?.[0] ?? null;
29230
29419
  }
29231
29420
  function setPngFontCss(source) {
@@ -29236,7 +29425,7 @@ function setPngFontCss(source) {
29236
29425
  async function pngPageHtml(item) {
29237
29426
  let faces = "";
29238
29427
  try {
29239
- faces = await pngFontCss?.(item.tokens ?? null) ?? "";
29428
+ faces = await pngFontCss?.(item.tokens ?? null, item.idiomContext) ?? "";
29240
29429
  } catch {
29241
29430
  faces = "";
29242
29431
  }
@@ -29434,6 +29623,17 @@ async function pngFontFaceCss(tokens, opts = {}) {
29434
29623
  });
29435
29624
  return rules.join("");
29436
29625
  }
29626
+ async function idiomFontFaceCss(idiomContext) {
29627
+ const { controlIdiomFor: controlIdiomFor2, idiomControls: idiomControls2 } = await Promise.resolve().then(() => (init_src3(), src_exports));
29628
+ const bundled = idiomControls2(controlIdiomFor2(idiomContext)).font.bundled;
29629
+ if (!bundled) return "";
29630
+ try {
29631
+ const bytes2 = readFileSync34(new URL(`../../web-host/public/${bundled.file}`, import.meta.url));
29632
+ return `@font-face { font-family: "${bundled.family}"; font-weight: 100 900; font-style: normal; src: url(data:font/ttf;base64,${bytes2.toString("base64")}) format("truetype"); }`;
29633
+ } catch {
29634
+ return "";
29635
+ }
29636
+ }
29437
29637
  var encoded, cssString, FORMATS;
29438
29638
  var init_render_fonts = __esm({
29439
29639
  "../../core/dev-server/src/render-fonts.ts"() {
@@ -32838,7 +33038,7 @@ async function handleWebWix(req, res, state) {
32838
33038
  const method = String(b.method ?? "GET").toUpperCase();
32839
33039
  const requestedPath = String(b.path ?? "/");
32840
33040
  const body = b.body === void 0 ? void 0 : typeof b.body === "string" ? b.body : JSON.stringify(b.body);
32841
- const forwardedHeaders = normalizeWixForwardedHeaders(b.headers);
33041
+ const forwardedHeaders = normalizeWixForwardedHeaders(b.headers, requestedPath);
32842
33042
  const target = new URL(requestedPath, "https://www.wixapis.com");
32843
33043
  const query = Object.fromEntries(target.searchParams.entries());
32844
33044
  const started = Date.now();
@@ -32978,15 +33178,25 @@ async function handleDev(req, res, state) {
32978
33178
  }
32979
33179
  const screenId = descriptor.screenId ?? body.screenId ?? "render";
32980
33180
  const diagnostics = await runScreenDiagnostics(state, screenId, descriptor);
32981
- const appTokens = readAppTokens(state);
33181
+ let idiomContext;
33182
+ if (body.platform !== void 0) {
33183
+ if (body.platform !== "ios" && body.platform !== "android") {
33184
+ return sendJson(res, 400, { error: "invalid_platform", allowed: ["ios", "android"] });
33185
+ }
33186
+ idiomContext = { ...IDIOM_CONTEXTS[body.platform], surface: "phone" };
33187
+ }
33188
+ const baseTokens = readAppTokens(state);
33189
+ const policy = state.app?.module.config && "idiom" in state.app.module.config ? state.app.module.config.idiom : void 0;
33190
+ const stamped = idiomContext && baseTokens && policy !== void 0 ? { ...baseTokens, idiomPolicy: policy } : baseTokens;
32982
33191
  const render = await renderDescriptorWeb(
32983
33192
  descriptor,
32984
- appTokens && state.app ? withServedFontFaces(appTokens, state.app.dir) : appTokens,
33193
+ stamped && state.app ? withServedFontFaces(stamped, state.app.dir) : stamped,
32985
33194
  scheme,
32986
33195
  body.png === true,
32987
33196
  body.services ?? null,
32988
33197
  width,
32989
- body.rtl === true
33198
+ body.rtl === true,
33199
+ idiomContext
32990
33200
  );
32991
33201
  const legacy = { screenId, scheme, ...render, diagnostics };
32992
33202
  const report2 = foldLegacyReport(legacy, {
@@ -34444,6 +34654,7 @@ var init_src6 = __esm({
34444
34654
  init_inject();
34445
34655
  init_face_source();
34446
34656
  init_himi_report();
34657
+ init_idiom();
34447
34658
  init_store2();
34448
34659
  init_derive_transport_capability();
34449
34660
  init_wix_gateway();
@@ -34476,7 +34687,7 @@ var init_src6 = __esm({
34476
34687
  init_kits();
34477
34688
  init_widgets();
34478
34689
  ROOT6 = resolve30(fileURLToPath18(new URL("../../..", import.meta.url)));
34479
- setPngFontCss((tokens) => pngFontFaceCss(tokens));
34690
+ setPngFontCss(async (tokens, idiomContext) => await pngFontFaceCss(tokens) + (idiomContext ? await idiomFontFaceCss(idiomContext) : ""));
34480
34691
  TIER3_ROOT = resolve30(ROOT6, "core/dev-server/web");
34481
34692
  TIER4_ROOT = resolve30(ROOT6, "core/dev-server/tier4-bundles");
34482
34693
  tier4Verifier = new TierFourVerifier(TIER4_ROOT);
@@ -48367,19 +48578,21 @@ var init_preview_assets_generated = __esm({
48367
48578
  "../../packages/serve/src/preview-assets.generated.ts"() {
48368
48579
  init_preview_assets_format();
48369
48580
  PREVIEW_ASSET_MANIFEST = [
48370
- { key: "assets/index-DkRimsxm.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1020087, sha256: "4cb472d9d6d952d85166c025544c8bae669215c1a5eafbb800cec32d199bad14" },
48371
- { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1020095, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
48372
- { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1111019, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
48373
- { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1202003, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
48374
- { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1292983, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
48375
- { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1383855, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
48376
- { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1474835, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
48377
- { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 1583471, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
48378
- { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 1692555, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
48379
- { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 1801603, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
48380
- { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 1909303, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
48381
- { key: "index.html", contentType: "text/html; charset=utf-8", offset: 1997411, length: 4448, sha256: "22fbd62116f9571c6b01c1960990e392cce62198610eea2f661cb4d97bc05787" },
48382
- { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2001859, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
48581
+ { key: "assets/index-DcmBzFl6.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1030965, sha256: "e587f0db5f5d239871c73ee0976d387d6372768a239d04597e263f3c5bb0f395" },
48582
+ { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1030973, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
48583
+ { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1035583, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
48584
+ { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1524167, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
48585
+ { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1615091, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
48586
+ { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1706075, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
48587
+ { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1797055, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
48588
+ { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1887927, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
48589
+ { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1978907, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
48590
+ { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2087543, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
48591
+ { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2196627, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
48592
+ { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2305675, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
48593
+ { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2413375, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
48594
+ { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2501483, length: 4947, sha256: "551ec6b86f9e791e00d12beddd0480448ef4ff37fa33d4c6529b21e91406a753" },
48595
+ { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2506430, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
48383
48596
  ];
48384
48597
  }
48385
48598
  });
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1020.0",
3
+ "version": "0.1021.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": "74688239abb5b83cdc592a78513066472b921f1273d54470d9796cab"
68
+ "falconPackageHash": "d1be6f315217ca51d1f3bc16c0ce5d9d9e20bbc364b3a3a171c15ace"
69
69
  }