This commit is contained in:
2026-04-03 20:36:40 -07:00
parent bfc9277f15
commit fd97d2167e
20 changed files with 1134 additions and 282 deletions

View File

@@ -1426,7 +1426,7 @@ updateRec ctx fc clauses arg ty = do
getTele Nothing (VPi _ _ _ _ a b) = do
a <- forceType ctx.env a
getTele (Just $ RVar fc "$ru") a
getTele Nothing v = error fc "Expected a pi type, got \{show v}"
getTele Nothing v = error fc "Expected \{show v}, missing argument to record update."
getTele (Just tm) v = error (getFC tm) "Expected a record type, got \{show v}"
infer : Context -> Raw -> M (Tm × Val)

View File

@@ -282,8 +282,6 @@ instance Eq String where
instance Eq Char where
a == b = eqChar a b
ptype Array : U U
pfunc listToArray : a. List a Array a := `
(a, l) => {

113
src/Web/Spruce.newt Normal file
View File

@@ -0,0 +1,113 @@
module Web.Spruce
-- Spruce is not Elm
-- TODO better story for import..
-- also, we'll probably want to expose something with subscriptions / dispatch / ...
import Prelude
ptype Element
pfunc getElementById : String Element := `(id) => document.getElementById(id)`
-- Make this align with spruce.ts
data Attr msg = SAttr String String | MAttr String msg | VAttr String (String msg)
data VNode msg = TNode String | ENode String (List $ Attr msg) (List $ VNode msg)
text : msg. String VNode msg
text = TNode
tag : msg. String List (Attr msg) List (VNode msg) VNode msg
tag = ENode
tag_ : msg. String List (VNode msg) VNode msg
tag_ tag es = ENode tag Nil es
pfunc runApp : msg model. Element model (update : msg model model) (view : model VNode msg) Unit := `
(_msg, _model, node, init, update, view) => {
function replace(parent, node, child) {
if (node) {
parent.insertBefore(child, node);
parent.removeChild(node);
}
else {
parent.appendChild(child);
}
return child;
}
// patch node, possibly returning a new node
function patch(parentNode, node, vnode) {
if (vnode.tag == 0) {
if (node && node.nodeType == 3) {
node.nodeValue = vnode.h1;
return node;
}
return replace(parentNode, node, document.createTextNode(vnode.h1));
}
let el;
if (node instanceof Element && node.tagName.toLowerCase() == vnode.h1) {
el = node;
}
else {
el = document.createElement(vnode.h1);
}
// update node here
let has = {};
for (let attrs = vnode.h2; attrs.tag == 1; attrs = attrs.h2) {
let attr = attrs.h1;
if (attr.tag == 0) {
let key = attr.h1;
has[key] = true;
if (key in el)
el[key] = attr.h2;
else
el.setAttribute(key, attr.h2);
} else {
let key = attr.h1.slice(2).toLowerCase();
has[key] = true;
let events = el.events || (el.events = {});
if (!events[key])
el.addEventListener(key, listener);
events[key] = attr;
}
}
for (let i = 0; i < el.attributes.length;) {
let attr = el.attributes[i];
if (!has[attr.name]) {
el.removeAttribute(attr.name);
} else {
i++;
}
}
if (el.events) {
for (let key of Object.keys(el.events)) {
if (!has[key]) delete el.events[key];
}
}
let i = 0;
for (let kids = vnode.h3; kids.tag == 1; kids = kids.h2) {
patch(el, el.childNodes[i++], kids.h1);
}
while (el.childNodes[i]) el.removeChild(el.childNodes[i]);
return node == el ? node : replace(parentNode, node, el);
}
let model = init;
let vdom = view(model);
let listener = (ev) => {
console.log('listener', ev, ev.target);
let target = ev.target;
for (;;) {
if (!target) return;
if (target.events?.[ev.type]) break;
target = target.parentElement;
}
const attr = target.events[ev.type];
let action = attr.tag == 2 ? attr.h2(ev.target.value) : attr.h2;
model = update(action)(model);
vdom = view(model);
node = patch(node.parentNode, node, vdom);
};
node = patch(node.parentNode, node, vdom);
return 0
}
`