Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions blog/deciding-well-solved/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
}
</script>
<script>
/* ─── theme boot · v5 · page ────────────────────────────────────
/* ─── theme boot · v6 · page ────────────────────────────────────
Set the theme before anything paints. Generated from @robertblust/design —
editing it here does nothing, because the next `npm run design` overwrites it.

Expand Down Expand Up @@ -187,13 +187,14 @@
try {
if (window.top !== window && /[?&]embed(&|$)/.test(location.search)) document.documentElement.setAttribute("data-embed", "");
} catch (e) {}
// A conversation the tab keeps open: the keys are chat.js's, `chat` and `chat-size`, read
// the way it reads them. Never in a frame, which takes no chat of its own.
// A conversation the tab keeps open: the keys are chat.js's, `chat` in the tab's storage and
// `chat-size` in the browser's, read the way it reads them. Never in a frame, which takes no
// chat of its own.
try {
var r = document.documentElement, c = JSON.parse(sessionStorage.getItem("chat") || "null");
if (c && c.open && window.top === window) {
r.setAttribute("data-chat-waiting", location.host);
var z = JSON.parse(sessionStorage.getItem("chat-size") || "null");
var z = JSON.parse(localStorage.getItem("chat-size") || "null");
if (z && z.w && z.h) { r.style.setProperty("--rbchat-kept-w", z.w + "px"); r.style.setProperty("--rbchat-kept-h", z.h + "px"); }
}
} catch (e) {}
Expand Down
2 changes: 1 addition & 1 deletion blog/deciding-well-solved/og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6fa00f1cdf203c25416e3789de3483bd459c6e42a49810cb723b4cb268b8ee6e
bd374b662595438d2a64b876e5b5be50461e7bead089ad51c80660101ab023c8
9 changes: 5 additions & 4 deletions blog/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
}
</script>
<script>
/* ─── theme boot · v5 · page ────────────────────────────────────
/* ─── theme boot · v6 · page ────────────────────────────────────
Set the theme before anything paints. Generated from @robertblust/design —
editing it here does nothing, because the next `npm run design` overwrites it.

Expand Down Expand Up @@ -155,13 +155,14 @@
try {
if (window.top !== window && /[?&]embed(&|$)/.test(location.search)) document.documentElement.setAttribute("data-embed", "");
} catch (e) {}
// A conversation the tab keeps open: the keys are chat.js's, `chat` and `chat-size`, read
// the way it reads them. Never in a frame, which takes no chat of its own.
// A conversation the tab keeps open: the keys are chat.js's, `chat` in the tab's storage and
// `chat-size` in the browser's, read the way it reads them. Never in a frame, which takes no
// chat of its own.
try {
var r = document.documentElement, c = JSON.parse(sessionStorage.getItem("chat") || "null");
if (c && c.open && window.top === window) {
r.setAttribute("data-chat-waiting", location.host);
var z = JSON.parse(sessionStorage.getItem("chat-size") || "null");
var z = JSON.parse(localStorage.getItem("chat-size") || "null");
if (z && z.w && z.h) { r.style.setProperty("--rbchat-kept-w", z.w + "px"); r.style.setProperty("--rbchat-kept-h", z.h + "px"); }
}
} catch (e) {}
Expand Down
2 changes: 1 addition & 1 deletion blog/og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4c74b15f0943d1b60a4d319cb06d996d7580050c0816de6c4a2345d0518f161f
159693450fffd0499d27c4771bd1fef9d4fdea78dc8132077fc431dd9e2fe1e0
9 changes: 5 additions & 4 deletions blog/one-question-answered/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
}
</script>
<script>
/* ─── theme boot · v5 · page ────────────────────────────────────
/* ─── theme boot · v6 · page ────────────────────────────────────
Set the theme before anything paints. Generated from @robertblust/design —
editing it here does nothing, because the next `npm run design` overwrites it.

Expand Down Expand Up @@ -187,13 +187,14 @@
try {
if (window.top !== window && /[?&]embed(&|$)/.test(location.search)) document.documentElement.setAttribute("data-embed", "");
} catch (e) {}
// A conversation the tab keeps open: the keys are chat.js's, `chat` and `chat-size`, read
// the way it reads them. Never in a frame, which takes no chat of its own.
// A conversation the tab keeps open: the keys are chat.js's, `chat` in the tab's storage and
// `chat-size` in the browser's, read the way it reads them. Never in a frame, which takes no
// chat of its own.
try {
var r = document.documentElement, c = JSON.parse(sessionStorage.getItem("chat") || "null");
if (c && c.open && window.top === window) {
r.setAttribute("data-chat-waiting", location.host);
var z = JSON.parse(sessionStorage.getItem("chat-size") || "null");
var z = JSON.parse(localStorage.getItem("chat-size") || "null");
if (z && z.w && z.h) { r.style.setProperty("--rbchat-kept-w", z.w + "px"); r.style.setProperty("--rbchat-kept-h", z.h + "px"); }
}
} catch (e) {}
Expand Down
2 changes: 1 addition & 1 deletion blog/one-question-answered/og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7514dd0b7f23a7b742a614cdd79dd9b5814c6feef5140629cac77fd2e2fc9395
fc05950ec498af33cfe68fe0052ac42617ff4bcb4967ec3071eb751ec45d8b5a
41 changes: 26 additions & 15 deletions chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -714,21 +714,22 @@
// rendered, each answer with the entities it cited. Every access is wrapped, because a
// browser with site data blocked throws on the first read and the chat still has to work.
var STORE_KEY = "chat";
// How big the panel is, kept beside the conversation and for the same lifetime: a size is a
// choice about this tab's reading, not a preference to carry to the next visit, and the
// conversation it frames goes when the tab goes. Its own key, because it outlives any one
// conversation: a visitor who sizes the panel, empties it and asks again keeps the size.
// How big the panel is, kept in `localStorage` as `lang` and `theme` are: a size is a choice
// about how this visitor reads the chat on this screen, so it holds in every tab and on the
// next visit, where the conversation it frames goes when its tab goes. Its own key, because it
// is no part of any conversation: a link opened in a new tab finds the panel at the size the
// visitor gave it, with nothing else carried over.
var SIZE_KEY = "chat-size";
var MIN_W = 320, MIN_H = 260, STEP = 24;
function stored(){
try { var raw = sessionStorage.getItem(STORE_KEY); return raw ? JSON.parse(raw) : null; }
catch (e) { return null; }
}
function storedSize(){
try { var raw = sessionStorage.getItem(SIZE_KEY); var v = raw ? JSON.parse(raw) : null; return v && v.w && v.h ? v : null; }
try { var raw = localStorage.getItem(SIZE_KEY); var v = raw ? JSON.parse(raw) : null; return v && v.w && v.h ? v : null; }
catch (e) { return null; }
}
function keepSize(w, h){ try { sessionStorage.setItem(SIZE_KEY, JSON.stringify({ w: w, h: h })); } catch (e) {} }
function keepSize(w, h){ try { localStorage.setItem(SIZE_KEY, JSON.stringify({ w: w, h: h })); } catch (e) {} }

// Where the visitor was reading, kept with the conversation so the back button returns them
// to it and not to the end. A place is a turn and a distance into its bubble, not a pixel
Expand Down Expand Up @@ -1872,9 +1873,10 @@
var introPick = null;
// The question a link carries, waiting: a card the visitor acts on, built from the strings and
// never from markup, so whatever the link says is shown as text. Send hands it to send() as if
// typed, Edit puts it in the input to change, and Discard drops it. Under the notice while the
// intro stands, since that is what the visitor reads first; at the end of a conversation the
// tab already holds, since the intro is then out of sight above it.
// typed, Edit puts it in the input to change, and Discard drops it. It stands right after the
// intro's prompt line, where a question the visitor typed would stand, so the hello, the notice
// and what the panel offers read in the order they always do and the question is the last thing
// in the log; at the end of a conversation the tab already holds, for the same reason.
function askCard(){
var s = strings(langNow()).ask, card = el("div", "rbchat-ask"), row = el("div", "rbchat-ask-row");
card.setAttribute("role", "group"); card.setAttribute("aria-label", s.label);
Expand All @@ -1892,9 +1894,10 @@
askEl = card;
return card;
}
// Wherever the card stands it is the last thing in the log, so the log's end is where it shows.
function showAsk(){
if (!linkAsk || (askEl && askEl.isConnected)) return;
log.appendChild(askCard());
if (!linkAsk) return;
if (!(askEl && askEl.isConnected)) log.appendChild(askCard());
log.scrollTop = log.scrollHeight;
}

Expand All @@ -1919,11 +1922,11 @@
hello.appendChild(el("span", "rbchat-h2", lines[1]));
introEl.appendChild(hello);
notice = el("p", "rbchat-notice"); introEl.appendChild(notice); writeNotice();
if (linkAsk && !messages.length) introEl.appendChild(askCard());
var groups = el("div", "rbchat-groups"); introEl.appendChild(groups);
var promptLine = el("p", "rbchat-prompt-line", "› " + s.prompt + " ");
// The block cursor waits here while the intro plays, and hands over to the command line.
promptLine.appendChild(el("span", "rbchat-cur")); introEl.appendChild(promptLine);
if (linkAsk && !messages.length) introEl.appendChild(askCard());
log.insertBefore(introEl, log.firstChild);
if (!play || still()) finishIntro();
facts(function(f){
Expand Down Expand Up @@ -1956,6 +1959,8 @@
// A restored conversation has moved past the intro: its menus are spent, and the rows a
// number picks stay those of the menu after the last answer.
if (messages.length) spendIntro(); else { menuRows = rows.concat(picked).map(function(r){ return r[0]; }); keysLine(); }
// The menus are drawn above a waiting question and would push it out of sight.
if (askEl && askEl.parentNode === introEl) log.scrollTop = log.scrollHeight;
if (!introEl.classList.contains("rbchat-still")) playIntro();
});
});
Expand Down Expand Up @@ -2080,7 +2085,13 @@
for (var b = 0; b < labels.length; b++) labels[b].textContent = strings(answerLang(turns, langNow())).askNext;
}
// A language switch redraws the intro in the new language, finished, where the log holds one.
if (introEl && log) { var keep_ = log.scrollTop; introEl.parentNode && introEl.parentNode.removeChild(introEl); introEl = null; intro(false); log.scrollTop = keep_; }
// A waiting question is redrawn with it, and the button that held the focus holds it again:
// a page sets its language after the panel has opened, and Enter still has to send.
if (introEl && log) {
var keep_ = log.scrollTop, held = askEl && askEl.contains(document.activeElement) ? Array.prototype.indexOf.call(askEl.querySelectorAll("button"), document.activeElement) : -1;
introEl.parentNode && introEl.parentNode.removeChild(introEl); introEl = null; intro(false); log.scrollTop = keep_;
if (held >= 0 && askEl) askEl.querySelectorAll("button")[held].focus({ preventScroll: true });
}
// The chips standing under an answer are drawn again from the titles they were picked as, so a
// model question reads in the new language and a number key sends what its row now shows.
if (qBox && qPicked && qPicked.length) { var was_ = qPicked.slice(); hideQuestions(); offer(was_); }
Expand Down Expand Up @@ -2111,7 +2122,7 @@
// something to clear: an empty panel shows no control for emptying it. A conversation has no
// length at which it must start over, so this is the only door; its reset takes the turns,
// the log and the tab's copy together; the panel's size stays, being a choice
// about this tab's reading rather than part of the conversation.
// about how the visitor reads rather than part of the conversation.
newBtn = el("button", "rbchat-new"); newBtn.type = "button"; newBtn.hidden = true;
// The glyph is an arrow come back round, not a plus: a plus beside the cross read as "add",
// and what the button does is start over. The note under it names the action for a pointer
Expand Down Expand Up @@ -2206,7 +2217,7 @@
// Double-click gives the stylesheet's size back and forgets the stored one, as the stage's
// gutter does: otherwise the only way back is to drag until it looks right again.
grip.addEventListener("dblclick", function(){
try { sessionStorage.removeItem(SIZE_KEY); } catch (e) {}
try { localStorage.removeItem(SIZE_KEY); } catch (e) {}
unsize();
});
grip.addEventListener("keydown", function(ev){
Expand Down
9 changes: 5 additions & 4 deletions ideas/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
}
</script>
<script>
/* ─── theme boot · v5 · page ────────────────────────────────────
/* ─── theme boot · v6 · page ────────────────────────────────────
Set the theme before anything paints. Generated from @robertblust/design —
editing it here does nothing, because the next `npm run design` overwrites it.

Expand Down Expand Up @@ -157,13 +157,14 @@
try {
if (window.top !== window && /[?&]embed(&|$)/.test(location.search)) document.documentElement.setAttribute("data-embed", "");
} catch (e) {}
// A conversation the tab keeps open: the keys are chat.js's, `chat` and `chat-size`, read
// the way it reads them. Never in a frame, which takes no chat of its own.
// A conversation the tab keeps open: the keys are chat.js's, `chat` in the tab's storage and
// `chat-size` in the browser's, read the way it reads them. Never in a frame, which takes no
// chat of its own.
try {
var r = document.documentElement, c = JSON.parse(sessionStorage.getItem("chat") || "null");
if (c && c.open && window.top === window) {
r.setAttribute("data-chat-waiting", location.host);
var z = JSON.parse(sessionStorage.getItem("chat-size") || "null");
var z = JSON.parse(localStorage.getItem("chat-size") || "null");
if (z && z.w && z.h) { r.style.setProperty("--rbchat-kept-w", z.w + "px"); r.style.setProperty("--rbchat-kept-h", z.h + "px"); }
}
} catch (e) {}
Expand Down
2 changes: 1 addition & 1 deletion ideas/og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
b30dad0b99c88d8c2f85d54492fec35d4fe49b86823539cc1f38fa32f85a457d
c453eaff43ce4168ffa28bb87db19fe0d41e591b8dbeb64cbe78eea14189e1b9
9 changes: 5 additions & 4 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
}
</script>
<script>
/* ─── theme boot · v5 · page ────────────────────────────────────
/* ─── theme boot · v6 · page ────────────────────────────────────
Set the theme before anything paints. Generated from @robertblust/design —
editing it here does nothing, because the next `npm run design` overwrites it.

Expand Down Expand Up @@ -134,13 +134,14 @@
try {
if (window.top !== window && /[?&]embed(&|$)/.test(location.search)) document.documentElement.setAttribute("data-embed", "");
} catch (e) {}
// A conversation the tab keeps open: the keys are chat.js's, `chat` and `chat-size`, read
// the way it reads them. Never in a frame, which takes no chat of its own.
// A conversation the tab keeps open: the keys are chat.js's, `chat` in the tab's storage and
// `chat-size` in the browser's, read the way it reads them. Never in a frame, which takes no
// chat of its own.
try {
var r = document.documentElement, c = JSON.parse(sessionStorage.getItem("chat") || "null");
if (c && c.open && window.top === window) {
r.setAttribute("data-chat-waiting", location.host);
var z = JSON.parse(sessionStorage.getItem("chat-size") || "null");
var z = JSON.parse(localStorage.getItem("chat-size") || "null");
if (z && z.w && z.h) { r.style.setProperty("--rbchat-kept-w", z.w + "px"); r.style.setProperty("--rbchat-kept-h", z.h + "px"); }
}
} catch (e) {}
Expand Down
5 changes: 3 additions & 2 deletions model.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"commit": "43dfa37bb4fa92ca6fe2a6a311b3f80c1d135158",
"commit": "de6fa1327af126b53af0c88de385592d8ba1b6a2",
"core": "0.69.0",
"root": "Robert Blust",
"rootId": "01a04c85-bc20-7849-9ec1-d877215d70b4",
Expand Down Expand Up @@ -20070,8 +20070,9 @@
"fields": {
"id": "01a108dd-6751-7040-bd3c-4bb5ecf70bc6",
"source": "Local",
"mechanism": "session-storage",
"mechanism": "local-storage",
"necessity": "optional",
"duration": "Until the visitor clears it",
"surfaces": [
"chat.blust.ch chat"
]
Expand Down
9 changes: 5 additions & 4 deletions model/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
<!-- Publisher and page, nothing about the product. The same rule the other two pages
follow: structured data is not a back door for claims the prose does not make. -->
<script>
/* ─── theme boot · v5 · page ────────────────────────────────────
/* ─── theme boot · v6 · page ────────────────────────────────────
Set the theme before anything paints. Generated from @robertblust/design —
editing it here does nothing, because the next `npm run design` overwrites it.

Expand Down Expand Up @@ -154,13 +154,14 @@
try {
if (window.top !== window && /[?&]embed(&|$)/.test(location.search)) document.documentElement.setAttribute("data-embed", "");
} catch (e) {}
// A conversation the tab keeps open: the keys are chat.js's, `chat` and `chat-size`, read
// the way it reads them. Never in a frame, which takes no chat of its own.
// A conversation the tab keeps open: the keys are chat.js's, `chat` in the tab's storage and
// `chat-size` in the browser's, read the way it reads them. Never in a frame, which takes no
// chat of its own.
try {
var r = document.documentElement, c = JSON.parse(sessionStorage.getItem("chat") || "null");
if (c && c.open && window.top === window) {
r.setAttribute("data-chat-waiting", location.host);
var z = JSON.parse(sessionStorage.getItem("chat-size") || "null");
var z = JSON.parse(localStorage.getItem("chat-size") || "null");
if (z && z.w && z.h) { r.style.setProperty("--rbchat-kept-w", z.w + "px"); r.style.setProperty("--rbchat-kept-h", z.h + "px"); }
}
} catch (e) {}
Expand Down
2 changes: 1 addition & 1 deletion model/og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
707f659fa51df4fed37b442ae534a135cabacb3783a2da957793560f9ae4850c
f5d058c57b8eaf551a82868e0f800b4f484bf1605c84eb216c34b46ba7f0292f
2 changes: 1 addition & 1 deletion og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
aa62036005892c1af005b70c23a499fa6d079257639c3af520c3cd51951f0a40
49b0520b27bf571c8eafd5d34fc33f6a0014f6e543df2de49437efdb31b82c0b
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"pin:check": "node pin-check.mjs"
},
"devDependencies": {
"@robertblust/design": "github:robertblust/design#v0.155.0",
"@robertblust/design": "github:robertblust/design#v0.156.0",
"companygraph-mcp-server": "github:companygraph/mcp-server#v0.69.0",
"pdf-lib": "^1.17.1",
"playwright": "^1.63.0"
Expand Down
Loading
Loading