Compare commits

..

10 Commits

Author SHA1 Message Date
ee9b6be95e Temporarily remove web and server from workspace to publish other crates
Some checks failed
Continuous integration / Test Suite (push) Successful in 28s
Continuous integration / Check (push) Successful in 42s
Continuous integration / Trunk (push) Failing after 28s
Continuous integration / Rustfmt (push) Successful in 36s
Continuous integration / build (push) Successful in 27s
2025-02-13 10:16:55 -08:00
38c553d385 Use packaged version of crates 2025-02-13 10:16:36 -08:00
1b073665a7 chore: Release 2025-02-13 09:49:11 -08:00
2076596f50 Rename all crates to start with letterbox- 2025-02-13 09:48:24 -08:00
d1beaded09 Update Cargo.toml for packaging 2025-02-13 09:47:41 -08:00
2562bdfedf server: tool for testing inline code 2025-02-13 09:47:41 -08:00
86c6face7d server: sql to debug search indexing w/ postgres 2025-02-13 09:47:41 -08:00
4a7ff8bf7b notmuch: exclude testdata dir when packaging
Contains filenames cargo package doesn't like
2025-02-13 09:47:41 -08:00
8c280d3616 web: fix styling for slashdot's story byline 2025-02-13 09:47:41 -08:00
eb4d4164ef web: fix progress bar on mobile 2025-02-13 09:47:41 -08:00
19 changed files with 160 additions and 7069 deletions

7077
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,8 @@
[workspace] [workspace]
resolver = "2" resolver = "2"
default-members = ["server"] #default-members = ["server"]
members = ["web", "server", "notmuch", "procmail2notmuch", "shared"] members = ["notmuch", "procmail2notmuch", "shared"]
#members = ["web", "server", "notmuch", "procmail2notmuch", "shared"]
[profile.dev] [profile.dev]
opt-level = 1 opt-level = 1

View File

@ -1,9 +1,13 @@
[package] [package]
name = "notmuch" name = "letterbox-notmuch"
version = "0.0.144" version = "0.1.0"
edition = "2021" edition = "2021"
exclude = ["/testdata"]
description = "Wrapper for calling notmuch cli"
license = "UNLICENSED"
repository = "https://git.z.xinu.tv/wathiede/letterbox"
publish = ["xinu"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
log = "0.4.14" log = "0.4.14"

View File

@ -4,7 +4,7 @@ use std::{
time::Instant, time::Instant,
}; };
use notmuch::Notmuch; use letterbox_notmuch::Notmuch;
use rayon::iter::{ParallelBridge, ParallelIterator}; use rayon::iter::{ParallelBridge, ParallelIterator};
#[test] #[test]

View File

@ -1,7 +1,11 @@
[package] [package]
name = "procmail2notmuch" name = "letterbox-procmail2notmuch"
version = "0.0.144" version = "0.1.0"
edition = "2021" edition = "2021"
description = "Tool for generating notmuch rules from procmail"
license = "UNLICENSED"
repository = "https://git.z.xinu.tv/wathiede/letterbox"
publish = ["xinu"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,8 +1,12 @@
[package] [package]
name = "letterbox-server" name = "letterbox-server"
version = "0.0.144" version = "0.1.0"
edition = "2021" edition = "2021"
default-run = "letterbox-server" default-run = "letterbox-server"
description = "Backend for letterbox"
license = "UNLICENSED"
repository = "https://git.z.xinu.tv/wathiede/letterbox"
publish = ["xinu"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -25,7 +29,6 @@ lol_html = "2.0.0"
mailparse = "0.16.0" mailparse = "0.16.0"
maplit = "1.0.2" maplit = "1.0.2"
memmap = "0.7.0" memmap = "0.7.0"
notmuch = { path = "../notmuch" }
opentelemetry = "0.28.0" opentelemetry = "0.28.0"
regex = "1.11.1" regex = "1.11.1"
reqwest = { version = "0.12.7", features = ["blocking"] } reqwest = { version = "0.12.7", features = ["blocking"] }
@ -34,7 +37,6 @@ rocket_cors = "0.6.0"
scraper = "0.22.0" scraper = "0.22.0"
serde = { version = "1.0.147", features = ["derive"] } serde = { version = "1.0.147", features = ["derive"] }
serde_json = "1.0.87" serde_json = "1.0.87"
shared = { path = "../shared" }
sqlx = { version = "0.8.2", features = ["postgres", "runtime-tokio", "time"] } sqlx = { version = "0.8.2", features = ["postgres", "runtime-tokio", "time"] }
tantivy = { version = "0.22.0", optional = true } tantivy = { version = "0.22.0", optional = true }
thiserror = "2.0.0" thiserror = "2.0.0"
@ -45,6 +47,8 @@ urlencoding = "2.1.3"
#xtracing = { path = "../../xtracing" } #xtracing = { path = "../../xtracing" }
#xtracing = { git = "http://git-private.h.xinu.tv/wathiede/xtracing.git" } #xtracing = { git = "http://git-private.h.xinu.tv/wathiede/xtracing.git" }
xtracing = { version = "0.2.0", registry = "xinu" } xtracing = { version = "0.2.0", registry = "xinu" }
letterbox-notmuch = { version = "0.1.0", registry = "xinu" }
letterbox-shared = { version = "0.1.0", registry = "xinu" }
[build-dependencies] [build-dependencies]
build-info-build = "0.0.39" build-info-build = "0.0.39"

View File

@ -0,0 +1,13 @@
select t.id, tt.tokid, tt.alias, length(t.token), t.token from (
select id, (ts_parse('default',
-- regexp_replace(
-- regexp_replace(summary, '<[^>]+>', ' ', 'g'),
-- '\s+',
-- ' ',
-- 'g'
-- )
summary
)).* from post) t
inner join ts_token_type('default') tt
on t.tokid = tt.tokid
where length(token) >= 2*1024;

View File

@ -0,0 +1,21 @@
use std::fs;
use url::Url;
fn main() -> anyhow::Result<()> {
println!("PWD: {}", std::env::current_dir()?.display());
let _url = "https://slashdot.org/story/25/01/24/1813201/walgreens-replaced-fridge-doors-with-smart-screens-its-now-a-200-million-fiasco?utm_source=rss1.0mainlinkanon&utm_medium=feed";
let _url = "https://hackaday.com/2025/01/24/hackaday-podcast-episode-305-caustic-clocks-practice-bones-and-brick-layers/";
let _url = "https://theonion.com/monster-devastated-to-see-film-depicting-things-he-told-guillermo-del-toro-in-confidence/";
let _url = "https://trofi.github.io/posts/330-another-nix-language-nondeterminism-example.html";
let _url = "https://blog.cloudflare.com/ddos-threat-report-for-2024-q4/";
let url = "https://trofi.github.io/posts/330-another-nix-language-nondeterminism-example.html";
let body = reqwest::blocking::get(url)?.text()?;
let output = "/tmp/h2md/output.html";
let inliner = css_inline::CSSInliner::options()
.base_url(Url::parse(url).ok())
.build();
let inlined = inliner.inline(&body)?;
fs::write(output, inlined)?;
Ok(())
}

View File

@ -8,6 +8,7 @@ use std::{error::Error, io::Cursor, str::FromStr};
use async_graphql::{extensions, http::GraphiQLSource, EmptySubscription, Schema}; use async_graphql::{extensions, http::GraphiQLSource, EmptySubscription, Schema};
use async_graphql_rocket::{GraphQLQuery, GraphQLRequest, GraphQLResponse}; use async_graphql_rocket::{GraphQLQuery, GraphQLRequest, GraphQLResponse};
use cacher::FilesystemCacher; use cacher::FilesystemCacher;
use letterbox_notmuch::{Notmuch, NotmuchError, ThreadSet};
#[cfg(feature = "tantivy")] #[cfg(feature = "tantivy")]
use letterbox_server::tantivy::TantivyConnection; use letterbox_server::tantivy::TantivyConnection;
use letterbox_server::{ use letterbox_server::{
@ -16,7 +17,6 @@ use letterbox_server::{
graphql::{Attachment, GraphqlSchema, Mutation, QueryRoot}, graphql::{Attachment, GraphqlSchema, Mutation, QueryRoot},
nm::{attachment_bytes, cid_attachment_bytes}, nm::{attachment_bytes, cid_attachment_bytes},
}; };
use notmuch::{Notmuch, NotmuchError, ThreadSet};
use rocket::{ use rocket::{
fairing::AdHoc, fairing::AdHoc,
http::{ContentType, Header}, http::{ContentType, Header},
@ -179,7 +179,7 @@ async fn graphql_request(
async fn main() -> Result<(), Box<dyn Error>> { async fn main() -> Result<(), Box<dyn Error>> {
let _guard = xtracing::init(env!("CARGO_BIN_NAME"))?; let _guard = xtracing::init(env!("CARGO_BIN_NAME"))?;
build_info::build_info!(fn bi); build_info::build_info!(fn bi);
info!("Build Info: {}", shared::build_version(bi)); info!("Build Info: {}", letterbox_shared::build_version(bi));
let allowed_origins = AllowedOrigins::all(); let allowed_origins = AllowedOrigins::all();
let cors = rocket_cors::CorsOptions { let cors = rocket_cors::CorsOptions {
allowed_origins, allowed_origins,
@ -195,7 +195,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
let rkt = rocket::build() let rkt = rocket::build()
.mount( .mount(
shared::urls::MOUNT_POINT, letterbox_shared::urls::MOUNT_POINT,
routes![ routes![
original, original,
show_pretty, show_pretty,

View File

@ -10,7 +10,7 @@ use crate::TransformError;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum ServerError { pub enum ServerError {
#[error("notmuch: {0}")] #[error("notmuch: {0}")]
NotmuchError(#[from] notmuch::NotmuchError), NotmuchError(#[from] letterbox_notmuch::NotmuchError),
#[error("flatten")] #[error("flatten")]
FlattenError, FlattenError,
#[error("mail parse error: {0}")] #[error("mail parse error: {0}")]

View File

@ -6,8 +6,8 @@ use async_graphql::{
SimpleObject, Union, SimpleObject, Union,
}; };
use cacher::FilesystemCacher; use cacher::FilesystemCacher;
use letterbox_notmuch::Notmuch;
use log::info; use log::info;
use notmuch::Notmuch;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::postgres::PgPool; use sqlx::postgres::PgPool;
use tokio::join; use tokio::join;
@ -283,7 +283,7 @@ pub struct QueryRoot;
impl QueryRoot { impl QueryRoot {
async fn version<'ctx>(&self, _ctx: &Context<'ctx>) -> Result<String, Error> { async fn version<'ctx>(&self, _ctx: &Context<'ctx>) -> Result<String, Error> {
build_info::build_info!(fn bi); build_info::build_info!(fn bi);
Ok(shared::build_version(bi)) Ok(letterbox_shared::build_version(bi))
} }
#[instrument(skip_all, fields(query=query))] #[instrument(skip_all, fields(query=query))]
#[instrument(skip_all, fields(query=query, request_id=request_id()))] #[instrument(skip_all, fields(query=query, request_id=request_id()))]

View File

@ -2,10 +2,10 @@ use std::collections::HashMap;
use cacher::FilesystemCacher; use cacher::FilesystemCacher;
use futures::{stream::FuturesUnordered, StreamExt}; use futures::{stream::FuturesUnordered, StreamExt};
use letterbox_shared::compute_color;
use log::{error, info}; use log::{error, info};
use maplit::hashmap; use maplit::hashmap;
use scraper::Selector; use scraper::Selector;
use shared::compute_color;
use sqlx::postgres::PgPool; use sqlx::postgres::PgPool;
use tracing::instrument; use tracing::instrument;
use url::Url; use url::Url;

View File

@ -5,10 +5,10 @@ use std::{
time::Instant, time::Instant,
}; };
use letterbox_notmuch::Notmuch;
use log::{error, info, warn}; use log::{error, info, warn};
use mailparse::{parse_content_type, parse_mail, MailHeader, MailHeaderMap, ParsedMail}; use mailparse::{parse_content_type, parse_mail, MailHeader, MailHeaderMap, ParsedMail};
use memmap::MmapOptions; use memmap::MmapOptions;
use notmuch::Notmuch;
use sqlx::PgPool; use sqlx::PgPool;
use tracing::instrument; use tracing::instrument;
@ -43,7 +43,9 @@ pub fn is_notmuch_thread_or_id(id: &str) -> bool {
} }
// TODO(wathiede): decide good error type // TODO(wathiede): decide good error type
pub fn threadset_to_messages(thread_set: notmuch::ThreadSet) -> Result<Vec<Message>, ServerError> { pub fn threadset_to_messages(
thread_set: letterbox_notmuch::ThreadSet,
) -> Result<Vec<Message>, ServerError> {
for t in thread_set.0 { for t in thread_set.0 {
for _tn in t.0 {} for _tn in t.0 {}
} }
@ -190,7 +192,7 @@ pub async fn thread(
.headers .headers
.get_first_value("date") .get_first_value("date")
.and_then(|d| mailparse::dateparse(&d).ok()); .and_then(|d| mailparse::dateparse(&d).ok());
let cid_prefix = shared::urls::cid_prefix(None, &id); let cid_prefix = letterbox_shared::urls::cid_prefix(None, &id);
let base_url = None; let base_url = None;
let mut part_addr = Vec::new(); let mut part_addr = Vec::new();
part_addr.push(id.to_string()); part_addr.push(id.to_string());

View File

@ -1,11 +1,15 @@
[package] [package]
name = "shared" name = "letterbox-shared"
version = "0.0.144" version = "0.1.0"
edition = "2021" edition = "2021"
description = "Shared module for letterbox"
license = "UNLICENSED"
repository = "https://git.z.xinu.tv/wathiede/letterbox"
publish = ["xinu"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
build-info = "0.0.39" build-info = "0.0.39"
notmuch = { path = "../notmuch" } letterbox-notmuch = { version = "0.1.0", registry = "xinu" }
serde = { version = "1.0.147", features = ["derive"] } serde = { version = "1.0.147", features = ["derive"] }

View File

@ -1,7 +1,7 @@
use std::hash::{DefaultHasher, Hash, Hasher}; use std::hash::{DefaultHasher, Hash, Hasher};
use build_info::{BuildInfo, VersionControl}; use build_info::{BuildInfo, VersionControl};
use notmuch::SearchSummary; use letterbox_notmuch::SearchSummary;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]

View File

@ -1,13 +1,12 @@
[package] [package]
version = "0.0.144" version = "0.1.0"
name = "letterbox" name = "letterbox-web"
repository = "https://github.com/seed-rs/seed-quickstart"
authors = ["Bill Thiede <git@xinu.tv>"] authors = ["Bill Thiede <git@xinu.tv>"]
description = "App Description"
categories = ["category"]
license = "MIT"
readme = "./README.md"
edition = "2021" edition = "2021"
description = "Web frontend for letterbox"
license = "UNLICENSED"
repository = "https://git.z.xinu.tv/wathiede/letterbox"
publish = ["xinu"]
[build-dependencies] [build-dependencies]
build-info-build = "0.0.39" build-info-build = "0.0.39"
@ -22,8 +21,6 @@ seed = { version = "0.10.0", features = ["routing"] }
#seed = "0.9.2" #seed = "0.9.2"
console_log = { version = "0.1.0", registry = "xinu" } console_log = { version = "0.1.0", registry = "xinu" }
serde = { version = "1.0.147", features = ["derive"] } serde = { version = "1.0.147", features = ["derive"] }
notmuch = { path = "../notmuch" }
shared = { path = "../shared" }
itertools = "0.14.0" itertools = "0.14.0"
serde_json = { version = "1.0.93", features = ["unbounded_depth"] } serde_json = { version = "1.0.93", features = ["unbounded_depth"] }
chrono = "0.4.31" chrono = "0.4.31"
@ -37,6 +34,8 @@ wasm-bindgen = "=0.2.100"
uuid = { version = "1.13.1", features = [ uuid = { version = "1.13.1", features = [
"js", "js",
] } # direct dep to set js feature, prevents Rng issues ] } # direct dep to set js feature, prevents Rng issues
letterbox-shared = { version = "0.1.0", registry = "xinu" }
letterbox-notmuch = { version = "0.1.0", path = "../notmuch", registry = "xinu" }
[package.metadata.wasm-pack.profile.release] [package.metadata.wasm-pack.profile.release]
wasm-opt = ['-Os'] wasm-opt = ['-Os']

View File

@ -27,7 +27,7 @@ pub fn unread_query() -> &'static str {
// `init` describes what should happen when your app started. // `init` describes what should happen when your app started.
pub fn init(url: Url, orders: &mut impl Orders<Msg>) -> Model { pub fn init(url: Url, orders: &mut impl Orders<Msg>) -> Model {
let version = shared::build_version(bi); let version = letterbox_shared::build_version(bi);
info!("Build Info: {}", version); info!("Build Info: {}", version);
if url.hash().is_none() { if url.hash().is_none() {
orders.request_url(urls::search(unread_query(), 0)); orders.request_url(urls::search(unread_query(), 0));

View File

@ -3,10 +3,10 @@ use std::collections::HashSet;
use chrono::{DateTime, Datelike, Duration, Local, Utc}; use chrono::{DateTime, Datelike, Duration, Local, Utc};
use human_format::{Formatter, Scales}; use human_format::{Formatter, Scales};
use itertools::Itertools; use itertools::Itertools;
use letterbox_shared::compute_color;
use log::{debug, error, info}; use log::{debug, error, info};
use seed::{prelude::*, *}; use seed::{prelude::*, *};
use seed_hooks::{state_access::CloneState, topo, use_state}; use seed_hooks::{state_access::CloneState, topo, use_state};
use shared::compute_color;
use web_sys::HtmlElement; use web_sys::HtmlElement;
use crate::{ use crate::{
@ -87,6 +87,7 @@ pub fn view(model: &Model) -> Node<Msg> {
}; };
div![ div![
C![ C![
"relative",
"flex", "flex",
"flex-wrap-reverse", "flex-wrap-reverse",
"bg-black", "bg-black",
@ -94,14 +95,17 @@ pub fn view(model: &Model) -> Node<Msg> {
"lg:flex-nowrap", "lg:flex-nowrap",
"w-full" "w-full"
], ],
reading_progress(model.read_completion_ratio),
div![ div![
C!["w-full", "lg:w-48", "flex-none", "flex", "flex-col"], C!["w-full", "lg:w-48", "flex-none", "flex", "flex-col"],
tags(model), tags(model),
versions(&model.versions) versions(&model.versions)
], ],
reading_progress(model.read_completion_ratio),
div![ div![
C!["flex-auto", "flex", "flex-col"], // TODO: This "overflow-hidden" is a hack because I can't figure out
// how to prevent the search input box on mobile for growing it's
// parent wider
C!["flex-auto", "flex", "flex-col", "overflow-hidden"],
view_header(&model.query, &model.refreshing_state, true), view_header(&model.query, &model.refreshing_state, true),
content, content,
view_header(&model.query, &model.refreshing_state, false), view_header(&model.query, &model.refreshing_state, false),
@ -693,7 +697,12 @@ fn render_attachements(
let default = "UNKNOWN_FILE".to_string(); let default = "UNKNOWN_FILE".to_string();
let filename = a.filename.as_ref().unwrap_or(&default); let filename = a.filename.as_ref().unwrap_or(&default);
let host = seed::window().location().host().expect("couldn't get host"); let host = seed::window().location().host().expect("couldn't get host");
let url = shared::urls::download_attachment(Some(&host), &a.id, &a.idx, filename); let url = letterbox_shared::urls::download_attachment(
Some(&host),
&a.id,
&a.idx,
filename,
);
let mut fmtr = Formatter::new(); let mut fmtr = Formatter::new();
fmtr.with_separator(" "); fmtr.with_separator(" ");
fmtr.with_scales(Scales::Binary()); fmtr.with_scales(Scales::Binary());
@ -871,7 +880,7 @@ fn view_header(
}; };
let query = Url::decode_uri_component(query).unwrap_or("".to_string()); let query = Url::decode_uri_component(query).unwrap_or("".to_string());
nav![ nav![
C!["flex", "px-4", "pt-4"], C!["flex", "px-4", "pt-4", "overflow-hidden"],
a![ a![
C![IF![is_error => "bg-red-500"], "rounded-r-none"], C![IF![is_error => "bg-red-500"], "rounded-r-none"],
C![&tw_classes::BUTTON], C![&tw_classes::BUTTON],

View File

@ -56,7 +56,14 @@ html {
background-color: initial !important; background-color: initial !important;
} }
.news-post .site-nautilus .article-ad, .news-post.site-nautilus .article-ad,
.news-post .site-nautilus .primis-ad { .news-post.site-nautilus .primis-ad {
display: none !important; display: none !important;
}
.news-post.site-slashdot .story-byline {
display: block !important;
height: initial !important;
overflow: auto !important;
position: static !important;
} }