-
Notifications
You must be signed in to change notification settings - Fork 612
[Frontend feat] Workflow creation unification #4274
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e2218ff
5a41db1
4a23c23
94a3c5e
854a5fe
5da102c
0a55ae8
e240610
214be97
b4a2b19
6d8c4b8
5255817
164724b
7fc76ca
e1581d9
1e8c411
8d34a15
5ae8827
4c263b2
7c376d7
4856228
4aef30c
f1855bb
a5a5ecb
17b44d9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,6 +18,7 @@ import { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parseEvaluatorKeyFromUri, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| evaluatorTemplatesMapAtom, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workflowMolecule, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| discardLocalServerDataAtom, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "@agenta/entities/workflow" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import {EntityPicker} from "@agenta/entity-ui" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import {PlaygroundConfigSection} from "@agenta/entity-ui/drill-in" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -36,7 +37,9 @@ import { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import {type PlaygroundUIProviders} from "@agenta/playground-ui" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DrawerProvidersProvider, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isCreateContext, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workflowRevisionDrawerAtom, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workflowRevisionDrawerContextAtom, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| closeWorkflowRevisionDrawerAtom, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workflowRevisionDrawerCallbackAtom, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workflowRevisionDrawerEntityIdAtom, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -51,6 +54,7 @@ import {Rocket} from "@phosphor-icons/react" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import {Button, Typography, message} from "antd" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import {getDefaultStore, useAtom, useAtomValue, useSetAtom} from "jotai" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import dynamic from "next/dynamic" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import {useRouter} from "next/router" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import OSSdrillInUIProvider from "@/oss/components/DrillInView/OSSdrillInUIProvider" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import SimpleSharedEditor from "@/oss/components/EditorViews/SimpleSharedEditor" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -62,12 +66,14 @@ import { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "@/oss/components/Evaluators/components/ConfigureEvaluator/atoms" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import EvaluatorPlaygroundHeader from "@/oss/components/Evaluators/components/ConfigureEvaluator/EvaluatorPlaygroundHeader" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import {clearEvaluatorWorkflowCache} from "@/oss/components/Evaluators/store/evaluatorsPaginatedStore" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import {invalidateAppManagementWorkflowQueries} from "@/oss/components/pages/app-management/store" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import CommitVariantChangesButton from "@/oss/components/Playground/Components/Modals/CommitVariantChangesModal/assets/CommitVariantChangesButton" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import DeployVariantButton from "@/oss/components/Playground/Components/Modals/DeployVariantModal/assets/DeployVariantButton" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import PlaygroundTestcaseEditor from "@/oss/components/Playground/Components/PlaygroundTestcaseEditor" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import {OSSPlaygroundShell} from "@/oss/components/Playground/OSSPlaygroundShell" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import SharedGenerationResultUtils from "@/oss/components/SharedGenerationResultUtils" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import {usePlaygroundNavigation} from "@/oss/hooks/usePlaygroundNavigation" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import useURL from "@/oss/hooks/useURL" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import {useQueryParamState} from "@/oss/state/appState" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const PlaygroundMainView = dynamic( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -397,10 +403,19 @@ const DrawerPlayground = memo(({entityId}: {entityId: string}) => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ================================================================ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // COMMIT CALLBACK (evaluator create mode) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // COMMIT CALLBACK (evaluator + app create modes) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Fires on commit-success inside the drawer for: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // - evaluator-create: closes drawer, callback receives new revision ID | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // - evaluator-view: updates drawer entityId to the newly committed revision | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // - app-create: closes drawer FIRST (sync atom resets), then callback | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // receives {newAppId, newRevisionId} so the dropdown | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // handler can router.push to /apps/<id>/playground. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Order: close → navigate (avoids drawer flicker on | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // destination page during Next.js async transition). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ================================================================ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const useEvaluatorCommitCallback = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const useDrawerCreateCommitCallback = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const {context} = useAtomValue(workflowRevisionDrawerAtom) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const drawerCallback = useAtomValue(workflowRevisionDrawerCallbackAtom) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const drawerCallbackRef = useRef(drawerCallback) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -410,21 +425,65 @@ const useEvaluatorCommitCallback = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const closeDrawerRef = useRef(closeDrawer) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| closeDrawerRef.current = closeDrawer | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const router = useRouter() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const routerRef = useRef(router) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| routerRef.current = router | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const {baseAppURL} = useURL() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const baseAppURLRef = useRef(baseAppURL) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| baseAppURLRef.current = baseAppURL | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isEvaluator = context === "evaluator-create" || context === "evaluator-view" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isEvaluatorCreate = context === "evaluator-create" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isAppCreate = context === "app-create" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!isEvaluator) return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!isEvaluator && !isAppCreate) return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const previousOnNewRevision = getWorkflowCommitCallbacks().onNewRevision | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| registerWorkflowCommitCallbacks({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onNewRevision: async (result, params) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| clearEvaluatorWorkflowCache() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isEvaluator) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| clearEvaluatorWorkflowCache() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await previousOnNewRevision?.(result, params) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isEvaluatorCreate) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| drawerCallbackRef.current?.(result.newRevisionId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| drawerCallbackRef.current?.({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| configId: result.newRevisionId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| newRevisionId: result.newRevisionId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| closeDrawerRef.current() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (isAppCreate) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const newWorkflow = result.workflow as | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | {workflow_id?: string; id?: string} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | undefined | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const newAppId = newWorkflow?.workflow_id ?? newWorkflow?.id ?? undefined | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const newRevisionId = result.newRevisionId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Refresh the apps-page paginated table + count caches so | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // the new app shows up immediately on /apps when the user | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // navigates back. The shared workflow-list invalidation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // (commit.ts:590) doesn't cover the app-management | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // paginated store. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| void invalidateAppManagementWorkflowQueries() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Fire the user callback first so any analytics/hooks | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // see the result. Then handle navigation + close inside | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // the wrapper itself — owning the routing here keeps the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // contract simple for callers and avoids brittleness from | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // a callback closure capturing stale router references. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| drawerCallbackRef.current?.({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| newAppId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| newRevisionId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (newAppId && newRevisionId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| routerRef.current.push( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `${baseAppURLRef.current}/${newAppId}/playground?revisions=${newRevisionId}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| closeDrawerRef.current() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // In evaluator-view mode, the selection change callback | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -437,9 +496,11 @@ const useEvaluatorCommitCallback = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message.success( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isEvaluatorCreate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? "Evaluator created successfully" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : "Evaluator committed successfully", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isAppCreate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? "App created successfully" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : isEvaluatorCreate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? "Evaluator created successfully" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : "Evaluator committed successfully", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
498
to
504
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Double success toast on app-create commit: both commit callback and EntityCommitModal show 'App created successfully' When an ephemeral app is committed, the flow is: Prompt for agentsWas this helpful? React with 👍 or 👎 to provide feedback. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -449,19 +510,105 @@ const useEvaluatorCommitCallback = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onNewRevision: previousOnNewRevision, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [isEvaluator, isEvaluatorCreate]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [isEvaluator, isEvaluatorCreate, isAppCreate]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ================================================================ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // MAIN WRAPPER | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ================================================================ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ================================================================ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // CROSS-CONTEXT CLEANUP — release local-* on close (idle, no commit) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Wires `closeWorkflowRevisionDrawerAtom` to also dispatch | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // `discardLocalServerDataAtom` for any local-* entity. Applies to all | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // drawer-create contexts (app-create, evaluator-create, trace-replay). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Commit-in-flight gate: if a commit just succeeded, the close was | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // triggered BY the commit handler (above), and the entity has already | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // been promoted to a real ID. Releasing the local-* entry then is | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // safe — the new real entity is in a different atom family. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If a commit is in-flight at the moment of close (e.g., user clicks | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // the X mid-commit), the existing close-handler runs synchronously | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // before the commit settles. Acceptable for v1: the commit will still | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // complete on the server and the user can find the new app in the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // list. The orphan local-* would be cleared by the close, but the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // commit's discardWorkflowDraftAtom call already clears the draft | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // layer; the local server data is dead either way. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ================================================================ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const useDrawerCloseCleanup = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isOpen = useAtomValue(workflowRevisionDrawerOpenAtom) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const entityIdRef = useRef<string | null>(null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const entityId = useAtomValue(workflowRevisionDrawerEntityIdAtom) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // `closeWorkflowRevisionDrawerAtom` resets `isOpen` and `entityId` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // atomically in one Jotai write. React batches both updates into the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // same render, so a naive `entityIdRef.current = entityId` during | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // render would clobber the ref with `null` BEFORE the cleanup effect | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // fires. Only update the ref while the drawer is open, so we keep | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // the last truthy ID around to discard on close. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isOpen && entityId && entityIdRef.current !== entityId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| entityIdRef.current = entityId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const discard = useSetAtom(discardLocalServerDataAtom) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const discardRef = useRef(discard) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| discardRef.current = discard | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const prevOpenRef = useRef(isOpen) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (prevOpenRef.current && !isOpen) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Drawer just closed — release the entity that was open. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Read from the ref captured BEFORE the close (since close | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // resets entityId to null in the same render). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const id = entityIdRef.current | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (id) discardRef.current(id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+543
to
+567
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 useDrawerCloseCleanup never discards local- entity data because entityIdRef is clobbered to null before effect fires* When
Suggested change
Was this helpful? React with 👍 or 👎 to provide feedback. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| entityIdRef.current = null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| prevOpenRef.current = isOpen | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [isOpen]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ================================================================ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // REFRESH WARNING — beforeunload guard for unsaved drawer edits | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Fires the standard browser "you have unsaved changes" warning when | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // the user tries to refresh / close tab while a drawer-create context | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // is open AND the entity has unsaved edits. Cross-context: covers | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // app-create, evaluator-create, trace-replay. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ================================================================ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const useUnsavedDrawerWarning = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isOpen = useAtomValue(workflowRevisionDrawerOpenAtom) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const context = useAtomValue(workflowRevisionDrawerContextAtom) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const entityId = useAtomValue(workflowRevisionDrawerEntityIdAtom) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isDirty = useAtomValue( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useMemo(() => workflowMolecule.atoms.isDirty(entityId ?? "__none__"), [entityId]), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!isOpen || !isCreateContext(context) || !isDirty) return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handler = (e: BeforeUnloadEvent) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| e.preventDefault() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Modern browsers ignore the message, but setting returnValue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // is required to trigger the prompt at all. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| e.returnValue = "" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| window.addEventListener("beforeunload", handler) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return () => window.removeEventListener("beforeunload", handler) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [isOpen, context, isDirty]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const WorkflowRevisionDrawerWrapper = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isOpen = useAtomValue(workflowRevisionDrawerOpenAtom) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const entityId = useAtomValue(workflowRevisionDrawerEntityIdAtom) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [, setQueryRevision] = useQueryParamState("revisionId") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEvaluatorCommitCallback() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useDrawerCreateCommitCallback() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useDrawerCloseCleanup() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useUnsavedDrawerWarning() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Clear revisionId from URL when drawer closes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const prevOpenRef = useRef(isOpen) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔴 Double router.push navigation on app-create commit: wrapper navigates AND caller's onWorkflowCreated callback navigates
In
useDrawerCreateCommitCallback, theisAppCreatebranch both invokesdrawerCallbackRef.current?.({newAppId, newRevisionId})(line 477-480) AND performs its ownrouterRef.current.push(...)(line 483-485). All callers that passonWorkflowCreated—CreateAppDropdown(CreateAppDropdown/index.tsx:115-118),AppManagement(index.tsx:75), andPromptsPage(PromptsPage.tsx:388) — also callrouter.push()to the same playground URL inside their callback. This results in tworouter.pushcalls to the same URL on every successful app creation, which can cause duplicate browser history entries, double renders, or race conditions during the Next.js page transition.Wrapper comment vs. actual behavior
The wrapper's comment on line 472-476 says "owning the routing here keeps the contract simple for callers", implying callers should NOT navigate. But every caller does navigate. Either remove the wrapper's
router.push(and let the callback own it) or remove therouter.pushfrom allonWorkflowCreatedcallbacks.Prompt for agents
Was this helpful? React with 👍 or 👎 to provide feedback.