diff --git a/blog/deciding-well-solved/og.sha b/blog/deciding-well-solved/og.sha index 10537fd..b828828 100644 --- a/blog/deciding-well-solved/og.sha +++ b/blog/deciding-well-solved/og.sha @@ -1 +1 @@ -fd33eff778150dc9761c06686c8724f3a410d2d92c31773199bf296e7a978280 +c68071b7668acbc7ccd38164266f7e969467cb3dfaef6991b9450ad18a08b401 diff --git a/blog/og.sha b/blog/og.sha index 74654db..63e6ca9 100644 --- a/blog/og.sha +++ b/blog/og.sha @@ -1 +1 @@ -0c6e7f492bbc825a28152adc82ecaa38db04c4783d8ceff19764d4da82f8a08b +2c4f702f53bca6ffe6048809778c2b4321a82cfe50204683004f39c5d5453fae diff --git a/blog/one-question-answered/og.sha b/blog/one-question-answered/og.sha index af50050..e672561 100644 --- a/blog/one-question-answered/og.sha +++ b/blog/one-question-answered/og.sha @@ -1 +1 @@ -0685827113872e73e353e63f4760d642c5453e7536aaa751598c87f69767a264 +daa7df89413ce5a2be60a0ce3f05370ec03da5dd3af382d95f6e3e88bf0fe2dd diff --git a/chat.js b/chat.js index fb9887a..f7dfb97 100644 --- a/chat.js +++ b/chat.js @@ -106,9 +106,10 @@ // root with a code; wherever the widget then runs, it checks the state, exchanges the code and // returns to that path. The access token and the ID token are kept in the browser under // `chat-signin`, every message after carries the access token as `Authorization: Bearer`, and -// `/logout` forgets both and ends the session at the service. A token that has lapsed, or that -// the host no longer takes, is forgotten too, and the message goes as a visitor's, which Sonnet 5 -// answers, under a line that says so. A tag without `data-issuer` offers +// `/logout` forgets both and ends the session at the service. While the browser waits for the +// service, which may be waking from zero, the spinner says where it is going. A token that has +// lapsed, or that the host no longer takes, is forgotten too, and the message goes as a +// visitor's, which Sonnet 5 answers, under a line that says so. A tag without `data-issuer` offers // none of the four words, `/login`, `/logout`, `/model` and `/effort`, and sends them as it // sends any question. // @@ -146,7 +147,7 @@ helloHost: ["Hello. I answer from the model of {host}, and link", "every entity I name back to where it is written."], sub: "chat · {host}", bar: "ask · {host}", prompt: "Type a question, a number, or /help", - asking: "asking the model", answered: "answered", model: "model {sha} · {secs}s", + asking: "asking the model", signingIn: "signing in at {host}", signingOut: "signing out at {host}", answered: "answered", model: "model {sha} · {secs}s", claim: { partial: ["Partly backed", "The model's pages say only part of this."], contradicted: ["Contradicted", "The model's pages say otherwise."], absent: ["Not backed", "The model's pages don't say this."], unsourced: ["Not backed", "No page the chat read says this."] }, claimsOne: "1 statement here isn't fully backed by the model's pages.", claimsMany: "{n} statements here aren't fully backed by the model's pages.", keys: { send: "enter send", last: "↑ last question", pick: "{range} pick", help: "/help", chosen: "{model} · {effort}" }, @@ -209,7 +210,7 @@ helloHost: ["Hallo. Ich antworte aus dem Modell von {host}", "und verlinke jede Entität, die ich nenne."], sub: "Chat · {host}", bar: "fragen · {host}", prompt: "Frage, Nummer oder /help tippen", - asking: "frage das Modell", answered: "beantwortet", model: "Modell {sha} · {secs}s", + asking: "frage das Modell", signingIn: "Anmeldung bei {host}", signingOut: "Abmeldung bei {host}", answered: "beantwortet", model: "Modell {sha} · {secs}s", claim: { partial: ["Teilweise belegt", "Die Seiten des Modells sagen das nur zum Teil."], contradicted: ["Widerspricht dem Modell", "Die Seiten des Modells sagen etwas anderes."], absent: ["Nicht belegt", "Die Seiten des Modells sagen das nicht."], unsourced: ["Nicht belegt", "Keine Seite, die der Chat gelesen hat, sagt das."] }, claimsOne: "Eine Aussage in dieser Antwort ist durch die Seiten des Modells nicht vollständig belegt.", claimsMany: "{n} Aussagen in dieser Antwort sind durch die Seiten des Modells nicht vollständig belegt.", keys: { send: "Enter senden", last: "↑ letzte Frage", pick: "{range} wählen", help: "/help", chosen: "{model} · {effort}" }, @@ -2513,6 +2514,30 @@ else line.appendChild(document.createTextNode(" \u00b7 ")); line.appendChild(b); } + // The spinner under the log: a frame that turns and the seconds counted, after the words for + // what is awaited. It returns what stops it and takes it away. + function spinner(words){ + var t0 = Date.now(), wait = el("p", "rbchat-spin"), frame = el("span", "rbchat-frame", "|"), secs = el("span", "rbchat-secs"); + wait.appendChild(frame); wait.appendChild(document.createTextNode(" " + words + "\u2026 ")); wait.appendChild(secs); + log.appendChild(wait); log.scrollTop = log.scrollHeight; + var spin = setInterval(function(){ frame.textContent = "|/-\\"[Math.floor((Date.now() - t0) / 90) % 4]; secs.textContent = seconds(Date.now() - t0); }, 90); + return function(){ clearInterval(spin); if (wait.parentNode) wait.parentNode.removeChild(wait); }; + } + // The trip to the identity service, waited for: it scales to zero and takes a while to wake, and + // until its page arrives this one stays, so the spinner says where the reader is going and the + // line takes nothing meanwhile. A page the back button brings back from the cache is the one + // that left, spinner and all, so it is given back its line. + var stopLeaving = null; + function leaving(words){ + busy = true; input.disabled = true; + stopLeaving = spinner(words.replace("{host}", hostOf(SITE.issuer))); + } + window.addEventListener("pageshow", function(e){ + if (!e.persisted || !stopLeaving) return; + stopLeaving(); stopLeaving = null; + busy = false; input.disabled = false; + }); + function hostOf(url){ try { return new URL(url).host; } catch (e) { return url; } } function b64url(bytes){ var bin = ""; for (var i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]); @@ -2534,6 +2559,7 @@ try { sessionStorage.setItem(PKCE_KEY, JSON.stringify({ verifier: verifier, state: state, path: path })); } catch (e) { refuse("internal"); return; } keep(); + leaving(strings(langNow()).signingIn); location.assign(authorizeUrl(SITE, b64url(new Uint8Array(h)), state, REDIRECT)); }, function(){ refuse("internal"); }); } @@ -2543,6 +2569,7 @@ var s = signedIn(); forget(); keep(); + leaving(strings(langNow()).signingOut); var q = [["client_id", SITE.client]].concat(s && s.id ? [["id_token_hint", s.id]] : []).concat([["post_logout_redirect_uri", REDIRECT]]); location.assign(SITE.issuer + "/oidc/v1/end_session?" + q.map(function(p){ return p[0] + "=" + encodeURIComponent(p[1]); }).join("&")); } @@ -2665,14 +2692,10 @@ // wait, counting the seconds, and the finished answer arrives in one piece. The answer's // element is made now and filled as the stream comes, but joins the log only in finish(). var ans = el("div", "rbchat-msg rbchat-assistant"), body = el("div", "rbchat-body"), t0 = Date.now(); - var wait = el("p", "rbchat-spin"), frame = el("span", "rbchat-frame", "|"), secs = el("span", "rbchat-secs"); - wait.appendChild(frame); wait.appendChild(document.createTextNode(" " + s.asking + "\u2026 ")); wait.appendChild(secs); - log.appendChild(wait); log.scrollTop = log.scrollHeight; - var spin = setInterval(function(){ frame.textContent = "|/-\\"[Math.floor((Date.now() - t0) / 90) % 4]; secs.textContent = seconds(Date.now() - t0); }, 90); + var stopSpin = spinner(s.asking); ans.appendChild(body); var acc = "", cites = [], names = [], cut = false, pictures = [], verdict = null, errored = false, ended = false, spoke = null, by = null; function render(){ body.innerHTML = md(acc); numberColumns(body); } - function stopSpin(){ clearInterval(spin); if (wait.parentNode) wait.parentNode.removeChild(wait); } // A stream that never ends — a dropped connection the browser does not notice — would // otherwise lock the panel forever: nothing else re-enables the form. Ninety seconds after // the request goes out, fifteen minutes above `low`, the controller aborts it, and the abort diff --git a/ideas/og.sha b/ideas/og.sha index 0b8a01d..51d1a14 100644 --- a/ideas/og.sha +++ b/ideas/og.sha @@ -1 +1 @@ -357038d5824bab66c7797dd7ac911b08c479fa86551478b25c4dc1213d0aeddb +74f9c4c3f537fd0b29bae7207ea82b4738bd99d96f4370ec6f347d4e2142c205 diff --git a/model/og.sha b/model/og.sha index f000a18..d1730b9 100644 --- a/model/og.sha +++ b/model/og.sha @@ -1 +1 @@ -4a45327d9aeca8b09809f3da286c662d77b8e44ce8a35e381aa351dccb4298ee +ac29b989dc84d108240e216ffa6b0aead41dc72d15fa25f5b99f5d846ca52ad5 diff --git a/og.sha b/og.sha index 786273b..667675c 100644 --- a/og.sha +++ b/og.sha @@ -1 +1 @@ -9e465cdc89df7f5033bb803b9bc9e7a054cc9f4e450b577331d0d6f143aae3ab +afde0464d3311517d43cd3d15dace77a7f17842606921658ec0e7c55f3c00255 diff --git a/package-lock.json b/package-lock.json index 75509cd..788849f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "companygraph-meta-model": "github:companygraph/meta-model#v0.95.0" }, "devDependencies": { - "@robertblust/design": "github:robertblust/design#v0.159.0", + "@robertblust/design": "github:robertblust/design#v0.160.0", "companygraph-mcp-server": "github:companygraph/mcp-server#v0.71.0", "pdf-lib": "^1.17.1", "playwright": "^1.64.1" @@ -95,8 +95,8 @@ } }, "node_modules/@robertblust/design": { - "version": "0.159.0", - "resolved": "git+ssh://git@github.com/robertblust/design.git#017a6f23ceb5fc5200aabedc3d24ebecdd7d6418", + "version": "0.160.0", + "resolved": "git+ssh://git@github.com/robertblust/design.git#7fcde97756f576317f67e67b3f6d2cbfdeea62a1", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/package.json b/package.json index e34f2ad..bcf1531 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "pin:check": "node pin-check.mjs" }, "devDependencies": { - "@robertblust/design": "github:robertblust/design#v0.159.0", + "@robertblust/design": "github:robertblust/design#v0.160.0", "companygraph-mcp-server": "github:companygraph/mcp-server#v0.71.0", "pdf-lib": "^1.17.1", "playwright": "^1.64.1" diff --git a/principles/og.sha b/principles/og.sha index a5feb50..9f5277a 100644 --- a/principles/og.sha +++ b/principles/og.sha @@ -1 +1 @@ -8c1042f6e5f5f0ea23e6b5520fd8ebe6dffa890304f3b1d34414ec6d2328fb53 +348f4e6b4920c1d3e179e9a287469701a0a4fbb2c10cc85fe160e9c9f8911cc4 diff --git a/privacy/og.sha b/privacy/og.sha index ff8225f..c134465 100644 --- a/privacy/og.sha +++ b/privacy/og.sha @@ -1 +1 @@ -b05078056aa77d2103256317bfe4df70d16547557574ed7d946521b75b25b280 +f113315cc80f1d10f13d6b4190e144d836e3e3f1cb9acbf089444b438e1c07a9 diff --git a/processes/og.sha b/processes/og.sha index 1186f1a..65af88c 100644 --- a/processes/og.sha +++ b/processes/og.sha @@ -1 +1 @@ -8df1c725808c65b41c8c7c43205aef77a577b9f20f7cfbaf2772f42e13f1b447 +a8d4c40c2f4db7bee5342242ab314eb4a60fec255b9c07ae2edc7cda3496d672 diff --git a/processes/pictures/answering.sha b/processes/pictures/answering.sha index 62af499..38dda96 100644 --- a/processes/pictures/answering.sha +++ b/processes/pictures/answering.sha @@ -1 +1 @@ -610f6047458f5666d89656f0b4825a9a46b7d63a44059764cbd3495df41214b5 +92124a8bcdc4c70612f8007dd0038091ecf44477ac5d273e4c0acecd2380d1e0 diff --git a/processes/pictures/deciding.sha b/processes/pictures/deciding.sha index efae957..f07f8ea 100644 --- a/processes/pictures/deciding.sha +++ b/processes/pictures/deciding.sha @@ -1 +1 @@ -ea88132bfcfa25ccaf4729eaf232c7f86cbf82c31b63f04017e9153b8b384e38 +5ff49e62856e7bddccee921edc9e1b8e25c141ed481fd6e2890466731993f502 diff --git a/processes/pictures/delivery.sha b/processes/pictures/delivery.sha index d96d494..c22331d 100644 --- a/processes/pictures/delivery.sha +++ b/processes/pictures/delivery.sha @@ -1 +1 @@ -5f49f4463780bbd940150c920fe58fc935ed47302af5626b37c0eba02ebcd828 +2bde0e4ff652433f8cd43242bd96c4472213be52e47357dac552f236442b8969 diff --git a/processes/pictures/narrating.sha b/processes/pictures/narrating.sha index b64ca2e..c7a6479 100644 --- a/processes/pictures/narrating.sha +++ b/processes/pictures/narrating.sha @@ -1 +1 @@ -1b6f46e68be95db285db4c7d00a9dbc160259c6f75d7456d21a213c0db7e650b +24c2337b2e7e55839fbdd7de3e48a7dac9ef68977d0e71dcdf296215f031955f diff --git a/surfaces/og.sha b/surfaces/og.sha index 533aeed..dd8ccbd 100644 --- a/surfaces/og.sha +++ b/surfaces/og.sha @@ -1 +1 @@ -f1479c9e788c6a714627b81b97267e6305ede677b89f07cfcac197f2b51edf50 +0b3abad8be86d0701a7ecdc54679528ee5b3f25cdd5607975db7b46fba03f3a2 diff --git a/talks/deciding-well/cost/og.sha b/talks/deciding-well/cost/og.sha index 72398bf..41936e0 100644 --- a/talks/deciding-well/cost/og.sha +++ b/talks/deciding-well/cost/og.sha @@ -1 +1 @@ -9c54e6176b0d8ed14427aa52a74e26cbf520b5d6c76a6a5a0b582b49e42faec5 +f055ce62d99f9d9543b106e4b7b82f11c07a65f4875914906d4009ee2552e61c diff --git a/talks/deciding-well/og.sha b/talks/deciding-well/og.sha index 6f2544d..1006b7f 100644 --- a/talks/deciding-well/og.sha +++ b/talks/deciding-well/og.sha @@ -1 +1 @@ -4b191eb1d05cf138d50d77adfe902f4a748affce2d34165aa95e1637a693160e +aef7400bc6c0819c2519add309bcfa55d35fe562900685fa91fa7d8d44308e76 diff --git a/talks/essential-complexity/og.sha b/talks/essential-complexity/og.sha index 208a559..d49e24a 100644 --- a/talks/essential-complexity/og.sha +++ b/talks/essential-complexity/og.sha @@ -1 +1 @@ -79ebef26252b28f0d681260f2dfeb736c71a6b55c813d63b69aa823bade18dd8 +69b6706251684282cf73ceff5a21edb013bfafbe3d04e91e7e0a56e355693f93 diff --git a/talks/mental-model/og.sha b/talks/mental-model/og.sha index 31fa361..2a341c2 100644 --- a/talks/mental-model/og.sha +++ b/talks/mental-model/og.sha @@ -1 +1 @@ -a8fca3758f3e383dbb29568baf20361917fe1c1693b9264e9a2dacc32fa4b23c +ea88b1643660457667376e55ec765149e0e6a65fc92896c0e95406431dbb7ade diff --git a/talks/og.sha b/talks/og.sha index 870f548..296caa6 100644 --- a/talks/og.sha +++ b/talks/og.sha @@ -1 +1 @@ -a851c464e729c6ae8dee4d6dad2127a13e360e55064d20c12190d3106990a488 +938f2c0c4675bd454e53a4ea9593f4be79df7a44b5ecd078ce2742311f029b4b diff --git a/timeline/og.sha b/timeline/og.sha index 8c8d98f..23f92a7 100644 --- a/timeline/og.sha +++ b/timeline/og.sha @@ -1 +1 @@ -2bfb2b5053eb04b9eddba309816fc7db11571819ce56a80f0573abd0884d9084 +1c740d1c7e46e8325ca8c79cfaeea5cbbde1b0707853c3738d9f489f2b096d9b