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
2 changes: 1 addition & 1 deletion api/og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
b09b0bd5e9cb5619888cb6fe73af6a2a6d769ae371b2e05c17e54eca1b5c1956
51f9ffaa661399ba3ea9a65459ec5f995969ccb5407c0d6ada0a09c89fc1e51a
2 changes: 1 addition & 1 deletion billing/og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6afedc20df64916ab5d6f8828eb235da1de32029baeb07fd4c288e73b4e7b74f
02e0153705f4f4e86cacd1cbb8974aec050467ffcf0d4de9ee123d9d1ae5da21
18 changes: 18 additions & 0 deletions chat.css
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,24 @@ html[data-chat-waiting]::after{content:"ask \00b7 " attr(data-chat-waiting);pos
/* A finished answer, as the tooling prints a finished step: a rail in the terminal's green and a
head line with the tick, on a card a step lighter than the terminal, so the one thing the model
wrote stands apart from the question and the menus around it. It has no animation of any kind. */
/* A question a link carries, waiting for the visitor: a card framed as an answer is, its edge the
accent at 3px where an answer's is the state color at 1.5px, its line in the accent as a thing
to act on, and one ring as it arrives. The accent is what every control is; --t-part is not
used, since in this panel it underlines a claim the evidence carries only in part. */
.rbchat-ask{margin:.2rem 0 1rem;display:grid;gap:.55rem;padding:.7rem .9rem .8rem;background:var(--t-card);border:1px solid var(--t-edge);border-left:3px solid var(--t-accent);border-radius:0 8px 8px 0}
.rbchat-ask p{margin:0}
.rbchat-ask-from{color:var(--t-accent)}
.rbchat-ask-q{color:var(--t-strong);white-space:pre-wrap;overflow-wrap:anywhere;background:var(--t-sel);padding:.45rem .7rem;border-radius:6px}
.rbchat-ask-q::before{content:"\203a ";color:var(--t-accent)}
.rbchat-ask-row{display:flex;gap:.45rem;flex-wrap:wrap}
.rbchat-ask-row button{font:inherit;font-size:.95em;padding:.3rem .65rem;border-radius:6px;border:1px solid var(--t-line);background:transparent;color:var(--t-ink);cursor:pointer}
.rbchat-ask-row button:hover{border-color:var(--t-accent)}
.rbchat-ask-row button:focus-visible{outline:2px solid var(--t-accent);outline-offset:2px}
.rbchat-ask-row .rbchat-ask-go{background:var(--t-accent);border-color:var(--t-accent);color:var(--t-bg);font-weight:600}
@media (prefers-reduced-motion:no-preference){
.rbchat-ask{animation:rbchat-ask-ring 1.6s ease-out .3s 1 both}
@keyframes rbchat-ask-ring{0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--t-accent) 55%,transparent)}100%{box-shadow:0 0 0 10px color-mix(in srgb,var(--t-accent) 0%,transparent)}}
}
.rbchat-assistant{margin:.4rem 0 0 .35rem;padding:.55rem .9rem .55rem .95rem;border-left:1.5px solid var(--t-good);background:var(--t-card);border-radius:0 8px 8px 0}
.rbchat-done{margin:0 0 .35rem;color:var(--t-dim)}
.rbchat-tick{color:var(--t-good)}
Expand Down
64 changes: 58 additions & 6 deletions chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
// rbChat.heard(turns) every name and cite the conversation's answers brought
// rbChat.refocus(window) whether the cursor goes back after an answer
// rbChat.asked(location.search) the address without ?chat=open, or null if it has none
// rbChat.linkQuestion(location.search) the question ?ask= carries and the address without it, or null
// rbChat.when(retryAt, now, lang) when a limit lifts, in the visitor's language and time
// rbChat.refusalText(code, retryAt, …) the refusal sentence, ending with that moment where there is one
// rbChat.citeLine(cites, model, icon, doc) the line under an answer: the icon, each title, each mark
Expand Down Expand Up @@ -127,6 +128,7 @@
again: { sentence: "You can ask again {when}.", minute: "in a minute", minutes: "in {n} minutes", at: "at {time}", tomorrow: "tomorrow at {time}", day: "on {day} at {time}" },
github: "{title} on GitHub", commit: "commit {sha}",
modalClose: "Close \u00b7 Esc",
ask: { from: "The link you opened asks:", label: "A question from the link you opened", send: "Send", edit: "Edit", discard: "Discard" },
diagram: { concepts: "Concepts", process: "Process", neighborhood: "Connections", schema: "Meta-model", context: "Context map", aggregate: "Aggregate", flow: "Flow", lifecycle: "Lifecycle", organization: "Organization", reading: { context: "One-way arrows run from upstream to downstream; each arrow names the pattern between the two contexts.", aggregate: "The root holds what the diamonds join; the dashed arrows are the events it emits.", flow: "Solid arrows are commands sent to the aggregate, dashed arrows the events it emits; a box names the condition of each branch.", lifecycle: "Each arrow is a step from one state to the next, labeled with the command that takes it where there is one.", organization: "Solid arrows run from a lead to the leads below; a dashed line joins staff to the head they serve; a dashed box is an open position, beside a lead the search for a successor; agents stand in the shaded frame." }, expand: "Open full screen", shut: "Close full screen", zoomIn: "Zoom in", zoomOut: "Zoom out", fit: "Fit", fitTip: "Fit to the screen", failed: "The diagram could not be drawn; this is its source." },
refusal: {
too_long: "That message is over 1,000 characters.",
Expand Down Expand Up @@ -174,6 +176,7 @@
again: { sentence: "Sie können {when} wieder fragen.", minute: "in einer Minute", minutes: "in {n} Minuten", at: "um {time}", tomorrow: "morgen um {time}", day: "am {day} um {time}" },
github: "{title} auf GitHub", commit: "Commit {sha}",
modalClose: "Schliessen \u00b7 Esc",
ask: { from: "Der Link, den Sie geöffnet haben, enthält diese Frage:", label: "Eine Frage aus dem Link, den Sie geöffnet haben", send: "Senden", edit: "Bearbeiten", discard: "Verwerfen" },
diagram: { concepts: "Konzepte", process: "Prozess", neighborhood: "Verbindungen", schema: "Meta-Modell", context: "Context Map", aggregate: "Aggregat", flow: "Ablauf", lifecycle: "Lebenszyklus", organization: "Organisation", reading: { context: "Einfache Pfeile laufen vom Upstream- zum Downstream-Kontext; jeder Pfeil nennt das Muster zwischen den beiden Kontexten.", aggregate: "Die Wurzel des Aggregats hält, was die Rauten verbinden; die gestrichelten Pfeile sind die Ereignisse, die sie auslöst.", flow: "Durchgezogene Pfeile sind Befehle an das Aggregat, gestrichelte die Ereignisse, die es auslöst; ein Kasten nennt die Bedingung jedes Zweigs.", lifecycle: "Jeder Pfeil ist ein Schritt von einem Zustand zum nächsten, beschriftet mit dem Befehl, der ihn auslöst, wo es einen gibt.", organization: "Durchgezogene Pfeile laufen von einer Führungskraft zu den Führungskräften darunter; eine gestrichelte Linie verbindet eine Stabsstelle mit der Leitung, der sie zuarbeitet; ein gestrichelter Kasten ist eine offene Stelle, neben einer Führungskraft die Suche nach ihrer Nachfolge; Software-Agenten stehen im farbig hinterlegten Rahmen." }, expand: "Im Vollbild öffnen", shut: "Vollbild schliessen", zoomIn: "Vergrössern", zoomOut: "Verkleinern", fit: "Einpassen", fitTip: "Auf den Bildschirm einpassen", failed: "Das Diagramm konnte nicht gezeichnet werden; dies ist seine Quelle." },
refusal: {
too_long: "Diese Nachricht ist länger als 1’000 Zeichen.",
Expand Down Expand Up @@ -525,6 +528,21 @@
return search.replace(re, "$1").replace(/[?&]$/, "");
}

// A link may carry a question too, with ?ask=, so a post can send a reader to one answer. The
// question is never sent on arrival: the panel opens with it waiting under the notice that says
// nothing is sent until the visitor sends, and the visitor sends, edits or discards it. Read
// once and taken out of the address, as chat=open is. This gives the question, trimmed, and the
// query string without the parameter; the question is null where it is empty, longer than a
// message may be, or not decodable, and the whole is null where the address carries no ask.
function linkQuestion(search){
var re = /([?&])ask=([^&#]*)(&|$)/, m = re.exec(search || "");
if (!m) return null;
var question = null;
try { question = decodeURIComponent(m[2].replace(/\+/g, " ")).trim(); } catch (e) { question = null; }
if (!question || question.length > LIMIT) question = null;
return { question: question, rest: search.replace(re, "$1").replace(/[?&]$/, "") };
}

// n items of a list, at random and without repeats: a Fisher–Yates shuffle of a copy, cut to
// n, which is uniform over every ordering and never picks the same item twice even where the
// caller asks for more than the list holds — it then gives back the whole list, shuffled.
Expand Down Expand Up @@ -876,7 +894,7 @@
// The rows a number picks, as the keys line and /help name them: none, one, or a range.
function rangeOf(n){ return n > 1 ? "1-" + n : n === 1 ? "1" : ""; }

window.rbChat = { md: md, readEvents: readEvents, strings: strings, link: link, refocus: refocus, asked: asked, nameLinks: nameLinks, heard: heard, when: when, refusalText: refusalText, citeLine: citeLine, iconOf: iconOf, pick: pick, unasked: unasked, sayIn: sayIn, asTitles: asTitles, answerLang: answerLang, spread: spread, mentioned: mentioned, mermaidConfig: mermaidConfig, nodeElement: nodeElement, diagramCaption: diagramCaption, nodeHref: nodeHref, oriented: oriented, follow: follow, place: place, placed: placed, lockupOf: lockupOf, command: command, picked: picked, tryRows: tryRows, commitOf: commitOf, seconds: seconds, rangeOf: rangeOf, versionsOf: versionsOf, graphHref: graphHref, entityOf: entityOf, graphTarget: graphTarget };
window.rbChat = { md: md, readEvents: readEvents, strings: strings, link: link, refocus: refocus, asked: asked, linkQuestion: linkQuestion, nameLinks: nameLinks, heard: heard, when: when, refusalText: refusalText, citeLine: citeLine, iconOf: iconOf, pick: pick, unasked: unasked, sayIn: sayIn, asTitles: asTitles, answerLang: answerLang, spread: spread, mentioned: mentioned, mermaidConfig: mermaidConfig, nodeElement: nodeElement, diagramCaption: diagramCaption, nodeHref: nodeHref, oriented: oriented, follow: follow, place: place, placed: placed, lockupOf: lockupOf, command: command, picked: picked, tryRows: tryRows, commitOf: commitOf, seconds: seconds, rangeOf: rangeOf, versionsOf: versionsOf, graphHref: graphHref, entityOf: entityOf, graphTarget: graphTarget };

// ─── The page ─────────────────────────────────────────────────────────────────────────────
var tag = document.currentScript;
Expand Down Expand Up @@ -1437,6 +1455,7 @@

// `messages` is what the server sees, `turns` the same exchange as the panel shows it: an
// answer's cites are the widget's to draw and are no part of a message.
var linkAsk = null, askEl = null;
var messages = [], turns = [], busy = false, panel = null, log = null, input = null, notice = null, title = null, closeBtn = null, grip = null, newBtn = null, keysEl = null, say = null;
// The place a restore owes the visitor, held until the log is shown and every picture above it
// has drawn, and let go the moment the visitor scrolls, sends or starts afresh: from then on
Expand Down Expand Up @@ -1756,6 +1775,34 @@
// What the intro drew at random, kept for its conversation, so a language switch redraws the
// same process and questions in the other language rather than drawing again.
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.
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);
card.appendChild(el("p", "rbchat-ask-from", s.from));
card.appendChild(el("p", "rbchat-ask-q", linkAsk));
var go = el("button", "rbchat-ask-go", s.send), edit = el("button", "", s.edit), drop = el("button", "", s.discard);
go.type = edit.type = drop.type = "button";
go.appendChild(el("span", "rbchat-ask-key", " \u23ce")).setAttribute("aria-hidden", "true");
edit.setAttribute("data-ask", "edit"); drop.setAttribute("data-ask", "discard");
row.appendChild(go); row.appendChild(edit); row.appendChild(drop); card.appendChild(row);
function done(){ var q = linkAsk; linkAsk = null; askEl = null; if (card.parentNode) card.parentNode.removeChild(card); return q; }
go.addEventListener("click", function(){ input.value = done(); send(); });
edit.addEventListener("click", function(){ input.value = done(); input.focus(); });
drop.addEventListener("click", function(){ done(); if (refocus(window)) input.focus(); });
askEl = card;
return card;
}
function showAsk(){
if (!linkAsk || (askEl && askEl.isConnected)) return;
log.appendChild(askCard());
log.scrollTop = log.scrollHeight;
}

function intro(play){
introRun++;
var mine = introRun, s = strings(langNow()), lock = lockupOf(document);
Expand All @@ -1777,6 +1824,7 @@
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.
Expand Down Expand Up @@ -2311,11 +2359,15 @@
// click: open() focuses the input, which on a touch screen would raise the keyboard over a page
// the visitor has not read yet, so there the focus is taken back at once, where refocus says.
(function arrive(){
var rest = asked(location.search);
if (rest === null) return;
var rest = asked(location.search), link = linkQuestion(rest === null ? location.search : rest);
if (rest === null && !link) return;
if (link) { rest = link.rest; linkAsk = link.question; }
try { history.replaceState(null, "", location.pathname + rest + location.hash); } catch (err) {}
if (panel && !panel.hidden) return;
open();
if (!refocus(window)) input.blur();
if (!panel || panel.hidden) open();
showAsk();
// A waiting question takes the focus on a desk, so Enter sends it; on a touch screen nothing
// keeps it, as the panel opened by an address never raises the keyboard.
if (!refocus(window)) { input.blur(); return; }
if (askEl) askEl.querySelector(".rbchat-ask-go").focus({ preventScroll: true });
})();
})();
2 changes: 1 addition & 1 deletion model/og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
c21e12c6bfeb3972b496f8c6771d4b1d68379c005ab6dafae2509e36468edf6f
62621dc202a81b9f3eef57a00d2a07ddede86be2669fda1572b8b950df692ea9
2 changes: 1 addition & 1 deletion og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
47552f53e2a220a00e53d55ceeb1d6ac1d9b78f1e520308f3e490391ad8a4d86
26c61439f947f01b412a70d7a7c9ccd2e1c03f28a8fc87b7459b67f3bd91da2d
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 @@ -25,7 +25,7 @@
"pin:check": "node pin-check.mjs"
},
"devDependencies": {
"@robertblust/design": "github:robertblust/design#v0.145.0",
"@robertblust/design": "github:robertblust/design#v0.146.0",
"companygraph-mcp-server": "github:companygraph/mcp-server#v0.62.0",
"pdf-lib": "^1.17.1",
"playwright": "^1.63.0",
Expand Down
2 changes: 1 addition & 1 deletion principles/og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
c228bbbb3f5414a986a6b0495036eeeabbec9f83ebe04b24f65458b0af53e8de
305bbef0aa0366e829db71a0fdd70089eabaedd39aeffe2b47d804aa24b1cc39
2 changes: 1 addition & 1 deletion privacy/og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2b078486c35f2881c64827627f875983294b14a878347ca8161406273c192dd1
f6d7a5f2d275ad749f91ca7bc4493064bf506e76b4b6f5137e5d02205ef42c0d
2 changes: 1 addition & 1 deletion problems/og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ab3139faa678190dafce7b5143b9a12c0b93d84c13c9b4c99539870e4e4ebda5
6d3e78d7f4c73c4cd538f3c0cc5e45c241d3c2394e1cfb30c4a824e77e5052f1
2 changes: 1 addition & 1 deletion processes/og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6ecf489951e320d0d9f4b128856b1797f50b207b06989c767162ac60f7826ea9
7a0f3c3f1dd453f6028d2b9628558be9599ae1ad10a305044a56589d64f5c09a
2 changes: 1 addition & 1 deletion processes/pictures/answering.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6d35268c4c26bc976996daf05d38a9a2719ff818c9a45a650c45afe41ef5d1b9
36c28e3a9af3515c5be560e9624c91141c16e421bd306706ba20317deedc0d48
2 changes: 1 addition & 1 deletion processes/pictures/contribution.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
275922c70f1ad840b00c68a052424da4a756b0e1e8728a3cacdf6d756c503ab0
39fbe2b63466e47740d5a0b18e0e7539b0ca1c1b772697a597a6e4dce4a052fa
2 changes: 1 addition & 1 deletion processes/pictures/deciding.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2681eacfee8f399c68cc09b95fc710b24c6aa6fb311b9b45f8137ec0717bf459
38fbc888caadf93f21afae94897af7ae56985f3c40064fde31ed04c7e282af06
2 changes: 1 addition & 1 deletion processes/pictures/delivery.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
618264e967daf54b8021630340c8a456e45241df22595fdfb49e4b4ab3a30b9a
cd93b530078ab085dec3fd07ca7d4da66ab9d5bf81fa551353840562c7e1433d
2 changes: 1 addition & 1 deletion processes/pictures/feature-request.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
032e9c38a39c65c872a8d4659cacd7ec1a3d0ea7ce10accdc7412ab2c04aa264
69b2ec4eec4dc11171187c708c60f0c447e3434b7548d80c8ebeab6a7425cef4
2 changes: 1 addition & 1 deletion processes/pictures/narrating.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1a02fd9600d268e1cf43aba6631ae8c8d9cc0cd806334c4aed167628e26d65e0
048e6e882f2082f7259c87825cd3f7a5a4c78c12e53c9866f75069d6d0119b24
2 changes: 1 addition & 1 deletion surfaces/og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
a62bece10e68732b3c4adec3b0312f2a9f1ea25de6999106e4bda41c55cea110
3e1f1dd4434aab3436b5e62cf8546bcc2902f86d25109679f09f9c5986d1949f
2 changes: 1 addition & 1 deletion talks/intro/og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7b6272bbf733b8be32b7177fcfbe7ce85cdcfb67fac453d2cf793c0f9588b270
ce0fc93d958f918cffb3e9bc490bee4a4f2804d5d88d350e8036e71bb46fb8ac
2 changes: 1 addition & 1 deletion talks/og.sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
a08ac12412c0559d898882814c184768bb410e1ea94c70f11b381ed8a27ae3ee
4a8276464058b68abd8dad5826ab61fdc61646b210ce507fe5841c249ad03a3e
Loading