From 49f6966d9cbecd25aceb9f2510e59d9831dd863c Mon Sep 17 00:00:00 2001 From: Breadway Date: Tue, 16 Jun 2026 16:57:16 +0800 Subject: [PATCH 1/6] 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. --- breadpad-shared/Cargo.toml | 2 +- breadpad-shared/src/theme.rs | 105 ++++++++++------------------------- 2 files changed, 31 insertions(+), 76 deletions(-) diff --git a/breadpad-shared/Cargo.toml b/breadpad-shared/Cargo.toml index 610c040..cea30b3 100644 --- a/breadpad-shared/Cargo.toml +++ b/breadpad-shared/Cargo.toml @@ -7,7 +7,7 @@ authors.workspace = true [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.6" } anyhow.workspace = true tracing.workspace = true serde.workspace = true diff --git a/breadpad-shared/src/theme.rs b/breadpad-shared/src/theme.rs index adc76dc..857dcfe 100644 --- a/breadpad-shared/src/theme.rs +++ b/breadpad-shared/src/theme.rs @@ -1,31 +1,21 @@ pub use bread_theme::{load_palette, Palette}; -/// Generate the full breadpad CSS string. The base colour variables come from -/// `bread-theme`; the widget rules below are breadpad-specific. +/// 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 { - 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#" -@define-color bg {bg}; -@define-color fg {fg}; -@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}; +/* breadpad/breadman-specific components */ +window { border-radius: 8px; } -* {{ - font-family: 'Varela Round', sans-serif; -}} - -window {{ - background-color: @bg; - color: @fg; - border-radius: 8px; -}} - -.popup-entry {{ +.popup-entry { background: @bg; color: @fg; border: 2px solid @blue; @@ -33,80 +23,59 @@ window {{ padding: 12px 16px; font-size: 14px; caret-color: @fg; -}} +} -.popup-entry:focus {{ +.popup-entry:focus { outline: none; border-color: @teal; -}} +} -.type-chip {{ +.type-chip { background: @overlay; color: @fg; border-radius: 999px; padding: 4px 12px; font-size: 12px; margin: 4px; -}} +} -.type-chip.active {{ +.type-chip.active { background: @blue; color: @bg; -}} +} -.confirm-button {{ +.confirm-button { background: @blue; color: @bg; border: none; border-radius: 8px; padding: 8px 16px; font-weight: bold; -}} +} -.note-card {{ +.note-card { background: shade(@bg, 1.1); border-radius: 8px; padding: 12px; margin: 8px; border-left: 3px solid @blue; -}} +} -.note-card:hover {{ +.note-card:hover { background: shade(@bg, 1.2); -}} +} -.search-entry {{ +.search-entry { background: shade(@bg, 1.1); color: @fg; border: 1px solid @overlay; border-radius: 6px; 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 { - background: shade(@bg, 0.93); +.search-entry:focus { + border-color: @blue; + outline: none; } .sidebar-row { @@ -217,20 +186,6 @@ window {{ } .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 { From 08d8956eac73d96e2eb396fb2b58422a98ad89b4 Mon Sep 17 00:00:00 2001 From: Breadway Date: Tue, 16 Jun 2026 17:07:06 +0800 Subject: [PATCH 2/6] 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. --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index 02ec8a2..b39ea3d 100644 --- a/README.md +++ b/README.md @@ -190,8 +190,41 @@ enabled = true # set false to never call Ollama [reminders] default_morning = "08:00" # what "tomorrow morning" resolves to 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///`. +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 From dfe19708ba334e9da4c109a0afde39c4cfdc1993 Mon Sep 17 00:00:00 2001 From: Breadway Date: Tue, 16 Jun 2026 18:35:33 +0800 Subject: [PATCH 3/6] Release 0.3.4: shared bread-theme stylesheet (overlay=color7) --- Cargo.lock | 12 ++++++------ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8769946..46fa8d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -304,8 +304,8 @@ dependencies = [ [[package]] name = "bread-theme" -version = "0.1.0" -source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.1.0#6b5f4f475f66a645b08cb865e6dda8228d23679b" +version = "0.2.3" +source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.2.6#0c8c5c00e435fedff4f81e36d603424c153519a9" dependencies = [ "dirs 5.0.1", "serde", @@ -314,7 +314,7 @@ dependencies = [ [[package]] name = "breadman" -version = "0.3.1" +version = "0.3.4" dependencies = [ "anyhow", "breadpad-shared", @@ -331,7 +331,7 @@ dependencies = [ [[package]] name = "breadpad" -version = "0.3.1" +version = "0.3.4" dependencies = [ "anyhow", "breadpad-shared", @@ -350,7 +350,7 @@ dependencies = [ [[package]] name = "breadpad-shared" -version = "0.3.1" +version = "0.3.4" dependencies = [ "anyhow", "bread-theme", @@ -376,7 +376,7 @@ dependencies = [ [[package]] name = "breadpad-test" -version = "0.3.1" +version = "0.3.4" dependencies = [ "anyhow", "breadpad-shared", diff --git a/Cargo.toml b/Cargo.toml index af2b588..4092bed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ resolver = "2" [workspace.package] -version = "0.3.1" +version = "0.3.4" edition = "2021" license = "MIT" authors = ["Breadway"] From c30aa2497e49451f3915707fb79be2858408eb87 Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 17 Jun 2026 12:42:12 +0800 Subject: [PATCH 4/6] Fix illegible text on light pywal palettes + hot-reload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- Cargo.lock | 3 ++- breadman/src/main.rs | 19 +++---------------- breadpad-shared/Cargo.toml | 2 +- breadpad-shared/src/theme.rs | 21 +++++++++++++++++---- breadpad/src/main.rs | 21 ++++----------------- 5 files changed, 27 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46fa8d0..11b33d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -305,9 +305,10 @@ dependencies = [ [[package]] name = "bread-theme" version = "0.2.3" -source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.2.6#0c8c5c00e435fedff4f81e36d603424c153519a9" +source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.2.7#ea87083c0615fc9141b0ae4c99f833748a0189d1" dependencies = [ "dirs 5.0.1", + "gtk4", "serde", "serde_json", ] diff --git a/breadman/src/main.rs b/breadman/src/main.rs index 0ee89bc..d46557a 100644 --- a/breadman/src/main.rs +++ b/breadman/src/main.rs @@ -4,7 +4,6 @@ use breadpad_shared::{ parser::parse_rule_based, scheduler::Scheduler, store::Store, - theme::{build_css, load_palette}, types::{Note, NoteType, RecurrenceRule}, }; use chrono::Local; @@ -924,19 +923,7 @@ fn show_add_note_window(parent: >k4::ApplicationWindow, state: AppState) { // ── CSS ─────────────────────────────────────────────────────────────────────── fn apply_css(_cfg: &Config) { - let palette = load_palette(); - let user_css = std::fs::read_to_string(breadpad_shared::config::style_css_path()).ok(); - let css = build_css(&palette, user_css.as_deref()); - - 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, - ); + // Hot-reloads on `bread-theme reload` (recolours to the new pywal palette + // and re-reads the user's style.css). See breadpad_shared::theme::apply_live. + breadpad_shared::theme::apply_live(); } diff --git a/breadpad-shared/Cargo.toml b/breadpad-shared/Cargo.toml index cea30b3..52a5b21 100644 --- a/breadpad-shared/Cargo.toml +++ b/breadpad-shared/Cargo.toml @@ -7,7 +7,7 @@ authors.workspace = true [dependencies] -bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.2.6" } +bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.2.7", features = ["gtk"] } anyhow.workspace = true tracing.workspace = true serde.workspace = true diff --git a/breadpad-shared/src/theme.rs b/breadpad-shared/src/theme.rs index 857dcfe..cb1139a 100644 --- a/breadpad-shared/src/theme.rs +++ b/breadpad-shared/src/theme.rs @@ -1,5 +1,18 @@ pub use bread_theme::{load_palette, Palette}; +/// Apply breadpad/breadman's stylesheet and keep it live across palette changes. +/// [`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 @@ -32,7 +45,7 @@ window { border-radius: 8px; } .type-chip { background: @overlay; - color: @fg; + color: @on-overlay; border-radius: 999px; padding: 4px 12px; font-size: 12px; @@ -41,12 +54,12 @@ window { border-radius: 8px; } .type-chip.active { background: @blue; - color: @bg; + color: @on-accent; } .confirm-button { background: @blue; - color: @bg; + color: @on-accent; border: none; border-radius: 8px; padding: 8px 16px; @@ -90,7 +103,7 @@ window { border-radius: 8px; } .sidebar-row:selected { background: @blue; - color: @bg; + color: @on-accent; font-weight: 500; } diff --git a/breadpad/src/main.rs b/breadpad/src/main.rs index c840557..aa924d0 100644 --- a/breadpad/src/main.rs +++ b/breadpad/src/main.rs @@ -2,10 +2,9 @@ use anyhow::Result; use breadpad_shared::{ calendar::CalDavClient, classifier::Classifier, - config::{style_css_path, Config}, + config::Config, scheduler::Scheduler, store::Store, - theme::{build_css, load_palette}, types::{Note, NoteType}, }; use gtk4::{glib, prelude::*}; @@ -765,19 +764,7 @@ fn save_note_classified( } fn apply_css(_cfg: &Config) { - let palette = load_palette(); - let user_css = std::fs::read_to_string(style_css_path()).ok(); - let css = build_css(&palette, user_css.as_deref()); - - 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, - ); + // Hot-reloads on `bread-theme reload` (recolours to the new pywal palette + // and re-reads the user's style.css). See breadpad_shared::theme::apply_live. + breadpad_shared::theme::apply_live(); } From e0b55e1713661f8fc7f28cff8711ecd2271a6856 Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 17 Jun 2026 12:55:57 +0800 Subject: [PATCH 5/6] Bump bread-theme to v0.2.8 (live-reload fix) --- Cargo.lock | 2 +- breadpad-shared/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 11b33d9..756f0c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -305,7 +305,7 @@ dependencies = [ [[package]] name = "bread-theme" version = "0.2.3" -source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.2.7#ea87083c0615fc9141b0ae4c99f833748a0189d1" +source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.2.8#77417d552130281ff787e07d52541eb25e9d533b" dependencies = [ "dirs 5.0.1", "gtk4", diff --git a/breadpad-shared/Cargo.toml b/breadpad-shared/Cargo.toml index 52a5b21..01470f9 100644 --- a/breadpad-shared/Cargo.toml +++ b/breadpad-shared/Cargo.toml @@ -7,7 +7,7 @@ authors.workspace = true [dependencies] -bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.2.7", features = ["gtk"] } +bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.2.8", features = ["gtk"] } anyhow.workspace = true tracing.workspace = true serde.workspace = true From df42aba1d32ff85b5d20dbb7f14c22905aacf85b Mon Sep 17 00:00:00 2001 From: Breadway Date: Fri, 19 Jun 2026 08:38:40 +0800 Subject: [PATCH 6/6] CI: use /tmp for ecosystem clone, avoid permissions conflict --- .github/workflows/release.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8df4368..e492838 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,7 @@ permissions: env: DL_DIR: /srv/breadway-dl - ECOSYSTEM_DIR: /home/breadway/Projects/bread-ecosystem + ECOSYSTEM_DIR: /tmp/bread-ecosystem-ci jobs: build: @@ -40,12 +40,8 @@ jobs: - name: ensure bread-ecosystem run: | - if [[ -d "${ECOSYSTEM_DIR}/.git" ]]; then - git -C "${ECOSYSTEM_DIR}" pull --ff-only - else - mkdir -p "$(dirname "${ECOSYSTEM_DIR}")" - git clone https://github.com/Breadway/bread-ecosystem.git "${ECOSYSTEM_DIR}" - fi + rm -rf "${ECOSYSTEM_DIR}" + git clone https://github.com/Breadway/bread-ecosystem.git "${ECOSYSTEM_DIR}" - name: regenerate index.json run: bash "${ECOSYSTEM_DIR}/scripts/gen-index.sh"