@fluid-app/portal-sdk 0.1.198 → 0.1.200

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/dist/{CardWidget-HgYL7cEj.cjs → CardWidget-BVYjwdua.cjs} +2 -2
  2. package/dist/{CardWidget-BDIscQev.mjs → CardWidget-BjvrudYQ.mjs} +2 -2
  3. package/dist/{CardWidget-BDIscQev.mjs.map → CardWidget-BjvrudYQ.mjs.map} +1 -1
  4. package/dist/{CardWidget-DSq_Q8kv.cjs → CardWidget-DVi0NVhb.cjs} +2 -2
  5. package/dist/{CardWidget-DSq_Q8kv.cjs.map → CardWidget-DVi0NVhb.cjs.map} +1 -1
  6. package/dist/{ContactsScreen-Cheiwaxn.cjs → ContactsScreen-BOMxeTG8.cjs} +2 -2
  7. package/dist/{ContactsScreen-BtBNMZzG.mjs → ContactsScreen-Bm9SlMY3.mjs} +17 -7
  8. package/dist/ContactsScreen-Bm9SlMY3.mjs.map +1 -0
  9. package/dist/{ContactsScreen-DvohvoOE.cjs → ContactsScreen-DflUjayA.cjs} +291 -281
  10. package/dist/ContactsScreen-DflUjayA.cjs.map +1 -0
  11. package/dist/{ContainerWidget-PjWuvAlm.mjs → ContainerWidget-B51UF7DK.mjs} +2 -2
  12. package/dist/{ContainerWidget-PjWuvAlm.mjs.map → ContainerWidget-B51UF7DK.mjs.map} +1 -1
  13. package/dist/{ContainerWidget-Blb9A9i6.cjs → ContainerWidget-DNo8CkQ7.cjs} +2 -2
  14. package/dist/{ContainerWidget-Blb9A9i6.cjs.map → ContainerWidget-DNo8CkQ7.cjs.map} +1 -1
  15. package/dist/{ContainerWidget-CTP4UZXI.cjs → ContainerWidget-gtjlMITv.cjs} +3 -3
  16. package/dist/{FluidProvider-j5QX1XRm.cjs → FluidProvider-ByBDIQeW.cjs} +61 -999
  17. package/dist/FluidProvider-ByBDIQeW.cjs.map +1 -0
  18. package/dist/{FluidProvider-CGqVqcHP.mjs → FluidProvider-DFZiXiqm.mjs} +58 -827
  19. package/dist/FluidProvider-DFZiXiqm.mjs.map +1 -0
  20. package/dist/{LayoutWidget-oVmk8nm9.mjs → LayoutWidget-CtqIb8_Z.mjs} +2 -2
  21. package/dist/{LayoutWidget-oVmk8nm9.mjs.map → LayoutWidget-CtqIb8_Z.mjs.map} +1 -1
  22. package/dist/{LayoutWidget-D05U-Fai.cjs → LayoutWidget-D5gh0ejr.cjs} +2 -2
  23. package/dist/{LayoutWidget-BUxgiC_H.cjs → LayoutWidget-DHlXzqUk.cjs} +2 -2
  24. package/dist/{LayoutWidget-BUxgiC_H.cjs.map → LayoutWidget-DHlXzqUk.cjs.map} +1 -1
  25. package/dist/{MessagingScreen-CpUfQIFf.mjs → MessagingScreen-D6d83q2n.mjs} +2 -2
  26. package/dist/{MessagingScreen-CpUfQIFf.mjs.map → MessagingScreen-D6d83q2n.mjs.map} +1 -1
  27. package/dist/{MessagingScreen-Beyb0TUm.cjs → MessagingScreen-HT_HSiNW.cjs} +7 -7
  28. package/dist/{MessagingScreen-BaGLql5n.cjs → MessagingScreen-shEWzTmQ.cjs} +2 -2
  29. package/dist/{MessagingScreen-BaGLql5n.cjs.map → MessagingScreen-shEWzTmQ.cjs.map} +1 -1
  30. package/dist/{OrdersScreen-BgyMsZpn.mjs → OrdersScreen-BioiulGx.mjs} +2 -2
  31. package/dist/{OrdersScreen-BgyMsZpn.mjs.map → OrdersScreen-BioiulGx.mjs.map} +1 -1
  32. package/dist/{OrdersScreen-CCVPgVWa.cjs → OrdersScreen-DzCvZ6AW.cjs} +2 -2
  33. package/dist/{OrdersScreen-CCVPgVWa.cjs.map → OrdersScreen-DzCvZ6AW.cjs.map} +1 -1
  34. package/dist/{OrdersScreen-DWNylH_i.cjs → OrdersScreen-NIJGMwfQ.cjs} +1 -1
  35. package/dist/{PortalContentApiProvider-tSZDt_f2.mjs → PortalContentApiProvider-CNYq1_OD.mjs} +195 -91
  36. package/dist/PortalContentApiProvider-CNYq1_OD.mjs.map +1 -0
  37. package/dist/{PortalContentApiProvider-Bn44plrY.cjs → PortalContentApiProvider-ChmcXmbv.cjs} +194 -90
  38. package/dist/PortalContentApiProvider-ChmcXmbv.cjs.map +1 -0
  39. package/dist/{PortalProductsApiProvider-D0jul2Fh.mjs → PortalProductsApiProvider-B1IjNhl8.mjs} +22 -2
  40. package/dist/PortalProductsApiProvider-B1IjNhl8.mjs.map +1 -0
  41. package/dist/{PortalProductsApiProvider-CRN-i8Rh.cjs → PortalProductsApiProvider-o1axGadc.cjs} +22 -2
  42. package/dist/{PortalProductsApiProvider-CRN-i8Rh.cjs.map → PortalProductsApiProvider-o1axGadc.cjs.map} +1 -1
  43. package/dist/{ProductsScreen-o7LU64rN.mjs → ProductsScreen-CDjpHF49.mjs} +3 -3
  44. package/dist/{ProductsScreen-o7LU64rN.mjs.map → ProductsScreen-CDjpHF49.mjs.map} +1 -1
  45. package/dist/{ProductsScreen-uTXJCUPs.cjs → ProductsScreen-CzfDX0xx.cjs} +3 -3
  46. package/dist/{ProductsScreen-uTXJCUPs.cjs.map → ProductsScreen-CzfDX0xx.cjs.map} +1 -1
  47. package/dist/{ProductsScreen-nryGJv7S.mjs → ProductsScreen-D6-ehQjh.mjs} +3 -3
  48. package/dist/{ProductsScreen-Cvzwt-X-.cjs → ProductsScreen-fN3fh3PB.cjs} +3 -3
  49. package/dist/{ProfileScreen-CtMB7I96.cjs → ProfileScreen-B0EU-TLa.cjs} +101 -101
  50. package/dist/{ProfileScreen-CtMB7I96.cjs.map → ProfileScreen-B0EU-TLa.cjs.map} +1 -1
  51. package/dist/{ProfileScreen-th1mmEn0.mjs → ProfileScreen-BIs70k9W.mjs} +3 -3
  52. package/dist/{ProfileScreen-th1mmEn0.mjs.map → ProfileScreen-BIs70k9W.mjs.map} +1 -1
  53. package/dist/{ProfileScreen-BoFYel4G.cjs → ProfileScreen-Dqu2nK_2.cjs} +7 -7
  54. package/dist/{ScreenRenderer-CgSF9HEd.mjs → ScreenRenderer-Cv2rxvtF.mjs} +1 -9
  55. package/dist/ScreenRenderer-Cv2rxvtF.mjs.map +1 -0
  56. package/dist/{ScreenRenderer-Cx2KWPVu.cjs → ScreenRenderer-DfeRRgBO.cjs} +1 -9
  57. package/dist/ScreenRenderer-DfeRRgBO.cjs.map +1 -0
  58. package/dist/{ShareablesScreen-DPAjypds.mjs → ShareablesScreen-B8rPq-_7.mjs} +3 -3
  59. package/dist/{ShareablesScreen-DPAjypds.mjs.map → ShareablesScreen-B8rPq-_7.mjs.map} +1 -1
  60. package/dist/{ShareablesScreen-iUMt1ep3.cjs → ShareablesScreen-DFLAJxjs.cjs} +3 -3
  61. package/dist/{ShareablesScreen-Dw818mdV.cjs → ShareablesScreen-DLXK1PAg.cjs} +3 -3
  62. package/dist/{ShareablesScreen-Dw818mdV.cjs.map → ShareablesScreen-DLXK1PAg.cjs.map} +1 -1
  63. package/dist/{ShareablesScreen-DrMFfqJl.mjs → ShareablesScreen-Dc57L9m8.mjs} +3 -3
  64. package/dist/{ShopScreen-BBFsy04y.mjs → ShopScreen-2yMsyFwk.mjs} +3 -3
  65. package/dist/{ShopScreen-BBFsy04y.mjs.map → ShopScreen-2yMsyFwk.mjs.map} +1 -1
  66. package/dist/{ShopScreen-BCZ_tsd3.cjs → ShopScreen-BmHSLZ7H.cjs} +3 -3
  67. package/dist/{ShopScreen-BCZ_tsd3.cjs.map → ShopScreen-BmHSLZ7H.cjs.map} +1 -1
  68. package/dist/{ShopScreen-tjFoj6_r.cjs → ShopScreen-kk4yLzrW.cjs} +8 -8
  69. package/dist/{SubscriptionsScreen-BwRoXYF8.mjs → SubscriptionsScreen-1G9oa46y.mjs} +2 -2
  70. package/dist/{SubscriptionsScreen-BwRoXYF8.mjs.map → SubscriptionsScreen-1G9oa46y.mjs.map} +1 -1
  71. package/dist/{SubscriptionsScreen-mhTl7OIp.cjs → SubscriptionsScreen-Bt_YtoA3.cjs} +1 -1
  72. package/dist/{SubscriptionsScreen-DbcnEDD0.cjs → SubscriptionsScreen-Dn6LldL3.cjs} +2 -2
  73. package/dist/{SubscriptionsScreen-DbcnEDD0.cjs.map → SubscriptionsScreen-Dn6LldL3.cjs.map} +1 -1
  74. package/dist/index.cjs +78 -78
  75. package/dist/index.cjs.map +1 -1
  76. package/dist/index.d.cts +132 -128
  77. package/dist/index.d.cts.map +1 -1
  78. package/dist/index.d.mts +132 -128
  79. package/dist/index.d.mts.map +1 -1
  80. package/dist/index.mjs +32 -32
  81. package/dist/{portal_tenant-CWKHVgID.mjs → portal_tenant-8RPVJCll.mjs} +42 -2
  82. package/dist/{portal_tenant-CWKHVgID.mjs.map → portal_tenant-8RPVJCll.mjs.map} +1 -1
  83. package/dist/{portal_tenant-pozf8Ht6.cjs → portal_tenant-BdKp073-.cjs} +65 -1
  84. package/dist/{portal_tenant-pozf8Ht6.cjs.map → portal_tenant-BdKp073-.cjs.map} +1 -1
  85. package/dist/{portal_tenant_content-Dwmo8XZ-.cjs → portal_tenant_content-0zpnjBot.cjs} +97 -1
  86. package/dist/{portal_tenant_content-Dwmo8XZ-.cjs.map → portal_tenant_content-0zpnjBot.cjs.map} +1 -1
  87. package/dist/{portal_tenant_content-DgM42X08.mjs → portal_tenant_content-DzIQtSLE.mjs} +62 -2
  88. package/dist/{portal_tenant_content-DgM42X08.mjs.map → portal_tenant_content-DzIQtSLE.mjs.map} +1 -1
  89. package/dist/src-BRTXunU1.mjs +851 -0
  90. package/dist/src-BRTXunU1.mjs.map +1 -0
  91. package/dist/src-DvVPCD01.cjs +1038 -0
  92. package/dist/src-DvVPCD01.cjs.map +1 -0
  93. package/package.json +19 -19
  94. package/dist/ContactsScreen-BtBNMZzG.mjs.map +0 -1
  95. package/dist/ContactsScreen-DvohvoOE.cjs.map +0 -1
  96. package/dist/FluidProvider-CGqVqcHP.mjs.map +0 -1
  97. package/dist/FluidProvider-j5QX1XRm.cjs.map +0 -1
  98. package/dist/PortalContentApiProvider-Bn44plrY.cjs.map +0 -1
  99. package/dist/PortalContentApiProvider-tSZDt_f2.mjs.map +0 -1
  100. package/dist/PortalProductsApiProvider-D0jul2Fh.mjs.map +0 -1
  101. package/dist/ScreenRenderer-CgSF9HEd.mjs.map +0 -1
  102. package/dist/ScreenRenderer-Cx2KWPVu.cjs.map +0 -1
  103. package/dist/countries-api-context-Dob_AzPO.mjs +0 -13
  104. package/dist/countries-api-context-Dob_AzPO.mjs.map +0 -1
  105. package/dist/countries-api-context-G-NW4BoH.cjs +0 -25
  106. package/dist/countries-api-context-G-NW4BoH.cjs.map +0 -1
@@ -1,24 +1,24 @@
1
- import { d as DataSourceApiProvider, l as RegistryProvider } from "./ScreenRenderer-CgSF9HEd.mjs";
1
+ import { d as DataSourceApiProvider, l as RegistryProvider } from "./ScreenRenderer-Cv2rxvtF.mjs";
2
2
  import { t as AccountApiProvider } from "./account-api-context-BIZ_aibJ.mjs";
3
3
  import { t as StoreApiProvider } from "./store-api-context-C1lXT_3d.mjs";
4
4
  import { t as MySiteApiProvider } from "./mysite-api-context-bXtZ8Gbm.mjs";
5
- import { t as CountriesApiProvider } from "./countries-api-context-Dob_AzPO.mjs";
6
- import { a as enrollment_packs_show, c as products_list, d as products_show, f as store_show, i as calendar_events_list, m as subscriptions_list, n as account_update, o as orders_list, r as app_manifest_show, t as account_show, x as todos_list } from "./portal_tenant-CWKHVgID.mjs";
7
- import { C as playlists_show, g as pages_show, m as media_show } from "./portal_tenant_content-DgM42X08.mjs";
5
+ import { A as CountriesApiProvider, r as removeTheme, t as applyTheme, w as resolveTheme } from "./src-BRTXunU1.mjs";
6
+ import { T as todos_list, a as enrollment_pack_metrics_share_visits, c as orders_list, d as product_metrics_visits, f as products_list, g as store_show, h as products_show, i as calendar_events_list, n as account_update, o as enrollment_pack_metrics_visits, r as app_manifest_show, s as enrollment_packs_show, t as account_show, u as product_metrics_share_visits, v as subscriptions_list } from "./portal_tenant-8RPVJCll.mjs";
7
+ import { S as pages_show, a as content_playlists_metrics_share_visits, b as media_show, i as content_pages_metrics_visits, k as playlists_show, n as content_media_metrics_visits, o as content_playlists_metrics_visits, r as content_pages_metrics_share_visits, t as content_media_metrics_share_visits } from "./portal_tenant_content-DzIQtSLE.mjs";
8
8
  import { n as usePortalTenantClient, t as PortalTenantClientProvider } from "./PortalTenantClientProvider-4ZmY6hac.mjs";
9
9
  import { t as DataSourceRegistryProvider } from "./registry-context-CrKm3pxA.mjs";
10
10
  import { t as EmbedWidget } from "./EmbedWidget-DmgayrKN.mjs";
11
11
  import { r as WidgetsApiProvider } from "./error-state-DSJb-3r5.mjs";
12
- import { t as LayoutWidget } from "./LayoutWidget-oVmk8nm9.mjs";
12
+ import { t as LayoutWidget } from "./LayoutWidget-CtqIb8_Z.mjs";
13
13
  import { t as TextWidget } from "./TextWidget-O550ivSg.mjs";
14
14
  import { t as AlertWidget } from "./AlertWidget-DRX_3F4c.mjs";
15
15
  import { t as BulletListWidget } from "./BulletListWidget-D0ivXcaY.mjs";
16
16
  import { t as CalendarWidget } from "./CalendarWidget-CoicJaJV.mjs";
17
- import { t as CardWidget } from "./CardWidget-BDIscQev.mjs";
17
+ import { t as CardWidget } from "./CardWidget-BjvrudYQ.mjs";
18
18
  import { t as CarouselWidget } from "./CarouselWidget-CCmfNNEU.mjs";
19
19
  import { t as CatchUpWidget } from "./CatchUpWidget-BU3Z0m-4.mjs";
20
20
  import { t as ChartWidget } from "./ChartWidget-BZDwNO8g.mjs";
21
- import { t as ContainerWidget } from "./ContainerWidget-PjWuvAlm.mjs";
21
+ import { t as ContainerWidget } from "./ContainerWidget-B51UF7DK.mjs";
22
22
  import { t as ImageWidget } from "./ImageWidget-C_pLQwbi.mjs";
23
23
  import { t as LinkWidget } from "./LinkWidget-DSOKZ1zC.mjs";
24
24
  import { t as ListWidget } from "./ListWidget-D6ASLbnl.mjs";
@@ -35,7 +35,6 @@ import { t as VideoWidget } from "./VideoWidget-BdXJhiPF.mjs";
35
35
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
36
36
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
37
37
  import { jsx } from "react/jsx-runtime";
38
- import Color from "colorjs.io";
39
38
  //#region ../../../node_modules/.pnpm/@tanstack+query-core@5.90.12/node_modules/@tanstack/query-core/build/modern/timeoutManager.js
40
39
  var defaultTimeoutProvider = {
41
40
  setTimeout: (callback, delay) => setTimeout(callback, delay),
@@ -696,6 +695,40 @@ function throwIfAborted(signal) {
696
695
  function bffNotSupported(op) {
697
696
  throw new Error(`[portal-tenant-adapter] "${op}" is not available on the portal-tenant BFF. Wire the legacy adapter for this feature, or re-add the endpoint to the BFF spec.`);
698
697
  }
698
+ function isProduction() {
699
+ return globalThis.process?.env?.NODE_ENV === "production";
700
+ }
701
+ function narrowPeriod(value) {
702
+ if (value === "7d" || value === "30d" || value === "90d" || value === "1y" || value === "all") return value;
703
+ if (value !== void 0 && !isProduction()) console.warn(`[portal-sdk] narrowPeriod: unknown value "${value}" dropped`);
704
+ }
705
+ async function fetchShareVisited(shareableType, client, contentClient, query) {
706
+ switch (shareableType) {
707
+ case "products": return (await product_metrics_share_visits(client, query)).resources ?? [];
708
+ case "media": return (await content_media_metrics_share_visits(contentClient, query)).resources ?? [];
709
+ case "libraries": return (await content_playlists_metrics_share_visits(contentClient, query)).resources ?? [];
710
+ case "pages": return (await content_pages_metrics_share_visits(contentClient, query)).resources ?? [];
711
+ case "enrollment_packs": return (await enrollment_pack_metrics_share_visits(client, query)).resources ?? [];
712
+ case "promotions": bffNotSupported(`fetchMostShared(${shareableType})`);
713
+ }
714
+ }
715
+ async function fetchVisited(shareableType, client, contentClient, query) {
716
+ switch (shareableType) {
717
+ case "products": return (await product_metrics_visits(client, query)).resources ?? [];
718
+ case "media": return (await content_media_metrics_visits(contentClient, query)).resources ?? [];
719
+ case "libraries": return (await content_playlists_metrics_visits(contentClient, query)).resources ?? [];
720
+ case "pages": return (await content_pages_metrics_visits(contentClient, query)).resources ?? [];
721
+ case "enrollment_packs": return (await enrollment_pack_metrics_visits(client, query)).resources ?? [];
722
+ case "promotions": bffNotSupported(`fetchMostViewed(${shareableType})`);
723
+ }
724
+ }
725
+ function toMetricsQuery(options) {
726
+ const query = {};
727
+ const period = narrowPeriod(options.period);
728
+ if (period) query.period = period;
729
+ if (options.limit != null) query.limit = options.limit;
730
+ return query;
731
+ }
699
732
  /**
700
733
  * Map a BFF order response to the port's NormalizedOrder shape.
701
734
  * Centralizes the boundary between BFF wire format and our canonical type
@@ -748,11 +781,19 @@ function toNormalizedSubscription(raw) {
748
781
  */
749
782
  function createDataSourceApiAdapter(client, contentClient) {
750
783
  return {
751
- async fetchMostShared(_repId, _options) {
752
- bffNotSupported("fetchMostShared");
784
+ async fetchMostShared(_repId, options) {
785
+ throwIfAborted(options.signal);
786
+ const query = toMetricsQuery(options);
787
+ const result = await fetchShareVisited(options.shareableType, client, contentClient, query);
788
+ throwIfAborted(options.signal);
789
+ return result;
753
790
  },
754
- async fetchMostViewed(_repId, _options) {
755
- bffNotSupported("fetchMostViewed");
791
+ async fetchMostViewed(_repId, options) {
792
+ throwIfAborted(options.signal);
793
+ const query = toMetricsQuery(options);
794
+ const result = await fetchVisited(options.shareableType, client, contentClient, query);
795
+ throwIfAborted(options.signal);
796
+ return result;
756
797
  },
757
798
  async fetchOrders(_customerId, options) {
758
799
  throwIfAborted(options?.signal);
@@ -1528,816 +1569,6 @@ function createMySiteApiAdapter(client) {
1528
1569
  };
1529
1570
  }
1530
1571
  //#endregion
1531
- //#region ../core/src/theme/types.ts
1532
- const SEMANTIC_COLOR_NAMES = [
1533
- "background",
1534
- "foreground",
1535
- "primary",
1536
- "secondary",
1537
- "accent",
1538
- "muted",
1539
- "destructive"
1540
- ];
1541
- const SHADE_STEPS = [
1542
- 100,
1543
- 200,
1544
- 300,
1545
- 400,
1546
- 500,
1547
- 600,
1548
- 700,
1549
- 800,
1550
- 900
1551
- ];
1552
- const FONT_SIZE_KEYS = [
1553
- "extraSmall",
1554
- "small",
1555
- "regular",
1556
- "large",
1557
- "extraLarge",
1558
- "giant"
1559
- ];
1560
- const FONT_FAMILY_KEYS = ["header", "body"];
1561
- const RADIUS_KEYS = [
1562
- "small",
1563
- "medium",
1564
- "large",
1565
- "extraLarge"
1566
- ];
1567
- //#endregion
1568
- //#region ../core/src/theme/color-engine.ts
1569
- /**
1570
- * Attempt to convert any string into a Color using colorjs.io.
1571
- * If the string is exactly 6 characters it is assumed to be a bare hex value
1572
- * (e.g. "3b82f6") and a "#" prefix is added before parsing.
1573
- *
1574
- * @returns the parsed Color, or a neutral gray (`oklch(0.5 0 0)`) on failure
1575
- */
1576
- function parseColor(value) {
1577
- if (value.length === 6) value = `#${value}`;
1578
- try {
1579
- return new Color(value);
1580
- } catch (error) {
1581
- console.warn("[theme] Failed to parse color:", value, error);
1582
- return new Color("oklch", [
1583
- .5,
1584
- 0,
1585
- 0
1586
- ]);
1587
- }
1588
- }
1589
- /**
1590
- * Returns either the original foreground or a corrected lightness variant,
1591
- * whichever provides better contrast against `color`.
1592
- * Inversion triggers when the APCA contrast is below 50.
1593
- */
1594
- function getForegroundColor(foreground, color) {
1595
- if (foreground.oklch.l == null || color.oklch.l == null) return foreground;
1596
- if (color.contrastAPCA(foreground) < 50) return new Color("oklch", [
1597
- color.oklch.l < .7 ? .95 : .15,
1598
- foreground.oklch.c || 0,
1599
- foreground.oklch.h || 0
1600
- ]);
1601
- return foreground;
1602
- }
1603
- /**
1604
- * Generate a 100–900 shade ramp from a base color.
1605
- * Base anchors at 500. Light shades (100–400) step toward white,
1606
- * dark shades (600–900) step toward black. Dark steps use an asymmetric
1607
- * multiplier (1.6×, 1.875×, 3×, 4× of `darkStep`) for a more gradual
1608
- * initial descent. Chroma is nudged per step for perceptually natural ramps.
1609
- */
1610
- function generateShades(base) {
1611
- const l = base.oklch.l ?? 0;
1612
- const c = base.oklch.c ?? 0;
1613
- const h = base.oklch.h ?? 0;
1614
- const safeMax = l >= .885 ? .995 : .97;
1615
- const safeMin = l <= .33 ? 0 : .21;
1616
- const lightStep = (safeMax - l) / 5;
1617
- const darkStep = -(l - safeMin) / 8;
1618
- const shade = (lDelta, cDelta) => {
1619
- return new Color("oklch", [
1620
- Math.max(0, Math.min(1, l + lDelta)),
1621
- c <= .001 ? c : Math.max(0, c + cDelta),
1622
- h
1623
- ]);
1624
- };
1625
- return {
1626
- 100: shade(5 * lightStep, -.00375),
1627
- 200: shade(4 * lightStep, -.00375),
1628
- 300: shade(3 * lightStep, -.00375),
1629
- 400: shade(2 * lightStep, -.00375),
1630
- 500: new Color("oklch", [
1631
- l,
1632
- c,
1633
- h
1634
- ]),
1635
- 600: shade(1.6 * darkStep, .025),
1636
- 700: shade(1.875 * 2 * darkStep, .05),
1637
- 800: shade(6 * darkStep, .075),
1638
- 900: shade(8 * darkStep, .1)
1639
- };
1640
- }
1641
- const DARK_DERIVATION_CONFIG = {
1642
- background: {
1643
- baseLightness: .15,
1644
- fgLightness: .93
1645
- },
1646
- foreground: {
1647
- baseLightness: .93,
1648
- fgLightness: .15
1649
- },
1650
- muted: {
1651
- baseLightness: .22,
1652
- fgLightness: .75
1653
- },
1654
- primary: {
1655
- baseLightness: "invert",
1656
- fgLightness: .95,
1657
- chromaScale: .9
1658
- },
1659
- secondary: {
1660
- baseLightness: "invert",
1661
- fgLightness: .93,
1662
- chromaScale: .85
1663
- },
1664
- accent: {
1665
- baseLightness: "invert",
1666
- fgLightness: .95,
1667
- chromaScale: .9
1668
- },
1669
- destructive: {
1670
- baseLightness: "invert",
1671
- fgLightness: .95,
1672
- chromaScale: .95
1673
- }
1674
- };
1675
- /** Invert OKLCH lightness (1 - l), clamped to [0.35, 0.75] to avoid extremes. */
1676
- function invertLightness(l) {
1677
- const inverted = 1 - l;
1678
- return Math.max(.35, Math.min(.75, inverted));
1679
- }
1680
- /**
1681
- * Derive a dark-mode ThemeColorInput from its light-mode counterpart.
1682
- */
1683
- function deriveDarkVariant(name, light) {
1684
- const config = DARK_DERIVATION_CONFIG[name];
1685
- const chromaScale = config.chromaScale ?? 1;
1686
- const baseLightness = config.baseLightness === "invert" ? invertLightness(light.base.oklch.l ?? 0) : config.baseLightness;
1687
- const fgLightness = config.fgLightness === "invert" ? invertLightness(light.foreground.oklch.l ?? 0) : config.fgLightness;
1688
- return {
1689
- base: new Color("oklch", [
1690
- baseLightness,
1691
- (light.base.oklch.c || 0) * chromaScale,
1692
- light.base.oklch.h || 0
1693
- ]),
1694
- foreground: new Color("oklch", [
1695
- fgLightness,
1696
- (light.foreground.oklch.c || 0) * chromaScale,
1697
- light.foreground.oklch.h || 0
1698
- ])
1699
- };
1700
- }
1701
- /**
1702
- * Merge auto-derived dark colors with any user-specified overrides.
1703
- * For each semantic color, if the user has fully overridden both base and
1704
- * foreground those are used; otherwise the missing channels are derived.
1705
- */
1706
- function mergeDarkOverrides(def) {
1707
- const darkColors = {};
1708
- for (const name of SEMANTIC_COLOR_NAMES) {
1709
- const lightInput = def.light[name];
1710
- const darkOverride = def.dark[name];
1711
- if (darkOverride?.base && darkOverride?.foreground) darkColors[name] = darkOverride;
1712
- else if (darkOverride) {
1713
- const base = darkOverride.base ?? deriveDarkVariant(name, lightInput).base;
1714
- darkColors[name] = {
1715
- base,
1716
- foreground: darkOverride.foreground ?? getForegroundColor(def.light.foreground.base, base)
1717
- };
1718
- } else darkColors[name] = deriveDarkVariant(name, lightInput);
1719
- }
1720
- return darkColors;
1721
- }
1722
- function resolveColorSet(colors) {
1723
- const resolved = {};
1724
- for (const name of SEMANTIC_COLOR_NAMES) {
1725
- const input = colors[name];
1726
- const shades = generateShades(input.base);
1727
- const resolvedShades = {};
1728
- for (const step of SHADE_STEPS) resolvedShades[step] = shades[step];
1729
- resolved[name] = {
1730
- base: input.base.clone(),
1731
- foreground: input.foreground.clone(),
1732
- shades: resolvedShades
1733
- };
1734
- }
1735
- return resolved;
1736
- }
1737
- /**
1738
- * Resolve a ThemeDefinition into a complete ResolvedTheme.
1739
- * Dark mode colors are derived from light where not overridden.
1740
- */
1741
- function resolveTheme(def) {
1742
- return {
1743
- id: def.id,
1744
- name: def.name,
1745
- light: resolveColorSet(def.light),
1746
- dark: resolveColorSet(mergeDarkOverrides(def)),
1747
- fontSizes: { ...def.fontSizes },
1748
- fontFamilies: { ...def.fontFamilies },
1749
- spacing: def.spacing,
1750
- radii: { ...def.radii }
1751
- };
1752
- }
1753
- //#endregion
1754
- //#region ../core/src/theme/tailwind-overrides.ts
1755
- /**
1756
- * Specific overrides, otherwise all the overrides are generated using emitTailwindOverrides
1757
- */
1758
- const OVERRIDES = {
1759
- "--color-gray-50": "var(--color-muted)",
1760
- "--color-gray-100": "var(--color-muted-600)",
1761
- "--color-gray-200": "var(--color-border)"
1762
- };
1763
- /**
1764
- * Returns the inverted shade for dark mode foreground colors.
1765
- * In dark mode, light shades (50, 100) should map to dark values (950, 900) and vice versa.
1766
- */
1767
- function getInvertedStep(shade) {
1768
- const shadeIndex = SHADE_STEPS.indexOf(shade);
1769
- return SHADE_STEPS[SHADE_STEPS.length - 1 - shadeIndex] || 500;
1770
- }
1771
- /**
1772
- * Map semantic colors to Tailwind built-in color names.
1773
- */
1774
- function emitTailwindOverrides(darkMode = false) {
1775
- const TAILWIND_COLOR_MAP = {
1776
- gray: "foreground",
1777
- red: "destructive",
1778
- blue: "primary",
1779
- green: "accent"
1780
- };
1781
- const TAILWIND_SHADES = [
1782
- 50,
1783
- 100,
1784
- 200,
1785
- 300,
1786
- 400,
1787
- 500,
1788
- 600,
1789
- 700,
1790
- 800,
1791
- 900,
1792
- 950
1793
- ];
1794
- const SHADE_REMAP = {
1795
- 50: 100,
1796
- 950: 900
1797
- };
1798
- const lines = [];
1799
- for (const [twName, semantic] of Object.entries(TAILWIND_COLOR_MAP)) for (const shade of TAILWIND_SHADES) {
1800
- const step = SHADE_REMAP[shade] ?? shade;
1801
- const override = OVERRIDES[`--color-${twName}-${shade}`];
1802
- lines.push(`--color-${twName}-${shade}: ${override ? override : `var(--color-${semantic}-${semantic === "foreground" && darkMode === true ? getInvertedStep(step) : step})`};`);
1803
- }
1804
- lines.push("--color-white: var(--color-background);");
1805
- lines.push("--color-black: var(--color-foreground);");
1806
- return lines;
1807
- }
1808
- //#endregion
1809
- //#region ../core/src/theme/css-generator.ts
1810
- function colorToCSS(color) {
1811
- const result = color.toString({ format: "oklch" });
1812
- if (result.includes("NaN")) {
1813
- console.warn("[theme] colorToCSS produced NaN, using neutral fallback:", result);
1814
- return "oklch(0.5 0 0)";
1815
- }
1816
- return result;
1817
- }
1818
- function camelToKebab(str) {
1819
- return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
1820
- }
1821
- /**
1822
- * Emit --color-{name}, --color-{name}-foreground, --color-{name}-{shade} vars.
1823
- * Uses --color- prefix to match portal-widgets/tailwind.config.ts.
1824
- */
1825
- function emitColorVars(colors) {
1826
- const lines = [];
1827
- for (const name of SEMANTIC_COLOR_NAMES) {
1828
- const color = colors[name];
1829
- lines.push(`--color-${name}: ${colorToCSS(color.base)};`);
1830
- lines.push(`--color-${name}-foreground: ${colorToCSS(color.foreground)};`);
1831
- for (const step of SHADE_STEPS) lines.push(`--color-${name}-${step}: ${colorToCSS(color.shades[step])};`);
1832
- }
1833
- return lines;
1834
- }
1835
- /**
1836
- * Format a font family value for CSS output.
1837
- * - If the value starts with "var(" (legacy), pass through as-is
1838
- * - If the value already contains a comma (has fallback), pass through as-is
1839
- * - Otherwise, wrap in quotes and append a generic sans-serif fallback
1840
- */
1841
- function formatFontFamily(value) {
1842
- if (value.startsWith("var(")) return value;
1843
- if (value.includes(",")) return value;
1844
- return `'${value}', sans-serif`;
1845
- }
1846
- /**
1847
- * Emit non-color CSS variables (font sizes, families, spacing, radii).
1848
- */
1849
- function emitNonColorVars(theme) {
1850
- const lines = [];
1851
- for (const key of FONT_SIZE_KEYS) lines.push(`--font-size-${camelToKebab(key)}: ${theme.fontSizes[key]};`);
1852
- for (const key of FONT_FAMILY_KEYS) lines.push(`--font-${key}: ${formatFontFamily(theme.fontFamilies[key])};`);
1853
- lines.push(`--spacing: ${theme.spacing};`);
1854
- for (const key of RADIUS_KEYS) lines.push(`--radius-${camelToKebab(key)}: ${theme.radii[key]};`);
1855
- return lines;
1856
- }
1857
- /**
1858
- * Static CSS alias variables that bridge theme var names to Tailwind/component conventions.
1859
- * These are always emitted and not mode-dependent.
1860
- */
1861
- const globalCSSOverride = [
1862
- "--color-background-foreground: var(--color-foreground);",
1863
- "--color-foreground-foreground: var(--color-background);",
1864
- "--color-contrast: var(--color-foreground);",
1865
- ...SEMANTIC_COLOR_NAMES.map((value) => `--${value}: var(--color-${value});`),
1866
- ...SEMANTIC_COLOR_NAMES.map((value) => `--${value}-foreground: var(--color-${value}-foreground);`),
1867
- "--sidebar-ring: var(--color-primary);",
1868
- "--sidebar-border: var(--color-border);",
1869
- "--sidebar-accent-foreground: var(--color-accent-foreground);",
1870
- "--sidebar-accent: var(--color-accent);",
1871
- "--sidebar-primary-foreground: var(--color-primary-foreground);",
1872
- "--sidebar-primary: var(--color-primary);",
1873
- "--sidebar-foreground: var(--color-muted-foreground);",
1874
- "--sidebar: var(--color-muted);",
1875
- "--border: var(--color-background-600);",
1876
- "--ring: var(--color-primary);",
1877
- "--popover: var(--color-background);",
1878
- "--popover-foreground: var(--color-foreground);",
1879
- "--card: var(--color-muted);",
1880
- "--card-foreground: var(--color-muted-foreground);",
1881
- "--radius-sm: var(--radius-small);",
1882
- "--radius-md: var(--radius-medium);",
1883
- "--radius-lg: var(--radius-large);",
1884
- "--radius-xl: var(--radius-extra-large);",
1885
- "--text-xs: var(--font-size-extra-small);",
1886
- "--text-sm: var(--font-size-small);",
1887
- "--text-base: var(--font-size-regular);",
1888
- "--text-lg: var(--font-size-large);",
1889
- "--text-xl: var(--font-size-extra-large);",
1890
- "--text-2xl: var(--font-size-giant);"
1891
- ];
1892
- /**
1893
- * Overrides for global tailwindcss for specifically dark mode.
1894
- */
1895
- const globalDarkCSSOverride = ["--border: var(--color-background-400);"];
1896
- /**
1897
- * Generate a complete CSS string for a resolved theme.
1898
- * Outputs 2–3 blocks: light default, dark explicit via `[data-theme-mode="dark"]`,
1899
- * and (unless `disableAutoTheme`) a `prefers-color-scheme: dark` media query block.
1900
- */
1901
- function generateThemeCSS(theme, options = {}) {
1902
- const sel = `[data-theme="${theme.id}"]`;
1903
- const tw = options.mapTailwindColors ?? true;
1904
- const blocks = [];
1905
- blocks.push(`${sel} {`);
1906
- blocks.push(...globalCSSOverride);
1907
- blocks.push(...emitNonColorVars(theme));
1908
- blocks.push(...emitColorVars(theme.light));
1909
- if (tw) blocks.push(...emitTailwindOverrides());
1910
- blocks.push(`}`);
1911
- blocks.push(`${sel}[data-theme-mode="dark"] {`);
1912
- blocks.push(...globalDarkCSSOverride);
1913
- blocks.push(...emitColorVars(theme.dark));
1914
- if (tw) blocks.push(...emitTailwindOverrides(true));
1915
- blocks.push(`}`);
1916
- if (!options.disableAutoTheme) {
1917
- blocks.push(`@media (prefers-color-scheme: dark) {`);
1918
- blocks.push(`${sel}:not([data-theme-mode]) {`);
1919
- blocks.push(...globalDarkCSSOverride);
1920
- blocks.push(...emitColorVars(theme.dark).map((l) => `${l}`));
1921
- if (tw) blocks.push(...emitTailwindOverrides(true).map((l) => `${l}`));
1922
- blocks.push(`}`);
1923
- blocks.push(`}`);
1924
- }
1925
- return blocks.join("\n");
1926
- }
1927
- //#endregion
1928
- //#region ../core/src/theme/defaults.ts
1929
- const DEFAULT_FONT_SIZES = {
1930
- extraSmall: "0.75rem",
1931
- small: "0.875rem",
1932
- regular: "1rem",
1933
- large: "1.125rem",
1934
- extraLarge: "1.25rem",
1935
- giant: "1.5rem"
1936
- };
1937
- const DEFAULT_FONT_FAMILIES = {
1938
- header: "Inter",
1939
- body: "Inter"
1940
- };
1941
- const DEFAULT_SPACING = "0.25rem";
1942
- const DEFAULT_RADII = {
1943
- small: "0.25rem",
1944
- medium: "0.5rem",
1945
- large: "0.75rem",
1946
- extraLarge: "1rem"
1947
- };
1948
- const DEFAULT_COLORS = {
1949
- background: "#ffffff",
1950
- foreground: "#1a1a1a",
1951
- primary: "#3b82f6",
1952
- secondary: "#6b7280",
1953
- accent: "#10b981",
1954
- muted: "#f3f4f6",
1955
- destructive: "#ef4444",
1956
- mutedForeground: "#6b7280"
1957
- };
1958
- const DEFAULT_THEME_ID = "default";
1959
- const DEFAULT_THEME_NAME = "Default Theme";
1960
- /**
1961
- * Build a fresh ThemeDefinition populated with all defaults.
1962
- * Returns a new object each call because Color instances are mutable — do not cache the result.
1963
- */
1964
- function getDefaultThemeDefinition() {
1965
- const bg = new Color(DEFAULT_COLORS.background);
1966
- const fg = new Color(DEFAULT_COLORS.foreground);
1967
- const primary = new Color(DEFAULT_COLORS.primary);
1968
- const secondary = new Color(DEFAULT_COLORS.secondary);
1969
- const accent = new Color(DEFAULT_COLORS.accent);
1970
- const muted = new Color(DEFAULT_COLORS.muted);
1971
- const destructive = new Color(DEFAULT_COLORS.destructive);
1972
- const mutedFg = new Color(DEFAULT_COLORS.mutedForeground);
1973
- const darkBg = new Color("#0a0a0a");
1974
- const darkFg = new Color("#fafafa");
1975
- const darkMuted = new Color("#171717");
1976
- const darkMutedForeground = new Color("#dddddd");
1977
- return {
1978
- id: DEFAULT_THEME_ID,
1979
- name: DEFAULT_THEME_NAME,
1980
- light: {
1981
- background: {
1982
- base: bg,
1983
- foreground: fg
1984
- },
1985
- foreground: {
1986
- base: fg,
1987
- foreground: bg
1988
- },
1989
- primary: {
1990
- base: primary,
1991
- foreground: getForegroundColor(fg, primary)
1992
- },
1993
- secondary: {
1994
- base: secondary,
1995
- foreground: getForegroundColor(fg, secondary)
1996
- },
1997
- accent: {
1998
- base: accent,
1999
- foreground: getForegroundColor(fg, accent)
2000
- },
2001
- muted: {
2002
- base: muted,
2003
- foreground: mutedFg
2004
- },
2005
- destructive: {
2006
- base: destructive,
2007
- foreground: getForegroundColor(fg, destructive)
2008
- }
2009
- },
2010
- dark: {
2011
- background: {
2012
- base: darkBg,
2013
- foreground: darkFg
2014
- },
2015
- foreground: {
2016
- base: darkFg,
2017
- foreground: darkBg
2018
- },
2019
- muted: {
2020
- base: darkMuted,
2021
- foreground: darkMutedForeground
2022
- }
2023
- },
2024
- fontSizes: { ...DEFAULT_FONT_SIZES },
2025
- fontFamilies: { ...DEFAULT_FONT_FAMILIES },
2026
- spacing: DEFAULT_SPACING,
2027
- radii: { ...DEFAULT_RADII }
2028
- };
2029
- }
2030
- //#endregion
2031
- //#region ../core/src/theme/serialisation.ts
2032
- function colorToPlain(color) {
2033
- return {
2034
- l: color.oklch.l ?? 0,
2035
- c: color.oklch.c ?? 0,
2036
- h: color.oklch.h ?? 0
2037
- };
2038
- }
2039
- function plainToColor(plain) {
2040
- return new Color("oklch", [
2041
- plain.l,
2042
- plain.c,
2043
- plain.h
2044
- ]);
2045
- }
2046
- /**
2047
- * Serialise a ThemeDefinition (with Color objects) to a plain JSON payload
2048
- * suitable for backend storage.
2049
- */
2050
- function serialiseTheme(def) {
2051
- const light = {};
2052
- for (const name of SEMANTIC_COLOR_NAMES) light[name] = {
2053
- base: colorToPlain(def.light[name].base),
2054
- foreground: colorToPlain(def.light[name].foreground)
2055
- };
2056
- const dark = {};
2057
- for (const [name, value] of Object.entries(def.dark)) {
2058
- if (!value) continue;
2059
- dark[name] = {
2060
- ...value.base ? { base: colorToPlain(value.base) } : {},
2061
- ...value.foreground ? { foreground: colorToPlain(value.foreground) } : {}
2062
- };
2063
- }
2064
- return {
2065
- id: def.id,
2066
- name: def.name,
2067
- light,
2068
- dark,
2069
- fontSizes: { ...def.fontSizes },
2070
- fontFamilies: { ...def.fontFamilies },
2071
- spacing: def.spacing,
2072
- radii: { ...def.radii },
2073
- ...def.syncWithBrandColors ? { syncWithBrandColors: true } : {}
2074
- };
2075
- }
2076
- /**
2077
- * Deserialise a backend payload into a ThemeDefinition with Color objects.
2078
- * Accepts `Record<string, unknown>` because API data is untyped at the boundary.
2079
- * Falls back to default colors for any missing light-mode entries.
2080
- */
2081
- function deserialiseTheme(payload) {
2082
- const lightRaw = payload.light ?? {};
2083
- const darkRaw = payload.dark ?? {};
2084
- const defaults = getDefaultThemeDefinition();
2085
- const light = {};
2086
- for (const name of SEMANTIC_COLOR_NAMES) {
2087
- const entry = lightRaw[name];
2088
- if (entry) light[name] = {
2089
- base: plainToColor(entry.base),
2090
- foreground: plainToColor(entry.foreground)
2091
- };
2092
- else {
2093
- console.warn(`[theme] deserialiseTheme: missing light color "${name}", using default`);
2094
- light[name] = defaults.light[name];
2095
- }
2096
- }
2097
- const dark = {};
2098
- for (const [name, value] of Object.entries(darkRaw)) {
2099
- if (!value) continue;
2100
- dark[name] = {
2101
- ...value.base ? { base: plainToColor(value.base) } : {},
2102
- ...value.foreground ? { foreground: plainToColor(value.foreground) } : {}
2103
- };
2104
- }
2105
- return {
2106
- id: payload.id,
2107
- name: payload.name,
2108
- light,
2109
- dark,
2110
- fontSizes: payload.fontSizes ?? DEFAULT_FONT_SIZES,
2111
- fontFamilies: payload.fontFamilies ?? DEFAULT_FONT_FAMILIES,
2112
- spacing: payload.spacing ?? "0.25rem",
2113
- radii: payload.radii ?? DEFAULT_RADII,
2114
- ...payload.syncWithBrandColors === true ? { syncWithBrandColors: true } : {}
2115
- };
2116
- }
2117
- //#endregion
2118
- //#region ../core/src/theme/transforms.ts
2119
- /**
2120
- * Check if a theme config uses the new structured format (has a `light` key
2121
- * that is an object) vs the legacy flat format.
2122
- */
2123
- function isNewThemeFormat(config) {
2124
- return config.light != null && typeof config.light === "object";
2125
- }
2126
- /**
2127
- * Convert a legacy flat config to a ThemeDefinition.
2128
- * Legacy format: { base: "#fff", text: "#000", primary: "oklch(0.6 0.2 250)", ... }
2129
- */
2130
- function legacyConfigToDefinition(id, name, config) {
2131
- const bg = parseColor(config.base ?? config.background ?? DEFAULT_COLORS.background);
2132
- const fg = parseColor(config.text ?? config.foreground ?? DEFAULT_COLORS.foreground);
2133
- const primary = parseColor(config.primary ?? DEFAULT_COLORS.primary);
2134
- const secondary = parseColor(config.secondary ?? DEFAULT_COLORS.secondary);
2135
- const accent = parseColor(config.accent ?? DEFAULT_COLORS.accent);
2136
- const muted = parseColor(config.muted ?? DEFAULT_COLORS.muted);
2137
- const destructive = parseColor(config.destructive ?? DEFAULT_COLORS.destructive);
2138
- const mutedFg = parseColor(config.mutedForeground ?? DEFAULT_COLORS.mutedForeground);
2139
- return {
2140
- id: String(id),
2141
- name,
2142
- light: {
2143
- background: {
2144
- base: bg,
2145
- foreground: fg
2146
- },
2147
- foreground: {
2148
- base: fg,
2149
- foreground: bg
2150
- },
2151
- primary: {
2152
- base: primary,
2153
- foreground: getForegroundColor(fg, primary)
2154
- },
2155
- secondary: {
2156
- base: secondary,
2157
- foreground: getForegroundColor(fg, secondary)
2158
- },
2159
- accent: {
2160
- base: accent,
2161
- foreground: getForegroundColor(fg, accent)
2162
- },
2163
- muted: {
2164
- base: muted,
2165
- foreground: mutedFg
2166
- },
2167
- destructive: {
2168
- base: destructive,
2169
- foreground: getForegroundColor(fg, destructive)
2170
- }
2171
- },
2172
- dark: {},
2173
- fontSizes: {
2174
- extraSmall: config.extraSmall ?? DEFAULT_FONT_SIZES.extraSmall,
2175
- small: config.small ?? DEFAULT_FONT_SIZES.small,
2176
- regular: config.regular ?? DEFAULT_FONT_SIZES.regular,
2177
- large: config.large ?? DEFAULT_FONT_SIZES.large,
2178
- extraLarge: config.extraLarge ?? DEFAULT_FONT_SIZES.extraLarge,
2179
- giant: config.giant ?? DEFAULT_FONT_SIZES.giant
2180
- },
2181
- fontFamilies: {
2182
- header: config.headerFont ?? DEFAULT_FONT_FAMILIES.header,
2183
- body: config.bodyFont ?? DEFAULT_FONT_FAMILIES.body
2184
- },
2185
- spacing: config.globalSpacing ?? "0.25rem",
2186
- radii: {
2187
- small: config.radiusSmall ?? DEFAULT_RADII.small,
2188
- medium: config.radiusMedium ?? DEFAULT_RADII.medium,
2189
- large: config.radiusLarge ?? DEFAULT_RADII.large,
2190
- extraLarge: config.radiusExtraLarge ?? DEFAULT_RADII.extraLarge
2191
- }
2192
- };
2193
- }
2194
- /**
2195
- * Build a ThemeDefinition from a single API theme object.
2196
- * Handles both new structured format and legacy flat format.
2197
- */
2198
- function buildThemeDefinition(theme) {
2199
- const config = theme.config ?? {};
2200
- if (isNewThemeFormat(config)) return deserialiseTheme({
2201
- ...config,
2202
- id: String(theme.id),
2203
- name: theme.name ?? "Untitled Theme"
2204
- });
2205
- return legacyConfigToDefinition(theme.id, theme.name ?? "Untitled Theme", config);
2206
- }
2207
- /**
2208
- * Transform raw API themes to ThemeDefinition[].
2209
- * Catches and logs errors per theme (graceful degradation).
2210
- */
2211
- function transformThemes(themes) {
2212
- return themes.flatMap((theme) => {
2213
- try {
2214
- return [buildThemeDefinition(theme)];
2215
- } catch (error) {
2216
- console.error(`[theme] Failed to build theme id=${theme.id}:`, error);
2217
- return [];
2218
- }
2219
- });
2220
- }
2221
- /**
2222
- * Get the active theme ID from a list of raw API themes.
2223
- * Falls back to the first theme if none is marked active.
2224
- */
2225
- function getActiveThemeId(themes) {
2226
- const active = themes.find((t) => t.active) ?? themes[0];
2227
- return active ? String(active.id) : void 0;
2228
- }
2229
- //#endregion
2230
- //#region ../core/src/theme/theme-applicator.ts
2231
- const STYLE_PREFIX = "theme-style-";
2232
- const FONT_LINK_PREFIX = "theme-font-";
2233
- const SYSTEM_FONTS = new Set([
2234
- "sans-serif",
2235
- "serif",
2236
- "monospace",
2237
- "cursive",
2238
- "fantasy",
2239
- "system-ui",
2240
- "ui-sans-serif",
2241
- "ui-serif",
2242
- "ui-monospace"
2243
- ]);
2244
- /** Build a Google Fonts CSS2 URL for a given font family with all weights. */
2245
- function buildGoogleFontUrl(family) {
2246
- return `https://fonts.googleapis.com/css2?family=${encodeURIComponent(family).replace(/%20/g, "+")}:wght@100;200;300;400;500;600;700;800;900&display=swap`;
2247
- }
2248
- /** Check if a font family value needs to be loaded (i.e. is not a CSS var or system font). */
2249
- function isLoadableFont(value) {
2250
- if (!value) return false;
2251
- if (value.startsWith("var(")) return false;
2252
- return !SYSTEM_FONTS.has(value.toLowerCase());
2253
- }
2254
- /** Deterministic link element ID for a font family. */
2255
- function fontLinkId(family) {
2256
- return `${FONT_LINK_PREFIX}${family.replace(/\s+/g, "-").toLowerCase()}`;
2257
- }
2258
- /**
2259
- * Inject or update `<link>` elements for Google Fonts used by the theme.
2260
- * Removes links for fonts that are no longer referenced.
2261
- */
2262
- function loadThemeFonts(theme) {
2263
- if (typeof document === "undefined") return;
2264
- const fontsToLoad = /* @__PURE__ */ new Set();
2265
- for (const key of FONT_FAMILY_KEYS) {
2266
- const value = theme.fontFamilies[key];
2267
- if (isLoadableFont(value)) fontsToLoad.add(value);
2268
- }
2269
- document.querySelectorAll(`link[id^="${FONT_LINK_PREFIX}"]`).forEach((link) => {
2270
- const owners = link.getAttribute("data-font-theme-ids")?.split(",") ?? [];
2271
- if (!owners.includes(theme.id)) return;
2272
- const fontName = link.getAttribute("data-font-family");
2273
- if (fontName && !fontsToLoad.has(fontName)) {
2274
- const remaining = owners.filter((id) => id !== theme.id);
2275
- if (remaining.length === 0) link.remove();
2276
- else link.setAttribute("data-font-theme-ids", remaining.join(","));
2277
- }
2278
- });
2279
- for (const family of fontsToLoad) {
2280
- const id = fontLinkId(family);
2281
- const existing = document.getElementById(id);
2282
- if (existing) {
2283
- const owners = existing.getAttribute("data-font-theme-ids")?.split(",") ?? [];
2284
- if (!owners.includes(theme.id)) existing.setAttribute("data-font-theme-ids", [...owners, theme.id].join(","));
2285
- } else {
2286
- const link = document.createElement("link");
2287
- link.id = id;
2288
- link.rel = "stylesheet";
2289
- link.href = buildGoogleFontUrl(family);
2290
- link.setAttribute("data-font-family", family);
2291
- link.setAttribute("data-font-theme-ids", theme.id);
2292
- document.head.appendChild(link);
2293
- }
2294
- }
2295
- }
2296
- /** Remove all font `<link>` elements injected by the theme system. */
2297
- function removeAllFontLinks() {
2298
- if (typeof document === "undefined") return;
2299
- document.querySelectorAll(`link[id^="${FONT_LINK_PREFIX}"]`).forEach((el) => el.remove());
2300
- }
2301
- /**
2302
- * Inject or update a `<style>` element in `<head>` for the given theme.
2303
- * The element ID is deterministic (`theme-style-{themeId}`) so repeated calls
2304
- * for the same theme are idempotent — the existing element is updated in place.
2305
- * Also loads Google Fonts referenced by the theme's font families.
2306
- * No-op when `document` is unavailable (SSR).
2307
- */
2308
- function applyTheme(theme, options) {
2309
- if (typeof document === "undefined") return;
2310
- try {
2311
- loadThemeFonts(theme);
2312
- const styleId = `${STYLE_PREFIX}${theme.id}`;
2313
- let el = document.getElementById(styleId);
2314
- if (!el) {
2315
- el = document.createElement("style");
2316
- el.id = styleId;
2317
- document.head.appendChild(el);
2318
- }
2319
- el.textContent = generateThemeCSS(theme, options);
2320
- } catch (error) {
2321
- console.error(`[theme] applyTheme failed for "${theme.id}":`, error);
2322
- }
2323
- }
2324
- /** Remove an injected theme stylesheet and clean up font link ownership. No-op during SSR. */
2325
- function removeTheme(themeId) {
2326
- if (typeof document === "undefined") return;
2327
- document.getElementById(`${STYLE_PREFIX}${themeId}`)?.remove();
2328
- document.querySelectorAll(`link[id^="${FONT_LINK_PREFIX}"]`).forEach((link) => {
2329
- const remaining = (link.getAttribute("data-font-theme-ids")?.split(",") ?? []).filter((id) => id !== themeId);
2330
- if (remaining.length === 0) link.remove();
2331
- else link.setAttribute("data-font-theme-ids", remaining.join(","));
2332
- });
2333
- }
2334
- /** Remove all injected theme stylesheets and font links. No-op during SSR. */
2335
- function removeAllThemes() {
2336
- if (typeof document === "undefined") return;
2337
- document.querySelectorAll(`style[id^="${STYLE_PREFIX}"]`).forEach((el) => el.remove());
2338
- removeAllFontLinks();
2339
- }
2340
- //#endregion
2341
1572
  //#region src/providers/FluidThemeProvider.tsx
2342
1573
  /**
2343
1574
  * Theme Provider for Fluid SDK
@@ -2714,14 +1945,14 @@ const widgetPropertySchemas = {
2714
1945
  AlertWidget: () => import("./AlertWidget-DRX_3F4c.mjs").then((n) => n.n).then((m) => m.alertWidgetPropertySchema),
2715
1946
  BulletListWidget: () => import("./BulletListWidget-D0ivXcaY.mjs").then((n) => n.n).then((m) => m.bulletListWidgetPropertySchema),
2716
1947
  CalendarWidget: () => import("./CalendarWidget-CoicJaJV.mjs").then((n) => n.n).then((m) => m.calendarWidgetPropertySchema),
2717
- CardWidget: () => import("./CardWidget-BDIscQev.mjs").then((n) => n.n).then((m) => m.cardWidgetPropertySchema),
1948
+ CardWidget: () => import("./CardWidget-BjvrudYQ.mjs").then((n) => n.n).then((m) => m.cardWidgetPropertySchema),
2718
1949
  CarouselWidget: () => import("./CarouselWidget-CCmfNNEU.mjs").then((n) => n.n).then((m) => m.carouselWidgetPropertySchema),
2719
1950
  CatchUpWidget: () => import("./CatchUpWidget-BU3Z0m-4.mjs").then((n) => n.n).then((m) => m.catchUpWidgetPropertySchema),
2720
1951
  ChartWidget: () => import("./ChartWidget-BZDwNO8g.mjs").then((n) => n.n).then((m) => m.chartWidgetPropertySchema),
2721
- ContainerWidget: () => import("./ContainerWidget-PjWuvAlm.mjs").then((n) => n.n).then((m) => m.containerWidgetPropertySchema),
1952
+ ContainerWidget: () => import("./ContainerWidget-B51UF7DK.mjs").then((n) => n.n).then((m) => m.containerWidgetPropertySchema),
2722
1953
  EmbedWidget: () => import("./EmbedWidget-DmgayrKN.mjs").then((n) => n.n).then((m) => m.embedWidgetPropertySchema),
2723
1954
  ImageWidget: () => import("./ImageWidget-C_pLQwbi.mjs").then((n) => n.n).then((m) => m.imageWidgetPropertySchema),
2724
- LayoutWidget: () => import("./LayoutWidget-oVmk8nm9.mjs").then((n) => n.n).then((m) => m.layoutWidgetPropertySchema),
1955
+ LayoutWidget: () => import("./LayoutWidget-CtqIb8_Z.mjs").then((n) => n.n).then((m) => m.layoutWidgetPropertySchema),
2725
1956
  LinkWidget: () => import("./LinkWidget-DSOKZ1zC.mjs").then((n) => n.n).then((m) => m.linkWidgetPropertySchema),
2726
1957
  ListWidget: () => import("./ListWidget-D6ASLbnl.mjs").then((n) => n.n).then((m) => m.listWidgetPropertySchema),
2727
1958
  MySiteWidget: () => import("./MySiteWidget-Q3Zduh9j.mjs").then((n) => n.n).then((m) => m.mySiteWidgetPropertySchema),
@@ -2908,6 +2139,6 @@ function useFluidContext() {
2908
2139
  return context;
2909
2140
  }
2910
2141
  //#endregion
2911
- export { mergeDarkOverrides as A, usePayApi as B, DEFAULT_THEME_ID as C, deriveDarkVariant as D, generateThemeCSS as E, RADIUS_KEYS as F, createPersister as H, SEMANTIC_COLOR_NAMES as I, SHADE_STEPS as L, resolveTheme as M, FONT_FAMILY_KEYS as N, generateShades as O, FONT_SIZE_KEYS as P, ApiError as R, DEFAULT_SPACING as S, getDefaultThemeDefinition as T, deleteDatabase as U, useAppDefinitionApi as V, serialiseTheme as _, createScreen as a, DEFAULT_FONT_SIZES as b, FluidThemeProvider as c, removeAllThemes as d, removeTheme as f, deserialiseTheme as g, transformThemes as h, widgetPropertySchemas as i, parseColor as j, getForegroundColor as k, useThemeContext as l, getActiveThemeId as m, useFluidContext as n, createWidgetFromShareable as o, buildThemeDefinition as p, DEFAULT_SDK_WIDGET_REGISTRY as r, createWidgetRegistry as s, FluidProvider as t, applyTheme as u, DEFAULT_COLORS as v, DEFAULT_THEME_NAME as w, DEFAULT_RADII as x, DEFAULT_FONT_FAMILIES as y, useLanguagesApi as z };
2142
+ export { createScreen as a, FluidThemeProvider as c, useLanguagesApi as d, usePayApi as f, deleteDatabase as h, widgetPropertySchemas as i, useThemeContext as l, createPersister as m, useFluidContext as n, createWidgetFromShareable as o, useAppDefinitionApi as p, DEFAULT_SDK_WIDGET_REGISTRY as r, createWidgetRegistry as s, FluidProvider as t, ApiError as u };
2912
2143
 
2913
- //# sourceMappingURL=FluidProvider-CGqVqcHP.mjs.map
2144
+ //# sourceMappingURL=FluidProvider-DFZiXiqm.mjs.map