@bobfrankston/rmfmail 1.2.164 → 1.2.165
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 +100 -82
- package/bin/mailx.js.map +1 -1
- package/bin/mailx.ts +89 -71
- package/client/app.bundle.js +45 -0
- package/client/app.bundle.js.map +3 -3
- package/client/app.js +48 -0
- package/client/app.js.map +1 -1
- package/client/app.ts +47 -0
- package/client/compose/compose.bundle.js +7 -0
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/index.html +1 -0
- package/client/lib/api-client.js +7 -0
- package/client/lib/api-client.js.map +1 -1
- package/client/lib/api-client.ts +7 -0
- package/client/lib/mailxapi.js +7 -0
- package/package.json +5 -5
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
- package/packages/mailx-service/index.d.ts +15 -0
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +15 -0
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +12 -0
- package/packages/mailx-service/jsonrpc.js +2 -0
- package/packages/mailx-service/jsonrpc.js.map +1 -1
- package/packages/mailx-service/jsonrpc.ts +2 -0
- package/packages/mailx-service/package.json +1 -1
- package/packages/mailx-settings/package.json +1 -1
- package/packages/mailx-store/package.json +1 -1
- package/packages/mailx-store-web/package.json +1 -1
- package/packages/mailx-types/mailx-api.d.ts +6 -0
- package/packages/mailx-types/mailx-api.d.ts.map +1 -1
- package/packages/mailx-types/mailx-api.ts +3 -0
- package/packages/mailx-types/package.json +1 -1
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-10468 → node_modules.npmglobalize-stash-52720}/.package-lock.json +0 -0
package/bin/mailx.ts
CHANGED
|
@@ -183,53 +183,13 @@ const isDaemon = hasFlag("daemon"); // internal: re-spawned detached process
|
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const desktopPath = path.join(appsDir, "rmfmail.desktop");
|
|
194
|
-
if (unregister) {
|
|
195
|
-
try { fs.unlinkSync(desktopPath); } catch { /* not registered */ }
|
|
196
|
-
try { execSync(`update-desktop-database "${appsDir}"`); } catch { /* optional tool */ }
|
|
197
|
-
console.log("Unregistered rmfmail mailto: handler.");
|
|
198
|
-
process.exit(0);
|
|
199
|
-
}
|
|
200
|
-
const __mailtoJs = path.join(import.meta.dirname, "mailx.js");
|
|
201
|
-
const __iconPng = path.join(import.meta.dirname, "..", "client", "icon.png");
|
|
202
|
-
// Exec quoting follows the Desktop Entry spec: quoted absolute paths,
|
|
203
|
-
// bare %u field code (quoting %u breaks argument expansion).
|
|
204
|
-
const desktopEntry = [
|
|
205
|
-
"[Desktop Entry]",
|
|
206
|
-
"Type=Application",
|
|
207
|
-
"Name=rmfmail",
|
|
208
|
-
"Comment=rmfmail email client",
|
|
209
|
-
`Exec="${process.execPath}" "${__mailtoJs}" --mailto %u`,
|
|
210
|
-
`Icon=${__iconPng}`,
|
|
211
|
-
"Terminal=false",
|
|
212
|
-
"MimeType=x-scheme-handler/mailto;",
|
|
213
|
-
"Categories=Network;Email;",
|
|
214
|
-
"",
|
|
215
|
-
].join("\n");
|
|
216
|
-
fs.mkdirSync(appsDir, { recursive: true });
|
|
217
|
-
fs.writeFileSync(desktopPath, desktopEntry);
|
|
218
|
-
try {
|
|
219
|
-
execSync("xdg-mime default rmfmail.desktop x-scheme-handler/mailto");
|
|
220
|
-
} catch (e: any) {
|
|
221
|
-
console.error(`xdg-mime failed (${e.message}) — handler installed but not set as default. Set it in your DE's default-apps settings.`);
|
|
222
|
-
}
|
|
223
|
-
try { execSync(`update-desktop-database "${appsDir}"`); } catch { /* optional tool */ }
|
|
224
|
-
console.log(`Registered rmfmail as mailto: handler (${desktopPath}).`);
|
|
225
|
-
process.exit(0);
|
|
226
|
-
}
|
|
227
|
-
if (process.platform !== "win32") {
|
|
228
|
-
// macOS needs a real .app bundle with CFBundleURLTypes — LaunchServices
|
|
229
|
-
// won't register a bare script. Tracked as the macOS half of C124.
|
|
230
|
-
console.error("--register-mailto / --unregister-mailto: macOS registration not implemented yet (needs an .app bundle — TODO C124).");
|
|
231
|
-
process.exit(1);
|
|
232
|
-
}
|
|
186
|
+
/** Windows mailto: registration — registry keys via reg.exe. Shared by the
|
|
187
|
+
* `-register-mailto` CLI flag and the Settings → "Make default email app"
|
|
188
|
+
* menu action (injected into MailxService; the registry work stays HERE in
|
|
189
|
+
* the platform launcher — packages/ stay cross-platform). Returns the
|
|
190
|
+
* outcome text instead of printing so both callers can surface it. */
|
|
191
|
+
function registerMailtoWindows(unregister: boolean): { ok: boolean; message: string } {
|
|
192
|
+
const lines: string[] = [];
|
|
233
193
|
// Registered command points at the rmfmailto.exe launcher (per-app
|
|
234
194
|
// binary at %LOCALAPPDATA%\rmfmail\bin\rmfmailto.exe). The exe has its
|
|
235
195
|
// own VERSIONINFO with FileDescription="rmfmail" so the Win11 "Select
|
|
@@ -247,17 +207,16 @@ if (hasFlag("register-mailto") || hasFlag("unregister-mailto")) {
|
|
|
247
207
|
try {
|
|
248
208
|
fs.mkdirSync(__perAppBin, { recursive: true });
|
|
249
209
|
fs.copyFileSync(__pkgRmfmailtoExe, __rmfmailtoExe);
|
|
250
|
-
|
|
210
|
+
lines.push(`Installed rmfmailto.exe to ${__rmfmailtoExe}`);
|
|
251
211
|
} catch (e: any) {
|
|
252
|
-
|
|
212
|
+
lines.push(`Failed to copy rmfmailto.exe to per-app dir: ${e.message}`);
|
|
253
213
|
// Fall through and try registering the in-package path so
|
|
254
214
|
// mailto still works, just from inside node_modules.
|
|
255
215
|
}
|
|
256
216
|
}
|
|
257
217
|
const __launcherExe = fs.existsSync(__rmfmailtoExe) ? __rmfmailtoExe : __pkgRmfmailtoExe;
|
|
258
218
|
if (!fs.existsSync(__launcherExe)) {
|
|
259
|
-
|
|
260
|
-
process.exit(1);
|
|
219
|
+
return { ok: false, message: `rmfmailto.exe not found at ${__launcherExe} — package may have been built without it. Re-run 'npm run build' on the build machine.` };
|
|
261
220
|
}
|
|
262
221
|
// reg.exe value for `\"path\" \"%1\"` — double-backslash escapes for
|
|
263
222
|
// cmd.exe's shell layer, which strips one level before the value reaches
|
|
@@ -310,15 +269,15 @@ if (hasFlag("register-mailto") || hasFlag("unregister-mailto")) {
|
|
|
310
269
|
try {
|
|
311
270
|
const out = execSync(`reg query "${key}" /ve`, { stdio: ["pipe", "pipe", "pipe"] }).toString();
|
|
312
271
|
if (/mailx\.js|rmfmail/i.test(out)) {
|
|
313
|
-
try { execSync(`reg delete "${key}" /f`, { stdio: "pipe" });
|
|
272
|
+
try { execSync(`reg delete "${key}" /f`, { stdio: "pipe" }); lines.push(` cleaned leftover ${oldExe} mailto handler`); }
|
|
314
273
|
catch { /* */ }
|
|
315
274
|
}
|
|
316
275
|
} catch { /* not present — fine */ }
|
|
317
276
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
277
|
+
lines.push("rmfmail unregistered as a mailto: handler.");
|
|
278
|
+
lines.push("Note: if rmfmail was the active default in Settings → Default apps → Email,");
|
|
279
|
+
lines.push("Windows may keep the prior selection in UserChoice until you pick another app.");
|
|
280
|
+
return { ok: true, message: lines.join("\n") };
|
|
322
281
|
}
|
|
323
282
|
// Prepass: same defensive cleanup as --unregister, restricted to
|
|
324
283
|
// rmfmail/mailx-related leftover keys. Without this, registering on
|
|
@@ -329,31 +288,84 @@ if (hasFlag("register-mailto") || hasFlag("unregister-mailto")) {
|
|
|
329
288
|
try {
|
|
330
289
|
const out = execSync(`reg query "${key}" /ve`, { stdio: ["pipe", "pipe", "pipe"] }).toString();
|
|
331
290
|
if (/mailx\.js|rmfmail/i.test(out)) {
|
|
332
|
-
try { execSync(`reg delete "${key}" /f`, { stdio: "pipe" });
|
|
291
|
+
try { execSync(`reg delete "${key}" /f`, { stdio: "pipe" }); lines.push(` cleaned leftover ${oldExe} mailto handler`); }
|
|
333
292
|
catch { /* */ }
|
|
334
293
|
}
|
|
335
294
|
} catch { /* not present — fine */ }
|
|
336
295
|
}
|
|
296
|
+
let failed = 0;
|
|
337
297
|
for (const [keyPath, name, data] of keys) {
|
|
338
298
|
const valueArg = name ? `/v "${name}"` : "/ve";
|
|
339
299
|
const dataArg = data ? `/d "${data}"` : "";
|
|
340
300
|
const type = data ? "/t REG_SZ" : "/t REG_SZ /d \"\"";
|
|
341
301
|
const cmdLine = `reg add "${keyPath}" ${valueArg} ${type} ${dataArg} /f`;
|
|
342
302
|
try { execSync(cmdLine, { stdio: "pipe" }); }
|
|
343
|
-
catch (e: any) {
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
303
|
+
catch (e: any) { failed++; lines.push(` failed: ${cmdLine}\n ${e.message}`); }
|
|
304
|
+
}
|
|
305
|
+
lines.push("rmfmail registered as a mailto: handler.");
|
|
306
|
+
lines.push("");
|
|
307
|
+
lines.push("To make it the default in Windows 11:");
|
|
308
|
+
lines.push(" Settings → Apps → Default apps → search \"rmfmail\" → click rmfmail");
|
|
309
|
+
lines.push(" → \"Set default\" for the mailto link type.");
|
|
310
|
+
lines.push("");
|
|
311
|
+
lines.push("Win11 enforces a hash on `HKCU\\...\\UrlAssociations\\mailto\\UserChoice`,");
|
|
312
|
+
lines.push("so the only programmatic ways to flip the active default are MSIX packaging");
|
|
313
|
+
lines.push("(parked under C46) or a hash-aware tool like SetUserFTA. The picker may");
|
|
314
|
+
lines.push("warn \"app must be from the Store\" — click \"More options\" / \"Choose another");
|
|
315
|
+
lines.push("app on this PC\" to reach rmfmail.");
|
|
316
|
+
return { ok: failed === 0, message: lines.join("\n") };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (hasFlag("register-mailto") || hasFlag("unregister-mailto")) {
|
|
320
|
+
const unregister = hasFlag("unregister-mailto");
|
|
321
|
+
// C124: Linux registration — a .desktop entry claiming the mailto:
|
|
322
|
+
// scheme + xdg-mime default. No exe-launcher needed (unlike Windows):
|
|
323
|
+
// Linux pickers read Name= from the .desktop file directly.
|
|
324
|
+
if (process.platform === "linux") {
|
|
325
|
+
const appsDir = path.join(os.homedir(), ".local", "share", "applications");
|
|
326
|
+
const desktopPath = path.join(appsDir, "rmfmail.desktop");
|
|
327
|
+
if (unregister) {
|
|
328
|
+
try { fs.unlinkSync(desktopPath); } catch { /* not registered */ }
|
|
329
|
+
try { execSync(`update-desktop-database "${appsDir}"`); } catch { /* optional tool */ }
|
|
330
|
+
console.log("Unregistered rmfmail mailto: handler.");
|
|
331
|
+
process.exit(0);
|
|
332
|
+
}
|
|
333
|
+
const __mailtoJs = path.join(import.meta.dirname, "mailx.js");
|
|
334
|
+
const __iconPng = path.join(import.meta.dirname, "..", "client", "icon.png");
|
|
335
|
+
// Exec quoting follows the Desktop Entry spec: quoted absolute paths,
|
|
336
|
+
// bare %u field code (quoting %u breaks argument expansion).
|
|
337
|
+
const desktopEntry = [
|
|
338
|
+
"[Desktop Entry]",
|
|
339
|
+
"Type=Application",
|
|
340
|
+
"Name=rmfmail",
|
|
341
|
+
"Comment=rmfmail email client",
|
|
342
|
+
`Exec="${process.execPath}" "${__mailtoJs}" --mailto %u`,
|
|
343
|
+
`Icon=${__iconPng}`,
|
|
344
|
+
"Terminal=false",
|
|
345
|
+
"MimeType=x-scheme-handler/mailto;",
|
|
346
|
+
"Categories=Network;Email;",
|
|
347
|
+
"",
|
|
348
|
+
].join("\n");
|
|
349
|
+
fs.mkdirSync(appsDir, { recursive: true });
|
|
350
|
+
fs.writeFileSync(desktopPath, desktopEntry);
|
|
351
|
+
try {
|
|
352
|
+
execSync("xdg-mime default rmfmail.desktop x-scheme-handler/mailto");
|
|
353
|
+
} catch (e: any) {
|
|
354
|
+
console.error(`xdg-mime failed (${e.message}) — handler installed but not set as default. Set it in your DE's default-apps settings.`);
|
|
355
|
+
}
|
|
356
|
+
try { execSync(`update-desktop-database "${appsDir}"`); } catch { /* optional tool */ }
|
|
357
|
+
console.log(`Registered rmfmail as mailto: handler (${desktopPath}).`);
|
|
358
|
+
process.exit(0);
|
|
359
|
+
}
|
|
360
|
+
if (process.platform !== "win32") {
|
|
361
|
+
// macOS needs a real .app bundle with CFBundleURLTypes — LaunchServices
|
|
362
|
+
// won't register a bare script. Tracked as the macOS half of C124.
|
|
363
|
+
console.error("--register-mailto / --unregister-mailto: macOS registration not implemented yet (needs an .app bundle — TODO C124).");
|
|
364
|
+
process.exit(1);
|
|
365
|
+
}
|
|
366
|
+
const r = registerMailtoWindows(unregister);
|
|
367
|
+
console.log(r.message);
|
|
368
|
+
process.exit(r.ok ? 0 : 1);
|
|
357
369
|
}
|
|
358
370
|
|
|
359
371
|
// Read our own version once — used for the instance file + upgrade check below.
|
|
@@ -2048,6 +2060,12 @@ RFC 5322 with CRLF line endings. Bodies are quoted-printable encoded (readable i
|
|
|
2048
2060
|
// shell can SIGTERM it without name-based sweeps (cross-platform —
|
|
2049
2061
|
// see memory: feedback_cross_platform_strong_rule). Remove on
|
|
2050
2062
|
// resolution so the file doesn't accumulate stale PIDs.
|
|
2063
|
+
// Settings → "Make rmfmail the default email app" runs the same
|
|
2064
|
+
// registration the -register-mailto CLI flag does. Windows-only —
|
|
2065
|
+
// Linux/mac users still go through the CLI (xdg / TODO C124).
|
|
2066
|
+
if (process.platform === "win32") {
|
|
2067
|
+
svc.setMailtoRegisterFn(async () => registerMailtoWindows(false));
|
|
2068
|
+
}
|
|
2051
2069
|
svc.setPopupFn(async (opts: any) => {
|
|
2052
2070
|
// Own WebView2 profile so a reminder popup can never share (and
|
|
2053
2071
|
// crash) the main window's browser process — see S67 note on the
|
package/client/app.bundle.js
CHANGED
|
@@ -97,6 +97,7 @@ __export(api_client_exports, {
|
|
|
97
97
|
reauthGoogleScopes: () => reauthGoogleScopes,
|
|
98
98
|
reauthenticate: () => reauthenticate,
|
|
99
99
|
recordSpamReport: () => recordSpamReport,
|
|
100
|
+
registerMailto: () => registerMailto,
|
|
100
101
|
removeUserDictWord: () => removeUserDictWord,
|
|
101
102
|
renameFolder: () => renameFolder,
|
|
102
103
|
repairAccounts: () => repairAccounts,
|
|
@@ -631,6 +632,12 @@ async function openAttachment(accountId, uid, attachmentId, folderId, filename)
|
|
|
631
632
|
async function getMessageSource(accountId, uid, folderId) {
|
|
632
633
|
return ipc().getMessageSource(accountId, uid, folderId);
|
|
633
634
|
}
|
|
635
|
+
async function registerMailto() {
|
|
636
|
+
const fn = ipc().registerMailto;
|
|
637
|
+
if (!fn)
|
|
638
|
+
return { ok: false, message: "Not available in this host \u2014 run `rmfmail -register-mailto` from a terminal." };
|
|
639
|
+
return fn();
|
|
640
|
+
}
|
|
634
641
|
async function popoutWindow(accountId, uid, folderId, subject) {
|
|
635
642
|
const fn = ipc().popoutWindow;
|
|
636
643
|
if (!fn)
|
|
@@ -11139,6 +11146,44 @@ document.getElementById("btn-edit-jsonc")?.addEventListener("click", async () =>
|
|
|
11139
11146
|
if (settingsDropdown2) settingsDropdown2.hidden = true;
|
|
11140
11147
|
await openJsoncEditor("accounts.jsonc");
|
|
11141
11148
|
});
|
|
11149
|
+
document.getElementById("btn-register-mailto")?.addEventListener("click", async () => {
|
|
11150
|
+
const settingsDropdown2 = document.getElementById("settings-dropdown");
|
|
11151
|
+
if (settingsDropdown2) settingsDropdown2.hidden = true;
|
|
11152
|
+
const { registerMailto: registerMailto2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
11153
|
+
const r = await registerMailto2();
|
|
11154
|
+
const backdrop = document.createElement("div");
|
|
11155
|
+
backdrop.className = "mailx-modal-backdrop";
|
|
11156
|
+
backdrop.style.cssText = "position:fixed;inset:0;z-index:2000;background:rgba(0,0,0,0.4);display:flex;align-items:center;justify-content:center;";
|
|
11157
|
+
const panel = document.createElement("div");
|
|
11158
|
+
panel.style.cssText = "background:var(--color-bg,#fff);color:var(--color-text,#000);border-radius:8px;box-shadow:0 8px 32px rgba(0,0,0,0.3);width:min(520px,90vw);padding:18px;display:flex;flex-direction:column;gap:12px;";
|
|
11159
|
+
const heading = document.createElement("div");
|
|
11160
|
+
heading.style.cssText = "font-weight:600;font-size:1.05em;";
|
|
11161
|
+
heading.textContent = r.ok ? "rmfmail registered as an email handler" : "Registration failed";
|
|
11162
|
+
const body = document.createElement("div");
|
|
11163
|
+
body.style.cssText = "font-size:0.92em;line-height:1.45;white-space:pre-wrap;";
|
|
11164
|
+
body.textContent = r.ok ? 'One manual step remains \u2014 Windows only lets YOU flip the default:\n\nSettings \u2192 Apps \u2192 Default apps \u2192 search "rmfmail" \u2192 set it for MAILTO.\n\nIf a picker warns the app must be from the Store, choose "More options" / "Choose an app on this PC".' : r.message;
|
|
11165
|
+
const btnRow = document.createElement("div");
|
|
11166
|
+
btnRow.style.cssText = "display:flex;gap:8px;justify-content:flex-end;";
|
|
11167
|
+
const closeBtn = document.createElement("button");
|
|
11168
|
+
closeBtn.textContent = "Close";
|
|
11169
|
+
closeBtn.style.cssText = "padding:6px 14px;";
|
|
11170
|
+
closeBtn.addEventListener("click", () => backdrop.remove());
|
|
11171
|
+
btnRow.append(closeBtn);
|
|
11172
|
+
if (r.ok) {
|
|
11173
|
+
const openBtn = document.createElement("button");
|
|
11174
|
+
openBtn.textContent = "Open Default-apps settings";
|
|
11175
|
+
openBtn.style.cssText = "padding:6px 14px;font-weight:500;";
|
|
11176
|
+
openBtn.addEventListener("click", () => {
|
|
11177
|
+
const api = window.mailxapi;
|
|
11178
|
+
if (api?.openExternal) api.openExternal("ms-settings:defaultapps");
|
|
11179
|
+
backdrop.remove();
|
|
11180
|
+
});
|
|
11181
|
+
btnRow.append(openBtn);
|
|
11182
|
+
}
|
|
11183
|
+
panel.append(heading, body, btnRow);
|
|
11184
|
+
backdrop.append(panel);
|
|
11185
|
+
document.body.append(backdrop);
|
|
11186
|
+
});
|
|
11142
11187
|
document.addEventListener("mailx-open-jsonc-editor", async (ev) => {
|
|
11143
11188
|
const file = ev.detail?.file || "accounts.jsonc";
|
|
11144
11189
|
await openJsoncEditor(file);
|