@auxilium/datalynk-client 1.6.2 → 1.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/api.d.ts +9 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/auth.d.ts +6 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/chat-gui.d.ts +11 -0
- package/dist/chat-gui.d.ts.map +1 -1
- package/dist/chat-gui.view.d.ts +3 -1
- package/dist/chat-gui.view.d.ts.map +1 -1
- package/dist/chat.cjs +212 -43
- package/dist/chat.d.ts +13 -0
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.mjs +212 -43
- package/dist/index.cjs +455 -104
- package/dist/index.mjs +455 -104
- package/dist/offline-auth.d.ts +68 -4
- package/dist/offline-auth.d.ts.map +1 -1
- package/dist/service.worker.mjs +156 -40
- package/dist/slice.d.ts.map +1 -1
- package/dist/utils.d.ts +13 -0
- package/dist/utils.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/chat.mjs
CHANGED
|
@@ -1390,6 +1390,9 @@ class Chat {
|
|
|
1390
1390
|
__publicField(this, "socketUnsubscribe");
|
|
1391
1391
|
__publicField(this, "typingTimers", /* @__PURE__ */ new Map());
|
|
1392
1392
|
__publicField(this, "initialized", false);
|
|
1393
|
+
__publicField(this, "resyncing");
|
|
1394
|
+
__publicField(this, "readVisibility", () => false);
|
|
1395
|
+
__publicField(this, "directorySeq", 0);
|
|
1393
1396
|
this.api = api;
|
|
1394
1397
|
}
|
|
1395
1398
|
async initialize(force = false) {
|
|
@@ -1415,14 +1418,12 @@ class Chat {
|
|
|
1415
1418
|
}
|
|
1416
1419
|
async loadDirectory(search = "", cursor) {
|
|
1417
1420
|
const query = search.trim();
|
|
1418
|
-
|
|
1419
|
-
this.users$.next([]);
|
|
1420
|
-
return [];
|
|
1421
|
-
}
|
|
1421
|
+
const seq = ++this.directorySeq;
|
|
1422
1422
|
const response = await this.run({
|
|
1423
|
-
"$/chat/directory": { query, cursor, limit:
|
|
1423
|
+
"$/chat/directory": { query, cursor, limit: 10 }
|
|
1424
1424
|
});
|
|
1425
1425
|
const users = response.users || [];
|
|
1426
|
+
if (seq !== this.directorySeq) return users;
|
|
1426
1427
|
this.users$.next(users);
|
|
1427
1428
|
const selected = this.selectedUser$.value;
|
|
1428
1429
|
const replacement = selected && users.find((user) => user.id === selected.id);
|
|
@@ -1452,7 +1453,8 @@ class Chat {
|
|
|
1452
1453
|
"$/chat/history": { userId, before, limit: 75 }
|
|
1453
1454
|
});
|
|
1454
1455
|
const messages = (response.messages || []).map((message) => ({ ...message, status: "sent" }));
|
|
1455
|
-
this.
|
|
1456
|
+
if (this.selectedUser$.value && this.selectedUser$.value.id !== userId) return messages;
|
|
1457
|
+
this.messages$.next(this.sortMessages(this.mergeMessages(messages, this.messages$.value)));
|
|
1456
1458
|
this.hasMore$.next(Boolean(response.hasMore));
|
|
1457
1459
|
return messages;
|
|
1458
1460
|
} finally {
|
|
@@ -1495,7 +1497,15 @@ class Chat {
|
|
|
1495
1497
|
async setTyping(userId, typing) {
|
|
1496
1498
|
await this.run({ "$/chat/typing": { userId, typing } }, true);
|
|
1497
1499
|
}
|
|
1500
|
+
/** Only the visible chat window may acknowledge messages as seen. */
|
|
1501
|
+
setReadVisibility(visible) {
|
|
1502
|
+
this.readVisibility = visible;
|
|
1503
|
+
return () => {
|
|
1504
|
+
if (this.readVisibility === visible) this.readVisibility = () => false;
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1498
1507
|
async markLatestRead() {
|
|
1508
|
+
if (!this.readVisibility()) return;
|
|
1499
1509
|
const peer = this.selectedUser$.value;
|
|
1500
1510
|
const messages = this.messages$.value;
|
|
1501
1511
|
const latest = [...messages].reverse().find((message) => message.senderId === (peer == null ? void 0 : peer.id) && !message.id.startsWith("pending:"));
|
|
@@ -1520,17 +1530,43 @@ class Chat {
|
|
|
1520
1530
|
() => this.api.socket.send({ chat: { subscribe: true } })
|
|
1521
1531
|
);
|
|
1522
1532
|
}
|
|
1533
|
+
/**
|
|
1534
|
+
* Catch up after every (re)subscribe. Events published while the socket was down
|
|
1535
|
+
* (up to the reconnect delay plus ticket fetch) are never replayed, so reload the
|
|
1536
|
+
* inbox and the newest page of the open thread from the API.
|
|
1537
|
+
*/
|
|
1538
|
+
resync() {
|
|
1539
|
+
if (this.resyncing) return this.resyncing;
|
|
1540
|
+
this.resyncing = (async () => {
|
|
1541
|
+
var _a;
|
|
1542
|
+
try {
|
|
1543
|
+
await this.loadConversations();
|
|
1544
|
+
const peer = this.selectedUser$.value;
|
|
1545
|
+
if (!peer) return;
|
|
1546
|
+
const response = await this.run({ "$/chat/history": { userId: peer.id, limit: 75 } }, true);
|
|
1547
|
+
if (((_a = this.selectedUser$.value) == null ? void 0 : _a.id) !== peer.id) return;
|
|
1548
|
+
const latest = (response.messages || []).map((message) => ({ ...message, status: "sent" }));
|
|
1549
|
+
this.messages$.next(this.sortMessages(this.mergeMessages(latest, this.messages$.value)));
|
|
1550
|
+
await this.markLatestRead();
|
|
1551
|
+
} catch {
|
|
1552
|
+
} finally {
|
|
1553
|
+
this.resyncing = void 0;
|
|
1554
|
+
}
|
|
1555
|
+
})();
|
|
1556
|
+
return this.resyncing;
|
|
1557
|
+
}
|
|
1523
1558
|
onSocketEvent(event) {
|
|
1524
1559
|
var _a, _b, _c, _d, _e;
|
|
1525
1560
|
if (!(event == null ? void 0 : event.type) || event.type.indexOf("chat.") !== 0) return;
|
|
1526
|
-
if (event.type === "chat.
|
|
1561
|
+
if (event.type === "chat.subscribed") {
|
|
1562
|
+
void this.resync();
|
|
1563
|
+
} else if (event.type === "chat.message") {
|
|
1527
1564
|
const message = (_a = event.data) == null ? void 0 : _a.message;
|
|
1528
1565
|
const selected = this.selectedUser$.value;
|
|
1529
1566
|
if (message && selected && (message.senderId === selected.id || message.recipientId === selected.id)) {
|
|
1530
1567
|
this.upsertMessage({ ...message, status: "sent" });
|
|
1531
|
-
if (message.senderId === selected.id) void this.markLatestRead();
|
|
1532
1568
|
}
|
|
1533
|
-
void this.loadConversations();
|
|
1569
|
+
void this.loadConversations().then(() => this.markLatestRead()).catch(() => void 0);
|
|
1534
1570
|
} else if (event.type === "chat.read") {
|
|
1535
1571
|
this.applyReadReceipt(event.data);
|
|
1536
1572
|
void this.loadConversations();
|
|
@@ -1584,6 +1620,12 @@ class Chat {
|
|
|
1584
1620
|
this.messages$.next(next);
|
|
1585
1621
|
}
|
|
1586
1622
|
}
|
|
1623
|
+
/** Stream order for persisted messages; optimistic/failed sends stay at the end in send order. */
|
|
1624
|
+
sortMessages(messages) {
|
|
1625
|
+
const persisted = messages.filter((message) => !message.id.startsWith("pending:"));
|
|
1626
|
+
const pending = messages.filter((message) => message.id.startsWith("pending:"));
|
|
1627
|
+
return [...persisted.sort((a, b) => this.compareStreamIds(a.id, b.id)), ...pending];
|
|
1628
|
+
}
|
|
1587
1629
|
mergeMessages(first, second) {
|
|
1588
1630
|
const out = [];
|
|
1589
1631
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -1622,16 +1664,23 @@ class Chat {
|
|
|
1622
1664
|
}
|
|
1623
1665
|
}
|
|
1624
1666
|
const icon = (path) => `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="${path}"/></svg>`;
|
|
1667
|
+
const SOUND_ON_ICON = "M3 9v6h4l5 5V4L7 9H3zm13.5 3A4.5 4.5 0 0 0 14 7.97v8.05A4.47 4.47 0 0 0 16.5 12zM14 3.23v2.06a7 7 0 0 1 0 13.42v2.06A9 9 0 0 0 14 3.23z";
|
|
1668
|
+
const SOUND_OFF_ICON = "M16.5 12A4.5 4.5 0 0 0 14 7.97v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51A8.8 8.8 0 0 0 21 12a9 9 0 0 0-7-8.77v2.06A7 7 0 0 1 19 12zM4.27 3 3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06a9 9 0 0 0 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4 9.91 6.09 12 8.18V4z";
|
|
1625
1669
|
const CHAT_HTML = `
|
|
1626
1670
|
<section class="dl-chat" role="dialog" aria-label="Datalynk Chat">
|
|
1627
1671
|
<header class="dl-chat__titlebar">
|
|
1628
|
-
<div class="dl-chat__brand"><span class="dl-chat__mark">${icon("M4 5h16v11H9l-5 4V5zm3 4v2h2V9H7zm4 0v2h2V9h-2zm4 0v2h2V9h-2z")}</span><strong>Chat</strong></div>
|
|
1672
|
+
<div class="dl-chat__brand"><span class="dl-chat__mark">${icon("M4 5h16v11H9l-5 4V5zm3 4v2h2V9H7zm4 0v2h2V9h-2zm4 0v2h2V9h-2z")}</span><strong>Chat</strong><span class="dl-chat__title-unread" aria-live="polite" hidden></span></div>
|
|
1629
1673
|
<div class="dl-chat__window-actions">
|
|
1674
|
+
<button class="dl-chat__icon-button dl-chat__sound" type="button" aria-pressed="false" aria-label="Mute message sound" title="Mute message sound">${icon(SOUND_ON_ICON)}</button>
|
|
1630
1675
|
<button class="dl-chat__icon-button dl-chat__popout" type="button" aria-label="Open chat in a separate window" title="Open in new window">${icon("M14 3h7v7h-2V6.41l-9.29 9.3-1.42-1.42 9.3-9.29H14V3zM5 5h6v2H5v12h12v-6h2v8H3V5h2z")}</button>
|
|
1631
1676
|
<button class="dl-chat__icon-button dl-chat__minimize" type="button" aria-label="Minimize chat" title="Minimize">${icon("M5 11h14v2H5z")}</button>
|
|
1632
1677
|
<button class="dl-chat__icon-button dl-chat__close" type="button" aria-label="Close chat" title="Close">${icon("M6.4 5 12 10.6 17.6 5 19 6.4 13.4 12 19 17.6 17.6 19 12 13.4 6.4 19 5 17.6 10.6 12 5 6.4z")}</button>
|
|
1633
1678
|
</div>
|
|
1634
1679
|
</header>
|
|
1680
|
+
<button class="dl-chat__restore" type="button" aria-label="Restore chat" hidden>
|
|
1681
|
+
<span class="dl-chat__mark">${icon("M4 5h16v11H9l-5 4V5zm3 4v2h2V9H7zm4 0v2h2V9h-2zm4 0v2h2V9h-2z")}</span>
|
|
1682
|
+
<span><strong>Chat</strong><small>Open conversations</small></span>
|
|
1683
|
+
</button>
|
|
1635
1684
|
<div class="dl-chat__connection" role="status" hidden></div>
|
|
1636
1685
|
<div class="dl-chat__error" role="alert" hidden></div>
|
|
1637
1686
|
<div class="dl-chat__body">
|
|
@@ -1677,25 +1726,39 @@ const CHAT_CSS = `
|
|
|
1677
1726
|
:host{all:initial;position:fixed;inset:0;z-index:2147483000;pointer-events:none;color:#17242b;font-family:"Segoe UI",Tahoma,Arial,sans-serif}
|
|
1678
1727
|
*{box-sizing:border-box}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
|
|
1679
1728
|
button,input,textarea{font:inherit}
|
|
1680
|
-
.dl-chat{--teal
|
|
1729
|
+
.dl-chat{--teal:var(--primary,#3f6f85);--teal-fore:var(--primary-fore,#fff);--teal-deep:color-mix(in srgb,var(--teal) 78%,#000);--teal-soft:color-mix(in srgb,var(--teal) 12%,#fff);--teal-wash:color-mix(in srgb,var(--teal) 5%,#fff);--teal-hover:color-mix(in srgb,var(--teal) 10%,#fff);--teal-selected:color-mix(in srgb,var(--teal) 17%,#fff);--red:var(--accent,#c42d27);--red-fore:var(--accentText,#fff);--green:#599e21;--ink:#17242b;--muted:#60717a;--line:#d7e0e4;position:fixed;right:22px;bottom:22px;width:min(720px,calc(100vw - 32px));height:min(640px,calc(100dvh - 32px));min-width:360px;min-height:460px;display:flex;flex-direction:column;overflow:hidden;resize:both;pointer-events:auto;background:#fff;border:1px solid #c7d2d7;border-radius:10px;box-shadow:0 18px 54px rgba(23,36,43,.24),0 3px 10px rgba(23,36,43,.12);container-type:inline-size}
|
|
1681
1730
|
.dl-chat.is-popout{inset:0;width:100%;height:100dvh;min-width:0;min-height:0;resize:none;border:0;border-radius:0;box-shadow:none}.dl-chat.is-popout .dl-chat__minimize{display:none}
|
|
1682
|
-
.dl-chat__titlebar{height:46px;flex:0 0 46px;display:flex;align-items:center;justify-content:space-between;padding:0 8px 0 14px;color
|
|
1731
|
+
.dl-chat__titlebar{height:46px;flex:0 0 46px;display:flex;align-items:center;justify-content:space-between;padding:0 8px 0 14px;color:var(--teal-fore);background:var(--teal);cursor:move;touch-action:none;user-select:none}
|
|
1683
1732
|
.dl-chat__brand{display:flex;align-items:center;gap:9px;font-size:15px;letter-spacing:.01em}.dl-chat__mark{display:grid;place-items:center;width:24px;height:24px}.dl-chat__mark svg{width:22px;height:22px;fill:currentColor}
|
|
1733
|
+
.dl-chat__title-unread{min-width:20px;height:20px;padding:0 6px;border-radius:10px;font-size:12px;font-weight:700;line-height:20px;text-align:center;color:var(--red-fore);background:var(--red);box-shadow:0 0 0 2px rgba(255,255,255,.85)}.dl-chat__title-unread[hidden]{display:none}
|
|
1684
1734
|
.dl-chat__window-actions{display:flex;gap:2px}.dl-chat__icon-button{display:grid;place-items:center;width:34px;height:34px;padding:0;border:0;border-radius:5px;color:inherit;background:transparent;cursor:pointer;transition:background-color 120ms ease-out,color 120ms ease-out}.dl-chat__icon-button:hover{background:rgba(255,255,255,.14)}.dl-chat__icon-button:focus-visible,.dl-chat button:focus-visible,.dl-chat input:focus-visible,.dl-chat textarea:focus-visible{outline:2px solid #f2c94c;outline-offset:2px}.dl-chat__icon-button svg{width:18px;height:18px;fill:currentColor}
|
|
1685
1735
|
.dl-chat__connection,.dl-chat__error{padding:7px 14px;font-size:12px;line-height:1.35}.dl-chat__connection{color:#654d00;background:#fff4c7;border-bottom:1px solid #e8d68c}.dl-chat__error{color:#7b201c;background:#fbe9e8;border-bottom:1px solid #efbfbc}
|
|
1686
|
-
.dl-chat__body{min-height:0;flex:1;display:grid;grid-template-columns:260px minmax(0,1fr)}.dl-chat__sidebar{min-width:0;display:flex;flex-direction:column;background
|
|
1687
|
-
.dl-chat__search-wrap{position:relative;display:block;margin:14px 12px 9px}.dl-chat__search-wrap>svg{position:absolute;left:10px;top:50%;width:17px;height:17px;fill:#70818a;transform:translateY(-50%);pointer-events:none}.dl-chat__search{width:100%;height:36px;padding:0 10px 0 34px;color:var(--ink);background:#fff;border:1px solid #c7d2d7;border-radius:6px;outline:0}.dl-chat__search:focus,.dl-chat__composer:focus{border-color:var(--teal);box-shadow:0 0 0 2px
|
|
1688
|
-
.dl-chat__scroll{min-height:0;overflow:auto;padding:0 8px 16px}.dl-chat__scroll h2{margin:14px 8px 6px;color:#63757e;font-size:11px;font-weight:700;letter-spacing:.07em;text-transform:uppercase}
|
|
1689
|
-
.dl-chat__person{position:relative;width:100%;display:grid;grid-template-columns:38px minmax(0,1fr) 8px;align-items:center;gap:10px;padding:8px;border:0;border-radius:7px;text-align:left;color:var(--ink);background:transparent;cursor:pointer;transition:background-color 120ms ease-out}.dl-chat__person:hover{background
|
|
1690
|
-
.dl-chat__main{min-width:0;min-height:0}.dl-chat__empty{height:
|
|
1691
|
-
.dl-chat__thread{height:
|
|
1692
|
-
.dl-chat__older{
|
|
1693
|
-
.dl-chat__compose{position:relative;display:grid;grid-template-columns:minmax(0,1fr) 38px;gap:8px;padding:10px 14px 22px;border-top:1px solid var(--line)}.dl-chat__composer{width:100%;min-height:38px;max-height:92px;resize:none;padding:9px 11px;color:var(--ink);border:1px solid #bdcbd1;border-radius:7px;line-height:1.35;outline:0}.dl-chat__send{display:grid;place-items:center;width:38px;height:38px;padding:0;border:0;border-radius:7px;color
|
|
1694
|
-
.dl-
|
|
1695
|
-
|
|
1696
|
-
|
|
1736
|
+
.dl-chat__body{min-height:0;flex:1;display:grid;grid-template-columns:260px minmax(0,1fr);grid-template-rows:minmax(0,1fr)}.dl-chat__sidebar{min-width:0;min-height:0;display:flex;flex-direction:column;background:var(--teal-wash);border-right:1px solid var(--line)}
|
|
1737
|
+
.dl-chat__search-wrap{position:relative;display:block;margin:14px 12px 9px}.dl-chat__search-wrap>svg{position:absolute;left:10px;top:50%;width:17px;height:17px;fill:#70818a;transform:translateY(-50%);pointer-events:none}.dl-chat__search{width:100%;height:36px;padding:0 10px 0 34px;color:var(--ink);background:#fff;border:1px solid #c7d2d7;border-radius:6px;outline:0}.dl-chat__search:focus,.dl-chat__composer:focus{border-color:var(--teal);box-shadow:0 0 0 2px color-mix(in srgb,var(--teal) 15%,transparent)}
|
|
1738
|
+
.dl-chat__scroll{flex:1 1 auto;min-height:0;overflow:auto;padding:0 8px 16px}.dl-chat__scroll h2{margin:14px 8px 6px;color:#63757e;font-size:11px;font-weight:700;letter-spacing:.07em;text-transform:uppercase}
|
|
1739
|
+
.dl-chat__person{position:relative;width:100%;display:grid;grid-template-columns:38px minmax(0,1fr) 8px;align-items:center;gap:10px;padding:8px;border:0;border-radius:7px;text-align:left;color:var(--ink);background:transparent;cursor:pointer;transition:background-color 120ms ease-out}.dl-chat__person:hover{background:var(--teal-hover)}.dl-chat__person.is-selected{background:var(--teal-selected)}.dl-chat__avatar{display:grid;place-items:center;width:38px;height:38px;border-radius:50%;color:var(--teal-fore);background:var(--teal);font-size:12px;font-weight:700;letter-spacing:.03em}.dl-chat__person-copy{min-width:0}.dl-chat__person-top,.dl-chat__person-bottom{display:flex;align-items:center;justify-content:space-between;gap:8px;min-width:0}.dl-chat__person-top strong,.dl-chat__person-bottom>span:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-chat__person-top strong{font-size:13px;font-weight:600}.dl-chat__person-top time{flex:0 0 auto;color:#778891;font-size:10px}.dl-chat__person-bottom>span:first-child{color:var(--muted);font-size:11px}.dl-chat__presence{width:8px;height:8px;border:2px solid #87969d;border-radius:50%}.dl-chat__presence.is-online{border-color:var(--green);background:var(--green)}.dl-chat__unread{display:grid;place-items:center;min-width:18px;height:18px;padding:0 5px;border-radius:9px;color:var(--red-fore)!important;background:var(--red);font-size:10px!important;font-weight:700}.dl-chat__list-empty{margin:6px 8px 12px;color:var(--muted);font-size:12px;line-height:1.45}
|
|
1740
|
+
.dl-chat__main{min-width:0;min-height:0;overflow:hidden;height:100%;display:flex;flex-direction:column}.dl-chat__empty{flex:1;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:36px;text-align:center}.dl-chat__empty-icon{display:grid;place-items:center;width:58px;height:58px;color:var(--teal);background:var(--teal-soft);border-radius:50%}.dl-chat__empty-icon svg{width:27px;height:27px;fill:currentColor}.dl-chat__empty h2{margin:17px 0 5px;font-size:17px}.dl-chat__empty p{max-width:280px;margin:0;color:var(--muted);font-size:13px;line-height:1.5}
|
|
1741
|
+
.dl-chat__thread{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.dl-chat__thread[hidden],.dl-chat__empty[hidden]{display:none}.dl-chat__thread-header{flex:0 0 auto;height:62px;display:flex;align-items:center;gap:10px;padding:0 16px;border-bottom:1px solid var(--line)}.dl-chat__thread-header .dl-chat__avatar{width:36px;height:36px;flex:0 0 36px}.dl-chat__thread-header>div{min-width:0;display:flex;flex-direction:column}.dl-chat__thread-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px}.dl-chat__thread-status{position:relative;padding-left:11px;color:var(--muted);font-size:11px}.dl-chat__thread-status:before{content:"";position:absolute;left:0;top:50%;width:7px;height:7px;border:1.5px solid #87969d;border-radius:50%;transform:translateY(-50%)}.dl-chat__thread-status.is-online:before{border-color:var(--green);background:var(--green)}.dl-chat__back{display:none;color:var(--teal)}
|
|
1742
|
+
.dl-chat__older{flex:0 0 auto;align-self:center;margin:7px 0 0;padding:4px 8px;border:0;color:var(--teal-deep);background:transparent;font-size:11px;cursor:pointer}.dl-chat__older:hover{text-decoration:underline}.dl-chat__messages{flex:1 1 auto;min-height:0;overflow-y:auto;padding:12px 18px 18px;scroll-behavior:smooth}.dl-chat__date{display:flex;align-items:center;gap:10px;margin:12px 0;color:#71838c;font-size:10px}.dl-chat__date:before,.dl-chat__date:after{content:"";height:1px;flex:1;background:#e3e9ec}.dl-chat__message{display:flex;margin:4px 0}.dl-chat__message.is-mine{justify-content:flex-end}.dl-chat__bubble{max-width:min(76%,430px);padding:8px 10px 6px;color:var(--ink);background:#edf2f4;border-radius:5px 11px 11px 11px;font-size:13px;line-height:1.42;overflow-wrap:anywhere}.dl-chat__message.is-mine .dl-chat__bubble{color:var(--teal-fore);background:var(--teal);border-radius:11px 5px 11px 11px}.dl-chat__bubble>span{white-space:pre-wrap}.dl-chat__bubble time{display:block;margin-top:3px;color:#6c7e87;font-size:9px;text-align:right}.dl-chat__message.is-mine time{color:color-mix(in srgb,var(--teal-fore) 72%,transparent)}.dl-chat__retry{display:block;margin:5px 0 0 auto;padding:2px 7px;border:1px solid rgba(255,255,255,.65);border-radius:4px;color:var(--teal-fore);background:transparent;font-size:10px;cursor:pointer}.dl-chat__retry:hover{background:rgba(255,255,255,.12)}.dl-chat__thread-empty{max-width:300px;margin:auto;padding:24px;color:var(--muted);font-size:12px;line-height:1.5;text-align:center}.dl-chat__typing{flex:0 0 auto;height:20px;padding:0 18px;color:var(--muted);font-size:11px;font-style:italic}
|
|
1743
|
+
.dl-chat__compose{flex:0 0 auto;position:relative;display:grid;grid-template-columns:minmax(0,1fr) 38px;gap:8px;padding:10px 14px 22px;border-top:1px solid var(--line)}.dl-chat__composer{width:100%;min-height:38px;max-height:92px;overflow-y:auto;resize:none;box-sizing:border-box;padding:9px 11px;color:var(--ink);border:1px solid #bdcbd1;border-radius:7px;line-height:1.35;outline:0}.dl-chat__send{display:grid;place-items:center;width:38px;height:38px;padding:0;border:0;border-radius:7px;color:var(--teal-fore);background:var(--teal);cursor:pointer;transition:background-color 120ms ease-out}.dl-chat__send:hover{background:var(--teal-deep)}.dl-chat__send:disabled{cursor:not-allowed;opacity:.45}.dl-chat__send svg{width:19px;height:19px;fill:currentColor}.dl-chat__hint{position:absolute;left:15px;bottom:5px;color:#829098;font-size:9px}
|
|
1744
|
+
.dl-chat__restore{display:none}
|
|
1745
|
+
.dl-chat.is-minimized{left:auto!important;top:auto!important;right:max(22px,env(safe-area-inset-right))!important;bottom:max(22px,env(safe-area-inset-bottom))!important;width:min(310px,calc(100vw - 32px))!important;min-width:0;height:68px!important;min-height:68px;resize:none;border:1px solid var(--teal-deep);border-radius:12px;background:var(--teal);color:var(--teal-fore);box-shadow:0 6px 24px rgba(23,36,43,.24)}
|
|
1746
|
+
.dl-chat.is-minimized .dl-chat__titlebar{position:absolute;inset:0 0 0 auto;height:68px;padding:0 8px;cursor:default;background:transparent;touch-action:auto}
|
|
1747
|
+
.dl-chat.is-minimized .dl-chat__brand{display:contents}
|
|
1748
|
+
.dl-chat.is-minimized .dl-chat__brand>.dl-chat__mark,.dl-chat.is-minimized .dl-chat__brand>strong,.dl-chat.is-minimized .dl-chat__sound,.dl-chat.is-minimized .dl-chat__popout,.dl-chat.is-minimized .dl-chat__minimize{display:none}
|
|
1749
|
+
.dl-chat.is-minimized .dl-chat__title-unread{margin-right:10px}
|
|
1750
|
+
.dl-chat.is-minimized .dl-chat__close{width:44px;height:44px;border:1px solid rgba(255,255,255,.4);border-radius:7px}
|
|
1751
|
+
.dl-chat.is-minimized .dl-chat__restore{display:flex;align-items:center;gap:12px;flex:1;width:100%;padding:10px 110px 10px 16px;border:0;background:transparent;color:inherit;text-align:left;cursor:pointer;touch-action:manipulation}
|
|
1752
|
+
.dl-chat.is-minimized .dl-chat__restore:hover{background:rgba(255,255,255,.1)}
|
|
1753
|
+
.dl-chat__restore .dl-chat__mark{flex:none;width:32px;height:32px}.dl-chat__restore .dl-chat__mark svg{width:28px;height:28px}
|
|
1754
|
+
.dl-chat__restore strong,.dl-chat__restore small{display:block;white-space:nowrap}.dl-chat__restore strong{font-size:15px}.dl-chat__restore small{margin-top:3px;font-size:11px}
|
|
1755
|
+
.dl-chat.is-minimized .dl-chat__body,.dl-chat.is-minimized .dl-chat__connection,.dl-chat.is-minimized .dl-chat__error{display:none}
|
|
1756
|
+
@container (max-width:520px){.dl-chat__body{grid-template-columns:1fr}.dl-chat__main{display:none}.dl-chat.has-selection .dl-chat__sidebar{display:none}.dl-chat.has-selection .dl-chat__main{display:flex}.dl-chat__back{display:grid}.dl-chat__hint{display:none}.dl-chat__compose{padding-bottom:10px}}
|
|
1757
|
+
@media(max-width:600px){.dl-chat{inset:0;width:100%;height:100dvh;min-width:0;min-height:0;border:0;border-radius:0;resize:none}.dl-chat.is-minimized{right:max(12px,env(safe-area-inset-right))!important;bottom:max(12px,env(safe-area-inset-bottom))!important;width:min(310px,calc(100vw - 24px))!important}.dl-chat__titlebar{cursor:default}.dl-chat__popout{display:none}}
|
|
1758
|
+
@supports not (color:color-mix(in srgb,red,blue)){.dl-chat{--teal-deep:#2f596d;--teal-soft:#e9f1f4;--teal-wash:#f4f7f8;--teal-hover:#e5edef;--teal-selected:#dce8ed}}
|
|
1697
1759
|
@media(prefers-reduced-motion:reduce){.dl-chat *{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important}}
|
|
1698
1760
|
`;
|
|
1761
|
+
const SOUND_KEY = "datalynk-chat-sound";
|
|
1699
1762
|
const windows = /* @__PURE__ */ new WeakMap();
|
|
1700
1763
|
function openChat(chat, options = {}) {
|
|
1701
1764
|
const doc = options.document || document;
|
|
@@ -1726,6 +1789,8 @@ class ChatWindow {
|
|
|
1726
1789
|
__publicField(this, "resizeObserver");
|
|
1727
1790
|
__publicField(this, "renderedMessageIds", "");
|
|
1728
1791
|
__publicField(this, "destroyed", false);
|
|
1792
|
+
__publicField(this, "stopReadVisibility");
|
|
1793
|
+
__publicField(this, "onVisibilityChange", () => void this.chat.markLatestRead().catch(() => void 0));
|
|
1729
1794
|
__publicField(this, "onDestroyed");
|
|
1730
1795
|
this.chat = chat;
|
|
1731
1796
|
this.options = options;
|
|
@@ -1736,9 +1801,11 @@ class ChatWindow {
|
|
|
1736
1801
|
this.root.innerHTML = `<style>${CHAT_CSS}</style>${CHAT_HTML}`;
|
|
1737
1802
|
this.doc.body.appendChild(this.host);
|
|
1738
1803
|
this.bind();
|
|
1804
|
+
this.stopReadVisibility = this.chat.setReadVisibility(() => this.canReadSelectedThread());
|
|
1739
1805
|
this.restoreGeometry();
|
|
1740
1806
|
this.subscribe();
|
|
1741
1807
|
this.restoreMinimized();
|
|
1808
|
+
this.renderSound();
|
|
1742
1809
|
void this.initializeGui();
|
|
1743
1810
|
this.searchInput.focus();
|
|
1744
1811
|
}
|
|
@@ -1749,20 +1816,24 @@ class ChatWindow {
|
|
|
1749
1816
|
this.host.hidden = false;
|
|
1750
1817
|
this.setMinimized(false);
|
|
1751
1818
|
this.searchInput.focus();
|
|
1819
|
+
this.onVisibilityChange();
|
|
1752
1820
|
}
|
|
1753
1821
|
destroy() {
|
|
1754
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1822
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1755
1823
|
if (this.destroyed) return;
|
|
1756
1824
|
this.destroyed = true;
|
|
1825
|
+
(_a = this.stopReadVisibility) == null ? void 0 : _a.call(this);
|
|
1826
|
+
this.doc.removeEventListener("visibilitychange", this.onVisibilityChange);
|
|
1827
|
+
(_b = this.doc.defaultView) == null ? void 0 : _b.removeEventListener("focus", this.onVisibilityChange);
|
|
1757
1828
|
this.subscriptions.forEach((subscription) => subscription.unsubscribe());
|
|
1758
|
-
(
|
|
1829
|
+
(_c = this.resizeObserver) == null ? void 0 : _c.disconnect();
|
|
1759
1830
|
clearTimeout(this.searchTimer);
|
|
1760
1831
|
clearTimeout(this.typingTimer);
|
|
1761
1832
|
if (this.typingSent && this.selected) void this.chat.setTyping(this.selected.id, false).catch(() => void 0);
|
|
1762
1833
|
this.host.remove();
|
|
1763
|
-
(
|
|
1764
|
-
(
|
|
1765
|
-
if (this.options.popout && !((
|
|
1834
|
+
(_e = (_d = this.options).onClose) == null ? void 0 : _e.call(_d);
|
|
1835
|
+
(_f = this.onDestroyed) == null ? void 0 : _f.call(this);
|
|
1836
|
+
if (this.options.popout && !((_g = this.doc.defaultView) == null ? void 0 : _g.closed)) (_h = this.doc.defaultView) == null ? void 0 : _h.close();
|
|
1766
1837
|
}
|
|
1767
1838
|
get shell() {
|
|
1768
1839
|
return this.query(".dl-chat");
|
|
@@ -1782,23 +1853,49 @@ class ChatWindow {
|
|
|
1782
1853
|
get sendButton() {
|
|
1783
1854
|
return this.query(".dl-chat__send");
|
|
1784
1855
|
}
|
|
1856
|
+
canReadSelectedThread() {
|
|
1857
|
+
return !this.destroyed && !this.host.hidden && !this.shell.classList.contains("is-minimized") && this.doc.visibilityState === "visible" && Boolean(this.doc.hasFocus()) && this.query(".dl-chat__thread").getClientRects().length > 0;
|
|
1858
|
+
}
|
|
1785
1859
|
query(selector) {
|
|
1786
1860
|
const node = this.root.querySelector(selector);
|
|
1787
1861
|
if (!node) throw new Error(`Missing chat element: ${selector}`);
|
|
1788
1862
|
return node;
|
|
1789
1863
|
}
|
|
1790
1864
|
bind() {
|
|
1791
|
-
var _a;
|
|
1865
|
+
var _a, _b;
|
|
1792
1866
|
this.query(".dl-chat__close").addEventListener("click", () => this.destroy());
|
|
1867
|
+
this.query(".dl-chat__restore").addEventListener("click", () => this.setMinimized(false));
|
|
1793
1868
|
this.query(".dl-chat__minimize").addEventListener("click", () => this.setMinimized(!this.shell.classList.contains("is-minimized")));
|
|
1794
1869
|
this.query(".dl-chat__popout").addEventListener("click", () => this.popout());
|
|
1870
|
+
this.query(".dl-chat__sound").addEventListener("click", () => this.setMuted(!this.isMuted()));
|
|
1795
1871
|
this.query(".dl-chat__back").addEventListener("click", () => {
|
|
1796
1872
|
this.shell.classList.remove("has-selection");
|
|
1797
1873
|
this.searchInput.focus();
|
|
1798
1874
|
});
|
|
1799
1875
|
this.searchInput.addEventListener("input", () => {
|
|
1876
|
+
this.renderPeople(this.chat.users$.value);
|
|
1800
1877
|
clearTimeout(this.searchTimer);
|
|
1801
|
-
this.searchTimer = setTimeout(() => void this.chat.loadDirectory(this.searchInput.value).catch(() => void 0),
|
|
1878
|
+
this.searchTimer = setTimeout(() => void this.chat.loadDirectory(this.searchInput.value).catch(() => void 0), 150);
|
|
1879
|
+
});
|
|
1880
|
+
this.searchInput.addEventListener("keydown", (event) => {
|
|
1881
|
+
const first = this.query(".dl-chat__people").querySelector(".dl-chat__person");
|
|
1882
|
+
if (event.key === "Enter" && first) {
|
|
1883
|
+
event.preventDefault();
|
|
1884
|
+
first.click();
|
|
1885
|
+
} else if (event.key === "ArrowDown" && first) {
|
|
1886
|
+
event.preventDefault();
|
|
1887
|
+
first.focus();
|
|
1888
|
+
}
|
|
1889
|
+
});
|
|
1890
|
+
this.query(".dl-chat__people").addEventListener("keydown", (event) => {
|
|
1891
|
+
if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
|
|
1892
|
+
const buttons = Array.from(this.query(".dl-chat__people").querySelectorAll(".dl-chat__person"));
|
|
1893
|
+
const index = buttons.indexOf(this.doc.activeElement);
|
|
1894
|
+
if (index < 0) return;
|
|
1895
|
+
event.preventDefault();
|
|
1896
|
+
if (event.key === "ArrowDown") buttons[Math.min(index + 1, buttons.length - 1)].focus();
|
|
1897
|
+
else if (index === 0) this.searchInput.focus();
|
|
1898
|
+
else buttons[index - 1].focus();
|
|
1802
1899
|
});
|
|
1803
1900
|
this.composer.addEventListener("input", () => this.onComposerInput());
|
|
1804
1901
|
this.composer.addEventListener("keydown", (event) => {
|
|
@@ -1818,6 +1915,8 @@ class ChatWindow {
|
|
|
1818
1915
|
this.resizeObserver.observe(this.shell);
|
|
1819
1916
|
}
|
|
1820
1917
|
(_a = this.doc.defaultView) == null ? void 0 : _a.addEventListener("beforeunload", () => this.destroy(), { once: true });
|
|
1918
|
+
this.doc.addEventListener("visibilitychange", this.onVisibilityChange);
|
|
1919
|
+
(_b = this.doc.defaultView) == null ? void 0 : _b.addEventListener("focus", this.onVisibilityChange);
|
|
1821
1920
|
}
|
|
1822
1921
|
subscribe() {
|
|
1823
1922
|
this.subscriptions.push(
|
|
@@ -1834,10 +1933,18 @@ class ChatWindow {
|
|
|
1834
1933
|
this.api.status$.subscribe((status) => this.renderConnection(status)),
|
|
1835
1934
|
this.chat.unread$.subscribe((count) => {
|
|
1836
1935
|
var _a, _b;
|
|
1837
|
-
|
|
1936
|
+
this.renderUnread(count);
|
|
1937
|
+
(_b = (_a = this.options).onUnreadChange) == null ? void 0 : _b.call(_a, count);
|
|
1838
1938
|
})
|
|
1839
1939
|
);
|
|
1840
1940
|
}
|
|
1941
|
+
/** Titlebar count; stays visible while the window is minimized. */
|
|
1942
|
+
renderUnread(count) {
|
|
1943
|
+
const badge = this.query(".dl-chat__title-unread");
|
|
1944
|
+
badge.toggleAttribute("hidden", count <= 0);
|
|
1945
|
+
badge.textContent = count > 99 ? "99+" : String(count);
|
|
1946
|
+
badge.setAttribute("aria-label", `${count} unread message${count === 1 ? "" : "s"}`);
|
|
1947
|
+
}
|
|
1841
1948
|
renderPeople(users) {
|
|
1842
1949
|
const query = this.searchInput.value.trim().toLowerCase();
|
|
1843
1950
|
const conversationIds = new Set(this.chat.conversations$.value.map((row) => row.peer.id));
|
|
@@ -1847,11 +1954,11 @@ class ChatWindow {
|
|
|
1847
1954
|
if (!filtered.length) {
|
|
1848
1955
|
const empty = this.doc.createElement("p");
|
|
1849
1956
|
empty.className = "dl-chat__list-empty";
|
|
1850
|
-
empty.textContent = query ? "No matching people" : "
|
|
1957
|
+
empty.textContent = query ? "No matching people" : "No other people to start a conversation with.";
|
|
1851
1958
|
target.appendChild(empty);
|
|
1852
1959
|
return;
|
|
1853
1960
|
}
|
|
1854
|
-
filtered.slice(0,
|
|
1961
|
+
filtered.slice(0, 10).forEach((user) => target.appendChild(this.personButton(user)));
|
|
1855
1962
|
}
|
|
1856
1963
|
renderConversations(rows) {
|
|
1857
1964
|
this.conversationList.replaceChildren();
|
|
@@ -1993,7 +2100,13 @@ class ChatWindow {
|
|
|
1993
2100
|
node.textContent = status === "unauthorized" ? "Your session expired. Sign in again to continue chatting." : "Chat is reconnecting. Messages cannot be sent yet.";
|
|
1994
2101
|
this.sendButton.disabled = status !== "online" || !this.composer.value.trim();
|
|
1995
2102
|
}
|
|
2103
|
+
growComposer() {
|
|
2104
|
+
const box = this.composer;
|
|
2105
|
+
box.style.height = "auto";
|
|
2106
|
+
box.style.height = `${box.scrollHeight + box.offsetHeight - box.clientHeight}px`;
|
|
2107
|
+
}
|
|
1996
2108
|
onComposerInput() {
|
|
2109
|
+
this.growComposer();
|
|
1997
2110
|
if (!this.selected) return;
|
|
1998
2111
|
this.writeDraft(this.selected.id, this.composer.value);
|
|
1999
2112
|
this.sendButton.disabled = !this.composer.value.trim() || this.api.status !== "online";
|
|
@@ -2017,6 +2130,7 @@ class ChatWindow {
|
|
|
2017
2130
|
try {
|
|
2018
2131
|
await this.chat.send(peer.id, body);
|
|
2019
2132
|
this.composer.value = "";
|
|
2133
|
+
this.growComposer();
|
|
2020
2134
|
this.writeDraft(peer.id, "");
|
|
2021
2135
|
if (this.typingSent) await this.chat.setTyping(peer.id, false).catch(() => void 0);
|
|
2022
2136
|
this.typingSent = false;
|
|
@@ -2027,7 +2141,7 @@ class ChatWindow {
|
|
|
2027
2141
|
}
|
|
2028
2142
|
}
|
|
2029
2143
|
popout() {
|
|
2030
|
-
var _a;
|
|
2144
|
+
var _a, _b;
|
|
2031
2145
|
const popup = (_a = this.doc.defaultView) == null ? void 0 : _a.open("", "datalynk-chat", "popup,width=760,height=720,resizable=yes");
|
|
2032
2146
|
if (!popup) {
|
|
2033
2147
|
this.chat.error$.next("Allow pop-ups to open chat in a separate window.");
|
|
@@ -2035,6 +2149,11 @@ class ChatWindow {
|
|
|
2035
2149
|
}
|
|
2036
2150
|
popup.document.title = this.options.title || "Datalynk Chat";
|
|
2037
2151
|
popup.document.body.style.margin = "0";
|
|
2152
|
+
const theme = (_b = this.doc.defaultView) == null ? void 0 : _b.getComputedStyle(this.doc.documentElement);
|
|
2153
|
+
["--primary", "--primary-fore", "--accent", "--accentText"].forEach((name) => {
|
|
2154
|
+
const value = theme == null ? void 0 : theme.getPropertyValue(name).trim();
|
|
2155
|
+
if (value) popup.document.documentElement.style.setProperty(name, value);
|
|
2156
|
+
});
|
|
2038
2157
|
openChat(this.chat, { ...this.options, document: popup.document, popout: true });
|
|
2039
2158
|
this.destroy();
|
|
2040
2159
|
}
|
|
@@ -2080,6 +2199,7 @@ class ChatWindow {
|
|
|
2080
2199
|
async initializeGui() {
|
|
2081
2200
|
try {
|
|
2082
2201
|
await this.chat.initialize();
|
|
2202
|
+
void this.chat.loadDirectory(this.searchInput.value).catch(() => void 0);
|
|
2083
2203
|
const selectedId = this.readSelectedUser();
|
|
2084
2204
|
if (!selectedId || this.chat.selectedUser$.value) return;
|
|
2085
2205
|
const conversation = this.chat.conversations$.value.find((row) => row.peer.id === selectedId);
|
|
@@ -2090,6 +2210,30 @@ class ChatWindow {
|
|
|
2090
2210
|
} catch {
|
|
2091
2211
|
}
|
|
2092
2212
|
}
|
|
2213
|
+
isMuted() {
|
|
2214
|
+
try {
|
|
2215
|
+
return localStorage.getItem(SOUND_KEY) === "off";
|
|
2216
|
+
} catch {
|
|
2217
|
+
return false;
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
setMuted(muted) {
|
|
2221
|
+
try {
|
|
2222
|
+
if (muted) localStorage.setItem(SOUND_KEY, "off");
|
|
2223
|
+
else localStorage.removeItem(SOUND_KEY);
|
|
2224
|
+
} catch {
|
|
2225
|
+
}
|
|
2226
|
+
this.renderSound();
|
|
2227
|
+
}
|
|
2228
|
+
renderSound() {
|
|
2229
|
+
var _a;
|
|
2230
|
+
const muted = this.isMuted();
|
|
2231
|
+
const button = this.query(".dl-chat__sound");
|
|
2232
|
+
button.setAttribute("aria-pressed", String(muted));
|
|
2233
|
+
button.setAttribute("aria-label", muted ? "Unmute message sound" : "Mute message sound");
|
|
2234
|
+
button.title = muted ? "Unmute message sound" : "Mute message sound";
|
|
2235
|
+
(_a = button.querySelector("path")) == null ? void 0 : _a.setAttribute("d", muted ? SOUND_OFF_ICON : SOUND_ON_ICON);
|
|
2236
|
+
}
|
|
2093
2237
|
restoreMinimized() {
|
|
2094
2238
|
try {
|
|
2095
2239
|
if (localStorage.getItem(this.storageKey("minimized")) === "1") this.setMinimized(true, false);
|
|
@@ -2101,11 +2245,17 @@ class ChatWindow {
|
|
|
2101
2245
|
if (this.options.popout) return;
|
|
2102
2246
|
if (minimized && !this.shell.classList.contains("is-minimized")) this.saveGeometry();
|
|
2103
2247
|
this.shell.classList.toggle("is-minimized", minimized);
|
|
2248
|
+
this.query(".dl-chat__restore").hidden = !minimized;
|
|
2104
2249
|
const button = this.query(".dl-chat__minimize");
|
|
2105
2250
|
button.setAttribute("aria-label", minimized ? "Restore chat" : "Minimize chat");
|
|
2106
2251
|
button.title = minimized ? "Restore" : "Minimize";
|
|
2107
2252
|
(_a = button.querySelector("path")) == null ? void 0 : _a.setAttribute("d", minimized ? "M7.4 15.4 12 10.8l4.6 4.6L18 14l-6-6-6 6 1.4 1.4z" : "M5 11h14v2H5z");
|
|
2108
2253
|
if (!minimized) this.restoreGeometry();
|
|
2254
|
+
if (!minimized) this.onVisibilityChange();
|
|
2255
|
+
if (persist) {
|
|
2256
|
+
if (minimized) this.query(".dl-chat__restore").focus();
|
|
2257
|
+
else this.searchInput.focus();
|
|
2258
|
+
}
|
|
2109
2259
|
if (persist) this.saveMinimized(minimized);
|
|
2110
2260
|
}
|
|
2111
2261
|
saveMinimized(value) {
|
|
@@ -2134,22 +2284,41 @@ class ChatWindow {
|
|
|
2134
2284
|
var _a, _b;
|
|
2135
2285
|
if (this.options.popout) return;
|
|
2136
2286
|
try {
|
|
2287
|
+
const viewWidth = ((_a = this.doc.defaultView) == null ? void 0 : _a.innerWidth) || 760;
|
|
2288
|
+
const viewHeight = ((_b = this.doc.defaultView) == null ? void 0 : _b.innerHeight) || 720;
|
|
2289
|
+
if (viewWidth <= 600) {
|
|
2290
|
+
["width", "height", "left", "top", "right", "bottom"].forEach((key) => this.shell.style.removeProperty(key));
|
|
2291
|
+
return;
|
|
2292
|
+
}
|
|
2137
2293
|
const stored = JSON.parse(localStorage.getItem(this.storageKey("geometry")) || "null");
|
|
2138
|
-
|
|
2139
|
-
const
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2294
|
+
const valid = stored && [stored.width, stored.height, stored.left, stored.top].every((value) => typeof value === "number" && Number.isFinite(value)) && stored.width > 0 && stored.height > 0;
|
|
2295
|
+
const margin = this.options.placement === "bottom-right" ? 22 : 8;
|
|
2296
|
+
if (valid) {
|
|
2297
|
+
const width = Math.min(viewWidth - margin * 2, Math.max(560, stored.width));
|
|
2298
|
+
const height = Math.min(viewHeight - margin * 2, Math.max(460, stored.height));
|
|
2299
|
+
this.shell.style.width = width + "px";
|
|
2300
|
+
this.shell.style.height = height + "px";
|
|
2301
|
+
if (this.options.placement !== "bottom-right") {
|
|
2302
|
+
const clamp = (value, max) => Math.max(margin, Math.min(value, max));
|
|
2303
|
+
this.shell.style.left = clamp(stored.left, viewWidth - width - margin) + "px";
|
|
2304
|
+
this.shell.style.top = clamp(stored.top, viewHeight - height - margin) + "px";
|
|
2305
|
+
this.shell.style.right = "auto";
|
|
2306
|
+
this.shell.style.bottom = "auto";
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
if (this.options.placement === "bottom-right") {
|
|
2310
|
+
this.shell.style.left = "auto";
|
|
2311
|
+
this.shell.style.top = "auto";
|
|
2312
|
+
this.shell.style.right = margin + "px";
|
|
2313
|
+
this.shell.style.bottom = margin + "px";
|
|
2314
|
+
}
|
|
2147
2315
|
} catch {
|
|
2148
2316
|
}
|
|
2149
2317
|
}
|
|
2150
2318
|
saveGeometry() {
|
|
2151
2319
|
if (this.options.popout || this.destroyed || this.shell.classList.contains("is-minimized")) return;
|
|
2152
2320
|
const rect = this.shell.getBoundingClientRect();
|
|
2321
|
+
if (!this.host.isConnected || this.host.hidden || rect.width <= 0 || rect.height <= 0) return;
|
|
2153
2322
|
try {
|
|
2154
2323
|
localStorage.setItem(this.storageKey("geometry"), JSON.stringify({
|
|
2155
2324
|
left: rect.left,
|