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:
43
examples/lang/http-ui-kv.mcl
Normal file
43
examples/lang/http-ui-kv.mcl
Normal 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
|
||||
}
|
||||
65
examples/lang/http-ui-value.mcl
Normal file
65
examples/lang/http-ui-value.mcl
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user