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';
|
// Pass-through service worker — does NOT cache responses (response caching is what
|
||||||
const STATIC_ASSETS = [
|
// made us disable the previous SW; assets must always come fresh from the network).
|
||||||
'/',
|
//
|
||||||
'/index.html',
|
// Its only job: add the `ngrok-skip-browser-warning` header to top-level navigation
|
||||||
'/login.html',
|
// requests, so ngrok's free-tier browser interstitial ("You are about to visit …")
|
||||||
'/css/main.css',
|
// doesn't appear on repeat visits / PWA launches. ngrok skips the warning whenever
|
||||||
'/css/terminal.css',
|
// that header is present (verified). The very first visit on a fresh browser still
|
||||||
'/css/keyboard.css',
|
// shows it once — the SW can't exist before that initial load — but ngrok's own
|
||||||
'/css/sidebar.css',
|
// cookie also suppresses it after a single "Visit Site" click.
|
||||||
'/css/themes.css',
|
self.addEventListener('install', () => self.skipWaiting());
|
||||||
'/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();
|
|
||||||
});
|
|
||||||
|
|
||||||
self.addEventListener('activate', (event) => {
|
self.addEventListener('activate', (event) => {
|
||||||
event.waitUntil(
|
event.waitUntil((async () => {
|
||||||
caches.keys().then((keys) =>
|
// Drop any caches left behind by the old caching SW so nothing serves stale.
|
||||||
Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)))
|
const keys = await caches.keys();
|
||||||
)
|
await Promise.all(keys.map((k) => caches.delete(k)));
|
||||||
);
|
await self.clients.claim();
|
||||||
self.clients.claim();
|
})());
|
||||||
});
|
});
|
||||||
|
|
||||||
self.addEventListener('fetch', (event) => {
|
self.addEventListener('fetch', (event) => {
|
||||||
// Skip API and WebSocket requests
|
const req = event.request;
|
||||||
const url = new URL(event.request.url);
|
// Only top-level navigations trigger ngrok's interstitial. Leave everything else
|
||||||
if (url.pathname.startsWith('/api/') || event.request.headers.get('upgrade') === 'websocket') {
|
// (assets, /api, POST, WebSocket upgrades) completely untouched — no rewrite and
|
||||||
return;
|
// 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(
|
event.respondWith(
|
||||||
fetch(event.request)
|
fetch(
|
||||||
.then((response) => {
|
new Request(req.url, {
|
||||||
const clone = response.clone();
|
method: 'GET',
|
||||||
caches.open(CACHE_NAME).then((cache) => cache.put(event.request, clone));
|
headers,
|
||||||
return response;
|
credentials: req.credentials,
|
||||||
|
redirect: 'manual',
|
||||||
})
|
})
|
||||||
.catch(() => caches.match(event.request))
|
).catch(() => fetch(req))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue