feat(sidebar): Notion-style collapse/peek, desktop reopen button, Ctrl+\\ toggle
FlowDeck CI / lint (push) Successful in 45s
FlowDeck CI / test (push) Successful in 4m20s
FlowDeck CI / docker (push) Successful in 40s

Stop collapsing the sidebar on navigation (was hiding the nav when opening Shared pages). Show the hamburger in the top-left on desktop when collapsed, add a left-edge hover zone that floats the sidebar in and auto-collapses on mouse leave, and bind Ctrl/Cmd+\\ to toggle it.
This commit is contained in:
2026-09-12 22:41:22 -04:00
parent 61a33a7891
commit 9836c85e24
4 changed files with 67 additions and 8 deletions
+2 -1
View File
@@ -36,7 +36,8 @@
<header class="topbar unified-header">
{% if not hide_hamburger %}
<button class="hamburger-btn"
@click="{{ hamburger_click|default('sidebarCollapsed ? (sidebarCollapsed = false) : (mobileSidebarOpen = true, sidebarCollapsed = false)') }}"
:class="{ 'hamburger-desktop-show': sidebarCollapsed }"
@click="{{ hamburger_click|default('sidebarCollapsed ? toggleSidebar() : (mobileSidebarOpen = true, sidebarCollapsed = false)') }}"
title="Toggle sidebar">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="3" y1="6" x2="21" y2="6"/>
+19 -6
View File
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FlowDeck — {% block title_prefix %}Home{% endblock %}</title>
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
<link rel="stylesheet" href="/static/css/app.css?v=5.0.0">
<link rel="stylesheet" href="/static/css/app.css?v=5.0.1">
<link rel="stylesheet" href="/static/css/design-tokens.css?v=5.2.0">
<link rel="stylesheet" href="/static/css/components.css?v=5.2.0">
<link rel="stylesheet" href="/static/css/katex.min.css?v=0.16.11">
@@ -118,9 +118,14 @@
<!-- ═══════════ MOBILE SIDEBAR OVERLAY ═══════════ -->
<div class="sidebar-overlay"
:class="{ visible: mobileSidebarOpen }"
@click="mobileSidebarOpen = false, sidebarCollapsed = true">
@click="mobileSidebarOpen = false">
</div>
<!-- ═══════════ SIDEBAR PEEK ZONE (collapsed, desktop) ═══════════ -->
<div class="sidebar-peek-zone"
x-show="sidebarCollapsed" x-cloak
@mouseenter="sidebarPeek = true"></div>
{# ─── Recursive tree macro ─── #}
{% macro render_tree_item(page, depth=0) %}
{% set indent_px = depth * 16 %}
@@ -163,8 +168,9 @@
<!-- ═══════════ SIDEBAR Notion ═══════════ -->
<aside class="sidebar"
:class="{ collapsed: sidebarCollapsed, 'mobile-open': mobileSidebarOpen }"
:class="{ collapsed: sidebarCollapsed && !sidebarPeek, 'mobile-open': mobileSidebarOpen, peeking: sidebarPeek }"
id="sidebar"
@mouseleave="sidebarPeek = false"
x-init="$nextTick(() => { if(typeof initTreeSortable==='function') initTreeSortable(); })">
<!-- Header + dropdown wrapper -->
<div style="position:relative;">
@@ -180,7 +186,7 @@
{% endif %}
</div>
<span class="workspace-chevron" :class="{ open: workspaceMenuOpen }">▾</span>
<button class="sidebar-collapse-inline" @click.stop="sidebarCollapsed = !sidebarCollapsed" title="Collapse sidebar">
<button class="sidebar-collapse-inline" @click.stop="toggleSidebar()" title="Collapse sidebar">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="11 17 6 12 11 7"/><polyline points="18 17 13 12 18 7"/></svg>
</button>
</div>
@@ -963,6 +969,7 @@
window.appState = this;
this.loadSidebarConfig();
this.loadAgents();
document.addEventListener('fd-toggle-sidebar', () => this.toggleSidebar());
},
agentList: [],
loadAgents() {
@@ -980,6 +987,7 @@
else { document.dispatchEvent(new CustomEvent('fd-agent-toggle')); }
},
sidebarCollapsed: false,
sidebarPeek: false,
mobileSidebarOpen: false,
showInvite: true,
// -- Toast notifications --
@@ -1325,7 +1333,7 @@
navigateTo(url) {
this.mobileSidebarOpen = false;
this.sidebarCollapsed = true;
this.sidebarPeek = false;
// Preserve workspace context when navigating to library/trash
if ((url === '/library' || url === '/trash') && this.workspaceKey && this.workspaceKey.includes('/')) {
const parts = this.workspaceKey.split('/');
@@ -1335,6 +1343,11 @@
else window.location.href = url;
},
toggleSidebar() {
this.sidebarPeek = false;
this.sidebarCollapsed = !this.sidebarCollapsed;
},
showContextMenu(event, pageId, pageName) {
var x = event.clientX, y = event.clientY;
// Prevent menu from overflowing right edge
@@ -1842,7 +1855,7 @@
}
});
</script>
<script src="/static/js/app.js?v=2.4.7" defer></script>
<script src="/static/js/app.js?v=2.4.8" defer></script>
<style>
.flowdeck-modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.6);z-index:2000;display:flex;align-items:center;justify-content:center;}