@object-ui/app-shell 3.3.0 → 3.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +22 -2
  3. package/dist/chrome/CommandPalette.d.ts +16 -0
  4. package/dist/chrome/CommandPalette.js +74 -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 +202 -0
  101. package/dist/layout/AppSwitcher.d.ts +12 -0
  102. package/dist/layout/AppSwitcher.js +24 -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/ConsoleFloatingChatbot.d.ts +10 -0
  108. package/dist/layout/ConsoleFloatingChatbot.js +27 -0
  109. package/dist/layout/ConsoleLayout.d.ts +22 -0
  110. package/dist/layout/ConsoleLayout.js +49 -0
  111. package/dist/layout/LocaleSwitcher.d.ts +7 -0
  112. package/dist/layout/LocaleSwitcher.js +27 -0
  113. package/dist/layout/ModeToggle.d.ts +1 -0
  114. package/dist/layout/ModeToggle.js +10 -0
  115. package/dist/layout/UnifiedSidebar.d.ts +27 -0
  116. package/dist/layout/UnifiedSidebar.js +186 -0
  117. package/dist/layout/index.d.ts +11 -0
  118. package/dist/layout/index.js +10 -0
  119. package/dist/providers/AdapterProvider.d.ts +21 -0
  120. package/dist/providers/AdapterProvider.js +52 -0
  121. package/dist/providers/ExpressionProvider.d.ts +50 -0
  122. package/dist/providers/ExpressionProvider.js +67 -0
  123. package/dist/providers/MetadataProvider.d.ts +18 -0
  124. package/dist/providers/MetadataProvider.js +324 -0
  125. package/dist/services/MetadataService.d.ts +119 -0
  126. package/dist/services/MetadataService.js +211 -0
  127. package/dist/skeletons/SkeletonDashboard.d.ts +13 -0
  128. package/dist/skeletons/SkeletonDashboard.js +12 -0
  129. package/dist/skeletons/SkeletonDetail.d.ts +15 -0
  130. package/dist/skeletons/SkeletonDetail.js +12 -0
  131. package/dist/skeletons/SkeletonGrid.d.ts +15 -0
  132. package/dist/skeletons/SkeletonGrid.js +12 -0
  133. package/dist/skeletons/index.d.ts +3 -0
  134. package/dist/skeletons/index.js +3 -0
  135. package/dist/utils/getIcon.d.ts +24 -0
  136. package/dist/utils/getIcon.js +87 -0
  137. package/dist/utils/index.d.ts +33 -0
  138. package/dist/utils/index.js +62 -0
  139. package/dist/utils/metadataConverters.d.ts +78 -0
  140. package/dist/utils/metadataConverters.js +74 -0
  141. package/dist/views/ActionConfirmDialog.d.ts +22 -0
  142. package/dist/views/ActionConfirmDialog.js +22 -0
  143. package/dist/views/ActionParamDialog.d.ts +23 -0
  144. package/dist/views/ActionParamDialog.js +67 -0
  145. package/dist/views/DashboardView.d.ts +9 -0
  146. package/dist/views/DashboardView.js +357 -0
  147. package/dist/views/DesignDrawer.d.ts +28 -0
  148. package/dist/views/DesignDrawer.js +51 -0
  149. package/dist/views/MetadataInspector.d.ts +40 -0
  150. package/dist/views/MetadataInspector.js +69 -0
  151. package/dist/views/ObjectView.d.ts +11 -0
  152. package/dist/views/ObjectView.js +769 -0
  153. package/dist/views/PageView.d.ts +6 -0
  154. package/dist/views/PageView.js +49 -0
  155. package/dist/views/RecordDetailView.d.ts +14 -0
  156. package/dist/views/RecordDetailView.js +508 -0
  157. package/dist/views/ReportView.d.ts +4 -0
  158. package/dist/views/ReportView.js +285 -0
  159. package/dist/views/SearchResultsPage.d.ts +9 -0
  160. package/dist/views/SearchResultsPage.js +107 -0
  161. package/dist/views/ViewConfigPanel.d.ts +56 -0
  162. package/dist/views/ViewConfigPanel.js +91 -0
  163. package/dist/views/index.d.ts +11 -0
  164. package/dist/views/index.js +11 -0
  165. package/package.json +59 -11
  166. package/.turbo/turbo-build.log +0 -4
  167. package/src/components/AppShell.tsx +0 -31
  168. package/src/components/DashboardRenderer.tsx +0 -36
  169. package/src/components/FormRenderer.tsx +0 -71
  170. package/src/components/ObjectRenderer.tsx +0 -122
  171. package/src/components/PageRenderer.tsx +0 -28
  172. package/src/index.ts +0 -20
  173. package/src/types.ts +0 -78
  174. package/tsconfig.json +0 -20
  175. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,202 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * AppSidebar
4
+ *
5
+ * Collapsible sidebar navigation for the console. Delegates navigation
6
+ * rendering to `NavigationRenderer` from `@object-ui/layout` (using
7
+ * @dnd-kit drag-to-reorder and pin/unpin), while keeping Console-specific
8
+ * features: app switcher dropdown, user footer, favorites, recent items,
9
+ * and mobile swipe gesture.
10
+ * @module
11
+ */
12
+ import * as React from 'react';
13
+ import { useNavigate, Link } from 'react-router-dom';
14
+ import { Layers } from 'lucide-react';
15
+ import { getIcon as resolveIcon } from '../utils/getIcon';
16
+ import { Sidebar, SidebarHeader, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarGroupContent, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarMenuAction, SidebarInput, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup, Avatar, AvatarImage, AvatarFallback, useSidebar, } from '@object-ui/components';
17
+ import { ChevronsUpDown, Plus, Settings, LogOut, Database, Clock, Star, StarOff, Search, Pencil, ChevronRight, Home, } from 'lucide-react';
18
+ import { NavigationRenderer } from '@object-ui/layout';
19
+ import { useMetadata } from '../providers/MetadataProvider';
20
+ import { useExpressionContext, evaluateVisibility } from '../providers/ExpressionProvider';
21
+ import { useAuth, getUserInitials } from '@object-ui/auth';
22
+ import { usePermissions } from '@object-ui/permissions';
23
+ import { useRecentItems } from '../hooks/useRecentItems';
24
+ import { useFavorites } from '../hooks/useFavorites';
25
+ import { useNavPins } from '../hooks/useNavPins';
26
+ import { resolveI18nLabel } from '../utils';
27
+ import { useObjectTranslation, useObjectLabel } from '@object-ui/i18n';
28
+ // ---------------------------------------------------------------------------
29
+ // useNavOrder – localStorage-persisted drag-and-drop reorder for nav items
30
+ // ---------------------------------------------------------------------------
31
+ function useNavOrder(appName) {
32
+ const storageKey = `objectui-nav-order-${appName}`;
33
+ const [orderMap, setOrderMap] = React.useState(() => {
34
+ try {
35
+ const raw = localStorage.getItem(storageKey);
36
+ if (!raw)
37
+ return {};
38
+ const parsed = JSON.parse(raw);
39
+ // Validate shape: must be a plain object with string[] values
40
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
41
+ return {};
42
+ const result = {};
43
+ for (const [k, v] of Object.entries(parsed)) {
44
+ if (Array.isArray(v) && v.every((i) => typeof i === 'string')) {
45
+ result[k] = v;
46
+ }
47
+ }
48
+ return result;
49
+ }
50
+ catch {
51
+ return {};
52
+ }
53
+ });
54
+ const persist = React.useCallback((next) => {
55
+ setOrderMap(next);
56
+ try {
57
+ localStorage.setItem(storageKey, JSON.stringify(next));
58
+ }
59
+ catch { /* full */ }
60
+ }, [storageKey]);
61
+ /** Apply saved order to a flat list of navigation items. */
62
+ const applyOrder = React.useCallback((items) => {
63
+ const saved = orderMap['__root__'];
64
+ if (!saved)
65
+ return items;
66
+ const byId = new Map(items.map(i => [i.id, i]));
67
+ const ordered = [];
68
+ for (const id of saved) {
69
+ const item = byId.get(id);
70
+ if (item) {
71
+ ordered.push(item);
72
+ byId.delete(id);
73
+ }
74
+ }
75
+ // Append any items not in the saved order (newly added)
76
+ byId.forEach(item => ordered.push(item));
77
+ return ordered;
78
+ }, [orderMap]);
79
+ /** Persist reordered items from NavigationRenderer's onReorder callback. */
80
+ const handleReorder = React.useCallback((reorderedItems) => {
81
+ const ids = reorderedItems.map(i => i.id);
82
+ persist({ ...orderMap, __root__: ids });
83
+ }, [orderMap, persist]);
84
+ return { applyOrder, handleReorder };
85
+ }
86
+ /**
87
+ * Resolve a Lucide icon component by name string.
88
+ * Delegates to the shared lazy `getIcon` helper which returns a wrapper
89
+ * around lucide-react `DynamicIcon` so individual icons are code-split.
90
+ */
91
+ const getIcon = resolveIcon;
92
+ export function AppSidebar({ activeAppName, onAppChange }) {
93
+ const { isMobile } = useSidebar();
94
+ const { user, signOut } = useAuth();
95
+ const navigate = useNavigate();
96
+ const { t } = useObjectTranslation();
97
+ const { objectLabel: resolveNavObjectLabel } = useObjectLabel();
98
+ // Swipe-from-left-edge gesture to open sidebar on mobile
99
+ React.useEffect(() => {
100
+ const EDGE_THRESHOLD = 30;
101
+ const SWIPE_DISTANCE = 50;
102
+ let touchStartX = 0;
103
+ const handleTouchStart = (e) => {
104
+ touchStartX = e.touches[0].clientX;
105
+ };
106
+ const handleTouchEnd = (e) => {
107
+ const deltaX = e.changedTouches[0].clientX - touchStartX;
108
+ if (touchStartX < EDGE_THRESHOLD && deltaX > SWIPE_DISTANCE && isMobile) {
109
+ document.querySelector('[data-sidebar="trigger"]')?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
110
+ }
111
+ };
112
+ document.addEventListener('touchstart', handleTouchStart, { passive: true });
113
+ document.addEventListener('touchend', handleTouchEnd, { passive: true });
114
+ return () => {
115
+ document.removeEventListener('touchstart', handleTouchStart);
116
+ document.removeEventListener('touchend', handleTouchEnd);
117
+ };
118
+ }, [isMobile]);
119
+ const { recentItems } = useRecentItems();
120
+ const { favorites, removeFavorite } = useFavorites();
121
+ const { apps: metadataApps } = useMetadata();
122
+ const apps = metadataApps || [];
123
+ // Filter out inactive apps
124
+ const activeApps = apps.filter((a) => a.active !== false);
125
+ const activeApp = activeApps.find((a) => a.name === activeAppName) || activeApps[0];
126
+ // Extract branding information from spec
127
+ const logo = activeApp?.branding?.logo;
128
+ const primaryColor = activeApp?.branding?.primaryColor;
129
+ // Drag-reorder persistence via localStorage (adapted for @dnd-kit)
130
+ const { applyOrder, handleReorder } = useNavOrder(activeAppName);
131
+ // Navigation pin persistence via localStorage
132
+ const { togglePin, applyPins } = useNavPins();
133
+ // Area management — track selected area when app defines areas
134
+ const areas = activeApp?.areas || [];
135
+ const [activeAreaId, setActiveAreaId] = React.useState(() => areas.length > 0 ? areas[0].id : null);
136
+ // Reset area when app changes or areas become available
137
+ React.useEffect(() => {
138
+ if (areas.length > 0) {
139
+ setActiveAreaId(prev => areas.some((a) => a.id === prev) ? prev : areas[0].id);
140
+ }
141
+ else {
142
+ setActiveAreaId(null);
143
+ }
144
+ }, [activeAppName, areas.length]);
145
+ // Resolve navigation items: area navigation > flat navigation > empty
146
+ const activeArea = areas.find((a) => a.id === activeAreaId);
147
+ const resolvedNavigation = activeArea?.navigation || activeApp?.navigation || [];
148
+ // Apply saved order and pin state to navigation items
149
+ const processedNavigation = React.useMemo(() => {
150
+ const ordered = applyOrder(resolvedNavigation);
151
+ return applyPins(ordered);
152
+ }, [resolvedNavigation, applyOrder, applyPins]);
153
+ // Search filter state for sidebar navigation
154
+ const [navSearchQuery, setNavSearchQuery] = React.useState('');
155
+ // Recent section collapsed by default
156
+ const [recentExpanded, setRecentExpanded] = React.useState(false);
157
+ // Visibility evaluation from Console expression context
158
+ const { evaluator } = useExpressionContext();
159
+ const evalVis = React.useCallback((expr) => evaluateVisibility(expr, evaluator), [evaluator]);
160
+ // Permission check from Console permissions context
161
+ const { can } = usePermissions();
162
+ const checkPerm = React.useCallback((permissions) => permissions.every((perm) => {
163
+ const parts = perm.split(':');
164
+ const [object, action] = parts.length >= 2
165
+ ? [parts[0], parts[1]]
166
+ : [perm, 'read'];
167
+ return can(object, action);
168
+ }), [can]);
169
+ const basePath = activeApp ? `/apps/${activeAppName}` : '';
170
+ // Fallback system navigation when no active app exists — routes into the Setup app.
171
+ const systemFallbackNavigation = React.useMemo(() => [
172
+ { id: 'sys-settings', label: 'System Settings', type: 'url', url: '/apps/setup', icon: 'settings' },
173
+ { id: 'sys-apps', label: 'Applications', type: 'url', url: '/apps/setup/system/apps', icon: 'layout-grid' },
174
+ { id: 'sys-objects', label: 'Object Manager', type: 'url', url: '/apps/setup/system/metadata/object', icon: 'database' },
175
+ { id: 'sys-users', label: 'Users', type: 'url', url: '/apps/setup/system/users', icon: 'users' },
176
+ { id: 'sys-orgs', label: 'Organizations', type: 'url', url: '/apps/setup/system/organizations', icon: 'building-2' },
177
+ { id: 'sys-roles', label: 'Roles', type: 'url', url: '/apps/setup/system/roles', icon: 'shield' },
178
+ { id: 'sys-create-app', label: 'Create App', type: 'url', url: '/create-app', icon: 'plus' },
179
+ ], []);
180
+ return (_jsxs(_Fragment, { children: [_jsxs(Sidebar, { collapsible: "icon", children: [_jsx(SidebarHeader, { children: _jsx(SidebarMenu, { children: _jsx(SidebarMenuItem, { children: activeApp ? (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(SidebarMenuButton, { size: "lg", className: "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground", children: [_jsx("div", { className: "flex aspect-square size-8 items-center justify-center rounded-lg bg-primary text-primary-foreground", style: primaryColor ? { backgroundColor: primaryColor } : undefined, children: logo ? (_jsx("img", { src: logo, alt: resolveI18nLabel(activeApp.label, t), className: "size-6 object-contain" })) : (React.createElement(getIcon(activeApp.icon), { className: "size-4" })) }), _jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [_jsx("span", { className: "truncate font-semibold", children: resolveI18nLabel(activeApp.label, t) }), _jsx("span", { className: "truncate text-xs", children: resolveI18nLabel(activeApp.description, t) || `${activeApps.length} Apps Available` })] }), _jsx(ChevronsUpDown, { className: "ml-auto" })] }) }), _jsxs(DropdownMenuContent, { className: "w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg", align: "start", side: isMobile ? "bottom" : "right", sideOffset: 4, children: [_jsx(DropdownMenuLabel, { className: "text-xs text-muted-foreground", children: "Switch Application" }), activeApps.map((app) => (_jsxs(DropdownMenuItem, { onClick: () => onAppChange(app.name), className: "gap-2 p-2", children: [_jsx("div", { className: "flex size-6 items-center justify-center rounded-sm border", children: app.icon ? React.createElement(getIcon(app.icon), { className: "size-3" }) : _jsx(Database, { className: "size-3" }) }), resolveI18nLabel(app.label, t), activeApp.name === app.name && _jsx("span", { className: "ml-auto text-xs", children: "\u2713" })] }, app.name))), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { className: "gap-2 p-2", onClick: () => navigate('/home'), "data-testid": "home-link-btn", children: [_jsx("div", { className: "flex size-6 items-center justify-center rounded-md border bg-background", children: _jsx(Home, { className: "size-4" }) }), _jsx("div", { className: "font-medium text-muted-foreground", children: "Home" })] }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { className: "gap-2 p-2", onClick: () => navigate(`/apps/${activeAppName}/create-app`), "data-testid": "add-app-btn", children: [_jsx("div", { className: "flex size-6 items-center justify-center rounded-md border bg-background", children: _jsx(Plus, { className: "size-4" }) }), _jsx("div", { className: "font-medium text-muted-foreground", children: "Add App" })] }), _jsxs(DropdownMenuItem, { className: "gap-2 p-2", onClick: () => navigate(`/apps/${activeAppName}/edit-app/${activeAppName}`), "data-testid": "edit-app-btn", children: [_jsx("div", { className: "flex size-6 items-center justify-center rounded-md border bg-background", children: _jsx(Pencil, { className: "size-4" }) }), _jsx("div", { className: "font-medium text-muted-foreground", children: "Edit App" })] }), _jsxs(DropdownMenuItem, { className: "gap-2 p-2", onClick: () => navigate('/apps/setup/system/apps'), "data-testid": "manage-all-apps-btn", children: [_jsx("div", { className: "flex size-6 items-center justify-center rounded-md border bg-background", children: _jsx(Settings, { className: "size-4" }) }), _jsx("div", { className: "font-medium text-muted-foreground", children: "Manage All Apps" })] })] })] })) : (_jsxs(SidebarMenuButton, { size: "lg", onClick: () => navigate('/apps/setup'), "data-testid": "system-sidebar-header", children: [_jsx("div", { className: "flex aspect-square size-8 items-center justify-center rounded-lg bg-primary text-primary-foreground", children: _jsx(Settings, { className: "size-4" }) }), _jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [_jsx("span", { className: "truncate font-semibold", children: "System Console" }), _jsx("span", { className: "truncate text-xs text-muted-foreground", children: "No apps configured" })] })] })) }) }) }), _jsx(SidebarContent, { children: activeApp ? (_jsxs(_Fragment, { children: [areas.length > 1 && (_jsxs(SidebarGroup, { children: [_jsxs(SidebarGroupLabel, { className: "flex items-center gap-1.5", children: [_jsx(Layers, { className: "h-3.5 w-3.5" }), "Area"] }), _jsx(SidebarGroupContent, { children: _jsx(SidebarMenu, { children: areas.map((area) => {
181
+ const AreaIcon = getIcon(area.icon);
182
+ const isActiveArea = area.id === activeAreaId;
183
+ return (_jsx(SidebarMenuItem, { children: _jsxs(SidebarMenuButton, { isActive: isActiveArea, tooltip: area.label, onClick: () => setActiveAreaId(area.id), children: [_jsx(AreaIcon, { className: "h-4 w-4" }), _jsx("span", { children: area.label })] }) }, area.id));
184
+ }) }) })] })), _jsx(SidebarGroup, { className: "py-0", children: _jsxs(SidebarGroupContent, { className: "relative", children: [_jsx(Search, { className: "pointer-events-none absolute left-2 top-1/2 size-4 -translate-y-1/2 select-none opacity-70" }), _jsx(SidebarInput, { placeholder: "Search navigation...", value: navSearchQuery, onChange: (e) => setNavSearchQuery(e.target.value), className: "pl-8" })] }) }), _jsx(NavigationRenderer, { items: processedNavigation, basePath: basePath, evaluateVisibility: evalVis, checkPermission: checkPerm, searchQuery: navSearchQuery, enablePinning: true, onPinToggle: togglePin, enableReorder: true, onReorder: handleReorder, resolveObjectLabel: (objectName, fallback) => resolveNavObjectLabel({ name: objectName, label: fallback }), t: t }), recentItems.length > 0 && (_jsxs(SidebarGroup, { children: [_jsxs(SidebarGroupLabel, { className: "flex items-center gap-1.5 cursor-pointer select-none", onClick: () => setRecentExpanded(prev => !prev), children: [_jsx(ChevronRight, { className: `h-3 w-3 transition-transform duration-150 ${recentExpanded ? 'rotate-90' : ''}` }), _jsx(Clock, { className: "h-3.5 w-3.5" }), "Recent"] }), recentExpanded && (_jsx(SidebarGroupContent, { children: _jsx(SidebarMenu, { children: recentItems.slice(0, 5).map(item => (_jsx(SidebarMenuItem, { children: _jsx(SidebarMenuButton, { asChild: true, tooltip: item.label, children: _jsxs(Link, { to: item.href, children: [_jsx("span", { className: "text-muted-foreground", children: item.type === 'dashboard' ? '📊' : item.type === 'report' ? '📈' : '📄' }), _jsx("span", { className: "truncate", children: item.label })] }) }) }, item.id))) }) }))] })), favorites.length > 0 && (_jsxs(SidebarGroup, { children: [_jsxs(SidebarGroupLabel, { className: "flex items-center gap-1.5", children: [_jsx(Star, { className: "h-3.5 w-3.5" }), "Favorites"] }), _jsx(SidebarGroupContent, { children: _jsx(SidebarMenu, { children: favorites.slice(0, 8).map(item => (_jsxs(SidebarMenuItem, { children: [_jsx(SidebarMenuButton, { asChild: true, tooltip: item.label, children: _jsxs(Link, { to: item.href, children: [_jsx("span", { className: "text-muted-foreground", children: item.type === 'dashboard' ? '📊' : item.type === 'report' ? '📈' : item.type === 'page' ? '📄' : '📋' }), _jsx("span", { className: "truncate", children: item.label })] }) }), _jsx(SidebarMenuAction, { showOnHover: true, onClick: (e) => { e.stopPropagation(); removeFavorite(item.id); }, "aria-label": `Remove ${item.label} from favorites`, children: _jsx(StarOff, { className: "h-3 w-3" }) })] }, item.id))) }) })] }))] })) : (_jsxs(SidebarGroup, { "data-testid": "system-fallback-nav", children: [_jsxs(SidebarGroupLabel, { className: "flex items-center gap-1.5", children: [_jsx(Settings, { className: "h-3.5 w-3.5" }), "System"] }), _jsx(SidebarGroupContent, { children: _jsx(SidebarMenu, { children: systemFallbackNavigation.map((item) => {
185
+ const NavIcon = getIcon(item.icon);
186
+ return (_jsx(SidebarMenuItem, { children: _jsx(SidebarMenuButton, { asChild: true, tooltip: item.label, children: _jsxs(Link, { to: item.url || '/system', children: [_jsx(NavIcon, { className: "h-4 w-4" }), _jsx("span", { children: item.label })] }) }) }, item.id));
187
+ }) }) })] })) }), _jsx(SidebarFooter, { children: _jsx(SidebarMenu, { children: _jsx(SidebarMenuItem, { children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(SidebarMenuButton, { size: "lg", className: "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground", children: [_jsxs(Avatar, { className: "h-8 w-8 rounded-lg", children: [_jsx(AvatarImage, { src: user?.image, alt: user?.name ?? 'User' }), _jsx(AvatarFallback, { className: "rounded-lg bg-primary text-primary-foreground", children: getUserInitials(user) })] }), _jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [_jsx("span", { className: "truncate font-semibold", children: user?.name ?? 'User' }), _jsx("span", { className: "truncate text-xs text-muted-foreground", children: user?.email ?? '' })] }), _jsx(ChevronsUpDown, { className: "ml-auto size-4" })] }) }), _jsxs(DropdownMenuContent, { className: "w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg", side: isMobile ? "bottom" : "right", align: "end", sideOffset: 4, children: [_jsx(DropdownMenuLabel, { className: "p-0 font-normal", children: _jsxs("div", { className: "flex items-center gap-2 px-1 py-1.5 text-left text-sm", children: [_jsxs(Avatar, { className: "h-8 w-8 rounded-lg", children: [_jsx(AvatarImage, { src: user?.image, alt: user?.name ?? 'User' }), _jsx(AvatarFallback, { className: "rounded-lg bg-primary text-primary-foreground", children: getUserInitials(user) })] }), _jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [_jsx("span", { className: "truncate font-semibold", children: user?.name ?? 'User' }), _jsx("span", { className: "truncate text-xs text-muted-foreground", children: user?.email ?? '' })] })] }) }), _jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuGroup, { children: _jsxs(DropdownMenuItem, { onClick: () => navigate('/apps/setup'), children: [_jsx(Settings, { className: "mr-2 h-4 w-4" }), "Settings"] }) }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { className: "text-destructive focus:text-destructive", onClick: () => signOut(), children: [_jsx(LogOut, { className: "mr-2 h-4 w-4" }), "Log out"] })] })] }) }) }) })] }), isMobile && (_jsx("div", { className: "fixed bottom-0 left-0 right-0 z-50 flex items-center justify-around border-t bg-background/95 backdrop-blur-sm px-2 py-1 sm:hidden safe-area-bottom", children: (activeApp ? resolvedNavigation : systemFallbackNavigation).filter((n) => n.type !== 'group').slice(0, 5).map((item) => {
188
+ const NavIcon = getIcon(item.icon);
189
+ const baseUrl = activeApp ? `/apps/${activeAppName}` : '';
190
+ let href = item.url || '#';
191
+ if (item.type === 'object') {
192
+ href = `${baseUrl}/${item.objectName}`;
193
+ if (item.viewName)
194
+ href += `/view/${item.viewName}`;
195
+ }
196
+ else if (item.type === 'dashboard')
197
+ href = item.dashboardName ? `${baseUrl}/dashboard/${item.dashboardName}` : '#';
198
+ else if (item.type === 'page')
199
+ href = item.pageName ? `${baseUrl}/page/${item.pageName}` : '#';
200
+ return (_jsxs(Link, { to: href, className: "flex flex-col items-center gap-0.5 px-2 py-1.5 text-muted-foreground hover:text-foreground transition-colors min-w-[44px] min-h-[44px] justify-center", children: [_jsx(NavIcon, { className: "h-5 w-5" }), _jsx("span", { className: "text-[10px] truncate max-w-[60px]", children: resolveI18nLabel(item.label, t) })] }, item.id));
201
+ }) }))] }));
202
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * AppSwitcher
3
+ *
4
+ * Supabase-style application switcher for the top bar.
5
+ * Renders as: [App Name] [ChevronDown] — minimal ghost trigger, no icon box.
6
+ * @module
7
+ */
8
+ export interface AppSwitcherProps {
9
+ activeAppName: string;
10
+ onAppChange: (name: string) => void;
11
+ }
12
+ export declare function AppSwitcher({ activeAppName, onAppChange }: AppSwitcherProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, } from '@object-ui/components';
3
+ import { ChevronDown, Check } from 'lucide-react';
4
+ import { useMetadata } from '../providers/MetadataProvider';
5
+ import { resolveI18nLabel } from '../utils';
6
+ import { useObjectTranslation, useObjectLabel } from '@object-ui/i18n';
7
+ import { getIcon } from '../utils/getIcon';
8
+ export function AppSwitcher({ activeAppName, onAppChange }) {
9
+ const { apps: metadataApps } = useMetadata();
10
+ const { t } = useObjectTranslation();
11
+ const { appLabel } = useObjectLabel();
12
+ const apps = metadataApps || [];
13
+ const activeApps = apps.filter((a) => a.active !== false);
14
+ const activeApp = activeApps.find((a) => a.name === activeAppName) || activeApps[0];
15
+ if (!activeApp)
16
+ return null;
17
+ const appLabelText = appLabel({ name: activeApp.name, label: resolveI18nLabel(activeApp.label, t) });
18
+ return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs("button", { className: "flex items-center gap-1 rounded-md px-1.5 py-1 text-sm font-medium text-foreground/80 hover:bg-accent hover:text-foreground transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring", children: [appLabelText, _jsx(ChevronDown, { className: "h-3.5 w-3.5 text-muted-foreground" })] }) }), _jsxs(DropdownMenuContent, { align: "start", sideOffset: 8, className: "w-60", children: [_jsx(DropdownMenuLabel, { className: "text-xs text-muted-foreground font-normal", children: "Switch Application" }), _jsx(DropdownMenuSeparator, {}), activeApps.map((app) => {
19
+ const AppIcon = getIcon(app.icon);
20
+ const label = appLabel({ name: app.name, label: resolveI18nLabel(app.label, t) });
21
+ const isActive = activeApp.name === app.name;
22
+ return (_jsxs(DropdownMenuItem, { onClick: () => onAppChange(app.name), className: "flex items-center gap-2.5 py-2", children: [_jsx("div", { className: "flex size-5 shrink-0 items-center justify-center rounded border bg-muted text-muted-foreground", children: _jsx(AppIcon, { className: "size-3" }) }), _jsx("span", { className: "flex-1 truncate", children: label }), isActive && _jsx(Check, { className: "size-3.5 text-primary shrink-0" })] }, app.name));
23
+ })] })] }));
24
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Shared layout for authentication pages (login, register, forgot password).
3
+ * Provides a widescreen-optimized split-panel design with branding on the left
4
+ * and form content on the right, inspired by enterprise platforms like Airtable and Salesforce.
5
+ */
6
+ import type React from 'react';
7
+ export declare function AuthPageLayout({ children }: {
8
+ children: React.ReactNode;
9
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function AuthPageLayout({ children }) {
3
+ return (_jsxs("div", { className: "flex min-h-screen", children: [_jsx("div", { className: "hidden lg:flex lg:w-1/2 items-center justify-center bg-primary p-12", children: _jsxs("div", { className: "max-w-md space-y-6 text-primary-foreground", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "h-10 w-10", role: "img", "aria-label": "ObjectStack logo", children: [_jsx("title", { children: "ObjectStack" }), _jsx("path", { d: "M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3" })] }), _jsx("span", { className: "text-2xl font-bold", children: "ObjectStack" })] }), _jsx("h2", { className: "text-3xl font-bold leading-tight", children: "Build powerful business applications, faster." }), _jsx("p", { className: "text-lg opacity-90", children: "The universal platform for enterprise data management, workflows, and analytics." })] }) }), _jsx("div", { className: "flex w-full lg:w-1/2 items-center justify-center bg-background px-6 py-12", children: children })] }));
4
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * ConnectionStatus
3
+ *
4
+ * Displays the current ObjectStack connection state in the header area.
5
+ * Shows a subtle indicator when connected (auto-hides after a moment),
6
+ * and a prominent one when reconnecting or in error state.
7
+ */
8
+ import type { ConnectionState } from '@object-ui/data-objectstack';
9
+ interface ConnectionStatusProps {
10
+ state: ConnectionState;
11
+ className?: string;
12
+ }
13
+ export declare function ConnectionStatus({ state, className }: ConnectionStatusProps): import("react/jsx-runtime").JSX.Element | null;
14
+ export {};
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * ConnectionStatus
4
+ *
5
+ * Displays the current ObjectStack connection state in the header area.
6
+ * Shows a subtle indicator when connected (auto-hides after a moment),
7
+ * and a prominent one when reconnecting or in error state.
8
+ */
9
+ import { useState, useEffect } from 'react';
10
+ import { cn } from '@object-ui/components';
11
+ import { WifiOff, Loader2, CheckCircle2 } from 'lucide-react';
12
+ const statusConfig = {
13
+ connected: { label: 'Connected', color: 'text-green-500', icon: CheckCircle2 },
14
+ connecting: { label: 'Connecting...', color: 'text-yellow-500', icon: Loader2 },
15
+ reconnecting: { label: 'Reconnecting...', color: 'text-yellow-500', icon: Loader2 },
16
+ disconnected: { label: 'Disconnected', color: 'text-muted-foreground', icon: WifiOff },
17
+ error: { label: 'Connection Error', color: 'text-destructive', icon: WifiOff },
18
+ };
19
+ export function ConnectionStatus({ state, className }) {
20
+ const config = statusConfig[state];
21
+ const Icon = config.icon;
22
+ const [showConnected, setShowConnected] = useState(false);
23
+ // Briefly show "Connected" when transitioning to connected state
24
+ useEffect(() => {
25
+ if (state === 'connected') {
26
+ setShowConnected(true);
27
+ const timer = setTimeout(() => setShowConnected(false), 2000);
28
+ return () => clearTimeout(timer);
29
+ }
30
+ setShowConnected(false);
31
+ }, [state]);
32
+ // Hide when connected (after the brief flash)
33
+ if (state === 'connected' && !showConnected)
34
+ return null;
35
+ return (_jsxs("div", { className: cn('flex items-center gap-1.5 text-xs font-medium px-2 py-1 rounded-md transition-opacity duration-300', config.color, state === 'connected' && 'bg-green-500/10', state === 'error' && 'bg-destructive/10', state === 'reconnecting' && 'bg-yellow-500/10', className), title: config.label, children: [_jsx(Icon, { className: cn('h-3.5 w-3.5', (state === 'connecting' || state === 'reconnecting') && 'animate-spin') }), _jsx("span", { className: "hidden sm:inline", children: config.label })] }));
36
+ }
@@ -0,0 +1,10 @@
1
+ interface ConsoleObject {
2
+ name: string;
3
+ label?: string;
4
+ }
5
+ export interface ConsoleFloatingChatbotProps {
6
+ appLabel: string;
7
+ objects: ConsoleObject[];
8
+ }
9
+ export default function ConsoleFloatingChatbot({ appLabel, objects }: ConsoleFloatingChatbotProps): import("react/jsx-runtime").JSX.Element;
10
+ export {};
@@ -0,0 +1,27 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { FloatingChatbot, useObjectChat } from '@object-ui/plugin-chatbot';
3
+ export default function ConsoleFloatingChatbot({ appLabel, objects }) {
4
+ const objectNames = objects.map((o) => o.label || o.name).join(', ');
5
+ const { messages, isLoading, error, sendMessage, stop, reload, clear, } = useObjectChat({
6
+ initialMessages: [
7
+ {
8
+ id: 'welcome',
9
+ role: 'assistant',
10
+ content: `Hello! I'm your **${appLabel}** assistant. How can I help you today?`,
11
+ },
12
+ ],
13
+ autoResponse: true,
14
+ autoResponseText: objectNames
15
+ ? `I can help you work with ${objectNames}. What would you like to do?`
16
+ : 'Thanks for your message! I\'m here to help you navigate and manage your data.',
17
+ autoResponseDelay: 800,
18
+ });
19
+ return (_jsx(FloatingChatbot, { floatingConfig: {
20
+ position: 'bottom-right',
21
+ defaultOpen: false,
22
+ panelWidth: 400,
23
+ panelHeight: 520,
24
+ title: `${appLabel} Assistant`,
25
+ triggerSize: 56,
26
+ }, messages: messages, placeholder: "Ask anything...", onSendMessage: (content) => sendMessage(content), onClear: clear, onStop: isLoading ? stop : undefined, onReload: reload, isLoading: isLoading, error: error, enableMarkdown: true }));
27
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * ConsoleLayout
3
+ *
4
+ * Root layout shell for the console application. Composes the AppShell
5
+ * with the UnifiedSidebar, header, and main content area.
6
+ * Includes the global floating chatbot (FAB) widget.
7
+ * Sets navigation context to 'app' for app-specific routes.
8
+ * @module
9
+ */
10
+ import React from 'react';
11
+ import type { ConnectionState } from '@object-ui/data-objectstack';
12
+ interface ConsoleLayoutProps {
13
+ children: React.ReactNode;
14
+ activeAppName: string;
15
+ activeApp: any;
16
+ onAppChange: (name: string) => void;
17
+ objects: any[];
18
+ connectionState?: ConnectionState;
19
+ }
20
+ /** Floating chatbot wired with useObjectChat for demo auto-response */
21
+ export declare function ConsoleLayout({ children, activeAppName, activeApp, onAppChange, objects, connectionState }: ConsoleLayoutProps): import("react/jsx-runtime").JSX.Element;
22
+ export {};
@@ -0,0 +1,49 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * ConsoleLayout
4
+ *
5
+ * Root layout shell for the console application. Composes the AppShell
6
+ * with the UnifiedSidebar, header, and main content area.
7
+ * Includes the global floating chatbot (FAB) widget.
8
+ * Sets navigation context to 'app' for app-specific routes.
9
+ * @module
10
+ */
11
+ import { useEffect, Suspense, lazy } from 'react';
12
+ import { AppShell } from '@object-ui/layout';
13
+ import { useDiscovery } from '@object-ui/react';
14
+ // Lazy-load the chatbot so its heavy markdown deps (~150 KB) stay out of
15
+ // the initial paint until the AI assistant is actually enabled.
16
+ const ConsoleFloatingChatbot = lazy(() => import('./ConsoleFloatingChatbot'));
17
+ import { UnifiedSidebar } from './UnifiedSidebar';
18
+ import { AppHeader } from './AppHeader';
19
+ import { useResponsiveSidebar } from '../hooks/useResponsiveSidebar';
20
+ import { useNavigationContext } from '../context/NavigationContext';
21
+ import { resolveI18nLabel } from '../utils';
22
+ /** Inner component that can access SidebarProvider context */
23
+ function ConsoleLayoutInner({ children }) {
24
+ useResponsiveSidebar();
25
+ return _jsx(_Fragment, { children: children });
26
+ }
27
+ /** Floating chatbot wired with useObjectChat for demo auto-response */
28
+ // (moved to ./ConsoleFloatingChatbot.tsx for code-splitting)
29
+ export function ConsoleLayout({ children, activeAppName, activeApp, onAppChange, objects, connectionState }) {
30
+ const appLabel = resolveI18nLabel(activeApp?.label) || activeAppName;
31
+ const { isAiEnabled } = useDiscovery();
32
+ const { setContext, setCurrentAppName } = useNavigationContext();
33
+ // Set navigation context to 'app' when this layout mounts
34
+ useEffect(() => {
35
+ setContext('app');
36
+ setCurrentAppName(activeAppName);
37
+ }, [setContext, setCurrentAppName, activeAppName]);
38
+ return (_jsxs(AppShell, { sidebar: _jsx(UnifiedSidebar, { activeAppName: activeAppName, onAppChange: onAppChange }), navbar: _jsx(AppHeader, { variant: "app", appName: appLabel, objects: objects, connectionState: connectionState, activeAppName: activeAppName, onAppChange: onAppChange }), className: "!p-0 overflow-hidden bg-muted/5", branding: activeApp?.branding
39
+ ? {
40
+ primaryColor: activeApp.branding.primaryColor,
41
+ accentColor: activeApp.branding.accentColor,
42
+ favicon: activeApp.branding.favicon,
43
+ logo: activeApp.branding.logo,
44
+ title: activeApp.label
45
+ ? `${resolveI18nLabel(activeApp.label)} — ObjectStack Console`
46
+ : undefined,
47
+ }
48
+ : undefined, children: [_jsx(ConsoleLayoutInner, { children: children }), isAiEnabled && (_jsx(Suspense, { fallback: null, children: _jsx(ConsoleFloatingChatbot, { appLabel: appLabel, objects: objects }) }))] }));
49
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * LocaleSwitcher
3
+ *
4
+ * Dropdown menu for switching the application language.
5
+ * Uses the i18n context to change language at runtime.
6
+ */
7
+ export declare function LocaleSwitcher(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,27 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * LocaleSwitcher
4
+ *
5
+ * Dropdown menu for switching the application language.
6
+ * Uses the i18n context to change language at runtime.
7
+ */
8
+ import { Globe } from 'lucide-react';
9
+ import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@object-ui/components';
10
+ import { useObjectTranslation } from '@object-ui/i18n';
11
+ /** Supported language metadata */
12
+ const LANGUAGES = [
13
+ { code: 'en', label: 'English' },
14
+ { code: 'zh', label: '中文' },
15
+ { code: 'ja', label: '日本語' },
16
+ { code: 'ko', label: '한국어' },
17
+ { code: 'de', label: 'Deutsch' },
18
+ { code: 'fr', label: 'Français' },
19
+ { code: 'es', label: 'Español' },
20
+ { code: 'pt', label: 'Português' },
21
+ { code: 'ru', label: 'Русский' },
22
+ { code: 'ar', label: 'العربية' },
23
+ ];
24
+ export function LocaleSwitcher() {
25
+ const { t, language, changeLanguage } = useObjectTranslation();
26
+ return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "icon", className: "h-8 w-8", children: [_jsx(Globe, { className: "h-4 w-4" }), _jsx("span", { className: "sr-only", children: t('console.localeSwitcher.label') })] }) }), _jsx(DropdownMenuContent, { align: "end", className: "max-h-72 overflow-y-auto", children: LANGUAGES.map((lang) => (_jsxs(DropdownMenuItem, { onClick: () => changeLanguage(lang.code), className: "gap-2", children: [lang.label, language === lang.code && _jsx("span", { className: "ml-auto text-xs", children: "\u2713" })] }, lang.code))) })] }));
27
+ }
@@ -0,0 +1 @@
1
+ export declare function ModeToggle(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Moon, Sun, Monitor } from "lucide-react";
3
+ import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@object-ui/components";
4
+ import { useTheme } from "../chrome/ThemeProvider";
5
+ import { useObjectTranslation } from '@object-ui/i18n';
6
+ export function ModeToggle() {
7
+ const { theme, setTheme } = useTheme();
8
+ const { t } = useObjectTranslation();
9
+ return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "icon", className: "h-8 w-8", children: [_jsx(Sun, { className: "h-4 w-4 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" }), _jsx(Moon, { className: "absolute h-4 w-4 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" }), _jsx("span", { className: "sr-only", children: t('console.theme.toggle') })] }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsxs(DropdownMenuItem, { onClick: () => setTheme("light"), className: "gap-2", children: [_jsx(Sun, { className: "h-4 w-4" }), t('console.theme.light'), theme === "light" && _jsx("span", { className: "ml-auto text-xs", children: "\u2713" })] }), _jsxs(DropdownMenuItem, { onClick: () => setTheme("dark"), className: "gap-2", children: [_jsx(Moon, { className: "h-4 w-4" }), t('console.theme.dark'), theme === "dark" && _jsx("span", { className: "ml-auto text-xs", children: "\u2713" })] }), _jsxs(DropdownMenuItem, { onClick: () => setTheme("system"), className: "gap-2", children: [_jsx(Monitor, { className: "h-4 w-4" }), t('console.theme.system'), theme === "system" && _jsx("span", { className: "ml-auto text-xs", children: "\u2713" })] })] })] }));
10
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * UnifiedSidebar
3
+ *
4
+ * Airtable-style contextual sidebar that dynamically switches between Home and App navigation.
5
+ * Features:
6
+ * - Persistent across all authenticated routes
7
+ * - Context-aware navigation (Home vs App)
8
+ * - Pinned bottom area (Settings, Help, User Profile)
9
+ * - Smooth transitions between contexts
10
+ * - Back to Home navigation from App context
11
+ * - App switcher dropdown
12
+ *
13
+ * @module
14
+ */
15
+ /**
16
+ * Lazy-resolved Lucide icon — see ../utils/getIcon for impl.
17
+ * The local symbol is kept for backwards compat with existing call sites
18
+ * within this file.
19
+ */
20
+ interface UnifiedSidebarProps {
21
+ /** When in app context, the active app name */
22
+ activeAppName?: string;
23
+ /** Callback when user switches apps */
24
+ onAppChange?: (name: string) => void;
25
+ }
26
+ export declare function UnifiedSidebar({ activeAppName }: UnifiedSidebarProps): import("react/jsx-runtime").JSX.Element;
27
+ export {};