Conversation
Co-authored-by: Evan <iamEvanYT@users.noreply.github.com>
|
Cursor Agent can help with this pull request. Just |
Build artifacts for all platforms are ready! 🚀Download the artifacts for: One-line installer (Unstable):bunx flow-debug-build --open 22789508418(execution 22789508418 / attempt 1) |
Greptile SummaryThis PR implements custom, tab-scoped JavaScript dialog overlays ( Key changes:
Issues found:
Confidence Score: 4/5
Sequence DiagramsequenceDiagram
participant Page as Web Page (Renderer)
participant Preload as Preload Script
participant Protocol as flow-dialog:// Handler (Main)
participant Controller as TabDialogsController (Main)
participant BrowserUI as Browser UI Renderer
participant User as User
Note over Preload: On load: installTabDialogsOverride()
Preload->>Protocol: ipcRenderer.sendSync("tab-dialogs:register-client")
Protocol-->>Preload: clientId
Note over Preload: Patches window.alert/confirm/prompt
Page->>Preload: window.alert("Hello!") [blocks]
Preload->>Protocol: XHR POST flow-dialog://dialog/request (sync, blocks page)
Protocol->>Controller: handleProtocolRequest(payload)
Controller->>Controller: Create PendingDialogEntry + dialogId
Controller->>BrowserUI: IPC "tab-dialogs:on-state-changed" [dialogs array]
BrowserUI->>User: Render JavaScriptDialogCard overlay
User->>BrowserUI: Click OK / press Enter
BrowserUI->>Controller: IPC "tab-dialogs:respond"(dialogId, response)
Controller->>Controller: Resolve pending Promise → HTTP Response
Controller->>BrowserUI: IPC "tab-dialogs:on-state-changed" [empty]
BrowserUI->>User: Dialog dismissed
Protocol-->>Preload: XHR response {accept: true, promptText: ""}
Preload-->>Page: alert() returns [unblocks]
|
| for (const [dialogId, entry] of this.pendingDialogs) { | ||
| if (entry.clientId !== clientId) continue; | ||
|
|
||
| entry.resolve(this.createResolvedResponse(false, "")); |
There was a problem hiding this comment.
In handleProtocolRequest, when no valid client exists the code correctly returns accept: true for alert dialogs:
return this.createResolvedResponse(payload.dialogType === "alert", "");However, in cleanupClient, all pending dialogs — regardless of type — are resolved with accept: false:
entry.resolve(this.createResolvedResponse(false, ""));For alert, this is functionally harmless today (the preload ignores the return value), but it creates an inconsistency. The cleanup should mirror the handling in handleProtocolRequest by resolving alert dialogs with accept: true:
| entry.resolve(this.createResolvedResponse(false, "")); | |
| entry.resolve(this.createResolvedResponse(entry.state.type === "alert", "")); |
| return ( | ||
| <> | ||
| {dialogs.map((dialog) => { | ||
| const tab = tabsById.get(dialog.tabId); | ||
| const originLabel = getDialogOriginLabel(tab?.url ?? ""); | ||
|
|
||
| return ( | ||
| <TabOverlayPortal key={dialog.id} tabId={dialog.tabId} autoFocus className="pointer-events-auto"> | ||
| <JavaScriptDialogCard dialog={dialog} originLabel={originLabel} onRespond={handleRespond} /> | ||
| </TabOverlayPortal> | ||
| ); | ||
| })} | ||
| </> |
There was a problem hiding this comment.
JavaScriptDialogCard declares an exit transition on its motion.div (opacity: 0, y: 6, scale: 0.98), but Framer Motion only plays exit animations when the animated element is a direct child of AnimatePresence. Without this wrapper, dismissed dialogs vanish instantly instead of animating out.
Wrap the dialogs.map() block in AnimatePresence (imported from motion/react) to enable the exit transition.
Co-authored-by: Evan <iamEvanYT@users.noreply.github.com>
Co-authored-by: Evan <iamEvanYT@users.noreply.github.com>
Implement custom, tab-scoped JavaScript dialogs (
alert,confirm,prompt) to replace inconsistent, window-blocking native dialogs.The initial design for intercepting synchronous JavaScript dialogs via Chromium DevTools Protocol (CDP) proved unviable at runtime. This PR pivots to a robust solution using a preload main-world override combined with a synchronous custom protocol (
flow-dialog://) handler in the main process. This approach ensures dialogs remain synchronous from the page's perspective while allowing the main process to manage the custom overlay lifecycle and tab-specific geometry.