The main webview retained `shell:allow-open` even though the frontend never
uses the shell plugin directly. File-manager opening already flows through the
`open_game_files` command, which checks catalog membership and containment.
Remove only the frontend permission. The Rust shell plugin and dependency stay
available for the checked backend open operation and bundled unrar sidecar.
Test Plan:
- `just build-fixture` -- passed, including the frontend build and Tauri
capability validation.
- `git diff --cached --check` -- passed.
Captures stdout, stderr, exit status and start/finish timestamps for every
unrar sidecar invocation and exposes them through a dedicated "Unpack Logs"
window. Triggered by the need to debug why a particular game's archive
failed to extract -- previously the only artifact of a failed unpack was a
log line in the Tauri process stdout, which is awkward to inspect on an
end-user machine.
Implementation:
* `LanSpreadState` gains an in-memory ring buffer (`unpack_logs`) capped at
`MAX_UNPACK_LOGS` (100). The previous monolithic `do_unrar` is split into
`prepare_unrar_paths` and `run_unrar_sidecar` so every failure path (mkdir
failure, canonicalize failure, non-UTF-8 destination, sidecar spawn error,
non-zero exit) records an `UnpackLogEntry` before bailing.
* A `get_unpack_logs` Tauri command returns the current snapshot; an
`unpack-logs-updated` event is emitted after every write so the viewer can
refresh without polling.
* The React `App` component now routes on `?view=unpack-logs` and renders a
dedicated `UnpackLogsWindow`. The main window opens the viewer via
`WebviewWindow` with label `unpack-logs`; an existing window is focused
instead of being recreated.
Capability scoping: the new window is given its own capability file
(`capabilities/unpack-logs.json`) granting only `core:default`. The main
capability is unchanged in window scope and only gains the two permissions
the main window itself needs (`core:window:allow-set-focus` to focus an
existing log window, `core:webview:allow-create-webview-window` to spawn
it). Splitting the capability keeps the log window from inheriting
`shell:allow-open`, `dialog:default` and `store:default`, which it has no
reason to use.
Known limitations (intentionally out of scope here):
* Logs are process-local; they vanish on app restart. Persistence can be
added later if it turns out users want to inspect failures across runs.
* Entries are presented as a flat chronological list identified by archive
path. No per-game grouping or filtering yet -- the archive filename is
usually enough to identify the game in practice.
* The `unpack-logs-updated` event carries no payload; the viewer re-fetches
the full snapshot on every notification. Acceptable given the 100-entry
cap, but a payload-bearing event would be cheaper if the cap grows.
Test plan:
* `just clippy` and `just build` are clean.
* Manual: start the GUI, point it at a games directory containing at least
one peer-hosted game, trigger an install, then click "Unpack Logs". The
window should show one entry per unrar invocation with stdout, stderr,
status code and timestamps; stderr/error lines render in the warning
color. Triggering further unpacks should update the open window live via
the `unpack-logs-updated` event without manual refresh.
* Negative path: rename or remove the archive between handshake and
extraction to force a canonicalize failure; confirm a failed entry with
the corresponding stderr appears in the viewer.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>