@bobfrankston/rmfmail 1.1.174 → 1.1.177
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/bin/mailx.js +30 -2
- package/bin/mailx.js.map +1 -1
- package/bin/mailx.ts +32 -3
- package/client/app.bundle.js +36 -0
- package/client/app.bundle.js.map +2 -2
- package/client/app.js +39 -0
- package/client/app.js.map +1 -1
- package/client/app.ts +33 -0
- package/client/components/message-list.js +18 -0
- package/client/components/message-list.js.map +1 -1
- package/client/components/message-list.ts +19 -0
- package/client/compose/compose.bundle.js +2068 -1775
- package/client/compose/compose.bundle.js.map +4 -4
- package/client/compose/editor.js +85 -0
- package/client/compose/editor.js.map +1 -1
- package/client/compose/editor.ts +79 -0
- package/client/compose/spellcheck-core.js +253 -0
- package/client/compose/spellcheck-core.js.map +1 -0
- package/client/compose/spellcheck-core.ts +242 -0
- package/package.json +5 -5
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-50120 → node_modules.npmglobalize-stash-50992}/.package-lock.json +0 -0
|
@@ -31,1884 +31,2111 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
31
|
mod
|
|
32
32
|
));
|
|
33
33
|
|
|
34
|
-
//
|
|
35
|
-
var
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
addUserDictWord: () => addUserDictWord,
|
|
42
|
-
addUserDictWords: () => addUserDictWords,
|
|
43
|
-
aiTransform: () => aiTransform,
|
|
44
|
-
allowRemoteContent: () => allowRemoteContent,
|
|
45
|
-
autocomplete: () => autocomplete,
|
|
46
|
-
cancelQueuedOutgoing: () => cancelQueuedOutgoing,
|
|
47
|
-
cancelServerSearch: () => cancelServerSearch,
|
|
48
|
-
closeWordEdit: () => closeWordEdit,
|
|
49
|
-
connectEvents: () => connectEvents,
|
|
50
|
-
connectWebSocket: () => connectWebSocket,
|
|
51
|
-
consumePendingMailto: () => consumePendingMailto,
|
|
52
|
-
createCalendarEvent: () => createCalendarEvent,
|
|
53
|
-
createFolder: () => createFolder,
|
|
54
|
-
createTask: () => createTask,
|
|
55
|
-
deleteCalendarEvent: () => deleteCalendarEvent,
|
|
56
|
-
deleteContact: () => deleteContact,
|
|
57
|
-
deleteDraft: () => deleteDraft,
|
|
58
|
-
deleteFolder: () => deleteFolder,
|
|
59
|
-
deleteMessage: () => deleteMessage,
|
|
60
|
-
deleteMessages: () => deleteMessages,
|
|
61
|
-
deleteTask: () => deleteTask,
|
|
62
|
-
drainStoreSync: () => drainStoreSync,
|
|
63
|
-
emptyFolder: () => emptyFolder,
|
|
64
|
-
flagSenderOrDomain: () => flagSenderOrDomain,
|
|
65
|
-
formatJsonc: () => formatJsonc,
|
|
66
|
-
getAccounts: () => getAccounts,
|
|
67
|
-
getAttachment: () => getAttachment,
|
|
68
|
-
getAutocompleteSettings: () => getAutocompleteSettings,
|
|
69
|
-
getCalendarEvents: () => getCalendarEvents,
|
|
70
|
-
getCalendars: () => getCalendars,
|
|
71
|
-
getDeviceAccounts: () => getDeviceAccounts,
|
|
72
|
-
getDiagnostics: () => getDiagnostics,
|
|
73
|
-
getFolders: () => getFolders,
|
|
74
|
-
getMessage: () => getMessage,
|
|
75
|
-
getMessages: () => getMessages,
|
|
76
|
-
getOutboxStatus: () => getOutboxStatus,
|
|
77
|
-
getPrimaryAccount: () => getPrimaryAccount,
|
|
78
|
-
getPriorityLists: () => getPriorityLists,
|
|
79
|
-
getSettings: () => getSettings,
|
|
80
|
-
getSyncPending: () => getSyncPending,
|
|
81
|
-
getTasks: () => getTasks,
|
|
82
|
-
getThreadMessages: () => getThreadMessages,
|
|
83
|
-
getUnifiedInbox: () => getUnifiedInbox,
|
|
84
|
-
getUserDict: () => getUserDict,
|
|
85
|
-
getVersion: () => getVersion,
|
|
86
|
-
hasBccHistoryTo: () => hasBccHistoryTo,
|
|
87
|
-
hasCcHistoryTo: () => hasCcHistoryTo,
|
|
88
|
-
listContacts: () => listContacts,
|
|
89
|
-
listQueuedOutgoing: () => listQueuedOutgoing,
|
|
90
|
-
logClientEvent: () => logClientEvent,
|
|
91
|
-
markAsSpamMessages: () => markAsSpamMessages,
|
|
92
|
-
markFolderRead: () => markFolderRead,
|
|
93
|
-
moveFolderToTrash: () => moveFolderToTrash,
|
|
94
|
-
moveMessage: () => moveMessage,
|
|
95
|
-
moveMessages: () => moveMessages,
|
|
96
|
-
onEvent: () => onEvent,
|
|
97
|
-
onWsEvent: () => onWsEvent,
|
|
98
|
-
openAttachment: () => openAttachment,
|
|
99
|
-
openInTextEditor: () => openInTextEditor,
|
|
100
|
-
openInWord: () => openInWord,
|
|
101
|
-
openLocalPath: () => openLocalPath,
|
|
102
|
-
readConfigHelp: () => readConfigHelp,
|
|
103
|
-
readJsoncFile: () => readJsoncFile,
|
|
104
|
-
reauthGoogleScopes: () => reauthGoogleScopes,
|
|
105
|
-
reauthenticate: () => reauthenticate,
|
|
106
|
-
recordSpamReport: () => recordSpamReport,
|
|
107
|
-
removeUserDictWord: () => removeUserDictWord,
|
|
108
|
-
renameFolder: () => renameFolder,
|
|
109
|
-
repairAccounts: () => repairAccounts,
|
|
110
|
-
restartServer: () => restartServer,
|
|
111
|
-
saveAutocompleteSettings: () => saveAutocompleteSettings,
|
|
112
|
-
saveDraft: () => saveDraft,
|
|
113
|
-
saveSettings: () => saveSettings,
|
|
114
|
-
searchContacts: () => searchContacts,
|
|
115
|
-
searchMessages: () => searchMessages,
|
|
116
|
-
sendMessage: () => sendMessage,
|
|
117
|
-
setPriorityDomain: () => setPriorityDomain,
|
|
118
|
-
setPrioritySender: () => setPrioritySender,
|
|
119
|
-
setupAccount: () => setupAccount,
|
|
120
|
-
showReminderPopup: () => showReminderPopup,
|
|
121
|
-
subscribeStore: () => subscribeStore,
|
|
122
|
-
syncAccount: () => syncAccount,
|
|
123
|
-
triggerSync: () => triggerSync,
|
|
124
|
-
undeleteMessage: () => undeleteMessage,
|
|
125
|
-
unsubscribeOneClick: () => unsubscribeOneClick,
|
|
126
|
-
updateCalendarEvent: () => updateCalendarEvent,
|
|
127
|
-
updateFlags: () => updateFlags,
|
|
128
|
-
updateTask: () => updateTask,
|
|
129
|
-
upsertContact: () => upsertContact,
|
|
130
|
-
writeJsoncFile: () => writeJsoncFile
|
|
34
|
+
// node_modules/is-buffer/index.js
|
|
35
|
+
var require_is_buffer = __commonJS({
|
|
36
|
+
"node_modules/is-buffer/index.js"(exports, module) {
|
|
37
|
+
module.exports = function isBuffer(obj) {
|
|
38
|
+
return obj != null && obj.constructor != null && typeof obj.constructor.isBuffer === "function" && obj.constructor.isBuffer(obj);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
131
41
|
});
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
clearTimeout(entry.timer);
|
|
151
|
-
if (ev.data.ok)
|
|
152
|
-
entry.resolve(ev.data.result);
|
|
153
|
-
else
|
|
154
|
-
entry.reject(new Error(ev.data.error || "parent-relay ipc error"));
|
|
155
|
-
});
|
|
156
|
-
const call = (method, args) => {
|
|
157
|
-
const id = `ipc-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
158
|
-
return new Promise((resolve, reject) => {
|
|
159
|
-
const timer = setTimeout(() => {
|
|
160
|
-
pending.delete(id);
|
|
161
|
-
reject(new Error(`parent-relay timeout: ${method}`));
|
|
162
|
-
}, 12e4);
|
|
163
|
-
pending.set(id, { resolve, reject, timer });
|
|
164
|
-
try {
|
|
165
|
-
window.parent.postMessage({ type: "mailx-ipc", id, method, args }, "*");
|
|
166
|
-
} catch (e) {
|
|
167
|
-
clearTimeout(timer);
|
|
168
|
-
pending.delete(id);
|
|
169
|
-
reject(e);
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
};
|
|
173
|
-
return new Proxy({}, {
|
|
174
|
-
get(_t, prop) {
|
|
175
|
-
if (prop === "isApp")
|
|
176
|
-
return true;
|
|
177
|
-
if (prop === "platform")
|
|
178
|
-
return window.parent?.mailxapi?.platform || "webview2";
|
|
179
|
-
if (prop === "onEvent") {
|
|
180
|
-
return (handler) => window.parent?.mailxapi?.onEvent?.(handler);
|
|
42
|
+
|
|
43
|
+
// node_modules/nspell/lib/util/rule-codes.js
|
|
44
|
+
var require_rule_codes = __commonJS({
|
|
45
|
+
"node_modules/nspell/lib/util/rule-codes.js"(exports, module) {
|
|
46
|
+
"use strict";
|
|
47
|
+
module.exports = ruleCodes;
|
|
48
|
+
var NO_CODES = [];
|
|
49
|
+
function ruleCodes(flags, value) {
|
|
50
|
+
var index = 0;
|
|
51
|
+
var result;
|
|
52
|
+
if (!value) return NO_CODES;
|
|
53
|
+
if (flags.FLAG === "long") {
|
|
54
|
+
result = new Array(Math.ceil(value.length / 2));
|
|
55
|
+
while (index < value.length) {
|
|
56
|
+
result[index / 2] = value.slice(index, index + 2);
|
|
57
|
+
index += 2;
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
181
60
|
}
|
|
182
|
-
return (
|
|
61
|
+
return value.split(flags.FLAG === "num" ? "," : "");
|
|
183
62
|
}
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
function ipc() {
|
|
187
|
-
const inIframe = window.parent && window.parent !== window;
|
|
188
|
-
if (inIframe && window.parent?.mailxapi?.isApp) {
|
|
189
|
-
if (!cachedRelayBridge)
|
|
190
|
-
cachedRelayBridge = buildRelayBridge();
|
|
191
|
-
return cachedRelayBridge;
|
|
192
|
-
}
|
|
193
|
-
const bridge = getIpc();
|
|
194
|
-
if (!bridge)
|
|
195
|
-
throw new Error("IPC bridge not available");
|
|
196
|
-
return bridge;
|
|
197
|
-
}
|
|
198
|
-
function abortMessageListRequests() {
|
|
199
|
-
if (messageListAbort) {
|
|
200
|
-
messageListAbort.abort();
|
|
201
|
-
messageListAbort = null;
|
|
202
63
|
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
function recordSpamReport(accountId, uid, folderId) {
|
|
282
|
-
return ipc().recordSpamReport?.(accountId, uid, folderId);
|
|
283
|
-
}
|
|
284
|
-
function getOutboxStatus() {
|
|
285
|
-
return ipc().getOutboxStatus();
|
|
286
|
-
}
|
|
287
|
-
function listQueuedOutgoing() {
|
|
288
|
-
return ipc().listQueuedOutgoing();
|
|
289
|
-
}
|
|
290
|
-
function cancelQueuedOutgoing(p) {
|
|
291
|
-
return ipc().cancelQueuedOutgoing(p);
|
|
292
|
-
}
|
|
293
|
-
function searchContacts(query) {
|
|
294
|
-
return ipc().searchContacts(query);
|
|
295
|
-
}
|
|
296
|
-
function hasCcHistoryTo(email) {
|
|
297
|
-
return ipc().hasCcHistoryTo(email);
|
|
298
|
-
}
|
|
299
|
-
function hasBccHistoryTo(email) {
|
|
300
|
-
return ipc().hasBccHistoryTo(email);
|
|
301
|
-
}
|
|
302
|
-
function listContacts(query, page = 1, pageSize = 100) {
|
|
303
|
-
return ipc().listContacts(query, page, pageSize);
|
|
304
|
-
}
|
|
305
|
-
function upsertContact(name, email) {
|
|
306
|
-
return ipc().upsertContact(name, email);
|
|
307
|
-
}
|
|
308
|
-
function deleteContact(email) {
|
|
309
|
-
return ipc().deleteContact(email);
|
|
310
|
-
}
|
|
311
|
-
function addPreferredContact(entry) {
|
|
312
|
-
return ipc().addPreferredContact(entry.name, entry.email, entry.source, entry.organization);
|
|
313
|
-
}
|
|
314
|
-
function getPriorityLists() {
|
|
315
|
-
return ipc().getPriorityLists();
|
|
316
|
-
}
|
|
317
|
-
function setPrioritySender(email, value, name) {
|
|
318
|
-
return ipc().setPrioritySender(email, value, name);
|
|
319
|
-
}
|
|
320
|
-
function setPriorityDomain(domain, value) {
|
|
321
|
-
return ipc().setPriorityDomain(domain, value);
|
|
322
|
-
}
|
|
323
|
-
function addToDenylist(email) {
|
|
324
|
-
return ipc().addToDenylist(email);
|
|
325
|
-
}
|
|
326
|
-
function openLocalPath(which) {
|
|
327
|
-
return ipc().openLocalPath(which);
|
|
328
|
-
}
|
|
329
|
-
function openInTextEditor(path) {
|
|
330
|
-
return ipc().openInTextEditor?.(path) ?? Promise.resolve({ ok: false, opener: "none", reason: "no host" });
|
|
331
|
-
}
|
|
332
|
-
function allowRemoteContent(type, value) {
|
|
333
|
-
return ipc().allowRemoteContent(type, value);
|
|
334
|
-
}
|
|
335
|
-
function getUserDict() {
|
|
336
|
-
return ipc().getUserDict?.() ?? Promise.resolve([]);
|
|
337
|
-
}
|
|
338
|
-
function addUserDictWord(word) {
|
|
339
|
-
return ipc().addUserDictWord?.(word) ?? Promise.resolve([]);
|
|
340
|
-
}
|
|
341
|
-
function addUserDictWords(words) {
|
|
342
|
-
return ipc().addUserDictWords?.(words) ?? Promise.resolve([]);
|
|
343
|
-
}
|
|
344
|
-
function removeUserDictWord(word) {
|
|
345
|
-
return ipc().removeUserDictWord?.(word) ?? Promise.resolve([]);
|
|
346
|
-
}
|
|
347
|
-
function flagSenderOrDomain(type, value) {
|
|
348
|
-
return ipc().flagSenderOrDomain?.(type, value) ?? Promise.resolve({ flagged: false });
|
|
349
|
-
}
|
|
350
|
-
function deleteMessage(accountId, uid) {
|
|
351
|
-
return ipc().deleteMessage?.(accountId, uid);
|
|
352
|
-
}
|
|
353
|
-
function deleteMessages(accountId, uids) {
|
|
354
|
-
if (uids.length === 1)
|
|
355
|
-
return deleteMessage(accountId, uids[0]);
|
|
356
|
-
return ipc().deleteMessages?.(accountId, uids);
|
|
357
|
-
}
|
|
358
|
-
function moveMessages(accountId, uids, targetFolderId, targetAccountId) {
|
|
359
|
-
if (uids.length === 1)
|
|
360
|
-
return moveMessage(accountId, uids[0], targetFolderId, targetAccountId);
|
|
361
|
-
return ipc().moveMessages?.(accountId, uids, targetFolderId, targetAccountId);
|
|
362
|
-
}
|
|
363
|
-
function markAsSpamMessages(accountId, uids) {
|
|
364
|
-
return ipc().markAsSpamMessages?.(accountId, uids);
|
|
365
|
-
}
|
|
366
|
-
function undeleteMessage(accountId, uid, folderId) {
|
|
367
|
-
return ipc().undeleteMessage?.(accountId, uid, folderId);
|
|
368
|
-
}
|
|
369
|
-
function moveMessage(accountId, uid, targetFolderId, targetAccountId) {
|
|
370
|
-
return ipc().moveMessage?.(accountId, uid, targetFolderId, targetAccountId);
|
|
371
|
-
}
|
|
372
|
-
function restartServer() {
|
|
373
|
-
return ipc().restart?.();
|
|
374
|
-
}
|
|
375
|
-
function markFolderRead(accountId, folderId) {
|
|
376
|
-
return ipc().markFolderRead?.(accountId, folderId);
|
|
377
|
-
}
|
|
378
|
-
function createFolder(accountId, parentPath, name) {
|
|
379
|
-
return ipc().createFolder?.(accountId, parentPath, name);
|
|
380
|
-
}
|
|
381
|
-
function renameFolder(accountId, folderId, newName) {
|
|
382
|
-
return ipc().renameFolder?.(accountId, folderId, newName);
|
|
383
|
-
}
|
|
384
|
-
function deleteFolder(accountId, folderId) {
|
|
385
|
-
return ipc().deleteFolder?.(accountId, folderId);
|
|
386
|
-
}
|
|
387
|
-
function moveFolderToTrash(accountId, folderId) {
|
|
388
|
-
return ipc().moveFolderToTrash?.(accountId, folderId);
|
|
389
|
-
}
|
|
390
|
-
function emptyFolder(accountId, folderId) {
|
|
391
|
-
return ipc().emptyFolder?.(accountId, folderId);
|
|
392
|
-
}
|
|
393
|
-
function logClientEvent(tag, data) {
|
|
394
|
-
let delivered = false;
|
|
395
|
-
try {
|
|
396
|
-
const bridge = typeof globalThis.mailxapi !== "undefined" && globalThis.mailxapi?.isApp ? globalThis.mailxapi : window.opener?.mailxapi?.isApp ? window.opener.mailxapi : window.parent?.mailxapi?.isApp ? window.parent.mailxapi : null;
|
|
397
|
-
if (bridge?.logClientEvent) {
|
|
398
|
-
bridge.logClientEvent(tag, data);
|
|
399
|
-
delivered = true;
|
|
400
|
-
}
|
|
401
|
-
} catch {
|
|
402
|
-
}
|
|
403
|
-
try {
|
|
404
|
-
if (window.parent && window.parent !== window) {
|
|
405
|
-
window.parent.postMessage({ type: "mailx-trace", tag, data, bridged: delivered }, "*");
|
|
406
|
-
}
|
|
407
|
-
} catch {
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
function sendMessage(body) {
|
|
411
|
-
return ipc().sendMessage?.(body);
|
|
412
|
-
}
|
|
413
|
-
function saveDraft(body) {
|
|
414
|
-
return ipc().saveDraft?.(body);
|
|
415
|
-
}
|
|
416
|
-
function onEvent(handler) {
|
|
417
|
-
eventHandlers.push(handler);
|
|
418
|
-
return () => {
|
|
419
|
-
const i = eventHandlers.indexOf(handler);
|
|
420
|
-
if (i >= 0)
|
|
421
|
-
eventHandlers.splice(i, 1);
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
function subscribeStore(topic, handler) {
|
|
425
|
-
let set = storeSubs.get(topic);
|
|
426
|
-
if (!set) {
|
|
427
|
-
set = /* @__PURE__ */ new Set();
|
|
428
|
-
storeSubs.set(topic, set);
|
|
429
|
-
}
|
|
430
|
-
set.add(handler);
|
|
431
|
-
return () => {
|
|
432
|
-
const s = storeSubs.get(topic);
|
|
433
|
-
if (s) {
|
|
434
|
-
s.delete(handler);
|
|
435
|
-
if (s.size === 0)
|
|
436
|
-
storeSubs.delete(topic);
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
function deliverStore(event) {
|
|
441
|
-
const exact = storeSubs.get(event.topic);
|
|
442
|
-
if (exact)
|
|
443
|
-
for (const h of exact) {
|
|
444
|
-
try {
|
|
445
|
-
h(event);
|
|
446
|
-
} catch (e) {
|
|
447
|
-
console.error("[store-bus]", e);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
const wild = storeSubs.get("*");
|
|
451
|
-
if (wild)
|
|
452
|
-
for (const h of wild) {
|
|
453
|
-
try {
|
|
454
|
-
h(event);
|
|
455
|
-
} catch (e) {
|
|
456
|
-
console.error("[store-bus]", e);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
function connectEvents() {
|
|
461
|
-
ipc().onEvent((event) => {
|
|
462
|
-
if (event && event._event === "store")
|
|
463
|
-
deliverStore(event);
|
|
464
|
-
for (const h of eventHandlers)
|
|
465
|
-
h(event);
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
function autocomplete(body, signal) {
|
|
469
|
-
return ipc().autocomplete?.(body);
|
|
470
|
-
}
|
|
471
|
-
function getAutocompleteSettings() {
|
|
472
|
-
return ipc().getAutocompleteSettings?.();
|
|
473
|
-
}
|
|
474
|
-
function saveAutocompleteSettings(settings) {
|
|
475
|
-
return ipc().saveAutocompleteSettings?.(settings);
|
|
476
|
-
}
|
|
477
|
-
function getVersion() {
|
|
478
|
-
return ipc().getVersion();
|
|
479
|
-
}
|
|
480
|
-
function getSettings() {
|
|
481
|
-
return ipc().getSettings();
|
|
482
|
-
}
|
|
483
|
-
function saveSettings(settings) {
|
|
484
|
-
return ipc().saveSettingsData?.(settings);
|
|
485
|
-
}
|
|
486
|
-
function repairAccounts() {
|
|
487
|
-
return ipc().repairAccounts?.();
|
|
488
|
-
}
|
|
489
|
-
function deleteDraft(accountId, draftUid2, draftId2) {
|
|
490
|
-
return ipc().deleteDraft?.(accountId, draftUid2, draftId2);
|
|
491
|
-
}
|
|
492
|
-
function addContact(name, email) {
|
|
493
|
-
return ipc().addContact?.(name, email);
|
|
494
|
-
}
|
|
495
|
-
function getThreadMessages(accountId, threadId) {
|
|
496
|
-
return ipc().getThreadMessages?.(accountId, threadId);
|
|
497
|
-
}
|
|
498
|
-
function readJsoncFile(name) {
|
|
499
|
-
return ipc().readJsoncFile?.(name);
|
|
500
|
-
}
|
|
501
|
-
function writeJsoncFile(name, content) {
|
|
502
|
-
return ipc().writeJsoncFile?.(name, content);
|
|
503
|
-
}
|
|
504
|
-
function formatJsonc(content) {
|
|
505
|
-
return ipc().formatJsonc?.(content);
|
|
506
|
-
}
|
|
507
|
-
function readConfigHelp(name) {
|
|
508
|
-
return ipc().readConfigHelp?.(name) ?? Promise.resolve({ content: "" });
|
|
509
|
-
}
|
|
510
|
-
function unsubscribeOneClick(url) {
|
|
511
|
-
return ipc().unsubscribeOneClick?.(url);
|
|
512
|
-
}
|
|
513
|
-
function openInWord(editId, html) {
|
|
514
|
-
return ipc().openInWord?.(editId, html) ?? Promise.resolve({ ok: false, path: "", opener: "none" });
|
|
515
|
-
}
|
|
516
|
-
function closeWordEdit(editId) {
|
|
517
|
-
return ipc().closeWordEdit?.(editId) ?? Promise.resolve();
|
|
518
|
-
}
|
|
519
|
-
function showReminderPopup(opts) {
|
|
520
|
-
return ipc().showReminderPopup?.(opts) ?? Promise.resolve({ button: "", reason: "no host" });
|
|
521
|
-
}
|
|
522
|
-
function consumePendingMailto() {
|
|
523
|
-
return ipc().consumePendingMailto?.() ?? Promise.resolve(null);
|
|
524
|
-
}
|
|
525
|
-
function aiTransform(req) {
|
|
526
|
-
return ipc().aiTransform?.(req) ?? Promise.resolve({ text: "", reason: "AI not available in this host" });
|
|
527
|
-
}
|
|
528
|
-
function setupAccount(name, email, password) {
|
|
529
|
-
return ipc().setupAccount?.(name, email, password);
|
|
530
|
-
}
|
|
531
|
-
async function getAttachment(accountId, uid, attachmentId, folderId) {
|
|
532
|
-
return ipc().getAttachment(accountId, uid, attachmentId, folderId);
|
|
533
|
-
}
|
|
534
|
-
async function openAttachment(accountId, uid, attachmentId, folderId) {
|
|
535
|
-
const fn = ipc().openAttachment;
|
|
536
|
-
return fn ? fn(accountId, uid, attachmentId, folderId) : void 0;
|
|
537
|
-
}
|
|
538
|
-
async function getDeviceAccounts() {
|
|
539
|
-
return ipc().getDeviceAccounts?.() ?? [];
|
|
540
|
-
}
|
|
541
|
-
var cachedRelayBridge, messageListAbort, eventHandlers, storeSubs, connectWebSocket, onWsEvent;
|
|
542
|
-
var init_api_client = __esm({
|
|
543
|
-
"client/lib/api-client.js"() {
|
|
544
|
-
"use strict";
|
|
545
|
-
cachedRelayBridge = null;
|
|
546
|
-
messageListAbort = null;
|
|
547
|
-
eventHandlers = [];
|
|
548
|
-
storeSubs = /* @__PURE__ */ new Map();
|
|
549
|
-
connectWebSocket = connectEvents;
|
|
550
|
-
onWsEvent = onEvent;
|
|
551
|
-
}
|
|
552
|
-
});
|
|
553
|
-
|
|
554
|
-
// client/lib/rmf-tiny.js
|
|
555
|
-
var rmf_tiny_exports = {};
|
|
556
|
-
__export(rmf_tiny_exports, {
|
|
557
|
-
createTinyMceEditor: () => createTinyMceEditor
|
|
558
|
-
});
|
|
559
|
-
async function loadTinymce(opts) {
|
|
560
|
-
try {
|
|
561
|
-
const mod = await import(
|
|
562
|
-
/* @vite-ignore */
|
|
563
|
-
"tinymce"
|
|
564
|
-
);
|
|
565
|
-
const tinymce = mod.default || mod;
|
|
566
|
-
await Promise.all([
|
|
567
|
-
import("tinymce/themes/silver").catch(() => {
|
|
568
|
-
}),
|
|
569
|
-
import("tinymce/icons/default").catch(() => {
|
|
570
|
-
}),
|
|
571
|
-
import("tinymce/models/dom").catch(() => {
|
|
572
|
-
}),
|
|
573
|
-
import("tinymce/plugins/paste").catch(() => {
|
|
574
|
-
}),
|
|
575
|
-
import("tinymce/plugins/lists").catch(() => {
|
|
576
|
-
}),
|
|
577
|
-
import("tinymce/plugins/link").catch(() => {
|
|
578
|
-
}),
|
|
579
|
-
import("tinymce/plugins/table").catch(() => {
|
|
580
|
-
}),
|
|
581
|
-
import("tinymce/plugins/code").catch(() => {
|
|
582
|
-
}),
|
|
583
|
-
import("tinymce/plugins/image").catch(() => {
|
|
584
|
-
})
|
|
585
|
-
]);
|
|
586
|
-
return tinymce;
|
|
587
|
-
} catch {
|
|
588
|
-
}
|
|
589
|
-
const w = window;
|
|
590
|
-
if (w.tinymce)
|
|
591
|
-
return w.tinymce;
|
|
592
|
-
if (!opts.cdnUrl) {
|
|
593
|
-
throw new Error("rmf-tiny: tinymce not installed (npm install tinymce) and no cdnUrl supplied. See README for Android setup.");
|
|
594
|
-
}
|
|
595
|
-
const url = opts.apiKey && !opts.cdnUrl.includes("api-key") ? `${opts.cdnUrl}${opts.cdnUrl.includes("?") ? "&" : "?"}apiKey=${encodeURIComponent(opts.apiKey)}` : opts.cdnUrl;
|
|
596
|
-
await new Promise((resolve, reject) => {
|
|
597
|
-
const s = document.createElement("script");
|
|
598
|
-
s.src = url;
|
|
599
|
-
s.referrerPolicy = "origin";
|
|
600
|
-
s.onload = () => resolve();
|
|
601
|
-
s.onerror = () => reject(new Error(`rmf-tiny: failed to load TinyMCE from ${url}`));
|
|
602
|
-
document.head.appendChild(s);
|
|
603
|
-
});
|
|
604
|
-
if (!w.tinymce)
|
|
605
|
-
throw new Error("rmf-tiny: TinyMCE script loaded but window.tinymce is missing");
|
|
606
|
-
return w.tinymce;
|
|
607
|
-
}
|
|
608
|
-
async function createTinyMceEditor(container2, opts = {}) {
|
|
609
|
-
const tinymce = await loadTinymce(opts);
|
|
610
|
-
const target = document.createElement("div");
|
|
611
|
-
target.id = `rmf-tiny-${Math.random().toString(36).slice(2, 10)}`;
|
|
612
|
-
target.style.cssText = "width:100%;height:100%;";
|
|
613
|
-
container2.appendChild(target);
|
|
614
|
-
const editor2 = await new Promise((resolve) => {
|
|
615
|
-
tinymce.init({
|
|
616
|
-
target,
|
|
617
|
-
// Word-paste fidelity is the entire point of using TinyMCE here.
|
|
618
|
-
// `paste_as_text: false` keeps formatting; powerpaste isn't in
|
|
619
|
-
// OSS but the standard paste plugin handles Word reasonably well.
|
|
620
|
-
// All free / OSS plugins bundled in the jsDelivr tinymce@6 script.
|
|
621
|
-
// No premium plugins listed — listing one without an API key
|
|
622
|
-
// triggers TinyMCE's upsell dialog on every load.
|
|
623
|
-
plugins: "paste lists advlist link table code codesample image searchreplace autolink wordcount emoticons charmap insertdatetime quickbars nonbreaking directionality help",
|
|
624
|
-
toolbar: [
|
|
625
|
-
"undo redo | bold italic underline strikethrough | forecolor backcolor",
|
|
626
|
-
"bullist numlist outdent indent | link table image code codesample | emoticons charmap | help"
|
|
627
|
-
].join(" | "),
|
|
628
|
-
// Include "tools" so wordcount and searchreplace are reachable.
|
|
629
|
-
menubar: "file edit view insert format tools",
|
|
630
|
-
// View menu — append zoom commands. fullscreen omitted: this editor
|
|
631
|
-
// lives inside a compose iframe overlay that already fills its host
|
|
632
|
-
// window; TinyMCE's fullscreen plugin re-positions the container in
|
|
633
|
-
// ways that produce a blank body, so it's not in the plugin list.
|
|
634
|
-
menu: {
|
|
635
|
-
view: { title: "View", items: "code | visualaid visualchars visualblocks | preview | zoomIn zoomOut zoomReset" }
|
|
636
|
-
},
|
|
637
|
-
// Disable the "Get all features" upsell badge that TinyMCE 6+
|
|
638
|
-
// injects automatically when no premium plugins are listed.
|
|
639
|
-
promotion: false,
|
|
640
|
-
// Floating toolbar that appears on text selection — keeps the
|
|
641
|
-
// main toolbar uncluttered while exposing common formatters
|
|
642
|
-
// where the cursor already is. quickbars_insert_toolbar:false
|
|
643
|
-
// suppresses the second (insert-on-blank-line) popup which
|
|
644
|
-
// clutters more than it helps in an email composer.
|
|
645
|
-
quickbars_selection_toolbar: "bold italic underline | forecolor | quicklink blockquote",
|
|
646
|
-
quickbars_insert_toolbar: false,
|
|
647
|
-
quickbars_image_toolbar: "alignleft aligncenter alignright",
|
|
648
|
-
// Code-sample dropdown languages. TinyMCE's default list omits
|
|
649
|
-
// "Text" / plain — every option triggers syntax highlighting
|
|
650
|
-
// which mangles unrelated paste content (Bob 2026-05-24).
|
|
651
|
-
// Adding Text first so it's the default; rest are the modern
|
|
652
|
-
// languages we actually paste.
|
|
653
|
-
codesample_languages: [
|
|
654
|
-
{ text: "Text", value: "text" },
|
|
655
|
-
{ text: "HTML/XML", value: "markup" },
|
|
656
|
-
{ text: "JavaScript", value: "javascript" },
|
|
657
|
-
{ text: "TypeScript", value: "typescript" },
|
|
658
|
-
{ text: "CSS", value: "css" },
|
|
659
|
-
{ text: "JSON", value: "json" },
|
|
660
|
-
{ text: "Python", value: "python" },
|
|
661
|
-
{ text: "Java", value: "java" },
|
|
662
|
-
{ text: "C", value: "c" },
|
|
663
|
-
{ text: "C++", value: "cpp" },
|
|
664
|
-
{ text: "C#", value: "csharp" },
|
|
665
|
-
{ text: "Go", value: "go" },
|
|
666
|
-
{ text: "Rust", value: "rust" },
|
|
667
|
-
{ text: "Ruby", value: "ruby" },
|
|
668
|
-
{ text: "PHP", value: "php" },
|
|
669
|
-
{ text: "Shell", value: "bash" },
|
|
670
|
-
{ text: "SQL", value: "sql" }
|
|
671
|
-
],
|
|
672
|
-
// WebView's native spell-check (red underlines, right-click
|
|
673
|
-
// "Add to dictionary"). Free; same UX as Quill's spellcheck=true.
|
|
674
|
-
// Premium tinymcespellchecker plugin would replace this with a
|
|
675
|
-
// custom backend via spellchecker_rpc_url, but requires a
|
|
676
|
-
// Tiny Cloud subscription.
|
|
677
|
-
browser_spellcheck: true,
|
|
678
|
-
// Right-click context menu DISABLED so WebView2's native menu
|
|
679
|
-
// fires instead. We need the native menu because that's where
|
|
680
|
-
// the browser shows spelling suggestions (TinyMCE's contextmenu
|
|
681
|
-
// intercepts the right-click and replaces the menu — red
|
|
682
|
-
// underlines appear but suggestions are unreachable). Trade-off:
|
|
683
|
-
// lose TinyMCE's link / image / table quick actions. Standard
|
|
684
|
-
// text formatting is still on the toolbar.
|
|
685
|
-
contextmenu: false,
|
|
686
|
-
statusbar: false,
|
|
687
|
-
branding: false,
|
|
688
|
-
license_key: "gpl",
|
|
689
|
-
paste_data_images: true,
|
|
690
|
-
// Permissive valid_elements — preserve as much of the source
|
|
691
|
-
// formatting as possible. The default is more aggressive about
|
|
692
|
-
// stripping. Empty string for `valid_elements` means accept
|
|
693
|
-
// everything that the schema allows.
|
|
694
|
-
paste_word_valid_elements: "@[style|class],-strong/b,-em/i,-u,-s,-sub,-sup,-strike,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,-blockquote,-table[border|cellpadding|cellspacing|width|height|class|style],-tr,-td[colspan|rowspan|width|height|class|style|valign|align|background|bgcolor],-th,-thead,-tbody,-tfoot,-pre,-br,-a[href|target|title],-img[src|alt|width|height|style|class]",
|
|
695
|
-
paste_retain_style_properties: "color background background-color font-family font-size font-weight font-style text-decoration text-align padding padding-top padding-bottom padding-left padding-right margin margin-top margin-bottom margin-left margin-right border border-top border-bottom border-left border-right",
|
|
696
|
-
// Auto-link bare URLs in pasted content. TinyMCE's `autolink`
|
|
697
|
-
// plugin only fires on TYPED space/enter; URLs that arrive via
|
|
698
|
-
// clipboard (browser address bar, terminal copy) come in as
|
|
699
|
-
// plain text and stay un-linked. paste_preprocess runs on the
|
|
700
|
-
// HTML the paste plugin produced.
|
|
701
|
-
//
|
|
702
|
-
// CRITICAL: skip auto-link when the content already contains
|
|
703
|
-
// anchors. Naive regex over the whole content would wrap
|
|
704
|
-
// `<a href="X">X</a>` in ANOTHER anchor (nested anchors are
|
|
705
|
-
// invalid HTML and browsers split them, producing visible
|
|
706
|
-
// junk). For HTML pastes that already have linked URLs (the
|
|
707
|
-
// common case from a browser address bar or another mail
|
|
708
|
-
// client), TinyMCE preserves them — no auto-link pass needed.
|
|
709
|
-
// For plain-text pastes (no anchors present), wrap any bare
|
|
710
|
-
// http(s)://… runs. Trailing sentence punctuation is excluded
|
|
711
|
-
// from the URL.
|
|
712
|
-
paste_preprocess: (_plugin, args) => {
|
|
713
|
-
if (/<a[\s>]/i.test(args.content))
|
|
714
|
-
return;
|
|
715
|
-
args.content = args.content.replace(/(^|[\s(\[])((?:https?|ftp):\/\/[^\s<>"']+[^\s<>"'.,;:!?)\]])/gi, (_m, lead, url) => `${lead}<a href="${url}">${url}</a>`);
|
|
716
|
-
},
|
|
717
|
-
// Body font + quoted-reply styling. Without explicit rules for
|
|
718
|
-
// <blockquote> and div.reply the editor iframe renders the
|
|
719
|
-
// quoted block with no visual distinction from the user's own
|
|
720
|
-
// text — pasted reply quotes vanish into a wall of unformatted
|
|
721
|
-
// paragraphs (Bob 2026-05-12: "the body losing all formatting").
|
|
722
|
-
// The styles below match Thunderbird/Outlook conventions: a
|
|
723
|
-
// muted left-bar + indent on blockquotes, slight color shift on
|
|
724
|
-
// the "On … wrote:" attribution, untouched typography for
|
|
725
|
-
// everything else so genuine HTML formatting (bold / italic /
|
|
726
|
-
// tables / inline color) still comes through verbatim.
|
|
727
|
-
content_style: [
|
|
728
|
-
// Dark-blue native caret bar. caret-shape:block produced a
|
|
729
|
-
// column wider than the gap between letters AND caused the
|
|
730
|
-
// caret to "scoot back to its old position" after an arrow
|
|
731
|
-
// press (Chromium block-caret quirk with the arrow-key
|
|
732
|
-
// selection adjustment, Bob 2026-05-24). Plain bar is
|
|
733
|
-
// browser-default and behaves correctly with arrow keys;
|
|
734
|
-
// just darken the color so it stays visible.
|
|
735
|
-
"body { font-family: system-ui, sans-serif; font-size: 14px; caret-color: #0a2647; }",
|
|
736
|
-
"blockquote { border-left: 3px solid #c0c8d0; margin: 0 0 0 4px; padding: 2px 0 2px 10px; color: #555; }",
|
|
737
|
-
"div.reply { margin-top: 0.5em; }",
|
|
738
|
-
"div.reply > p:first-child { color: #666; font-size: 0.95em; margin: 0 0 4px 0; }",
|
|
739
|
-
"pre, code { font-family: ui-monospace, Consolas, Menlo, monospace; }"
|
|
740
|
-
].join(" "),
|
|
741
|
-
init_instance_callback: (ed) => resolve(ed),
|
|
742
|
-
setup: (ed) => {
|
|
743
|
-
if (opts.initialHtml)
|
|
744
|
-
ed.on("init", () => ed.setContent(opts.initialHtml));
|
|
745
|
-
const ZOOM_DEFAULT = 14;
|
|
746
|
-
const ZOOM_STEP = 2;
|
|
747
|
-
const ZOOM_MIN = 8;
|
|
748
|
-
const ZOOM_MAX = 32;
|
|
749
|
-
const ZOOM_STORAGE_KEY = "rmf-tiny:zoom-px";
|
|
750
|
-
let zoomPx = ZOOM_DEFAULT;
|
|
751
|
-
try {
|
|
752
|
-
const stored = Number(localStorage.getItem(ZOOM_STORAGE_KEY));
|
|
753
|
-
if (Number.isFinite(stored) && stored >= ZOOM_MIN && stored <= ZOOM_MAX) {
|
|
754
|
-
zoomPx = stored;
|
|
755
|
-
}
|
|
756
|
-
} catch {
|
|
757
|
-
}
|
|
758
|
-
const saveZoom = () => {
|
|
759
|
-
try {
|
|
760
|
-
localStorage.setItem(ZOOM_STORAGE_KEY, String(zoomPx));
|
|
761
|
-
} catch {
|
|
762
|
-
}
|
|
763
|
-
};
|
|
764
|
-
const applyZoom = () => {
|
|
765
|
-
try {
|
|
766
|
-
const body = ed.getBody();
|
|
767
|
-
if (body)
|
|
768
|
-
body.style.fontSize = `${zoomPx}px`;
|
|
769
|
-
} catch {
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// node_modules/nspell/lib/util/affix.js
|
|
67
|
+
var require_affix = __commonJS({
|
|
68
|
+
"node_modules/nspell/lib/util/affix.js"(exports, module) {
|
|
69
|
+
"use strict";
|
|
70
|
+
var parse = require_rule_codes();
|
|
71
|
+
module.exports = affix;
|
|
72
|
+
var push = [].push;
|
|
73
|
+
var alphabet = "etaoinshrdlcumwfgypbvkjxqz".split("");
|
|
74
|
+
var whiteSpaceExpression = /\s+/;
|
|
75
|
+
var defaultKeyboardLayout = [
|
|
76
|
+
"qwertzuop",
|
|
77
|
+
"yxcvbnm",
|
|
78
|
+
"qaw",
|
|
79
|
+
"say",
|
|
80
|
+
"wse",
|
|
81
|
+
"dsx",
|
|
82
|
+
"sy",
|
|
83
|
+
"edr",
|
|
84
|
+
"fdc",
|
|
85
|
+
"dx",
|
|
86
|
+
"rft",
|
|
87
|
+
"gfv",
|
|
88
|
+
"fc",
|
|
89
|
+
"tgz",
|
|
90
|
+
"hgb",
|
|
91
|
+
"gv",
|
|
92
|
+
"zhu",
|
|
93
|
+
"jhn",
|
|
94
|
+
"hb",
|
|
95
|
+
"uji",
|
|
96
|
+
"kjm",
|
|
97
|
+
"jn",
|
|
98
|
+
"iko",
|
|
99
|
+
"lkm"
|
|
100
|
+
];
|
|
101
|
+
function affix(doc) {
|
|
102
|
+
var rules = /* @__PURE__ */ Object.create(null);
|
|
103
|
+
var compoundRuleCodes = /* @__PURE__ */ Object.create(null);
|
|
104
|
+
var flags = /* @__PURE__ */ Object.create(null);
|
|
105
|
+
var replacementTable = [];
|
|
106
|
+
var conversion = { in: [], out: [] };
|
|
107
|
+
var compoundRules = [];
|
|
108
|
+
var aff = doc.toString("utf8");
|
|
109
|
+
var lines = [];
|
|
110
|
+
var last = 0;
|
|
111
|
+
var index = aff.indexOf("\n");
|
|
112
|
+
var parts;
|
|
113
|
+
var line;
|
|
114
|
+
var ruleType;
|
|
115
|
+
var count;
|
|
116
|
+
var remove;
|
|
117
|
+
var add;
|
|
118
|
+
var source;
|
|
119
|
+
var entry;
|
|
120
|
+
var position;
|
|
121
|
+
var rule;
|
|
122
|
+
var value;
|
|
123
|
+
var offset;
|
|
124
|
+
var character;
|
|
125
|
+
flags.KEY = [];
|
|
126
|
+
while (index > -1) {
|
|
127
|
+
pushLine(aff.slice(last, index));
|
|
128
|
+
last = index + 1;
|
|
129
|
+
index = aff.indexOf("\n", last);
|
|
130
|
+
}
|
|
131
|
+
pushLine(aff.slice(last));
|
|
132
|
+
index = -1;
|
|
133
|
+
while (++index < lines.length) {
|
|
134
|
+
line = lines[index];
|
|
135
|
+
parts = line.split(whiteSpaceExpression);
|
|
136
|
+
ruleType = parts[0];
|
|
137
|
+
if (ruleType === "REP") {
|
|
138
|
+
count = index + parseInt(parts[1], 10);
|
|
139
|
+
while (++index <= count) {
|
|
140
|
+
parts = lines[index].split(whiteSpaceExpression);
|
|
141
|
+
replacementTable.push([parts[1], parts[2]]);
|
|
770
142
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
text: "Zoom in",
|
|
779
|
-
shortcut: "Ctrl+=",
|
|
780
|
-
onAction: () => bumpZoom(ZOOM_STEP)
|
|
781
|
-
});
|
|
782
|
-
ed.ui.registry.addMenuItem("zoomOut", {
|
|
783
|
-
text: "Zoom out",
|
|
784
|
-
shortcut: "Ctrl+-",
|
|
785
|
-
onAction: () => bumpZoom(-ZOOM_STEP)
|
|
786
|
-
});
|
|
787
|
-
ed.ui.registry.addMenuItem("zoomReset", {
|
|
788
|
-
text: "Reset zoom",
|
|
789
|
-
shortcut: "Ctrl+0",
|
|
790
|
-
onAction: () => {
|
|
791
|
-
zoomPx = ZOOM_DEFAULT;
|
|
792
|
-
applyZoom();
|
|
793
|
-
saveZoom();
|
|
143
|
+
index--;
|
|
144
|
+
} else if (ruleType === "ICONV" || ruleType === "OCONV") {
|
|
145
|
+
count = index + parseInt(parts[1], 10);
|
|
146
|
+
entry = conversion[ruleType === "ICONV" ? "in" : "out"];
|
|
147
|
+
while (++index <= count) {
|
|
148
|
+
parts = lines[index].split(whiteSpaceExpression);
|
|
149
|
+
entry.push([new RegExp(parts[1], "g"), parts[2]]);
|
|
794
150
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
151
|
+
index--;
|
|
152
|
+
} else if (ruleType === "COMPOUNDRULE") {
|
|
153
|
+
count = index + parseInt(parts[1], 10);
|
|
154
|
+
while (++index <= count) {
|
|
155
|
+
rule = lines[index].split(whiteSpaceExpression)[1];
|
|
156
|
+
position = -1;
|
|
157
|
+
compoundRules.push(rule);
|
|
158
|
+
while (++position < rule.length) {
|
|
159
|
+
compoundRuleCodes[rule.charAt(position)] = [];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
index--;
|
|
163
|
+
} else if (ruleType === "PFX" || ruleType === "SFX") {
|
|
164
|
+
count = index + parseInt(parts[3], 10);
|
|
165
|
+
rule = {
|
|
166
|
+
type: ruleType,
|
|
167
|
+
combineable: parts[2] === "Y",
|
|
168
|
+
entries: []
|
|
169
|
+
};
|
|
170
|
+
rules[parts[1]] = rule;
|
|
171
|
+
while (++index <= count) {
|
|
172
|
+
parts = lines[index].split(whiteSpaceExpression);
|
|
173
|
+
remove = parts[2];
|
|
174
|
+
add = parts[3].split("/");
|
|
175
|
+
source = parts[4];
|
|
176
|
+
entry = {
|
|
177
|
+
add: "",
|
|
178
|
+
remove: "",
|
|
179
|
+
match: "",
|
|
180
|
+
continuation: parse(flags, add[1])
|
|
181
|
+
};
|
|
182
|
+
if (add && add[0] !== "0") {
|
|
183
|
+
entry.add = add[0];
|
|
184
|
+
}
|
|
820
185
|
try {
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
186
|
+
if (remove !== "0") {
|
|
187
|
+
entry.remove = ruleType === "SFX" ? end(remove) : remove;
|
|
188
|
+
}
|
|
189
|
+
if (source && source !== ".") {
|
|
190
|
+
entry.match = ruleType === "SFX" ? end(source) : start(source);
|
|
191
|
+
}
|
|
192
|
+
} catch (_) {
|
|
193
|
+
entry = null;
|
|
825
194
|
}
|
|
826
|
-
if (
|
|
827
|
-
|
|
828
|
-
const after = doc.createRange();
|
|
829
|
-
after.setStartAfter(link);
|
|
830
|
-
after.collapse(true);
|
|
831
|
-
sel.removeAllRanges();
|
|
832
|
-
sel.addRange(after);
|
|
833
|
-
}, true);
|
|
834
|
-
};
|
|
835
|
-
ed.on("init", installLinkEscape);
|
|
836
|
-
ed.on("SetContent", installLinkEscape);
|
|
837
|
-
ed.on("init", () => {
|
|
838
|
-
try {
|
|
839
|
-
const body = ed.getBody();
|
|
840
|
-
if (body) {
|
|
841
|
-
body.setAttribute("spellcheck", "true");
|
|
842
|
-
body.setAttribute("lang", "en");
|
|
195
|
+
if (entry) {
|
|
196
|
+
rule.entries.push(entry);
|
|
843
197
|
}
|
|
844
|
-
const doc = ed.getDoc();
|
|
845
|
-
if (doc?.documentElement)
|
|
846
|
-
doc.documentElement.setAttribute("lang", "en");
|
|
847
|
-
} catch {
|
|
848
198
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
}
|
|
859
|
-
doc.addEventListener("keydown", (e) => {
|
|
860
|
-
if (!(e.ctrlKey || e.metaKey))
|
|
861
|
-
return;
|
|
862
|
-
if (e.key === "=" || e.key === "+") {
|
|
863
|
-
e.preventDefault();
|
|
864
|
-
e.stopPropagation();
|
|
865
|
-
bumpZoom(ZOOM_STEP);
|
|
866
|
-
} else if (e.key === "-") {
|
|
867
|
-
e.preventDefault();
|
|
868
|
-
e.stopPropagation();
|
|
869
|
-
bumpZoom(-ZOOM_STEP);
|
|
870
|
-
} else if (e.key === "0") {
|
|
871
|
-
e.preventDefault();
|
|
872
|
-
e.stopPropagation();
|
|
873
|
-
zoomPx = ZOOM_DEFAULT;
|
|
874
|
-
applyZoom();
|
|
875
|
-
}
|
|
876
|
-
}, true);
|
|
877
|
-
} catch {
|
|
199
|
+
index--;
|
|
200
|
+
} else if (ruleType === "TRY") {
|
|
201
|
+
source = parts[1];
|
|
202
|
+
offset = -1;
|
|
203
|
+
value = [];
|
|
204
|
+
while (++offset < source.length) {
|
|
205
|
+
character = source.charAt(offset);
|
|
206
|
+
if (character.toLowerCase() === character) {
|
|
207
|
+
value.push(character);
|
|
208
|
+
}
|
|
878
209
|
}
|
|
879
|
-
|
|
210
|
+
offset = -1;
|
|
211
|
+
while (++offset < alphabet.length) {
|
|
212
|
+
if (source.indexOf(alphabet[offset]) < 0) {
|
|
213
|
+
value.push(alphabet[offset]);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
flags[ruleType] = value;
|
|
217
|
+
} else if (ruleType === "KEY") {
|
|
218
|
+
push.apply(flags[ruleType], parts[1].split("|"));
|
|
219
|
+
} else if (ruleType === "COMPOUNDMIN") {
|
|
220
|
+
flags[ruleType] = Number(parts[1]);
|
|
221
|
+
} else if (ruleType === "ONLYINCOMPOUND") {
|
|
222
|
+
flags[ruleType] = parts[1];
|
|
223
|
+
compoundRuleCodes[parts[1]] = [];
|
|
224
|
+
} else if (ruleType === "FLAG" || ruleType === "KEEPCASE" || ruleType === "NOSUGGEST" || ruleType === "WORDCHARS") {
|
|
225
|
+
flags[ruleType] = parts[1];
|
|
226
|
+
} else {
|
|
227
|
+
flags[ruleType] = parts[1];
|
|
228
|
+
}
|
|
880
229
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
230
|
+
if (isNaN(flags.COMPOUNDMIN)) {
|
|
231
|
+
flags.COMPOUNDMIN = 3;
|
|
232
|
+
}
|
|
233
|
+
if (!flags.KEY.length) {
|
|
234
|
+
flags.KEY = defaultKeyboardLayout;
|
|
235
|
+
}
|
|
236
|
+
if (!flags.TRY) {
|
|
237
|
+
flags.TRY = alphabet.concat();
|
|
238
|
+
}
|
|
239
|
+
if (!flags.KEEPCASE) {
|
|
240
|
+
flags.KEEPCASE = false;
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
compoundRuleCodes,
|
|
244
|
+
replacementTable,
|
|
245
|
+
conversion,
|
|
246
|
+
compoundRules,
|
|
247
|
+
rules,
|
|
248
|
+
flags
|
|
249
|
+
};
|
|
250
|
+
function pushLine(line2) {
|
|
251
|
+
line2 = line2.trim();
|
|
252
|
+
if (line2 && line2.charCodeAt(0) !== 35) {
|
|
253
|
+
lines.push(line2);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function end(source) {
|
|
258
|
+
return new RegExp(source + "$");
|
|
259
|
+
}
|
|
260
|
+
function start(source) {
|
|
261
|
+
return new RegExp("^" + source);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// node_modules/nspell/lib/util/normalize.js
|
|
267
|
+
var require_normalize = __commonJS({
|
|
268
|
+
"node_modules/nspell/lib/util/normalize.js"(exports, module) {
|
|
269
|
+
"use strict";
|
|
270
|
+
module.exports = normalize;
|
|
271
|
+
function normalize(value, patterns) {
|
|
272
|
+
var index = -1;
|
|
273
|
+
while (++index < patterns.length) {
|
|
274
|
+
value = value.replace(patterns[index][0], patterns[index][1]);
|
|
275
|
+
}
|
|
276
|
+
return value;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// node_modules/nspell/lib/util/flag.js
|
|
282
|
+
var require_flag = __commonJS({
|
|
283
|
+
"node_modules/nspell/lib/util/flag.js"(exports, module) {
|
|
284
|
+
"use strict";
|
|
285
|
+
module.exports = flag;
|
|
286
|
+
function flag(values, value, flags) {
|
|
287
|
+
return flags && value in values && flags.indexOf(values[value]) > -1;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// node_modules/nspell/lib/util/exact.js
|
|
293
|
+
var require_exact = __commonJS({
|
|
294
|
+
"node_modules/nspell/lib/util/exact.js"(exports, module) {
|
|
295
|
+
"use strict";
|
|
296
|
+
var flag = require_flag();
|
|
297
|
+
module.exports = exact;
|
|
298
|
+
function exact(context, value) {
|
|
299
|
+
var index = -1;
|
|
300
|
+
if (context.data[value]) {
|
|
301
|
+
return !flag(context.flags, "ONLYINCOMPOUND", context.data[value]);
|
|
302
|
+
}
|
|
303
|
+
if (value.length >= context.flags.COMPOUNDMIN) {
|
|
304
|
+
while (++index < context.compoundRules.length) {
|
|
305
|
+
if (context.compoundRules[index].test(value)) {
|
|
306
|
+
return true;
|
|
906
307
|
}
|
|
907
|
-
editor2.focus();
|
|
908
|
-
editor2.getWin()?.scrollTo(0, 0);
|
|
909
|
-
} else {
|
|
910
|
-
editor2.selection.select(body, true);
|
|
911
|
-
editor2.selection.collapse(false);
|
|
912
|
-
editor2.focus();
|
|
913
|
-
editor2.selection.scrollIntoView();
|
|
914
308
|
}
|
|
915
|
-
};
|
|
916
|
-
try {
|
|
917
|
-
place();
|
|
918
|
-
editor2.getWin()?.requestAnimationFrame?.(() => {
|
|
919
|
-
try {
|
|
920
|
-
place();
|
|
921
|
-
} catch {
|
|
922
|
-
}
|
|
923
|
-
});
|
|
924
|
-
} catch {
|
|
925
309
|
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
editor2.off(event, handler);
|
|
935
|
-
},
|
|
936
|
-
nativeEditor: editor2
|
|
937
|
-
};
|
|
938
|
-
}
|
|
939
|
-
var init_rmf_tiny = __esm({
|
|
940
|
-
"client/lib/rmf-tiny.js"() {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// node_modules/nspell/lib/util/form.js
|
|
316
|
+
var require_form = __commonJS({
|
|
317
|
+
"node_modules/nspell/lib/util/form.js"(exports, module) {
|
|
941
318
|
"use strict";
|
|
319
|
+
var normalize = require_normalize();
|
|
320
|
+
var exact = require_exact();
|
|
321
|
+
var flag = require_flag();
|
|
322
|
+
module.exports = form;
|
|
323
|
+
function form(context, value, all) {
|
|
324
|
+
var normal = value.trim();
|
|
325
|
+
var alternative;
|
|
326
|
+
if (!normal) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
normal = normalize(normal, context.conversion.in);
|
|
330
|
+
if (exact(context, normal)) {
|
|
331
|
+
if (!all && flag(context.flags, "FORBIDDENWORD", context.data[normal])) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
return normal;
|
|
335
|
+
}
|
|
336
|
+
if (normal.toUpperCase() === normal) {
|
|
337
|
+
alternative = normal.charAt(0) + normal.slice(1).toLowerCase();
|
|
338
|
+
if (ignore(context.flags, context.data[alternative], all)) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
if (exact(context, alternative)) {
|
|
342
|
+
return alternative;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
alternative = normal.toLowerCase();
|
|
346
|
+
if (alternative !== normal) {
|
|
347
|
+
if (ignore(context.flags, context.data[alternative], all)) {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
if (exact(context, alternative)) {
|
|
351
|
+
return alternative;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
function ignore(flags, dict, all) {
|
|
357
|
+
return flag(flags, "KEEPCASE", dict) || all || flag(flags, "FORBIDDENWORD", dict);
|
|
358
|
+
}
|
|
942
359
|
}
|
|
943
360
|
});
|
|
944
361
|
|
|
945
|
-
// node_modules/
|
|
946
|
-
var
|
|
947
|
-
"node_modules/
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
362
|
+
// node_modules/nspell/lib/correct.js
|
|
363
|
+
var require_correct = __commonJS({
|
|
364
|
+
"node_modules/nspell/lib/correct.js"(exports, module) {
|
|
365
|
+
"use strict";
|
|
366
|
+
var form = require_form();
|
|
367
|
+
module.exports = correct;
|
|
368
|
+
function correct(value) {
|
|
369
|
+
return Boolean(form(this, value));
|
|
370
|
+
}
|
|
951
371
|
}
|
|
952
372
|
});
|
|
953
373
|
|
|
954
|
-
// node_modules/nspell/lib/util/
|
|
955
|
-
var
|
|
956
|
-
"node_modules/nspell/lib/util/
|
|
374
|
+
// node_modules/nspell/lib/util/casing.js
|
|
375
|
+
var require_casing = __commonJS({
|
|
376
|
+
"node_modules/nspell/lib/util/casing.js"(exports, module) {
|
|
957
377
|
"use strict";
|
|
958
|
-
module.exports =
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
var
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
378
|
+
module.exports = casing;
|
|
379
|
+
function casing(value) {
|
|
380
|
+
var head = exact(value.charAt(0));
|
|
381
|
+
var rest = value.slice(1);
|
|
382
|
+
if (!rest) {
|
|
383
|
+
return head;
|
|
384
|
+
}
|
|
385
|
+
rest = exact(rest);
|
|
386
|
+
if (head === rest) {
|
|
387
|
+
return head;
|
|
388
|
+
}
|
|
389
|
+
if (head === "u" && rest === "l") {
|
|
390
|
+
return "s";
|
|
391
|
+
}
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
function exact(value) {
|
|
395
|
+
return value === value.toLowerCase() ? "l" : value === value.toUpperCase() ? "u" : null;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// node_modules/nspell/lib/suggest.js
|
|
401
|
+
var require_suggest = __commonJS({
|
|
402
|
+
"node_modules/nspell/lib/suggest.js"(exports, module) {
|
|
403
|
+
"use strict";
|
|
404
|
+
var casing = require_casing();
|
|
405
|
+
var normalize = require_normalize();
|
|
406
|
+
var flag = require_flag();
|
|
407
|
+
var form = require_form();
|
|
408
|
+
module.exports = suggest;
|
|
409
|
+
var push = [].push;
|
|
410
|
+
function suggest(value) {
|
|
411
|
+
var self = this;
|
|
412
|
+
var charAdded = {};
|
|
413
|
+
var suggestions = [];
|
|
414
|
+
var weighted = {};
|
|
415
|
+
var memory;
|
|
416
|
+
var replacement;
|
|
417
|
+
var edits = [];
|
|
418
|
+
var values;
|
|
419
|
+
var index;
|
|
420
|
+
var offset;
|
|
421
|
+
var position;
|
|
422
|
+
var count;
|
|
423
|
+
var otherOffset;
|
|
424
|
+
var otherCharacter;
|
|
425
|
+
var character;
|
|
426
|
+
var group;
|
|
427
|
+
var before;
|
|
428
|
+
var after;
|
|
429
|
+
var upper;
|
|
430
|
+
var insensitive;
|
|
431
|
+
var firstLevel;
|
|
432
|
+
var previous;
|
|
433
|
+
var next;
|
|
434
|
+
var nextCharacter;
|
|
435
|
+
var max;
|
|
436
|
+
var distance;
|
|
437
|
+
var size;
|
|
438
|
+
var normalized;
|
|
439
|
+
var suggestion;
|
|
440
|
+
var currentCase;
|
|
441
|
+
value = normalize(value.trim(), self.conversion.in);
|
|
442
|
+
if (!value || self.correct(value)) {
|
|
443
|
+
return [];
|
|
444
|
+
}
|
|
445
|
+
currentCase = casing(value);
|
|
446
|
+
index = -1;
|
|
447
|
+
while (++index < self.replacementTable.length) {
|
|
448
|
+
replacement = self.replacementTable[index];
|
|
449
|
+
offset = value.indexOf(replacement[0]);
|
|
450
|
+
while (offset > -1) {
|
|
451
|
+
edits.push(value.replace(replacement[0], replacement[1]));
|
|
452
|
+
offset = value.indexOf(replacement[0], offset + 1);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
index = -1;
|
|
456
|
+
while (++index < value.length) {
|
|
457
|
+
character = value.charAt(index);
|
|
458
|
+
before = value.slice(0, index);
|
|
459
|
+
after = value.slice(index + 1);
|
|
460
|
+
insensitive = character.toLowerCase();
|
|
461
|
+
upper = insensitive !== character;
|
|
462
|
+
charAdded = {};
|
|
463
|
+
offset = -1;
|
|
464
|
+
while (++offset < self.flags.KEY.length) {
|
|
465
|
+
group = self.flags.KEY[offset];
|
|
466
|
+
position = group.indexOf(insensitive);
|
|
467
|
+
if (position < 0) {
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
otherOffset = -1;
|
|
471
|
+
while (++otherOffset < group.length) {
|
|
472
|
+
if (otherOffset !== position) {
|
|
473
|
+
otherCharacter = group.charAt(otherOffset);
|
|
474
|
+
if (charAdded[otherCharacter]) {
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
charAdded[otherCharacter] = true;
|
|
478
|
+
if (upper) {
|
|
479
|
+
otherCharacter = otherCharacter.toUpperCase();
|
|
480
|
+
}
|
|
481
|
+
edits.push(before + otherCharacter + after);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
index = -1;
|
|
487
|
+
nextCharacter = value.charAt(0);
|
|
488
|
+
values = [""];
|
|
489
|
+
max = 1;
|
|
490
|
+
distance = 0;
|
|
491
|
+
while (++index < value.length) {
|
|
492
|
+
character = nextCharacter;
|
|
493
|
+
nextCharacter = value.charAt(index + 1);
|
|
494
|
+
before = value.slice(0, index);
|
|
495
|
+
replacement = character === nextCharacter ? "" : character + character;
|
|
496
|
+
offset = -1;
|
|
497
|
+
count = values.length;
|
|
498
|
+
while (++offset < count) {
|
|
499
|
+
if (offset <= max) {
|
|
500
|
+
values.push(values[offset] + replacement);
|
|
501
|
+
}
|
|
502
|
+
values[offset] += character;
|
|
503
|
+
}
|
|
504
|
+
if (++distance < 3) {
|
|
505
|
+
max = values.length;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
push.apply(edits, values);
|
|
509
|
+
values = [value];
|
|
510
|
+
replacement = value.toLowerCase();
|
|
511
|
+
if (value === replacement || currentCase === null) {
|
|
512
|
+
values.push(value.charAt(0).toUpperCase() + replacement.slice(1));
|
|
513
|
+
}
|
|
514
|
+
replacement = value.toUpperCase();
|
|
515
|
+
if (value !== replacement) {
|
|
516
|
+
values.push(replacement);
|
|
517
|
+
}
|
|
518
|
+
memory = {
|
|
519
|
+
state: {},
|
|
520
|
+
weighted,
|
|
521
|
+
suggestions
|
|
522
|
+
};
|
|
523
|
+
firstLevel = generate(self, memory, values, edits);
|
|
524
|
+
previous = 0;
|
|
525
|
+
max = Math.min(firstLevel.length, Math.pow(Math.max(15 - value.length, 3), 3));
|
|
526
|
+
size = Math.max(Math.pow(10 - value.length, 3), 1);
|
|
527
|
+
while (!suggestions.length && previous < max) {
|
|
528
|
+
next = previous + size;
|
|
529
|
+
generate(self, memory, firstLevel.slice(previous, next));
|
|
530
|
+
previous = next;
|
|
531
|
+
}
|
|
532
|
+
suggestions.sort(sort);
|
|
533
|
+
values = [];
|
|
534
|
+
normalized = [];
|
|
535
|
+
index = -1;
|
|
536
|
+
while (++index < suggestions.length) {
|
|
537
|
+
suggestion = normalize(suggestions[index], self.conversion.out);
|
|
538
|
+
replacement = suggestion.toLowerCase();
|
|
539
|
+
if (normalized.indexOf(replacement) < 0) {
|
|
540
|
+
values.push(suggestion);
|
|
541
|
+
normalized.push(replacement);
|
|
969
542
|
}
|
|
970
|
-
return result;
|
|
971
543
|
}
|
|
972
|
-
return
|
|
544
|
+
return values;
|
|
545
|
+
function sort(a, b) {
|
|
546
|
+
return sortWeight(a, b) || sortCasing(a, b) || sortAlpha(a, b);
|
|
547
|
+
}
|
|
548
|
+
function sortWeight(a, b) {
|
|
549
|
+
return weighted[a] === weighted[b] ? 0 : weighted[a] > weighted[b] ? -1 : 1;
|
|
550
|
+
}
|
|
551
|
+
function sortCasing(a, b) {
|
|
552
|
+
var leftCasing = casing(a);
|
|
553
|
+
var rightCasing = casing(b);
|
|
554
|
+
return leftCasing === rightCasing ? 0 : leftCasing === currentCase ? -1 : rightCasing === currentCase ? 1 : void 0;
|
|
555
|
+
}
|
|
556
|
+
function sortAlpha(a, b) {
|
|
557
|
+
return a.localeCompare(b);
|
|
558
|
+
}
|
|
973
559
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
var
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
"yxcvbnm",
|
|
989
|
-
"qaw",
|
|
990
|
-
"say",
|
|
991
|
-
"wse",
|
|
992
|
-
"dsx",
|
|
993
|
-
"sy",
|
|
994
|
-
"edr",
|
|
995
|
-
"fdc",
|
|
996
|
-
"dx",
|
|
997
|
-
"rft",
|
|
998
|
-
"gfv",
|
|
999
|
-
"fc",
|
|
1000
|
-
"tgz",
|
|
1001
|
-
"hgb",
|
|
1002
|
-
"gv",
|
|
1003
|
-
"zhu",
|
|
1004
|
-
"jhn",
|
|
1005
|
-
"hb",
|
|
1006
|
-
"uji",
|
|
1007
|
-
"kjm",
|
|
1008
|
-
"jn",
|
|
1009
|
-
"iko",
|
|
1010
|
-
"lkm"
|
|
1011
|
-
];
|
|
1012
|
-
function affix(doc) {
|
|
1013
|
-
var rules = /* @__PURE__ */ Object.create(null);
|
|
1014
|
-
var compoundRuleCodes = /* @__PURE__ */ Object.create(null);
|
|
1015
|
-
var flags = /* @__PURE__ */ Object.create(null);
|
|
1016
|
-
var replacementTable = [];
|
|
1017
|
-
var conversion = { in: [], out: [] };
|
|
1018
|
-
var compoundRules = [];
|
|
1019
|
-
var aff = doc.toString("utf8");
|
|
1020
|
-
var lines = [];
|
|
1021
|
-
var last = 0;
|
|
1022
|
-
var index = aff.indexOf("\n");
|
|
1023
|
-
var parts;
|
|
1024
|
-
var line;
|
|
1025
|
-
var ruleType;
|
|
1026
|
-
var count;
|
|
1027
|
-
var remove;
|
|
1028
|
-
var add;
|
|
1029
|
-
var source;
|
|
1030
|
-
var entry;
|
|
560
|
+
function generate(context, memory, words, edits) {
|
|
561
|
+
var characters = context.flags.TRY;
|
|
562
|
+
var data = context.data;
|
|
563
|
+
var flags = context.flags;
|
|
564
|
+
var result = [];
|
|
565
|
+
var index = -1;
|
|
566
|
+
var word;
|
|
567
|
+
var before;
|
|
568
|
+
var character;
|
|
569
|
+
var nextCharacter;
|
|
570
|
+
var nextAfter;
|
|
571
|
+
var nextNextAfter;
|
|
572
|
+
var nextUpper;
|
|
573
|
+
var currentCase;
|
|
1031
574
|
var position;
|
|
1032
|
-
var
|
|
1033
|
-
var
|
|
575
|
+
var after;
|
|
576
|
+
var upper;
|
|
577
|
+
var inject;
|
|
1034
578
|
var offset;
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
last = index + 1;
|
|
1040
|
-
index = aff.indexOf("\n", last);
|
|
579
|
+
if (edits) {
|
|
580
|
+
while (++index < edits.length) {
|
|
581
|
+
check(edits[index], true);
|
|
582
|
+
}
|
|
1041
583
|
}
|
|
1042
|
-
pushLine(aff.slice(last));
|
|
1043
584
|
index = -1;
|
|
1044
|
-
while (++index <
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
count = index + parseInt(parts[1], 10);
|
|
1065
|
-
while (++index <= count) {
|
|
1066
|
-
rule = lines[index].split(whiteSpaceExpression)[1];
|
|
1067
|
-
position = -1;
|
|
1068
|
-
compoundRules.push(rule);
|
|
1069
|
-
while (++position < rule.length) {
|
|
1070
|
-
compoundRuleCodes[rule.charAt(position)] = [];
|
|
1071
|
-
}
|
|
585
|
+
while (++index < words.length) {
|
|
586
|
+
word = words[index];
|
|
587
|
+
before = "";
|
|
588
|
+
character = "";
|
|
589
|
+
nextCharacter = word.charAt(0);
|
|
590
|
+
nextAfter = word;
|
|
591
|
+
nextNextAfter = word.slice(1);
|
|
592
|
+
nextUpper = nextCharacter.toLowerCase() !== nextCharacter;
|
|
593
|
+
currentCase = casing(word);
|
|
594
|
+
position = -1;
|
|
595
|
+
while (++position <= word.length) {
|
|
596
|
+
before += character;
|
|
597
|
+
after = nextAfter;
|
|
598
|
+
nextAfter = nextNextAfter;
|
|
599
|
+
nextNextAfter = nextAfter.slice(1);
|
|
600
|
+
character = nextCharacter;
|
|
601
|
+
nextCharacter = word.charAt(position + 1);
|
|
602
|
+
upper = nextUpper;
|
|
603
|
+
if (nextCharacter) {
|
|
604
|
+
nextUpper = nextCharacter.toLowerCase() !== nextCharacter;
|
|
1072
605
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
combineable: parts[2] === "Y",
|
|
1079
|
-
entries: []
|
|
1080
|
-
};
|
|
1081
|
-
rules[parts[1]] = rule;
|
|
1082
|
-
while (++index <= count) {
|
|
1083
|
-
parts = lines[index].split(whiteSpaceExpression);
|
|
1084
|
-
remove = parts[2];
|
|
1085
|
-
add = parts[3].split("/");
|
|
1086
|
-
source = parts[4];
|
|
1087
|
-
entry = {
|
|
1088
|
-
add: "",
|
|
1089
|
-
remove: "",
|
|
1090
|
-
match: "",
|
|
1091
|
-
continuation: parse(flags, add[1])
|
|
1092
|
-
};
|
|
1093
|
-
if (add && add[0] !== "0") {
|
|
1094
|
-
entry.add = add[0];
|
|
1095
|
-
}
|
|
1096
|
-
try {
|
|
1097
|
-
if (remove !== "0") {
|
|
1098
|
-
entry.remove = ruleType === "SFX" ? end(remove) : remove;
|
|
1099
|
-
}
|
|
1100
|
-
if (source && source !== ".") {
|
|
1101
|
-
entry.match = ruleType === "SFX" ? end(source) : start(source);
|
|
1102
|
-
}
|
|
1103
|
-
} catch (_) {
|
|
1104
|
-
entry = null;
|
|
1105
|
-
}
|
|
1106
|
-
if (entry) {
|
|
1107
|
-
rule.entries.push(entry);
|
|
1108
|
-
}
|
|
606
|
+
if (nextAfter && upper !== nextUpper) {
|
|
607
|
+
check(before + switchCase(nextAfter));
|
|
608
|
+
check(
|
|
609
|
+
before + switchCase(nextCharacter) + switchCase(character) + nextNextAfter
|
|
610
|
+
);
|
|
1109
611
|
}
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
offset = -1;
|
|
1114
|
-
value = [];
|
|
1115
|
-
while (++offset < source.length) {
|
|
1116
|
-
character = source.charAt(offset);
|
|
1117
|
-
if (character.toLowerCase() === character) {
|
|
1118
|
-
value.push(character);
|
|
1119
|
-
}
|
|
612
|
+
check(before + nextAfter);
|
|
613
|
+
if (nextAfter) {
|
|
614
|
+
check(before + nextCharacter + character + nextNextAfter);
|
|
1120
615
|
}
|
|
1121
616
|
offset = -1;
|
|
1122
|
-
while (++offset <
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
} else {
|
|
1138
|
-
flags[ruleType] = parts[1];
|
|
617
|
+
while (++offset < characters.length) {
|
|
618
|
+
inject = characters[offset];
|
|
619
|
+
if (upper && inject !== inject.toUpperCase()) {
|
|
620
|
+
if (currentCase !== "s") {
|
|
621
|
+
check(before + inject + after);
|
|
622
|
+
check(before + inject + nextAfter);
|
|
623
|
+
}
|
|
624
|
+
inject = inject.toUpperCase();
|
|
625
|
+
check(before + inject + after);
|
|
626
|
+
check(before + inject + nextAfter);
|
|
627
|
+
} else {
|
|
628
|
+
check(before + inject + after);
|
|
629
|
+
check(before + inject + nextAfter);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
1139
632
|
}
|
|
1140
633
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
compoundRules,
|
|
1158
|
-
rules,
|
|
1159
|
-
flags
|
|
1160
|
-
};
|
|
1161
|
-
function pushLine(line2) {
|
|
1162
|
-
line2 = line2.trim();
|
|
1163
|
-
if (line2 && line2.charCodeAt(0) !== 35) {
|
|
1164
|
-
lines.push(line2);
|
|
634
|
+
return result;
|
|
635
|
+
function check(value, double) {
|
|
636
|
+
var state = memory.state[value];
|
|
637
|
+
var corrected;
|
|
638
|
+
if (state !== Boolean(state)) {
|
|
639
|
+
result.push(value);
|
|
640
|
+
corrected = form(context, value);
|
|
641
|
+
state = corrected && !flag(flags, "NOSUGGEST", data[corrected]);
|
|
642
|
+
memory.state[value] = state;
|
|
643
|
+
if (state) {
|
|
644
|
+
memory.weighted[value] = double ? 10 : 0;
|
|
645
|
+
memory.suggestions.push(value);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (state) {
|
|
649
|
+
memory.weighted[value]++;
|
|
1165
650
|
}
|
|
1166
651
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
}
|
|
1171
|
-
function start(source) {
|
|
1172
|
-
return new RegExp("^" + source);
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
});
|
|
1176
|
-
|
|
1177
|
-
// node_modules/nspell/lib/util/normalize.js
|
|
1178
|
-
var require_normalize = __commonJS({
|
|
1179
|
-
"node_modules/nspell/lib/util/normalize.js"(exports, module) {
|
|
1180
|
-
"use strict";
|
|
1181
|
-
module.exports = normalize;
|
|
1182
|
-
function normalize(value, patterns) {
|
|
1183
|
-
var index = -1;
|
|
1184
|
-
while (++index < patterns.length) {
|
|
1185
|
-
value = value.replace(patterns[index][0], patterns[index][1]);
|
|
652
|
+
function switchCase(fragment) {
|
|
653
|
+
var first = fragment.charAt(0);
|
|
654
|
+
return (first.toLowerCase() === first ? first.toUpperCase() : first.toLowerCase()) + fragment.slice(1);
|
|
1186
655
|
}
|
|
1187
|
-
return value;
|
|
1188
656
|
}
|
|
1189
657
|
}
|
|
1190
658
|
});
|
|
1191
659
|
|
|
1192
|
-
// node_modules/nspell/lib/
|
|
1193
|
-
var
|
|
1194
|
-
"node_modules/nspell/lib/
|
|
660
|
+
// node_modules/nspell/lib/spell.js
|
|
661
|
+
var require_spell = __commonJS({
|
|
662
|
+
"node_modules/nspell/lib/spell.js"(exports, module) {
|
|
1195
663
|
"use strict";
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
664
|
+
var form = require_form();
|
|
665
|
+
var flag = require_flag();
|
|
666
|
+
module.exports = spell;
|
|
667
|
+
function spell(word) {
|
|
668
|
+
var self = this;
|
|
669
|
+
var value = form(self, word, true);
|
|
670
|
+
return {
|
|
671
|
+
correct: self.correct(word),
|
|
672
|
+
forbidden: Boolean(
|
|
673
|
+
value && flag(self.flags, "FORBIDDENWORD", self.data[value])
|
|
674
|
+
),
|
|
675
|
+
warn: Boolean(value && flag(self.flags, "WARN", self.data[value]))
|
|
676
|
+
};
|
|
1199
677
|
}
|
|
1200
678
|
}
|
|
1201
679
|
});
|
|
1202
680
|
|
|
1203
|
-
// node_modules/nspell/lib/util/
|
|
1204
|
-
var
|
|
1205
|
-
"node_modules/nspell/lib/util/
|
|
681
|
+
// node_modules/nspell/lib/util/apply.js
|
|
682
|
+
var require_apply = __commonJS({
|
|
683
|
+
"node_modules/nspell/lib/util/apply.js"(exports, module) {
|
|
1206
684
|
"use strict";
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
function exact(context, value) {
|
|
685
|
+
module.exports = apply;
|
|
686
|
+
function apply(value, rule, rules, words) {
|
|
1210
687
|
var index = -1;
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
688
|
+
var entry;
|
|
689
|
+
var next;
|
|
690
|
+
var continuationRule;
|
|
691
|
+
var continuation;
|
|
692
|
+
var position;
|
|
693
|
+
while (++index < rule.entries.length) {
|
|
694
|
+
entry = rule.entries[index];
|
|
695
|
+
continuation = entry.continuation;
|
|
696
|
+
position = -1;
|
|
697
|
+
if (!entry.match || entry.match.test(value)) {
|
|
698
|
+
next = entry.remove ? value.replace(entry.remove, "") : value;
|
|
699
|
+
next = rule.type === "SFX" ? next + entry.add : entry.add + next;
|
|
700
|
+
words.push(next);
|
|
701
|
+
if (continuation && continuation.length) {
|
|
702
|
+
while (++position < continuation.length) {
|
|
703
|
+
continuationRule = rules[continuation[position]];
|
|
704
|
+
if (continuationRule) {
|
|
705
|
+
apply(next, continuationRule, rules, words);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
1218
708
|
}
|
|
1219
709
|
}
|
|
1220
710
|
}
|
|
1221
|
-
return
|
|
711
|
+
return words;
|
|
1222
712
|
}
|
|
1223
713
|
}
|
|
1224
714
|
});
|
|
1225
715
|
|
|
1226
|
-
// node_modules/nspell/lib/util/
|
|
1227
|
-
var
|
|
1228
|
-
"node_modules/nspell/lib/util/
|
|
716
|
+
// node_modules/nspell/lib/util/add.js
|
|
717
|
+
var require_add = __commonJS({
|
|
718
|
+
"node_modules/nspell/lib/util/add.js"(exports, module) {
|
|
1229
719
|
"use strict";
|
|
1230
|
-
var
|
|
1231
|
-
|
|
1232
|
-
var
|
|
1233
|
-
|
|
1234
|
-
function
|
|
1235
|
-
var
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
if (exact(context, normal)) {
|
|
1242
|
-
if (!all && flag(context.flags, "FORBIDDENWORD", context.data[normal])) {
|
|
1243
|
-
return null;
|
|
720
|
+
var apply = require_apply();
|
|
721
|
+
module.exports = add;
|
|
722
|
+
var push = [].push;
|
|
723
|
+
var NO_RULES = [];
|
|
724
|
+
function addRules(dict, word, rules) {
|
|
725
|
+
var curr = dict[word];
|
|
726
|
+
if (word in dict) {
|
|
727
|
+
if (curr === NO_RULES) {
|
|
728
|
+
dict[word] = rules.concat();
|
|
729
|
+
} else {
|
|
730
|
+
push.apply(curr, rules);
|
|
1244
731
|
}
|
|
1245
|
-
|
|
732
|
+
} else {
|
|
733
|
+
dict[word] = rules.concat();
|
|
1246
734
|
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
735
|
+
}
|
|
736
|
+
function add(dict, word, codes, options) {
|
|
737
|
+
var position = -1;
|
|
738
|
+
var rule;
|
|
739
|
+
var offset;
|
|
740
|
+
var subposition;
|
|
741
|
+
var suboffset;
|
|
742
|
+
var combined;
|
|
743
|
+
var newWords;
|
|
744
|
+
var otherNewWords;
|
|
745
|
+
if (!("NEEDAFFIX" in options.flags) || codes.indexOf(options.flags.NEEDAFFIX) < 0) {
|
|
746
|
+
addRules(dict, word, codes);
|
|
1255
747
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
if (
|
|
1259
|
-
|
|
748
|
+
while (++position < codes.length) {
|
|
749
|
+
rule = options.rules[codes[position]];
|
|
750
|
+
if (codes[position] in options.compoundRuleCodes) {
|
|
751
|
+
options.compoundRuleCodes[codes[position]].push(word);
|
|
1260
752
|
}
|
|
1261
|
-
if (
|
|
1262
|
-
|
|
753
|
+
if (rule) {
|
|
754
|
+
newWords = apply(word, rule, options.rules, []);
|
|
755
|
+
offset = -1;
|
|
756
|
+
while (++offset < newWords.length) {
|
|
757
|
+
if (!(newWords[offset] in dict)) {
|
|
758
|
+
dict[newWords[offset]] = NO_RULES;
|
|
759
|
+
}
|
|
760
|
+
if (rule.combineable) {
|
|
761
|
+
subposition = position;
|
|
762
|
+
while (++subposition < codes.length) {
|
|
763
|
+
combined = options.rules[codes[subposition]];
|
|
764
|
+
if (combined && combined.combineable && rule.type !== combined.type) {
|
|
765
|
+
otherNewWords = apply(
|
|
766
|
+
newWords[offset],
|
|
767
|
+
combined,
|
|
768
|
+
options.rules,
|
|
769
|
+
[]
|
|
770
|
+
);
|
|
771
|
+
suboffset = -1;
|
|
772
|
+
while (++suboffset < otherNewWords.length) {
|
|
773
|
+
if (!(otherNewWords[suboffset] in dict)) {
|
|
774
|
+
dict[otherNewWords[suboffset]] = NO_RULES;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
1263
781
|
}
|
|
1264
782
|
}
|
|
1265
|
-
return null;
|
|
1266
783
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
// node_modules/nspell/lib/add.js
|
|
788
|
+
var require_add2 = __commonJS({
|
|
789
|
+
"node_modules/nspell/lib/add.js"(exports, module) {
|
|
790
|
+
"use strict";
|
|
791
|
+
var push = require_add();
|
|
792
|
+
module.exports = add;
|
|
793
|
+
var NO_CODES = [];
|
|
794
|
+
function add(value, model) {
|
|
795
|
+
var self = this;
|
|
796
|
+
push(self.data, value, self.data[model] || NO_CODES, self);
|
|
797
|
+
return self;
|
|
1269
798
|
}
|
|
1270
799
|
}
|
|
1271
800
|
});
|
|
1272
801
|
|
|
1273
|
-
// node_modules/nspell/lib/
|
|
1274
|
-
var
|
|
1275
|
-
"node_modules/nspell/lib/
|
|
802
|
+
// node_modules/nspell/lib/remove.js
|
|
803
|
+
var require_remove = __commonJS({
|
|
804
|
+
"node_modules/nspell/lib/remove.js"(exports, module) {
|
|
805
|
+
"use strict";
|
|
806
|
+
module.exports = remove;
|
|
807
|
+
function remove(value) {
|
|
808
|
+
var self = this;
|
|
809
|
+
delete self.data[value];
|
|
810
|
+
return self;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
// node_modules/nspell/lib/word-characters.js
|
|
816
|
+
var require_word_characters = __commonJS({
|
|
817
|
+
"node_modules/nspell/lib/word-characters.js"(exports, module) {
|
|
1276
818
|
"use strict";
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
return Boolean(form(this, value));
|
|
819
|
+
module.exports = wordCharacters;
|
|
820
|
+
function wordCharacters() {
|
|
821
|
+
return this.flags.WORDCHARS || null;
|
|
1281
822
|
}
|
|
1282
823
|
}
|
|
1283
824
|
});
|
|
1284
825
|
|
|
1285
|
-
// node_modules/nspell/lib/util/
|
|
1286
|
-
var
|
|
1287
|
-
"node_modules/nspell/lib/util/
|
|
826
|
+
// node_modules/nspell/lib/util/dictionary.js
|
|
827
|
+
var require_dictionary = __commonJS({
|
|
828
|
+
"node_modules/nspell/lib/util/dictionary.js"(exports, module) {
|
|
1288
829
|
"use strict";
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
830
|
+
var parseCodes = require_rule_codes();
|
|
831
|
+
var add = require_add();
|
|
832
|
+
module.exports = parse;
|
|
833
|
+
var whiteSpaceExpression = /\s/g;
|
|
834
|
+
function parse(buf, options, dict) {
|
|
835
|
+
var value = buf.toString("utf8");
|
|
836
|
+
var last = value.indexOf("\n") + 1;
|
|
837
|
+
var index = value.indexOf("\n", last);
|
|
838
|
+
while (index > -1) {
|
|
839
|
+
if (value.charCodeAt(last) !== 9) {
|
|
840
|
+
parseLine(value.slice(last, index), options, dict);
|
|
841
|
+
}
|
|
842
|
+
last = index + 1;
|
|
843
|
+
index = value.indexOf("\n", last);
|
|
1295
844
|
}
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
845
|
+
parseLine(value.slice(last), options, dict);
|
|
846
|
+
}
|
|
847
|
+
function parseLine(line, options, dict) {
|
|
848
|
+
var slashOffset = line.indexOf("/");
|
|
849
|
+
var hashOffset = line.indexOf("#");
|
|
850
|
+
var codes = "";
|
|
851
|
+
var word;
|
|
852
|
+
var result;
|
|
853
|
+
while (slashOffset > -1 && line.charCodeAt(slashOffset - 1) === 92) {
|
|
854
|
+
line = line.slice(0, slashOffset - 1) + line.slice(slashOffset);
|
|
855
|
+
slashOffset = line.indexOf("/", slashOffset);
|
|
1299
856
|
}
|
|
1300
|
-
if (
|
|
1301
|
-
|
|
857
|
+
if (hashOffset > -1) {
|
|
858
|
+
if (slashOffset > -1 && slashOffset < hashOffset) {
|
|
859
|
+
word = line.slice(0, slashOffset);
|
|
860
|
+
whiteSpaceExpression.lastIndex = slashOffset + 1;
|
|
861
|
+
result = whiteSpaceExpression.exec(line);
|
|
862
|
+
codes = line.slice(slashOffset + 1, result ? result.index : void 0);
|
|
863
|
+
} else {
|
|
864
|
+
word = line.slice(0, hashOffset);
|
|
865
|
+
}
|
|
866
|
+
} else if (slashOffset > -1) {
|
|
867
|
+
word = line.slice(0, slashOffset);
|
|
868
|
+
codes = line.slice(slashOffset + 1);
|
|
869
|
+
} else {
|
|
870
|
+
word = line;
|
|
871
|
+
}
|
|
872
|
+
word = word.trim();
|
|
873
|
+
if (word) {
|
|
874
|
+
add(dict, word, parseCodes(options.flags, codes.trim()), options);
|
|
1302
875
|
}
|
|
1303
|
-
return null;
|
|
1304
|
-
}
|
|
1305
|
-
function exact(value) {
|
|
1306
|
-
return value === value.toLowerCase() ? "l" : value === value.toUpperCase() ? "u" : null;
|
|
1307
876
|
}
|
|
1308
877
|
}
|
|
1309
878
|
});
|
|
1310
879
|
|
|
1311
|
-
// node_modules/nspell/lib/
|
|
1312
|
-
var
|
|
1313
|
-
"node_modules/nspell/lib/
|
|
880
|
+
// node_modules/nspell/lib/dictionary.js
|
|
881
|
+
var require_dictionary2 = __commonJS({
|
|
882
|
+
"node_modules/nspell/lib/dictionary.js"(exports, module) {
|
|
1314
883
|
"use strict";
|
|
1315
|
-
var
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
var form = require_form();
|
|
1319
|
-
module.exports = suggest;
|
|
1320
|
-
var push = [].push;
|
|
1321
|
-
function suggest(value) {
|
|
884
|
+
var parse = require_dictionary();
|
|
885
|
+
module.exports = add;
|
|
886
|
+
function add(buf) {
|
|
1322
887
|
var self = this;
|
|
1323
|
-
var
|
|
1324
|
-
var
|
|
1325
|
-
var
|
|
1326
|
-
var memory;
|
|
1327
|
-
var replacement;
|
|
1328
|
-
var edits = [];
|
|
1329
|
-
var values;
|
|
1330
|
-
var index;
|
|
1331
|
-
var offset;
|
|
1332
|
-
var position;
|
|
1333
|
-
var count;
|
|
1334
|
-
var otherOffset;
|
|
1335
|
-
var otherCharacter;
|
|
888
|
+
var index = -1;
|
|
889
|
+
var rule;
|
|
890
|
+
var source;
|
|
1336
891
|
var character;
|
|
1337
|
-
var
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
var firstLevel;
|
|
1343
|
-
var previous;
|
|
1344
|
-
var next;
|
|
1345
|
-
var nextCharacter;
|
|
1346
|
-
var max;
|
|
1347
|
-
var distance;
|
|
1348
|
-
var size;
|
|
1349
|
-
var normalized;
|
|
1350
|
-
var suggestion;
|
|
1351
|
-
var currentCase;
|
|
1352
|
-
value = normalize(value.trim(), self.conversion.in);
|
|
1353
|
-
if (!value || self.correct(value)) {
|
|
1354
|
-
return [];
|
|
1355
|
-
}
|
|
1356
|
-
currentCase = casing(value);
|
|
1357
|
-
index = -1;
|
|
1358
|
-
while (++index < self.replacementTable.length) {
|
|
1359
|
-
replacement = self.replacementTable[index];
|
|
1360
|
-
offset = value.indexOf(replacement[0]);
|
|
1361
|
-
while (offset > -1) {
|
|
1362
|
-
edits.push(value.replace(replacement[0], replacement[1]));
|
|
1363
|
-
offset = value.indexOf(replacement[0], offset + 1);
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
index = -1;
|
|
1367
|
-
while (++index < value.length) {
|
|
1368
|
-
character = value.charAt(index);
|
|
1369
|
-
before = value.slice(0, index);
|
|
1370
|
-
after = value.slice(index + 1);
|
|
1371
|
-
insensitive = character.toLowerCase();
|
|
1372
|
-
upper = insensitive !== character;
|
|
1373
|
-
charAdded = {};
|
|
892
|
+
var offset;
|
|
893
|
+
parse(buf, self, self.data);
|
|
894
|
+
while (++index < self.compoundRules.length) {
|
|
895
|
+
rule = self.compoundRules[index];
|
|
896
|
+
source = "";
|
|
1374
897
|
offset = -1;
|
|
1375
|
-
while (++offset <
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
if (position < 0) {
|
|
1379
|
-
continue;
|
|
1380
|
-
}
|
|
1381
|
-
otherOffset = -1;
|
|
1382
|
-
while (++otherOffset < group.length) {
|
|
1383
|
-
if (otherOffset !== position) {
|
|
1384
|
-
otherCharacter = group.charAt(otherOffset);
|
|
1385
|
-
if (charAdded[otherCharacter]) {
|
|
1386
|
-
continue;
|
|
1387
|
-
}
|
|
1388
|
-
charAdded[otherCharacter] = true;
|
|
1389
|
-
if (upper) {
|
|
1390
|
-
otherCharacter = otherCharacter.toUpperCase();
|
|
1391
|
-
}
|
|
1392
|
-
edits.push(before + otherCharacter + after);
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
898
|
+
while (++offset < rule.length) {
|
|
899
|
+
character = rule.charAt(offset);
|
|
900
|
+
source += self.compoundRuleCodes[character].length ? "(?:" + self.compoundRuleCodes[character].join("|") + ")" : character;
|
|
1395
901
|
}
|
|
902
|
+
self.compoundRules[index] = new RegExp(source, "i");
|
|
1396
903
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
904
|
+
return self;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
// node_modules/nspell/lib/personal.js
|
|
910
|
+
var require_personal = __commonJS({
|
|
911
|
+
"node_modules/nspell/lib/personal.js"(exports, module) {
|
|
912
|
+
"use strict";
|
|
913
|
+
module.exports = add;
|
|
914
|
+
function add(buf) {
|
|
915
|
+
var self = this;
|
|
916
|
+
var lines = buf.toString("utf8").split("\n");
|
|
917
|
+
var index = -1;
|
|
918
|
+
var line;
|
|
919
|
+
var forbidden;
|
|
920
|
+
var word;
|
|
921
|
+
var flag;
|
|
922
|
+
if (self.flags.FORBIDDENWORD === void 0) self.flags.FORBIDDENWORD = false;
|
|
923
|
+
flag = self.flags.FORBIDDENWORD;
|
|
924
|
+
while (++index < lines.length) {
|
|
925
|
+
line = lines[index].trim();
|
|
926
|
+
if (!line) {
|
|
927
|
+
continue;
|
|
1417
928
|
}
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
values.push(replacement);
|
|
1428
|
-
}
|
|
1429
|
-
memory = {
|
|
1430
|
-
state: {},
|
|
1431
|
-
weighted,
|
|
1432
|
-
suggestions
|
|
1433
|
-
};
|
|
1434
|
-
firstLevel = generate(self, memory, values, edits);
|
|
1435
|
-
previous = 0;
|
|
1436
|
-
max = Math.min(firstLevel.length, Math.pow(Math.max(15 - value.length, 3), 3));
|
|
1437
|
-
size = Math.max(Math.pow(10 - value.length, 3), 1);
|
|
1438
|
-
while (!suggestions.length && previous < max) {
|
|
1439
|
-
next = previous + size;
|
|
1440
|
-
generate(self, memory, firstLevel.slice(previous, next));
|
|
1441
|
-
previous = next;
|
|
1442
|
-
}
|
|
1443
|
-
suggestions.sort(sort);
|
|
1444
|
-
values = [];
|
|
1445
|
-
normalized = [];
|
|
1446
|
-
index = -1;
|
|
1447
|
-
while (++index < suggestions.length) {
|
|
1448
|
-
suggestion = normalize(suggestions[index], self.conversion.out);
|
|
1449
|
-
replacement = suggestion.toLowerCase();
|
|
1450
|
-
if (normalized.indexOf(replacement) < 0) {
|
|
1451
|
-
values.push(suggestion);
|
|
1452
|
-
normalized.push(replacement);
|
|
929
|
+
line = line.split("/");
|
|
930
|
+
word = line[0];
|
|
931
|
+
forbidden = word.charAt(0) === "*";
|
|
932
|
+
if (forbidden) {
|
|
933
|
+
word = word.slice(1);
|
|
934
|
+
}
|
|
935
|
+
self.add(word, line[1]);
|
|
936
|
+
if (forbidden) {
|
|
937
|
+
self.data[word].push(flag);
|
|
1453
938
|
}
|
|
1454
939
|
}
|
|
1455
|
-
return
|
|
1456
|
-
function sort(a, b) {
|
|
1457
|
-
return sortWeight(a, b) || sortCasing(a, b) || sortAlpha(a, b);
|
|
1458
|
-
}
|
|
1459
|
-
function sortWeight(a, b) {
|
|
1460
|
-
return weighted[a] === weighted[b] ? 0 : weighted[a] > weighted[b] ? -1 : 1;
|
|
1461
|
-
}
|
|
1462
|
-
function sortCasing(a, b) {
|
|
1463
|
-
var leftCasing = casing(a);
|
|
1464
|
-
var rightCasing = casing(b);
|
|
1465
|
-
return leftCasing === rightCasing ? 0 : leftCasing === currentCase ? -1 : rightCasing === currentCase ? 1 : void 0;
|
|
1466
|
-
}
|
|
1467
|
-
function sortAlpha(a, b) {
|
|
1468
|
-
return a.localeCompare(b);
|
|
1469
|
-
}
|
|
940
|
+
return self;
|
|
1470
941
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
// node_modules/nspell/lib/index.js
|
|
946
|
+
var require_lib = __commonJS({
|
|
947
|
+
"node_modules/nspell/lib/index.js"(exports, module) {
|
|
948
|
+
"use strict";
|
|
949
|
+
var buffer = require_is_buffer();
|
|
950
|
+
var affix = require_affix();
|
|
951
|
+
module.exports = NSpell3;
|
|
952
|
+
var proto = NSpell3.prototype;
|
|
953
|
+
proto.correct = require_correct();
|
|
954
|
+
proto.suggest = require_suggest();
|
|
955
|
+
proto.spell = require_spell();
|
|
956
|
+
proto.add = require_add2();
|
|
957
|
+
proto.remove = require_remove();
|
|
958
|
+
proto.wordCharacters = require_word_characters();
|
|
959
|
+
proto.dictionary = require_dictionary2();
|
|
960
|
+
proto.personal = require_personal();
|
|
961
|
+
function NSpell3(aff, dic) {
|
|
1476
962
|
var index = -1;
|
|
1477
|
-
var
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
var nextCharacter;
|
|
1481
|
-
var nextAfter;
|
|
1482
|
-
var nextNextAfter;
|
|
1483
|
-
var nextUpper;
|
|
1484
|
-
var currentCase;
|
|
1485
|
-
var position;
|
|
1486
|
-
var after;
|
|
1487
|
-
var upper;
|
|
1488
|
-
var inject;
|
|
1489
|
-
var offset;
|
|
1490
|
-
if (edits) {
|
|
1491
|
-
while (++index < edits.length) {
|
|
1492
|
-
check(edits[index], true);
|
|
1493
|
-
}
|
|
963
|
+
var dictionaries;
|
|
964
|
+
if (!(this instanceof NSpell3)) {
|
|
965
|
+
return new NSpell3(aff, dic);
|
|
1494
966
|
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
while (++position <= word.length) {
|
|
1507
|
-
before += character;
|
|
1508
|
-
after = nextAfter;
|
|
1509
|
-
nextAfter = nextNextAfter;
|
|
1510
|
-
nextNextAfter = nextAfter.slice(1);
|
|
1511
|
-
character = nextCharacter;
|
|
1512
|
-
nextCharacter = word.charAt(position + 1);
|
|
1513
|
-
upper = nextUpper;
|
|
1514
|
-
if (nextCharacter) {
|
|
1515
|
-
nextUpper = nextCharacter.toLowerCase() !== nextCharacter;
|
|
1516
|
-
}
|
|
1517
|
-
if (nextAfter && upper !== nextUpper) {
|
|
1518
|
-
check(before + switchCase(nextAfter));
|
|
1519
|
-
check(
|
|
1520
|
-
before + switchCase(nextCharacter) + switchCase(character) + nextNextAfter
|
|
1521
|
-
);
|
|
1522
|
-
}
|
|
1523
|
-
check(before + nextAfter);
|
|
1524
|
-
if (nextAfter) {
|
|
1525
|
-
check(before + nextCharacter + character + nextNextAfter);
|
|
1526
|
-
}
|
|
1527
|
-
offset = -1;
|
|
1528
|
-
while (++offset < characters.length) {
|
|
1529
|
-
inject = characters[offset];
|
|
1530
|
-
if (upper && inject !== inject.toUpperCase()) {
|
|
1531
|
-
if (currentCase !== "s") {
|
|
1532
|
-
check(before + inject + after);
|
|
1533
|
-
check(before + inject + nextAfter);
|
|
1534
|
-
}
|
|
1535
|
-
inject = inject.toUpperCase();
|
|
1536
|
-
check(before + inject + after);
|
|
1537
|
-
check(before + inject + nextAfter);
|
|
1538
|
-
} else {
|
|
1539
|
-
check(before + inject + after);
|
|
1540
|
-
check(before + inject + nextAfter);
|
|
1541
|
-
}
|
|
967
|
+
if (typeof aff === "string" || buffer(aff)) {
|
|
968
|
+
if (typeof dic === "string" || buffer(dic)) {
|
|
969
|
+
dictionaries = [{ dic }];
|
|
970
|
+
}
|
|
971
|
+
} else if (aff) {
|
|
972
|
+
if ("length" in aff) {
|
|
973
|
+
dictionaries = aff;
|
|
974
|
+
aff = aff[0] && aff[0].aff;
|
|
975
|
+
} else {
|
|
976
|
+
if (aff.dic) {
|
|
977
|
+
dictionaries = [aff];
|
|
1542
978
|
}
|
|
979
|
+
aff = aff.aff;
|
|
1543
980
|
}
|
|
1544
981
|
}
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
982
|
+
if (!aff) {
|
|
983
|
+
throw new Error("Missing `aff` in dictionary");
|
|
984
|
+
}
|
|
985
|
+
aff = affix(aff);
|
|
986
|
+
this.data = /* @__PURE__ */ Object.create(null);
|
|
987
|
+
this.compoundRuleCodes = aff.compoundRuleCodes;
|
|
988
|
+
this.replacementTable = aff.replacementTable;
|
|
989
|
+
this.conversion = aff.conversion;
|
|
990
|
+
this.compoundRules = aff.compoundRules;
|
|
991
|
+
this.rules = aff.rules;
|
|
992
|
+
this.flags = aff.flags;
|
|
993
|
+
if (dictionaries) {
|
|
994
|
+
while (++index < dictionaries.length) {
|
|
995
|
+
if (dictionaries[index].dic) {
|
|
996
|
+
this.dictionary(dictionaries[index].dic);
|
|
1557
997
|
}
|
|
1558
998
|
}
|
|
1559
|
-
if (state) {
|
|
1560
|
-
memory.weighted[value]++;
|
|
1561
|
-
}
|
|
1562
999
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
// client/lib/api-client.js
|
|
1005
|
+
var api_client_exports = {};
|
|
1006
|
+
__export(api_client_exports, {
|
|
1007
|
+
abortMessageListRequests: () => abortMessageListRequests,
|
|
1008
|
+
addContact: () => addContact,
|
|
1009
|
+
addPreferredContact: () => addPreferredContact,
|
|
1010
|
+
addToDenylist: () => addToDenylist,
|
|
1011
|
+
addUserDictWord: () => addUserDictWord,
|
|
1012
|
+
addUserDictWords: () => addUserDictWords,
|
|
1013
|
+
aiTransform: () => aiTransform,
|
|
1014
|
+
allowRemoteContent: () => allowRemoteContent,
|
|
1015
|
+
autocomplete: () => autocomplete,
|
|
1016
|
+
cancelQueuedOutgoing: () => cancelQueuedOutgoing,
|
|
1017
|
+
cancelServerSearch: () => cancelServerSearch,
|
|
1018
|
+
closeWordEdit: () => closeWordEdit,
|
|
1019
|
+
connectEvents: () => connectEvents,
|
|
1020
|
+
connectWebSocket: () => connectWebSocket,
|
|
1021
|
+
consumePendingMailto: () => consumePendingMailto,
|
|
1022
|
+
createCalendarEvent: () => createCalendarEvent,
|
|
1023
|
+
createFolder: () => createFolder,
|
|
1024
|
+
createTask: () => createTask,
|
|
1025
|
+
deleteCalendarEvent: () => deleteCalendarEvent,
|
|
1026
|
+
deleteContact: () => deleteContact,
|
|
1027
|
+
deleteDraft: () => deleteDraft,
|
|
1028
|
+
deleteFolder: () => deleteFolder,
|
|
1029
|
+
deleteMessage: () => deleteMessage,
|
|
1030
|
+
deleteMessages: () => deleteMessages,
|
|
1031
|
+
deleteTask: () => deleteTask,
|
|
1032
|
+
drainStoreSync: () => drainStoreSync,
|
|
1033
|
+
emptyFolder: () => emptyFolder,
|
|
1034
|
+
flagSenderOrDomain: () => flagSenderOrDomain,
|
|
1035
|
+
formatJsonc: () => formatJsonc,
|
|
1036
|
+
getAccounts: () => getAccounts,
|
|
1037
|
+
getAttachment: () => getAttachment,
|
|
1038
|
+
getAutocompleteSettings: () => getAutocompleteSettings,
|
|
1039
|
+
getCalendarEvents: () => getCalendarEvents,
|
|
1040
|
+
getCalendars: () => getCalendars,
|
|
1041
|
+
getDeviceAccounts: () => getDeviceAccounts,
|
|
1042
|
+
getDiagnostics: () => getDiagnostics,
|
|
1043
|
+
getFolders: () => getFolders,
|
|
1044
|
+
getMessage: () => getMessage,
|
|
1045
|
+
getMessages: () => getMessages,
|
|
1046
|
+
getOutboxStatus: () => getOutboxStatus,
|
|
1047
|
+
getPrimaryAccount: () => getPrimaryAccount,
|
|
1048
|
+
getPriorityLists: () => getPriorityLists,
|
|
1049
|
+
getSettings: () => getSettings,
|
|
1050
|
+
getSyncPending: () => getSyncPending,
|
|
1051
|
+
getTasks: () => getTasks,
|
|
1052
|
+
getThreadMessages: () => getThreadMessages,
|
|
1053
|
+
getUnifiedInbox: () => getUnifiedInbox,
|
|
1054
|
+
getUserDict: () => getUserDict,
|
|
1055
|
+
getVersion: () => getVersion,
|
|
1056
|
+
hasBccHistoryTo: () => hasBccHistoryTo,
|
|
1057
|
+
hasCcHistoryTo: () => hasCcHistoryTo,
|
|
1058
|
+
listContacts: () => listContacts,
|
|
1059
|
+
listQueuedOutgoing: () => listQueuedOutgoing,
|
|
1060
|
+
logClientEvent: () => logClientEvent,
|
|
1061
|
+
markAsSpamMessages: () => markAsSpamMessages,
|
|
1062
|
+
markFolderRead: () => markFolderRead,
|
|
1063
|
+
moveFolderToTrash: () => moveFolderToTrash,
|
|
1064
|
+
moveMessage: () => moveMessage,
|
|
1065
|
+
moveMessages: () => moveMessages,
|
|
1066
|
+
onEvent: () => onEvent,
|
|
1067
|
+
onWsEvent: () => onWsEvent,
|
|
1068
|
+
openAttachment: () => openAttachment,
|
|
1069
|
+
openInTextEditor: () => openInTextEditor,
|
|
1070
|
+
openInWord: () => openInWord,
|
|
1071
|
+
openLocalPath: () => openLocalPath,
|
|
1072
|
+
readConfigHelp: () => readConfigHelp,
|
|
1073
|
+
readJsoncFile: () => readJsoncFile,
|
|
1074
|
+
reauthGoogleScopes: () => reauthGoogleScopes,
|
|
1075
|
+
reauthenticate: () => reauthenticate,
|
|
1076
|
+
recordSpamReport: () => recordSpamReport,
|
|
1077
|
+
removeUserDictWord: () => removeUserDictWord,
|
|
1078
|
+
renameFolder: () => renameFolder,
|
|
1079
|
+
repairAccounts: () => repairAccounts,
|
|
1080
|
+
restartServer: () => restartServer,
|
|
1081
|
+
saveAutocompleteSettings: () => saveAutocompleteSettings,
|
|
1082
|
+
saveDraft: () => saveDraft,
|
|
1083
|
+
saveSettings: () => saveSettings,
|
|
1084
|
+
searchContacts: () => searchContacts,
|
|
1085
|
+
searchMessages: () => searchMessages,
|
|
1086
|
+
sendMessage: () => sendMessage,
|
|
1087
|
+
setPriorityDomain: () => setPriorityDomain,
|
|
1088
|
+
setPrioritySender: () => setPrioritySender,
|
|
1089
|
+
setupAccount: () => setupAccount,
|
|
1090
|
+
showReminderPopup: () => showReminderPopup,
|
|
1091
|
+
subscribeStore: () => subscribeStore,
|
|
1092
|
+
syncAccount: () => syncAccount,
|
|
1093
|
+
triggerSync: () => triggerSync,
|
|
1094
|
+
undeleteMessage: () => undeleteMessage,
|
|
1095
|
+
unsubscribeOneClick: () => unsubscribeOneClick,
|
|
1096
|
+
updateCalendarEvent: () => updateCalendarEvent,
|
|
1097
|
+
updateFlags: () => updateFlags,
|
|
1098
|
+
updateTask: () => updateTask,
|
|
1099
|
+
upsertContact: () => upsertContact,
|
|
1100
|
+
writeJsoncFile: () => writeJsoncFile
|
|
1101
|
+
});
|
|
1102
|
+
function getIpc() {
|
|
1103
|
+
if (typeof mailxapi !== "undefined" && mailxapi?.isApp)
|
|
1104
|
+
return mailxapi;
|
|
1105
|
+
if (window.opener?.mailxapi?.isApp)
|
|
1106
|
+
return window.opener.mailxapi;
|
|
1107
|
+
if (window.parent?.mailxapi?.isApp)
|
|
1108
|
+
return window.parent.mailxapi;
|
|
1109
|
+
return null;
|
|
1110
|
+
}
|
|
1111
|
+
function buildRelayBridge() {
|
|
1112
|
+
const pending = /* @__PURE__ */ new Map();
|
|
1113
|
+
window.addEventListener("message", (ev) => {
|
|
1114
|
+
if (!ev.data || ev.data.type !== "mailx-ipc-result" || !ev.data.id)
|
|
1115
|
+
return;
|
|
1116
|
+
const entry = pending.get(ev.data.id);
|
|
1117
|
+
if (!entry)
|
|
1118
|
+
return;
|
|
1119
|
+
pending.delete(ev.data.id);
|
|
1120
|
+
clearTimeout(entry.timer);
|
|
1121
|
+
if (ev.data.ok)
|
|
1122
|
+
entry.resolve(ev.data.result);
|
|
1123
|
+
else
|
|
1124
|
+
entry.reject(new Error(ev.data.error || "parent-relay ipc error"));
|
|
1125
|
+
});
|
|
1126
|
+
const call = (method, args) => {
|
|
1127
|
+
const id = `ipc-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1128
|
+
return new Promise((resolve, reject) => {
|
|
1129
|
+
const timer = setTimeout(() => {
|
|
1130
|
+
pending.delete(id);
|
|
1131
|
+
reject(new Error(`parent-relay timeout: ${method}`));
|
|
1132
|
+
}, 12e4);
|
|
1133
|
+
pending.set(id, { resolve, reject, timer });
|
|
1134
|
+
try {
|
|
1135
|
+
window.parent.postMessage({ type: "mailx-ipc", id, method, args }, "*");
|
|
1136
|
+
} catch (e) {
|
|
1137
|
+
clearTimeout(timer);
|
|
1138
|
+
pending.delete(id);
|
|
1139
|
+
reject(e);
|
|
1140
|
+
}
|
|
1141
|
+
});
|
|
1142
|
+
};
|
|
1143
|
+
return new Proxy({}, {
|
|
1144
|
+
get(_t, prop) {
|
|
1145
|
+
if (prop === "isApp")
|
|
1146
|
+
return true;
|
|
1147
|
+
if (prop === "platform")
|
|
1148
|
+
return window.parent?.mailxapi?.platform || "webview2";
|
|
1149
|
+
if (prop === "onEvent") {
|
|
1150
|
+
return (handler) => window.parent?.mailxapi?.onEvent?.(handler);
|
|
1566
1151
|
}
|
|
1152
|
+
return (...args) => call(prop, args);
|
|
1153
|
+
}
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
function ipc() {
|
|
1157
|
+
const inIframe = window.parent && window.parent !== window;
|
|
1158
|
+
if (inIframe && window.parent?.mailxapi?.isApp) {
|
|
1159
|
+
if (!cachedRelayBridge)
|
|
1160
|
+
cachedRelayBridge = buildRelayBridge();
|
|
1161
|
+
return cachedRelayBridge;
|
|
1162
|
+
}
|
|
1163
|
+
const bridge = getIpc();
|
|
1164
|
+
if (!bridge)
|
|
1165
|
+
throw new Error("IPC bridge not available");
|
|
1166
|
+
return bridge;
|
|
1167
|
+
}
|
|
1168
|
+
function abortMessageListRequests() {
|
|
1169
|
+
if (messageListAbort) {
|
|
1170
|
+
messageListAbort.abort();
|
|
1171
|
+
messageListAbort = null;
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
function getAccounts() {
|
|
1175
|
+
return ipc().getAccounts();
|
|
1176
|
+
}
|
|
1177
|
+
function getFolders(accountId) {
|
|
1178
|
+
return ipc().getFolders(accountId);
|
|
1179
|
+
}
|
|
1180
|
+
function getMessages(accountId, folderId, page = 1, pageSize = 50, flaggedOnly = false, sort, sortDir) {
|
|
1181
|
+
abortMessageListRequests();
|
|
1182
|
+
return ipc().getMessages(accountId, folderId, page, pageSize, sort, sortDir, void 0, flaggedOnly);
|
|
1183
|
+
}
|
|
1184
|
+
function getUnifiedInbox(page = 1, pageSize = 50) {
|
|
1185
|
+
abortMessageListRequests();
|
|
1186
|
+
return ipc().getUnifiedInbox(page, pageSize);
|
|
1187
|
+
}
|
|
1188
|
+
function searchMessages(query, page = 1, pageSize = 50, scope = "all", accountId = "", folderId = 0, includeTrashSpam = false) {
|
|
1189
|
+
return ipc().searchMessages(query, page, pageSize, scope, accountId, folderId, includeTrashSpam);
|
|
1190
|
+
}
|
|
1191
|
+
function cancelServerSearch() {
|
|
1192
|
+
return ipc().cancelServerSearch?.();
|
|
1193
|
+
}
|
|
1194
|
+
function getMessage(accountId, uid, allowRemote = false, folderId) {
|
|
1195
|
+
return ipc().getMessage(accountId, uid, allowRemote, folderId);
|
|
1196
|
+
}
|
|
1197
|
+
function updateFlags(accountId, uid, flags) {
|
|
1198
|
+
return ipc().updateFlags(accountId, uid, flags);
|
|
1199
|
+
}
|
|
1200
|
+
function triggerSync() {
|
|
1201
|
+
return ipc().syncAll();
|
|
1202
|
+
}
|
|
1203
|
+
function syncAccount(accountId) {
|
|
1204
|
+
return ipc().syncAccount(accountId);
|
|
1205
|
+
}
|
|
1206
|
+
function reauthenticate(accountId) {
|
|
1207
|
+
return ipc().reauthenticate(accountId);
|
|
1208
|
+
}
|
|
1209
|
+
function reauthGoogleScopes() {
|
|
1210
|
+
return ipc().reauthGoogleScopes();
|
|
1211
|
+
}
|
|
1212
|
+
function getSyncPending() {
|
|
1213
|
+
return ipc().getSyncPending();
|
|
1214
|
+
}
|
|
1215
|
+
function getDiagnostics() {
|
|
1216
|
+
return ipc().getDiagnostics?.() ?? Promise.resolve([]);
|
|
1217
|
+
}
|
|
1218
|
+
function getPrimaryAccount(feature) {
|
|
1219
|
+
return ipc().getPrimaryAccount?.(feature) ?? Promise.resolve(null);
|
|
1220
|
+
}
|
|
1221
|
+
function getCalendarEvents(fromMs, toMs) {
|
|
1222
|
+
return ipc().getCalendarEvents?.(fromMs, toMs) ?? Promise.resolve([]);
|
|
1223
|
+
}
|
|
1224
|
+
function getCalendars() {
|
|
1225
|
+
return ipc().getCalendars?.() ?? Promise.resolve([]);
|
|
1226
|
+
}
|
|
1227
|
+
function createCalendarEvent(ev) {
|
|
1228
|
+
return ipc().createCalendarEvent?.(ev);
|
|
1229
|
+
}
|
|
1230
|
+
function updateCalendarEvent(uuid, patch) {
|
|
1231
|
+
return ipc().updateCalendarEvent?.(uuid, patch);
|
|
1232
|
+
}
|
|
1233
|
+
function deleteCalendarEvent(uuid) {
|
|
1234
|
+
return ipc().deleteCalendarEvent?.(uuid);
|
|
1235
|
+
}
|
|
1236
|
+
function getTasks(includeCompleted = false) {
|
|
1237
|
+
return ipc().getTasks?.(includeCompleted) ?? Promise.resolve([]);
|
|
1238
|
+
}
|
|
1239
|
+
function createTask(t) {
|
|
1240
|
+
return ipc().createTask?.(t);
|
|
1241
|
+
}
|
|
1242
|
+
function updateTask(uuid, patch) {
|
|
1243
|
+
return ipc().updateTask?.(uuid, patch);
|
|
1244
|
+
}
|
|
1245
|
+
function deleteTask(uuid) {
|
|
1246
|
+
return ipc().deleteTask?.(uuid);
|
|
1247
|
+
}
|
|
1248
|
+
function drainStoreSync() {
|
|
1249
|
+
return ipc().drainStoreSync?.();
|
|
1250
|
+
}
|
|
1251
|
+
function recordSpamReport(accountId, uid, folderId) {
|
|
1252
|
+
return ipc().recordSpamReport?.(accountId, uid, folderId);
|
|
1253
|
+
}
|
|
1254
|
+
function getOutboxStatus() {
|
|
1255
|
+
return ipc().getOutboxStatus();
|
|
1256
|
+
}
|
|
1257
|
+
function listQueuedOutgoing() {
|
|
1258
|
+
return ipc().listQueuedOutgoing();
|
|
1259
|
+
}
|
|
1260
|
+
function cancelQueuedOutgoing(p) {
|
|
1261
|
+
return ipc().cancelQueuedOutgoing(p);
|
|
1262
|
+
}
|
|
1263
|
+
function searchContacts(query) {
|
|
1264
|
+
return ipc().searchContacts(query);
|
|
1265
|
+
}
|
|
1266
|
+
function hasCcHistoryTo(email) {
|
|
1267
|
+
return ipc().hasCcHistoryTo(email);
|
|
1268
|
+
}
|
|
1269
|
+
function hasBccHistoryTo(email) {
|
|
1270
|
+
return ipc().hasBccHistoryTo(email);
|
|
1271
|
+
}
|
|
1272
|
+
function listContacts(query, page = 1, pageSize = 100) {
|
|
1273
|
+
return ipc().listContacts(query, page, pageSize);
|
|
1274
|
+
}
|
|
1275
|
+
function upsertContact(name, email) {
|
|
1276
|
+
return ipc().upsertContact(name, email);
|
|
1277
|
+
}
|
|
1278
|
+
function deleteContact(email) {
|
|
1279
|
+
return ipc().deleteContact(email);
|
|
1280
|
+
}
|
|
1281
|
+
function addPreferredContact(entry) {
|
|
1282
|
+
return ipc().addPreferredContact(entry.name, entry.email, entry.source, entry.organization);
|
|
1283
|
+
}
|
|
1284
|
+
function getPriorityLists() {
|
|
1285
|
+
return ipc().getPriorityLists();
|
|
1286
|
+
}
|
|
1287
|
+
function setPrioritySender(email, value, name) {
|
|
1288
|
+
return ipc().setPrioritySender(email, value, name);
|
|
1289
|
+
}
|
|
1290
|
+
function setPriorityDomain(domain, value) {
|
|
1291
|
+
return ipc().setPriorityDomain(domain, value);
|
|
1292
|
+
}
|
|
1293
|
+
function addToDenylist(email) {
|
|
1294
|
+
return ipc().addToDenylist(email);
|
|
1295
|
+
}
|
|
1296
|
+
function openLocalPath(which) {
|
|
1297
|
+
return ipc().openLocalPath(which);
|
|
1298
|
+
}
|
|
1299
|
+
function openInTextEditor(path) {
|
|
1300
|
+
return ipc().openInTextEditor?.(path) ?? Promise.resolve({ ok: false, opener: "none", reason: "no host" });
|
|
1301
|
+
}
|
|
1302
|
+
function allowRemoteContent(type, value) {
|
|
1303
|
+
return ipc().allowRemoteContent(type, value);
|
|
1304
|
+
}
|
|
1305
|
+
function getUserDict() {
|
|
1306
|
+
return ipc().getUserDict?.() ?? Promise.resolve([]);
|
|
1307
|
+
}
|
|
1308
|
+
function addUserDictWord(word) {
|
|
1309
|
+
return ipc().addUserDictWord?.(word) ?? Promise.resolve([]);
|
|
1310
|
+
}
|
|
1311
|
+
function addUserDictWords(words) {
|
|
1312
|
+
return ipc().addUserDictWords?.(words) ?? Promise.resolve([]);
|
|
1313
|
+
}
|
|
1314
|
+
function removeUserDictWord(word) {
|
|
1315
|
+
return ipc().removeUserDictWord?.(word) ?? Promise.resolve([]);
|
|
1316
|
+
}
|
|
1317
|
+
function flagSenderOrDomain(type, value) {
|
|
1318
|
+
return ipc().flagSenderOrDomain?.(type, value) ?? Promise.resolve({ flagged: false });
|
|
1319
|
+
}
|
|
1320
|
+
function deleteMessage(accountId, uid) {
|
|
1321
|
+
return ipc().deleteMessage?.(accountId, uid);
|
|
1322
|
+
}
|
|
1323
|
+
function deleteMessages(accountId, uids) {
|
|
1324
|
+
if (uids.length === 1)
|
|
1325
|
+
return deleteMessage(accountId, uids[0]);
|
|
1326
|
+
return ipc().deleteMessages?.(accountId, uids);
|
|
1327
|
+
}
|
|
1328
|
+
function moveMessages(accountId, uids, targetFolderId, targetAccountId) {
|
|
1329
|
+
if (uids.length === 1)
|
|
1330
|
+
return moveMessage(accountId, uids[0], targetFolderId, targetAccountId);
|
|
1331
|
+
return ipc().moveMessages?.(accountId, uids, targetFolderId, targetAccountId);
|
|
1332
|
+
}
|
|
1333
|
+
function markAsSpamMessages(accountId, uids) {
|
|
1334
|
+
return ipc().markAsSpamMessages?.(accountId, uids);
|
|
1335
|
+
}
|
|
1336
|
+
function undeleteMessage(accountId, uid, folderId) {
|
|
1337
|
+
return ipc().undeleteMessage?.(accountId, uid, folderId);
|
|
1338
|
+
}
|
|
1339
|
+
function moveMessage(accountId, uid, targetFolderId, targetAccountId) {
|
|
1340
|
+
return ipc().moveMessage?.(accountId, uid, targetFolderId, targetAccountId);
|
|
1341
|
+
}
|
|
1342
|
+
function restartServer() {
|
|
1343
|
+
return ipc().restart?.();
|
|
1344
|
+
}
|
|
1345
|
+
function markFolderRead(accountId, folderId) {
|
|
1346
|
+
return ipc().markFolderRead?.(accountId, folderId);
|
|
1347
|
+
}
|
|
1348
|
+
function createFolder(accountId, parentPath, name) {
|
|
1349
|
+
return ipc().createFolder?.(accountId, parentPath, name);
|
|
1350
|
+
}
|
|
1351
|
+
function renameFolder(accountId, folderId, newName) {
|
|
1352
|
+
return ipc().renameFolder?.(accountId, folderId, newName);
|
|
1353
|
+
}
|
|
1354
|
+
function deleteFolder(accountId, folderId) {
|
|
1355
|
+
return ipc().deleteFolder?.(accountId, folderId);
|
|
1356
|
+
}
|
|
1357
|
+
function moveFolderToTrash(accountId, folderId) {
|
|
1358
|
+
return ipc().moveFolderToTrash?.(accountId, folderId);
|
|
1359
|
+
}
|
|
1360
|
+
function emptyFolder(accountId, folderId) {
|
|
1361
|
+
return ipc().emptyFolder?.(accountId, folderId);
|
|
1362
|
+
}
|
|
1363
|
+
function logClientEvent(tag, data) {
|
|
1364
|
+
let delivered = false;
|
|
1365
|
+
try {
|
|
1366
|
+
const bridge = typeof globalThis.mailxapi !== "undefined" && globalThis.mailxapi?.isApp ? globalThis.mailxapi : window.opener?.mailxapi?.isApp ? window.opener.mailxapi : window.parent?.mailxapi?.isApp ? window.parent.mailxapi : null;
|
|
1367
|
+
if (bridge?.logClientEvent) {
|
|
1368
|
+
bridge.logClientEvent(tag, data);
|
|
1369
|
+
delivered = true;
|
|
1567
1370
|
}
|
|
1371
|
+
} catch {
|
|
1568
1372
|
}
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
var require_spell = __commonJS({
|
|
1573
|
-
"node_modules/nspell/lib/spell.js"(exports, module) {
|
|
1574
|
-
"use strict";
|
|
1575
|
-
var form = require_form();
|
|
1576
|
-
var flag = require_flag();
|
|
1577
|
-
module.exports = spell;
|
|
1578
|
-
function spell(word) {
|
|
1579
|
-
var self = this;
|
|
1580
|
-
var value = form(self, word, true);
|
|
1581
|
-
return {
|
|
1582
|
-
correct: self.correct(word),
|
|
1583
|
-
forbidden: Boolean(
|
|
1584
|
-
value && flag(self.flags, "FORBIDDENWORD", self.data[value])
|
|
1585
|
-
),
|
|
1586
|
-
warn: Boolean(value && flag(self.flags, "WARN", self.data[value]))
|
|
1587
|
-
};
|
|
1373
|
+
try {
|
|
1374
|
+
if (window.parent && window.parent !== window) {
|
|
1375
|
+
window.parent.postMessage({ type: "mailx-trace", tag, data, bridged: delivered }, "*");
|
|
1588
1376
|
}
|
|
1377
|
+
} catch {
|
|
1589
1378
|
}
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
next = rule.type === "SFX" ? next + entry.add : entry.add + next;
|
|
1611
|
-
words.push(next);
|
|
1612
|
-
if (continuation && continuation.length) {
|
|
1613
|
-
while (++position < continuation.length) {
|
|
1614
|
-
continuationRule = rules[continuation[position]];
|
|
1615
|
-
if (continuationRule) {
|
|
1616
|
-
apply(next, continuationRule, rules, words);
|
|
1617
|
-
}
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1622
|
-
return words;
|
|
1623
|
-
}
|
|
1379
|
+
}
|
|
1380
|
+
function sendMessage(body) {
|
|
1381
|
+
return ipc().sendMessage?.(body);
|
|
1382
|
+
}
|
|
1383
|
+
function saveDraft(body) {
|
|
1384
|
+
return ipc().saveDraft?.(body);
|
|
1385
|
+
}
|
|
1386
|
+
function onEvent(handler) {
|
|
1387
|
+
eventHandlers.push(handler);
|
|
1388
|
+
return () => {
|
|
1389
|
+
const i = eventHandlers.indexOf(handler);
|
|
1390
|
+
if (i >= 0)
|
|
1391
|
+
eventHandlers.splice(i, 1);
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
function subscribeStore(topic, handler) {
|
|
1395
|
+
let set = storeSubs.get(topic);
|
|
1396
|
+
if (!set) {
|
|
1397
|
+
set = /* @__PURE__ */ new Set();
|
|
1398
|
+
storeSubs.set(topic, set);
|
|
1624
1399
|
}
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
module.exports = add;
|
|
1633
|
-
var push = [].push;
|
|
1634
|
-
var NO_RULES = [];
|
|
1635
|
-
function addRules(dict, word, rules) {
|
|
1636
|
-
var curr = dict[word];
|
|
1637
|
-
if (word in dict) {
|
|
1638
|
-
if (curr === NO_RULES) {
|
|
1639
|
-
dict[word] = rules.concat();
|
|
1640
|
-
} else {
|
|
1641
|
-
push.apply(curr, rules);
|
|
1642
|
-
}
|
|
1643
|
-
} else {
|
|
1644
|
-
dict[word] = rules.concat();
|
|
1645
|
-
}
|
|
1400
|
+
set.add(handler);
|
|
1401
|
+
return () => {
|
|
1402
|
+
const s = storeSubs.get(topic);
|
|
1403
|
+
if (s) {
|
|
1404
|
+
s.delete(handler);
|
|
1405
|
+
if (s.size === 0)
|
|
1406
|
+
storeSubs.delete(topic);
|
|
1646
1407
|
}
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
addRules(dict, word, codes);
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
function deliverStore(event) {
|
|
1411
|
+
const exact = storeSubs.get(event.topic);
|
|
1412
|
+
if (exact)
|
|
1413
|
+
for (const h of exact) {
|
|
1414
|
+
try {
|
|
1415
|
+
h(event);
|
|
1416
|
+
} catch (e) {
|
|
1417
|
+
console.error("[store-bus]", e);
|
|
1658
1418
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
while (++offset < newWords.length) {
|
|
1668
|
-
if (!(newWords[offset] in dict)) {
|
|
1669
|
-
dict[newWords[offset]] = NO_RULES;
|
|
1670
|
-
}
|
|
1671
|
-
if (rule.combineable) {
|
|
1672
|
-
subposition = position;
|
|
1673
|
-
while (++subposition < codes.length) {
|
|
1674
|
-
combined = options.rules[codes[subposition]];
|
|
1675
|
-
if (combined && combined.combineable && rule.type !== combined.type) {
|
|
1676
|
-
otherNewWords = apply(
|
|
1677
|
-
newWords[offset],
|
|
1678
|
-
combined,
|
|
1679
|
-
options.rules,
|
|
1680
|
-
[]
|
|
1681
|
-
);
|
|
1682
|
-
suboffset = -1;
|
|
1683
|
-
while (++suboffset < otherNewWords.length) {
|
|
1684
|
-
if (!(otherNewWords[suboffset] in dict)) {
|
|
1685
|
-
dict[otherNewWords[suboffset]] = NO_RULES;
|
|
1686
|
-
}
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
}
|
|
1419
|
+
}
|
|
1420
|
+
const wild = storeSubs.get("*");
|
|
1421
|
+
if (wild)
|
|
1422
|
+
for (const h of wild) {
|
|
1423
|
+
try {
|
|
1424
|
+
h(event);
|
|
1425
|
+
} catch (e) {
|
|
1426
|
+
console.error("[store-bus]", e);
|
|
1693
1427
|
}
|
|
1694
1428
|
}
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1429
|
+
}
|
|
1430
|
+
function connectEvents() {
|
|
1431
|
+
ipc().onEvent((event) => {
|
|
1432
|
+
if (event && event._event === "store")
|
|
1433
|
+
deliverStore(event);
|
|
1434
|
+
for (const h of eventHandlers)
|
|
1435
|
+
h(event);
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
function autocomplete(body, signal) {
|
|
1439
|
+
return ipc().autocomplete?.(body);
|
|
1440
|
+
}
|
|
1441
|
+
function getAutocompleteSettings() {
|
|
1442
|
+
return ipc().getAutocompleteSettings?.();
|
|
1443
|
+
}
|
|
1444
|
+
function saveAutocompleteSettings(settings) {
|
|
1445
|
+
return ipc().saveAutocompleteSettings?.(settings);
|
|
1446
|
+
}
|
|
1447
|
+
function getVersion() {
|
|
1448
|
+
return ipc().getVersion();
|
|
1449
|
+
}
|
|
1450
|
+
function getSettings() {
|
|
1451
|
+
return ipc().getSettings();
|
|
1452
|
+
}
|
|
1453
|
+
function saveSettings(settings) {
|
|
1454
|
+
return ipc().saveSettingsData?.(settings);
|
|
1455
|
+
}
|
|
1456
|
+
function repairAccounts() {
|
|
1457
|
+
return ipc().repairAccounts?.();
|
|
1458
|
+
}
|
|
1459
|
+
function deleteDraft(accountId, draftUid2, draftId2) {
|
|
1460
|
+
return ipc().deleteDraft?.(accountId, draftUid2, draftId2);
|
|
1461
|
+
}
|
|
1462
|
+
function addContact(name, email) {
|
|
1463
|
+
return ipc().addContact?.(name, email);
|
|
1464
|
+
}
|
|
1465
|
+
function getThreadMessages(accountId, threadId) {
|
|
1466
|
+
return ipc().getThreadMessages?.(accountId, threadId);
|
|
1467
|
+
}
|
|
1468
|
+
function readJsoncFile(name) {
|
|
1469
|
+
return ipc().readJsoncFile?.(name);
|
|
1470
|
+
}
|
|
1471
|
+
function writeJsoncFile(name, content) {
|
|
1472
|
+
return ipc().writeJsoncFile?.(name, content);
|
|
1473
|
+
}
|
|
1474
|
+
function formatJsonc(content) {
|
|
1475
|
+
return ipc().formatJsonc?.(content);
|
|
1476
|
+
}
|
|
1477
|
+
function readConfigHelp(name) {
|
|
1478
|
+
return ipc().readConfigHelp?.(name) ?? Promise.resolve({ content: "" });
|
|
1479
|
+
}
|
|
1480
|
+
function unsubscribeOneClick(url) {
|
|
1481
|
+
return ipc().unsubscribeOneClick?.(url);
|
|
1482
|
+
}
|
|
1483
|
+
function openInWord(editId, html) {
|
|
1484
|
+
return ipc().openInWord?.(editId, html) ?? Promise.resolve({ ok: false, path: "", opener: "none" });
|
|
1485
|
+
}
|
|
1486
|
+
function closeWordEdit(editId) {
|
|
1487
|
+
return ipc().closeWordEdit?.(editId) ?? Promise.resolve();
|
|
1488
|
+
}
|
|
1489
|
+
function showReminderPopup(opts) {
|
|
1490
|
+
return ipc().showReminderPopup?.(opts) ?? Promise.resolve({ button: "", reason: "no host" });
|
|
1491
|
+
}
|
|
1492
|
+
function consumePendingMailto() {
|
|
1493
|
+
return ipc().consumePendingMailto?.() ?? Promise.resolve(null);
|
|
1494
|
+
}
|
|
1495
|
+
function aiTransform(req) {
|
|
1496
|
+
return ipc().aiTransform?.(req) ?? Promise.resolve({ text: "", reason: "AI not available in this host" });
|
|
1497
|
+
}
|
|
1498
|
+
function setupAccount(name, email, password) {
|
|
1499
|
+
return ipc().setupAccount?.(name, email, password);
|
|
1500
|
+
}
|
|
1501
|
+
async function getAttachment(accountId, uid, attachmentId, folderId) {
|
|
1502
|
+
return ipc().getAttachment(accountId, uid, attachmentId, folderId);
|
|
1503
|
+
}
|
|
1504
|
+
async function openAttachment(accountId, uid, attachmentId, folderId) {
|
|
1505
|
+
const fn = ipc().openAttachment;
|
|
1506
|
+
return fn ? fn(accountId, uid, attachmentId, folderId) : void 0;
|
|
1507
|
+
}
|
|
1508
|
+
async function getDeviceAccounts() {
|
|
1509
|
+
return ipc().getDeviceAccounts?.() ?? [];
|
|
1510
|
+
}
|
|
1511
|
+
var cachedRelayBridge, messageListAbort, eventHandlers, storeSubs, connectWebSocket, onWsEvent;
|
|
1512
|
+
var init_api_client = __esm({
|
|
1513
|
+
"client/lib/api-client.js"() {
|
|
1701
1514
|
"use strict";
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
return self;
|
|
1709
|
-
}
|
|
1515
|
+
cachedRelayBridge = null;
|
|
1516
|
+
messageListAbort = null;
|
|
1517
|
+
eventHandlers = [];
|
|
1518
|
+
storeSubs = /* @__PURE__ */ new Map();
|
|
1519
|
+
connectWebSocket = connectEvents;
|
|
1520
|
+
onWsEvent = onEvent;
|
|
1710
1521
|
}
|
|
1711
1522
|
});
|
|
1712
1523
|
|
|
1713
|
-
//
|
|
1714
|
-
var
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1524
|
+
// client/compose/spellcheck-core.js
|
|
1525
|
+
var spellcheck_core_exports = {};
|
|
1526
|
+
__export(spellcheck_core_exports, {
|
|
1527
|
+
MARKER_ATTR: () => MARKER_ATTR,
|
|
1528
|
+
MIN_WORD_LEN: () => MIN_WORD_LEN,
|
|
1529
|
+
SKIP_TAGS: () => SKIP_TAGS,
|
|
1530
|
+
USER_DICT_KEY: () => USER_DICT_KEY,
|
|
1531
|
+
addToUserDict: () => addToUserDict,
|
|
1532
|
+
buildSuggestionList: () => buildSuggestionList,
|
|
1533
|
+
getSpell: () => getSpell,
|
|
1534
|
+
getWordAtPoint: () => getWordAtPoint,
|
|
1535
|
+
showSuggestionsMenu: () => showSuggestionsMenu
|
|
1724
1536
|
});
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1537
|
+
async function getSpell() {
|
|
1538
|
+
if (spellPromise)
|
|
1539
|
+
return spellPromise;
|
|
1540
|
+
spellPromise = (async () => {
|
|
1541
|
+
const [affRes, dicRes] = await Promise.all([
|
|
1542
|
+
fetch("../lib/dict/en.aff"),
|
|
1543
|
+
fetch("../lib/dict/en.dic")
|
|
1544
|
+
]);
|
|
1545
|
+
if (!affRes.ok || !dicRes.ok) {
|
|
1546
|
+
throw new Error(`spellcheck: dict fetch failed (aff=${affRes.status} dic=${dicRes.status})`);
|
|
1733
1547
|
}
|
|
1734
|
-
|
|
1735
|
-
});
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
var add = require_add();
|
|
1743
|
-
module.exports = parse;
|
|
1744
|
-
var whiteSpaceExpression = /\s/g;
|
|
1745
|
-
function parse(buf, options, dict) {
|
|
1746
|
-
var value = buf.toString("utf8");
|
|
1747
|
-
var last = value.indexOf("\n") + 1;
|
|
1748
|
-
var index = value.indexOf("\n", last);
|
|
1749
|
-
while (index > -1) {
|
|
1750
|
-
if (value.charCodeAt(last) !== 9) {
|
|
1751
|
-
parseLine(value.slice(last, index), options, dict);
|
|
1752
|
-
}
|
|
1753
|
-
last = index + 1;
|
|
1754
|
-
index = value.indexOf("\n", last);
|
|
1755
|
-
}
|
|
1756
|
-
parseLine(value.slice(last), options, dict);
|
|
1548
|
+
const [aff, dic] = await Promise.all([affRes.text(), dicRes.text()]);
|
|
1549
|
+
const sp = new import_nspell.default({ aff, dic });
|
|
1550
|
+
try {
|
|
1551
|
+
const raw = localStorage.getItem(USER_DICT_KEY);
|
|
1552
|
+
if (raw)
|
|
1553
|
+
for (const w of JSON.parse(raw))
|
|
1554
|
+
sp.add(w);
|
|
1555
|
+
} catch {
|
|
1757
1556
|
}
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1557
|
+
getUserDict().then((cloud) => {
|
|
1558
|
+
const cloudArr = Array.isArray(cloud) ? cloud : [];
|
|
1559
|
+
for (const w of cloudArr)
|
|
1560
|
+
sp.add(w);
|
|
1561
|
+
let local = [];
|
|
1562
|
+
try {
|
|
1563
|
+
const raw = localStorage.getItem(USER_DICT_KEY);
|
|
1564
|
+
local = raw ? JSON.parse(raw) : [];
|
|
1565
|
+
} catch {
|
|
1566
|
+
local = [];
|
|
1767
1567
|
}
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
result = whiteSpaceExpression.exec(line);
|
|
1773
|
-
codes = line.slice(slashOffset + 1, result ? result.index : void 0);
|
|
1774
|
-
} else {
|
|
1775
|
-
word = line.slice(0, hashOffset);
|
|
1776
|
-
}
|
|
1777
|
-
} else if (slashOffset > -1) {
|
|
1778
|
-
word = line.slice(0, slashOffset);
|
|
1779
|
-
codes = line.slice(slashOffset + 1);
|
|
1780
|
-
} else {
|
|
1781
|
-
word = line;
|
|
1568
|
+
const cloudSet = new Set(cloudArr);
|
|
1569
|
+
const localOnly = local.filter((w) => !cloudSet.has(w));
|
|
1570
|
+
if (localOnly.length > 0) {
|
|
1571
|
+
addUserDictWords(localOnly).catch((e) => console.error("[spell] reconcile:", e));
|
|
1782
1572
|
}
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1573
|
+
try {
|
|
1574
|
+
const merged = [.../* @__PURE__ */ new Set([...local, ...cloudArr])];
|
|
1575
|
+
localStorage.setItem(USER_DICT_KEY, JSON.stringify(merged));
|
|
1576
|
+
} catch {
|
|
1786
1577
|
}
|
|
1578
|
+
}).catch(() => {
|
|
1579
|
+
});
|
|
1580
|
+
return sp;
|
|
1581
|
+
})();
|
|
1582
|
+
return spellPromise;
|
|
1583
|
+
}
|
|
1584
|
+
function addToUserDict(word, sp) {
|
|
1585
|
+
try {
|
|
1586
|
+
const raw = localStorage.getItem(USER_DICT_KEY);
|
|
1587
|
+
const arr = raw ? JSON.parse(raw) : [];
|
|
1588
|
+
if (!arr.includes(word)) {
|
|
1589
|
+
arr.push(word);
|
|
1590
|
+
localStorage.setItem(USER_DICT_KEY, JSON.stringify(arr));
|
|
1787
1591
|
}
|
|
1592
|
+
} catch {
|
|
1788
1593
|
}
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
var self = this;
|
|
1799
|
-
var index = -1;
|
|
1800
|
-
var rule;
|
|
1801
|
-
var source;
|
|
1802
|
-
var character;
|
|
1803
|
-
var offset;
|
|
1804
|
-
parse(buf, self, self.data);
|
|
1805
|
-
while (++index < self.compoundRules.length) {
|
|
1806
|
-
rule = self.compoundRules[index];
|
|
1807
|
-
source = "";
|
|
1808
|
-
offset = -1;
|
|
1809
|
-
while (++offset < rule.length) {
|
|
1810
|
-
character = rule.charAt(offset);
|
|
1811
|
-
source += self.compoundRuleCodes[character].length ? "(?:" + self.compoundRuleCodes[character].join("|") + ")" : character;
|
|
1812
|
-
}
|
|
1813
|
-
self.compoundRules[index] = new RegExp(source, "i");
|
|
1814
|
-
}
|
|
1815
|
-
return self;
|
|
1594
|
+
sp.add(word);
|
|
1595
|
+
addUserDictWord(word).catch((e) => console.error("[spell] addUserDictWord:", e));
|
|
1596
|
+
}
|
|
1597
|
+
function buildSuggestionList(word, sp) {
|
|
1598
|
+
const transposed = [];
|
|
1599
|
+
for (let i = 0; i < word.length - 1; i++) {
|
|
1600
|
+
const swapped = word.slice(0, i) + word[i + 1] + word[i] + word.slice(i + 2);
|
|
1601
|
+
if (swapped !== word && sp.correct(swapped) && !transposed.includes(swapped)) {
|
|
1602
|
+
transposed.push(swapped);
|
|
1816
1603
|
}
|
|
1817
1604
|
}
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1605
|
+
const nspellSugs = sp.suggest(word);
|
|
1606
|
+
const sugs = [];
|
|
1607
|
+
for (const s of [...transposed, ...nspellSugs]) {
|
|
1608
|
+
if (!sugs.includes(s))
|
|
1609
|
+
sugs.push(s);
|
|
1610
|
+
if (sugs.length >= 7)
|
|
1611
|
+
break;
|
|
1612
|
+
}
|
|
1613
|
+
return sugs;
|
|
1614
|
+
}
|
|
1615
|
+
function showSuggestionsMenu(parentDoc, x, y, items, extraDismissDocs = []) {
|
|
1616
|
+
parentDoc.getElementById("mailx-spell-menu")?.remove();
|
|
1617
|
+
const menu = parentDoc.createElement("div");
|
|
1618
|
+
menu.id = "mailx-spell-menu";
|
|
1619
|
+
menu.style.cssText = `
|
|
1620
|
+
position: fixed;
|
|
1621
|
+
left: ${x}px; top: ${y}px;
|
|
1622
|
+
z-index: 10000;
|
|
1623
|
+
background: var(--color-bg, #fff);
|
|
1624
|
+
color: var(--color-text, #222);
|
|
1625
|
+
border: 1px solid var(--color-border, #ccc);
|
|
1626
|
+
border-radius: 6px;
|
|
1627
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.18);
|
|
1628
|
+
padding: 4px 0;
|
|
1629
|
+
font: 13px system-ui, sans-serif;
|
|
1630
|
+
min-width: 180px;
|
|
1631
|
+
max-width: 320px;
|
|
1632
|
+
`;
|
|
1633
|
+
for (const it of items) {
|
|
1634
|
+
if (it.separator) {
|
|
1635
|
+
const sep = parentDoc.createElement("div");
|
|
1636
|
+
sep.style.cssText = "border-top:1px solid var(--color-border,#ddd); margin: 4px 0;";
|
|
1637
|
+
menu.appendChild(sep);
|
|
1638
|
+
continue;
|
|
1639
|
+
}
|
|
1640
|
+
const btn = parentDoc.createElement("button");
|
|
1641
|
+
btn.type = "button";
|
|
1642
|
+
btn.textContent = it.label;
|
|
1643
|
+
btn.style.cssText = `
|
|
1644
|
+
display: block; width: 100%; text-align: left;
|
|
1645
|
+
padding: 5px 12px; border: none; background: none;
|
|
1646
|
+
color: inherit; cursor: pointer; font: inherit;
|
|
1647
|
+
${it.emphasized ? "font-weight: 600;" : ""}
|
|
1648
|
+
`;
|
|
1649
|
+
btn.addEventListener("mouseenter", () => {
|
|
1650
|
+
btn.style.background = "var(--color-bg-hover, #eef)";
|
|
1651
|
+
});
|
|
1652
|
+
btn.addEventListener("mouseleave", () => {
|
|
1653
|
+
btn.style.background = "none";
|
|
1654
|
+
});
|
|
1655
|
+
btn.addEventListener("click", () => {
|
|
1656
|
+
try {
|
|
1657
|
+
it.action();
|
|
1658
|
+
} finally {
|
|
1659
|
+
menu.remove();
|
|
1850
1660
|
}
|
|
1851
|
-
|
|
1661
|
+
});
|
|
1662
|
+
menu.appendChild(btn);
|
|
1663
|
+
}
|
|
1664
|
+
parentDoc.body.appendChild(menu);
|
|
1665
|
+
const r = menu.getBoundingClientRect();
|
|
1666
|
+
if (r.right > window.innerWidth)
|
|
1667
|
+
menu.style.left = `${Math.max(8, window.innerWidth - r.width - 8)}px`;
|
|
1668
|
+
if (r.bottom > window.innerHeight)
|
|
1669
|
+
menu.style.top = `${Math.max(8, window.innerHeight - r.height - 8)}px`;
|
|
1670
|
+
const docs = [parentDoc, ...extraDismissDocs];
|
|
1671
|
+
const dismiss2 = (e) => {
|
|
1672
|
+
if (e.type === "keydown" && e.key !== "Escape")
|
|
1673
|
+
return;
|
|
1674
|
+
if (e.type === "mousedown" && menu.contains(e.target))
|
|
1675
|
+
return;
|
|
1676
|
+
menu.remove();
|
|
1677
|
+
for (const d of docs) {
|
|
1678
|
+
d.removeEventListener("mousedown", dismiss2, true);
|
|
1679
|
+
d.removeEventListener("keydown", dismiss2, true);
|
|
1852
1680
|
}
|
|
1681
|
+
};
|
|
1682
|
+
setTimeout(() => {
|
|
1683
|
+
for (const d of docs) {
|
|
1684
|
+
d.addEventListener("mousedown", dismiss2, true);
|
|
1685
|
+
d.addEventListener("keydown", dismiss2, true);
|
|
1686
|
+
}
|
|
1687
|
+
}, 0);
|
|
1688
|
+
}
|
|
1689
|
+
function getWordAtPoint(root, x, y) {
|
|
1690
|
+
const doc = root.ownerDocument || document;
|
|
1691
|
+
let node = null;
|
|
1692
|
+
let offset = 0;
|
|
1693
|
+
const winAny = doc.defaultView;
|
|
1694
|
+
if (typeof doc.caretPositionFromPoint === "function") {
|
|
1695
|
+
const pos = doc.caretPositionFromPoint(x, y);
|
|
1696
|
+
if (pos) {
|
|
1697
|
+
node = pos.offsetNode;
|
|
1698
|
+
offset = pos.offset;
|
|
1699
|
+
}
|
|
1700
|
+
} else if (typeof doc.caretRangeFromPoint === "function") {
|
|
1701
|
+
const range = doc.caretRangeFromPoint(x, y);
|
|
1702
|
+
if (range) {
|
|
1703
|
+
node = range.startContainer;
|
|
1704
|
+
offset = range.startOffset;
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
if (!node || node.nodeType !== Node.TEXT_NODE)
|
|
1708
|
+
return null;
|
|
1709
|
+
let walk = node;
|
|
1710
|
+
while (walk && walk !== root)
|
|
1711
|
+
walk = walk.parentNode;
|
|
1712
|
+
if (!walk)
|
|
1713
|
+
return null;
|
|
1714
|
+
let p = node.parentNode;
|
|
1715
|
+
while (p && p !== root) {
|
|
1716
|
+
if (p.nodeType === Node.ELEMENT_NODE && SKIP_TAGS.has(p.tagName))
|
|
1717
|
+
return null;
|
|
1718
|
+
p = p.parentNode;
|
|
1719
|
+
}
|
|
1720
|
+
const text = node.data;
|
|
1721
|
+
if (offset > text.length)
|
|
1722
|
+
offset = text.length;
|
|
1723
|
+
const isWordChar = (c) => /[\p{L}'’\-]/u.test(c);
|
|
1724
|
+
let start = offset;
|
|
1725
|
+
while (start > 0 && isWordChar(text[start - 1]))
|
|
1726
|
+
start--;
|
|
1727
|
+
let end = offset;
|
|
1728
|
+
while (end < text.length && isWordChar(text[end]))
|
|
1729
|
+
end++;
|
|
1730
|
+
if (end - start < MIN_WORD_LEN)
|
|
1731
|
+
return null;
|
|
1732
|
+
const word = text.slice(start, end);
|
|
1733
|
+
if (!/^[\p{L}]/u.test(word))
|
|
1734
|
+
return null;
|
|
1735
|
+
return { word, node, start, end };
|
|
1736
|
+
}
|
|
1737
|
+
var import_nspell, USER_DICT_KEY, MARKER_ATTR, MIN_WORD_LEN, SKIP_TAGS, spellPromise;
|
|
1738
|
+
var init_spellcheck_core = __esm({
|
|
1739
|
+
"client/compose/spellcheck-core.js"() {
|
|
1740
|
+
"use strict";
|
|
1741
|
+
import_nspell = __toESM(require_lib(), 1);
|
|
1742
|
+
init_api_client();
|
|
1743
|
+
USER_DICT_KEY = "mailx-user-dict";
|
|
1744
|
+
MARKER_ATTR = "data-mailx-spellerror";
|
|
1745
|
+
MIN_WORD_LEN = 3;
|
|
1746
|
+
SKIP_TAGS = /* @__PURE__ */ new Set(["BLOCKQUOTE", "CODE", "PRE", "A", "SCRIPT", "STYLE", "KBD", "SAMP", "VAR"]);
|
|
1747
|
+
spellPromise = null;
|
|
1853
1748
|
}
|
|
1854
1749
|
});
|
|
1855
1750
|
|
|
1856
|
-
//
|
|
1857
|
-
var
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1751
|
+
// client/lib/rmf-tiny.js
|
|
1752
|
+
var rmf_tiny_exports = {};
|
|
1753
|
+
__export(rmf_tiny_exports, {
|
|
1754
|
+
createTinyMceEditor: () => createTinyMceEditor
|
|
1755
|
+
});
|
|
1756
|
+
async function loadTinymce(opts) {
|
|
1757
|
+
try {
|
|
1758
|
+
const mod = await import(
|
|
1759
|
+
/* @vite-ignore */
|
|
1760
|
+
"tinymce"
|
|
1761
|
+
);
|
|
1762
|
+
const tinymce = mod.default || mod;
|
|
1763
|
+
await Promise.all([
|
|
1764
|
+
import("tinymce/themes/silver").catch(() => {
|
|
1765
|
+
}),
|
|
1766
|
+
import("tinymce/icons/default").catch(() => {
|
|
1767
|
+
}),
|
|
1768
|
+
import("tinymce/models/dom").catch(() => {
|
|
1769
|
+
}),
|
|
1770
|
+
import("tinymce/plugins/paste").catch(() => {
|
|
1771
|
+
}),
|
|
1772
|
+
import("tinymce/plugins/lists").catch(() => {
|
|
1773
|
+
}),
|
|
1774
|
+
import("tinymce/plugins/link").catch(() => {
|
|
1775
|
+
}),
|
|
1776
|
+
import("tinymce/plugins/table").catch(() => {
|
|
1777
|
+
}),
|
|
1778
|
+
import("tinymce/plugins/code").catch(() => {
|
|
1779
|
+
}),
|
|
1780
|
+
import("tinymce/plugins/image").catch(() => {
|
|
1781
|
+
})
|
|
1782
|
+
]);
|
|
1783
|
+
return tinymce;
|
|
1784
|
+
} catch {
|
|
1785
|
+
}
|
|
1786
|
+
const w = window;
|
|
1787
|
+
if (w.tinymce)
|
|
1788
|
+
return w.tinymce;
|
|
1789
|
+
if (!opts.cdnUrl) {
|
|
1790
|
+
throw new Error("rmf-tiny: tinymce not installed (npm install tinymce) and no cdnUrl supplied. See README for Android setup.");
|
|
1791
|
+
}
|
|
1792
|
+
const url = opts.apiKey && !opts.cdnUrl.includes("api-key") ? `${opts.cdnUrl}${opts.cdnUrl.includes("?") ? "&" : "?"}apiKey=${encodeURIComponent(opts.apiKey)}` : opts.cdnUrl;
|
|
1793
|
+
await new Promise((resolve, reject) => {
|
|
1794
|
+
const s = document.createElement("script");
|
|
1795
|
+
s.src = url;
|
|
1796
|
+
s.referrerPolicy = "origin";
|
|
1797
|
+
s.onload = () => resolve();
|
|
1798
|
+
s.onerror = () => reject(new Error(`rmf-tiny: failed to load TinyMCE from ${url}`));
|
|
1799
|
+
document.head.appendChild(s);
|
|
1800
|
+
});
|
|
1801
|
+
if (!w.tinymce)
|
|
1802
|
+
throw new Error("rmf-tiny: TinyMCE script loaded but window.tinymce is missing");
|
|
1803
|
+
return w.tinymce;
|
|
1804
|
+
}
|
|
1805
|
+
async function createTinyMceEditor(container2, opts = {}) {
|
|
1806
|
+
const tinymce = await loadTinymce(opts);
|
|
1807
|
+
const target = document.createElement("div");
|
|
1808
|
+
target.id = `rmf-tiny-${Math.random().toString(36).slice(2, 10)}`;
|
|
1809
|
+
target.style.cssText = "width:100%;height:100%;";
|
|
1810
|
+
container2.appendChild(target);
|
|
1811
|
+
const editor2 = await new Promise((resolve) => {
|
|
1812
|
+
tinymce.init({
|
|
1813
|
+
target,
|
|
1814
|
+
// Word-paste fidelity is the entire point of using TinyMCE here.
|
|
1815
|
+
// `paste_as_text: false` keeps formatting; powerpaste isn't in
|
|
1816
|
+
// OSS but the standard paste plugin handles Word reasonably well.
|
|
1817
|
+
// All free / OSS plugins bundled in the jsDelivr tinymce@6 script.
|
|
1818
|
+
// No premium plugins listed — listing one without an API key
|
|
1819
|
+
// triggers TinyMCE's upsell dialog on every load.
|
|
1820
|
+
plugins: "paste lists advlist link table code codesample image searchreplace autolink wordcount emoticons charmap insertdatetime quickbars nonbreaking directionality help",
|
|
1821
|
+
toolbar: [
|
|
1822
|
+
"undo redo | bold italic underline strikethrough | forecolor backcolor",
|
|
1823
|
+
"bullist numlist outdent indent | link table image code codesample | emoticons charmap | help"
|
|
1824
|
+
].join(" | "),
|
|
1825
|
+
// Include "tools" so wordcount and searchreplace are reachable.
|
|
1826
|
+
menubar: "file edit view insert format tools",
|
|
1827
|
+
// View menu — append zoom commands. fullscreen omitted: this editor
|
|
1828
|
+
// lives inside a compose iframe overlay that already fills its host
|
|
1829
|
+
// window; TinyMCE's fullscreen plugin re-positions the container in
|
|
1830
|
+
// ways that produce a blank body, so it's not in the plugin list.
|
|
1831
|
+
menu: {
|
|
1832
|
+
view: { title: "View", items: "code | visualaid visualchars visualblocks | preview | zoomIn zoomOut zoomReset" }
|
|
1833
|
+
},
|
|
1834
|
+
// Disable the "Get all features" upsell badge that TinyMCE 6+
|
|
1835
|
+
// injects automatically when no premium plugins are listed.
|
|
1836
|
+
promotion: false,
|
|
1837
|
+
// Floating toolbar that appears on text selection — keeps the
|
|
1838
|
+
// main toolbar uncluttered while exposing common formatters
|
|
1839
|
+
// where the cursor already is. quickbars_insert_toolbar:false
|
|
1840
|
+
// suppresses the second (insert-on-blank-line) popup which
|
|
1841
|
+
// clutters more than it helps in an email composer.
|
|
1842
|
+
quickbars_selection_toolbar: "bold italic underline | forecolor | quicklink blockquote",
|
|
1843
|
+
quickbars_insert_toolbar: false,
|
|
1844
|
+
quickbars_image_toolbar: "alignleft aligncenter alignright",
|
|
1845
|
+
// Code-sample dropdown languages. TinyMCE's default list omits
|
|
1846
|
+
// "Text" / plain — every option triggers syntax highlighting
|
|
1847
|
+
// which mangles unrelated paste content (Bob 2026-05-24).
|
|
1848
|
+
// Adding Text first so it's the default; rest are the modern
|
|
1849
|
+
// languages we actually paste.
|
|
1850
|
+
codesample_languages: [
|
|
1851
|
+
{ text: "Text", value: "text" },
|
|
1852
|
+
{ text: "HTML/XML", value: "markup" },
|
|
1853
|
+
{ text: "JavaScript", value: "javascript" },
|
|
1854
|
+
{ text: "TypeScript", value: "typescript" },
|
|
1855
|
+
{ text: "CSS", value: "css" },
|
|
1856
|
+
{ text: "JSON", value: "json" },
|
|
1857
|
+
{ text: "Python", value: "python" },
|
|
1858
|
+
{ text: "Java", value: "java" },
|
|
1859
|
+
{ text: "C", value: "c" },
|
|
1860
|
+
{ text: "C++", value: "cpp" },
|
|
1861
|
+
{ text: "C#", value: "csharp" },
|
|
1862
|
+
{ text: "Go", value: "go" },
|
|
1863
|
+
{ text: "Rust", value: "rust" },
|
|
1864
|
+
{ text: "Ruby", value: "ruby" },
|
|
1865
|
+
{ text: "PHP", value: "php" },
|
|
1866
|
+
{ text: "Shell", value: "bash" },
|
|
1867
|
+
{ text: "SQL", value: "sql" }
|
|
1868
|
+
],
|
|
1869
|
+
// WebView's native spell-check (red underlines, right-click
|
|
1870
|
+
// "Add to dictionary"). Free; same UX as Quill's spellcheck=true.
|
|
1871
|
+
// Premium tinymcespellchecker plugin would replace this with a
|
|
1872
|
+
// custom backend via spellchecker_rpc_url, but requires a
|
|
1873
|
+
// Tiny Cloud subscription.
|
|
1874
|
+
browser_spellcheck: true,
|
|
1875
|
+
// Right-click context menu DISABLED so WebView2's native menu
|
|
1876
|
+
// fires instead. We need the native menu because that's where
|
|
1877
|
+
// the browser shows spelling suggestions (TinyMCE's contextmenu
|
|
1878
|
+
// intercepts the right-click and replaces the menu — red
|
|
1879
|
+
// underlines appear but suggestions are unreachable). Trade-off:
|
|
1880
|
+
// lose TinyMCE's link / image / table quick actions. Standard
|
|
1881
|
+
// text formatting is still on the toolbar.
|
|
1882
|
+
contextmenu: false,
|
|
1883
|
+
statusbar: false,
|
|
1884
|
+
branding: false,
|
|
1885
|
+
license_key: "gpl",
|
|
1886
|
+
paste_data_images: true,
|
|
1887
|
+
// Permissive valid_elements — preserve as much of the source
|
|
1888
|
+
// formatting as possible. The default is more aggressive about
|
|
1889
|
+
// stripping. Empty string for `valid_elements` means accept
|
|
1890
|
+
// everything that the schema allows.
|
|
1891
|
+
paste_word_valid_elements: "@[style|class],-strong/b,-em/i,-u,-s,-sub,-sup,-strike,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,-blockquote,-table[border|cellpadding|cellspacing|width|height|class|style],-tr,-td[colspan|rowspan|width|height|class|style|valign|align|background|bgcolor],-th,-thead,-tbody,-tfoot,-pre,-br,-a[href|target|title],-img[src|alt|width|height|style|class]",
|
|
1892
|
+
paste_retain_style_properties: "color background background-color font-family font-size font-weight font-style text-decoration text-align padding padding-top padding-bottom padding-left padding-right margin margin-top margin-bottom margin-left margin-right border border-top border-bottom border-left border-right",
|
|
1893
|
+
// Auto-link bare URLs in pasted content. TinyMCE's `autolink`
|
|
1894
|
+
// plugin only fires on TYPED space/enter; URLs that arrive via
|
|
1895
|
+
// clipboard (browser address bar, terminal copy) come in as
|
|
1896
|
+
// plain text and stay un-linked. paste_preprocess runs on the
|
|
1897
|
+
// HTML the paste plugin produced.
|
|
1898
|
+
//
|
|
1899
|
+
// CRITICAL: skip auto-link when the content already contains
|
|
1900
|
+
// anchors. Naive regex over the whole content would wrap
|
|
1901
|
+
// `<a href="X">X</a>` in ANOTHER anchor (nested anchors are
|
|
1902
|
+
// invalid HTML and browsers split them, producing visible
|
|
1903
|
+
// junk). For HTML pastes that already have linked URLs (the
|
|
1904
|
+
// common case from a browser address bar or another mail
|
|
1905
|
+
// client), TinyMCE preserves them — no auto-link pass needed.
|
|
1906
|
+
// For plain-text pastes (no anchors present), wrap any bare
|
|
1907
|
+
// http(s)://… runs. Trailing sentence punctuation is excluded
|
|
1908
|
+
// from the URL.
|
|
1909
|
+
paste_preprocess: (_plugin, args) => {
|
|
1910
|
+
if (/<a[\s>]/i.test(args.content))
|
|
1911
|
+
return;
|
|
1912
|
+
args.content = args.content.replace(/(^|[\s(\[])((?:https?|ftp):\/\/[^\s<>"']+[^\s<>"'.,;:!?)\]])/gi, (_m, lead, url) => `${lead}<a href="${url}">${url}</a>`);
|
|
1913
|
+
},
|
|
1914
|
+
// Body font + quoted-reply styling. Without explicit rules for
|
|
1915
|
+
// <blockquote> and div.reply the editor iframe renders the
|
|
1916
|
+
// quoted block with no visual distinction from the user's own
|
|
1917
|
+
// text — pasted reply quotes vanish into a wall of unformatted
|
|
1918
|
+
// paragraphs (Bob 2026-05-12: "the body losing all formatting").
|
|
1919
|
+
// The styles below match Thunderbird/Outlook conventions: a
|
|
1920
|
+
// muted left-bar + indent on blockquotes, slight color shift on
|
|
1921
|
+
// the "On … wrote:" attribution, untouched typography for
|
|
1922
|
+
// everything else so genuine HTML formatting (bold / italic /
|
|
1923
|
+
// tables / inline color) still comes through verbatim.
|
|
1924
|
+
content_style: [
|
|
1925
|
+
// Dark-blue native caret bar. caret-shape:block produced a
|
|
1926
|
+
// column wider than the gap between letters AND caused the
|
|
1927
|
+
// caret to "scoot back to its old position" after an arrow
|
|
1928
|
+
// press (Chromium block-caret quirk with the arrow-key
|
|
1929
|
+
// selection adjustment, Bob 2026-05-24). Plain bar is
|
|
1930
|
+
// browser-default and behaves correctly with arrow keys;
|
|
1931
|
+
// just darken the color so it stays visible.
|
|
1932
|
+
"body { font-family: system-ui, sans-serif; font-size: 14px; caret-color: #0a2647; }",
|
|
1933
|
+
"blockquote { border-left: 3px solid #c0c8d0; margin: 0 0 0 4px; padding: 2px 0 2px 10px; color: #555; }",
|
|
1934
|
+
"div.reply { margin-top: 0.5em; }",
|
|
1935
|
+
"div.reply > p:first-child { color: #666; font-size: 0.95em; margin: 0 0 4px 0; }",
|
|
1936
|
+
"pre, code { font-family: ui-monospace, Consolas, Menlo, monospace; }"
|
|
1937
|
+
].join(" "),
|
|
1938
|
+
init_instance_callback: (ed) => resolve(ed),
|
|
1939
|
+
setup: (ed) => {
|
|
1940
|
+
if (opts.initialHtml)
|
|
1941
|
+
ed.on("init", () => ed.setContent(opts.initialHtml));
|
|
1942
|
+
const ZOOM_DEFAULT = 14;
|
|
1943
|
+
const ZOOM_STEP = 2;
|
|
1944
|
+
const ZOOM_MIN = 8;
|
|
1945
|
+
const ZOOM_MAX = 32;
|
|
1946
|
+
const ZOOM_STORAGE_KEY = "rmf-tiny:zoom-px";
|
|
1947
|
+
let zoomPx = ZOOM_DEFAULT;
|
|
1948
|
+
try {
|
|
1949
|
+
const stored = Number(localStorage.getItem(ZOOM_STORAGE_KEY));
|
|
1950
|
+
if (Number.isFinite(stored) && stored >= ZOOM_MIN && stored <= ZOOM_MAX) {
|
|
1951
|
+
zoomPx = stored;
|
|
1889
1952
|
}
|
|
1890
|
-
|
|
1953
|
+
} catch {
|
|
1891
1954
|
}
|
|
1955
|
+
const saveZoom = () => {
|
|
1956
|
+
try {
|
|
1957
|
+
localStorage.setItem(ZOOM_STORAGE_KEY, String(zoomPx));
|
|
1958
|
+
} catch {
|
|
1959
|
+
}
|
|
1960
|
+
};
|
|
1961
|
+
const applyZoom = () => {
|
|
1962
|
+
try {
|
|
1963
|
+
const body = ed.getBody();
|
|
1964
|
+
if (body)
|
|
1965
|
+
body.style.fontSize = `${zoomPx}px`;
|
|
1966
|
+
} catch {
|
|
1967
|
+
}
|
|
1968
|
+
};
|
|
1969
|
+
const bumpZoom = (delta) => {
|
|
1970
|
+
zoomPx = Math.max(ZOOM_MIN, Math.min(ZOOM_MAX, zoomPx + delta));
|
|
1971
|
+
applyZoom();
|
|
1972
|
+
saveZoom();
|
|
1973
|
+
};
|
|
1974
|
+
ed.ui.registry.addMenuItem("zoomIn", {
|
|
1975
|
+
text: "Zoom in",
|
|
1976
|
+
shortcut: "Ctrl+=",
|
|
1977
|
+
onAction: () => bumpZoom(ZOOM_STEP)
|
|
1978
|
+
});
|
|
1979
|
+
ed.ui.registry.addMenuItem("zoomOut", {
|
|
1980
|
+
text: "Zoom out",
|
|
1981
|
+
shortcut: "Ctrl+-",
|
|
1982
|
+
onAction: () => bumpZoom(-ZOOM_STEP)
|
|
1983
|
+
});
|
|
1984
|
+
ed.ui.registry.addMenuItem("zoomReset", {
|
|
1985
|
+
text: "Reset zoom",
|
|
1986
|
+
shortcut: "Ctrl+0",
|
|
1987
|
+
onAction: () => {
|
|
1988
|
+
zoomPx = ZOOM_DEFAULT;
|
|
1989
|
+
applyZoom();
|
|
1990
|
+
saveZoom();
|
|
1991
|
+
}
|
|
1992
|
+
});
|
|
1993
|
+
const installLinkEscape = () => {
|
|
1994
|
+
const doc = ed.getDoc();
|
|
1995
|
+
if (!doc || doc.__rmfLinkEscapeInstalled)
|
|
1996
|
+
return;
|
|
1997
|
+
doc.__rmfLinkEscapeInstalled = true;
|
|
1998
|
+
doc.addEventListener("beforeinput", (e) => {
|
|
1999
|
+
const kind = e.inputType || "";
|
|
2000
|
+
const isInsert = kind === "insertText" || kind === "insertCompositionText" || kind === "insertFromPaste" || kind === "insertFromDrop" || kind === "insertFromComposition";
|
|
2001
|
+
if (!isInsert)
|
|
2002
|
+
return;
|
|
2003
|
+
const sel = doc.getSelection();
|
|
2004
|
+
if (!sel || sel.rangeCount === 0)
|
|
2005
|
+
return;
|
|
2006
|
+
const rng = sel.getRangeAt(0);
|
|
2007
|
+
if (!rng.collapsed)
|
|
2008
|
+
return;
|
|
2009
|
+
const node = rng.startContainer;
|
|
2010
|
+
const a = node.nodeType === Node.ELEMENT_NODE ? node : node.parentNode;
|
|
2011
|
+
if (!a)
|
|
2012
|
+
return;
|
|
2013
|
+
const link = a.closest?.("a");
|
|
2014
|
+
if (!link)
|
|
2015
|
+
return;
|
|
2016
|
+
let tail;
|
|
2017
|
+
try {
|
|
2018
|
+
tail = rng.cloneRange();
|
|
2019
|
+
tail.setEndAfter(link);
|
|
2020
|
+
} catch {
|
|
2021
|
+
return;
|
|
2022
|
+
}
|
|
2023
|
+
if (tail.toString().length > 0)
|
|
2024
|
+
return;
|
|
2025
|
+
const after = doc.createRange();
|
|
2026
|
+
after.setStartAfter(link);
|
|
2027
|
+
after.collapse(true);
|
|
2028
|
+
sel.removeAllRanges();
|
|
2029
|
+
sel.addRange(after);
|
|
2030
|
+
}, true);
|
|
2031
|
+
};
|
|
2032
|
+
ed.on("init", installLinkEscape);
|
|
2033
|
+
ed.on("SetContent", installLinkEscape);
|
|
2034
|
+
ed.on("init", () => {
|
|
2035
|
+
try {
|
|
2036
|
+
const body = ed.getBody();
|
|
2037
|
+
if (body) {
|
|
2038
|
+
body.setAttribute("spellcheck", "true");
|
|
2039
|
+
body.setAttribute("lang", "en");
|
|
2040
|
+
}
|
|
2041
|
+
const doc = ed.getDoc();
|
|
2042
|
+
if (doc?.documentElement)
|
|
2043
|
+
doc.documentElement.setAttribute("lang", "en");
|
|
2044
|
+
} catch {
|
|
2045
|
+
}
|
|
2046
|
+
if (zoomPx !== ZOOM_DEFAULT)
|
|
2047
|
+
applyZoom();
|
|
2048
|
+
try {
|
|
2049
|
+
const doc = ed.getDoc();
|
|
2050
|
+
doc.addEventListener("wheel", (e) => {
|
|
2051
|
+
if (!e.ctrlKey)
|
|
2052
|
+
return;
|
|
2053
|
+
e.preventDefault();
|
|
2054
|
+
bumpZoom(e.deltaY < 0 ? ZOOM_STEP : -ZOOM_STEP);
|
|
2055
|
+
}, { passive: false });
|
|
2056
|
+
doc.addEventListener("keydown", (e) => {
|
|
2057
|
+
if (!(e.ctrlKey || e.metaKey))
|
|
2058
|
+
return;
|
|
2059
|
+
if (e.key === "=" || e.key === "+") {
|
|
2060
|
+
e.preventDefault();
|
|
2061
|
+
e.stopPropagation();
|
|
2062
|
+
bumpZoom(ZOOM_STEP);
|
|
2063
|
+
} else if (e.key === "-") {
|
|
2064
|
+
e.preventDefault();
|
|
2065
|
+
e.stopPropagation();
|
|
2066
|
+
bumpZoom(-ZOOM_STEP);
|
|
2067
|
+
} else if (e.key === "0") {
|
|
2068
|
+
e.preventDefault();
|
|
2069
|
+
e.stopPropagation();
|
|
2070
|
+
zoomPx = ZOOM_DEFAULT;
|
|
2071
|
+
applyZoom();
|
|
2072
|
+
}
|
|
2073
|
+
}, true);
|
|
2074
|
+
} catch {
|
|
2075
|
+
}
|
|
2076
|
+
});
|
|
1892
2077
|
}
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
2078
|
+
});
|
|
2079
|
+
});
|
|
2080
|
+
return {
|
|
2081
|
+
setHtml(html) {
|
|
2082
|
+
editor2.setContent(html);
|
|
2083
|
+
},
|
|
2084
|
+
getHtml() {
|
|
2085
|
+
return editor2.getContent();
|
|
2086
|
+
},
|
|
2087
|
+
getText() {
|
|
2088
|
+
return editor2.getContent({ format: "text" });
|
|
2089
|
+
},
|
|
2090
|
+
focus() {
|
|
2091
|
+
editor2.focus();
|
|
2092
|
+
},
|
|
2093
|
+
setCursor(pos) {
|
|
2094
|
+
const place = () => {
|
|
2095
|
+
const body = editor2.getBody();
|
|
2096
|
+
if (pos === 0) {
|
|
2097
|
+
const first = body.firstChild;
|
|
2098
|
+
if (first && first.nodeType === 1) {
|
|
2099
|
+
editor2.selection.setCursorLocation(first, 0);
|
|
2100
|
+
} else {
|
|
2101
|
+
editor2.selection.select(body, true);
|
|
2102
|
+
editor2.selection.collapse(true);
|
|
1908
2103
|
}
|
|
2104
|
+
editor2.focus();
|
|
2105
|
+
editor2.getWin()?.scrollTo(0, 0);
|
|
2106
|
+
} else {
|
|
2107
|
+
editor2.selection.select(body, true);
|
|
2108
|
+
editor2.selection.collapse(false);
|
|
2109
|
+
editor2.focus();
|
|
2110
|
+
editor2.selection.scrollIntoView();
|
|
1909
2111
|
}
|
|
2112
|
+
};
|
|
2113
|
+
try {
|
|
2114
|
+
place();
|
|
2115
|
+
editor2.getWin()?.requestAnimationFrame?.(() => {
|
|
2116
|
+
try {
|
|
2117
|
+
place();
|
|
2118
|
+
} catch {
|
|
2119
|
+
}
|
|
2120
|
+
});
|
|
2121
|
+
} catch {
|
|
1910
2122
|
}
|
|
1911
|
-
}
|
|
2123
|
+
},
|
|
2124
|
+
get root() {
|
|
2125
|
+
return editor2.getContainer();
|
|
2126
|
+
},
|
|
2127
|
+
on(event, handler) {
|
|
2128
|
+
editor2.on(event, handler);
|
|
2129
|
+
},
|
|
2130
|
+
off(event, handler) {
|
|
2131
|
+
editor2.off(event, handler);
|
|
2132
|
+
},
|
|
2133
|
+
nativeEditor: editor2
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
var init_rmf_tiny = __esm({
|
|
2137
|
+
"client/lib/rmf-tiny.js"() {
|
|
2138
|
+
"use strict";
|
|
1912
2139
|
}
|
|
1913
2140
|
});
|
|
1914
2141
|
|
|
@@ -1917,10 +2144,10 @@ var spellcheck_exports = {};
|
|
|
1917
2144
|
__export(spellcheck_exports, {
|
|
1918
2145
|
wireSpellcheck: () => wireSpellcheck
|
|
1919
2146
|
});
|
|
1920
|
-
async function
|
|
1921
|
-
if (
|
|
1922
|
-
return
|
|
1923
|
-
|
|
2147
|
+
async function getSpell2() {
|
|
2148
|
+
if (spellPromise2)
|
|
2149
|
+
return spellPromise2;
|
|
2150
|
+
spellPromise2 = (async () => {
|
|
1924
2151
|
const [affRes, dicRes] = await Promise.all([
|
|
1925
2152
|
fetch("../lib/dict/en.aff"),
|
|
1926
2153
|
fetch("../lib/dict/en.dic")
|
|
@@ -1929,9 +2156,9 @@ async function getSpell() {
|
|
|
1929
2156
|
throw new Error(`spellcheck: dict fetch failed (aff=${affRes.status} dic=${dicRes.status})`);
|
|
1930
2157
|
}
|
|
1931
2158
|
const [aff, dic] = await Promise.all([affRes.text(), dicRes.text()]);
|
|
1932
|
-
const sp = new
|
|
2159
|
+
const sp = new import_nspell2.default({ aff, dic });
|
|
1933
2160
|
try {
|
|
1934
|
-
const raw = localStorage.getItem(
|
|
2161
|
+
const raw = localStorage.getItem(USER_DICT_KEY2);
|
|
1935
2162
|
if (raw)
|
|
1936
2163
|
for (const w of JSON.parse(raw))
|
|
1937
2164
|
sp.add(w);
|
|
@@ -1943,7 +2170,7 @@ async function getSpell() {
|
|
|
1943
2170
|
sp.add(w);
|
|
1944
2171
|
let local = [];
|
|
1945
2172
|
try {
|
|
1946
|
-
const raw = localStorage.getItem(
|
|
2173
|
+
const raw = localStorage.getItem(USER_DICT_KEY2);
|
|
1947
2174
|
local = raw ? JSON.parse(raw) : [];
|
|
1948
2175
|
} catch {
|
|
1949
2176
|
local = [];
|
|
@@ -1955,22 +2182,22 @@ async function getSpell() {
|
|
|
1955
2182
|
}
|
|
1956
2183
|
try {
|
|
1957
2184
|
const merged = [.../* @__PURE__ */ new Set([...local, ...cloudArr])];
|
|
1958
|
-
localStorage.setItem(
|
|
2185
|
+
localStorage.setItem(USER_DICT_KEY2, JSON.stringify(merged));
|
|
1959
2186
|
} catch {
|
|
1960
2187
|
}
|
|
1961
2188
|
}).catch(() => {
|
|
1962
2189
|
});
|
|
1963
2190
|
return sp;
|
|
1964
2191
|
})();
|
|
1965
|
-
return
|
|
2192
|
+
return spellPromise2;
|
|
1966
2193
|
}
|
|
1967
|
-
function
|
|
2194
|
+
function addToUserDict2(word, sp) {
|
|
1968
2195
|
try {
|
|
1969
|
-
const raw = localStorage.getItem(
|
|
2196
|
+
const raw = localStorage.getItem(USER_DICT_KEY2);
|
|
1970
2197
|
const arr = raw ? JSON.parse(raw) : [];
|
|
1971
2198
|
if (!arr.includes(word)) {
|
|
1972
2199
|
arr.push(word);
|
|
1973
|
-
localStorage.setItem(
|
|
2200
|
+
localStorage.setItem(USER_DICT_KEY2, JSON.stringify(arr));
|
|
1974
2201
|
}
|
|
1975
2202
|
} catch {
|
|
1976
2203
|
}
|
|
@@ -1998,7 +2225,7 @@ function decorate(editor2, sp) {
|
|
|
1998
2225
|
const savedBodyScrollTop = body.scrollTop;
|
|
1999
2226
|
try {
|
|
2000
2227
|
editor2.undoManager?.ignore?.(() => {
|
|
2001
|
-
const old = body.querySelectorAll(`span[${
|
|
2228
|
+
const old = body.querySelectorAll(`span[${MARKER_ATTR2}]`);
|
|
2002
2229
|
for (const m of old) {
|
|
2003
2230
|
const parent2 = m.parentNode;
|
|
2004
2231
|
if (!parent2)
|
|
@@ -2012,7 +2239,7 @@ function decorate(editor2, sp) {
|
|
|
2012
2239
|
acceptNode(node) {
|
|
2013
2240
|
let p = node.parentNode;
|
|
2014
2241
|
while (p && p !== body) {
|
|
2015
|
-
if (p.nodeType === Node.ELEMENT_NODE &&
|
|
2242
|
+
if (p.nodeType === Node.ELEMENT_NODE && SKIP_TAGS2.has(p.tagName)) {
|
|
2016
2243
|
return NodeFilter.FILTER_REJECT;
|
|
2017
2244
|
}
|
|
2018
2245
|
p = p.parentNode;
|
|
@@ -2047,7 +2274,7 @@ function decorate(editor2, sp) {
|
|
|
2047
2274
|
WORD_RE.lastIndex = 0;
|
|
2048
2275
|
while ((m = WORD_RE.exec(text)) !== null) {
|
|
2049
2276
|
const word = m[0];
|
|
2050
|
-
if (word.length <
|
|
2277
|
+
if (word.length < MIN_WORD_LEN2)
|
|
2051
2278
|
continue;
|
|
2052
2279
|
const wStart = m.index, wEnd = m.index + word.length;
|
|
2053
2280
|
if (emailRanges.some(([s, e]) => wStart < e && wEnd > s))
|
|
@@ -2067,7 +2294,7 @@ function decorate(editor2, sp) {
|
|
|
2067
2294
|
range.setStart(h.node, h.start);
|
|
2068
2295
|
range.setEnd(h.node, h.end);
|
|
2069
2296
|
const span = doc.createElement("span");
|
|
2070
|
-
span.setAttribute(
|
|
2297
|
+
span.setAttribute(MARKER_ATTR2, "1");
|
|
2071
2298
|
try {
|
|
2072
2299
|
range.surroundContents(span);
|
|
2073
2300
|
} catch {
|
|
@@ -2172,7 +2399,7 @@ function installDecorationStyle(editor2) {
|
|
|
2172
2399
|
const style = doc.createElement("style");
|
|
2173
2400
|
style.id = "mailx-spell-style";
|
|
2174
2401
|
style.textContent = `
|
|
2175
|
-
span[${
|
|
2402
|
+
span[${MARKER_ATTR2}] {
|
|
2176
2403
|
text-decoration: underline wavy #d33;
|
|
2177
2404
|
text-decoration-skip-ink: none;
|
|
2178
2405
|
text-underline-offset: 2px;
|
|
@@ -2188,7 +2415,7 @@ function installSerializerFilter(editor2) {
|
|
|
2188
2415
|
return;
|
|
2189
2416
|
editor2.__mailxSpellSerializerWired = true;
|
|
2190
2417
|
try {
|
|
2191
|
-
editor2.serializer.addAttributeFilter(
|
|
2418
|
+
editor2.serializer.addAttributeFilter(MARKER_ATTR2, (nodes) => {
|
|
2192
2419
|
for (const node of nodes) {
|
|
2193
2420
|
if (typeof node.unwrap === "function")
|
|
2194
2421
|
node.unwrap();
|
|
@@ -2198,7 +2425,7 @@ function installSerializerFilter(editor2) {
|
|
|
2198
2425
|
console.warn("[spellcheck] serializer filter setup failed:", e);
|
|
2199
2426
|
}
|
|
2200
2427
|
}
|
|
2201
|
-
function
|
|
2428
|
+
function showSuggestionsMenu2(parentDoc, x, y, items) {
|
|
2202
2429
|
parentDoc.getElementById("mailx-spell-menu")?.remove();
|
|
2203
2430
|
const menu = parentDoc.createElement("div");
|
|
2204
2431
|
menu.id = "mailx-spell-menu";
|
|
@@ -2313,7 +2540,7 @@ function cleanupCorrected(editor2, sp) {
|
|
|
2313
2540
|
const activeSel = doc.getSelection();
|
|
2314
2541
|
if (activeSel && activeSel.rangeCount > 0 && !activeSel.isCollapsed)
|
|
2315
2542
|
return;
|
|
2316
|
-
const markers = body.querySelectorAll(`span[${
|
|
2543
|
+
const markers = body.querySelectorAll(`span[${MARKER_ATTR2}]`);
|
|
2317
2544
|
if (markers.length === 0)
|
|
2318
2545
|
return;
|
|
2319
2546
|
let caretMarker = null;
|
|
@@ -2321,7 +2548,7 @@ function cleanupCorrected(editor2, sp) {
|
|
|
2321
2548
|
if (sel && sel.rangeCount > 0) {
|
|
2322
2549
|
let p = sel.focusNode;
|
|
2323
2550
|
while (p && p !== body) {
|
|
2324
|
-
if (p.nodeType === Node.ELEMENT_NODE && p.hasAttribute?.(
|
|
2551
|
+
if (p.nodeType === Node.ELEMENT_NODE && p.hasAttribute?.(MARKER_ATTR2)) {
|
|
2325
2552
|
caretMarker = p;
|
|
2326
2553
|
break;
|
|
2327
2554
|
}
|
|
@@ -2395,7 +2622,7 @@ function wireSpellcheck(editor2) {
|
|
|
2395
2622
|
cleanupCorrected(editor2, sp);
|
|
2396
2623
|
}, CLEANUP_DEBOUNCE_MS);
|
|
2397
2624
|
};
|
|
2398
|
-
|
|
2625
|
+
getSpell2().then((loaded) => {
|
|
2399
2626
|
sp = loaded;
|
|
2400
2627
|
installDecorationStyle(editor2);
|
|
2401
2628
|
installSerializerFilter(editor2);
|
|
@@ -2411,7 +2638,7 @@ function wireSpellcheck(editor2) {
|
|
|
2411
2638
|
const target = e.target;
|
|
2412
2639
|
if (!target)
|
|
2413
2640
|
return;
|
|
2414
|
-
const marker = target.closest?.(`span[${
|
|
2641
|
+
const marker = target.closest?.(`span[${MARKER_ATTR2}]`);
|
|
2415
2642
|
if (!marker)
|
|
2416
2643
|
return;
|
|
2417
2644
|
const word = marker.textContent || "";
|
|
@@ -2458,7 +2685,7 @@ function wireSpellcheck(editor2) {
|
|
|
2458
2685
|
label: `Add "${word}" to dictionary`,
|
|
2459
2686
|
action: () => {
|
|
2460
2687
|
if (sp)
|
|
2461
|
-
|
|
2688
|
+
addToUserDict2(word, sp);
|
|
2462
2689
|
scheduleDecorate();
|
|
2463
2690
|
}
|
|
2464
2691
|
});
|
|
@@ -2470,22 +2697,22 @@ function wireSpellcheck(editor2) {
|
|
|
2470
2697
|
scheduleDecorate();
|
|
2471
2698
|
}
|
|
2472
2699
|
});
|
|
2473
|
-
|
|
2700
|
+
showSuggestionsMenu2(document, iframeRect.left + e.clientX, iframeRect.top + e.clientY, items);
|
|
2474
2701
|
}, true);
|
|
2475
2702
|
}
|
|
2476
|
-
var
|
|
2703
|
+
var import_nspell2, USER_DICT_KEY2, MARKER_ATTR2, DECORATE_DEBOUNCE_MS, CLEANUP_DEBOUNCE_MS, MIN_WORD_LEN2, SKIP_TAGS2, spellPromise2;
|
|
2477
2704
|
var init_spellcheck = __esm({
|
|
2478
2705
|
"client/compose/spellcheck.js"() {
|
|
2479
2706
|
"use strict";
|
|
2480
|
-
|
|
2707
|
+
import_nspell2 = __toESM(require_lib(), 1);
|
|
2481
2708
|
init_api_client();
|
|
2482
|
-
|
|
2483
|
-
|
|
2709
|
+
USER_DICT_KEY2 = "mailx-user-dict";
|
|
2710
|
+
MARKER_ATTR2 = "data-mailx-spellerror";
|
|
2484
2711
|
DECORATE_DEBOUNCE_MS = 1200;
|
|
2485
2712
|
CLEANUP_DEBOUNCE_MS = 300;
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2713
|
+
MIN_WORD_LEN2 = 3;
|
|
2714
|
+
SKIP_TAGS2 = /* @__PURE__ */ new Set(["BLOCKQUOTE", "CODE", "PRE", "A", "SCRIPT", "STYLE", "KBD", "SAMP", "VAR"]);
|
|
2715
|
+
spellPromise2 = null;
|
|
2489
2716
|
}
|
|
2490
2717
|
});
|
|
2491
2718
|
|
|
@@ -2952,6 +3179,72 @@ function createQuillEditor(container2) {
|
|
|
2952
3179
|
toolbar?.addHandler("link", function() {
|
|
2953
3180
|
openLinkForRange(q, q.getSelection() || { index: q.getLength() - 1, length: 0 });
|
|
2954
3181
|
});
|
|
3182
|
+
let _spellSp = null;
|
|
3183
|
+
Promise.resolve().then(() => (init_spellcheck_core(), spellcheck_core_exports)).then((m) => m.getSpell()).then((sp) => {
|
|
3184
|
+
_spellSp = sp;
|
|
3185
|
+
}).catch(() => {
|
|
3186
|
+
});
|
|
3187
|
+
q.root.addEventListener("contextmenu", async (e) => {
|
|
3188
|
+
try {
|
|
3189
|
+
if (!_spellSp)
|
|
3190
|
+
return;
|
|
3191
|
+
const sel = q.getSelection();
|
|
3192
|
+
if (sel && sel.length > 0)
|
|
3193
|
+
return;
|
|
3194
|
+
const core = await Promise.resolve().then(() => (init_spellcheck_core(), spellcheck_core_exports));
|
|
3195
|
+
const hit = core.getWordAtPoint(q.root, e.clientX, e.clientY);
|
|
3196
|
+
if (!hit)
|
|
3197
|
+
return;
|
|
3198
|
+
const { word, node, start, end } = hit;
|
|
3199
|
+
if (_spellSp.correct(word))
|
|
3200
|
+
return;
|
|
3201
|
+
e.preventDefault();
|
|
3202
|
+
e.stopPropagation();
|
|
3203
|
+
const sugs = core.buildSuggestionList(word, _spellSp);
|
|
3204
|
+
const items = [];
|
|
3205
|
+
if (sugs.length === 0) {
|
|
3206
|
+
items.push({ label: "(no suggestions)", action: () => {
|
|
3207
|
+
} });
|
|
3208
|
+
} else {
|
|
3209
|
+
for (const s of sugs) {
|
|
3210
|
+
items.push({
|
|
3211
|
+
label: s,
|
|
3212
|
+
emphasized: true,
|
|
3213
|
+
action: () => {
|
|
3214
|
+
try {
|
|
3215
|
+
const range = document.createRange();
|
|
3216
|
+
range.setStart(node, start);
|
|
3217
|
+
range.setEnd(node, end);
|
|
3218
|
+
const docSel = document.getSelection();
|
|
3219
|
+
if (docSel) {
|
|
3220
|
+
docSel.removeAllRanges();
|
|
3221
|
+
docSel.addRange(range);
|
|
3222
|
+
if (!document.execCommand("insertText", false, s)) {
|
|
3223
|
+
range.deleteContents();
|
|
3224
|
+
range.insertNode(document.createTextNode(s));
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
} catch {
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
});
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
items.push({ label: "", action: () => {
|
|
3234
|
+
}, separator: true });
|
|
3235
|
+
items.push({
|
|
3236
|
+
label: `Add "${word}" to dictionary`,
|
|
3237
|
+
action: () => core.addToUserDict(word, _spellSp)
|
|
3238
|
+
});
|
|
3239
|
+
items.push({
|
|
3240
|
+
label: "Ignore (this session)",
|
|
3241
|
+
action: () => _spellSp.add(word)
|
|
3242
|
+
});
|
|
3243
|
+
core.showSuggestionsMenu(document, e.clientX, e.clientY, items);
|
|
3244
|
+
} catch (err) {
|
|
3245
|
+
console.warn("[spellcheck] right-click handler error:", err?.message || err);
|
|
3246
|
+
}
|
|
3247
|
+
});
|
|
2955
3248
|
q.root.addEventListener("contextmenu", async (e) => {
|
|
2956
3249
|
try {
|
|
2957
3250
|
const sel = q.getSelection();
|