random commit message, read it yourself

This commit is contained in:
Breadway 2026-07-22 11:57:53 +08:00
commit 6fc834a27d
15 changed files with 776 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
public/

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Breadway Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

17
README.md Normal file
View file

@ -0,0 +1,17 @@
# breadway.dev
Personal portfolio site — built with [Zola](https://www.getzola.org/) and the Bread Design System.
## Developing
```bash
zola serve
```
## Building
```bash
zola build
```
Output goes to `public/`.

7
config.toml Normal file
View file

@ -0,0 +1,7 @@
base_url = "https://breadway.dev"
title = "breadway.dev"
description = "Projects by Breadway"
compile_sass = false
minify_html = true
generate_feeds = false
build_search_index = false

3
content/_index.md Normal file
View file

@ -0,0 +1,3 @@
+++
template = "index.html"
+++

154
data/projects.toml Normal file
View file

@ -0,0 +1,154 @@
[[category]]
name = "Bread Desktop Ecosystem"
[[category.project]]
name = "bos"
role = "distro"
lang = "rust"
description = "Arch-based Hyprland desktop distribution shipping the whole bread ecosystem preconfigured, via a Calamares installer."
tags = ["Lua", "Shell"]
source = "https://git.breadway.dev/Breadway/bos"
flagship = true
[[category.project]]
name = "bos-settings"
role = "settings app"
lang = "rust"
description = "GTK4 system settings app for BOS — configures every bread app plus core system settings."
tags = ["GTK4"]
source = "https://git.breadway.dev/Breadway/bos-settings"
[[category.project]]
name = "bread"
role = "daemon"
lang = "rust"
description = "Reactive automation daemon for Linux desktops — reacts to system state via a Lua scripting API over Hyprland, udev, power, network, and Bluetooth events."
tags = ["Lua"]
source = "https://git.breadway.dev/Breadway/bread"
flagship = true
[[category.project]]
name = "bread-ecosystem"
role = "registry"
lang = "rust"
description = "Package registry (\"bakery\") distributing the bread-ecosystem tools with a one-line installer, plus the shared bread-theme crate."
tags = []
source = "https://git.breadway.dev/Breadway/bread-ecosystem"
[[category.project]]
name = "breadbar"
role = "status bar"
lang = "rust"
description = "Minimal Hyprland status bar and notification daemon — workspaces, media widget, clock, system tray, OSDs."
tags = ["GTK4"]
source = "https://git.breadway.dev/Breadway/breadbar"
flagship = true
[[category.project]]
name = "breadbox"
role = "launcher"
lang = "rust"
description = "GTK4 app launcher for Hyprland and Wayland."
tags = ["GTK4"]
source = "https://git.breadway.dev/Breadway/breadbox"
[[category.project]]
name = "breadclip"
role = "clipboard"
lang = "rust"
description = "Wayland clipboard history manager for Hyprland — daemon plus a GTK4 popup."
tags = ["GTK4", "SQLite"]
source = "https://git.breadway.dev/Breadway/breadclip"
[[category.project]]
name = "breadcrumbs"
role = "wifi daemon"
lang = "rust"
description = "Profile-aware Wi-Fi state machine with Tailscale exit-node management and a self-healing watch daemon."
tags = []
source = "https://git.breadway.dev/Breadway/breadcrumbs"
[[category.project]]
name = "breadlock"
role = "locker"
lang = "rust"
description = "Session locker and greetd greeter for Hyprland — a replacement for hyprlock and tuigreet."
tags = []
source = "https://git.breadway.dev/Breadway/breadlock"
[[category.project]]
name = "breadmon"
role = "monitor tool"
lang = "rust"
description = "Terminal UI monitor manager for Hyprland — position, configure, and mirror displays live via hyprctl."
tags = []
source = "https://git.breadway.dev/Breadway/breadmon"
[[category.project]]
name = "breadpad"
role = "scratchpad"
lang = "rust"
description = "Quick-capture scratchpad and note viewer for Hyprland, with AI-powered classification, reminders, and snooze."
tags = []
source = "https://git.breadway.dev/Breadway/breadpad"
[[category.project]]
name = "breadpaper"
role = "wallpaper"
lang = "rust"
description = "Wallpaper manager for the bread desktop — sets wallpaper, generates a pywal palette, and reloads the bread theme."
tags = []
source = "https://git.breadway.dev/Breadway/breadpaper"
[[category.project]]
name = "breadsearch"
role = "search"
lang = "rust"
description = "Semantic document search for BOS — embeds and indexes docs, serves ranked results via a GTK4 overlay."
tags = ["Python", "ONNX"]
source = "https://git.breadway.dev/Breadway/breadsearch"
[[category.project]]
name = "breadshot"
role = "screenshot"
lang = "rust"
description = "Wayland screenshot utility for the bread ecosystem, wrapping grim/slurp/wl-copy with Hyprland-aware geometry."
tags = []
source = "https://git.breadway.dev/Breadway/breadshot"
[[category]]
name = "Hardware & Firmware"
[[category.project]]
name = "ESP32-P2P-Laser-Tag"
role = "firmware"
lang = "c++"
description = "Peer-to-peer laser tag firmware for ESP32 nodes — IR transmit/receive, HMAC-secured packets, per-device IDs."
tags = ["ESP32"]
source = "https://github.com/Breadway/ESP32-P2P-Laser-Tag"
[[category.project]]
name = "redox-layout-viewer"
role = "viewer"
lang = "rust"
description = "Reads a Redox keyboard's live Vial/VIA layout over raw HID and renders it as an on-screen keyboard with layer tracking."
tags = []
source = "https://github.com/Breadway/redox-layout-viewer"
[[category.project]]
name = "vial-qmk"
role = "firmware fork"
lang = "c"
description = "Personal fork of Vial QMK, pruned down to just the Redox keyboard build."
tags = ["QMK"]
source = "https://github.com/Breadway/vial-qmk"
[[category]]
name = "Misc & Utilities"
[[category.project]]
name = "mathspace-dark-mode"
role = "extension"
lang = "javascript"
description = "Browser extension that adds a dark-mode theme to Mathspace."
tags = []

101
static/css/base.css Normal file
View file

@ -0,0 +1,101 @@
@font-face {
font-family: "Varela Round";
src: url("/fonts/varela-round-regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "JetBrains Mono";
src: url("/fonts/jetbrains-mono-regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "JetBrains Mono";
src: url("/fonts/jetbrains-mono-bold.woff2") format("woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
}
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
}
body {
background: var(--bg);
color: var(--fg);
font-family: var(--font-sans);
font-size: 15px;
line-height: 1.65;
}
h1, h2 {
font-weight: 400;
margin: 0;
}
a {
color: var(--accent);
text-decoration: none;
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
::selection {
background: var(--accent-dim);
color: var(--accent);
}
:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
border-radius: var(--radius-tertiary);
}
html {
scrollbar-color: var(--surface-3) var(--bg);
scrollbar-width: thin;
}
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-track {
background: var(--bg);
}
::-webkit-scrollbar-thumb {
background: var(--surface-3);
border-radius: var(--radius-pill);
border: 2px solid var(--bg);
}
::-webkit-scrollbar-thumb:hover {
background: var(--accent-line);
}
@keyframes rise {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

318
static/css/layout.css Normal file
View file

@ -0,0 +1,318 @@
html {
background-color: var(--bg);
background-image:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.05'/%3E%3C/svg%3E"),
radial-gradient(680px 380px at 15% -120px, var(--bg-glow), transparent 65%),
radial-gradient(520px 320px at 100% 0%, rgba(250, 179, 135, 0.08), transparent 60%);
background-blend-mode: overlay, normal, normal;
background-attachment: fixed;
}
body {
max-width: 760px;
margin: 0 auto;
padding: 64px 24px 40px;
}
/* ---------- Header ---------- */
.site-header {
margin-bottom: 40px;
animation: rise 550ms ease-out both;
}
.eyebrow {
display: block;
font-family: var(--font-mono);
font-size: 12px;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: var(--space-lg);
}
.brand {
display: flex;
align-items: center;
gap: var(--space-md);
}
.mark {
color: var(--accent);
flex: none;
}
.site-header h1 {
font-family: var(--font-mono);
font-weight: 700;
font-size: clamp(30px, 6vw, 42px);
letter-spacing: -0.01em;
color: var(--fg);
}
.tagline {
color: var(--muted);
font-size: 15px;
max-width: 46ch;
margin: var(--space-lg) 0 0;
}
.stats {
font-family: var(--font-mono);
font-size: 12px;
color: var(--muted);
margin: var(--space-sm) 0 0;
font-variant-numeric: tabular-nums;
}
.stats strong {
color: var(--accent);
font-weight: 700;
}
/* ---------- Featured ---------- */
.featured {
margin-bottom: 48px;
animation: rise 550ms ease-out both;
animation-delay: 30ms;
}
.featured-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-md);
}
.feature-card {
padding: var(--space-lg);
background: rgba(24, 24, 37, 0.72);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid var(--border);
border-left: 3px solid var(--accent);
border-radius: var(--radius-primary);
box-shadow: var(--shadow-ambient);
}
.feature-card .entry-name {
font-size: 16px;
}
.feature-card .entry-desc {
margin-bottom: 0;
font-size: 13px;
}
/* ---------- Category groups ---------- */
.group {
margin-bottom: 48px;
animation: rise 550ms ease-out both;
}
.group:nth-of-type(1) { animation-delay: 60ms; }
.group:nth-of-type(2) { animation-delay: 120ms; }
.group:nth-of-type(3) { animation-delay: 180ms; }
.group:nth-of-type(4) { animation-delay: 240ms; }
.cat-ecosystem { --cat-color: var(--accent); }
.cat-hardware { --cat-color: #74c7ec; }
.cat-misc { --cat-color: #a6e3a1; }
.group-head {
position: relative;
display: flex;
align-items: center;
gap: var(--space-sm);
padding-bottom: var(--space-md);
margin-bottom: var(--space-lg);
}
.group-head::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 1px;
background: linear-gradient(90deg, var(--cat-color, var(--border)), transparent 85%);
opacity: 0.5;
}
.group-icon {
color: var(--cat-color, var(--accent));
flex: none;
}
.group h2 {
font-family: var(--font-mono);
font-size: 13px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--fg);
}
.group-count {
font-family: var(--font-mono);
font-size: 11px;
color: var(--cat-color, var(--muted));
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: var(--radius-pill);
padding: 1px var(--space-sm);
font-variant-numeric: tabular-nums;
}
/* ---------- Entries ---------- */
.entries {
display: flex;
flex-direction: column;
gap: var(--space-md);
}
.entry {
position: relative;
padding: var(--space-lg);
background: rgba(24, 24, 37, 0.72);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid var(--border);
border-left: 3px solid transparent;
border-radius: var(--radius-primary);
box-shadow: var(--shadow-ambient);
transition: border-color 150ms ease, background 150ms ease, box-shadow 150ms ease;
}
.entry:hover {
border-color: var(--border);
border-left-color: var(--cat-color, var(--accent));
background: rgba(36, 36, 58, 0.78);
box-shadow: var(--shadow-hover);
}
.entry-head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: var(--space-md);
}
.entry-name {
font-family: var(--font-mono);
font-weight: 700;
font-size: 15px;
color: var(--fg);
}
.entry-name::before {
content: "./";
color: var(--muted);
font-weight: 400;
}
.entry-meta {
font-family: var(--font-mono);
font-size: 11px;
color: var(--muted);
margin-top: 2px;
}
.entry-meta .lang {
color: var(--cat-color, var(--accent));
}
.entry-meta .sep {
margin: 0 var(--space-xs);
opacity: 0.6;
}
.entry-source {
font-family: var(--font-mono);
font-size: 12px;
color: var(--muted);
white-space: nowrap;
padding: 3px var(--space-sm);
border-radius: var(--radius-pill);
border: 1px solid var(--border);
transition: color 150ms ease, border-color 150ms ease, background 150ms ease;
}
.entry-source::after {
content: " \2192";
}
.entry-source:hover {
color: var(--cat-color, var(--accent));
border-color: var(--accent-line);
background: var(--accent-dim);
}
.entry-desc {
margin: var(--space-sm) 0 var(--space-md);
color: var(--muted);
}
.tags {
display: flex;
flex-wrap: wrap;
gap: var(--space-xs);
}
.tag {
font-family: var(--font-mono);
font-size: 11px;
color: var(--accent);
background: var(--accent-dim);
border: 1px solid var(--accent-line);
border-radius: var(--radius-pill);
padding: 2px var(--space-sm);
}
/* ---------- Footer ---------- */
.site-footer {
margin-top: 56px;
padding-top: var(--space-lg);
border-top: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: baseline;
font-family: var(--font-mono);
color: var(--muted);
font-size: 12px;
}
@media (max-width: 640px) {
body {
padding: 40px 16px 32px;
}
.featured-grid {
grid-template-columns: 1fr;
}
.entry-head {
flex-direction: column;
gap: var(--space-sm);
}
.site-footer {
flex-direction: column;
gap: var(--space-xs);
}
}
@media (prefers-reduced-motion: reduce) {
.site-header,
.featured,
.group {
animation: none;
}
.entry {
transition: none;
}
}

30
static/css/tokens.css Normal file
View file

@ -0,0 +1,30 @@
:root {
--bg: #1e1e2e;
--bg-glow: #2a2440;
--fg: #cdd6f4;
--surface: #181825;
--surface-2: #24243a;
--surface-3: #2e2e46;
--accent: #fab387;
--accent-dim: rgba(250, 179, 135, 0.12);
--accent-line: rgba(250, 179, 135, 0.28);
--muted: #9399b2;
--border: #313147;
--font-sans: "Varela Round", sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, monospace;
--space-xs: 4px;
--space-sm: 8px;
--space-md: 12px;
--space-lg: 16px;
--space-xl: 20px;
--radius-primary: 8px;
--radius-secondary: 6px;
--radius-tertiary: 4px;
--radius-pill: 999px;
--shadow-ambient: 0 1px 2px rgba(0, 0, 0, 0.24);
--shadow-hover: 0 12px 28px -10px rgba(250, 179, 135, 0.28);
}

4
static/favicon.svg Normal file
View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="7" fill="#181825"/>
<text x="16" y="23" font-family="monospace" font-weight="700" font-size="20" fill="#fab387" text-anchor="middle">b</text>
</svg>

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

16
templates/base.html Normal file
View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}breadway.dev{% endblock title %}</title>
<meta name="description" content="Projects by Breadway">
<link rel="icon" type="image/svg+xml" href="{{ get_url(path="favicon.svg") }}">
<link rel="stylesheet" href="{{ get_url(path="css/tokens.css") }}">
<link rel="stylesheet" href="{{ get_url(path="css/base.css") }}">
<link rel="stylesheet" href="{{ get_url(path="css/layout.css") }}">
</head>
<body>
{% block content %}{% endblock content %}
</body>
</html>

104
templates/index.html Normal file
View file

@ -0,0 +1,104 @@
{% extends "base.html" %}
{% block content %}
{% set data = load_data(path="data/projects.toml") %}
{% set_global total = 0 %}
{% set_global flagships = [] %}
{% for category in data.category %}
{% set n = category.project | length %}
{% set_global total = total + n %}
{% for p in category.project %}
{% if p.flagship %}{% set_global flagships = flagships | concat(with=[p]) %}{% endif %}
{% endfor %}
{% endfor %}
<header class="site-header">
<span class="eyebrow">~/breadway.dev</span>
<div class="brand">
<svg class="mark" width="34" height="34" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path d="M4 20c0-7.2 4.5-12 10-12s10 4.8 10 12" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/>
<path d="M4 20h20" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/>
<path d="M10 11l2 3M14 9.5l2 4M18 11l2 3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
</svg>
<h1>Breadway</h1>
</div>
<p class="tagline">Personal projects — mostly Rust tooling for a Hyprland desktop, plus whatever else comes up.</p>
<p class="stats"><strong>{{ total }}</strong> projects across <strong>{{ data.category | length }}</strong> categories</p>
</header>
<section class="featured">
<div class="featured-grid">
{% for p in flagships %}
<div class="feature-card">
<div class="entry-head">
<span class="entry-name">{{ p.name }}</span>
{% if p.source %}<a class="entry-source" href="{{ p.source }}">source</a>{% endif %}
</div>
<div class="entry-meta"><span class="lang">{{ p.lang }}</span><span class="sep">·</span><span class="role">{{ p.role }}</span></div>
<p class="entry-desc">{{ p.description }}</p>
</div>
{% endfor %}
</div>
</section>
<main>
{% for category in data.category %}
{% if category.name == "Bread Desktop Ecosystem" %}{% set cat_class = "cat-ecosystem" %}
{% elif category.name == "Hardware & Firmware" %}{% set cat_class = "cat-hardware" %}
{% else %}{% set cat_class = "cat-misc" %}{% endif %}
<section class="group {{ cat_class }}">
<div class="group-head">
{% if category.name == "Bread Desktop Ecosystem" %}
<svg class="group-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="4 17 10 11 4 5"></polyline>
<line x1="12" y1="19" x2="20" y2="19"></line>
</svg>
{% elif category.name == "Hardware & Firmware" %}
<svg class="group-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<rect x="6" y="6" width="12" height="12" rx="1"></rect>
<rect x="9" y="9" width="6" height="6"></rect>
<line x1="9" y1="1" x2="9" y2="4"></line>
<line x1="15" y1="1" x2="15" y2="4"></line>
<line x1="9" y1="20" x2="9" y2="23"></line>
<line x1="15" y1="20" x2="15" y2="23"></line>
<line x1="20" y1="9" x2="23" y2="9"></line>
<line x1="20" y1="15" x2="23" y2="15"></line>
<line x1="1" y1="9" x2="4" y2="9"></line>
<line x1="1" y1="15" x2="4" y2="15"></line>
</svg>
{% else %}
<svg class="group-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94z"></path>
</svg>
{% endif %}
<h2>{{ category.name }}</h2>
<span class="group-count">{{ category.project | length }}</span>
</div>
<ul class="entries">
{% for p in category.project %}
{% if not p.flagship %}
<li class="entry">
<div class="entry-head">
<span class="entry-name">{{ p.name }}</span>
{% if p.source %}<a class="entry-source" href="{{ p.source }}">source</a>{% endif %}
</div>
<div class="entry-meta"><span class="lang">{{ p.lang }}</span><span class="sep">·</span><span class="role">{{ p.role }}</span></div>
<p class="entry-desc">{{ p.description }}</p>
{% if p.tags and p.tags | length > 0 %}
<div class="tags">
{% for t in p.tags %}<span class="tag">{{ t }}</span>{% endfor %}
</div>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
</section>
{% endfor %}
</main>
<footer class="site-footer">
<span>Breadway</span>
<span>built with Zola</span>
</footer>
{% endblock content %}