aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorHayleigh Thompson <me@hayleigh.dev>2024-01-23 00:09:45 +0000
committerGitHub <noreply@github.com>2024-01-23 00:09:45 +0000
commit24f6962aa457d32319756f6217aafde7b0a9c752 (patch)
tree42119d9b073f56eabe9dda4ae2065ef4b2086e6a /examples
parent45e671ac32de95ae1a0a9f9e98da8645d01af3cf (diff)
downloadlustre-24f6962aa457d32319756f6217aafde7b0a9c752.tar.gz
lustre-24f6962aa457d32319756f6217aafde7b0a9c752.zip
✨ Add universal components that can run on the server (#39)
* :heavy_plus_sign: Add gleam_erlang gleam_otp and gleam_json dependencies. * :sparkles: Add json encoders for elememnts and attributes. * :sparkles: Add the ability to perform an effect with a custom dispatch function. * :construction: Experiment with a server-side component runtime. * :construction: Expose special server click events. * :construction: Experiment with a server-side component runtime. * :construction: Experiment with a server-side component runtime. * :construction: Experiment with a server-side component runtime. * :construction: Create a basic server component client bundle. * :construction: Create a basic server component demo. * :bug: Fixed a bug where the runtime stopped performing patches. * :refactor: Roll back introduction of shadow dom. * :recycle: Refactor to Custom Element-based approach to encapsulating server components. * :truck: Move some things around. * :sparkles: Add a minified version of the server component runtime. * :wrench: Add lustre/server/* to internal modules. * :recycle: on_attribute_change and on_client_event handlers are now functions not dicts. * :recycle: Refactor server component event handling to no longer need explicit tags. * :fire: Remove unnecessary attempt to stringify events. * :memo: Start documeint lustre/server functions. * :construction: Experiment with a js implementation of the server component backend runtime. * :recycle: Experiment with an API that makes heavier use of conditional complilation. * :recycle: Big refactor to unify server components, client components, and client apps. * :bug: Fixed some bugs with client runtimes. * :recycle: Update examples to new lustre api/ * :truck: Move server demo into examples/ folder/ * :wrench: Add lustre/runtime to internal modules. * :construction: Experiment with a diffing implementation. * :wrench: Hide internal modules from docs. * :heavy_plus_sign: Update deps to latest versions. * :recycle: Move diffing and vdom code into separate internal modules. * :sparkles: Bring server components to feature parity with client components. * :recycle: Update server component demo. * :bug: Fix bug where attribute changes weren't properly broadcast. * :fire: Remove unused 'Patch' type. * :recycle: Stub out empty js implementations so we can build for js. * :memo: Docs for the docs gods. * :recycle: Rename lustre.server_component to lustre.component.
Diffstat (limited to 'examples')
-rw-r--r--examples/components/gleam.toml2
-rw-r--r--examples/components/manifest.toml6
-rw-r--r--examples/components/src/components.gleam15
-rw-r--r--examples/counter/gleam.toml2
-rw-r--r--examples/counter/manifest.toml6
-rw-r--r--examples/events/manifest.toml6
-rw-r--r--examples/input/manifest.toml6
-rw-r--r--examples/nested/manifest.toml6
-rw-r--r--examples/nested/src/nested.gleam2
-rw-r--r--examples/nested/src/nested/counter.gleam53
-rw-r--r--examples/server_demo/README.md22
-rw-r--r--examples/server_demo/gleam.toml13
-rw-r--r--examples/server_demo/manifest.toml32
-rw-r--r--examples/server_demo/src/demo.gleam22
-rw-r--r--examples/server_demo/src/demo/app.gleam88
-rw-r--r--examples/server_demo/src/demo/socket.gleam78
-rw-r--r--examples/server_demo/src/demo/web.gleam58
-rw-r--r--examples/svg/manifest.toml6
18 files changed, 405 insertions, 18 deletions
diff --git a/examples/components/gleam.toml b/examples/components/gleam.toml
index 131d773..3cac691 100644
--- a/examples/components/gleam.toml
+++ b/examples/components/gleam.toml
@@ -4,4 +4,4 @@ target = "javascript"
[dependencies]
gleam_stdlib = "~> 0.34"
-lustre = { path = "../../" } \ No newline at end of file
+lustre = { path = "../../" }
diff --git a/examples/components/manifest.toml b/examples/components/manifest.toml
index 715dadc..1c72364 100644
--- a/examples/components/manifest.toml
+++ b/examples/components/manifest.toml
@@ -2,8 +2,12 @@
# You typically do not need to edit this file
packages = [
+ { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" },
+ { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["thoas", "gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" },
+ { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" },
{ name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" },
- { name = "lustre", version = "3.0.12", build_tools = ["gleam"], requirements = ["gleam_stdlib"], source = "local", path = "../.." },
+ { name = "lustre", version = "3.1.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], source = "local", path = "../.." },
+ { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" },
]
[requirements]
diff --git a/examples/components/src/components.gleam b/examples/components/src/components.gleam
index 85fc583..03400bf 100644
--- a/examples/components/src/components.gleam
+++ b/examples/components/src/components.gleam
@@ -16,23 +16,20 @@ import lustre/event
// MAIN ------------------------------------------------------------------------
pub fn main() {
- let assert Ok(_) =
+ let counter =
lustre.component(
- "custom-counter",
counter_init,
counter_update,
counter_view,
dict.from_list([
- #(
- "count",
- fn(attr) {
- dynamic.int(attr)
- |> result.map(GotCount)
- },
- ),
+ #("count", fn(attr) {
+ dynamic.int(attr)
+ |> result.map(GotCount)
+ }),
]),
)
+ let assert Ok(_) = lustre.register(counter, "custom-counter")
// A `simple` lustre application doesn't produce `Effect`s. These are best to
// start with if you're just getting started with lustre or you know you don't
// need the runtime to manage any side effects.
diff --git a/examples/counter/gleam.toml b/examples/counter/gleam.toml
index 2a5f0f5..e6bddb8 100644
--- a/examples/counter/gleam.toml
+++ b/examples/counter/gleam.toml
@@ -4,4 +4,4 @@ target = "javascript"
[dependencies]
gleam_stdlib = "~> 0.34"
-lustre = { path = "../../" } \ No newline at end of file
+lustre = { path = "../../" }
diff --git a/examples/counter/manifest.toml b/examples/counter/manifest.toml
index 715dadc..1c72364 100644
--- a/examples/counter/manifest.toml
+++ b/examples/counter/manifest.toml
@@ -2,8 +2,12 @@
# You typically do not need to edit this file
packages = [
+ { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" },
+ { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["thoas", "gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" },
+ { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" },
{ name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" },
- { name = "lustre", version = "3.0.12", build_tools = ["gleam"], requirements = ["gleam_stdlib"], source = "local", path = "../.." },
+ { name = "lustre", version = "3.1.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], source = "local", path = "../.." },
+ { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" },
]
[requirements]
diff --git a/examples/events/manifest.toml b/examples/events/manifest.toml
index 715dadc..4116093 100644
--- a/examples/events/manifest.toml
+++ b/examples/events/manifest.toml
@@ -2,8 +2,12 @@
# You typically do not need to edit this file
packages = [
+ { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" },
+ { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" },
+ { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" },
{ name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" },
- { name = "lustre", version = "3.0.12", build_tools = ["gleam"], requirements = ["gleam_stdlib"], source = "local", path = "../.." },
+ { name = "lustre", version = "3.1.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], source = "local", path = "../.." },
+ { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" },
]
[requirements]
diff --git a/examples/input/manifest.toml b/examples/input/manifest.toml
index 715dadc..16bdd27 100644
--- a/examples/input/manifest.toml
+++ b/examples/input/manifest.toml
@@ -2,8 +2,12 @@
# You typically do not need to edit this file
packages = [
+ { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" },
+ { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" },
+ { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_erlang"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" },
{ name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" },
- { name = "lustre", version = "3.0.12", build_tools = ["gleam"], requirements = ["gleam_stdlib"], source = "local", path = "../.." },
+ { name = "lustre", version = "3.1.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], source = "local", path = "../.." },
+ { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" },
]
[requirements]
diff --git a/examples/nested/manifest.toml b/examples/nested/manifest.toml
index 715dadc..6593d5e 100644
--- a/examples/nested/manifest.toml
+++ b/examples/nested/manifest.toml
@@ -2,8 +2,12 @@
# You typically do not need to edit this file
packages = [
+ { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" },
+ { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["thoas", "gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" },
+ { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_erlang"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" },
{ name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" },
- { name = "lustre", version = "3.0.12", build_tools = ["gleam"], requirements = ["gleam_stdlib"], source = "local", path = "../.." },
+ { name = "lustre", version = "3.1.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], source = "local", path = "../.." },
+ { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" },
]
[requirements]
diff --git a/examples/nested/src/nested.gleam b/examples/nested/src/nested.gleam
index 89e68f0..2e1b9ec 100644
--- a/examples/nested/src/nested.gleam
+++ b/examples/nested/src/nested.gleam
@@ -1,6 +1,6 @@
// IMPORTS ---------------------------------------------------------------------
-import examples/counter
+import nested/counter
import gleam/list
import gleam/map.{type Map}
import gleam/pair
diff --git a/examples/nested/src/nested/counter.gleam b/examples/nested/src/nested/counter.gleam
new file mode 100644
index 0000000..37af39a
--- /dev/null
+++ b/examples/nested/src/nested/counter.gleam
@@ -0,0 +1,53 @@
+// IMPORTS ---------------------------------------------------------------------
+
+import gleam/int
+import lustre
+import lustre/element.{type Element, text}
+import lustre/element/html.{button, div, p}
+import lustre/event
+
+// MAIN ------------------------------------------------------------------------
+
+pub fn main() {
+ // A `simple` lustre application doesn't produce `Effect`s. These are best to
+ // start with if you're just getting started with lustre or you know you don't
+ // need the runtime to manage any side effects.
+ let app = lustre.simple(init, update, view)
+ let assert Ok(_) = lustre.start(app, "[data-lustre-app]", Nil)
+}
+
+// MODEL -----------------------------------------------------------------------
+
+pub type Model =
+ Int
+
+pub fn init(_) -> Model {
+ 0
+}
+
+// UPDATE ----------------------------------------------------------------------
+
+pub opaque type Msg {
+ Incr
+ Decr
+ Reset
+}
+
+pub fn update(model: Model, msg: Msg) -> Model {
+ case msg {
+ Incr -> model + 1
+ Decr -> model - 1
+ Reset -> 0
+ }
+}
+
+// VIEW ------------------------------------------------------------------------
+
+pub fn view(model: Model) -> Element(Msg) {
+ div([], [
+ button([event.on_click(Incr)], [text("+")]),
+ button([event.on_click(Decr)], [text("-")]),
+ button([event.on_click(Reset)], [text("Reset")]),
+ p([], [text(int.to_string(model))]),
+ ])
+}
diff --git a/examples/server_demo/README.md b/examples/server_demo/README.md
new file mode 100644
index 0000000..c0d6a2a
--- /dev/null
+++ b/examples/server_demo/README.md
@@ -0,0 +1,22 @@
+# server_demo
+
+[![Package Version](https://img.shields.io/hexpm/v/server_demo)](https://hex.pm/packages/server_demo)
+[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/server_demo/)
+
+## Quick start
+
+```sh
+gleam run # Run the project
+gleam test # Run the tests
+gleam shell # Run an Erlang shell
+```
+
+## Installation
+
+If available on Hex this package can be added to your Gleam project:
+
+```sh
+gleam add server_demo
+```
+
+and its documentation can be found at <https://hexdocs.pm/server_demo>.
diff --git a/examples/server_demo/gleam.toml b/examples/server_demo/gleam.toml
new file mode 100644
index 0000000..81a3557
--- /dev/null
+++ b/examples/server_demo/gleam.toml
@@ -0,0 +1,13 @@
+name = "demo"
+version = "1.0.0"
+
+[dependencies]
+gleam_erlang = "~> 0.23"
+gleam_http = "~> 3.5"
+gleam_json = "~> 0.7"
+gleam_otp = "~> 0.8"
+gleam_stdlib = "~> 0.34"
+lustre = { path = "../../" }
+lustre_ui = "~> 0.2"
+mist = "~> 0.15"
+wisp = "~> 0.8"
diff --git a/examples/server_demo/manifest.toml b/examples/server_demo/manifest.toml
new file mode 100644
index 0000000..d78134c
--- /dev/null
+++ b/examples/server_demo/manifest.toml
@@ -0,0 +1,32 @@
+# This file was generated by Gleam
+# You typically do not need to edit this file
+
+packages = [
+ { name = "exception", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "984401CFC95BCA87C391E36194D2B9E5B946467D44893FADB1CA4ACD4B7A29CE" },
+ { name = "gleam_community_colour", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "A49A5E3AE8B637A5ACBA80ECB9B1AFE89FD3D5351FF6410A42B84F666D40D7D5" },
+ { name = "gleam_crypto", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "DE1FC4E631CA374AB29CCAEAC043EE171B86114D7DC66DD483F0A93BF0C4C6FF" },
+ { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" },
+ { name = "gleam_http", version = "3.5.3", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "C2FC3322203B16F897C1818D9810F5DEFCE347F0751F3B44421E1261277A7373" },
+ { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" },
+ { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" },
+ { name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" },
+ { name = "glisten", version = "0.9.2", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_erlang", "gleam_otp"], otp_app = "glisten", source = "hex", outer_checksum = "C960B6CF25D4AABAB01211146E9B57E11827B9C49E4175217E0FB7EF5BCB0FF7" },
+ { name = "lustre", version = "3.1.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], source = "local", path = "../.." },
+ { name = "lustre_ui", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib", "lustre"], otp_app = "lustre_ui", source = "hex", outer_checksum = "F81BE5D20153CFFC717C2C4687A19375A8613528908AF7069DDA1B929C8398B1" },
+ { name = "marceau", version = "1.1.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "1AAD727A30BE0F95562C3403BB9B27C823797AD90037714255EEBF617B1CDA81" },
+ { name = "mist", version = "0.15.0", build_tools = ["gleam"], requirements = ["gleam_otp", "gleam_erlang", "gleam_stdlib", "gleam_http", "glisten"], otp_app = "mist", source = "hex", outer_checksum = "49F51DDB64D7B2832F72727CC9721C478D6B524C96EA444C601A19D01E023C03" },
+ { name = "simplifile", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "359CD7006E2F69255025C858CCC6407C11A876EC179E6ED1E46809E8DC6B1AAD" },
+ { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" },
+ { name = "wisp", version = "0.10.0", build_tools = ["gleam"], requirements = ["marceau", "gleam_json", "simplifile", "gleam_http", "gleam_crypto", "mist", "gleam_stdlib", "exception", "gleam_erlang"], otp_app = "wisp", source = "hex", outer_checksum = "744FF91702078301BDF8FE76F26C14B658A7D151D867FA6995762318ED2536A0" },
+]
+
+[requirements]
+gleam_erlang = { version = "~> 0.23" }
+gleam_http = { version = "~> 3.5" }
+gleam_json = { version = "~> 0.7" }
+gleam_otp = { version = "~> 0.8" }
+gleam_stdlib = { version = "~> 0.34" }
+lustre = { path = "../../" }
+lustre_ui = { version = "~> 0.2" }
+mist = { version = "~> 0.15" }
+wisp = { version = "~> 0.8" }
diff --git a/examples/server_demo/src/demo.gleam b/examples/server_demo/src/demo.gleam
new file mode 100644
index 0000000..ebe858a
--- /dev/null
+++ b/examples/server_demo/src/demo.gleam
@@ -0,0 +1,22 @@
+// IMPORTS ---------------------------------------------------------------------
+
+import demo/socket
+import demo/web
+import gleam/erlang/process
+import mist
+
+// MAIN ------------------------------------------------------------------------
+
+pub fn main() {
+ let assert Ok(_) =
+ mist.new(fn(req) {
+ case req.path {
+ "/ws" -> socket.handle(req)
+ _ -> web.handle(req)
+ }
+ })
+ |> mist.port(8000)
+ |> mist.start_http
+
+ process.sleep_forever()
+}
diff --git a/examples/server_demo/src/demo/app.gleam b/examples/server_demo/src/demo/app.gleam
new file mode 100644
index 0000000..13d09a8
--- /dev/null
+++ b/examples/server_demo/src/demo/app.gleam
@@ -0,0 +1,88 @@
+// IMPORTS ---------------------------------------------------------------------
+
+import gleam/dict.{type Dict}
+import gleam/dynamic.{type Decoder}
+import gleam/int
+import gleam/json
+import gleam/result
+import lustre/attribute
+import lustre/effect.{type Effect}
+import lustre/element.{type Element}
+import lustre/element/html
+import lustre/event
+import lustre/server
+import lustre/ui
+
+// MODEL -----------------------------------------------------------------------
+
+pub type Model =
+ Int
+
+pub fn init(count: Int) -> #(Model, Effect(Msg)) {
+ #(count, effect.none())
+}
+
+// UPDATE ----------------------------------------------------------------------
+
+pub opaque type Msg {
+ Incr
+ Decr
+ Reset(Int)
+}
+
+pub fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) {
+ case msg {
+ Incr -> #(model + 1, effect.none())
+ Decr -> #(model - 1, effect.none())
+ Reset(count) -> #(
+ count,
+ effect.event(
+ "changed",
+ json.string("You reset the count to: " <> int.to_string(count)),
+ ),
+ )
+ }
+}
+
+pub fn on_attribute_change() -> Dict(String, Decoder(Msg)) {
+ dict.from_list([
+ #("count", fn(dyn) {
+ dyn
+ |> dynamic.int
+ |> result.map(Reset)
+ }),
+ ])
+}
+
+// VIEW ------------------------------------------------------------------------
+
+pub fn view(model: Model) -> Element(Msg) {
+ let count = int.to_string(model)
+
+ ui.centre(
+ [attribute.style([#("width", "100vw"), #("height", "100vh")])],
+ ui.sequence([], [
+ ui.button([event.on_click(Decr)], [element.text("-")]),
+ ui.centre([], html.span([], [element.text(count)])),
+ ui.button([event.on_click(Incr)], [element.text("+")]),
+ ]),
+ )
+ // ui.cluster([], [
+ // ui.input([event.on_input(Change), attribute.value(model.input)]),
+ // html.span([], [element.text(model.input)]),
+ // ]),
+ // ui.centre(
+ // [
+ // event.on("mousemove", on_mouse_move),
+ // server.include(["offsetX", "offsetY"]),
+ // attribute.style([
+ // #("aspect-ratio", "1 / 1 "),
+ // #("background-color", "var(--element-background)"),
+ // ]),
+ // ],
+ // html.div([], [
+ // html.p([], [element.text("x: " <> int.to_string(model.mouse.0))]),
+ // html.p([], [element.text("y: " <> int.to_string(model.mouse.1))]),
+ // ]),
+ // ),
+}
diff --git a/examples/server_demo/src/demo/socket.gleam b/examples/server_demo/src/demo/socket.gleam
new file mode 100644
index 0000000..bc43962
--- /dev/null
+++ b/examples/server_demo/src/demo/socket.gleam
@@ -0,0 +1,78 @@
+// IMPORTS ---------------------------------------------------------------------
+
+import demo/app
+import gleam/bit_array
+import gleam/erlang/process.{type Subject}
+import gleam/function.{identity}
+import gleam/http/request.{type Request as HttpRequest}
+import gleam/http/response.{type Response as HttpResponse}
+import gleam/json
+import gleam/option.{Some}
+import gleam/otp/actor
+import gleam/result
+import lustre.{type Action, type ServerComponent}
+import lustre/element.{type Patch}
+import lustre/server
+import mist.{
+ type Connection, type ResponseData, type WebsocketConnection,
+ type WebsocketMessage,
+}
+
+//
+
+pub fn handle(req: HttpRequest(Connection)) -> HttpResponse(ResponseData) {
+ mist.websocket(req, update, init, function.constant(Nil))
+}
+
+type Model(flags, model, msg) {
+ Model(self: Subject(Patch(msg)), app: Subject(Action(ServerComponent, msg)))
+}
+
+fn init(_) {
+ let assert Ok(app) =
+ lustre.component(app.init, app.update, app.view, app.on_attribute_change())
+ |> lustre.start_actor(0)
+ let self = process.new_subject()
+ let model = Model(self, app)
+ let selector = process.selecting(process.new_selector(), self, identity)
+
+ actor.send(app, lustre.add_renderer(process.self(), process.send(self, _)))
+ #(model, Some(selector))
+}
+
+fn update(
+ model: Model(flags, model, msg),
+ conn: WebsocketConnection,
+ msg: WebsocketMessage(Patch(a)),
+) {
+ case msg {
+ mist.Text(bits) -> {
+ let _ = {
+ use dyn <- json.decode_bits(bits)
+ use action <- result.try(server.decode_action(dyn))
+
+ actor.send(model.app, action)
+ Ok(Nil)
+ }
+
+ actor.continue(model)
+ }
+ mist.Binary(_) -> actor.continue(model)
+ mist.Closed -> {
+ actor.send(model.app, lustre.remove_renderer(process.self()))
+ actor.continue(model)
+ }
+ mist.Shutdown -> {
+ actor.send(model.app, lustre.shutdown())
+ actor.Stop(process.Normal)
+ }
+ mist.Custom(patch) -> {
+ element.encode_patch(patch)
+ |> json.to_string
+ |> bit_array.from_string
+ |> mist.send_text_frame(conn, _)
+
+ actor.continue(model)
+ }
+ }
+}
diff --git a/examples/server_demo/src/demo/web.gleam b/examples/server_demo/src/demo/web.gleam
new file mode 100644
index 0000000..b8c17e2
--- /dev/null
+++ b/examples/server_demo/src/demo/web.gleam
@@ -0,0 +1,58 @@
+// IMPORTS ---------------------------------------------------------------------
+
+import gleam/http/request.{type Request as HttpRequest}
+import gleam/http/response.{type Response as HttpResponse}
+import gleam/result
+import gleam/string_builder
+import lustre/attribute.{attribute}
+import lustre/element
+import lustre/element/html.{html}
+import lustre/server
+import lustre/ui/styles
+import mist.{type Connection, type ResponseData}
+import wisp.{type Request, type Response}
+
+//
+
+pub fn handle(req: HttpRequest(Connection)) -> HttpResponse(ResponseData) {
+ wisp.mist_handler(handler, "")(req)
+}
+
+fn handler(req: Request) -> Response {
+ use req <- wisp.handle_head(req)
+ use <- wisp.serve_static(req, under: "/static", from: static_directory())
+
+ html([attribute("lang", "en")], [
+ html.head([], [
+ html.meta([attribute("charset", "utf-8")]),
+ html.meta([
+ attribute("name", "viewport"),
+ attribute("content", "width=device-width, initial-scale=1"),
+ ]),
+ styles.elements(),
+ html.script(
+ [attribute("type", "module")],
+ "
+ import '/static/lustre-server-component.mjs'
+
+ document.addEventListener('DOMContentLoaded', () => {
+ document
+ .querySelector('lustre-server-component')
+ .addEventListener('alert', event => {
+ console.log(`The server count says: ${event.detail}`)
+ })
+ })
+ ",
+ ),
+ ]),
+ html.body([], [server.component([server.route("/ws")])]),
+ ])
+ |> element.to_string_builder
+ |> string_builder.prepend("<!DOCTYPE html>\n")
+ |> wisp.html_response(200)
+}
+
+fn static_directory() -> String {
+ wisp.priv_directory("lustre")
+ |> result.unwrap("")
+}
diff --git a/examples/svg/manifest.toml b/examples/svg/manifest.toml
index 715dadc..1c72364 100644
--- a/examples/svg/manifest.toml
+++ b/examples/svg/manifest.toml
@@ -2,8 +2,12 @@
# You typically do not need to edit this file
packages = [
+ { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" },
+ { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["thoas", "gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" },
+ { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" },
{ name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" },
- { name = "lustre", version = "3.0.12", build_tools = ["gleam"], requirements = ["gleam_stdlib"], source = "local", path = "../.." },
+ { name = "lustre", version = "3.1.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], source = "local", path = "../.." },
+ { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" },
]
[requirements]