Skip to content

Commit 07f0085

Browse files
suleimanshclaude
andcommitted
Extension: wait for the picker's list to load before filtering it, fall back to the whole list, and say what the list showed on failure (#1328 dogfood 3)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent d4eff8a commit 07f0085

3 files changed

Lines changed: 32 additions & 11 deletions

File tree

packages/chrome-extension/background.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ async function pollStarts() {
266266
id: start.id,
267267
ok,
268268
...(ok ? { sessionId: outcome.sessionId } : {}),
269-
...(outcome?.note ? { note: String(outcome.note).slice(0, 300) } : {}),
269+
...(outcome?.note ? { note: String(outcome.note).slice(0, 1500) } : {}),
270270
}),
271271
})
272272
} catch {

packages/chrome-extension/content.js

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -562,20 +562,35 @@ const MENU_WAIT_MS = 6000
562562
*/
563563
async function chooseFrom(trigger, wanted, hint) {
564564
trigger.el.click()
565-
const search = await waitFor(pickerSearch, 1500, 100)
566-
if (search) typeInto(search, wanted[0])
567565
const outside = e => e.el !== trigger.el && !trigger.el.contains(e.el)
568-
const hit = await waitFor(() => {
569-
const entries = menuEntries().filter(outside)
566+
const entries = () => menuEntries().filter(outside)
567+
const find = () => {
568+
const now = entries()
570569
for (const w of wanted) {
571-
const exact = entries.find(e => e.text.toLowerCase() === w)
570+
const exact = now.find(e => e.text.toLowerCase() === w)
572571
if (exact) return exact
573572
}
574573
return undefined
575-
}, MENU_WAIT_MS)
574+
}
575+
// The list is fetched after the picker opens: wait for it to hold something before filtering
576+
// it, and filter only when it has a search box. A filter that finds nothing is cleared and the
577+
// whole list scanned, in case the page's search matches differently than expected.
578+
const search = await waitFor(pickerSearch, 1500, 100)
579+
const loaded = await waitFor(() => entries().length > 0, MENU_WAIT_MS)
580+
let hit = loaded ? find() : undefined
581+
if (!hit && search) {
582+
typeInto(search, wanted[0])
583+
hit = await waitFor(find, 3000)
584+
if (!hit) {
585+
typeInto(search, '')
586+
hit = await waitFor(find, 3000)
587+
}
588+
}
576589
if (!hit) {
590+
const seen = entries()
591+
const diag = `search ${search ? `"${search.placeholder}"` : 'none'}, ${seen.length} visible of ${deepQueryAll('[role="option"]').length} options${seen.length ? `: ${seen.slice(0, 5).map(e => JSON.stringify(e.text.slice(0, 40))).join(', ')}` : ''}; chips ${JSON.stringify(chips().map(c => c.text))}`
577592
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))
578-
return { ok: false, note: `${hint}: the list offered no "${wanted[0]}"` }
593+
return { ok: false, note: `${hint}: the list offered no "${wanted[0]}" (${diag})` }
579594
}
580595
hit.el.click()
581596
return { ok: true, note: `${hint}: clicked "${hit.text}" via ${trigger.via}` }
@@ -629,8 +644,13 @@ async function createSession({ repo, branch, prompt }) {
629644
// A remembered other repository's chip is the picker; with nothing remembered, the page offers one.
630645
const trigger = chips()[0]?.text ? chips()[0] : selectRepo()
631646
if (!trigger) return { ok: false, note: `no repo picker on the page (${JSON.stringify(probeNewSession())})` }
632-
const pick = await chooseFrom(trigger, repoWanted, 'repo')
633-
if (!pick.ok) return pick
647+
let pick = await chooseFrom(trigger, repoWanted, 'repo')
648+
if (!pick.ok) {
649+
// The page may have finished loading its remembered repository under us: read again once.
650+
await new Promise(resolve => setTimeout(resolve, 1500))
651+
if (repoWanted.includes(chips()[0]?.text.toLowerCase())) pick = { ok: true, note: `repo already ${chips()[0].text} (after a late render)` }
652+
else return pick
653+
}
634654
repoNote = pick.note
635655
const chip = await waitFor(() => (repoWanted.includes(chips()[0]?.text.toLowerCase()) ? chips()[0] : undefined), MENU_WAIT_MS)
636656
if (!chip) return { ok: false, note: `${repoNote}, but the repo chip does not read "${repo}" afterwards (chips: ${JSON.stringify(chips().map(c => c.text))})` }

packages/framework/src/dashboard/bridge-endpoints.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,8 @@ async function handleStarted(req: IncomingMessage, res: ServerResponse, handlers
385385
return end(res, 400, 'sessionId must look like session_<id>')
386386
}
387387
if (note !== undefined && typeof note !== 'string') return end(res, 400, 'note must be a string')
388-
handlers.started?.(id, ok, typeof sessionId === 'string' ? sessionId : undefined, typeof note === 'string' ? note.slice(0, 300) : undefined)
388+
// A failure's note is the only diagnosis the run gets, so it may carry the page's controls.
389+
handlers.started?.(id, ok, typeof sessionId === 'string' ? sessionId : undefined, typeof note === 'string' ? note.slice(0, 1500) : undefined)
389390
end(res, 204, '')
390391
}
391392

0 commit comments

Comments
 (0)