pinokiod 3.94.0 → 3.95.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/util.js +3 -1
- package/package.json +1 -1
- package/server/index.js +60 -1
- package/server/public/tab-idle-notifier.js +61 -4
- package/server/views/app.ejs +3 -0
package/kernel/util.js
CHANGED
|
@@ -546,7 +546,9 @@ function push(params) {
|
|
|
546
546
|
if (!params.title) {
|
|
547
547
|
params.title = "Pinokio"
|
|
548
548
|
}
|
|
549
|
-
|
|
549
|
+
if (!params.contentImage) {
|
|
550
|
+
params.contentImage = path.resolve(__dirname, "../server/public/pinokio-black.png")
|
|
551
|
+
}
|
|
550
552
|
notifier.notify(params)
|
|
551
553
|
}
|
|
552
554
|
function p2u(localPath) {
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -3998,7 +3998,66 @@ class Server {
|
|
|
3998
3998
|
this.app.post("/push", ex(async (req, res) => {
|
|
3999
3999
|
console.log("Push", req.body)
|
|
4000
4000
|
try {
|
|
4001
|
-
|
|
4001
|
+
const payload = { ...(req.body || {}) }
|
|
4002
|
+
if (typeof payload.image === 'string' && payload.image.trim()) {
|
|
4003
|
+
const resolveAssetPath = (raw) => {
|
|
4004
|
+
if (typeof raw !== 'string') {
|
|
4005
|
+
return null
|
|
4006
|
+
}
|
|
4007
|
+
const trimmed = raw.trim()
|
|
4008
|
+
if (!trimmed) {
|
|
4009
|
+
return null
|
|
4010
|
+
}
|
|
4011
|
+
let candidate = trimmed
|
|
4012
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
4013
|
+
try {
|
|
4014
|
+
const parsed = new URL(trimmed)
|
|
4015
|
+
candidate = parsed.pathname
|
|
4016
|
+
} catch (_) {
|
|
4017
|
+
return null
|
|
4018
|
+
}
|
|
4019
|
+
}
|
|
4020
|
+
if (!candidate.startsWith('/asset/')) {
|
|
4021
|
+
return null
|
|
4022
|
+
}
|
|
4023
|
+
const pathPart = candidate.split('?')[0].split('#')[0]
|
|
4024
|
+
const rel = pathPart.replace(/^\/asset\/+/, '')
|
|
4025
|
+
if (!rel) {
|
|
4026
|
+
return null
|
|
4027
|
+
}
|
|
4028
|
+
const parts = rel.split('/').filter(Boolean)
|
|
4029
|
+
if (!parts.length || parts.some((part) => part === '..')) {
|
|
4030
|
+
return null
|
|
4031
|
+
}
|
|
4032
|
+
try {
|
|
4033
|
+
return this.kernel.path(...parts)
|
|
4034
|
+
} catch (_) {
|
|
4035
|
+
return null
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
const resolvedImage = resolveAssetPath(payload.image)
|
|
4039
|
+
if (resolvedImage) {
|
|
4040
|
+
payload.image = resolvedImage
|
|
4041
|
+
} else {
|
|
4042
|
+
const normalised = payload.image.trim()
|
|
4043
|
+
if (normalised.startsWith('/')) {
|
|
4044
|
+
const relative = normalised.replace(/^\/+/, '')
|
|
4045
|
+
if (relative) {
|
|
4046
|
+
const publicRoot = path.resolve(__dirname, 'public')
|
|
4047
|
+
const candidate = path.resolve(publicRoot, relative)
|
|
4048
|
+
if (candidate.startsWith(publicRoot)) {
|
|
4049
|
+
try {
|
|
4050
|
+
await fs.promises.access(candidate, fs.constants.R_OK)
|
|
4051
|
+
payload.image = candidate
|
|
4052
|
+
} catch (_) {
|
|
4053
|
+
// ignore missing fallback asset
|
|
4054
|
+
}
|
|
4055
|
+
}
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
}
|
|
4060
|
+
Util.push(payload)
|
|
4002
4061
|
res.json({ success: true })
|
|
4003
4062
|
} catch (e) {
|
|
4004
4063
|
res.json({ error: e.stack })
|
|
@@ -176,7 +176,7 @@
|
|
|
176
176
|
return value;
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
|
-
return
|
|
179
|
+
return '/pinokio-black.png';
|
|
180
180
|
};
|
|
181
181
|
|
|
182
182
|
const findLinkByFrameName = (frameName) => {
|
|
@@ -198,6 +198,57 @@
|
|
|
198
198
|
return null;
|
|
199
199
|
};
|
|
200
200
|
|
|
201
|
+
const normaliseImageSrc = (value) => {
|
|
202
|
+
if (typeof value !== 'string') {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
const trimmed = value.trim();
|
|
206
|
+
if (!trimmed) {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
const url = new URL(trimmed, window.location.origin);
|
|
211
|
+
if (url.origin === window.location.origin) {
|
|
212
|
+
if (url.pathname.startsWith('/asset/')) {
|
|
213
|
+
return url.pathname;
|
|
214
|
+
}
|
|
215
|
+
return url.href;
|
|
216
|
+
}
|
|
217
|
+
return url.href;
|
|
218
|
+
} catch (_) {
|
|
219
|
+
if (trimmed.startsWith('/')) {
|
|
220
|
+
return trimmed;
|
|
221
|
+
}
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const resolveTabImage = (link) => {
|
|
227
|
+
if (!link) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
const direct = link.querySelector('img.menu-item-image');
|
|
231
|
+
if (direct) {
|
|
232
|
+
const candidates = [direct.currentSrc, direct.src, direct.getAttribute('src')];
|
|
233
|
+
for (const candidate of candidates) {
|
|
234
|
+
const normalised = normaliseImageSrc(candidate);
|
|
235
|
+
if (normalised) {
|
|
236
|
+
return normalised;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const attrCandidates = ['data-iconpath', 'data-icon'];
|
|
241
|
+
for (const attr of attrCandidates) {
|
|
242
|
+
if (link.hasAttribute(attr)) {
|
|
243
|
+
const normalised = normaliseImageSrc(link.getAttribute(attr));
|
|
244
|
+
if (normalised) {
|
|
245
|
+
return normalised;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return null;
|
|
250
|
+
};
|
|
251
|
+
|
|
201
252
|
const findIndicatorForFrame = (frameName) => {
|
|
202
253
|
const link = findLinkByFrameName(frameName);
|
|
203
254
|
if (!link) {
|
|
@@ -427,15 +478,21 @@ const ensureTabAccessories = aggregateDebounce(() => {
|
|
|
427
478
|
}
|
|
428
479
|
const tab = link.querySelector('.tab');
|
|
429
480
|
const title = tab ? tab.textContent.trim() : 'Tab activity';
|
|
430
|
-
const subtitle = title || 'Pinokio';
|
|
431
|
-
const message = state.lastInput ? `Last input: ${state.lastInput}` : 'Tab is now idle.';
|
|
481
|
+
//const subtitle = title || 'Pinokio';
|
|
482
|
+
//const message = state.lastInput ? `Last input: ${state.lastInput}` : 'Tab is now idle.';
|
|
483
|
+
const message = state.lastInput ? `Response to: "${state.lastInput}` : "Tab is now idle."
|
|
484
|
+
const image = resolveTabImage(link);
|
|
432
485
|
|
|
433
486
|
const payload = {
|
|
434
487
|
title: 'Pinokio',
|
|
435
|
-
subtitle,
|
|
488
|
+
//subtitle,
|
|
436
489
|
message,
|
|
490
|
+
timeout: 60,
|
|
437
491
|
sound: true,
|
|
438
492
|
};
|
|
493
|
+
if (image) {
|
|
494
|
+
payload.image = image;
|
|
495
|
+
}
|
|
439
496
|
|
|
440
497
|
try {
|
|
441
498
|
log('Sending notification payload', payload);
|
package/server/views/app.ejs
CHANGED