Commit Graph
290 Commits
Author SHA1 Message Date
nesquena-hermesandRod Boev b931ae2f9b fix(settings): reconcile #5145 rename+steer-flip onto master's #5170 mirror
Rebase PR #5162 (rename busy_input_mode -> default_message_mode; flip the
default from 'queue' to 'steer') onto current origin/master WITHOUT dropping
the shipped #5170 localStorage persistence mirror.

Rename the mirror machinery to the new setting name for consistency:
  _BUSY_INPUT_MODES        -> _DEFAULT_MESSAGE_MODES        (values unchanged)
  _normalizeBusyInputMode  -> _normalizeDefaultMessageMode  (fallback now 'steer')
  _persistBusyInputMode    -> _persistDefaultMessageMode
  _readPersistedBusyInputMode -> _readPersistedDefaultMessageMode
  window._busyInputMode    -> window._defaultMessageMode (+ renamed exports)

localStorage: write the new 'hermes-default-message-mode' key; read it with a
fallback to the legacy 'hermes-busy-input-mode' key so an existing user's
persisted preference survives the rename.

Preserve #5170 behavior at every mirror site under the new names:
  - boot success  -> window._defaultMessageMode=_persistDefaultMessageMode(...)
  - boot FAILURE  -> window._defaultMessageMode=_readPersistedDefaultMessageMode()
    (NOT a hardcoded 'steer' — a saved 'interrupt'/'queue' must still apply when
    the server is unreachable; do not regress #5167/#5132)
  - preferences autosave, settings-panel load, and _applySavedSettingsUi all
    persist through _persistDefaultMessageMode(...)

Tests updated for the rename while keeping the persistence-behavior assertions
(test_1062, test_5145, test_5167); test_5167 gains explicit guards that the
load-failure path reads the persisted pref and never hardcodes a literal mode,
plus autosave/panel-load mirror-write coverage.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-07-02 05:44:48 +00:00
nesquena-hermes f50b1d84e1 feat(sessions): move Export-to-HTML into the sidebar conversation menu
Follow-up to v0.51.819 which removed the export button from the composer footer
(it tripped the footer overflow-collapse, hiding model/workspace labels). Per
design consult (Fable) + ChatGPT/Open-WebUI convention, export now lives in the
per-conversation sidebar three-dot action menu, right after Duplicate:
- exportSessionHTML(session) parameterized (was active-session-only); Settings
  button now wired ()=>exportSessionHTML() and still exports the active session
- new _appendSessionExportHtmlAction() added after Duplicate + in the read-only
  early-return branch (export is non-mutating; imported sessions re-exportable)
- exports THAT row's conversation, not just the active one
- download icon added to ICONS; session_export_html[_desc] added to 14 locales
- Settings HTML button retained as secondary data-management entry
2026-07-02 03:21:18 +00:00
nesquena-hermes 219615b5a7 fix(composer): remove Export-to-HTML button from composer footer (keep settings-panel export)
The #4968 export button was hard-inserted into the composer footer .composer-left
row, bypassing the configurable composer-control framework. On desktop it pushed the
footer over its overflow threshold, tripping _fitComposerFooter into cf-icons mode
which HIDES the model/workspace/profile text labels. Removing it restores label
visibility. Export stays available via the settings-panel HTML button (#btnExportHTML).
2026-07-02 02:48:45 +00:00
nesquena-hermes b31f3fab3b #4968: export chat to self-contained themed HTML, rebased on master 2026-07-02 02:36:09 +00:00
nesquena-hermes 9fb2731848 #5310: push-to-talk hold gesture (#3700) 2026-07-01 23:26:15 +00:00
Rod Boev c561cf546b fix(#5340): use local time for pasted-text filenames 2026-07-01 15:22:59 -04:00
6716f03f4d fix(webui): stop false clarify-unavailable toast; add interrupt provenance (#5345)
/api/clarify/pending always returns HTTP 200 when present (returns
{"pending": null} for an unknown session — it never 404s). The front-end
clarify poller warned "Clarify endpoint unavailable. Please restart
server." on ANY caught error whose message merely contained "404" or
"not found", so an unrelated stale-session 404 ("Session not found", e.g.
an old-profile session polling briefly after a profile switch) or a
transient error produced a misleading missing-endpoint toast that pointed
operators at the wrong layer.

Clarify polling now branches on the structured HTTP status that api()
attaches to the thrown Error (err.status):
- 404 "Session not found" -> handled as a stale-session poll (stop + hide
  card silently), no toast;
- restart-server warning fires only on a genuine route-not-found 404 whose
  body is NOT session-scoped;
- poll failures are logged with path, status, polling session id, and
  current session id for diagnosis.

Interrupt provenance: cancelStream()/cancelSessionStream() now log a
'[stream] cancel requested' line with the trigger reason
(composer-stop / slash-stop / slash-interrupt / busy-interrupt /
sidebar-stop). Passive UI lifecycle events (session switch, tab hide, page
unload) already tear down only the local SSE transport via
closeLiveStream() and never call /api/chat/cancel — only explicit
Stop/interrupt paths interrupt the backend agent/tool run. This is
confirmed by test_clarify_pending_never_404s locking the handler shape.

Supersedes #5343 (which handled only the profile-switch sub-case and kept
the broad message-scrape). Adds tests/test_issue5345_*.py (9 tests).

Co-authored-by: claw-io <claw-io@users.noreply.github.com>
Co-authored-by: ruizanthony <ruizanthony@users.noreply.github.com>
2026-07-01 17:17:11 +00:00
nesquena-hermes 3d780836d3 stage #5170 2026-07-01 06:54:07 +00:00
Paladin173 d8f0f8f97f Fix composer control reorder rebase collision
Reapply footer control ordering on current upstream/master while preserving the required situational chip renderer. Persist composer_control_order with backend validation, keep the settings descriptions reorder-aware, and make primary/situational chip renderers participate in same-group drag ordering.

Verified with: node --check static/boot.js; node --check static/panels.js; git diff --check; ./scripts/test.sh tests/test_issue4598_composer_control_visibility.py
2026-06-30 21:11:54 -05:00
nesquena-hermes 2ec7df8a27 rebase #5170 onto current master (union-resolved busy-mode boot conflicts: keep persisted pref on settings-load-fail + preserve placeholder-hint/showBusyPlaceholderHint) 2026-07-01 01:40:53 +00:00
Frank SongandPaperclip af7da472d2 Merge origin/master into PR #5271
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-01 07:27:27 +08:00
nesquena-hermes 373c3e4b63 Merge #4969 into stage 2026-06-30 23:07:53 +00:00
Frank SongandPaperclip fdeb37bbe2 Merge origin/master into PR #5271
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-01 07:02:01 +08:00
nesquena-hermes 338060e910 Merge #5287 into stage-b1 2026-06-30 22:45:08 +00:00
Frank SongandPaperclip 52f5d15214 Merge origin/master into PR #5271
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-01 06:42:56 +08:00
Frank SongandPaperclip f874f4027c Merge origin/master into PR #5271
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-01 06:22:02 +08:00
nesquena-hermes 3e84d7bf11 Merge #4738 (savagebread neon skins + skin-persistence fix) into stage 2026-06-30 22:21:28 +00:00
nesquena-hermes 78ca4d43a1 Merge #5005 (futureworld678 Shift+Enter) into stage 2026-06-30 21:51:42 +00:00
Frank SongandPaperclip 4da83ada63 Merge origin/master into PR #5271
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-01 05:42:12 +08:00
nesquena-hermes ee7f6b584d fix(#5002): hydrate _projectQuickCreate at boot + rebuild sidebar on toggle change
Codex gate findings: (1) the opt-in flag was only set when Settings opened, so an
enabled setting didn't take effect on a fresh load — now hydrated from /api/settings
at boot (mirrors _largeTextPasteAsAttachment, default-false); (2) toggling the
checkbox now rebuilds the sidebar so the + buttons appear/disappear immediately.
2026-06-30 21:06:35 +00:00
Frank SongandPaperclip bb95539918 Merge origin/master into PR #5271
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-01 05:02:19 +08:00
nesquena-hermes ba401d3784 Merge #5079 (OpenAI TTS + SSRF hardening) into stage 2026-06-30 20:39:04 +00:00
santastabber 4b33458c4b fix(pwa): defer new-chat model hydration 2026-07-01 05:29:34 +10:00
Rod Boev 6b9686d5ae fix(#4961): preserve authenticated drafts without starting a session 2026-06-30 14:31:28 -04:00
savage_bread 2bd1f68805 Add Neon Soft and Neon Paint skins
- Neon Soft: lighter dark-mode surfaces with the same neon purple/cyan palette
  as the existing Neon skin.
- Neon Paint: cyberpunk-themed skin with hot pink + electric cyan + warm amber
  on a deep purple-gray background.

Both skins include full light and dark mode variable sets plus component-level
overrides (selection, titlebar, sidebar, inputs, buttons, scrollbars, tool
cards, tree nodes, session badges, diff blocks, and links). Skin registration
is updated in boot.js and the inline skin whitelist in index.html
2026-06-30 18:28:33 +00:00
Lester b1c11939aa Add Shift+Enter send key option 2026-06-30 18:04:29 +00:00
Frank SongandPaperclip 2ffb8ab470 Merge origin/master into PR #5271
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-01 01:42:44 +08:00
nesquena-hermes 121059b384 Merge #5161 (pr/5144-busy-composer-placeholder-hint) into stage-w5 2026-06-30 17:25:03 +00:00
Frank SongandPaperclip 8a556df032 Merge origin/master into PR #5271
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-01 01:02:18 +08:00
Frank Song 70b019f710 feat: let extension skins declare base scheme 2026-06-30 18:17:40 +08:00
Stacey2911 6b0d8eaadf fix(webui): make large text paste-as-attachment toggleable via settings
Add a user setting 'large_text_paste_as_attachment' (default: on) that lets
users paste large text inline instead of having it auto-converted to .md file
attachments. The toggle lives in Control Center → Chat tab and persists via
settings.json, following the established upstream pattern for boolean settings.
2026-06-30 14:25:53 +10:00
Rod Boev db5d406d88 fix(boot): restore voice-mode runtime prefs during prefill rework (#4961) 2026-06-29 17:36:51 -04:00
nesquena-hermes 0aea4c7e18 harden(#5176): clamp configurable voice silence timeout (maintainer hardening)
Apply the warm-review polish: parseInt radix 10, fall back to 1800 for
missing/NaN/non-positive values, and floor at 200ms so a mistyped tiny/negative
hermes-voice-silence-ms can't make the recognizer auto-send instantly. De-brittle
the source-string test to assert behavior (reads key / 1800 fallback / guards
non-positive) instead of one literal expression. Default behavior unchanged.
2026-06-29 20:21:22 +00:00
Sean 5bfa9a8ec0 feat(voice): make silence timeout and continuous recognition configurable via localStorage
The voice-mode loop hardcoded SILENCE_MS=1800 and
_recognition.continuous=false, giving users no way to tune the
auto-send timing or keep the mic open across natural pauses.

Changes:
- SILENCE_MS now reads from localStorage key 'hermes-voice-silence-ms'
  with 1800ms fallback — users set it from the dev console or a future
  settings toggle
- _recognition.continuous reads from 'hermes-voice-continuous' (default
  false) — set to 'true' to keep the mic open across pauses within an
  utterance, with the silence timer remaining the sole send decision

New test file (tests/test_issue4761_voice_mode_config.py, 8 tests)
pins both localStorage reads and verifies the surrounding voice-mode
infrastructure is intact.

Refs #4761
2026-06-29 20:19:40 +00:00
Frank Song 28c2c37c1c Fix new chat empty-session reuse guard 2026-06-29 18:38:25 +00:00
Rod Boev 30fd052efa fix(boot): preserve native Ctrl+K text editing during URL prefill rework (#4961) 2026-06-29 14:29:47 -04:00
nankingjing 0a94e55384 fix(ux): skip Ctrl+K new-chat shortcut in text inputs (#5099)
Let Emacs-adjacent users keep kill-line in the composer and other
editable fields while preserving global Cmd/Ctrl+K elsewhere.

Fixes #5099
2026-06-29 17:36:47 +00:00
Rod Boev faed296819 fix(#4961): keep URL boot prefill draft-only 2026-06-29 10:03:36 -04:00
Rod Boev 71cd69cfbf fix(#4961): honor send query prefill flag 2026-06-29 09:52:43 -04:00
Rod Boev ac5a849301 feat(#4961): support URL query prefill for browser search 2026-06-29 09:52:43 -04:00
nesquena-hermes 16e280804b Merge branch 'master' into fix/4657-fit-engine-rebased 2026-06-28 19:24:13 -07:00
nesquena-hermesandPaladin173 628d378857 chore(composer): drop #4657's quota-chip default-flip — keep just the fit-engine
Per maintainer decision: #4657 bundled a scope-creep change flipping
show_quota_chip default off->on plus a migration force-resetting stored false->true
(stomping explicit opt-outs). Master deliberately ships quota-chip default-off.
Reverted config.py + the quota-chip wiring in panels.js/boot.js + the quota test
to master, keeping ONLY the footer fit-by-overflow engine. The mobile quota-action
selector addition (hide_composer_quota_chip selectors) is kept (fit-related, not a
default change).

Co-authored-by: Paladin173 <Paladin173@users.noreply.github.com>
2026-06-29 01:38:34 +00:00
claw-ioandnesquena-hermes ec0ef4a0d3 fix(profiles): show active profile workspace on blank new-chat (#5169)
Expose a profile-scoped default_workspace on GET /api/profile/active (resolved
by the profile-aware get_last_workspace(), fail-open) and hydrate boot.js from
it, overriding the global GET /api/settings value. GET /api/settings stays
global-only so the settings UI can't clobber the shared default. Combines
claw-io's #5173 (primary) with the fail-open backend hardening + regression test
from #5171.

Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
2026-06-29 01:25:58 +00:00
nesquena-hermes c6370235be feat(composer): fit footer controls by overflow (#4657, rebased onto master)
Rebased Paladin173's #4657 onto current master. The per-control visibility
toggles half already shipped via his own #4598, so this rebase carries only the
net-new JS fit-by-overflow engine (_fitComposerFooter: ResizeObserver +
MutationObserver measure .composer-left overflow, step full -> cf-icons ->
cf-burger). Co-authored-by: Paladin173 <Paladin173@users.noreply.github.com>
2026-06-29 01:19:02 +00:00
Rod Boev 9a1f27a48b feat(settings): add opt-in busy placeholder hint (#5144) 2026-06-28 18:47:42 -04:00
Rod Boev 7702acfca6 fix(tts): close rework blockers for OpenAI TTS (#4982) 2026-06-28 09:55:46 -04:00
Rod Boev 21e229cb0f feat(#4982): support OpenAI-compatible TTS backend in WebUI (#4982) 2026-06-28 09:50:51 -04:00
nesquena-hermesandksj5131513-ops 7ec7b9c942 fix(mobile): PWA reflow guard + avoid redundant same-session refresh (salvage of #3976)
Salvages PR #3976 by ksj5131513-ops, ported fresh onto current master and
re-applied on top of #4195's external-refresh gate (the original PR predated
#3916/#4195's rework of refreshActiveSessionIfExternallyUpdated).

Two parts:

1. Avoid an unconditional same-session force reload the moment streaming
   settles. _scheduleActiveSessionIdleReload previously called
   loadSession(force:true) on every idle reconcile, which on mobile PWA caused
   a visible end-of-turn flash and could briefly restore the pane with stale
   layout geometry. It now reconciles against server metadata first via
   refreshActiveSessionIfExternallyUpdated('idle-reconcile', {ignoreStreamJustFinished:true})
   — reloading ONLY when the message count actually changed — and falls back to
   the original forced reload only if the metadata probe itself fails.

   This COEXISTS with #4195: the poll-only external-session early-return
   (if((reason||'poll')==='poll' && !_isExternalSession(S.session)) return) is
   left untouched. The idle path uses a non-'poll' reason so it never trips that
   gate, and opts (read via arguments[1], same pattern as loadSession) only
   bypasses the post-stream cooldown, not the external gate. The function keeps
   its (reason) signature and now returns a status string
   ('skipped'/'unchanged'/'reloaded'/'failed') for callers that want it.

2. Mobile viewport reflow guard for PWA visualViewport changes. When the
   on-screen keyboard / browser chrome shows or hides, the phone layout can stay
   painted against stale geometry. _forceMobileViewportReflow() toggles a
   one-frame html.viewport-reflow class (cheap GPU-promotion transform) to force
   a repaint, then resyncs the workspace panel + sidebar aria. Wired to
   visualViewport resize/scroll (debounced) where available, falling back to the
   window resize handler otherwise. Adds safe-area-inset padding to topbar,
   messages-inner, and composer-wrap on phones. Uses overflow-x:clip (NOT hidden)
   on .layout per the #4856 lesson to avoid coercing overflow-y into a scroller.

Co-authored-by: ksj5131513-ops <ksj5131513-ops@users.noreply.github.com>
2026-06-28 09:02:10 +00:00
Paladin173 aceb5411ee Clear archived saved session on boot restore 2026-06-28 06:51:36 +00:00
Paladin173 a51e58efc0 fix: skip archived saved session on boot 2026-06-28 06:51:36 +00:00