mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-13 23:53:25 +00:00
Compare commits
4 Commits
fix/patch-
...
feat/inter
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc84af8d14 | ||
|
|
d01eac9d0b | ||
|
|
be2422437c | ||
|
|
b2d3acde4e |
@@ -1,6 +1,7 @@
|
||||
import { ChevronDown, LogIn, LogOut, User } from 'lucide-react'
|
||||
import type { FC } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import { i18n } from '#i18n'
|
||||
import ProductLogo from '@/assets/product_logo.svg'
|
||||
import { ThemeToggle } from '@/components/elements/theme-toggle'
|
||||
import {
|
||||
@@ -68,7 +69,11 @@ export const SidebarBranding: FC<SidebarBrandingProps> = ({
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<img src={ProductLogo} alt="BrowserOS" className="size-8" />
|
||||
<img
|
||||
src={ProductLogo}
|
||||
alt={i18n.t('sidebar.branding.alt')}
|
||||
className="size-8"
|
||||
/>
|
||||
)
|
||||
|
||||
return (
|
||||
@@ -105,7 +110,9 @@ export const SidebarBranding: FC<SidebarBrandingProps> = ({
|
||||
: 'font-medium text-primary',
|
||||
)}
|
||||
>
|
||||
{isLoggedIn ? 'Personal' : 'Sign in'}
|
||||
{isLoggedIn
|
||||
? i18n.t('sidebar.branding.personal')
|
||||
: i18n.t('sidebar.branding.signIn')}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
@@ -123,14 +130,14 @@ export const SidebarBranding: FC<SidebarBrandingProps> = ({
|
||||
{displayName}
|
||||
</p>
|
||||
<p className="text-muted-foreground text-xs leading-none">
|
||||
Personal
|
||||
{i18n.t('sidebar.branding.personal')}
|
||||
</p>
|
||||
</div>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={() => navigate('/profile')}>
|
||||
<User className="mr-2 size-4" />
|
||||
Update Profile
|
||||
{i18n.t('sidebar.branding.updateProfile')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
@@ -138,13 +145,13 @@ export const SidebarBranding: FC<SidebarBrandingProps> = ({
|
||||
variant="destructive"
|
||||
>
|
||||
<LogOut className="mr-2 size-4" />
|
||||
Sign out
|
||||
{i18n.t('sidebar.branding.signOut')}
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
) : (
|
||||
<DropdownMenuItem onClick={() => navigate('/login')}>
|
||||
<LogIn className="mr-2 size-4" />
|
||||
Sign in
|
||||
{i18n.t('sidebar.branding.signIn')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from 'lucide-react'
|
||||
import type { FC } from 'react'
|
||||
import { NavLink, useLocation } from 'react-router'
|
||||
import { i18n } from '#i18n'
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
@@ -24,40 +25,44 @@ interface SidebarNavigationProps {
|
||||
}
|
||||
|
||||
type NavItem = {
|
||||
name: string
|
||||
nameKey: string
|
||||
to: string
|
||||
icon: typeof Home
|
||||
feature?: Feature
|
||||
}
|
||||
|
||||
const primaryNavItems: NavItem[] = [
|
||||
{ name: 'Home', to: '/home', icon: Home },
|
||||
{ nameKey: 'sidebar.nav.home', to: '/home', icon: Home },
|
||||
{
|
||||
name: 'Connect Apps',
|
||||
nameKey: 'sidebar.nav.connectApps',
|
||||
to: '/connect-apps',
|
||||
icon: PlugZap,
|
||||
feature: Feature.MANAGED_MCP_SUPPORT,
|
||||
},
|
||||
{ name: 'Scheduled Tasks', to: '/scheduled', icon: CalendarClock },
|
||||
{
|
||||
name: 'Skills',
|
||||
nameKey: 'sidebar.nav.scheduledTasks',
|
||||
to: '/scheduled',
|
||||
icon: CalendarClock,
|
||||
},
|
||||
{
|
||||
nameKey: 'sidebar.nav.skills',
|
||||
to: '/home/skills',
|
||||
icon: Wand2,
|
||||
feature: Feature.SKILLS_SUPPORT,
|
||||
},
|
||||
{
|
||||
name: 'Memory',
|
||||
nameKey: 'sidebar.nav.memory',
|
||||
to: '/home/memory',
|
||||
icon: Brain,
|
||||
feature: Feature.MEMORY_SUPPORT,
|
||||
},
|
||||
{
|
||||
name: 'Soul',
|
||||
nameKey: 'sidebar.nav.soul',
|
||||
to: '/home/soul',
|
||||
icon: Sparkles,
|
||||
feature: Feature.SOUL_SUPPORT,
|
||||
},
|
||||
{ name: 'Settings', to: '/settings/ai', icon: Settings },
|
||||
{ nameKey: 'sidebar.nav.settings', to: '/settings/ai', icon: Settings },
|
||||
]
|
||||
|
||||
export const SidebarNavigation: FC<SidebarNavigationProps> = ({
|
||||
@@ -97,7 +102,7 @@ export const SidebarNavigation: FC<SidebarNavigationProps> = ({
|
||||
expanded ? 'opacity-100' : 'opacity-0',
|
||||
)}
|
||||
>
|
||||
{item.name}
|
||||
{i18n.t(item.nameKey as never)}
|
||||
</span>
|
||||
</NavLink>
|
||||
)
|
||||
@@ -106,7 +111,9 @@ export const SidebarNavigation: FC<SidebarNavigationProps> = ({
|
||||
return (
|
||||
<Tooltip key={item.to}>
|
||||
<TooltipTrigger asChild>{navItem}</TooltipTrigger>
|
||||
<TooltipContent side="right">{item.name}</TooltipContent>
|
||||
<TooltipContent side="right">
|
||||
{i18n.t(item.nameKey as never)}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Info, Keyboard } from 'lucide-react'
|
||||
import type { FC } from 'react'
|
||||
import { i18n } from '#i18n'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Tooltip,
|
||||
@@ -50,7 +51,7 @@ export const SidebarUserFooter: FC<SidebarUserFooterProps> = ({
|
||||
expanded ? 'opacity-100' : 'opacity-0',
|
||||
)}
|
||||
>
|
||||
About BrowserOS
|
||||
{i18n.t('sidebar.footer.about')}
|
||||
</span>
|
||||
</a>
|
||||
)
|
||||
@@ -68,7 +69,7 @@ export const SidebarUserFooter: FC<SidebarUserFooterProps> = ({
|
||||
expanded ? 'opacity-100' : 'opacity-0',
|
||||
)}
|
||||
>
|
||||
Shortcuts
|
||||
{i18n.t('sidebar.footer.shortcuts')}
|
||||
</span>
|
||||
</Button>
|
||||
)
|
||||
@@ -81,7 +82,9 @@ export const SidebarUserFooter: FC<SidebarUserFooterProps> = ({
|
||||
) : (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>{shortcutsButton}</TooltipTrigger>
|
||||
<TooltipContent side="right">Shortcuts</TooltipContent>
|
||||
<TooltipContent side="right">
|
||||
{i18n.t('sidebar.footer.shortcuts')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
@@ -90,7 +93,9 @@ export const SidebarUserFooter: FC<SidebarUserFooterProps> = ({
|
||||
) : (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>{aboutLink}</TooltipTrigger>
|
||||
<TooltipContent side="right">About BrowserOS</TooltipContent>
|
||||
<TooltipContent side="right">
|
||||
{i18n.t('sidebar.footer.about')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Upload, X } from 'lucide-react'
|
||||
import { AnimatePresence, motion } from 'motion/react'
|
||||
import { useState } from 'react'
|
||||
import { i18n } from '#i18n'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Card,
|
||||
@@ -48,7 +49,9 @@ export const ImportDataHint = () => {
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Upload className="size-5 text-muted-foreground" />
|
||||
<CardTitle className="text-base">Import your data</CardTitle>
|
||||
<CardTitle className="text-base">
|
||||
{i18n.t('newtab.importData.title')}
|
||||
</CardTitle>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -60,7 +63,7 @@ export const ImportDataHint = () => {
|
||||
</Button>
|
||||
</div>
|
||||
<CardDescription>
|
||||
Bring bookmarks, history, and passwords from Chrome.
|
||||
{i18n.t('newtab.importData.description')}
|
||||
</CardDescription>
|
||||
<label
|
||||
htmlFor="import-dont-ask-again"
|
||||
@@ -73,11 +76,11 @@ export const ImportDataHint = () => {
|
||||
setDontAskAgain(checked === true)
|
||||
}
|
||||
/>
|
||||
Don't show this again
|
||||
{i18n.t('newtab.importData.dontShowAgain')}
|
||||
</label>
|
||||
<Button className="w-full" onClick={handleImport}>
|
||||
<Upload className="size-4" />
|
||||
Open Import Settings
|
||||
{i18n.t('newtab.importData.openSettings')}
|
||||
</Button>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import { AnimatePresence, motion } from 'motion/react'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import { i18n } from '#i18n'
|
||||
import { ChatProviderSelector } from '@/components/chat/ChatProviderSelector'
|
||||
import { AppSelector } from '@/components/elements/AppSelector'
|
||||
import {
|
||||
@@ -129,7 +130,9 @@ export const NewTab = () => {
|
||||
query: inputValue,
|
||||
selectedTabs,
|
||||
})
|
||||
const searchPlaceholder = `Ask BrowserOS or search ${providerConfig.name}...`
|
||||
const searchPlaceholder = i18n.t('newtab.search.placeholder', [
|
||||
providerConfig.name,
|
||||
])
|
||||
|
||||
const {
|
||||
isOpen,
|
||||
@@ -495,7 +498,7 @@ export const NewTab = () => {
|
||||
{selectedTab.title}
|
||||
</div>
|
||||
<div className="text-muted-foreground text-xs">
|
||||
Tab
|
||||
{i18n.t('newtab.selectedTab')}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
@@ -569,7 +572,10 @@ export const NewTab = () => {
|
||||
)}
|
||||
>
|
||||
<Folder className="h-4 w-4" />
|
||||
<span>{selectedFolder?.name || 'Add workspace'}</span>
|
||||
<span>
|
||||
{selectedFolder?.name ||
|
||||
i18n.t('newtab.addWorkspace')}
|
||||
</span>
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
</Button>
|
||||
</WorkspaceSelector>
|
||||
@@ -592,7 +598,7 @@ export const NewTab = () => {
|
||||
)}
|
||||
>
|
||||
<Layers className="h-4 w-4" />
|
||||
<span>Tabs</span>
|
||||
<span>{i18n.t('common.tabs')}</span>
|
||||
</Button>
|
||||
</TabPickerPopover>
|
||||
</div>
|
||||
@@ -602,7 +608,7 @@ export const NewTab = () => {
|
||||
<div className="ml-auto flex items-center gap-1.5">
|
||||
{connectedManagedServers.length === 0 && (
|
||||
<span className="flex items-center gap-1 font-semibold text-[var(--accent-orange)] text-sm">
|
||||
New!
|
||||
{i18n.t('newtab.appsNew')}
|
||||
</span>
|
||||
)}
|
||||
{connectedManagedServers.length === 0 ? (
|
||||
@@ -623,14 +629,13 @@ export const NewTab = () => {
|
||||
)}
|
||||
>
|
||||
<PlugZap className="h-4 w-4" />
|
||||
<span>Apps</span>
|
||||
<span>{i18n.t('common.apps')}</span>
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
</AppSelector>
|
||||
<TooltipContent side="left" className="max-w-56">
|
||||
Apps directly connected will have more accurate and
|
||||
faster responses for your queries!
|
||||
{i18n.t('newtab.appsTooltip')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
) : (
|
||||
@@ -667,7 +672,7 @@ export const NewTab = () => {
|
||||
+{connectedManagedServers.length - 4}
|
||||
</span>
|
||||
)}
|
||||
<span>Apps</span>
|
||||
<span>{i18n.t('common.apps')}</span>
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
</Button>
|
||||
</AppSelector>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { motion } from 'motion/react'
|
||||
import type { FC } from 'react'
|
||||
import { i18n } from '#i18n'
|
||||
import ProductLogoSvg from '@/assets/product_logo.svg'
|
||||
|
||||
export const NewTabBranding: FC = () => {
|
||||
@@ -15,7 +16,11 @@ export const NewTabBranding: FC = () => {
|
||||
}}
|
||||
className="flex h-20 w-20 items-center justify-center rounded-xl bg-transparent"
|
||||
>
|
||||
<img src={ProductLogoSvg} alt="BrowserOS" className="h-20 w-20" />
|
||||
<img
|
||||
src={ProductLogoSvg}
|
||||
alt={i18n.t('newtab.branding.alt')}
|
||||
className="h-20 w-20"
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ChevronRight, Lightbulb, X } from 'lucide-react'
|
||||
import { AnimatePresence, motion } from 'motion/react'
|
||||
import { type FC, useState } from 'react'
|
||||
import { i18n } from '#i18n'
|
||||
import { NEWTAB_TIP_DISMISSED_EVENT } from '@/lib/constants/analyticsEvents'
|
||||
import { track } from '@/lib/metrics/track'
|
||||
import { dismissTip, shouldShowTip, TIPS } from './tips'
|
||||
@@ -39,7 +40,7 @@ export const NewTabTip: FC = () => {
|
||||
<Lightbulb className="h-3.5 w-3.5 flex-shrink-0 text-[var(--accent-orange)]" />
|
||||
<p className="text-muted-foreground text-xs leading-relaxed">
|
||||
<span className="font-semibold text-[var(--accent-orange)]">
|
||||
Tip:
|
||||
{i18n.t('newtab.tip.label')}
|
||||
</span>{' '}
|
||||
{tip.text}
|
||||
</p>
|
||||
@@ -47,7 +48,7 @@ export const NewTabTip: FC = () => {
|
||||
type="button"
|
||||
onClick={handleNext}
|
||||
className="flex-shrink-0 rounded-sm p-0.5 text-muted-foreground/50 opacity-0 transition-all hover:text-muted-foreground group-hover:opacity-100"
|
||||
title="Next tip"
|
||||
title={i18n.t('newtab.tip.nextTip')}
|
||||
>
|
||||
<ChevronRight className="h-3 w-3" />
|
||||
</button>
|
||||
@@ -55,7 +56,7 @@ export const NewTabTip: FC = () => {
|
||||
type="button"
|
||||
onClick={handleDismiss}
|
||||
className="flex-shrink-0 rounded-sm p-0.5 text-muted-foreground/50 opacity-0 transition-all hover:text-muted-foreground group-hover:opacity-100"
|
||||
title="Dismiss"
|
||||
title={i18n.t('newtab.tip.dismiss')}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</button>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { i18n } from '#i18n'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -25,10 +26,10 @@ export const ShortcutsDialog = ({
|
||||
<DialogContent className="styled-scrollbar max-h-[80vh] max-w-xl overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="font-semibold text-2xl">
|
||||
Keyboard Shortcuts
|
||||
{i18n.t('newtab.shortcuts.title')}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Use these shortcuts to navigate BrowserOS faster
|
||||
{i18n.t('newtab.shortcuts.description')}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -59,7 +60,7 @@ export const ShortcutsDialog = ({
|
||||
</div>
|
||||
|
||||
<div className="mt-8 border-border/50 border-t pt-4 text-center text-muted-foreground text-xs">
|
||||
More shortcuts coming soon
|
||||
{i18n.t('newtab.shortcuts.moreComingSoon')}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Cloud, X } from 'lucide-react'
|
||||
import { AnimatePresence, motion } from 'motion/react'
|
||||
import { useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import { i18n } from '#i18n'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Card,
|
||||
@@ -47,7 +48,9 @@ export const SignInHint = () => {
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Cloud className="size-5 text-muted-foreground" />
|
||||
<CardTitle className="text-base">Sync your data</CardTitle>
|
||||
<CardTitle className="text-base">
|
||||
{i18n.t('newtab.signIn.title')}
|
||||
</CardTitle>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -59,7 +62,7 @@ export const SignInHint = () => {
|
||||
</Button>
|
||||
</div>
|
||||
<CardDescription>
|
||||
Sign in to sync conversation history to the cloud.
|
||||
{i18n.t('newtab.signIn.description')}
|
||||
</CardDescription>
|
||||
<label
|
||||
htmlFor="sync-dont-ask-again"
|
||||
@@ -72,10 +75,10 @@ export const SignInHint = () => {
|
||||
setDontAskAgain(checked === true)
|
||||
}
|
||||
/>
|
||||
Don't ask again
|
||||
{i18n.t('newtab.signIn.dontAskAgain')}
|
||||
</label>
|
||||
<Button className="w-full" onClick={() => navigate('/login')}>
|
||||
Sign in
|
||||
{i18n.t('newtab.signIn.signInButton')}
|
||||
</Button>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Globe, X } from 'lucide-react'
|
||||
import type { FC } from 'react'
|
||||
import { i18n } from '#i18n'
|
||||
|
||||
interface ChatAttachedTabsProps {
|
||||
tabs: chrome.tabs.Tab[]
|
||||
@@ -34,7 +35,7 @@ export const ChatAttachedTabs: FC<ChatAttachedTabsProps> = ({
|
||||
type="button"
|
||||
onClick={() => onRemoveTab(tab.id)}
|
||||
className="flex-shrink-0 rounded p-0.5 transition-colors hover:bg-background"
|
||||
title="Remove tab"
|
||||
title={i18n.t('attachedTabs.removeTab')}
|
||||
>
|
||||
<X className="h-3 w-3 text-muted-foreground" />
|
||||
</button>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Sparkles } from 'lucide-react'
|
||||
import type { FC } from 'react'
|
||||
import { i18n } from '#i18n'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { AGENT_SUGGESTIONS, CHAT_SUGGESTIONS, type ChatMode } from './chatTypes'
|
||||
|
||||
@@ -28,12 +29,14 @@ export const ChatEmptyState: FC<ChatEmptyStateProps> = ({
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="mb-1 font-semibold text-lg">
|
||||
{mode === 'chat' ? 'Chat with this page' : 'Agent at your service'}
|
||||
{mode === 'chat'
|
||||
? i18n.t('chat.empty.chatTitle')
|
||||
: i18n.t('chat.empty.agentTitle')}
|
||||
</h2>
|
||||
<p className="max-w-[200px] text-muted-foreground text-xs">
|
||||
{mode === 'chat'
|
||||
? 'Ask questions about the current page or any topic'
|
||||
: 'Let AI automate tasks and browse for you'}
|
||||
? i18n.t('chat.empty.chatSubtitle')
|
||||
: i18n.t('chat.empty.agentSubtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AlertCircle, RefreshCw } from 'lucide-react'
|
||||
import type { FC } from 'react'
|
||||
import { i18n } from '#i18n'
|
||||
// import { useMemo } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
@@ -38,7 +39,7 @@ function parseErrorMessage(message: string): {
|
||||
message.includes('127.0.0.1')
|
||||
) {
|
||||
return {
|
||||
text: 'Unable to connect to BrowserOS agent. Follow below instructions.',
|
||||
text: i18n.t('chat.error.connectionMessage'),
|
||||
url: 'https://docs.browseros.com/troubleshooting/connection-issues',
|
||||
isConnectionError: true,
|
||||
}
|
||||
@@ -47,7 +48,7 @@ function parseErrorMessage(message: string): {
|
||||
// Detect BrowserOS rate limit (unique pattern, no provider uses this)
|
||||
if (message.includes('BrowserOS LLM daily limit reached')) {
|
||||
return {
|
||||
text: 'Add your own API key for unlimited usage.',
|
||||
text: i18n.t('chat.error.rateLimitMessage'),
|
||||
url: 'https://dub.sh/browseros-usage-limit',
|
||||
isRateLimit: true,
|
||||
}
|
||||
@@ -83,9 +84,9 @@ export const ChatError: FC<ChatErrorProps> = ({ error, onRetry }) => {
|
||||
// --- End commented out survey code ---
|
||||
|
||||
const getTitle = () => {
|
||||
if (isRateLimit) return 'Daily limit reached'
|
||||
if (isConnectionError) return 'Connection failed'
|
||||
return 'Something went wrong'
|
||||
if (isRateLimit) return i18n.t('chat.error.dailyLimitTitle')
|
||||
if (isConnectionError) return i18n.t('chat.error.connectionFailedTitle')
|
||||
return i18n.t('chat.error.genericTitle')
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -102,7 +103,7 @@ export const ChatError: FC<ChatErrorProps> = ({ error, onRetry }) => {
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground text-xs underline hover:text-foreground"
|
||||
>
|
||||
View troubleshooting guide
|
||||
{i18n.t('chat.error.troubleshootingLink')}
|
||||
</a>
|
||||
)}
|
||||
{/* --- Commented out for Kimi partnership launch (restore after) ---
|
||||
@@ -139,7 +140,7 @@ export const ChatError: FC<ChatErrorProps> = ({ error, onRetry }) => {
|
||||
className="underline hover:text-foreground"
|
||||
onClick={() => track(KIMI_RATE_LIMIT_DOCS_CLICKED_EVENT)}
|
||||
>
|
||||
Learn how to get a Kimi API key
|
||||
{i18n.t('chat.error.kimiApiKeyLink')}
|
||||
</a>
|
||||
{' or '}
|
||||
<a
|
||||
@@ -149,7 +150,7 @@ export const ChatError: FC<ChatErrorProps> = ({ error, onRetry }) => {
|
||||
className="underline hover:text-foreground"
|
||||
onClick={() => track(KIMI_RATE_LIMIT_PLATFORM_CLICKED_EVENT)}
|
||||
>
|
||||
get your API key
|
||||
{i18n.t('chat.error.getApiKey')}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -162,7 +163,7 @@ export const ChatError: FC<ChatErrorProps> = ({ error, onRetry }) => {
|
||||
className="mt-1 gap-2"
|
||||
>
|
||||
<RefreshCw className="h-3.5 w-3.5" />
|
||||
Try again
|
||||
{i18n.t('chat.error.tryAgain')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ChevronDown, Folder, Layers, PlugZap } from 'lucide-react'
|
||||
import type { FC, FormEvent } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { i18n } from '#i18n'
|
||||
import { AppSelector } from '@/components/elements/AppSelector'
|
||||
import { WorkspaceSelector } from '@/components/elements/workspace-selector'
|
||||
import { McpServerIcon } from '@/entrypoints/app/connect-mcp/McpServerIcon'
|
||||
@@ -142,7 +143,7 @@ export const ChatFooter: FC<ChatFooterProps> = ({
|
||||
aria-expanded={isTabMentionOpen}
|
||||
aria-haspopup="dialog"
|
||||
className="flex cursor-pointer items-center gap-1 rounded-lg p-1.5 text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground data-[state=open]:bg-accent"
|
||||
title="Attach tabs (@)"
|
||||
title={i18n.t('footer.attachTabs')}
|
||||
>
|
||||
<Layers className="h-4 w-4" />
|
||||
{attachedTabs.length > 0 && (
|
||||
@@ -166,7 +167,7 @@ export const ChatFooter: FC<ChatFooterProps> = ({
|
||||
title={
|
||||
selectedFolder
|
||||
? selectedFolder.name
|
||||
: 'Select workspace folder'
|
||||
: i18n.t('footer.selectWorkspace')
|
||||
}
|
||||
>
|
||||
<div className="relative">
|
||||
@@ -185,7 +186,7 @@ export const ChatFooter: FC<ChatFooterProps> = ({
|
||||
<button
|
||||
type="button"
|
||||
className="flex cursor-pointer items-center gap-1 rounded-lg p-1.5 text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground data-[state=open]:bg-accent"
|
||||
title="Connect apps"
|
||||
title={i18n.t('footer.connectApps')}
|
||||
>
|
||||
{connectedManagedServers.length > 0 ? (
|
||||
<>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Github, History, Plus, SettingsIcon } from 'lucide-react'
|
||||
import type { FC } from 'react'
|
||||
import { Link, useLocation, useNavigate } from 'react-router'
|
||||
import { i18n } from '#i18n'
|
||||
import { ChatProviderSelector } from '@/components/chat/ChatProviderSelector'
|
||||
import type { Provider } from '@/components/chat/chatComponentTypes'
|
||||
import { ThemeToggle } from '@/components/elements/theme-toggle'
|
||||
@@ -46,7 +47,7 @@ export const ChatHeader: FC<ChatHeaderProps> = ({
|
||||
<button
|
||||
type="button"
|
||||
className="group relative inline-flex cursor-pointer items-center gap-2 rounded-lg p-2 text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground data-[state=open]:bg-accent"
|
||||
title="Change AI Provider"
|
||||
title={i18n.t('chat.header.changeProvider')}
|
||||
>
|
||||
{selectedProvider.type === 'browseros' ? (
|
||||
<BrowserOSIcon size={18} />
|
||||
@@ -69,7 +70,7 @@ export const ChatHeader: FC<ChatHeaderProps> = ({
|
||||
type="button"
|
||||
onClick={onNewConversation}
|
||||
className="cursor-pointer rounded-lg p-2 text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground"
|
||||
title="New conversation"
|
||||
title={i18n.t('chat.header.newConversation')}
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
</button>
|
||||
@@ -81,7 +82,7 @@ export const ChatHeader: FC<ChatHeaderProps> = ({
|
||||
type="button"
|
||||
onClick={handleNewConversationFromHistory}
|
||||
className="cursor-pointer rounded-lg p-2 text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground"
|
||||
title="New conversation"
|
||||
title={i18n.t('chat.header.newConversation')}
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
</button>
|
||||
@@ -89,7 +90,7 @@ export const ChatHeader: FC<ChatHeaderProps> = ({
|
||||
<Link
|
||||
to="/history"
|
||||
className="cursor-pointer rounded-lg p-2 text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground"
|
||||
title="Chat history"
|
||||
title={i18n.t('chat.header.chatHistory')}
|
||||
>
|
||||
<History className="h-4 w-4" />
|
||||
</Link>
|
||||
@@ -100,7 +101,7 @@ export const ChatHeader: FC<ChatHeaderProps> = ({
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="cursor-pointer rounded-lg p-2 text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground"
|
||||
title="Star on Github"
|
||||
title={i18n.t('chat.header.starOnGithub')}
|
||||
>
|
||||
<Github className="h-4 w-4" />
|
||||
</a>
|
||||
@@ -110,7 +111,7 @@ export const ChatHeader: FC<ChatHeaderProps> = ({
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="cursor-pointer rounded-lg p-2 text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground"
|
||||
title="Settings"
|
||||
title={i18n.t('chat.header.settings')}
|
||||
>
|
||||
<SettingsIcon className="h-4 w-4" />
|
||||
</a>
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { i18n } from '#i18n'
|
||||
import { TabPickerPopover } from '@/components/elements/tab-picker-popover'
|
||||
import { cn } from '@/lib/utils'
|
||||
import type { VoiceInputState } from '@/lib/voice/useVoiceInput'
|
||||
@@ -273,7 +274,9 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
||||
className="cursor-pointer rounded-full bg-red-600 p-2 text-white shadow-sm transition-all duration-200 hover:bg-red-900"
|
||||
>
|
||||
<Square className="h-3.5 w-3.5" />
|
||||
<span className="sr-only">Stop recording</span>
|
||||
<span className="sr-only">
|
||||
{i18n.t('chat.input.stopRecording')}
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -286,7 +289,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
||||
className="rounded-full p-2 text-muted-foreground"
|
||||
>
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
||||
<span className="sr-only">Transcribing</span>
|
||||
<span className="sr-only">{i18n.t('chat.input.transcribing')}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -299,7 +302,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
||||
className="cursor-pointer rounded-full p-2 text-muted-foreground transition-all duration-200 hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
<Mic className="h-3.5 w-3.5" />
|
||||
<span className="sr-only">Voice input</span>
|
||||
<span className="sr-only">{i18n.t('chat.input.voiceInput')}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -313,7 +316,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
||||
className="cursor-pointer rounded-full bg-red-600 p-2 text-white shadow-sm transition-all duration-200 hover:bg-red-900"
|
||||
>
|
||||
<SquareStop className="h-3.5 w-3.5" />
|
||||
<span className="sr-only">Stop</span>
|
||||
<span className="sr-only">{i18n.t('chat.input.stop')}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -327,7 +330,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
||||
className="cursor-pointer rounded-full bg-[var(--accent-orange)] p-2 text-white shadow-sm transition-all duration-200 hover:bg-[var(--accent-orange-bright)] disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
<Send className="h-3.5 w-3.5" />
|
||||
<span className="sr-only">Send</span>
|
||||
<span className="sr-only">{i18n.t('chat.input.send')}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -370,10 +373,10 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={
|
||||
voice?.isTranscribing
|
||||
? 'Transcribing...'
|
||||
? i18n.t('chat.input.placeholderTranscribing')
|
||||
: mode === 'chat'
|
||||
? 'Ask about this page...'
|
||||
: 'What should I do?'
|
||||
? i18n.t('chat.input.placeholderChat')
|
||||
: i18n.t('chat.input.placeholderAgent')
|
||||
}
|
||||
disabled={voice?.isTranscribing}
|
||||
rows={1}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CheckIcon, CopyIcon, ThumbsDownIcon, ThumbsUpIcon } from 'lucide-react'
|
||||
import { AnimatePresence, motion } from 'motion/react'
|
||||
import { type FC, useState } from 'react'
|
||||
import { i18n } from '#i18n'
|
||||
import { MessageAction, MessageActions } from '@/components/ai-elements/message'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
@@ -63,8 +64,8 @@ export const ChatMessageActions: FC<ChatMessageActionsProps> = ({
|
||||
navigator.clipboard.writeText(messageText)
|
||||
track(SIDEPANEL_MESSAGE_COPIED_EVENT)
|
||||
}}
|
||||
label="Copy"
|
||||
tooltip="Copy to clipboard"
|
||||
label={i18n.t('chat.actions.copy')}
|
||||
tooltip={i18n.t('chat.actions.copyToClipboard')}
|
||||
>
|
||||
<CopyIcon className="size-3" />
|
||||
</MessageAction>
|
||||
@@ -79,7 +80,7 @@ export const ChatMessageActions: FC<ChatMessageActionsProps> = ({
|
||||
className="flex items-center gap-1 text-muted-foreground text-xs"
|
||||
>
|
||||
<CheckIcon className="size-3" />
|
||||
<span>Feedback submitted</span>
|
||||
<span>{i18n.t('chat.actions.feedbackSubmitted')}</span>
|
||||
</motion.div>
|
||||
) : (
|
||||
<motion.div
|
||||
@@ -91,9 +92,9 @@ export const ChatMessageActions: FC<ChatMessageActionsProps> = ({
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<MessageAction
|
||||
label="Like"
|
||||
label={i18n.t('chat.actions.like')}
|
||||
onClick={handleLike}
|
||||
tooltip="Like this response"
|
||||
tooltip={i18n.t('chat.actions.likeTooltip')}
|
||||
>
|
||||
<ThumbsUpIcon
|
||||
className="size-4"
|
||||
@@ -101,9 +102,9 @@ export const ChatMessageActions: FC<ChatMessageActionsProps> = ({
|
||||
/>
|
||||
</MessageAction>
|
||||
<MessageAction
|
||||
label="Dislike"
|
||||
label={i18n.t('chat.actions.dislike')}
|
||||
onClick={handleDislikeClick}
|
||||
tooltip="Dislike this response"
|
||||
tooltip={i18n.t('chat.actions.dislikeTooltip')}
|
||||
>
|
||||
<ThumbsDownIcon
|
||||
className="size-4"
|
||||
@@ -117,13 +118,13 @@ export const ChatMessageActions: FC<ChatMessageActionsProps> = ({
|
||||
<Dialog open={dislikeDialogOpen} onOpenChange={setDislikeDialogOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>What went wrong?</DialogTitle>
|
||||
<DialogTitle>{i18n.t('chat.actions.feedbackTitle')}</DialogTitle>
|
||||
<DialogDescription>
|
||||
Help us improve by sharing what was wrong with this response.
|
||||
{i18n.t('chat.actions.feedbackDescription')}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Input
|
||||
placeholder="Add a comment (optional)"
|
||||
placeholder={i18n.t('chat.actions.feedbackPlaceholder')}
|
||||
value={dislikeComment}
|
||||
onChange={(e) => setDislikeComment(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
@@ -134,9 +135,11 @@ export const ChatMessageActions: FC<ChatMessageActionsProps> = ({
|
||||
/>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={handleDislikeCancel}>
|
||||
Cancel
|
||||
{i18n.t('common.cancel')}
|
||||
</Button>
|
||||
<Button onClick={handleDislikeSubmit}>
|
||||
{i18n.t('common.submit')}
|
||||
</Button>
|
||||
<Button onClick={handleDislikeSubmit}>Submit</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { MessageSquare, MousePointer2 } from 'lucide-react'
|
||||
import type { FC } from 'react'
|
||||
import { i18n } from '#i18n'
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
@@ -37,20 +38,20 @@ export const ChatModeToggle: FC<ChatModeToggleProps> = ({
|
||||
{isAgentMode ? (
|
||||
<>
|
||||
<MousePointer2 className="h-3 w-3" />
|
||||
<span>Agent Mode ON</span>
|
||||
<span>{i18n.t('chat.mode.agent')}</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<MessageSquare className="h-3 w-3" />
|
||||
<span>Chat Mode ON</span>
|
||||
<span>{i18n.t('chat.mode.chat')}</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top" className="max-w-[220px]">
|
||||
{isAgentMode
|
||||
? 'AI can browse, click, and navigate'
|
||||
: 'AI can only read, cannot click or navigate'}
|
||||
? i18n.t('chat.mode.agentTooltip')
|
||||
: i18n.t('chat.mode.chatTooltip')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { FileText, X } from 'lucide-react'
|
||||
import type { FC } from 'react'
|
||||
import { i18n } from '#i18n'
|
||||
import type { SelectedTextData } from '@/lib/selected-text/selectedTextStorage'
|
||||
|
||||
const MAX_DISPLAY_LENGTH = 200
|
||||
@@ -35,7 +36,7 @@ export const ChatSelectedText: FC<ChatSelectedTextProps> = ({
|
||||
type="button"
|
||||
onClick={onDismiss}
|
||||
className="flex-shrink-0 rounded p-0.5 transition-colors hover:bg-background"
|
||||
title="Remove selected text"
|
||||
title={i18n.t('selectedText.removeTitle')}
|
||||
>
|
||||
<X className="h-3 w-3 text-muted-foreground" />
|
||||
</button>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Check, Plug } from 'lucide-react'
|
||||
import { type FC, useEffect, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import { i18n } from '#i18n'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
BREADCRUMB_CONNECT_CLICKED_EVENT,
|
||||
@@ -38,7 +39,11 @@ export const ConnectAppCard: FC<ConnectAppCardProps> = ({
|
||||
apiKeyUrl: string
|
||||
} | null>(null)
|
||||
const [resolvedText, setResolvedText] = useState(
|
||||
isLastMessage ? '' : `${(data.appName as string) ?? 'App'} suggested`,
|
||||
isLastMessage
|
||||
? ''
|
||||
: i18n.t('chat.connectApp.suggested', [
|
||||
(data.appName as string) ?? 'App',
|
||||
]),
|
||||
)
|
||||
|
||||
const { sendMessage } = useChatSessionContext()
|
||||
@@ -110,9 +115,11 @@ export const ConnectAppCard: FC<ConnectAppCardProps> = ({
|
||||
managedServerDescription: '',
|
||||
})
|
||||
track(MANAGED_MCP_ADDED_EVENT, { server_name: appName })
|
||||
toast.success(`${apiKeyServer.name} connected successfully`)
|
||||
toast.success(
|
||||
i18n.t('chat.connectApp.connectedSuccess', [apiKeyServer.name]),
|
||||
)
|
||||
setApiKeyServer(null)
|
||||
setResolvedText(`Connected ${appName}`)
|
||||
setResolvedText(i18n.t('chat.connectApp.connected', [appName]))
|
||||
setPhase('resolved')
|
||||
sendMessage({
|
||||
text: `I've connected ${appName}, continue with the task`,
|
||||
@@ -127,7 +134,7 @@ export const ConnectAppCard: FC<ConnectAppCardProps> = ({
|
||||
|
||||
const handleOAuthComplete = () => {
|
||||
track(BREADCRUMB_CONNECT_COMPLETED_EVENT, { app_name: appName })
|
||||
setResolvedText(`Connected ${appName}`)
|
||||
setResolvedText(i18n.t('chat.connectApp.connected', [appName]))
|
||||
setPhase('resolved')
|
||||
sendMessage({
|
||||
text: `I've connected ${appName}, continue with the task`,
|
||||
@@ -140,7 +147,7 @@ export const ConnectAppCard: FC<ConnectAppCardProps> = ({
|
||||
if (!current.includes(appName)) {
|
||||
await declinedAppsStorage.setValue([...current, appName])
|
||||
}
|
||||
setResolvedText(`Continuing without ${appName}`)
|
||||
setResolvedText(i18n.t('chat.connectApp.continuedWithout', [appName]))
|
||||
setPhase('resolved')
|
||||
sendMessage({
|
||||
text: `Continue without connecting ${appName}, do it manually with browser automation`,
|
||||
@@ -165,20 +172,20 @@ export const ConnectAppCard: FC<ConnectAppCardProps> = ({
|
||||
<Plug className="h-5 w-5 shrink-0 text-[var(--accent-orange)]" />
|
||||
<div>
|
||||
<p className="font-medium text-sm">
|
||||
Authorize {appName} in the opened tab
|
||||
{i18n.t('chat.connectApp.authorizeTitle', [appName])}
|
||||
</p>
|
||||
<p className="mt-1 text-muted-foreground text-xs">
|
||||
Complete the sign-in flow, then click the button below.
|
||||
{i18n.t('chat.connectApp.authorizeDescription')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 flex gap-2">
|
||||
<Button size="sm" onClick={handleOAuthComplete}>
|
||||
I've authorized {appName}, continue
|
||||
{i18n.t('chat.connectApp.authorizedButton', [appName])}
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" onClick={handleManual}>
|
||||
Skip, do it manually
|
||||
{i18n.t('chat.connectApp.skipButton')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -192,7 +199,7 @@ export const ConnectAppCard: FC<ConnectAppCardProps> = ({
|
||||
<Plug className="h-5 w-5 shrink-0 text-[var(--accent-orange)]" />
|
||||
<div>
|
||||
<p className="font-medium text-sm">
|
||||
Connect {appName} for better results
|
||||
{i18n.t('chat.connectApp.connectForBetter', [appName])}
|
||||
</p>
|
||||
{reason && (
|
||||
<p className="mt-1 text-muted-foreground text-xs">{reason}</p>
|
||||
@@ -202,10 +209,12 @@ export const ConnectAppCard: FC<ConnectAppCardProps> = ({
|
||||
|
||||
<div className="mt-3 flex gap-2">
|
||||
<Button size="sm" onClick={handleConnect} disabled={connecting}>
|
||||
{connecting ? 'Connecting...' : `Connect ${appName}`}
|
||||
{connecting
|
||||
? i18n.t('chat.connectApp.connecting')
|
||||
: i18n.t('chat.connectApp.connectButton', [appName])}
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" onClick={handleManual}>
|
||||
Do it manually
|
||||
{i18n.t('chat.connectApp.doItManually')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Clock, X } from 'lucide-react'
|
||||
import { type FC, useEffect, useState } from 'react'
|
||||
import { i18n } from '#i18n'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
BREADCRUMB_SCHEDULE_CLICKED_EVENT,
|
||||
@@ -40,7 +41,9 @@ export const ScheduleSuggestionCard: FC<ScheduleSuggestionCardProps> = ({
|
||||
if (dismissed) return null
|
||||
|
||||
const scheduleLabel =
|
||||
scheduleType === 'daily' ? `daily at ${scheduleTime}` : 'every hour'
|
||||
scheduleType === 'daily'
|
||||
? i18n.t('chat.schedule.dailyAt', [scheduleTime])
|
||||
: i18n.t('chat.schedule.everyHour')
|
||||
|
||||
const handleSchedule = () => {
|
||||
track(BREADCRUMB_SCHEDULE_CLICKED_EVENT, {
|
||||
@@ -75,7 +78,7 @@ export const ScheduleSuggestionCard: FC<ScheduleSuggestionCardProps> = ({
|
||||
<div className="flex items-start gap-3 pr-6">
|
||||
<Clock className="h-5 w-5 shrink-0 text-[var(--accent-orange)]" />
|
||||
<div>
|
||||
<p className="font-medium text-sm">Run this automatically?</p>
|
||||
<p className="font-medium text-sm">{i18n.t('chat.schedule.title')}</p>
|
||||
<p className="mt-1 text-muted-foreground text-xs">
|
||||
“{suggestedName}” — I can run this {scheduleLabel}
|
||||
</p>
|
||||
@@ -84,10 +87,10 @@ export const ScheduleSuggestionCard: FC<ScheduleSuggestionCardProps> = ({
|
||||
|
||||
<div className="mt-3 flex gap-2">
|
||||
<Button size="sm" onClick={handleSchedule}>
|
||||
Schedule this task
|
||||
{i18n.t('chat.schedule.scheduleButton')}
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" onClick={handleDismiss}>
|
||||
Maybe later
|
||||
{i18n.t('chat.schedule.maybeLater')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
134
packages/browseros-agent/apps/agent/locales/de.yml
Normal file
134
packages/browseros-agent/apps/agent/locales/de.yml
Normal file
@@ -0,0 +1,134 @@
|
||||
extName: BrowserOS-Assistent
|
||||
extActionTitle: BrowserOS fragen
|
||||
chat:
|
||||
input:
|
||||
placeholderAgent: Was soll ich tun?
|
||||
placeholderChat: Frage zu dieser Seite...
|
||||
placeholderTranscribing: Transkribieren...
|
||||
send: Senden
|
||||
stop: Stop
|
||||
stopRecording: Aufnahme stoppen
|
||||
transcribing: Transkribieren
|
||||
voiceInput: Spracheingabe
|
||||
mode:
|
||||
agent: Agent-Modus AN
|
||||
chat: Chat-Modus AN
|
||||
agentTooltip: KI kann browsen, klicken und navigieren
|
||||
chatTooltip: KI kann nur lesen, nicht klicken oder navigieren
|
||||
empty:
|
||||
agentTitle: Agent zu Ihren Diensten
|
||||
agentSubtitle: KI automatisiert Aufgaben und browset für Sie
|
||||
chatTitle: Mit dieser Seite chatten
|
||||
chatSubtitle: Fragen zur aktuellen Seite oder einem beliebigen Thema stellen
|
||||
header:
|
||||
changeProvider: KI-Anbieter wechseln
|
||||
newConversation: Neues Gespräch
|
||||
chatHistory: Chat-Verlauf
|
||||
starOnGithub: Auf Github starren
|
||||
settings: Einstellungen
|
||||
error:
|
||||
dailyLimitTitle: Tageslimit erreicht
|
||||
connectionFailedTitle: Verbindung fehlgeschlagen
|
||||
genericTitle: Fehler aufgetreten
|
||||
connectionMessage: Verbindung zu BrowserOS-Agent nicht möglich. Folgen Sie den Anweisungen unten.
|
||||
rateLimitMessage: Eigenen API-Key hinzufügen für unbegrenzte Nutzung.
|
||||
troubleshootingLink: Fehlerbehebung anzeigen
|
||||
kimiApiKeyLink: Anleitung zum Kimi API-Key
|
||||
getApiKey: API-Key erhalten
|
||||
tryAgain: Erneut versuchen
|
||||
actions:
|
||||
copy: Kopieren
|
||||
copyToClipboard: In Zwischenablage kopieren
|
||||
feedbackSubmitted: Feedback gesendet
|
||||
like: Gefällt mir
|
||||
likeTooltip: Antwort gefällt mir
|
||||
dislike: Gefällt nicht
|
||||
dislikeTooltip: Antwort gefällt nicht
|
||||
feedbackTitle: Was ist schiefgelaufen?
|
||||
feedbackDescription: Teilen Sie uns mit, was an dieser Antwort nicht stimmte.
|
||||
feedbackPlaceholder: Kommentar (optional)
|
||||
schedule:
|
||||
title: Automatisch ausführen?
|
||||
dailyAt: täglich um $1
|
||||
everyHour: jede Stunde
|
||||
scheduleButton: Aufgabe planen
|
||||
maybeLater: Später vielleicht
|
||||
connectApp:
|
||||
connecting: Verbinden...
|
||||
connectButton: $1 verbinden
|
||||
authorizeTitle: $1 im geöffneten Tab autorisieren
|
||||
authorizeDescription: Anmeldung abschließen, dann Button klicken.
|
||||
authorizedButton: $1 autorisiert, weiter
|
||||
skipButton: Überspringen, manuell
|
||||
suggested: $1 vorgeschlagen
|
||||
connected: $1 verbunden
|
||||
continuedWithout: Ohne $1 fortfahren
|
||||
connectForBetter: $1 für bessere Ergebnisse verbinden
|
||||
doItManually: Manuell erledigen
|
||||
connectedSuccess: $1 erfolgreich verbunden
|
||||
connectFailed: Verbindung zu $1 fehlgeschlagen
|
||||
footer:
|
||||
attachTabs: Tabs anhängen (@)
|
||||
selectWorkspace: Arbeitsbereich wählen
|
||||
connectApps: Apps verbinden
|
||||
selectedText:
|
||||
removeTitle: Textauswahl entfernen
|
||||
attachedTabs:
|
||||
removeTab: Tab entfernen
|
||||
newtab:
|
||||
search:
|
||||
placeholder: BrowserOS fragen oder $1 suchen...
|
||||
branding:
|
||||
alt: BrowserOS
|
||||
selectedTab: Tab
|
||||
appsNew: Neu!
|
||||
appsTooltip: Direkt verbundene Apps liefern genauere und schnellere Antworten!
|
||||
addWorkspace: Arbeitsbereich hinzufügen
|
||||
tip:
|
||||
label: "Tipp:"
|
||||
nextTip: Nächster Tipp
|
||||
dismiss: Schließen
|
||||
signIn:
|
||||
title: Daten synchronisieren
|
||||
description: Anmelden zum Synchronisieren des Chat-Verlaufs.
|
||||
dontAskAgain: Nicht wieder fragen
|
||||
signInButton: Anmelden
|
||||
importData:
|
||||
title: Daten importieren
|
||||
description: Lesezeichen, Verlauf und Passwörter aus Chrome importieren.
|
||||
dontShowAgain: Nicht mehr anzeigen
|
||||
openSettings: Importeinstellungen öffnen
|
||||
shortcuts:
|
||||
title: Tastenkürzel
|
||||
description: Diese Kürzel für schnellere Navigation in BrowserOS
|
||||
moreComingSoon: Weitere Kürzel folgen
|
||||
sidebar:
|
||||
nav:
|
||||
home: Start
|
||||
connectApps: Apps verbinden
|
||||
scheduledTasks: Geplante Aufgaben
|
||||
workflows: Workflows
|
||||
skills: Skills
|
||||
memory: Speicher
|
||||
soul: Seele
|
||||
settings: Einstellungen
|
||||
footer:
|
||||
about: Über BrowserOS
|
||||
shortcuts: Kürzel
|
||||
branding:
|
||||
alt: BrowserOS
|
||||
personal: Persönlich
|
||||
updateProfile: Profil aktualisieren
|
||||
signOut: Abmelden
|
||||
signIn: Anmelden
|
||||
common:
|
||||
cancel: Abbrechen
|
||||
submit: Absenden
|
||||
save: Speichern
|
||||
delete: Löschen
|
||||
edit: Bearbeiten
|
||||
close: Schließen
|
||||
loading: Laden...
|
||||
error: Fehler aufgetreten
|
||||
apps: Apps
|
||||
tabs: Tabs
|
||||
169
packages/browseros-agent/apps/agent/locales/en.yml
Normal file
169
packages/browseros-agent/apps/agent/locales/en.yml
Normal file
@@ -0,0 +1,169 @@
|
||||
# Extension manifest
|
||||
extName: BrowserOS Assistant
|
||||
extActionTitle: Ask BrowserOS
|
||||
|
||||
# Chat input
|
||||
chat:
|
||||
input:
|
||||
placeholderAgent: What should I do?
|
||||
placeholderChat: Ask about this page...
|
||||
placeholderTranscribing: Transcribing...
|
||||
send: Send
|
||||
stop: Stop
|
||||
stopRecording: Stop recording
|
||||
transcribing: Transcribing
|
||||
voiceInput: Voice input
|
||||
|
||||
# Chat mode toggle
|
||||
mode:
|
||||
agent: Agent Mode ON
|
||||
chat: Chat Mode ON
|
||||
agentTooltip: AI can browse, click, and navigate
|
||||
chatTooltip: AI can only read, cannot click or navigate
|
||||
|
||||
# Chat empty state
|
||||
empty:
|
||||
agentTitle: Agent at your service
|
||||
agentSubtitle: Let AI automate tasks and browse for you
|
||||
chatTitle: Chat with this page
|
||||
chatSubtitle: Ask questions about the current page or any topic
|
||||
|
||||
# Chat header
|
||||
header:
|
||||
changeProvider: Change AI Provider
|
||||
newConversation: New conversation
|
||||
chatHistory: Chat history
|
||||
starOnGithub: Star on Github
|
||||
settings: Settings
|
||||
|
||||
# Chat errors
|
||||
error:
|
||||
dailyLimitTitle: Daily limit reached
|
||||
connectionFailedTitle: Connection failed
|
||||
genericTitle: Something went wrong
|
||||
connectionMessage: "Unable to connect to BrowserOS agent. Follow below instructions."
|
||||
rateLimitMessage: Add your own API key for unlimited usage.
|
||||
troubleshootingLink: View troubleshooting guide
|
||||
kimiApiKeyLink: Learn how to get a Kimi API key
|
||||
getApiKey: get your API key
|
||||
tryAgain: Try again
|
||||
|
||||
# Chat message actions
|
||||
actions:
|
||||
copy: Copy
|
||||
copyToClipboard: Copy to clipboard
|
||||
feedbackSubmitted: Feedback submitted
|
||||
like: Like
|
||||
likeTooltip: Like this response
|
||||
dislike: Dislike
|
||||
dislikeTooltip: Dislike this response
|
||||
feedbackTitle: What went wrong?
|
||||
feedbackDescription: Help us improve by sharing what was wrong with this response.
|
||||
feedbackPlaceholder: Add a comment (optional)
|
||||
|
||||
# Schedule suggestion card
|
||||
schedule:
|
||||
title: Run this automatically?
|
||||
dailyAt: "daily at $1"
|
||||
everyHour: every hour
|
||||
scheduleButton: Schedule this task
|
||||
maybeLater: Maybe later
|
||||
|
||||
# Connect app card
|
||||
connectApp:
|
||||
connecting: Connecting...
|
||||
connectButton: "Connect $1"
|
||||
authorizeTitle: "Authorize $1 in the opened tab"
|
||||
authorizeDescription: Complete the sign-in flow, then click the button below.
|
||||
authorizedButton: "I've authorized $1, continue"
|
||||
skipButton: "Skip, do it manually"
|
||||
suggested: "$1 suggested"
|
||||
connected: "Connected $1"
|
||||
continuedWithout: "Continuing without $1"
|
||||
connectForBetter: "Connect $1 for better results"
|
||||
doItManually: Do it manually
|
||||
connectedSuccess: "$1 connected successfully"
|
||||
connectFailed: "Failed to connect $1"
|
||||
|
||||
# Chat footer
|
||||
footer:
|
||||
attachTabs: Attach tabs (@)
|
||||
selectWorkspace: Select workspace folder
|
||||
connectApps: Connect apps
|
||||
|
||||
# Selected text card
|
||||
selectedText:
|
||||
removeTitle: Remove selected text
|
||||
|
||||
# Attached tabs
|
||||
attachedTabs:
|
||||
removeTab: Remove tab
|
||||
|
||||
# Newtab page
|
||||
newtab:
|
||||
search:
|
||||
placeholder: "Ask BrowserOS or search $1..."
|
||||
branding:
|
||||
alt: BrowserOS
|
||||
selectedTab: Tab
|
||||
appsNew: "New!"
|
||||
appsTooltip: Apps directly connected will have more accurate and faster responses for your queries!
|
||||
addWorkspace: Add workspace
|
||||
tip:
|
||||
label: "Tip:"
|
||||
nextTip: Next tip
|
||||
dismiss: Dismiss
|
||||
|
||||
# Sign in hint
|
||||
signIn:
|
||||
title: Sync your data
|
||||
description: Sign in to sync conversation history to the cloud.
|
||||
dontAskAgain: Don't ask again
|
||||
signInButton: Sign in
|
||||
|
||||
# Import data hint
|
||||
importData:
|
||||
title: Import your data
|
||||
description: "Bring bookmarks, history, and passwords from Chrome."
|
||||
dontShowAgain: Don't show this again
|
||||
openSettings: Open Import Settings
|
||||
|
||||
# Shortcuts dialog
|
||||
shortcuts:
|
||||
title: Keyboard Shortcuts
|
||||
description: Use these shortcuts to navigate BrowserOS faster
|
||||
moreComingSoon: More shortcuts coming soon
|
||||
|
||||
# Sidebar navigation
|
||||
sidebar:
|
||||
nav:
|
||||
home: Home
|
||||
connectApps: Connect Apps
|
||||
scheduledTasks: Scheduled Tasks
|
||||
workflows: Workflows
|
||||
skills: Skills
|
||||
memory: Memory
|
||||
soul: Soul
|
||||
settings: Settings
|
||||
footer:
|
||||
about: About BrowserOS
|
||||
shortcuts: Shortcuts
|
||||
branding:
|
||||
alt: BrowserOS
|
||||
personal: Personal
|
||||
updateProfile: Update Profile
|
||||
signOut: Sign out
|
||||
signIn: Sign in
|
||||
|
||||
# Common / shared
|
||||
common:
|
||||
cancel: Cancel
|
||||
submit: Submit
|
||||
save: Save
|
||||
delete: Delete
|
||||
edit: Edit
|
||||
close: Close
|
||||
loading: Loading...
|
||||
error: Something went wrong
|
||||
apps: Apps
|
||||
tabs: Tabs
|
||||
134
packages/browseros-agent/apps/agent/locales/es.yml
Normal file
134
packages/browseros-agent/apps/agent/locales/es.yml
Normal file
@@ -0,0 +1,134 @@
|
||||
extName: Asistente BrowserOS
|
||||
extActionTitle: Pregunta a BrowserOS
|
||||
chat:
|
||||
input:
|
||||
placeholderAgent: ¿Qué debo hacer?
|
||||
placeholderChat: Pregunta sobre esta página...
|
||||
placeholderTranscribing: Transcribiendo...
|
||||
send: Enviar
|
||||
stop: Detener
|
||||
stopRecording: Detener grabación
|
||||
transcribing: Transcribiendo
|
||||
voiceInput: Entrada de voz
|
||||
mode:
|
||||
agent: Modo Agente ON
|
||||
chat: Modo Chat ON
|
||||
agentTooltip: La IA puede navegar e interactuar
|
||||
chatTooltip: Solo lectura, sin interacción
|
||||
empty:
|
||||
agentTitle: Agente a su servicio
|
||||
agentSubtitle: Deja que la IA automatice y navegue por ti
|
||||
chatTitle: Chatea con esta página
|
||||
chatSubtitle: Pregunta sobre la página actual o cualquier tema
|
||||
header:
|
||||
changeProvider: Cambiar proveedor de IA
|
||||
newConversation: Nueva conversación
|
||||
chatHistory: Historial
|
||||
starOnGithub: Star en GitHub
|
||||
settings: Ajustes
|
||||
error:
|
||||
dailyLimitTitle: Límite diario alcanzado
|
||||
connectionFailedTitle: Conexión fallida
|
||||
genericTitle: Algo salió mal
|
||||
connectionMessage: No se pudo conectar al agente. Sigue las instrucciones.
|
||||
rateLimitMessage: Añade tu clave API para uso ilimitado.
|
||||
troubleshootingLink: Ver guía de solución
|
||||
kimiApiKeyLink: Cómo obtener clave API de Kimi
|
||||
getApiKey: obtener tu clave API
|
||||
tryAgain: Reintentar
|
||||
actions:
|
||||
copy: Copiar
|
||||
copyToClipboard: Copiar al portapapeles
|
||||
feedbackSubmitted: Feedback enviado
|
||||
like: Me gusta
|
||||
likeTooltip: Me gusta esta respuesta
|
||||
dislike: No me gusta
|
||||
dislikeTooltip: No me gusta esta respuesta
|
||||
feedbackTitle: ¿Qué salió mal?
|
||||
feedbackDescription: Cuéntanos qué salió mal para mejorar.
|
||||
feedbackPlaceholder: Añade un comentario (opcional)
|
||||
schedule:
|
||||
title: ¿Ejecutar automáticamente?
|
||||
dailyAt: diariamente a las $1
|
||||
everyHour: cada hora
|
||||
scheduleButton: Programar tarea
|
||||
maybeLater: Más tarde
|
||||
connectApp:
|
||||
connecting: Conectando...
|
||||
connectButton: Conectar $1
|
||||
authorizeTitle: Autoriza $1 en la pestaña abierta
|
||||
authorizeDescription: Completa el inicio de sesión y haz clic abajo.
|
||||
authorizedButton: He autorizado $1, continuar
|
||||
skipButton: Omitir, hacer manualmente
|
||||
suggested: $1 sugerido
|
||||
connected: $1 conectado
|
||||
continuedWithout: Continuando sin $1
|
||||
connectForBetter: Conecta $1 para mejores resultados
|
||||
doItManually: Hacerlo manualmente
|
||||
connectedSuccess: $1 conectado con éxito
|
||||
connectFailed: Error al conectar $1
|
||||
footer:
|
||||
attachTabs: Adjuntar pestañas (@)
|
||||
selectWorkspace: Seleccionar carpeta
|
||||
connectApps: Conectar apps
|
||||
selectedText:
|
||||
removeTitle: Eliminar texto seleccionado
|
||||
attachedTabs:
|
||||
removeTab: Quitar pestaña
|
||||
newtab:
|
||||
search:
|
||||
placeholder: Pregunta a BrowserOS o busca $1...
|
||||
branding:
|
||||
alt: BrowserOS
|
||||
selectedTab: Pestaña
|
||||
appsNew: ¡Nuevo!
|
||||
appsTooltip: Las apps conectadas ofrecen respuestas más precisas y rápidas
|
||||
addWorkspace: Añadir espacio de trabajo
|
||||
tip:
|
||||
label: "Consejo:"
|
||||
nextTip: Siguiente consejo
|
||||
dismiss: Descartar
|
||||
signIn:
|
||||
title: Sincroniza tus datos
|
||||
description: Inicia sesión para sincronizar el historial en la nube.
|
||||
dontAskAgain: No preguntar de nuevo
|
||||
signInButton: Iniciar sesión
|
||||
importData:
|
||||
title: Importar tus datos
|
||||
description: Importa marcadores, historial y contraseñas de Chrome.
|
||||
dontShowAgain: No mostrar de nuevo
|
||||
openSettings: Abrir ajustes de importación
|
||||
shortcuts:
|
||||
title: Atajos de teclado
|
||||
description: Usa estos atajos para navegar más rápido
|
||||
moreComingSoon: Más atajos pronto
|
||||
sidebar:
|
||||
nav:
|
||||
home: Inicio
|
||||
connectApps: Conectar Apps
|
||||
scheduledTasks: Tareas programadas
|
||||
workflows: Flujos
|
||||
skills: Habilidades
|
||||
memory: Memoria
|
||||
soul: Alma
|
||||
settings: Ajustes
|
||||
footer:
|
||||
about: Acerca de BrowserOS
|
||||
shortcuts: Atajos
|
||||
branding:
|
||||
alt: BrowserOS
|
||||
personal: Personal
|
||||
updateProfile: Actualizar perfil
|
||||
signOut: Cerrar sesión
|
||||
signIn: Iniciar sesión
|
||||
common:
|
||||
cancel: Cancelar
|
||||
submit: Enviar
|
||||
save: Guardar
|
||||
delete: Eliminar
|
||||
edit: Editar
|
||||
close: Cerrar
|
||||
loading: Cargando...
|
||||
error: Algo salió mal
|
||||
apps: Apps
|
||||
tabs: Pestañas
|
||||
134
packages/browseros-agent/apps/agent/locales/fr.yml
Normal file
134
packages/browseros-agent/apps/agent/locales/fr.yml
Normal file
@@ -0,0 +1,134 @@
|
||||
extName: Assistant BrowserOS
|
||||
extActionTitle: Demander à BrowserOS
|
||||
chat:
|
||||
input:
|
||||
placeholderAgent: Que dois-je faire ?
|
||||
placeholderChat: Demandez sur cette page...
|
||||
placeholderTranscribing: Transcription...
|
||||
send: Envoyer
|
||||
stop: Arrêter
|
||||
stopRecording: Arrêter l'enregistrement
|
||||
transcribing: Transcription
|
||||
voiceInput: Saisie vocale
|
||||
mode:
|
||||
agent: Mode agent activé
|
||||
chat: Mode chat activé
|
||||
agentTooltip: L'IA peut naviguer, cliquer et se déplacer
|
||||
chatTooltip: L'IA peut lire mais pas cliquer ni naviguer
|
||||
empty:
|
||||
agentTitle: Agent à votre service
|
||||
agentSubtitle: Laissez l'IA automatiser et naviguer pour vous
|
||||
chatTitle: Discuter avec cette page
|
||||
chatSubtitle: Posez des questions sur cette page ou tout sujet
|
||||
header:
|
||||
changeProvider: Changer fournisseur IA
|
||||
newConversation: Nouvelle conversation
|
||||
chatHistory: Historique
|
||||
starOnGithub: Étoiler sur Github
|
||||
settings: Paramètres
|
||||
error:
|
||||
dailyLimitTitle: Limite quotidienne atteinte
|
||||
connectionFailedTitle: Connexion échouée
|
||||
genericTitle: Une erreur s'est produite
|
||||
connectionMessage: Impossible de connecter l'agent BrowserOS. Suivez les instructions ci-dessous.
|
||||
rateLimitMessage: Ajoutez votre propre clé API pour un usage illimité.
|
||||
troubleshootingLink: Voir le guide de dépannage
|
||||
kimiApiKeyLink: Comment obtenir une clé API Kimi
|
||||
getApiKey: obtenir votre clé API
|
||||
tryAgain: Réessayer
|
||||
actions:
|
||||
copy: Copier
|
||||
copyToClipboard: Copier dans le presse-papiers
|
||||
feedbackSubmitted: Feedback envoyé
|
||||
like: Utile
|
||||
likeTooltip: Cette réponse est utile
|
||||
dislike: Inutile
|
||||
dislikeTooltip: Cette réponse est inutile
|
||||
feedbackTitle: Quel est le problème ?
|
||||
feedbackDescription: Aidez-nous à nous améliorer en indiquant le problème.
|
||||
feedbackPlaceholder: Commentaire (facultatif)
|
||||
schedule:
|
||||
title: Exécuter automatiquement ?
|
||||
dailyAt: tous les jours à $1
|
||||
everyHour: toutes les heures
|
||||
scheduleButton: Planifier cette tâche
|
||||
maybeLater: Plus tard
|
||||
connectApp:
|
||||
connecting: Connexion...
|
||||
connectButton: Connecter $1
|
||||
authorizeTitle: Autoriser $1 dans l'onglet ouvert
|
||||
authorizeDescription: Terminez la connexion, puis cliquez ci-dessous.
|
||||
authorizedButton: J'ai autorisé $1, continuer
|
||||
skipButton: Passer, faire manuellement
|
||||
suggested: $1 suggéré
|
||||
connected: $1 connecté
|
||||
continuedWithout: Continuer sans $1
|
||||
connectForBetter: Connectez $1 pour de meilleurs résultats
|
||||
doItManually: Faire manuellement
|
||||
connectedSuccess: $1 connecté avec succès
|
||||
connectFailed: Impossible de connecter $1
|
||||
footer:
|
||||
attachTabs: Joindre onglets (@)
|
||||
selectWorkspace: Choisir l'espace de travail
|
||||
connectApps: Connecter apps
|
||||
selectedText:
|
||||
removeTitle: Supprimer le texte sélectionné
|
||||
attachedTabs:
|
||||
removeTab: Supprimer l'onglet
|
||||
newtab:
|
||||
search:
|
||||
placeholder: Demandez à BrowserOS ou cherchez $1...
|
||||
branding:
|
||||
alt: BrowserOS
|
||||
selectedTab: Onglet
|
||||
appsNew: Nouveau !
|
||||
appsTooltip: Les apps connectées directement offrent des réponses plus précises et rapides !
|
||||
addWorkspace: Ajouter un espace de travail
|
||||
tip:
|
||||
label: "Astuce :"
|
||||
nextTip: Astuce suivante
|
||||
dismiss: Ignorer
|
||||
signIn:
|
||||
title: Synchroniser vos données
|
||||
description: Connectez-vous pour synchroniser l'historique dans le cloud.
|
||||
dontAskAgain: Ne plus demander
|
||||
signInButton: Se connecter
|
||||
importData:
|
||||
title: Importer vos données
|
||||
description: Importez marque-pages, historique et mots de passe depuis Chrome.
|
||||
dontShowAgain: Ne plus afficher
|
||||
openSettings: Ouvrir les paramètres d'importation
|
||||
shortcuts:
|
||||
title: Raccourcis clavier
|
||||
description: Utilisez ces raccourcis pour naviguer plus vite dans BrowserOS
|
||||
moreComingSoon: Plus de raccourcis bientôt
|
||||
sidebar:
|
||||
nav:
|
||||
home: Accueil
|
||||
connectApps: Apps connectées
|
||||
scheduledTasks: Tâches planifiées
|
||||
workflows: Workflows
|
||||
skills: Compétences
|
||||
memory: Mémoire
|
||||
soul: Âme
|
||||
settings: Paramètres
|
||||
footer:
|
||||
about: À propos de BrowserOS
|
||||
shortcuts: Raccourcis
|
||||
branding:
|
||||
alt: BrowserOS
|
||||
personal: Personnel
|
||||
updateProfile: Mettre à jour le profil
|
||||
signOut: Déconnexion
|
||||
signIn: Connexion
|
||||
common:
|
||||
cancel: Annuler
|
||||
submit: Valider
|
||||
save: Enregistrer
|
||||
delete: Supprimer
|
||||
edit: Modifier
|
||||
close: Fermer
|
||||
loading: Chargement...
|
||||
error: Une erreur s'est produite
|
||||
apps: Apps
|
||||
tabs: Onglets
|
||||
134
packages/browseros-agent/apps/agent/locales/ja.yml
Normal file
134
packages/browseros-agent/apps/agent/locales/ja.yml
Normal file
@@ -0,0 +1,134 @@
|
||||
extName: BrowserOS アシスタント
|
||||
extActionTitle: BrowserOS に質問
|
||||
chat:
|
||||
input:
|
||||
placeholderAgent: 何をしますか?
|
||||
placeholderChat: このページについて...
|
||||
placeholderTranscribing: 文字起こし中...
|
||||
send: 送信
|
||||
stop: 停止
|
||||
stopRecording: 録音停止
|
||||
transcribing: 文字起こし中
|
||||
voiceInput: 音声入力
|
||||
mode:
|
||||
agent: エージェントモードON
|
||||
chat: チャットモードON
|
||||
agentTooltip: 閲覧・クリック・ナビゲート可能
|
||||
chatTooltip: 閲覧のみ、クリック・ナビゲート不可
|
||||
empty:
|
||||
agentTitle: エージェントが対応します
|
||||
agentSubtitle: AIにタスク自動化と閲覧をお任せ
|
||||
chatTitle: このページとチャット
|
||||
chatSubtitle: 現在のページやトピックについて質問
|
||||
header:
|
||||
changeProvider: AIプロバイダー変更
|
||||
newConversation: 新規会話
|
||||
chatHistory: 履歴
|
||||
starOnGithub: GitHubでスター
|
||||
settings: 設定
|
||||
error:
|
||||
dailyLimitTitle: 1日の制限に達しました
|
||||
connectionFailedTitle: 接続失敗
|
||||
genericTitle: エラーが発生しました
|
||||
connectionMessage: BrowserOSエージェントに接続できません。以下の手順に従ってください。
|
||||
rateLimitMessage: 無制限利用にはAPIキーを追加してください。
|
||||
troubleshootingLink: トラブルシューティングを見る
|
||||
kimiApiKeyLink: Kimi APIキーの取得方法
|
||||
getApiKey: APIキーを取得
|
||||
tryAgain: 再試行
|
||||
actions:
|
||||
copy: コピー
|
||||
copyToClipboard: クリップボードにコピー
|
||||
feedbackSubmitted: フィードバック送信済み
|
||||
like: いいね
|
||||
likeTooltip: この回答にいいね
|
||||
dislike: よくないね
|
||||
dislikeTooltip: この回答に不満
|
||||
feedbackTitle: 何が問題でしたか?
|
||||
feedbackDescription: 回答の問題点を教えてください
|
||||
feedbackPlaceholder: コメントを追加(任意)
|
||||
schedule:
|
||||
title: 自動実行しますか?
|
||||
dailyAt: 毎日 $1
|
||||
everyHour: 毎時間
|
||||
scheduleButton: タスクを予約
|
||||
maybeLater: 後で
|
||||
connectApp:
|
||||
connecting: 接続中...
|
||||
connectButton: $1 に接続
|
||||
authorizeTitle: 開いたタブで $1 を承認
|
||||
authorizeDescription: サインインを完了し、下のボタンをクリックしてください。
|
||||
authorizedButton: $1 の承認完了、続ける
|
||||
skipButton: スキップして手動で実行
|
||||
suggested: $1 を推奨
|
||||
connected: $1 に接続済み
|
||||
continuedWithout: $1 なしで続行
|
||||
connectForBetter: より良い結果のため $1 に接続
|
||||
doItManually: 手動で実行
|
||||
connectedSuccess: $1 の接続に成功
|
||||
connectFailed: $1 の接続に失敗
|
||||
footer:
|
||||
attachTabs: タブを添付(@)
|
||||
selectWorkspace: ワークスペースフォルダを選択
|
||||
connectApps: アプリを接続
|
||||
selectedText:
|
||||
removeTitle: 選択テキストを削除
|
||||
attachedTabs:
|
||||
removeTab: タブを削除
|
||||
newtab:
|
||||
search:
|
||||
placeholder: BrowserOSに質問、または$1を検索...
|
||||
branding:
|
||||
alt: BrowserOS
|
||||
selectedTab: タブ
|
||||
appsNew: 新着!
|
||||
appsTooltip: 直接接続したアプリはより正確・高速な回答を提供します!
|
||||
addWorkspace: ワークスペースを追加
|
||||
tip:
|
||||
label: ヒント:
|
||||
nextTip: 次のヒント
|
||||
dismiss: 閉じる
|
||||
signIn:
|
||||
title: データを同期
|
||||
description: 会話履歴をクラウドに同期するにはサインインしてください。
|
||||
dontAskAgain: 今後表示しない
|
||||
signInButton: サインイン
|
||||
importData:
|
||||
title: データをインポート
|
||||
description: Chromeからブックマーク、履歴、パスワードを取り込みます。
|
||||
dontShowAgain: 今後表示しない
|
||||
openSettings: インポート設定を開く
|
||||
shortcuts:
|
||||
title: キーボードショートカット
|
||||
description: BrowserOSをより速く操作するためのショートカット
|
||||
moreComingSoon: さらにショートカットを追加予定
|
||||
sidebar:
|
||||
nav:
|
||||
home: ホーム
|
||||
connectApps: アプリ接続
|
||||
scheduledTasks: 予定タスク
|
||||
workflows: ワークフロー
|
||||
skills: スキル
|
||||
memory: メモリ
|
||||
soul: ソウル
|
||||
settings: 設定
|
||||
footer:
|
||||
about: BrowserOSについて
|
||||
shortcuts: ショートカット
|
||||
branding:
|
||||
alt: BrowserOS
|
||||
personal: 個人
|
||||
updateProfile: プロフィール更新
|
||||
signOut: サインアウト
|
||||
signIn: サインイン
|
||||
common:
|
||||
cancel: キャンセル
|
||||
submit: 送信
|
||||
save: 保存
|
||||
delete: 削除
|
||||
edit: 編集
|
||||
close: 閉じる
|
||||
loading: 読み込み中...
|
||||
error: エラーが発生しました
|
||||
apps: アプリ
|
||||
tabs: タブ
|
||||
134
packages/browseros-agent/apps/agent/locales/ko.yml
Normal file
134
packages/browseros-agent/apps/agent/locales/ko.yml
Normal file
@@ -0,0 +1,134 @@
|
||||
extName: BrowserOS Assistant
|
||||
extActionTitle: BrowserOS에게 물어보기
|
||||
chat:
|
||||
input:
|
||||
placeholderAgent: 무엇을 할까요?
|
||||
placeholderChat: 이 페이지에 대해 질문...
|
||||
placeholderTranscribing: 전사 중...
|
||||
send: 전송
|
||||
stop: 중지
|
||||
stopRecording: 녹음 중지
|
||||
transcribing: 전사 중
|
||||
voiceInput: 음성 입력
|
||||
mode:
|
||||
agent: 에이전트 모드 ON
|
||||
chat: 채팅 모드 ON
|
||||
agentTooltip: AI가 탐색, 클릭, 이동 가능
|
||||
chatTooltip: AI는 읽기만 가능, 클릭/이동 불가
|
||||
empty:
|
||||
agentTitle: 에이전트가 대기 중
|
||||
agentSubtitle: AI가 작업을 자동화하고 탐색합니다
|
||||
chatTitle: 이 페이지와 채팅
|
||||
chatSubtitle: 현재 페이지나 주제에 대해 질문하세요
|
||||
header:
|
||||
changeProvider: AI 제공자 변경
|
||||
newConversation: 새 대화
|
||||
chatHistory: 대화 기록
|
||||
starOnGithub: Github에서 Star
|
||||
settings: 설정
|
||||
error:
|
||||
dailyLimitTitle: 일일 한도 도달
|
||||
connectionFailedTitle: 연결 실패
|
||||
genericTitle: 문제가 발생했습니다
|
||||
connectionMessage: BrowserOS 에이전트에 연결할 수 없습니다. 아래 안내를 따르세요.
|
||||
rateLimitMessage: 무제한 사용을 위해 API 키를 추가하세요.
|
||||
troubleshootingLink: 문제 해결 가이드 보기
|
||||
kimiApiKeyLink: Kimi API 키 얻는 방법 알아보기
|
||||
getApiKey: API 키 받기
|
||||
tryAgain: 다시 시도
|
||||
actions:
|
||||
copy: 복사
|
||||
copyToClipboard: 클립보드에 복사
|
||||
feedbackSubmitted: 피드백 전송됨
|
||||
like: 좋아요
|
||||
likeTooltip: 이 응답이 좋아요
|
||||
dislike: 싫어요
|
||||
dislikeTooltip: 이 응답이 싫어요
|
||||
feedbackTitle: 무엇이 잘못되었나요?
|
||||
feedbackDescription: 이 응답의 문제를 공유하여 개선을 도와주세요.
|
||||
feedbackPlaceholder: 댓글 추가 (선택사항)
|
||||
schedule:
|
||||
title: 자동으로 실행할까요?
|
||||
dailyAt: 매일 $1
|
||||
everyHour: 매시간
|
||||
scheduleButton: 작업 예약
|
||||
maybeLater: 나중에
|
||||
connectApp:
|
||||
connecting: 연결 중...
|
||||
connectButton: $1 연결
|
||||
authorizeTitle: 열린 탭에서 $1 인증
|
||||
authorizeDescription: 로그인을 완료한 후 아래 버튼을 클릭하세요.
|
||||
authorizedButton: $1 인증 완료, 계속하기
|
||||
skipButton: 건너뛰기, 수동으로 실행
|
||||
suggested: $1 추천
|
||||
connected: $1 연결됨
|
||||
continuedWithout: $1 없이 계속
|
||||
connectForBetter: 더 나은 결과를 위해 $1 연결
|
||||
doItManually: 수동으로 실행
|
||||
connectedSuccess: $1 연결 성공
|
||||
connectFailed: $1 연결 실패
|
||||
footer:
|
||||
attachTabs: 탭 첨부 (@)
|
||||
selectWorkspace: 작업 폴더 선택
|
||||
connectApps: 앱 연결
|
||||
selectedText:
|
||||
removeTitle: 선택한 텍스트 제거
|
||||
attachedTabs:
|
||||
removeTab: 탭 제거
|
||||
newtab:
|
||||
search:
|
||||
placeholder: BrowserOS에게 묻거나 $1 검색...
|
||||
branding:
|
||||
alt: BrowserOS
|
||||
selectedTab: 탭
|
||||
appsNew: 신규!
|
||||
appsTooltip: 직접 연결된 앱은 더 정확하고 빠른 응답을 제공합니다!
|
||||
addWorkspace: 작업 공간 추가
|
||||
tip:
|
||||
label: "팁:"
|
||||
nextTip: 다음 팁
|
||||
dismiss: 닫기
|
||||
signIn:
|
||||
title: 데이터 동기화
|
||||
description: 대화 기록을 클라우드에 동기화하려면 로그인하세요.
|
||||
dontAskAgain: 다시 묻지 않기
|
||||
signInButton: 로그인
|
||||
importData:
|
||||
title: 데이터 가져오기
|
||||
description: Chrome에서 북마크, 기록, 비밀번호를 가져옵니다.
|
||||
dontShowAgain: 다시 표시하지 않기
|
||||
openSettings: 가져오기 설정 열기
|
||||
shortcuts:
|
||||
title: 키보드 단축키
|
||||
description: BrowserOS를 더 빠르게 탐색하려면 이 단축키를 사용하세요.
|
||||
moreComingSoon: 더 많은 단축키가 곧 제공됩니다
|
||||
sidebar:
|
||||
nav:
|
||||
home: 홈
|
||||
connectApps: 앱 연결
|
||||
scheduledTasks: 예약된 작업
|
||||
workflows: 워크플로우
|
||||
skills: 스킬
|
||||
memory: 메모리
|
||||
soul: 소울
|
||||
settings: 설정
|
||||
footer:
|
||||
about: BrowserOS 정보
|
||||
shortcuts: 단축키
|
||||
branding:
|
||||
alt: BrowserOS
|
||||
personal: 개인
|
||||
updateProfile: 프로필 업데이트
|
||||
signOut: 로그아웃
|
||||
signIn: 로그인
|
||||
common:
|
||||
cancel: 취소
|
||||
submit: 제출
|
||||
save: 저장
|
||||
delete: 삭제
|
||||
edit: 편집
|
||||
close: 닫기
|
||||
loading: 로딩 중...
|
||||
error: 문제가 발생했습니다
|
||||
apps: 앱
|
||||
tabs: 탭
|
||||
134
packages/browseros-agent/apps/agent/locales/pt_BR.yml
Normal file
134
packages/browseros-agent/apps/agent/locales/pt_BR.yml
Normal file
@@ -0,0 +1,134 @@
|
||||
extName: Assistente BrowserOS
|
||||
extActionTitle: Pergunte ao BrowserOS
|
||||
chat:
|
||||
input:
|
||||
placeholderAgent: O que devo fazer?
|
||||
placeholderChat: Pergunte sobre esta página...
|
||||
placeholderTranscribing: Transcrevendo...
|
||||
send: Enviar
|
||||
stop: Parar
|
||||
stopRecording: Parar gravação
|
||||
transcribing: Transcrevendo
|
||||
voiceInput: Entrada de voz
|
||||
mode:
|
||||
agent: Modo Agente ATIVO
|
||||
chat: Modo Chat ATIVO
|
||||
agentTooltip: A IA pode navegar, clicar e acessar sites
|
||||
chatTooltip: A IA só lê, não pode clicar ou navegar
|
||||
empty:
|
||||
agentTitle: Agente à sua disposição
|
||||
agentSubtitle: Deixe a IA automatizar tarefas e navegar por você
|
||||
chatTitle: Converse com esta página
|
||||
chatSubtitle: Faça perguntas sobre a página atual ou qualquer tópico
|
||||
header:
|
||||
changeProvider: Mudar provedor de IA
|
||||
newConversation: Nova conversa
|
||||
chatHistory: Histórico de chat
|
||||
starOnGithub: Estrelar no GitHub
|
||||
settings: Configurações
|
||||
error:
|
||||
dailyLimitTitle: Limite diário atingido
|
||||
connectionFailedTitle: Falha na conexão
|
||||
genericTitle: Algo deu errado
|
||||
connectionMessage: Não foi possível conectar ao agente BrowserOS. Siga as instruções abaixo.
|
||||
rateLimitMessage: Adicione sua própria chave de API para uso ilimitado.
|
||||
troubleshootingLink: Ver guia de solução de problemas
|
||||
kimiApiKeyLink: Saiba como obter uma chave de API Kimi
|
||||
getApiKey: obtenha sua chave de API
|
||||
tryAgain: Tentar novamente
|
||||
actions:
|
||||
copy: Copiar
|
||||
copyToClipboard: Copiar para a área de transferência
|
||||
feedbackSubmitted: Feedback enviado
|
||||
like: Curtir
|
||||
likeTooltip: Curtir esta resposta
|
||||
dislike: Descurtir
|
||||
dislikeTooltip: Não curtir esta resposta
|
||||
feedbackTitle: O que deu errado?
|
||||
feedbackDescription: Ajude-nos a melhorar informando o que estava errado.
|
||||
feedbackPlaceholder: Adicione um comentário (opcional)
|
||||
schedule:
|
||||
title: Executar isso automaticamente?
|
||||
dailyAt: diariamente às $1
|
||||
everyHour: a cada hora
|
||||
scheduleButton: Agendar esta tarefa
|
||||
maybeLater: Talvez depois
|
||||
connectApp:
|
||||
connecting: Conectando...
|
||||
connectButton: Conectar $1
|
||||
authorizeTitle: Autorize $1 na aba aberta
|
||||
authorizeDescription: Complete o fluxo de login, depois clique no botão abaixo.
|
||||
authorizedButton: Autorizei $1, continuar
|
||||
skipButton: Pular, fazer manualmente
|
||||
suggested: $1 sugerido
|
||||
connected: $1 conectado
|
||||
continuedWithout: Continuando sem $1
|
||||
connectForBetter: Conecte $1 para melhores resultados
|
||||
doItManually: Fazer manualmente
|
||||
connectedSuccess: $1 conectado com sucesso
|
||||
connectFailed: Falha ao conectar $1
|
||||
footer:
|
||||
attachTabs: Anexar abas (@)
|
||||
selectWorkspace: Selecionar workspace
|
||||
connectApps: Conectar apps
|
||||
selectedText:
|
||||
removeTitle: Remover texto selecionado
|
||||
attachedTabs:
|
||||
removeTab: Remover aba
|
||||
newtab:
|
||||
search:
|
||||
placeholder: Pergunte ao BrowserOS ou pesquise em $1...
|
||||
branding:
|
||||
alt: BrowserOS
|
||||
selectedTab: Aba
|
||||
appsNew: Novo!
|
||||
appsTooltip: Apps conectados diretamente oferecem respostas mais precisas e rápidas!
|
||||
addWorkspace: Adicionar workspace
|
||||
tip:
|
||||
label: "Dica:"
|
||||
nextTip: Próxima dica
|
||||
dismiss: Fechar
|
||||
signIn:
|
||||
title: Sincronize seus dados
|
||||
description: Entre para sincronizar o histórico de conversas na nuvem.
|
||||
dontAskAgain: Não perguntar novamente
|
||||
signInButton: Entrar
|
||||
importData:
|
||||
title: Importe seus dados
|
||||
description: Traga favoritos, histórico e senhas do Chrome.
|
||||
dontShowAgain: Não mostrar novamente
|
||||
openSettings: Abrir Configurações de Importação
|
||||
shortcuts:
|
||||
title: Atalhos de Teclado
|
||||
description: Use atalhos para navegar mais rápido no BrowserOS
|
||||
moreComingSoon: Mais atalhos em breve
|
||||
sidebar:
|
||||
nav:
|
||||
home: Início
|
||||
connectApps: Conectar Apps
|
||||
scheduledTasks: Tarefas Agendadas
|
||||
workflows: Fluxos de trabalho
|
||||
skills: Habilidades
|
||||
memory: Memória
|
||||
soul: Alma
|
||||
settings: Configurações
|
||||
footer:
|
||||
about: Sobre o BrowserOS
|
||||
shortcuts: Atalhos
|
||||
branding:
|
||||
alt: BrowserOS
|
||||
personal: Pessoal
|
||||
updateProfile: Atualizar Perfil
|
||||
signOut: Sair
|
||||
signIn: Entrar
|
||||
common:
|
||||
cancel: Cancelar
|
||||
submit: Enviar
|
||||
save: Salvar
|
||||
delete: Excluir
|
||||
edit: Editar
|
||||
close: Fechar
|
||||
loading: Carregando...
|
||||
error: Algo deu errado
|
||||
apps: Apps
|
||||
tabs: Abas
|
||||
134
packages/browseros-agent/apps/agent/locales/zh_CN.yml
Normal file
134
packages/browseros-agent/apps/agent/locales/zh_CN.yml
Normal file
@@ -0,0 +1,134 @@
|
||||
extName: BrowserOS 助手
|
||||
extActionTitle: 询问 BrowserOS
|
||||
chat:
|
||||
input:
|
||||
placeholderAgent: 需要做什么?
|
||||
placeholderChat: 询问此页面...
|
||||
placeholderTranscribing: 正在转录...
|
||||
send: 发送
|
||||
stop: 停止
|
||||
stopRecording: 停止录音
|
||||
transcribing: 转录中
|
||||
voiceInput: 语音输入
|
||||
mode:
|
||||
agent: Agent 模式开启
|
||||
chat: 聊天模式开启
|
||||
agentTooltip: AI 可浏览、点击、导航
|
||||
chatTooltip: AI 仅可阅读,不可点击导航
|
||||
empty:
|
||||
agentTitle: Agent 为您服务
|
||||
agentSubtitle: 让 AI 自动执行任务、代为浏览
|
||||
chatTitle: 与此页对话
|
||||
chatSubtitle: 询问当前页面或任意话题
|
||||
header:
|
||||
changeProvider: 切换 AI 提供商
|
||||
newConversation: 新建对话
|
||||
chatHistory: 对话历史
|
||||
starOnGithub: 在 GitHub 标星
|
||||
settings: 设置
|
||||
error:
|
||||
dailyLimitTitle: 已达每日上限
|
||||
connectionFailedTitle: 连接失败
|
||||
genericTitle: 出错了
|
||||
connectionMessage: 无法连接 BrowserOS Agent,请按以下步骤操作。
|
||||
rateLimitMessage: 添加个人 API 密钥以解除限制。
|
||||
troubleshootingLink: 查看排查指南
|
||||
kimiApiKeyLink: 了解如何获取 Kimi API 密钥
|
||||
getApiKey: 获取 API 密钥
|
||||
tryAgain: 重试
|
||||
actions:
|
||||
copy: 复制
|
||||
copyToClipboard: 复制到剪贴板
|
||||
feedbackSubmitted: 反馈已提交
|
||||
like: 点赞
|
||||
likeTooltip: 为此回复点赞
|
||||
dislike: 点踩
|
||||
dislikeTooltip: 为此回复点踩
|
||||
feedbackTitle: 出了什么问题?
|
||||
feedbackDescription: 分享此回复的问题,助我们改进。
|
||||
feedbackPlaceholder: 添加评论(可选)
|
||||
schedule:
|
||||
title: 自动运行此任务?
|
||||
dailyAt: 每天 $1
|
||||
everyHour: 每小时
|
||||
scheduleButton: 安排此任务
|
||||
maybeLater: 稍后
|
||||
connectApp:
|
||||
connecting: 连接中...
|
||||
connectButton: 连接 $1
|
||||
authorizeTitle: 在已打开的标签页中授权 $1
|
||||
authorizeDescription: 完成登录后点击下方按钮。
|
||||
authorizedButton: 已授权 $1,继续
|
||||
skipButton: 跳过,手动操作
|
||||
suggested: 建议使用 $1
|
||||
connected: 已连接 $1
|
||||
continuedWithout: 继续,不使用 $1
|
||||
connectForBetter: 连接 $1 以获得更好结果
|
||||
doItManually: 手动操作
|
||||
connectedSuccess: $1 连接成功
|
||||
connectFailed: $1 连接失败
|
||||
footer:
|
||||
attachTabs: 附加标签 (@)
|
||||
selectWorkspace: 选择工作区文件夹
|
||||
connectApps: 连接应用
|
||||
selectedText:
|
||||
removeTitle: 移除选中文本
|
||||
attachedTabs:
|
||||
removeTab: 移除标签
|
||||
newtab:
|
||||
search:
|
||||
placeholder: 询问 BrowserOS 或搜索 $1...
|
||||
branding:
|
||||
alt: BrowserOS
|
||||
selectedTab: 标签
|
||||
appsNew: 新!
|
||||
appsTooltip: 直接连接的应用回复更准确、更快速!
|
||||
addWorkspace: 添加工作区
|
||||
tip:
|
||||
label: 提示:
|
||||
nextTip: 下一条提示
|
||||
dismiss: 忽略
|
||||
signIn:
|
||||
title: 同步数据
|
||||
description: 登录以同步对话历史至云端。
|
||||
dontAskAgain: 不再询问
|
||||
signInButton: 登录
|
||||
importData:
|
||||
title: 导入数据
|
||||
description: 从 Chrome 导入书签、历史和密码。
|
||||
dontShowAgain: 不再显示
|
||||
openSettings: 打开导入设置
|
||||
shortcuts:
|
||||
title: 快捷键
|
||||
description: 使用快捷键更快操作 BrowserOS
|
||||
moreComingSoon: 更多快捷键即将推出
|
||||
sidebar:
|
||||
nav:
|
||||
home: 首页
|
||||
connectApps: 连接应用
|
||||
scheduledTasks: 定时任务
|
||||
workflows: 工作流
|
||||
skills: 技能
|
||||
memory: 记忆
|
||||
soul: 灵魂
|
||||
settings: 设置
|
||||
footer:
|
||||
about: 关于 BrowserOS
|
||||
shortcuts: 快捷键
|
||||
branding:
|
||||
alt: BrowserOS
|
||||
personal: 个人版
|
||||
updateProfile: 更新资料
|
||||
signOut: 退出
|
||||
signIn: 登录
|
||||
common:
|
||||
cancel: 取消
|
||||
submit: 提交
|
||||
save: 保存
|
||||
delete: 删除
|
||||
edit: 编辑
|
||||
close: 关闭
|
||||
loading: 加载中...
|
||||
error: 出错了
|
||||
apps: 应用
|
||||
tabs: 标签
|
||||
@@ -15,7 +15,8 @@
|
||||
"lint:fix": "bunx biome check --write --unsafe",
|
||||
"clean:cache": "rm -rf node_modules/.cache && rm -rf .output/ && rm -rf .wxt/",
|
||||
"codegen": "bun --env-file=.env.development graphql-codegen --config codegen.ts",
|
||||
"codegen:watch": "graphql-codegen --config codegen.ts --watch"
|
||||
"codegen:watch": "graphql-codegen --config codegen.ts --watch",
|
||||
"translate": "bun run scripts/translate.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/react": "^3.0.96",
|
||||
@@ -96,6 +97,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@0no-co/graphqlsp": "^1.15.2",
|
||||
"@ai-sdk/openai-compatible": "^2.0.35",
|
||||
"@eslint/compat": "^2.0.1",
|
||||
"@graphql-codegen/cli": "^6.1.1",
|
||||
"@graphql-codegen/client-preset": "^5.2.2",
|
||||
|
||||
261
packages/browseros-agent/apps/agent/scripts/translate.ts
Normal file
261
packages/browseros-agent/apps/agent/scripts/translate.ts
Normal file
@@ -0,0 +1,261 @@
|
||||
/* biome-ignore-all lint/suspicious/noConsole: CLI script requires console output */
|
||||
/* biome-ignore-all lint/style/noProcessEnv: CLI script reads env vars directly */
|
||||
/**
|
||||
* Auto-translate locale files using any OpenAI-compatible API.
|
||||
*
|
||||
* Usage:
|
||||
* bun run scripts/translate.ts # translate all target languages
|
||||
* bun run scripts/translate.ts --lang=zh_CN # translate one language
|
||||
* bun run scripts/translate.ts --lang=ja --dry-run # preview without writing
|
||||
*
|
||||
* Environment variables:
|
||||
* TRANSLATE_API_KEY — API key (required)
|
||||
* TRANSLATE_BASE_URL — Base URL (default: https://api.anthropic.com/v1)
|
||||
* TRANSLATE_MODEL — Model ID (default: claude-sonnet-4-20250514)
|
||||
*/
|
||||
|
||||
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { createOpenAICompatible } from '@ai-sdk/openai-compatible'
|
||||
import { generateText } from 'ai'
|
||||
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml'
|
||||
|
||||
const LOCALES_DIR = join(import.meta.dir, '..', 'locales')
|
||||
const SOURCE_LOCALE = 'en'
|
||||
|
||||
// Target languages to translate to
|
||||
const TARGET_LOCALES: Record<string, string> = {
|
||||
zh_CN: 'Chinese Simplified',
|
||||
ja: 'Japanese',
|
||||
ko: 'Korean',
|
||||
es: 'Spanish',
|
||||
fr: 'French',
|
||||
de: 'German',
|
||||
pt_BR: 'Portuguese (Brazil)',
|
||||
}
|
||||
|
||||
// Terms that should NOT be translated
|
||||
const PRESERVE_TERMS = [
|
||||
'BrowserOS',
|
||||
'GitHub',
|
||||
'Github',
|
||||
'OpenAI',
|
||||
'Anthropic',
|
||||
'Gemini',
|
||||
'Claude',
|
||||
'Gmail',
|
||||
'Slack',
|
||||
'Linear',
|
||||
'Notion',
|
||||
'Kimi',
|
||||
'API',
|
||||
'MCP',
|
||||
'OAuth',
|
||||
]
|
||||
|
||||
function flattenYaml(
|
||||
obj: Record<string, unknown>,
|
||||
prefix = '',
|
||||
): Record<string, string> {
|
||||
const result: Record<string, string> = {}
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
const fullKey = prefix ? `${prefix}.${key}` : key
|
||||
if (typeof value === 'string') {
|
||||
result[fullKey] = value
|
||||
} else if (typeof value === 'object' && value !== null) {
|
||||
Object.assign(
|
||||
result,
|
||||
flattenYaml(value as Record<string, unknown>, fullKey),
|
||||
)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function unflattenYaml(flat: Record<string, string>): Record<string, unknown> {
|
||||
const result: Record<string, unknown> = {}
|
||||
for (const [key, value] of Object.entries(flat)) {
|
||||
const parts = key.split('.')
|
||||
let current = result
|
||||
for (let i = 0; i < parts.length - 1; i++) {
|
||||
if (!current[parts[i]] || typeof current[parts[i]] !== 'object') {
|
||||
current[parts[i]] = {}
|
||||
}
|
||||
current = current[parts[i]] as Record<string, unknown>
|
||||
}
|
||||
current[parts[parts.length - 1]] = value
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async function translateKeys(
|
||||
keys: Record<string, string>,
|
||||
targetLang: string,
|
||||
targetName: string,
|
||||
): Promise<Record<string, string>> {
|
||||
const apiKey = process.env.TRANSLATE_API_KEY
|
||||
if (!apiKey) {
|
||||
throw new Error(
|
||||
'TRANSLATE_API_KEY environment variable is required. Set it before running.',
|
||||
)
|
||||
}
|
||||
|
||||
const baseURL =
|
||||
process.env.TRANSLATE_BASE_URL || 'https://api.anthropic.com/v1'
|
||||
const modelId = process.env.TRANSLATE_MODEL || 'claude-sonnet-4-20250514'
|
||||
|
||||
const provider = createOpenAICompatible({
|
||||
name: 'translate-provider',
|
||||
baseURL,
|
||||
apiKey,
|
||||
})
|
||||
|
||||
const keysText = Object.entries(keys)
|
||||
.map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
|
||||
.join('\n')
|
||||
|
||||
const { text } = await generateText({
|
||||
model: provider.chatModel(modelId),
|
||||
prompt: `Translate the following UI strings from English to ${targetName} (${targetLang}).
|
||||
|
||||
These are for a browser extension called BrowserOS — an AI-powered browser assistant.
|
||||
|
||||
Rules:
|
||||
- Keep translations concise — they must fit in the same UI space as the English text.
|
||||
- Preserve $1, $2 substitution placeholders exactly as-is.
|
||||
- Do NOT translate these proper nouns: ${PRESERVE_TERMS.join(', ')}
|
||||
- Return ONLY a valid JSON object mapping keys to translated strings. No markdown, no explanations.
|
||||
|
||||
Keys to translate:
|
||||
${keysText}`,
|
||||
})
|
||||
|
||||
// Extract JSON from response (handle potential markdown wrapping)
|
||||
const jsonMatch = text.match(/\{[\s\S]*\}/)
|
||||
if (!jsonMatch) {
|
||||
throw new Error(`Failed to parse translation response for ${targetLang}`)
|
||||
}
|
||||
|
||||
return JSON.parse(jsonMatch[0]) as Record<string, string>
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2)
|
||||
const langArg = args.find((a) => a.startsWith('--lang='))
|
||||
const dryRun = args.includes('--dry-run')
|
||||
const targetLang = langArg?.split('=')[1]
|
||||
|
||||
// Read source locale
|
||||
const sourcePath = join(LOCALES_DIR, `${SOURCE_LOCALE}.yml`)
|
||||
if (!existsSync(sourcePath)) {
|
||||
console.error(`Source locale not found: ${sourcePath}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const sourceYaml = parseYaml(readFileSync(sourcePath, 'utf-8')) as Record<
|
||||
string,
|
||||
unknown
|
||||
>
|
||||
const sourceFlat = flattenYaml(sourceYaml)
|
||||
|
||||
const locales = targetLang
|
||||
? { [targetLang]: TARGET_LOCALES[targetLang] || targetLang }
|
||||
: TARGET_LOCALES
|
||||
|
||||
for (const [locale, langName] of Object.entries(locales)) {
|
||||
console.log(`\n--- ${langName} (${locale}) ---`)
|
||||
|
||||
const targetPath = join(LOCALES_DIR, `${locale}.yml`)
|
||||
let existingFlat: Record<string, string> = {}
|
||||
|
||||
if (existsSync(targetPath)) {
|
||||
const existingYaml = parseYaml(
|
||||
readFileSync(targetPath, 'utf-8'),
|
||||
) as Record<string, unknown>
|
||||
existingFlat = flattenYaml(existingYaml)
|
||||
}
|
||||
|
||||
// Find keys that need translation (new or changed in source)
|
||||
const keysToTranslate: Record<string, string> = {}
|
||||
for (const [key, value] of Object.entries(sourceFlat)) {
|
||||
if (!existingFlat[key]) {
|
||||
keysToTranslate[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
// Find keys to remove (no longer in source)
|
||||
const keysToRemove = Object.keys(existingFlat).filter(
|
||||
(key) => !sourceFlat[key],
|
||||
)
|
||||
|
||||
if (
|
||||
Object.keys(keysToTranslate).length === 0 &&
|
||||
keysToRemove.length === 0
|
||||
) {
|
||||
console.log(' ✓ Up to date — no changes needed')
|
||||
continue
|
||||
}
|
||||
|
||||
console.log(
|
||||
` ${Object.keys(keysToTranslate).length} key(s) to translate, ${keysToRemove.length} key(s) to remove`,
|
||||
)
|
||||
|
||||
if (dryRun) {
|
||||
if (Object.keys(keysToTranslate).length > 0) {
|
||||
console.log(' New/changed keys:')
|
||||
for (const key of Object.keys(keysToTranslate)) {
|
||||
console.log(` + ${key}`)
|
||||
}
|
||||
}
|
||||
if (keysToRemove.length > 0) {
|
||||
console.log(' Removed keys:')
|
||||
for (const key of keysToRemove) {
|
||||
console.log(` - ${key}`)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Translate new keys
|
||||
let translated: Record<string, string> = {}
|
||||
if (Object.keys(keysToTranslate).length > 0) {
|
||||
console.log(' Translating...')
|
||||
translated = await translateKeys(keysToTranslate, locale, langName)
|
||||
console.log(` ✓ Translated ${Object.keys(translated).length} key(s)`)
|
||||
}
|
||||
|
||||
// Merge: existing (preserving human edits) + new translations - removed keys
|
||||
const merged = { ...existingFlat, ...translated }
|
||||
for (const key of keysToRemove) {
|
||||
delete merged[key]
|
||||
}
|
||||
|
||||
// Validate
|
||||
const missingPlaceholders: string[] = []
|
||||
for (const [key, value] of Object.entries(merged)) {
|
||||
const sourcePlaceholders = (sourceFlat[key] || '').match(/\$\d+/g) || []
|
||||
const translatedPlaceholders = value.match(/\$\d+/g) || []
|
||||
if (sourcePlaceholders.length !== translatedPlaceholders.length) {
|
||||
missingPlaceholders.push(key)
|
||||
}
|
||||
}
|
||||
if (missingPlaceholders.length > 0) {
|
||||
console.warn(
|
||||
` ⚠ Placeholder mismatch in: ${missingPlaceholders.join(', ')}`,
|
||||
)
|
||||
}
|
||||
|
||||
// Write
|
||||
const nestedYaml = unflattenYaml(merged)
|
||||
const yamlStr = stringifyYaml(nestedYaml, { lineWidth: 0 })
|
||||
writeFileSync(targetPath, yamlStr, 'utf-8')
|
||||
console.log(` ✓ Written to ${targetPath}`)
|
||||
}
|
||||
|
||||
console.log('\nDone!')
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -5,7 +5,8 @@
|
||||
"allowImportingTsExtensions": true,
|
||||
"jsx": "react-jsx",
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
"@/*": ["./*"],
|
||||
"#i18n": ["./.wxt/i18n/index.ts"]
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
|
||||
@@ -17,9 +17,10 @@ const apiPattern = apiUrl.port
|
||||
// Extension ID will be bflpfmnmnokmjhmgnolecpppdbdophmk
|
||||
export default defineConfig({
|
||||
outDir: 'dist',
|
||||
modules: ['@wxt-dev/module-react'],
|
||||
modules: ['@wxt-dev/module-react', '@wxt-dev/i18n/module'],
|
||||
manifest: {
|
||||
name: 'Assistant',
|
||||
name: '__MSG_extName__',
|
||||
default_locale: 'en',
|
||||
key: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvBDAaDRvv61NpBeLR8etBRw82lv9VJO3sz/mA26gDzWKtVuzW4DXCl8Zfj5oWmoXLTfv3aiTigUXo/LHOoGpSucEVroMmAc7cgu2KuQ1fZPpMvYa0npD/m4h89360q8Oz0oKKaZGS905IJ04M2IkF4CuU3YEHFJBWb+cUyK9H8YVugelYbPD0IVs63T1SkGbh/t/Tfb2DpkinduSO8+x26sKydm30SRt+iZ2+7Nolcdum3LExInUiX2Pgb65Jb+mVw8NqyTVJyCEp8uq0cSHomWFQirSJ80tsDhISp4btwaRKHrXqovQx9XHQv4hCd+3LuB830eUEVMUNuCO+OyPxQIDAQAB',
|
||||
update_url: 'https://cdn.browseros.com/extensions/update-manifest.xml',
|
||||
// update_url: 'https://cdn.browseros.com/extensions/update-manifest.alpha.xml',
|
||||
@@ -50,7 +51,7 @@ export default defineConfig({
|
||||
48: 'icon/48.png',
|
||||
128: 'icon/128.png',
|
||||
},
|
||||
default_title: 'Ask BrowserOS',
|
||||
default_title: '__MSG_extActionTitle__',
|
||||
},
|
||||
permissions: [
|
||||
'topSites',
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "browseros-monorepo",
|
||||
"dependencies": {
|
||||
"@wxt-dev/i18n": "^0.2.5",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/client-s3": "^3.933.0",
|
||||
"@biomejs/biome": "2.4.8",
|
||||
@@ -19,6 +22,7 @@
|
||||
"rimraf": "^6.0.1",
|
||||
"typedoc": "^0.28.15",
|
||||
"typescript": "^5.9.2",
|
||||
"yaml": "^2.8.2",
|
||||
},
|
||||
},
|
||||
"apps/agent": {
|
||||
@@ -103,6 +107,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@0no-co/graphqlsp": "^1.15.2",
|
||||
"@ai-sdk/openai-compatible": "^2.0.35",
|
||||
"@eslint/compat": "^2.0.1",
|
||||
"@graphql-codegen/cli": "^6.1.1",
|
||||
"@graphql-codegen/client-preset": "^5.2.2",
|
||||
@@ -287,11 +292,11 @@
|
||||
|
||||
"@ai-sdk/openai": ["@ai-sdk/openai@3.0.30", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.15" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-YDht3t7TDyWKP+JYZp20VuYqSjyF2brHYh47GGFDUPf2wZiqNQ263ecL+quar2bP3GZ3BeQA8f0m2B7UwLPR+g=="],
|
||||
|
||||
"@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.30", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.15" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iTjumHf1/u4NhjXYFn/aONM2GId3/o7J1Lp5ql8FCbgIMyRwrmanR5xy1S3aaVkfTscuDvLTzWiy1mAbGzK3nQ=="],
|
||||
"@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.35", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-g3wA57IAQFb+3j4YuFndgkUdXyRETZVvbfAWM+UX7bZSxA3xjes0v3XKgIdKdekPtDGsh4ZX2byHD0gJIMPfiA=="],
|
||||
|
||||
"@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
|
||||
|
||||
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.15", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w=="],
|
||||
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.19", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-3eG55CrSWCu2SXlqq2QCsFjo3+E7+Gmg7i/oRVoSZzIodTuDSfLb3MRje67xE9RFea73Zao7Lm4mADIfUETKGg=="],
|
||||
|
||||
"@ai-sdk/react": ["@ai-sdk/react@3.0.99", "", { "dependencies": { "@ai-sdk/provider-utils": "4.0.15", "ai": "6.0.97", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-xMsp5br4Dpr/3BYq/jrE8q4YLgViU1KHVq8VB0+dzdLJFU3jKA83uoxpbWqzV/edQOBPgGBSb2CgmV5v77rvzA=="],
|
||||
|
||||
@@ -1997,6 +2002,8 @@
|
||||
|
||||
"@wxt-dev/browser": ["@wxt-dev/browser@0.1.37", "", { "dependencies": { "@types/filesystem": "*", "@types/har-format": "*" } }, "sha512-I32XWCNRy2W6UgbaVXz8BHGBGtm8urGRRBrcNLagUBXTrBi7wCE6zWePUvvK+nUl7qUCZ7iQ1ufdP0c1DEWisw=="],
|
||||
|
||||
"@wxt-dev/i18n": ["@wxt-dev/i18n@0.2.5", "", { "dependencies": { "@wxt-dev/browser": "^0.1.37", "chokidar": "^5.0.0", "confbox": "^0.1.8 || ^0.2.2", "fast-glob": "^3.3.3" }, "peerDependencies": { "wxt": ">=0.19.7" }, "optionalPeers": ["wxt"] }, "sha512-B9EwzR7eTIZv5HQSsQL9/NaBHXH7G/esEtxMCoBMQyHx6QWWJNMJLX2C4z8slQUbblvlt4bPa+/zIC1IrWc1LA=="],
|
||||
|
||||
"@wxt-dev/module-react": ["@wxt-dev/module-react@1.1.5", "", { "dependencies": { "@vitejs/plugin-react": "^4.4.1 || ^5.0.0" }, "peerDependencies": { "wxt": ">=0.19.16" } }, "sha512-KgsUrsgH5rBT8MwiipnDEOHBXmLvTIdFICrI7KjngqSf9DpVRn92HsKmToxY0AYpkP19hHWta2oNYFTzmmm++g=="],
|
||||
|
||||
"@wxt-dev/storage": ["@wxt-dev/storage@1.2.8", "", { "dependencies": { "@wxt-dev/browser": "^0.1.37", "async-mutex": "^0.5.0", "dequal": "^2.0.3" } }, "sha512-GWCFKgF5+d7eslOxUDFC70ypA9njupmJb1nQM8uZoX0J3sWT2BO5xJLzb1sYahWAfID9p2BMtnUBN1lkWxPsbQ=="],
|
||||
@@ -4417,8 +4424,24 @@
|
||||
|
||||
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
||||
|
||||
"@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.15", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w=="],
|
||||
|
||||
"@ai-sdk/amazon-bedrock/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
|
||||
|
||||
"@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.15", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w=="],
|
||||
|
||||
"@ai-sdk/azure/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.15", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w=="],
|
||||
|
||||
"@ai-sdk/gateway/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.15", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w=="],
|
||||
|
||||
"@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.15", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w=="],
|
||||
|
||||
"@ai-sdk/mcp/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.15", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w=="],
|
||||
|
||||
"@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.15", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w=="],
|
||||
|
||||
"@ai-sdk/react/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.15", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w=="],
|
||||
|
||||
"@aklinker1/rollup-plugin-visualizer/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
|
||||
|
||||
"@aklinker1/rollup-plugin-visualizer/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
@@ -4863,6 +4886,8 @@
|
||||
|
||||
"accepts/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
||||
|
||||
"ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.15", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w=="],
|
||||
|
||||
"antd/@ant-design/cssinjs": ["@ant-design/cssinjs@1.24.0", "", { "dependencies": { "@babel/runtime": "^7.11.1", "@emotion/hash": "^0.8.0", "@emotion/unitless": "^0.7.5", "classnames": "^2.3.1", "csstype": "^3.1.3", "rc-util": "^5.35.0", "stylis": "^4.3.4" }, "peerDependencies": { "react": ">=16.0.0", "react-dom": ">=16.0.0" } }, "sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg=="],
|
||||
|
||||
"antd/rc-collapse": ["rc-collapse@3.9.0", "", { "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "2.x", "rc-motion": "^2.3.4", "rc-util": "^5.27.0" }, "peerDependencies": { "react": ">=16.9.0", "react-dom": ">=16.9.0" } }, "sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA=="],
|
||||
|
||||
@@ -57,7 +57,8 @@
|
||||
"picocolors": "^1.1.1",
|
||||
"rimraf": "^6.0.1",
|
||||
"typedoc": "^0.28.15",
|
||||
"typescript": "^5.9.2"
|
||||
"typescript": "^5.9.2",
|
||||
"yaml": "^2.8.2"
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"lefthook"
|
||||
@@ -73,5 +74,8 @@
|
||||
"overrides": {
|
||||
"serialize-javascript": "7.0.3",
|
||||
"lodash-es": "4.17.23"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wxt-dev/i18n": "^0.2.5"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user