Skip to content

feat: add configurable sidebar width in settings#1975

Open
soumik183 wants to merge 1 commit intoAcode-Foundation:mainfrom
soumik183:feature/configurable-sidebar-width
Open

feat: add configurable sidebar width in settings#1975
soumik183 wants to merge 1 commit intoAcode-Foundation:mainfrom
soumik183:feature/configurable-sidebar-width

Conversation

@soumik183
Copy link

This PR introduces a configurable sidebar width, allowing users to customize the size of the sidebar via the app settings. It includes dynamic safety clamping to prevent the sidebar from taking up the entire screen on mobile devices.

@github-actions github-actions bot added enhancement New feature or request translations Anything related to Translations Whether a Issue or PR labels Mar 23, 2026
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 23, 2026

Greptile Summary

This PR adds a user-configurable sidebar width setting, persisting the value in appSettings (replacing the old localStorage.sideBarWidth key) and wiring up a live update:sidebarWidth listener so the sidebar reacts to settings changes without a restart. The CSS default is also updated from a viewport-relative rule to a fixed 280px baseline.

Key changes:

  • New sidebarWidth default (280 px) added to src/lib/settings.js and a settings UI entry added to src/settings/appSettings.js.
  • setWidth now sets both width and maxWidth on the element, and skips the root margin/width adjustments in phone mode.
  • show() always calls setWidth(width) upfront regardless of mode (previously this only happened in tab mode).
  • The resize-bar drag handler now persists to appSettings instead of localStorage.

Issues found:

  • The appSettings.on("update:sidebarWidth", ...) listener registered in create() is never removed, causing a memory leak if the sidebar is ever rebuilt.
  • Directly mutating appSettings.value.sidebarWidth before calling appSettings.update(false) causes #getChangedKeys to detect the change and fire the update:sidebarWidth listener, leading to a redundant setWidth call; appSettings.update({sidebarWidth: width}, false) should be used instead.
  • The settings validation permits values up to 1500 px, but the runtime cap is innerWidth - 40 — a user can enter a value that passes validation yet silently renders as a different width.
  • The info string in the settings item is a hard-coded English literal rather than a localized strings[...] lookup.

Confidence Score: 3/5

  • Merging carries a real memory leak risk and a user-visible UX bug (persisted width doesn't match displayed width) that should be fixed before shipping.
  • Two P1 issues exist: an unregistered settings event listener that leaks every time create() is called, and a validation ceiling (1500 px) that is completely disconnected from the runtime MAX_WIDTH() clamp, producing a silent mismatch between the stored and rendered sidebar width. These are not catastrophic but they are real bugs that affect correctness and UX. The remaining P2s (redundant setWidth call, stale width after resize, non-localized info string) are lower risk but worth addressing.
  • src/components/sidebar/index.js (memory leak + stale width), src/settings/appSettings.js (validation ceiling + non-localized string)

Important Files Changed

Filename Overview
src/components/sidebar/index.js Core change: migrates sidebar width from localStorage to appSettings, adds a live update:sidebarWidth listener, and updates setWidth and show. The listener is never removed (memory leak), direct mutation of appSettings.value triggers a redundant setWidth call, and width can go stale after window resize in tab mode.
src/settings/appSettings.js Adds the sidebarWidth settings item with a numeric prompt and validation. The validation ceiling (1500 px) is disconnected from the runtime MAX_WIDTH() cap (innerWidth - 40), and the info string is a hard-coded English literal rather than a localized string.
src/lib/settings.js Adds sidebarWidth: 280 as the default setting value — straightforward and consistent with other defaults.
src/components/sidebar/style.scss Replaces the CSS-driven width: 70vw; max-width: 350px with a fixed width: 280px matching the new default setting. Clean change with no issues.
src/lang/en-us.json Adds the "sidebar width" localization key. Consistent with surrounding entries.

Sequence Diagram

sequenceDiagram
    participant User
    participant SettingsUI as Settings UI (appSettings.js)
    participant AppSettings as appSettings (lib/settings.js)
    participant Sidebar as Sidebar (index.js)

    User->>SettingsUI: Enter new sidebar width value
    SettingsUI->>SettingsUI: validate (200 ≤ value ≤ 1500)
    SettingsUI->>AppSettings: update({ sidebarWidth: value })
    AppSettings->>AppSettings: persist to settings.json
    AppSettings-->>Sidebar: emit "update:sidebarWidth"
    Sidebar->>Sidebar: width = Math.min(sidebarWidth, MAX_WIDTH())
    Sidebar->>Sidebar: setWidth(width)

    User->>Sidebar: Drag resize bar
    Sidebar->>Sidebar: resize(deltaX) → setWidth(newWidth)
    Sidebar->>AppSettings: value.sidebarWidth = width (direct mutation)
    Sidebar->>AppSettings: update(false)
    AppSettings->>AppSettings: #getChangedKeys detects mutation
    AppSettings-->>Sidebar: emit "update:sidebarWidth" (redundant)
    Sidebar->>Sidebar: setWidth(width) called again (no-op)
Loading

Reviews (1): Last reviewed commit: "feat: add configurable sidebar width in ..." | Re-trigger Greptile

Comment on lines +97 to +100
appSettings.on("update:sidebarWidth", () => {
width = Math.min(appSettings.value.sidebarWidth || MIN_WIDTH, MAX_WIDTH());
setWidth(width);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Memory leak – listener never removed

appSettings.on("update:sidebarWidth", ...) is registered inside create() but there is no corresponding appSettings.off(...) call when the sidebar element is destroyed (e.g. after hideMaster removes it from the DOM, or if create is ever called more than once in the singleton pattern).

Each call to create() accumulates another live listener on the global appSettings object. Over time those stale callbacks will call setWidth on detached elements and keep closed-over DOM nodes alive. Store the callback reference and deregister it in a cleanup path (the existing hide/hideMaster flow would be a good place):

const onSidebarWidthUpdate = () => {
    width = Math.min(appSettings.value.sidebarWidth || MIN_WIDTH, MAX_WIDTH());
    setWidth(width);
};
appSettings.on("update:sidebarWidth", onSidebarWidthUpdate);
// … later, when the sidebar is destroyed:
// appSettings.off("update:sidebarWidth", onSidebarWidthUpdate);

Comment on lines +359 to +360
appSettings.value.sidebarWidth = width;
appSettings.update(false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Direct mutation triggers a redundant setWidth call

Setting appSettings.value.sidebarWidth = width directly mutates the settings object, and then appSettings.update(false) detects it as a change through #getChangedKeys(). This causes the update:sidebarWidth listener (registered at line 97) to fire, which calls setWidth(width) a second time with the exact value that was just applied.

Use the idiomatic update API instead to avoid the extra round-trip and make the intent clear:

Suggested change
appSettings.value.sidebarWidth = width;
appSettings.update(false);
appSettings.update({ sidebarWidth: width }, false);

Comment on lines +96 to +101
promptOptions: {
test(value) {
value = Number.parseInt(value);
return value >= 200 && value <= 1500;
},
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Validation ceiling (1500px) is disconnected from the runtime cap

The promptOptions.test allows values up to 1500 px, but at runtime the sidebar is clamped to innerWidth - 40. On a typical mobile device (e.g. 400 px wide) the ceiling is ~360 px — far below 1500. A user who enters, say, 800 px will pass validation, the value 800 will be persisted to settings, but the sidebar will silently render at 360 px. The mismatch between the stored value and the rendered width will be confusing.

Consider deriving the max from screen dimensions at validation time, or at least document the runtime clamping in the info string so users understand why the displayed width may differ from what they entered:

Suggested change
promptOptions: {
test(value) {
value = Number.parseInt(value);
return value >= 200 && value <= 1500;
},
},
test(value) {
value = Number.parseInt(value);
const runtimeMax = window.innerWidth - 40;
return value >= 200 && value <= runtimeMax;
},

Comment on lines 505 to 508
$el.getWidth = function () {
const width = innerWidth * 0.7;
return mode === "phone" ? (width >= 350 ? 350 : width) : MIN_WIDTH;
const configuredWidth = appSettings.value.sidebarWidth || MIN_WIDTH;
return mode === "phone" ? Math.min(configuredWidth, MAX_WIDTH()) : width;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 getWidth() inconsistency in tab mode

For mode === "phone", getWidth() correctly re-evaluates Math.min(configuredWidth, MAX_WIDTH()). However for tab mode it returns the closed-over width local variable. After a window resize event onWindowResize updates the local innerWidth and hides the sidebar, but it does not re-clamp width. On the next show() call setWidth(width) may use a stale value larger than the new MAX_WIDTH().

For consistency (and to avoid the stale-value issue), tab mode should also be re-clamped:

Suggested change
$el.getWidth = function () {
const width = innerWidth * 0.7;
return mode === "phone" ? (width >= 350 ? 350 : width) : MIN_WIDTH;
const configuredWidth = appSettings.value.sidebarWidth || MIN_WIDTH;
return mode === "phone" ? Math.min(configuredWidth, MAX_WIDTH()) : width;
};
$el.getWidth = function () {
const configuredWidth = appSettings.value.sidebarWidth || MIN_WIDTH;
return Math.min(configuredWidth, MAX_WIDTH());
};

return value >= 200 && value <= 1500;
},
},
info: "Adjust the width of the sidebar (min 200px).",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Hard-coded English info string

Every other settings item in this file uses a localized strings[...] lookup for its info field. This one uses a bare string literal "Adjust the width of the sidebar (min 200px).", which will not be translated when the user changes language. Add a corresponding key to en-us.json and look it up via strings:

Suggested change
info: "Adjust the width of the sidebar (min 200px).",
info: strings["info-sidebarWidth"] || "Adjust the width of the sidebar (min 200px).",

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request translations Anything related to Translations Whether a Issue or PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant