mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
feat(desktop): open edit bubble on pick, bundle diff+description+annotated screenshot, signal picker-exited
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
83a1a6a789
commit
c99472222a
File diff suppressed because one or more lines are too long
@ -52,4 +52,12 @@ describe('subscribePreviewEvents', () => {
|
||||
expect(() => listeners['preview://event']!({ payload: JSON.stringify({ v: 1, type: 'selection', payload: { pageUrl: 'http://x/' } }) })).not.toThrow()
|
||||
expect(useBrowserPanelStore.getState().bySession['s1']!.pickerActive).toBe(false)
|
||||
})
|
||||
|
||||
it('picker-exited event resets pickerActive', async () => {
|
||||
useBrowserPanelStore.getState().open('s1', 'http://x/a')
|
||||
useBrowserPanelStore.getState().setPicker('s1', true)
|
||||
await subscribePreviewEvents('s1')
|
||||
listeners['preview://event']!({ payload: JSON.stringify({ v: 1, type: 'picker-exited' }) })
|
||||
expect(useBrowserPanelStore.getState().bySession['s1']!.pickerActive).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@ -34,6 +34,9 @@ export async function subscribePreviewEvents(sessionId: string): Promise<() => v
|
||||
: [],
|
||||
})
|
||||
}
|
||||
else if (msg.type === 'picker-exited') {
|
||||
store.setPicker(sessionId, false)
|
||||
}
|
||||
else if (msg.type === 'error') {
|
||||
console.warn('[preview-agent]', msg)
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import { createBridge } from './bridge'
|
||||
import { captureToDataUrl, captureAnnotatedRegion } from './screenshot'
|
||||
import { createPicker } from './picker'
|
||||
import { buildElementMetadata } from './metadata'
|
||||
import { createEditBubble } from './editBubble'
|
||||
|
||||
;(() => {
|
||||
;(window as unknown as { __PREVIEW_AGENT__?: boolean }).__PREVIEW_AGENT__ = true
|
||||
@ -22,9 +23,18 @@ import { buildElementMetadata } from './metadata'
|
||||
})
|
||||
|
||||
let pickerOn = false
|
||||
let activeBubble: { destroy: () => void } | null = null
|
||||
const picker = createPicker({ onSelect: () => {} })
|
||||
|
||||
const emitSelection = async (el: Element) => {
|
||||
const teardown = () => {
|
||||
activeBubble?.destroy()
|
||||
activeBubble = null
|
||||
pickerOn = false
|
||||
picker.exit()
|
||||
bridge.send({ type: 'picker-exited' })
|
||||
}
|
||||
|
||||
const emitSelection = async (el: Element, change?: unknown) => {
|
||||
try {
|
||||
const dataUrl = await captureAnnotatedRegion(el)
|
||||
bridge.send({
|
||||
@ -33,6 +43,7 @@ import { buildElementMetadata } from './metadata'
|
||||
pageUrl: window.location.href,
|
||||
sourceHint: document.title || undefined,
|
||||
element: buildElementMetadata(el),
|
||||
change,
|
||||
screenshot: { dataUrl, kind: 'region' },
|
||||
},
|
||||
})
|
||||
@ -40,7 +51,7 @@ import { buildElementMetadata } from './metadata'
|
||||
}
|
||||
|
||||
bridge.on('enter-picker', () => { pickerOn = true; picker.enter() })
|
||||
bridge.on('exit-picker', () => { pickerOn = false; picker.exit() })
|
||||
bridge.on('exit-picker', () => { teardown() })
|
||||
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
if (!pickerOn) return
|
||||
@ -49,13 +60,16 @@ import { buildElementMetadata } from './metadata'
|
||||
}, true)
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!pickerOn) return
|
||||
if (!pickerOn || activeBubble) return
|
||||
e.preventDefault(); e.stopPropagation()
|
||||
picker.select()
|
||||
const el = picker.current() // capture BEFORE exit (exit clears current)
|
||||
pickerOn = false
|
||||
picker.exit()
|
||||
if (el) void emitSelection(el)
|
||||
const el = picker.current()
|
||||
pickerOn = false // stop hovering; keep highlight on the selected element while the bubble is open
|
||||
if (!(el instanceof HTMLElement)) { teardown(); return }
|
||||
activeBubble = createEditBubble(el, {
|
||||
onConfirm: (change) => { teardown(); void emitSelection(el, change) },
|
||||
onCancel: () => { teardown() },
|
||||
})
|
||||
}, true)
|
||||
|
||||
const onReady = () => { bridge.reportReady(); bridge.reportNavigated() }
|
||||
|
||||
@ -4,6 +4,7 @@ export type AgentMessage =
|
||||
| { type: 'error'; message: string }
|
||||
| { type: 'selection'; payload: unknown } // M5 填充结构
|
||||
| { type: 'screenshot'; dataUrl: string; kind: 'full' | 'viewport' | 'element' } // M4
|
||||
| { type: 'picker-exited' }
|
||||
|
||||
export type HostMessage =
|
||||
| { type: 'enter-picker' }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user