@bobfrankston/rmfmail 1.0.467
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/README.md +429 -0
- package/bin/build-icon-ico.js +283 -0
- package/bin/lean-accounts.js +172 -0
- package/bin/mailx.js +1580 -0
- package/bin/mailx.js.map +1 -0
- package/bin/mailx.ts +1549 -0
- package/bin/postinstall.js +39 -0
- package/bin/postinstall.js.map +1 -0
- package/bin/postinstall.ts +41 -0
- package/bin/tsconfig.json +10 -0
- package/client/.gitattributes +10 -0
- package/client/.msger-window.json +1 -0
- package/client/android.html +226 -0
- package/client/app.js +3400 -0
- package/client/app.js.map +1 -0
- package/client/app.ts +3190 -0
- package/client/components/address-book.js +199 -0
- package/client/components/address-book.js.map +1 -0
- package/client/components/address-book.ts +204 -0
- package/client/components/alarms.js +299 -0
- package/client/components/alarms.js.map +1 -0
- package/client/components/alarms.ts +276 -0
- package/client/components/calendar-sidebar.js +508 -0
- package/client/components/calendar-sidebar.js.map +1 -0
- package/client/components/calendar-sidebar.ts +474 -0
- package/client/components/calendar.js +217 -0
- package/client/components/calendar.js.map +1 -0
- package/client/components/calendar.ts +211 -0
- package/client/components/context-menu.js +80 -0
- package/client/components/context-menu.js.map +1 -0
- package/client/components/context-menu.ts +95 -0
- package/client/components/folder-picker.js +119 -0
- package/client/components/folder-picker.js.map +1 -0
- package/client/components/folder-picker.ts +127 -0
- package/client/components/folder-tree.js +1093 -0
- package/client/components/folder-tree.js.map +1 -0
- package/client/components/folder-tree.ts +1069 -0
- package/client/components/message-list.js +1140 -0
- package/client/components/message-list.js.map +1 -0
- package/client/components/message-list.ts +1129 -0
- package/client/components/message-viewer.js +1291 -0
- package/client/components/message-viewer.js.map +1 -0
- package/client/components/message-viewer.ts +1257 -0
- package/client/components/outbox-view.js +104 -0
- package/client/components/outbox-view.js.map +1 -0
- package/client/components/outbox-view.ts +102 -0
- package/client/components/tasks.js +261 -0
- package/client/components/tasks.js.map +1 -0
- package/client/components/tasks.ts +234 -0
- package/client/compose/compose.css +560 -0
- package/client/compose/compose.html +52 -0
- package/client/compose/compose.js +1289 -0
- package/client/compose/compose.js.map +1 -0
- package/client/compose/compose.ts +1231 -0
- package/client/compose/editor.js +616 -0
- package/client/compose/editor.js.map +1 -0
- package/client/compose/editor.ts +599 -0
- package/client/compose/ghost-text.js +122 -0
- package/client/compose/ghost-text.js.map +1 -0
- package/client/compose/ghost-text.ts +140 -0
- package/client/favicon.svg +11 -0
- package/client/icon.ico +0 -0
- package/client/icon.png +0 -0
- package/client/index.html +230 -0
- package/client/lib/android-bootstrap.js +9 -0
- package/client/lib/android-bootstrap.js.map +1 -0
- package/client/lib/android-bootstrap.ts +9 -0
- package/client/lib/api-client.js +378 -0
- package/client/lib/api-client.js.map +1 -0
- package/client/lib/api-client.ts +439 -0
- package/client/lib/local-service.js +603 -0
- package/client/lib/local-service.js.map +1 -0
- package/client/lib/local-service.ts +646 -0
- package/client/lib/local-store.js +214 -0
- package/client/lib/local-store.js.map +1 -0
- package/client/lib/local-store.ts +283 -0
- package/client/lib/mailxapi.js +304 -0
- package/client/lib/message-state.js +132 -0
- package/client/lib/message-state.js.map +1 -0
- package/client/lib/message-state.ts +160 -0
- package/client/package.json +16 -0
- package/client/styles/components.css +2049 -0
- package/client/styles/layout.css +332 -0
- package/client/styles/variables.css +85 -0
- package/client/tsconfig.json +19 -0
- package/package.json +124 -0
- package/packages/mailx-api/.gitattributes +10 -0
- package/packages/mailx-api/index.d.ts +10 -0
- package/packages/mailx-api/index.d.ts.map +1 -0
- package/packages/mailx-api/index.js +322 -0
- package/packages/mailx-api/index.js.map +1 -0
- package/packages/mailx-api/index.ts +283 -0
- package/packages/mailx-api/package.json +27 -0
- package/packages/mailx-api/tsconfig.json +9 -0
- package/packages/mailx-api/tsconfig.tsbuildinfo +1 -0
- package/packages/mailx-compose/.gitattributes +10 -0
- package/packages/mailx-compose/index.d.ts +21 -0
- package/packages/mailx-compose/index.d.ts.map +1 -0
- package/packages/mailx-compose/index.js +61 -0
- package/packages/mailx-compose/index.js.map +1 -0
- package/packages/mailx-compose/index.ts +85 -0
- package/packages/mailx-compose/package.json +18 -0
- package/packages/mailx-compose/tsconfig.json +9 -0
- package/packages/mailx-core/index.d.ts +139 -0
- package/packages/mailx-core/index.d.ts.map +1 -0
- package/packages/mailx-core/index.js +403 -0
- package/packages/mailx-core/index.js.map +1 -0
- package/packages/mailx-core/index.ts +424 -0
- package/packages/mailx-core/ipc.d.ts +13 -0
- package/packages/mailx-core/ipc.d.ts.map +1 -0
- package/packages/mailx-core/ipc.js +56 -0
- package/packages/mailx-core/ipc.js.map +1 -0
- package/packages/mailx-core/ipc.ts +62 -0
- package/packages/mailx-core/package.json +18 -0
- package/packages/mailx-core/tsconfig.json +9 -0
- package/packages/mailx-host/.gitattributes +10 -0
- package/packages/mailx-host/index.d.ts +21 -0
- package/packages/mailx-host/index.d.ts.map +1 -0
- package/packages/mailx-host/index.js +29 -0
- package/packages/mailx-host/index.js.map +1 -0
- package/packages/mailx-host/index.ts +38 -0
- package/packages/mailx-host/package.json +23 -0
- package/packages/mailx-host/tsconfig.json +9 -0
- package/packages/mailx-host/types-shim.d.ts +14 -0
- package/packages/mailx-imap/.gitattributes +10 -0
- package/packages/mailx-imap/index.d.ts +442 -0
- package/packages/mailx-imap/index.d.ts.map +1 -0
- package/packages/mailx-imap/index.js +3684 -0
- package/packages/mailx-imap/index.js.map +1 -0
- package/packages/mailx-imap/index.ts +3652 -0
- package/packages/mailx-imap/package-lock.json +131 -0
- package/packages/mailx-imap/package.json +28 -0
- package/packages/mailx-imap/providers/gmail-api.d.ts +8 -0
- package/packages/mailx-imap/providers/gmail-api.d.ts.map +1 -0
- package/packages/mailx-imap/providers/gmail-api.js +8 -0
- package/packages/mailx-imap/providers/gmail-api.js.map +1 -0
- package/packages/mailx-imap/providers/gmail-api.ts +8 -0
- package/packages/mailx-imap/providers/outlook-api.ts +7 -0
- package/packages/mailx-imap/providers/types.d.ts +9 -0
- package/packages/mailx-imap/providers/types.d.ts.map +1 -0
- package/packages/mailx-imap/providers/types.js +9 -0
- package/packages/mailx-imap/providers/types.js.map +1 -0
- package/packages/mailx-imap/providers/types.ts +9 -0
- package/packages/mailx-imap/tsconfig.json +9 -0
- package/packages/mailx-imap/tsconfig.tsbuildinfo +1 -0
- package/packages/mailx-send/.gitattributes +10 -0
- package/packages/mailx-send/README.md +217 -0
- package/packages/mailx-send/cli-queue.d.ts +11 -0
- package/packages/mailx-send/cli-queue.d.ts.map +1 -0
- package/packages/mailx-send/cli-queue.js +59 -0
- package/packages/mailx-send/cli-queue.js.map +1 -0
- package/packages/mailx-send/cli-queue.ts +62 -0
- package/packages/mailx-send/cli-send.d.ts +17 -0
- package/packages/mailx-send/cli-send.d.ts.map +1 -0
- package/packages/mailx-send/cli-send.js +75 -0
- package/packages/mailx-send/cli-send.js.map +1 -0
- package/packages/mailx-send/cli-send.ts +83 -0
- package/packages/mailx-send/cli.d.ts +22 -0
- package/packages/mailx-send/cli.d.ts.map +1 -0
- package/packages/mailx-send/cli.js +115 -0
- package/packages/mailx-send/cli.js.map +1 -0
- package/packages/mailx-send/cli.ts +126 -0
- package/packages/mailx-send/index.d.ts +77 -0
- package/packages/mailx-send/index.d.ts.map +1 -0
- package/packages/mailx-send/index.js +264 -0
- package/packages/mailx-send/index.js.map +1 -0
- package/packages/mailx-send/index.ts +333 -0
- package/packages/mailx-send/mailsend/README.md +133 -0
- package/packages/mailx-send/mailsend/cli.d.ts +12 -0
- package/packages/mailx-send/mailsend/cli.d.ts.map +1 -0
- package/packages/mailx-send/mailsend/cli.js +70 -0
- package/packages/mailx-send/mailsend/cli.js.map +1 -0
- package/packages/mailx-send/mailsend/cli.ts +81 -0
- package/packages/mailx-send/mailsend/index.d.ts +77 -0
- package/packages/mailx-send/mailsend/index.d.ts.map +1 -0
- package/packages/mailx-send/mailsend/index.js +264 -0
- package/packages/mailx-send/mailsend/index.js.map +1 -0
- package/packages/mailx-send/mailsend/index.ts +333 -0
- package/packages/mailx-send/mailsend/package-lock.json +65 -0
- package/packages/mailx-send/mailsend/package.json +26 -0
- package/packages/mailx-send/mailsend/tsconfig.json +21 -0
- package/packages/mailx-send/package-lock.json +65 -0
- package/packages/mailx-send/package.json +37 -0
- package/packages/mailx-send/tsconfig.json +21 -0
- package/packages/mailx-server/.gitattributes +10 -0
- package/packages/mailx-server/index.d.ts +8 -0
- package/packages/mailx-server/index.d.ts.map +1 -0
- package/packages/mailx-server/index.js +397 -0
- package/packages/mailx-server/index.js.map +1 -0
- package/packages/mailx-server/index.ts +429 -0
- package/packages/mailx-server/package.json +28 -0
- package/packages/mailx-server/tsconfig.json +9 -0
- package/packages/mailx-server/tsconfig.tsbuildinfo +1 -0
- package/packages/mailx-service/google-sync.d.ts +96 -0
- package/packages/mailx-service/google-sync.d.ts.map +1 -0
- package/packages/mailx-service/google-sync.js +153 -0
- package/packages/mailx-service/google-sync.js.map +1 -0
- package/packages/mailx-service/google-sync.ts +238 -0
- package/packages/mailx-service/index.d.ts +366 -0
- package/packages/mailx-service/index.d.ts.map +1 -0
- package/packages/mailx-service/index.js +2589 -0
- package/packages/mailx-service/index.js.map +1 -0
- package/packages/mailx-service/index.ts +2512 -0
- package/packages/mailx-service/jsonrpc.d.ts +25 -0
- package/packages/mailx-service/jsonrpc.d.ts.map +1 -0
- package/packages/mailx-service/jsonrpc.js +238 -0
- package/packages/mailx-service/jsonrpc.js.map +1 -0
- package/packages/mailx-service/jsonrpc.ts +268 -0
- package/packages/mailx-service/package.json +22 -0
- package/packages/mailx-service/tsconfig.json +9 -0
- package/packages/mailx-settings/.gitattributes +10 -0
- package/packages/mailx-settings/cloud.d.ts +47 -0
- package/packages/mailx-settings/cloud.d.ts.map +1 -0
- package/packages/mailx-settings/cloud.js +408 -0
- package/packages/mailx-settings/cloud.js.map +1 -0
- package/packages/mailx-settings/cloud.ts +393 -0
- package/packages/mailx-settings/index.d.ts +140 -0
- package/packages/mailx-settings/index.d.ts.map +1 -0
- package/packages/mailx-settings/index.js +872 -0
- package/packages/mailx-settings/index.js.map +1 -0
- package/packages/mailx-settings/index.ts +908 -0
- package/packages/mailx-settings/package.json +20 -0
- package/packages/mailx-settings/tsconfig.json +9 -0
- package/packages/mailx-settings/tsconfig.tsbuildinfo +1 -0
- package/packages/mailx-store/.gitattributes +10 -0
- package/packages/mailx-store/db.d.ts +346 -0
- package/packages/mailx-store/db.d.ts.map +1 -0
- package/packages/mailx-store/db.js +1796 -0
- package/packages/mailx-store/db.js.map +1 -0
- package/packages/mailx-store/db.ts +2007 -0
- package/packages/mailx-store/file-store.d.ts +36 -0
- package/packages/mailx-store/file-store.d.ts.map +1 -0
- package/packages/mailx-store/file-store.js +80 -0
- package/packages/mailx-store/file-store.js.map +1 -0
- package/packages/mailx-store/file-store.ts +82 -0
- package/packages/mailx-store/index.d.ts +7 -0
- package/packages/mailx-store/index.d.ts.map +1 -0
- package/packages/mailx-store/index.js +7 -0
- package/packages/mailx-store/index.js.map +1 -0
- package/packages/mailx-store/index.ts +7 -0
- package/packages/mailx-store/package.json +19 -0
- package/packages/mailx-store/tsconfig.json +9 -0
- package/packages/mailx-store/tsconfig.tsbuildinfo +1 -0
- package/packages/mailx-store-web/android-bootstrap.d.ts +16 -0
- package/packages/mailx-store-web/android-bootstrap.d.ts.map +1 -0
- package/packages/mailx-store-web/android-bootstrap.js +1247 -0
- package/packages/mailx-store-web/android-bootstrap.js.map +1 -0
- package/packages/mailx-store-web/android-bootstrap.ts +1262 -0
- package/packages/mailx-store-web/db.d.ts +132 -0
- package/packages/mailx-store-web/db.d.ts.map +1 -0
- package/packages/mailx-store-web/db.js +642 -0
- package/packages/mailx-store-web/db.js.map +1 -0
- package/packages/mailx-store-web/db.ts +756 -0
- package/packages/mailx-store-web/gmail-api-web.d.ts +11 -0
- package/packages/mailx-store-web/gmail-api-web.d.ts.map +1 -0
- package/packages/mailx-store-web/gmail-api-web.js +11 -0
- package/packages/mailx-store-web/gmail-api-web.js.map +1 -0
- package/packages/mailx-store-web/gmail-api-web.ts +11 -0
- package/packages/mailx-store-web/imap-web-provider.d.ts +33 -0
- package/packages/mailx-store-web/imap-web-provider.d.ts.map +1 -0
- package/packages/mailx-store-web/imap-web-provider.js +140 -0
- package/packages/mailx-store-web/imap-web-provider.js.map +1 -0
- package/packages/mailx-store-web/imap-web-provider.ts +156 -0
- package/packages/mailx-store-web/index.d.ts +10 -0
- package/packages/mailx-store-web/index.d.ts.map +1 -0
- package/packages/mailx-store-web/index.js +10 -0
- package/packages/mailx-store-web/index.js.map +1 -0
- package/packages/mailx-store-web/index.ts +10 -0
- package/packages/mailx-store-web/main-thread-host.d.ts +15 -0
- package/packages/mailx-store-web/main-thread-host.d.ts.map +1 -0
- package/packages/mailx-store-web/main-thread-host.js +292 -0
- package/packages/mailx-store-web/main-thread-host.js.map +1 -0
- package/packages/mailx-store-web/main-thread-host.ts +322 -0
- package/packages/mailx-store-web/package.json +23 -0
- package/packages/mailx-store-web/provider-types.d.ts +7 -0
- package/packages/mailx-store-web/provider-types.d.ts.map +1 -0
- package/packages/mailx-store-web/provider-types.js +7 -0
- package/packages/mailx-store-web/provider-types.js.map +1 -0
- package/packages/mailx-store-web/provider-types.ts +7 -0
- package/packages/mailx-store-web/sql-wasm-esm.js +10 -0
- package/packages/mailx-store-web/sql.js.d.ts +29 -0
- package/packages/mailx-store-web/sync-manager.d.ts +68 -0
- package/packages/mailx-store-web/sync-manager.d.ts.map +1 -0
- package/packages/mailx-store-web/sync-manager.js +506 -0
- package/packages/mailx-store-web/sync-manager.js.map +1 -0
- package/packages/mailx-store-web/sync-manager.ts +508 -0
- package/packages/mailx-store-web/tsconfig.json +10 -0
- package/packages/mailx-store-web/web-jsonrpc.d.ts +20 -0
- package/packages/mailx-store-web/web-jsonrpc.d.ts.map +1 -0
- package/packages/mailx-store-web/web-jsonrpc.js +102 -0
- package/packages/mailx-store-web/web-jsonrpc.js.map +1 -0
- package/packages/mailx-store-web/web-jsonrpc.ts +116 -0
- package/packages/mailx-store-web/web-message-store.d.ts +16 -0
- package/packages/mailx-store-web/web-message-store.d.ts.map +1 -0
- package/packages/mailx-store-web/web-message-store.js +89 -0
- package/packages/mailx-store-web/web-message-store.js.map +1 -0
- package/packages/mailx-store-web/web-message-store.ts +97 -0
- package/packages/mailx-store-web/web-service.d.ts +111 -0
- package/packages/mailx-store-web/web-service.d.ts.map +1 -0
- package/packages/mailx-store-web/web-service.js +542 -0
- package/packages/mailx-store-web/web-service.js.map +1 -0
- package/packages/mailx-store-web/web-service.ts +616 -0
- package/packages/mailx-store-web/web-settings.d.ts +83 -0
- package/packages/mailx-store-web/web-settings.d.ts.map +1 -0
- package/packages/mailx-store-web/web-settings.js +491 -0
- package/packages/mailx-store-web/web-settings.js.map +1 -0
- package/packages/mailx-store-web/web-settings.ts +522 -0
- package/packages/mailx-store-web/worker-bundle.js +6838 -0
- package/packages/mailx-store-web/worker-entry.d.ts +8 -0
- package/packages/mailx-store-web/worker-entry.d.ts.map +1 -0
- package/packages/mailx-store-web/worker-entry.js +187 -0
- package/packages/mailx-store-web/worker-entry.js.map +1 -0
- package/packages/mailx-store-web/worker-entry.ts +215 -0
- package/packages/mailx-store-web/worker-tcp-transport.d.ts +28 -0
- package/packages/mailx-store-web/worker-tcp-transport.d.ts.map +1 -0
- package/packages/mailx-store-web/worker-tcp-transport.js +98 -0
- package/packages/mailx-store-web/worker-tcp-transport.js.map +1 -0
- package/packages/mailx-store-web/worker-tcp-transport.ts +101 -0
- package/packages/mailx-types/.gitattributes +10 -0
- package/packages/mailx-types/index.d.ts +319 -0
- package/packages/mailx-types/index.d.ts.map +1 -0
- package/packages/mailx-types/index.js +243 -0
- package/packages/mailx-types/index.js.map +1 -0
- package/packages/mailx-types/index.ts +498 -0
- package/packages/mailx-types/package.json +16 -0
- package/packages/mailx-types/tsconfig.json +9 -0
- package/packages/mailx-types/tsconfig.tsbuildinfo +1 -0
- package/todo.json +13 -0
- package/tsconfig.base.json +31 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ghost text autocomplete — shows AI suggestions as translucent overlay at cursor.
|
|
3
|
+
* Tab accepts, Escape dismisses, any other key dismisses and re-triggers after debounce.
|
|
4
|
+
* Editor-agnostic: works with both Quill and tiptap via MailxEditor interface.
|
|
5
|
+
*/
|
|
6
|
+
import { autocomplete } from "../lib/api-client.js";
|
|
7
|
+
let ghostEl = null;
|
|
8
|
+
let currentSuggestion = null;
|
|
9
|
+
let debounceTimer = null;
|
|
10
|
+
let abortController = null;
|
|
11
|
+
let activeEditor = null;
|
|
12
|
+
let debounceMs = 600;
|
|
13
|
+
function dismiss() {
|
|
14
|
+
currentSuggestion = null;
|
|
15
|
+
if (ghostEl) {
|
|
16
|
+
ghostEl.remove();
|
|
17
|
+
ghostEl = null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function positionGhost(editor, text) {
|
|
21
|
+
dismiss();
|
|
22
|
+
const sel = window.getSelection();
|
|
23
|
+
if (!sel || sel.rangeCount === 0 || !sel.isCollapsed)
|
|
24
|
+
return;
|
|
25
|
+
const range = sel.getRangeAt(0);
|
|
26
|
+
let rect = range.getBoundingClientRect();
|
|
27
|
+
// Collapsed range may return zero-width rect — use a temp span to measure
|
|
28
|
+
if (rect.width === 0 && rect.height === 0) {
|
|
29
|
+
const span = document.createElement("span");
|
|
30
|
+
span.textContent = "\u200B"; // zero-width space
|
|
31
|
+
range.insertNode(span);
|
|
32
|
+
rect = span.getBoundingClientRect();
|
|
33
|
+
span.remove();
|
|
34
|
+
// Restore selection
|
|
35
|
+
sel.removeAllRanges();
|
|
36
|
+
sel.addRange(range);
|
|
37
|
+
}
|
|
38
|
+
const container = editor.getScrollContainer();
|
|
39
|
+
const containerRect = container.getBoundingClientRect();
|
|
40
|
+
ghostEl = document.createElement("span");
|
|
41
|
+
ghostEl.className = "ghost-text";
|
|
42
|
+
ghostEl.textContent = text;
|
|
43
|
+
ghostEl.style.top = `${rect.top - containerRect.top + container.scrollTop}px`;
|
|
44
|
+
ghostEl.style.left = `${rect.left - containerRect.left + container.scrollLeft}px`;
|
|
45
|
+
container.appendChild(ghostEl);
|
|
46
|
+
currentSuggestion = text;
|
|
47
|
+
}
|
|
48
|
+
function requestSuggestion(editor, context) {
|
|
49
|
+
// Cancel any in-flight request
|
|
50
|
+
if (abortController) {
|
|
51
|
+
abortController.abort();
|
|
52
|
+
abortController = null;
|
|
53
|
+
}
|
|
54
|
+
const bodyText = editor.getText();
|
|
55
|
+
if (!bodyText || bodyText.trim().length < 3)
|
|
56
|
+
return; // need at least a few characters
|
|
57
|
+
abortController = new AbortController();
|
|
58
|
+
const signal = abortController.signal;
|
|
59
|
+
autocomplete({
|
|
60
|
+
subject: context.getSubject(),
|
|
61
|
+
to: context.getTo(),
|
|
62
|
+
bodyText,
|
|
63
|
+
cursorOffset: bodyText.length,
|
|
64
|
+
}, signal).then((result) => {
|
|
65
|
+
if (signal.aborted)
|
|
66
|
+
return;
|
|
67
|
+
if (result.suggestion) {
|
|
68
|
+
positionGhost(editor, result.suggestion);
|
|
69
|
+
}
|
|
70
|
+
}).catch((e) => {
|
|
71
|
+
if (e.name === "AbortError")
|
|
72
|
+
return;
|
|
73
|
+
// Silently ignore autocomplete errors
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
export function initGhostText(editor, context, options) {
|
|
77
|
+
activeEditor = editor;
|
|
78
|
+
if (options?.debounceMs)
|
|
79
|
+
debounceMs = options.debounceMs;
|
|
80
|
+
// Debounced content change → request suggestion
|
|
81
|
+
editor.onContentChange(() => {
|
|
82
|
+
dismiss();
|
|
83
|
+
if (debounceTimer)
|
|
84
|
+
clearTimeout(debounceTimer);
|
|
85
|
+
debounceTimer = setTimeout(() => {
|
|
86
|
+
requestSuggestion(editor, context);
|
|
87
|
+
}, debounceMs);
|
|
88
|
+
});
|
|
89
|
+
// Key handler: Tab accepts, Escape dismisses
|
|
90
|
+
editor.onKeyDown((e) => {
|
|
91
|
+
if (!currentSuggestion)
|
|
92
|
+
return;
|
|
93
|
+
if (e.key === "Tab") {
|
|
94
|
+
e.preventDefault();
|
|
95
|
+
e.stopPropagation();
|
|
96
|
+
const text = currentSuggestion;
|
|
97
|
+
dismiss();
|
|
98
|
+
editor.insertTextAtCursor(text);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (e.key === "Escape") {
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
e.stopPropagation();
|
|
104
|
+
dismiss();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// Any other key: dismiss (new suggestion will come after debounce)
|
|
108
|
+
dismiss();
|
|
109
|
+
});
|
|
110
|
+
// Dismiss on blur/scroll
|
|
111
|
+
editor.root.addEventListener("blur", dismiss);
|
|
112
|
+
editor.getScrollContainer().addEventListener("scroll", dismiss);
|
|
113
|
+
}
|
|
114
|
+
export function destroyGhostText() {
|
|
115
|
+
dismiss();
|
|
116
|
+
if (debounceTimer)
|
|
117
|
+
clearTimeout(debounceTimer);
|
|
118
|
+
if (abortController)
|
|
119
|
+
abortController.abort();
|
|
120
|
+
activeEditor = null;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=ghost-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ghost-text.js","sourceRoot":"","sources":["ghost-text.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ghost text autocomplete — shows AI suggestions as translucent overlay at cursor.
|
|
3
|
+
* Tab accepts, Escape dismisses, any other key dismisses and re-triggers after debounce.
|
|
4
|
+
* Editor-agnostic: works with both Quill and tiptap via MailxEditor interface.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { MailxEditor } from "./editor.js";
|
|
8
|
+
import { autocomplete } from "../lib/api-client.js";
|
|
9
|
+
|
|
10
|
+
interface GhostTextContext {
|
|
11
|
+
getSubject: () => string;
|
|
12
|
+
getTo: () => string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let ghostEl: HTMLElement | null = null;
|
|
16
|
+
let currentSuggestion: string | null = null;
|
|
17
|
+
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
18
|
+
let abortController: AbortController | null = null;
|
|
19
|
+
let activeEditor: MailxEditor | null = null;
|
|
20
|
+
let debounceMs = 600;
|
|
21
|
+
|
|
22
|
+
function dismiss(): void {
|
|
23
|
+
currentSuggestion = null;
|
|
24
|
+
if (ghostEl) {
|
|
25
|
+
ghostEl.remove();
|
|
26
|
+
ghostEl = null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function positionGhost(editor: MailxEditor, text: string): void {
|
|
31
|
+
dismiss();
|
|
32
|
+
|
|
33
|
+
const sel = window.getSelection();
|
|
34
|
+
if (!sel || sel.rangeCount === 0 || !sel.isCollapsed) return;
|
|
35
|
+
|
|
36
|
+
const range = sel.getRangeAt(0);
|
|
37
|
+
let rect = range.getBoundingClientRect();
|
|
38
|
+
|
|
39
|
+
// Collapsed range may return zero-width rect — use a temp span to measure
|
|
40
|
+
if (rect.width === 0 && rect.height === 0) {
|
|
41
|
+
const span = document.createElement("span");
|
|
42
|
+
span.textContent = "\u200B"; // zero-width space
|
|
43
|
+
range.insertNode(span);
|
|
44
|
+
rect = span.getBoundingClientRect();
|
|
45
|
+
span.remove();
|
|
46
|
+
// Restore selection
|
|
47
|
+
sel.removeAllRanges();
|
|
48
|
+
sel.addRange(range);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const container = editor.getScrollContainer();
|
|
52
|
+
const containerRect = container.getBoundingClientRect();
|
|
53
|
+
|
|
54
|
+
ghostEl = document.createElement("span");
|
|
55
|
+
ghostEl.className = "ghost-text";
|
|
56
|
+
ghostEl.textContent = text;
|
|
57
|
+
ghostEl.style.top = `${rect.top - containerRect.top + container.scrollTop}px`;
|
|
58
|
+
ghostEl.style.left = `${rect.left - containerRect.left + container.scrollLeft}px`;
|
|
59
|
+
container.appendChild(ghostEl);
|
|
60
|
+
|
|
61
|
+
currentSuggestion = text;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function requestSuggestion(editor: MailxEditor, context: GhostTextContext): void {
|
|
65
|
+
// Cancel any in-flight request
|
|
66
|
+
if (abortController) {
|
|
67
|
+
abortController.abort();
|
|
68
|
+
abortController = null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const bodyText = editor.getText();
|
|
72
|
+
if (!bodyText || bodyText.trim().length < 3) return; // need at least a few characters
|
|
73
|
+
|
|
74
|
+
abortController = new AbortController();
|
|
75
|
+
const signal = abortController.signal;
|
|
76
|
+
|
|
77
|
+
autocomplete({
|
|
78
|
+
subject: context.getSubject(),
|
|
79
|
+
to: context.getTo(),
|
|
80
|
+
bodyText,
|
|
81
|
+
cursorOffset: bodyText.length,
|
|
82
|
+
}, signal).then((result: any) => {
|
|
83
|
+
if (signal.aborted) return;
|
|
84
|
+
if (result.suggestion) {
|
|
85
|
+
positionGhost(editor, result.suggestion);
|
|
86
|
+
}
|
|
87
|
+
}).catch((e: any) => {
|
|
88
|
+
if (e.name === "AbortError") return;
|
|
89
|
+
// Silently ignore autocomplete errors
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function initGhostText(editor: MailxEditor, context: GhostTextContext, options?: { debounceMs?: number }): void {
|
|
94
|
+
activeEditor = editor;
|
|
95
|
+
if (options?.debounceMs) debounceMs = options.debounceMs;
|
|
96
|
+
|
|
97
|
+
// Debounced content change → request suggestion
|
|
98
|
+
editor.onContentChange(() => {
|
|
99
|
+
dismiss();
|
|
100
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
101
|
+
debounceTimer = setTimeout(() => {
|
|
102
|
+
requestSuggestion(editor, context);
|
|
103
|
+
}, debounceMs);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Key handler: Tab accepts, Escape dismisses
|
|
107
|
+
editor.onKeyDown((e: KeyboardEvent) => {
|
|
108
|
+
if (!currentSuggestion) return;
|
|
109
|
+
|
|
110
|
+
if (e.key === "Tab") {
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
e.stopPropagation();
|
|
113
|
+
const text = currentSuggestion;
|
|
114
|
+
dismiss();
|
|
115
|
+
editor.insertTextAtCursor(text);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (e.key === "Escape") {
|
|
120
|
+
e.preventDefault();
|
|
121
|
+
e.stopPropagation();
|
|
122
|
+
dismiss();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Any other key: dismiss (new suggestion will come after debounce)
|
|
127
|
+
dismiss();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Dismiss on blur/scroll
|
|
131
|
+
editor.root.addEventListener("blur", dismiss);
|
|
132
|
+
editor.getScrollContainer().addEventListener("scroll", dismiss);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function destroyGhostText(): void {
|
|
136
|
+
dismiss();
|
|
137
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
138
|
+
if (abortController) abortController.abort();
|
|
139
|
+
activeEditor = null;
|
|
140
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
2
|
+
<!-- Envelope body -->
|
|
3
|
+
<rect x="4" y="16" width="56" height="34" rx="3" fill="#2563eb" stroke="#1e40af" stroke-width="1.5"/>
|
|
4
|
+
<!-- Envelope flap -->
|
|
5
|
+
<path d="M4 16 L32 36 L60 16" fill="#3b82f6" stroke="#1e40af" stroke-width="1.5" stroke-linejoin="round"/>
|
|
6
|
+
<!-- Fold lines -->
|
|
7
|
+
<path d="M4 50 L22 34" fill="none" stroke="#1e40af" stroke-width="1"/>
|
|
8
|
+
<path d="M60 50 L42 34" fill="none" stroke="#1e40af" stroke-width="1"/>
|
|
9
|
+
<!-- "mx" text -->
|
|
10
|
+
<text x="32" y="48" text-anchor="middle" font-family="system-ui,sans-serif" font-weight="800" font-size="16" fill="#e0e7ff" letter-spacing="-0.5">mx</text>
|
|
11
|
+
</svg>
|
package/client/icon.ico
ADDED
|
Binary file
|
package/client/icon.png
ADDED
|
Binary file
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>rmfmail</title>
|
|
7
|
+
<link rel="icon" type="image/svg+xml" href="favicon.svg">
|
|
8
|
+
<link rel="stylesheet" href="styles/variables.css">
|
|
9
|
+
<link rel="stylesheet" href="styles/components.css">
|
|
10
|
+
<link rel="stylesheet" href="styles/layout.css">
|
|
11
|
+
<script type="module" src="app.js"></script>
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<header class="toolbar">
|
|
15
|
+
<div class="toolbar-left">
|
|
16
|
+
<button class="tb-btn" id="btn-menu" title="Menu / rail" hidden>☰</button>
|
|
17
|
+
<button class="tb-btn" id="btn-folder-toggle" title="Show / hide folders" hidden>📁</button>
|
|
18
|
+
<button class="tb-btn" id="btn-compose" title="Compose (Ctrl+N)">
|
|
19
|
+
<span class="tb-icon">✏</span><span class="tb-label"> Compose</span>
|
|
20
|
+
</button>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="toolbar-center">
|
|
23
|
+
<div class="tb-menu" id="view-menu">
|
|
24
|
+
<button class="tb-btn" id="btn-view">View</button>
|
|
25
|
+
<div class="tb-menu-dropdown" id="view-dropdown" hidden>
|
|
26
|
+
<label class="tb-menu-item" title="Stack From + Subject on two lines per row, date to the right — denser on narrow windows"><input type="checkbox" id="opt-two-line"> Two-line view</label>
|
|
27
|
+
<label class="tb-menu-item" title="Show the reading pane to the right of the message list"><input type="checkbox" id="opt-preview" checked> Preview pane</label>
|
|
28
|
+
<label class="tb-menu-item" title="Show a short body-text preview (first ~80 chars) beneath each row's subject"><input type="checkbox" id="opt-snippet" checked> Preview snippets</label>
|
|
29
|
+
<label class="tb-menu-item" title="Collapse reply chains to one row showing the newest message; a pill shows thread size"><input type="checkbox" id="opt-threaded"> Group by thread</label>
|
|
30
|
+
<label class="tb-menu-item" title="Filter the list to rows sharing the selected message's thread (set selection first)"><input type="checkbox" id="opt-thread-filter"> Only this conversation</label>
|
|
31
|
+
<label class="tb-menu-item" title="Show only flagged (★) messages in the list"><input type="checkbox" id="opt-flagged"> ★ Flagged only</label>
|
|
32
|
+
<label class="tb-menu-item" title="Show unread/total counts next to each folder in the tree"><input type="checkbox" id="opt-folder-counts"> Folder counts</label>
|
|
33
|
+
<label class="tb-menu-item" title="Show the right-side calendar/tasks sidebar (Thunderbird-Lightning style)"><input type="checkbox" id="opt-calendar-sidebar"> Calendar sidebar</label>
|
|
34
|
+
<hr class="tb-menu-sep">
|
|
35
|
+
<button class="tb-menu-item" id="btn-reset-widths" title="Restore the default list / viewer split position">Reset column widths</button>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="tb-menu" id="settings-menu">
|
|
39
|
+
<button class="tb-btn" id="btn-settings">Settings</button>
|
|
40
|
+
<div class="tb-menu-dropdown" id="settings-dropdown" hidden>
|
|
41
|
+
<span class="tb-menu-label">Theme</span>
|
|
42
|
+
<label class="tb-menu-item"><input type="radio" name="opt-theme" value="system" id="opt-theme-system"> System</label>
|
|
43
|
+
<label class="tb-menu-item"><input type="radio" name="opt-theme" value="light" id="opt-theme-light"> Light</label>
|
|
44
|
+
<label class="tb-menu-item"><input type="radio" name="opt-theme" value="dark" id="opt-theme-dark"> Dark</label>
|
|
45
|
+
<hr class="tb-menu-sep">
|
|
46
|
+
<span class="tb-menu-label">Editor</span>
|
|
47
|
+
<label class="tb-menu-item"><input type="radio" name="opt-editor" value="quill" id="opt-editor-quill" checked> Quill</label>
|
|
48
|
+
<label class="tb-menu-item"><input type="radio" name="opt-editor" value="tiptap" id="opt-editor-tiptap"> tiptap</label>
|
|
49
|
+
<hr class="tb-menu-sep">
|
|
50
|
+
<span class="tb-menu-label" title="Which app the compose 'Edit in Word' button hands the body off to. Auto = Word first, then LibreOffice, then OS default.">External editor</span>
|
|
51
|
+
<label class="tb-menu-item"><input type="radio" name="opt-extedit" value="auto" id="opt-extedit-auto" checked> Auto (Word → LibreOffice → default)</label>
|
|
52
|
+
<label class="tb-menu-item"><input type="radio" name="opt-extedit" value="word" id="opt-extedit-word"> Microsoft Word</label>
|
|
53
|
+
<label class="tb-menu-item"><input type="radio" name="opt-extedit" value="libreoffice" id="opt-extedit-libre"> LibreOffice Writer</label>
|
|
54
|
+
<hr class="tb-menu-sep">
|
|
55
|
+
<label class="tb-menu-item" title="Ghost-text completions while composing — Ollama / Claude / OpenAI back-end, off by default"><input type="checkbox" id="opt-autocomplete"> AI autocomplete</label>
|
|
56
|
+
<label class="tb-menu-item" title="Right-click in message body → Translate"><input type="checkbox" id="opt-ai-translate"> AI translate (off by default)</label>
|
|
57
|
+
<label class="tb-menu-item" title="Right-click in compose editor → Proofread (when wired)"><input type="checkbox" id="opt-ai-proofread"> AI proofread (off by default)</label>
|
|
58
|
+
<label class="tb-menu-item" title="When opening a message with remote content, look up the sender's domain in three free DNS blocklists in parallel: Spamhaus DBL, SURBL, URIBL. The banner shows N-of-3 services flagging the domain. Each query leaks the bare domain to that DNSBL's DNS. Off by default."><input type="checkbox" id="opt-check-reputation"> Check sender reputation (DNSBLs)</label>
|
|
59
|
+
<hr class="tb-menu-sep">
|
|
60
|
+
<label class="tb-menu-item" title="When viewing a message, automatically mark it as read after a short delay. Uncheck to require an explicit Mark-Read action."><input type="checkbox" id="opt-automark-read" checked> Auto mark-as-read</label>
|
|
61
|
+
<label class="tb-menu-item" title="Seconds to wait before auto-marking a viewed message as read. Higher = scroll past messages without changing their unread state. 0 = mark immediately.">Mark-read delay (sec) <input type="number" id="opt-automark-delay" min="0" max="30" step="0.5" style="width:4em"></label>
|
|
62
|
+
<hr class="tb-menu-sep">
|
|
63
|
+
<button class="tb-menu-item" id="btn-edit-jsonc" title="Edit accounts.jsonc / allowlist.jsonc">Edit config files...</button>
|
|
64
|
+
<button class="tb-menu-item" id="btn-open-mailx-dir" title="Open ~/.rmfmail in file explorer">Open rmfmail folder...</button>
|
|
65
|
+
<button class="tb-menu-item" id="btn-open-log" title="Open today's log file">Open log...</button>
|
|
66
|
+
<button class="tb-menu-item" id="btn-about" title="Show version and build info">About rmfmail...</button>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
<button class="tb-btn" id="btn-tb-delete" title="Delete selected message (Del)">🗑</button>
|
|
70
|
+
<button class="tb-btn" id="btn-tb-spam" title="Mark as spam — move to the configured Junk folder">⚠</button>
|
|
71
|
+
<span id="app-version" class="app-version">rmfmail</span>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="toolbar-right">
|
|
74
|
+
<button class="tb-btn" id="btn-sync" title="Sync all folders (F5)">
|
|
75
|
+
<span class="tb-icon">↻</span><span class="tb-label"> Sync</span>
|
|
76
|
+
</button>
|
|
77
|
+
<div class="tb-menu" id="restart-menu">
|
|
78
|
+
<button class="tb-btn" id="btn-restart" title="Reload">
|
|
79
|
+
<span class="tb-icon">⚡</span><span class="tb-label"> Restart ▾</span>
|
|
80
|
+
</button>
|
|
81
|
+
<div class="tb-menu-dropdown" id="restart-dropdown" hidden>
|
|
82
|
+
<button class="tb-menu-item" id="btn-restart-quick" title="Restart the rmfmail daemon and reload the UI — picks up new code after a build">Restart</button>
|
|
83
|
+
<button class="tb-menu-item" id="btn-update" title="Check for updates and install if available">Check for updates</button>
|
|
84
|
+
<button class="tb-menu-item" id="btn-rebuild" title="Wipe local DB and message cache, re-download everything. Accounts and settings are preserved. Safe and fast.">Rebuild local cache</button>
|
|
85
|
+
<hr class="tb-menu-sep">
|
|
86
|
+
<span class="tb-menu-hint">CLI: rmfmail -rebuild for full reset</span>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</header>
|
|
91
|
+
|
|
92
|
+
<div class="alert-banner" id="alert-banner" hidden>
|
|
93
|
+
<span id="alert-text"></span>
|
|
94
|
+
<button class="alert-dismiss" id="alert-dismiss" title="Dismiss">×</button>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<aside class="icon-rail" id="icon-rail" aria-label="App rail">
|
|
98
|
+
<div class="rail-top">
|
|
99
|
+
<button class="rail-btn" id="rail-compose" title="Compose (Ctrl+N)" aria-label="Compose">✏</button>
|
|
100
|
+
<button class="rail-btn" id="rail-inbox" title="Inbox" aria-label="Inbox" data-active="true">✉</button>
|
|
101
|
+
<button class="rail-btn" id="rail-unified" title="All Inboxes" aria-label="All Inboxes">⌘</button>
|
|
102
|
+
<button class="rail-btn" id="rail-contacts" title="Contacts / Address book" aria-label="Contacts">👤</button>
|
|
103
|
+
<button class="rail-btn" id="rail-calendar" title="Calendar" aria-label="Calendar">📅</button>
|
|
104
|
+
<button class="rail-btn" id="rail-tasks" title="Tasks" aria-label="Tasks">☑</button>
|
|
105
|
+
</div>
|
|
106
|
+
<div class="rail-bottom">
|
|
107
|
+
<button class="rail-btn" id="rail-view" title="View options" aria-label="View options">👁</button>
|
|
108
|
+
<button class="rail-btn" id="rail-settings" title="Settings" aria-label="Settings">⚙</button>
|
|
109
|
+
<button class="rail-btn" id="rail-theme" title="Toggle theme" aria-label="Toggle theme">◐</button>
|
|
110
|
+
<button class="rail-btn" id="rail-help" title="Help / About" aria-label="Help">?</button>
|
|
111
|
+
</div>
|
|
112
|
+
</aside>
|
|
113
|
+
|
|
114
|
+
<div class="folder-panel">
|
|
115
|
+
<div class="ft-filter">
|
|
116
|
+
<input type="text" id="ft-filter-input" placeholder="Find folder..." autocomplete="off">
|
|
117
|
+
</div>
|
|
118
|
+
<nav class="folder-tree" id="folder-tree">
|
|
119
|
+
<div class="folder-loading">Loading accounts...</div>
|
|
120
|
+
</nav>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<main class="main-area">
|
|
124
|
+
<section class="message-list" id="message-list">
|
|
125
|
+
<search class="search-bar ml-search">
|
|
126
|
+
<select id="search-scope" title="Local search scope">
|
|
127
|
+
<option value="all">All folders</option>
|
|
128
|
+
<option value="current">This folder</option>
|
|
129
|
+
</select>
|
|
130
|
+
<input type="search" id="search-input" placeholder="Search... (/regex/)" autocomplete="off" title="Search messages. /pattern/ for regex. Qualifiers: from: to: subject: date: after: before: has:attachment is:flagged|unread|answered|draft folder:">
|
|
131
|
+
<label class="search-server-check" title="Also search the IMAP server (slower; spans all folders on all accounts)"><input type="checkbox" id="search-server-too"> Server</label>
|
|
132
|
+
</search>
|
|
133
|
+
<div class="ml-folder-title" id="ml-folder-title"></div>
|
|
134
|
+
<div class="ml-header" id="ml-header">
|
|
135
|
+
<span class="ml-col ml-col-avatar"></span>
|
|
136
|
+
<span class="ml-col ml-col-flag"></span>
|
|
137
|
+
<span class="ml-col ml-col-from ml-col-sortable" data-sort="from">From</span>
|
|
138
|
+
<span class="ml-col ml-col-date ml-col-sortable" data-sort="date">Date</span>
|
|
139
|
+
<span class="ml-col ml-col-subject ml-col-sortable" data-sort="subject">Subject</span>
|
|
140
|
+
</div>
|
|
141
|
+
<div class="ml-body" id="ml-body">
|
|
142
|
+
<div class="ml-empty">Select a folder to view messages</div>
|
|
143
|
+
</div>
|
|
144
|
+
</section>
|
|
145
|
+
|
|
146
|
+
<div class="splitter" id="splitter-h"></div>
|
|
147
|
+
|
|
148
|
+
<section class="message-viewer" id="message-viewer">
|
|
149
|
+
<div class="mv-header" id="mv-header" hidden>
|
|
150
|
+
<div class="mv-toolbar">
|
|
151
|
+
<button class="tb-btn btn-back-list" id="btn-back" title="Back to message list" hidden>← Inbox</button>
|
|
152
|
+
<button class="tb-btn" id="btn-reply" title="Reply (Ctrl+R)">↩</button>
|
|
153
|
+
<button class="tb-btn" id="btn-reply-all" title="Reply All (Ctrl+Shift+R)">↩↩</button>
|
|
154
|
+
<button class="tb-btn" id="btn-forward" title="Forward">→</button>
|
|
155
|
+
<button class="tb-btn" id="btn-delete" title="Delete (Del)">🗑</button>
|
|
156
|
+
<button class="tb-btn" id="btn-spam" title="Mark as spam — move to configured spam folder" hidden>⚠</button>
|
|
157
|
+
<button class="tb-btn" id="btn-spam-report" title="Report as spam (append to ~/.rmfmail/spam.csv for later analysis)">🚫</button>
|
|
158
|
+
<button class="tb-btn" id="btn-flag" title="Flag">⚑</button>
|
|
159
|
+
<button class="tb-btn" id="btn-mark-unread" title="Mark unread (R)">◉</button>
|
|
160
|
+
<button class="tb-btn" id="mv-view-thread" title="View thread (conversation)" hidden>💬</button>
|
|
161
|
+
<span style="flex:1"></span>
|
|
162
|
+
<button class="mv-action mv-action-primary" id="mv-edit-draft" hidden>Edit & Send</button>
|
|
163
|
+
<a class="mv-unsubscribe" id="mv-unsubscribe" hidden>Unsubscribe</a>
|
|
164
|
+
<button class="mv-action" id="mv-view-source" title="View source (.eml)" hidden>Source</button>
|
|
165
|
+
<button class="mv-action" id="mv-popout" title="Pop out (desktop) / Full-screen (mobile)">⤢</button>
|
|
166
|
+
<button class="mv-action" id="mv-toggle-details" title="Show/hide extra headers">Details</button>
|
|
167
|
+
</div>
|
|
168
|
+
<div class="mv-header-info">
|
|
169
|
+
<div class="mv-from"></div>
|
|
170
|
+
<div class="mv-to"></div>
|
|
171
|
+
</div>
|
|
172
|
+
<div class="mv-subject"></div>
|
|
173
|
+
<div class="mv-date"></div>
|
|
174
|
+
<div class="mv-details" id="mv-details" hidden></div>
|
|
175
|
+
</div>
|
|
176
|
+
<div class="mv-body" id="mv-body">
|
|
177
|
+
<div class="mv-empty">Select a message to read</div>
|
|
178
|
+
</div>
|
|
179
|
+
<div class="mv-attachments" id="mv-attachments" hidden></div>
|
|
180
|
+
</section>
|
|
181
|
+
</main>
|
|
182
|
+
|
|
183
|
+
<aside class="calendar-sidebar" id="calendar-sidebar" hidden aria-label="Calendar sidebar">
|
|
184
|
+
<header class="cal-side-head">
|
|
185
|
+
<button class="cal-side-nav" id="cal-side-prev" title="Previous">‹</button>
|
|
186
|
+
<span class="cal-side-date" id="cal-side-date"></span>
|
|
187
|
+
<button class="cal-side-nav" id="cal-side-today" title="Today">○</button>
|
|
188
|
+
<button class="cal-side-nav" id="cal-side-next" title="Next">›</button>
|
|
189
|
+
</header>
|
|
190
|
+
<div class="cal-side-actions">
|
|
191
|
+
<button class="cal-side-new" id="cal-side-new" title="New event">+ New event</button>
|
|
192
|
+
<button class="cal-side-new" id="cal-side-refresh-events" title="Refresh events from Google">↻</button>
|
|
193
|
+
<label class="cal-side-opt" title="Include expanded instances of recurring events">
|
|
194
|
+
<input type="checkbox" id="cal-side-show-recurring" checked> Show recurring
|
|
195
|
+
</label>
|
|
196
|
+
<label class="cal-side-opt" title="How many days ahead to list">
|
|
197
|
+
Horizon <input type="number" id="cal-side-horizon" min="1" max="365" step="1" style="width:4em">
|
|
198
|
+
</label>
|
|
199
|
+
</div>
|
|
200
|
+
<div class="cal-side-body" id="cal-side-body">
|
|
201
|
+
<div class="cal-side-empty">Loading…</div>
|
|
202
|
+
</div>
|
|
203
|
+
<footer class="cal-side-foot">
|
|
204
|
+
<div class="cal-side-task-header-row">
|
|
205
|
+
<label><input type="checkbox" id="cal-side-show-done"> Show completed Tasks</label>
|
|
206
|
+
<button class="cal-side-new" id="cal-side-refresh-tasks" title="Refresh tasks from Google">↻</button>
|
|
207
|
+
<button class="cal-side-new" id="cal-side-new-task" title="New task">+ Task</button>
|
|
208
|
+
</div>
|
|
209
|
+
<div class="cal-side-tasks" id="cal-side-tasks"></div>
|
|
210
|
+
</footer>
|
|
211
|
+
</aside>
|
|
212
|
+
|
|
213
|
+
<footer class="status-bar" id="status-bar">
|
|
214
|
+
<span id="status-accounts"></span>
|
|
215
|
+
<span id="status-sync">Syncing...</span>
|
|
216
|
+
<span id="status-offline" class="status-offline" hidden title="No network — local actions queue for later">⚡ offline</span>
|
|
217
|
+
<span id="status-diag" class="status-diag" hidden title=""></span>
|
|
218
|
+
<span id="status-pending"></span>
|
|
219
|
+
<span id="status-queue"></span>
|
|
220
|
+
<span class="app-version" id="status-version">rmfmail</span>
|
|
221
|
+
</footer>
|
|
222
|
+
|
|
223
|
+
<div id="startup-overlay" class="startup-overlay">
|
|
224
|
+
<div class="startup-content">
|
|
225
|
+
<div class="startup-spinner"></div>
|
|
226
|
+
<div id="startup-status">Connecting to server...</div>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
</body>
|
|
230
|
+
</html>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stub — the real android bootstrap is in packages/mailx-store-web/android-bootstrap.ts.
|
|
3
|
+
* This file exists only because it was created during development. The Android HTML
|
|
4
|
+
* (client/android.html) loads the bootstrap from the package via import map.
|
|
5
|
+
*
|
|
6
|
+
* This file is excluded from the desktop build (not imported by anything).
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=android-bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"android-bootstrap.js","sourceRoot":"","sources":["android-bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stub — the real android bootstrap is in packages/mailx-store-web/android-bootstrap.ts.
|
|
3
|
+
* This file exists only because it was created during development. The Android HTML
|
|
4
|
+
* (client/android.html) loads the bootstrap from the package via import map.
|
|
5
|
+
*
|
|
6
|
+
* This file is excluded from the desktop build (not imported by anything).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export {};
|