max / goingson
10 files changed,
+2023 insertions,
-95 deletions
| @@ -71,7 +71,7 @@ dependencies = [ | |||
| 71 | 71 | "cssparser 0.35.0", | |
| 72 | 72 | "html5ever 0.35.0", | |
| 73 | 73 | "maplit", | |
| 74 | - | "tendril", | |
| 74 | + | "tendril 0.4.3", | |
| 75 | 75 | "url", | |
| 76 | 76 | ] | |
| 77 | 77 | ||
| @@ -86,18 +86,9 @@ dependencies = [ | |||
| 86 | 86 | ||
| 87 | 87 | [[package]] | |
| 88 | 88 | name = "anyhow" | |
| 89 | - | version = "1.0.101" | |
| 89 | + | version = "1.0.102" | |
| 90 | 90 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 91 | - | checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" | |
| 92 | - | ||
| 93 | - | [[package]] | |
| 94 | - | name = "ar_archive_writer" | |
| 95 | - | version = "0.5.1" | |
| 96 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 97 | - | checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" | |
| 98 | - | dependencies = [ | |
| 99 | - | "object", | |
| 100 | - | ] | |
| 91 | + | checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" | |
| 101 | 92 | ||
| 102 | 93 | [[package]] | |
| 103 | 94 | name = "arbitrary" | |
| @@ -157,9 +148,9 @@ dependencies = [ | |||
| 157 | 148 | ||
| 158 | 149 | [[package]] | |
| 159 | 150 | name = "async-compression" | |
| 160 | - | version = "0.4.37" | |
| 151 | + | version = "0.4.41" | |
| 161 | 152 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 162 | - | checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40" | |
| 153 | + | checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" | |
| 163 | 154 | dependencies = [ | |
| 164 | 155 | "compression-codecs", | |
| 165 | 156 | "compression-core", | |
| @@ -169,9 +160,9 @@ dependencies = [ | |||
| 169 | 160 | ||
| 170 | 161 | [[package]] | |
| 171 | 162 | name = "async-executor" | |
| 172 | - | version = "1.13.3" | |
| 163 | + | version = "1.14.0" | |
| 173 | 164 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 174 | - | checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" | |
| 165 | + | checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" | |
| 175 | 166 | dependencies = [ | |
| 176 | 167 | "async-task", | |
| 177 | 168 | "concurrent-queue", | |
| @@ -259,7 +250,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" | |||
| 259 | 250 | dependencies = [ | |
| 260 | 251 | "proc-macro2", | |
| 261 | 252 | "quote", | |
| 262 | - | "syn 2.0.114", | |
| 253 | + | "syn 2.0.117", | |
| 263 | 254 | ] | |
| 264 | 255 | ||
| 265 | 256 | [[package]] | |
| @@ -316,7 +307,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" | |||
| 316 | 307 | dependencies = [ | |
| 317 | 308 | "proc-macro2", | |
| 318 | 309 | "quote", | |
| 319 | - | "syn 2.0.114", | |
| 310 | + | "syn 2.0.117", | |
| 320 | 311 | ] | |
| 321 | 312 | ||
| 322 | 313 | [[package]] | |
| @@ -382,6 +373,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 382 | 373 | checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" | |
| 383 | 374 | ||
| 384 | 375 | [[package]] | |
| 376 | + | name = "bit-set" | |
| 377 | + | version = "0.8.0" | |
| 378 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 379 | + | checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" | |
| 380 | + | dependencies = [ | |
| 381 | + | "bit-vec", | |
| 382 | + | ] | |
| 383 | + | ||
| 384 | + | [[package]] | |
| 385 | + | name = "bit-vec" | |
| 386 | + | version = "0.8.0" | |
| 387 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 388 | + | checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" | |
| 389 | + | ||
| 390 | + | [[package]] | |
| 385 | 391 | name = "bitflags" | |
| 386 | 392 | version = "1.3.2" | |
| 387 | 393 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -389,9 +395,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" | |||
| 389 | 395 | ||
| 390 | 396 | [[package]] | |
| 391 | 397 | name = "bitflags" | |
| 392 | - | version = "2.10.0" | |
| 398 | + | version = "2.11.0" | |
| 393 | 399 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 394 | - | checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" | |
| 400 | + | checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" | |
| 395 | 401 | dependencies = [ | |
| 396 | 402 | "serde_core", | |
| 397 | 403 | ] | |
| @@ -459,9 +465,9 @@ dependencies = [ | |||
| 459 | 465 | ||
| 460 | 466 | [[package]] | |
| 461 | 467 | name = "bumpalo" | |
| 462 | - | version = "3.19.1" | |
| 468 | + | version = "3.20.2" | |
| 463 | 469 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 464 | - | checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" | |
| 470 | + | checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" | |
| 465 | 471 | ||
| 466 | 472 | [[package]] | |
| 467 | 473 | name = "bytemuck" | |
| @@ -496,7 +502,7 @@ version = "0.18.5" | |||
| 496 | 502 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 497 | 503 | checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" | |
| 498 | 504 | dependencies = [ | |
| 499 | - | "bitflags 2.10.0", | |
| 505 | + | "bitflags 2.11.0", | |
| 500 | 506 | "cairo-sys-rs", | |
| 501 | 507 | "glib", | |
| 502 | 508 | "libc", | |
| @@ -554,14 +560,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 554 | 560 | checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" | |
| 555 | 561 | dependencies = [ | |
| 556 | 562 | "serde", | |
| 557 | - | "toml 0.9.11+spec-1.1.0", | |
| 563 | + | "toml 0.9.12+spec-1.1.0", | |
| 558 | 564 | ] | |
| 559 | 565 | ||
| 560 | 566 | [[package]] | |
| 561 | 567 | name = "cc" | |
| 562 | - | version = "1.2.55" | |
| 568 | + | version = "1.2.58" | |
| 563 | 569 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 564 | - | checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" | |
| 570 | + | checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" | |
| 565 | 571 | dependencies = [ | |
| 566 | 572 | "find-msvc-tools", | |
| 567 | 573 | "shlex", | |
| @@ -636,9 +642,9 @@ dependencies = [ | |||
| 636 | 642 | ||
| 637 | 643 | [[package]] | |
| 638 | 644 | name = "chrono" | |
| 639 | - | version = "0.4.43" | |
| 645 | + | version = "0.4.44" | |
| 640 | 646 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 641 | - | checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" | |
| 647 | + | checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" | |
| 642 | 648 | dependencies = [ | |
| 643 | 649 | "iana-time-zone", | |
| 644 | 650 | "js-sys", | |
| @@ -649,16 +655,6 @@ dependencies = [ | |||
| 649 | 655 | ] | |
| 650 | 656 | ||
| 651 | 657 | [[package]] | |
| 652 | - | name = "chumsky" | |
| 653 | - | version = "0.9.3" | |
| 654 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 655 | - | checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9" | |
| 656 | - | dependencies = [ | |
| 657 | - | "hashbrown 0.14.5", | |
| 658 | - | "stacker", | |
| 659 | - | ] | |
| 660 | - | ||
| 661 | - | [[package]] | |
| 662 | 658 | name = "cipher" | |
| 663 | 659 | version = "0.4.4" | |
| 664 | 660 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -681,9 +677,9 @@ dependencies = [ | |||
| 681 | 677 | ||
| 682 | 678 | [[package]] | |
| 683 | 679 | name = "compression-codecs" | |
| 684 | - | version = "0.4.36" | |
| 680 | + | version = "0.4.37" | |
| 685 | 681 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 686 | - | checksum = "00828ba6fd27b45a448e57dbfe84f1029d4c9f26b368157e9a448a5f49a2ec2a" | |
| 682 | + | checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" | |
| 687 | 683 | dependencies = [ | |
| 688 | 684 | "compression-core", | |
| 689 | 685 | "flate2", | |
| @@ -774,11 +770,11 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" | |||
| 774 | 770 | ||
| 775 | 771 | [[package]] | |
| 776 | 772 | name = "core-graphics" | |
| 777 | - | version = "0.24.0" | |
| 773 | + | version = "0.25.0" | |
| 778 | 774 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 779 | - | checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" | |
| 775 | + | checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" | |
| 780 | 776 | dependencies = [ | |
| 781 | - | "bitflags 2.10.0", | |
| 777 | + | "bitflags 2.11.0", | |
| 782 | 778 | "core-foundation 0.10.1", | |
| 783 | 779 | "core-graphics-types", | |
| 784 | 780 | "foreign-types 0.5.0", | |
| @@ -791,7 +787,7 @@ version = "0.2.0" | |||
| 791 | 787 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 792 | 788 | checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" | |
| 793 | 789 | dependencies = [ | |
| 794 | - | "bitflags 2.10.0", | |
| 790 | + | "bitflags 2.11.0", | |
| 795 | 791 | "core-foundation 0.10.1", | |
| 796 | 792 | "libc", | |
| 797 | 793 | ] | |
| @@ -901,13 +897,26 @@ dependencies = [ | |||
| 901 | 897 | ] | |
| 902 | 898 | ||
| 903 | 899 | [[package]] | |
| 900 | + | name = "cssparser" | |
| 901 | + | version = "0.36.0" | |
| 902 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 903 | + | checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" | |
| 904 | + | dependencies = [ | |
| 905 | + | "cssparser-macros", | |
| 906 | + | "dtoa-short", | |
| 907 | + | "itoa", | |
| 908 | + | "phf 0.13.1", | |
| 909 | + | "smallvec", | |
| 910 | + | ] | |
| 911 | + | ||
| 912 | + | [[package]] | |
| 904 | 913 | name = "cssparser-macros" | |
| 905 | 914 | version = "0.6.1" | |
| 906 | 915 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 907 | 916 | checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" | |
| 908 | 917 | dependencies = [ | |
| 909 | 918 | "quote", | |
| 910 | - | "syn 2.0.114", | |
| 919 | + | "syn 2.0.117", | |
| 911 | 920 | ] | |
| 912 | 921 | ||
| 913 | 922 | [[package]] | |
| @@ -938,14 +947,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 938 | 947 | checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" | |
| 939 | 948 | dependencies = [ | |
| 940 | 949 | "quote", | |
| 941 | - | "syn 2.0.114", | |
| 950 | + | "syn 2.0.117", | |
| 942 | 951 | ] | |
| 943 | 952 | ||
| 944 | 953 | [[package]] | |
| 945 | 954 | name = "darling" | |
| 946 | - | version = "0.21.3" | |
| 955 | + | version = "0.23.0" | |
| 947 | 956 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 948 | - | checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" | |
| 957 | + | checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" | |
| 949 | 958 | dependencies = [ | |
| 950 | 959 | "darling_core", | |
| 951 | 960 | "darling_macro", | |
| @@ -953,27 +962,26 @@ dependencies = [ | |||
| 953 | 962 | ||
| 954 | 963 | [[package]] | |
| 955 | 964 | name = "darling_core" | |
| 956 | - | version = "0.21.3" | |
| 965 | + | version = "0.23.0" | |
| 957 | 966 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 958 | - | checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" | |
| 967 | + | checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" | |
| 959 | 968 | dependencies = [ | |
| 960 | - | "fnv", | |
| 961 | 969 | "ident_case", | |
| 962 | 970 | "proc-macro2", | |
| 963 | 971 | "quote", | |
| 964 | 972 | "strsim", | |
| 965 | - | "syn 2.0.114", | |
| 973 | + | "syn 2.0.117", | |
| 966 | 974 | ] | |
| 967 | 975 | ||
| 968 | 976 | [[package]] | |
| 969 | 977 | name = "darling_macro" | |
| 970 | - | version = "0.21.3" | |
| 978 | + | version = "0.23.0" | |
| 971 | 979 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 972 | - | checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" | |
| 980 | + | checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" | |
| 973 | 981 | dependencies = [ | |
| 974 | 982 | "darling_core", | |
| 975 | 983 | "quote", | |
| 976 | - | "syn 2.0.114", | |
| 984 | + | "syn 2.0.117", | |
| 977 | 985 | ] | |
| 978 | 986 | ||
| 979 | 987 | [[package]] | |
| @@ -995,9 +1003,9 @@ dependencies = [ | |||
| 995 | 1003 | ||
| 996 | 1004 | [[package]] | |
| 997 | 1005 | name = "deranged" | |
| 998 | - | version = "0.5.5" | |
| 1006 | + | version = "0.5.8" | |
| 999 | 1007 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1000 | - | checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" | |
| 1008 | + | checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" | |
| 1001 | 1009 | dependencies = [ | |
| 1002 | 1010 | "powerfmt", | |
| 1003 | 1011 | "serde_core", | |
| @@ -1011,7 +1019,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" | |||
| 1011 | 1019 | dependencies = [ | |
| 1012 | 1020 | "proc-macro2", | |
| 1013 | 1021 | "quote", | |
| 1014 | - | "syn 2.0.114", | |
| 1022 | + | "syn 2.0.117", | |
| 1015 | 1023 | ] | |
| 1016 | 1024 | ||
| 1017 | 1025 | [[package]] | |
| @@ -1024,7 +1032,28 @@ dependencies = [ | |||
| 1024 | 1032 | "proc-macro2", | |
| 1025 | 1033 | "quote", | |
| 1026 | 1034 | "rustc_version", | |
| 1027 | - | "syn 2.0.114", | |
| 1035 | + | "syn 2.0.117", | |
| 1036 | + | ] | |
| 1037 | + | ||
| 1038 | + | [[package]] | |
| 1039 | + | name = "derive_more" | |
| 1040 | + | version = "2.1.1" | |
| 1041 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1042 | + | checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" | |
| 1043 | + | dependencies = [ | |
| 1044 | + | "derive_more-impl", | |
| 1045 | + | ] | |
| 1046 | + | ||
| 1047 | + | [[package]] | |
| 1048 | + | name = "derive_more-impl" | |
| 1049 | + | version = "2.1.1" | |
| 1050 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1051 | + | checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" | |
| 1052 | + | dependencies = [ | |
| 1053 | + | "proc-macro2", | |
| 1054 | + | "quote", | |
| 1055 | + | "rustc_version", | |
| 1056 | + | "syn 2.0.117", | |
| 1028 | 1057 | ] | |
| 1029 | 1058 | ||
| 1030 | 1059 | [[package]] | |
| @@ -1061,18 +1090,12 @@ dependencies = [ | |||
| 1061 | 1090 | ] | |
| 1062 | 1091 | ||
| 1063 | 1092 | [[package]] | |
| 1064 | - | name = "dispatch" | |
| 1065 | - | version = "0.2.0" | |
| 1066 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1067 | - | checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" | |
| 1068 | - | ||
| 1069 | - | [[package]] | |
| 1070 | 1093 | name = "dispatch2" | |
| 1071 | - | version = "0.3.0" | |
| 1094 | + | version = "0.3.1" | |
| 1072 | 1095 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1073 | - | checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" | |
| 1096 | + | checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" | |
| 1074 | 1097 | dependencies = [ | |
| 1075 | - | "bitflags 2.10.0", | |
| 1098 | + | "bitflags 2.11.0", | |
| 1076 | 1099 | "block2", | |
| 1077 | 1100 | "libc", | |
| 1078 | 1101 | "objc2", | |
| @@ -1086,7 +1109,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" | |||
| 1086 | 1109 | dependencies = [ | |
| 1087 | 1110 | "proc-macro2", | |
| 1088 | 1111 | "quote", | |
| 1089 | - | "syn 2.0.114", | |
| 1112 | + | "syn 2.0.117", | |
| 1090 | 1113 | ] | |
| 1091 | 1114 | ||
| 1092 | 1115 | [[package]] | |
| @@ -1109,7 +1132,7 @@ checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" | |||
| 1109 | 1132 | dependencies = [ | |
| 1110 | 1133 | "proc-macro2", | |
| 1111 | 1134 | "quote", | |
| 1112 | - | "syn 2.0.114", | |
| 1135 | + | "syn 2.0.117", | |
| 1113 | 1136 | ] | |
| 1114 | 1137 | ||
| 1115 | 1138 | [[package]] | |
| @@ -1122,6 +1145,21 @@ dependencies = [ | |||
| 1122 | 1145 | ] | |
| 1123 | 1146 | ||
| 1124 | 1147 | [[package]] | |
| 1148 | + | name = "dom_query" | |
| 1149 | + | version = "0.27.0" | |
| 1150 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1151 | + | checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" | |
| 1152 | + | dependencies = [ | |
| 1153 | + | "bit-set", | |
| 1154 | + | "cssparser 0.36.0", | |
| 1155 | + | "foldhash 0.2.0", | |
| 1156 | + | "html5ever 0.38.0", | |
| 1157 | + | "precomputed-hash", | |
| 1158 | + | "selectors 0.36.1", | |
| 1159 | + | "tendril 0.5.0", | |
| 1160 | + | ] | |
| 1161 | + | ||
| 1162 | + | [[package]] | |
| 1125 | 1163 | name = "dotenvy" | |
| 1126 | 1164 | version = "0.15.7" | |
| 1127 | 1165 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -1190,14 +1228,14 @@ checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" | |||
| 1190 | 1228 | ||
| 1191 | 1229 | [[package]] | |
| 1192 | 1230 | name = "embed-resource" | |
| 1193 | - | version = "3.0.6" | |
| 1231 | + | version = "3.0.8" | |
| 1194 | 1232 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1195 | - | checksum = "55a075fc573c64510038d7ee9abc7990635863992f83ebc52c8b433b8411a02e" | |
| 1233 | + | checksum = "63a1d0de4f2249aa0ff5884d7080814f446bb241a559af6c170a41e878ed2d45" | |
| 1196 | 1234 | dependencies = [ | |
| 1197 | 1235 | "cc", | |
| 1198 | 1236 | "memchr", | |
| 1199 | 1237 | "rustc_version", | |
| 1200 | - | "toml 0.9.11+spec-1.1.0", | |
| 1238 | + | "toml 0.9.12+spec-1.1.0", | |
| 1201 | 1239 | "vswhom", | |
| 1202 | 1240 | "winreg", | |
| 1203 | 1241 | ] | |
| @@ -1241,7 +1279,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" | |||
| 1241 | 1279 | dependencies = [ | |
| 1242 | 1280 | "proc-macro2", | |
| 1243 | 1281 | "quote", | |
| 1244 | - | "syn 2.0.114", | |
| 1282 | + | "syn 2.0.117", | |
| 1245 | 1283 | ] | |
| 1246 | 1284 | ||
| 1247 | 1285 | [[package]] | |
| @@ -1252,9 +1290,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" | |||
| 1252 | 1290 | ||
| 1253 | 1291 | [[package]] | |
| 1254 | 1292 | name = "erased-serde" | |
| 1255 | - | version = "0.4.9" | |
| 1293 | + | version = "0.4.10" | |
| 1256 | 1294 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1257 | - | checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3" | |
| 1295 | + | checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" | |
| 1258 | 1296 | dependencies = [ | |
| 1259 | 1297 | "serde", | |
| 1260 | 1298 | "serde_core", | |
| @@ -1385,6 +1423,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 1385 | 1423 | checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" | |
| 1386 | 1424 | ||
| 1387 | 1425 | [[package]] | |
| 1426 | + | name = "foldhash" | |
| 1427 | + | version = "0.2.0" | |
| 1428 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1429 | + | checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" | |
| 1430 | + | ||
| 1431 | + | [[package]] | |
| 1388 | 1432 | name = "foreign-types" | |
| 1389 | 1433 | version = "0.3.2" | |
| 1390 | 1434 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -1411,7 +1455,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" | |||
| 1411 | 1455 | dependencies = [ | |
| 1412 | 1456 | "proc-macro2", | |
| 1413 | 1457 | "quote", | |
| 1414 | - | "syn 2.0.114", | |
| 1458 | + | "syn 2.0.117", | |
| 1415 | 1459 | ] | |
| 1416 | 1460 | ||
| 1417 | 1461 | [[package]] | |
| @@ -1456,9 +1500,9 @@ dependencies = [ | |||
| 1456 | 1500 | ||
| 1457 | 1501 | [[package]] | |
| 1458 | 1502 | name = "futures" | |
| 1459 | - | version = "0.3.31" | |
| 1503 | + | version = "0.3.32" | |
| 1460 | 1504 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1461 | - | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" | |
| 1505 | + | checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" | |
| 1462 | 1506 | dependencies = [ | |
| 1463 | 1507 | "futures-channel", | |
| 1464 | 1508 | "futures-core", | |
| @@ -1471,9 +1515,9 @@ dependencies = [ | |||
| 1471 | 1515 | ||
| 1472 | 1516 | [[package]] | |
| 1473 | 1517 | name = "futures-channel" | |
| 1474 | - | version = "0.3.31" | |
| 1518 | + | version = "0.3.32" | |
| 1475 | 1519 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1476 | - | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" | |
| 1520 | + | checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" | |
| 1477 | 1521 | dependencies = [ | |
| 1478 | 1522 | "futures-core", | |
| 1479 | 1523 | "futures-sink", | |
| @@ -1481,15 +1525,15 @@ dependencies = [ | |||
| 1481 | 1525 | ||
| 1482 | 1526 | [[package]] | |
| 1483 | 1527 | name = "futures-core" | |
| 1484 | - | version = "0.3.31" | |
| 1528 | + | version = "0.3.32" | |
| 1485 | 1529 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1486 | - | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" | |
| 1530 | + | checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" | |
| 1487 | 1531 | ||
| 1488 | 1532 | [[package]] | |
| 1489 | 1533 | name = "futures-executor" | |
| 1490 | - | version = "0.3.31" | |
| 1534 | + | version = "0.3.32" | |
| 1491 | 1535 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1492 | - | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" | |
| 1536 | + | checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" | |
| 1493 | 1537 | dependencies = [ | |
| 1494 | 1538 | "futures-core", | |
| 1495 | 1539 | "futures-task", | |
| @@ -1509,9 +1553,9 @@ dependencies = [ | |||
| 1509 | 1553 | ||
| 1510 | 1554 | [[package]] | |
| 1511 | 1555 | name = "futures-io" | |
| 1512 | - | version = "0.3.31" | |
| 1556 | + | version = "0.3.32" | |
| 1513 | 1557 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1514 | - | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" | |
| 1558 | + | checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" | |
| 1515 | 1559 | ||
| 1516 | 1560 | [[package]] | |
| 1517 | 1561 | name = "futures-lite" | |
| @@ -1528,32 +1572,32 @@ dependencies = [ | |||
| 1528 | 1572 | ||
| 1529 | 1573 | [[package]] | |
| 1530 | 1574 | name = "futures-macro" | |
| 1531 | - | version = "0.3.31" | |
| 1575 | + | version = "0.3.32" | |
| 1532 | 1576 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1533 | - | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" | |
| 1577 | + | checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" | |
| 1534 | 1578 | dependencies = [ | |
| 1535 | 1579 | "proc-macro2", | |
| 1536 | 1580 | "quote", | |
| 1537 | - | "syn 2.0.114", | |
| 1581 | + | "syn 2.0.117", | |
| 1538 | 1582 | ] | |
| 1539 | 1583 |
Lines truncated
| @@ -67,7 +67,7 @@ tauri-plugin-updater = "2" | |||
| 67 | 67 | rhai = { version = "1.17", features = ["sync", "serde"] } | |
| 68 | 68 | notify = "6.0" | |
| 69 | 69 | notify-debouncer-mini = "0.4" | |
| 70 | - | theme-common = { path = "../theme-common" } | |
| 70 | + | theme-common = { path = "../../Shared/theme-common" } | |
| 71 | 71 | toml = "0.8" | |
| 72 | 72 | ||
| 73 | 73 | # Enums | |
| @@ -85,7 +85,7 @@ flate2 = "1.0" | |||
| 85 | 85 | open = "5" | |
| 86 | 86 | ||
| 87 | 87 | # Markdown rendering + HTML sanitization | |
| 88 | - | docengine = { path = "../docengine" } | |
| 88 | + | docengine = { path = "../../Shared/docengine" } | |
| 89 | 89 | ||
| 90 | 90 | # Filesystem | |
| 91 | 91 | dirs = "6" | |
| @@ -95,7 +95,7 @@ tracing = "0.1" | |||
| 95 | 95 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } | |
| 96 | 96 | ||
| 97 | 97 | # Tag standard | |
| 98 | - | tagtree = { path = "../tagtree" } | |
| 98 | + | tagtree = { path = "../../Shared/tagtree" } | |
| 99 | 99 | ||
| 100 | 100 | # Internal crates | |
| 101 | 101 | goingson-core = { path = "crates/core" } |
| @@ -0,0 +1,374 @@ | |||
| 1 | + | # GoingsOn Architecture | |
| 2 | + | ||
| 3 | + | Email, calendar, tasks in one place. Project management for individuals and small teams. | |
| 4 | + | ||
| 5 | + | A Rust-based productivity application built with Tauri 2 (Rust backend + Vanilla JS frontend), SQLite with sqlx 0.8, and a "Skeubrute" design aesthetic. 4 crates: `core` (domain models), `db-sqlite` (repository), `goingson-mcp` (Claude Desktop integration), `plugin-runtime` (Rhai plugins). | |
| 6 | + | ||
| 7 | + | ## High-Level Overview | |
| 8 | + | ||
| 9 | + | ``` | |
| 10 | + | ┌─────────────────────────────────────────────────────────┐ | |
| 11 | + | │ User Interface │ | |
| 12 | + | │ ┌────────────────────────────────────────────────────┐ │ | |
| 13 | + | │ │ Tauri Desktop (Vanilla JS) │ │ | |
| 14 | + | │ └─────────────────────┬──────────────────────────────┘ │ | |
| 15 | + | │ │ │ | |
| 16 | + | │ ┌─────────────────────▼──────────────────────────────┐ │ | |
| 17 | + | │ │ Tauri Commands (src-tauri/) │ │ | |
| 18 | + | │ └─────────────────────┬──────────────────────────────┘ │ | |
| 19 | + | └────────────────────────┼─────────────────────────────────┘ | |
| 20 | + | │ | |
| 21 | + | ┌────────────────────────▼─────────────────────────────────┐ | |
| 22 | + | │ goingson-core │ | |
| 23 | + | │ ┌──────────┐ ┌──────────┐ ┌────────────┐ ┌────────────┐ │ | |
| 24 | + | │ │ Models │ │Repository│ │ Urgency │ │ Parser │ │ | |
| 25 | + | │ │ │ │ Traits │ │ Calculator │ │ (Quick-Add)│ │ | |
| 26 | + | │ └──────────┘ └────┬─────┘ └────────────┘ └────────────┘ │ | |
| 27 | + | └────────────────────┼─────────────────────────────────────┘ | |
| 28 | + | │ | |
| 29 | + | ┌──────▼───────────┐ | |
| 30 | + | │ goingson-db- │ | |
| 31 | + | │ sqlite │ | |
| 32 | + | │ (Desktop) │ | |
| 33 | + | └──────────────────┘ | |
| 34 | + | ||
| 35 | + | Additional crates: | |
| 36 | + | goingson-mcp MCP server (Claude Desktop integration) | |
| 37 | + | plugin-runtime Rhai plugin system (import plugins) | |
| 38 | + | ``` | |
| 39 | + | ||
| 40 | + | ## Workspace Structure | |
| 41 | + | ||
| 42 | + | ``` | |
| 43 | + | goingson/ | |
| 44 | + | ├── crates/ | |
| 45 | + | │ ├── core/ # Domain models, traits, business logic | |
| 46 | + | │ ├── db-sqlite/ # SQLite repository implementations | |
| 47 | + | │ ├── goingson-mcp/ # MCP server for Claude Desktop | |
| 48 | + | │ └── plugin-runtime/ # Rhai plugin system | |
| 49 | + | ├── src-tauri/ # Tauri desktop app (single-user) | |
| 50 | + | └── migrations/ | |
| 51 | + | └── sqlite/ # SQLite schema migrations (32 files) | |
| 52 | + | ``` | |
| 53 | + | ||
| 54 | + | ## Crate Dependencies | |
| 55 | + | ||
| 56 | + | ``` | |
| 57 | + | goingson-desktop (src-tauri) | |
| 58 | + | ├── goingson-db-sqlite | |
| 59 | + | │ └── goingson-core | |
| 60 | + | ├── goingson-mcp | |
| 61 | + | │ └── goingson-core | |
| 62 | + | └── plugin-runtime | |
| 63 | + | └── goingson-core (for shared types) | |
| 64 | + | ``` | |
| 65 | + | ||
| 66 | + | ## Core Crate (`crates/core/`) | |
| 67 | + | ||
| 68 | + | The core crate defines domain models and repository traits, independent of persistence. | |
| 69 | + | ||
| 70 | + | ### Modules | |
| 71 | + | ||
| 72 | + | | Module | Purpose | | |
| 73 | + | |--------|---------| | |
| 74 | + | | `models/` | Domain types (13 model files) | | |
| 75 | + | | `repository.rs` | Repository traits (data access contracts) | | |
| 76 | + | | `urgency.rs` | TaskWarrior-inspired urgency calculation algorithm | | |
| 77 | + | | `parser.rs` | Quick-add natural language parser | | |
| 78 | + | | `recurrence.rs` | Task/event recurrence logic | | |
| 79 | + | | `validation.rs` | Input validation trait | | |
| 80 | + | | `constants.rs` | Named constants for thresholds, formats | | |
| 81 | + | | `error.rs` | Unified CoreError type | | |
| 82 | + | ||
| 83 | + | ### Key Types | |
| 84 | + | ||
| 85 | + | ```rust | |
| 86 | + | // Domain entities | |
| 87 | + | Project, Task, Event, Email, EmailAccount, User | |
| 88 | + | Contact, ContactEmail, ContactPhone, SocialHandle, ContactCustomField | |
| 89 | + | SavedView, Annotation, Subtask, Milestone | |
| 90 | + | WeeklyReview, LlmSettings, BackupSettings | |
| 91 | + | ||
| 92 | + | // Enums with display/parse support | |
| 93 | + | ProjectType, ProjectStatus, TaskStatus, Priority, Recurrence | |
| 94 | + | SortDirection, SortField, TaskSortColumn | |
| 95 | + | ViewType, ViewFilters, BlockType | |
| 96 | + | EmailAuthType, LlmProviderType, MilestoneStatus | |
| 97 | + | ||
| 98 | + | // DTOs for creation/updates | |
| 99 | + | NewProject, NewTask, NewEvent, NewEmail | |
| 100 | + | UpdateProject, UpdateTask, UpdateEvent | |
| 101 | + | ||
| 102 | + | // Newtype IDs | |
| 103 | + | ProjectId, TaskId, EventId, EmailId, ContactId | |
| 104 | + | EmailAccountId, AnnotationId, SubtaskId, SavedViewId | |
| 105 | + | MilestoneId, CustomFieldId | |
| 106 | + | ``` | |
| 107 | + | ||
| 108 | + | ### Repository Traits | |
| 109 | + | ||
| 110 | + | ```rust | |
| 111 | + | ProjectRepository, TaskRepository, EventRepository | |
| 112 | + | EmailRepository, EmailAccountRepository, ContactRepository | |
| 113 | + | SearchRepository, StatsRepository, SavedViewRepository | |
| 114 | + | LlmSettingsRepository, LlmCacheRepository | |
| 115 | + | AnnotationRepository, SubtaskRepository, MilestoneRepository | |
| 116 | + | WeeklyReviewRepository, BackupSettingsRepository | |
| 117 | + | UserRepository | |
| 118 | + | ``` | |
| 119 | + | ||
| 120 | + | ## Database Layer (`crates/db-sqlite/`) | |
| 121 | + | ||
| 122 | + | SQLite persistence for the desktop app. Single-user, local storage. 32 migrations in `migrations/sqlite/`. | |
| 123 | + | ||
| 124 | + | ``` | |
| 125 | + | src/ | |
| 126 | + | ├── lib.rs # SQLite pool initialization | |
| 127 | + | ├── utils.rs # format_datetime, parse_uuid, email validation | |
| 128 | + | └── repository/ | |
| 129 | + | ├── mod.rs # Re-exports all repositories | |
| 130 | + | ├── project_repo.rs | |
| 131 | + | ├── task_repo.rs | |
| 132 | + | ├── event_repo.rs | |
| 133 | + | ├── email_repo.rs | |
| 134 | + | ├── email_account_repo.rs | |
| 135 | + | ├── contact_repo.rs | |
| 136 | + | ├── user_repo.rs | |
| 137 | + | ├── search_repo.rs # FTS5 full-text search | |
| 138 | + | ├── stats_repo.rs # Dashboard aggregations | |
| 139 | + | ├── saved_view_repo.rs | |
| 140 | + | ├── llm_repo.rs # LLM settings + response cache | |
| 141 | + | ├── annotation_repo.rs | |
| 142 | + | ├── subtask_repo.rs | |
| 143 | + | ├── milestone_repo.rs | |
| 144 | + | ├── weekly_review_repo.rs | |
| 145 | + | └── backup_settings_repo.rs | |
| 146 | + | ``` | |
| 147 | + | ||
| 148 | + | ## Tauri Desktop App (`src-tauri/`) | |
| 149 | + | ||
| 150 | + | Single-user desktop application. | |
| 151 | + | ||
| 152 | + | ``` | |
| 153 | + | src/ | |
| 154 | + | ├── main.rs # Tauri app setup, command registration | |
| 155 | + | ├── state.rs # AppState with repository instances | |
| 156 | + | ├── notifications.rs # Snooze watcher, native notifications | |
| 157 | + | ├── email/ # IMAP/SMTP client | |
| 158 | + | ├── llm/ # LLM provider clients (Ollama, OpenAI) | |
| 159 | + | └── commands/ | |
| 160 | + | ├── mod.rs # Re-exports all commands | |
| 161 | + | ├── error.rs # Error type definitions | |
| 162 | + | ├── task.rs # Task CRUD, annotations, subtasks, snoozing, waiting | |
| 163 | + | ├── project.rs # Project management | |
| 164 | + | ├── event.rs # Calendar events | |
| 165 | + | ├── email.rs # Email CRUD, threading, archive | |
| 166 | + | ├── email_account.rs # Email account setup, sync interval | |
| 167 | + | ├── email_sync.rs # IMAP/SMTP sync orchestration | |
| 168 | + | ├── contact.rs # Contact CRUD, emails, phones, social handles | |
| 169 | + | ├── search.rs # Full-text search across all entities | |
| 170 | + | ├── stats.rs # Dashboard statistics | |
| 171 | + | ├── day_planning.rs # Time blocking | |
| 172 | + | ├── weekly_review.rs # Weekly review workflow | |
| 173 | + | ├── saved_views.rs # Custom filter views | |
| 174 | + | ├── milestone.rs # Milestone CRUD and reordering | |
| 175 | + | ├── llm.rs # LLM settings, template evaluation | |
| 176 | + | ├── plugin.rs # Plugin registry, hot-reload, import | |
| 177 | + | ├── oauth.rs # OAuth2 flows (Fastmail, Google, Microsoft) | |
| 178 | + | ├── export.rs # JSON, CSV, ICS export; backup/restore | |
| 179 | + | ├── sync.rs # Cloud sync via SyncKit | |
| 180 | + | ├── themes.rs # Theme list and color queries | |
| 181 | + | └── window.rs # Window management, compose window | |
| 182 | + | ``` | |
| 183 | + | ||
| 184 | + | ### Command Pattern | |
| 185 | + | ||
| 186 | + | Tauri commands are async functions that: | |
| 187 | + | 1. Accept `State<Arc<AppState>>` for repository access | |
| 188 | + | 2. Deserialize input from frontend via `#[serde(rename_all = "camelCase")]` | |
| 189 | + | 3. Call repository methods | |
| 190 | + | 4. Serialize response types back to frontend | |
| 191 | + | ||
| 192 | + | ```rust | |
| 193 | + | #[tauri::command] | |
| 194 | + | pub async fn create_task( | |
| 195 | + | state: State<'_, Arc<AppState>>, | |
| 196 | + | input: TaskInput, | |
| 197 | + | ) -> Result<TaskResponse, String> { | |
| 198 | + | state.tasks | |
| 199 | + | .create(DESKTOP_USER_ID, new_task) | |
| 200 | + | .await | |
| 201 | + | .map(TaskResponse::from) | |
| 202 | + | .map_err(|e| e.to_string()) | |
| 203 | + | } | |
| 204 | + | ``` | |
| 205 | + | ||
| 206 | + | ## Frontend Architecture (Tauri Desktop) | |
| 207 | + | ||
| 208 | + | The desktop frontend uses vanilla JavaScript organized under the `GoingsOn` global namespace. 39 source files + 2 test files. | |
| 209 | + | ||
| 210 | + | ### Namespace Organization | |
| 211 | + | ||
| 212 | + | ``` | |
| 213 | + | window.GoingsOn = { | |
| 214 | + | api: { ... }, // Tauri IPC abstraction layer | |
| 215 | + | state: { ... }, // Centralized state with pub/sub | |
| 216 | + | ui: { ... }, // Modal, toast, form utilities | |
| 217 | + | utils: { ... }, // HTML escaping, validation | |
| 218 | + | ||
| 219 | + | // Domain modules (IIFE-wrapped) | |
| 220 | + | projects: { ... }, | |
| 221 | + | tasks: { ... }, | |
| 222 | + | events: { ... }, | |
| 223 | + | emails: { ... }, | |
| 224 | + | contacts: { ... }, | |
| 225 | + | ||
| 226 | + | // Feature modules | |
| 227 | + | savedViews: { ... }, | |
| 228 | + | snooze: { ... }, | |
| 229 | + | navigation: { ... }, | |
| 230 | + | settings: { ... }, | |
| 231 | + | app: { ... }, | |
| 232 | + | ||
| 233 | + | // Infrastructure | |
| 234 | + | VirtualScroller, // Virtual scrolling for large lists | |
| 235 | + | SelectionManager, // Multi-select with shift/ctrl | |
| 236 | + | PaginationManager, // Page navigation | |
| 237 | + | }; | |
| 238 | + | ``` | |
| 239 | + | ||
| 240 | + | ### Module Pattern | |
| 241 | + | ||
| 242 | + | Each domain module is wrapped in an IIFE and exposes its public API through the namespace: | |
| 243 | + | ||
| 244 | + | ```javascript | |
| 245 | + | (function() { | |
| 246 | + | 'use strict'; | |
| 247 | + | // Private state and helpers | |
| 248 | + | async function load() { ... } | |
| 249 | + | function openNew() { ... } | |
| 250 | + | ||
| 251 | + | // Public API | |
| 252 | + | GoingsOn.myModule = { load, openNew }; | |
| 253 | + | })(); | |
| 254 | + | ``` | |
| 255 | + | ||
| 256 | + | ### Pre-computed Response Fields | |
| 257 | + | ||
| 258 | + | Rust response types include pre-computed display values so JS never calculates dates, formatting, or derived state: | |
| 259 | + | ||
| 260 | + | | Response Type | Pre-computed Fields | | |
| 261 | + | |--------------|---------------------| | |
| 262 | + | | TaskResponse | `dueFormatted`, `urgencyClass`, `isOverdue`, `isSnoozed`, `subtaskCount`, `subtaskCompleted`, `subtaskProgress` | | |
| 263 | + | | EventResponse | `timeFormatted`, `dateFormatted`, `isPast`, `proximityClass`, `proximityLabel` | | |
| 264 | + | | EmailResponse | `receivedFormatted` | | |
| 265 | + | | EmailAccountResponse | `lastSyncFormatted` | | |
| 266 | + | ||
| 267 | + | ### Centralized State | |
| 268 | + | ||
| 269 | + | All shared data lives in `GoingsOn.state` with reactive pub/sub: | |
| 270 | + | ||
| 271 | + | ```javascript | |
| 272 | + | GoingsOn.state.set('tasks', updatedTasks); // Triggers subscribers | |
| 273 | + | GoingsOn.state.subscribe('tasks', (newVal, oldVal) => { ... }); | |
| 274 | + | ``` | |
| 275 | + | ||
| 276 | + | ### File Organization | |
| 277 | + | ||
| 278 | + | ``` | |
| 279 | + | src-tauri/frontend/ | |
| 280 | + | ├── css/ | |
| 281 | + | │ └── styles.css # Design system + all components | |
| 282 | + | ├── fonts/ | |
| 283 | + | │ └── Reglo-Bold.woff2 # Display font | |
| 284 | + | ├── js/ | |
| 285 | + | │ ├── goingson.js # Namespace root (window.GoingsOn) | |
| 286 | + | │ ├── api.js # Tauri IPC abstraction | |
| 287 | + | │ ├── state.js # Centralized state + pub/sub | |
| 288 | + | │ ├── utils.js # Escaping, validation, debounce | |
| 289 | + | │ ├── router.js # View routing | |
| 290 | + | │ ├── app.js # App initialization, menu listeners | |
| 291 | + | │ │ | |
| 292 | + | │ ├── components.js # Toast, confirm dialog | |
| 293 | + | │ ├── components-modal.js # Modal system | |
| 294 | + | │ ├── form-modal.js # Form modal (openFormModal) | |
| 295 | + | │ ├── navigation.js # View switching, sidebar | |
| 296 | + | │ ├── keyboard.js # Keyboard shortcuts | |
| 297 | + | │ ├── selection-manager.js # Multi-select with shift/ctrl | |
| 298 | + | │ ├── pagination-manager.js # Page navigation | |
| 299 | + | │ ├── virtual-scroller.js # Virtual scrolling for large lists | |
| 300 | + | │ ├── context-menus.js # Right-click context menus | |
| 301 | + | │ ├── bulk-actions.js # Multi-select bulk operations | |
| 302 | + | │ ├── touch.js # Touch event handling | |
| 303 | + | │ ├── mobile.js # Mobile-specific behavior | |
| 304 | + | │ │ | |
| 305 | + | │ ├── tasks.js # Task list, CRUD | |
| 306 | + | │ ├── tasks-render.js # Task rendering | |
| 307 | + | │ ├── tasks-kanban.js # Kanban board view | |
| 308 | + | │ ├── projects.js # Project list, detail, CRUD | |
| 309 | + | │ ├── projects-render.js # Project rendering | |
| 310 | + | │ ├── events.js # Event list, CRUD | |
| 311 | + | │ ├── emails.js # Email list, threading, CRUD | |
| 312 | + | │ ├── email-accounts.js # Email account management | |
| 313 | + | │ ├── contacts.js # Contact CRUD | |
| 314 | + | │ ├── contacts-render.js # Contact rendering | |
| 315 | + | │ │ | |
| 316 | + | │ ├── day-planning.js # Time-blocking day planner | |
| 317 | + | │ ├── day-planning-render.js # Day plan rendering | |
| 318 | + | │ ├── weekly-review.js # Weekly review workflow | |
| 319 | + | │ ├── weekly-review-render.js # Weekly review rendering | |
| 320 | + | │ ├── snooze.js # Snooze modal + actions | |
| 321 | + | │ ├── settings.js # Settings, LLM config, export | |
| 322 | + | │ ├── settings-sync.js # Cloud sync settings | |
| 323 | + | │ ├── themes.js # Theme switching | |
| 324 | + | │ ├── export.js # Data export | |
| 325 | + | │ ├── import.js # Data import from JSON | |
| 326 | + | │ ├── llm-templates.js # LLM template evaluation | |
| 327 | + | │ ├── seed-data.js # Demo data seeding | |
| 328 | + | │ │ | |
| 329 | + | │ └── tests/ | |
| 330 | + | │ ├── test-runner.js # Test framework | |
| 331 | + | │ └── run.js # Test execution | |
| 332 | + | └── index.html # Entry point | |
| 333 | + | ``` | |
| 334 | + | ||
| 335 | + | ## Data Flow | |
| 336 | + | ||
| 337 | + | ``` | |
| 338 | + | Frontend (JS) | |
| 339 | + | → invoke("command_name", { args }) | |
| 340 | + | → Tauri IPC | |
| 341 | + | → commands/module.rs | |
| 342 | + | → Repository trait method | |
| 343 | + | → SQLite query | |
| 344 | + | → Response (with pre-computed display fields) → Frontend | |
| 345 | + | → JS renders pre-computed values directly to DOM | |
| 346 | + | ``` | |
| 347 | + | ||
| 348 | + | ## Key Design Decisions | |
| 349 | + | ||
| 350 | + | ### Clean Architecture | |
| 351 | + | - Core domain models have no dependencies on persistence | |
| 352 | + | - Repository traits define contracts, implementations are separate crates | |
| 353 | + | - Easy to swap databases or add new ones | |
| 354 | + | ||
| 355 | + | ### Vanilla Frontend | |
| 356 | + | - No JavaScript framework — vanilla JS with IIFE modules | |
| 357 | + | - All code under `GoingsOn` global namespace (no `window.*` exports) | |
| 358 | + | - Centralized state via `GoingsOn.state` with pub/sub reactivity | |
| 359 | + | - IPC via Tauri invoke | |
| 360 | + | - Virtual scrolling for large lists (`GoingsOn.VirtualScroller`) | |
| 361 | + | - Optimized for desktop-class performance | |
| 362 | + | ||
| 363 | + | ### TaskWarrior-Inspired Features | |
| 364 | + | - Urgency calculation algorithm | |
| 365 | + | - Quick-add parser with natural language | |
| 366 | + | - Annotations and recurring tasks | |
| 367 | + | ||
| 368 | + | ## Testing Strategy | |
| 369 | + | ||
| 370 | + | - 658 Rust tests + 48 JS tests | |
| 371 | + | - Unit tests in core crate for business logic | |
| 372 | + | - Integration tests for repository implementations | |
| 373 | + | - Tauri command tests | |
| 374 | + | - JS tests in `frontend/js/tests/` |
| @@ -0,0 +1,373 @@ | |||
| 1 | + | # GoingsOn -- Audit Review | |
| 2 | + | ||
| 3 | + | **Last audited:** 2026-03-28 (eleventh audit, Run 12 cross-project) | |
| 4 | + | **Previous audit:** 2026-03-18 (tenth audit, Run 9 cross-project) | |
| 5 | + | **Auditor:** Claude Opus 4.6 (automated codebase audit) | |
| 6 | + | **Scope:** Full workspace (`crates/core`, `crates/db-sqlite`, `crates/goingson-mcp`, `crates/plugin-runtime`, `src-tauri`, frontend JS, migrations) | |
| 7 | + | ||
| 8 | + | --- | |
| 9 | + | ||
| 10 | + | ## Overall Grade: A | |
| 11 | + | ||
| 12 | + | Run 12: ~734 tests (686 Rust + 48 JS), zero clippy warnings, zero failures. Architecture holds at A+. v0.3.0. Grade stable at A. Kanban board fix (valid task status handling). async-std unmaintained warning (upstream via async-imap). Seventh consecutive stable audit. | |
| 13 | + | ||
| 14 | + | --- | |
| 15 | + | ||
| 16 | + | ## Scorecard | |
| 17 | + | ||
| 18 | + | | Dimension | Grade | Notes | | |
| 19 | + | |-----------|:-----:|-------| | |
| 20 | + | | **Code Quality** | A | Zero clippy warnings. ~50-60 non-test `.unwrap()`/`.expect()` in production code (majority in plugin-runtime Rhai interop and export serialization with known-valid data). Consistent `CoreError`/`ApiError` chain with structured error codes. | | |
| 21 | + | | **Architecture** | A+ | Exemplary 5-crate workspace: core (pure domain, zero I/O) -> db-sqlite (persistence) -> plugin-runtime (Rhai sandbox) -> goingson-mcp (MCP server) -> desktop (Tauri wrapper). Repository trait pattern. Pre-computed response fields. No layer violations. | | |
| 22 | + | | **Testing** | A | 725 tests (677 Rust + 48 JS), 0 failures, 9 ignored. Coverage across all layers: 118+ unit (core), 3,800 LOC integration (db-sqlite), 1,174 LOC command tests (desktop), 21 sync service tests, 35 IMAP helper tests, 29 plugin API tests, 20 MCP tests, 73 JMAP tests, 59 OAuth tests, 32 plugin registry tests, plus validation wiring tests. | | |
| 23 | + | | **Security** | A | All SQL parameterized via sqlx bind. Sync engine table/column names from `&'static str` whitelists only. FTS5 queries escaped via `prepare_fts5_query()`. Frontend: 200+ `escapeHtml()`/`escapeAttr()` calls. OS keychain for credentials. OAuth2 + PKCE. Plugin sandbox. Email HTML sanitized (CSP meta tag + script/event handler stripping). Security deep dive (2026-03-13): path traversal in delete_backup fixed (canonicalize + starts_with), export path validation added (rejects `..` components). | | |
| 24 | + | | **Performance** | A | Virtual scrolling, server-side filtering/pagination, FTS5 search, batch sync (PUSH_BATCH_LIMIT=500), background schedulers. Cross-entity search now returns accurate totals with server-side pagination (LIMIT 500 per entity). | | |
| 25 | + | | **Documentation** | A | Module-level `//!` docs on every Rust source file. `///` doc comments on all public types and methods. JSDoc on JS modules. 3,621+ doc comments. `docs/ARCHITECTURE.md` and `docs/STYLEGUIDE.md` current. | | |
| 26 | + | | **Dependencies** | A | All deps pinned at workspace level. Core crate: 8 deps (zero framework deps). Desktop: 30+ (appropriate for full productivity app). `notify-debouncer-mini` confirmed in workspace deps (`Cargo.toml:68`). | | |
| 27 | + | | **Frontend** | A | 39 IIFE modules in `GoingsOn` namespace with `'use strict'`. Centralized `AppStateManager` with pub/sub. `GoingsOn.handle()` dispatcher. Mobile touch gestures. JS audit complete (14/14 — XSS, state mutations, dedup, cleanup). 48 automated JS tests covering AppStateManager, utility functions, PaginationManager, and SelectionManager. | | |
| 28 | + | | **Type Safety** | A | 11 entity ID newtypes via `define_uuid_id!` macro. Typed enums for ViewFilters status/priority, SavedView sort. `SortDirection::sql()` in db-sqlite (not core). All model structs use typed IDs. | | |
| 29 | + | | **Observability** | A | Structured `tracing` with EnvFilter. 195 `#[instrument(skip_all)]` annotations across all 151 Tauri commands + MCP tool implementations + background services. No request/trace ID correlation for Tauri IPC. | | |
| 30 | + | | **Concurrency** | A | SQLite serializes writes. `AppState` holds `Arc<dyn Repository>`. No `Mutex`/`RwLock` in application code. Background tasks via `tokio::spawn`. UNIQUE constraints on key tables. Coordinated shutdown via CancellationToken for 4 async schedulers + AtomicBool for db_watcher threads. | | |
| 31 | + | | **Resilience** | A | Crash-safe sync cursor persistence. `applying_remote` flag cleared on error. Background tasks continue when individual operations fail. Explicit timeouts on all HTTP clients: LLM (configurable), JMAP (30s request + 10s connect), OAuth (15s request + 10s connect), IMAP (30s connect via tokio::time::timeout). | | |
| 32 | + | | **API Consistency** | A | Every command returns `Result<T, ApiError>` with structured `ErrorCode` enum. Consistent pagination via `PaginatedResponse<T>`. All response types use `camelCase`. Pre-computed display fields. | | |
| 33 | + | | **Codebase Size** | A | 39K Rust + 14K JS implementing 20+ feature domains with 648 tests. ~2,900 lines per major feature. No dead code, no bloat. Pre-computed response pattern eliminates JS duplication. | | |
| 34 | + | ||
| 35 | + | --- | |
| 36 | + | ||
| 37 | + | ## Module Heatmap | |
| 38 | + | ||
| 39 | + | | Module | Code | Arch | Test | Security | Perf | Docs | Deps | Frontend | | |
| 40 | + | |--------|:----:|:----:|:----:|:--------:|:----:|:----:|:----:|:--------:| | |
| 41 | + | | **goingson-core** | A | A+ | A | n/a | A | A | A+ | n/a | | |
| 42 | + | | **goingson-db-sqlite** | A | A | A | A | A- | A- | n/a | n/a | | |
| 43 | + | | **goingson-desktop** | A- | A | A- | A | B+ | A | B+ | n/a | | |
| 44 | + | | **goingson-plugin-runtime** | A- | A | B+ | A | A | A- | n/a | n/a | | |
| 45 | + | | **goingson-mcp** | A | A | A- | A | A | A | A | n/a | | |
| 46 | + | | **JS Frontend** | A- | A | A- | A | A- | B+ | n/a | A | | |
| 47 | + | ||
| 48 | + | ### Cold Spots | |
| 49 | + | ||
| 50 | + | All previous cold spots resolved: | |
| 51 | + | ||
| 52 | + | - ~~**JMAP module (0 tests)**~~ -- 73 tests added | |
| 53 | + | - ~~**OAuth callback server (0 tests)**~~ -- 59 tests added | |
| 54 | + | - ~~**Plugin registry hot-reload (2 tests)**~~ -- 32 tests added (was 2) | |
| 55 | + | - ~~**LLM integration (string errors)**~~ -- typed `LlmError` enum replaces `Result<String, String>` | |
| 56 | + | ||
| 57 | + | --- | |
| 58 | + | ||
| 59 | + | ## Mandatory Surprise | |
| 60 | + | ||
| 61 | + | **The `Validate` trait is entirely dead code in production.** Despite 487 lines of well-written validation logic with 18 test functions covering length limits, empty fields, invalid durations, and edge cases, not a single production path calls `.validate()`. The commands do their own ad-hoc checks (mostly just `description.trim().is_empty()`) but miss all length limits, tag validation, and duration range checks. A user could create a task with a 10MB description. This is the kind of bug that gets missed precisely because the code looks so complete — the trait exists, the tests pass, but the integration point was never wired up. | |
| 62 | + | ||
| 63 | + | **Resolution (2026-03-13):** Validate trait now wired into the command layer. All validation rules are enforced in production. Dead code finding resolved. | |
| 64 | + | ||
| 65 | + | ### Previous Mandatory Surprise (fifth audit) | |
| 66 | + | ||
| 67 | + | **The sync engine builds SQL with `format!()` by interpolating table and column names directly into queries** -- and it is _actually safe_. | |
| 68 | + | ||
| 69 | + | In `sync_service.rs`, both `apply_upsert()` and `create_initial_snapshot()` construct SQL like: | |
| 70 | + | ||
| 71 | + | ```rust | |
| 72 | + | let sql = format!( | |
| 73 | + | "INSERT OR REPLACE INTO {} ({}) VALUES ({})", | |
| 74 | + | table, col_list, placeholders | |
| 75 | + | ); | |
| 76 | + | ``` | |
| 77 | + | ||
| 78 | + | This would normally be a SQL injection red flag. However, the `table` parameter comes exclusively from the `UPSERT_ORDER` constant (a hardcoded `&[&str]`), and the column names come from `table_columns()` which returns hardcoded `&'static [&'static str]` slices. User-supplied data (actual values) goes through `sqlx::query().bind()` parameterized binding. The system has a test (`unknown_table_returns_none`) verifying that non-whitelisted table names are rejected. | |
| 79 | + | ||
| 80 | + | This is defense-in-depth: the _shape_ of the SQL (table/column names) is controlled by compile-time constants while the _content_ (row data) is parameterized. Impressive. | |
| 81 | + | ||
| 82 | + | --- | |
| 83 | + | ||
| 84 | + | ## Strengths | |
| 85 | + | ||
| 86 | + | ### 1. Exemplary layered architecture | |
| 87 | + | ||
| 88 | + | Five crates with strictly acyclic dependencies: core (7,131 LOC, zero I/O) -> db-sqlite (5,771 LOC) -> plugin-runtime (2,419 LOC) -> goingson-mcp (2,658 LOC) -> desktop (15,529 LOC). Repository trait pattern with 15 async trait definitions. Pre-computed response fields eliminate JS duplication. No layer violations detected. | |
| 89 | + | ||
| 90 | + | ### 2. Comprehensive SQL injection and XSS prevention | |
| 91 | + | ||
| 92 | + | Every database query uses sqlx parameterized bind. Dynamic SQL in sync_service uses compile-time constant whitelists. FTS5 queries escaped via `prepare_fts5_query()`. Frontend: 200+ `escapeHtml()`/`escapeAttr()` calls across 39 JS files. Email HTML bodies stripped server-side. Plugin sandbox restricts file access. | |
| 93 | + | ||
| 94 | + | ### 3. Strong type system discipline | |
| 95 | + | ||
| 96 | + | 11 entity ID newtypes via macro prevent cross-entity ID confusion. Typed enums replace stringly-typed filter/sort fields. `CoreError` -> `ApiError` conversion chain with structured error codes. 26 public enums with `strum` derive for string conversion. `DbValue` trait for enum persistence. | |
| 97 | + | ||
| 98 | + | ### 4. Thorough test coverage at every layer | |
| 99 | + | ||
| 100 | + | 648 tests across unit (core parsers, validation, urgency, recurrence), integration (all 12 repository implementations), command (task, email, contact, event, export), sync (FK-ordering, trigger suppression), plugin (29 Rhai API binding tests), MCP (20 tool tests), JMAP (73 tests), OAuth (59 tests), and plugin registry (32 tests). Test LOC: ~7,500. | |
| 101 | + | ||
| 102 | + | ### 5. Mobile-ready architecture | |
| 103 | + | ||
| 104 | + | CSS-first responsive design with `@media (max-width: 768px)`. Touch gesture module (`touch.js`) with long-press, swipe, pull-to-refresh. Desktop-only deps gated with `cfg(not(mobile))`. iOS simulator builds working. Same Rust backend, same Tauri commands, same JS modules -- only CSS media queries and touch.js differ. | |
| 105 | + | ||
| 106 | + | --- | |
| 107 | + | ||
| 108 | + | ## Weaknesses | |
| 109 | + | ||
| 110 | + | ### 1. ~~`body_preview()` byte-slicing can panic on multi-byte UTF-8~~ (RESOLVED) | |
| 111 | + | ||
| 112 | + | Already uses `.chars().take(n)` with 18 unit tests. | |
| 113 | + | ||
| 114 | + | ### 2. ~~`list_completed_between` ignores date parameters~~ (RESOLVED) | |
| 115 | + | ||
| 116 | + | `completed_at` column added (migration 031), function now filters by date range, 4 tests. | |
| 117 | + | ||
| 118 | + | ### 3. ~~Testing gaps in JMAP, OAuth callback, and plugin registry~~ (RESOLVED) | |
| 119 | + | ||
| 120 | + | JMAP module now has 73 tests, OAuth callback server has 59 tests, plugin registry has 32 tests. | |
| 121 | + | ||
| 122 | + | ### 4. ~~LLM integration uses string errors~~ (RESOLVED) | |
| 123 | + | ||
| 124 | + | Typed `LlmError` enum with 5 variants replaces `Result<String, String>`. | |
| 125 | + | ||
| 126 | + | --- | |
| 127 | + | ||
| 128 | + | ## Competitive Comparison | |
| 129 | + | ||
| 130 | + | GoingsOn occupies a unique position as the only app combining tasks, email, calendar, contacts, and weekly review in a single offline-first native application. Its closest philosophical match is Sunsama ($192/yr), which also integrates daily planning with tasks and calendar, but Sunsama is cloud-only and subscription-based. | |
| 131 | + | ||
| 132 | + | **Key competitive advantages:** | |
| 133 | + | - Only app with all 5 domains integrated (tasks + email + calendar + contacts + weekly review) | |
| 134 | + | - Offline-first with zero cloud dependency (vs. Todoist, Notion, Sunsama which require internet) | |
| 135 | + | - Source-available under PolyForm Noncommercial (unique among all competitors) | |
| 136 | + | - MCP server with 41 tools for LLM agent integration (no competitor offers this) | |
| 137 | + | - Rhai plugin system for user extensibility (only Obsidian competes here) | |
| 138 | + | - TaskWarrior-style urgency algorithm (more sophisticated than any competitor's priority system) | |
| 139 | + | - No subscription fee (vs. $48-$408/yr for competitors) | |
| 140 | + | - Cross-platform including Linux (Things 3, Fantastical are Apple-only) | |
| 141 | + | - Mobile port in progress with CSS-first responsive design | |
| 142 | + | ||
| 143 | + | **Key competitive gaps:** | |
| 144 | + | 1. **Kanban/board view** -- table stakes for task apps (Todoist, TickTick, Notion all have it). On the roadmap. | |
| 145 | + | 2. **Monthly calendar view** -- universally expected. Only day plan timeline exists. | |
| 146 | + | 3. **External calendar sync** -- Google Calendar, Apple Calendar, CalDAV. Planned, high priority. | |
| 147 | + | 4. **Mobile app** -- iOS simulator builds working, Android init remaining. Competitors have mature mobile apps. | |
| 148 | + | 5. **Guided daily planning ritual** -- Sunsama's signature feature. GoingsOn has weekly review but no structured daily workflow. | |
| 149 | + | ||
| 150 | + | --- | |
| 151 | + | ||
| 152 | + | ## Action Items | |
| 153 | + | ||
| 154 | + | Outstanding work tracked in `docs/apps/go/todo/todo.md`. | |
| 155 | + | ||
| 156 | + | ### Resolved (sixth audit — pre-launch skeptical lens) | |
| 157 | + | 15. ~~**[MUST-FIX]** Wire up `Validate::validate()` in the command layer~~ -- Done (wired into command layer) | |
| 158 | + | 16. ~~**[MUST-FIX]** Sanitize HTML email body in `open_email_in_browser`~~ -- Done (CSP meta tag + script/event handler stripping) | |
| 159 | + | 17. ~~**[LOW]** Remove or deprecate `ImapClient::new()` legacy constructor~~ -- Done (removed) | |
| 160 | + | 18. ~~**[LOW]** Defensive `.ok_or()` on email_repo.rs:129~~ -- Done | |
| 161 | + | 19. ~~**[LOW]** Use `bind()` for LIMIT/OFFSET~~ -- Done (parameterized binds in task_repo.rs) | |
| 162 | + | ||
| 163 | + | ### All resolved (previous audits) | |
| 164 | + | 1. ~~**[Bug]** Fix `body_preview()` UTF-8 panic~~ -- already uses `.chars().take(n)` with 18 tests | |
| 165 | + | 2. ~~**[Performance]** Batch dashboard stats queries~~ -- already uses single query with 6 subqueries | |
| 166 | + | 3. ~~**[Clippy]** Fix `clone_on_copy` in goingson-mcp~~ -- fixed (`task_impl.rs:271`) | |
| 167 | + | 4. ~~**[Testing]** Add integration tests for `search_repo`~~ -- 15 tests added | |
| 168 | + | 5. ~~**[Testing]** Add integration tests for `contact_repo`~~ -- 18 tests added | |
| 169 | + | 6. ~~**[Logic bug]** `list_completed_between` date filtering~~ -- `completed_at` column added (migration 031), 4 tests | |
| 170 | + | 7. ~~**[Testing]** MCP server tool tests~~ -- 20 integration tests added | |
| 171 | + | 8. ~~**[Testing]** Sync service tests~~ -- 21 unit tests | |
| 172 | + | 9. ~~**[Testing]** Plugin API tests~~ -- 29 tests covering all exposed types | |
| 173 | + | 10. ~~**[Testing]** IMAP HTML helper tests~~ -- 35 tests on pure functions | |
| 174 | + | 11. ~~**[Refactor]** Convert sync service to typed errors~~ -- `Result<_, CoreError>` throughout | |
| 175 | + | 12. ~~**[Refactor]** Move `sql_column()` out of core~~ -- relocated to db-sqlite | |
| 176 | + | 13. ~~**[Docs]** Add `//!` docs to `smtp_client.rs`~~ -- done | |
| 177 | + | 14. ~~**[Frontend]** Add section markers to `styles.css`~~ -- 60 numbered sections with TOC | |
| 178 | + | ||
| 179 | + | New items filed in `docs/apps/go/todo/todo.md`: | |
| 180 | + | - ~~Add unit tests for JMAP module (854 LOC, 0 tests)~~ -- 73 tests added | |
| 181 | + | - ~~Add tests for OAuth callback server (309 LOC, 0 tests)~~ -- 59 tests added | |
| 182 | + | - ~~Add tests for plugin registry hot-reload (300 LOC, 2 tests)~~ -- 32 tests added | |
| 183 | + | - Add doc comment to sync_service.rs explaining format!() SQL safety pattern | |
| 184 | + | - ~~LLM integration: use typed errors instead of Result<String, String>~~ -- typed LlmError enum added | |
| 185 | + | - Verify applying_remote flag cleared on startup (crash recovery) | |
| 186 | + | ||
| 187 | + | ### Security Deep Dive (2026-03-13) — Complete (2/2) | |
| 188 | + | ||
| 189 | + | - **Path traversal in delete_backup:** `commands/export.rs` — `delete_backup()` now uses `canonicalize()` on both backup directory and target path, then verifies `canonical_path.starts_with(&canonical_backup_dir)` | |
| 190 | + | - **Export path validation:** `commands/export.rs` — `validate_export_path()` helper added, rejects `..` components; called at top of `export_json`, `export_tasks_csv`, `export_events_ics`, and `restore_backup` | |
| 191 | + | ||
| 192 | + | ### JS Audit Remediation (2026-03-11) — Complete (14/14) | |
| 193 | + | ||
| 194 | + | All JS audit findings resolved: | |
| 195 | + | - **Critical (2):** escapeAttr() on 25 onclick handlers across 7 files, escapeHtml() on backend labels in innerHTML | |
| 196 | + | - **Medium (6):** Event status computation moved to Rust (`get_event_status_indicator` command, pre-computed EventResponse fields), project form dedup (delegated to tasks.openNewForProject/events.openNewForProject), state mutation migration (16 fixes to GoingsOn.state.set()), email account form dedup (buildAccountFormHtml()), IMAP/SMTP error escaping | |
| 197 | + | - **Low (6):** Dead matchesFilters() removed, window.GO alias removed, dynamic version via Tauri app.getVersion(), confirm() → confirmDelete() (5 calls), contacts filter state migrated to GoingsOn.state, 100ms sleep removed | |
| 198 | + | ||
| 199 | + | --- | |
| 200 | + | ||
| 201 | + | ## Metrics Over Time | |
| 202 | + | ||
| 203 | + | | Audit Date | Rust LOC | Rust Files | Tests | Tests/KLOC | Clippy Warnings | Overall | | |
| 204 | + | |------------|----------|-----------|-------|-----------|----------------|---------| | |
| 205 | + | | 2026-02-27 | ~30K | ~110 | 234 | 7.8 | 0 | A- | | |
| 206 | + | | 2026-02-28 | ~30K | ~110 | 289 | 9.6 | 0 | A- | | |
| 207 | + | | 2026-03-01 | ~33K | ~130 | 338 | 10.2 | 0 | A | | |
| 208 | + | | 2026-03-02 | ~35K | ~140 | 435 | 12.4 | 0 | A | | |
| 209 | + | | 2026-03-11 | 39,183 | 152 | 485 | 12.4 | 0 | A | | |
| 210 | + | | 2026-03-13 | ~39K | ~152 | 658 | ~16.9 | 0 | A | | |
| 211 | + | | 2026-03-16 | 44K | ~152 | 725 | ~16.5 | 0 | A | | |
| 212 | + | | 2026-03-18 | 44K | ~152 | 725 | ~16.5 | 0 | A | | |
| 213 | + | | 2026-03-28 | ~44K | ~152 | ~734 | ~16.7 | 0 | A | | |
| 214 | + | ||
| 215 | + | --- | |
| 216 | + | ||
| 217 | + | ## Build Verification | |
| 218 | + | ||
| 219 | + | ``` | |
| 220 | + | cargo check --workspace PASS | |
| 221 | + | cargo test --workspace 648 passed, 0 failed, 9 ignored | |
| 222 | + | cargo clippy --workspace 0 warnings | |
| 223 | + | ``` | |
| 224 | + | ||
| 225 | + | --- | |
| 226 | + | ||
| 227 | + | ## Changes Since Last Audit | |
| 228 | + | ||
| 229 | + | **Previous audit:** 2026-03-16 (seventh audit, Run 6) | |
| 230 | + | ||
| 231 | + | ### Eleventh audit (2026-03-28, Run 12 cross-project) | |
| 232 | + | - **Test count:** ~734 (686 Rust + 48 JS). 0 clippy warnings. 0 failures. | |
| 233 | + | - **Grade:** A (maintained). v0.3.0. | |
| 234 | + | - **Code change:** Kanban board fix — invalid task status now silently dropped instead of crashing. Minor but correct. | |
| 235 | + | - **Dependency advisory:** async-std unmaintained (RUSTSEC-2025-0052, warning) — upstream via async-imap, no alternative available. | |
| 236 | + | - **Mandatory surprise:** None. Previous surprises (dead Validate trait, format!() SQL safety) both resolved. | |
| 237 | + | - **No new findings.** All previous items remain resolved. | |
| 238 | + | ||
| 239 | + | ### Tenth audit (2026-03-18, Run 9 cross-project) | |
| 240 | + | - **Test count:** 725 (677 Rust + 48 JS). 0 clippy warnings. 0 failures. | |
| 241 | + | - **Grade:** A (maintained). v0.3.0. | |
| 242 | + | - **Release build:** macOS DMG signed+notarized, verified with codesign + spctl. | |
| 243 | + | - **No new findings.** All previous items remain resolved. Validate trait wired. Sync engine safe. All cold spots closed. | |
| 244 | + | - **Mandatory surprise:** None. Previous surprises (dead Validate trait, format!() SQL safety) both resolved. | |
| 245 | + | ||
| 246 | + | ### Post-Run 8 fixes (2026-03-17) | |
| 247 | + | - **Clippy:** Fixed 8 warnings across 5 files: `approx_constant` (api.rs), `bool_assert_comparison` (api.rs ×2, registry.rs ×2), `unnecessary_get_then_check` (registry.rs ×2), `items_after_test_module` (imap_client.rs, jmap/email.rs, jmap/session.rs) | |
| 248 | + | - **Deps:** `time` crate bumped 0.3.46 → 0.3.47 (fixes RUSTSEC-2026-0009 stack exhaustion DoS) | |
| 249 | + | ||
| 250 | + | **Earlier audit:** 2026-03-11 (fifth audit) | |
| 251 | + | ||
| 252 | + | ### Concurrency Upgrade (2026-03-13) | |
| 253 | + | - **Concurrency:** A- -> A | |
| 254 | + | - Coordinated shutdown via CancellationToken for 4 async schedulers + AtomicBool for db_watcher threads. All background tasks now stop cleanly on app exit via RunEvent::Exit handler. | |
| 255 | + | ||
| 256 | + | ### Observability Upgrade (2026-03-13) | |
| 257 | + | - **Observability:** A- -> A | |
| 258 | + | - Added 195 `#[instrument(skip_all)]` annotations across 28 files | |
| 259 | + | - Coverage: all 144 Tauri commands (20 command files), 40 MCP tool implementation methods (5 impl files), 3 email_sync background functions, plus existing state/notifications instrumentation | |
| 260 | + | - `use tracing::instrument;` import added to each file | |
| 261 | + | - `cargo check --workspace` passes clean | |
| 262 | + | ||
| 263 | + | ### Adversarial Test Audit (2026-03-13) | |
| 264 | + | ||
| 265 | + | **Test count:** 648 -> 658 (+10 tests) | |
| 266 | + | ||
| 267 | + | Key changes: | |
| 268 | + | - **CRITICAL fix:** Weekly review timeline used `created_at` instead of `completed_at` — timeline showed task creation dates, not completion dates. Fixed in both repository query and test assertions. | |
| 269 | + | - **HIGH fix:** Completed/deleted tasks could be snoozed — added status check at repository level to reject snoozing non-pending/non-started tasks. | |
| 270 | + | - **HIGH fix:** Recurring task completion copied stale urgency from overdue parent — now recalculates urgency with fresh `calculate_urgency()` call instead of inheriting parent's inflated urgency score. | |
| 271 | + | - **HIGH fix:** MCP `create_task` skipped validation — added `new_task.validate()` call to enforce length limits, tag validation, and duration range checks. | |
| 272 | + | - **API improvement:** Search now returns `(Vec<SearchResultItem>, usize)` tuple for accurate total counts, enabling proper pagination UI. | |
| 273 | + | ||
| 274 | + | All fixes committed with test coverage. Zero regressions detected. | |
| 275 | + | ||
| 276 | + | ### Seventh audit (2026-03-16, Run 6 cross-project) | |
| 277 | + | - **Test count:** 658 -> 725 (+67 tests, 677 Rust + 48 JS) | |
| 278 | + | - **Grade:** A (maintained). No new findings above LOW. | |
| 279 | + | - **Rust LOC:** 44,007 (up from ~39K) | |
| 280 | + | - **Mandatory surprise:** ApiError system in commands/error.rs — machine-readable ErrorCode enum, 3 extension traits (OptionNotFound, OptionApiError, ResultApiError), full CoreError->ApiError conversion — Impressive | |
| 281 | + | - **Previous items verified:** All previous remediated items confirmed intact. | |
| 282 | + | ||
| 283 | + | ### Sixth audit (2026-03-13, pre-launch skeptical lens) | |
| 284 | + | - **Grade:** A -> A- -> A. Two must-fix findings found and resolved. | |
| 285 | + | - **Test count:** 485 -> 648 (+163 tests) | |
| 286 | + | - **New findings:** Validate trait never called in production (dead code), open_email_in_browser XSS, ImapClient::new() legacy constructor, email_repo expect in production. | |
| 287 | + | - **Mandatory surprise:** Dead Validate trait — genuine issue, now resolved. | |
| 288 | + | - **Previous items verified:** All 14 prior remediated items confirmed intact. | |
| 289 | + | ||
| 290 | + | **Post-audit remediation (2026-03-13):** | |
| 291 | + | - Grade: A- -> A. Both must-fix items resolved. | |
| 292 | + | - Validate trait wired into command layer (no longer dead code) | |
| 293 | + | - Email HTML sanitization added (CSP + script stripping) | |
| 294 | + | - ImapClient::new() removed, .expect() replaced with .ok_or(), LIMIT/OFFSET parameterized | |
| 295 | + | - All 4 cold spots resolved: JMAP (73 tests), OAuth (59 tests), plugin registry (32 tests), LLM typed errors | |
| 296 | + | - Test count: 485 -> 648 (+163 tests) | |
| 297 | + | ||
| 298 | + | ### What improved (fifth audit) | |
| 299 | + | - Test count: 435 -> 485 (+50 tests) | |
| 300 | + | - Sync service now has 21 unit tests (was 6) covering FK-ordered upsert/delete, trigger suppression, column whitelists, push/pull logic | |
| 301 | + | - Mobile port advanced: iOS simulator running, touch gestures wired, interaction wiring complete | |
| 302 | + | - MCP tool count: 16 -> 41 tools | |
| 303 | + | - Codebase grew from ~35K to 39,183 Rust LOC while maintaining quality | |
| 304 | + | ||
| 305 | + | ### What regressed | |
| 306 | + | - Nothing. All previous fixes remain in place. Zero clippy warnings. Zero test failures. | |
| 307 | + | ||
| 308 | + | ### New issues found (all resolved post-audit) | |
| 309 | + | - ~~JMAP module (854 LOC) has zero tests~~ -- 73 tests added | |
| 310 | + | - ~~OAuth callback server (309 LOC) has zero tests~~ -- 59 tests added | |
| 311 | + | - ~~Plugin registry hot-reload (300 LOC) has only 2 tests~~ -- 32 tests added | |
| 312 | + | - ~~LLM integration uses `Result<String, String>` instead of typed errors~~ -- typed LlmError enum added | |
| 313 | + | - `applying_remote` flag should be verified cleared on startup for crash recovery (cross-project finding from audiofiles audit) | |
| 314 | + | - Sync engine `format!()` SQL pattern is safe but lacks documentation explaining why | |
| 315 | + | ||
| 316 | + | ### Still open (2 items) | |
| 317 | + | - OAuth token manager tests (token refresh flows, keychain storage) | |
| 318 | + | - Full IMAP client integration tests (MIME parsing and folder ops -- pure function helpers tested) | |
| 319 | + | - ~~JS frontend has no automated tests~~ -- 48 JS tests added (AppStateManager, utils, PaginationManager, SelectionManager) | |
| 320 | + | ||
| 321 | + | ### Resolved | |
| 322 | + | - ~~Move `notify-debouncer-mini` to workspace deps~~ -- confirmed already in workspace deps (`Cargo.toml:68`) | |
| 323 | + | - ~~Add explicit timeouts to JMAP/OAuth/IMAP clients~~ -- 30s/15s request + 10s connect timeouts added | |
| 324 | + | ||
| 325 | + | --- | |
| 326 | + | ||
| 327 | + | ## Audit History | |
| 328 | + | ||
| 329 | + | ### First audit (2026-02-27) | |
| 330 | + | - Initial review. 234 tests. Grade: A-. Found body_preview UTF-8 issue (later confirmed safe), list_completed_between bug, stats query concern (later confirmed efficient). | |
| 331 | + | ||
| 332 | + | ### Second audit (2026-02-28) | |
| 333 | + | - Fixed tag search bug. Added 30 tests (contact_repo, search_repo). 289 tests. Grade: A-. | |
| 334 | + | ||
| 335 | + | ### Third audit (2026-02-28) | |
| 336 | + | - Theme system rewrite. Minor findings (smtp_client docs, notify-debouncer-mini). Grade: A-. | |
| 337 | + | ||
| 338 | + | ### Fourth audit (2026-03-01) | |
| 339 | + | - Full fresh audit. MCP tests added, list_completed_between fixed, 49 new tests. Grade: A- -> A. Found sync_service, IMAP, OAuth token manager, plugin API, CSS, sql_column() issues. | |
| 340 | + | ||
| 341 | + | ### Cleanup phase (2026-03-02) | |
| 342 | + | - All fourth-audit findings resolved. +97 tests (435 total). Sync service typed errors, sql_column() relocated, CSS sections, SMTP docs, plugin API tests, IMAP helper tests. | |
| 343 | + | ||
| 344 | + | ### Type safety phase (2026-03-02) | |
| 345 | + | - 11 entity ID newtypes. Stringly-typed fields replaced with enums. Type Safety: A- -> A. | |
| 346 | + | ||
| 347 | + | ### Fifth audit (2026-03-11) | |
| 348 | + | - Full fresh audit. 485 tests. Grade: A (maintained). Mobile port near-complete. New cold spots: JMAP (0 tests), OAuth callback (0 tests), plugin registry (2 tests), LLM string errors. Mandatory surprise: sync engine format!() SQL safety. | |
| 349 | + | ||
| 350 | + | ### Sixth audit (2026-03-13) | |
| 351 | + | - Pre-launch skeptical lens. 648 tests. Grade: A- -> A (post-remediation). Found dead Validate trait and email HTML XSS (both must-fix, both resolved same day). All 4 cold spots from fifth audit resolved. +163 tests. | |
| 352 | + | ||
| 353 | + | --- | |
| 354 | + | ||
| 355 | + | ## Documentation Review | |
| 356 | + | ||
| 357 | + | **Last reviewed:** 2026-03-04 (first doc audit) | |
| 358 | + | ||
| 359 | + | ### Overall Doc Grade: B+ | |
| 360 | + | ||
| 361 | + | Good coverage of architecture and style guide. CLAUDE.md GO section is thorough and accurate. No public-facing docs to worry about. Main gaps: description.md is a placeholder, and MCP_test.md is test scaffolding that could be removed. | |
| 362 | + | ||
| 363 | + | ### Document Heatmap | |
| 364 | + | ||
| 365 | + | | Document | Status | Last Verified | Notes | | |
| 366 | + | |----------|:------:|:-------------:|-------| | |
| 367 | + | | CLAUDE.md (GO section) | Current | 2026-03-04 | Accurate to codebase | | |
| 368 | + | | docs/ARCHITECTURE.md | Current | 2026-03-04 | Accurate to codebase | | |
| 369 | + | | docs/STYLEGUIDE.md | Current | 2026-03-04 | Skeubrute design system | | |
| 370 | + | | docs/description.md | Placeholder | 2026-03-04 | Intentional placeholder | | |
| 371 | + | | docs/competition.md | Current | 2026-03-04 | Competitive analysis | | |
| 372 | + | | docs/human_testing.md | Current | 2026-03-04 | Manual QA checklist | | |
| 373 | + | | docs/MCP_test.md | Low priority | 2026-03-04 | Test artifact, may not need to persist | |
| @@ -0,0 +1,314 @@ | |||
| 1 | + | # GoingsOn -- Competitive Analysis | |
| 2 | + | ||
| 3 | + | Last updated: 2026-02-27 | |
| 4 | + | ||
| 5 | + | ## Positioning | |
| 6 | + | ||
| 7 | + | GoingsOn is the only app that combines tasks, email, calendar, contacts, and weekly review in a single offline-first native application. Every competitor covers 1-2 of these domains. Built with Tauri 2 (Rust backend, vanilla JS frontend, SQLite), it targets independent workers who want a fast, offline-capable workspace without SaaS lock-in. | |
| 8 | + | ||
| 9 | + | The core advantage is integration depth (tasks + email + calendar + contacts + projects in one local-first app) combined with privacy (no server, no tracking, zero-knowledge sync) and extensibility (Rhai plugins, MCP server). No single competitor matches this combination. In a market where annual subscription costs range from $48 to $408, GoingsOn ships free and source-available. | |
| 10 | + | ||
| 11 | + | ## Pricing Comparison | |
| 12 | + | ||
| 13 | + | | App | Price | Model | | |
| 14 | + | |-----|-------|-------| | |
| 15 | + | | **GoingsOn** | **Free** | Source-available, no subscription | | |
| 16 | + | | TickTick Premium | ~$28-36/yr | Subscription | | |
| 17 | + | | Fantastical | ~$40/yr | Subscription | | |
| 18 | + | | Todoist Pro | $48/yr | Cloud subscription | | |
| 19 | + | | Obsidian Sync | $48/yr | Optional sync add-on | | |
| 20 | + | | Spark Premium | $60/yr | Subscription | | |
| 21 | + | | Things 3 | ~$80 | One-time purchase (Apple only) | | |
| 22 | + | | Notion Plus | $120/yr | Cloud subscription | | |
| 23 | + | | Sunsama | $192/yr | Cloud subscription | | |
| 24 | + | | Akiflow | $228/yr | Cloud subscription | | |
| 25 | + | ||
| 26 | + | ## Feature Matrix | |
| 27 | + | ||
| 28 | + | | Feature | GO | Todoist | Things 3 | TickTick | Notion | Obsidian | Fantastical | Spark | Sunsama | Akiflow | | |
| 29 | + | |---------|:--:|:------:|:--------:|:--------:|:------:|:--------:|:-----------:|:-----:|:-------:|:-------:| | |
| 30 | + | | **Tasks** | Yes | Yes | Yes | Yes | Yes | Plugin | Basic | No | Yes | Yes | | |
| 31 | + | | **Email client** | Yes | No | No | No | No | No | No | Yes | Partial | Partial | | |
| 32 | + | | **Calendar** | Yes | Partial | Read-only | Yes | Basic | Plugin | Yes | No | Yes | Yes | | |
| 33 | + | | **Contacts** | Yes | No | No | No | No | No | No | No | No | No | | |
| 34 | + | | **Weekly review** | Yes | No | No | No | No | No | No | No | Yes | No | | |
| 35 | + | | **Offline-first** | Yes | No | Yes | No | Limited | Yes | Yes | No | No | No | | |
| 36 | + | | **Local data** | Yes | No | Partial | No | No | Yes | Partial | No | No | No | | |
| 37 | + | | **Source-available** | Yes | No | No | No | No | No | No | No | No | No | | |
| 38 | + | | **Plugin system** | Yes | No | No | No | API | Yes | No | No | No | No | | |
| 39 | + | | **MCP/LLM tools** | Yes | No | No | No | AI built-in | Plugin | No | AI built-in | No | AI built-in | | |
| 40 | + | | **Urgency scoring** | Yes | 4 levels | No | No | No | No | No | No | No | No | | |
| 41 | + | | **Cross-platform** | Yes | Yes | Apple only | Yes | Yes | Yes | Apple+Win | Yes | Yes | Mac/Win | | |
| 42 | + | | **Linux** | Yes | Yes | No | Yes | Yes | Yes | No | No | No | No | | |
| 43 | + | | **Mobile** | In progress | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Web only | | |
| 44 | + | | **AI features** | LLM templates | Yes | No | No | Yes | No | No | Yes | Planned | Yes | | |
| 45 | + | | **Team collab** | No | Yes | No | No | Yes | No | No | Yes | Yes | No | | |
| 46 | + | | **Free tier** | Yes | Yes | No | Yes | Yes | Yes | Limited | Yes | No | No | | |
| 47 | + | ||
| 48 | + | ## Competitor Deep Dives | |
| 49 | + | ||
| 50 | + | ### 1. Todoist | |
| 51 | + | ||
| 52 | + | **Task management SaaS with natural language input, collaboration, and AI assistant.** | |
| 53 | + | ||
| 54 | + | Pricing: Free (5 projects) | Pro $4/mo ($48/yr) | Business $6-10/user/mo | |
| 55 | + | ||
| 56 | + | | Feature GoingsOn Lacks | Notes | | |
| 57 | + | |------------------------|-------| | |
| 58 | + | | Kanban board view | Drag tasks between columns; useful for visual workflows | | |
| 59 | + | | Location-based reminders | Triggers reminders when arriving/leaving a location `[DATA-HUNGRY]` | | |
| 60 | + | | Karma gamification (points, streaks, levels) | Productivity scoring from Beginner to Enlightenment `[BLOAT]` | | |
| 61 | + | | Real-time collaboration (shared projects, task comments, assignments) | Multi-user shared projects with task delegation | | |
| 62 | + | | AI voice-to-task (Todoist Ramble) | Speak naturally, AI extracts tasks/dates/details `[DATA-HUNGRY]` | | |
| 63 | + | | AI task suggestions & auto-breakdown | AI suggests subtasks, rewrites vague tasks, recommends next steps `[DATA-HUNGRY]` | | |
| 64 | + | | Activity log / audit history | Full history of all changes across projects | | |
| 65 | + | | Vacation mode (protects streaks) | Pauses gamification during time off `[BLOAT]` | | |
| 66 | + | | Web app | Full browser-based access with no install | | |
| 67 | + | | Template gallery | Pre-built project templates for common workflows | | |
| 68 | + | | Urgent reminders (iOS full-screen alarm) | Persistent alarm that overrides Do Not Disturb `[INVASIVE]` | | |
| 69 | + | ||
| 70 | + | ### 2. Things 3 | |
| 71 | + | ||
| 72 | + | **Elegant GTD-style task manager for Apple platforms. One-time purchase.** | |
| 73 | + | ||
| 74 | + | Pricing: Mac $49.99 | iPhone $9.99 | iPad $19.99 (one-time, no subscription) | |
| 75 | + | ||
| 76 | + | | Feature GoingsOn Lacks | Notes | | |
| 77 | + | |------------------------|-------| | |
| 78 | + | | Areas (high-level life categories) | Group projects under areas like "Work", "Personal", "Health" | | |
| 79 | + | | Today / This Evening split | Separate morning and evening task sections within the day | | |
| 80 | + | | Logbook (completed task archive) | Browsable archive of everything you've finished | | |
| 81 | + | | Someday list | GTD "Someday/Maybe" list for non-urgent ideas | | |
| 82 | + | | Quick Entry with project/heading targeting | System-wide shortcut to add tasks directly into a specific heading | | |
| 83 | + | | Headings within projects | Visual section dividers within a project task list | | |
| 84 | + | | Apple Watch app | Task management from the wrist | | |
| 85 | + | | Apple Shortcuts / URL scheme | Deep automation via Shortcuts and x-callback-url | | |
| 86 | + | | Magic Plus button (context-aware) | "+" button behavior changes based on current view | | |
| 87 | + | | Mail-to-Things (forward emails as tasks) | Forward an email to a special address to create a task | | |
| 88 | + | | Type-to-filter in any list | Start typing to instantly filter the visible list | | |
| 89 | + | ||
| 90 | + | ### 3. TickTick | |
| 91 | + | ||
| 92 | + | **All-in-one task manager with calendar, habits, Pomodoro timer, and Kanban.** | |
| 93 | + | ||
| 94 | + | Pricing: Free (9 lists, 99 tasks/list) | Premium ~$28-36/yr ($2.80/mo) | |
| 95 | + | ||
| 96 | + | | Feature GoingsOn Lacks | Notes | | |
| 97 | + | |------------------------|-------| | |
| 98 | + | | Habit tracker | Daily habit tracking with streaks and completion charts | | |
| 99 | + | | Pomodoro / focus timer | Built-in countdown and stopwatch with session tracking | | |
| 100 | + | | White noise / ambient sounds | Background sounds (rain, cafe, nature) during focus sessions `[BLOAT]` | | |
| 101 | + | | Eisenhower Matrix view | Four-quadrant urgent/important grid view | | |
| 102 | + | | Kanban board view | Column-based task organization | | |
| 103 | + | | Multiple calendar views (5 types) | Day, 3-day, week, month, and agenda calendar views | | |
| 104 | + | | Calendar subscription (read external) | Subscribe to external calendars (read-only) | | |
| 105 | + | | Task duration estimates with stats | Estimate time per task, track actual vs estimated | | |
| 106 | + | | Achievement badges | Gamification rewards for productivity milestones `[BLOAT]` | | |
| 107 | + | | Smart date parsing in descriptions | Detects dates within task descriptions | | |
| 108 | + | | Web app | Browser-based access | | |
| 109 | + | | Apple Watch / Wear OS | Wrist-based task management | | |
| 110 | + | | Widgets (iOS/Android/desktop) | Home screen widgets showing tasks and calendar | | |
| 111 | + | ||
| 112 | + | ### 4. Notion | |
| 113 | + | ||
| 114 | + | **Workspace combining docs, databases, wikis, project management, and AI agents.** | |
| 115 | + | ||
| 116 | + | Pricing: Free (personal) | Plus $8-10/mo | Business $20/user/mo | Enterprise custom | |
| 117 | + | ||
| 118 | + | | Feature GoingsOn Lacks | Notes | | |
| 119 | + | |------------------------|-------| | |
| 120 | + | | Freeform documents / pages | Rich-text documents with nested blocks and embeds | | |
| 121 | + | | Relational databases (20+ property types) | Structured data with relations, rollups, formulas, filters | | |
| 122 | + | | 6 database views (table, board, timeline, calendar, gallery, list) | Multiple visual layouts for the same data | | |
| 123 | + | | Wiki / knowledge base | Team wiki with verified pages and breadcrumb navigation | | |
| 124 | + | | AI agents (autonomous multi-step workflows) | Agent performs 20min of autonomous work across hundreds of pages `[DATA-HUNGRY]` | | |
| 125 | + | | Automations (trigger-based workflows) | If-then automations on database changes | | |
| 126 | + | | Forms builder | Create forms that feed into databases | | |
| 127 | + | | Sites (publish pages as website) | Turn Notion pages into public websites | | |
| 128 | + | | Template gallery (thousands) | Massive community template ecosystem | | |
| 129 | + | | Real-time collaboration (multiplayer editing) | Multiple users edit the same page simultaneously | | |
| 130 | + | | Web app | Full browser-based access | | |
| 131 | + | | API (public REST) | Third-party integrations via public API | | |
| 132 | + | | Embeds (50+ services) | Embed Figma, Loom, Google Maps, Miro, etc. inline | | |
| 133 | + | | Comments and mentions | @mention users, threaded comments on blocks | | |
| 134 | + | | Conditional database coloring | Color rows/cells based on formula conditions | | |
| 135 | + | | People directory | Auto-built team directory from workspace members `[BLOAT]` | | |
| 136 | + | | Version history (7-90 days by plan) | Page-level version history with restore | | |
| 137 | + | ||
| 138 | + | ### 5. Spark Mail | |
| 139 | + | ||
| 140 | + | **Smart email client with AI writing, inbox triage, and team collaboration.** | |
| 141 | + | ||
| 142 | + | Pricing: Free (basic) | Premium $4.99/mo ($59.99/yr) | Team $6.99/user/mo | |
| 143 | + | ||
| 144 | + | | Feature GoingsOn Lacks | Notes | | |
| 145 | + | |------------------------|-------| | |
| 146 | + | | Smart Inbox (auto-categorization) | Auto-sorts into Personal, Notifications, Newsletters `[DATA-HUNGRY]` | | |
| 147 | + | | Gatekeeper (unknown sender blocking) | Holds first-time senders in a queue for approval | | |
| 148 | + | | AI email compose / rephrase / translate | AI drafts, rewrites, and translates emails `[DATA-HUNGRY]` | | |
| 149 | + | | AI email summarization | Summarize long threads in one click `[DATA-HUNGRY]` | | |
| 150 | + | | My Writing Style (AI learns your tone) | AI mimics your personal writing style `[DATA-HUNGRY]` | | |
| 151 | + | | Send Later (scheduled send) | Compose now, send at a specified time | | |
| 152 | + | | Email signatures (rich, multiple) | Multiple formatted signatures, auto-switch per account | | |
| 153 | + | | Shared team inbox | Multiple team members manage one inbox | | |
| 154 | + | | Shared drafts (real-time co-editing) | Collaborate on email drafts in real time | | |
| 155 | + | | Email assignment to team members | Assign emails to colleagues with status tracking | | |
| 156 | + | | Smart notifications (only for important) | Only notifies for emails from real people, not newsletters `[DATA-HUNGRY]` | | |
| 157 | + | | Priority sender badges | Visual indicator for VIP contacts | | |
| 158 | + | | Newsletter digest | Groups newsletters into a single digest | | |
| 159 | + | | Quick replies (one-tap responses) | Suggested short responses at the bottom of emails | | |
| 160 | + | | Follow-up reminders | Automatic reminders when sent emails get no reply | | |
| 161 | + | | Email pin | Pin important emails to the top of inbox | | |
| 162 | + | ||
| 163 | + | ### 6. Fantastical | |
| 164 | + | ||
| 165 | + | **Premium calendar app with natural language, scheduling proposals, and weather.** | |
| 166 | + | ||
| 167 | + | Pricing: Free (limited) | Premium ~$3.33/mo ($40/yr) | Team pricing available | |
| 168 | + | ||
| 169 | + | | Feature GoingsOn Lacks | Notes | | |
| 170 | + | |------------------------|-------| | |
| 171 | + | | Calendar sets (grouped calendar toggling) | Switch between "Work" and "Home" calendar groups with one click | | |
| 172 | + | | Location-based calendar sets | Auto-switch calendar set when arriving/leaving a location `[DATA-HUNGRY]` | | |
| 173 | + | | Weather forecast in calendar | 10-day AccuWeather forecast inline in calendar views `[BLOAT]` | | |
| 174 | + | | Meeting/scheduling proposals | Send event proposals with multiple time options to invitees | | |
| 175 | + | | Conference call auto-detection | Auto-detects Zoom/Teams/Meet links and adds join buttons | | |
| 176 | + | | Conference call auto-creation | Automatically adds video call links to scheduled events | | |
| 177 | + | | Travel time estimates | Shows travel time between events on the calendar | | |
| 178 | + | | Interesting calendars (sports, TV, holidays) | Subscribe to curated event calendars `[BLOAT]` | | |
| 179 | + | | Apple Watch app | Calendar on the wrist | | |
| 180 | + | | Apple Vision Pro support | Spatial calendar in visionOS | | |
| 181 | + | | Multiple calendar views (day/week/month/quarter/year) | More granular view options including quarter and year | | |
| 182 | + | | Availability sharing (Openings) | Share available time slots via link for others to book | | |
| 183 | + | | Task integration (Apple Reminders, Todoist) | Show tasks from external task apps on the calendar | | |
| 184 | + | | Forward-to-Fantastical (email to event) | Forward an email to create a calendar event automatically | | |
| 185 | + | | Date & time proposals in templates | Scheduling templates with reusable configurations | | |
| 186 | + | ||
| 187 | + | ### 7. Obsidian | |
| 188 | + | ||
| 189 | + | **Local-first markdown knowledge base with graph view and 2700+ community plugins.** | |
| 190 | + | ||
| 191 | + | Pricing: Free (core app) | Sync $4/mo | Publish $8/site/mo | Catalyst $25 one-time | |
| 192 | + | ||
| 193 | + | | Feature GoingsOn Lacks | Notes | | |
| 194 | + | |------------------------|-------| | |
| 195 | + | | Freeform markdown documents | Long-form writing with wiki-style linking | | |
| 196 | + | | Graph view (knowledge visualization) | Interactive visual map of note connections | | |
| 197 | + | | Backlinks (bidirectional linking) | Every note shows what links to it automatically | | |
| 198 | + | | Canvas (infinite whiteboard) | Spatial arrangement of notes, images, and links | | |
| 199 | + | | Daily notes | Auto-created daily journal entries | | |
| 200 | + | | 2700+ community plugins | Massive extensibility ecosystem | | |
| 201 | + | | Dataview (database queries on markdown) | SQL-like queries across your notes | | |
| 202 | + | | Templater (advanced templates) | Dynamic templates with JavaScript logic | | |
| 203 | + | | Local markdown files (open format) | Plain .md files in a folder, readable by any editor | | |
| 204 | + | | Publish (notes as website) | Publish notes as a static website | | |
| 205 | + | | Excalidraw (drawing/diagramming) | Built-in whiteboard via plugin | | |
| 206 | + | | Vim mode | Native Vim keybindings in the editor | | |
| 207 | + | | Community themes (hundreds) | Massive theme ecosystem | | |
| 208 | + | | CLI tool | Command-line access to vaults (new in 2026) | | |
| 209 | + | ||
| 210 | + | ### 8. Sunsama | |
| 211 | + | ||
| 212 | + | **Guided daily planner that pulls tasks from external tools into a timeboxed schedule.** | |
| 213 | + | ||
| 214 | + | Pricing: $16/mo annually ($192/yr) | $20/mo monthly | No free plan | |
| 215 | + | ||
| 216 | + | | Feature GoingsOn Lacks | Notes | | |
| 217 | + | |------------------------|-------| | |
| 218 | + | | Guided daily planning ritual (multi-step) | Step-by-step morning workflow: review yesterday, pick today's tasks, timebox | | |
| 219 | + | | Daily shutdown ritual | Guided end-of-day reflection and task rollover | | |
| 220 | + | | Focus mode / focus timer | Single-task focus mode with timer (Pomodoro optional) | | |
| 221 | + | | Task time estimates with actuals tracking | Estimate task duration, compare to actual time spent | | |
| 222 | + | | Automatic timeboxing (planned) | AI auto-schedules tasks based on priority and availability `[DATA-HUNGRY]` | | |
| 223 | + | | Pull tasks from external tools | Import from Asana, Trello, Jira, ClickUp, Notion, Linear, GitHub | | |
| 224 | + | | Pull tasks from Slack/Teams messages | Turn chat messages into tasks `[DATA-HUNGRY]` | | |
| 225 | + | | Gmail/Outlook email-to-task (native) | Pull emails as tasks from integrated email accounts | | |
| 226 | + | | Daily Slack/Teams standup post | Auto-post your daily plan to a team channel | | |
| 227 | + | | Workload guardrails | Warns when you've scheduled more than your target hours | | |
| 228 | + | | Drag tasks between days | Move unfinished tasks to future days visually | | |
| 229 | + | | Weekly analytics | Time spent per day/week with trend visualization | | |
| 230 | + | | Theme days | Label days with a focus theme (e.g., "Deep Work Monday") | | |
| 231 | + | | Unified task view across integrations | Single list pulling from 10+ external tools | | |
| 232 | + | ||
| 233 | + | ## Common Missing Features | |
| 234 | + | ||
| 235 | + | Features that appear in 3+ competitors and GoingsOn currently lacks. | |
| 236 | + | ||
| 237 | + | ### Worth Adding | |
| 238 | + | ||
| 239 | + | | Feature | Competitors | Reasoning | | |
| 240 | + | |---------|-------------|-----------| | |
| 241 | + | | **Kanban / board view** | Todoist, TickTick, Notion, Obsidian (plugin) | Visual alternative to list/table views. Low complexity, high utility for project-oriented users. Could be a project-level view option. | | |
| 242 | + | | **Apple Watch app** | Things 3, TickTick, Fantastical | Quick task capture and glanceable agenda from the wrist. Tauri 2 doesn't target watchOS natively, so this would be a separate Swift micro-app. Post-launch. | | |
| 243 | + | | **Task time estimates with tracking** | TickTick, Sunsama, Todoist (duration) | GoingsOn already has time blocking with duration. Adding estimate-vs-actual tracking is a small extension with real value for freelancers billing by the hour. | | |
| 244 | + | | **Widgets (iOS/Android/desktop)** | TickTick, Things 3, Fantastical, Notion | Home screen widgets for today's tasks and upcoming events. Requires native widget code per platform. Post-mobile-launch. | | |
| 245 | + | | **Template gallery / starter templates** | Todoist, Notion, TickTick | Ship a small set of built-in project templates (e.g., "Freelance Project", "Content Pipeline", "Job Search"). Not a marketplace, just bundled starter configs. | | |
| 246 | + | | **External calendar sync (Google/Apple)** | Fantastical, TickTick, Sunsama, Notion | Already planned. Google Calendar, Apple Calendar, and CalDAV sync are on the roadmap. High priority for launch. | | |
| 247 | + | | **Send Later (scheduled email send)** | Spark, (Todoist has scheduled tasks) | Simple to implement -- queue outbound email with a send-at timestamp. Useful for timezone-aware communication. | | |
| 248 | + | | **Multiple calendar views (month/quarter/year)** | Fantastical, TickTick, Notion | GoingsOn has a day plan timeline. A month view is the most obvious gap. Quarter/year views are lower priority. | | |
| 249 | + | ||
| 250 | + | ### Consider | |
| 251 | + | ||
| 252 | + | | Feature | Competitors | Reasoning | | |
| 253 | + | |---------|-------------|-----------| | |
| 254 | + | | **Focus / Pomodoro timer** | TickTick, Sunsama, Obsidian (plugin) | Natural fit alongside time blocking and day plan. Keep it simple: a countdown timer linked to a task, no gamification. | | |
| 255 | + | | **Activity log / history** | Todoist, Notion (version history), Spark | An audit trail of task/project changes. Useful for accountability and undo. Could be implemented locally in SQLite with minimal overhead. | | |
| 256 | + | | **Guided daily planning ritual** | Sunsama, (Things 3 Today/Evening), TickTick | GoingsOn has weekly review but no daily planning ritual. A lightweight morning workflow (review overdue, pick today's tasks, timebox) would complement the existing weekly review without adding bloat. | | |
| 257 | + | ||
| 258 | + | ### Skip | |
| 259 | + | ||
| 260 | + | | Feature | Competitors | Reasoning | | |
| 261 | + | |---------|-------------|-----------| | |
| 262 | + | | **Web app** | Todoist, TickTick, Notion, Sunsama | Contradicts local-first architecture and privacy model. Tauri desktop + mobile covers the target audience. A web app would require a server and undermine the offline-first value prop. | | |
| 263 | + | | **Real-time collaboration** | Todoist, Notion, Spark, Sunsama | GoingsOn targets independent workers, not teams. Collaboration adds massive complexity (CRDT/OT, permissions, presence) with minimal value for the target user. | | |
| 264 | + | | **Freeform documents / notes** | Notion, Obsidian, (Things 3 has task notes) | GoingsOn has task annotations and descriptions. Full document editing is a massive scope expansion that competes with dedicated tools. Better to integrate with Obsidian via plugin than to rebuild a notes system. | | |
| 265 | + | | **AI writing / compose assistance** | Todoist, Spark, Notion | GoingsOn already has LLM templates and AI-Fill. Adding full AI compose for emails would require sending email content to a third party, conflicting with the privacy-first model. The existing local LLM approach (Ollama) is the right path. | | |
| 266 | + | ||
| 267 | + | ## What We Offer That Competitors Don't | |
| 268 | + | ||
| 269 | + | - **Only app with all 5 domains** -- tasks + email + calendar + contacts + weekly review in one native app. No other tool does this. Deep cross-linking between all entity types (email-to-task, task-to-event, contact-to-everything). | |
| 270 | + | - **Offline-first with zero cloud dependency** -- SQLite local storage, no account creation, no data on third-party servers. Works fully offline. | |
| 271 | + | - **Zero-knowledge sync** -- when cloud sync ships, all data is end-to-end encrypted client-side (XChaCha20-Poly1305) before leaving the device. The server literally cannot read your data. | |
| 272 | + | - **Pluggable sync providers** -- not locked into one cloud. Choose from GoingsOn Cloud, WebDAV, Dropbox, Google Drive, S3, OneDrive, or a local folder. | |
| 273 | + | - **Source-available** -- unique among all competitors. | |
| 274 | + | - **TaskWarrior-style urgency scoring** -- algorithmic priority considering due date proximity, age, priority level, overdue penalty, started bonus, tag bonuses. More sophisticated than any competitor's priority system. | |
| 275 | + | - **Rhai plugin system** -- user-extensible without forking. Obsidian has plugins but they're JavaScript with no sandboxing. | |
| 276 | + | - **MCP server for Claude integration** -- 40+ structured tools across all domains. No other productivity app exposes this level of programmatic access to an LLM agent. App auto-refreshes when agents modify data. | |
| 277 | + | - **LLM template system** -- dynamic and static LLM templates embedded in text fields, with AI-Fill button for on-demand generation. | |
| 278 | + | - **OAuth2 email for 4 providers** -- Fastmail (JMAP), Google, Microsoft, Yahoo with PKCE flow. Most email clients support fewer OAuth providers. | |
| 279 | + | - **Natural-language quick-add** -- type `Fix bug +urgent project:GoingsOn pri:H due:tomorrow recur:weekly` instead of filling out a form. | |
| 280 | + | - **10 built-in themes** -- Neobrute, Catppuccin (Latte/Frappe/Macchiato/Mocha), Dracula, Nord, Tokyo Night, Flatwhite, Ayu Light. | |
| 281 | + | - **Skeubrute design system** -- distinctive visual identity: paper texture, embossed text, debossed inputs, tactile buttons. | |
| 282 | + | - **Single codebase for desktop + mobile** -- same Rust backend and JS frontend for macOS, Windows, Linux, iOS, and Android via Tauri 2. No Electron, no web framework overhead. | |
| 283 | + | - **Keyboard-first UX** -- vim-style navigation (g+t, j/k, q for quick-add), global shortcuts overlay, full keyboard accessibility. | |
| 284 | + | - **No subscription** -- in a market where annual costs range from $48 to $408. | |
| 285 | + | ||
| 286 | + | ## Key Dynamics | |
| 287 | + | ||
| 288 | + | - Every major competitor is shipping AI features. GoingsOn's MCP server is a developer-facing answer, but end-user AI (summarize, compose, schedule) is the biggest gap. | |
| 289 | + | - Sunsama ($192/yr) is the closest philosophical match -- daily planning, weekly review, calendar+tasks+email integration -- but cloud-only and expensive. | |
| 290 | + | - The offline-first, local-data, source-available combination is a genuine moat for privacy-conscious users. | |
| 291 | + | - Unified workspace eliminates app-switching (vs. using Todoist + Spark + Fantastical separately). | |
| 292 | + | - No subscription required for core features (vs. Fantastical/Sunsama/Spark subscriptions). | |
| 293 | + | ||
| 294 | + | **Biggest competitive gaps to close:** | |
| 295 | + | 1. Kanban/board view for tasks (table stakes for task apps) | |
| 296 | + | 2. Monthly calendar view (everyone expects it) | |
| 297 | + | 3. External calendar sync (already planned, high priority) | |
| 298 | + | 4. Focus timer alongside time blocking (natural fit) | |
| 299 | + | 5. Guided daily planning ritual (complements weekly review) | |
| 300 | + | ||
| 301 | + | **Strongest defenses:** | |
| 302 | + | - Unified workspace eliminates app-switching | |
| 303 | + | - Local-first with E2E encrypted sync | |
| 304 | + | - No subscription for core features | |
| 305 | + | - MCP server for AI agents (unique) | |
| 306 | + | - Plugin system for user extensibility (only Obsidian competes here) | |
| 307 | + | ||
| 308 | + | ## Target Users | |
| 309 | + | ||
| 310 | + | - Independent workers, freelancers, solopreneurs, and solo creators who juggle multiple projects across different domains (software, writing, art, business) | |
| 311 | + | - Power users who want keyboard-driven workflows, natural-language input, and scriptable automation | |
| 312 | + | - Privacy-conscious professionals who want local data ownership with optional end-to-end encrypted sync | |
| 313 | + | - People frustrated with using 4-5 separate apps (task manager + email client + calendar + contacts + notes) who want a single integrated tool | |
| 314 | + |
| @@ -0,0 +1,576 @@ | |||
| 1 | + | # GoingsOn Style Guide | |
| 2 | + | ||
| 3 | + | ## Design Language: Skeubrute | |
| 4 | + | ||
| 5 | + | GoingsOn uses **Skeubrute**, a design system that combines **strong neobrutalism** with **abstract skeuomorphism**. | |
| 6 | + | ||
| 7 | + | ### Core Philosophy | |
| 8 | + | ||
| 9 | + | - **Neobrutalism**: Bold 3px borders, hard-edged offset shadows (4px), high contrast | |
| 10 | + | - **Skeuomorphism**: Paper textures, embossed text effects, tactile button states, realistic depth | |
| 11 | + | ||
| 12 | + | The result is an interface that feels like physical paper and buttons while maintaining a bold, modern aesthetic. | |
| 13 | + | ||
| 14 | + | --- | |
| 15 | + | ||
| 16 | + | ## Color System | |
| 17 | + | ||
| 18 | + | ### Background Colors | |
| 19 | + | ||
| 20 | + | | Variable | Hex | Usage | | |
| 21 | + | |----------|-----|-------| | |
| 22 | + | | `--bg-primary` | `#E8F4F8` | Page background | | |
| 23 | + | | `--bg-secondary` | `#D4EBF2` | Secondary surfaces, hover states | | |
| 24 | + | | `--bg-tertiary` | `#C0E2EC` | Tertiary surfaces | | |
| 25 | + | | `--bg-card` | `#FFFFFF` | Cards, modals, inputs | | |
| 26 | + | ||
| 27 | + | ### Text Colors | |
| 28 | + | ||
| 29 | + | | Variable | Hex | Usage | | |
| 30 | + | |----------|-----|-------| | |
| 31 | + | | `--text-primary` | `#1B365D` | Headings, primary text | | |
| 32 | + | | `--text-secondary` | `#3D5A80` | Body text, descriptions | | |
| 33 | + | | `--text-muted` | `#6B8CAE` | Captions, hints, disabled | | |
| 34 | + | ||
| 35 | + | ### Accent Colors | |
| 36 | + | ||
| 37 | + | | Variable | Hex | Usage | | |
| 38 | + | |----------|-----|-------| | |
| 39 | + | | `--accent-yellow` | `#F7D154` | Primary actions, active states, focus | | |
| 40 | + | | `--accent-green` | `#5CB85C` | Success, active status, tasks | | |
| 41 | + | | `--accent-blue` | `#1B365D` | Borders, info, emails | | |
| 42 | + | | `--accent-purple` | `#7B68EE` | Recurrence, essays, special | | |
| 43 | + | | `--accent-red` | `#DC3545` | Errors, high priority, warnings | | |
| 44 | + | | `--accent-cyan` | `#17A2B8` | Info, side projects, completed | | |
| 45 | + | ||
| 46 | + | --- | |
| 47 | + | ||
| 48 | + | ## Logo | |
| 49 | + | ||
| 50 | + | ### Full Logo | |
| 51 | + | ||
| 52 | + | The GoingsOn wordmark uses **Reglo Bold** with the following specifications: | |
| 53 | + | ||
| 54 | + | - **Font**: Reglo Bold | |
| 55 | + | - **Color**: `--text-primary` (#1B365D) on light backgrounds | |
| 56 | + | - **Alternate**: White on dark backgrounds | |
| 57 | + | - **Letter-spacing**: -0.02em (slightly tightened) | |
| 58 | + | ||
| 59 | + | ### Small Logo (Icon) | |
| 60 | + | ||
| 61 | + | The compact logo displays **"GO"** in Reglo Bold, centered within a neobrutalist container: | |
| 62 | + | ||
| 63 | + | ``` | |
| 64 | + | ┌─────────────────┐ | |
| 65 | + | │ │ | |
| 66 | + | │ GO │ | |
| 67 | + | │ │ | |
| 68 | + | └─────────────────┘ | |
| 69 | + | ``` | |
| 70 | + | ||
| 71 | + | **Specifications:** | |
| 72 | + | - **Background**: `--accent-yellow` (#F7D154) | |
| 73 | + | - **Text**: `--text-primary` (#1B365D) | |
| 74 | + | - **Border**: 3px solid `--border-color` (#1B365D) | |
| 75 | + | - **Border Radius**: `--radius-sm` (6px) | |
| 76 | + | - **Shadow**: 2px 2px 0 `--border-color` (neobrutalist offset) | |
| 77 | + | ||
| 78 | + | **Files:** | |
| 79 | + | - `media/logo-go.svg` - Small "GO" icon logo | |
| 80 | + | - `media/logo-goingson.svg` - Full wordmark (future) | |
| 81 | + | ||
| 82 | + | ### Usage Guidelines | |
| 83 | + | ||
| 84 | + | | Context | Logo | Min Size | | |
| 85 | + | |---------|------|----------| | |
| 86 | + | | App icon / Favicon | GO icon | 16x16px | | |
| 87 | + | | Sidebar / Header | GO icon | 32x32px | | |
| 88 | + | | Splash / Marketing | Full wordmark | 120px wide | | |
| 89 | + | | Documentation | Either | Context-dependent | | |
| 90 | + | ||
| 91 | + | --- | |
| 92 | + | ||
| 93 | + | ## Typography | |
| 94 | + | ||
| 95 | + | ### Font Families | |
| 96 | + | ||
| 97 | + | ```css | |
| 98 | + | --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
| 99 | + | --font-serif: Georgia, 'Times New Roman', serif; | |
| 100 | + | --font-mono: 'SF Mono', 'Consolas', 'Liberation Mono', monospace; | |
| 101 | + | --font-display: 'Reglo', var(--font-serif); | |
| 102 | + | ``` | |
| 103 | + | ||
| 104 | + | ### Display Font: Reglo | |
| 105 | + | ||
| 106 | + | The **Reglo** font is used for the logo and prominent H1-style headings. Reglo is an open-source display font with a bold, geometric character that complements the neobrutalist aesthetic. | |
| 107 | + | ||
| 108 | + | - **Source**: [Reglo by Sebastien Sanfilippo](https://github.com/nicokant/reglo) (OFL license) | |
| 109 | + | - **Usage**: Logo wordmark "GoingsOn", hero headings, splash screens | |
| 110 | + | - **Weights**: Bold only (display use) | |
| 111 | + | ||
| 112 | + | ```css | |
| 113 | + | @font-face { | |
| 114 | + | font-family: 'Reglo'; | |
| 115 | + | src: url('fonts/Reglo-Bold.woff2') format('woff2'); | |
| 116 | + | font-weight: 700; | |
| 117 | + | font-display: swap; | |
| 118 | + | } | |
| 119 | + | ``` | |
| 120 | + | ||
| 121 | + | ### Semantic Aliases | |
| 122 | + | ||
| 123 | + | - `--font-display`: Uses `Reglo` for logo and hero headings | |
| 124 | + | - `--font-heading`: Uses `--font-serif` for titles and headings | |
| 125 | + | - `--font-body`: Uses `--font-sans` for body text | |
| 126 | + | ||
| 127 | + | ### Type Scale | |
| 128 | + | ||
| 129 | + | | Element | Size | Weight | Font | | |
| 130 | + | |---------|------|--------|------| | |
| 131 | + | | Page Title | 1.75rem | 700 | Serif | | |
| 132 | + | | Card Title | 1.1rem | 700 | Serif | | |
| 133 | + | | Modal Title | 1.25rem | 700 | Serif | | |
| 134 | + | | Body | 1rem | 400 | Sans | | |
| 135 | + | | Small | 0.875rem | 400 | Sans | | |
| 136 | + | | Caption | 0.75rem | 600 | Sans | | |
| 137 | + | ||
| 138 | + | --- | |
| 139 | + | ||
| 140 | + | ## Spacing | |
| 141 | + | ||
| 142 | + | The spacing system uses a consistent rem-based scale: | |
| 143 | + | ||
| 144 | + | | Name | Value | Usage | | |
| 145 | + | |------|-------|-------| | |
| 146 | + | | xs | 0.25rem | Badge padding, tight gaps | | |
| 147 | + | | sm | 0.5rem | Small gaps, icon spacing | | |
| 148 | + | | md | 0.75rem | Standard padding | | |
| 149 | + | | lg | 1rem | Section padding | | |
| 150 | + | | xl | 1.25rem | Card padding | | |
| 151 | + | | 2xl | 1.5rem | Page margins | | |
| 152 | + | ||
| 153 | + | --- | |
| 154 | + | ||
| 155 | + | ## Border & Shadow System | |
| 156 | + | ||
| 157 | + | ### Border Widths | |
| 158 | + | ||
| 159 | + | | Element | Width | | |
| 160 | + | |---------|-------| | |
| 161 | + | | Cards, Buttons, Modals | 3px (`--border-width`) | | |
| 162 | + | | Inputs, Badges, Tags | 2px | | |
| 163 | + | | Dividers | 2px | | |
| 164 | + | ||
| 165 | + | ### Border Radius | |
| 166 | + | ||
| 167 | + | | Variable | Value | Usage | | |
| 168 | + | |----------|-------|-------| | |
| 169 | + | | `--radius-sm` | 6px | Buttons, badges, inputs | | |
| 170 | + | | `--radius-md` | 12px | Cards, filter bars | | |
| 171 | + | | `--radius-lg` | 16px | Modals | | |
| 172 | + | ||
| 173 | + | ### Shadow System | |
| 174 | + | ||
| 175 | + | ```css | |
| 176 | + | /* Shadow utilities */ | |
| 177 | + | .shadow-sm { box-shadow: 2px 2px 0 var(--border-color); } | |
| 178 | + | .shadow-md { box-shadow: 4px 4px 0 var(--border-color); } /* Default */ | |
| 179 | + | .shadow-lg { box-shadow: 6px 6px 0 var(--border-color); } | |
| 180 | + | .shadow-xl { box-shadow: 8px 8px 0 var(--border-color); } /* Modals */ | |
| 181 | + | .shadow-none { box-shadow: none; } | |
| 182 | + | ``` | |
| 183 | + | ||
| 184 | + | ### Offset Shadow Values | |
| 185 | + | ||
| 186 | + | | Element | Shadow Offset | | |
| 187 | + | |---------|---------------| | |
| 188 | + | | Cards | 4px (with stacked paper effect) | | |
| 189 | + | | Buttons | 4px | | |
| 190 | + | | Modals | 8px | | |
| 191 | + | | Small buttons | 2-3px | | |
| 192 | + | | Inputs | Inset deboss | | |
| 193 | + | ||
| 194 | + | --- | |
| 195 | + | ||
| 196 | + | ## Skeuomorphic Effects | |
| 197 | + | ||
| 198 | + | ### Paper Texture | |
| 199 | + | ||
| 200 | + | Cards have a subtle SVG noise pattern for a paper-like feel: | |
| 201 | + | ||
| 202 | + | ```css | |
| 203 | + | .card { | |
| 204 | + | background-image: var(--texture-paper); | |
| 205 | + | } | |
| 206 | + | ``` | |
| 207 | + | ||
| 208 | + | ### Embossed Text | |
| 209 | + | ||
| 210 | + | Headings use subtle text shadows to create a pressed-in effect: | |
| 211 | + | ||
| 212 | + | ```css | |
| 213 | + | .card-title, .modal-title, .page-title { | |
| 214 | + | text-shadow: var(--emboss-light), var(--emboss-dark); | |
| 215 | + | } | |
| 216 | + | ``` | |
| 217 | + | ||
| 218 | + | Where: | |
| 219 | + | - `--emboss-light`: `1px 1px 0 rgba(255, 255, 255, 0.5)` | |
| 220 | + | - `--emboss-dark`: `-1px -1px 0 rgba(0, 0, 0, 0.08)` | |
| 221 | + | ||
| 222 | + | ### Debossed Inputs | |
| 223 | + | ||
| 224 | + | Form inputs have an inset shadow for a carved-in feel: | |
| 225 | + | ||
| 226 | + | ```css | |
| 227 | + | .form-input { | |
| 228 | + | box-shadow: var(--deboss); | |
| 229 | + | } | |
| 230 | + | /* --deboss: inset 1px 1px 2px rgba(0,0,0,0.08), inset -1px -1px 0 rgba(255,255,255,0.5) */ | |
| 231 | + | ``` | |
| 232 | + | ||
| 233 | + | ### Tactile Buttons | |
| 234 | + | ||
| 235 | + | Buttons have a gradient overlay for a 3D, pressable feel: | |
| 236 | + | ||
| 237 | + | ```css | |
| 238 | + | .btn { | |
| 239 | + | background-image: var(--btn-gradient); | |
| 240 | + | } | |
| 241 | + | .btn:active { | |
| 242 | + | background-image: var(--btn-gradient-pressed); | |
| 243 | + | } | |
| 244 | + | ``` | |
| 245 | + | ||
| 246 | + | --- | |
| 247 | + | ||
| 248 | + | ## Components | |
| 249 | + | ||
| 250 | + | ### Buttons | |
| 251 | + | ||
| 252 | + | ```html | |
| 253 | + | <!-- Primary button (yellow background) --> | |
| 254 | + | <button class="btn btn-primary">Action</button> | |
| 255 | + | ||
| 256 | + | <!-- Secondary button (light background) --> | |
| 257 | + | <button class="btn btn-secondary">Cancel</button> | |
| 258 | + | ||
| 259 | + | <!-- Small button --> | |
| 260 | + | <button class="btn btn-sm">Small</button> | |
| 261 | + | ``` | |
| 262 | + | ||
| 263 | + | **States:** | |
| 264 | + | - **Default**: 4px offset shadow | |
| 265 | + | - **Hover**: Lifts up (-2px, -2px), shadow increases | |
| 266 | + | - **Active/Pressed**: Pushes down (2px, 2px), shadow disappears | |
| 267 | + | ||
| 268 | + | ### Cards | |
| 269 | + | ||
| 270 | + | ```html | |
| 271 | + | <div class="card"> | |
| 272 | + | <div class="card-header"> | |
| 273 | + | <h3 class="card-title">Card Title</h3> | |
| 274 | + | </div> | |
| 275 | + | <p class="card-description">Description text</p> | |
| 276 | + | <div class="card-meta"> | |
| 277 | + | <span class="tag type-job">Job</span> | |
| 278 | + | <span class="tag status-active">Active</span> | |
| 279 | + | </div> | |
| 280 | + | </div> | |
| 281 | + | ``` | |
| 282 | + | ||
| 283 | + | Cards have: | |
| 284 | + | - Paper texture background | |
| 285 | + | - Stacked paper shadow effect (two-layer shadow) | |
| 286 | + | - Lift on hover | |
| 287 | + | ||
| 288 | + | ### Badges & Tags | |
| 289 | + | ||
| 290 | + | **Using data attributes (preferred):** | |
| 291 | + | ||
| 292 | + | ```html | |
| 293 | + | <span class="badge" data-color="green">Success</span> | |
| 294 | + | <span class="badge" data-color="yellow">Warning</span> | |
| 295 | + | <span class="badge" data-color="red">Error</span> | |
| 296 | + | <span class="badge" data-color="cyan">Info</span> | |
| 297 | + | <span class="badge" data-color="purple">Special</span> | |
| 298 | + | <span class="badge" data-color="muted">Default</span> | |
| 299 | + | ``` | |
| 300 | + | ||
| 301 | + | **Legacy classes:** | |
| 302 | + | ||
| 303 | + | ```html | |
| 304 | + | <span class="tag type-job">Job</span> | |
| 305 | + | <span class="tag type-sideproject">Side Project</span> | |
| 306 | + | <span class="tag status-active">Active</span> | |
| 307 | + | <span class="tag status-completed">Completed</span> | |
| 308 | + | ``` | |
| 309 | + | ||
| 310 | + | ### Form Inputs | |
| 311 | + | ||
| 312 | + | ```html | |
| 313 | + | <div class="form-group"> | |
| 314 | + | <label class="form-label">Label</label> | |
| 315 | + | <input type="text" class="form-input" placeholder="Enter text..."> | |
| 316 | + | </div> | |
| 317 | + | ||
| 318 | + | <div class="form-group"> | |
| 319 | + | <label class="form-label">Select</label> | |
| 320 | + | <select class="form-select"> | |
| 321 | + | <option>Option 1</option> | |
| 322 | + | </select> | |
| 323 | + | </div> | |
| 324 | + | ||
| 325 | + | <div class="form-group"> | |
| 326 | + | <label class="form-label">Textarea</label> | |
| 327 | + | <textarea class="form-textarea"></textarea> | |
| 328 | + | </div> | |
| 329 | + | ``` | |
| 330 | + | ||
| 331 | + | **Focus state**: Yellow ring (3px) around the input | |
| 332 | + | ||
| 333 | + | ### Modals | |
| 334 | + | ||
| 335 | + | ```html | |
| 336 | + | <div class="modal-overlay"> | |
| 337 | + | <div class="modal-container"> | |
| 338 | + | <div class="modal-header"> | |
| 339 | + | <h2 class="modal-title">Modal Title</h2> | |
| 340 | + | <button class="modal-close">×</button> | |
| 341 | + | </div> | |
| 342 | + | <div class="modal-content"> | |
| 343 | + | <!-- Content here --> | |
| 344 | + | </div> | |
| 345 | + | </div> | |
| 346 | + | </div> | |
| 347 | + | ``` | |
| 348 | + | ||
| 349 | + | Modals have: | |
| 350 | + | - 8px offset shadow | |
| 351 | + | - 16px border radius | |
| 352 | + | - Semi-transparent overlay | |
| 353 | + | ||
| 354 | + | ### Tables | |
| 355 | + | ||
| 356 | + | ```html | |
| 357 | + | <table class="task-table"> | |
| 358 | + | <thead> | |
| 359 | + | <tr> | |
| 360 | + | <th>Column</th> | |
| 361 | + | </tr> | |
| 362 | + | </thead> | |
| 363 | + | <tbody> | |
| 364 | + | <tr> | |
| 365 | + | <td>Data</td> | |
| 366 | + | </tr> | |
| 367 | + | </tbody> | |
| 368 | + | </table> | |
| 369 | + | ``` | |
| 370 | + | ||
| 371 | + | Features: | |
| 372 | + | - Uppercase, letter-spaced headers | |
| 373 | + | - Hover state on rows | |
| 374 | + | - Selected state (yellow background) | |
| 375 | + | ||
| 376 | + | ### Empty & Error States | |
| 377 | + | ||
| 378 | + | ```html | |
| 379 | + | <div class="empty-state"> | |
| 380 | + | <div class="empty-state-icon">icon</div> | |
| 381 | + | <p class="empty-state-text">No items found</p> | |
| 382 | + | </div> | |
| 383 | + | ||
| 384 | + | <div class="error-state"> | |
| 385 | + | Error message here | |
| 386 | + | </div> | |
| 387 | + | ``` | |
| 388 | + | ||
| 389 | + | --- | |
| 390 | + | ||
| 391 | + | ## Animation Standards | |
| 392 | + | ||
| 393 | + | ### Timing | |
| 394 | + | ||
| 395 | + | | Type | Duration | Easing | | |
| 396 | + | |------|----------|--------| | |
| 397 | + | | Hover effects | 0.1s - 0.15s | ease | | |
| 398 | + | | Transform (lift/press) | 0.1s | ease | | |
| 399 | + | | Focus rings | instant | - | | |
| 400 | + | ||
| 401 | + | ### Hover Lift Effect | |
| 402 | + | ||
| 403 | + | ```css | |
| 404 | + | .hover-lift { | |
| 405 | + | transition: transform 0.15s ease, box-shadow 0.15s ease; | |
| 406 | + | } | |
| 407 | + | .hover-lift:hover { | |
| 408 | + | transform: translate(-2px, -2px); | |
| 409 | + | } | |
| 410 | + | .hover-lift:active { | |
| 411 | + | transform: translate(2px, 2px); | |
| 412 | + | } | |
| 413 | + | ``` | |
| 414 | + | ||
| 415 | + | --- | |
| 416 | + | ||
| 417 | + | ## Accessibility | |
| 418 | + | ||
| 419 | + | ### Focus States | |
| 420 | + | ||
| 421 | + | All interactive elements have visible focus indicators: | |
| 422 | + | ||
| 423 | + | ```css | |
| 424 | + | .btn:focus-visible, | |
| 425 | + | .form-input:focus-visible { | |
| 426 | + | outline: 3px solid var(--accent-yellow); | |
| 427 | + | outline-offset: 2px; | |
| 428 | + | } | |
| 429 | + | ``` | |
| 430 | + | ||
| 431 | + | ### Screen Reader Support | |
| 432 | + | ||
| 433 | + | Use `.sr-only` for visually hidden but accessible text: | |
| 434 | + | ||
| 435 | + | ```html | |
| 436 | + | <span class="sr-only">Screen reader text</span> | |
| 437 | + | ``` | |
| 438 | + | ||
| 439 | + | ### Color Contrast | |
| 440 | + | ||
| 441 | + | All text colors meet WCAG AA standards against their backgrounds: | |
| 442 | + | - Primary text on card: 8.5:1 | |
| 443 | + | - Secondary text on card: 5.2:1 | |
| 444 | + | - Muted text on card: 3.8:1 | |
| 445 | + | ||
| 446 | + | --- | |
| 447 | + | ||
| 448 | + | ## File Organization | |
| 449 | + | ||
| 450 | + | ``` | |
| 451 | + | src-tauri/frontend/ | |
| 452 | + | ├── css/ | |
| 453 | + | │ └── styles.css # All styles (design system + components) | |
| 454 | + | ├── fonts/ | |
| 455 | + | │ └── Reglo-Bold.woff2 # Display font | |
| 456 | + | ├── js/ | |
| 457 | + | │ ├── goingson.js # Namespace root (window.GoingsOn) | |
| 458 | + | │ ├── api.js # Tauri IPC abstraction | |
| 459 | + | │ ├── state.js # Centralized state + pub/sub | |
| 460 | + | │ ├── utils.js # HTML escaping, validation, debounce | |
| 461 | + | │ ├── components.js # Modal, toast, form modal, confirm dialog | |
| 462 | + | │ ├── navigation.js # View switching, sidebar | |
| 463 | + | │ ├── tasks.js # Task list, CRUD, rendering | |
| 464 | + | │ ├── projects.js # Project list, detail view | |
| 465 | + | │ ├── events.js # Event list, CRUD | |
| 466 | + | │ ├── emails.js # Email list, threading | |
| 467 | + | │ ├── settings.js # Settings, LLM config, export | |
| 468 | + | │ └── app.js # App initialization, menu listeners | |
| 469 | + | └── index.html # Entry point (no inline styles) | |
| 470 | + | ``` | |
| 471 | + | ||
| 472 | + | See `ARCHITECTURE.md` (in this folder) for the full JS file listing and namespace organization. | |
| 473 | + | ||
| 474 | + | --- | |
| 475 | + | ||
| 476 | + | ## CSS Naming Convention | |
| 477 | + | ||
| 478 | + | GoingsOn uses a simplified BEM-adjacent pattern with kebab-case. | |
| 479 | + | ||
| 480 | + | ### Pattern: `.block-element` | |
| 481 | + | ||
| 482 | + | ``` | |
| 483 | + | .component → Block (card, modal, btn, form) | |
| 484 | + | .component-part → Element within block (card-header, modal-title) | |
| 485 | + | .component-modifier → Variant (btn-primary, btn-sm) | |
| 486 | + | ``` | |
| 487 | + | ||
| 488 | + | ### Examples | |
| 489 | + | ||
| 490 | + | ```css | |
| 491 | + | /* Block */ | |
| 492 | + | .card { } | |
| 493 | + | .modal { } | |
| 494 | + | .btn { } | |
| 495 | + | ||
| 496 | + | /* Elements (single hyphen) */ | |
| 497 | + | .card-header { } | |
| 498 | + | .card-title { } | |
| 499 | + | .card-description { } | |
| 500 | + | .modal-overlay { } |
Lines truncated
| @@ -0,0 +1,118 @@ | |||
| 1 | + | # GoingsOn - Roadmap | |
| 2 | + | ||
| 3 | + | Pending features and improvements. Completed work archived in `docs/archive/go_todo_done.md`. | |
| 4 | + | ||
| 5 | + | **Scope:** Sections tagged `(pre-beta)` ship before initial beta. Untagged sections are post-beta. Ready for extensive human testing. | |
| 6 | + | ||
| 7 | + | --- | |
| 8 | + | ||
| 9 | + | ## Desktop Distribution | |
| 10 | + | ||
| 11 | + | ### Mobile Port (Tauri 2) | |
| 12 | + | CSS-first responsive design with touch gesture module. See **[todo_mobile.md](./todo_mobile.md)** for remaining work. | |
| 13 | + | ||
| 14 | + | **Remaining (pre-beta):** | |
| 15 | + | - [ ] `cargo tauri android init` | |
| 16 | + | - [ ] Test all CRUD operations on mobile WebView | |
| 17 | + | - [ ] Physical device testing and polish | |
| 18 | + | ||
| 19 | + | ### Windows Build (pre-beta) | |
| 20 | + | - [ ] `cargo tauri build` on Windows — verify `.msi` installer | |
| 21 | + | - [ ] Test on Windows (VM or physical) | |
| 22 | + | - [ ] Code-sign with Authenticode certificate | |
| 23 | + | ||
| 24 | + | ### Linux Build | |
| 25 | + | - [ ] AppImage (x86_64 + aarch64), .deb, .rpm. Deps: WebKit2GTK 4.1, OpenSSL, libayatana-appindicator. | |
| 26 | + | ||
| 27 | + | ### Package Managers (post-beta) | |
| 28 | + | - [ ] Homebrew Cask (macOS), Flatpak (Linux), winget (Windows) | |
| 29 | + | - [ ] MCP server standalone binary (~7MB) — separate distribution for CLI/agent users | |
| 30 | + | ||
| 31 | + | --- | |
| 32 | + | ||
| 33 | + | ### Calendar Views | |
| 34 | + | - [ ] Restructure navigation: domain tabs vs temporal views (day/week/month) | |
| 35 | + | - [ ] Month calendar view (grid with event + task dots) | |
| 36 | + | - [ ] Week view (multi-day timeline) | |
| 37 | + | ||
| 38 | + | ### Time Tracking | |
| 39 | + | - [ ] Optional `estimated_minutes` field on tasks | |
| 40 | + | - [ ] Start/stop timer, actual vs estimated comparison | |
| 41 | + | - [ ] Focus timer mode (Pomodoro-style linked to active task) | |
| 42 | + | - [ ] Time tracking summary: per-project and per-day totals | |
| 43 | + | ||
| 44 | + | ### Cloud Sync — Remaining | |
| 45 | + | #### Pre-beta | |
| 46 | + | - [ ] Test full sync flow against live MNW server | |
| 47 | + | ||
| 48 | + | #### Post-beta | |
| 49 | + | - [ ] Recovery key generation (printable paper backup for encryption password) | |
| 50 | + | ||
| 51 | + | #### Deferred | |
| 52 | + | - [ ] Field-level merge / conflict resolution UI | |
| 53 | + | - [ ] Sync provider plugins (filesystem, WebDAV, S3-compatible) | |
| 54 | + | - [ ] Sync history/log viewer | |
| 55 | + | - [ ] Send Later (cloud-dependent) | |
| 56 | + | - [ ] Activity log | |
| 57 | + | ||
| 58 | + | --- | |
| 59 | + | ||
| 60 | + | ### File Attachments | |
| 61 | + | Depends on Cloud Sync. | |
| 62 | + | - [ ] Attachment storage, attach to tasks/projects, view email attachments, download, link to local files, thumbnails | |
| 63 | + | ||
| 64 | + | ### Passkey Authentication (WebAuthn/FIDO2) | |
| 65 | + | - [ ] Phase 1: Local biometric unlock (Touch ID, Windows Hello) | |
| 66 | + | - [ ] Phase 2: Hardware security key support (YubiKey, SoloKey) | |
| 67 | + | - [ ] Phase 3: Cloud passkey authentication | |
| 68 | + | - [ ] Phase 4: E2EE key protection (passkey PRF extension) | |
| 69 | + | ||
| 70 | + | ### Agent Integration (MCP Server) — Remaining | |
| 71 | + | - [ ] Phase 3: In-app agent tab (chat UI, message history, tool calling, streaming) | |
| 72 | + | - [ ] Phase 4: Conversation persistence, suggested actions, keyboard shortcuts | |
| 73 | + | ||
| 74 | + | ### Plugin System (Rhai) — Remaining | |
| 75 | + | - [ ] Phase 3: Export adapters, custom commands, lifecycle hooks | |
| 76 | + | - [ ] Phase 4: Hot-reload, AST cache, install from URL, update checking | |
| 77 | + | - [ ] Starter plugins: import (TaskWarrior, Todoist, Things 3, Apple Reminders, Notion, Trello, Google Tasks), export (CSV, JSON, Markdown, ICS, Obsidian) | |
| 78 | + | - [ ] Contact plugins: import (vCard, Apple, Google, Microsoft, LinkedIn), sync (CardDAV, Google, Microsoft Graph) | |
| 79 | + | - [ ] Calendar sync: Google, Apple/iCloud CalDAV, generic CalDAV, .ics import | |
| 80 | + | - [ ] External tools: Raycast, Alfred, iOS Shortcuts, Zapier/Make webhooks | |
| 81 | + | ||
| 82 | + | --- | |
| 83 | + | ||
| 84 | + | ## OAuth Provider Registration | |
| 85 | + | ||
| 86 | + | | Provider | Registered | Client ID Set | Tested | | |
| 87 | + | |-----------|:----------:|:-------------:|:------:| | |
| 88 | + | | Fastmail | [~] (email sent) | [ ] | [ ] | | |
| 89 | + | | Google | [x] | [x] | [ ] | | |
| 90 | + | | Microsoft | [x] | [x] | [ ] | | |
| 91 | + | ||
| 92 | + | ## Shared Code Extraction (Cross-Project) | |
| 93 | + | - [ ] Updater UI: extract nearly-identical updater.js from GO/BB into shared module | |
| 94 | + | - [ ] Theme loading: deduplicate TOML theme parser across GO/BB/AF | |
| 95 | + | - [ ] Rhai host functions: deduplicate plugin runtime setup across GO/BB/AF | |
| 96 | + | - [ ] Saved queries: unify GO saved views, BB query feeds, AF smart folders into shared pattern | |
| 97 | + | - [ ] FTS5 query building: extract shared SQLite full-text search utilities | |
| 98 | + | ||
| 99 | + | ## Deferred | |
| 100 | + | - [ ] Co-working feature: E2E encrypted project sharing via UUID-based links (key material in URL fragment, never sent to server). XChaCha20-Poly1305 symmetric per project, X25519 key exchange, Argon2 key derivation. Relay server MVP, P2P later. CRDTs for conflict resolution (LWW-Register for fields, OR-Set for tags, RGA for ordered lists). Permissions: viewer / collaborator / admin / owner. Shareable units: project, saved view, calendar overlay (busy/free only), individual task. 7 implementation phases. | |
| 101 | + | - [ ] Portability: publish synckit-client as a crate or use git submodule | |
| 102 | + | - [ ] Apple Watch app | |
| 103 | + | - [ ] Home screen widgets (iOS/Android) | |
| 104 | + | ||
| 105 | + | --- | |
| 106 | + | ||
| 107 | + | ## Architecture | |
| 108 | + | ``` | |
| 109 | + | crates/core/ Domain types, urgency calc, parser, repository traits | |
| 110 | + | crates/db-sqlite/ SQLite repository implementations | |
| 111 | + | crates/plugin-runtime/ Rhai plugin system | |
| 112 | + | plugins/ Bundled reference plugins | |
| 113 | + | src-tauri/ Desktop app (Tauri 2 + vanilla JS) | |
| 114 | + | migrations/sqlite/ SQLite migrations | |
| 115 | + | ``` | |
| 116 | + | ||
| 117 | + | - **Domain purchased:** goingson.app (Feb 2026) | |
| 118 | + | - docs/architecture.md documents crate structure and data flow |
| @@ -0,0 +1,205 @@ | |||
| 1 | + | # GoingsOn - Mobile Port | |
| 2 | + | ||
| 3 | + | Tauri 2 iOS/Android port. CSS-first responsive design with touch gesture module. | |
| 4 | + | ||
| 5 | + | **What stays the same:** | |
| 6 | + | - `crates/core/` — domain logic, unchanged | |
| 7 | + | - `crates/db-sqlite/` — SQLite repository, unchanged | |
| 8 | + | - `src-tauri/src/commands/` — Tauri commands, unchanged | |
| 9 | + | - `src-tauri/frontend/js/` — all JS modules (minor mobile branches) | |
| 10 | + | - `api.js` with `invoke()` — same IPC mechanism | |
| 11 | + | ||
| 12 | + | **What changed:** CSS responsiveness, touch interaction module, mobile navigation, Tauri mobile configuration. | |
| 13 | + | ||
| 14 | + | --- | |
| 15 | + | ||
| 16 | + | ## Completed Work | |
| 17 | + | ||
| 18 | + | ### Phase 1: CSS Foundation | |
| 19 | + | - [x] `@media (max-width: 768px)` breakpoint in `styles.css` | |
| 20 | + | - [x] `@media (hover: none)` disables sticky hover effects | |
| 21 | + | - [x] `viewport-fit=cover` meta tag | |
| 22 | + | - [x] Safe area insets on fixed elements (`env(safe-area-inset-*)`) | |
| 23 | + | - [x] Tab navigation hidden on mobile | |
| 24 | + | - [x] Page titles hidden on mobile (shown in header instead) | |
| 25 | + | - [x] "Project Management" subtitle hidden, current view title in header | |
| 26 | + | - [x] Contacts & Settings buttons in upper-right header (stacked vertically) | |
| 27 | + | - [x] Task table → card layout with priority-colored left borders | |
| 28 | + | - [x] Event table → list reflow | |
| 29 | + | - [x] Modals → bottom sheets (anchored to bottom, rounded top corners, drag handle) | |
| 30 | + | - [x] Toasts repositioned for mobile | |
| 31 | + | - [x] Filter bar collapses on mobile | |
| 32 | + | - [x] Mobile sort bar for tasks | |
| 33 | + | - [x] Day plan sidebar toggle button | |
| 34 | + | ||
| 35 | + | ### Phase 2: Touch Module (`js/touch.js`) | |
| 36 | + | - [x] `isTouchDevice` detection | |
| 37 | + | - [x] `addLongPress(element, callback, duration)` | |
| 38 | + | - [x] `addSwipeActions(element, config)` | |
| 39 | + | - [x] `addPullToRefresh(container, callback)` | |
| 40 | + | - [x] `addSwipeNavigation(container, handlers)` | |
| 41 | + | - [x] `addDragToDismiss(element, onDismiss)` | |
| 42 | + | - [x] All functions return cleanup functions, all are no-ops on non-touch devices | |
| 43 | + | ||
| 44 | + | ### Phase 3: Navigation + Bottom Sheets | |
| 45 | + | - [x] Floating nav dot (70px Skeubrute button, bottom-right) | |
| 46 | + | - [x] Radial petal dial (6 views: Projects, Tasks, Emails, Events, Daily, Weekly) | |
| 47 | + | - [x] Rectangular petal spokes with auto-computed radius (no overlap) | |
| 48 | + | - [x] 90° arc based on which corner the dot is nearest | |
| 49 | + | - [x] Petals rotated along spoke, flipped for readability | |
| 50 | + | - [x] Reverse z-order for natural fan overlap | |
| 51 | + | - [x] Center "+" button creates new item for current view | |
| 52 | + | - [x] Click handler (mouse) + touch handler (tap vs drag) | |
| 53 | + | - [x] Double-fire prevention (touch + synthesized click) | |
| 54 | + | - [x] Action bottom sheets replace context menus on touch | |
| 55 | + | - [x] `showContextMenuSmart()` delegates to action sheet on touch, regular menu on desktop | |
| 56 | + | - [x] Modal swipe-to-dismiss wiring | |
| 57 | + | ||
| 58 | + | ### Phase 4: View Adaptations | |
| 59 | + | - [x] Tasks: mobile sort dropdown, filter bottom sheet toggle | |
| 60 | + | - [x] Events: date group headers in mobile render | |
| 61 | + | - [x] Events: status indicator below nav dot (dial closed) / on petal badge (dial open) | |
| 62 | + | - [x] Events: indicator logic — empty if no events today, green if none soon, yellow if imminent, red if now | |
| 63 | + | - [x] Events tab pushed to far right on desktop (`margin-left: auto`) | |
| 64 | + | - [x] Day plan: `onSlotTap` handler (tap-to-create with 30min default on touch) | |
| 65 | + | - [x] Day plan: swipe left/right navigates days | |
| 66 | + | - [x] Day plan: collapsible unscheduled tasks sidebar | |
| 67 | + | - [x] Emails: in-app compose modal on mobile (no separate window) | |
| 68 | + | - [x] Keyboard shortcuts disabled on touch devices | |
| 69 | + | ||
| 70 | + | ### Phase 5: Tauri Mobile Build Config | |
| 71 | + | - [x] `Cargo.toml`: desktop-only deps gated with `cfg(not(any(target_os = "ios", target_os = "android")))` | |
| 72 | + | - `tauri-plugin-shell`, `tauri-plugin-notification`, `tauri-plugin-window-state`, `notify`, `notify-debouncer-mini` | |
| 73 | + | - [x] `Cargo.toml`: `crate-type = ["staticlib", "cdylib", "lib"]` for iOS static library | |
| 74 | + | - [x] `lib.rs`: `db_watcher` and `notifications` modules gated for desktop only | |
| 75 | + | - [x] `lib.rs`: `build_mobile_app()` function + `#[cfg(mobile)] #[tauri::mobile_entry_point]` | |
| 76 | + | - [x] `main.rs`: desktop-only plugin init, tray, background services gated with `cfg` | |
| 77 | + | - [x] `commands/window.rs`: `open_compose_window` and `set_window_title` gated for desktop only | |
| 78 | + | - [x] `tauri.conf.json`: identifier `com.goingson.app`, minWidth 320 | |
| 79 | + | - [x] Capabilities split: `default.json` (cross-platform) + `desktop.json` (shell, notifications) | |
| 80 | + | - [x] `cargo tauri ios init` → generates `gen/apple/` Xcode project | |
| 81 | + | - [x] Builds and runs on iOS simulator (iPhone 17 Pro, iOS 26.2) | |
| 82 | + | - [x] Safe area inset padding on header for iOS dynamic island | |
| 83 | + | ||
| 84 | + | --- | |
| 85 | + | ||
| 86 | + | ## Remaining Work | |
| 87 | + | ||
| 88 | + | ### Build & Test | |
| 89 | + | - [x] Run `cargo tauri ios init` (creates `gen/apple/`) | |
| 90 | + | - [ ] Run `cargo tauri android init` (creates `gen/android/`) | |
| 91 | + | - [x] Add Rust cross-compilation targets (`rustup target add aarch64-apple-ios-sim`) | |
| 92 | + | - [x] Test on iOS simulator (`cargo tauri ios dev "iPhone 17 Pro"`) | |
| 93 | + | - [ ] Test on Android emulator (`cargo tauri android dev`) | |
| 94 | + | - [x] Verify SQLite path resolution via `app_data_dir()` | |
| 95 | + | - [ ] All CRUD operations verified on mobile WebView | |
| 96 | + | ||
| 97 | + | ### Interaction Wiring | |
| 98 | + | - [x] Wire swipe actions to task rows (swipe right: complete, swipe left: snooze) | |
| 99 | + | - [x] Wire swipe actions to email items (swipe right: archive, swipe left: delete) | |
| 100 | + | - [x] Wire swipe actions to event rows (swipe left: delete) | |
| 101 | + | - [x] Wire pull-to-refresh to list views (tasks, emails, events) | |
| 102 | + | - [x] Long-press selection mode on list items (tasks, emails — toggles bulk selection) | |
| 103 | + | - [x] Nav dot drag-to-reposition (touch drag with edge snapping — tested, working) | |
| 104 | + | ||
| 105 | + | ### Polish | |
| 106 | + | - [ ] Physical device testing (iOS + Android) | |
| 107 | + | - [ ] Safe area insets on various device models (notched, non-notched) | |
| 108 | + | - [ ] Virtual scroller performance on mobile | |
| 109 | + | - [ ] VoiceOver / TalkBack accessibility | |
| 110 | + | - [ ] Keyboard doesn't obscure inputs in modals | |
| 111 | + | - [ ] Background/foreground transitions work correctly | |
| 112 | + | ||
| 113 | + | ### Platform Features (Future) | |
| 114 | + | - [ ] Push notifications via Tauri plugin | |
| 115 | + | - [ ] Haptic feedback for actions | |
| 116 | + | - [ ] Share extension (receive shared text as task) | |
| 117 | + | - [ ] Widgets (iOS 14+, Android) | |
| 118 | + | - [ ] Biometric unlock (Face ID, fingerprint) | |
| 119 | + | ||
| 120 | + | --- | |
| 121 | + | ||
| 122 | + | ## Key Files | |
| 123 | + | ||
| 124 | + | | File | Role | | |
| 125 | + | |------|------| | |
| 126 | + | | `src-tauri/frontend/css/styles.css` | All responsive CSS (~400 lines of mobile rules) | | |
| 127 | + | | `src-tauri/frontend/js/touch.js` | Gesture utilities (~300 lines) | | |
| 128 | + | | `src-tauri/frontend/js/navigation.js` | Nav dot + radial dial | | |
| 129 | + | | `src-tauri/frontend/js/components.js` | Action sheets, modal swipe-dismiss | | |
| 130 | + | | `src-tauri/frontend/js/day-planning.js` | Tap-to-create, swipe day nav | | |
| 131 | + | | `src-tauri/frontend/js/events.js` | Mobile status indicator, date groups | | |
| 132 | + | | `src-tauri/frontend/js/emails.js` | In-app compose modal | | |
| 133 | + | | `src-tauri/frontend/js/mobile.js` | Swipe, pull-to-refresh, long-press wiring | | |
| 134 | + | | `src-tauri/frontend/js/keyboard.js` | Touch device gate | | |
| 135 | + | | `src-tauri/frontend/index.html` | Nav dot/dial HTML, mobile elements | | |
| 136 | + | | `src-tauri/Cargo.toml` | Desktop-only dep gating | | |
| 137 | + | | `src-tauri/src/main.rs` | Desktop-only plugin/service gating | | |
| 138 | + | | `src-tauri/src/lib.rs` | Desktop-only module gating | | |
| 139 | + | ||
| 140 | + | --- | |
| 141 | + | ||
| 142 | + | ## Running on Mobile | |
| 143 | + | ||
| 144 | + | ```bash | |
| 145 | + | # iOS | |
| 146 | + | cargo tauri ios dev # simulator | |
| 147 | + | cargo tauri ios dev --device "iPhone 15 Pro" # specific simulator | |
| 148 | + | cargo tauri ios dev --device # physical device (needs provisioning) | |
| 149 | + | ||
| 150 | + | # Android | |
| 151 | + | cargo tauri android dev # emulator | |
| 152 | + | cargo tauri android dev --device "Pixel_7" # specific emulator | |
| 153 | + | cargo tauri android dev --device # physical device (USB debugging) | |
| 154 | + | ``` | |
| 155 | + | ||
| 156 | + | **WebView debugging:** | |
| 157 | + | - iOS: Safari → Develop → Simulator → JSContext | |
| 158 | + | - Android: Chrome → `chrome://inspect` → Select WebView | |
| 159 | + | ||
| 160 | + | --- | |
| 161 | + | ||
| 162 | + | ## Known Issues | |
| 163 | + | ||
| 164 | + | **iOS Simulator deploy race condition** — `cargo tauri ios dev` sometimes fails with "Unable to lookup in current state: Shutdown" even though the simulator appears booted. Workaround: boot the simulator manually first (`open -a Simulator && sleep 5`) before running `cargo tauri ios dev`. If it still fails, install/launch manually: | |
| 165 | + | ```bash | |
| 166 | + | xcrun simctl install booted /path/to/GoingsOn.app | |
| 167 | + | xcrun simctl launch booted com.goingson.app | |
| 168 | + | ``` | |
| 169 | + | ||
| 170 | + | **SQLite on Android** — If SQLite fails to load, bundle it: | |
| 171 | + | ```toml | |
| 172 | + | [target.'cfg(target_os = "android")'.dependencies] | |
| 173 | + | rusqlite = { version = "0.31", features = ["bundled"] } | |
| 174 | + | ``` | |
| 175 | + | ||
| 176 | + | **Tauri macOS WebView** — Uses WKWebView with Safari Web Inspector (not Chrome DevTools). No responsive design mode in inspector; resize the window to test mobile breakpoint. | |
| 177 | + | ||
| 178 | + | --- | |
| 179 | + | ||
| 180 | + | ## Release | |
| 181 | + | ||
| 182 | + | ### iOS (TestFlight) | |
| 183 | + | ```bash | |
| 184 | + | cargo tauri ios build --release | |
| 185 | + | # Output: src-tauri/gen/apple/build/ → Open in Xcode to archive and upload | |
| 186 | + | ``` | |
| 187 | + | - [x] Apple Developer account ($99/year) — approved | |
| 188 | + | - [ ] Provisioning profiles configured (distribution profile for `com.goingson.app`) | |
| 189 | + | - [ ] App Store Connect: create app record | |
| 190 | + | - [ ] `cargo tauri ios build --release` — archive in Xcode | |
| 191 | + | - [ ] Upload to TestFlight via Xcode or `xcrun altool` | |
| 192 | + | - [ ] Add beta testers | |
| 193 | + | - [ ] Physical device testing before sending TestFlight invites | |
| 194 | + | ||
| 195 | + | ### Android | |
| 196 | + | ```bash | |
| 197 | + | cargo tauri android build --release --aab | |
| 198 | + | # Output: src-tauri/gen/android/app/build/outputs/ | |
| 199 | + | ``` | |
| 200 | + | - [ ] Google Play Developer account ($25 one-time) | |
| 201 | + | - [ ] `cargo tauri android init` | |
| 202 | + | - [ ] Signing key generated (Android keystore) | |
| 203 | + | - [ ] Play Console listing (title, description, screenshots) | |
| 204 | + | - [ ] Test on emulator + physical device | |
| 205 | + | - [ ] Play Store submission |
| @@ -19,7 +19,7 @@ tauri-build = { workspace = true } | |||
| 19 | 19 | goingson-core = { workspace = true } | |
| 20 | 20 | goingson-db-sqlite = { workspace = true } | |
| 21 | 21 | goingson-plugin-runtime = { workspace = true } | |
| 22 | - | synckit-client = { path = "../../synckit-client" } | |
| 22 | + | synckit-client = { path = "../../../Shared/synckit-client" } | |
| 23 | 23 | ||
| 24 | 24 | # Tauri | |
| 25 | 25 | tauri = { workspace = true, features = ["image-png"] } |
| @@ -38,7 +38,7 @@ | |||
| 38 | 38 | "icons/icon.ico" | |
| 39 | 39 | ], | |
| 40 | 40 | "resources": [ | |
| 41 | - | "../../../themes/*.toml" | |
| 41 | + | "../../../Shared/themes/*.toml" | |
| 42 | 42 | ], | |
| 43 | 43 | "iOS": { | |
| 44 | 44 | "developmentTeam": "93C54W92UP" |