fix: Claude view parsing/scroll, ngrok warning bypass, reliable send
Claude parsed view:
- Tables now require a real column-border row (┌┬┐/├┼┤); welcome/notice boxes
(rounded ╭╰, single │ column) no longer render as garbled tables
- Tighten code detection so log lines / prose with parentheses aren't boxed as code
- Status bar no longer eats prose/question lines containing a "·"; pull ctx%/model
from combined statuslines
- Renderer hashes full block content, so streaming updates re-render instead of
freezing ("parsed, then not")
- Left-normalize TUI lines shoved to the right on wide panes (text/response/tool
output only; code/tables/diffs untouched)
Scroll history:
- Stop the loader flash-loop once scrollback is exhausted (noMoreHistory guard)
- Keep the reading position on load; older content is revealed by scrolling up
further (preserve-position)
ngrok:
- No-cache pass-through service worker adds the ngrok-skip-browser-warning header
to navigations so the free-tier interstitial is skipped on revisits
(replaces the old caching SW; sw-unregister removed)
Send:
- Server awaits send_text before pressing Enter so the submit can't race the
bracketed paste (fixes "had to press Enter twice")
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
main
parent
5e473b9ff6
commit
d34359c9ab
@ -0,0 +1,11 @@
|
||||
// Register the pass-through service worker (see /sw.js) so ngrok's free-tier browser
|
||||
// interstitial is skipped on revisits / PWA launches. Registering /sw.js also UPDATES
|
||||
// (and thereby replaces) any previously-installed caching service worker, whose
|
||||
// activate handler then clears the old caches.
|
||||
//
|
||||
// Caveat: the very first visit on a new browser still shows the ngrok page once — a
|
||||
// service worker cannot exist before that initial load. ngrok's own cookie suppresses
|
||||
// it after one "Visit Site" click; the SW then keeps it gone across sessions.
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('/sw.js').catch(() => {});
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
// Unregister any cached service workers so CSS/JS changes are always fresh
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.getRegistrations().then(regs => {
|
||||
regs.forEach(r => r.unregister());
|
||||
});
|
||||
}
|
||||
@ -1,53 +1,40 @@
|
||||
const CACHE_NAME = 'cmux-remote-v3';
|
||||
const STATIC_ASSETS = [
|
||||
'/',
|
||||
'/index.html',
|
||||
'/login.html',
|
||||
'/css/main.css',
|
||||
'/css/terminal.css',
|
||||
'/css/keyboard.css',
|
||||
'/css/sidebar.css',
|
||||
'/css/themes.css',
|
||||
'/js/app.js',
|
||||
'/js/auth.js',
|
||||
'/js/terminal-view.js',
|
||||
'/js/websocket-client.js',
|
||||
'/js/virtual-keyboard.js',
|
||||
'/js/sidebar.js',
|
||||
'/js/gestures.js',
|
||||
'/js/theme.js',
|
||||
];
|
||||
|
||||
self.addEventListener('install', (event) => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS))
|
||||
);
|
||||
self.skipWaiting();
|
||||
});
|
||||
// Pass-through service worker — does NOT cache responses (response caching is what
|
||||
// made us disable the previous SW; assets must always come fresh from the network).
|
||||
//
|
||||
// Its only job: add the `ngrok-skip-browser-warning` header to top-level navigation
|
||||
// requests, so ngrok's free-tier browser interstitial ("You are about to visit …")
|
||||
// doesn't appear on repeat visits / PWA launches. ngrok skips the warning whenever
|
||||
// that header is present (verified). The very first visit on a fresh browser still
|
||||
// shows it once — the SW can't exist before that initial load — but ngrok's own
|
||||
// cookie also suppresses it after a single "Visit Site" click.
|
||||
self.addEventListener('install', () => self.skipWaiting());
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(
|
||||
caches.keys().then((keys) =>
|
||||
Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)))
|
||||
)
|
||||
);
|
||||
self.clients.claim();
|
||||
event.waitUntil((async () => {
|
||||
// Drop any caches left behind by the old caching SW so nothing serves stale.
|
||||
const keys = await caches.keys();
|
||||
await Promise.all(keys.map((k) => caches.delete(k)));
|
||||
await self.clients.claim();
|
||||
})());
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', (event) => {
|
||||
// Skip API and WebSocket requests
|
||||
const url = new URL(event.request.url);
|
||||
if (url.pathname.startsWith('/api/') || event.request.headers.get('upgrade') === 'websocket') {
|
||||
return;
|
||||
}
|
||||
const req = event.request;
|
||||
// Only top-level navigations trigger ngrok's interstitial. Leave everything else
|
||||
// (assets, /api, POST, WebSocket upgrades) completely untouched — no rewrite and
|
||||
// no caching — so behaviour is otherwise identical to having no SW at all.
|
||||
if (req.mode !== 'navigate') return;
|
||||
|
||||
const headers = new Headers(req.headers);
|
||||
headers.set('ngrok-skip-browser-warning', 'true');
|
||||
event.respondWith(
|
||||
fetch(event.request)
|
||||
.then((response) => {
|
||||
const clone = response.clone();
|
||||
caches.open(CACHE_NAME).then((cache) => cache.put(event.request, clone));
|
||||
return response;
|
||||
fetch(
|
||||
new Request(req.url, {
|
||||
method: 'GET',
|
||||
headers,
|
||||
credentials: req.credentials,
|
||||
redirect: 'manual',
|
||||
})
|
||||
.catch(() => caches.match(event.request))
|
||||
).catch(() => fetch(req))
|
||||
);
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue