@shellui/core 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. package/README.md +17 -0
  2. package/dist/ContentView-CZG-ro_B.js +146 -0
  3. package/dist/ContentView-CZG-ro_B.js.map +1 -0
  4. package/dist/CookiePreferencesView-MhO9FO-4.js +213 -0
  5. package/dist/CookiePreferencesView-MhO9FO-4.js.map +1 -0
  6. package/dist/DefaultLayout-Dbb3uJED.js +394 -0
  7. package/dist/DefaultLayout-Dbb3uJED.js.map +1 -0
  8. package/dist/FullscreenLayout-1SgPHWw-.js +30 -0
  9. package/dist/FullscreenLayout-1SgPHWw-.js.map +1 -0
  10. package/dist/HomeView-DYU-O_Il.js +21 -0
  11. package/dist/HomeView-DYU-O_Il.js.map +1 -0
  12. package/dist/NotFoundView-CeYjJNg0.js +52 -0
  13. package/dist/NotFoundView-CeYjJNg0.js.map +1 -0
  14. package/dist/OverlayShell-pzbqQW25.js +642 -0
  15. package/dist/OverlayShell-pzbqQW25.js.map +1 -0
  16. package/dist/SettingsView-Bndrta44.js +2207 -0
  17. package/dist/SettingsView-Bndrta44.js.map +1 -0
  18. package/dist/ViewRoute-ChSPabOy.js +32 -0
  19. package/dist/ViewRoute-ChSPabOy.js.map +1 -0
  20. package/dist/WindowsLayout-CXGNPKoY.js +633 -0
  21. package/dist/WindowsLayout-CXGNPKoY.js.map +1 -0
  22. package/dist/app.d.ts +3 -0
  23. package/dist/app.d.ts.map +1 -0
  24. package/dist/components/ContentView.d.ts +10 -0
  25. package/dist/components/ContentView.d.ts.map +1 -0
  26. package/dist/components/HomeView.d.ts +2 -0
  27. package/dist/components/HomeView.d.ts.map +1 -0
  28. package/dist/components/LoadingOverlay.d.ts +2 -0
  29. package/dist/components/LoadingOverlay.d.ts.map +1 -0
  30. package/dist/components/NotFoundView.d.ts +2 -0
  31. package/dist/components/NotFoundView.d.ts.map +1 -0
  32. package/dist/components/RouteErrorBoundary.d.ts +2 -0
  33. package/dist/components/RouteErrorBoundary.d.ts.map +1 -0
  34. package/dist/components/ViewRoute.d.ts +7 -0
  35. package/dist/components/ViewRoute.d.ts.map +1 -0
  36. package/dist/components/ui/alert-dialog.d.ts +32 -0
  37. package/dist/components/ui/alert-dialog.d.ts.map +1 -0
  38. package/dist/components/ui/breadcrumb.d.ts +20 -0
  39. package/dist/components/ui/breadcrumb.d.ts.map +1 -0
  40. package/dist/components/ui/button-group.d.ts +7 -0
  41. package/dist/components/ui/button-group.d.ts.map +1 -0
  42. package/dist/components/ui/button.d.ts +12 -0
  43. package/dist/components/ui/button.d.ts.map +1 -0
  44. package/dist/components/ui/dialog.d.ts +24 -0
  45. package/dist/components/ui/dialog.d.ts.map +1 -0
  46. package/dist/components/ui/drawer.d.ts +38 -0
  47. package/dist/components/ui/drawer.d.ts.map +1 -0
  48. package/dist/components/ui/select.d.ts +5 -0
  49. package/dist/components/ui/select.d.ts.map +1 -0
  50. package/dist/components/ui/sidebar.d.ts +46 -0
  51. package/dist/components/ui/sidebar.d.ts.map +1 -0
  52. package/dist/components/ui/sonner.d.ts +6 -0
  53. package/dist/components/ui/sonner.d.ts.map +1 -0
  54. package/dist/components/ui/switch.d.ts +8 -0
  55. package/dist/components/ui/switch.d.ts.map +1 -0
  56. package/dist/constants/urls.d.ts +6 -0
  57. package/dist/constants/urls.d.ts.map +1 -0
  58. package/dist/constants/urls.js +8 -0
  59. package/dist/constants/urls.js.map +1 -0
  60. package/dist/features/alertDialog/DialogContext.d.ts +12 -0
  61. package/dist/features/alertDialog/DialogContext.d.ts.map +1 -0
  62. package/dist/features/config/ConfigProvider.d.ts +15 -0
  63. package/dist/features/config/ConfigProvider.d.ts.map +1 -0
  64. package/dist/features/config/types.d.ts +177 -0
  65. package/dist/features/config/types.d.ts.map +1 -0
  66. package/dist/features/config/useConfig.d.ts +8 -0
  67. package/dist/features/config/useConfig.d.ts.map +1 -0
  68. package/dist/features/cookieConsent/CookieConsentModal.d.ts +6 -0
  69. package/dist/features/cookieConsent/CookieConsentModal.d.ts.map +1 -0
  70. package/dist/features/cookieConsent/CookiePreferencesView.d.ts +2 -0
  71. package/dist/features/cookieConsent/CookiePreferencesView.d.ts.map +1 -0
  72. package/dist/features/cookieConsent/cookieConsent.d.ts +22 -0
  73. package/dist/features/cookieConsent/cookieConsent.d.ts.map +1 -0
  74. package/dist/features/cookieConsent/useCookieConsent.d.ts +15 -0
  75. package/dist/features/cookieConsent/useCookieConsent.d.ts.map +1 -0
  76. package/dist/features/drawer/DrawerContext.d.ts +24 -0
  77. package/dist/features/drawer/DrawerContext.d.ts.map +1 -0
  78. package/dist/features/layouts/AppLayout.d.ts +12 -0
  79. package/dist/features/layouts/AppLayout.d.ts.map +1 -0
  80. package/dist/features/layouts/DefaultLayout.d.ts +10 -0
  81. package/dist/features/layouts/DefaultLayout.d.ts.map +1 -0
  82. package/dist/features/layouts/FullscreenLayout.d.ts +9 -0
  83. package/dist/features/layouts/FullscreenLayout.d.ts.map +1 -0
  84. package/dist/features/layouts/LayoutProviders.d.ts +9 -0
  85. package/dist/features/layouts/LayoutProviders.d.ts.map +1 -0
  86. package/dist/features/layouts/OverlayShell.d.ts +10 -0
  87. package/dist/features/layouts/OverlayShell.d.ts.map +1 -0
  88. package/dist/features/layouts/WindowsLayout.d.ts +24 -0
  89. package/dist/features/layouts/WindowsLayout.d.ts.map +1 -0
  90. package/dist/features/layouts/utils.d.ts +16 -0
  91. package/dist/features/layouts/utils.d.ts.map +1 -0
  92. package/dist/features/modal/ModalContext.d.ts +20 -0
  93. package/dist/features/modal/ModalContext.d.ts.map +1 -0
  94. package/dist/features/sentry/initSentry.d.ts +14 -0
  95. package/dist/features/sentry/initSentry.d.ts.map +1 -0
  96. package/dist/features/settings/SettingsContext.d.ts +10 -0
  97. package/dist/features/settings/SettingsContext.d.ts.map +1 -0
  98. package/dist/features/settings/SettingsIcons.d.ts +22 -0
  99. package/dist/features/settings/SettingsIcons.d.ts.map +1 -0
  100. package/dist/features/settings/SettingsProvider.d.ts +5 -0
  101. package/dist/features/settings/SettingsProvider.d.ts.map +1 -0
  102. package/dist/features/settings/SettingsRoutes.d.ts +7 -0
  103. package/dist/features/settings/SettingsRoutes.d.ts.map +1 -0
  104. package/dist/features/settings/SettingsView.d.ts +2 -0
  105. package/dist/features/settings/SettingsView.d.ts.map +1 -0
  106. package/dist/features/settings/components/Advanced.d.ts +2 -0
  107. package/dist/features/settings/components/Advanced.d.ts.map +1 -0
  108. package/dist/features/settings/components/Appearance.d.ts +2 -0
  109. package/dist/features/settings/components/Appearance.d.ts.map +1 -0
  110. package/dist/features/settings/components/DataPrivacy.d.ts +2 -0
  111. package/dist/features/settings/components/DataPrivacy.d.ts.map +1 -0
  112. package/dist/features/settings/components/Develop.d.ts +2 -0
  113. package/dist/features/settings/components/Develop.d.ts.map +1 -0
  114. package/dist/features/settings/components/LanguageAndRegion.d.ts +2 -0
  115. package/dist/features/settings/components/LanguageAndRegion.d.ts.map +1 -0
  116. package/dist/features/settings/components/ServiceWorker.d.ts +2 -0
  117. package/dist/features/settings/components/ServiceWorker.d.ts.map +1 -0
  118. package/dist/features/settings/components/UpdateApp.d.ts +2 -0
  119. package/dist/features/settings/components/UpdateApp.d.ts.map +1 -0
  120. package/dist/features/settings/components/develop/DialogTestButtons.d.ts +2 -0
  121. package/dist/features/settings/components/develop/DialogTestButtons.d.ts.map +1 -0
  122. package/dist/features/settings/components/develop/DrawerTestButtons.d.ts +2 -0
  123. package/dist/features/settings/components/develop/DrawerTestButtons.d.ts.map +1 -0
  124. package/dist/features/settings/components/develop/ModalTestButtons.d.ts +2 -0
  125. package/dist/features/settings/components/develop/ModalTestButtons.d.ts.map +1 -0
  126. package/dist/features/settings/components/develop/ToastTestButtons.d.ts +2 -0
  127. package/dist/features/settings/components/develop/ToastTestButtons.d.ts.map +1 -0
  128. package/dist/features/settings/hooks/useSettings.d.ts +2 -0
  129. package/dist/features/settings/hooks/useSettings.d.ts.map +1 -0
  130. package/dist/features/sonner/SonnerContext.d.ts +29 -0
  131. package/dist/features/sonner/SonnerContext.d.ts.map +1 -0
  132. package/dist/features/theme/ThemeProvider.d.ts +11 -0
  133. package/dist/features/theme/ThemeProvider.d.ts.map +1 -0
  134. package/dist/features/theme/themes.d.ts +114 -0
  135. package/dist/features/theme/themes.d.ts.map +1 -0
  136. package/dist/features/theme/useTheme.d.ts +10 -0
  137. package/dist/features/theme/useTheme.d.ts.map +1 -0
  138. package/dist/i18n/I18nProvider.d.ts +9 -0
  139. package/dist/i18n/I18nProvider.d.ts.map +1 -0
  140. package/dist/i18n/config.d.ts +23 -0
  141. package/dist/i18n/config.d.ts.map +1 -0
  142. package/dist/i18n/translations/en/common.json.d.ts +19 -0
  143. package/dist/i18n/translations/en/cookieConsent.json.d.ts +53 -0
  144. package/dist/i18n/translations/en/settings.json.d.ts +358 -0
  145. package/dist/i18n/translations/fr/common.json.d.ts +19 -0
  146. package/dist/i18n/translations/fr/cookieConsent.json.d.ts +53 -0
  147. package/dist/i18n/translations/fr/settings.json.d.ts +358 -0
  148. package/dist/index-lmRk5L6z.js +2160 -0
  149. package/dist/index-lmRk5L6z.js.map +1 -0
  150. package/dist/index.d.ts +7 -0
  151. package/dist/index.d.ts.map +1 -0
  152. package/dist/index.js +12 -0
  153. package/dist/index.js.map +1 -0
  154. package/dist/lib/utils.d.ts +3 -0
  155. package/dist/lib/utils.d.ts.map +1 -0
  156. package/dist/lib/z-index.d.ts +29 -0
  157. package/dist/lib/z-index.d.ts.map +1 -0
  158. package/dist/router/router.d.ts +3 -0
  159. package/dist/router/router.d.ts.map +1 -0
  160. package/dist/router/routes.d.ts +4 -0
  161. package/dist/router/routes.d.ts.map +1 -0
  162. package/dist/sidebar-ClIeZ2zb.js +303 -0
  163. package/dist/sidebar-ClIeZ2zb.js.map +1 -0
  164. package/dist/style.css +1 -0
  165. package/dist/switch-8SzUJz7Q.js +44 -0
  166. package/dist/switch-8SzUJz7Q.js.map +1 -0
  167. package/dist/types.js +2 -0
  168. package/dist/types.js.map +1 -0
  169. package/package.json +93 -0
  170. package/postcss.config.js +6 -0
  171. package/src/app.tsx +119 -0
  172. package/src/components/ContentView.tsx +258 -0
  173. package/src/components/HomeView.tsx +19 -0
  174. package/src/components/LoadingOverlay.tsx +12 -0
  175. package/src/components/NotFoundView.tsx +84 -0
  176. package/src/components/RouteErrorBoundary.tsx +95 -0
  177. package/src/components/ViewRoute.tsx +47 -0
  178. package/src/components/ui/alert-dialog.tsx +181 -0
  179. package/src/components/ui/breadcrumb.tsx +155 -0
  180. package/src/components/ui/button-group.tsx +52 -0
  181. package/src/components/ui/button.tsx +51 -0
  182. package/src/components/ui/dialog.tsx +160 -0
  183. package/src/components/ui/drawer.tsx +200 -0
  184. package/src/components/ui/select.tsx +24 -0
  185. package/src/components/ui/sidebar.tsx +406 -0
  186. package/src/components/ui/sonner.tsx +36 -0
  187. package/src/components/ui/switch.tsx +45 -0
  188. package/src/constants/urls.ts +4 -0
  189. package/src/features/alertDialog/DialogContext.tsx +468 -0
  190. package/src/features/config/ConfigProvider.ts +96 -0
  191. package/src/features/config/types.ts +195 -0
  192. package/src/features/config/useConfig.ts +15 -0
  193. package/src/features/cookieConsent/CookieConsentModal.tsx +122 -0
  194. package/src/features/cookieConsent/CookiePreferencesView.tsx +328 -0
  195. package/src/features/cookieConsent/cookieConsent.ts +84 -0
  196. package/src/features/cookieConsent/useCookieConsent.ts +39 -0
  197. package/src/features/drawer/DrawerContext.tsx +116 -0
  198. package/src/features/layouts/AppLayout.tsx +63 -0
  199. package/src/features/layouts/DefaultLayout.tsx +625 -0
  200. package/src/features/layouts/FullscreenLayout.tsx +55 -0
  201. package/src/features/layouts/LayoutProviders.tsx +20 -0
  202. package/src/features/layouts/OverlayShell.tsx +171 -0
  203. package/src/features/layouts/WindowsLayout.tsx +860 -0
  204. package/src/features/layouts/utils.ts +99 -0
  205. package/src/features/modal/ModalContext.tsx +112 -0
  206. package/src/features/sentry/initSentry.ts +72 -0
  207. package/src/features/settings/SettingsContext.tsx +19 -0
  208. package/src/features/settings/SettingsIcons.tsx +452 -0
  209. package/src/features/settings/SettingsProvider.tsx +341 -0
  210. package/src/features/settings/SettingsRoutes.tsx +66 -0
  211. package/src/features/settings/SettingsView.tsx +327 -0
  212. package/src/features/settings/components/Advanced.tsx +128 -0
  213. package/src/features/settings/components/Appearance.tsx +306 -0
  214. package/src/features/settings/components/DataPrivacy.tsx +142 -0
  215. package/src/features/settings/components/Develop.tsx +174 -0
  216. package/src/features/settings/components/LanguageAndRegion.tsx +329 -0
  217. package/src/features/settings/components/ServiceWorker.tsx +363 -0
  218. package/src/features/settings/components/UpdateApp.tsx +206 -0
  219. package/src/features/settings/components/develop/DialogTestButtons.tsx +137 -0
  220. package/src/features/settings/components/develop/DrawerTestButtons.tsx +67 -0
  221. package/src/features/settings/components/develop/ModalTestButtons.tsx +30 -0
  222. package/src/features/settings/components/develop/ToastTestButtons.tsx +179 -0
  223. package/src/features/settings/hooks/useSettings.tsx +10 -0
  224. package/src/features/sonner/SonnerContext.tsx +286 -0
  225. package/src/features/theme/ThemeProvider.tsx +16 -0
  226. package/src/features/theme/themes.ts +561 -0
  227. package/src/features/theme/useTheme.tsx +71 -0
  228. package/src/i18n/I18nProvider.tsx +32 -0
  229. package/src/i18n/config.ts +107 -0
  230. package/src/i18n/translations/en/common.json +16 -0
  231. package/src/i18n/translations/en/cookieConsent.json +50 -0
  232. package/src/i18n/translations/en/settings.json +355 -0
  233. package/src/i18n/translations/fr/common.json +16 -0
  234. package/src/i18n/translations/fr/cookieConsent.json +50 -0
  235. package/src/i18n/translations/fr/settings.json +355 -0
  236. package/src/index.css +412 -0
  237. package/src/index.html +100 -0
  238. package/src/index.ts +31 -0
  239. package/src/lib/utils.ts +6 -0
  240. package/src/lib/z-index.ts +29 -0
  241. package/src/main.tsx +26 -0
  242. package/src/router/router.tsx +8 -0
  243. package/src/router/routes.tsx +115 -0
  244. package/src/service-worker/register.ts +1199 -0
  245. package/src/service-worker/sw-dev.ts +87 -0
  246. package/src/service-worker/sw.ts +105 -0
  247. package/tailwind.config.js +60 -0
@@ -0,0 +1,341 @@
1
+ import { useRef, useState, useEffect, useCallback, useMemo, type ReactNode } from 'react';
2
+ import {
3
+ getLogger,
4
+ shellui,
5
+ type ShellUIMessage,
6
+ type Settings,
7
+ type SettingsNavigationItem,
8
+ } from '@shellui/sdk';
9
+ import { SettingsContext } from './SettingsContext';
10
+ import { useConfig } from '../config/useConfig';
11
+ import { useTranslation } from 'react-i18next';
12
+ import type { NavigationItem, NavigationGroup } from '../config/types';
13
+
14
+ const logger = getLogger('shellcore');
15
+
16
+ function flattenNavigationItems(
17
+ navigation: (NavigationItem | NavigationGroup)[],
18
+ ): NavigationItem[] {
19
+ if (navigation.length === 0) return [];
20
+ return navigation.flatMap((item) => {
21
+ if ('title' in item && 'items' in item) return (item as NavigationGroup).items;
22
+ return [item as NavigationItem];
23
+ });
24
+ }
25
+
26
+ function resolveLabel(
27
+ value: string | { en: string; fr: string; [key: string]: string },
28
+ lang: string,
29
+ ): string {
30
+ if (typeof value === 'string') return value;
31
+ return value[lang] || value.en || value.fr || Object.values(value)[0] || '';
32
+ }
33
+
34
+ function buildSettingsWithNavigation(
35
+ settings: Settings,
36
+ navigation: (NavigationItem | NavigationGroup)[] | undefined,
37
+ lang: string,
38
+ ): Settings {
39
+ if (!navigation?.length) return settings;
40
+ const items: SettingsNavigationItem[] = flattenNavigationItems(navigation).map((item) => ({
41
+ path: item.path,
42
+ url: item.url,
43
+ label: resolveLabel(item.label, lang),
44
+ }));
45
+ return { ...settings, navigation: { items } };
46
+ }
47
+
48
+ const STORAGE_KEY = 'shellui:settings';
49
+
50
+ // Get browser's timezone as default
51
+ const getBrowserTimezone = (): string => {
52
+ if (typeof window !== 'undefined' && Intl) {
53
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
54
+ }
55
+ return 'UTC';
56
+ };
57
+
58
+ const defaultSettings: Settings = {
59
+ developerFeatures: {
60
+ enabled: false,
61
+ },
62
+ errorReporting: {
63
+ enabled: true,
64
+ },
65
+ logging: {
66
+ namespaces: {
67
+ shellsdk: false,
68
+ shellcore: false,
69
+ },
70
+ },
71
+ appearance: {
72
+ theme: 'system',
73
+ themeName: 'default',
74
+ },
75
+ language: {
76
+ code: 'en',
77
+ },
78
+ region: {
79
+ timezone: getBrowserTimezone(),
80
+ },
81
+ cookieConsent: {
82
+ acceptedHosts: [],
83
+ consentedCookieHosts: [],
84
+ },
85
+ serviceWorker: {
86
+ enabled: true,
87
+ },
88
+ };
89
+
90
+ export function SettingsProvider({ children }: { children: ReactNode }) {
91
+ const { config } = useConfig();
92
+ const { i18n } = useTranslation();
93
+ // Use a ref to always have current settings for message listeners (avoids closure issues)
94
+ const settingsRef = useRef<Settings | null>(null);
95
+ const [settings, setSettings] = useState<Settings>(() => {
96
+ let initialSettings: Settings;
97
+
98
+ if (shellui.initialSettings) {
99
+ initialSettings = shellui.initialSettings;
100
+ settingsRef.current = initialSettings;
101
+ return initialSettings;
102
+ }
103
+
104
+ // Initialize from localStorage
105
+ if (typeof window !== 'undefined') {
106
+ try {
107
+ const stored = localStorage.getItem(STORAGE_KEY);
108
+ if (stored) {
109
+ const parsed = JSON.parse(stored);
110
+ // Deep merge with defaults to handle new settings
111
+ initialSettings = {
112
+ ...defaultSettings,
113
+ ...parsed,
114
+ errorReporting: {
115
+ enabled: parsed.errorReporting?.enabled ?? defaultSettings.errorReporting.enabled,
116
+ },
117
+ logging: {
118
+ namespaces: {
119
+ ...defaultSettings.logging.namespaces,
120
+ ...parsed.logging?.namespaces,
121
+ },
122
+ },
123
+ appearance: {
124
+ theme: parsed.appearance?.theme || defaultSettings.appearance.theme,
125
+ themeName: parsed.appearance?.themeName || defaultSettings.appearance.themeName,
126
+ },
127
+ language: {
128
+ code: parsed.language?.code || defaultSettings.language.code,
129
+ },
130
+ region: {
131
+ // Only use stored timezone if it exists, otherwise use browser's current timezone
132
+ timezone: parsed.region?.timezone || getBrowserTimezone(),
133
+ },
134
+ cookieConsent: {
135
+ acceptedHosts: Array.isArray(parsed.cookieConsent?.acceptedHosts)
136
+ ? parsed.cookieConsent.acceptedHosts
137
+ : (defaultSettings.cookieConsent?.acceptedHosts ?? []),
138
+ consentedCookieHosts: Array.isArray(parsed.cookieConsent?.consentedCookieHosts)
139
+ ? parsed.cookieConsent.consentedCookieHosts
140
+ : (defaultSettings.cookieConsent?.consentedCookieHosts ?? []),
141
+ },
142
+ serviceWorker: {
143
+ // Migrate from legacy "caching" key if present
144
+ enabled: parsed.serviceWorker?.enabled ?? parsed.caching?.enabled ?? true,
145
+ },
146
+ };
147
+ settingsRef.current = initialSettings;
148
+ return initialSettings;
149
+ }
150
+ } catch (error) {
151
+ logger.error('Failed to load settings from localStorage:', { error });
152
+ }
153
+ }
154
+ settingsRef.current = defaultSettings;
155
+ return defaultSettings;
156
+ });
157
+
158
+ // Keep ref in sync with state for message listeners
159
+ useEffect(() => {
160
+ settingsRef.current = settings;
161
+ }, [settings]);
162
+
163
+ // Listen for settings updates from parent/other nodes
164
+ useEffect(() => {
165
+ if (typeof window === 'undefined') {
166
+ return;
167
+ }
168
+
169
+ const cleanup = shellui.addMessageListener(
170
+ 'SHELLUI_SETTINGS_UPDATED',
171
+ (message: ShellUIMessage) => {
172
+ const payload = message.payload as { settings: Settings };
173
+ const newSettings = payload.settings;
174
+ if (newSettings) {
175
+ // Update localStorage with new settings value
176
+ setSettings(newSettings);
177
+ if (window.parent === window) {
178
+ try {
179
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(newSettings));
180
+ // Confirm: root updated localStorage; re-inject navigation when propagating
181
+ const settingsToPropagate = buildSettingsWithNavigation(
182
+ newSettings,
183
+ config?.navigation,
184
+ i18n.language || 'en',
185
+ );
186
+ logger.info('Root Parent received settings update', { message });
187
+ shellui.propagateMessage({
188
+ type: 'SHELLUI_SETTINGS',
189
+ payload: { settings: settingsToPropagate },
190
+ });
191
+ } catch (error) {
192
+ logger.error('Failed to update settings from message:', { error });
193
+ }
194
+ }
195
+ }
196
+ },
197
+ );
198
+
199
+ const cleanupSettingsRequested = shellui.addMessageListener(
200
+ 'SHELLUI_SETTINGS_REQUESTED',
201
+ () => {
202
+ // Use ref to always get current settings (avoids stale closure)
203
+ const currentSettings = settingsRef.current ?? defaultSettings;
204
+ const settingsWithNav = buildSettingsWithNavigation(
205
+ currentSettings,
206
+ config?.navigation,
207
+ i18n.language || 'en',
208
+ );
209
+ shellui.propagateMessage({
210
+ type: 'SHELLUI_SETTINGS',
211
+ payload: { settings: settingsWithNav },
212
+ });
213
+ },
214
+ );
215
+
216
+ const cleanupSettings = shellui.addMessageListener(
217
+ 'SHELLUI_SETTINGS',
218
+ (data: ShellUIMessage) => {
219
+ const message = data as ShellUIMessage;
220
+ const payload = message.payload as { settings: Settings };
221
+ const newSettings = payload.settings;
222
+ if (newSettings) {
223
+ setSettings(newSettings);
224
+ }
225
+ },
226
+ );
227
+
228
+ return () => {
229
+ cleanup();
230
+ cleanupSettings();
231
+ cleanupSettingsRequested();
232
+ };
233
+ }, [settings, config?.navigation, i18n.language]);
234
+
235
+ // ACTIONS
236
+ const updateSettings = useCallback(
237
+ (updates: Partial<Settings>) => {
238
+ const newSettings = { ...settings, ...updates };
239
+
240
+ // Update localStorage and propagate to children if we're in the root window
241
+ if (typeof window !== 'undefined' && window.parent === window) {
242
+ try {
243
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(newSettings));
244
+ setSettings(newSettings);
245
+ // Propagate to child iframes (sendMessageToParent does nothing in root)
246
+ const settingsWithNav = buildSettingsWithNavigation(
247
+ newSettings,
248
+ config?.navigation,
249
+ i18n.language || 'en',
250
+ );
251
+ shellui.propagateMessage({
252
+ type: 'SHELLUI_SETTINGS',
253
+ payload: { settings: settingsWithNav },
254
+ });
255
+ } catch (error) {
256
+ logger.error('Failed to update settings in localStorage:', { error });
257
+ }
258
+ }
259
+
260
+ // For child iframes, send to parent (parent will propagate to siblings)
261
+ shellui.sendMessageToParent({
262
+ type: 'SHELLUI_SETTINGS_UPDATED',
263
+ payload: { settings: newSettings },
264
+ });
265
+ },
266
+ [settings, config?.navigation, i18n.language],
267
+ );
268
+
269
+ const updateSetting = useCallback(
270
+ <K extends keyof Settings>(key: K, updates: Partial<Settings[K]>) => {
271
+ // Deep merge: preserve existing nested properties
272
+ const currentValue = settings[key];
273
+ const mergedValue =
274
+ typeof currentValue === 'object' && currentValue !== null && !Array.isArray(currentValue)
275
+ ? { ...currentValue, ...updates }
276
+ : updates;
277
+ updateSettings({ [key]: mergedValue } as Partial<Settings>);
278
+ },
279
+ [settings, updateSettings],
280
+ );
281
+
282
+ const resetAllData = useCallback(() => {
283
+ // Clear all localStorage data
284
+ if (typeof window !== 'undefined') {
285
+ try {
286
+ // Clear settings
287
+ localStorage.removeItem(STORAGE_KEY);
288
+
289
+ // Clear all other localStorage items that start with shellui:
290
+ const keysToRemove: string[] = [];
291
+ for (let i = 0; i < localStorage.length; i++) {
292
+ const key = localStorage.key(i);
293
+ if (key && key.startsWith('shellui:')) {
294
+ keysToRemove.push(key);
295
+ }
296
+ }
297
+ keysToRemove.forEach((key) => localStorage.removeItem(key));
298
+
299
+ // Reset settings to defaults
300
+ const newSettings = defaultSettings;
301
+ setSettings(newSettings);
302
+
303
+ // If we're in the root window, update localStorage with defaults
304
+ if (window.parent === window) {
305
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(newSettings));
306
+ const settingsToPropagate = buildSettingsWithNavigation(
307
+ newSettings,
308
+ config?.navigation,
309
+ i18n.language || 'en',
310
+ );
311
+ shellui.propagateMessage({
312
+ type: 'SHELLUI_SETTINGS',
313
+ payload: { settings: settingsToPropagate },
314
+ });
315
+ }
316
+
317
+ // Notify parent about reset
318
+ shellui.sendMessageToParent({
319
+ type: 'SHELLUI_SETTINGS_UPDATED',
320
+ payload: { settings: newSettings },
321
+ });
322
+
323
+ logger.info('All app data has been reset');
324
+ } catch (error) {
325
+ logger.error('Failed to reset all data:', { error });
326
+ }
327
+ }
328
+ }, [config?.navigation, i18n.language]);
329
+
330
+ const value = useMemo(
331
+ () => ({
332
+ settings,
333
+ updateSettings,
334
+ updateSetting,
335
+ resetAllData,
336
+ }),
337
+ [settings, updateSettings, updateSetting, resetAllData],
338
+ );
339
+
340
+ return <SettingsContext.Provider value={value}>{children}</SettingsContext.Provider>;
341
+ }
@@ -0,0 +1,66 @@
1
+ import {
2
+ PaintbrushIcon,
3
+ GlobeIcon,
4
+ SettingsIcon,
5
+ CodeIcon,
6
+ ShieldIcon,
7
+ PackageIcon,
8
+ RefreshDoubleIcon,
9
+ } from './SettingsIcons';
10
+ import { Appearance } from './components/Appearance';
11
+ import { LanguageAndRegion } from './components/LanguageAndRegion';
12
+ import { UpdateApp } from './components/UpdateApp';
13
+ import { Advanced } from './components/Advanced';
14
+ import { Develop } from './components/Develop';
15
+ import { DataPrivacy } from './components/DataPrivacy';
16
+ import { ServiceWorker } from './components/ServiceWorker';
17
+ import { isTauri } from '../../service-worker/register';
18
+
19
+ export const createSettingsRoutes = (t: (key: string) => string) => [
20
+ {
21
+ name: t('routes.appearance'),
22
+ icon: PaintbrushIcon,
23
+ path: 'appearance',
24
+ element: <Appearance />,
25
+ },
26
+ {
27
+ name: t('routes.languageAndRegion'),
28
+ icon: GlobeIcon,
29
+ path: 'language-and-region',
30
+ element: <LanguageAndRegion />,
31
+ },
32
+ {
33
+ name: t('routes.updateApp'),
34
+ icon: RefreshDoubleIcon,
35
+ path: 'update-app',
36
+ element: <UpdateApp />,
37
+ },
38
+ {
39
+ name: t('routes.advanced'),
40
+ icon: SettingsIcon,
41
+ path: 'advanced',
42
+ element: <Advanced />,
43
+ },
44
+ {
45
+ name: t('routes.dataPrivacy'),
46
+ icon: ShieldIcon,
47
+ path: 'data-privacy',
48
+ element: <DataPrivacy />,
49
+ },
50
+ {
51
+ name: t('routes.develop'),
52
+ icon: CodeIcon,
53
+ path: 'developpers',
54
+ element: <Develop />,
55
+ },
56
+ ...(isTauri()
57
+ ? []
58
+ : [
59
+ {
60
+ name: t('routes.serviceWorker'),
61
+ icon: PackageIcon,
62
+ path: 'service-worker',
63
+ element: <ServiceWorker />,
64
+ },
65
+ ]),
66
+ ];