bloby-bot 0.68.4 → 0.69.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bloby-bot",
3
- "version": "0.68.4",
3
+ "version": "0.69.0",
4
4
  "releaseNotes": [
5
5
  "1. Fix: agent self-update ",
6
6
  "1",
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
  // Injected by the supervisor into the workspace dashboard HTML (and ONLY there — never the
3
- // chat PWA or the supervisor's own interstitials). Five jobs, all aimed at non-technical users:
3
+ // chat PWA or the supervisor's own interstitials). Six jobs, all aimed at non-technical users:
4
4
  //
5
5
  // 1. Backend-down auto-detect — when the workspace backend crash-loops and gives up, the
6
6
  // supervisor serves a full "backend down" interstitial on the next navigation. Poll for
@@ -35,6 +35,10 @@
35
35
  // handler starts a recovery poll against a server-side change counter
36
36
  // (/__bloby/fe-stamp) and reloads ONLY when frontend files actually changed while the
37
37
  // socket was dead.
38
+ //
39
+ // 6. Pull-to-refresh — shell mode cost installed PWAs their native drag-down refresh (the
40
+ // gesture lands in this iframe; the shell is overflow:hidden). Recreated here, scoped
41
+ // tighter than the native one: it reloads only the workspace iframe, never the chat.
38
42
  if (window.__blobyWorkspaceGuard) return;
39
43
  window.__blobyWorkspaceGuard = true;
40
44
 
@@ -317,6 +321,107 @@
317
321
  });
318
322
  })();
319
323
 
324
+ /* ── 6. Pull-to-refresh ───────────────────────────────────────────────── */
325
+ // Native pull-to-refresh belongs to the top-level document; in shell mode that's the
326
+ // overflow:hidden shell, and touch gestures land in this iframe — so installed PWAs lost
327
+ // drag-down refresh entirely. Recreate it here: a mostly-vertical downward drag that
328
+ // starts with every scrollable ancestor at its top pulls out an indicator; releasing past
329
+ // the threshold reloads ONLY this workspace document (through the forensics wrapper →
330
+ // splash), so the chat in the shell never blinks. Framed + touch devices only — legacy
331
+ // mode (BLOBY_NO_SHELL=1) keeps the browser's native behavior.
332
+ (function () {
333
+ var framed = false;
334
+ try { framed = !!window.parent && window.parent !== window; } catch (e) {}
335
+ if (!framed || !('ontouchstart' in window)) return;
336
+
337
+ var THRESHOLD = 75; // px of damped pull that arms the refresh
338
+ var ENGAGE = 10; // px of raw downward travel before we claim the gesture
339
+ var startX = null, startY = null, pulling = false, dist = 0;
340
+ var indicator = null, arrow = null;
341
+
342
+ var spinStyle = document.createElement('style');
343
+ spinStyle.textContent = '@keyframes bloby-ptr-spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}';
344
+ (document.head || document.documentElement).appendChild(spinStyle);
345
+
346
+ function ensureIndicator() {
347
+ if (indicator) return;
348
+ indicator = document.createElement('div');
349
+ // z-index 99996: above the app and the splash (9998) so the spinner stays visible
350
+ // through the pre-reload splash, below the shell's chat chrome (which is a separate
351
+ // document anyway).
352
+ indicator.style.cssText = 'position:fixed;top:-48px;left:50%;width:38px;height:38px;margin-left:-19px;z-index:99996;border-radius:50%;background:#18181b;border:1px solid #27272a;box-shadow:0 2px 12px rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;pointer-events:none';
353
+ indicator.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#4AEEFF" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="transition:transform .15s ease"><path d="M12 5v14"/><path d="M5 12l7 7 7-7"/></svg>';
354
+ document.body.appendChild(indicator);
355
+ arrow = indicator.firstChild;
356
+ }
357
+
358
+ function setPull(d) {
359
+ ensureIndicator();
360
+ indicator.style.transition = 'none';
361
+ indicator.style.transform = 'translateY(' + Math.min(d + 48, 140) + 'px)';
362
+ if (arrow) arrow.style.transform = d >= THRESHOLD ? 'rotate(180deg)' : 'rotate(0deg)';
363
+ }
364
+
365
+ function settle(triggered) {
366
+ if (!indicator) return;
367
+ indicator.style.transition = 'transform .25s ease';
368
+ if (triggered) {
369
+ indicator.style.transform = 'translateY(72px)';
370
+ if (arrow) { arrow.style.transition = 'none'; arrow.style.animation = 'bloby-ptr-spin .7s linear infinite'; }
371
+ } else {
372
+ indicator.style.transform = 'translateY(0)'; // back above the viewport edge
373
+ }
374
+ }
375
+
376
+ // The pull only makes sense when the gesture couldn't have scrolled anything instead:
377
+ // every scrollable ancestor of the touch target (and the page itself) is at its top.
378
+ function ancestorsAtTop(el) {
379
+ var n = el;
380
+ while (n && n.nodeType === 1 && n !== document.documentElement) {
381
+ if (n.scrollTop > 0) return false;
382
+ n = n.parentElement;
383
+ }
384
+ var se = document.scrollingElement || document.documentElement;
385
+ return (se.scrollTop || 0) <= 0;
386
+ }
387
+
388
+ window.addEventListener('touchstart', function (e) {
389
+ startX = startY = null; pulling = false; dist = 0;
390
+ if (e.touches.length !== 1) return;
391
+ if (!ancestorsAtTop(e.target)) return;
392
+ startX = e.touches[0].clientX;
393
+ startY = e.touches[0].clientY;
394
+ }, { passive: true });
395
+
396
+ window.addEventListener('touchmove', function (e) {
397
+ if (startY === null || e.touches.length !== 1) return;
398
+ var dx = e.touches[0].clientX - startX;
399
+ var dy = e.touches[0].clientY - startY;
400
+ if (!pulling) {
401
+ // Claim only a clearly-vertical downward drag; anything else stays a normal gesture.
402
+ if (dy < ENGAGE || Math.abs(dy) < Math.abs(dx) * 1.5) {
403
+ if (dy < -ENGAGE || Math.abs(dx) > 24) startY = null; // it's a scroll/swipe — let go for good
404
+ return;
405
+ }
406
+ pulling = true;
407
+ }
408
+ e.preventDefault(); // claimed: keep the page from rubber-banding underneath the pull
409
+ dist = Math.max(0, (dy - ENGAGE) * 0.45); // damped, native-ish feel
410
+ setPull(dist);
411
+ }, { passive: false });
412
+
413
+ function release(cancelled) {
414
+ if (pulling) {
415
+ var go = !cancelled && dist >= THRESHOLD;
416
+ settle(go);
417
+ if (go) { flog('pull-to-refresh', 'triggered'); location.reload(); }
418
+ }
419
+ startX = startY = null; pulling = false; dist = 0;
420
+ }
421
+ window.addEventListener('touchend', function () { release(false); }, { passive: true });
422
+ window.addEventListener('touchcancel', function () { release(true); }, { passive: true });
423
+ })();
424
+
320
425
  /* ── 1. Backend-down auto-detect ──────────────────────────────────────── */
321
426
  var reloading = false;
322
427
  function pollBackend() {