refactor: codebase audit cleanup — dead code, dedup, module splits
Multi-agent audit + aggressive cleanup across server/web/coder/booterm, delivered behind a DEFER discipline so none of the in-flight files were touched. Removes dead code/deps/columns, dedups server + coder helpers, and splits the oversized modules (tools.ts, opencode-server.ts, sentinel-summaries, turn.ts, TerminalPane.tsx) behind stable contracts. Adds 78 parity/unit tests (server 587, coder 323); fixes two latent bugs (ChatPane queue keys, FileViewerOverlay blank-line parity). Intended tag: v2.7.12-audit-cleanup. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
115
apps/web/src/components/panes/terminal/TerminalHotkeyBar.tsx
Normal file
115
apps/web/src/components/panes/terminal/TerminalHotkeyBar.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import { useCallback } from 'react';
|
||||
import { Maximize2 } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
// ============================================================
|
||||
// TerminalHotkeyBar — v1.10.8d port of boolab's TerminalHotkeyBar.jsx +
|
||||
// terminalHotkeysStore.js DEFAULT_BAR. The catalog is hardcoded inline (no
|
||||
// zustand store, no settings UI) — single-user homelab doesn't need either.
|
||||
// Add new entries by extending HOTKEY_BAR below.
|
||||
// ============================================================
|
||||
type Hotkey =
|
||||
| { id: string; label: string; bytes: string; sticky?: undefined }
|
||||
| { id: string; label: string; sticky: 'ctrl'; bytes?: undefined };
|
||||
|
||||
const HOTKEY_BAR: Hotkey[] = [
|
||||
{ id: 'esc', label: 'Esc', bytes: '\x1b' },
|
||||
{ id: 'shift-tab', label: '⇧Tab', bytes: '\x1b[Z' },
|
||||
{ id: 'tab', label: 'Tab', bytes: '\t' },
|
||||
{ id: 'ctrl', label: 'Ctrl', sticky: 'ctrl' },
|
||||
{ id: 'ctrl-c', label: 'Ctrl+C', bytes: '\x03' },
|
||||
{ id: 'arrow-up', label: '↑', bytes: '\x1b[A' },
|
||||
{ id: 'arrow-down', label: '↓', bytes: '\x1b[B' },
|
||||
{ id: 'arrow-left', label: '←', bytes: '\x1b[D' },
|
||||
{ id: 'arrow-right', label: '→', bytes: '\x1b[C' },
|
||||
];
|
||||
|
||||
interface TerminalHotkeyBarProps {
|
||||
ctrlArmed: boolean;
|
||||
onSendBytes: (bytes: string) => void;
|
||||
onArmCtrl: () => void;
|
||||
onFit: () => void;
|
||||
}
|
||||
|
||||
export function TerminalHotkeyBar({
|
||||
ctrlArmed,
|
||||
onSendBytes,
|
||||
onArmCtrl,
|
||||
onFit,
|
||||
}: TerminalHotkeyBarProps) {
|
||||
// Stop the touch from reaching the terminal pane below (which calls
|
||||
// preventDefault on touchmove to suppress page-scroll). Without this a
|
||||
// tap-and-drag on a hotkey button would also scroll the terminal buffer.
|
||||
const stopTouch = useCallback((e: React.TouchEvent) => e.stopPropagation(), []);
|
||||
const press = useCallback(
|
||||
(entry: Hotkey) => {
|
||||
if (entry.sticky === 'ctrl') {
|
||||
onArmCtrl();
|
||||
} else if (entry.bytes !== undefined) {
|
||||
onSendBytes(entry.bytes);
|
||||
}
|
||||
},
|
||||
[onArmCtrl, onSendBytes],
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
role="toolbar"
|
||||
aria-label="Terminal hotkeys"
|
||||
className="flex shrink-0 items-center gap-1 overflow-x-auto border-b border-border bg-muted/30 px-2 py-1"
|
||||
style={{
|
||||
scrollbarWidth: 'thin',
|
||||
WebkitOverflowScrolling: 'touch',
|
||||
// Suppress iOS native swipe-back gesture starting on the bar; pinch
|
||||
// and other multi-touch gestures still pass through.
|
||||
touchAction: 'pan-x',
|
||||
}}
|
||||
onTouchStart={stopTouch}
|
||||
onTouchMove={stopTouch}
|
||||
>
|
||||
{HOTKEY_BAR.map((entry) => {
|
||||
const isCtrl = entry.sticky === 'ctrl';
|
||||
const armed = isCtrl && ctrlArmed;
|
||||
return (
|
||||
<button
|
||||
key={entry.id}
|
||||
type="button"
|
||||
onClick={() => press(entry)}
|
||||
aria-pressed={isCtrl ? armed : undefined}
|
||||
aria-label={entry.label}
|
||||
className={cn(
|
||||
'shrink-0 rounded border px-2 py-0.5 text-xs font-mono transition-colors',
|
||||
armed
|
||||
? 'border-primary bg-primary text-primary-foreground'
|
||||
: 'border-border text-foreground hover:bg-muted',
|
||||
)}
|
||||
style={{
|
||||
minHeight: 28,
|
||||
minWidth: 36,
|
||||
WebkitTouchCallout: 'none',
|
||||
userSelect: 'none',
|
||||
}}
|
||||
>
|
||||
{entry.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
<button
|
||||
type="button"
|
||||
onClick={onFit}
|
||||
aria-label="Fit terminal"
|
||||
title="Fit terminal to container"
|
||||
className="shrink-0 inline-flex items-center justify-center rounded border border-border text-foreground hover:bg-muted"
|
||||
style={{
|
||||
minHeight: 28,
|
||||
minWidth: 36,
|
||||
paddingInline: 8,
|
||||
WebkitTouchCallout: 'none',
|
||||
userSelect: 'none',
|
||||
}}
|
||||
>
|
||||
<Maximize2 size={14} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user