Compare commits

..

4 Commits

Author SHA1 Message Date
jknapp ccdfc52dce Merge pull request 'docs: document terminal layout & StatusBar control gotchas' (#11) from docs/terminal-layout-gotchas into main 2026-07-19 16:26:02 +00:00
shadow-test 2e661979ea docs: document terminal layout & StatusBar control gotchas
Capture the non-obvious implementation gotchas from PR #7
(terminal-layout-statusbar) in TECHNICAL.md: wrapper-vs-host xterm
padding, global StatusBar controls, recordingSessionIdRef transcript
pinning, active-only Jump-to-Current state, and the Zustand
object-merge rule for publishing action callbacks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 09:12:54 -07:00
jknapp 59d89bcd1b Merge pull request 'Rename backup archive root so extraction dir mode isn't clobbered' (#10) from fix/backup-root-dir-mode into main
Build App / compute-version (push) Successful in 4s
Build App / build-macos (push) Successful in 2m21s
Build App / build-windows (push) Successful in 4m35s
Build App / build-linux (push) Successful in 5m1s
Build App / create-tag (push) Successful in 4s
Build App / sync-to-github (push) Successful in 10s
2026-07-01 13:35:02 +00:00
shadow-test 26adccce5b Rename backup archive root so extraction dir mode isn't clobbered
Build App / compute-version (pull_request) Successful in 4s
Build App / build-macos (pull_request) Successful in 2m14s
Build App / build-windows (pull_request) Successful in 4m26s
Build App / build-linux (pull_request) Successful in 5m0s
Build App / create-tag (pull_request) Has been skipped
Build App / sync-to-github (pull_request) Has been skipped
The transform used `s,^\./,workspace/,`, which rewrites the workspace
*contents* (`./foo` -> `workspace/foo`) but leaves tar's root member as a
bare `./`. That `./` entry carries the source root's mode/mtime, and on
extraction tar stamps them onto the extraction directory itself.

Match the leading `.` instead (`s,^\.,workspace,`) so the root member is
renamed `./` -> `workspace`, giving the archive a proper `workspace/`
directory entry and no bare `./`. The extraction directory is left
untouched. Contents, hidden files, excludes, symlink targets and the
`flags=rh` hardlink handling are unchanged.

Verified in-container: archive top level is exactly `workspace/` +
`home-claude/`, no `./` member, node_modules excluded, extraction into a
0755 dir leaves it 0755, workspace/.git preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 06:33:02 -07:00
2 changed files with 19 additions and 5 deletions
+10
View File
@@ -57,6 +57,16 @@ Tauri uses a Rust backend paired with a web-based frontend rendered by the OS-na
- **Web links addon** — `@xterm/addon-web-links` makes URLs in terminal output clickable. Combined with `tauri-plugin-opener`, clicked URLs open in the host browser — essential for the `claude login` OAuth flow where Claude prints an authentication URL that must be opened on the host. - **Web links addon** — `@xterm/addon-web-links` makes URLs in terminal output clickable. Combined with `tauri-plugin-opener`, clicked URLs open in the host browser — essential for the `claude login` OAuth flow where Claude prints an authentication URL that must be opened on the host.
- **Bidirectional data flow** — xterm.js exposes `term.onData()` for user keystrokes and `term.write()` for incoming data. This maps directly to our Tauri event-based streaming architecture. - **Bidirectional data flow** — xterm.js exposes `term.onData()` for user keystrokes and `term.write()` for incoming data. This maps directly to our Tauri event-based streaming architecture.
#### Terminal Layout & StatusBar Controls
Implementation gotchas for the terminal view and its global controls (merged in PR #7, `terminal-layout-statusbar`):
- **xterm padding lives on a wrapper, never the host.** FitAddon measures the same element that `term.open()` mounts into, so any padding on that host element makes the grid overhang and clip its rightmost column / bottom row. Padding must live on a **wrapper `div`**; the xterm host fills it with no padding of its own. Do not reintroduce padding on the host element in `TerminalView.tsx`.
- **STT mic and "Jump to Current" live in the global `StatusBar`, not per-terminal overlays.** There is a single `useSTT` instance in `App.tsx` bound to the active session. `Ctrl+Shift+M` routes through the Zustand store (`sttToggle`).
- **Recording is pinned to where it started.** The STT transcript targets `recordingSessionIdRef` (the session recording began in), **not** the live active session — switching tabs mid-recording must not misroute the transcript.
- **"Jump to Current" state is written only by the active terminal.** The active `TerminalView` surfaces `terminalAtBottom` and `scrollActiveToBottom` through the store; only the active terminal writes them, and they are cleared on its unmount.
- **Set store function values via object-merge, not the updater form** — `set({ fn: value })`, not `set(state => ...)` — when publishing action callbacks (like `scrollActiveToBottom`) into the Zustand store.
### bollard (Docker API) ### bollard (Docker API)
**Chosen over:** Shelling out to the `docker` CLI, dockerode (Node.js), docker-api (Python) **Chosen over:** Shelling out to the `docker` CLI, dockerode (Node.js), docker-api (Python)
+9 -5
View File
@@ -206,10 +206,14 @@ pub async fn download_container_backup(
// so secrets can't leak through the sanitization fallback. // so secrets can't leak through the sanitization fallback.
// The `--transform` nests the workspace under `workspace/` (parallel to // The `--transform` nests the workspace under `workspace/` (parallel to
// `home-claude/`) so an extracted archive has both clearly labeled instead // `home-claude/`) so an extracted archive has both clearly labeled instead
// of scattering the workspace files into the extraction dir. `flags=rh` // of scattering the workspace files into the extraction dir. Rewriting the
// rewrites regular member names AND hardlink target names (so an intra- // leading `.` (rather than `./`) also renames tar's root member from `./` to
// workspace hardlink pair still resolves on extract) while leaving symlink // `workspace`, so the archive carries a proper `workspace/` dir entry rather
// targets untouched (rewriting those would corrupt relative/absolute links). // than a bare `./` that would stamp the source root's mode/mtime onto the
// extraction directory. `flags=rh` rewrites regular member names AND
// hardlink target names (so an intra-workspace hardlink pair still resolves
// on extract) while leaving symlink targets untouched (rewriting those would
// corrupt relative/absolute links).
let script = r#"set -e let script = r#"set -e
STAGE=$(mktemp -d) STAGE=$(mktemp -d)
trap 'rm -rf "$STAGE"' EXIT trap 'rm -rf "$STAGE"' EXIT
@@ -227,7 +231,7 @@ if [ -d "$HOME/.claude" ]; then
fi fi
tar czf - --ignore-failed-read \ tar czf - --ignore-failed-read \
--exclude='*/node_modules' --exclude='*/target' \ --exclude='*/node_modules' --exclude='*/target' \
--transform='flags=rh;s,^\./,workspace/,' \ --transform='flags=rh;s,^\.,workspace,' \
-C "$TC_BACKUP_SRC" . \ -C "$TC_BACKUP_SRC" . \
-C "$STAGE" home-claude"#; -C "$STAGE" home-claude"#;