Skip to content

feat(dashboard): remove the top navbar, move its chrome into the sidebar - #1128

Merged
suleimansh merged 29 commits into
mainfrom
suleimansh/feat/remove-top-navbar
Jul 24, 2026
Merged

feat(dashboard): remove the top navbar, move its chrome into the sidebar#1128
suleimansh merged 29 commits into
mainfrom
suleimansh/feat/remove-top-navbar

Conversation

@suleimansh

Copy link
Copy Markdown
Contributor

Closes #1127. Follow-up to #772.

Removes the global top navbar and moves everything it held into the left sidebar, so the workspace and right rail get the full window height (max space usage).

What moved

  • Brand (mark + wordmark) -> top of the sidebar (still the way home).
  • Overview -> its own item, pinned above the session list and made prominent, carrying the Human Queue count. Active when no project is selected.
  • Project selector -> a separate element (no longer fused with Overview). Interim behavior is today's navigate-on-select; the filter-vs-navigate decision is parked in Remove the top navbar, move its chrome into the sidebar (max space) #1127.
  • Utility controls (Local/remote connection indicator, theme toggle, notifications, Settings) -> a sidebar footer.

Implementation notes

  • +Page drops the <header> and threads the controls' callbacks (onDashboard, onSelectProject, onSettings, working, interventionCount) into RunHistory, which now composes the sidebar chrome around the runs list.
  • Those callbacks are optional (no-op default) so RunHistory's focused unit test still mounts the rail on its own; that test stubs the three chrome pieces (theme/notifications/connection) that would otherwise drag the preferences/devices telefunc shims into jsdom.

Verification

  • pnpm typecheck clean; pnpm test 423/423.
  • pnpm build + prerender succeed.
  • Live on the dev server for a visual pass (light + dark).

Still open (parked, see #1127)

The project element's behavior: navigate into a project (today) vs filter the Overview session list. Kept navigate-on-select as the interim so nothing is blocked.

Reclaim the full window height for the workspace (max space, like the old
vike.dev). The global top navbar is gone; everything it held moves into the
left sidebar, which becomes the app's chrome:

- Brand mark + wordmark at the top of the sidebar.
- Overview is its own item, pinned above the session list and given prominence,
  carrying the Human Queue count.
- The project selector is a separate element (no longer fused with Overview).
  Its exact filter-vs-navigate behaviour is still open; today's navigate-on-
  select stays as the interim.
- The utility controls (Local/remote connection, theme, notifications, Settings)
  sit in a sidebar footer.

The controls are threaded into RunHistory as optional callbacks (no-op default),
so its focused unit test still mounts the rail without wiring the shell; that
test stubs the three chrome pieces that would otherwise drag telefunc into jsdom.
@suleimansh suleimansh added enhancement New feature or request priority: high Should be addressed soon the-framework ♻️ UX ✨ labels Jul 24, 2026
@suleimansh suleimansh self-assigned this Jul 24, 2026
…cus ring

Addressing feedback on the navbar removal:

- Order is now New, Overview, then the project selector (Overview moved up out of
  the content into the header, directly under New).
- New no longer takes an "active" fill: it is an action, not a view, so only the
  current nav item (Overview) is highlighted. Both used to look selected on the
  Overview.
- The scroll viewport's focus ring is gone. It painted a green box down the
  sidebar edge whenever the rail took focus; a scroll region is not a control,
  so it stays focusable for keyboard scrolling but without the outline.
Refine the active rule so exactly one nav item is highlighted per view: New is
active on a project's own launcher (project open, no run selected), and the
Overview item is active on the Overview (no project). The two conditions are
mutually exclusive, so they never both light up.
- Logo mark a touch bigger (h-5 -> h-6).
- New and Overview now share the same box (h-9 px-2 gap-2), so their icons and
  labels line up exactly; New's default px-4 was insetting it past Overview.
- Tighten the header gap (gap-3 -> gap-1) so New and Overview read as one nav
  group, with a little breathing room kept under the brand.
Replace the project-picker dropdown with a "Projects" nav row in the same style
as Overview (icon + label + chevron). It expands into an indented sub-list of
projects with the connecting rule the reference draws, each with the activated
dot the picker used and the selected one highlighted, plus an "Add project" row.
Selecting a project still navigates into it (the interim, pending the filter-vs-
navigate call). Starts open when a project is selected so the active one shows.

Retires ProjectPicker (and its test): nothing else used it.
- A little space below the logo and below the nav group (before Recents).
- Drop the medium weight from the nav items (New/Overview/Projects) so they read
  as plain rows; the active fill still marks the current one.
- Drop the bold from the session title in each Recents row.
Push New down with a clearer gap below the logo, and tighten the gap between the
nav rows (New/Overview/Projects) so they stack as one compact group.
Let the nav rows (New/Overview/Projects) size to their content with py-1.5
padding rather than a fixed h-9 height. New overrides the button size default
(h-9/py-2) with h-auto/py-1.5 to match.
- Projects nav no longer auto-opens on a project URL; it starts collapsed and you
  open it to switch projects.
- Push the Recents label down a little and drop its medium weight.
… on hover

- Keep the rows (and the active card) clear of the overlaid scrollbar (pr-3).
- Drop the bold from the session title: it was inheriting the button base's
  font-medium, now overridden with font-normal.
- Fade the truncated title's end (like the reference) and marquee it on row hover
  by exactly its overflow, so a long title reads without a tooltip. Self-measures
  via container-query units, so short titles never move; respects reduced-motion.
The title clipped at its own edge, a row-padding inside the card, so the cut sat
mid-card. Bleed the title to the card border (cancel the row's px-2, re-add it as
inner padding so the text still starts in the same place), and fade both borders
while marqueeing so the title dissolves at each card edge.
Right-side inner padding was insetting the content box, so the marquee revealed
only to a row-padding short of the border. Keep the inner pad on the left only
(text still aligns with the subtitle), leaving the right free to reach the border.
Remove the negative margin and left inner padding; the title reads better clipped
at its own edge.
…rows

The card carries no horizontal padding now, so the title spans edge to edge and
its clip/fade land on the border; the subtitle/badge row keeps its own px-2 inset.
…; pad the title

The button base contributes px-4, so dropping my px-2 left the card padded, not
flush. Force px-0 on the card and put px-2 on the title itself, so the text aligns
with the subtitle while overflow still clips at the card border.
…lows

A short title showed the fade gradient even though it was not truncated. Measure
overflow in RunRow and gate the mask and the marquee on an is-overflowing class,
so a title that fits shows plainly.
Widen the end gradient from 1.25rem to 3rem so a long title dissolves more.
- Drop the uppercase on the Recents label (plain "Recents").
- Make the label sticky to the top of the scroll, and add a sticky gradient strip
  under it (background -> transparent) so rows dissolve into the bg as they scroll
  up beneath the label.
…oser

Make the gradient strip absolute (hanging just below the sticky label) instead of
taking flow space, so the first row sits right under Recents; it still overlays
the rows that scroll up beneath it.
Add rounded-none so the sticky label's background covers fully, with no rounded
corners letting content peek through.
The strip dimmed the first row at rest. Drive its opacity from the rail's scroll
position (the same scroll-timeline the chat log's scroll-fade uses): invisible at
the top, fading in as the list scrolls, so it only overlays rows that pass under it.
Each toolbar reader blanked to empty on a session switch (use-async resets to the
initial on any dep change) and refilled on its own timeline, so the left cluster
vanished and popped and the GitHub/PR data arrived late, shifting the row.

Add an opt-in keepPrevious to the async hooks and use it for the two display
reads: git status (branch/dirty/PR) and the GitHub URL. The toolbar now keeps its
resolved header visible and updates it in place instead of blanking. As a bonus,
GitStatusBar no longer blanks when only its poll rate changes (prPending 10s->1s).

Handoff is left as-is on purpose: its value also drives the push / Open PR
buttons, so holding a stale copy could briefly offer the wrong action.
…chevron

The chevron used to appear only when there were changes or an expandable handoff,
so it popped in and out with the git/handoff data. Make it always available and
give it a stable "about this run" panel: the agent and the spend so far (cost,
tokens, cache, turns — folded from the #322 usage events), above the existing
changes/handoff file detail (kept). Branch/dirty/PR stay in the bar row above, so
they are not repeated. Starts collapsed.
Replace the row of icon buttons (GitHub, open folder, open in editor, Serve, Stop,
Remove worktree, Delete, Open session) with a single ⋮ overflow menu, so the bar
stops growing and shifting as a run's state changes. Serve keeps its state (Serve
-> Open/Stop, or a picker submenu in a multi-app repo) and the editor keeps its
preferred-editor submenu. The handoff's Push / Open PR stay visible in the bar,
since they move the work forward rather than just opening it somewhere.

- New SessionActionsMenu owns all of it; ConfirmDialog gains a controlled mode so
  Delete can open from a menu item (a menu item cannot also be a dialog trigger).
- Retire DeleteSessionButton and RemoveWorktreeButton (their logic moved into the
  menu); WorkspaceActions stays for the project home. Coverage moves to a new
  SessionActionsMenu test (menu contents, folder target, delete-confirm flow).
Prefix the session name in the action bar with its project as a breadcrumb —
"project / session" — so the toolbar says which project the session belongs to.
The project part is muted and gives up width first, so a long project name
truncates before the session name does. Threaded projectName from the page
(already computed there) down through RunView and RunActionBar to GitStatusBar.
…tus line

- Remove the bottom border from the session toolbar (RunActionBar).
- The run's status line no longer repeats the session name: it showed the agent's
  self-reported name only when there was one, so the "stopped" banner read
  inconsistently (with a name for some sessions, without for others). The action
  bar's breadcrumb now names the session, so the run view hides it (showName=false)
  and the line just shows the state; the relay watch and project home keep it.
`pnpm dev` alone is the Vite dev server with no Telefunc context, so sendStart
reports "starting a session is not enabled on this server" and you cannot start a
run there (only the daemon has the startRun handler). Add `pnpm dev:daemon`
(FRAMEWORK_DEV_DAEMON=1): a serve-only Vite plugin that brings up the daemon via
the framework's own idempotent ensureDaemon (reusing one already running) and
proxies /_telefunc (RPCs and the SSE Channel) to it, so the live-reload UI gets
the full backend, run-starting included. The proxy middleware registers ahead of
Telefunc's own and holds requests until the daemon is up.

Default `pnpm dev` is unchanged (no daemon spawned). The daemon is detached and
outlives the dev server; stop it with `the-framework stop`.
Tested pnpm dev:daemon end to end and fixed two issues:
- Resolve the framework bin via import.meta.resolve, not require.resolve: the
  package is ESM-only (no CJS main), so require.resolve threw and the daemon never
  started. Pass it as binPath so ensureDaemon re-invokes the framework, not vite.
- When the daemon is not up, the /_telefunc middleware now falls through to Vite's
  own handling instead of 503ing every request (which flooded telefunc client
  errors); reads keep working, only starting a run is unavailable.

Verified: the dev server proxies /_telefunc to the daemon (same response as the
daemon direct), "starting runs is enabled", no error flood.
@suleimansh
suleimansh marked this pull request as ready for review July 24, 2026 22:08
@suleimansh
suleimansh merged commit 23385a1 into main Jul 24, 2026
1 check passed
@suleimansh
suleimansh deleted the suleimansh/feat/remove-top-navbar branch July 24, 2026 22:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request priority: high Should be addressed soon the-framework ♻️ UX ✨

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove the top navbar, move its chrome into the sidebar (max space)

1 participant