@object-ui/app-shell 3.3.0 → 3.3.1

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 (173) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +22 -2
  3. package/dist/chrome/CommandPalette.d.ts +16 -0
  4. package/dist/chrome/CommandPalette.js +88 -0
  5. package/dist/chrome/ConditionalAuthWrapper.d.ts +23 -0
  6. package/dist/chrome/ConditionalAuthWrapper.js +135 -0
  7. package/dist/chrome/ConsoleToaster.d.ts +11 -0
  8. package/dist/chrome/ConsoleToaster.js +28 -0
  9. package/dist/chrome/ErrorBoundary.d.ts +39 -0
  10. package/dist/chrome/ErrorBoundary.js +64 -0
  11. package/dist/chrome/KeyboardShortcutsDialog.d.ts +7 -0
  12. package/dist/chrome/KeyboardShortcutsDialog.js +63 -0
  13. package/dist/chrome/LoadingScreen.d.ts +12 -0
  14. package/dist/chrome/LoadingScreen.js +44 -0
  15. package/dist/chrome/OnboardingWalkthrough.d.ts +9 -0
  16. package/dist/chrome/OnboardingWalkthrough.js +11 -0
  17. package/dist/chrome/ThemeProvider.d.ts +13 -0
  18. package/dist/chrome/ThemeProvider.js +37 -0
  19. package/dist/chrome/index.d.ts +8 -0
  20. package/dist/chrome/index.js +8 -0
  21. package/dist/console/AppContent.d.ts +26 -0
  22. package/dist/console/AppContent.js +266 -0
  23. package/dist/console/ConsoleShell.d.ts +66 -0
  24. package/dist/console/ConsoleShell.js +97 -0
  25. package/dist/console/auth/AuthPageLayout.d.ts +9 -0
  26. package/dist/console/auth/AuthPageLayout.js +4 -0
  27. package/dist/console/auth/ForgotPasswordPage.d.ts +4 -0
  28. package/dist/console/auth/ForgotPasswordPage.js +23 -0
  29. package/dist/console/auth/LoginPage.d.ts +4 -0
  30. package/dist/console/auth/LoginPage.js +24 -0
  31. package/dist/console/auth/RegisterPage.d.ts +4 -0
  32. package/dist/console/auth/RegisterPage.js +29 -0
  33. package/dist/console/auth/index.d.ts +4 -0
  34. package/dist/console/auth/index.js +4 -0
  35. package/dist/console/home/AppCard.d.ts +14 -0
  36. package/dist/console/home/AppCard.js +36 -0
  37. package/dist/console/home/HomeLayout.d.ts +15 -0
  38. package/dist/console/home/HomeLayout.js +20 -0
  39. package/dist/console/home/HomePage.d.ts +18 -0
  40. package/dist/console/home/HomePage.js +54 -0
  41. package/dist/console/home/QuickActions.d.ts +9 -0
  42. package/dist/console/home/QuickActions.js +53 -0
  43. package/dist/console/home/RecentApps.d.ts +13 -0
  44. package/dist/console/home/RecentApps.js +32 -0
  45. package/dist/console/home/StarredApps.d.ts +13 -0
  46. package/dist/console/home/StarredApps.js +29 -0
  47. package/dist/console/home/index.d.ts +11 -0
  48. package/dist/console/home/index.js +11 -0
  49. package/dist/console/organizations/CreateWorkspaceDialog.d.ts +16 -0
  50. package/dist/console/organizations/CreateWorkspaceDialog.js +71 -0
  51. package/dist/console/organizations/OrganizationsLayout.d.ts +15 -0
  52. package/dist/console/organizations/OrganizationsLayout.js +20 -0
  53. package/dist/console/organizations/OrganizationsPage.d.ts +9 -0
  54. package/dist/console/organizations/OrganizationsPage.js +76 -0
  55. package/dist/console/organizations/index.d.ts +8 -0
  56. package/dist/console/organizations/index.js +8 -0
  57. package/dist/console/organizations/manage/AcceptInvitationPage.d.ts +7 -0
  58. package/dist/console/organizations/manage/AcceptInvitationPage.js +100 -0
  59. package/dist/console/organizations/manage/InvitationsPage.d.ts +6 -0
  60. package/dist/console/organizations/manage/InvitationsPage.js +107 -0
  61. package/dist/console/organizations/manage/InviteMemberDialog.d.ts +16 -0
  62. package/dist/console/organizations/manage/InviteMemberDialog.js +83 -0
  63. package/dist/console/organizations/manage/MembersPage.d.ts +6 -0
  64. package/dist/console/organizations/manage/MembersPage.js +89 -0
  65. package/dist/console/organizations/manage/OrganizationLayout.d.ts +8 -0
  66. package/dist/console/organizations/manage/OrganizationLayout.js +51 -0
  67. package/dist/console/organizations/manage/SettingsPage.d.ts +6 -0
  68. package/dist/console/organizations/manage/SettingsPage.js +141 -0
  69. package/dist/console/organizations/manage/orgContext.d.ts +5 -0
  70. package/dist/console/organizations/manage/orgContext.js +4 -0
  71. package/dist/context/FavoritesProvider.d.ts +50 -0
  72. package/dist/context/FavoritesProvider.js +129 -0
  73. package/dist/context/NavigationContext.d.ts +46 -0
  74. package/dist/context/NavigationContext.js +59 -0
  75. package/dist/context/index.d.ts +3 -0
  76. package/dist/context/index.js +2 -0
  77. package/dist/hooks/index.d.ts +7 -0
  78. package/dist/hooks/index.js +7 -0
  79. package/dist/hooks/useFavorites.d.ts +13 -0
  80. package/dist/hooks/useFavorites.js +13 -0
  81. package/dist/hooks/useMetadataService.d.ts +13 -0
  82. package/dist/hooks/useMetadataService.js +18 -0
  83. package/dist/hooks/useNavPins.d.ts +16 -0
  84. package/dist/hooks/useNavPins.js +72 -0
  85. package/dist/hooks/useNavigationSync.d.ts +82 -0
  86. package/dist/hooks/useNavigationSync.js +495 -0
  87. package/dist/hooks/useObjectActions.d.ts +38 -0
  88. package/dist/hooks/useObjectActions.js +92 -0
  89. package/dist/hooks/useRecentItems.d.ts +21 -0
  90. package/dist/hooks/useRecentItems.js +50 -0
  91. package/dist/hooks/useResponsiveSidebar.d.ts +8 -0
  92. package/dist/hooks/useResponsiveSidebar.js +28 -0
  93. package/dist/index.d.ts +29 -0
  94. package/dist/index.js +35 -0
  95. package/dist/layout/ActivityFeed.d.ts +22 -0
  96. package/dist/layout/ActivityFeed.js +57 -0
  97. package/dist/layout/AppHeader.d.ts +33 -0
  98. package/dist/layout/AppHeader.js +182 -0
  99. package/dist/layout/AppSidebar.d.ts +14 -0
  100. package/dist/layout/AppSidebar.js +217 -0
  101. package/dist/layout/AppSwitcher.d.ts +12 -0
  102. package/dist/layout/AppSwitcher.js +39 -0
  103. package/dist/layout/AuthPageLayout.d.ts +9 -0
  104. package/dist/layout/AuthPageLayout.js +4 -0
  105. package/dist/layout/ConnectionStatus.d.ts +14 -0
  106. package/dist/layout/ConnectionStatus.js +36 -0
  107. package/dist/layout/ConsoleLayout.d.ts +21 -0
  108. package/dist/layout/ConsoleLayout.js +71 -0
  109. package/dist/layout/LocaleSwitcher.d.ts +7 -0
  110. package/dist/layout/LocaleSwitcher.js +27 -0
  111. package/dist/layout/ModeToggle.d.ts +1 -0
  112. package/dist/layout/ModeToggle.js +10 -0
  113. package/dist/layout/UnifiedSidebar.d.ts +22 -0
  114. package/dist/layout/UnifiedSidebar.js +210 -0
  115. package/dist/layout/index.d.ts +11 -0
  116. package/dist/layout/index.js +10 -0
  117. package/dist/providers/AdapterProvider.d.ts +21 -0
  118. package/dist/providers/AdapterProvider.js +52 -0
  119. package/dist/providers/ExpressionProvider.d.ts +50 -0
  120. package/dist/providers/ExpressionProvider.js +67 -0
  121. package/dist/providers/MetadataProvider.d.ts +18 -0
  122. package/dist/providers/MetadataProvider.js +324 -0
  123. package/dist/services/MetadataService.d.ts +119 -0
  124. package/dist/services/MetadataService.js +211 -0
  125. package/dist/skeletons/SkeletonDashboard.d.ts +13 -0
  126. package/dist/skeletons/SkeletonDashboard.js +12 -0
  127. package/dist/skeletons/SkeletonDetail.d.ts +15 -0
  128. package/dist/skeletons/SkeletonDetail.js +12 -0
  129. package/dist/skeletons/SkeletonGrid.d.ts +15 -0
  130. package/dist/skeletons/SkeletonGrid.js +12 -0
  131. package/dist/skeletons/index.d.ts +3 -0
  132. package/dist/skeletons/index.js +3 -0
  133. package/dist/utils/getIcon.d.ts +10 -0
  134. package/dist/utils/getIcon.js +24 -0
  135. package/dist/utils/index.d.ts +33 -0
  136. package/dist/utils/index.js +62 -0
  137. package/dist/utils/metadataConverters.d.ts +78 -0
  138. package/dist/utils/metadataConverters.js +74 -0
  139. package/dist/views/ActionConfirmDialog.d.ts +22 -0
  140. package/dist/views/ActionConfirmDialog.js +22 -0
  141. package/dist/views/ActionParamDialog.d.ts +23 -0
  142. package/dist/views/ActionParamDialog.js +67 -0
  143. package/dist/views/DashboardView.d.ts +9 -0
  144. package/dist/views/DashboardView.js +357 -0
  145. package/dist/views/DesignDrawer.d.ts +28 -0
  146. package/dist/views/DesignDrawer.js +51 -0
  147. package/dist/views/MetadataInspector.d.ts +40 -0
  148. package/dist/views/MetadataInspector.js +69 -0
  149. package/dist/views/ObjectView.d.ts +14 -0
  150. package/dist/views/ObjectView.js +770 -0
  151. package/dist/views/PageView.d.ts +6 -0
  152. package/dist/views/PageView.js +49 -0
  153. package/dist/views/RecordDetailView.d.ts +14 -0
  154. package/dist/views/RecordDetailView.js +508 -0
  155. package/dist/views/ReportView.d.ts +4 -0
  156. package/dist/views/ReportView.js +284 -0
  157. package/dist/views/SearchResultsPage.d.ts +9 -0
  158. package/dist/views/SearchResultsPage.js +107 -0
  159. package/dist/views/ViewConfigPanel.d.ts +56 -0
  160. package/dist/views/ViewConfigPanel.js +91 -0
  161. package/dist/views/index.d.ts +11 -0
  162. package/dist/views/index.js +11 -0
  163. package/package.json +59 -11
  164. package/.turbo/turbo-build.log +0 -4
  165. package/src/components/AppShell.tsx +0 -31
  166. package/src/components/DashboardRenderer.tsx +0 -36
  167. package/src/components/FormRenderer.tsx +0 -71
  168. package/src/components/ObjectRenderer.tsx +0 -122
  169. package/src/components/PageRenderer.tsx +0 -28
  170. package/src/index.ts +0 -20
  171. package/src/types.ts +0 -78
  172. package/tsconfig.json +0 -20
  173. package/tsconfig.tsbuildinfo +0 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,45 @@
1
+ # @object-ui/app-shell — Changelog
2
+
3
+ ## 3.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - b429568: chore(examples): relocate console templates under `examples/`
8
+
9
+ The fork-ready ObjectStack console template moved from `apps/console-starter`
10
+ to `examples/console-starter`, so `apps/` only contains real deployable
11
+ products (`console`, `site`). The third-party integration demo
12
+ `examples/minimal-console` was renamed to `examples/byo-backend-console`
13
+ to make its "bring-your-own backend" purpose explicit and to remove the
14
+ naming collision with the starter template. Source comments and READMEs in
15
+ `@object-ui/app-shell` and `@object-ui/components` have been updated to
16
+ point at the new paths; no runtime behaviour changed. A new
17
+ `examples/README.md` provides a "which example should I use?" selector.
18
+
19
+ - Updated dependencies [b429568]
20
+ - @object-ui/components@3.3.1
21
+ - @object-ui/fields@3.3.1
22
+ - @object-ui/layout@3.3.1
23
+ - @object-ui/plugin-calendar@3.3.1
24
+ - @object-ui/plugin-charts@3.3.1
25
+ - @object-ui/plugin-chatbot@3.3.1
26
+ - @object-ui/plugin-dashboard@3.3.1
27
+ - @object-ui/plugin-designer@3.3.1
28
+ - @object-ui/plugin-detail@3.3.1
29
+ - @object-ui/plugin-form@3.3.1
30
+ - @object-ui/plugin-grid@3.3.1
31
+ - @object-ui/plugin-kanban@3.3.1
32
+ - @object-ui/plugin-list@3.3.1
33
+ - @object-ui/plugin-report@3.3.1
34
+ - @object-ui/plugin-view@3.3.1
35
+ - @object-ui/types@3.3.1
36
+ - @object-ui/core@3.3.1
37
+ - @object-ui/i18n@3.3.1
38
+ - @object-ui/react@3.3.1
39
+ - @object-ui/data-objectstack@3.3.1
40
+ - @object-ui/auth@3.3.1
41
+ - @object-ui/permissions@3.3.1
42
+ - @object-ui/collaboration@3.3.1
43
+
44
+ All notable changes to this package will be documented in this file.
45
+ See the [monorepo CHANGELOG](../../CHANGELOG.md) for cross-package release notes.
package/README.md CHANGED
@@ -168,13 +168,33 @@ Third-Party App
168
168
 
169
169
  ## Examples
170
170
 
171
- See `examples/minimal-console` for a complete working example that demonstrates:
171
+ See `examples/byo-backend-console` for a complete working example that demonstrates:
172
172
  - Custom routing with React Router
173
173
  - Custom data adapter (not ObjectStack)
174
174
  - Custom authentication
175
175
  - Cherry-picking only needed components
176
176
  - Building a console in ~100 lines of code
177
177
 
178
+ <!-- release-metadata:v3.3.0 -->
179
+
180
+ ## Compatibility
181
+
182
+ - **React:** 18.x or 19.x
183
+ - **Node.js:** ≥ 18
184
+ - **TypeScript:** ≥ 5.0 (strict mode)
185
+ - **`@objectstack/spec`:** ^3.3.0
186
+ - **`@objectstack/client`:** ^3.3.0
187
+ - **Tailwind CSS:** ≥ 3.4 (for packages with UI)
188
+
189
+ ## Links
190
+
191
+ - 📚 [Documentation](https://www.objectui.org/docs/layout/app-shell)
192
+ - 📦 [npm package](https://www.npmjs.com/package/@object-ui/app-shell)
193
+ - 📝 [Changelog](./CHANGELOG.md)
194
+ - 🐛 [Report an issue](https://github.com/objectstack-ai/objectui/issues)
195
+ - 🤝 [Contributing Guide](https://github.com/objectstack-ai/objectui/blob/main/CONTRIBUTING.md)
196
+ - 🗺️ [Roadmap](https://github.com/objectstack-ai/objectui/blob/main/ROADMAP.md)
197
+
178
198
  ## License
179
199
 
180
- MIT
200
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,16 @@
1
+ /**
2
+ * CommandPalette
3
+ *
4
+ * A ⌘+K (Ctrl+K) command palette for quick navigation across apps, objects,
5
+ * dashboards, pages, reports, and global actions.
6
+ *
7
+ * Uses Shadcn's Command (cmdk) component — keyboard-accessible, fuzzy search.
8
+ */
9
+ interface CommandPaletteProps {
10
+ apps: any[];
11
+ activeApp: any;
12
+ objects: any[];
13
+ onAppChange: (name: string) => void;
14
+ }
15
+ export declare function CommandPalette({ apps, activeApp, objects: _objects, onAppChange }: CommandPaletteProps): import("react/jsx-runtime").JSX.Element;
16
+ export {};
@@ -0,0 +1,88 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * CommandPalette
4
+ *
5
+ * A ⌘+K (Ctrl+K) command palette for quick navigation across apps, objects,
6
+ * dashboards, pages, reports, and global actions.
7
+ *
8
+ * Uses Shadcn's Command (cmdk) component — keyboard-accessible, fuzzy search.
9
+ */
10
+ import { useEffect, useState, useCallback } from 'react';
11
+ import { useNavigate, useParams } from 'react-router-dom';
12
+ import * as LucideIcons from 'lucide-react';
13
+ import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, } from '@object-ui/components';
14
+ import { LayoutDashboard, FileText, BarChart3, Database, Moon, Sun, Monitor, Search, Plus, } from 'lucide-react';
15
+ import { useTheme } from './ThemeProvider';
16
+ import { useExpressionContext, evaluateVisibility } from '../providers/ExpressionProvider';
17
+ import { useObjectTranslation } from '@object-ui/i18n';
18
+ import { resolveI18nLabel } from '../utils';
19
+ /** Resolve a Lucide icon by name (kebab-case or PascalCase) */
20
+ function getIcon(name) {
21
+ if (!name)
22
+ return Database;
23
+ if (LucideIcons[name])
24
+ return LucideIcons[name];
25
+ const pascal = name
26
+ .split('-')
27
+ .map(p => p.charAt(0).toUpperCase() + p.slice(1))
28
+ .join('');
29
+ if (LucideIcons[pascal])
30
+ return LucideIcons[pascal];
31
+ return Database;
32
+ }
33
+ export function CommandPalette({ apps, activeApp, objects: _objects, onAppChange }) {
34
+ const [open, setOpen] = useState(false);
35
+ const navigate = useNavigate();
36
+ const { appName } = useParams();
37
+ const { setTheme } = useTheme();
38
+ const { evaluator } = useExpressionContext();
39
+ const { t } = useObjectTranslation();
40
+ // ⌘+K / Ctrl+K shortcut
41
+ useEffect(() => {
42
+ const down = (e) => {
43
+ if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
44
+ e.preventDefault();
45
+ setOpen(prev => !prev);
46
+ }
47
+ };
48
+ document.addEventListener('keydown', down);
49
+ return () => document.removeEventListener('keydown', down);
50
+ }, []);
51
+ const baseUrl = `/apps/${appName || activeApp?.name}`;
52
+ const runCommand = useCallback((command) => {
53
+ setOpen(false);
54
+ command();
55
+ }, []);
56
+ // Extract navigation items from active app, filtering by visibility expressions
57
+ const navItems = flattenNavigation(activeApp?.navigation || []).filter((item) => evaluateVisibility(item.visible ?? item.visibleOn, evaluator));
58
+ return (_jsxs(CommandDialog, { open: open, onOpenChange: setOpen, children: [_jsx(CommandInput, { placeholder: t('console.commandPalette.placeholder') }), _jsxs(CommandList, { children: [_jsx(CommandEmpty, { children: t('console.commandPalette.noResults') }), navItems.filter(i => i.type === 'object').length > 0 && (_jsx(CommandGroup, { heading: t('console.commandPalette.objects'), children: navItems
59
+ .filter(i => i.type === 'object')
60
+ .map(item => {
61
+ const Icon = getIcon(item.icon);
62
+ return (_jsxs(CommandItem, { value: `object ${resolveI18nLabel(item.label, t)} ${item.objectName}`, onSelect: () => runCommand(() => navigate(`${baseUrl}/${item.objectName}`)), children: [_jsx(Icon, { className: "mr-2 h-4 w-4" }), _jsx("span", { children: resolveI18nLabel(item.label, t) })] }, item.id));
63
+ }) })), navItems.filter(i => i.type === 'dashboard').length > 0 && (_jsx(CommandGroup, { heading: t('console.commandPalette.dashboards'), children: navItems
64
+ .filter(i => i.type === 'dashboard')
65
+ .map(item => (_jsxs(CommandItem, { value: `dashboard ${resolveI18nLabel(item.label, t)} ${item.dashboardName}`, onSelect: () => runCommand(() => navigate(`${baseUrl}/dashboard/${item.dashboardName}`)), children: [_jsx(LayoutDashboard, { className: "mr-2 h-4 w-4" }), _jsx("span", { children: resolveI18nLabel(item.label, t) })] }, item.id))) })), navItems.filter(i => i.type === 'page').length > 0 && (_jsx(CommandGroup, { heading: t('console.commandPalette.pages'), children: navItems
66
+ .filter(i => i.type === 'page')
67
+ .map(item => (_jsxs(CommandItem, { value: `page ${resolveI18nLabel(item.label, t)} ${item.pageName}`, onSelect: () => runCommand(() => navigate(`${baseUrl}/page/${item.pageName}`)), children: [_jsx(FileText, { className: "mr-2 h-4 w-4" }), _jsx("span", { children: resolveI18nLabel(item.label, t) })] }, item.id))) })), navItems.filter(i => i.type === 'report').length > 0 && (_jsx(CommandGroup, { heading: t('console.commandPalette.reports'), children: navItems
68
+ .filter(i => i.type === 'report')
69
+ .map(item => (_jsxs(CommandItem, { value: `report ${resolveI18nLabel(item.label, t)} ${item.reportName}`, onSelect: () => runCommand(() => navigate(`${baseUrl}/report/${item.reportName}`)), children: [_jsx(BarChart3, { className: "mr-2 h-4 w-4" }), _jsx("span", { children: resolveI18nLabel(item.label, t) })] }, item.id))) })), apps.filter(a => a.active !== false).length > 1 && (_jsxs(_Fragment, { children: [_jsx(CommandSeparator, {}), _jsx(CommandGroup, { heading: t('console.commandPalette.switchApp'), children: apps
70
+ .filter(a => a.active !== false)
71
+ .map(app => {
72
+ const Icon = getIcon(app.icon);
73
+ return (_jsxs(CommandItem, { value: `app ${resolveI18nLabel(app.label, t)} ${app.name}`, onSelect: () => runCommand(() => onAppChange(app.name)), children: [_jsx(Icon, { className: "mr-2 h-4 w-4" }), _jsx("span", { children: resolveI18nLabel(app.label, t) }), app.name === activeApp?.name && (_jsx("span", { className: "ml-auto text-xs text-muted-foreground", children: t('console.commandPalette.current') }))] }, app.name));
74
+ }) })] })), _jsx(CommandSeparator, {}), _jsxs(CommandGroup, { heading: t('console.commandPalette.preferences'), children: [_jsxs(CommandItem, { value: "theme light", onSelect: () => runCommand(() => setTheme('light')), children: [_jsx(Sun, { className: "mr-2 h-4 w-4" }), _jsx("span", { children: t('console.commandPalette.lightTheme') })] }), _jsxs(CommandItem, { value: "theme dark", onSelect: () => runCommand(() => setTheme('dark')), children: [_jsx(Moon, { className: "mr-2 h-4 w-4" }), _jsx("span", { children: t('console.commandPalette.darkTheme') })] }), _jsxs(CommandItem, { value: "theme system", onSelect: () => runCommand(() => setTheme('system')), children: [_jsx(Monitor, { className: "mr-2 h-4 w-4" }), _jsx("span", { children: t('console.commandPalette.systemTheme') })] })] }), _jsx(CommandSeparator, {}), _jsxs(CommandGroup, { heading: t('console.commandPalette.actions'), children: [_jsxs(CommandItem, { value: "create new app application", onSelect: () => runCommand(() => navigate(`${baseUrl}/create-app`)), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), _jsx("span", { children: t('console.commandPalette.createApp') })] }), _jsxs(CommandItem, { value: "search all results full page", onSelect: () => runCommand(() => navigate(`${baseUrl}/search`)), children: [_jsx(Search, { className: "mr-2 h-4 w-4" }), _jsx("span", { children: t('console.commandPalette.openFullSearch') })] })] })] })] }));
75
+ }
76
+ /** Flatten nested navigation groups into a flat list of leaf items */
77
+ function flattenNavigation(items) {
78
+ const result = [];
79
+ for (const item of items) {
80
+ if (item.type === 'group' && item.children) {
81
+ result.push(...flattenNavigation(item.children));
82
+ }
83
+ else {
84
+ result.push(item);
85
+ }
86
+ }
87
+ return result;
88
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * ObjectUI Console - Conditional Auth Wrapper
3
+ *
4
+ * This component fetches discovery information from the server and conditionally
5
+ * enables/disables authentication based on the server's auth service status.
6
+ * Also detects preview mode from the server and configures the auth provider accordingly.
7
+ */
8
+ import { ReactNode } from 'react';
9
+ interface ConditionalAuthWrapperProps {
10
+ children: ReactNode;
11
+ authUrl: string;
12
+ }
13
+ /**
14
+ * Wrapper component that conditionally enables authentication based on server discovery.
15
+ *
16
+ * On startup it:
17
+ * 1. Calls `/api/v1/discovery` (with a 10s AbortController timeout)
18
+ * 2. Detects preview mode + auth.enabled from the response
19
+ * 3. On failure, shows the LoadingScreen in error mode with a Retry button
20
+ * (no silent fallback to "auth enabled" — the user is told the server is unreachable)
21
+ */
22
+ export declare function ConditionalAuthWrapper({ children, authUrl }: ConditionalAuthWrapperProps): import("react/jsx-runtime").JSX.Element;
23
+ export {};
@@ -0,0 +1,135 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * ObjectUI Console - Conditional Auth Wrapper
4
+ *
5
+ * This component fetches discovery information from the server and conditionally
6
+ * enables/disables authentication based on the server's auth service status.
7
+ * Also detects preview mode from the server and configures the auth provider accordingly.
8
+ */
9
+ import { useState, useEffect, useCallback } from 'react';
10
+ import { getSharedDiscovery } from '@object-ui/data-objectstack';
11
+ import { AuthProvider } from '@object-ui/auth';
12
+ import { LoadingScreen } from './LoadingScreen';
13
+ const DISCOVERY_TIMEOUT_MS = 10000;
14
+ // Bootstrap-critical UI: must render before i18n is loaded (especially when the
15
+ // server is unreachable, which is also when i18n can't load translations).
16
+ // We deliberately do NOT use useObjectTranslation here — it suspends on first
17
+ // render and would prevent the discovery fetch from ever running.
18
+ const STRINGS = {
19
+ timeout: 'Connection timed out after 10 seconds.',
20
+ serverUnreachable: (url) => `The server at ${url} is unreachable.`,
21
+ };
22
+ /**
23
+ * Wrapper component that conditionally enables authentication based on server discovery.
24
+ *
25
+ * On startup it:
26
+ * 1. Calls `/api/v1/discovery` (with a 10s AbortController timeout)
27
+ * 2. Detects preview mode + auth.enabled from the response
28
+ * 3. On failure, shows the LoadingScreen in error mode with a Retry button
29
+ * (no silent fallback to "auth enabled" — the user is told the server is unreachable)
30
+ */
31
+ export function ConditionalAuthWrapper({ children, authUrl }) {
32
+ const [authEnabled, setAuthEnabled] = useState(null);
33
+ const [previewMode, setPreviewMode] = useState(undefined);
34
+ const [isLoading, setIsLoading] = useState(true);
35
+ const [error, setError] = useState(null);
36
+ const [retrying, setRetrying] = useState(false);
37
+ const runDiscovery = useCallback(async (isRetry = false) => {
38
+ if (isRetry)
39
+ setRetrying(true);
40
+ setError(null);
41
+ const baseUrl = import.meta.env.VITE_SERVER_URL || '';
42
+ const discoveryUrl = baseUrl
43
+ ? `${baseUrl.replace(/\/$/, '')}/api/v1/discovery`
44
+ : '/api/v1/discovery';
45
+ try {
46
+ const discovery = (await getSharedDiscovery(baseUrl, async () => {
47
+ const ctrl = new AbortController();
48
+ const timeoutId = setTimeout(() => ctrl.abort(), DISCOVERY_TIMEOUT_MS);
49
+ try {
50
+ const res = await fetch(discoveryUrl, {
51
+ credentials: 'include',
52
+ signal: ctrl.signal,
53
+ });
54
+ if (!res.ok) {
55
+ throw new Error(`HTTP ${res.status}`);
56
+ }
57
+ const body = await res.json();
58
+ if (body && typeof body.success === 'boolean' && 'data' in body) {
59
+ return body.data;
60
+ }
61
+ return body;
62
+ }
63
+ catch (e) {
64
+ if (e.name === 'AbortError') {
65
+ throw new Error('timeout');
66
+ }
67
+ throw e;
68
+ }
69
+ finally {
70
+ clearTimeout(timeoutId);
71
+ }
72
+ }));
73
+ // Defensive: an empty/undefined discovery means the server didn't actually
74
+ // respond with usable data (e.g. a cached rejection that resolved as null,
75
+ // or an upstream adapter swallowed the error). Treat it as a connectivity
76
+ // failure so the user sees the error UI instead of being silently dropped
77
+ // onto a "auth enabled" code path against a non-existent server.
78
+ if (!discovery) {
79
+ throw new Error('empty discovery response');
80
+ }
81
+ if (discovery?.mode === 'preview') {
82
+ setPreviewMode({
83
+ autoLogin: discovery.previewMode?.autoLogin ?? true,
84
+ simulatedRole: discovery.previewMode?.simulatedRole ?? 'admin',
85
+ simulatedUserName: discovery.previewMode?.simulatedUserName ?? 'Preview User',
86
+ readOnly: discovery.previewMode?.readOnly ?? false,
87
+ expiresInSeconds: discovery.previewMode?.expiresInSeconds ?? 0,
88
+ bannerMessage: discovery.previewMode?.bannerMessage,
89
+ });
90
+ setAuthEnabled(false);
91
+ }
92
+ else {
93
+ const isAuthEnabled = discovery?.services?.auth?.enabled ?? true;
94
+ setAuthEnabled(isAuthEnabled);
95
+ }
96
+ setIsLoading(false);
97
+ }
98
+ catch (e) {
99
+ const err = e;
100
+ let message;
101
+ if (err.message === 'timeout') {
102
+ message = STRINGS.timeout;
103
+ }
104
+ else if (err.message?.startsWith('HTTP ')) {
105
+ message = `${STRINGS.serverUnreachable(discoveryUrl)} (${err.message})`;
106
+ }
107
+ else {
108
+ message = `${STRINGS.serverUnreachable(discoveryUrl)}${err.message ? ` (${err.message})` : ''}`;
109
+ }
110
+ console.warn('[ConditionalAuthWrapper] Discovery failed:', err);
111
+ setError(message);
112
+ // Keep isLoading true so the splash stays mounted with the error UI.
113
+ setIsLoading(true);
114
+ }
115
+ finally {
116
+ setRetrying(false);
117
+ }
118
+ }, []);
119
+ useEffect(() => {
120
+ runDiscovery();
121
+ }, [runDiscovery]);
122
+ if (isLoading) {
123
+ return (_jsx(LoadingScreen, { error: error, onRetry: error ? () => runDiscovery(true) : undefined, retrying: retrying }));
124
+ }
125
+ // If in preview mode, wrap with a preview-configured AuthProvider
126
+ if (previewMode) {
127
+ return (_jsx(AuthProvider, { authUrl: authUrl, previewMode: previewMode, children: children }));
128
+ }
129
+ // If auth is enabled, wrap with AuthProvider
130
+ if (authEnabled) {
131
+ return (_jsx(AuthProvider, { authUrl: authUrl, children: children }));
132
+ }
133
+ // If auth is disabled, wrap with a disabled AuthProvider (guest mode)
134
+ return (_jsx(AuthProvider, { authUrl: authUrl, enabled: false, children: children }));
135
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * ConsoleToaster
3
+ *
4
+ * Sonner Toaster configured for the console app. Uses the local ThemeProvider
5
+ * instead of next-themes to resolve the current color scheme.
6
+ * @module
7
+ */
8
+ import { Toaster as Sonner } from 'sonner';
9
+ type ToasterProps = React.ComponentProps<typeof Sonner>;
10
+ export declare function ConsoleToaster(props: ToasterProps): import("react/jsx-runtime").JSX.Element;
11
+ export {};
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * ConsoleToaster
4
+ *
5
+ * Sonner Toaster configured for the console app. Uses the local ThemeProvider
6
+ * instead of next-themes to resolve the current color scheme.
7
+ * @module
8
+ */
9
+ import { Toaster as Sonner } from 'sonner';
10
+ import { CircleCheck, Info, LoaderCircle, OctagonX, TriangleAlert } from 'lucide-react';
11
+ import { useTheme } from './ThemeProvider';
12
+ export function ConsoleToaster(props) {
13
+ const { theme = 'system' } = useTheme();
14
+ return (_jsx(Sonner, { theme: theme, className: "toaster group", icons: {
15
+ success: _jsx(CircleCheck, { className: "h-4 w-4" }),
16
+ info: _jsx(Info, { className: "h-4 w-4" }),
17
+ warning: _jsx(TriangleAlert, { className: "h-4 w-4" }),
18
+ error: _jsx(OctagonX, { className: "h-4 w-4" }),
19
+ loading: _jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" }),
20
+ }, toastOptions: {
21
+ classNames: {
22
+ toast: 'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
23
+ description: 'group-[.toast]:text-muted-foreground',
24
+ actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
25
+ cancelButton: 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',
26
+ },
27
+ }, ...props }));
28
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * ErrorBoundary
3
+ *
4
+ * Catches unhandled React render errors per route and displays a
5
+ * user-friendly fallback UI with a "Try Again" recovery action.
6
+ *
7
+ * Usage:
8
+ * <ErrorBoundary>
9
+ * <SomeComponent />
10
+ * </ErrorBoundary>
11
+ *
12
+ * Or with a custom fallback:
13
+ * <ErrorBoundary fallback={<MyError />}>
14
+ * <SomeComponent />
15
+ * </ErrorBoundary>
16
+ */
17
+ import { Component, type ErrorInfo, type ReactNode } from 'react';
18
+ interface ErrorBoundaryProps {
19
+ children: ReactNode;
20
+ /** Custom fallback UI. Receives error and resetErrorBoundary. */
21
+ fallback?: ReactNode | ((props: {
22
+ error: Error;
23
+ reset: () => void;
24
+ }) => ReactNode);
25
+ /** Callback when an error is caught */
26
+ onError?: (error: Error, errorInfo: ErrorInfo) => void;
27
+ }
28
+ interface ErrorBoundaryState {
29
+ hasError: boolean;
30
+ error: Error | null;
31
+ }
32
+ export declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
33
+ constructor(props: ErrorBoundaryProps);
34
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState;
35
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
36
+ resetErrorBoundary: () => void;
37
+ render(): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
38
+ }
39
+ export {};
@@ -0,0 +1,64 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * ErrorBoundary
4
+ *
5
+ * Catches unhandled React render errors per route and displays a
6
+ * user-friendly fallback UI with a "Try Again" recovery action.
7
+ *
8
+ * Usage:
9
+ * <ErrorBoundary>
10
+ * <SomeComponent />
11
+ * </ErrorBoundary>
12
+ *
13
+ * Or with a custom fallback:
14
+ * <ErrorBoundary fallback={<MyError />}>
15
+ * <SomeComponent />
16
+ * </ErrorBoundary>
17
+ */
18
+ import { Component } from 'react';
19
+ import { Button, Empty, EmptyTitle, EmptyDescription } from '@object-ui/components';
20
+ import { AlertTriangle, RotateCcw, Home } from 'lucide-react';
21
+ import { useObjectTranslation } from '@object-ui/i18n';
22
+ /** Inner fallback component that uses the i18n hook */
23
+ function DefaultErrorFallback({ error, onReset }) {
24
+ const { t } = useObjectTranslation();
25
+ return (_jsx("div", { className: "flex h-full items-center justify-center p-8", children: _jsxs("div", { className: "max-w-md text-center", children: [_jsxs(Empty, { children: [_jsx("div", { className: "mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-destructive/10", children: _jsx(AlertTriangle, { className: "h-6 w-6 text-destructive" }) }), _jsx(EmptyTitle, { children: t('console.errors.somethingWentWrong') }), _jsx(EmptyDescription, { className: "mb-4", children: error.message || t('console.errors.unexpectedError') }), _jsxs("div", { className: "flex items-center justify-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: onReset, className: "gap-2", children: [_jsx(RotateCcw, { className: "h-4 w-4" }), t('console.errors.tryAgain')] }), _jsxs(Button, { variant: "ghost", size: "sm", onClick: () => { window.location.href = '/'; }, className: "gap-2", children: [_jsx(Home, { className: "h-4 w-4" }), t('console.errors.goHome')] })] })] }), import.meta.env.DEV && (_jsxs("details", { className: "mt-6 text-left", children: [_jsx("summary", { className: "cursor-pointer text-xs text-muted-foreground", children: t('console.errors.errorDetails') }), _jsx("pre", { className: "mt-2 max-h-60 overflow-auto rounded-md border bg-muted p-3 text-xs", children: error.stack })] }))] }) }));
26
+ }
27
+ export class ErrorBoundary extends Component {
28
+ constructor(props) {
29
+ super(props);
30
+ Object.defineProperty(this, "resetErrorBoundary", {
31
+ enumerable: true,
32
+ configurable: true,
33
+ writable: true,
34
+ value: () => {
35
+ this.setState({ hasError: false, error: null });
36
+ }
37
+ });
38
+ this.state = { hasError: false, error: null };
39
+ }
40
+ static getDerivedStateFromError(error) {
41
+ return { hasError: true, error };
42
+ }
43
+ componentDidCatch(error, errorInfo) {
44
+ console.error('[ErrorBoundary] Caught error:', error, errorInfo);
45
+ this.props.onError?.(error, errorInfo);
46
+ }
47
+ render() {
48
+ if (this.state.hasError && this.state.error) {
49
+ // Custom fallback
50
+ if (this.props.fallback) {
51
+ if (typeof this.props.fallback === 'function') {
52
+ return this.props.fallback({
53
+ error: this.state.error,
54
+ reset: this.resetErrorBoundary,
55
+ });
56
+ }
57
+ return this.props.fallback;
58
+ }
59
+ // Default fallback UI
60
+ return (_jsx(DefaultErrorFallback, { error: this.state.error, onReset: this.resetErrorBoundary }));
61
+ }
62
+ return this.props.children;
63
+ }
64
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * KeyboardShortcutsDialog
3
+ *
4
+ * A dialog listing all available keyboard shortcuts, triggered by pressing "?".
5
+ * @module
6
+ */
7
+ export declare function KeyboardShortcutsDialog(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,63 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * KeyboardShortcutsDialog
4
+ *
5
+ * A dialog listing all available keyboard shortcuts, triggered by pressing "?".
6
+ * @module
7
+ */
8
+ import { useEffect, useState, useMemo } from 'react';
9
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, } from '@object-ui/components';
10
+ import { useObjectTranslation } from '@object-ui/i18n';
11
+ export function KeyboardShortcutsDialog() {
12
+ const { t } = useObjectTranslation();
13
+ const [open, setOpen] = useState(false);
14
+ const shortcutGroups = useMemo(() => [
15
+ {
16
+ title: t('console.shortcuts.groups.general'),
17
+ shortcuts: [
18
+ { keys: ['⌘', 'K'], description: t('console.shortcuts.openCommandPalette') },
19
+ { keys: ['?'], description: t('console.shortcuts.showShortcuts') },
20
+ { keys: ['Esc'], description: t('console.shortcuts.closeDialog') },
21
+ ],
22
+ },
23
+ {
24
+ title: t('console.shortcuts.groups.navigation'),
25
+ shortcuts: [
26
+ { keys: ['B'], description: t('console.shortcuts.toggleSidebar') },
27
+ { keys: ['⌘', '/'], description: t('console.shortcuts.focusSearch') },
28
+ ],
29
+ },
30
+ {
31
+ title: t('console.shortcuts.groups.dataViews'),
32
+ shortcuts: [
33
+ { keys: ['N'], description: t('console.shortcuts.createRecord') },
34
+ { keys: ['R'], description: t('console.shortcuts.refreshData') },
35
+ { keys: ['⌘', 'E'], description: t('console.shortcuts.editRecord') },
36
+ ],
37
+ },
38
+ {
39
+ title: t('console.shortcuts.groups.preferences'),
40
+ shortcuts: [
41
+ { keys: ['⌘', 'D'], description: t('console.shortcuts.toggleDarkMode') },
42
+ ],
43
+ },
44
+ ], [t]);
45
+ useEffect(() => {
46
+ function handleKeyDown(e) {
47
+ // Only trigger when not in an input/textarea/contenteditable
48
+ const target = e.target;
49
+ if (target.tagName === 'INPUT' ||
50
+ target.tagName === 'TEXTAREA' ||
51
+ target.isContentEditable) {
52
+ return;
53
+ }
54
+ if (e.key === '?' && !e.metaKey && !e.ctrlKey && !e.altKey) {
55
+ e.preventDefault();
56
+ setOpen(prev => !prev);
57
+ }
58
+ }
59
+ document.addEventListener('keydown', handleKeyDown);
60
+ return () => document.removeEventListener('keydown', handleKeyDown);
61
+ }, []);
62
+ return (_jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "sm:max-w-lg max-h-[80vh] overflow-y-auto", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: t('console.shortcuts.title') }), _jsx(DialogDescription, { children: t('console.shortcuts.description') })] }), _jsx("div", { className: "space-y-6 pt-2", children: shortcutGroups.map(group => (_jsxs("div", { children: [_jsx("h3", { className: "text-sm font-medium text-muted-foreground mb-3", children: group.title }), _jsx("div", { className: "space-y-2", children: group.shortcuts.map((shortcut, idx) => (_jsxs("div", { className: "flex items-center justify-between py-1.5", children: [_jsx("span", { className: "text-sm", children: shortcut.description }), _jsx("div", { className: "flex items-center gap-1", children: shortcut.keys.map((key, kidx) => (_jsx("kbd", { className: "inline-flex h-6 min-w-[24px] items-center justify-center rounded border bg-muted px-1.5 text-xs font-medium text-muted-foreground", children: key }, kidx))) })] }, idx))) })] }, group.title))) })] }) }));
63
+ }
@@ -0,0 +1,12 @@
1
+ interface LoadingScreenProps {
2
+ /** Optional message override */
3
+ message?: string;
4
+ /** When set, renders an error block instead of the step list */
5
+ error?: string | null;
6
+ /** Optional retry callback; renders a Retry button when provided alongside `error` */
7
+ onRetry?: () => void;
8
+ /** When true, disables the retry button and shows a spinner */
9
+ retrying?: boolean;
10
+ }
11
+ export declare function LoadingScreen({ message, error, onRetry, retrying }: LoadingScreenProps): import("react/jsx-runtime").JSX.Element;
12
+ export {};
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Spinner, Button } from '@object-ui/components';
3
+ import { Database, CheckCircle2, Loader2, AlertCircle, RefreshCw } from 'lucide-react';
4
+ import { useState, useEffect, useMemo } from 'react';
5
+ // Bootstrap-critical UI: must render before i18n is loaded (especially when the
6
+ // server is unreachable, which is also when i18n can't load translations).
7
+ // We deliberately do NOT use useObjectTranslation here — it suspends on first
8
+ // render and would prevent the splash from rendering at all on a server-down
9
+ // boot. Strings stay hardcoded English; localized error UX should happen
10
+ // post-bootstrap, after i18n has loaded successfully.
11
+ const STRINGS = {
12
+ title: 'ObjectUI Console',
13
+ initializing: 'Initializing…',
14
+ steps: {
15
+ connecting: 'Connecting to server',
16
+ loadingConfig: 'Loading configuration',
17
+ preparingWorkspace: 'Preparing workspace',
18
+ },
19
+ error: {
20
+ connectionFailed: 'Cannot connect to server',
21
+ checkServer: 'Please check your network connection or that the backend is running.',
22
+ },
23
+ actions: {
24
+ retry: 'Retry',
25
+ retrying: 'Retrying…',
26
+ },
27
+ };
28
+ export function LoadingScreen({ message, error, onRetry, retrying }) {
29
+ const [currentStep, setCurrentStep] = useState(0);
30
+ const loadingSteps = useMemo(() => [
31
+ STRINGS.steps.connecting,
32
+ STRINGS.steps.loadingConfig,
33
+ STRINGS.steps.preparingWorkspace,
34
+ ], []);
35
+ useEffect(() => {
36
+ if (message || error)
37
+ return; // skip auto-progression when overridden or in error state
38
+ const timer = setInterval(() => {
39
+ setCurrentStep((prev) => Math.min(prev + 1, loadingSteps.length - 1));
40
+ }, 1200);
41
+ return () => clearInterval(timer);
42
+ }, [message, error, loadingSteps.length]);
43
+ return (_jsx("div", { className: "flex flex-col items-center justify-center h-screen bg-background", children: _jsxs("div", { className: "flex flex-col items-center gap-6", children: [_jsxs("div", { className: "relative", children: [_jsx("div", { className: "absolute inset-0 bg-primary/20 rounded-2xl blur-xl animate-pulse" }), _jsx("div", { className: "relative bg-linear-to-br from-primary to-primary/80 p-4 rounded-2xl shadow-lg", children: _jsx(Database, { className: "h-10 w-10 text-primary-foreground" }) })] }), _jsxs("div", { className: "text-center space-y-2", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight", children: STRINGS.title }), _jsx("p", { className: "text-sm text-muted-foreground", children: STRINGS.initializing })] }), error ? (_jsxs("div", { className: "flex flex-col items-center gap-4 max-w-md w-full px-6", children: [_jsxs("div", { className: "flex flex-col items-center gap-3 p-5 rounded-lg border border-destructive/30 bg-destructive/5 w-full", children: [_jsxs("div", { className: "flex items-center gap-2 text-destructive", children: [_jsx(AlertCircle, { className: "h-5 w-5 shrink-0" }), _jsx("span", { className: "text-sm font-semibold", children: STRINGS.error.connectionFailed })] }), _jsx("p", { className: "text-xs text-muted-foreground text-center break-words", children: error }), _jsx("p", { className: "text-xs text-muted-foreground text-center", children: STRINGS.error.checkServer })] }), onRetry && (_jsx(Button, { onClick: onRetry, disabled: retrying, variant: "default", size: "sm", className: "gap-2", children: retrying ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), STRINGS.actions.retrying] })) : (_jsxs(_Fragment, { children: [_jsx(RefreshCw, { className: "h-4 w-4" }), STRINGS.actions.retry] })) }))] })) : message ? (_jsxs("div", { className: "flex items-center gap-3 px-4 py-2 bg-muted/50 rounded-full", children: [_jsx(Spinner, { className: "h-4 w-4 text-primary" }), _jsx("span", { className: "text-sm text-muted-foreground", children: message })] })) : (_jsx("div", { className: "flex flex-col gap-2 w-64", children: loadingSteps.map((step, index) => (_jsxs("div", { className: "flex items-center gap-2.5 text-sm transition-opacity duration-300", style: { opacity: index <= currentStep ? 1 : 0.3 }, children: [index < currentStep ? (_jsx(CheckCircle2, { className: "h-4 w-4 text-primary shrink-0" })) : index === currentStep ? (_jsx(Loader2, { className: "h-4 w-4 text-primary shrink-0 animate-spin" })) : (_jsx("div", { className: "h-4 w-4 rounded-full border border-muted-foreground/30 shrink-0" })), _jsx("span", { className: index <= currentStep ? 'text-foreground' : 'text-muted-foreground', children: step })] }, step))) }))] }) }));
44
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * OnboardingWalkthrough
3
+ *
4
+ * Intentionally disabled. The first-time onboarding dialog has been
5
+ * suppressed per product decision. To re-enable, restore the full
6
+ * implementation from git history (see the commit that introduced this stub).
7
+ * @module
8
+ */
9
+ export declare function OnboardingWalkthrough(): null;