- stdlib/html.lux: Type-safe HTML construction - stdlib/browser.lux: Browser utilities - examples/web/: Counter app with DOM manipulation - examples/counter.lux: Simple counter example Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
145 lines
5.0 KiB
JavaScript
145 lines
5.0 KiB
JavaScript
// Lux Runtime
|
|
const Lux = {
|
|
Some: (value) => ({ tag: "Some", value }),
|
|
None: () => ({ tag: "None" }),
|
|
|
|
Ok: (value) => ({ tag: "Ok", value }),
|
|
Err: (error) => ({ tag: "Err", error }),
|
|
|
|
Cons: (head, tail) => [head, ...tail],
|
|
Nil: () => [],
|
|
|
|
defaultHandlers: {
|
|
Console: {
|
|
print: (msg) => console.log(msg),
|
|
readLine: () => {
|
|
if (typeof require !== 'undefined') {
|
|
const readline = require('readline');
|
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
return new Promise(resolve => rl.question('', answer => { rl.close(); resolve(answer); }));
|
|
}
|
|
return prompt('') || '';
|
|
},
|
|
readInt: () => parseInt(Lux.defaultHandlers.Console.readLine(), 10)
|
|
},
|
|
Random: {
|
|
int: (min, max) => Math.floor(Math.random() * (max - min + 1)) + min,
|
|
bool: () => Math.random() < 0.5,
|
|
float: () => Math.random()
|
|
},
|
|
Time: {
|
|
now: () => Date.now(),
|
|
sleep: (ms) => new Promise(resolve => setTimeout(resolve, ms))
|
|
},
|
|
Http: {
|
|
get: async (url) => {
|
|
try {
|
|
const response = await fetch(url);
|
|
const body = await response.text();
|
|
const headers = [];
|
|
response.headers.forEach((v, k) => headers.push([k, v]));
|
|
return Lux.Ok({ status: response.status, body, headers });
|
|
} catch (e) {
|
|
return Lux.Err(e.message);
|
|
}
|
|
},
|
|
post: async (url, body) => {
|
|
try {
|
|
const response = await fetch(url, { method: 'POST', body });
|
|
const respBody = await response.text();
|
|
const headers = [];
|
|
response.headers.forEach((v, k) => headers.push([k, v]));
|
|
return Lux.Ok({ status: response.status, body: respBody, headers });
|
|
} catch (e) {
|
|
return Lux.Err(e.message);
|
|
}
|
|
},
|
|
postJson: async (url, json) => {
|
|
try {
|
|
const response = await fetch(url, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(json)
|
|
});
|
|
const body = await response.text();
|
|
const headers = [];
|
|
response.headers.forEach((v, k) => headers.push([k, v]));
|
|
return Lux.Ok({ status: response.status, body, headers });
|
|
} catch (e) {
|
|
return Lux.Err(e.message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Model constructors
|
|
function Counter_lux(field0) { return { tag: "Counter", field0: field0 }; }
|
|
|
|
// Msg constructors
|
|
const Increment_lux = { tag: "Increment" };
|
|
const Decrement_lux = { tag: "Decrement" };
|
|
const Reset_lux = { tag: "Reset" };
|
|
|
|
function getCount_lux(m) {
|
|
const _match_0 = m;
|
|
let _result_1;
|
|
if (_match_0.tag === "Counter") {
|
|
const n = _match_0.field0;
|
|
_result_1 = n;
|
|
} else {
|
|
throw new Error('Non-exhaustive match on ' + JSON.stringify(_match_0));
|
|
}
|
|
return _result_1;
|
|
}
|
|
|
|
function init_lux() {
|
|
return Counter_lux(0);
|
|
}
|
|
|
|
function update_lux(model, msg) {
|
|
const _match_2 = msg;
|
|
let _result_3;
|
|
if (_match_2.tag === "Increment") {
|
|
_result_3 = Counter_lux((getCount_lux(model) + 1));
|
|
} else if (_match_2.tag === "Decrement") {
|
|
_result_3 = Counter_lux((getCount_lux(model) - 1));
|
|
} else if (_match_2.tag === "Reset") {
|
|
_result_3 = Counter_lux(0);
|
|
} else {
|
|
throw new Error('Non-exhaustive match on ' + JSON.stringify(_match_2));
|
|
}
|
|
return _result_3;
|
|
}
|
|
|
|
function view_lux(model) {
|
|
const count_4 = getCount_lux(model);
|
|
return (((((((((("<div class=\"counter\">" + "<h1>Lux Counter</h1>") + "<div class=\"display\">") + String(count_4)) + "</div>") + "<div class=\"buttons\">") + "<button onclick=\"dispatch('Decrement')\">-</button>") + "<button onclick=\"dispatch('Reset')\">Reset</button>") + "<button onclick=\"dispatch('Increment')\">+</button>") + "</div>") + "</div>");
|
|
}
|
|
|
|
function luxInit_lux() {
|
|
return init_lux();
|
|
}
|
|
|
|
function luxUpdate_lux(model, msgName) {
|
|
const _match_5 = msgName;
|
|
let _result_6;
|
|
if (_match_5 === "Increment") {
|
|
_result_6 = update_lux(model, Increment_lux);
|
|
} else if (_match_5 === "Decrement") {
|
|
_result_6 = update_lux(model, Decrement_lux);
|
|
} else if (_match_5 === "Reset") {
|
|
_result_6 = update_lux(model, Reset_lux);
|
|
} else if (true) {
|
|
_result_6 = model;
|
|
} else {
|
|
throw new Error('Non-exhaustive match on ' + JSON.stringify(_match_5));
|
|
}
|
|
return _result_6;
|
|
}
|
|
|
|
function luxView_lux(model) {
|
|
return view_lux(model);
|
|
}
|
|
|