This commit is contained in:
23
.github/workflows/test.yml
vendored
Normal file
23
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
name: test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: "26.0.2"
|
||||
gleam-version: "1.3.2"
|
||||
rebar3-version: "3"
|
||||
# elixir-version: "1.15.4"
|
||||
- run: gleam deps download
|
||||
- run: gleam test
|
||||
- run: gleam format --check src test
|
||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*.beam
|
||||
*.ez
|
||||
/build
|
||||
erl_crash.dump
|
||||
24
README.md
Normal file
24
README.md
Normal file
@ -0,0 +1,24 @@
|
||||
# app
|
||||
|
||||
[](https://hex.pm/packages/app)
|
||||
[](https://hexdocs.pm/app/)
|
||||
|
||||
```sh
|
||||
gleam add app@1
|
||||
```
|
||||
```gleam
|
||||
import app
|
||||
|
||||
pub fn main() {
|
||||
// TODO: An example of the project in use
|
||||
}
|
||||
```
|
||||
|
||||
Further documentation can be found at <https://hexdocs.pm/app>.
|
||||
|
||||
## Development
|
||||
|
||||
```sh
|
||||
gleam run # Run the project
|
||||
gleam test # Run the tests
|
||||
```
|
||||
24
gleam.toml
Normal file
24
gleam.toml
Normal file
@ -0,0 +1,24 @@
|
||||
name = "app"
|
||||
version = "1.0.0"
|
||||
target = "javascript"
|
||||
|
||||
# Fill out these fields if you intend to generate HTML documentation or publish
|
||||
# your project to the Hex package manager.
|
||||
#
|
||||
# description = ""
|
||||
# licences = ["Apache-2.0"]
|
||||
# repository = { type = "github", user = "", repo = "" }
|
||||
# links = [{ title = "Website", href = "" }]
|
||||
#
|
||||
# For a full reference of all the available options, you can have a look at
|
||||
# https://gleam.run/writing-gleam/gleam-toml/.
|
||||
|
||||
[dependencies]
|
||||
gleam_stdlib = ">= 0.34.0 and < 2.0.0"
|
||||
gleam_json = "1.0.1"
|
||||
lustre = ">= 4.3.0 and < 5.0.0"
|
||||
lustre_http = ">= 0.5.2 and < 1.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
gleeunit = ">= 1.0.0 and < 2.0.0"
|
||||
lustre_dev_tools = ">= 1.3.4 and < 2.0.0"
|
||||
17
index.html
Normal file
17
index.html
Normal file
@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>🚧 app</title>
|
||||
|
||||
<!-- Uncomment this if you add the TailwindCSS integration -->
|
||||
<!-- <link rel="stylesheet" href="/priv/static/app.css> -->
|
||||
<script type="module" src="/priv/static/app.mjs"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
51
manifest.toml
Normal file
51
manifest.toml
Normal file
@ -0,0 +1,51 @@
|
||||
# This file was generated by Gleam
|
||||
# You typically do not need to edit this file
|
||||
|
||||
packages = [
|
||||
{ name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" },
|
||||
{ name = "birl", version = "1.7.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "5C66647D62BCB11FE327E7A6024907C4A17954EF22865FE0940B54A852446D01" },
|
||||
{ name = "exception", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "F5580D584F16A20B7FCDCABF9E9BE9A2C1F6AC4F9176FA6DD0B63E3B20D450AA" },
|
||||
{ name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" },
|
||||
{ name = "fs", version = "8.6.1", build_tools = ["rebar3"], requirements = [], otp_app = "fs", source = "hex", outer_checksum = "61EA2BDAEDAE4E2024D0D25C63E44DCCF65622D4402DB4A2DF12868D1546503F" },
|
||||
{ name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" },
|
||||
{ name = "gleam_community_colour", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "795964217EBEDB3DA656F5EB8F67D7AD22872EB95182042D3E7AFEF32D3FD2FE" },
|
||||
{ name = "gleam_crypto", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "ADD058DEDE8F0341F1ADE3AAC492A224F15700829D9A3A3F9ADF370F875C51B7" },
|
||||
{ name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" },
|
||||
{ name = "gleam_fetch", version = "0.4.0", build_tools = ["gleam"], requirements = ["gleam_http", "gleam_javascript", "gleam_stdlib"], otp_app = "gleam_fetch", source = "hex", outer_checksum = "7446410A44A1D1328F5BC1FF4FC9CBD1570479EA69349237B3F82E34521CCC10" },
|
||||
{ name = "gleam_http", version = "3.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "8C07DF9DF8CC7F054C650839A51C30A7D3C26482AC241C899C1CEA86B22DBE51" },
|
||||
{ name = "gleam_httpc", version = "2.2.0", build_tools = ["gleam"], requirements = ["gleam_http", "gleam_stdlib"], otp_app = "gleam_httpc", source = "hex", outer_checksum = "CF76C71002DEECF6DC5D9CA83D962728FAE166B57926BE442D827004D3C7DF1B" },
|
||||
{ name = "gleam_javascript", version = "0.11.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_javascript", source = "hex", outer_checksum = "483631D3001FCE8EB12ADEAD5E1B808440038E96F93DA7A32D326C82F480C0B2" },
|
||||
{ name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" },
|
||||
{ name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" },
|
||||
{ name = "gleam_package_interface", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_package_interface", source = "hex", outer_checksum = "CF3BFC5D0997750D9550D8D73A90F4B8D71C6C081B20ED4E70FFBE1E99AFC3C2" },
|
||||
{ name = "gleam_stdlib", version = "0.39.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "2D7DE885A6EA7F1D5015D1698920C9BAF7241102836CE0C3837A4F160128A9C4" },
|
||||
{ name = "glearray", version = "0.2.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glearray", source = "hex", outer_checksum = "9C207E05F38D724F464FA921378DB3ABC2B0A2F5821116D8BC8B2CACC68930D5" },
|
||||
{ name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
|
||||
{ name = "glint", version = "0.18.1", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_community_colour", "gleam_stdlib", "snag"], otp_app = "glint", source = "hex", outer_checksum = "5FB54D7732B4105E4AF4D89A7EE6D5E8CF33DA13A3575D0C6ECE470B97958454" },
|
||||
{ name = "glisten", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "glisten", source = "hex", outer_checksum = "CF3A9383E9BA4A8CBAF2F7B799716290D02F2AC34E7A77556B49376B662B9314" },
|
||||
{ name = "gramps", version = "2.0.3", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "3CCAA6E081225180D95C79679D383BBF51C8D1FDC1B84DA1DA444F628C373793" },
|
||||
{ name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" },
|
||||
{ name = "logging", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "FCB111401BDB4703A440A94FF8CC7DA521112269C065F219C2766998333E7738" },
|
||||
{ name = "lustre", version = "4.3.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], otp_app = "lustre", source = "hex", outer_checksum = "43642C0602D3E2D6FEC3E24173D68A1F8E646969B53A2B0A5EB61238DDA739C4" },
|
||||
{ name = "lustre_dev_tools", version = "1.3.4", build_tools = ["gleam"], requirements = ["argv", "filepath", "fs", "gleam_community_ansi", "gleam_erlang", "gleam_http", "gleam_httpc", "gleam_json", "gleam_otp", "gleam_package_interface", "gleam_stdlib", "glint", "glisten", "mist", "simplifile", "spinner", "term_size", "tom", "wisp"], otp_app = "lustre_dev_tools", source = "hex", outer_checksum = "FB056F18870EA7FE2A070264A598CFCDB8F6F24D65FF989F18F3F46C9ABEEE31" },
|
||||
{ name = "lustre_http", version = "0.5.2", build_tools = ["gleam"], requirements = ["gleam_fetch", "gleam_http", "gleam_javascript", "gleam_json", "gleam_stdlib", "lustre"], otp_app = "lustre_http", source = "hex", outer_checksum = "FB0478CBFA6B16DBE8ECA326DAE2EC15645E04900595EF2C4F039ABFA0512ABA" },
|
||||
{ name = "marceau", version = "1.2.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "5188D643C181EE350D8A20A3BDBD63AF7B6C505DE333CFBE05EF642ADD88A59B" },
|
||||
{ name = "mist", version = "1.2.0", build_tools = ["gleam"], requirements = ["birl", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "109B4D64E68C104CC23BB3CC5441ECD479DD7444889DA01113B75C6AF0F0E17B" },
|
||||
{ name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" },
|
||||
{ name = "repeatedly", version = "2.1.1", build_tools = ["gleam"], requirements = [], otp_app = "repeatedly", source = "hex", outer_checksum = "38808C3EC382B0CD981336D5879C24ECB37FCB9C1D1BD128F7A80B0F74404D79" },
|
||||
{ name = "simplifile", version = "1.7.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "1D5DFA3A2F9319EC85825F6ED88B8E449F381B0D55A62F5E61424E748E7DDEB0" },
|
||||
{ name = "snag", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "54D32E16E33655346AA3E66CBA7E191DE0A8793D2C05284E3EFB90AD2CE92BCC" },
|
||||
{ name = "spinner", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_erlang", "gleam_stdlib", "glearray", "repeatedly"], otp_app = "spinner", source = "hex", outer_checksum = "200BA3D4A04D468898E63C0D316E23F526E02514BC46454091975CB5BAE41E8F" },
|
||||
{ name = "term_size", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "term_size", source = "hex", outer_checksum = "D00BD2BC8FB3EBB7E6AE076F3F1FF2AC9D5ED1805F004D0896C784D06C6645F1" },
|
||||
{ name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" },
|
||||
{ name = "tom", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "tom", source = "hex", outer_checksum = "0831C73E45405A2153091226BF98FB485ED16376988602CC01A5FD086B82D577" },
|
||||
{ name = "wisp", version = "0.14.0", build_tools = ["gleam"], requirements = ["exception", "gleam_crypto", "gleam_erlang", "gleam_http", "gleam_json", "gleam_stdlib", "logging", "marceau", "mist", "simplifile"], otp_app = "wisp", source = "hex", outer_checksum = "9F5453AF1F9275E6F8707BC815D6A6A9DF41551921B16FBDBA52883773BAE684" },
|
||||
]
|
||||
|
||||
[requirements]
|
||||
gleam_json = { version = "1.0.1" }
|
||||
gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" }
|
||||
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
|
||||
lustre = { version = ">= 4.3.0 and < 5.0.0" }
|
||||
lustre_dev_tools = { version = ">= 1.3.4 and < 2.0.0" }
|
||||
lustre_http = { version = ">= 0.5.2 and < 1.0.0"}
|
||||
771
priv/static/app.mjs
Normal file
771
priv/static/app.mjs
Normal file
@ -0,0 +1,771 @@
|
||||
// build/dev/javascript/prelude.mjs
|
||||
var CustomType = class {
|
||||
withFields(fields) {
|
||||
let properties = Object.keys(this).map(
|
||||
(label) => label in fields ? fields[label] : this[label]
|
||||
);
|
||||
return new this.constructor(...properties);
|
||||
}
|
||||
};
|
||||
var List = class {
|
||||
static fromArray(array3, tail) {
|
||||
let t = tail || new Empty();
|
||||
for (let i = array3.length - 1; i >= 0; --i) {
|
||||
t = new NonEmpty(array3[i], t);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
[Symbol.iterator]() {
|
||||
return new ListIterator(this);
|
||||
}
|
||||
toArray() {
|
||||
return [...this];
|
||||
}
|
||||
// @internal
|
||||
atLeastLength(desired) {
|
||||
for (let _ of this) {
|
||||
if (desired <= 0)
|
||||
return true;
|
||||
desired--;
|
||||
}
|
||||
return desired <= 0;
|
||||
}
|
||||
// @internal
|
||||
hasLength(desired) {
|
||||
for (let _ of this) {
|
||||
if (desired <= 0)
|
||||
return false;
|
||||
desired--;
|
||||
}
|
||||
return desired === 0;
|
||||
}
|
||||
countLength() {
|
||||
let length4 = 0;
|
||||
for (let _ of this)
|
||||
length4++;
|
||||
return length4;
|
||||
}
|
||||
};
|
||||
function toList(elements, tail) {
|
||||
return List.fromArray(elements, tail);
|
||||
}
|
||||
var ListIterator = class {
|
||||
#current;
|
||||
constructor(current) {
|
||||
this.#current = current;
|
||||
}
|
||||
next() {
|
||||
if (this.#current instanceof Empty) {
|
||||
return { done: true };
|
||||
} else {
|
||||
let { head, tail } = this.#current;
|
||||
this.#current = tail;
|
||||
return { value: head, done: false };
|
||||
}
|
||||
}
|
||||
};
|
||||
var Empty = class extends List {
|
||||
};
|
||||
var NonEmpty = class extends List {
|
||||
constructor(head, tail) {
|
||||
super();
|
||||
this.head = head;
|
||||
this.tail = tail;
|
||||
}
|
||||
};
|
||||
var Result = class _Result extends CustomType {
|
||||
// @internal
|
||||
static isResult(data) {
|
||||
return data instanceof _Result;
|
||||
}
|
||||
};
|
||||
var Ok = class extends Result {
|
||||
constructor(value) {
|
||||
super();
|
||||
this[0] = value;
|
||||
}
|
||||
// @internal
|
||||
isOk() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
var Error = class extends Result {
|
||||
constructor(detail) {
|
||||
super();
|
||||
this[0] = detail;
|
||||
}
|
||||
// @internal
|
||||
isOk() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
function makeError(variant, module, line, fn, message, extra) {
|
||||
let error = new globalThis.Error(message);
|
||||
error.gleam_error = variant;
|
||||
error.module = module;
|
||||
error.line = line;
|
||||
error.fn = fn;
|
||||
for (let k in extra)
|
||||
error[k] = extra[k];
|
||||
return error;
|
||||
}
|
||||
|
||||
// build/dev/javascript/gleam_stdlib/gleam/option.mjs
|
||||
var None = class extends CustomType {
|
||||
};
|
||||
|
||||
// build/dev/javascript/gleam_stdlib/dict.mjs
|
||||
var tempDataView = new DataView(new ArrayBuffer(8));
|
||||
var SHIFT = 5;
|
||||
var BUCKET_SIZE = Math.pow(2, SHIFT);
|
||||
var MASK = BUCKET_SIZE - 1;
|
||||
var MAX_INDEX_NODE = BUCKET_SIZE / 2;
|
||||
var MIN_ARRAY_NODE = BUCKET_SIZE / 4;
|
||||
|
||||
// build/dev/javascript/gleam_stdlib/gleam_stdlib.mjs
|
||||
function to_string3(term) {
|
||||
return term.toString();
|
||||
}
|
||||
var unicode_whitespaces = [
|
||||
" ",
|
||||
// Space
|
||||
" ",
|
||||
// Horizontal tab
|
||||
"\n",
|
||||
// Line feed
|
||||
"\v",
|
||||
// Vertical tab
|
||||
"\f",
|
||||
// Form feed
|
||||
"\r",
|
||||
// Carriage return
|
||||
"\x85",
|
||||
// Next line
|
||||
"\u2028",
|
||||
// Line separator
|
||||
"\u2029"
|
||||
// Paragraph separator
|
||||
].join();
|
||||
var left_trim_regex = new RegExp(`^([${unicode_whitespaces}]*)`, "g");
|
||||
var right_trim_regex = new RegExp(`([${unicode_whitespaces}]*)$`, "g");
|
||||
|
||||
// build/dev/javascript/gleam_stdlib/gleam/int.mjs
|
||||
function to_string2(x) {
|
||||
return to_string3(x);
|
||||
}
|
||||
|
||||
// build/dev/javascript/gleam_stdlib/gleam/bool.mjs
|
||||
function guard(requirement, consequence, alternative) {
|
||||
if (requirement) {
|
||||
return consequence;
|
||||
} else {
|
||||
return alternative();
|
||||
}
|
||||
}
|
||||
|
||||
// build/dev/javascript/lustre/lustre/effect.mjs
|
||||
var Effect = class extends CustomType {
|
||||
constructor(all) {
|
||||
super();
|
||||
this.all = all;
|
||||
}
|
||||
};
|
||||
function none() {
|
||||
return new Effect(toList([]));
|
||||
}
|
||||
|
||||
// build/dev/javascript/lustre/lustre/internals/vdom.mjs
|
||||
var Text = class extends CustomType {
|
||||
constructor(content) {
|
||||
super();
|
||||
this.content = content;
|
||||
}
|
||||
};
|
||||
var Element = class extends CustomType {
|
||||
constructor(key, namespace, tag, attrs, children, self_closing, void$) {
|
||||
super();
|
||||
this.key = key;
|
||||
this.namespace = namespace;
|
||||
this.tag = tag;
|
||||
this.attrs = attrs;
|
||||
this.children = children;
|
||||
this.self_closing = self_closing;
|
||||
this.void = void$;
|
||||
}
|
||||
};
|
||||
var Event = class extends CustomType {
|
||||
constructor(x0, x1) {
|
||||
super();
|
||||
this[0] = x0;
|
||||
this[1] = x1;
|
||||
}
|
||||
};
|
||||
|
||||
// build/dev/javascript/lustre/lustre/attribute.mjs
|
||||
function on(name, handler) {
|
||||
return new Event("on" + name, handler);
|
||||
}
|
||||
|
||||
// build/dev/javascript/lustre/lustre/element.mjs
|
||||
function element(tag, attrs, children) {
|
||||
if (tag === "area") {
|
||||
return new Element("", "", tag, attrs, toList([]), false, true);
|
||||
} else if (tag === "base") {
|
||||
return new Element("", "", tag, attrs, toList([]), false, true);
|
||||
} else if (tag === "br") {
|
||||
return new Element("", "", tag, attrs, toList([]), false, true);
|
||||
} else if (tag === "col") {
|
||||
return new Element("", "", tag, attrs, toList([]), false, true);
|
||||
} else if (tag === "embed") {
|
||||
return new Element("", "", tag, attrs, toList([]), false, true);
|
||||
} else if (tag === "hr") {
|
||||
return new Element("", "", tag, attrs, toList([]), false, true);
|
||||
} else if (tag === "img") {
|
||||
return new Element("", "", tag, attrs, toList([]), false, true);
|
||||
} else if (tag === "input") {
|
||||
return new Element("", "", tag, attrs, toList([]), false, true);
|
||||
} else if (tag === "link") {
|
||||
return new Element("", "", tag, attrs, toList([]), false, true);
|
||||
} else if (tag === "meta") {
|
||||
return new Element("", "", tag, attrs, toList([]), false, true);
|
||||
} else if (tag === "param") {
|
||||
return new Element("", "", tag, attrs, toList([]), false, true);
|
||||
} else if (tag === "source") {
|
||||
return new Element("", "", tag, attrs, toList([]), false, true);
|
||||
} else if (tag === "track") {
|
||||
return new Element("", "", tag, attrs, toList([]), false, true);
|
||||
} else if (tag === "wbr") {
|
||||
return new Element("", "", tag, attrs, toList([]), false, true);
|
||||
} else {
|
||||
return new Element("", "", tag, attrs, children, false, false);
|
||||
}
|
||||
}
|
||||
function text(content) {
|
||||
return new Text(content);
|
||||
}
|
||||
|
||||
// build/dev/javascript/lustre/lustre/internals/runtime.mjs
|
||||
var Debug = class extends CustomType {
|
||||
constructor(x0) {
|
||||
super();
|
||||
this[0] = x0;
|
||||
}
|
||||
};
|
||||
var Dispatch = class extends CustomType {
|
||||
constructor(x0) {
|
||||
super();
|
||||
this[0] = x0;
|
||||
}
|
||||
};
|
||||
var Shutdown = class extends CustomType {
|
||||
};
|
||||
var ForceModel = class extends CustomType {
|
||||
constructor(x0) {
|
||||
super();
|
||||
this[0] = x0;
|
||||
}
|
||||
};
|
||||
|
||||
// build/dev/javascript/lustre/vdom.ffi.mjs
|
||||
function morph(prev, next, dispatch, isComponent = false) {
|
||||
let out;
|
||||
let stack = [{ prev, next, parent: prev.parentNode }];
|
||||
while (stack.length) {
|
||||
let { prev: prev2, next: next2, parent } = stack.pop();
|
||||
if (next2.subtree !== void 0)
|
||||
next2 = next2.subtree();
|
||||
if (next2.content !== void 0) {
|
||||
if (!prev2) {
|
||||
const created = document.createTextNode(next2.content);
|
||||
parent.appendChild(created);
|
||||
out ??= created;
|
||||
} else if (prev2.nodeType === Node.TEXT_NODE) {
|
||||
if (prev2.textContent !== next2.content)
|
||||
prev2.textContent = next2.content;
|
||||
out ??= prev2;
|
||||
} else {
|
||||
const created = document.createTextNode(next2.content);
|
||||
parent.replaceChild(created, prev2);
|
||||
out ??= created;
|
||||
}
|
||||
} else if (next2.tag !== void 0) {
|
||||
const created = createElementNode({
|
||||
prev: prev2,
|
||||
next: next2,
|
||||
dispatch,
|
||||
stack,
|
||||
isComponent
|
||||
});
|
||||
if (!prev2) {
|
||||
parent.appendChild(created);
|
||||
} else if (prev2 !== created) {
|
||||
parent.replaceChild(created, prev2);
|
||||
}
|
||||
out ??= created;
|
||||
} else if (next2.elements !== void 0) {
|
||||
iterateElement(next2, (fragmentElement) => {
|
||||
stack.unshift({ prev: prev2, next: fragmentElement, parent });
|
||||
prev2 = prev2?.nextSibling;
|
||||
});
|
||||
} else if (next2.subtree !== void 0) {
|
||||
stack.push({ prev: prev2, next: next2, parent });
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
function createElementNode({ prev, next, dispatch, stack }) {
|
||||
const namespace = next.namespace || "http://www.w3.org/1999/xhtml";
|
||||
const canMorph = prev && prev.nodeType === Node.ELEMENT_NODE && prev.localName === next.tag && prev.namespaceURI === (next.namespace || "http://www.w3.org/1999/xhtml");
|
||||
const el2 = canMorph ? prev : namespace ? document.createElementNS(namespace, next.tag) : document.createElement(next.tag);
|
||||
let handlersForEl;
|
||||
if (!registeredHandlers.has(el2)) {
|
||||
const emptyHandlers = /* @__PURE__ */ new Map();
|
||||
registeredHandlers.set(el2, emptyHandlers);
|
||||
handlersForEl = emptyHandlers;
|
||||
} else {
|
||||
handlersForEl = registeredHandlers.get(el2);
|
||||
}
|
||||
const prevHandlers = canMorph ? new Set(handlersForEl.keys()) : null;
|
||||
const prevAttributes = canMorph ? new Set(Array.from(prev.attributes, (a) => a.name)) : null;
|
||||
let className = null;
|
||||
let style = null;
|
||||
let innerHTML = null;
|
||||
for (const attr of next.attrs) {
|
||||
const name = attr[0];
|
||||
const value = attr[1];
|
||||
if (attr.as_property) {
|
||||
if (el2[name] !== value)
|
||||
el2[name] = value;
|
||||
if (canMorph)
|
||||
prevAttributes.delete(name);
|
||||
} else if (name.startsWith("on")) {
|
||||
const eventName = name.slice(2);
|
||||
const callback = dispatch(value);
|
||||
if (!handlersForEl.has(eventName)) {
|
||||
el2.addEventListener(eventName, lustreGenericEventHandler);
|
||||
}
|
||||
handlersForEl.set(eventName, callback);
|
||||
if (canMorph)
|
||||
prevHandlers.delete(eventName);
|
||||
} else if (name.startsWith("data-lustre-on-")) {
|
||||
const eventName = name.slice(15);
|
||||
const callback = dispatch(lustreServerEventHandler);
|
||||
if (!handlersForEl.has(eventName)) {
|
||||
el2.addEventListener(eventName, lustreGenericEventHandler);
|
||||
}
|
||||
handlersForEl.set(eventName, callback);
|
||||
el2.setAttribute(name, value);
|
||||
} else if (name === "class") {
|
||||
className = className === null ? value : className + " " + value;
|
||||
} else if (name === "style") {
|
||||
style = style === null ? value : style + value;
|
||||
} else if (name === "dangerous-unescaped-html") {
|
||||
innerHTML = value;
|
||||
} else {
|
||||
if (el2.getAttribute(name) !== value)
|
||||
el2.setAttribute(name, value);
|
||||
if (name === "value" || name === "selected")
|
||||
el2[name] = value;
|
||||
if (canMorph)
|
||||
prevAttributes.delete(name);
|
||||
}
|
||||
}
|
||||
if (className !== null) {
|
||||
el2.setAttribute("class", className);
|
||||
if (canMorph)
|
||||
prevAttributes.delete("class");
|
||||
}
|
||||
if (style !== null) {
|
||||
el2.setAttribute("style", style);
|
||||
if (canMorph)
|
||||
prevAttributes.delete("style");
|
||||
}
|
||||
if (canMorph) {
|
||||
for (const attr of prevAttributes) {
|
||||
el2.removeAttribute(attr);
|
||||
}
|
||||
for (const eventName of prevHandlers) {
|
||||
handlersForEl.delete(eventName);
|
||||
el2.removeEventListener(eventName, lustreGenericEventHandler);
|
||||
}
|
||||
}
|
||||
if (next.key !== void 0 && next.key !== "") {
|
||||
el2.setAttribute("data-lustre-key", next.key);
|
||||
} else if (innerHTML !== null) {
|
||||
el2.innerHTML = innerHTML;
|
||||
return el2;
|
||||
}
|
||||
let prevChild = el2.firstChild;
|
||||
let seenKeys = null;
|
||||
let keyedChildren = null;
|
||||
let incomingKeyedChildren = null;
|
||||
let firstChild = next.children[Symbol.iterator]().next().value;
|
||||
if (canMorph && firstChild !== void 0 && // Explicit checks are more verbose but truthy checks force a bunch of comparisons
|
||||
// we don't care about: it's never gonna be a number etc.
|
||||
firstChild.key !== void 0 && firstChild.key !== "") {
|
||||
seenKeys = /* @__PURE__ */ new Set();
|
||||
keyedChildren = getKeyedChildren(prev);
|
||||
incomingKeyedChildren = getKeyedChildren(next);
|
||||
}
|
||||
for (const child of next.children) {
|
||||
iterateElement(child, (currElement) => {
|
||||
if (currElement.key !== void 0 && seenKeys !== null) {
|
||||
prevChild = diffKeyedChild(
|
||||
prevChild,
|
||||
currElement,
|
||||
el2,
|
||||
stack,
|
||||
incomingKeyedChildren,
|
||||
keyedChildren,
|
||||
seenKeys
|
||||
);
|
||||
} else {
|
||||
stack.unshift({ prev: prevChild, next: currElement, parent: el2 });
|
||||
prevChild = prevChild?.nextSibling;
|
||||
}
|
||||
});
|
||||
}
|
||||
while (prevChild) {
|
||||
const next2 = prevChild.nextSibling;
|
||||
el2.removeChild(prevChild);
|
||||
prevChild = next2;
|
||||
}
|
||||
return el2;
|
||||
}
|
||||
var registeredHandlers = /* @__PURE__ */ new WeakMap();
|
||||
function lustreGenericEventHandler(event2) {
|
||||
const target = event2.currentTarget;
|
||||
if (!registeredHandlers.has(target)) {
|
||||
target.removeEventListener(event2.type, lustreGenericEventHandler);
|
||||
return;
|
||||
}
|
||||
const handlersForEventTarget = registeredHandlers.get(target);
|
||||
if (!handlersForEventTarget.has(event2.type)) {
|
||||
target.removeEventListener(event2.type, lustreGenericEventHandler);
|
||||
return;
|
||||
}
|
||||
handlersForEventTarget.get(event2.type)(event2);
|
||||
}
|
||||
function lustreServerEventHandler(event2) {
|
||||
const el2 = event2.currentTarget;
|
||||
const tag = el2.getAttribute(`data-lustre-on-${event2.type}`);
|
||||
const data = JSON.parse(el2.getAttribute("data-lustre-data") || "{}");
|
||||
const include = JSON.parse(el2.getAttribute("data-lustre-include") || "[]");
|
||||
switch (event2.type) {
|
||||
case "input":
|
||||
case "change":
|
||||
include.push("target.value");
|
||||
break;
|
||||
}
|
||||
return {
|
||||
tag,
|
||||
data: include.reduce(
|
||||
(data2, property) => {
|
||||
const path = property.split(".");
|
||||
for (let i = 0, o = data2, e = event2; i < path.length; i++) {
|
||||
if (i === path.length - 1) {
|
||||
o[path[i]] = e[path[i]];
|
||||
} else {
|
||||
o[path[i]] ??= {};
|
||||
e = e[path[i]];
|
||||
o = o[path[i]];
|
||||
}
|
||||
}
|
||||
return data2;
|
||||
},
|
||||
{ data }
|
||||
)
|
||||
};
|
||||
}
|
||||
function getKeyedChildren(el2) {
|
||||
const keyedChildren = /* @__PURE__ */ new Map();
|
||||
if (el2) {
|
||||
for (const child of el2.children) {
|
||||
iterateElement(child, (currElement) => {
|
||||
const key = currElement?.key || currElement?.getAttribute?.("data-lustre-key");
|
||||
if (key)
|
||||
keyedChildren.set(key, currElement);
|
||||
});
|
||||
}
|
||||
}
|
||||
return keyedChildren;
|
||||
}
|
||||
function diffKeyedChild(prevChild, child, el2, stack, incomingKeyedChildren, keyedChildren, seenKeys) {
|
||||
while (prevChild && !incomingKeyedChildren.has(prevChild.getAttribute("data-lustre-key"))) {
|
||||
const nextChild = prevChild.nextSibling;
|
||||
el2.removeChild(prevChild);
|
||||
prevChild = nextChild;
|
||||
}
|
||||
if (keyedChildren.size === 0) {
|
||||
iterateElement(child, (currChild) => {
|
||||
stack.unshift({ prev: prevChild, next: currChild, parent: el2 });
|
||||
prevChild = prevChild?.nextSibling;
|
||||
});
|
||||
return prevChild;
|
||||
}
|
||||
if (seenKeys.has(child.key)) {
|
||||
console.warn(`Duplicate key found in Lustre vnode: ${child.key}`);
|
||||
stack.unshift({ prev: null, next: child, parent: el2 });
|
||||
return prevChild;
|
||||
}
|
||||
seenKeys.add(child.key);
|
||||
const keyedChild = keyedChildren.get(child.key);
|
||||
if (!keyedChild && !prevChild) {
|
||||
stack.unshift({ prev: null, next: child, parent: el2 });
|
||||
return prevChild;
|
||||
}
|
||||
if (!keyedChild && prevChild !== null) {
|
||||
const placeholder = document.createTextNode("");
|
||||
el2.insertBefore(placeholder, prevChild);
|
||||
stack.unshift({ prev: placeholder, next: child, parent: el2 });
|
||||
return prevChild;
|
||||
}
|
||||
if (!keyedChild || keyedChild === prevChild) {
|
||||
stack.unshift({ prev: prevChild, next: child, parent: el2 });
|
||||
prevChild = prevChild?.nextSibling;
|
||||
return prevChild;
|
||||
}
|
||||
el2.insertBefore(keyedChild, prevChild);
|
||||
stack.unshift({ prev: keyedChild, next: child, parent: el2 });
|
||||
return prevChild;
|
||||
}
|
||||
function iterateElement(element2, processElement) {
|
||||
if (element2.elements !== void 0) {
|
||||
for (const currElement of element2.elements) {
|
||||
processElement(currElement);
|
||||
}
|
||||
} else {
|
||||
processElement(element2);
|
||||
}
|
||||
}
|
||||
|
||||
// build/dev/javascript/lustre/client-runtime.ffi.mjs
|
||||
var LustreClientApplication2 = class _LustreClientApplication {
|
||||
#root = null;
|
||||
#queue = [];
|
||||
#effects = [];
|
||||
#didUpdate = false;
|
||||
#isComponent = false;
|
||||
#model = null;
|
||||
#update = null;
|
||||
#view = null;
|
||||
static start(flags, selector, init3, update2, view2) {
|
||||
if (!is_browser())
|
||||
return new Error(new NotABrowser());
|
||||
const root2 = selector instanceof HTMLElement ? selector : document.querySelector(selector);
|
||||
if (!root2)
|
||||
return new Error(new ElementNotFound(selector));
|
||||
const app = new _LustreClientApplication(init3(flags), update2, view2, root2);
|
||||
return new Ok((msg) => app.send(msg));
|
||||
}
|
||||
constructor([model, effects], update2, view2, root2 = document.body, isComponent = false) {
|
||||
this.#model = model;
|
||||
this.#update = update2;
|
||||
this.#view = view2;
|
||||
this.#root = root2;
|
||||
this.#effects = effects.all.toArray();
|
||||
this.#didUpdate = true;
|
||||
this.#isComponent = isComponent;
|
||||
window.requestAnimationFrame(() => this.#tick());
|
||||
}
|
||||
send(action) {
|
||||
switch (true) {
|
||||
case action instanceof Dispatch: {
|
||||
this.#queue.push(action[0]);
|
||||
this.#tick();
|
||||
return;
|
||||
}
|
||||
case action instanceof Shutdown: {
|
||||
this.#shutdown();
|
||||
return;
|
||||
}
|
||||
case action instanceof Debug: {
|
||||
this.#debug(action[0]);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
emit(event2, data) {
|
||||
this.#root.dispatchEvent(
|
||||
new CustomEvent(event2, {
|
||||
bubbles: true,
|
||||
detail: data,
|
||||
composed: true
|
||||
})
|
||||
);
|
||||
}
|
||||
#tick() {
|
||||
this.#flush_queue();
|
||||
if (this.#didUpdate) {
|
||||
const vdom = this.#view(this.#model);
|
||||
const dispatch = (handler) => (e) => {
|
||||
const result = handler(e);
|
||||
if (result instanceof Ok) {
|
||||
this.send(new Dispatch(result[0]));
|
||||
}
|
||||
};
|
||||
this.#didUpdate = false;
|
||||
this.#root = morph(this.#root, vdom, dispatch, this.#isComponent);
|
||||
}
|
||||
}
|
||||
#flush_queue(iterations = 0) {
|
||||
while (this.#queue.length) {
|
||||
const [next, effects] = this.#update(this.#model, this.#queue.shift());
|
||||
this.#didUpdate ||= this.#model !== next;
|
||||
this.#model = next;
|
||||
this.#effects = this.#effects.concat(effects.all.toArray());
|
||||
}
|
||||
while (this.#effects.length) {
|
||||
this.#effects.shift()(
|
||||
(msg) => this.send(new Dispatch(msg)),
|
||||
(event2, data) => this.emit(event2, data)
|
||||
);
|
||||
}
|
||||
if (this.#queue.length) {
|
||||
if (iterations < 5) {
|
||||
this.#flush_queue(++iterations);
|
||||
} else {
|
||||
window.requestAnimationFrame(() => this.#tick());
|
||||
}
|
||||
}
|
||||
}
|
||||
#debug(action) {
|
||||
switch (true) {
|
||||
case action instanceof ForceModel: {
|
||||
const vdom = this.#view(action[0]);
|
||||
const dispatch = (handler) => (e) => {
|
||||
const result = handler(e);
|
||||
if (result instanceof Ok) {
|
||||
this.send(new Dispatch(result[0]));
|
||||
}
|
||||
};
|
||||
this.#queue = [];
|
||||
this.#effects = [];
|
||||
this.#didUpdate = false;
|
||||
this.#root = morph(this.#root, vdom, dispatch, this.#isComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
#shutdown() {
|
||||
this.#root.remove();
|
||||
this.#root = null;
|
||||
this.#model = null;
|
||||
this.#queue = [];
|
||||
this.#effects = [];
|
||||
this.#didUpdate = false;
|
||||
this.#update = () => {
|
||||
};
|
||||
this.#view = () => {
|
||||
};
|
||||
}
|
||||
};
|
||||
var start = (app, selector, flags) => LustreClientApplication2.start(
|
||||
flags,
|
||||
selector,
|
||||
app.init,
|
||||
app.update,
|
||||
app.view
|
||||
);
|
||||
var is_browser = () => globalThis.window && window.document;
|
||||
|
||||
// build/dev/javascript/lustre/lustre.mjs
|
||||
var App = class extends CustomType {
|
||||
constructor(init3, update2, view2, on_attribute_change) {
|
||||
super();
|
||||
this.init = init3;
|
||||
this.update = update2;
|
||||
this.view = view2;
|
||||
this.on_attribute_change = on_attribute_change;
|
||||
}
|
||||
};
|
||||
var ElementNotFound = class extends CustomType {
|
||||
constructor(selector) {
|
||||
super();
|
||||
this.selector = selector;
|
||||
}
|
||||
};
|
||||
var NotABrowser = class extends CustomType {
|
||||
};
|
||||
function application(init3, update2, view2) {
|
||||
return new App(init3, update2, view2, new None());
|
||||
}
|
||||
function start3(app, selector, flags) {
|
||||
return guard(
|
||||
!is_browser(),
|
||||
new Error(new NotABrowser()),
|
||||
() => {
|
||||
return start(app, selector, flags);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// build/dev/javascript/lustre/lustre/element/html.mjs
|
||||
function div(attrs, children) {
|
||||
return element("div", attrs, children);
|
||||
}
|
||||
function button(attrs, children) {
|
||||
return element("button", attrs, children);
|
||||
}
|
||||
|
||||
// build/dev/javascript/lustre/lustre/event.mjs
|
||||
function on2(name, handler) {
|
||||
return on(name, handler);
|
||||
}
|
||||
function on_click(msg) {
|
||||
return on2("click", (_) => {
|
||||
return new Ok(msg);
|
||||
});
|
||||
}
|
||||
|
||||
// build/dev/javascript/app/app.mjs
|
||||
var Increment = class extends CustomType {
|
||||
};
|
||||
var Decrement = class extends CustomType {
|
||||
};
|
||||
function init2(_) {
|
||||
return [0, none()];
|
||||
}
|
||||
function update(model, msg) {
|
||||
if (msg instanceof Increment) {
|
||||
return [model + 1, none()];
|
||||
} else {
|
||||
return [model - 1, none()];
|
||||
}
|
||||
}
|
||||
function view(model) {
|
||||
let count = to_string2(model);
|
||||
return div(
|
||||
toList([]),
|
||||
toList([
|
||||
button(
|
||||
toList([on_click(new Increment())]),
|
||||
toList([text("+")])
|
||||
),
|
||||
text(count),
|
||||
button(
|
||||
toList([on_click(new Decrement())]),
|
||||
toList([text("-")])
|
||||
)
|
||||
])
|
||||
);
|
||||
}
|
||||
function main() {
|
||||
let app = application(init2, update, view);
|
||||
let $ = start3(app, "#app", void 0);
|
||||
if (!$.isOk()) {
|
||||
throw makeError(
|
||||
"assignment_no_match",
|
||||
"app",
|
||||
12,
|
||||
"main",
|
||||
"Assignment pattern did not match",
|
||||
{ value: $ }
|
||||
);
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
|
||||
// build/.lustre/entry.mjs
|
||||
main();
|
||||
54
src/app.gleam
Normal file
54
src/app.gleam
Normal file
@ -0,0 +1,54 @@
|
||||
import gleam/int
|
||||
import gleam/io
|
||||
import lustre
|
||||
import lustre/effect
|
||||
import lustre/element
|
||||
import lustre/element/html
|
||||
import lustre/event
|
||||
import lustre_http
|
||||
|
||||
pub fn main() {
|
||||
let app = lustre.application(init, update, view)
|
||||
let assert Ok(_) = lustre.start(app, "#app", Nil)
|
||||
|
||||
Nil
|
||||
}
|
||||
|
||||
pub type Incident {
|
||||
Incident(number: String, brief_description: String)
|
||||
}
|
||||
|
||||
pub type Model =
|
||||
List(Incident)
|
||||
|
||||
pub fn init(_flags) -> #(Model, effect.Effect(Msg)) {
|
||||
#(0, effect.none())
|
||||
}
|
||||
|
||||
pub type Msg {
|
||||
TopdeskReturnedTickets(Result(List(Incident), lustre_http.HttpError))
|
||||
UserRefreshedTickets
|
||||
}
|
||||
|
||||
pub fn update(model: Model, msg: Msg) -> #(Model, effect.Effect(Msg)) {
|
||||
case msg {
|
||||
UserRefreshedTickets -> #(model, get_tickets())
|
||||
TopdeskReturnedTickets(Ok(incidents)) -> #(incidents, effect.none())
|
||||
TopdeskReturnedTickets(Error(_)) -> #(model, effect.none())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_tickets() -> effect.Effect(Msg) {
|
||||
let url = "https://support.kembit.nl/"
|
||||
let decoder = dynamic
|
||||
}
|
||||
|
||||
pub fn view(model: Model) -> element.Element(Msg) {
|
||||
let count = int.to_string(model)
|
||||
|
||||
html.div([], [
|
||||
html.button([event.on_click(Increment)], [element.text("+")]),
|
||||
element.text(count),
|
||||
html.button([event.on_click(Decrement)], [element.text("-")]),
|
||||
])
|
||||
}
|
||||
12
test/app_test.gleam
Normal file
12
test/app_test.gleam
Normal file
@ -0,0 +1,12 @@
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
// gleeunit test functions end in `_test`
|
||||
pub fn hello_world_test() {
|
||||
1
|
||||
|> should.equal(1)
|
||||
}
|
||||
Reference in New Issue
Block a user