From ba4d7fc9c8befb5a07012047dcf44dddc4a7c5cf Mon Sep 17 00:00:00 2001 From: Jad Seifeddine Date: Tue, 13 Oct 2026 13:58:05 +0700 Subject: [PATCH 1/4] connections: app shell with independent panes, sidebar panel, tenant sublines The sticky sidebar was sized from the viewport rather than from where it starts under the header, so at the top of the page its bottom hung off screen and the page had to be scrolled before the list could be scrolled to its end. The page is now an app shell: the header stays, and the sidebar and the entries area are two independent scroll panes that always fit the viewport. Selecting a folder scrolls the entries pane, not the page. The sidebar is a bordered panel: one pinned header band on a surface tone with the Folders label, a compact new-folder button and the filter box, then the list with hairline dividers, 46px rows and one 12px edge for every band. "host (Tenant)" names read as the host over a quieter tenant line in the entries and search tables, and a role-gate reconciliation covers API-key users. Part of #254 connections: scroll only the entries pane on selection, clip the body jumpToEntriesHeader() used scrollIntoView(), which scrolls every scrollable ancestor, and a body with overflow hidden can still be scrolled by script: selecting a folder pushed the brand row and the nav off the top of the page. Scroll the entries pane to its top instead, and make the body overflow: clip, which no script can scroll. The body's border box now spans the full height so the page padding stays inside the viewport. Part of #254 tests: app shell with two independent scroll panes and a clipped body (#254) --- static/connections.html | 114 +++++++++++++++--- tests/e2e/README.md | 1 + .../e2e/specs/connections-app-shell.spec.mjs | 59 +++++++++ 3 files changed, 158 insertions(+), 16 deletions(-) create mode 100644 tests/e2e/specs/connections-app-shell.spec.mjs diff --git a/static/connections.html b/static/connections.html index 4b0f0b24..9282cd4b 100644 --- a/static/connections.html +++ b/static/connections.html @@ -535,6 +535,66 @@ .folder-list li.filtered-out { display: none; } .folder-row:focus { outline: none; } .folder-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; } + + /* UX (#254) app shell: the header stays put, the sidebar and the + entries area are two independent scroll panes that always fit the + viewport. The earlier sticky sidebar was sized from the viewport, + not from where it starts below the header, so at the top of the + page its bottom hung off screen and the page had to be scrolled + before the list could be scrolled to its end. */ + html, body { height: 100%; } + /* overflow: clip, not hidden: hidden still lets scripts scroll the + body (scrollIntoView on anything inside), which moved the header + out of view; clip makes the body unscrollable for good. The border + box is the full height so the page padding is inside the viewport. */ + body { display: flex; flex-direction: column; overflow: clip; min-height: 0; box-sizing: border-box; } + #main-content { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; } + #main-content > :not(.layout) { flex: none; } + .layout { flex: 1 1 auto; min-height: 0; align-items: stretch; } + .sidebar { + position: static; top: auto; align-self: auto; + max-height: none; height: auto; min-height: 0; + display: flex; flex-direction: column; + overflow-y: auto; overflow-x: hidden; + padding: 0; margin: 0; + border: 1px solid var(--border); border-radius: var(--radius); + background: rgba(255,255,255,0.015); + } + .main { min-height: 0; overflow-y: auto; overflow-x: hidden; padding-right: var(--s-2); } + + /* Sidebar panel: one pinned header band (label, new-folder button, + filter) on a surface tone, then the list with hairline dividers. + Every band shares the same 12px horizontal padding so edges line up. */ + .sidebar-pin { + background: var(--surface); + border-bottom: 1px solid var(--border); + padding: var(--s-2) var(--s-3); + gap: var(--s-2); + } + .sidebar-pin .sidebar-header { + margin: 0; padding: 0; border: 0; + min-height: 30px; + justify-content: space-between; + } + .sidebar-pin .sidebar-header strong { font-size: var(--fz-xs); letter-spacing: 0.14em; } + #btn-new-folder { height: 30px; padding: 0 var(--s-2); font-size: var(--fz-xs); } + #sidebar-tools { margin: 0; } + #sidebar-tools:not(:empty) { padding-bottom: 0; } + #folder-filter { + height: 34px; font-size: var(--fz-sm); + background: var(--bg); border: 1px solid var(--border); + } + .folder-list { padding: var(--s-1) 0 var(--s-2); } + .folder-list > li { margin: 0; } + .folder-list > li + li > .folder-row { border-top: 1px solid rgba(255,255,255,0.045); } + .folder-row { + padding: var(--s-2) var(--s-3) var(--s-2) calc(var(--s-3) - 3px); + min-height: 46px; + } + .folder-list li.selected > .folder-row { border-top-color: transparent; } + .folder-row-main { gap: 2px; } + .folder-desc { font-size: 12px; } + .tree-children { margin-left: var(--s-3) !important; } @@ -545,12 +605,12 @@

rustguac