Compare commits

...

23 Commits

Author SHA1 Message Date
53e62fcb68 I just bumped the version so I could test.
All checks were successful
Build Tavernworks / Explore-Gitea-Actions (push) Successful in 3m22s
I needed a fast test to make sure the Gitea server repository was
working.
2025-10-21 12:17:38 -04:00
04c2182705 The logos for the project.
All checks were successful
Build Tavernworks / Explore-Gitea-Actions (push) Successful in 2m41s
2025-10-17 10:29:41 -04:00
f023f36558 Adjusted the responsive layout targets.
All checks were successful
Build Tavernworks / Explore-Gitea-Actions (push) Successful in 3m11s
2025-10-16 18:47:20 -04:00
62a2c4f5bc Fixed a spelling mistake in the blog css.
All checks were successful
Build Tavernworks / Explore-Gitea-Actions (push) Successful in 3m11s
2025-10-16 18:32:01 -04:00
87bb1459b8 Bumped the bard version for publishing.
All checks were successful
Build Tavernworks / Explore-Gitea-Actions (push) Successful in 3m49s
2025-10-16 13:58:36 -04:00
8dd20cb0cb Fixed responsiveness of the blog.
All checks were successful
Build Tavernworks / Explore-Gitea-Actions (push) Successful in 2m44s
Also added the publish date to a post.
2025-10-16 13:56:58 -04:00
55f1ac22b0 Changed the target dir tot he home directory bin.
All checks were successful
Build Tavernworks / Explore-Gitea-Actions (push) Successful in 2m37s
2025-10-11 20:43:47 -04:00
c3ae332b0b Changed the target directory to place executables.
Some checks failed
Build Tavernworks / Explore-Gitea-Actions (push) Failing after 2m38s
2025-10-11 20:34:23 -04:00
dede2e9072 Fixed the build script to use this repository.
Some checks failed
Build Tavernworks / Explore-Gitea-Actions (push) Failing after 2m42s
2025-10-11 20:27:26 -04:00
4c03aea947 Fixing the build script for gitea.
Some checks failed
Build Tavernworks / Explore-Gitea-Actions (push) Failing after 1m49s
2025-10-11 20:19:40 -04:00
ee82498161 Adding the build testing script.
Some checks failed
Build Tavernworks / Explore-Gitea-Actions (push) Failing after 7s
This will also install loreweaver for use by the blog.
2025-10-11 18:43:17 -04:00
d822ecdf84 Fixed up the OpenGraph meta. 2025-10-11 18:38:46 -04:00
e8a4c19fdb Post page now is completely done in SSR.
This was done to make loading time faster as it's all static information
and to make it so blog posts can be linked to and get OpenGraph data.
2025-10-10 11:19:14 -04:00
1a57f3d143 openGraph image now set properly to default image. 2025-10-10 01:24:41 -04:00
1a82e856d9 use_resource is the correct thing to use.
The top components can use_resource and be properly setup during
hydration. The sub components need to use_server_resource so that they
can be hydrated later.
2025-10-10 00:26:17 -04:00
e49ecbac4d The TagSelector needed to stay a use_resource. 2025-10-10 00:01:31 -04:00
bca3e9f939 Switching all server function calls.
Now server function calls use, use_server_future. It seems to be working
now, unlike in 0.6.3
2025-10-09 23:49:35 -04:00
3fe3d874ca Added the ability to specify images.
The blog title can now be set to be an image. If not it will default to
the text. If no text it will just be "Blog".

A Post has now been given a default post image that will be used for all
posts for their openGraph sharing. This can later be expanded to allow
a blog post to have a desired image.
2025-10-09 17:00:56 -04:00
725824003b Updated to use the new Dioxus v0.7.0-rc.1
The system is using the nightly toolchain now due to Dioxus needing it.
2025-10-08 15:17:48 -04:00
ce80af94ee Cleaned up some Author related displays.
It was determined to use the Author's desired name and no handle. The
handle will only be used for unique linking.

Also, the CyberMages LLC was dropped from the Author metadata until a
company can be specified per Author. Not all users will be associated
with CyberMages.
2025-10-07 11:31:07 -04:00
cba2e95290 Doing some overall warning cleanup. 2025-09-28 20:25:19 -04:00
468b9449b2 Client side settings can now be specified.
Here we use a context provider on the app itself to handle passing the
settings to our library. This way even the layout can use them.
2025-09-28 15:12:03 -04:00
6efec6bd22 TagList and TagItem are now used in the post nav.
The TagItem is now used properly in the post nav area. Only the
ToggleTags are different now.

Bumped the patch version.
2025-09-27 14:22:26 -04:00
27 changed files with 1986 additions and 764 deletions

View File

@ -0,0 +1,41 @@
name: Build Tavernworks
run-name: Building on Silverymoon by ${{ gitea.actor }}.
on:
push:
branches:
- main
jobs:
Explore-Gitea-Actions:
runs-on: FreeBSD-14.1
env:
SOURCE_DIR: tavernworks
TARGET_DIR: /home/gitea/bin
steps:
- name: Setup Workspace
run: |
rm -rf ${{ github.workspace }}/*
echo "Source Dir: $SOURCE_DIR"
- name: Clone Repo
uses: https://gitea.com/actions/checkout@v4
with:
repository: CyberMages/tavernworks
token: ${{ secrets.CM_GIT_TOKEN }}
path: ${{ env.SOURCE_DIR }}
- name: Build Repo
run: |
pwd
ls -la
cd "$SOURCE_DIR"
echo "Current Shell: $SHELL"
echo "PATH=$PATH"
. ~/.profile
echo "PATH=$PATH"
echo "Building release."
cargo build --release
echo "Installing loreweaver."
cp ./target/release/loreweaver "$TARGET_DIR"
- name: Clean Workspace
run: |
rm -rf ${{ github.workspace }}/*
ls -la

2058
Cargo.lock generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 KiB

View File

@ -1,6 +1,6 @@
[package]
name = "bard"
version = "0.3.1"
version = "0.3.17"
edition = "2024"
description = "Dioxus components that will display a Tavern blogging system Blog."
repository = "/CyberMages/tavern"
@ -14,12 +14,12 @@ name = "blog"
path = "examples/blog.rs"
[dependencies]
dioxus = { version = "*", features = ["router", "fullstack"] }
dioxus = { version = "=0.7.0-rc.1", features = ["router", "fullstack"] }
tavern = { version = "0.3.0", path = "../tavern", registry="cybermages", optional = true}
tokio = { version = "1.0", features = ["rt", "macros"], optional = true }
[features]
default = ["web"]
default = ["tavern"]
web = ["tavern", "dioxus/web"]
server = ["tavern/database", "dioxus/server", "tokio"]

View File

@ -35,11 +35,22 @@
.blog_list
{
display: flex;
flex-direction: column;
width: 100%;
order: 5;
.blog_title
{
margin-top: 0px;
color: var(--accent-color);
}
.blog_title_image
{
width: 23rem;
align-self: center;
margin-bottom: 25px;
}
.blog_item
@ -50,6 +61,16 @@
{
margin: 0px;
color: var(--accent-color);
a:link, a:visited
{
color: var(--accent-color);
}
a:hover
{
color: var(--text-color);
}
}
p
@ -89,16 +110,24 @@
margin-bottom: 20px;
}
.embeded_video
.blog_post_tale
{
margin-top: 50px;
display: flex;
align-content: center;
justify-content: center;
iframe
pre
{
box-shadow: 0 0 80px var(--accent-color);
padding-left: 20px;
}
.embedded_video
{
margin-top: 50px;
display: flex;
align-content: center;
justify-content: center;
iframe
{
box-shadow: 0 0 80px var(--accent-color);
}
}
}
}
@ -131,8 +160,11 @@
order: 1;
display: flex;
flex-direction: column;
margin-top: 70px;
margin-top: 185px;
margin-right: 70px;
position:sticky;
top: 150px;
height: 50vh;
fieldset
{
@ -144,9 +176,10 @@
legend
{
min-width: 120px;
min-width: 130px;
padding: 0;
margin: 0;
font-weight: bold;
}
.tag_list
@ -155,40 +188,99 @@
flex-direction: column;
}
.toggle_button {
display: inline-block;
margin-top: 5px;
margin-right: 5px;
padding: 0px 6px;
border: 2px solid var(--text-color);
border-radius: 5px;
color: var(--text-color);
cursor: pointer;
user-select: none;
transition: 0.1s ease-in;
}
.toggle_button:hover {
color: var(--accent-color);
border-color: var(--accent-color);
transform: translateY(-2px);
}
.toggle_button input[type="checkbox"] {
position: absolute;
opacity: 0;
width: 0;
height: 0;
pointer-events: none;
}
/* Checked state using :has() — modern browsers only */
.toggle_button:has(input:checked) {
color: var(--accent-color);
border-color: var(--accent-color);
}
@container site (max-width: 1230px)
{
display: none;
}
}
.tag_list
.tag_list
{
list-style: none;
padding: 0px;
margin: 0px;
.tag_item
{
list-style: none;
padding: 0px;
margin: 0px;
margin-top: 5px;
margin-right: 5px;
.tag_item
a
{
margin-top: 5px;
margin-right: 5px;
display: inline-block;
padding: 0px 4px;
text-decoration: none;
border: 2px solid var(--text-color);
border-radius: 5px;
a
color: var(--text-color);
transition: 0.1s ease-in;
&:hover
{
display: block;
padding: 0px 4px;
text-decoration: none;
border: 2px solid var(--text-color);
border-radius: 5px;
color: var(--text-color);
transition: 0.1s ease-in;
&:hover
{
color: var(--accent-color);
border: 2px solid var(--accent-color);
transform: translateY(-2px);
}
color: var(--accent-color);
border: 2px solid var(--accent-color);
transform: translateY(-2px);
}
}
}
}
.video_link
{
display: none;
width: 100%;
justify-content: center;
}
@media screen and (max-width: 1000px)
{
.blog_post .blog_post_tale
{
.video_link
{
display: flex;
}
.embedded_video
{
display: none;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

View File

@ -1,35 +1,27 @@
use std::borrow::Borrow;
use std::collections::HashSet;
use std::hash::Hash;
use dioxus::prelude::*;
use tavern::{Adventurer, Legend, Lore, Tale};
use crate::togglable::Togglable;
use crate::page::Page;
use crate::server::*;
use crate::settings::{BardSettings};
use super::tags::*;
#[component]
pub fn BlogAuthor() -> Element
{
rsx!
{
section
{
class: "blog_author_style",
}
}
}
#[component]
pub fn BlogItem(title: String, slug: String, author: String, summary: String,
tags: Vec<String>)
-> Element
{
let author_future = use_server_future(move ||
{
let handle: String = author.clone();
async move { get_author(handle).await }
})?;
rsx!
{
article
@ -57,7 +49,24 @@ pub fn BlogItem(title: String, slug: String, author: String, summary: String,
}
}
p { b { "Author: {author}" } }
if let Some(Ok(Some(adventurer))) = (author_future.value())()
{
p
{
b
{
"Author: "
a
{
href: "{adventurer.legend.profile}", "{adventurer.name}"
}
}
}
}
else
{
p { "Loading author..." }
}
p { "{summary}" }
}
@ -68,13 +77,16 @@ pub fn BlogItem(title: String, slug: String, author: String, summary: String,
#[component]
pub fn BlogList(tags: Signal<HashSet<String>>, children: Element) -> Element
{
let list = use_server_future(move ||
// Retrieve the provided settings from context.
let settings = use_context::<BardSettings>();
let list = use_resource(move ||
{
let t = tags();
let categories = t.iter().cloned().collect();
let tags = tags();
let categories: Vec<String> = tags.iter().cloned().collect();
async move { get_blog_list(categories).await }
})?;
});
rsx!
{
@ -82,7 +94,54 @@ pub fn BlogList(tags: Signal<HashSet<String>>, children: Element) -> Element
{
class: "blog_list",
h1 { class: "blog_title", "Runes & Ramblings" }
match settings.blog_image
{
Some(image) =>
{
match settings.blog_name
{
Some(title) =>
{
rsx!
{
h1 { class: "blog_title visually_hidden", "{title}" }
img { class: "blog_title_image", alt: "{title} blog logo", src: image }
}
}
None =>
{
rsx!
{
h1 { class: "blog_title visually_hidden", "Blog" }
img { class: "blog_title_image", alt: "Blog logo", src: image }
}
}
}
}
None =>
{
match settings.blog_name
{
Some(title) =>
{
rsx!
{
h1 { class: "blog_title", "{title}" }
}
}
None =>
{
rsx!
{
h1 { class: "blog_title visually_hidden", "Blog" }
}
}
}
}
}
if let Some(Ok(lores)) = &*list.read()
{
@ -90,6 +149,7 @@ pub fn BlogList(tags: Signal<HashSet<String>>, children: Element) -> Element
{
BlogItem
{
key: "{lore.slug}",
title: lore.title.clone(),
slug: lore.slug.clone(),
author: lore.author.clone(),
@ -98,10 +158,9 @@ pub fn BlogList(tags: Signal<HashSet<String>>, children: Element) -> Element
}
}
}
else if let Some(Err(e)) = &*list.read()
else if let Some(Err(_e)) = &*list.read()
{
p { "Unable to show post header." }
p { "{e}" }
p { "Please choose a category to see related blog posts." }
}
else
{
@ -117,6 +176,7 @@ pub fn BlogList(tags: Signal<HashSet<String>>, children: Element) -> Element
#[component]
pub fn ToggleTag(tag: String, toggled_tags: Signal<HashSet<String>>) -> Element
{
let is_checked = toggled_tags.read().is_toggled(&tag);
rsx!
{
label
@ -126,7 +186,7 @@ pub fn ToggleTag(tag: String, toggled_tags: Signal<HashSet<String>>) -> Element
input
{
r#type: "checkbox",
checked: toggled_tags.read().is_toggled(&tag),
checked: is_checked,
onchange: move |_|
{
toggled_tags.write().toggle(&tag.clone());
@ -155,7 +215,7 @@ pub fn ToggleTag(tag: String, toggled_tags: Signal<HashSet<String>>) -> Element
// which tags should be selected based on the current URL, returning both pieces
// of data together for clean state management.
#[component]
pub fn TagSelector(url_tag: ReadOnlySignal<String>,
pub fn TagSelector(url_tag: ReadSignal<String>,
toggled_tags: Signal<HashSet<String>>) -> Element
{
// Use use_resource to handle both fetching tags AND initializing selection

View File

@ -1,12 +1,9 @@
use std::borrow::Borrow;
use std::collections::HashSet;
use std::hash::Hash;
use tavern::{Adventurer, Tale};
use dioxus::prelude::*;
use tavern::{Adventurer, Legend, Lore, Tale};
use crate::togglable::Togglable;
use crate::page::Page;
use crate::settings::BardSettings;
use crate::server::*;
use super::tags::*;
@ -26,33 +23,87 @@ pub fn BlogAuthor() -> Element
#[component]
pub fn PostHeaderAuthor(adventurer: Adventurer) -> Element
pub fn PostHeaderAuthor(author: Option<Adventurer>, publish_date: String) -> Element
{
rsx!
match author
{
p
Some(adventurer) =>
{
b
rsx!
{
"Author: ",
a { href: "{adventurer.legend.profile}", "{adventurer.name} @{adventurer.handle}" }
document::Meta
{
name: "author",
content: "{adventurer.name}"
}
i
{
"Published: {publish_date}"
}
p
{
b
{
"Author: ",
a { href: "{adventurer.legend.profile}", "{adventurer.name}" }
}
}
}
}
None=>
{
rsx!
{
document::Meta
{
name: "author",
content: "Unknown"
}
i
{
"Published: {publish_date}"
}
p
{
b
{
"Author: Unknown"
}
}
}
}
}
}
#[component]
pub fn PostHeader(tale: Tale) -> Element
pub fn PostHeader(tale: Tale, adventurer: Option<Adventurer>) -> Element
{
let author_future = use_server_future(move ||
{
let handle: String = tale.lore.author.clone();
// Get the pages URL.
let url: Page = use_route();
async move { get_author(handle).await }
})?;
// Get the blog's image to use as a placeholder if there isn't one for the
// blog post.
// Retrieve the provided settings from context.
let settings = use_context::<BardSettings>();
rsx!
{
// Open Graph (used by LinkedIn, Bluesky, Discord, etc.)
document::Meta { property: "og:locale", content: "en_US" }
document::Meta { name: "title", property: "og:title", content: "{tale.lore.title}" }
document::Meta { name: "description", property: "og:description", content: "{tale.lore.summary}" }
document::Meta { property: "og:type", content: "article" }
document::Meta { property: "og:url", content: "{url}" }
if let Some(image) = settings.default_post_image
{
document::Meta { name: "image", property: "og:image", content: "{image}" }
}
h1 { {tale.lore.title} }
TagList
@ -66,13 +117,10 @@ pub fn PostHeader(tale: Tale) -> Element
}
}
if let Some(Ok(Some(adventurer))) = (author_future.value())()
PostHeaderAuthor
{
PostHeaderAuthor { adventurer: adventurer }
}
else
{
p { "Loading author..." }
author: adventurer,
publish_date: tale.lore.publish_date.format("%m/%d/%Y").to_string()
}
}
}
@ -82,7 +130,12 @@ pub fn Story(text: String) -> Element
{
rsx!
{
div { dangerous_inner_html: "{text}" }
div
{
class: "blog_post_tale",
dangerous_inner_html: "{text}"
}
}
}
@ -95,7 +148,12 @@ pub fn BlogPost(slug: Signal<String>, children: Element) -> Element
// Make this reactive so that as the page changes it should rerun this.
let url_slug = slug();
async move { get_blog_post(url_slug).await }
async move
{
let post = get_blog_post(url_slug).await?;
let author = get_author(post.lore.author.clone()).await?;
Ok::<_, ServerFnError>((post, author))
}
})?;
rsx!
@ -104,11 +162,12 @@ pub fn BlogPost(slug: Signal<String>, children: Element) -> Element
{
class: "blog_post",
if let Some(Ok(tale)) = (post_future.value())()
if let Some(Ok((tale, adventurer))) = (post_future.value())()
{
PostHeader
{
tale: tale.clone()
tale: tale.clone(),
adventurer: adventurer.clone()
}
Story { text: tale.story }
@ -150,17 +209,16 @@ pub fn TagNav() -> Element
if let Some(Ok(categories)) = &*tags.read()
{
for tag in categories
TagList
{
Link
for tag in categories
{
TagItem
{
class: "tag_item tag_{tag}",
to: Page::Blog { tag: tag.clone() },
"{tag}"
tag: tag.clone()
}
}
}
}
else if let Some(Err(e)) = &*tags.read()
{

View File

@ -22,7 +22,7 @@ pub fn TagItem(tag: String, style: Option<String>) -> Element
Link
{
class: "{tag_style}",
class: "{tag_style} tag_{tag}",
to: Page::Blog { tag: tag.clone() },

View File

@ -6,6 +6,7 @@ mod components;
mod page;
mod pages;
mod server;
mod settings;
mod togglable;
@ -14,5 +15,6 @@ pub use crate::components::*;
pub use crate::info::{get_name, get_version};
pub use crate::page::Page;
pub use crate::pages::*;
pub use crate::settings::*;
#[cfg(feature = "server")]
pub use crate::server::*;

View File

@ -1,6 +1,7 @@
use dioxus::prelude::*;
use crate::pages::{Blog, Post, Root};
use crate::settings::{BardSettings, StylesheetBehavior};
@ -11,9 +12,38 @@ const BLOG_CSS: Asset = asset!("/assets/css/blog.css");
#[component]
fn BlogLayout() -> Element
{
// Retrieve the provided settings from context.
let settings = use_context::<BardSettings>();
rsx!
{
document::Stylesheet { href: BLOG_CSS }
match settings.stylesheet
{
StylesheetBehavior::Override(asset) =>
{
rsx!
{
document::Stylesheet { href: asset }
}
}
StylesheetBehavior::Extend(asset) =>
{
rsx!
{
document::Stylesheet { href: BLOG_CSS }
document::Stylesheet { href: asset }
}
}
StylesheetBehavior::None =>
{
rsx!
{
document::Stylesheet { href: BLOG_CSS }
}
}
}
Outlet::<Page> {}
}

View File

@ -3,15 +3,14 @@ use std::collections::HashSet;
use dioxus::prelude::*;
use crate::components::{BlogList, TagSelector};
use crate::page::Page;
/// Blog page
#[component]
pub fn Blog(tag: ReadOnlySignal<String>) -> Element
pub fn Blog(tag: ReadSignal<String>) -> Element
{
let mut categories: Signal<HashSet<String>> =
let categories: Signal<HashSet<String>> =
use_signal(|| HashSet::new());
rsx!

View File

@ -1,16 +1,15 @@
use dioxus::prelude::*;
use crate::components::{BlogPost, TagNav};
use crate::page::Page;
/// Blog page
#[component]
pub fn Post(slug: ReadOnlySignal<String>) -> Element
pub fn Post(slug: ReadSignal<String>) -> Element
{
// Create a copy of the current slug to detect changes.
let mut url_slug = use_signal(|| slug());
let url_slug = use_signal(|| slug());
rsx!
{

View File

@ -1,8 +1,5 @@
use std::collections::HashSet;
use dioxus::prelude::*;
use crate::page::Page;
use crate::pages::Blog;

46
bard/src/settings.rs Normal file
View File

@ -0,0 +1,46 @@
use dioxus::prelude::*;
#[derive(Copy, Clone)]
pub enum StylesheetBehavior
{
/// Overrides the default stylesheet.
Override(Asset),
/// Extends the default stylesheet. The library will load both.
Extend(Asset),
/// Uses the library's default stylesheet.
None
}
#[derive(Clone, Default)]
pub struct BardSettings
{
/// The name to use for the blog.
/// If None, then the name will be Blog, but made invisible so that
/// it is still available for screen readers.
pub blog_name: Option<String>,
/// The image to use for the blog title instead of just the name.
pub blog_image: Option<Asset>,
/// The image to use for the default post image in open graph. This is what
/// is shown when sharing the article.
pub default_post_image: Option<Asset>,
/// A user defined stylesheet and how the library should include it.
pub stylesheet: StylesheetBehavior
}
impl Default for StylesheetBehavior
{
fn default() -> Self
{
StylesheetBehavior::None
}
}

View File

@ -1,23 +1,22 @@
[package]
name = "blog_test"
version = "0.3.0"
version = "0.3.2"
authors = ["Myrddin Dundragon <myrddin@cybermages.tech>"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
axum = { version = "0.7.0", optional = true }
axum-server = { version = "0.7.1", optional = true }
dioxus = { version = "*", features = ["router", "fullstack"] }
dioxus-cli-config = { version = "*", optional = true }
axum = { version = "0.8.4", optional = true }
axum-server = { version = "0.7.2", optional = true }
dioxus = { version = "=0.7.0-rc.1", features = ["router", "fullstack"] }
bard = { version = "*", path="../bard", optional = true }
tokio = { version = "1.0", optional = true }
[features]
default = ["web"]
web = ["dioxus/web", "bard"]
server = ["dioxus/server", "axum", "axum-server", "tokio/rt-multi-thread", "tokio/macros", "dioxus-cli-config", "bard/server"]
default = ["bard"]
web = ["dioxus/web", "bard/web"]
server = ["dioxus/server", "axum", "axum-server", "tokio/rt-multi-thread", "tokio/macros", "bard/server"]
[profile.wasm-dev]
inherits = "dev"

45
blog_test/assets/blog.css Normal file
View File

@ -0,0 +1,45 @@
:root
{
--text-color: #000000;
--bg-color: #FFFFFF;
--primary-color: #ffffff;
--secondary-color: #000000;
--accent-color: #7dfdfe; /* Tron grid or try 00a8ff 00eaff 26b4ca 59b4c7 5584AC*/
--mobile-color: #363636;
--desktop-size: 1230px;
}
.toggle_button {
display: inline-block;
margin-top: 5px;
margin-right: 5px;
padding: 0px 6px;
border: 2px solid var(--text-color);
border-radius: 5px;
color: var(--text-color);
cursor: pointer;
user-select: none;
transition: 0.1s ease-in;
}
.toggle_button:hover {
color: var(--accent-color);
border-color: var(--accent-color);
transform: translateY(-2px);
}
.toggle_button input[type="checkbox"] {
position: absolute;
opacity: 0;
width: 0;
height: 0;
pointer-events: none;
}
/* Checked state using :has() — modern browsers only */
.toggle_button:has(input:checked) {
color: var(--accent-color);
border-color: var(--accent-color);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1019 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 KiB

32
blog_test/blog.css Normal file
View File

@ -0,0 +1,32 @@
.toggle_button {
display: inline-block;
margin-top: 5px;
margin-right: 5px;
padding: 0px 6px;
border: 2px solid var(--text-color);
border-radius: 5px;
color: var(--text-color);
cursor: pointer;
user-select: none;
transition: 0.1s ease-in;
}
.toggle_button:hover {
color: var(--accent-color);
border-color: var(--accent-color);
transform: translateY(-2px);
}
.toggle_button input[type="checkbox"] {
position: absolute;
opacity: 0;
width: 0;
height: 0;
pointer-events: none;
}
/* Checked state using :has() — modern browsers only */
.toggle_button:has(input:checked) {
color: var(--accent-color);
border-color: var(--accent-color);
}

View File

@ -1,27 +1,15 @@
use std::net::SocketAddr;
use dioxus::prelude::*;
#[cfg(feature = "server")]
use axum::Router;
#[cfg(feature = "server")]
use axum::ServiceExt;
#[cfg(feature = "server")]
use axum::extract::{Extension, Host};
#[cfg(feature = "server")]
use axum::http::uri::{Parts, Uri};
#[cfg(feature = "server")]
use axum::http::StatusCode;
#[cfg(feature = "server")]
use axum::response::{IntoResponse, Redirect};
#[cfg(feature = "server")]
use axum::routing::get;
use bard::*;
const FAVICON: Asset = asset!("/assets/favicon.ico");
const BLOG: Asset = asset!("/assets/blog.css");
const BLOG_IMAGE: Asset = asset!("/assets/runes_and_ramblings_text.png",
AssetOptions::builder().with_hash_suffix(false));
const POST_IMAGE: Asset = asset!("/assets/runes_and_ramblings_logo.png",
AssetOptions::builder().with_hash_suffix(false));
@ -29,18 +17,30 @@ fn main()
{
#[cfg(feature = "server")]
{
tokio::runtime::Runtime::new()
.unwrap()
.block_on(async move { bard::init_database("/home/myrddin/cybermages/website/tavern.db").await });
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async
{
let db_path = "/home/myrddin/cybermages/website/tavern.db";
let _ = bard::init_database(db_path).await;
});
}
#[cfg(feature = "web")]
dioxus::launch(App);
LaunchBuilder::new().launch(App);
}
#[component]
fn App() -> Element
{
let custom_settings = BardSettings
{
blog_name: Some(String::from("Blog Test")),
blog_image: Some(BLOG_IMAGE),
default_post_image: Some(POST_IMAGE),
stylesheet: StylesheetBehavior::Extend(BLOG),
};
provide_context(custom_settings);
rsx!
{
document::Link { rel: "icon", href: FAVICON }
@ -52,8 +52,7 @@ fn App() -> Element
#[component]
fn Home() -> Element
{
rsx!
{
rsx! {
h1 { "Blog Test" }
}
}
@ -62,8 +61,7 @@ fn Home() -> Element
#[component]
pub fn PageNotFound(route: Vec<String>) -> Element
{
rsx!
{
rsx! {
h1 { "Page not found" }
p { "We are terribly sorry, but the page you requested doesn't exist." }
pre { color: "red", "log:\nattemped to navigate to: {route:?}" }
@ -94,7 +92,6 @@ fn Navbar() -> Element
#[derive(Debug, Clone, Routable, PartialEq)]
#[rustfmt::skip]
pub enum Page

View File

@ -1,6 +1,6 @@
[package]
name = "loreweaver"
version = "0.3.0"
version = "0.3.2"
edition = "2024"
description = "Converts a blog repository into an SQLite database using the Tavern blog system."
repository = "/CyberMages/tavern"

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

View File

@ -3,12 +3,15 @@
/// The environment variable defined by Cargo for the name.
#[allow(dead_code)]
const NAME: Option<&str> = option_env!("CARGO_PKG_NAME");
/// The environment variable defined by Cargo for the version.
#[allow(dead_code)]
const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION");
/// The string to display if a value is not defined during compile time.
#[allow(dead_code)]
const NOT_DEFINED: &'static str = "UNDEFINED";
@ -17,6 +20,7 @@ const NOT_DEFINED: &'static str = "UNDEFINED";
/// set at compile time and comes from the Cargo.toml file.
///
/// If a value is not found, then it will return the not defined value.
#[allow(dead_code)]
pub fn get_name() -> &'static str
{
NAME.unwrap_or(NOT_DEFINED)
@ -27,6 +31,7 @@ pub fn get_name() -> &'static str
/// This is set at compile time and comes from the Cargo.toml file.
///
/// If a value is not found, then it will return the not defined value.
#[allow(dead_code)]
pub fn get_version() -> &'static str
{
VERSION.unwrap_or(NOT_DEFINED)

2
rust-toolchain.toml Normal file
View File

@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB