@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,327 @@
1
+ import { useState, useEffect, useMemo, useCallback } from 'react';
2
+ import {
3
+ Breadcrumb,
4
+ BreadcrumbItem,
5
+ BreadcrumbList,
6
+ BreadcrumbPage,
7
+ BreadcrumbSeparator,
8
+ } from '@/components/ui/breadcrumb';
9
+ import {
10
+ Sidebar,
11
+ SidebarContent,
12
+ SidebarGroup,
13
+ SidebarGroupContent,
14
+ SidebarGroupLabel,
15
+ SidebarMenu,
16
+ SidebarMenuButton,
17
+ SidebarMenuItem,
18
+ SidebarProvider,
19
+ } from '@/components/ui/sidebar';
20
+ import { Route, Routes, useLocation, useNavigate, Navigate } from 'react-router';
21
+ import { useTranslation } from 'react-i18next';
22
+ import urls from '@/constants/urls';
23
+ import { createSettingsRoutes } from './SettingsRoutes';
24
+ import { useSettings } from './hooks/useSettings';
25
+ import { useConfig } from '../config/useConfig';
26
+ import { isTauri } from '@/service-worker/register';
27
+ import { Button } from '@/components/ui/button';
28
+ import { ChevronRightIcon, ChevronLeftIcon } from './SettingsIcons';
29
+
30
+ export const SettingsView = () => {
31
+ const location = useLocation();
32
+ const navigate = useNavigate();
33
+ const { settings } = useSettings();
34
+ const { config } = useConfig();
35
+ const { t } = useTranslation('settings');
36
+ // Re-check isTauri after mount and after a short delay so we catch late-injected __TAURI__ in dev
37
+ const [isTauriEnv, setIsTauriEnv] = useState(() => isTauri());
38
+
39
+ useEffect(() => {
40
+ setIsTauriEnv(isTauri());
41
+ const tid = window.setTimeout(() => setIsTauriEnv(isTauri()), 200);
42
+ return () => window.clearTimeout(tid);
43
+ }, []);
44
+
45
+ useEffect(() => {
46
+ if (config?.title) {
47
+ const settingsLabel = t('settings', { ns: 'common' });
48
+ document.title = `${settingsLabel} | ${config.title}`;
49
+ }
50
+ }, [config?.title, t]);
51
+
52
+ // Create routes with translations (service-worker route is already omitted in Tauri by createSettingsRoutes)
53
+ const settingsRoutes = useMemo(() => createSettingsRoutes(t), [t]);
54
+
55
+ // In Tauri, hide service worker route; use reactive isTauriEnv so we catch late injection (e.g. dev)
56
+ const routesWithoutTauriSw = useMemo(() => {
57
+ if (isTauriEnv) {
58
+ return settingsRoutes.filter((route) => route.path !== 'service-worker');
59
+ }
60
+ return settingsRoutes;
61
+ }, [settingsRoutes, isTauriEnv]);
62
+
63
+ // Filter routes based on developer features setting
64
+ const filteredRoutes = useMemo(() => {
65
+ if (settings.developerFeatures.enabled) {
66
+ return routesWithoutTauriSw;
67
+ }
68
+ return routesWithoutTauriSw.filter(
69
+ (route) => route.path !== 'developpers' && route.path !== 'service-worker',
70
+ );
71
+ }, [settings.developerFeatures.enabled, routesWithoutTauriSw]);
72
+
73
+ // Group routes by category
74
+ const groupedRoutes = useMemo(() => {
75
+ const developerOnlyPaths = ['developpers', 'service-worker'];
76
+ const groups = [
77
+ {
78
+ title: t('categories.preferences'),
79
+ routes: filteredRoutes.filter((route) =>
80
+ ['appearance', 'language-and-region', 'data-privacy'].includes(route.path),
81
+ ),
82
+ },
83
+ {
84
+ title: t('categories.system'),
85
+ routes: filteredRoutes.filter((route) => ['update-app', 'advanced'].includes(route.path)),
86
+ },
87
+ {
88
+ title: t('categories.developer'),
89
+ routes: filteredRoutes.filter((route) => developerOnlyPaths.includes(route.path)),
90
+ },
91
+ ];
92
+ return groups.filter((group) => group.routes.length > 0);
93
+ }, [filteredRoutes, t]);
94
+
95
+ // Find matching nav item by checking if URL contains or ends with the item path
96
+ const getSelectedItemFromUrl = useCallback(() => {
97
+ const pathname = location.pathname;
98
+
99
+ // Find matching nav item by checking if pathname contains the item path
100
+ // This works regardless of the URL structure/prefix
101
+ const matchedItem = filteredRoutes.find((item) => {
102
+ // Normalize paths for comparison (remove leading/trailing slashes)
103
+ const normalizedPathname = pathname.replace(/^\/+|\/+$/g, '');
104
+ const normalizedItemPath = item.path.replace(/^\/+|\/+$/g, '');
105
+
106
+ // Check if pathname ends with the item path, or contains it as a path segment
107
+ return (
108
+ normalizedPathname === normalizedItemPath ||
109
+ normalizedPathname.endsWith(`/${normalizedItemPath}`) ||
110
+ normalizedPathname.includes(`/${normalizedItemPath}/`)
111
+ );
112
+ });
113
+
114
+ return matchedItem;
115
+ }, [location.pathname, filteredRoutes]);
116
+
117
+ const selectedItem = useMemo(() => getSelectedItemFromUrl(), [getSelectedItemFromUrl]);
118
+
119
+ // Check if we're at the settings root (no specific route selected)
120
+ const isSettingsRoot = useMemo(() => {
121
+ const pathname = location.pathname;
122
+ // Normalize the settings URL (remove leading/trailing slashes)
123
+ const normalizedSettingsPath = urls.settings.replace(/^\/+|\/+$/g, '');
124
+ // Normalize the current pathname (remove leading/trailing slashes)
125
+ const normalizedPathname = pathname.replace(/^\/+|\/+$/g, '');
126
+
127
+ // Check if we're exactly at the settings root
128
+ // This handles both "/__settings" and "/__settings/" cases
129
+ if (normalizedPathname === normalizedSettingsPath) {
130
+ return true;
131
+ }
132
+
133
+ // If pathname starts with settings path followed by a slash, we're at a subpage
134
+ // e.g., "__settings/appearance" means we're NOT at root
135
+ const settingsPathWithSlash = `${normalizedSettingsPath}/`;
136
+ if (normalizedPathname.startsWith(settingsPathWithSlash)) {
137
+ return false;
138
+ }
139
+
140
+ // Pathname doesn't match settings path structure - not in settings
141
+ return false;
142
+ }, [location.pathname, filteredRoutes]);
143
+
144
+ // Navigate back to settings root
145
+ const handleBackToSettings = useCallback(() => {
146
+ // Navigate to settings root, replacing current history entry
147
+ navigate(urls.settings, { replace: true });
148
+ }, [navigate]);
149
+
150
+ return (
151
+ <SidebarProvider>
152
+ <div className="flex h-full w-full overflow-hidden items-start">
153
+ {/* Desktop Sidebar */}
154
+ <Sidebar className="hidden md:flex">
155
+ <SidebarContent>
156
+ {groupedRoutes.map((group) => (
157
+ <SidebarGroup key={group.title}>
158
+ <SidebarGroupLabel>{group.title}</SidebarGroupLabel>
159
+ <SidebarGroupContent>
160
+ <SidebarMenu>
161
+ {group.routes.map((item) => (
162
+ <SidebarMenuItem key={item.name}>
163
+ <SidebarMenuButton
164
+ asChild
165
+ isActive={item.name === selectedItem?.name}
166
+ >
167
+ <button
168
+ onClick={() => navigate(`${urls.settings}/${item.path}`)}
169
+ className="cursor-pointer"
170
+ >
171
+ <item.icon />
172
+ <span>{item.name}</span>
173
+ </button>
174
+ </SidebarMenuButton>
175
+ </SidebarMenuItem>
176
+ ))}
177
+ </SidebarMenu>
178
+ </SidebarGroupContent>
179
+ </SidebarGroup>
180
+ ))}
181
+ </SidebarContent>
182
+ </Sidebar>
183
+
184
+ {/* Mobile List View */}
185
+ <div className="md:hidden flex h-full w-full flex-col overflow-hidden">
186
+ {isSettingsRoot ? (
187
+ // Show list of settings pages
188
+ <div className="flex flex-1 flex-col overflow-y-auto bg-background">
189
+ <header className="flex h-16 shrink-0 items-center justify-center px-4 border-b">
190
+ <h1 className="text-lg font-semibold">{t('title')}</h1>
191
+ </header>
192
+ <div className="flex flex-1 flex-col p-4 gap-6">
193
+ {groupedRoutes.map((group) => (
194
+ <div
195
+ key={group.title}
196
+ className="flex flex-col gap-2"
197
+ >
198
+ <h2
199
+ className="text-xs font-semibold text-foreground/60 uppercase tracking-wider px-2"
200
+ style={{ fontFamily: 'var(--heading-font-family, inherit)' }}
201
+ >
202
+ {group.title}
203
+ </h2>
204
+ <div className="flex flex-col bg-card rounded-lg overflow-hidden border border-border">
205
+ {group.routes.map((item, itemIndex) => {
206
+ const Icon = item.icon;
207
+ const isLast = itemIndex === group.routes.length - 1;
208
+ return (
209
+ <div
210
+ key={item.name}
211
+ className="relative"
212
+ >
213
+ {!isLast && (
214
+ <div className="absolute left-0 right-0 bottom-0 h-[1px] bg-border" />
215
+ )}
216
+ <button
217
+ onClick={() => navigate(`${urls.settings}/${item.path}`)}
218
+ className="w-full flex items-center justify-between px-4 py-3 bg-transparent hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground transition-colors cursor-pointer rounded-none"
219
+ >
220
+ <div className="flex items-center gap-2 flex-1 min-w-0">
221
+ <div className="flex-shrink-0 text-foreground/70">
222
+ <Icon />
223
+ </div>
224
+ <span className="text-sm font-normal text-foreground">
225
+ {item.name}
226
+ </span>
227
+ </div>
228
+ <div className="flex-shrink-0 ml-2 text-foreground/40">
229
+ <ChevronRightIcon />
230
+ </div>
231
+ </button>
232
+ </div>
233
+ );
234
+ })}
235
+ </div>
236
+ </div>
237
+ ))}
238
+ </div>
239
+ </div>
240
+ ) : (
241
+ // Show selected settings page with back button
242
+ <div className="flex h-full flex-1 flex-col overflow-hidden">
243
+ <header className="flex h-16 shrink-0 items-center gap-2 px-4 border-b">
244
+ <Button
245
+ variant="ghost"
246
+ size="icon"
247
+ onClick={handleBackToSettings}
248
+ className="mr-2"
249
+ >
250
+ <ChevronLeftIcon />
251
+ </Button>
252
+ <h1 className="text-lg font-semibold">{selectedItem?.name}</h1>
253
+ </header>
254
+ <div className="flex flex-1 flex-col gap-4 overflow-y-auto p-4 pt-4">
255
+ <Routes>
256
+ <Route
257
+ index
258
+ element={
259
+ filteredRoutes.length > 0 ? (
260
+ <Navigate
261
+ to={`${urls.settings}/${filteredRoutes[0].path}`}
262
+ replace
263
+ />
264
+ ) : null
265
+ }
266
+ />
267
+ {filteredRoutes.map((item) => (
268
+ <Route
269
+ key={item.path}
270
+ path={item.path}
271
+ element={item.element}
272
+ />
273
+ ))}
274
+ </Routes>
275
+ </div>
276
+ </div>
277
+ )}
278
+ </div>
279
+
280
+ {/* Desktop Main Content */}
281
+ <main className="hidden md:flex h-full flex-1 flex-col overflow-hidden">
282
+ {selectedItem && (
283
+ <header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear">
284
+ <div className="flex items-center gap-2 px-4">
285
+ <Breadcrumb>
286
+ <BreadcrumbList>
287
+ <BreadcrumbItem>{t('title')}</BreadcrumbItem>
288
+ <BreadcrumbSeparator />
289
+ <BreadcrumbItem>
290
+ <BreadcrumbPage>{selectedItem.name}</BreadcrumbPage>
291
+ </BreadcrumbItem>
292
+ </BreadcrumbList>
293
+ </Breadcrumb>
294
+ </div>
295
+ </header>
296
+ )}
297
+ <div className="flex flex-1 flex-col gap-4 overflow-y-auto p-4 pt-0">
298
+ <Routes>
299
+ <Route
300
+ index
301
+ element={
302
+ <div className="flex flex-1 flex-col items-center justify-center p-8 text-center">
303
+ <div className="max-w-md">
304
+ <h2 className="text-lg font-semibold mb-2">{t('title')}</h2>
305
+ <p className="text-sm text-muted-foreground">
306
+ {t('selectCategory', {
307
+ defaultValue: 'Select a category from the sidebar to get started.',
308
+ })}
309
+ </p>
310
+ </div>
311
+ </div>
312
+ }
313
+ />
314
+ {filteredRoutes.map((item) => (
315
+ <Route
316
+ key={item.path}
317
+ path={item.path}
318
+ element={item.element}
319
+ />
320
+ ))}
321
+ </Routes>
322
+ </div>
323
+ </main>
324
+ </div>
325
+ </SidebarProvider>
326
+ );
327
+ };
@@ -0,0 +1,128 @@
1
+ import { useTranslation } from 'react-i18next';
2
+ import { Switch } from '@/components/ui/switch';
3
+ import { useConfig } from '@/features/config/useConfig';
4
+ import { closeSentry, initSentry } from '@/features/sentry/initSentry';
5
+ import { useSettings } from '../hooks/useSettings';
6
+ import { Button } from '@/components/ui/button';
7
+ import { shellui } from '@shellui/sdk';
8
+
9
+ export const Advanced = () => {
10
+ const { t } = useTranslation('settings');
11
+ const { config } = useConfig();
12
+ const { settings, updateSetting, resetAllData } = useSettings();
13
+ const errorReportingConfigured = Boolean(config?.sentry?.dsn);
14
+
15
+ const handleErrorReportingChange = (checked: boolean) => {
16
+ updateSetting('errorReporting', { enabled: checked });
17
+ if (checked) {
18
+ initSentry();
19
+ } else {
20
+ closeSentry();
21
+ }
22
+ };
23
+
24
+ const handleResetClick = () => {
25
+ shellui.dialog({
26
+ title: t('advanced.dangerZone.reset.toast.title'),
27
+ description: t('advanced.dangerZone.reset.toast.description'),
28
+ mode: 'delete',
29
+ size: 'sm',
30
+ okLabel: t('advanced.dangerZone.reset.toast.confirm'),
31
+ cancelLabel: t('advanced.dangerZone.reset.toast.cancel'),
32
+ onOk: () => {
33
+ resetAllData();
34
+ shellui.toast({
35
+ title: t('advanced.dangerZone.reset.toast.success.title'),
36
+ description: t('advanced.dangerZone.reset.toast.success.description'),
37
+ type: 'success',
38
+ });
39
+ shellui.navigate('/');
40
+ },
41
+ onCancel: () => {
42
+ // User cancelled, no action needed
43
+ },
44
+ });
45
+ };
46
+
47
+ return (
48
+ <div className="space-y-4">
49
+ <div className="flex items-center justify-between">
50
+ <div className="space-y-0.5">
51
+ <span
52
+ className="text-sm font-medium leading-none"
53
+ style={{ fontFamily: 'var(--heading-font-family, inherit)' }}
54
+ >
55
+ {t('advanced.errorReporting.label')}
56
+ </span>
57
+ <p className="text-sm text-muted-foreground">
58
+ {errorReportingConfigured
59
+ ? t('advanced.errorReporting.statusConfigured')
60
+ : t('advanced.errorReporting.statusNotConfigured')}
61
+ </p>
62
+ </div>
63
+ {errorReportingConfigured && (
64
+ <Switch
65
+ id="error-reporting"
66
+ checked={settings.errorReporting.enabled}
67
+ onCheckedChange={handleErrorReportingChange}
68
+ />
69
+ )}
70
+ </div>
71
+
72
+ <div className="flex items-center justify-between">
73
+ <div className="space-y-0.5">
74
+ <label
75
+ htmlFor="developer-features"
76
+ className="text-sm font-medium leading-none"
77
+ style={{ fontFamily: 'var(--heading-font-family, inherit)' }}
78
+ >
79
+ {t('advanced.developerFeatures.label')}
80
+ </label>
81
+ <p className="text-sm text-muted-foreground">
82
+ {t('advanced.developerFeatures.description')}
83
+ </p>
84
+ </div>
85
+ <Switch
86
+ id="developer-features"
87
+ checked={settings.developerFeatures.enabled}
88
+ onCheckedChange={(checked) => updateSetting('developerFeatures', { enabled: checked })}
89
+ />
90
+ </div>
91
+
92
+ <div className="border-t pt-6 mt-6">
93
+ <div className="space-y-4">
94
+ <div className="space-y-0.5">
95
+ <h3
96
+ className="text-sm font-semibold leading-none text-destructive"
97
+ style={{ fontFamily: 'var(--heading-font-family, inherit)' }}
98
+ >
99
+ {t('advanced.dangerZone.title')}
100
+ </h3>
101
+ <p className="text-sm text-muted-foreground">{t('advanced.dangerZone.description')}</p>
102
+ </div>
103
+
104
+ <div className="space-y-2">
105
+ <div className="space-y-0.5">
106
+ <label
107
+ className="text-sm font-medium leading-none"
108
+ style={{ fontFamily: 'var(--heading-font-family, inherit)' }}
109
+ >
110
+ {t('advanced.dangerZone.reset.title')}
111
+ </label>
112
+ <p className="text-sm text-muted-foreground">
113
+ {t('advanced.dangerZone.reset.warning')}
114
+ </p>
115
+ </div>
116
+ <Button
117
+ variant="destructive"
118
+ onClick={handleResetClick}
119
+ className="w-full sm:w-auto"
120
+ >
121
+ {t('advanced.dangerZone.reset.button')}
122
+ </Button>
123
+ </div>
124
+ </div>
125
+ </div>
126
+ </div>
127
+ );
128
+ };