pinokiod 3.133.0 → 3.135.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/connect/backend.js +0 -1
- package/kernel/connect/providers/huggingface/index.js +0 -1
- package/kernel/index.js +1 -1
- package/kernel/util.js +124 -2
- package/package.json +1 -1
- package/server/index.js +6 -5
- package/server/public/common.js +315 -6
- package/server/public/nav.js +18 -0
- package/server/socket.js +58 -1
- package/server/views/app.ejs +162 -1
- package/server/views/connect/x.ejs +1 -1
- package/server/views/connect.ejs +1 -1
- package/server/views/container.ejs +1 -1
- package/server/views/download.ejs +1 -1
- package/server/views/explore.ejs +1 -1
- package/server/views/form.ejs +1 -1
- package/server/views/frame.ejs +1 -1
- package/server/views/github.ejs +1 -1
- package/server/views/help.ejs +1 -1
- package/server/views/index.ejs +1 -1
- package/server/views/index2.ejs +1 -1
- package/server/views/init/index.ejs +1 -1
- package/server/views/mini.ejs +1 -1
- package/server/views/net.ejs +1 -1
- package/server/views/network.ejs +1 -1
- package/server/views/network2.ejs +1 -1
- package/server/views/old_network.ejs +1 -1
- package/server/views/prototype/index.ejs +1 -1
- package/server/views/review.ejs +1 -1
- package/server/views/screenshots.ejs +1 -1
- package/server/views/settings.ejs +1 -1
- package/server/views/setup.ejs +1 -1
- package/server/views/setup_home.ejs +1 -1
- package/server/views/terminals.ejs +1 -1
- package/server/views/tools.ejs +1 -1
|
@@ -47,7 +47,6 @@ class Backend {
|
|
|
47
47
|
let authPath = this.kernel.path(`connect/${this.name}/auth.json`)
|
|
48
48
|
this.auth = (await this.kernel.loader.load(authPath)).resolved
|
|
49
49
|
if (!this.auth) {
|
|
50
|
-
console.log("not authenticated")
|
|
51
50
|
return null
|
|
52
51
|
}
|
|
53
52
|
if (!this.auth.refresh_token) {
|
package/kernel/index.js
CHANGED
|
@@ -488,7 +488,7 @@ class Kernel {
|
|
|
488
488
|
let changed
|
|
489
489
|
let new_config = JSON.stringify(await this.peer.current_host())
|
|
490
490
|
if (this.old_config !== new_config) {
|
|
491
|
-
console.log("Proc config has changed. update router.")
|
|
491
|
+
// console.log("Proc config has changed. update router.")
|
|
492
492
|
changed = true
|
|
493
493
|
} else {
|
|
494
494
|
// console.log("Proc config is the same")
|
package/kernel/util.js
CHANGED
|
@@ -30,6 +30,70 @@ const {
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
const platform = os.platform()
|
|
33
|
+
const WINDOWS_TOAST_APP_ID = process.env.PINOKIO_WINDOWS_APP_ID || 'computer.pinokio'
|
|
34
|
+
const DEFAULT_CHIME_URL_PATH = '/chime.mp3'
|
|
35
|
+
const pushListeners = new Set()
|
|
36
|
+
|
|
37
|
+
function registerPushListener(listener) {
|
|
38
|
+
if (typeof listener !== 'function') {
|
|
39
|
+
throw new TypeError('push listener must be a function')
|
|
40
|
+
}
|
|
41
|
+
pushListeners.add(listener)
|
|
42
|
+
return () => pushListeners.delete(listener)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function emitPushEvent(event) {
|
|
46
|
+
if (!event) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
pushListeners.forEach((listener) => {
|
|
50
|
+
try {
|
|
51
|
+
listener(event)
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.error('Push listener error:', err)
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function resolvePublicAssetUrl(filePath) {
|
|
59
|
+
if (!filePath) {
|
|
60
|
+
return null
|
|
61
|
+
}
|
|
62
|
+
if (/^https?:\/\//i.test(filePath)) {
|
|
63
|
+
return filePath
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const absolute = path.resolve(filePath)
|
|
67
|
+
const publicRoot = path.resolve(__dirname, '../server/public')
|
|
68
|
+
if (absolute === publicRoot) {
|
|
69
|
+
return '/'
|
|
70
|
+
}
|
|
71
|
+
if (absolute.startsWith(publicRoot + path.sep) || absolute === publicRoot) {
|
|
72
|
+
const relative = path.relative(publicRoot, absolute).replace(/\\/g, '/')
|
|
73
|
+
return '/' + relative
|
|
74
|
+
}
|
|
75
|
+
} catch (_) {
|
|
76
|
+
// ignore resolution failures
|
|
77
|
+
}
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function soundTargetToClientUrl(target) {
|
|
82
|
+
if (!target) {
|
|
83
|
+
return null
|
|
84
|
+
}
|
|
85
|
+
if (target.kind === 'url') {
|
|
86
|
+
return target.value
|
|
87
|
+
}
|
|
88
|
+
if (target.kind === 'file') {
|
|
89
|
+
const resolved = path.resolve(target.value)
|
|
90
|
+
if (resolved === DEFAULT_CHIME_PATH) {
|
|
91
|
+
return DEFAULT_CHIME_URL_PATH
|
|
92
|
+
}
|
|
93
|
+
return resolvePublicAssetUrl(resolved)
|
|
94
|
+
}
|
|
95
|
+
return null
|
|
96
|
+
}
|
|
33
97
|
function ensureNotifierBinaries() {
|
|
34
98
|
if (platform !== 'darwin') {
|
|
35
99
|
return
|
|
@@ -729,10 +793,39 @@ function push(params) {
|
|
|
729
793
|
if (!notifyParams.contentImage) {
|
|
730
794
|
notifyParams.contentImage = path.resolve(__dirname, "../server/public/pinokio-black.png")
|
|
731
795
|
}
|
|
796
|
+
if (platform === 'win32') {
|
|
797
|
+
// Ensure Windows toast branding aligns with Pinokio assets.
|
|
798
|
+
if (!notifyParams.icon && notifyParams.contentImage) {
|
|
799
|
+
notifyParams.icon = notifyParams.contentImage
|
|
800
|
+
}
|
|
801
|
+
if (!notifyParams.appID && !notifyParams.appName) {
|
|
802
|
+
notifyParams.appID = WINDOWS_TOAST_APP_ID
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
const clientSoundUrl = soundTargetToClientUrl(customSoundTarget)
|
|
732
807
|
if (customSoundTarget) {
|
|
733
808
|
notifyParams.sound = false
|
|
734
|
-
|
|
809
|
+
const shouldPlayLocally = platform !== 'win32' || !clientSoundUrl
|
|
810
|
+
if (shouldPlayLocally) {
|
|
811
|
+
scheduleSoundPlayback(customSoundTarget)
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
const clientImage = resolvePublicAssetUrl(notifyParams.contentImage) || resolvePublicAssetUrl(notifyParams.image)
|
|
816
|
+
const eventPayload = {
|
|
817
|
+
id: randomUUID(),
|
|
818
|
+
title: notifyParams.title,
|
|
819
|
+
subtitle: notifyParams.subtitle || null,
|
|
820
|
+
message: notifyParams.message || '',
|
|
821
|
+
image: clientImage,
|
|
822
|
+
sound: clientSoundUrl,
|
|
823
|
+
timestamp: Date.now(),
|
|
824
|
+
platform,
|
|
735
825
|
}
|
|
826
|
+
|
|
827
|
+
emitPushEvent(eventPayload)
|
|
828
|
+
|
|
736
829
|
console.log("notifyParams", notifyParams)
|
|
737
830
|
notifier.notify(notifyParams)
|
|
738
831
|
}
|
|
@@ -1076,5 +1169,34 @@ const rewrite_localhost= (kernel, obj, source) => {
|
|
|
1076
1169
|
|
|
1077
1170
|
|
|
1078
1171
|
module.exports = {
|
|
1079
|
-
parse_env,
|
|
1172
|
+
parse_env,
|
|
1173
|
+
log_path,
|
|
1174
|
+
api_path,
|
|
1175
|
+
api_name,
|
|
1176
|
+
update_env,
|
|
1177
|
+
parse_env_detail,
|
|
1178
|
+
openfs,
|
|
1179
|
+
port_running,
|
|
1180
|
+
du,
|
|
1181
|
+
is_port_available,
|
|
1182
|
+
find_python,
|
|
1183
|
+
find_venv,
|
|
1184
|
+
fill_object,
|
|
1185
|
+
run,
|
|
1186
|
+
openURL,
|
|
1187
|
+
u2p,
|
|
1188
|
+
p2u,
|
|
1189
|
+
log,
|
|
1190
|
+
diffLinesWithContext,
|
|
1191
|
+
classifyChange,
|
|
1192
|
+
push,
|
|
1193
|
+
filepicker,
|
|
1194
|
+
exists,
|
|
1195
|
+
clipboard,
|
|
1196
|
+
mergeLines,
|
|
1197
|
+
ignore_subrepos,
|
|
1198
|
+
rewrite_localhost,
|
|
1199
|
+
symlink,
|
|
1200
|
+
file_type,
|
|
1201
|
+
registerPushListener,
|
|
1080
1202
|
}
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -449,9 +449,6 @@ class Server {
|
|
|
449
449
|
} else {
|
|
450
450
|
cfg.menu = cfg.menu(this.kernel, this.kernel.info)
|
|
451
451
|
}
|
|
452
|
-
cfg = await this.renderIndex(name, cfg)
|
|
453
|
-
} else if (Array.isArray(cfg.menu)) {
|
|
454
|
-
cfg = await this.renderIndex(name, cfg)
|
|
455
452
|
}
|
|
456
453
|
} else {
|
|
457
454
|
cfg = await this.renderIndex(name, cfg)
|
|
@@ -698,7 +695,7 @@ class Server {
|
|
|
698
695
|
if (config && config.version) {
|
|
699
696
|
let coerced = semver.coerce(config.version)
|
|
700
697
|
if (semver.satisfies(coerced, this.kernel.schema)) {
|
|
701
|
-
console.log("semver satisfied", config.version, this.kernel.schema)
|
|
698
|
+
// console.log("semver satisfied", config.version, this.kernel.schema)
|
|
702
699
|
} else {
|
|
703
700
|
console.log("semver NOT satisfied", config.version, this.kernel.schema)
|
|
704
701
|
err = `Please update to the latest Pinokio (current script version: ${config.version}, supported: ${this.kernel.schema})`
|
|
@@ -3722,7 +3719,11 @@ class Server {
|
|
|
3722
3719
|
this.app.use(express.json());
|
|
3723
3720
|
this.app.use(express.urlencoded({ extended: true }));
|
|
3724
3721
|
this.app.use(cookieParser());
|
|
3725
|
-
this.app.use(session({
|
|
3722
|
+
this.app.use(session({
|
|
3723
|
+
secret: "secret",
|
|
3724
|
+
resave: false,
|
|
3725
|
+
saveUninitialized: false
|
|
3726
|
+
}))
|
|
3726
3727
|
this.app.use((req, res, next) => {
|
|
3727
3728
|
const originalRedirect = res.redirect;
|
|
3728
3729
|
res.redirect = function (url) {
|
package/server/public/common.js
CHANGED
|
@@ -1398,14 +1398,323 @@ const open_url2 = (href, target, features) => {
|
|
|
1398
1398
|
window.open(href, "_self", features)
|
|
1399
1399
|
}
|
|
1400
1400
|
}
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1401
|
+
if (typeof hotkeys === 'function') {
|
|
1402
|
+
hotkeys("ctrl+t,cmd+t,ctrl+n,cmd+n", (e) => {
|
|
1403
|
+
let agent = document.body.getAttribute("data-agent")
|
|
1404
|
+
if (agent === "electron") {
|
|
1405
|
+
window.open(location.href, "_blank", "pinokio")
|
|
1406
|
+
} else {
|
|
1407
|
+
window.open(location.href, "_blank")
|
|
1408
|
+
}
|
|
1409
|
+
})
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
(function initNotificationAudioBridge() {
|
|
1413
|
+
if (typeof window === 'undefined') {
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
if (window.__pinokioNotificationAudioInitialized) {
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
window.__pinokioNotificationAudioInitialized = true;
|
|
1420
|
+
|
|
1421
|
+
const CHANNEL_ID = 'kernel.notifications';
|
|
1422
|
+
const pendingSounds = [];
|
|
1423
|
+
let isPlaying = false;
|
|
1424
|
+
let currentSocket = null;
|
|
1425
|
+
let reconnectTimeout = null;
|
|
1426
|
+
let activeAudio = null;
|
|
1427
|
+
|
|
1428
|
+
const leaderStorageKey = 'pinokio.notification.leader';
|
|
1429
|
+
const leaderHeartbeatMs = 5000;
|
|
1430
|
+
const leaderStaleMs = 15000;
|
|
1431
|
+
const tabId = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
1432
|
+
const storageEnabled = (() => {
|
|
1433
|
+
try {
|
|
1434
|
+
const testKey = '__pinokio_notification_test__';
|
|
1435
|
+
localStorage.setItem(testKey, '1');
|
|
1436
|
+
localStorage.removeItem(testKey);
|
|
1437
|
+
return true;
|
|
1438
|
+
} catch (_) {
|
|
1439
|
+
return false;
|
|
1440
|
+
}
|
|
1441
|
+
})();
|
|
1442
|
+
let isLeader = false;
|
|
1443
|
+
let heartbeatTimer = null;
|
|
1444
|
+
let leadershipCheckTimer = null;
|
|
1445
|
+
|
|
1446
|
+
const parseLeaderValue = (value) => {
|
|
1447
|
+
if (!value) return null;
|
|
1448
|
+
try {
|
|
1449
|
+
return JSON.parse(value);
|
|
1450
|
+
} catch (_) {
|
|
1451
|
+
return null;
|
|
1452
|
+
}
|
|
1453
|
+
};
|
|
1454
|
+
|
|
1455
|
+
const writeHeartbeat = () => {
|
|
1456
|
+
try {
|
|
1457
|
+
localStorage.setItem(leaderStorageKey, JSON.stringify({ id: tabId, ts: Date.now() }));
|
|
1458
|
+
} catch (err) {
|
|
1459
|
+
console.warn('Notification leader heartbeat failed:', err);
|
|
1460
|
+
}
|
|
1461
|
+
};
|
|
1462
|
+
|
|
1463
|
+
const clearHeartbeat = () => {
|
|
1464
|
+
if (heartbeatTimer) {
|
|
1465
|
+
clearInterval(heartbeatTimer);
|
|
1466
|
+
heartbeatTimer = null;
|
|
1467
|
+
}
|
|
1468
|
+
};
|
|
1469
|
+
|
|
1470
|
+
const stopSocket = () => {
|
|
1471
|
+
if (currentSocket && typeof currentSocket.close === 'function') {
|
|
1472
|
+
try {
|
|
1473
|
+
currentSocket.close();
|
|
1474
|
+
} catch (_) {
|
|
1475
|
+
// ignore
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
currentSocket = null;
|
|
1479
|
+
};
|
|
1480
|
+
|
|
1481
|
+
const stopAudio = () => {
|
|
1482
|
+
pendingSounds.length = 0;
|
|
1483
|
+
if (activeAudio) {
|
|
1484
|
+
try {
|
|
1485
|
+
activeAudio.pause();
|
|
1486
|
+
activeAudio.currentTime = 0;
|
|
1487
|
+
} catch (_) {
|
|
1488
|
+
// ignore
|
|
1489
|
+
}
|
|
1490
|
+
activeAudio = null;
|
|
1491
|
+
}
|
|
1492
|
+
isPlaying = false;
|
|
1493
|
+
};
|
|
1494
|
+
|
|
1495
|
+
const resignLeadership = () => {
|
|
1496
|
+
if (!isLeader) {
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
isLeader = false;
|
|
1500
|
+
clearHeartbeat();
|
|
1501
|
+
if (reconnectTimeout != null) {
|
|
1502
|
+
clearTimeout(reconnectTimeout);
|
|
1503
|
+
reconnectTimeout = null;
|
|
1504
|
+
}
|
|
1505
|
+
stopSocket();
|
|
1506
|
+
stopAudio();
|
|
1507
|
+
try {
|
|
1508
|
+
const stored = parseLeaderValue(localStorage.getItem(leaderStorageKey));
|
|
1509
|
+
if (stored && stored.id === tabId) {
|
|
1510
|
+
localStorage.removeItem(leaderStorageKey);
|
|
1511
|
+
}
|
|
1512
|
+
} catch (_) {
|
|
1513
|
+
// ignore
|
|
1514
|
+
}
|
|
1515
|
+
};
|
|
1516
|
+
|
|
1517
|
+
const startLeadership = () => {
|
|
1518
|
+
if (isLeader) {
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
isLeader = true;
|
|
1522
|
+
writeHeartbeat();
|
|
1523
|
+
if (!heartbeatTimer) {
|
|
1524
|
+
heartbeatTimer = setInterval(writeHeartbeat, leaderHeartbeatMs);
|
|
1525
|
+
}
|
|
1526
|
+
connect();
|
|
1527
|
+
};
|
|
1528
|
+
|
|
1529
|
+
const playNextSound = () => {
|
|
1530
|
+
if (isPlaying) {
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
const next = pendingSounds.shift();
|
|
1534
|
+
if (!next) {
|
|
1535
|
+
return;
|
|
1536
|
+
}
|
|
1537
|
+
isPlaying = true;
|
|
1538
|
+
activeAudio = new Audio(next);
|
|
1539
|
+
activeAudio.preload = 'auto';
|
|
1540
|
+
const cleanup = () => {
|
|
1541
|
+
if (!activeAudio) {
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
activeAudio.removeEventListener('ended', handleEnded);
|
|
1545
|
+
activeAudio.removeEventListener('error', handleError);
|
|
1546
|
+
activeAudio = null;
|
|
1547
|
+
isPlaying = false;
|
|
1548
|
+
playNextSound();
|
|
1549
|
+
};
|
|
1550
|
+
const handleEnded = () => cleanup();
|
|
1551
|
+
const handleError = (err) => {
|
|
1552
|
+
console.error('Notification audio playback failed:', err);
|
|
1553
|
+
cleanup();
|
|
1554
|
+
};
|
|
1555
|
+
activeAudio.addEventListener('ended', handleEnded, { once: true });
|
|
1556
|
+
activeAudio.addEventListener('error', handleError, { once: true });
|
|
1557
|
+
const playPromise = activeAudio.play();
|
|
1558
|
+
if (playPromise && typeof playPromise.catch === 'function') {
|
|
1559
|
+
playPromise.catch((err) => {
|
|
1560
|
+
console.error('Notification audio play() rejected:', err);
|
|
1561
|
+
cleanup();
|
|
1562
|
+
});
|
|
1563
|
+
}
|
|
1564
|
+
};
|
|
1565
|
+
|
|
1566
|
+
const enqueueSound = (url) => {
|
|
1567
|
+
if (!url) {
|
|
1568
|
+
return;
|
|
1569
|
+
}
|
|
1570
|
+
pendingSounds.push(url);
|
|
1571
|
+
playNextSound();
|
|
1572
|
+
};
|
|
1573
|
+
|
|
1574
|
+
const handlePacket = (packet) => {
|
|
1575
|
+
if (!packet || packet.id !== CHANNEL_ID || packet.type !== 'notification') {
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
const payload = packet.data || {};
|
|
1579
|
+
if (typeof payload.sound === 'string' && payload.sound) {
|
|
1580
|
+
enqueueSound(payload.sound);
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
|
|
1584
|
+
const attemptLeadership = () => {
|
|
1585
|
+
if (isLeader) {
|
|
1586
|
+
writeHeartbeat();
|
|
1587
|
+
return;
|
|
1588
|
+
}
|
|
1589
|
+
let current = null;
|
|
1590
|
+
try {
|
|
1591
|
+
current = parseLeaderValue(localStorage.getItem(leaderStorageKey));
|
|
1592
|
+
} catch (_) {
|
|
1593
|
+
current = null;
|
|
1594
|
+
}
|
|
1595
|
+
const now = Date.now();
|
|
1596
|
+
const isStale = !current || !current.ts || (now - current.ts) > leaderStaleMs;
|
|
1597
|
+
if (isStale || (current && current.id === tabId)) {
|
|
1598
|
+
writeHeartbeat();
|
|
1599
|
+
const freshlyStored = parseLeaderValue(localStorage.getItem(leaderStorageKey));
|
|
1600
|
+
if (freshlyStored && freshlyStored.id === tabId) {
|
|
1601
|
+
startLeadership();
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1605
|
+
|
|
1606
|
+
const scheduleReconnect = (delay) => {
|
|
1607
|
+
if (!isLeader) {
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
if (reconnectTimeout != null) {
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1613
|
+
reconnectTimeout = setTimeout(() => {
|
|
1614
|
+
reconnectTimeout = null;
|
|
1615
|
+
connect();
|
|
1616
|
+
}, delay);
|
|
1617
|
+
};
|
|
1618
|
+
|
|
1619
|
+
const connect = () => {
|
|
1620
|
+
if (!isLeader) {
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
const SocketCtor = typeof window.Socket === 'function' ? window.Socket : (typeof Socket === 'function' ? Socket : null);
|
|
1624
|
+
if (!SocketCtor) {
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
if (typeof WebSocket === 'undefined') {
|
|
1628
|
+
return;
|
|
1629
|
+
}
|
|
1630
|
+
if (currentSocket && currentSocket.ws && currentSocket.ws.readyState === WebSocket.OPEN) {
|
|
1631
|
+
return;
|
|
1632
|
+
}
|
|
1633
|
+
if (currentSocket && typeof currentSocket.close === 'function') {
|
|
1634
|
+
try {
|
|
1635
|
+
currentSocket.close();
|
|
1636
|
+
} catch (_) {
|
|
1637
|
+
// ignore
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
const socket = new SocketCtor();
|
|
1641
|
+
try {
|
|
1642
|
+
const promise = socket.run(
|
|
1643
|
+
{
|
|
1644
|
+
method: CHANNEL_ID,
|
|
1645
|
+
mode: 'listen',
|
|
1646
|
+
},
|
|
1647
|
+
handlePacket
|
|
1648
|
+
);
|
|
1649
|
+
currentSocket = socket;
|
|
1650
|
+
promise.then(() => {
|
|
1651
|
+
// Attempt to reconnect after a brief delay when the socket closes normally.
|
|
1652
|
+
if (currentSocket === socket) {
|
|
1653
|
+
currentSocket = null;
|
|
1654
|
+
scheduleReconnect(1500);
|
|
1655
|
+
}
|
|
1656
|
+
}).catch((err) => {
|
|
1657
|
+
console.warn('Notification listener socket closed with error:', err);
|
|
1658
|
+
if (currentSocket === socket) {
|
|
1659
|
+
currentSocket = null;
|
|
1660
|
+
scheduleReconnect(2500);
|
|
1661
|
+
}
|
|
1662
|
+
});
|
|
1663
|
+
window.__pinokioNotificationSocket = socket;
|
|
1664
|
+
} catch (err) {
|
|
1665
|
+
console.error('Failed to establish notification listener socket:', err);
|
|
1666
|
+
scheduleReconnect(3000);
|
|
1667
|
+
}
|
|
1668
|
+
};
|
|
1669
|
+
|
|
1670
|
+
if (!storageEnabled) {
|
|
1671
|
+
isLeader = true;
|
|
1672
|
+
const start = () => connect();
|
|
1673
|
+
if (document.readyState === 'loading') {
|
|
1674
|
+
document.addEventListener('DOMContentLoaded', start, { once: true });
|
|
1675
|
+
} else {
|
|
1676
|
+
connect();
|
|
1677
|
+
}
|
|
1678
|
+
window.addEventListener('beforeunload', () => {
|
|
1679
|
+
stopSocket();
|
|
1680
|
+
stopAudio();
|
|
1681
|
+
});
|
|
1682
|
+
return;
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
if (document.readyState === 'loading') {
|
|
1686
|
+
document.addEventListener('DOMContentLoaded', connect, { once: true });
|
|
1405
1687
|
} else {
|
|
1406
|
-
|
|
1688
|
+
connect();
|
|
1407
1689
|
}
|
|
1408
|
-
|
|
1690
|
+
|
|
1691
|
+
if (!leadershipCheckTimer) {
|
|
1692
|
+
leadershipCheckTimer = setInterval(attemptLeadership, leaderHeartbeatMs);
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
window.addEventListener('storage', (event) => {
|
|
1696
|
+
if (event.key !== leaderStorageKey) {
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
const data = parseLeaderValue(event.newValue);
|
|
1700
|
+
if (data && data.id === tabId) {
|
|
1701
|
+
startLeadership();
|
|
1702
|
+
} else {
|
|
1703
|
+
resignLeadership();
|
|
1704
|
+
}
|
|
1705
|
+
});
|
|
1706
|
+
|
|
1707
|
+
window.addEventListener('beforeunload', () => {
|
|
1708
|
+
if (leadershipCheckTimer) {
|
|
1709
|
+
clearInterval(leadershipCheckTimer);
|
|
1710
|
+
leadershipCheckTimer = null;
|
|
1711
|
+
}
|
|
1712
|
+
resignLeadership();
|
|
1713
|
+
});
|
|
1714
|
+
|
|
1715
|
+
// Attempt to become leader immediately on load.
|
|
1716
|
+
attemptLeadership();
|
|
1717
|
+
})();
|
|
1409
1718
|
const refreshParent = (e) => {
|
|
1410
1719
|
// if (window.parent === window.top) {
|
|
1411
1720
|
window.parent.postMessage(e, "*")
|
package/server/public/nav.js
CHANGED
|
@@ -18,6 +18,16 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
18
18
|
return;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
const dispatchHeaderState = (minimized, detail = {}) => {
|
|
22
|
+
if (typeof window === "undefined" || typeof window.CustomEvent !== "function") {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const payload = { minimized, ...detail };
|
|
26
|
+
document.dispatchEvent(new CustomEvent("pinokio:header-state", { detail: payload }));
|
|
27
|
+
const aliasEvent = minimized ? "pinokio:header-minimized" : "pinokio:header-restored";
|
|
28
|
+
document.dispatchEvent(new CustomEvent(aliasEvent, { detail: { ...payload } }));
|
|
29
|
+
};
|
|
30
|
+
|
|
21
31
|
const headerTitle = header.querySelector("h1") || header;
|
|
22
32
|
let dragHandle = headerTitle.querySelector(".header-drag-handle");
|
|
23
33
|
if (!dragHandle) {
|
|
@@ -45,6 +55,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
45
55
|
transitionHandler: null,
|
|
46
56
|
};
|
|
47
57
|
|
|
58
|
+
dispatchHeaderState(state.minimized, { phase: "init" });
|
|
59
|
+
|
|
48
60
|
const MIN_MARGIN = 8;
|
|
49
61
|
|
|
50
62
|
const clampPosition = (left, top) => {
|
|
@@ -139,6 +151,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
139
151
|
header.classList.add("minimized");
|
|
140
152
|
applyPosition(targetLeft, targetTop);
|
|
141
153
|
|
|
154
|
+
dispatchHeaderState(true, { phase: "start" });
|
|
155
|
+
|
|
142
156
|
const lastRect = header.getBoundingClientRect();
|
|
143
157
|
const deltaX = firstRect.left - lastRect.left;
|
|
144
158
|
const deltaY = firstRect.top - lastRect.top;
|
|
@@ -164,6 +178,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
164
178
|
state.transitionHandler = null;
|
|
165
179
|
stopTransition();
|
|
166
180
|
state.minimized = true;
|
|
181
|
+
dispatchHeaderState(true, { phase: "settled" });
|
|
167
182
|
};
|
|
168
183
|
|
|
169
184
|
header.addEventListener("transitionend", state.transitionHandler);
|
|
@@ -189,6 +204,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
189
204
|
header.style.right = state.originalPosition.right;
|
|
190
205
|
header.style.bottom = state.originalPosition.bottom;
|
|
191
206
|
|
|
207
|
+
dispatchHeaderState(false, { phase: "start" });
|
|
208
|
+
|
|
192
209
|
const lastRect = header.getBoundingClientRect();
|
|
193
210
|
const deltaX = firstRect.left - lastRect.left;
|
|
194
211
|
const deltaY = firstRect.top - lastRect.top;
|
|
@@ -213,6 +230,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
213
230
|
state.hasCustomPosition = false;
|
|
214
231
|
state.lastLeft = parseFloat(header.style.left) || 0;
|
|
215
232
|
state.lastTop = parseFloat(header.style.top) || 0;
|
|
233
|
+
dispatchHeaderState(false, { phase: "settled" });
|
|
216
234
|
};
|
|
217
235
|
|
|
218
236
|
header.addEventListener("transitionend", state.transitionHandler);
|
package/server/socket.js
CHANGED
|
@@ -3,6 +3,7 @@ const WebSocket = require('ws');
|
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const Util = require("../kernel/util")
|
|
5
5
|
const Environment = require("../kernel/environment")
|
|
6
|
+
const NOTIFICATION_CHANNEL = 'kernel.notifications'
|
|
6
7
|
class Socket {
|
|
7
8
|
constructor(parent) {
|
|
8
9
|
this.buffer = {}
|
|
@@ -15,6 +16,8 @@ class Socket {
|
|
|
15
16
|
// this.kernel = parent.kernel
|
|
16
17
|
const wss = new WebSocket.Server({ server: this.parent.server })
|
|
17
18
|
this.subscriptions = new Map(); // Initialize a Map to store the WebSocket connections interested in each event
|
|
19
|
+
this.notificationChannel = NOTIFICATION_CHANNEL
|
|
20
|
+
this.notificationBridgeDispose = null
|
|
18
21
|
this.parent.kernel.api.listen("server.socket", this.trigger.bind(this))
|
|
19
22
|
wss.on('connection', (ws, request) => {
|
|
20
23
|
ws._headers = request.headers;
|
|
@@ -25,8 +28,15 @@ class Socket {
|
|
|
25
28
|
ws._origin = request.headers.origin;
|
|
26
29
|
ws.on('close', () => {
|
|
27
30
|
this.subscriptions.forEach((set, eventName) => {
|
|
31
|
+
if (!set) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
28
34
|
set.delete(ws);
|
|
35
|
+
if (set.size === 0) {
|
|
36
|
+
this.subscriptions.delete(eventName);
|
|
37
|
+
}
|
|
29
38
|
});
|
|
39
|
+
this.checkNotificationBridge();
|
|
30
40
|
});
|
|
31
41
|
ws.on('message', async (message, isBinary) => {
|
|
32
42
|
let req
|
|
@@ -217,7 +227,11 @@ class Socket {
|
|
|
217
227
|
if (!this.subscriptions.has(id)) {
|
|
218
228
|
this.subscriptions.set(id, new Set());
|
|
219
229
|
}
|
|
220
|
-
this.subscriptions.get(id)
|
|
230
|
+
const set = this.subscriptions.get(id)
|
|
231
|
+
set.add(ws);
|
|
232
|
+
if (set.size === 1 && id === this.notificationChannel) {
|
|
233
|
+
this.ensureNotificationBridge();
|
|
234
|
+
}
|
|
221
235
|
}
|
|
222
236
|
trigger(e) {
|
|
223
237
|
// send to id session
|
|
@@ -321,6 +335,49 @@ class Socket {
|
|
|
321
335
|
}
|
|
322
336
|
}
|
|
323
337
|
}
|
|
338
|
+
|
|
339
|
+
broadcastNotification(payload) {
|
|
340
|
+
if (!payload) {
|
|
341
|
+
return
|
|
342
|
+
}
|
|
343
|
+
const subscribers = this.subscriptions.get(this.notificationChannel)
|
|
344
|
+
if (!subscribers || subscribers.size === 0) {
|
|
345
|
+
return
|
|
346
|
+
}
|
|
347
|
+
const envelope = {
|
|
348
|
+
id: this.notificationChannel,
|
|
349
|
+
type: 'notification',
|
|
350
|
+
data: payload,
|
|
351
|
+
}
|
|
352
|
+
const frame = JSON.stringify(envelope)
|
|
353
|
+
subscribers.forEach((subscriber) => {
|
|
354
|
+
if (subscriber.readyState === WebSocket.OPEN) {
|
|
355
|
+
subscriber.send(frame)
|
|
356
|
+
}
|
|
357
|
+
})
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
ensureNotificationBridge() {
|
|
361
|
+
if (this.notificationBridgeDispose) {
|
|
362
|
+
return
|
|
363
|
+
}
|
|
364
|
+
this.notificationBridgeDispose = Util.registerPushListener((payload) => {
|
|
365
|
+
this.broadcastNotification(payload)
|
|
366
|
+
})
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
checkNotificationBridge() {
|
|
370
|
+
const subscribers = this.subscriptions.get(this.notificationChannel)
|
|
371
|
+
if ((!subscribers || subscribers.size === 0) && this.notificationBridgeDispose) {
|
|
372
|
+
try {
|
|
373
|
+
this.notificationBridgeDispose()
|
|
374
|
+
} catch (err) {
|
|
375
|
+
console.error('Failed to dispose notification bridge:', err)
|
|
376
|
+
}
|
|
377
|
+
this.notificationBridgeDispose = null
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
324
381
|
async log_buffer(key, buf) {
|
|
325
382
|
|
|
326
383
|
/*
|
package/server/views/app.ejs
CHANGED
|
@@ -180,6 +180,20 @@ body.dark .appcanvas_filler {
|
|
|
180
180
|
--sidebar-tab-outline: var(--pinokio-sidebar-tabbar-border);
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
+
.appcanvas > aside.appcanvas-aside-animating {
|
|
184
|
+
will-change: height, opacity;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.appcanvas > aside.appcanvas-aside-collapsed {
|
|
188
|
+
height: 0 !important;
|
|
189
|
+
opacity: 0;
|
|
190
|
+
pointer-events: none;
|
|
191
|
+
padding-top: 0;
|
|
192
|
+
padding-bottom: 0;
|
|
193
|
+
margin-top: 0;
|
|
194
|
+
margin-bottom: 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
183
197
|
/*
|
|
184
198
|
body.dark .appcanvas > aside {
|
|
185
199
|
background: var(--pinokio-sidebar-tabbar-bg);
|
|
@@ -2829,7 +2843,7 @@ body.dark {
|
|
|
2829
2843
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
2830
2844
|
</a>
|
|
2831
2845
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
2832
|
-
<div><i class="fa-solid fa-
|
|
2846
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
2833
2847
|
</button>
|
|
2834
2848
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
2835
2849
|
<div><i class="fa-solid fa-plus"></i></div>
|
|
@@ -8554,6 +8568,153 @@ body.dark {
|
|
|
8554
8568
|
|
|
8555
8569
|
</script>
|
|
8556
8570
|
<% } %>
|
|
8571
|
+
<script>
|
|
8572
|
+
(() => {
|
|
8573
|
+
const aside = document.querySelector(".appcanvas > aside")
|
|
8574
|
+
if (!aside) {
|
|
8575
|
+
return
|
|
8576
|
+
}
|
|
8577
|
+
|
|
8578
|
+
const collapsedClass = "appcanvas-aside-collapsed"
|
|
8579
|
+
const animatingClass = "appcanvas-aside-animating"
|
|
8580
|
+
const collapseDuration = 420
|
|
8581
|
+
const collapseEasing = "cubic-bezier(0.22, 1, 0.36, 1)"
|
|
8582
|
+
const expandEasing = "cubic-bezier(0.18, 0.85, 0.4, 1)"
|
|
8583
|
+
|
|
8584
|
+
let transitionHandler = null
|
|
8585
|
+
let queuedFrame = null
|
|
8586
|
+
|
|
8587
|
+
const clearAnimation = () => {
|
|
8588
|
+
if (transitionHandler) {
|
|
8589
|
+
aside.removeEventListener("transitionend", transitionHandler)
|
|
8590
|
+
transitionHandler = null
|
|
8591
|
+
}
|
|
8592
|
+
if (queuedFrame !== null) {
|
|
8593
|
+
cancelAnimationFrame(queuedFrame)
|
|
8594
|
+
queuedFrame = null
|
|
8595
|
+
}
|
|
8596
|
+
aside.classList.remove(animatingClass)
|
|
8597
|
+
aside.style.transition = ""
|
|
8598
|
+
aside.style.height = ""
|
|
8599
|
+
aside.style.opacity = ""
|
|
8600
|
+
}
|
|
8601
|
+
|
|
8602
|
+
const finish = (minimized) => {
|
|
8603
|
+
aside.classList.toggle(collapsedClass, minimized)
|
|
8604
|
+
aside.classList.remove(animatingClass)
|
|
8605
|
+
aside.style.transition = ""
|
|
8606
|
+
aside.style.height = ""
|
|
8607
|
+
aside.style.opacity = ""
|
|
8608
|
+
aside.dataset.asideState = minimized ? "collapsed" : "expanded"
|
|
8609
|
+
}
|
|
8610
|
+
|
|
8611
|
+
const collapse = (immediate) => {
|
|
8612
|
+
if (aside.dataset.asideState === "collapsed" && !immediate) {
|
|
8613
|
+
return
|
|
8614
|
+
}
|
|
8615
|
+
clearAnimation()
|
|
8616
|
+
if (immediate) {
|
|
8617
|
+
finish(true)
|
|
8618
|
+
return
|
|
8619
|
+
}
|
|
8620
|
+
const startHeight = aside.getBoundingClientRect().height
|
|
8621
|
+
if (startHeight <= 0.5) {
|
|
8622
|
+
finish(true)
|
|
8623
|
+
return
|
|
8624
|
+
}
|
|
8625
|
+
|
|
8626
|
+
aside.classList.add(animatingClass)
|
|
8627
|
+
aside.style.height = `${startHeight}px`
|
|
8628
|
+
aside.style.opacity = getComputedStyle(aside).opacity || "1"
|
|
8629
|
+
aside.dataset.asideState = "animating"
|
|
8630
|
+
|
|
8631
|
+
queuedFrame = requestAnimationFrame(() => {
|
|
8632
|
+
queuedFrame = null
|
|
8633
|
+
aside.style.transition = `height ${collapseDuration}ms ${collapseEasing}, opacity ${collapseDuration - 120}ms ease`
|
|
8634
|
+
aside.style.height = "0px"
|
|
8635
|
+
aside.style.opacity = "0"
|
|
8636
|
+
})
|
|
8637
|
+
|
|
8638
|
+
transitionHandler = (event) => {
|
|
8639
|
+
if (event.target !== aside || event.propertyName !== "height") {
|
|
8640
|
+
return
|
|
8641
|
+
}
|
|
8642
|
+
aside.removeEventListener("transitionend", transitionHandler)
|
|
8643
|
+
transitionHandler = null
|
|
8644
|
+
finish(true)
|
|
8645
|
+
}
|
|
8646
|
+
aside.addEventListener("transitionend", transitionHandler)
|
|
8647
|
+
}
|
|
8648
|
+
|
|
8649
|
+
const expand = (immediate) => {
|
|
8650
|
+
if (aside.dataset.asideState === "expanded" && !immediate) {
|
|
8651
|
+
return
|
|
8652
|
+
}
|
|
8653
|
+
clearAnimation()
|
|
8654
|
+
if (immediate) {
|
|
8655
|
+
finish(false)
|
|
8656
|
+
return
|
|
8657
|
+
}
|
|
8658
|
+
|
|
8659
|
+
aside.classList.remove(collapsedClass)
|
|
8660
|
+
const targetHeight = aside.scrollHeight
|
|
8661
|
+
aside.classList.add(animatingClass)
|
|
8662
|
+
aside.style.height = "0px"
|
|
8663
|
+
aside.style.opacity = "0"
|
|
8664
|
+
aside.dataset.asideState = "animating"
|
|
8665
|
+
|
|
8666
|
+
aside.offsetHeight
|
|
8667
|
+
|
|
8668
|
+
queuedFrame = requestAnimationFrame(() => {
|
|
8669
|
+
queuedFrame = null
|
|
8670
|
+
aside.style.transition = `height ${collapseDuration}ms ${expandEasing}, opacity ${collapseDuration - 140}ms ease`
|
|
8671
|
+
aside.style.height = `${targetHeight}px`
|
|
8672
|
+
aside.style.opacity = ""
|
|
8673
|
+
})
|
|
8674
|
+
|
|
8675
|
+
transitionHandler = (event) => {
|
|
8676
|
+
if (event.target !== aside || event.propertyName !== "height") {
|
|
8677
|
+
return
|
|
8678
|
+
}
|
|
8679
|
+
aside.removeEventListener("transitionend", transitionHandler)
|
|
8680
|
+
transitionHandler = null
|
|
8681
|
+
finish(false)
|
|
8682
|
+
}
|
|
8683
|
+
aside.addEventListener("transitionend", transitionHandler)
|
|
8684
|
+
}
|
|
8685
|
+
|
|
8686
|
+
const setAside = (minimized, immediate) => {
|
|
8687
|
+
if (minimized) {
|
|
8688
|
+
collapse(immediate)
|
|
8689
|
+
} else {
|
|
8690
|
+
expand(immediate)
|
|
8691
|
+
}
|
|
8692
|
+
}
|
|
8693
|
+
|
|
8694
|
+
const header = document.querySelector("header.navheader")
|
|
8695
|
+
const initialMinimized = !!(header && header.classList.contains("minimized"))
|
|
8696
|
+
setAside(initialMinimized, true)
|
|
8697
|
+
|
|
8698
|
+
document.addEventListener("pinokio:header-state", (event) => {
|
|
8699
|
+
if (!event || !event.detail) {
|
|
8700
|
+
return
|
|
8701
|
+
}
|
|
8702
|
+
const { minimized, phase } = event.detail
|
|
8703
|
+
if (phase === "init") {
|
|
8704
|
+
setAside(!!minimized, true)
|
|
8705
|
+
return
|
|
8706
|
+
}
|
|
8707
|
+
if (phase === "start") {
|
|
8708
|
+
setAside(!!minimized, false)
|
|
8709
|
+
return
|
|
8710
|
+
}
|
|
8711
|
+
if (phase === "settled") {
|
|
8712
|
+
clearAnimation()
|
|
8713
|
+
finish(!!minimized)
|
|
8714
|
+
}
|
|
8715
|
+
})
|
|
8716
|
+
})()
|
|
8717
|
+
</script>
|
|
8557
8718
|
<script src="/tab-idle-notifier.js"></script>
|
|
8558
8719
|
</body>
|
|
8559
8720
|
</html>
|
|
@@ -206,7 +206,7 @@ pre {
|
|
|
206
206
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
207
207
|
</a>
|
|
208
208
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
209
|
-
<div><i class="fa-solid fa-
|
|
209
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
210
210
|
</button>
|
|
211
211
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
212
212
|
<div><i class="fa-solid fa-plus"></i></div>
|
package/server/views/connect.ejs
CHANGED
|
@@ -831,7 +831,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
831
831
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
832
832
|
</a>
|
|
833
833
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
834
|
-
<div><i class="fa-solid fa-
|
|
834
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
835
835
|
</button>
|
|
836
836
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
837
837
|
<div><i class="fa-solid fa-plus"></i></div>
|
|
@@ -345,7 +345,7 @@ iframe {
|
|
|
345
345
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
346
346
|
</a>
|
|
347
347
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
348
|
-
<div><i class="fa-solid fa-
|
|
348
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
349
349
|
</button>
|
|
350
350
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
351
351
|
<div><i class="fa-solid fa-plus"></i></div>
|
|
@@ -134,7 +134,7 @@ body.frozen {
|
|
|
134
134
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
135
135
|
</a>
|
|
136
136
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
137
|
-
<div><i class="fa-solid fa-
|
|
137
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
138
138
|
</button>
|
|
139
139
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
140
140
|
<div><i class="fa-solid fa-plus"></i></div>
|
package/server/views/explore.ejs
CHANGED
|
@@ -135,7 +135,7 @@ body main iframe {
|
|
|
135
135
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
136
136
|
</a>
|
|
137
137
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
138
|
-
<div><i class="fa-solid fa-
|
|
138
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
139
139
|
</button>
|
|
140
140
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
141
141
|
<div><i class="fa-solid fa-plus"></i></div>
|
package/server/views/form.ejs
CHANGED
|
@@ -168,7 +168,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
168
168
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
169
169
|
</a>
|
|
170
170
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
171
|
-
<div><i class="fa-solid fa-
|
|
171
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
172
172
|
</button>
|
|
173
173
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
174
174
|
<div><i class="fa-solid fa-plus"></i></div>
|
package/server/views/frame.ejs
CHANGED
|
@@ -64,7 +64,7 @@ main iframe {
|
|
|
64
64
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
65
65
|
</a>
|
|
66
66
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
67
|
-
<div><i class="fa-solid fa-
|
|
67
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
68
68
|
</button>
|
|
69
69
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
70
70
|
<div><i class="fa-solid fa-plus"></i></div>
|
package/server/views/github.ejs
CHANGED
|
@@ -247,7 +247,7 @@ ol {
|
|
|
247
247
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
248
248
|
</a>
|
|
249
249
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
250
|
-
<div><i class="fa-solid fa-
|
|
250
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
251
251
|
</button>
|
|
252
252
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
253
253
|
<div><i class="fa-solid fa-plus"></i></div>
|
package/server/views/help.ejs
CHANGED
|
@@ -268,7 +268,7 @@ body.dark .item .tile .badge {
|
|
|
268
268
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
269
269
|
</a>
|
|
270
270
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
271
|
-
<div><i class="fa-solid fa-
|
|
271
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
272
272
|
</button>
|
|
273
273
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
274
274
|
<div><i class="fa-solid fa-plus"></i></div>
|
package/server/views/index.ejs
CHANGED
|
@@ -436,7 +436,7 @@ body.dark aside .current.selected {
|
|
|
436
436
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
437
437
|
</a>
|
|
438
438
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
439
|
-
<div><i class="fa-solid fa-
|
|
439
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
440
440
|
</button>
|
|
441
441
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
442
442
|
<div><i class="fa-solid fa-plus"></i></div>
|
package/server/views/index2.ejs
CHANGED
|
@@ -273,7 +273,7 @@ body.dark .open-menu, body.dark .browse {
|
|
|
273
273
|
<a id='downloadlogs' download class='hidden btn2' href="/pinokio/logs.zip"><div><i class="fa-solid fa-download"></i></div><div>Download logs</div></a>
|
|
274
274
|
<a class='btn2' href="/home?mode=settings"><div><i class="fa-solid fa-gear"></i></div><div>Settings</div></a>
|
|
275
275
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
276
|
-
<div><i class="fa-solid fa-
|
|
276
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
277
277
|
</button>
|
|
278
278
|
<button id='new-window' title='open a new window' class='btn2' data-agent="<%=agent%>"><div><i class="fa-solid fa-plus"></i></div><div>Window</div></button>
|
|
279
279
|
</div>
|
|
@@ -1533,7 +1533,7 @@ body.dark .ace-editor {
|
|
|
1533
1533
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
1534
1534
|
</a>
|
|
1535
1535
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
1536
|
-
<div><i class="fa-solid fa-
|
|
1536
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
1537
1537
|
</button>
|
|
1538
1538
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
1539
1539
|
<div><i class="fa-solid fa-plus"></i></div>
|
package/server/views/mini.ejs
CHANGED
|
@@ -768,7 +768,7 @@ body.dark .appcanvas {
|
|
|
768
768
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
769
769
|
</a>
|
|
770
770
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
771
|
-
<div><i class="fa-solid fa-
|
|
771
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
772
772
|
</button>
|
|
773
773
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
774
774
|
<div><i class="fa-solid fa-plus"></i></div>
|
package/server/views/net.ejs
CHANGED
|
@@ -554,7 +554,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
554
554
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
555
555
|
</a>
|
|
556
556
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
557
|
-
<div><i class="fa-solid fa-
|
|
557
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
558
558
|
</button>
|
|
559
559
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
560
560
|
<div><i class="fa-solid fa-plus"></i></div>
|
package/server/views/network.ejs
CHANGED
|
@@ -1067,7 +1067,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
1067
1067
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
1068
1068
|
</a>
|
|
1069
1069
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
1070
|
-
<div><i class="fa-solid fa-
|
|
1070
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
1071
1071
|
</button>
|
|
1072
1072
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
1073
1073
|
<div><i class="fa-solid fa-plus"></i></div>
|
|
@@ -429,7 +429,7 @@ table h3 {
|
|
|
429
429
|
<a id='downloadlogs' download class='hidden btn2' href="/pinokio/logs.zip"><div><i class="fa-solid fa-download"></i></div><div>Download logs</div></a>
|
|
430
430
|
<a class='btn2' href="/home?mode=settings"><div><i class="fa-solid fa-gear"></i></div><div>Settings</div></a>
|
|
431
431
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
432
|
-
<div><i class="fa-solid fa-
|
|
432
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
433
433
|
</button>
|
|
434
434
|
<button id='new-window' title='open a new window' class='btn2' data-agent="<%=agent%>"><div><i class="fa-solid fa-plus"></i></div><div>Window</div></button>
|
|
435
435
|
</div>
|
|
@@ -415,7 +415,7 @@ input:checked + .slider:before {
|
|
|
415
415
|
<a id='downloadlogs' download class='hidden btn2' href="/pinokio/logs.zip"><div><i class="fa-solid fa-download"></i></div><div>Download logs</div></a>
|
|
416
416
|
<a class='btn2' href="/home?mode=settings"><div><i class="fa-solid fa-gear"></i></div><div>Settings</div></a>
|
|
417
417
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
418
|
-
<div><i class="fa-solid fa-
|
|
418
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
419
419
|
</button>
|
|
420
420
|
<button id='new-window' title='open a new window' class='btn2' data-agent="<%=agent%>"><div><i class="fa-solid fa-plus"></i></div><div>Window</div></button>
|
|
421
421
|
</div>
|
|
@@ -1015,7 +1015,7 @@ body.dark .appcanvas {
|
|
|
1015
1015
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
1016
1016
|
</a>
|
|
1017
1017
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
1018
|
-
<div><i class="fa-solid fa-
|
|
1018
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
1019
1019
|
</button>
|
|
1020
1020
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
1021
1021
|
<div><i class="fa-solid fa-plus"></i></div>
|
package/server/views/review.ejs
CHANGED
|
@@ -955,7 +955,7 @@ body.dark .top-menu .btn2.selected {
|
|
|
955
955
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
956
956
|
</a>
|
|
957
957
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
958
|
-
<div><i class="fa-solid fa-
|
|
958
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
959
959
|
</button>
|
|
960
960
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
961
961
|
<div><i class="fa-solid fa-plus"></i></div>
|
|
@@ -727,7 +727,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
727
727
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
728
728
|
</a>
|
|
729
729
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
730
|
-
<div><i class="fa-solid fa-
|
|
730
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
731
731
|
</button>
|
|
732
732
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
733
733
|
<div><i class="fa-solid fa-plus"></i></div>
|
|
@@ -399,7 +399,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
399
399
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
400
400
|
</a>
|
|
401
401
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
402
|
-
<div><i class="fa-solid fa-
|
|
402
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
403
403
|
</button>
|
|
404
404
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
405
405
|
<div><i class="fa-solid fa-plus"></i></div>
|
package/server/views/setup.ejs
CHANGED
|
@@ -145,7 +145,7 @@ body {
|
|
|
145
145
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
146
146
|
</a>
|
|
147
147
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
148
|
-
<div><i class="fa-solid fa-
|
|
148
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
149
149
|
</button>
|
|
150
150
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
151
151
|
<div><i class="fa-solid fa-plus"></i></div>
|
|
@@ -161,7 +161,7 @@ body.dark .card {
|
|
|
161
161
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
162
162
|
</a>
|
|
163
163
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
164
|
-
<div><i class="fa-solid fa-
|
|
164
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
165
165
|
</button>
|
|
166
166
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
167
167
|
<div><i class="fa-solid fa-plus"></i></div>
|
|
@@ -382,7 +382,7 @@ body.dark .plugin-option:hover {
|
|
|
382
382
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
383
383
|
</a>
|
|
384
384
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
385
|
-
<div><i class="fa-solid fa-
|
|
385
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
386
386
|
</button>
|
|
387
387
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
388
388
|
<div><i class="fa-solid fa-plus"></i></div>
|
package/server/views/tools.ejs
CHANGED
|
@@ -1117,7 +1117,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
1117
1117
|
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
1118
1118
|
</a>
|
|
1119
1119
|
<button class='btn2' id='minimize-header' data-tippy-content="minimize header" title='minimize header'>
|
|
1120
|
-
<div><i class="fa-solid fa-
|
|
1120
|
+
<div><i class="fa-solid fa-expand"></i></div>
|
|
1121
1121
|
</button>
|
|
1122
1122
|
<button class='btn2' id='new-window' data-tippy-content="open a new window" title='open a new window' data-agent="<%=agent%>">
|
|
1123
1123
|
<div><i class="fa-solid fa-plus"></i></div>
|