Skip to main content

max / balanced_breakfast

Take themes from published makeover instead of the MNW checkout theme-common was reachable only as ../../MNW/shared/theme-common, and tauri.conf.json globbed ../../../MNW/shared/themes/*.toml, so both the code and the theme data came from a sibling repo. makeover 0.9.0 embeds the theme set and exposes it without a path. Tauri's resource globs are resolved by the Tauri CLI against the crate directory and cannot reach a registry checkout, so build.rs materializes the embedded themes into a gitignored src-tauri/themes/ that the bundler globs. makeover stays the single source of truth; no theme files are vendored here. Drops palenight along with upstream, over unresolvable provenance.
Author: Max Johnson <me@maxj.phd> · 2026-07-19 19:51 UTC
Commit: 7ba16e1fedb9b73d0baa978feb4a202d11ef4505
Parent: 995f47e
8 files changed, +82 insertions, -186 deletions
M .gitignore +3
@@ -31,3 +31,6 @@ src-tauri/gen/
31 31
32 32 # Claude Code instructions (project-local; not for the public repo)
33 33 CLAUDE.md
34 +
35 + # Generated by src-tauri/build.rs from makeover's embedded theme set
36 + /src-tauri/themes/
M Cargo.lock +29 -169
@@ -199,9 +199,9 @@ dependencies = [
199 199 "bb-db",
200 200 "bb-feed",
201 201 "bb-interface",
202 - "bb-pdf",
203 202 "chrono",
204 203 "futures",
204 + "makeover",
205 205 "open",
206 206 "parking_lot",
207 207 "rand 0.8.5",
@@ -211,7 +211,6 @@ dependencies = [
211 211 "serde_json",
212 212 "sha2",
213 213 "sqlx",
214 - "supernote-push",
215 214 "synckit-client",
216 215 "tagtree",
217 216 "tauri",
@@ -220,7 +219,6 @@ dependencies = [
220 219 "tauri-plugin-shell",
221 220 "tauri-plugin-updater",
222 221 "tauri-plugin-window-state",
223 - "theme-common",
224 222 "tokio",
225 223 "toml 0.8.2",
226 224 "tracing",
@@ -262,7 +260,7 @@ dependencies = [
262 260 "futures",
263 261 "keyring",
264 262 "parking_lot",
265 - "pter 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
263 + "pter",
266 264 "rand 0.8.5",
267 265 "readable-readability",
268 266 "regex",
@@ -319,16 +317,6 @@ dependencies = [
319 317 ]
320 318
321 319 [[package]]
322 - name = "bb-pdf"
323 - version = "0.3.1"
324 - dependencies = [
325 - "printpdf",
326 - "pter 0.1.0",
327 - "pulldown-cmark",
328 - "thiserror 1.0.69",
329 - ]
330 -
331 - [[package]]
332 320 name = "bitflags"
333 321 version = "1.3.2"
334 322 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -392,17 +380,6 @@ dependencies = [
392 380 ]
393 381
394 382 [[package]]
395 - name = "bstr"
396 - version = "1.13.0"
397 - source = "registry+https://github.com/rust-lang/crates.io-index"
398 - checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530"
399 - dependencies = [
400 - "memchr",
401 - "regex-automata",
402 - "serde_core",
403 - ]
404 -
405 - [[package]]
406 383 name = "bumpalo"
407 384 version = "3.19.1"
408 385 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2061,7 +2038,7 @@ dependencies = [
2061 2038 "libc",
2062 2039 "percent-encoding",
2063 2040 "pin-project-lite",
2064 - "socket2 0.6.2",
2041 + "socket2",
2065 2042 "system-configuration",
2066 2043 "tokio",
2067 2044 "tower-service",
@@ -2212,13 +2189,22 @@ dependencies = [
2212 2189 ]
2213 2190
2214 2191 [[package]]
2215 - name = "if-addrs"
2216 - version = "0.13.4"
2192 + name = "include_dir"
2193 + version = "0.7.4"
2217 2194 source = "registry+https://github.com/rust-lang/crates.io-index"
2218 - checksum = "69b2eeee38fef3aa9b4cc5f1beea8a2444fc00e7377cafae396de3f5c2065e24"
2195 + checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd"
2219 2196 dependencies = [
2220 - "libc",
2221 - "windows-sys 0.59.0",
2197 + "include_dir_macros",
2198 + ]
2199 +
2200 + [[package]]
2201 + name = "include_dir_macros"
2202 + version = "0.7.4"
2203 + source = "registry+https://github.com/rust-lang/crates.io-index"
2204 + checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
2205 + dependencies = [
2206 + "proc-macro2",
2207 + "quote",
2222 2208 ]
2223 2209
2224 2210 [[package]]
@@ -2514,12 +2500,6 @@ dependencies = [
2514 2500 ]
2515 2501
2516 2502 [[package]]
2517 - name = "linked-hash-map"
2518 - version = "0.5.6"
2519 - source = "registry+https://github.com/rust-lang/crates.io-index"
2520 - checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
2521 -
2522 - [[package]]
2523 2503 name = "linux-keyutils"
2524 2504 version = "0.2.5"
2525 2505 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2557,29 +2537,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2557 2537 checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
2558 2538
2559 2539 [[package]]
2560 - name = "lopdf"
2561 - version = "0.31.0"
2562 - source = "registry+https://github.com/rust-lang/crates.io-index"
2563 - checksum = "07c8e1b6184b1b32ea5f72f572ebdc40e5da1d2921fa469947ff7c480ad1f85a"
2564 - dependencies = [
2565 - "encoding_rs",
2566 - "flate2",
2567 - "itoa 1.0.17",
2568 - "linked-hash-map",
2569 - "log",
2570 - "md5",
2571 - "pom",
2572 - "time",
2573 - "weezl",
2574 - ]
2575 -
2576 - [[package]]
2577 2540 name = "mac"
2578 2541 version = "0.1.1"
2579 2542 source = "registry+https://github.com/rust-lang/crates.io-index"
2580 2543 checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
2581 2544
2582 2545 [[package]]
2546 + name = "makeover"
2547 + version = "0.9.0"
2548 + source = "registry+https://github.com/rust-lang/crates.io-index"
2549 + checksum = "8901217cbffb659a961ac7b63fbfb72bacd4eda13b1d5a7329a9ffed54616165"
2550 + dependencies = [
2551 + "include_dir",
2552 + "serde",
2553 + "toml 0.8.2",
2554 + ]
2555 +
2556 + [[package]]
2583 2557 name = "maplit"
2584 2558 version = "1.0.2"
2585 2559 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2683,25 +2657,6 @@ dependencies = [
2683 2657 ]
2684 2658
2685 2659 [[package]]
2686 - name = "md5"
2687 - version = "0.7.0"
2688 - source = "registry+https://github.com/rust-lang/crates.io-index"
2689 - checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
2690 -
2691 - [[package]]
2692 - name = "mdns-sd"
2693 - version = "0.11.5"
2694 - source = "registry+https://github.com/rust-lang/crates.io-index"
2695 - checksum = "8fe7c11a1eb3cfbfcf702d1601c1f5f4c102cdc8665b8a557783ef634741676e"
2696 - dependencies = [
2697 - "flume",
2698 - "if-addrs",
2699 - "log",
2700 - "polling",
2701 - "socket2 0.5.10",
2702 - ]
2703 -
2704 - [[package]]
2705 2660 name = "memchr"
2706 2661 version = "2.8.0"
2707 2662 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3257,15 +3212,6 @@ dependencies = [
3257 3212 ]
3258 3213
3259 3214 [[package]]
3260 - name = "owned_ttf_parser"
3261 - version = "0.19.0"
3262 - source = "registry+https://github.com/rust-lang/crates.io-index"
3263 - checksum = "706de7e2214113d63a8238d1910463cfce781129a6f263d13fdb09ff64355ba4"
3264 - dependencies = [
3265 - "ttf-parser",
3266 - ]
3267 -
3268 - [[package]]
3269 3215 name = "pango"
3270 3216 version = "0.18.3"
3271 3217 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3620,22 +3566,6 @@ dependencies = [
3620 3566 ]
3621 3567
3622 3568 [[package]]
3623 - name = "polling"
3624 - version = "2.8.0"
3625 - source = "registry+https://github.com/rust-lang/crates.io-index"
3626 - checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
3627 - dependencies = [
3628 - "autocfg",
3629 - "bitflags 1.3.2",
3630 - "cfg-if",
3631 - "concurrent-queue",
3632 - "libc",
3633 - "log",
3634 - "pin-project-lite",
3635 - "windows-sys 0.48.0",
3636 - ]
3637 -
3638 - [[package]]
3639 3569 name = "poly1305"
3640 3570 version = "0.8.0"
3641 3571 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3659,15 +3589,6 @@ dependencies = [
3659 3589 ]
3660 3590
3661 3591 [[package]]
3662 - name = "pom"
3663 - version = "3.4.0"
3664 - source = "registry+https://github.com/rust-lang/crates.io-index"
3665 - checksum = "6c972d8f86e943ad532d0b04e8965a749ad1d18bb981a9c7b3ae72fe7fd7744b"
3666 - dependencies = [
3667 - "bstr",
3668 - ]
3669 -
3670 - [[package]]
3671 3592 name = "portable-atomic"
3672 3593 version = "1.13.1"
3673 3594 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3704,18 +3625,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3704 3625 checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
3705 3626
3706 3627 [[package]]
3707 - name = "printpdf"
3708 - version = "0.7.0"
3709 - source = "registry+https://github.com/rust-lang/crates.io-index"
3710 - checksum = "c30a4cc87c3ca9a98f4970db158a7153f8d1ec8076e005751173c57836380b1d"
3711 - dependencies = [
3712 - "js-sys",
3713 - "lopdf",
3714 - "owned_ttf_parser",
3715 - "time",
3716 - ]
3717 -
3718 - [[package]]
3719 3628 name = "proc-macro-crate"
3720 3629 version = "1.3.1"
3721 3630 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3786,13 +3695,6 @@ dependencies = [
3786 3695 [[package]]
3787 3696 name = "pter"
3788 3697 version = "0.1.0"
3789 - dependencies = [
3790 - "scraper",
3791 - ]
3792 -
3793 - [[package]]
3794 - name = "pter"
3795 - version = "0.1.0"
3796 3698 source = "registry+https://github.com/rust-lang/crates.io-index"
3797 3699 checksum = "ecc3629f628e0e8d975cd206ef331ce598dd457d94cd609c96a6809934732859"
3798 3700 dependencies = [
@@ -4872,16 +4774,6 @@ dependencies = [
4872 4774
4873 4775 [[package]]
4874 4776 name = "socket2"
4875 - version = "0.5.10"
4876 - source = "registry+https://github.com/rust-lang/crates.io-index"
4877 - checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
4878 - dependencies = [
4879 - "libc",
4880 - "windows-sys 0.52.0",
4881 - ]
4882 -
4883 - [[package]]
4884 - name = "socket2"
4885 4777 version = "0.6.2"
4886 4778 source = "registry+https://github.com/rust-lang/crates.io-index"
4887 4779 checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0"
@@ -5244,18 +5136,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
5244 5136 checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
5245 5137
5246 5138 [[package]]
5247 - name = "supernote-push"
5248 - version = "0.1.0"
5249 - dependencies = [
5250 - "mdns-sd",
5251 - "percent-encoding",
5252 - "serde",
5253 - "serde_json",
5254 - "thiserror 2.0.18",
5255 - "ureq",
5256 - ]
5257 -
5258 - [[package]]
5259 5139 name = "swift-rs"
5260 5140 version = "1.0.7"
5261 5141 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5817,14 +5697,6 @@ dependencies = [
5817 5697 ]
5818 5698
5819 5699 [[package]]
5820 - name = "theme-common"
5821 - version = "0.7.0"
5822 - dependencies = [
5823 - "serde",
5824 - "toml 0.8.2",
5825 - ]
5826 -
5827 - [[package]]
5828 5700 name = "thin-slice"
5829 5701 version = "0.1.1"
5830 5702 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5963,7 +5835,7 @@ dependencies = [
5963 5835 "libc",
5964 5836 "mio",
5965 5837 "pin-project-lite",
5966 - "socket2 0.6.2",
5838 + "socket2",
5967 5839 "tokio-macros",
5968 5840 "windows-sys 0.61.2",
5969 5841 ]
@@ -6255,12 +6127,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
6255 6127 checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
6256 6128
6257 6129 [[package]]
6258 - name = "ttf-parser"
6259 - version = "0.19.2"
6260 - source = "registry+https://github.com/rust-lang/crates.io-index"
6261 - checksum = "49d64318d8311fc2668e48b63969f4343e0a85c4a109aa8460d6672e364b8bd1"
6262 -
6263 - [[package]]
6264 6130 name = "typeid"
6265 6131 version = "1.0.3"
6266 6132 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -6774,12 +6640,6 @@ dependencies = [
6774 6640 ]
6775 6641
6776 6642 [[package]]
6777 - name = "weezl"
6778 - version = "0.1.12"
6779 - source = "registry+https://github.com/rust-lang/crates.io-index"
6780 - checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
6781 -
6782 - [[package]]
6783 6643 name = "whoami"
6784 6644 version = "1.6.1"
6785 6645 source = "registry+https://github.com/rust-lang/crates.io-index"
M Cargo.toml +1 -1
@@ -49,7 +49,7 @@ open = "5"
49 49 # Utilities
50 50 chrono = { version = "0.4.43", features = ["serde"] }
51 51 uuid = { version = "1.20.0", features = ["v4", "serde"] }
52 - theme-common = { path = "../../MNW/shared/theme-common" }
52 + makeover = "0.9.0"
53 53 toml = "0.8.2"
54 54 dirs = "6.0.0"
55 55
@@ -14,6 +14,7 @@ crate-type = ["staticlib", "cdylib", "lib"]
14 14
15 15 [build-dependencies]
16 16 tauri-build = { version = "2.5.5", features = [] }
17 + makeover.workspace = true
17 18
18 19 [dependencies]
19 20 bb-interface.workspace = true
@@ -56,7 +57,7 @@ rand.workspace = true
56 57 roxmltree.workspace = true
57 58
58 59 # Theme loading
59 - theme-common.workspace = true
60 + makeover.workspace = true
60 61 toml.workspace = true
61 62
62 63 # HTTP (download_and_open command)
@@ -1,3 +1,32 @@
1 + use std::path::Path;
2 +
3 + /// Write the themes `makeover` ships into `themes/`, where `tauri.conf.json`
4 + /// picks them up as a bundled resource.
5 + ///
6 + /// Tauri's resource globs are read by the Tauri CLI against the crate
7 + /// directory, so they cannot point into a registry checkout or `OUT_DIR`.
8 + /// Materializing the embedded set here keeps `makeover` the single source of
9 + /// truth without vendoring a second copy of the theme files into this repo.
10 + /// The directory is generated, and gitignored.
11 + fn materialize_themes() {
12 + let dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("themes");
13 + std::fs::create_dir_all(&dir).expect("create themes/");
14 +
15 + // Clear stale files first, so a theme removed upstream (or renamed) does
16 + // not linger in the bundle from an earlier build.
17 + for entry in std::fs::read_dir(&dir).expect("read themes/").flatten() {
18 + let path = entry.path();
19 + if path.extension().is_some_and(|e| e == "toml") {
20 + std::fs::remove_file(&path).expect("remove stale theme");
21 + }
22 + }
23 +
24 + for (id, source) in makeover::embedded_themes() {
25 + std::fs::write(dir.join(format!("{id}.toml")), source).expect("write theme");
26 + }
27 + }
28 +
1 29 fn main() {
30 + materialize_themes();
2 31 tauri_build::build()
3 32 }
@@ -1,4 +1,4 @@
1 - //! Theme loading commands — thin Tauri wrappers over `theme_common`.
1 + //! Theme loading commands — thin Tauri wrappers over `makeover`.
2 2
3 3 use std::path::PathBuf;
4 4 use tauri::{AppHandle, Manager};
@@ -6,7 +6,7 @@ use tracing::instrument;
6 6
7 7 use super::error::ApiError;
8 8
9 - pub use theme_common::{SemanticTokens, ThemeMeta};
9 + pub use makeover::{SemanticTokens, ThemeMeta};
10 10
11 11 /// Returns theme directories in priority order (later overrides earlier by ID).
12 12 /// Each entry is `(path, is_custom)`.
@@ -21,9 +21,12 @@ fn theme_dirs(app: &AppHandle) -> Vec<(PathBuf, bool)> {
21 21 }
22 22 }
23 23
24 - // 2. Dev fallback: CARGO_MANIFEST_DIR → 3 parents up → Git/themes/
25 - if let Some(dev_path) = theme_common::dev_themes_dir(env!("CARGO_MANIFEST_DIR").as_ref(), 3) {
26 - dirs.push((dev_path, false));
24 + // 2. Dev fallback: the themes build.rs materialized from makeover. In a
25 + // dev run there is no bundled resource dir, so this is what supplies the
26 + // stock set; in production it is redundant with (1) and harmless.
27 + let generated = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("themes");
28 + if generated.is_dir() {
29 + dirs.push((generated, false));
27 30 }
28 31
29 32 // 3. User custom themes (highest priority)
@@ -41,14 +44,14 @@ fn theme_dirs(app: &AppHandle) -> Vec<(PathBuf, bool)> {
41 44 #[instrument(skip_all)]
42 45 pub fn list_themes(app: AppHandle) -> Result<Vec<ThemeMeta>, ApiError> {
43 46 let dirs = theme_dirs(&app);
44 - Ok(theme_common::list_themes_from_dirs(&dirs))
47 + Ok(makeover::list_themes_from_dirs(&dirs))
45 48 }
46 49
47 50 #[tauri::command]
48 51 #[instrument(skip_all)]
49 52 pub fn get_theme(app: AppHandle, id: String) -> Result<SemanticTokens, ApiError> {
50 53 let dirs = theme_dirs(&app);
51 - theme_common::load_semantic(&dirs, &id).map_err(ApiError::internal)
54 + makeover::load_semantic(&dirs, &id).map_err(ApiError::internal)
52 55 }
53 56
54 57 #[tauri::command]
@@ -72,7 +75,7 @@ pub fn import_theme(app: AppHandle, path: String) -> Result<ThemeMeta, ApiError>
72 75 .app_config_dir()
73 76 .map_err(|e| ApiError::internal(e.to_string()))?
74 77 .join("themes");
75 - theme_common::import_theme(std::path::Path::new(&path), &custom_dir)
78 + makeover::import_theme(std::path::Path::new(&path), &custom_dir)
76 79 .map_err(ApiError::internal)
77 80 }
78 81
@@ -80,12 +83,12 @@ pub fn import_theme(app: AppHandle, path: String) -> Result<ThemeMeta, ApiError>
80 83 #[instrument(skip_all)]
81 84 pub fn export_theme(app: AppHandle, id: String, path: String) -> Result<(), ApiError> {
82 85 let dirs = theme_dirs(&app);
83 - theme_common::export_theme(&dirs, &id, std::path::Path::new(&path))
86 + makeover::export_theme(&dirs, &id, std::path::Path::new(&path))
84 87 .map_err(ApiError::internal)
85 88 }
86 89
87 90 #[cfg(test)]
88 91 mod tests {
89 - // Core theme logic tests live in theme-common crate.
92 + // Core theme logic tests live in the makeover crate.
90 93 // App-specific tests here only if needed for theme_dirs behavior.
91 94 }
@@ -32,7 +32,7 @@
32 32 "targets": "all",
33 33 "resources": [
34 34 "../plugins/*.rhai",
35 - "../../../MNW/shared/themes/*.toml"
35 + "themes/*.toml"
36 36 ],
37 37 "icon": [
38 38 "icons/32x32.png",
@@ -38,11 +38,11 @@ five = "#9d4edd"
38 38 six = "#4cc9f0"
39 39 "##;
40 40
41 - let theme = theme_common::parse_theme_str("ocean-dark", toml, false).unwrap();
41 + let theme = makeover::parse_theme_str("ocean-dark", toml, false).unwrap();
42 42 assert_eq!(theme.meta.name, "Ocean Dark");
43 43 assert_eq!(theme.meta.variant, "dark");
44 44
45 - let t = theme_common::resolve(&theme);
45 + let t = makeover::resolve(&theme);
46 46 // Base intents pass through.
47 47 assert_eq!(t.hex("surface-page"), Some("#1a1a2e"));
48 48 assert_eq!(t.hex("content"), Some("#e0e0e0"));
@@ -58,14 +58,14 @@ six = "#4cc9f0"
58 58
59 59 #[test]
60 60 fn theme_variant_defaults_to_dark() {
61 - let theme = theme_common::parse_theme_str("m", "[meta]\nname = \"M\"\n", false).unwrap();
61 + let theme = makeover::parse_theme_str("m", "[meta]\nname = \"M\"\n", false).unwrap();
62 62 assert_eq!(theme.meta.variant, "dark");
63 63 }
64 64
65 65 #[test]
66 66 fn unknown_sections_are_ignored() {
67 67 let toml = "[surface]\npage = \"#000\"\n[custom_section]\nfoo = \"bar\"\n";
68 - let theme = theme_common::parse_theme_str("c", toml, false).unwrap();
68 + let theme = makeover::parse_theme_str("c", toml, false).unwrap();
69 69 assert_eq!(theme.colors.get("surface.page").unwrap(), "#000");
70 70 assert!(!theme.colors.contains_key("custom_section.foo"));
71 71 }