From 63ea67e4c3c8caa2cf0dc6d7c940981bd40b3974 Mon Sep 17 00:00:00 2001 From: Stanisław Zagórowski Date: Mon, 15 Jan 2024 14:21:34 +0100 Subject: all: move out from luax/ --- blog/building-reluax.md | 211 ++++ blog/cache.lua | 23 + blog/posts.lua | 8 + common.luax | 78 ++ components/animated_bg.luax | 3 + components/carousel.luax | 10 + components/navbar-item.luax | 6 + components/navbar.luax | 18 + components/project.luax | 62 + data/games.lua | 229 ++++ data/projects.lua | 56 + libs/markdown.lua | 1281 ++++++++++++++++++++ luax/blog/building-reluax.md | 211 ---- luax/blog/cache.lua | 23 - luax/blog/posts.lua | 8 - luax/common.luax | 78 -- luax/components/animated_bg.luax | 3 - luax/components/carousel.luax | 10 - luax/components/navbar-item.luax | 6 - luax/components/navbar.luax | 18 - luax/components/project.luax | 62 - luax/data/games.lua | 229 ---- luax/data/projects.lua | 56 - luax/libs/markdown.lua | 1281 -------------------- luax/not_found.luax | 3 - luax/pages/blog.luax | 30 - luax/pages/blogpost.luax | 23 - luax/pages/contact.luax | 31 - luax/pages/games.luax | 25 - luax/pages/home.luax | 33 - luax/pages/projects.luax | 24 - luax/public/CNAME | 1 - luax/public/assets/app.css | 171 --- luax/public/assets/blog.css | 108 -- luax/public/assets/blog/building-reluax/cli.png | Bin 25294 -> 0 bytes .../assets/blog/building-reluax/first-demo.png | Bin 105706 -> 0 bytes .../assets/blog/building-reluax/second-demo.png | Bin 147578 -> 0 bytes luax/public/assets/carousel.css | 69 -- luax/public/assets/contact.css | 22 - .../assets/games/bumbling-builders/screenshot1.png | Bin 58058 -> 0 bytes .../assets/games/bumbling-builders/screenshot2.png | Bin 99514 -> 0 bytes .../assets/games/bumbling-builders/screenshot3.png | Bin 44823 -> 0 bytes .../assets/games/bumbling-builders/thumbnail.png | Bin 468975 -> 0 bytes luax/public/assets/games/dicemask/thumbnail.png | Bin 5538916 -> 0 bytes luax/public/assets/games/greachers/screenshot1.png | Bin 3649 -> 0 bytes luax/public/assets/games/greachers/screenshot2.png | Bin 3678 -> 0 bytes luax/public/assets/games/greachers/screenshot3.png | Bin 4072 -> 0 bytes luax/public/assets/games/greachers/title.png | Bin 4920 -> 0 bytes luax/public/assets/games/loud/screenshot1.jpg | Bin 89887 -> 0 bytes luax/public/assets/games/loud/screenshot2.jpg | Bin 99070 -> 0 bytes luax/public/assets/games/loud/screenshot3.jpg | Bin 106199 -> 0 bytes luax/public/assets/games/loud/thumbnail.jpg | Bin 47291 -> 0 bytes luax/public/assets/games/miro/screenshot1.png | Bin 16082 -> 0 bytes luax/public/assets/games/miro/screenshot2.png | Bin 16874 -> 0 bytes luax/public/assets/games/miro/screenshot3.png | Bin 18886 -> 0 bytes luax/public/assets/games/miro/thumbnail.png | Bin 6602 -> 0 bytes .../assets/games/mr-final-boss/screenshot1.png | Bin 1450742 -> 0 bytes .../assets/games/mr-final-boss/screenshot2.png | Bin 1989016 -> 0 bytes .../assets/games/mr-final-boss/screenshot3.png | Bin 1439568 -> 0 bytes .../assets/games/mr-final-boss/screenshot4.png | Bin 2193361 -> 0 bytes .../assets/games/mr-final-boss/thumbnail.png | Bin 41705 -> 0 bytes .../assets/games/riff-n-rage/screenshot1.png | Bin 10890 -> 0 bytes .../assets/games/riff-n-rage/screenshot2.png | Bin 10833 -> 0 bytes luax/public/assets/games/riff-n-rage/thumbnail.png | Bin 3685 -> 0 bytes .../assets/games/survive-the-temple/cover.png | Bin 7797 -> 0 bytes .../assets/games/survive-the-temple/screenshot.png | Bin 156733 -> 0 bytes luax/public/assets/games/thermal-trail/gameart.png | Bin 34983 -> 0 bytes .../assets/games/thermal-trail/screenshot1.png | Bin 59904 -> 0 bytes .../assets/games/thermal-trail/screenshot2.png | Bin 50655 -> 0 bytes .../assets/games/thermal-trail/screenshot3.png | Bin 59175 -> 0 bytes .../assets/games/thermal-trail/screenshot4.png | Bin 61715 -> 0 bytes .../assets/games/thermal-trail/thumbnail.png | Bin 34372 -> 0 bytes .../assets/games/third-party/screenshot1.jpg | Bin 27953 -> 0 bytes .../assets/games/third-party/screenshot2.jpg | Bin 75854 -> 0 bytes .../assets/games/third-party/screenshot3.jpg | Bin 86290 -> 0 bytes .../assets/games/third-party/screenshot4.jpg | Bin 115453 -> 0 bytes luax/public/assets/games/third-party/thumbnail.png | Bin 38018 -> 0 bytes luax/public/assets/games/wuzmag/screenshot1.png | Bin 20205 -> 0 bytes luax/public/assets/games/wuzmag/screenshot2.png | Bin 27941 -> 0 bytes luax/public/assets/games/wuzmag/screenshot3.png | Bin 26136 -> 0 bytes luax/public/assets/games/wuzmag/thumbnail.png | Bin 10256 -> 0 bytes luax/public/assets/games/your-left/screenshot1.jpg | Bin 46052 -> 0 bytes luax/public/assets/games/your-left/screenshot2.jpg | Bin 69916 -> 0 bytes luax/public/assets/games/your-left/screenshot3.jpg | Bin 69533 -> 0 bytes luax/public/assets/games/your-left/thumbnail.png | Bin 55977 -> 0 bytes luax/public/assets/images/android.png | Bin 4572 -> 0 bytes luax/public/assets/images/android.svg | 56 - luax/public/assets/images/cpp.png | Bin 6630 -> 0 bytes luax/public/assets/images/cpp.svg | 41 - luax/public/assets/images/csharp.png | Bin 7481 -> 0 bytes luax/public/assets/images/csharp.svg | 58 - luax/public/assets/images/duckpfp.png | Bin 2602 -> 0 bytes luax/public/assets/images/favicon.png | Bin 1608 -> 0 bytes luax/public/assets/images/favicon.svg | 54 - luax/public/assets/images/github.png | Bin 8133 -> 0 bytes luax/public/assets/images/github.svg | 66 - luax/public/assets/images/godot.png | Bin 7910 -> 0 bytes luax/public/assets/images/godot.svg | 64 - luax/public/assets/images/html5.png | Bin 4446 -> 0 bytes luax/public/assets/images/html5.svg | 56 - luax/public/assets/images/hyprland.png | Bin 6829 -> 0 bytes luax/public/assets/images/hyprland.svg | 50 - luax/public/assets/images/linux.png | Bin 3487 -> 0 bytes luax/public/assets/images/linux.svg | 42 - luax/public/assets/images/macos.png | Bin 2469 -> 0 bytes luax/public/assets/images/macos.svg | 39 - luax/public/assets/images/monogame.png | Bin 12087 -> 0 bytes luax/public/assets/images/pfp.png | Bin 1074 -> 0 bytes luax/public/assets/images/placeholder.png | Bin 9636 -> 0 bytes luax/public/assets/images/playstation.png | Bin 5762 -> 0 bytes luax/public/assets/images/playstation.svg | 61 - luax/public/assets/images/switch.png | Bin 4736 -> 0 bytes luax/public/assets/images/switch.svg | 43 - luax/public/assets/images/symbols.png | Bin 431 -> 0 bytes luax/public/assets/images/thumbnail.png | Bin 119178 -> 0 bytes luax/public/assets/images/thumbnail.svg | 70 -- luax/public/assets/images/thumbnail.zvg | 67 - luax/public/assets/images/unity.png | Bin 8893 -> 0 bytes luax/public/assets/images/unity.svg | 56 - luax/public/assets/images/windows.png | Bin 2446 -> 0 bytes luax/public/assets/images/windows.svg | 56 - luax/public/assets/images/xamarin.png | Bin 15826 -> 0 bytes luax/public/assets/images/xbox.png | Bin 8038 -> 0 bytes luax/public/assets/images/xbox.svg | 74 -- luax/public/assets/index.css | 108 -- luax/public/assets/index.js | 91 -- luax/public/assets/loose/github-yellow.svg | 66 - luax/public/assets/loose/itch-yellow.svg | 55 - luax/public/assets/loose/twitter-yellow.svg | 91 -- luax/public/assets/loose/web-yellow.svg | 58 - luax/public/assets/navbar.css | 278 ----- luax/public/assets/project.css | 126 -- luax/public/assets/projects/hyprload/logo.png | Bin 31147 -> 0 bytes luax/public/assets/projects/spirit-mod/demo1.gif | Bin 1075246 -> 0 bytes luax/public/assets/projects/spirit-mod/demo2.gif | Bin 4380429 -> 0 bytes .../assets/projects/spirit-mod/thumbnail.png | Bin 73781 -> 0 bytes .../assets/projects/spirit-mod/thumbnail.webp | Bin 266196 -> 0 bytes luax/reluax.luax | 72 -- not_found.luax | 3 + pages/blog.luax | 30 + pages/blogpost.luax | 23 + pages/contact.luax | 31 + pages/games.luax | 25 + pages/home.luax | 33 + pages/projects.luax | 24 + public/CNAME | 1 + public/assets/app.css | 171 +++ public/assets/blog.css | 108 ++ public/assets/blog/building-reluax/cli.png | Bin 0 -> 25294 bytes public/assets/blog/building-reluax/first-demo.png | Bin 0 -> 105706 bytes public/assets/blog/building-reluax/second-demo.png | Bin 0 -> 147578 bytes public/assets/carousel.css | 69 ++ public/assets/contact.css | 22 + .../assets/games/bumbling-builders/screenshot1.png | Bin 0 -> 58058 bytes .../assets/games/bumbling-builders/screenshot2.png | Bin 0 -> 99514 bytes .../assets/games/bumbling-builders/screenshot3.png | Bin 0 -> 44823 bytes .../assets/games/bumbling-builders/thumbnail.png | Bin 0 -> 468975 bytes public/assets/games/dicemask/thumbnail.png | Bin 0 -> 5538916 bytes public/assets/games/greachers/screenshot1.png | Bin 0 -> 3649 bytes public/assets/games/greachers/screenshot2.png | Bin 0 -> 3678 bytes public/assets/games/greachers/screenshot3.png | Bin 0 -> 4072 bytes public/assets/games/greachers/title.png | Bin 0 -> 4920 bytes public/assets/games/loud/screenshot1.jpg | Bin 0 -> 89887 bytes public/assets/games/loud/screenshot2.jpg | Bin 0 -> 99070 bytes public/assets/games/loud/screenshot3.jpg | Bin 0 -> 106199 bytes public/assets/games/loud/thumbnail.jpg | Bin 0 -> 47291 bytes public/assets/games/miro/screenshot1.png | Bin 0 -> 16082 bytes public/assets/games/miro/screenshot2.png | Bin 0 -> 16874 bytes public/assets/games/miro/screenshot3.png | Bin 0 -> 18886 bytes public/assets/games/miro/thumbnail.png | Bin 0 -> 6602 bytes public/assets/games/mr-final-boss/screenshot1.png | Bin 0 -> 1450742 bytes public/assets/games/mr-final-boss/screenshot2.png | Bin 0 -> 1989016 bytes public/assets/games/mr-final-boss/screenshot3.png | Bin 0 -> 1439568 bytes public/assets/games/mr-final-boss/screenshot4.png | Bin 0 -> 2193361 bytes public/assets/games/mr-final-boss/thumbnail.png | Bin 0 -> 41705 bytes public/assets/games/riff-n-rage/screenshot1.png | Bin 0 -> 10890 bytes public/assets/games/riff-n-rage/screenshot2.png | Bin 0 -> 10833 bytes public/assets/games/riff-n-rage/thumbnail.png | Bin 0 -> 3685 bytes public/assets/games/survive-the-temple/cover.png | Bin 0 -> 7797 bytes .../assets/games/survive-the-temple/screenshot.png | Bin 0 -> 156733 bytes public/assets/games/thermal-trail/gameart.png | Bin 0 -> 34983 bytes public/assets/games/thermal-trail/screenshot1.png | Bin 0 -> 59904 bytes public/assets/games/thermal-trail/screenshot2.png | Bin 0 -> 50655 bytes public/assets/games/thermal-trail/screenshot3.png | Bin 0 -> 59175 bytes public/assets/games/thermal-trail/screenshot4.png | Bin 0 -> 61715 bytes public/assets/games/thermal-trail/thumbnail.png | Bin 0 -> 34372 bytes public/assets/games/third-party/screenshot1.jpg | Bin 0 -> 27953 bytes public/assets/games/third-party/screenshot2.jpg | Bin 0 -> 75854 bytes public/assets/games/third-party/screenshot3.jpg | Bin 0 -> 86290 bytes public/assets/games/third-party/screenshot4.jpg | Bin 0 -> 115453 bytes public/assets/games/third-party/thumbnail.png | Bin 0 -> 38018 bytes public/assets/games/wuzmag/screenshot1.png | Bin 0 -> 20205 bytes public/assets/games/wuzmag/screenshot2.png | Bin 0 -> 27941 bytes public/assets/games/wuzmag/screenshot3.png | Bin 0 -> 26136 bytes public/assets/games/wuzmag/thumbnail.png | Bin 0 -> 10256 bytes public/assets/games/your-left/screenshot1.jpg | Bin 0 -> 46052 bytes public/assets/games/your-left/screenshot2.jpg | Bin 0 -> 69916 bytes public/assets/games/your-left/screenshot3.jpg | Bin 0 -> 69533 bytes public/assets/games/your-left/thumbnail.png | Bin 0 -> 55977 bytes public/assets/images/android.png | Bin 0 -> 4572 bytes public/assets/images/android.svg | 56 + public/assets/images/cpp.png | Bin 0 -> 6630 bytes public/assets/images/cpp.svg | 41 + public/assets/images/csharp.png | Bin 0 -> 7481 bytes public/assets/images/csharp.svg | 58 + public/assets/images/duckpfp.png | Bin 0 -> 2602 bytes public/assets/images/favicon.png | Bin 0 -> 1608 bytes public/assets/images/favicon.svg | 54 + public/assets/images/github.png | Bin 0 -> 8133 bytes public/assets/images/github.svg | 66 + public/assets/images/godot.png | Bin 0 -> 7910 bytes public/assets/images/godot.svg | 64 + public/assets/images/html5.png | Bin 0 -> 4446 bytes public/assets/images/html5.svg | 56 + public/assets/images/hyprland.png | Bin 0 -> 6829 bytes public/assets/images/hyprland.svg | 50 + public/assets/images/linux.png | Bin 0 -> 3487 bytes public/assets/images/linux.svg | 42 + public/assets/images/macos.png | Bin 0 -> 2469 bytes public/assets/images/macos.svg | 39 + public/assets/images/monogame.png | Bin 0 -> 12087 bytes public/assets/images/pfp.png | Bin 0 -> 1074 bytes public/assets/images/placeholder.png | Bin 0 -> 9636 bytes public/assets/images/playstation.png | Bin 0 -> 5762 bytes public/assets/images/playstation.svg | 61 + public/assets/images/switch.png | Bin 0 -> 4736 bytes public/assets/images/switch.svg | 43 + public/assets/images/symbols.png | Bin 0 -> 431 bytes public/assets/images/thumbnail.png | Bin 0 -> 119178 bytes public/assets/images/thumbnail.svg | 70 ++ public/assets/images/thumbnail.zvg | 67 + public/assets/images/unity.png | Bin 0 -> 8893 bytes public/assets/images/unity.svg | 56 + public/assets/images/windows.png | Bin 0 -> 2446 bytes public/assets/images/windows.svg | 56 + public/assets/images/xamarin.png | Bin 0 -> 15826 bytes public/assets/images/xbox.png | Bin 0 -> 8038 bytes public/assets/images/xbox.svg | 74 ++ public/assets/index.css | 108 ++ public/assets/index.js | 91 ++ public/assets/loose/github-yellow.svg | 66 + public/assets/loose/itch-yellow.svg | 55 + public/assets/loose/twitter-yellow.svg | 91 ++ public/assets/loose/web-yellow.svg | 58 + public/assets/navbar.css | 278 +++++ public/assets/project.css | 126 ++ public/assets/projects/hyprload/logo.png | Bin 0 -> 31147 bytes public/assets/projects/spirit-mod/demo1.gif | Bin 0 -> 1075246 bytes public/assets/projects/spirit-mod/demo2.gif | Bin 0 -> 4380429 bytes public/assets/projects/spirit-mod/thumbnail.png | Bin 0 -> 73781 bytes public/assets/projects/spirit-mod/thumbnail.webp | Bin 0 -> 266196 bytes reluax.luax | 72 ++ 252 files changed, 4423 insertions(+), 4423 deletions(-) create mode 100644 blog/building-reluax.md create mode 100644 blog/cache.lua create mode 100644 blog/posts.lua create mode 100644 common.luax create mode 100644 components/animated_bg.luax create mode 100644 components/carousel.luax create mode 100644 components/navbar-item.luax create mode 100644 components/navbar.luax create mode 100644 components/project.luax create mode 100644 data/games.lua create mode 100644 data/projects.lua create mode 100644 libs/markdown.lua delete mode 100644 luax/blog/building-reluax.md delete mode 100644 luax/blog/cache.lua delete mode 100644 luax/blog/posts.lua delete mode 100644 luax/common.luax delete mode 100644 luax/components/animated_bg.luax delete mode 100644 luax/components/carousel.luax delete mode 100644 luax/components/navbar-item.luax delete mode 100644 luax/components/navbar.luax delete mode 100644 luax/components/project.luax delete mode 100644 luax/data/games.lua delete mode 100644 luax/data/projects.lua delete mode 100644 luax/libs/markdown.lua delete mode 100644 luax/not_found.luax delete mode 100644 luax/pages/blog.luax delete mode 100644 luax/pages/blogpost.luax delete mode 100644 luax/pages/contact.luax delete mode 100644 luax/pages/games.luax delete mode 100644 luax/pages/home.luax delete mode 100644 luax/pages/projects.luax delete mode 100644 luax/public/CNAME delete mode 100644 luax/public/assets/app.css delete mode 100644 luax/public/assets/blog.css delete mode 100644 luax/public/assets/blog/building-reluax/cli.png delete mode 100644 luax/public/assets/blog/building-reluax/first-demo.png delete mode 100644 luax/public/assets/blog/building-reluax/second-demo.png delete mode 100644 luax/public/assets/carousel.css delete mode 100644 luax/public/assets/contact.css delete mode 100644 luax/public/assets/games/bumbling-builders/screenshot1.png delete mode 100644 luax/public/assets/games/bumbling-builders/screenshot2.png delete mode 100644 luax/public/assets/games/bumbling-builders/screenshot3.png delete mode 100644 luax/public/assets/games/bumbling-builders/thumbnail.png delete mode 100644 luax/public/assets/games/dicemask/thumbnail.png delete mode 100644 luax/public/assets/games/greachers/screenshot1.png delete mode 100644 luax/public/assets/games/greachers/screenshot2.png delete mode 100644 luax/public/assets/games/greachers/screenshot3.png delete mode 100644 luax/public/assets/games/greachers/title.png delete mode 100644 luax/public/assets/games/loud/screenshot1.jpg delete mode 100644 luax/public/assets/games/loud/screenshot2.jpg delete mode 100644 luax/public/assets/games/loud/screenshot3.jpg delete mode 100644 luax/public/assets/games/loud/thumbnail.jpg delete mode 100644 luax/public/assets/games/miro/screenshot1.png delete mode 100644 luax/public/assets/games/miro/screenshot2.png delete mode 100644 luax/public/assets/games/miro/screenshot3.png delete mode 100644 luax/public/assets/games/miro/thumbnail.png delete mode 100644 luax/public/assets/games/mr-final-boss/screenshot1.png delete mode 100644 luax/public/assets/games/mr-final-boss/screenshot2.png delete mode 100644 luax/public/assets/games/mr-final-boss/screenshot3.png delete mode 100644 luax/public/assets/games/mr-final-boss/screenshot4.png delete mode 100644 luax/public/assets/games/mr-final-boss/thumbnail.png delete mode 100644 luax/public/assets/games/riff-n-rage/screenshot1.png delete mode 100644 luax/public/assets/games/riff-n-rage/screenshot2.png delete mode 100644 luax/public/assets/games/riff-n-rage/thumbnail.png delete mode 100644 luax/public/assets/games/survive-the-temple/cover.png delete mode 100644 luax/public/assets/games/survive-the-temple/screenshot.png delete mode 100644 luax/public/assets/games/thermal-trail/gameart.png delete mode 100644 luax/public/assets/games/thermal-trail/screenshot1.png delete mode 100644 luax/public/assets/games/thermal-trail/screenshot2.png delete mode 100644 luax/public/assets/games/thermal-trail/screenshot3.png delete mode 100644 luax/public/assets/games/thermal-trail/screenshot4.png delete mode 100644 luax/public/assets/games/thermal-trail/thumbnail.png delete mode 100644 luax/public/assets/games/third-party/screenshot1.jpg delete mode 100644 luax/public/assets/games/third-party/screenshot2.jpg delete mode 100644 luax/public/assets/games/third-party/screenshot3.jpg delete mode 100644 luax/public/assets/games/third-party/screenshot4.jpg delete mode 100644 luax/public/assets/games/third-party/thumbnail.png delete mode 100644 luax/public/assets/games/wuzmag/screenshot1.png delete mode 100644 luax/public/assets/games/wuzmag/screenshot2.png delete mode 100644 luax/public/assets/games/wuzmag/screenshot3.png delete mode 100644 luax/public/assets/games/wuzmag/thumbnail.png delete mode 100644 luax/public/assets/games/your-left/screenshot1.jpg delete mode 100644 luax/public/assets/games/your-left/screenshot2.jpg delete mode 100644 luax/public/assets/games/your-left/screenshot3.jpg delete mode 100644 luax/public/assets/games/your-left/thumbnail.png delete mode 100644 luax/public/assets/images/android.png delete mode 100644 luax/public/assets/images/android.svg delete mode 100644 luax/public/assets/images/cpp.png delete mode 100644 luax/public/assets/images/cpp.svg delete mode 100644 luax/public/assets/images/csharp.png delete mode 100644 luax/public/assets/images/csharp.svg delete mode 100644 luax/public/assets/images/duckpfp.png delete mode 100644 luax/public/assets/images/favicon.png delete mode 100644 luax/public/assets/images/favicon.svg delete mode 100644 luax/public/assets/images/github.png delete mode 100644 luax/public/assets/images/github.svg delete mode 100644 luax/public/assets/images/godot.png delete mode 100644 luax/public/assets/images/godot.svg delete mode 100644 luax/public/assets/images/html5.png delete mode 100644 luax/public/assets/images/html5.svg delete mode 100644 luax/public/assets/images/hyprland.png delete mode 100644 luax/public/assets/images/hyprland.svg delete mode 100644 luax/public/assets/images/linux.png delete mode 100644 luax/public/assets/images/linux.svg delete mode 100644 luax/public/assets/images/macos.png delete mode 100644 luax/public/assets/images/macos.svg delete mode 100644 luax/public/assets/images/monogame.png delete mode 100644 luax/public/assets/images/pfp.png delete mode 100644 luax/public/assets/images/placeholder.png delete mode 100644 luax/public/assets/images/playstation.png delete mode 100644 luax/public/assets/images/playstation.svg delete mode 100644 luax/public/assets/images/switch.png delete mode 100644 luax/public/assets/images/switch.svg delete mode 100644 luax/public/assets/images/symbols.png delete mode 100644 luax/public/assets/images/thumbnail.png delete mode 100644 luax/public/assets/images/thumbnail.svg delete mode 100644 luax/public/assets/images/thumbnail.zvg delete mode 100644 luax/public/assets/images/unity.png delete mode 100644 luax/public/assets/images/unity.svg delete mode 100644 luax/public/assets/images/windows.png delete mode 100644 luax/public/assets/images/windows.svg delete mode 100644 luax/public/assets/images/xamarin.png delete mode 100644 luax/public/assets/images/xbox.png delete mode 100644 luax/public/assets/images/xbox.svg delete mode 100644 luax/public/assets/index.css delete mode 100644 luax/public/assets/index.js delete mode 100644 luax/public/assets/loose/github-yellow.svg delete mode 100644 luax/public/assets/loose/itch-yellow.svg delete mode 100644 luax/public/assets/loose/twitter-yellow.svg delete mode 100644 luax/public/assets/loose/web-yellow.svg delete mode 100644 luax/public/assets/navbar.css delete mode 100644 luax/public/assets/project.css delete mode 100644 luax/public/assets/projects/hyprload/logo.png delete mode 100644 luax/public/assets/projects/spirit-mod/demo1.gif delete mode 100644 luax/public/assets/projects/spirit-mod/demo2.gif delete mode 100644 luax/public/assets/projects/spirit-mod/thumbnail.png delete mode 100644 luax/public/assets/projects/spirit-mod/thumbnail.webp delete mode 100644 luax/reluax.luax create mode 100644 not_found.luax create mode 100644 pages/blog.luax create mode 100644 pages/blogpost.luax create mode 100644 pages/contact.luax create mode 100644 pages/games.luax create mode 100644 pages/home.luax create mode 100644 pages/projects.luax create mode 100644 public/CNAME create mode 100644 public/assets/app.css create mode 100644 public/assets/blog.css create mode 100644 public/assets/blog/building-reluax/cli.png create mode 100644 public/assets/blog/building-reluax/first-demo.png create mode 100644 public/assets/blog/building-reluax/second-demo.png create mode 100644 public/assets/carousel.css create mode 100644 public/assets/contact.css create mode 100644 public/assets/games/bumbling-builders/screenshot1.png create mode 100644 public/assets/games/bumbling-builders/screenshot2.png create mode 100644 public/assets/games/bumbling-builders/screenshot3.png create mode 100644 public/assets/games/bumbling-builders/thumbnail.png create mode 100644 public/assets/games/dicemask/thumbnail.png create mode 100644 public/assets/games/greachers/screenshot1.png create mode 100644 public/assets/games/greachers/screenshot2.png create mode 100644 public/assets/games/greachers/screenshot3.png create mode 100644 public/assets/games/greachers/title.png create mode 100644 public/assets/games/loud/screenshot1.jpg create mode 100644 public/assets/games/loud/screenshot2.jpg create mode 100644 public/assets/games/loud/screenshot3.jpg create mode 100644 public/assets/games/loud/thumbnail.jpg create mode 100644 public/assets/games/miro/screenshot1.png create mode 100644 public/assets/games/miro/screenshot2.png create mode 100644 public/assets/games/miro/screenshot3.png create mode 100644 public/assets/games/miro/thumbnail.png create mode 100644 public/assets/games/mr-final-boss/screenshot1.png create mode 100644 public/assets/games/mr-final-boss/screenshot2.png create mode 100644 public/assets/games/mr-final-boss/screenshot3.png create mode 100644 public/assets/games/mr-final-boss/screenshot4.png create mode 100644 public/assets/games/mr-final-boss/thumbnail.png create mode 100644 public/assets/games/riff-n-rage/screenshot1.png create mode 100644 public/assets/games/riff-n-rage/screenshot2.png create mode 100644 public/assets/games/riff-n-rage/thumbnail.png create mode 100644 public/assets/games/survive-the-temple/cover.png create mode 100644 public/assets/games/survive-the-temple/screenshot.png create mode 100644 public/assets/games/thermal-trail/gameart.png create mode 100644 public/assets/games/thermal-trail/screenshot1.png create mode 100644 public/assets/games/thermal-trail/screenshot2.png create mode 100644 public/assets/games/thermal-trail/screenshot3.png create mode 100644 public/assets/games/thermal-trail/screenshot4.png create mode 100644 public/assets/games/thermal-trail/thumbnail.png create mode 100644 public/assets/games/third-party/screenshot1.jpg create mode 100644 public/assets/games/third-party/screenshot2.jpg create mode 100644 public/assets/games/third-party/screenshot3.jpg create mode 100644 public/assets/games/third-party/screenshot4.jpg create mode 100644 public/assets/games/third-party/thumbnail.png create mode 100644 public/assets/games/wuzmag/screenshot1.png create mode 100644 public/assets/games/wuzmag/screenshot2.png create mode 100644 public/assets/games/wuzmag/screenshot3.png create mode 100644 public/assets/games/wuzmag/thumbnail.png create mode 100644 public/assets/games/your-left/screenshot1.jpg create mode 100644 public/assets/games/your-left/screenshot2.jpg create mode 100644 public/assets/games/your-left/screenshot3.jpg create mode 100644 public/assets/games/your-left/thumbnail.png create mode 100644 public/assets/images/android.png create mode 100644 public/assets/images/android.svg create mode 100644 public/assets/images/cpp.png create mode 100644 public/assets/images/cpp.svg create mode 100644 public/assets/images/csharp.png create mode 100644 public/assets/images/csharp.svg create mode 100644 public/assets/images/duckpfp.png create mode 100644 public/assets/images/favicon.png create mode 100644 public/assets/images/favicon.svg create mode 100644 public/assets/images/github.png create mode 100644 public/assets/images/github.svg create mode 100644 public/assets/images/godot.png create mode 100644 public/assets/images/godot.svg create mode 100644 public/assets/images/html5.png create mode 100644 public/assets/images/html5.svg create mode 100644 public/assets/images/hyprland.png create mode 100644 public/assets/images/hyprland.svg create mode 100644 public/assets/images/linux.png create mode 100644 public/assets/images/linux.svg create mode 100644 public/assets/images/macos.png create mode 100644 public/assets/images/macos.svg create mode 100644 public/assets/images/monogame.png create mode 100644 public/assets/images/pfp.png create mode 100644 public/assets/images/placeholder.png create mode 100644 public/assets/images/playstation.png create mode 100644 public/assets/images/playstation.svg create mode 100644 public/assets/images/switch.png create mode 100644 public/assets/images/switch.svg create mode 100644 public/assets/images/symbols.png create mode 100644 public/assets/images/thumbnail.png create mode 100644 public/assets/images/thumbnail.svg create mode 100644 public/assets/images/thumbnail.zvg create mode 100644 public/assets/images/unity.png create mode 100644 public/assets/images/unity.svg create mode 100644 public/assets/images/windows.png create mode 100644 public/assets/images/windows.svg create mode 100644 public/assets/images/xamarin.png create mode 100644 public/assets/images/xbox.png create mode 100644 public/assets/images/xbox.svg create mode 100644 public/assets/index.css create mode 100644 public/assets/index.js create mode 100644 public/assets/loose/github-yellow.svg create mode 100644 public/assets/loose/itch-yellow.svg create mode 100644 public/assets/loose/twitter-yellow.svg create mode 100644 public/assets/loose/web-yellow.svg create mode 100644 public/assets/navbar.css create mode 100644 public/assets/project.css create mode 100644 public/assets/projects/hyprload/logo.png create mode 100644 public/assets/projects/spirit-mod/demo1.gif create mode 100644 public/assets/projects/spirit-mod/demo2.gif create mode 100644 public/assets/projects/spirit-mod/thumbnail.png create mode 100644 public/assets/projects/spirit-mod/thumbnail.webp create mode 100644 reluax.luax diff --git a/blog/building-reluax.md b/blog/building-reluax.md new file mode 100644 index 0000000..08f8b5d --- /dev/null +++ b/blog/building-reluax.md @@ -0,0 +1,211 @@ +## The inspiration +Around a week ago, I was working on some projects, as usual, and had YouTube playing on the +second monitor. The video roulette landed on +[ThePrimagen](https://www.youtube.com/watch?v=r4HyasuvjKM). This time he was reading a post that +really intrigued me, because it shared many of the sentiments that were boiling in my head. This +website you're reading was originally written in raw HTML, served on GitHub pages. This was +obviously not ideal for maintenance. Adding anything involved copypasting large chunks of +code, changing anything about the layout required changes across multiple files. Not a very +satisfying process, and it made me avoid doing anything with it. + +Then, I had to learn React. It was pretty neat. I could define *actual, reusable components*. +I could actually write logic to manipulate data! Have it structured! Write functions! And it +only needed small changes to the actual HTML of my site, I just had to move it around a bit +so it sat right in the middle of some TypeScript code. + +I thought this was a really neat idea. So I rewrote the old website in it, and even added some +*visual flair*, like the shader at the top of this page (I had to show off my love of graphics +programming *somehow*). And to manage the project, I used [Vite](https://vitejs.dev/), which let +me easily pack up the project into static files, and still serve them from GitHub pages (with a +neat tool to automate it, [react-gh-pages](https://github.com/gitname/react-gh-pages)). I was +pretty proud of how it looked, and it was a good experience to write. + +But after a while, I wanted to build a blog. Everyone has one, right? Why not me? I can do +interesting things too. But as of then, the project and games sections of the website were just +big JSON files, included into the TS of the React project. I couldn't think of a simpler way to +have the data and still keep all of the site static. I did not really want to pay for hosting if +I didn't have to. But a project description needed formatting sometimes, so it used a rather ugly +way of just storing the HTML as a string in the JSON, like so: + +``` +json +[ + { + "name": "Mr Final Boss", + "name_normalized": "mr-final-boss", + "type": "game", + "link": "https://jellingfish-games.itch.io/mr-final-boss", + "tech": [ + "unity", + "csharp" + ], + "targets": [ + "windows", + "html5" + ], + "images": [ + "thumbnail.png", + "screenshot1.png", + "screenshot2.png", + "screenshot3.png", + "screenshot4.png" + ], + "description": "The legends say that one day, brave Heroes will slay the evil and malicious Ignatius, the Chaos Bringer... But YOU won't go down without a fight! Be the bossfight everyone is dreading and hunt down as many Heroes as you can in your last stand using a variety of abilities you can choose from!
Game made in 72 hours for Ludum Dare 50, with the theme \"Delay the Inevitable\"." + } +] +``` + +Since it's JSON, the string couldn't have line breaks, which is really just something I could +live with when the text was a short summary. This would just not work for blog posts. To actually +write blog posts, I wanted to use the beloved, old classic: Markdown. So I would basically have +two options: either have *another* preprocessing step when building the website, which would +require writing some more scripts and making the process of deployment more complicated. It would +also mean I would store all the content of all the blog posts in the code that gets served +to anyone who visits any page on the site, which just felt stupid, even if text is a fraction of +the size of a single image. + +The alternative was serving markdown files in full, and processing them into HTML client-side. +Now, this is my personal website, a site serving probably an average of the 2 times a month I +visit it myself to show people. But it still felt like a really dumb way to do things. I'm sure +many sites do more intensive processing client-side, but I just couldn't think for a good reason +"*why?*" + +To solve these issues, I would have to unavoidably write code. There were many options for what +paths I could take, such as rewriting my site in a Static Site Generator like +[Hugo](https://gohugo.io/), or writing a server in whatever of the countless options exist. +I didn't want to go the templating route. It felt really limiting after encountering JSX, and I +would have to rewrite my site inside-out, with a different approach. I could split my codebase in +half, keeping the React site as the front but making it actually do its job of requesting data +from the backend. I felt like doing it that way just for serving blog posts would grow the effort +required to change anything about the site yet again, which was something I really would rather +not do. The entire point is to be able to enjoy making this stuff. + +### The LuaX revelation +Coming back to the beginning of the article, I watched a video of ThePrimagen reading a +fascinating blog post, +[I made JSX for Lua (because I hate static sites)](https://bvisness.me/luax) by Ben Visness. +He seemed to share my feeling about the approach to managing HTML, and how JSX was really +convenient. And his solution was simple, powerful, and perhaps most importantly, cool. Writing a +preprocessor to create a custom dialect of Lua. And he finished the blog post with a note of +encouragement: +> I don’t know what the future holds for LuaX. It’s not ready for other people to use, and I’m +> not sure it will ever be. So rather than tell you all to go to luax dot dev and start building +> your websites with my tool, here’s what I’ll say instead: +> +> Try building something for yourself. Try writing code for you, and you alone. Don’t worry about +> whether it will look good on your resumé or attract lots of stars on GitHub. Just write +> something that feels good to you. Explore a weird idea and see where it takes you. +> +> Who knows—maybe someday other people will like it too. + +So I set out to recreate it. After all, I wanted my site to run on LuaX as well, and I wouldn't +be deterred by inconvenient facts such as the fact it doesn't really exist yet. + +### Enter Reluax +I decided to name my version `reluax`, since it is a remake, and it was relaxing to write on the +side, procrastinating other responsibilities. I wrote the bulk of it in Rust, since I had a +experience writing a language in it from writing [brakion](https://github.com/Duckonaut/brakion), +and I could copy the lexer almost in full. Rust would also provide a good ecosystem for the +HTTP server portion, with battle-tested libraries like [hyper](https://hyper.rs/). I didn't go +for any of the higher-level libraries like [axum](https://github.com/tokio-rs/axum), since I +felt like I wouldn't really benefit from them. What I was building was not to be a layer on top +of one, but a replacement of it. + +The parser portion took a bit, but it was not anything too complex. Writing a lexer that +simultaneously structures the data, but can emit whatever garbage comes inside of an HTML tag, was +a bit tricky. But in two evenings I had a demo: + +![the first demo](https://cdn.duckonaut.dev/assets/blog/building-reluax/first-demo.png) + +It was a bit rough, but proved it was all working. The next day I had something akin to React's +functional components: + +![the second demo](https://cdn.duckonaut.dev/assets/blog/building-reluax/second-demo.png) + +And on the third day, this site you're reading right now was running fully on Reluax. I've also +taken the extra time to add some nicer output to the program, just for fun. + +![cli with emojis](https://cdn.duckonaut.dev/assets/blog/building-reluax/cli.png) + +And of course, with making a custom dialect of Lua, I would throw away all the goodies that give +modern developers a good experience writing code, like syntax highlighting or a language server. +Not content with that, I threw together a tree-sitter parser to at least have syntax highlighting +back in my beloved Neovim. I've written a couple in the past, so adapting the +[tree-sitter-lua](https://github.com/tjdevries/tree-sitter-lua) grammar only took a cople hours. +The syntax highlighting of this blog is powered by tree-sitter, so you can enjoy a pretty +view of how the entry point of this website looks! + +``` +luax +local Common = require("common") +local Navbar = require("components.navbar") +local AnimatedBg = require("components.animated_bg") + +local Home = require("pages.home") +local Projects = require("pages.projects") +local Games = require("pages.games") +local Contact = require("pages.contact") +local Blogpost = require("pages.blogpost") +local Blog = require("pages.blog") +local NotFound = require("not_found") + +local blogposts = require("blog.posts") + +math.randomseed(os.time()) + +local function wrap_component(component) + return + + +
+
+ {$ component $} + +
+
+end + +local function route(path) + if path == '/' then + return 200, wrap_component() + elseif path == '/projects' then + return 200, wrap_component() + elseif path == '/games' then + return 200, wrap_component() + elseif path == '/contact' then + return 200, wrap_component() + elseif path == '/blog' then + return 200, wrap_component() + elseif reluax.url_matches('/blog/{slug}', path) then + local slug = reluax.url_extract('/blog/{slug}', path).slug + -- blogposts is a list of blogposts, with the slug a property of each + local post = nil + for _, p in ipairs(blogposts) do + if p.slug == slug then + post = p + break + end + end + if post then + local article = wrap_component() + return 200, article + end + end + + return 404, NotFound() +end + +return { + name = "duckonaut.dev", + route = route +} +``` + +After that, I've taken some time to wrap Reluax in a Nix flake so I could easily run it on my +Oracle Cloud server, add some descriptions to the CLI help, things like that. And now I'm writing +this blog post, satisfied with how quickly it all came together. + +Building things is fun! I highly recommend it. This project gave me a really fun distraction, and +now I have my site running on something I made, something I have full control over. That's a +really good feeling. And I won't end the article the same way as Ben: you can check out the early +version of Reluax over at [GitHub](https://github.com/Duckonaut/reluax) and try it for yourself. diff --git a/blog/cache.lua b/blog/cache.lua new file mode 100644 index 0000000..e8f5b0c --- /dev/null +++ b/blog/cache.lua @@ -0,0 +1,23 @@ +local md = require "libs.markdown" + +local blog_cache = {} + +local function get_blog_post_content(post) + local slug = post.slug + if blog_cache[slug] then + return blog_cache[slug] + end + -- pull markdown content from file + local content = io.open("blog/" .. slug .. ".md", "r"):read("*a") + + if content then + -- convert markdown to html + content = md(content) + end + + blog_cache[slug] = content + + return content +end + +return get_blog_post_content diff --git a/blog/posts.lua b/blog/posts.lua new file mode 100644 index 0000000..f1d8408 --- /dev/null +++ b/blog/posts.lua @@ -0,0 +1,8 @@ +return { + { + title = "I (re)made JSX for Lua", + slug = 'building-reluax', + date = os.time({year=2024, month=1, day=13}), + description = "Because it was too good to pass up.", + }, +} diff --git a/common.luax b/common.luax new file mode 100644 index 0000000..ab29a02 --- /dev/null +++ b/common.luax @@ -0,0 +1,78 @@ +return function(props) + local use_cdn = reluax.dev_mode == false + local asset_prefix = use_cdn and "https://cdn.duckonaut.dev" or "" + + local thumbnail = props.attrs.thumbnail or + asset_prefix .. "/assets/images/thumbnail.png" + local title = props.attrs.title or "duckonaut.dev" + local description = props.attrs.description or "duckonaut's stuff" + local stylesheelts = props.attrs.stylesheets or + { 'app.css', + 'index.css', + 'contact.css', + 'project.css', + 'navbar.css', + 'carousel.css', + 'blog.css' + } + local scripts = props.attrs.scripts or + { 'index.js' } + + local style_tags = {} + for _, stylesheet in ipairs(stylesheelts) do + style_tags[#style_tags + 1] = + + end + + local script_tags = {} + for _, script in ipairs(scripts) do + script_tags[#script_tags + 1] = + + end + + return + + + + + + + + + + + + + + + + {$ script_tags $} + + {$ style_tags $} + + + + + + + + + + + {$ title $} + + + +
{$ props.children $}
+ + + +end diff --git a/components/animated_bg.luax b/components/animated_bg.luax new file mode 100644 index 0000000..0c5105c --- /dev/null +++ b/components/animated_bg.luax @@ -0,0 +1,3 @@ +return function() + return
+end diff --git a/components/carousel.luax b/components/carousel.luax new file mode 100644 index 0000000..f2c5fbe --- /dev/null +++ b/components/carousel.luax @@ -0,0 +1,10 @@ +return function(props) + if props == nil or props.children == nil or #props.children == 0 then + return nil + end + + return + +end diff --git a/components/navbar-item.luax b/components/navbar-item.luax new file mode 100644 index 0000000..d201f82 --- /dev/null +++ b/components/navbar-item.luax @@ -0,0 +1,6 @@ +return function(props) + return + + {$ props.children $} + +end diff --git a/components/navbar.luax b/components/navbar.luax new file mode 100644 index 0000000..4c0c54b --- /dev/null +++ b/components/navbar.luax @@ -0,0 +1,18 @@ +local NavbarItem = require('components.navbar-item') + +return function() + return + +end diff --git a/components/project.luax b/components/project.luax new file mode 100644 index 0000000..cad844f --- /dev/null +++ b/components/project.luax @@ -0,0 +1,62 @@ +local Carousel = require('components.carousel') + +local asset_prefix = reluax.dev_mode and '/assets/' or 'https://cdn.duckonaut.dev/assets/' + +function getProjectImageLink(project, image) + return asset_prefix .. project.type .. 's/' .. project.name_normalized .. '/' .. image +end + +function getProjectTechIconLink(tech) + return asset_prefix .. 'images/' .. tech .. '.png' +end + +function getProjectTargetIconLink(target) + return asset_prefix .. 'images/' .. target .. '.png' +end + +return function (props) + local project = props.attrs.project + local techs = {} + for _, tech in ipairs(project.tech) do + techs[#techs + 1] = + end + local targets = {} + for _, target in ipairs(project.targets) do + targets[#targets + 1] = + end + + local images = nil + if project.images then + images = {} + for _, image in ipairs(project.images) do + images[#images + 1] = + end + + images = {$ images $} + end + + return ( +
+ +
+
+
Made using
+
+ {$ techs $} +
+
+
+
Available on
+
+ {$ targets $} +
+
+
+
+ {$ project.description $} +
+
+ {$ images $} +
+ ) +end diff --git a/data/games.lua b/data/games.lua new file mode 100644 index 0000000..643f48f --- /dev/null +++ b/data/games.lua @@ -0,0 +1,229 @@ +return { + { + name = "Mr Final Boss", + name_normalized = "mr-final-boss", + type = "game", + link = "https://jellingfish-games.itch.io/mr-final-boss", + tech = { + "unity", + "csharp" + }, + targets = { + "windows", + "html5" + }, + images = { + "thumbnail.png", + "screenshot1.png", + "screenshot2.png", + "screenshot3.png", + "screenshot4.png" + }, + description = "The legends say that one day, brave Heroes will slay the evil and malicious Ignatius, the Chaos Bringer... But YOU won't go down without a fight! Be the bossfight everyone is dreading and hunt down as many Heroes as you can in your last stand using a variety of abilities you can choose from!
Game made in 72 hours for Ludum Dare 50, with the theme \"Delay the Inevitable\"." + }, + { + name = "BUMBLING BUILDERS", + name_normalized = "bumbling-builders", + type = "game", + link = "https://oroshibu.itch.io/bumbling-builders", + tech = { + "unity", + "csharp" + }, + targets = { + "windows", + "html5" + }, + images = { + "thumbnail.png", + "screenshot1.png", + "screenshot2.png", + "screenshot3.png" + }, + description = "A physics puzzle game in which you need to protect little creatures from danger that could come from any direction. Build forts with tetris-style blocks. The game features 30+ levels, multiple distinct enemies and a star rating system.
Game made in 72 hours for Ludum Dare 49, with the theme \"Unstable\"." + }, + { + name = "LOUD: My Road To Fame", + name_normalized = "loud", + type = "game", + link = "https://store.steampowered.com/app/1838290/LOUD_My_Road_to_Fame/", + tech = { + "unity", + "csharp" + }, + targets = { + "windows", + "xbox", + "playstation", + "switch" + }, + images = { + "thumbnail.jpg", + "screenshot1.jpg", + "screenshot2.jpg", + "screenshot3.jpg" + }, + description = "A wholesome, music-filled arcade rhythm game in which a young, reclusive rocker goes on a journey of self-discovery. Once she begins to follow her dreams and finds her inner voice, she discovers that it is LOUD!
One of the games I've worked on at Hyperstrange" + }, + { + name = "WUZMAG", + name_normalized = "wuzmag", + type = "game", + link = "https://duckonaut.itch.io/wuzmag", + tech = { + "unity", + "csharp" + }, + targets = { + "windows", + "html5" + }, + images = { + "thumbnail.png", + "screenshot1.png", + "screenshot2.png", + "screenshot3.png" + }, + description = "Turn based arcade survival game. Survive as long as you can by freezing enemies and using them to defeat more enemies.
Game made in roughly 8 hours for a private game jam with friends." + }, + { + name = "Miro", + name_normalized = "miro", + type = "game", + link = "https://duckonaut.itch.io/miro", + tech = { + "monogame", + "csharp" + }, + targets = { + "windows" + }, + images = { + "thumbnail.png", + "screenshot1.png", + "screenshot2.png", + "screenshot3.png" + }, + description = "Small chill game made mostly as an experiment for a nice 2D renderer mechanic. Infect the world around you, breaking it down into its simplest components.
Game made in roughly 8 hours for a private game jam with friends." + }, + { + name = "Thermal Trail", + name_normalized = "thermal-trail", + type = "game", + link = "https://duckonaut.itch.io/thermal-trail", + tech = { + "godot", + "csharp" + }, + targets = { + "windows", + "html5" + }, + images = { + "gameart.png", + "screenshot1.png", + "screenshot2.png", + "screenshot3.png", + "screenshot4.png" + }, + description = "Play as Yolka, a mountain goat who found an unknown egg. Keep the egg warm as you navigate the 3 distinct worlds, and bring it to the hottest place you know: the depths of a volcano.
Game made in a week for Newgrounds July Jam 2021, with the theme \"Egg\"." + }, + { + name = "Greachers", + name_normalized = "greachers", + type = "game", + link = "https://duckonaut.itch.io/greachers", + tech = { + "monogame", + "csharp" + }, + targets = { + "windows" + }, + images = { + "title.png", + "screenshot1.png", + "screenshot2.png", + "screenshot3.png" + }, + description = "Tiny idle simulation game where you manage a team of randomly generated little fellas as they fight other greachers in a sidescrolling world with beautiful procedurally generated graphics.
Game made in 2 weeks for LOWREZJAM 2021, with the theme \"Minibeasts\"." + }, + { + name = "Third Party", + name_normalized = "third-party", + type = "game", + link = "https://duckonaut.itch.io/third-party", + tech = { + "godot" + }, + targets = { + "windows", + "html5" + }, + images = { + "thumbnail.png", + "screenshot1.jpg", + "screenshot2.jpg", + "screenshot3.jpg", + "screenshot4.jpg" + }, + description = "A top-down stealth game where you play as a spy tasked with stealing important data from a rivaling government. Your task would be easy enough if your objective wasn’t in a underground top-secret facility handling an alien outbreak. Without access to conventional stealth techniques, use corpses of your enemies to sneak through the battlefield.
Game made in 72 hours for Ludum Dare 48, with the theme \"Deeper and deeper\"." + }, + { + name = "Your Left", + name_normalized = "your-left", + type = "game", + link = "https://xatriv.itch.io/your-left", + tech = { + "unity", + "csharp" + }, + targets = { + "windows" + }, + images = { + "thumbnail.png", + "screenshot1.jpg", + "screenshot2.jpg", + "screenshot3.jpg" + }, + description = "A simple pacman-inspired arcade game. Try to grab all the stars in a randomly generated maze before the time expires, while your controls are scattered all over the screen. Get as far as you can to get the highest score.
Game made in 8 hours for Jam Jam, a local in-person event organized by KNTG Polygon, with the theme \"One Click\"." + }, + { + name = "Riff n' Rage", + name_normalized = "riff-n-rage", + type = "game", + link = "https://duckonaut.itch.io/riff-n-rage", + tech = { + "monogame", + "csharp" + }, + targets = { + "windows" + }, + images = { + "thumbnail.png", + "screenshot1.png", + "screenshot2.png" + }, + description = "You just got your first guitar, and through sheer luck managed to land in the king's throne room, trying to entertain him. Sadly, his subjects aren't huge fans of your music. Dodge them while trying to play the best you can, or the king won't be merciful.
Game made in 48 hours for a special solo game jam among friends. My theme was \"Power at a price\"." + }, + { + name = "Survive the Temple", + name_normalized = "survive-the-temple", + type = "game", + link = "https://duckonaut.itch.io/survive-the-temple", + tech = { + "godot" + }, + targets = { + "windows", + "html5" + }, + images = { + "cover.png", + "screenshot.png" + }, + description = "A quick movement focused platformer that tests your short term memory. Survive the trap system of an ancient temple! Dash through the lanterns in the correct order before the time runs out, or face certain death!
Game made in 48 hours for Ludum Dare Compo 47 with the theme \"Stuck in a loop\"." + } +} diff --git a/data/projects.lua b/data/projects.lua new file mode 100644 index 0000000..5248a2a --- /dev/null +++ b/data/projects.lua @@ -0,0 +1,56 @@ +return { + { + name = "hyprload", + name_normalized = "hyprload", + type = "project", + link = "https://github.com/duckonaut/hyprload", + tech = { + "cpp" + }, + targets = { + "linux", + "hyprland" + }, + images = { + "logo.png" + }, + description = + "hyprload acts as a plugin manager for the Hyprland Wayland Compositor. It's loaded as a plugin and allows easy, declarative installation and updates of other plugins." + }, + { + name = "split-monitor-workspaces", + name_normalized = "split-monitor-workspaces", + type = "project", + link = "https://github.com/duckonaut/split-monitor-workspaces", + tech = { + "cpp" + }, + targets = { + "linux", + "hyprland" + }, + description = + "A small plugin for the Hyprland Wayland Compositor that allows you to have different workspace namespaces on different monitors." + }, + { + name = "Spirit Mod", + name_normalized = "spirit-mod", + type = "project", + link = "https://spiritmod.fandom.com/wiki/Spirit_Mod_Wiki", + tech = { + "csharp" + }, + targets = { + "windows", + "linux", + "macos" + }, + images = { + "thumbnail.png", + "demo1.gif", + "demo2.gif" + }, + description = + "The Spirit Mod is one of the largest mods for Terraria: with more than 1300 items, 8 bosses, and hundreds of enemies, as well as 3 new events and 3 brand new biomes, the mod has a lot to offer.
I had the privilege of contributing some system code, mainly the \"Stargoop\" system." + } +} diff --git a/libs/markdown.lua b/libs/markdown.lua new file mode 100644 index 0000000..3fe90c1 --- /dev/null +++ b/libs/markdown.lua @@ -0,0 +1,1281 @@ +#!/usr/bin/env lua + +---------------------------------------------------------------------- +-- Utility functions +---------------------------------------------------------------------- + +local unpack = table.unpack or unpack + +-- Returns the result of mapping the values in table t through the function f +local function map(t, f) + local out = {} + for k,v in pairs(t) do out[k] = f(v,k) end + return out +end + +-- Functional style if statement. (NOTE: no short circuit evaluation) +local function iff(t, a, b) if t then return a else return b end end + +-- Splits the text into an array of separate lines. +local function split(text, sep) + sep = sep or "\n" + local lines = {} + local pos = 1 + while true do + local b,e = text:find(sep, pos) + if not b then table.insert(lines, text:sub(pos)) break end + table.insert(lines, text:sub(pos, b-1)) + pos = e + 1 + end + return lines +end + +-- Converts tabs to spaces +local function detab(text) + local tab_width = 4 + local function rep(match) + local spaces = -match:len() + while spaces<1 do spaces = spaces + tab_width end + return match .. string.rep(" ", spaces) + end + text = text:gsub("([^\n]-)\t", rep) + return text +end + +-- Applies string.find for every pattern in the list and returns the first match +local function find_first(s, patterns, index) + local res = {} + for _,p in ipairs(patterns) do + local match = {s:find(p, index)} + if #match>0 and (#res==0 or match[1] < res[1]) then res = match end + end + return unpack(res) +end + +-- If a replacement array is specified, the range [start, stop] in the array is replaced +-- with the replacement array and the resulting array is returned. Without a replacement +-- array the section of the array between start and stop is returned. +local function splice(array, start, stop, replacement) + if replacement then + local n = stop - start + 1 + while n > 0 do + table.remove(array, start) + n = n - 1 + end + for _,v in ipairs(replacement) do + table.insert(array, start, v) + end + return array + else + local res = {} + for i = start,stop do + table.insert(res, array[i]) + end + return res + end +end + +-- Outdents the text one step. +local function outdent(text) + text = "\n" .. text + text = text:gsub("\n ? ? ?", "\n") + text = text:sub(2) + return text +end + +-- Indents the text one step. +local function indent(text) + text = text:gsub("\n", "\n ") + return text +end + +-- Does a simple tokenization of html data. Returns the data as a list of tokens. +-- Each token is a table with a type field (which is either "tag" or "text") and +-- a text field (which contains the original token data). +local function tokenize_html(html) + local tokens = {} + local pos = 1 + while true do + local start = find_first(html, {"", start) + elseif html:match("^<%?", start) then + _,stop = html:find("?>", start) + else + _,stop = html:find("%b<>", start) + end + if not stop then + -- error("Could not match html tag " .. html:sub(start,start+30)) + table.insert(tokens, {type="text", text=html:sub(start, start)}) + pos = start + 1 + else + table.insert(tokens, {type="tag", text=html:sub(start, stop)}) + pos = stop + 1 + end + end + return tokens +end + +---------------------------------------------------------------------- +-- Hash +---------------------------------------------------------------------- + +-- This is used to "hash" data into alphanumeric strings that are unique +-- in the document. (Note that this is not cryptographic hash, the hash +-- function is not one-way.) The hash procedure is used to protect parts +-- of the document from further processing. + +local HASH = { + -- Has the hash been inited. + inited = false, + + -- The unique string prepended to all hash values. This is to ensure + -- that hash values do not accidently coincide with an actual existing + -- string in the document. + identifier = "", + + -- Counter that counts up for each new hash instance. + counter = 0, + + -- Hash table. + table = {} +} + +-- Inits hashing. Creates a hash_identifier that doesn't occur anywhere +-- in the text. +local function init_hash(text) + HASH.inited = true + HASH.identifier = "" + HASH.counter = 0 + HASH.table = {} + + local s = "HASH" + local counter = 0 + local id + while true do + id = s .. counter + if not text:find(id, 1, true) then break end + counter = counter + 1 + end + HASH.identifier = id +end + +-- Returns the hashed value for s. +local function hash(s) + assert(HASH.inited) + if not HASH.table[s] then + HASH.counter = HASH.counter + 1 + local id = HASH.identifier .. HASH.counter .. "X" + HASH.table[s] = id + end + return HASH.table[s] +end + +---------------------------------------------------------------------- +-- Protection +---------------------------------------------------------------------- + +-- The protection module is used to "protect" parts of a document +-- so that they are not modified by subsequent processing steps. +-- Protected parts are saved in a table for later unprotection + +-- Protection data +local PD = { + -- Saved blocks that have been converted + blocks = {}, + + -- Block level tags that will be protected + tags = {"p", "div", "h1", "h2", "h3", "h4", "h5", "h6", "blockquote", + "pre", "table", "dl", "ol", "ul", "script", "noscript", "form", "fieldset", + "iframe", "math", "ins", "del"} +} + +-- Pattern for matching a block tag that begins and ends in the leftmost +-- column and may contain indented subtags, i.e. +--
+-- A nested block. +--
+-- Nested data. +--
+--
+local function block_pattern(tag) + return "\n<" .. tag .. ".-\n[ \t]*\n" +end + +-- Pattern for matching a block tag that begins and ends with a newline +local function line_pattern(tag) + return "\n<" .. tag .. ".-[ \t]*\n" +end + +-- Protects the range of characters from start to stop in the text and +-- returns the protected string. +local function protect_range(text, start, stop) + local s = text:sub(start, stop) + local h = hash(s) + PD.blocks[h] = s + text = text:sub(1,start) .. h .. text:sub(stop) + return text +end + +-- Protect every part of the text that matches any of the patterns. The first +-- matching pattern is protected first, etc. +local function protect_matches(text, patterns) + while true do + local start, stop = find_first(text, patterns) + if not start then break end + text = protect_range(text, start, stop) + end + return text +end + +-- Protects blocklevel tags in the specified text +local function protect(text) + -- First protect potentially nested block tags + text = protect_matches(text, map(PD.tags, block_pattern)) + -- Then protect block tags at the line level. + text = protect_matches(text, map(PD.tags, line_pattern)) + -- Protect
and comment tags + text = protect_matches(text, {"\n]->[ \t]*\n"}) + text = protect_matches(text, {"\n[ \t]*\n"}) + return text +end + +-- Returns true if the string s is a hash resulting from protection +local function is_protected(s) + return PD.blocks[s] +end + +-- Unprotects the specified text by expanding all the nonces +local function unprotect(text) + for k,v in pairs(PD.blocks) do + v = v:gsub("%%", "%%%%") + text = text:gsub(k, v) + end + return text +end + + +local code_block_ranges = {} + +---------------------------------------------------------------------- +-- Block transform +---------------------------------------------------------------------- + +-- The block transform functions transform the text on the block level. +-- They work with the text as an array of lines rather than as individual +-- characters. + +-- Returns true if the line is a ruler of (char) characters. +-- The line must contain at least three char characters and contain only spaces and +-- char characters. +local function is_ruler_of(line, char) + if not line:match("^[ %" .. char .. "]*$") then return false end + if not line:match("%" .. char .. ".*%" .. char .. ".*%" .. char) then return false end + return true +end + +-- Identifies the block level formatting present in the line +local function classify(line) + local info = {line = line, text = line} + + if line:match("^ ") then + info.type = "indented" + info.outdented = line:sub(5) + return info + end + + for _,c in ipairs({'*', '-', '_', '='}) do + if is_ruler_of(line, c) then + info.type = "ruler" + info.ruler_char = c + return info + end + end + + if line == "" then + info.type = "blank" + return info + end + + if line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") then + local m1, m2 = line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") + info.type = "header" + info.level = m1:len() + info.text = m2 + return info + end + + if line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") then + local number, text = line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") + info.type = "list_item" + info.list_type = "numeric" + info.number = 0 + number + info.text = text + return info + end + + if line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") then + local bullet, text = line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") + info.type = "list_item" + info.list_type = "bullet" + info.bullet = bullet + info.text= text + return info + end + + if line:match("^>[ \t]?(.*)") then + info.type = "blockquote" + info.text = line:match("^>[ \t]?(.*)") + return info + end + + if is_protected(line) then + info.type = "raw" + info.html = unprotect(line) + return info + end + + info.type = "normal" + return info +end + +-- Find headers constisting of a normal line followed by a ruler and converts them to +-- header entries. +local function headers(array) + local i = 1 + while i <= #array - 1 do + if array[i].type == "normal" and array[i+1].type == "ruler" and + (array[i+1].ruler_char == "-" or array[i+1].ruler_char == "=") then + local info = {line = array[i].line} + info.text = info.line + info.type = "header" + info.level = iff(array[i+1].ruler_char == "=", 1, 2) + table.remove(array, i+1) + array[i] = info + end + i = i + 1 + end + return array +end + +-- Forward declarations +local block_transform, span_transform, encode_code + +-- Convert lines to html code +local function blocks_to_html(lines, no_paragraphs) + local out = {} + local i = 1 + while i <= #lines do + local line = lines[i] + if line.type == "ruler" then + table.insert(out, "
") + elseif line.type == "raw" then + table.insert(out, line.html) + elseif line.type == "normal" then + local s = line.line + + while i+1 <= #lines and lines[i+1].type == "normal" do + i = i + 1 + s = s .. "\n" .. lines[i].line + end + + if no_paragraphs then + table.insert(out, span_transform(s)) + else + table.insert(out, "

" .. span_transform(s) .. "

") + end + elseif line.type == "header" then + local s = "" .. span_transform(line.text) .. "" + table.insert(out, s) + else + table.insert(out, line.line) + end + i = i + 1 + end + return out +end + +-- Find list blocks and convert them to protected data blocks +local function lists(array, sublist) + local function process_list(arr) + local function any_blanks(arr) + for i = 1, #arr do + if arr[i].type == "blank" then return true end + end + return false + end + + local function split_list_items(arr) + local acc = {arr[1]} + local res = {} + for i=2,#arr do + if arr[i].type == "list_item" then + table.insert(res, acc) + acc = {arr[i]} + else + table.insert(acc, arr[i]) + end + end + table.insert(res, acc) + return res + end + + local function process_list_item(lines, block) + while lines[#lines].type == "blank" do + table.remove(lines) + end + + local itemtext = lines[1].text + for i=2,#lines do + itemtext = itemtext .. "\n" .. outdent(lines[i].line) + end + if block then + itemtext = block_transform(itemtext, true) + if not itemtext:find("
") then itemtext = indent(itemtext) end
+            return "    
  • " .. itemtext .. "
  • " + else + local lines = split(itemtext) + lines = map(lines, classify) + lines = lists(lines, true) + lines = blocks_to_html(lines, true) + itemtext = table.concat(lines, "\n") + if not itemtext:find("
    ") then itemtext = indent(itemtext) end
    +            return "    
  • " .. itemtext .. "
  • " + end + end + + local block_list = any_blanks(arr) + local items = split_list_items(arr) + local out = "" + for _, item in ipairs(items) do + out = out .. process_list_item(item, block_list) .. "\n" + end + if arr[1].list_type == "numeric" then + return "
      \n" .. out .. "
    " + else + return "
      \n" .. out .. "
    " + end + end + + -- Finds the range of lines composing the first list in the array. A list + -- starts with (^ list_item) or (blank list_item) and ends with + -- (blank* $) or (blank normal). + -- + -- A sublist can start with just (list_item) does not need a blank... + local function find_list(array, sublist) + local function find_list_start(array, sublist) + if array[1].type == "list_item" then return 1 end + if sublist then + for i = 1,#array do + if array[i].type == "list_item" then return i end + end + else + for i = 1, #array-1 do + if array[i].type == "blank" and array[i+1].type == "list_item" then + return i+1 + end + end + end + return nil + end + local function find_list_end(array, start) + local pos = #array + for i = start, #array-1 do + if array[i].type == "blank" and array[i+1].type ~= "list_item" + and array[i+1].type ~= "indented" and array[i+1].type ~= "blank" then + pos = i-1 + break + end + end + while pos > start and array[pos].type == "blank" do + pos = pos - 1 + end + return pos + end + + local start = find_list_start(array, sublist) + if not start then return nil end + return start, find_list_end(array, start) + end + + while true do + local start, stop = find_list(array, sublist) + if not start then break end + local text = process_list(splice(array, start, stop)) + local info = { + line = text, + type = "raw", + html = text + } + array = splice(array, start, stop, {info}) + end + + -- Convert any remaining list items to normal + for _,line in ipairs(array) do + if line.type == "list_item" then line.type = "normal" end + end + + return array +end + +-- Find and convert blockquote markers. +local function blockquotes(lines) + local function find_blockquote(lines) + local start + for i,line in ipairs(lines) do + if line.type == "blockquote" then + start = i + break + end + end + if not start then return nil end + + local stop = #lines + for i = start+1, #lines do + if lines[i].type == "blank" or lines[i].type == "blockquote" then + elseif lines[i].type == "normal" then + if lines[i-1].type == "blank" then stop = i-1 break end + else + stop = i-1 break + end + end + while lines[stop].type == "blank" do stop = stop - 1 end + return start, stop + end + + local function process_blockquote(lines) + local raw = lines[1].text + for i = 2,#lines do + raw = raw .. "\n" .. lines[i].text + end + local bt = block_transform(raw) + if not bt:find("
    ") then bt = indent(bt) end
    +      return "
    \n " .. bt .. + "\n
    " + end + + while true do + local start, stop = find_blockquote(lines) + if not start then break end + local text = process_blockquote(splice(lines, start, stop)) + local info = { + line = text, + type = "raw", + html = text + } + lines = splice(lines, start, stop, {info}) + end + return lines +end + +-- Find and convert codeblocks. +local function codeblocks(lines) + local function find_codeblock(lines) + local start + for i,line in ipairs(lines) do + if line.type == "indented" then start = i break end + end + if not start then return nil end + + local stop = #lines + for i = start+1, #lines do + if lines[i].type ~= "indented" and lines[i].type ~= "blank" then + stop = i-1 + break + end + end + while lines[stop].type == "blank" do stop = stop - 1 end + return start, stop + end + + local function process_codeblock(lines) + local raw = detab(encode_code(outdent(lines[1].line))) + for i = 2,#lines do + raw = raw .. "\n" .. detab(encode_code(outdent(lines[i].line))) + end + return "
    " .. raw .. "\n
    " + end + + while true do + local start, stop = find_codeblock(lines) + if not start then break end + local text = process_codeblock(splice(lines, start, stop)) + local info = { + line = text, + type = "raw", + html = text + } + lines = splice(lines, start, stop, {info}) + end + return lines +end + +-- Perform all the block level transforms +function block_transform(text, sublist) + local lines = split(text) + lines = map(lines, classify) + lines = headers(lines) + lines = lists(lines, sublist) + lines = codeblocks(lines) + lines = blockquotes(lines) + lines = blocks_to_html(lines) + local text = table.concat(lines, "\n") + return text +end + +---------------------------------------------------------------------- +-- Span transform +---------------------------------------------------------------------- + +-- Functions for transforming the text at the span level. + +-- These characters may need to be escaped because they have a special +-- meaning in markdown. +local escape_chars = "'\\`*_{}[]()>#+-.!'" +local escape_table = {} + +local function init_escape_table() + escape_table = {} + for i = 1,#escape_chars do + local c = escape_chars:sub(i,i) + escape_table[c] = hash(c) + end +end + +-- Adds a new escape to the escape table. +local function add_escape(text) + if not escape_table[text] then + escape_table[text] = hash(text) + end + return escape_table[text] +end + +-- Encode backspace-escaped characters in the markdown source. +local function encode_backslash_escapes(t) + for i=1,escape_chars:len() do + local c = escape_chars:sub(i,i) + t = t:gsub("\\%" .. c, escape_table[c]) + end + return t +end + +-- Escape characters that should not be disturbed by markdown. +local function escape_special_chars(text) + local tokens = tokenize_html(text) + + local out = "" + for _, token in ipairs(tokens) do + local t = token.text + if token.type == "tag" then + -- In tags, encode * and _ so they don't conflict with their use in markdown. + t = t:gsub("%*", escape_table["*"]) + t = t:gsub("%_", escape_table["_"]) + else + t = encode_backslash_escapes(t) + end + out = out .. t + end + return out +end + +-- Unescape characters that have been encoded. +local function unescape_special_chars(t) + local tin = t + for k,v in pairs(escape_table) do + k = k:gsub("%%", "%%%%") + t = t:gsub(v,k) + end + if t ~= tin then t = unescape_special_chars(t) end + return t +end + +-- Encode/escape certain characters inside Markdown code runs. +-- The point is that in code, these characters are literals, +-- and lose their special Markdown meanings. +function encode_code(s) + s = s:gsub("%&", "&") + s = s:gsub("<", "<") + s = s:gsub(">", ">") + for k,v in pairs(escape_table) do + s = s:gsub("%"..k, v) + end + return s +end + +-- Handle backtick blocks. +local function code_spans(s) + s = s:gsub("\\\\", escape_table["\\"]) + s = s:gsub("\\`", escape_table["`"]) + + local pos = 1 + while true do + local start, stop = s:find("`+", pos) + if not start then return s end + local count = stop - start + 1 + -- Find a matching numbert of backticks + local estart, estop = s:find(string.rep("`", count), stop+1) + local brstart = s:find("\n", stop+1) + if estart and (not brstart or estart < brstart) then + local code = s:sub(stop+1, estart-1) + code = code:gsub("^[ \t]+", "") + code = code:gsub("[ \t]+$", "") + code = code:gsub(escape_table["\\"], escape_table["\\"] .. escape_table["\\"]) + code = code:gsub(escape_table["`"], escape_table["\\"] .. escape_table["`"]) + code = "" .. encode_code(code) .. "" + code = add_escape(code) + s = s:sub(1, start-1) .. code .. s:sub(estop+1) + pos = start + code:len() + else + pos = stop + 1 + end + end + return s +end + +-- Encode alt text... enodes &, and ". +local function encode_alt(s) + if not s then return s end + s = s:gsub('&', '&') + s = s:gsub('"', '"') + s = s:gsub('<', '<') + return s +end + +-- Forward declaration for link_db as returned by strip_link_definitions. +local link_database + +-- Handle image references +local function images(text) + local function reference_link(alt, id) + alt = encode_alt(alt:match("%b[]"):sub(2,-2)) + id = id:match("%[(.*)%]"):lower() + if id == "" then id = text:lower() end + link_database[id] = link_database[id] or {} + if not link_database[id].url then return nil end + local url = link_database[id].url or id + url = encode_alt(url) + local title = encode_alt(link_database[id].title) + if title then title = " title=\"" .. title .. "\"" else title = "" end + return add_escape ('' .. alt .. '") + end + + local function inline_link(alt, link) + alt = encode_alt(alt:match("%b[]"):sub(2,-2)) + local url, title = link:match("%(?[ \t]*['\"](.+)['\"]") + url = url or link:match("%(?%)") + url = encode_alt(url) + title = encode_alt(title) + if title then + return add_escape('' .. alt .. '') + else + return add_escape('' .. alt .. '') + end + end + + text = text:gsub("!(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) + text = text:gsub("!(%b[])(%b())", inline_link) + return text +end + +-- Handle anchor references +local function anchors(text) + local function reference_link(text, id) + text = text:match("%b[]"):sub(2,-2) + id = id:match("%b[]"):sub(2,-2):lower() + if id == "" then id = text:lower() end + link_database[id] = link_database[id] or {} + if not link_database[id].url then return nil end + local url = link_database[id].url or id + url = encode_alt(url) + local title = encode_alt(link_database[id].title) + if title then title = " title=\"" .. title .. "\"" else title = "" end + return add_escape("") .. text .. add_escape("") + end + + local function inline_link(text, link) + text = text:match("%b[]"):sub(2,-2) + local url, title = link:match("%(?[ \t]*['\"](.+)['\"]") + title = encode_alt(title) + url = url or link:match("%(?%)") or "" + url = encode_alt(url) + if title then + return add_escape("") .. text .. "" + else + return add_escape("") .. text .. add_escape("") + end + end + + text = text:gsub("(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) + text = text:gsub("(%b[])(%b())", inline_link) + return text +end + +-- Handle auto links, i.e. . +local function auto_links(text) + local function link(s) + return add_escape("") .. s .. "" + end + -- Encode chars as a mix of dec and hex entitites to (perhaps) fool + -- spambots. + local function encode_email_address(s) + -- Use a deterministic encoding to make unit testing possible. + -- Code 45% hex, 45% dec, 10% plain. + local hex = {code = function(c) return "&#x" .. string.format("%x", c:byte()) .. ";" end, count = 1, rate = 0.45} + local dec = {code = function(c) return "&#" .. c:byte() .. ";" end, count = 0, rate = 0.45} + local plain = {code = function(c) return c end, count = 0, rate = 0.1} + local codes = {hex, dec, plain} + local function swap(t,k1,k2) local temp = t[k2] t[k2] = t[k1] t[k1] = temp end + + local out = "" + for i = 1,s:len() do + for _,code in ipairs(codes) do code.count = code.count + code.rate end + if codes[1].count < codes[2].count then swap(codes,1,2) end + if codes[2].count < codes[3].count then swap(codes,2,3) end + if codes[1].count < codes[2].count then swap(codes,1,2) end + + local code = codes[1] + local c = s:sub(i,i) + -- Force encoding of "@" to make email address more invisible. + if c == "@" and code == plain then code = codes[2] end + out = out .. code.code(c) + code.count = code.count - 1 + end + return out + end + local function mail(s) + s = unescape_special_chars(s) + local address = encode_email_address("mailto:" .. s) + local text = encode_email_address(s) + return add_escape("") .. text .. "" + end + -- links + text = text:gsub("<(https?:[^'\">%s]+)>", link) + text = text:gsub("<(ftp:[^'\">%s]+)>", link) + + -- mail + text = text:gsub("%s]+)>", mail) + text = text:gsub("<([-.%w]+%@[-.%w]+)>", mail) + return text +end + +-- Encode free standing amps (&) and angles (<)... note that this does not +-- encode free >. +local function amps_and_angles(s) + -- encode amps not part of &..; expression + local pos = 1 + while true do + local amp = s:find("&", pos) + if not amp then break end + local semi = s:find(";", amp+1) + local stop = s:find("[ \t\n&]", amp+1) + if not semi or (stop and stop < semi) or (semi - amp) > 15 then + s = s:sub(1,amp-1) .. "&" .. s:sub(amp+1) + pos = amp+1 + else + pos = amp+1 + end + end + + -- encode naked <'s + s = s:gsub("<([^a-zA-Z/?$!])", "<%1") + s = s:gsub("<$", "<") + + -- what about >, nothing done in the original markdown source to handle them + return s +end + +-- Handles emphasis markers (* and _) in the text. +local function emphasis(text) + for _, s in ipairs {"%*%*", "%_%_"} do + text = text:gsub(s .. "([^%s][%*%_]?)" .. s, "%1") + text = text:gsub(s .. "([^%s][^<>]-[^%s][%*%_]?)" .. s, "%1") + end + for _, s in ipairs {"%*", "%_"} do + text = text:gsub(s .. "([^%s_])" .. s, "%1") + text = text:gsub(s .. "([^%s_])" .. s, "%1") + text = text:gsub(s .. "([^%s_][^<>_]-[^%s_])" .. s, "%1") + text = text:gsub(s .. "([^<>_]-[^<>_]-[^<>_]-)" .. s, "%1") + end + return text +end + +-- Handles line break markers in the text. +local function line_breaks(text) + return text:gsub(" +\n", "
    \n") +end + +-- Perform all span level transforms. +function span_transform(text) + text = code_spans(text) + text = escape_special_chars(text) + text = images(text) + text = anchors(text) + text = auto_links(text) + text = amps_and_angles(text) + text = emphasis(text) + text = line_breaks(text) + return text +end + +---------------------------------------------------------------------- +-- Markdown +---------------------------------------------------------------------- + +-- Cleanup the text by normalizing some possible variations to make further +-- processing easier. +local function cleanup(text) + -- Standardize line endings + text = text:gsub("\r\n", "\n") -- DOS to UNIX + text = text:gsub("\r", "\n") -- Mac to UNIX + + -- Convert all tabs to spaces + text = detab(text) + + -- Strip lines with only spaces and tabs + while true do + local subs + text, subs = text:gsub("\n[ \t]+\n", "\n\n") + if subs == 0 then break end + end + + return "\n" .. text .. "\n" +end + +-- Strips link definitions from the text and stores the data in a lookup table. +local function strip_link_definitions(text) + local linkdb = {} + + local function link_def(id, url, title) + id = id:match("%[(.+)%]"):lower() + linkdb[id] = linkdb[id] or {} + linkdb[id].url = url or linkdb[id].url + linkdb[id].title = title or linkdb[id].title + return "" + end + + local def_no_title = "\n ? ? ?(%b[]):[ \t]*\n?[ \t]*]+)>?[ \t]*" + local def_title1 = def_no_title .. "[ \t]+\n?[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" + local def_title2 = def_no_title .. "[ \t]*\n[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" + local def_title3 = def_no_title .. "[ \t]*\n?[ \t]+[\"'(]([^\n]+)[\"')][ \t]*" + + text = text:gsub(def_title1, link_def) + text = text:gsub(def_title2, link_def) + text = text:gsub(def_title3, link_def) + text = text:gsub(def_no_title, link_def) + return text, linkdb +end + +local lang_to_ext = { + rust = "rs", + lua = "lua", + python = "py", + javascript = "js", + luax = "luax", + cpp = "cpp", + c = "c", + java = "java", + go = "go", + html = "html", + css = "css", + bash = "bash", + sh = "sh", + json = "json", + toml = "toml", + c_sharp = "cs", +} + +local function highlight(lang, code) + -- create tmp file with code + local tmp_filename = os.tmpname() + local ext = lang_to_ext[lang] + if not ext then + ext = lang + end + -- add matching extension to tmp_filename (just lang for now) + tmp_filename = tmp_filename .. "." .. ext + local tmp_file = io.open(tmp_filename, "w") + if not tmp_file then + error("Could not open tmp file: " .. tmp_filename) + end + tmp_file:write(code) + tmp_file:close() + -- run tree-sitter highlight on the code with lang + local tree_sitter_output = io.popen("/usr/bin/env tree-sitter highlight -H " .. tmp_filename) + if not tree_sitter_output then + error("Could not run tree-sitter highlight") + end + + return tree_sitter_output:read("*a") +end + +local function code_block_highlight(text) + -- find ``` blocks + local pos = 1 + while true do + local start, stop = text:find("```", pos) + if not start then break end + local endstart, endstop = text:find("```", stop+1) + if not endstart then break end + + local block = text:sub(stop+2, endstart-1) + -- treat first line after ``` as language + local lang = block:match("^(.-)\n") + -- remove first line from block + block = block:gsub("^[^\n]*\n", "") + -- replace block with highlighted block + block = '
    ' .. highlight(lang, block) .. '
    ' + text = text:sub(1, start-1) .. block .. text:sub(endstop+1) + code_block_ranges[#code_block_ranges+1] = {start, start+block:len()} + pos = start + block:len() + end + return text +end + +-- Main markdown processing function +local function markdown(text) + init_hash(text) + init_escape_table() + + text = cleanup(text) + text = protect(text) + text = code_block_highlight(text) + text, link_database = strip_link_definitions(text) + text = block_transform(text) + text = unescape_special_chars(text) + return text +end + +---------------------------------------------------------------------- +-- End of module +---------------------------------------------------------------------- + +-- For compatibility, set markdown function as a global +_G.markdown = markdown + +-- Class for parsing command-line options +local OptionParser = {} +OptionParser.__index = OptionParser + +-- Creates a new option parser +function OptionParser:new() + local o = {short = {}, long = {}} + setmetatable(o, self) + return o +end + +-- Calls f() whenever a flag with specified short and long name is encountered +function OptionParser:flag(short, long, f) + local info = {type = "flag", f = f} + if short then self.short[short] = info end + if long then self.long[long] = info end +end + +-- Calls f(param) whenever a parameter flag with specified short and long name is encountered +function OptionParser:param(short, long, f) + local info = {type = "param", f = f} + if short then self.short[short] = info end + if long then self.long[long] = info end +end + +-- Calls f(v) for each non-flag argument +function OptionParser:arg(f) + self.arg = f +end + +-- Runs the option parser for the specified set of arguments. Returns true if all arguments +-- where successfully parsed and false otherwise. +function OptionParser:run(args) + local pos = 1 + while pos <= #args do + local arg = args[pos] + if arg == "--" then + for i=pos+1,#args do + if self.arg then self.arg(args[i]) end + return true + end + end + if arg:match("^%-%-") then + local info = self.long[arg:sub(3)] + if not info then print("Unknown flag: " .. arg) return false end + if info.type == "flag" then + info.f() + pos = pos + 1 + else + local param = args[pos+1] + if not param then print("No parameter for flag: " .. arg) return false end + info.f(param) + pos = pos+2 + end + elseif arg:match("^%-") then + for i=2,arg:len() do + local c = arg:sub(i,i) + local info = self.short[c] + if not info then print("Unknown flag: -" .. c) return false end + if info.type == "flag" then + info.f() + else + if i == arg:len() then + local param = args[pos+1] + if not param then print("No parameter for flag: -" .. c) return false end + info.f(param) + pos = pos + 1 + else + local param = arg:sub(i+1) + info.f(param) + end + break + end + end + pos = pos + 1 + else + if self.arg then self.arg(arg) end + pos = pos + 1 + end + end + return true +end + +local function read_file(path, descr) + local file = io.open(path) or error("Could not open " .. descr .. " file: " .. path) + local contents = file:read("*a") or error("Could not read " .. descr .. " from " .. path) + file:close() + return contents +end + +-- Handles the case when markdown is run from the command line +local function run_command_line(arg) + -- Generate output for input s given options + local function run(s, options) + s = markdown(s) + if not options.wrap_header then return s end + local header + if options.header then + header = read_file(options.header, "header") + else + header = [[ + + + + + TITLE + + + +]] + local title = options.title or s:match("

    (.-)

    ") or s:match("

    (.-)

    ") or + s:match("

    (.-)

    ") or "Untitled" + header = header:gsub("TITLE", title) + if options.inline_style then + local style = read_file(options.stylesheet, "style sheet") + header = header:gsub('', + "") + else + header = header:gsub("STYLESHEET", options.stylesheet) + end + header = header:gsub("CHARSET", options.charset) + end + local footer = "" + if options.footer then + footer = read_file(options.footer, "footer") + end + return header .. s .. footer + end + + -- Generate output path name from input path name given options. + local function outpath(path, options) + if options.append then return path .. ".html" end + local m = path:match("^(.+%.html)[^/\\]+$") if m then return m end + m = path:match("^(.+%.)[^/\\]*$") if m and path ~= m .. "html" then return m .. "html" end + return path .. ".html" + end + + -- Default commandline options + local options = { + wrap_header = true, + header = nil, + footer = nil, + charset = "utf-8", + title = nil, + stylesheet = "default.css", + inline_style = false + } + local help = [[ +Usage: markdown.lua [OPTION] [FILE] +Runs the markdown text markup to HTML converter on each file specified on the +command line. If no files are specified, runs on standard input. + +No header: + -n, --no-wrap Don't wrap the output in ... tags. +Custom header: + -e, --header FILE Use content of FILE for header. + -f, --footer FILE Use content of FILE for footer. +Generated header: + -c, --charset SET Specifies charset (default utf-8). + -i, --title TITLE Specifies title (default from first

    tag). + -s, --style STYLE Specifies style sheet file (default default.css). + -l, --inline-style Include the style sheet file inline in the header. +Generated files: + -a, --append Append .html extension (instead of replacing). +Other options: + -h, --help Print this help text. + -t, --test Run the unit tests. +]] + + local run_stdin = true + local op = OptionParser:new() + op:flag("n", "no-wrap", function () options.wrap_header = false end) + op:param("e", "header", function (x) options.header = x end) + op:param("f", "footer", function (x) options.footer = x end) + op:param("c", "charset", function (x) options.charset = x end) + op:param("i", "title", function(x) options.title = x end) + op:param("s", "style", function(x) options.stylesheet = x end) + op:flag("l", "inline-style", function() options.inline_style = true end) + op:flag("a", "append", function() options.append = true end) + op:flag("t", "test", function() + local n = arg[0]:gsub("markdown%.lua", "markdown-tests.lua") + local f = io.open(n) + if f then + f:close() + package.loaded.markdown = markdown + dofile(n) + else + error("Cannot find markdown-tests.lua") + end + run_stdin = false + end) + op:flag("h", "help", function() print(help) run_stdin = false end) + op:arg(function(path) + local s = read_file(path, "input") + s = run(s, options) + local file = io.open(outpath(path, options), "w") or error("Could not open output file: " .. outpath(path, options)) + file:write(s) + file:close() + run_stdin = false + end + ) + + if not op:run(arg) then + print(help) + run_stdin = false + end + + if run_stdin then + local s = io.read("*a") + s = run(s, options) + io.write(s) + end +end + +-- If we are being run from the command-line, act accordingly +if arg and arg[0]:find("markdown%.lua$") then + run_command_line(arg) +else + return markdown +end diff --git a/luax/blog/building-reluax.md b/luax/blog/building-reluax.md deleted file mode 100644 index 08f8b5d..0000000 --- a/luax/blog/building-reluax.md +++ /dev/null @@ -1,211 +0,0 @@ -## The inspiration -Around a week ago, I was working on some projects, as usual, and had YouTube playing on the -second monitor. The video roulette landed on -[ThePrimagen](https://www.youtube.com/watch?v=r4HyasuvjKM). This time he was reading a post that -really intrigued me, because it shared many of the sentiments that were boiling in my head. This -website you're reading was originally written in raw HTML, served on GitHub pages. This was -obviously not ideal for maintenance. Adding anything involved copypasting large chunks of -code, changing anything about the layout required changes across multiple files. Not a very -satisfying process, and it made me avoid doing anything with it. - -Then, I had to learn React. It was pretty neat. I could define *actual, reusable components*. -I could actually write logic to manipulate data! Have it structured! Write functions! And it -only needed small changes to the actual HTML of my site, I just had to move it around a bit -so it sat right in the middle of some TypeScript code. - -I thought this was a really neat idea. So I rewrote the old website in it, and even added some -*visual flair*, like the shader at the top of this page (I had to show off my love of graphics -programming *somehow*). And to manage the project, I used [Vite](https://vitejs.dev/), which let -me easily pack up the project into static files, and still serve them from GitHub pages (with a -neat tool to automate it, [react-gh-pages](https://github.com/gitname/react-gh-pages)). I was -pretty proud of how it looked, and it was a good experience to write. - -But after a while, I wanted to build a blog. Everyone has one, right? Why not me? I can do -interesting things too. But as of then, the project and games sections of the website were just -big JSON files, included into the TS of the React project. I couldn't think of a simpler way to -have the data and still keep all of the site static. I did not really want to pay for hosting if -I didn't have to. But a project description needed formatting sometimes, so it used a rather ugly -way of just storing the HTML as a string in the JSON, like so: - -``` -json -[ - { - "name": "Mr Final Boss", - "name_normalized": "mr-final-boss", - "type": "game", - "link": "https://jellingfish-games.itch.io/mr-final-boss", - "tech": [ - "unity", - "csharp" - ], - "targets": [ - "windows", - "html5" - ], - "images": [ - "thumbnail.png", - "screenshot1.png", - "screenshot2.png", - "screenshot3.png", - "screenshot4.png" - ], - "description": "The legends say that one day, brave Heroes will slay the evil and malicious Ignatius, the Chaos Bringer... But YOU won't go down without a fight! Be the bossfight everyone is dreading and hunt down as many Heroes as you can in your last stand using a variety of abilities you can choose from!
    Game made in 72 hours for Ludum Dare 50, with the theme \"Delay the Inevitable\"." - } -] -``` - -Since it's JSON, the string couldn't have line breaks, which is really just something I could -live with when the text was a short summary. This would just not work for blog posts. To actually -write blog posts, I wanted to use the beloved, old classic: Markdown. So I would basically have -two options: either have *another* preprocessing step when building the website, which would -require writing some more scripts and making the process of deployment more complicated. It would -also mean I would store all the content of all the blog posts in the code that gets served -to anyone who visits any page on the site, which just felt stupid, even if text is a fraction of -the size of a single image. - -The alternative was serving markdown files in full, and processing them into HTML client-side. -Now, this is my personal website, a site serving probably an average of the 2 times a month I -visit it myself to show people. But it still felt like a really dumb way to do things. I'm sure -many sites do more intensive processing client-side, but I just couldn't think for a good reason -"*why?*" - -To solve these issues, I would have to unavoidably write code. There were many options for what -paths I could take, such as rewriting my site in a Static Site Generator like -[Hugo](https://gohugo.io/), or writing a server in whatever of the countless options exist. -I didn't want to go the templating route. It felt really limiting after encountering JSX, and I -would have to rewrite my site inside-out, with a different approach. I could split my codebase in -half, keeping the React site as the front but making it actually do its job of requesting data -from the backend. I felt like doing it that way just for serving blog posts would grow the effort -required to change anything about the site yet again, which was something I really would rather -not do. The entire point is to be able to enjoy making this stuff. - -### The LuaX revelation -Coming back to the beginning of the article, I watched a video of ThePrimagen reading a -fascinating blog post, -[I made JSX for Lua (because I hate static sites)](https://bvisness.me/luax) by Ben Visness. -He seemed to share my feeling about the approach to managing HTML, and how JSX was really -convenient. And his solution was simple, powerful, and perhaps most importantly, cool. Writing a -preprocessor to create a custom dialect of Lua. And he finished the blog post with a note of -encouragement: -> I don’t know what the future holds for LuaX. It’s not ready for other people to use, and I’m -> not sure it will ever be. So rather than tell you all to go to luax dot dev and start building -> your websites with my tool, here’s what I’ll say instead: -> -> Try building something for yourself. Try writing code for you, and you alone. Don’t worry about -> whether it will look good on your resumé or attract lots of stars on GitHub. Just write -> something that feels good to you. Explore a weird idea and see where it takes you. -> -> Who knows—maybe someday other people will like it too. - -So I set out to recreate it. After all, I wanted my site to run on LuaX as well, and I wouldn't -be deterred by inconvenient facts such as the fact it doesn't really exist yet. - -### Enter Reluax -I decided to name my version `reluax`, since it is a remake, and it was relaxing to write on the -side, procrastinating other responsibilities. I wrote the bulk of it in Rust, since I had a -experience writing a language in it from writing [brakion](https://github.com/Duckonaut/brakion), -and I could copy the lexer almost in full. Rust would also provide a good ecosystem for the -HTTP server portion, with battle-tested libraries like [hyper](https://hyper.rs/). I didn't go -for any of the higher-level libraries like [axum](https://github.com/tokio-rs/axum), since I -felt like I wouldn't really benefit from them. What I was building was not to be a layer on top -of one, but a replacement of it. - -The parser portion took a bit, but it was not anything too complex. Writing a lexer that -simultaneously structures the data, but can emit whatever garbage comes inside of an HTML tag, was -a bit tricky. But in two evenings I had a demo: - -![the first demo](https://cdn.duckonaut.dev/assets/blog/building-reluax/first-demo.png) - -It was a bit rough, but proved it was all working. The next day I had something akin to React's -functional components: - -![the second demo](https://cdn.duckonaut.dev/assets/blog/building-reluax/second-demo.png) - -And on the third day, this site you're reading right now was running fully on Reluax. I've also -taken the extra time to add some nicer output to the program, just for fun. - -![cli with emojis](https://cdn.duckonaut.dev/assets/blog/building-reluax/cli.png) - -And of course, with making a custom dialect of Lua, I would throw away all the goodies that give -modern developers a good experience writing code, like syntax highlighting or a language server. -Not content with that, I threw together a tree-sitter parser to at least have syntax highlighting -back in my beloved Neovim. I've written a couple in the past, so adapting the -[tree-sitter-lua](https://github.com/tjdevries/tree-sitter-lua) grammar only took a cople hours. -The syntax highlighting of this blog is powered by tree-sitter, so you can enjoy a pretty -view of how the entry point of this website looks! - -``` -luax -local Common = require("common") -local Navbar = require("components.navbar") -local AnimatedBg = require("components.animated_bg") - -local Home = require("pages.home") -local Projects = require("pages.projects") -local Games = require("pages.games") -local Contact = require("pages.contact") -local Blogpost = require("pages.blogpost") -local Blog = require("pages.blog") -local NotFound = require("not_found") - -local blogposts = require("blog.posts") - -math.randomseed(os.time()) - -local function wrap_component(component) - return - - -
    -
    - {$ component $} - -
    -
    -end - -local function route(path) - if path == '/' then - return 200, wrap_component() - elseif path == '/projects' then - return 200, wrap_component() - elseif path == '/games' then - return 200, wrap_component() - elseif path == '/contact' then - return 200, wrap_component() - elseif path == '/blog' then - return 200, wrap_component() - elseif reluax.url_matches('/blog/{slug}', path) then - local slug = reluax.url_extract('/blog/{slug}', path).slug - -- blogposts is a list of blogposts, with the slug a property of each - local post = nil - for _, p in ipairs(blogposts) do - if p.slug == slug then - post = p - break - end - end - if post then - local article = wrap_component() - return 200, article - end - end - - return 404, NotFound() -end - -return { - name = "duckonaut.dev", - route = route -} -``` - -After that, I've taken some time to wrap Reluax in a Nix flake so I could easily run it on my -Oracle Cloud server, add some descriptions to the CLI help, things like that. And now I'm writing -this blog post, satisfied with how quickly it all came together. - -Building things is fun! I highly recommend it. This project gave me a really fun distraction, and -now I have my site running on something I made, something I have full control over. That's a -really good feeling. And I won't end the article the same way as Ben: you can check out the early -version of Reluax over at [GitHub](https://github.com/Duckonaut/reluax) and try it for yourself. diff --git a/luax/blog/cache.lua b/luax/blog/cache.lua deleted file mode 100644 index e8f5b0c..0000000 --- a/luax/blog/cache.lua +++ /dev/null @@ -1,23 +0,0 @@ -local md = require "libs.markdown" - -local blog_cache = {} - -local function get_blog_post_content(post) - local slug = post.slug - if blog_cache[slug] then - return blog_cache[slug] - end - -- pull markdown content from file - local content = io.open("blog/" .. slug .. ".md", "r"):read("*a") - - if content then - -- convert markdown to html - content = md(content) - end - - blog_cache[slug] = content - - return content -end - -return get_blog_post_content diff --git a/luax/blog/posts.lua b/luax/blog/posts.lua deleted file mode 100644 index f1d8408..0000000 --- a/luax/blog/posts.lua +++ /dev/null @@ -1,8 +0,0 @@ -return { - { - title = "I (re)made JSX for Lua", - slug = 'building-reluax', - date = os.time({year=2024, month=1, day=13}), - description = "Because it was too good to pass up.", - }, -} diff --git a/luax/common.luax b/luax/common.luax deleted file mode 100644 index ab29a02..0000000 --- a/luax/common.luax +++ /dev/null @@ -1,78 +0,0 @@ -return function(props) - local use_cdn = reluax.dev_mode == false - local asset_prefix = use_cdn and "https://cdn.duckonaut.dev" or "" - - local thumbnail = props.attrs.thumbnail or - asset_prefix .. "/assets/images/thumbnail.png" - local title = props.attrs.title or "duckonaut.dev" - local description = props.attrs.description or "duckonaut's stuff" - local stylesheelts = props.attrs.stylesheets or - { 'app.css', - 'index.css', - 'contact.css', - 'project.css', - 'navbar.css', - 'carousel.css', - 'blog.css' - } - local scripts = props.attrs.scripts or - { 'index.js' } - - local style_tags = {} - for _, stylesheet in ipairs(stylesheelts) do - style_tags[#style_tags + 1] = - - end - - local script_tags = {} - for _, script in ipairs(scripts) do - script_tags[#script_tags + 1] = - - end - - return - - - - - - - - - - - - - - - - {$ script_tags $} - - {$ style_tags $} - - - - - - - - - - - {$ title $} - - - -
    {$ props.children $}
    - - - -end diff --git a/luax/components/animated_bg.luax b/luax/components/animated_bg.luax deleted file mode 100644 index 0c5105c..0000000 --- a/luax/components/animated_bg.luax +++ /dev/null @@ -1,3 +0,0 @@ -return function() - return
    -end diff --git a/luax/components/carousel.luax b/luax/components/carousel.luax deleted file mode 100644 index f2c5fbe..0000000 --- a/luax/components/carousel.luax +++ /dev/null @@ -1,10 +0,0 @@ -return function(props) - if props == nil or props.children == nil or #props.children == 0 then - return nil - end - - return - -end diff --git a/luax/components/navbar-item.luax b/luax/components/navbar-item.luax deleted file mode 100644 index d201f82..0000000 --- a/luax/components/navbar-item.luax +++ /dev/null @@ -1,6 +0,0 @@ -return function(props) - return - - {$ props.children $} - -end diff --git a/luax/components/navbar.luax b/luax/components/navbar.luax deleted file mode 100644 index 4c0c54b..0000000 --- a/luax/components/navbar.luax +++ /dev/null @@ -1,18 +0,0 @@ -local NavbarItem = require('components.navbar-item') - -return function() - return - -end diff --git a/luax/components/project.luax b/luax/components/project.luax deleted file mode 100644 index cad844f..0000000 --- a/luax/components/project.luax +++ /dev/null @@ -1,62 +0,0 @@ -local Carousel = require('components.carousel') - -local asset_prefix = reluax.dev_mode and '/assets/' or 'https://cdn.duckonaut.dev/assets/' - -function getProjectImageLink(project, image) - return asset_prefix .. project.type .. 's/' .. project.name_normalized .. '/' .. image -end - -function getProjectTechIconLink(tech) - return asset_prefix .. 'images/' .. tech .. '.png' -end - -function getProjectTargetIconLink(target) - return asset_prefix .. 'images/' .. target .. '.png' -end - -return function (props) - local project = props.attrs.project - local techs = {} - for _, tech in ipairs(project.tech) do - techs[#techs + 1] = - end - local targets = {} - for _, target in ipairs(project.targets) do - targets[#targets + 1] = - end - - local images = nil - if project.images then - images = {} - for _, image in ipairs(project.images) do - images[#images + 1] = - end - - images = {$ images $} - end - - return ( -
    - -
    -
    -
    Made using
    -
    - {$ techs $} -
    -
    -
    -
    Available on
    -
    - {$ targets $} -
    -
    -
    -
    - {$ project.description $} -
    -
    - {$ images $} -
    - ) -end diff --git a/luax/data/games.lua b/luax/data/games.lua deleted file mode 100644 index 643f48f..0000000 --- a/luax/data/games.lua +++ /dev/null @@ -1,229 +0,0 @@ -return { - { - name = "Mr Final Boss", - name_normalized = "mr-final-boss", - type = "game", - link = "https://jellingfish-games.itch.io/mr-final-boss", - tech = { - "unity", - "csharp" - }, - targets = { - "windows", - "html5" - }, - images = { - "thumbnail.png", - "screenshot1.png", - "screenshot2.png", - "screenshot3.png", - "screenshot4.png" - }, - description = "The legends say that one day, brave Heroes will slay the evil and malicious Ignatius, the Chaos Bringer... But YOU won't go down without a fight! Be the bossfight everyone is dreading and hunt down as many Heroes as you can in your last stand using a variety of abilities you can choose from!
    Game made in 72 hours for Ludum Dare 50, with the theme \"Delay the Inevitable\"." - }, - { - name = "BUMBLING BUILDERS", - name_normalized = "bumbling-builders", - type = "game", - link = "https://oroshibu.itch.io/bumbling-builders", - tech = { - "unity", - "csharp" - }, - targets = { - "windows", - "html5" - }, - images = { - "thumbnail.png", - "screenshot1.png", - "screenshot2.png", - "screenshot3.png" - }, - description = "A physics puzzle game in which you need to protect little creatures from danger that could come from any direction. Build forts with tetris-style blocks. The game features 30+ levels, multiple distinct enemies and a star rating system.
    Game made in 72 hours for Ludum Dare 49, with the theme \"Unstable\"." - }, - { - name = "LOUD: My Road To Fame", - name_normalized = "loud", - type = "game", - link = "https://store.steampowered.com/app/1838290/LOUD_My_Road_to_Fame/", - tech = { - "unity", - "csharp" - }, - targets = { - "windows", - "xbox", - "playstation", - "switch" - }, - images = { - "thumbnail.jpg", - "screenshot1.jpg", - "screenshot2.jpg", - "screenshot3.jpg" - }, - description = "A wholesome, music-filled arcade rhythm game in which a young, reclusive rocker goes on a journey of self-discovery. Once she begins to follow her dreams and finds her inner voice, she discovers that it is LOUD!
    One of the games I've worked on at Hyperstrange" - }, - { - name = "WUZMAG", - name_normalized = "wuzmag", - type = "game", - link = "https://duckonaut.itch.io/wuzmag", - tech = { - "unity", - "csharp" - }, - targets = { - "windows", - "html5" - }, - images = { - "thumbnail.png", - "screenshot1.png", - "screenshot2.png", - "screenshot3.png" - }, - description = "Turn based arcade survival game. Survive as long as you can by freezing enemies and using them to defeat more enemies.
    Game made in roughly 8 hours for a private game jam with friends." - }, - { - name = "Miro", - name_normalized = "miro", - type = "game", - link = "https://duckonaut.itch.io/miro", - tech = { - "monogame", - "csharp" - }, - targets = { - "windows" - }, - images = { - "thumbnail.png", - "screenshot1.png", - "screenshot2.png", - "screenshot3.png" - }, - description = "Small chill game made mostly as an experiment for a nice 2D renderer mechanic. Infect the world around you, breaking it down into its simplest components.
    Game made in roughly 8 hours for a private game jam with friends." - }, - { - name = "Thermal Trail", - name_normalized = "thermal-trail", - type = "game", - link = "https://duckonaut.itch.io/thermal-trail", - tech = { - "godot", - "csharp" - }, - targets = { - "windows", - "html5" - }, - images = { - "gameart.png", - "screenshot1.png", - "screenshot2.png", - "screenshot3.png", - "screenshot4.png" - }, - description = "Play as Yolka, a mountain goat who found an unknown egg. Keep the egg warm as you navigate the 3 distinct worlds, and bring it to the hottest place you know: the depths of a volcano.
    Game made in a week for Newgrounds July Jam 2021, with the theme \"Egg\"." - }, - { - name = "Greachers", - name_normalized = "greachers", - type = "game", - link = "https://duckonaut.itch.io/greachers", - tech = { - "monogame", - "csharp" - }, - targets = { - "windows" - }, - images = { - "title.png", - "screenshot1.png", - "screenshot2.png", - "screenshot3.png" - }, - description = "Tiny idle simulation game where you manage a team of randomly generated little fellas as they fight other greachers in a sidescrolling world with beautiful procedurally generated graphics.
    Game made in 2 weeks for LOWREZJAM 2021, with the theme \"Minibeasts\"." - }, - { - name = "Third Party", - name_normalized = "third-party", - type = "game", - link = "https://duckonaut.itch.io/third-party", - tech = { - "godot" - }, - targets = { - "windows", - "html5" - }, - images = { - "thumbnail.png", - "screenshot1.jpg", - "screenshot2.jpg", - "screenshot3.jpg", - "screenshot4.jpg" - }, - description = "A top-down stealth game where you play as a spy tasked with stealing important data from a rivaling government. Your task would be easy enough if your objective wasn’t in a underground top-secret facility handling an alien outbreak. Without access to conventional stealth techniques, use corpses of your enemies to sneak through the battlefield.
    Game made in 72 hours for Ludum Dare 48, with the theme \"Deeper and deeper\"." - }, - { - name = "Your Left", - name_normalized = "your-left", - type = "game", - link = "https://xatriv.itch.io/your-left", - tech = { - "unity", - "csharp" - }, - targets = { - "windows" - }, - images = { - "thumbnail.png", - "screenshot1.jpg", - "screenshot2.jpg", - "screenshot3.jpg" - }, - description = "A simple pacman-inspired arcade game. Try to grab all the stars in a randomly generated maze before the time expires, while your controls are scattered all over the screen. Get as far as you can to get the highest score.
    Game made in 8 hours for Jam Jam, a local in-person event organized by KNTG Polygon, with the theme \"One Click\"." - }, - { - name = "Riff n' Rage", - name_normalized = "riff-n-rage", - type = "game", - link = "https://duckonaut.itch.io/riff-n-rage", - tech = { - "monogame", - "csharp" - }, - targets = { - "windows" - }, - images = { - "thumbnail.png", - "screenshot1.png", - "screenshot2.png" - }, - description = "You just got your first guitar, and through sheer luck managed to land in the king's throne room, trying to entertain him. Sadly, his subjects aren't huge fans of your music. Dodge them while trying to play the best you can, or the king won't be merciful.
    Game made in 48 hours for a special solo game jam among friends. My theme was \"Power at a price\"." - }, - { - name = "Survive the Temple", - name_normalized = "survive-the-temple", - type = "game", - link = "https://duckonaut.itch.io/survive-the-temple", - tech = { - "godot" - }, - targets = { - "windows", - "html5" - }, - images = { - "cover.png", - "screenshot.png" - }, - description = "A quick movement focused platformer that tests your short term memory. Survive the trap system of an ancient temple! Dash through the lanterns in the correct order before the time runs out, or face certain death!
    Game made in 48 hours for Ludum Dare Compo 47 with the theme \"Stuck in a loop\"." - } -} diff --git a/luax/data/projects.lua b/luax/data/projects.lua deleted file mode 100644 index 5248a2a..0000000 --- a/luax/data/projects.lua +++ /dev/null @@ -1,56 +0,0 @@ -return { - { - name = "hyprload", - name_normalized = "hyprload", - type = "project", - link = "https://github.com/duckonaut/hyprload", - tech = { - "cpp" - }, - targets = { - "linux", - "hyprland" - }, - images = { - "logo.png" - }, - description = - "hyprload acts as a plugin manager for the Hyprland Wayland Compositor. It's loaded as a plugin and allows easy, declarative installation and updates of other plugins." - }, - { - name = "split-monitor-workspaces", - name_normalized = "split-monitor-workspaces", - type = "project", - link = "https://github.com/duckonaut/split-monitor-workspaces", - tech = { - "cpp" - }, - targets = { - "linux", - "hyprland" - }, - description = - "A small plugin for the Hyprland Wayland Compositor that allows you to have different workspace namespaces on different monitors." - }, - { - name = "Spirit Mod", - name_normalized = "spirit-mod", - type = "project", - link = "https://spiritmod.fandom.com/wiki/Spirit_Mod_Wiki", - tech = { - "csharp" - }, - targets = { - "windows", - "linux", - "macos" - }, - images = { - "thumbnail.png", - "demo1.gif", - "demo2.gif" - }, - description = - "The Spirit Mod is one of the largest mods for Terraria: with more than 1300 items, 8 bosses, and hundreds of enemies, as well as 3 new events and 3 brand new biomes, the mod has a lot to offer.
    I had the privilege of contributing some system code, mainly the \"Stargoop\" system." - } -} diff --git a/luax/libs/markdown.lua b/luax/libs/markdown.lua deleted file mode 100644 index 3fe90c1..0000000 --- a/luax/libs/markdown.lua +++ /dev/null @@ -1,1281 +0,0 @@ -#!/usr/bin/env lua - ----------------------------------------------------------------------- --- Utility functions ----------------------------------------------------------------------- - -local unpack = table.unpack or unpack - --- Returns the result of mapping the values in table t through the function f -local function map(t, f) - local out = {} - for k,v in pairs(t) do out[k] = f(v,k) end - return out -end - --- Functional style if statement. (NOTE: no short circuit evaluation) -local function iff(t, a, b) if t then return a else return b end end - --- Splits the text into an array of separate lines. -local function split(text, sep) - sep = sep or "\n" - local lines = {} - local pos = 1 - while true do - local b,e = text:find(sep, pos) - if not b then table.insert(lines, text:sub(pos)) break end - table.insert(lines, text:sub(pos, b-1)) - pos = e + 1 - end - return lines -end - --- Converts tabs to spaces -local function detab(text) - local tab_width = 4 - local function rep(match) - local spaces = -match:len() - while spaces<1 do spaces = spaces + tab_width end - return match .. string.rep(" ", spaces) - end - text = text:gsub("([^\n]-)\t", rep) - return text -end - --- Applies string.find for every pattern in the list and returns the first match -local function find_first(s, patterns, index) - local res = {} - for _,p in ipairs(patterns) do - local match = {s:find(p, index)} - if #match>0 and (#res==0 or match[1] < res[1]) then res = match end - end - return unpack(res) -end - --- If a replacement array is specified, the range [start, stop] in the array is replaced --- with the replacement array and the resulting array is returned. Without a replacement --- array the section of the array between start and stop is returned. -local function splice(array, start, stop, replacement) - if replacement then - local n = stop - start + 1 - while n > 0 do - table.remove(array, start) - n = n - 1 - end - for _,v in ipairs(replacement) do - table.insert(array, start, v) - end - return array - else - local res = {} - for i = start,stop do - table.insert(res, array[i]) - end - return res - end -end - --- Outdents the text one step. -local function outdent(text) - text = "\n" .. text - text = text:gsub("\n ? ? ?", "\n") - text = text:sub(2) - return text -end - --- Indents the text one step. -local function indent(text) - text = text:gsub("\n", "\n ") - return text -end - --- Does a simple tokenization of html data. Returns the data as a list of tokens. --- Each token is a table with a type field (which is either "tag" or "text") and --- a text field (which contains the original token data). -local function tokenize_html(html) - local tokens = {} - local pos = 1 - while true do - local start = find_first(html, {"", start) - elseif html:match("^<%?", start) then - _,stop = html:find("?>", start) - else - _,stop = html:find("%b<>", start) - end - if not stop then - -- error("Could not match html tag " .. html:sub(start,start+30)) - table.insert(tokens, {type="text", text=html:sub(start, start)}) - pos = start + 1 - else - table.insert(tokens, {type="tag", text=html:sub(start, stop)}) - pos = stop + 1 - end - end - return tokens -end - ----------------------------------------------------------------------- --- Hash ----------------------------------------------------------------------- - --- This is used to "hash" data into alphanumeric strings that are unique --- in the document. (Note that this is not cryptographic hash, the hash --- function is not one-way.) The hash procedure is used to protect parts --- of the document from further processing. - -local HASH = { - -- Has the hash been inited. - inited = false, - - -- The unique string prepended to all hash values. This is to ensure - -- that hash values do not accidently coincide with an actual existing - -- string in the document. - identifier = "", - - -- Counter that counts up for each new hash instance. - counter = 0, - - -- Hash table. - table = {} -} - --- Inits hashing. Creates a hash_identifier that doesn't occur anywhere --- in the text. -local function init_hash(text) - HASH.inited = true - HASH.identifier = "" - HASH.counter = 0 - HASH.table = {} - - local s = "HASH" - local counter = 0 - local id - while true do - id = s .. counter - if not text:find(id, 1, true) then break end - counter = counter + 1 - end - HASH.identifier = id -end - --- Returns the hashed value for s. -local function hash(s) - assert(HASH.inited) - if not HASH.table[s] then - HASH.counter = HASH.counter + 1 - local id = HASH.identifier .. HASH.counter .. "X" - HASH.table[s] = id - end - return HASH.table[s] -end - ----------------------------------------------------------------------- --- Protection ----------------------------------------------------------------------- - --- The protection module is used to "protect" parts of a document --- so that they are not modified by subsequent processing steps. --- Protected parts are saved in a table for later unprotection - --- Protection data -local PD = { - -- Saved blocks that have been converted - blocks = {}, - - -- Block level tags that will be protected - tags = {"p", "div", "h1", "h2", "h3", "h4", "h5", "h6", "blockquote", - "pre", "table", "dl", "ol", "ul", "script", "noscript", "form", "fieldset", - "iframe", "math", "ins", "del"} -} - --- Pattern for matching a block tag that begins and ends in the leftmost --- column and may contain indented subtags, i.e. ---
    --- A nested block. ---
    --- Nested data. ---
    ---
    -local function block_pattern(tag) - return "\n<" .. tag .. ".-\n[ \t]*\n" -end - --- Pattern for matching a block tag that begins and ends with a newline -local function line_pattern(tag) - return "\n<" .. tag .. ".-[ \t]*\n" -end - --- Protects the range of characters from start to stop in the text and --- returns the protected string. -local function protect_range(text, start, stop) - local s = text:sub(start, stop) - local h = hash(s) - PD.blocks[h] = s - text = text:sub(1,start) .. h .. text:sub(stop) - return text -end - --- Protect every part of the text that matches any of the patterns. The first --- matching pattern is protected first, etc. -local function protect_matches(text, patterns) - while true do - local start, stop = find_first(text, patterns) - if not start then break end - text = protect_range(text, start, stop) - end - return text -end - --- Protects blocklevel tags in the specified text -local function protect(text) - -- First protect potentially nested block tags - text = protect_matches(text, map(PD.tags, block_pattern)) - -- Then protect block tags at the line level. - text = protect_matches(text, map(PD.tags, line_pattern)) - -- Protect
    and comment tags - text = protect_matches(text, {"\n]->[ \t]*\n"}) - text = protect_matches(text, {"\n[ \t]*\n"}) - return text -end - --- Returns true if the string s is a hash resulting from protection -local function is_protected(s) - return PD.blocks[s] -end - --- Unprotects the specified text by expanding all the nonces -local function unprotect(text) - for k,v in pairs(PD.blocks) do - v = v:gsub("%%", "%%%%") - text = text:gsub(k, v) - end - return text -end - - -local code_block_ranges = {} - ----------------------------------------------------------------------- --- Block transform ----------------------------------------------------------------------- - --- The block transform functions transform the text on the block level. --- They work with the text as an array of lines rather than as individual --- characters. - --- Returns true if the line is a ruler of (char) characters. --- The line must contain at least three char characters and contain only spaces and --- char characters. -local function is_ruler_of(line, char) - if not line:match("^[ %" .. char .. "]*$") then return false end - if not line:match("%" .. char .. ".*%" .. char .. ".*%" .. char) then return false end - return true -end - --- Identifies the block level formatting present in the line -local function classify(line) - local info = {line = line, text = line} - - if line:match("^ ") then - info.type = "indented" - info.outdented = line:sub(5) - return info - end - - for _,c in ipairs({'*', '-', '_', '='}) do - if is_ruler_of(line, c) then - info.type = "ruler" - info.ruler_char = c - return info - end - end - - if line == "" then - info.type = "blank" - return info - end - - if line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") then - local m1, m2 = line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") - info.type = "header" - info.level = m1:len() - info.text = m2 - return info - end - - if line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") then - local number, text = line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") - info.type = "list_item" - info.list_type = "numeric" - info.number = 0 + number - info.text = text - return info - end - - if line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") then - local bullet, text = line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") - info.type = "list_item" - info.list_type = "bullet" - info.bullet = bullet - info.text= text - return info - end - - if line:match("^>[ \t]?(.*)") then - info.type = "blockquote" - info.text = line:match("^>[ \t]?(.*)") - return info - end - - if is_protected(line) then - info.type = "raw" - info.html = unprotect(line) - return info - end - - info.type = "normal" - return info -end - --- Find headers constisting of a normal line followed by a ruler and converts them to --- header entries. -local function headers(array) - local i = 1 - while i <= #array - 1 do - if array[i].type == "normal" and array[i+1].type == "ruler" and - (array[i+1].ruler_char == "-" or array[i+1].ruler_char == "=") then - local info = {line = array[i].line} - info.text = info.line - info.type = "header" - info.level = iff(array[i+1].ruler_char == "=", 1, 2) - table.remove(array, i+1) - array[i] = info - end - i = i + 1 - end - return array -end - --- Forward declarations -local block_transform, span_transform, encode_code - --- Convert lines to html code -local function blocks_to_html(lines, no_paragraphs) - local out = {} - local i = 1 - while i <= #lines do - local line = lines[i] - if line.type == "ruler" then - table.insert(out, "
    ") - elseif line.type == "raw" then - table.insert(out, line.html) - elseif line.type == "normal" then - local s = line.line - - while i+1 <= #lines and lines[i+1].type == "normal" do - i = i + 1 - s = s .. "\n" .. lines[i].line - end - - if no_paragraphs then - table.insert(out, span_transform(s)) - else - table.insert(out, "

    " .. span_transform(s) .. "

    ") - end - elseif line.type == "header" then - local s = "" .. span_transform(line.text) .. "" - table.insert(out, s) - else - table.insert(out, line.line) - end - i = i + 1 - end - return out -end - --- Find list blocks and convert them to protected data blocks -local function lists(array, sublist) - local function process_list(arr) - local function any_blanks(arr) - for i = 1, #arr do - if arr[i].type == "blank" then return true end - end - return false - end - - local function split_list_items(arr) - local acc = {arr[1]} - local res = {} - for i=2,#arr do - if arr[i].type == "list_item" then - table.insert(res, acc) - acc = {arr[i]} - else - table.insert(acc, arr[i]) - end - end - table.insert(res, acc) - return res - end - - local function process_list_item(lines, block) - while lines[#lines].type == "blank" do - table.remove(lines) - end - - local itemtext = lines[1].text - for i=2,#lines do - itemtext = itemtext .. "\n" .. outdent(lines[i].line) - end - if block then - itemtext = block_transform(itemtext, true) - if not itemtext:find("
    ") then itemtext = indent(itemtext) end
    -            return "    
  • " .. itemtext .. "
  • " - else - local lines = split(itemtext) - lines = map(lines, classify) - lines = lists(lines, true) - lines = blocks_to_html(lines, true) - itemtext = table.concat(lines, "\n") - if not itemtext:find("
    ") then itemtext = indent(itemtext) end
    -            return "    
  • " .. itemtext .. "
  • " - end - end - - local block_list = any_blanks(arr) - local items = split_list_items(arr) - local out = "" - for _, item in ipairs(items) do - out = out .. process_list_item(item, block_list) .. "\n" - end - if arr[1].list_type == "numeric" then - return "
      \n" .. out .. "
    " - else - return "
      \n" .. out .. "
    " - end - end - - -- Finds the range of lines composing the first list in the array. A list - -- starts with (^ list_item) or (blank list_item) and ends with - -- (blank* $) or (blank normal). - -- - -- A sublist can start with just (list_item) does not need a blank... - local function find_list(array, sublist) - local function find_list_start(array, sublist) - if array[1].type == "list_item" then return 1 end - if sublist then - for i = 1,#array do - if array[i].type == "list_item" then return i end - end - else - for i = 1, #array-1 do - if array[i].type == "blank" and array[i+1].type == "list_item" then - return i+1 - end - end - end - return nil - end - local function find_list_end(array, start) - local pos = #array - for i = start, #array-1 do - if array[i].type == "blank" and array[i+1].type ~= "list_item" - and array[i+1].type ~= "indented" and array[i+1].type ~= "blank" then - pos = i-1 - break - end - end - while pos > start and array[pos].type == "blank" do - pos = pos - 1 - end - return pos - end - - local start = find_list_start(array, sublist) - if not start then return nil end - return start, find_list_end(array, start) - end - - while true do - local start, stop = find_list(array, sublist) - if not start then break end - local text = process_list(splice(array, start, stop)) - local info = { - line = text, - type = "raw", - html = text - } - array = splice(array, start, stop, {info}) - end - - -- Convert any remaining list items to normal - for _,line in ipairs(array) do - if line.type == "list_item" then line.type = "normal" end - end - - return array -end - --- Find and convert blockquote markers. -local function blockquotes(lines) - local function find_blockquote(lines) - local start - for i,line in ipairs(lines) do - if line.type == "blockquote" then - start = i - break - end - end - if not start then return nil end - - local stop = #lines - for i = start+1, #lines do - if lines[i].type == "blank" or lines[i].type == "blockquote" then - elseif lines[i].type == "normal" then - if lines[i-1].type == "blank" then stop = i-1 break end - else - stop = i-1 break - end - end - while lines[stop].type == "blank" do stop = stop - 1 end - return start, stop - end - - local function process_blockquote(lines) - local raw = lines[1].text - for i = 2,#lines do - raw = raw .. "\n" .. lines[i].text - end - local bt = block_transform(raw) - if not bt:find("
    ") then bt = indent(bt) end
    -      return "
    \n " .. bt .. - "\n
    " - end - - while true do - local start, stop = find_blockquote(lines) - if not start then break end - local text = process_blockquote(splice(lines, start, stop)) - local info = { - line = text, - type = "raw", - html = text - } - lines = splice(lines, start, stop, {info}) - end - return lines -end - --- Find and convert codeblocks. -local function codeblocks(lines) - local function find_codeblock(lines) - local start - for i,line in ipairs(lines) do - if line.type == "indented" then start = i break end - end - if not start then return nil end - - local stop = #lines - for i = start+1, #lines do - if lines[i].type ~= "indented" and lines[i].type ~= "blank" then - stop = i-1 - break - end - end - while lines[stop].type == "blank" do stop = stop - 1 end - return start, stop - end - - local function process_codeblock(lines) - local raw = detab(encode_code(outdent(lines[1].line))) - for i = 2,#lines do - raw = raw .. "\n" .. detab(encode_code(outdent(lines[i].line))) - end - return "
    " .. raw .. "\n
    " - end - - while true do - local start, stop = find_codeblock(lines) - if not start then break end - local text = process_codeblock(splice(lines, start, stop)) - local info = { - line = text, - type = "raw", - html = text - } - lines = splice(lines, start, stop, {info}) - end - return lines -end - --- Perform all the block level transforms -function block_transform(text, sublist) - local lines = split(text) - lines = map(lines, classify) - lines = headers(lines) - lines = lists(lines, sublist) - lines = codeblocks(lines) - lines = blockquotes(lines) - lines = blocks_to_html(lines) - local text = table.concat(lines, "\n") - return text -end - ----------------------------------------------------------------------- --- Span transform ----------------------------------------------------------------------- - --- Functions for transforming the text at the span level. - --- These characters may need to be escaped because they have a special --- meaning in markdown. -local escape_chars = "'\\`*_{}[]()>#+-.!'" -local escape_table = {} - -local function init_escape_table() - escape_table = {} - for i = 1,#escape_chars do - local c = escape_chars:sub(i,i) - escape_table[c] = hash(c) - end -end - --- Adds a new escape to the escape table. -local function add_escape(text) - if not escape_table[text] then - escape_table[text] = hash(text) - end - return escape_table[text] -end - --- Encode backspace-escaped characters in the markdown source. -local function encode_backslash_escapes(t) - for i=1,escape_chars:len() do - local c = escape_chars:sub(i,i) - t = t:gsub("\\%" .. c, escape_table[c]) - end - return t -end - --- Escape characters that should not be disturbed by markdown. -local function escape_special_chars(text) - local tokens = tokenize_html(text) - - local out = "" - for _, token in ipairs(tokens) do - local t = token.text - if token.type == "tag" then - -- In tags, encode * and _ so they don't conflict with their use in markdown. - t = t:gsub("%*", escape_table["*"]) - t = t:gsub("%_", escape_table["_"]) - else - t = encode_backslash_escapes(t) - end - out = out .. t - end - return out -end - --- Unescape characters that have been encoded. -local function unescape_special_chars(t) - local tin = t - for k,v in pairs(escape_table) do - k = k:gsub("%%", "%%%%") - t = t:gsub(v,k) - end - if t ~= tin then t = unescape_special_chars(t) end - return t -end - --- Encode/escape certain characters inside Markdown code runs. --- The point is that in code, these characters are literals, --- and lose their special Markdown meanings. -function encode_code(s) - s = s:gsub("%&", "&") - s = s:gsub("<", "<") - s = s:gsub(">", ">") - for k,v in pairs(escape_table) do - s = s:gsub("%"..k, v) - end - return s -end - --- Handle backtick blocks. -local function code_spans(s) - s = s:gsub("\\\\", escape_table["\\"]) - s = s:gsub("\\`", escape_table["`"]) - - local pos = 1 - while true do - local start, stop = s:find("`+", pos) - if not start then return s end - local count = stop - start + 1 - -- Find a matching numbert of backticks - local estart, estop = s:find(string.rep("`", count), stop+1) - local brstart = s:find("\n", stop+1) - if estart and (not brstart or estart < brstart) then - local code = s:sub(stop+1, estart-1) - code = code:gsub("^[ \t]+", "") - code = code:gsub("[ \t]+$", "") - code = code:gsub(escape_table["\\"], escape_table["\\"] .. escape_table["\\"]) - code = code:gsub(escape_table["`"], escape_table["\\"] .. escape_table["`"]) - code = "" .. encode_code(code) .. "" - code = add_escape(code) - s = s:sub(1, start-1) .. code .. s:sub(estop+1) - pos = start + code:len() - else - pos = stop + 1 - end - end - return s -end - --- Encode alt text... enodes &, and ". -local function encode_alt(s) - if not s then return s end - s = s:gsub('&', '&') - s = s:gsub('"', '"') - s = s:gsub('<', '<') - return s -end - --- Forward declaration for link_db as returned by strip_link_definitions. -local link_database - --- Handle image references -local function images(text) - local function reference_link(alt, id) - alt = encode_alt(alt:match("%b[]"):sub(2,-2)) - id = id:match("%[(.*)%]"):lower() - if id == "" then id = text:lower() end - link_database[id] = link_database[id] or {} - if not link_database[id].url then return nil end - local url = link_database[id].url or id - url = encode_alt(url) - local title = encode_alt(link_database[id].title) - if title then title = " title=\"" .. title .. "\"" else title = "" end - return add_escape ('' .. alt .. '") - end - - local function inline_link(alt, link) - alt = encode_alt(alt:match("%b[]"):sub(2,-2)) - local url, title = link:match("%(?[ \t]*['\"](.+)['\"]") - url = url or link:match("%(?%)") - url = encode_alt(url) - title = encode_alt(title) - if title then - return add_escape('' .. alt .. '') - else - return add_escape('' .. alt .. '') - end - end - - text = text:gsub("!(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) - text = text:gsub("!(%b[])(%b())", inline_link) - return text -end - --- Handle anchor references -local function anchors(text) - local function reference_link(text, id) - text = text:match("%b[]"):sub(2,-2) - id = id:match("%b[]"):sub(2,-2):lower() - if id == "" then id = text:lower() end - link_database[id] = link_database[id] or {} - if not link_database[id].url then return nil end - local url = link_database[id].url or id - url = encode_alt(url) - local title = encode_alt(link_database[id].title) - if title then title = " title=\"" .. title .. "\"" else title = "" end - return add_escape("") .. text .. add_escape("") - end - - local function inline_link(text, link) - text = text:match("%b[]"):sub(2,-2) - local url, title = link:match("%(?[ \t]*['\"](.+)['\"]") - title = encode_alt(title) - url = url or link:match("%(?%)") or "" - url = encode_alt(url) - if title then - return add_escape("") .. text .. "" - else - return add_escape("") .. text .. add_escape("") - end - end - - text = text:gsub("(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) - text = text:gsub("(%b[])(%b())", inline_link) - return text -end - --- Handle auto links, i.e. . -local function auto_links(text) - local function link(s) - return add_escape("") .. s .. "" - end - -- Encode chars as a mix of dec and hex entitites to (perhaps) fool - -- spambots. - local function encode_email_address(s) - -- Use a deterministic encoding to make unit testing possible. - -- Code 45% hex, 45% dec, 10% plain. - local hex = {code = function(c) return "&#x" .. string.format("%x", c:byte()) .. ";" end, count = 1, rate = 0.45} - local dec = {code = function(c) return "&#" .. c:byte() .. ";" end, count = 0, rate = 0.45} - local plain = {code = function(c) return c end, count = 0, rate = 0.1} - local codes = {hex, dec, plain} - local function swap(t,k1,k2) local temp = t[k2] t[k2] = t[k1] t[k1] = temp end - - local out = "" - for i = 1,s:len() do - for _,code in ipairs(codes) do code.count = code.count + code.rate end - if codes[1].count < codes[2].count then swap(codes,1,2) end - if codes[2].count < codes[3].count then swap(codes,2,3) end - if codes[1].count < codes[2].count then swap(codes,1,2) end - - local code = codes[1] - local c = s:sub(i,i) - -- Force encoding of "@" to make email address more invisible. - if c == "@" and code == plain then code = codes[2] end - out = out .. code.code(c) - code.count = code.count - 1 - end - return out - end - local function mail(s) - s = unescape_special_chars(s) - local address = encode_email_address("mailto:" .. s) - local text = encode_email_address(s) - return add_escape("") .. text .. "" - end - -- links - text = text:gsub("<(https?:[^'\">%s]+)>", link) - text = text:gsub("<(ftp:[^'\">%s]+)>", link) - - -- mail - text = text:gsub("%s]+)>", mail) - text = text:gsub("<([-.%w]+%@[-.%w]+)>", mail) - return text -end - --- Encode free standing amps (&) and angles (<)... note that this does not --- encode free >. -local function amps_and_angles(s) - -- encode amps not part of &..; expression - local pos = 1 - while true do - local amp = s:find("&", pos) - if not amp then break end - local semi = s:find(";", amp+1) - local stop = s:find("[ \t\n&]", amp+1) - if not semi or (stop and stop < semi) or (semi - amp) > 15 then - s = s:sub(1,amp-1) .. "&" .. s:sub(amp+1) - pos = amp+1 - else - pos = amp+1 - end - end - - -- encode naked <'s - s = s:gsub("<([^a-zA-Z/?$!])", "<%1") - s = s:gsub("<$", "<") - - -- what about >, nothing done in the original markdown source to handle them - return s -end - --- Handles emphasis markers (* and _) in the text. -local function emphasis(text) - for _, s in ipairs {"%*%*", "%_%_"} do - text = text:gsub(s .. "([^%s][%*%_]?)" .. s, "%1") - text = text:gsub(s .. "([^%s][^<>]-[^%s][%*%_]?)" .. s, "%1") - end - for _, s in ipairs {"%*", "%_"} do - text = text:gsub(s .. "([^%s_])" .. s, "%1") - text = text:gsub(s .. "([^%s_])" .. s, "%1") - text = text:gsub(s .. "([^%s_][^<>_]-[^%s_])" .. s, "%1") - text = text:gsub(s .. "([^<>_]-[^<>_]-[^<>_]-)" .. s, "%1") - end - return text -end - --- Handles line break markers in the text. -local function line_breaks(text) - return text:gsub(" +\n", "
    \n") -end - --- Perform all span level transforms. -function span_transform(text) - text = code_spans(text) - text = escape_special_chars(text) - text = images(text) - text = anchors(text) - text = auto_links(text) - text = amps_and_angles(text) - text = emphasis(text) - text = line_breaks(text) - return text -end - ----------------------------------------------------------------------- --- Markdown ----------------------------------------------------------------------- - --- Cleanup the text by normalizing some possible variations to make further --- processing easier. -local function cleanup(text) - -- Standardize line endings - text = text:gsub("\r\n", "\n") -- DOS to UNIX - text = text:gsub("\r", "\n") -- Mac to UNIX - - -- Convert all tabs to spaces - text = detab(text) - - -- Strip lines with only spaces and tabs - while true do - local subs - text, subs = text:gsub("\n[ \t]+\n", "\n\n") - if subs == 0 then break end - end - - return "\n" .. text .. "\n" -end - --- Strips link definitions from the text and stores the data in a lookup table. -local function strip_link_definitions(text) - local linkdb = {} - - local function link_def(id, url, title) - id = id:match("%[(.+)%]"):lower() - linkdb[id] = linkdb[id] or {} - linkdb[id].url = url or linkdb[id].url - linkdb[id].title = title or linkdb[id].title - return "" - end - - local def_no_title = "\n ? ? ?(%b[]):[ \t]*\n?[ \t]*]+)>?[ \t]*" - local def_title1 = def_no_title .. "[ \t]+\n?[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" - local def_title2 = def_no_title .. "[ \t]*\n[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" - local def_title3 = def_no_title .. "[ \t]*\n?[ \t]+[\"'(]([^\n]+)[\"')][ \t]*" - - text = text:gsub(def_title1, link_def) - text = text:gsub(def_title2, link_def) - text = text:gsub(def_title3, link_def) - text = text:gsub(def_no_title, link_def) - return text, linkdb -end - -local lang_to_ext = { - rust = "rs", - lua = "lua", - python = "py", - javascript = "js", - luax = "luax", - cpp = "cpp", - c = "c", - java = "java", - go = "go", - html = "html", - css = "css", - bash = "bash", - sh = "sh", - json = "json", - toml = "toml", - c_sharp = "cs", -} - -local function highlight(lang, code) - -- create tmp file with code - local tmp_filename = os.tmpname() - local ext = lang_to_ext[lang] - if not ext then - ext = lang - end - -- add matching extension to tmp_filename (just lang for now) - tmp_filename = tmp_filename .. "." .. ext - local tmp_file = io.open(tmp_filename, "w") - if not tmp_file then - error("Could not open tmp file: " .. tmp_filename) - end - tmp_file:write(code) - tmp_file:close() - -- run tree-sitter highlight on the code with lang - local tree_sitter_output = io.popen("/usr/bin/env tree-sitter highlight -H " .. tmp_filename) - if not tree_sitter_output then - error("Could not run tree-sitter highlight") - end - - return tree_sitter_output:read("*a") -end - -local function code_block_highlight(text) - -- find ``` blocks - local pos = 1 - while true do - local start, stop = text:find("```", pos) - if not start then break end - local endstart, endstop = text:find("```", stop+1) - if not endstart then break end - - local block = text:sub(stop+2, endstart-1) - -- treat first line after ``` as language - local lang = block:match("^(.-)\n") - -- remove first line from block - block = block:gsub("^[^\n]*\n", "") - -- replace block with highlighted block - block = '
    ' .. highlight(lang, block) .. '
    ' - text = text:sub(1, start-1) .. block .. text:sub(endstop+1) - code_block_ranges[#code_block_ranges+1] = {start, start+block:len()} - pos = start + block:len() - end - return text -end - --- Main markdown processing function -local function markdown(text) - init_hash(text) - init_escape_table() - - text = cleanup(text) - text = protect(text) - text = code_block_highlight(text) - text, link_database = strip_link_definitions(text) - text = block_transform(text) - text = unescape_special_chars(text) - return text -end - ----------------------------------------------------------------------- --- End of module ----------------------------------------------------------------------- - --- For compatibility, set markdown function as a global -_G.markdown = markdown - --- Class for parsing command-line options -local OptionParser = {} -OptionParser.__index = OptionParser - --- Creates a new option parser -function OptionParser:new() - local o = {short = {}, long = {}} - setmetatable(o, self) - return o -end - --- Calls f() whenever a flag with specified short and long name is encountered -function OptionParser:flag(short, long, f) - local info = {type = "flag", f = f} - if short then self.short[short] = info end - if long then self.long[long] = info end -end - --- Calls f(param) whenever a parameter flag with specified short and long name is encountered -function OptionParser:param(short, long, f) - local info = {type = "param", f = f} - if short then self.short[short] = info end - if long then self.long[long] = info end -end - --- Calls f(v) for each non-flag argument -function OptionParser:arg(f) - self.arg = f -end - --- Runs the option parser for the specified set of arguments. Returns true if all arguments --- where successfully parsed and false otherwise. -function OptionParser:run(args) - local pos = 1 - while pos <= #args do - local arg = args[pos] - if arg == "--" then - for i=pos+1,#args do - if self.arg then self.arg(args[i]) end - return true - end - end - if arg:match("^%-%-") then - local info = self.long[arg:sub(3)] - if not info then print("Unknown flag: " .. arg) return false end - if info.type == "flag" then - info.f() - pos = pos + 1 - else - local param = args[pos+1] - if not param then print("No parameter for flag: " .. arg) return false end - info.f(param) - pos = pos+2 - end - elseif arg:match("^%-") then - for i=2,arg:len() do - local c = arg:sub(i,i) - local info = self.short[c] - if not info then print("Unknown flag: -" .. c) return false end - if info.type == "flag" then - info.f() - else - if i == arg:len() then - local param = args[pos+1] - if not param then print("No parameter for flag: -" .. c) return false end - info.f(param) - pos = pos + 1 - else - local param = arg:sub(i+1) - info.f(param) - end - break - end - end - pos = pos + 1 - else - if self.arg then self.arg(arg) end - pos = pos + 1 - end - end - return true -end - -local function read_file(path, descr) - local file = io.open(path) or error("Could not open " .. descr .. " file: " .. path) - local contents = file:read("*a") or error("Could not read " .. descr .. " from " .. path) - file:close() - return contents -end - --- Handles the case when markdown is run from the command line -local function run_command_line(arg) - -- Generate output for input s given options - local function run(s, options) - s = markdown(s) - if not options.wrap_header then return s end - local header - if options.header then - header = read_file(options.header, "header") - else - header = [[ - - - - - TITLE - - - -]] - local title = options.title or s:match("

    (.-)

    ") or s:match("

    (.-)

    ") or - s:match("

    (.-)

    ") or "Untitled" - header = header:gsub("TITLE", title) - if options.inline_style then - local style = read_file(options.stylesheet, "style sheet") - header = header:gsub('', - "") - else - header = header:gsub("STYLESHEET", options.stylesheet) - end - header = header:gsub("CHARSET", options.charset) - end - local footer = "" - if options.footer then - footer = read_file(options.footer, "footer") - end - return header .. s .. footer - end - - -- Generate output path name from input path name given options. - local function outpath(path, options) - if options.append then return path .. ".html" end - local m = path:match("^(.+%.html)[^/\\]+$") if m then return m end - m = path:match("^(.+%.)[^/\\]*$") if m and path ~= m .. "html" then return m .. "html" end - return path .. ".html" - end - - -- Default commandline options - local options = { - wrap_header = true, - header = nil, - footer = nil, - charset = "utf-8", - title = nil, - stylesheet = "default.css", - inline_style = false - } - local help = [[ -Usage: markdown.lua [OPTION] [FILE] -Runs the markdown text markup to HTML converter on each file specified on the -command line. If no files are specified, runs on standard input. - -No header: - -n, --no-wrap Don't wrap the output in ... tags. -Custom header: - -e, --header FILE Use content of FILE for header. - -f, --footer FILE Use content of FILE for footer. -Generated header: - -c, --charset SET Specifies charset (default utf-8). - -i, --title TITLE Specifies title (default from first

    tag). - -s, --style STYLE Specifies style sheet file (default default.css). - -l, --inline-style Include the style sheet file inline in the header. -Generated files: - -a, --append Append .html extension (instead of replacing). -Other options: - -h, --help Print this help text. - -t, --test Run the unit tests. -]] - - local run_stdin = true - local op = OptionParser:new() - op:flag("n", "no-wrap", function () options.wrap_header = false end) - op:param("e", "header", function (x) options.header = x end) - op:param("f", "footer", function (x) options.footer = x end) - op:param("c", "charset", function (x) options.charset = x end) - op:param("i", "title", function(x) options.title = x end) - op:param("s", "style", function(x) options.stylesheet = x end) - op:flag("l", "inline-style", function() options.inline_style = true end) - op:flag("a", "append", function() options.append = true end) - op:flag("t", "test", function() - local n = arg[0]:gsub("markdown%.lua", "markdown-tests.lua") - local f = io.open(n) - if f then - f:close() - package.loaded.markdown = markdown - dofile(n) - else - error("Cannot find markdown-tests.lua") - end - run_stdin = false - end) - op:flag("h", "help", function() print(help) run_stdin = false end) - op:arg(function(path) - local s = read_file(path, "input") - s = run(s, options) - local file = io.open(outpath(path, options), "w") or error("Could not open output file: " .. outpath(path, options)) - file:write(s) - file:close() - run_stdin = false - end - ) - - if not op:run(arg) then - print(help) - run_stdin = false - end - - if run_stdin then - local s = io.read("*a") - s = run(s, options) - io.write(s) - end -end - --- If we are being run from the command-line, act accordingly -if arg and arg[0]:find("markdown%.lua$") then - run_command_line(arg) -else - return markdown -end diff --git a/luax/not_found.luax b/luax/not_found.luax deleted file mode 100644 index 9ad3806..0000000 --- a/luax/not_found.luax +++ /dev/null @@ -1,3 +0,0 @@ -return function() - return "404 Not Found" -end diff --git a/luax/pages/blog.luax b/luax/pages/blog.luax deleted file mode 100644 index a729b63..0000000 --- a/luax/pages/blog.luax +++ /dev/null @@ -1,30 +0,0 @@ -local blogposts = require('blog.posts') - -return function() - local blogpost_html = {} - for _, post in ipairs(blogposts) do - blogpost_html[#blogpost_html + 1] = -
    -
    -
    - {$ post.title $} -
    - -
    -
    - {$ post.description $} -
    -
    - end - return -
    -
    - Blog -
    -
    - {$ blogpost_html $} -
    -
    -end diff --git a/luax/pages/blogpost.luax b/luax/pages/blogpost.luax deleted file mode 100644 index e7d7b86..0000000 --- a/luax/pages/blogpost.luax +++ /dev/null @@ -1,23 +0,0 @@ -local blog_cache = require('blog.cache') - -return function(props) - local post = props.attrs.post - local content = blog_cache(post) - return -
    -
    -
    -
    - {$ post.title $} -
    - -
    - {$ post.description $} -
    -
    {$ content $}
    -
    -
    -
    -end diff --git a/luax/pages/contact.luax b/luax/pages/contact.luax deleted file mode 100644 index 95ad0fb..0000000 --- a/luax/pages/contact.luax +++ /dev/null @@ -1,31 +0,0 @@ -return function() - return -
    -
    - Contact -
    -
    -
    - Discord -
    - - usually the fastest way to reach me -
    - EchoDuck#7866 - -
    - Matrix -
    - for the FOSS folks -
    - @echoduck:matrix.org - -
    - Email -
    - I check my email daily -
    - duckonaut@gmail.com -
    -
    -end diff --git a/luax/pages/games.luax b/luax/pages/games.luax deleted file mode 100644 index 59a68b4..0000000 --- a/luax/pages/games.luax +++ /dev/null @@ -1,25 +0,0 @@ -local Project = require("components.project") - -local games = require("data.games") - -local game_display = {} -for _, game in ipairs(games) do - game_display[#game_display + 1] = -end - -return function() - return -
    -
    - Game Projects -
    -
    - games I've worked on -
    - -
    - {$ game_display $} -
    -
    -end - diff --git a/luax/pages/home.luax b/luax/pages/home.luax deleted file mode 100644 index ed1b464..0000000 --- a/luax/pages/home.luax +++ /dev/null @@ -1,33 +0,0 @@ -return function() - return
    -
    - Stanisław Zagórowski -
    -
    - aka Duckonaut, EchoDuck -
    - - -
    -

    - Hello! I'm a programmer, game designer, and artist. I'm currently a - student at the Warsaw University of Technology, studying Computer Science. - A list of the games I've worked on is in the - games - section. Other projects I've been a part of are available in the - projects - section. I also enjoy some lower-level programming and learning about the - inner workings of various systems. The main technologies I use are - C#, Unity, - Python, Rust - and C although I'm also familiar with - Godot, C++ - , Java and some - JavaScript and TypeScript. -

    -
    -
    -end diff --git a/luax/pages/projects.luax b/luax/pages/projects.luax deleted file mode 100644 index 219f45d..0000000 --- a/luax/pages/projects.luax +++ /dev/null @@ -1,24 +0,0 @@ -local Project = require("components.project") - -local projects = require("data.projects") - -local project_display = {} -for _, project in ipairs(projects) do - project_display[#project_display + 1] = -end - -return function() - return -
    -
    - Other Projects -
    -
    - projects that are not games -
    - -
    - {$ project_display $} -
    -
    -end diff --git a/luax/public/CNAME b/luax/public/CNAME deleted file mode 100644 index a2fa953..0000000 --- a/luax/public/CNAME +++ /dev/null @@ -1 +0,0 @@ -cdn.duckonaut.dev diff --git a/luax/public/assets/app.css b/luax/public/assets/app.css deleted file mode 100644 index c98e9b0..0000000 --- a/luax/public/assets/app.css +++ /dev/null @@ -1,171 +0,0 @@ -#app-main { - font-family: 'Hack', monospace; - max-width: 1280px; - margin: 0 auto; - padding: 1rem 1rem 1rem 1rem; - text-align: center; -} - -#app { - margin: 0 auto; - text-align: center; - align-content: center; - justify-content: center; -} - -.logo { - height: 6em; - padding: 1.5em; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.preact:hover { - filter: drop-shadow(0 0 2em #673ab8aa); -} - -/* fade in */ -@keyframes fade-in { - 0% { - opacity: 0; - transform: translateY(1em); - } - 60% { - transform: translateY(-0.2em); - } - 100% { - opacity: 1; - transform: translateY(0); - } -} - -.main-info > * { - animation: fade-in 0.2s ease-in; -} - -.main-info { - text-align: center; -} - -@media (min-width: 1150px) { - .main-info { - margin-top: 72px; - } -} - -.main-info-image { - width: 128px; - height: 128px; - border-radius: 100%; - margin: 6px 6px 6px 6px; -} - -.main-info-title { - font-size: 42pt; -} - -.main-info-subtitle { - font-size: 26pt; - font-style: italic; - color: #5c6370; -} - -.quiet { - color: var(--color-gray); - font-style: italic; -} - -.section { - font-size: 22pt; -} - -.highlight { - color: #e5c07b; -} - -.back-shadows { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: -1; - background: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, var(--color-bg) 10%, var(--color-bg) 90%, rgba(0, 0, 0, 0) 100%); -} - -#animated-bg { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: -10; -} - - -@media (max-width: 480px) { - .back-shadows { - margin: auto; - max-width: 80%; - } - .main-info-title { - font-size: 32pt; - } - - .main-info-subtitle { - font-size: 18pt; - } -} - -@media (max-width: 1150px) and (min-width: 480px) { - .back-shadows { - margin: auto; - max-width: 60%; - } - .main-info-title { - font-size: 42pt; - } - - .main-info-subtitle { - font-size: 26pt; - } -} - -@media (min-width: 1150px) { - .back-shadows { - margin: auto; - max-width: 50%; - } - .main-info-title { - font-size: 42pt; - } - - .main-info-subtitle { - font-size: 26pt; - } -} - - -@media (max-width: 480px) { - .about-paragraph { - margin: auto; - font-size: 16pt; - max-width: 90%; - } -} - -@media (max-width: 1150px) and (min-width: 480px) { - .about-paragraph { - margin: auto; - font-size: 18pt; - max-width: 80%; - } -} - -@media (min-width: 1150px) { - .about-paragraph { - margin: auto; - font-size: 18pt; - max-width: 60%; - } -} diff --git a/luax/public/assets/blog.css b/luax/public/assets/blog.css deleted file mode 100644 index 9d0180c..0000000 --- a/luax/public/assets/blog.css +++ /dev/null @@ -1,108 +0,0 @@ -.code-block { - text-align: left; - background-color: #161f30; - border: 2px solid #2a3a4f; - border-radius: 8px; - padding: 2px; - margin: 8px; - font-family: Hack, monospace; - font-size: 10pt; - overflow-x: auto; -} - -code { - font-family: monospace; - font-size: 14pt; -} - -blockquote { - border-left: 4px solid #2a3a4f; - padding-left: 8px; - margin-left: 8px; - font-style: italic; -} - -td.line-number { - text-align: right; - padding-right: 5px; - color: #647089; -} - -.post-description { - text-align: justify; - font-style: italic; - color: #647089; - margin: 0 auto; - max-width: 800px; - padding: 0 10px; -} - -.post-date { - text-align: right; - font-style: italic; - margin: 0 auto; - max-width: 800px; - padding: 0 10px; -} - -.post-content { - text-align: justify; - margin: 0 auto; - max-width: 800px; -} - -.post-content img { - max-width: 100%; - height: auto; - border-radius: 8px; - border: 2px solid #2a3a4f; - margin: auto 0; -} - -.post-content p:has(img) { - text-align: center; -} - -.blog-post-header { - align-items: center; - text-align: center; - margin: 0 auto; - max-width: 800px; - padding: 0 10px; -} - -.blog-post-title { - font-size: 24pt; - font-weight: bold; - margin: 0; - padding: 0; -} - -.blog-post-date { - font-size: 12pt; - font-style: italic; - color: #647089; - margin: 0; - padding: 0; -} - -@media (max-width: 480px) { - .post-content { - margin: auto; - max-width: 96%; - } -} - -@media (max-width: 1150px) and (min-width: 480px) { - .post-content { - margin: auto; - max-width: 80%; - } -} - -@media (min-width: 1150px) { - .post-content { - margin: auto; - max-width: 60%; - } -} diff --git a/luax/public/assets/blog/building-reluax/cli.png b/luax/public/assets/blog/building-reluax/cli.png deleted file mode 100644 index 097957f..0000000 Binary files a/luax/public/assets/blog/building-reluax/cli.png and /dev/null differ diff --git a/luax/public/assets/blog/building-reluax/first-demo.png b/luax/public/assets/blog/building-reluax/first-demo.png deleted file mode 100644 index 81c212c..0000000 Binary files a/luax/public/assets/blog/building-reluax/first-demo.png and /dev/null differ diff --git a/luax/public/assets/blog/building-reluax/second-demo.png b/luax/public/assets/blog/building-reluax/second-demo.png deleted file mode 100644 index 52e9a10..0000000 Binary files a/luax/public/assets/blog/building-reluax/second-demo.png and /dev/null differ diff --git a/luax/public/assets/carousel.css b/luax/public/assets/carousel.css deleted file mode 100644 index 2465370..0000000 --- a/luax/public/assets/carousel.css +++ /dev/null @@ -1,69 +0,0 @@ -.carousel-content { - align-self: center; - max-height: 100%; - white-space: nowrap; - -ms-overflow-style: none; /* hide scrollbar in IE and Edge */ - scrollbar-width: none; /* hide scrollbar in Firefox */ -} - -.carousel-item { - width: 100%; - flex-shrink: 0; - height: auto; - float: left; - text-align: center; - transition: all 250ms linear; - white-space: nowrap; - vertical-align: middle; -} - -.slick-slider { - margin: 24px; -} - -.slick-slide { - margin: 0 auto; - border-radius: 10px; - text-align: center; - float: none !important; - display: inline-block !important; - vertical-align: middle; - min-height: 1px; -} - -/* .carousel-item > * { */ -/* border-radius: 10px; */ -/* vertical-align: middle; */ -/* width: 100%; */ -/* height: auto; */ -/* } */ - -.slick-dots li button::before { - color: var(--color-fg) !important; -} - -@media (max-width: 1150px) { - .carousel-wrapper { - margin: 60px auto; - width: 330px; - height: 220px; - } -} - -@media (max-width: 1150px) and (min-width: 480px) { - .carousel-wrapper { - margin: 60px auto; - width: 480px; - height: 360px; - } -} - -@media (min-width: 1150px) { - .carousel-wrapper { - float: right; - width: 480px; - height: 360px; - } -} - - diff --git a/luax/public/assets/contact.css b/luax/public/assets/contact.css deleted file mode 100644 index 93b63bd..0000000 --- a/luax/public/assets/contact.css +++ /dev/null @@ -1,22 +0,0 @@ -.contact-container { - width: 100%; - max-width: 1120px; - margin: auto; - margin-top: 24pt; - margin-bottom: 48pt; - align-items: center; - justify-content: center; - align-self: center; - font-size: 18pt; -} - -.contact { - margin-top: 24pt; - margin-bottom: 8pt; - font-size: 20pt; - color: #98C379; -} - -.highlight { - color: #e5c07b; -} diff --git a/luax/public/assets/games/bumbling-builders/screenshot1.png b/luax/public/assets/games/bumbling-builders/screenshot1.png deleted file mode 100644 index 7a8a2db..0000000 Binary files a/luax/public/assets/games/bumbling-builders/screenshot1.png and /dev/null differ diff --git a/luax/public/assets/games/bumbling-builders/screenshot2.png b/luax/public/assets/games/bumbling-builders/screenshot2.png deleted file mode 100644 index 5eb0a1f..0000000 Binary files a/luax/public/assets/games/bumbling-builders/screenshot2.png and /dev/null differ diff --git a/luax/public/assets/games/bumbling-builders/screenshot3.png b/luax/public/assets/games/bumbling-builders/screenshot3.png deleted file mode 100644 index 41f65ac..0000000 Binary files a/luax/public/assets/games/bumbling-builders/screenshot3.png and /dev/null differ diff --git a/luax/public/assets/games/bumbling-builders/thumbnail.png b/luax/public/assets/games/bumbling-builders/thumbnail.png deleted file mode 100644 index 1c723a2..0000000 Binary files a/luax/public/assets/games/bumbling-builders/thumbnail.png and /dev/null differ diff --git a/luax/public/assets/games/dicemask/thumbnail.png b/luax/public/assets/games/dicemask/thumbnail.png deleted file mode 100644 index f9bc9ee..0000000 Binary files a/luax/public/assets/games/dicemask/thumbnail.png and /dev/null differ diff --git a/luax/public/assets/games/greachers/screenshot1.png b/luax/public/assets/games/greachers/screenshot1.png deleted file mode 100644 index 444106e..0000000 Binary files a/luax/public/assets/games/greachers/screenshot1.png and /dev/null differ diff --git a/luax/public/assets/games/greachers/screenshot2.png b/luax/public/assets/games/greachers/screenshot2.png deleted file mode 100644 index e968c4f..0000000 Binary files a/luax/public/assets/games/greachers/screenshot2.png and /dev/null differ diff --git a/luax/public/assets/games/greachers/screenshot3.png b/luax/public/assets/games/greachers/screenshot3.png deleted file mode 100644 index 6433fb6..0000000 Binary files a/luax/public/assets/games/greachers/screenshot3.png and /dev/null differ diff --git a/luax/public/assets/games/greachers/title.png b/luax/public/assets/games/greachers/title.png deleted file mode 100644 index cdb3e67..0000000 Binary files a/luax/public/assets/games/greachers/title.png and /dev/null differ diff --git a/luax/public/assets/games/loud/screenshot1.jpg b/luax/public/assets/games/loud/screenshot1.jpg deleted file mode 100644 index 8192c15..0000000 Binary files a/luax/public/assets/games/loud/screenshot1.jpg and /dev/null differ diff --git a/luax/public/assets/games/loud/screenshot2.jpg b/luax/public/assets/games/loud/screenshot2.jpg deleted file mode 100644 index 88a1ffe..0000000 Binary files a/luax/public/assets/games/loud/screenshot2.jpg and /dev/null differ diff --git a/luax/public/assets/games/loud/screenshot3.jpg b/luax/public/assets/games/loud/screenshot3.jpg deleted file mode 100644 index dfbe555..0000000 Binary files a/luax/public/assets/games/loud/screenshot3.jpg and /dev/null differ diff --git a/luax/public/assets/games/loud/thumbnail.jpg b/luax/public/assets/games/loud/thumbnail.jpg deleted file mode 100644 index 9ec9764..0000000 Binary files a/luax/public/assets/games/loud/thumbnail.jpg and /dev/null differ diff --git a/luax/public/assets/games/miro/screenshot1.png b/luax/public/assets/games/miro/screenshot1.png deleted file mode 100644 index 5ba2365..0000000 Binary files a/luax/public/assets/games/miro/screenshot1.png and /dev/null differ diff --git a/luax/public/assets/games/miro/screenshot2.png b/luax/public/assets/games/miro/screenshot2.png deleted file mode 100644 index 706e47a..0000000 Binary files a/luax/public/assets/games/miro/screenshot2.png and /dev/null differ diff --git a/luax/public/assets/games/miro/screenshot3.png b/luax/public/assets/games/miro/screenshot3.png deleted file mode 100644 index 237be9c..0000000 Binary files a/luax/public/assets/games/miro/screenshot3.png and /dev/null differ diff --git a/luax/public/assets/games/miro/thumbnail.png b/luax/public/assets/games/miro/thumbnail.png deleted file mode 100644 index 152acfc..0000000 Binary files a/luax/public/assets/games/miro/thumbnail.png and /dev/null differ diff --git a/luax/public/assets/games/mr-final-boss/screenshot1.png b/luax/public/assets/games/mr-final-boss/screenshot1.png deleted file mode 100644 index d13f639..0000000 Binary files a/luax/public/assets/games/mr-final-boss/screenshot1.png and /dev/null differ diff --git a/luax/public/assets/games/mr-final-boss/screenshot2.png b/luax/public/assets/games/mr-final-boss/screenshot2.png deleted file mode 100644 index 6b07d1c..0000000 Binary files a/luax/public/assets/games/mr-final-boss/screenshot2.png and /dev/null differ diff --git a/luax/public/assets/games/mr-final-boss/screenshot3.png b/luax/public/assets/games/mr-final-boss/screenshot3.png deleted file mode 100644 index bf6d965..0000000 Binary files a/luax/public/assets/games/mr-final-boss/screenshot3.png and /dev/null differ diff --git a/luax/public/assets/games/mr-final-boss/screenshot4.png b/luax/public/assets/games/mr-final-boss/screenshot4.png deleted file mode 100644 index c6b4cbe..0000000 Binary files a/luax/public/assets/games/mr-final-boss/screenshot4.png and /dev/null differ diff --git a/luax/public/assets/games/mr-final-boss/thumbnail.png b/luax/public/assets/games/mr-final-boss/thumbnail.png deleted file mode 100644 index 024dab4..0000000 Binary files a/luax/public/assets/games/mr-final-boss/thumbnail.png and /dev/null differ diff --git a/luax/public/assets/games/riff-n-rage/screenshot1.png b/luax/public/assets/games/riff-n-rage/screenshot1.png deleted file mode 100644 index 6a9b075..0000000 Binary files a/luax/public/assets/games/riff-n-rage/screenshot1.png and /dev/null differ diff --git a/luax/public/assets/games/riff-n-rage/screenshot2.png b/luax/public/assets/games/riff-n-rage/screenshot2.png deleted file mode 100644 index 5b68901..0000000 Binary files a/luax/public/assets/games/riff-n-rage/screenshot2.png and /dev/null differ diff --git a/luax/public/assets/games/riff-n-rage/thumbnail.png b/luax/public/assets/games/riff-n-rage/thumbnail.png deleted file mode 100644 index fa8dcad..0000000 Binary files a/luax/public/assets/games/riff-n-rage/thumbnail.png and /dev/null differ diff --git a/luax/public/assets/games/survive-the-temple/cover.png b/luax/public/assets/games/survive-the-temple/cover.png deleted file mode 100644 index 5394eeb..0000000 Binary files a/luax/public/assets/games/survive-the-temple/cover.png and /dev/null differ diff --git a/luax/public/assets/games/survive-the-temple/screenshot.png b/luax/public/assets/games/survive-the-temple/screenshot.png deleted file mode 100644 index 92b8ba9..0000000 Binary files a/luax/public/assets/games/survive-the-temple/screenshot.png and /dev/null differ diff --git a/luax/public/assets/games/thermal-trail/gameart.png b/luax/public/assets/games/thermal-trail/gameart.png deleted file mode 100644 index 34c82bc..0000000 Binary files a/luax/public/assets/games/thermal-trail/gameart.png and /dev/null differ diff --git a/luax/public/assets/games/thermal-trail/screenshot1.png b/luax/public/assets/games/thermal-trail/screenshot1.png deleted file mode 100644 index 4b940aa..0000000 Binary files a/luax/public/assets/games/thermal-trail/screenshot1.png and /dev/null differ diff --git a/luax/public/assets/games/thermal-trail/screenshot2.png b/luax/public/assets/games/thermal-trail/screenshot2.png deleted file mode 100644 index 0594eb9..0000000 Binary files a/luax/public/assets/games/thermal-trail/screenshot2.png and /dev/null differ diff --git a/luax/public/assets/games/thermal-trail/screenshot3.png b/luax/public/assets/games/thermal-trail/screenshot3.png deleted file mode 100644 index b5a4a6d..0000000 Binary files a/luax/public/assets/games/thermal-trail/screenshot3.png and /dev/null differ diff --git a/luax/public/assets/games/thermal-trail/screenshot4.png b/luax/public/assets/games/thermal-trail/screenshot4.png deleted file mode 100644 index 1356226..0000000 Binary files a/luax/public/assets/games/thermal-trail/screenshot4.png and /dev/null differ diff --git a/luax/public/assets/games/thermal-trail/thumbnail.png b/luax/public/assets/games/thermal-trail/thumbnail.png deleted file mode 100644 index d779c51..0000000 Binary files a/luax/public/assets/games/thermal-trail/thumbnail.png and /dev/null differ diff --git a/luax/public/assets/games/third-party/screenshot1.jpg b/luax/public/assets/games/third-party/screenshot1.jpg deleted file mode 100644 index 6052ea2..0000000 Binary files a/luax/public/assets/games/third-party/screenshot1.jpg and /dev/null differ diff --git a/luax/public/assets/games/third-party/screenshot2.jpg b/luax/public/assets/games/third-party/screenshot2.jpg deleted file mode 100644 index 7a16edf..0000000 Binary files a/luax/public/assets/games/third-party/screenshot2.jpg and /dev/null differ diff --git a/luax/public/assets/games/third-party/screenshot3.jpg b/luax/public/assets/games/third-party/screenshot3.jpg deleted file mode 100644 index 57e2d5b..0000000 Binary files a/luax/public/assets/games/third-party/screenshot3.jpg and /dev/null differ diff --git a/luax/public/assets/games/third-party/screenshot4.jpg b/luax/public/assets/games/third-party/screenshot4.jpg deleted file mode 100644 index 331594a..0000000 Binary files a/luax/public/assets/games/third-party/screenshot4.jpg and /dev/null differ diff --git a/luax/public/assets/games/third-party/thumbnail.png b/luax/public/assets/games/third-party/thumbnail.png deleted file mode 100644 index c3cd158..0000000 Binary files a/luax/public/assets/games/third-party/thumbnail.png and /dev/null differ diff --git a/luax/public/assets/games/wuzmag/screenshot1.png b/luax/public/assets/games/wuzmag/screenshot1.png deleted file mode 100644 index 186be8c..0000000 Binary files a/luax/public/assets/games/wuzmag/screenshot1.png and /dev/null differ diff --git a/luax/public/assets/games/wuzmag/screenshot2.png b/luax/public/assets/games/wuzmag/screenshot2.png deleted file mode 100644 index 683d7ee..0000000 Binary files a/luax/public/assets/games/wuzmag/screenshot2.png and /dev/null differ diff --git a/luax/public/assets/games/wuzmag/screenshot3.png b/luax/public/assets/games/wuzmag/screenshot3.png deleted file mode 100644 index b5398bb..0000000 Binary files a/luax/public/assets/games/wuzmag/screenshot3.png and /dev/null differ diff --git a/luax/public/assets/games/wuzmag/thumbnail.png b/luax/public/assets/games/wuzmag/thumbnail.png deleted file mode 100644 index 0e6dea1..0000000 Binary files a/luax/public/assets/games/wuzmag/thumbnail.png and /dev/null differ diff --git a/luax/public/assets/games/your-left/screenshot1.jpg b/luax/public/assets/games/your-left/screenshot1.jpg deleted file mode 100644 index e76861a..0000000 Binary files a/luax/public/assets/games/your-left/screenshot1.jpg and /dev/null differ diff --git a/luax/public/assets/games/your-left/screenshot2.jpg b/luax/public/assets/games/your-left/screenshot2.jpg deleted file mode 100644 index 9e0f674..0000000 Binary files a/luax/public/assets/games/your-left/screenshot2.jpg and /dev/null differ diff --git a/luax/public/assets/games/your-left/screenshot3.jpg b/luax/public/assets/games/your-left/screenshot3.jpg deleted file mode 100644 index d025ec1..0000000 Binary files a/luax/public/assets/games/your-left/screenshot3.jpg and /dev/null differ diff --git a/luax/public/assets/games/your-left/thumbnail.png b/luax/public/assets/games/your-left/thumbnail.png deleted file mode 100644 index 95f5c17..0000000 Binary files a/luax/public/assets/games/your-left/thumbnail.png and /dev/null differ diff --git a/luax/public/assets/images/android.png b/luax/public/assets/images/android.png deleted file mode 100644 index d1cef88..0000000 Binary files a/luax/public/assets/images/android.png and /dev/null differ diff --git a/luax/public/assets/images/android.svg b/luax/public/assets/images/android.svg deleted file mode 100644 index 910aaab..0000000 --- a/luax/public/assets/images/android.svg +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - diff --git a/luax/public/assets/images/cpp.png b/luax/public/assets/images/cpp.png deleted file mode 100644 index ab883f7..0000000 Binary files a/luax/public/assets/images/cpp.png and /dev/null differ diff --git a/luax/public/assets/images/cpp.svg b/luax/public/assets/images/cpp.svg deleted file mode 100644 index f306183..0000000 --- a/luax/public/assets/images/cpp.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - diff --git a/luax/public/assets/images/csharp.png b/luax/public/assets/images/csharp.png deleted file mode 100644 index 065aa53..0000000 Binary files a/luax/public/assets/images/csharp.png and /dev/null differ diff --git a/luax/public/assets/images/csharp.svg b/luax/public/assets/images/csharp.svg deleted file mode 100644 index 77d5b38..0000000 --- a/luax/public/assets/images/csharp.svg +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - diff --git a/luax/public/assets/images/duckpfp.png b/luax/public/assets/images/duckpfp.png deleted file mode 100644 index bdad92c..0000000 Binary files a/luax/public/assets/images/duckpfp.png and /dev/null differ diff --git a/luax/public/assets/images/favicon.png b/luax/public/assets/images/favicon.png deleted file mode 100644 index 3cfd37a..0000000 Binary files a/luax/public/assets/images/favicon.png and /dev/null differ diff --git a/luax/public/assets/images/favicon.svg b/luax/public/assets/images/favicon.svg deleted file mode 100644 index 3614084..0000000 --- a/luax/public/assets/images/favicon.svg +++ /dev/null @@ -1,54 +0,0 @@ - - - - diff --git a/luax/public/assets/images/github.png b/luax/public/assets/images/github.png deleted file mode 100644 index 0954581..0000000 Binary files a/luax/public/assets/images/github.png and /dev/null differ diff --git a/luax/public/assets/images/github.svg b/luax/public/assets/images/github.svg deleted file mode 100644 index 11f84aa..0000000 --- a/luax/public/assets/images/github.svg +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - diff --git a/luax/public/assets/images/godot.png b/luax/public/assets/images/godot.png deleted file mode 100644 index 4f2bc22..0000000 Binary files a/luax/public/assets/images/godot.png and /dev/null differ diff --git a/luax/public/assets/images/godot.svg b/luax/public/assets/images/godot.svg deleted file mode 100644 index 6a2176a..0000000 --- a/luax/public/assets/images/godot.svg +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - diff --git a/luax/public/assets/images/html5.png b/luax/public/assets/images/html5.png deleted file mode 100644 index e0dcb1f..0000000 Binary files a/luax/public/assets/images/html5.png and /dev/null differ diff --git a/luax/public/assets/images/html5.svg b/luax/public/assets/images/html5.svg deleted file mode 100644 index 70959e4..0000000 --- a/luax/public/assets/images/html5.svg +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - diff --git a/luax/public/assets/images/hyprland.png b/luax/public/assets/images/hyprland.png deleted file mode 100644 index 36d8162..0000000 Binary files a/luax/public/assets/images/hyprland.png and /dev/null differ diff --git a/luax/public/assets/images/hyprland.svg b/luax/public/assets/images/hyprland.svg deleted file mode 100644 index 1f03c7e..0000000 --- a/luax/public/assets/images/hyprland.svg +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - diff --git a/luax/public/assets/images/linux.png b/luax/public/assets/images/linux.png deleted file mode 100644 index 492d0bf..0000000 Binary files a/luax/public/assets/images/linux.png and /dev/null differ diff --git a/luax/public/assets/images/linux.svg b/luax/public/assets/images/linux.svg deleted file mode 100644 index cab00df..0000000 --- a/luax/public/assets/images/linux.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/luax/public/assets/images/macos.png b/luax/public/assets/images/macos.png deleted file mode 100644 index 1f4625c..0000000 Binary files a/luax/public/assets/images/macos.png and /dev/null differ diff --git a/luax/public/assets/images/macos.svg b/luax/public/assets/images/macos.svg deleted file mode 100644 index ec4dc0c..0000000 --- a/luax/public/assets/images/macos.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - diff --git a/luax/public/assets/images/monogame.png b/luax/public/assets/images/monogame.png deleted file mode 100644 index 8648bcc..0000000 Binary files a/luax/public/assets/images/monogame.png and /dev/null differ diff --git a/luax/public/assets/images/pfp.png b/luax/public/assets/images/pfp.png deleted file mode 100644 index a18b664..0000000 Binary files a/luax/public/assets/images/pfp.png and /dev/null differ diff --git a/luax/public/assets/images/placeholder.png b/luax/public/assets/images/placeholder.png deleted file mode 100644 index 97084c7..0000000 Binary files a/luax/public/assets/images/placeholder.png and /dev/null differ diff --git a/luax/public/assets/images/playstation.png b/luax/public/assets/images/playstation.png deleted file mode 100644 index dd22b43..0000000 Binary files a/luax/public/assets/images/playstation.png and /dev/null differ diff --git a/luax/public/assets/images/playstation.svg b/luax/public/assets/images/playstation.svg deleted file mode 100644 index 5d56897..0000000 --- a/luax/public/assets/images/playstation.svg +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - image/svg+xml - - - - - - - - - diff --git a/luax/public/assets/images/switch.png b/luax/public/assets/images/switch.png deleted file mode 100644 index 723817e..0000000 Binary files a/luax/public/assets/images/switch.png and /dev/null differ diff --git a/luax/public/assets/images/switch.svg b/luax/public/assets/images/switch.svg deleted file mode 100644 index 4d891ae..0000000 --- a/luax/public/assets/images/switch.svg +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - diff --git a/luax/public/assets/images/symbols.png b/luax/public/assets/images/symbols.png deleted file mode 100644 index 3d0e9aa..0000000 Binary files a/luax/public/assets/images/symbols.png and /dev/null differ diff --git a/luax/public/assets/images/thumbnail.png b/luax/public/assets/images/thumbnail.png deleted file mode 100644 index f343a8b..0000000 Binary files a/luax/public/assets/images/thumbnail.png and /dev/null differ diff --git a/luax/public/assets/images/thumbnail.svg b/luax/public/assets/images/thumbnail.svg deleted file mode 100644 index 3783fe1..0000000 --- a/luax/public/assets/images/thumbnail.svg +++ /dev/null @@ -1,70 +0,0 @@ - - - -duckonaut.dev diff --git a/luax/public/assets/images/thumbnail.zvg b/luax/public/assets/images/thumbnail.zvg deleted file mode 100644 index 4d675af..0000000 --- a/luax/public/assets/images/thumbnail.zvg +++ /dev/null @@ -1,67 +0,0 @@ - - - -duckonaut.dev diff --git a/luax/public/assets/images/unity.png b/luax/public/assets/images/unity.png deleted file mode 100644 index 5fea459..0000000 Binary files a/luax/public/assets/images/unity.png and /dev/null differ diff --git a/luax/public/assets/images/unity.svg b/luax/public/assets/images/unity.svg deleted file mode 100644 index b9d1859..0000000 --- a/luax/public/assets/images/unity.svg +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - diff --git a/luax/public/assets/images/windows.png b/luax/public/assets/images/windows.png deleted file mode 100644 index a09553e..0000000 Binary files a/luax/public/assets/images/windows.png and /dev/null differ diff --git a/luax/public/assets/images/windows.svg b/luax/public/assets/images/windows.svg deleted file mode 100644 index cb347b6..0000000 --- a/luax/public/assets/images/windows.svg +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - diff --git a/luax/public/assets/images/xamarin.png b/luax/public/assets/images/xamarin.png deleted file mode 100644 index 91ce4ac..0000000 Binary files a/luax/public/assets/images/xamarin.png and /dev/null differ diff --git a/luax/public/assets/images/xbox.png b/luax/public/assets/images/xbox.png deleted file mode 100644 index 9a439ba..0000000 Binary files a/luax/public/assets/images/xbox.png and /dev/null differ diff --git a/luax/public/assets/images/xbox.svg b/luax/public/assets/images/xbox.svg deleted file mode 100644 index 13d7bdd..0000000 --- a/luax/public/assets/images/xbox.svg +++ /dev/null @@ -1,74 +0,0 @@ - - - - - Xbox Logo - - - - image/svg+xml - - Xbox Logo - - - - - - - - - diff --git a/luax/public/assets/index.css b/luax/public/assets/index.css deleted file mode 100644 index 9941a36..0000000 --- a/luax/public/assets/index.css +++ /dev/null @@ -1,108 +0,0 @@ -:root { - font-weight: 400; - - color-scheme: dark; - color: var(--color-fg); - background-color: var(--color-bg); - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; - - /* variables */ - --color-bg: #161f30; - --color-fg: #a3b3d9; - --color-search-blue: #083C5A; - --color-white: #a3b3d9; - --color-gray: #647089; - --color-dark-gray: #3e4451; - --color-context: #6b737f; - --color-light-gray: #c8ccd4; - --color-tree-gray: #1b1f27; - --color-blue: #6080dd; - --color-vivid-blue: #61afef; - --color-dark-blue: #223E55; - --color-folder-blue: #61afef; - --color-light-blue: #a3b3d9; - --color-green: #449871; - --color-cyan: #56b6c2; - --color-light-green: #98c379; - --color-red: #c13251; - --color-orange: #c18a56; - --color-light-red: #e06c75; - --color-yellow: #e5c07b; - --color-purple: #604bd8; - --color-magenta: #b071fc; -} - -a:link { - font-weight: 500; - color: #4fc1ff; - text-decoration: inherit; -} - -a:visited { - color: #4fc1ff; -} - -a:hover { - color: #60d2ff; -} - -a:active { - color: #66d8ff; -} - - -body { - margin: 0; - /* display: flex; */ - /* place-items: center; */ - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -span { - display: inline; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -.hack { - font-family: Hack, monospace; - font-weight: 500; -} - -.grow { - transition: all .2s ease-in-out; - transform: scale(1); -} - -.grow:hover { - transition: all .2s ease-in-out; - transform: scale(1.1); -} diff --git a/luax/public/assets/index.js b/luax/public/assets/index.js deleted file mode 100644 index 6dd6ab6..0000000 --- a/luax/public/assets/index.js +++ /dev/null @@ -1,91 +0,0 @@ -$(document).ready(function() { - $('.carousel-wrapper').slick({ - dots: true, - infinite: true, - speed: 500, - slidesToShow: 1, - slidesToScroll: 1 - }); - - var canvas_parent = document.getElementById('animated-bg'); - var canvas = document.createElement('canvas'); - canvas.width = window.innerWidth; - canvas.height = window.innerHeight / 2; - window.sandbox_canvas = canvas; - - canvas_parent.appendChild(canvas, null, { filtering: 'nearest' }); - var sandbox = new GlslCanvas(canvas); - // randomize time - sandbox.timeLoad = Math.random() * 100000; - - window.sandbox = sandbox; // for access - - const shader_code = ` -precision highp float; -uniform vec3 color; -uniform sampler2D t; -uniform vec2 tiles; -uniform float u_time; -uniform vec2 u_resolution; -uniform float symbol_count; - -vec2 map_to_symbol_uv(vec2 image_coord, float symbol_index) { - float symbol_width = 1.0 / symbol_count; - float symbol_height = 1.0; - float symbol_x = symbol_index * symbol_width; - return vec2(symbol_x + image_coord.x * symbol_width, image_coord.y * symbol_height); -} - -vec2 tile_index(vec2 p, vec2 tiles) { - return vec2(floor(p.x * tiles.x), floor(p.y * tiles.y)); -} - -vec2 tilefy (vec2 p, vec2 tiles) { - return vec2(fract(p.x * tiles.x), fract(p.y * tiles.y)); -} - -float randf(float n) { - return fract(sin(n) * 43758.5453123); -} - -void main() { - vec2 uv = gl_FragCoord.xy / u_resolution.xy; - vec2 tile = tile_index(uv, tiles); - float tilerand = randf(tile.x + tile.y * tiles.x); - float v = tilerand + randf(floor(tilerand * u_time)); - float diff = fract(tilerand * u_time); - float s = floor(v * 9.0); - vec2 tp = tilefy(uv, tiles); - vec2 tp_center = vec2(0.5, 0.5); - float d = distance(tp, tp_center); - float scale = min(sin(diff * 3.1415) * 1.2, 1.0); - tp = tp_center + (tp - tp_center) / scale; - if (tp.x < 0.0 || tp.x > 1.0 || tp.y < 0.0 || tp.y > 1.0) { - gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); - return; - } - vec2 p = map_to_symbol_uv(tp, mod(s, symbol_count)); - vec4 c = texture2D(t, p); - c.rgb *= color; - c.a *= uv.y * 4.0; - gl_FragColor = vec4(c.rgb * c.a, c.a); -} -` - - sandbox.load(shader_code); - - sandbox.setUniform('t', "https://cdn.duckonaut.dev/assets/images/symbols.png"); - - sandbox.setUniform('color', 42 / 255, 42 / 255, 80 / 255); - - sandbox.setUniform('symbol_count', 9); - - sandbox.setUniform('tiles', Math.ceil(window.innerWidth / 32), Math.ceil(window.innerHeight / 64)); - - addEventListener('resize', function() { - window.sandbox_canvas.width = window.innerWidth; - window.sandbox_canvas.height = window.innerHeight / 2; - window.sandbox.setUniform('tiles', Math.ceil(window.innerWidth / 32), Math.ceil(window.innerHeight / 64)); - }); -}); - diff --git a/luax/public/assets/loose/github-yellow.svg b/luax/public/assets/loose/github-yellow.svg deleted file mode 100644 index f81e9b9..0000000 --- a/luax/public/assets/loose/github-yellow.svg +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - diff --git a/luax/public/assets/loose/itch-yellow.svg b/luax/public/assets/loose/itch-yellow.svg deleted file mode 100644 index 008c7b1..0000000 --- a/luax/public/assets/loose/itch-yellow.svg +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - diff --git a/luax/public/assets/loose/twitter-yellow.svg b/luax/public/assets/loose/twitter-yellow.svg deleted file mode 100644 index ec6168d..0000000 --- a/luax/public/assets/loose/twitter-yellow.svg +++ /dev/null @@ -1,91 +0,0 @@ - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/luax/public/assets/loose/web-yellow.svg b/luax/public/assets/loose/web-yellow.svg deleted file mode 100644 index b61babc..0000000 --- a/luax/public/assets/loose/web-yellow.svg +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - diff --git a/luax/public/assets/navbar.css b/luax/public/assets/navbar.css deleted file mode 100644 index 9120052..0000000 --- a/luax/public/assets/navbar.css +++ /dev/null @@ -1,278 +0,0 @@ -/* on big screens, navbar is fixed to the top of the screen */ -.navbar-container { - position: fixed; - /* gradient from top to bottom, full to transparent */ - top: 0; - width: 100%; - z-index: 9999; - margin: auto; -} - -/* on smaller ones, make it behave as a normal element laid out at the top of the page */ -@media (max-width: 1150px) { - .navbar-container { - position: static; - } -} - -.navbar { - padding-top: 20pt; - padding-bottom: 12pt; - align-items: center; - justify-content: center; - align-self: center; - background: linear-gradient(to bottom, var(--color-bg) 0%, var(--color-bg) 75%, rgba(0, 0, 0, 0) 100%); -} - -.navbar-icon { - font-family: Hack, monospace; - font-weight: 500; - color: #646cff; - font-size: 1.5em; - padding: 0.5em; - margin: 0.5em; -} - -.navbar { - align-self: center; - display: block; - width: 100%; - text-align: center; -} - -.navbar-item { - font-family: Hack, monospace; - font-size: 18pt; - text-align: center; - float: none; - clear: left; - display: inline-block; - margin: 0 0.25em; - transition: all 0.2s ease-in-out; -} - -.navbar-item:hover { - padding-left: 0.5em; - padding-right: 0.5em; - transition: all 0.2s ease-in-out; -} - -.navbar-current { - font-weight: bold; -} - -/* home */ - -.navbar-item-home:link { - color: #98C379; - text-decoration: none; -} - -.navbar-item-home:visited { - color: #98C379; - text-decoration: none; -} - -.navbar-item-home:hover { - color: #afd088; - text-decoration: underline; -} - -.navbar-item-home:active { - color: #afd088; - text-decoration: underline; -} - -/* games */ - -.navbar-item-games:link { - color: #e06c75; - text-decoration: none; -} - -.navbar-item-games:visited { - color: #e06c75; - text-decoration: none; -} - -.navbar-item-games:hover { - color: #ef7a80; - text-decoration: underline; -} - -.navbar-item-games:active { - color: #ef7a90; - text-decoration: underline; -} - -/* projects */ - -.navbar-item-projects:link { - color: #d19a66; - text-decoration: none; -} - -.navbar-item-projects:visited { - color: #d19a66; - text-decoration: none; -} - -.navbar-item-projects:hover { - color: #dfa873; - text-decoration: underline; -} - -.navbar-item-projects:active { - color: #dffa73; - text-decoration: underline; -} - -/* github */ - -.navbar-item-github:link { - color: #c678dd; - text-decoration: none; -} - -.navbar-item-github:visited { - color: #c678dd; - text-decoration: none; -} - -.navbar-item-github:hover { - color: #d286eb; - text-decoration: underline; -} - -.navbar-item-github:active { - color: #d286eb; - text-decoration: underline; -} - -/* itch */ - -.navbar-item-itch:link { - color: #e06c75; - text-decoration: none; -} - -.navbar-item-itch:visited { - color: #e06c75; - text-decoration: none; -} - -.navbar-item-itch:hover { - color: #ef7a80; - text-decoration: underline; -} - -.navbar-item-itch:active { - color: #ef7a90; - text-decoration: underline; -} - -/* twitter */ - -.navbar-item-twitter:link { - text-decoration: none; -} - -.navbar-item-twitter:visited { - text-decoration: none; -} - -.navbar-item-twitter:hover { - text-decoration: underline; -} - -.navbar-item-twitter:active { - text-decoration: underline; -} - -/* mastodon */ - -.navbar-item-mastodon:link { - color: #6a4ae0; - text-decoration: none; -} - -.navbar-item-mastodon:visited { - color: #6a4ae0; - text-decoration: none; -} - -.navbar-item-mastodon:hover { - color: #7a55e8; - text-decoration: underline; -} - -.navbar-item-mastodon:active { - color: #7a55e8; - text-decoration: underline; -} - -/* cohost */ - -.navbar-item-cohost:link { - color: #b63b73; - text-decoration: none; -} - -.navbar-item-cohost:visited { - color: #b63b73; - text-decoration: none; -} - -.navbar-item-cohost:hover { - color: #c54a7f; - text-decoration: underline; -} - -.navbar-item-cohost:active { - color: #c54a7f; - text-decoration: underline; -} - -/* tumblr */ - -.navbar-item-tumblr:link { - color: #00b8ff; - text-decoration: none; -} - -.navbar-item-tumblr:visited { - color: #00b8ff; - text-decoration: none; -} - -.navbar-item-tumblr:hover { - color: #00c5ff; - text-decoration: underline; -} - -.navbar-item-tumblr:active { - color: #00c5ff; - text-decoration: underline; -} - -/* contact */ - -.navbar-item-contact:link { - color: #e5c07b; - text-decoration: none; -} - -.navbar-item-contact:visited { - color: #e5c07b; - text-decoration: none; -} - -.navbar-item-contact:hover { - color: #f2ce89; - text-decoration: underline; -} - -.navbar-item-contact:active { - color: #f2ce89; - text-decoration: underline; -} diff --git a/luax/public/assets/project.css b/luax/public/assets/project.css deleted file mode 100644 index 742c072..0000000 --- a/luax/public/assets/project.css +++ /dev/null @@ -1,126 +0,0 @@ -.icon { - display: inline; - width: 32px; - height: 32px; - transition: 0.3s; - transform-origin: center; - filter: brightness(70%) -} - -.icon:hover { - filter: brightness(80%); -} - -.icon-row { - width: 320px; - height: 36px; -} - -.icon-container { - height: 36px; - float: right; -} - -.icon-text { - width: auto; - height: 36px; - line-height: 36px; - float: left; - vertical-align: middle; - margin-right: 8px; - color: #5c6370; -} - -.project { - display: table; - align-self: center; - clear: both; - margin-top: 24pt; -} - -.project-info { - font-size: 14pt; -} - -.project-description { - font-size: 16pt; - text-align: justify; -} - -.project-image-gallery { - margin-left: 24px; - margin-right: 10px; -} - -@media (max-width: 1150px) { - .project-title { - text-align: center; - font-size: 24pt; - margin-bottom: 8px; - text-decoration-line: underline; - } - - .project-image-gallery { - margin: 60px auto; - width: 330px; - height: 220px; - } - - .project-info { - float: none; - width: 90%; - margin: auto; - } - - .project-description { - font-size: 14pt; - } -} - -@media (max-width: 1150px) and (min-width: 480px) { - .project-title { - text-align: center; - font-size: 24pt; - margin-bottom: 8px; - text-decoration-line: underline; - } - .project-image-gallery { - margin: 60px auto; - width: 480px; - height: 360px; - } - - .project-info { - float: none; - width: 90%; - margin: auto; - } - - .project-description { - font-size: 14pt; - } -} - -@media (min-width: 1150px) { - .project-title { - text-align: left; - font-size: 32pt; - margin-bottom: 8px; - text-decoration-line: underline; - } - .project-image-gallery { - float: right; - width: 480px; - height: 360px; - } - .project-info { - float: left; - max-width: 600px; - } - - .project-description { - font-size: 16pt; - } -} - - diff --git a/luax/public/assets/projects/hyprload/logo.png b/luax/public/assets/projects/hyprload/logo.png deleted file mode 100644 index 92fb9ce..0000000 Binary files a/luax/public/assets/projects/hyprload/logo.png and /dev/null differ diff --git a/luax/public/assets/projects/spirit-mod/demo1.gif b/luax/public/assets/projects/spirit-mod/demo1.gif deleted file mode 100644 index 324de0c..0000000 Binary files a/luax/public/assets/projects/spirit-mod/demo1.gif and /dev/null differ diff --git a/luax/public/assets/projects/spirit-mod/demo2.gif b/luax/public/assets/projects/spirit-mod/demo2.gif deleted file mode 100644 index 4e87fd8..0000000 Binary files a/luax/public/assets/projects/spirit-mod/demo2.gif and /dev/null differ diff --git a/luax/public/assets/projects/spirit-mod/thumbnail.png b/luax/public/assets/projects/spirit-mod/thumbnail.png deleted file mode 100644 index 55f8d7e..0000000 Binary files a/luax/public/assets/projects/spirit-mod/thumbnail.png and /dev/null differ diff --git a/luax/public/assets/projects/spirit-mod/thumbnail.webp b/luax/public/assets/projects/spirit-mod/thumbnail.webp deleted file mode 100644 index 0250f72..0000000 Binary files a/luax/public/assets/projects/spirit-mod/thumbnail.webp and /dev/null differ diff --git a/luax/reluax.luax b/luax/reluax.luax deleted file mode 100644 index 1cfc04c..0000000 --- a/luax/reluax.luax +++ /dev/null @@ -1,72 +0,0 @@ -local Common = require("common") -local Navbar = require("components.navbar") -local AnimatedBg = require("components.animated_bg") - -local Home = require("pages.home") -local Projects = require("pages.projects") -local Games = require("pages.games") -local Contact = require("pages.contact") -local Blogpost = require("pages.blogpost") -local Blog = require("pages.blog") -local NotFound = require("not_found") - -local blogposts = require("blog.posts") - -math.randomseed(os.time()) - -local function wrap_component(component) - return - - -
    -
    - {$ component $} - -
    -
    -end - -local function route(path) - if path == '/' then - return 200, wrap_component() - elseif path == '/projects' then - return 200, wrap_component() - elseif path == '/games' then - return 200, wrap_component() - elseif path == '/contact' then - return 200, wrap_component() - elseif path == '/blog' then - return 200, wrap_component() - elseif reluax.url_matches('/blog/{slug}', path) then - local slug = reluax.url_extract('/blog/{slug}', path).slug - -- blogposts is a list of blogposts, with the slug a property of each - local post = nil - for _, p in ipairs(blogposts) do - if p.slug == slug then - post = p - break - end - end - if post then - local article = - - -
    -
    - - -
    -
    - - return 200, article - end - end - - return 404, NotFound() -end - -return { - name = "duckonaut.dev", - route = route -} - diff --git a/not_found.luax b/not_found.luax new file mode 100644 index 0000000..9ad3806 --- /dev/null +++ b/not_found.luax @@ -0,0 +1,3 @@ +return function() + return "404 Not Found" +end diff --git a/pages/blog.luax b/pages/blog.luax new file mode 100644 index 0000000..a729b63 --- /dev/null +++ b/pages/blog.luax @@ -0,0 +1,30 @@ +local blogposts = require('blog.posts') + +return function() + local blogpost_html = {} + for _, post in ipairs(blogposts) do + blogpost_html[#blogpost_html + 1] = +
    +
    +
    + {$ post.title $} +
    + +
    +
    + {$ post.description $} +
    +
    + end + return +
    +
    + Blog +
    +
    + {$ blogpost_html $} +
    +
    +end diff --git a/pages/blogpost.luax b/pages/blogpost.luax new file mode 100644 index 0000000..e7d7b86 --- /dev/null +++ b/pages/blogpost.luax @@ -0,0 +1,23 @@ +local blog_cache = require('blog.cache') + +return function(props) + local post = props.attrs.post + local content = blog_cache(post) + return +
    +
    +
    +
    + {$ post.title $} +
    + +
    + {$ post.description $} +
    +
    {$ content $}
    +
    +
    +
    +end diff --git a/pages/contact.luax b/pages/contact.luax new file mode 100644 index 0000000..95ad0fb --- /dev/null +++ b/pages/contact.luax @@ -0,0 +1,31 @@ +return function() + return +
    +
    + Contact +
    +
    +
    + Discord +
    + + usually the fastest way to reach me +
    + EchoDuck#7866 + +
    + Matrix +
    + for the FOSS folks +
    + @echoduck:matrix.org + +
    + Email +
    + I check my email daily +
    + duckonaut@gmail.com +
    +
    +end diff --git a/pages/games.luax b/pages/games.luax new file mode 100644 index 0000000..59a68b4 --- /dev/null +++ b/pages/games.luax @@ -0,0 +1,25 @@ +local Project = require("components.project") + +local games = require("data.games") + +local game_display = {} +for _, game in ipairs(games) do + game_display[#game_display + 1] = +end + +return function() + return +
    +
    + Game Projects +
    +
    + games I've worked on +
    + +
    + {$ game_display $} +
    +
    +end + diff --git a/pages/home.luax b/pages/home.luax new file mode 100644 index 0000000..ed1b464 --- /dev/null +++ b/pages/home.luax @@ -0,0 +1,33 @@ +return function() + return
    +
    + Stanisław Zagórowski +
    +
    + aka Duckonaut, EchoDuck +
    + + +
    +

    + Hello! I'm a programmer, game designer, and artist. I'm currently a + student at the Warsaw University of Technology, studying Computer Science. + A list of the games I've worked on is in the + games + section. Other projects I've been a part of are available in the + projects + section. I also enjoy some lower-level programming and learning about the + inner workings of various systems. The main technologies I use are + C#, Unity, + Python, Rust + and C although I'm also familiar with + Godot, C++ + , Java and some + JavaScript and TypeScript. +

    +
    +
    +end diff --git a/pages/projects.luax b/pages/projects.luax new file mode 100644 index 0000000..219f45d --- /dev/null +++ b/pages/projects.luax @@ -0,0 +1,24 @@ +local Project = require("components.project") + +local projects = require("data.projects") + +local project_display = {} +for _, project in ipairs(projects) do + project_display[#project_display + 1] = +end + +return function() + return +
    +
    + Other Projects +
    +
    + projects that are not games +
    + +
    + {$ project_display $} +
    +
    +end diff --git a/public/CNAME b/public/CNAME new file mode 100644 index 0000000..a2fa953 --- /dev/null +++ b/public/CNAME @@ -0,0 +1 @@ +cdn.duckonaut.dev diff --git a/public/assets/app.css b/public/assets/app.css new file mode 100644 index 0000000..c98e9b0 --- /dev/null +++ b/public/assets/app.css @@ -0,0 +1,171 @@ +#app-main { + font-family: 'Hack', monospace; + max-width: 1280px; + margin: 0 auto; + padding: 1rem 1rem 1rem 1rem; + text-align: center; +} + +#app { + margin: 0 auto; + text-align: center; + align-content: center; + justify-content: center; +} + +.logo { + height: 6em; + padding: 1.5em; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.preact:hover { + filter: drop-shadow(0 0 2em #673ab8aa); +} + +/* fade in */ +@keyframes fade-in { + 0% { + opacity: 0; + transform: translateY(1em); + } + 60% { + transform: translateY(-0.2em); + } + 100% { + opacity: 1; + transform: translateY(0); + } +} + +.main-info > * { + animation: fade-in 0.2s ease-in; +} + +.main-info { + text-align: center; +} + +@media (min-width: 1150px) { + .main-info { + margin-top: 72px; + } +} + +.main-info-image { + width: 128px; + height: 128px; + border-radius: 100%; + margin: 6px 6px 6px 6px; +} + +.main-info-title { + font-size: 42pt; +} + +.main-info-subtitle { + font-size: 26pt; + font-style: italic; + color: #5c6370; +} + +.quiet { + color: var(--color-gray); + font-style: italic; +} + +.section { + font-size: 22pt; +} + +.highlight { + color: #e5c07b; +} + +.back-shadows { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: -1; + background: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, var(--color-bg) 10%, var(--color-bg) 90%, rgba(0, 0, 0, 0) 100%); +} + +#animated-bg { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -10; +} + + +@media (max-width: 480px) { + .back-shadows { + margin: auto; + max-width: 80%; + } + .main-info-title { + font-size: 32pt; + } + + .main-info-subtitle { + font-size: 18pt; + } +} + +@media (max-width: 1150px) and (min-width: 480px) { + .back-shadows { + margin: auto; + max-width: 60%; + } + .main-info-title { + font-size: 42pt; + } + + .main-info-subtitle { + font-size: 26pt; + } +} + +@media (min-width: 1150px) { + .back-shadows { + margin: auto; + max-width: 50%; + } + .main-info-title { + font-size: 42pt; + } + + .main-info-subtitle { + font-size: 26pt; + } +} + + +@media (max-width: 480px) { + .about-paragraph { + margin: auto; + font-size: 16pt; + max-width: 90%; + } +} + +@media (max-width: 1150px) and (min-width: 480px) { + .about-paragraph { + margin: auto; + font-size: 18pt; + max-width: 80%; + } +} + +@media (min-width: 1150px) { + .about-paragraph { + margin: auto; + font-size: 18pt; + max-width: 60%; + } +} diff --git a/public/assets/blog.css b/public/assets/blog.css new file mode 100644 index 0000000..9d0180c --- /dev/null +++ b/public/assets/blog.css @@ -0,0 +1,108 @@ +.code-block { + text-align: left; + background-color: #161f30; + border: 2px solid #2a3a4f; + border-radius: 8px; + padding: 2px; + margin: 8px; + font-family: Hack, monospace; + font-size: 10pt; + overflow-x: auto; +} + +code { + font-family: monospace; + font-size: 14pt; +} + +blockquote { + border-left: 4px solid #2a3a4f; + padding-left: 8px; + margin-left: 8px; + font-style: italic; +} + +td.line-number { + text-align: right; + padding-right: 5px; + color: #647089; +} + +.post-description { + text-align: justify; + font-style: italic; + color: #647089; + margin: 0 auto; + max-width: 800px; + padding: 0 10px; +} + +.post-date { + text-align: right; + font-style: italic; + margin: 0 auto; + max-width: 800px; + padding: 0 10px; +} + +.post-content { + text-align: justify; + margin: 0 auto; + max-width: 800px; +} + +.post-content img { + max-width: 100%; + height: auto; + border-radius: 8px; + border: 2px solid #2a3a4f; + margin: auto 0; +} + +.post-content p:has(img) { + text-align: center; +} + +.blog-post-header { + align-items: center; + text-align: center; + margin: 0 auto; + max-width: 800px; + padding: 0 10px; +} + +.blog-post-title { + font-size: 24pt; + font-weight: bold; + margin: 0; + padding: 0; +} + +.blog-post-date { + font-size: 12pt; + font-style: italic; + color: #647089; + margin: 0; + padding: 0; +} + +@media (max-width: 480px) { + .post-content { + margin: auto; + max-width: 96%; + } +} + +@media (max-width: 1150px) and (min-width: 480px) { + .post-content { + margin: auto; + max-width: 80%; + } +} + +@media (min-width: 1150px) { + .post-content { + margin: auto; + max-width: 60%; + } +} diff --git a/public/assets/blog/building-reluax/cli.png b/public/assets/blog/building-reluax/cli.png new file mode 100644 index 0000000..097957f Binary files /dev/null and b/public/assets/blog/building-reluax/cli.png differ diff --git a/public/assets/blog/building-reluax/first-demo.png b/public/assets/blog/building-reluax/first-demo.png new file mode 100644 index 0000000..81c212c Binary files /dev/null and b/public/assets/blog/building-reluax/first-demo.png differ diff --git a/public/assets/blog/building-reluax/second-demo.png b/public/assets/blog/building-reluax/second-demo.png new file mode 100644 index 0000000..52e9a10 Binary files /dev/null and b/public/assets/blog/building-reluax/second-demo.png differ diff --git a/public/assets/carousel.css b/public/assets/carousel.css new file mode 100644 index 0000000..2465370 --- /dev/null +++ b/public/assets/carousel.css @@ -0,0 +1,69 @@ +.carousel-content { + align-self: center; + max-height: 100%; + white-space: nowrap; + -ms-overflow-style: none; /* hide scrollbar in IE and Edge */ + scrollbar-width: none; /* hide scrollbar in Firefox */ +} + +.carousel-item { + width: 100%; + flex-shrink: 0; + height: auto; + float: left; + text-align: center; + transition: all 250ms linear; + white-space: nowrap; + vertical-align: middle; +} + +.slick-slider { + margin: 24px; +} + +.slick-slide { + margin: 0 auto; + border-radius: 10px; + text-align: center; + float: none !important; + display: inline-block !important; + vertical-align: middle; + min-height: 1px; +} + +/* .carousel-item > * { */ +/* border-radius: 10px; */ +/* vertical-align: middle; */ +/* width: 100%; */ +/* height: auto; */ +/* } */ + +.slick-dots li button::before { + color: var(--color-fg) !important; +} + +@media (max-width: 1150px) { + .carousel-wrapper { + margin: 60px auto; + width: 330px; + height: 220px; + } +} + +@media (max-width: 1150px) and (min-width: 480px) { + .carousel-wrapper { + margin: 60px auto; + width: 480px; + height: 360px; + } +} + +@media (min-width: 1150px) { + .carousel-wrapper { + float: right; + width: 480px; + height: 360px; + } +} + + diff --git a/public/assets/contact.css b/public/assets/contact.css new file mode 100644 index 0000000..93b63bd --- /dev/null +++ b/public/assets/contact.css @@ -0,0 +1,22 @@ +.contact-container { + width: 100%; + max-width: 1120px; + margin: auto; + margin-top: 24pt; + margin-bottom: 48pt; + align-items: center; + justify-content: center; + align-self: center; + font-size: 18pt; +} + +.contact { + margin-top: 24pt; + margin-bottom: 8pt; + font-size: 20pt; + color: #98C379; +} + +.highlight { + color: #e5c07b; +} diff --git a/public/assets/games/bumbling-builders/screenshot1.png b/public/assets/games/bumbling-builders/screenshot1.png new file mode 100644 index 0000000..7a8a2db Binary files /dev/null and b/public/assets/games/bumbling-builders/screenshot1.png differ diff --git a/public/assets/games/bumbling-builders/screenshot2.png b/public/assets/games/bumbling-builders/screenshot2.png new file mode 100644 index 0000000..5eb0a1f Binary files /dev/null and b/public/assets/games/bumbling-builders/screenshot2.png differ diff --git a/public/assets/games/bumbling-builders/screenshot3.png b/public/assets/games/bumbling-builders/screenshot3.png new file mode 100644 index 0000000..41f65ac Binary files /dev/null and b/public/assets/games/bumbling-builders/screenshot3.png differ diff --git a/public/assets/games/bumbling-builders/thumbnail.png b/public/assets/games/bumbling-builders/thumbnail.png new file mode 100644 index 0000000..1c723a2 Binary files /dev/null and b/public/assets/games/bumbling-builders/thumbnail.png differ diff --git a/public/assets/games/dicemask/thumbnail.png b/public/assets/games/dicemask/thumbnail.png new file mode 100644 index 0000000..f9bc9ee Binary files /dev/null and b/public/assets/games/dicemask/thumbnail.png differ diff --git a/public/assets/games/greachers/screenshot1.png b/public/assets/games/greachers/screenshot1.png new file mode 100644 index 0000000..444106e Binary files /dev/null and b/public/assets/games/greachers/screenshot1.png differ diff --git a/public/assets/games/greachers/screenshot2.png b/public/assets/games/greachers/screenshot2.png new file mode 100644 index 0000000..e968c4f Binary files /dev/null and b/public/assets/games/greachers/screenshot2.png differ diff --git a/public/assets/games/greachers/screenshot3.png b/public/assets/games/greachers/screenshot3.png new file mode 100644 index 0000000..6433fb6 Binary files /dev/null and b/public/assets/games/greachers/screenshot3.png differ diff --git a/public/assets/games/greachers/title.png b/public/assets/games/greachers/title.png new file mode 100644 index 0000000..cdb3e67 Binary files /dev/null and b/public/assets/games/greachers/title.png differ diff --git a/public/assets/games/loud/screenshot1.jpg b/public/assets/games/loud/screenshot1.jpg new file mode 100644 index 0000000..8192c15 Binary files /dev/null and b/public/assets/games/loud/screenshot1.jpg differ diff --git a/public/assets/games/loud/screenshot2.jpg b/public/assets/games/loud/screenshot2.jpg new file mode 100644 index 0000000..88a1ffe Binary files /dev/null and b/public/assets/games/loud/screenshot2.jpg differ diff --git a/public/assets/games/loud/screenshot3.jpg b/public/assets/games/loud/screenshot3.jpg new file mode 100644 index 0000000..dfbe555 Binary files /dev/null and b/public/assets/games/loud/screenshot3.jpg differ diff --git a/public/assets/games/loud/thumbnail.jpg b/public/assets/games/loud/thumbnail.jpg new file mode 100644 index 0000000..9ec9764 Binary files /dev/null and b/public/assets/games/loud/thumbnail.jpg differ diff --git a/public/assets/games/miro/screenshot1.png b/public/assets/games/miro/screenshot1.png new file mode 100644 index 0000000..5ba2365 Binary files /dev/null and b/public/assets/games/miro/screenshot1.png differ diff --git a/public/assets/games/miro/screenshot2.png b/public/assets/games/miro/screenshot2.png new file mode 100644 index 0000000..706e47a Binary files /dev/null and b/public/assets/games/miro/screenshot2.png differ diff --git a/public/assets/games/miro/screenshot3.png b/public/assets/games/miro/screenshot3.png new file mode 100644 index 0000000..237be9c Binary files /dev/null and b/public/assets/games/miro/screenshot3.png differ diff --git a/public/assets/games/miro/thumbnail.png b/public/assets/games/miro/thumbnail.png new file mode 100644 index 0000000..152acfc Binary files /dev/null and b/public/assets/games/miro/thumbnail.png differ diff --git a/public/assets/games/mr-final-boss/screenshot1.png b/public/assets/games/mr-final-boss/screenshot1.png new file mode 100644 index 0000000..d13f639 Binary files /dev/null and b/public/assets/games/mr-final-boss/screenshot1.png differ diff --git a/public/assets/games/mr-final-boss/screenshot2.png b/public/assets/games/mr-final-boss/screenshot2.png new file mode 100644 index 0000000..6b07d1c Binary files /dev/null and b/public/assets/games/mr-final-boss/screenshot2.png differ diff --git a/public/assets/games/mr-final-boss/screenshot3.png b/public/assets/games/mr-final-boss/screenshot3.png new file mode 100644 index 0000000..bf6d965 Binary files /dev/null and b/public/assets/games/mr-final-boss/screenshot3.png differ diff --git a/public/assets/games/mr-final-boss/screenshot4.png b/public/assets/games/mr-final-boss/screenshot4.png new file mode 100644 index 0000000..c6b4cbe Binary files /dev/null and b/public/assets/games/mr-final-boss/screenshot4.png differ diff --git a/public/assets/games/mr-final-boss/thumbnail.png b/public/assets/games/mr-final-boss/thumbnail.png new file mode 100644 index 0000000..024dab4 Binary files /dev/null and b/public/assets/games/mr-final-boss/thumbnail.png differ diff --git a/public/assets/games/riff-n-rage/screenshot1.png b/public/assets/games/riff-n-rage/screenshot1.png new file mode 100644 index 0000000..6a9b075 Binary files /dev/null and b/public/assets/games/riff-n-rage/screenshot1.png differ diff --git a/public/assets/games/riff-n-rage/screenshot2.png b/public/assets/games/riff-n-rage/screenshot2.png new file mode 100644 index 0000000..5b68901 Binary files /dev/null and b/public/assets/games/riff-n-rage/screenshot2.png differ diff --git a/public/assets/games/riff-n-rage/thumbnail.png b/public/assets/games/riff-n-rage/thumbnail.png new file mode 100644 index 0000000..fa8dcad Binary files /dev/null and b/public/assets/games/riff-n-rage/thumbnail.png differ diff --git a/public/assets/games/survive-the-temple/cover.png b/public/assets/games/survive-the-temple/cover.png new file mode 100644 index 0000000..5394eeb Binary files /dev/null and b/public/assets/games/survive-the-temple/cover.png differ diff --git a/public/assets/games/survive-the-temple/screenshot.png b/public/assets/games/survive-the-temple/screenshot.png new file mode 100644 index 0000000..92b8ba9 Binary files /dev/null and b/public/assets/games/survive-the-temple/screenshot.png differ diff --git a/public/assets/games/thermal-trail/gameart.png b/public/assets/games/thermal-trail/gameart.png new file mode 100644 index 0000000..34c82bc Binary files /dev/null and b/public/assets/games/thermal-trail/gameart.png differ diff --git a/public/assets/games/thermal-trail/screenshot1.png b/public/assets/games/thermal-trail/screenshot1.png new file mode 100644 index 0000000..4b940aa Binary files /dev/null and b/public/assets/games/thermal-trail/screenshot1.png differ diff --git a/public/assets/games/thermal-trail/screenshot2.png b/public/assets/games/thermal-trail/screenshot2.png new file mode 100644 index 0000000..0594eb9 Binary files /dev/null and b/public/assets/games/thermal-trail/screenshot2.png differ diff --git a/public/assets/games/thermal-trail/screenshot3.png b/public/assets/games/thermal-trail/screenshot3.png new file mode 100644 index 0000000..b5a4a6d Binary files /dev/null and b/public/assets/games/thermal-trail/screenshot3.png differ diff --git a/public/assets/games/thermal-trail/screenshot4.png b/public/assets/games/thermal-trail/screenshot4.png new file mode 100644 index 0000000..1356226 Binary files /dev/null and b/public/assets/games/thermal-trail/screenshot4.png differ diff --git a/public/assets/games/thermal-trail/thumbnail.png b/public/assets/games/thermal-trail/thumbnail.png new file mode 100644 index 0000000..d779c51 Binary files /dev/null and b/public/assets/games/thermal-trail/thumbnail.png differ diff --git a/public/assets/games/third-party/screenshot1.jpg b/public/assets/games/third-party/screenshot1.jpg new file mode 100644 index 0000000..6052ea2 Binary files /dev/null and b/public/assets/games/third-party/screenshot1.jpg differ diff --git a/public/assets/games/third-party/screenshot2.jpg b/public/assets/games/third-party/screenshot2.jpg new file mode 100644 index 0000000..7a16edf Binary files /dev/null and b/public/assets/games/third-party/screenshot2.jpg differ diff --git a/public/assets/games/third-party/screenshot3.jpg b/public/assets/games/third-party/screenshot3.jpg new file mode 100644 index 0000000..57e2d5b Binary files /dev/null and b/public/assets/games/third-party/screenshot3.jpg differ diff --git a/public/assets/games/third-party/screenshot4.jpg b/public/assets/games/third-party/screenshot4.jpg new file mode 100644 index 0000000..331594a Binary files /dev/null and b/public/assets/games/third-party/screenshot4.jpg differ diff --git a/public/assets/games/third-party/thumbnail.png b/public/assets/games/third-party/thumbnail.png new file mode 100644 index 0000000..c3cd158 Binary files /dev/null and b/public/assets/games/third-party/thumbnail.png differ diff --git a/public/assets/games/wuzmag/screenshot1.png b/public/assets/games/wuzmag/screenshot1.png new file mode 100644 index 0000000..186be8c Binary files /dev/null and b/public/assets/games/wuzmag/screenshot1.png differ diff --git a/public/assets/games/wuzmag/screenshot2.png b/public/assets/games/wuzmag/screenshot2.png new file mode 100644 index 0000000..683d7ee Binary files /dev/null and b/public/assets/games/wuzmag/screenshot2.png differ diff --git a/public/assets/games/wuzmag/screenshot3.png b/public/assets/games/wuzmag/screenshot3.png new file mode 100644 index 0000000..b5398bb Binary files /dev/null and b/public/assets/games/wuzmag/screenshot3.png differ diff --git a/public/assets/games/wuzmag/thumbnail.png b/public/assets/games/wuzmag/thumbnail.png new file mode 100644 index 0000000..0e6dea1 Binary files /dev/null and b/public/assets/games/wuzmag/thumbnail.png differ diff --git a/public/assets/games/your-left/screenshot1.jpg b/public/assets/games/your-left/screenshot1.jpg new file mode 100644 index 0000000..e76861a Binary files /dev/null and b/public/assets/games/your-left/screenshot1.jpg differ diff --git a/public/assets/games/your-left/screenshot2.jpg b/public/assets/games/your-left/screenshot2.jpg new file mode 100644 index 0000000..9e0f674 Binary files /dev/null and b/public/assets/games/your-left/screenshot2.jpg differ diff --git a/public/assets/games/your-left/screenshot3.jpg b/public/assets/games/your-left/screenshot3.jpg new file mode 100644 index 0000000..d025ec1 Binary files /dev/null and b/public/assets/games/your-left/screenshot3.jpg differ diff --git a/public/assets/games/your-left/thumbnail.png b/public/assets/games/your-left/thumbnail.png new file mode 100644 index 0000000..95f5c17 Binary files /dev/null and b/public/assets/games/your-left/thumbnail.png differ diff --git a/public/assets/images/android.png b/public/assets/images/android.png new file mode 100644 index 0000000..d1cef88 Binary files /dev/null and b/public/assets/images/android.png differ diff --git a/public/assets/images/android.svg b/public/assets/images/android.svg new file mode 100644 index 0000000..910aaab --- /dev/null +++ b/public/assets/images/android.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/public/assets/images/cpp.png b/public/assets/images/cpp.png new file mode 100644 index 0000000..ab883f7 Binary files /dev/null and b/public/assets/images/cpp.png differ diff --git a/public/assets/images/cpp.svg b/public/assets/images/cpp.svg new file mode 100644 index 0000000..f306183 --- /dev/null +++ b/public/assets/images/cpp.svg @@ -0,0 +1,41 @@ + + + + + + diff --git a/public/assets/images/csharp.png b/public/assets/images/csharp.png new file mode 100644 index 0000000..065aa53 Binary files /dev/null and b/public/assets/images/csharp.png differ diff --git a/public/assets/images/csharp.svg b/public/assets/images/csharp.svg new file mode 100644 index 0000000..77d5b38 --- /dev/null +++ b/public/assets/images/csharp.svg @@ -0,0 +1,58 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/public/assets/images/duckpfp.png b/public/assets/images/duckpfp.png new file mode 100644 index 0000000..bdad92c Binary files /dev/null and b/public/assets/images/duckpfp.png differ diff --git a/public/assets/images/favicon.png b/public/assets/images/favicon.png new file mode 100644 index 0000000..3cfd37a Binary files /dev/null and b/public/assets/images/favicon.png differ diff --git a/public/assets/images/favicon.svg b/public/assets/images/favicon.svg new file mode 100644 index 0000000..3614084 --- /dev/null +++ b/public/assets/images/favicon.svg @@ -0,0 +1,54 @@ + + + + diff --git a/public/assets/images/github.png b/public/assets/images/github.png new file mode 100644 index 0000000..0954581 Binary files /dev/null and b/public/assets/images/github.png differ diff --git a/public/assets/images/github.svg b/public/assets/images/github.svg new file mode 100644 index 0000000..11f84aa --- /dev/null +++ b/public/assets/images/github.svg @@ -0,0 +1,66 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/public/assets/images/godot.png b/public/assets/images/godot.png new file mode 100644 index 0000000..4f2bc22 Binary files /dev/null and b/public/assets/images/godot.png differ diff --git a/public/assets/images/godot.svg b/public/assets/images/godot.svg new file mode 100644 index 0000000..6a2176a --- /dev/null +++ b/public/assets/images/godot.svg @@ -0,0 +1,64 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/public/assets/images/html5.png b/public/assets/images/html5.png new file mode 100644 index 0000000..e0dcb1f Binary files /dev/null and b/public/assets/images/html5.png differ diff --git a/public/assets/images/html5.svg b/public/assets/images/html5.svg new file mode 100644 index 0000000..70959e4 --- /dev/null +++ b/public/assets/images/html5.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/public/assets/images/hyprland.png b/public/assets/images/hyprland.png new file mode 100644 index 0000000..36d8162 Binary files /dev/null and b/public/assets/images/hyprland.png differ diff --git a/public/assets/images/hyprland.svg b/public/assets/images/hyprland.svg new file mode 100644 index 0000000..1f03c7e --- /dev/null +++ b/public/assets/images/hyprland.svg @@ -0,0 +1,50 @@ + + + + + + + + + + diff --git a/public/assets/images/linux.png b/public/assets/images/linux.png new file mode 100644 index 0000000..492d0bf Binary files /dev/null and b/public/assets/images/linux.png differ diff --git a/public/assets/images/linux.svg b/public/assets/images/linux.svg new file mode 100644 index 0000000..cab00df --- /dev/null +++ b/public/assets/images/linux.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/public/assets/images/macos.png b/public/assets/images/macos.png new file mode 100644 index 0000000..1f4625c Binary files /dev/null and b/public/assets/images/macos.png differ diff --git a/public/assets/images/macos.svg b/public/assets/images/macos.svg new file mode 100644 index 0000000..ec4dc0c --- /dev/null +++ b/public/assets/images/macos.svg @@ -0,0 +1,39 @@ + + + + + + diff --git a/public/assets/images/monogame.png b/public/assets/images/monogame.png new file mode 100644 index 0000000..8648bcc Binary files /dev/null and b/public/assets/images/monogame.png differ diff --git a/public/assets/images/pfp.png b/public/assets/images/pfp.png new file mode 100644 index 0000000..a18b664 Binary files /dev/null and b/public/assets/images/pfp.png differ diff --git a/public/assets/images/placeholder.png b/public/assets/images/placeholder.png new file mode 100644 index 0000000..97084c7 Binary files /dev/null and b/public/assets/images/placeholder.png differ diff --git a/public/assets/images/playstation.png b/public/assets/images/playstation.png new file mode 100644 index 0000000..dd22b43 Binary files /dev/null and b/public/assets/images/playstation.png differ diff --git a/public/assets/images/playstation.svg b/public/assets/images/playstation.svg new file mode 100644 index 0000000..5d56897 --- /dev/null +++ b/public/assets/images/playstation.svg @@ -0,0 +1,61 @@ + + + + + + + image/svg+xml + + + + + + + + + diff --git a/public/assets/images/switch.png b/public/assets/images/switch.png new file mode 100644 index 0000000..723817e Binary files /dev/null and b/public/assets/images/switch.png differ diff --git a/public/assets/images/switch.svg b/public/assets/images/switch.svg new file mode 100644 index 0000000..4d891ae --- /dev/null +++ b/public/assets/images/switch.svg @@ -0,0 +1,43 @@ + + + + + + diff --git a/public/assets/images/symbols.png b/public/assets/images/symbols.png new file mode 100644 index 0000000..3d0e9aa Binary files /dev/null and b/public/assets/images/symbols.png differ diff --git a/public/assets/images/thumbnail.png b/public/assets/images/thumbnail.png new file mode 100644 index 0000000..f343a8b Binary files /dev/null and b/public/assets/images/thumbnail.png differ diff --git a/public/assets/images/thumbnail.svg b/public/assets/images/thumbnail.svg new file mode 100644 index 0000000..3783fe1 --- /dev/null +++ b/public/assets/images/thumbnail.svg @@ -0,0 +1,70 @@ + + + +duckonaut.dev diff --git a/public/assets/images/thumbnail.zvg b/public/assets/images/thumbnail.zvg new file mode 100644 index 0000000..4d675af --- /dev/null +++ b/public/assets/images/thumbnail.zvg @@ -0,0 +1,67 @@ + + + +duckonaut.dev diff --git a/public/assets/images/unity.png b/public/assets/images/unity.png new file mode 100644 index 0000000..5fea459 Binary files /dev/null and b/public/assets/images/unity.png differ diff --git a/public/assets/images/unity.svg b/public/assets/images/unity.svg new file mode 100644 index 0000000..b9d1859 --- /dev/null +++ b/public/assets/images/unity.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/public/assets/images/windows.png b/public/assets/images/windows.png new file mode 100644 index 0000000..a09553e Binary files /dev/null and b/public/assets/images/windows.png differ diff --git a/public/assets/images/windows.svg b/public/assets/images/windows.svg new file mode 100644 index 0000000..cb347b6 --- /dev/null +++ b/public/assets/images/windows.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/public/assets/images/xamarin.png b/public/assets/images/xamarin.png new file mode 100644 index 0000000..91ce4ac Binary files /dev/null and b/public/assets/images/xamarin.png differ diff --git a/public/assets/images/xbox.png b/public/assets/images/xbox.png new file mode 100644 index 0000000..9a439ba Binary files /dev/null and b/public/assets/images/xbox.png differ diff --git a/public/assets/images/xbox.svg b/public/assets/images/xbox.svg new file mode 100644 index 0000000..13d7bdd --- /dev/null +++ b/public/assets/images/xbox.svg @@ -0,0 +1,74 @@ + + + + + Xbox Logo + + + + image/svg+xml + + Xbox Logo + + + + + + + + + diff --git a/public/assets/index.css b/public/assets/index.css new file mode 100644 index 0000000..9941a36 --- /dev/null +++ b/public/assets/index.css @@ -0,0 +1,108 @@ +:root { + font-weight: 400; + + color-scheme: dark; + color: var(--color-fg); + background-color: var(--color-bg); + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + + /* variables */ + --color-bg: #161f30; + --color-fg: #a3b3d9; + --color-search-blue: #083C5A; + --color-white: #a3b3d9; + --color-gray: #647089; + --color-dark-gray: #3e4451; + --color-context: #6b737f; + --color-light-gray: #c8ccd4; + --color-tree-gray: #1b1f27; + --color-blue: #6080dd; + --color-vivid-blue: #61afef; + --color-dark-blue: #223E55; + --color-folder-blue: #61afef; + --color-light-blue: #a3b3d9; + --color-green: #449871; + --color-cyan: #56b6c2; + --color-light-green: #98c379; + --color-red: #c13251; + --color-orange: #c18a56; + --color-light-red: #e06c75; + --color-yellow: #e5c07b; + --color-purple: #604bd8; + --color-magenta: #b071fc; +} + +a:link { + font-weight: 500; + color: #4fc1ff; + text-decoration: inherit; +} + +a:visited { + color: #4fc1ff; +} + +a:hover { + color: #60d2ff; +} + +a:active { + color: #66d8ff; +} + + +body { + margin: 0; + /* display: flex; */ + /* place-items: center; */ + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +span { + display: inline; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +.hack { + font-family: Hack, monospace; + font-weight: 500; +} + +.grow { + transition: all .2s ease-in-out; + transform: scale(1); +} + +.grow:hover { + transition: all .2s ease-in-out; + transform: scale(1.1); +} diff --git a/public/assets/index.js b/public/assets/index.js new file mode 100644 index 0000000..6dd6ab6 --- /dev/null +++ b/public/assets/index.js @@ -0,0 +1,91 @@ +$(document).ready(function() { + $('.carousel-wrapper').slick({ + dots: true, + infinite: true, + speed: 500, + slidesToShow: 1, + slidesToScroll: 1 + }); + + var canvas_parent = document.getElementById('animated-bg'); + var canvas = document.createElement('canvas'); + canvas.width = window.innerWidth; + canvas.height = window.innerHeight / 2; + window.sandbox_canvas = canvas; + + canvas_parent.appendChild(canvas, null, { filtering: 'nearest' }); + var sandbox = new GlslCanvas(canvas); + // randomize time + sandbox.timeLoad = Math.random() * 100000; + + window.sandbox = sandbox; // for access + + const shader_code = ` +precision highp float; +uniform vec3 color; +uniform sampler2D t; +uniform vec2 tiles; +uniform float u_time; +uniform vec2 u_resolution; +uniform float symbol_count; + +vec2 map_to_symbol_uv(vec2 image_coord, float symbol_index) { + float symbol_width = 1.0 / symbol_count; + float symbol_height = 1.0; + float symbol_x = symbol_index * symbol_width; + return vec2(symbol_x + image_coord.x * symbol_width, image_coord.y * symbol_height); +} + +vec2 tile_index(vec2 p, vec2 tiles) { + return vec2(floor(p.x * tiles.x), floor(p.y * tiles.y)); +} + +vec2 tilefy (vec2 p, vec2 tiles) { + return vec2(fract(p.x * tiles.x), fract(p.y * tiles.y)); +} + +float randf(float n) { + return fract(sin(n) * 43758.5453123); +} + +void main() { + vec2 uv = gl_FragCoord.xy / u_resolution.xy; + vec2 tile = tile_index(uv, tiles); + float tilerand = randf(tile.x + tile.y * tiles.x); + float v = tilerand + randf(floor(tilerand * u_time)); + float diff = fract(tilerand * u_time); + float s = floor(v * 9.0); + vec2 tp = tilefy(uv, tiles); + vec2 tp_center = vec2(0.5, 0.5); + float d = distance(tp, tp_center); + float scale = min(sin(diff * 3.1415) * 1.2, 1.0); + tp = tp_center + (tp - tp_center) / scale; + if (tp.x < 0.0 || tp.x > 1.0 || tp.y < 0.0 || tp.y > 1.0) { + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); + return; + } + vec2 p = map_to_symbol_uv(tp, mod(s, symbol_count)); + vec4 c = texture2D(t, p); + c.rgb *= color; + c.a *= uv.y * 4.0; + gl_FragColor = vec4(c.rgb * c.a, c.a); +} +` + + sandbox.load(shader_code); + + sandbox.setUniform('t', "https://cdn.duckonaut.dev/assets/images/symbols.png"); + + sandbox.setUniform('color', 42 / 255, 42 / 255, 80 / 255); + + sandbox.setUniform('symbol_count', 9); + + sandbox.setUniform('tiles', Math.ceil(window.innerWidth / 32), Math.ceil(window.innerHeight / 64)); + + addEventListener('resize', function() { + window.sandbox_canvas.width = window.innerWidth; + window.sandbox_canvas.height = window.innerHeight / 2; + window.sandbox.setUniform('tiles', Math.ceil(window.innerWidth / 32), Math.ceil(window.innerHeight / 64)); + }); +}); + diff --git a/public/assets/loose/github-yellow.svg b/public/assets/loose/github-yellow.svg new file mode 100644 index 0000000..f81e9b9 --- /dev/null +++ b/public/assets/loose/github-yellow.svg @@ -0,0 +1,66 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/public/assets/loose/itch-yellow.svg b/public/assets/loose/itch-yellow.svg new file mode 100644 index 0000000..008c7b1 --- /dev/null +++ b/public/assets/loose/itch-yellow.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/public/assets/loose/twitter-yellow.svg b/public/assets/loose/twitter-yellow.svg new file mode 100644 index 0000000..ec6168d --- /dev/null +++ b/public/assets/loose/twitter-yellow.svg @@ -0,0 +1,91 @@ + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/assets/loose/web-yellow.svg b/public/assets/loose/web-yellow.svg new file mode 100644 index 0000000..b61babc --- /dev/null +++ b/public/assets/loose/web-yellow.svg @@ -0,0 +1,58 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/public/assets/navbar.css b/public/assets/navbar.css new file mode 100644 index 0000000..9120052 --- /dev/null +++ b/public/assets/navbar.css @@ -0,0 +1,278 @@ +/* on big screens, navbar is fixed to the top of the screen */ +.navbar-container { + position: fixed; + /* gradient from top to bottom, full to transparent */ + top: 0; + width: 100%; + z-index: 9999; + margin: auto; +} + +/* on smaller ones, make it behave as a normal element laid out at the top of the page */ +@media (max-width: 1150px) { + .navbar-container { + position: static; + } +} + +.navbar { + padding-top: 20pt; + padding-bottom: 12pt; + align-items: center; + justify-content: center; + align-self: center; + background: linear-gradient(to bottom, var(--color-bg) 0%, var(--color-bg) 75%, rgba(0, 0, 0, 0) 100%); +} + +.navbar-icon { + font-family: Hack, monospace; + font-weight: 500; + color: #646cff; + font-size: 1.5em; + padding: 0.5em; + margin: 0.5em; +} + +.navbar { + align-self: center; + display: block; + width: 100%; + text-align: center; +} + +.navbar-item { + font-family: Hack, monospace; + font-size: 18pt; + text-align: center; + float: none; + clear: left; + display: inline-block; + margin: 0 0.25em; + transition: all 0.2s ease-in-out; +} + +.navbar-item:hover { + padding-left: 0.5em; + padding-right: 0.5em; + transition: all 0.2s ease-in-out; +} + +.navbar-current { + font-weight: bold; +} + +/* home */ + +.navbar-item-home:link { + color: #98C379; + text-decoration: none; +} + +.navbar-item-home:visited { + color: #98C379; + text-decoration: none; +} + +.navbar-item-home:hover { + color: #afd088; + text-decoration: underline; +} + +.navbar-item-home:active { + color: #afd088; + text-decoration: underline; +} + +/* games */ + +.navbar-item-games:link { + color: #e06c75; + text-decoration: none; +} + +.navbar-item-games:visited { + color: #e06c75; + text-decoration: none; +} + +.navbar-item-games:hover { + color: #ef7a80; + text-decoration: underline; +} + +.navbar-item-games:active { + color: #ef7a90; + text-decoration: underline; +} + +/* projects */ + +.navbar-item-projects:link { + color: #d19a66; + text-decoration: none; +} + +.navbar-item-projects:visited { + color: #d19a66; + text-decoration: none; +} + +.navbar-item-projects:hover { + color: #dfa873; + text-decoration: underline; +} + +.navbar-item-projects:active { + color: #dffa73; + text-decoration: underline; +} + +/* github */ + +.navbar-item-github:link { + color: #c678dd; + text-decoration: none; +} + +.navbar-item-github:visited { + color: #c678dd; + text-decoration: none; +} + +.navbar-item-github:hover { + color: #d286eb; + text-decoration: underline; +} + +.navbar-item-github:active { + color: #d286eb; + text-decoration: underline; +} + +/* itch */ + +.navbar-item-itch:link { + color: #e06c75; + text-decoration: none; +} + +.navbar-item-itch:visited { + color: #e06c75; + text-decoration: none; +} + +.navbar-item-itch:hover { + color: #ef7a80; + text-decoration: underline; +} + +.navbar-item-itch:active { + color: #ef7a90; + text-decoration: underline; +} + +/* twitter */ + +.navbar-item-twitter:link { + text-decoration: none; +} + +.navbar-item-twitter:visited { + text-decoration: none; +} + +.navbar-item-twitter:hover { + text-decoration: underline; +} + +.navbar-item-twitter:active { + text-decoration: underline; +} + +/* mastodon */ + +.navbar-item-mastodon:link { + color: #6a4ae0; + text-decoration: none; +} + +.navbar-item-mastodon:visited { + color: #6a4ae0; + text-decoration: none; +} + +.navbar-item-mastodon:hover { + color: #7a55e8; + text-decoration: underline; +} + +.navbar-item-mastodon:active { + color: #7a55e8; + text-decoration: underline; +} + +/* cohost */ + +.navbar-item-cohost:link { + color: #b63b73; + text-decoration: none; +} + +.navbar-item-cohost:visited { + color: #b63b73; + text-decoration: none; +} + +.navbar-item-cohost:hover { + color: #c54a7f; + text-decoration: underline; +} + +.navbar-item-cohost:active { + color: #c54a7f; + text-decoration: underline; +} + +/* tumblr */ + +.navbar-item-tumblr:link { + color: #00b8ff; + text-decoration: none; +} + +.navbar-item-tumblr:visited { + color: #00b8ff; + text-decoration: none; +} + +.navbar-item-tumblr:hover { + color: #00c5ff; + text-decoration: underline; +} + +.navbar-item-tumblr:active { + color: #00c5ff; + text-decoration: underline; +} + +/* contact */ + +.navbar-item-contact:link { + color: #e5c07b; + text-decoration: none; +} + +.navbar-item-contact:visited { + color: #e5c07b; + text-decoration: none; +} + +.navbar-item-contact:hover { + color: #f2ce89; + text-decoration: underline; +} + +.navbar-item-contact:active { + color: #f2ce89; + text-decoration: underline; +} diff --git a/public/assets/project.css b/public/assets/project.css new file mode 100644 index 0000000..742c072 --- /dev/null +++ b/public/assets/project.css @@ -0,0 +1,126 @@ +.icon { + display: inline; + width: 32px; + height: 32px; + transition: 0.3s; + transform-origin: center; + filter: brightness(70%) +} + +.icon:hover { + filter: brightness(80%); +} + +.icon-row { + width: 320px; + height: 36px; +} + +.icon-container { + height: 36px; + float: right; +} + +.icon-text { + width: auto; + height: 36px; + line-height: 36px; + float: left; + vertical-align: middle; + margin-right: 8px; + color: #5c6370; +} + +.project { + display: table; + align-self: center; + clear: both; + margin-top: 24pt; +} + +.project-info { + font-size: 14pt; +} + +.project-description { + font-size: 16pt; + text-align: justify; +} + +.project-image-gallery { + margin-left: 24px; + margin-right: 10px; +} + +@media (max-width: 1150px) { + .project-title { + text-align: center; + font-size: 24pt; + margin-bottom: 8px; + text-decoration-line: underline; + } + + .project-image-gallery { + margin: 60px auto; + width: 330px; + height: 220px; + } + + .project-info { + float: none; + width: 90%; + margin: auto; + } + + .project-description { + font-size: 14pt; + } +} + +@media (max-width: 1150px) and (min-width: 480px) { + .project-title { + text-align: center; + font-size: 24pt; + margin-bottom: 8px; + text-decoration-line: underline; + } + .project-image-gallery { + margin: 60px auto; + width: 480px; + height: 360px; + } + + .project-info { + float: none; + width: 90%; + margin: auto; + } + + .project-description { + font-size: 14pt; + } +} + +@media (min-width: 1150px) { + .project-title { + text-align: left; + font-size: 32pt; + margin-bottom: 8px; + text-decoration-line: underline; + } + .project-image-gallery { + float: right; + width: 480px; + height: 360px; + } + .project-info { + float: left; + max-width: 600px; + } + + .project-description { + font-size: 16pt; + } +} + + diff --git a/public/assets/projects/hyprload/logo.png b/public/assets/projects/hyprload/logo.png new file mode 100644 index 0000000..92fb9ce Binary files /dev/null and b/public/assets/projects/hyprload/logo.png differ diff --git a/public/assets/projects/spirit-mod/demo1.gif b/public/assets/projects/spirit-mod/demo1.gif new file mode 100644 index 0000000..324de0c Binary files /dev/null and b/public/assets/projects/spirit-mod/demo1.gif differ diff --git a/public/assets/projects/spirit-mod/demo2.gif b/public/assets/projects/spirit-mod/demo2.gif new file mode 100644 index 0000000..4e87fd8 Binary files /dev/null and b/public/assets/projects/spirit-mod/demo2.gif differ diff --git a/public/assets/projects/spirit-mod/thumbnail.png b/public/assets/projects/spirit-mod/thumbnail.png new file mode 100644 index 0000000..55f8d7e Binary files /dev/null and b/public/assets/projects/spirit-mod/thumbnail.png differ diff --git a/public/assets/projects/spirit-mod/thumbnail.webp b/public/assets/projects/spirit-mod/thumbnail.webp new file mode 100644 index 0000000..0250f72 Binary files /dev/null and b/public/assets/projects/spirit-mod/thumbnail.webp differ diff --git a/reluax.luax b/reluax.luax new file mode 100644 index 0000000..1cfc04c --- /dev/null +++ b/reluax.luax @@ -0,0 +1,72 @@ +local Common = require("common") +local Navbar = require("components.navbar") +local AnimatedBg = require("components.animated_bg") + +local Home = require("pages.home") +local Projects = require("pages.projects") +local Games = require("pages.games") +local Contact = require("pages.contact") +local Blogpost = require("pages.blogpost") +local Blog = require("pages.blog") +local NotFound = require("not_found") + +local blogposts = require("blog.posts") + +math.randomseed(os.time()) + +local function wrap_component(component) + return + + +
    +
    + {$ component $} + +
    +
    +end + +local function route(path) + if path == '/' then + return 200, wrap_component() + elseif path == '/projects' then + return 200, wrap_component() + elseif path == '/games' then + return 200, wrap_component() + elseif path == '/contact' then + return 200, wrap_component() + elseif path == '/blog' then + return 200, wrap_component() + elseif reluax.url_matches('/blog/{slug}', path) then + local slug = reluax.url_extract('/blog/{slug}', path).slug + -- blogposts is a list of blogposts, with the slug a property of each + local post = nil + for _, p in ipairs(blogposts) do + if p.slug == slug then + post = p + break + end + end + if post then + local article = + + +
    +
    + + +
    +
    + + return 200, article + end + end + + return 404, NotFound() +end + +return { + name = "duckonaut.dev", + route = route +} + -- cgit v1.3.1