@fluid-app/portal-sdk 0.1.159 → 0.1.160
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -196
- package/dist/{FluidProvider-Cqf2kmUc.mjs → FluidProvider-B00jTGTH.mjs} +1510 -1624
- package/dist/FluidProvider-B00jTGTH.mjs.map +1 -0
- package/dist/{FluidProvider-Bc-3uN7M.cjs → FluidProvider-BtGi2jJt.cjs} +1458 -1614
- package/dist/FluidProvider-BtGi2jJt.cjs.map +1 -0
- package/dist/{MessagingScreen-BGqIn-c2.cjs → MessagingScreen-BRCUJDDZ.cjs} +2 -2
- package/dist/{MessagingScreen-CbmuvlH6.mjs → MessagingScreen-BWpSXB8Q.mjs} +2 -2
- package/dist/{MessagingScreen-CbmuvlH6.mjs.map → MessagingScreen-BWpSXB8Q.mjs.map} +1 -1
- package/dist/{MessagingScreen-CBPjP4du.cjs → MessagingScreen-C_OLIWCC.cjs} +2 -2
- package/dist/{MessagingScreen-CBPjP4du.cjs.map → MessagingScreen-C_OLIWCC.cjs.map} +1 -1
- package/dist/{MySiteScreen-DSDLDnCN.cjs → MySiteScreen-B90qzZPe.cjs} +2 -2
- package/dist/{MySiteScreen-Cl6nuU99.cjs → MySiteScreen-DLuHDXB1.cjs} +2 -2
- package/dist/{MySiteScreen-Cl6nuU99.cjs.map → MySiteScreen-DLuHDXB1.cjs.map} +1 -1
- package/dist/{MySiteScreen-BT1PBPsH.mjs → MySiteScreen-VaOB-vWk.mjs} +2 -2
- package/dist/{MySiteScreen-BT1PBPsH.mjs.map → MySiteScreen-VaOB-vWk.mjs.map} +1 -1
- package/dist/ProductsScreen-B8OynxlP.cjs +13 -0
- package/dist/{ProductsScreen-4WRrJcvw.mjs → ProductsScreen-CbVSNv1l.mjs} +3 -5
- package/dist/ProductsScreen-CbVSNv1l.mjs.map +1 -0
- package/dist/{ProductsScreen-BCs3YKVk.cjs → ProductsScreen-D1bw4ZIH.cjs} +3 -5
- package/dist/ProductsScreen-D1bw4ZIH.cjs.map +1 -0
- package/dist/ProductsScreen-Pq3j09nI.mjs +11 -0
- package/dist/{ProfileScreen-BgyrIQdL.mjs → ProfileScreen-BCHljkWD.mjs} +2 -2
- package/dist/{ProfileScreen-BgyrIQdL.mjs.map → ProfileScreen-BCHljkWD.mjs.map} +1 -1
- package/dist/{ProfileScreen-B0KNWXpV.cjs → ProfileScreen-Bf-lYNzz.cjs} +2 -2
- package/dist/{ProfileScreen-DLLLRNB2.cjs → ProfileScreen-ksdtbydb.cjs} +2 -2
- package/dist/{ProfileScreen-DLLLRNB2.cjs.map → ProfileScreen-ksdtbydb.cjs.map} +1 -1
- package/dist/{ShareablesScreen-ySSwCVSI.cjs → ShareablesScreen-6wgiwi_9.cjs} +3 -5
- package/dist/ShareablesScreen-6wgiwi_9.cjs.map +1 -0
- package/dist/{ShareablesScreen-B8cmh8JC.mjs → ShareablesScreen-BLCukNTk.mjs} +3 -5
- package/dist/{ShareablesScreen-B8cmh8JC.mjs.map → ShareablesScreen-BLCukNTk.mjs.map} +1 -1
- package/dist/ShareablesScreen-CM9OH-Nx.mjs +11 -0
- package/dist/ShareablesScreen-rmLcUhbL.cjs +13 -0
- package/dist/{ShopScreen-DhMo8bvP.cjs → ShopScreen-8KKwwjka.cjs} +3 -3
- package/dist/{ShopScreen-DhMo8bvP.cjs.map → ShopScreen-8KKwwjka.cjs.map} +1 -1
- package/dist/{ShopScreen-BkvyW0pE.cjs → ShopScreen-CjEbB3Q7.cjs} +2 -2
- package/dist/{ShopScreen-BFFGYwdV.mjs → ShopScreen-Dn5LwwYD.mjs} +3 -3
- package/dist/{ShopScreen-BFFGYwdV.mjs.map → ShopScreen-Dn5LwwYD.mjs.map} +1 -1
- package/dist/{SubscriptionsScreen-RScBCcY0.cjs → SubscriptionsScreen-B16wPAoA.cjs} +2 -2
- package/dist/SubscriptionsScreen-B16wPAoA.cjs.map +1 -0
- package/dist/{SubscriptionsScreen-CiNR7JUC.mjs → SubscriptionsScreen-B4cTlgDU.mjs} +2 -2
- package/dist/SubscriptionsScreen-B4cTlgDU.mjs.map +1 -0
- package/dist/{SubscriptionsScreen-DMh-GE6n.cjs → SubscriptionsScreen-nRUMdnx7.cjs} +1 -1
- package/dist/{dist-Bg8UyHyM.cjs → dist-lO2OG0T5.cjs} +1 -1
- package/dist/{dist-Bg8UyHyM.cjs.map → dist-lO2OG0T5.cjs.map} +1 -1
- package/dist/index.cjs +134 -143
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +444 -655
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +444 -655
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +127 -130
- package/dist/index.mjs.map +1 -1
- package/dist/{sortable.esm-Cz-CP2N8.mjs → sortable.esm-DSrWP4x9.mjs} +1 -1
- package/dist/{sortable.esm-Cz-CP2N8.mjs.map → sortable.esm-DSrWP4x9.mjs.map} +1 -1
- package/dist/{use-portal-products-client-BmlUixy4.cjs → use-portal-products-client-BHSBT64s.cjs} +2 -2
- package/dist/use-portal-products-client-BHSBT64s.cjs.map +1 -0
- package/dist/{use-portal-products-client-DQK9nFxT.mjs → use-portal-products-client-tbqk6XUq.mjs} +2 -2
- package/dist/use-portal-products-client-tbqk6XUq.mjs.map +1 -0
- package/dist/{use-portal-shareables-api-KVPj0Jfr.mjs → use-portal-shareables-api-B9B4XTjw.mjs} +107 -201
- package/dist/use-portal-shareables-api-B9B4XTjw.mjs.map +1 -0
- package/dist/{use-portal-shareables-api-D5D6uIJy.cjs → use-portal-shareables-api-MBl0d0eQ.cjs} +106 -206
- package/dist/use-portal-shareables-api-MBl0d0eQ.cjs.map +1 -0
- package/package.json +15 -15
- package/dist/FluidProvider-Bc-3uN7M.cjs.map +0 -1
- package/dist/FluidProvider-Cqf2kmUc.mjs.map +0 -1
- package/dist/ProductsScreen-4WRrJcvw.mjs.map +0 -1
- package/dist/ProductsScreen-BCs3YKVk.cjs.map +0 -1
- package/dist/ProductsScreen-BR9TN4So.cjs +0 -48
- package/dist/ProductsScreen-Da6eWJ_c.mjs +0 -46
- package/dist/ShareablesScreen-DW0wCYOj.mjs +0 -46
- package/dist/ShareablesScreen-DbPJOtCF.cjs +0 -48
- package/dist/ShareablesScreen-ySSwCVSI.cjs.map +0 -1
- package/dist/SubscriptionsScreen-CiNR7JUC.mjs.map +0 -1
- package/dist/SubscriptionsScreen-RScBCcY0.cjs.map +0 -1
- package/dist/use-portal-products-client-BmlUixy4.cjs.map +0 -1
- package/dist/use-portal-products-client-DQK9nFxT.mjs.map +0 -1
- package/dist/use-portal-shareables-api-D5D6uIJy.cjs.map +0 -1
- package/dist/use-portal-shareables-api-KVPj0Jfr.mjs.map +0 -1
|
@@ -473,7 +473,7 @@ function createPersister() {
|
|
|
473
473
|
});
|
|
474
474
|
}
|
|
475
475
|
//#endregion
|
|
476
|
-
//#region ../core/src/
|
|
476
|
+
//#region ../core/src/fluidos-api-context.ts
|
|
477
477
|
const FluidOsReadApiContext = createContext(null);
|
|
478
478
|
const FluidOsBuilderApiContext = createContext(null);
|
|
479
479
|
/** Provider for read-only manifest fetching (used by SDK). */
|
|
@@ -694,18 +694,6 @@ function createFetchClient(config) {
|
|
|
694
694
|
};
|
|
695
695
|
}
|
|
696
696
|
//#endregion
|
|
697
|
-
//#region ../../api-clients/fluidos/src/namespaces/fluid_os.ts
|
|
698
|
-
/**
|
|
699
|
-
* Get active Fluid OS definition
|
|
700
|
-
* Retrieve the active Fluid OS definition manifest for a specific platform
|
|
701
|
-
*
|
|
702
|
-
* @param client - Fetch client instance
|
|
703
|
-
* @param params - params
|
|
704
|
-
*/
|
|
705
|
-
async function getFluidOSManifest(client, params) {
|
|
706
|
-
return client.get(`/api/fluid_os/definitions/active`, params);
|
|
707
|
-
}
|
|
708
|
-
//#endregion
|
|
709
697
|
//#region src/client/types.ts
|
|
710
698
|
/**
|
|
711
699
|
* HTTP methods supported by the API client.
|
|
@@ -719,1807 +707,1705 @@ const HTTP_METHODS = {
|
|
|
719
707
|
DELETE: "DELETE"
|
|
720
708
|
};
|
|
721
709
|
//#endregion
|
|
722
|
-
//#region
|
|
723
|
-
const SEMANTIC_COLOR_NAMES = [
|
|
724
|
-
"background",
|
|
725
|
-
"foreground",
|
|
726
|
-
"primary",
|
|
727
|
-
"secondary",
|
|
728
|
-
"accent",
|
|
729
|
-
"muted",
|
|
730
|
-
"destructive"
|
|
731
|
-
];
|
|
732
|
-
const SHADE_STEPS = [
|
|
733
|
-
100,
|
|
734
|
-
200,
|
|
735
|
-
300,
|
|
736
|
-
400,
|
|
737
|
-
500,
|
|
738
|
-
600,
|
|
739
|
-
700,
|
|
740
|
-
800,
|
|
741
|
-
900
|
|
742
|
-
];
|
|
743
|
-
const FONT_SIZE_KEYS = [
|
|
744
|
-
"extraSmall",
|
|
745
|
-
"small",
|
|
746
|
-
"regular",
|
|
747
|
-
"large",
|
|
748
|
-
"extraLarge",
|
|
749
|
-
"giant"
|
|
750
|
-
];
|
|
751
|
-
const FONT_FAMILY_KEYS = ["header", "body"];
|
|
752
|
-
const RADIUS_KEYS = [
|
|
753
|
-
"small",
|
|
754
|
-
"medium",
|
|
755
|
-
"large",
|
|
756
|
-
"extraLarge"
|
|
757
|
-
];
|
|
758
|
-
//#endregion
|
|
759
|
-
//#region ../core/src/theme/color-engine.ts
|
|
710
|
+
//#region src/client/fluid-client.ts
|
|
760
711
|
/**
|
|
761
|
-
*
|
|
762
|
-
*
|
|
763
|
-
*
|
|
764
|
-
*
|
|
765
|
-
* @returns the parsed Color, or a neutral gray (`oklch(0.5 0 0)`) on failure
|
|
712
|
+
* Fluid API Client
|
|
713
|
+
* Adapted from: packages/fluidos-api-client/src/lib/fetch-client.ts
|
|
714
|
+
* Provides authenticated API access with domain-specific methods
|
|
766
715
|
*/
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
716
|
+
/**
|
|
717
|
+
* API Error class for structured error handling
|
|
718
|
+
*/
|
|
719
|
+
var ApiError = class ApiError extends Error {
|
|
720
|
+
status;
|
|
721
|
+
data;
|
|
722
|
+
constructor(message, status, data) {
|
|
723
|
+
super(message);
|
|
724
|
+
this.name = "ApiError";
|
|
725
|
+
this.status = status;
|
|
726
|
+
this.data = data;
|
|
727
|
+
const errorWithCapture = Error;
|
|
728
|
+
if (errorWithCapture.captureStackTrace) errorWithCapture.captureStackTrace(this, ApiError);
|
|
778
729
|
}
|
|
779
|
-
|
|
730
|
+
toJSON() {
|
|
731
|
+
return {
|
|
732
|
+
name: this.name,
|
|
733
|
+
message: this.message,
|
|
734
|
+
status: this.status,
|
|
735
|
+
data: this.data
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
};
|
|
780
739
|
/**
|
|
781
|
-
*
|
|
782
|
-
* whichever provides better contrast against `color`.
|
|
783
|
-
* Inversion triggers when the APCA contrast is below 50.
|
|
740
|
+
* Type guard for ApiError
|
|
784
741
|
*/
|
|
785
|
-
function
|
|
786
|
-
|
|
787
|
-
if (color.contrastAPCA(foreground) < 50) return new Color("oklch", [
|
|
788
|
-
color.oklch.l < .7 ? .95 : .15,
|
|
789
|
-
foreground.oklch.c || 0,
|
|
790
|
-
foreground.oklch.h || 0
|
|
791
|
-
]);
|
|
792
|
-
return foreground;
|
|
742
|
+
function isApiError(error) {
|
|
743
|
+
return error instanceof ApiError;
|
|
793
744
|
}
|
|
794
745
|
/**
|
|
795
|
-
*
|
|
796
|
-
* Base anchors at 500. Light shades (100–400) step toward white,
|
|
797
|
-
* dark shades (600–900) step toward black. Dark steps use an asymmetric
|
|
798
|
-
* multiplier (1.6×, 1.875×, 3×, 4× of `darkStep`) for a more gradual
|
|
799
|
-
* initial descent. Chroma is nudged per step for perceptually natural ramps.
|
|
746
|
+
* Type guard to check if a value is a non-null string
|
|
800
747
|
*/
|
|
801
|
-
function
|
|
802
|
-
|
|
803
|
-
const c = base.oklch.c ?? 0;
|
|
804
|
-
const h = base.oklch.h ?? 0;
|
|
805
|
-
const safeMax = l >= .885 ? .995 : .97;
|
|
806
|
-
const safeMin = l <= .33 ? 0 : .21;
|
|
807
|
-
const lightStep = (safeMax - l) / 5;
|
|
808
|
-
const darkStep = -(l - safeMin) / 8;
|
|
809
|
-
const shade = (lDelta, cDelta) => {
|
|
810
|
-
return new Color("oklch", [
|
|
811
|
-
Math.max(0, Math.min(1, l + lDelta)),
|
|
812
|
-
c <= .001 ? c : Math.max(0, c + cDelta),
|
|
813
|
-
h
|
|
814
|
-
]);
|
|
815
|
-
};
|
|
816
|
-
return {
|
|
817
|
-
100: shade(5 * lightStep, -.00375),
|
|
818
|
-
200: shade(4 * lightStep, -.00375),
|
|
819
|
-
300: shade(3 * lightStep, -.00375),
|
|
820
|
-
400: shade(2 * lightStep, -.00375),
|
|
821
|
-
500: new Color("oklch", [
|
|
822
|
-
l,
|
|
823
|
-
c,
|
|
824
|
-
h
|
|
825
|
-
]),
|
|
826
|
-
600: shade(1.6 * darkStep, .025),
|
|
827
|
-
700: shade(1.875 * 2 * darkStep, .05),
|
|
828
|
-
800: shade(6 * darkStep, .075),
|
|
829
|
-
900: shade(8 * darkStep, .1)
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
const DARK_DERIVATION_CONFIG = {
|
|
833
|
-
background: {
|
|
834
|
-
baseLightness: .15,
|
|
835
|
-
fgLightness: .93
|
|
836
|
-
},
|
|
837
|
-
foreground: {
|
|
838
|
-
baseLightness: .93,
|
|
839
|
-
fgLightness: .15
|
|
840
|
-
},
|
|
841
|
-
muted: {
|
|
842
|
-
baseLightness: .22,
|
|
843
|
-
fgLightness: .75
|
|
844
|
-
},
|
|
845
|
-
primary: {
|
|
846
|
-
baseLightness: "invert",
|
|
847
|
-
fgLightness: .95,
|
|
848
|
-
chromaScale: .9
|
|
849
|
-
},
|
|
850
|
-
secondary: {
|
|
851
|
-
baseLightness: "invert",
|
|
852
|
-
fgLightness: .93,
|
|
853
|
-
chromaScale: .85
|
|
854
|
-
},
|
|
855
|
-
accent: {
|
|
856
|
-
baseLightness: "invert",
|
|
857
|
-
fgLightness: .95,
|
|
858
|
-
chromaScale: .9
|
|
859
|
-
},
|
|
860
|
-
destructive: {
|
|
861
|
-
baseLightness: "invert",
|
|
862
|
-
fgLightness: .95,
|
|
863
|
-
chromaScale: .95
|
|
864
|
-
}
|
|
865
|
-
};
|
|
866
|
-
/** Invert OKLCH lightness (1 - l), clamped to [0.35, 0.75] to avoid extremes. */
|
|
867
|
-
function invertLightness(l) {
|
|
868
|
-
const inverted = 1 - l;
|
|
869
|
-
return Math.max(.35, Math.min(.75, inverted));
|
|
748
|
+
function isString(value) {
|
|
749
|
+
return typeof value === "string";
|
|
870
750
|
}
|
|
871
751
|
/**
|
|
872
|
-
*
|
|
752
|
+
* Extract error message from API response data using `in` operator narrowing.
|
|
753
|
+
* Checks common error message field names in order of precedence.
|
|
873
754
|
*/
|
|
874
|
-
function
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
return {
|
|
880
|
-
base: new Color("oklch", [
|
|
881
|
-
baseLightness,
|
|
882
|
-
(light.base.oklch.c || 0) * chromaScale,
|
|
883
|
-
light.base.oklch.h || 0
|
|
884
|
-
]),
|
|
885
|
-
foreground: new Color("oklch", [
|
|
886
|
-
fgLightness,
|
|
887
|
-
(light.foreground.oklch.c || 0) * chromaScale,
|
|
888
|
-
light.foreground.oklch.h || 0
|
|
889
|
-
])
|
|
890
|
-
};
|
|
755
|
+
function extractErrorMessage(data, fallback) {
|
|
756
|
+
if ("message" in data && isString(data.message)) return data.message;
|
|
757
|
+
if ("error_message" in data && isString(data.error_message)) return data.error_message;
|
|
758
|
+
if ("error" in data && isString(data.error)) return data.error;
|
|
759
|
+
return fallback;
|
|
891
760
|
}
|
|
892
761
|
/**
|
|
893
|
-
*
|
|
894
|
-
*
|
|
895
|
-
* foreground those are used; otherwise the missing channels are derived.
|
|
762
|
+
* Type guard to detect whether a parsed JSON value is an API envelope.
|
|
763
|
+
* Envelopes always have numeric `status` and a `data` key.
|
|
896
764
|
*/
|
|
897
|
-
function
|
|
898
|
-
|
|
899
|
-
for (const name of SEMANTIC_COLOR_NAMES) {
|
|
900
|
-
const lightInput = def.light[name];
|
|
901
|
-
const darkOverride = def.dark[name];
|
|
902
|
-
if (darkOverride?.base && darkOverride?.foreground) darkColors[name] = darkOverride;
|
|
903
|
-
else if (darkOverride) {
|
|
904
|
-
const base = darkOverride.base ?? deriveDarkVariant(name, lightInput).base;
|
|
905
|
-
darkColors[name] = {
|
|
906
|
-
base,
|
|
907
|
-
foreground: darkOverride.foreground ?? getForegroundColor(def.light.foreground.base, base)
|
|
908
|
-
};
|
|
909
|
-
} else darkColors[name] = deriveDarkVariant(name, lightInput);
|
|
910
|
-
}
|
|
911
|
-
return darkColors;
|
|
912
|
-
}
|
|
913
|
-
function resolveColorSet(colors) {
|
|
914
|
-
const resolved = {};
|
|
915
|
-
for (const name of SEMANTIC_COLOR_NAMES) {
|
|
916
|
-
const input = colors[name];
|
|
917
|
-
const shades = generateShades(input.base);
|
|
918
|
-
const resolvedShades = {};
|
|
919
|
-
for (const step of SHADE_STEPS) resolvedShades[step] = shades[step];
|
|
920
|
-
resolved[name] = {
|
|
921
|
-
base: input.base.clone(),
|
|
922
|
-
foreground: input.foreground.clone(),
|
|
923
|
-
shades: resolvedShades
|
|
924
|
-
};
|
|
925
|
-
}
|
|
926
|
-
return resolved;
|
|
765
|
+
function isApiEnvelope(value) {
|
|
766
|
+
return typeof value === "object" && value !== null && "status" in value && typeof value.status === "number" && "data" in value;
|
|
927
767
|
}
|
|
928
768
|
/**
|
|
929
|
-
*
|
|
930
|
-
* Dark mode colors are derived from light where not overridden.
|
|
769
|
+
* Creates a configured Fluid API client instance
|
|
931
770
|
*/
|
|
932
|
-
function
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
"--color-gray-100": "var(--color-muted-600)",
|
|
952
|
-
"--color-gray-200": "var(--color-border)"
|
|
953
|
-
};
|
|
954
|
-
/**
|
|
955
|
-
* Returns the inverted shade for dark mode foreground colors.
|
|
956
|
-
* In dark mode, light shades (50, 100) should map to dark values (950, 900) and vice versa.
|
|
957
|
-
*/
|
|
958
|
-
function getInvertedStep(shade) {
|
|
959
|
-
const shadeIndex = SHADE_STEPS.indexOf(shade);
|
|
960
|
-
return SHADE_STEPS[SHADE_STEPS.length - 1 - shadeIndex] || 500;
|
|
961
|
-
}
|
|
962
|
-
/**
|
|
963
|
-
* Map semantic colors to Tailwind built-in color names.
|
|
964
|
-
*/
|
|
965
|
-
function emitTailwindOverrides(darkMode = false) {
|
|
966
|
-
const TAILWIND_COLOR_MAP = {
|
|
967
|
-
gray: "foreground",
|
|
968
|
-
red: "destructive",
|
|
969
|
-
blue: "primary",
|
|
970
|
-
green: "accent"
|
|
971
|
-
};
|
|
972
|
-
const TAILWIND_SHADES = [
|
|
973
|
-
50,
|
|
974
|
-
100,
|
|
975
|
-
200,
|
|
976
|
-
300,
|
|
977
|
-
400,
|
|
978
|
-
500,
|
|
979
|
-
600,
|
|
980
|
-
700,
|
|
981
|
-
800,
|
|
982
|
-
900,
|
|
983
|
-
950
|
|
984
|
-
];
|
|
985
|
-
const SHADE_REMAP = {
|
|
986
|
-
50: 100,
|
|
987
|
-
950: 900
|
|
988
|
-
};
|
|
989
|
-
const lines = [];
|
|
990
|
-
for (const [twName, semantic] of Object.entries(TAILWIND_COLOR_MAP)) for (const shade of TAILWIND_SHADES) {
|
|
991
|
-
const step = SHADE_REMAP[shade] ?? shade;
|
|
992
|
-
const override = OVERRIDES[`--color-${twName}-${shade}`];
|
|
993
|
-
lines.push(`--color-${twName}-${shade}: ${override ? override : `var(--color-${semantic}-${semantic === "foreground" && darkMode === true ? getInvertedStep(step) : step})`};`);
|
|
994
|
-
}
|
|
995
|
-
lines.push("--color-white: var(--color-background);");
|
|
996
|
-
lines.push("--color-black: var(--color-foreground);");
|
|
997
|
-
return lines;
|
|
998
|
-
}
|
|
999
|
-
//#endregion
|
|
1000
|
-
//#region ../core/src/theme/css-generator.ts
|
|
1001
|
-
function colorToCSS(color) {
|
|
1002
|
-
const result = color.toString({ format: "oklch" });
|
|
1003
|
-
if (result.includes("NaN")) {
|
|
1004
|
-
console.warn("[theme] colorToCSS produced NaN, using neutral fallback:", result);
|
|
1005
|
-
return "oklch(0.5 0 0)";
|
|
771
|
+
function createFluidClient(config) {
|
|
772
|
+
const { baseUrl, getAuthToken, onAuthError, defaultHeaders = {} } = config;
|
|
773
|
+
const fetchClient = createFetchClient({
|
|
774
|
+
baseUrl,
|
|
775
|
+
...getAuthToken ? { getAuthToken } : {},
|
|
776
|
+
onAuthError,
|
|
777
|
+
defaultHeaders,
|
|
778
|
+
credentials: "include"
|
|
779
|
+
});
|
|
780
|
+
/**
|
|
781
|
+
* Build headers for a request.
|
|
782
|
+
* Auth is handled by session cookies via `credentials: 'include'` on fetch calls.
|
|
783
|
+
*/
|
|
784
|
+
function buildHeaders(customHeaders) {
|
|
785
|
+
return {
|
|
786
|
+
"Content-Type": "application/json",
|
|
787
|
+
...defaultHeaders,
|
|
788
|
+
...customHeaders
|
|
789
|
+
};
|
|
1006
790
|
}
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
791
|
+
/**
|
|
792
|
+
* Build URL with query parameters (Rails-compatible)
|
|
793
|
+
*/
|
|
794
|
+
function buildUrl(endpoint, params) {
|
|
795
|
+
const normalizedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
796
|
+
const normalizedEndpoint = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
|
|
797
|
+
const url = normalizedBase ? new URL(normalizedBase + normalizedEndpoint) : new URL(normalizedEndpoint, typeof window !== "undefined" ? window.location.origin : "http://localhost");
|
|
798
|
+
if (params) for (const [key, value] of Object.entries(params)) {
|
|
799
|
+
if (value === void 0 || value === null) continue;
|
|
800
|
+
if (Array.isArray(value)) for (const item of value) url.searchParams.append(`${key}[]`, String(item));
|
|
801
|
+
else if (typeof value === "object") for (const [subKey, subValue] of Object.entries(value)) {
|
|
802
|
+
if (subValue === void 0 || subValue === null) continue;
|
|
803
|
+
if (Array.isArray(subValue)) for (const item of subValue) url.searchParams.append(`${key}[${subKey}][]`, String(item));
|
|
804
|
+
else url.searchParams.append(`${key}[${subKey}]`, String(subValue));
|
|
805
|
+
}
|
|
806
|
+
else url.searchParams.append(key, String(value));
|
|
807
|
+
}
|
|
808
|
+
return url.toString();
|
|
1023
809
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
*
|
|
1031
|
-
*/
|
|
1032
|
-
function
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
"--card-foreground: var(--color-muted-foreground);",
|
|
1072
|
-
"--radius-sm: var(--radius-small);",
|
|
1073
|
-
"--radius-md: var(--radius-medium);",
|
|
1074
|
-
"--radius-lg: var(--radius-large);",
|
|
1075
|
-
"--radius-xl: var(--radius-extra-large);",
|
|
1076
|
-
"--text-xs: var(--font-size-extra-small);",
|
|
1077
|
-
"--text-sm: var(--font-size-small);",
|
|
1078
|
-
"--text-base: var(--font-size-regular);",
|
|
1079
|
-
"--text-lg: var(--font-size-large);",
|
|
1080
|
-
"--text-xl: var(--font-size-extra-large);",
|
|
1081
|
-
"--text-2xl: var(--font-size-giant);"
|
|
1082
|
-
];
|
|
1083
|
-
/**
|
|
1084
|
-
* Overrides for global tailwindcss for specifically dark mode.
|
|
1085
|
-
*/
|
|
1086
|
-
const globalDarkCSSOverride = ["--border: var(--color-background-400);"];
|
|
1087
|
-
/**
|
|
1088
|
-
* Generate a complete CSS string for a resolved theme.
|
|
1089
|
-
* Outputs 2–3 blocks: light default, dark explicit via `[data-theme-mode="dark"]`,
|
|
1090
|
-
* and (unless `disableAutoTheme`) a `prefers-color-scheme: dark` media query block.
|
|
1091
|
-
*/
|
|
1092
|
-
function generateThemeCSS(theme, options = {}) {
|
|
1093
|
-
const sel = `[data-theme="${theme.id}"]`;
|
|
1094
|
-
const tw = options.mapTailwindColors ?? true;
|
|
1095
|
-
const blocks = [];
|
|
1096
|
-
blocks.push(`${sel} {`);
|
|
1097
|
-
blocks.push(...globalCSSOverride);
|
|
1098
|
-
blocks.push(...emitNonColorVars(theme));
|
|
1099
|
-
blocks.push(...emitColorVars(theme.light));
|
|
1100
|
-
if (tw) blocks.push(...emitTailwindOverrides());
|
|
1101
|
-
blocks.push(`}`);
|
|
1102
|
-
blocks.push(`${sel}[data-theme-mode="dark"] {`);
|
|
1103
|
-
blocks.push(...globalDarkCSSOverride);
|
|
1104
|
-
blocks.push(...emitColorVars(theme.dark));
|
|
1105
|
-
if (tw) blocks.push(...emitTailwindOverrides(true));
|
|
1106
|
-
blocks.push(`}`);
|
|
1107
|
-
if (!options.disableAutoTheme) {
|
|
1108
|
-
blocks.push(`@media (prefers-color-scheme: dark) {`);
|
|
1109
|
-
blocks.push(`${sel}:not([data-theme-mode]) {`);
|
|
1110
|
-
blocks.push(...globalDarkCSSOverride);
|
|
1111
|
-
blocks.push(...emitColorVars(theme.dark).map((l) => `${l}`));
|
|
1112
|
-
if (tw) blocks.push(...emitTailwindOverrides(true).map((l) => `${l}`));
|
|
1113
|
-
blocks.push(`}`);
|
|
1114
|
-
blocks.push(`}`);
|
|
810
|
+
/**
|
|
811
|
+
* Default request options for type-safe defaults.
|
|
812
|
+
* Uses `satisfies` to validate against RequestOptions while preserving literal types.
|
|
813
|
+
*/
|
|
814
|
+
const defaultRequestOptions = { method: HTTP_METHODS.GET };
|
|
815
|
+
/**
|
|
816
|
+
* Main request function
|
|
817
|
+
*/
|
|
818
|
+
async function request(endpoint, options = {}) {
|
|
819
|
+
const { method = defaultRequestOptions.method, headers: customHeaders, params, body, signal } = options;
|
|
820
|
+
const url = buildUrl(endpoint, method === HTTP_METHODS.GET ? params : void 0);
|
|
821
|
+
const headers = buildHeaders(customHeaders);
|
|
822
|
+
let response;
|
|
823
|
+
try {
|
|
824
|
+
const fetchOptions = {
|
|
825
|
+
method,
|
|
826
|
+
headers,
|
|
827
|
+
credentials: "include"
|
|
828
|
+
};
|
|
829
|
+
if (signal !== void 0) fetchOptions.signal = signal;
|
|
830
|
+
if (body && method !== HTTP_METHODS.GET) fetchOptions.body = JSON.stringify(body);
|
|
831
|
+
response = await fetch(url, fetchOptions);
|
|
832
|
+
} catch (networkError) {
|
|
833
|
+
throw new ApiError(`Network error: ${networkError instanceof Error ? networkError.message : "Unknown network error"}`, 0, null);
|
|
834
|
+
}
|
|
835
|
+
if (response.status === 401) {
|
|
836
|
+
onAuthError?.();
|
|
837
|
+
throw new ApiError("Authentication required", 401, null);
|
|
838
|
+
}
|
|
839
|
+
if (!response.ok) try {
|
|
840
|
+
if (response.headers.get("content-type")?.includes("application/json")) {
|
|
841
|
+
const data = await response.json();
|
|
842
|
+
throw new ApiError(extractErrorMessage(data, `${method} request failed`), response.status, "errors" in data ? data.errors : data);
|
|
843
|
+
} else throw new ApiError(`${method} request failed with status ${response.status}`, response.status, null);
|
|
844
|
+
} catch (error) {
|
|
845
|
+
if (isApiError(error)) throw error;
|
|
846
|
+
throw new ApiError(`${method} request failed with status ${response.status}`, response.status, null);
|
|
847
|
+
}
|
|
848
|
+
if (response.status === 204 || response.headers.get("content-length") === "0") return null;
|
|
849
|
+
try {
|
|
850
|
+
const raw = await response.json();
|
|
851
|
+
if (raw === null || raw === void 0) throw new ApiError("Unexpected null/undefined in JSON response", response.status, null);
|
|
852
|
+
return isApiEnvelope(raw) ? raw.data : raw;
|
|
853
|
+
} catch (parseError) {
|
|
854
|
+
if (isApiError(parseError)) throw parseError;
|
|
855
|
+
throw new ApiError("Failed to parse response as JSON", response.status, null);
|
|
856
|
+
}
|
|
1115
857
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
small: "0.875rem",
|
|
1123
|
-
regular: "1rem",
|
|
1124
|
-
large: "1.125rem",
|
|
1125
|
-
extraLarge: "1.25rem",
|
|
1126
|
-
giant: "1.5rem"
|
|
1127
|
-
};
|
|
1128
|
-
const DEFAULT_FONT_FAMILIES = {
|
|
1129
|
-
header: "Inter",
|
|
1130
|
-
body: "Inter"
|
|
1131
|
-
};
|
|
1132
|
-
const DEFAULT_SPACING = "0.25rem";
|
|
1133
|
-
const DEFAULT_RADII = {
|
|
1134
|
-
small: "0.25rem",
|
|
1135
|
-
medium: "0.5rem",
|
|
1136
|
-
large: "0.75rem",
|
|
1137
|
-
extraLarge: "1rem"
|
|
1138
|
-
};
|
|
1139
|
-
const DEFAULT_COLORS = {
|
|
1140
|
-
background: "#ffffff",
|
|
1141
|
-
foreground: "#1a1a1a",
|
|
1142
|
-
primary: "#3b82f6",
|
|
1143
|
-
secondary: "#6b7280",
|
|
1144
|
-
accent: "#10b981",
|
|
1145
|
-
muted: "#f3f4f6",
|
|
1146
|
-
destructive: "#ef4444",
|
|
1147
|
-
mutedForeground: "#6b7280"
|
|
1148
|
-
};
|
|
1149
|
-
const DEFAULT_THEME_ID = "default";
|
|
1150
|
-
const DEFAULT_THEME_NAME = "Default Theme";
|
|
1151
|
-
/**
|
|
1152
|
-
* Build a fresh ThemeDefinition populated with all defaults.
|
|
1153
|
-
* Returns a new object each call because Color instances are mutable — do not cache the result.
|
|
1154
|
-
*/
|
|
1155
|
-
function getDefaultThemeDefinition() {
|
|
1156
|
-
const bg = new Color(DEFAULT_COLORS.background);
|
|
1157
|
-
const fg = new Color(DEFAULT_COLORS.foreground);
|
|
1158
|
-
const primary = new Color(DEFAULT_COLORS.primary);
|
|
1159
|
-
const secondary = new Color(DEFAULT_COLORS.secondary);
|
|
1160
|
-
const accent = new Color(DEFAULT_COLORS.accent);
|
|
1161
|
-
const muted = new Color(DEFAULT_COLORS.muted);
|
|
1162
|
-
const destructive = new Color(DEFAULT_COLORS.destructive);
|
|
1163
|
-
const mutedFg = new Color(DEFAULT_COLORS.mutedForeground);
|
|
1164
|
-
const darkBg = new Color("#0a0a0a");
|
|
1165
|
-
const darkFg = new Color("#fafafa");
|
|
1166
|
-
const darkMuted = new Color("#171717");
|
|
1167
|
-
const darkMutedForeground = new Color("#dddddd");
|
|
1168
|
-
return {
|
|
1169
|
-
id: DEFAULT_THEME_ID,
|
|
1170
|
-
name: DEFAULT_THEME_NAME,
|
|
1171
|
-
light: {
|
|
1172
|
-
background: {
|
|
1173
|
-
base: bg,
|
|
1174
|
-
foreground: fg
|
|
1175
|
-
},
|
|
1176
|
-
foreground: {
|
|
1177
|
-
base: fg,
|
|
1178
|
-
foreground: bg
|
|
1179
|
-
},
|
|
1180
|
-
primary: {
|
|
1181
|
-
base: primary,
|
|
1182
|
-
foreground: getForegroundColor(fg, primary)
|
|
1183
|
-
},
|
|
1184
|
-
secondary: {
|
|
1185
|
-
base: secondary,
|
|
1186
|
-
foreground: getForegroundColor(fg, secondary)
|
|
1187
|
-
},
|
|
1188
|
-
accent: {
|
|
1189
|
-
base: accent,
|
|
1190
|
-
foreground: getForegroundColor(fg, accent)
|
|
1191
|
-
},
|
|
1192
|
-
muted: {
|
|
1193
|
-
base: muted,
|
|
1194
|
-
foreground: mutedFg
|
|
1195
|
-
},
|
|
1196
|
-
destructive: {
|
|
1197
|
-
base: destructive,
|
|
1198
|
-
foreground: getForegroundColor(fg, destructive)
|
|
1199
|
-
}
|
|
1200
|
-
},
|
|
1201
|
-
dark: {
|
|
1202
|
-
background: {
|
|
1203
|
-
base: darkBg,
|
|
1204
|
-
foreground: darkFg
|
|
1205
|
-
},
|
|
1206
|
-
foreground: {
|
|
1207
|
-
base: darkFg,
|
|
1208
|
-
foreground: darkBg
|
|
1209
|
-
},
|
|
1210
|
-
muted: {
|
|
1211
|
-
base: darkMuted,
|
|
1212
|
-
foreground: darkMutedForeground
|
|
1213
|
-
}
|
|
1214
|
-
},
|
|
1215
|
-
fontSizes: { ...DEFAULT_FONT_SIZES },
|
|
1216
|
-
fontFamilies: { ...DEFAULT_FONT_FAMILIES },
|
|
1217
|
-
spacing: DEFAULT_SPACING,
|
|
1218
|
-
radii: { ...DEFAULT_RADII }
|
|
1219
|
-
};
|
|
1220
|
-
}
|
|
1221
|
-
//#endregion
|
|
1222
|
-
//#region ../core/src/theme/serialisation.ts
|
|
1223
|
-
function colorToPlain(color) {
|
|
1224
|
-
return {
|
|
1225
|
-
l: color.oklch.l ?? 0,
|
|
1226
|
-
c: color.oklch.c ?? 0,
|
|
1227
|
-
h: color.oklch.h ?? 0
|
|
1228
|
-
};
|
|
1229
|
-
}
|
|
1230
|
-
function plainToColor(plain) {
|
|
1231
|
-
return new Color("oklch", [
|
|
1232
|
-
plain.l,
|
|
1233
|
-
plain.c,
|
|
1234
|
-
plain.h
|
|
1235
|
-
]);
|
|
1236
|
-
}
|
|
1237
|
-
/**
|
|
1238
|
-
* Serialise a ThemeDefinition (with Color objects) to a plain JSON payload
|
|
1239
|
-
* suitable for backend storage.
|
|
1240
|
-
*/
|
|
1241
|
-
function serialiseTheme(def) {
|
|
1242
|
-
const light = {};
|
|
1243
|
-
for (const name of SEMANTIC_COLOR_NAMES) light[name] = {
|
|
1244
|
-
base: colorToPlain(def.light[name].base),
|
|
1245
|
-
foreground: colorToPlain(def.light[name].foreground)
|
|
1246
|
-
};
|
|
1247
|
-
const dark = {};
|
|
1248
|
-
for (const [name, value] of Object.entries(def.dark)) {
|
|
1249
|
-
if (!value) continue;
|
|
1250
|
-
dark[name] = {
|
|
1251
|
-
...value.base ? { base: colorToPlain(value.base) } : {},
|
|
1252
|
-
...value.foreground ? { foreground: colorToPlain(value.foreground) } : {}
|
|
1253
|
-
};
|
|
858
|
+
/**
|
|
859
|
+
* Request function for endpoints that may return null (204 No Content).
|
|
860
|
+
* Properly types the return as T | null.
|
|
861
|
+
*/
|
|
862
|
+
async function requestNullable(endpoint, options = {}) {
|
|
863
|
+
return request(endpoint, options);
|
|
1254
864
|
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
const darkRaw = payload.dark ?? {};
|
|
1275
|
-
const defaults = getDefaultThemeDefinition();
|
|
1276
|
-
const light = {};
|
|
1277
|
-
for (const name of SEMANTIC_COLOR_NAMES) {
|
|
1278
|
-
const entry = lightRaw[name];
|
|
1279
|
-
if (entry) light[name] = {
|
|
1280
|
-
base: plainToColor(entry.base),
|
|
1281
|
-
foreground: plainToColor(entry.foreground)
|
|
1282
|
-
};
|
|
1283
|
-
else {
|
|
1284
|
-
console.warn(`[theme] deserialiseTheme: missing light color "${name}", using default`);
|
|
1285
|
-
light[name] = defaults.light[name];
|
|
865
|
+
/**
|
|
866
|
+
* Safe request wrapper that returns a discriminated union instead of throwing.
|
|
867
|
+
* Use `isApiSuccess` or `isApiFailure` to narrow the result.
|
|
868
|
+
*/
|
|
869
|
+
async function safeRequest(endpoint, options = {}) {
|
|
870
|
+
try {
|
|
871
|
+
return {
|
|
872
|
+
success: true,
|
|
873
|
+
data: await request(endpoint, options)
|
|
874
|
+
};
|
|
875
|
+
} catch (error) {
|
|
876
|
+
if (isApiError(error)) return {
|
|
877
|
+
success: false,
|
|
878
|
+
error
|
|
879
|
+
};
|
|
880
|
+
return {
|
|
881
|
+
success: false,
|
|
882
|
+
error: new ApiError(error instanceof Error ? error.message : "Unknown error", 0, null)
|
|
883
|
+
};
|
|
1286
884
|
}
|
|
1287
885
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
886
|
+
/**
|
|
887
|
+
* Helper to safely convert typed params to Record<string, unknown>.
|
|
888
|
+
* Type assertion required: TypeScript's structural typing allows any object
|
|
889
|
+
* to be treated as Record<string, unknown> when we only need to iterate
|
|
890
|
+
* over its entries. This is safe because buildUrl only reads properties.
|
|
891
|
+
*/
|
|
892
|
+
function toParams(params) {
|
|
893
|
+
return params;
|
|
1295
894
|
}
|
|
895
|
+
const get = (endpoint, params, options) => {
|
|
896
|
+
const baseOptions = {
|
|
897
|
+
...options,
|
|
898
|
+
method: HTTP_METHODS.GET
|
|
899
|
+
};
|
|
900
|
+
const convertedParams = toParams(params);
|
|
901
|
+
return request(endpoint, convertedParams !== void 0 ? {
|
|
902
|
+
...baseOptions,
|
|
903
|
+
params: convertedParams
|
|
904
|
+
} : baseOptions);
|
|
905
|
+
};
|
|
906
|
+
const post = (endpoint, body, options) => request(endpoint, {
|
|
907
|
+
...options,
|
|
908
|
+
method: HTTP_METHODS.POST,
|
|
909
|
+
body
|
|
910
|
+
});
|
|
911
|
+
const put = (endpoint, body, options) => request(endpoint, {
|
|
912
|
+
...options,
|
|
913
|
+
method: HTTP_METHODS.PUT,
|
|
914
|
+
body
|
|
915
|
+
});
|
|
916
|
+
const patch = (endpoint, body, options) => request(endpoint, {
|
|
917
|
+
...options,
|
|
918
|
+
method: HTTP_METHODS.PATCH,
|
|
919
|
+
body
|
|
920
|
+
});
|
|
921
|
+
const del = (endpoint, options) => request(endpoint, {
|
|
922
|
+
...options,
|
|
923
|
+
method: HTTP_METHODS.DELETE
|
|
924
|
+
});
|
|
1296
925
|
return {
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
926
|
+
fetchClient,
|
|
927
|
+
request,
|
|
928
|
+
requestNullable,
|
|
929
|
+
safeRequest,
|
|
930
|
+
get,
|
|
931
|
+
post,
|
|
932
|
+
put,
|
|
933
|
+
patch,
|
|
934
|
+
delete: del
|
|
1306
935
|
};
|
|
1307
936
|
}
|
|
1308
937
|
//#endregion
|
|
1309
|
-
//#region
|
|
938
|
+
//#region ../../api-clients/fluidos/src/namespaces/fluid_os.ts
|
|
1310
939
|
/**
|
|
1311
|
-
*
|
|
1312
|
-
*
|
|
940
|
+
* Get active Fluid OS definition
|
|
941
|
+
* Retrieve the active Fluid OS definition manifest for a specific platform
|
|
942
|
+
*
|
|
943
|
+
* @param client - Fetch client instance
|
|
944
|
+
* @param params - params
|
|
1313
945
|
*/
|
|
1314
|
-
function
|
|
1315
|
-
return
|
|
946
|
+
async function getFluidOSManifest(client, params) {
|
|
947
|
+
return client.get(`/api/fluid_os/definitions/active`, params);
|
|
1316
948
|
}
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
const accent = parseColor(config.accent ?? DEFAULT_COLORS.accent);
|
|
1327
|
-
const muted = parseColor(config.muted ?? DEFAULT_COLORS.muted);
|
|
1328
|
-
const destructive = parseColor(config.destructive ?? DEFAULT_COLORS.destructive);
|
|
1329
|
-
const mutedFg = parseColor(config.mutedForeground ?? DEFAULT_COLORS.mutedForeground);
|
|
949
|
+
//#endregion
|
|
950
|
+
//#region src/adapters/fluidos-api-adapter.ts
|
|
951
|
+
/** Create a FluidOsReadApi adapter backed by a FetchClient. */
|
|
952
|
+
function createFluidOsReadAdapter(client) {
|
|
953
|
+
return { getManifest: (params) => getFluidOSManifest(client, params) };
|
|
954
|
+
}
|
|
955
|
+
//#endregion
|
|
956
|
+
//#region src/adapters/app-definition-api-adapter.ts
|
|
957
|
+
function mapDefinition(raw) {
|
|
1330
958
|
return {
|
|
1331
|
-
id:
|
|
1332
|
-
name,
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
foreground: fg
|
|
1337
|
-
},
|
|
1338
|
-
foreground: {
|
|
1339
|
-
base: fg,
|
|
1340
|
-
foreground: bg
|
|
1341
|
-
},
|
|
1342
|
-
primary: {
|
|
1343
|
-
base: primary,
|
|
1344
|
-
foreground: getForegroundColor(fg, primary)
|
|
1345
|
-
},
|
|
1346
|
-
secondary: {
|
|
1347
|
-
base: secondary,
|
|
1348
|
-
foreground: getForegroundColor(fg, secondary)
|
|
1349
|
-
},
|
|
1350
|
-
accent: {
|
|
1351
|
-
base: accent,
|
|
1352
|
-
foreground: getForegroundColor(fg, accent)
|
|
1353
|
-
},
|
|
1354
|
-
muted: {
|
|
1355
|
-
base: muted,
|
|
1356
|
-
foreground: mutedFg
|
|
1357
|
-
},
|
|
1358
|
-
destructive: {
|
|
1359
|
-
base: destructive,
|
|
1360
|
-
foreground: getForegroundColor(fg, destructive)
|
|
1361
|
-
}
|
|
1362
|
-
},
|
|
1363
|
-
dark: {},
|
|
1364
|
-
fontSizes: {
|
|
1365
|
-
extraSmall: config.extraSmall ?? DEFAULT_FONT_SIZES.extraSmall,
|
|
1366
|
-
small: config.small ?? DEFAULT_FONT_SIZES.small,
|
|
1367
|
-
regular: config.regular ?? DEFAULT_FONT_SIZES.regular,
|
|
1368
|
-
large: config.large ?? DEFAULT_FONT_SIZES.large,
|
|
1369
|
-
extraLarge: config.extraLarge ?? DEFAULT_FONT_SIZES.extraLarge,
|
|
1370
|
-
giant: config.giant ?? DEFAULT_FONT_SIZES.giant
|
|
1371
|
-
},
|
|
1372
|
-
fontFamilies: {
|
|
1373
|
-
header: config.headerFont ?? DEFAULT_FONT_FAMILIES.header,
|
|
1374
|
-
body: config.bodyFont ?? DEFAULT_FONT_FAMILIES.body
|
|
1375
|
-
},
|
|
1376
|
-
spacing: config.globalSpacing ?? "0.25rem",
|
|
1377
|
-
radii: {
|
|
1378
|
-
small: config.radiusSmall ?? DEFAULT_RADII.small,
|
|
1379
|
-
medium: config.radiusMedium ?? DEFAULT_RADII.medium,
|
|
1380
|
-
large: config.radiusLarge ?? DEFAULT_RADII.large,
|
|
1381
|
-
extraLarge: config.radiusExtraLarge ?? DEFAULT_RADII.extraLarge
|
|
1382
|
-
}
|
|
959
|
+
id: raw.id ?? 0,
|
|
960
|
+
name: raw.name ?? "",
|
|
961
|
+
version: raw.version ?? null,
|
|
962
|
+
components: raw.components ?? [],
|
|
963
|
+
active: raw.active ?? false
|
|
1383
964
|
};
|
|
1384
965
|
}
|
|
1385
966
|
/**
|
|
1386
|
-
*
|
|
1387
|
-
*
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
if (isNewThemeFormat(config)) return deserialiseTheme({
|
|
1392
|
-
...config,
|
|
1393
|
-
id: String(theme.id),
|
|
1394
|
-
name: theme.name ?? "Untitled Theme"
|
|
1395
|
-
});
|
|
1396
|
-
return legacyConfigToDefinition(theme.id, theme.name ?? "Untitled Theme", config);
|
|
1397
|
-
}
|
|
1398
|
-
/**
|
|
1399
|
-
* Transform raw API themes to ThemeDefinition[].
|
|
1400
|
-
* Catches and logs errors per theme (graceful degradation).
|
|
1401
|
-
*/
|
|
1402
|
-
function transformThemes(themes) {
|
|
1403
|
-
return themes.flatMap((theme) => {
|
|
1404
|
-
try {
|
|
1405
|
-
return [buildThemeDefinition(theme)];
|
|
1406
|
-
} catch (error) {
|
|
1407
|
-
console.error(`[theme] Failed to build theme id=${theme.id}:`, error);
|
|
1408
|
-
return [];
|
|
1409
|
-
}
|
|
1410
|
-
});
|
|
1411
|
-
}
|
|
1412
|
-
/**
|
|
1413
|
-
* Get the active theme ID from a list of raw API themes.
|
|
1414
|
-
* Falls back to the first theme if none is marked active.
|
|
967
|
+
* Creates an AppDefinitionApi adapter backed by the portal-tenant BFF client.
|
|
968
|
+
*
|
|
969
|
+
* Maps the generated portal-tenant `app_definition_show` response to the
|
|
970
|
+
* `AppDefinitionApi` port, applying runtime defaults for optional BFF fields
|
|
971
|
+
* so TypeScript catches schema drift at compile time via `satisfies`.
|
|
1415
972
|
*/
|
|
1416
|
-
function
|
|
1417
|
-
|
|
1418
|
-
|
|
973
|
+
function createAppDefinitionApiAdapter(client) {
|
|
974
|
+
return { fetchDefinition: async () => {
|
|
975
|
+
const response = await app_definition_show(client);
|
|
976
|
+
return {
|
|
977
|
+
definition: mapDefinition(response.definition ?? {}),
|
|
978
|
+
meta: {
|
|
979
|
+
request_id: response.meta?.request_id ?? "",
|
|
980
|
+
timestamp: response.meta?.timestamp ?? ""
|
|
981
|
+
}
|
|
982
|
+
};
|
|
983
|
+
} };
|
|
1419
984
|
}
|
|
1420
985
|
//#endregion
|
|
1421
|
-
//#region
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
/** Build a Google Fonts CSS2 URL for a given font family with all weights. */
|
|
1436
|
-
function buildGoogleFontUrl(family) {
|
|
1437
|
-
return `https://fonts.googleapis.com/css2?family=${encodeURIComponent(family).replace(/%20/g, "+")}:wght@100;200;300;400;500;600;700;800;900&display=swap`;
|
|
1438
|
-
}
|
|
1439
|
-
/** Check if a font family value needs to be loaded (i.e. is not a CSS var or system font). */
|
|
1440
|
-
function isLoadableFont(value) {
|
|
1441
|
-
if (!value) return false;
|
|
1442
|
-
if (value.startsWith("var(")) return false;
|
|
1443
|
-
return !SYSTEM_FONTS.has(value.toLowerCase());
|
|
1444
|
-
}
|
|
1445
|
-
/** Deterministic link element ID for a font family. */
|
|
1446
|
-
function fontLinkId(family) {
|
|
1447
|
-
return `${FONT_LINK_PREFIX}${family.replace(/\s+/g, "-").toLowerCase()}`;
|
|
986
|
+
//#region src/adapters/account-api-adapter.ts
|
|
987
|
+
function mapAccount(raw) {
|
|
988
|
+
return {
|
|
989
|
+
id: raw.id ?? 0,
|
|
990
|
+
member_type: raw.member_type ?? "rep",
|
|
991
|
+
first_name: raw.first_name ?? "",
|
|
992
|
+
last_name: raw.last_name ?? "",
|
|
993
|
+
email: raw.email ?? "",
|
|
994
|
+
phone: raw.phone ?? null,
|
|
995
|
+
bio: raw.bio ?? null,
|
|
996
|
+
avatar_url: raw.avatar_url ?? null,
|
|
997
|
+
slug: raw.slug ?? "",
|
|
998
|
+
social_links: raw.social_links ?? null
|
|
999
|
+
};
|
|
1448
1000
|
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
for (const family of fontsToLoad) {
|
|
1471
|
-
const id = fontLinkId(family);
|
|
1472
|
-
const existing = document.getElementById(id);
|
|
1473
|
-
if (existing) {
|
|
1474
|
-
const owners = existing.getAttribute("data-font-theme-ids")?.split(",") ?? [];
|
|
1475
|
-
if (!owners.includes(theme.id)) existing.setAttribute("data-font-theme-ids", [...owners, theme.id].join(","));
|
|
1476
|
-
} else {
|
|
1477
|
-
const link = document.createElement("link");
|
|
1478
|
-
link.id = id;
|
|
1479
|
-
link.rel = "stylesheet";
|
|
1480
|
-
link.href = buildGoogleFontUrl(family);
|
|
1481
|
-
link.setAttribute("data-font-family", family);
|
|
1482
|
-
link.setAttribute("data-font-theme-ids", theme.id);
|
|
1483
|
-
document.head.appendChild(link);
|
|
1001
|
+
function createAccountApiAdapter(client) {
|
|
1002
|
+
return {
|
|
1003
|
+
fetchAccount: async () => {
|
|
1004
|
+
const response = await account_show(client);
|
|
1005
|
+
return {
|
|
1006
|
+
account: mapAccount(response.account ?? {}),
|
|
1007
|
+
meta: {
|
|
1008
|
+
request_id: response.meta?.request_id ?? "",
|
|
1009
|
+
timestamp: response.meta?.timestamp ?? ""
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
},
|
|
1013
|
+
updateAccount: async (body) => {
|
|
1014
|
+
const response = await account_update(client, body);
|
|
1015
|
+
return {
|
|
1016
|
+
account: mapAccount(response.account ?? {}),
|
|
1017
|
+
meta: {
|
|
1018
|
+
request_id: response.meta?.request_id ?? "",
|
|
1019
|
+
timestamp: response.meta?.timestamp ?? ""
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
1484
1022
|
}
|
|
1485
|
-
}
|
|
1023
|
+
};
|
|
1486
1024
|
}
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1025
|
+
//#endregion
|
|
1026
|
+
//#region ../../api-clients/portal-tenant-pay/src/namespaces/portal_tenant_pay.ts
|
|
1027
|
+
/**
|
|
1028
|
+
* List addresses
|
|
1029
|
+
* Returns addresses associated with the member's customer record in this tenant.
|
|
1030
|
+
*
|
|
1031
|
+
* @param client - Fetch client instance
|
|
1032
|
+
* @param [params] - params
|
|
1033
|
+
*/
|
|
1034
|
+
async function addresses_list(client, params) {
|
|
1035
|
+
return client.get(`/api/pay/addresses`, params);
|
|
1491
1036
|
}
|
|
1492
1037
|
/**
|
|
1493
|
-
*
|
|
1494
|
-
*
|
|
1495
|
-
*
|
|
1496
|
-
*
|
|
1497
|
-
*
|
|
1038
|
+
* Create an address
|
|
1039
|
+
* Adds a new address to the member's customer record. If an identical address already exists it is returned instead of creating a duplicate.
|
|
1040
|
+
*
|
|
1041
|
+
* @param client - Fetch client instance
|
|
1042
|
+
* @param body - body
|
|
1498
1043
|
*/
|
|
1499
|
-
function
|
|
1500
|
-
|
|
1501
|
-
try {
|
|
1502
|
-
loadThemeFonts(theme);
|
|
1503
|
-
const styleId = `${STYLE_PREFIX}${theme.id}`;
|
|
1504
|
-
let el = document.getElementById(styleId);
|
|
1505
|
-
if (!el) {
|
|
1506
|
-
el = document.createElement("style");
|
|
1507
|
-
el.id = styleId;
|
|
1508
|
-
document.head.appendChild(el);
|
|
1509
|
-
}
|
|
1510
|
-
el.textContent = generateThemeCSS(theme, options);
|
|
1511
|
-
} catch (error) {
|
|
1512
|
-
console.error(`[theme] applyTheme failed for "${theme.id}":`, error);
|
|
1513
|
-
}
|
|
1044
|
+
async function addresses_create(client, body) {
|
|
1045
|
+
return client.post(`/api/pay/addresses`, body);
|
|
1514
1046
|
}
|
|
1515
|
-
/**
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1047
|
+
/**
|
|
1048
|
+
* Update an address
|
|
1049
|
+
* Creates a new address with the merged attributes and discards the old one, preserving references from existing orders.
|
|
1050
|
+
*
|
|
1051
|
+
* @param client - Fetch client instance
|
|
1052
|
+
* @param id - id
|
|
1053
|
+
* @param body - body
|
|
1054
|
+
*/
|
|
1055
|
+
async function addresses_update(client, id, body) {
|
|
1056
|
+
return client.patch(`/api/pay/addresses/${id}`, body);
|
|
1524
1057
|
}
|
|
1525
|
-
/**
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1058
|
+
/**
|
|
1059
|
+
* Delete an address
|
|
1060
|
+
* Removes an address from the member's customer record. The default address cannot be removed.
|
|
1061
|
+
*
|
|
1062
|
+
* @param client - Fetch client instance
|
|
1063
|
+
* @param id - id
|
|
1064
|
+
*/
|
|
1065
|
+
async function addresses_destroy(client, id) {
|
|
1066
|
+
return client.delete(`/api/pay/addresses/${id}`);
|
|
1530
1067
|
}
|
|
1531
|
-
//#endregion
|
|
1532
|
-
//#region src/transforms/screen-transforms.ts
|
|
1533
1068
|
/**
|
|
1534
|
-
*
|
|
1535
|
-
*
|
|
1069
|
+
* List payment methods
|
|
1070
|
+
* Returns displayable payment methods on the member's customer record, excluding Apple Pay sources.
|
|
1071
|
+
*
|
|
1072
|
+
* @param client - Fetch client instance
|
|
1073
|
+
* @param [params] - params
|
|
1536
1074
|
*/
|
|
1537
|
-
function
|
|
1538
|
-
|
|
1539
|
-
if (Array.isArray(componentTree)) return componentTree;
|
|
1540
|
-
if (typeof componentTree === "object") return [componentTree];
|
|
1541
|
-
return [];
|
|
1075
|
+
async function payment_methods_list(client, params) {
|
|
1076
|
+
return client.get(`/api/pay/payment_methods`, params);
|
|
1542
1077
|
}
|
|
1543
1078
|
/**
|
|
1544
|
-
*
|
|
1545
|
-
*
|
|
1079
|
+
* Create a payment method
|
|
1080
|
+
* Tokenizes and stores a new payment method via the vault provider. Requires a vault token obtained from the vault credentials endpoint.
|
|
1081
|
+
*
|
|
1082
|
+
* @param client - Fetch client instance
|
|
1083
|
+
* @param body - body
|
|
1546
1084
|
*/
|
|
1547
|
-
function
|
|
1548
|
-
return
|
|
1549
|
-
id: Number(screen.id),
|
|
1550
|
-
slug: screen.slug ?? "",
|
|
1551
|
-
name: screen.name ?? "",
|
|
1552
|
-
component_tree: normalizeComponentTree(screen.component_tree)
|
|
1553
|
-
};
|
|
1085
|
+
async function payment_methods_create(client, body) {
|
|
1086
|
+
return client.post(`/api/pay/payment_methods`, body);
|
|
1554
1087
|
}
|
|
1555
|
-
//#endregion
|
|
1556
|
-
//#region src/transforms/navigation-transforms.ts
|
|
1557
1088
|
/**
|
|
1558
|
-
*
|
|
1559
|
-
*
|
|
1089
|
+
* Update a payment method
|
|
1090
|
+
* Updates a payment method's attributes. Currently supports setting a payment method as the default.
|
|
1091
|
+
*
|
|
1092
|
+
* @param client - Fetch client instance
|
|
1093
|
+
* @param id - id
|
|
1094
|
+
* @param body - body
|
|
1560
1095
|
*/
|
|
1561
|
-
function
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
};
|
|
1096
|
+
async function payment_methods_update(client, id, body) {
|
|
1097
|
+
return client.patch(`/api/pay/payment_methods/${id}`, body);
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Delete a payment method
|
|
1101
|
+
* Removes a payment method from the member's customer record. If the removed method was the default, the default is cleared.
|
|
1102
|
+
*
|
|
1103
|
+
* @param client - Fetch client instance
|
|
1104
|
+
* @param id - id
|
|
1105
|
+
*/
|
|
1106
|
+
async function payment_methods_destroy(client, id) {
|
|
1107
|
+
return client.delete(`/api/pay/payment_methods/${id}`);
|
|
1574
1108
|
}
|
|
1575
|
-
//#endregion
|
|
1576
|
-
//#region src/transforms/index.ts
|
|
1577
1109
|
/**
|
|
1578
|
-
*
|
|
1110
|
+
* Get vault credentials
|
|
1111
|
+
* Returns a short-lived vault token and environment identifier for initializing the client-side payment vault SDK.
|
|
1579
1112
|
*
|
|
1580
|
-
*
|
|
1581
|
-
|
|
1582
|
-
* This function bridges that gap so callers avoid `as unknown as` casts.
|
|
1113
|
+
* @param client - Fetch client instance
|
|
1114
|
+
|
|
1583
1115
|
*/
|
|
1584
|
-
function
|
|
1585
|
-
|
|
1586
|
-
return raw;
|
|
1116
|
+
async function payment_methods_vault_show(client) {
|
|
1117
|
+
return client.get(`/api/pay/payment_methods/vault`);
|
|
1587
1118
|
}
|
|
1588
1119
|
/**
|
|
1589
|
-
*
|
|
1120
|
+
* List points ledger entries
|
|
1121
|
+
* Returns loyalty points ledger entries for the member's customer record in this tenant, ordered by most recent first.
|
|
1590
1122
|
*
|
|
1591
|
-
*
|
|
1592
|
-
*
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
const rawThemes = Array.isArray(rawProfile?.themes) ? rawProfile.themes : [];
|
|
1601
|
-
const screens = (manifest.screens ?? []).map((screen) => toScreenDefinition(screen));
|
|
1602
|
-
const navigationItems = (rawProfile?.navigation?.navigation_items ?? []).map(toNavigationItem);
|
|
1603
|
-
const nav = rawProfile?.navigation;
|
|
1604
|
-
const mobileNav = rawProfile?.mobile_navigation;
|
|
1605
|
-
const mobileNavigationItems = (mobileNav?.navigation_items ?? []).map(toNavigationItem);
|
|
1606
|
-
const activeThemeId = getActiveThemeId(rawThemes);
|
|
1123
|
+
* @param client - Fetch client instance
|
|
1124
|
+
* @param [params] - params
|
|
1125
|
+
*/
|
|
1126
|
+
async function points_ledgers_list(client, params) {
|
|
1127
|
+
return client.get(`/api/pay/points_ledgers`, params);
|
|
1128
|
+
}
|
|
1129
|
+
//#endregion
|
|
1130
|
+
//#region src/adapters/pay-api-adapter.ts
|
|
1131
|
+
function mapAddress(raw) {
|
|
1607
1132
|
return {
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
id: nav?.id ?? 0,
|
|
1619
|
-
name: nav?.name ?? "Main Navigation",
|
|
1620
|
-
navigation_items: navigationItems,
|
|
1621
|
-
screens
|
|
1622
|
-
},
|
|
1623
|
-
...mobileNav ? { mobile_navigation: {
|
|
1624
|
-
definition_id: mobileNav.definition_id ?? manifest.definition_id,
|
|
1625
|
-
id: mobileNav.id ?? 0,
|
|
1626
|
-
name: mobileNav.name ?? "Mobile Navigation",
|
|
1627
|
-
navigation_items: mobileNavigationItems,
|
|
1628
|
-
screens
|
|
1629
|
-
} } : {}
|
|
1630
|
-
}
|
|
1133
|
+
id: raw.id ?? 0,
|
|
1134
|
+
street1: raw.street1 ?? "",
|
|
1135
|
+
street2: raw.street2 ?? null,
|
|
1136
|
+
city: raw.city ?? "",
|
|
1137
|
+
state: raw.state ?? "",
|
|
1138
|
+
zip: raw.zip ?? "",
|
|
1139
|
+
country: raw.country ?? "",
|
|
1140
|
+
default: raw.default ?? false,
|
|
1141
|
+
created_at: raw.created_at ?? null,
|
|
1142
|
+
updated_at: raw.updated_at ?? null
|
|
1631
1143
|
};
|
|
1632
1144
|
}
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
}
|
|
1145
|
+
function mapPaymentMethod(raw) {
|
|
1146
|
+
return {
|
|
1147
|
+
id: raw.id ?? 0,
|
|
1148
|
+
type: raw.type ?? "card",
|
|
1149
|
+
brand: raw.brand ?? null,
|
|
1150
|
+
last_four: raw.last_four ?? "",
|
|
1151
|
+
exp_month: raw.exp_month ?? null,
|
|
1152
|
+
exp_year: raw.exp_year ?? null,
|
|
1153
|
+
default: raw.default ?? false,
|
|
1154
|
+
created_at: raw.created_at ?? null,
|
|
1155
|
+
updated_at: raw.updated_at ?? null
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
function createPortalTenantPayAdapter(client) {
|
|
1159
|
+
return {
|
|
1160
|
+
fetchAddresses: async () => {
|
|
1161
|
+
const response = await addresses_list(client);
|
|
1162
|
+
return {
|
|
1163
|
+
addresses: (response.addresses ?? []).map(mapAddress),
|
|
1164
|
+
meta: {
|
|
1165
|
+
request_id: response.meta?.request_id ?? "",
|
|
1166
|
+
timestamp: response.meta?.timestamp ?? ""
|
|
1167
|
+
}
|
|
1168
|
+
};
|
|
1169
|
+
},
|
|
1170
|
+
createAddress: async (body) => {
|
|
1171
|
+
await addresses_create(client, body);
|
|
1172
|
+
},
|
|
1173
|
+
updateAddress: async (addressId, body) => {
|
|
1174
|
+
await addresses_update(client, addressId, body);
|
|
1175
|
+
},
|
|
1176
|
+
deleteAddress: async (addressId) => {
|
|
1177
|
+
await addresses_destroy(client, addressId);
|
|
1178
|
+
},
|
|
1179
|
+
fetchPaymentMethods: async () => {
|
|
1180
|
+
const response = await payment_methods_list(client);
|
|
1181
|
+
return {
|
|
1182
|
+
payment_methods: (response.payment_methods ?? []).map(mapPaymentMethod),
|
|
1183
|
+
meta: {
|
|
1184
|
+
request_id: response.meta?.request_id ?? "",
|
|
1185
|
+
timestamp: response.meta?.timestamp ?? ""
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
},
|
|
1189
|
+
createPaymentMethod: async (body) => {
|
|
1190
|
+
await payment_methods_create(client, body);
|
|
1191
|
+
},
|
|
1192
|
+
updatePaymentMethod: async (paymentMethodId, body) => {
|
|
1193
|
+
await payment_methods_update(client, paymentMethodId, body);
|
|
1194
|
+
},
|
|
1195
|
+
deletePaymentMethod: async (paymentMethodId) => {
|
|
1196
|
+
await payment_methods_destroy(client, paymentMethodId);
|
|
1197
|
+
},
|
|
1198
|
+
fetchVaultCredentials: async () => {
|
|
1199
|
+
const response = await payment_methods_vault_show(client);
|
|
1200
|
+
return {
|
|
1201
|
+
vault: {
|
|
1202
|
+
token: response.vault?.token ?? null,
|
|
1203
|
+
environment: response.vault?.environment ?? "production"
|
|
1204
|
+
},
|
|
1205
|
+
meta: {
|
|
1206
|
+
request_id: response.meta?.request_id ?? "",
|
|
1207
|
+
timestamp: response.meta?.timestamp ?? ""
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
},
|
|
1211
|
+
fetchPointsLedgers: async () => {
|
|
1212
|
+
const response = await points_ledgers_list(client);
|
|
1213
|
+
return {
|
|
1214
|
+
points_ledgers: (response.points_ledgers ?? []).map((entry) => ({
|
|
1215
|
+
id: entry.id ?? 0,
|
|
1216
|
+
amount: entry.amount ?? 0,
|
|
1217
|
+
total_balance: entry.total_balance ?? 0,
|
|
1218
|
+
metadata: entry.metadata ?? null,
|
|
1219
|
+
created_at: entry.created_at ?? ""
|
|
1220
|
+
})),
|
|
1221
|
+
meta: {
|
|
1222
|
+
request_id: response.meta?.request_id ?? "",
|
|
1223
|
+
timestamp: response.meta?.timestamp ?? ""
|
|
1224
|
+
}
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
//#endregion
|
|
1230
|
+
//#region ../../api-clients/portal-tenant-store/src/namespaces/portal_tenant_store.ts
|
|
1658
1231
|
/**
|
|
1659
|
-
*
|
|
1232
|
+
* List available countries
|
|
1233
|
+
* Returns countries enabled for the tenant store, each with its ISO 3166 subdivisions (states/provinces).
|
|
1234
|
+
*
|
|
1235
|
+
* @param client - Fetch client instance
|
|
1236
|
+
* @param [params] - params
|
|
1660
1237
|
*/
|
|
1661
|
-
function
|
|
1662
|
-
return
|
|
1238
|
+
async function countries_list(client, params) {
|
|
1239
|
+
return client.get(`/api/store/countries`, params);
|
|
1663
1240
|
}
|
|
1664
1241
|
/**
|
|
1665
|
-
*
|
|
1242
|
+
* List available languages
|
|
1243
|
+
* Returns languages enabled for the tenant store, sorted by name.
|
|
1244
|
+
*
|
|
1245
|
+
* @param client - Fetch client instance
|
|
1246
|
+
* @param [params] - params
|
|
1666
1247
|
*/
|
|
1667
|
-
function
|
|
1668
|
-
return
|
|
1248
|
+
async function languages_list(client, params) {
|
|
1249
|
+
return client.get(`/api/store/languages`, params);
|
|
1669
1250
|
}
|
|
1251
|
+
//#endregion
|
|
1252
|
+
//#region ../../store/api-client/src/portal-tenant-countries-adapter.ts
|
|
1670
1253
|
/**
|
|
1671
|
-
*
|
|
1672
|
-
* Checks common error message field names in order of precedence.
|
|
1254
|
+
* Maps a BFF state to the port's State shape.
|
|
1673
1255
|
*/
|
|
1674
|
-
function
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1256
|
+
function mapState(raw) {
|
|
1257
|
+
return {
|
|
1258
|
+
code: raw.code ?? "",
|
|
1259
|
+
name: raw.name ?? ""
|
|
1260
|
+
};
|
|
1679
1261
|
}
|
|
1680
1262
|
/**
|
|
1681
|
-
*
|
|
1682
|
-
* Envelopes always have numeric `status` and a `data` key.
|
|
1263
|
+
* Maps a BFF country to the port's Country shape.
|
|
1683
1264
|
*/
|
|
1684
|
-
function
|
|
1685
|
-
return
|
|
1265
|
+
function mapCountry(raw) {
|
|
1266
|
+
return {
|
|
1267
|
+
code: raw.code ?? "",
|
|
1268
|
+
name: raw.name ?? "",
|
|
1269
|
+
currency_code: raw.currency_code ?? "",
|
|
1270
|
+
states: (raw.states ?? []).map(mapState)
|
|
1271
|
+
};
|
|
1686
1272
|
}
|
|
1687
1273
|
/**
|
|
1688
|
-
*
|
|
1274
|
+
* Maps the BFF meta envelope to the port's ApiMeta shape.
|
|
1689
1275
|
*/
|
|
1690
|
-
function
|
|
1691
|
-
const { baseUrl, getAuthToken, onAuthError, defaultHeaders = {} } = config;
|
|
1692
|
-
const fetchClient = createFetchClient({
|
|
1693
|
-
baseUrl,
|
|
1694
|
-
...getAuthToken ? { getAuthToken } : {},
|
|
1695
|
-
onAuthError,
|
|
1696
|
-
defaultHeaders,
|
|
1697
|
-
credentials: "include"
|
|
1698
|
-
});
|
|
1699
|
-
/**
|
|
1700
|
-
* Build headers for a request.
|
|
1701
|
-
* Auth is handled by session cookies via `credentials: 'include'` on fetch calls.
|
|
1702
|
-
*/
|
|
1703
|
-
function buildHeaders(customHeaders) {
|
|
1704
|
-
return {
|
|
1705
|
-
"Content-Type": "application/json",
|
|
1706
|
-
...defaultHeaders,
|
|
1707
|
-
...customHeaders
|
|
1708
|
-
};
|
|
1709
|
-
}
|
|
1710
|
-
/**
|
|
1711
|
-
* Build URL with query parameters (Rails-compatible)
|
|
1712
|
-
*/
|
|
1713
|
-
function buildUrl(endpoint, params) {
|
|
1714
|
-
const normalizedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
1715
|
-
const normalizedEndpoint = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
|
|
1716
|
-
const url = normalizedBase ? new URL(normalizedBase + normalizedEndpoint) : new URL(normalizedEndpoint, typeof window !== "undefined" ? window.location.origin : "http://localhost");
|
|
1717
|
-
if (params) for (const [key, value] of Object.entries(params)) {
|
|
1718
|
-
if (value === void 0 || value === null) continue;
|
|
1719
|
-
if (Array.isArray(value)) for (const item of value) url.searchParams.append(`${key}[]`, String(item));
|
|
1720
|
-
else if (typeof value === "object") for (const [subKey, subValue] of Object.entries(value)) {
|
|
1721
|
-
if (subValue === void 0 || subValue === null) continue;
|
|
1722
|
-
if (Array.isArray(subValue)) for (const item of subValue) url.searchParams.append(`${key}[${subKey}][]`, String(item));
|
|
1723
|
-
else url.searchParams.append(`${key}[${subKey}]`, String(subValue));
|
|
1724
|
-
}
|
|
1725
|
-
else url.searchParams.append(key, String(value));
|
|
1726
|
-
}
|
|
1727
|
-
return url.toString();
|
|
1728
|
-
}
|
|
1729
|
-
/**
|
|
1730
|
-
* Default request options for type-safe defaults.
|
|
1731
|
-
* Uses `satisfies` to validate against RequestOptions while preserving literal types.
|
|
1732
|
-
*/
|
|
1733
|
-
const defaultRequestOptions = { method: HTTP_METHODS.GET };
|
|
1734
|
-
/**
|
|
1735
|
-
* Main request function
|
|
1736
|
-
*/
|
|
1737
|
-
async function request(endpoint, options = {}) {
|
|
1738
|
-
const { method = defaultRequestOptions.method, headers: customHeaders, params, body, signal } = options;
|
|
1739
|
-
const url = buildUrl(endpoint, method === HTTP_METHODS.GET ? params : void 0);
|
|
1740
|
-
const headers = buildHeaders(customHeaders);
|
|
1741
|
-
let response;
|
|
1742
|
-
try {
|
|
1743
|
-
const fetchOptions = {
|
|
1744
|
-
method,
|
|
1745
|
-
headers,
|
|
1746
|
-
credentials: "include"
|
|
1747
|
-
};
|
|
1748
|
-
if (signal !== void 0) fetchOptions.signal = signal;
|
|
1749
|
-
if (body && method !== HTTP_METHODS.GET) fetchOptions.body = JSON.stringify(body);
|
|
1750
|
-
response = await fetch(url, fetchOptions);
|
|
1751
|
-
} catch (networkError) {
|
|
1752
|
-
throw new ApiError(`Network error: ${networkError instanceof Error ? networkError.message : "Unknown network error"}`, 0, null);
|
|
1753
|
-
}
|
|
1754
|
-
if (response.status === 401) {
|
|
1755
|
-
onAuthError?.();
|
|
1756
|
-
throw new ApiError("Authentication required", 401, null);
|
|
1757
|
-
}
|
|
1758
|
-
if (!response.ok) try {
|
|
1759
|
-
if (response.headers.get("content-type")?.includes("application/json")) {
|
|
1760
|
-
const data = await response.json();
|
|
1761
|
-
throw new ApiError(extractErrorMessage(data, `${method} request failed`), response.status, "errors" in data ? data.errors : data);
|
|
1762
|
-
} else throw new ApiError(`${method} request failed with status ${response.status}`, response.status, null);
|
|
1763
|
-
} catch (error) {
|
|
1764
|
-
if (isApiError(error)) throw error;
|
|
1765
|
-
throw new ApiError(`${method} request failed with status ${response.status}`, response.status, null);
|
|
1766
|
-
}
|
|
1767
|
-
if (response.status === 204 || response.headers.get("content-length") === "0") return null;
|
|
1768
|
-
try {
|
|
1769
|
-
const raw = await response.json();
|
|
1770
|
-
if (raw === null || raw === void 0) throw new ApiError("Unexpected null/undefined in JSON response", response.status, null);
|
|
1771
|
-
return isApiEnvelope(raw) ? raw.data : raw;
|
|
1772
|
-
} catch (parseError) {
|
|
1773
|
-
if (isApiError(parseError)) throw parseError;
|
|
1774
|
-
throw new ApiError("Failed to parse response as JSON", response.status, null);
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
/**
|
|
1778
|
-
* Request function for endpoints that may return null (204 No Content).
|
|
1779
|
-
* Properly types the return as T | null.
|
|
1780
|
-
*/
|
|
1781
|
-
async function requestNullable(endpoint, options = {}) {
|
|
1782
|
-
return request(endpoint, options);
|
|
1783
|
-
}
|
|
1784
|
-
/**
|
|
1785
|
-
* Safe request wrapper that returns a discriminated union instead of throwing.
|
|
1786
|
-
* Use `isApiSuccess` or `isApiFailure` to narrow the result.
|
|
1787
|
-
*/
|
|
1788
|
-
async function safeRequest(endpoint, options = {}) {
|
|
1789
|
-
try {
|
|
1790
|
-
return {
|
|
1791
|
-
success: true,
|
|
1792
|
-
data: await request(endpoint, options)
|
|
1793
|
-
};
|
|
1794
|
-
} catch (error) {
|
|
1795
|
-
if (isApiError(error)) return {
|
|
1796
|
-
success: false,
|
|
1797
|
-
error
|
|
1798
|
-
};
|
|
1799
|
-
return {
|
|
1800
|
-
success: false,
|
|
1801
|
-
error: new ApiError(error instanceof Error ? error.message : "Unknown error", 0, null)
|
|
1802
|
-
};
|
|
1803
|
-
}
|
|
1804
|
-
}
|
|
1805
|
-
/**
|
|
1806
|
-
* Helper to safely convert typed params to Record<string, unknown>.
|
|
1807
|
-
* Type assertion required: TypeScript's structural typing allows any object
|
|
1808
|
-
* to be treated as Record<string, unknown> when we only need to iterate
|
|
1809
|
-
* over its entries. This is safe because buildUrl only reads properties.
|
|
1810
|
-
*/
|
|
1811
|
-
function toParams(params) {
|
|
1812
|
-
return params;
|
|
1813
|
-
}
|
|
1814
|
-
const get = (endpoint, params, options) => {
|
|
1815
|
-
const baseOptions = {
|
|
1816
|
-
...options,
|
|
1817
|
-
method: HTTP_METHODS.GET
|
|
1818
|
-
};
|
|
1819
|
-
const convertedParams = toParams(params);
|
|
1820
|
-
return request(endpoint, convertedParams !== void 0 ? {
|
|
1821
|
-
...baseOptions,
|
|
1822
|
-
params: convertedParams
|
|
1823
|
-
} : baseOptions);
|
|
1824
|
-
};
|
|
1825
|
-
const post = (endpoint, body, options) => request(endpoint, {
|
|
1826
|
-
...options,
|
|
1827
|
-
method: HTTP_METHODS.POST,
|
|
1828
|
-
body
|
|
1829
|
-
});
|
|
1830
|
-
const put = (endpoint, body, options) => request(endpoint, {
|
|
1831
|
-
...options,
|
|
1832
|
-
method: HTTP_METHODS.PUT,
|
|
1833
|
-
body
|
|
1834
|
-
});
|
|
1835
|
-
const patch = (endpoint, body, options) => request(endpoint, {
|
|
1836
|
-
...options,
|
|
1837
|
-
method: HTTP_METHODS.PATCH,
|
|
1838
|
-
body
|
|
1839
|
-
});
|
|
1840
|
-
const del = (endpoint, options) => request(endpoint, {
|
|
1841
|
-
...options,
|
|
1842
|
-
method: HTTP_METHODS.DELETE
|
|
1843
|
-
});
|
|
1276
|
+
function mapMeta$1(raw) {
|
|
1844
1277
|
return {
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
delete: del,
|
|
1854
|
-
reps: {
|
|
1855
|
-
current: () => get("/api/reps/me"),
|
|
1856
|
-
updateProfile: (data) => patch("/api/reps/me", data)
|
|
1857
|
-
},
|
|
1858
|
-
app: {
|
|
1859
|
-
getRaw: async () => {
|
|
1860
|
-
return toRawManifest(await getFluidOSManifest(fetchClient, { platform: "browser" }));
|
|
1861
|
-
},
|
|
1862
|
-
get: async () => {
|
|
1863
|
-
return transformManifestToRepAppData(toRawManifest(await getFluidOSManifest(fetchClient, { platform: "browser" })));
|
|
1864
|
-
}
|
|
1865
|
-
},
|
|
1866
|
-
permissions: { get: () => get("/api/company/roles/my_permissions") },
|
|
1867
|
-
analytics: {
|
|
1868
|
-
dashboard: () => get("/api/analytics/dashboard"),
|
|
1869
|
-
sales: (params) => get("/api/analytics/sales", params)
|
|
1870
|
-
}
|
|
1278
|
+
request_id: raw?.request_id ?? null,
|
|
1279
|
+
timestamp: raw?.timestamp ?? "",
|
|
1280
|
+
pagination: raw?.pagination ? {
|
|
1281
|
+
cursor: raw.pagination.cursor ?? null,
|
|
1282
|
+
limit: raw.pagination.limit,
|
|
1283
|
+
next_cursor: raw.pagination.next_cursor ?? null,
|
|
1284
|
+
prev_cursor: raw.pagination.prev_cursor ?? null
|
|
1285
|
+
} : void 0
|
|
1871
1286
|
};
|
|
1872
1287
|
}
|
|
1288
|
+
/**
|
|
1289
|
+
* Creates a CountriesApi adapter backed by the portal-tenant store BFF.
|
|
1290
|
+
*
|
|
1291
|
+
* Maps the generated portal-tenant-store namespace functions to the abstract
|
|
1292
|
+
* CountriesApi port, closing over the FetchClient so consumers don't need
|
|
1293
|
+
* to pass it per-call.
|
|
1294
|
+
*/
|
|
1295
|
+
function createPortalTenantCountriesAdapter(client) {
|
|
1296
|
+
return { listCountries: async (params) => {
|
|
1297
|
+
const response = await countries_list(client, {
|
|
1298
|
+
"page[cursor]": params?.cursor,
|
|
1299
|
+
"page[limit]": params?.limit
|
|
1300
|
+
});
|
|
1301
|
+
return {
|
|
1302
|
+
countries: (response.countries ?? []).map(mapCountry),
|
|
1303
|
+
meta: mapMeta$1(response.meta)
|
|
1304
|
+
};
|
|
1305
|
+
} };
|
|
1306
|
+
}
|
|
1873
1307
|
//#endregion
|
|
1874
|
-
//#region src/
|
|
1875
|
-
/**
|
|
1876
|
-
|
|
1877
|
-
|
|
1308
|
+
//#region ../../store/api-client/src/portal-tenant-languages-adapter.ts
|
|
1309
|
+
/**
|
|
1310
|
+
* Maps a BFF language to the port's Language shape.
|
|
1311
|
+
*/
|
|
1312
|
+
function mapLanguage(raw) {
|
|
1313
|
+
return {
|
|
1314
|
+
code: raw.code ?? "",
|
|
1315
|
+
name: raw.name ?? ""
|
|
1316
|
+
};
|
|
1878
1317
|
}
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1318
|
+
/**
|
|
1319
|
+
* Maps the BFF meta envelope to the port's ApiMeta shape.
|
|
1320
|
+
*/
|
|
1321
|
+
function mapMeta(raw) {
|
|
1882
1322
|
return {
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1323
|
+
request_id: raw?.request_id ?? null,
|
|
1324
|
+
timestamp: raw?.timestamp ?? "",
|
|
1325
|
+
pagination: raw?.pagination ? {
|
|
1326
|
+
cursor: raw.pagination.cursor ?? null,
|
|
1327
|
+
limit: raw.pagination.limit,
|
|
1328
|
+
next_cursor: raw.pagination.next_cursor ?? null,
|
|
1329
|
+
prev_cursor: raw.pagination.prev_cursor ?? null
|
|
1330
|
+
} : void 0
|
|
1888
1331
|
};
|
|
1889
1332
|
}
|
|
1890
1333
|
/**
|
|
1891
|
-
* Creates
|
|
1334
|
+
* Creates a LanguagesApi adapter backed by the portal-tenant store BFF.
|
|
1892
1335
|
*
|
|
1893
|
-
* Maps the generated portal-tenant
|
|
1894
|
-
*
|
|
1895
|
-
*
|
|
1336
|
+
* Maps the generated portal-tenant-store namespace functions to the abstract
|
|
1337
|
+
* LanguagesApi port, closing over the FetchClient so consumers don't need
|
|
1338
|
+
* to pass it per-call.
|
|
1896
1339
|
*/
|
|
1897
|
-
function
|
|
1898
|
-
return {
|
|
1899
|
-
const response = await
|
|
1340
|
+
function createPortalTenantLanguagesAdapter(client) {
|
|
1341
|
+
return { listLanguages: async (params) => {
|
|
1342
|
+
const response = await languages_list(client, {
|
|
1343
|
+
"page[cursor]": params?.cursor,
|
|
1344
|
+
"page[limit]": params?.limit
|
|
1345
|
+
});
|
|
1900
1346
|
return {
|
|
1901
|
-
|
|
1902
|
-
meta:
|
|
1903
|
-
request_id: response.meta?.request_id ?? "",
|
|
1904
|
-
timestamp: response.meta?.timestamp ?? ""
|
|
1905
|
-
}
|
|
1347
|
+
languages: (response.languages ?? []).map(mapLanguage),
|
|
1348
|
+
meta: mapMeta(response.meta)
|
|
1906
1349
|
};
|
|
1907
1350
|
} };
|
|
1908
1351
|
}
|
|
1909
1352
|
//#endregion
|
|
1910
|
-
//#region src/adapters/
|
|
1911
|
-
|
|
1353
|
+
//#region src/adapters/countries-api-adapter.ts
|
|
1354
|
+
/**
|
|
1355
|
+
* Creates a CountriesApi adapter backed by the portal-tenant store BFF.
|
|
1356
|
+
*
|
|
1357
|
+
* Delegates to the adapter factory in `@fluid-app/store-api-client` which
|
|
1358
|
+
* maps the generated portal-tenant-store namespace functions to the abstract
|
|
1359
|
+
* CountriesApi port.
|
|
1360
|
+
*/
|
|
1361
|
+
function createCountriesApiAdapter(client) {
|
|
1362
|
+
return createPortalTenantCountriesAdapter(client);
|
|
1363
|
+
}
|
|
1364
|
+
//#endregion
|
|
1365
|
+
//#region src/adapters/languages-api-adapter.ts
|
|
1366
|
+
/**
|
|
1367
|
+
* Creates a LanguagesApi adapter backed by the portal-tenant store BFF.
|
|
1368
|
+
*
|
|
1369
|
+
* Delegates to the adapter factory in `@fluid-app/store-api-client` which
|
|
1370
|
+
* maps the generated portal-tenant-store namespace functions to the abstract
|
|
1371
|
+
* LanguagesApi port.
|
|
1372
|
+
*/
|
|
1373
|
+
function createLanguagesApiAdapter(client) {
|
|
1374
|
+
return createPortalTenantLanguagesAdapter(client);
|
|
1375
|
+
}
|
|
1376
|
+
//#endregion
|
|
1377
|
+
//#region ../../api-clients/portal-tenant-mysite/src/namespaces/portal_tenant_mysite.ts
|
|
1378
|
+
/**
|
|
1379
|
+
* Get MySite profile for the current user
|
|
1380
|
+
* Returns the member's MySite profile.
|
|
1381
|
+
*
|
|
1382
|
+
* @param client - Fetch client instance
|
|
1383
|
+
|
|
1384
|
+
*/
|
|
1385
|
+
async function mysite_profile_show(client) {
|
|
1386
|
+
return client.get(`/api/mysite/profile`);
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Update MySite profile
|
|
1390
|
+
* Updates the member's MySite profile fields.
|
|
1391
|
+
*
|
|
1392
|
+
* @param client - Fetch client instance
|
|
1393
|
+
* @param body - body
|
|
1394
|
+
*/
|
|
1395
|
+
async function mysite_profile_update(client, body) {
|
|
1396
|
+
return client.put(`/api/mysite/profile`, body);
|
|
1397
|
+
}
|
|
1398
|
+
/**
|
|
1399
|
+
* List available MySite themes
|
|
1400
|
+
* Returns available MySite themes.
|
|
1401
|
+
*
|
|
1402
|
+
* @param client - Fetch client instance
|
|
1403
|
+
* @param [params] - params
|
|
1404
|
+
*/
|
|
1405
|
+
async function mysite_themes_list(client, params) {
|
|
1406
|
+
return client.get(`/api/mysite/themes`, params);
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Update MySite settings
|
|
1410
|
+
* Updates the member's MySite settings.
|
|
1411
|
+
*
|
|
1412
|
+
* @param client - Fetch client instance
|
|
1413
|
+
* @param body - body
|
|
1414
|
+
*/
|
|
1415
|
+
async function mysite_settings_update(client, body) {
|
|
1416
|
+
return client.put(`/api/mysite/settings`, body);
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* List MySite links for the current user
|
|
1420
|
+
* Returns the member's MySite links, ordered by position.
|
|
1421
|
+
*
|
|
1422
|
+
* @param client - Fetch client instance
|
|
1423
|
+
* @param [params] - params
|
|
1424
|
+
*/
|
|
1425
|
+
async function mysite_links_list(client, params) {
|
|
1426
|
+
return client.get(`/api/mysite/links`, params);
|
|
1427
|
+
}
|
|
1428
|
+
/**
|
|
1429
|
+
* Create a MySite link
|
|
1430
|
+
* Adds a new link to the member's MySite.
|
|
1431
|
+
*
|
|
1432
|
+
* @param client - Fetch client instance
|
|
1433
|
+
* @param body - body
|
|
1434
|
+
*/
|
|
1435
|
+
async function mysite_links_create(client, body) {
|
|
1436
|
+
return client.post(`/api/mysite/links`, body);
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* Update a MySite link
|
|
1440
|
+
* Updates an existing MySite link.
|
|
1441
|
+
*
|
|
1442
|
+
* @param client - Fetch client instance
|
|
1443
|
+
* @param id - id
|
|
1444
|
+
* @param body - body
|
|
1445
|
+
*/
|
|
1446
|
+
async function mysite_links_update(client, id, body) {
|
|
1447
|
+
return client.put(`/api/mysite/links/${id}`, body);
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Delete a MySite link
|
|
1451
|
+
* Removes a link from the member's MySite.
|
|
1452
|
+
*
|
|
1453
|
+
* @param client - Fetch client instance
|
|
1454
|
+
* @param id - id
|
|
1455
|
+
*/
|
|
1456
|
+
async function mysite_links_destroy(client, id) {
|
|
1457
|
+
return client.delete(`/api/mysite/links/${id}`);
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* Reorder MySite links
|
|
1461
|
+
* Reorders MySite links by providing an ordered list of IDs.
|
|
1462
|
+
*
|
|
1463
|
+
* @param client - Fetch client instance
|
|
1464
|
+
* @param body - body
|
|
1465
|
+
*/
|
|
1466
|
+
async function mysite_links_bulk_reorder(client, body) {
|
|
1467
|
+
return client.patch(`/api/mysite/links/bulk`, body);
|
|
1468
|
+
}
|
|
1469
|
+
/**
|
|
1470
|
+
* List MySite favorite products
|
|
1471
|
+
* Returns the member's MySite favorite products.
|
|
1472
|
+
*
|
|
1473
|
+
* @param client - Fetch client instance
|
|
1474
|
+
* @param [params] - params
|
|
1475
|
+
*/
|
|
1476
|
+
async function mysite_favorites_list(client, params) {
|
|
1477
|
+
return client.get(`/api/mysite/favorites`, params);
|
|
1478
|
+
}
|
|
1479
|
+
/**
|
|
1480
|
+
* Add a product to MySite favorites
|
|
1481
|
+
* Adds a product to the member's MySite favorites.
|
|
1482
|
+
*
|
|
1483
|
+
* @param client - Fetch client instance
|
|
1484
|
+
* @param body - body
|
|
1485
|
+
*/
|
|
1486
|
+
async function mysite_favorites_create(client, body) {
|
|
1487
|
+
return client.post(`/api/mysite/favorites`, body);
|
|
1488
|
+
}
|
|
1489
|
+
/**
|
|
1490
|
+
* Remove a product from MySite favorites
|
|
1491
|
+
* Removes a product from the member's MySite favorites.
|
|
1492
|
+
*
|
|
1493
|
+
* @param client - Fetch client instance
|
|
1494
|
+
* @param id - id
|
|
1495
|
+
*/
|
|
1496
|
+
async function mysite_favorites_destroy(client, id) {
|
|
1497
|
+
return client.delete(`/api/mysite/favorites/${id}`);
|
|
1498
|
+
}
|
|
1499
|
+
/**
|
|
1500
|
+
* Reorder MySite favorites
|
|
1501
|
+
* Reorders MySite favorites by providing an ordered list of IDs.
|
|
1502
|
+
*
|
|
1503
|
+
* @param client - Fetch client instance
|
|
1504
|
+
* @param body - body
|
|
1505
|
+
*/
|
|
1506
|
+
async function mysite_favorites_bulk_reorder(client, body) {
|
|
1507
|
+
return client.patch(`/api/mysite/favorites/bulk`, body);
|
|
1508
|
+
}
|
|
1509
|
+
//#endregion
|
|
1510
|
+
//#region src/adapters/mysite-api-adapter.ts
|
|
1511
|
+
function mapProfile(raw) {
|
|
1912
1512
|
return {
|
|
1913
1513
|
id: raw.id ?? 0,
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
last_name: raw.last_name ?? "",
|
|
1917
|
-
email: raw.email ?? "",
|
|
1918
|
-
phone: raw.phone ?? null,
|
|
1514
|
+
mysite_url: raw.mysite_url ?? null,
|
|
1515
|
+
theme_id: raw.theme_id ?? null,
|
|
1919
1516
|
bio: raw.bio ?? null,
|
|
1920
1517
|
avatar_url: raw.avatar_url ?? null,
|
|
1921
|
-
|
|
1922
|
-
|
|
1518
|
+
display_name: raw.display_name ?? null,
|
|
1519
|
+
slug: raw.slug ?? null
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
function mapLink(raw) {
|
|
1523
|
+
return {
|
|
1524
|
+
id: raw.id ?? 0,
|
|
1525
|
+
url: raw.url ?? "",
|
|
1526
|
+
title: raw.title ?? "",
|
|
1527
|
+
position: raw.position ?? 0
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
function mapFavorite(raw) {
|
|
1531
|
+
return {
|
|
1532
|
+
id: raw.id ?? 0,
|
|
1533
|
+
product_id: raw.product_id ?? 0,
|
|
1534
|
+
product_name: raw.product_name ?? null,
|
|
1535
|
+
product_image_url: raw.product_image_url ?? null,
|
|
1536
|
+
position: raw.position ?? 0,
|
|
1537
|
+
created_at: raw.created_at ?? null
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
function mapTheme(raw) {
|
|
1541
|
+
return {
|
|
1542
|
+
id: raw.id ?? 0,
|
|
1543
|
+
name: raw.name ?? "",
|
|
1544
|
+
preview_url: raw.preview_url ?? null
|
|
1923
1545
|
};
|
|
1924
1546
|
}
|
|
1925
|
-
function
|
|
1547
|
+
function createMySiteApiAdapter(client) {
|
|
1926
1548
|
return {
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
return {
|
|
1930
|
-
account: mapAccount(response.account ?? {}),
|
|
1931
|
-
meta: {
|
|
1932
|
-
request_id: response.meta?.request_id ?? "",
|
|
1933
|
-
timestamp: response.meta?.timestamp ?? ""
|
|
1934
|
-
}
|
|
1935
|
-
};
|
|
1549
|
+
fetchProfile: async () => {
|
|
1550
|
+
return mapProfile((await mysite_profile_show(client)).profile ?? {});
|
|
1936
1551
|
},
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
};
|
|
1552
|
+
updateProfile: async (body) => {
|
|
1553
|
+
return mapProfile((await mysite_profile_update(client, { profile: {
|
|
1554
|
+
display_name: body.display_name,
|
|
1555
|
+
bio: body.bio,
|
|
1556
|
+
avatar_url: body.avatar_url
|
|
1557
|
+
} })).profile ?? {});
|
|
1558
|
+
},
|
|
1559
|
+
updateSettings: async (body) => {
|
|
1560
|
+
await Promise.all([body.theme_id !== void 0 ? mysite_settings_update(client, { settings: { theme_id: body.theme_id } }) : Promise.resolve(), body.slug !== void 0 ? mysite_profile_update(client, { profile: { slug: body.slug } }) : Promise.resolve()]);
|
|
1561
|
+
},
|
|
1562
|
+
listLinks: async () => {
|
|
1563
|
+
return ((await mysite_links_list(client)).links ?? []).map(mapLink);
|
|
1564
|
+
},
|
|
1565
|
+
createLink: async (body) => {
|
|
1566
|
+
return mapLink((await mysite_links_create(client, { link: {
|
|
1567
|
+
title: body.title,
|
|
1568
|
+
url: body.url
|
|
1569
|
+
} })).link ?? {});
|
|
1570
|
+
},
|
|
1571
|
+
updateLink: async (linkId, body) => {
|
|
1572
|
+
return mapLink((await mysite_links_update(client, linkId, { link: {
|
|
1573
|
+
title: body.title,
|
|
1574
|
+
url: body.url
|
|
1575
|
+
} })).link ?? {});
|
|
1576
|
+
},
|
|
1577
|
+
deleteLink: async (linkId) => {
|
|
1578
|
+
await mysite_links_destroy(client, linkId);
|
|
1579
|
+
},
|
|
1580
|
+
reorderLinks: async (orderedIds) => {
|
|
1581
|
+
return ((await mysite_links_bulk_reorder(client, { ordered_ids: orderedIds })).links ?? []).map(mapLink);
|
|
1582
|
+
},
|
|
1583
|
+
listFavorites: async () => {
|
|
1584
|
+
return ((await mysite_favorites_list(client)).favorites ?? []).map(mapFavorite);
|
|
1585
|
+
},
|
|
1586
|
+
addFavorite: async (body) => {
|
|
1587
|
+
return mapFavorite((await mysite_favorites_create(client, { favorite: { product_id: body.product_id } })).favorite ?? {});
|
|
1588
|
+
},
|
|
1589
|
+
deleteFavorite: async (favoriteId) => {
|
|
1590
|
+
await mysite_favorites_destroy(client, favoriteId);
|
|
1591
|
+
},
|
|
1592
|
+
reorderFavorites: async (orderedIds) => {
|
|
1593
|
+
return ((await mysite_favorites_bulk_reorder(client, { ordered_ids: orderedIds })).favorites ?? []).map(mapFavorite);
|
|
1594
|
+
},
|
|
1595
|
+
listThemes: async () => {
|
|
1596
|
+
return ((await mysite_themes_list(client)).themes ?? []).map(mapTheme);
|
|
1946
1597
|
}
|
|
1947
1598
|
};
|
|
1948
1599
|
}
|
|
1949
1600
|
//#endregion
|
|
1950
|
-
//#region
|
|
1601
|
+
//#region ../core/src/theme/types.ts
|
|
1602
|
+
const SEMANTIC_COLOR_NAMES = [
|
|
1603
|
+
"background",
|
|
1604
|
+
"foreground",
|
|
1605
|
+
"primary",
|
|
1606
|
+
"secondary",
|
|
1607
|
+
"accent",
|
|
1608
|
+
"muted",
|
|
1609
|
+
"destructive"
|
|
1610
|
+
];
|
|
1611
|
+
const SHADE_STEPS = [
|
|
1612
|
+
100,
|
|
1613
|
+
200,
|
|
1614
|
+
300,
|
|
1615
|
+
400,
|
|
1616
|
+
500,
|
|
1617
|
+
600,
|
|
1618
|
+
700,
|
|
1619
|
+
800,
|
|
1620
|
+
900
|
|
1621
|
+
];
|
|
1622
|
+
const FONT_SIZE_KEYS = [
|
|
1623
|
+
"extraSmall",
|
|
1624
|
+
"small",
|
|
1625
|
+
"regular",
|
|
1626
|
+
"large",
|
|
1627
|
+
"extraLarge",
|
|
1628
|
+
"giant"
|
|
1629
|
+
];
|
|
1630
|
+
const FONT_FAMILY_KEYS = ["header", "body"];
|
|
1631
|
+
const RADIUS_KEYS = [
|
|
1632
|
+
"small",
|
|
1633
|
+
"medium",
|
|
1634
|
+
"large",
|
|
1635
|
+
"extraLarge"
|
|
1636
|
+
];
|
|
1637
|
+
//#endregion
|
|
1638
|
+
//#region ../core/src/theme/color-engine.ts
|
|
1951
1639
|
/**
|
|
1952
|
-
*
|
|
1953
|
-
*
|
|
1640
|
+
* Attempt to convert any string into a Color using colorjs.io.
|
|
1641
|
+
* If the string is exactly 6 characters it is assumed to be a bare hex value
|
|
1642
|
+
* (e.g. "3b82f6") and a "#" prefix is added before parsing.
|
|
1954
1643
|
*
|
|
1955
|
-
* @
|
|
1956
|
-
* @param [params] - params
|
|
1644
|
+
* @returns the parsed Color, or a neutral gray (`oklch(0.5 0 0)`) on failure
|
|
1957
1645
|
*/
|
|
1958
|
-
|
|
1959
|
-
|
|
1646
|
+
function parseColor(value) {
|
|
1647
|
+
if (value.length === 6) value = `#${value}`;
|
|
1648
|
+
try {
|
|
1649
|
+
return new Color(value);
|
|
1650
|
+
} catch (error) {
|
|
1651
|
+
console.warn("[theme] Failed to parse color:", value, error);
|
|
1652
|
+
return new Color("oklch", [
|
|
1653
|
+
.5,
|
|
1654
|
+
0,
|
|
1655
|
+
0
|
|
1656
|
+
]);
|
|
1657
|
+
}
|
|
1960
1658
|
}
|
|
1961
1659
|
/**
|
|
1962
|
-
*
|
|
1963
|
-
*
|
|
1964
|
-
*
|
|
1965
|
-
* @param client - Fetch client instance
|
|
1966
|
-
* @param body - body
|
|
1660
|
+
* Returns either the original foreground or a corrected lightness variant,
|
|
1661
|
+
* whichever provides better contrast against `color`.
|
|
1662
|
+
* Inversion triggers when the APCA contrast is below 50.
|
|
1967
1663
|
*/
|
|
1968
|
-
|
|
1969
|
-
|
|
1664
|
+
function getForegroundColor(foreground, color) {
|
|
1665
|
+
if (foreground.oklch.l == null || color.oklch.l == null) return foreground;
|
|
1666
|
+
if (color.contrastAPCA(foreground) < 50) return new Color("oklch", [
|
|
1667
|
+
color.oklch.l < .7 ? .95 : .15,
|
|
1668
|
+
foreground.oklch.c || 0,
|
|
1669
|
+
foreground.oklch.h || 0
|
|
1670
|
+
]);
|
|
1671
|
+
return foreground;
|
|
1970
1672
|
}
|
|
1971
1673
|
/**
|
|
1972
|
-
*
|
|
1973
|
-
*
|
|
1974
|
-
*
|
|
1975
|
-
*
|
|
1976
|
-
*
|
|
1977
|
-
* @param body - body
|
|
1674
|
+
* Generate a 100–900 shade ramp from a base color.
|
|
1675
|
+
* Base anchors at 500. Light shades (100–400) step toward white,
|
|
1676
|
+
* dark shades (600–900) step toward black. Dark steps use an asymmetric
|
|
1677
|
+
* multiplier (1.6×, 1.875×, 3×, 4× of `darkStep`) for a more gradual
|
|
1678
|
+
* initial descent. Chroma is nudged per step for perceptually natural ramps.
|
|
1978
1679
|
*/
|
|
1979
|
-
|
|
1980
|
-
|
|
1680
|
+
function generateShades(base) {
|
|
1681
|
+
const l = base.oklch.l ?? 0;
|
|
1682
|
+
const c = base.oklch.c ?? 0;
|
|
1683
|
+
const h = base.oklch.h ?? 0;
|
|
1684
|
+
const safeMax = l >= .885 ? .995 : .97;
|
|
1685
|
+
const safeMin = l <= .33 ? 0 : .21;
|
|
1686
|
+
const lightStep = (safeMax - l) / 5;
|
|
1687
|
+
const darkStep = -(l - safeMin) / 8;
|
|
1688
|
+
const shade = (lDelta, cDelta) => {
|
|
1689
|
+
return new Color("oklch", [
|
|
1690
|
+
Math.max(0, Math.min(1, l + lDelta)),
|
|
1691
|
+
c <= .001 ? c : Math.max(0, c + cDelta),
|
|
1692
|
+
h
|
|
1693
|
+
]);
|
|
1694
|
+
};
|
|
1695
|
+
return {
|
|
1696
|
+
100: shade(5 * lightStep, -.00375),
|
|
1697
|
+
200: shade(4 * lightStep, -.00375),
|
|
1698
|
+
300: shade(3 * lightStep, -.00375),
|
|
1699
|
+
400: shade(2 * lightStep, -.00375),
|
|
1700
|
+
500: new Color("oklch", [
|
|
1701
|
+
l,
|
|
1702
|
+
c,
|
|
1703
|
+
h
|
|
1704
|
+
]),
|
|
1705
|
+
600: shade(1.6 * darkStep, .025),
|
|
1706
|
+
700: shade(1.875 * 2 * darkStep, .05),
|
|
1707
|
+
800: shade(6 * darkStep, .075),
|
|
1708
|
+
900: shade(8 * darkStep, .1)
|
|
1709
|
+
};
|
|
1981
1710
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1711
|
+
const DARK_DERIVATION_CONFIG = {
|
|
1712
|
+
background: {
|
|
1713
|
+
baseLightness: .15,
|
|
1714
|
+
fgLightness: .93
|
|
1715
|
+
},
|
|
1716
|
+
foreground: {
|
|
1717
|
+
baseLightness: .93,
|
|
1718
|
+
fgLightness: .15
|
|
1719
|
+
},
|
|
1720
|
+
muted: {
|
|
1721
|
+
baseLightness: .22,
|
|
1722
|
+
fgLightness: .75
|
|
1723
|
+
},
|
|
1724
|
+
primary: {
|
|
1725
|
+
baseLightness: "invert",
|
|
1726
|
+
fgLightness: .95,
|
|
1727
|
+
chromaScale: .9
|
|
1728
|
+
},
|
|
1729
|
+
secondary: {
|
|
1730
|
+
baseLightness: "invert",
|
|
1731
|
+
fgLightness: .93,
|
|
1732
|
+
chromaScale: .85
|
|
1733
|
+
},
|
|
1734
|
+
accent: {
|
|
1735
|
+
baseLightness: "invert",
|
|
1736
|
+
fgLightness: .95,
|
|
1737
|
+
chromaScale: .9
|
|
1738
|
+
},
|
|
1739
|
+
destructive: {
|
|
1740
|
+
baseLightness: "invert",
|
|
1741
|
+
fgLightness: .95,
|
|
1742
|
+
chromaScale: .95
|
|
1743
|
+
}
|
|
1744
|
+
};
|
|
1745
|
+
/** Invert OKLCH lightness (1 - l), clamped to [0.35, 0.75] to avoid extremes. */
|
|
1746
|
+
function invertLightness(l) {
|
|
1747
|
+
const inverted = 1 - l;
|
|
1748
|
+
return Math.max(.35, Math.min(.75, inverted));
|
|
1991
1749
|
}
|
|
1992
1750
|
/**
|
|
1993
|
-
*
|
|
1994
|
-
* Returns displayable payment methods on the member's customer record, excluding Apple Pay sources.
|
|
1995
|
-
*
|
|
1996
|
-
* @param client - Fetch client instance
|
|
1997
|
-
* @param [params] - params
|
|
1751
|
+
* Derive a dark-mode ThemeColorInput from its light-mode counterpart.
|
|
1998
1752
|
*/
|
|
1999
|
-
|
|
2000
|
-
|
|
1753
|
+
function deriveDarkVariant(name, light) {
|
|
1754
|
+
const config = DARK_DERIVATION_CONFIG[name];
|
|
1755
|
+
const chromaScale = config.chromaScale ?? 1;
|
|
1756
|
+
const baseLightness = config.baseLightness === "invert" ? invertLightness(light.base.oklch.l ?? 0) : config.baseLightness;
|
|
1757
|
+
const fgLightness = config.fgLightness === "invert" ? invertLightness(light.foreground.oklch.l ?? 0) : config.fgLightness;
|
|
1758
|
+
return {
|
|
1759
|
+
base: new Color("oklch", [
|
|
1760
|
+
baseLightness,
|
|
1761
|
+
(light.base.oklch.c || 0) * chromaScale,
|
|
1762
|
+
light.base.oklch.h || 0
|
|
1763
|
+
]),
|
|
1764
|
+
foreground: new Color("oklch", [
|
|
1765
|
+
fgLightness,
|
|
1766
|
+
(light.foreground.oklch.c || 0) * chromaScale,
|
|
1767
|
+
light.foreground.oklch.h || 0
|
|
1768
|
+
])
|
|
1769
|
+
};
|
|
2001
1770
|
}
|
|
2002
1771
|
/**
|
|
2003
|
-
*
|
|
2004
|
-
*
|
|
2005
|
-
*
|
|
2006
|
-
* @param client - Fetch client instance
|
|
2007
|
-
* @param body - body
|
|
1772
|
+
* Merge auto-derived dark colors with any user-specified overrides.
|
|
1773
|
+
* For each semantic color, if the user has fully overridden both base and
|
|
1774
|
+
* foreground those are used; otherwise the missing channels are derived.
|
|
2008
1775
|
*/
|
|
2009
|
-
|
|
2010
|
-
|
|
1776
|
+
function mergeDarkOverrides(def) {
|
|
1777
|
+
const darkColors = {};
|
|
1778
|
+
for (const name of SEMANTIC_COLOR_NAMES) {
|
|
1779
|
+
const lightInput = def.light[name];
|
|
1780
|
+
const darkOverride = def.dark[name];
|
|
1781
|
+
if (darkOverride?.base && darkOverride?.foreground) darkColors[name] = darkOverride;
|
|
1782
|
+
else if (darkOverride) {
|
|
1783
|
+
const base = darkOverride.base ?? deriveDarkVariant(name, lightInput).base;
|
|
1784
|
+
darkColors[name] = {
|
|
1785
|
+
base,
|
|
1786
|
+
foreground: darkOverride.foreground ?? getForegroundColor(def.light.foreground.base, base)
|
|
1787
|
+
};
|
|
1788
|
+
} else darkColors[name] = deriveDarkVariant(name, lightInput);
|
|
1789
|
+
}
|
|
1790
|
+
return darkColors;
|
|
1791
|
+
}
|
|
1792
|
+
function resolveColorSet(colors) {
|
|
1793
|
+
const resolved = {};
|
|
1794
|
+
for (const name of SEMANTIC_COLOR_NAMES) {
|
|
1795
|
+
const input = colors[name];
|
|
1796
|
+
const shades = generateShades(input.base);
|
|
1797
|
+
const resolvedShades = {};
|
|
1798
|
+
for (const step of SHADE_STEPS) resolvedShades[step] = shades[step];
|
|
1799
|
+
resolved[name] = {
|
|
1800
|
+
base: input.base.clone(),
|
|
1801
|
+
foreground: input.foreground.clone(),
|
|
1802
|
+
shades: resolvedShades
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
return resolved;
|
|
2011
1806
|
}
|
|
2012
1807
|
/**
|
|
2013
|
-
*
|
|
2014
|
-
*
|
|
2015
|
-
*
|
|
2016
|
-
* @param client - Fetch client instance
|
|
2017
|
-
* @param id - id
|
|
2018
|
-
* @param body - body
|
|
1808
|
+
* Resolve a ThemeDefinition into a complete ResolvedTheme.
|
|
1809
|
+
* Dark mode colors are derived from light where not overridden.
|
|
2019
1810
|
*/
|
|
2020
|
-
|
|
2021
|
-
return
|
|
1811
|
+
function resolveTheme(def) {
|
|
1812
|
+
return {
|
|
1813
|
+
id: def.id,
|
|
1814
|
+
name: def.name,
|
|
1815
|
+
light: resolveColorSet(def.light),
|
|
1816
|
+
dark: resolveColorSet(mergeDarkOverrides(def)),
|
|
1817
|
+
fontSizes: { ...def.fontSizes },
|
|
1818
|
+
fontFamilies: { ...def.fontFamilies },
|
|
1819
|
+
spacing: def.spacing,
|
|
1820
|
+
radii: { ...def.radii }
|
|
1821
|
+
};
|
|
2022
1822
|
}
|
|
1823
|
+
//#endregion
|
|
1824
|
+
//#region ../core/src/theme/tailwind-overrides.ts
|
|
2023
1825
|
/**
|
|
2024
|
-
*
|
|
2025
|
-
* Removes a payment method from the member's customer record. If the removed method was the default, the default is cleared.
|
|
2026
|
-
*
|
|
2027
|
-
* @param client - Fetch client instance
|
|
2028
|
-
* @param id - id
|
|
1826
|
+
* Specific overrides, otherwise all the overrides are generated using emitTailwindOverrides
|
|
2029
1827
|
*/
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
1828
|
+
const OVERRIDES = {
|
|
1829
|
+
"--color-gray-50": "var(--color-muted)",
|
|
1830
|
+
"--color-gray-100": "var(--color-muted-600)",
|
|
1831
|
+
"--color-gray-200": "var(--color-border)"
|
|
1832
|
+
};
|
|
2033
1833
|
/**
|
|
2034
|
-
*
|
|
2035
|
-
*
|
|
2036
|
-
*
|
|
2037
|
-
* @param client - Fetch client instance
|
|
2038
|
-
|
|
1834
|
+
* Returns the inverted shade for dark mode foreground colors.
|
|
1835
|
+
* In dark mode, light shades (50, 100) should map to dark values (950, 900) and vice versa.
|
|
2039
1836
|
*/
|
|
2040
|
-
|
|
2041
|
-
|
|
1837
|
+
function getInvertedStep(shade) {
|
|
1838
|
+
const shadeIndex = SHADE_STEPS.indexOf(shade);
|
|
1839
|
+
return SHADE_STEPS[SHADE_STEPS.length - 1 - shadeIndex] || 500;
|
|
2042
1840
|
}
|
|
2043
1841
|
/**
|
|
2044
|
-
*
|
|
2045
|
-
* Returns loyalty points ledger entries for the member's customer record in this tenant, ordered by most recent first.
|
|
2046
|
-
*
|
|
2047
|
-
* @param client - Fetch client instance
|
|
2048
|
-
* @param [params] - params
|
|
1842
|
+
* Map semantic colors to Tailwind built-in color names.
|
|
2049
1843
|
*/
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
return {
|
|
2057
|
-
id: raw.id ?? 0,
|
|
2058
|
-
street1: raw.street1 ?? "",
|
|
2059
|
-
street2: raw.street2 ?? null,
|
|
2060
|
-
city: raw.city ?? "",
|
|
2061
|
-
state: raw.state ?? "",
|
|
2062
|
-
zip: raw.zip ?? "",
|
|
2063
|
-
country: raw.country ?? "",
|
|
2064
|
-
default: raw.default ?? false,
|
|
2065
|
-
created_at: raw.created_at ?? null,
|
|
2066
|
-
updated_at: raw.updated_at ?? null
|
|
2067
|
-
};
|
|
2068
|
-
}
|
|
2069
|
-
function mapPaymentMethod(raw) {
|
|
2070
|
-
return {
|
|
2071
|
-
id: raw.id ?? 0,
|
|
2072
|
-
type: raw.type ?? "card",
|
|
2073
|
-
brand: raw.brand ?? null,
|
|
2074
|
-
last_four: raw.last_four ?? "",
|
|
2075
|
-
exp_month: raw.exp_month ?? null,
|
|
2076
|
-
exp_year: raw.exp_year ?? null,
|
|
2077
|
-
default: raw.default ?? false,
|
|
2078
|
-
created_at: raw.created_at ?? null,
|
|
2079
|
-
updated_at: raw.updated_at ?? null
|
|
1844
|
+
function emitTailwindOverrides(darkMode = false) {
|
|
1845
|
+
const TAILWIND_COLOR_MAP = {
|
|
1846
|
+
gray: "foreground",
|
|
1847
|
+
red: "destructive",
|
|
1848
|
+
blue: "primary",
|
|
1849
|
+
green: "accent"
|
|
2080
1850
|
};
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
updateAddress: async (addressId, body) => {
|
|
2098
|
-
await addresses_update(client, addressId, body);
|
|
2099
|
-
},
|
|
2100
|
-
deleteAddress: async (addressId) => {
|
|
2101
|
-
await addresses_destroy(client, addressId);
|
|
2102
|
-
},
|
|
2103
|
-
fetchPaymentMethods: async () => {
|
|
2104
|
-
const response = await payment_methods_list(client);
|
|
2105
|
-
return {
|
|
2106
|
-
payment_methods: (response.payment_methods ?? []).map(mapPaymentMethod),
|
|
2107
|
-
meta: {
|
|
2108
|
-
request_id: response.meta?.request_id ?? "",
|
|
2109
|
-
timestamp: response.meta?.timestamp ?? ""
|
|
2110
|
-
}
|
|
2111
|
-
};
|
|
2112
|
-
},
|
|
2113
|
-
createPaymentMethod: async (body) => {
|
|
2114
|
-
await payment_methods_create(client, body);
|
|
2115
|
-
},
|
|
2116
|
-
updatePaymentMethod: async (paymentMethodId, body) => {
|
|
2117
|
-
await payment_methods_update(client, paymentMethodId, body);
|
|
2118
|
-
},
|
|
2119
|
-
deletePaymentMethod: async (paymentMethodId) => {
|
|
2120
|
-
await payment_methods_destroy(client, paymentMethodId);
|
|
2121
|
-
},
|
|
2122
|
-
fetchVaultCredentials: async () => {
|
|
2123
|
-
const response = await payment_methods_vault_show(client);
|
|
2124
|
-
return {
|
|
2125
|
-
vault: {
|
|
2126
|
-
token: response.vault?.token ?? null,
|
|
2127
|
-
environment: response.vault?.environment ?? "production"
|
|
2128
|
-
},
|
|
2129
|
-
meta: {
|
|
2130
|
-
request_id: response.meta?.request_id ?? "",
|
|
2131
|
-
timestamp: response.meta?.timestamp ?? ""
|
|
2132
|
-
}
|
|
2133
|
-
};
|
|
2134
|
-
},
|
|
2135
|
-
fetchPointsLedgers: async () => {
|
|
2136
|
-
const response = await points_ledgers_list(client);
|
|
2137
|
-
return {
|
|
2138
|
-
points_ledgers: (response.points_ledgers ?? []).map((entry) => ({
|
|
2139
|
-
id: entry.id ?? 0,
|
|
2140
|
-
amount: entry.amount ?? 0,
|
|
2141
|
-
total_balance: entry.total_balance ?? 0,
|
|
2142
|
-
metadata: entry.metadata ?? null,
|
|
2143
|
-
created_at: entry.created_at ?? ""
|
|
2144
|
-
})),
|
|
2145
|
-
meta: {
|
|
2146
|
-
request_id: response.meta?.request_id ?? "",
|
|
2147
|
-
timestamp: response.meta?.timestamp ?? ""
|
|
2148
|
-
}
|
|
2149
|
-
};
|
|
2150
|
-
}
|
|
1851
|
+
const TAILWIND_SHADES = [
|
|
1852
|
+
50,
|
|
1853
|
+
100,
|
|
1854
|
+
200,
|
|
1855
|
+
300,
|
|
1856
|
+
400,
|
|
1857
|
+
500,
|
|
1858
|
+
600,
|
|
1859
|
+
700,
|
|
1860
|
+
800,
|
|
1861
|
+
900,
|
|
1862
|
+
950
|
|
1863
|
+
];
|
|
1864
|
+
const SHADE_REMAP = {
|
|
1865
|
+
50: 100,
|
|
1866
|
+
950: 900
|
|
2151
1867
|
};
|
|
1868
|
+
const lines = [];
|
|
1869
|
+
for (const [twName, semantic] of Object.entries(TAILWIND_COLOR_MAP)) for (const shade of TAILWIND_SHADES) {
|
|
1870
|
+
const step = SHADE_REMAP[shade] ?? shade;
|
|
1871
|
+
const override = OVERRIDES[`--color-${twName}-${shade}`];
|
|
1872
|
+
lines.push(`--color-${twName}-${shade}: ${override ? override : `var(--color-${semantic}-${semantic === "foreground" && darkMode === true ? getInvertedStep(step) : step})`};`);
|
|
1873
|
+
}
|
|
1874
|
+
lines.push("--color-white: var(--color-background);");
|
|
1875
|
+
lines.push("--color-black: var(--color-foreground);");
|
|
1876
|
+
return lines;
|
|
2152
1877
|
}
|
|
2153
1878
|
//#endregion
|
|
2154
|
-
//#region
|
|
1879
|
+
//#region ../core/src/theme/css-generator.ts
|
|
1880
|
+
function colorToCSS(color) {
|
|
1881
|
+
const result = color.toString({ format: "oklch" });
|
|
1882
|
+
if (result.includes("NaN")) {
|
|
1883
|
+
console.warn("[theme] colorToCSS produced NaN, using neutral fallback:", result);
|
|
1884
|
+
return "oklch(0.5 0 0)";
|
|
1885
|
+
}
|
|
1886
|
+
return result;
|
|
1887
|
+
}
|
|
1888
|
+
function camelToKebab(str) {
|
|
1889
|
+
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
1890
|
+
}
|
|
2155
1891
|
/**
|
|
2156
|
-
*
|
|
2157
|
-
*
|
|
2158
|
-
*
|
|
2159
|
-
* @param client - Fetch client instance
|
|
2160
|
-
* @param [params] - params
|
|
1892
|
+
* Emit --color-{name}, --color-{name}-foreground, --color-{name}-{shade} vars.
|
|
1893
|
+
* Uses --color- prefix to match portal-widgets/tailwind.config.ts.
|
|
2161
1894
|
*/
|
|
2162
|
-
|
|
2163
|
-
|
|
1895
|
+
function emitColorVars(colors) {
|
|
1896
|
+
const lines = [];
|
|
1897
|
+
for (const name of SEMANTIC_COLOR_NAMES) {
|
|
1898
|
+
const color = colors[name];
|
|
1899
|
+
lines.push(`--color-${name}: ${colorToCSS(color.base)};`);
|
|
1900
|
+
lines.push(`--color-${name}-foreground: ${colorToCSS(color.foreground)};`);
|
|
1901
|
+
for (const step of SHADE_STEPS) lines.push(`--color-${name}-${step}: ${colorToCSS(color.shades[step])};`);
|
|
1902
|
+
}
|
|
1903
|
+
return lines;
|
|
2164
1904
|
}
|
|
2165
1905
|
/**
|
|
2166
|
-
*
|
|
2167
|
-
*
|
|
2168
|
-
*
|
|
2169
|
-
*
|
|
2170
|
-
* @param [params] - params
|
|
1906
|
+
* Format a font family value for CSS output.
|
|
1907
|
+
* - If the value starts with "var(" (legacy), pass through as-is
|
|
1908
|
+
* - If the value already contains a comma (has fallback), pass through as-is
|
|
1909
|
+
* - Otherwise, wrap in quotes and append a generic sans-serif fallback
|
|
2171
1910
|
*/
|
|
2172
|
-
|
|
2173
|
-
|
|
1911
|
+
function formatFontFamily(value) {
|
|
1912
|
+
if (value.startsWith("var(")) return value;
|
|
1913
|
+
if (value.includes(",")) return value;
|
|
1914
|
+
return `'${value}', sans-serif`;
|
|
2174
1915
|
}
|
|
2175
|
-
//#endregion
|
|
2176
|
-
//#region ../../store/api-client/src/portal-tenant-countries-adapter.ts
|
|
2177
1916
|
/**
|
|
2178
|
-
*
|
|
1917
|
+
* Emit non-color CSS variables (font sizes, families, spacing, radii).
|
|
2179
1918
|
*/
|
|
2180
|
-
function
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
};
|
|
1919
|
+
function emitNonColorVars(theme) {
|
|
1920
|
+
const lines = [];
|
|
1921
|
+
for (const key of FONT_SIZE_KEYS) lines.push(`--font-size-${camelToKebab(key)}: ${theme.fontSizes[key]};`);
|
|
1922
|
+
for (const key of FONT_FAMILY_KEYS) lines.push(`--font-${key}: ${formatFontFamily(theme.fontFamilies[key])};`);
|
|
1923
|
+
lines.push(`--spacing: ${theme.spacing};`);
|
|
1924
|
+
for (const key of RADIUS_KEYS) lines.push(`--radius-${camelToKebab(key)}: ${theme.radii[key]};`);
|
|
1925
|
+
return lines;
|
|
2185
1926
|
}
|
|
2186
1927
|
/**
|
|
2187
|
-
*
|
|
1928
|
+
* Static CSS alias variables that bridge theme var names to Tailwind/component conventions.
|
|
1929
|
+
* These are always emitted and not mode-dependent.
|
|
2188
1930
|
*/
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
1931
|
+
const globalCSSOverride = [
|
|
1932
|
+
"--color-background-foreground: var(--color-foreground);",
|
|
1933
|
+
"--color-foreground-foreground: var(--color-background);",
|
|
1934
|
+
"--color-contrast: var(--color-foreground);",
|
|
1935
|
+
...SEMANTIC_COLOR_NAMES.map((value) => `--${value}: var(--color-${value});`),
|
|
1936
|
+
...SEMANTIC_COLOR_NAMES.map((value) => `--${value}-foreground: var(--color-${value}-foreground);`),
|
|
1937
|
+
"--sidebar-ring: var(--color-primary);",
|
|
1938
|
+
"--sidebar-border: var(--color-border);",
|
|
1939
|
+
"--sidebar-accent-foreground: var(--color-accent-foreground);",
|
|
1940
|
+
"--sidebar-accent: var(--color-accent);",
|
|
1941
|
+
"--sidebar-primary-foreground: var(--color-primary-foreground);",
|
|
1942
|
+
"--sidebar-primary: var(--color-primary);",
|
|
1943
|
+
"--sidebar-foreground: var(--color-muted-foreground);",
|
|
1944
|
+
"--sidebar: var(--color-muted);",
|
|
1945
|
+
"--border: var(--color-background-600);",
|
|
1946
|
+
"--ring: var(--color-primary);",
|
|
1947
|
+
"--popover: var(--color-background);",
|
|
1948
|
+
"--popover-foreground: var(--color-foreground);",
|
|
1949
|
+
"--card: var(--color-muted);",
|
|
1950
|
+
"--card-foreground: var(--color-muted-foreground);",
|
|
1951
|
+
"--radius-sm: var(--radius-small);",
|
|
1952
|
+
"--radius-md: var(--radius-medium);",
|
|
1953
|
+
"--radius-lg: var(--radius-large);",
|
|
1954
|
+
"--radius-xl: var(--radius-extra-large);",
|
|
1955
|
+
"--text-xs: var(--font-size-extra-small);",
|
|
1956
|
+
"--text-sm: var(--font-size-small);",
|
|
1957
|
+
"--text-base: var(--font-size-regular);",
|
|
1958
|
+
"--text-lg: var(--font-size-large);",
|
|
1959
|
+
"--text-xl: var(--font-size-extra-large);",
|
|
1960
|
+
"--text-2xl: var(--font-size-giant);"
|
|
1961
|
+
];
|
|
2197
1962
|
/**
|
|
2198
|
-
*
|
|
1963
|
+
* Overrides for global tailwindcss for specifically dark mode.
|
|
2199
1964
|
*/
|
|
2200
|
-
|
|
2201
|
-
return {
|
|
2202
|
-
request_id: raw?.request_id ?? null,
|
|
2203
|
-
timestamp: raw?.timestamp ?? "",
|
|
2204
|
-
pagination: raw?.pagination ? {
|
|
2205
|
-
cursor: raw.pagination.cursor ?? null,
|
|
2206
|
-
limit: raw.pagination.limit,
|
|
2207
|
-
next_cursor: raw.pagination.next_cursor ?? null,
|
|
2208
|
-
prev_cursor: raw.pagination.prev_cursor ?? null
|
|
2209
|
-
} : void 0
|
|
2210
|
-
};
|
|
2211
|
-
}
|
|
1965
|
+
const globalDarkCSSOverride = ["--border: var(--color-background-400);"];
|
|
2212
1966
|
/**
|
|
2213
|
-
*
|
|
2214
|
-
*
|
|
2215
|
-
*
|
|
2216
|
-
* CountriesApi port, closing over the FetchClient so consumers don't need
|
|
2217
|
-
* to pass it per-call.
|
|
1967
|
+
* Generate a complete CSS string for a resolved theme.
|
|
1968
|
+
* Outputs 2–3 blocks: light default, dark explicit via `[data-theme-mode="dark"]`,
|
|
1969
|
+
* and (unless `disableAutoTheme`) a `prefers-color-scheme: dark` media query block.
|
|
2218
1970
|
*/
|
|
2219
|
-
function
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
}
|
|
1971
|
+
function generateThemeCSS(theme, options = {}) {
|
|
1972
|
+
const sel = `[data-theme="${theme.id}"]`;
|
|
1973
|
+
const tw = options.mapTailwindColors ?? true;
|
|
1974
|
+
const blocks = [];
|
|
1975
|
+
blocks.push(`${sel} {`);
|
|
1976
|
+
blocks.push(...globalCSSOverride);
|
|
1977
|
+
blocks.push(...emitNonColorVars(theme));
|
|
1978
|
+
blocks.push(...emitColorVars(theme.light));
|
|
1979
|
+
if (tw) blocks.push(...emitTailwindOverrides());
|
|
1980
|
+
blocks.push(`}`);
|
|
1981
|
+
blocks.push(`${sel}[data-theme-mode="dark"] {`);
|
|
1982
|
+
blocks.push(...globalDarkCSSOverride);
|
|
1983
|
+
blocks.push(...emitColorVars(theme.dark));
|
|
1984
|
+
if (tw) blocks.push(...emitTailwindOverrides(true));
|
|
1985
|
+
blocks.push(`}`);
|
|
1986
|
+
if (!options.disableAutoTheme) {
|
|
1987
|
+
blocks.push(`@media (prefers-color-scheme: dark) {`);
|
|
1988
|
+
blocks.push(`${sel}:not([data-theme-mode]) {`);
|
|
1989
|
+
blocks.push(...globalDarkCSSOverride);
|
|
1990
|
+
blocks.push(...emitColorVars(theme.dark).map((l) => `${l}`));
|
|
1991
|
+
if (tw) blocks.push(...emitTailwindOverrides(true).map((l) => `${l}`));
|
|
1992
|
+
blocks.push(`}`);
|
|
1993
|
+
blocks.push(`}`);
|
|
1994
|
+
}
|
|
1995
|
+
return blocks.join("\n");
|
|
2230
1996
|
}
|
|
2231
1997
|
//#endregion
|
|
2232
|
-
//#region
|
|
1998
|
+
//#region ../core/src/theme/defaults.ts
|
|
1999
|
+
const DEFAULT_FONT_SIZES = {
|
|
2000
|
+
extraSmall: "0.75rem",
|
|
2001
|
+
small: "0.875rem",
|
|
2002
|
+
regular: "1rem",
|
|
2003
|
+
large: "1.125rem",
|
|
2004
|
+
extraLarge: "1.25rem",
|
|
2005
|
+
giant: "1.5rem"
|
|
2006
|
+
};
|
|
2007
|
+
const DEFAULT_FONT_FAMILIES = {
|
|
2008
|
+
header: "Inter",
|
|
2009
|
+
body: "Inter"
|
|
2010
|
+
};
|
|
2011
|
+
const DEFAULT_SPACING = "0.25rem";
|
|
2012
|
+
const DEFAULT_RADII = {
|
|
2013
|
+
small: "0.25rem",
|
|
2014
|
+
medium: "0.5rem",
|
|
2015
|
+
large: "0.75rem",
|
|
2016
|
+
extraLarge: "1rem"
|
|
2017
|
+
};
|
|
2018
|
+
const DEFAULT_COLORS = {
|
|
2019
|
+
background: "#ffffff",
|
|
2020
|
+
foreground: "#1a1a1a",
|
|
2021
|
+
primary: "#3b82f6",
|
|
2022
|
+
secondary: "#6b7280",
|
|
2023
|
+
accent: "#10b981",
|
|
2024
|
+
muted: "#f3f4f6",
|
|
2025
|
+
destructive: "#ef4444",
|
|
2026
|
+
mutedForeground: "#6b7280"
|
|
2027
|
+
};
|
|
2028
|
+
const DEFAULT_THEME_ID = "default";
|
|
2029
|
+
const DEFAULT_THEME_NAME = "Default Theme";
|
|
2233
2030
|
/**
|
|
2234
|
-
*
|
|
2031
|
+
* Build a fresh ThemeDefinition populated with all defaults.
|
|
2032
|
+
* Returns a new object each call because Color instances are mutable — do not cache the result.
|
|
2235
2033
|
*/
|
|
2236
|
-
function
|
|
2034
|
+
function getDefaultThemeDefinition() {
|
|
2035
|
+
const bg = new Color(DEFAULT_COLORS.background);
|
|
2036
|
+
const fg = new Color(DEFAULT_COLORS.foreground);
|
|
2037
|
+
const primary = new Color(DEFAULT_COLORS.primary);
|
|
2038
|
+
const secondary = new Color(DEFAULT_COLORS.secondary);
|
|
2039
|
+
const accent = new Color(DEFAULT_COLORS.accent);
|
|
2040
|
+
const muted = new Color(DEFAULT_COLORS.muted);
|
|
2041
|
+
const destructive = new Color(DEFAULT_COLORS.destructive);
|
|
2042
|
+
const mutedFg = new Color(DEFAULT_COLORS.mutedForeground);
|
|
2043
|
+
const darkBg = new Color("#0a0a0a");
|
|
2044
|
+
const darkFg = new Color("#fafafa");
|
|
2045
|
+
const darkMuted = new Color("#171717");
|
|
2046
|
+
const darkMutedForeground = new Color("#dddddd");
|
|
2237
2047
|
return {
|
|
2238
|
-
|
|
2239
|
-
name:
|
|
2048
|
+
id: DEFAULT_THEME_ID,
|
|
2049
|
+
name: DEFAULT_THEME_NAME,
|
|
2050
|
+
light: {
|
|
2051
|
+
background: {
|
|
2052
|
+
base: bg,
|
|
2053
|
+
foreground: fg
|
|
2054
|
+
},
|
|
2055
|
+
foreground: {
|
|
2056
|
+
base: fg,
|
|
2057
|
+
foreground: bg
|
|
2058
|
+
},
|
|
2059
|
+
primary: {
|
|
2060
|
+
base: primary,
|
|
2061
|
+
foreground: getForegroundColor(fg, primary)
|
|
2062
|
+
},
|
|
2063
|
+
secondary: {
|
|
2064
|
+
base: secondary,
|
|
2065
|
+
foreground: getForegroundColor(fg, secondary)
|
|
2066
|
+
},
|
|
2067
|
+
accent: {
|
|
2068
|
+
base: accent,
|
|
2069
|
+
foreground: getForegroundColor(fg, accent)
|
|
2070
|
+
},
|
|
2071
|
+
muted: {
|
|
2072
|
+
base: muted,
|
|
2073
|
+
foreground: mutedFg
|
|
2074
|
+
},
|
|
2075
|
+
destructive: {
|
|
2076
|
+
base: destructive,
|
|
2077
|
+
foreground: getForegroundColor(fg, destructive)
|
|
2078
|
+
}
|
|
2079
|
+
},
|
|
2080
|
+
dark: {
|
|
2081
|
+
background: {
|
|
2082
|
+
base: darkBg,
|
|
2083
|
+
foreground: darkFg
|
|
2084
|
+
},
|
|
2085
|
+
foreground: {
|
|
2086
|
+
base: darkFg,
|
|
2087
|
+
foreground: darkBg
|
|
2088
|
+
},
|
|
2089
|
+
muted: {
|
|
2090
|
+
base: darkMuted,
|
|
2091
|
+
foreground: darkMutedForeground
|
|
2092
|
+
}
|
|
2093
|
+
},
|
|
2094
|
+
fontSizes: { ...DEFAULT_FONT_SIZES },
|
|
2095
|
+
fontFamilies: { ...DEFAULT_FONT_FAMILIES },
|
|
2096
|
+
spacing: DEFAULT_SPACING,
|
|
2097
|
+
radii: { ...DEFAULT_RADII }
|
|
2098
|
+
};
|
|
2099
|
+
}
|
|
2100
|
+
//#endregion
|
|
2101
|
+
//#region ../core/src/theme/serialisation.ts
|
|
2102
|
+
function colorToPlain(color) {
|
|
2103
|
+
return {
|
|
2104
|
+
l: color.oklch.l ?? 0,
|
|
2105
|
+
c: color.oklch.c ?? 0,
|
|
2106
|
+
h: color.oklch.h ?? 0
|
|
2240
2107
|
};
|
|
2241
2108
|
}
|
|
2109
|
+
function plainToColor(plain) {
|
|
2110
|
+
return new Color("oklch", [
|
|
2111
|
+
plain.l,
|
|
2112
|
+
plain.c,
|
|
2113
|
+
plain.h
|
|
2114
|
+
]);
|
|
2115
|
+
}
|
|
2242
2116
|
/**
|
|
2243
|
-
*
|
|
2117
|
+
* Serialise a ThemeDefinition (with Color objects) to a plain JSON payload
|
|
2118
|
+
* suitable for backend storage.
|
|
2244
2119
|
*/
|
|
2245
|
-
function
|
|
2120
|
+
function serialiseTheme(def) {
|
|
2121
|
+
const light = {};
|
|
2122
|
+
for (const name of SEMANTIC_COLOR_NAMES) light[name] = {
|
|
2123
|
+
base: colorToPlain(def.light[name].base),
|
|
2124
|
+
foreground: colorToPlain(def.light[name].foreground)
|
|
2125
|
+
};
|
|
2126
|
+
const dark = {};
|
|
2127
|
+
for (const [name, value] of Object.entries(def.dark)) {
|
|
2128
|
+
if (!value) continue;
|
|
2129
|
+
dark[name] = {
|
|
2130
|
+
...value.base ? { base: colorToPlain(value.base) } : {},
|
|
2131
|
+
...value.foreground ? { foreground: colorToPlain(value.foreground) } : {}
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
2246
2134
|
return {
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2135
|
+
id: def.id,
|
|
2136
|
+
name: def.name,
|
|
2137
|
+
light,
|
|
2138
|
+
dark,
|
|
2139
|
+
fontSizes: { ...def.fontSizes },
|
|
2140
|
+
fontFamilies: { ...def.fontFamilies },
|
|
2141
|
+
spacing: def.spacing,
|
|
2142
|
+
radii: { ...def.radii },
|
|
2143
|
+
...def.syncWithBrandColors ? { syncWithBrandColors: true } : {}
|
|
2255
2144
|
};
|
|
2256
2145
|
}
|
|
2257
2146
|
/**
|
|
2258
|
-
*
|
|
2259
|
-
*
|
|
2260
|
-
*
|
|
2261
|
-
* LanguagesApi port, closing over the FetchClient so consumers don't need
|
|
2262
|
-
* to pass it per-call.
|
|
2147
|
+
* Deserialise a backend payload into a ThemeDefinition with Color objects.
|
|
2148
|
+
* Accepts `Record<string, unknown>` because API data is untyped at the boundary.
|
|
2149
|
+
* Falls back to default colors for any missing light-mode entries.
|
|
2263
2150
|
*/
|
|
2264
|
-
function
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2151
|
+
function deserialiseTheme(payload) {
|
|
2152
|
+
const lightRaw = payload.light ?? {};
|
|
2153
|
+
const darkRaw = payload.dark ?? {};
|
|
2154
|
+
const defaults = getDefaultThemeDefinition();
|
|
2155
|
+
const light = {};
|
|
2156
|
+
for (const name of SEMANTIC_COLOR_NAMES) {
|
|
2157
|
+
const entry = lightRaw[name];
|
|
2158
|
+
if (entry) light[name] = {
|
|
2159
|
+
base: plainToColor(entry.base),
|
|
2160
|
+
foreground: plainToColor(entry.foreground)
|
|
2273
2161
|
};
|
|
2274
|
-
|
|
2275
|
-
}
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
return createPortalTenantLanguagesAdapter(client);
|
|
2162
|
+
else {
|
|
2163
|
+
console.warn(`[theme] deserialiseTheme: missing light color "${name}", using default`);
|
|
2164
|
+
light[name] = defaults.light[name];
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
const dark = {};
|
|
2168
|
+
for (const [name, value] of Object.entries(darkRaw)) {
|
|
2169
|
+
if (!value) continue;
|
|
2170
|
+
dark[name] = {
|
|
2171
|
+
...value.base ? { base: plainToColor(value.base) } : {},
|
|
2172
|
+
...value.foreground ? { foreground: plainToColor(value.foreground) } : {}
|
|
2173
|
+
};
|
|
2174
|
+
}
|
|
2175
|
+
return {
|
|
2176
|
+
id: payload.id,
|
|
2177
|
+
name: payload.name,
|
|
2178
|
+
light,
|
|
2179
|
+
dark,
|
|
2180
|
+
fontSizes: payload.fontSizes ?? DEFAULT_FONT_SIZES,
|
|
2181
|
+
fontFamilies: payload.fontFamilies ?? DEFAULT_FONT_FAMILIES,
|
|
2182
|
+
spacing: payload.spacing ?? "0.25rem",
|
|
2183
|
+
radii: payload.radii ?? DEFAULT_RADII,
|
|
2184
|
+
...payload.syncWithBrandColors === true ? { syncWithBrandColors: true } : {}
|
|
2185
|
+
};
|
|
2299
2186
|
}
|
|
2300
2187
|
//#endregion
|
|
2301
|
-
//#region
|
|
2302
|
-
/**
|
|
2303
|
-
* Get MySite profile for the current user
|
|
2304
|
-
* Returns the member's MySite profile.
|
|
2305
|
-
*
|
|
2306
|
-
* @param client - Fetch client instance
|
|
2307
|
-
|
|
2308
|
-
*/
|
|
2309
|
-
async function mysite_profile_show(client) {
|
|
2310
|
-
return client.get(`/api/mysite/profile`);
|
|
2311
|
-
}
|
|
2312
|
-
/**
|
|
2313
|
-
* Update MySite profile
|
|
2314
|
-
* Updates the member's MySite profile fields.
|
|
2315
|
-
*
|
|
2316
|
-
* @param client - Fetch client instance
|
|
2317
|
-
* @param body - body
|
|
2318
|
-
*/
|
|
2319
|
-
async function mysite_profile_update(client, body) {
|
|
2320
|
-
return client.put(`/api/mysite/profile`, body);
|
|
2321
|
-
}
|
|
2322
|
-
/**
|
|
2323
|
-
* List available MySite themes
|
|
2324
|
-
* Returns available MySite themes.
|
|
2325
|
-
*
|
|
2326
|
-
* @param client - Fetch client instance
|
|
2327
|
-
* @param [params] - params
|
|
2328
|
-
*/
|
|
2329
|
-
async function mysite_themes_list(client, params) {
|
|
2330
|
-
return client.get(`/api/mysite/themes`, params);
|
|
2331
|
-
}
|
|
2332
|
-
/**
|
|
2333
|
-
* Update MySite settings
|
|
2334
|
-
* Updates the member's MySite settings.
|
|
2335
|
-
*
|
|
2336
|
-
* @param client - Fetch client instance
|
|
2337
|
-
* @param body - body
|
|
2338
|
-
*/
|
|
2339
|
-
async function mysite_settings_update(client, body) {
|
|
2340
|
-
return client.put(`/api/mysite/settings`, body);
|
|
2341
|
-
}
|
|
2342
|
-
/**
|
|
2343
|
-
* List MySite links for the current user
|
|
2344
|
-
* Returns the member's MySite links, ordered by position.
|
|
2345
|
-
*
|
|
2346
|
-
* @param client - Fetch client instance
|
|
2347
|
-
* @param [params] - params
|
|
2348
|
-
*/
|
|
2349
|
-
async function mysite_links_list(client, params) {
|
|
2350
|
-
return client.get(`/api/mysite/links`, params);
|
|
2351
|
-
}
|
|
2352
|
-
/**
|
|
2353
|
-
* Create a MySite link
|
|
2354
|
-
* Adds a new link to the member's MySite.
|
|
2355
|
-
*
|
|
2356
|
-
* @param client - Fetch client instance
|
|
2357
|
-
* @param body - body
|
|
2358
|
-
*/
|
|
2359
|
-
async function mysite_links_create(client, body) {
|
|
2360
|
-
return client.post(`/api/mysite/links`, body);
|
|
2361
|
-
}
|
|
2188
|
+
//#region ../core/src/theme/transforms.ts
|
|
2362
2189
|
/**
|
|
2363
|
-
*
|
|
2364
|
-
*
|
|
2365
|
-
*
|
|
2366
|
-
* @param client - Fetch client instance
|
|
2367
|
-
* @param id - id
|
|
2368
|
-
* @param body - body
|
|
2190
|
+
* Check if a theme config uses the new structured format (has a `light` key
|
|
2191
|
+
* that is an object) vs the legacy flat format.
|
|
2369
2192
|
*/
|
|
2370
|
-
|
|
2371
|
-
return
|
|
2193
|
+
function isNewThemeFormat(config) {
|
|
2194
|
+
return config.light != null && typeof config.light === "object";
|
|
2372
2195
|
}
|
|
2373
2196
|
/**
|
|
2374
|
-
*
|
|
2375
|
-
*
|
|
2376
|
-
*
|
|
2377
|
-
* @param client - Fetch client instance
|
|
2378
|
-
* @param id - id
|
|
2197
|
+
* Convert a legacy flat config to a ThemeDefinition.
|
|
2198
|
+
* Legacy format: { base: "#fff", text: "#000", primary: "oklch(0.6 0.2 250)", ... }
|
|
2379
2199
|
*/
|
|
2380
|
-
|
|
2381
|
-
|
|
2200
|
+
function legacyConfigToDefinition(id, name, config) {
|
|
2201
|
+
const bg = parseColor(config.base ?? config.background ?? DEFAULT_COLORS.background);
|
|
2202
|
+
const fg = parseColor(config.text ?? config.foreground ?? DEFAULT_COLORS.foreground);
|
|
2203
|
+
const primary = parseColor(config.primary ?? DEFAULT_COLORS.primary);
|
|
2204
|
+
const secondary = parseColor(config.secondary ?? DEFAULT_COLORS.secondary);
|
|
2205
|
+
const accent = parseColor(config.accent ?? DEFAULT_COLORS.accent);
|
|
2206
|
+
const muted = parseColor(config.muted ?? DEFAULT_COLORS.muted);
|
|
2207
|
+
const destructive = parseColor(config.destructive ?? DEFAULT_COLORS.destructive);
|
|
2208
|
+
const mutedFg = parseColor(config.mutedForeground ?? DEFAULT_COLORS.mutedForeground);
|
|
2209
|
+
return {
|
|
2210
|
+
id: String(id),
|
|
2211
|
+
name,
|
|
2212
|
+
light: {
|
|
2213
|
+
background: {
|
|
2214
|
+
base: bg,
|
|
2215
|
+
foreground: fg
|
|
2216
|
+
},
|
|
2217
|
+
foreground: {
|
|
2218
|
+
base: fg,
|
|
2219
|
+
foreground: bg
|
|
2220
|
+
},
|
|
2221
|
+
primary: {
|
|
2222
|
+
base: primary,
|
|
2223
|
+
foreground: getForegroundColor(fg, primary)
|
|
2224
|
+
},
|
|
2225
|
+
secondary: {
|
|
2226
|
+
base: secondary,
|
|
2227
|
+
foreground: getForegroundColor(fg, secondary)
|
|
2228
|
+
},
|
|
2229
|
+
accent: {
|
|
2230
|
+
base: accent,
|
|
2231
|
+
foreground: getForegroundColor(fg, accent)
|
|
2232
|
+
},
|
|
2233
|
+
muted: {
|
|
2234
|
+
base: muted,
|
|
2235
|
+
foreground: mutedFg
|
|
2236
|
+
},
|
|
2237
|
+
destructive: {
|
|
2238
|
+
base: destructive,
|
|
2239
|
+
foreground: getForegroundColor(fg, destructive)
|
|
2240
|
+
}
|
|
2241
|
+
},
|
|
2242
|
+
dark: {},
|
|
2243
|
+
fontSizes: {
|
|
2244
|
+
extraSmall: config.extraSmall ?? DEFAULT_FONT_SIZES.extraSmall,
|
|
2245
|
+
small: config.small ?? DEFAULT_FONT_SIZES.small,
|
|
2246
|
+
regular: config.regular ?? DEFAULT_FONT_SIZES.regular,
|
|
2247
|
+
large: config.large ?? DEFAULT_FONT_SIZES.large,
|
|
2248
|
+
extraLarge: config.extraLarge ?? DEFAULT_FONT_SIZES.extraLarge,
|
|
2249
|
+
giant: config.giant ?? DEFAULT_FONT_SIZES.giant
|
|
2250
|
+
},
|
|
2251
|
+
fontFamilies: {
|
|
2252
|
+
header: config.headerFont ?? DEFAULT_FONT_FAMILIES.header,
|
|
2253
|
+
body: config.bodyFont ?? DEFAULT_FONT_FAMILIES.body
|
|
2254
|
+
},
|
|
2255
|
+
spacing: config.globalSpacing ?? "0.25rem",
|
|
2256
|
+
radii: {
|
|
2257
|
+
small: config.radiusSmall ?? DEFAULT_RADII.small,
|
|
2258
|
+
medium: config.radiusMedium ?? DEFAULT_RADII.medium,
|
|
2259
|
+
large: config.radiusLarge ?? DEFAULT_RADII.large,
|
|
2260
|
+
extraLarge: config.radiusExtraLarge ?? DEFAULT_RADII.extraLarge
|
|
2261
|
+
}
|
|
2262
|
+
};
|
|
2382
2263
|
}
|
|
2383
2264
|
/**
|
|
2384
|
-
*
|
|
2385
|
-
*
|
|
2386
|
-
*
|
|
2387
|
-
* @param client - Fetch client instance
|
|
2388
|
-
* @param body - body
|
|
2265
|
+
* Build a ThemeDefinition from a single API theme object.
|
|
2266
|
+
* Handles both new structured format and legacy flat format.
|
|
2389
2267
|
*/
|
|
2390
|
-
|
|
2391
|
-
|
|
2268
|
+
function buildThemeDefinition(theme) {
|
|
2269
|
+
const config = theme.config ?? {};
|
|
2270
|
+
if (isNewThemeFormat(config)) return deserialiseTheme({
|
|
2271
|
+
...config,
|
|
2272
|
+
id: String(theme.id),
|
|
2273
|
+
name: theme.name ?? "Untitled Theme"
|
|
2274
|
+
});
|
|
2275
|
+
return legacyConfigToDefinition(theme.id, theme.name ?? "Untitled Theme", config);
|
|
2392
2276
|
}
|
|
2393
2277
|
/**
|
|
2394
|
-
*
|
|
2395
|
-
*
|
|
2396
|
-
*
|
|
2397
|
-
* @param client - Fetch client instance
|
|
2398
|
-
* @param [params] - params
|
|
2278
|
+
* Transform raw API themes to ThemeDefinition[].
|
|
2279
|
+
* Catches and logs errors per theme (graceful degradation).
|
|
2399
2280
|
*/
|
|
2400
|
-
|
|
2401
|
-
return
|
|
2281
|
+
function transformThemes(themes) {
|
|
2282
|
+
return themes.flatMap((theme) => {
|
|
2283
|
+
try {
|
|
2284
|
+
return [buildThemeDefinition(theme)];
|
|
2285
|
+
} catch (error) {
|
|
2286
|
+
console.error(`[theme] Failed to build theme id=${theme.id}:`, error);
|
|
2287
|
+
return [];
|
|
2288
|
+
}
|
|
2289
|
+
});
|
|
2402
2290
|
}
|
|
2403
2291
|
/**
|
|
2404
|
-
*
|
|
2405
|
-
*
|
|
2406
|
-
*
|
|
2407
|
-
* @param client - Fetch client instance
|
|
2408
|
-
* @param body - body
|
|
2292
|
+
* Get the active theme ID from a list of raw API themes.
|
|
2293
|
+
* Falls back to the first theme if none is marked active.
|
|
2409
2294
|
*/
|
|
2410
|
-
|
|
2411
|
-
|
|
2295
|
+
function getActiveThemeId(themes) {
|
|
2296
|
+
const active = themes.find((t) => t.active) ?? themes[0];
|
|
2297
|
+
return active ? String(active.id) : void 0;
|
|
2412
2298
|
}
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2299
|
+
//#endregion
|
|
2300
|
+
//#region ../core/src/theme/theme-applicator.ts
|
|
2301
|
+
const STYLE_PREFIX = "theme-style-";
|
|
2302
|
+
const FONT_LINK_PREFIX = "theme-font-";
|
|
2303
|
+
const SYSTEM_FONTS = new Set([
|
|
2304
|
+
"sans-serif",
|
|
2305
|
+
"serif",
|
|
2306
|
+
"monospace",
|
|
2307
|
+
"cursive",
|
|
2308
|
+
"fantasy",
|
|
2309
|
+
"system-ui",
|
|
2310
|
+
"ui-sans-serif",
|
|
2311
|
+
"ui-serif",
|
|
2312
|
+
"ui-monospace"
|
|
2313
|
+
]);
|
|
2314
|
+
/** Build a Google Fonts CSS2 URL for a given font family with all weights. */
|
|
2315
|
+
function buildGoogleFontUrl(family) {
|
|
2316
|
+
return `https://fonts.googleapis.com/css2?family=${encodeURIComponent(family).replace(/%20/g, "+")}:wght@100;200;300;400;500;600;700;800;900&display=swap`;
|
|
2317
|
+
}
|
|
2318
|
+
/** Check if a font family value needs to be loaded (i.e. is not a CSS var or system font). */
|
|
2319
|
+
function isLoadableFont(value) {
|
|
2320
|
+
if (!value) return false;
|
|
2321
|
+
if (value.startsWith("var(")) return false;
|
|
2322
|
+
return !SYSTEM_FONTS.has(value.toLowerCase());
|
|
2323
|
+
}
|
|
2324
|
+
/** Deterministic link element ID for a font family. */
|
|
2325
|
+
function fontLinkId(family) {
|
|
2326
|
+
return `${FONT_LINK_PREFIX}${family.replace(/\s+/g, "-").toLowerCase()}`;
|
|
2422
2327
|
}
|
|
2423
2328
|
/**
|
|
2424
|
-
*
|
|
2425
|
-
*
|
|
2426
|
-
*
|
|
2427
|
-
* @param client - Fetch client instance
|
|
2428
|
-
* @param body - body
|
|
2329
|
+
* Inject or update `<link>` elements for Google Fonts used by the theme.
|
|
2330
|
+
* Removes links for fonts that are no longer referenced.
|
|
2429
2331
|
*/
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2332
|
+
function loadThemeFonts(theme) {
|
|
2333
|
+
if (typeof document === "undefined") return;
|
|
2334
|
+
const fontsToLoad = /* @__PURE__ */ new Set();
|
|
2335
|
+
for (const key of FONT_FAMILY_KEYS) {
|
|
2336
|
+
const value = theme.fontFamilies[key];
|
|
2337
|
+
if (isLoadableFont(value)) fontsToLoad.add(value);
|
|
2338
|
+
}
|
|
2339
|
+
document.querySelectorAll(`link[id^="${FONT_LINK_PREFIX}"]`).forEach((link) => {
|
|
2340
|
+
const owners = link.getAttribute("data-font-theme-ids")?.split(",") ?? [];
|
|
2341
|
+
if (!owners.includes(theme.id)) return;
|
|
2342
|
+
const fontName = link.getAttribute("data-font-family");
|
|
2343
|
+
if (fontName && !fontsToLoad.has(fontName)) {
|
|
2344
|
+
const remaining = owners.filter((id) => id !== theme.id);
|
|
2345
|
+
if (remaining.length === 0) link.remove();
|
|
2346
|
+
else link.setAttribute("data-font-theme-ids", remaining.join(","));
|
|
2347
|
+
}
|
|
2348
|
+
});
|
|
2349
|
+
for (const family of fontsToLoad) {
|
|
2350
|
+
const id = fontLinkId(family);
|
|
2351
|
+
const existing = document.getElementById(id);
|
|
2352
|
+
if (existing) {
|
|
2353
|
+
const owners = existing.getAttribute("data-font-theme-ids")?.split(",") ?? [];
|
|
2354
|
+
if (!owners.includes(theme.id)) existing.setAttribute("data-font-theme-ids", [...owners, theme.id].join(","));
|
|
2355
|
+
} else {
|
|
2356
|
+
const link = document.createElement("link");
|
|
2357
|
+
link.id = id;
|
|
2358
|
+
link.rel = "stylesheet";
|
|
2359
|
+
link.href = buildGoogleFontUrl(family);
|
|
2360
|
+
link.setAttribute("data-font-family", family);
|
|
2361
|
+
link.setAttribute("data-font-theme-ids", theme.id);
|
|
2362
|
+
document.head.appendChild(link);
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2445
2365
|
}
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
title: raw.title ?? "",
|
|
2451
|
-
position: raw.position ?? 0
|
|
2452
|
-
};
|
|
2366
|
+
/** Remove all font `<link>` elements injected by the theme system. */
|
|
2367
|
+
function removeAllFontLinks() {
|
|
2368
|
+
if (typeof document === "undefined") return;
|
|
2369
|
+
document.querySelectorAll(`link[id^="${FONT_LINK_PREFIX}"]`).forEach((el) => el.remove());
|
|
2453
2370
|
}
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2371
|
+
/**
|
|
2372
|
+
* Inject or update a `<style>` element in `<head>` for the given theme.
|
|
2373
|
+
* The element ID is deterministic (`theme-style-{themeId}`) so repeated calls
|
|
2374
|
+
* for the same theme are idempotent — the existing element is updated in place.
|
|
2375
|
+
* Also loads Google Fonts referenced by the theme's font families.
|
|
2376
|
+
* No-op when `document` is unavailable (SSR).
|
|
2377
|
+
*/
|
|
2378
|
+
function applyTheme(theme, options) {
|
|
2379
|
+
if (typeof document === "undefined") return;
|
|
2380
|
+
try {
|
|
2381
|
+
loadThemeFonts(theme);
|
|
2382
|
+
const styleId = `${STYLE_PREFIX}${theme.id}`;
|
|
2383
|
+
let el = document.getElementById(styleId);
|
|
2384
|
+
if (!el) {
|
|
2385
|
+
el = document.createElement("style");
|
|
2386
|
+
el.id = styleId;
|
|
2387
|
+
document.head.appendChild(el);
|
|
2388
|
+
}
|
|
2389
|
+
el.textContent = generateThemeCSS(theme, options);
|
|
2390
|
+
} catch (error) {
|
|
2391
|
+
console.error(`[theme] applyTheme failed for "${theme.id}":`, error);
|
|
2392
|
+
}
|
|
2463
2393
|
}
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2394
|
+
/** Remove an injected theme stylesheet and clean up font link ownership. No-op during SSR. */
|
|
2395
|
+
function removeTheme(themeId) {
|
|
2396
|
+
if (typeof document === "undefined") return;
|
|
2397
|
+
document.getElementById(`${STYLE_PREFIX}${themeId}`)?.remove();
|
|
2398
|
+
document.querySelectorAll(`link[id^="${FONT_LINK_PREFIX}"]`).forEach((link) => {
|
|
2399
|
+
const remaining = (link.getAttribute("data-font-theme-ids")?.split(",") ?? []).filter((id) => id !== themeId);
|
|
2400
|
+
if (remaining.length === 0) link.remove();
|
|
2401
|
+
else link.setAttribute("data-font-theme-ids", remaining.join(","));
|
|
2402
|
+
});
|
|
2470
2403
|
}
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
updateProfile: async (body) => {
|
|
2477
|
-
return mapProfile((await mysite_profile_update(client, { profile: {
|
|
2478
|
-
display_name: body.display_name,
|
|
2479
|
-
bio: body.bio,
|
|
2480
|
-
avatar_url: body.avatar_url
|
|
2481
|
-
} })).profile ?? {});
|
|
2482
|
-
},
|
|
2483
|
-
updateSettings: async (body) => {
|
|
2484
|
-
await Promise.all([body.theme_id !== void 0 ? mysite_settings_update(client, { settings: { theme_id: body.theme_id } }) : Promise.resolve(), body.slug !== void 0 ? mysite_profile_update(client, { profile: { slug: body.slug } }) : Promise.resolve()]);
|
|
2485
|
-
},
|
|
2486
|
-
listLinks: async () => {
|
|
2487
|
-
return ((await mysite_links_list(client)).links ?? []).map(mapLink);
|
|
2488
|
-
},
|
|
2489
|
-
createLink: async (body) => {
|
|
2490
|
-
return mapLink((await mysite_links_create(client, { link: {
|
|
2491
|
-
title: body.title,
|
|
2492
|
-
url: body.url
|
|
2493
|
-
} })).link ?? {});
|
|
2494
|
-
},
|
|
2495
|
-
updateLink: async (linkId, body) => {
|
|
2496
|
-
return mapLink((await mysite_links_update(client, linkId, { link: {
|
|
2497
|
-
title: body.title,
|
|
2498
|
-
url: body.url
|
|
2499
|
-
} })).link ?? {});
|
|
2500
|
-
},
|
|
2501
|
-
deleteLink: async (linkId) => {
|
|
2502
|
-
await mysite_links_destroy(client, linkId);
|
|
2503
|
-
},
|
|
2504
|
-
reorderLinks: async (orderedIds) => {
|
|
2505
|
-
return ((await mysite_links_bulk_reorder(client, { ordered_ids: orderedIds })).links ?? []).map(mapLink);
|
|
2506
|
-
},
|
|
2507
|
-
listFavorites: async () => {
|
|
2508
|
-
return ((await mysite_favorites_list(client)).favorites ?? []).map(mapFavorite);
|
|
2509
|
-
},
|
|
2510
|
-
addFavorite: async (body) => {
|
|
2511
|
-
return mapFavorite((await mysite_favorites_create(client, { favorite: { product_id: body.product_id } })).favorite ?? {});
|
|
2512
|
-
},
|
|
2513
|
-
deleteFavorite: async (favoriteId) => {
|
|
2514
|
-
await mysite_favorites_destroy(client, favoriteId);
|
|
2515
|
-
},
|
|
2516
|
-
reorderFavorites: async (orderedIds) => {
|
|
2517
|
-
return ((await mysite_favorites_bulk_reorder(client, { ordered_ids: orderedIds })).favorites ?? []).map(mapFavorite);
|
|
2518
|
-
},
|
|
2519
|
-
listThemes: async () => {
|
|
2520
|
-
return ((await mysite_themes_list(client)).themes ?? []).map(mapTheme);
|
|
2521
|
-
}
|
|
2522
|
-
};
|
|
2404
|
+
/** Remove all injected theme stylesheets and font links. No-op during SSR. */
|
|
2405
|
+
function removeAllThemes() {
|
|
2406
|
+
if (typeof document === "undefined") return;
|
|
2407
|
+
document.querySelectorAll(`style[id^="${STYLE_PREFIX}"]`).forEach((el) => el.remove());
|
|
2408
|
+
removeAllFontLinks();
|
|
2523
2409
|
}
|
|
2524
2410
|
//#endregion
|
|
2525
2411
|
//#region src/providers/FluidThemeProvider.tsx
|
|
@@ -2812,6 +2698,6 @@ function useFluidContext() {
|
|
|
2812
2698
|
return context;
|
|
2813
2699
|
}
|
|
2814
2700
|
//#endregion
|
|
2815
|
-
export {
|
|
2701
|
+
export { mergeDarkOverrides as A, ApiError$1 as B, DEFAULT_THEME_ID as C, deriveDarkVariant as D, generateThemeCSS as E, RADIUS_KEYS as F, createPersister as G, usePayApi as H, SEMANTIC_COLOR_NAMES as I, deleteDatabase as K, 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, useAppDefinitionApi as U, useLanguagesApi as V, useFluidOsApi as W, 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, isApiError as z };
|
|
2816
2702
|
|
|
2817
|
-
//# sourceMappingURL=FluidProvider-
|
|
2703
|
+
//# sourceMappingURL=FluidProvider-B00jTGTH.mjs.map
|