@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
@@ -0,0 +1,92 @@
1
+ /**
2
+ * useObjectActions Hook
3
+ *
4
+ * Provides action handlers for CRUD operations on an object, backed by
5
+ * the ActionRunner from @object-ui/core via the useActionRunner hook.
6
+ *
7
+ * Supports:
8
+ * - create: Open create dialog
9
+ * - delete: Delete a record with confirmation
10
+ * - navigate: Route to a specific view or record
11
+ * - refresh: Trigger a data refresh
12
+ */
13
+ import { useCallback, useEffect } from 'react';
14
+ import { useNavigate, useParams } from 'react-router-dom';
15
+ import { useActionRunner } from '@object-ui/react';
16
+ import { toast } from 'sonner';
17
+ export function useObjectActions({ objectName, objectLabel, dataSource, onEdit, onRefresh, }) {
18
+ const navigate = useNavigate();
19
+ const { appName } = useParams();
20
+ const baseUrl = `/apps/${appName}`;
21
+ const { execute, loading, error, runner } = useActionRunner({
22
+ context: {
23
+ objectName,
24
+ objectLabel: objectLabel || objectName,
25
+ baseUrl,
26
+ },
27
+ });
28
+ // Register custom handlers
29
+ useEffect(() => {
30
+ // Handler: create
31
+ runner.registerHandler('create', async () => {
32
+ onEdit?.(null);
33
+ return { success: true };
34
+ });
35
+ // Handler: delete
36
+ runner.registerHandler('delete', async (action) => {
37
+ const recordId = action.params?.recordId || action.recordId;
38
+ if (!recordId)
39
+ return { success: false, error: 'No record ID provided' };
40
+ try {
41
+ await dataSource.delete(objectName, recordId);
42
+ onRefresh?.();
43
+ toast.success(`${objectLabel || objectName} deleted successfully`);
44
+ return { success: true, reload: true };
45
+ }
46
+ catch (err) {
47
+ toast.error(`Failed to delete ${objectLabel || objectName}`, {
48
+ description: err.message,
49
+ });
50
+ return { success: false, error: err.message };
51
+ }
52
+ });
53
+ // Handler: navigate
54
+ runner.registerHandler('navigate', async (action) => {
55
+ const url = action.params?.url || action.url;
56
+ if (url) {
57
+ navigate(url.startsWith('/') ? url : `${baseUrl}/${url}`);
58
+ }
59
+ return { success: true };
60
+ });
61
+ // Handler: refresh
62
+ runner.registerHandler('refresh', async () => {
63
+ onRefresh?.();
64
+ return { success: true, reload: true };
65
+ });
66
+ }, [runner, objectName, dataSource, onEdit, onRefresh, navigate, baseUrl]);
67
+ const create = useCallback(() => {
68
+ onEdit?.(null);
69
+ }, [onEdit]);
70
+ const deleteRecord = useCallback(async (recordId) => {
71
+ return execute({
72
+ type: 'delete',
73
+ confirmText: `Are you sure you want to delete this record?`,
74
+ params: { recordId },
75
+ });
76
+ }, [execute]);
77
+ const navigateToView = useCallback((viewId) => {
78
+ navigate(`${baseUrl}/${objectName}/view/${viewId}`);
79
+ }, [navigate, baseUrl, objectName]);
80
+ const navigateToRecord = useCallback((recordId) => {
81
+ navigate(`${baseUrl}/${objectName}/record/${encodeURIComponent(recordId)}`);
82
+ }, [navigate, baseUrl, objectName]);
83
+ return {
84
+ execute,
85
+ create,
86
+ deleteRecord,
87
+ navigateToView,
88
+ navigateToRecord,
89
+ loading,
90
+ error,
91
+ };
92
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * useRecentItems
3
+ *
4
+ * Tracks recently visited items (objects, dashboards, pages) with
5
+ * localStorage persistence. Exposes helpers to add and retrieve items.
6
+ * @module
7
+ */
8
+ export interface RecentItem {
9
+ /** Unique key, e.g. "object:contact" or "dashboard:sales_overview" */
10
+ id: string;
11
+ label: string;
12
+ href: string;
13
+ type: 'object' | 'dashboard' | 'page' | 'report' | 'record';
14
+ /** ISO timestamp of last visit */
15
+ visitedAt: string;
16
+ }
17
+ export declare function useRecentItems(): {
18
+ recentItems: RecentItem[];
19
+ addRecentItem: (item: Omit<RecentItem, "visitedAt">) => void;
20
+ clearRecentItems: () => void;
21
+ };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * useRecentItems
3
+ *
4
+ * Tracks recently visited items (objects, dashboards, pages) with
5
+ * localStorage persistence. Exposes helpers to add and retrieve items.
6
+ * @module
7
+ */
8
+ import { useState, useCallback, useEffect } from 'react';
9
+ const STORAGE_KEY = 'objectui-recent-items';
10
+ const MAX_RECENT = 8;
11
+ function loadRecent() {
12
+ try {
13
+ const raw = localStorage.getItem(STORAGE_KEY);
14
+ return raw ? JSON.parse(raw) : [];
15
+ }
16
+ catch {
17
+ return [];
18
+ }
19
+ }
20
+ function saveRecent(items) {
21
+ try {
22
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(items));
23
+ }
24
+ catch {
25
+ // Storage full — silently ignore
26
+ }
27
+ }
28
+ export function useRecentItems() {
29
+ const [recentItems, setRecentItems] = useState(loadRecent);
30
+ // Sync from storage on mount
31
+ useEffect(() => {
32
+ setRecentItems(loadRecent());
33
+ }, []);
34
+ const addRecentItem = useCallback((item) => {
35
+ setRecentItems(prev => {
36
+ const filtered = prev.filter(r => r.id !== item.id);
37
+ const updated = [
38
+ { ...item, visitedAt: new Date().toISOString() },
39
+ ...filtered,
40
+ ].slice(0, MAX_RECENT);
41
+ saveRecent(updated);
42
+ return updated;
43
+ });
44
+ }, []);
45
+ const clearRecentItems = useCallback(() => {
46
+ setRecentItems([]);
47
+ saveRecent([]);
48
+ }, []);
49
+ return { recentItems, addRecentItem, clearRecentItems };
50
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * useResponsiveSidebar
3
+ *
4
+ * Auto-collapses the sidebar on tablet-width viewports (768px–1023px).
5
+ * Must be called inside a SidebarProvider context.
6
+ * @module
7
+ */
8
+ export declare function useResponsiveSidebar(): void;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * useResponsiveSidebar
3
+ *
4
+ * Auto-collapses the sidebar on tablet-width viewports (768px–1023px).
5
+ * Must be called inside a SidebarProvider context.
6
+ * @module
7
+ */
8
+ import { useEffect } from 'react';
9
+ import { useSidebar } from '@object-ui/components';
10
+ /** Tablet breakpoint range: 768px <= width < 1024px */
11
+ const TABLET_MIN = 768;
12
+ const TABLET_MAX = 1024;
13
+ export function useResponsiveSidebar() {
14
+ const { setOpen, isMobile } = useSidebar();
15
+ useEffect(() => {
16
+ function handleResize() {
17
+ const width = window.innerWidth;
18
+ if (width >= TABLET_MIN && width < TABLET_MAX) {
19
+ // Tablet: auto-collapse sidebar
20
+ setOpen(false);
21
+ }
22
+ }
23
+ // Run on mount to set initial state
24
+ handleResize();
25
+ window.addEventListener('resize', handleResize);
26
+ return () => window.removeEventListener('resize', handleResize);
27
+ }, [setOpen, isMobile]);
28
+ }
package/dist/index.d.ts CHANGED
@@ -9,4 +9,33 @@ export { ObjectRenderer } from './components/ObjectRenderer';
9
9
  export { DashboardRenderer } from './components/DashboardRenderer';
10
10
  export { PageRenderer } from './components/PageRenderer';
11
11
  export { FormRenderer } from './components/FormRenderer';
12
+ export { AdapterProvider, useAdapter } from './providers/AdapterProvider';
13
+ export { MetadataProvider, useMetadata, useMetadataItem } from './providers/MetadataProvider';
14
+ export { ExpressionProvider, useExpressionContext, evaluateVisibility } from './providers/ExpressionProvider';
15
+ export { useObjectActions } from './hooks/useObjectActions';
16
+ export { useRecentItems } from './hooks/useRecentItems';
12
17
  export type { AppShellProps, ObjectRendererProps, DashboardRendererProps, PageRendererProps, FormRendererProps, } from './types';
18
+ export type { MetadataState, MetadataContextValue, MetadataTypeStatus, } from './providers/MetadataProvider';
19
+ export type { ExpressionContextValue, } from './providers/ExpressionProvider';
20
+ export type { RecentItem, } from './hooks/useRecentItems';
21
+ export { ConsoleShell, ConnectedShell, RequireOrganization, AuthenticatedRoute, RootRedirect, SystemRedirect, LoadingFallback, } from './console/ConsoleShell';
22
+ export { ConsoleLayout, AppHeader, AppSidebar, UnifiedSidebar, AppSwitcher, ConnectionStatus, ActivityFeed, LocaleSwitcher, ModeToggle, AuthPageLayout, } from './layout';
23
+ export type { ActivityItem } from './layout';
24
+ export { CommandPalette, KeyboardShortcutsDialog, OnboardingWalkthrough, ConditionalAuthWrapper, ConsoleToaster, ErrorBoundary, LoadingScreen, ThemeProvider, useTheme, } from './chrome';
25
+ export { ObjectView, RecordDetailView, DashboardView, PageView, ReportView, SearchResultsPage, ViewConfigPanel, } from './views';
26
+ export { useFavorites, useMetadataService, useNavPins, useNavigationSync, NavigationSyncEffect, addNavigationItem, removeNavigationItems, renameNavigationItems, navigationEqual, generateNavId, useResponsiveSidebar, } from './hooks';
27
+ export type { FavoriteItem } from './hooks';
28
+ export { NavigationProvider, useNavigationContext, FavoritesProvider } from './context';
29
+ export { AppContent as DefaultAppContent } from './console/AppContent';
30
+ export { LoginPage as DefaultLoginPage } from './console/auth/LoginPage';
31
+ export { RegisterPage as DefaultRegisterPage } from './console/auth/RegisterPage';
32
+ export { ForgotPasswordPage as DefaultForgotPasswordPage } from './console/auth/ForgotPasswordPage';
33
+ export { HomeLayout as DefaultHomeLayout, HomeLayout } from './console/home/HomeLayout';
34
+ export { HomePage as DefaultHomePage, HomePage } from './console/home/HomePage';
35
+ export { OrganizationsLayout as DefaultOrganizationsLayout } from './console/organizations/OrganizationsLayout';
36
+ export { OrganizationsPage as DefaultOrganizationsPage } from './console/organizations/OrganizationsPage';
37
+ export { OrganizationLayout as DefaultOrganizationLayout } from './console/organizations/manage/OrganizationLayout';
38
+ export { MembersPage as DefaultMembersPage } from './console/organizations/manage/MembersPage';
39
+ export { InvitationsPage as DefaultInvitationsPage } from './console/organizations/manage/InvitationsPage';
40
+ export { SettingsPage as DefaultSettingsPage } from './console/organizations/manage/SettingsPage';
41
+ export { AcceptInvitationPage as DefaultAcceptInvitationPage } from './console/organizations/manage/AcceptInvitationPage';
package/dist/index.js CHANGED
@@ -4,8 +4,43 @@
4
4
  * Minimal Application Shell for ObjectUI
5
5
  * Framework-agnostic rendering engine for third-party integration
6
6
  */
7
+ // Components
7
8
  export { AppShell } from './components/AppShell';
8
9
  export { ObjectRenderer } from './components/ObjectRenderer';
9
10
  export { DashboardRenderer } from './components/DashboardRenderer';
10
11
  export { PageRenderer } from './components/PageRenderer';
11
12
  export { FormRenderer } from './components/FormRenderer';
13
+ // Providers
14
+ export { AdapterProvider, useAdapter } from './providers/AdapterProvider';
15
+ export { MetadataProvider, useMetadata, useMetadataItem } from './providers/MetadataProvider';
16
+ export { ExpressionProvider, useExpressionContext, evaluateVisibility } from './providers/ExpressionProvider';
17
+ // Hooks
18
+ export { useObjectActions } from './hooks/useObjectActions';
19
+ export { useRecentItems } from './hooks/useRecentItems';
20
+ // Console building blocks — compose these in your App.tsx to build the console
21
+ // routing tree. See examples/console-starter/src/App.tsx for a minimal example.
22
+ export { ConsoleShell, ConnectedShell, RequireOrganization, AuthenticatedRoute, RootRedirect, SystemRedirect, LoadingFallback, } from './console/ConsoleShell';
23
+ // Layout chrome
24
+ export { ConsoleLayout, AppHeader, AppSidebar, UnifiedSidebar, AppSwitcher, ConnectionStatus, ActivityFeed, LocaleSwitcher, ModeToggle, AuthPageLayout, } from './layout';
25
+ // Top-level chrome (dialogs, providers, error boundaries)
26
+ export { CommandPalette, KeyboardShortcutsDialog, OnboardingWalkthrough, ConditionalAuthWrapper, ConsoleToaster, ErrorBoundary, LoadingScreen, ThemeProvider, useTheme, } from './chrome';
27
+ // Standard inner-SPA views
28
+ export { ObjectView, RecordDetailView, DashboardView, PageView, ReportView, SearchResultsPage, ViewConfigPanel, } from './views';
29
+ // Hooks
30
+ export { useFavorites, useMetadataService, useNavPins, useNavigationSync, NavigationSyncEffect, addNavigationItem, removeNavigationItems, renameNavigationItems, navigationEqual, generateNavId, useResponsiveSidebar, } from './hooks';
31
+ // Context providers
32
+ export { NavigationProvider, useNavigationContext, FavoritesProvider } from './context';
33
+ // Default page implementations (consumers can partial-override slots)
34
+ export { AppContent as DefaultAppContent } from './console/AppContent';
35
+ export { LoginPage as DefaultLoginPage } from './console/auth/LoginPage';
36
+ export { RegisterPage as DefaultRegisterPage } from './console/auth/RegisterPage';
37
+ export { ForgotPasswordPage as DefaultForgotPasswordPage } from './console/auth/ForgotPasswordPage';
38
+ export { HomeLayout as DefaultHomeLayout, HomeLayout } from './console/home/HomeLayout';
39
+ export { HomePage as DefaultHomePage, HomePage } from './console/home/HomePage';
40
+ export { OrganizationsLayout as DefaultOrganizationsLayout } from './console/organizations/OrganizationsLayout';
41
+ export { OrganizationsPage as DefaultOrganizationsPage } from './console/organizations/OrganizationsPage';
42
+ export { OrganizationLayout as DefaultOrganizationLayout } from './console/organizations/manage/OrganizationLayout';
43
+ export { MembersPage as DefaultMembersPage } from './console/organizations/manage/MembersPage';
44
+ export { InvitationsPage as DefaultInvitationsPage } from './console/organizations/manage/InvitationsPage';
45
+ export { SettingsPage as DefaultSettingsPage } from './console/organizations/manage/SettingsPage';
46
+ export { AcceptInvitationPage as DefaultAcceptInvitationPage } from './console/organizations/manage/AcceptInvitationPage';
@@ -0,0 +1,22 @@
1
+ /**
2
+ * ActivityFeed
3
+ *
4
+ * Sidebar panel that displays recent activity items (create, update, delete,
5
+ * comment). Opens as a slide-out Sheet triggered by a bell icon button.
6
+ * Phase 17 L1 – local state only, no server integration.
7
+ * @module
8
+ */
9
+ export interface ActivityItem {
10
+ id: string;
11
+ type: 'create' | 'update' | 'delete' | 'comment';
12
+ objectName: string;
13
+ recordId?: string;
14
+ user: string;
15
+ description: string;
16
+ timestamp: string;
17
+ }
18
+ export interface ActivityFeedProps {
19
+ activities?: ActivityItem[];
20
+ className?: string;
21
+ }
22
+ export declare function ActivityFeed({ activities, className }: ActivityFeedProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * ActivityFeed
4
+ *
5
+ * Sidebar panel that displays recent activity items (create, update, delete,
6
+ * comment). Opens as a slide-out Sheet triggered by a bell icon button.
7
+ * Phase 17 L1 – local state only, no server integration.
8
+ * @module
9
+ */
10
+ import { useState } from 'react';
11
+ import { Button, Badge, Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, } from '@object-ui/components';
12
+ import { Bell, Plus, Pencil, Trash2, MessageSquare, Filter } from 'lucide-react';
13
+ const typeConfig = {
14
+ create: { icon: Plus, color: 'text-green-500' },
15
+ update: { icon: Pencil, color: 'text-blue-500' },
16
+ delete: { icon: Trash2, color: 'text-red-500' },
17
+ comment: { icon: MessageSquare, color: 'text-amber-500' },
18
+ };
19
+ /** Format an ISO timestamp as a relative string (e.g. "2m ago"). */
20
+ function formatRelativeTime(iso) {
21
+ const ms = new Date(iso).getTime();
22
+ if (Number.isNaN(ms))
23
+ return '';
24
+ const seconds = Math.floor((Date.now() - ms) / 1000);
25
+ if (seconds < 60)
26
+ return `${Math.max(seconds, 0)}s ago`;
27
+ const minutes = Math.floor(seconds / 60);
28
+ if (minutes < 60)
29
+ return `${minutes}m ago`;
30
+ const hours = Math.floor(minutes / 60);
31
+ if (hours < 24)
32
+ return `${hours}h ago`;
33
+ const days = Math.floor(hours / 24);
34
+ return `${days}d ago`;
35
+ }
36
+ export function ActivityFeed({ activities = [], className }) {
37
+ const [open, setOpen] = useState(false);
38
+ const [showFilters, setShowFilters] = useState(false);
39
+ const [notificationPreferences, setNotificationPreferences] = useState({
40
+ create: true,
41
+ update: true,
42
+ delete: true,
43
+ comment: true,
44
+ });
45
+ const togglePreference = (type) => {
46
+ setNotificationPreferences(prev => ({ ...prev, [type]: !prev[type] }));
47
+ };
48
+ const filteredActivities = activities.filter(a => notificationPreferences[a.type]);
49
+ return (_jsxs(Sheet, { open: open, onOpenChange: setOpen, children: [_jsx(SheetTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "icon", className: className ?? 'h-8 w-8', "aria-label": "Activity feed", children: [_jsx(Bell, { className: "h-4 w-4" }), activities.length > 0 && (_jsx("span", { className: "absolute -top-0.5 -right-0.5 h-3.5 w-3.5 rounded-full bg-primary text-[9px] font-bold text-primary-foreground flex items-center justify-center", children: activities.length > 9 ? '9+' : activities.length }))] }) }), _jsxs(SheetContent, { side: "right", className: "w-80 sm:w-96", children: [_jsx(SheetHeader, { children: _jsxs(SheetTitle, { className: "flex items-center justify-between", children: ["Recent Activity", _jsxs(Button, { variant: showFilters ? 'secondary' : 'ghost', size: "sm", className: "h-7 px-2", onClick: () => setShowFilters(!showFilters), children: [_jsx(Filter, { className: "h-3.5 w-3.5 mr-1" }), "Filter"] })] }) }), showFilters && (_jsx("div", { className: "flex flex-wrap gap-1.5 mt-3 px-1", children: Object.keys(typeConfig).map(type => {
50
+ const { icon: Icon, color } = typeConfig[type];
51
+ const active = notificationPreferences[type];
52
+ return (_jsxs(Badge, { variant: active ? 'default' : 'outline', className: "cursor-pointer select-none gap-1 capitalize", onClick: () => togglePreference(type), children: [_jsx(Icon, { className: `h-3 w-3 ${active ? '' : color}` }), type] }, type));
53
+ }) })), filteredActivities.length === 0 ? (_jsxs("div", { className: "flex flex-col items-center justify-center gap-2 py-16 text-muted-foreground", children: [_jsx(Bell, { className: "h-8 w-8 opacity-40" }), _jsx("p", { className: "text-sm", children: "No recent activity" })] })) : (_jsx("ul", { className: "mt-4 space-y-1 overflow-y-auto max-h-[calc(100vh-8rem)]", children: filteredActivities.map((item) => {
54
+ const { icon: Icon, color } = typeConfig[item.type];
55
+ return (_jsxs("li", { className: "flex items-start gap-3 rounded-md px-2 py-2 hover:bg-muted/50 transition-colors", children: [_jsx("span", { className: `mt-0.5 shrink-0 ${color}`, children: _jsx(Icon, { className: "h-4 w-4" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-sm leading-snug", children: item.description }), _jsxs("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [item.user, " \u00B7 ", formatRelativeTime(item.timestamp)] })] })] }, item.id));
56
+ }) }))] })] }));
57
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * AppHeader — unified top bar
3
+ *
4
+ * Supabase-style top bar used across the whole console:
5
+ * [Logo] [/ App ▾ / Object ▾ ...] [actions] [user ▾]
6
+ *
7
+ * Variants:
8
+ * - `app` (default when `appName` is present): sidebar trigger + AppSwitcher
9
+ * + breadcrumb path. Used by `ConsoleLayout` inside `/apps/:appName/*`.
10
+ * - `home` : no breadcrumb; displays the "ObjectStack" wordmark next to the
11
+ * logo. Used by `/home`.
12
+ * - `orgs` : no breadcrumb; logo + "Organizations" title. Used by the
13
+ * `/organizations` landing page.
14
+ *
15
+ * The user avatar dropdown includes the organization (workspace) switcher at
16
+ * the top so the same chrome lets users change orgs from any page.
17
+ * @module
18
+ */
19
+ import { type PresenceUser } from '@object-ui/collaboration';
20
+ import { type ActivityItem } from './ActivityFeed';
21
+ import type { ConnectionState } from '@object-ui/data-objectstack';
22
+ export type AppHeaderVariant = 'app' | 'home' | 'orgs';
23
+ export interface AppHeaderProps {
24
+ variant?: AppHeaderVariant;
25
+ appName?: string;
26
+ objects?: any[];
27
+ connectionState?: ConnectionState;
28
+ presenceUsers?: PresenceUser[];
29
+ activities?: ActivityItem[];
30
+ activeAppName?: string;
31
+ onAppChange?: (name: string) => void;
32
+ }
33
+ export declare function AppHeader({ variant, appName, objects, connectionState, presenceUsers, activities, activeAppName, onAppChange, }: AppHeaderProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,182 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * AppHeader — unified top bar
4
+ *
5
+ * Supabase-style top bar used across the whole console:
6
+ * [Logo] [/ App ▾ / Object ▾ ...] [actions] [user ▾]
7
+ *
8
+ * Variants:
9
+ * - `app` (default when `appName` is present): sidebar trigger + AppSwitcher
10
+ * + breadcrumb path. Used by `ConsoleLayout` inside `/apps/:appName/*`.
11
+ * - `home` : no breadcrumb; displays the "ObjectStack" wordmark next to the
12
+ * logo. Used by `/home`.
13
+ * - `orgs` : no breadcrumb; logo + "Organizations" title. Used by the
14
+ * `/organizations` landing page.
15
+ *
16
+ * The user avatar dropdown includes the organization (workspace) switcher at
17
+ * the top so the same chrome lets users change orgs from any page.
18
+ * @module
19
+ */
20
+ import { useLocation, useParams, Link, useNavigate } from 'react-router-dom';
21
+ import { SidebarTrigger, Button, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup, Avatar, AvatarImage, AvatarFallback, } from '@object-ui/components';
22
+ import { Search, HelpCircle, ChevronDown, Settings, LogOut, User as UserIcon, Boxes, } from 'lucide-react';
23
+ import { useState, useEffect, useCallback, useRef } from 'react';
24
+ import { useOffline } from '@object-ui/react';
25
+ import { PresenceAvatars } from '@object-ui/collaboration';
26
+ import { ModeToggle } from './ModeToggle';
27
+ import { LocaleSwitcher } from './LocaleSwitcher';
28
+ import { ConnectionStatus } from './ConnectionStatus';
29
+ import { ActivityFeed } from './ActivityFeed';
30
+ import { AppSwitcher } from './AppSwitcher';
31
+ import { useAdapter } from '../providers/AdapterProvider';
32
+ import { useObjectTranslation, useObjectLabel } from '@object-ui/i18n';
33
+ import { useAuth, getUserInitials } from '@object-ui/auth';
34
+ import { useMetadata } from '../providers/MetadataProvider';
35
+ import { useNavigationContext } from '../context/NavigationContext';
36
+ function humanizeSlug(slug) {
37
+ return slug.replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
38
+ }
39
+ /** Muted `/` separator between path segments */
40
+ function PathSep() {
41
+ return (_jsx("span", { className: "select-none text-muted-foreground/40 mx-1.5 text-base font-light", "aria-hidden": true, children: "/" }));
42
+ }
43
+ // No fake fallback presence — render nothing when the API has no data so the
44
+ // header doesn't ship phantom collaborators in production.
45
+ const EMPTY_PRESENCE_USERS = [];
46
+ export function AppHeader({ variant, appName, objects, connectionState, presenceUsers, activities, activeAppName, onAppChange, }) {
47
+ const resolvedVariant = variant ?? (appName ? 'app' : 'home');
48
+ const isApp = resolvedVariant === 'app';
49
+ const location = useLocation();
50
+ const params = useParams();
51
+ const navigate = useNavigate();
52
+ const { isOnline } = useOffline();
53
+ const { user, signOut, organizations, activeOrganization, isOrganizationsLoading, } = useAuth();
54
+ const dataSource = useAdapter();
55
+ const { t } = useObjectTranslation();
56
+ const { objectLabel } = useObjectLabel();
57
+ const { apps: metadataApps } = useMetadata();
58
+ const { currentAppName, recordTitle } = useNavigationContext();
59
+ const [apiPresenceUsers, setApiPresenceUsers] = useState(null);
60
+ const [apiActivities, setApiActivities] = useState(null);
61
+ // Once the server returns 404 for these collections we stop retrying for
62
+ // the lifetime of the page — they're optional features and re-requesting
63
+ // on every navigation creates console noise + wasted round trips.
64
+ const presenceUnavailableRef = useRef(false);
65
+ const activityUnavailableRef = useRef(false);
66
+ const fetchPresenceAndActivities = useCallback(async () => {
67
+ if (!dataSource || !isApp)
68
+ return;
69
+ const presenceP = presenceUnavailableRef.current
70
+ ? Promise.resolve({ data: [] })
71
+ : dataSource.find('sys_presence').catch((err) => {
72
+ if (err?.status === 404)
73
+ presenceUnavailableRef.current = true;
74
+ return { data: [] };
75
+ });
76
+ const activityP = activityUnavailableRef.current
77
+ ? Promise.resolve({ data: [] })
78
+ : dataSource
79
+ .find('sys_activity', { $orderby: { timestamp: 'desc' }, $top: 20 })
80
+ .catch((err) => {
81
+ if (err?.status === 404)
82
+ activityUnavailableRef.current = true;
83
+ return { data: [] };
84
+ });
85
+ try {
86
+ const [presenceResult, activityResult] = await Promise.all([presenceP, activityP]);
87
+ if (presenceResult.data?.length) {
88
+ const users = presenceResult.data.filter((u) => typeof u.userId === 'string');
89
+ if (users.length)
90
+ setApiPresenceUsers(users);
91
+ }
92
+ if (activityResult.data?.length) {
93
+ const items = activityResult.data.filter((a) => typeof a.type === 'string');
94
+ if (items.length)
95
+ setApiActivities(items);
96
+ }
97
+ }
98
+ catch { /* fallback below */ }
99
+ }, [dataSource, isApp]);
100
+ useEffect(() => { fetchPresenceAndActivities(); }, [fetchPresenceAndActivities]);
101
+ const activeUsers = presenceUsers ?? apiPresenceUsers ?? EMPTY_PRESENCE_USERS;
102
+ const activeActivities = activities ?? apiActivities ?? [];
103
+ const orgList = organizations ?? [];
104
+ const hasOrgSection = isOrganizationsLoading || orgList.length > 0 || !!activeOrganization;
105
+ // Build path segments (only used in `app` variant)
106
+ const pathParts = location.pathname.split('/').filter(Boolean);
107
+ const appNameFromRoute = params.appName || pathParts[1];
108
+ const routeType = pathParts[2];
109
+ const baseHref = `/apps/${appNameFromRoute}`;
110
+ const safeObjects = objects ?? [];
111
+ // Filter objects to only those belonging to the current app via its navigation
112
+ const appNameKey = activeAppName || currentAppName || appNameFromRoute;
113
+ const currentApp = (metadataApps || []).find((a) => a.name === appNameKey);
114
+ const appNavObjectNames = new Set();
115
+ const collectNavObjects = (items) => {
116
+ for (const item of items || []) {
117
+ if (item.type === 'object' && item.objectName)
118
+ appNavObjectNames.add(item.objectName);
119
+ if (item.children)
120
+ collectNavObjects(item.children);
121
+ }
122
+ };
123
+ collectNavObjects(currentApp?.navigation || []);
124
+ for (const area of currentApp?.areas || [])
125
+ collectNavObjects(area.navigation || []);
126
+ const appObjects = appNavObjectNames.size > 0
127
+ ? safeObjects.filter((o) => appNavObjectNames.has(o.name))
128
+ : safeObjects.filter((o) => !o.name.startsWith('sys_') && !o.name.startsWith('auth_'));
129
+ const objectSiblings = appObjects.map((o) => ({
130
+ label: objectLabel(o),
131
+ href: `${baseHref}/${o.name}`,
132
+ }));
133
+ const extraSegments = [];
134
+ if (isApp) {
135
+ if (routeType === 'dashboard') {
136
+ extraSegments.push({ label: t('console.breadcrumb.dashboards'), href: baseHref });
137
+ if (pathParts[3])
138
+ extraSegments.push({ label: humanizeSlug(pathParts[3]) });
139
+ }
140
+ else if (routeType === 'page') {
141
+ extraSegments.push({ label: t('console.breadcrumb.pages'), href: baseHref });
142
+ if (pathParts[3])
143
+ extraSegments.push({ label: humanizeSlug(pathParts[3]) });
144
+ }
145
+ else if (routeType === 'report') {
146
+ extraSegments.push({ label: t('console.breadcrumb.reports'), href: baseHref });
147
+ if (pathParts[3])
148
+ extraSegments.push({ label: humanizeSlug(pathParts[3]) });
149
+ }
150
+ else if (routeType === 'system') {
151
+ extraSegments.push({ label: t('console.breadcrumb.system') });
152
+ if (pathParts[3])
153
+ extraSegments.push({ label: humanizeSlug(pathParts[3]) });
154
+ }
155
+ else if (routeType) {
156
+ const currentObject = safeObjects.find((o) => o.name === routeType);
157
+ if (currentObject) {
158
+ extraSegments.push({
159
+ label: objectLabel(currentObject),
160
+ href: `${baseHref}/${routeType}`,
161
+ siblings: objectSiblings,
162
+ });
163
+ if (pathParts[3] === 'record' && pathParts[4]) {
164
+ const shortId = pathParts[4].length > 12 ? `${pathParts[4].slice(0, 8)}…` : pathParts[4];
165
+ const trimmedTitle = recordTitle?.trim();
166
+ const displayTitle = trimmedTitle && trimmedTitle.length > 48
167
+ ? `${trimmedTitle.slice(0, 45)}…`
168
+ : trimmedTitle;
169
+ extraSegments.push({ label: displayTitle || `#${shortId}` });
170
+ }
171
+ else if (pathParts[3] === 'view' && pathParts[4]) {
172
+ extraSegments.push({ label: humanizeSlug(pathParts[4]) });
173
+ }
174
+ }
175
+ }
176
+ }
177
+ const lastSegmentLabel = extraSegments[extraSegments.length - 1]?.label || appName || '';
178
+ return (_jsxs("div", { className: "flex items-center justify-between w-full h-full", children: [_jsxs("div", { className: "flex items-center min-w-0 flex-1", children: [_jsx(Link, { to: "/home", className: "flex items-center justify-center h-7 w-7 shrink-0 rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors", title: "ObjectStack", children: _jsx(Boxes, { className: "h-4 w-4" }) }), resolvedVariant === 'home' && (_jsx("span", { className: "hidden sm:inline ml-2 text-sm font-semibold tracking-tight", children: "ObjectStack" })), resolvedVariant === 'orgs' && (_jsxs(_Fragment, { children: [_jsx(PathSep, {}), _jsx("span", { className: "text-sm font-medium text-foreground/80 px-1.5", children: t('organizations.title', { defaultValue: 'Organizations' }) })] })), isApp && (_jsxs(_Fragment, { children: [_jsx(SidebarTrigger, { className: "md:hidden shrink-0 ml-1" }), activeAppName && onAppChange ? (_jsxs(_Fragment, { children: [_jsx(PathSep, {}), _jsx(AppSwitcher, { activeAppName: activeAppName, onAppChange: onAppChange })] })) : appName ? (_jsxs(_Fragment, { children: [_jsx(PathSep, {}), _jsx("span", { className: "text-sm font-medium text-foreground/80 px-1.5", children: appName })] })) : null, extraSegments.map((seg, i) => {
179
+ const isLast = i === extraSegments.length - 1;
180
+ return (_jsxs("span", { className: "hidden sm:flex items-center min-w-0", children: [_jsx(PathSep, {}), seg.siblings && seg.siblings.length > 1 ? (_jsxs(DropdownMenu, { children: [_jsxs(DropdownMenuTrigger, { className: `flex items-center gap-1 rounded-md px-1.5 py-1 text-sm font-medium transition-colors outline-none hover:bg-accent hover:text-foreground ${!isLast ? 'text-foreground/60' : 'text-foreground/80'}`, children: [seg.label, _jsx(ChevronDown, { className: "h-3.5 w-3.5 text-muted-foreground" })] }), _jsxs(DropdownMenuContent, { align: "start", sideOffset: 8, className: "w-56 max-h-72 overflow-y-auto", children: [_jsx(DropdownMenuLabel, { className: "text-xs text-muted-foreground font-normal", children: "Switch Object" }), _jsx(DropdownMenuSeparator, {}), seg.siblings.map((sibling) => (_jsx(DropdownMenuItem, { asChild: true, children: _jsx(Link, { to: sibling.href, className: "w-full", children: sibling.label }) }, sibling.href)))] })] })) : seg.href ? (_jsx(Link, { to: seg.href, className: `rounded-md px-1.5 py-1 text-sm font-medium transition-colors hover:bg-accent hover:text-foreground truncate max-w-[160px] ${isLast ? 'text-foreground/80' : 'text-foreground/60'}`, children: seg.label })) : (_jsx("span", { className: `px-1.5 py-1 text-sm font-medium truncate max-w-[160px] ${isLast ? 'text-foreground/80' : 'text-foreground/60'}`, children: seg.label }))] }, i));
181
+ }), _jsx("span", { className: "text-sm font-medium sm:hidden truncate min-w-0 ml-1", children: lastSegmentLabel })] }))] }), _jsxs("div", { className: "flex items-center gap-0.5 sm:gap-1 shrink-0 [&>*+*[data-topbar-group]]:ml-1 [&>[data-topbar-group]+[data-topbar-group]]:border-l [&>[data-topbar-group]+[data-topbar-group]]:border-border/60 [&>[data-topbar-group]+[data-topbar-group]]:pl-1 sm:[&>[data-topbar-group]+[data-topbar-group]]:pl-2 sm:[&>[data-topbar-group]+[data-topbar-group]]:ml-2", children: [!isOnline && (_jsxs("div", { className: "flex items-center gap-1 px-2 py-1 rounded-full bg-yellow-100 dark:bg-yellow-900/30 text-yellow-800 dark:text-yellow-200 text-xs font-medium", children: [_jsx("span", { className: "h-2 w-2 rounded-full bg-yellow-500 animate-pulse" }), "Offline"] })), isApp && connectionState && _jsx(ConnectionStatus, { state: connectionState }), isApp && activeUsers.length > 0 && (_jsx("div", { className: "hidden md:flex items-center shrink-0", title: "Users currently online", children: _jsx(PresenceAvatars, { users: activeUsers, size: "sm", maxVisible: 3, showStatus: true }) })), _jsxs("div", { "data-topbar-group": true, className: "flex items-center gap-0.5 sm:gap-1 shrink-0", children: [_jsxs("button", { onClick: () => document.dispatchEvent(new KeyboardEvent('keydown', { key: 'k', metaKey: true })), className: "hidden lg:flex relative items-center gap-2 w-48 xl:w-64 h-8 px-3 text-sm rounded-md border bg-muted/50 text-muted-foreground hover:bg-muted transition-colors", children: [_jsx(Search, { className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "flex-1 text-left text-xs", children: t('console.search', { defaultValue: 'Search...' }) }), _jsxs("kbd", { className: "pointer-events-none inline-flex h-5 items-center gap-0.5 rounded border bg-background px-1.5 text-[10px] font-medium text-muted-foreground", children: [_jsx("span", { className: "text-xs", children: "\u2318" }), "K"] })] }), _jsx(Button, { variant: "ghost", size: "icon", className: "lg:hidden h-8 w-8 shrink-0", onClick: () => document.dispatchEvent(new KeyboardEvent('keydown', { key: 'k', metaKey: true })), "aria-label": t('console.search', { defaultValue: 'Search...' }), children: _jsx(Search, { className: "h-4 w-4" }) })] }), _jsxs("div", { "data-topbar-group": true, className: "flex items-center gap-0.5 shrink-0", children: [_jsx("div", { className: "hidden sm:flex shrink-0", children: _jsx(ActivityFeed, { activities: activeActivities }) }), _jsx(Button, { variant: "ghost", size: "icon", className: "h-8 w-8 hidden md:flex shrink-0", asChild: true, "aria-label": t('sidebar.helpTooltip', { defaultValue: 'Help & Documentation' }), children: _jsx("a", { href: "https://docs.objectstack.ai", target: "_blank", rel: "noopener noreferrer", children: _jsx(HelpCircle, { className: "h-4 w-4" }) }) })] }), _jsxs("div", { "data-topbar-group": true, className: "flex items-center gap-0.5 shrink-0", children: [_jsx("div", { className: "hidden sm:flex shrink-0", children: _jsx(ModeToggle, {}) }), _jsx("div", { className: "hidden sm:flex shrink-0", children: _jsx(LocaleSwitcher, {}) }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", className: "h-8 w-8 shrink-0 rounded-full", children: _jsxs(Avatar, { className: "h-7 w-7 rounded-full", children: [_jsx(AvatarImage, { src: user?.image ?? '/avatars/user.jpg', alt: user?.name ?? 'User' }), _jsx(AvatarFallback, { className: "rounded-full bg-primary text-primary-foreground text-xs", children: getUserInitials(user) })] }) }) }), _jsxs(DropdownMenuContent, { align: "end", className: "min-w-64 rounded-lg", sideOffset: 4, children: [_jsx(DropdownMenuLabel, { className: "p-0 font-normal", children: _jsxs("div", { className: "flex items-center gap-2 px-2 py-2", children: [_jsxs(Avatar, { className: "h-8 w-8 rounded-lg", children: [_jsx(AvatarImage, { src: user?.image ?? '/avatars/user.jpg', 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, {}), _jsxs(DropdownMenuGroup, { children: [hasOrgSection && (_jsxs(DropdownMenuItem, { onClick: () => navigate('/organizations'), className: "cursor-pointer", children: [_jsx(Boxes, { className: "mr-2 h-4 w-4" }), t('organizations.mine', { defaultValue: 'My Organizations' })] })), _jsxs(DropdownMenuItem, { onClick: () => navigate('/apps/setup/system/profile'), children: [_jsx(UserIcon, { className: "mr-2 h-4 w-4" }), t('user.profile', { defaultValue: 'Profile' })] }), _jsxs(DropdownMenuItem, { onClick: () => navigate('/apps/setup'), children: [_jsx(Settings, { className: "mr-2 h-4 w-4" }), t('sidebar.settings', { defaultValue: 'Settings' })] })] }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { className: "text-destructive focus:text-destructive", onClick: () => signOut(), children: [_jsx(LogOut, { className: "mr-2 h-4 w-4" }), t('user.logout', { defaultValue: 'Log out' })] })] })] })] })] })] }));
182
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * AppSidebar
3
+ *
4
+ * Collapsible sidebar navigation for the console. Delegates navigation
5
+ * rendering to `NavigationRenderer` from `@object-ui/layout` (using
6
+ * @dnd-kit drag-to-reorder and pin/unpin), while keeping Console-specific
7
+ * features: app switcher dropdown, user footer, favorites, recent items,
8
+ * and mobile swipe gesture.
9
+ * @module
10
+ */
11
+ export declare function AppSidebar({ activeAppName, onAppChange }: {
12
+ activeAppName: string;
13
+ onAppChange: (name: string) => void;
14
+ }): import("react/jsx-runtime").JSX.Element;