diff --git a/core/protocol/ideWebview.ts b/core/protocol/ideWebview.ts index e3c2e946c8..0a7dc19a74 100644 --- a/core/protocol/ideWebview.ts +++ b/core/protocol/ideWebview.ts @@ -1,4 +1,5 @@ import { AiderState } from "../../extensions/vscode/src/integrations/aider/types/aiderTypes.js"; +import { ToolType } from "../../extensions/vscode/src/util/integrationUtils.js"; import type { RangeInFileWithContents } from "../commands/util.js"; import type { ContextSubmenuItem } from "../index.js"; import { ToIdeFromWebviewOrCoreProtocol } from "./ide.js"; @@ -56,12 +57,13 @@ export type ToIdeFromWebviewProtocol = ToIdeFromWebviewOrCoreProtocol & { completeWelcome: [undefined, void]; openInventory: [undefined, void]; getUrlTitle: [string, string]; + pearAIinstallation: [{tools: ToolType[], installExtensions: boolean}, void]; }; export type ToWebviewFromIdeProtocol = ToWebviewFromIdeOrCoreProtocol & { setInactive: [undefined, void]; setActiveFilePath: [string | undefined, void]; - resetInteractiveContinueTutorial: [undefined, void]; + restFirstLaunchInGUI: [undefined, void]; showInteractiveContinueTutorial: [undefined, void]; submitMessage: [{ message: any }, void]; // any -> JSONContent from TipTap updateSubmenuItems: [ diff --git a/extensions/vscode/src/commands.ts b/extensions/vscode/src/commands.ts index 8614004cc9..f4ab103ba8 100644 --- a/extensions/vscode/src/commands.ts +++ b/extensions/vscode/src/commands.ts @@ -252,11 +252,12 @@ const commandsMap: ( await importUserSettingsFromVSCode(); }, "pearai.welcome.markNewOnboardingComplete": async () => { - // vscode.window.showInformationMessage("Marking onboarding complete."); await extensionContext.globalState.update(FIRST_LAUNCH_KEY, true); + await vscode.commands.executeCommand('pearai.unlockOverlay'); + await vscode.commands.executeCommand('pearai.hideOverlay'); }, - "pearai.resetInteractiveContinueTutorial": async () => { - sidebar.webviewProtocol?.request("resetInteractiveContinueTutorial", undefined, [PEAR_CONTINUE_VIEW_ID]); + "pearai.restFirstLaunchInGUI": async () => { + sidebar.webviewProtocol?.request("restFirstLaunchInGUI", undefined, [PEAR_CONTINUE_VIEW_ID]); }, "pearai.showInteractiveContinueTutorial": async () => { sidebar.webviewProtocol?.request("showInteractiveContinueTutorial", undefined, [PEAR_CONTINUE_VIEW_ID]); @@ -386,7 +387,7 @@ const commandsMap: ( await vscode.commands.executeCommand("pearai.showInteractiveContinueTutorial"); }, "pearai.developer.restFirstLaunch": async () => { - vscode.commands.executeCommand("pearai.resetInteractiveContinueTutorial"); + vscode.commands.executeCommand("pearai.restFirstLaunchInGUI"); extensionContext.globalState.update(FIRST_LAUNCH_KEY, false); vscode.window.showInformationMessage("Successfully reset PearAI first launch flag, RELOAD WINDOW TO SEE WELCOME PAGE", 'Reload Window') .then(selection => { diff --git a/extensions/vscode/src/extension/VsCodeMessenger.ts b/extensions/vscode/src/extension/VsCodeMessenger.ts index 8facd20991..b98437f7cd 100644 --- a/extensions/vscode/src/extension/VsCodeMessenger.ts +++ b/extensions/vscode/src/extension/VsCodeMessenger.ts @@ -28,6 +28,7 @@ import { getExtensionUri } from "../util/vscode"; import { VsCodeWebviewProtocol } from "../webviewProtocol"; import { attemptInstallExtension, attemptUninstallExtension, isVSCodeExtensionInstalled } from "../activation/activate"; import { checkAiderInstallation } from "../integrations/aider/aiderUtil"; +import { TOOL_COMMANDS, ToolType } from "../util/integrationUtils"; /** * A shared messenger class between Core and Webview @@ -122,13 +123,6 @@ export class VsCodeMessenger { }); this.onWebview("pearWelcomeOpenFolder", (msg) => { vscode.commands.executeCommand("workbench.action.files.openFolder"); - // force close overlay if a folder is already open - if (vscode.workspace.workspaceFolders?.length) { - vscode.commands.executeCommand("pearai.unlockOverlay"); - vscode.commands.executeCommand("pearai.hideOverlay"); - // force reload to update overlay with new global state - vscode.commands.executeCommand("workbench.action.reloadWindow"); - } }); this.onWebview("pearInstallCommandLine", (msg) => { vscode.commands.executeCommand("workbench.action.installCommandLine"); @@ -168,11 +162,28 @@ export class VsCodeMessenger { this.onWebview("openInventory", (msg) => { vscode.commands.executeCommand("pearai.toggleInventoryHome"); }); - this.onWebview("completeWelcome", (msg) => { + this.onWebview("pearAIinstallation", (msg) => { + const { tools, installExtensions } = msg.data; + if (installExtensions) { + vscode.commands.executeCommand("pearai.welcome.importUserSettingsFromVSCode"); + } + + tools.forEach((tool: ToolType) => { + const toolCommand = TOOL_COMMANDS[tool]; + if (toolCommand) { + if (toolCommand.args) { + vscode.commands.executeCommand(toolCommand.command, toolCommand.args); + } else { + vscode.commands.executeCommand(toolCommand.command); + } + } else { + console.warn(`Unknown tool: ${tool}`); + } + }); + }); + this.onWebview("closePearAIOverlay", (msg) => { vscode.commands.executeCommand("pearai.unlockOverlay"); vscode.commands.executeCommand("pearai.hideOverlay"); - // force reload to update overlay with new global state - vscode.commands.executeCommand("workbench.action.reloadWindow"); }); this.onWebview("highlightElement", (msg) => { vscode.commands.executeCommand("pearai.highlightElement", msg); diff --git a/extensions/vscode/src/util/integrationUtils.ts b/extensions/vscode/src/util/integrationUtils.ts index 74fad706a2..f2ea21b8fd 100644 --- a/extensions/vscode/src/util/integrationUtils.ts +++ b/extensions/vscode/src/util/integrationUtils.ts @@ -1,7 +1,30 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import * as vscode from "vscode"; import { ContinueGUIWebviewViewProvider } from "../ContinueGUIWebviewViewProvider"; import { ToWebviewProtocol } from "core/protocol"; +export enum InstallableTool { + AIDER = "aider", + SUPERMAVEN = "supermaven" +} + +export interface ToolCommand { + command: string; + args?: any; +} + +export type ToolType = typeof InstallableTool[keyof typeof InstallableTool]; + +export const TOOL_COMMANDS: Record = { + [InstallableTool.AIDER]: { + command: "pearai.installAider" + }, + [InstallableTool.SUPERMAVEN]: { + command: "workbench.extensions.installExtension", + args: "supermaven.supermaven" + } +}; + export function getIntegrationTab(webviewName: string) { const tabs = vscode.window.tabGroups.all.flatMap((tabGroup) => tabGroup.tabs); diff --git a/gui/src/pages/gui.tsx b/gui/src/pages/gui.tsx index 35d2cf8a6b..afe174e9fe 100644 --- a/gui/src/pages/gui.tsx +++ b/gui/src/pages/gui.tsx @@ -293,8 +293,10 @@ function GUI() { [loadMostRecentChat], ); - useWebviewListener("resetInteractiveContinueTutorial", async () => { + useWebviewListener("restFirstLaunchInGUI", async () => { setLocalStorage("showTutorialCard", true); + localStorage.removeItem("onboardingSelectedTools"); + localStorage.removeItem("importUserSettingsFromVSCode"); dispatch(setShowInteractiveContinueTutorial(true)); }); diff --git a/gui/src/pages/inventory.tsx b/gui/src/pages/inventory.tsx index 3d2a4e9dec..d55dcc6ae9 100644 --- a/gui/src/pages/inventory.tsx +++ b/gui/src/pages/inventory.tsx @@ -108,7 +108,7 @@ export default function Inventory() { {component} diff --git a/gui/src/pages/welcome/FinalStep.tsx b/gui/src/pages/welcome/FinalStep.tsx index b65c1d48ce..16d86c6168 100644 --- a/gui/src/pages/welcome/FinalStep.tsx +++ b/gui/src/pages/welcome/FinalStep.tsx @@ -6,22 +6,28 @@ import { IdeMessengerContext } from "@/context/IdeMessenger"; import { FolderOpen } from "lucide-react"; import { useNavigate } from "react-router-dom"; -export default function FinalStep({ onBack }: { onBack: () => void }) { +export default function FinalStep({ onNext }: { onNext: () => void }) { const navigate = useNavigate(); + const selectedTools = JSON.parse(localStorage.getItem('onboardingSelectedTools')); + const installExtensions = localStorage.getItem('importUserSettingsFromVSCode') === 'true'; const handleOpenFolder = () => { ideMessenger.post("pearWelcomeOpenFolder", undefined); + ideMessenger.post("pearAIinstallation", {tools: selectedTools, installExtensions: installExtensions}) + ideMessenger.post("markNewOnboardingComplete", undefined); + onNext() // navigates to inventory page }; const handleClose = () => { - ideMessenger.post("completeWelcome", undefined); + ideMessenger.post("pearAIinstallation", {tools: selectedTools, installExtensions: installExtensions}); + ideMessenger.post("markNewOnboardingComplete", undefined); + onNext() // navigates to inventory page }; useEffect(() => { // unlock overlay when we get to last page ideMessenger.post("unlockOverlay", undefined); - ideMessenger.post("markNewOnboardingComplete", undefined); const handleKeyPress = (event: KeyboardEvent) => { if (event.key === 'Enter') { handleOpenFolder(); @@ -34,7 +40,6 @@ export default function FinalStep({ onBack }: { onBack: () => void }) { const ideMessenger = useContext(IdeMessengerContext); return (
-
diff --git a/gui/src/pages/welcome/SetupPage.tsx b/gui/src/pages/welcome/SetupPage.tsx index e11ce960e5..28ecc0fb9b 100644 --- a/gui/src/pages/welcome/SetupPage.tsx +++ b/gui/src/pages/welcome/SetupPage.tsx @@ -45,10 +45,11 @@ export default function SetupPage({ onNext }: { onNext: () => void }) { const allSetupSteps = [ { - icon: , - title: "Sign in", - description: "Have PearAI work for free out of the box by signing in.", - component: , + icon: , + title: "Import VSCode Extensions", + description: + "Automatically import your extensions from VSCode to feel at home.", + component: , }, { icon: , @@ -63,15 +64,12 @@ export default function SetupPage({ onNext }: { onNext: () => void }) { description: "Install recommended tools to enhance your PearAI experience.", component: , }, - // devnote: keep import extensions as last step. - // because if user has lots of extensions, it can take a while to activate them all - // which makes extension host unresponsive and it makes the UI feel more laggy. + { - icon: , - title: "Import VSCode Extensions", - description: - "Automatically import your extensions from VSCode to feel at home.", - component: , + icon: , + title: "Sign in", + description: "Have PearAI work for free out of the box by signing in.", + component: , }, ]; diff --git a/gui/src/pages/welcome/setup/ImportExtensions.tsx b/gui/src/pages/welcome/setup/ImportExtensions.tsx index 8167c1a834..8273d80b53 100644 --- a/gui/src/pages/welcome/setup/ImportExtensions.tsx +++ b/gui/src/pages/welcome/setup/ImportExtensions.tsx @@ -14,15 +14,21 @@ export default function ImportExtensions({ }: { onNext: () => void; }) { - const ideMessenger = useContext(IdeMessengerContext); const [isImporting, setIsImporting] = useState(false); const handleImport = () => { + localStorage.setItem('importUserSettingsFromVSCode', 'true'); setIsImporting(true); - ideMessenger.post("importUserSettingsFromVSCode", undefined); + onNext(); }; + const handleSkip = () => { + localStorage.setItem('importUserSettingsFromVSCode', 'false'); + onNext() + } + useEffect(() => { + setIsImporting(localStorage.getItem('importUserSettingsFromVSCode') === 'true') const handleKeyPress = (event: KeyboardEvent) => { if (event.key === 'Enter' && !isImporting) { handleImport(); @@ -51,7 +57,7 @@ export default function ImportExtensions({ {!isImporting ?
-
+
Skip
diff --git a/gui/src/pages/welcome/setup/SignIn.tsx b/gui/src/pages/welcome/setup/SignIn.tsx index d776388348..13b0ecb6cb 100644 --- a/gui/src/pages/welcome/setup/SignIn.tsx +++ b/gui/src/pages/welcome/setup/SignIn.tsx @@ -39,7 +39,6 @@ export default function SignIn({ } else if ((event.metaKey || event.ctrlKey) && event.key === 'ArrowRight') { // Ctrl/Cmd + ArrowRight for Skip event.preventDefault(); - ideMessenger.post("markNewOnboardingComplete", undefined); onNext(); } }; @@ -64,7 +63,6 @@ export default function SignIn({
{ - ideMessenger.post("markNewOnboardingComplete", undefined); onNext(); }} className="flex items-center gap-2 cursor-pointer" diff --git a/gui/src/pages/welcome/welcomeGui.tsx b/gui/src/pages/welcome/welcomeGui.tsx index b6c153841b..724914dd4f 100644 --- a/gui/src/pages/welcome/welcomeGui.tsx +++ b/gui/src/pages/welcome/welcomeGui.tsx @@ -4,6 +4,7 @@ import FinalStep from "./FinalStep"; import SetupPage from "./SetupPage"; import { IdeMessengerContext } from "@/context/IdeMessenger"; import { WelcomeHeader } from "./WelcomeHeader"; +import InventoryPage from "@/inventory/pages/InventoryPage"; export default function Welcome() { const ideMessenger = useContext(IdeMessengerContext); @@ -44,8 +45,7 @@ export default function Welcome() { }, [ideMessenger]); // Dependency array ensures this runs once when the component mounts const handleNextStep = () => { - setStep((prevStep) => Math.min(prevStep + 1, 2)); - console.dir(`step: ${step}`); + setStep((prevStep) => Math.min(prevStep + 1, 3)); }; const handleBackStep = () => { @@ -59,7 +59,9 @@ export default function Welcome() { case 1: return ; case 2: - return ; + return ; + case 3: + return ; default: return null; }