-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathConsoleLayout.tsx
More file actions
151 lines (141 loc) · 5.31 KB
/
Copy pathConsoleLayout.tsx
File metadata and controls
151 lines (141 loc) · 5.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/**
* ConsoleLayout
*
* Root layout shell for the console application. Composes the AppShell
* with the UnifiedSidebar, header, and main content area.
* Includes the global floating chatbot (FAB) widget.
* Sets navigation context to 'app' for app-specific routes.
* @module
*/
import React, { useEffect } from 'react';
import { AppShell } from '@object-ui/layout';
import { useDiscovery } from '@object-ui/react';
// Lightweight FAB stub — the heavy chat chunk graph (plugin-chatbot,
// shiki, streamdown, mermaid, @ai-sdk, ~20MB) only downloads on first
// hover/click. See ConsoleChatbotFab.tsx.
import { ConsoleChatbotFab } from './ConsoleChatbotFab';
import { DraftPreviewBar } from '../preview/DraftPreviewBar';
import { UnpublishedAppBar } from '../preview/UnpublishedAppBar';
import { UnifiedSidebar } from './UnifiedSidebar';
import { AppHeader } from './AppHeader';
import { MobileViewSwitcherProvider } from './MobileViewSwitcherContext';
import { useResponsiveSidebar } from '../hooks/useResponsiveSidebar';
import { useNavigationContext } from '../context/NavigationContext';
import { resolveI18nLabel } from '../utils';
import { getProductName, getRuntimeConfig } from '../runtime-config';
import type { ConnectionState } from '@object-ui/data-objectstack';
/** Minimal object shape used by the chatbot context */
interface ConsoleObject {
name: string;
label?: string;
}
interface ConsoleLayoutProps {
children: React.ReactNode;
activeAppName: string;
activeApp: any;
onAppChange: (name: string) => void;
objects: any[];
connectionState?: ConnectionState;
/**
* Signed-in user id. Forwarded to the floating chatbot so it can hydrate
* server-backed conversation history. Omit for unauthenticated/local-only.
*/
userId?: string;
}
/** Inner component that can access SidebarProvider context */
function ConsoleLayoutInner({ children }: { children: React.ReactNode }) {
useResponsiveSidebar();
return <>{children}</>;
}
/** Floating chatbot wired with useObjectChat for demo auto-response */
// (moved to ./ConsoleFloatingChatbot.tsx for code-splitting)
export function ConsoleLayout({
children,
activeAppName,
activeApp,
onAppChange,
objects,
connectionState,
userId,
}: ConsoleLayoutProps) {
const appLabel = resolveI18nLabel(activeApp?.label) || activeAppName;
const { isAiEnabled } = useDiscovery();
// Trust an explicit `VITE_AI_BASE_URL` opt-in even when discovery reports
// AI as disabled (e.g. framework started without `--preset full`).
const aiBaseUrlConfigured = Boolean(import.meta.env?.VITE_AI_BASE_URL);
const showChatbot = isAiEnabled || aiBaseUrlConfigured;
// AI Studio (AI-driven metadata authoring / "online development") can be
// turned off per deployment. When off, suppress the metadata-authoring
// assistant so the chatbot falls back to the generic data assistant — the
// generic data-chat experience stays available.
const aiStudioEnabled = getRuntimeConfig().features.aiStudio !== false;
const effectiveDefaultAgent =
!aiStudioEnabled && activeApp?.defaultAgent === 'metadata_assistant'
? undefined
: activeApp?.defaultAgent;
const { setContext, setCurrentAppName } = useNavigationContext();
// Set navigation context to 'app' when this layout mounts
useEffect(() => {
setContext('app');
setCurrentAppName(activeAppName);
}, [setContext, setCurrentAppName, activeAppName]);
return (
<MobileViewSwitcherProvider>
<AppShell
sidebar={
<UnifiedSidebar
activeAppName={activeAppName}
onAppChange={onAppChange}
/>
}
navbar={
<AppHeader
variant="app"
appName={appLabel}
objects={objects}
connectionState={connectionState}
activeAppName={activeAppName}
onAppChange={onAppChange}
/>
}
className="!p-0 overflow-y-auto overflow-x-hidden bg-muted/5"
branding={
activeApp?.branding
? {
primaryColor: activeApp.branding.primaryColor,
accentColor: activeApp.branding.accentColor,
favicon: activeApp.branding.favicon,
logo: activeApp.branding.logo,
title: activeApp.label
? `${resolveI18nLabel(activeApp.label)} — ${getProductName()}`
: undefined,
}
: undefined
}
>
<ConsoleLayoutInner>
{/* ADR-0037: unmistakable watermark while rendering the draft overlay
(?preview=draft) — with one-click exit and one-click Publish. */}
<DraftPreviewBar />
{/* ADR-0045: materialized-but-unlisted app — real and interactive,
invisible to end users until the Publish visibility flip. */}
<UnpublishedAppBar />
{children}
</ConsoleLayoutInner>
{/* Global floating chatbot — rendered when AI service is available
OR when `VITE_AI_BASE_URL` has been explicitly configured. The
stub FAB is dependency-free; the heavy chat bundle only loads
on first interaction. */}
{showChatbot && (
<ConsoleChatbotFab
appLabel={appLabel}
appName={activeAppName}
defaultAgent={effectiveDefaultAgent}
objects={objects}
userId={userId}
/>
)}
</AppShell>
</MobileViewSwitcherProvider>
);
}