@bobfrankston/mailx 1.0.180 → 1.0.181

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 CHANGED
@@ -28,6 +28,20 @@ function hasFlag(name) { return args.includes(`-${name}`) || args.includes(`--${
28
28
  const serverMode = hasFlag("server");
29
29
  const noBrowser = hasFlag("no-browser");
30
30
  const verbose = hasFlag("verbose");
31
+ const isDaemon = hasFlag("daemon"); // internal: re-spawned detached process
32
+ // Auto-detach: re-spawn as background process so terminal returns immediately
33
+ // Skip for: --verbose (want console), --server (needs terminal), --daemon (already detached),
34
+ // and any command flags (setup, kill, test, etc.)
35
+ if (!verbose && !serverMode && !isDaemon && !process.argv.slice(2).some(a => /^-/.test(a) && !["--no-browser"].includes(a))) {
36
+ const { spawn } = await import("node:child_process");
37
+ const child = spawn(process.execPath, [...process.argv.slice(1), "--daemon"], {
38
+ detached: true,
39
+ stdio: "ignore",
40
+ windowsHide: true,
41
+ });
42
+ child.unref();
43
+ process.exit(0);
44
+ }
31
45
  const setupMode = hasFlag("setup");
32
46
  const addMode = hasFlag("add");
33
47
  const testMode = hasFlag("test");
@@ -35,7 +49,7 @@ const rebuildMode = hasFlag("rebuild");
35
49
  const repairMode = hasFlag("repair");
36
50
  const importMode = hasFlag("import");
37
51
  // Validate arguments
38
- const knownFlags = ["server", "no-browser", "verbose", "external", "kill", "v", "version", "setup", "add", "test", "rebuild", "repair", "native-imap", "log", "import", "email", "mail"];
52
+ const knownFlags = ["server", "no-browser", "verbose", "external", "kill", "v", "version", "setup", "add", "test", "rebuild", "repair", "native-imap", "log", "import", "email", "mail", "daemon"];
39
53
  for (const arg of args) {
40
54
  const flag = arg.replace(/^--?/, "");
41
55
  if (arg.startsWith("-") && !knownFlags.includes(flag)) {
@@ -665,6 +679,10 @@ async function main() {
665
679
  // Pass server version to dispatch so getVersion returns it
666
680
  const rootPkg = JSON.parse(fs.readFileSync(path.join(import.meta.dirname, "..", "package.json"), "utf-8"));
667
681
  handle.onRequest(async (req) => {
682
+ if (!req._action) {
683
+ console.log(`[ipc] ← ignored (no _action): ${JSON.stringify(req).substring(0, 100)}`);
684
+ return;
685
+ }
668
686
  console.log(`[ipc] ← ${req._action} (${req._cbid})`);
669
687
  try {
670
688
  const response = await dispatch(svc, req);
@@ -1 +1 @@
1
- {"height":1047,"width":1844,"x":531,"y":264}
1
+ {"height":1047,"width":1844,"x":339,"y":168}
package/client/app.js CHANGED
@@ -565,22 +565,10 @@ onWsEvent((event) => {
565
565
  break;
566
566
  }
567
567
  case "folderCountsChanged": {
568
- // Incremental count updateno DOM rebuild, no jitter
568
+ // Update folder badges only never reload the message list or touch the viewer.
569
+ // The list refreshes when the user clicks a folder or presses Sync.
569
570
  updateFolderCounts();
570
571
  updateNewMessageCount();
571
- // Only reload message list if the synced account is the one we're viewing
572
- // (or unified inbox which shows all accounts). Debounce to avoid rapid reloads
573
- // during first sync which emits per-batch.
574
- const syncedAccount = event.accountId;
575
- const viewingThis = !currentAccountId || currentAccountId === syncedAccount;
576
- if (viewingThis) {
577
- if (reloadDebounceTimer)
578
- clearTimeout(reloadDebounceTimer);
579
- reloadDebounceTimer = setTimeout(() => {
580
- reloadDebounceTimer = null;
581
- reloadCurrentFolder();
582
- }, 500);
583
- }
584
572
  // Sync finished — re-enable sync button
585
573
  const syncBtn = document.getElementById("btn-sync");
586
574
  if (syncBtn) {
@@ -17,16 +17,23 @@ export function getCurrentMessage() {
17
17
  /** Initialize viewer — subscribe to state changes */
18
18
  export function initViewer() {
19
19
  state.subscribe((change) => {
20
- if (change === "removed" || change === "messages") {
20
+ if (change === "removed") {
21
+ // Message was deleted/moved — show auto-selected next, or clear
21
22
  const sel = state.getSelected();
22
23
  if (!sel) {
23
24
  clearViewer();
24
25
  }
25
26
  else if (sel.uid !== currentMessage?.uid || sel.accountId !== currentAccountId) {
26
- // State auto-selected a new message after removal — show it
27
27
  showMessage(sel.accountId, sel.uid, sel.folderId);
28
28
  }
29
29
  }
30
+ else if (change === "selected") {
31
+ // Explicit deselect (folder switch, clearViewer)
32
+ if (!state.getSelected()) {
33
+ clearViewer();
34
+ }
35
+ }
36
+ // "messages" change (sync reload) — don't touch the viewer
30
37
  });
31
38
  }
32
39
  function clearViewer() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx",
3
- "version": "1.0.180",
3
+ "version": "1.0.181",
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.21",
27
- "@bobfrankston/msger": "^0.1.230",
27
+ "@bobfrankston/msger": "^0.1.231",
28
28
  "@capacitor/android": "^8.3.0",
29
29
  "@capacitor/cli": "^8.3.0",
30
30
  "@capacitor/core": "^8.3.0",