From d16559a60784f0e3499828edd7aeebb445b382a9 Mon Sep 17 00:00:00 2001 From: Len Huang Date: Sun, 26 Jan 2025 22:51:56 -0500 Subject: [PATCH 1/9] copy over what i have from other branch but without less formatting changes --- .prettierignore | 4 +- core/protocol/ide.ts | 4 +- core/protocol/ideWebview.ts | 31 ++-- extensions/vscode/src/commands.ts | 19 +- extensions/vscode/src/copySettings.ts | 42 +++-- .../vscode/src/extension/VsCodeMessenger.ts | 39 ++-- gui/src/pages/welcome/FinalStep.tsx | 7 +- .../pages/welcome/setup/ImportExtensions.tsx | 167 +++++++++++------- 8 files changed, 180 insertions(+), 133 deletions(-) diff --git a/.prettierignore b/.prettierignore index dc7f7a91bf..29240fa592 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,5 @@ extensions/vscode/continue_rc_schema.json **/.pearaiignore -CHANGELOG.md \ No newline at end of file +CHANGELOG.md +# *.tsx +*.ts \ No newline at end of file diff --git a/core/protocol/ide.ts b/core/protocol/ide.ts index 2616a8fdc5..dc9121a4a0 100644 --- a/core/protocol/ide.ts +++ b/core/protocol/ide.ts @@ -7,11 +7,11 @@ import type { IdeSettings, IndexTag, Location, + PearAuth, Problem, Range, RangeInFile, Thread, - PearAuth, } from "../index.js"; export type ToIdeFromWebviewOrCoreProtocol = { @@ -91,7 +91,7 @@ export type ToIdeFromWebviewOrCoreProtocol = { // new welcome page markNewOnboardingComplete: [undefined, void]; - importUserSettingsFromVSCode: [undefined, void]; + importUserSettingsFromVSCode: [undefined, boolean]; pearWelcomeOpenFolder: [undefined, void]; pearInstallCommandLine: [undefined, void]; installVscodeExtension: [{ extensionId: string }, void]; diff --git a/core/protocol/ideWebview.ts b/core/protocol/ideWebview.ts index 54600f5a9a..5e06e51af5 100644 --- a/core/protocol/ideWebview.ts +++ b/core/protocol/ideWebview.ts @@ -1,5 +1,9 @@ import { AiderState } from "../../extensions/vscode/src/integrations/aider/types/aiderTypes.js"; -import { ToolType, Memory, MemoryChange } from "../../extensions/vscode/src/util/integrationUtils.js"; +import { + Memory, + MemoryChange, + 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"; @@ -19,17 +23,17 @@ export type ToIdeFromWebviewProtocol = ToIdeFromWebviewOrCoreProtocol & { openUrl: [string, void]; applyToCurrentFile: [{ text: string }, void]; applyWithRelaceHorizontal: [{ contentToApply: string }, void]; - acceptRelaceDiff: [{ originalFileUri: string, diffFileUri: string }, void]; - rejectRelaceDiff: [{ originalFileUri: string, diffFileUri: string }, void]; - createFile: [{ path: string}, void]; + acceptRelaceDiff: [{ originalFileUri: string; diffFileUri: string }, void]; + rejectRelaceDiff: [{ originalFileUri: string; diffFileUri: string }, void]; + createFile: [{ path: string }, void]; showTutorial: [undefined, void]; showFile: [{ filepath: string }, void]; openConfigJson: [undefined, void]; - highlightElement: [{elementSelectors: string[]}, void]; - unhighlightElement: [{elementSelectors: string[]}, void]; + highlightElement: [{ elementSelectors: string[] }, void]; + unhighlightElement: [{ elementSelectors: string[] }, void]; perplexityMode: [undefined, void]; - addPerplexityContext: [{text: string, language: string}, void] - addPerplexityContextinChat: [{ text: string, language: string }, void]; + addPerplexityContext: [{ text: string; language: string }, void]; + addPerplexityContextinChat: [{ text: string; language: string }, void]; aiderMode: [undefined, void]; aiderCtrlC: [undefined, void]; sendAiderProcessStateToGUI: [undefined, void]; @@ -60,7 +64,8 @@ export type ToIdeFromWebviewProtocol = ToIdeFromWebviewOrCoreProtocol & { completeWelcome: [undefined, void]; openInventoryHome: [undefined, void]; getUrlTitle: [string, string]; - pearAIinstallation: [{tools: ToolType[], installExtensions: boolean}, void]; + pearAIinstallation: [{ tools: ToolType[] }, void]; + importUserSettingsFromVSCode: [undefined, boolean]; "mem0/getMemories": [undefined, Memory[]]; "mem0/updateMemories": [{ changes: MemoryChange[] }, boolean]; }; @@ -93,7 +98,7 @@ export type ToWebviewFromIdeProtocol = ToWebviewFromIdeOrCoreProtocol & { viewHistory: [undefined, void]; newSession: [undefined, void]; quickEdit: [undefined, void]; - acceptedOrRejectedDiff: [undefined, void] + acceptedOrRejectedDiff: [undefined, void]; setTheme: [{ theme: any }, void]; setThemeType: [{ themeType: string }, void]; setColors: [{ [key: string]: string }, void]; @@ -102,8 +107,8 @@ export type ToWebviewFromIdeProtocol = ToWebviewFromIdeOrCoreProtocol & { setupLocalModel: [undefined, void]; incrementFtc: [undefined, void]; openOnboarding: [undefined, void]; - addPerplexityContext: [{text: string, language: string}, void] - addPerplexityContextinChat: [{ text: string, language: string }, void]; + addPerplexityContext: [{ text: string; language: string }, void]; + addPerplexityContextinChat: [{ text: string; language: string }, void]; navigateToCreator: [undefined, void]; navigateToSearch: [undefined, void]; navigateToMem0: [undefined, void]; @@ -112,5 +117,5 @@ export type ToWebviewFromIdeProtocol = ToWebviewFromIdeOrCoreProtocol & { navigateToInventoryHome: [undefined, void]; getCurrentTab: [undefined, string]; setAiderProcessStateInGUI: [AiderState, void]; - setRelaceDiffState: [{diffVisible: boolean}, void]; + setRelaceDiffState: [{ diffVisible: boolean }, void]; }; diff --git a/extensions/vscode/src/commands.ts b/extensions/vscode/src/commands.ts index d1d7051d72..a3e773b95f 100644 --- a/extensions/vscode/src/commands.ts +++ b/extensions/vscode/src/commands.ts @@ -22,20 +22,17 @@ import { quickPickStatusText, setupStatusBar, } from "./autocomplete/statusBar"; -import { ContinueGUIWebviewViewProvider, PEAR_OVERLAY_VIEW_ID } from "./ContinueGUIWebviewViewProvider"; +import { ContinueGUIWebviewViewProvider, PEAR_CONTINUE_VIEW_ID, PEAR_OVERLAY_VIEW_ID } from "./ContinueGUIWebviewViewProvider"; +import { FIRST_LAUNCH_KEY, importUserSettingsFromVSCode, isFirstLaunch } from "./copySettings"; import { DiffManager } from "./diff/horizontal"; import { VerticalPerLineDiffManager } from "./diff/verticalPerLine/manager"; +import { aiderCtrlC, aiderResetSession, installAider, sendAiderProcessStateToGUI, uninstallAider } from './integrations/aider/aiderUtil'; +import { AiderState } from "./integrations/aider/types/aiderTypes"; import { QuickEdit, QuickEditShowParams } from "./quickEdit/QuickEditQuickPick"; import { Battery } from "./util/battery"; -import type { VsCodeWebviewProtocol } from "./webviewProtocol"; -import { getExtensionUri } from "./util/vscode"; -import { aiderCtrlC, aiderResetSession, openAiderPanel, sendAiderProcessStateToGUI, installAider, uninstallAider } from './integrations/aider/aiderUtil'; -import { handlePerplexityMode } from "./integrations/perplexity/perplexity"; -import { PEAR_CONTINUE_VIEW_ID } from "./ContinueGUIWebviewViewProvider"; import { handleIntegrationShortcutKey } from "./util/integrationUtils"; -import { FIRST_LAUNCH_KEY, importUserSettingsFromVSCode, isFirstLaunch } from "./copySettings"; -import { attemptInstallExtension } from "./activation/activate"; -import { AiderState } from "./integrations/aider/types/aiderTypes"; +import { getExtensionUri } from "./util/vscode"; +import type { VsCodeWebviewProtocol } from "./webviewProtocol"; let fullScreenPanel: vscode.WebviewPanel | undefined; @@ -260,9 +257,9 @@ const commandsMap: ( if (!isFirstLaunch(extensionContext)) { vscode.window.showInformationMessage("Welcome back! User settings import is skipped as this is not the first launch."); console.dir("Extension launch detected as a subsequent launch. Skipping user settings import."); - return; + return true; } - await importUserSettingsFromVSCode(); + return await importUserSettingsFromVSCode(); }, "pearai.welcome.markNewOnboardingComplete": async () => { await extensionContext.globalState.update(FIRST_LAUNCH_KEY, true); diff --git a/extensions/vscode/src/copySettings.ts b/extensions/vscode/src/copySettings.ts index 8d25cf8a47..82e93698ad 100644 --- a/extensions/vscode/src/copySettings.ts +++ b/extensions/vscode/src/copySettings.ts @@ -1,7 +1,7 @@ -import * as vscode from "vscode"; import * as fs from 'fs'; -import * as path from 'path'; import * as os from 'os'; +import * as path from 'path'; +import * as vscode from "vscode"; export const FIRST_LAUNCH_KEY = 'pearai.firstLaunch'; const pearAISettingsDir = path.join(os.homedir(), '.pearai'); @@ -46,11 +46,11 @@ async function copyVSCodeSettingsToPearAIDir() { await fs.promises.mkdir(pearAIDevExtensionsDir, { recursive: true }); const itemsToCopy = ['settings.json', 'keybindings.json', 'snippets', 'sync', 'globalStorage/state.vscdb', 'globalStorage/state.vscdb.backup']; - + for (const item of itemsToCopy) { const source = path.join(vscodeSettingsDir, item); const destination = path.join(pearAIDevSettingsDir, item); - + try { if (await fs.promises.access(source).then(() => true).catch(() => false)) { const stats = await fs.promises.lstat(source); @@ -67,7 +67,7 @@ async function copyVSCodeSettingsToPearAIDir() { const baseExclusions = new Set([ 'pearai.pearai', - 'ms-python.vscode-pylance', + 'ms-python.vscode-pylance', 'ms-python.python', 'codeium', 'github.copilot', @@ -81,7 +81,7 @@ async function copyVSCodeSettingsToPearAIDir() { baseExclusions.add('ms-vscode-remote.remote-ssh'); baseExclusions.add('ms-vscode-remote.remote-ssh-edit'); } - + // Add platform specific exclusions if (process.platform === 'darwin' && process.arch === 'x64') { baseExclusions.add('ms-python.vscode-pylance'); @@ -96,7 +96,7 @@ async function copyVSCodeSettingsToPearAIDir() { // Add Linux specific exclusions // if (process.platform === 'linux') { // } - + await copyDirectoryRecursiveSync(vscodeExtensionsDir, pearAIDevExtensionsDir, Array.from(baseExclusions)); } @@ -113,7 +113,7 @@ function getVSCodeSettingsDir() { async function copyDirectoryRecursiveSync(source: string, destination: string, exclusions: string[] = []) { await fs.promises.mkdir(destination, { recursive: true }); - + const items = await fs.promises.readdir(source); for (const item of items) { const sourcePath = path.join(source, item); @@ -121,7 +121,7 @@ async function copyDirectoryRecursiveSync(source: string, destination: string, e const shouldExclude = exclusions.some(exclusion => sourcePath.toLowerCase().includes(exclusion.toLowerCase()) - + ); if (!shouldExclude) { @@ -138,27 +138,25 @@ async function copyDirectoryRecursiveSync(source: string, destination: string, e export async function importUserSettingsFromVSCode() { try { - await new Promise(resolve => setTimeout(resolve, 3000)); - - vscode.window.showInformationMessage('Copying your current VSCode settings and extensions over to PearAI!'); - await copyVSCodeSettingsToPearAIDir(); - - vscode.window.showInformationMessage( - 'Your VSCode settings and extensions have been transferred over to PearAI! You may need to restart your editor for the changes to take effect.', - 'Ok' - ); - } catch (error) { + await Promise.all([ + new Promise((resolve) => setTimeout(resolve, 1000)), // Take at least one second + copyVSCodeSettingsToPearAIDir(), + ]); + // throw new Error("Test error message"); + return true; + } catch (error) { vscode.window.showErrorMessage(`Failed to copy settings: ${error}`); + return false; + } } -} export async function markCreatorOnboardingCompleteFileBased() { try { await new Promise(resolve => setTimeout(resolve, 3000)); - + const flagFile = firstPearAICreatorLaunchFlag; const productName = 'PearAI Creator'; - + const exists = await fs.promises.access(flagFile).then(() => true).catch(() => false); if (!exists) { await fs.promises.writeFile(flagFile, `This is the first launch flag file for ${productName}`); diff --git a/extensions/vscode/src/extension/VsCodeMessenger.ts b/extensions/vscode/src/extension/VsCodeMessenger.ts index ca02fe80ac..857a068965 100644 --- a/extensions/vscode/src/extension/VsCodeMessenger.ts +++ b/extensions/vscode/src/extension/VsCodeMessenger.ts @@ -1,6 +1,7 @@ // Note: This file has been modified significantly from its original contents. New commands have been added, and there has been renaming from Continue to PearAI. pearai-submodule is a fork of Continue (https://github.com/continuedev/continue). import { ConfigHandler } from "core/config/ConfigHandler"; +import PearAIServer from "core/llm/llms/PearAIServer"; import { FromCoreProtocol, FromWebviewProtocol, @@ -18,22 +19,19 @@ import { getConfigJsonPath } from "core/util/paths"; import * as fs from "node:fs"; import * as path from "node:path"; import * as vscode from "vscode"; +import { attemptInstallExtension, attemptUninstallExtension, isVSCodeExtensionInstalled } from "../activation/activate"; import { VerticalPerLineDiffManager } from "../diff/verticalPerLine/manager"; import { VsCodeIde } from "../ideProtocol"; +import { checkAiderInstallation } from "../integrations/aider/aiderUtil"; +import { getMem0Memories, updateMem0Memories } from "../integrations/mem0/mem0Service"; +import { getFastApplyChangesWithRelace } from "../integrations/relace/relace"; import { getControlPlaneSessionInfo, WorkOsAuthProvider, } from "../stubs/WorkOsAuthProvider"; +import { extractCodeFromMarkdown, TOOL_COMMANDS, ToolType } from "../util/integrationUtils"; import { getExtensionUri } from "../util/vscode"; import { VsCodeWebviewProtocol } from "../webviewProtocol"; -import { attemptInstallExtension, attemptUninstallExtension, isVSCodeExtensionInstalled } from "../activation/activate"; -import { checkAiderInstallation } from "../integrations/aider/aiderUtil"; -import { getMem0Memories, updateMem0Memories } from "../integrations/mem0/mem0Service"; -import { TOOL_COMMANDS, ToolType, extractCodeFromMarkdown } from "../util/integrationUtils"; -import PearAIServer from "core/llm/llms/PearAIServer"; -import { getFastApplyChangesWithRelace } from "../integrations/relace/relace"; -import { RelaceDiffManager } from "../integrations/relace/relaceDiffManager"; -import { getMarkdownLanguageTagForFile } from "core/util"; /** * A shared messenger class between Core and Webview @@ -106,8 +104,22 @@ export class VsCodeMessenger { this.onWebview("unlockOverlay", (msg) => { vscode.commands.executeCommand("pearai.unlockOverlay"); }); - this.onWebview("importUserSettingsFromVSCode", (msg) => { - vscode.commands.executeCommand("pearai.welcome.importUserSettingsFromVSCode"); + this.onWebview("importUserSettingsFromVSCode", async (msg) => { + try { + const val = await vscode.commands.executeCommand( + "pearai.welcome.importUserSettingsFromVSCode", + ); + console.log( + "importUserSettingsFromVSCode result:", + val, + "Type:", + typeof val, + ); + return typeof val === "boolean" ? val : false; + } catch (error) { + console.log("importUserSettingsFromVSCode rejectionReason", error); + return false; + } }); this.onWebview("installVscodeExtension", (msg) => { attemptInstallExtension(msg.data.extensionId); @@ -131,7 +143,7 @@ export class VsCodeMessenger { const memories = await getMem0Memories(PearAIServer._getRepoId()); return memories; - }); + }); this.onWebview("mem0/updateMemories", async (msg) => { const response = await updateMem0Memories(PearAIServer._getRepoId(), msg.data.changes); return response; @@ -183,10 +195,7 @@ export class VsCodeMessenger { vscode.commands.executeCommand("pearai.toggleInventoryHome"); }); this.onWebview("pearAIinstallation", (msg) => { - const { tools, installExtensions } = msg.data; - if (installExtensions) { - vscode.commands.executeCommand("pearai.welcome.importUserSettingsFromVSCode"); - } + const { tools } = msg.data; if (tools) { tools.forEach((tool: ToolType) => { const toolCommand = TOOL_COMMANDS[tool]; diff --git a/gui/src/pages/welcome/FinalStep.tsx b/gui/src/pages/welcome/FinalStep.tsx index de75e261cf..41e78ae946 100644 --- a/gui/src/pages/welcome/FinalStep.tsx +++ b/gui/src/pages/welcome/FinalStep.tsx @@ -1,19 +1,16 @@ "use client"; import { Button } from "@/components/ui/button"; -import { useContext, useEffect } from "react"; import { IdeMessengerContext } from "@/context/IdeMessenger"; import { FolderOpen } from "lucide-react"; -import { useNavigate } from "react-router-dom"; +import { useContext, useEffect } from "react"; export default function FinalStep({ onNext }: { onNext: () => void }) { - const navigate = useNavigate(); const selectedTools = JSON.parse(localStorage.getItem('onboardingSelectedTools')); - const installExtensions = localStorage.getItem('importUserSettingsFromVSCode') === 'true'; const initiateInstallations = () => { - ideMessenger.post("pearAIinstallation", {tools: selectedTools, installExtensions: installExtensions}) + ideMessenger.post("pearAIinstallation", {tools: selectedTools}); ideMessenger.post("markNewOnboardingComplete", undefined); }; diff --git a/gui/src/pages/welcome/setup/ImportExtensions.tsx b/gui/src/pages/welcome/setup/ImportExtensions.tsx index 8273d80b53..d6fe1d5c50 100644 --- a/gui/src/pages/welcome/setup/ImportExtensions.tsx +++ b/gui/src/pages/welcome/setup/ImportExtensions.tsx @@ -1,45 +1,66 @@ "use client"; import { Button } from "@/components/ui/button"; -import { ArrowLongRightIcon } from "@heroicons/react/24/outline"; -import { useContext, useState, useEffect } from "react"; import { IdeMessengerContext } from "@/context/IdeMessenger"; +import { ArrowLongRightIcon } from "@heroicons/react/24/outline"; +import { useContext, useEffect, useState } from "react"; export const getLogoPath = (assetName: string) => { return `${window.vscMediaUrl}/logos/${assetName}`; }; -export default function ImportExtensions({ - onNext, -}: { - onNext: () => void; -}) { +export default function ImportExtensions({ onNext }: { onNext: () => void }) { const [isImporting, setIsImporting] = useState(false); + const [isDone, setIsDone] = useState(false); + const [importError, setImportError] = useState(""); + const ideMessenger = useContext(IdeMessengerContext); - const handleImport = () => { - localStorage.setItem('importUserSettingsFromVSCode', 'true'); + const handleImport = async () => { + localStorage.setItem("importUserSettingsFromVSCode", "true"); setIsImporting(true); - onNext(); + setImportError(""); + const settingsLoaded = await ideMessenger.request( + "importUserSettingsFromVSCode", + undefined, + ); + if (typeof settingsLoaded === "boolean" && settingsLoaded) { + setIsDone(true); + onNext(); + } else { + console.dir("settings not loaded"); + setIsImporting(false); + localStorage.setItem("importUserSettingsFromVSCode", "false"); + setIsDone(false); // being verbose on purpose + setImportError( + "Something went wrong while importing your settings. Please skip or try again.", + ); + } }; const handleSkip = () => { - localStorage.setItem('importUserSettingsFromVSCode', 'false'); - onNext() - } + localStorage.setItem("importUserSettingsFromVSCode", "false"); + onNext(); + }; useEffect(() => { - setIsImporting(localStorage.getItem('importUserSettingsFromVSCode') === 'true') + setIsImporting( + localStorage.getItem("importUserSettingsFromVSCode") === "true", + ); const handleKeyPress = (event: KeyboardEvent) => { - if (event.key === 'Enter' && !isImporting) { + if (event.key === "Enter" && !isImporting) { handleImport(); - } else if ((event.metaKey || event.ctrlKey) && event.key === 'ArrowRight' && !isImporting) { + } else if ( + (event.metaKey || event.ctrlKey) && + event.key === "ArrowRight" && + !isImporting + ) { event.preventDefault(); onNext(); } }; - window.addEventListener('keydown', handleKeyPress); - return () => window.removeEventListener('keydown', handleKeyPress); + window.addEventListener("keydown", handleKeyPress); + return () => window.removeEventListener("keydown", handleKeyPress); }, [isImporting]); // Include isImporting in dependencies to prevent import when already in progress return ( @@ -50,61 +71,79 @@ export default function ImportExtensions({ Import your extensions
and user settings from VSCode
- VS Code + VS Code - PearAI + PearAI
- {!isImporting ? -
-
- Skip -
-
- - : + ) : (
-
Import in progress! You can leave this page
-
- Continue +
Done importing! You can leave this page
+
+ Continue
- } + )}
From e5d829cc146e074a80d5e30332992f845a2b231c Mon Sep 17 00:00:00 2001 From: Len Huang Date: Sun, 26 Jan 2025 22:52:22 -0500 Subject: [PATCH 2/9] temp --- .prettierignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.prettierignore b/.prettierignore index 29240fa592..6aa4cbe6ba 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,5 @@ extensions/vscode/continue_rc_schema.json **/.pearaiignore CHANGELOG.md -# *.tsx -*.ts \ No newline at end of file +# *.tsx # temporary for local dev, do not merge with this +# *.ts # temporary for local dev, do not merge with this \ No newline at end of file From 3e98ca7cc785759329c86571b60849fdefbec344 Mon Sep 17 00:00:00 2001 From: Len Huang Date: Sun, 26 Jan 2025 23:09:43 -0500 Subject: [PATCH 3/9] undo some formatting changes --- .prettierignore | 6 +++-- .../pages/welcome/setup/ImportExtensions.tsx | 27 +++++-------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/.prettierignore b/.prettierignore index 6aa4cbe6ba..4cc60fc4ba 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,7 @@ extensions/vscode/continue_rc_schema.json **/.pearaiignore CHANGELOG.md -# *.tsx # temporary for local dev, do not merge with this -# *.ts # temporary for local dev, do not merge with this \ No newline at end of file + +# temporary for local dev, do not merge with this +*.tsx +*.ts \ No newline at end of file diff --git a/gui/src/pages/welcome/setup/ImportExtensions.tsx b/gui/src/pages/welcome/setup/ImportExtensions.tsx index d6fe1d5c50..ac35271489 100644 --- a/gui/src/pages/welcome/setup/ImportExtensions.tsx +++ b/gui/src/pages/welcome/setup/ImportExtensions.tsx @@ -59,8 +59,8 @@ export default function ImportExtensions({ onNext }: { onNext: () => void }) { } }; - window.addEventListener("keydown", handleKeyPress); - return () => window.removeEventListener("keydown", handleKeyPress); + window.addEventListener('keydown', handleKeyPress); + return () => window.removeEventListener('keydown', handleKeyPress); }, [isImporting]); // Include isImporting in dependencies to prevent import when already in progress return ( @@ -71,17 +71,9 @@ export default function ImportExtensions({ onNext }: { onNext: () => void }) { Import your extensions
and user settings from VSCode
- VS Code - - PearAI + VS Code + + PearAI
{!isDone ? ( @@ -89,10 +81,7 @@ export default function ImportExtensions({ onNext }: { onNext: () => void }) { {importError ?

{importError}

: null}
{/* TODO: hide skip button for the first five seconds or something and then show */} -
+
Skip