@bobfrankston/rmfmail 1.2.276 → 1.2.277

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.
Files changed (87) hide show
  1. package/.commitmsg +28 -23
  2. package/bin/build-tinymce.js +27 -0
  3. package/client/app.bundle.js +108 -0
  4. package/client/app.bundle.js.map +4 -4
  5. package/client/app.js +65 -0
  6. package/client/app.js.map +1 -1
  7. package/client/app.ts +59 -0
  8. package/client/compose/compose.bundle.js +90 -2
  9. package/client/compose/compose.bundle.js.map +4 -4
  10. package/client/compose/compose.js +26 -1
  11. package/client/compose/compose.js.map +1 -1
  12. package/client/compose/compose.ts +22 -1
  13. package/client/compose/editor.js +12 -1
  14. package/client/compose/editor.js.map +1 -1
  15. package/client/compose/editor.ts +17 -2
  16. package/client/index.html +1 -0
  17. package/client/lib/rmf-tiny.js +3 -0
  18. package/client/lib/tinymce/langs/ar-SA.js +440 -0
  19. package/client/lib/tinymce/langs/ar.js +441 -0
  20. package/client/lib/tinymce/langs/az.js +440 -0
  21. package/client/lib/tinymce/langs/be.js +440 -0
  22. package/client/lib/tinymce/langs/bg-BG.js +440 -0
  23. package/client/lib/tinymce/langs/bn-BD.js +440 -0
  24. package/client/lib/tinymce/langs/bs.js +440 -0
  25. package/client/lib/tinymce/langs/ca.js +440 -0
  26. package/client/lib/tinymce/langs/cs.js +440 -0
  27. package/client/lib/tinymce/langs/cy.js +440 -0
  28. package/client/lib/tinymce/langs/da.js +440 -0
  29. package/client/lib/tinymce/langs/de.js +440 -0
  30. package/client/lib/tinymce/langs/el.js +440 -0
  31. package/client/lib/tinymce/langs/eo.js +440 -0
  32. package/client/lib/tinymce/langs/es-MX.js +440 -0
  33. package/client/lib/tinymce/langs/es.js +440 -0
  34. package/client/lib/tinymce/langs/et.js +440 -0
  35. package/client/lib/tinymce/langs/eu.js +440 -0
  36. package/client/lib/tinymce/langs/fa.js +441 -0
  37. package/client/lib/tinymce/langs/fi.js +440 -0
  38. package/client/lib/tinymce/langs/fr-FR.js +440 -0
  39. package/client/lib/tinymce/langs/gl.js +440 -0
  40. package/client/lib/tinymce/langs/he-IL.js +440 -0
  41. package/client/lib/tinymce/langs/hr.js +440 -0
  42. package/client/lib/tinymce/langs/hu-HU.js +440 -0
  43. package/client/lib/tinymce/langs/hy.js +440 -0
  44. package/client/lib/tinymce/langs/id.js +440 -0
  45. package/client/lib/tinymce/langs/index.json +63 -0
  46. package/client/lib/tinymce/langs/is-IS.js +440 -0
  47. package/client/lib/tinymce/langs/it.js +440 -0
  48. package/client/lib/tinymce/langs/ja.js +440 -0
  49. package/client/lib/tinymce/langs/ka-GE.js +440 -0
  50. package/client/lib/tinymce/langs/kab.js +440 -0
  51. package/client/lib/tinymce/langs/kk.js +440 -0
  52. package/client/lib/tinymce/langs/ko-KR.js +440 -0
  53. package/client/lib/tinymce/langs/lt.js +440 -0
  54. package/client/lib/tinymce/langs/lv.js +440 -0
  55. package/client/lib/tinymce/langs/nb-NO.js +440 -0
  56. package/client/lib/tinymce/langs/ne.js +440 -0
  57. package/client/lib/tinymce/langs/nl-BE.js +440 -0
  58. package/client/lib/tinymce/langs/nl.js +440 -0
  59. package/client/lib/tinymce/langs/oc.js +440 -0
  60. package/client/lib/tinymce/langs/pl.js +440 -0
  61. package/client/lib/tinymce/langs/pt-BR.js +440 -0
  62. package/client/lib/tinymce/langs/pt-PT.js +440 -0
  63. package/client/lib/tinymce/langs/ro.js +440 -0
  64. package/client/lib/tinymce/langs/ru.js +440 -0
  65. package/client/lib/tinymce/langs/sk.js +440 -0
  66. package/client/lib/tinymce/langs/sl-SI.js +440 -0
  67. package/client/lib/tinymce/langs/sr.js +440 -0
  68. package/client/lib/tinymce/langs/sv-SE.js +440 -0
  69. package/client/lib/tinymce/langs/ta.js +440 -0
  70. package/client/lib/tinymce/langs/tg.js +440 -0
  71. package/client/lib/tinymce/langs/th-TH.js +440 -0
  72. package/client/lib/tinymce/langs/tr.js +440 -0
  73. package/client/lib/tinymce/langs/ug.js +440 -0
  74. package/client/lib/tinymce/langs/uk.js +440 -0
  75. package/client/lib/tinymce/langs/vi.js +440 -0
  76. package/client/lib/tinymce/langs/zh-CN.js +440 -0
  77. package/client/lib/tinymce/langs/zh-HK.js +440 -0
  78. package/client/lib/tinymce/langs/zh-MO.js +440 -0
  79. package/client/lib/tinymce/langs/zh-TW.js +440 -0
  80. package/client/lib/tinymce-langs.js +96 -0
  81. package/client/lib/tinymce-langs.js.map +1 -0
  82. package/client/lib/tinymce-langs.ts +99 -0
  83. package/docs/editor.md +29 -3
  84. package/docs/preferences.md +2 -1
  85. package/npmchanges.md +29 -0
  86. package/package.json +11 -8
  87. /package/packages/mailx-imap/{node_modules.npmglobalize-stash-59216 → node_modules.npmglobalize-stash-48352}/.package-lock.json +0 -0
package/client/app.ts CHANGED
@@ -5670,6 +5670,65 @@ document.getElementById("opt-compose-font-size")?.addEventListener("change", (e)
5670
5670
  }).catch(() => {});
5671
5671
  });
5672
5672
 
5673
+ // TinyMCE UI-language picker. The options are whatever language packs
5674
+ // build-tinymce.js actually copied next to the script (langs/index.json), so
5675
+ // the menu can never offer a pack that would 404 at editor init — TinyMCE
5676
+ // raises a visible error notification for a missing langs/<code>.js.
5677
+ //
5678
+ // These translate the editor CHROME only. Content, the spellcheck dictionary
5679
+ // and Harper are untouched and stay English, which is why the control sits
5680
+ // under Editor rather than anywhere that reads as a global language setting.
5681
+ //
5682
+ // TinyMCE itself has no detection — "Auto" is ours, resolving the browser's
5683
+ // ordered language list against the shipped packs. The Auto row names what it
5684
+ // currently resolves to so the choice is not a guess. (Claude Code 2026-08-23)
5685
+ const optEditorLanguage = document.getElementById("opt-editor-language") as HTMLSelectElement;
5686
+ if (optEditorLanguage) {
5687
+ (async () => {
5688
+ const scriptUrl = localStorage.getItem("mailx-tinymce-cdn") || "lib/tinymce/tinymce.min.js";
5689
+ const { availableLanguages, resolveLanguage, languageLabel, LANGUAGE_AUTO, LANGUAGE_ENGLISH } =
5690
+ await import("./lib/tinymce-langs.js");
5691
+ const available = await availableLanguages(scriptUrl);
5692
+ const autoPick = resolveLanguage(LANGUAGE_AUTO, available);
5693
+ const add = (value: string, text: string) => {
5694
+ const o = document.createElement("option");
5695
+ o.value = value;
5696
+ o.textContent = text;
5697
+ optEditorLanguage.appendChild(o);
5698
+ };
5699
+ add(LANGUAGE_AUTO, `Auto — ${languageLabel(autoPick)}`);
5700
+ add(LANGUAGE_ENGLISH, languageLabel(LANGUAGE_ENGLISH));
5701
+ for (const code of [...available].sort((a, b) => languageLabel(a).localeCompare(languageLabel(b)))) {
5702
+ add(code, `${languageLabel(code)} (${code})`);
5703
+ }
5704
+ if (!available.length) {
5705
+ optEditorLanguage.disabled = true;
5706
+ optEditorLanguage.title = "No language packs found next to tinymce.min.js — English only";
5707
+ }
5708
+ let current = LANGUAGE_AUTO;
5709
+ try { current = localStorage.getItem("mailx-editor-language") || LANGUAGE_AUTO; } catch { /* */ }
5710
+ try {
5711
+ const settings: any = await getSettings();
5712
+ if (typeof settings?.ui?.editorLanguage === "string" && settings.ui.editorLanguage) current = settings.ui.editorLanguage;
5713
+ } catch { /* cache value stands */ }
5714
+ optEditorLanguage.value = current;
5715
+ // A pinned pack that is no longer shipped would leave the select on
5716
+ // nothing at all; say so rather than showing a blank control.
5717
+ if (!optEditorLanguage.value) optEditorLanguage.value = LANGUAGE_AUTO;
5718
+ })().catch((e: any) => console.error("[settings] editor-language menu failed:", e?.message || e));
5719
+
5720
+ optEditorLanguage.addEventListener("change", () => {
5721
+ const value = optEditorLanguage.value;
5722
+ // Synchronous cache write for the same reason as saveEditorSetting:
5723
+ // compose reads it at module load, so the next compose-open is right.
5724
+ try { localStorage.setItem("mailx-editor-language", value); } catch { /* private mode */ }
5725
+ getSettings().then((settings: any) => {
5726
+ settings.ui = { ...settings.ui, editorLanguage: value };
5727
+ saveSettings(settings);
5728
+ }).catch((e: any) => console.error("[settings] editor language save failed:", e?.message || e));
5729
+ });
5730
+ }
5731
+
5673
5732
  // Save editor choice to server settings
5674
5733
  function saveEditorSetting(editor: string): void {
5675
5734
  // Update the localStorage cache SYNCHRONOUSLY. compose.ts reads
@@ -2714,6 +2714,9 @@ async function createTinyMceEditor(container2, opts = {}) {
2714
2714
  // Adding Text first so it's the default; rest are the modern
2715
2715
  // languages we actually paste.
2716
2716
  codesample_languages: CODE_LANGS,
2717
+ // UI language. Left unset (TinyMCE default "en") when the host
2718
+ // passes nothing, which also skips the langs/ fetch entirely.
2719
+ ...opts.language && opts.language !== "en" ? { language: opts.language } : {},
2717
2720
  // WebView's native spell-check (red underlines, right-click
2718
2721
  // "Add to dictionary"). Free; same UX as Quill's spellcheck=true.
2719
2722
  // Premium tinymcespellchecker plugin would replace this with a
@@ -3428,6 +3431,67 @@ var init_rmf_tiny = __esm({
3428
3431
  }
3429
3432
  });
3430
3433
 
3434
+ // client/lib/tinymce-langs.js
3435
+ var tinymce_langs_exports = {};
3436
+ __export(tinymce_langs_exports, {
3437
+ LANGUAGE_AUTO: () => LANGUAGE_AUTO,
3438
+ LANGUAGE_ENGLISH: () => LANGUAGE_ENGLISH,
3439
+ availableLanguages: () => availableLanguages,
3440
+ langManifestUrl: () => langManifestUrl,
3441
+ languageLabel: () => languageLabel,
3442
+ resolveLanguage: () => resolveLanguage
3443
+ });
3444
+ function langManifestUrl(tinymceScriptUrl) {
3445
+ return new URL("langs/index.json", new URL(tinymceScriptUrl, location.href)).href;
3446
+ }
3447
+ async function availableLanguages(tinymceScriptUrl) {
3448
+ try {
3449
+ const r = await fetch(langManifestUrl(tinymceScriptUrl));
3450
+ if (!r.ok)
3451
+ return [];
3452
+ const list = await r.json();
3453
+ return Array.isArray(list) ? list.filter((x) => typeof x === "string") : [];
3454
+ } catch {
3455
+ return [];
3456
+ }
3457
+ }
3458
+ function resolveLanguage(setting, available, preferred = navigator.languages || [navigator.language]) {
3459
+ const byLower = new Map(available.map((code) => [code.toLowerCase(), code]));
3460
+ if (setting && setting !== LANGUAGE_AUTO) {
3461
+ if (setting === LANGUAGE_ENGLISH)
3462
+ return LANGUAGE_ENGLISH;
3463
+ return byLower.get(setting.toLowerCase()) || LANGUAGE_ENGLISH;
3464
+ }
3465
+ for (const tag of preferred) {
3466
+ if (!tag)
3467
+ continue;
3468
+ const lower = tag.toLowerCase();
3469
+ const base = lower.split("-")[0];
3470
+ if (base === LANGUAGE_ENGLISH)
3471
+ return LANGUAGE_ENGLISH;
3472
+ const hit = byLower.get(lower) || byLower.get(base) || available.find((code) => code.toLowerCase().split("-")[0] === base);
3473
+ if (hit)
3474
+ return hit;
3475
+ }
3476
+ return LANGUAGE_ENGLISH;
3477
+ }
3478
+ function languageLabel(code) {
3479
+ try {
3480
+ const names = new Intl.DisplayNames(navigator.languages || void 0, { type: "language" });
3481
+ return names.of(code) || code;
3482
+ } catch {
3483
+ return code;
3484
+ }
3485
+ }
3486
+ var LANGUAGE_AUTO, LANGUAGE_ENGLISH;
3487
+ var init_tinymce_langs = __esm({
3488
+ "client/lib/tinymce-langs.js"() {
3489
+ "use strict";
3490
+ LANGUAGE_AUTO = "auto";
3491
+ LANGUAGE_ENGLISH = "en";
3492
+ }
3493
+ });
3494
+
3431
3495
  // client/compose/spellcheck.js
3432
3496
  var spellcheck_exports = {};
3433
3497
  __export(spellcheck_exports, {
@@ -5074,7 +5138,15 @@ async function createTinyMceEditor2(container2, opts = {}) {
5074
5138
  }
5075
5139
  const m = await Promise.resolve().then(() => (init_rmf_tiny(), rmf_tiny_exports));
5076
5140
  const cssPx = parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--compose-font-size"));
5077
- const ed = await m.createTinyMceEditor(container2, { cdnUrl, apiKey, onFiles: opts.onFiles, fontSizePx: Number.isFinite(cssPx) ? cssPx : void 0 });
5141
+ let language;
5142
+ try {
5143
+ const { availableLanguages: availableLanguages2, resolveLanguage: resolveLanguage2, LANGUAGE_ENGLISH: LANGUAGE_ENGLISH2 } = await Promise.resolve().then(() => (init_tinymce_langs(), tinymce_langs_exports));
5144
+ const resolved = resolveLanguage2(opts.language || "auto", await availableLanguages2(cdnUrl));
5145
+ if (resolved !== LANGUAGE_ENGLISH2)
5146
+ language = resolved;
5147
+ } catch {
5148
+ }
5149
+ const ed = await m.createTinyMceEditor(container2, { cdnUrl, apiKey, onFiles: opts.onFiles, fontSizePx: Number.isFinite(cssPx) ? cssPx : void 0, language });
5078
5150
  try {
5079
5151
  const native = ed.nativeEditor;
5080
5152
  const body = native?.getBody?.();
@@ -5161,6 +5233,15 @@ if (__bootUi?.editor === "tiptap" || __bootUi?.editor === "quill" || __bootUi?.e
5161
5233
  } catch {
5162
5234
  }
5163
5235
  }
5236
+ var editorLanguage = "auto";
5237
+ if (typeof __bootUi?.editorLanguage === "string" && __bootUi.editorLanguage) {
5238
+ editorLanguage = __bootUi.editorLanguage;
5239
+ } else {
5240
+ try {
5241
+ editorLanguage = localStorage.getItem("mailx-editor-language") || "auto";
5242
+ } catch {
5243
+ }
5244
+ }
5164
5245
  var COMPOSE_FONT_MIN_PX = 8;
5165
5246
  var COMPOSE_FONT_MAX_PX = 32;
5166
5247
  var COMPOSE_FONT_DEFAULT_PX = 20;
@@ -5183,6 +5264,13 @@ try {
5183
5264
  localStorage.setItem("mailx-editor-type", next);
5184
5265
  } catch {
5185
5266
  }
5267
+ const lang = appSettings?.ui?.editorLanguage;
5268
+ if (typeof lang === "string" && lang) {
5269
+ try {
5270
+ localStorage.setItem("mailx-editor-language", lang);
5271
+ } catch {
5272
+ }
5273
+ }
5186
5274
  const fontPx = Number(appSettings?.ui?.composeFontSize);
5187
5275
  if (Number.isFinite(fontPx) && fontPx >= COMPOSE_FONT_MIN_PX && fontPx <= COMPOSE_FONT_MAX_PX) {
5188
5276
  try {
@@ -5243,7 +5331,7 @@ async function tryEditor(type) {
5243
5331
  try {
5244
5332
  const ed = await createEditor(container, type, { onFiles: (files) => {
5245
5333
  void ingestFiles(files);
5246
- } });
5334
+ }, language: editorLanguage });
5247
5335
  if (type === "tinymce" && ed && ed.nativeEditor) {
5248
5336
  const native = ed.nativeEditor;
5249
5337
  const attach = () => {