feat(aiui): receive a Cmd+K handoff and prefill the composer

Completes the 'Talk to AIUI about it' path from the other side. archyBridge
gains a chat:prefill case behind its existing parent-origin validation, and
ChatInput prefills + focuses with the caret at the end.

Prefills rather than auto-sends: the operator sees and can amend the question
before it costs a model call, and a draft they had already started is never
clobbered by a background handoff. Auto-send is the natural seam for the
follow-up that actions things directly.

The bridge buffers a prefill that arrives before the composer mounts (collapsed
chat, mobile content tab) and replays it on registration, so a Cmd+K ask into a
cold frame is not silently dropped. onPrefill returns an unsubscribe so a
remounting composer cannot leak a stale handler.

Verified: vue-tsc clean; AIUI suite 332 passed. The 3 remaining failures
(seed-songs extraction x2, web-search system prompt) are pre-existing — I
confirmed by reverting 13-01's two AIUI files to their parent state and
reproducing the identical 3 failures without any phase-13 change present.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-03 16:50:23 -04:00
co-authored by Claude Opus 5
parent 97b3707b5e
commit e1624dda08
2 changed files with 61 additions and 1 deletions
@@ -101,6 +101,19 @@ function handleMessage(event: MessageEvent) {
}
break
}
// Archy's ⌘K search offers "Talk to AIUI about it"; this carries the text
// the operator typed there into our composer.
case 'chat:prefill': {
const text = typeof msg.text === 'string' ? msg.text : ''
if (!text) break
if (prefillCallback) prefillCallback(text)
// Buffer when nothing is listening yet: the composer may not be mounted
// at handshake time (collapsed chat, mobile content tab), and dropping
// the text would silently lose what the operator typed.
else bufferedPrefill = text
break
}
}
}
@@ -116,6 +129,9 @@ function handleMessage(event: MessageEvent) {
* dev server port). `document.referrer` is the standard, cross-origin-safe
* way an iframed document learns its embedding parent's URL.
*/
let prefillCallback: ((text: string) => void) | null = null
let bufferedPrefill = ''
function deriveParentOrigin(): string {
if (typeof document !== 'undefined' && document.referrer) {
try {
@@ -142,6 +158,23 @@ export const archyBridge = {
postToParent({ type: 'ready' })
},
/**
* Register the composer's prefill handler. Replays a prefill that arrived
* before the composer mounted, so a ⌘K ask is never dropped on a cold frame.
* Returns an unsubscribe so a remounting composer does not leak the old one.
*/
onPrefill(fn: (text: string) => void) {
prefillCallback = fn
if (bufferedPrefill) {
const text = bufferedPrefill
bufferedPrefill = ''
fn(text)
}
return () => {
if (prefillCallback === fn) prefillCallback = null
}
},
/** Clean up listeners */
destroy() {
window.removeEventListener('message', handleMessage)