Skip to content

Commit 4203d80

Browse files
committed
getting there with the tsunami widget
1 parent d7b0755 commit 4203d80

3 files changed

Lines changed: 127 additions & 6 deletions

File tree

frontend/app/view/tsunami/tsunami.tsx

Lines changed: 118 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import { BlockNodeModel } from "@/app/block/blocktypes";
5-
import { WOS } from "@/app/store/global";
5+
import { atoms, globalStore, WOS } from "@/app/store/global";
6+
import { waveEventSubscribe } from "@/app/store/wps";
7+
import { RpcApi } from "@/app/store/wshclientapi";
8+
import { TabRpcClient } from "@/app/store/wshrpcutil";
9+
import * as services from "@/store/services";
610
import * as jotai from "jotai";
711
import { memo } from "react";
812

@@ -12,19 +16,74 @@ class TsunamiViewModel implements ViewModel {
1216
blockId: string;
1317
viewIcon: jotai.Atom<string>;
1418
viewName: jotai.Atom<string>;
19+
shellProcFullStatus: jotai.PrimitiveAtom<BlockControllerRuntimeStatus>;
20+
shellProcStatusUnsubFn: () => void;
21+
isRestarting: jotai.PrimitiveAtom<boolean>;
1522

1623
constructor(blockId: string, nodeModel: BlockNodeModel) {
1724
this.viewType = "tsunami";
1825
this.blockId = blockId;
1926
this.blockAtom = WOS.getWaveObjectAtom<Block>(`block:${blockId}`);
2027
this.viewIcon = jotai.atom("cube");
2128
this.viewName = jotai.atom("Tsunami");
29+
this.isRestarting = jotai.atom(false);
30+
31+
this.shellProcFullStatus = jotai.atom(null) as jotai.PrimitiveAtom<BlockControllerRuntimeStatus>;
32+
const initialShellProcStatus = services.BlockService.GetControllerStatus(blockId);
33+
initialShellProcStatus.then((rts) => {
34+
this.updateShellProcStatus(rts);
35+
});
36+
this.shellProcStatusUnsubFn = waveEventSubscribe({
37+
eventType: "controllerstatus",
38+
scope: WOS.makeORef("block", blockId),
39+
handler: (event) => {
40+
let bcRTS: BlockControllerRuntimeStatus = event.data;
41+
this.updateShellProcStatus(bcRTS);
42+
},
43+
});
2244
}
2345

2446
get viewComponent(): ViewComponent {
2547
return TsunamiView;
2648
}
2749

50+
updateShellProcStatus(fullStatus: BlockControllerRuntimeStatus) {
51+
console.log("tsunami-status", fullStatus);
52+
if (fullStatus == null) {
53+
return;
54+
}
55+
const curStatus = globalStore.get(this.shellProcFullStatus);
56+
if (curStatus == null || curStatus.version < fullStatus.version) {
57+
globalStore.set(this.shellProcFullStatus, fullStatus);
58+
}
59+
}
60+
61+
triggerRestartAtom() {
62+
globalStore.set(this.isRestarting, true);
63+
setTimeout(() => {
64+
globalStore.set(this.isRestarting, false);
65+
}, 300);
66+
}
67+
68+
forceRestartController() {
69+
if (globalStore.get(this.isRestarting)) {
70+
return;
71+
}
72+
this.triggerRestartAtom();
73+
const prtn = RpcApi.ControllerResyncCommand(TabRpcClient, {
74+
tabid: globalStore.get(atoms.staticTabId),
75+
blockid: this.blockId,
76+
forcerestart: true,
77+
});
78+
prtn.catch((e) => console.log("error controller resync (force restart)", e));
79+
}
80+
81+
dispose() {
82+
if (this.shellProcStatusUnsubFn) {
83+
this.shellProcStatusUnsubFn();
84+
}
85+
}
86+
2887
getSettingsMenuItems(): ContextMenuItem[] {
2988
return [];
3089
}
@@ -35,13 +94,69 @@ type TsunamiViewProps = {
3594
};
3695

3796
const TsunamiView = memo(({ model }: TsunamiViewProps) => {
97+
const shellProcFullStatus = jotai.useAtomValue(model.shellProcFullStatus);
98+
const blockData = jotai.useAtomValue(model.blockAtom);
99+
const isRestarting = jotai.useAtomValue(model.isRestarting);
100+
101+
const appPath = blockData?.meta?.["tsunami:apppath"];
102+
const controller = blockData?.meta?.controller;
103+
104+
// Check for configuration errors
105+
const errors = [];
106+
if (!appPath) {
107+
errors.push("App path must be set (tsunami:apppath)");
108+
}
109+
if (controller !== "tsunami") {
110+
errors.push("Invalid controller (must be 'tsunami')");
111+
}
112+
113+
// Show errors if any exist
114+
if (errors.length > 0) {
115+
return (
116+
<div className="w-full h-full flex flex-col items-center justify-center gap-4">
117+
<h1 className="text-4xl font-bold text-main-text-color">Tsunami</h1>
118+
<div className="flex flex-col gap-2">
119+
{errors.map((error, index) => (
120+
<div key={index} className="text-sm" style={{ color: "var(--color-error)" }}>
121+
{error}
122+
</div>
123+
))}
124+
</div>
125+
</div>
126+
);
127+
}
128+
129+
// Check if we should show the iframe
130+
const shouldShowIframe =
131+
shellProcFullStatus?.shellprocstatus === "running" &&
132+
shellProcFullStatus?.tsunamiport &&
133+
shellProcFullStatus.tsunamiport !== 0;
134+
135+
if (shouldShowIframe) {
136+
const iframeUrl = `http://localhost:${shellProcFullStatus.tsunamiport}/?clientid=wave:${model.blockId}`;
137+
return <iframe src={iframeUrl} className="w-full h-full border-0" title="Tsunami Application" />;
138+
}
139+
140+
const status = shellProcFullStatus?.shellprocstatus ?? "init";
141+
const isNotRunning = status === "done" || status === "init";
142+
38143
return (
39-
<div className="w-full h-full flex items-center justify-center">
144+
<div className="w-full h-full flex flex-col items-center justify-center gap-4">
40145
<h1 className="text-4xl font-bold text-main-text-color">Tsunami</h1>
146+
{appPath && <div className="text-sm text-main-text-color opacity-70">{appPath}</div>}
147+
{isNotRunning && !isRestarting && (
148+
<button
149+
onClick={() => model.forceRestartController()}
150+
className="px-4 py-2 bg-accent-color text-primary-text-color rounded hover:bg-accent-color/80 transition-colors cursor-pointer"
151+
>
152+
Start
153+
</button>
154+
)}
155+
{isRestarting && <div className="text-sm text-success-color">Starting...</div>}
41156
</div>
42157
);
43158
});
44159

45160
TsunamiView.displayName = "TsunamiView";
46161

47-
export { TsunamiViewModel };
162+
export { TsunamiViewModel };

pkg/blockcontroller/tsunamicontroller.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ func isBuildCacheUpToDate(appPath string) (bool, error) {
127127
}
128128

129129
func (c *TsunamiController) Start(ctx context.Context, blockMeta waveobj.MetaMapType, rtOpts *waveobj.RuntimeOpts, force bool) error {
130+
log.Printf("TsunamiController.Start called for block %s", c.blockId)
130131
c.runLock.Lock()
131132
defer c.runLock.Unlock()
132133

@@ -198,6 +199,7 @@ func (c *TsunamiController) Start(ctx context.Context, blockMeta waveobj.MetaMap
198199

199200
err = build.TsunamiBuild(opts)
200201
if err != nil {
202+
log.Printf("TsunamiController build error for block %s: %v", c.blockId, err)
201203
return fmt.Errorf("failed to build tsunami app: %w", err)
202204
}
203205
}
@@ -225,7 +227,7 @@ func (c *TsunamiController) Start(ctx context.Context, blockMeta waveobj.MetaMap
225227
c.port = tsunamiProc.Port
226228
})
227229
go c.sendStatusUpdate()
228-
230+
229231
// Monitor process completion
230232
go func() {
231233
<-tsunamiProc.WaitCh
@@ -246,6 +248,7 @@ func (c *TsunamiController) Start(ctx context.Context, blockMeta waveobj.MetaMap
246248
}
247249

248250
func (c *TsunamiController) Stop(graceful bool, newStatus string) error {
251+
log.Printf("TsunamiController.Stop called for block %s (graceful: %t, newStatus: %s)", c.blockId, graceful, newStatus)
249252
c.runLock.Lock()
250253
defer c.runLock.Unlock()
251254

@@ -283,12 +286,12 @@ func (c *TsunamiController) GetRuntimeStatus() *BlockControllerRuntimeStatus {
283286
ShellProcStatus: c.status,
284287
ShellProcExitCode: c.exitCode,
285288
}
286-
289+
287290
if c.status == Status_Running && c.port > 0 {
288291
rtn.TsunamiPort = c.port
289292
}
290293
})
291-
294+
292295
return rtn
293296
}
294297

@@ -388,6 +391,7 @@ func runTsunamiAppBinary(ctx context.Context, appBinPath string) (*TsunamiAppPro
388391
}
389392

390393
func MakeTsunamiController(tabId string, blockId string) Controller {
394+
log.Printf("make tsunami controller: %s %s\n", tabId, blockId)
391395
return &TsunamiController{
392396
blockId: blockId,
393397
tabId: tabId,

tsunami/app/defaultclient.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"flag"
99
"io"
1010
"io/fs"
11+
"log"
1112
"net/http"
1213
"os"
1314

@@ -98,6 +99,7 @@ func RunMain() {
9899
go func() {
99100
// Read stdin until EOF/close, then exit the process
100101
io.ReadAll(os.Stdin)
102+
log.Printf("[tsunami] shutting down due to close of stdin\n")
101103
os.Exit(0)
102104
}()
103105
}

0 commit comments

Comments
 (0)