diff --git a/skyvern-frontend/src/routes/workflows/copilot/PasteRecordedStepsHint.tsx b/skyvern-frontend/src/routes/workflows/copilot/PasteRecordedStepsHint.tsx new file mode 100644 index 000000000..47a96b834 --- /dev/null +++ b/skyvern-frontend/src/routes/workflows/copilot/PasteRecordedStepsHint.tsx @@ -0,0 +1,61 @@ +import { useState } from "react"; +import { Cross2Icon } from "@radix-ui/react-icons"; + +import { cn } from "@/util/utils"; +import { usePasteSkillHintStore } from "@/store/usePasteSkillHintStore"; + +export function PasteRecordedStepsHint({ className }: { className?: string }) { + const dismissed = usePasteSkillHintStore((s) => s.dismissed); + const dismiss = usePasteSkillHintStore((s) => s.dismiss); + const [expanded, setExpanded] = useState(false); + + if (dismissed) { + return null; + } + + return ( +
+
+ + đź’ˇ + +
+ + Already recorded this with another agent? Paste the steps into + Copilot to build it here. + {" "} + +
+ +
+ {expanded ? ( +
    +
  1. + In your agent (e.g. the Claude Chrome extension), open your recorded + workflow and copy its prompt text. +
  2. +
  3. Paste it into the Copilot chat.
  4. +
  5. Copilot turns it into a Skyvern agent you can run and edit.
  6. +
+ ) : null} +
+ ); +} diff --git a/skyvern-frontend/src/routes/workflows/copilot/WorkflowCopilotChat.tsx b/skyvern-frontend/src/routes/workflows/copilot/WorkflowCopilotChat.tsx index 5f266622a..dd77b3f73 100644 --- a/skyvern-frontend/src/routes/workflows/copilot/WorkflowCopilotChat.tsx +++ b/skyvern-frontend/src/routes/workflows/copilot/WorkflowCopilotChat.tsx @@ -21,6 +21,7 @@ import { stringify as convertToYAML } from "yaml"; import { useWorkflowHasChangesStore } from "@/store/WorkflowHasChangesStore"; import { useCopilotActionStore } from "@/store/useCopilotActionStore"; import { useCopilotHeaderStore } from "@/store/useCopilotHeaderStore"; +import { usePasteSkillHintStore } from "@/store/usePasteSkillHintStore"; import { WorkflowCreateYAMLRequest } from "@/routes/workflows/types/workflowYamlTypes"; import { WorkflowApiResponse } from "@/routes/workflows/types/workflowTypes"; import { toast } from "@/components/ui/use-toast"; @@ -509,6 +510,7 @@ export function WorkflowCopilotChat({ ); const [autoAccept, setAutoAccept] = useState(false); const [inputValue, setInputValue] = useState(""); + const dismissPasteSkillHint = usePasteSkillHintStore((s) => s.dismiss); const [isLoading, setIsLoading] = useState(false); const [queuedPrompt, setQueuedPrompt] = useState(null); const [narrative, setNarrative] = @@ -2382,10 +2384,11 @@ export function WorkflowCopilotChat({ ? "Type a message to send next…" : isWaitingForLiveBrowser ? "Type a prompt to send when ready..." - : "Message Skyvern Copilot…" + : "Message Skyvern Copilot, or paste recorded steps…" } value={inputValue} onChange={(e) => setInputValue(e.target.value)} + onPaste={() => dismissPasteSkillHint()} onKeyDown={handleKeyPress} disabled={inputDisabled} rows={1} diff --git a/skyvern-frontend/src/routes/workflows/studio/BrowserTab.tsx b/skyvern-frontend/src/routes/workflows/studio/BrowserTab.tsx index 2bce5e546..fd5bad12b 100644 --- a/skyvern-frontend/src/routes/workflows/studio/BrowserTab.tsx +++ b/skyvern-frontend/src/routes/workflows/studio/BrowserTab.tsx @@ -4,6 +4,9 @@ import { usePostHog } from "posthog-js/react"; import { StreamStatusPanel } from "@/routes/streaming/StreamDiagnostics"; +import { PasteRecordedStepsHint } from "@/routes/workflows/copilot/PasteRecordedStepsHint"; +import { useRecordingStore } from "@/store/useRecordingStore"; + import { HeroRecording } from "./runview/HeroRecording"; import { HeroScreenshot } from "./runview/HeroScreenshot"; import { RunLiveStream } from "./runview/RunLiveStream"; @@ -28,6 +31,7 @@ export function BrowserTab() { liveSurface, } = useBrowserPaneView(); const postHog = usePostHog(); + const isRecording = useRecordingStore((s) => s.isRecording); const { workflowRun, @@ -61,7 +65,9 @@ export function BrowserTab() { const showRunStream = liveSurface === "run" && runId != null; return ( -
+
+ {!isRecording ? : null} +
{view === "live" ? ( showRunStream ? ( diff --git a/skyvern-frontend/src/store/usePasteSkillHintStore.ts b/skyvern-frontend/src/store/usePasteSkillHintStore.ts new file mode 100644 index 000000000..66172e89d --- /dev/null +++ b/skyvern-frontend/src/store/usePasteSkillHintStore.ts @@ -0,0 +1,23 @@ +import { create } from "zustand"; +import { persist, createJSONStorage } from "zustand/middleware"; + +type PasteSkillHintState = { + dismissed: boolean; + dismiss: () => void; +}; + +export const PASTE_SKILL_HINT_STORAGE_KEY = "skyvern.copilot.pasteSkillHint"; + +export const usePasteSkillHintStore = create()( + persist( + (set) => ({ + dismissed: false, + dismiss: () => set({ dismissed: true }), + }), + { + name: PASTE_SKILL_HINT_STORAGE_KEY, + storage: createJSONStorage(() => localStorage), + partialize: (state) => ({ dismissed: state.dismissed }), + }, + ), +);