mailmate 1.8.1 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -3
- data/lib/mailmate/cli/search.rb +140 -26
- data/lib/mailmate/search_syntax.rb +14 -4
- data/lib/mailmate/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dd43afdf338a47a4616748d1e3ecb7781466ee73c836030bb9fdd763af1e65e3
|
|
4
|
+
data.tar.gz: d0d87cfbe4166a9aa8868e237ff2fc628a3d00b57f8de8079eacdb4cb4282de1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 052c4619e7cf07129903242c5d8081f61ae64a46371cdf04fc5df0a6f1dbbef982a4e944e5629d12aa82ff036f14016080fb0e77fc814e23f0a9595ef2064962
|
|
7
|
+
data.tar.gz: 1a9918739f80c5b5d46030214b58e9c8dcb87ba76e12c5dee14c9c79f2e96de34dc3bf2461f528edc9f22deb1e1f5497566e14c58d31f981e42cd8a819c76c65
|
data/README.md
CHANGED
|
@@ -185,10 +185,11 @@ mmsearch 'f acme' 'id flags subject from' --limit 20 --no-align
|
|
|
185
185
|
| `a <term>` | Any address header contains. |
|
|
186
186
|
| `b <term>` | Body (plain text) contains. |
|
|
187
187
|
| `m <term>` | Common headers OR body. |
|
|
188
|
-
| `d <date>` | Received: `Nh` (rolling clock hours, `24h` = last 24 hours), `Nd`/`Nw`/`Nm`/`Ny` (
|
|
188
|
+
| `d <date>` | Received: `Nh` (rolling clock hours, `24h` = last 24 hours), `Nd`/`Nw`/`Nm`/`Ny` (calendar units floored to the unit start, app parity — `1d` = today, `1w` = this week, `1y` = this year), absolute `Y`, `Y-M`, `Y-M-D`, or day-of-month `D` / `M-D` (`d 7` = the most recent 7th). Slash dates are month-first American (`8/9/2026` = Aug 9); `--european` flips to day-first. Comparisons on absolute dates: `d >2026-08` (after), `d <2026-08` (before), also `>=`/`<=`. |
|
|
189
189
|
| `T <tag>` | Tags / IMAP keywords (`K` is a synonym). |
|
|
190
|
-
| `is:<state>` | Message state: `is:unread`, `is:read`, `is:flagged`, `is:replied`, `is:draft` (Gmail synonyms `starred`/`answered` work; `-is:unread` negates). |
|
|
191
|
-
| `has:attachment` | Root MIME type is `multipart/mixed`
|
|
190
|
+
| `is:<state>` | Message state: `is:unread`, `is:read`, `is:flagged`, `is:replied`, `is:draft`, `is:archived` (Gmail synonyms `starred`/`answered` work; `-is:unread` negates; `in:inbox`/`in:archive` translate onto `is:archived`). |
|
|
191
|
+
| `has:attachment` | Root MIME type is `multipart/mixed`; wrapper types (`signed`/`encrypted`/`related`) are opened and checked for real attachments. |
|
|
192
|
+
| `<header>:<term>` | Any indexed header contains (`delivered-to:joe`). A header this store has never seen is a usage error, not an empty result. |
|
|
192
193
|
| `!<value>` | Negate, e.g. `f !smith` = From does NOT contain smith; works on dates too (`d !3d` = more than 3 days ago). |
|
|
193
194
|
|
|
194
195
|
Dates match on the **display-zone day** — the same day the `date`/`time` output columns show. An impossible date term or combination (`d 2026-02-31`, `d >2026 d <2025`) is a usage error, not a silent empty result. Familiar foreign `key:value` tokens (`from:bob`, `date:today`, `after:2026-08-01`, `older_than:2w`) are auto-translated to quicksearch with each rewrite announced on stderr; unrecognized keys (`is:unread`) are searched as literal text, and an empty result says so.
|
|
@@ -433,6 +434,8 @@ A few rough edges to be aware of:
|
|
|
433
434
|
|
|
434
435
|
## Status
|
|
435
436
|
|
|
437
|
+
1.9.0 — App-parity search release. Arbitrary header specs are native (`delivered-to:joe`, `x-mailer.name:mailmate` — subpaths search the whole header value); index-only by design: a header the store has never seen is a usage error suggesting quotes, never a `Mail.read` per candidate. Relative calendar units now floor to the unit start, matching the app's documented semantics (`1w` = this week, `1y` = this year; `Nh` stays a rolling clock window on purpose). Day-of-month terms (`d 7` = the most recent 7th, `d 12-25` = last Christmas) close the last known silent-empty. `is:archived` joins the state specs (path-derived, same source as the flags column), with `in:inbox`/`in:archive` translating onto it; `has:attachment` now opens `multipart/signed`/`encrypted`/`related` wrappers and checks for real attachments. New `rake release` runs the whole mechanical release — clean-tree + green-tests + unreleased-version guards, then build, tag, sync, `gem push`.
|
|
438
|
+
|
|
436
439
|
1.8.0 — Reply derivation, and message-state specs.
|
|
437
440
|
|
|
438
441
|
**Composing:** replies and forwards are now derived from the parent instead of hand-assembled. `mm-send` / `mm-draft` gain `--reply-to <id>`, `--reply-all-to <id>`, `--forward <id>` (eml-id or Message-ID), which compute `In-Reply-To`, the full `References` chain, recipients and the `Re:`/`Fwd:` subject, plus `--no-quote` and `--print-prefill` (derived fields as JSON, sends nothing). The MCP `send`/`draft` tools take the same `reply_to` / `reply_all_to` / `forward` arguments. Explicitly-passed fields win over derived ones, and overriding a visible field never drops the threading headers. A forward deliberately does *not* thread into the original conversation. The chain is built in exactly one place (`Mailmate::ReplyPrefill`), and `--header` values now route through one sanitizer (`Mailmate::HeaderValue`) — previously the MCP's own argv builder lacked the CR/LF defense that stops a parent `Message-ID` smuggling extra headers. All of it is documented once in [docs/Composing and threading.md](docs/Composing%20and%20threading.md), which the README, `--help` preambles and MCP instructions now point at rather than restate. Every `exe/` command answers `--version`, so consumers can check the installed version instead of probing for a flag (the CLIs pass unknown flags through to `emate`, which makes flag-probing unsafe). The gemspec finally carries `homepage` and `metadata`, so the rubygems page links back to the source.
|
data/lib/mailmate/cli/search.rb
CHANGED
|
@@ -405,6 +405,14 @@ module Mailmate
|
|
|
405
405
|
# Gmail callers actually write.
|
|
406
406
|
negate ||= operand.start_with?("-")
|
|
407
407
|
specs << [:state, operand.delete_prefix("-").downcase, negate]
|
|
408
|
+
elsif !quoted && (hdr = header_spec_token(operand))
|
|
409
|
+
# Arbitrary header specs (delivered-to:joe) — native app
|
|
410
|
+
# syntax per the manual. Foreign keys (date:, from:, ...) never
|
|
411
|
+
# reach here: translate() rewrote the translatable ones before
|
|
412
|
+
# parsing, and the rest stay literal so zero_result_hint can
|
|
413
|
+
# suggest their quicksearch equivalent.
|
|
414
|
+
negate ||= operand.start_with?("-")
|
|
415
|
+
specs << [:header, hdr, negate]
|
|
408
416
|
else
|
|
409
417
|
# A bare term opening an or-group inherits the modifier in
|
|
410
418
|
# force (`d 2024 or 2025`). Elsewhere it is MailMate's
|
|
@@ -420,13 +428,29 @@ module Mailmate
|
|
|
420
428
|
[specs, in_force]
|
|
421
429
|
end
|
|
422
430
|
|
|
431
|
+
# The downcased "name:value" for a bare token that should parse as an
|
|
432
|
+
# arbitrary-header spec, nil otherwise. Excluded: keys the translator
|
|
433
|
+
# owns (FOREIGN_KEYS — their untranslatable forms stay literal for the
|
|
434
|
+
# zero-result hint), state keys (is/has, handled first), and
|
|
435
|
+
# URL-shaped tokens (http://... is a term, not a search of the
|
|
436
|
+
# nonexistent "http" header).
|
|
437
|
+
def header_spec_token(operand)
|
|
438
|
+
m = operand.match(/\A-?([A-Za-z][\w-]*(?:\.[\w.-]+)?):(\S+)\z/)
|
|
439
|
+
return nil unless m
|
|
440
|
+
return nil if m[2].start_with?("/")
|
|
441
|
+
key = Mailmate::SearchSyntax.normalize_key(m[1].sub(/\..*/, ""))
|
|
442
|
+
return nil if Mailmate::SearchSyntax::FOREIGN_KEYS.include?(key)
|
|
443
|
+
return nil if %w[is has].include?(key)
|
|
444
|
+
"#{m[1]}:#{m[2]}".downcase
|
|
445
|
+
end
|
|
446
|
+
|
|
423
447
|
# Static cost rank per spec field for AND evaluation order: compiled
|
|
424
448
|
# date compare < header/tag index lookup < body matching (resolves
|
|
425
449
|
# part-ids and walks every body segment). Used by order_specs.
|
|
426
450
|
SPEC_COST = {
|
|
427
451
|
date: 0,
|
|
428
452
|
from: 1, recipients: 1, cc: 1, subject: 1, address_any: 1, any: 1,
|
|
429
|
-
tag: 1, keyword: 1, state: 1,
|
|
453
|
+
tag: 1, keyword: 1, state: 1, header: 1,
|
|
430
454
|
body: 2, message_or_body: 2,
|
|
431
455
|
}.freeze
|
|
432
456
|
|
|
@@ -438,6 +462,7 @@ module Mailmate
|
|
|
438
462
|
"flagged" => :flagged, "starred" => :flagged,
|
|
439
463
|
"replied" => :replied, "answered" => :replied,
|
|
440
464
|
"draft" => :draft,
|
|
465
|
+
"archived" => :archived, "archive" => :archived,
|
|
441
466
|
"attachment" => :attachment, "attachments" => :attachment,
|
|
442
467
|
}.freeze
|
|
443
468
|
|
|
@@ -528,16 +553,23 @@ module Mailmate
|
|
|
528
553
|
if term =~ /\A(\d+)([dwmy])\z/
|
|
529
554
|
n, u = Regexp.last_match(1).to_i, Regexp.last_match(2)
|
|
530
555
|
return nil if n.zero? # a zero-length window matches nothing
|
|
531
|
-
#
|
|
532
|
-
#
|
|
533
|
-
#
|
|
534
|
-
#
|
|
535
|
-
# rolling
|
|
556
|
+
# Calendar units floored to the unit start, matching the app's
|
|
557
|
+
# documented semantics ("1y means this year and not 365 days"):
|
|
558
|
+
# 1d = today, 1w = this ISO week (from Monday), 1m = this month,
|
|
559
|
+
# 1y = this year; N reaches back N-1 further units. Only `Nh` is a
|
|
560
|
+
# rolling clock window — that split is deliberate (2026-08-18):
|
|
561
|
+
# calendar words mean calendar spans, and "the last 24 hours" is
|
|
562
|
+
# spelled d 24h.
|
|
536
563
|
cutoff = case u
|
|
537
564
|
when "d" then today - (n - 1)
|
|
538
|
-
when "w"
|
|
539
|
-
|
|
540
|
-
|
|
565
|
+
when "w"
|
|
566
|
+
start = today - (7 * (n - 1))
|
|
567
|
+
start - (start.cwday - 1)
|
|
568
|
+
when "m"
|
|
569
|
+
start = today << (n - 1)
|
|
570
|
+
Date.new(start.year, start.month, 1)
|
|
571
|
+
when "y"
|
|
572
|
+
Date.new(today.year - (n - 1), 1, 1)
|
|
541
573
|
end
|
|
542
574
|
return [ymd_int(cutoff), 9999_12_31]
|
|
543
575
|
end
|
|
@@ -547,15 +579,34 @@ module Mailmate
|
|
|
547
579
|
|
|
548
580
|
case parts.size
|
|
549
581
|
when 1
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
582
|
+
if parts[0].length == 4
|
|
583
|
+
y = parts[0].to_i
|
|
584
|
+
return nil if y.zero?
|
|
585
|
+
[y * 10_000 + 101, y * 10_000 + 1231]
|
|
586
|
+
else
|
|
587
|
+
# App semantics: a bare small number is a day of the current
|
|
588
|
+
# month — or the most recent month containing that day when it
|
|
589
|
+
# hasn't happened yet (`d 7` on the 5th = last month's 7th).
|
|
590
|
+
most_recent_day_range(parts[0].to_i, today)
|
|
591
|
+
end
|
|
553
592
|
when 2
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
593
|
+
if parts[1].length == 4
|
|
594
|
+
# Month-first with a 4-digit year (8/2026).
|
|
595
|
+
y, m = parts[1].to_i, parts[0].to_i
|
|
596
|
+
return nil if y.zero? || !(1..12).cover?(m)
|
|
597
|
+
[y * 10_000 + m * 100 + 1, y * 10_000 + m * 100 + 31]
|
|
598
|
+
elsif parts[0].length == 4
|
|
599
|
+
# Year-first (2026-08).
|
|
600
|
+
y, m = parts[0].to_i, parts[1].to_i
|
|
601
|
+
return nil if y.zero? || !(1..12).cover?(m)
|
|
602
|
+
[y * 10_000 + m * 100 + 1, y * 10_000 + m * 100 + 31]
|
|
603
|
+
else
|
|
604
|
+
# No year: month + day, ordered per date_order, most recent
|
|
605
|
+
# occurrence (`d 12-25` in August = last year's Dec 25).
|
|
606
|
+
a, b = parts.map(&:to_i)
|
|
607
|
+
m, d = date_order == :dmy ? [b, a] : [a, b]
|
|
608
|
+
most_recent_month_day_range(m, d, today)
|
|
609
|
+
end
|
|
559
610
|
when 3
|
|
560
611
|
# ISO year-first, or slash-date with trailing 4-digit year ordered
|
|
561
612
|
# per date_order. Impossible calendar dates (2026-02-31, month 13)
|
|
@@ -590,7 +641,14 @@ module Mailmate
|
|
|
590
641
|
# would otherwise quietly match no message ever.
|
|
591
642
|
if field == :state && !STATE_CANON.key?(term.split(":", 2).last)
|
|
592
643
|
return "state term cannot match anything: #{term} " \
|
|
593
|
-
"(known: is:unread is:read is:flagged is:replied is:draft has:attachment)"
|
|
644
|
+
"(known: is:unread is:read is:flagged is:replied is:draft is:archived has:attachment)"
|
|
645
|
+
end
|
|
646
|
+
if field == :header
|
|
647
|
+
name = term.split(":", 2).first.sub(/\..*/, "")
|
|
648
|
+
if reader_for(name).nil?
|
|
649
|
+
return "no '#{name}' header index — this MailMate store has never seen that " \
|
|
650
|
+
"header. Quote the token (\"#{term}\") to search it as literal text."
|
|
651
|
+
end
|
|
594
652
|
end
|
|
595
653
|
next unless field == :date
|
|
596
654
|
range = hour_range_for(term) || date_range_for(term)
|
|
@@ -615,6 +673,33 @@ module Mailmate
|
|
|
615
673
|
nil
|
|
616
674
|
end
|
|
617
675
|
|
|
676
|
+
# Single day for the most recent occurrence of day-of-month `day`,
|
|
677
|
+
# stepping back past months that lack it (`d 31` in early March =
|
|
678
|
+
# Jan 31). nil when no month within a year works (day > 31).
|
|
679
|
+
def most_recent_day_range(day, today)
|
|
680
|
+
return nil unless (1..31).cover?(day)
|
|
681
|
+
0.upto(12) do |back|
|
|
682
|
+
m = today << back
|
|
683
|
+
next unless Date.valid_date?(m.year, m.month, day)
|
|
684
|
+
candidate = Date.new(m.year, m.month, day)
|
|
685
|
+
return [ymd_int(candidate), ymd_int(candidate)] if candidate <= today
|
|
686
|
+
end
|
|
687
|
+
nil
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
# Single day for the most recent occurrence of month+day: this year if
|
|
691
|
+
# it has happened, else last year. nil for impossible dates.
|
|
692
|
+
def most_recent_month_day_range(month, day, today)
|
|
693
|
+
return nil unless (1..12).cover?(month) && (1..31).cover?(day)
|
|
694
|
+
[0, 1].each do |back|
|
|
695
|
+
y = today.year - back
|
|
696
|
+
next unless Date.valid_date?(y, month, day)
|
|
697
|
+
candidate = Date.new(y, month, day)
|
|
698
|
+
return [ymd_int(candidate), ymd_int(candidate)] if candidate <= today
|
|
699
|
+
end
|
|
700
|
+
nil
|
|
701
|
+
end
|
|
702
|
+
|
|
618
703
|
# `13/8/2026` under month-first ordering is month 13 — almost certainly
|
|
619
704
|
# a day-first date (and vice versa). Name the likely fix instead of
|
|
620
705
|
# leaving the generic cannot-match.
|
|
@@ -785,27 +870,54 @@ module Mailmate
|
|
|
785
870
|
end
|
|
786
871
|
|
|
787
872
|
# term is the full lowercased token ("is:unread", "has:attachment").
|
|
788
|
-
# Flag states read the #flags index;
|
|
789
|
-
#
|
|
790
|
-
#
|
|
791
|
-
#
|
|
873
|
+
# Flag states read the #flags index; archive state reads the path
|
|
874
|
+
# (same source as the flags output column); attachment presence reads
|
|
875
|
+
# the indexed root content-type — multipart/mixed is the standard
|
|
876
|
+
# attachment layout. Wrapper types that can HIDE attachments
|
|
877
|
+
# (signed/encrypted/related) fall back to reading the message and
|
|
878
|
+
# asking Mail for real attachments; plain and alternative roots are
|
|
879
|
+
# trusted as attachment-free. Unknown state values never reach here:
|
|
792
880
|
# date_spec_error rejects them up front.
|
|
793
|
-
def state_matches?(eml_id, mail, term)
|
|
881
|
+
def state_matches?(eml_id, mail, path, term)
|
|
794
882
|
state = STATE_CANON[term.split(":", 2).last]
|
|
795
883
|
return false unless state
|
|
796
884
|
|
|
797
885
|
case state
|
|
798
886
|
when :unread
|
|
799
887
|
eml_id ? !message_flags(eml_id).include?("\\Seen") : false
|
|
888
|
+
when :archived
|
|
889
|
+
path.to_s.include?("/Archive.mailbox/")
|
|
800
890
|
when :attachment
|
|
801
891
|
ct = eml_id ? (reader_for("content-type")&.value_for(eml_id.to_i) rescue nil).to_s : ""
|
|
802
|
-
|
|
803
|
-
|
|
892
|
+
if ct.empty?
|
|
893
|
+
m = mail || (path && (Mail.read(path) rescue nil))
|
|
894
|
+
return m ? m.attachments.any? : false
|
|
895
|
+
end
|
|
896
|
+
ctl = ct.downcase
|
|
897
|
+
return true if ctl.include?("multipart/mixed")
|
|
898
|
+
if ctl.match?(%r{multipart/(signed|encrypted|related)})
|
|
899
|
+
m = mail || (path && (Mail.read(path) rescue nil))
|
|
900
|
+
return m ? m.attachments.any? : false
|
|
901
|
+
end
|
|
902
|
+
false
|
|
804
903
|
else
|
|
805
904
|
message_flags(eml_id).include?(STATE_FLAGS[state])
|
|
806
905
|
end
|
|
807
906
|
end
|
|
808
907
|
|
|
908
|
+
# term is the downcased "name:value" (subpath allowed on the name and
|
|
909
|
+
# ignored: `x-mailer.name:mailmate` searches the whole x-mailer value,
|
|
910
|
+
# which substring matching covers anyway). Index-only by design — a
|
|
911
|
+
# header this store has never seen has no index, and date_spec_error
|
|
912
|
+
# reports that up front instead of this method quietly missing.
|
|
913
|
+
def header_matches?(eml_id, mail, term)
|
|
914
|
+
name, value = term.split(":", 2)
|
|
915
|
+
name = name.sub(/\..*/, "")
|
|
916
|
+
v = eml_id ? (reader_for(name)&.value_for(eml_id.to_i) rescue nil) : nil
|
|
917
|
+
v = (mail[name]&.to_s rescue nil) if v.nil? && mail
|
|
918
|
+
v.to_s.b.downcase.include?(value.b)
|
|
919
|
+
end
|
|
920
|
+
|
|
809
921
|
def message_flags(eml_id)
|
|
810
922
|
return [] unless eml_id
|
|
811
923
|
reader_for("#flags")&.flags_for(eml_id.to_i) || []
|
|
@@ -906,7 +1018,9 @@ module Mailmate
|
|
|
906
1018
|
when :date
|
|
907
1019
|
date_matches?(mail, eml_id, term)
|
|
908
1020
|
when :state
|
|
909
|
-
state_matches?(eml_id, mail, term)
|
|
1021
|
+
state_matches?(eml_id, mail, path, term)
|
|
1022
|
+
when :header
|
|
1023
|
+
header_matches?(eml_id, mail, term)
|
|
910
1024
|
when :any
|
|
911
1025
|
%i[from recipients subject].any? { |f| field_value(eml_id, mail, f).include?(term_b) }
|
|
912
1026
|
end
|
|
@@ -24,10 +24,11 @@ module Mailmate
|
|
|
24
24
|
["a <term>", "any address header contains"],
|
|
25
25
|
["b <term>", "body contains"],
|
|
26
26
|
["m <term>", "common headers OR body (same as a bare term)"],
|
|
27
|
-
["d <date>", "received: Nh (rolling clock hours), Nd|Nw|Nm|Ny (
|
|
27
|
+
["d <date>", "received: Nh (rolling clock hours), Nd|Nw|Nm|Ny (calendar, floored: 1d = today, 1w = this week, 1y = this year), Y | Y-M | Y-M-D, or D | M-D (day of month, most recent occurrence)"],
|
|
28
28
|
["T <tag>", "tag / IMAP keyword contains (K is a synonym)"],
|
|
29
|
-
["is:<state>", "message state: unread, read, flagged, replied, draft"],
|
|
30
|
-
["has:attachment", "root MIME is multipart/mixed (
|
|
29
|
+
["is:<state>", "message state: unread, read, flagged, replied, draft, archived"],
|
|
30
|
+
["has:attachment", "root MIME is multipart/mixed (wrappers like signed/related are opened and checked)"],
|
|
31
|
+
["<header>:<term>", "any indexed header contains (delivered-to:joe); unknown header = error"],
|
|
31
32
|
].freeze
|
|
32
33
|
|
|
33
34
|
EXAMPLES = [
|
|
@@ -39,7 +40,8 @@ module Mailmate
|
|
|
39
40
|
["d 24h", "received in the last 24 hours (rolling, not calendar)"],
|
|
40
41
|
["d >=2026-05 d <2026-08", "received May through July 2026"],
|
|
41
42
|
["d 1h or 2026-08-09", "last hour, plus everything from Aug 9"],
|
|
42
|
-
["is:unread d
|
|
43
|
+
["is:unread d 1w", "unread, received this week"],
|
|
44
|
+
["d 7", "day 7 of this month (last month's if the 7th is still ahead)"],
|
|
43
45
|
["T urgent", "tagged 'urgent'"],
|
|
44
46
|
].freeze
|
|
45
47
|
|
|
@@ -260,6 +262,14 @@ module Mailmate
|
|
|
260
262
|
(rel = parse_relative(value)) && "d #{rel}"
|
|
261
263
|
when "older_than", "older"
|
|
262
264
|
(rel = parse_relative(value)) && "d !#{rel}"
|
|
265
|
+
when "in"
|
|
266
|
+
# Archive state is path-derived, so Gmail's location idiom maps onto
|
|
267
|
+
# the is:archived state spec. Other locations (in:trash, in:spam)
|
|
268
|
+
# have no equivalent and stay flagged.
|
|
269
|
+
case value.downcase
|
|
270
|
+
when "archive", "archived" then "is:archived"
|
|
271
|
+
when "inbox" then "!is:archived"
|
|
272
|
+
end
|
|
263
273
|
end
|
|
264
274
|
end
|
|
265
275
|
|
data/lib/mailmate/version.rb
CHANGED