Merge remote-tracking branch 'origin/master' into hosting

This commit is contained in:
Seth Trowbridge 2025-02-07 15:36:31 -05:00
commit f371339dcc
19 changed files with 249 additions and 567 deletions

View File

@ -1,3 +0,0 @@
{
"liveServer.settings.port": 5501
}

73
app.js
View File

@ -1,57 +1,32 @@
//@ts-check
import Gale from "./lib/gale/gale.js";
import Van from "./lib/van/van.js";
import HMR from "./lib/van/hmr.js";
const css = Gale({
Board:{
padding: "1rem",
const CSS = Gale({
Button:{
padding: "20px",
background: "blue",
color: "white"
borderRadius: "20px",
color: "white",
fontWeight: "bolder",
":hover":{
background: "yellow"
}
})
}
});
const state = vanX.reactive({key1:"value1", key2:"value2", count:7}, "THING")
console.log(state);
const Components = {
Title()
van.add
(
document.body,
van.tags.div
(
{
const titleString = Van.state("title");
return ()=> div({class:css("Board"), onclick(){
titleString.val = Math.random();
}}, titleString.val);
class:CSS("Button"),
onclick()
{
state.count++;
}
}
, ()=>state.count
)
);
const {div} = Van.tags;
Van.add(document.querySelector("#app"), Components.Title());
const World = {
Round: Van.state(0),
Turn: Van.state(0),
Energy: Van.state([4, 4, 4])
};
/** @typedef {[From:number, To:number]} Transfer */
/** @typedef {{Transfers:Transfer[], Power:number, Vector:number[], Charge:number, Effect:string}} Ability */
/** @type Ability */
const Ability = {
Transfers:[],
Power:1,
Vector:[],
Charge:0,
Effect:"damage"
};
/** @typedef {{}} Character */
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}

View File

@ -1,7 +0,0 @@
import Test from "./lib/test.js";
/** @type {Test.de} */
const thing =()=>{}
console.log(Test.default("a", "b"));

26
appx.js
View File

@ -1,26 +0,0 @@
import vanX from "./lib/vanx/vanx.js";
import Van from "./lib/van/van.js";
const {span, input, button} = Van.tags;
const data = vanX.reactive({name: {first: "Tao", last: "Xin"}});
const flatDerived = vanX.calc(() => `${data.name.first} ${data.name.last}`);
const Name = () => {
return span(
"First name: ",
input({type: "text", value: () => data.name.first,
oninput: e => data.name.first = e.target.value}), " ",
"Last name: ",
input({type: "text", value: () => data.name.last,
oninput: e => data.name.last = e.target.value}), " ",
//"Full name: ", () => derived.fullName, " ",
"CALC:", flatDerived,
button({onclick: () => data.name = {first: "Tao", last: "Xin"}}, "Reset"),
)
}
Van.add(document.body, Name());

15
deno.json Normal file
View File

@ -0,0 +1,15 @@
{
"compilerOptions": {
"checkJs": true,
"lib": [
"dom",
"dom.iterable",
"dom.asynciterable",
"deno.ns",
"deno.unstable"
]
},
"imports": {
"entry": "./app.js"
}
}

92
hmr.js Normal file
View File

@ -0,0 +1,92 @@
//@ts-check
// hmr
const HMR = globalThis.HMR = {
Time: 0,
/** @type {Record<string, string>} */
Temp:{},
Tick()
{
for(const k in HMR.Temp)
{
sessionStorage.setItem(k, HMR.Temp[k]);
}
HMR.Temp = {};
HMR.Time = 0;
},
/** @type {(key:string, value:string)=>void} */
Save(key, value)
{
this.Temp[key] = value;
if(!this.Time)
{
this.Time = setTimeout(this.Tick, 500);
}
console.log("SAVE", key, value);
},
/** @type {(key:string)=>string|null} */
Load(key)
{
const value = sessionStorage.getItem(key);
console.log("LOAD", key, value);
return value;
},
/** @type {string|undefined} */
_ID: undefined,
_index: 0,
/** @type {(id:string|undefined = undefined)=>void} */
StartID(id)
{
this._index = 0;
this._ID = id;
},
NextID()
{
return this._ID ? this._ID + "_" + (this._index++) + "_" : "";
},
BindVan()
{
//bind Van
const origninalState = globalThis.van.state;
globalThis.van.state =(value, key="")=>
{
const type = typeof value;
let reader =d=>d;
let writer =d=>d?.toString() || null;
switch(type)
{
case "boolean" :
reader =(data)=> data === "true"; break;
case "number" :
reader = parseFloat; break;
case "object" :
reader = JSON.parse;
writer = JSON.stringify;
break;
}
const fullKey = "HMR_" + HMR.NextID() + key;
const stringValue = HMR.Load(fullKey);
const signal = origninalState(/**@type{T}*/(stringValue ? reader(stringValue) : value));
van.derive(()=>HMR.Save(fullKey, writer(signal.val)));
return signal;
};
},
BindVanX()
{
//bind VanX
const originalReactive = globalThis.vanX.reactive;
globalThis.vanX.reactive =(obj, id)=>
{
HMR.StartID(id);
const state = originalReactive(obj);
HMR.StartID();
return state;
}
}
}

View File

@ -1,10 +1,29 @@
<!DOCTYPE html>
<html>
<head></head>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="hmr.js"></script>
<script>
// van:
{let e,t,r,o,n,l,s,i,f,h,w,a,d,u,_,c,S,g,y,b,m,v,j,x,O;s=Object.getPrototypeOf,f={},h=s(i={isConnected:1}),w=s(s),a=(e,t,r,o)=>(e??(setTimeout(r,o),new Set)).add(t),d=(e,t,o)=>{let n=r;r=t;try{return e(o)}catch(e){return console.error(e),o}finally{r=n}},u=e=>e.filter(e=>e.t?.isConnected),_=e=>n=a(n,e,()=>{for(let e of n)e.o=u(e.o),e.l=u(e.l);n=l},1e3),c={get val(){return r?.i?.add(this),this.rawVal},get oldVal(){return r?.i?.add(this),this.h},set val(o){r?.u?.add(this),o!==this.rawVal&&(this.rawVal=o,this.o.length+this.l.length?(t?.add(this),e=a(e,this,x)):this.h=o)}},S=e=>({__proto__:c,rawVal:e,h:e,o:[],l:[]}),g=(e,t)=>{let r={i:new Set,u:new Set},n={f:e},l=o;o=[];let s=d(e,r,t);s=(s??document).nodeType?s:new Text(s);for(let e of r.i)r.u.has(e)||(_(e),e.o.push(n));for(let e of o)e.t=s;return o=l,n.t=s},y=(e,t=S(),r)=>{let n={i:new Set,u:new Set},l={f:e,s:t};l.t=r??o?.push(l)??i,t.val=d(e,n,t.rawVal);for(let e of n.i)n.u.has(e)||(_(e),e.l.push(l));return t},b=(e,...t)=>{for(let r of t.flat(1/0)){let t=s(r??0),o=t===c?g(()=>r.val):t===w?g(r):r;o!=l&&e.append(o)}return e},m=(e,t,...r)=>{let[{is:o,...n},...i]=s(r[0]??0)===h?r:[{},...r],a=e?document.createElementNS(e,t,{is:o}):document.createElement(t,{is:o});for(let[e,r]of Object.entries(n)){let o=t=>t?Object.getOwnPropertyDescriptor(t,e)??o(s(t)):l,n=t+","+e,i=f[n]??=o(s(a))?.set??0,h=e.startsWith("on")?(t,r)=>{let o=e.slice(2);a.removeEventListener(o,r),a.addEventListener(o,t)}:i?i.bind(a):a.setAttribute.bind(a,e),d=s(r??0);e.startsWith("on")||d===w&&(r=y(r),d=c),d===c?g(()=>(h(r.val,r.h),a)):h(r)}return b(a,i)},v=e=>({get:(t,r)=>m.bind(l,e,r)}),j=(e,t)=>t?t!==e&&e.replaceWith(t):e.remove(),x=()=>{let r=0,o=[...e].filter(e=>e.rawVal!==e.h);do{t=new Set;for(let e of new Set(o.flatMap(e=>e.l=u(e.l))))y(e.f,e.s,e.t),e.t=l}while(++r<100&&(o=[...t]).length);let n=[...e].filter(e=>e.rawVal!==e.h);e=l;for(let e of new Set(n.flatMap(e=>e.o=u(e.o))))j(e.t,g(e.f,e.t)),e.t=l;for(let e of n)e.h=e.rawVal},O={tags:new Proxy(e=>new Proxy(m,v(e)),v()),hydrate:(e,t)=>j(e,g(t,e)),add:b,state:S,derive:y},window.van=O;}
globalThis.HMR?.BindVan();
// vanX:
{let e,t,r,{fromEntries:o,entries:l,keys:n,hasOwn:f,getPrototypeOf:a}=Object,{get:i,set:y,deleteProperty:c,ownKeys:s}=Reflect,{state:m,derive:d,add:u}=van,b=1e3,w=Symbol(),A=Symbol(),S=Symbol(),_=Symbol(),g=Symbol(),p=Symbol(),P=e=>(e[A]=1,e),v=e=>e instanceof Object&&!(e instanceof Function)&&!e[p],h=e=>{if(e?.[A]){let t=m();return d(()=>{let r=e();v(t.rawVal)&&v(r)?B(t.rawVal,r):t.val=x(r)}),t}return m(x(e))},F=e=>{let t=Array.isArray(e)?[]:{__proto__:a(e)};for(let[r,o]of l(e))t[r]=h(o);return t[S]=[],t[_]=m(1),t},O={get:(e,t,r)=>t===w?e:f(e,t)?Array.isArray(e)&&"length"===t?(e[_].val,e.length):e[t].val:i(e,t,r),set:(e,o,l,n)=>f(e,o)?Array.isArray(e)&&"length"===o?(l!==e.length&&++e[_].val,e.length=l,1):(e[o].val=x(l),1):o in e?y(e,o,l,n):y(e,o,h(l))&&(++e[_].val,C(e).forEach(E.bind(t,n,o,e[o],r)),1),deleteProperty:(e,t)=>(c(e,t)&&R(e,t),++e[_].val),ownKeys:e=>(e[_].val,s(e))},x=e=>!v(e)||e[w]?e:new Proxy(F(e),O),D=e=>(e[p]=1,e),j=e=>e[w],K=a(m()),N=e=>new Proxy(e,{get:(e,t,r)=>a(e[t]??0)===K?{val:k(e[t].rawVal)}:i(e,t,r)}),k=e=>e?.[w]?new Proxy(N(e[w]),O):e,C=e=>e[S]=e[S].filter(e=>e.t.isConnected),E=(e,t,r,o,{t:l,f:f})=>{let a=Array.isArray(e),i=a?Number(t):t;u(l,()=>l[g][t]=f(r,()=>delete e[t],i)),a&&!o&&i!==e.length-1&&l.insertBefore(l.lastChild,l[g][n(e).find(e=>Number(e)>i)])},R=(e,t)=>{for(let r of C(e)){let e=r.t[g];e[t]?.remove(),delete e[t]}},T=r=>(e??(setTimeout(()=>(e.forEach(C),e=t),b),e=new Set)).add(r),q=(e,t,r)=>{let o={t:e instanceof Function?e():e,f:r},n=t[w];o.t[g]={},n[S].push(o),T(n);for(let[e,r]of l(n))E(t,e,r,1,o);return o.t},z=(e,t)=>{for(let[r,o]of l(t)){let t=e[r];v(t)&&v(o)?z(t,o):e[r]=o}for(let r in e)f(t,r)||delete e[r];let r=n(t),o=Array.isArray(e);if(o||n(e).some((e,t)=>e!==r[t])){let l=e[w];if(o)e.length=t.length;else{++l[_].val;let e={...l};for(let e of r)delete l[e];for(let t of r)l[t]=e[t]}for(let{t:e}of C(l)){let{firstChild:t,[g]:o}=e;for(let l of r)t===o[l]?t=t.nextSibling:e.insertBefore(o[l],t)}}return e},B=(e,n)=>{r=1;try{return z(e,n instanceof Function?Array.isArray(e)?n(e.filter(e=>1)):o(n(l(e))):n)}finally{r=t}},G=e=>Array.isArray(e)?e.filter(e=>1).map(G):v(e)?o(l(e).map(([e,t])=>[e,G(t)])):e;window.vanX={calc:P,reactive:x,noreactive:D,stateFields:j,raw:k,list:q,replace:B,compact:G}}
globalThis.HMR?.BindVanX();
// gale
{const e=(t,n)=>`${t}{${Object.keys(n).map((t=>{const s=n[t];switch(t[0]){case"@":return e(`@media(max-width:${t.substring(1)})`,s);case":":return e(`&${t}`,s);case".":return e(`${t}`,s);case"^":return e(`&:hover .${t.substring(1)}`,s)}return`${t.replace(/([a-z])([A-Z])/g,"$1-$2")}: ${s};`})).join("\n")}}`;let t=0;globalThis.Gale=n=>{const s=t?"_"+t:"";t++;const r=Object.keys(n).map((t=>e("."+t,n[t]))).join("\n");globalThis.document?.head.insertAdjacentHTML("beforeend",`<style data-sheet="${t}">${r}</style>`);const a=(...e)=>{const t=(e,t)=>{const n=t.lastIndexOf(e)+e.length;return n?t.substring(n):t};return e.map((e=>t("^",t(".",e)))).join(s+" ")+s};return a.css=r,a}};
// boot
((t="/")=>{fetch(t+"deno.json").then((t=>t.json())).then((e=>{const n=(t,e)=>{let n=document.createElement("script");n.type=t,n.textContent=e,document.head.appendChild(n)};for(let n in e.imports){const o=e.imports[n];o.startsWith("./")&&(e.imports[n]=t+o.substring(2))}n("importmap",JSON.stringify({imports:e.imports})),n("module",'import "entry";')}))})();
</script>
</head>
<body>
<h1>Le App</h1>
<div id="app"></div>
<script src="app.js" type="module"></script>
<!--<script src="appx.js" type="module"></script>-->
</body>
</html>

View File

@ -1,72 +0,0 @@
// @ts-check
const KeyQuery = "@";
const KeyPseudo = ":";
const KeyChild = ".";
const KeyGroup = "^";
/** @typedef { Partial<CSSStyleDeclaration> & {[key: `${KeyQuery|KeyPseudo|KeyChild|KeyGroup}${string}`]: UserStyles } } UserStyles */
/** @typedef {Record<string, UserStyles>} UserSheet */
/**
* @template Obj
* @typedef { { [Key in keyof Obj]: Obj[Key] extends object ? Key | CollectKeys<Obj[Key]> : Key }[keyof Obj] } CollectKeys
*/
/**
* @template Keys
* @typedef { Keys extends `${KeyChild|KeyGroup}${infer Rest}` ? Keys : never } FilterKeys
*/
/**
* @template A
* @template B
* @typedef {A extends string ? B extends string ? `${A}${B}` : never : never } CrossMultiply
*/
/**
* @template Rec
* @typedef { keyof Rec | { [K in keyof Rec]: K extends string ? CrossMultiply<K, FilterKeys<CollectKeys<Rec[K]>>> : never }[keyof Rec] } CrossMultiplyRecord
*/
/** @type {(selector:string, obj:UserStyles)=>string} */
const Tier=(selector, obj)=>
{
const styles = Object.keys(obj).map((key)=>
{
const value = obj[key];
switch(key[0])
{
case KeyQuery :
return Tier(`@media(max-width:${key.substring(KeyQuery.length)})`, value);
case KeyPseudo :
return Tier(`&${key}`, value);
case KeyChild :
return Tier(`${key}`, value);
case KeyGroup :
return Tier(`&:hover .${key.substring(KeyGroup.length)}`, value);
}
return `${ key.replace(/([a-z])([A-Z])/g, '$1-$2') }: ${value};`
});
return `${selector}{${styles.join("\n")}}`;
}
let i = 0;
/** @type {<T extends UserSheet>(sheet:UserSheet&T)=> ((...args:CrossMultiplyRecord<T>[])=>string)&{css:string}} */
export default (sheet)=>
{
const id = i ? "_"+i : "";
i++;
const css = Object.keys(sheet).map(key=>Tier("."+key, sheet[key])).join(`\n`);
globalThis.document?.head.insertAdjacentHTML("beforeend", `<style data-sheet="${i}">${css}</style>`);
const classes =(...args)=>{
/** @type {(needle:string, str:string)=>string} */
const extractLast =(needle, str)=>{
const ind = str.lastIndexOf(needle)+needle.length;
return ind ? str.substring(ind) : str;
}
return args.map((arg)=>extractLast(KeyGroup, extractLast(KeyChild, arg))).join(id+" ")+id;
}
classes.css = css;
return classes;
}

6
lib/test.d.ts vendored
View File

@ -1,6 +0,0 @@
export type defaultType = (a:string, b:string)=>string;
declare const members:defaultType
export default members

View File

@ -1,4 +0,0 @@
export default function(a, b)
{
return a + "---" + b;
}

View File

@ -1,62 +0,0 @@
//@ts-check
import * as Van from "./van.members.js";
const Gateway = {
Time: 0,
Temp:{},
Tick()
{
for(let k in Gateway.Temp)
{
localStorage.setItem(k, Gateway.Temp[k]);
}
Gateway.Temp = {};
Gateway.Time = 0;
},
Save(key, value)
{
Gateway.Temp[key] = value;
if(!Gateway.Time)
{
Gateway.Time = setTimeout(Gateway.Tick, 500);
}
},
Load(key)
{
return localStorage.getItem(key);
}
}
/**
* HMR Wrapper for Van.state
* @template T
* @param {T} value - initial value
* @param {string} key - Storage ID
* @returns {Van.State<T>}
*/
export default function(value, key)
{
const type = typeof value;
let reader =(data)=>data;
let writer =(data)=> data.toString();
if(type === "object")
{
reader = JSON.parse;
writer = JSON.stringify;
}
else if(type === "number")
{
reader = parseFloat;
}
else if(type === "boolean")
{
reader =(data)=> data === "true";
}
const stringValue = Gateway.Load(key);
const signal = Van.state(/**@type{T}*/(stringValue ? reader(stringValue) : value));
Van.derive(()=>Gateway.Save(key, writer(signal.val)));
return signal;
}

48
lib/van/van.d.ts vendored
View File

@ -1,48 +0,0 @@
export interface State<T> {
val: T
readonly oldVal: T
readonly rawVal: T
}
// Defining readonly view of State<T> for covariance.
// Basically we want StateView<string> to implement StateView<string | number>
export type StateView<T> = Readonly<State<T>>
export type Val<T> = State<T> | T
export type Primitive = string | number | boolean | bigint
export type PropValue = Primitive | ((e: any) => void) | null
export type PropValueOrDerived = PropValue | StateView<PropValue> | (() => PropValue)
export type Props = Record<string, PropValueOrDerived> & { class?: PropValueOrDerived; is?: string }
export type PropsWithKnownKeys<ElementType> = Partial<{[K in keyof ElementType]: PropValueOrDerived}>
export type ValidChildDomValue = Primitive | Node | null | undefined
export type BindingFunc = ((dom?: Node) => ValidChildDomValue) | ((dom?: Element) => Element)
export type ChildDom = ValidChildDomValue | StateView<Primitive | null | undefined> | BindingFunc | readonly ChildDom[]
export type TagFunc<Result> = (first?: Props & PropsWithKnownKeys<Result> | ChildDom, ...rest: readonly ChildDom[]) => Result
type Tags = Readonly<Record<string, TagFunc<Element>>> & {
[K in keyof HTMLElementTagNameMap]: TagFunc<HTMLElementTagNameMap[K]>
}
declare function state<T>(): State<T>
declare function state<T>(initVal: T): State<T>
export interface Van {
readonly state: typeof state
readonly derive: <T>(f: () => T) => State<T>
readonly add: (dom: Element, ...children: readonly ChildDom[]) => Element
readonly tags: Tags & ((namespaceURI: string) => Readonly<Record<string, TagFunc<Element>>>)
readonly hydrate: <T extends Node>(dom: T, f: (dom: T) => T | null | undefined) => T
}
declare const van: Van
export default van

View File

@ -1,227 +0,0 @@
// This file consistently uses `let` keyword instead of `const` for reducing the bundle size.
// Global variables - aliasing some builtin symbols to reduce the bundle size.
let protoOf = Object.getPrototypeOf
let changedStates, derivedStates, curDeps, curNewDerives, alwaysConnectedDom = {isConnected: 1}
let gcCycleInMs = 1000, statesToGc, propSetterCache = {}
let objProto = protoOf(alwaysConnectedDom), funcProto = protoOf(protoOf), _undefined
let addAndScheduleOnFirst = (set, s, f, waitMs) =>
(set ?? (setTimeout(f, waitMs), new Set)).add(s)
let runAndCaptureDeps = (f, deps, arg) => {
let prevDeps = curDeps
curDeps = deps
try {
return f(arg)
} catch (e) {
console.error(e)
return arg
} finally {
curDeps = prevDeps
}
}
let keepConnected = l => l.filter(b => b._dom?.isConnected)
let addStatesToGc = d => statesToGc = addAndScheduleOnFirst(statesToGc, d, () => {
for (let s of statesToGc)
s._bindings = keepConnected(s._bindings),
s._listeners = keepConnected(s._listeners)
statesToGc = _undefined
}, gcCycleInMs)
let stateProto = {
get val() {
curDeps?._getters?.add(this)
return this.rawVal
},
get oldVal() {
curDeps?._getters?.add(this)
return this._oldVal
},
set val(v) {
curDeps?._setters?.add(this)
if (v !== this.rawVal) {
this.rawVal = v
this._bindings.length + this._listeners.length ?
(derivedStates?.add(this), changedStates = addAndScheduleOnFirst(changedStates, this, updateDoms)) :
this._oldVal = v
}
},
}
let state = initVal => ({
__proto__: stateProto,
rawVal: initVal,
_oldVal: initVal,
_bindings: [],
_listeners: [],
})
let bind = (f, dom) => {
let deps = {_getters: new Set, _setters: new Set}, binding = {f}, prevNewDerives = curNewDerives
curNewDerives = []
let newDom = runAndCaptureDeps(f, deps, dom)
newDom = (newDom ?? document).nodeType ? newDom : new Text(newDom)
for (let d of deps._getters)
deps._setters.has(d) || (addStatesToGc(d), d._bindings.push(binding))
for (let l of curNewDerives) l._dom = newDom
curNewDerives = prevNewDerives
return binding._dom = newDom
}
let derive = (f, s = state(), dom) => {
let deps = {_getters: new Set, _setters: new Set}, listener = {f, s}
listener._dom = dom ?? curNewDerives?.push(listener) ?? alwaysConnectedDom
s.val = runAndCaptureDeps(f, deps, s.rawVal)
for (let d of deps._getters)
deps._setters.has(d) || (addStatesToGc(d), d._listeners.push(listener))
return s
}
let add = (dom, ...children) => {
for (let c of children.flat(Infinity)) {
let protoOfC = protoOf(c ?? 0)
let child = protoOfC === stateProto ? bind(() => c.val) :
protoOfC === funcProto ? bind(c) : c
child != _undefined && dom.append(child)
}
return dom
}
let tag = (ns, name, ...args) => {
let [{is, ...props}, ...children] = protoOf(args[0] ?? 0) === objProto ? args : [{}, ...args]
let dom = ns ? document.createElementNS(ns, name, {is}) : document.createElement(name, {is})
for (let [k, v] of Object.entries(props)) {
let getPropDescriptor = proto => proto ?
Object.getOwnPropertyDescriptor(proto, k) ?? getPropDescriptor(protoOf(proto)) :
_undefined
let cacheKey = name + "," + k
let propSetter = propSetterCache[cacheKey] ??= getPropDescriptor(protoOf(dom))?.set ?? 0
let setter = k.startsWith("on") ?
(v, oldV) => {
let event = k.slice(2)
dom.removeEventListener(event, oldV)
dom.addEventListener(event, v)
} :
propSetter ? propSetter.bind(dom) : dom.setAttribute.bind(dom, k)
let protoOfV = protoOf(v ?? 0)
k.startsWith("on") || protoOfV === funcProto && (v = derive(v), protoOfV = stateProto)
protoOfV === stateProto ? bind(() => (setter(v.val, v._oldVal), dom)) : setter(v)
}
return add(dom, children)
}
let handler = ns => ({get: (_, name) => tag.bind(_undefined, ns, name)})
let update = (dom, newDom) => newDom ? newDom !== dom && dom.replaceWith(newDom) : dom.remove()
//let updateDoms = () => {
// let iter = 0, derivedStatesArray = [...changedStates].filter(s => s.rawVal !== s._oldVal)
// do {
// derivedStates = new Set
// for (let l of new Set(derivedStatesArray.flatMap(s => s._listeners = keepConnected(s._listeners))))
// derive(l.f, l.s, l._dom), l._dom = _undefined
// } while (++iter < 100 && (derivedStatesArray = [...derivedStates]).length)
// let changedStatesArray = [...changedStates].filter(s => s.rawVal !== s._oldVal)
// changedStates = _undefined
// for (let b of new Set(changedStatesArray.flatMap(s => s._bindings = keepConnected(s._bindings))))
// update(b._dom, bind(b.f, b._dom)), b._dom = _undefined
// for (let s of changedStatesArray) s._oldVal = s.rawVal
//}
/*********************************************** */
const Gateway = {
Time: 0,
Temp:{},
Tick()
{
for(let k in Gateway.Temp)
{
localStorage.setItem(k, Gateway.Temp[k]);
}
Gateway.Temp = {};
Gateway.Time = 0;
},
Save(key, value)
{
Gateway.Temp[key] = value;
if(!Gateway.Time)
{
Gateway.Time = setTimeout(Gateway.Tick, 500);
}
},
Load(key)
{
return localStorage.getItem(key);
}
}
function pathHash(/** @type {HTMLElement} */element)
{
const path = [];
while (element && element !== document.body) {
const parent = element.parentElement
path.unshift(Array.from(parent.children).indexOf(element)); // Push the index of the current element at the start of the path
element = parent;
}
return path.join('-'); // Return the path as a string, e.g., "1-0-0-2"
}
/**
* Logs a relationship between a state and a DOM element.
* @param {any} state - The state object being tracked.
* @param {Element} dom - The DOM element affected by the state.
*/
const trackStateDomRelationship = (state, doms) => {
doms.forEach(dom => {
if(dom)
{
const hash = pathHash(dom);
Gateway.Save(hash, JSON.stringify(state));
}
});
};
// Enhanced updateDoms function
let updateDoms = () => {
let iter = 0, derivedStatesArray = [...changedStates].filter(s => s.rawVal !== s._oldVal)
do {
derivedStates = new Set
for (let l of new Set(derivedStatesArray.flatMap(s => s._listeners = keepConnected(s._listeners))))
derive(l.f, l.s, l._dom), l._dom = _undefined
} while (++iter < 100 && (derivedStatesArray = [...derivedStates]).length)
let changedStatesArray = [...changedStates].filter(s => s.rawVal !== s._oldVal)
changedStates = _undefined
const _block = new Set(changedStatesArray.flatMap(s =>{
trackStateDomRelationship(s.rawVal, s._bindings.map(b=>b._dom));
s.rawVal = "FORCE";
return s._bindings = keepConnected(s._bindings);
}));
for (let b of _block) {
update(b._dom, bind(b.f, b._dom));
b._dom = _undefined;
}
// Update old values for changed states
for (let s of changedStatesArray) {
s._oldVal = s.rawVal;
}
};
// Debugging helper to log the state-DOM relationships
globalThis.Divulge = () => console.log(Gateway.Temp);
globalThis.path = pathHash;
/*********************************************** */
export default {
tags: new Proxy(ns => new Proxy(tag, handler(ns)), handler()),
hydrate: (dom, f) => update(dom, bind(f, dom)),
add, state, derive,
}

View File

@ -1,8 +0,0 @@
export * from "./van.js";
import Van from "./van.js";
export const add = Van.add;
export const derive = Van.derive;
export const state = Van.state;
export const hydrate = Van.hydrate;
export const tags = Van.tags;

View File

@ -1,8 +0,0 @@
import * as VanX from "./vanx.js";
const HMR =()=>
{
}
VanX.default.reactive

24
lib/vanx/vanx.d.ts vendored
View File

@ -1,24 +0,0 @@
import * as Van from "../van/van.members.js";
export type StateOf<T> = { readonly [K in keyof T]: Van.State<T[K]> }
export type ValueType<T> = T extends (infer V)[] ? V : T[keyof T]
export type KeyType<T> = T extends unknown[] ? number : string
export type ReplacementFunc<T> =
T extends (infer V)[] ? (items: V[]) => readonly V[] :
(items: [string, T[keyof T]][]) => readonly [string, T[keyof T]][]
declare const vanX:{
calc: <R>(f: () => R) => R
reactive: <T extends object>(obj: T) => T
noreactive: <T extends object>(obj: T) => T,
stateFields: <T extends object>(obj: T) => StateOf<T>
raw: <T extends object>(obj: T) => T,
list: <T extends object, ElementType extends Element>(
container: (() => ElementType) | ElementType, items: T,
itemFunc: (v: Van.State<ValueType<T>>,
deleter: () => void, k: KeyType<T>) => Node
) => ElementType,
replace: <T extends object>(obj: T, replacement: ReplacementFunc<T> | T) => T,
compact: <T extends object>(obj: T) => T,
}
export default vanX;

View File

@ -1,5 +0,0 @@
import van from "../van/van.js";
let e,t,r,{fromEntries:o,entries:l,keys:n,hasOwn:f,getPrototypeOf:a}=Object,{get:i,set:y,deleteProperty:c,ownKeys:s}=Reflect,
{state:m,derive:d,add:u}=van,
b=1e3,w=Symbol(),A=Symbol(),S=Symbol(),_=Symbol(),g=Symbol(),p=Symbol(),P=e=>(e[A]=1,e),v=e=>e instanceof Object&&!(e instanceof Function)&&!e[p],h=e=>{if(e?.[A]){let t=m();return d(()=>{let r=e();v(t.rawVal)&&v(r)?B(t.rawVal,r):t.val=x(r)}),t}return m(x(e))},F=e=>{let t=Array.isArray(e)?[]:{__proto__:a(e)};for(let[r,o]of l(e))t[r]=h(o);return t[S]=[],t[_]=m(1),t},O={get:(e,t,r)=>t===w?e:f(e,t)?Array.isArray(e)&&"length"===t?(e[_].val,e.length):e[t].val:i(e,t,r),set:(e,o,l,n)=>f(e,o)?Array.isArray(e)&&"length"===o?(l!==e.length&&++e[_].val,e.length=l,1):(e[o].val=x(l),1):o in e?y(e,o,l,n):y(e,o,h(l))&&(++e[_].val,C(e).forEach(E.bind(t,n,o,e[o],r)),1),deleteProperty:(e,t)=>(c(e,t)&&R(e,t),++e[_].val),ownKeys:e=>(e[_].val,s(e))},x=e=>!v(e)||e[w]?e:new Proxy(F(e),O),D=e=>(e[p]=1,e),j=e=>e[w],K=a(m()),N=e=>new Proxy(e,{get:(e,t,r)=>a(e[t]??0)===K?{val:k(e[t].rawVal)}:i(e,t,r)}),k=e=>e?.[w]?new Proxy(N(e[w]),O):e,C=e=>e[S]=e[S].filter(e=>e.t.isConnected),E=(e,t,r,o,{t:l,f:f})=>{let a=Array.isArray(e),i=a?Number(t):t;u(l,()=>l[g][t]=f(r,()=>delete e[t],i)),a&&!o&&i!==e.length-1&&l.insertBefore(l.lastChild,l[g][n(e).find(e=>Number(e)>i)])},R=(e,t)=>{for(let r of C(e)){let e=r.t[g];e[t]?.remove(),delete e[t]}},T=r=>(e??(setTimeout(()=>(e.forEach(C),e=t),b),e=new Set)).add(r),q=(e,t,r)=>{let o={t:e instanceof Function?e():e,f:r},n=t[w];o.t[g]={},n[S].push(o),T(n);for(let[e,r]of l(n))E(t,e,r,1,o);return o.t},z=(e,t)=>{for(let[r,o]of l(t)){let t=e[r];v(t)&&v(o)?z(t,o):e[r]=o}for(let r in e)f(t,r)||delete e[r];let r=n(t),o=Array.isArray(e);if(o||n(e).some((e,t)=>e!==r[t])){let l=e[w];if(o)e.length=t.length;else{++l[_].val;let e={...l};for(let e of r)delete l[e];for(let t of r)l[t]=e[t]}for(let{t:e}of C(l)){let{firstChild:t,[g]:o}=e;for(let l of r)t===o[l]?t=t.nextSibling:e.insertBefore(o[l],t)}}return e},B=(e,n)=>{r=1;try{return z(e,n instanceof Function?Array.isArray(e)?n(e.filter(e=>1)):o(n(l(e))):n)}finally{r=t}},G=e=>Array.isArray(e)?e.filter(e=>1).map(G):v(e)?o(l(e).map(([e,t])=>[e,G(t)])):e;
export default {calc:P,reactive:x,noreactive:D,stateFields:j,raw:k,list:q,replace:B,compact:G}

View File

@ -1,6 +0,0 @@
{
"compilerOptions": {
"checkJs": true,
"lib": ["ES2024", "DOM"]
}
}

87
van.d.ts vendored Normal file
View File

@ -0,0 +1,87 @@
export {}
declare global
{
namespace Van {
interface State<T> {
val: T
readonly oldVal: T
readonly rawVal: T
}
// Defining readonly view of State<T> for covariance.
// Basically we want StateView<string> to implement StateView<string | number>
type StateView<T> = Readonly<State<T>>
type Val<T> = State<T> | T
type Primitive = string | number | boolean | bigint
// deno-lint-ignore no-explicit-any
type PropValue = Primitive | ((e: any) => void) | null
type PropValueOrDerived = PropValue | StateView<PropValue> | (() => PropValue)
type Props = Record<string, PropValueOrDerived> & { class?: PropValueOrDerived; is?: string }
type PropsWithKnownKeys<ElementType> = Partial<{[K in keyof ElementType]: PropValueOrDerived}>
type ValidChildDomValue = Primitive | Node | null | undefined
type BindingFunc = ((dom?: Node) => ValidChildDomValue) | ((dom?: Element) => Element)
type ChildDom = ValidChildDomValue | StateView<Primitive | null | undefined> | BindingFunc | readonly ChildDom[]
type TagFunc<Result> = (first?: Props & PropsWithKnownKeys<Result> | ChildDom, ...rest: readonly ChildDom[]) => Result
type Tags = Readonly<Record<string, TagFunc<Element>>> & {
[K in keyof HTMLElementTagNameMap]: TagFunc<HTMLElementTagNameMap[K]>
}
}
const van:{
readonly state: <T>(initVal: T, HMRKey?:string)=> Van.State<T>
readonly derive: <T>(f: () => T) => Van.State<T>
readonly add: (dom: Element, ...children: readonly Van.ChildDom[]) => Element
readonly tags: Van.Tags & ((namespaceURI: string) => Readonly<Record<string, Van.TagFunc<Element>>>)
readonly hydrate: <T extends Node>(dom: T, f: (dom: T) => T | null | undefined) => T
};
namespace VanX
{
type StateOf<T> = { readonly [K in keyof T]: Van.State<T[K]> }
type ValueType<T> = T extends (infer V)[] ? V : T[keyof T]
type KeyType<T> = T extends unknown[] ? number : string
type ReplacementFunc<T> =
T extends (infer V)[] ? (items: V[]) => readonly V[] :
(items: [string, T[keyof T]][]) => readonly [string, T[keyof T]][]
}
const vanX:{
readonly calc: <R>(f: () => R) => R
readonly reactive: <T extends object>(obj: T, HMRKey?:string) => T
readonly noreactive: <T extends object>(obj: T) => T
readonly stateFields: <T extends object>(obj: T) => VanX.StateOf<T>
readonly raw: <T extends object>(obj: T) => T
readonly list: <T extends object, ElementType extends Element>(container: (() => ElementType) | ElementType, items: T,itemFunc: (v: Van.State<VanX.ValueType<T>>, deleter: () => void, k: VanX.KeyType<T>) => Node) => ElementType
readonly replace: <T extends object>(obj: T, replacement: VanX.ReplacementFunc<T> | T) => T
readonly compact: <T extends object>(obj: T) => T
};
namespace Gale {
type KeyQuery = "@";
type KeyPseudo = ":";
type KeyChild = ".";
type KeyGroup = "^";
type UserStyles = Partial<CSSStyleDeclaration> & {[key: `${KeyQuery|KeyPseudo|KeyChild|KeyGroup}${string}`]: UserStyles }
type UserSheet = Record<string, UserStyles>
type CollectKeys<Obj> = {[Key in keyof Obj]: Obj[Key] extends object ? Key | CollectKeys<Obj[Key]> : Key }[keyof Obj]
type FilterKeys<Keys> = Keys extends `${KeyChild|KeyGroup}${infer Rest}` ? Keys : never
type CrossMultiply<A, B> = A extends string ? B extends string ? `${A}${B}` : never : never
type CrossMultiplyRecord<Rec> = keyof Rec | { [K in keyof Rec]: K extends string ? CrossMultiply<K, FilterKeys<CollectKeys<Rec[K]>>> : never }[keyof Rec]
type Tier = (selector:string, obj:UserStyles)=>string;
type CreateSheet = <T extends UserSheet>(sheet:UserSheet&T)=> ((...args:CrossMultiplyRecord<T>[])=>string)&{css:string}
}
const Gale:Gale.CreateSheet
}