Add Stop, Restart, and Force Rebuild menu items to Tsunami view (#2483)

Adds settings menu items to the Tsunami app view for stopping and
restarting the controller, with support for forcing rebuilds that bypass
the build cache.

### Changes

- **Added three menu items** to
`TsunamiViewModel.getSettingsMenuItems()`:
- "Stop WaveApp" - stops the running tsunami controller using
`ControllerStopCommand`
- "Restart WaveApp" - stops the controller, waits 300ms, then resyncs to
restart it (uses existing cache if available)
- "Restart WaveApp and Force Rebuild" - triggers
`ControllerResyncCommand` with `forcerestart: true` (bypasses build
cache per line 228 in `tsunamicontroller.go`)

- **Refactored controller resync logic** - extracted common code into
`doControllerResync()` helper with optional `triggerRestart` parameter
to handle both user-initiated restarts and automatic mount sync

- **Implemented proper restart behavior** - "Restart WaveApp" now
performs a stop → wait → resync sequence to ensure the controller
actually restarts instead of just resyncing to the already-running state

### Implementation Note

No backend changes required. The existing
`CommandControllerResyncData.ForceRestart` flag already controls cache
bypass behavior in the tsunami controller's build logic, and
`ControllerStopCommand` is used for stopping the controller.


Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
This commit is contained in:
Copilot 2025-10-27 11:18:26 -07:00 committed by GitHub
parent 755d9783c9
commit 80c25de5d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -72,26 +72,58 @@ class TsunamiViewModel extends WebViewModel {
}, 300);
}
resyncController() {
private doControllerResync(forceRestart: boolean, logContext: string, triggerRestart: boolean = true) {
if (triggerRestart) {
if (globalStore.get(this.isRestarting)) {
return;
}
this.triggerRestartAtom();
}
const prtn = RpcApi.ControllerResyncCommand(TabRpcClient, {
tabid: globalStore.get(atoms.staticTabId),
blockid: this.blockId,
forcerestart: false,
forcerestart: forceRestart,
});
prtn.catch((e) => console.log("error controller resync", e));
prtn.catch((e) => console.log(`error controller resync (${logContext})`, e));
}
forceRestartController() {
resyncController() {
this.doControllerResync(false, "resync", false);
}
stopController() {
const prtn = RpcApi.ControllerStopCommand(TabRpcClient, this.blockId);
prtn.catch((e) => console.log("error stopping controller", e));
}
async restartController() {
if (globalStore.get(this.isRestarting)) {
return;
}
this.triggerRestartAtom();
const prtn = RpcApi.ControllerResyncCommand(TabRpcClient, {
tabid: globalStore.get(atoms.staticTabId),
blockid: this.blockId,
forcerestart: true,
});
prtn.catch((e) => console.log("error controller resync (force restart)", e));
try {
// Stop the controller first
await RpcApi.ControllerStopCommand(TabRpcClient, this.blockId);
// Wait a bit for the controller to fully stop
await new Promise((resolve) => setTimeout(resolve, 300));
// Then resync to restart it
await RpcApi.ControllerResyncCommand(TabRpcClient, {
tabid: globalStore.get(atoms.staticTabId),
blockid: this.blockId,
forcerestart: false,
});
} catch (e) {
console.log("error restarting controller", e);
}
}
restartAndForceRebuild() {
this.doControllerResync(true, "force rebuild");
}
forceRestartController() {
// Keep this for backward compatibility with the Start button
this.doControllerResync(true, "force restart");
}
setAppMeta(meta: TsunamiAppMeta) {
@ -125,7 +157,7 @@ class TsunamiViewModel extends WebViewModel {
getSettingsMenuItems(): ContextMenuItem[] {
const items = super.getSettingsMenuItems();
// Filter out homepage and navigation-related menu items for tsunami view
return items.filter((item) => {
const filteredItems = items.filter((item) => {
const label = item.label?.toLowerCase() || "";
return (
!label.includes("homepage") &&
@ -134,6 +166,27 @@ class TsunamiViewModel extends WebViewModel {
!label.includes("nav")
);
});
// Add tsunami-specific menu items at the beginning
const tsunamiItems: ContextMenuItem[] = [
{
label: "Stop WaveApp",
click: () => this.stopController(),
},
{
label: "Restart WaveApp",
click: () => this.restartController(),
},
{
label: "Restart WaveApp and Force Rebuild",
click: () => this.restartAndForceRebuild(),
},
{
type: "separator",
},
];
return [...tsunamiItems, ...filteredItems];
}
}