From 13a12c2a2cf6cdfd364016edad8d469c571df041 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sat, 18 Jul 2026 23:27:41 +0200 Subject: [PATCH] Migrate to crafter-build project.cpp and restructure views - Replace project.json + run.sh with a C++ project.cpp build descriptor (crafter-build), targeting wasm32-wasip1 with the Crafter.Graphics dep - Add Catcrafts-Views implementation; drop Catcrafts-Component - Add catcrafts-head.js (sets /<link> tags at runtime) - Update Blog/Root/main modules, README, and favicon Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --- README.md | 2 +- catcrafts-head.js | 24 +++++++++ favicon.svg | 4 +- implementations/Catcrafts-Blog.cpp | 35 +++++++------- implementations/Catcrafts-Root.cpp | 20 ++++---- implementations/Catcrafts-Views.cpp | 51 ++++++++++++++++++++ implementations/main.cpp | 36 +++++++++----- interfaces/Catcrafts-Blog.cppm | 14 +++--- interfaces/Catcrafts-Component.cppm | 16 ------ interfaces/Catcrafts-Root.cppm | 4 +- interfaces/Catcrafts-Views.cppm | 51 ++++++-------------- interfaces/Catcrafts.cppm | 2 +- project.cpp | 75 +++++++++++++++++++++++++++++ project.json | 18 ------- run.sh | 1 - 15 files changed, 227 insertions(+), 126 deletions(-) create mode 100644 catcrafts-head.js create mode 100644 implementations/Catcrafts-Views.cpp delete mode 100644 interfaces/Catcrafts-Component.cppm create mode 100644 project.cpp delete mode 100644 project.json delete mode 100755 run.sh diff --git a/README.md b/README.md index be1d292..800d0a7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # catcrafts.net -This is the source code for catcrafts.net, a website built entirely in C++ using the Crafter.CppDOM library. +This is the source code for catcrafts.net, a website built entirely in C++ using the Crafter.Graphics library. ```bash crafter-build build executable diff --git a/catcrafts-head.js b/catcrafts-head.js new file mode 100644 index 0000000..409d92b --- /dev/null +++ b/catcrafts-head.js @@ -0,0 +1,24 @@ +// Head-element setup for catcrafts.net. The Crafter.Graphics Dom partition +// only exposes element creation under <body>, so anything that has to live in +// <head> (title, stylesheet link, favicon link) gets injected from this +// loader. EnableWasiBrowserRuntime auto-picks up every *.js in cfg.files and +// emits a <script type="module"> tag for it ahead of runtime.js, so this +// runs before the wasm module starts and styles are in place by first paint. + +document.title = "Catcrafts.net"; + +const styles = document.createElement("link"); +styles.rel = "stylesheet"; +styles.href = "styles.css"; +document.head.appendChild(styles); + +const favicon = document.createElement("link"); +favicon.rel = "icon"; +favicon.type = "image/svg+xml"; +favicon.href = "favicon.svg"; +document.head.appendChild(favicon); + +const viewport = document.createElement("meta"); +viewport.name = "viewport"; +viewport.content = "width=device-width, initial-scale=1.0"; +document.head.appendChild(viewport); diff --git a/favicon.svg b/favicon.svg index 4cae83e..f6d77d6 100644 --- a/favicon.svg +++ b/favicon.svg @@ -1,6 +1,6 @@ <!-- save this as emoji-favicon.svg --> -<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64"> - <text x="50%" y="50%" font-size="48" text-anchor="middle" dominant-baseline="central"> +<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"> + <text x="50%" y="50%" font-size="110" text-anchor="middle" dominant-baseline="central"> 🐱 </text> </svg> diff --git a/implementations/Catcrafts-Blog.cpp b/implementations/Catcrafts-Blog.cpp index 47e0974..2b5433b 100644 --- a/implementations/Catcrafts-Blog.cpp +++ b/implementations/Catcrafts-Blog.cpp @@ -1,8 +1,8 @@ /* catcrafts.net -Copyright (C) 2025 Catcrafts +Copyright (C) 2026 Catcrafts -The source code of this website is made available for viewing purposes only. +The source code of this website is made available for viewing purposes only. No permission is granted to copy, modify, distribute, or create derivative works. */ @@ -10,22 +10,24 @@ export module Catcrafts:Blog_impl; import :Blog; import :Root; import :Views; +import Crafter.Graphics; import std; -using namespace Crafter::CppDOMBindings; +using namespace Crafter; export namespace Catcrafts { - std::vector<HtmlElementView> blogButtons; + // Persistent storage for the per-post card click handlers. HtmlElementPtr + // unregisters its listeners on destruction, so the elements have to + // outlive Render(); clear() at the top of RenderBlog() detaches the + // previous render's handlers before we attach the new ones. + std::vector<Dom::HtmlElementPtr> blogButtons; void RenderBlog() { blogButtons.clear(); std::string html = ""; for(const BlogPost& post : posts) { - std::cout << "test" << std::endl; - // For preview, we'll limit the content to first 200 characters std::string previewContent = post.content; if(previewContent.length() > 200) { - // Find the last space before 200 characters to avoid cutting words std::size_t lastSpace = previewContent.find_last_of(' ', 200); if(lastSpace != std::string::npos) { previewContent = previewContent.substr(0, lastSpace) + "..."; @@ -47,24 +49,21 @@ export namespace Catcrafts { </div> </div>)", post.slug, post.name, post.date, previewContent); } - main.SetInnerHTML(std::format(R"(<div class="blog-posts">{}</div>)", html)); + MainContent().SetInnerHTML(std::format(R"(<div class="blog-posts">{}</div>)", html)); for(const BlogPost& post : posts) { - // Add click listener to the entire post card - HtmlElementView& cardView = blogButtons.emplace_back(std::format("blog-post-{}", post.slug)); - cardView.AddClickListener([slug = post.slug](Crafter::MouseEvent e) { - PushState("{}", "", std::format("/blog/{}", slug)); + Dom::HtmlElementPtr& cardView = blogButtons.emplace_back(std::format("blog-post-{}", post.slug)); + cardView.AddClickListener([slug = post.slug](Dom::MouseEvent) { + Router::PushState("{}", "", std::format("/blog/{}", slug)); RenderRoot(std::format("/blog/{}", slug)); }); - } } - + void RenderBlogPost(const std::string_view slug) { - std::cout << "render "<< std::endl; for(const BlogPost& post : posts) { if(post.slug == slug) { - main.SetInnerHTML(std::format(R"( + MainContent().SetInnerHTML(std::format(R"( <div class="blog-post-page"> <div class="post-header"> <h1 class="post-title">{}</h1> @@ -78,6 +77,6 @@ export namespace Catcrafts { } } - main.SetInnerHTML("<h1>Post Not Found</h1><p>The requested blog post could not be found.</p>"); + MainContent().SetInnerHTML("<h1>Post Not Found</h1><p>The requested blog post could not be found.</p>"); } -} \ No newline at end of file +} diff --git a/implementations/Catcrafts-Root.cpp b/implementations/Catcrafts-Root.cpp index 1510169..9f6c699 100644 --- a/implementations/Catcrafts-Root.cpp +++ b/implementations/Catcrafts-Root.cpp @@ -1,8 +1,8 @@ /* catcrafts.net -Copyright (C) 2025 Catcrafts +Copyright (C) 2026 Catcrafts -The source code of this website is made available for viewing purposes only. +The source code of this website is made available for viewing purposes only. No permission is granted to copy, modify, distribute, or create derivative works. */ @@ -10,29 +10,27 @@ export module Catcrafts:Root_impl; import :Root; import :Views; import :Blog; +import Crafter.Graphics; import std; +using namespace Crafter; + namespace Catcrafts { void RenderRoot(const std::string_view route) { std::string currentRoute = std::string(route); - + if(currentRoute == "/blog" || currentRoute == "/") { RenderBlog(); } else if(currentRoute.rfind("/blog/", 0) == 0) { - // Handle individual blog post routes like /blog/0, /blog/1, etc. std::size_t pos = currentRoute.find_last_of('/'); - std::cout << pos << std::endl; - std::cout << std::string::npos << std::endl; - std::cout << (pos != std::string::npos) << std::endl; if(pos != std::string::npos) { std::string postSlug = currentRoute.substr(pos + 1); - std::cout << postSlug << std::endl; RenderBlogPost(postSlug); } else { - main.SetInnerHTML("<h1>Post Not Found</h1><p>The requested blog post could not be found.</p>"); + MainContent().SetInnerHTML("<h1>Post Not Found</h1><p>The requested blog post could not be found.</p>"); } } else { - RenderBlog(); //default route + RenderBlog(); } } -} \ No newline at end of file +} diff --git a/implementations/Catcrafts-Views.cpp b/implementations/Catcrafts-Views.cpp new file mode 100644 index 0000000..891fcde --- /dev/null +++ b/implementations/Catcrafts-Views.cpp @@ -0,0 +1,51 @@ +/* +catcrafts.net +Copyright (C) 2026 Catcrafts + +The source code of this website is made available for viewing purposes only. +No permission is granted to copy, modify, distribute, or create derivative works. +*/ + +export module Catcrafts:Views_impl; +import :Views; +import Crafter.Graphics; +import std; + +using namespace Crafter; + +namespace Catcrafts { + namespace { + // Owning handle for the page-chrome root. `std::optional` lets us + // defer construction until InitializePage() runs (the CreateInBody + // call would otherwise fire at static-init time, before main()). + std::optional<Dom::HtmlElement> root; + } + + void InitializePage() { + root.emplace(Dom::HtmlElement::CreateInBody("div", "catcrafts-root")); + root->SetInnerHTML(R"( +<header> + <div class="nav-container"> + <a href="/" class="logo">🐱 Catcrafts</a> + <nav> + <ul> + <li><a id="blog-nav-button" style="cursor: pointer;" class="active">Blog</a></li> + <li><a href="https://forgejo.catcrafts.net/Catcrafts/">Forgejo</a></li> + </ul> + </nav> + </div> +</header> + +<main id="main"></main> + +<footer> + <div class="footer-content"> + <div class="footer-links"> + Powered by Crafter.Graphics, Running near native with WASM! + <a href="https://forgejo.catcrafts.net/Catcrafts/catcrafts.net">View source</a> + </div> + <p>© 2026 Catcrafts®. All rights reserved. Crafter® and Catcrafts® are registered trademarks with the EUIPO</p> + </div> +</footer>)"); + } +} diff --git a/implementations/main.cpp b/implementations/main.cpp index 51d1ab1..5f510a2 100644 --- a/implementations/main.cpp +++ b/implementations/main.cpp @@ -1,30 +1,40 @@ /* catcrafts.net -Copyright (C) 2025 Catcrafts +Copyright (C) 2026 Catcrafts -The source code of this website is made available for viewing purposes only. +The source code of this website is made available for viewing purposes only. No permission is granted to copy, modify, distribute, or create derivative works. */ import Catcrafts; -import Crafter.CppDOM; +import Crafter.Graphics; import std; using namespace Catcrafts; using namespace Crafter; -using namespace Crafter::CppDOMBindings; - -HtmlElementView* blogButton; int main() { - AddPopStateListener([]() { - RenderRoot(GetPathNameString()); + Device::Initialize(); + // Window in CRAFTER_GRAPHICS_WINDOW_DOM mode is a page-level event hub + // plus the rAF driver; even without subscribing to its events we need + // it so StartSync() can keep the wasm module alive past main() (the + // bridge stashes a raw pointer, so the storage has to outlive main). + static Window window(0, 0, "Catcrafts"); + + InitializePage(); + + Router::AddPopStateListener([]{ + RenderRoot(Router::GetPath()); }); - blogButton = new HtmlElementView("blog-nav-button"); - blogButton->AddClickListener([](Crafter::MouseEvent e) { - PushState("{}", "", "/blog"); + static Dom::HtmlElementPtr blogButton("blog-nav-button"); + blogButton.AddClickListener([](Dom::MouseEvent) { + Router::PushState("{}", "", "/blog"); RenderRoot("/blog"); }); - RenderRoot(GetPathNameString()); -} \ No newline at end of file + RenderRoot(Router::GetPath()); + + window.StartUpdate(); + window.StartSync(); + return 0; +} diff --git a/interfaces/Catcrafts-Blog.cppm b/interfaces/Catcrafts-Blog.cppm index 53b7e83..6a1bbd9 100644 --- a/interfaces/Catcrafts-Blog.cppm +++ b/interfaces/Catcrafts-Blog.cppm @@ -1,13 +1,13 @@ /* catcrafts.net -Copyright (C) 2025 Catcrafts +Copyright (C) 2026 Catcrafts The source code of this website is made available for viewing purposes only. No permission is granted to copy, modify, distribute, or create derivative works. */ export module Catcrafts:Blog; -import Crafter.CppDOM; +import Crafter.Graphics; import std; using namespace Crafter; @@ -22,8 +22,8 @@ export namespace Catcrafts { { "In WASM, Exit doesn't mean done.", "in-wasm-exit-doesnt-mean-done", - "2025-11-14", - R"(So if anyone looked at the source code for this website pefore this post you would have seen that every is allocated with new, for example this very blog was defined as <code>std::vector<BlogPost> posts = new std::vector<BlogPost>{...</code><br><br> + "2026-11-14", + R"(So if anyone looked at the source code for this website pefore this post you would have seen that everything was allocated with new, for example this very blog was defined as <code>std::vector<BlogPost> posts = new std::vector<BlogPost>{...};</code><br><br> Reason for this was that everything became corrupted when callbacking from JS, not knowing sure as to why this was the fastest solution, but after debugging the problem became clear.<br><br> @@ -31,7 +31,7 @@ export namespace Catcrafts { This caused all the memory corruption errors since everything was destructed.<br><br> - Luckily this is a very simple fix of adding <code>-fno-c++-static-destructors</code>, and things like this are is also the reason i use clang instead of gcc.<br><br> + Luckily this is a very simple fix of adding <code>-fno-c++-static-destructors</code>, and the ease of things like this is also one of the reasons i use clang instead of gcc.<br><br> So now all examples and this site have been updated to use normal variables again.<br><br> @@ -40,12 +40,12 @@ export namespace Catcrafts { { "Hello World!", "hello-world", - "2025-11-12", + "2026-11-12", R"(Welcome to catcrafts.net!<br><br> Here we believe optimization is everything and C++ is a gift from god.<br> This blog will mostly be dedicated to random tidbits i come across while working on my Crafter series of libraries.<br><br> - Like this website which is fully written in C++ using the Crafter.CppDOM library.<br> + Like this website which is fully written in C++ using the Crafter.Graphics library.<br> And source available too!<br> <a href="https://forgejo.catcrafts.net/Catcrafts/catcrafts.net">https://forgejo.catcrafts.net/Catcrafts/catcrafts.net<a>)" } diff --git a/interfaces/Catcrafts-Component.cppm b/interfaces/Catcrafts-Component.cppm deleted file mode 100644 index 1996871..0000000 --- a/interfaces/Catcrafts-Component.cppm +++ /dev/null @@ -1,16 +0,0 @@ -/* -catcrafts.net -Copyright (C) 2025 Catcrafts - -The source code of this website is made available for viewing purposes only. -No permission is granted to copy, modify, distribute, or create derivative works. -*/ - -export module Catcrafts:Component; -using namespace Crafter; - -export namespace Catcrafts { - class Component { - std::string render(); - }; -} diff --git a/interfaces/Catcrafts-Root.cppm b/interfaces/Catcrafts-Root.cppm index e0429b7..a7092d5 100644 --- a/interfaces/Catcrafts-Root.cppm +++ b/interfaces/Catcrafts-Root.cppm @@ -1,13 +1,13 @@ /* catcrafts.net -Copyright (C) 2025 Catcrafts +Copyright (C) 2026 Catcrafts The source code of this website is made available for viewing purposes only. No permission is granted to copy, modify, distribute, or create derivative works. */ export module Catcrafts:Root; -import Crafter.CppDOM; +import Crafter.Graphics; import std; using namespace Crafter; diff --git a/interfaces/Catcrafts-Views.cppm b/interfaces/Catcrafts-Views.cppm index 98496e4..f926dbe 100644 --- a/interfaces/Catcrafts-Views.cppm +++ b/interfaces/Catcrafts-Views.cppm @@ -1,48 +1,27 @@ /* catcrafts.net -Copyright (C) 2025 Catcrafts +Copyright (C) 2026 Catcrafts -The source code of this website is made available for viewing purposes only. +The source code of this website is made available for viewing purposes only. No permission is granted to copy, modify, distribute, or create derivative works. */ export module Catcrafts:Views; -import Crafter.CppDOM; +import Crafter.Graphics; +import std; using namespace Crafter; export namespace Catcrafts { - HtmlElementPtr body("body", R"( -<header> - <div class="nav-container"> - <a href="/" class="logo">🐱 Catcrafts</a> - <nav> - <ul> - <li><a id="blog-nav-button" style="cursor: pointer;" class="active">Blog</a></li> - <li><a href="https://forgejo.catcrafts.net/Catcrafts/">Forgejo</a></li> - </ul> - </nav> - </div> -</header> + // Builds the persistent page chrome (header / <main id="main"> / footer) + // under <body>. Owns the root element for the page's lifetime so its + // children don't get yanked out of the DOM. Must be called from main() + // — the Crafter.Graphics Dom bridge is only safe to use after wasm + // instantiation hands control to user code. + void InitializePage(); -<main id="main"></main> - -<footer> - <div class="footer-content"> - <div class="footer-links"> - Powered by Crafter.CppDOM, Running near native with WASM! - <a href="https://forgejo.catcrafts.net/Catcrafts/catcrafts.net">View source</a> - </div> - <p>© 2025 Catcrafts®. All rights reserved. Crafter® and Catcrafts® are registered trademarks with the EUIPO</p> - </div> -</footer>)"); - - HtmlElementPtr head("head", R"( -<meta charset="UTF-8"> -<meta name="viewport" content="width=device-width, initial-scale=1.0"> -<title>Catcrafts.net - - -)"); - - HtmlElementPtr main("main"); + // Per-render scratch ref to the
content container created by + // InitializePage(). Looking it up fresh on every call costs one + // document.getElementById; in return we don't have to thread a long-lived + // reference across module boundaries. + inline Dom::HtmlElementPtr MainContent() { return Dom::HtmlElementPtr("main"); } } diff --git a/interfaces/Catcrafts.cppm b/interfaces/Catcrafts.cppm index d2ed576..bfe722d 100644 --- a/interfaces/Catcrafts.cppm +++ b/interfaces/Catcrafts.cppm @@ -1,6 +1,6 @@ /* catcrafts.net -Copyright (C) 2025 Catcrafts +Copyright (C) 2026 Catcrafts The source code of this website is made available for viewing purposes only. No permission is granted to copy, modify, distribute, or create derivative works. diff --git a/project.cpp b/project.cpp new file mode 100644 index 0000000..1460a2f --- /dev/null +++ b/project.cpp @@ -0,0 +1,75 @@ +/* +catcrafts.net +Copyright (C) 2026 Catcrafts + +The source code of this website is made available for viewing purposes only. +No permission is granted to copy, modify, distribute, or create derivative works. +*/ + +import std; +import Crafter.Build; +namespace fs = std::filesystem; +using namespace Crafter; + +extern "C" Configuration CrafterBuildProject(std::span args) { + std::vector depArgs(args.begin(), args.end()); + depArgs.push_back("--target=wasm32-wasip1"); + + // --local resolves the Crafter.Graphics dep from a sibling working tree + // instead of fetching it from forgejo. Mirrors Crafter.Graphics's own + // project.cpp convention so edits across the two repos pick up without + // commit-and-pull. + bool useLocal = false; + for (std::string_view a : args) { + if (a == "--local") { useLocal = true; break; } + } + if (useLocal && std::find(depArgs.begin(), depArgs.end(), std::string("--local")) == depArgs.end()) { + depArgs.push_back("--local"); + } + + Configuration* graphics = useLocal + ? LocalProject({ + .projectFile = "../Crafter/Crafter.Graphics/project.cpp", + .args = depArgs, + }) + : GitProject({ + .source = { .url = "https://forgejo.catcrafts.net/Catcrafts/Crafter.Graphics.git" }, + .args = depArgs, + }); + + Configuration cfg; + cfg.path = "./"; + cfg.name = "Catcrafts.Net"; + cfg.outputName = "catcrafts"; + cfg.type = ConfigurationType::Executable; + cfg.target = "wasm32-wasip1"; + ApplyStandardArgs(cfg, args); + cfg.dependencies = { graphics }; + + std::array ifaces = { + "interfaces/Catcrafts", + "interfaces/Catcrafts-Views", + "interfaces/Catcrafts-Blog", + "interfaces/Catcrafts-Root", + }; + std::array impls = { + "implementations/main", + "implementations/Catcrafts-Blog", + "implementations/Catcrafts-Root", + "implementations/Catcrafts-Views", + }; + cfg.GetInterfacesAndImplementations(ifaces, impls); + + cfg.files.emplace_back(fs::path("styles/styles.css")); + cfg.files.emplace_back(fs::path("robots.txt")); + cfg.files.emplace_back(fs::path("sitemap.xml")); + cfg.files.emplace_back(fs::path("favicon.svg")); + // Loaded as a