Update website to be 100% static

-1
.dockerignore
··· 1 - fly.toml
-3
.gitmodules
··· 1 - [submodule "pages"] 2 - path = pages 3 - url = git@codeberg.org:naomi/pages
-6
Caddyfile
··· 1 - :8080 { 2 - root * /srv 3 - encode gzip 4 - try_files {path} /index.html 5 - file_server 6 - }
-4
Dockerfile
··· 1 - FROM caddy:2.8-alpine 2 - COPY ./Caddyfile /etc/caddy/Caddyfile 3 - COPY ./index.html /srv/index.html 4 - COPY ./priv/ /srv/priv/
-22
fly.toml
··· 1 - # fly.toml app configuration file generated for naomieow-xyz on 2024-05-29T21:30:13+01:00 2 - # 3 - # See https://fly.io/docs/reference/configuration/ for information about how to use this file. 4 - # 5 - 6 - app = 'naomieow-xyz' 7 - primary_region = 'lhr' 8 - 9 - [build] 10 - 11 - [http_service] 12 - internal_port = 8080 13 - force_https = true 14 - auto_stop_machines = true 15 - auto_start_machines = true 16 - min_machines_running = 0 17 - processes = ['app'] 18 - 19 - [[vm]] 20 - memory = '1gb' 21 - cpu_kind = 'shared' 22 - cpus = 1
+5 -6
gleam.toml
··· 1 1 name = "website" 2 - target = "javascript" 3 2 version = "1.0.0" 4 3 5 4 [dependencies] 6 5 gleam_stdlib = ">= 0.34.0 and < 2.0.0" 7 6 lustre = ">= 4.2.0 and < 5.0.0" 8 - modem = ">= 1.1.0 and < 2.0.0" 9 - lustre_ui = ">= 0.6.0 and < 1.0.0" 10 - birl = ">= 1.7.0 and < 2.0.0" 11 - lustre_http = ">= 0.5.2 and < 1.0.0" 7 + tom = ">= 0.3.0 and < 1.0.0" 8 + simplifile = ">= 1.7.0 and < 2.0.0" 9 + commonmark = ">= 0.1.8 and < 1.0.0" 10 + glailglind = ">= 1.1.2 and < 2.0.0" 11 + lustre_ssg = ">= 0.8.0 and < 1.0.0" 12 12 13 13 [dev-dependencies] 14 14 gleeunit = ">= 1.0.0 and < 2.0.0" 15 - lustre_dev_tools = ">= 1.3.2 and < 2.0.0"
+6
ls.json
··· 1 + { 2 + "serve": "priv", 3 + "serveRedirect": true, 4 + "port": 1234, 5 + "bind": "localhost" 6 + }
+10 -33
manifest.toml
··· 2 2 # You typically do not need to edit this file 3 3 4 4 packages = [ 5 - { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, 6 - { name = "birl", version = "1.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "B1FA529E7BE3FF12CADF32814AB8EC7294E74CEDEE8CC734505707B929A98985" }, 7 - { name = "exception", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "F5580D584F16A20B7FCDCABF9E9BE9A2C1F6AC4F9176FA6DD0B63E3B20D450AA" }, 5 + { name = "commonmark", version = "0.1.8", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "commonmark", source = "hex", outer_checksum = "D20FD8F1B147C3F9288FDB84CC790B5136FFD9D4BD04E121BE851B7EB02A6566" }, 8 6 { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, 9 - { name = "fs", version = "8.6.1", build_tools = ["rebar3"], requirements = [], otp_app = "fs", source = "hex", outer_checksum = "61EA2BDAEDAE4E2024D0D25C63E44DCCF65622D4402DB4A2DF12868D1546503F" }, 10 - { 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" }, 11 - { 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" }, 12 - { name = "gleam_crypto", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "ADD058DEDE8F0341F1ADE3AAC492A224F15700829D9A3A3F9ADF370F875C51B7" }, 7 + { name = "glailglind", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_http", "gleam_httpc", "gleam_stdlib", "shellout", "simplifile", "tom"], otp_app = "glailglind", source = "hex", outer_checksum = "DD037D129592B871E943EAB9C137CE3B017C784D3DEB8180A2954D113D4EE479" }, 13 8 { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, 14 - { 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" }, 15 9 { name = "gleam_http", version = "3.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "8C07DF9DF8CC7F054C650839A51C30A7D3C26482AC241C899C1CEA86B22DBE51" }, 16 10 { name = "gleam_httpc", version = "2.2.0", build_tools = ["gleam"], requirements = ["gleam_http", "gleam_stdlib"], otp_app = "gleam_httpc", source = "hex", outer_checksum = "CF76C71002DEECF6DC5D9CA83D962728FAE166B57926BE442D827004D3C7DF1B" }, 17 - { name = "gleam_javascript", version = "0.8.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_javascript", source = "hex", outer_checksum = "14D5B7E1A70681E0776BF0A0357F575B822167960C844D3D3FA114D3A75F05A8" }, 18 11 { name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" }, 19 12 { name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, 20 - { name = "gleam_package_interface", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_package_interface", source = "hex", outer_checksum = "52A721BCA972C8099BB881195D821AAA64B9F2655BECC102165D5A1097731F01" }, 21 13 { name = "gleam_stdlib", version = "0.37.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "5398BD6C2ABA17338F676F42F404B9B7BABE1C8DC7380031ACB05BBE1BCF3742" }, 22 - { name = "glearray", version = "0.2.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glearray", source = "hex", outer_checksum = "9C207E05F38D724F464FA921378DB3ABC2B0A2F5821116D8BC8B2CACC68930D5" }, 23 14 { name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" }, 24 - { 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" }, 25 - { name = "glisten", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "glisten", source = "hex", outer_checksum = "CF3A9383E9BA4A8CBAF2F7B799716290D02F2AC34E7A77556B49376B662B9314" }, 26 - { name = "gramps", version = "2.0.1", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "FBB7EA641C8A1EF02C0E938B6045A1360B925E5E65BAD0E228C4AEF6C6933722" }, 27 - { name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" }, 28 - { name = "logging", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "A996064F04EF6E67F0668FD0ACFB309830B05D0EE3A0C11BBBD2D4464334F792" }, 15 + { name = "jot", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "jot", source = "hex", outer_checksum = "A929403A34DC43422BA50D9BE0B61447DE4F0AD62215BB8C640445370C02EC8B" }, 29 16 { name = "lustre", version = "4.2.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], otp_app = "lustre", source = "hex", outer_checksum = "258F876CD7AB12C2C773F1A30F76DFC0A0ED989B720070DF32FC0717A6A0E60C" }, 30 - { name = "lustre_dev_tools", version = "1.3.2", 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 = "CC8F46BCE51C1349862C5F6BA0075B0C68096B866ED1C520B60358FAAB398B60" }, 31 - { 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" }, 32 - { name = "lustre_ui", version = "0.6.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_json", "gleam_stdlib", "lustre"], otp_app = "lustre_ui", source = "hex", outer_checksum = "FA1F9E89D89CDD5DF376ED86ABA8A38441CB2E664CD4D402F22A49DA4D7BB56D" }, 33 - { name = "marceau", version = "1.1.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "1AAD727A30BE0F95562C3403BB9B27C823797AD90037714255EEBF617B1CDA81" }, 34 - { 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" }, 35 - { name = "modem", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "lustre"], otp_app = "modem", source = "hex", outer_checksum = "4C6E448089B09A57C179455D44526A717E4E217D4000B91201617FD2D9F18E68" }, 36 - { name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" }, 37 - { name = "repeatedly", version = "2.1.1", build_tools = ["gleam"], requirements = [], otp_app = "repeatedly", source = "hex", outer_checksum = "38808C3EC382B0CD981336D5879C24ECB37FCB9C1D1BD128F7A80B0F74404D79" }, 17 + { name = "lustre_ssg", version = "0.8.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "jot", "lustre", "simplifile", "tom"], otp_app = "lustre_ssg", source = "hex", outer_checksum = "46BC70203D7730314E2CB78A3F7237F1753D86B05F4B7B26722F7FAB7345047C" }, 18 + { name = "shellout", version = "1.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "shellout", source = "hex", outer_checksum = "E2FCD18957F0E9F67E1F497FC9FF57393392F8A9BAEAEA4779541DE7A68DD7E0" }, 38 19 { name = "simplifile", version = "1.7.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "1D5DFA3A2F9319EC85825F6ED88B8E449F381B0D55A62F5E61424E748E7DDEB0" }, 39 - { name = "snag", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "54D32E16E33655346AA3E66CBA7E191DE0A8793D2C05284E3EFB90AD2CE92BCC" }, 40 - { 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" }, 41 - { name = "term_size", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "term_size", source = "hex", outer_checksum = "D00BD2BC8FB3EBB7E6AE076F3F1FF2AC9D5ED1805F004D0896C784D06C6645F1" }, 42 20 { name = "thoas", version = "1.2.0", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "540C8CB7D9257F2AD0A14145DC23560F91ACDCA995F0CCBA779EB33AF5D859D1" }, 43 21 { name = "tom", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "tom", source = "hex", outer_checksum = "0831C73E45405A2153091226BF98FB485ED16376988602CC01A5FD086B82D577" }, 44 - { 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" }, 45 22 ] 46 23 47 24 [requirements] 48 - birl = { version = ">= 1.7.0 and < 2.0.0" } 25 + commonmark = { version = ">= 0.1.8 and < 1.0.0" } 26 + glailglind = { version = ">= 1.1.2 and < 2.0.0" } 49 27 gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } 50 28 gleeunit = { version = ">= 1.0.0 and < 2.0.0" } 51 29 lustre = { version = ">= 4.2.0 and < 5.0.0" } 52 - lustre_dev_tools = { version = ">= 1.3.2 and < 2.0.0" } 53 - lustre_http = { version = ">= 0.5.2 and < 1.0.0"} 54 - lustre_ui = { version = ">= 0.6.0 and < 1.0.0" } 55 - modem = { version = ">= 1.1.0 and < 2.0.0" } 30 + lustre_ssg = { version = ">= 0.8.0 and < 1.0.0" } 31 + simplifile = { version = ">= 1.7.0 and < 2.0.0" } 32 + tom = { version = ">= 0.3.0 and < 1.0.0" }
+65
src/build.gleam
··· 1 + import gleam/dict 2 + import gleam/io 3 + import gleam/list 4 + import gleam/result 5 + import gleam/string 6 + import lustre/ssg 7 + import tailwind 8 + import website/common 9 + 10 + import website/page/index 11 + import website/page/post 12 + import website/page/project 13 + 14 + import website/data/posts 15 + import website/data/projects 16 + 17 + pub fn main() { 18 + let projects = 19 + dict.from_list( 20 + projects.all() 21 + |> list.map(fn(project) { #(project.id, project) }), 22 + ) 23 + let posts = 24 + dict.from_list( 25 + posts.all() 26 + |> list.map(fn(post) { #(post.id, post) }), 27 + ) 28 + 29 + let build = 30 + ssg.new("./priv") 31 + |> ssg.add_static_route("/", index.view() |> common.wrapper("Home", _)) 32 + |> ssg.add_static_route( 33 + "/projects", 34 + project.view_all(projects.all()) |> common.wrapper("Projects", _), 35 + ) 36 + |> ssg.add_dynamic_route("/projects", projects, fn(p) { 37 + project.view(p) |> common.wrapper(p.title, _) 38 + }) 39 + |> ssg.add_static_route( 40 + "/posts", 41 + post.view_all(posts.all()) |> common.wrapper("Posts", _), 42 + ) 43 + |> ssg.add_dynamic_route("/posts", posts, fn(p) { 44 + post.view(p) |> common.wrapper(p.title, _) 45 + }) 46 + |> ssg.use_index_routes 47 + |> ssg.add_static_dir("./src/static/") 48 + |> ssg.build 49 + |> result.map_error(string.inspect) 50 + |> result.try(fn(_) { 51 + [ 52 + "--config=tailwind.config.js", "--input=./src/input.css", 53 + "--output=./priv/style.css", 54 + ] 55 + |> tailwind.run 56 + }) 57 + 58 + case build { 59 + Ok(_) -> io.println("Build succeeded!") 60 + Error(e) -> { 61 + io.debug(e) 62 + io.println("Build failed!") 63 + } 64 + } 65 + }
+12 -1
src/input.css
··· 1 1 @tailwind base; 2 2 @tailwind components; 3 - @tailwind utilities; 3 + @tailwind utilities; 4 + 5 + * { 6 + font-family: "Recursive", sans-serif; 7 + font-optical-sizing: auto; 8 + font-style: normal; 9 + font-variation-settings: 10 + "slnt" 0, 11 + "CASL" 0, 12 + "CRSV" 0.5, 13 + "MONO" 0; 14 + }
+4
src/static/.domains
··· 1 + lesbian.skin 2 + naomieow.xyz 3 + www.lesbian.skin 4 + www.naomieow.xyz
src/static/assets/ages.png

This is a binary file and will not be displayed.

src/static/assets/caramel.png

This is a binary file and will not be displayed.

src/static/assets/cfs.png

This is a binary file and will not be displayed.

src/static/assets/curseforge_64h.png

This is a binary file and will not be displayed.

src/static/assets/git_64h.png

This is a binary file and will not be displayed.

src/static/assets/ibo.webp

This is a binary file and will not be displayed.

src/static/assets/lilypad.png

This is a binary file and will not be displayed.

src/static/assets/modrinth_64h.png

This is a binary file and will not be displayed.

src/static/assets/pricked.png

This is a binary file and will not be displayed.

src/static/assets/titles.png

This is a binary file and will not be displayed.

+2 -543
src/website.gleam
··· 1 - import birl 2 - import gleam/dynamic 3 - import gleam/int 4 - import gleam/list 5 - import gleam/string 6 - import gleam/uri.{type Uri} 7 - import lustre 8 - import lustre/attribute as attr 9 - import lustre/effect.{type Effect} 10 - import lustre/element.{type Element} 11 - import lustre/element/html 12 - import lustre/event 13 - import lustre/ui 14 - import lustre/ui/layout/cluster 15 - import lustre_http 16 - import modem 17 - import website/common 18 - import website/posts 19 - import website/projects 20 - import gleam/order 21 - 22 - // Main 1 + import gleam/io 23 2 24 3 pub fn main() { 25 - let app = lustre.application(init, update, view) 26 - let assert Ok(dispatch) = lustre.start(app, "#app", Nil) 27 - 28 - dispatch 29 - } 30 - 31 - // Model 32 - 33 - type Model { 34 - Model( 35 - current_route: Route, 36 - posts: List(posts.Post(Msg)), 37 - projects: List(projects.Project(Msg)), 38 - repos: List(Repository), 39 - ) 40 - } 41 - 42 - type Route { 43 - Home 44 - Posts 45 - Post(id: String) 46 - Projects 47 - Project(id: String) 48 - } 49 - 50 - @external(javascript, "./ffi.mjs", "get_route") 51 - fn do_get_route() -> String 52 - 53 - @external(javascript, "./ffi.mjs", "init_theme") 54 - fn init_theme() -> Bool 55 - 56 - @external(javascript, "./ffi.mjs", "is_dark_mode") 57 - fn is_dark_mode() -> Bool 58 - 59 - @external(javascript, "./ffi.mjs", "switch_theme") 60 - fn do_switch_theme() -> Nil 61 - 62 - type Repository { 63 - Repository( 64 - avatar_url: String, 65 - description: String, 66 - name: String, 67 - stars_count: Int, 68 - html_url: String, 69 - updated_at: String, 70 - ) 71 - } 72 - 73 - type RepositoryBirled { 74 - RepositoryBirled( 75 - avatar_url: String, 76 - description: String, 77 - name: String, 78 - stars_count: Int, 79 - html_url: String, 80 - updated_at: birl.Time, 81 - ) 82 - } 83 - 84 - fn get_repositories() -> Effect(Msg) { 85 - let url = "https://codeberg.org/api/v1/users/naomi/repos" 86 - let decoder = 87 - dynamic.list(of: dynamic.decode6( 88 - Repository, 89 - dynamic.field("avatar_url", dynamic.string), 90 - dynamic.field("description", dynamic.string), 91 - dynamic.field("name", dynamic.string), 92 - dynamic.field("stars_count", dynamic.int), 93 - dynamic.field("html_url", dynamic.string), 94 - dynamic.field("updated_at", dynamic.string), 95 - )) 96 - lustre_http.get(url, lustre_http.expect_json(decoder, ApiGotRepositories)) 97 - } 98 - 99 - fn sort_repos(repos: List(Repository)) -> List(RepositoryBirled) { 100 - let repos = 101 - repos 102 - |> list.map(fn(repo) { 103 - case repo { 104 - Repository(a, b, c, d, e, time) -> { 105 - let time = case time |> birl.parse { 106 - Ok(time) -> time 107 - Error(_) -> { 108 - birl.now() |> birl.set_day(birl.Day(1, 1, 1970)) 109 - } 110 - } 111 - RepositoryBirled(a, b, c, d, e, time) 112 - } 113 - } 114 - }) 115 - |> list.sort(fn(a, b) { 116 - let a = a.updated_at |> birl.get_day 117 - let b = b.updated_at |> birl.get_day 118 - order.break_tie(order.break_tie(int.compare(a.year, b.year), int.compare(a.month, b.month)), int.compare(a.date, b.date)) 119 - }) 120 - |> list.reverse 121 - } 122 - 123 - fn switch_theme() -> Effect(Msg) { 124 - effect.from(fn(disp) { 125 - do_switch_theme() 126 - DoneChangeDarkMode 127 - |> disp 128 - }) 129 - } 130 - 131 - fn get_route() -> Route { 132 - case do_get_route() |> string.split("/") { 133 - ["", "projects", id] | ["", "project", id] -> Project(id) 134 - ["", "projects"] -> Projects 135 - ["", "posts", id] | ["", "post", id] -> Post(id) 136 - ["", "posts"] -> Posts 137 - _ -> Home 138 - } 139 - } 140 - 141 - fn init(_flags) -> #(Model, Effect(Msg)) { 142 - #( 143 - Model( 144 - current_route: get_route(), 145 - projects: projects.all(), 146 - posts: posts.all(), 147 - repos: [], 148 - ), 149 - { 150 - init_theme() 151 - effect.batch([get_repositories(), modem.init(on_route_change)]) 152 - }, 153 - ) 154 - } 155 - 156 - fn on_route_change(uri: Uri) -> Msg { 157 - case uri.path_segments(uri.path) { 158 - ["projects", id] | ["project", id] -> OnRouteChange(Project(id)) 159 - ["projects"] -> OnRouteChange(Projects) 160 - ["posts", id] | ["post", id] -> OnRouteChange(Post(id)) 161 - ["posts"] -> OnRouteChange(Posts) 162 - _ -> OnRouteChange(Home) 163 - } 164 - } 165 - 166 - // Update 167 - 168 - pub opaque type Msg { 169 - OnRouteChange(Route) 170 - ChangeDarkMode 171 - DoneChangeDarkMode 172 - ApiGotRepositories(Result(List(Repository), lustre_http.HttpError)) 173 - } 174 - 175 - fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) { 176 - case msg { 177 - OnRouteChange(route) -> #( 178 - Model(..model, current_route: route), 179 - effect.none(), 180 - ) 181 - ChangeDarkMode -> #(model, switch_theme()) 182 - DoneChangeDarkMode -> #(model, effect.none()) 183 - ApiGotRepositories(Ok(repos)) -> #( 184 - Model(..model, repos: repos), 185 - effect.none(), 186 - ) 187 - 188 - ApiGotRepositories(Error(_)) -> #(model, effect.none()) 189 - } 190 - } 191 - 192 - // View 193 - 194 - fn view(model: Model) -> Element(Msg) { 195 - let page = case model.current_route { 196 - Home -> view_home(model) 197 - Projects -> view_projects(model) 198 - Project(id) -> view_project(model, id) 199 - Posts -> view_posts(model) 200 - Post(id) -> view_post(model, id) 201 - } 202 - 203 - // padding: 3vh; 204 - ui.stack( 205 - [ 206 - attr.class( 207 - "font-inter font-normal not-italic min-h-screen p-5 bg-gray-100 dark:bg-neutral-900", 208 - ), 209 - ], 210 - [ 211 - html.div( 212 - [attr.class("mx-0 sm:mx-6 md:mx-10 lg:mx-20 xl:mx-48 2xl:mx-64")], 213 - [view_navbar(model), page], 214 - ), 215 - ], 216 - ) 217 - } 218 - 219 - fn view_navbar(model: Model) -> Element(Msg) { 220 - let view_nav_item = fn(path, text) { 221 - html.a( 222 - [ 223 - attr.href("/" <> path), 224 - attr.class( 225 - "drop-shadow-md no-underline bg-gray-300 dark:bg-neutral-900 p-3 rounded-xl text-black dark:text-neutral-200", 226 - ), 227 - ], 228 - [element.text(text)], 229 - ) 230 - } 231 - 232 - html.span([attr.class("drop-shadow-md flex gap-4 w-full")], [ 233 - // margin-bottom: 3vh; 234 - cluster.of( 235 - html.nav, 236 - [ 237 - attr.class( 238 - "flex rounded-xl p-4 items-center gap-4 border-0 grow mb-5 bg-gray-200 dark:bg-neutral-800", 239 - ), 240 - ], 241 - [ 242 - html.h1([attr.class("hidden md:block text-3xl m-0 text-pink-400")], [ 243 - element.text("naomieow"), 244 - ]), 245 - view_nav_item("", "Home"), 246 - view_nav_item("projects", "Projects"), 247 - view_nav_item("posts", "Posts"), 248 - ], 249 - ), 250 - ui.button( 251 - [ 252 - // width: 6%; 253 - // margin-bottom: 3vh; 254 - attr.class( 255 - "min-w-9 text-3xl border-0 aspect-square items-center mb-5 p-4 rounded-xl bg-gray-200 dark:bg-neutral-800", 256 - ), 257 - event.on_click(ChangeDarkMode), 258 - ], 259 - [ 260 - html.h1([attr.class("m-0")], [ 261 - case is_dark_mode() { 262 - True -> element.text("🌕") 263 - False -> element.text("☀️") 264 - }, 265 - ]), 266 - ], 267 - ), 268 - ]) 269 - } 270 - 271 - fn view_home(model: Model) -> Element(Msg) { 272 - html.div([], [ 273 - html.div( 274 - [ 275 - attr.class( 276 - "drop-shadow-md text-xl p-4 mb-4 rounded-xl bg-gray-200 dark:bg-neutral-800 dark:text-neutral-200", 277 - ), 278 - ], 279 - [ 280 - html.p([attr.class("mb-4")], [ 281 - html.h1([attr.class("text-3xl m-0 mb-4 text-pink-400")], [ 282 - element.text("About Me"), 283 - ]), 284 - element.text( 285 - "Hi! I'm Naomi (or Mia), a trans girl from the UK who loves to code! I mostly make Minecraft mods, but have started 286 - to begin other projects like ", 287 - ), 288 - common.link("Sheetr", "https://sheetr.app/"), 289 - element.text(" and "), 290 - common.link("Lilypad", "https://codeberg.org/naomi/lilypad"), 291 - element.text( 292 - ". Both projects are in their infancy, but I hope that they can both become useful to someone 293 - out there!", 294 - ), 295 - ]), 296 - html.p([], [ 297 - element.text( 298 - "I absolutely love learning new languages, so here is what I know:", 299 - ), 300 - cluster.of( 301 - html.div, 302 - [attr.class("gap-4 sm:gap-20 flex flex-col md:flex-row")], 303 - [ 304 - ui.stack([], [ 305 - html.h3([attr.class("text-2xl underline")], [ 306 - element.text("Proficient"), 307 - ]), 308 - html.ul([attr.class("list-disc pl-8")], [ 309 - html.li([], [element.text("⭐ Gleam")]), 310 - html.li([], [element.text("☕ Java")]), 311 - html.li([], [element.text("📜 JavaScript")]), 312 - html.li([], [element.text("🐍 Python")]), 313 - html.li([], [element.text("❓mcfunction")]), 314 - ]), 315 - ]), 316 - ui.stack([], [ 317 - html.h3([attr.class("text-2xl underline")], [ 318 - element.text("Learning"), 319 - ]), 320 - html.ul([attr.class("list-disc pl-8")], [ 321 - html.li([], [element.text("🦀 Rust")]), 322 - html.li([], [element.text("🟦 Go")]), 323 - html.li([], [element.text("🎮 C#")]), 324 - html.li([], [element.text("💀 C++")]), 325 - ]), 326 - ]), 327 - ui.stack([], [ 328 - html.h3([attr.class("text-2xl underline")], [ 329 - element.text("Want to learn"), 330 - ]), 331 - html.ul([attr.class("list-disc pl-8")], [ 332 - html.li([], [element.text("⚡ Zig")]), 333 - html.li([], [element.text("♻️ Kotlin")]), 334 - ]), 335 - ]), 336 - ], 337 - ), 338 - ]), 339 - // html.p([], [ 340 - // element.text("As of this coming September, I will be studying a BSc in Computer Games Programming @ "), 341 - // common.link("Staffs", "https://staffs.ac.uk/") 342 - // ]) 343 - ], 344 - ), 345 - html.div( 346 - [ 347 - attr.class( 348 - "drop-shadow-md text-xl p-4 mb-4 rounded-xl bg-gray-200 dark:bg-neutral-800 dark:text-neutral-200", 349 - ), 350 - ], 351 - [ 352 - html.h1([attr.class("text-3xl m-0 mb-4 text-pink-400")], [ 353 - element.text("Repositories"), 354 - ]), 355 - html.div( 356 - [attr.class("grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4")], 357 - model.repos 358 - |> sort_repos 359 - |> list.map(fn(repo) { 360 - let url = case repo.avatar_url { 361 - "" -> "https://avatars.githubusercontent.com/u/95784613?v=4" 362 - a -> a 363 - } 364 - html.a( 365 - [ 366 - attr.class( 367 - " 368 - drop-shadow-lg rounded-xl p-4 369 - dark:text-neutral-200 370 - bg-gray-300 dark:bg-neutral-700 371 - hover:bg-slate-300 dark:hover:bg-slate-700 372 - ", 373 - ), 374 - attr.href(repo.html_url), 375 - ], 376 - [ 377 - html.div([attr.class("flex items-center")], [ 378 - html.img([ 379 - attr.src(url), 380 - attr.class("drop-shadow-md rounded-xl max-w-16 max-h-16"), 381 - ]), 382 - html.div([attr.class("m-2")], [ 383 - html.h2([], [element.text(repo.name)]), 384 - html.h2([], [ 385 - element.text( 386 - "⭐ " <> repo.stars_count |> int.to_string, 387 - ), 388 - ]), 389 - ]), 390 - ]), 391 - html.div([], [ 392 - case repo.description { 393 - "" -> html.p([attr.class("text-neutral-500 dark:text-neutral-400")], [element.text("No description")]) 394 - a -> element.text(a) 395 - } 396 - ]), 397 - ], 398 - ) 399 - }), 400 - ), 401 - ], 402 - ), 403 - ]) 404 - } 405 - 406 - fn view_projects(model: Model) -> Element(Msg) { 407 - let projects = 408 - model.projects 409 - |> list.map(fn(project: projects.Project(Msg)) { 410 - ui.stack( 411 - [ 412 - attr.id("project-" <> project.id), 413 - attr.class( 414 - "drop-shadow-md text-xl rounded-xl p-4 mb-4 bg-gray-200 dark:bg-neutral-800 dark:text-neutral-200", 415 - ), 416 - ], 417 - [ 418 - cluster.of(html.div, [attr.class("flex items-center gap-4 pb-1.5")], [ 419 - html.img([ 420 - attr.src(project.img), 421 - attr.class("rounded-xl max-w-16 max-h-16"), 422 - ]), 423 - html.h1([attr.class("text-2xl")], [ 424 - common.link(project.title, "projects/" <> project.id), 425 - ]), 426 - cluster.of( 427 - html.div, 428 - [attr.class("flex items-center gap-2")], 429 - project.links 430 - |> list.map(fn(link) { 431 - html.a([attr.href(link.url), attr.alt(link.title)], [ 432 - html.img([ 433 - attr.src(link.img), 434 - attr.class("rounded-xl max-w-14 max-h-14"), 435 - ]), 436 - ]) 437 - }), 438 - ), 439 - ]), 440 - html.p([], [project.summary]), 441 - ], 442 - ) 443 - }) 444 - html.div([], projects) 445 - } 446 - 447 - fn view_project(model: Model, id: String) -> Element(Msg) { 448 - let project = 449 - model.projects 450 - |> list.find(fn(project) { project.id == id }) 451 - html.div( 452 - [ 453 - attr.class( 454 - "drop-shadow-md text-xl rounded-xl p-4 mb-4 bg-gray-200 dark:bg-neutral-800 dark:text-neutral-200", 455 - ), 456 - ], 457 - case project { 458 - Ok(project) -> [ 459 - cluster.of(html.div, [attr.class("flex items-center gap-4 pb-1.5")], [ 460 - html.img([ 461 - attr.src(project.img), 462 - attr.class("rounded-xl max-w-16 max-h-16"), 463 - ]), 464 - html.h1([attr.class("text-2xl")], [element.text(project.title)]), 465 - ]), 466 - html.p([], [project.summary]), 467 - html.h2([], [ 468 - element.text("More information will be added eventually.."), 469 - ]), 470 - ] 471 - Error(_) -> [ 472 - html.h1([attr.class("text-center")], [element.text("Invalid project!")]), 473 - ] 474 - }, 475 - ) 476 - } 477 - 478 - fn view_posts(model: Model) -> Element(Msg) { 479 - let posts = 480 - model.posts 481 - |> list.map(fn(post: posts.Post(Msg)) { 482 - ui.stack( 483 - [ 484 - attr.class( 485 - "drop-shadow-md text-xl rounded-xl p-4 mb-4 bg-gray-200 dark:bg-neutral-800 dark:text-neutral-200", 486 - ), 487 - ], 488 - [ 489 - cluster.of(html.div, [attr.class("flex items-center gap-4")], [ 490 - html.h1([attr.class("text-2xl")], [ 491 - common.link(post.title, "posts/" <> post.id), 492 - ]), 493 - html.p([attr.class("text-neutral-500")], [ 494 - element.text("Author: " <> post.author), 495 - ]), 496 - html.p([attr.class("text-neutral-500")], [ 497 - element.text( 498 - "Date Posted: " <> post.date_posted |> common.day_to_string, 499 - ), 500 - ]), 501 - ]), 502 - html.p([], [post.summary]), 503 - ], 504 - ) 505 - }) 506 - html.div([], [ 507 - common.warning( 508 - "Quick Apology!", 509 - "Due to not having a proper markdown renderer implmented, the posts currently look a bit awful as I'm too lazy to style them manually. Sorry if this makes it harder to read!", 510 - ), 511 - ..posts 512 - ]) 513 - } 514 - 515 - fn view_post(model: Model, id: String) -> Element(Msg) { 516 - let post = 517 - model.posts 518 - |> list.find(fn(post) { post.id == id }) 519 - html.div( 520 - [ 521 - attr.class( 522 - "drop-shadow-md text-xl rounded-xl p-4 mb-4 bg-gray-200 dark:bg-neutral-800 dark:text-neutral-200", 523 - ), 524 - ], 525 - case post { 526 - Ok(post) -> [ 527 - cluster.of(html.div, [attr.class("flex items-center gap-4")], [ 528 - html.h1([], [element.text(post.title)]), 529 - html.p([attr.class("text-neutral-500")], [ 530 - element.text("Author: " <> post.author), 531 - ]), 532 - html.p([attr.class("text-neutral-500")], [ 533 - element.text( 534 - "Date Posted: " <> post.date_posted |> common.day_to_string, 535 - ), 536 - ]), 537 - ]), 538 - html.hr([]), 539 - html.div([], [post.content]), 540 - ] 541 - Error(_) -> [ 542 - html.h1([attr.class("text-center")], [element.text("Invalid post!")]), 543 - ] 544 - }, 545 - ) 4 + io.println("run `gleam run -m build` to generate the site") 546 5 }
+114 -5
src/website/common.gleam
··· 1 - import birl.{type Day} 2 1 import gleam/int 3 2 import lustre/attribute as attr 4 3 import lustre/element 5 4 import lustre/element/html 5 + import tom 6 + import website/md 7 + 8 + pub type Post(a) { 9 + Post( 10 + id: String, 11 + title: String, 12 + author: String, 13 + date_posted: tom.Date, 14 + summary: element.Element(a), 15 + content: element.Element(a), 16 + ) 17 + } 18 + 19 + pub type Link { 20 + Link(title: String, url: String, img: String) 21 + } 22 + 23 + pub type Project(a) { 24 + Project( 25 + id: String, 26 + title: String, 27 + author: String, 28 + img: String, 29 + links: List(Link), 30 + archived: Bool, 31 + summary: element.Element(a), 32 + content: element.Element(a), 33 + ) 34 + } 35 + 36 + pub fn wrapper( 37 + page: String, 38 + elements: List(element.Element(a)), 39 + ) -> element.Element(a) { 40 + html.html([attr.attribute("lang", "en")], [ 41 + html.head([], [ 42 + html.meta([attr.attribute("charset", "UTF-8")]), 43 + html.link([attr.rel("stylesheet"), attr.href("/style.css")]), 44 + html.link([ 45 + attr.rel("preconnect"), 46 + attr.href("https://fonts.googleapis.com"), 47 + attr.attribute("crossorigin", ""), 48 + ]), 49 + html.link([ 50 + attr.rel("preconnect"), 51 + attr.href("https://fonts.gstatic.com"), 52 + attr.attribute("crossorigin", ""), 53 + ]), 54 + html.link([ 55 + attr.rel("stylesheet"), 56 + attr.href( 57 + "https://fonts.googleapis.com/css2?family=Recursive:wght@300..1000&display=swap", 58 + ), 59 + ]), 60 + html.title([], page <> " | Naomi Roberts"), 61 + ]), 62 + html.body( 63 + [ 64 + attr.class( 65 + "my-0 sm:my-5 mx-0 sm:mx-6 md:mx-10 lg:mx-20 xl:mx-48 2xl:mx-64", 66 + ), 67 + ], 68 + [navbar(), ..elements], 69 + ), 70 + html.footer( 71 + [ 72 + attr.class( 73 + "rounded-xl bg-gray-200 dark:bg-neutral-800 border-0 p-4 text-sm text-neutral-500 items-center flex flex-col", 74 + ), 75 + ], 76 + " 77 + The source code for this website is available [on Codeberg](https://codeberg.org/naomi/naomieow.xyz) under the [BSD 3 with Attribution](https://spdx.org/licenses/BSD-3-Clause-Attribution) license. 78 + 79 + If you wish to get in contact, please send an email to [mia@naomieow.xyz](mailto:mia@naomieow.xyz), or contact me on [Discord](https://chat.lesbian.skin). 80 + " 81 + |> md.render, 82 + ), 83 + ]) 84 + } 85 + 86 + pub fn navbar() -> element.Element(a) { 87 + html.nav( 88 + [ 89 + attr.class( 90 + "drop-shadow-md flex gap-4 w-full mb-5 rounded-xl p-4 items-center border-0 grow bg-gray-200 dark:bg-neutral-800", 91 + ), 92 + ], 93 + [ 94 + html.h1([attr.class("hidden md:block text-3xl m-0 text-pink-400")], [ 95 + element.text("Naomi Roberts"), 96 + ]), 97 + navitem(name: "Home", href: ""), 98 + navitem(name: "Projects", href: "projects"), 99 + navitem(name: "Posts", href: "posts"), 100 + ], 101 + ) 102 + } 103 + 104 + fn navitem(name name: String, href href: String) -> element.Element(a) { 105 + html.a( 106 + [ 107 + attr.href("/" <> href), 108 + attr.class( 109 + "drop-shadow-md no-underline bg-gray-300 dark:bg-neutral-900 p-3 rounded-xl text-black dark:text-neutral-200", 110 + ), 111 + ], 112 + [element.text(name)], 113 + ) 114 + } 6 115 7 116 pub fn link(text: String, link: String) -> element.Element(a) { 8 117 html.a([attr.href(link), attr.class("text-pink-400 underline")], [ ··· 33 142 ) 34 143 } 35 144 36 - pub fn day_to_string(day: Day) -> String { 37 - day.date |> int.to_string 145 + pub fn date_to_string(date: tom.Date) -> String { 146 + date.day |> int.to_string 38 147 <> "/" 39 - <> day.month |> int.to_string 148 + <> date.month |> int.to_string 40 149 <> "/" 41 - <> day.year |> int.to_string 150 + <> date.year |> int.to_string 42 151 }
+47
src/website/data/posts.gleam
··· 1 + import gleam/int 2 + import gleam/list 3 + import gleam/order 4 + import gleam/string 5 + import lustre/element/html 6 + import lustre/ssg/djot 7 + import simplifile 8 + import tom 9 + import website/common.{type Post, Post} 10 + import website/md 11 + 12 + pub fn all() -> List(Post(a)) { 13 + let path = "./src/website/data/posts/" 14 + let assert Ok(posts) = simplifile.read_directory(path) 15 + posts 16 + |> list.map(fn(post) { 17 + let assert Ok(post_md) = simplifile.read(path <> post) 18 + let assert Ok(meta) = djot.metadata(post_md) 19 + let assert Ok(title) = tom.get_string(meta, ["title"]) 20 + let assert Ok(author) = tom.get_string(meta, ["author"]) 21 + let assert Ok(date_posted) = tom.get_date(meta, ["date"]) 22 + let assert Ok(summary) = tom.get_string(meta, ["summary"]) 23 + Post( 24 + id: post 25 + |> string.drop_right(3) 26 + |> string.lowercase 27 + |> string.replace(" ", "-"), 28 + title:, 29 + author:, 30 + date_posted:, 31 + summary: html.p([], summary |> md.render), 32 + content: html.div([], post_md |> djot.content |> md.render), 33 + ) 34 + }) 35 + } 36 + 37 + pub fn sorted(posts: List(Post(a))) -> List(Post(a)) { 38 + posts 39 + |> list.sort(fn(left, right) { 40 + int.compare(left.date_posted.year, right.date_posted.year) 41 + |> order.break_tie(int.compare( 42 + left.date_posted.month, 43 + right.date_posted.month, 44 + )) 45 + |> order.break_tie(int.compare(left.date_posted.day, right.date_posted.day)) 46 + }) 47 + }
+28
src/website/data/posts/mystcraft-ages-alpha-1.md
··· 1 + --- 2 + title = "Mystcraft: Ages" 3 + author = "Naomi Roberts" 4 + date = 2023-09-30 5 + summary = "A modern reimagining of the original [Mystcraft](https://www.curseforge.com/minecraft/mc-mods/mystcraft) mod!" 6 + --- 7 + # Alpha 1 8 + This is an alpha release, the mod is nowhere near complete and some bugs may exist. 9 + If you do find any bugs please report them on [the bug tracker](https://codeberg.org/naomi/mystcraft-ages/issues) 10 + 11 + ## Developer Notes 12 + It's been a while since I said I was gonna start this project and after over 60 hours of work 😅 you now have a semi-functioning proof-of-concept! I'm very efficient I know 😔 13 + 14 + Jokes aside, the (albeit limited) content list/changelog is below! 15 + 16 + ## Changelog 17 + 1. Linking Books 18 + - These are the main attraction of this alpha, and using one can link it so wherever you are standing. There are a few known bugs with these so please check before reporting. 19 + 2. Book Receptacle 20 + - These are small, lectern-like blocks that you can place Linked Books into. (Currently) they allow multiple people to use the same book, but the book will not travel with you. 21 + 3. Writing Stand 22 + - This is where most of the development time has gone and is the place where you can create your Linking Books. It requires you to provide it with an ink supply. 23 + 4. Ink Vials 24 + - A slightly more efficient ink than regular ink sacs. 25 + 26 + ## Links 27 + - [CurseForge](https://legacy.curseforge.com/minecraft/mc-mods/mystcraft-ages) 28 + - [Modrinth](https://modrinth.com/mod/ages)
+26
src/website/data/posts/mystcraft-ages-alpha-2.md
··· 1 + --- 2 + title = "UPDATE: Mystcraft: Ages" 3 + author = "Naomi Roberts" 4 + date = 2023-11-30 5 + summary = "A modern reimagining of the original [Mystcraft](https://www.curseforge.com/minecraft/mc-mods/mystcraft) mod!" 6 + --- 7 + # Alpha 2 8 + This is an alpha release, the mod is nowhere near complete and some bugs may exist. If you do find any bugs please report them on [the bug tracker](https://codeberg.org/naomi/mystcraft-ages/issues) 9 + 10 + ## Developer Notes 11 + I hate entities. 12 + 13 + ## Changelog 14 + 1. Linking Books [\[MA26\]](https://codeberg.org/naomi/mystcraft-ages/issues/26) 15 + - They no longer directly teleport you, they will summon a Book entity on the ground 16 + 2. Book Entities [\[MA26\]](https://codeberg.org/naomi/mystcraft-ages/issues/26) 17 + - These are entities (wow) that are books (wow!!). They replace the teleportation function that the previous iteration of Linking Books has, to see the contained book, Crouch + Interact, to teleport just Interact. _THERE IS CURRENTLY NO WAY TO PICK THEM UP_ 18 + 3. Writing Stand Recipes [\[MA31\]](https://codeberg.org/naomi/mystcraft-ages/issues/31) 19 + - The recipes in the writing stand have been updated, now requireing an additional item. Unlinked Books require an enderpearl. 20 + 4. Book Receptacle [\[MA21\]](https://codeberg.org/naomi/mystcraft-ages/issues/21) 21 + - The book now renders correctly. I'm so cool. 22 + 23 + ## Links 24 + - [CurseForge](https://legacy.curseforge.com/minecraft/mc-mods/mystcraft-ages) 25 + - [Modrinth](https://modrinth.com/mod/ages) 26 + - [Source Code](https://modrinth.com/mod/ages)
+85
src/website/data/projects.gleam
··· 1 + import gleam/list 2 + import gleam/result 3 + import gleam/string 4 + import lustre/element/html 5 + import lustre/ssg/djot 6 + import simplifile 7 + import tom 8 + import website/common.{type Link, type Project, Link, Project} 9 + import website/md 10 + 11 + pub fn all() -> List(Project(a)) { 12 + let path = "./src/website/data/projects/" 13 + let assert Ok(projects) = simplifile.read_directory(path) 14 + projects 15 + |> list.map(fn(project) { 16 + let assert Ok(project_md) = simplifile.read(path <> project) 17 + let assert Ok(meta) = djot.metadata(project_md) 18 + let assert Ok(title) = tom.get_string(meta, ["title"]) 19 + let assert Ok(author) = tom.get_string(meta, ["author"]) 20 + let assert Ok(img) = tom.get_string(meta, ["icon"]) 21 + let assert Ok(summary) = tom.get_string(meta, ["summary"]) 22 + let assert Ok(archived) = tom.get_bool(meta, ["archived"]) 23 + let links = 24 + [ 25 + tom.get_string(meta, ["source"]) 26 + |> result.try(fn(link) { 27 + Ok(Link(title: "Source Code", url: link, img: "/assets/git_64h.png")) 28 + }), 29 + tom.get_string(meta, ["modrinth"]) 30 + |> result.try(fn(link) { 31 + Ok(Link( 32 + title: "Modrinth", 33 + url: link, 34 + img: "/assets/modrinth_64h.png", 35 + )) 36 + }), 37 + tom.get_string(meta, ["curseforge"]) 38 + |> result.try(fn(link) { 39 + Ok(Link( 40 + title: "CurseForge", 41 + url: link, 42 + img: "/assets/curseforge_64h.png", 43 + )) 44 + }), 45 + ] 46 + |> result.values 47 + 48 + Project( 49 + archived:, 50 + author:, 51 + id: project 52 + |> string.drop_right(3) 53 + |> string.lowercase 54 + |> string.replace(" ", "-"), 55 + img:, 56 + links:, 57 + summary: html.p([], summary |> md.render), 58 + title:, 59 + content: html.div([], project_md |> djot.content |> md.render), 60 + ) 61 + }) 62 + } 63 + // fn curseforge_mod(slug: String) -> Link { 64 + // Link( 65 + // title: "CurseForge", 66 + // url: "https://www.curseforge.com/minecraft/mc-mods/" <> slug, 67 + // img: "https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy-minimal/available/curseforge_64h.png", 68 + // ) 69 + // } 70 + 71 + // fn modrinth(slug: String) -> Link { 72 + // Link( 73 + // title: "Modrinth", 74 + // url: "https://mods.gay/" <> slug, 75 + // img: "https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy-minimal/available/modrinth_64h.png", 76 + // ) 77 + // } 78 + 79 + // fn codeberg(user: String, repo: String) -> Link { 80 + // Link( 81 + // title: "Source Code", 82 + // url: "https://codeberg.org/" <> user <> "/" <> repo, 83 + // img: "https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy-minimal/available/codeberg_64h.png", 84 + // ) 85 + // }
+8
src/website/data/projects/caramel.md
··· 1 + --- 2 + title = "Caramel" 3 + author = "Naomi Roberts" 4 + icon = "/assets/caramel.png" 5 + summary = "A Discord/Revolt bridge using [arikawa](https://github.com/diamondburned/arikawa/) and [revolt.go](https://github.com/ben-forster/revolt/)" 6 + source = "https://codeberg.org/naomi/Caramel" 7 + archived = true 8 + ---
+9
src/website/data/projects/cfs.md
··· 1 + --- 2 + title = "Carcinogenic Fibrous Silicate" 3 + icon = "/assets/cfs.png" 4 + author = "Naomi Roberts" 5 + summary = "Small joke mod requested by some friends that adds Asbestos to the game." 6 + source = "https://codeberg.org/naomi/asbestos" 7 + modrinth = "https://modrinth.com/datapack/cfs" 8 + archived = true 9 + ---
+10
src/website/data/projects/ibo.md
··· 1 + --- 2 + title = "Incendium Biomes Only" 3 + icon = "/assets/ibo.webp" 4 + author = "Naomi Roberts" 5 + summary = "Removes structures, items, mobs and bosses from [Incendium](https://www.stardustlabs.net/incendium), leaving only the biomes and terrain." 6 + source = "https://codeberg.org/naomi/ibo" 7 + modrinth = "https://modrinth.com/datapack/ibo" 8 + curseforge = "https://www.curseforge.com/minecraft/mc-mods/incendium-biomes-only" 9 + archived = false 10 + ---
+8
src/website/data/projects/lilypad.md
··· 1 + --- 2 + title = "Lilypad" 3 + icon = "/assets/lilypad.png" 4 + author = "Naomi Roberts" 5 + summary = "Lilypad is a general-purpose Discord bot built using [arikawa](https://github.com/diamondburned/arikawa/), mainly for use in my own [Discord server.](https://chat.lesbian.skin)" 6 + source = "https://codeberg.org/Lilypad" 7 + archived = false 8 + ---
+10
src/website/data/projects/mystcraft-ages.md
··· 1 + --- 2 + title = "Mystcraft: Ages" 3 + icon = "/assets/ages.png" 4 + author = "Naomi Roberts" 5 + summary = "Mystcraft: Ages is a modern rewrite and reimagining of the original [Mystcraft](https://www.curseforge.com/minecraft/mc-mods/mystcraft) mod." 6 + source = "https://codeberg.org/naomi/mystcraft-ages" 7 + modrinth = "https://modrinth.com/mod/ages" 8 + curseforge = "https://www.curseforge.com/minecraft/mc-mods/mystcraft-ages" 9 + archived = false 10 + ---
+10
src/website/data/projects/pricked.md
··· 1 + --- 2 + title = "Get Pricked!" 3 + icon = "/assets/pricked.png" 4 + author = "Naomi Roberts" 5 + summary = "Ever been playing the game and thought a block should damage you even though it doesn't? Well, now they do!" 6 + source = "https://codeberg.org/naomi/pricked" 7 + modrinth = "https://modrinth.com/datapack/pricked" 8 + curseforge = "https://www.curseforge.com/minecraft/mc-mods/get-pricked" 9 + archived = false 10 + ---
+10
src/website/data/projects/rpgtitles.md
··· 1 + --- 2 + title = "RPGTitles" 3 + icon = "/assets/titles.png" 4 + author = "Naomi Roberts" 5 + summary = "Small, cosmetic datapack that adds titles to various things in the game!" 6 + source = "https://codeberg.org/naomi/rpgtitles" 7 + modrinth = "https://modrinth.com/datapack/rpgtitles" 8 + curseforge = "https://www.curseforge.com/minecraft/mc-mods/rpgtitles" 9 + archived = false 10 + ---
+138
src/website/md.gleam
··· 1 + import commonmark 2 + import commonmark/ast 3 + import gleam/int 4 + import gleam/list 5 + import gleam/option.{None, Some} 6 + import lustre/attribute 7 + import lustre/element 8 + import lustre/element/html 9 + 10 + pub fn render(document doc: String) -> List(element.Element(a)) { 11 + doc 12 + |> commonmark.parse 13 + |> render_commonmark 14 + } 15 + 16 + pub fn render_commonmark(document doc: ast.Document) -> List(element.Element(a)) { 17 + doc.blocks 18 + |> list.map(render_block_node) 19 + } 20 + 21 + fn render_block_node(blocknode: ast.BlockNode) -> element.Element(a) { 22 + case blocknode { 23 + ast.HorizontalBreak -> html.hr([]) 24 + ast.Heading(level:, contents:) -> { 25 + let contents = contents |> list.map(render_inline_node) 26 + case level { 27 + 1 -> 28 + html.h1( 29 + [attribute.class("text-3xl font-semibold my-3 text-pink-400")], 30 + contents, 31 + ) 32 + 2 -> html.h2([attribute.class("text-2xl font-semibold my-2")], contents) 33 + 3 -> html.h3([attribute.class("text-xl font-semibold my-1")], contents) 34 + 4 -> html.h4([attribute.class("text-lg font-semibold")], contents) 35 + 5 -> html.h5([attribute.class("font-bold")], contents) 36 + 6 -> html.h6([attribute.class("font-semibold")], contents) 37 + _ -> html.p([], contents) 38 + } 39 + } 40 + ast.CodeBlock(info:, full_info:, contents:) -> 41 + html.pre([], [html.code([], [element.text(contents)])]) 42 + ast.HtmlBlock(html:) -> html.html([], [element.text(html)]) 43 + ast.Paragraph(contents:) -> 44 + html.p([], contents |> list.map(render_inline_node)) 45 + ast.BlockQuote(contents:) -> 46 + html.blockquote([], contents |> list.map(render_block_node)) 47 + ast.AlertBlock(level:, contents:) -> { 48 + let contents = contents |> list.map(render_block_node) 49 + case level { 50 + ast.NoteAlert -> 51 + html.blockquote([attribute.class("alert alert-note")], contents) 52 + ast.CautionAlert -> 53 + html.blockquote([attribute.class("alert alert-caution")], contents) 54 + ast.ImportantAlert -> 55 + html.blockquote([attribute.class("alert alert-important")], contents) 56 + ast.TipAlert -> 57 + html.blockquote([attribute.class("alert alert-tip")], contents) 58 + ast.WarningAlert -> 59 + html.blockquote([attribute.class("alert alert-warning")], contents) 60 + } 61 + } 62 + ast.OrderedList(contents:, start:, marker:) -> 63 + html.ol( 64 + [ 65 + attribute.attribute("start", start |> int.to_string), 66 + attribute.class("list-decimal ml-8"), 67 + ], 68 + contents |> list.map(render_list_item), 69 + ) 70 + ast.UnorderedList(contents:, marker:) -> 71 + html.ul( 72 + [attribute.class("list-disc ml-8")], 73 + contents |> list.map(render_list_item), 74 + ) 75 + } 76 + } 77 + 78 + fn render_list_item(list_item: ast.ListItem) { 79 + case list_item { 80 + ast.ListItem(contents:) -> 81 + html.li( 82 + [], 83 + contents 84 + |> list.map(fn(item) { html.p([], [item |> render_block_node]) }), 85 + ) 86 + ast.TightListItem(contents:) -> 87 + html.li([], contents |> list.map(render_block_node)) 88 + } 89 + } 90 + 91 + fn render_inline_node(inline_node: ast.InlineNode) { 92 + case inline_node { 93 + ast.CodeSpan(contents:) -> html.code([], [element.text(contents)]) 94 + ast.Emphasis(contents:, marker:) -> 95 + html.em([], contents |> list.map(render_inline_node)) 96 + ast.StrongEmphasis(contents:, marker:) -> 97 + html.strong([], contents |> list.map(render_inline_node)) 98 + ast.StrikeThrough(contents:) -> 99 + html.s([], contents |> list.map(render_inline_node)) 100 + ast.Link(contents:, title:, href:) -> 101 + html.a( 102 + [ 103 + attribute.href(href), 104 + attribute.class("text-pink-400 underline"), 105 + case title { 106 + Some(t) -> attribute.attribute("title", t) 107 + None -> attribute.none() 108 + }, 109 + ], 110 + contents |> list.map(render_inline_node), 111 + ) 112 + ast.ReferenceLink(contents:, ref:) -> 113 + html.a([attribute.href(ref)], contents |> list.map(render_inline_node)) 114 + ast.Image(alt:, title:, href:) -> 115 + html.img([ 116 + attribute.alt(alt), 117 + attribute.href(href), 118 + case title { 119 + Some(t) -> attribute.attribute("title", t) 120 + None -> attribute.none() 121 + }, 122 + ]) 123 + ast.ReferenceImage(alt:, ref:) -> 124 + html.img([attribute.alt(alt), attribute.href(ref)]) 125 + ast.UriAutolink(href:) -> 126 + html.a([attribute.href(href)], [element.text(href)]) 127 + ast.EmailAutolink(href:) -> 128 + html.a([attribute.href(href)], [element.text(href)]) 129 + ast.HtmlInline(html:) -> 130 + html.p([], [ 131 + element.text("Unimplemented, dump of inline HTML:"), 132 + element.text(html), 133 + ]) 134 + ast.PlainText(contents:) -> html.text(contents) 135 + ast.HardLineBreak -> html.br([]) 136 + ast.SoftLineBreak -> html.text("") 137 + } 138 + }
+69
src/website/page/index.gleam
··· 1 + import gleam/list 2 + import lustre/attribute as attr 3 + import lustre/element.{type Element} 4 + import lustre/element/html 5 + import website/common 6 + import website/md 7 + 8 + fn lang_list(title: String, langs: List(String)) { 9 + html.ul([], [ 10 + html.li([], [ 11 + html.h3([attr.class("text-2xl underline")], [element.text(title)]), 12 + ]), 13 + html.li([], [ 14 + html.ul( 15 + [attr.class("list-disc pl-8")], 16 + langs 17 + |> list.map(fn(lang) { html.li([], [element.text(lang)]) }), 18 + ), 19 + ]), 20 + ]) 21 + } 22 + 23 + pub fn view() -> List(Element(a)) { 24 + let proficient = [ 25 + "⭐ Gleam", "☕ Java", "📜 JavaScript", "🐍 Python", "❓mcfunction", 26 + ] 27 + let learning = [ 28 + "🦀 Rust", "🟦 Go", "🎮 C#", "💀 C++", "♻️ Kotlin", 29 + ] 30 + let want = ["⚡ Zig", "🐪 OCaml", "😃 Odin"] 31 + [ 32 + html.div( 33 + [ 34 + attr.class( 35 + "drop-shadow-md text-xl p-4 mb-4 rounded-xl bg-gray-200 dark:bg-neutral-800 dark:text-neutral-200", 36 + ), 37 + ], 38 + [ 39 + html.div( 40 + [], 41 + "# About Me 42 + Hi! I'm Naomi (or Mia), a trans girl from the UK who loves to code! I mostly make [Minecraft mods](https://modrinth.com/user/naomi), but have started to begin other projects like [Sheetr](https://sheetr.app/) and [Lilypad](https://codeberg.org/naomi/lilypad). Both projects are in their infancy, but I hope that they can become useful to someone out there eventually!" 43 + |> md.render, 44 + ), 45 + html.div([attr.class("my-4")], [ 46 + html.p([], [ 47 + element.text( 48 + "I absolutely love learning new languages, so here is what I know:", 49 + ), 50 + ]), 51 + html.div([attr.class("gap-4 sm:gap-20 flex flex-col md:flex-row")], [ 52 + lang_list("Proficient", proficient), 53 + lang_list("Learning", learning), 54 + lang_list("Want to learn", want), 55 + ]), 56 + ]), 57 + html.p([attr.class("text-sm")], [ 58 + element.text("Currently studying "), 59 + common.link( 60 + "Computer Games Programming", 61 + "https://www.staffs.ac.uk/course/computer-games-programming-bsc", 62 + ), 63 + element.text(" @ "), 64 + common.link("Staffodshire University", "https://staffs.ac.uk/"), 65 + ]), 66 + ], 67 + ), 68 + ] 69 + }
+68
src/website/page/post.gleam
··· 1 + import gleam/list 2 + import lustre/attribute as attr 3 + import lustre/element.{type Element} 4 + import lustre/element/html 5 + import website/common.{type Post} 6 + import website/data/posts 7 + 8 + pub fn view_all(posts: List(Post(a))) -> List(Element(a)) { 9 + posts 10 + |> posts.sorted 11 + |> list.reverse 12 + |> list.map(fn(post: Post(a)) { 13 + html.ul( 14 + [ 15 + attr.class( 16 + "drop-shadow-md text-xl rounded-xl p-4 mb-4 bg-gray-200 dark:bg-neutral-800 dark:text-neutral-200", 17 + ), 18 + ], 19 + [ 20 + html.li([], [ 21 + html.div([attr.class("flex items-center gap-4")], [ 22 + html.h1([attr.class("text-2xl")], [ 23 + common.link(post.title, "/posts/" <> post.id), 24 + ]), 25 + html.p([attr.class("text-neutral-500")], [ 26 + element.text("Author: " <> post.author), 27 + ]), 28 + html.p([attr.class("text-neutral-500")], [ 29 + element.text( 30 + "Date Posted: " <> post.date_posted |> common.date_to_string, 31 + ), 32 + ]), 33 + ]), 34 + ]), 35 + html.li([], [html.p([], [post.summary])]), 36 + ], 37 + ) 38 + }) 39 + } 40 + 41 + pub fn view(post: Post(a)) -> List(Element(a)) { 42 + [ 43 + html.div( 44 + [ 45 + attr.class( 46 + "drop-shadow-md text-xl rounded-xl p-4 mb-4 bg-gray-200 dark:bg-neutral-800 dark:text-neutral-200", 47 + ), 48 + ], 49 + [ 50 + html.div([attr.class("flex items-center align-middle gap-4")], [ 51 + html.h1([attr.class("text-3xl m-0 text-pink-400")], [ 52 + element.text(post.title), 53 + ]), 54 + html.p([attr.class("text-neutral-500")], [ 55 + element.text("Author: " <> post.author), 56 + ]), 57 + html.p([attr.class("text-neutral-500")], [ 58 + element.text( 59 + "Date Posted: " <> post.date_posted |> common.date_to_string, 60 + ), 61 + ]), 62 + ]), 63 + html.hr([attr.class("h-px my-4 dark:bg-gray-200 border-0 bg-gray-400")]), 64 + html.div([], [post.content]), 65 + ], 66 + ), 67 + ] 68 + }
+66
src/website/page/project.gleam
··· 1 + import gleam/list 2 + import lustre/attribute as attr 3 + import lustre/element.{type Element} 4 + import lustre/element/html 5 + import website/common.{type Project, Project} 6 + 7 + pub fn view_all(projects: List(Project(a))) -> List(Element(a)) { 8 + projects 9 + |> list.map(fn(project: Project(a)) { 10 + html.ul( 11 + [ 12 + attr.id("project-" <> project.id), 13 + attr.class( 14 + "drop-shadow-md text-xl rounded-xl p-4 mb-4 bg-gray-200 dark:bg-neutral-800 dark:text-neutral-200", 15 + ), 16 + ], 17 + [ 18 + html.li([attr.class("flex items-center gap-4 pb-1.5")], [ 19 + html.img([ 20 + attr.src(project.img), 21 + attr.class("rounded-xl max-w-16 max-h-16"), 22 + ]), 23 + html.h1([attr.class("text-2xl")], [ 24 + common.link(project.title, "/projects/" <> project.id), 25 + ]), 26 + html.div( 27 + [attr.class("flex items-center gap-2")], 28 + project.links 29 + |> list.map(fn(link) { 30 + html.a([attr.href(link.url), attr.alt(link.title)], [ 31 + html.img([ 32 + attr.src(link.img), 33 + attr.class("rounded-xl max-w-14 max-h-14"), 34 + ]), 35 + ]) 36 + }), 37 + ), 38 + ]), 39 + html.li([], [html.p([], [project.summary])]), 40 + ], 41 + ) 42 + }) 43 + } 44 + 45 + pub fn view(project: Project(a)) -> List(Element(a)) { 46 + [ 47 + html.div( 48 + [ 49 + attr.class( 50 + "drop-shadow-md text-xl rounded-xl p-4 mb-4 bg-gray-200 dark:bg-neutral-800 dark:text-neutral-200", 51 + ), 52 + ], 53 + [ 54 + html.div([attr.class("flex items-center gap-4 pb-1.5")], [ 55 + html.img([ 56 + attr.src(project.img), 57 + attr.class("rounded-xl max-w-16 max-h-16"), 58 + ]), 59 + html.h1([attr.class("text-2xl")], [element.text(project.title)]), 60 + ]), 61 + html.p([], [project.summary]), 62 + project.content, 63 + ], 64 + ), 65 + ] 66 + }
-171
src/website/posts.gleam
··· 1 - import birl.{type Day} 2 - import lustre/element 3 - import lustre/element/html 4 - import website/common.{link} 5 - 6 - pub type Post(a) { 7 - Post( 8 - id: String, 9 - title: String, 10 - author: String, 11 - date_posted: Day, 12 - summary: element.Element(a), 13 - content: element.Element(a), 14 - ) 15 - } 16 - 17 - pub fn all() -> List(Post(a)) { 18 - [mystcraft_ages_alpha_2(), release_mystcraft_ages()] 19 - } 20 - 21 - fn mystcraft_short_descriptor() -> element.Element(a) { 22 - html.p([], [ 23 - element.text("A modern reimagining of the original "), 24 - link("Mystcraft", "https://www.curseforge.com/minecraft/mc-mods/mystcraft"), 25 - element.text(" mod!"), 26 - ]) 27 - } 28 - 29 - fn release_mystcraft_ages() -> Post(a) { 30 - Post( 31 - id: "mystcraft-ages-alpha-1", 32 - title: "Mystcraft: Ages", 33 - author: "naomieow", 34 - date_posted: birl.Day(2023, 09, 30), 35 - summary: mystcraft_short_descriptor(), 36 - content: html.div([], [ 37 - html.h2([], [element.text("Alpha 1")]), 38 - html.p([], [ 39 - element.text( 40 - "This is an alpha release, the mod is nowhere near complete and some bugs may exist. If you do find any bugs please report them on ", 41 - ), 42 - link( 43 - "the bug tracker", 44 - "https://codeberg.org/naomi/mystcraft-ages/issues", 45 - ), 46 - ]), 47 - html.h3([], [element.text("Developer Notes")]), 48 - html.p([], [ 49 - element.text( 50 - "It's been a while since I said I was gonna start this project and after over 60 hours of work 😅 you now have a semi-functioning proof-of-concept! I'm very efficient I know 😔", 51 - ), 52 - ]), 53 - html.p([], [ 54 - element.text( 55 - "Jokes aside, the (albeit limited) content list/changelog is below!", 56 - ), 57 - ]), 58 - html.h3([], [element.text("Changelog")]), 59 - html.ul([], [html.li([], [element.text("Linking Books")])]), 60 - html.p([], [ 61 - element.text( 62 - "These are the main attraction of this alpha, and using one can link it so wherever you are standing. There are a few known bugs with these so please check before reporting.", 63 - ), 64 - ]), 65 - html.ul([], [html.li([], [element.text("Book Receptacle")])]), 66 - html.p([], [ 67 - element.text( 68 - "These are small, lectern-like blocks that you can place Linked Books into. (Currently) they allow multiple people to use the same book, but the book will not travel with you.", 69 - ), 70 - ]), 71 - html.ul([], [html.li([], [element.text("Writing Stand")])]), 72 - html.p([], [ 73 - element.text( 74 - "This is where most of the development time has gone and is the place where you can create your Linking Books. It requires you to provide it with an ink supply.", 75 - ), 76 - ]), 77 - html.ul([], [html.li([], [element.text("Ink Vials")])]), 78 - html.p([], [ 79 - element.text("A slightly more efficient ink than regular ink sacs."), 80 - ]), 81 - html.h3([], [element.text("Links")]), 82 - html.p([], [ 83 - link( 84 - "CurseForge", 85 - "https://legacy.curseforge.com/minecraft/mc-mods/mystcraft-ages", 86 - ), 87 - ]), 88 - html.p([], [link("Modrinth", "https://modrinth.com/mod/ages")]), 89 - ]), 90 - ) 91 - } 92 - 93 - fn mystcraft_ages_alpha_2() -> Post(a) { 94 - Post( 95 - id: "mystcraft-ages-alpha-2", 96 - title: "UPDATE: Mystcraft: Ages", 97 - author: "naomieow", 98 - date_posted: birl.Day(2023, 11, 30), 99 - summary: mystcraft_short_descriptor(), 100 - content: html.div([], [ 101 - html.h2([], [element.text("Alpha 2")]), 102 - html.p([], [ 103 - element.text( 104 - "This is an alpha release, the mod is nowhere near complete and some bugs may exist. If you do find any bugs please report them on ", 105 - ), 106 - link( 107 - "the bug tracker", 108 - "https://codeberg.org/naomi/mystcraft-ages/issues", 109 - ), 110 - ]), 111 - html.h3([], [element.text("Developer Notes")]), 112 - html.p([], [element.text("I hate entities.")]), 113 - html.h3([], [element.text("Changelog")]), 114 - html.ul([], [ 115 - html.li([], [ 116 - element.text("Linking Books ["), 117 - link("MA26", "https://codeberg.org/naomi/mystcraft-ages/issues/26"), 118 - element.text("]"), 119 - ]), 120 - ]), 121 - html.p([], [ 122 - element.text( 123 - "They no longer directly teleport you, they will summon a Book entity on the ground", 124 - ), 125 - ]), 126 - html.ul([], [ 127 - html.li([], [ 128 - element.text("Book Entities ["), 129 - link("MA26", "https://codeberg.org/naomi/mystcraft-ages/issues/26"), 130 - element.text("]"), 131 - ]), 132 - ]), 133 - html.p([], [ 134 - element.text( 135 - "These are entities (wow) that are books (wow!!). They replace the teleportation function that the previous iteration of Linking Books has, to see the contained book, Crouch + Interact, to teleport just Interact. THERE IS CURRENTLY NO WAY TO PICK THEM UP", 136 - ), 137 - ]), 138 - html.ul([], [ 139 - html.li([], [ 140 - element.text("Writing Stand Recipes ["), 141 - link("MA31", "https://codeberg.org/naomi/mystcraft-ages/issues/31"), 142 - element.text("]"), 143 - ]), 144 - ]), 145 - html.p([], [ 146 - element.text( 147 - "The recipes in the writing stand have been updated, now requireing an additional item. Unlinked Books require an enderpearl.", 148 - ), 149 - ]), 150 - html.ul([], [ 151 - html.li([], [ 152 - element.text("Book Receptacle ["), 153 - link("MA21", "https://codeberg.org/naomi/mystcraft-ages/issues/21"), 154 - element.text("]"), 155 - ]), 156 - ]), 157 - html.p([], [element.text("The book now renders correctly. I'm so cool.")]), 158 - html.h3([], [element.text("Links")]), 159 - html.ul([], [ 160 - html.li([], [link("Modrinth", "https://modrinth.com/mod/ages")]), 161 - html.li([], [ 162 - link( 163 - "CurseForge", 164 - "https://legacy.curseforge.com/minecraft/mc-mods/mystcraft-ages", 165 - ), 166 - ]), 167 - html.li([], [link("Source Code", "https://modrinth.com/mod/ages")]), 168 - ]), 169 - ]), 170 - ) 171 - }
-181
src/website/projects.gleam
··· 1 - import lustre/element 2 - import lustre/element/html 3 - import website/common.{link} 4 - 5 - pub type Link { 6 - Link(title: String, url: String, img: String) 7 - } 8 - 9 - pub type Project(a) { 10 - Project( 11 - id: String, 12 - title: String, 13 - author: String, 14 - img: String, 15 - links: List(Link), 16 - archived: Bool, 17 - summary: element.Element(a), 18 - ) 19 - } 20 - 21 - pub fn all() -> List(Project(a)) { 22 - [ 23 - lilypad(), 24 - caramel(), 25 - mystcraft_ages(), 26 - rpgtitles(), 27 - biomes_only(), 28 - get_pricked(), 29 - asbestos(), 30 - ] 31 - } 32 - 33 - fn curseforge_mod(slug: String) -> Link { 34 - Link( 35 - title: "CurseForge", 36 - url: "https://www.curseforge.com/minecraft/mc-mods/" <> slug, 37 - img: "https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy-minimal/available/curseforge_64h.png", 38 - ) 39 - } 40 - 41 - fn modrinth(slug: String) -> Link { 42 - Link( 43 - title: "Modrinth", 44 - url: "https://mods.gay/" <> slug, 45 - img: "https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy-minimal/available/modrinth_64h.png", 46 - ) 47 - } 48 - 49 - fn codeberg(user: String, repo: String) -> Link { 50 - Link( 51 - title: "Source Code", 52 - url: "https://codeberg.org/" <> user <> "/" <> repo, 53 - img: "https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy-minimal/available/codeberg_64h.png", 54 - ) 55 - } 56 - 57 - fn lilypad() -> Project(a) { 58 - Project( 59 - id: "lilypad", 60 - title: "Lilypad", 61 - author: "naomieow", 62 - img: "https://codeberg.org/repo-avatars/028f375a8ea1cc6fd92e85466b2dfb49247c97f07f66c4dd7d7efb06a8fd3dd6", 63 - links: [codeberg("naomi", "lilypad")], 64 - archived: False, 65 - summary: html.p([], [ 66 - element.text("Lilypad is a general-purpose Discord bot built using "), 67 - link("arikawa", "https://github.com/diamondburned/arikawa/"), 68 - element.text(", mainly for use in my own "), 69 - link("Discord server.", "https://discord.naomieow.xyz/"), 70 - ]), 71 - ) 72 - } 73 - 74 - fn caramel() -> Project(a) { 75 - Project( 76 - id: "caramel", 77 - title: "Caramel", 78 - author: "naomieow", 79 - img: "https://codeberg.org/repo-avatars/1e15f1174f6a90152e3027a13a3727230a32154de6ab12325e6fa25d5a04a5c1", 80 - links: [codeberg("naomi", "caramel")], 81 - archived: False, 82 - summary: html.p([], [ 83 - element.text("A Discord/Revolt bridge using "), 84 - link("arikawa", "https://github.com/diamondburned/arikawa/"), 85 - element.text(" and "), 86 - link("revolt.go", "https://github.com/ben-forster/revolt/"), 87 - ]), 88 - ) 89 - } 90 - 91 - fn mystcraft_ages() -> Project(a) { 92 - Project( 93 - id: "mystcraft_ages", 94 - title: "Mystcraft: Ages", 95 - author: "naomieow", 96 - img: "https://codeberg.org/repo-avatars/de7a4fbec03846014acbbd6751aca89b7e1af056e74cb2750e3d554b5842b1f5", 97 - links: [ 98 - modrinth("ages"), 99 - curseforge_mod("mystcraft-ages"), 100 - codeberg("naomi", "mystcraft-ages"), 101 - ], 102 - archived: False, 103 - summary: html.p([], [ 104 - element.text( 105 - "Mystcraft: Ages is a modern rewrite and reimagining of the original Mystcraft mod.", 106 - ), 107 - ]), 108 - ) 109 - } 110 - 111 - fn rpgtitles() -> Project(a) { 112 - Project( 113 - id: "rpgtitles", 114 - title: "RPGTitles", 115 - author: "naomieow", 116 - img: "https://codeberg.org/repo-avatars/285354680e5bdf1c00b03b453fdb46d8538f371f06ccb7ec85f68681d5f250dd", 117 - links: [ 118 - modrinth("rpgtitles"), 119 - curseforge_mod("rpgtitles"), 120 - codeberg("naomi", "rpgtitles"), 121 - ], 122 - archived: False, 123 - summary: html.p([], [ 124 - element.text( 125 - "Small, cosmetic datapack that adds titles to small things in the game!", 126 - ), 127 - ]), 128 - ) 129 - } 130 - 131 - fn biomes_only() -> Project(a) { 132 - Project( 133 - id: "biomes_only", 134 - title: "Incendium Biomes Only", 135 - author: "naomieow", 136 - img: "https://codeberg.org/repo-avatars/201bb4544ceaf3ca51a75958e3e276741e7ded9af6ee54e045be0b8010a45c4a", 137 - links: [modrinth("ibo"), curseforge_mod("incendium-biomes-only")], 138 - archived: False, 139 - summary: html.p([], [ 140 - element.text( 141 - "Removes structures, items, mobs and bosses from Incendium, leaving only the biomes and terrain.", 142 - ), 143 - ]), 144 - ) 145 - } 146 - 147 - fn get_pricked() -> Project(a) { 148 - Project( 149 - id: "pricked", 150 - title: "Get Pricked!", 151 - author: "naomieow", 152 - img: "https://codeberg.org/repo-avatars/c2ab43e92df20ee1441c1d7a583c5aeaf361c0621d0af2fa5d33d1c5b00bc3b3", 153 - links: [ 154 - modrinth("pricked"), 155 - curseforge_mod("get-pricked"), 156 - codeberg("naomi", "pricked"), 157 - ], 158 - archived: False, 159 - summary: html.p([], [ 160 - element.text( 161 - "Ever been playing the game and thought a block should damage you even though it doesn't? Well, now they do!", 162 - ), 163 - ]), 164 - ) 165 - } 166 - 167 - fn asbestos() -> Project(a) { 168 - Project( 169 - id: "asbestos", 170 - title: "Carcinogenic Fibrous Silicate", 171 - author: "naomieow", 172 - img: "https://codeberg.org/repo-avatars/4bc99be0657207ffeededcf7b62c9b032dae9570c17af372755755b5c9566175", 173 - links: [modrinth("cfs")], 174 - archived: True, 175 - summary: html.p([], [ 176 - element.text( 177 - "Small joke mod requested by some friends that adds Asbestos to the game.", 178 - ), 179 - ]), 180 - ) 181 - }