@bobfrankston/rmfmail 1.0.634 → 1.0.643
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/TODO.md +4 -1
- package/client/app.js +22 -0
- package/client/app.js.map +1 -1
- package/client/app.ts +15 -0
- package/client/components/alarms.js +17 -7
- package/client/components/alarms.js.map +1 -1
- package/client/components/alarms.ts +17 -6
- package/client/compose/compose.css +14 -1
- package/client/compose/compose.html +1 -0
- package/client/compose/compose.js +34 -15
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +37 -8
- package/client/compose/editor.js +44 -3
- package/client/compose/editor.js.map +1 -1
- package/client/compose/editor.ts +49 -3
- package/client/index.html +273 -15
- package/client/styles/components.css +14 -0
- package/docs/prod-android.md +55 -69
- package/docs/rmf-tiny.md +156 -0
- package/package.json +7 -1
- package/packages/mailx-store-web/package.json +1 -1
package/docs/rmf-tiny.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# `rmf-tiny` — optional TinyMCE editor adapter for rmfmail
|
|
2
|
+
|
|
3
|
+
Status: design only. Not implemented.
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
Let users who want Thunderbird-class paste fidelity opt into TinyMCE without baking it into rmfmail itself. Bob 2026-05-09 verified: pasted a Word document into Thunderbird, tiptap, Quill, and TinyMCE — only TinyMCE preserved the formatting (boxed monospace block with borders, paragraph spacing, font preservation). Nothing else came close.
|
|
8
|
+
|
|
9
|
+
The license catch — TinyMCE is GPLv2 — means rmfmail (MIT) can't bundle it. The arms-length pattern is the resolution: rmfmail ships **no** TinyMCE bytes; a separate `rmf-tiny` package the user installs themselves provides the adapter + pulls in TinyMCE via npm.
|
|
10
|
+
|
|
11
|
+
## Shape
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
@bobfrankston/rmfmail ← MIT, ships no TinyMCE
|
|
15
|
+
client/compose/editor.ts ← MailxEditor interface + factory
|
|
16
|
+
gains a "tinymce" branch that
|
|
17
|
+
dynamically imports rmf-tiny
|
|
18
|
+
|
|
19
|
+
@bobfrankston/rmf-tiny ← MIT (the adapter glue is original code)
|
|
20
|
+
package.json ← peerDependency: tinymce
|
|
21
|
+
src/adapter.ts ← implements MailxEditor against TinyMCE
|
|
22
|
+
README.md ← install instructions
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
User opts in:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
npm install -g @bobfrankston/rmf-tiny tinymce
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
(Two packages explicit — keeps the licensing posture clean. rmf-tiny is just the adapter; tinymce comes from Tiny's own npm publication, not bundled with anything rmfmail-related.)
|
|
32
|
+
|
|
33
|
+
In rmfmail Settings → Compose → Editor: the dropdown grows a "TinyMCE (requires rmf-tiny)" option. If selected and the import fails, surface a status-bar error pointing at the install command.
|
|
34
|
+
|
|
35
|
+
## Why a separate package, not a flag inside rmfmail
|
|
36
|
+
|
|
37
|
+
- rmfmail's git history and npm tarball never contain TinyMCE → distribution layer is clean.
|
|
38
|
+
- rmf-tiny is small (~200 lines of adapter glue + a thin install README) and can be MIT — no GPL infection because it imports TinyMCE at runtime via the same kind of dynamic boundary npm packages always use. Combination at user's machine is fully GPL-permitted.
|
|
39
|
+
- Other editors can follow the same pattern (`rmf-ckeditor`, `rmf-prosemirror-pro`) without polluting rmfmail.
|
|
40
|
+
|
|
41
|
+
## Why call it `rmf-tiny`
|
|
42
|
+
|
|
43
|
+
Short, distinguishable from the upstream package, namespaces under your scope (`@bobfrankston/rmf-tiny`), and "tiny" is the upstream's own brand so the connection is obvious. Other naming options:
|
|
44
|
+
- `@bobfrankston/mailx-tinymce-adapter` (verbose, descriptive)
|
|
45
|
+
- `@bobfrankston/rmftiny` (no hyphen, matches rmfmail style)
|
|
46
|
+
|
|
47
|
+
`rmf-tiny` is fine; pick the form that scans cleanest in the install line.
|
|
48
|
+
|
|
49
|
+
## Files
|
|
50
|
+
|
|
51
|
+
### `rmf-tiny/package.json`
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"name": "@bobfrankston/rmf-tiny",
|
|
56
|
+
"version": "0.1.0",
|
|
57
|
+
"description": "TinyMCE editor adapter for rmfmail. Bring-your-own TinyMCE.",
|
|
58
|
+
"type": "module",
|
|
59
|
+
"main": "src/adapter.js",
|
|
60
|
+
"license": "MIT",
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"tinymce": ">=6"
|
|
63
|
+
},
|
|
64
|
+
"peerDependenciesMeta": {
|
|
65
|
+
"tinymce": { "optional": false }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`peerDependencies` puts the install responsibility on the user — `npm install @bobfrankston/rmf-tiny` warns that `tinymce` must also be installed; rmf-tiny does not vendor or bundle TinyMCE.
|
|
71
|
+
|
|
72
|
+
### `rmf-tiny/src/adapter.ts`
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
// MIT-licensed adapter glue. Imports the user-installed TinyMCE at
|
|
76
|
+
// runtime; doesn't redistribute any TinyMCE bytes. Implements the same
|
|
77
|
+
// `MailxEditor` interface rmfmail's createEditor factory expects, so
|
|
78
|
+
// the host code path is identical.
|
|
79
|
+
import type { Editor as TinyEditor } from "tinymce";
|
|
80
|
+
|
|
81
|
+
export interface MailxEditor {
|
|
82
|
+
setHtml(html: string): void;
|
|
83
|
+
getHtml(): string;
|
|
84
|
+
getText(): string;
|
|
85
|
+
focus(): void;
|
|
86
|
+
setCursor(pos: number): void;
|
|
87
|
+
root: HTMLElement;
|
|
88
|
+
// …other methods rmfmail's interface requires
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function createTinyMceEditor(container: HTMLElement): Promise<MailxEditor> {
|
|
92
|
+
// Dynamic import — fails clearly if the user hasn't installed tinymce.
|
|
93
|
+
const tinymce = (await import("tinymce")).default;
|
|
94
|
+
// Plus the plugins TinyMCE's paste-from-Word handler needs:
|
|
95
|
+
await Promise.all([
|
|
96
|
+
import("tinymce/themes/silver"),
|
|
97
|
+
import("tinymce/icons/default"),
|
|
98
|
+
import("tinymce/plugins/paste"),
|
|
99
|
+
import("tinymce/plugins/lists"),
|
|
100
|
+
import("tinymce/plugins/link"),
|
|
101
|
+
import("tinymce/plugins/table"),
|
|
102
|
+
import("tinymce/plugins/code"),
|
|
103
|
+
]);
|
|
104
|
+
// ... initialize tinymce against `container`, return MailxEditor shim.
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The actual init/wire-up is the work — wrapping TinyMCE's API in our `MailxEditor` shape, hooking paste events, setHtml/getHtml round-tripping, etc. Maybe ~200 lines.
|
|
109
|
+
|
|
110
|
+
### rmfmail-side change to `editor.ts`
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
export async function createEditor(
|
|
114
|
+
container: HTMLElement,
|
|
115
|
+
type: "quill" | "tiptap" | "tinymce"
|
|
116
|
+
): Promise<MailxEditor> {
|
|
117
|
+
if (type === "tinymce") {
|
|
118
|
+
try {
|
|
119
|
+
const m = await import("@bobfrankston/rmf-tiny");
|
|
120
|
+
return m.createTinyMceEditor(container);
|
|
121
|
+
} catch (e: any) {
|
|
122
|
+
const status = document.getElementById("status-sync");
|
|
123
|
+
if (status) status.textContent = `TinyMCE editor not available — install: npm install -g @bobfrankston/rmf-tiny tinymce`;
|
|
124
|
+
return createQuillEditor(container); // fall back so compose still works
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (type === "tiptap") return createTiptapEditor(container);
|
|
128
|
+
return createQuillEditor(container);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Settings panel adds the "TinyMCE" option in the editor dropdown.
|
|
133
|
+
|
|
134
|
+
## Effort
|
|
135
|
+
|
|
136
|
+
- `rmf-tiny` package skeleton (package.json, README, basic adapter shell): ~30 minutes.
|
|
137
|
+
- Adapter wiring (init, setHtml/getHtml, paste handling, toolbar config): ~3 hours including testing the Word-paste case.
|
|
138
|
+
- rmfmail-side factory branch + Settings dropdown option: ~30 minutes.
|
|
139
|
+
- Documentation for users (README in rmf-tiny + Settings tooltip): ~30 minutes.
|
|
140
|
+
|
|
141
|
+
About half a day total.
|
|
142
|
+
|
|
143
|
+
## Order
|
|
144
|
+
|
|
145
|
+
1. Build `rmf-tiny` against your local TinyMCE install. Verify the Word-paste case actually works through your adapter.
|
|
146
|
+
2. Add the factory branch and Settings option to rmfmail.
|
|
147
|
+
3. Publish `rmf-tiny` to npm.
|
|
148
|
+
4. Document the opt-in install command in rmfmail's Settings tooltip and `README.md`.
|
|
149
|
+
|
|
150
|
+
Don't publish the adapter until the Word-paste case actually works end-to-end through the adapter — TinyMCE direct is one thing, our adapter possibly mangles it differently.
|
|
151
|
+
|
|
152
|
+
## Caveats
|
|
153
|
+
|
|
154
|
+
- The adapter must not re-implement parts of TinyMCE — it's a thin wrapper. Re-implementing parts of GPL code in MIT is the legal trap; staying purely in adapter / interface territory is safe.
|
|
155
|
+
- TinyMCE's bundle is large (~1MB minified). Users opting in pay that cost on the first compose-window load. Fine for opt-in; would be unacceptable as a default.
|
|
156
|
+
- `rmf-tiny` major version should track TinyMCE's major version (rmf-tiny@7.x for tinymce@7.x) so peerDependency mismatches are obvious.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/rmfmail",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.643",
|
|
4
4
|
"description": "Local-first email client with IMAP sync and standalone native app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/mailx.js",
|
|
@@ -36,11 +36,13 @@
|
|
|
36
36
|
"@bobfrankston/iflow-direct": "^0.1.39",
|
|
37
37
|
"@bobfrankston/mailx-host": "^0.1.11",
|
|
38
38
|
"@bobfrankston/mailx-imap": "^0.1.34",
|
|
39
|
+
"@bobfrankston/mailx-store-web": "^0.1.4",
|
|
39
40
|
"@bobfrankston/mailx-sync": "^0.1.16",
|
|
40
41
|
"@bobfrankston/miscinfo": "^1.0.10",
|
|
41
42
|
"@bobfrankston/msger": "^0.1.378",
|
|
42
43
|
"@bobfrankston/node-tcp-transport": "^0.1.8",
|
|
43
44
|
"@bobfrankston/oauthsupport": "^1.0.26",
|
|
45
|
+
"@bobfrankston/rmf-tiny": "^0.1.3",
|
|
44
46
|
"@bobfrankston/smtp-direct": "^0.1.8",
|
|
45
47
|
"@bobfrankston/tcp-transport": "^0.1.6",
|
|
46
48
|
"@capacitor/android": "^8.3.0",
|
|
@@ -80,11 +82,13 @@
|
|
|
80
82
|
"@bobfrankston/iflow-direct": "file:../../MailApps/iflow-direct",
|
|
81
83
|
"@bobfrankston/mailx-host": "file:packages/mailx-host",
|
|
82
84
|
"@bobfrankston/mailx-imap": "file:packages/mailx-imap",
|
|
85
|
+
"@bobfrankston/mailx-store-web": "file:packages/mailx-store-web",
|
|
83
86
|
"@bobfrankston/mailx-sync": "file:../../MailApps/mailx-sync",
|
|
84
87
|
"@bobfrankston/miscinfo": "file:../../../projects/npm/miscinfo",
|
|
85
88
|
"@bobfrankston/msger": "file:../../../utils/msgx/msger",
|
|
86
89
|
"@bobfrankston/node-tcp-transport": "file:../../MailApps/node-tcp-transport",
|
|
87
90
|
"@bobfrankston/oauthsupport": "file:../../../projects/oauth/oauthsupport",
|
|
91
|
+
"@bobfrankston/rmf-tiny": "file:../../../utils/msgx/libs/rmf-tiny",
|
|
88
92
|
"@bobfrankston/smtp-direct": "file:../../MailApps/smtp-direct",
|
|
89
93
|
"@bobfrankston/tcp-transport": "file:../../MailApps/tcp-transport",
|
|
90
94
|
"@capacitor/android": "^8.3.0",
|
|
@@ -105,11 +109,13 @@
|
|
|
105
109
|
"@bobfrankston/iflow-direct": "^0.1.39",
|
|
106
110
|
"@bobfrankston/mailx-host": "^0.1.11",
|
|
107
111
|
"@bobfrankston/mailx-imap": "^0.1.34",
|
|
112
|
+
"@bobfrankston/mailx-store-web": "^0.1.4",
|
|
108
113
|
"@bobfrankston/mailx-sync": "^0.1.16",
|
|
109
114
|
"@bobfrankston/miscinfo": "^1.0.10",
|
|
110
115
|
"@bobfrankston/msger": "^0.1.378",
|
|
111
116
|
"@bobfrankston/node-tcp-transport": "^0.1.8",
|
|
112
117
|
"@bobfrankston/oauthsupport": "^1.0.26",
|
|
118
|
+
"@bobfrankston/rmf-tiny": "^0.1.3",
|
|
113
119
|
"@bobfrankston/smtp-direct": "^0.1.8",
|
|
114
120
|
"@bobfrankston/tcp-transport": "^0.1.6",
|
|
115
121
|
"@capacitor/android": "^8.3.0",
|