@bobfrankston/rmfmail 1.2.219 → 1.2.221
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/bin/mailx.js +93 -5
- package/bin/mailx.js.map +1 -1
- package/bin/mailx.ts +83 -5
- package/client/app.bundle.js +2 -2
- package/client/app.bundle.js.map +2 -2
- package/client/compose/compose.bundle.js +9 -3
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/compose/compose.js +17 -1
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +15 -1
- package/client/lib/api-client.js +2 -2
- package/client/lib/api-client.js.map +1 -1
- package/client/lib/api-client.ts +2 -2
- package/client/lib/mailxapi.js +2 -2
- package/client/prewarm.html +16 -0
- package/package.json +5 -5
- package/packages/mailx-imap/index.d.ts +9 -0
- package/packages/mailx-imap/index.d.ts.map +1 -1
- package/packages/mailx-imap/index.js +55 -1
- package/packages/mailx-imap/index.js.map +1 -1
- package/packages/mailx-imap/index.ts +50 -1
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
- package/packages/mailx-service/index.d.ts +1 -1
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +102 -26
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +89 -23
- package/packages/mailx-service/jsonrpc.js +1 -1
- package/packages/mailx-service/jsonrpc.js.map +1 -1
- package/packages/mailx-service/jsonrpc.ts +1 -1
- package/packages/mailx-service/package.json +1 -1
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-24032 → node_modules.npmglobalize-stash-39780}/.package-lock.json +0 -0
package/bin/mailx.js
CHANGED
|
@@ -2166,6 +2166,16 @@ RFC 5322 with CRLF line endings. Bodies are quoted-printable encoded (readable i
|
|
|
2166
2166
|
// main-window shutdown log line to prove/refute the "closing a popout
|
|
2167
2167
|
// closes the main window" coupling (Bob 2026-07-06, unreproduced).
|
|
2168
2168
|
let lastPopoutClose = null;
|
|
2169
|
+
/** Hidden window that keeps the "popout" WebView2 profile's browser
|
|
2170
|
+
* process warm — see the prewarm block below showService. Deliberately
|
|
2171
|
+
* NOT in popoutWindows: it is infrastructure, not a user window, and must
|
|
2172
|
+
* not show up in the shutdown popout census. */
|
|
2173
|
+
let prewarmHandle = null;
|
|
2174
|
+
/** How long after boot to warm the popout profile. Late enough that it
|
|
2175
|
+
* isn't racing the MAIN window's own WebView creation for the same CPU,
|
|
2176
|
+
* early enough to beat a user reaching for ^N (the 2026-08-05 case was at
|
|
2177
|
+
* boot + 18s). */
|
|
2178
|
+
const PREWARM_DELAY_MS = 6000;
|
|
2169
2179
|
const popoutKey = (a, f, u) => `${a}|${f ?? ""}|${u}`;
|
|
2170
2180
|
try {
|
|
2171
2181
|
const { startPopoutServer } = await import("./popout-server.js");
|
|
@@ -2571,6 +2581,54 @@ RFC 5322 with CRLF line endings. Bodies are quoted-printable encoded (readable i
|
|
|
2571
2581
|
return { ok: true };
|
|
2572
2582
|
});
|
|
2573
2583
|
}
|
|
2584
|
+
// Pre-warm the "popout" WebView2 profile.
|
|
2585
|
+
//
|
|
2586
|
+
// Every secondary window (compose, message popout, extra main window) runs
|
|
2587
|
+
// on profile "popout" — deliberately NOT the main window's profile, so a
|
|
2588
|
+
// crash there can't black out the main window (S67). The cost is that the
|
|
2589
|
+
// FIRST popout of a session pays for cold-starting an entire WebView2
|
|
2590
|
+
// browser process for that profile, and if the daemon is busy with initial
|
|
2591
|
+
// sync at the time, that cold start is not cheap: ^N 18 seconds after boot
|
|
2592
|
+
// on 2026-08-05 sat on a blank window for 59.4 SECONDS, all of it inside
|
|
2593
|
+
// WebViewBuilder::build() before a line of compose JS could run (the page
|
|
2594
|
+
// itself then rendered in 397ms). Nothing the page can do about it — there
|
|
2595
|
+
// is no WebView yet to draw a spinner into.
|
|
2596
|
+
//
|
|
2597
|
+
// So pay it up front, off the user's critical path: one hidden window on
|
|
2598
|
+
// that profile, opened a few seconds after boot (late enough not to
|
|
2599
|
+
// compete with the main window's own WebView creation), held open for the
|
|
2600
|
+
// life of the daemon so the browser process stays warm. Hidden windows
|
|
2601
|
+
// still create their WebView, which is the whole point.
|
|
2602
|
+
if (popoutInfo) {
|
|
2603
|
+
const pi3 = popoutInfo;
|
|
2604
|
+
setTimeout(() => {
|
|
2605
|
+
const t0 = Date.now();
|
|
2606
|
+
try {
|
|
2607
|
+
const h = showMessageBoxEx({
|
|
2608
|
+
title: "rmfmail (prewarm)",
|
|
2609
|
+
url: `http://127.0.0.1:${pi3.port}/app/${pi3.token}/client/prewarm.html`,
|
|
2610
|
+
size: { width: 200, height: 100 },
|
|
2611
|
+
hidden: true,
|
|
2612
|
+
focusOnCreate: false,
|
|
2613
|
+
aumid: "com.frankston.rmfmail",
|
|
2614
|
+
profile: "popout",
|
|
2615
|
+
});
|
|
2616
|
+
prewarmHandle = h;
|
|
2617
|
+
if (typeof h.pid === "number")
|
|
2618
|
+
addChildPid(h.pid);
|
|
2619
|
+
console.log(` [prewarm] popout WebView2 profile warming (pid=${h.pid ?? "?"})`);
|
|
2620
|
+
void h.result.catch(() => { }).finally(() => {
|
|
2621
|
+
if (typeof h.pid === "number")
|
|
2622
|
+
removeChildPid(h.pid);
|
|
2623
|
+
console.log(` [prewarm] popout profile window closed after ${Date.now() - t0}ms`);
|
|
2624
|
+
});
|
|
2625
|
+
}
|
|
2626
|
+
catch (e) {
|
|
2627
|
+
// Never fatal — a cold first popout is slow, not broken.
|
|
2628
|
+
console.log(` [prewarm] skipped: ${e?.message || e}`);
|
|
2629
|
+
}
|
|
2630
|
+
}, PREWARM_DELAY_MS);
|
|
2631
|
+
}
|
|
2574
2632
|
_tick("calling showService (WebView opens here)");
|
|
2575
2633
|
const handle = showService({
|
|
2576
2634
|
title: `rmfmail v${rootPkgVersion}`,
|
|
@@ -2656,6 +2714,12 @@ RFC 5322 with CRLF line endings. Bodies are quoted-printable encoded (readable i
|
|
|
2656
2714
|
handle.close();
|
|
2657
2715
|
}
|
|
2658
2716
|
catch { /* already gone */ }
|
|
2717
|
+
// Hidden and therefore invisible to the user if orphaned — close it on
|
|
2718
|
+
// every exit path, not just gracefulShutdown.
|
|
2719
|
+
try {
|
|
2720
|
+
prewarmHandle?.close?.();
|
|
2721
|
+
}
|
|
2722
|
+
catch { /* already gone */ }
|
|
2659
2723
|
};
|
|
2660
2724
|
process.once("exit", __cleanupInstance);
|
|
2661
2725
|
process.once("SIGINT", () => { __cleanupInstance(); process.exit(0); });
|
|
@@ -2841,10 +2905,22 @@ RFC 5322 with CRLF line endings. Bodies are quoted-printable encoded (readable i
|
|
|
2841
2905
|
// and emits this when Word writes; compose.ts listens and reloads Quill.
|
|
2842
2906
|
imapManager.on("wordEditUpdated", (payload) => {
|
|
2843
2907
|
handle.send({ _event: "wordEditUpdated", type: "wordEditUpdated", ...payload });
|
|
2844
|
-
//
|
|
2845
|
-
//
|
|
2846
|
-
//
|
|
2847
|
-
|
|
2908
|
+
// Deliberately NO { _msgerWindow: "focus" } here. The autosave sidecar
|
|
2909
|
+
// saves every ~2s while the user types, and each save lands in this
|
|
2910
|
+
// handler — so this line pulled the foreground off Word mid-sentence,
|
|
2911
|
+
// every couple of seconds, for the whole editing session (Bob
|
|
2912
|
+
// 2026-08-06: "keeps flipping back to rmfmail on any change"). The
|
|
2913
|
+
// focus now happens once, on wordEditClosed, below.
|
|
2914
|
+
});
|
|
2915
|
+
// Word (or the document) closed — the external edit is over. THIS is the
|
|
2916
|
+
// moment to bring rmfmail forward, replacing the old per-save focus grab.
|
|
2917
|
+
imapManager.on("wordEditClosed", (payload) => {
|
|
2918
|
+
handle.send({ _event: "wordEditClosed", type: "wordEditClosed", ...payload });
|
|
2919
|
+
// Only raise the MAIN window when the compose lives IN it. A popout
|
|
2920
|
+
// compose is a separate window with no service channel, so focusing
|
|
2921
|
+
// main would just cover it up.
|
|
2922
|
+
if (!payload.fromPopout)
|
|
2923
|
+
handle.send({ _msgerWindow: "focus" });
|
|
2848
2924
|
});
|
|
2849
2925
|
// Cloud-write/read failures from mailx-settings → push to UI as a banner so
|
|
2850
2926
|
// silent fall-back-to-local can no longer swallow Drive errors.
|
|
@@ -3289,6 +3365,13 @@ RFC 5322 with CRLF line endings. Bodies are quoted-printable encoded (readable i
|
|
|
3289
3365
|
return;
|
|
3290
3366
|
shuttingDown = true;
|
|
3291
3367
|
console.log(`${reason} — shutting down`);
|
|
3368
|
+
// The prewarm window is hidden, so an orphaned one is invisible AND
|
|
3369
|
+
// unkillable by the user — close it explicitly rather than relying on
|
|
3370
|
+
// child-process cascade.
|
|
3371
|
+
try {
|
|
3372
|
+
prewarmHandle?.close?.();
|
|
3373
|
+
}
|
|
3374
|
+
catch { /* already gone */ }
|
|
3292
3375
|
imapManager.stopPeriodicSync();
|
|
3293
3376
|
imapManager.stopOutboxWorker();
|
|
3294
3377
|
// 3s hard timeout — don't hang on broken IMAP connections
|
|
@@ -3345,7 +3428,12 @@ RFC 5322 with CRLF line endings. Bodies are quoted-printable encoded (readable i
|
|
|
3345
3428
|
const popoutDelta = lastPopoutClose
|
|
3346
3429
|
? `; last popout closed ${Date.now() - lastPopoutClose.at}ms ago (${lastPopoutClose.key})`
|
|
3347
3430
|
: "; no popout closed this session";
|
|
3348
|
-
|
|
3431
|
+
// Name the still-tracked popouts, not just the count. "2 popout window(s)
|
|
3432
|
+
// still tracked" (2026-08-05) was unactionable: two legitimately-open
|
|
3433
|
+
// compose windows and two entries whose close handler never fired look
|
|
3434
|
+
// identical from a bare count.
|
|
3435
|
+
const popoutKeys = popoutWindows.size > 0 ? ` [${[...popoutWindows.keys()].join(", ")}]` : "";
|
|
3436
|
+
await gracefulShutdown(`Window closed (main service child; ${popoutWindows.size} popout window(s) still tracked${popoutKeys}${popoutDelta})`);
|
|
3349
3437
|
}
|
|
3350
3438
|
main().catch(console.error);
|
|
3351
3439
|
//# sourceMappingURL=mailx.js.map
|