@bobfrankston/mailx 1.0.229 → 1.0.230

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
@@ -775,6 +775,9 @@ async function main() {
775
775
  imapManager.on("accountError", (accountId, error, hint, isOAuth) => {
776
776
  handle.send({ _event: "accountError", type: "accountError", accountId, error, hint, isOAuth });
777
777
  });
778
+ imapManager.on("configChanged", (filename) => {
779
+ handle.send({ _event: "configChanged", type: "configChanged", filename });
780
+ });
778
781
  // Brief pause for WebView2 to initialize before starting IMAP (avoids stdin writes during init)
779
782
  await new Promise(r => setTimeout(r, 500));
780
783
  // Register all accounts (OAuth may open browser for Gmail — event loop stays free for IPC)
@@ -797,6 +800,7 @@ async function main() {
797
800
  }
798
801
  imapManager.startPeriodicSync(settings.sync.intervalMinutes);
799
802
  imapManager.startOutboxWorker();
803
+ imapManager.watchConfigFiles();
800
804
  // Graceful shutdown — close IMAP connections, stop timers, close DB
801
805
  let shuttingDown = false;
802
806
  async function gracefulShutdown(reason) {
package/client/app.js CHANGED
@@ -866,6 +866,11 @@ onWsEvent((event) => {
866
866
  case "reload":
867
867
  location.reload();
868
868
  break;
869
+ case "configChanged":
870
+ // A watched config file (accounts.jsonc etc.) was modified externally.
871
+ // Show a banner telling the user to restart for changes to take effect.
872
+ showAlert(`${event.filename} was updated — restart mailx to apply changes`, `config-${event.filename}`);
873
+ break;
869
874
  case "error":
870
875
  if (statusSync)
871
876
  statusSync.textContent = `Error: ${event.message}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx",
3
- "version": "1.0.229",
3
+ "version": "1.0.230",
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.291",
27
+ "@bobfrankston/msger": "^0.1.292",
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.291",
81
+ "@bobfrankston/msger": "^0.1.292",
82
82
  "@capacitor/android": "^8.3.0",
83
83
  "@capacitor/cli": "^8.3.0",
84
84
  "@capacitor/core": "^8.3.0",
@@ -18,6 +18,7 @@ export interface ImapManagerEvents {
18
18
  unread: number;
19
19
  }>) => void;
20
20
  accountError: (accountId: string, error: string, hint: string, isOAuth: boolean) => void;
21
+ configChanged: (filename: string) => void;
21
22
  }
22
23
  export declare class ImapManager extends EventEmitter {
23
24
  private configs;
@@ -205,6 +206,13 @@ export declare class ImapManager extends EventEmitter {
205
206
  startOutboxWorker(): void;
206
207
  /** Stop Outbox worker */
207
208
  stopOutboxWorker(): void;
209
+ private configWatchers;
210
+ /** Watch the local config files for external changes. On change, emit
211
+ * configChanged so the UI can show a "restart to apply" banner. Uses
212
+ * a debounce to coalesce rapid writes from save tools. */
213
+ watchConfigFiles(): void;
214
+ /** Stop all config file watchers */
215
+ stopWatchingConfig(): void;
208
216
  private contactsSyncToken;
209
217
  /** Get an OAuth token for Google APIs (contacts, calendar, etc.)
210
218
  * Uses the SAME token as IMAP — scopes are combined in one grant */
@@ -2116,6 +2116,47 @@ export class ImapManager extends EventEmitter {
2116
2116
  this.outboxInterval = null;
2117
2117
  }
2118
2118
  }
2119
+ // ── Config file watcher ──
2120
+ configWatchers = [];
2121
+ /** Watch the local config files for external changes. On change, emit
2122
+ * configChanged so the UI can show a "restart to apply" banner. Uses
2123
+ * a debounce to coalesce rapid writes from save tools. */
2124
+ watchConfigFiles() {
2125
+ const files = ["accounts.jsonc", "allowlist.jsonc", "clients.jsonc", "config.jsonc"];
2126
+ const configDir = getConfigDir();
2127
+ const debounce = new Map();
2128
+ for (const filename of files) {
2129
+ const full = path.join(configDir, filename);
2130
+ if (!fs.existsSync(full))
2131
+ continue;
2132
+ try {
2133
+ const watcher = fs.watch(full, () => {
2134
+ const prev = debounce.get(filename);
2135
+ if (prev)
2136
+ clearTimeout(prev);
2137
+ debounce.set(filename, setTimeout(() => {
2138
+ debounce.delete(filename);
2139
+ console.log(` [watch] ${filename} changed`);
2140
+ this.emit("configChanged", filename);
2141
+ }, 500));
2142
+ });
2143
+ this.configWatchers.push(watcher);
2144
+ }
2145
+ catch (e) {
2146
+ console.error(` [watch] Failed to watch ${filename}: ${e.message}`);
2147
+ }
2148
+ }
2149
+ }
2150
+ /** Stop all config file watchers */
2151
+ stopWatchingConfig() {
2152
+ for (const w of this.configWatchers) {
2153
+ try {
2154
+ w.close();
2155
+ }
2156
+ catch { /* ignore */ }
2157
+ }
2158
+ this.configWatchers = [];
2159
+ }
2119
2160
  // ── Google Contacts Sync ──
2120
2161
  contactsSyncToken = null;
2121
2162
  /** Get an OAuth token for Google APIs (contacts, calendar, etc.)