@bobfrankston/rmfmail 1.2.120 → 1.2.122
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/client/app.bundle.js +14 -2
- package/client/app.bundle.js.map +2 -2
- package/client/app.js +24 -6
- package/client/app.js.map +1 -1
- package/client/app.ts +23 -6
- package/client/compose/compose.bundle.js +31 -8
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/compose/compose.html +0 -1
- package/client/compose/compose.js +15 -13
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +13 -11
- package/client/lib/rmf-tiny.js +37 -0
- package/package.json +3 -3
package/client/app.ts
CHANGED
|
@@ -1266,6 +1266,7 @@ function showComposeOverlay(title = "Compose"): HTMLIFrameElement {
|
|
|
1266
1266
|
const SVG_ATTRS = `width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"`;
|
|
1267
1267
|
const TRASH_SVG = `<svg ${SVG_ATTRS}><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>`;
|
|
1268
1268
|
const POPOUT_SVG = `<svg ${SVG_ATTRS}><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg>`;
|
|
1269
|
+
const MAXIMIZE_SVG = `<svg ${SVG_ATTRS}><rect x="4" y="4" width="16" height="16" rx="2"/><path d="M4 9h16"/></svg>`;
|
|
1269
1270
|
const RESTORE_SVG = `<svg ${SVG_ATTRS}><path d="M21 9v-6h-6"/><path d="M3 15v6h6"/><path d="M21 3l-7 7"/><path d="M3 21l7-7"/></svg>`;
|
|
1270
1271
|
const makeIconBtn = (svg: string, titleStr: string, hoverColor: string): HTMLButtonElement => {
|
|
1271
1272
|
const b = document.createElement("button");
|
|
@@ -1286,11 +1287,27 @@ function showComposeOverlay(title = "Compose"): HTMLIFrameElement {
|
|
|
1286
1287
|
});
|
|
1287
1288
|
btnCluster.appendChild(discardBtn);
|
|
1288
1289
|
|
|
1289
|
-
//
|
|
1290
|
-
//
|
|
1291
|
-
//
|
|
1292
|
-
//
|
|
1293
|
-
|
|
1290
|
+
// True popout — hand the live compose off to its own OS window (native
|
|
1291
|
+
// msger window via the daemon; see compose.ts popoutHandoff). Lives in
|
|
1292
|
+
// the TITLE BAR with the other frame controls because it's about the
|
|
1293
|
+
// frame, not the content (Bob 2026-07-12). The iframe owns the state, so
|
|
1294
|
+
// this just pokes it with an event, same pattern as Discard/Close.
|
|
1295
|
+
// Hidden on small screens where the overlay is full-screen and a separate
|
|
1296
|
+
// OS window makes no sense (and Android has no popout host).
|
|
1297
|
+
if (!isSmall) {
|
|
1298
|
+
const osPopoutBtn = makeIconBtn(POPOUT_SVG, "Open in separate window", "#000");
|
|
1299
|
+
osPopoutBtn.addEventListener("click", () => {
|
|
1300
|
+
try {
|
|
1301
|
+
const win = frame.contentWindow;
|
|
1302
|
+
if (win) win.dispatchEvent(new Event("compose-popout"));
|
|
1303
|
+
} catch { /* */ }
|
|
1304
|
+
});
|
|
1305
|
+
btnCluster.appendChild(osPopoutBtn);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// Maximize = toggle between the floating overlay and a host-window-filling
|
|
1309
|
+
// layout — second click restores the floating geometry.
|
|
1310
|
+
const popoutBtn = makeIconBtn(MAXIMIZE_SVG, "Maximize", "#000");
|
|
1294
1311
|
let maximized = false;
|
|
1295
1312
|
let savedCss = "";
|
|
1296
1313
|
popoutBtn.addEventListener("click", () => {
|
|
@@ -1302,7 +1319,7 @@ function showComposeOverlay(title = "Compose"): HTMLIFrameElement {
|
|
|
1302
1319
|
maximized = true;
|
|
1303
1320
|
} else {
|
|
1304
1321
|
wrapper.style.cssText = savedCss;
|
|
1305
|
-
popoutBtn.innerHTML =
|
|
1322
|
+
popoutBtn.innerHTML = MAXIMIZE_SVG;
|
|
1306
1323
|
popoutBtn.title = "Maximize";
|
|
1307
1324
|
maximized = false;
|
|
1308
1325
|
}
|
|
@@ -2187,6 +2187,26 @@ async function createTinyMceEditor(container2, opts = {}) {
|
|
|
2187
2187
|
setup: (ed) => {
|
|
2188
2188
|
if (opts.initialHtml)
|
|
2189
2189
|
ed.on("init", () => ed.setContent(opts.initialHtml));
|
|
2190
|
+
ed.on("GetContent", (e) => {
|
|
2191
|
+
if (!e?.content || typeof e.content !== "string" || !e.content.includes("blob:"))
|
|
2192
|
+
return;
|
|
2193
|
+
e.content = e.content.replace(/src="(blob:[^"]+)"/g, (m, uri) => {
|
|
2194
|
+
const info = ed.editorUpload?.blobCache?.getByUri?.(uri);
|
|
2195
|
+
return info ? `src="data:${info.blob().type};base64,${info.base64()}"` : m;
|
|
2196
|
+
});
|
|
2197
|
+
});
|
|
2198
|
+
ed.on("OpenWindow", () => {
|
|
2199
|
+
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
2200
|
+
const ta = document.querySelector(".tox-dialog textarea");
|
|
2201
|
+
if (ta && ta.value) {
|
|
2202
|
+
try {
|
|
2203
|
+
ta.setSelectionRange(0, 0);
|
|
2204
|
+
ta.scrollTop = 0;
|
|
2205
|
+
} catch {
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
}));
|
|
2209
|
+
});
|
|
2190
2210
|
const ZOOM_DEFAULT = opts.fontSizePx ?? 14;
|
|
2191
2211
|
const ZOOM_STEP = 2;
|
|
2192
2212
|
const ZOOM_MIN = 8;
|
|
@@ -5138,9 +5158,10 @@ document.getElementById("btn-discard")?.addEventListener("click", async () => {
|
|
|
5138
5158
|
}
|
|
5139
5159
|
closeCompose();
|
|
5140
5160
|
});
|
|
5141
|
-
var
|
|
5142
|
-
|
|
5143
|
-
|
|
5161
|
+
var popoutHandoffInFlight = false;
|
|
5162
|
+
async function popoutHandoff() {
|
|
5163
|
+
if (popoutInitId || popoutHandoffInFlight) return;
|
|
5164
|
+
popoutHandoffInFlight = true;
|
|
5144
5165
|
if (!draftId) draftId = `mailx-draft-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
5145
5166
|
const init = {
|
|
5146
5167
|
// "draft" mode so applyInit does NOT re-append the signature — the
|
|
@@ -5160,7 +5181,6 @@ composePopoutBtn?.addEventListener("click", async () => {
|
|
|
5160
5181
|
draftUid: draftUid ?? void 0,
|
|
5161
5182
|
draftId: draftId ?? void 0
|
|
5162
5183
|
};
|
|
5163
|
-
composePopoutBtn.disabled = true;
|
|
5164
5184
|
try {
|
|
5165
5185
|
const r = await popoutCompose(init);
|
|
5166
5186
|
if (r?.ok) {
|
|
@@ -5173,14 +5193,17 @@ composePopoutBtn?.addEventListener("click", async () => {
|
|
|
5173
5193
|
draftTimer = null;
|
|
5174
5194
|
}
|
|
5175
5195
|
closeCompose();
|
|
5176
|
-
|
|
5177
|
-
showDraftStatus(`Popout failed: ${r?.reason || "unknown"}`, true);
|
|
5178
|
-
composePopoutBtn.disabled = false;
|
|
5196
|
+
return;
|
|
5179
5197
|
}
|
|
5198
|
+
showDraftStatus(`Popout failed: ${r?.reason || "unknown"}`, true);
|
|
5180
5199
|
} catch (e) {
|
|
5181
5200
|
showDraftStatus(`Popout failed: ${e?.message || e}`, true);
|
|
5182
|
-
|
|
5201
|
+
} finally {
|
|
5202
|
+
popoutHandoffInFlight = false;
|
|
5183
5203
|
}
|
|
5204
|
+
}
|
|
5205
|
+
window.addEventListener("compose-popout", () => {
|
|
5206
|
+
void popoutHandoff();
|
|
5184
5207
|
});
|
|
5185
5208
|
var ccRow = document.getElementById("compose-cc-row");
|
|
5186
5209
|
var bccRow = document.getElementById("compose-bcc-row");
|