@dashadmin/dash-utils 1.3.73 → 1.3.74-srcfree.2
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/dist/components/LazyAppLoader.d.ts +17 -0
- package/dist/components/MinimalLayout.d.ts +6 -0
- package/dist/components/RouterComponent.d.ts +22 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/contexts/IPCMessageBrokerContext.d.ts +42 -0
- package/dist/hooks/useCapacitorAppStateRefresh.d.ts +2 -0
- package/dist/hooks/useCapacitorKeyboard.d.ts +19 -0
- package/dist/hooks/useDeviceStorageSync.d.ts +4 -0
- package/dist/hooks/useDraggable.d.ts +24 -0
- package/dist/hooks/useMenuStateInit.d.ts +10 -0
- package/dist/hooks/useMobileDetection.d.ts +20 -0
- package/dist/hooks/useNotifications.d.ts +51 -0
- package/dist/hooks/useQuickSearch.d.ts +6 -0
- package/dist/hooks/useWindowSize.d.ts +6 -0
- package/dist/index.d.ts +30 -0
- package/dist/utils/authProviderUtils.d.ts +37 -0
- package/dist/utils/dashBootstrapApp.d.ts +9 -0
- package/dist/utils/dashDtorage.d.ts +8 -0
- package/dist/utils/dashIPCService.d.ts +93 -0
- package/dist/utils/dataProviderUtils.d.ts +44 -0
- package/dist/utils/electronStoreSync.d.ts +3 -0
- package/dist/utils/envUtils.d.ts +1 -0
- package/dist/utils/environmentBodyClasses.d.ts +11 -0
- package/dist/utils/isDev.d.ts +5 -0
- package/dist/utils/notificationPermission.d.ts +5 -0
- package/dist/utils/platformDetection.d.ts +13 -0
- package/dist/utils/priceFormatter.d.ts +2 -0
- package/dist/utils/resourceLoader.d.ts +8 -0
- package/dist/utils/speechService.d.ts +7 -0
- package/dist/utils/storedTheme.d.ts +11 -0
- package/dist/utils/updateDomCssVariables.d.ts +6 -0
- package/dist/utils/validation.d.ts +12 -0
- package/package.json +13 -10
- package/src/components/LazyAppLoader.tsx +0 -143
- package/src/components/MinimalLayout.tsx +0 -22
- package/src/components/RouterComponent.tsx +0 -169
- package/src/components/index.ts +0 -16
- package/src/contexts/IPCMessageBrokerContext.tsx +0 -303
- package/src/hooks/useCapacitorAppStateRefresh.ts +0 -34
- package/src/hooks/useCapacitorKeyboard.ts +0 -213
- package/src/hooks/useDeviceStorageSync.ts +0 -70
- package/src/hooks/useDraggable.ts +0 -133
- package/src/hooks/useMenuStateInit.ts +0 -85
- package/src/hooks/useMobileDetection.ts +0 -165
- package/src/hooks/useNotifications.ts +0 -220
- package/src/hooks/useQuickSearch.ts +0 -42
- package/src/hooks/useWindowSize.tsx +0 -36
- package/src/index.ts +0 -202
- package/src/utils/authProviderUtils.ts +0 -170
- package/src/utils/dashBootstrapApp.ts +0 -104
- package/src/utils/dashDtorage.tsx +0 -51
- package/src/utils/dashDtorage.tsx.old +0 -78
- package/src/utils/dashIPCService.ts +0 -204
- package/src/utils/dataProviderUtils.ts +0 -221
- package/src/utils/electronStoreSync.ts +0 -34
- package/src/utils/envUtils.tsx +0 -27
- package/src/utils/environmentBodyClasses.ts +0 -72
- package/src/utils/isDev.ts +0 -65
- package/src/utils/notificationPermission.ts +0 -57
- package/src/utils/platformDetection.ts +0 -98
- package/src/utils/priceFormatter.ts +0 -36
- package/src/utils/resourceLoader.ts +0 -107
- package/src/utils/speechService.ts +0 -112
- package/src/utils/storedTheme.ts +0 -60
- package/src/utils/updateDomCssVariables.tsx +0 -181
- package/src/utils/validation.ts +0 -147
package/src/utils/envUtils.tsx
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
const getEnvironmentVariable = (environmentVariable: string): string => {
|
|
2
|
-
/* @ts-ignore Expected access to process */
|
|
3
|
-
const unvalidatedEnvironmentVariable = process.env[environmentVariable];
|
|
4
|
-
/*if (!unvalidatedEnvironmentVariable) {
|
|
5
|
-
throw new Error(
|
|
6
|
-
`Couldn't find environment variable: ${environmentVariable}`,
|
|
7
|
-
);
|
|
8
|
-
} else {
|
|
9
|
-
return unvalidatedEnvironmentVariable;
|
|
10
|
-
}*/
|
|
11
|
-
return unvalidatedEnvironmentVariable || null;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export const getEnv = (key: string) => {
|
|
16
|
-
|
|
17
|
-
// @ts-ignore Expected access to process
|
|
18
|
-
const PREFIX = process.env.ENV_PREFIX || process.env.NEXT_PUBLIC_ENV_PREFIX || process.env.VITE_ENV_PREFIX || process.env.REACT_ENV_PREFIX || 'VITE_';
|
|
19
|
-
//console.log('GET ENV', PREFIX, key);
|
|
20
|
-
|
|
21
|
-
if (PREFIX === 'NEXT_PUBLIC_') {
|
|
22
|
-
return getEnvironmentVariable(PREFIX + key);
|
|
23
|
-
}
|
|
24
|
-
// @ts-ignore Expected access to process
|
|
25
|
-
return process.env[PREFIX + key] || null;
|
|
26
|
-
|
|
27
|
-
};
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Environment Body Classes Utility
|
|
3
|
-
*
|
|
4
|
-
* Manages body CSS classes based on the runtime environment.
|
|
5
|
-
* Useful for platform-specific styling (Electron, Mac, mobile, etc.)
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export interface EnvironmentVars {
|
|
9
|
-
PLATFORM_TYPE?: string;
|
|
10
|
-
IS_ELECTRON?: boolean;
|
|
11
|
-
IS_MAC?: boolean;
|
|
12
|
-
IS_WINDOWS?: boolean;
|
|
13
|
-
IS_LINUX?: boolean;
|
|
14
|
-
PLATFORM?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Add environment-based classes to the document body
|
|
19
|
-
* @param envVars - Environment variables object
|
|
20
|
-
* @returns Cleanup function to remove the classes
|
|
21
|
-
*/
|
|
22
|
-
export const setupEnvironmentBodyClasses = (envVars: EnvironmentVars): (() => void) => {
|
|
23
|
-
if (typeof document === 'undefined') {
|
|
24
|
-
return () => {}; // No-op for SSR
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const { PLATFORM_TYPE, IS_ELECTRON, IS_MAC, IS_WINDOWS, IS_LINUX } = envVars;
|
|
28
|
-
const classesToAdd: string[] = [];
|
|
29
|
-
|
|
30
|
-
if (PLATFORM_TYPE) {
|
|
31
|
-
document.body.classList.add(PLATFORM_TYPE);
|
|
32
|
-
classesToAdd.push(PLATFORM_TYPE);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (IS_ELECTRON) {
|
|
36
|
-
document.body.classList.add('electron');
|
|
37
|
-
classesToAdd.push('electron');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (IS_MAC) {
|
|
41
|
-
document.body.classList.add('mac');
|
|
42
|
-
classesToAdd.push('mac');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (IS_WINDOWS) {
|
|
46
|
-
document.body.classList.add('windows');
|
|
47
|
-
classesToAdd.push('windows');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (IS_LINUX) {
|
|
51
|
-
document.body.classList.add('linux');
|
|
52
|
-
classesToAdd.push('linux');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Return cleanup function
|
|
56
|
-
return () => {
|
|
57
|
-
classesToAdd.forEach(className => {
|
|
58
|
-
document.body.classList.remove(className);
|
|
59
|
-
});
|
|
60
|
-
};
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* React hook for environment body classes
|
|
65
|
-
* Use with useEffect to automatically clean up on unmount
|
|
66
|
-
*/
|
|
67
|
-
export const useEnvironmentBodyClasses = (envVars: EnvironmentVars): void => {
|
|
68
|
-
// Note: This is a simple helper. For actual use, wrap in useEffect in your component:
|
|
69
|
-
// useEffect(() => setupEnvironmentBodyClasses(envVars), [envVars]);
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export default setupEnvironmentBodyClasses;
|
package/src/utils/isDev.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Development Environment Detection Utility
|
|
3
|
-
*
|
|
4
|
-
* Provides functions to check if the application is running
|
|
5
|
-
* in a development environment.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// Safe check for Node.js process object
|
|
9
|
-
const getNodeEnv = (): string | undefined => {
|
|
10
|
-
try {
|
|
11
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
-
if (typeof globalThis !== 'undefined' && (globalThis as any).process?.env?.NODE_ENV) {
|
|
13
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
-
return (globalThis as any).process.env.NODE_ENV;
|
|
15
|
-
}
|
|
16
|
-
} catch {
|
|
17
|
-
// Ignore errors in environments where process is not available
|
|
18
|
-
}
|
|
19
|
-
return undefined;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Check if the application is running in development mode
|
|
24
|
-
* Detects localhost, 127.0.0.1, or development hostnames
|
|
25
|
-
*/
|
|
26
|
-
export const isDev = (): boolean => {
|
|
27
|
-
if (typeof window === 'undefined') {
|
|
28
|
-
// Node.js environment - check for NODE_ENV
|
|
29
|
-
return getNodeEnv() === 'development';
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const { hostname } = window.location;
|
|
33
|
-
return (
|
|
34
|
-
hostname === 'localhost' ||
|
|
35
|
-
hostname === '127.0.0.1' ||
|
|
36
|
-
hostname.startsWith('192.168.') ||
|
|
37
|
-
hostname.endsWith('.local') ||
|
|
38
|
-
hostname.includes('dev.')
|
|
39
|
-
);
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Check if running in production mode
|
|
44
|
-
*/
|
|
45
|
-
export const isProd = (): boolean => {
|
|
46
|
-
return !isDev();
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Check if running in test environment
|
|
51
|
-
*/
|
|
52
|
-
export const isTest = (): boolean => {
|
|
53
|
-
return getNodeEnv() === 'test';
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Get the current environment name
|
|
58
|
-
*/
|
|
59
|
-
export const getEnvironment = (): 'development' | 'production' | 'test' => {
|
|
60
|
-
if (isTest()) return 'test';
|
|
61
|
-
if (isDev()) return 'development';
|
|
62
|
-
return 'production';
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export default isDev;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Notification Permission Utilities
|
|
3
|
-
*
|
|
4
|
-
* Utilities for managing browser notification permissions.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Request browser notification permission
|
|
9
|
-
* Only requests if permission is in 'default' state (not yet decided)
|
|
10
|
-
*
|
|
11
|
-
* @returns Promise that resolves when permission is granted or rejected
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```tsx
|
|
15
|
-
* // Request permission on app startup
|
|
16
|
-
* await requestNotificationPermission();
|
|
17
|
-
*
|
|
18
|
-
* // Check if granted
|
|
19
|
-
* if (Notification.permission === 'granted') {
|
|
20
|
-
* new Notification('Hello!');
|
|
21
|
-
* }
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
export const requestNotificationPermission = async (): Promise<NotificationPermission | null> => {
|
|
25
|
-
if ('Notification' in window && Notification.permission === 'default') {
|
|
26
|
-
return await Notification.requestPermission();
|
|
27
|
-
}
|
|
28
|
-
return Notification.permission ?? null;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Check if notifications are supported in the current environment
|
|
33
|
-
*/
|
|
34
|
-
export const isNotificationSupported = (): boolean => {
|
|
35
|
-
return 'Notification' in window;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Check if notification permission has been granted
|
|
40
|
-
*/
|
|
41
|
-
export const hasNotificationPermission = (): boolean => {
|
|
42
|
-
return 'Notification' in window && Notification.permission === 'granted';
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Check if notification permission has been denied
|
|
47
|
-
*/
|
|
48
|
-
export const isNotificationDenied = (): boolean => {
|
|
49
|
-
return 'Notification' in window && Notification.permission === 'denied';
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Get current notification permission status
|
|
54
|
-
*/
|
|
55
|
-
export const getNotificationPermission = (): NotificationPermission | null => {
|
|
56
|
-
return 'Notification' in window ? Notification.permission : null;
|
|
57
|
-
};
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Platform Detection Utilities
|
|
3
|
-
*
|
|
4
|
-
* Functions to detect the current platform/environment.
|
|
5
|
-
* These are used to add appropriate body classes for platform-specific styling.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Check if running in a WebView
|
|
10
|
-
*/
|
|
11
|
-
export const isWebView = (): boolean => {
|
|
12
|
-
if (typeof navigator === 'undefined') return false;
|
|
13
|
-
|
|
14
|
-
const userAgent = navigator.userAgent.toLowerCase();
|
|
15
|
-
console.log('userAgent', userAgent);
|
|
16
|
-
return (/(webview|wv)/.test(userAgent) ||
|
|
17
|
-
/android.*(wv|.net)/.test(userAgent) || /iphone|ipod|ipad.*applewebkit(?!.*safari)/i.test(userAgent));
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Check if running on Android
|
|
22
|
-
*/
|
|
23
|
-
export const isAndroid = (): boolean => {
|
|
24
|
-
if (typeof navigator === 'undefined') return false;
|
|
25
|
-
return /android/i.test(navigator.userAgent.toLowerCase());
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Check if running on iOS
|
|
30
|
-
*/
|
|
31
|
-
export const isIOS = (): boolean => {
|
|
32
|
-
if (typeof navigator === 'undefined') return false;
|
|
33
|
-
return /iphone|ipad|ipod/i.test(navigator.userAgent.toLowerCase());
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Check if running on Windows
|
|
38
|
-
*/
|
|
39
|
-
export const isWindows = (): boolean => {
|
|
40
|
-
try {
|
|
41
|
-
// Check via globalThis to avoid process not defined errors
|
|
42
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
-
const globalProcess = (globalThis as any).process;
|
|
44
|
-
const platform = globalProcess?.platform;
|
|
45
|
-
return platform === 'win32' ||
|
|
46
|
-
(typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().includes('windows'));
|
|
47
|
-
} catch (error) {
|
|
48
|
-
console.error('Error checking Windows platform:', error);
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Apply platform-specific body classes
|
|
55
|
-
*/
|
|
56
|
-
export const applyPlatformBodyClasses = (): void => {
|
|
57
|
-
if (typeof document === 'undefined') return;
|
|
58
|
-
|
|
59
|
-
if (isWindows()) {
|
|
60
|
-
document.body.classList.add('windows');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (isWebView()) {
|
|
64
|
-
console.log('webview');
|
|
65
|
-
document.body.classList.add('webview');
|
|
66
|
-
|
|
67
|
-
if (isAndroid()) {
|
|
68
|
-
document.body.classList.add('android');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (isIOS()) {
|
|
72
|
-
document.body.classList.add('ios');
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Add sanitized user agent as body class
|
|
77
|
-
if (typeof navigator !== 'undefined' && navigator.userAgent) {
|
|
78
|
-
const userAgent = navigator.userAgent.toLowerCase();
|
|
79
|
-
// Sanitize user agent for use as CSS class
|
|
80
|
-
const sanitizedUA = userAgent
|
|
81
|
-
.replace(/[^a-z0-9]/g, '-') // Replace non-alphanumeric with hyphens
|
|
82
|
-
.replace(/-+/g, '-') // Replace multiple hyphens with single
|
|
83
|
-
.replace(/^-|-$/g, '') // Remove leading/trailing hyphens
|
|
84
|
-
.substring(0, 100); // Limit length
|
|
85
|
-
|
|
86
|
-
if (sanitizedUA) {
|
|
87
|
-
document.body.classList.add(`ua-${sanitizedUA}`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
export default {
|
|
93
|
-
isWebView,
|
|
94
|
-
isAndroid,
|
|
95
|
-
isIOS,
|
|
96
|
-
isWindows,
|
|
97
|
-
applyPlatformBodyClasses,
|
|
98
|
-
};
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/***
|
|
2
|
-
* Centralized price formatter for all currencies.
|
|
3
|
-
*
|
|
4
|
-
* Amounts are ALWAYS passed as their real value (never in cents).
|
|
5
|
-
* - e.g. CLP 4990 → "$4.990"
|
|
6
|
-
* - e.g. USD 6.00 → "$6.00"
|
|
7
|
-
* - e.g. USD 6 → "$6"
|
|
8
|
-
*
|
|
9
|
-
* Formatting rules:
|
|
10
|
-
* - CLP: always formatted as an integer (no decimals), with thousands separator.
|
|
11
|
-
* - Other currencies: if the value is an integer → no decimals;
|
|
12
|
-
* if the value has a fractional part → up to 2 decimal places.
|
|
13
|
-
*/
|
|
14
|
-
export const priceFormatter = (price: number, currencyCode: string = 'CLP'): string => {
|
|
15
|
-
// Ensure we work with an actual number
|
|
16
|
-
const numericPrice = typeof price === 'number' ? price : parseFloat(String(price));
|
|
17
|
-
if (isNaN(numericPrice)) return '$0';
|
|
18
|
-
|
|
19
|
-
if (currencyCode === 'CLP') {
|
|
20
|
-
// CLP never uses decimals – format as whole number with locale thousands separator
|
|
21
|
-
return `$${Math.round(numericPrice).toLocaleString('es-CL')}`;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// For all other currencies: detect int vs float
|
|
25
|
-
const isInteger = Number.isInteger(numericPrice);
|
|
26
|
-
|
|
27
|
-
if (isInteger) {
|
|
28
|
-
// Integer amount → no decimals, with thousands separator
|
|
29
|
-
return `$${numericPrice.toLocaleString('en-US')}`;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Float amount → up to 2 decimal places
|
|
33
|
-
return `$${numericPrice.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export default priceFormatter;
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Resource Loader Utility
|
|
3
|
-
*
|
|
4
|
-
* Provides a generic mechanism for lazy-loading and caching resources.
|
|
5
|
-
* Useful for loading React-Admin resource configurations on demand.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Creates a resource loader with caching support
|
|
10
|
-
*
|
|
11
|
-
* @param loadFn - Async function that loads the resources
|
|
12
|
-
* @returns Object with getResources() method and cached resources array
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```typescript
|
|
16
|
-
* const { getResources, resources } = createResourceLoader(async () => {
|
|
17
|
-
* const [{ default: systemResources }, { default: todoResource }] = await Promise.all([
|
|
18
|
-
* import('dash-admin/src/systemResources'),
|
|
19
|
-
* import('@app/resources/demo/todoResource'),
|
|
20
|
-
* ]);
|
|
21
|
-
* return [...systemResources, todoResource];
|
|
22
|
-
* });
|
|
23
|
-
*
|
|
24
|
-
* // Async usage (recommended)
|
|
25
|
-
* const allResources = await getResources();
|
|
26
|
-
*
|
|
27
|
-
* // Sync usage (returns cached or empty array)
|
|
28
|
-
* const cachedResources = resources;
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
export const createResourceLoader = <T>(
|
|
32
|
-
loadFn: () => Promise<T[]>
|
|
33
|
-
): {
|
|
34
|
-
getResources: () => Promise<T[]>;
|
|
35
|
-
resources: T[];
|
|
36
|
-
clearCache: () => void;
|
|
37
|
-
isLoaded: () => boolean;
|
|
38
|
-
} => {
|
|
39
|
-
let cachedResources: T[] | null = null;
|
|
40
|
-
let loadingPromise: Promise<T[]> | null = null;
|
|
41
|
-
|
|
42
|
-
const getResources = async (): Promise<T[]> => {
|
|
43
|
-
// Return cached if available
|
|
44
|
-
if (cachedResources) {
|
|
45
|
-
return cachedResources;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// If already loading, return the existing promise to avoid duplicate loads
|
|
49
|
-
if (loadingPromise) {
|
|
50
|
-
return loadingPromise;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Start loading
|
|
54
|
-
loadingPromise = loadFn().then((resources) => {
|
|
55
|
-
cachedResources = resources;
|
|
56
|
-
loadingPromise = null;
|
|
57
|
-
return resources;
|
|
58
|
-
}).catch((error) => {
|
|
59
|
-
loadingPromise = null;
|
|
60
|
-
console.error('Failed to load resources:', error);
|
|
61
|
-
throw error;
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
return loadingPromise;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const clearCache = (): void => {
|
|
68
|
-
cachedResources = null;
|
|
69
|
-
loadingPromise = null;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const isLoaded = (): boolean => {
|
|
73
|
-
return cachedResources !== null;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
// Return empty array for sync access, will be populated after getResources() is called
|
|
77
|
-
const resources: T[] = [];
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
getResources,
|
|
81
|
-
resources,
|
|
82
|
-
clearCache,
|
|
83
|
-
isLoaded,
|
|
84
|
-
};
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Simple resource loader for cases where you just need the async getter
|
|
89
|
-
*
|
|
90
|
-
* @param loadFn - Async function that loads the resources
|
|
91
|
-
* @returns Async function that returns cached resources
|
|
92
|
-
*/
|
|
93
|
-
export const createSimpleResourceLoader = <T>(
|
|
94
|
-
loadFn: () => Promise<T[]>
|
|
95
|
-
): (() => Promise<T[]>) => {
|
|
96
|
-
let cache: T[] | null = null;
|
|
97
|
-
|
|
98
|
-
return async (): Promise<T[]> => {
|
|
99
|
-
if (cache) {
|
|
100
|
-
return cache;
|
|
101
|
-
}
|
|
102
|
-
cache = await loadFn();
|
|
103
|
-
return cache;
|
|
104
|
-
};
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
export default createResourceLoader;
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { getDashIPCService, isElectron } from './dashIPCService';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Unified text-to-speech entry point, routed by platform:
|
|
5
|
-
* - Android (native/Capacitor): @capacitor-community/text-to-speech
|
|
6
|
-
* - Electron: window.DashIPCService -> Python tts_service (unchanged)
|
|
7
|
-
* - Web/other: no-op (not yet supported)
|
|
8
|
-
*
|
|
9
|
-
* Capacitor is deliberately accessed via `window.Capacitor` rather than a
|
|
10
|
-
* static `import ... from '@capacitor/core'`: the Android build externalizes
|
|
11
|
-
* @capacitor/core from the Rollup bundle (it's injected as a global by the
|
|
12
|
-
* native bridge at runtime), so a static import resolves to a bare, unbundled
|
|
13
|
-
* specifier that the WebView cannot load.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
interface CapacitorGlobal {
|
|
17
|
-
isNativePlatform?: () => boolean;
|
|
18
|
-
getPlatform?: () => string;
|
|
19
|
-
Plugins?: {
|
|
20
|
-
TextToSpeech?: {
|
|
21
|
-
speak: (options: {
|
|
22
|
-
text: string;
|
|
23
|
-
lang?: string;
|
|
24
|
-
rate?: number;
|
|
25
|
-
pitch?: number;
|
|
26
|
-
volume?: number;
|
|
27
|
-
}) => Promise<unknown>;
|
|
28
|
-
};
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const getCapacitor = (): CapacitorGlobal | undefined =>
|
|
33
|
-
(window as unknown as { Capacitor?: CapacitorGlobal })?.Capacitor;
|
|
34
|
-
|
|
35
|
-
const getTextToSpeechPlugin = () => getCapacitor()?.Plugins?.TextToSpeech;
|
|
36
|
-
|
|
37
|
-
const LANG_LOCALE_MAP: Record<string, string> = {
|
|
38
|
-
es: 'es-ES',
|
|
39
|
-
en: 'en-US',
|
|
40
|
-
pt: 'pt-BR',
|
|
41
|
-
fr: 'fr-FR',
|
|
42
|
-
de: 'de-DE',
|
|
43
|
-
it: 'it-IT',
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
// The Electron/Python path accepts short codes (gTTS); the Capacitor plugin
|
|
47
|
-
// expects BCP-47 locale tags, so only the Android branch needs this mapping.
|
|
48
|
-
const toLocale = (lang?: string): string => {
|
|
49
|
-
if (!lang) return LANG_LOCALE_MAP.es;
|
|
50
|
-
if (lang.includes('-')) return lang;
|
|
51
|
-
return LANG_LOCALE_MAP[lang.toLowerCase()] || lang;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const isNativeAndroid = (): boolean => {
|
|
55
|
-
const capacitor = getCapacitor();
|
|
56
|
-
return !!capacitor?.isNativePlatform?.() && capacitor.getPlatform?.() === 'android';
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export const speak = (text: string, lang?: string): void => {
|
|
60
|
-
if (!text) return;
|
|
61
|
-
|
|
62
|
-
if (isNativeAndroid()) {
|
|
63
|
-
const textToSpeech = getTextToSpeechPlugin();
|
|
64
|
-
if (!textToSpeech) {
|
|
65
|
-
console.warn('Native Android TTS plugin not available on window.Capacitor.Plugins');
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
textToSpeech
|
|
69
|
-
.speak({ text, lang: toLocale(lang), rate: 1.0, pitch: 1.0, volume: 1.0 })
|
|
70
|
-
.catch((error) => {
|
|
71
|
-
console.error('Native Android TTS failed:', error);
|
|
72
|
-
});
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (isElectron()) {
|
|
77
|
-
getDashIPCService()?.speak({ message: text, lang });
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Web/other: no-op (unchanged behavior)
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
export const speakAsync = async (text: string, lang?: string): Promise<void> => {
|
|
85
|
-
if (!text) return;
|
|
86
|
-
|
|
87
|
-
if (isNativeAndroid()) {
|
|
88
|
-
const textToSpeech = getTextToSpeechPlugin();
|
|
89
|
-
if (!textToSpeech) {
|
|
90
|
-
console.warn('Native Android TTS plugin not available on window.Capacitor.Plugins');
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
await textToSpeech.speak({ text, lang: toLocale(lang), rate: 1.0, pitch: 1.0, volume: 1.0 });
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (isElectron()) {
|
|
98
|
-
const ipc = getDashIPCService() as (ReturnType<typeof getDashIPCService> & {
|
|
99
|
-
speakAsync?: (text: string, lang?: string) => Promise<unknown>;
|
|
100
|
-
});
|
|
101
|
-
if (ipc?.speakAsync) {
|
|
102
|
-
await ipc.speakAsync(text, lang);
|
|
103
|
-
} else {
|
|
104
|
-
ipc?.speak({ message: text, lang });
|
|
105
|
-
}
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Web/other: no-op (unchanged behavior)
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
export default { speak, speakAsync };
|
package/src/utils/storedTheme.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stored Theme Utility
|
|
3
|
-
*
|
|
4
|
-
* Handles theme persistence and initialization in localStorage
|
|
5
|
-
* and the document's data-theme attribute.
|
|
6
|
-
*/
|
|
7
|
-
import { dashStorage } from './dashDtorage';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Get the stored theme from localStorage
|
|
11
|
-
*/
|
|
12
|
-
export const getStoredTheme = (): string | null => {
|
|
13
|
-
return dashStorage.getItem('theme');
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Set the theme in localStorage and update the document attribute
|
|
18
|
-
*/
|
|
19
|
-
export const setStoredTheme = (theme: string): void => {
|
|
20
|
-
dashStorage.setItem('theme', theme);
|
|
21
|
-
if (typeof document !== 'undefined') {
|
|
22
|
-
document.documentElement.setAttribute('data-theme', theme);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Initialize the theme from storage or use a default
|
|
28
|
-
* Sets both localStorage and the data-theme attribute
|
|
29
|
-
*/
|
|
30
|
-
export const initializeStoredTheme = (defaultTheme: string): string => {
|
|
31
|
-
const storedTheme = getStoredTheme();
|
|
32
|
-
const themeToUse = storedTheme || defaultTheme;
|
|
33
|
-
|
|
34
|
-
if (typeof document !== 'undefined') {
|
|
35
|
-
document.documentElement.setAttribute('data-theme', themeToUse);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (!storedTheme) {
|
|
39
|
-
dashStorage.setItem('theme', defaultTheme);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return themeToUse;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Clear the stored theme
|
|
47
|
-
*/
|
|
48
|
-
export const clearStoredTheme = (): void => {
|
|
49
|
-
dashStorage.removeItem('theme');
|
|
50
|
-
if (typeof document !== 'undefined') {
|
|
51
|
-
document.documentElement.removeAttribute('data-theme');
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export default {
|
|
56
|
-
getStoredTheme,
|
|
57
|
-
setStoredTheme,
|
|
58
|
-
initializeStoredTheme,
|
|
59
|
-
clearStoredTheme,
|
|
60
|
-
};
|