Commit graph

1945 commits

Author SHA1 Message Date
nesquena-hermes
1dbff4e708 Merge branch 'peek-4800' into gate/4800-stream-perf 2026-06-23 22:46:14 +00:00
nesquena-hermes
e8b33066ec Merge remote-tracking branch 'origin/master' into stage/rodboev-4797-4796
# Conflicts:
#	CHANGELOG.md
2026-06-23 22:32:36 +00:00
nesquena-hermes
edbe676b0d Merge branch 'peek-4796' into stage/rodboev-4797-4796 2026-06-23 22:19:13 +00:00
nesquena-hermes
e26a6e47c0 Merge branch 'peek-4797' into stage/rodboev-4797-4796 2026-06-23 22:19:12 +00:00
wlknight
20a900be60 fix(perf): eliminate O(DOM) streaming render freeze on long agent answers
Introduce _safeSmdRenderer — a renderer wrapper that hooks smd's set_attr
to validate URL schemes inline as each DOM node is created, eliminating
the per-token _sanitizeSmdLinks(assistantBody) full-DOM scan that caused
progressive freezing on answers of 30,000+ chars (5,000+ DOM nodes).

Also add parse-result caching to _scheduleRender to avoid redundant
_extractInlineThinkingFromContent scans between the token event handler
and the rAF render callback.

Changes:
- New _safeSmdRenderer(el): URL scheme safety inline via set_attr hook
  (identical algorithm to proven _streamFadeRenderer.set_attr, no animation)
- _smdNewParser: non-fade path uses _safeSmdRenderer instead of bare
  default_renderer
- _smdWrite: removed _sanitizeSmdLinks(assistantBody) call — safety now
  handled by renderer's set_attr at node-creation time
- _scheduleRender(parsed): accepts optional cached parse result from
  token handler to avoid duplicate O(n) text scans
- Token handler: passes _parseStreamState() result to _scheduleRender

_sanitizeSmdLinks is preserved at _smdEndParser (stream-end safety net)
and in fallback paths where smd is not available.

All 166 existing tests pass.
2026-06-24 06:09:44 +08:00
nesquena-hermes
8e384548c3 fix(#4788): reword a comment apostrophe that tripped test_smooth_text_fade's brace-matcher
test_smooth_text_fade.py::test_preferences_ui_exposes_and_saves_fade_text_effect
extracts loadSettingsPanel() via a brace-matcher that does NOT skip comments. The
new structured-code wiring added a comment containing 'isn't' + ''auto' mode' —
the apostrophes were mis-read as string delimiters, scrambling the brace depth so
the matcher closed the function early (before the fade-checkbox wiring), failing
the test. Reworded to 'is not' / 'auto mode' (zero behavior change; node -c clean).
The fade feature + the structured-code feature are both unchanged. Co-authored-by: metaember
2026-06-23 21:40:08 +00:00
nesquena-hermes
48663a4af3 Merge branch 'peek-4788' into gate/4788-json-view 2026-06-23 21:19:18 +00:00
Rod Boev
94e25ad494 fix(#4753): notify background desktop tabs without hiding streams 2026-06-23 17:10:03 -04:00
nesquena-hermes
7db3aa123f Merge branch 'peek-4656' into stage/4656-escape-readonly 2026-06-23 20:43:53 +00:00
nesquena-hermes
2d6b3c067f Merge branch 'peek-4781' into stage/4781-worklog-collapse
# Conflicts:
#	CHANGELOG.md
2026-06-23 20:27:38 +00:00
nesquena-hermes
ca368cc166 Merge branch 'peek-4478' into stage/4478-dom-recycle 2026-06-23 20:07:56 +00:00
Charles Bine
dd66640fae
feat(#484): configurable default view for JSON/YAML code blocks
The JSON/YAML tree viewer (#484) hardcoded its default: valid json/yaml
fenced blocks opened in Tree view at 10+ lines (`const showTree=lineCount>=10;`).
Make that default user-configurable while preserving current behavior.

- New settings (persisted via /api/settings -> settings.json):
  - structured_code_default_view: auto | on | off  (default auto)
  - structured_code_auto_tree_lines: int 1..1000    (default 10)
- ui.js: replace the hardcode with a sanitized, unit-testable decision
  helper _structuredCodeShowTree(mode, threshold, lineCount); Tree/Raw
  toggle, Raw <pre><code>, parse-failure fallback, and YAML lazy-load
  are unchanged.
- Appearance settings: 'JSON/YAML code blocks' select + 'Auto threshold
  lines' input (disabled outside auto mode); wired through the existing
  appearance-autosave and explicit-save paths; live re-render on change.
- boot.js seeds window globals; i18n labels added to all 13 locales.
- Default (auto + 10) reproduces the original >=10 behavior exactly.
2026-06-23 13:51:45 -04:00
nesquena-hermes
a7eb7de83f Merge remote-tracking branch 'origin/master' into peek-4748
# Conflicts:
#	CHANGELOG.md
2026-06-23 17:43:38 +00:00
nesquena-hermes
98e672551a Merge branch 'peek-4782' into gate/4782-sidebar-trim 2026-06-23 16:46:10 +00:00
nesquena-hermes
29ceed3a72 fix(#4778): gate deferred rAF re-restore on still-unpinned (Opus gate)
Opus caught a narrow timing race: if a reader re-pins between the scroll-rebuild
guard engaging and its deferred rAF firing, the stale re-restore would yank them
back off the bottom. Gate the re-restore on _messageUserUnpinned at rAF time; the
minHeight release still always runs. Normal single-render unpinned case unaffected.
2026-06-23 16:19:50 +00:00
Rod Boev
2504eb90d7 fix(#4755): honor profile default workspace on fresh sessions 2026-06-23 12:18:44 -04:00
nesquena-hermes
f4af6d8419 fix(#4778): scroll-rebuild guard must not reclassify a pinned follower as unpinned
Codex regression gate caught: the guard predicate keyed on a raw messagesEl.scrollTop>0,
so a pinned reader transiently at bottomDistance>250 mid-render (assistant body grows
before the anchor scene re-renders) got flipped to _messageUserUnpinned=true/_scrollPinned=false
and lost auto-follow. Require an explicit non-follow signal (_messageUserUnpinned || _scrollPinned===false)
instead. Updated the existing guard test to a genuine unpinned reader + added a pinned-large-growth
regression. Co-authored-by: franksong2702
2026-06-23 16:17:01 +00:00
nesquena-hermes
c6d633fd2c Merge branch 'peek-4777' into stage/livestream-chat-scroll-4778-4777-4780
# Conflicts:
#	CHANGELOG.md
2026-06-23 16:09:00 +00:00
nesquena-hermes
a349b0b7e8 Merge branch 'peek-4780' into stage/livestream-chat-scroll-4778-4777-4780 2026-06-23 16:08:31 +00:00
Frank Song
f653b1b8d9 feat: allow live worklog summary collapse 2026-06-23 23:22:16 +08:00
Hermes Agent
ba6349bdcb fix: reset scroll state on session switch to prevent stale unpin blocking scroll-to-bottom 2026-06-23 17:19:50 +02:00
Rod Boev
bd72c785b5 fix(sidebar): reject cached fallback rows from the wrong hidden-filter mode (#4775) 2026-06-23 11:13:33 -04:00
Frank Song
4c8e88ab2d fix: dedupe live compact worklog process prose 2026-06-23 23:03:04 +08:00
Rod Boev
9296604667 fix(sidebar): keep delete fallback in the visible session scope (#4775) 2026-06-23 11:00:40 -04:00
Frank Song
1269289d01 fix: guard live worklog scroll rebuilds 2026-06-23 23:00:32 +08:00
Rod Boev
df6dba7a94 fix(sidebar): refresh project-chip payload after filter switch (#4775) 2026-06-23 10:40:40 -04:00
Rod Boev
e27b7cf042 perf(sidebar): trim hidden and empty sidebar payload rows (#4775) 2026-06-23 10:29:06 -04:00
Rod Boev
148a1bfab2 Keep source-bucket cleanup tied to real session origin 2026-06-23 07:58:55 -04:00
Rod Boev
df4cdf7f9b Prevent stale cross-bucket rows during source-tab switches 2026-06-23 07:49:46 -04:00
Rod Boev
0f5e0d71b7 Keep sidebar state helpers compatible with isolated JS harnesses 2026-06-23 07:37:42 -04:00
Rod Boev
f5f8f48006 Keep hidden source state through failed source refreshes 2026-06-23 07:06:07 -04:00
Rod Boev
efb784730a Keep sidebar source fallbacks honest under refresh failure 2026-06-23 06:51:12 -04:00
Rod Boev
534008c3fc Preserve hidden source runtime state across tab refreshes 2026-06-23 06:39:34 -04:00
Rod Boev
4ee5ef0d01 fix(#4766): harden source-tab refresh state 2026-06-23 06:21:15 -04:00
Rod Boev
73a209aca7 fix(#4766): close review gaps in sidebar source filtering 2026-06-23 06:10:16 -04:00
Rod Boev
9a5a3eb6d5 fix(#4766): filter /api/sessions by active source bucket 2026-06-23 05:56:00 -04:00
Rod Boev
631d62d9e8 fix(scroll): keep shared anchor restore semantics after rebase (#4346) 2026-06-23 03:11:06 -04:00
Rod Boev
e4dcfb3cb8 fix(#4346): reject dual-class assistant turns from user-row recycling 2026-06-23 02:57:00 -04:00
Rod Boev
568fb52727 fix(#4346): address drag-release and recycled-row review regressions
# Conflicts:
#	static/ui.js
#	tests/test_issue677.py
2026-06-23 02:57:00 -04:00
Rod Boev
c4bec146a3 fix(#4346): keep scrollHeight stable during native scrollbar drag 2026-06-23 02:55:54 -04:00
Rod Boev
8b9805b6dd fix(#4346): use data-recycle-key for turn stash, add type checks per review
# Conflicts:
#	static/ui.js
2026-06-23 02:55:25 -04:00
Rod Boev
621b1b1a62 fix(#4346): prevent _programmaticScroll from staying stuck after load
# Conflicts:
#	static/ui.js
2026-06-23 02:54:56 -04:00
Rod Boev
2389eda01d fix(#4346): stamp dataset.msgIdx on assistant-turn containers so recycling stash can find them 2026-06-23 02:54:28 -04:00
Rod Boev
9c364d8498 perf(#4346): recycle stable DOM nodes during virtual-scroll measurement re-renders
# Conflicts:
#	static/ui.js
#	tests/test_issue4346_vscroll_footer_jitter.py
2026-06-23 02:54:28 -04:00
Rod Boev
decb1037bc Keep escape grants browser-confirmed and refreshable (#4582) 2026-06-23 02:53:12 -04:00
Rod Boev
e8c4cb85c3 Preserve explicit inline markers for HTML preview guards 2026-06-23 02:53:12 -04:00
Rod Boev
d20e9dd852 Prevent stale escape errors from collapsing live browse grants 2026-06-23 02:53:12 -04:00
Rod Boev
58b361b811 Keep escape navigation compatible with workspace static guards 2026-06-23 02:53:12 -04:00
Rod Boev
29f38902be Keep escape navigation on the tested UI route path (#4582)
# Conflicts:
#	static/workspace.js
2026-06-23 02:53:12 -04:00
Rod Boev
0f80113c12 feat(workspace): allow browser-only escape-target navigation (#4582)
# Conflicts:
#	static/workspace.js
2026-06-23 02:52:44 -04:00