mailscope 0.1.0 → 0.1.1
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/CHANGELOG.md +19 -1
- data/README.md +4 -0
- data/app/assets/mailscope/mailscope.css +36 -3
- data/app/assets/mailscope/mailscope.js +65 -4
- data/app/helpers/mailscope/application_helper.rb +4 -1
- data/app/views/mailscope/messages/_confirm.html.erb +20 -0
- data/app/views/mailscope/messages/_list.html.erb +20 -17
- data/app/views/mailscope/messages/_rail.html.erb +0 -4
- data/app/views/mailscope/messages/index.html.erb +23 -18
- data/config/locales/mailscope.en.yml +9 -4
- data/config/locales/mailscope.pt-BR.yml +9 -4
- data/lib/mailscope/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 19f786326def785dfcd3da7cb0b4e41bfb4397a0f98c9fe45ad9f12b04414f45
|
|
4
|
+
data.tar.gz: d1c3d1b5eb0f9d13cba31bc51827fad2ab41ce734acd8dd591b09b6cf1fdfa50
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bf9af26bdcb82142354e62d7ab4a1398dde03952473207a48e86b4966acacd8c917553614f89fee7319f3c8bafad99f4edaea417482e2dda68d41897cc237527
|
|
7
|
+
data.tar.gz: b37911850eb6b561d12c4ea4ca8b56caa37b68a7e6a20a34bbdd3acf44142a71d90136944f6d976ecc66099e36f139fa20a96a9bf86c7396255e14196e46f895
|
data/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
-
[Unreleased]: https://github.com/renatomeloferraz/mailscope/compare/v0.1.
|
|
5
|
+
[Unreleased]: https://github.com/renatomeloferraz/mailscope/compare/v0.1.1...HEAD
|
|
6
|
+
|
|
7
|
+
## [0.1.1] — 2026-08-22
|
|
8
|
+
|
|
9
|
+
[0.1.1]: https://github.com/renatomeloferraz/mailscope/compare/v0.1.0...v0.1.1
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Clearing the inbox asks through an in-app dialog instead of `window.confirm`,
|
|
14
|
+
which could not be themed and prefixed the browser origin. Escape and the
|
|
15
|
+
backdrop cancel, focus starts on Cancel and is trapped while it is open.
|
|
16
|
+
- The header's utility controls (refresh, live, theme, shortcuts) sit in one
|
|
17
|
+
group, with the destructive Clear action kept outside it.
|
|
18
|
+
- The empty-inbox notice sits near the top of the message list, centred, and
|
|
19
|
+
now says how to make messages appear; centring it vertically in a tall empty
|
|
20
|
+
column read as a loading state.
|
|
21
|
+
- Dropped the rail's "no recipients yet" line and the reading pane's "no
|
|
22
|
+
message selected" notice when nothing has been captured — three empty states
|
|
23
|
+
for one empty inbox, and the pane invited you to pick from an empty list.
|
|
6
24
|
|
|
7
25
|
## [0.1.0] — 2026-08-22
|
|
8
26
|
|
data/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Mailscope
|
|
2
2
|
|
|
3
|
+
[](https://rubygems.org/gems/mailscope)
|
|
4
|
+
[](https://github.com/renatomeloferraz/mailscope/actions/workflows/main.yml)
|
|
5
|
+
[](LICENSE.txt)
|
|
6
|
+
|
|
3
7
|
A mail inspector for Rails development and staging environments. It captures
|
|
4
8
|
everything your app sends and gives you a fast web UI to **search, preview and
|
|
5
9
|
inspect** it: HTML, plain text, the raw `.eml`, real headers and attachments —
|
|
@@ -193,7 +193,24 @@ kbd {
|
|
|
193
193
|
.ms-search__kbd { flex: none; }
|
|
194
194
|
.ms-search:focus-within .ms-search__kbd { opacity: 0; }
|
|
195
195
|
|
|
196
|
-
.ms-topbar__actions { display: flex; align-items: center; gap:
|
|
196
|
+
.ms-topbar__actions { display: flex; align-items: center; gap: 10px; margin-left: auto; flex: none; }
|
|
197
|
+
|
|
198
|
+
/* One surface for the utility controls, so they stop floating loose next to
|
|
199
|
+
the destructive one. */
|
|
200
|
+
.ms-cluster {
|
|
201
|
+
display: flex; align-items: center; gap: 1px;
|
|
202
|
+
padding: 2px;
|
|
203
|
+
background: var(--sunken);
|
|
204
|
+
border: 1px solid var(--border-soft);
|
|
205
|
+
border-radius: 9px;
|
|
206
|
+
}
|
|
207
|
+
.ms-cluster .ms-btn,
|
|
208
|
+
.ms-cluster .ms-live { height: 28px; border-radius: 7px; }
|
|
209
|
+
.ms-cluster .ms-btn:hover,
|
|
210
|
+
.ms-cluster .ms-live:hover { background: var(--surface); }
|
|
211
|
+
.ms-cluster .ms-live:has(input:checked) { background: var(--surface); }
|
|
212
|
+
.ms-topbar__actions > .ms-btn--danger { border: 1px solid transparent; }
|
|
213
|
+
.ms-topbar__actions > .ms-btn--danger:hover { border-color: color-mix(in oklab, var(--danger) 30%, transparent); }
|
|
197
214
|
|
|
198
215
|
/* Opens the mailbox rail once it stops fitting beside the list. */
|
|
199
216
|
.ms-topbar .ms-rail-toggle { display: none; }
|
|
@@ -271,7 +288,6 @@ kbd {
|
|
|
271
288
|
font-size: 10.5px; font-weight: 680; letter-spacing: .07em; text-transform: uppercase;
|
|
272
289
|
color: var(--muted);
|
|
273
290
|
}
|
|
274
|
-
.ms-rail__hint { padding: 6px 8px; font-size: 12.5px; color: var(--muted); }
|
|
275
291
|
|
|
276
292
|
.ms-box {
|
|
277
293
|
display: flex; align-items: center; gap: 9px;
|
|
@@ -516,9 +532,14 @@ kbd {
|
|
|
516
532
|
|
|
517
533
|
/* ── empty states ───────────────────────────────────────────────────── */
|
|
518
534
|
.ms-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; text-align: center; padding: 40px 24px; }
|
|
519
|
-
|
|
535
|
+
/* Anchored to the top of the list column: centring it in a tall, otherwise
|
|
536
|
+
empty column reads as a loading state. */
|
|
537
|
+
.ms-empty--list { justify-content: flex-start; padding: 48px 22px; }
|
|
538
|
+
.ms-empty--list .ms-empty__text { max-width: 34ch; }
|
|
539
|
+
.ms-empty--list .ms-empty__text strong { color: var(--text-2); font-weight: 600; }
|
|
520
540
|
.ms-empty--pane { height: 100%; }
|
|
521
541
|
.ms-empty__icon { display: grid; place-items: center; width: 52px; height: 52px; margin-bottom: 6px; border-radius: 14px; background: var(--sunken); color: var(--muted); }
|
|
542
|
+
.ms-empty--list .ms-empty__icon { width: 34px; height: 34px; border-radius: 10px; margin-bottom: 4px; }
|
|
522
543
|
.ms-empty__icon--lg { width: 66px; height: 66px; border-radius: 18px; }
|
|
523
544
|
.ms-empty__title { font-size: 14.5px; font-weight: 620; color: var(--text); }
|
|
524
545
|
.ms-empty__text { font-size: 13px; color: var(--muted); line-height: 1.6; }
|
|
@@ -535,6 +556,18 @@ kbd {
|
|
|
535
556
|
}
|
|
536
557
|
.ms-modal__card header { display: flex; align-items: center; padding: 14px 12px 14px 18px; border-bottom: 1px solid var(--border-soft); }
|
|
537
558
|
.ms-modal__card h2 { flex: 1; font-size: 14.5px; font-weight: 620; }
|
|
559
|
+
/* Confirmation dialog */
|
|
560
|
+
.ms-modal__card--confirm { width: min(400px, calc(100vw - 32px)); }
|
|
561
|
+
.ms-modal__card--confirm header { gap: 10px; border-bottom: 0; padding: 18px 18px 0; }
|
|
562
|
+
.ms-confirm__icon {
|
|
563
|
+
display: grid; place-items: center; width: 32px; height: 32px; flex: none;
|
|
564
|
+
border-radius: 9px; background: var(--danger-soft); color: var(--danger);
|
|
565
|
+
}
|
|
566
|
+
.ms-confirm__body { padding: 8px 18px 0; color: var(--text-2); font-size: 13px; line-height: 1.55; }
|
|
567
|
+
.ms-confirm__actions { display: flex; justify-content: flex-end; gap: 8px; padding: 18px; }
|
|
568
|
+
.ms-btn--solid-danger { background: var(--danger); color: #fff; font-weight: 600; }
|
|
569
|
+
.ms-btn--solid-danger:hover { background: color-mix(in oklab, var(--danger) 85%, #000); color: #fff; }
|
|
570
|
+
|
|
538
571
|
.ms-shortcuts { padding: 8px 18px 18px; }
|
|
539
572
|
.ms-shortcuts > div { display: flex; align-items: center; gap: 12px; padding: 6px 0; }
|
|
540
573
|
.ms-shortcuts dt { flex: none; width: 88px; display: flex; gap: 4px; }
|
|
@@ -78,6 +78,55 @@
|
|
|
78
78
|
function hide() { node.classList.remove('is-visible'); }
|
|
79
79
|
})();
|
|
80
80
|
|
|
81
|
+
// ── confirmation dialog ──────────────────────────────────────────────
|
|
82
|
+
// Replaces window.confirm: that one cannot be themed, ignores the app's
|
|
83
|
+
// typography and prefixes the origin, so it reads as a browser warning
|
|
84
|
+
// rather than a deliberate action inside the tool.
|
|
85
|
+
var confirmDialog = (function () {
|
|
86
|
+
var modal = $('[data-confirm]');
|
|
87
|
+
var pending = null;
|
|
88
|
+
var lastFocused = null;
|
|
89
|
+
|
|
90
|
+
function close(accepted) {
|
|
91
|
+
if (!pending) return;
|
|
92
|
+
var resolve = pending;
|
|
93
|
+
pending = null;
|
|
94
|
+
modal.hidden = true;
|
|
95
|
+
if (lastFocused && lastFocused.isConnected) lastFocused.focus();
|
|
96
|
+
resolve(accepted);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function open(options) {
|
|
100
|
+
if (!modal) return Promise.resolve(window.confirm(options.body)); // no markup: degrade
|
|
101
|
+
|
|
102
|
+
$('[data-confirm-title]', modal).textContent = options.title;
|
|
103
|
+
$('[data-confirm-body]', modal).textContent = options.body;
|
|
104
|
+
$('[data-confirm-accept]', modal).textContent = options.accept;
|
|
105
|
+
|
|
106
|
+
lastFocused = document.activeElement;
|
|
107
|
+
modal.hidden = false;
|
|
108
|
+
// Focus the safe option, so Enter never destroys anything by reflex.
|
|
109
|
+
$('[data-confirm-cancel]', modal).focus();
|
|
110
|
+
|
|
111
|
+
return new Promise(function (resolve) { pending = resolve; });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (modal) {
|
|
115
|
+
// Keep Tab inside the dialog while it is open.
|
|
116
|
+
modal.addEventListener('keydown', function (event) {
|
|
117
|
+
if (event.key !== 'Tab') return;
|
|
118
|
+
var focusable = $$('button', modal);
|
|
119
|
+
var first = focusable[0];
|
|
120
|
+
var last = focusable[focusable.length - 1];
|
|
121
|
+
if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); }
|
|
122
|
+
else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); }
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return { open: open, cancel: function () { close(false); }, accept: function () { close(true); },
|
|
127
|
+
get isOpen() { return !!pending; } };
|
|
128
|
+
})();
|
|
129
|
+
|
|
81
130
|
// ── theme ────────────────────────────────────────────────────────────
|
|
82
131
|
function currentTheme() { return document.documentElement.dataset.theme || 'system'; }
|
|
83
132
|
|
|
@@ -345,9 +394,15 @@
|
|
|
345
394
|
.catch(function () { toast(t('deleteFailed')); });
|
|
346
395
|
}
|
|
347
396
|
|
|
348
|
-
function clearAll(url) {
|
|
349
|
-
|
|
397
|
+
function clearAll(url, count) {
|
|
398
|
+
confirmDialog.open({
|
|
399
|
+
title: t('confirmClearTitle'),
|
|
400
|
+
body: count === 1 ? t('confirmClearBodyOne') : t('confirmClearBody', { count: count }),
|
|
401
|
+
accept: t('confirmClearAccept')
|
|
402
|
+
}).then(function (accepted) { if (accepted) performClear(url); });
|
|
403
|
+
}
|
|
350
404
|
|
|
405
|
+
function performClear(url) {
|
|
351
406
|
request(url, { method: 'DELETE', headers: { Accept: 'application/json' } })
|
|
352
407
|
.then(function (r) { return r.ok ? r.json() : Promise.reject(r.status); })
|
|
353
408
|
.then(function (data) {
|
|
@@ -417,13 +472,14 @@
|
|
|
417
472
|
if (event.metaKey || event.ctrlKey || event.altKey) return;
|
|
418
473
|
|
|
419
474
|
if (event.key === 'Escape') {
|
|
475
|
+
if (confirmDialog.isOpen) { confirmDialog.cancel(); return; }
|
|
420
476
|
if (!$('[data-shortcuts]').hidden) { $('[data-shortcuts]').hidden = true; return; }
|
|
421
477
|
closeRail();
|
|
422
478
|
if (typingInField(event)) event.target.blur();
|
|
423
479
|
return;
|
|
424
480
|
}
|
|
425
481
|
|
|
426
|
-
if (typingInField(event)) return;
|
|
482
|
+
if (typingInField(event) || confirmDialog.isOpen) return;
|
|
427
483
|
|
|
428
484
|
switch (event.key) {
|
|
429
485
|
case 'j': case 'ArrowDown': event.preventDefault(); moveSelection(1); break;
|
|
@@ -494,7 +550,12 @@
|
|
|
494
550
|
}
|
|
495
551
|
case 'shortcuts': event.preventDefault(); $('[data-shortcuts]').hidden = false; break;
|
|
496
552
|
case 'close-shortcuts': event.preventDefault(); $('[data-shortcuts]').hidden = true; break;
|
|
497
|
-
case 'clear':
|
|
553
|
+
case 'clear':
|
|
554
|
+
event.preventDefault();
|
|
555
|
+
clearAll(action.dataset.url, parseInt(action.dataset.count, 10) || 0);
|
|
556
|
+
break;
|
|
557
|
+
case 'confirm-cancel': event.preventDefault(); confirmDialog.cancel(); break;
|
|
558
|
+
case 'confirm-accept': event.preventDefault(); confirmDialog.accept(); break;
|
|
498
559
|
case 'delete-item': {
|
|
499
560
|
event.preventDefault();
|
|
500
561
|
var item = action.closest('[data-item]');
|
|
@@ -49,7 +49,10 @@ module Mailscope
|
|
|
49
49
|
# bundle stays a static, cacheable asset with no locale baked in.
|
|
50
50
|
def mailscope_js_strings
|
|
51
51
|
{
|
|
52
|
-
|
|
52
|
+
confirmClearTitle: ms_t('confirm.clear_title'),
|
|
53
|
+
confirmClearBodyOne: ms_t('confirm.clear_body', count: 1),
|
|
54
|
+
confirmClearBody: ms_t('confirm.clear_body', count: 2).sub('2', '%{count}'),
|
|
55
|
+
confirmClearAccept: ms_t('confirm.clear_accept'),
|
|
53
56
|
deleted: ms_t('toasts.deleted'),
|
|
54
57
|
deleteFailed: ms_t('toasts.delete_failed'),
|
|
55
58
|
clearedOne: ms_t('toasts.cleared', count: 1),
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<%# Generic confirmation dialog. The copy is filled in by mailscope.js so one
|
|
2
|
+
element serves every destructive action. %>
|
|
3
|
+
<div class="ms-modal" data-confirm hidden>
|
|
4
|
+
<div class="ms-modal__backdrop" data-action="confirm-cancel"></div>
|
|
5
|
+
<div class="ms-modal__card ms-modal__card--confirm" role="alertdialog" aria-modal="true"
|
|
6
|
+
aria-labelledby="ms-confirm-title" aria-describedby="ms-confirm-body">
|
|
7
|
+
<header>
|
|
8
|
+
<span class="ms-confirm__icon" aria-hidden="true"><%= mailscope_icon('i-trash', size: 18) %></span>
|
|
9
|
+
<h2 id="ms-confirm-title" data-confirm-title></h2>
|
|
10
|
+
</header>
|
|
11
|
+
<p class="ms-confirm__body" id="ms-confirm-body" data-confirm-body></p>
|
|
12
|
+
<footer class="ms-confirm__actions">
|
|
13
|
+
<button type="button" class="ms-btn ms-btn--soft" data-action="confirm-cancel" data-confirm-cancel>
|
|
14
|
+
<%= ms_t('actions.cancel') %>
|
|
15
|
+
</button>
|
|
16
|
+
<button type="button" class="ms-btn ms-btn--solid-danger" data-action="confirm-accept" data-confirm-accept>
|
|
17
|
+
</button>
|
|
18
|
+
</footer>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
@@ -11,24 +11,27 @@
|
|
|
11
11
|
</div>
|
|
12
12
|
</header>
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
</ol>
|
|
20
|
-
|
|
21
|
-
<% if @messages.empty? %>
|
|
22
|
-
<div class="ms-empty ms-empty--list">
|
|
23
|
-
<span class="ms-empty__icon"><%= mailscope_icon('i-inbox', size: 28) %></span>
|
|
24
|
-
<% if @query.filtered? %>
|
|
25
|
-
<p class="ms-empty__title"><%= ms_t('empty.no_results_title') %></p>
|
|
26
|
-
<p class="ms-empty__text"><%= ms_t('empty.no_results_text') %></p>
|
|
27
|
-
<a class="ms-btn ms-btn--soft" href="<%= root_path %>"><%= ms_t('actions.clear_filters_button') %></a>
|
|
28
|
-
<% else %>
|
|
29
|
-
<p class="ms-empty__title"><%= ms_t('empty.inbox_title') %></p>
|
|
30
|
-
<p class="ms-empty__text"><%= ms_t('empty.inbox_text') %></p>
|
|
14
|
+
<% if @messages.any? %>
|
|
15
|
+
<ol class="ms-list__items" data-list role="listbox" tabindex="0"
|
|
16
|
+
aria-label="<%= ms_t('list.listbox') %>">
|
|
17
|
+
<% @messages.each do |message| %>
|
|
18
|
+
<%= render 'mailscope/messages/list_item', message: message, selected: @message&.id == message.id %>
|
|
31
19
|
<% end %>
|
|
20
|
+
</ol>
|
|
21
|
+
<% elsif @query.filtered? %>
|
|
22
|
+
<div class="ms-empty ms-empty--list">
|
|
23
|
+
<span class="ms-empty__icon"><%= mailscope_icon('i-search', size: 24) %></span>
|
|
24
|
+
<p class="ms-empty__title"><%= ms_t('empty.no_results_title') %></p>
|
|
25
|
+
<p class="ms-empty__text"><%= ms_t('empty.no_results_text') %></p>
|
|
26
|
+
<a class="ms-btn ms-btn--soft" href="<%= root_path %>"><%= ms_t('actions.clear_filters_button') %></a>
|
|
27
|
+
</div>
|
|
28
|
+
<% else %>
|
|
29
|
+
<div class="ms-empty ms-empty--list">
|
|
30
|
+
<span class="ms-empty__icon"><%= mailscope_icon('i-inbox', size: 24) %></span>
|
|
31
|
+
<p class="ms-empty__title"><%= ms_t('empty.inbox_title') %></p>
|
|
32
|
+
<p class="ms-empty__text">
|
|
33
|
+
<%= ms_t('empty.inbox_text_html', live: tag.strong(ms_t('actions.live'))) %>
|
|
34
|
+
</p>
|
|
32
35
|
</div>
|
|
33
36
|
<% end %>
|
|
34
37
|
</section>
|
|
@@ -8,10 +8,6 @@
|
|
|
8
8
|
<span class="ms-box__count"><%= @all.size %></span>
|
|
9
9
|
</a>
|
|
10
10
|
|
|
11
|
-
<% if @mailboxes.empty? %>
|
|
12
|
-
<p class="ms-rail__hint"><%= ms_t('rail.empty') %></p>
|
|
13
|
-
<% end %>
|
|
14
|
-
|
|
15
11
|
<% @mailboxes.each do |box| %>
|
|
16
12
|
<a class="ms-box <%= 'is-active' if @mailbox == box.address %>"
|
|
17
13
|
href="<%= mailscope_filter_url(mailbox: box.address) %>"
|
|
@@ -22,28 +22,32 @@
|
|
|
22
22
|
</form>
|
|
23
23
|
|
|
24
24
|
<div class="ms-topbar__actions">
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
<%# Utilities travel together; the destructive action stands apart. %>
|
|
26
|
+
<div class="ms-cluster">
|
|
27
|
+
<button type="button" class="ms-btn ms-btn--ghost" data-action="refresh" title="<%= ms_t('actions.refresh_hint') %>">
|
|
28
|
+
<%= mailscope_icon('i-refresh') %><span class="ms-sr"><%= ms_t('actions.refresh') %></span>
|
|
29
|
+
</button>
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
<label class="ms-live" title="<%= ms_t('actions.live_hint') %>">
|
|
32
|
+
<input type="checkbox" data-live-toggle <%= 'checked' if mailscope_config.auto_refresh %>>
|
|
33
|
+
<span class="ms-live__dot" aria-hidden="true"></span>
|
|
34
|
+
<span class="ms-live__label"><%= ms_t('actions.live') %></span>
|
|
35
|
+
</label>
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
<button type="button" class="ms-btn ms-btn--ghost" data-action="theme" title="<%= ms_t('actions.theme') %>">
|
|
38
|
+
<span data-theme-icon="light"><%= mailscope_icon('i-sun') %></span>
|
|
39
|
+
<span data-theme-icon="dark"><%= mailscope_icon('i-moon') %></span>
|
|
40
|
+
<span class="ms-sr"><%= ms_t('actions.theme') %></span>
|
|
41
|
+
</button>
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
<button type="button" class="ms-btn ms-btn--ghost" data-action="shortcuts" title="<%= ms_t('actions.shortcuts_hint') %>">
|
|
44
|
+
<%= mailscope_icon('i-keyboard') %><span class="ms-sr"><%= ms_t('actions.shortcuts') %></span>
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
44
47
|
|
|
45
48
|
<button type="button" class="ms-btn ms-btn--danger" data-action="clear"
|
|
46
|
-
data-url="<%= clear_messages_path %>" <%=
|
|
49
|
+
data-url="<%= clear_messages_path %>" data-count="<%= @all.size %>"
|
|
50
|
+
<%= 'disabled' if @all.empty? %>>
|
|
47
51
|
<%= mailscope_icon('i-trash') %><span><%= ms_t('actions.clear') %></span>
|
|
48
52
|
</button>
|
|
49
53
|
</div>
|
|
@@ -56,11 +60,12 @@
|
|
|
56
60
|
<section class="ms-pane" data-pane aria-live="polite">
|
|
57
61
|
<% if @message %>
|
|
58
62
|
<%= render 'mailscope/messages/pane', message: @message %>
|
|
59
|
-
<%
|
|
63
|
+
<% elsif @all.any? %>
|
|
60
64
|
<%= render 'mailscope/messages/blank_pane' %>
|
|
61
65
|
<% end %>
|
|
62
66
|
</section>
|
|
63
67
|
</div>
|
|
64
68
|
|
|
65
69
|
<%= render 'mailscope/messages/shortcuts' %>
|
|
70
|
+
<%= render 'mailscope/messages/confirm' %>
|
|
66
71
|
</div>
|
|
@@ -12,7 +12,6 @@ en:
|
|
|
12
12
|
shortcuts: "Shortcuts"
|
|
13
13
|
shortcuts_hint: "Shortcuts (?)"
|
|
14
14
|
clear: "Clear"
|
|
15
|
-
clear_confirm: "Delete every captured message? This cannot be undone."
|
|
16
15
|
clear_filters: "clear filters"
|
|
17
16
|
clear_filters_button: "Clear filters"
|
|
18
17
|
delete: "Delete"
|
|
@@ -23,11 +22,17 @@ en:
|
|
|
23
22
|
open_preview: "Open the preview"
|
|
24
23
|
allow_remote: "Load anyway"
|
|
25
24
|
close: "Close"
|
|
25
|
+
cancel: "Cancel"
|
|
26
|
+
confirm:
|
|
27
|
+
clear_title: "Delete every captured message?"
|
|
28
|
+
clear_body:
|
|
29
|
+
one: "The message is removed from disk. This cannot be undone."
|
|
30
|
+
other: "All %{count} messages are removed from disk. This cannot be undone."
|
|
31
|
+
clear_accept: "Delete all"
|
|
26
32
|
rail:
|
|
27
33
|
heading: "Mailboxes"
|
|
28
34
|
label: "Mailboxes"
|
|
29
35
|
all: "All messages"
|
|
30
|
-
empty: "No recipients yet."
|
|
31
36
|
list:
|
|
32
37
|
label: "Messages"
|
|
33
38
|
listbox: "Message list"
|
|
@@ -42,8 +47,8 @@ en:
|
|
|
42
47
|
empty:
|
|
43
48
|
no_results_title: "Nothing found"
|
|
44
49
|
no_results_text: "No message matches these filters."
|
|
45
|
-
inbox_title: "
|
|
46
|
-
|
|
50
|
+
inbox_title: "No messages yet"
|
|
51
|
+
inbox_text_html: "Anything your app sends lands here. Turn on %{live} in the header to watch messages arrive without reloading."
|
|
47
52
|
no_selection_title: "No message selected"
|
|
48
53
|
no_selection_text_html: "Pick a message on the left, or send a new one from your app.<br>Use %{keys} to move around and %{search} to search."
|
|
49
54
|
message:
|
|
@@ -12,7 +12,6 @@ pt-BR:
|
|
|
12
12
|
shortcuts: "Atalhos"
|
|
13
13
|
shortcuts_hint: "Atalhos (?)"
|
|
14
14
|
clear: "Limpar"
|
|
15
|
-
clear_confirm: "Apagar todas as mensagens capturadas? Isso não tem volta."
|
|
16
15
|
clear_filters: "limpar filtros"
|
|
17
16
|
clear_filters_button: "Limpar filtros"
|
|
18
17
|
delete: "Apagar"
|
|
@@ -23,11 +22,17 @@ pt-BR:
|
|
|
23
22
|
open_preview: "Abrir a pré-visualização"
|
|
24
23
|
allow_remote: "Carregar mesmo assim"
|
|
25
24
|
close: "Fechar"
|
|
25
|
+
cancel: "Cancelar"
|
|
26
|
+
confirm:
|
|
27
|
+
clear_title: "Apagar todas as mensagens capturadas?"
|
|
28
|
+
clear_body:
|
|
29
|
+
one: "A mensagem é removida do disco. Isso não tem volta."
|
|
30
|
+
other: "As %{count} mensagens são removidas do disco. Isso não tem volta."
|
|
31
|
+
clear_accept: "Apagar tudo"
|
|
26
32
|
rail:
|
|
27
33
|
heading: "Caixas"
|
|
28
34
|
label: "Caixas de e-mail"
|
|
29
35
|
all: "Todas as mensagens"
|
|
30
|
-
empty: "Nenhum destinatário ainda."
|
|
31
36
|
list:
|
|
32
37
|
label: "Mensagens"
|
|
33
38
|
listbox: "Lista de mensagens"
|
|
@@ -42,8 +47,8 @@ pt-BR:
|
|
|
42
47
|
empty:
|
|
43
48
|
no_results_title: "Nada encontrado"
|
|
44
49
|
no_results_text: "Nenhuma mensagem bate com esses filtros."
|
|
45
|
-
inbox_title: "
|
|
46
|
-
|
|
50
|
+
inbox_title: "Nenhuma mensagem ainda"
|
|
51
|
+
inbox_text_html: "Tudo que a aplicação enviar cai aqui. Ative %{live} no topo para ver as mensagens chegarem sem recarregar."
|
|
47
52
|
no_selection_title: "Nenhuma mensagem selecionada"
|
|
48
53
|
no_selection_text_html: "Escolha uma mensagem à esquerda, ou envie uma nova pela aplicação.<br>Use %{keys} para navegar e %{search} para buscar."
|
|
49
54
|
message:
|
data/lib/mailscope/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mailscope
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Renato Ferraz
|
|
@@ -76,6 +76,7 @@ files:
|
|
|
76
76
|
- app/helpers/mailscope/application_helper.rb
|
|
77
77
|
- app/views/layouts/mailscope/application.html.erb
|
|
78
78
|
- app/views/mailscope/messages/_blank_pane.html.erb
|
|
79
|
+
- app/views/mailscope/messages/_confirm.html.erb
|
|
79
80
|
- app/views/mailscope/messages/_list.html.erb
|
|
80
81
|
- app/views/mailscope/messages/_list_item.html.erb
|
|
81
82
|
- app/views/mailscope/messages/_pane.html.erb
|