refactor(ui): three-column layout with left rail + main-view migration - #899
Conversation
Inline style="display:none" on #mainSettings had higher specificity than the .showing-settings CSS rule meant to reveal it, so clicking the rail cog toggled the class but the panes never appeared. The CSS already handles visibility via main.main.showing-settings #mainSettings.
Redo the settings canvas to match the hermes-desktop mock: - Larger page-style section header (18px title, subtitle, bottom border) - Each form row wraps its label + input in a card surface using existing --surface / --border tokens - Comfortable 768px max-width, 28-32px padding - Active sidebar menu item picks up accent colour - Primary save button reskinned with accent background; neutral outline for sign-out/disable-auth - Drop the legacy border-top separator on the password field now that every row is already a card - Responsive: tighten canvas on narrow viewports
- Upsized icons to 16px with 1.5 stroke to match refined rail style - Removed border/hover-bg chrome on active item — plain accent-bg + accent-text tint, no border ring - Introduced .settings-menu-head with uppercase tracking + bottom border (replaces ad-hoc .sidebar-section wrapper) - Hover now uses --surface instead of --hover-bg for consistency with the reference's --surface-2 hover
…ence - Introduced --border-subtle token (color-mix of --border at 60%) declared in both :root and .dark so cards can have softer internal dividers without a hardcoded colour - Canvas padding 24px (down from 28/32) to match reference px-3 sm:px-6 - Labels are now small, muted, 12px — reference look (was bold --text) - Section meta bumped to 13px to match reference sizing - Card surface uses --sidebar (brand panel tone) instead of --surface for more breathing room vs. the canvas --bg - Inputs/selects/password forced to uniform 8px-radius 12px-pad look via !important, overriding legacy inline styles from the modal era - Action buttons hover into --accent-bg to feel tappable - Picker grids (theme/skin/font-size) normalised to tokens so Ares, Mono, Slate, Poseidon, Sisyphus and Charizard skins all flip - Neutralised the legacy amber/red inline tints on Disable Auth / Sign Out — now plain secondary, hover picks up --accent-bg-strong - Version badge slimmed to a monospace muted pill (was accent-tinted) - Mobile: 16/12 padding, 14px card padding
Moves scheduled-job details out of inline sidebar bodies into a dedicated #mainTasks view (Phase 2 of main-view migration). Clicking a job in the sidebar now opens it in the main canvas with schedule, status, prompt, skills, and recent run history cards. Header actions (run, pause/resume, edit, delete) live next to the title. The sidebar's inline edit form is repurposed to handle edits from the detail view. Also introduces #mainWorkspaces and #mainProfiles containers plus shared .detail-card styling and tweaks empty-state copy; the workspaces and profiles JS wiring lands in follow-up commits.
Clicking a workspace in the sidebar now opens it in #mainWorkspaces (path, active/default status) instead of exposing inline per-row action buttons. Activation and removal live as header actions in the main view. The sidebar's add-space input auto-selects the newly added space in the detail pane.
Clicking an agent profile in the sidebar now opens it in #mainProfiles with a full settings card (status, gateway state, model, base URL, API key, default space). Activate and delete move to the main-view header and are hidden for the currently active / default profile respectively. Newly created profiles auto-select into the detail pane.
Brings the 6 pre-existing CI failures on refactor/left-rail-layout in line with the new main-view layout: - Replace native confirm() in deleteCurrentSkill with showConfirmDialog - Point cache-invalidation tests at openCronCreate/saveSkillForm (renamed from toggleCronForm/submitSkillSave; old names remain as shims) - Point cron-history test at _loadCronDetailRuns/cron_last_output (inline run list replaced the standalone "All runs" button) - Point sprint6 served-HTML check at #mainTasks and #settingsMenu (cron create form is rendered dynamically; settings tabs are now left-rail nav) - Point sprint34 control-center test at .settings-menu-item.active (the settings-tabs shell was replaced by the sidebar menu) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Hey @aronprins — excellent progress, and the new commit ( ✅ What's confirmed working
Bugs still open🔴 Critical — i18n keys missing for all new form labelsStill present — confirmed by both Opus and Sonnet in the browser. Every create/edit form (skills, tasks, workspaces, profiles) shows raw key strings instead of labels:
Root cause: Note: The select/option values are also affected — 🟡 Medium — Skill list sidebar truncation (items have
|
| Surface | Result | Notes |
|---|---|---|
| Rail navigation (8 tabs) | ✅ | Active state syncs, all panels switch |
| App titlebar | ✅ | Updates on every switch |
| Chat panel | ✅ | Session list, composer, new chat all work |
| Workspace panel (right) | ✅ | File tree, file preview, action icons work |
| Workspace chip files icon | ✅ | Opens file panel correctly |
| Workspace chip label | ✅ | Opens workspace picker correctly |
| Settings (5 sections) | ✅ | Full page, not modal |
| Skill list sidebar | ❌ | Items collapse to zero width |
| Skill detail view | Works but YAML frontmatter leaks | |
| Skill create/edit form | ❌ | All labels show raw i18n key strings |
| Task list sidebar | ❌ | Same item collapse issue |
| Task detail view | ✅ | Header, run history render |
| Task create/edit form | ❌ | All labels show raw i18n key strings |
| Workspace panel (left sidebar) | ✅ | Empty state, add button |
| Profile panel | ✅ | Empty state, list renders |
| Memory panel | ✅ | Content displays |
| Memory edit | Edit trigger has poor discoverability | |
| Toasts | ✅ | Top-right, all variants |
| Mobile layout | ✅ | Hamburger shows, rail hidden |
Fixes needed before merge
- Add ~25 i18n keys to
i18n.jsfor all new form labels (most critical — affects every create/edit form) - Fix sidebar item width collapse —
.skill-item,.cron-itemneedwidth: 100%; min-width: 0 - Strip YAML frontmatter in
_renderSkillDetail()before passing torenderMd() - Fix workspace chip border-radius on child buttons to match the container's pill shape
Tests: all 1974 pass. Security: clean. JS syntax: clean.
Really solid architecture — the main-view migration pattern works well and the three-column layout feels right. The blockers are all fixable.
Integrates 52 upstream commits (through v0.50.185) into the three-column layout branch. Conflict resolutions (3 files, 6 regions): static/index.html: - Kept HEAD's #mainMemory main-view structure; dropped master's leftover settings-tabs fragment (already replaced by #mainSettings + settings-menu in this branch). static/panels.js: - Kept HEAD's restyled provider-card rendering (body/field/row structure) from c9fe7fe; dropped master's pre-restyle flat card layout. Nothing lost — restyled version covers input, save, remove + adds show/hide. static/style.css (4 regions): - Mobile composer chips: merged master's reasoning-chip / reasoning-chevron rules into HEAD's workspace-group split layout. - Settings section/action-btn: kept HEAD's page-style #mainSettings rules; dropped master's modal-era styles (deleted by main-view migration). - Provider cards: kept HEAD's hermes-desktop match; dropped master's pre-restyle 9 lines. Removed duplicate section comment. - Main-view tokens + /btw bubble + /background badge: kept HEAD's complete main-view token block; kept master's /btw and /background CSS (new features); kept master's mobile message-controls override; dropped master's dead mobile selectors (.settings-shell, .settings-tabs, .provider-save-btn) that target deleted elements. Auto-merged without conflict: static/ui.js (reasoning chip logic), static/messages.js (/btw bubble + streaming-markdown), static/commands.js (/btw, /background, /reasoning), static/i18n.js, plus 20+ new test files and api/background.py, api/providers.py, static/manifest.json, static/sw.js, static/vendor/smd.min.js. Reasoning-chip restyle to match revamp visual system lands in a follow-up commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three inconsistencies with the rest of the composer-footer design: - Removed reference to undefined @Keyframes dropdown-in animation. Other composer dropdowns (.model-dropdown, .ws-dropdown) don't animate; reasoning-dropdown should match. - .reasoning-option.selected now uses background:var(--accent-bg) to match .model-opt.active / .ws-opt.active — selection is indicated by background fill across all composer dropdowns. - .reasoning-option:hover now uses rgba(255,255,255,.07) to match .model-opt:hover; both are consistent elevation hints on dark surfaces. Added overflow:hidden to the dropdown container so item corners are clipped by the 10px border-radius (matches .model-dropdown). Reasoning chip wrap/chip/icon/label/chevron rules were already aligned with the profile-chip + model-chip pattern; no changes needed there. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…button)
Master's /btw undo button on the last assistant message called
li('undo-2', 13) but icons.js had no matching entry, producing
"li(): unknown icon undo-2" in the console each time the button
rendered.
Added the Lucide undo path (curved arrow — same glyph Lucide exports
as "undo-2") and registered it under the cleaner name 'undo'.
Updated the single call site in ui.js to match. No other callers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
_setResolvedTheme() swapped link.href between the dark (prism-tomorrow) and light (prism) CSS but left the integrity attribute pointing at the dark theme's SHA-384. When resolving to light mode the browser refused the stylesheet for failing Subresource Integrity validation, logging "Failed to find a valid digest in the integrity attribute". Pair the href swap with a matching integrity swap so SRI still holds after a theme change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… frontmatter, side-menu reuse
- Add missing i18n keys for skill/cron/workspace/profile create/edit forms across en/ru/es/de/zh locales (root cause: t('key') || fallback doesn't fire when key is missing, since t() returns the key itself)
- Fix sidebar item collapse: add width:100%; min-width:0; box-sizing:border-box to .skill-item and .cron-item
- Strip YAML frontmatter in skill detail and surface it in a collapsible <details> block above the rendered markdown body
- Give workspace chip children split border-radius so the pill shape stays cohesive when either child has a background
- Memory edit button now shows "Edit" label alongside the pencil icon (new .panel-head-btn.has-label modifier)
- Rename .settings-menu -> .side-menu so the side-nav pattern is reusable (shared by memoryPanel and settingsMenu); updated test assertion
|
All review items addressed in 9a87612. Summary: Merge blockers (all fixed)
Medium / low
Tests: 2070 passed, 46 skipped. |
Memory edit button stays consistent with skill/task/workspace edit buttons — pencil icon + tooltip, no text label. Drops the unused .panel-head-btn.has-label modifier.
|
Thanks @aronprins — that's a thorough resolution summary. All the merge blockers addressed:
2070 tests passing with 46 skipped is a solid green signal. This PR looks ready for maintainer merge review. 🚀 |
|
Hey @aronprins — ran a full second-pass review on the latest commits (9a87612 + 0bd1881): live browser testing across all eight rail panels and all create/edit forms, three viewport sizes via Puppeteer, JS syntax + security scan, and the full test suite. Then ran Claude Opus 4.7 (max effort + extended thinking) for an independent verification pass. Really strong iteration — every blocker from the first pass is resolved. ✅ Confirmed workingLayout + structure
Sidebar item widths (previously 0px — now fixed)
i18n — forms (previously all raw keys — now fixed)
YAML frontmatter (previously leaked into renderMd — now fixed)
Workspace chip
Toasts
Mobile (390px, verified with real Puppeteer viewport)
New settings navigation test: 9/9 passed ✅ (TestSettingsNavigationGuard + TestMasterDetailRefreshClearsRemovedSelections) Viewport summary
🟡 Fix needed before merge
Every user who opens "New job" sees the placeholder text
placeholder="${esc(t('cron_name_placeholder') || 'Optional')}"
Fix: add one line per locale next to the existing cron_name_placeholder: 'Optional',(Or an equivalent localized string for ru/es/de/zh — English fallback would also work if you prefer to keep it simple.) 🟢 Low — worth fixing, not blockingGerman locale missing
Mobile sidebar has no scrim 641–767px middle zone SummaryTwo independent review passes (Sonnet + Opus max effort) agree: one fix needed — |
|
On it. |
|
All three reported issues confirmed fixed in
2116 tests pass. QA harness clean (1 pre-existing |
Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
|
Merged as v0.50.186. Thank you @aronprins — this is a significant architectural improvement. The three-column layout, main-view migration, and mobile handling all work cleanly. Really great work. |
nesquena#899) refactor(ui): three-column layout with left rail + main-view migration (nesquena#899) Unifies the shell into a three-column layout (rail + sidebar + main) matching the hermes-desktop reference, and migrates every per-item detail/edit surface into a shared main-view canvas with consistent headers, empty states, and action buttons. Changes: - New desktop-only left rail (48px) with 8 nav tabs (chat/tasks/skills/memory/workspaces/profiles/todos/settings) - Persistent app titlebar (replaces per-chat topbar), active conversation title shown - All panel detail/create/edit views migrated to #mainSkills, #mainTasks, #mainSettings, #mainWorkspaces, #mainProfiles, #mainMemory - Settings moved out of modal into main-view page; ESC closes it - YAML frontmatter rendered in collapsible <details> block in skill detail - Toasts repositioned from bottom-center to top-right with theme-aware success/error/warning/info variants - Composer workspace chip split into two-button group: files-icon toggles file panel, label opens workspace picker - .settings-menu → .side-menu / .side-menu-item (generalised, shared by memory and settings panels) - i18n: ~25 new keys across en/ru/es/de/zh/zh-Hant for all new form labels, placeholders, and empty states - Mobile: hamburger in titlebar, slide-in sidebar; box-shadow removed from sidebar - New regression test: tests/test_settings_navigation_and_detail_refresh.py (9 tests) Co-authored-by: Aron Prins <pwf.aron@gmail.com>
Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
nesquena#899) refactor(ui): three-column layout with left rail + main-view migration (nesquena#899) Unifies the shell into a three-column layout (rail + sidebar + main) matching the hermes-desktop reference, and migrates every per-item detail/edit surface into a shared main-view canvas with consistent headers, empty states, and action buttons. Changes: - New desktop-only left rail (48px) with 8 nav tabs (chat/tasks/skills/memory/workspaces/profiles/todos/settings) - Persistent app titlebar (replaces per-chat topbar), active conversation title shown - All panel detail/create/edit views migrated to #mainSkills, #mainTasks, #mainSettings, #mainWorkspaces, #mainProfiles, #mainMemory - Settings moved out of modal into main-view page; ESC closes it - YAML frontmatter rendered in collapsible <details> block in skill detail - Toasts repositioned from bottom-center to top-right with theme-aware success/error/warning/info variants - Composer workspace chip split into two-button group: files-icon toggles file panel, label opens workspace picker - .settings-menu → .side-menu / .side-menu-item (generalised, shared by memory and settings panels) - i18n: ~25 new keys across en/ru/es/de/zh/zh-Hant for all new form labels, placeholders, and empty states - Mobile: hamburger in titlebar, slide-in sidebar; box-shadow removed from sidebar - New regression test: tests/test_settings_navigation_and_detail_refresh.py (9 tests) Co-authored-by: Aron Prins <pwf.aron@gmail.com>
Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
Thinking Path
.main-viewcanvas with consistent headers, empty states, and action buttons.Before / After
1. Chat layout — three-column shell with rail + app titlebar
Before (master): chat topbar owned the conversation title and the Files button; no rail, no app titlebar.
After: left rail for primary navigation, persistent app titlebar shows the active conversation, composer workspace chip splits into files-icon + label halves.
2. Settings — out of the modal, into the main view
Before (master): "Hermes Control Center" modal overlaid the chat.
After: settings live in
#mainSettingswith the sidebar menu, reached via the rail cog; saving stays on the page.3. Skill detail — dedicated main view, not a right-panel preview
Before (master): skill content rendered in the right-side WORKSPACE preview panel on top of the chat view.
After:
#mainSkillsowns skill detail with a titlebar, header action buttons (edit / delete), and consistent empty state.What Changed
Layout shell
sidebar-nav.Main-view migration
.main-viewtokens (header, body, empty state) and a renamed.panel-headclass used across every sidebar panel.#mainSkills/#mainTasks/#mainWorkspaces/#mainProfiles/#mainMemorycontainers with empty states and header action buttons. Inline sidebar forms / right-panel previews for these are gone.#mainSettingsand restyled to the desktop reference (page-style headers, card form rows, tokenised theme/skin/font pickers, neutralised legacy inline tints on Disable Auth / Sign Out).Chat view
Misc
--success/--error/--warning/--accent);showToast()auto-detects variant so existing call sites route without changes..panel-head/.main-view-headershare a 41px min-height so underlines align regardless of which 24px action buttons happen to be visible.tab_settings+ empty-state / back / edit / delete keys and deletion confirmations added. English only in this PR — other locales fall through to English strings; follow-up PR will addes/de/zh/ru/zh-Hanttranslations.tests/test_settings_navigation_and_detail_refresh.py.Diffstat
40 commits; 8 files changed, +1813 / -685. Touched files:
static/index.html,static/style.css,static/panels.js(bulk),static/boot.js,static/sessions.js,static/ui.js,static/i18n.js, plus the new test.Why It Matters
.main-viewtokens and.panel-headgive future per-entity views a single pattern to extend, instead of choosing between inline-sidebar, modal, or right-panel.Verification
Automated
pytest tests/test_settings_navigation_and_detail_refresh.py— covers settings-panel navigation and stale detail refresh.pytest tests/ -v --timeout=60locally and report back in a comment before requesting merge.Manual checks performed
Security
A focused security review of the diff (all frontend) was performed. All newly introduced
innerHTMLwrites route user/server-controlled values through the existingesc()helper; dynamic click handlers usedata-*attributes +addEventListenerrather than inline handlers for user content. No new network endpoints, auth, crypto, deserialization, or server surface is added. No high-confidence vulnerabilities identified.Risks / Follow-ups
Model Used
claude-opus-4-7[1m]