@bobfrankston/mailx 1.0.15 → 1.0.17
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/client/app.js
CHANGED
|
@@ -309,6 +309,19 @@ window.addEventListener("message", (e) => {
|
|
|
309
309
|
if (e.data?.type === "openLink" && e.data.url) {
|
|
310
310
|
window.open(e.data.url, "_blank", "noopener,noreferrer");
|
|
311
311
|
}
|
|
312
|
+
if (e.data?.type === "linkHover") {
|
|
313
|
+
const statusEl = document.getElementById("status-sync");
|
|
314
|
+
if (statusEl) {
|
|
315
|
+
if (e.data.url) {
|
|
316
|
+
statusEl.textContent = e.data.url;
|
|
317
|
+
statusEl.style.color = "var(--color-text-muted)";
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
statusEl.textContent = "";
|
|
321
|
+
statusEl.style.color = "";
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
312
325
|
});
|
|
313
326
|
// ── Splitter drag ──
|
|
314
327
|
const splitter = document.getElementById("splitter-h");
|
|
@@ -505,7 +518,8 @@ fetch("/api/version").then(r => r.json()).then(d => {
|
|
|
505
518
|
startupStatus.textContent = "Server offline — start with: node packages/mailx-server/index.js";
|
|
506
519
|
}
|
|
507
520
|
});
|
|
508
|
-
// ── Sync pending indicator ──
|
|
521
|
+
// ── Sync pending indicator + server health check ──
|
|
522
|
+
let serverDown = false;
|
|
509
523
|
setInterval(async () => {
|
|
510
524
|
try {
|
|
511
525
|
const res = await fetch("/api/sync/pending");
|
|
@@ -517,8 +531,25 @@ setInterval(async () => {
|
|
|
517
531
|
el.textContent = data.pending > 0 ? `↻ ${data.pending} pending` : "";
|
|
518
532
|
el.style.color = data.pending > 0 ? "oklch(0.75 0.15 60)" : "";
|
|
519
533
|
}
|
|
534
|
+
// Server is back — reload if it was down
|
|
535
|
+
if (serverDown) {
|
|
536
|
+
serverDown = false;
|
|
537
|
+
const statusEl = document.getElementById("status-sync");
|
|
538
|
+
if (statusEl)
|
|
539
|
+
statusEl.textContent = "Server reconnected";
|
|
540
|
+
location.reload();
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
catch {
|
|
544
|
+
if (!serverDown) {
|
|
545
|
+
serverDown = true;
|
|
546
|
+
const statusEl = document.getElementById("status-sync");
|
|
547
|
+
if (statusEl) {
|
|
548
|
+
statusEl.textContent = "SERVER OFFLINE";
|
|
549
|
+
statusEl.style.color = "oklch(0.65 0.2 25)";
|
|
550
|
+
}
|
|
551
|
+
}
|
|
520
552
|
}
|
|
521
|
-
catch { /* offline */ }
|
|
522
553
|
}, 5000);
|
|
523
554
|
console.log("mailx client initialized, location:", location.href);
|
|
524
555
|
// Diagnostic: test API connectivity (helps debug WebView2 blank screen)
|
|
@@ -6,13 +6,16 @@ import { getMessage, updateFlags } from "../lib/api-client.js";
|
|
|
6
6
|
let currentMessage = null;
|
|
7
7
|
let currentAccountId = "";
|
|
8
8
|
let showMessageGeneration = 0; // Cancel stale fetches
|
|
9
|
+
let retryCount = 0;
|
|
9
10
|
export function getCurrentMessage() {
|
|
10
11
|
if (!currentMessage)
|
|
11
12
|
return null;
|
|
12
13
|
return { accountId: currentAccountId, message: currentMessage };
|
|
13
14
|
}
|
|
14
|
-
export async function showMessage(accountId, uid, folderId, specialUse) {
|
|
15
|
+
export async function showMessage(accountId, uid, folderId, specialUse, isRetry = false) {
|
|
15
16
|
const gen = ++showMessageGeneration;
|
|
17
|
+
if (!isRetry)
|
|
18
|
+
retryCount = 0;
|
|
16
19
|
const headerEl = document.getElementById("mv-header");
|
|
17
20
|
const bodyEl = document.getElementById("mv-body");
|
|
18
21
|
const attEl = document.getElementById("mv-attachments");
|
|
@@ -221,9 +224,17 @@ export async function showMessage(accountId, uid, folderId, specialUse) {
|
|
|
221
224
|
}
|
|
222
225
|
}
|
|
223
226
|
catch (e) {
|
|
224
|
-
const
|
|
225
|
-
bodyEl.innerHTML = `<div class="mv-empty">Failed to load message: ${msg}<br><button onclick="location.reload()">Retry</button></div>`;
|
|
227
|
+
const err = e.message || "Unknown error";
|
|
226
228
|
console.error("showMessage error:", e);
|
|
229
|
+
if (retryCount < 3) {
|
|
230
|
+
retryCount++;
|
|
231
|
+
bodyEl.innerHTML = `<div class="mv-empty">Loading failed: ${err} — retrying (${retryCount}/3)...</div>`;
|
|
232
|
+
setTimeout(() => { if (gen === showMessageGeneration)
|
|
233
|
+
showMessage(accountId, uid, folderId, specialUse, true); }, 3000);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
bodyEl.innerHTML = `<div class="mv-empty">Failed to load message: ${err}</div>`;
|
|
237
|
+
}
|
|
227
238
|
}
|
|
228
239
|
}
|
|
229
240
|
function formatAddr(addr) {
|
|
@@ -254,7 +265,7 @@ function wrapHtmlBody(html, allowRemote = false) {
|
|
|
254
265
|
// CSP blocks remote resource loading (tracking pixels, external CSS) but allows link clicks
|
|
255
266
|
const csp = allowRemote
|
|
256
267
|
? ""
|
|
257
|
-
: `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data: cid:; form-action 'none';">`;
|
|
268
|
+
: `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data: cid:; form-action 'none';">`;
|
|
258
269
|
return `<!DOCTYPE html>
|
|
259
270
|
<html><head>
|
|
260
271
|
<meta charset="UTF-8">
|
|
@@ -282,6 +293,12 @@ ${csp}
|
|
|
282
293
|
}
|
|
283
294
|
</style>
|
|
284
295
|
<base target="_blank">
|
|
296
|
+
<script>
|
|
297
|
+
document.addEventListener("mouseover", e => {
|
|
298
|
+
const a = e.target.closest("a[href]");
|
|
299
|
+
window.parent.postMessage({ type: "linkHover", url: a ? a.href : "" }, "*");
|
|
300
|
+
});
|
|
301
|
+
</script>
|
|
285
302
|
</head><body>${html}</body></html>`;
|
|
286
303
|
}
|
|
287
304
|
//# sourceMappingURL=message-viewer.js.map
|
package/client/lib/api-client.js
CHANGED
|
@@ -19,10 +19,19 @@ function newMessageListSignal() {
|
|
|
19
19
|
return messageListAbort.signal;
|
|
20
20
|
}
|
|
21
21
|
async function api(path, options) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
let res;
|
|
23
|
+
try {
|
|
24
|
+
res = await fetch(`/api${path}`, {
|
|
25
|
+
headers: { "Content-Type": "application/json" },
|
|
26
|
+
...options
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
// Network error — server is down
|
|
31
|
+
if (e.name === "AbortError")
|
|
32
|
+
throw e;
|
|
33
|
+
throw new Error("Server offline — restart with launch.ps1");
|
|
34
|
+
}
|
|
26
35
|
if (!res.ok) {
|
|
27
36
|
const err = await res.json().catch(() => ({ error: res.statusText }));
|
|
28
37
|
throw new Error(err.error || res.statusText);
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
background: var(--color-bg-surface);
|
|
93
93
|
color: var(--color-text);
|
|
94
94
|
font-size: var(--font-size-sm);
|
|
95
|
-
width:
|
|
95
|
+
width: 500px;
|
|
96
96
|
|
|
97
97
|
&::placeholder { color: var(--color-text-muted); }
|
|
98
98
|
&:focus { outline: 1px solid var(--color-accent); border-color: var(--color-accent); }
|
|
@@ -1,21 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* mailx postinstall —
|
|
3
|
+
* mailx postinstall — sets binary permissions on Linux/Mac.
|
|
4
|
+
* Tries shared rust-builder; falls back to inline logic if not available.
|
|
4
5
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
try {
|
|
7
|
+
const { runPostinstall } = await import("@bobfrankston/rust-builder/postinstall");
|
|
8
|
+
const path = await import("path");
|
|
9
|
+
const { fileURLToPath } = await import("url");
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
runPostinstall({
|
|
13
|
+
binaryName: "mailx-app",
|
|
14
|
+
binDir: path.join(__dirname, "..", "bin"),
|
|
15
|
+
binaries: {
|
|
16
|
+
win32: "mailx-app.exe",
|
|
17
|
+
darwin: "mailx-app",
|
|
18
|
+
darwinArm64: "mailx-app-arm64",
|
|
19
|
+
linux: "mailx-app-linux",
|
|
20
|
+
linuxArm64: "mailx-app-linux-aarch64",
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
} catch {
|
|
24
|
+
// rust-builder not available (e.g., local dev with file: deps)
|
|
25
|
+
// On Windows, nothing to do. On Linux/Mac, try chmod directly.
|
|
26
|
+
if (process.platform !== "win32") {
|
|
27
|
+
const fs = await import("fs");
|
|
28
|
+
const path = await import("path");
|
|
29
|
+
const { fileURLToPath } = await import("url");
|
|
30
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
31
|
+
const arch = process.arch;
|
|
32
|
+
const name = process.platform === "darwin"
|
|
33
|
+
? (arch === "arm64" ? "mailx-app-arm64" : "mailx-app")
|
|
34
|
+
: (arch === "arm64" ? "mailx-app-linux-aarch64" : "mailx-app-linux");
|
|
35
|
+
const bin = path.join(__dirname, "..", "bin", name);
|
|
36
|
+
try { fs.chmodSync(bin, 0o755); } catch { /* binary may not exist */ }
|
|
37
|
+
}
|
|
38
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
4
4
|
"description": "Local-first email client with IMAP sync and standalone native app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/mailx.js",
|
|
@@ -44,6 +44,9 @@
|
|
|
44
44
|
],
|
|
45
45
|
"author": "Bob Frankston",
|
|
46
46
|
"license": "MIT",
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
47
50
|
"repository": {
|
|
48
51
|
"type": "git",
|
|
49
52
|
"url": "https://github.com/BobFrankston/mailx.git"
|
|
@@ -233,6 +233,14 @@ process.on("SIGTERM", shutdown);
|
|
|
233
233
|
process.on("unhandledRejection", (err) => {
|
|
234
234
|
console.error("Unhandled rejection:", err?.message || err);
|
|
235
235
|
});
|
|
236
|
+
process.on("uncaughtException", (err) => {
|
|
237
|
+
console.error("FATAL uncaught exception:", err.message);
|
|
238
|
+
console.error(err.stack);
|
|
239
|
+
// Don't exit — let node --watch handle restart
|
|
240
|
+
});
|
|
241
|
+
process.on("exit", (code) => {
|
|
242
|
+
console.log(`Process exiting with code ${code}`);
|
|
243
|
+
});
|
|
236
244
|
// ── Entry Point ──
|
|
237
245
|
await start();
|
|
238
246
|
export { app, start };
|