feat: shell terminal, virtual keyboard fixes, settings font slider
- Add shell terminal: new tmux window spawned at project cwd, opened from the Terminal icon in the ChatView header; ShellTerminalView lets you navigate back to chat or jump to the Claude tmux terminal mode - Fix system keyboard appearing on first terminal launch: ghostty-web calls focus() on a contenteditable div inside open(), triggering iOS keyboard before we can suppress it; patch HTMLElement.prototype.focus as a no-op for the synchronous duration of terminal.open() - Add keyboard font-size slider in Settings - Fix virtual keyboard button corner radius and depth scaling with key height - Fix keybar gap/padding proportionality via heightScale factor - Cap keybar font size so it does not grow when bar height is increased Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import { useEffect, useRef, useImperativeHandle, forwardRef } from 'react';
|
||||
import { STORAGE } from '../lib/storage-keys';
|
||||
import { WebTerminal } from '../lib/terminal/WebTerminal';
|
||||
import type { TerminalConfig } from '../lib/terminal/terminal-config';
|
||||
|
||||
interface TerminalViewProps {
|
||||
sessionId: string;
|
||||
adapter?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export interface TerminalViewHandle {
|
||||
closeKeyboard(): void;
|
||||
}
|
||||
|
||||
function buildTerminalWsUrl(sessionId: string, adapter?: string): string {
|
||||
const proto = location.protocol === 'https:' ? 'wss' : 'ws';
|
||||
const token = localStorage.getItem(STORAGE.TOKEN) ?? '';
|
||||
const adapterParam = adapter ? `&adapter=${encodeURIComponent(adapter)}` : '';
|
||||
return `${proto}://${location.host}/terminal/${encodeURIComponent(sessionId)}?token=${encodeURIComponent(token)}${adapterParam}`;
|
||||
}
|
||||
|
||||
const TERMINAL_CONFIG: TerminalConfig = {
|
||||
fontSize: 14,
|
||||
};
|
||||
|
||||
export const TerminalView = forwardRef<TerminalViewHandle, TerminalViewProps>(
|
||||
function TerminalView({ sessionId, adapter, style }, ref) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const termRef = useRef<WebTerminal | null>(null);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
closeKeyboard() {
|
||||
termRef.current?.closeKeyboard();
|
||||
},
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
const el = containerRef.current;
|
||||
if (!el) return;
|
||||
|
||||
const wsUrl = buildTerminalWsUrl(sessionId, adapter);
|
||||
let cancelled = false;
|
||||
|
||||
WebTerminal.create(el, wsUrl, TERMINAL_CONFIG).then((t) => {
|
||||
if (cancelled) {
|
||||
t.dispose();
|
||||
return;
|
||||
}
|
||||
termRef.current = t;
|
||||
}).catch((err) => {
|
||||
console.error('[TerminalView] Failed to create terminal:', err);
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
termRef.current?.dispose();
|
||||
termRef.current = null;
|
||||
};
|
||||
}, [sessionId, adapter]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: '#000',
|
||||
overflow: 'hidden',
|
||||
...style,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user