mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-06 08:18:39 +00:00
Add Hugging Face to welcome screen
This commit is contained in:
parent
912d57c692
commit
1389e6232b
20 changed files with 127 additions and 23 deletions
BIN
src/assets/images/hf.png
Normal file
BIN
src/assets/images/hf.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
|
|
@ -2,6 +2,7 @@ import { useCallback, useState } from "react"
|
|||
import knuthShuffle from "knuth-shuffle-seeded"
|
||||
import { Trans } from "react-i18next"
|
||||
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
||||
import pkceChallenge from "pkce-challenge"
|
||||
|
||||
import type { ProviderSettings } from "@roo-code/types"
|
||||
|
||||
|
|
@ -9,7 +10,7 @@ import { useExtensionState } from "@src/context/ExtensionStateContext"
|
|||
import { validateApiConfiguration } from "@src/utils/validate"
|
||||
import { vscode } from "@src/utils/vscode"
|
||||
import { useAppTranslation } from "@src/i18n/TranslationContext"
|
||||
import { getRequestyAuthUrl, getOpenRouterAuthUrl } from "@src/oauth/urls"
|
||||
import { getRequestyAuthUrl, getOpenRouterAuthUrl, getHuggingFaceAuthUrl } from "@src/oauth/urls"
|
||||
|
||||
import ApiOptions from "../settings/ApiOptions"
|
||||
import { Tab, TabContent } from "../common/Tab"
|
||||
|
|
@ -47,23 +48,57 @@ const WelcomeView = () => {
|
|||
return w.IMAGES_BASE_URI || ""
|
||||
})
|
||||
|
||||
// Handle Hugging Face OAuth with PKCE
|
||||
const handleHuggingFaceOAuth = useCallback(async () => {
|
||||
try {
|
||||
// Generate PKCE challenge using the library
|
||||
const pkce = await pkceChallenge()
|
||||
const state = crypto.randomUUID() // Use built-in UUID for state
|
||||
|
||||
// Store verifier/state in extension (secrets)
|
||||
vscode.postMessage({
|
||||
type: "storeHuggingFacePkce",
|
||||
values: { verifier: pkce.code_verifier, state },
|
||||
})
|
||||
|
||||
const authUrl = getHuggingFaceAuthUrl(uriScheme, pkce.code_challenge, state)
|
||||
|
||||
// Open externally via extension
|
||||
vscode.postMessage({ type: "openExternal", url: authUrl })
|
||||
} catch (e) {
|
||||
console.error("Failed to start Hugging Face OAuth:", e)
|
||||
}
|
||||
}, [uriScheme])
|
||||
|
||||
// Handle provider click
|
||||
const handleProviderClick = useCallback(
|
||||
(provider: any) => {
|
||||
if (provider.slug === "hf") {
|
||||
// Hugging Face needs special handling for PKCE
|
||||
handleHuggingFaceOAuth()
|
||||
} else {
|
||||
// Other providers can use direct links
|
||||
vscode.postMessage({ type: "openExternal", url: provider.authUrl })
|
||||
}
|
||||
},
|
||||
[handleHuggingFaceOAuth],
|
||||
)
|
||||
|
||||
return (
|
||||
<Tab>
|
||||
<TabContent className="flex flex-col gap-4 p-6">
|
||||
<TabContent className="flex flex-col gap-2 p-6">
|
||||
<RooHero />
|
||||
<h2 className="mt-0 mb-4 text-xl text-center">{t("welcome:greeting")}</h2>
|
||||
<h2 className="mt-0 mb-0 text-xl text-center">{t("welcome:greeting")}</h2>
|
||||
|
||||
<div className="text-base text-vscode-foreground py-2 px-2 mb-4">
|
||||
<div className="text-base text-vscode-foreground py-0 px-0 mb-2">
|
||||
<p className="mb-3 leading-relaxed">
|
||||
<Trans i18nKey="welcome:introduction" />
|
||||
</p>
|
||||
<p className="mb-0 leading-relaxed">
|
||||
<p className="mb-3 leading-relaxed">
|
||||
<Trans i18nKey="welcome:chooseProvider" />
|
||||
|
||||
<Trans i18nKey="welcome:startRouter" />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<p className="text-sm font-medium mt-4 mb-3">{t("welcome:startRouter")}</p>
|
||||
|
||||
<div>
|
||||
{/* Define the providers */}
|
||||
|
|
@ -83,6 +118,12 @@ const WelcomeView = () => {
|
|||
description: t("welcome:routers.openrouter.description"),
|
||||
authUrl: getOpenRouterAuthUrl(uriScheme),
|
||||
},
|
||||
{
|
||||
slug: "hf",
|
||||
name: "Hugging Face",
|
||||
description: t("welcome:routers.huggingface.description"),
|
||||
authUrl: getHuggingFaceAuthUrl(uriScheme),
|
||||
},
|
||||
]
|
||||
|
||||
// Shuffle providers based on machine ID (will be consistent for the same machine)
|
||||
|
|
@ -91,12 +132,26 @@ const WelcomeView = () => {
|
|||
|
||||
// Render the provider cards
|
||||
return orderedProviders.map((provider, index) => (
|
||||
<a
|
||||
<div
|
||||
key={index}
|
||||
href={provider.authUrl}
|
||||
className="flex-1 border border-vscode-panel-border hover:bg-secondary rounded-md py-3 px-4 mb-2 flex flex-row gap-3 cursor-pointer transition-all no-underline text-inherit"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
handleProviderClick(provider)
|
||||
}}
|
||||
className="relative flex-1 border border-vscode-panel-border hover:bg-secondary rounded-md py-3 px-4 mb-2 flex flex-row gap-3 cursor-pointer transition-all no-underline text-inherit"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
e.preventDefault()
|
||||
handleProviderClick(provider)
|
||||
}
|
||||
}}>
|
||||
{provider.incentive && (
|
||||
<div className="absolute top-0 right-0 bg-vscode-badge-background text-vscode-badge-foreground px-1.5 py-0.5 rounded-bl rounded-tr-md text-[10px] font-medium">
|
||||
{provider.incentive}
|
||||
</div>
|
||||
)}
|
||||
<div className="w-8 h-8 flex-shrink-0">
|
||||
<img
|
||||
src={`${imagesBaseUri}/${provider.slug}.png`}
|
||||
|
|
@ -108,21 +163,16 @@ const WelcomeView = () => {
|
|||
<div className="text-sm font-medium text-vscode-foreground">
|
||||
{provider.name}
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs text-vscode-descriptionForeground">
|
||||
{provider.description}
|
||||
</div>
|
||||
{provider.incentive && (
|
||||
<div className="text-xs mt-1">{provider.incentive}</div>
|
||||
)}
|
||||
<div className="text-xs text-vscode-descriptionForeground">
|
||||
{provider.description}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
))
|
||||
})()}
|
||||
</div>
|
||||
|
||||
<p className="text-sm font-medium mt-6 mb-3">{t("welcome:startCustom")}</p>
|
||||
<p className="text-base mt-4 mb-3">{t("welcome:startCustom")}</p>
|
||||
<ApiOptions
|
||||
fromWelcomeView
|
||||
apiConfiguration={apiConfiguration || {}}
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ca/welcome.json
generated
3
webview-ui/src/i18n/locales/ca/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "Una interfície unificada per a LLMs"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "Un router per als millors models oberts"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Per fer la seva màgia, Roo necessita una clau API.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/de/welcome.json
generated
3
webview-ui/src/i18n/locales/de/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "Eine einheitliche Schnittstelle für LLMs"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "Ein Router für die besten Open-Source-Modelle"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Um seine Magie zu entfalten, benötigt Roo einen API-Schlüssel.",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "A unified interface for LLMs"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "A router for the best open models"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "To do its magic, Roo needs an API key.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/es/welcome.json
generated
3
webview-ui/src/i18n/locales/es/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "Una interfaz unificada para LLMs"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "Un router para los mejores modelos abiertos"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Para hacer su magia, Roo necesita una clave API.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/fr/welcome.json
generated
3
webview-ui/src/i18n/locales/fr/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "Une interface unifiée pour les LLMs"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "Un routeur pour les meilleurs modèles open source"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Pour faire sa magie, Roo a besoin d'une clé API.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/hi/welcome.json
generated
3
webview-ui/src/i18n/locales/hi/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "LLMs के लिए एक एकीकृत इंटरफेस"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "सर्वश्रेष्ठ ओपन मॉडल्स के लिए एक राउटर"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "अपना जादू दिखाने के लिए, Roo को एक API कुंजी की आवश्यकता है।",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/id/welcome.json
generated
3
webview-ui/src/i18n/locales/id/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "Interface terpadu untuk LLM"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "Router untuk model terbuka terbaik"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Untuk melakukan keajaibannya, Roo membutuhkan API key.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/it/welcome.json
generated
3
webview-ui/src/i18n/locales/it/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "Un'interfaccia unificata per LLMs"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "Un router per i migliori modelli open source"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Per fare la sua magia, Roo ha bisogno di una chiave API.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ja/welcome.json
generated
3
webview-ui/src/i18n/locales/ja/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "LLMsのための統一インターフェース"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "最高のオープンモデルのためのルーター"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Rooが機能するには、APIキーが必要です。",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ko/welcome.json
generated
3
webview-ui/src/i18n/locales/ko/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "LLM을 위한 통합 인터페이스"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "최고의 오픈 모델을 위한 라우터"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Roo가 작동하려면 API 키가 필요합니다.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/nl/welcome.json
generated
3
webview-ui/src/i18n/locales/nl/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "Een uniforme interface voor LLM's"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "Een router voor de beste open modellen"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Om zijn magie te doen, heeft Roo een API-sleutel nodig.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/pl/welcome.json
generated
3
webview-ui/src/i18n/locales/pl/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "Ujednolicony interfejs dla LLMs"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "Router dla najlepszych otwartych modeli"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Aby działać, Roo potrzebuje klucza API.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/pt-BR/welcome.json
generated
3
webview-ui/src/i18n/locales/pt-BR/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "Uma interface unificada para LLMs"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "Um roteador para os melhores modelos abertos"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Para fazer sua mágica, o Roo precisa de uma chave API.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ru/welcome.json
generated
3
webview-ui/src/i18n/locales/ru/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "Унифицированный интерфейс для LLM"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "Маршрутизатор для лучших открытых моделей"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Для своей магии Roo нуждается в API-ключе.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/tr/welcome.json
generated
3
webview-ui/src/i18n/locales/tr/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "LLM'ler için birleşik bir arayüz"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "En iyi açık modeller için bir yönlendirici"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Sihirini yapabilmesi için Roo'nun bir API anahtarına ihtiyacı var.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/vi/welcome.json
generated
3
webview-ui/src/i18n/locales/vi/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "Giao diện thống nhất cho các LLM"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "Bộ định tuyến cho các mô hình mở tốt nhất"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Để thực hiện phép màu của mình, Roo cần một khóa API.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/zh-CN/welcome.json
generated
3
webview-ui/src/i18n/locales/zh-CN/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "统一了大语言模型的接口"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "开源模型路由器"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Roo 需要一个 API 密钥才能发挥魔力。",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/zh-TW/welcome.json
generated
3
webview-ui/src/i18n/locales/zh-TW/welcome.json
generated
|
|
@ -10,6 +10,9 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "LLM 的統一介面"
|
||||
},
|
||||
"huggingface": {
|
||||
"description": "最佳開放模型的路由器"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "Roo 需要 API 金鑰才能發揮魔力。",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue