Skip to content

Fix: The button for toggling the left pane visibility in the launcher… #1962

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
May 20, 2025
3 changes: 3 additions & 0 deletions apps/client/src/components/app_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,9 @@ export type CommandMappings = {
type EventMappings = {
initialRenderComplete: {};
frocaReloaded: {};
setLeftPaneVisibility: {
leftPaneVisible: boolean | null;
}
protectedSessionStarted: {};
notesReloaded: {
noteIds: string[];
Expand Down
6 changes: 3 additions & 3 deletions apps/client/src/components/root_command_executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,15 @@ export default class RootCommandExecutor extends Component {
}

hideLeftPaneCommand() {
options.save(`leftPaneVisible`, "false");
appContext.triggerEvent("setLeftPaneVisibility", { leftPaneVisible: false });
}

showLeftPaneCommand() {
options.save(`leftPaneVisible`, "true");
appContext.triggerEvent("setLeftPaneVisibility", { leftPaneVisible: true });
}

toggleLeftPaneCommand() {
options.toggle("leftPaneVisible");
appContext.triggerEvent("setLeftPaneVisibility", { leftPaneVisible: null });
}

async showBackendLogCommand() {
Expand Down
30 changes: 22 additions & 8 deletions apps/client/src/services/resizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import Split from "split.js"

export const DEFAULT_GUTTER_SIZE = 5;

let leftPaneWidth: number;
let reservedPx: number;
let layoutOrientation: string;
let leftInstance: ReturnType<typeof Split> | null;
let rightPaneWidth: number;
let rightInstance: ReturnType<typeof Split> | null;

function setupLeftPaneResizer(leftPaneVisible: boolean) {
Expand All @@ -14,27 +18,34 @@ function setupLeftPaneResizer(leftPaneVisible: boolean) {

$("#left-pane").toggle(leftPaneVisible);

layoutOrientation = layoutOrientation ?? options.get("layoutOrientation");
reservedPx = reservedPx ?? (layoutOrientation === "vertical" ? ($("#launcher-pane").outerWidth() || 0) : 0);
// Window resizing causes `window.innerWidth` to change, so `reservedWidth` needs to be recalculated each time.
const reservedWidth = reservedPx / window.innerWidth * 100;
if (!leftPaneVisible) {
$("#rest-pane").css("width", "100%");

$("#rest-pane").css("width", layoutOrientation === "vertical" ? `${100 - reservedWidth}%` : "100%");
return;
}

let leftPaneWidth = options.getInt("leftPaneWidth");
leftPaneWidth = leftPaneWidth ?? (options.getInt("leftPaneWidth") ?? 0);
if (!leftPaneWidth || leftPaneWidth < 5) {
leftPaneWidth = 5;
}

const restPaneWidth = 100 - leftPaneWidth - reservedWidth;
if (leftPaneVisible) {
// Delayed initialization ensures that all DOM elements are fully rendered and part of the layout,
// preventing Split.js from retrieving incorrect dimensions due to #left-pane not being rendered yet,
// which would cause the minSize setting to have no effect.
requestAnimationFrame(() => {
leftInstance = Split(["#left-pane", "#rest-pane"], {
sizes: [leftPaneWidth, 100 - leftPaneWidth],
sizes: [leftPaneWidth, restPaneWidth],
gutterSize: DEFAULT_GUTTER_SIZE,
minSize: [150, 300],
onDragEnd: (sizes) => options.save("leftPaneWidth", Math.round(sizes[0]))
onDragEnd: (sizes) => {
leftPaneWidth = Math.round(sizes[0]);
options.save("leftPaneWidth", Math.round(sizes[0]));
}
});
});
}
Expand All @@ -54,7 +65,7 @@ function setupRightPaneResizer() {
return;
}

let rightPaneWidth = options.getInt("rightPaneWidth");
rightPaneWidth = rightPaneWidth ?? (options.getInt("rightPaneWidth") ?? 0);
if (!rightPaneWidth || rightPaneWidth < 5) {
rightPaneWidth = 5;
}
Expand All @@ -63,8 +74,11 @@ function setupRightPaneResizer() {
rightInstance = Split(["#center-pane", "#right-pane"], {
sizes: [100 - rightPaneWidth, rightPaneWidth],
gutterSize: DEFAULT_GUTTER_SIZE,
minSize: [ 300, 180 ],
onDragEnd: (sizes) => options.save("rightPaneWidth", Math.round(sizes[1]))
minSize: [300, 180],
onDragEnd: (sizes) => {
rightPaneWidth = Math.round(sizes[1]);
options.save("rightPaneWidth", Math.round(sizes[1]));
}
});
}
}
Expand Down
1 change: 1 addition & 0 deletions apps/client/src/stylesheets/theme-next/shell.css
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ body.layout-horizontal > .horizontal {
--launcher-pane-button-gap: var(--launcher-pane-vert-button-gap);

width: var(--launcher-pane-size) !important;
min-width: var(--launcher-pane-size) !important;
padding-bottom: var(--launcher-pane-button-gap);
}

Expand Down
20 changes: 8 additions & 12 deletions apps/client/src/widgets/buttons/left_pane_toggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ export default class LeftPaneToggleWidget extends CommandButtonWidget {
return "bx-sidebar";
}

return options.is("leftPaneVisible") ? "bx-chevrons-left" : "bx-chevrons-right";
return this.currentLeftPaneVisible ? "bx-chevrons-left" : "bx-chevrons-right";
};

this.settings.title = () => (options.is("leftPaneVisible") ? t("left_pane_toggle.hide_panel") : t("left_pane_toggle.show_panel"));
this.settings.title = () => (this.currentLeftPaneVisible ? t("left_pane_toggle.hide_panel") : t("left_pane_toggle.show_panel"));

this.settings.command = () => (this.currentLeftPaneVisible ? "hideLeftPane" : "showLeftPane");

Expand All @@ -32,16 +32,12 @@ export default class LeftPaneToggleWidget extends CommandButtonWidget {
}

refreshIcon() {
if (document.hasFocus() || this.currentLeftPaneVisible === true) {
super.refreshIcon();
splitService.setupLeftPaneResizer(this.currentLeftPaneVisible);
}
super.refreshIcon();
splitService.setupLeftPaneResizer(this.currentLeftPaneVisible);
}

entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
if (loadResults.isOptionReloaded("leftPaneVisible") && document.hasFocus()) {
this.currentLeftPaneVisible = options.is("leftPaneVisible");
this.refreshIcon();
}

setLeftPaneVisibilityEvent({ leftPaneVisible }: EventData<"setLeftPaneVisibility">) {
this.currentLeftPaneVisible = leftPaneVisible ?? !this.currentLeftPaneVisible;
this.refreshIcon();
}
}
25 changes: 15 additions & 10 deletions apps/client/src/widgets/containers/left_pane_container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,33 @@ import appContext, { type EventData } from "../../components/app_context.js";
import type Component from "../../components/component.js";

export default class LeftPaneContainer extends FlexContainer<Component> {
private currentLeftPaneVisible: boolean;

constructor() {
super("column");

this.currentLeftPaneVisible = options.is("leftPaneVisible");

this.id("left-pane");
this.css("height", "100%");
this.collapsible();
}

isEnabled() {
return super.isEnabled() && options.is("leftPaneVisible");
return super.isEnabled() && this.currentLeftPaneVisible;
}

entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
if (loadResults.isOptionReloaded("leftPaneVisible") && document.hasFocus()) {
const visible = this.isEnabled();
this.toggleInt(visible);
setLeftPaneVisibilityEvent({ leftPaneVisible }: EventData<"setLeftPaneVisibility">) {
this.currentLeftPaneVisible = leftPaneVisible ?? !this.currentLeftPaneVisible;
const visible = this.isEnabled();
this.toggleInt(visible);

if (visible) {
this.triggerEvent("focusTree", {});
} else {
this.triggerEvent("focusOnDetail", { ntxId: appContext.tabManager.getActiveContext()?.ntxId });
}
if (visible) {
this.triggerEvent("focusTree", {});
} else {
this.triggerEvent("focusOnDetail", { ntxId: appContext.tabManager.getActiveContext()?.ntxId });
}

options.save("leftPaneVisible", this.currentLeftPaneVisible.toString());
}
}
Loading