@lovalingo/lovalingo 0.3.2 → 0.3.3

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.
@@ -9,6 +9,7 @@ import { processPath } from '../utils/pathNormalizer';
9
9
  import { LanguageSwitcher } from './LanguageSwitcher';
10
10
  const LOCALE_STORAGE_KEY = 'Lovalingo_locale';
11
11
  const LOADING_BG_STORAGE_PREFIX = "Lovalingo_loading_bg_color";
12
+ const BRANDING_STORAGE_PREFIX = "Lovalingo_branding_enabled";
12
13
  const CRITICAL_CACHE_PREFIX = "Lovalingo_critical_v0_3";
13
14
  export const LovalingoProvider = ({ children, apiKey: apiKeyProp, publicAnonKey, defaultLocale, locales, apiBase = 'https://leuskvkajliuzalrlwhw.supabase.co', routing = 'query', // Default to query mode (backward compatible)
14
15
  autoPrefixLinks = true, autoApplyRules = true, switcherPosition = 'bottom-right', switcherOffsetY = 20, switcherTheme = 'dark', editMode: initialEditMode = false, editKey = 'KeyE', pathNormalization = { enabled: true }, // Enable by default
@@ -80,6 +81,21 @@ navigateRef, // For path mode routing
80
81
  const exclusionsCacheRef = useRef(null);
81
82
  const domRulesCacheRef = useRef(new Map());
82
83
  const loadingBgStorageKey = `${LOADING_BG_STORAGE_PREFIX}:${resolvedApiKey || "anonymous"}`;
84
+ const brandingStorageKey = `${BRANDING_STORAGE_PREFIX}:${resolvedApiKey || "anonymous"}`;
85
+ const readBrandingCache = () => {
86
+ try {
87
+ const cached = (localStorage.getItem(brandingStorageKey) || "").trim();
88
+ if (cached === "0")
89
+ return false;
90
+ if (cached === "1")
91
+ return true;
92
+ }
93
+ catch {
94
+ // ignore
95
+ }
96
+ return true;
97
+ };
98
+ const [brandingEnabled, setBrandingEnabled] = useState(readBrandingCache);
83
99
  const prehideStateRef = useRef({
84
100
  active: false,
85
101
  timeoutId: null,
@@ -110,6 +126,18 @@ navigateRef, // For path mode routing
110
126
  // ignore
111
127
  }
112
128
  }, [loadingBgStorageKey]);
129
+ const setCachedBrandingEnabled = useCallback((enabled) => {
130
+ try {
131
+ localStorage.setItem(brandingStorageKey, enabled === false ? "0" : "1");
132
+ }
133
+ catch {
134
+ // ignore
135
+ }
136
+ }, [brandingStorageKey]);
137
+ useEffect(() => {
138
+ setBrandingEnabled(readBrandingCache());
139
+ // eslint-disable-next-line react-hooks/exhaustive-deps
140
+ }, [brandingStorageKey]);
113
141
  const enablePrehide = useCallback((bgColor) => {
114
142
  if (typeof document === "undefined")
115
143
  return;
@@ -306,11 +334,19 @@ navigateRef, // For path mode routing
306
334
  setEntitlements(bootstrap.entitlements);
307
335
  if (bootstrap?.loading_bg_color)
308
336
  setCachedLoadingBgColor(bootstrap.loading_bg_color);
337
+ if (bootstrap?.entitlements?.brandingRequired) {
338
+ setBrandingEnabled(true);
339
+ setCachedBrandingEnabled(true);
340
+ }
341
+ else if (typeof bootstrap?.branding_enabled === "boolean") {
342
+ setBrandingEnabled(bootstrap.branding_enabled);
343
+ setCachedBrandingEnabled(bootstrap.branding_enabled);
344
+ }
309
345
  })();
310
346
  return () => {
311
347
  cancelled = true;
312
348
  };
313
- }, [defaultLocale, entitlements, locale, setCachedLoadingBgColor]);
349
+ }, [defaultLocale, entitlements, locale, setCachedBrandingEnabled, setCachedLoadingBgColor]);
314
350
  const applySeoBundle = useCallback((bundle, hreflangEnabled) => {
315
351
  try {
316
352
  const head = document.head;
@@ -535,6 +571,14 @@ navigateRef, // For path mode routing
535
571
  setCachedLoadingBgColor(bootstrap.loading_bg_color);
536
572
  enablePrehide(bootstrap.loading_bg_color);
537
573
  }
574
+ if ((bootstrap?.entitlements || nextEntitlements)?.brandingRequired) {
575
+ setBrandingEnabled(true);
576
+ setCachedBrandingEnabled(true);
577
+ }
578
+ else if (typeof bootstrap?.branding_enabled === "boolean") {
579
+ setBrandingEnabled(bootstrap.branding_enabled);
580
+ setCachedBrandingEnabled(bootstrap.branding_enabled);
581
+ }
538
582
  const exclusions = Array.isArray(bootstrap?.exclusions)
539
583
  ? bootstrap.exclusions
540
584
  .map((row) => {
@@ -1032,7 +1076,9 @@ navigateRef, // For path mode routing
1032
1076
  };
1033
1077
  return (React.createElement(LovalingoContext.Provider, { value: contextValue },
1034
1078
  children,
1035
- React.createElement(LanguageSwitcher, { locales: allLocales, currentLocale: locale, onLocaleChange: setLocale, position: switcherPosition, offsetY: switcherOffsetY, theme: switcherTheme, branding: entitlements?.brandingRequired
1036
- ? { required: true, href: "https://lovalingo.com" }
1037
- : undefined })));
1079
+ React.createElement(LanguageSwitcher, { locales: allLocales, currentLocale: locale, onLocaleChange: setLocale, position: switcherPosition, offsetY: switcherOffsetY, theme: switcherTheme, branding: {
1080
+ required: Boolean(entitlements?.brandingRequired),
1081
+ enabled: brandingEnabled,
1082
+ href: "https://lovalingo.com",
1083
+ } })));
1038
1084
  };
@@ -8,6 +8,7 @@ interface LanguageSwitcherProps {
8
8
  theme?: 'dark' | 'light';
9
9
  branding?: {
10
10
  required?: boolean;
11
+ enabled?: boolean;
11
12
  label?: string;
12
13
  href?: string;
13
14
  };
@@ -205,7 +205,7 @@ export const LanguageSwitcher = ({ locales, currentLocale, onLocaleChange, posit
205
205
  e.currentTarget.style.filter = 'brightness(1)';
206
206
  e.currentTarget.style.transform = 'scale(1)';
207
207
  }, "aria-label": `Switch to ${locale.toUpperCase()}`, title: locale.toUpperCase(), tabIndex: isOpen ? 0 : -1 }, LANGUAGE_FLAGS[locale] || '🏳️')))),
208
- branding?.required && (React.createElement("div", { style: badgeRowStyles, "aria-label": "Lovalingo branding" },
208
+ (branding?.required || branding?.enabled) && (React.createElement("div", { style: badgeRowStyles, "aria-label": "Lovalingo branding" },
209
209
  React.createElement("a", { href: branding.href || 'https://lovalingo.com', target: "_blank", rel: "noreferrer", style: badgeLinkStyles, tabIndex: isOpen ? 0 : -1, "aria-label": "Localized by Lovalingo", title: "Localized by Lovalingo" },
210
210
  React.createElement("span", { style: {
211
211
  width: '16px',
@@ -26,6 +26,7 @@ export type BootstrapResponse = {
26
26
  normalized_path?: string;
27
27
  routing_strategy?: string;
28
28
  loading_bg_color?: string | null;
29
+ branding_enabled?: boolean;
29
30
  seoEnabled?: boolean;
30
31
  entitlements?: ProjectEntitlements;
31
32
  alternates?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lovalingo/lovalingo",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "React translation runtime with i18n routing, deterministic bundles + DOM rules, and zero-flash rendering.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",