@bobfrankston/rmfmail 1.2.276 → 1.2.278
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/.commitmsg +26 -23
- package/bin/build-tinymce.js +27 -0
- package/client/app.bundle.js +108 -0
- package/client/app.bundle.js.map +4 -4
- package/client/app.js +65 -0
- package/client/app.js.map +1 -1
- package/client/app.ts +59 -0
- package/client/compose/compose.bundle.js +90 -2
- package/client/compose/compose.bundle.js.map +4 -4
- package/client/compose/compose.js +26 -1
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +22 -1
- package/client/compose/editor.js +12 -1
- package/client/compose/editor.js.map +1 -1
- package/client/compose/editor.ts +17 -2
- package/client/index.html +1 -0
- package/client/lib/rmf-tiny.js +3 -0
- package/client/lib/tinymce/CHANGELOG.md +47 -0
- package/client/lib/tinymce/composer.json +1 -1
- package/client/lib/tinymce/icons/default/icons.js +13 -0
- package/client/lib/tinymce/icons/default/icons.min.js +1 -1
- package/client/lib/tinymce/langs/ar-SA.js +440 -0
- package/client/lib/tinymce/langs/ar.js +441 -0
- package/client/lib/tinymce/langs/az.js +440 -0
- package/client/lib/tinymce/langs/be.js +440 -0
- package/client/lib/tinymce/langs/bg-BG.js +440 -0
- package/client/lib/tinymce/langs/bn-BD.js +440 -0
- package/client/lib/tinymce/langs/bs.js +440 -0
- package/client/lib/tinymce/langs/ca.js +440 -0
- package/client/lib/tinymce/langs/cs.js +440 -0
- package/client/lib/tinymce/langs/cy.js +440 -0
- package/client/lib/tinymce/langs/da.js +440 -0
- package/client/lib/tinymce/langs/de.js +440 -0
- package/client/lib/tinymce/langs/el.js +440 -0
- package/client/lib/tinymce/langs/eo.js +440 -0
- package/client/lib/tinymce/langs/es-MX.js +440 -0
- package/client/lib/tinymce/langs/es.js +440 -0
- package/client/lib/tinymce/langs/et.js +440 -0
- package/client/lib/tinymce/langs/eu.js +440 -0
- package/client/lib/tinymce/langs/fa.js +441 -0
- package/client/lib/tinymce/langs/fi.js +440 -0
- package/client/lib/tinymce/langs/fr-FR.js +440 -0
- package/client/lib/tinymce/langs/gl.js +440 -0
- package/client/lib/tinymce/langs/he-IL.js +440 -0
- package/client/lib/tinymce/langs/hr.js +440 -0
- package/client/lib/tinymce/langs/hu-HU.js +440 -0
- package/client/lib/tinymce/langs/hy.js +440 -0
- package/client/lib/tinymce/langs/id.js +440 -0
- package/client/lib/tinymce/langs/index.json +63 -0
- package/client/lib/tinymce/langs/is-IS.js +440 -0
- package/client/lib/tinymce/langs/it.js +440 -0
- package/client/lib/tinymce/langs/ja.js +440 -0
- package/client/lib/tinymce/langs/ka-GE.js +440 -0
- package/client/lib/tinymce/langs/kab.js +440 -0
- package/client/lib/tinymce/langs/kk.js +440 -0
- package/client/lib/tinymce/langs/ko-KR.js +440 -0
- package/client/lib/tinymce/langs/lt.js +440 -0
- package/client/lib/tinymce/langs/lv.js +440 -0
- package/client/lib/tinymce/langs/nb-NO.js +440 -0
- package/client/lib/tinymce/langs/ne.js +440 -0
- package/client/lib/tinymce/langs/nl-BE.js +440 -0
- package/client/lib/tinymce/langs/nl.js +440 -0
- package/client/lib/tinymce/langs/oc.js +440 -0
- package/client/lib/tinymce/langs/pl.js +440 -0
- package/client/lib/tinymce/langs/pt-BR.js +440 -0
- package/client/lib/tinymce/langs/pt-PT.js +440 -0
- package/client/lib/tinymce/langs/ro.js +440 -0
- package/client/lib/tinymce/langs/ru.js +440 -0
- package/client/lib/tinymce/langs/sk.js +440 -0
- package/client/lib/tinymce/langs/sl-SI.js +440 -0
- package/client/lib/tinymce/langs/sr.js +440 -0
- package/client/lib/tinymce/langs/sv-SE.js +440 -0
- package/client/lib/tinymce/langs/ta.js +440 -0
- package/client/lib/tinymce/langs/tg.js +440 -0
- package/client/lib/tinymce/langs/th-TH.js +440 -0
- package/client/lib/tinymce/langs/tr.js +440 -0
- package/client/lib/tinymce/langs/ug.js +440 -0
- package/client/lib/tinymce/langs/uk.js +440 -0
- package/client/lib/tinymce/langs/vi.js +440 -0
- package/client/lib/tinymce/langs/zh-CN.js +440 -0
- package/client/lib/tinymce/langs/zh-HK.js +440 -0
- package/client/lib/tinymce/langs/zh-MO.js +440 -0
- package/client/lib/tinymce/langs/zh-TW.js +440 -0
- package/client/lib/tinymce/models/dom/model.js +1 -1
- package/client/lib/tinymce/package.json +1 -1
- package/client/lib/tinymce/plugins/accordion/plugin.js +1 -1
- package/client/lib/tinymce/plugins/advlist/plugin.js +1 -1
- package/client/lib/tinymce/plugins/anchor/plugin.js +1 -1
- package/client/lib/tinymce/plugins/autolink/plugin.js +1 -1
- package/client/lib/tinymce/plugins/autolink/plugin.min.js +1 -1
- package/client/lib/tinymce/plugins/autoresize/plugin.js +1 -1
- package/client/lib/tinymce/plugins/autosave/plugin.js +1 -1
- package/client/lib/tinymce/plugins/autosave/plugin.min.js +1 -1
- package/client/lib/tinymce/plugins/charmap/plugin.js +1 -1
- package/client/lib/tinymce/plugins/code/plugin.js +1 -1
- package/client/lib/tinymce/plugins/codesample/plugin.js +2 -2
- package/client/lib/tinymce/plugins/codesample/plugin.min.js +2 -2
- package/client/lib/tinymce/plugins/directionality/plugin.js +1 -1
- package/client/lib/tinymce/plugins/emoticons/plugin.js +5 -11
- package/client/lib/tinymce/plugins/emoticons/plugin.min.js +1 -1
- package/client/lib/tinymce/plugins/fullscreen/plugin.js +1 -1
- package/client/lib/tinymce/plugins/help/plugin.js +1 -1
- package/client/lib/tinymce/plugins/help/plugin.min.js +1 -1
- package/client/lib/tinymce/plugins/image/plugin.js +15 -28
- package/client/lib/tinymce/plugins/image/plugin.min.js +1 -1
- package/client/lib/tinymce/plugins/importcss/plugin.js +1 -1
- package/client/lib/tinymce/plugins/insertdatetime/plugin.js +1 -1
- package/client/lib/tinymce/plugins/link/plugin.js +3 -2
- package/client/lib/tinymce/plugins/link/plugin.min.js +1 -1
- package/client/lib/tinymce/plugins/lists/plugin.js +1 -1
- package/client/lib/tinymce/plugins/media/plugin.js +1 -1
- package/client/lib/tinymce/plugins/nonbreaking/plugin.js +1 -1
- package/client/lib/tinymce/plugins/pagebreak/plugin.js +1 -1
- package/client/lib/tinymce/plugins/preview/plugin.js +12 -2
- package/client/lib/tinymce/plugins/preview/plugin.min.js +1 -1
- package/client/lib/tinymce/plugins/quickbars/plugin.js +1 -1
- package/client/lib/tinymce/plugins/save/plugin.js +1 -1
- package/client/lib/tinymce/plugins/searchreplace/plugin.js +1 -1
- package/client/lib/tinymce/plugins/table/plugin.js +5 -3
- package/client/lib/tinymce/plugins/table/plugin.min.js +1 -1
- package/client/lib/tinymce/plugins/visualblocks/plugin.js +1 -1
- package/client/lib/tinymce/plugins/visualchars/plugin.js +1 -1
- package/client/lib/tinymce/plugins/wordcount/plugin.js +1 -1
- package/client/lib/tinymce/skins/content/dark/content.css +3 -0
- package/client/lib/tinymce/skins/content/dark/content.js +1 -1
- package/client/lib/tinymce/skins/content/dark/content.min.css +1 -1
- package/client/lib/tinymce/skins/ui/oxide/content.css +53 -42
- package/client/lib/tinymce/skins/ui/oxide/content.inline.css +53 -42
- package/client/lib/tinymce/skins/ui/oxide/content.inline.js +1 -1
- package/client/lib/tinymce/skins/ui/oxide/content.inline.min.css +1 -1
- package/client/lib/tinymce/skins/ui/oxide/content.inline.min.ts +2 -2
- package/client/lib/tinymce/skins/ui/oxide/content.inline.ts +2 -2
- package/client/lib/tinymce/skins/ui/oxide/content.js +1 -1
- package/client/lib/tinymce/skins/ui/oxide/content.min.css +1 -1
- package/client/lib/tinymce/skins/ui/oxide/content.min.ts +2 -2
- package/client/lib/tinymce/skins/ui/oxide/content.ts +2 -2
- package/client/lib/tinymce/skins/ui/oxide/skin.css +94 -22
- package/client/lib/tinymce/skins/ui/oxide/skin.js +1 -1
- package/client/lib/tinymce/skins/ui/oxide/skin.min.css +1 -1
- package/client/lib/tinymce/skins/ui/oxide/skin.min.ts +8 -3
- package/client/lib/tinymce/skins/ui/oxide/skin.ts +8 -3
- package/client/lib/tinymce/skins/ui/oxide-dark/content.css +53 -42
- package/client/lib/tinymce/skins/ui/oxide-dark/content.inline.css +53 -42
- package/client/lib/tinymce/skins/ui/oxide-dark/content.inline.js +1 -1
- package/client/lib/tinymce/skins/ui/oxide-dark/content.inline.min.css +1 -1
- package/client/lib/tinymce/skins/ui/oxide-dark/content.inline.min.ts +2 -2
- package/client/lib/tinymce/skins/ui/oxide-dark/content.inline.ts +2 -2
- package/client/lib/tinymce/skins/ui/oxide-dark/content.js +1 -1
- package/client/lib/tinymce/skins/ui/oxide-dark/content.min.css +1 -1
- package/client/lib/tinymce/skins/ui/oxide-dark/content.min.ts +2 -2
- package/client/lib/tinymce/skins/ui/oxide-dark/content.ts +2 -2
- package/client/lib/tinymce/skins/ui/oxide-dark/skin.css +94 -22
- package/client/lib/tinymce/skins/ui/oxide-dark/skin.js +1 -1
- package/client/lib/tinymce/skins/ui/oxide-dark/skin.min.css +1 -1
- package/client/lib/tinymce/skins/ui/oxide-dark/skin.min.ts +8 -3
- package/client/lib/tinymce/skins/ui/oxide-dark/skin.ts +8 -3
- package/client/lib/tinymce/skins/ui/tinymce-5/content.css +53 -42
- package/client/lib/tinymce/skins/ui/tinymce-5/content.inline.css +53 -42
- package/client/lib/tinymce/skins/ui/tinymce-5/content.inline.js +1 -1
- package/client/lib/tinymce/skins/ui/tinymce-5/content.inline.min.css +1 -1
- package/client/lib/tinymce/skins/ui/tinymce-5/content.inline.min.ts +2 -2
- package/client/lib/tinymce/skins/ui/tinymce-5/content.inline.ts +2 -2
- package/client/lib/tinymce/skins/ui/tinymce-5/content.js +1 -1
- package/client/lib/tinymce/skins/ui/tinymce-5/content.min.css +1 -1
- package/client/lib/tinymce/skins/ui/tinymce-5/content.min.ts +2 -2
- package/client/lib/tinymce/skins/ui/tinymce-5/content.ts +2 -2
- package/client/lib/tinymce/skins/ui/tinymce-5/skin.css +94 -22
- package/client/lib/tinymce/skins/ui/tinymce-5/skin.js +1 -1
- package/client/lib/tinymce/skins/ui/tinymce-5/skin.min.css +1 -1
- package/client/lib/tinymce/skins/ui/tinymce-5/skin.min.ts +8 -3
- package/client/lib/tinymce/skins/ui/tinymce-5/skin.ts +8 -3
- package/client/lib/tinymce/skins/ui/tinymce-5-dark/content.css +53 -42
- package/client/lib/tinymce/skins/ui/tinymce-5-dark/content.inline.css +53 -42
- package/client/lib/tinymce/skins/ui/tinymce-5-dark/content.inline.js +1 -1
- package/client/lib/tinymce/skins/ui/tinymce-5-dark/content.inline.min.css +1 -1
- package/client/lib/tinymce/skins/ui/tinymce-5-dark/content.inline.min.ts +2 -2
- package/client/lib/tinymce/skins/ui/tinymce-5-dark/content.inline.ts +2 -2
- package/client/lib/tinymce/skins/ui/tinymce-5-dark/content.js +1 -1
- package/client/lib/tinymce/skins/ui/tinymce-5-dark/content.min.css +1 -1
- package/client/lib/tinymce/skins/ui/tinymce-5-dark/content.min.ts +2 -2
- package/client/lib/tinymce/skins/ui/tinymce-5-dark/content.ts +2 -2
- package/client/lib/tinymce/skins/ui/tinymce-5-dark/skin.css +94 -22
- package/client/lib/tinymce/skins/ui/tinymce-5-dark/skin.js +1 -1
- package/client/lib/tinymce/skins/ui/tinymce-5-dark/skin.min.css +1 -1
- package/client/lib/tinymce/skins/ui/tinymce-5-dark/skin.min.ts +8 -3
- package/client/lib/tinymce/skins/ui/tinymce-5-dark/skin.ts +8 -3
- package/client/lib/tinymce/themes/silver/theme.js +1602 -579
- package/client/lib/tinymce/themes/silver/theme.min.js +3 -3
- package/client/lib/tinymce/tinymce.js +1410 -294
- package/client/lib/tinymce/tinymce.min.js +5 -5
- package/client/lib/tinymce-langs.js +96 -0
- package/client/lib/tinymce-langs.js.map +1 -0
- package/client/lib/tinymce-langs.ts +99 -0
- package/docs/editor.md +29 -3
- package/docs/preferences.md +2 -1
- package/npmchanges.md +63 -0
- package/package.json +11 -8
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-59216 → node_modules.npmglobalize-stash-65048}/.package-lock.json +0 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TinyMCE UI-language packs: what is available, and which one a setting
|
|
3
|
+
* resolves to.
|
|
4
|
+
*
|
|
5
|
+
* Scope: these packs translate the editor CHROME — menus, dialogs, tooltips.
|
|
6
|
+
* They do not touch message content, the spellchecker (English dictionary) or
|
|
7
|
+
* Harper (English only). Choosing German gets you a German toolbar around an
|
|
8
|
+
* unchanged English writing experience.
|
|
9
|
+
*
|
|
10
|
+
* TinyMCE has NO auto-detection: its `language` option defaults to "en", and
|
|
11
|
+
* for anything else it fetches `<baseURL>/langs/<code>.js` and raises an
|
|
12
|
+
* in-editor error notification if that file is missing. Detection is therefore
|
|
13
|
+
* ours, and we must only ever hand it a code we shipped a pack for.
|
|
14
|
+
*
|
|
15
|
+
* The packs are copied next to tinymce.min.js by bin/build-tinymce.js, which
|
|
16
|
+
* also writes langs/index.json listing what it copied. Resolving against that
|
|
17
|
+
* manifest — rather than a hardcoded list — means the copied files stay the
|
|
18
|
+
* single source of truth for what can be offered, and a custom
|
|
19
|
+
* `mailx-tinymce-cdn` override is followed automatically because the manifest
|
|
20
|
+
* URL is derived from the script URL by the same rule TinyMCE uses.
|
|
21
|
+
*
|
|
22
|
+
* (Claude Code 2026-08-23)
|
|
23
|
+
*/
|
|
24
|
+
/** Setting value meaning "follow the browser's language list". */
|
|
25
|
+
export const LANGUAGE_AUTO = "auto";
|
|
26
|
+
/** TinyMCE's built-in strings. No pack, no fetch. */
|
|
27
|
+
export const LANGUAGE_ENGLISH = "en";
|
|
28
|
+
/** Where build-tinymce.js wrote the list of packs it copied, derived from the
|
|
29
|
+
* TinyMCE script URL exactly as TinyMCE derives `langs/<code>.js`. */
|
|
30
|
+
export function langManifestUrl(tinymceScriptUrl) {
|
|
31
|
+
return new URL("langs/index.json", new URL(tinymceScriptUrl, location.href)).href;
|
|
32
|
+
}
|
|
33
|
+
/** Language codes we have packs for, e.g. ["ar", "de", "pt-BR", …]. Empty when
|
|
34
|
+
* the manifest is missing (an older build, or a CDN override pointing at a
|
|
35
|
+
* tree without our langs/ directory) — callers then stay on English rather
|
|
36
|
+
* than provoking TinyMCE's load-error notification. */
|
|
37
|
+
export async function availableLanguages(tinymceScriptUrl) {
|
|
38
|
+
try {
|
|
39
|
+
const r = await fetch(langManifestUrl(tinymceScriptUrl));
|
|
40
|
+
if (!r.ok)
|
|
41
|
+
return [];
|
|
42
|
+
const list = await r.json();
|
|
43
|
+
return Array.isArray(list) ? list.filter(x => typeof x === "string") : [];
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Resolve a stored setting to a code TinyMCE can actually load.
|
|
51
|
+
*
|
|
52
|
+
* `setting` is either LANGUAGE_AUTO, LANGUAGE_ENGLISH, or a pack code. A code
|
|
53
|
+
* we no longer ship falls back to English rather than failing at load time.
|
|
54
|
+
*
|
|
55
|
+
* Auto walks the browser's ordered preference list and takes the first hit:
|
|
56
|
+
* an exact pack ("pt-BR"), then the bare language ("pt"), then any regional
|
|
57
|
+
* pack for that language ("pt" → "pt-BR"). English short-circuits — it needs
|
|
58
|
+
* no pack. Matching is case-insensitive because BCP-47 region subtags are
|
|
59
|
+
* conventionally upper-case in the pack names and browsers are not required
|
|
60
|
+
* to agree.
|
|
61
|
+
*/
|
|
62
|
+
export function resolveLanguage(setting, available, preferred = navigator.languages || [navigator.language]) {
|
|
63
|
+
const byLower = new Map(available.map(code => [code.toLowerCase(), code]));
|
|
64
|
+
if (setting && setting !== LANGUAGE_AUTO) {
|
|
65
|
+
if (setting === LANGUAGE_ENGLISH)
|
|
66
|
+
return LANGUAGE_ENGLISH;
|
|
67
|
+
return byLower.get(setting.toLowerCase()) || LANGUAGE_ENGLISH;
|
|
68
|
+
}
|
|
69
|
+
for (const tag of preferred) {
|
|
70
|
+
if (!tag)
|
|
71
|
+
continue;
|
|
72
|
+
const lower = tag.toLowerCase();
|
|
73
|
+
const base = lower.split("-")[0];
|
|
74
|
+
if (base === LANGUAGE_ENGLISH)
|
|
75
|
+
return LANGUAGE_ENGLISH;
|
|
76
|
+
const hit = byLower.get(lower)
|
|
77
|
+
|| byLower.get(base)
|
|
78
|
+
|| available.find(code => code.toLowerCase().split("-")[0] === base);
|
|
79
|
+
if (hit)
|
|
80
|
+
return hit;
|
|
81
|
+
}
|
|
82
|
+
return LANGUAGE_ENGLISH;
|
|
83
|
+
}
|
|
84
|
+
/** Human-readable name for a pack code, in the reader's own language —
|
|
85
|
+
* `Intl.DisplayNames` already knows every one of these, so no name table is
|
|
86
|
+
* maintained here. Falls back to the raw code on a runtime that doesn't. */
|
|
87
|
+
export function languageLabel(code) {
|
|
88
|
+
try {
|
|
89
|
+
const names = new Intl.DisplayNames(navigator.languages || undefined, { type: "language" });
|
|
90
|
+
return names.of(code) || code;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return code;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=tinymce-langs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tinymce-langs.js","sourceRoot":"","sources":["tinymce-langs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TinyMCE UI-language packs: what is available, and which one a setting
|
|
3
|
+
* resolves to.
|
|
4
|
+
*
|
|
5
|
+
* Scope: these packs translate the editor CHROME — menus, dialogs, tooltips.
|
|
6
|
+
* They do not touch message content, the spellchecker (English dictionary) or
|
|
7
|
+
* Harper (English only). Choosing German gets you a German toolbar around an
|
|
8
|
+
* unchanged English writing experience.
|
|
9
|
+
*
|
|
10
|
+
* TinyMCE has NO auto-detection: its `language` option defaults to "en", and
|
|
11
|
+
* for anything else it fetches `<baseURL>/langs/<code>.js` and raises an
|
|
12
|
+
* in-editor error notification if that file is missing. Detection is therefore
|
|
13
|
+
* ours, and we must only ever hand it a code we shipped a pack for.
|
|
14
|
+
*
|
|
15
|
+
* The packs are copied next to tinymce.min.js by bin/build-tinymce.js, which
|
|
16
|
+
* also writes langs/index.json listing what it copied. Resolving against that
|
|
17
|
+
* manifest — rather than a hardcoded list — means the copied files stay the
|
|
18
|
+
* single source of truth for what can be offered, and a custom
|
|
19
|
+
* `mailx-tinymce-cdn` override is followed automatically because the manifest
|
|
20
|
+
* URL is derived from the script URL by the same rule TinyMCE uses.
|
|
21
|
+
*
|
|
22
|
+
* (Claude Code 2026-08-23)
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** Setting value meaning "follow the browser's language list". */
|
|
26
|
+
export const LANGUAGE_AUTO = "auto";
|
|
27
|
+
/** TinyMCE's built-in strings. No pack, no fetch. */
|
|
28
|
+
export const LANGUAGE_ENGLISH = "en";
|
|
29
|
+
|
|
30
|
+
/** Where build-tinymce.js wrote the list of packs it copied, derived from the
|
|
31
|
+
* TinyMCE script URL exactly as TinyMCE derives `langs/<code>.js`. */
|
|
32
|
+
export function langManifestUrl(tinymceScriptUrl: string): string {
|
|
33
|
+
return new URL("langs/index.json", new URL(tinymceScriptUrl, location.href)).href;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Language codes we have packs for, e.g. ["ar", "de", "pt-BR", …]. Empty when
|
|
37
|
+
* the manifest is missing (an older build, or a CDN override pointing at a
|
|
38
|
+
* tree without our langs/ directory) — callers then stay on English rather
|
|
39
|
+
* than provoking TinyMCE's load-error notification. */
|
|
40
|
+
export async function availableLanguages(tinymceScriptUrl: string): Promise<string[]> {
|
|
41
|
+
try {
|
|
42
|
+
const r = await fetch(langManifestUrl(tinymceScriptUrl));
|
|
43
|
+
if (!r.ok) return [];
|
|
44
|
+
const list = await r.json();
|
|
45
|
+
return Array.isArray(list) ? list.filter(x => typeof x === "string") : [];
|
|
46
|
+
} catch {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Resolve a stored setting to a code TinyMCE can actually load.
|
|
53
|
+
*
|
|
54
|
+
* `setting` is either LANGUAGE_AUTO, LANGUAGE_ENGLISH, or a pack code. A code
|
|
55
|
+
* we no longer ship falls back to English rather than failing at load time.
|
|
56
|
+
*
|
|
57
|
+
* Auto walks the browser's ordered preference list and takes the first hit:
|
|
58
|
+
* an exact pack ("pt-BR"), then the bare language ("pt"), then any regional
|
|
59
|
+
* pack for that language ("pt" → "pt-BR"). English short-circuits — it needs
|
|
60
|
+
* no pack. Matching is case-insensitive because BCP-47 region subtags are
|
|
61
|
+
* conventionally upper-case in the pack names and browsers are not required
|
|
62
|
+
* to agree.
|
|
63
|
+
*/
|
|
64
|
+
export function resolveLanguage(
|
|
65
|
+
setting: string,
|
|
66
|
+
available: readonly string[],
|
|
67
|
+
preferred: readonly string[] = navigator.languages || [navigator.language],
|
|
68
|
+
): string {
|
|
69
|
+
const byLower = new Map(available.map(code => [code.toLowerCase(), code]));
|
|
70
|
+
|
|
71
|
+
if (setting && setting !== LANGUAGE_AUTO) {
|
|
72
|
+
if (setting === LANGUAGE_ENGLISH) return LANGUAGE_ENGLISH;
|
|
73
|
+
return byLower.get(setting.toLowerCase()) || LANGUAGE_ENGLISH;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for (const tag of preferred) {
|
|
77
|
+
if (!tag) continue;
|
|
78
|
+
const lower = tag.toLowerCase();
|
|
79
|
+
const base = lower.split("-")[0];
|
|
80
|
+
if (base === LANGUAGE_ENGLISH) return LANGUAGE_ENGLISH;
|
|
81
|
+
const hit = byLower.get(lower)
|
|
82
|
+
|| byLower.get(base)
|
|
83
|
+
|| available.find(code => code.toLowerCase().split("-")[0] === base);
|
|
84
|
+
if (hit) return hit;
|
|
85
|
+
}
|
|
86
|
+
return LANGUAGE_ENGLISH;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Human-readable name for a pack code, in the reader's own language —
|
|
90
|
+
* `Intl.DisplayNames` already knows every one of these, so no name table is
|
|
91
|
+
* maintained here. Falls back to the raw code on a runtime that doesn't. */
|
|
92
|
+
export function languageLabel(code: string): string {
|
|
93
|
+
try {
|
|
94
|
+
const names = new Intl.DisplayNames(navigator.languages || undefined, { type: "language" });
|
|
95
|
+
return names.of(code) || code;
|
|
96
|
+
} catch {
|
|
97
|
+
return code;
|
|
98
|
+
}
|
|
99
|
+
}
|
package/docs/editor.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# Compose editor — formatting and shortcuts
|
|
2
2
|
|
|
3
|
-
mailx ships with
|
|
4
|
-
Most things work the same in
|
|
3
|
+
mailx ships with three rich-text editors: **Quill** (default), **tiptap**,
|
|
4
|
+
and **TinyMCE**. Most things work the same in all of them. Differences are
|
|
5
|
+
called out below.
|
|
5
6
|
|
|
6
|
-
Switch editors via **Settings → Editor → Quill | tiptap**.
|
|
7
|
+
Switch editors via **Settings → Editor → Quill | tiptap | TinyMCE**.
|
|
7
8
|
|
|
8
9
|
## Universal — works in both editors
|
|
9
10
|
|
|
@@ -74,6 +75,31 @@ use the heading select / format menu instead.
|
|
|
74
75
|
- **Ghost-text autocomplete** (off by default) — Settings →
|
|
75
76
|
AI autocomplete → on. Predicts the next words while you type.
|
|
76
77
|
|
|
78
|
+
## Editor language (TinyMCE only)
|
|
79
|
+
|
|
80
|
+
**Settings → Editor → Editor language** translates the editor *chrome* —
|
|
81
|
+
menus, dialogs, tooltips — into one of 61 languages. It does **not** touch:
|
|
82
|
+
|
|
83
|
+
- your message text,
|
|
84
|
+
- the spellchecker (English dictionary),
|
|
85
|
+
- the grammar check (Harper is English-only).
|
|
86
|
+
|
|
87
|
+
So German chrome wraps an unchanged English writing experience. Quill and
|
|
88
|
+
tiptap have no localized chrome and ignore the setting.
|
|
89
|
+
|
|
90
|
+
**Auto** (the default) follows this browser's language list, taking the first
|
|
91
|
+
one we have a pack for: an exact match (`pt-BR`), then the bare language
|
|
92
|
+
(`de-CH` → `de`), then any regional variant of it (`he` → `he-IL`). English
|
|
93
|
+
short-circuits — TinyMCE's built-in strings are English, there is no pack to
|
|
94
|
+
load. The menu names what Auto currently resolves to, so it is never a guess.
|
|
95
|
+
|
|
96
|
+
Everything is local: the packs ship with the app, and only the one language in
|
|
97
|
+
force is ever loaded. Translation coverage is community-contributed and uneven
|
|
98
|
+
— untranslated strings stay English even in widely-used languages.
|
|
99
|
+
|
|
100
|
+
The language is fixed when a compose window opens, so a change takes effect on
|
|
101
|
+
the next Reply / Forward / Compose, not in a window already open.
|
|
102
|
+
|
|
77
103
|
## When the toolbar doesn't appear
|
|
78
104
|
|
|
79
105
|
The editor loads from a CDN (jsdelivr). If your network can't reach it,
|
package/docs/preferences.md
CHANGED
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"flaggedOnly": false,
|
|
21
21
|
"folderCounts": false,
|
|
22
22
|
"calendarSidebar": false,
|
|
23
|
-
"editor": "quill"
|
|
23
|
+
"editor": "quill", // "quill" | "tiptap" | "tinymce"
|
|
24
|
+
"editorLanguage": "auto" // TinyMCE menu/dialog language; "auto" | "en" | pack code ("de", "pt-BR", ...)
|
|
24
25
|
},
|
|
25
26
|
"sync": {
|
|
26
27
|
"intervalMinutes": 5, // full-sync interval (per-folder); IDLE handles INBOX inline
|
package/npmchanges.md
CHANGED
|
@@ -501,3 +501,66 @@ gets retried instead of staying broken until the next -repair. Pairs
|
|
|
501
501
|
with the elapsed-time wait indicator (1.1.135) which surfaces the
|
|
502
502
|
in-progress retry to the user.
|
|
503
503
|
|
|
504
|
+
## v1.2.276 — 2026-08-23
|
|
505
|
+
|
|
506
|
+
Preview hotkeys fired twice — Ctrl+F opened two compose windows
|
|
507
|
+
|
|
508
|
+
A keydown in the message preview reached app.ts by TWO routes on desktop
|
|
509
|
+
WebView2: the iframe's own inline script posts every keydown to the parent as
|
|
510
|
+
`previewKey` (app.ts re-dispatches it), and installPreviewControls ALSO attached
|
|
511
|
+
a contentDocument listener that re-dispatched a synthetic keydown of its own.
|
|
512
|
+
The srcdoc document is same-origin, so both are live. One Ctrl+F therefore ran
|
|
513
|
+
openCompose("forward") twice and opened two compose popouts — the second never
|
|
514
|
+
finished loading and sat blank and "(Not Responding)" until it was killed (Bob
|
|
515
|
+
2026-08-23, "Compose: Fwd: FirstCare Slide Deck (Not Responding)"; the log shows
|
|
516
|
+
openCompose-entry mode=forward twice in the same millisecond, two popoutCompose
|
|
517
|
+
windows, and the loaded one exiting while the blank duplicate survived). Ctrl+D
|
|
518
|
+
and Ctrl+P doubled the same way.
|
|
519
|
+
|
|
520
|
+
The inline forwarder is the mechanism that survives — it is the only one that
|
|
521
|
+
works on Android WebView, where parent-side contentDocument listeners never
|
|
522
|
+
fire. installPreviewControls keeps only what is genuinely iframe-local: zoom and
|
|
523
|
+
Ctrl+A.
|
|
524
|
+
|
|
525
|
+
Also: push the Windows taskbar overlay only when the unread count CHANGES.
|
|
526
|
+
Windows holds the overlay icon until the next SetOverlayIcon, so the host cannot
|
|
527
|
+
free the icon it just handed over; re-sending an identical badge every few
|
|
528
|
+
seconds cost GDI objects for nothing and helped drive rmfmail.exe to the
|
|
529
|
+
10,000-object per-process cap (measured at GDI=9995, with 3423
|
|
530
|
+
"CreateIconIndirect failed: Not enough memory resources" in one day's log).
|
|
531
|
+
msger 0.1.429 fixes the leak itself by freeing the retired icon.
|
|
532
|
+
|
|
533
|
+
## v1.2.277 — 2026-08-23
|
|
534
|
+
|
|
535
|
+
TinyMCE UI language: 61 packs, auto-detected from the browser
|
|
536
|
+
|
|
537
|
+
The tinymce package ships no langs/ directory at all — the packs are a separate
|
|
538
|
+
community package (tinymce-i18n), one file per language, each a plain
|
|
539
|
+
tinymce.addI18n("de", {...}). build-tinymce.js now copies langs8/ next to
|
|
540
|
+
tinymce.min.js, where TinyMCE's own <baseURL>/langs/<code>.js rule finds it, and
|
|
541
|
+
writes langs/index.json listing what it copied. 61 packs, 1.6 MB; only the
|
|
542
|
+
language in force is ever fetched.
|
|
543
|
+
|
|
544
|
+
TinyMCE has no detection of its own: `language` defaults to "en" and a missing
|
|
545
|
+
langs/<code>.js raises a visible in-editor error notification. So "Auto" is
|
|
546
|
+
ours (client/lib/tinymce-langs.ts): walk navigator.languages and take the first
|
|
547
|
+
hit against the manifest — exact ("pt-BR"), then bare ("de-CH" → "de"), then
|
|
548
|
+
any regional variant ("he" → "he-IL"). English short-circuits, since TinyMCE's
|
|
549
|
+
built-in strings are English. Anything unmatched, and any pinned pack we no
|
|
550
|
+
longer ship, stays English rather than provoking the error. Resolving against
|
|
551
|
+
the manifest rather than a hardcoded list keeps the copied files the single
|
|
552
|
+
source of truth, and follows a mailx-tinymce-cdn override for free because the
|
|
553
|
+
manifest URL is derived from the script URL by the same rule TinyMCE uses.
|
|
554
|
+
|
|
555
|
+
Settings → Editor → Editor language: Auto (naming what it resolves to right
|
|
556
|
+
now), English, then every pack, named via Intl.DisplayNames so no name table is
|
|
557
|
+
maintained here. Stored as ui.editorLanguage; same synchronous-localStorage-
|
|
558
|
+
then-persist shape as the editor type, so the next compose-open is correct.
|
|
559
|
+
|
|
560
|
+
Scope, stated in the menu tooltip and docs/editor.md: these translate the
|
|
561
|
+
editor CHROME only. Message content, the spellcheck dictionary (English) and
|
|
562
|
+
Harper (English only) are untouched, and Quill/tiptap have no localized chrome.
|
|
563
|
+
|
|
564
|
+
tests/tinymce-langs.test.ts pins the resolution against the real manifest.
|
|
565
|
+
Needs rmf-tiny 0.1.52 for the adapter's `language` option.
|
|
566
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/rmfmail",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.278",
|
|
4
4
|
"description": "Local-first email client with IMAP sync and standalone native app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/mailx.js",
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@bobfrankston/iflow-direct": "^0.1.65",
|
|
36
36
|
"@bobfrankston/mailx-host": "^0.1.15",
|
|
37
|
-
"@bobfrankston/mailx-imap": "^0.1.
|
|
37
|
+
"@bobfrankston/mailx-imap": "^0.1.158",
|
|
38
38
|
"@bobfrankston/mailx-store-web": "^0.1.86",
|
|
39
39
|
"@bobfrankston/mailx-sync": "^0.1.29",
|
|
40
40
|
"@bobfrankston/miscinfo": "^1.0.20",
|
|
41
41
|
"@bobfrankston/msger": "^0.1.429",
|
|
42
42
|
"@bobfrankston/node-tcp-transport": "^0.1.10",
|
|
43
43
|
"@bobfrankston/oauthsupport": "^1.0.35",
|
|
44
|
-
"@bobfrankston/rmf-tiny": "^0.1.
|
|
44
|
+
"@bobfrankston/rmf-tiny": "^0.1.52",
|
|
45
45
|
"@bobfrankston/smtp-direct": "^0.1.9",
|
|
46
46
|
"@bobfrankston/tcp-transport": "^0.1.8",
|
|
47
47
|
"@capacitor/android": "^8.3.0",
|
|
@@ -58,7 +58,8 @@
|
|
|
58
58
|
"nspell": "^2.1.5",
|
|
59
59
|
"quill": "^2.0.3",
|
|
60
60
|
"sql.js": "^1.14.1",
|
|
61
|
-
"tinymce": "^8.
|
|
61
|
+
"tinymce": "^8.8.2",
|
|
62
|
+
"tinymce-i18n": "26.8.2"
|
|
62
63
|
},
|
|
63
64
|
"devDependencies": {
|
|
64
65
|
"@types/mailparser": "^3.4.6",
|
|
@@ -108,20 +109,21 @@
|
|
|
108
109
|
"nspell": "^2.1.5",
|
|
109
110
|
"quill": "^2.0.3",
|
|
110
111
|
"sql.js": "^1.14.1",
|
|
111
|
-
"tinymce": "^8.
|
|
112
|
+
"tinymce": "^8.8.2",
|
|
113
|
+
"tinymce-i18n": "26.8.2"
|
|
112
114
|
},
|
|
113
115
|
".transformedSnapshot": {
|
|
114
116
|
"dependencies": {
|
|
115
117
|
"@bobfrankston/iflow-direct": "^0.1.65",
|
|
116
118
|
"@bobfrankston/mailx-host": "^0.1.15",
|
|
117
|
-
"@bobfrankston/mailx-imap": "^0.1.
|
|
119
|
+
"@bobfrankston/mailx-imap": "^0.1.158",
|
|
118
120
|
"@bobfrankston/mailx-store-web": "^0.1.86",
|
|
119
121
|
"@bobfrankston/mailx-sync": "^0.1.29",
|
|
120
122
|
"@bobfrankston/miscinfo": "^1.0.20",
|
|
121
123
|
"@bobfrankston/msger": "^0.1.429",
|
|
122
124
|
"@bobfrankston/node-tcp-transport": "^0.1.10",
|
|
123
125
|
"@bobfrankston/oauthsupport": "^1.0.35",
|
|
124
|
-
"@bobfrankston/rmf-tiny": "^0.1.
|
|
126
|
+
"@bobfrankston/rmf-tiny": "^0.1.52",
|
|
125
127
|
"@bobfrankston/smtp-direct": "^0.1.9",
|
|
126
128
|
"@bobfrankston/tcp-transport": "^0.1.8",
|
|
127
129
|
"@capacitor/android": "^8.3.0",
|
|
@@ -138,7 +140,8 @@
|
|
|
138
140
|
"nspell": "^2.1.5",
|
|
139
141
|
"quill": "^2.0.3",
|
|
140
142
|
"sql.js": "^1.14.1",
|
|
141
|
-
"tinymce": "^8.
|
|
143
|
+
"tinymce": "^8.8.2",
|
|
144
|
+
"tinymce-i18n": "26.8.2"
|
|
142
145
|
}
|
|
143
146
|
}
|
|
144
147
|
}
|