@@ -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,6 +44,7 @@ class WorkspaceLayoutModel {
4444 innerPanelGroupRef : ImperativePanelGroupHandle | null ;
4545 panelContainerRef : HTMLDivElement | null ;
4646 aiPanelWrapperRef : HTMLDivElement | null ;
47+ vtabPanelWrapperRef : HTMLDivElement | null ;
4748 panelVisibleAtom : jotai . PrimitiveAtom < boolean > ;
4849 vtabVisibleAtom : jotai . PrimitiveAtom < boolean > ;
4950
@@ -55,8 +56,8 @@ class WorkspaceLayoutModel {
5556 private initialized : boolean = false ;
5657 private transitionTimeoutRef : NodeJS . Timeout | null = null ;
5758 private focusTimeoutRef : NodeJS . Timeout | null = null ;
58- private debouncedPersistAIWidth : ( width : number ) => void ;
59- private debouncedPersistVTabWidth : ( width : number ) => void ;
59+ private debouncedPersistAIWidth : ( ) => void ;
60+ private debouncedPersistVTabWidth : ( ) => void ;
6061
6162 private constructor ( ) {
6263 this . aiPanelRef = null ;
@@ -65,19 +66,24 @@ class WorkspaceLayoutModel {
6566 this . innerPanelGroupRef = null ;
6667 this . panelContainerRef = null ;
6768 this . aiPanelWrapperRef = null ;
69+ this . vtabPanelWrapperRef = null ;
6870 this . inResize = false ;
6971 this . aiPanelVisible = false ;
7072 this . aiPanelWidth = null ;
7173 this . vtabWidth = VTabBar_DefaultWidth ;
7274 this . vtabVisible = false ;
7375 this . panelVisibleAtom = jotai . atom ( false ) ;
7476 this . vtabVisibleAtom = jotai . atom ( false ) ;
77+ this . initializeFromMeta ( ) ;
7578
7679 this . handleWindowResize = this . handleWindowResize . bind ( this ) ;
7780 this . handleOuterPanelLayout = this . handleOuterPanelLayout . bind ( this ) ;
7881 this . handleInnerPanelLayout = this . handleInnerPanelLayout . bind ( this ) ;
7982
80- this . debouncedPersistAIWidth = debounce ( ( width : number ) => {
83+ this . debouncedPersistAIWidth = debounce ( ( ) => {
84+ if ( ! this . aiPanelVisible ) return ;
85+ const width = this . aiPanelWrapperRef ?. offsetWidth ;
86+ if ( width == null || width <= 0 ) return ;
8187 try {
8288 RpcApi . SetMetaCommand ( TabRpcClient , {
8389 oref : WOS . makeORef ( "tab" , this . getTabId ( ) ) ,
@@ -88,7 +94,10 @@ class WorkspaceLayoutModel {
8894 }
8995 } , 300 ) ;
9096
91- this . debouncedPersistVTabWidth = debounce ( ( width : number ) => {
97+ this . debouncedPersistVTabWidth = debounce ( ( ) => {
98+ if ( ! this . vtabVisible ) return ;
99+ const width = this . vtabPanelWrapperRef ?. offsetWidth ;
100+ if ( width == null || width <= 0 ) return ;
92101 try {
93102 RpcApi . SetMetaCommand ( TabRpcClient , {
94103 oref : WOS . makeORef ( "workspace" , this . getWorkspaceId ( ) ) ,
@@ -130,8 +139,6 @@ class WorkspaceLayoutModel {
130139 }
131140
132141 private initializeFromMeta ( ) : void {
133- if ( this . initialized ) return ;
134- this . initialized = true ;
135142 try {
136143 const savedVisible = globalStore . get ( this . getPanelOpenAtom ( ) ) ;
137144 const savedAIWidth = globalStore . get ( this . getPanelWidthAtom ( ) ) ;
@@ -146,6 +153,10 @@ class WorkspaceLayoutModel {
146153 if ( savedVTabWidth != null && savedVTabWidth > 0 ) {
147154 this . vtabWidth = savedVTabWidth ;
148155 }
156+ const tabBarPosition = globalStore . get ( getSettingsKeyAtom ( "app:tabbar" ) ) ?? "top" ;
157+ const showLeftTabBar = tabBarPosition === "left" && ! isBuilderWindow ( ) ;
158+ this . vtabVisible = showLeftTabBar ;
159+ globalStore . set ( this . vtabVisibleAtom , showLeftTabBar ) ;
149160 } catch ( e ) {
150161 console . warn ( "Failed to initialize from tab meta:" , e ) ;
151162 }
@@ -154,7 +165,6 @@ class WorkspaceLayoutModel {
154165 // ---- Resolved width getters (always clamped) ----
155166
156167 private getResolvedAIWidth ( windowWidth : number ) : number {
157- this . initializeFromMeta ( ) ;
158168 let w = this . aiPanelWidth ;
159169 if ( w == null ) {
160170 w = Math . max ( AIPanel_DefaultWidth , windowWidth * AIPanel_DefaultWidthRatio ) ;
@@ -164,7 +174,6 @@ class WorkspaceLayoutModel {
164174 }
165175
166176 private getResolvedVTabWidth ( ) : number {
167- this . initializeFromMeta ( ) ;
168177 return clampVTabWidth ( this . vtabWidth ) ;
169178 }
170179
@@ -218,17 +227,14 @@ class WorkspaceLayoutModel {
218227 if ( this . vtabVisible && this . aiPanelVisible ) {
219228 // vtab stays constant, aipanel absorbs the change
220229 const vtabW = this . getResolvedVTabWidth ( ) ;
221- const newAIW = clampAIPanelWidth ( newLeftGroupPx - vtabW , windowWidth ) ;
222- this . aiPanelWidth = newAIW ;
223- this . debouncedPersistAIWidth ( newAIW ) ;
230+ this . aiPanelWidth = clampAIPanelWidth ( newLeftGroupPx - vtabW , windowWidth ) ;
231+ this . debouncedPersistAIWidth ( ) ;
224232 } else if ( this . vtabVisible ) {
225- const clamped = clampVTabWidth ( newLeftGroupPx ) ;
226- this . vtabWidth = clamped ;
227- this . debouncedPersistVTabWidth ( clamped ) ;
233+ this . vtabWidth = clampVTabWidth ( newLeftGroupPx ) ;
234+ this . debouncedPersistVTabWidth ( ) ;
228235 } else if ( this . aiPanelVisible ) {
229- const clamped = clampAIPanelWidth ( newLeftGroupPx , windowWidth ) ;
230- this . aiPanelWidth = clamped ;
231- this . debouncedPersistAIWidth ( clamped ) ;
236+ this . aiPanelWidth = clampAIPanelWidth ( newLeftGroupPx , windowWidth ) ;
237+ this . debouncedPersistAIWidth ( ) ;
232238 }
233239
234240 this . commitLayouts ( windowWidth ) ;
@@ -249,11 +255,11 @@ class WorkspaceLayoutModel {
249255
250256 if ( clampedVTab !== this . vtabWidth ) {
251257 this . vtabWidth = clampedVTab ;
252- this . debouncedPersistVTabWidth ( clampedVTab ) ;
258+ this . debouncedPersistVTabWidth ( ) ;
253259 }
254260 if ( newAIW !== this . aiPanelWidth ) {
255261 this . aiPanelWidth = newAIW ;
256- this . debouncedPersistAIWidth ( newAIW ) ;
262+ this . debouncedPersistAIWidth ( ) ;
257263 }
258264
259265 this . commitLayouts ( windowWidth ) ;
@@ -280,6 +286,7 @@ class WorkspaceLayoutModel {
280286 panelContainerRef : HTMLDivElement ,
281287 aiPanelWrapperRef : HTMLDivElement ,
282288 vtabPanelRef ?: ImperativePanelHandle ,
289+ vtabPanelWrapperRef ?: HTMLDivElement ,
283290 showLeftTabBar ?: boolean
284291 ) : void {
285292 this . aiPanelRef = aiPanelRef ;
@@ -288,6 +295,7 @@ class WorkspaceLayoutModel {
288295 this . innerPanelGroupRef = innerPanelGroupRef ;
289296 this . panelContainerRef = panelContainerRef ;
290297 this . aiPanelWrapperRef = aiPanelWrapperRef ;
298+ this . vtabPanelWrapperRef = vtabPanelWrapperRef ?? null ;
291299 this . vtabVisible = showLeftTabBar ?? false ;
292300 globalStore . set ( this . vtabVisibleAtom , this . vtabVisible ) ;
293301 this . syncPanelCollapse ( ) ;
@@ -342,7 +350,6 @@ class WorkspaceLayoutModel {
342350 // ---- Public getters ----
343351
344352 getAIPanelVisible ( ) : boolean {
345- this . initializeFromMeta ( ) ;
346353 return this . aiPanelVisible ;
347354 }
348355
@@ -353,15 +360,13 @@ class WorkspaceLayoutModel {
353360 // ---- Initial percentage helpers (used by workspace.tsx for defaultSize) ----
354361
355362 getLeftGroupInitialPercentage ( windowWidth : number , showLeftTabBar : boolean ) : number {
356- this . initializeFromMeta ( ) ;
357363 const vtabW = showLeftTabBar && ! isBuilderWindow ( ) ? this . getResolvedVTabWidth ( ) : 0 ;
358364 const aiW = this . aiPanelVisible ? this . getResolvedAIWidth ( windowWidth ) : 0 ;
359365 return ( ( vtabW + aiW ) / windowWidth ) * 100 ;
360366 }
361367
362368 getInnerVTabInitialPercentage ( windowWidth : number , showLeftTabBar : boolean ) : number {
363369 if ( ! showLeftTabBar || isBuilderWindow ( ) ) return 0 ;
364- this . initializeFromMeta ( ) ;
365370 const vtabW = this . getResolvedVTabWidth ( ) ;
366371 const aiW = this . aiPanelVisible ? this . getResolvedAIWidth ( windowWidth ) : 0 ;
367372 const total = vtabW + aiW ;
@@ -370,7 +375,6 @@ class WorkspaceLayoutModel {
370375 }
371376
372377 getInnerAIPanelInitialPercentage ( windowWidth : number , showLeftTabBar : boolean ) : number {
373- this . initializeFromMeta ( ) ;
374378 const vtabW = showLeftTabBar && ! isBuilderWindow ( ) ? this . getResolvedVTabWidth ( ) : 0 ;
375379 const aiW = this . aiPanelVisible ? this . getResolvedAIWidth ( windowWidth ) : 0 ;
376380 const total = vtabW + aiW ;
0 commit comments