Compare commits

..
17 changed files with 126 additions and 344 deletions

View file

@ -1,96 +0,0 @@
name: Deploy
on:
push:
branches: [master]
workflow_dispatch:
# One deploy at a time; if you push twice quickly, cancel the older run so the
# newest commit is what lands on the server.
concurrency:
group: deploy
cancel-in-progress: true
jobs:
build-deploy:
runs-on: arch-latest
steps:
- name: Install build dependencies
run: |
# Same keyring bootstrap the Crafter.Build CI does: the slim
# archlinux:latest image ships without a populated pacman keyring
# or local master key.
pacman-key --init
pacman-key --populate archlinux
pacman -Sy --noconfirm --needed archlinux-keyring
pacman -Syu --noconfirm --needed \
clang lld libc++ \
wasi-libc wasi-libc++ wasi-libc++abi wasi-compiler-rt \
git curl tar rsync
# Container runs as root; workspace may be owned by another uid.
git config --global --add safe.directory '*'
- name: Install crafter-build
# Pull the rolling 'latest' Linux build from the Crafter.Build repo and
# install it distro-style so it auto-discovers its modules under
# /usr/share/crafter-build. v2 = SSE4.2 baseline, safe on the CI SBC.
run: |
set -eux
url="https://forgejo.catcrafts.net/Catcrafts/Crafter.Build/releases/download/latest/crafter-build-linux-x86_64-v2.tar.gz"
mkdir -p /tmp/cb
curl -fsSL "$url" -o /tmp/cb.tar.gz
tar -xzf /tmp/cb.tar.gz -C /tmp/cb
install -Dm755 /tmp/cb/bin/crafter-build /usr/bin/crafter-build
cp -r /tmp/cb/share/crafter-build /usr/share/
crafter-build --version || true
- name: Checkout
uses: actions/checkout@v4
- name: Cache crafter-build dependency clones
# ~/.cache/crafter.build holds the Crafter.Graphics clone and prebuilt
# module cache. crafter-build still git-pulls the dep each run, so a
# stale cache only means a smaller delta fetch, never a stale build.
uses: actions/cache@v4
with:
path: ~/.cache/crafter.build
key: crafter-cache-${{ runner.os }}-${{ hashFiles('project.cpp') }}
restore-keys: |
crafter-cache-${{ runner.os }}-
- name: Build (wasm bundle)
run: crafter-build
- name: Locate build output
id: out
run: |
set -eu
dist=$(find bin -maxdepth 1 -type d -name 'Catcrafts.Net-wasm32-wasip1-*' | head -n1)
if [ -z "$dist" ]; then
echo "No build output directory found under bin/" >&2
ls -la bin || true
exit 1
fi
echo "dist=$dist" >> "$GITHUB_OUTPUT"
echo "Built bundle: $dist"
ls -la "$dist"
- name: Deploy to web root
# No SSH: the job already runs on the deploy box. The server's web root
# is bind-mounted into this container at /deploy via the runner config
# (container.options: "-v /path/to/webroot:/deploy"). We just copy into
# it. --delete makes the root an exact mirror of the build; Caddyfile.coi
# is a local dev helper (its 'root' points at the build machine), so the
# server's own Caddy config supplies the headers instead.
env:
DIST: ${{ steps.out.outputs.dist }}
run: |
set -eu
if [ ! -d /deploy ]; then
echo "ERROR: /deploy is not mounted into the runner container." >&2
echo "Add '-v /your/webroot:/deploy' to the runner's container.options" >&2
echo "in config.yaml and restart the runner." >&2
exit 1
fi
rsync -a --delete --exclude 'Caddyfile.coi' "$DIST"/ /deploy/
echo "Deployed $DIST -> /deploy (host web root)"

View file

@ -1,6 +1,6 @@
# catcrafts.net # catcrafts.net
This is the source code for catcrafts.net, a website built entirely in C++ using the Crafter.Graphics library. This is the source code for catcrafts.net, a website built entirely in C++ using the Crafter.CppDOM library.
```bash ```bash
crafter-build build executable crafter-build build executable

View file

@ -1,24 +0,0 @@
// 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);

View file

@ -1,21 +0,0 @@
# catcrafts.net Caddy site block
#
# The WASM app (Crafter.Graphics) needs a cross-origin-isolated context
# (SharedArrayBuffer / threads), which requires these response headers. They
# are NOT optional — without them the page loads but the runtime fails.
#
# If you already have a `catcrafts.net { ... }` block, you only need to ADD the
# three Cross-Origin-* header lines to it. This file is the complete block for
# reference. Point `root` at the host directory you bind-mount into the runner
# as /deploy (the "-v /path/to/webroot:/deploy" in the runner's config.yaml).
catcrafts.net {
root * /srv/catcrafts.net
header Cross-Origin-Opener-Policy "same-origin"
header Cross-Origin-Embedder-Policy "require-corp"
header Cross-Origin-Resource-Policy "same-origin"
encode zstd gzip
file_server
}

View file

@ -1,6 +1,6 @@
<!-- save this as emoji-favicon.svg --> <!-- save this as emoji-favicon.svg -->
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"> <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64">
<text x="50%" y="50%" font-size="110" text-anchor="middle" dominant-baseline="central"> <text x="50%" y="50%" font-size="48" text-anchor="middle" dominant-baseline="central">
🐱 🐱
</text> </text>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 222 B

After

Width:  |  Height:  |  Size: 219 B

Before After
Before After

View file

@ -1,8 +1,8 @@
/* /*
catcrafts.net catcrafts.net
Copyright (C) 2026 Catcrafts Copyright (C) 2025 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. No permission is granted to copy, modify, distribute, or create derivative works.
*/ */
@ -10,24 +10,22 @@ export module Catcrafts:Blog_impl;
import :Blog; import :Blog;
import :Root; import :Root;
import :Views; import :Views;
import Crafter.Graphics;
import std; import std;
using namespace Crafter; using namespace Crafter::CppDOMBindings;
export namespace Catcrafts { export namespace Catcrafts {
// Persistent storage for the per-post card click handlers. HtmlElementPtr std::vector<HtmlElementView> blogButtons;
// 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() { void RenderBlog() {
blogButtons.clear(); blogButtons.clear();
std::string html = ""; std::string html = "";
for(const BlogPost& post : posts) { 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; std::string previewContent = post.content;
if(previewContent.length() > 200) { if(previewContent.length() > 200) {
// Find the last space before 200 characters to avoid cutting words
std::size_t lastSpace = previewContent.find_last_of(' ', 200); std::size_t lastSpace = previewContent.find_last_of(' ', 200);
if(lastSpace != std::string::npos) { if(lastSpace != std::string::npos) {
previewContent = previewContent.substr(0, lastSpace) + "..."; previewContent = previewContent.substr(0, lastSpace) + "...";
@ -49,21 +47,24 @@ export namespace Catcrafts {
</div> </div>
</div>)", post.slug, post.name, post.date, previewContent); </div>)", post.slug, post.name, post.date, previewContent);
} }
MainContent().SetInnerHTML(std::format(R"(<div class="blog-posts">{}</div>)", html)); main.SetInnerHTML(std::format(R"(<div class="blog-posts">{}</div>)", html));
for(const BlogPost& post : posts) { for(const BlogPost& post : posts) {
Dom::HtmlElementPtr& cardView = blogButtons.emplace_back(std::format("blog-post-{}", post.slug)); // Add click listener to the entire post card
cardView.AddClickListener([slug = post.slug](Dom::MouseEvent) { HtmlElementView& cardView = blogButtons.emplace_back(std::format("blog-post-{}", post.slug));
Router::PushState("{}", "", std::format("/blog/{}", slug)); cardView.AddClickListener([slug = post.slug](Crafter::MouseEvent e) {
PushState("{}", "", std::format("/blog/{}", slug));
RenderRoot(std::format("/blog/{}", slug)); RenderRoot(std::format("/blog/{}", slug));
}); });
} }
} }
void RenderBlogPost(const std::string_view slug) { void RenderBlogPost(const std::string_view slug) {
std::cout << "render "<< std::endl;
for(const BlogPost& post : posts) { for(const BlogPost& post : posts) {
if(post.slug == slug) { if(post.slug == slug) {
MainContent().SetInnerHTML(std::format(R"( main.SetInnerHTML(std::format(R"(
<div class="blog-post-page"> <div class="blog-post-page">
<div class="post-header"> <div class="post-header">
<h1 class="post-title">{}</h1> <h1 class="post-title">{}</h1>
@ -77,6 +78,6 @@ export namespace Catcrafts {
} }
} }
MainContent().SetInnerHTML("<h1>Post Not Found</h1><p>The requested blog post could not be found.</p>"); main.SetInnerHTML("<h1>Post Not Found</h1><p>The requested blog post could not be found.</p>");
} }
} }

View file

@ -1,8 +1,8 @@
/* /*
catcrafts.net catcrafts.net
Copyright (C) 2026 Catcrafts Copyright (C) 2025 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. No permission is granted to copy, modify, distribute, or create derivative works.
*/ */
@ -10,27 +10,29 @@ export module Catcrafts:Root_impl;
import :Root; import :Root;
import :Views; import :Views;
import :Blog; import :Blog;
import Crafter.Graphics;
import std; import std;
using namespace Crafter;
namespace Catcrafts { namespace Catcrafts {
void RenderRoot(const std::string_view route) { void RenderRoot(const std::string_view route) {
std::string currentRoute = std::string(route); std::string currentRoute = std::string(route);
if(currentRoute == "/blog" || currentRoute == "/") { if(currentRoute == "/blog" || currentRoute == "/") {
RenderBlog(); RenderBlog();
} else if(currentRoute.rfind("/blog/", 0) == 0) { } 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::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) { if(pos != std::string::npos) {
std::string postSlug = currentRoute.substr(pos + 1); std::string postSlug = currentRoute.substr(pos + 1);
std::cout << postSlug << std::endl;
RenderBlogPost(postSlug); RenderBlogPost(postSlug);
} else { } else {
MainContent().SetInnerHTML("<h1>Post Not Found</h1><p>The requested blog post could not be found.</p>"); main.SetInnerHTML("<h1>Post Not Found</h1><p>The requested blog post could not be found.</p>");
} }
} else { } else {
RenderBlog(); RenderBlog(); //default route
} }
} }
} }

View file

@ -1,51 +0,0 @@
/*
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>&copy; 2026 Catcrafts®. All rights reserved. Crafter® and Catcrafts® are registered trademarks with the EUIPO</p>
</div>
</footer>)");
}
}

View file

@ -1,40 +1,30 @@
/* /*
catcrafts.net catcrafts.net
Copyright (C) 2026 Catcrafts Copyright (C) 2025 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. No permission is granted to copy, modify, distribute, or create derivative works.
*/ */
import Catcrafts; import Catcrafts;
import Crafter.Graphics; import Crafter.CppDOM;
import std; import std;
using namespace Catcrafts; using namespace Catcrafts;
using namespace Crafter; using namespace Crafter;
using namespace Crafter::CppDOMBindings;
HtmlElementView* blogButton;
int main() { int main() {
Device::Initialize(); AddPopStateListener([]() {
// Window in CRAFTER_GRAPHICS_WINDOW_DOM mode is a page-level event hub RenderRoot(GetPathNameString());
// 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());
}); });
static Dom::HtmlElementPtr blogButton("blog-nav-button"); blogButton = new HtmlElementView("blog-nav-button");
blogButton.AddClickListener([](Dom::MouseEvent) { blogButton->AddClickListener([](Crafter::MouseEvent e) {
Router::PushState("{}", "", "/blog"); PushState("{}", "", "/blog");
RenderRoot("/blog"); RenderRoot("/blog");
}); });
RenderRoot(Router::GetPath()); RenderRoot(GetPathNameString());
}
window.StartUpdate();
window.StartSync();
return 0;
}

View file

@ -1,13 +1,13 @@
/* /*
catcrafts.net catcrafts.net
Copyright (C) 2026 Catcrafts Copyright (C) 2025 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. No permission is granted to copy, modify, distribute, or create derivative works.
*/ */
export module Catcrafts:Blog; export module Catcrafts:Blog;
import Crafter.Graphics; import Crafter.CppDOM;
import std; import std;
using namespace Crafter; using namespace Crafter;
@ -22,8 +22,8 @@ export namespace Catcrafts {
{ {
"In WASM, Exit doesn't mean done.", "In WASM, Exit doesn't mean done.",
"in-wasm-exit-doesnt-mean-done", "in-wasm-exit-doesnt-mean-done",
"2026-11-14", "2025-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&lt;BlogPost&gt; posts = new std::vector&lt;BlogPost&gt;{...};</code><br><br> 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>
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> 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> 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 the ease of things like this is also one of the reasons i use clang instead of gcc.<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>
So now all examples and this site have been updated to use normal variables again.<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!",
"hello-world", "hello-world",
"2026-11-12", "2025-11-12",
R"(Welcome to catcrafts.net!<br><br> R"(Welcome to catcrafts.net!<br><br>
Here we believe optimization is everything and C++ is a gift from god.<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> 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.Graphics library.<br> Like this website which is fully written in C++ using the Crafter.CppDOM library.<br>
And source available too!<br> And source available too!<br>
<a href="https://forgejo.catcrafts.net/Catcrafts/catcrafts.net">https://forgejo.catcrafts.net/Catcrafts/catcrafts.net<a>)" <a href="https://forgejo.catcrafts.net/Catcrafts/catcrafts.net">https://forgejo.catcrafts.net/Catcrafts/catcrafts.net<a>)"
} }

View file

@ -0,0 +1,16 @@
/*
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();
};
}

View file

@ -1,13 +1,13 @@
/* /*
catcrafts.net catcrafts.net
Copyright (C) 2026 Catcrafts Copyright (C) 2025 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. No permission is granted to copy, modify, distribute, or create derivative works.
*/ */
export module Catcrafts:Root; export module Catcrafts:Root;
import Crafter.Graphics; import Crafter.CppDOM;
import std; import std;
using namespace Crafter; using namespace Crafter;

View file

@ -1,27 +1,48 @@
/* /*
catcrafts.net catcrafts.net
Copyright (C) 2026 Catcrafts Copyright (C) 2025 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. No permission is granted to copy, modify, distribute, or create derivative works.
*/ */
export module Catcrafts:Views; export module Catcrafts:Views;
import Crafter.Graphics; import Crafter.CppDOM;
import std;
using namespace Crafter; using namespace Crafter;
export namespace Catcrafts { export namespace Catcrafts {
// Builds the persistent page chrome (header / <main id="main"> / footer) HtmlElementPtr body("body", R"(
// under <body>. Owns the root element for the page's lifetime so its <header>
// children don't get yanked out of the DOM. Must be called from main() <div class="nav-container">
// — the Crafter.Graphics Dom bridge is only safe to use after wasm <a href="/" class="logo">🐱 Catcrafts</a>
// instantiation hands control to user code. <nav>
void InitializePage(); <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>
// Per-render scratch ref to the <main> content container created by <main id="main"></main>
// InitializePage(). Looking it up fresh on every call costs one
// document.getElementById; in return we don't have to thread a long-lived <footer>
// reference across module boundaries. <div class="footer-content">
inline Dom::HtmlElementPtr MainContent() { return Dom::HtmlElementPtr("main"); } <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>&copy; 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</title>
<link rel="stylesheet" href="/styles.css">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
)");
HtmlElementPtr main("main");
} }

View file

@ -1,6 +1,6 @@
/* /*
catcrafts.net catcrafts.net
Copyright (C) 2026 Catcrafts Copyright (C) 2025 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. No permission is granted to copy, modify, distribute, or create derivative works.

View file

@ -1,75 +0,0 @@
/*
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<const std::string_view> args) {
std::vector<std::string> 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<fs::path, 4> ifaces = {
"interfaces/Catcrafts",
"interfaces/Catcrafts-Views",
"interfaces/Catcrafts-Blog",
"interfaces/Catcrafts-Root",
};
std::array<fs::path, 4> 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 <script type="module"> before runtime.js by
// EnableWasiBrowserRuntime — sets <title>/<link> tags since the
// Dom partition has no head-element access.
cfg.files.emplace_back(fs::path("catcrafts-head.js"));
EnableWasiBrowserRuntime(cfg);
return cfg;
}

18
project.json Normal file
View file

@ -0,0 +1,18 @@
{
"name": "main",
"configurations": [
{
"name": "executable",
"interfaces": ["interfaces/Catcrafts", "interfaces/Catcrafts-Views", "interfaces/Catcrafts-Blog", "interfaces/Catcrafts-Root"],
"implementations": ["implementations/main", "implementations/Catcrafts-Blog", "implementations/Catcrafts-Root"],
"target": "wasm32-wasi",
"additional_files": ["styles/styles.css", "robots.txt", "sitemap.xml", "favicon.svg"],
"dependencies": [
{
"path":"https://forgejo.catcrafts.net/Catcrafts/Crafter.CppDOM.git",
"configuration":"lib"
}
]
}
]
}

1
run.sh Executable file
View file

@ -0,0 +1 @@
caddy file-server --listen :8080 --root bin/executable