mirror of
https://github.com/pimalaya/himalaya.git
synced 2026-06-17 21:37:55 +08:00
style: put back color inside table
This commit is contained in:
+212
-6
@@ -30,15 +30,24 @@
|
||||
|
||||
use std::{collections::HashMap, env::temp_dir, path::PathBuf};
|
||||
|
||||
use comfy_table::{presets, ContentArrangement};
|
||||
use comfy_table::{presets, Color as TableColor, ContentArrangement};
|
||||
use crossterm::style::Color;
|
||||
use dirs::download_dir;
|
||||
|
||||
use crate::config::{AccountConfig, ComposerConfig, Config, ReaderConfig, TableArrangementConfig};
|
||||
use crate::config::{
|
||||
AccountConfig, AttachmentListTableConfig, ComposerConfig, Config, EnvelopeListTableConfig,
|
||||
MailboxListTableConfig, ReaderConfig, TableArrangementConfig,
|
||||
};
|
||||
|
||||
const DEFAULT_DATETIME_FMT: &str = "%F %R%:z";
|
||||
const DEFAULT_MAILBOX_ALIAS: &str = "inbox";
|
||||
const DEFAULT_ENVELOPES_LIST_PAGE_SIZE: u32 = 25;
|
||||
|
||||
const DEFAULT_UNSEEN_CHAR: char = '*';
|
||||
const DEFAULT_REPLIED_CHAR: char = 'R';
|
||||
const DEFAULT_FLAGGED_CHAR: char = '!';
|
||||
const DEFAULT_ATTACHMENT_CHAR: char = '@';
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Account {
|
||||
pub downloads_dir: Option<PathBuf>,
|
||||
@@ -49,6 +58,13 @@ pub struct Account {
|
||||
pub datetime_local_tz: Option<bool>,
|
||||
pub envelopes_list_page_size: Option<u32>,
|
||||
|
||||
/// Per-column color + flag glyph overrides for `envelopes list`.
|
||||
pub envelopes_list_table: EnvelopeListTableConfig,
|
||||
/// Per-column color overrides for `mailboxes list`.
|
||||
pub mailboxes_list_table: MailboxListTableConfig,
|
||||
/// Per-column color overrides for `attachments list`.
|
||||
pub attachments_list_table: AttachmentListTableConfig,
|
||||
|
||||
/// Mailbox aliases, keys lowercased. Populated from
|
||||
/// `mailbox.alias` at the global and account levels; account
|
||||
/// entries overwrite same-named global entries.
|
||||
@@ -87,6 +103,19 @@ impl Account {
|
||||
.envelopes_list_page_size
|
||||
.or(self.envelopes_list_page_size),
|
||||
|
||||
envelopes_list_table: merge_envelope_table(
|
||||
self.envelopes_list_table,
|
||||
other.envelopes_list_table,
|
||||
),
|
||||
mailboxes_list_table: merge_mailbox_table(
|
||||
self.mailboxes_list_table,
|
||||
other.mailboxes_list_table,
|
||||
),
|
||||
attachments_list_table: merge_attachment_table(
|
||||
self.attachments_list_table,
|
||||
other.attachments_list_table,
|
||||
),
|
||||
|
||||
mailbox_alias,
|
||||
|
||||
composer,
|
||||
@@ -167,6 +196,175 @@ impl Account {
|
||||
.get(DEFAULT_MAILBOX_ALIAS)
|
||||
.map(String::as_str)
|
||||
}
|
||||
|
||||
// ── envelopes list — flag glyphs ─────────────────────────────────────
|
||||
|
||||
pub fn envelopes_list_table_unseen_char(&self) -> char {
|
||||
self.envelopes_list_table
|
||||
.unseen_char
|
||||
.unwrap_or(DEFAULT_UNSEEN_CHAR)
|
||||
}
|
||||
pub fn envelopes_list_table_replied_char(&self) -> char {
|
||||
self.envelopes_list_table
|
||||
.replied_char
|
||||
.unwrap_or(DEFAULT_REPLIED_CHAR)
|
||||
}
|
||||
pub fn envelopes_list_table_flagged_char(&self) -> char {
|
||||
self.envelopes_list_table
|
||||
.flagged_char
|
||||
.unwrap_or(DEFAULT_FLAGGED_CHAR)
|
||||
}
|
||||
pub fn envelopes_list_table_attachment_char(&self) -> char {
|
||||
self.envelopes_list_table
|
||||
.attachment_char
|
||||
.unwrap_or(DEFAULT_ATTACHMENT_CHAR)
|
||||
}
|
||||
|
||||
// ── envelopes list — column colors ───────────────────────────────────
|
||||
//
|
||||
// Defaults mirror pimalaya-tui v1.2.0
|
||||
// (`ListEnvelopesTableConfig::{id,flags,subject,sender,date}_color`).
|
||||
pub fn envelopes_list_table_id_color(&self) -> TableColor {
|
||||
map_color_or(self.envelopes_list_table.id_color, Color::Red)
|
||||
}
|
||||
pub fn envelopes_list_table_flags_color(&self) -> TableColor {
|
||||
map_color_or(self.envelopes_list_table.flags_color, Color::Reset)
|
||||
}
|
||||
pub fn envelopes_list_table_att_color(&self) -> TableColor {
|
||||
// No v1 precedent for a standalone ATT column (v1 embedded the
|
||||
// attachment glyph inside FLAGS); leave it neutral.
|
||||
map_color_or(self.envelopes_list_table.att_color, Color::Reset)
|
||||
}
|
||||
pub fn envelopes_list_table_subject_color(&self) -> TableColor {
|
||||
map_color_or(self.envelopes_list_table.subject_color, Color::Green)
|
||||
}
|
||||
pub fn envelopes_list_table_from_color(&self) -> TableColor {
|
||||
map_color_or(self.envelopes_list_table.from_color, Color::Blue)
|
||||
}
|
||||
pub fn envelopes_list_table_to_color(&self) -> TableColor {
|
||||
// `to` mirrors `from`'s default; v1 didn't surface a TO column.
|
||||
map_color_or(self.envelopes_list_table.to_color, Color::Blue)
|
||||
}
|
||||
pub fn envelopes_list_table_date_color(&self) -> TableColor {
|
||||
map_color_or(self.envelopes_list_table.date_color, Color::DarkYellow)
|
||||
}
|
||||
pub fn envelopes_list_table_size_color(&self) -> TableColor {
|
||||
// New in v2, no v1 precedent.
|
||||
map_color_or(self.envelopes_list_table.size_color, Color::Reset)
|
||||
}
|
||||
|
||||
// ── mailboxes list — column colors ───────────────────────────────────
|
||||
//
|
||||
// `name` matches the v1 `folder.list.table.name-color` default
|
||||
// (`pimalaya-tui::ListFoldersTableConfig::name_color`); the other
|
||||
// columns are new in v2.
|
||||
pub fn mailboxes_list_table_id_color(&self) -> TableColor {
|
||||
map_color_or(self.mailboxes_list_table.id_color, Color::Reset)
|
||||
}
|
||||
pub fn mailboxes_list_table_name_color(&self) -> TableColor {
|
||||
map_color_or(self.mailboxes_list_table.name_color, Color::Blue)
|
||||
}
|
||||
pub fn mailboxes_list_table_total_color(&self) -> TableColor {
|
||||
map_color_or(self.mailboxes_list_table.total_color, Color::Reset)
|
||||
}
|
||||
pub fn mailboxes_list_table_unread_color(&self) -> TableColor {
|
||||
map_color_or(self.mailboxes_list_table.unread_color, Color::Reset)
|
||||
}
|
||||
|
||||
// ── attachments list — column colors ─────────────────────────────────
|
||||
//
|
||||
// No v1 precedent; defaults left neutral.
|
||||
pub fn attachments_list_table_id_color(&self) -> TableColor {
|
||||
map_color_or(self.attachments_list_table.id_color, Color::Reset)
|
||||
}
|
||||
pub fn attachments_list_table_filename_color(&self) -> TableColor {
|
||||
map_color_or(self.attachments_list_table.filename_color, Color::Reset)
|
||||
}
|
||||
pub fn attachments_list_table_type_color(&self) -> TableColor {
|
||||
map_color_or(self.attachments_list_table.type_color, Color::Reset)
|
||||
}
|
||||
pub fn attachments_list_table_size_color(&self) -> TableColor {
|
||||
map_color_or(self.attachments_list_table.size_color, Color::Reset)
|
||||
}
|
||||
pub fn attachments_list_table_inline_color(&self) -> TableColor {
|
||||
map_color_or(self.attachments_list_table.inline_color, Color::Reset)
|
||||
}
|
||||
pub fn attachments_list_table_path_color(&self) -> TableColor {
|
||||
map_color_or(self.attachments_list_table.path_color, Color::Reset)
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a [`crossterm::style::Color`] (deserialized from TOML) into a
|
||||
/// [`comfy_table::Color`] used by the renderers, substituting
|
||||
/// `fallback` when the TOML field is unset.
|
||||
pub(crate) fn map_color_or(color: Option<Color>, fallback: Color) -> TableColor {
|
||||
match color.unwrap_or(fallback) {
|
||||
Color::Reset => TableColor::Reset,
|
||||
Color::Black => TableColor::Black,
|
||||
Color::DarkGrey => TableColor::DarkGrey,
|
||||
Color::Red => TableColor::Red,
|
||||
Color::DarkRed => TableColor::DarkRed,
|
||||
Color::Green => TableColor::Green,
|
||||
Color::DarkGreen => TableColor::DarkGreen,
|
||||
Color::Yellow => TableColor::Yellow,
|
||||
Color::DarkYellow => TableColor::DarkYellow,
|
||||
Color::Blue => TableColor::Blue,
|
||||
Color::DarkBlue => TableColor::DarkBlue,
|
||||
Color::Magenta => TableColor::Magenta,
|
||||
Color::DarkMagenta => TableColor::DarkMagenta,
|
||||
Color::Cyan => TableColor::Cyan,
|
||||
Color::DarkCyan => TableColor::DarkCyan,
|
||||
Color::White => TableColor::White,
|
||||
Color::Grey => TableColor::Grey,
|
||||
Color::Rgb { r, g, b } => TableColor::Rgb { r, g, b },
|
||||
Color::AnsiValue(n) => TableColor::AnsiValue(n),
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_envelope_table(
|
||||
base: EnvelopeListTableConfig,
|
||||
over: EnvelopeListTableConfig,
|
||||
) -> EnvelopeListTableConfig {
|
||||
EnvelopeListTableConfig {
|
||||
unseen_char: over.unseen_char.or(base.unseen_char),
|
||||
replied_char: over.replied_char.or(base.replied_char),
|
||||
flagged_char: over.flagged_char.or(base.flagged_char),
|
||||
attachment_char: over.attachment_char.or(base.attachment_char),
|
||||
id_color: over.id_color.or(base.id_color),
|
||||
flags_color: over.flags_color.or(base.flags_color),
|
||||
att_color: over.att_color.or(base.att_color),
|
||||
subject_color: over.subject_color.or(base.subject_color),
|
||||
from_color: over.from_color.or(base.from_color),
|
||||
to_color: over.to_color.or(base.to_color),
|
||||
date_color: over.date_color.or(base.date_color),
|
||||
size_color: over.size_color.or(base.size_color),
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_mailbox_table(
|
||||
base: MailboxListTableConfig,
|
||||
over: MailboxListTableConfig,
|
||||
) -> MailboxListTableConfig {
|
||||
MailboxListTableConfig {
|
||||
id_color: over.id_color.or(base.id_color),
|
||||
name_color: over.name_color.or(base.name_color),
|
||||
total_color: over.total_color.or(base.total_color),
|
||||
unread_color: over.unread_color.or(base.unread_color),
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_attachment_table(
|
||||
base: AttachmentListTableConfig,
|
||||
over: AttachmentListTableConfig,
|
||||
) -> AttachmentListTableConfig {
|
||||
AttachmentListTableConfig {
|
||||
id_color: over.id_color.or(base.id_color),
|
||||
filename_color: over.filename_color.or(base.filename_color),
|
||||
type_color: over.type_color.or(base.type_color),
|
||||
size_color: over.size_color.or(base.size_color),
|
||||
inline_color: over.inline_color.or(base.inline_color),
|
||||
path_color: over.path_color.or(base.path_color),
|
||||
}
|
||||
}
|
||||
|
||||
/// Lowercases every key of `aliases`, leaving values untouched. Used at
|
||||
@@ -184,13 +382,17 @@ impl From<Config> for Account {
|
||||
fn from(config: Config) -> Self {
|
||||
Self {
|
||||
downloads_dir: config.downloads_dir,
|
||||
table_preset: config.table_preset,
|
||||
table_arrangement: config.table_arrangement,
|
||||
table_preset: config.table.preset,
|
||||
table_arrangement: config.table.arrangement,
|
||||
|
||||
datetime_fmt: config.envelope.list.datetime_fmt,
|
||||
datetime_local_tz: config.envelope.list.datetime_local_tz,
|
||||
envelopes_list_page_size: config.envelope.list.page_size,
|
||||
|
||||
envelopes_list_table: config.envelope.list.table,
|
||||
mailboxes_list_table: config.mailbox.list.table,
|
||||
attachments_list_table: config.attachment.list.table,
|
||||
|
||||
mailbox_alias: lowercase_alias_keys(config.mailbox.alias),
|
||||
|
||||
composer: config.message.composer,
|
||||
@@ -203,13 +405,17 @@ impl From<AccountConfig> for Account {
|
||||
fn from(config: AccountConfig) -> Self {
|
||||
Self {
|
||||
downloads_dir: config.downloads_dir,
|
||||
table_preset: config.table_preset,
|
||||
table_arrangement: config.table_arrangement,
|
||||
table_preset: config.table.preset,
|
||||
table_arrangement: config.table.arrangement,
|
||||
|
||||
datetime_fmt: config.envelope.list.datetime_fmt,
|
||||
datetime_local_tz: config.envelope.list.datetime_local_tz,
|
||||
envelopes_list_page_size: config.envelope.list.page_size,
|
||||
|
||||
envelopes_list_table: config.envelope.list.table,
|
||||
mailboxes_list_table: config.mailbox.list.table,
|
||||
attachments_list_table: config.attachment.list.table,
|
||||
|
||||
mailbox_alias: lowercase_alias_keys(config.mailbox.alias),
|
||||
|
||||
composer: HashMap::new(),
|
||||
|
||||
+33
-7
@@ -19,12 +19,16 @@ use std::{fmt, path::PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use comfy_table::{Cell, ContentArrangement, Row, Table};
|
||||
use comfy_table::{Cell, Color, ContentArrangement, Row, Table};
|
||||
use crossterm::style::Color as CrosstermColor;
|
||||
use pimalaya_cli::printer::Printer;
|
||||
use pimalaya_config::toml::TomlConfig;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::config::{AccountConfig, Config, TableArrangementConfig};
|
||||
use crate::{
|
||||
account::context::map_color_or,
|
||||
config::{AccountConfig, Config, TableArrangementConfig},
|
||||
};
|
||||
|
||||
/// List all accounts declared in the configuration.
|
||||
///
|
||||
@@ -38,15 +42,25 @@ impl AccountListCommand {
|
||||
let config = load_config(config_paths)?;
|
||||
|
||||
let preset = config
|
||||
.table_preset
|
||||
.table
|
||||
.preset
|
||||
.clone()
|
||||
.unwrap_or_else(|| comfy_table::presets::UTF8_FULL_CONDENSED.to_string());
|
||||
let arrangement = config
|
||||
.table_arrangement
|
||||
.table
|
||||
.arrangement
|
||||
.clone()
|
||||
.unwrap_or(TableArrangementConfig::Dynamic)
|
||||
.into();
|
||||
|
||||
let table_cfg = &config.account.list.table;
|
||||
let colors = AccountColors {
|
||||
// v1.2.0 defaults: name=Green, backends=Blue, default=Reset.
|
||||
name: map_color_or(table_cfg.name_color, CrosstermColor::Green),
|
||||
backends: map_color_or(table_cfg.backends_color, CrosstermColor::Blue),
|
||||
default: map_color_or(table_cfg.default_color, CrosstermColor::Reset),
|
||||
};
|
||||
|
||||
let mut accounts: Vec<AccountRow> = config
|
||||
.accounts
|
||||
.iter()
|
||||
@@ -57,6 +71,7 @@ impl AccountListCommand {
|
||||
let table = AccountsTable {
|
||||
preset,
|
||||
arrangement,
|
||||
colors,
|
||||
accounts,
|
||||
};
|
||||
|
||||
@@ -64,6 +79,13 @@ impl AccountListCommand {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct AccountColors {
|
||||
name: Color,
|
||||
backends: Color,
|
||||
default: Color,
|
||||
}
|
||||
|
||||
fn load_config(paths: &[PathBuf]) -> Result<Config> {
|
||||
match Config::from_paths_or_default(paths)? {
|
||||
Some(config) => Ok(config),
|
||||
@@ -111,6 +133,8 @@ pub struct AccountsTable {
|
||||
pub preset: String,
|
||||
#[serde(skip)]
|
||||
pub arrangement: ContentArrangement,
|
||||
#[serde(skip)]
|
||||
colors: AccountColors,
|
||||
pub accounts: Vec<AccountRow>,
|
||||
}
|
||||
|
||||
@@ -129,9 +153,11 @@ impl fmt::Display for AccountsTable {
|
||||
.add_rows(self.accounts.iter().map(|account| {
|
||||
let mut row = Row::new();
|
||||
row.max_height(1);
|
||||
row.add_cell(Cell::new(&account.name));
|
||||
row.add_cell(Cell::new(account.backends.join(", ")));
|
||||
row.add_cell(Cell::new(if account.default { "yes" } else { "" }));
|
||||
row.add_cell(Cell::new(&account.name).fg(self.colors.name));
|
||||
row.add_cell(Cell::new(account.backends.join(", ")).fg(self.colors.backends));
|
||||
row.add_cell(
|
||||
Cell::new(if account.default { "yes" } else { "" }).fg(self.colors.default),
|
||||
);
|
||||
row
|
||||
}));
|
||||
|
||||
|
||||
+141
-12
@@ -19,6 +19,7 @@ use std::{collections::HashMap, fs, path::Path, path::PathBuf};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use comfy_table::ContentArrangement;
|
||||
use crossterm::style::Color;
|
||||
use pimalaya_config::{
|
||||
secret::Secret,
|
||||
toml::{shell_expanded_string, TomlConfig},
|
||||
@@ -38,14 +39,20 @@ use serde::{Deserialize, Serialize};
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
pub struct Config {
|
||||
pub downloads_dir: Option<PathBuf>,
|
||||
pub table_preset: Option<String>,
|
||||
pub table_arrangement: Option<TableArrangementConfig>,
|
||||
#[serde(default)]
|
||||
pub table: TableConfig,
|
||||
#[serde(default)]
|
||||
pub envelope: EnvelopeConfig,
|
||||
#[serde(default)]
|
||||
pub mailbox: MailboxConfig,
|
||||
#[serde(default)]
|
||||
pub message: MessageConfig,
|
||||
#[serde(default)]
|
||||
pub attachment: AttachmentConfig,
|
||||
/// `account list` rendering options (global only — there is no
|
||||
/// per-account override for the listing of accounts).
|
||||
#[serde(default)]
|
||||
pub account: AccountListingConfig,
|
||||
pub accounts: HashMap<String, AccountConfig>,
|
||||
}
|
||||
|
||||
@@ -98,8 +105,8 @@ pub struct AccountConfig {
|
||||
pub default: bool,
|
||||
|
||||
pub downloads_dir: Option<PathBuf>,
|
||||
pub table_preset: Option<String>,
|
||||
pub table_arrangement: Option<TableArrangementConfig>,
|
||||
#[serde(default)]
|
||||
pub table: TableConfig,
|
||||
|
||||
#[serde(default)]
|
||||
pub envelope: EnvelopeConfig,
|
||||
@@ -107,6 +114,9 @@ pub struct AccountConfig {
|
||||
#[serde(default)]
|
||||
pub mailbox: MailboxConfig,
|
||||
|
||||
#[serde(default)]
|
||||
pub attachment: AttachmentConfig,
|
||||
|
||||
#[allow(unused)]
|
||||
pub imap: Option<ImapConfig>,
|
||||
#[allow(unused)]
|
||||
@@ -127,21 +137,88 @@ pub struct EnvelopeConfig {
|
||||
|
||||
/// Mailbox-level configuration.
|
||||
///
|
||||
/// Currently exposes user-defined aliases mapping a friendly name to a
|
||||
/// backend-native id. Alias names are looked up case-insensitively at
|
||||
/// resolution time, so `INBOX`, `Inbox` and `inbox` all hit the same
|
||||
/// entry. Ids are stored verbatim. The entry `inbox` (case-insensitive)
|
||||
/// acts as the implicit default mailbox when a shared command omits
|
||||
/// `-m/--mailbox`.
|
||||
/// Exposes user-defined aliases mapping a friendly name to a
|
||||
/// backend-native id (looked up case-insensitively at resolution
|
||||
/// time; the `inbox` alias acts as the implicit default mailbox when
|
||||
/// a shared command omits `-m/--mailbox`) and the `mailboxes list`
|
||||
/// rendering options.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
pub struct MailboxConfig {
|
||||
#[serde(default, alias = "aliases")]
|
||||
pub alias: HashMap<String, String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub list: MailboxListConfig,
|
||||
}
|
||||
|
||||
/// `envelopes list` rendering options. Mirrors the pre-v2
|
||||
/// `envelope.list.*` keys.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
pub struct MailboxListConfig {
|
||||
#[serde(default)]
|
||||
pub table: MailboxListTableConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
pub struct MailboxListTableConfig {
|
||||
pub id_color: Option<Color>,
|
||||
pub name_color: Option<Color>,
|
||||
pub total_color: Option<Color>,
|
||||
pub unread_color: Option<Color>,
|
||||
}
|
||||
|
||||
/// `attachments list` rendering options.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
pub struct AttachmentConfig {
|
||||
#[serde(default)]
|
||||
pub list: AttachmentListConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
pub struct AttachmentListConfig {
|
||||
#[serde(default)]
|
||||
pub table: AttachmentListTableConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
pub struct AttachmentListTableConfig {
|
||||
pub id_color: Option<Color>,
|
||||
pub filename_color: Option<Color>,
|
||||
pub type_color: Option<Color>,
|
||||
pub size_color: Option<Color>,
|
||||
pub inline_color: Option<Color>,
|
||||
pub path_color: Option<Color>,
|
||||
}
|
||||
|
||||
/// `account list` rendering options. Top-level only — there is no
|
||||
/// per-account override.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
pub struct AccountListingConfig {
|
||||
#[serde(default)]
|
||||
pub list: AccountListingListConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
pub struct AccountListingListConfig {
|
||||
#[serde(default)]
|
||||
pub table: AccountListingTableConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
pub struct AccountListingTableConfig {
|
||||
pub name_color: Option<Color>,
|
||||
pub backends_color: Option<Color>,
|
||||
pub default_color: Option<Color>,
|
||||
}
|
||||
|
||||
/// `envelopes list` rendering options under `envelope.list.*`.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
pub struct EnvelopeListConfig {
|
||||
@@ -159,6 +236,43 @@ pub struct EnvelopeListConfig {
|
||||
/// flag wins when passed; otherwise the merged account/global
|
||||
/// config wins; otherwise the hard fallback (25) is used.
|
||||
pub page_size: Option<u32>,
|
||||
|
||||
/// Per-column color overrides + flag glyph customization for the
|
||||
/// rendered envelopes table. Keys mirror the v1.2.0 layout
|
||||
/// (`envelope.list.table.id-color`, `envelope.list.table.unseen-char`,
|
||||
/// etc.). Color values accept either a named [crossterm color]
|
||||
/// (`"red"`, `"dark-magenta"`, …) or an `{ Rgb = { r = .., g = ..,
|
||||
/// b = .. } }`/`{ AnsiValue = N }` table.
|
||||
///
|
||||
/// [crossterm color]: https://docs.rs/crossterm/latest/crossterm/style/enum.Color.html
|
||||
#[serde(default)]
|
||||
pub table: EnvelopeListTableConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
pub struct EnvelopeListTableConfig {
|
||||
/// Single character used in the FLAGS column for messages that
|
||||
/// lack `\Seen`. Defaults to `*` (v1.2.0 default).
|
||||
pub unseen_char: Option<char>,
|
||||
/// Single character used in the FLAGS column for messages with
|
||||
/// `\Answered`. Defaults to `R`.
|
||||
pub replied_char: Option<char>,
|
||||
/// Single character used in the FLAGS column for messages with
|
||||
/// `\Flagged`. Defaults to `!`.
|
||||
pub flagged_char: Option<char>,
|
||||
/// Single character used in the ATT column for messages with at
|
||||
/// least one attachment. Defaults to `@`.
|
||||
pub attachment_char: Option<char>,
|
||||
|
||||
pub id_color: Option<Color>,
|
||||
pub flags_color: Option<Color>,
|
||||
pub att_color: Option<Color>,
|
||||
pub subject_color: Option<Color>,
|
||||
pub from_color: Option<Color>,
|
||||
pub to_color: Option<Color>,
|
||||
pub date_color: Option<Color>,
|
||||
pub size_color: Option<Color>,
|
||||
}
|
||||
|
||||
/// Message-level configuration: user-defined composers and readers.
|
||||
@@ -211,6 +325,21 @@ pub struct ReaderConfig {
|
||||
pub default: bool,
|
||||
}
|
||||
|
||||
/// Global / per-account table rendering knobs shared across every list
|
||||
/// command (envelopes, mailboxes, attachments). The per-column color
|
||||
/// blocks live under `*.list.table.*-color` (see [`EnvelopeListTableConfig`]
|
||||
/// & co.).
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
pub struct TableConfig {
|
||||
/// `comfy_table` preset string (chars for borders / corners /
|
||||
/// separators). Defaults to `UTF8_FULL_CONDENSED`. See
|
||||
/// <https://docs.rs/comfy-table/latest/comfy_table/presets/>.
|
||||
pub preset: Option<String>,
|
||||
/// Column-arrangement strategy. Defaults to `Dynamic`.
|
||||
pub arrangement: Option<TableArrangementConfig>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
pub enum TableArrangementConfig {
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::{collections::BTreeMap, fmt, num::NonZeroU32};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use clap::Parser;
|
||||
use comfy_table::{Cell, ContentArrangement, Row, Table};
|
||||
use comfy_table::{Cell, Color, ContentArrangement, Row, Table};
|
||||
use io_imap::types::{
|
||||
core::Vec1,
|
||||
envelope::Address,
|
||||
@@ -102,6 +102,12 @@ impl ImapEnvelopeListCommand {
|
||||
let table = EnvelopesTable {
|
||||
preset: client.account.table_preset().to_string(),
|
||||
arrangement: client.account.table_arrangement(),
|
||||
colors: EnvelopeColors {
|
||||
id: client.account.envelopes_list_table_id_color(),
|
||||
subject: client.account.envelopes_list_table_subject_color(),
|
||||
from: client.account.envelopes_list_table_from_color(),
|
||||
date: client.account.envelopes_list_table_date_color(),
|
||||
},
|
||||
envelopes: map_envelopes_table_entries(data),
|
||||
};
|
||||
|
||||
@@ -109,12 +115,22 @@ impl ImapEnvelopeListCommand {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct EnvelopeColors {
|
||||
id: Color,
|
||||
subject: Color,
|
||||
from: Color,
|
||||
date: Color,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct EnvelopesTable {
|
||||
#[serde(skip)]
|
||||
preset: String,
|
||||
#[serde(skip)]
|
||||
arrangement: ContentArrangement,
|
||||
#[serde(skip)]
|
||||
colors: EnvelopeColors,
|
||||
envelopes: Vec<EnvelopesTableEntry>,
|
||||
}
|
||||
|
||||
@@ -136,11 +152,13 @@ impl fmt::Display for EnvelopesTable {
|
||||
for entry in &self.envelopes {
|
||||
let mut row = Row::new();
|
||||
row.max_height(1);
|
||||
row.add_cell(Cell::new(entry.seq));
|
||||
row.add_cell(Cell::new(entry.uid));
|
||||
row.add_cell(Cell::new(&entry.subject));
|
||||
row.add_cell(Cell::new(&entry.from));
|
||||
row.add_cell(Cell::new(&entry.date));
|
||||
// SEQ and UID share the same `id_color` since both are
|
||||
// protocol-side identifiers for the row.
|
||||
row.add_cell(Cell::new(entry.seq).fg(self.colors.id));
|
||||
row.add_cell(Cell::new(entry.uid).fg(self.colors.id));
|
||||
row.add_cell(Cell::new(&entry.subject).fg(self.colors.subject));
|
||||
row.add_cell(Cell::new(&entry.from).fg(self.colors.from));
|
||||
row.add_cell(Cell::new(&entry.date).fg(self.colors.date));
|
||||
table.add_row(row);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::fmt;
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use clap::Parser;
|
||||
use comfy_table::{Cell, ContentArrangement, Row, Table};
|
||||
use comfy_table::{Cell, Color, ContentArrangement, Row, Table};
|
||||
use io_imap::types::{
|
||||
core::{AString, Vec1},
|
||||
datetime::NaiveDate,
|
||||
@@ -88,6 +88,7 @@ impl ImapEnvelopeSearchCommand {
|
||||
let table = SearchTable {
|
||||
preset: client.account.table_preset().to_string(),
|
||||
arrangement: client.account.table_arrangement(),
|
||||
id_color: client.account.envelopes_list_table_id_color(),
|
||||
ids: ids
|
||||
.into_iter()
|
||||
.map(|id| SearchResult { id: id.get() })
|
||||
@@ -110,6 +111,8 @@ pub struct SearchTable {
|
||||
preset: String,
|
||||
#[serde(skip)]
|
||||
arrangement: ContentArrangement,
|
||||
#[serde(skip)]
|
||||
id_color: Color,
|
||||
uid_mode: bool,
|
||||
ids: Vec<SearchResult>,
|
||||
}
|
||||
@@ -126,7 +129,7 @@ impl fmt::Display for SearchTable {
|
||||
.set_header(Row::from([Cell::new(id_header)]));
|
||||
|
||||
for result in &self.ids {
|
||||
table.add_row(Row::from([Cell::new(result.id)]));
|
||||
table.add_row(Row::from([Cell::new(result.id).fg(self.id_color)]));
|
||||
}
|
||||
|
||||
writeln!(f)?;
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::fmt;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use clap::Parser;
|
||||
use comfy_table::{presets, Cell, ContentArrangement, Row, Table};
|
||||
use comfy_table::{presets, Cell, Color, ContentArrangement, Row, Table};
|
||||
use io_imap::types::{
|
||||
core::Vec1,
|
||||
extensions::sort::{SortCriterion, SortKey},
|
||||
@@ -82,7 +82,8 @@ impl ImapEnvelopeSortCommand {
|
||||
|
||||
let ids = client.sort(sort_criteria, search_criteria, !self.seq)?;
|
||||
|
||||
let table = SortResultsTable::new(ids, !self.seq);
|
||||
let id_color = client.account.envelopes_list_table_id_color();
|
||||
let table = SortResultsTable::new(ids, !self.seq, id_color);
|
||||
|
||||
printer.out(table)?;
|
||||
Ok(())
|
||||
@@ -108,12 +109,18 @@ fn parse_sort_key(s: &str) -> Result<SortKey> {
|
||||
pub struct SortResultsTable {
|
||||
ids: Vec<u32>,
|
||||
uid_mode: bool,
|
||||
#[serde(skip)]
|
||||
id_color: Color,
|
||||
}
|
||||
|
||||
impl SortResultsTable {
|
||||
pub fn new(ids: Vec<std::num::NonZeroU32>, uid_mode: bool) -> Self {
|
||||
pub fn new(ids: Vec<std::num::NonZeroU32>, uid_mode: bool, id_color: Color) -> Self {
|
||||
let ids = ids.into_iter().map(|id| id.get()).collect();
|
||||
Self { ids, uid_mode }
|
||||
Self {
|
||||
ids,
|
||||
uid_mode,
|
||||
id_color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +136,7 @@ impl fmt::Display for SortResultsTable {
|
||||
.set_header(Row::from([Cell::new(id_header)]));
|
||||
|
||||
for id in &self.ids {
|
||||
table.add_row(Row::from([Cell::new(id)]));
|
||||
table.add_row(Row::from([Cell::new(id).fg(self.id_color)]));
|
||||
}
|
||||
|
||||
writeln!(f)?;
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::fmt;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use comfy_table::{Cell, Row, Table};
|
||||
use comfy_table::{Cell, Color, Row, Table};
|
||||
use io_email::mailbox::MailboxRole;
|
||||
use io_imap::types::{core::QuotedChar, flag::FlagNameAttribute, mailbox::Mailbox};
|
||||
use pimalaya_cli::printer::Printer;
|
||||
@@ -60,6 +60,7 @@ impl ImapMailboxListCommand {
|
||||
|
||||
let table = MailboxesTable {
|
||||
preset: client.account.table_preset().to_string(),
|
||||
name_color: client.account.mailboxes_list_table_name_color(),
|
||||
mailboxes: mailboxes.into_iter().map(From::from).collect(),
|
||||
};
|
||||
|
||||
@@ -67,10 +68,12 @@ impl ImapMailboxListCommand {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize)]
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct MailboxesTable {
|
||||
#[serde(skip)]
|
||||
pub preset: String,
|
||||
#[serde(skip)]
|
||||
pub name_color: Color,
|
||||
pub mailboxes: Vec<MailboxRow>,
|
||||
}
|
||||
|
||||
@@ -99,7 +102,7 @@ impl fmt::Display for MailboxesTable {
|
||||
.unwrap_or_default();
|
||||
|
||||
row.max_height(1)
|
||||
.add_cell(Cell::new(&mbox.name))
|
||||
.add_cell(Cell::new(&mbox.name).fg(self.name_color))
|
||||
.add_cell(Cell::new(&mbox.delimiter))
|
||||
.add_cell(Cell::new(role))
|
||||
.add_cell(Cell::new(mbox.attributes.join(", ")));
|
||||
|
||||
+16
-1
@@ -20,7 +20,10 @@ use clap::Parser;
|
||||
use log::warn;
|
||||
use pimalaya_cli::printer::Printer;
|
||||
|
||||
use crate::jmap::{client::JmapClient, email::query::EmailsTable};
|
||||
use crate::jmap::{
|
||||
client::JmapClient,
|
||||
email::query::{EmailsChars, EmailsColors, EmailsTable},
|
||||
};
|
||||
|
||||
/// Get JMAP emails by ID (Email/get).
|
||||
///
|
||||
@@ -43,6 +46,18 @@ impl JmapEmailGetCommand {
|
||||
let table = EmailsTable {
|
||||
preset: client.account.table_preset().to_string(),
|
||||
arrangement: client.account.table_arrangement(),
|
||||
colors: EmailsColors {
|
||||
id: client.account.envelopes_list_table_id_color(),
|
||||
flags: client.account.envelopes_list_table_flags_color(),
|
||||
subject: client.account.envelopes_list_table_subject_color(),
|
||||
from: client.account.envelopes_list_table_from_color(),
|
||||
date: client.account.envelopes_list_table_date_color(),
|
||||
},
|
||||
chars: EmailsChars {
|
||||
unseen: client.account.envelopes_list_table_unseen_char(),
|
||||
flagged: client.account.envelopes_list_table_flagged_char(),
|
||||
attachment: client.account.envelopes_list_table_attachment_char(),
|
||||
},
|
||||
emails: output.emails,
|
||||
};
|
||||
|
||||
|
||||
+44
-11
@@ -19,7 +19,7 @@ use std::fmt;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, ValueEnum};
|
||||
use comfy_table::{Cell, ContentArrangement, Row, Table};
|
||||
use comfy_table::{Cell, Color, ContentArrangement, Row, Table};
|
||||
use io_jmap::rfc8621::email::{
|
||||
Email, EmailAddress, EmailComparator, EmailFilter, EmailSortProperty,
|
||||
};
|
||||
@@ -165,6 +165,18 @@ impl JmapEmailQueryCommand {
|
||||
let table = EmailsTable {
|
||||
preset: client.account.table_preset().to_string(),
|
||||
arrangement: client.account.table_arrangement(),
|
||||
colors: EmailsColors {
|
||||
id: client.account.envelopes_list_table_id_color(),
|
||||
flags: client.account.envelopes_list_table_flags_color(),
|
||||
subject: client.account.envelopes_list_table_subject_color(),
|
||||
from: client.account.envelopes_list_table_from_color(),
|
||||
date: client.account.envelopes_list_table_date_color(),
|
||||
},
|
||||
chars: EmailsChars {
|
||||
unseen: client.account.envelopes_list_table_unseen_char(),
|
||||
flagged: client.account.envelopes_list_table_flagged_char(),
|
||||
attachment: client.account.envelopes_list_table_attachment_char(),
|
||||
},
|
||||
emails: output.emails,
|
||||
};
|
||||
|
||||
@@ -172,12 +184,32 @@ impl JmapEmailQueryCommand {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct EmailsColors {
|
||||
pub id: Color,
|
||||
pub flags: Color,
|
||||
pub subject: Color,
|
||||
pub from: Color,
|
||||
pub date: Color,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct EmailsChars {
|
||||
pub unseen: char,
|
||||
pub flagged: char,
|
||||
pub attachment: char,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct EmailsTable {
|
||||
#[serde(skip)]
|
||||
pub preset: String,
|
||||
#[serde(skip)]
|
||||
pub arrangement: ContentArrangement,
|
||||
#[serde(skip)]
|
||||
pub colors: EmailsColors,
|
||||
#[serde(skip)]
|
||||
pub chars: EmailsChars,
|
||||
pub emails: Vec<Email>,
|
||||
}
|
||||
|
||||
@@ -200,24 +232,25 @@ impl fmt::Display for EmailsTable {
|
||||
let mut flags = String::new();
|
||||
let kw = e.keywords.as_ref();
|
||||
if !kw.and_then(|k| k.get("$seen")).copied().unwrap_or(false) {
|
||||
flags.push('U');
|
||||
flags.push(self.chars.unseen);
|
||||
}
|
||||
if kw.and_then(|k| k.get("$flagged")).copied().unwrap_or(false) {
|
||||
flags.push('F');
|
||||
flags.push(self.chars.flagged);
|
||||
}
|
||||
if e.has_attachment.unwrap_or(false) {
|
||||
flags.push('A');
|
||||
flags.push(self.chars.attachment);
|
||||
}
|
||||
|
||||
let mut row = Row::new();
|
||||
row.max_height(1);
|
||||
row.add_cell(Cell::new(e.id.as_deref().unwrap_or("")));
|
||||
row.add_cell(Cell::new(&flags));
|
||||
row.add_cell(Cell::new(e.subject.as_deref().unwrap_or("")));
|
||||
row.add_cell(Cell::new(format_addresses(
|
||||
e.from.as_deref().unwrap_or(&[]),
|
||||
)));
|
||||
row.add_cell(Cell::new(e.received_at.as_deref().unwrap_or("")));
|
||||
row.add_cell(Cell::new(e.id.as_deref().unwrap_or("")).fg(self.colors.id));
|
||||
row.add_cell(Cell::new(&flags).fg(self.colors.flags));
|
||||
row.add_cell(Cell::new(e.subject.as_deref().unwrap_or("")).fg(self.colors.subject));
|
||||
row.add_cell(
|
||||
Cell::new(format_addresses(e.from.as_deref().unwrap_or(&[])))
|
||||
.fg(self.colors.from),
|
||||
);
|
||||
row.add_cell(Cell::new(e.received_at.as_deref().unwrap_or("")).fg(self.colors.date));
|
||||
table.add_row(row);
|
||||
}
|
||||
|
||||
|
||||
+10
-1
@@ -20,7 +20,10 @@ use clap::Parser;
|
||||
use log::warn;
|
||||
use pimalaya_cli::printer::Printer;
|
||||
|
||||
use crate::jmap::{client::JmapClient, mailbox::query::MailboxesTable};
|
||||
use crate::jmap::{
|
||||
client::JmapClient,
|
||||
mailbox::query::{MailboxColors, MailboxesTable},
|
||||
};
|
||||
|
||||
/// Get JMAP mailboxes by ID (Mailbox/get).
|
||||
#[derive(Debug, Parser)]
|
||||
@@ -40,6 +43,12 @@ impl JmapMailboxGetCommand {
|
||||
|
||||
let table = MailboxesTable {
|
||||
preset: client.account.table_preset().to_string(),
|
||||
colors: MailboxColors {
|
||||
id: client.account.mailboxes_list_table_id_color(),
|
||||
name: client.account.mailboxes_list_table_name_color(),
|
||||
total: client.account.mailboxes_list_table_total_color(),
|
||||
unread: client.account.mailboxes_list_table_unread_color(),
|
||||
},
|
||||
mailboxes: output.mailboxes,
|
||||
};
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::{convert::Infallible, fmt, str::FromStr};
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, ValueEnum};
|
||||
use comfy_table::{Cell, Row, Table};
|
||||
use comfy_table::{Cell, Color, Row, Table};
|
||||
use io_jmap::rfc8621::mailbox::{
|
||||
Mailbox, MailboxFilter, MailboxRole, MailboxSortComparator, MailboxSortProperty,
|
||||
};
|
||||
@@ -110,6 +110,12 @@ impl JmapMailboxQueryCommand {
|
||||
|
||||
let table = MailboxesTable {
|
||||
preset: client.account.table_preset().to_string(),
|
||||
colors: MailboxColors {
|
||||
id: client.account.mailboxes_list_table_id_color(),
|
||||
name: client.account.mailboxes_list_table_name_color(),
|
||||
total: client.account.mailboxes_list_table_total_color(),
|
||||
unread: client.account.mailboxes_list_table_unread_color(),
|
||||
},
|
||||
mailboxes: output.mailboxes,
|
||||
};
|
||||
|
||||
@@ -117,10 +123,31 @@ impl JmapMailboxQueryCommand {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct MailboxColors {
|
||||
pub id: Color,
|
||||
pub name: Color,
|
||||
pub total: Color,
|
||||
pub unread: Color,
|
||||
}
|
||||
|
||||
impl Default for MailboxColors {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
id: Color::Reset,
|
||||
name: Color::Reset,
|
||||
total: Color::Reset,
|
||||
unread: Color::Reset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize)]
|
||||
pub struct MailboxesTable {
|
||||
#[serde(skip)]
|
||||
pub preset: String,
|
||||
#[serde(skip)]
|
||||
pub colors: MailboxColors,
|
||||
pub mailboxes: Vec<Mailbox>,
|
||||
}
|
||||
|
||||
@@ -141,14 +168,16 @@ impl fmt::Display for MailboxesTable {
|
||||
.add_rows(self.mailboxes.iter().map(|r| {
|
||||
let mut row = Row::new();
|
||||
row.max_height(1)
|
||||
.add_cell(Cell::new(r.id.as_deref().unwrap_or("Unknown")))
|
||||
.add_cell(Cell::new(r.name.as_deref().unwrap_or("Unknown")))
|
||||
.add_cell(Cell::new(r.id.as_deref().unwrap_or("Unknown")).fg(self.colors.id))
|
||||
.add_cell(
|
||||
Cell::new(r.name.as_deref().unwrap_or("Unknown")).fg(self.colors.name),
|
||||
)
|
||||
.add_cell(match r.role.as_ref() {
|
||||
Some(r) => Cell::new(r.to_string()),
|
||||
None => Cell::new(""),
|
||||
})
|
||||
.add_cell(Cell::new(r.total_emails))
|
||||
.add_cell(Cell::new(r.unread_emails))
|
||||
.add_cell(Cell::new(r.total_emails).fg(self.colors.total))
|
||||
.add_cell(Cell::new(r.unread_emails).fg(self.colors.unread))
|
||||
.add_cell(Cell::new(if r.is_subscribed { "yes" } else { "" }));
|
||||
row
|
||||
}));
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::fmt;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use comfy_table::{Cell, ContentArrangement, Row, Table};
|
||||
use comfy_table::{Cell, Color, ContentArrangement, Row, Table};
|
||||
use io_maildir::maildir::Maildir;
|
||||
use pimalaya_cli::printer::Printer;
|
||||
use serde::Serialize;
|
||||
@@ -79,6 +79,12 @@ impl MaildirEnvelopeListCommand {
|
||||
let table = EnvelopesTable {
|
||||
preset: client.account.table_preset().to_string(),
|
||||
arrangement: client.account.table_arrangement(),
|
||||
colors: EnvelopeColors {
|
||||
id: client.account.envelopes_list_table_id_color(),
|
||||
subject: client.account.envelopes_list_table_subject_color(),
|
||||
from: client.account.envelopes_list_table_from_color(),
|
||||
date: client.account.envelopes_list_table_date_color(),
|
||||
},
|
||||
envelopes,
|
||||
};
|
||||
|
||||
@@ -86,12 +92,22 @@ impl MaildirEnvelopeListCommand {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct EnvelopeColors {
|
||||
id: Color,
|
||||
subject: Color,
|
||||
from: Color,
|
||||
date: Color,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct EnvelopesTable {
|
||||
#[serde(skip)]
|
||||
preset: String,
|
||||
#[serde(skip)]
|
||||
arrangement: ContentArrangement,
|
||||
#[serde(skip)]
|
||||
colors: EnvelopeColors,
|
||||
envelopes: Vec<EnvelopesTableEntry>,
|
||||
}
|
||||
|
||||
@@ -113,10 +129,10 @@ impl fmt::Display for EnvelopesTable {
|
||||
let mut row = Row::new();
|
||||
|
||||
row.max_height(1)
|
||||
.add_cell(Cell::new(&entry.id))
|
||||
.add_cell(Cell::new(&entry.subject))
|
||||
.add_cell(Cell::new(&entry.from))
|
||||
.add_cell(Cell::new(&entry.date));
|
||||
.add_cell(Cell::new(&entry.id).fg(self.colors.id))
|
||||
.add_cell(Cell::new(&entry.subject).fg(self.colors.subject))
|
||||
.add_cell(Cell::new(&entry.from).fg(self.colors.from))
|
||||
.add_cell(Cell::new(&entry.date).fg(self.colors.date));
|
||||
|
||||
table.add_row(row);
|
||||
}
|
||||
|
||||
+6
-3
@@ -19,7 +19,7 @@ use std::{fmt, path::PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use comfy_table::{Cell, Row, Table};
|
||||
use comfy_table::{Cell, Color, Row, Table};
|
||||
use io_maildir::maildir::Maildir;
|
||||
use pimalaya_cli::printer::Printer;
|
||||
use serde::Serialize;
|
||||
@@ -40,6 +40,7 @@ impl MaildirMailboxListCommand {
|
||||
|
||||
let table = MaildirsTable {
|
||||
preset: client.account.table_preset().to_string(),
|
||||
name_color: client.account.mailboxes_list_table_name_color(),
|
||||
rows: maildirs.into_iter().map(From::from).collect(),
|
||||
};
|
||||
|
||||
@@ -47,10 +48,12 @@ impl MaildirMailboxListCommand {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize)]
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct MaildirsTable {
|
||||
#[serde(skip)]
|
||||
pub preset: String,
|
||||
#[serde(skip)]
|
||||
pub name_color: Color,
|
||||
#[serde(rename = "maildirs")]
|
||||
pub rows: Vec<MaildirRow>,
|
||||
}
|
||||
@@ -66,7 +69,7 @@ impl fmt::Display for MaildirsTable {
|
||||
let mut row = Row::new();
|
||||
|
||||
row.max_height(1)
|
||||
.add_cell(Cell::new(&m.name))
|
||||
.add_cell(Cell::new(&m.name).fg(self.name_color))
|
||||
.add_cell(Cell::new(format!("{}", m.path.display())));
|
||||
|
||||
row
|
||||
|
||||
@@ -27,7 +27,7 @@ use mail_parser::{MessageParser, MimeHeaders};
|
||||
use pimalaya_cli::printer::Printer;
|
||||
|
||||
use crate::shared::{
|
||||
attachments::list::{mime_string, Attachment, Attachments},
|
||||
attachments::list::{mime_string, Attachment, AttachmentColors, Attachments},
|
||||
client::EmailClient,
|
||||
mailboxes::arg::MailboxArg,
|
||||
};
|
||||
@@ -130,6 +130,14 @@ impl AttachmentDownloadCommand {
|
||||
arrangement: client.account.table_arrangement(),
|
||||
with_inline: written.iter().any(|a| a.inline),
|
||||
with_path: true,
|
||||
colors: AttachmentColors {
|
||||
id: client.account.attachments_list_table_id_color(),
|
||||
filename: client.account.attachments_list_table_filename_color(),
|
||||
r#type: client.account.attachments_list_table_type_color(),
|
||||
size: client.account.attachments_list_table_size_color(),
|
||||
inline: client.account.attachments_list_table_inline_color(),
|
||||
path: client.account.attachments_list_table_path_color(),
|
||||
},
|
||||
attachments: written,
|
||||
};
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::fmt;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use clap::Parser;
|
||||
use comfy_table::{Cell, ContentArrangement, Row, Table};
|
||||
use comfy_table::{Cell, Color, ContentArrangement, Row, Table};
|
||||
use humansize::{format_size, BINARY};
|
||||
use mail_parser::{MessageParser, MessagePart, MimeHeaders};
|
||||
use pimalaya_cli::printer::Printer;
|
||||
@@ -87,6 +87,14 @@ impl AttachmentListCommand {
|
||||
arrangement: client.account.table_arrangement(),
|
||||
with_inline: self.inline,
|
||||
with_path: false,
|
||||
colors: AttachmentColors {
|
||||
id: client.account.attachments_list_table_id_color(),
|
||||
filename: client.account.attachments_list_table_filename_color(),
|
||||
r#type: client.account.attachments_list_table_type_color(),
|
||||
size: client.account.attachments_list_table_size_color(),
|
||||
inline: client.account.attachments_list_table_inline_color(),
|
||||
path: client.account.attachments_list_table_path_color(),
|
||||
},
|
||||
attachments,
|
||||
};
|
||||
|
||||
@@ -94,6 +102,16 @@ impl AttachmentListCommand {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct AttachmentColors {
|
||||
pub id: Color,
|
||||
pub filename: Color,
|
||||
pub r#type: Color,
|
||||
pub size: Color,
|
||||
pub inline: Color,
|
||||
pub path: Color,
|
||||
}
|
||||
|
||||
/// One row of the `attachments list` / `attachments download` output.
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct Attachment {
|
||||
@@ -127,6 +145,8 @@ pub struct Attachments {
|
||||
pub with_inline: bool,
|
||||
#[serde(skip)]
|
||||
pub with_path: bool,
|
||||
#[serde(skip)]
|
||||
pub(crate) colors: AttachmentColors,
|
||||
pub attachments: Vec<Attachment>,
|
||||
}
|
||||
|
||||
@@ -154,15 +174,19 @@ impl fmt::Display for Attachments {
|
||||
.add_rows(self.attachments.iter().map(|a| {
|
||||
let mut row = Row::new();
|
||||
row.max_height(1);
|
||||
row.add_cell(Cell::new(&a.id));
|
||||
row.add_cell(Cell::new(a.filename.as_deref().unwrap_or("")));
|
||||
row.add_cell(Cell::new(a.mime.as_deref().unwrap_or("")));
|
||||
row.add_cell(Cell::new(format_size(a.size, BINARY)));
|
||||
row.add_cell(Cell::new(&a.id).fg(self.colors.id));
|
||||
row.add_cell(
|
||||
Cell::new(a.filename.as_deref().unwrap_or("")).fg(self.colors.filename),
|
||||
);
|
||||
row.add_cell(Cell::new(a.mime.as_deref().unwrap_or("")).fg(self.colors.r#type));
|
||||
row.add_cell(Cell::new(format_size(a.size, BINARY)).fg(self.colors.size));
|
||||
if self.with_inline {
|
||||
row.add_cell(Cell::new(if a.inline { "yes" } else { "no" }));
|
||||
row.add_cell(
|
||||
Cell::new(if a.inline { "yes" } else { "no" }).fg(self.colors.inline),
|
||||
);
|
||||
}
|
||||
if self.with_path {
|
||||
row.add_cell(Cell::new(a.path.as_deref().unwrap_or("")));
|
||||
row.add_cell(Cell::new(a.path.as_deref().unwrap_or("")).fg(self.colors.path));
|
||||
}
|
||||
row
|
||||
}));
|
||||
|
||||
@@ -20,7 +20,7 @@ use std::{collections::BTreeSet, fmt};
|
||||
use anyhow::Result;
|
||||
use chrono::{DateTime, FixedOffset, Local};
|
||||
use clap::Parser;
|
||||
use comfy_table::{Cell, ContentArrangement, Row, Table};
|
||||
use comfy_table::{Cell, Color, ContentArrangement, Row, Table};
|
||||
use humansize::{format_size, BINARY};
|
||||
use io_email::{address::Address, envelope::Envelope, flag::Flag};
|
||||
use pimalaya_cli::printer::Printer;
|
||||
@@ -93,6 +93,22 @@ impl EnvelopeListCommand {
|
||||
datetime_local_tz: client.account.datetime_local_tz(),
|
||||
recipient: self.recipient,
|
||||
with_attachment: self.has_attachment,
|
||||
chars: FlagChars {
|
||||
unseen: client.account.envelopes_list_table_unseen_char(),
|
||||
replied: client.account.envelopes_list_table_replied_char(),
|
||||
flagged: client.account.envelopes_list_table_flagged_char(),
|
||||
attachment: client.account.envelopes_list_table_attachment_char(),
|
||||
},
|
||||
colors: EnvelopeColors {
|
||||
id: client.account.envelopes_list_table_id_color(),
|
||||
flags: client.account.envelopes_list_table_flags_color(),
|
||||
att: client.account.envelopes_list_table_att_color(),
|
||||
subject: client.account.envelopes_list_table_subject_color(),
|
||||
from: client.account.envelopes_list_table_from_color(),
|
||||
to: client.account.envelopes_list_table_to_color(),
|
||||
date: client.account.envelopes_list_table_date_color(),
|
||||
size: client.account.envelopes_list_table_size_color(),
|
||||
},
|
||||
envelopes,
|
||||
};
|
||||
|
||||
@@ -100,6 +116,30 @@ impl EnvelopeListCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/// Glyphs the FLAGS / ATT columns substitute in, sourced from the
|
||||
/// merged account config (v1.2.0 defaults: `*`, `R`, `!`, `@`).
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(super) struct FlagChars {
|
||||
pub unseen: char,
|
||||
pub replied: char,
|
||||
pub flagged: char,
|
||||
pub attachment: char,
|
||||
}
|
||||
|
||||
/// Per-column foreground colors for the envelopes table. `Color::Reset`
|
||||
/// means "use the terminal default" (i.e. no override).
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(super) struct EnvelopeColors {
|
||||
pub id: Color,
|
||||
pub flags: Color,
|
||||
pub att: Color,
|
||||
pub subject: Color,
|
||||
pub from: Color,
|
||||
pub to: Color,
|
||||
pub date: Color,
|
||||
pub size: Color,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct Envelopes {
|
||||
#[serde(skip)]
|
||||
@@ -116,6 +156,10 @@ pub struct Envelopes {
|
||||
pub recipient: bool,
|
||||
#[serde(skip)]
|
||||
pub with_attachment: bool,
|
||||
#[serde(skip)]
|
||||
pub(super) chars: FlagChars,
|
||||
#[serde(skip)]
|
||||
pub(super) colors: EnvelopeColors,
|
||||
pub envelopes: Vec<Envelope>,
|
||||
}
|
||||
|
||||
@@ -139,22 +183,33 @@ impl fmt::Display for Envelopes {
|
||||
.add_rows(self.envelopes.iter().map(|env| {
|
||||
let mut row = Row::new();
|
||||
row.max_height(1);
|
||||
row.add_cell(Cell::new(&env.id));
|
||||
row.add_cell(Cell::new(format_flags(&env.flags)));
|
||||
row.add_cell(Cell::new(&env.id).fg(self.colors.id));
|
||||
row.add_cell(Cell::new(format_flags(&env.flags, &self.chars)).fg(self.colors.flags));
|
||||
if self.with_attachment {
|
||||
row.add_cell(Cell::new(format_attachment(env.has_attachment)));
|
||||
row.add_cell(
|
||||
Cell::new(format_attachment(env.has_attachment, self.chars.attachment))
|
||||
.fg(self.colors.att),
|
||||
);
|
||||
}
|
||||
row.add_cell(Cell::new(&env.subject));
|
||||
row.add_cell(Cell::new(&env.subject).fg(self.colors.subject));
|
||||
|
||||
let addresses = if self.recipient { &env.to } else { &env.from };
|
||||
row.add_cell(Cell::new(format_addresses(addresses)));
|
||||
let from_or_to_color = if self.recipient {
|
||||
self.colors.to
|
||||
} else {
|
||||
self.colors.from
|
||||
};
|
||||
row.add_cell(Cell::new(format_addresses(addresses)).fg(from_or_to_color));
|
||||
|
||||
row.add_cell(Cell::new(format_date(
|
||||
env.date,
|
||||
&self.datetime_fmt,
|
||||
self.datetime_local_tz,
|
||||
)));
|
||||
row.add_cell(Cell::new(format_size(env.size, BINARY)));
|
||||
row.add_cell(
|
||||
Cell::new(format_date(
|
||||
env.date,
|
||||
&self.datetime_fmt,
|
||||
self.datetime_local_tz,
|
||||
))
|
||||
.fg(self.colors.date),
|
||||
);
|
||||
row.add_cell(Cell::new(format_size(env.size, BINARY)).fg(self.colors.size));
|
||||
row
|
||||
}));
|
||||
|
||||
@@ -167,39 +222,34 @@ impl fmt::Display for Envelopes {
|
||||
}
|
||||
}
|
||||
|
||||
/// 4-character flag widget: one slot per LCD variant. Unread (no
|
||||
/// `Seen`) shows `N` in the first slot since unread is the
|
||||
/// attention-grabbing case.
|
||||
pub(super) fn format_flags(flags: &BTreeSet<Flag>) -> String {
|
||||
let mut out = String::with_capacity(4);
|
||||
/// 3-character flag widget: unseen, replied, flagged. Each slot is a
|
||||
/// space when the flag is absent, otherwise the configured glyph
|
||||
/// (v1.2.0 defaults: `*`, `R`, `!`).
|
||||
pub(super) fn format_flags(flags: &BTreeSet<Flag>, chars: &FlagChars) -> String {
|
||||
let mut out = String::with_capacity(3);
|
||||
out.push(if flags.contains(&Flag::Seen) {
|
||||
' '
|
||||
} else {
|
||||
'N'
|
||||
chars.unseen
|
||||
});
|
||||
out.push(if flags.contains(&Flag::Answered) {
|
||||
'r'
|
||||
chars.replied
|
||||
} else {
|
||||
' '
|
||||
});
|
||||
out.push(if flags.contains(&Flag::Flagged) {
|
||||
'*'
|
||||
} else {
|
||||
' '
|
||||
});
|
||||
out.push(if flags.contains(&Flag::Draft) {
|
||||
'D'
|
||||
chars.flagged
|
||||
} else {
|
||||
' '
|
||||
});
|
||||
out
|
||||
}
|
||||
|
||||
pub(super) fn format_attachment(has: Option<bool>) -> &'static str {
|
||||
pub(super) fn format_attachment(has: Option<bool>, glyph: char) -> String {
|
||||
match has {
|
||||
Some(true) => "@",
|
||||
Some(false) => "",
|
||||
None => "?",
|
||||
Some(true) => glyph.to_string(),
|
||||
Some(false) => String::new(),
|
||||
None => "?".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,11 @@ use clap::Parser;
|
||||
use io_email::search::{error::Error as SearchQueryError, query::SearchEmailsQuery};
|
||||
use pimalaya_cli::printer::Printer;
|
||||
|
||||
use crate::shared::{client::EmailClient, envelopes::list::Envelopes, mailboxes::arg::MailboxArg};
|
||||
use crate::shared::{
|
||||
client::EmailClient,
|
||||
envelopes::list::{EnvelopeColors, Envelopes, FlagChars},
|
||||
mailboxes::arg::MailboxArg,
|
||||
};
|
||||
|
||||
/// Search envelopes for the active account using the shared search
|
||||
/// query DSL, regardless of the underlying backend (IMAP, JMAP or
|
||||
@@ -102,6 +106,22 @@ impl EnvelopeSearchCommand {
|
||||
datetime_local_tz: client.account.datetime_local_tz(),
|
||||
recipient: self.recipient,
|
||||
with_attachment: self.has_attachment,
|
||||
chars: FlagChars {
|
||||
unseen: client.account.envelopes_list_table_unseen_char(),
|
||||
replied: client.account.envelopes_list_table_replied_char(),
|
||||
flagged: client.account.envelopes_list_table_flagged_char(),
|
||||
attachment: client.account.envelopes_list_table_attachment_char(),
|
||||
},
|
||||
colors: EnvelopeColors {
|
||||
id: client.account.envelopes_list_table_id_color(),
|
||||
flags: client.account.envelopes_list_table_flags_color(),
|
||||
att: client.account.envelopes_list_table_att_color(),
|
||||
subject: client.account.envelopes_list_table_subject_color(),
|
||||
from: client.account.envelopes_list_table_from_color(),
|
||||
to: client.account.envelopes_list_table_to_color(),
|
||||
date: client.account.envelopes_list_table_date_color(),
|
||||
size: client.account.envelopes_list_table_size_color(),
|
||||
},
|
||||
envelopes,
|
||||
};
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::fmt;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use comfy_table::{Cell, ContentArrangement, Row, Table};
|
||||
use comfy_table::{Cell, Color, ContentArrangement, Row, Table};
|
||||
use io_email::mailbox::Mailbox;
|
||||
use pimalaya_cli::printer::Printer;
|
||||
use serde::Serialize;
|
||||
@@ -56,6 +56,12 @@ impl MailboxListCommand {
|
||||
arrangement: client.account.table_arrangement(),
|
||||
max_width: self.max_width,
|
||||
with_counts: self.counts,
|
||||
colors: MailboxColors {
|
||||
id: client.account.mailboxes_list_table_id_color(),
|
||||
name: client.account.mailboxes_list_table_name_color(),
|
||||
total: client.account.mailboxes_list_table_total_color(),
|
||||
unread: client.account.mailboxes_list_table_unread_color(),
|
||||
},
|
||||
mailboxes,
|
||||
};
|
||||
|
||||
@@ -63,6 +69,14 @@ impl MailboxListCommand {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct MailboxColors {
|
||||
id: Color,
|
||||
name: Color,
|
||||
total: Color,
|
||||
unread: Color,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct Mailboxes {
|
||||
#[serde(skip)]
|
||||
@@ -73,6 +87,8 @@ pub struct Mailboxes {
|
||||
pub max_width: Option<u16>,
|
||||
#[serde(skip)]
|
||||
pub with_counts: bool,
|
||||
#[serde(skip)]
|
||||
colors: MailboxColors,
|
||||
pub mailboxes: Vec<Mailbox>,
|
||||
}
|
||||
|
||||
@@ -93,11 +109,11 @@ impl fmt::Display for Mailboxes {
|
||||
.add_rows(self.mailboxes.iter().map(|m| {
|
||||
let mut row = Row::new();
|
||||
row.max_height(1);
|
||||
row.add_cell(Cell::new(&m.id));
|
||||
row.add_cell(Cell::new(&m.name));
|
||||
row.add_cell(Cell::new(&m.id).fg(self.colors.id));
|
||||
row.add_cell(Cell::new(&m.name).fg(self.colors.name));
|
||||
if self.with_counts {
|
||||
row.add_cell(count_cell(m.total));
|
||||
row.add_cell(count_cell(m.unread));
|
||||
row.add_cell(count_cell(m.total).fg(self.colors.total));
|
||||
row.add_cell(count_cell(m.unread).fg(self.colors.unread));
|
||||
}
|
||||
row
|
||||
}));
|
||||
|
||||
@@ -113,11 +113,12 @@ pub fn run_or_exit(target: &Path) -> Result<Config> {
|
||||
|
||||
let config = Config {
|
||||
downloads_dir: None,
|
||||
table_preset: None,
|
||||
table_arrangement: None,
|
||||
table: Default::default(),
|
||||
envelope: Default::default(),
|
||||
mailbox: Default::default(),
|
||||
message: Default::default(),
|
||||
attachment: Default::default(),
|
||||
account: Default::default(),
|
||||
accounts: HashMap::from([(account_name, account)]),
|
||||
};
|
||||
|
||||
@@ -185,10 +186,10 @@ fn build_account_from_discovery(
|
||||
Ok(AccountConfig {
|
||||
default: true,
|
||||
downloads_dir: None,
|
||||
table_preset: None,
|
||||
table_arrangement: None,
|
||||
table: Default::default(),
|
||||
envelope: Default::default(),
|
||||
mailbox: Default::default(),
|
||||
attachment: Default::default(),
|
||||
imap: None,
|
||||
jmap: Some(jmap_to_config(jmap)?),
|
||||
maildir: None,
|
||||
@@ -201,10 +202,10 @@ fn build_account_from_discovery(
|
||||
Ok(AccountConfig {
|
||||
default: true,
|
||||
downloads_dir: None,
|
||||
table_preset: None,
|
||||
table_arrangement: None,
|
||||
table: Default::default(),
|
||||
envelope: Default::default(),
|
||||
mailbox: Default::default(),
|
||||
attachment: Default::default(),
|
||||
imap: Some(imap_to_config(imap)?),
|
||||
jmap: None,
|
||||
maildir: None,
|
||||
|
||||
+12
-6
@@ -94,8 +94,10 @@ pub fn edit_account(target: &Path, mut config: Config, account_name: &str) -> Re
|
||||
.map(|a| a.default)
|
||||
.unwrap_or(is_first_account);
|
||||
let downloads_dir = existing.as_ref().and_then(|a| a.downloads_dir.clone());
|
||||
let table_preset = existing.as_ref().and_then(|a| a.table_preset.clone());
|
||||
let table_arrangement = existing.as_ref().and_then(|a| a.table_arrangement.clone());
|
||||
let table = existing
|
||||
.as_ref()
|
||||
.map(|a| a.table.clone())
|
||||
.unwrap_or_default();
|
||||
let envelope = existing
|
||||
.as_ref()
|
||||
.map(|a| a.envelope.clone())
|
||||
@@ -104,6 +106,10 @@ pub fn edit_account(target: &Path, mut config: Config, account_name: &str) -> Re
|
||||
.as_ref()
|
||||
.map(|a| a.mailbox.clone())
|
||||
.unwrap_or_default();
|
||||
let attachment = existing
|
||||
.as_ref()
|
||||
.map(|a| a.attachment.clone())
|
||||
.unwrap_or_default();
|
||||
let maildir = existing.as_ref().and_then(|a| a.maildir.clone());
|
||||
|
||||
let account = if jmap_defaults.is_some() {
|
||||
@@ -111,10 +117,10 @@ pub fn edit_account(target: &Path, mut config: Config, account_name: &str) -> Re
|
||||
AccountConfig {
|
||||
default,
|
||||
downloads_dir,
|
||||
table_preset,
|
||||
table_arrangement,
|
||||
table,
|
||||
envelope,
|
||||
mailbox,
|
||||
attachment,
|
||||
imap: None,
|
||||
jmap: Some(jmap_to_config(jmap)?),
|
||||
maildir,
|
||||
@@ -126,10 +132,10 @@ pub fn edit_account(target: &Path, mut config: Config, account_name: &str) -> Re
|
||||
AccountConfig {
|
||||
default,
|
||||
downloads_dir,
|
||||
table_preset,
|
||||
table_arrangement,
|
||||
table,
|
||||
envelope,
|
||||
mailbox,
|
||||
attachment,
|
||||
imap: Some(imap_to_config(imap)?),
|
||||
jmap: None,
|
||||
maildir,
|
||||
|
||||
Reference in New Issue
Block a user