Skip to content

Commit 76f3ac5

Browse files
authored
Consolidate sidebar visilbity in the dojo (#264)
* Add ?features=false to hide the feature chooser in the left bar. * Make title configurable * add viewSelection=false to hide view selector * rename query params for consistency and clarity
1 parent 533a012 commit 76f3ac5

File tree

6 files changed

+107
-37
lines changed

6 files changed

+107
-37
lines changed

typescript-sdk/apps/dojo/scripts/run-dojo-everything.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ const dojo = {
126126
LLAMA_INDEX_URL: 'http://localhost:8007',
127127
MASTRA_URL: 'http://localhost:8008',
128128
PYDANTIC_AI_URL: 'http://localhost:8009',
129+
NEXT_PUBLIC_CUSTOM_DOMAIN_TITLE: 'cpkdojo.local___CopilotKit Feature Viewer',
129130
}
130131
}
131132

typescript-sdk/apps/dojo/src/components/layout/main-layout.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Menu, X } from "lucide-react";
77
import { Button } from "@/components/ui/button";
88

99
import { useURLParams } from "@/contexts/url-params-context";
10+
import { getTitleForCurrentDomain } from "@/utils/domain-config";
1011

1112
export function MainLayout({ children }: { children: React.ReactNode }) {
1213
const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false);
@@ -52,7 +53,7 @@ export function MainLayout({ children }: { children: React.ReactNode }) {
5253
>
5354
{isMobileSidebarOpen ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
5455
</Button>
55-
<h1 className="text-sm font-medium text-center flex-1">AG-UI Dojo</h1>
56+
<h1 className="text-sm font-medium text-center flex-1">{getTitleForCurrentDomain() || "AG-UI Dojo"}</h1>
5657
<div className="w-9" /> {/* Spacer for centering */}
5758
</div>
5859
</div>
@@ -100,8 +101,8 @@ function MaybeSidebar({ isMobile, isMobileSidebarOpen, onMobileClose }: MaybeSid
100101

101102
return (
102103
<div className={`
103-
${isMobile
104-
? 'absolute left-0 top-0 z-50 h-full w-80 transform transition-transform duration-300 ease-in-out'
104+
${isMobile
105+
? 'absolute left-0 top-0 z-50 h-full w-80 transform transition-transform duration-300 ease-in-out'
105106
: 'relative'
106107
}
107108
`}>

typescript-sdk/apps/dojo/src/components/sidebar/sidebar.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { menuIntegrations } from "@/menu";
1919
import { Feature } from "@/types/integration";
2020
import { useURLParams } from "@/contexts/url-params-context";
2121
import { View } from "@/types/interface";
22+
import { getTitleForCurrentDomain } from "@/utils/domain-config";
2223

2324
interface SidebarProps {
2425
isMobile?: boolean;
@@ -28,7 +29,7 @@ interface SidebarProps {
2829
export function Sidebar({ isMobile, onMobileClose }: SidebarProps) {
2930
const router = useRouter();
3031
const pathname = usePathname();
31-
const { view, pickerDisabled, setView } = useURLParams();
32+
const { view, frameworkPickerHidden, viewPickerHidden, featurePickerHidden, setView} = useURLParams();
3233
const [isDarkTheme, setIsDarkTheme] = useState<boolean>(false);
3334

3435
// Extract the current integration ID from the pathname
@@ -98,15 +99,15 @@ export function Sidebar({ isMobile, onMobileClose }: SidebarProps) {
9899
}, []);
99100

100101
return (
101-
<div className={`flex flex-col h-full bg-background border-r
102+
<div className={`flex flex-col h-full bg-background border-r
102103
${isMobile ? 'w-80 shadow-xl' : 'w-74 min-w-[296px] flex-shrink-0'}
103104
`}>
104105
{/* Sidebar Header */}
105106
<div className="p-4 border-b bg-background">
106107
<div className="flex items-center justify-between ml-1">
107108
<div className="flex items-start flex-col">
108109
<h1 className={`text-lg font-light ${isDarkTheme ? "text-white" : "text-gray-900"}`}>
109-
AG-UI Interactive Dojo
110+
{getTitleForCurrentDomain() || "AG-UI Interactive Dojo"}
110111
</h1>
111112
</div>
112113

@@ -115,9 +116,10 @@ export function Sidebar({ isMobile, onMobileClose }: SidebarProps) {
115116
</div>
116117

117118
{/* Controls Section */}
119+
{(!frameworkPickerHidden|| !viewPickerHidden) && (
118120
<div className="p-4 border-b bg-background">
119121
{/* Integration picker */}
120-
{!pickerDisabled && (
122+
{!frameworkPickerHidden&& (
121123
<div className="mb-1">
122124
<label className="block text-sm font-medium text-muted-foreground mb-2">Integrations</label>
123125
<DropdownMenu>
@@ -143,6 +145,7 @@ export function Sidebar({ isMobile, onMobileClose }: SidebarProps) {
143145
)}
144146

145147
{/* Preview/Code Tabs */}
148+
{!viewPickerHidden &&
146149
<div className="mb-1">
147150
<label className="block text-sm font-medium text-muted-foreground mb-2">View</label>
148151
<Tabs
@@ -175,11 +178,13 @@ export function Sidebar({ isMobile, onMobileClose }: SidebarProps) {
175178
</TabsList>
176179
</Tabs>
177180
</div>
181+
}
178182
</div>
183+
)}
179184

180185
{/* Demo List */}
181186
<div className="flex-1 overflow-auto">
182-
{currentIntegration ? (
187+
{(currentIntegration && !featurePickerHidden) ? (
183188
<DemoList
184189
demos={filteredDemos}
185190
selectedDemo={currentDemoId}

typescript-sdk/apps/dojo/src/contexts/url-params-context.tsx

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@ import { View } from "@/types/interface";
77
interface URLParamsState {
88
view: View;
99
sidebarHidden: boolean;
10-
pickerDisabled: boolean;
10+
frameworkPickerHidden: boolean;
11+
viewPickerHidden: boolean;
12+
featurePickerHidden: boolean;
1113
file?: string;
1214
}
1315

1416
interface URLParamsContextType extends URLParamsState {
1517
setView: (view: View) => void;
1618
setSidebarHidden: (disabled: boolean) => void;
17-
setPickerDisabled: (disabled: boolean) => void;
19+
setFrameworkPickerHidden: (disabled: boolean) => void;
20+
setViewPickerHidden: (disabled: boolean) => void;
21+
setFeaturePickerHidden: (disabled: boolean) => void;
1822
setCodeFile: (fileName: string) => void;
1923
}
2024

@@ -32,14 +36,16 @@ export function URLParamsProvider({ children }: URLParamsProviderProps) {
3236
// Initialize state from URL params
3337
const [state, setState] = useState<URLParamsState>(() => ({
3438
view: (searchParams.get("view") as View) || "preview",
35-
sidebarHidden: searchParams.get("sidebar") === "disabled",
36-
pickerDisabled: searchParams.get("picker") === "false",
39+
sidebarHidden: searchParams.get("sidebar") === "false",
40+
frameworkPickerHidden: searchParams.get("frameworkPicker") === "false",
41+
viewPickerHidden: searchParams.get("viewPicker") === "false",
42+
featurePickerHidden: searchParams.get("featurePicker") === "false",
3743
}));
3844

3945
// Update URL when state changes
4046
const updateURL = (newState: Partial<URLParamsState>) => {
4147
const params = new URLSearchParams(searchParams.toString());
42-
48+
4349
// Update view param
4450
if (newState.view !== undefined) {
4551
if (newState.view === "preview") {
@@ -48,22 +54,39 @@ export function URLParamsProvider({ children }: URLParamsProviderProps) {
4854
params.set("view", newState.view);
4955
}
5056
}
51-
57+
5258
// Update sidebar param
5359
if (newState.sidebarHidden !== undefined) {
5460
if (newState.sidebarHidden) {
55-
params.set("sidebar", "disabled");
61+
params.set("sidebar", "false");
5662
} else {
5763
params.delete("sidebar");
5864
}
5965
}
60-
61-
// Update picker param
62-
if (newState.pickerDisabled !== undefined) {
63-
if (newState.pickerDisabled) {
64-
params.set("picker", "false");
66+
67+
// Update frameworkPicker param
68+
if (newState.frameworkPickerHidden !== undefined) {
69+
if (newState.frameworkPickerHidden) {
70+
params.set("frameworkPicker", "false");
6571
} else {
66-
params.delete("picker");
72+
params.delete("frameworkPicker");
73+
}
74+
}
75+
76+
// Update viewPicker param
77+
if (newState.viewPickerHidden !== undefined) {
78+
if (newState.viewPickerHidden) {
79+
params.set("viewPicker", "false");
80+
} else {
81+
params.delete("viewPicker");
82+
}
83+
}
84+
// Update featurePicker param
85+
if (newState.featurePickerHidden !== undefined) {
86+
if (newState.featurePickerHidden) {
87+
params.set("featurePicker", "false");
88+
} else {
89+
params.delete("features");
6790
}
6891
}
6992

@@ -76,9 +99,11 @@ export function URLParamsProvider({ children }: URLParamsProviderProps) {
7699
const newState: URLParamsState = {
77100
view: (searchParams.get("view") as View) || "preview",
78101
sidebarHidden: searchParams.get("sidebar") === "disabled",
79-
pickerDisabled: searchParams.get("picker") === "false",
102+
frameworkPickerHidden: searchParams.get("frameworkPicker") === "false",
103+
viewPickerHidden: searchParams.get("viewPicker") === "false",
104+
featurePickerHidden: searchParams.get("featurePicker") === "false",
80105
};
81-
106+
82107
setState(newState);
83108
}, [searchParams]);
84109

@@ -95,10 +120,22 @@ export function URLParamsProvider({ children }: URLParamsProviderProps) {
95120
updateURL({ sidebarHidden });
96121
};
97122

98-
const setPickerDisabled = (pickerDisabled: boolean) => {
99-
const newState = { ...state, pickerDisabled };
123+
const setFrameworkPickerHidden = (frameworkPickerHidden: boolean) => {
124+
const newState = { ...state, frameworkPickerHidden };
125+
setState(newState);
126+
updateURL({ frameworkPickerHidden });
127+
};
128+
129+
const setViewPickerHidden = (viewPickerHidden: boolean) => {
130+
const newState = { ...state, viewPickerHidden };
131+
setState(newState);
132+
updateURL({ viewPickerHidden });
133+
};
134+
135+
const setFeaturePickerHidden = (featurePickerHidden: boolean) => {
136+
const newState = { ...state, featurePickerHidden };
100137
setState(newState);
101-
updateURL({ pickerDisabled });
138+
updateURL({ featurePickerHidden });
102139
};
103140

104141
const setCodeFile = (fileName: string) => {
@@ -111,7 +148,9 @@ export function URLParamsProvider({ children }: URLParamsProviderProps) {
111148
...state,
112149
setView,
113150
setSidebarHidden,
114-
setPickerDisabled,
151+
setFrameworkPickerHidden,
152+
setViewPickerHidden,
153+
setFeaturePickerHidden,
115154
setCodeFile,
116155
};
117156

typescript-sdk/apps/dojo/src/env.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,28 @@ type envVars = {
88
llamaIndexUrl: string;
99
crewAiUrl: string;
1010
pydanticAIUrl: string;
11+
customDomainTitle: Record<string, string>;
1112
}
1213

1314
export default function getEnvVars(): envVars {
14-
return {
15-
serverStarterUrl: process.env.SERVER_STARTER_URL || 'http://localhost:8000',
16-
serverStarterAllFeaturesUrl: process.env.SERVER_STARTER_ALL_FEATURES_URL || 'http://localhost:8000',
17-
mastraUrl: process.env.MASTRA_URL || 'http://localhost:4111',
18-
langgraphUrl: process.env.LANGGRAPH_URL || 'http://localhost:2024',
19-
langgraphFastApiUrl: process.env.LANGGRAPH_FAST_API_URL || 'http://localhost:8000',
20-
agnoUrl: process.env.AGNO_URL || 'http://localhost:9001',
21-
llamaIndexUrl: process.env.LLAMA_INDEX_URL || 'http://localhost:9000',
22-
crewAiUrl: process.env.CREW_AI_URL || 'http://localhost:9002',
23-
pydanticAIUrl: process.env.PYDANTIC_AI_URL || 'http://localhost:9000',
15+
const customDomainTitle: Record<string, string> = {};
16+
if (process.env.NEXT_PUBLIC_CUSTOM_DOMAIN_TITLE) {
17+
const [domain, title] = process.env.NEXT_PUBLIC_CUSTOM_DOMAIN_TITLE.split('___');
18+
if (domain && title) {
19+
customDomainTitle[domain] = title;
2420
}
21+
}
22+
23+
return {
24+
serverStarterUrl: process.env.SERVER_STARTER_URL || 'http://localhost:8000',
25+
serverStarterAllFeaturesUrl: process.env.SERVER_STARTER_ALL_FEATURES_URL || 'http://localhost:8000',
26+
mastraUrl: process.env.MASTRA_URL || 'http://localhost:4111',
27+
langgraphUrl: process.env.LANGGRAPH_URL || 'http://localhost:2024',
28+
langgraphFastApiUrl: process.env.LANGGRAPH_FAST_API_URL || 'http://localhost:8000',
29+
agnoUrl: process.env.AGNO_URL || 'http://localhost:9001',
30+
llamaIndexUrl: process.env.LLAMA_INDEX_URL || 'http://localhost:9000',
31+
crewAiUrl: process.env.CREW_AI_URL || 'http://localhost:9002',
32+
pydanticAIUrl: process.env.PYDANTIC_AI_URL || 'http://localhost:9000',
33+
customDomainTitle: customDomainTitle,
34+
}
2535
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import getEnvVars from "@/env";
2+
3+
4+
export function getTitleForCurrentDomain(): string | undefined {
5+
const envVars = getEnvVars();
6+
7+
// Check if we're in the browser
8+
if (typeof window == "undefined") {
9+
return undefined;
10+
}
11+
12+
const host = window.location.hostname;
13+
return envVars.customDomainTitle[host] || undefined;
14+
}

0 commit comments

Comments
 (0)