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 CHANGED
@@ -546,7 +546,9 @@ function push(params) {
546
546
  if (!params.title) {
547
547
  params.title = "Pinokio"
548
548
  }
549
- console.log("Notifier.notify", params)
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "3.94.0",
3
+ "version": "3.95.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
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
- Util.push(req.body)
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 null;
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);
@@ -798,6 +798,9 @@ body.dark .grid-btns .btn2 {
798
798
  /*
799
799
  margin-top: 4px;
800
800
  */
801
+ overflow: hidden;
802
+ text-overflow: ellipsis;
803
+ white-space: nowrap;
801
804
  }
802
805
  .tab.has-preview .tab-updated {
803
806
  color: rgba(0, 0, 0, 0.45);