mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-25 19:20:16 +00:00
7d1aa2e261f3530a1dca6468c83fce4e67ee41f2
14 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7d1aa2e261 |
v0.50.209: check-for-updates, workspace toggle, HTML preview, provider categories, queue flyout docs (#1042)
* feat: add manual 'Check for Updates' button in System settings (#785) Add a 'Check now' button next to the version badge in the System settings section, allowing users to manually trigger an update check at any time without waiting for the automatic periodic check. Changes: - index.html: add button with spinner and status text inline with version badge - panels.js: add checkUpdatesNow() calling /api/updates/check?force=1 with immediate feedback (checking... / up to date / X updates available) - style.css: style the button block and spinner - i18n.js: add 5 new keys (settings_check_now, settings_checking, settings_up_to_date, settings_updates_available, settings_updates_disabled) in all 6 locales (en, ru, es, de, zh, zh-Hant) * fix: sanitize error message in checkUpdatesNow to avoid exposing paths Review feedback: strip filesystem paths from error messages and cap length to prevent internal details leaking into the UI. * fix: fully sanitize error in update check — never expose raw e.message in UI Previous partial fix ( |
||
|
|
54e83fb8b6 |
feat: support subpath mount via reverse proxy — v0.50.67 (PR #588 by @vcavichini)
Squash-merges feature from PR #588 by @vcavichini. Dynamic <base href> injection + api() helper slash-stripping enables deploying hermes-webui behind a reverse proxy at any subpath without configuration. Also fixes pre-existing bug: api/upload was using location.origin instead of location.href (closes #596). Co-authored-by: vcavichini <vcavichini@users.noreply.github.com> |
||
|
|
d0e08fee88 |
feat: KaTeX math rendering for LaTeX in chat + workspace previews (#352)
* feat: KaTeX math rendering for $..$ and $$..$$ in chat and previews (fixes #347) - Stash math delimiters before markdown pipeline, restore as .katex-block/.katex-inline elements - KaTeX JS lazy-loaded from CDN on first math block (mirrors mermaid pattern) - KaTeX CSS loaded eagerly in <head> to prevent layout shift - SRI hashes on both CDN tags - throwOnError:false — bad LaTeX degrades to code span - Supports $$, $, \\(...\\), \\[...\\] delimiters - 18 new tests, 831/831 passing * fix: remove invalid \' escape sequences in math stash lines Lines 311, 314, 316, 317 had \' (backslash-quote) instead of plain ' in the arrow function bodies. This is a JS syntax error — node --check fails with 'Invalid or unexpected token'. Likely caused by a serialization artifact during code generation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: swap stash order (fence before math) to protect code spans; add renderKatexBlocks to workspace preview - static/ui.js: fence_stash now runs BEFORE math_stash so dollar signs inside backtick code spans are not extracted as math. Previously `$x$` would render as KaTeX inside a <code> tag instead of showing the literal string $x$. - static/workspace.js: add requestAnimationFrame(renderKatexBlocks) after markdown preview renders so math works in workspace file previews, not only in chat messages. * feat: KaTeX math rendering + stash order fix + workspace wiring (#352) - tests/test_issue347.py: 11 new tests (29 total) covering fence-before-math ordering, workspace.js renderKatexBlocks call, stash token distinctness, false-positive prevention, safe-tags boundary check - CHANGELOG.md: v0.50.15 entry; 870 tests total (up from 841) * fix: use literal null byte (\x00M) in math stash token — matches restore regex The original PR's second commit (fix: remove invalid \' escapes) accidentally doubled the backslash in the math stash tokens: '\\x00M' is a 5-char string (backslash + x + 0 + 0 + M) but the restore regex /\x00M/ expects a null byte. Result: $...$ in messages produced visible \x00M0\x00 tokens instead of KaTeX spans. Changed all 4 math stash return statements to use '\x00M' (single backslash = null byte, same convention as fence_stash's '\x00F'). Also updates test_stash_tokens_distinct to check for the correct pattern. * fix: add null-byte token test; update CHANGELOG to v0.50.15 with fixes - tests/test_issue347.py: add test_math_stash_token_uses_single_backslash_null_byte to catch the \\x00M double-backslash regression; 30 tests total (up from 29) - CHANGELOG.md: v0.50.15 entry documents all fixes including the token bug and workspace preview wiring; 871 tests total --------- Co-authored-by: Nathan Esquenazi <nesquena@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
28a0f0bef9 |
fix+feat: session title guard + breadcrumb nav + wider panel + responsive msgs (closes #300, #292)
PR #301 changes: - api/streaming.py: guard title_from() with s.title == 'Untitled' check - api/routes.py: same guard in sync/non-streaming path PR #302 changes (cleaned — restores accidentally-removed features): - static/boot.js: PANEL_MAX 500 -> 1200 - static/boot.js: clearPreview() calls renderBreadcrumb() to restore dir view - static/style.css: responsive .messages-inner breakpoints (1400px/1800px) - static/workspace.js: renderFileBreadcrumb() function with clickable segments - static/workspace.js: openFile() calls renderFileBreadcrumb(path) 12 new tests in tests/test_sprint35.py Note: PR #302 branch contained several accidental regressions (removed app-dialog system, onboarding CSS, _checkProviderMismatch, closeMobileFiles, etc.) that were not part of its stated scope. This clean branch applies only the three intended features on top of current master. Co-authored-by: Nathan Esquenazi <nesquena@gmail.com> |
||
|
|
b86ace6ce3 |
v0.47.0: dialogs, session menu, /skills, mobile fixes, mobile QA suite
* fix: custom provider with slash model name no longer rerouted to OpenRouter (#255) When base_url is configured in config.yaml, resolve_model_provider() now trusts the configured provider/base_url entirely and skips the slash-based OpenRouter heuristic. Fixes google/gemma-4-26b-a4b with provider:custom being silently routed to OpenRouter, resulting in 401 errors. Fixes #230 * test: mobile layout regression suite — 14 tests for every QA run (#254) Adds tests/test_mobile_layout.py with 14 static regression tests that run on every QA pass to catch mobile layout breakage before it reaches prod. Covers: breakpoints at 900px/640px, right panel slide-over CSS, mobile overlay, bottom nav, files button, profile dropdown z-index, chip overflow, workspace close, 100dvh, 44px touch targets, 16px font-size on textarea. * feat: /skills slash command lists and filters available Hermes skills (#257) Adds /skills [query] command to commands.js. Fetches from /api/skills, groups by category (alphabetically sorted), displays as a formatted assistant message. Optional query filters by name, description, or category. i18n keys added for en, de, zh, zh-Hant. 1 regression test added. Fixes #248 * feat: shared app dialogs replace native confirm()/prompt() calls (#251) Adds showConfirmDialog() and showPromptDialog() helpers to ui.js, backed by a themed #appDialogOverlay. Replaces all 11 native browser confirm/prompt call sites across panels.js, sessions.js, ui.js, workspace.js. Supports: danger mode, keyboard focus trap (Tab/Escape/Enter), focus restore, ARIA roles, mobile-responsive stacked buttons at 640px. i18n for en/de/zh/zh-Hant. 5 new tests in test_sprint33.py verify markup, CSS, helpers, and absence of native dialog calls. Extracted from PR #242. * fix: Android Chrome mobile — workspace panel close + profile dropdown (#256) Fix #247: toggleMobileFiles() now shows/hides the mobile overlay when toggling the right workspace panel. New closeMobileFiles() helper closes the panel with correct overlay state tracking. Overlay onclick calls both closeMobileSidebar() and closeMobileFiles(). Mobile-only close button (x) added to workspace panel header. Fix #246: profile dropdown uses position:fixed;top:56px;right:8px at max-width:900px, escaping the overflow-x:auto stacking context that was clipping it on Android Chrome. Fix applied during review: closeMobileSidebar() now checks if the right panel is still open before hiding the overlay, preventing the overlay from disappearing when only the sidebar is closed. Fixes #247 Fixes #246 * feat: session ⋯ action dropdown replaces per-row buttons (#252) Replaces the 5 per-row hover action buttons (pin/move/archive/duplicate/trash) with a single ⋯ trigger that opens a positioned dropdown menu. Menu has full keyboard (Escape), click-outside, scroll, and resize-reposition handling. Position:fixed prevents sidebar clipping. 5 actions: Pin/Unpin, Move to project, Archive/Unarchive, Duplicate, Delete (danger style). Each with icon and descriptive subtitle. Updated test_sprint16.py: test_sessions_js_uses_action_menu_not_per_row_buttons asserts the new trigger and menu functions exist, old per-row classes are gone. Extracted from PR #242. * docs: v0.47.0 release notes, bump version, update test counts (645) --------- Co-authored-by: Nathan Esquenazi <nesquena@gmail.com> |
||
|
|
b979b4c443 |
feat: pluggable i18n with English/Chinese language switcher in Settings
Introduces a locale bundle system that makes UI language switchable at runtime and trivially extensible to any future language. Architecture: - static/i18n.js: LOCALES object with 'en' and 'zh' bundles, t(key) helper with English fallback, setLocale()/loadLocale() for persistence via localStorage. Adding a new language = adding one object. - api/config.py: 'language' setting (default 'en'), BCP-47 validation - api/routes.py: _LOGIN_LOCALE dict for server-rendered login page; template placeholders substituted at request time from saved setting - static/index.html: loads i18n.js first (before other scripts); adds Language dropdown to Settings panel, auto-populated from LOCALES Wiring: - boot.js: applies server-persisted locale at startup (after /api/settings fetch); speech recognition lang follows _locale._speech - panels.js: populates Language dropdown from LOCALES on settings open; saves + applies locale on Save Settings - All JS files: hardcoded user-facing strings replaced with t() calls Coverage: - test_sprint20.py: relaxed recognition.lang assertion to accept dynamic locale-driven assignment (behavior unchanged for English default) - 499/499 tests pass Closes #177 (incorporates Chinese translations as a proper locale bundle rather than hardcoded strings, so English default is fully preserved) |
||
|
|
d8e6079a2c |
feat: workspace git detection with branch/status badge
When the workspace root is a git repo, a badge in the panel header shows the current branch name, dirty file count, and ahead/behind status. Updates on every root directory load. Backend: - git_info_for_workspace() in api/workspace.py runs lightweight git commands (rev-parse, status --porcelain, rev-list) with 3s timeout - New GET /api/git-info endpoint returns branch, dirty count, modified, untracked, ahead, behind Frontend: - _refreshGitBadge() in workspace.js fetches git info on root load - Git badge element in panel header shows branch + status - Badge turns gold when workspace has uncommitted changes Inspired by PR #75 (@MartinNielsenDev). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
b1d687ba22 |
feat: persist workspace tree expanded state across refreshes
Store expanded directory paths in localStorage keyed by workspace path
(key: 'hermes-webui-expanded:{workspacePath}'). On root load (loadDir('.')),
restore the saved set for the current workspace and pre-fetch dir contents
for any restored expanded directories so the tree renders fully on first
paint without requiring a second click to expand.
Saves on every expand/collapse toggle. Switching workspaces automatically
picks up that workspace's own saved state. Per-workspace (not per-session)
so the same tree state is shared across sessions using the same workspace,
which is the natural expectation.
|
||
|
|
c778c1eb0c |
fix: profile switch fails with 'does not exist' when server starts on non-default profile
Root cause: _DEFAULT_HERMES_HOME was evaluated at module import time from
os.getenv('HERMES_HOME'). HERMES_HOME is a MUTABLE env var -- init_profile_state()
at server startup calls _set_hermes_home() which writes to os.environ['HERMES_HOME'].
If the sticky active_profile file pointed to e.g. 'webui', HERMES_HOME was set to
~/.hermes/profiles/webui BEFORE api/profiles.py imported. So _DEFAULT_HERMES_HOME
resolved to ~/.hermes/profiles/webui. Then switch_profile('webui') computed:
home = ~/.hermes/profiles/webui / 'profiles' / 'webui'
= ~/.hermes/profiles/webui/profiles/webui -- doesn't exist -> 404 ValueError
Fix: replace the one-liner assignment with _resolve_base_hermes_home() which:
1. Checks HERMES_BASE_HOME env var (explicit override)
2. Checks HERMES_HOME -- but if it looks like a profiles/ subdir (parent.name ==
'profiles'), walks up two levels to the actual base
3. Falls back to Path.home() / '.hermes'
This means the server can start with HERMES_HOME pointing to any profile and
_DEFAULT_HERMES_HOME will still correctly point to ~/.hermes.
Also fix: api() helper in workspace.js was throwing new Error(await res.text())
which surfaced raw JSON to the UI: 'Switch failed: {"error":"Profile X does not exist."}'
Now parses the JSON and extracts j.error so the toast shows clean human-readable text.
Regression tests added in test_sprint23.py:
- test_profile_switch_base_home_not_subdir: static analysis verifying the resolver
- test_api_helper_returns_clean_error_message: verifies api() parses JSON errors
- test_profile_switch_resolve_base_home_logic: verifies the profiles/ subdir detection
Tests: 426 passed, 0 failed.
|
||
|
|
d0aef93372 |
fix: apply review fixes, update version to v0.20, add Sprint 18 changelog
- Fix stale tree cache: clear _dirCache and _expandedDirs on root nav - Fix clearPreview: prompt before discarding unsaved preview edits - Update UI version label from v0.17.1 to v0.20 - Add Sprint 18 entry to CHANGELOG.md - Update SPRINTS.md current state to v0.20 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
67324cc3bc |
feat: Sprint 18 — file preview auto-close, thinking display, workspace tree
- File preview auto-close: clearPreview() extracted as named function and called from loadDir(). Navigating directories (breadcrumbs, up button, folder clicks) now automatically closes the right panel file preview instead of leaving stale content visible. - Thinking/reasoning display: assistant messages with structured content arrays containing type=thinking or type=reasoning blocks now render as collapsible gold-themed cards above the response text. Collapsed by default, click header to expand. Works with Claude extended thinking and o3 reasoning tokens when preserved in the message array. - Workspace tree view (Issue #22): directories expand/collapse in-place with toggle arrows. Single-click toggles, double-click navigates (breadcrumb view). Subdirectory contents fetched lazily and cached. Indentation shows nesting depth. Empty directories show "(empty)". S._expandedDirs tracks open state, S._dirCache caches fetched entries. Tests: 295 passed, 23 pre-existing failures, 0 regressions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
0f2bd537f1 |
feat: Sprint 17 -- workspace breadcrumbs, slash commands, send key setting
Track A: Workspace breadcrumb navigation - Breadcrumb path bar with clickable segments when inside subdirectories - Up button in panel header for parent directory navigation - S.currentDir state tracking; file ops stay in current directory - New file/folder creation respects current subdirectory Track B: Slash commands foundation - New commands.js module (7th JS module) with command registry and parser - Built-in commands: /help, /clear, /model, /workspace, /new - Autocomplete dropdown on / input with arrow/tab/enter/escape navigation - Unrecognized commands pass through to agent normally Track C: Send key setting (closes #26) - send_key added to settings defaults in api/config.py - Settings panel dropdown: Enter (default) vs Ctrl/Cmd+Enter - Keydown handler rewritten for autocomplete + send key preference - Setting loaded on boot, persisted to settings.json 5 new tests, 242 total (219 passing, 22 pre-existing failures, 0 regressions). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
96547f68a3 |
fix(frontend): use URL origin for fetch/EventSource to support reverse proxy auth
When Hermes WebUI runs behind a reverse proxy with HTTP basic auth (e.g. Caddy basic_auth), browsers embed credentials in the page URL. The Fetch API and EventSource reject requests constructed from URLs that include credentials (per Fetch spec, all modern browsers). Fix: construct all fetch() and EventSource URLs via new URL(path, location.origin) which strips credentials from the base URL. Add credentials:"include" to ensure auth headers are forwarded on each request. |
||
|
|
a4e2174c29 | Hermes WebUI v0.1.0 — initial public release |