This commit is contained in:
Jorijn van der Graaf 2025-11-12 21:56:18 +01:00
commit 937b9fb48f
9 changed files with 180 additions and 90 deletions

View file

@ -1,78 +1,54 @@
# catcrafts.net # catcrafts.net
A modern, responsive website built with C++ using Crafter.CppDOM and compiled to WebAssembly. This is the source code for catcrafts.net, a website built entirely in C++ using the Crafter.CppDOM library.
## Features ## Features
- Modern, clean design with gradient colors and smooth animations - **Responsive Design**: Works on all device sizes
- Fully responsive layout that works on mobile and desktop - **Modern UI**: Clean and contemporary design with smooth animations
- Blog functionality with multiple posts - **Blog System**:
- Smooth navigation and transitions - Blog posts displayed with previews on the main blog page
- Semantic HTML structure - Individual blog post pages with full content
- Custom CSS with modern styling techniques - Navigation between posts
## Project Structure ## Project Structure
``` ```
catcrafts.net/ project.json - Build configuration
├── interfaces/ # Interface definitions interfaces/ - Interface definitions
│ ├── Catcrafts.cppm implementations/ - Implementation files
│ ├── Catcrafts-Views.cppm styles/styles.css - Styling
│ ├── Catcrafts-Component.cppm run.sh - Run script
│ └── Catcrafts-Blog.cppm README.md - This file
├── implementations/ # Implementation files LICENSE - License information
│ ├── Catcrafts-Blog.cpp
│ └── main.cpp
├── styles.css # Modern CSS styling
├── project.json # Build configuration
├── run.sh # Development server script
└── build.sh # Build script
``` ```
## Blog Implementation Details
### Blog Previews
On the main `/blog` page, each blog post is displayed with:
- Title and date
- Preview of the content (first 200 characters)
- "Read Full Post" button to view the complete article
### Individual Blog Pages
Each blog post has its own URL:
- `/blog/0` - First blog post
- `/blog/1` - Second blog post
- And so on...
When visiting these URLs, users see the complete blog post content.
## Building and Running ## Building and Running
### Prerequisites To build and run the project:
- C++ compiler with WebAssembly support
- Caddy server for serving files
### Quick Start
1. Make the scripts executable:
```bash
chmod +x run.sh build.sh
```
2. Run the development server:
```bash ```bash
./run.sh ./run.sh
``` ```
The website will be available at `http://localhost:8085` This will compile the project and serve it locally.
### Building Manually ## Contributing
```bash This project is open-source and made available for viewing purposes only. No permission is granted to copy, modify, distribute, or create derivative works.
./build.sh
```
## Technologies Used
- **C++**: Core application logic
- **Crafter.CppDOM**: DOM manipulation library
- **WebAssembly**: Runtime environment
- **CSS3**: Modern styling with animations and gradients
- **HTML5**: Semantic markup
## Customization
To add new blog posts:
1. Edit `interfaces/Catcrafts-Blog.cppm`
2. Add new entries to the `posts` vector
To modify styling:
1. Edit `styles.css`
2. Adjust colors, fonts, and layout as needed
## License
This project is licensed under the MIT License - see the LICENSE file for details.

View file

@ -8,23 +8,73 @@ No permission is granted to copy, modify, distribute, or create derivative works
export module Catcrafts:Blog_impl; export module Catcrafts:Blog_impl;
import :Blog; import :Blog;
import :Root;
import :Views;
import std; import std;
namespace Catcrafts { using namespace Crafter::CppDOMBindings;
std::string RenderBlog() {
export namespace Catcrafts {
std::vector<HtmlElementView>* blogButtons = new std::vector<HtmlElementView>();
void RenderBlog() {
delete blogButtons;
blogButtons = new std::vector<HtmlElementView>();
std::string html = ""; std::string html = "";
for(const BlogPost& post : *posts) { for(const BlogPost& post : *posts) {
// 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) + "...";
} else {
previewContent = previewContent.substr(0, 200) + "...";
}
}
html += std::format(R"( html += std::format(R"(
<div class="post fade-in"> <div class="post fade-in">
<div class="post-header"> <div class="post-header">
<h2 class="post-title">{}</h2> <h2 class="post-title"><a>{}</a></h2>
<span class="post-date">{}</span> <span class="post-date">{}</span>
</div> </div>
<div class="post-content"> <div class="post-content">
{} {}
</div> </div>
</div>)", post.name, post.date, post.content); <div class="post-footer">
<a id="blog-button-{}" class="btn">Read Full Post</a>
</div>
</div>)", post.name, post.date, previewContent, post.slug);
} }
return html; main->SetInnerHTML(html);
for(const BlogPost& post : *posts) {
HtmlElementView& view = blogButtons->emplace_back(std::format("blog-button-{}", post.slug));
view.AddClickListener([slug = post.slug](Crafter::MouseEvent e) {
PushState("{}", "", std::format("/blog/{}", slug));
RenderRoot(std::format("/blog/{}", slug));
});
}
}
void RenderBlogPost(const std::string_view slug) {
for(const BlogPost& post : *posts) {
if(post.slug == slug) {
main->SetInnerHTML(std::format(R"(
<div class="post fade-in">
<div class="post-header">
<h1 class="post-title">{}</h1>
<span class="post-date">{}</span>
</div>
<div class="post-content">
{}
</div>
</div>)", post.name, post.date, post.content));
return;
}
}
main->SetInnerHTML("<h1>Post Not Found</h1><p>The requested blog post could not be found.</p>");
} }
} }

View file

@ -0,0 +1,38 @@
/*
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:Root_impl;
import :Root;
import :Views;
import :Blog;
import std;
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>");
}
} else {
RenderBlog(); //default route
}
}
}

View file

@ -15,27 +15,6 @@ using namespace Crafter::CppDOMBindings;
HtmlElementView* blogButton; HtmlElementView* blogButton;
void RenderRoot(const std::string_view route) {
std::string pageContent;
if(route == "/blog") {
pageContent = RenderBlog();
} else {
pageContent = RenderBlog(); //default route
}
// Set body content
main->SetInnerHTML(pageContent);
// Update active nav link
// auto navLinks = document->GetElementsByClassName("nav-container")[0]->GetElementsByTagName("a");
// for(auto link : navLinks) {
// if(link->GetAttribute("id") == "blog-nav-button") {
// link->SetAttribute("class", "active");
// } else {
// link->SetAttribute("class", "");
// }
//}
}
int main() { int main() {
AddPopStateListener([]() { AddPopStateListener([]() {
RenderRoot(GetPathNameString()); RenderRoot(GetPathNameString());
@ -44,7 +23,7 @@ int main() {
blogButton = new HtmlElementView("blog-nav-button"); blogButton = new HtmlElementView("blog-nav-button");
blogButton->AddClickListener([](Crafter::MouseEvent e) { blogButton->AddClickListener([](Crafter::MouseEvent e) {
PushState("{}", "", "/blog"); PushState("{}", "", "/blog");
RenderRoot("blog"); RenderRoot("/blog");
}); });
RenderRoot("/"); RenderRoot("/");

View file

@ -14,15 +14,18 @@ using namespace Crafter;
export namespace Catcrafts { export namespace Catcrafts {
struct BlogPost { struct BlogPost {
std::string name; std::string name;
std::string slug;
std::string date; std::string date;
std::string content; std::string content;
}; };
std::vector<BlogPost>* posts = new std::vector<BlogPost>{ std::vector<BlogPost>* posts = new std::vector<BlogPost>{
{ {
"Hello World!", "Hello World!",
"hello-world",
"2025-11-12", "2025-11-12",
R"(Welcome to catcrafts.net!<br><br> This blog will mostly be dedicated to random tidbits i come across while working on my Crafter series of libraries, This website is fully written in C++ using the Crafter.CppDOM library.)" R"(Welcome to catcrafts.net!<br><br> This blog will mostly be dedicated to random tidbits i come across while working on my Crafter series of libraries, This website is fully written in C++ using the Crafter.CppDOM library.)"
} }
}; };
std::string RenderBlog(); void RenderBlog();
void RenderBlogPost(const std::string_view slug);
} }

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:Root;
import Crafter.CppDOM;
import std;
using namespace Crafter;
export namespace Catcrafts {
void RenderRoot(const std::string_view route);
}

View file

@ -8,4 +8,5 @@ No permission is granted to copy, modify, distribute, or create derivative works
export module Catcrafts; export module Catcrafts;
export import :Views; export import :Views;
export import :Blog; export import :Blog;
export import :Root;

View file

@ -3,10 +3,10 @@
"configurations": [ "configurations": [
{ {
"name": "executable", "name": "executable",
"interfaces": ["interfaces/Catcrafts", "interfaces/Catcrafts-Views", "interfaces/Catcrafts-Blog"], "interfaces": ["interfaces/Catcrafts", "interfaces/Catcrafts-Views", "interfaces/Catcrafts-Blog", "interfaces/Catcrafts-Root"],
"implementations": ["implementations/main", "implementations/Catcrafts-Blog"], "implementations": ["implementations/main", "implementations/Catcrafts-Blog", "implementations/Catcrafts-Root"],
"target": "wasm32-wasi", "target": "wasm32-wasi",
"additional_files": ["styles/style.css"], "additional_files": ["styles/styles.css"],
"dependencies": [ "dependencies": [
{ {
"path":"https://forgejo.catcrafts.net/Catcrafts/Crafter.CppDOM.git", "path":"https://forgejo.catcrafts.net/Catcrafts/Crafter.CppDOM.git",

View file

@ -222,6 +222,33 @@ main {
.post-content a:hover { .post-content a:hover {
text-decoration: underline; text-decoration: underline;
color: var(--secondary-color); color: var(--secondary-color);
}.post-content {
font-size: 1.1rem;
line-height: 1.8;
color: #333;
animation: fadeIn 0.6s ease-out 0.2s forwards;
opacity: 0;
}
.post-content p {
margin-bottom: 1.2rem;
}
.post-content a {
color: var(--primary-color);
text-decoration: none;
font-weight: 600;
transition: var(--transition);
}
.post-content a:hover {
text-decoration: underline;
color: var(--secondary-color);
}
.post-footer {
margin-top: 1.5rem;
text-align: right;
} }
.post-content ul { .post-content ul {