funuicss 3.7.14 → 3.7.16
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/css/fun.css +665 -0
- package/index.d.ts +2 -0
- package/index.js +5 -1
- package/package.json +1 -1
- package/ui/feature/Feature.d.ts +130 -0
- package/ui/feature/Feature.js +380 -0
- package/ui/footer/Footer.d.ts +89 -0
- package/ui/footer/Footer.js +329 -0
- package/ui/icons/Dynamic.d.ts +12 -0
- package/ui/icons/Dynamic.js +163 -0
- package/ui/theme/theme.d.ts +1 -0
- package/ui/theme/theme.js +571 -23
- package/ui/vista/Vista.js +8 -12
- package/utils/componentUtils.d.ts +126 -19
- package/utils/componentUtils.js +994 -57
package/ui/theme/theme.js
CHANGED
|
@@ -280,7 +280,7 @@ exports.getAllVariables = getAllVariables;
|
|
|
280
280
|
/* COMPONENT */
|
|
281
281
|
/* -------------------------------------------------------------------------- */
|
|
282
282
|
var ThemeProvider = function (_a) {
|
|
283
|
-
var theme = _a.theme, children = _a.children, _b = _a.funcss, funcss = _b === void 0 ? '' : _b, _c = _a.minHeight, minHeight = _c === void 0 ? '100vh' : _c, projectId = _a.projectId;
|
|
283
|
+
var theme = _a.theme, children = _a.children, _b = _a.funcss, funcss = _b === void 0 ? '' : _b, _c = _a.minHeight, minHeight = _c === void 0 ? '100vh' : _c, projectId = _a.projectId, providedProject = _a.project;
|
|
284
284
|
var _d = (0, react_1.useState)('standard'), variant = _d[0], setVariant = _d[1];
|
|
285
285
|
var _e = (0, react_1.useState)({}), themeConfig = _e[0], setThemeConfig = _e[1];
|
|
286
286
|
var _f = (0, react_1.useState)(null), projectData = _f[0], setProjectData = _f[1];
|
|
@@ -323,20 +323,25 @@ var ThemeProvider = function (_a) {
|
|
|
323
323
|
return __generator(this, function (_a) {
|
|
324
324
|
switch (_a.label) {
|
|
325
325
|
case 0:
|
|
326
|
-
_a.trys.push([0,
|
|
326
|
+
_a.trys.push([0, 9, 10, 11]);
|
|
327
327
|
finalTheme = null;
|
|
328
328
|
finalVersion = null;
|
|
329
|
-
return [
|
|
330
|
-
|
|
329
|
+
if (!providedProject) return [3 /*break*/, 1];
|
|
330
|
+
console.log('✅ Using provided project data directly');
|
|
331
|
+
finalTheme = providedProject;
|
|
332
|
+
finalVersion = providedProject.version || 0;
|
|
333
|
+
return [3 /*break*/, 8];
|
|
334
|
+
case 1: return [4 /*yield*/, loadLocalTheme()];
|
|
335
|
+
case 2:
|
|
331
336
|
localTheme = _a.sent();
|
|
332
337
|
localVersion = (localTheme === null || localTheme === void 0 ? void 0 : localTheme.version) || 0;
|
|
333
|
-
if (!projectId) return [3 /*break*/,
|
|
338
|
+
if (!projectId) return [3 /*break*/, 7];
|
|
334
339
|
return [4 /*yield*/, validateOriginAccess(projectId)];
|
|
335
|
-
case
|
|
340
|
+
case 3:
|
|
336
341
|
hasAccess = _a.sent();
|
|
337
|
-
if (!hasAccess) return [3 /*break*/,
|
|
342
|
+
if (!hasAccess) return [3 /*break*/, 5];
|
|
338
343
|
return [4 /*yield*/, loadThemeFromCDN(projectId)];
|
|
339
|
-
case
|
|
344
|
+
case 4:
|
|
340
345
|
cdnTheme = _a.sent();
|
|
341
346
|
cdnVersion = (cdnTheme === null || cdnTheme === void 0 ? void 0 : cdnTheme.version) || 0;
|
|
342
347
|
if (cdnTheme) {
|
|
@@ -359,8 +364,8 @@ var ThemeProvider = function (_a) {
|
|
|
359
364
|
console.warn('⚠️ No theme found (CDN unavailable and no local theme)');
|
|
360
365
|
setError('Theme not found');
|
|
361
366
|
}
|
|
362
|
-
return [3 /*break*/,
|
|
363
|
-
case
|
|
367
|
+
return [3 /*break*/, 6];
|
|
368
|
+
case 5:
|
|
364
369
|
// Origin validation failed
|
|
365
370
|
if (localTheme) {
|
|
366
371
|
console.log('⚠️ Origin validation failed, using local theme');
|
|
@@ -371,9 +376,9 @@ var ThemeProvider = function (_a) {
|
|
|
371
376
|
console.error('❌ Origin validation failed and no local theme available');
|
|
372
377
|
setError('Access denied and no local theme available');
|
|
373
378
|
}
|
|
374
|
-
_a.label =
|
|
375
|
-
case
|
|
376
|
-
case
|
|
379
|
+
_a.label = 6;
|
|
380
|
+
case 6: return [3 /*break*/, 8];
|
|
381
|
+
case 7:
|
|
377
382
|
// No project ID provided - only use local theme
|
|
378
383
|
console.log('ℹ️ No project ID provided, using local theme only');
|
|
379
384
|
if (localTheme) {
|
|
@@ -385,8 +390,8 @@ var ThemeProvider = function (_a) {
|
|
|
385
390
|
console.log('ℹ️ No local theme file found - using base theme only');
|
|
386
391
|
// No error here - it's valid to use only base theme
|
|
387
392
|
}
|
|
388
|
-
_a.label =
|
|
389
|
-
case
|
|
393
|
+
_a.label = 8;
|
|
394
|
+
case 8:
|
|
390
395
|
// Apply the theme if we have one
|
|
391
396
|
if (finalTheme && (!currentVersion || finalVersion !== currentVersion)) {
|
|
392
397
|
applyThemeData(finalTheme, root);
|
|
@@ -396,24 +401,24 @@ var ThemeProvider = function (_a) {
|
|
|
396
401
|
else if (finalTheme) {
|
|
397
402
|
console.log('✓ Theme up to date');
|
|
398
403
|
}
|
|
399
|
-
return [3 /*break*/,
|
|
400
|
-
case
|
|
404
|
+
return [3 /*break*/, 11];
|
|
405
|
+
case 9:
|
|
401
406
|
err_1 = _a.sent();
|
|
402
407
|
console.error('❌ Error loading theme:', err_1);
|
|
403
408
|
setError('Failed to load theme');
|
|
404
|
-
return [3 /*break*/,
|
|
405
|
-
case
|
|
409
|
+
return [3 /*break*/, 11];
|
|
410
|
+
case 10:
|
|
406
411
|
setIsLoading(false);
|
|
407
412
|
setIsInitialLoad(false);
|
|
408
413
|
return [7 /*endfinally*/];
|
|
409
|
-
case
|
|
414
|
+
case 11: return [2 /*return*/];
|
|
410
415
|
}
|
|
411
416
|
});
|
|
412
417
|
}); };
|
|
413
418
|
// Initial load
|
|
414
419
|
loadTheme();
|
|
415
|
-
// Only poll for updates if we have a project ID
|
|
416
|
-
if (projectId) {
|
|
420
|
+
// Only poll for updates if we have a project ID AND no provided project
|
|
421
|
+
if (projectId && !providedProject) {
|
|
417
422
|
pollTimer = setInterval(function () {
|
|
418
423
|
loadTheme();
|
|
419
424
|
}, 5 * 60 * 1000);
|
|
@@ -423,7 +428,7 @@ var ThemeProvider = function (_a) {
|
|
|
423
428
|
clearInterval(pollTimer);
|
|
424
429
|
}
|
|
425
430
|
};
|
|
426
|
-
}, [projectId, currentVersion, theme]);
|
|
431
|
+
}, [projectId, currentVersion, theme, providedProject]); // Added providedProject to dependencies
|
|
427
432
|
var applyThemeData = function (data, root) {
|
|
428
433
|
var _a;
|
|
429
434
|
var themeConfig = (_a = data.theme_config) !== null && _a !== void 0 ? _a : {};
|
|
@@ -619,3 +624,546 @@ var useDocumentAssets = function () {
|
|
|
619
624
|
return (0, exports.useAssetsByType)('document');
|
|
620
625
|
};
|
|
621
626
|
exports.useDocumentAssets = useDocumentAssets;
|
|
627
|
+
// 'use client'
|
|
628
|
+
// import React, {
|
|
629
|
+
// useEffect,
|
|
630
|
+
// createContext,
|
|
631
|
+
// useContext,
|
|
632
|
+
// useState,
|
|
633
|
+
// ReactNode,
|
|
634
|
+
// useMemo,
|
|
635
|
+
// } from 'react'
|
|
636
|
+
// import { colorVarsToDarken, themes } from './themes'
|
|
637
|
+
// import { getDarkenAmount, darkenToRgba } from './darkenUtils'
|
|
638
|
+
// /* -------------------------------------------------------------------------- */
|
|
639
|
+
// /* TYPES */
|
|
640
|
+
// /* -------------------------------------------------------------------------- */
|
|
641
|
+
// export type ThemeVariant = 'standard' | 'minimal'
|
|
642
|
+
// export type ThemeName =
|
|
643
|
+
// | 'light'
|
|
644
|
+
// | 'dark'
|
|
645
|
+
// | 'dark-blue'
|
|
646
|
+
// | 'light-gray'
|
|
647
|
+
// | 'pastel-green'
|
|
648
|
+
// | 'warm-orange'
|
|
649
|
+
// | 'frosted-glass'
|
|
650
|
+
// | 'midnight-purple'
|
|
651
|
+
// | 'cyber-metal'
|
|
652
|
+
// interface ThemeConfig {
|
|
653
|
+
// [key: string]: any
|
|
654
|
+
// }
|
|
655
|
+
// interface Variable {
|
|
656
|
+
// name: string
|
|
657
|
+
// value: any
|
|
658
|
+
// }
|
|
659
|
+
// interface ProjectData {
|
|
660
|
+
// theme_config?: {
|
|
661
|
+
// colors?: Record<string, string>
|
|
662
|
+
// typography?: Record<string, string>
|
|
663
|
+
// [key: string]: any
|
|
664
|
+
// }
|
|
665
|
+
// components?: Record<string, any>
|
|
666
|
+
// default_variation?: ThemeVariant
|
|
667
|
+
// variables?: Variable[]
|
|
668
|
+
// assets?: Asset[]
|
|
669
|
+
// name?: string
|
|
670
|
+
// project_id?: string
|
|
671
|
+
// version?: number
|
|
672
|
+
// updated_at?: string
|
|
673
|
+
// trustedDomains?: Array<{
|
|
674
|
+
// domain: string
|
|
675
|
+
// status: string
|
|
676
|
+
// isDefault?: boolean
|
|
677
|
+
// }>
|
|
678
|
+
// }
|
|
679
|
+
// interface ThemeProviderProps {
|
|
680
|
+
// theme: ThemeName
|
|
681
|
+
// projectId?: string
|
|
682
|
+
// funcss?: string
|
|
683
|
+
// minHeight?: string
|
|
684
|
+
// children: ReactNode
|
|
685
|
+
// }
|
|
686
|
+
// /* -------------------------------------------------------------------------- */
|
|
687
|
+
// /* THEME CONTEXT */
|
|
688
|
+
// /* -------------------------------------------------------------------------- */
|
|
689
|
+
// interface ThemeContextType {
|
|
690
|
+
// variant: ThemeVariant
|
|
691
|
+
// setVariant: React.Dispatch<React.SetStateAction<ThemeVariant>>
|
|
692
|
+
// themeConfig: ThemeConfig
|
|
693
|
+
// projectData: ProjectData | null
|
|
694
|
+
// isLoading: boolean
|
|
695
|
+
// isInitialLoad: boolean
|
|
696
|
+
// error: string | null
|
|
697
|
+
// }
|
|
698
|
+
// const ThemeContext = createContext<ThemeContextType>({
|
|
699
|
+
// variant: 'standard',
|
|
700
|
+
// setVariant: () => {},
|
|
701
|
+
// themeConfig: {},
|
|
702
|
+
// projectData: null,
|
|
703
|
+
// isLoading: true,
|
|
704
|
+
// isInitialLoad: true,
|
|
705
|
+
// error: null,
|
|
706
|
+
// })
|
|
707
|
+
// export const useTheme = (): ThemeContextType => {
|
|
708
|
+
// const context = useContext(ThemeContext)
|
|
709
|
+
// if (!context) {
|
|
710
|
+
// throw new Error('useTheme must be used within ThemeProvider')
|
|
711
|
+
// }
|
|
712
|
+
// return context
|
|
713
|
+
// }
|
|
714
|
+
// export const useVariant = () => {
|
|
715
|
+
// const { variant, setVariant } = useTheme()
|
|
716
|
+
// return { variant, setVariant }
|
|
717
|
+
// }
|
|
718
|
+
// /* -------------------------------------------------------------------------- */
|
|
719
|
+
// /* ORIGIN VALIDATION */
|
|
720
|
+
// /* -------------------------------------------------------------------------- */
|
|
721
|
+
// const getCurrentOrigin = (): string => {
|
|
722
|
+
// if (typeof window === 'undefined') return ''
|
|
723
|
+
// // For local development, return localhost
|
|
724
|
+
// if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
|
|
725
|
+
// return 'localhost'
|
|
726
|
+
// }
|
|
727
|
+
// // For production, return the domain without protocol and www
|
|
728
|
+
// let domain = window.location.hostname
|
|
729
|
+
// domain = domain.replace(/^www\./, '')
|
|
730
|
+
// return domain
|
|
731
|
+
// }
|
|
732
|
+
// const validateOriginAccess = async (projectId: string): Promise<boolean> => {
|
|
733
|
+
// if (!projectId) {
|
|
734
|
+
// console.error('❌ No project ID provided for origin validation')
|
|
735
|
+
// return false
|
|
736
|
+
// }
|
|
737
|
+
// const currentOrigin = getCurrentOrigin()
|
|
738
|
+
// console.log(`🔍 Validating origin access for: ${currentOrigin}`)
|
|
739
|
+
// try {
|
|
740
|
+
// // Load project data from CDN to check trusted domains
|
|
741
|
+
// const projectData = await loadThemeFromCDN(projectId)
|
|
742
|
+
// if (!projectData) {
|
|
743
|
+
// console.error('❌ Project not found or inaccessible')
|
|
744
|
+
// return false
|
|
745
|
+
// }
|
|
746
|
+
// const trustedDomains = projectData.trustedDomains || []
|
|
747
|
+
// // Check if current origin is in trusted domains
|
|
748
|
+
// const hasAccess = trustedDomains.some(domain => {
|
|
749
|
+
// const trustedDomain = domain.domain.toLowerCase()
|
|
750
|
+
// const currentDomain = currentOrigin.toLowerCase()
|
|
751
|
+
// // Exact match or subdomain match
|
|
752
|
+
// return currentDomain === trustedDomain ||
|
|
753
|
+
// currentDomain.endsWith('.' + trustedDomain) ||
|
|
754
|
+
// (trustedDomain === 'localhost' && currentOrigin === 'localhost')
|
|
755
|
+
// })
|
|
756
|
+
// if (!hasAccess) {
|
|
757
|
+
// console.error(`❌ Access denied: Origin "${currentOrigin}" is not in trusted domains`)
|
|
758
|
+
// console.log('📋 Trusted domains:', trustedDomains.map(d => d.domain))
|
|
759
|
+
// return false
|
|
760
|
+
// }
|
|
761
|
+
// return true
|
|
762
|
+
// } catch (error) {
|
|
763
|
+
// console.error('❌ Error during origin validation:', error)
|
|
764
|
+
// return false
|
|
765
|
+
// }
|
|
766
|
+
// }
|
|
767
|
+
// /* -------------------------------------------------------------------------- */
|
|
768
|
+
// /* LOCAL FILE MANAGEMENT */
|
|
769
|
+
// /* -------------------------------------------------------------------------- */
|
|
770
|
+
// const loadLocalTheme = async (): Promise<ProjectData | null> => {
|
|
771
|
+
// try {
|
|
772
|
+
// const response = await fetch('/funui.json', {
|
|
773
|
+
// cache: 'no-cache',
|
|
774
|
+
// })
|
|
775
|
+
// if (response.ok) {
|
|
776
|
+
// const data = await response.json()
|
|
777
|
+
// return data
|
|
778
|
+
// }
|
|
779
|
+
// } catch (error) {
|
|
780
|
+
// console.log('ℹ️ No local theme file found')
|
|
781
|
+
// }
|
|
782
|
+
// return null
|
|
783
|
+
// }
|
|
784
|
+
// /* -------------------------------------------------------------------------- */
|
|
785
|
+
// /* CDN THEME LOADER */
|
|
786
|
+
// /* -------------------------------------------------------------------------- */
|
|
787
|
+
// const loadThemeFromCDN = async (projectId: string): Promise<ProjectData | null> => {
|
|
788
|
+
// if (!projectId) {
|
|
789
|
+
// console.error('❌ No project ID provided for CDN loading')
|
|
790
|
+
// return null
|
|
791
|
+
// }
|
|
792
|
+
// // Try Firebase Storage public URL
|
|
793
|
+
// try {
|
|
794
|
+
// const publicUrl = `https://firebasestorage.googleapis.com/v0/b/funui-4bcd1.firebasestorage.app/o/themes%2F${projectId}.json?alt=media`
|
|
795
|
+
// const response = await fetch(publicUrl, {
|
|
796
|
+
// cache: 'no-cache',
|
|
797
|
+
// })
|
|
798
|
+
// if (response.ok) {
|
|
799
|
+
// const data = await response.json()
|
|
800
|
+
// return data
|
|
801
|
+
// } else {
|
|
802
|
+
// console.error('❌ Firebase Storage fetch failed:', response.status, response.statusText)
|
|
803
|
+
// }
|
|
804
|
+
// } catch (error) {
|
|
805
|
+
// console.error('❌ Error loading from Firebase Storage:', error)
|
|
806
|
+
// }
|
|
807
|
+
// return null
|
|
808
|
+
// }
|
|
809
|
+
// /* -------------------------------------------------------------------------- */
|
|
810
|
+
// /* CSS VARIABLE APPLIER */
|
|
811
|
+
// /* -------------------------------------------------------------------------- */
|
|
812
|
+
// const applyTypographyVariables = (typography: Record<string, string>, root: HTMLElement) => {
|
|
813
|
+
// if (!typography) return
|
|
814
|
+
// Object.entries(typography).forEach(([key, value]) => {
|
|
815
|
+
// const cssVarName = `--${key.replace(/_/g, '-')}`
|
|
816
|
+
// root.style.setProperty(cssVarName, value)
|
|
817
|
+
// })
|
|
818
|
+
// }
|
|
819
|
+
// const applyColorVariables = (colors: Record<string, string>, root: HTMLElement) => {
|
|
820
|
+
// if (!colors) return
|
|
821
|
+
// Object.entries(colors).forEach(([key, value]) => {
|
|
822
|
+
// const cssVarName = `--${key.replace(/_/g, '-')}`
|
|
823
|
+
// root.style.setProperty(cssVarName, value)
|
|
824
|
+
// })
|
|
825
|
+
// }
|
|
826
|
+
// const applyThemeConfig = (themeConfig: Record<string, any>, root: HTMLElement) => {
|
|
827
|
+
// if (!themeConfig) return
|
|
828
|
+
// if (themeConfig.colors) {
|
|
829
|
+
// applyColorVariables(themeConfig.colors, root)
|
|
830
|
+
// }
|
|
831
|
+
// if (themeConfig.typography) {
|
|
832
|
+
// applyTypographyVariables(themeConfig.typography, root)
|
|
833
|
+
// }
|
|
834
|
+
// Object.entries(themeConfig).forEach(([key, value]) => {
|
|
835
|
+
// if (key !== 'colors' && key !== 'typography' && typeof value === 'string') {
|
|
836
|
+
// const cssVarName = `--${key.replace(/_/g, '-')}`
|
|
837
|
+
// root.style.setProperty(cssVarName, value)
|
|
838
|
+
// }
|
|
839
|
+
// })
|
|
840
|
+
// }
|
|
841
|
+
// /* -------------------------------------------------------------------------- */
|
|
842
|
+
// /* VARIABLES HELPER */
|
|
843
|
+
// /* -------------------------------------------------------------------------- */
|
|
844
|
+
// let cachedProjectData: ProjectData | null = null
|
|
845
|
+
// export const getVariable = (name: string): { name: string; value: any } | undefined => {
|
|
846
|
+
// if (!cachedProjectData?.variables) {
|
|
847
|
+
// console.warn('No variables available. Make sure ThemeProvider is mounted.')
|
|
848
|
+
// return undefined
|
|
849
|
+
// }
|
|
850
|
+
// const variable = cachedProjectData.variables.find(v => v.name === name)
|
|
851
|
+
// return variable
|
|
852
|
+
// }
|
|
853
|
+
// export const getAllVariables = (): Variable[] => {
|
|
854
|
+
// return cachedProjectData?.variables || []
|
|
855
|
+
// }
|
|
856
|
+
// /* -------------------------------------------------------------------------- */
|
|
857
|
+
// /* COMPONENT */
|
|
858
|
+
// /* -------------------------------------------------------------------------- */
|
|
859
|
+
// const ThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
860
|
+
// theme,
|
|
861
|
+
// children,
|
|
862
|
+
// funcss = '',
|
|
863
|
+
// minHeight = '100vh',
|
|
864
|
+
// projectId,
|
|
865
|
+
// }) => {
|
|
866
|
+
// const [variant, setVariant] = useState<ThemeVariant>('standard')
|
|
867
|
+
// const [themeConfig, setThemeConfig] = useState<ThemeConfig>({})
|
|
868
|
+
// const [projectData, setProjectData] = useState<ProjectData | null>(null)
|
|
869
|
+
// const [isLoading, setIsLoading] = useState(true)
|
|
870
|
+
// const [isInitialLoad, setIsInitialLoad] = useState(true)
|
|
871
|
+
// const [error, setError] = useState<string | null>(null)
|
|
872
|
+
// const [currentVersion, setCurrentVersion] = useState<number | null>(null)
|
|
873
|
+
// /* -------------------------- Apply base theme --------------------------- */
|
|
874
|
+
// useEffect(() => {
|
|
875
|
+
// const root = document.documentElement
|
|
876
|
+
// const selectedTheme = themes[theme] || themes.light
|
|
877
|
+
// Object.entries(selectedTheme).forEach(([key, value]) => {
|
|
878
|
+
// root.style.setProperty(key, value)
|
|
879
|
+
// })
|
|
880
|
+
// if (
|
|
881
|
+
// ['dark', 'dark-blue', 'midnight-purple', 'cyber-metal'].includes(theme)
|
|
882
|
+
// ) {
|
|
883
|
+
// colorVarsToDarken.forEach((varName) => {
|
|
884
|
+
// const original = getComputedStyle(root)
|
|
885
|
+
// .getPropertyValue(varName)
|
|
886
|
+
// .trim()
|
|
887
|
+
// if (original) {
|
|
888
|
+
// const darkAmount = getDarkenAmount(varName)
|
|
889
|
+
// const rgba = darkenToRgba(original, darkAmount, 0.9)
|
|
890
|
+
// root.style.setProperty(varName, rgba)
|
|
891
|
+
// }
|
|
892
|
+
// })
|
|
893
|
+
// }
|
|
894
|
+
// }, [theme])
|
|
895
|
+
// /* ---------------------- Theme Loading Logic ----------------------- */
|
|
896
|
+
// useEffect(() => {
|
|
897
|
+
// if (typeof window === 'undefined') {
|
|
898
|
+
// setIsLoading(false)
|
|
899
|
+
// setIsInitialLoad(false)
|
|
900
|
+
// return
|
|
901
|
+
// }
|
|
902
|
+
// const root = document.documentElement
|
|
903
|
+
// let pollTimer: NodeJS.Timeout
|
|
904
|
+
// const loadTheme = async () => {
|
|
905
|
+
// try {
|
|
906
|
+
// let finalTheme: ProjectData | null = null
|
|
907
|
+
// let finalVersion: number | null = null
|
|
908
|
+
// // First, try to load local theme
|
|
909
|
+
// const localTheme = await loadLocalTheme()
|
|
910
|
+
// const localVersion = localTheme?.version || 0
|
|
911
|
+
// if (projectId) {
|
|
912
|
+
// // Validate origin access for CDN
|
|
913
|
+
// const hasAccess = await validateOriginAccess(projectId)
|
|
914
|
+
// if (hasAccess) {
|
|
915
|
+
// // Try to load from CDN
|
|
916
|
+
// const cdnTheme = await loadThemeFromCDN(projectId)
|
|
917
|
+
// const cdnVersion = cdnTheme?.version || 0
|
|
918
|
+
// if (cdnTheme) {
|
|
919
|
+
// // CDN theme available - use it
|
|
920
|
+
// finalTheme = cdnTheme
|
|
921
|
+
// finalVersion = cdnVersion
|
|
922
|
+
// if (cdnVersion !== localVersion) {
|
|
923
|
+
// console.log(`🔄 Version mismatch: Local(${localVersion}) vs CDN(${cdnVersion})`)
|
|
924
|
+
// console.log('ℹ️ Using CDN version. Please update your local funui.json file manually.')
|
|
925
|
+
// }
|
|
926
|
+
// } else if (localTheme) {
|
|
927
|
+
// // CDN not available but we have local theme
|
|
928
|
+
// console.log('⚠️ CDN unavailable, using local theme')
|
|
929
|
+
// finalTheme = localTheme
|
|
930
|
+
// finalVersion = localVersion
|
|
931
|
+
// } else {
|
|
932
|
+
// // No theme available anywhere
|
|
933
|
+
// console.warn('⚠️ No theme found (CDN unavailable and no local theme)')
|
|
934
|
+
// setError('Theme not found')
|
|
935
|
+
// }
|
|
936
|
+
// } else {
|
|
937
|
+
// // Origin validation failed
|
|
938
|
+
// if (localTheme) {
|
|
939
|
+
// console.log('⚠️ Origin validation failed, using local theme')
|
|
940
|
+
// finalTheme = localTheme
|
|
941
|
+
// finalVersion = localVersion
|
|
942
|
+
// } else {
|
|
943
|
+
// console.error('❌ Origin validation failed and no local theme available')
|
|
944
|
+
// setError('Access denied and no local theme available')
|
|
945
|
+
// }
|
|
946
|
+
// }
|
|
947
|
+
// } else {
|
|
948
|
+
// // No project ID provided - only use local theme
|
|
949
|
+
// console.log('ℹ️ No project ID provided, using local theme only')
|
|
950
|
+
// if (localTheme) {
|
|
951
|
+
// finalTheme = localTheme
|
|
952
|
+
// finalVersion = localVersion
|
|
953
|
+
// console.log('✅ Theme loaded from local file')
|
|
954
|
+
// } else {
|
|
955
|
+
// console.log('ℹ️ No local theme file found - using base theme only')
|
|
956
|
+
// // No error here - it's valid to use only base theme
|
|
957
|
+
// }
|
|
958
|
+
// }
|
|
959
|
+
// // Apply the theme if we have one
|
|
960
|
+
// if (finalTheme && (!currentVersion || finalVersion !== currentVersion)) {
|
|
961
|
+
// applyThemeData(finalTheme, root)
|
|
962
|
+
// setCurrentVersion(finalVersion)
|
|
963
|
+
// setError(null)
|
|
964
|
+
// } else if (finalTheme) {
|
|
965
|
+
// console.log('✓ Theme up to date')
|
|
966
|
+
// }
|
|
967
|
+
// } catch (err) {
|
|
968
|
+
// console.error('❌ Error loading theme:', err)
|
|
969
|
+
// setError('Failed to load theme')
|
|
970
|
+
// } finally {
|
|
971
|
+
// setIsLoading(false)
|
|
972
|
+
// setIsInitialLoad(false)
|
|
973
|
+
// }
|
|
974
|
+
// }
|
|
975
|
+
// // Initial load
|
|
976
|
+
// loadTheme()
|
|
977
|
+
// // Only poll for updates if we have a project ID
|
|
978
|
+
// if (projectId) {
|
|
979
|
+
// pollTimer = setInterval(() => {
|
|
980
|
+
// loadTheme()
|
|
981
|
+
// }, 5 * 60 * 1000)
|
|
982
|
+
// }
|
|
983
|
+
// return () => {
|
|
984
|
+
// if (pollTimer) {
|
|
985
|
+
// clearInterval(pollTimer)
|
|
986
|
+
// }
|
|
987
|
+
// }
|
|
988
|
+
// }, [projectId, currentVersion, theme])
|
|
989
|
+
// const applyThemeData = (data: ProjectData, root: HTMLElement) => {
|
|
990
|
+
// const themeConfig = data.theme_config ?? {}
|
|
991
|
+
// const newVariant = data.default_variation || 'standard'
|
|
992
|
+
// setVariant(newVariant)
|
|
993
|
+
// setThemeConfig(themeConfig)
|
|
994
|
+
// setProjectData(data)
|
|
995
|
+
// // Cache for variable access
|
|
996
|
+
// cachedProjectData = data
|
|
997
|
+
// // Cache for asset access
|
|
998
|
+
// cachedAssets = data.assets || []
|
|
999
|
+
// // Apply all theme config to CSS variables
|
|
1000
|
+
// applyThemeConfig(themeConfig, root)
|
|
1001
|
+
// }
|
|
1002
|
+
// const contextValue = useMemo(
|
|
1003
|
+
// () => ({
|
|
1004
|
+
// variant,
|
|
1005
|
+
// setVariant,
|
|
1006
|
+
// themeConfig,
|
|
1007
|
+
// projectData,
|
|
1008
|
+
// isLoading,
|
|
1009
|
+
// isInitialLoad,
|
|
1010
|
+
// error,
|
|
1011
|
+
// }),
|
|
1012
|
+
// [variant, themeConfig, projectData, isLoading, isInitialLoad, error]
|
|
1013
|
+
// )
|
|
1014
|
+
// return (
|
|
1015
|
+
// <ThemeContext.Provider value={contextValue}>
|
|
1016
|
+
// <div
|
|
1017
|
+
// className={`theme-${theme} ${funcss}`}
|
|
1018
|
+
// style={{
|
|
1019
|
+
// backgroundColor: 'var(--page-bg)',
|
|
1020
|
+
// color: 'var(--text-color)',
|
|
1021
|
+
// minHeight: minHeight,
|
|
1022
|
+
// transition: isInitialLoad ? 'none' : 'background-color 0.3s ease, color 0.3s ease',
|
|
1023
|
+
// }}
|
|
1024
|
+
// >
|
|
1025
|
+
// {children}
|
|
1026
|
+
// </div>
|
|
1027
|
+
// </ThemeContext.Provider>
|
|
1028
|
+
// )
|
|
1029
|
+
// }
|
|
1030
|
+
// export default ThemeProvider
|
|
1031
|
+
// /* -------------------------------------------------------------------------- */
|
|
1032
|
+
// /* HELPER HOOKS */
|
|
1033
|
+
// /* -------------------------------------------------------------------------- */
|
|
1034
|
+
// export const useThemeValue = (key: string): string | undefined => {
|
|
1035
|
+
// const { themeConfig } = useTheme()
|
|
1036
|
+
// return themeConfig[key]
|
|
1037
|
+
// }
|
|
1038
|
+
// export const useComponentConfig = (componentName: string): any => {
|
|
1039
|
+
// const { projectData } = useTheme()
|
|
1040
|
+
// return projectData?.components?.[componentName] || {}
|
|
1041
|
+
// }
|
|
1042
|
+
// export const useColors = (): Record<string, string> => {
|
|
1043
|
+
// const { projectData } = useTheme()
|
|
1044
|
+
// return projectData?.theme_config?.colors || {}
|
|
1045
|
+
// }
|
|
1046
|
+
// export const useTypography = (): Record<string, string> => {
|
|
1047
|
+
// const { projectData } = useTheme()
|
|
1048
|
+
// return projectData?.theme_config?.typography || {}
|
|
1049
|
+
// }
|
|
1050
|
+
// export const useThemeConfig = (): Record<string, any> => {
|
|
1051
|
+
// const { projectData } = useTheme()
|
|
1052
|
+
// return projectData?.theme_config || {}
|
|
1053
|
+
// }
|
|
1054
|
+
// export const useProjectData = (): ProjectData | null => {
|
|
1055
|
+
// const { projectData } = useTheme()
|
|
1056
|
+
// return projectData
|
|
1057
|
+
// }
|
|
1058
|
+
// export const useColor = (colorName: string): string | undefined => {
|
|
1059
|
+
// const colors = useColors()
|
|
1060
|
+
// return colors[colorName]
|
|
1061
|
+
// }
|
|
1062
|
+
// export const useTypographyValue = (property: string): string | undefined => {
|
|
1063
|
+
// const typography = useTypography()
|
|
1064
|
+
// return typography[property]
|
|
1065
|
+
// }
|
|
1066
|
+
// export const useComponentVariant = (componentName: string, variantName: string = 'default'): any => {
|
|
1067
|
+
// const componentConfig = useComponentConfig(componentName)
|
|
1068
|
+
// return componentConfig[variantName] || {}
|
|
1069
|
+
// }
|
|
1070
|
+
// // Hook to access variables
|
|
1071
|
+
// export const useVariables = (): Variable[] => {
|
|
1072
|
+
// const { projectData } = useTheme()
|
|
1073
|
+
// return projectData?.variables || []
|
|
1074
|
+
// }
|
|
1075
|
+
// // Hook to get a specific variable
|
|
1076
|
+
// export const useVariable = (name: string): any => {
|
|
1077
|
+
// const variables = useVariables()
|
|
1078
|
+
// const variable = variables.find(v => v.name === name)
|
|
1079
|
+
// return variable?.value
|
|
1080
|
+
// }
|
|
1081
|
+
// /* -------------------------------------------------------------------------- */
|
|
1082
|
+
// /* ASSETS HELPER */
|
|
1083
|
+
// /* -------------------------------------------------------------------------- */
|
|
1084
|
+
// interface Asset {
|
|
1085
|
+
// name: string
|
|
1086
|
+
// url: string
|
|
1087
|
+
// file_type: string
|
|
1088
|
+
// }
|
|
1089
|
+
// let cachedAssets: Asset[] = []
|
|
1090
|
+
// export const getAsset = (name: string): Asset | undefined => {
|
|
1091
|
+
// if (!cachedAssets.length) {
|
|
1092
|
+
// console.warn('No assets available. Make sure ThemeProvider is mounted and assets are loaded.')
|
|
1093
|
+
// return undefined
|
|
1094
|
+
// }
|
|
1095
|
+
// const asset = cachedAssets.find(a => a.name === name)
|
|
1096
|
+
// return asset
|
|
1097
|
+
// }
|
|
1098
|
+
// export const getAllAssets = (): Asset[] => {
|
|
1099
|
+
// return cachedAssets
|
|
1100
|
+
// }
|
|
1101
|
+
// export const getAssetValue = (name: string): string | undefined => {
|
|
1102
|
+
// const asset = getAsset(name)
|
|
1103
|
+
// return asset?.url
|
|
1104
|
+
// }
|
|
1105
|
+
// export const getAssetType = (name: string): string | undefined => {
|
|
1106
|
+
// const asset = getAsset(name)
|
|
1107
|
+
// return asset?.file_type
|
|
1108
|
+
// }
|
|
1109
|
+
// export const getAssetInfo = (name: string): { value: string; type: string; name: string } | undefined => {
|
|
1110
|
+
// const asset = getAsset(name)
|
|
1111
|
+
// if (!asset) return undefined
|
|
1112
|
+
// return {
|
|
1113
|
+
// value: asset.url,
|
|
1114
|
+
// type: asset.file_type,
|
|
1115
|
+
// name: asset.name
|
|
1116
|
+
// }
|
|
1117
|
+
// }
|
|
1118
|
+
// // Hook to access all assets
|
|
1119
|
+
// export const useAssets = (): Asset[] => {
|
|
1120
|
+
// const { projectData } = useTheme()
|
|
1121
|
+
// return projectData?.assets || []
|
|
1122
|
+
// }
|
|
1123
|
+
// // Hook to get a specific asset
|
|
1124
|
+
// export const useAsset = (name: string): Asset | undefined => {
|
|
1125
|
+
// const assets = useAssets()
|
|
1126
|
+
// const asset = assets.find(a => a.name === name)
|
|
1127
|
+
// return asset
|
|
1128
|
+
// }
|
|
1129
|
+
// // Hook to get asset URL (most common use case)
|
|
1130
|
+
// export const useAssetValue = (name: string): string | undefined => {
|
|
1131
|
+
// const asset = useAsset(name)
|
|
1132
|
+
// return asset?.url
|
|
1133
|
+
// }
|
|
1134
|
+
// // Hook to get asset type
|
|
1135
|
+
// export const useAssetType = (name: string): string | undefined => {
|
|
1136
|
+
// const asset = useAsset(name)
|
|
1137
|
+
// return asset?.file_type
|
|
1138
|
+
// }
|
|
1139
|
+
// // Hook to get complete asset info
|
|
1140
|
+
// export const useAssetInfo = (name: string): { value: string; type: string; name: string } | undefined => {
|
|
1141
|
+
// const asset = useAsset(name)
|
|
1142
|
+
// if (!asset) return undefined
|
|
1143
|
+
// return {
|
|
1144
|
+
// value: asset.url,
|
|
1145
|
+
// type: asset.file_type,
|
|
1146
|
+
// name: asset.name
|
|
1147
|
+
// }
|
|
1148
|
+
// }
|
|
1149
|
+
// // Hook to filter assets by type
|
|
1150
|
+
// export const useAssetsByType = (type: string): Asset[] => {
|
|
1151
|
+
// const assets = useAssets()
|
|
1152
|
+
// return assets.filter(asset => asset.file_type === type)
|
|
1153
|
+
// }
|
|
1154
|
+
// // Hook to get image assets
|
|
1155
|
+
// export const useImageAssets = (): Asset[] => {
|
|
1156
|
+
// return useAssetsByType('image')
|
|
1157
|
+
// }
|
|
1158
|
+
// // Hook to get video assets
|
|
1159
|
+
// export const useVideoAssets = (): Asset[] => {
|
|
1160
|
+
// return useAssetsByType('video')
|
|
1161
|
+
// }
|
|
1162
|
+
// // Hook to get audio assets
|
|
1163
|
+
// export const useAudioAssets = (): Asset[] => {
|
|
1164
|
+
// return useAssetsByType('audio')
|
|
1165
|
+
// }
|
|
1166
|
+
// // Hook to get document assets
|
|
1167
|
+
// export const useDocumentAssets = (): Asset[] => {
|
|
1168
|
+
// return useAssetsByType('document')
|
|
1169
|
+
// }
|