Path: blob/main/src/vs/sessions/browser/layoutPolicy.ts
13389 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Disposable } from '../../base/common/lifecycle.js';6import { observableValue, derived, IObservable } from '../../base/common/observable.js';7import { isIOS, isMobile } from '../../base/common/platform.js';8import { isAndroid } from '../../base/browser/browser.js';9import { Gesture } from '../../base/browser/touch.js';1011/** Viewport classification based on container width. */12export type ViewportClass = 'phone' | 'tablet' | 'desktop';1314/** Default visibility for each workbench part. */15export interface IPartVisibilityDefaults {16readonly sidebar: boolean;17readonly auxiliaryBar: boolean;18readonly panel: boolean;19readonly chatBar: boolean;20readonly editor: boolean;21}2223/** Default sizes (in pixels) for each workbench part. */24export interface IPartSizeDefaults {25readonly sideBarSize: number;26readonly auxiliaryBarSize: number;27readonly panelSize: number;28readonly chatBarWidth: number;29}3031const PHONE_MAX_WIDTH = 640;32const TABLET_MAX_WIDTH = 1024;3334/**35* Whether the current platform is a phone/tablet OS. The phone layout is36* only applied on actual mobile devices so that resizing a desktop window37* below 640px does not switch the agents workbench into phone mode.38*/39const isMobilePlatform = isMobile;4041/**42* Classifies the viewport into one of three classes based on width.43* Phone and tablet classifications are gated on a mobile OS; desktop44* browsers and Electron always report `desktop` regardless of width.45*/46function classifyViewport(width: number): ViewportClass {47if (!isMobilePlatform) {48return 'desktop';49}50if (width < PHONE_MAX_WIDTH) {51return 'phone';52}53if (width < TABLET_MAX_WIDTH) {54return 'tablet';55}56return 'desktop';57}5859/**60* Observable-based viewport classification and layout policy for61* the Sessions workbench. Consumed by `SessionsWorkbench` to drive62* part visibility, sizing, and behavior based on viewport dimensions63* and platform.64*/65export class SessionsLayoutPolicy extends Disposable {6667// --- Platform flags (static, read once) ---6869/** Whether the current platform is iOS. */70readonly isIOS: boolean;7172/** Whether the current platform is Android. */73readonly isAndroid: boolean;7475/** Whether the current device supports touch input. */76readonly isTouchDevice: boolean;7778// --- Observables ---7980private readonly _viewportClass = observableValue<ViewportClass>(this, 'desktop');8182/** Current viewport class derived from the most recent `update()` call. */83readonly viewportClass: IObservable<ViewportClass> = this._viewportClass;8485/** `true` when the viewport class is `phone`. */86readonly isPhoneLayout: IObservable<boolean> = derived(this, reader => {87return this._viewportClass.read(reader) === 'phone';88});8990constructor() {91super();9293this.isIOS = isIOS;94this.isAndroid = isAndroid;95this.isTouchDevice = Gesture.isTouchDevice();96}9798/**99* Update the viewport classification. Call this from the workbench100* `layout()` method whenever the container dimensions change.101*102* @param width Container width in pixels.103* @param height Container height in pixels (reserved for future use).104*/105update(width: number, _height: number): void {106const next = classifyViewport(width);107if (this._viewportClass.get() !== next) {108this._viewportClass.set(next, undefined);109}110}111112/**113* Returns the default part visibility for the given viewport class.114* If no class is supplied the current observed class is used.115*/116getPartVisibilityDefaults(viewportClass?: ViewportClass): IPartVisibilityDefaults {117const vc = viewportClass ?? this._viewportClass.get();118switch (vc) {119case 'phone':120return { sidebar: false, auxiliaryBar: false, panel: false, chatBar: true, editor: false };121case 'tablet':122case 'desktop':123// Tablet and desktop share the standard multi-part workbench defaults.124// A dedicated tablet layout has not been designed yet.125return { sidebar: true, auxiliaryBar: true, panel: false, chatBar: true, editor: false };126}127}128129/**130* Returns the default part sizes for the given viewport dimensions.131* If no viewport class is supplied the current observed class is used.132*133* @param width Container width in pixels.134* @param height Container height in pixels (reserved for future use).135* @param viewportClass Optional explicit viewport class override.136*/137getPartSizes(width: number, _height: number, viewportClass?: ViewportClass): IPartSizeDefaults {138const vc = viewportClass ?? this._viewportClass.get();139switch (vc) {140case 'phone':141return {142sideBarSize: 0,143auxiliaryBarSize: 0,144panelSize: 0,145chatBarWidth: width,146};147case 'tablet':148case 'desktop':149// Tablet currently falls back to desktop sizing.150return {151sideBarSize: 300,152auxiliaryBarSize: 340,153panelSize: 300,154chatBarWidth: width - 300,155};156}157}158}159160161