feat: add stdlib and browser examples
- 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>
This commit is contained in:
144
examples/web/counter.js
Normal file
144
examples/web/counter.js
Normal file
@@ -0,0 +1,144 @@
|
||||
// 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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user