feat: simplify tab layout to CSS-driven sizing and throttle terminal state broadcasts
This commit is contained in:
@@ -8,18 +8,17 @@ export type WorkspaceTabLayoutInput = {
|
||||
actionsReservedWidth: number;
|
||||
rowPaddingHorizontal: number;
|
||||
tabGap: number;
|
||||
minTabWidth: number;
|
||||
maxTabWidth: number;
|
||||
iconOnlyTabWidth: number;
|
||||
tabBaseWidthWithClose: number;
|
||||
minLabelChars: number;
|
||||
charWidth: number;
|
||||
tabIconWidth: number;
|
||||
tabHorizontalPadding: number;
|
||||
estimatedCharWidth: number;
|
||||
closeButtonWidth: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type WorkspaceTabLayoutResult = {
|
||||
mode: WorkspaceTabLayoutMode;
|
||||
tabWidths: number[];
|
||||
shouldScroll: boolean;
|
||||
showLabels: boolean;
|
||||
showCloseButtons: boolean;
|
||||
};
|
||||
@@ -42,18 +41,14 @@ function sum(values: number[]): number {
|
||||
return total;
|
||||
}
|
||||
|
||||
function computeTotalRowWidth(input: {
|
||||
tabWidths: number[];
|
||||
tabGap: number;
|
||||
rowPaddingHorizontal: number;
|
||||
}): number {
|
||||
if (input.tabWidths.length === 0) {
|
||||
function computeTotalRowWidth(input: { itemWidths: number[]; tabGap: number; rowPaddingHorizontal: number }): number {
|
||||
if (input.itemWidths.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
return (
|
||||
input.rowPaddingHorizontal * 2 +
|
||||
sum(input.tabWidths) +
|
||||
Math.max(input.tabWidths.length - 1, 0) * input.tabGap
|
||||
sum(input.itemWidths) +
|
||||
Math.max(input.itemWidths.length - 1, 0) * input.tabGap
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,8 +59,6 @@ export function computeWorkspaceTabLayout(
|
||||
if (tabCount === 0) {
|
||||
return {
|
||||
mode: "full",
|
||||
tabWidths: [],
|
||||
shouldScroll: false,
|
||||
showLabels: true,
|
||||
showCloseButtons: true,
|
||||
};
|
||||
@@ -75,68 +68,36 @@ export function computeWorkspaceTabLayout(
|
||||
0,
|
||||
input.viewportWidth - input.metrics.rowHorizontalInset * 2 - input.metrics.actionsReservedWidth
|
||||
);
|
||||
const minLabelWidth =
|
||||
input.metrics.tabBaseWidthWithClose + input.metrics.charWidth * input.metrics.minLabelChars;
|
||||
const baseTabWidth = input.metrics.tabIconWidth + input.metrics.tabHorizontalPadding * 2;
|
||||
const estimateLabelWidth = (labelLength: number) => labelLength * input.metrics.estimatedCharWidth;
|
||||
|
||||
const preferredTabWidths = input.tabLabelLengths.map((rawLength) => {
|
||||
const fullTabWidths = input.tabLabelLengths.map((rawLength) => {
|
||||
const labelLength = Math.max(rawLength, 1);
|
||||
const preferred = input.metrics.tabBaseWidthWithClose + labelLength * input.metrics.charWidth;
|
||||
return clamp(preferred, minLabelWidth, input.metrics.maxTabWidth);
|
||||
const estimatedWidth = baseTabWidth + estimateLabelWidth(labelLength) + input.metrics.closeButtonWidth;
|
||||
return clamp(estimatedWidth, input.metrics.minTabWidth, input.metrics.maxTabWidth);
|
||||
});
|
||||
|
||||
const preferredTotal = computeTotalRowWidth({
|
||||
tabWidths: preferredTabWidths,
|
||||
const fullTotal = computeTotalRowWidth({
|
||||
itemWidths: fullTabWidths,
|
||||
tabGap: input.metrics.tabGap,
|
||||
rowPaddingHorizontal: input.metrics.rowPaddingHorizontal,
|
||||
});
|
||||
|
||||
if (preferredTotal <= availableWidth) {
|
||||
return {
|
||||
mode: "full",
|
||||
tabWidths: preferredTabWidths,
|
||||
shouldScroll: false,
|
||||
showLabels: true,
|
||||
showCloseButtons: true,
|
||||
};
|
||||
if (fullTotal <= availableWidth) {
|
||||
return { mode: "full", showLabels: true, showCloseButtons: true };
|
||||
}
|
||||
|
||||
const compactMinTabWidths = preferredTabWidths.map(() => minLabelWidth);
|
||||
const compactTabWidths = input.tabLabelLengths.map((rawLength) => {
|
||||
const labelLength = Math.max(rawLength, 1);
|
||||
const estimatedWidth = baseTabWidth + estimateLabelWidth(labelLength);
|
||||
return clamp(estimatedWidth, input.metrics.minTabWidth, input.metrics.maxTabWidth);
|
||||
});
|
||||
const compactTotal = computeTotalRowWidth({
|
||||
tabWidths: compactMinTabWidths,
|
||||
itemWidths: compactTabWidths,
|
||||
tabGap: input.metrics.tabGap,
|
||||
rowPaddingHorizontal: input.metrics.rowPaddingHorizontal,
|
||||
});
|
||||
|
||||
if (compactTotal <= availableWidth) {
|
||||
const totalShrinkCapacity = sum(preferredTabWidths.map((width) => width - minLabelWidth));
|
||||
const targetShrink = preferredTotal - availableWidth;
|
||||
const shrinkRatio = totalShrinkCapacity > 0 ? targetShrink / totalShrinkCapacity : 1;
|
||||
const tabWidths = preferredTabWidths.map((preferred) => {
|
||||
const shrinkCapacity = preferred - minLabelWidth;
|
||||
return preferred - shrinkCapacity * shrinkRatio;
|
||||
});
|
||||
|
||||
return {
|
||||
mode: "compact",
|
||||
tabWidths,
|
||||
shouldScroll: false,
|
||||
showLabels: true,
|
||||
showCloseButtons: true,
|
||||
};
|
||||
return { mode: "compact", showLabels: true, showCloseButtons: false };
|
||||
}
|
||||
|
||||
const iconTabWidths = preferredTabWidths.map(() => input.metrics.iconOnlyTabWidth);
|
||||
const iconTotal = computeTotalRowWidth({
|
||||
tabWidths: iconTabWidths,
|
||||
tabGap: input.metrics.tabGap,
|
||||
rowPaddingHorizontal: input.metrics.rowPaddingHorizontal,
|
||||
});
|
||||
|
||||
return {
|
||||
mode: "icon",
|
||||
tabWidths: iconTabWidths,
|
||||
shouldScroll: iconTotal > availableWidth,
|
||||
showLabels: false,
|
||||
showCloseButtons: false,
|
||||
};
|
||||
return { mode: "icon", showLabels: false, showCloseButtons: false };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user