pinokiod 3.180.0 → 3.181.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/kernel/favicon.js +91 -34
- package/kernel/peer.js +73 -0
- package/kernel/util.js +13 -2
- package/package.json +1 -1
- package/server/index.js +126 -23
- package/server/public/common.js +244 -0
- package/server/public/layout.js +114 -0
- package/server/public/nav.js +227 -64
- package/server/public/style.css +22 -2
- package/server/public/tab-idle-notifier.js +3 -0
- package/server/socket.js +71 -4
- package/server/views/app.ejs +226 -56
- package/server/views/connect.ejs +9 -0
- package/server/views/index.ejs +9 -0
- package/server/views/init/index.ejs +9 -2
- package/server/views/layout.ejs +2 -0
- package/server/views/net.ejs +9 -0
- package/server/views/network.ejs +9 -0
- package/server/views/screenshots.ejs +9 -0
- package/server/views/settings.ejs +9 -0
- package/server/views/terminals.ejs +12 -3
- package/server/views/tools.ejs +10 -1
package/server/public/common.js
CHANGED
|
@@ -1462,10 +1462,56 @@ if (typeof hotkeys === 'function') {
|
|
|
1462
1462
|
})
|
|
1463
1463
|
}
|
|
1464
1464
|
|
|
1465
|
+
// Stable per-browser device identifier
|
|
1466
|
+
(function initPinokioDeviceId() {
|
|
1467
|
+
if (typeof window === 'undefined') {
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
try {
|
|
1471
|
+
const KEY = 'pinokio:device-id';
|
|
1472
|
+
const gen = () => `${Date.now()}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`;
|
|
1473
|
+
const get = () => {
|
|
1474
|
+
try {
|
|
1475
|
+
let id = localStorage.getItem(KEY);
|
|
1476
|
+
if (typeof id !== 'string' || id.length < 8) {
|
|
1477
|
+
id = gen();
|
|
1478
|
+
localStorage.setItem(KEY, id);
|
|
1479
|
+
}
|
|
1480
|
+
return id;
|
|
1481
|
+
} catch (_) {
|
|
1482
|
+
// Fallback when localStorage is unavailable
|
|
1483
|
+
if (!window.__pinokioVolatileDeviceId) {
|
|
1484
|
+
window.__pinokioVolatileDeviceId = gen();
|
|
1485
|
+
}
|
|
1486
|
+
return window.__pinokioVolatileDeviceId;
|
|
1487
|
+
}
|
|
1488
|
+
};
|
|
1489
|
+
// Expose helpers
|
|
1490
|
+
if (!window.PinokioGetDeviceId) {
|
|
1491
|
+
window.PinokioGetDeviceId = get;
|
|
1492
|
+
}
|
|
1493
|
+
// Convenience alias
|
|
1494
|
+
window.PinokioDeviceId = get();
|
|
1495
|
+
} catch (_) {
|
|
1496
|
+
// ignore
|
|
1497
|
+
}
|
|
1498
|
+
})();
|
|
1499
|
+
|
|
1465
1500
|
(function initNotificationAudioBridge() {
|
|
1466
1501
|
if (typeof window === 'undefined') {
|
|
1467
1502
|
return;
|
|
1468
1503
|
}
|
|
1504
|
+
// Avoid duplicate audio playback: if this is the top-level layout page, or if the
|
|
1505
|
+
// top window already owns notification playback, skip initialising this bridge.
|
|
1506
|
+
try {
|
|
1507
|
+
const isTop = window.top === window;
|
|
1508
|
+
if (isTop && document.getElementById('layout-root')) {
|
|
1509
|
+
return; // layout shell handles notifications
|
|
1510
|
+
}
|
|
1511
|
+
if (!isTop && window.top && window.top.__pinokioTopNotifyListener) {
|
|
1512
|
+
return; // top-level listener active; avoid duplicates from iframes
|
|
1513
|
+
}
|
|
1514
|
+
} catch (_) {}
|
|
1469
1515
|
if (window.__pinokioNotificationAudioInitialized) {
|
|
1470
1516
|
return;
|
|
1471
1517
|
}
|
|
@@ -1478,6 +1524,64 @@ if (typeof hotkeys === 'function') {
|
|
|
1478
1524
|
let reconnectTimeout = null;
|
|
1479
1525
|
let activeAudio = null;
|
|
1480
1526
|
|
|
1527
|
+
// Lightweight visual indicator to confirm notification receipt (mobile-friendly)
|
|
1528
|
+
let notifyIndicatorEl = null;
|
|
1529
|
+
let notifyIndicatorStyleInjected = false;
|
|
1530
|
+
const ensureNotifyIndicator = () => {
|
|
1531
|
+
if (!notifyIndicatorStyleInjected) {
|
|
1532
|
+
try {
|
|
1533
|
+
const style = document.createElement('style');
|
|
1534
|
+
style.textContent = `
|
|
1535
|
+
.pinokio-notify-indicator{position:fixed;top:12px;right:12px;z-index:2147483647;display:none;align-items:center;gap:8px;padding:8px 10px;border-radius:999px;background:rgba(15,23,42,0.92);color:#fff;font:600 12px/1.2 system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;box-shadow:0 10px 30px rgba(0,0,0,0.35)}
|
|
1536
|
+
.pinokio-notify-indicator .bell{font-size:14px}
|
|
1537
|
+
.pinokio-notify-indicator.show{display:inline-flex;animation:pinokioNotifyPop 160ms ease-out, pinokioNotifyFade 1600ms ease-in 700ms forwards}
|
|
1538
|
+
@keyframes pinokioNotifyPop{from{transform:translateY(-6px) scale(.98);opacity:0}to{transform:translateY(0) scale(1);opacity:1}}
|
|
1539
|
+
@keyframes pinokioNotifyFade{to{opacity:0;transform:translateY(-4px)}}
|
|
1540
|
+
@media (max-width: 768px){.pinokio-notify-indicator{top:10px;right:10px;padding:7px 9px;font-size:12px}}
|
|
1541
|
+
`;
|
|
1542
|
+
document.head.appendChild(style);
|
|
1543
|
+
notifyIndicatorStyleInjected = true;
|
|
1544
|
+
} catch (_) {}
|
|
1545
|
+
}
|
|
1546
|
+
if (!notifyIndicatorEl) {
|
|
1547
|
+
try {
|
|
1548
|
+
const el = document.createElement('div');
|
|
1549
|
+
el.className = 'pinokio-notify-indicator';
|
|
1550
|
+
const icon = document.createElement('span');
|
|
1551
|
+
icon.className = 'bell';
|
|
1552
|
+
icon.textContent = '🔔';
|
|
1553
|
+
const text = document.createElement('span');
|
|
1554
|
+
text.className = 'text';
|
|
1555
|
+
text.textContent = 'Notification received';
|
|
1556
|
+
el.appendChild(icon);
|
|
1557
|
+
el.appendChild(text);
|
|
1558
|
+
document.body.appendChild(el);
|
|
1559
|
+
notifyIndicatorEl = el;
|
|
1560
|
+
} catch (_) {}
|
|
1561
|
+
}
|
|
1562
|
+
};
|
|
1563
|
+
const flashNotifyIndicator = (payload) => {
|
|
1564
|
+
try {
|
|
1565
|
+
ensureNotifyIndicator();
|
|
1566
|
+
if (!notifyIndicatorEl) return;
|
|
1567
|
+
const text = notifyIndicatorEl.querySelector('.text');
|
|
1568
|
+
if (text) {
|
|
1569
|
+
const msg = (payload && typeof payload.message === 'string' && payload.message.trim()) ? payload.message.trim() : 'Notification received';
|
|
1570
|
+
// Keep it short on mobile
|
|
1571
|
+
text.textContent = msg.length > 80 ? (msg.slice(0, 77) + '…') : msg;
|
|
1572
|
+
}
|
|
1573
|
+
// retrigger animation
|
|
1574
|
+
notifyIndicatorEl.classList.remove('show');
|
|
1575
|
+
// force reflow
|
|
1576
|
+
void notifyIndicatorEl.offsetWidth;
|
|
1577
|
+
notifyIndicatorEl.classList.add('show');
|
|
1578
|
+
// Auto-hide handled by CSS animation; keep element for reuse
|
|
1579
|
+
window.setTimeout(() => {
|
|
1580
|
+
if (notifyIndicatorEl) notifyIndicatorEl.classList.remove('show');
|
|
1581
|
+
}, 2600);
|
|
1582
|
+
} catch (_) {}
|
|
1583
|
+
};
|
|
1584
|
+
|
|
1481
1585
|
const leaderStorageKey = 'pinokio.notification.leader';
|
|
1482
1586
|
const leaderHeartbeatMs = 5000;
|
|
1483
1587
|
const leaderStaleMs = 15000;
|
|
@@ -1629,6 +1733,18 @@ if (typeof hotkeys === 'function') {
|
|
|
1629
1733
|
return;
|
|
1630
1734
|
}
|
|
1631
1735
|
const payload = packet.data || {};
|
|
1736
|
+
// If targeted to a specific device, ignore only when our id exists and mismatches
|
|
1737
|
+
try {
|
|
1738
|
+
const targetId = (typeof payload.device_id === 'string' && payload.device_id.trim()) ? payload.device_id.trim() : null;
|
|
1739
|
+
if (targetId) {
|
|
1740
|
+
const myId = (typeof window.PinokioGetDeviceId === 'function') ? window.PinokioGetDeviceId() : null;
|
|
1741
|
+
if (myId && myId !== targetId) {
|
|
1742
|
+
return;
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
} catch (_) {}
|
|
1746
|
+
// Visual confirmation regardless of audio outcome (useful on mobile)
|
|
1747
|
+
flashNotifyIndicator(payload);
|
|
1632
1748
|
if (typeof payload.sound === 'string' && payload.sound) {
|
|
1633
1749
|
enqueueSound(payload.sound);
|
|
1634
1750
|
}
|
|
@@ -1696,6 +1812,7 @@ if (typeof hotkeys === 'function') {
|
|
|
1696
1812
|
{
|
|
1697
1813
|
method: CHANNEL_ID,
|
|
1698
1814
|
mode: 'listen',
|
|
1815
|
+
device_id: (typeof window.PinokioGetDeviceId === 'function') ? window.PinokioGetDeviceId() : undefined,
|
|
1699
1816
|
},
|
|
1700
1817
|
handlePacket
|
|
1701
1818
|
);
|
|
@@ -1768,6 +1885,127 @@ if (typeof hotkeys === 'function') {
|
|
|
1768
1885
|
// Attempt to become leader immediately on load.
|
|
1769
1886
|
attemptLeadership();
|
|
1770
1887
|
})();
|
|
1888
|
+
|
|
1889
|
+
// Mobile "Tap to connect" curtain to prime audio on the top-level page
|
|
1890
|
+
(function initMobileConnectCurtain() {
|
|
1891
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
1892
|
+
return;
|
|
1893
|
+
}
|
|
1894
|
+
try {
|
|
1895
|
+
if (window.__pinokioConnectCurtainInstalled || window.__pinokioConnectCurtainInstalling) {
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
} catch (_) {}
|
|
1899
|
+
try {
|
|
1900
|
+
if (window.top && window.top !== window) {
|
|
1901
|
+
return; // only top-level
|
|
1902
|
+
}
|
|
1903
|
+
} catch (_) {
|
|
1904
|
+
// cross-origin parent; just bail
|
|
1905
|
+
return;
|
|
1906
|
+
}
|
|
1907
|
+
if (window.__pinokioConnectCurtainInstalled) {
|
|
1908
|
+
return;
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
const isLikelyMobile = () => {
|
|
1912
|
+
try {
|
|
1913
|
+
if (navigator.userAgentData && typeof navigator.userAgentData.mobile === 'boolean') {
|
|
1914
|
+
if (navigator.userAgentData.mobile) return true;
|
|
1915
|
+
}
|
|
1916
|
+
} catch (_) {}
|
|
1917
|
+
try {
|
|
1918
|
+
const ua = (navigator.userAgent || '').toLowerCase();
|
|
1919
|
+
if (/iphone|ipad|ipod|android|mobile/.test(ua)) return true;
|
|
1920
|
+
} catch (_) {}
|
|
1921
|
+
try {
|
|
1922
|
+
if (navigator.maxTouchPoints && navigator.maxTouchPoints > 1) return true;
|
|
1923
|
+
} catch (_) {}
|
|
1924
|
+
try {
|
|
1925
|
+
if (window.matchMedia && window.matchMedia('(pointer: coarse)').matches) return true;
|
|
1926
|
+
} catch (_) {}
|
|
1927
|
+
try {
|
|
1928
|
+
if (window.matchMedia && window.matchMedia('(max-width: 900px)').matches) return true;
|
|
1929
|
+
} catch (_) {}
|
|
1930
|
+
return false;
|
|
1931
|
+
};
|
|
1932
|
+
|
|
1933
|
+
const createCurtain = () => {
|
|
1934
|
+
const style = document.createElement('style');
|
|
1935
|
+
style.textContent = `
|
|
1936
|
+
.pinokio-connect-curtain{position:fixed;top:0;left:0;right:0;bottom:0;z-index:2147483646;background:rgba(15,23,42,0.35);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center}
|
|
1937
|
+
.pinokio-connect-msg{user-select:none;-webkit-user-select:none;color:#fff;background:rgba(15,23,42,0.85);padding:14px 18px;border-radius:12px;font:600 16px/1.2 system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;box-shadow:0 16px 40px rgba(0,0,0,.38)}
|
|
1938
|
+
@media (max-width:768px){.pinokio-connect-msg{font-size:15px;padding:12px 16px}}
|
|
1939
|
+
`;
|
|
1940
|
+
document.head.appendChild(style);
|
|
1941
|
+
|
|
1942
|
+
const overlay = document.createElement('div');
|
|
1943
|
+
overlay.className = 'pinokio-connect-curtain';
|
|
1944
|
+
overlay.setAttribute('role', 'button');
|
|
1945
|
+
overlay.setAttribute('aria-label', 'Tap to connect');
|
|
1946
|
+
overlay.tabIndex = 0;
|
|
1947
|
+
const msg = document.createElement('div');
|
|
1948
|
+
msg.className = 'pinokio-connect-msg';
|
|
1949
|
+
msg.textContent = 'Tap to connect';
|
|
1950
|
+
overlay.appendChild(msg);
|
|
1951
|
+
window.__pinokioConnectCurtainInstalled = true;
|
|
1952
|
+
return overlay;
|
|
1953
|
+
};
|
|
1954
|
+
|
|
1955
|
+
const primeAudio = async () => {
|
|
1956
|
+
try {
|
|
1957
|
+
let a = window.__pinokioChimeAudio;
|
|
1958
|
+
if (!a) {
|
|
1959
|
+
a = new Audio('/chime.mp3');
|
|
1960
|
+
a.preload = 'auto';
|
|
1961
|
+
a.loop = false;
|
|
1962
|
+
a.muted = false;
|
|
1963
|
+
window.__pinokioChimeAudio = a;
|
|
1964
|
+
}
|
|
1965
|
+
a.currentTime = 0;
|
|
1966
|
+
await a.play(); // must be called synchronously in gesture handler
|
|
1967
|
+
try { a.pause(); a.currentTime = 0; } catch (_) {}
|
|
1968
|
+
try { window.__pinokioAudioArmed = true; } catch (_) {}
|
|
1969
|
+
return true;
|
|
1970
|
+
} catch (_) {
|
|
1971
|
+
try { window.__pinokioAudioArmed = true; } catch (_) {}
|
|
1972
|
+
return false;
|
|
1973
|
+
}
|
|
1974
|
+
};
|
|
1975
|
+
|
|
1976
|
+
const setup = () => {
|
|
1977
|
+
let forceParam = false;
|
|
1978
|
+
try {
|
|
1979
|
+
const usp = new URLSearchParams(window.location.search);
|
|
1980
|
+
forceParam = usp.has('connect') || usp.get('connect') === '1';
|
|
1981
|
+
} catch (_) {}
|
|
1982
|
+
if (!(forceParam || isLikelyMobile())) {
|
|
1983
|
+
return;
|
|
1984
|
+
}
|
|
1985
|
+
if (window.__pinokioConnectCurtainInstalled || window.__pinokioConnectCurtainInstalling) {
|
|
1986
|
+
return;
|
|
1987
|
+
}
|
|
1988
|
+
try { window.__pinokioConnectCurtainInstalling = true; } catch (_) {}
|
|
1989
|
+
const overlay = createCurtain();
|
|
1990
|
+
let handled = false;
|
|
1991
|
+
const onTap = async (e) => {
|
|
1992
|
+
if (handled) return;
|
|
1993
|
+
handled = true;
|
|
1994
|
+
try { e.preventDefault(); e.stopPropagation(); } catch (_) {}
|
|
1995
|
+
try { await primeAudio(); } catch (_) {}
|
|
1996
|
+
try { overlay.remove(); } catch (_) {}
|
|
1997
|
+
try { window.__pinokioConnectCurtainInstalled = true; window.__pinokioConnectCurtainInstalling = false; } catch (_) {}
|
|
1998
|
+
};
|
|
1999
|
+
overlay.addEventListener('pointerdown', onTap, { once: true, capture: true });
|
|
2000
|
+
document.body.appendChild(overlay);
|
|
2001
|
+
};
|
|
2002
|
+
|
|
2003
|
+
if (document.readyState === 'loading') {
|
|
2004
|
+
document.addEventListener('DOMContentLoaded', setup, { once: true });
|
|
2005
|
+
} else {
|
|
2006
|
+
setup();
|
|
2007
|
+
}
|
|
2008
|
+
})();
|
|
1771
2009
|
const refreshParent = (e) => {
|
|
1772
2010
|
let dispatched = false;
|
|
1773
2011
|
if (typeof window !== 'undefined' && typeof window.PinokioBroadcastMessage === 'function') {
|
|
@@ -1967,6 +2205,12 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
1967
2205
|
}
|
|
1968
2206
|
if (document.querySelector("#refresh-page")) {
|
|
1969
2207
|
document.querySelector("#refresh-page").addEventListener("click", (e) => {
|
|
2208
|
+
try {
|
|
2209
|
+
const headerEl = document.querySelector("header.navheader");
|
|
2210
|
+
const isMinimized = !!(headerEl && headerEl.classList.contains("minimized"));
|
|
2211
|
+
const key = `pinokio:header-restore-once:${location.pathname}`;
|
|
2212
|
+
sessionStorage.setItem(key, isMinimized ? "1" : "0");
|
|
2213
|
+
} catch (_) {}
|
|
1970
2214
|
location.reload()
|
|
1971
2215
|
/*
|
|
1972
2216
|
let browserview = document.querySelector(".browserview")
|
package/server/public/layout.js
CHANGED
|
@@ -790,4 +790,118 @@
|
|
|
790
790
|
};
|
|
791
791
|
|
|
792
792
|
window.PinokioLayout = api;
|
|
793
|
+
// Mobile "Tap to connect" curtain is centralized in common.js to avoid duplicates
|
|
794
|
+
|
|
795
|
+
// Top-level notification listener (indicator + optional chime) for mobile
|
|
796
|
+
(function initTopLevelNotificationListener() {
|
|
797
|
+
try { if (window.top && window.top !== window) return; } catch (_) { return; }
|
|
798
|
+
if (window.__pinokioTopNotifyListener) {
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
window.__pinokioTopNotifyListener = true;
|
|
802
|
+
|
|
803
|
+
const ensureIndicator = (() => {
|
|
804
|
+
let el = null;
|
|
805
|
+
let styleInjected = false;
|
|
806
|
+
return () => {
|
|
807
|
+
if (!styleInjected) {
|
|
808
|
+
const style = document.createElement('style');
|
|
809
|
+
style.textContent = `
|
|
810
|
+
.pinokio-notify-indicator{position:fixed;top:12px;right:12px;z-index:2147483647;display:none;align-items:center;gap:8px;padding:8px 10px;border-radius:999px;background:rgba(15,23,42,0.92);color:#fff;font:600 12px/1.2 system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;box-shadow:0 10px 30px rgba(0,0,0,0.35)}
|
|
811
|
+
.pinokio-notify-indicator .bell{font-size:14px}
|
|
812
|
+
.pinokio-notify-indicator.show{display:inline-flex;animation:pinokioNotifyPop 160ms ease-out, pinokioNotifyFade 1600ms ease-in 700ms forwards}
|
|
813
|
+
@keyframes pinokioNotifyPop{from{transform:translateY(-6px) scale(.98);opacity:0}to{transform:translateY(0) scale(1);opacity:1}}
|
|
814
|
+
@keyframes pinokioNotifyFade{to{opacity:0;transform:translateY(-4px)}}
|
|
815
|
+
@media (max-width: 768px){.pinokio-notify-indicator{top:10px;right:10px;padding:7px 9px;font-size:12px}}
|
|
816
|
+
`;
|
|
817
|
+
document.head.appendChild(style);
|
|
818
|
+
styleInjected = true;
|
|
819
|
+
}
|
|
820
|
+
if (!el) {
|
|
821
|
+
el = document.createElement('div');
|
|
822
|
+
el.className = 'pinokio-notify-indicator';
|
|
823
|
+
const icon = document.createElement('span');
|
|
824
|
+
icon.className = 'bell';
|
|
825
|
+
icon.textContent = '🔔';
|
|
826
|
+
const text = document.createElement('span');
|
|
827
|
+
text.className = 'text';
|
|
828
|
+
text.textContent = 'Notification received';
|
|
829
|
+
el.appendChild(icon);
|
|
830
|
+
el.appendChild(text);
|
|
831
|
+
document.body.appendChild(el);
|
|
832
|
+
}
|
|
833
|
+
return el;
|
|
834
|
+
};
|
|
835
|
+
})();
|
|
836
|
+
|
|
837
|
+
const flashIndicator = (message) => {
|
|
838
|
+
const node = ensureIndicator();
|
|
839
|
+
const text = node.querySelector('.text');
|
|
840
|
+
if (text) {
|
|
841
|
+
const msg = (message && typeof message === 'string' && message.trim()) ? message.trim() : 'Notification received';
|
|
842
|
+
text.textContent = msg.length > 80 ? (msg.slice(0,77) + '…') : msg;
|
|
843
|
+
}
|
|
844
|
+
node.classList.remove('show');
|
|
845
|
+
void node.offsetWidth;
|
|
846
|
+
node.classList.add('show');
|
|
847
|
+
setTimeout(() => node.classList.remove('show'), 2400);
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
const tryPlay = (url) => {
|
|
851
|
+
try {
|
|
852
|
+
const src = (typeof url === 'string' && url) ? url : '/chime.mp3';
|
|
853
|
+
let a = window.__pinokioChimeAudio;
|
|
854
|
+
if (!a) {
|
|
855
|
+
a = new Audio(src);
|
|
856
|
+
a.preload = 'auto';
|
|
857
|
+
a.loop = false;
|
|
858
|
+
a.muted = false;
|
|
859
|
+
window.__pinokioChimeAudio = a;
|
|
860
|
+
} else {
|
|
861
|
+
try { if (a.src && !a.src.endsWith(src)) a.src = src; } catch (_) {}
|
|
862
|
+
}
|
|
863
|
+
try { a.currentTime = 0; } catch (_) {}
|
|
864
|
+
const p = a.play();
|
|
865
|
+
if (p && typeof p.catch === 'function') { p.catch(() => {}); }
|
|
866
|
+
if (typeof navigator !== 'undefined' && typeof navigator.vibrate === 'function') {
|
|
867
|
+
try { navigator.vibrate(80); } catch (_) {}
|
|
868
|
+
}
|
|
869
|
+
} catch (_) {}
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
const listen = () => {
|
|
873
|
+
const SocketCtor = typeof window.Socket === 'function' ? window.Socket : (typeof Socket === 'function' ? Socket : null);
|
|
874
|
+
if (!SocketCtor || typeof WebSocket === 'undefined') {
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
const socket = new SocketCtor();
|
|
878
|
+
try {
|
|
879
|
+
socket.run({ method: 'kernel.notifications', mode: 'listen', device_id: (typeof window.PinokioGetDeviceId === 'function') ? window.PinokioGetDeviceId() : undefined }, (packet) => {
|
|
880
|
+
if (!packet || packet.id !== 'kernel.notifications' || packet.type !== 'notification') {
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
const payload = packet.data || {};
|
|
884
|
+
// If targeted to a specific device, ignore only when our id exists and mismatches
|
|
885
|
+
try {
|
|
886
|
+
const targetId = (typeof payload.device_id === 'string' && payload.device_id.trim()) ? payload.device_id.trim() : null;
|
|
887
|
+
if (targetId) {
|
|
888
|
+
const myId = (typeof window.PinokioGetDeviceId === 'function') ? window.PinokioGetDeviceId() : null;
|
|
889
|
+
if (myId && myId !== targetId) return;
|
|
890
|
+
}
|
|
891
|
+
} catch (_) {}
|
|
892
|
+
flashIndicator(payload.message);
|
|
893
|
+
tryPlay(payload.sound);
|
|
894
|
+
}).then(() => {
|
|
895
|
+
// socket closed; ignore
|
|
896
|
+
}).catch(() => {});
|
|
897
|
+
window.__pinokioTopNotifySocket = socket;
|
|
898
|
+
} catch (_) {}
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
if (document.readyState === 'loading') {
|
|
902
|
+
document.addEventListener('DOMContentLoaded', listen, { once: true });
|
|
903
|
+
} else {
|
|
904
|
+
listen();
|
|
905
|
+
}
|
|
906
|
+
})();
|
|
793
907
|
})();
|