Skip to content
Merged

VDom 9 #1205

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions cmd/wsh/cmd/htmlstyle.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.root {
padding: 10px;
}

.background {
display: flex;
align-items: center;
width: 100%;
}

.background-inner {
max-width: 300px;
}

.bg-item {
cursor: pointer;
padding: 8px 12px;
border-radius: 4px;
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
}

.bg-item:hover {
background-color: var(--button-grey-hover-bg);
}

.bg-preview {
width: 20px;
height: 20px;
margin-right: 10px;
border-radius: 50%;
border: 1px solid #777;
}

.bg-label {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
105 changes: 34 additions & 71 deletions cmd/wsh/cmd/wshcmd-html.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package cmd

import (
"context"
_ "embed"
"log"

"github.com/spf13/cobra"
Expand All @@ -16,6 +17,9 @@ import (
"github.com/wavetermdev/waveterm/pkg/wshutil"
)

//go:embed htmlstyle.css
var htmlStyleCSS []byte

var htmlCmdNewBlock bool
var HtmlVDomClient *vdomclient.Client = vdomclient.MakeClient(&vdom.VDomBackendOpts{CloseOnCtrlC: true})

Expand Down Expand Up @@ -50,66 +54,25 @@ type BgItem struct {
// Components
var Style = vdomclient.DefineComponent[struct{}](HtmlVDomClient, "Style",
func(ctx context.Context, _ struct{}) any {
return vdom.E("style", nil, `
.root {
padding: 10px;
}

.background {
display: flex;
align-items: center;
width: 100%;
}

.background-inner {
max-width: 300px;
}

.bg-item {
cursor: pointer;
padding: 8px 12px;
border-radius: 4px;
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
}

.bg-item:hover {
background-color: var(--button-grey-hover-bg);
}

.bg-preview {
width: 20px;
height: 20px;
margin-right: 10px;
border-radius: 50%;
border: 1px solid #777;
}

.bg-label {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`)
return vdom.E("wave:style",
vdom.P("src", "vdom:///style.css"),
)
},
)

var BgItemTag = vdomclient.DefineComponent[BgItemProps](HtmlVDomClient, "BgItem",
func(ctx context.Context, props BgItemProps) any {
return vdom.E("div",
vdom.P("className", "bg-item"),
vdom.P("onClick", props.OnClick),
vdom.Class("bg-item"),
vdom.E("div",
vdom.P("className", "bg-preview"),
vdom.Class("bg-preview"),
vdom.PStyle("background", props.Bg),
),
vdom.E("div",
vdom.P("className", "bg-label"),
vdom.Class("bg-label"),
props.Label,
),
vdom.P("onClick", props.OnClick),
)
},
)
Expand All @@ -133,20 +96,17 @@ var BgList = vdomclient.DefineComponent[BgListProps](HtmlVDomClient, "BgList",
}
}

items := make([]*vdom.VDomElem, 0, len(props.Items))
for _, item := range props.Items {
items = append(items, BgItemTag(BgItemProps{
Bg: item.Bg,
Label: item.Label,
OnClick: setBackground(item.Bg),
}))
}

return vdom.E("div",
vdom.P("className", "background"),
vdom.Class("background"),
vdom.E("div",
vdom.P("className", "background-inner"),
items,
vdom.Class("background-inner"),
vdom.ForEach(props.Items, func(item BgItem) any {
return BgItemTag(BgItemProps{
Bg: item.Bg,
Label: item.Label,
OnClick: setBackground(item.Bg),
})
}),
),
)
},
Expand All @@ -164,7 +124,7 @@ var App = vdomclient.DefineComponent[struct{}](HtmlVDomClient, "App",
}

return vdom.E("div",
vdom.P("className", "root"),
vdom.Class("root"),
Style(struct{}{}),
vdom.E("h1", nil, "Set Background"),
vdom.E("div", nil,
Expand All @@ -177,7 +137,11 @@ var App = vdomclient.DefineComponent[struct{}](HtmlVDomClient, "App",
),
vdom.E("div", nil,
vdom.E("img",
vdom.P("style", "width: 100%; height: 100%; max-width: 300px; max-height: 300px; object-fit: contain;"),
vdom.PStyle("width", "100%"),
vdom.PStyle("height", "100%"),
vdom.PStyle("maxWidth", "300px"),
vdom.PStyle("maxHeight", "300px"),
vdom.PStyle("objectFit", "contain"),
vdom.P("src", "vdom:///test.png"),
),
),
Expand All @@ -189,9 +153,7 @@ var App = vdomclient.DefineComponent[struct{}](HtmlVDomClient, "App",
setInputText(e.TargetValue)
}),
),
vdom.E("div", nil,
"text ", inputText,
),
vdom.E("div", nil, "text ", inputText),
),
)
},
Expand All @@ -205,19 +167,20 @@ func htmlRun(cmd *cobra.Command, args []string) error {
return err
}

// Set up the root component
client.SetRootElem(App(struct{}{}))
client.RegisterFileHandler("/style.css", vdomclient.FileHandlerOption{
Data: htmlStyleCSS,
MimeType: "text/css",
})
client.RegisterFileHandler("/test.png", vdomclient.FileHandlerOption{
FilePath: "~/Downloads/IMG_1939.png",
})

// Set up file handler
client.RegisterFileHandler("/test.png", "~/Downloads/IMG_1939.png")

// Create the VDOM context
err = client.CreateVDomContext(&vdom.VDomTarget{NewBlock: htmlCmdNewBlock})
if err != nil {
return err
}

// Handle shutdown
go func() {
<-client.DoneCh
wshutil.DoShutdown("vdom closed by FE", 0, true)
Expand Down
12 changes: 12 additions & 0 deletions emain/emain-vdomhandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@ import { RpcApi } from "../frontend/app/store/wshclientapi";
import { base64ToArray } from "../frontend/util/util";
import { ElectronWshClient } from "./emain-wsh";

export function registerVDomProtocol() {
protocol.registerSchemesAsPrivileged([
{
scheme: "vdom",
privileges: {
standard: true,
supportFetchAPI: true,
},
},
]);
}

export function setupVdomUrlHandler() {
protocol.handle("vdom", async (request) => {
// Only handle GET requests for now
Expand Down
3 changes: 2 additions & 1 deletion emain/emain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import * as electron from "electron";
import { setupVdomUrlHandler } from "emain/emain-vdomhandler";
import { registerVDomProtocol, setupVdomUrlHandler } from "emain/emain-vdomhandler";
import { FastAverageColor } from "fast-average-color";
import fs from "fs";
import * as child_process from "node:child_process";
Expand Down Expand Up @@ -700,6 +700,7 @@ async function appMain() {
electronApp.quit();
return;
}
registerVDomProtocol();
makeAppMenu();
try {
await runWaveSrv(handleWSEvent);
Expand Down
6 changes: 3 additions & 3 deletions frontend/app/store/wshclientapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,9 @@ class RpcApiType {
return client.wshRpcCall("vdomcreatecontext", data, opts);
}

// command "vdomrender" [call]
VDomRenderCommand(client: WshClient, data: VDomFrontendUpdate, opts?: RpcOpts): Promise<VDomBackendUpdate> {
return client.wshRpcCall("vdomrender", data, opts);
// command "vdomrender" [responsestream]
VDomRenderCommand(client: WshClient, data: VDomFrontendUpdate, opts?: RpcOpts): AsyncGenerator<VDomBackendUpdate, void, boolean> {
return client.wshRpcStream("vdomrender", data, opts);
}

// command "vdomurlrequest" [responsestream]
Expand Down
18 changes: 16 additions & 2 deletions frontend/app/view/vdom/vdom-model.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { RpcResponseHelper, WshClient } from "@/app/store/wshclient";
import { RpcApi } from "@/app/store/wshclientapi";
import { makeFeBlockRouteId } from "@/app/store/wshrouter";
import { DefaultRouter, TabRpcClient } from "@/app/store/wshrpcutil";
import { mergeBackendUpdates, restoreVDomElems } from "@/app/view/vdom/vdom-utils";
import { adaptFromReactOrNativeKeyEvent, checkKeyPressed } from "@/util/keyutil";
import debug from "debug";
import * as jotai from "jotai";
Expand Down Expand Up @@ -330,8 +331,21 @@ export class VDomModel {
try {
const feUpdate = this.createFeUpdate();
dlog("fe-update", feUpdate);
const beUpdate = await RpcApi.VDomRenderCommand(TabRpcClient, feUpdate, { route: backendRoute });
this.handleBackendUpdate(beUpdate);
const beUpdateGen = await RpcApi.VDomRenderCommand(TabRpcClient, feUpdate, { route: backendRoute });
let baseUpdate: VDomBackendUpdate = null;
for await (const beUpdate of beUpdateGen) {
if (baseUpdate === null) {
baseUpdate = beUpdate;
} else {
mergeBackendUpdates(baseUpdate, beUpdate);
}
}
if (baseUpdate !== null) {
restoreVDomElems(baseUpdate);
dlog("be-update", baseUpdate);
this.handleBackendUpdate(baseUpdate);
}
dlog("update cycle done");
} finally {
this.lastUpdateTs = Date.now();
this.hasPendingRequest = false;
Expand Down
Loading