fix: container mount permissions, terminal shift, ESC capture
Bug 1 — in-container repo mounting: - docker-compose.yml: added /data/working-copies:/data/working-copies mount to API container so workspace dirs are visible on host filesystem - Dockerfile: create /data/working-copies dir in image Bug 2 — /home/user not writable: - workspace_manager.py: chmod 777 workspace dirs + 666 files after clone and after sync, so any container user can write - manifest_compiler.py: explicit mkdir + chown + chmod 755 for home dir in generated Dockerfile Bug 3 — terminal text shifts left on typing: - terminal.tsx: removed manual term.refresh() after fit (caused reflow) - Track lastSentCols/lastSentRows and only send resize when dimensions actually changed, preventing resize feedback loops Bug 4 — ESC key captured by terminal: - terminal.tsx: attachCustomKeyEventHandler allows ESC to propagate to browser when not in alternate buffer (vim/tmux), so modals/navigation work; ESC still sent to PTY when in vim/tmux alternate screen Quality gates: ruff clean, tsc --noEmit clean, pytest workspaces (9 passed)
This commit is contained in:
@@ -71,16 +71,12 @@ export function StartToolFAB() {
|
||||
</p>
|
||||
) : !selectedWorkspace ? (
|
||||
<div className="form-group">
|
||||
<label htmlFor="fab-workspace-select">
|
||||
Select a workspace
|
||||
</label>
|
||||
<label htmlFor="fab-workspace-select">Select a workspace</label>
|
||||
<select
|
||||
id="fab-workspace-select"
|
||||
value=""
|
||||
onChange={(e) => {
|
||||
const ws = workspaces.find(
|
||||
(w) => w.id === e.target.value,
|
||||
);
|
||||
const ws = workspaces.find((w) => w.id === e.target.value);
|
||||
if (ws) setSelectedWorkspace(ws);
|
||||
}}
|
||||
>
|
||||
@@ -97,8 +93,7 @@ export function StartToolFAB() {
|
||||
<div className="tool-starter-header">
|
||||
<h4>
|
||||
{selectedWorkspace.project_name} /{" "}
|
||||
{selectedWorkspace.repo_name} /{" "}
|
||||
{selectedWorkspace.name}
|
||||
{selectedWorkspace.repo_name} / {selectedWorkspace.name}
|
||||
</h4>
|
||||
<button
|
||||
className="ghost-button small"
|
||||
|
||||
@@ -285,6 +285,8 @@ export const TerminalComponent = React.forwardRef<TerminalRef, TerminalProps>(
|
||||
const container = terminalRef.current;
|
||||
|
||||
// Define fitTerminal before connectWebSocket so it's available in onmessage
|
||||
let lastSentCols = 0;
|
||||
let lastSentRows = 0;
|
||||
const fitTerminal = () => {
|
||||
if (!fitAddonRef.current || !termRef.current) return;
|
||||
try {
|
||||
@@ -294,23 +296,35 @@ export const TerminalComponent = React.forwardRef<TerminalRef, TerminalProps>(
|
||||
return;
|
||||
}
|
||||
const { cols, rows } = termRef.current;
|
||||
// Force refresh if dimensions are valid
|
||||
if (cols > 0 && rows > 0) {
|
||||
try {
|
||||
termRef.current.refresh(0, rows - 1);
|
||||
} catch {
|
||||
// Ignore refresh errors
|
||||
// Only send resize when dimensions actually changed
|
||||
if (
|
||||
cols > 0 &&
|
||||
rows > 0 &&
|
||||
(cols !== lastSentCols || rows !== lastSentRows)
|
||||
) {
|
||||
lastSentCols = cols;
|
||||
lastSentRows = rows;
|
||||
const currentWs = wsRef.current;
|
||||
if (currentWs?.readyState === WebSocket.OPEN) {
|
||||
currentWs.send(JSON.stringify({ type: "resize", cols, rows }));
|
||||
}
|
||||
}
|
||||
const currentWs = wsRef.current;
|
||||
if (currentWs?.readyState === WebSocket.OPEN && cols > 0 && rows > 0) {
|
||||
currentWs.send(JSON.stringify({ type: "resize", cols, rows }));
|
||||
}
|
||||
};
|
||||
|
||||
// Open xterm first (must happen before fit)
|
||||
term.open(container);
|
||||
term.focus();
|
||||
|
||||
// Allow ESC to propagate to browser when not in alternate buffer (vim/tmux)
|
||||
term.attachCustomKeyEventHandler((e) => {
|
||||
if (e.key === "Escape") {
|
||||
const isAlternate =
|
||||
term.buffer.active.type === "alternate";
|
||||
return isAlternate; // true = xterm handles it, false = browser handles it
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
const ws = connectWebSocket();
|
||||
|
||||
// Mobile touch scroll.
|
||||
|
||||
Reference in New Issue
Block a user