Sidebar splitter. The sidebar's right edge can be dragged between 240 and 480px. The arrow keys move it 24px at a time, and a double-click resets it. The handle sits over the existing border rather than taking a grid column, so nothing new is drawn until you hover or focus it. It is not shown on a phone, where the sidebar is a drawer, or while the sidebar is collapsed to icons.
Saved per device. The new sidebarWidth setting joins DEVICE_KEYS next to the list-pane sizes. It stays null until the first drag and returns to null on reset. While it is null the stylesheet's --sidebar-w decides the width, so the reporter's own CSS keeps working until they drag.
Keyboard resizing now saves, on both splitters.Splitter called onResize for an arrow key but never onEnd. The pane moved on screen, the change was never saved, and the message list's in-progress size stayed stuck (a later double-click reset did nothing). Each key press now ends the resize. Both views also keep the in-progress size in a ref, because onEnd runs in the same tick as onResize, before React state has updated.
Label fix. The message-list splitter's aria-label was the literal "Resize message list", outside translate() and in no catalogue.
No transition while dragging..app-body animates grid-template-columns for the collapse toggle. That animation is turned off during a drag so the edge doesn't trail the pointer.
Translation
Two new keys, "Resize message list" and "Resize sidebar", are added to all nine catalogues (de, es, fr, ja, nl, pt-BR, ru, uk, zh-Hans), following each file's existing words for "sidebar" and "message list". Every catalogue goes from 1412/1428 to 1414/1430 translated. The 16 strings that fall back to English are unchanged. npm run i18n:check passes.
Checked
npm run typecheck -w web, npm run build -w web, and npm test -w web (1215 passed). settingsSync.test.ts now asserts that sidebarWidth stays out of the synced file.
In a visible Chrome tab against npm run dev:mock:
Dragging took the sidebar from 256 to 391px, and the resizing class cleared on release.
Two quick arrow-key presses moved it 48px. The first version measured the sidebar during its transition and moved only 24px; it now starts from the saved setting.
Double-click reset the width to 256px with no inline override left behind.
Dragging stops at 240 and at 480. At the old 200px floor, "Calendar" and "Contacts" were cut off in the module bar; they fit from 228px up.
The handle disappears when the sidebar is collapsed and comes back at the chosen width.
Two arrow keys on the message-list splitter took it to 568px, and a double-click then reset it to 520, which confirms the keyboard change was saved.
Not checked: the phone layout. The handle there is hidden by the same useIsMobile condition that already switches the sidebar to a drawer.
Closes #345.
## What changes
- **Sidebar splitter.** The sidebar's right edge can be dragged between **240 and 480px**. The arrow keys move it 24px at a time, and a double-click resets it. The handle sits over the existing border rather than taking a grid column, so nothing new is drawn until you hover or focus it. It is not shown on a phone, where the sidebar is a drawer, or while the sidebar is collapsed to icons.
- **Saved per device.** The new `sidebarWidth` setting joins `DEVICE_KEYS` next to the list-pane sizes. It stays `null` until the first drag and returns to `null` on reset. While it is `null` the stylesheet's `--sidebar-w` decides the width, so the reporter's own CSS keeps working until they drag.
- **Keyboard resizing now saves, on both splitters.** `Splitter` called `onResize` for an arrow key but never `onEnd`. The pane moved on screen, the change was never saved, and the message list's in-progress size stayed stuck (a later double-click reset did nothing). Each key press now ends the resize. Both views also keep the in-progress size in a ref, because `onEnd` runs in the same tick as `onResize`, before React state has updated.
- **Label fix.** The message-list splitter's `aria-label` was the literal `"Resize message list"`, outside `translate()` and in no catalogue.
- **No transition while dragging.** `.app-body` animates `grid-template-columns` for the collapse toggle. That animation is turned off during a drag so the edge doesn't trail the pointer.
## Translation
Two new keys, `"Resize message list"` and `"Resize sidebar"`, are added to all nine catalogues (de, es, fr, ja, nl, pt-BR, ru, uk, zh-Hans), following each file's existing words for "sidebar" and "message list". Every catalogue goes from 1412/1428 to 1414/1430 translated. The 16 strings that fall back to English are unchanged. `npm run i18n:check` passes.
## Checked
- `npm run typecheck -w web`, `npm run build -w web`, and `npm test -w web` (1215 passed). `settingsSync.test.ts` now asserts that `sidebarWidth` stays out of the synced file.
- In a visible Chrome tab against `npm run dev:mock`:
- Dragging took the sidebar from 256 to 391px, and the `resizing` class cleared on release.
- Two quick arrow-key presses moved it 48px. The first version measured the sidebar during its transition and moved only 24px; it now starts from the saved setting.
- Double-click reset the width to 256px with no inline override left behind.
- Dragging stops at 240 and at 480. At the old 200px floor, "Calendar" and "Contacts" were cut off in the module bar; they fit from 228px up.
- The handle disappears when the sidebar is collapsed and comes back at the chosen width.
- Two arrow keys on the message-list splitter took it to 568px, and a double-click then reset it to 520, which confirms the keyboard change was saved.
Not checked: the phone layout. The handle there is hidden by the same `useIsMobile` condition that already switches the sidebar to a drawer.
**Merged** 2026-09-14 as coffey-labs/ihasmail@af092b3942d9
<sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #345.
What changes
sidebarWidthsetting joinsDEVICE_KEYSnext to the list-pane sizes. It staysnulluntil the first drag and returns tonullon reset. While it isnullthe stylesheet's--sidebar-wdecides the width, so the reporter's own CSS keeps working until they drag.SplittercalledonResizefor an arrow key but neveronEnd. The pane moved on screen, the change was never saved, and the message list's in-progress size stayed stuck (a later double-click reset did nothing). Each key press now ends the resize. Both views also keep the in-progress size in a ref, becauseonEndruns in the same tick asonResize, before React state has updated.aria-labelwas the literal"Resize message list", outsidetranslate()and in no catalogue..app-bodyanimatesgrid-template-columnsfor the collapse toggle. That animation is turned off during a drag so the edge doesn't trail the pointer.Translation
Two new keys,
"Resize message list"and"Resize sidebar", are added to all nine catalogues (de, es, fr, ja, nl, pt-BR, ru, uk, zh-Hans), following each file's existing words for "sidebar" and "message list". Every catalogue goes from 1412/1428 to 1414/1430 translated. The 16 strings that fall back to English are unchanged.npm run i18n:checkpasses.Checked
npm run typecheck -w web,npm run build -w web, andnpm test -w web(1215 passed).settingsSync.test.tsnow asserts thatsidebarWidthstays out of the synced file.npm run dev:mock:resizingclass cleared on release.Not checked: the phone layout. The handle there is hidden by the same
useIsMobilecondition that already switches the sidebar to a drawer.Merged 2026-09-14 as coffey-labs/ihasmail@af092b3942
Rebuilt from: git history, session transcript.