@bobfrankston/mailx 1.0.224 → 1.0.225
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.
- package/README.md +39 -5
- package/client/compose/compose.css +23 -0
- package/client/compose/compose.html +6 -2
- package/client/compose/compose.js +32 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -137,18 +137,28 @@ Gmail OAuth requires a one-time Google Cloud setup:
|
|
|
137
137
|
- **Ctrl+R** -- Reply
|
|
138
138
|
- **Ctrl+Shift+R** -- Reply All
|
|
139
139
|
- **Ctrl+F** -- Forward
|
|
140
|
-
- From dropdown lets you pick which account to send from
|
|
140
|
+
- From dropdown lets you pick which account to send from; reply auto-detects which identity to reply from based on which of your addresses the mail was sent to
|
|
141
141
|
- Contact autocomplete searches Google Contacts as you type in To/Cc/Bcc
|
|
142
|
-
-
|
|
142
|
+
- **Cc / Bcc** are hidden by default — click the toggle buttons next to To to show them
|
|
143
|
+
- **Attach** opens a file picker; attachments show as chips with remove buttons
|
|
144
|
+
- Drafts auto-save 1.5s after you stop typing, plus a 5s safety-net interval, plus on window close
|
|
145
|
+
- Compose window close asks Save / Discard / Cancel if there's content
|
|
146
|
+
- Address validation (`local@domain.tld`) runs on To/Cc/Bcc/From before sending — invalid addresses are refused
|
|
147
|
+
- **Editor shortcuts**: Ctrl+K insert link, Ctrl+Shift+K remove link, Ctrl+Shift+X strikethrough, Ctrl+Shift+7/8 ordered/bullet list, Ctrl+]/[ indent/outdent, Ctrl+Shift+C color, Ctrl+\ clear formatting. Native spell-check via WebView2 (right-click to add to dictionary).
|
|
148
|
+
- **Link editor modal**: Ctrl+K opens a two-field dialog (text + URL) with Remove-link button; hovering any link in the editor shows a floating URL preview
|
|
149
|
+
- **Paste URL** auto-links: paste a bare URL over a selection and it wraps it, or paste into empty space to insert as a link
|
|
143
150
|
|
|
144
151
|
### Managing Messages
|
|
145
152
|
|
|
146
153
|
- **Delete** or **Ctrl+D** -- Delete selected messages (moves to Trash)
|
|
147
|
-
- **Ctrl+Z** -- Undo last delete
|
|
154
|
+
- **Ctrl+Z** -- Undo the last **delete or move** (whichever came last, 60s window)
|
|
148
155
|
- **Ctrl+A** -- Select all messages in the list
|
|
149
156
|
- **Drag and drop** -- Move messages to a folder by dragging them
|
|
150
157
|
- Click the **star** column to flag/unflag a message
|
|
151
|
-
- **Unsubscribe** button appears when the message has a List-Unsubscribe header
|
|
158
|
+
- **Unsubscribe** button appears when the message has a List-Unsubscribe header (one-click)
|
|
159
|
+
- **Right-click on a From/To/Cc address** -- Copy name, Copy address, Copy both, Add to contacts, or Reply/Reply All/Forward
|
|
160
|
+
- **Preview pane zoom** -- Ctrl+wheel, Ctrl+= / Ctrl+- / Ctrl+0, or right-click menu (Zoom in/out/reset, Copy, Select all). Persisted across messages.
|
|
161
|
+
- **Cross-folder search results** show the folder name for each hit
|
|
152
162
|
|
|
153
163
|
### Searching
|
|
154
164
|
|
|
@@ -191,11 +201,35 @@ Under **Settings** in the toolbar:
|
|
|
191
201
|
| Ctrl+Shift+R | Reply All |
|
|
192
202
|
| Ctrl+F | Forward |
|
|
193
203
|
| Delete / Ctrl+D | Delete |
|
|
194
|
-
| Ctrl+Z | Undo delete |
|
|
204
|
+
| Ctrl+Z | Undo last delete or move |
|
|
195
205
|
| Ctrl+A | Select all |
|
|
196
206
|
| F5 | Sync all folders |
|
|
197
207
|
| Escape | Clear search / close menus |
|
|
198
208
|
|
|
209
|
+
**In the compose editor:**
|
|
210
|
+
|
|
211
|
+
| Key | Action |
|
|
212
|
+
|-----|--------|
|
|
213
|
+
| Ctrl+K | Insert / edit link (opens dialog with text + URL fields) |
|
|
214
|
+
| Ctrl+Shift+K | Remove link |
|
|
215
|
+
| Ctrl+B / Ctrl+I / Ctrl+U | Bold / Italic / Underline |
|
|
216
|
+
| Ctrl+Shift+X | Strikethrough |
|
|
217
|
+
| Ctrl+Shift+7 / 8 | Ordered / Bullet list |
|
|
218
|
+
| Ctrl+] / Ctrl+[ | Indent / Outdent |
|
|
219
|
+
| Ctrl+Shift+C | Set text color |
|
|
220
|
+
| Ctrl+\ | Clear formatting |
|
|
221
|
+
| Ctrl+Enter | Send |
|
|
222
|
+
| Escape | Close (prompts Save / Discard / Cancel) |
|
|
223
|
+
|
|
224
|
+
**In the preview pane:**
|
|
225
|
+
|
|
226
|
+
| Key | Action |
|
|
227
|
+
|-----|--------|
|
|
228
|
+
| Ctrl+wheel | Zoom in/out |
|
|
229
|
+
| Ctrl+= / Ctrl+- | Zoom in / out |
|
|
230
|
+
| Ctrl+0 | Reset zoom |
|
|
231
|
+
| Delete | Delete message (also works with focus in preview) |
|
|
232
|
+
|
|
199
233
|
## Command Line
|
|
200
234
|
|
|
201
235
|
```
|
|
@@ -324,6 +324,29 @@ body {
|
|
|
324
324
|
}
|
|
325
325
|
.compose-att-chip button:hover { color: oklch(0.65 0.2 25); }
|
|
326
326
|
|
|
327
|
+
/* Cc/Bcc toggle buttons in the To row */
|
|
328
|
+
.compose-recipient-toggle {
|
|
329
|
+
display: inline-flex;
|
|
330
|
+
gap: 4px;
|
|
331
|
+
margin-left: var(--gap-xs);
|
|
332
|
+
}
|
|
333
|
+
.compose-toggle-btn {
|
|
334
|
+
background: transparent;
|
|
335
|
+
border: 1px solid var(--color-border);
|
|
336
|
+
border-radius: var(--radius-sm);
|
|
337
|
+
color: var(--color-text-muted);
|
|
338
|
+
padding: 1px 8px;
|
|
339
|
+
font-size: var(--font-size-sm);
|
|
340
|
+
cursor: pointer;
|
|
341
|
+
font-family: inherit;
|
|
342
|
+
}
|
|
343
|
+
.compose-toggle-btn:hover { background: var(--color-bg-hover); color: var(--color-text); }
|
|
344
|
+
.compose-toggle-btn.active {
|
|
345
|
+
background: var(--color-accent);
|
|
346
|
+
color: #fff;
|
|
347
|
+
border-color: transparent;
|
|
348
|
+
}
|
|
349
|
+
|
|
327
350
|
/* Link editor modal (Ctrl+K / toolbar link button) */
|
|
328
351
|
.mailx-modal-backdrop {
|
|
329
352
|
position: fixed;
|
|
@@ -20,12 +20,16 @@
|
|
|
20
20
|
<div class="compose-field">
|
|
21
21
|
<label for="compose-to">To</label>
|
|
22
22
|
<input type="text" id="compose-to" autocomplete="off">
|
|
23
|
+
<span class="compose-recipient-toggle">
|
|
24
|
+
<button type="button" class="compose-toggle-btn" id="btn-toggle-cc" title="Show/hide Cc">Cc</button>
|
|
25
|
+
<button type="button" class="compose-toggle-btn" id="btn-toggle-bcc" title="Show/hide Bcc">Bcc</button>
|
|
26
|
+
</span>
|
|
23
27
|
</div>
|
|
24
|
-
<div class="compose-field">
|
|
28
|
+
<div class="compose-field" id="compose-cc-row" hidden>
|
|
25
29
|
<label for="compose-cc">Cc</label>
|
|
26
30
|
<input type="text" id="compose-cc" autocomplete="off">
|
|
27
31
|
</div>
|
|
28
|
-
<div class="compose-field">
|
|
32
|
+
<div class="compose-field" id="compose-bcc-row" hidden>
|
|
29
33
|
<label for="compose-bcc">Bcc</label>
|
|
30
34
|
<input type="text" id="compose-bcc" autocomplete="off">
|
|
31
35
|
</div>
|
|
@@ -275,6 +275,15 @@ function applyInit(init) {
|
|
|
275
275
|
toInput.value = formatAddrs(init.to);
|
|
276
276
|
ccInput.value = formatAddrs(init.cc);
|
|
277
277
|
subjectInput.value = init.subject;
|
|
278
|
+
// Auto-expand Cc row if the init already has Cc content (reply-all, draft-with-cc)
|
|
279
|
+
if (ccInput.value.trim()) {
|
|
280
|
+
const ccRowEl = document.getElementById("compose-cc-row");
|
|
281
|
+
const ccBtn = document.getElementById("btn-toggle-cc");
|
|
282
|
+
if (ccRowEl)
|
|
283
|
+
ccRowEl.hidden = false;
|
|
284
|
+
if (ccBtn)
|
|
285
|
+
ccBtn.classList.add("active");
|
|
286
|
+
}
|
|
278
287
|
if (init.bodyHtml) {
|
|
279
288
|
editor.setHtml(init.bodyHtml);
|
|
280
289
|
editor.setCursor(0);
|
|
@@ -497,6 +506,29 @@ async function handleCloseRequest() {
|
|
|
497
506
|
document.getElementById("btn-discard")?.addEventListener("click", () => {
|
|
498
507
|
handleCloseRequest();
|
|
499
508
|
});
|
|
509
|
+
// ── Cc / Bcc toggle ──
|
|
510
|
+
const ccRow = document.getElementById("compose-cc-row");
|
|
511
|
+
const bccRow = document.getElementById("compose-bcc-row");
|
|
512
|
+
const toggleCcBtn = document.getElementById("btn-toggle-cc");
|
|
513
|
+
const toggleBccBtn = document.getElementById("btn-toggle-bcc");
|
|
514
|
+
function setCcVisible(visible) {
|
|
515
|
+
ccRow.hidden = !visible;
|
|
516
|
+
toggleCcBtn.classList.toggle("active", visible);
|
|
517
|
+
if (visible)
|
|
518
|
+
ccInput.focus();
|
|
519
|
+
else
|
|
520
|
+
ccInput.value = "";
|
|
521
|
+
}
|
|
522
|
+
function setBccVisible(visible) {
|
|
523
|
+
bccRow.hidden = !visible;
|
|
524
|
+
toggleBccBtn.classList.toggle("active", visible);
|
|
525
|
+
if (visible)
|
|
526
|
+
bccInput.focus();
|
|
527
|
+
else
|
|
528
|
+
bccInput.value = "";
|
|
529
|
+
}
|
|
530
|
+
toggleCcBtn?.addEventListener("click", () => setCcVisible(ccRow.hidden));
|
|
531
|
+
toggleBccBtn?.addEventListener("click", () => setBccVisible(bccRow.hidden));
|
|
500
532
|
// ── Attachments ──
|
|
501
533
|
const fileInput = document.getElementById("compose-file");
|
|
502
534
|
const attEl = document.getElementById("compose-attachments");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.225",
|
|
4
4
|
"description": "Local-first email client with IMAP sync and standalone native app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/mailx.js",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@bobfrankston/iflow-node": "^0.1.2",
|
|
25
25
|
"@bobfrankston/miscinfo": "^1.0.8",
|
|
26
26
|
"@bobfrankston/oauthsupport": "^1.0.22",
|
|
27
|
-
"@bobfrankston/msger": "^0.1.
|
|
27
|
+
"@bobfrankston/msger": "^0.1.287",
|
|
28
28
|
"@capacitor/android": "^8.3.0",
|
|
29
29
|
"@capacitor/cli": "^8.3.0",
|
|
30
30
|
"@capacitor/core": "^8.3.0",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"@bobfrankston/iflow-node": "^0.1.2",
|
|
79
79
|
"@bobfrankston/miscinfo": "^1.0.8",
|
|
80
80
|
"@bobfrankston/oauthsupport": "^1.0.22",
|
|
81
|
-
"@bobfrankston/msger": "^0.1.
|
|
81
|
+
"@bobfrankston/msger": "^0.1.287",
|
|
82
82
|
"@capacitor/android": "^8.3.0",
|
|
83
83
|
"@capacitor/cli": "^8.3.0",
|
|
84
84
|
"@capacitor/core": "^8.3.0",
|