aboutsummaryrefslogtreecommitdiffstats
path: root/CLAUDE.md
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-01 23:36:17 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-01 23:36:17 +0200
commitd6713a4c7b3f94a3b63e0c0f78e7939fa7eae4e8 (patch)
treef0c00979232c1fd4c5bad0ddd726fbbde15eff1f /CLAUDE.md
parent799d87999c8324564dce5159191532e008dd93d2 (diff)
parent32855a95e11032302f8d24036f6f2dd44b829368 (diff)
downloadmeshbay-d6713a4c7b3f94a3b63e0c0f78e7939fa7eae4e8.tar.gz
Merge branch 'fix/chat-scroll-up'
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W8oRqEHhnKUr1NfmTVdcyL
Diffstat (limited to 'CLAUDE.md')
-rw-r--r--CLAUDE.md17
1 files changed, 17 insertions, 0 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
index 8f55a60..5520391 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -436,6 +436,22 @@ anything that assumes one key per person.
`fit()` lifted out of `app.js` — the sizing code is never reimplemented in a
test, or the test outlives the code it was written for
+- **A handler bound to the event its own writes produce.** The chat panel's
+ `fit()` set a height, read `documentElement.scrollHeight` back and subtracted
+ the overflow — so the document alternately did and did not overflow the
+ window, the page scrollbar appeared and vanished with it, and `visualViewport`
+ fired `resize` at every pass. `fit()` listens to that event: it re-entered
+ itself ~120 times a second for the life of the panel (measured: 240 firings in
+ 2 s on an idle page, against 2 for a bare document). Each pass re-pinned the
+ list to the bottom, which undid every attempt to scroll up **inside the frame
+ it happened in** — before the `scroll` event that would have recorded it was
+ delivered, so `atBottomRef` never went false, no jump button appeared, and the
+ older messages were unreachable. Every pin in that file reads as correctly
+ guarded; the reader simply never got to stop being at the bottom. Mutate-then-
+ measure is a loop wherever layout can raise the event you are handling: learn
+ the correction once and write nothing in the steady state. And a scroll
+ position that must survive a gesture has to be released **by the gesture**
+ (`wheel`/`touchmove`/`pointerdown`), never by the `scroll` event alone
- **A refusal that never rejects.** Denying Chromium's `fullscreen` permission
does not make `requestFullscreen()` throw — the promise never settles. The
deny-everything handler was written from a true sentence ("nothing here needs
@@ -764,6 +780,7 @@ SFR residential Fedora 44 → meshbay.org OVH VPS:
| Seeking (node) | `webrtc_server.py` | `start` on `stream_req`; `-ss` **before** `-i` (index seek, not decode-and-discard), clamped away from the end, echoed in `stream_init` |
| Resume position | `static/video-player.js` | `readResumePosition` / `writeResumePosition` — localStorage, per file, per browser. No protocol, and nothing new learns what you watch |
| Layout, measured | `tests/harness/layout_probe.py` | Renders `style.css` in Chrome at any width and returns bounding boxes. Use it for layout, not `test_layout_responsive.py`, which only pins CSS values |
+| Chat scrolling, measured | `tests/harness/chat_scroll_probe.py` | Mounts the real `ChatPanel` in Chrome and reads a conversation back. Answers "can the reader scroll up" and "does the panel resize itself"; `test_chat_scroll_bottom.py` only pins the source's shape |
| Session renewal (browser) | `static/hub-client.js` | `refreshAccessToken` / `ensureFreshToken` — one writer (`setAuth`), one in-flight renewal, rotated refresh token stored. `hubFetch` renews on 401 and replays. Moved out of app.js in the 2026-08-23 split |
| Token lifetimes (hub) | `meshbay_hub.config` | `[jwt] access_token_ttl` 4 h, `refresh_token_ttl` 30 days. **Production sets both in `~/.config/meshbay/hub.toml`** — changing the code default alone does nothing there |