@gotcos/glasses-server 7.0.18 → 7.0.29

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.
@@ -19,5 +19,205 @@
19
19
  apply: function () { return apply(read()); }
20
20
  };
21
21
  apply(read());
22
+ // 7.0.19: the first paint (index.html #first-paint) shows only when this script ran, so with JavaScript off the page
23
+ // never shows a mark that nothing would take away. app.js removes the class when the sign-in card or the app shows.
24
+ document.documentElement.classList.add('fp-wait');
22
25
  if (media && media.addEventListener) media.addEventListener('change', function () { if (read() === 'system') apply('system'); });
23
26
  })();
27
+
28
+ /* 7.0.29 (Miles 21:42): "for shorter screens like the duo we should use the side rail when it's folded"; tall phones keep
29
+ the top menu. The test is the SCREEN's shape, never the window's: a screen whose short side is under 600 and whose long
30
+ side is under 1.6 times its short side is short (html.short-screen). app.css draws the side rail only with this class
31
+ AND a page under 600 px wide on a touch screen. Missing or odd numbers give no class: no rail, the layout as before.
32
+ MEASURED: his phone's own device reports (2026-11-10 and 11-11) give screen.width 466 and screen.height 678 in rows laid
33
+ out 449, 548 and 1119 px wide at visualViewport.scale 0.85, and the canary (11-11 21:56) read screen.width 466 in a
34
+ Safari tab, folded, at scale 1. 678 / 466 is 1.46. Every iPhone's published screen is 1.78 (SE, 8 Plus) or 2.16 and up.
35
+ ASSUMED, not measured: which of those rows were the Home Screen app and which a tab, that other zoom settings report
36
+ the same, and that folding or opening never changes the numbers. So this one function runs at load (before the first
37
+ paint, so the rail never moves the page on load) and again on every resize and orientation change, and the page's
38
+ device report and a 'rail' report on each change carry screen.width and screen.height, so his phone settles it. */
39
+ (function () {
40
+ function shortScreen() {
41
+ var de = document.documentElement, on = false;
42
+ try {
43
+ var s = window.screen, a = s ? Number(s.width) : 0, b = s ? Number(s.height) : 0;
44
+ if (a > 0 && b > 0 && isFinite(a) && isFinite(b)) { var lo = Math.min(a, b), hi = Math.max(a, b); on = lo < 600 && hi / lo < 1.6; }
45
+ } catch (_e) { on = false; }
46
+ try { if (de && de.classList) { if (on) de.classList.add('short-screen'); else de.classList.remove('short-screen'); } } catch (_e) { /* no class: no rail */ }
47
+ return on;
48
+ }
49
+ window.CosShortScreen = shortScreen;
50
+ shortScreen();
51
+ if (typeof window.addEventListener === 'function') { window.addEventListener('resize', shortScreen); window.addEventListener('orientationchange', shortScreen); }
52
+ })();
53
+
54
+ /* The app's scripts (index.html #boot-scripts), loaded one frame after the page is drawn so the first paint never waits
55
+ for them. Same files, same order (async=false keeps insertion order), and app.js's boot runs only once every one of
56
+ them has run, as when they were in the page: window.CosBoot.after(boot). The frame wait has a 50 ms fallback, because
57
+ a page opened in the background gets no animation frames. A script that fails to load counts as settled, as a failed
58
+ <script> tag did; nothing waits for it forever. */
59
+ (function () {
60
+ var waiting = [], settled = false;
61
+ function settle() { settled = true; var fns = waiting; waiting = []; fns.forEach(function (fn) { try { fn(); } catch (_e) { setTimeout(function () { throw _e; }, 0); } }); }
62
+ window.CosBoot = { after: function (fn) { if (typeof fn !== 'function') return; if (settled) fn(); else waiting.push(fn); } };
63
+ function load() {
64
+ var list = [];
65
+ try { list = JSON.parse((document.getElementById('boot-scripts') || {}).textContent || '[]'); } catch (_e) { list = []; }
66
+ list = Array.isArray(list) ? list.filter(function (u) { return typeof u === 'string' && u.indexOf('/control/') === 0; }) : [];
67
+ var left = list.length;
68
+ if (!left) { settle(); return; }
69
+ // 7.0.28: after a reload that the restyle check below made because a script had failed, ask for every script with a
70
+ // fresh query, so none comes from the browser's memory of the failed request (WebKit kept the failure across the
71
+ // reload). Read once.
72
+ var bust = '';
73
+ try { bust = sessionStorage.getItem('cos_web_bust') || ''; sessionStorage.removeItem('cos_web_bust'); } catch (_e) { bust = ''; }
74
+ list.forEach(function (src) {
75
+ var el = document.createElement('script');
76
+ el.src = /^\d{1,15}$/.test(bust) ? src + (src.indexOf('?') >= 0 ? '&' : '?') + 'r=' + bust : src; el.async = false;
77
+ el.onload = function () { if (--left === 0) settle(); };
78
+ el.onerror = function () { if (window.CosRestyle) window.CosRestyle.scriptFailed(src); if (--left === 0) settle(); };
79
+ document.body.appendChild(el);
80
+ });
81
+ }
82
+ function afterFrame() {
83
+ var done = false, go = function () { if (done) return; done = true; setTimeout(load, 0); };
84
+ if (window.requestAnimationFrame) window.requestAnimationFrame(go);
85
+ setTimeout(go, 50);
86
+ }
87
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', afterFrame); else afterFrame();
88
+ })();
89
+
90
+ /* 7.0.28 (Miles 19:28, "Connect this browser" with no styling): the page's own files must not stay missing. When the
91
+ phone opens the page before it can reach the Mac (the tunnel still coming up, or the page drawn from the browser's
92
+ copy), the HTML draws but a stylesheet or a script can fail, and nothing asked for it again. index.html's inline
93
+ critical CSS keeps the sign-in card right meanwhile; this brings the rest back. While anything failed, it asks the
94
+ Mac for the failed file after 1, 2, 4 and 8 s, then every 30 s, only while the page is in front. When the Mac
95
+ answers: a failed script reloads the page (app.js cannot be re-run in place); failed stylesheets are asked for again
96
+ in place. A file that keeps failing never loops: at most 2 reloads in 5 minutes. What happened is kept for app.js's numbers-only 'restyle' row (window.CosRestyle.last, and sessionStorage
97
+ across the reload).
98
+ 7.0.29 (Q1 FIX 1 and 2, the lead's R1 and R2): the page never reloads by itself over his words. While any text box
99
+ holds words, focus is in a text box, or a send is in flight (app.js's window.CosBusy; when app.js is the script that
100
+ failed, the boxes alone), it shows one line with a Reload button instead, and only his tap reloads. The cap fails
101
+ closed: if the reload count cannot be read, or does not read back after writing (storage blocked), it gives up and
102
+ shows the same line rather than reloading. */
103
+ (function () {
104
+ var STEPS = [1000, 2000, 4000, 8000], SETTLED_MS = 30000, KEY = 'cos_web_restyle', RELOADS_KEY = 'cos_web_restyle_reloads';
105
+ var RELOAD_MAX = 2, RELOAD_WINDOW_MS = 5 * 60000;
106
+ var R = { scripts: [], sheets: 0, tries: 0, t0: 0, timer: null, done: false, last: null };
107
+ // Every real browser has these; a harness that runs this file without them gets no restyle check, not a throw that
108
+ // would stop the theme and the loader above from being tested.
109
+ if (typeof window.addEventListener !== 'function' || typeof document.querySelectorAll !== 'function' || typeof document.addEventListener !== 'function') return;
110
+ function missing() {
111
+ return [].filter.call(document.querySelectorAll('link[rel="stylesheet"]'), function (l) {
112
+ // A sheet that failed to load throws on read in WebKit (SecurityError); count it as missing.
113
+ try { return !l.sheet || !l.sheet.cssRules || l.sheet.cssRules.length === 0; } catch (_e) { return true; }
114
+ });
115
+ }
116
+ function bust(href) { return href + (href.indexOf('?') >= 0 ? '&' : '?') + 'r=' + Date.now(); }
117
+ function record(how) {
118
+ R.last = { how: how, sheets: R.sheets, scripts: R.scripts.length, tries: R.tries, ms: R.t0 ? Date.now() - R.t0 : 0 };
119
+ try { window.dispatchEvent(new CustomEvent('cos-restyle', { detail: R.last })); } catch (_e) {}
120
+ }
121
+ // What would be lost by a reload: words in a text box he can see, focus in one, or a send on its way (app.js).
122
+ function busy() {
123
+ var out = { words: false, focus: false, sending: false };
124
+ try {
125
+ [].forEach.call(document.querySelectorAll('textarea, input, [contenteditable]'), function (el) {
126
+ if (el.getAttribute && el.getAttribute('aria-hidden') === 'true') return; // app.js's hidden measuring copy
127
+ if (el.tagName === 'INPUT' && !/^(|text|search|email|url|tel|password|number)$/i.test(el.getAttribute('type') || '')) return;
128
+ var v = el.isContentEditable ? el.textContent : el.value;
129
+ if (v && String(v).trim()) out.words = true;
130
+ });
131
+ var a = document.activeElement;
132
+ out.focus = !!(a && (a.tagName === 'TEXTAREA' || (a.tagName === 'INPUT' && /^(|text|search|email|url|tel|password|number)$/i.test(a.getAttribute('type') || '')) || a.isContentEditable));
133
+ } catch (_e) {}
134
+ try { out.sending = typeof window.CosBusy === 'function' && !!window.CosBusy(); } catch (_e) {}
135
+ out.any = out.words || out.focus || out.sending;
136
+ return out;
137
+ }
138
+ // The one line, in place of a reload. Inline styles with the theme's own colours where they exist, so it reads right
139
+ // on a page whose stylesheet failed too. At the top: a fixed bar near the bottom fights the phone browser's own bar.
140
+ // On the folded Duo it stops at the side rail (--rail-w is set only while that rail shows).
141
+ function offer(b) {
142
+ try { offerLine(b); } catch (_e) { /* no line, and still no reload by itself */ }
143
+ }
144
+ function offerLine(b) {
145
+ var line = document.getElementById('cos-reload-line');
146
+ if (!line) {
147
+ line = document.createElement('div');
148
+ line.id = 'cos-reload-line'; line.setAttribute('role', 'status');
149
+ line.style.cssText = 'position:fixed;left:12px;right:calc(12px + var(--rail-w, 0px));top:calc(env(safe-area-inset-top, 0px) + 8px);z-index:90;display:flex;align-items:center;gap:12px;' +
150
+ 'padding:10px 10px 10px 14px;border-radius:12px;border:1px solid var(--panel-border, #a08a66);background:var(--panel, #ffffff);color:var(--ink, #1f1712);' +
151
+ 'font:400 15px/1.4 -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif;max-width:560px;margin:0 auto;';
152
+ var p = document.createElement('p'); p.style.cssText = 'margin:0;flex:1 1 auto;min-width:0;'; p.id = 'cos-reload-words';
153
+ var btn = document.createElement('button'); btn.type = 'button'; btn.textContent = 'Reload';
154
+ btn.style.cssText = 'flex:none;min-height:44px;padding:0 16px;border-radius:10px;border:0;background:var(--accent, #89662d);color:var(--on-accent, #ffffff);font-family:inherit;font-size:15px;font-weight:600;line-height:1;';
155
+ btn.addEventListener('click', function () { reloadNow(); });
156
+ line.appendChild(p); line.appendChild(btn);
157
+ (document.body || document.documentElement).appendChild(line);
158
+ }
159
+ var words = document.getElementById('cos-reload-words');
160
+ if (words) words.textContent = 'Part of this page did not load.' + (b && b.words ? ' Send or copy what you have typed first. A reload clears it.' : '');
161
+ }
162
+ function reloadNow() {
163
+ try { sessionStorage.setItem(KEY, JSON.stringify(R.last)); sessionStorage.setItem('cos_web_bust', String(Date.now())); } catch (_e) {}
164
+ location.reload();
165
+ }
166
+ function recover() {
167
+ if (R.scripts.length) {
168
+ // Never by itself over his words (R1): the line, and only his tap reloads.
169
+ var b = busy();
170
+ if (b.any) { R.done = true; record('offered'); offer(b); return; }
171
+ // At most 2 reloads in 5 minutes: a file that keeps failing must never become a reload loop. Fails closed (R2): a
172
+ // count that cannot be read, or that does not read back after writing, means no reload by itself.
173
+ var recent = null, back = null;
174
+ try { recent = JSON.parse(sessionStorage.getItem(RELOADS_KEY) || '[]').filter(function (t) { return Date.now() - t < RELOAD_WINDOW_MS; }); } catch (_e) { recent = null; }
175
+ if (!recent || recent.length >= RELOAD_MAX) { R.done = true; record('gave-up'); offer(b); return; }
176
+ var now = Date.now();
177
+ try { recent.push(now); sessionStorage.setItem(RELOADS_KEY, JSON.stringify(recent)); back = JSON.parse(sessionStorage.getItem(RELOADS_KEY) || 'null'); } catch (_e) { back = null; }
178
+ if (!back || back.length !== recent.length || back[back.length - 1] !== now) { R.done = true; record('gave-up'); offer(b); return; }
179
+ record('reload');
180
+ reloadNow();
181
+ return;
182
+ }
183
+ var gone = missing();
184
+ gone.forEach(function (old) {
185
+ var el = document.createElement('link');
186
+ el.rel = 'stylesheet'; el.href = bust(old.getAttribute('href') || old.href);
187
+ el.onload = function () { if (old.parentNode) old.parentNode.removeChild(old); };
188
+ old.parentNode.insertBefore(el, old.nextSibling);
189
+ });
190
+ R.done = true; record('sheets');
191
+ }
192
+ function schedule() {
193
+ if (R.timer || R.done || document.hidden) return;
194
+ var ms = R.tries < STEPS.length ? STEPS[R.tries] : SETTLED_MS;
195
+ R.timer = setTimeout(check, ms);
196
+ }
197
+ function check() {
198
+ R.timer = null;
199
+ if (R.done || document.hidden) return;
200
+ R.tries++;
201
+ // Ask for the file that failed (a script first): it answering is what matters, and a file that is not there (404)
202
+ // never counts as the Mac being back.
203
+ var gone = missing(), url = R.scripts[0] || (gone[0] && (gone[0].getAttribute('href') || gone[0].href)) || '/control/app.css';
204
+ var probe = window.fetch ? window.fetch(bust(url), { cache: 'no-store', credentials: 'same-origin' }) : Promise.reject(new Error('no fetch'));
205
+ probe.then(function (r) { if (r && r.ok) recover(); else schedule(); }, function () { schedule(); });
206
+ }
207
+ function start() {
208
+ if (R.done || R.t0) return;
209
+ R.sheets = missing().length;
210
+ if (!R.sheets && !R.scripts.length) return;
211
+ R.t0 = Date.now();
212
+ schedule();
213
+ }
214
+ window.CosRestyle = {
215
+ scriptFailed: function (src) { if (R.scripts.indexOf(src) < 0) R.scripts.push(src); if (R.t0) schedule(); else start(); },
216
+ get last() { return R.last; },
217
+ // After a reload that this caused: what the page that reloaded had seen, read once.
218
+ takeSaved: function () { try { var v = sessionStorage.getItem(KEY); sessionStorage.removeItem(KEY); return v ? JSON.parse(v) : null; } catch (_e) { return null; } }
219
+ };
220
+ document.addEventListener('visibilitychange', function () { if (!document.hidden && R.t0 && !R.done && !R.timer) check(); });
221
+ // Every <link> in the head has finished (loaded or failed) by the time the page's load event fires.
222
+ if (document.readyState === 'complete') start(); else window.addEventListener('load', start);
223
+ })();