@camstack/addon-admin-ui 0.1.2 → 0.1.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 (127) hide show
  1. package/dist/assets/index-BoVZEQ1j.js +598 -0
  2. package/dist/assets/index-DwSc8ann.css +1 -0
  3. package/{index.html → dist/index.html} +3 -1
  4. package/dist/server/addon.d.ts +11 -0
  5. package/dist/server/addon.js +50 -0
  6. package/dist/server/addon.js.map +1 -0
  7. package/package.json +4 -1
  8. package/src/App.tsx +0 -71
  9. package/src/components/addons/AddonCard.tsx +0 -355
  10. package/src/components/addons/AddonUploadZone.tsx +0 -69
  11. package/src/components/addons/CapabilityBadge.tsx +0 -55
  12. package/src/components/addons/CapabilityMap.tsx +0 -133
  13. package/src/components/addons/UpdatesList.tsx +0 -108
  14. package/src/components/agents/AgentCard.tsx +0 -281
  15. package/src/components/agents/AgentLogs.tsx +0 -231
  16. package/src/components/agents/ProcessList.tsx +0 -127
  17. package/src/components/agents/ProcessTree.tsx +0 -369
  18. package/src/components/agents/TaskList.tsx +0 -68
  19. package/src/components/cameras/CameraCard.tsx +0 -60
  20. package/src/components/cameras/LiveEventsPanel.tsx +0 -91
  21. package/src/components/cameras/ProviderSection.tsx +0 -50
  22. package/src/components/cameras/StreamArea.tsx +0 -107
  23. package/src/components/cameras/tabs/AddonsTab.tsx +0 -113
  24. package/src/components/cameras/tabs/CameraEventsTab.tsx +0 -129
  25. package/src/components/cameras/tabs/PipelineTab.tsx +0 -118
  26. package/src/components/cameras/tabs/StreamsTab.tsx +0 -114
  27. package/src/components/dashboard/BlockPicker.tsx +0 -54
  28. package/src/components/dashboard/BlockWrapper.tsx +0 -97
  29. package/src/components/dashboard/DashboardGrid.tsx +0 -160
  30. package/src/components/dashboard/block-registry.ts +0 -15
  31. package/src/components/dashboard/blocks/PipelineStagesBlock.tsx +0 -39
  32. package/src/components/dashboard/blocks/StorageBlock.tsx +0 -66
  33. package/src/components/dashboard/blocks/SystemStatusBlock.tsx +0 -67
  34. package/src/components/dashboard/blocks/index.ts +0 -32
  35. package/src/components/device/DeviceHeader.tsx +0 -116
  36. package/src/components/device/FloatingPanel.tsx +0 -132
  37. package/src/components/device/FloatingPanelManager.tsx +0 -167
  38. package/src/components/device/PanelContent.tsx +0 -196
  39. package/src/components/device/QuickConfigWizard.tsx +0 -507
  40. package/src/components/device/tabs/DetectionConfigTab.tsx +0 -96
  41. package/src/components/device/tabs/EventsTab.tsx +0 -19
  42. package/src/components/device/tabs/LogsTab.tsx +0 -22
  43. package/src/components/device/tabs/OverviewTab.tsx +0 -104
  44. package/src/components/device/tabs/ProviderSettingsTab.tsx +0 -34
  45. package/src/components/device/tabs/RecordingTab.tsx +0 -47
  46. package/src/components/device/tabs/ReplTab.tsx +0 -153
  47. package/src/components/device/tabs/TrackTrailTab.tsx +0 -49
  48. package/src/components/device/tabs/ZonesTab.tsx +0 -98
  49. package/src/components/device/zone-editor/ZoneCanvas.tsx +0 -354
  50. package/src/components/device/zone-editor/ZoneForm.tsx +0 -128
  51. package/src/components/device/zone-editor/ZoneList.tsx +0 -150
  52. package/src/components/form-builder/FormBuilder.tsx +0 -135
  53. package/src/components/form-builder/FormField.tsx +0 -732
  54. package/src/components/form-builder/ModelSelector.tsx +0 -239
  55. package/src/components/integrations/AddDeviceDialog.tsx +0 -205
  56. package/src/components/integrations/CompactDeviceCard.tsx +0 -35
  57. package/src/components/integrations/DeviceCard.tsx +0 -29
  58. package/src/components/integrations/DeviceDiscoveryStep.tsx +0 -105
  59. package/src/components/integrations/DeviceGrid.tsx +0 -79
  60. package/src/components/integrations/DeviceGroupHeader.tsx +0 -17
  61. package/src/components/integrations/DiscoveredDeviceCard.tsx +0 -26
  62. package/src/components/integrations/IntegrationCard.tsx +0 -40
  63. package/src/components/integrations/IntegrationWizard.tsx +0 -172
  64. package/src/components/integrations/ProviderConfigForm.tsx +0 -89
  65. package/src/components/integrations/ProviderPicker.tsx +0 -91
  66. package/src/components/integrations/SnapshotPopover.tsx +0 -68
  67. package/src/components/metrics/AgentLoad.tsx +0 -105
  68. package/src/components/metrics/IntegrationUsage.tsx +0 -73
  69. package/src/components/metrics/PipelineStatus.tsx +0 -74
  70. package/src/components/metrics/ProcessResources.tsx +0 -123
  71. package/src/components/pipeline/PhaseSettings.tsx +0 -131
  72. package/src/components/shared/CapabilityBadges.tsx +0 -30
  73. package/src/components/shared/ProviderIcon.tsx +0 -42
  74. package/src/components/shared/StatusBadge.tsx +0 -23
  75. package/src/components/shared/WebRtcPlayer.tsx +0 -211
  76. package/src/components/timeline/EventMarker.tsx +0 -32
  77. package/src/components/timeline/TimelineBar.tsx +0 -131
  78. package/src/components/ui/ConfirmDialog.tsx +0 -115
  79. package/src/components/ui/ToastContainer.tsx +0 -92
  80. package/src/contexts/auth-context.tsx +0 -91
  81. package/src/hooks/useBackendClient.ts +0 -6
  82. package/src/hooks/useTheme.ts +0 -1
  83. package/src/i18n/en.json +0 -164
  84. package/src/i18n/index.ts +0 -29
  85. package/src/i18n/it.json +0 -164
  86. package/src/index.css +0 -63
  87. package/src/layouts/AddonPageLoader.tsx +0 -120
  88. package/src/layouts/AppLayout.tsx +0 -254
  89. package/src/layouts/ProtectedRoute.tsx +0 -25
  90. package/src/lib/addon-page-context.ts +0 -29
  91. package/src/lib/backend.ts +0 -16
  92. package/src/main.tsx +0 -21
  93. package/src/pages/AccessDenied.tsx +0 -22
  94. package/src/pages/Cameras.tsx +0 -127
  95. package/src/pages/Dashboard.tsx +0 -6
  96. package/src/pages/DeviceDetail.tsx +0 -175
  97. package/src/pages/IntegrationDetail.tsx +0 -222
  98. package/src/pages/Integrations.tsx +0 -333
  99. package/src/pages/Login.tsx +0 -106
  100. package/src/pages/Metrics.tsx +0 -18
  101. package/src/pages/PipelineConfig.tsx +0 -282
  102. package/src/pages/Showroom.tsx +0 -351
  103. package/src/pages/Timeline.tsx +0 -269
  104. package/src/pages/system/Addons.tsx +0 -396
  105. package/src/pages/system/Agents.tsx +0 -362
  106. package/src/pages/system/Logs.tsx +0 -131
  107. package/src/pages/system/Models.tsx +0 -102
  108. package/src/pages/system/Processes.tsx +0 -129
  109. package/src/pages/system/Repl.tsx +0 -148
  110. package/src/pages/system/Settings.tsx +0 -168
  111. package/src/pages/system/Users.tsx +0 -174
  112. package/src/server/addon.ts +0 -54
  113. package/src/types/config-ui.ts +0 -28
  114. package/src/types/dashboard.ts +0 -39
  115. package/tsconfig.json +0 -29
  116. package/tsconfig.server.json +0 -16
  117. package/tsup.config.ts +0 -20
  118. package/vite.config.ts +0 -68
  119. /package/{public → dist}/brand/logo-dark.svg +0 -0
  120. /package/{public → dist}/brand/logo-horizontal-dark.svg +0 -0
  121. /package/{public → dist}/brand/logo-horizontal-light.svg +0 -0
  122. /package/{public → dist}/brand/logo-light.svg +0 -0
  123. /package/{public → dist}/brand/logo-wide-dark.svg +0 -0
  124. /package/{public → dist}/brand/logo-wide-light.svg +0 -0
  125. /package/{public → dist}/favicon.svg +0 -0
  126. /package/{public → dist}/vendor/react-jsx-runtime.mjs +0 -0
  127. /package/{public → dist}/vendor/react.mjs +0 -0
package/src/i18n/en.json DELETED
@@ -1,164 +0,0 @@
1
- {
2
- "nav": {
3
- "dashboard": "Dashboard",
4
- "integrations": "Integrations",
5
- "metrics": "Metrics"
6
- },
7
- "system": {
8
- "title": "System",
9
- "addons": "Addons",
10
- "agents": "Agents",
11
- "processes": "Processes",
12
- "logs": "Logs",
13
- "users": "Users",
14
- "settings": "Settings",
15
- "repl": "REPL",
16
- "models": "Models"
17
- },
18
- "common": {
19
- "save": "Save",
20
- "cancel": "Cancel",
21
- "delete": "Delete",
22
- "enable": "Enable",
23
- "disable": "Disable",
24
- "edit": "Edit",
25
- "add": "Add",
26
- "remove": "Remove",
27
- "confirm": "Confirm",
28
- "close": "Close",
29
- "back": "Back",
30
- "next": "Next",
31
- "search": "Search",
32
- "filter": "Filter",
33
- "reset": "Reset",
34
- "apply": "Apply",
35
- "submit": "Submit",
36
- "loading": "Loading…",
37
- "error": "Error",
38
- "success": "Success",
39
- "warning": "Warning",
40
- "info": "Info",
41
- "noData": "No data available",
42
- "noResults": "No results found",
43
- "retry": "Retry",
44
- "refresh": "Refresh",
45
- "copy": "Copy",
46
- "copied": "Copied!",
47
- "yes": "Yes",
48
- "no": "No"
49
- },
50
- "status": {
51
- "online": "Online",
52
- "offline": "Offline",
53
- "connecting": "Connecting",
54
- "error": "Error",
55
- "disabled": "Disabled",
56
- "enabled": "Enabled",
57
- "active": "Active",
58
- "inactive": "Inactive",
59
- "running": "Running",
60
- "stopped": "Stopped",
61
- "pending": "Pending",
62
- "unknown": "Unknown"
63
- },
64
- "auth": {
65
- "login": "Login",
66
- "logout": "Logout",
67
- "username": "Username",
68
- "password": "Password",
69
- "signIn": "Sign In",
70
- "invalidCredentials": "Invalid username or password"
71
- },
72
- "theme": {
73
- "dark": "Dark",
74
- "light": "Light",
75
- "system": "System"
76
- },
77
- "language": {
78
- "select": "Language",
79
- "en": "English",
80
- "it": "Italian"
81
- },
82
- "pages": {
83
- "dashboard": {
84
- "title": "Dashboard",
85
- "subtitle": "Overview of all devices and their status"
86
- },
87
- "integrations": {
88
- "title": "Integrations",
89
- "subtitle": "Manage camera sources and provider connections"
90
- },
91
- "metrics": {
92
- "title": "Metrics",
93
- "subtitle": "System performance and usage statistics"
94
- },
95
- "agents": {
96
- "title": "Agents",
97
- "subtitle": "Manage distributed processing agents"
98
- },
99
- "addons": {
100
- "title": "Addons",
101
- "subtitle": "Extend functionality with addon modules"
102
- },
103
- "settings": {
104
- "title": "Settings",
105
- "subtitle": "Configure system-wide preferences"
106
- },
107
- "users": {
108
- "title": "Users",
109
- "subtitle": "Manage user accounts and permissions"
110
- },
111
- "logs": {
112
- "title": "Logs",
113
- "subtitle": "View system and device logs"
114
- }
115
- },
116
- "integrations": {
117
- "preview": "Preview",
118
- "snapshotUnavailable": "Snapshot unavailable",
119
- "noSnapshot": "No snapshot",
120
- "lastSnapshot": "Last snapshot:",
121
- "offline": "Offline",
122
- "active": "Active",
123
- "available": "Available",
124
- "noDevices": "No devices.",
125
- "addFirstDevice": "Add your first device to get started.",
126
- "import": "+ Import",
127
- "noProviders": "No providers available. Install a provider addon.",
128
- "manage": "Manage",
129
- "integrationName": "Integration name",
130
- "integrationNamePlaceholder": "e.g. Garage Server",
131
- "connectionSuccess": "Connection successful!",
132
- "errorPrefix": "Error:",
133
- "back": "Back",
134
- "testing": "Testing...",
135
- "testConnection": "Test connection",
136
- "forward": "Next",
137
- "searchingDevices": "Searching for devices...",
138
- "foundDevices": "Found {{count}} devices. Select the ones to import.",
139
- "noDevicesFound": "No devices found.",
140
- "skip": "Skip",
141
- "importSelected": "Import selected",
142
- "newIntegration": "New Integration",
143
- "configureIntegration": "Configure Integration",
144
- "importDevices": "Import Devices",
145
- "addDevice": "Add device",
146
- "name": "Name",
147
- "namePlaceholder": "e.g. Entrance Camera",
148
- "snapshotUrlOptional": "Snapshot URL (optional)",
149
- "streamUrls": "Stream URLs",
150
- "addStream": "Add stream",
151
- "cancel": "Cancel",
152
- "saving": "Saving...",
153
- "save": "Save",
154
- "saveAndNew": "Save and new",
155
- "test": "Test",
156
- "stream": "Stream {{number}}:",
157
- "devices": "devices",
158
- "rediscover": "Rediscover",
159
- "config": "Config",
160
- "integrationNotFound": "Integration not found.",
161
- "configComingSoon": "Config coming soon.",
162
- "providerConfig": "Provider Config"
163
- }
164
- }
package/src/i18n/index.ts DELETED
@@ -1,29 +0,0 @@
1
- import i18n from 'i18next'
2
- import { initReactI18next } from 'react-i18next'
3
- import en from './en.json'
4
- import it from './it.json'
5
-
6
- const STORAGE_KEY = 'camstack_language'
7
-
8
- const savedLanguage = localStorage.getItem(STORAGE_KEY) ?? 'en'
9
-
10
- i18n
11
- .use(initReactI18next)
12
- .init({
13
- resources: {
14
- en: { translation: en },
15
- it: { translation: it },
16
- },
17
- lng: savedLanguage,
18
- fallbackLng: 'en',
19
- interpolation: {
20
- escapeValue: false, // React already escapes values
21
- },
22
- })
23
-
24
- // Persist language changes
25
- i18n.on('languageChanged', (lng) => {
26
- localStorage.setItem(STORAGE_KEY, lng)
27
- })
28
-
29
- export default i18n
package/src/i18n/it.json DELETED
@@ -1,164 +0,0 @@
1
- {
2
- "nav": {
3
- "dashboard": "Dashboard",
4
- "integrations": "Integrazioni",
5
- "metrics": "Metriche"
6
- },
7
- "system": {
8
- "title": "Sistema",
9
- "addons": "Addon",
10
- "agents": "Agenti",
11
- "processes": "Processi",
12
- "logs": "Log",
13
- "users": "Utenti",
14
- "settings": "Impostazioni",
15
- "repl": "REPL",
16
- "models": "Modelli"
17
- },
18
- "common": {
19
- "save": "Salva",
20
- "cancel": "Annulla",
21
- "delete": "Elimina",
22
- "enable": "Abilita",
23
- "disable": "Disabilita",
24
- "edit": "Modifica",
25
- "add": "Aggiungi",
26
- "remove": "Rimuovi",
27
- "confirm": "Conferma",
28
- "close": "Chiudi",
29
- "back": "Indietro",
30
- "next": "Avanti",
31
- "search": "Cerca",
32
- "filter": "Filtra",
33
- "reset": "Reimposta",
34
- "apply": "Applica",
35
- "submit": "Invia",
36
- "loading": "Caricamento…",
37
- "error": "Errore",
38
- "success": "Successo",
39
- "warning": "Avviso",
40
- "info": "Info",
41
- "noData": "Nessun dato disponibile",
42
- "noResults": "Nessun risultato trovato",
43
- "retry": "Riprova",
44
- "refresh": "Aggiorna",
45
- "copy": "Copia",
46
- "copied": "Copiato!",
47
- "yes": "Sì",
48
- "no": "No"
49
- },
50
- "status": {
51
- "online": "Online",
52
- "offline": "Offline",
53
- "connecting": "Connessione in corso",
54
- "error": "Errore",
55
- "disabled": "Disabilitato",
56
- "enabled": "Abilitato",
57
- "active": "Attivo",
58
- "inactive": "Inattivo",
59
- "running": "In esecuzione",
60
- "stopped": "Fermato",
61
- "pending": "In attesa",
62
- "unknown": "Sconosciuto"
63
- },
64
- "auth": {
65
- "login": "Accesso",
66
- "logout": "Esci",
67
- "username": "Nome utente",
68
- "password": "Password",
69
- "signIn": "Accedi",
70
- "invalidCredentials": "Nome utente o password non validi"
71
- },
72
- "theme": {
73
- "dark": "Scuro",
74
- "light": "Chiaro",
75
- "system": "Sistema"
76
- },
77
- "language": {
78
- "select": "Lingua",
79
- "en": "Inglese",
80
- "it": "Italiano"
81
- },
82
- "pages": {
83
- "dashboard": {
84
- "title": "Dashboard",
85
- "subtitle": "Panoramica di tutti i dispositivi e il loro stato"
86
- },
87
- "integrations": {
88
- "title": "Integrazioni",
89
- "subtitle": "Gestisci le sorgenti video e le connessioni ai provider"
90
- },
91
- "metrics": {
92
- "title": "Metriche",
93
- "subtitle": "Statistiche di prestazioni e utilizzo del sistema"
94
- },
95
- "agents": {
96
- "title": "Agenti",
97
- "subtitle": "Gestisci gli agenti di elaborazione distribuita"
98
- },
99
- "addons": {
100
- "title": "Addon",
101
- "subtitle": "Estendi le funzionalità con moduli addon"
102
- },
103
- "settings": {
104
- "title": "Impostazioni",
105
- "subtitle": "Configura le preferenze di sistema"
106
- },
107
- "users": {
108
- "title": "Utenti",
109
- "subtitle": "Gestisci gli account utente e i permessi"
110
- },
111
- "logs": {
112
- "title": "Log",
113
- "subtitle": "Visualizza i log di sistema e dei dispositivi"
114
- }
115
- },
116
- "integrations": {
117
- "preview": "Anteprima",
118
- "snapshotUnavailable": "Snapshot non disponibile",
119
- "noSnapshot": "Nessuno snapshot",
120
- "lastSnapshot": "Ultimo snapshot:",
121
- "offline": "Offline",
122
- "active": "Attivi",
123
- "available": "Disponibili",
124
- "noDevices": "Nessun dispositivo.",
125
- "addFirstDevice": "Aggiungi il primo dispositivo per iniziare.",
126
- "import": "+ Importa",
127
- "noProviders": "Nessun provider disponibile. Installa un addon provider.",
128
- "manage": "Gestisci",
129
- "integrationName": "Nome integrazione",
130
- "integrationNamePlaceholder": "es. Server Garage",
131
- "connectionSuccess": "Connessione riuscita!",
132
- "errorPrefix": "Errore:",
133
- "back": "Indietro",
134
- "testing": "Test...",
135
- "testConnection": "Test connessione",
136
- "forward": "Avanti",
137
- "searchingDevices": "Ricerca dispositivi in corso...",
138
- "foundDevices": "Trovati {{count}} dispositivi. Seleziona quelli da importare.",
139
- "noDevicesFound": "Nessun dispositivo trovato.",
140
- "skip": "Salta",
141
- "importSelected": "Importa selezionati",
142
- "newIntegration": "Nuova Integrazione",
143
- "configureIntegration": "Configura Integrazione",
144
- "importDevices": "Importa Dispositivi",
145
- "addDevice": "Aggiungi dispositivo",
146
- "name": "Nome",
147
- "namePlaceholder": "es. Camera Ingresso",
148
- "snapshotUrlOptional": "Snapshot URL (opzionale)",
149
- "streamUrls": "Stream URLs",
150
- "addStream": "Aggiungi stream",
151
- "cancel": "Annulla",
152
- "saving": "Salvataggio...",
153
- "save": "Salva",
154
- "saveAndNew": "Salva e nuova",
155
- "test": "Test",
156
- "stream": "Stream {{number}}:",
157
- "devices": "dispositivi",
158
- "rediscover": "Riscopri",
159
- "config": "Config",
160
- "integrationNotFound": "Integrazione non trovata.",
161
- "configComingSoon": "Config coming soon.",
162
- "providerConfig": "Provider Config"
163
- }
164
- }
package/src/index.css DELETED
@@ -1,63 +0,0 @@
1
- @import "tailwindcss";
2
- @import "@camstack/ui-library/tailwind/camstack-theme.css";
3
-
4
- /* Scan ui-library source for Tailwind classes used in shared components */
5
- @source "../../ui-library/src";
6
-
7
- /* Force dark mode by default until ThemeProvider kicks in */
8
- :root {
9
- color-scheme: dark;
10
- }
11
-
12
- body {
13
- margin: 0;
14
- font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
15
- -webkit-font-smoothing: antialiased;
16
- -moz-osx-font-smoothing: grayscale;
17
- }
18
-
19
- /* No global transitions — use Tailwind transition-* utilities on specific elements */
20
-
21
- /* Override react-grid-layout default styles to match our theme */
22
- .react-grid-item.react-grid-placeholder {
23
- background-color: var(--color-primary) !important;
24
- opacity: 0.15 !important;
25
- border-radius: 8px !important;
26
- }
27
-
28
- .react-grid-item > .react-resizable-handle::after {
29
- border-right-color: var(--color-foreground-subtle) !important;
30
- border-bottom-color: var(--color-foreground-subtle) !important;
31
- }
32
-
33
- /* Scrollbar styling */
34
- ::-webkit-scrollbar {
35
- width: 6px;
36
- height: 6px;
37
- }
38
-
39
- ::-webkit-scrollbar-track {
40
- background: transparent;
41
- }
42
-
43
- ::-webkit-scrollbar-thumb {
44
- background-color: var(--color-border);
45
- border-radius: 3px;
46
- }
47
-
48
- ::-webkit-scrollbar-thumb:hover {
49
- background-color: var(--color-foreground-subtle);
50
- }
51
-
52
- /* Focus ring for keyboard accessibility */
53
- :focus-visible {
54
- outline: 2px solid var(--color-primary);
55
- outline-offset: 2px;
56
- border-radius: 4px;
57
- }
58
-
59
- /* Input placeholder color */
60
- input::placeholder,
61
- textarea::placeholder {
62
- color: var(--color-foreground-disabled);
63
- }
@@ -1,120 +0,0 @@
1
- import { useState, useEffect, Suspense, lazy, type ComponentType } from 'react'
2
- import { useParams } from 'react-router-dom'
3
- import { useQuery } from '@tanstack/react-query'
4
- import { getBackendClient } from '../lib/backend'
5
-
6
- export interface AddonPageInfo {
7
- readonly id: string
8
- readonly addonId: string
9
- readonly label: string
10
- readonly icon: string
11
- readonly path: string
12
- readonly bundle: string
13
- readonly bundleUrl: string
14
- }
15
-
16
- /** Flatten the nested tRPC response into a flat AddonPageInfo */
17
- export function flattenPages(raw: readonly unknown[]): readonly AddonPageInfo[] {
18
- return raw.map((item: any) => ({
19
- id: item.page.id,
20
- addonId: item.addonId,
21
- label: item.page.label,
22
- icon: item.page.icon,
23
- path: item.page.path,
24
- bundle: item.page.bundle,
25
- bundleUrl: item.bundleUrl,
26
- }))
27
- }
28
-
29
- /** Cache for loaded page components */
30
- const pageComponentCache = new Map<string, ComponentType<any>>()
31
-
32
- export function AddonPageLoader() {
33
- const { pagePath } = useParams<{ pagePath: string }>()
34
- const client = getBackendClient()
35
-
36
- const { data: pages } = useQuery({
37
- queryKey: ['addon-pages'],
38
- queryFn: async (): Promise<readonly AddonPageInfo[]> => {
39
- const raw = await client.trpc.addonPages.listPages.query()
40
- return flattenPages(raw as readonly unknown[])
41
- },
42
- staleTime: 60_000,
43
- })
44
-
45
- const page = pages?.find((p) => p.path === `/addon/${pagePath}`)
46
- const [PageComponent, setPageComponent] = useState<ComponentType<any> | null>(null)
47
- const [error, setError] = useState<string | null>(null)
48
-
49
- useEffect(() => {
50
- if (!page) return
51
-
52
- const cacheKey = `${page.addonId}/${page.bundle}`
53
-
54
- // Check cache
55
- const cached = pageComponentCache.get(cacheKey)
56
- if (cached) {
57
- setPageComponent(() => cached)
58
- return
59
- }
60
-
61
- // Dynamic import of the addon page module
62
- setError(null)
63
- setPageComponent(null)
64
-
65
- const url = page.bundleUrl
66
- import(/* @vite-ignore */ url)
67
- .then((mod) => {
68
- const Component = mod.default
69
- if (typeof Component !== 'function') {
70
- throw new Error(`Addon page "${page.id}" does not export a default React component`)
71
- }
72
- pageComponentCache.set(cacheKey, Component)
73
- setPageComponent(() => Component)
74
- })
75
- .catch((err) => {
76
- console.error(`[AddonPageLoader] Failed to load ${url}:`, err)
77
- setError(`Failed to load addon page: ${err.message}`)
78
- })
79
- }, [page])
80
-
81
- if (!pages) {
82
- return (
83
- <div className="flex items-center justify-center h-full">
84
- <div className="animate-spin h-6 w-6 border-2 border-primary border-t-transparent rounded-full" />
85
- </div>
86
- )
87
- }
88
-
89
- if (!page) {
90
- return (
91
- <div className="flex items-center justify-center h-full text-foreground-subtle">
92
- Addon page not found
93
- </div>
94
- )
95
- }
96
-
97
- if (error) {
98
- return (
99
- <div className="flex items-center justify-center h-full text-red-400 text-sm">
100
- {error}
101
- </div>
102
- )
103
- }
104
-
105
- if (!PageComponent) {
106
- return (
107
- <div className="flex items-center justify-center h-full">
108
- <div className="animate-spin h-6 w-6 border-2 border-primary border-t-transparent rounded-full" />
109
- </div>
110
- )
111
- }
112
-
113
- return (
114
- <PageComponent
115
- trpc={client.trpc}
116
- theme={{ isDark: document.documentElement.classList.contains('dark') }}
117
- navigate={(path: string) => { window.location.href = path }}
118
- />
119
- )
120
- }