@gotcos/glasses-server 6.99.29 → 6.99.33
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/CHANGELOG.md +58 -0
- package/package.json +1 -1
- package/server/web-control/app.css +40 -9
- package/server/web-control/app.js +245 -20
- package/server/web-control/control.css +12 -6
- package/server/web-control/index.html +1 -0
- package/server/web-control/panes/cos-chat.css +10 -4
- package/server/web-control/panes/cos-chat.js +1 -1
- package/server/web-control/panes/meetings.css +5 -2
- package/server/web-control/panes/meetings.js +1 -1
- package/server/web-control/panes/memories.css +1 -1
- package/server/web-control/panes/memories.js +3 -8
- package/server/web-control/panes/questions.css +20 -4
- package/server/web-control/panes/questions.js +102 -21
- package/server/web-control/panes/sessions.css +18 -7
- package/server/web-control/panes/speakers.css +3 -5
- package/server/web-control/panes/speakers.js +7 -4
- package/server/web-control/panes/threads.js +3 -2
- package/server/web-control/panes/today-actions.css +38 -18
- package/server/web-control/panes/today-actions.js +176 -42
- package/server/web-control/panes/work.css +1 -1
- package/server/web-control/shell.js +3 -0
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
var O = {
|
|
76
76
|
open: null, // { kind: 'need' | 'run' | 'wait' | 'pet' | 'meeting' | 'intake', key, all }
|
|
77
77
|
opener: null, openerSel: '',
|
|
78
|
-
pushed: false, ignorePops: 0,
|
|
78
|
+
pushed: false, ignorePops: 0, pops: [], // pops: our own Back steps still to arrive (quietBack)
|
|
79
79
|
depth: 1, // history entries the overlay holds: 2 while a session opened from the pet's list shows
|
|
80
80
|
back: null, // { scroll } the pet's list that session came from, for Back to sessions
|
|
81
81
|
dirty: false, // something was decided or sent: Today reloads on close
|
|
@@ -144,6 +144,8 @@
|
|
|
144
144
|
layer.addEventListener('click', onClick);
|
|
145
145
|
layer.addEventListener('submit', onSubmit);
|
|
146
146
|
layer.addEventListener('focusin', onFocusIn);
|
|
147
|
+
// Focus leaving a field (the keyboard's Hide key, a tap outside) refits on the next frame, once it has moved.
|
|
148
|
+
layer.addEventListener('focusout', function () { fit(); });
|
|
147
149
|
layer.addEventListener('input', function (e) {
|
|
148
150
|
if (e.target && e.target.id === 'ta-input' && O.r) keepDraft(O.r, e.target.value, O.r.draft && O.r.draft.text === e.target.value.trim() ? O.r.draft : undefined);
|
|
149
151
|
});
|
|
@@ -169,46 +171,96 @@
|
|
|
169
171
|
layer.addEventListener('touchmove', function (e) { if (e.target && (e.target === layer || (e.target.classList && e.target.classList.contains('ta-scrim')))) e.preventDefault(); }, { passive: false });
|
|
170
172
|
return layer;
|
|
171
173
|
}
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
//
|
|
174
|
+
// 6.99.30 (Miles's Duo, in Comet as well as Safari): the layer is ALWAYS the visible part of the page, read the one
|
|
175
|
+
// way Control Web reads it (app.js COSWeb.viewportFit): top and height from visualViewport, 100dvh where there is
|
|
176
|
+
// none. Nothing assumes a browser: whatever covers the bottom (an on-screen keyboard, a hardware keyboard's strip,
|
|
177
|
+
// Comet's or any browser's bottom bar) is what the browser reports, and the reply box, Send and a question card's
|
|
178
|
+
// Submit stay above it. Only the backdrop and the sheet's own colour reach on down to the physical bottom
|
|
179
|
+
// (--ta-below plus the bleed, today-actions.css), so no band of Today shows under a bar that lets the page through.
|
|
180
|
+
// (6.99.29 made the closed-keyboard layer the whole layout viewport to cover IMG_0503's 106 px band; that band may be
|
|
181
|
+
// Comet's bottom bar, real covered space, and the composer must not sit under it.)
|
|
182
|
+
// The layout on top of that box:
|
|
183
|
+
// ta-kb an on-screen keyboard: what covers the bottom is KB_MIN_PX or more. The header folds to one
|
|
184
|
+
// compact row, the sheet fills the box, the reply box stays pinned at its bottom, and the
|
|
185
|
+
// conversation takes what is left. Under about three lines left, ta-kb-tight also hides the
|
|
186
|
+
// "Working right now" note until the keyboard closes (or the screen turns).
|
|
187
|
+
// ta-kb-strip a field in the sheet has focus and something under KB_MIN_PX covers the bottom (a hardware
|
|
188
|
+
// keyboard's 55 to 70 px strip): the header stays as it is; the strip covers the home indicator,
|
|
189
|
+
// so the reply box drops that inset.
|
|
190
|
+
// KB_MIN_PX applies whether or not a field has focus: a bottom bar under it (Comet's folded bar measured about 110 px)
|
|
191
|
+
// never folds the header, one of 120 px or more does. A strip counts from KB_STRIP_MIN_PX, so desktop zoom or a
|
|
192
|
+
// scrollbar (1 to 17 px of rounding, QA 6.99.30) never reads as a keyboard strip. Read on visualViewport resize and scroll while the overlay is open, and on window resize and
|
|
193
|
+
// orientationchange, pageshow, the page coming back into view, and focus moving in the sheet: at most once a frame.
|
|
182
194
|
var KB_MIN_PX = 120;
|
|
195
|
+
var KB_STRIP_MIN_PX = 24;
|
|
183
196
|
var BODY_MIN_PX = 72; // about three lines of the conversation (15 px text at 1.5)
|
|
184
|
-
var KB = { on: false, tight: false, frame: 0, listening: false };
|
|
185
|
-
function keyboardInset(vv) {
|
|
186
|
-
var layoutH = window.innerHeight || (document.documentElement && document.documentElement.clientHeight) || 0;
|
|
187
|
-
var inset = Math.round(layoutH - vv.height * (vv.scale || 1));
|
|
188
|
-
return inset > 0 ? inset : 0;
|
|
189
|
-
}
|
|
197
|
+
var KB = { on: false, strip: false, tight: false, frame: 0, listening: false, width: 0, inset: 0 };
|
|
190
198
|
function tall(el) { return (el && el.offsetHeight) || 0; }
|
|
191
199
|
// What the conversation gets once the compact header and the reply box have theirs.
|
|
192
|
-
function bodyRoom(
|
|
200
|
+
function bodyRoom(height) {
|
|
193
201
|
var sheet = $('ta-sheet'), grip = sheet && sheet.querySelector ? sheet.querySelector('.ta-grip') : null;
|
|
194
|
-
return
|
|
202
|
+
return height - tall($('ta-head')) - tall($('ta-foot')) - tall(grip);
|
|
203
|
+
}
|
|
204
|
+
// A field that types (the reply box, a question card's Other) has focus inside the sheet.
|
|
205
|
+
function typing() {
|
|
206
|
+
var a = document.activeElement;
|
|
207
|
+
if (!a || !layer || a === layer || !layer.contains || !layer.contains(a)) return false;
|
|
208
|
+
var tag = String(a.tagName || '').toUpperCase();
|
|
209
|
+
if (tag === 'TEXTAREA' || tag === 'SELECT') return true;
|
|
210
|
+
if (tag === 'INPUT') return !/^(checkbox|radio|button|submit|reset|range|color|file|image|hidden)$/i.test(String(a.type || 'text'));
|
|
211
|
+
return !!a.isContentEditable;
|
|
212
|
+
}
|
|
213
|
+
function viewport() { return W.viewportFit ? W.viewportFit() : null; }
|
|
214
|
+
function crumb(what, v, focus) {
|
|
215
|
+
if (!W.layoutCrumb || !O.open || !v) return;
|
|
216
|
+
W.layoutCrumb(what, { kind: O.open.kind, kb: KB.on, strip: KB.strip, tight: KB.tight, focus: !!focus, inset: v.inset, below: v.below,
|
|
217
|
+
lh: v.layoutH, top: v.top, h: v.height, zoomed: v.zoomed, wide: !phone() });
|
|
195
218
|
}
|
|
196
219
|
function applyFit() {
|
|
197
220
|
KB.frame = 0;
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
layer.style.setProperty('--ta-
|
|
202
|
-
|
|
203
|
-
layer.style.setProperty('--ta-
|
|
221
|
+
if (!layer || !O.open) return null;
|
|
222
|
+
var v = viewport();
|
|
223
|
+
if (!v) return null;
|
|
224
|
+
layer.style.setProperty('--ta-vh', v.height + 'px');
|
|
225
|
+
layer.style.setProperty('--ta-vt', v.top + 'px');
|
|
226
|
+
layer.style.setProperty('--ta-below', v.below + 'px');
|
|
227
|
+
var focus = typing(), on = v.inset >= KB_MIN_PX, strip = !on && focus && v.inset >= KB_STRIP_MIN_PX;
|
|
228
|
+
// A turned screen measures the room again: what was tight in landscape is not in portrait (QA 6.99.29).
|
|
229
|
+
var w = window.innerWidth || 0, turned = !!(KB.width && w && w !== KB.width);
|
|
230
|
+
if (w) KB.width = w;
|
|
231
|
+
var was = KB.on + '|' + KB.strip + '|' + KB.tight;
|
|
232
|
+
if (turned) KB.tight = false;
|
|
233
|
+
// A rise is what covers the bottom growing, by any amount, while the keyboard layout is on: not a class flip (a
|
|
234
|
+
// 120 px bar can already hold ta-kb before the keyboard comes, QA 6.99.32 Skeptic N-3), and not a jump past a step
|
|
235
|
+
// (a keyboard animating up in 20 px frames never crossed one, Codex's root regression on 98f3cde). The reveal is
|
|
236
|
+
// scrollIntoView 'nearest', so a frame where the field is already in view moves nothing. Shrinking never reveals.
|
|
237
|
+
var rose = (on || strip) && v.inset > (KB.inset || 0);
|
|
238
|
+
KB.inset = v.inset;
|
|
204
239
|
if (on !== KB.on) { KB.on = on; KB.tight = false; layer.classList.toggle('ta-kb', on); }
|
|
240
|
+
if (strip !== KB.strip) { KB.strip = strip; layer.classList.toggle('ta-kb-strip', strip); }
|
|
241
|
+
// 6.99.32 (QA 6.99.30 canary W2): the keyboard rises after the focus, so onFocusIn's scroll (250 ms) can land before
|
|
242
|
+
// the layer shrinks and leave a question card's Other field under the keyboard. Once the layout has the keyboard,
|
|
243
|
+
// bring the focused field into view again.
|
|
244
|
+
if (rose && focus) revealFocused();
|
|
205
245
|
// Measured with the compact header in place. Once tight it stays so while the keyboard is up: showing the note
|
|
206
246
|
// again would take the room back and flicker.
|
|
207
|
-
if (on && !KB.tight) KB.tight = bodyRoom(
|
|
247
|
+
if (on && !KB.tight) KB.tight = bodyRoom(v.height) < BODY_MIN_PX;
|
|
208
248
|
layer.classList.toggle('ta-kb-tight', on && KB.tight);
|
|
249
|
+
if (was !== KB.on + '|' + KB.strip + '|' + KB.tight) crumb('kb', v, focus);
|
|
250
|
+
return v;
|
|
251
|
+
}
|
|
252
|
+
function revealFocused() {
|
|
253
|
+
// What has focus when the frame runs, not when it was asked for (QA 6.99.32 Skeptic N-2).
|
|
254
|
+
var go = function () {
|
|
255
|
+
if (!O.open || !typing()) return;
|
|
256
|
+
var a = document.activeElement, f = a && a.id === 'ta-input' ? $('ta-form') : a;
|
|
257
|
+
if (f && f.scrollIntoView) f.scrollIntoView({ block: 'nearest' });
|
|
258
|
+
};
|
|
259
|
+
// The next frame, so the scroll measures the layout with ta-kb (or ta-kb-strip) applied.
|
|
260
|
+
if (window.requestAnimationFrame) window.requestAnimationFrame(go); else go();
|
|
209
261
|
}
|
|
210
262
|
function fit() {
|
|
211
|
-
if (KB.frame) return;
|
|
263
|
+
if (KB.frame || !O.open) return;
|
|
212
264
|
if (!window.requestAnimationFrame) return applyFit(); // every browser with visualViewport has frames
|
|
213
265
|
KB.frame = window.requestAnimationFrame(applyFit);
|
|
214
266
|
}
|
|
@@ -222,11 +274,34 @@
|
|
|
222
274
|
function unfit() {
|
|
223
275
|
listenViewport(false);
|
|
224
276
|
if (KB.frame && window.cancelAnimationFrame) window.cancelAnimationFrame(KB.frame);
|
|
225
|
-
KB.frame = 0; KB.on = false; KB.tight = false;
|
|
277
|
+
KB.frame = 0; KB.on = false; KB.strip = false; KB.tight = false; KB.width = 0; KB.inset = 0;
|
|
226
278
|
if (!layer) return;
|
|
227
|
-
layer.classList.remove('ta-kb'); layer.classList.remove('ta-kb-tight');
|
|
228
|
-
|
|
229
|
-
|
|
279
|
+
layer.classList.remove('ta-kb'); layer.classList.remove('ta-kb-strip'); layer.classList.remove('ta-kb-tight');
|
|
280
|
+
}
|
|
281
|
+
// The rest of the moments the visible viewport can change without a visualViewport event of its own (QA 6.99.29: the
|
|
282
|
+
// layer stayed sized for a keyboard that had gone). Each is a no-op while the overlay is closed.
|
|
283
|
+
window.addEventListener('resize', function () { fit(); });
|
|
284
|
+
window.addEventListener('orientationchange', function () { if (O.open) { KB.tight = false; fit(); } });
|
|
285
|
+
document.addEventListener('visibilitychange', function () {
|
|
286
|
+
if (document.hidden || !O.open) return;
|
|
287
|
+
fit();
|
|
288
|
+
if (O.r && O.open.kind === 'run') paintLive(); // the clock and the state, at once rather than on the next tick
|
|
289
|
+
});
|
|
290
|
+
// Back from the back-forward cache (a swipe back, the app switcher): the live stream is closed and everything shown
|
|
291
|
+
// is as old as the visit away. Open the stream again and read the session again.
|
|
292
|
+
window.addEventListener('pageshow', function (e) {
|
|
293
|
+
if (!O.open) return;
|
|
294
|
+
fit();
|
|
295
|
+
if (e && e.persisted) revive();
|
|
296
|
+
});
|
|
297
|
+
// 6.99.30 (QA): the Mac going away or coming back shows in an open sheet at once, not after the stream gives up: the
|
|
298
|
+
// live line says the Mac is not answering, and back up the stream opens again and the session is read again.
|
|
299
|
+
if (W.onServer) W.onServer(function (up) {
|
|
300
|
+
var r = O.r;
|
|
301
|
+
if (!O.open || !r) return;
|
|
302
|
+
if (!up) { r.feed.connected = false; r.feed.fallback = 'unreachable'; if (r.live) r.live.working = false; paintLive(); paintVerdict(); }
|
|
303
|
+
else revive();
|
|
304
|
+
});
|
|
230
305
|
|
|
231
306
|
function openerSelector(t) {
|
|
232
307
|
if (!t || !t.getAttribute) return '';
|
|
@@ -275,6 +350,8 @@
|
|
|
275
350
|
var sheet = $('ta-sheet');
|
|
276
351
|
O.settleAt = Date.now() + settleMs(sheet);
|
|
277
352
|
if (sheet && sheet.focus) sheet.focus({ preventScroll: true });
|
|
353
|
+
// Measured once the sheet is drawn, and sent home as a breadcrumb (app.js layoutCrumb, 6.99.30).
|
|
354
|
+
crumb('open', applyFit(), typing());
|
|
278
355
|
}
|
|
279
356
|
// How long taps inside are ignored after opening: the longest open motion of the sheet and its backdrop (their computed
|
|
280
357
|
// animation and transition, delay included: today-actions.css ta-rise or ta-slide, 220 ms, and fade, 160 ms), plus
|
|
@@ -314,8 +391,8 @@
|
|
|
314
391
|
// X from a session opened off the pet's list unwinds both entries in one step, so the next Back leaves Today as before.
|
|
315
392
|
if (O.pushed && how !== 'pop' && how !== 'leave') {
|
|
316
393
|
var n = O.depth > 1 ? O.depth : 1;
|
|
317
|
-
O.pushed = false;
|
|
318
|
-
|
|
394
|
+
O.pushed = false;
|
|
395
|
+
quietBack(n);
|
|
319
396
|
}
|
|
320
397
|
if (how === 'pop' || how === 'leave') O.pushed = false;
|
|
321
398
|
clearTimeout(O.expireTimer);
|
|
@@ -333,11 +410,29 @@
|
|
|
333
410
|
if (el && el.focus) { try { el.focus({ preventScroll: true }); } catch (_e) { el.focus(); } }
|
|
334
411
|
}
|
|
335
412
|
// Leaving for a tab from inside the overlay replaces the overlay's history entry, so Back from there returns to Today.
|
|
413
|
+
// A session opened off the pet's list holds two entries: the list's comes off first (6.99.30, QA: it stayed under
|
|
414
|
+
// the new tab, and Back from there landed on it and showed nothing), then the tab replaces the overlay's own.
|
|
336
415
|
function leaveTo(pane, args) {
|
|
337
416
|
var hash = '#/' + [pane].concat(args || []).map(encodeURIComponent).join('/');
|
|
338
|
-
var replace = O.pushed;
|
|
417
|
+
var replace = O.pushed, extra = O.pushed && O.depth > 1 ? O.depth - 1 : 0;
|
|
339
418
|
close('leave');
|
|
340
|
-
|
|
419
|
+
O.depth = 1;
|
|
420
|
+
if (!replace) { W.go(pane, args || []); return; }
|
|
421
|
+
if (extra) { quietBack(extra, function () { location.replace(hash); }); return; }
|
|
422
|
+
location.replace(hash);
|
|
423
|
+
}
|
|
424
|
+
// A Back step the overlay takes itself (X, Back to sessions, leaving for a tab): its popstate is ours, not the phone's
|
|
425
|
+
// Back, and `then` runs once it lands. If it never arrives (history.go with nothing to go back to, or a browser that
|
|
426
|
+
// drops it) the wait lapses after POP_WAIT_MS and `then` runs anyway, so a real Back later is never swallowed
|
|
427
|
+
// (6.99.30, QA: ignorePops stayed counted and ate the next Back).
|
|
428
|
+
var POP_WAIT_MS = 700;
|
|
429
|
+
function quietBack(n, then) {
|
|
430
|
+
var w = { then: then || null, timer: 0 };
|
|
431
|
+
var drop = function () { var i = O.pops.indexOf(w); if (i < 0) return false; O.pops.splice(i, 1); O.ignorePops = O.pops.length; return true; };
|
|
432
|
+
w.timer = setTimeout(function () { if (drop() && w.then) w.then(); }, POP_WAIT_MS);
|
|
433
|
+
O.pops.push(w); O.ignorePops = O.pops.length;
|
|
434
|
+
try { if (n > 1 && typeof history.go === 'function') history.go(-n); else history.back(); }
|
|
435
|
+
catch (_e) { clearTimeout(w.timer); if (drop() && w.then) w.then(); }
|
|
341
436
|
}
|
|
342
437
|
// A reload with the overlay open keeps its history entry, and the entry before it is Today at the same address, so
|
|
343
438
|
// Back from there showed nothing (measured in Chrome 11/07: a same-document popstate). On load, app.js hands that
|
|
@@ -363,7 +458,7 @@
|
|
|
363
458
|
});
|
|
364
459
|
}
|
|
365
460
|
window.addEventListener('popstate', function () {
|
|
366
|
-
if (O.
|
|
461
|
+
if (O.pops.length) { var w = O.pops.shift(); clearTimeout(w.timer); O.ignorePops = O.pops.length; if (w.then) w.then(); return; }
|
|
367
462
|
// The phone's Back from a session opened off the pet's list goes back to that list, as Back to sessions does.
|
|
368
463
|
if (O.open && O.back) { backToList(); return; }
|
|
369
464
|
if (O.open) close('pop');
|
|
@@ -372,7 +467,7 @@
|
|
|
372
467
|
function backToList(fromButton) {
|
|
373
468
|
var b = O.back;
|
|
374
469
|
if (!b) return;
|
|
375
|
-
if (fromButton && O.depth > 1)
|
|
470
|
+
if (fromButton && O.depth > 1) quietBack(1);
|
|
376
471
|
O.depth = 1;
|
|
377
472
|
open('pet', '', O.opener, { again: true });
|
|
378
473
|
var body = $('ta-body'); if (body) body.scrollTop = b.scroll || 0;
|
|
@@ -454,11 +549,18 @@
|
|
|
454
549
|
function onSubmit(e) {
|
|
455
550
|
if (e.target && e.target.id === 'ta-form') { e.preventDefault(); sendReply(); }
|
|
456
551
|
}
|
|
457
|
-
// The keyboard rises over the bottom of the sheet: keep the box in view as it does.
|
|
552
|
+
// The keyboard rises over the bottom of the sheet: keep the box in view as it does. Any field that types counts
|
|
553
|
+
// (the reply box, a question card's Other): a hardware keyboard's strip is known only by focus (6.99.30).
|
|
458
554
|
function onFocusIn(e) {
|
|
459
|
-
if (!e.target ||
|
|
555
|
+
if (!e.target || !typing()) return;
|
|
460
556
|
fit();
|
|
461
|
-
|
|
557
|
+
var reply = e.target.id === 'ta-input';
|
|
558
|
+
setTimeout(function () {
|
|
559
|
+
if (!O.open) return;
|
|
560
|
+
applyFit();
|
|
561
|
+
var f = reply ? $('ta-form') : e.target;
|
|
562
|
+
if (f && f.scrollIntoView) f.scrollIntoView({ block: 'nearest' });
|
|
563
|
+
}, 250);
|
|
462
564
|
}
|
|
463
565
|
|
|
464
566
|
// ═════════════ From today's meetings ═════════════
|
|
@@ -706,7 +808,7 @@
|
|
|
706
808
|
setHead(kind === 'need' ? (O.item ? str(O.item.sessionTitle) || str(O.item.title) || 'Handoff' : 'Needs you')
|
|
707
809
|
: kind === 'wait' ? (O.wait ? O.wait.title : 'A session is waiting for you') : (O.row ? K.title(O.row) : 'Session'));
|
|
708
810
|
var body = $('ta-body');
|
|
709
|
-
if (body) body.innerHTML = '<div id="ta-meta"></div><div id="ta-q" class="ta-q" hidden></div><div id="ta-live"></div><div id="ta-last"></div>';
|
|
811
|
+
if (body) body.innerHTML = '<div id="ta-meta"></div><div id="ta-notsent" class="ta-notsent" role="status"></div><div id="ta-q" class="ta-q" hidden></div><div id="ta-live"></div><div id="ta-last"></div>';
|
|
710
812
|
var foot = $('ta-foot');
|
|
711
813
|
if (foot) { foot.innerHTML = O.r ? composerHtml(kind) : ''; foot.hidden = false; }
|
|
712
814
|
var input = $('ta-input');
|
|
@@ -733,8 +835,37 @@
|
|
|
733
835
|
if (kind === 'run') { paintLive(); watchStream(O.r); }
|
|
734
836
|
loadLast(O.r);
|
|
735
837
|
K.probe(O.r);
|
|
838
|
+
loadNotSent(O.r);
|
|
736
839
|
if (kind === 'need') recheckNeed(key);
|
|
737
840
|
}
|
|
841
|
+
// 6.99.30: a queued message for this session that the server refused or let expire, said here as in the reader
|
|
842
|
+
// ("Not sent: <reason>"), from the same queue read (app.js sessionKit.notSent). The Sessions list chip follows it.
|
|
843
|
+
function loadNotSent(r) {
|
|
844
|
+
if (!r || !K.notSent || !K.answer) return;
|
|
845
|
+
K.answer(K.path(r.provider, r.id, '/queued-turns')).then(function (q) {
|
|
846
|
+
if (O.r !== r) return;
|
|
847
|
+
var rows = (q && q.status === 200 && q.body && q.body.turns) || [];
|
|
848
|
+
if (q && q.status === 200) K.notSent.note(r.provider, r.id, rows);
|
|
849
|
+
var box = $('ta-notsent'); if (!box) return;
|
|
850
|
+
box.innerHTML = K.notSent.rows(rows).map(function (t) {
|
|
851
|
+
return '<p class="ta-note ta-notsent-line"><strong>' + esc(clip(t.preview || t.prompt || 'A queued message', 80)) + '</strong> ' + esc(K.notSent.line(t)) + '</p>';
|
|
852
|
+
}).join('');
|
|
853
|
+
}, function () {});
|
|
854
|
+
}
|
|
855
|
+
// Back from the back-forward cache with a session open (pageshow persisted): the stream again, the last reply, the
|
|
856
|
+
// write gate and the questions, as on opening.
|
|
857
|
+
function revive() {
|
|
858
|
+
var r = O.r;
|
|
859
|
+
if (!r || !O.open) return;
|
|
860
|
+
if (O.open.kind === 'run') {
|
|
861
|
+
if (O.stream) { try { O.stream.close(); } catch (_e) {} O.stream = null; }
|
|
862
|
+
watchStream(r); paintLive();
|
|
863
|
+
}
|
|
864
|
+
loadLast(r);
|
|
865
|
+
K.probe(r);
|
|
866
|
+
loadNotSent(r);
|
|
867
|
+
if (W.sessionQuestions && W.sessionQuestions.nudge) W.sessionQuestions.nudge();
|
|
868
|
+
}
|
|
738
869
|
function stopSession() {
|
|
739
870
|
if (K.imagesStop) K.imagesStop($('ta-last'));
|
|
740
871
|
if (O.stream) { try { O.stream.close(); } catch (_e) {} O.stream = null; }
|
|
@@ -1074,6 +1205,7 @@
|
|
|
1074
1205
|
if (w === 'Working' || w === 'Waiting') {
|
|
1075
1206
|
O.clock = setInterval(function () {
|
|
1076
1207
|
if (O.r !== r) { clearInterval(O.clock); return; }
|
|
1208
|
+
if (document.hidden) return; // a hidden page spends nothing; coming back repaints at once (visibilitychange)
|
|
1077
1209
|
var c = $('ta-clock'); if (c) c.textContent = K.feed.elapsed(r.feed, r);
|
|
1078
1210
|
if (K.feed.gateStale(r)) K.probe(r);
|
|
1079
1211
|
var rs = $('ta-run-state'); if (rs) rs.textContent = runWords();
|
|
@@ -1219,6 +1351,8 @@
|
|
|
1219
1351
|
clearInterval(O.watch);
|
|
1220
1352
|
var started = Date.now();
|
|
1221
1353
|
O.watch = setInterval(function () {
|
|
1354
|
+
// Hidden: no reads, and the three minutes count only while the page is in view (6.99.30).
|
|
1355
|
+
if (document.hidden) { started += 3000; return; }
|
|
1222
1356
|
if (O.r !== r || Date.now() - started > WATCH_MS) { clearInterval(O.watch); return; }
|
|
1223
1357
|
K.answer('/api/agent-sessions/bindings/' + encodeURIComponent(bindingId) + '/turns/' + encodeURIComponent(key)).then(function (s) {
|
|
1224
1358
|
if (O.r !== r) return;
|
|
@@ -1298,6 +1432,6 @@
|
|
|
1298
1432
|
open: open, close: close, restore: restore, summary: summary, summaryMax: SUMMARY_MAX,
|
|
1299
1433
|
isOpen: function () { return !!O.open; },
|
|
1300
1434
|
_state: O, _decide: decide, _undo: undo, _intakeHtml: intakeHtml, _send: sendReply, _view: VIEW, _paintVerdict: paintVerdict,
|
|
1301
|
-
_paintLive: paintLive, _paintMeta: paintMeta, _onClick: onClick, _expire: expireLater, _kb: KB, _fitNow: applyFit
|
|
1435
|
+
_paintLive: paintLive, _paintMeta: paintMeta, _onClick: onClick, _expire: expireLater, _kb: KB, _fitNow: applyFit, _revive: revive
|
|
1302
1436
|
});
|
|
1303
1437
|
})();
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
.wk-filters { width: 100%; justify-content: flex-start; }
|
|
60
60
|
.wk-filters .cx-search { max-width: none; flex-basis: 100%; }
|
|
61
61
|
.wk-pick { flex: 1 1 140px; flex-direction: column; align-items: stretch; gap: 4px; font-size: 11.5px; }
|
|
62
|
-
.wk-pick select.field {
|
|
62
|
+
.wk-pick select.field { font-size: 15px; width: 100%; background-position: calc(100% - 16px) 17px, calc(100% - 11px) 17px; }
|
|
63
63
|
.wk-text-btn { min-height: 36px; font-size: 14px; }
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -286,6 +286,9 @@
|
|
|
286
286
|
});
|
|
287
287
|
|
|
288
288
|
W.onRoute(paintNavbar);
|
|
289
|
+
// 6.99.30: the server line moves the moment a request fails to reach the Mac or reaches it again (app.js noteServer),
|
|
290
|
+
// not on the five-minute check. Back up, the health read confirms it and refreshes what rides on it.
|
|
291
|
+
if (W.onServer) W.onServer(function (up) { setServer(up); if (up) checkServer(); });
|
|
289
292
|
W.onShell(function () {
|
|
290
293
|
Object.keys(FALLBACK).forEach(function (k) { setStory(k, FALLBACK[k], false); });
|
|
291
294
|
setTimeout(function () { loadStories(); checkServer(); }, 2500);
|