@mrrocket/sdk 0.14.0 → 0.16.0
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/CHANGELOG.md +10 -0
- package/README.md +45 -4
- package/dist/api-client.d.ts +67 -5
- package/dist/api-client.js +39 -8
- package/dist/api-client.js.map +1 -1
- package/dist/api.d.ts +4 -1
- package/dist/api.js +2 -1
- package/dist/api.js.map +1 -1
- package/dist/batch.d.ts +6 -4
- package/dist/batch.js +3 -3
- package/dist/batch.js.map +1 -1
- package/dist/browser/mrrocket-flows.js +2 -2
- package/dist/browser/mrrocket-forms.js +1 -1
- package/dist/browser/mrrocket-messenger.js +202 -46
- package/dist/browser/mrrocket-push.js +1 -1
- package/dist/browser/mrrocket-replay.js +3 -3
- package/dist/browser/mrrocket.js +1 -1
- package/dist/client.d.ts +10 -2
- package/dist/client.js +36 -15
- package/dist/client.js.map +1 -1
- package/dist/context.d.ts +5 -1
- package/dist/context.js.map +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/errors.d.ts +5 -1
- package/dist/errors.js +8 -2
- package/dist/errors.js.map +1 -1
- package/dist/flows-ui.js +48 -1
- package/dist/flows-ui.js.map +1 -1
- package/dist/flows-web.d.ts +2 -0
- package/dist/flows-web.js +4 -1
- package/dist/flows-web.js.map +1 -1
- package/dist/flows.d.ts +61 -1
- package/dist/flows.js +64 -21
- package/dist/flows.js.map +1 -1
- package/dist/forms.d.ts +4 -0
- package/dist/forms.js +4 -1
- package/dist/forms.js.map +1 -1
- package/dist/identity.d.ts +42 -0
- package/dist/identity.js +24 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js.map +1 -1
- package/dist/messenger-ui.d.ts +21 -6
- package/dist/messenger-ui.js +334 -80
- package/dist/messenger-ui.js.map +1 -1
- package/dist/messenger.d.ts +11 -3
- package/dist/messenger.js +9 -5
- package/dist/messenger.js.map +1 -1
- package/dist/push-web.d.ts +2 -13
- package/dist/push-web.js +4 -2
- package/dist/push-web.js.map +1 -1
- package/dist/react-native/index.d.cts +162 -14
- package/dist/react-native/index.d.ts +162 -14
- package/dist/react-native/index.js +552 -207
- package/dist/react-native/index.js.map +1 -1
- package/dist/replay.d.ts +4 -1
- package/dist/replay.js +8 -2
- package/dist/replay.js.map +1 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -1
- package/dist/styles-css.d.ts +1 -1
- package/dist/styles-css.js +200 -44
- package/dist/styles-css.js.map +1 -1
- package/dist/user-hash.d.ts +27 -0
- package/dist/user-hash.js +24 -0
- package/dist/user-hash.js.map +1 -0
- package/package.json +9 -1
package/dist/messenger-ui.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
// src/messenger-ui.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
BRAND_URL,
|
|
4
|
+
CHAT_FILES_PER_MESSAGE,
|
|
5
|
+
CHAT_FILE_MAX_BYTES,
|
|
6
|
+
CHAT_FILE_TYPES,
|
|
7
|
+
createApi,
|
|
8
|
+
inkOn,
|
|
9
|
+
replyLine
|
|
10
|
+
} from "./api-client.js";
|
|
3
11
|
import { injectStyles } from "./styles-css.js";
|
|
4
12
|
var POLL_OPEN_MS = 4e3;
|
|
5
13
|
var POLL_WAITING_MS = 8e3;
|
|
@@ -61,8 +69,15 @@ var I = {
|
|
|
61
69
|
dots: "M13 12a1 1 0 1 1-2 0 1 1 0 0 1 2 0zM13 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0zM13 19a1 1 0 1 1-2 0 1 1 0 0 1 2 0z",
|
|
62
70
|
expand: "M15 3h6v6M21 3l-7 7M3 21l7-7M9 21H3v-6",
|
|
63
71
|
collapse: "m14 10 7-7M20 10h-6V4M3 21l7-7M4 14h6v6",
|
|
64
|
-
minus: "M5 12h14"
|
|
72
|
+
minus: "M5 12h14",
|
|
73
|
+
down: "m6 9 6 6 6-6",
|
|
74
|
+
help: "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20zM9.1 9a3 3 0 0 1 5.8 1c0 2-3 3-3 3M12 17h.01",
|
|
75
|
+
clip: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48",
|
|
76
|
+
image: "M19 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zM9 11a2 2 0 1 0 0-4 2 2 0 0 0 0 4zM21 15l-3.09-3.09a2 2 0 0 0-2.82 0L6 21",
|
|
77
|
+
screen: "M20 3H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zM8 21h8M12 17v4"
|
|
65
78
|
};
|
|
79
|
+
var lineOf = (m) => m.body || (m.attachments?.length === 1 ? `Sent ${m.attachments[0].name}` : m.attachments?.length ? `Sent ${m.attachments.length} files` : "");
|
|
80
|
+
var canShoot = () => typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getDisplayMedia === "function";
|
|
66
81
|
var BUBBLE = "M7.9 20A9 9 0 1 0 4 16.1L2 22z";
|
|
67
82
|
var filled = (d, size) => {
|
|
68
83
|
const svg = document.createElementNS(SVG_NS, "svg");
|
|
@@ -73,6 +88,18 @@ var filled = (d, size) => {
|
|
|
73
88
|
svg.appendChild(path);
|
|
74
89
|
return svg;
|
|
75
90
|
};
|
|
91
|
+
var duo = (shape, cut, size, width = 2) => {
|
|
92
|
+
const svg = filled(shape, size);
|
|
93
|
+
const path = document.createElementNS(SVG_NS, "path");
|
|
94
|
+
for (const [k, v] of Object.entries({ d: cut, class: "ckw-cut", fill: "none", "stroke-width": String(width), "stroke-linecap": "round", "stroke-linejoin": "round" })) path.setAttribute(k, v);
|
|
95
|
+
svg.appendChild(path);
|
|
96
|
+
return svg;
|
|
97
|
+
};
|
|
98
|
+
var DOTS = "M8 12h.01M12 12h.01M16 12h.01";
|
|
99
|
+
var HOUSE = "M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z";
|
|
100
|
+
var SMILE = "M9.5 15.5c1.4 1 3.6 1 5 0";
|
|
101
|
+
var DISC = "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z";
|
|
102
|
+
var QMARK = "M9.4 9.2a2.7 2.7 0 0 1 5.2 1c0 1.8-2.6 2.5-2.6 2.5M12 16.8h.01";
|
|
76
103
|
var chime = /* @__PURE__ */ (() => {
|
|
77
104
|
let ctx = null;
|
|
78
105
|
const get = () => {
|
|
@@ -149,11 +176,11 @@ var mountMessenger = (o) => {
|
|
|
149
176
|
const start = (look) => {
|
|
150
177
|
if (real || gone) return;
|
|
151
178
|
const defined = Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
|
|
152
|
-
real = build({ ...look ?? {}, ...defined, accent: o.accent ?? look?.accent, theme: o.theme ?? look?.theme });
|
|
179
|
+
real = build({ ...look ?? {}, ...defined, accent: o.accent ?? look?.accent, theme: o.theme ?? look?.theme, branding: look?.branding ?? true });
|
|
153
180
|
for (const fn of queued.splice(0)) fn(real);
|
|
154
181
|
};
|
|
155
182
|
const timer = setTimeout(() => start(null), LOOK_WAIT_MS);
|
|
156
|
-
(o.client ?? createApi({ host: o.host, apiKey: o.apiKey, distinctId: o.distinctId(), user: o.user() })).messengerLook(ctrl.signal).then((look) => start(look)).catch(() => start(null)).finally(() => clearTimeout(timer));
|
|
183
|
+
(o.client ?? createApi({ host: o.host, apiKey: o.apiKey, distinctId: o.distinctId(), user: o.user(), userHash: o.userHash?.() })).messengerLook(ctrl.signal).then((look) => start(look)).catch(() => start(null)).finally(() => clearTimeout(timer));
|
|
157
184
|
return {
|
|
158
185
|
open: (v) => later((h) => h.open(v)),
|
|
159
186
|
close: () => real?.close(),
|
|
@@ -166,10 +193,11 @@ var mountMessenger = (o) => {
|
|
|
166
193
|
}
|
|
167
194
|
};
|
|
168
195
|
};
|
|
196
|
+
var PLACES = ["top-left", "top-center", "top-right", "middle-left", "middle-right", "bottom-left", "bottom-center", "bottom-right"];
|
|
169
197
|
var build = (o) => {
|
|
170
198
|
injectStyles();
|
|
171
|
-
const position = o.position ?? "bottom-right";
|
|
172
|
-
const title = o.title || "
|
|
199
|
+
const position = PLACES.find((p) => p === o.position) ?? "bottom-right";
|
|
200
|
+
const title = o.title || "Support";
|
|
173
201
|
const greeting = o.greeting || "Hi! How can we help?";
|
|
174
202
|
const askEmail = o.askEmail ?? true;
|
|
175
203
|
const label = o.label ?? "";
|
|
@@ -179,13 +207,20 @@ var build = (o) => {
|
|
|
179
207
|
const helpOn = o.help ?? true;
|
|
180
208
|
const soundOn = o.sound ?? true;
|
|
181
209
|
const previewOn = o.preview ?? true;
|
|
182
|
-
const api = () => o.client ?? createApi({ host: o.host, apiKey: o.apiKey, distinctId: o.distinctId(), user: o.user() });
|
|
183
|
-
const root = el("div", {
|
|
210
|
+
const api = () => o.client ?? createApi({ host: o.host, apiKey: o.apiKey, distinctId: o.distinctId(), user: o.user(), userHash: o.userHash?.() });
|
|
211
|
+
const root = el("div", {
|
|
212
|
+
class: "ckw-root",
|
|
213
|
+
"data-theme": o.theme ?? "auto",
|
|
214
|
+
"data-corners": o.corners === "soft" || o.corners === "square" ? o.corners : "round",
|
|
215
|
+
"data-font": o.font === "system" ? "system" : "site",
|
|
216
|
+
"data-header": o.header === "color" ? "color" : "plain"
|
|
217
|
+
});
|
|
184
218
|
if (o.container) root.setAttribute("data-contained", "");
|
|
185
219
|
if (o.accent) {
|
|
186
220
|
root.style.setProperty("--ckw-accent", o.accent);
|
|
187
221
|
root.style.setProperty("--ckw-accent-ink", inkOn(o.accent));
|
|
188
222
|
}
|
|
223
|
+
o.glow?.slice(0, 2).forEach((c, i) => /^#[0-9a-f]{6}$/i.test(c) && root.style.setProperty(`--ckw-glow${i + 2}`, c));
|
|
189
224
|
(o.container ?? document.body).appendChild(root);
|
|
190
225
|
let open = false;
|
|
191
226
|
let view = "home";
|
|
@@ -234,6 +269,13 @@ var build = (o) => {
|
|
|
234
269
|
if (id) writeSeen(id);
|
|
235
270
|
};
|
|
236
271
|
const avatar = (size) => el("span", { class: `ckw-msgr-av${size ? " ckw-msgr-av-sm" : ""}`, "aria-hidden": "true" }, initial);
|
|
272
|
+
const logo = el("span", { class: "ckw-msgr-logo", "aria-hidden": "true" });
|
|
273
|
+
if (o.logo) {
|
|
274
|
+
const img = el("img", { src: o.logo, alt: "" });
|
|
275
|
+
img.addEventListener("error", () => logo.replaceChildren(avatar("sm")));
|
|
276
|
+
logo.append(img);
|
|
277
|
+
} else logo.append(avatar("sm"));
|
|
278
|
+
const sky = el("div", { class: "ckw-msgr-sky", "aria-hidden": "true" });
|
|
237
279
|
const leaving = /* @__PURE__ */ new Map();
|
|
238
280
|
const enter = (node, parent = root) => {
|
|
239
281
|
clearTimeout(leaving.get(node));
|
|
@@ -262,8 +304,8 @@ var build = (o) => {
|
|
|
262
304
|
const fabIcons = el(
|
|
263
305
|
"span",
|
|
264
306
|
{ class: "ckw-msgr-fab-ic", "aria-hidden": "true" },
|
|
265
|
-
el("span", { class: "ckw-msgr-fab-i", "data-i": "chat" },
|
|
266
|
-
el("span", { class: "ckw-msgr-fab-i", "data-i": "x" }, icon(I.
|
|
307
|
+
el("span", { class: "ckw-msgr-fab-i", "data-i": "chat" }, duo(BUBBLE, DOTS, label ? 20 : 27, 2.6)),
|
|
308
|
+
el("span", { class: "ckw-msgr-fab-i", "data-i": "x" }, icon(I.down, label ? 18 : 24))
|
|
267
309
|
);
|
|
268
310
|
const fabCount = el("span", { class: "ckw-msgr-count", "aria-hidden": "true" });
|
|
269
311
|
fab.append(fabIcons);
|
|
@@ -285,7 +327,7 @@ var build = (o) => {
|
|
|
285
327
|
const m = unseen.at(-1);
|
|
286
328
|
if (!previewShown() || !m) return leave(prev);
|
|
287
329
|
hideTeaser(true);
|
|
288
|
-
const x = el("button", { type: "button", class: "ckw-msgr-preview-x", "aria-label": "Dismiss" }, icon(I.cross,
|
|
330
|
+
const x = el("button", { type: "button", class: "ckw-msgr-preview-x", "aria-label": "Dismiss" }, icon(I.cross, 14));
|
|
289
331
|
x.addEventListener("click", (e) => {
|
|
290
332
|
e.stopPropagation();
|
|
291
333
|
previewGone = true;
|
|
@@ -294,12 +336,11 @@ var build = (o) => {
|
|
|
294
336
|
const card = el(
|
|
295
337
|
"button",
|
|
296
338
|
{ type: "button", class: "ckw-msgr-preview-card" },
|
|
297
|
-
avatar(),
|
|
298
339
|
el(
|
|
299
340
|
"span",
|
|
300
341
|
{ class: "ckw-msgr-preview-main" },
|
|
301
342
|
el("span", { class: "ckw-msgr-preview-who" }, `${whoLabel[m.from]}${unseen.length > 1 ? ` \xB7 ${unseen.length} new` : ""}`),
|
|
302
|
-
el("span", { class: "ckw-msgr-preview-text" }, m
|
|
343
|
+
el("span", { class: "ckw-msgr-preview-text" }, lineOf(m))
|
|
303
344
|
)
|
|
304
345
|
);
|
|
305
346
|
card.addEventListener("click", () => show("messages"));
|
|
@@ -311,7 +352,7 @@ var build = (o) => {
|
|
|
311
352
|
const showTeaser = () => {
|
|
312
353
|
clearTimeout(teaserTimer);
|
|
313
354
|
if (open || previewShown() || !launcherOn) return;
|
|
314
|
-
const chat = el("button", { type: "button", class: "ckw-msgr-teaser-b", "data-main": "" }, icon(I.chat), el("span", {}, "Chat
|
|
355
|
+
const chat = el("button", { type: "button", class: "ckw-msgr-teaser-b", "data-main": "" }, icon(I.chat), el("span", {}, "Chat"));
|
|
315
356
|
chat.addEventListener("click", () => show("messages"));
|
|
316
357
|
const buttons = [chat];
|
|
317
358
|
if (helpOn && hasArticles) {
|
|
@@ -352,23 +393,24 @@ var build = (o) => {
|
|
|
352
393
|
const body = el("div", { class: "ckw-pop-body ckw-msgr-body" });
|
|
353
394
|
pop.addEventListener("click", () => chime.unlock());
|
|
354
395
|
const lead = el("span", { class: "ckw-msgr-lead" });
|
|
355
|
-
const
|
|
356
|
-
const
|
|
357
|
-
const
|
|
358
|
-
const b = el("button", { type: "button", class: "ckw-msgr-
|
|
396
|
+
const mid = el("span", { class: "ckw-msgr-mid" });
|
|
397
|
+
const tabs = el("nav", { class: "ckw-msgr-tabs", "aria-label": "Messenger" });
|
|
398
|
+
const tab = (v, name, line, shape, cut) => {
|
|
399
|
+
const b = el("button", { type: "button", class: "ckw-msgr-tab" }, el("span", { class: "ckw-msgr-tab-ic" }, icon(line, 22), duo(shape, cut, 22)), el("span", {}, name));
|
|
359
400
|
b.addEventListener("click", () => {
|
|
360
401
|
collection = null;
|
|
361
402
|
go(v);
|
|
362
403
|
});
|
|
363
404
|
return b;
|
|
364
405
|
};
|
|
365
|
-
const
|
|
366
|
-
const
|
|
406
|
+
const homeTab = tab("home", "Home", I.home, HOUSE, SMILE);
|
|
407
|
+
const chatTab = tab("messages", "Messages", I.chat, BUBBLE, DOTS);
|
|
408
|
+
const helpTab = tab("help", "Help", I.help, DISC, QMARK);
|
|
367
409
|
const tabCount = el("span", { class: "ckw-msgr-count ckw-msgr-count-tab", "aria-hidden": "true" });
|
|
368
410
|
const menuBtn = el("button", { type: "button", class: "ckw-msgr-ib", "aria-label": "More", "aria-haspopup": "menu", "aria-expanded": "false" }, icon(I.dots, 18));
|
|
369
411
|
const xBtn = el("button", { type: "button", class: "ckw-msgr-ib ckw-msgr-x", "aria-label": "Close messenger" }, icon(I.cross));
|
|
370
412
|
xBtn.addEventListener("click", () => close());
|
|
371
|
-
bar.append(lead,
|
|
413
|
+
bar.append(lead, mid, el("span", { class: "ckw-msgr-trail" }, menuBtn, xBtn));
|
|
372
414
|
const menu = el("div", { class: "ckw-msgr-menu", role: "menu" });
|
|
373
415
|
const menuOpen = () => menu.isConnected && !menu.hasAttribute("data-out");
|
|
374
416
|
const closeMenu = () => {
|
|
@@ -393,7 +435,6 @@ var build = (o) => {
|
|
|
393
435
|
menuItem(wide ? I.collapse : I.expand, wide ? "Collapse view" : "Expand view", () => {
|
|
394
436
|
wide = !wide;
|
|
395
437
|
pop.toggleAttribute("data-wide", wide);
|
|
396
|
-
requestAnimationFrame(placeMark);
|
|
397
438
|
})
|
|
398
439
|
);
|
|
399
440
|
menuBtn.setAttribute("aria-expanded", "true");
|
|
@@ -401,16 +442,6 @@ var build = (o) => {
|
|
|
401
442
|
menu.querySelector("button")?.focus();
|
|
402
443
|
});
|
|
403
444
|
pop.addEventListener("click", () => menuOpen() && closeMenu());
|
|
404
|
-
function placeMark() {
|
|
405
|
-
const current = view === "messages" ? chatTab : view !== "home" ? helpTab : null;
|
|
406
|
-
if (!current || !current.isConnected) return mark.removeAttribute("data-on");
|
|
407
|
-
const first = !mark.hasAttribute("data-on");
|
|
408
|
-
if (first) mark.setAttribute("data-still", "");
|
|
409
|
-
mark.style.width = `${current.offsetWidth}px`;
|
|
410
|
-
mark.style.transform = `translateX(${current.offsetLeft}px)`;
|
|
411
|
-
mark.setAttribute("data-on", "");
|
|
412
|
-
if (first) requestAnimationFrame(() => mark.removeAttribute("data-still"));
|
|
413
|
-
}
|
|
414
445
|
const drawBar = () => {
|
|
415
446
|
const back = (to, name) => {
|
|
416
447
|
const b = el("button", { type: "button", class: "ckw-msgr-ib", "aria-label": name }, icon(I.back, 18));
|
|
@@ -428,18 +459,20 @@ var build = (o) => {
|
|
|
428
459
|
go("help");
|
|
429
460
|
}, "All collections")
|
|
430
461
|
);
|
|
431
|
-
else
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
|
|
462
|
+
else if (view === "messages") lead.replaceChildren(back(() => go("home"), "Back to home"));
|
|
463
|
+
else if (view === "home") lead.replaceChildren(logo);
|
|
464
|
+
else lead.replaceChildren();
|
|
465
|
+
if (view === "messages") mid.replaceChildren(avatar("sm"), el("span", { class: "ckw-msgr-mid-t" }, el("b", {}, title), el("span", { class: "ckw-msgr-online" }, reply)));
|
|
466
|
+
else if (view === "help") mid.replaceChildren(el("b", {}, "Help"));
|
|
467
|
+
else mid.replaceChildren();
|
|
468
|
+
const items = helpOn && hasArticles !== false ? [homeTab, chatTab, helpTab] : [homeTab, chatTab];
|
|
469
|
+
if (tabs.childNodes.length !== items.length) tabs.replaceChildren(...items);
|
|
470
|
+
homeTab.setAttribute("aria-current", view === "home" ? "page" : "false");
|
|
438
471
|
chatTab.setAttribute("aria-current", view === "messages" ? "page" : "false");
|
|
439
472
|
helpTab.setAttribute("aria-current", view !== "home" && view !== "messages" ? "page" : "false");
|
|
440
473
|
if (unseen.length && view !== "messages") {
|
|
441
474
|
tabCount.textContent = String(Math.min(unseen.length, 9));
|
|
442
|
-
if (!tabCount.isConnected) chatTab.
|
|
475
|
+
if (!tabCount.isConnected) chatTab.firstChild.appendChild(tabCount);
|
|
443
476
|
} else tabCount.remove();
|
|
444
477
|
};
|
|
445
478
|
const form = el("form", { class: "ckw-msgr-compose" });
|
|
@@ -448,13 +481,164 @@ var build = (o) => {
|
|
|
448
481
|
const row = el("div", { class: "ckw-msgr-box" });
|
|
449
482
|
const text = el("textarea", { class: "ckw-textarea ckw-msgr-input", rows: "1", placeholder: "Write your message\u2026", "aria-label": "Message", maxlength: "4000" });
|
|
450
483
|
const send = el("button", { type: "submit", class: "ckw-msgr-send", "aria-label": "Send" }, icon(I.up));
|
|
451
|
-
|
|
484
|
+
const shots = canShoot();
|
|
485
|
+
const clip = el(
|
|
486
|
+
"button",
|
|
487
|
+
{ type: "button", class: "ckw-msgr-clip", "aria-label": shots ? "Attach a file or a screenshot" : "Attach a file", ...shots ? { "aria-haspopup": "menu", "aria-expanded": "false" } : {} },
|
|
488
|
+
icon(I.clip, 18)
|
|
489
|
+
);
|
|
490
|
+
row.append(text, el("div", { class: "ckw-msgr-tools" }, clip, el("span", { class: "ckw-msgr-sp" }), send));
|
|
452
491
|
row.addEventListener("click", (e) => e.target === row && text.focus());
|
|
453
492
|
form.append(row);
|
|
493
|
+
const picker = el("input", { type: "file", accept: CHAT_FILE_TYPES.join(","), multiple: "", hidden: "", tabindex: "-1", "aria-hidden": "true" });
|
|
494
|
+
form.append(picker);
|
|
495
|
+
const tray = el("div", { class: "ckw-msgr-files", role: "list", "aria-label": "Files to send" });
|
|
496
|
+
let picked = [];
|
|
497
|
+
const localUrls = /* @__PURE__ */ new Map();
|
|
498
|
+
const fileUrl = (id) => localUrls.get(id) ?? api().attachmentUrl(id);
|
|
499
|
+
const forget = (p) => {
|
|
500
|
+
p.ctrl.abort();
|
|
501
|
+
if (![...localUrls.values()].includes(p.local)) URL.revokeObjectURL(p.local);
|
|
502
|
+
};
|
|
503
|
+
const dropFile = (p) => {
|
|
504
|
+
picked = picked.filter((x) => x !== p);
|
|
505
|
+
forget(p);
|
|
506
|
+
};
|
|
507
|
+
const addFiles = (list2) => {
|
|
508
|
+
error = "";
|
|
509
|
+
for (const f of list2) {
|
|
510
|
+
const name = f.name || "screenshot.png";
|
|
511
|
+
if (picked.length >= CHAT_FILES_PER_MESSAGE) {
|
|
512
|
+
error = `Up to ${CHAT_FILES_PER_MESSAGE} files per message.`;
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
if (!CHAT_FILE_TYPES.includes(f.type)) {
|
|
516
|
+
error = "Only pictures (PNG, JPEG, WebP, GIF) and PDFs can be sent.";
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
if (f.size > CHAT_FILE_MAX_BYTES) {
|
|
520
|
+
error = `${name} is larger than 10 MB.`;
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
const p = { key: `local-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 7)}`, name, type: f.type, size: f.size, local: URL.createObjectURL(f), up: Promise.resolve(), ctrl: new AbortController() };
|
|
524
|
+
p.up = api().uploadChatFile(f, p.ctrl.signal).then(
|
|
525
|
+
(r) => {
|
|
526
|
+
p.id = r.id;
|
|
527
|
+
draw();
|
|
528
|
+
},
|
|
529
|
+
(e) => {
|
|
530
|
+
if (e.name === "AbortError") return;
|
|
531
|
+
dropFile(p);
|
|
532
|
+
error = `${p.name}: ${e.message}`;
|
|
533
|
+
draw();
|
|
534
|
+
}
|
|
535
|
+
);
|
|
536
|
+
picked.push(p);
|
|
537
|
+
}
|
|
538
|
+
if (open && view !== "messages") go("messages");
|
|
539
|
+
else draw();
|
|
540
|
+
text.focus();
|
|
541
|
+
};
|
|
542
|
+
let traySig = "";
|
|
543
|
+
const drawTray = () => {
|
|
544
|
+
const sig = picked.map((p) => `${p.key}:${p.id ? 1 : 0}`).join(",");
|
|
545
|
+
if (sig !== traySig) {
|
|
546
|
+
traySig = sig;
|
|
547
|
+
tray.replaceChildren(
|
|
548
|
+
...picked.map((p) => {
|
|
549
|
+
const x = el("button", { type: "button", class: "ckw-msgr-file-x", "aria-label": `Remove ${p.name}` }, icon(I.cross, 12));
|
|
550
|
+
x.addEventListener("click", () => {
|
|
551
|
+
dropFile(p);
|
|
552
|
+
draw();
|
|
553
|
+
text.focus();
|
|
554
|
+
});
|
|
555
|
+
const face = p.type.startsWith("image/") ? el("img", { src: p.local, alt: "" }) : el("span", { class: "ckw-msgr-file-doc" }, icon(I.doc, 18), el("span", {}, p.name));
|
|
556
|
+
return el("div", { class: "ckw-msgr-file", role: "listitem", title: p.name, "aria-label": p.id ? p.name : `${p.name}, uploading`, ...p.id ? {} : { "aria-busy": "true" } }, face, x);
|
|
557
|
+
})
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
if (!picked.length) tray.remove();
|
|
561
|
+
else if (!tray.isConnected) form.insertBefore(tray, row);
|
|
562
|
+
};
|
|
563
|
+
const shoot = async () => {
|
|
564
|
+
let stream;
|
|
565
|
+
try {
|
|
566
|
+
stream = await navigator.mediaDevices.getDisplayMedia({ video: { displaySurface: "browser" }, audio: false, preferCurrentTab: true, selfBrowserSurface: "include" });
|
|
567
|
+
} catch (e) {
|
|
568
|
+
if (e.name !== "NotAllowedError" && e.name !== "AbortError") {
|
|
569
|
+
error = "The screenshot did not work here. Attach a picture instead.";
|
|
570
|
+
draw();
|
|
571
|
+
}
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
root.style.visibility = "hidden";
|
|
575
|
+
let blob = null;
|
|
576
|
+
try {
|
|
577
|
+
const video = document.createElement("video");
|
|
578
|
+
video.muted = true;
|
|
579
|
+
video.playsInline = true;
|
|
580
|
+
video.srcObject = stream;
|
|
581
|
+
await video.play();
|
|
582
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
583
|
+
const v = video;
|
|
584
|
+
await new Promise((r) => v.requestVideoFrameCallback ? v.requestVideoFrameCallback(() => r()) : requestAnimationFrame(() => r()));
|
|
585
|
+
const canvas = document.createElement("canvas");
|
|
586
|
+
canvas.width = video.videoWidth;
|
|
587
|
+
canvas.height = video.videoHeight;
|
|
588
|
+
canvas.getContext("2d")?.drawImage(video, 0, 0);
|
|
589
|
+
const encode = (type, q) => new Promise((r) => canvas.toBlob(r, type, q));
|
|
590
|
+
blob = await encode("image/png");
|
|
591
|
+
if (blob && blob.size > 4.5 * 1024 * 1024) blob = await encode("image/jpeg", 0.9);
|
|
592
|
+
} catch {
|
|
593
|
+
blob = null;
|
|
594
|
+
} finally {
|
|
595
|
+
for (const t of stream.getTracks()) t.stop();
|
|
596
|
+
root.style.visibility = "";
|
|
597
|
+
}
|
|
598
|
+
if (!blob || !blob.size) {
|
|
599
|
+
error = "The screenshot did not work here. Attach a picture instead.";
|
|
600
|
+
return draw();
|
|
601
|
+
}
|
|
602
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 16).replace(/[:T]/g, "-");
|
|
603
|
+
addFiles([Object.assign(blob, { name: `screenshot-${stamp}.${blob.type === "image/png" ? "png" : "jpg"}` })]);
|
|
604
|
+
};
|
|
605
|
+
const amenu = el("div", { class: "ckw-msgr-menu ckw-msgr-amenu", role: "menu" });
|
|
606
|
+
const amenuOpen = () => amenu.isConnected && !amenu.hasAttribute("data-out");
|
|
607
|
+
const closeAmenu = () => {
|
|
608
|
+
clip.setAttribute("aria-expanded", "false");
|
|
609
|
+
leave(amenu, 120);
|
|
610
|
+
};
|
|
611
|
+
const amenuItem = (d, name, act) => {
|
|
612
|
+
const b = el("button", { type: "button", class: "ckw-msgr-menu-i", role: "menuitem" }, icon(d, 15), el("span", {}, name));
|
|
613
|
+
b.addEventListener("click", (e) => {
|
|
614
|
+
e.stopPropagation();
|
|
615
|
+
closeAmenu();
|
|
616
|
+
act();
|
|
617
|
+
});
|
|
618
|
+
return b;
|
|
619
|
+
};
|
|
620
|
+
clip.addEventListener("click", (e) => {
|
|
621
|
+
e.stopPropagation();
|
|
622
|
+
chime.unlock();
|
|
623
|
+
if (!shots) return picker.click();
|
|
624
|
+
if (amenuOpen()) return closeAmenu();
|
|
625
|
+
amenu.replaceChildren(
|
|
626
|
+
amenuItem(I.image, "Photo or file", () => picker.click()),
|
|
627
|
+
amenuItem(I.screen, "Screenshot of this page", () => void shoot())
|
|
628
|
+
);
|
|
629
|
+
clip.setAttribute("aria-expanded", "true");
|
|
630
|
+
enter(amenu, form);
|
|
631
|
+
amenu.querySelector("button")?.focus();
|
|
632
|
+
});
|
|
633
|
+
picker.addEventListener("change", () => {
|
|
634
|
+
addFiles([...picker.files ?? []]);
|
|
635
|
+
picker.value = "";
|
|
636
|
+
});
|
|
637
|
+
const brand = el("a", { class: "ckw-msgr-powered", href: BRAND_URL, target: "_blank", rel: "noopener" }, "Powered by ", el("b", {}, "MRRocket"));
|
|
454
638
|
const list = el("ol", { class: "ckw-msgs", "aria-live": "polite", "aria-label": "Messages" });
|
|
455
639
|
const needEmail = () => askEmail && !o.user()?.email && !thread?.hasEmail;
|
|
456
640
|
const emailOk = () => !email.value.trim() || EMAIL.test(email.value.trim());
|
|
457
|
-
const canSend = () => !!text.value.trim() && !pending && emailOk();
|
|
641
|
+
const canSend = () => (!!text.value.trim() || picked.length > 0) && !pending && emailOk();
|
|
458
642
|
const go = (v) => {
|
|
459
643
|
const changed = JSON.stringify(v) !== JSON.stringify(view);
|
|
460
644
|
view = v;
|
|
@@ -468,9 +652,26 @@ var build = (o) => {
|
|
|
468
652
|
} else if (v === "help") helpInput.focus();
|
|
469
653
|
schedule(0);
|
|
470
654
|
};
|
|
655
|
+
const picture = (src, alt) => {
|
|
656
|
+
const img = el("img", { src, alt, loading: "lazy" });
|
|
657
|
+
img.addEventListener("load", () => {
|
|
658
|
+
if (body.scrollHeight - body.scrollTop - body.clientHeight < img.clientHeight + 120) body.scrollTop = body.scrollHeight;
|
|
659
|
+
});
|
|
660
|
+
return img;
|
|
661
|
+
};
|
|
471
662
|
const bubble = (m, isPending = false) => {
|
|
472
|
-
const li = el("li", { class: "ckw-msg", "data-from": m.from === "visitor" ? "me" : "them" }, el("div", { class: "ckw-bubble" }, m.body));
|
|
663
|
+
const li = el("li", { class: "ckw-msg", "data-from": m.from === "visitor" ? "me" : "them" }, m.body || !m.attachments?.length ? el("div", { class: "ckw-bubble" }, m.body) : null);
|
|
473
664
|
if (isPending) li.setAttribute("aria-busy", "true");
|
|
665
|
+
if (m.attachments?.length)
|
|
666
|
+
li.append(
|
|
667
|
+
el(
|
|
668
|
+
"div",
|
|
669
|
+
{ class: "ckw-msg-files" },
|
|
670
|
+
...m.attachments.map(
|
|
671
|
+
(f) => f.type.startsWith("image/") ? el("a", { class: "ckw-msg-img", href: fileUrl(f.id), target: "_blank", rel: "noopener", title: f.name }, picture(fileUrl(f.id), f.name)) : el("a", { class: "ckw-msgr-cite", href: fileUrl(f.id), target: "_blank", rel: "noopener" }, icon(I.doc, 14), el("span", {}, f.name))
|
|
672
|
+
)
|
|
673
|
+
)
|
|
674
|
+
);
|
|
474
675
|
for (const a of m.articles ?? []) {
|
|
475
676
|
const chip = el("button", { type: "button", class: "ckw-msgr-cite" }, icon(I.doc, 14), el("span", {}, a.title));
|
|
476
677
|
chip.addEventListener("click", () => go({ article: a.id }));
|
|
@@ -535,29 +736,43 @@ var build = (o) => {
|
|
|
535
736
|
}
|
|
536
737
|
draw();
|
|
537
738
|
};
|
|
739
|
+
const askForm = el("form", { class: "ckw-msgr-askq" });
|
|
740
|
+
const askIn = el("input", { class: "ckw-msgr-askq-in", placeholder: "Ask a question", "aria-label": "Ask a question", maxlength: "4000", autocomplete: "off" });
|
|
741
|
+
const askGo = el("button", { type: "submit", class: "ckw-msgr-send", "aria-label": "Send", disabled: "" }, icon(I.up));
|
|
742
|
+
askForm.append(askIn, askGo);
|
|
743
|
+
askIn.addEventListener("input", () => askGo.disabled = !askIn.value.trim());
|
|
744
|
+
askForm.addEventListener("submit", (e) => {
|
|
745
|
+
e.preventDefault();
|
|
746
|
+
if (!askIn.value.trim()) return go("messages");
|
|
747
|
+
text.value = askIn.value;
|
|
748
|
+
askIn.value = "";
|
|
749
|
+
askGo.disabled = true;
|
|
750
|
+
go("messages");
|
|
751
|
+
if (canSend()) void submit();
|
|
752
|
+
});
|
|
538
753
|
const drawHome = () => {
|
|
754
|
+
const first = o.user()?.name?.trim().split(/\s+/)[0];
|
|
755
|
+
const rest = greeting.replace(/^(hi|hello|hey|hallo)( there)?\s*[!,.]*\s*/i, "").trim() || "How can we help you today?";
|
|
539
756
|
const hero = el(
|
|
540
|
-
"
|
|
541
|
-
{ class: "ckw-msgr-hero" },
|
|
542
|
-
el("span", { class: "ckw-msgr-
|
|
543
|
-
el("
|
|
544
|
-
el("span", { class: "ckw-msgr-online" }, reply)
|
|
757
|
+
"h2",
|
|
758
|
+
{ class: "ckw-msgr-hero ckw-msgr-hello" },
|
|
759
|
+
el("span", {}, first ? `Hi, ${first} ` : "Hi there ", el("span", { class: "ckw-msgr-wave", "aria-hidden": "true" }, "\u{1F44B}")),
|
|
760
|
+
el("span", { class: "ckw-msgr-hello-sub" }, rest)
|
|
545
761
|
);
|
|
546
762
|
const last = (thread?.messages ?? []).at(-1);
|
|
547
|
-
const parts = [hero];
|
|
763
|
+
const parts = [hero, askForm];
|
|
548
764
|
if (last) {
|
|
549
765
|
const recent = el(
|
|
550
766
|
"button",
|
|
551
|
-
{ type: "button", class: "ckw-msgr-card ckw-msgr-recent" },
|
|
552
|
-
el("span", { class: "ckw-msgr-card-label" }, "Recent message"),
|
|
767
|
+
{ type: "button", class: "ckw-msgr-card ckw-msgr-recent", "aria-label": `Recent message: ${lineOf(last)}` },
|
|
553
768
|
el(
|
|
554
769
|
"span",
|
|
555
770
|
{ class: "ckw-msgr-recent-row" },
|
|
556
|
-
avatar(),
|
|
771
|
+
avatar("sm"),
|
|
557
772
|
el(
|
|
558
773
|
"span",
|
|
559
774
|
{ class: "ckw-msgr-card-main" },
|
|
560
|
-
el("span", { class: "ckw-msgr-card-sub ckw-msgr-recent-text" }, last
|
|
775
|
+
el("span", { class: "ckw-msgr-card-sub ckw-msgr-recent-text" }, lineOf(last)),
|
|
561
776
|
el("span", { class: "ckw-msgr-card-meta" }, `${last.from === "visitor" ? "You" : whoLabel[last.from]} \xB7 ${timeAgo(last.at)}`)
|
|
562
777
|
),
|
|
563
778
|
unseen.length ? el("span", { class: "ckw-msgr-badge" }, String(unseen.length)) : icon(I.next, 14)
|
|
@@ -566,17 +781,9 @@ var build = (o) => {
|
|
|
566
781
|
recent.addEventListener("click", () => go("messages"));
|
|
567
782
|
parts.push(recent);
|
|
568
783
|
}
|
|
569
|
-
const msgCard = el(
|
|
570
|
-
"button",
|
|
571
|
-
{ type: "button", class: "ckw-msgr-card" },
|
|
572
|
-
el("span", { class: "ckw-msgr-card-main" }, el("b", {}, "Send us a message"), el("span", { class: "ckw-msgr-card-sub" }, reply)),
|
|
573
|
-
el("span", { class: "ckw-msgr-go" }, icon(I.send))
|
|
574
|
-
);
|
|
575
|
-
msgCard.addEventListener("click", () => go("messages"));
|
|
576
|
-
parts.push(msgCard);
|
|
577
784
|
if (helpOn && hasArticles !== false) {
|
|
578
|
-
const helpCard = el("div", { class: "ckw-msgr-card ckw-msgr-card-col" }
|
|
579
|
-
const top = (articles ?? []).slice(0,
|
|
785
|
+
const helpCard = el("div", { class: "ckw-msgr-card ckw-msgr-card-col" });
|
|
786
|
+
const top = (articles ?? []).slice(0, 3);
|
|
580
787
|
if (top.length) helpCard.append(...top.map(articleRow));
|
|
581
788
|
else if (articles === null) helpCard.append(el("div", { class: "ckw-skel ckw-msgr-skel" }));
|
|
582
789
|
parts.push(helpCard);
|
|
@@ -667,20 +874,13 @@ var build = (o) => {
|
|
|
667
874
|
let drawn = -1;
|
|
668
875
|
let listSig = "";
|
|
669
876
|
let listCount = -1;
|
|
670
|
-
const intro = () => el(
|
|
671
|
-
"div",
|
|
672
|
-
{ class: "ckw-msgr-intro" },
|
|
673
|
-
el("span", { class: "ckw-msgr-intro-av" }, avatar()),
|
|
674
|
-
el("span", { class: "ckw-msgr-intro-t" }, el("b", {}, title), el("span", { class: "ckw-msgr-online" }, reply))
|
|
675
|
-
);
|
|
676
|
-
const introNode = intro();
|
|
677
877
|
const suggestions = el("div", { class: "ckw-msgr-sugg" });
|
|
678
878
|
const drawMessages = () => {
|
|
679
879
|
const msgs = thread?.messages ?? [];
|
|
680
|
-
const parts = [
|
|
880
|
+
const parts = [];
|
|
681
881
|
if (!loaded) parts.push(el("div", { class: "ckw-skel" }));
|
|
682
882
|
else {
|
|
683
|
-
const sig = `${msgs.map((m) => m.id).join(",")}|${pending ? pending.body : ""}|${Math.floor(Date.now() / 6e4)}`;
|
|
883
|
+
const sig = `${msgs.map((m) => m.id).join(",")}|${pending ? `${pending.body}+${pending.attachments?.length ?? 0}` : ""}|${Math.floor(Date.now() / 6e4)}`;
|
|
684
884
|
if (sig !== listSig) {
|
|
685
885
|
listSig = sig;
|
|
686
886
|
const greet = el("li", { class: "ckw-msg", "data-from": "them" }, el("div", { class: "ckw-bubble" }, greeting));
|
|
@@ -716,8 +916,9 @@ var build = (o) => {
|
|
|
716
916
|
if (needEmail()) {
|
|
717
917
|
if (emailOk()) email.removeAttribute("aria-invalid");
|
|
718
918
|
else email.setAttribute("aria-invalid", "true");
|
|
719
|
-
if (!email.isConnected) form.insertBefore(email, row);
|
|
919
|
+
if (!email.isConnected) form.insertBefore(email, tray.isConnected ? tray : row);
|
|
720
920
|
} else email.remove();
|
|
921
|
+
drawTray();
|
|
721
922
|
send.disabled = !canSend();
|
|
722
923
|
};
|
|
723
924
|
const draw = () => {
|
|
@@ -728,11 +929,11 @@ var build = (o) => {
|
|
|
728
929
|
else if (view === "messages") drawMessages();
|
|
729
930
|
else if (view === "help") drawHelp();
|
|
730
931
|
else drawArticle(view.article);
|
|
731
|
-
const kids = [bar, body];
|
|
732
|
-
|
|
932
|
+
const kids = view === "home" ? [sky, bar, body] : [bar, body];
|
|
933
|
+
kids.push(view === "messages" ? form : tabs);
|
|
934
|
+
if (o.branding !== false) kids.push(brand);
|
|
733
935
|
if (menu.isConnected) kids.push(menu);
|
|
734
936
|
if (pop.childNodes.length !== kids.length || kids.some((k, i) => pop.childNodes[i] !== k)) pop.replaceChildren(...kids);
|
|
735
|
-
placeMark();
|
|
736
937
|
};
|
|
737
938
|
const tick = async () => {
|
|
738
939
|
ctrl?.abort();
|
|
@@ -764,20 +965,46 @@ var build = (o) => {
|
|
|
764
965
|
document.addEventListener("visibilitychange", onVisible);
|
|
765
966
|
const submit = async () => {
|
|
766
967
|
const msg = text.value.trim();
|
|
767
|
-
|
|
968
|
+
const files = picked;
|
|
969
|
+
if (!msg && !files.length || pending || !emailOk()) return;
|
|
768
970
|
chime.unlock();
|
|
769
|
-
|
|
971
|
+
for (const p of files) localUrls.set(p.key, p.local);
|
|
972
|
+
pending = {
|
|
973
|
+
id: "pending",
|
|
974
|
+
from: "visitor",
|
|
975
|
+
body: msg,
|
|
976
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
977
|
+
...files.length ? { attachments: files.map((p) => ({ id: p.key, name: p.name, type: p.type, size: p.size })) } : {}
|
|
978
|
+
};
|
|
770
979
|
text.value = "";
|
|
980
|
+
picked = [];
|
|
771
981
|
error = "";
|
|
772
982
|
draw();
|
|
983
|
+
await Promise.all(files.map((p) => p.up));
|
|
984
|
+
const ids = files.filter((p) => p.id).map((p) => p.id);
|
|
985
|
+
let sent = false;
|
|
773
986
|
try {
|
|
774
|
-
|
|
987
|
+
if (!msg && !ids.length) throw new Error(error || "The file could not be sent.");
|
|
988
|
+
take(
|
|
989
|
+
await api().sendChat({
|
|
990
|
+
body: msg,
|
|
991
|
+
email: needEmail() && email.value.trim() ? email.value.trim() : void 0,
|
|
992
|
+
url: window.location.href.slice(0, 500),
|
|
993
|
+
...ids.length ? { attachments: ids } : {},
|
|
994
|
+
// A little context for a bug report; the server reads browser and system from the user agent.
|
|
995
|
+
context: { viewport: `${window.innerWidth}\xD7${window.innerHeight}` }
|
|
996
|
+
})
|
|
997
|
+
);
|
|
998
|
+
sent = true;
|
|
775
999
|
} catch (e) {
|
|
776
1000
|
text.value = msg;
|
|
1001
|
+
picked = files.filter((p) => p.id);
|
|
777
1002
|
error = e.message;
|
|
778
1003
|
}
|
|
779
1004
|
pending = null;
|
|
1005
|
+
for (const p of files) localUrls.delete(p.key);
|
|
780
1006
|
draw();
|
|
1007
|
+
if (sent) for (const p of files) URL.revokeObjectURL(p.local);
|
|
781
1008
|
schedule();
|
|
782
1009
|
};
|
|
783
1010
|
form.addEventListener("submit", (e) => {
|
|
@@ -791,6 +1018,27 @@ var build = (o) => {
|
|
|
791
1018
|
}
|
|
792
1019
|
});
|
|
793
1020
|
text.addEventListener("input", () => send.disabled = !canSend());
|
|
1021
|
+
text.addEventListener("paste", (e) => {
|
|
1022
|
+
const fs = [...e.clipboardData?.files ?? []];
|
|
1023
|
+
if (!fs.length) return;
|
|
1024
|
+
e.preventDefault();
|
|
1025
|
+
addFiles(fs);
|
|
1026
|
+
});
|
|
1027
|
+
const hasFiles = (e) => !!e.dataTransfer && [...e.dataTransfer.types].includes("Files");
|
|
1028
|
+
pop.addEventListener("dragover", (e) => {
|
|
1029
|
+
if (!hasFiles(e)) return;
|
|
1030
|
+
e.preventDefault();
|
|
1031
|
+
pop.setAttribute("data-drop", "");
|
|
1032
|
+
});
|
|
1033
|
+
pop.addEventListener("dragleave", (e) => {
|
|
1034
|
+
if (!e.relatedTarget || !pop.contains(e.relatedTarget)) pop.removeAttribute("data-drop");
|
|
1035
|
+
});
|
|
1036
|
+
pop.addEventListener("drop", (e) => {
|
|
1037
|
+
if (!hasFiles(e)) return;
|
|
1038
|
+
e.preventDefault();
|
|
1039
|
+
pop.removeAttribute("data-drop");
|
|
1040
|
+
addFiles([...e.dataTransfer?.files ?? []]);
|
|
1041
|
+
});
|
|
794
1042
|
email.addEventListener("input", () => draw());
|
|
795
1043
|
function show(v = "home", still = false) {
|
|
796
1044
|
const was = open;
|
|
@@ -820,9 +1068,13 @@ var build = (o) => {
|
|
|
820
1068
|
schedule();
|
|
821
1069
|
if (launcherOn) fab.focus();
|
|
822
1070
|
}
|
|
1071
|
+
pop.addEventListener("click", () => amenuOpen() && closeAmenu());
|
|
823
1072
|
const onKey = (e) => {
|
|
824
1073
|
if (e.key !== "Escape" || !open) return;
|
|
825
|
-
if (
|
|
1074
|
+
if (amenuOpen()) {
|
|
1075
|
+
closeAmenu();
|
|
1076
|
+
clip.focus();
|
|
1077
|
+
} else if (menuOpen()) {
|
|
826
1078
|
closeMenu();
|
|
827
1079
|
menuBtn.focus();
|
|
828
1080
|
} else close();
|
|
@@ -841,6 +1093,8 @@ var build = (o) => {
|
|
|
841
1093
|
clearTimeout(teaserTimer);
|
|
842
1094
|
for (const t of leaving.values()) clearTimeout(t);
|
|
843
1095
|
ctrl?.abort();
|
|
1096
|
+
for (const p of picked) forget(p);
|
|
1097
|
+
for (const u of localUrls.values()) URL.revokeObjectURL(u);
|
|
844
1098
|
document.removeEventListener("keydown", onKey);
|
|
845
1099
|
document.removeEventListener("visibilitychange", onVisible);
|
|
846
1100
|
root.remove();
|