engine: resources: Add an http ui resource

Many years ago I built and demoed a prototype of a simple web ui with a
slider, and as you moved it left and right, it started up or shutdown
some number of virtual machines.

The webui was standalone code, but the rough idea of having events from
a high-level overview flow into mgmt, was what I wanted to test out. At
this stage, I didn't even have the language built yet. This prototype
helped convince me of the way a web ui would fit into everything.

Years later, I build an autogrouping prototype which looks quite similar
to what we have today. I recently picked it back up to polish it a bit
more. It's certainly not perfect, and might even be buggy, but it's
useful enough that it's worth sharing.

If I had more cycles, I'd probably consider removing the "store" mode,
and replace it with the normal "value" system, but we would need the
resource "mutate" API if we wanted this. This would allow us to directly
change the "value" field, without triggering a graph swap, which would
be a lot less clunky than the "store" situation.

Of course I'd love to see a GTK version of this concept, but I figured
it would be more practical to have a web ui over HTTP.

One notable missing feature, is that if the "web ui" changes (rather
than just a value changing) we need to offer to the user to reload it.
It currently doesn't get an event for that, and so don't confuse your
users. We also need to be better at validating "untrusted" input here.

There's also no major reason to use the "gin" framework, we should
probably redo this with the standard library alone, but it was easier
for me to push out something quick this way. We can optimize that later.

Lastly, this is all quite ugly since I'm not a very good web dev, so if
you want to make this polished, please do! The wasm code is also quite
terrible due to limitations in the compiler, and maybe one day when that
works better and doesn't constantly deadlock, we can improve it.
This commit is contained in:
James Shubin
2025-05-02 02:14:14 -04:00
parent 6b10477ebc
commit 807c4b3430
27 changed files with 3266 additions and 43 deletions

View File

@@ -0,0 +1,43 @@
import "world"
http:server ":8080" { # by default http uses :80 but using :8080 avoids needing root!
#address => ":8080", # you can override the name like this
}
# you can add a raw file like this...
http:file "/file1" {
data => "hello, world, i'm file1 and i don't exist on disk!\n",
}
http:ui "/ui/" {
#path => "/ui/", # we can override the name like this if needed
data => struct{
title => "mgmt http ui",
head => "", # XXX: type unification requires specifying all fields for now
},
}
$text1_id = "text1"
$range1_id = "range1"
http:ui:input $text1_id {
store => "world://",
sort => "a",
}
http:ui:input $range1_id {
store => "world://",
type => "range://?min=0&max=5&step=1",
sort => "b",
}
#Http:Ui:Input[$text1_id].value -> Kv[$text1_id].value
#kv $text1_id { # store in world
#}
$ret1 = world.getval($text1_id) # name of kv resource
test "get1" {
anotherstr => $ret1->value,
onlyshow => ["AnotherStr",], # displays nicer
}

View File

@@ -0,0 +1,65 @@
import "value"
http:server ":8080" { # by default http uses :80 but using :8080 avoids needing root!
#address => ":8080", # you can override the name like this
}
# you can add a raw file like this...
http:file "/file1" {
data => "hello, world, i'm file1 and i don't exist on disk!\n",
}
http:ui "/ui/" {
#path => "/ui/", # we can override the name like this if needed
data => struct{
title => "mgmt http ui",
head => "", # XXX: type unification requires specifying all fields for now
},
}
$text1_id = "text1"
$range1_id = "range1"
$text1_val = if $ret1->ready {
$ret1->value
} else {
"default" # some default
}
$range1_val = if $ret2->ready {
$ret2->value
} else {
"2" # some default
}
http:ui:input $text1_id {
value => $text1_val, # it passes back into itself!
}
http:ui:input $range1_id {
value => $range1_val,
type => "range://?min=0&max=5&step=1",
sort => "b",
}
Http:Ui:Input[$text1_id].value -> Value[$text1_id].any
value $text1_id {
any => "whatever", # TODO: remove the temporary placeholder here
#any => "", # XXX: remove any placeholder to see the bug when absent
}
value $range1_id {
any => "whatever", # TODO: remove the temporary placeholder here
#any => "", # XXX: remove any placeholder to see the bug when absent
}
$ret1 = value.get_str($text1_id) # name of value resource
$ret2 = value.get_str($range1_id) # name of value resource
test "get1" {
anotherstr => $ret1->value,
onlyshow => ["AnotherStr",], # displays nicer
}
test "get2" {
anotherstr => $ret2->value,
onlyshow => ["AnotherStr",], # displays nicer
}