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:
co-authored by
Claude Opus 5
parent
97b3707b5e
commit
e1624dda08
@@ -167,7 +167,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, nextTick, watch } from 'vue'
|
||||
import { ref, computed, nextTick, watch, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { archyBridge } from '../../services/archyBridge'
|
||||
import { useFederatedSearch, type SearchResult } from '@/composables/useFederatedSearch'
|
||||
import SearchResults from '@/components/ui/SearchResults.vue'
|
||||
import PromptPalette from './PromptPalette.vue'
|
||||
@@ -212,6 +213,32 @@ const images = ref<ImageAttachment[]>([])
|
||||
|
||||
const paletteRef = ref<InstanceType<typeof PromptPalette> | null>(null)
|
||||
|
||||
// Archy's ⌘K search can hand us the text the operator typed there
|
||||
// ("Talk to AIUI about it"). We prefill and focus rather than auto-sending:
|
||||
// the operator gets to see and amend the question before it costs a model
|
||||
// call. Anything already half-typed here wins — their own draft is not
|
||||
// clobbered by a background handoff.
|
||||
let releasePrefill: (() => void) | null = null
|
||||
|
||||
onMounted(() => {
|
||||
releasePrefill = archyBridge.onPrefill((incoming: string) => {
|
||||
if (text.value.trim()) return
|
||||
text.value = incoming
|
||||
nextTick(() => {
|
||||
autoResize()
|
||||
textareaRef.value?.focus()
|
||||
// Caret to the end so they can keep typing straight away.
|
||||
const el = textareaRef.value
|
||||
if (el) el.setSelectionRange(el.value.length, el.value.length)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
releasePrefill?.()
|
||||
releasePrefill = null
|
||||
})
|
||||
|
||||
// Prompt palette: opens when text starts with "/" and has no space yet
|
||||
const isPaletteMode = computed(() => {
|
||||
const t = text.value.trimStart()
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user