Harden Chrome bridge framing and preserve panel state for IDE beta 7

This commit is contained in:
yilsem committed 2026-09-07 12:50:15 +03:00
1 parent fb6f57cd36
commit 57876d11f0
8 files changed
+192 -18

No files matched your search

+19 -7
View File
@@ -3,6 +3,7 @@ import { capturePageContext, composePrompt } from './page-context.js'
const $ = id => document.getElementById(id)
let port, connected = false, sessions = [], selected = '', generation = 0, nextId = 0
let stream, attachment = null, sending = false, opening = false, connectionGeneration = 0
let bufferedEvents = [], turnGeneration = 0
const pending = new Map()
const notice = text => { $('notice').textContent = text; $('notice').hidden = !text }
const activity = text => { $('activity').textContent = text }
@@ -112,6 +113,7 @@ function paintHistory(messages) {
async function openSession(id) {
const intent = ++generation
selected = id; opening = Boolean(id); stream = null
bufferedEvents = []
attachment = null; $('attachment').hidden = true
$('message').value = ''; $('stop').hidden = true
notice(''); controls()
@@ -127,24 +129,31 @@ async function openSession(id) {
if (data.attention) notice('Bu konuşma IDE’de onay veya ek bilgi bekliyor.')
await chrome.storage.local.set({ selectedSession: id })
} catch (error) { if (intent === generation) { selected = ''; notice(error.message) } }
finally { if (intent === generation) { opening = false; controls() } }
finally {
if (intent === generation) {
opening = false; controls()
const events = bufferedEvents; bufferedEvents = []
events.forEach(handleEvent)
}
}
}
async function reconcile() {
const intent = generation, sessionId = selected
const intent = generation, sessionId = selected, turn = turnGeneration
try {
const data = await request('session.history', { sessionId })
if (intent === generation && sessionId === selected && !opening) paintHistory(data.messages)
if (intent === generation && sessionId === selected && !opening && !stream && turn === turnGeneration) paintHistory(data.messages)
} catch (error) { if (intent === generation) notice(error.message) }
}
function handleEvent(frame) {
if (frame.event === 'disconnected') { disconnect(frame.message); return }
if (frame.event === 'error') { notice(frame.message); return }
if (frame.sessionId !== selected || opening) return
if (frame.sessionId !== selected) return
if (opening) { if (bufferedEvents.length < 2000) bufferedEvents.push(frame); return }
const nearBottom = $('transcript').scrollHeight - $('transcript').scrollTop - $('transcript').clientHeight < 120
switch (frame.event) {
case 'message.start': stream = null; $('stop').hidden = false; activity('AITURK düşünüyor…'); break
case 'message.start': turnGeneration++; stream = null; $('stop').hidden = false; activity('AITURK düşünüyor…'); break
case 'message.delta':
stream ||= bubble('assistant', '', true)
stream.textContent = (stream.textContent + frame.text).slice(-100000)
@@ -169,14 +178,17 @@ $('composer').addEventListener('submit', async event => {
const text = composePrompt(draft, attachment)
const intent = generation, sessionId = selected
sending = true; controls(); notice(''); activity('IDE’ye gönderiliyor…')
const optimistic = bubble('user', text); scrollEnd()
try {
await request('message.send', { sessionId, text })
if (intent !== generation) return
// Keep server history authoritative; optimistic text is replaced on finish.
bubble('user', text); scrollEnd()
$('message').value = ''; attachment = null; $('attachment').hidden = true
$('stop').hidden = false; activity('Mesaj IDE’ye iletildi; iş sürüyorsa sıraya alınır.')
} catch (error) { notice(error.message + ' Taslağınız korundu. Otomatik tekrar gönderilmedi.') }
} catch (error) {
optimistic.parentElement?.remove()
notice(error.message + ' Taslağınız korundu. Otomatik tekrar gönderilmedi.')
}
finally { sending = false; controls() }
})
$('message').addEventListener('input', controls)