# render


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

One runtime draws a spec everywhere. A host element carries the spec as
inert JSON, and the runtime mounts it:

``` html
<div data-rupaka><script type="application/json">{...spec...}</script></div>
```

The runtime boots on load and after each htmx swap. Nothing is templated
into the script.

## The bundle

[`bundle`](https://vedicreader.github.io/rupaka/render.html#bundle)
joins the core, the fns, the blocks and the boot script in a fixed
order. There is no bundler and no build step.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/render.py#L27"
target="_blank" style="float:right; font-size:smaller">source</a>

### bundle

``` python
def bundle(
    kind:str='js'
)->str:
```

*The runtime as one script (`js`) or one style sheet (`css`).*

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/render.py#L21"
target="_blank" style="float:right; font-size:smaller">source</a>

### assets

``` python
def assets()->fastcore.basics.AttrDict:
```

*The files of the runtime, in bundle order.*

The script starts with the core and ends with the boot. Each block is in
it.

``` python
js = bundle()
assert js.startswith('/* static/rupaka.js */') and js.rstrip().endswith('})();')
for n in manifests(): assert f"Rupaka.block('{n}'" in js, n
assert 'eval(' not in js and 'new Function' not in js and 'fetch(' not in js
```

Each block style uses only the `--rk-*` vars for colour, so a demo takes
the look of the page that holds it.

``` python
import re
```

``` python
for p in assets().css[1:]:
    cols = re.findall(r'(?:#[0-9a-fA-F]{3,6}\b|rgba?\(|hsl\()', p.read_text())
    test_eq((p.name, cols), (p.name, []))
```

## The host element

[`host`](https://vedicreader.github.io/rupaka/render.html#host) writes
the element that carries a spec. JSON in a `<script>` must not hold
`</script>`, so every `<` becomes `\u003c`. `JSON.parse` reads it back
as `<`.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/render.py#L36"
target="_blank" style="float:right; font-size:smaller">source</a>

### host

``` python
def host(
    spec
)->str:
```

*The host element: a div that carries its spec as inert JSON.*

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/render.py#L32"
target="_blank" style="float:right; font-size:smaller">source</a>

### spec_json

``` python
def spec_json(
    spec
)->str:
```

*The spec as JSON that is safe inside a `<script>` element.*

``` python
h = host(dict(id='x', ask='Do not stop at </script> here.'))
assert '</script>' not in h.split('<script type="application/json">')[1][:-len('</script></div>')]
test_eq(json.loads(h.split('json">')[1].split('</script>')[0]), dict(id='x', ask='Do not stop at </script> here.'))
```

## A static page

[`page`](https://vedicreader.github.io/rupaka/render.html#page) writes
one self-contained HTML file: the runtime, the style, the specs, and
abhyasa’s palette. The `fonts` argument picks how the page gets its
fonts:

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<thead>
<tr>
<th><code>fonts</code></th>
<th>what it does</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>'link'</code></td>
<td>links Literata and Libre Baskerville from <code>./fonts</code> (<a
href="https://vedicreader.github.io/rupaka/render.html#save"><code>save</code></a>
copies them)</td>
</tr>
<tr>
<td><code>'embed'</code></td>
<td>puts the fonts in the page as data URIs, about 160 KB more</td>
</tr>
<tr>
<td><code>'none'</code></td>
<td>uses the system serif; for a sandboxed <code>srcdoc</code> frame,
which cannot load files</td>
</tr>
</tbody>
</table>

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/render.py#L48"
target="_blank" style="float:right; font-size:smaller">source</a>

### font_css

``` python
def font_css(
    fonts:str='link', base:str='fonts/'
)->str:
```

*The `@font-face` rules: linked from `base`, embedded as data URIs, or
none.*

[`page`](https://vedicreader.github.io/rupaka/render.html#page) takes
one spec or a list of specs. A list makes a gallery page with a rule
between the demos.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/render.py#L58"
target="_blank" style="float:right; font-size:smaller">source</a>

### page

``` python
def page(
    specs, title:str=None, fonts:str='link', lead:str=''
)->str:
```

*One self-contained HTML document that draws `specs`, with no network.*

A page loads nothing from the network: no `src`, `href` or `url()`
points at `http`. (The SVG namespace string is not a request.) With
`fonts='none'` it has no `@font-face` rule.

``` python
spec = dict(rupaka=1, id='t', title='T', state=dict(a=1), blocks=[dict(type='readout', text='{a}')])
p = page(spec)
assert not re.search(r'''(src|href)=["']?https?:|url\(\s*["']?https?:''', p)
assert '@font-face' in p and '@font-face' not in page(spec, fonts='none')
assert 'data:font/woff2;base64,' in page(spec, fonts='embed')
```

[`save`](https://vedicreader.github.io/rupaka/render.html#save) writes a
page to a file. With linked fonts, it copies the font files beside it,
so the file works from `file://`.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/render.py#L72"
target="_blank" style="float:right; font-size:smaller">source</a>

### save

``` python
def save(
    specs, path, fonts:str='link', **kw
)->pathlib.Path:
```

*Write `page(specs)` to `path`; with `fonts='link'`, copy the fonts to
`fonts/` beside it.*

``` python
import tempfile
```

``` python
out = save(spec, Path(tempfile.mkdtemp())/'t.html')
assert (out.parent/'fonts'/'literata-latin-wght-normal.woff2').exists()
```

## Notebooks: show

`show(spec)` returns an object that a notebook draws. It gives two mime
types:

- `application/vnd.rupaka+json`: the spec, for a frontend that knows
  rupaka (a later leela change).
- `text/html`: the whole page with `fonts='none'`. leela, Jupyter and VS
  Code draw it in a sandboxed frame. This works in leela today.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/render.py#L90"
target="_blank" style="float:right; font-size:smaller">source</a>

### show

``` python
def show(
    spec
)->__main__.Show:
```

*Draw `spec` in a notebook output.*

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/render.py#L83"
target="_blank" style="float:right; font-size:smaller">source</a>

### Show

``` python
def Show(
    spec
):
```

*A demo for a notebook output: the rupaka mime and a full HTML page.*

``` python
mb = show(spec)._repr_mimebundle_()
test_eq(mb[MIME], spec)
assert '<script' in mb['text/html'] and '@font-face' not in mb['text/html']
```

## Markdown: md

`md(spec)` writes a ```` ```rupaka ```` fence with readable JSON. A
renderer that knows rupaka mounts it; others show the JSON as code.
`md(spec, frame=True)` writes a ```` ```{=html} ```` fence that holds a
sandboxed frame. leela draws this in a Markdown cell today, with no
change to leela.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/render.py#L95"
target="_blank" style="float:right; font-size:smaller">source</a>

### md

``` python
def md(
    spec, frame:bool=False, height:int=760
)->str:
```

*A Markdown fence for `spec`: readable JSON, or a sandboxed frame.*

``` python
assert md(spec).startswith('```rupaka\n{') and json.loads(md(spec)[10:-4]) == spec
f = md(spec, frame=True)
assert f.startswith('```{=html}\n<iframe') and '<script' not in f and '&lt;script' in f
```

## FastHTML: Demo and Assets

`Demo(spec)` is the host element as an FT component. `Assets(prefix)`
gives the `Link` and the `Script` for a server that serves the bundle at
`prefix`.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/render.py#L106"
target="_blank" style="float:right; font-size:smaller">source</a>

### Assets

``` python
def Assets(
    prefix:str='/rupaka', v:str=''
):
```

*The style and the script of the runtime, served at `prefix`; `v` busts
the cache.*

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/render.py#L102"
target="_blank" style="float:right; font-size:smaller">source</a>

### Demo

``` python
def Demo(
    spec, **kw
):
```

*The host element of `spec` for a FastHTML page.*

``` python
x = to_xml(Demo(dict(id='d', ask='a </script> b')))
assert '<script type="application/json">' in x and 'data-rupaka' in x and 'a \\u003c/script> b' in x
test_eq(to_xml(Assets(v='1')[1]), '<script src="/rupaka/rupaka.js?v=1"></script>')
```

[`version()`](https://vedicreader.github.io/rupaka/render.html#version)
is a short hash of the bundle, for a cache-busting `v`.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/rupaka/blob/main/rupaka/render.py#L112"
target="_blank" style="float:right; font-size:smaller">source</a>

### version

``` python
def version()->str:
```

*A short hash of the script and the style.*

``` python
test_eq(len(version()), 10)
```
