@wix/himalaya-cli 0.1405.0 → 0.1406.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.
@@ -62,6 +62,7 @@ export interface NavigationConfig {
62
62
  site?: import("@wix/himalaya/core").SiteNavConfig;
63
63
  /** Signed release allowlist for contained browser Tier-5 `_stream` requests. */
64
64
  streamPolicy?: StreamPolicy;
65
+ topBarLogo?: import("@wix/himalaya/core").TopBarLogo;
65
66
  splash?: SplashConfig;
66
67
  locale?: LocaleConfig;
67
68
  backAffordance?: BackAffordanceConfig;
@@ -227,6 +227,9 @@ export interface DerivedAppMeta {
227
227
  site?: import("@wix/himalaya/core").SiteNavConfig;
228
228
  /** Forwarded from AppConfig.navigation.streamPolicy — the signed browser stream allowlist. */
229
229
  streamPolicy?: import("@wix/himalaya/core").StreamPolicy;
230
+ /** Forwarded from AppConfig.navigation.topBarLogo — the app-wide logo for tab-stack roots
231
+ * (RFC 0066 amendment "top-bar gap items"). */
232
+ topBarLogo?: import("@wix/himalaya/core").TopBarLogo;
230
233
  /** Forwarded from AppConfig.`splash` — the in-app splash surface. NOTE the
231
234
  * asymmetry with the fields above: this reads the TOP-LEVEL `config.splash`, not
232
235
  * `config.navigation.splash`, because the same block also drives the build-time
@@ -2700,6 +2700,8 @@ export interface NavigationConfig {
2700
2700
  * links resolve as before. Build it with `siteNavConfig()` from
2701
2701
  * `@himalaya/core/site-shell`; resolution is `resolveSiteLink()`. */
2702
2702
  site?: SiteNavConfig;
2703
+ /** RFC 0066 amendment "top-bar gap items" — app-wide logo for tab-stack roots. */
2704
+ topBarLogo?: TopBarLogo;
2703
2705
  scheme: string;
2704
2706
  deepLinkPatterns: DeepLinkPattern[];
2705
2707
  linkableScreens: LinkableScreen[];
@@ -2903,6 +2905,86 @@ export interface TopBarConfig {
2903
2905
  leading?: string;
2904
2906
  /** Content-under-bar: "" | "opaque" (default) | "transparent" | "immersive". */
2905
2907
  style?: string;
2908
+ /** RFC 0066 §1 — toolbar items (drawn after the legacy `actions`). */
2909
+ items?: ToolbarItem[];
2910
+ /** RFC 0066 §1 — "" / "automatic" (default) | "large" | "inline". */
2911
+ titleDisplay?: string;
2912
+ /** RFC 0066 §1 — the literal subtitle (the fallback for `subtitlePath`). */
2913
+ subtitle?: string;
2914
+ /** RFC 0066 §1 — "" / "visible" (default) | "hidden". */
2915
+ visibility?: string;
2916
+ /** RFC 0066 §1 — bar fill token. iOS 26 keeps the system glass (amendment R12). */
2917
+ background?: TokenRef;
2918
+ /** RFC 0066 amendment — the screen's logo; overrides `NavigationConfig.topBarLogo`. */
2919
+ logo?: TopBarLogo;
2920
+ /** RFC 0066 amendment — the title as a menu. */
2921
+ titleMenu?: TopBarTitleMenu;
2922
+ /** RFC 0066 amendment — live subtitle (`$service:`); `subtitle` is the fallback. */
2923
+ subtitlePath?: string;
2924
+ /** RFC 0066 amendment — "" / "pinned" (default) | "hide_on_scroll". */
2925
+ scrollBehavior?: string;
2926
+ /** RFC 0066 amendment — truthy ⇒ leaving the presentation asks to discard. Unset/unresolved ⇒ false. */
2927
+ dirtyPath?: string;
2928
+ }
2929
+ /** RFC 0066 §1 (+ its "top-bar gap items" amendment) — one top-bar item. Test id `toolbar.<id>`
2930
+ * (`toolbar-<id>` on web and React Native). Every tap carries an existing `Action`
2931
+ * (invariant 15). Bindings are `$service:` sentinels resolved by the screen host. */
2932
+ export interface ToolbarItem {
2933
+ id: string;
2934
+ /** "leading" | "trailing" (default) | "principal" | "bottom" | "keyboard" | "overflow". */
2935
+ placement?: string;
2936
+ /** Text button when there is no icon; the accessible name always. */
2937
+ label?: string;
2938
+ icon?: IconRef;
2939
+ onTap?: Action;
2940
+ /** "" | "cancel" | "confirm" | "destructive". */
2941
+ role?: string;
2942
+ disabled?: boolean;
2943
+ /** OR'd with `disabled`. For Save, bind "nothing to save" (e.g. `cannotSave`), never the dirty flag. */
2944
+ disabledPath?: string;
2945
+ /** Non-empty ⇒ the item opens a menu. */
2946
+ menu?: MenuItem[];
2947
+ /** "principal" only: a custom title view. */
2948
+ content?: Component;
2949
+ /** Truthy ⇒ a progress indicator replaces the item. Unset/unresolved ⇒ false. */
2950
+ loadingPath?: string;
2951
+ /** Set ⇒ the item draws an avatar; `label` stays the accessible name ("Profile" by default). */
2952
+ avatar?: ToolbarAvatar;
2953
+ /** Live label; `label` is the fallback. */
2954
+ labelPath?: string;
2955
+ }
2956
+ /** RFC 0066 amendment — an avatar item. Images are absolute https URLs (resolve a Wix
2957
+ * media id with `wixMediaUrl` before publishing). */
2958
+ export interface ToolbarAvatar {
2959
+ imageUrl?: string;
2960
+ initials?: string;
2961
+ imageUrlPath?: string;
2962
+ initialsPath?: string;
2963
+ }
2964
+ /** RFC 0066 amendment — the bar logo, per screen (`TopBarConfig.logo`) or app-wide
2965
+ * (`NavigationConfig.topBarLogo`). `imageUrlPath` resolves by the parent's context: the
2966
+ * screen host per screen, the nav worker app-wide. */
2967
+ export interface TopBarLogo {
2968
+ imageUrl?: string;
2969
+ imageUrlDark?: string;
2970
+ /** Baked asset name; wins over `imageUrl`. */
2971
+ imageAsset?: string;
2972
+ /** Optional business name beside a mark-only logo. */
2973
+ name?: string;
2974
+ /** "" / "title" (default) | "leading" | "none". */
2975
+ placement?: string;
2976
+ imageUrlPath?: string;
2977
+ /** Default: the app's display name. */
2978
+ accessibilityLabel?: string;
2979
+ }
2980
+ /** RFC 0066 amendment — the title as a menu (switch location or feed). */
2981
+ export interface TopBarTitleMenu {
2982
+ items?: MenuItem[];
2983
+ selectedId?: string;
2984
+ /** `selectedId` is the fallback. */
2985
+ selectedIdPath?: string;
2986
+ /** Default "<title>, switch". */
2987
+ accessibilityLabel?: string;
2906
2988
  }
2907
2989
  /** RFC 0010 (top-bar amendment) — one trailing top-bar action. Reuses the
2908
2990
  * design-system icon name, the `NavBadge` overlay, and the same `Action`
@@ -272,6 +272,181 @@ export declare const screenDescriptorSchema: {
272
272
  readonly style: {
273
273
  readonly type: "string";
274
274
  };
275
+ readonly items: {
276
+ readonly type: "array";
277
+ readonly items: {
278
+ readonly $ref: "#/$defs/toolbarItem";
279
+ };
280
+ };
281
+ readonly titleDisplay: {
282
+ readonly type: "string";
283
+ };
284
+ readonly subtitle: {
285
+ readonly type: "string";
286
+ };
287
+ readonly visibility: {
288
+ readonly type: "string";
289
+ };
290
+ readonly background: {
291
+ readonly $ref: "#/$defs/tokenRef";
292
+ };
293
+ readonly logo: {
294
+ readonly $ref: "#/$defs/topBarLogo";
295
+ };
296
+ readonly titleMenu: {
297
+ readonly $ref: "#/$defs/topBarTitleMenu";
298
+ };
299
+ readonly subtitlePath: {
300
+ readonly type: "string";
301
+ };
302
+ readonly scrollBehavior: {
303
+ readonly type: "string";
304
+ };
305
+ readonly dirtyPath: {
306
+ readonly type: "string";
307
+ };
308
+ };
309
+ };
310
+ readonly toolbarItem: {
311
+ readonly type: "object";
312
+ readonly additionalProperties: false;
313
+ readonly required: readonly ["id"];
314
+ readonly properties: {
315
+ readonly id: {
316
+ readonly type: "string";
317
+ readonly minLength: 1;
318
+ };
319
+ readonly placement: {
320
+ readonly type: "string";
321
+ };
322
+ readonly label: {
323
+ readonly type: "string";
324
+ };
325
+ readonly icon: {
326
+ readonly type: "string";
327
+ };
328
+ readonly onTap: {
329
+ readonly $ref: "#/$defs/action";
330
+ };
331
+ readonly role: {
332
+ readonly type: "string";
333
+ };
334
+ readonly disabled: {
335
+ readonly type: "boolean";
336
+ };
337
+ readonly disabledPath: {
338
+ readonly type: "string";
339
+ };
340
+ readonly menu: {
341
+ readonly type: "array";
342
+ readonly items: {
343
+ readonly $ref: "#/$defs/topBarMenuItem";
344
+ };
345
+ };
346
+ readonly content: {
347
+ readonly $ref: "#/$defs/component";
348
+ };
349
+ readonly loadingPath: {
350
+ readonly type: "string";
351
+ };
352
+ readonly avatar: {
353
+ readonly $ref: "#/$defs/toolbarAvatar";
354
+ };
355
+ readonly labelPath: {
356
+ readonly type: "string";
357
+ };
358
+ };
359
+ };
360
+ readonly toolbarAvatar: {
361
+ readonly type: "object";
362
+ readonly additionalProperties: false;
363
+ readonly properties: {
364
+ readonly imageUrl: {
365
+ readonly type: "string";
366
+ };
367
+ readonly initials: {
368
+ readonly type: "string";
369
+ };
370
+ readonly imageUrlPath: {
371
+ readonly type: "string";
372
+ };
373
+ readonly initialsPath: {
374
+ readonly type: "string";
375
+ };
376
+ };
377
+ };
378
+ readonly topBarLogo: {
379
+ readonly type: "object";
380
+ readonly additionalProperties: false;
381
+ readonly properties: {
382
+ readonly imageUrl: {
383
+ readonly type: "string";
384
+ };
385
+ readonly imageUrlDark: {
386
+ readonly type: "string";
387
+ };
388
+ readonly imageAsset: {
389
+ readonly type: "string";
390
+ };
391
+ readonly name: {
392
+ readonly type: "string";
393
+ };
394
+ readonly placement: {
395
+ readonly type: "string";
396
+ };
397
+ readonly imageUrlPath: {
398
+ readonly type: "string";
399
+ };
400
+ readonly accessibilityLabel: {
401
+ readonly type: "string";
402
+ };
403
+ };
404
+ };
405
+ readonly topBarTitleMenu: {
406
+ readonly type: "object";
407
+ readonly additionalProperties: false;
408
+ readonly properties: {
409
+ readonly items: {
410
+ readonly type: "array";
411
+ readonly items: {
412
+ readonly $ref: "#/$defs/topBarMenuItem";
413
+ };
414
+ };
415
+ readonly selectedId: {
416
+ readonly type: "string";
417
+ };
418
+ readonly selectedIdPath: {
419
+ readonly type: "string";
420
+ };
421
+ readonly accessibilityLabel: {
422
+ readonly type: "string";
423
+ };
424
+ };
425
+ };
426
+ readonly topBarMenuItem: {
427
+ readonly type: "object";
428
+ readonly additionalProperties: false;
429
+ readonly required: readonly ["id", "label"];
430
+ readonly properties: {
431
+ readonly id: {
432
+ readonly type: "string";
433
+ readonly minLength: 1;
434
+ };
435
+ readonly label: {
436
+ readonly type: "string";
437
+ };
438
+ readonly icon: {
439
+ readonly type: "string";
440
+ };
441
+ readonly destructive: {
442
+ readonly type: "boolean";
443
+ };
444
+ readonly disabled: {
445
+ readonly type: "boolean";
446
+ };
447
+ readonly action: {
448
+ readonly $ref: "#/$defs/action";
449
+ };
275
450
  };
276
451
  };
277
452
  readonly topBarAction: {
@@ -1260,6 +1435,181 @@ export declare const widgetSnapshotSchema: {
1260
1435
  readonly style: {
1261
1436
  readonly type: "string";
1262
1437
  };
1438
+ readonly items: {
1439
+ readonly type: "array";
1440
+ readonly items: {
1441
+ readonly $ref: "#/$defs/toolbarItem";
1442
+ };
1443
+ };
1444
+ readonly titleDisplay: {
1445
+ readonly type: "string";
1446
+ };
1447
+ readonly subtitle: {
1448
+ readonly type: "string";
1449
+ };
1450
+ readonly visibility: {
1451
+ readonly type: "string";
1452
+ };
1453
+ readonly background: {
1454
+ readonly $ref: "#/$defs/tokenRef";
1455
+ };
1456
+ readonly logo: {
1457
+ readonly $ref: "#/$defs/topBarLogo";
1458
+ };
1459
+ readonly titleMenu: {
1460
+ readonly $ref: "#/$defs/topBarTitleMenu";
1461
+ };
1462
+ readonly subtitlePath: {
1463
+ readonly type: "string";
1464
+ };
1465
+ readonly scrollBehavior: {
1466
+ readonly type: "string";
1467
+ };
1468
+ readonly dirtyPath: {
1469
+ readonly type: "string";
1470
+ };
1471
+ };
1472
+ };
1473
+ readonly toolbarItem: {
1474
+ readonly type: "object";
1475
+ readonly additionalProperties: false;
1476
+ readonly required: readonly ["id"];
1477
+ readonly properties: {
1478
+ readonly id: {
1479
+ readonly type: "string";
1480
+ readonly minLength: 1;
1481
+ };
1482
+ readonly placement: {
1483
+ readonly type: "string";
1484
+ };
1485
+ readonly label: {
1486
+ readonly type: "string";
1487
+ };
1488
+ readonly icon: {
1489
+ readonly type: "string";
1490
+ };
1491
+ readonly onTap: {
1492
+ readonly $ref: "#/$defs/action";
1493
+ };
1494
+ readonly role: {
1495
+ readonly type: "string";
1496
+ };
1497
+ readonly disabled: {
1498
+ readonly type: "boolean";
1499
+ };
1500
+ readonly disabledPath: {
1501
+ readonly type: "string";
1502
+ };
1503
+ readonly menu: {
1504
+ readonly type: "array";
1505
+ readonly items: {
1506
+ readonly $ref: "#/$defs/topBarMenuItem";
1507
+ };
1508
+ };
1509
+ readonly content: {
1510
+ readonly $ref: "#/$defs/component";
1511
+ };
1512
+ readonly loadingPath: {
1513
+ readonly type: "string";
1514
+ };
1515
+ readonly avatar: {
1516
+ readonly $ref: "#/$defs/toolbarAvatar";
1517
+ };
1518
+ readonly labelPath: {
1519
+ readonly type: "string";
1520
+ };
1521
+ };
1522
+ };
1523
+ readonly toolbarAvatar: {
1524
+ readonly type: "object";
1525
+ readonly additionalProperties: false;
1526
+ readonly properties: {
1527
+ readonly imageUrl: {
1528
+ readonly type: "string";
1529
+ };
1530
+ readonly initials: {
1531
+ readonly type: "string";
1532
+ };
1533
+ readonly imageUrlPath: {
1534
+ readonly type: "string";
1535
+ };
1536
+ readonly initialsPath: {
1537
+ readonly type: "string";
1538
+ };
1539
+ };
1540
+ };
1541
+ readonly topBarLogo: {
1542
+ readonly type: "object";
1543
+ readonly additionalProperties: false;
1544
+ readonly properties: {
1545
+ readonly imageUrl: {
1546
+ readonly type: "string";
1547
+ };
1548
+ readonly imageUrlDark: {
1549
+ readonly type: "string";
1550
+ };
1551
+ readonly imageAsset: {
1552
+ readonly type: "string";
1553
+ };
1554
+ readonly name: {
1555
+ readonly type: "string";
1556
+ };
1557
+ readonly placement: {
1558
+ readonly type: "string";
1559
+ };
1560
+ readonly imageUrlPath: {
1561
+ readonly type: "string";
1562
+ };
1563
+ readonly accessibilityLabel: {
1564
+ readonly type: "string";
1565
+ };
1566
+ };
1567
+ };
1568
+ readonly topBarTitleMenu: {
1569
+ readonly type: "object";
1570
+ readonly additionalProperties: false;
1571
+ readonly properties: {
1572
+ readonly items: {
1573
+ readonly type: "array";
1574
+ readonly items: {
1575
+ readonly $ref: "#/$defs/topBarMenuItem";
1576
+ };
1577
+ };
1578
+ readonly selectedId: {
1579
+ readonly type: "string";
1580
+ };
1581
+ readonly selectedIdPath: {
1582
+ readonly type: "string";
1583
+ };
1584
+ readonly accessibilityLabel: {
1585
+ readonly type: "string";
1586
+ };
1587
+ };
1588
+ };
1589
+ readonly topBarMenuItem: {
1590
+ readonly type: "object";
1591
+ readonly additionalProperties: false;
1592
+ readonly required: readonly ["id", "label"];
1593
+ readonly properties: {
1594
+ readonly id: {
1595
+ readonly type: "string";
1596
+ readonly minLength: 1;
1597
+ };
1598
+ readonly label: {
1599
+ readonly type: "string";
1600
+ };
1601
+ readonly icon: {
1602
+ readonly type: "string";
1603
+ };
1604
+ readonly destructive: {
1605
+ readonly type: "boolean";
1606
+ };
1607
+ readonly disabled: {
1608
+ readonly type: "boolean";
1609
+ };
1610
+ readonly action: {
1611
+ readonly $ref: "#/$defs/action";
1612
+ };
1263
1613
  };
1264
1614
  };
1265
1615
  readonly topBarAction: {
@@ -8271,7 +8271,88 @@ var screenDescriptorSchema = {
8271
8271
  properties: {
8272
8272
  actions: { type: "array", items: { $ref: "#/$defs/topBarAction" } },
8273
8273
  leading: { type: "string" },
8274
- style: { type: "string" }
8274
+ style: { type: "string" },
8275
+ // RFC 0066 §1 (fields 4–8) + the "top-bar gap items" amendment (#4414, fields
8276
+ // 10–14). Discriminators stay free strings (invariant 6). Field 9 (`searchable`)
8277
+ // arrives with the RFC 0010 search implementation.
8278
+ items: { type: "array", items: { $ref: "#/$defs/toolbarItem" } },
8279
+ titleDisplay: { type: "string" },
8280
+ subtitle: { type: "string" },
8281
+ visibility: { type: "string" },
8282
+ background: { $ref: "#/$defs/tokenRef" },
8283
+ logo: { $ref: "#/$defs/topBarLogo" },
8284
+ titleMenu: { $ref: "#/$defs/topBarTitleMenu" },
8285
+ subtitlePath: { type: "string" },
8286
+ scrollBehavior: { type: "string" },
8287
+ dirtyPath: { type: "string" }
8288
+ }
8289
+ },
8290
+ toolbarItem: {
8291
+ type: "object",
8292
+ additionalProperties: false,
8293
+ required: ["id"],
8294
+ properties: {
8295
+ id: { type: "string", minLength: 1 },
8296
+ placement: { type: "string" },
8297
+ label: { type: "string" },
8298
+ icon: { type: "string" },
8299
+ onTap: { $ref: "#/$defs/action" },
8300
+ role: { type: "string" },
8301
+ disabled: { type: "boolean" },
8302
+ disabledPath: { type: "string" },
8303
+ menu: { type: "array", items: { $ref: "#/$defs/topBarMenuItem" } },
8304
+ content: { $ref: "#/$defs/component" },
8305
+ loadingPath: { type: "string" },
8306
+ avatar: { $ref: "#/$defs/toolbarAvatar" },
8307
+ labelPath: { type: "string" }
8308
+ }
8309
+ },
8310
+ toolbarAvatar: {
8311
+ type: "object",
8312
+ additionalProperties: false,
8313
+ properties: {
8314
+ imageUrl: { type: "string" },
8315
+ initials: { type: "string" },
8316
+ imageUrlPath: { type: "string" },
8317
+ initialsPath: { type: "string" }
8318
+ }
8319
+ },
8320
+ topBarLogo: {
8321
+ type: "object",
8322
+ additionalProperties: false,
8323
+ properties: {
8324
+ imageUrl: { type: "string" },
8325
+ imageUrlDark: { type: "string" },
8326
+ imageAsset: { type: "string" },
8327
+ name: { type: "string" },
8328
+ placement: { type: "string" },
8329
+ imageUrlPath: { type: "string" },
8330
+ accessibilityLabel: { type: "string" }
8331
+ }
8332
+ },
8333
+ topBarTitleMenu: {
8334
+ type: "object",
8335
+ additionalProperties: false,
8336
+ properties: {
8337
+ items: { type: "array", items: { $ref: "#/$defs/topBarMenuItem" } },
8338
+ selectedId: { type: "string" },
8339
+ selectedIdPath: { type: "string" },
8340
+ accessibilityLabel: { type: "string" }
8341
+ }
8342
+ },
8343
+ // The proto `MenuItem` (id, label, icon, destructive, disabled, action), as the
8344
+ // top bar carries it outside a Menu component's props.
8345
+ topBarMenuItem: {
8346
+ type: "object",
8347
+ additionalProperties: false,
8348
+ required: ["id", "label"],
8349
+ properties: {
8350
+ id: { type: "string", minLength: 1 },
8351
+ label: { type: "string" },
8352
+ icon: { type: "string" },
8353
+ destructive: { type: "boolean" },
8354
+ disabled: { type: "boolean" },
8355
+ action: { $ref: "#/$defs/action" }
8275
8356
  }
8276
8357
  },
8277
8358
  topBarAction: {
@@ -7141,7 +7141,88 @@ var screenDescriptorSchema = {
7141
7141
  properties: {
7142
7142
  actions: { type: "array", items: { $ref: "#/$defs/topBarAction" } },
7143
7143
  leading: { type: "string" },
7144
- style: { type: "string" }
7144
+ style: { type: "string" },
7145
+ // RFC 0066 §1 (fields 4–8) + the "top-bar gap items" amendment (#4414, fields
7146
+ // 10–14). Discriminators stay free strings (invariant 6). Field 9 (`searchable`)
7147
+ // arrives with the RFC 0010 search implementation.
7148
+ items: { type: "array", items: { $ref: "#/$defs/toolbarItem" } },
7149
+ titleDisplay: { type: "string" },
7150
+ subtitle: { type: "string" },
7151
+ visibility: { type: "string" },
7152
+ background: { $ref: "#/$defs/tokenRef" },
7153
+ logo: { $ref: "#/$defs/topBarLogo" },
7154
+ titleMenu: { $ref: "#/$defs/topBarTitleMenu" },
7155
+ subtitlePath: { type: "string" },
7156
+ scrollBehavior: { type: "string" },
7157
+ dirtyPath: { type: "string" }
7158
+ }
7159
+ },
7160
+ toolbarItem: {
7161
+ type: "object",
7162
+ additionalProperties: false,
7163
+ required: ["id"],
7164
+ properties: {
7165
+ id: { type: "string", minLength: 1 },
7166
+ placement: { type: "string" },
7167
+ label: { type: "string" },
7168
+ icon: { type: "string" },
7169
+ onTap: { $ref: "#/$defs/action" },
7170
+ role: { type: "string" },
7171
+ disabled: { type: "boolean" },
7172
+ disabledPath: { type: "string" },
7173
+ menu: { type: "array", items: { $ref: "#/$defs/topBarMenuItem" } },
7174
+ content: { $ref: "#/$defs/component" },
7175
+ loadingPath: { type: "string" },
7176
+ avatar: { $ref: "#/$defs/toolbarAvatar" },
7177
+ labelPath: { type: "string" }
7178
+ }
7179
+ },
7180
+ toolbarAvatar: {
7181
+ type: "object",
7182
+ additionalProperties: false,
7183
+ properties: {
7184
+ imageUrl: { type: "string" },
7185
+ initials: { type: "string" },
7186
+ imageUrlPath: { type: "string" },
7187
+ initialsPath: { type: "string" }
7188
+ }
7189
+ },
7190
+ topBarLogo: {
7191
+ type: "object",
7192
+ additionalProperties: false,
7193
+ properties: {
7194
+ imageUrl: { type: "string" },
7195
+ imageUrlDark: { type: "string" },
7196
+ imageAsset: { type: "string" },
7197
+ name: { type: "string" },
7198
+ placement: { type: "string" },
7199
+ imageUrlPath: { type: "string" },
7200
+ accessibilityLabel: { type: "string" }
7201
+ }
7202
+ },
7203
+ topBarTitleMenu: {
7204
+ type: "object",
7205
+ additionalProperties: false,
7206
+ properties: {
7207
+ items: { type: "array", items: { $ref: "#/$defs/topBarMenuItem" } },
7208
+ selectedId: { type: "string" },
7209
+ selectedIdPath: { type: "string" },
7210
+ accessibilityLabel: { type: "string" }
7211
+ }
7212
+ },
7213
+ // The proto `MenuItem` (id, label, icon, destructive, disabled, action), as the
7214
+ // top bar carries it outside a Menu component's props.
7215
+ topBarMenuItem: {
7216
+ type: "object",
7217
+ additionalProperties: false,
7218
+ required: ["id", "label"],
7219
+ properties: {
7220
+ id: { type: "string", minLength: 1 },
7221
+ label: { type: "string" },
7222
+ icon: { type: "string" },
7223
+ destructive: { type: "boolean" },
7224
+ disabled: { type: "boolean" },
7225
+ action: { $ref: "#/$defs/action" }
7145
7226
  }
7146
7227
  },
7147
7228
  topBarAction: {
@@ -9114,6 +9195,7 @@ function deriveAppMeta(name, m, platforms = {}, provisioning = {}) {
9114
9195
  appIconBadgeSources: config.navigation?.appIconBadgeSources,
9115
9196
  site: config.navigation?.site,
9116
9197
  streamPolicy: config.navigation?.streamPolicy,
9198
+ topBarLogo: config.navigation?.topBarLogo,
9117
9199
  splash: config.splash,
9118
9200
  locale: config.locale,
9119
9201
  deepLinkPatterns: config.navigation?.deepLinkPatterns,
package/dist/cli.mjs CHANGED
@@ -7314,6 +7314,7 @@ function deriveAppMeta(name, m2, platforms = {}, provisioning = {}) {
7314
7314
  appIconBadgeSources: config.navigation?.appIconBadgeSources,
7315
7315
  site: config.navigation?.site,
7316
7316
  streamPolicy: config.navigation?.streamPolicy,
7317
+ topBarLogo: config.navigation?.topBarLogo,
7317
7318
  splash: config.splash,
7318
7319
  locale: config.locale,
7319
7320
  deepLinkPatterns: config.navigation?.deepLinkPatterns,
@@ -8107,7 +8108,88 @@ var init_validator = __esm({
8107
8108
  properties: {
8108
8109
  actions: { type: "array", items: { $ref: "#/$defs/topBarAction" } },
8109
8110
  leading: { type: "string" },
8110
- style: { type: "string" }
8111
+ style: { type: "string" },
8112
+ // RFC 0066 §1 (fields 4–8) + the "top-bar gap items" amendment (#4414, fields
8113
+ // 10–14). Discriminators stay free strings (invariant 6). Field 9 (`searchable`)
8114
+ // arrives with the RFC 0010 search implementation.
8115
+ items: { type: "array", items: { $ref: "#/$defs/toolbarItem" } },
8116
+ titleDisplay: { type: "string" },
8117
+ subtitle: { type: "string" },
8118
+ visibility: { type: "string" },
8119
+ background: { $ref: "#/$defs/tokenRef" },
8120
+ logo: { $ref: "#/$defs/topBarLogo" },
8121
+ titleMenu: { $ref: "#/$defs/topBarTitleMenu" },
8122
+ subtitlePath: { type: "string" },
8123
+ scrollBehavior: { type: "string" },
8124
+ dirtyPath: { type: "string" }
8125
+ }
8126
+ },
8127
+ toolbarItem: {
8128
+ type: "object",
8129
+ additionalProperties: false,
8130
+ required: ["id"],
8131
+ properties: {
8132
+ id: { type: "string", minLength: 1 },
8133
+ placement: { type: "string" },
8134
+ label: { type: "string" },
8135
+ icon: { type: "string" },
8136
+ onTap: { $ref: "#/$defs/action" },
8137
+ role: { type: "string" },
8138
+ disabled: { type: "boolean" },
8139
+ disabledPath: { type: "string" },
8140
+ menu: { type: "array", items: { $ref: "#/$defs/topBarMenuItem" } },
8141
+ content: { $ref: "#/$defs/component" },
8142
+ loadingPath: { type: "string" },
8143
+ avatar: { $ref: "#/$defs/toolbarAvatar" },
8144
+ labelPath: { type: "string" }
8145
+ }
8146
+ },
8147
+ toolbarAvatar: {
8148
+ type: "object",
8149
+ additionalProperties: false,
8150
+ properties: {
8151
+ imageUrl: { type: "string" },
8152
+ initials: { type: "string" },
8153
+ imageUrlPath: { type: "string" },
8154
+ initialsPath: { type: "string" }
8155
+ }
8156
+ },
8157
+ topBarLogo: {
8158
+ type: "object",
8159
+ additionalProperties: false,
8160
+ properties: {
8161
+ imageUrl: { type: "string" },
8162
+ imageUrlDark: { type: "string" },
8163
+ imageAsset: { type: "string" },
8164
+ name: { type: "string" },
8165
+ placement: { type: "string" },
8166
+ imageUrlPath: { type: "string" },
8167
+ accessibilityLabel: { type: "string" }
8168
+ }
8169
+ },
8170
+ topBarTitleMenu: {
8171
+ type: "object",
8172
+ additionalProperties: false,
8173
+ properties: {
8174
+ items: { type: "array", items: { $ref: "#/$defs/topBarMenuItem" } },
8175
+ selectedId: { type: "string" },
8176
+ selectedIdPath: { type: "string" },
8177
+ accessibilityLabel: { type: "string" }
8178
+ }
8179
+ },
8180
+ // The proto `MenuItem` (id, label, icon, destructive, disabled, action), as the
8181
+ // top bar carries it outside a Menu component's props.
8182
+ topBarMenuItem: {
8183
+ type: "object",
8184
+ additionalProperties: false,
8185
+ required: ["id", "label"],
8186
+ properties: {
8187
+ id: { type: "string", minLength: 1 },
8188
+ label: { type: "string" },
8189
+ icon: { type: "string" },
8190
+ destructive: { type: "boolean" },
8191
+ disabled: { type: "boolean" },
8192
+ action: { $ref: "#/$defs/action" }
8111
8193
  }
8112
8194
  },
8113
8195
  topBarAction: {
@@ -9986,6 +10068,8 @@ __export(validate_descriptor_core_exports, {
9986
10068
  findSkipDerive: () => findSkipDerive,
9987
10069
  installedActionEvaluator: () => installedActionEvaluator,
9988
10070
  installedBootEvaluator: () => installedBootEvaluator,
10071
+ isInLenientTopBarBlock: () => isInLenientTopBarBlock,
10072
+ isLenientTopBarSubtree: () => isLenientTopBarSubtree,
9989
10073
  isOpaqueAuthorJson: () => isOpaqueAuthorJson,
9990
10074
  loadBundleServicesLive: () => loadBundleServicesLive,
9991
10075
  nativeRequiredFindings: () => nativeRequiredFindings,
@@ -10000,6 +10084,7 @@ __export(validate_descriptor_core_exports, {
10000
10084
  setActionEvaluator: () => setActionEvaluator,
10001
10085
  setBootEvaluator: () => setBootEvaluator,
10002
10086
  setDefaultEvaluator: () => setDefaultEvaluator,
10087
+ topBarFindings: () => topBarFindings,
10003
10088
  validateScreenDescriptor: () => validateScreenDescriptor,
10004
10089
  workerSubtreeAllowed: () => workerSubtreeAllowed
10005
10090
  });
@@ -10744,6 +10829,13 @@ function pathCrossesOpaqueAuthorJson(path2) {
10744
10829
  if (typeof path2 !== "string") return false;
10745
10830
  return /^\$\.input(?:[.[]|$)/.test(path2) || /\.args(?:[.[]|$)/.test(path2);
10746
10831
  }
10832
+ function isLenientTopBarSubtree(parentPath, key2) {
10833
+ return parentPath === "topBar" && LENIENT_TOP_BAR_BLOCKS.has(key2);
10834
+ }
10835
+ function isInLenientTopBarBlock(path2) {
10836
+ const m2 = /^topBar\.([A-Za-z]+)(?:[.[]|$)/.exec(path2);
10837
+ return !!m2 && LENIENT_TOP_BAR_BLOCKS.has(m2[1]);
10838
+ }
10747
10839
  function nativeRequiredFindings(descriptor) {
10748
10840
  const out2 = [];
10749
10841
  const shapeOf = (ref) => ref.startsWith("list:") ? "an array" : ref.startsWith("map:") ? "an object keyed by name" : "an object";
@@ -10860,6 +10952,7 @@ function nativeRequiredFindings(descriptor) {
10860
10952
  }
10861
10953
  for (const [k2, v2] of Object.entries(record2)) {
10862
10954
  if (isOpaqueAuthorJson(k2, v2, at2 === "")) continue;
10955
+ if (isLenientTopBarSubtree(at2, k2)) continue;
10863
10956
  walk2(v2, at2 ? `${at2}.${k2}` : k2);
10864
10957
  }
10865
10958
  };
@@ -11450,6 +11543,52 @@ function bottomSheetFindings(descriptor) {
11450
11543
  visit(descriptor, "", true);
11451
11544
  return out2;
11452
11545
  }
11546
+ function topBarFindings(descriptor) {
11547
+ const out2 = [];
11548
+ const bar = descriptor?.topBar;
11549
+ if (!bar || typeof bar !== "object") return out2;
11550
+ const b2 = bar;
11551
+ const legacy = Array.isArray(b2.actions) ? b2.actions.length : 0;
11552
+ const items = Array.isArray(b2.items) ? b2.items : [];
11553
+ const trailingItems = items.filter((i2) => i2 && typeof i2 === "object" && !TOP_BAR_NON_TRAILING.has(String(i2.placement ?? "")));
11554
+ const hasOverflow = items.some((i2) => i2?.placement === "overflow");
11555
+ if (legacy > TOP_BAR_TRAILING_LIMIT) {
11556
+ out2.push({ path: "topBar.actions", message: `The top bar has ${legacy} legacy actions. The guideline shows at most two trailing slots; legacy actions are never moved, so all ${legacy} still draw. Move the extra ones into \`items\` with \`placement: "overflow"\`` });
11557
+ } else if (legacy + trailingItems.length + (hasOverflow ? 1 : 0) > TOP_BAR_TRAILING_LIMIT) {
11558
+ out2.push({ path: "topBar.items", message: `The top bar declares ${legacy + trailingItems.length + (hasOverflow ? 1 : 0)} trailing slots; at most two show. The shell moves the extra items into More Actions in authored order (RFC 0066 amendment R2)` });
11559
+ }
11560
+ const logo = b2.logo;
11561
+ const logoInTitle = logo && typeof logo === "object" && logo.placement !== "leading" && logo.placement !== "none";
11562
+ if (b2.titleMenu && logoInTitle) {
11563
+ out2.push({ path: "topBar.titleMenu", message: 'The top bar sets both `titleMenu` and a title-placement `logo`. The title position holds one control, so the shell draws the title menu and not the logo (R7). Remove the logo, or set `logo.placement: "leading"`' });
11564
+ }
11565
+ if (typeof b2.dirtyPath === "string" && b2.dirtyPath !== "") {
11566
+ items.forEach((i2, idx) => {
11567
+ if (i2?.role === "confirm" && i2.disabledPath === b2.dirtyPath) {
11568
+ out2.push({ path: `topBar.items[${idx}].disabledPath`, message: `This confirm item's \`disabledPath\` is the same binding as \`topBar.dirtyPath\` (${JSON.stringify(b2.dirtyPath)}), so it is disabled exactly when there are changes to save. Bind \`disabledPath\` to "nothing to save" (for example \`$service:form.cannotSave\`) (R3)` });
11569
+ }
11570
+ });
11571
+ }
11572
+ const notHttps = (v2) => {
11573
+ if (typeof v2 !== "string" || v2 === "") return false;
11574
+ if (!/^https:\/\/[^/?#\s]/i.test(v2)) return true;
11575
+ try {
11576
+ return new URL(v2).hostname === "";
11577
+ } catch {
11578
+ return true;
11579
+ }
11580
+ };
11581
+ items.forEach((i2, idx) => {
11582
+ const avatar = i2?.avatar;
11583
+ if (avatar && notHttps(avatar.imageUrl)) out2.push({ path: `topBar.items[${idx}].avatar.imageUrl`, message: `Avatar imageUrl ${JSON.stringify(avatar.imageUrl)} is not an absolute https URL; resolve a Wix media id with wixMediaUrl before publishing it` });
11584
+ });
11585
+ if (logo && typeof logo === "object") {
11586
+ for (const key2 of ["imageUrl", "imageUrlDark"]) {
11587
+ if (notHttps(logo[key2])) out2.push({ path: `topBar.logo.${key2}`, message: `Logo ${key2} ${JSON.stringify(logo[key2])} is not an absolute https URL; resolve a Wix media id with wixMediaUrl before publishing it` });
11588
+ }
11589
+ }
11590
+ return out2;
11591
+ }
11453
11592
  function buttonVariantFindings(descriptor) {
11454
11593
  const out2 = [];
11455
11594
  const visit = (v2, path2, atRoot) => {
@@ -11655,6 +11794,9 @@ function validateScreenDescriptor(screenId, descriptor, opts = {}) {
11655
11794
  for (const finding of bottomSheetFindings(descriptor)) {
11656
11795
  issues.push({ severity: "error", pass: "bottom-sheet", path: finding.path, message: finding.message });
11657
11796
  }
11797
+ for (const finding of topBarFindings(descriptor)) {
11798
+ issues.push({ severity: "warning", pass: "top-bar", path: finding.path, message: finding.message });
11799
+ }
11658
11800
  const bindings = collectServiceBindings(descriptor);
11659
11801
  const descriptorActions = collectDescriptorActions(descriptor);
11660
11802
  const workerActions = descriptorActions.filter(({ actionId }) => actionId.startsWith("tier5."));
@@ -11879,6 +12021,7 @@ function validateScreenDescriptor(screenId, descriptor, opts = {}) {
11879
12021
  for (const [k2, val] of Object.entries(node)) {
11880
12022
  if (isOpaqueAuthorJson(k2, val, at2 === "")) continue;
11881
12023
  if (k2 === "params" && typeof node.actionId === "string") continue;
12024
+ if (isLenientTopBarSubtree(at2, k2)) continue;
11882
12025
  const here = at2 ? `${at2}.${k2}` : k2;
11883
12026
  const allowed2 = own3(NATIVE_PROP_ENUMS, k2);
11884
12027
  if (typeof val === "string" && enums.has(k2) && DYNAMIC_PLACEHOLDER.test(val)) {
@@ -11917,7 +12060,7 @@ function validateScreenDescriptor(screenId, descriptor, opts = {}) {
11917
12060
  const propFindings = componentPropFindings(descriptor);
11918
12061
  for (const finding of [
11919
12062
  ...nativeRequiredFindings(descriptor),
11920
- ...propFindings.filter((f2) => f2.kind === "decode")
12063
+ ...propFindings.filter((f2) => f2.kind === "decode" && !isInLenientTopBarBlock(f2.path))
11921
12064
  ]) {
11922
12065
  const candidates = (schemaIssuesAt.get(finding.path) ?? []).filter((i2) => !consumed.has(i2) && !superseded.has(i2));
11923
12066
  const [duplicate, ...alsoAboutThisField] = candidates;
@@ -12153,7 +12296,7 @@ function validateScreenDescriptor(screenId, descriptor, opts = {}) {
12153
12296
  services: Object.keys(services)
12154
12297
  };
12155
12298
  }
12156
- var CRAWL_BLOCKING_PASSES, BOOT_EVAL_TIMEOUT_MS, DRIFT_PROBES, DEVICE_PARITY_WITHHELD, defaultEvaluator, bootEvaluator, actionEvaluator, NATIVE_PROP_ENUMS, KNOWN_PROPS, IN_ARRAY_BINDING_PROPS, SERVICE_SIGIL, INTEGER_PROPS, NATIVE_REQUIRED_PROPS, bareNestedClass, NATIVE_NESTED_CLASSES, NATIVE_MODIFIERS_CLASS, NATIVE_DESCRIPTOR_CLASS, NATIVE_DESCRIPTOR_NESTED, NATIVE_NESTED_PROPS, MODIFIER_KEYS, INTEGER_MODIFIERS, MODIFIER_KINDS, SCHEMA_SCORED_NODES, CORE_PACKAGES, LEGACY_UNSCORED_KEYS, DYNAMIC_PLACEHOLDER, LITERAL_ONLY_PROPS, SERVICE_ONLY_BINDING_PROPS, WHOLE_VALUE_BINDING, EMBEDDED_BINDING, OPTIONS_BINDING_PROPS, EDITOR_SERVICE_NAME, SHEET_PLACEMENTS, BUTTON_STYLES, EMPTY_STATE_ACTION_STYLES, EMPTY_STATE_ACTION_SIZES;
12299
+ var CRAWL_BLOCKING_PASSES, BOOT_EVAL_TIMEOUT_MS, DRIFT_PROBES, DEVICE_PARITY_WITHHELD, defaultEvaluator, bootEvaluator, actionEvaluator, NATIVE_PROP_ENUMS, KNOWN_PROPS, IN_ARRAY_BINDING_PROPS, SERVICE_SIGIL, INTEGER_PROPS, NATIVE_REQUIRED_PROPS, bareNestedClass, NATIVE_NESTED_CLASSES, NATIVE_MODIFIERS_CLASS, NATIVE_DESCRIPTOR_CLASS, NATIVE_DESCRIPTOR_NESTED, NATIVE_NESTED_PROPS, LENIENT_TOP_BAR_BLOCKS, MODIFIER_KEYS, INTEGER_MODIFIERS, MODIFIER_KINDS, SCHEMA_SCORED_NODES, CORE_PACKAGES, LEGACY_UNSCORED_KEYS, DYNAMIC_PLACEHOLDER, LITERAL_ONLY_PROPS, SERVICE_ONLY_BINDING_PROPS, WHOLE_VALUE_BINDING, EMBEDDED_BINDING, OPTIONS_BINDING_PROPS, EDITOR_SERVICE_NAME, SHEET_PLACEMENTS, TOP_BAR_TRAILING_LIMIT, TOP_BAR_NON_TRAILING, BUTTON_STYLES, EMPTY_STATE_ACTION_STYLES, EMPTY_STATE_ACTION_SIZES;
12157
12300
  var init_validate_descriptor_core = __esm({
12158
12301
  "../../core/dev-server/src/validate-descriptor.core.ts"() {
12159
12302
  init_validator();
@@ -13072,6 +13215,7 @@ var init_validate_descriptor_core = __esm({
13072
13215
  WheelPicker: { options: "list:PickerOption", onChange: "Action" },
13073
13216
  WorkerSubtree: { policy: "WorkerSubtreePolicy" }
13074
13217
  };
13218
+ LENIENT_TOP_BAR_BLOCKS = /* @__PURE__ */ new Set(["items", "titleMenu", "logo", "background"]);
13075
13219
  MODIFIER_KEYS = new Set(
13076
13220
  Object.keys(screenDescriptorSchema.$defs.modifiers.properties)
13077
13221
  );
@@ -13143,6 +13287,8 @@ var init_validate_descriptor_core = __esm({
13143
13287
  OPTIONS_BINDING_PROPS = /* @__PURE__ */ new Set(["optionsPath"]);
13144
13288
  EDITOR_SERVICE_NAME = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
13145
13289
  SHEET_PLACEMENTS = /* @__PURE__ */ new Set(["overlay", "inline"]);
13290
+ TOP_BAR_TRAILING_LIMIT = 2;
13291
+ TOP_BAR_NON_TRAILING = /* @__PURE__ */ new Set(["leading", "principal", "bottom", "keyboard", "overflow"]);
13146
13292
  BUTTON_STYLES = /* @__PURE__ */ new Set(["filled", "tonal", "outlined", "text", "elevated", "bordered", "prominent", "plain", "glass", "primary", "secondary", "tertiary", "link", "destructive"]);
13147
13293
  EMPTY_STATE_ACTION_STYLES = /* @__PURE__ */ new Set(["primary", "secondary", "plain"]);
13148
13294
  EMPTY_STATE_ACTION_SIZES = /* @__PURE__ */ new Set(["sm", "md"]);
@@ -13697,6 +13843,8 @@ __export(validate_descriptor_exports, {
13697
13843
  findSkipDerive: () => findSkipDerive,
13698
13844
  installedActionEvaluator: () => installedActionEvaluator,
13699
13845
  installedBootEvaluator: () => installedBootEvaluator,
13846
+ isInLenientTopBarBlock: () => isInLenientTopBarBlock,
13847
+ isLenientTopBarSubtree: () => isLenientTopBarSubtree,
13700
13848
  isOpaqueAuthorJson: () => isOpaqueAuthorJson,
13701
13849
  loadAllowedComponents: () => loadAllowedComponents,
13702
13850
  loadBundleServicesLive: () => loadBundleServicesLive,
@@ -13717,6 +13865,7 @@ __export(validate_descriptor_exports, {
13717
13865
  setActionEvaluator: () => setActionEvaluator,
13718
13866
  setBootEvaluator: () => setBootEvaluator,
13719
13867
  setDefaultEvaluator: () => setDefaultEvaluator,
13868
+ topBarFindings: () => topBarFindings,
13720
13869
  validateScreenDescriptor: () => validateScreenDescriptor,
13721
13870
  workerSubtreeAllowed: () => workerSubtreeAllowed
13722
13871
  });
@@ -29343,6 +29492,7 @@ function deriveNavigationConfig(app, bundleCtx) {
29343
29492
  if (app.appIconBadgeSources !== void 0) config.appIconBadgeSources = app.appIconBadgeSources;
29344
29493
  if (app.site !== void 0) config.site = app.site;
29345
29494
  if (app.streamPolicy !== void 0) config.streamPolicy = app.streamPolicy;
29495
+ if (app.topBarLogo !== void 0) config.topBarLogo = app.topBarLogo;
29346
29496
  if (app.splash !== void 0) config.splash = app.splash;
29347
29497
  if (app.locale !== void 0) config.locale = app.locale;
29348
29498
  if (app.homeScreenId) config.homeScreenId = app.homeScreenId;
@@ -151722,6 +151872,7 @@ var init_nav_lint = __esm({
151722
151872
  "appIconBadgeSources",
151723
151873
  "streamPolicy",
151724
151874
  "site",
151875
+ "topBarLogo",
151725
151876
  "scheme",
151726
151877
  "deepLinkPatterns",
151727
151878
  "linkableScreens"
@@ -151747,6 +151898,7 @@ var init_nav_lint = __esm({
151747
151898
  "appIconBadgeSources",
151748
151899
  "streamPolicy",
151749
151900
  "site",
151901
+ "topBarLogo",
151750
151902
  "scheme",
151751
151903
  "deepLinkPatterns",
151752
151904
  "linkableScreens"
@@ -1053,7 +1053,7 @@
1053
1053
  "name": "NavigationConfig",
1054
1054
  "kind": "interface",
1055
1055
  "jsdoc": "/** Per-app navigation/deep-link config, served OTA at GET /v1/app-config/:appName. */",
1056
- "decl": "export interface NavigationConfig {\n /** Tab-bar structure (which tabs; their icons/labels/badges). */\n tabBar?: TabBarConfig;\n /** Slide-in drawer / hamburger-menu destinations. */\n drawer?: DrawerConfig;\n /** Navigation chrome the shell draws.\n * Omitted ⇒ \"bottomTabs\". The shell (not the descriptor) reads this. */\n navKind?: NavKind;\n /** Destinations for \"sidebar\" mode.\n * Absent ⇒ the shell derives the rail from `tabBar.items`. */\n sidebar?: SidebarConfig;\n /** App-level default top bar; a per-screen\n * `ScreenDescriptor.topBar` fully replaces it (override, not merge). */\n topBarDefault?: TopBarConfig;\n /** The post-OS-splash brand surface (see `SplashConfig`). Absent / not enabled\n * ⇒ today's behavior. Operational per (live-flippable). */\n splash?: SplashConfig;\n /** App-level default locale. Operational per so a Hebrew (or Spanish)\n * branded app can pin RTL/l10n OTA without a launch flag. `default` is a\n * BCP-47 primary subtag; clients coerce unknown tags rather than failing the\n * envelope. `pin: true` skips the device/browser language; in-app `locale.set`\n * still wins. Absent ⇒ today's device-then-en behavior. */\n locale?: LocaleConfig;\n /** Effective orientations, resolved INSIDE\n * the binary's build-time envelope (config.ts `orientationEnvelope`). The\n * envelope is the ceiling and can never be OTA (iOS reads the plist before\n * any app code runs); this narrows within it. Always intersected with the\n * envelope — it can never widen it. Absent/empty ⇒ the envelope itself. */\n orientation?: OrientationConfig;\n /** Per-surface nav projection, keyed by catalog\n * surface id (`phone` | `tablet` | `desktop` | `mac` | `watch` | `wear` | `rn`).\n * Keyed the way `orientation` is keyed by device family and narrowed the same\n * way: a key outside the app's `platforms.json` envelope is DROPPED, never\n * honored (see `resolveSurfaceNav`). Absent key ⇒ that surface uses the default\n * projection, so an app that never thinks about surfaces is unaffected. */\n surfaces?: Record<string, SurfaceNavConfig>;\n /** The APP-LEVEL nav worker\n * that feeds every `*Path` binding on `NavTabItem` / `DrawerItem` /\n * `TopBarAction`. App-level, not a screen's worker, because chrome outlives\n * every screen: a cart badge must survive leaving the cart, and a flag-hidden\n * tab must stay hidden three pushes deep in another stack. Absent ⇒ paths stay\n * unresolved and the literals render — the worker enriches, it never gates. */\n navLogicBundle?: LogicBundleRef;\n /** Which nav badges ALSO drive the OS app-icon\n * badge. Each entry is `\"<kind>:<id>\"` (`tab` | `drawer` | `topBarAction`, the\n * spelling `navKey` uses); the icon shows the SUM of those slots' resolved counts.\n * Empty/absent ⇒ no mirroring. An entry naming a slot this build does not have is\n * ignored, never a blanked badge. Requires the `push` capability — `defineApp`\n * rejects the combination, because the badge would be set and never shown. */\n appIconBadgeSources?: string[];\n /** Signed release allowlist for contained browser Tier-5 `_stream` requests. */\n streamPolicy?: StreamPolicy;\n /** A site shell's URL space. A push tap, app link or\n * custom-scheme link whose URL is an https page on one of `site.origins` opens the\n * screen owning its path, and that screen's WebView loads the full URL. Absent ⇒\n * links resolve as before. Build it with `siteNavConfig()` from\n * `@himalaya/core/site-shell`; resolution is `resolveSiteLink()`. */\n site?: SiteNavConfig;\n scheme: string;\n deepLinkPatterns: DeepLinkPattern[];\n linkableScreens: LinkableScreen[];\n}"
1056
+ "decl": "export interface NavigationConfig {\n /** Tab-bar structure (which tabs; their icons/labels/badges). */\n tabBar?: TabBarConfig;\n /** Slide-in drawer / hamburger-menu destinations. */\n drawer?: DrawerConfig;\n /** Navigation chrome the shell draws.\n * Omitted ⇒ \"bottomTabs\". The shell (not the descriptor) reads this. */\n navKind?: NavKind;\n /** Destinations for \"sidebar\" mode.\n * Absent ⇒ the shell derives the rail from `tabBar.items`. */\n sidebar?: SidebarConfig;\n /** App-level default top bar; a per-screen\n * `ScreenDescriptor.topBar` fully replaces it (override, not merge). */\n topBarDefault?: TopBarConfig;\n /** The post-OS-splash brand surface (see `SplashConfig`). Absent / not enabled\n * ⇒ today's behavior. Operational per (live-flippable). */\n splash?: SplashConfig;\n /** App-level default locale. Operational per so a Hebrew (or Spanish)\n * branded app can pin RTL/l10n OTA without a launch flag. `default` is a\n * BCP-47 primary subtag; clients coerce unknown tags rather than failing the\n * envelope. `pin: true` skips the device/browser language; in-app `locale.set`\n * still wins. Absent ⇒ today's device-then-en behavior. */\n locale?: LocaleConfig;\n /** Effective orientations, resolved INSIDE\n * the binary's build-time envelope (config.ts `orientationEnvelope`). The\n * envelope is the ceiling and can never be OTA (iOS reads the plist before\n * any app code runs); this narrows within it. Always intersected with the\n * envelope — it can never widen it. Absent/empty ⇒ the envelope itself. */\n orientation?: OrientationConfig;\n /** Per-surface nav projection, keyed by catalog\n * surface id (`phone` | `tablet` | `desktop` | `mac` | `watch` | `wear` | `rn`).\n * Keyed the way `orientation` is keyed by device family and narrowed the same\n * way: a key outside the app's `platforms.json` envelope is DROPPED, never\n * honored (see `resolveSurfaceNav`). Absent key ⇒ that surface uses the default\n * projection, so an app that never thinks about surfaces is unaffected. */\n surfaces?: Record<string, SurfaceNavConfig>;\n /** The APP-LEVEL nav worker\n * that feeds every `*Path` binding on `NavTabItem` / `DrawerItem` /\n * `TopBarAction`. App-level, not a screen's worker, because chrome outlives\n * every screen: a cart badge must survive leaving the cart, and a flag-hidden\n * tab must stay hidden three pushes deep in another stack. Absent ⇒ paths stay\n * unresolved and the literals render — the worker enriches, it never gates. */\n navLogicBundle?: LogicBundleRef;\n /** Which nav badges ALSO drive the OS app-icon\n * badge. Each entry is `\"<kind>:<id>\"` (`tab` | `drawer` | `topBarAction`, the\n * spelling `navKey` uses); the icon shows the SUM of those slots' resolved counts.\n * Empty/absent ⇒ no mirroring. An entry naming a slot this build does not have is\n * ignored, never a blanked badge. Requires the `push` capability — `defineApp`\n * rejects the combination, because the badge would be set and never shown. */\n appIconBadgeSources?: string[];\n /** Signed release allowlist for contained browser Tier-5 `_stream` requests. */\n streamPolicy?: StreamPolicy;\n /** A site shell's URL space. A push tap, app link or\n * custom-scheme link whose URL is an https page on one of `site.origins` opens the\n * screen owning its path, and that screen's WebView loads the full URL. Absent ⇒\n * links resolve as before. Build it with `siteNavConfig()` from\n * `@himalaya/core/site-shell`; resolution is `resolveSiteLink()`. */\n site?: SiteNavConfig;\n /** amendment \"top-bar gap items\" — app-wide logo for tab-stack roots. */\n topBarLogo?: TopBarLogo;\n scheme: string;\n deepLinkPatterns: DeepLinkPattern[];\n linkableScreens: LinkableScreen[];\n}"
1057
1057
  },
1058
1058
  {
1059
1059
  "name": "NavKind",
@@ -1095,7 +1095,7 @@
1095
1095
  "name": "TopBarConfig",
1096
1096
  "kind": "interface",
1097
1097
  "jsdoc": "/** The shell-owned top bar. The shell paints\n * `ScreenDescriptor.title` and computes the leading slot (back XOR menu) from\n * nav depth; this adds the trailing action cluster + leading/style overrides.\n * Lives on `ScreenDescriptor.topBar` (authoritative) and\n * `NavigationConfig.topBarDefault` (app-level fallback, fully replaced). */",
1098
- "decl": "export interface TopBarConfig {\n /** Trailing (right-edge) actions, authored order = leading→trailing. */\n actions?: TopBarAction[];\n /** Leading override: \"\" | \"auto\" (shell-computed) | \"back\" | \"menu\" | \"none\". */\n leading?: string;\n /** Content-under-bar: \"\" | \"opaque\" (default) | \"transparent\" | \"immersive\". */\n style?: string;\n}"
1098
+ "decl": "export interface TopBarConfig {\n /** Trailing (right-edge) actions, authored order = leading→trailing. */\n actions?: TopBarAction[];\n /** Leading override: \"\" | \"auto\" (shell-computed) | \"back\" | \"menu\" | \"none\". */\n leading?: string;\n /** Content-under-bar: \"\" | \"opaque\" (default) | \"transparent\" | \"immersive\". */\n style?: string;\n /** Toolbar items (drawn after the legacy `actions`). */\n items?: ToolbarItem[];\n /** \"\" / \"automatic\" (default) | \"large\" | \"inline\". */\n titleDisplay?: string;\n /** The literal subtitle (the fallback for `subtitlePath`). */\n subtitle?: string;\n /** \"\" / \"visible\" (default) | \"hidden\". */\n visibility?: string;\n /** Bar fill token. iOS 26 keeps the system glass (amendment R12). */\n background?: TokenRef;\n // Field 9 (`searchable`) belongs to the \"Native top-bar search\" implementation.\n /** amendment — the screen's logo; overrides `NavigationConfig.topBarLogo`. */\n logo?: TopBarLogo;\n /** amendment — the title as a menu. */\n titleMenu?: TopBarTitleMenu;\n /** amendment — live subtitle (`$service:`); `subtitle` is the fallback. */\n subtitlePath?: string;\n /** amendment — \"\" / \"pinned\" (default) | \"hide_on_scroll\". */\n scrollBehavior?: string;\n /** amendment — truthy ⇒ leaving the presentation asks to discard. Unset/unresolved ⇒ false. */\n dirtyPath?: string;\n}"
1099
1099
  },
1100
1100
  {
1101
1101
  "name": "SplashConfig",
@@ -1151,6 +1151,30 @@
1151
1151
  "jsdoc": "/** One entry of {@link SiteNavConfig.routes}. */",
1152
1152
  "decl": "export interface SiteRoute {\n /** Site-relative path prefix: `\"/\"`, `\"/shop\"`, `\"/blog\"`. */\n path: string;\n /** The screen whose WebView loads the URL. */\n screenId: string;\n}"
1153
1153
  },
1154
+ {
1155
+ "name": "TopBarLogo",
1156
+ "kind": "interface",
1157
+ "jsdoc": "/** amendment — the bar logo, per screen (`TopBarConfig.logo`) or app-wide\n * (`NavigationConfig.topBarLogo`). `imageUrlPath` resolves by the parent's context: the\n * screen host per screen, the nav worker app-wide. */",
1158
+ "decl": "export interface TopBarLogo {\n imageUrl?: string;\n imageUrlDark?: string;\n /** Baked asset name; wins over `imageUrl`. */\n imageAsset?: string;\n /** Optional business name beside a mark-only logo. */\n name?: string;\n /** \"\" / \"title\" (default) | \"leading\" | \"none\". */\n placement?: string;\n imageUrlPath?: string;\n /** Default: the app's display name. */\n accessibilityLabel?: string;\n}"
1159
+ },
1160
+ {
1161
+ "name": "ToolbarItem",
1162
+ "kind": "interface",
1163
+ "jsdoc": "/** One top-bar item. Test id `toolbar.<id>`\n * (`toolbar-<id>` on web and React Native). Every tap carries an existing `Action`\n *. Bindings are `$service:` sentinels resolved by the screen host. */",
1164
+ "decl": "export interface ToolbarItem {\n id: string;\n /** \"leading\" | \"trailing\" (default) | \"principal\" | \"bottom\" | \"keyboard\" | \"overflow\". */\n placement?: string;\n /** Text button when there is no icon; the accessible name always. */\n label?: string;\n icon?: IconRef;\n onTap?: Action;\n /** \"\" | \"cancel\" | \"confirm\" | \"destructive\". */\n role?: string;\n disabled?: boolean;\n /** OR'd with `disabled`. For Save, bind \"nothing to save\" (e.g. `cannotSave`), never the dirty flag. */\n disabledPath?: string;\n /** Non-empty ⇒ the item opens a menu. */\n menu?: MenuItem[];\n /** \"principal\" only: a custom title view. */\n content?: Component;\n /** Truthy ⇒ a progress indicator replaces the item. Unset/unresolved ⇒ false. */\n loadingPath?: string;\n /** Set ⇒ the item draws an avatar; `label` stays the accessible name (\"Profile\" by default). */\n avatar?: ToolbarAvatar;\n /** Live label; `label` is the fallback. */\n labelPath?: string;\n}"
1165
+ },
1166
+ {
1167
+ "name": "ToolbarAvatar",
1168
+ "kind": "interface",
1169
+ "jsdoc": "/** amendment — an avatar item. Images are absolute https URLs (resolve a Wix\n * media id with `wixMediaUrl` before publishing). */",
1170
+ "decl": "export interface ToolbarAvatar {\n imageUrl?: string;\n initials?: string;\n imageUrlPath?: string;\n initialsPath?: string;\n}"
1171
+ },
1172
+ {
1173
+ "name": "TopBarTitleMenu",
1174
+ "kind": "interface",
1175
+ "jsdoc": "/** amendment — the title as a menu (switch location or feed). */",
1176
+ "decl": "export interface TopBarTitleMenu {\n items?: MenuItem[];\n selectedId?: string;\n /** `selectedId` is the fallback. */\n selectedIdPath?: string;\n /** Default \"<title>, switch\". */\n accessibilityLabel?: string;\n}"
1177
+ },
1154
1178
  {
1155
1179
  "name": "LocaleConfig",
1156
1180
  "kind": "interface",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1405.0",
3
+ "version": "0.1406.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": [
@@ -71,5 +71,5 @@
71
71
  "artifactId": "himalaya-cli"
72
72
  }
73
73
  },
74
- "falconPackageHash": "0a22f58e5c6c241689ced19f73766e414c3004bb061a6c6b8f738a92"
74
+ "falconPackageHash": "964966afc4bcb743188c9fdf21d84de92fb1d9a392e0a48b0644a39a"
75
75
  }