Skip to content

Commit eecb225

Browse files
authored
Merge pull request #120 from HexmosTech/ui-rewamp
UI rewamp
2 parents 573de49 + ae02e22 commit eecb225

130 files changed

Lines changed: 4433 additions & 1339 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
---
2+
name: Collapsible Sidebar Implementation
3+
overview: Implement a collapsible sidebar with icon-only collapsed state, search icon that opens the search popup directly, Ctrl+K opening popup when collapsed, tooltips for icons, and hide theme switcher in collapsed mode.
4+
todos: []
5+
isProject: false
6+
---
7+
8+
# Collapsible Sidebar Implementation
9+
10+
## Current Architecture
11+
12+
- **Sidebar**: [components/common/sidebar.templ](components/common/sidebar.templ) - static 14rem width, contains SearchBar, nav links (icon + text), SidebarThemeSwitcher, SidebarProfile
13+
- **Search flow**: Typing in `#search` input updates `window.searchState` and hash `#search?q=...`. Base layout's `toggleSearchView()` shows search overlay when hash is `#search?q=` (even empty)
14+
- **Ctrl+K**: Sidebar handler (lines 569-582) focuses `#sidebar #search` on non-homepage; index.templ focuses homepage search
15+
- **SidebarProfile**: React component mounted in `#sidebar-profile-container`, shows user/sign-in with avatar + text
16+
17+
## Implementation Plan
18+
19+
### 1. State and Toggle Button
20+
21+
- Add `sidebar-collapsed` class to `#sidebar` when collapsed; persist state in `localStorage` (e.g. `fdt-sidebar-collapsed`)
22+
- Add collapse/expand toggle button (chevron or panel icon) - positioned after logo in expanded mode, visible in both modes
23+
- Collapsed width: ~4rem (64px) for icon-only layout; expanded: 14rem (unchanged)
24+
- Animate width transition via CSS
25+
26+
### 2. Collapsed Layout Structure
27+
28+
**Logo section**: Show only logo image (32px), hide title/subtitle. Wrap in same homepage link. Toggle button next to it.
29+
30+
**Search section**:
31+
32+
- Expanded: Keep current SearchBar (input visible)
33+
- Collapsed: Replace with search icon button. On click: set `window.location.hash = '#search?q='`, `window.searchState.setQuery('')` to open search overlay directly (no focus on hidden input)
34+
35+
**Nav section**: In collapsed mode, hide all `<span>` text nodes; show only icons. Each nav item becomes icon-only with `title` attribute for native tooltip (or use a small tooltip lib if needed - native `title` is simplest). Preserve existing `href` and click behavior.
36+
37+
**Theme section**: Hide `#sidebar-theme-switcher-section` when collapsed (CSS or JS based on `sidebar-collapsed`)
38+
39+
**Profile section**: SidebarProfile needs collapsed variant:
40+
41+
- Option A: Pass `collapsed` prop via data attribute from templ, SidebarProfile reads it and renders icon-only (avatar or sign-in icon)
42+
- Option B: In collapsed mode, hide profile container and show minimal icon - requires coordination between templ (hiding container) and possibly a separate icon
43+
- Simplest: In collapsed mode, hide `#sidebar-profile-container` entirely, or render a compact icon that links to /pro or triggers sign-in. Recommend: Show avatar/sign-in icon only with tooltip "Sign In" or user name.
44+
45+
### 3. Ctrl+K Behavior
46+
47+
Modify the Ctrl+K handler in [sidebar.templ](components/common/sidebar.templ) (lines 569-582):
48+
49+
```javascript
50+
// When collapsed: open search popup directly (set hash + searchState)
51+
// When expanded: focus sidebar #search (current behavior)
52+
```
53+
54+
Check for `#sidebar.sidebar-collapsed` - if true, call `openSearchPopup()` instead of focusing input. The search_bar.templ also has a global Ctrl+K - we need a single source of truth. Recommendation: Centralize Ctrl+K in base_layout or have sidebar handler take precedence when sidebar is visible and collapsed.
55+
56+
**Order of handlers**: index.templ (homepage) has its own handler. For non-homepage: sidebar.templ handler runs. Update sidebar handler to:
57+
58+
- If homepage: do nothing (index handles it)
59+
- If non-homepage + sidebar collapsed: `e.preventDefault()`, set hash `#search?q=`, `searchState.setQuery('')`
60+
- If non-homepage + sidebar expanded: focus `#sidebar #search`
61+
62+
The search_bar.templ attaches to `document` - so both fire. We need the sidebar handler to run first and `preventDefault` + return when collapsed, so search_bar doesn't try to focus (and fail since input may be hidden). Use `stopImmediatePropagation` or check collapsed state in a unified handler.
63+
64+
### 4. Tooltips
65+
66+
Add `title` attribute to each nav link with the text content:
67+
68+
- "VS Code extension", "My Bookmarks", "Pro", "Tools", "Installerpedia", "Emojis", "SVG Icons", "PNG Icons", "MCP", "TLDR", "Man Pages", "Cheatsheets"
69+
70+
For collapsed search icon button: `title="Search (Ctrl+K)"`
71+
72+
### 5. Files to Modify
73+
74+
75+
| File | Changes |
76+
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
77+
| [sidebar.templ](components/common/sidebar.templ) | Add collapse state, toggle button, collapsed layout (hide text/spans, replace search with icon), CSS for collapsed width, update hideSidebarSearch for collapsed, Ctrl+K logic |
78+
| [SidebarProfile.tsx](frontend/components/common/SidebarProfile.tsx) | Accept collapsed prop (via container data attribute or React context) and render icon-only variant with tooltip |
79+
| [search_bar.templ](components/common/search_bar.templ) | May need to support "collapsed sidebar" mode - or sidebar uses a different structure when collapsed and doesn't include SearchBar, instead has custom icon button |
80+
81+
82+
### 6. SearchBar in Collapsed Mode
83+
84+
The SearchBar is one component. For collapsed sidebar we have two options:
85+
86+
**A)** Keep SearchBar in DOM but hide `#desktop-search-container` and show a new `#sidebar-search-icon-btn` when collapsed. The templ would conditionally render both; CSS/JS shows one based on collapsed state.
87+
88+
**B)** Create a `SidebarSearch` component or block in sidebar.templ that:
89+
90+
- Expanded: renders `@SearchBar()`
91+
- Collapsed: renders icon button that opens popup
92+
93+
Since SearchBar is shared and has complex logic, option A is cleaner: add a sibling icon button in `#sidebar-search-section`, hide it when expanded and hide SearchBar when collapsed. Wire the icon click to open popup.
94+
95+
### 7. CSS Structure
96+
97+
```css
98+
#sidebar { transition: width 0.2s; }
99+
#sidebar.sidebar-collapsed { width: 4rem !important; }
100+
#sidebar.sidebar-collapsed #sidebar-logo-text-container,
101+
#sidebar.sidebar-collapsed .nav-link-text span,
102+
#sidebar.sidebar-collapsed #sidebar-theme-switcher-section,
103+
#sidebar.sidebar-collapsed #desktop-search-container { display: none !important; }
104+
#sidebar.sidebar-collapsed #sidebar-search-icon-btn { display: flex !important; }
105+
```
106+
107+
### 8. SidebarProfile Collapsed
108+
109+
SidebarProfile is rendered by `window.renderTool('sidebarProfile', 'sidebar-profile-container')`. The parent div can have `data-collapsed="true"` when collapsed. SidebarProfile reads `document.getElementById('sidebar-profile-container')?.closest('#sidebar')?.classList.contains('sidebar-collapsed')` on mount and when receiving a custom event `sidebar-collapsed-changed`. Or simpler: pass nothing and have SidebarProfile check `#sidebar.sidebar-collapsed` in a `useEffect` + `ResizeObserver`/`MutationObserver` - overkill.
110+
111+
Simpler: Dispatch `sidebar-collapsed-changed` when toggle is clicked; SidebarProfile listens and re-renders. Or use a data attribute on the container that the parent updates - React would need to re-mount or receive new props. Easiest: Put a class on `#sidebar-profile-container` when collapsed, and use CSS to hide text and show icon-only. But SidebarProfile is React - its DOM is managed by React. So we need React to know about collapsed state.
112+
113+
**Approach**: Add `window.sidebarCollapsed = true/false` or dispatch `sidebar-collapsed-changed` with `detail: { collapsed: true }`. SidebarProfile subscribes and sets local state to re-render icon-only layout.
114+
115+
### 9. Edge Cases
116+
117+
- **Mobile**: Current responsive layout hides sidebar by default, shows as overlay. Collapse/expand is desktop-only - on mobile keep current overlay behavior. Add `@media (min-width: 1024px)` guard so collapse UI only appears on desktop.
118+
- **Homepage**: Sidebar search is already hidden on homepage (`hideSidebarSearch`). In collapsed mode, the search icon should still show and open popup when clicked.
119+
- **Hash changes**: When user opens search via collapsed icon, hash becomes `#search?q=`, which triggers `toggleSearchView` - good.
120+
121+
## Diagram
122+
123+
```mermaid
124+
flowchart TD
125+
subgraph Expanded [Expanded Sidebar]
126+
E1[Logo + Text]
127+
E2[Search Input]
128+
E3[Nav: Icon + Text]
129+
E4[Theme Switcher]
130+
E5[Profile Full]
131+
end
132+
133+
subgraph Collapsed [Collapsed Sidebar]
134+
C1[Logo only]
135+
C2[Search Icon]
136+
C3[Nav: Icon only + tooltip]
137+
C4[Hidden]
138+
C5[Profile Icon]
139+
end
140+
141+
Toggle[Toggle Button] --> Expanded
142+
Toggle --> Collapsed
143+
```
144+
145+
146+
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
---
2+
name: Sidebar Tooltip and Toggle UX
3+
overview: Implement custom tooltips with immediate show and 1s hide delay, increase collapsed sidebar width, replace toggle with panel SVG icons, and change collapsed-mode expand UX to show expand icon on logo hover (ChatGPT-style).
4+
todos: []
5+
isProject: false
6+
---
7+
8+
# Sidebar Tooltip and Toggle UX Improvements
9+
10+
## 1. Custom Tooltips (Immediate Show, 1s Hide Delay)
11+
12+
**Limitation**: Native `title` cannot control timing. We need custom tooltips.
13+
14+
**Approach**: Replace `title` with custom tooltip elements. Use CSS:
15+
16+
```css
17+
.sidebar-tooltip-wrap { position: relative; }
18+
.sidebar-tooltip-wrap .sidebar-tooltip {
19+
position: absolute; left: 100%; top: 50%; transform: translateY(-50%);
20+
margin-left: 0.5rem; padding: 0.25rem 0.5rem; background: #1e293b; color: white;
21+
border-radius: 0.25rem; font-size: 0.75rem; white-space: nowrap; z-index: 100;
22+
visibility: hidden; opacity: 0;
23+
transition: opacity 0.15s 1s, visibility 0s 1s; /* Hide: 1s delay then fade */
24+
}
25+
.sidebar-tooltip-wrap:hover .sidebar-tooltip {
26+
visibility: visible; opacity: 1;
27+
transition: opacity 0.15s 0s, visibility 0s 0s; /* Show: immediate */
28+
}
29+
```
30+
31+
- **Show**: 0ms delay (immediate on hover)
32+
- **Hide**: 1s delay before fading out (transition-delay on the non-hover state)
33+
34+
**Files**: [sidebar.templ](components/common/sidebar.templ). Wrap each nav link and the search icon button in a tooltip container, add `<span class="sidebar-tooltip">Label</span>`, remove `title`. Add dark mode tooltip styling. SidebarProfile uses `title` for its collapsed avatar - consider custom tooltip there too or leave as-is if scope is sidebar-only.
35+
36+
## 2. Collapsed Sidebar Width
37+
38+
Current: `4rem` (64px). Icons (20px) + padding overflow.
39+
40+
**Change**: Increase to `5rem` (80px) in [sidebar.templ](components/common/sidebar.templ) CSS:
41+
42+
```css
43+
#sidebar.sidebar-collapsed {
44+
width: 5rem !important;
45+
}
46+
```
47+
48+
## 3. Panel Icons for Toggle (Expanded vs Collapsed)
49+
50+
Replace the chevron SVG with:
51+
52+
- **Expanded mode**: `panel-left-close.svg` (click to collapse)
53+
- **Collapsed mode**: `panel-left-open.svg` (click to expand)
54+
55+
Files exist at:
56+
57+
- `/freedevtools/svg_icons/panel/panel-left-close.svg`
58+
- `/freedevtools/svg_icons/panel/panel-left-open.svg`
59+
60+
Use `<img>` tags with `src` switching via JS, or two `<img>` elements toggled by CSS based on `.sidebar-collapsed`. The toggle button shows one icon when expanded, the other when collapsed.
61+
62+
## 4. Collapsed Mode: Expand on Logo Hover (ChatGPT-style)
63+
64+
**Current**: Collapsed shows logo + toggle button side-by-side; toggle is always visible and causes width issues.
65+
66+
**New**:
67+
68+
- **Collapsed**: Hide the toggle button. Logo section shows only the logo image, centered.
69+
- **On hover of logo** (when collapsed): Show the expand icon (`panel-left-open.svg`) - e.g. replace logo with icon or overlay it.
70+
- **On click of logo** (when collapsed): Expand sidebar (prevent navigation to homepage).
71+
72+
**Implementation**:
73+
74+
- Structure: Logo section when collapsed contains logo `<img>` and expand icon `<img>`. Both in a wrapper. Default: logo visible, icon `display: none`. Hover: logo hidden, icon visible. Single click target.
75+
- Make the logo section a `<div>` or `<button>` when collapsed instead of `<a>` - or keep `<a>` but add JS: when collapsed, `click` handler calls `preventDefault()` and `setCollapsed(false)`, does not navigate.
76+
- CSS: `#sidebar.sidebar-collapsed #sidebar-collapse-toggle { display: none !important; }` - hide toggle in collapsed.
77+
- Add `#sidebar-collapsed-expand-hint` - an img that appears on logo hover when collapsed. Use `#sidebar-logo-section:hover #sidebar-collapsed-expand-hint { display: block }` and `#sidebar-collapsed-expand-hint { display: none }` by default when collapsed.
78+
- Logo link: In collapsed mode, the logo section needs to be clickable to expand. Option A: Wrap logo + hint in a div, use `role="button"` and click handler. Option B: Keep the `<a>`, add `data-collapsed-click` behavior - JS intercepts click when collapsed.
79+
80+
**Recommended structure**:
81+
82+
```html
83+
<div id="sidebar-logo-section">
84+
<a id="sidebar-logo-link" href="/freedevtools/">
85+
<img id="sidebar-logo-img" ... />
86+
<img id="sidebar-collapsed-expand-hint" src=".../panel-left-open.svg"
87+
style="display:none" alt="Expand" /> <!-- shown on hover when collapsed -->
88+
<div id="sidebar-logo-text-container">...</div>
89+
</a>
90+
<button id="sidebar-collapse-toggle"> <!-- visible only when expanded -->
91+
<img src=".../panel-left-close.svg" alt="Collapse" />
92+
</button>
93+
</div>
94+
```
95+
96+
- When collapsed: hide `#sidebar-logo-text-container`, hide `#sidebar-collapse-toggle`, show `#sidebar-logo-img` by default. On `#sidebar-logo-section:hover` when collapsed: hide logo img, show expand hint img.
97+
- Click on logo section when collapsed: JS handler prevents default, calls setCollapsed(false). Need to ensure the `<a>` doesn't navigate - either use a div with click handler when collapsed, or add `event.preventDefault()` in click handler when collapsed.
98+
99+
Simpler: When collapsed, the logo link's href could be `#` or we use a click handler that checks `isCollapsed()` and preventsDefault + expand. The link wraps both logo and expand hint.
100+
101+
## 5. Files to Modify
102+
103+
104+
| File | Changes |
105+
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
106+
| [sidebar.templ](components/common/sidebar.templ) | Custom tooltip CSS + wrap nav links and search btn; width 5rem; replace chevron with panel img tags; collapsed: hide toggle, add expand-hint img, logo-section hover shows it, click handler to expand |
107+
108+
109+
## 6. JS Updates
110+
111+
In the collapse init script:
112+
113+
- Replace chevron with two `<img>` elements (or single img with src swap). Toggle button shows `panel-left-close.svg` when expanded.
114+
- When collapsed: add click listener to `#sidebar-logo-section` or `#sidebar-logo-link` that checks `isCollapsed()` and if true, `e.preventDefault()`, `setCollapsed(false)`. Remove or adjust so it only fires when collapsed.
115+
- Collapsed hover: pure CSS can handle `.sidebar-collapsed #sidebar-logo-section:hover #sidebar-logo-img { display: none }` and `.sidebar-collapsed #sidebar-logo-section:hover #sidebar-collapsed-expand-hint { display: block }` - but the hint needs to be a sibling or descendant of the hover target.
116+

0 commit comments

Comments
 (0)