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 <title>/<link> tags at runtime) - Update Blog/Root/main modules, README, and favicon Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d87c408712
commit
13a12c2a2c
15 changed files with 227 additions and 126 deletions
|
|
@ -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>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
51
implementations/Catcrafts-Views.cpp
Normal file
51
implementations/Catcrafts-Views.cpp
Normal file
|
|
@ -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>)");
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
RenderRoot(Router::GetPath());
|
||||
|
||||
window.StartUpdate();
|
||||
window.StartSync();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue