Skip to content

Commit 7540b13

Browse files
authored
Fix bug with WaveAI panel growing in size when vtabs were open (#3097)
1 parent 884884f commit 7540b13

File tree

2 files changed

+38
-37
lines changed

2 files changed

+38
-37
lines changed

frontend/app/workspace/workspace-layout-model.ts

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import * as WOS from "@/app/store/wos";
88
import { RpcApi } from "@/app/store/wshclientapi";
99
import { TabRpcClient } from "@/app/store/wshrpcutil";
1010
import { getLayoutModelForStaticTab } from "@/layout/lib/layoutModelHooks";
11-
import { atoms, getApi, getOrefMetaKeyAtom, recordTEvent, refocusNode } from "@/store/global";
11+
import { atoms, getApi, getOrefMetaKeyAtom, getSettingsKeyAtom, recordTEvent, refocusNode } from "@/store/global";
1212
import debug from "debug";
1313
import * as jotai from "jotai";
1414
import { debounce } from "lodash-es";
@@ -44,19 +44,18 @@ class WorkspaceLayoutModel {
4444
innerPanelGroupRef: ImperativePanelGroupHandle | null;
4545
panelContainerRef: HTMLDivElement | null;
4646
aiPanelWrapperRef: HTMLDivElement | null;
47+
vtabPanelWrapperRef: HTMLDivElement | null;
4748
panelVisibleAtom: jotai.PrimitiveAtom<boolean>;
48-
vtabVisibleAtom: jotai.PrimitiveAtom<boolean>;
4949

5050
private inResize: boolean;
5151
private aiPanelVisible: boolean;
5252
private aiPanelWidth: number | null;
5353
private vtabWidth: number;
5454
private vtabVisible: boolean;
55-
private initialized: boolean = false;
5655
private transitionTimeoutRef: NodeJS.Timeout | null = null;
5756
private focusTimeoutRef: NodeJS.Timeout | null = null;
58-
private debouncedPersistAIWidth: (width: number) => void;
59-
private debouncedPersistVTabWidth: (width: number) => void;
57+
private debouncedPersistAIWidth: () => void;
58+
private debouncedPersistVTabWidth: () => void;
6059

6160
private constructor() {
6261
this.aiPanelRef = null;
@@ -65,19 +64,23 @@ class WorkspaceLayoutModel {
6564
this.innerPanelGroupRef = null;
6665
this.panelContainerRef = null;
6766
this.aiPanelWrapperRef = null;
67+
this.vtabPanelWrapperRef = null;
6868
this.inResize = false;
6969
this.aiPanelVisible = false;
7070
this.aiPanelWidth = null;
7171
this.vtabWidth = VTabBar_DefaultWidth;
7272
this.vtabVisible = false;
7373
this.panelVisibleAtom = jotai.atom(false);
74-
this.vtabVisibleAtom = jotai.atom(false);
74+
this.initializeFromMeta();
7575

7676
this.handleWindowResize = this.handleWindowResize.bind(this);
7777
this.handleOuterPanelLayout = this.handleOuterPanelLayout.bind(this);
7878
this.handleInnerPanelLayout = this.handleInnerPanelLayout.bind(this);
7979

80-
this.debouncedPersistAIWidth = debounce((width: number) => {
80+
this.debouncedPersistAIWidth = debounce(() => {
81+
if (!this.aiPanelVisible) return;
82+
const width = this.aiPanelWrapperRef?.offsetWidth;
83+
if (width == null || width <= 0) return;
8184
try {
8285
RpcApi.SetMetaCommand(TabRpcClient, {
8386
oref: WOS.makeORef("tab", this.getTabId()),
@@ -88,7 +91,10 @@ class WorkspaceLayoutModel {
8891
}
8992
}, 300);
9093

91-
this.debouncedPersistVTabWidth = debounce((width: number) => {
94+
this.debouncedPersistVTabWidth = debounce(() => {
95+
if (!this.vtabVisible) return;
96+
const width = this.vtabPanelWrapperRef?.offsetWidth;
97+
if (width == null || width <= 0) return;
9298
try {
9399
RpcApi.SetMetaCommand(TabRpcClient, {
94100
oref: WOS.makeORef("workspace", this.getWorkspaceId()),
@@ -130,8 +136,6 @@ class WorkspaceLayoutModel {
130136
}
131137

132138
private initializeFromMeta(): void {
133-
if (this.initialized) return;
134-
this.initialized = true;
135139
try {
136140
const savedVisible = globalStore.get(this.getPanelOpenAtom());
137141
const savedAIWidth = globalStore.get(this.getPanelWidthAtom());
@@ -146,6 +150,9 @@ class WorkspaceLayoutModel {
146150
if (savedVTabWidth != null && savedVTabWidth > 0) {
147151
this.vtabWidth = savedVTabWidth;
148152
}
153+
const tabBarPosition = globalStore.get(getSettingsKeyAtom("app:tabbar")) ?? "top";
154+
const showLeftTabBar = tabBarPosition === "left" && !isBuilderWindow();
155+
this.vtabVisible = showLeftTabBar;
149156
} catch (e) {
150157
console.warn("Failed to initialize from tab meta:", e);
151158
}
@@ -154,7 +161,6 @@ class WorkspaceLayoutModel {
154161
// ---- Resolved width getters (always clamped) ----
155162

156163
private getResolvedAIWidth(windowWidth: number): number {
157-
this.initializeFromMeta();
158164
let w = this.aiPanelWidth;
159165
if (w == null) {
160166
w = Math.max(AIPanel_DefaultWidth, windowWidth * AIPanel_DefaultWidthRatio);
@@ -164,7 +170,6 @@ class WorkspaceLayoutModel {
164170
}
165171

166172
private getResolvedVTabWidth(): number {
167-
this.initializeFromMeta();
168173
return clampVTabWidth(this.vtabWidth);
169174
}
170175

@@ -218,17 +223,14 @@ class WorkspaceLayoutModel {
218223
if (this.vtabVisible && this.aiPanelVisible) {
219224
// vtab stays constant, aipanel absorbs the change
220225
const vtabW = this.getResolvedVTabWidth();
221-
const newAIW = clampAIPanelWidth(newLeftGroupPx - vtabW, windowWidth);
222-
this.aiPanelWidth = newAIW;
223-
this.debouncedPersistAIWidth(newAIW);
226+
this.aiPanelWidth = clampAIPanelWidth(newLeftGroupPx - vtabW, windowWidth);
227+
this.debouncedPersistAIWidth();
224228
} else if (this.vtabVisible) {
225-
const clamped = clampVTabWidth(newLeftGroupPx);
226-
this.vtabWidth = clamped;
227-
this.debouncedPersistVTabWidth(clamped);
229+
this.vtabWidth = clampVTabWidth(newLeftGroupPx);
230+
this.debouncedPersistVTabWidth();
228231
} else if (this.aiPanelVisible) {
229-
const clamped = clampAIPanelWidth(newLeftGroupPx, windowWidth);
230-
this.aiPanelWidth = clamped;
231-
this.debouncedPersistAIWidth(clamped);
232+
this.aiPanelWidth = clampAIPanelWidth(newLeftGroupPx, windowWidth);
233+
this.debouncedPersistAIWidth();
232234
}
233235

234236
this.commitLayouts(windowWidth);
@@ -249,11 +251,11 @@ class WorkspaceLayoutModel {
249251

250252
if (clampedVTab !== this.vtabWidth) {
251253
this.vtabWidth = clampedVTab;
252-
this.debouncedPersistVTabWidth(clampedVTab);
254+
this.debouncedPersistVTabWidth();
253255
}
254256
if (newAIW !== this.aiPanelWidth) {
255257
this.aiPanelWidth = newAIW;
256-
this.debouncedPersistAIWidth(newAIW);
258+
this.debouncedPersistAIWidth();
257259
}
258260

259261
this.commitLayouts(windowWidth);
@@ -280,6 +282,7 @@ class WorkspaceLayoutModel {
280282
panelContainerRef: HTMLDivElement,
281283
aiPanelWrapperRef: HTMLDivElement,
282284
vtabPanelRef?: ImperativePanelHandle,
285+
vtabPanelWrapperRef?: HTMLDivElement,
283286
showLeftTabBar?: boolean
284287
): void {
285288
this.aiPanelRef = aiPanelRef;
@@ -288,8 +291,8 @@ class WorkspaceLayoutModel {
288291
this.innerPanelGroupRef = innerPanelGroupRef;
289292
this.panelContainerRef = panelContainerRef;
290293
this.aiPanelWrapperRef = aiPanelWrapperRef;
294+
this.vtabPanelWrapperRef = vtabPanelWrapperRef ?? null;
291295
this.vtabVisible = showLeftTabBar ?? false;
292-
globalStore.set(this.vtabVisibleAtom, this.vtabVisible);
293296
this.syncPanelCollapse();
294297
this.commitLayouts(window.innerWidth);
295298
}
@@ -342,7 +345,6 @@ class WorkspaceLayoutModel {
342345
// ---- Public getters ----
343346

344347
getAIPanelVisible(): boolean {
345-
this.initializeFromMeta();
346348
return this.aiPanelVisible;
347349
}
348350

@@ -353,15 +355,13 @@ class WorkspaceLayoutModel {
353355
// ---- Initial percentage helpers (used by workspace.tsx for defaultSize) ----
354356

355357
getLeftGroupInitialPercentage(windowWidth: number, showLeftTabBar: boolean): number {
356-
this.initializeFromMeta();
357358
const vtabW = showLeftTabBar && !isBuilderWindow() ? this.getResolvedVTabWidth() : 0;
358359
const aiW = this.aiPanelVisible ? this.getResolvedAIWidth(windowWidth) : 0;
359360
return ((vtabW + aiW) / windowWidth) * 100;
360361
}
361362

362363
getInnerVTabInitialPercentage(windowWidth: number, showLeftTabBar: boolean): number {
363364
if (!showLeftTabBar || isBuilderWindow()) return 0;
364-
this.initializeFromMeta();
365365
const vtabW = this.getResolvedVTabWidth();
366366
const aiW = this.aiPanelVisible ? this.getResolvedAIWidth(windowWidth) : 0;
367367
const total = vtabW + aiW;
@@ -370,7 +370,6 @@ class WorkspaceLayoutModel {
370370
}
371371

372372
getInnerAIPanelInitialPercentage(windowWidth: number, showLeftTabBar: boolean): number {
373-
this.initializeFromMeta();
374373
const vtabW = showLeftTabBar && !isBuilderWindow() ? this.getResolvedVTabWidth() : 0;
375374
const aiW = this.aiPanelVisible ? this.getResolvedAIWidth(windowWidth) : 0;
376375
const total = vtabW + aiW;
@@ -424,7 +423,6 @@ class WorkspaceLayoutModel {
424423
setShowLeftTabBar(showLeftTabBar: boolean): void {
425424
if (this.vtabVisible === showLeftTabBar) return;
426425
this.vtabVisible = showLeftTabBar;
427-
globalStore.set(this.vtabVisibleAtom, showLeftTabBar);
428426
this.enableTransitions(250);
429427
this.syncPanelCollapse();
430428
this.commitLayouts(window.innerWidth);

frontend/app/workspace/workspace.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ const WorkspaceElem = memo(() => {
4646
const tabBarPosition = useAtomValue(getSettingsKeyAtom("app:tabbar")) ?? "top";
4747
const showLeftTabBar = tabBarPosition === "left";
4848
const aiPanelVisible = useAtomValue(workspaceLayoutModel.panelVisibleAtom);
49-
const vtabVisible = useAtomValue(workspaceLayoutModel.vtabVisibleAtom);
5049
const windowWidth = window.innerWidth;
5150
const leftGroupInitialPct = workspaceLayoutModel.getLeftGroupInitialPercentage(windowWidth, showLeftTabBar);
5251
const innerVTabInitialPct = workspaceLayoutModel.getInnerVTabInitialPercentage(windowWidth, showLeftTabBar);
@@ -57,6 +56,7 @@ const WorkspaceElem = memo(() => {
5756
const vtabPanelRef = useRef<ImperativePanelHandle>(null);
5857
const panelContainerRef = useRef<HTMLDivElement>(null);
5958
const aiPanelWrapperRef = useRef<HTMLDivElement>(null);
59+
const vtabPanelWrapperRef = useRef<HTMLDivElement>(null);
6060

6161
// showLeftTabBar is passed as a seed value only; subsequent changes are handled by setShowLeftTabBar below.
6262
// Do NOT add showLeftTabBar as a dep here — re-registering refs on config changes would redundantly re-run commitLayouts.
@@ -75,6 +75,7 @@ const WorkspaceElem = memo(() => {
7575
panelContainerRef.current,
7676
aiPanelWrapperRef.current,
7777
vtabPanelRef.current ?? undefined,
78+
vtabPanelWrapperRef.current ?? undefined,
7879
showLeftTabBar
7980
);
8081
}
@@ -85,24 +86,24 @@ const WorkspaceElem = memo(() => {
8586
getApi().setWaveAIOpen(isVisible);
8687
}, []);
8788

88-
useEffect(() => {
89-
workspaceLayoutModel.setShowLeftTabBar(showLeftTabBar);
90-
}, [showLeftTabBar]);
91-
9289
useEffect(() => {
9390
window.addEventListener("resize", workspaceLayoutModel.handleWindowResize);
9491
return () => window.removeEventListener("resize", workspaceLayoutModel.handleWindowResize);
9592
}, []);
9693

94+
useEffect(() => {
95+
workspaceLayoutModel.setShowLeftTabBar(showLeftTabBar);
96+
}, [showLeftTabBar]);
97+
9798
useEffect(() => {
9899
const handleFocus = () => workspaceLayoutModel.syncVTabWidthFromMeta();
99100
window.addEventListener("focus", handleFocus);
100101
return () => window.removeEventListener("focus", handleFocus);
101102
}, []);
102103

103-
const innerHandleVisible = vtabVisible && aiPanelVisible;
104+
const innerHandleVisible = showLeftTabBar && aiPanelVisible;
104105
const innerHandleClass = `bg-transparent hover:bg-zinc-500/20 transition-colors ${innerHandleVisible ? "w-0.5" : "w-0 pointer-events-none"}`;
105-
const outerHandleVisible = vtabVisible || aiPanelVisible;
106+
const outerHandleVisible = showLeftTabBar || aiPanelVisible;
106107
const outerHandleClass = `bg-transparent hover:bg-zinc-500/20 transition-colors ${outerHandleVisible ? "w-0.5" : "w-0 pointer-events-none"}`;
107108

108109
return (
@@ -129,7 +130,9 @@ const WorkspaceElem = memo(() => {
129130
order={0}
130131
className="overflow-hidden"
131132
>
132-
{showLeftTabBar && <VTabBar workspace={ws} />}
133+
<div ref={vtabPanelWrapperRef} className="w-full h-full">
134+
{showLeftTabBar && <VTabBar workspace={ws} />}
135+
</div>
133136
</Panel>
134137
<PanelResizeHandle className={innerHandleClass} />
135138
<Panel

0 commit comments

Comments
 (0)