Commit Graph

695 Commits

Author SHA1 Message Date
Michael Lam ee9ae29596 fix: persist activity disclosure state 2026-05-06 06:30:32 +00:00
Michael Lam a7b6cd2cda fix: simplify compact activity summaries 2026-05-06 06:27:13 +00:00
starship-s 74eb55d986 fix(profile): preserve context when starting chats 2026-05-06 06:27:00 +00:00
Michael Lam 5272215e7c docs: clarify Anthropic auth choices in onboarding 2026-05-06 06:26:43 +00:00
Michael Lam e509faec44 feat: link Claude Code OAuth in onboarding 2026-05-06 06:26:43 +00:00
Sanjays2402 9bb4fad0e8 fix(streaming): unpin scroll on small upward motion during streaming (#1731)
The streaming scroll listener applied hysteresis symmetrically: an
upward scroll that landed inside the 250px near-bottom dead zone still
reported the user as near the bottom, so _nearBottomCount kept
incrementing and _scrollPinned stayed true. The next streaming token
snapped the user back to the bottom. The user effectively had to escape
the 250px zone in one fling to read earlier output.

The 250px dead zone itself is required by #1360 / #677 (macOS small
window + trackpad momentum re-pin protection) so the fix is direction
detection, not threshold relaxation: track _lastScrollTop and unpin
immediately on an explicit upward movement (>2px decrease), while
downward / stationary movement keeps the original hysteresis re-pin
path so the macOS momentum protection is preserved.

Programmatic scrolls are still masked by the existing _programmaticScroll
guard, so scrollToBottom() never updates _lastScrollTop and never
spuriously unpins.

Adds tests/test_issue1731_upward_scroll_unpins.py covering: direction
tracker exists, upward branch sets _scrollPinned=false and resets the
counter without hysteresis, downward branch preserves the >=2
hysteresis re-pin requirement, the 250px threshold remains, and the
_programmaticScroll bail still runs before the rAF schedule.

Closes #1731.

Co-Authored-By: Potato (OpenClaw assistant) <noreply@openclaw.ai>
2026-05-06 06:26:28 +00:00
Michael Lam ecdbc8d4df fix: prevent sticky sidebar hover drag state 2026-05-05 19:17:27 -07:00
Nathan Esquenazi b6567addb1 Stage 303: PR #1719 2026-05-05 21:58:21 +00:00
Nathan Esquenazi cbdf770d36 Stage 303: PR #1722 2026-05-05 21:58:21 +00:00
ai-ag2026 b66e720673 fix: suppress stale preserved task lists
Hide preserved compression task lists when the latest todo tool state
shows no pending or in-progress items. This prevents completed tasks from
reappearing after reloads or context compaction.

Tests: uv run --with pytest --with pyyaml python -m pytest -q tests/test_auto_compression_card.py
Tests: node --check static/ui.js
2026-05-05 23:00:18 +02:00
Michael Lam 2c5acb9725 feat: show active elapsed timer in compact activity 2026-05-05 13:42:47 -07:00
Michael Lam dd2bc38473 fix: preserve activity count across chat focus changes 2026-05-05 13:42:45 -07:00
test b59164b0a8 Stage 302: PR #1688 2026-05-05 17:31:01 +00:00
Michael Lam fe9e4645ac fix: move system health panel into insights 2026-05-05 17:30:56 +00:00
Michael Lam fdeac578da feat: add VPS resource health panel 2026-05-05 17:30:56 +00:00
Nathan Esquenazi 967f7876e9 Stage 302: PR #1709 2026-05-05 17:29:47 +00:00
Nathan Esquenazi 77052fd4ec Stage 302: PR #1711 2026-05-05 17:29:47 +00:00
Basit Mustafa 9a0a6214cf fix: guard localStorage.setItem('hermes-webui-model') against QuotaExceededError
On some setups the localStorage quota is exhausted; the bare setItem
call throws an unhandled DOMException that breaks model selection and
prevents the chat UI from loading.

Wrap both call-sites (boot.js model-select onChange, onboarding.js
_saveOnboardingDefaults) in try/catch so the error is logged to the
console as a warning instead of surfacing as a fatal exception.

Fixes: 'Failed to execute setItem on Storage: Setting the value of
hermes-webui-model exceeded the quota.'
2026-05-05 17:29:47 +00:00
nesquena-hermes d3c8a7c6a5 fix(workspace): hide 'Double-click to rename' tooltip on folders (#1710)
The file-tree row tooltip says 'Double-click to rename' on every entry,
but folders don't actually rename on double-click — they navigate via
loadDir(). The tooltip is therefore misleading on directory rows.

Reported by @Deor in the WebUI Discord testers thread (May 5 2026):
'Ah that works yeah. May want to change the popup text as it also says
double click at the moment.'

Fix: gate the tooltip on item.type !== 'dir' so it only attaches to file
rows, where double-click does what the hint advertises. Folder rename
still reachable via the right-click context menu (unchanged).

Companion to #1698/#1702/#1707 — completes the rename-affordance triage:
- #1698 fixed: dblclick rename was unreachable on files (preview hijacked)
- #1707 fixed: single-click on filename did nothing (over-aggressive guard)
- #1710 (this PR): tooltip claimed dblclick-rename on folders too

Closes #1710

Tests: 4 source-level regression tests in tests/test_1710_folder_tooltip.py
guard the gate, the unchanged dir-dblclick navigate behaviour, the i18n key,
and that files still receive the tooltip. All 13 file-tree handler tests
(4 new + 9 from #1707) pass.
2026-05-05 16:41:30 +00:00
Michael Lam 311e69b0ba fix: preserve scroll on stream completion 2026-05-05 09:23:29 -07:00
nesquena-hermes b5e8e67d71 fix(workspace): preserve single-click open + double-click rename on filename (#1707)
Closes #1707 — single-click on a workspace tree filename did nothing.

#1698 was a regression where the filename's dblclick rename handler was
unreachable because the row's el.onclick (openFile) fired synchronously
on the first click. The fix in #1702 stopped click propagation on nameEl
— but that broke single-click activation entirely (#1707): clicking the
filename now did nothing, you had to click the icon or row whitespace
to open the file.

Restored fix preserves both intents via a 300ms debounced delegator:

  let _nameClickTimer = null;
  nameEl.onclick = (e) => {
    e.stopPropagation();
    if (_nameClickTimer) { clearTimeout(_nameClickTimer); _nameClickTimer = null; }
    _nameClickTimer = setTimeout(() => {
      _nameClickTimer = null;
      if (typeof el.onclick === 'function') el.onclick(e);
    }, 300);
  };
  nameEl.ondblclick = (e) => {
    e.stopPropagation();
    if (_nameClickTimer) { clearTimeout(_nameClickTimer); _nameClickTimer = null; }
    // ... existing rename body
  };

Single-click on nameEl schedules a setTimeout that calls el.onclick(e)
after the dblclick threshold passes (300ms — matches the OS dblclick
threshold on most platforms). Double-click cancels the pending timer
and triggers the existing rename input.

Cost: 300ms latency on file-open clicks. Acceptable trade for keeping
rename reachable on single-click.

Also updated tests/test_workspace_tree_rename.py to accept both the
pre-#1707 (pure stopPropagation) and post-#1707 (debounced delegator)
shapes — the original assertion was too narrow and would have rejected
the correct fix.

9 new regression tests in tests/test_1707_workspace_filename_click.py:
  - 6 source-level static-analysis checks on the patched handler shape
  - 3 behavioral tests via Node VM (synthesize click → 300ms delay,
    click → dblclick within tick → assert rename mounts + openFile
    is not called).

7 of 9 tests fail on master pre-fix (verified); all 9 pass after.
2026-05-05 16:13:58 +00:00
Nathan Esquenazi 2a838ee95a Stage 301: PR #1706 2026-05-05 15:49:28 +00:00
Michael Lam 8c8e2d3573 fix: keep multi-image paste attachments 2026-05-05 08:45:14 -07:00
Nathan Esquenazi e5927c6d0a Stage 301: PR #1704 2026-05-05 15:41:44 +00:00
Nathan Esquenazi debb4c5282 Stage 301: PR #1702 2026-05-05 15:41:43 +00:00
Nathan Esquenazi 8e7a9b1632 Stage 301: PR #1684 2026-05-05 15:41:43 +00:00
bergeouss 6173d6d0ea fix(ui): inline provider chip + group model count in model picker (#1425)
- Add .model-opt-provider chip (right-aligned, muted) on every model row
  that belongs to a provider group, making same-name models across
  providers visually distinguishable at a glance.
- Add per-group model count to group headings: 'OpenRouter (47)'.
- Add subtle border-top divider between provider groups for visual
  separation during scroll.

Scope: Shape A from #1425 — smallest change, ~15 LOC, no API churn.
Note: Settings model picker is a native <select> and already has optgroup
labels; this targets the custom dropdown used in the composer.

Closes #1425
2026-05-05 15:41:22 +00:00
Michael Lam f76921d322 fix: honor markdown fence lengths 2026-05-05 08:36:17 -07:00
Michael Lam ff232493ce fix: keep workspace rename double-click reachable 2026-05-05 08:33:34 -07:00
Michael Lam d51510a7dc fix: keep HTTP update errors out of network recovery 2026-05-05 03:13:55 -07:00
Michael Lam 03949f8093 fix: clarify update network failures 2026-05-04 21:02:03 -07:00
test 449f37ebd8 Stage 300: PR #1673 — feat: show LLM Gateway routing metadata by @Michaelyklam 2026-05-05 02:27:24 +00:00
test 32f37d3d78 Stage 300: PR #1676 — Add Hermes agent heartbeat alert by @Michaelyklam 2026-05-05 02:27:24 +00:00
Michael Lam c94ec31dec feat: show LLM Gateway routing metadata 2026-05-05 02:26:55 +00:00
Michael Lam 22df075b8a feat: add active provider quota status 2026-05-05 02:26:52 +00:00
Michael Lam 960e45f77f feat: add agent heartbeat alert 2026-05-05 02:25:06 +00:00
Nathan Esquenazi e2748fe961 Apply Opus pre-release SHOULD-FIX (absorbed in stage-299)
Per Opus advisor on stage-299:

1. Bounded WIKI_PATH walk + forbidden-root guard (api/routes.py)
   - _LLM_WIKI_MAX_FILES = 10000 caps rglob iteration (prevents hangs on
     symlink loops or pathologically-large trees)
   - _LLM_WIKI_FORBIDDEN_ROOTS blocklist refuses '/' '/etc' '/usr' '/var'
     '/opt' '/sys' '/proc' even if WIKI_PATH is misconfigured to point
     at them
   - Self-DoS prevention: /api/wiki/status fires on every Insights tab
     open via Promise.all, and unbounded rglob would block the endpoint

2. URL-scheme guard for docs_url interpolation (static/panels.js)
   - rawDocsUrl is regex-validated against /^https?:\/\//i before being
     interpolated into the <a href=> attribute
   - esc() HTML-escapes but doesn't validate URL scheme; docs_url is
     server-controlled today but the contributor scaffolded it for
     potential config-driven use, so future-proof against javascript:
     scheme XSS

6 regression tests in tests/test_stage299_opus_fixes.py pin both fixes.
2026-05-05 02:15:25 +00:00
Nathan Esquenazi 4e9ec6f191 fix(sidebar): scroll jumps back to 0 on small lists (≤80 sessions) — #1669 follow-up
PR #1669 added DOM virtualization to renderSessionListFromCache() with two issues
for lists below the virtualization threshold (≤80 rows):

1. The unconditional scroll listener triggered renderSessionListFromCache() on
   every rAF, rebuilding the entire list DOM on every scroll event.
2. After each rebuild, scrollTop was only restored when virtualWindow.virtualized
   was true (i.e. total > 80). For lists ≤ 80 rows, scrollTop dropped to 0 on
   every scroll event, producing a 'scroll keeps jumping back' feel.

Fix:
- Always restore scrollTop after re-render when listScrollTopBeforeRender > 0
  (regardless of virtualized flag).
- Short-circuit _scheduleSessionVirtualizedRender when total <=
  SESSION_VIRTUAL_THRESHOLD_ROWS (saves wasteful rebuild on small lists).

Live verified on a 56-session sidebar: scrollTop holds across animation frames.
3 regression tests pin the fix shape.
2026-05-05 02:02:54 +00:00
test 136d858963 Stage 299: PR #1587 — Filter low-value CLI agent sessions by @franksong2702 2026-05-05 01:54:08 +00:00
test df8ee6a8ad Stage 299: PR #1662 — feat(logs): add Logs tab MVP by @Michaelyklam 2026-05-05 01:53:56 +00:00
Frank Song 79d0762d8c Filter low-value CLI agent sessions 2026-05-05 01:52:42 +00:00
Michael Lam af1c628292 feat: add logs tab MVP 2026-05-05 01:51:05 +00:00
Michael Lam 2684d6fa98 feat: add LLM Wiki status panel 2026-05-05 01:48:32 +00:00
test 3699e83c43 Stage 298: PR #1677 — feat: link official Hermes dashboard by @Michaelyklam 2026-05-05 01:29:49 +00:00
Michael Lam b0953b6a7f feat: link official Hermes dashboard 2026-05-05 01:23:55 +00:00
Michael Lam e0e991126f feat: add searchable MCP tool inventory 2026-05-05 01:20:32 +00:00
test 2ec18b728a Stage 298: PR #1670 — feat: add MCP server visibility panel by @Michaelyklam 2026-05-05 01:18:35 +00:00
test 8c93b995ef Stage 298: PR #1678 — Add Claude Code session imports by @Michaelyklam 2026-05-05 01:18:35 +00:00
test def1507828 Stage 298: PR #1674 — feat(tasks): add scheduled job profile selector by @Michaelyklam 2026-05-05 01:18:35 +00:00
test dfb3798470 Stage 298: PR #1663 — feat: add plugins visibility panel by @Michaelyklam 2026-05-05 01:18:35 +00:00