mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Revert "feat: Experiment: Show a bit of stats in Cloud tab to help users discover there's more in Cloud" (#8559)
This commit is contained in:
parent
cd8036d2d8
commit
eeaafef786
48 changed files with 146 additions and 695 deletions
|
|
@ -1,13 +1,6 @@
|
|||
import { z } from "zod"
|
||||
|
||||
import {
|
||||
type AuthService,
|
||||
type ShareVisibility,
|
||||
type ShareResponse,
|
||||
shareResponseSchema,
|
||||
type UsageStats,
|
||||
usageStatsSchema,
|
||||
} from "@roo-code/types"
|
||||
import { type AuthService, type ShareVisibility, type ShareResponse, shareResponseSchema } from "@roo-code/types"
|
||||
|
||||
import { getRooCodeApiUrl } from "./config.js"
|
||||
import { getUserAgent } from "./utils.js"
|
||||
|
|
@ -60,11 +53,9 @@ export class CloudAPI {
|
|||
})
|
||||
|
||||
if (!response.ok) {
|
||||
this.log(`[CloudAPI] Request to ${endpoint} failed with status ${response.status}`)
|
||||
await this.handleErrorResponse(response, endpoint)
|
||||
}
|
||||
|
||||
// Log before attempting to read the body
|
||||
const data = await response.json()
|
||||
|
||||
if (parseResponse) {
|
||||
|
|
@ -95,15 +86,9 @@ export class CloudAPI {
|
|||
let responseBody: unknown
|
||||
|
||||
try {
|
||||
const bodyText = await response.text()
|
||||
|
||||
try {
|
||||
responseBody = JSON.parse(bodyText)
|
||||
} catch {
|
||||
responseBody = bodyText
|
||||
}
|
||||
} catch (_error) {
|
||||
responseBody = "Failed to read error response"
|
||||
responseBody = await response.json()
|
||||
} catch {
|
||||
responseBody = await response.text()
|
||||
}
|
||||
|
||||
switch (response.status) {
|
||||
|
|
@ -124,12 +109,15 @@ export class CloudAPI {
|
|||
}
|
||||
|
||||
async shareTask(taskId: string, visibility: ShareVisibility = "organization"): Promise<ShareResponse> {
|
||||
this.log(`[CloudAPI] Sharing task ${taskId} with visibility: ${visibility}`)
|
||||
|
||||
const response = await this.request("/api/extension/share", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ taskId, visibility }),
|
||||
parseResponse: (data) => shareResponseSchema.parse(data),
|
||||
})
|
||||
|
||||
this.log("[CloudAPI] Share response:", response)
|
||||
return response
|
||||
}
|
||||
|
||||
|
|
@ -146,14 +134,4 @@ export class CloudAPI {
|
|||
.parse(data),
|
||||
})
|
||||
}
|
||||
|
||||
async getUsagePreview(): Promise<UsageStats> {
|
||||
const response = await this.request("/api/analytics/usage/daily?period=7", {
|
||||
method: "GET",
|
||||
parseResponse: (data) => {
|
||||
return usageStatsSchema.parse(data)
|
||||
},
|
||||
})
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3110,68 +3110,5 @@ export const webviewMessageHandler = async (
|
|||
})
|
||||
break
|
||||
}
|
||||
case "getUsagePreview": {
|
||||
try {
|
||||
// Get the CloudAPI instance and fetch usage preview
|
||||
const cloudApi = CloudService.instance.cloudAPI
|
||||
if (!cloudApi) {
|
||||
// User is not authenticated
|
||||
provider.log("[webviewMessageHandler] User not authenticated for usage preview")
|
||||
await provider.postMessageToWebview({
|
||||
type: "usagePreviewData",
|
||||
error: "Authentication required",
|
||||
data: null,
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
// Fetch usage preview data
|
||||
const rawUsageData = await cloudApi.getUsagePreview()
|
||||
|
||||
// Transform the data to match UI expectations
|
||||
// The API returns data with separate arrays, but UI expects an array of day objects
|
||||
const dates = rawUsageData.data?.dates ?? []
|
||||
const tasks = rawUsageData.data?.tasks ?? []
|
||||
const tokens = rawUsageData.data?.tokens ?? []
|
||||
const costs = rawUsageData.data?.costs ?? []
|
||||
const len = Math.min(dates.length, tasks.length, tokens.length, costs.length)
|
||||
|
||||
const transformedData = {
|
||||
days: Array.from({ length: len }).map((_, index) => ({
|
||||
date: dates[index] ?? "",
|
||||
taskCount: tasks[index] ?? 0,
|
||||
tokenCount: tokens[index] ?? 0,
|
||||
cost: costs[index] ?? 0,
|
||||
})),
|
||||
totals: rawUsageData.data?.totals || {
|
||||
tasks: 0,
|
||||
tokens: 0,
|
||||
cost: 0,
|
||||
},
|
||||
}
|
||||
|
||||
// Send the transformed data back to the webview
|
||||
await provider.postMessageToWebview({
|
||||
type: "usagePreviewData",
|
||||
data: transformedData,
|
||||
error: undefined,
|
||||
})
|
||||
} catch (error) {
|
||||
provider.log(
|
||||
`[webviewMessageHandler] Failed to fetch usage preview: ${error instanceof Error ? error.message : String(error)}`,
|
||||
)
|
||||
provider.log(
|
||||
`[webviewMessageHandler] Error stack trace: ${error instanceof Error ? error.stack : "No stack trace"}`,
|
||||
)
|
||||
|
||||
// Send error back to webview
|
||||
await provider.postMessageToWebview({
|
||||
type: "usagePreviewData",
|
||||
error: error instanceof Error ? error.message : "Failed to load usage data",
|
||||
data: null,
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,7 +126,6 @@ export interface ExtensionMessage {
|
|||
| "insertTextIntoTextarea"
|
||||
| "dismissedUpsells"
|
||||
| "organizationSwitchResult"
|
||||
| "usagePreviewData"
|
||||
text?: string
|
||||
payload?: any // Add a generic payload for now, can refine later
|
||||
action?:
|
||||
|
|
@ -206,7 +205,6 @@ export interface ExtensionMessage {
|
|||
queuedMessages?: QueuedMessage[]
|
||||
list?: string[] // For dismissedUpsells
|
||||
organizationId?: string | null // For organizationSwitchResult
|
||||
data?: any // For usagePreviewData
|
||||
}
|
||||
|
||||
export type ExtensionState = Pick<
|
||||
|
|
|
|||
|
|
@ -229,7 +229,6 @@ export interface WebviewMessage {
|
|||
| "editQueuedMessage"
|
||||
| "dismissUpsell"
|
||||
| "getDismissedUpsells"
|
||||
| "getUsagePreview"
|
||||
text?: string
|
||||
editedMessageContent?: string
|
||||
tab?: "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "cloud"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import TaskHeader from "@src/components/chat/TaskHeader"
|
|||
// Mock formatLargeNumber function
|
||||
vi.mock("@/utils/format", () => ({
|
||||
formatLargeNumber: vi.fn((num) => num.toString()),
|
||||
formatCost: (cost: number) => `$${cost.toFixed(2)}`,
|
||||
}))
|
||||
|
||||
// Mock VSCodeBadge component for all tests
|
||||
|
|
@ -129,7 +128,12 @@ describe("ContextWindowProgress", () => {
|
|||
expect(windowSize).toBeInTheDocument()
|
||||
expect(windowSize).toHaveTextContent("4000")
|
||||
|
||||
const progressBarContainer = screen.getByTestId("context-progress-bar-container").parentElement
|
||||
// The progress bar is now wrapped in tooltips, but we can verify the structure exists
|
||||
// by checking for the progress bar container
|
||||
const progressBarContainer = screen.getByTestId("context-tokens-count").parentElement
|
||||
expect(progressBarContainer).toBeInTheDocument()
|
||||
|
||||
// Verify the flex container has the expected structure
|
||||
expect(progressBarContainer?.querySelector(".flex-1.relative")).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -132,8 +132,7 @@ export const ChatRowContent = ({
|
|||
}: ChatRowContentProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { mcpServers, alwaysAllowMcp, currentCheckpoint, mode, apiConfiguration, cloudIsAuthenticated } =
|
||||
useExtensionState()
|
||||
const { mcpServers, alwaysAllowMcp, currentCheckpoint, mode, apiConfiguration } = useExtensionState()
|
||||
const { info: model } = useSelectedModel(apiConfiguration)
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
const [editedContent, setEditedContent] = useState("")
|
||||
|
|
@ -1075,17 +1074,8 @@ export const ChatRowContent = ({
|
|||
{title}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
"text-xs text-vscode-dropdown-foreground border-vscode-dropdown-border/50 border px-1.5 py-0.5 rounded-lg",
|
||||
cloudIsAuthenticated &&
|
||||
"cursor-pointer hover:bg-vscode-dropdown-background hover:border-vscode-dropdown-border transition-colors",
|
||||
)}
|
||||
style={{ opacity: cost !== null && cost !== undefined && cost > 0 ? 1 : 0 }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation() // Prevent parent onClick from firing
|
||||
vscode.postMessage({ type: "switchTab", tab: "cloud" })
|
||||
}}
|
||||
title={t("chat:apiRequest.viewTokenUsage")}>
|
||||
className="text-xs text-vscode-dropdown-foreground border-vscode-dropdown-border/50 border px-1.5 py-0.5 rounded-lg"
|
||||
style={{ opacity: cost !== null && cost !== undefined && cost > 0 ? 1 : 0 }}>
|
||||
${Number(cost || 0)?.toFixed(4)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -60,9 +60,7 @@ export const ContextWindowProgress = ({ contextWindow, contextTokens, maxTokens
|
|||
<StandardTooltip content={tooltipContent} side="top" sideOffset={8}>
|
||||
<div className="flex-1 relative">
|
||||
{/* Main progress bar container */}
|
||||
<div
|
||||
data-testid="context-progress-bar-container"
|
||||
className="flex items-center h-1 rounded-[2px] overflow-hidden w-full bg-[color-mix(in_srgb,var(--vscode-foreground)_20%,transparent)]">
|
||||
<div className="flex items-center h-1 rounded-[2px] overflow-hidden w-full bg-[color-mix(in_srgb,var(--vscode-foreground)_20%,transparent)]">
|
||||
{/* Current tokens container */}
|
||||
<div
|
||||
className="relative h-full"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { useTranslation } from "react-i18next"
|
|||
import { useCloudUpsell } from "@src/hooks/useCloudUpsell"
|
||||
import { CloudUpsellDialog } from "@src/components/cloud/CloudUpsellDialog"
|
||||
import DismissibleUpsell from "@src/components/common/DismissibleUpsell"
|
||||
import { FoldVertical, ChevronUp, ChevronDown, ChartColumn } from "lucide-react"
|
||||
import { FoldVertical, ChevronUp, ChevronDown } from "lucide-react"
|
||||
import prettyBytes from "pretty-bytes"
|
||||
|
||||
import type { ClineMessage } from "@roo-code/types"
|
||||
|
|
@ -11,7 +11,7 @@ import type { ClineMessage } from "@roo-code/types"
|
|||
import { getModelMaxOutputTokens } from "@roo/api"
|
||||
import { findLastIndex } from "@roo/array"
|
||||
|
||||
import { formatCost, formatLargeNumber } from "@src/utils/format"
|
||||
import { formatLargeNumber } from "@src/utils/format"
|
||||
import { cn } from "@src/lib/utils"
|
||||
import { StandardTooltip } from "@src/components/ui"
|
||||
import { useExtensionState } from "@src/context/ExtensionStateContext"
|
||||
|
|
@ -24,8 +24,6 @@ import { ContextWindowProgress } from "./ContextWindowProgress"
|
|||
import { Mention } from "./Mention"
|
||||
import { TodoListDisplay } from "./TodoListDisplay"
|
||||
|
||||
import { vscode } from "@src/utils/vscode"
|
||||
|
||||
export interface TaskHeaderProps {
|
||||
task: ClineMessage
|
||||
tokensIn: number
|
||||
|
|
@ -303,19 +301,7 @@ const TaskHeader = ({
|
|||
{t("chat:task.apiCost")}
|
||||
</th>
|
||||
<td className="align-top">
|
||||
<span>{formatCost(totalCost)}</span>
|
||||
<StandardTooltip content={t("chat:apiRequest.viewTokenUsage")}>
|
||||
<ChartColumn
|
||||
className="inline size-3.5 -mt-0.5 ml-2 text-vscode-textLink-foreground cursor-pointer hover:text-vscode-textLink-activeForeground transition-colors"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
vscode.postMessage({
|
||||
type: "switchTab",
|
||||
tab: "cloud",
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</StandardTooltip>
|
||||
<span>${totalCost?.toFixed(2)}</span>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -88,15 +88,6 @@ vi.mock("@roo/array", () => ({
|
|||
},
|
||||
}))
|
||||
|
||||
// Mock the format utilities
|
||||
vi.mock("@/utils/format", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@/utils/format")>()
|
||||
return {
|
||||
...actual,
|
||||
formatCost: (cost: number) => `$${cost.toFixed(2)}`,
|
||||
}
|
||||
})
|
||||
|
||||
describe("TaskHeader", () => {
|
||||
const defaultProps: TaskHeaderProps = {
|
||||
task: { type: "say", ts: Date.now(), text: "Test task", images: [] },
|
||||
|
|
|
|||
|
|
@ -9,13 +9,12 @@ import { vscode } from "@src/utils/vscode"
|
|||
import { telemetryClient } from "@src/utils/TelemetryClient"
|
||||
import { ToggleSwitch } from "@/components/ui/toggle-switch"
|
||||
import { renderCloudBenefitsContent } from "./CloudUpsellDialog"
|
||||
import { CircleAlert, Lock, TriangleAlert } from "lucide-react"
|
||||
import { CircleAlert, Info, Lock, TriangleAlert } from "lucide-react"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Tab, TabContent, TabHeader } from "../common/Tab"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { OrganizationSwitcher } from "./OrganizationSwitcher"
|
||||
import { StandardTooltip } from "../ui"
|
||||
import { UsagePreview } from "./UsagePreview"
|
||||
|
||||
// Define the production URL constant locally to avoid importing from cloud package in tests
|
||||
const PRODUCTION_ROO_CODE_API_URL = "https://app.roocode.com"
|
||||
|
|
@ -146,14 +145,6 @@ export const CloudView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone, orga
|
|||
}
|
||||
}
|
||||
|
||||
const handleViewUsageStats = () => {
|
||||
const baseUrl = cloudApiUrl || PRODUCTION_ROO_CODE_API_URL
|
||||
vscode.postMessage({
|
||||
type: "openExternal",
|
||||
url: `${baseUrl}/usage?utm_source=extension&utm_medium=stats_preview&utm_campaign=stats_preview`,
|
||||
})
|
||||
}
|
||||
|
||||
const handleRemoteControlToggle = () => {
|
||||
const newValue = !remoteControlEnabled
|
||||
setRemoteControlEnabled(newValue)
|
||||
|
|
@ -173,128 +164,119 @@ export const CloudView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone, orga
|
|||
<Button onClick={onDone}>{t("settings:common.done")}</Button>
|
||||
</TabHeader>
|
||||
|
||||
<TabContent className="flex flex-col justify-between min-h-full">
|
||||
<TabContent className="pt-10">
|
||||
{isAuthenticated ? (
|
||||
<div className="flex flex-col h-full">
|
||||
{/* Content that scrolls */}
|
||||
<div className="">
|
||||
{userInfo && (
|
||||
<div className="flex items-start gap-4 ml-4 mb-6 flex-col min-[300px]:flex-row">
|
||||
{/* Avatar */}
|
||||
<div className="w-12 h-12 rounded-full overflow-hidden flex-shrink-0">
|
||||
{userInfo?.picture ? (
|
||||
<img
|
||||
src={userInfo.picture}
|
||||
alt={t("cloud:profilePicture")}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center bg-vscode-button-background text-vscode-button-foreground text-xl">
|
||||
{userInfo?.name?.charAt(0) || userInfo?.email?.charAt(0) || "?"}
|
||||
<>
|
||||
{userInfo && (
|
||||
<div className="flex flex-col items-start ml-4 mb-6">
|
||||
<div className="w-16 h-16 mb-3 rounded-full overflow-hidden">
|
||||
{userInfo?.picture ? (
|
||||
<img
|
||||
src={userInfo.picture}
|
||||
alt={t("cloud:profilePicture")}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center bg-vscode-button-background text-vscode-button-foreground text-xl">
|
||||
{userInfo?.name?.charAt(0) || userInfo?.email?.charAt(0) || "?"}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{userInfo.name && (
|
||||
<h2 className="text-lg font-medium text-vscode-foreground my-0">{userInfo.name}</h2>
|
||||
)}
|
||||
{userInfo?.email && (
|
||||
<p className="text-sm text-vscode-descriptionForeground my-0">{userInfo?.email}</p>
|
||||
)}
|
||||
|
||||
{/* Organization Switcher - moved below email */}
|
||||
<div className="w-full max-w-60 mt-4">
|
||||
<OrganizationSwitcher
|
||||
userInfo={userInfo}
|
||||
organizations={organizations}
|
||||
cloudApiUrl={cloudApiUrl}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Task Sync Toggle - Always shown when authenticated */}
|
||||
<div className="mt-4 p-4 border-b border-t border-vscode-widget-border pl-4 max-w-140">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<ToggleSwitch
|
||||
checked={taskSyncEnabled}
|
||||
onChange={handleTaskSyncToggle}
|
||||
size="medium"
|
||||
aria-label={t("cloud:taskSync")}
|
||||
data-testid="task-sync-toggle"
|
||||
disabled={!!userInfo?.organizationId}
|
||||
/>
|
||||
<span className="font-medium text-vscode-foreground flex items-center">
|
||||
{t("cloud:taskSync")}
|
||||
{userInfo?.organizationId && (
|
||||
<StandardTooltip content={t("cloud:taskSyncManagedByOrganization")}>
|
||||
<div className="bg-vscode-badge-background text-vscode-badge-foreground/80 p-1.5 ml-2 -mb-2 relative -top-1 rounded-full inline-block cursor-help">
|
||||
<Lock className="size-3 block" />
|
||||
</div>
|
||||
</StandardTooltip>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-vscode-descriptionForeground text-sm mt-1 ml-8">
|
||||
{t("cloud:taskSyncDescription")}
|
||||
</div>
|
||||
|
||||
{/* Remote Control Toggle - Only shown when both extensionBridgeEnabled and featureRoomoteControlEnabled are true */}
|
||||
{userInfo?.extensionBridgeEnabled && featureRoomoteControlEnabled && (
|
||||
<>
|
||||
<div className="flex items-center gap-3 mt-4 mb-2">
|
||||
<ToggleSwitch
|
||||
checked={remoteControlEnabled}
|
||||
onChange={handleRemoteControlToggle}
|
||||
size="medium"
|
||||
aria-label={t("cloud:remoteControl")}
|
||||
data-testid="remote-control-toggle"
|
||||
disabled={!taskSyncEnabled}
|
||||
/>
|
||||
<span className="font-medium text-vscode-foreground">
|
||||
{t("cloud:remoteControl")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-vscode-descriptionForeground text-sm mt-1 mb-2 ml-8">
|
||||
{t("cloud:remoteControlDescription")}
|
||||
{!taskSyncEnabled && (
|
||||
<div className="text-vscode-editorWarning-foreground mt-2">
|
||||
<CircleAlert className="inline size-3 mr-1 mb-0.5 text-vscode-editorWarning-foreground" />
|
||||
{t("cloud:remoteControlRequiresTaskSync")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Name, email and org switcher */}
|
||||
<div className="flex flex-col">
|
||||
{userInfo.name && (
|
||||
<h2 className="text-lg font-medium text-vscode-foreground my-0">
|
||||
{userInfo.name}
|
||||
</h2>
|
||||
)}
|
||||
{userInfo?.email && (
|
||||
<p className="text-sm text-vscode-descriptionForeground my-0 mb-2">
|
||||
{userInfo?.email}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Organization Switcher */}
|
||||
<div className="max-w-60">
|
||||
<OrganizationSwitcher
|
||||
userInfo={userInfo}
|
||||
organizations={organizations}
|
||||
cloudApiUrl={cloudApiUrl}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Task Sync Toggle - Always shown when authenticated */}
|
||||
<div className="mt-4 p-4 border-b border-t border-vscode-panel-border pl-4 max-w-140">
|
||||
<div className="flex items-center gap-3">
|
||||
<ToggleSwitch
|
||||
checked={taskSyncEnabled}
|
||||
onChange={handleTaskSyncToggle}
|
||||
size="medium"
|
||||
aria-label={t("cloud:taskSync")}
|
||||
data-testid="task-sync-toggle"
|
||||
disabled={!!userInfo?.organizationId}
|
||||
/>
|
||||
<span className="font-medium text-vscode-foreground flex items-center">
|
||||
{t("cloud:taskSync")}
|
||||
{userInfo?.organizationId && (
|
||||
<StandardTooltip content={t("cloud:taskSyncManagedByOrganization")}>
|
||||
<div className="bg-vscode-badge-background text-vscode-badge-foreground/80 p-1.5 ml-2 -mb-2 relative -top-1 rounded-full inline-block cursor-help">
|
||||
<Lock className="size-3 block" />
|
||||
</div>
|
||||
</StandardTooltip>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-vscode-descriptionForeground text-sm mt-1 ml-8">
|
||||
{t("cloud:taskSyncDescription")}
|
||||
</div>
|
||||
|
||||
{/* Remote Control Toggle - Only shown when both extensionBridgeEnabled and featureRoomoteControlEnabled are true */}
|
||||
{userInfo?.extensionBridgeEnabled && featureRoomoteControlEnabled && (
|
||||
<>
|
||||
<div className="flex items-center gap-3 mt-4">
|
||||
<ToggleSwitch
|
||||
checked={remoteControlEnabled}
|
||||
onChange={handleRemoteControlToggle}
|
||||
size="medium"
|
||||
aria-label={t("cloud:remoteControl")}
|
||||
data-testid="remote-control-toggle"
|
||||
disabled={!taskSyncEnabled}
|
||||
/>
|
||||
<span className="font-medium text-vscode-foreground">
|
||||
{t("cloud:remoteControl")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-vscode-descriptionForeground text-sm mt-1 mb-2 ml-8">
|
||||
{t("cloud:remoteControlDescription")}
|
||||
{!taskSyncEnabled && (
|
||||
<div className="text-vscode-editorWarning-foreground mt-2">
|
||||
<CircleAlert className="inline size-3 mr-1 mb-0.5 text-vscode-editorWarning-foreground" />
|
||||
{t("cloud:remoteControlRequiresTaskSync")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Usage Stats Chart Section */}
|
||||
<div className="mt-4 mb-4 px-4">
|
||||
<UsagePreview onViewDetails={handleViewUsageStats} />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2 mt-4 px-4">
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
onClick={handleVisitCloudWebsite}
|
||||
className="w-full">
|
||||
{t("cloud:visitCloudWebsite")}
|
||||
</VSCodeButton>
|
||||
<VSCodeButton appearance="secondary" onClick={handleLogoutClick} className="w-full">
|
||||
{t("cloud:logOut")}
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-vscode-descriptionForeground text-sm mt-4 mb-8 pl-4">
|
||||
<Info className="inline size-3 mr-1 mb-0.5 text-vscode-descriptionForeground" />
|
||||
{t("cloud:usageMetricsAlwaysReported")}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2 mt-4 pl-4">
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
onClick={handleVisitCloudWebsite}
|
||||
className="w-full max-w-80">
|
||||
{t("cloud:visitCloudWebsite")}
|
||||
</VSCodeButton>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
onClick={handleLogoutClick}
|
||||
className="w-full max-w-80">
|
||||
{t("cloud:logOut")}
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div>
|
||||
<>
|
||||
<div className="flex flex-col items-start gap-4 px-8 max-w-100">
|
||||
<div className={cn(authInProgress && "opacity-50")}>{renderCloudBenefitsContent(t)}</div>
|
||||
|
||||
|
|
@ -347,7 +329,7 @@ export const CloudView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone, orga
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{cloudApiUrl && cloudApiUrl !== PRODUCTION_ROO_CODE_API_URL && (
|
||||
<div className="ml-4 mt-6 flex">
|
||||
|
|
|
|||
|
|
@ -1,183 +0,0 @@
|
|||
import { useEffect, useState } from "react"
|
||||
import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"
|
||||
import { CircleAlert, SquareArrowOutUpRight } from "lucide-react"
|
||||
import { useAppTranslation } from "@src/i18n/TranslationContext"
|
||||
import { vscode } from "@src/utils/vscode"
|
||||
import { formatDateShort, formatLargeNumber, formatCost } from "@/utils/format"
|
||||
|
||||
interface DailyUsage {
|
||||
date: string // ISO date string
|
||||
taskCount: number
|
||||
tokenCount: number
|
||||
cost: number // in USD
|
||||
}
|
||||
|
||||
interface UsageStats {
|
||||
days: DailyUsage[]
|
||||
totals: {
|
||||
tasks: number
|
||||
tokens: number
|
||||
cost: number
|
||||
}
|
||||
}
|
||||
|
||||
interface UsagePreviewProps {
|
||||
onViewDetails: () => void
|
||||
}
|
||||
|
||||
export const UsagePreview = ({ onViewDetails }: UsagePreviewProps) => {
|
||||
const { t } = useAppTranslation()
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [data, setData] = useState<UsageStats | null>(null)
|
||||
|
||||
// Fetch usage data on mount
|
||||
useEffect(() => {
|
||||
setIsLoading(true)
|
||||
setError(null)
|
||||
|
||||
// Request usage preview data from the extension
|
||||
vscode.postMessage({ type: "getUsagePreview" })
|
||||
|
||||
// Listen for the response
|
||||
let timeoutId: ReturnType<typeof setTimeout> | null = null
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
const message = event.data
|
||||
|
||||
if (message.type === "usagePreviewData") {
|
||||
// Clear timeout on success/error to avoid stale timeout flipping UI into error
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId)
|
||||
}
|
||||
|
||||
if (message.error) {
|
||||
setError(message.error)
|
||||
} else if (message.data) {
|
||||
// Validate the data structure
|
||||
if (!message.data.days || !Array.isArray(message.data.days)) {
|
||||
setError(t("cloud:usagePreview.invalidDataFormat"))
|
||||
} else {
|
||||
setData(message.data)
|
||||
}
|
||||
}
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("message", handleMessage)
|
||||
|
||||
// Clean up listener after 10 seconds (timeout)
|
||||
timeoutId = setTimeout(() => {
|
||||
if (isLoading) {
|
||||
setError(t("cloud:usagePreview.failedToLoad"))
|
||||
setIsLoading(false)
|
||||
}
|
||||
}, 10000)
|
||||
|
||||
return () => {
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId)
|
||||
}
|
||||
window.removeEventListener("message", handleMessage)
|
||||
}
|
||||
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const getBarHeight = (cost: number): number => {
|
||||
if (!data || !data.days || data.days.length === 0) return 1
|
||||
const maxCost = Math.max(...data.days.map((d) => d.cost))
|
||||
if (!Number.isFinite(maxCost) || maxCost <= 0) return 1
|
||||
// Compute percentage first, then round; enforce minimum height for visibility
|
||||
return Math.max(1, Math.round((cost / maxCost) * 100))
|
||||
}
|
||||
|
||||
// Retry loading
|
||||
const handleRetry = () => {
|
||||
setError(null)
|
||||
setIsLoading(true)
|
||||
vscode.postMessage({ type: "getUsagePreview" })
|
||||
}
|
||||
|
||||
// Loading state
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div
|
||||
className="cursor-pointer group rounded-lg bg-vscode-editor-background hover:bg-vscode-list-hoverBackground transition-colors relative"
|
||||
onClick={onViewDetails}>
|
||||
<div className="p-4">
|
||||
{/* Loading spinner centered in chart area */}
|
||||
<div className="h-20 flex items-center justify-center mb-3">
|
||||
<VSCodeProgressRing className="size-6" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Error state
|
||||
if (error || !data) {
|
||||
return (
|
||||
<div
|
||||
className="cursor-pointer group rounded-lg bg-vscode-editor-background hover:bg-vscode-list-hoverBackground transition-colors relative"
|
||||
onClick={handleRetry}>
|
||||
<div className="p-4">
|
||||
{/* Error message in chart area */}
|
||||
<div className="mb-3 text-vscode-descriptionForeground">
|
||||
<CircleAlert className="size-4 mb-2 text-vscode-muted-foreground" />
|
||||
<p className="text-xs font-mono font-bold">{t("cloud:usagePreview.couldNotLoadChart")}</p>
|
||||
<p className="text-xs font-mono">{error}</p>
|
||||
<p className="text-xs font-medium mt-1">{t("cloud:usagePreview.clickToRetry")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="cursor-pointer group rounded-lg bg-vscode-editor-background relative" onClick={onViewDetails}>
|
||||
<div className="p-4">
|
||||
{/* Chart with daily usage bars */}
|
||||
<div
|
||||
className="h-24 min-[450px]:h-40 rounded mb-3 flex items-end gap-1 pb-2"
|
||||
role="img"
|
||||
aria-label={t("cloud:usagePreview.costPastDays", { count: data.days.length })}>
|
||||
{data &&
|
||||
Array.isArray(data.days) &&
|
||||
data.days.map((day, index) => (
|
||||
<div key={index} className="w-full flex flex-col items-center justify-end h-full">
|
||||
<div
|
||||
className="w-full rounded-t-xs transition-all bg-vscode-button-background"
|
||||
style={{ height: `${getBarHeight(day.cost)}%` }}
|
||||
aria-label={`${formatDateShort(new Date(day.date).getTime())}: ${formatCost(day.cost)}`}
|
||||
/>
|
||||
<span className="text-[9px] h-[1em] hidden min-[300px]:block overflow-clip text-center text-muted-foreground mt-0.5">
|
||||
{formatDateShort(new Date(day.date).getTime())}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Stats text */}
|
||||
<div className="flex flex-col justify-between text-sm min-[400px]:flex-row min-[450px]:items-center">
|
||||
<span className="flex items-center gap-1 text-vscode-descriptionForeground">
|
||||
{t("cloud:usagePreview.costPastDays", { count: data.days.length })}
|
||||
</span>
|
||||
<span className="text-vscode-foreground">
|
||||
{t("cloud:usagePreview.tasks", { count: data.totals.tasks })}
|
||||
<span> · </span>
|
||||
{t("cloud:usagePreview.tokens", { count: formatLargeNumber(data.totals.tokens) })}
|
||||
<span> · </span>
|
||||
{formatCost(data.totals.cost)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Hover overlay */}
|
||||
<div className="absolute inset-0 bg-vscode-editor-background/85 rounded-lg flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none">
|
||||
<div className="flex items-center gap-2 text-vscode-foreground">
|
||||
<span>{t("cloud:usagePreview.seeMoreStats")}</span>
|
||||
<SquareArrowOutUpRight className="size-3" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
3
webview-ui/src/i18n/locales/ca/chat.json
generated
3
webview-ui/src/i18n/locales/ca/chat.json
generated
|
|
@ -141,8 +141,7 @@
|
|||
"failed": "Sol·licitud API ha fallat",
|
||||
"streaming": "Sol·licitud API...",
|
||||
"cancelled": "Sol·licitud API cancel·lada",
|
||||
"streamingFailed": "Transmissió API ha fallat",
|
||||
"viewTokenUsage": "Veure les estadístiques d'ús de fitxes"
|
||||
"streamingFailed": "Transmissió API ha fallat"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "Punt de control",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/ca/cloud.json
generated
11
webview-ui/src/i18n/locales/ca/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"personalAccount": "Compte Personal",
|
||||
"createTeamAccount": "Crear Compte d'Equip",
|
||||
"switchAccount": "Canviar Compte de Roo Code Cloud",
|
||||
"usagePreview": {
|
||||
"costPastDays": "Cost diari dels darrers {{count}} dies",
|
||||
"seeMoreStats": "Consulta més estadístiques a Roo Code Cloud",
|
||||
"invalidDataFormat": "S'ha rebut un format de dades no vàlid",
|
||||
"failedToLoad": "No s'han pogut carregar les dades d'ús",
|
||||
"couldNotLoadChart": "No s'ha pogut carregar el gràfic:",
|
||||
"clickToRetry": "Fes clic per tornar a intentar-ho",
|
||||
"tasks_one": "{{count}} tasca",
|
||||
"tasks_other": "{{count}} tasques",
|
||||
"tokens": "{{count}} tokens"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "Donant-li una mica d'independència a Roo? Controla'l des de qualsevol lloc amb Roo Code Cloud. <learnMoreLink>Més informació</learnMoreLink>.",
|
||||
"longRunningTask": "Això pot trigar una estona. Continua des de qualsevol lloc amb Cloud.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/de/chat.json
generated
3
webview-ui/src/i18n/locales/de/chat.json
generated
|
|
@ -141,8 +141,7 @@
|
|||
"failed": "API-Anfrage fehlgeschlagen",
|
||||
"streaming": "API-Anfrage...",
|
||||
"cancelled": "API-Anfrage abgebrochen",
|
||||
"streamingFailed": "API-Streaming fehlgeschlagen",
|
||||
"viewTokenUsage": "Token-Nutzungsstatistiken anzeigen"
|
||||
"streamingFailed": "API-Streaming fehlgeschlagen"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "Checkpoint",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/de/cloud.json
generated
11
webview-ui/src/i18n/locales/de/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"switchAccount": "Roo Code Cloud Konto wechseln",
|
||||
"createTeamAccount": "Team-Konto erstellen",
|
||||
"cloudUrlPillLabel": "Roo Code Cloud URL",
|
||||
"usagePreview": {
|
||||
"costPastDays": "Tägliche Kosten der letzten {{count}} Tage",
|
||||
"seeMoreStats": "Weitere Statistiken in Roo Code Cloud anzeigen",
|
||||
"invalidDataFormat": "Ungültiges Datenformat empfangen",
|
||||
"failedToLoad": "Nutzungsdaten konnten nicht geladen werden",
|
||||
"couldNotLoadChart": "Diagramm konnte nicht geladen werden:",
|
||||
"clickToRetry": "Klicken, um es erneut zu versuchen",
|
||||
"tasks_one": "{{count}} Aufgabe",
|
||||
"tasks_other": "{{count}} Aufgaben",
|
||||
"tokens": "{{count}} Tokens"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "Roo etwas Unabhängigkeit geben? Kontrolliere es von überall mit Roo Code Cloud. <learnMoreLink>Mehr erfahren</learnMoreLink>.",
|
||||
"longRunningTask": "Das könnte eine Weile dauern. Mit Cloud von überall weitermachen.",
|
||||
|
|
|
|||
|
|
@ -147,8 +147,7 @@
|
|||
"failed": "API Request Failed",
|
||||
"streaming": "API Request...",
|
||||
"cancelled": "API Request Cancelled",
|
||||
"streamingFailed": "API Streaming Failed",
|
||||
"viewTokenUsage": "View token usage stats"
|
||||
"streamingFailed": "API Streaming Failed"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "Checkpoint",
|
||||
|
|
|
|||
|
|
@ -26,17 +26,6 @@
|
|||
"personalAccount": "Personal Account",
|
||||
"switchAccount": "Switch Roo Code Cloud Account",
|
||||
"createTeamAccount": "Create Team Account",
|
||||
"usagePreview": {
|
||||
"costPastDays": "Daily cost past {{count}} days",
|
||||
"seeMoreStats": "See more stats in Roo Code Cloud",
|
||||
"invalidDataFormat": "Invalid data format received",
|
||||
"failedToLoad": "Failed to load usage data",
|
||||
"couldNotLoadChart": "Couldn't load chart:",
|
||||
"clickToRetry": "Click to retry",
|
||||
"tasks_one": "{{count}} task",
|
||||
"tasks_other": "{{count}} tasks",
|
||||
"tokens": "{{count}} tokens"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "Giving Roo some independence? Control it from anywhere with Roo Code Cloud. <learnMoreLink>Learn more</learnMoreLink>.",
|
||||
"longRunningTask": "This might take a while. Continue from anywhere with Cloud.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/es/chat.json
generated
3
webview-ui/src/i18n/locales/es/chat.json
generated
|
|
@ -141,8 +141,7 @@
|
|||
"failed": "Solicitud API falló",
|
||||
"streaming": "Solicitud API...",
|
||||
"cancelled": "Solicitud API cancelada",
|
||||
"streamingFailed": "Transmisión API falló",
|
||||
"viewTokenUsage": "Ver estadísticas de uso de tokens"
|
||||
"streamingFailed": "Transmisión API falló"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "Punto de control",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/es/cloud.json
generated
11
webview-ui/src/i18n/locales/es/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"switchAccount": "Cambiar Cuenta de Roo Code Cloud",
|
||||
"createTeamAccount": "Crear Cuenta de Equipo",
|
||||
"cloudUrlPillLabel": "URL de Roo Code Cloud",
|
||||
"usagePreview": {
|
||||
"costPastDays": "Costo diario de los últimos {{count}} días",
|
||||
"seeMoreStats": "Ver más estadísticas en Roo Code Cloud",
|
||||
"invalidDataFormat": "Formato de datos recibido no válido",
|
||||
"failedToLoad": "No se pudieron cargar los datos de uso",
|
||||
"couldNotLoadChart": "No se pudo cargar el gráfico:",
|
||||
"clickToRetry": "Haz clic para reintentar",
|
||||
"tasks_one": "{{count}} tarea",
|
||||
"tasks_other": "{{count}} tareas",
|
||||
"tokens": "{{count}} tokens"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "¿Dándole a Roo un poco de independencia? Contrólalo desde cualquier lugar con Roo Code Cloud. <learnMoreLink>Saber más</learnMoreLink>.",
|
||||
"longRunningTask": "Esto podría tardar un poco. Continúa desde cualquier lugar con la Nube.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/fr/chat.json
generated
3
webview-ui/src/i18n/locales/fr/chat.json
generated
|
|
@ -141,8 +141,7 @@
|
|||
"failed": "Échec de la requête API",
|
||||
"streaming": "Requête API...",
|
||||
"cancelled": "Requête API annulée",
|
||||
"streamingFailed": "Échec du streaming API",
|
||||
"viewTokenUsage": "Voir les statistiques d'utilisation des tokens"
|
||||
"streamingFailed": "Échec du streaming API"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "Point de contrôle",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/fr/cloud.json
generated
11
webview-ui/src/i18n/locales/fr/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"switchAccount": "Changer de Compte Roo Code Cloud",
|
||||
"createTeamAccount": "Créer un Compte d'Équipe",
|
||||
"cloudUrlPillLabel": "URL de Roo Code Cloud",
|
||||
"usagePreview": {
|
||||
"costPastDays": "Coût quotidien des {{count}} derniers jours",
|
||||
"seeMoreStats": "Voir plus de statistiques dans Roo Code Cloud",
|
||||
"invalidDataFormat": "Format de données reçu invalide",
|
||||
"failedToLoad": "Échec du chargement des données d'utilisation",
|
||||
"couldNotLoadChart": "Impossible de charger le graphique :",
|
||||
"clickToRetry": "Cliquez pour réessayer",
|
||||
"tasks_one": "{{count}} tâche",
|
||||
"tasks_other": "{{count}} tâches",
|
||||
"tokens": "{{count}} jetons"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "Donner à Roo un peu d'indépendance ? Contrôlez-le de n'importe où avec Roo Code Cloud. <learnMoreLink>En savoir plus</learnMoreLink>.",
|
||||
"longRunningTask": "Cela peut prendre un certain temps. Continuez de n'importe où avec le Cloud.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/hi/chat.json
generated
3
webview-ui/src/i18n/locales/hi/chat.json
generated
|
|
@ -141,8 +141,7 @@
|
|||
"failed": "API अनुरोध विफल हुआ",
|
||||
"streaming": "API अनुरोध...",
|
||||
"cancelled": "API अनुरोध रद्द किया गया",
|
||||
"streamingFailed": "API स्ट्रीमिंग विफल हुई",
|
||||
"viewTokenUsage": "टोकन उपयोग आँकड़े देखें"
|
||||
"streamingFailed": "API स्ट्रीमिंग विफल हुई"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "चेकपॉइंट",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/hi/cloud.json
generated
11
webview-ui/src/i18n/locales/hi/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"switchAccount": "Roo Code Cloud खाता बदलें",
|
||||
"createTeamAccount": "टीम खाता बनाएं",
|
||||
"cloudUrlPillLabel": "Roo Code Cloud URL",
|
||||
"usagePreview": {
|
||||
"costPastDays": "पिछले {{count}} दिनों की दैनिक लागत",
|
||||
"seeMoreStats": "रू कोड क्लाउड में और आँकड़े देखें",
|
||||
"invalidDataFormat": "अमान्य डेटा प्रारूप प्राप्त हुआ",
|
||||
"failedToLoad": "उपयोग डेटा लोड करने में विफल",
|
||||
"couldNotLoadChart": "चार्ट लोड नहीं हो सका:",
|
||||
"clickToRetry": "पुनः प्रयास करने के लिए क्लिक करें",
|
||||
"tasks_one": "{{count}} कार्य",
|
||||
"tasks_other": "{{count}} कार्य",
|
||||
"tokens": "{{count}} टोकन"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "रू को थोड़ी स्वतंत्रता दे रहे हैं? रू कोड क्लाउड के साथ इसे कहीं से भी नियंत्रित करें। <learnMoreLink>और जानें</learnMoreLink>।",
|
||||
"longRunningTask": "इसमें थोड़ा समय लग सकता है। क्लाउड के साथ कहीं से भी जारी रखें।",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/id/chat.json
generated
3
webview-ui/src/i18n/locales/id/chat.json
generated
|
|
@ -150,8 +150,7 @@
|
|||
"failed": "Permintaan API Gagal",
|
||||
"streaming": "Permintaan API...",
|
||||
"cancelled": "Permintaan API Dibatalkan",
|
||||
"streamingFailed": "Streaming API Gagal",
|
||||
"viewTokenUsage": "Lihat statistik penggunaan token"
|
||||
"streamingFailed": "Streaming API Gagal"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "Checkpoint",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/id/cloud.json
generated
11
webview-ui/src/i18n/locales/id/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"switchAccount": "Ganti Akun Roo Code Cloud",
|
||||
"createTeamAccount": "Buat Akun Tim",
|
||||
"cloudUrlPillLabel": "URL Roo Code Cloud",
|
||||
"usagePreview": {
|
||||
"costPastDays": "Biaya harian {{count}} hari terakhir",
|
||||
"seeMoreStats": "Lihat statistik lainnya di Roo Code Cloud",
|
||||
"invalidDataFormat": "Format data yang diterima tidak valid",
|
||||
"failedToLoad": "Gagal memuat data penggunaan",
|
||||
"couldNotLoadChart": "Tidak dapat memuat bagan:",
|
||||
"clickToRetry": "Klik untuk mencoba lagi",
|
||||
"tasks_one": "{{count}} tugas",
|
||||
"tasks_other": "{{count}} tugas",
|
||||
"tokens": "{{count}} token"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "Memberi Roo sedikit kebebasan? Kendalikan dari mana saja dengan Roo Code Cloud. <learnMoreLink>Pelajari lebih lanjut</learnMoreLink>.",
|
||||
"longRunningTask": "Ini mungkin akan memakan waktu cukup lama. Lanjutkan dari mana saja dengan Cloud.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/it/chat.json
generated
3
webview-ui/src/i18n/locales/it/chat.json
generated
|
|
@ -144,8 +144,7 @@
|
|||
"failed": "Richiesta API fallita",
|
||||
"streaming": "Richiesta API...",
|
||||
"cancelled": "Richiesta API annullata",
|
||||
"streamingFailed": "Streaming API fallito",
|
||||
"viewTokenUsage": "Visualizza le statistiche di utilizzo dei token"
|
||||
"streamingFailed": "Streaming API fallito"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "Checkpoint",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/it/cloud.json
generated
11
webview-ui/src/i18n/locales/it/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"switchAccount": "Cambia Account Roo Code Cloud",
|
||||
"createTeamAccount": "Crea Account del Team",
|
||||
"cloudUrlPillLabel": "URL di Roo Code Cloud",
|
||||
"usagePreview": {
|
||||
"costPastDays": "Costo giornaliero degli ultimi {{count}} giorni",
|
||||
"seeMoreStats": "Vedi altre statistiche in Roo Code Cloud",
|
||||
"invalidDataFormat": "Formato dati ricevuto non valido",
|
||||
"failedToLoad": "Impossibile caricare i dati di utilizzo",
|
||||
"couldNotLoadChart": "Impossibile caricare il grafico:",
|
||||
"clickToRetry": "Clicca per riprovare",
|
||||
"tasks_one": "{{count}} attività",
|
||||
"tasks_other": "{{count}} attività",
|
||||
"tokens": "{{count}} token"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "Vuoi dare un po' di indipendenza a Roo? Controllalo da qualsiasi luogo con Roo Code Cloud. <learnMoreLink>Scopri di più</learnMoreLink>.",
|
||||
"longRunningTask": "Potrebbe volerci un po' di tempo. Continua da qualsiasi luogo con il Cloud.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ja/chat.json
generated
3
webview-ui/src/i18n/locales/ja/chat.json
generated
|
|
@ -141,8 +141,7 @@
|
|||
"failed": "APIリクエスト失敗",
|
||||
"streaming": "APIリクエスト...",
|
||||
"cancelled": "APIリクエストキャンセル",
|
||||
"streamingFailed": "APIストリーミング失敗",
|
||||
"viewTokenUsage": "トークン使用状況の統計を表示"
|
||||
"streamingFailed": "APIストリーミング失敗"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "チェックポイント",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/ja/cloud.json
generated
11
webview-ui/src/i18n/locales/ja/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"switchAccount": "Roo Code Cloud アカウントを切り替え",
|
||||
"createTeamAccount": "チームアカウントを作成",
|
||||
"cloudUrlPillLabel": "Roo Code Cloud URL",
|
||||
"usagePreview": {
|
||||
"costPastDays": "過去{{count}}日間の日次コスト",
|
||||
"seeMoreStats": "Roo Code Cloudでさらに統計を見る",
|
||||
"invalidDataFormat": "無効なデータ形式を受信しました",
|
||||
"failedToLoad": "使用状況データの読み込みに失敗しました",
|
||||
"couldNotLoadChart": "チャートを読み込めませんでした:",
|
||||
"clickToRetry": "クリックして再試行",
|
||||
"tasks_one": "{{count}}タスク",
|
||||
"tasks_other": "{{count}}タスク",
|
||||
"tokens": "{{count}}トークン"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "Rooに少し独立性を与えませんか?Roo Code Cloudでどこからでもコントロールできます。<learnMoreLink>詳細</learnMoreLink>。",
|
||||
"longRunningTask": "これには時間がかかるかもしれません。Cloudを使えばどこからでも続けられます。",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ko/chat.json
generated
3
webview-ui/src/i18n/locales/ko/chat.json
generated
|
|
@ -141,8 +141,7 @@
|
|||
"failed": "API 요청 실패",
|
||||
"streaming": "API 요청...",
|
||||
"cancelled": "API 요청 취소됨",
|
||||
"streamingFailed": "API 스트리밍 실패",
|
||||
"viewTokenUsage": "토큰 사용 통계 보기"
|
||||
"streamingFailed": "API 스트리밍 실패"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "체크포인트",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/ko/cloud.json
generated
11
webview-ui/src/i18n/locales/ko/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"switchAccount": "Roo Code Cloud 계정 전환",
|
||||
"createTeamAccount": "팀 계정 만들기",
|
||||
"cloudUrlPillLabel": "Roo Code Cloud URL",
|
||||
"usagePreview": {
|
||||
"costPastDays": "지난 {{count}}일간의 일일 비용",
|
||||
"seeMoreStats": "Roo Code Cloud에서 더 많은 통계 보기",
|
||||
"invalidDataFormat": "잘못된 데이터 형식을 받았습니다",
|
||||
"failedToLoad": "사용 데이터를 로드하지 못했습니다",
|
||||
"couldNotLoadChart": "차트를 로드할 수 없습니다:",
|
||||
"clickToRetry": "클릭하여 다시 시도",
|
||||
"tasks_one": "{{count}}개 작업",
|
||||
"tasks_other": "{{count}}개 작업",
|
||||
"tokens": "{{count}}개 토큰"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "Roo에게 약간의 독립성을 부여하시겠습니까? Roo Code Cloud로 어디서든 제어하세요. <learnMoreLink>더 알아보기</learnMoreLink>.",
|
||||
"longRunningTask": "시간이 좀 걸릴 수 있습니다. Cloud로 어디서든 계속하세요.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/nl/chat.json
generated
3
webview-ui/src/i18n/locales/nl/chat.json
generated
|
|
@ -136,8 +136,7 @@
|
|||
"failed": "API-verzoek mislukt",
|
||||
"streaming": "API-verzoek...",
|
||||
"cancelled": "API-verzoek geannuleerd",
|
||||
"streamingFailed": "API-streaming mislukt",
|
||||
"viewTokenUsage": "Bekijk tokengebruikstatistieken"
|
||||
"streamingFailed": "API-streaming mislukt"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "Checkpoint",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/nl/cloud.json
generated
11
webview-ui/src/i18n/locales/nl/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"switchAccount": "Wissel van Roo Code Cloud Account",
|
||||
"createTeamAccount": "Teamaccount aanmaken",
|
||||
"cloudUrlPillLabel": "Roo Code Cloud URL",
|
||||
"usagePreview": {
|
||||
"costPastDays": "Dagelijkse kosten afgelopen {{count}} dagen",
|
||||
"seeMoreStats": "Bekijk meer statistieken in Roo Code Cloud",
|
||||
"invalidDataFormat": "Ongeldig dataformaat ontvangen",
|
||||
"failedToLoad": "Laden van gebruiksgegevens mislukt",
|
||||
"couldNotLoadChart": "Grafiek kon niet geladen worden:",
|
||||
"clickToRetry": "Klik om opnieuw te proberen",
|
||||
"tasks_one": "{{count}} taak",
|
||||
"tasks_other": "{{count}} taken",
|
||||
"tokens": "{{count}} tokens"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "Roo wat onafhankelijkheid geven? Bedien het overal met Roo Code Cloud. <learnMoreLink>Meer informatie</learnMoreLink>.",
|
||||
"longRunningTask": "Dit kan even duren. Ga overal verder met de Cloud.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/pl/chat.json
generated
3
webview-ui/src/i18n/locales/pl/chat.json
generated
|
|
@ -141,8 +141,7 @@
|
|||
"failed": "Zapytanie API nie powiodło się",
|
||||
"streaming": "Zapytanie API...",
|
||||
"cancelled": "Zapytanie API anulowane",
|
||||
"streamingFailed": "Strumieniowanie API nie powiodło się",
|
||||
"viewTokenUsage": "Wyświetl statystyki użycia tokenów"
|
||||
"streamingFailed": "Strumieniowanie API nie powiodło się"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "Punkt kontrolny",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/pl/cloud.json
generated
11
webview-ui/src/i18n/locales/pl/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"switchAccount": "Przełącz Konto Roo Code Cloud",
|
||||
"createTeamAccount": "Utwórz Konto Zespołu",
|
||||
"cloudUrlPillLabel": "URL Roo Code Cloud",
|
||||
"usagePreview": {
|
||||
"costPastDays": "Dzienny koszt w ciągu ostatnich {{count}} dni",
|
||||
"seeMoreStats": "Zobacz więcej statystyk w Roo Code Cloud",
|
||||
"invalidDataFormat": "Otrzymano nieprawidłowy format danych",
|
||||
"failedToLoad": "Nie udało się załadować danych o użytkowaniu",
|
||||
"couldNotLoadChart": "Nie można załadować wykresu:",
|
||||
"clickToRetry": "Kliknij, aby ponowić próbę",
|
||||
"tasks_one": "{{count}} zadanie",
|
||||
"tasks_other": "{{count}} zadania",
|
||||
"tokens": "{{count}} tokeny"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "Dać Roo trochę niezależności? Kontroluj go z dowolnego miejsca dzięki Roo Code Cloud. <learnMoreLink>Dowiedz się więcej</learnMoreLink>.",
|
||||
"longRunningTask": "To może chwilę potrwać. Kontynuuj z dowolnego miejsca dzięki Chmurze.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/pt-BR/chat.json
generated
3
webview-ui/src/i18n/locales/pt-BR/chat.json
generated
|
|
@ -141,8 +141,7 @@
|
|||
"failed": "Requisição API falhou",
|
||||
"streaming": "Requisição API...",
|
||||
"cancelled": "Requisição API cancelada",
|
||||
"streamingFailed": "Streaming API falhou",
|
||||
"viewTokenUsage": "Ver estatísticas de uso de token"
|
||||
"streamingFailed": "Streaming API falhou"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "Ponto de verificação",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/pt-BR/cloud.json
generated
11
webview-ui/src/i18n/locales/pt-BR/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"switchAccount": "Alternar Conta do Roo Code Cloud",
|
||||
"createTeamAccount": "Criar Conta de Equipe",
|
||||
"cloudUrlPillLabel": "URL do Roo Code Cloud ",
|
||||
"usagePreview": {
|
||||
"costPastDays": "Custo diário dos últimos {{count}} dias",
|
||||
"seeMoreStats": "Veja mais estatísticas no Roo Code Cloud",
|
||||
"invalidDataFormat": "Formato de dados recebido inválido",
|
||||
"failedToLoad": "Falha ao carregar dados de uso",
|
||||
"couldNotLoadChart": "Não foi possível carregar o gráfico:",
|
||||
"clickToRetry": "Clique para tentar novamente",
|
||||
"tasks_one": "{{count}} tarefa",
|
||||
"tasks_other": "{{count}} tarefas",
|
||||
"tokens": "{{count}} tokens"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "Dando um pouco de independência ao Roo? Controle-o de qualquer lugar com o Roo Code Cloud. <learnMoreLink>Saiba mais</learnMoreLink>.",
|
||||
"longRunningTask": "Isso pode levar um tempo. Continue de qualquer lugar com a Nuvem.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ru/chat.json
generated
3
webview-ui/src/i18n/locales/ru/chat.json
generated
|
|
@ -136,8 +136,7 @@
|
|||
"failed": "API-запрос не выполнен",
|
||||
"streaming": "API-запрос...",
|
||||
"cancelled": "API-запрос отменен",
|
||||
"streamingFailed": "Ошибка потокового API-запроса",
|
||||
"viewTokenUsage": "Посмотреть статистику использования токенов"
|
||||
"streamingFailed": "Ошибка потокового API-запроса"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "Точка сохранения",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/ru/cloud.json
generated
11
webview-ui/src/i18n/locales/ru/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"switchAccount": "Переключить аккаунт Roo Code Cloud",
|
||||
"createTeamAccount": "Создать командный аккаунт",
|
||||
"cloudUrlPillLabel": "URL Roo Code Cloud",
|
||||
"usagePreview": {
|
||||
"costPastDays": "Ежедневная стоимость за последние {{count}} дней",
|
||||
"seeMoreStats": "Смотреть больше статистики в Roo Code Cloud",
|
||||
"invalidDataFormat": "Получен неверный формат данных",
|
||||
"failedToLoad": "Не удалось загрузить данные об использовании",
|
||||
"couldNotLoadChart": "Не удалось загрузить диаграмму:",
|
||||
"clickToRetry": "Нажмите, чтобы повторить попытку",
|
||||
"tasks_one": "{{count}} задача",
|
||||
"tasks_other": "{{count}} задачи",
|
||||
"tokens": "{{count}} токенов"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "Предоставить Roo немного независимости? Управляйте им из любого места с помощью Roo Code Cloud. <learnMoreLink>Узнать больше</learnMoreLink>.",
|
||||
"longRunningTask": "Это может занять некоторое время. Продолжайте из любого места с помощью Облака.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/tr/chat.json
generated
3
webview-ui/src/i18n/locales/tr/chat.json
generated
|
|
@ -141,8 +141,7 @@
|
|||
"failed": "API İsteği Başarısız",
|
||||
"streaming": "API İsteği...",
|
||||
"cancelled": "API İsteği İptal Edildi",
|
||||
"streamingFailed": "API Akışı Başarısız",
|
||||
"viewTokenUsage": "Token kullanım istatistiklerini görüntüle"
|
||||
"streamingFailed": "API Akışı Başarısız"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "Kontrol Noktası",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/tr/cloud.json
generated
11
webview-ui/src/i18n/locales/tr/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"switchAccount": "Roo Code Cloud Hesabını Değiştir",
|
||||
"createTeamAccount": "Takım Hesabı Oluştur",
|
||||
"cloudUrlPillLabel": "Roo Code Cloud URL'si",
|
||||
"usagePreview": {
|
||||
"costPastDays": "Son {{count}} günün günlük maliyeti",
|
||||
"seeMoreStats": "Roo Code Cloud'da daha fazla istatistik görün",
|
||||
"invalidDataFormat": "Geçersiz veri formatı alındı",
|
||||
"failedToLoad": "Kullanım verileri yüklenemedi",
|
||||
"couldNotLoadChart": "Grafik yüklenemedi:",
|
||||
"clickToRetry": "Yeniden denemek için tıklayın",
|
||||
"tasks_one": "{{count}} görev",
|
||||
"tasks_other": "{{count}} görev",
|
||||
"tokens": "{{count}} token"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "Roo'ya biraz bağımsızlık mı veriyorsunuz? Roo Code Cloud ile onu her yerden kontrol edin. <learnMoreLink>Daha fazla bilgi edinin</learnMoreLink>.",
|
||||
"longRunningTask": "Bu biraz zaman alabilir. Bulut ile her yerden devam edin.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/vi/chat.json
generated
3
webview-ui/src/i18n/locales/vi/chat.json
generated
|
|
@ -141,8 +141,7 @@
|
|||
"failed": "Yêu cầu API thất bại",
|
||||
"streaming": "Yêu cầu API...",
|
||||
"cancelled": "Yêu cầu API đã hủy",
|
||||
"streamingFailed": "Streaming API thất bại",
|
||||
"viewTokenUsage": "Xem thống kê sử dụng token"
|
||||
"streamingFailed": "Streaming API thất bại"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "Điểm kiểm tra",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/vi/cloud.json
generated
11
webview-ui/src/i18n/locales/vi/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"switchAccount": "Chuyển Tài Khoản Roo Code Cloud",
|
||||
"createTeamAccount": "Tạo Tài Khoản Nhóm",
|
||||
"cloudUrlPillLabel": "URL Roo Code Cloud",
|
||||
"usagePreview": {
|
||||
"costPastDays": "Chi phí hàng ngày trong {{count}} ngày qua",
|
||||
"seeMoreStats": "Xem thêm số liệu thống kê trong Roo Code Cloud",
|
||||
"invalidDataFormat": "Định dạng dữ liệu nhận được không hợp lệ",
|
||||
"failedToLoad": "Không thể tải dữ liệu sử dụng",
|
||||
"couldNotLoadChart": "Không thể tải biểu đồ:",
|
||||
"clickToRetry": "Nhấp để thử lại",
|
||||
"tasks_one": "{{count}} nhiệm vụ",
|
||||
"tasks_other": "{{count}} nhiệm vụ",
|
||||
"tokens": "{{count}} token"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "Trao cho Roo một chút độc lập? Kiểm soát nó từ mọi nơi với Roo Code Cloud. <learnMoreLink>Tìm hiểu thêm</learnMoreLink>.",
|
||||
"longRunningTask": "Việc này có thể mất một lúc. Tiếp tục từ mọi nơi với Cloud.",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/zh-CN/chat.json
generated
3
webview-ui/src/i18n/locales/zh-CN/chat.json
generated
|
|
@ -141,8 +141,7 @@
|
|||
"failed": "API请求失败",
|
||||
"streaming": "API请求...",
|
||||
"cancelled": "API请求已取消",
|
||||
"streamingFailed": "API流式传输失败",
|
||||
"viewTokenUsage": "查看 Token 使用情况统计"
|
||||
"streamingFailed": "API流式传输失败"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "检查点",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/zh-CN/cloud.json
generated
11
webview-ui/src/i18n/locales/zh-CN/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"switchAccount": "切换 Roo Code Cloud 账户",
|
||||
"createTeamAccount": "创建团队账户",
|
||||
"cloudUrlPillLabel": "Roo Code Cloud URL",
|
||||
"usagePreview": {
|
||||
"costPastDays": "过去 {{count}} 天的每日费用",
|
||||
"seeMoreStats": "在 Roo Code Cloud 中查看更多统计信息",
|
||||
"invalidDataFormat": "收到的数据格式无效",
|
||||
"failedToLoad": "无法加载使用数据",
|
||||
"couldNotLoadChart": "无法加载图表:",
|
||||
"clickToRetry": "点击重试",
|
||||
"tasks_one": "{{count}} 个任务",
|
||||
"tasks_other": "{{count}} 个任务",
|
||||
"tokens": "{{count}} 令牌"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "给 Roo 一些独立性?使用 Roo Code Cloud 从任何地方控制它。 <learnMoreLink>了解更多</learnMoreLink>。",
|
||||
"longRunningTask": "这可能需要一段时间。使用 Cloud 从任何地方继续。",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/zh-TW/chat.json
generated
3
webview-ui/src/i18n/locales/zh-TW/chat.json
generated
|
|
@ -147,8 +147,7 @@
|
|||
"failed": "API 請求失敗",
|
||||
"streaming": "正在處理 API 請求...",
|
||||
"cancelled": "API 請求已取消",
|
||||
"streamingFailed": "API 串流處理失敗",
|
||||
"viewTokenUsage": "檢視權杖使用情況統計"
|
||||
"streamingFailed": "API 串流處理失敗"
|
||||
},
|
||||
"checkpoint": {
|
||||
"regular": "檢查點",
|
||||
|
|
|
|||
11
webview-ui/src/i18n/locales/zh-TW/cloud.json
generated
11
webview-ui/src/i18n/locales/zh-TW/cloud.json
generated
|
|
@ -26,17 +26,6 @@
|
|||
"switchAccount": "切換 Roo Code Cloud 帳戶",
|
||||
"createTeamAccount": "創建團隊帳戶",
|
||||
"cloudUrlPillLabel": "Roo Code Cloud URL",
|
||||
"usagePreview": {
|
||||
"costPastDays": "過去 {{count}} 天的每日費用",
|
||||
"seeMoreStats": "在 Roo Code Cloud 中查看更多統計訊息",
|
||||
"invalidDataFormat": "收到的資料格式無效",
|
||||
"failedToLoad": "無法載入使用資料",
|
||||
"couldNotLoadChart": "無法載入圖表:",
|
||||
"clickToRetry": "點擊重試",
|
||||
"tasks_one": "{{count}} 個任務",
|
||||
"tasks_other": "{{count}} 個任務",
|
||||
"tokens": "{{count}} 個 token"
|
||||
},
|
||||
"upsell": {
|
||||
"autoApprovePowerUser": "給 Roo 一點獨立性?使用 Roo Code Cloud 隨時隨地控制它。<learnMoreLink>了解更多</learnMoreLink>。",
|
||||
"longRunningTask": "這可能需要一些時間。使用雲端隨時隨地繼續。",
|
||||
|
|
|
|||
|
|
@ -26,15 +26,6 @@ export const formatDate = (timestamp: number) => {
|
|||
})
|
||||
}
|
||||
|
||||
export const formatDateShort = (timestamp: number) => {
|
||||
const date = new Date(timestamp)
|
||||
const locale = i18next.language || "en"
|
||||
return date.toLocaleDateString(locale, {
|
||||
month: "numeric",
|
||||
day: "numeric",
|
||||
})
|
||||
}
|
||||
|
||||
export const formatTimeAgo = (timestamp: number) => {
|
||||
const now = Date.now()
|
||||
const diff = now - timestamp
|
||||
|
|
@ -82,7 +73,3 @@ export const formatTimeAgo = (timestamp: number) => {
|
|||
|
||||
return i18next.t("common:time_ago.just_now")
|
||||
}
|
||||
|
||||
export const formatCost = (cost: number): string => {
|
||||
return `$${cost.toFixed(2)}`
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue