kweb: a ui for k
there are three design rules:
- ONE k file is enough to write a gui application. use show and hide to put k data on screen.
- TWO files can be used (html/js for ui and k for computation) if you prefer separation of concerns.
- THREE ways to data-bind: by value, by reference or by expression. k needs nothing special: no views or triggers
calling js from k
k runs in the browser as a WebAssembly module. It is extended with a js
function which creates an anonymous javascript function. Arguments are converted from k to js and back.
With some projections we can write a classic html page in k:
css/style rules could be changed from k like that:
style:js[`x;"function style(x){let s=document.createElement('style'); s.innerText=x;document.head.appendChild(s);return null}"] style"*{background-color:black}"
link k and ui
kweb.js defines a show
function that enables creating ui elements from k values.
show[dst; value ]
takes a k valueshow[dst;`symbol]
references a global variableshow[dst; {expr}]
evaluates a k expression, which is given by a niladic lambda
A k value is simply shown in the gui. It cannot be modified.
If the value is referenced by symbol, it may be modified by ui interactions which mirror the modification to the k side.
A k {expression}
is evaluated each time the ui is updated.
It can be used e.g. to add computed columns to a table.
show[dst;x]
dst
is the id of a div element where the ui is placed inside. If it does not exist or is empty, a new element is created and appended to the body.show
returns the element id.x
is a k value, reference or expression. The kind of ui element that is build depends on the type ofx
, the value reference by it or returned by the expression.
The example shows a table stored in the variable t
.
It can be edited by clicking on the values.
Editing is commited with the Enter
key.
The string value is verified by k if it is valid for the row type.
Edited values always keep the original type.
There is also hide`id
to hide an element.
It adds the hidden class to the containing div element.
Use show[`id;`]
to display it again.
Computed column: The next example shows two tables. The first is stored in the variable t
and shown in the div element a
that is created in the first line.
The second table is created by an expression and shown in b
.
You can edit the first table and see the second updating.
immediate mode ui
The ui updates every time when there is an interaction between k and html/js.
To save work, updating is restricted by the following:
- only visible elements are rebuild
- static k values are not updated
- referenced globals are only updated if the value of the variable has changed
- expressions are always updated
ui elements that are linked by symbols also store the k value of the variable (increasing it's refcount).
Due to k's immutability rules, it is sufficient to compare the stored k value with the value of the variable to detect modifications.
ui variants
So far show
decides how to render a k variable by it's type.
But there are cases when this is not enough, e.g. a general list may be rendered as:
- a select element (dropdown menu)
- a multi select element (listbox)
- a table
- a nested tree with expanding leaves...
Or a table should have further properties such as readonly, different row styles, pageable, ...
Variants can be selected by adding a symbol to show
's first argument:
t:+`a`b!(1 2;3 4) show[`dstid`listbox;t]
`listbox
chooses an alternative ui building function. There are:
k-types default for table DT TD listbox ISFZLDT ISFZL multi selection select ISFZL S dropdown tree D expanding input Cisfz isfz edit C textarea h1 C heading tty C k console text cC span element(`k@)
Custom ui functions can be added in js with kweb.register(dst,f)
If more parameters should be supplied to a ui element, they are passed in a dict as the first argument to show
.
The fields id, class
and type
are treated specially and define the id and classes of the parent div element as well as the ui type.
Remaining fields are added to the created ui element as js properties.
js callbacks and events
If the first argument to show
is a dictionary, keys starting with on… are recognized as events.
Their values are k functions that are called when the event is triggered.
As an example we create a dropdown list with months. A table query is updated every time the dropdown changes.
separate html and k
When the application has reached a certain size, it may be beneficial to separate k and html.
The gui can be declared in html and k remains functional/analytical without any ui code.
To do so, use a data attribute in the html tag of the div container:
<div id="ta" data-k-type="listbox" data-k-var="t"></div> <div id="kv" data-k-val="1 2 3"></div> <div id="dt" data-k-expr="{+/t}"></div>
This is equivalent to the following code in k:
show[`ta`listbox;`t] show[`kv;1 2 3] show[`dt;{+/t}]
sep.html
is an example of separated html and k.
It calls sep.k
in the init function:
kweb.init("sep.k")
sep.k
only defines the table t:+`abc`def!(|!10;?10)
An application can also be written in html/js without any of the kweb functionality and call k directly when needed.
map.html
is an example application that uses the leaflet library to show a slippery map from tiles.
Tiles are normally served from a tile server for each pair of corner coordinates and zoom level.
In this case they are generated from k:
tile:{[x;y;z]c:`c@?128*256 /random floats reinterpreted as bytes @[c;3+4*!256*256;_-1]} /set alpha opaque
The requested coordinates are written on top of each image.
The js part inside a leaflet function looks like that:
var ctx = tile.getContext('2d'); let I=K.CK(K.Kx("tile",K.Ki(x),K.Ki(y),K.Ki(z))) //get a tile from k let u = new Uint8ClampedArray(I.buffer); let d = new ImageData(u,256,256); ctx.putImageData(d,0,0); //draw to canvas background ctx.font="30px monospace" ctx.fillText(x+"/"+y+"@"+z,0,30) //draw tile coordinates return tile;
plot
sin.html
is a program that identifies parameters from a sine wave.
It uses the following k code to update a gui for each change of the input parameters, including a graph of the curves:
a:1.;b:2.;r:.5; x:.4*!50 R:?-#x f:{[a;b;r;x](r*R)+(a*sin x)+(b* cos x)} fit:{[]ab::solve[(sin x;cos x);f[a;b;r;x]]} fit[] p:{[]`x`y!(x;(f[a;b;r;x];f[ab 0;ab 1;0;x]))}
The input element for A is declared as a k expr in html. It triggeres the recalculation during the update.
<div id="A" data-k-expr="{fit[];ab 0}"></div> <div id="B" data-k-expr="{ab 1}"></div>
The plot element is also defined as an expression which returns a dict:
<div id="cnv" class="right" data-k-type="plot" data-k-expr="p" data-width=800 data-height=400>
The plot ui element expects a dict whose keys are axis labels and the values are x and y data.
Plots are interactive: Zoom by drawing a rectangle with the mouse and doubleclick to show coordinates.
plot.js converts a k dict to drawing commands that execute canvas api calls. The conversion is written in k in the default application a.k
draw
Plotting is built upon drawing. The draw commands provide a subset of the canvas api without transformations or scaling:
online application
The website ktye.github.io/kweb/k runs all k examples on this page in the iframe. It also serves as an empty program. If you drop a k file on the page, it will be executed.
The front page ktye.github.io is a larger example that shows an editor, a repl and has a plot canvas.
Type plot d
into the repl at the bottom to see an example and hide`cnv
to hide the canvas.
data i/o
kweb/k
is served from github which is a static server.
The program runs isolated inside the browser, including k as a WebAssembly module.
That means that k has no access to data on disk.
Data files can be added to a virtual filesystem, by dropping them into the browser.
Then k can access them with it's read mechanism: data:<`filename
(in this dialect of k).
Files written by k trigger a download: `filename<"data"
Alternatively kweb can be served from a local http server:
kweb.c
kweb.c
is a fileserver that runs locally.
It has all necessary files attached to the binary, to run a gui application written in k.
You can edit k.exe with a texteditor, jump to the end and change a.html or a.k.
Files read by k are served from the binary, or from disk.
Files written by k are written to disk.
kweb.exe(92kb) the poor man's electron includes: kweb.exe:7680(thanks to tcc) k.wasm:48610 k.js:7389 kweb.js:12315 plot.js:7589 kweb.css:311 a.html:4011 a.k:4099On Windows, double-click on kweb.exe opens a web browser running the gui application.