Skip to content

Commit 73a74be

Browse files
committed
ship the tsunami "rtinfo" back to the server
1 parent 434e2bc commit 73a74be

14 files changed

Lines changed: 265 additions & 0 deletions

File tree

frontend/app/store/wshclientapi.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,11 @@ class RpcApiType {
257257
return client.wshRpcCall("getmeta", data, opts);
258258
}
259259

260+
// command "getrtinfo" [call]
261+
GetRTInfoCommand(client: WshClient, data: CommandGetRTInfoData, opts?: RpcOpts): Promise<ObjRTInfo> {
262+
return client.wshRpcCall("getrtinfo", data, opts);
263+
}
264+
260265
// command "gettab" [call]
261266
GetTabCommand(client: WshClient, data: string, opts?: RpcOpts): Promise<Tab> {
262267
return client.wshRpcCall("gettab", data, opts);
@@ -397,6 +402,11 @@ class RpcApiType {
397402
return client.wshRpcCall("setmeta", data, opts);
398403
}
399404

405+
// command "setrtinfo" [call]
406+
SetRTInfoCommand(client: WshClient, data: CommandSetRTInfoData, opts?: RpcOpts): Promise<void> {
407+
return client.wshRpcCall("setrtinfo", data, opts);
408+
}
409+
400410
// command "setvar" [call]
401411
SetVarCommand(client: WshClient, data: CommandVarData, opts?: RpcOpts): Promise<void> {
402412
return client.wshRpcCall("setvar", data, opts);

frontend/app/view/tsunami/tsunami.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ import * as services from "@/store/services";
1111
import * as jotai from "jotai";
1212
import { memo, useEffect } from "react";
1313

14+
interface TsunamiAppMeta {
15+
title: string;
16+
shortdesc: string;
17+
}
18+
1419
class TsunamiViewModel extends WebViewModel {
1520
shellProcFullStatus: jotai.PrimitiveAtom<BlockControllerRuntimeStatus>;
1621
shellProcStatusUnsubFn: () => void;
@@ -88,6 +93,30 @@ class TsunamiViewModel extends WebViewModel {
8893
prtn.catch((e) => console.log("error controller resync (force restart)", e));
8994
}
9095

96+
setAppMeta(meta: TsunamiAppMeta) {
97+
console.log("tsunami app meta:", meta);
98+
99+
const rtInfo: ObjRTInfo = {};
100+
if (meta.title) {
101+
rtInfo["tsunami:title"] = meta.title;
102+
}
103+
if (meta.shortdesc) {
104+
rtInfo["tsunami:shortdesc"] = meta.shortdesc;
105+
}
106+
107+
if (Object.keys(rtInfo).length > 0) {
108+
const oref = WOS.makeORef("block", this.blockId);
109+
const data: CommandSetRTInfoData = {
110+
oref: oref,
111+
data: rtInfo
112+
};
113+
114+
RpcApi.SetRTInfoCommand(TabRpcClient, data).catch((e) =>
115+
console.log("error setting RT info", e)
116+
);
117+
}
118+
}
119+
91120
dispose() {
92121
if (this.shellProcStatusUnsubFn) {
93122
this.shellProcStatusUnsubFn();
@@ -120,6 +149,32 @@ const TsunamiView = memo((props: ViewComponentProps<TsunamiViewModel>) => {
120149
model.resyncController();
121150
}, [model]);
122151

152+
useEffect(() => {
153+
if (!domReady || !model.webviewRef?.current) return;
154+
155+
const webviewElement = model.webviewRef.current;
156+
157+
const handleConsoleMessage = (e: any) => {
158+
const message = e.message;
159+
if (typeof message === 'string' && message.startsWith('TSUNAMI_META ')) {
160+
try {
161+
const jsonStr = message.substring('TSUNAMI_META '.length);
162+
const meta = JSON.parse(jsonStr);
163+
if (meta.title || meta.shortdesc) {
164+
model.setAppMeta(meta);
165+
}
166+
} catch (error) {
167+
console.error('Failed to parse TSUNAMI_META message:', error);
168+
}
169+
}
170+
};
171+
172+
webviewElement.addEventListener('console-message', handleConsoleMessage);
173+
174+
return () => {
175+
webviewElement.removeEventListener('console-message', handleConsoleMessage);
176+
};
177+
}, [domReady, model]);
123178

124179
const appPath = blockData?.meta?.["tsunami:apppath"];
125180
const controller = blockData?.meta?.controller;

frontend/types/gotypes.d.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,11 @@ declare global {
199199
oref: ORef;
200200
};
201201

202+
// wshrpc.CommandGetRTInfoData
203+
type CommandGetRTInfoData = {
204+
oref: ORef;
205+
};
206+
202207
// wshrpc.CommandMessageData
203208
type CommandMessageData = {
204209
oref: ORef;
@@ -245,6 +250,12 @@ declare global {
245250
meta: MetaType;
246251
};
247252

253+
// wshrpc.CommandSetRTInfoData
254+
type CommandSetRTInfoData = {
255+
oref: ORef;
256+
data: ObjRTInfo;
257+
};
258+
248259
// wshrpc.CommandVarData
249260
type CommandVarData = {
250261
key: string;
@@ -621,6 +632,12 @@ declare global {
621632
// waveobj.ORef
622633
type ORef = string;
623634

635+
// waveobj.ObjRTInfo
636+
type ObjRTInfo = {
637+
"tsunami:title"?: string;
638+
"tsunami:shortdesc"?: string;
639+
};
640+
624641
// iochantypes.Packet
625642
type Packet = {
626643
Data: string;

pkg/tsgen/tsgen.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ var ExtraTypes = []any{
5050
vdom.VDomFrontendUpdate{},
5151
vdom.VDomBackendUpdate{},
5252
waveobj.MetaTSType{},
53+
waveobj.ObjRTInfo{},
5354
}
5455

5556
// add extra type unions to generate here

pkg/waveobj/blockrtinfo.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright 2025, Command Line Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package waveobj
5+
6+
type ObjRTInfo struct {
7+
TsunamiTitle string `json:"tsunami:title,omitempty"`
8+
TsunamiShortDesc string `json:"tsunami:shortdesc,omitempty"`
9+
}

pkg/wcore/block.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,11 @@ func deleteBlockObj(ctx context.Context, blockId string) (int, error) {
223223
}
224224
}
225225
wstore.DBDelete(tx.Context(), waveobj.OType_Block, blockId)
226+
227+
// Clean up block runtime info
228+
blockORef := waveobj.MakeORef(waveobj.OType_Block, blockId)
229+
wstore.DeleteRTInfo(blockORef)
230+
226231
return parentBlockCount, nil
227232
})
228233
}

pkg/wshrpc/wshclient/wshclient.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,12 @@ func GetMetaCommand(w *wshutil.WshRpc, data wshrpc.CommandGetMetaData, opts *wsh
313313
return resp, err
314314
}
315315

316+
// command "getrtinfo", wshserver.GetRTInfoCommand
317+
func GetRTInfoCommand(w *wshutil.WshRpc, data wshrpc.CommandGetRTInfoData, opts *wshrpc.RpcOpts) (*waveobj.ObjRTInfo, error) {
318+
resp, err := sendRpcRequestCallHelper[*waveobj.ObjRTInfo](w, "getrtinfo", data, opts)
319+
return resp, err
320+
}
321+
316322
// command "gettab", wshserver.GetTabCommand
317323
func GetTabCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) (*waveobj.Tab, error) {
318324
resp, err := sendRpcRequestCallHelper[*waveobj.Tab](w, "gettab", data, opts)
@@ -477,6 +483,12 @@ func SetMetaCommand(w *wshutil.WshRpc, data wshrpc.CommandSetMetaData, opts *wsh
477483
return err
478484
}
479485

486+
// command "setrtinfo", wshserver.SetRTInfoCommand
487+
func SetRTInfoCommand(w *wshutil.WshRpc, data wshrpc.CommandSetRTInfoData, opts *wshrpc.RpcOpts) error {
488+
_, err := sendRpcRequestCallHelper[any](w, "setrtinfo", data, opts)
489+
return err
490+
}
491+
480492
// command "setvar", wshserver.SetVarCommand
481493
func SetVarCommand(w *wshutil.WshRpc, data wshrpc.CommandVarData, opts *wshrpc.RpcOpts) error {
482494
_, err := sendRpcRequestCallHelper[any](w, "setvar", data, opts)

pkg/wshrpc/wshrpctypes.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ const (
137137
Command_VDomUrlRequest = "vdomurlrequest"
138138

139139
Command_AiSendMessage = "aisendmessage"
140+
141+
Command_GetRTInfo = "getrtinfo"
142+
Command_SetRTInfo = "setrtinfo"
140143
)
141144

142145
type RespOrErrorUnion[T any] struct {
@@ -256,6 +259,10 @@ type WshRpcInterface interface {
256259
// ai
257260
AiSendMessageCommand(ctx context.Context, data AiMessageData) error
258261

262+
// rtinfo
263+
GetRTInfoCommand(ctx context.Context, data CommandGetRTInfoData) (*waveobj.ObjRTInfo, error)
264+
SetRTInfoCommand(ctx context.Context, data CommandSetRTInfoData) error
265+
259266
// proc
260267
VDomRenderCommand(ctx context.Context, data vdom.VDomFrontendUpdate) chan RespOrErrorUnion[*vdom.VDomBackendUpdate]
261268
VDomUrlRequestCommand(ctx context.Context, data VDomUrlRequestData) chan RespOrErrorUnion[VDomUrlRequestResponse]
@@ -780,3 +787,12 @@ type FileShareCapability struct {
780787
// CanMkdir indicates whether the file share supports creating directories
781788
CanMkdir bool `json:"canmkdir"`
782789
}
790+
791+
type CommandGetRTInfoData struct {
792+
ORef waveobj.ORef `json:"oref"`
793+
}
794+
795+
type CommandSetRTInfoData struct {
796+
ORef waveobj.ORef `json:"oref"`
797+
Data map[string]any `json:"data" tstype:"ObjRTInfo"`
798+
}

pkg/wshrpc/wshserver/wshserver.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,15 @@ func (ws *WshServer) SetMetaCommand(ctx context.Context, data wshrpc.CommandSetM
159159
return nil
160160
}
161161

162+
func (ws *WshServer) GetRTInfoCommand(ctx context.Context, data wshrpc.CommandGetRTInfoData) (*waveobj.ObjRTInfo, error) {
163+
return wstore.GetRTInfo(data.ORef), nil
164+
}
165+
166+
func (ws *WshServer) SetRTInfoCommand(ctx context.Context, data wshrpc.CommandSetRTInfoData) error {
167+
wstore.SetRTInfo(data.ORef, data.Data)
168+
return nil
169+
}
170+
162171
func sendWaveObjUpdate(oref waveobj.ORef) {
163172
ctx, cancelFn := context.WithTimeout(context.Background(), 2*time.Second)
164173
defer cancelFn()

pkg/wstore/blockrtinfo.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright 2025, Command Line Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package wstore
5+
6+
import (
7+
"reflect"
8+
"strings"
9+
"sync"
10+
11+
"github.com/wavetermdev/waveterm/pkg/waveobj"
12+
)
13+
14+
var (
15+
blockRTInfoStore = make(map[waveobj.ORef]*waveobj.ObjRTInfo)
16+
blockRTInfoMutex sync.RWMutex
17+
)
18+
19+
// SetRTInfo merges the provided info map into the BlockRTInfo for the given ORef.
20+
// Only updates fields that exist in the BlockRTInfo struct.
21+
// Removes fields that have nil values.
22+
func SetRTInfo(oref waveobj.ORef, info map[string]any) {
23+
blockRTInfoMutex.Lock()
24+
defer blockRTInfoMutex.Unlock()
25+
26+
rtInfo, exists := blockRTInfoStore[oref]
27+
if !exists {
28+
rtInfo = &waveobj.ObjRTInfo{}
29+
blockRTInfoStore[oref] = rtInfo
30+
}
31+
32+
rtInfoValue := reflect.ValueOf(rtInfo).Elem()
33+
rtInfoType := rtInfoValue.Type()
34+
35+
// Build a map of json tags to field indices for quick lookup
36+
jsonTagToField := make(map[string]int)
37+
for i := 0; i < rtInfoType.NumField(); i++ {
38+
field := rtInfoType.Field(i)
39+
jsonTag := field.Tag.Get("json")
40+
if jsonTag != "" {
41+
// Remove omitempty and other options
42+
tagParts := strings.Split(jsonTag, ",")
43+
if len(tagParts) > 0 && tagParts[0] != "" {
44+
jsonTagToField[tagParts[0]] = i
45+
}
46+
}
47+
}
48+
49+
// Merge the info map into the struct
50+
for key, value := range info {
51+
fieldIndex, exists := jsonTagToField[key]
52+
if !exists {
53+
continue // Skip keys that don't exist in the struct
54+
}
55+
56+
fieldValue := rtInfoValue.Field(fieldIndex)
57+
if !fieldValue.CanSet() {
58+
continue
59+
}
60+
61+
if value == nil {
62+
// Set to zero value (empty string for string fields)
63+
fieldValue.Set(reflect.Zero(fieldValue.Type()))
64+
} else {
65+
// Convert and set the value
66+
if valueStr, ok := value.(string); ok && fieldValue.Kind() == reflect.String {
67+
fieldValue.SetString(valueStr)
68+
}
69+
}
70+
}
71+
}
72+
73+
// GetRTInfo returns the BlockRTInfo for the given ORef, or nil if not found
74+
func GetRTInfo(oref waveobj.ORef) *waveobj.ObjRTInfo {
75+
blockRTInfoMutex.RLock()
76+
defer blockRTInfoMutex.RUnlock()
77+
78+
if rtInfo, exists := blockRTInfoStore[oref]; exists {
79+
// Return a copy to avoid external modification
80+
copy := *rtInfo
81+
return &copy
82+
}
83+
return nil
84+
}
85+
86+
// DeleteRTInfo removes the BlockRTInfo for the given ORef
87+
func DeleteRTInfo(oref waveobj.ORef) {
88+
blockRTInfoMutex.Lock()
89+
defer blockRTInfoMutex.Unlock()
90+
91+
delete(blockRTInfoStore, oref)
92+
}

0 commit comments

Comments
 (0)