Compare commits

...

7 commits
v0.3.2 ... main

Author SHA1 Message Date
Breadway
df42aba1d3 CI: use /tmp for ecosystem clone, avoid permissions conflict
All checks were successful
Mirror to GitHub / mirror (push) Successful in 2s
2026-06-19 08:38:40 +08:00
Breadway
e0b55e1713 Bump bread-theme to v0.2.8 (live-reload fix)
Some checks failed
Mirror to GitHub / mirror (push) Successful in 2s
Build and publish package / package (push) Failing after 4m47s
2026-06-17 12:55:57 +08:00
Breadway
c30aa2497e Fix illegible text on light pywal palettes + hot-reload
Use bread-theme 0.2.7's luminance-picked ink (@on-*): type chips on @overlay and
selected sidebar rows / confirm buttons on @blue kept @fg or @bg, which vanished
when those slots came out light/dark. They now use @on-overlay / @on-accent.

Add breadpad_shared::theme::apply_live (wraps bread_theme::gtk::apply_app_css) so
breadpad and breadman recolour live on `bread-theme reload` and re-read the user's
style.css — replacing the build-once provider. bread-theme bumped to v0.2.7
(gtk feature).
2026-06-17 12:42:12 +08:00
Breadway
dfe19708ba Release 0.3.4: shared bread-theme stylesheet (overlay=color7)
Some checks failed
Mirror to GitHub / mirror (push) Successful in 2s
Build and publish package / package (push) Failing after 4m33s
2026-06-16 18:35:33 +08:00
Breadway
08d8956eac docs: add CalDAV calendar-sync walkthrough
The [calendar] config keys existed without explanation. Document enabling
CalDAV sync end to end: finding the collection URL, creating an app password,
the config block, and the best-effort sync behaviour.
2026-06-16 17:07:06 +08:00
Breadway
49f6966d9c theme: build on the shared bread-theme stylesheet
build_css() now starts from bread_theme::stylesheet(palette) and appends only
breadpad/breadman-specific components. This unifies fonts, palette, and generic
widgets with the rest of the ecosystem and fixes the colour mapping (overlay is
now color7, matching every other app, not color0). Bump bread-theme to v0.2.6.
2026-06-16 16:57:16 +08:00
Breadway
eab3775de1 Disable debug package so the main package publishes correctly
Some checks failed
Mirror to GitHub / mirror (push) Successful in 2s
Build and publish package / package (push) Failing after 3m53s
makepkg's debug split produced a -debug pkg; the upload's head -1 could
grab it instead of the main package. !debug yields a single package.
2026-06-13 23:00:52 +08:00
9 changed files with 100 additions and 128 deletions

View file

@ -9,7 +9,7 @@ permissions:
env: env:
DL_DIR: /srv/breadway-dl DL_DIR: /srv/breadway-dl
ECOSYSTEM_DIR: /home/breadway/Projects/bread-ecosystem ECOSYSTEM_DIR: /tmp/bread-ecosystem-ci
jobs: jobs:
build: build:
@ -40,12 +40,8 @@ jobs:
- name: ensure bread-ecosystem - name: ensure bread-ecosystem
run: | run: |
if [[ -d "${ECOSYSTEM_DIR}/.git" ]]; then rm -rf "${ECOSYSTEM_DIR}"
git -C "${ECOSYSTEM_DIR}" pull --ff-only git clone https://github.com/Breadway/bread-ecosystem.git "${ECOSYSTEM_DIR}"
else
mkdir -p "$(dirname "${ECOSYSTEM_DIR}")"
git clone https://github.com/Breadway/bread-ecosystem.git "${ECOSYSTEM_DIR}"
fi
- name: regenerate index.json - name: regenerate index.json
run: bash "${ECOSYSTEM_DIR}/scripts/gen-index.sh" run: bash "${ECOSYSTEM_DIR}/scripts/gen-index.sh"

13
Cargo.lock generated
View file

@ -304,17 +304,18 @@ dependencies = [
[[package]] [[package]]
name = "bread-theme" name = "bread-theme"
version = "0.1.0" version = "0.2.3"
source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.1.0#6b5f4f475f66a645b08cb865e6dda8228d23679b" source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.2.8#77417d552130281ff787e07d52541eb25e9d533b"
dependencies = [ dependencies = [
"dirs 5.0.1", "dirs 5.0.1",
"gtk4",
"serde", "serde",
"serde_json", "serde_json",
] ]
[[package]] [[package]]
name = "breadman" name = "breadman"
version = "0.3.1" version = "0.3.4"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"breadpad-shared", "breadpad-shared",
@ -331,7 +332,7 @@ dependencies = [
[[package]] [[package]]
name = "breadpad" name = "breadpad"
version = "0.3.1" version = "0.3.4"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"breadpad-shared", "breadpad-shared",
@ -350,7 +351,7 @@ dependencies = [
[[package]] [[package]]
name = "breadpad-shared" name = "breadpad-shared"
version = "0.3.1" version = "0.3.4"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bread-theme", "bread-theme",
@ -376,7 +377,7 @@ dependencies = [
[[package]] [[package]]
name = "breadpad-test" name = "breadpad-test"
version = "0.3.1" version = "0.3.4"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"breadpad-shared", "breadpad-shared",

View file

@ -8,7 +8,7 @@ members = [
resolver = "2" resolver = "2"
[workspace.package] [workspace.package]
version = "0.3.1" version = "0.3.4"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
authors = ["Breadway"] authors = ["Breadway"]

View file

@ -190,8 +190,41 @@ enabled = true # set false to never call Ollama
[reminders] [reminders]
default_morning = "08:00" # what "tomorrow morning" resolves to default_morning = "08:00" # what "tomorrow morning" resolves to
missed_grace_minutes = 60 # how long after boot to still fire a missed reminder missed_grace_minutes = 60 # how long after boot to still fire a missed reminder
[calendar]
enabled = false # turn on CalDAV sync (see below)
url = "" # CalDAV calendar collection URL
username = ""
password = "" # app password / token recommended
``` ```
### Calendar sync (CalDAV)
When `[calendar].enabled = true`, reminders and dated notes are pushed to a
CalDAV calendar as events (tracked by `caldav_uid` on each note), so they show
up alongside the rest of your calendar.
1. Find your calendar's **collection URL**. It's the per-calendar CalDAV path,
not the server root — e.g. Nextcloud:
`https://host/remote.php/dav/calendars/<user>/<calendar-id>/`.
2. Create an **app password** for breadpad (don't use your main password):
Nextcloud → Settings → Security → *Devices & sessions* → "Create new app
password". Most CalDAV servers have an equivalent.
3. Fill in `breadpad.toml` (or BOS Settings → breadpad → Calendar):
```toml
[calendar]
enabled = true
url = "https://host/remote.php/dav/calendars/me/breadpad/"
username = "me"
password = "xxxx-xxxx-xxxx-xxxx"
```
4. Restart breadpad. New dated/reminder notes sync up; the `caldav_uid` field
links each note to its event so updates and deletes stay in step.
If the server is unreachable, breadpad logs a warning and keeps the note
locally — sync is best-effort and never blocks capture.
--- ---
## Usage ## Usage

View file

@ -4,7 +4,6 @@ use breadpad_shared::{
parser::parse_rule_based, parser::parse_rule_based,
scheduler::Scheduler, scheduler::Scheduler,
store::Store, store::Store,
theme::{build_css, load_palette},
types::{Note, NoteType, RecurrenceRule}, types::{Note, NoteType, RecurrenceRule},
}; };
use chrono::Local; use chrono::Local;
@ -924,19 +923,7 @@ fn show_add_note_window(parent: &gtk4::ApplicationWindow, state: AppState) {
// ── CSS ─────────────────────────────────────────────────────────────────────── // ── CSS ───────────────────────────────────────────────────────────────────────
fn apply_css(_cfg: &Config) { fn apply_css(_cfg: &Config) {
let palette = load_palette(); // Hot-reloads on `bread-theme reload` (recolours to the new pywal palette
let user_css = std::fs::read_to_string(breadpad_shared::config::style_css_path()).ok(); // and re-reads the user's style.css). See breadpad_shared::theme::apply_live.
let css = build_css(&palette, user_css.as_deref()); breadpad_shared::theme::apply_live();
let provider = gtk4::CssProvider::new();
provider.load_from_string(&css);
let Some(display) = gtk4::gdk::Display::default() else {
tracing::warn!("no default display; skipping CSS provider");
return;
};
gtk4::style_context_add_provider_for_display(
&display,
&provider,
gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
} }

View file

@ -7,7 +7,7 @@ authors.workspace = true
[dependencies] [dependencies]
bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.1.0" } bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.2.8", features = ["gtk"] }
anyhow.workspace = true anyhow.workspace = true
tracing.workspace = true tracing.workspace = true
serde.workspace = true serde.workspace = true

View file

@ -1,31 +1,34 @@
pub use bread_theme::{load_palette, Palette}; pub use bread_theme::{load_palette, Palette};
/// Generate the full breadpad CSS string. The base colour variables come from /// Apply breadpad/breadman's stylesheet and keep it live across palette changes.
/// `bread-theme`; the widget rules below are breadpad-specific. /// [`build_css`] bundles the shared component sheet with the app's own rules from
/// the current pywal palette; `bread_theme::gtk::apply_app_css` re-runs this
/// whenever `bread-theme reload` rewrites the shared theme file, so the UI
/// recolours in place (and re-reads the user's `style.css` override too).
pub fn apply_live() {
bread_theme::gtk::apply_app_css(|| {
let palette = load_palette();
let user_css = std::fs::read_to_string(crate::config::style_css_path()).ok();
build_css(&palette, user_css.as_deref())
});
}
/// Generate the full breadpad/breadman CSS string. The base — `@define-color`
/// palette, fonts, and generic widget styling — comes from the shared
/// `bread_theme::stylesheet`, so breadpad and breadman look identical to the
/// rest of the ecosystem. Only breadpad-specific component rules are appended.
pub fn build_css(palette: &Palette, user_css: Option<&str>) -> String { pub fn build_css(palette: &Palette, user_css: Option<&str>) -> String {
let mut css = format!( // Shared ecosystem base (define-colors incl. accent, font, buttons, entries,
// switches, lists, cards, scrollbars). `overlay` here is color7 — consistent
// with every other bread app (breadpad previously mapped it to color0).
let mut css = bread_theme::stylesheet(palette);
css.push_str(
r#" r#"
@define-color bg {bg}; /* breadpad/breadman-specific components */
@define-color fg {fg}; window { border-radius: 8px; }
@define-color red {c1};
@define-color green {c2};
@define-color yellow {c3};
@define-color blue {c4};
@define-color pink {c5};
@define-color teal {c6};
@define-color overlay {c0};
* {{ .popup-entry {
font-family: 'Varela Round', sans-serif;
}}
window {{
background-color: @bg;
color: @fg;
border-radius: 8px;
}}
.popup-entry {{
background: @bg; background: @bg;
color: @fg; color: @fg;
border: 2px solid @blue; border: 2px solid @blue;
@ -33,80 +36,59 @@ window {{
padding: 12px 16px; padding: 12px 16px;
font-size: 14px; font-size: 14px;
caret-color: @fg; caret-color: @fg;
}} }
.popup-entry:focus {{ .popup-entry:focus {
outline: none; outline: none;
border-color: @teal; border-color: @teal;
}} }
.type-chip {{ .type-chip {
background: @overlay; background: @overlay;
color: @fg; color: @on-overlay;
border-radius: 999px; border-radius: 999px;
padding: 4px 12px; padding: 4px 12px;
font-size: 12px; font-size: 12px;
margin: 4px; margin: 4px;
}} }
.type-chip.active {{ .type-chip.active {
background: @blue; background: @blue;
color: @bg; color: @on-accent;
}} }
.confirm-button {{ .confirm-button {
background: @blue; background: @blue;
color: @bg; color: @on-accent;
border: none; border: none;
border-radius: 8px; border-radius: 8px;
padding: 8px 16px; padding: 8px 16px;
font-weight: bold; font-weight: bold;
}} }
.note-card {{ .note-card {
background: shade(@bg, 1.1); background: shade(@bg, 1.1);
border-radius: 8px; border-radius: 8px;
padding: 12px; padding: 12px;
margin: 8px; margin: 8px;
border-left: 3px solid @blue; border-left: 3px solid @blue;
}} }
.note-card:hover {{ .note-card:hover {
background: shade(@bg, 1.2); background: shade(@bg, 1.2);
}} }
.search-entry {{ .search-entry {
background: shade(@bg, 1.1); background: shade(@bg, 1.1);
color: @fg; color: @fg;
border: 1px solid @overlay; border: 1px solid @overlay;
border-radius: 6px; border-radius: 6px;
padding: 8px 12px; padding: 8px 12px;
}}
.search-entry:focus {{
border-color: @blue;
outline: none;
}}
"#,
bg = palette.background,
fg = palette.foreground,
c0 = palette.color0,
c1 = palette.color1,
c2 = palette.color2,
c3 = palette.color3,
c4 = palette.color4,
c5 = palette.color5,
c6 = palette.color6,
);
css.push_str(r#"
.dim-label {
color: alpha(@fg, 0.5);
font-size: 12px;
} }
.sidebar { .search-entry:focus {
background: shade(@bg, 0.93); border-color: @blue;
outline: none;
} }
.sidebar-row { .sidebar-row {
@ -121,7 +103,7 @@ window {{
.sidebar-row:selected { .sidebar-row:selected {
background: @blue; background: @blue;
color: @bg; color: @on-accent;
font-weight: 500; font-weight: 500;
} }
@ -217,20 +199,6 @@ window {{
} }
.snooze-option:hover { background: shade(@bg, 1.2); } .snooze-option:hover { background: shade(@bg, 1.2); }
entry {
background: shade(@bg, 1.1);
color: @fg;
border: 1px solid @overlay;
border-radius: 6px;
caret-color: @fg;
padding: 5px 10px;
}
entry:focus {
border-color: @blue;
outline: none;
}
"#); "#);
if let Some(extra) = user_css { if let Some(extra) = user_css {

View file

@ -2,10 +2,9 @@ use anyhow::Result;
use breadpad_shared::{ use breadpad_shared::{
calendar::CalDavClient, calendar::CalDavClient,
classifier::Classifier, classifier::Classifier,
config::{style_css_path, Config}, config::Config,
scheduler::Scheduler, scheduler::Scheduler,
store::Store, store::Store,
theme::{build_css, load_palette},
types::{Note, NoteType}, types::{Note, NoteType},
}; };
use gtk4::{glib, prelude::*}; use gtk4::{glib, prelude::*};
@ -765,19 +764,7 @@ fn save_note_classified(
} }
fn apply_css(_cfg: &Config) { fn apply_css(_cfg: &Config) {
let palette = load_palette(); // Hot-reloads on `bread-theme reload` (recolours to the new pywal palette
let user_css = std::fs::read_to_string(style_css_path()).ok(); // and re-reads the user's style.css). See breadpad_shared::theme::apply_live.
let css = build_css(&palette, user_css.as_deref()); breadpad_shared::theme::apply_live();
let provider = gtk4::CssProvider::new();
provider.load_from_string(&css);
let Some(display) = gtk4::gdk::Display::default() else {
tracing::warn!("no default display; skipping CSS provider");
return;
};
gtk4::style_context_add_provider_for_display(
&display,
&provider,
gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
} }

View file

@ -10,7 +10,7 @@ license=('MIT')
# Some Rust deps (ring/mlua) build vendored C/asm into static archives; makepkg's # Some Rust deps (ring/mlua) build vendored C/asm into static archives; makepkg's
# default -flto=auto emits GCC LTO bitcode the Rust (lld) link cannot read, # default -flto=auto emits GCC LTO bitcode the Rust (lld) link cannot read,
# causing undefined-symbol errors. Disable LTO. # causing undefined-symbol errors. Disable LTO.
options=(!lto) options=(!lto !debug)
depends=('gtk4' 'gtk4-layer-shell') depends=('gtk4' 'gtk4-layer-shell')
optdepends=( optdepends=(
'ollama: local AI note classification' 'ollama: local AI note classification'