Commit graph

2013 commits

Author SHA1 Message Date
Rod Boev
36742abcee fix(#4875): widen continuation-prompt filter to cover output-length and tool-call variants 2026-06-25 01:21:08 +00:00
Rod Boev
f43bf2df6a docs(#4856): update stale call-site comment to match overflow-anchor semantics 2026-06-25 01:06:09 +00:00
Rod Boev
0668cfa77c fix(#4856): suppress Chromium scroll-anchor re-selection during mobile DOM rebuild 2026-06-25 01:06:09 +00:00
Charles Bine
867471c7d3 fix(#484): drop noisy "parse failed" note on JSON/YAML fragments
LLMs routinely emit JSON/YAML *fragments* in fenced blocks — a bare
`"key": "value"` line, snippets with `...`, trailing commas — which
legitimately fail JSON.parse. initTreeViews() stamped an unstyled
`<div class="tree-parse-note">parse failed</div>` below such blocks,
producing bare white text under an otherwise-clean code box.

The note was incidental, not designed: it was bolted onto the YAML
lazy-load commit (29a31a6e), has no CSS, no behavior test, and originally
even fired on *valid* YAML before the CDN script finished loading (later
fixed). The block still renders as syntax-highlighted raw, so the note
added only noise.

Remove the note insertion; fall through silently to raw view. The per-block
Raw/Tree toggle, YAML lazy-load, and the #484 default-view config are all
unchanged. The `parse_failed_note` i18n key is intentionally retained (kept
green by test_chinese_locale.py) to avoid touching all 13 locales.

Verification: ./scripts/test.sh tests/test_issue484_json_tree_viewer.py
tests/test_chinese_locale.py — 29 passed.
2026-06-24 22:14:27 +00:00
Rod Boev
2ca65abefa fix: use JSON.parse for bracket-prefixed lines instead of blanket suppression 2026-06-24 20:35:04 +00:00
Rod Boev
29604e5e7b fix: fall back to tc.snippet for cold-loaded tool results 2026-06-24 20:35:04 +00:00
Rod Boev
c19aaae27f fix(#4752): show tool result instead of call args in collapsed card preview 2026-06-24 20:35:04 +00:00
nesquena-hermes
57a78e9284 fix(#4728): clear stale custom:* base_url + default wins without loaded session (gate findings)
Two gate-found CORE/SILENT edges fixed on-branch:
- api/config.py: switching the main default to a different custom:* provider kept
  the OLD provider's base_url, routing New Chat to the wrong endpoint. The
  base_url-clear on provider-change now covers custom:* too (was exempted).
- static/sessions.js: newSession() skipped the configured default when S.session
  was null but the picker held stale state (e.g. after deleting the last session),
  inheriting the stale model. Default now wins over legacy picker regardless of
  loaded-session state (explicit empty-composer override still takes priority).
- Updated 2 stale source-grep precedence assertions + widened a brittle 6000-char
  newSession() slice window to 7000 (assignment block grew; intent unchanged).
- 2 new non-vacuous regression tests (custom base_url clear; precedence).

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-24 20:17:29 +00:00
Rod Boev
23b3206f83 fix(#4728): guard settings default-model capture when no option is selected 2026-06-24 20:09:30 +00:00
Rod Boev
bede035a5e fix(#4728): persist provider-only default-model changes end to end 2026-06-24 20:09:30 +00:00
Rod Boev
69d202b94a fix(#4728): treat provider-only default changes as settings edits 2026-06-24 20:09:30 +00:00
Rod Boev
eba089fd90 fix(#4728): preserve fallback-order guarantees for default-model routing 2026-06-24 20:09:30 +00:00
Rod Boev
cdb44fb7e0 fix(#4728): sync saved default model provider into new chat state 2026-06-24 20:09:30 +00:00
Rod Boev
dde3845171 fix(#4728): keep default-model requests on the configured provider 2026-06-24 20:09:30 +00:00
Rod Boev
d8b58da018 fix(#4728): use configured default for new conversations 2026-06-24 20:09:30 +00:00
nesquena-hermes
3c8cfdc589 fix(#4811): regenerate row_id/seq from final order index so anonymous tool rows don't collide (gate finding)
Codex gate found a SILENT drop: reconnect scene rows are built with orderIndex=0,
so two tools WITHOUT a tool id at the same message index both got row_id
'...:tool:<idx>:0'; _completeSettledAnchorSceneForTurn dedupes by row_id, silently
dropping the second. Now the emit loop rewrites order_index AND regenerates the
index-derived row_id + seq + identity.seq from the final per-bucket position
(tid-based tool row_ids, already unique, are left untouched). Regression test
added + proven non-vacuous.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-24 19:48:56 +00:00
Rod Boev
f330fa7f44 perf(reconnect): pre-index toolCalls and remove unreachable guard (#4811) 2026-06-24 19:39:40 +00:00
Rod Boev
057b878136 fix(#4811): sort reconnect scene rows by phase and started_at chronology 2026-06-24 19:39:40 +00:00
nesquena-hermes
c21d30e02b fix(#4757): defer window-drop cleanup a tick so target ondrop runs first (gate finding)
Codex re-gate: the capture-phase window 'drop' listener fired BEFORE the target
el.ondrop, clearing _wsActiveDragPath before _isWorkspaceTreeMoveDrag/_wsDragSrcPath
ran — re-breaking the macOS stripped-MIME move it set out to fix. setTimeout(_clearWsDragData,0)
lets the real drop handler complete, then clears the lingering flag.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-24 19:13:59 +00:00
nesquena-hermes
63e3f91654 harden(#4757): gate stripped-MIME drop on text/plain===tracked path + robust drag-flag cleanup (gate finding)
Codex gate found a SILENT hazard: if a workspace drag is abandoned without
ondragend firing (cancel/drop-outside/blur), _wsActiveDragPath lingered and a
later FOREIGN text/plain drag could be misread as a workspace move. Hardening:
- _wsDragSrcPath now only trusts the active flag when the drop's own text/plain
  EQUALS _wsActiveDragPath (a foreign/empty text drag resolves to '' -> no move).
- Shared _clearWsDragData() called from ondragend, a drop finally, and
  window-level dragend/drop/pagehide/blur fallbacks (lost-dragend cleanup).
- 2 regression tests (stale-flag foreign-text -> empty; _clearWsDragData resets),
  proven non-vacuous.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-24 19:06:30 +00:00
Rod Boev
36fee0c6a4 fix(#4757): add active-drag flags so macOS workspace drag-drop lands 2026-06-24 18:58:33 +00:00
Brett Yelverton
2a40ca4240 fix(streaming): restore mobile scroll-jank guard during live renders
A later streaming parse-cache change dropped the #MOBILESCROLL guard from
the live-stream render tick while leaving the helper and CSS in place.
On iOS PWA, Safari can paint a transient scrollTop=0 frame during streamed
DOM rebuilds, forcing the reader to the first/oldest message.

Re-add _fixMobileScrollJank() before streaming DOM work begins and pin it
with a regression test so the third layer of the mobile scroll fix cannot
be silently dropped again.
2026-06-24 17:20:40 +00:00
nesquena-hermes
984fd9557c Merge remote-tracking branch 'origin/master' into gate/4813-cron-prefix 2026-06-24 11:01:50 +00:00
nesquena-hermes
9493f1640f Merge branch 'peek-4813' into gate/4813-cron-prefix 2026-06-24 10:49:48 +00:00
nesquena-hermes
631f1a35da Merge branch 'peek-4832' into gate/4832-kanban-height 2026-06-24 10:38:49 +00:00
Hoang Luan
78cd8798ec
Merge branch 'master' into fix/cron-model-provider-prefix 2026-06-24 17:05:29 +07:00
nesquena-hermes
671f4504ae fix(#4702): co-seed _lastMessageClientHeight with every programmatic _lastScrollTop write (Codex gate)
Codex found the guard had a first-event gap: programmatic seeds of _lastScrollTop
(_setMessageScrollToBottom, scrollToBottom, snapshot restore paths) didn't seed
_lastMessageClientHeight, so the FIRST native toolbar-collapse scroll saw a null
prior height -> grew=false -> the false-unpin still fired. Now every programmatic
_lastScrollTop=el.scrollTop is paired with _lastMessageClientHeight=el.clientHeight.
2026-06-24 10:02:52 +00:00
nesquena-hermes
fe40912eb9 fix(#4702): don't false-unpin a freshly-opened session on iOS portrait toolbar settle
iOS Safari portrait resolves its dynamic toolbar height after first paint; when the
toolbar collapses the #messages scroller grows (clientHeight up), firing a scroll
event with decreased scrollTop that was misread as an upward user scroll -> falsely
set _messageUserUnpinned, and the ResizeObserver settle then self-cancelled, leaving
the reader stranded at the TOP of a freshly-opened conversation (landscape unaffected
since it doesn't resize late). Sibling of #4701.

Fix (touch-safe, no-op on desktop/landscape where clientHeight is stable):
- Track _lastMessageClientHeight; gate movedUp on !grew so a container-height
  INCREASE can't be read as an upward scroll. Reset on session switch.
- For explicit (open) settles, also observe the scroller itself so a late portrait
  toolbar collapse re-anchors the bottom.

Independent of #4818 (overflow-anchor paint-gap) — different mechanism, coexists.
Diagnosis credit: investigation. Co-authored-by: reinocheong <reporter>
2026-06-24 09:56:06 +00:00
jkobject
93afd60425 fix(kanban): bound column card list height 2026-06-24 10:57:03 +02:00
Orange Pi Vietnam
d341f2084c
Merge branch 'nesquena:master' into master 2026-06-24 15:12:44 +07:00
Hoang Luan
c54186c42a
Merge branch 'master' into fix/cron-model-provider-prefix 2026-06-24 14:47:27 +07:00
Tony Tran
730c6940c9 fix: add missing Vietnamese locale keys 2026-06-24 14:28:22 +07:00
nesquena-hermes
92b3ca7046 Merge branch 'peek-4818' into gate/4818-mobile-scroll 2026-06-24 06:59:17 +00:00
nesquena-hermes
988acdb441 Merge remote-tracking branch 'origin/master' into gate/4803-recycle-harden 2026-06-24 06:49:41 +00:00
Orange Pi Vietnam
e42d308245
Merge branch 'master' into master 2026-06-24 13:42:54 +07:00
akrhin
d89cfea45e
Merge branch 'master' into fix/mobile-scroll-jank-clean 2026-06-24 09:41:54 +03:00
Orange Pi Vietnam
b12f541394
Merge branch 'nesquena:master' into master 2026-06-24 13:41:38 +07:00
akrhin
c4e82c6155 chore: cleanup per Greptile review
- Remove dead _scrollAnchorRestore variable (returns undefined)
- Remove redundant _fixMobileScrollJank() call from _doRender()
  (CSS already gives overflow-anchor:auto on mobile via media query;
   desktop exits guard early — call was a no-op on every tick)
- Update JSDoc on _fixMobileScrollJank to reflect actual usage
2026-06-24 09:38:01 +03:00
akrhin
98688491c1 fix(#MOBILESCROLL): prevent scrollTop jank to top on mobile during streaming DOM rebuild
Root cause: .messages had overflow-anchor:none unconditionally. On mobile
touch devices (iOS Safari, Android Chrome), the compositor can paint a frame
with scrollTop=0 between innerHTML='' and snapshot restore, scroll-janking
the user to the top of the transcript.

Three-layer fix:

1. CSS (style.css): Use overflow-anchor:auto on touch devices (mobile),
   keep overflow-anchor:none on desktop (mouse-driven) via
   @media (hover:hover) and (pointer:fine) — preserves the existing
   behavior where tool/card inserts don't yank a reading user.

2. JS guard (ui.js): Add window._fixMobileScrollJank() that temporarily
   sets overflowAnchor='auto' on #messages before innerHTML='' and
   clears it one rAF later — prevents the compositor frame with scrollTop=0.

3. Streaming protection (messages.js): Call _fixMobileScrollJank() at the
   top of each _doRender() tick so streaming DOM growth doesn't jank the
   viewport position.

Tests:
- Updated test_issue1360_streaming_scroll_hardening.py: split the
  overflow-anchor test into desktop (none) and mobile (auto) variants.
- Added test_mobile_scroll_jank.html: interactive visual test that
  simulates the scroll jank on a mobile-sized viewport.
2026-06-24 09:37:55 +03:00
nesquena-hermes
05cf44ffca Merge branch 'peek-4803' into gate/4803-recycle-harden 2026-06-24 06:37:38 +00:00
nesquena-hermes
b9f1491de7 Merge branch 'peek-4798-v2' into gate/4798-v2 2026-06-24 06:25:59 +00:00
luandnh
878001c231 fix: strip @provider: prefix from cron job model override
When a cron job's model/provider override was set via the form, the dropdown
option value could contain a @provider: prefix (e.g. @custom:9router:chat)
from _apply_provider_prefix in the API response. Since cron jobs store model
and provider as separate fields, the model should be just the bare model name
(e.g. chat) without the provider prefix.

- Add _cronModelBareName() helper to strip @provider: prefix
- Apply in saveCronForm for create, update, and duplicate paths
- Update CHANGELOG.md
2026-06-24 05:03:38 +00:00
nesquena-hermes
e9283c8182 fix(#2814): default _kanbanLanesByProfile=true to match server default (Opus note)
The JS module default was false (consolidated) while the server defaults
lane_by_profile=True (lanes). If the board painted before _kanbanApplyConfigDefaults
ran, there'd be a one-frame consolidated flicker before snapping to lanes. Matching
the client default to the server default removes the flicker. Co-authored-by: stefan-wisbric
2026-06-23 23:51:22 +00:00
nesquena-hermes
5253724f90 rebase(#2814): resolve i18n onto current master + add kanban_view_* keys to all 13 locales
Took master's i18n.js as base (it has all 13 locale blocks + had reorganized the
kanban keys, so the stale-branch conflict block would have duplicated them in
zh-Hant), then inserted #2814's 5 kanban_view_* keys into every locale block after
kanban_new_task — real translations for it/ja/zh/zh-Hant/fr (from the PR), English
fallback elsewhere. All 13 locales now have exactly 1 of each key; locale-parity +
kanban tests green (291 passed).
2026-06-23 23:40:45 +00:00
nesquena-hermes
d68afd7862 fix(#4800): key the streaming parse-cache on liveReasoningText too (Codex gate)
The parse-result cache keyed only on assistantText, but _parseStreamState() is a
function of BOTH assistantText AND liveReasoningText. A 'reasoning' SSE event (which
mutates liveReasoningText without calling _scheduleRender) landing between a token's
_scheduleRender(parsed) and the rAF firing made _doRender serve a parsed computed from
the older reasoning -> _renderLiveThinking could show stale thinking text. Keying the
cache on liveReasoningText too forces a fresh _parseStreamState when reasoning changed,
and also makes a canceled-then-rescheduled render miss the stale entry. Co-authored-by: wlknight
2026-06-23 22:52:51 +00:00
Rod Boev
6cf4f7a5d0 test(#4793): decouple recycle reset assertions 2026-06-23 18:49:50 -04:00
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