@@ -8,7 +8,7 @@ import * as WOS from "@/app/store/wos";
88import { RpcApi } from "@/app/store/wshclientapi" ;
99import { TabRpcClient } from "@/app/store/wshrpcutil" ;
1010import { 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" ;
1212import debug from "debug" ;
1313import * as jotai from "jotai" ;
1414import { 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 ) ;
0 commit comments