Skip to content

Commit f9505d4

Browse files
authored
Fix terminal not appearing in developer panel (#7753)
`isPanelHidden` was called at module load time when computing `DEFAULT_PANEL_LAYOUT`, before capabilities were received from the backend. This meant terminal was filtered out and never added to the stored layout. These changes check static `hidden` property at module init, defer capability checks to render time.
1 parent de6f700 commit f9505d4

File tree

4 files changed

+30
-14
lines changed

4 files changed

+30
-14
lines changed

frontend/src/components/editor/chrome/state.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { createReducerAndAtoms } from "@/utils/createReducer";
88
import { jotaiJsonStorage } from "@/utils/storage/jotai";
99
import { ZodLocalStorage } from "@/utils/storage/typed";
1010
import type { PanelSection, PanelType } from "./types";
11-
import { isPanelHidden, PANELS } from "./types";
11+
import { PANELS } from "./types";
1212

1313
export interface ChromeState {
1414
selectedPanel: PanelType | undefined;
@@ -28,10 +28,10 @@ export interface PanelLayout {
2828

2929
const DEFAULT_PANEL_LAYOUT: PanelLayout = {
3030
sidebar: PANELS.filter(
31-
(p) => !isPanelHidden(p) && p.defaultSection === "sidebar",
31+
(p) => !p.hidden && p.defaultSection === "sidebar",
3232
).map((p) => p.type),
3333
developerPanel: PANELS.filter(
34-
(p) => !isPanelHidden(p) && p.defaultSection === "developer-panel",
34+
(p) => !p.hidden && p.defaultSection === "developer-panel",
3535
).map((p) => p.type),
3636
};
3737

frontend/src/components/editor/chrome/types.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {
1818
VariableIcon,
1919
XCircleIcon,
2020
} from "lucide-react";
21-
import { hasCapability } from "@/core/config/capabilities";
2221
import { getFeatureFlag } from "@/core/config/feature-flag";
2322
import type { Capabilities } from "@/core/kernel/messages";
2423
import { isWasm } from "@/core/wasm/utils";
@@ -188,12 +187,15 @@ export const PANEL_MAP = new Map<PanelType, PanelDescriptor>(
188187
* Check if a panel should be hidden based on its `hidden` property
189188
* and `requiredCapability`.
190189
*/
191-
export function isPanelHidden(panel: PanelDescriptor): boolean {
190+
export function isPanelHidden(
191+
panel: PanelDescriptor,
192+
capabilities: Capabilities,
193+
): boolean {
192194
if (panel.hidden) {
193195
return true;
194196
}
195197
if (panel.requiredCapability) {
196-
if (!hasCapability(panel.requiredCapability)) {
198+
if (!capabilities[panel.requiredCapability]) {
197199
return true;
198200
}
199201
}

frontend/src/components/editor/chrome/wrapper/app-chrome.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { ReorderableList } from "@/components/ui/reorderable-list";
2222
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
2323
import { LazyMount } from "@/components/utils/lazy-mount";
2424
import { cellErrorCount } from "@/core/cells/cells";
25+
import { capabilitiesAtom } from "@/core/config/capabilities";
2526
import { getFeatureFlag } from "@/core/config/feature-flag";
2627
import { cn } from "@/utils/cn";
2728
import { ErrorBoundary } from "../../boundary/ErrorBoundary";
@@ -83,14 +84,20 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
8384
const { dependencyPanelTab, setDependencyPanelTab } = useDependencyPanelTab();
8485
const errorCount = useAtomValue(cellErrorCount);
8586
const [panelLayout, setPanelLayout] = useAtom(panelLayoutAtom);
87+
// Subscribe to capabilities to re-render when they change (e.g., terminal capability)
88+
const capabilities = useAtomValue(capabilitiesAtom);
8689

8790
// Convert current developer panel items to PanelDescriptors
91+
// Filter out hidden panels (e.g., terminal when capability is not available)
8892
const devPanelItems = useMemo(() => {
8993
return panelLayout.developerPanel.flatMap((id) => {
9094
const panel = PANEL_MAP.get(id);
91-
return panel ? [panel] : [];
95+
if (!panel || isPanelHidden(panel, capabilities)) {
96+
return [];
97+
}
98+
return [panel];
9299
});
93-
}, [panelLayout.developerPanel]);
100+
}, [panelLayout.developerPanel, capabilities]);
94101

95102
const handleSetDevPanelItems = (items: PanelDescriptor[]) => {
96103
setPanelLayout((prev) => ({
@@ -127,7 +134,7 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
127134
const availableDevPanels = useMemo(() => {
128135
const sidebarIds = new Set(panelLayout.sidebar);
129136
return PANELS.filter((p) => {
130-
if (isPanelHidden(p)) {
137+
if (isPanelHidden(p, capabilities)) {
131138
return false;
132139
}
133140
// Exclude panels that are in the sidebar
@@ -136,7 +143,7 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
136143
}
137144
return true;
138145
});
139-
}, [panelLayout.sidebar]);
146+
}, [panelLayout.sidebar, capabilities]);
140147

141148
// sync sidebar
142149
useEffect(() => {

frontend/src/components/editor/chrome/wrapper/sidebar.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
cellErrorCount,
1212
notebookQueuedOrRunningCountAtom,
1313
} from "@/core/cells/cells";
14+
import { capabilitiesAtom } from "@/core/config/capabilities";
1415
import { cn } from "@/utils/cn";
1516
import { FeedbackButton } from "../components/feedback-button";
1617
import { panelLayoutAtom, useChromeActions, useChromeState } from "../state";
@@ -25,6 +26,8 @@ export const Sidebar: React.FC = () => {
2526
const { selectedPanel, selectedDeveloperPanelTab } = useChromeState();
2627
const { toggleApplication, openApplication } = useChromeActions();
2728
const [panelLayout, setPanelLayout] = useAtom(panelLayoutAtom);
29+
// Subscribe to capabilities to re-render when they change
30+
const capabilities = useAtomValue(capabilitiesAtom);
2831

2932
const renderIcon = ({ Icon }: PanelDescriptor, className?: string) => {
3033
return <Icon className={cn("h-5 w-5", className)} />;
@@ -35,7 +38,7 @@ export const Sidebar: React.FC = () => {
3538
const availableSidebarPanels = useMemo(() => {
3639
const devPanelIds = new Set(panelLayout.developerPanel);
3740
return PANELS.filter((p) => {
38-
if (isPanelHidden(p)) {
41+
if (isPanelHidden(p, capabilities)) {
3942
return false;
4043
}
4144
// Exclude panels that are in the developer panel
@@ -44,15 +47,19 @@ export const Sidebar: React.FC = () => {
4447
}
4548
return true;
4649
});
47-
}, [panelLayout.developerPanel]);
50+
}, [panelLayout.developerPanel, capabilities]);
4851

4952
// Convert current sidebar items to PanelDescriptors
53+
// Filter out hidden panels (e.g., when capability is not available)
5054
const sidebarItems = useMemo(() => {
5155
return panelLayout.sidebar.flatMap((id) => {
5256
const panel = PANEL_MAP.get(id);
53-
return panel ? [panel] : [];
57+
if (!panel || isPanelHidden(panel, capabilities)) {
58+
return [];
59+
}
60+
return [panel];
5461
});
55-
}, [panelLayout.sidebar]);
62+
}, [panelLayout.sidebar, capabilities]);
5663

5764
const handleSetSidebarItems = (items: PanelDescriptor[]) => {
5865
setPanelLayout((prev) => ({

0 commit comments

Comments
 (0)