Compare commits

...

6 Commits

Author SHA1 Message Date
frostbournesb d708e29fc1 Merge remote-tracking branch 'origin/main' into frostbourne/eng-265 2025-04-04 21:08:52 -07:00
frostbournesb b2c51a66fc changeset 2025-03-25 21:58:11 -07:00
frostbournesb ee6c5d0b4d Migrate history table to heroui table 2025-03-25 21:56:45 -07:00
frostbournesb 0d5c5d347b make heroui work 2025-03-25 21:11:44 -07:00
frostbournesb e1cbc72c98 Merge remote-tracking branch 'origin/main' into frostbourne/eng-265 2025-03-25 20:54:56 -07:00
frostbournesb f8166fb9ca Install heroui, reorganize providers 2025-03-24 21:44:30 -07:00
11 changed files with 7278 additions and 99 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": minor
---
Paginate history table in account view
+3620 -5
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -350,6 +350,7 @@
"@aws-sdk/client-bedrock-runtime": "^3.758.0",
"@google-cloud/vertexai": "^1.9.3",
"@google/generative-ai": "^0.18.0",
"@heroui/react": "^2.7.6-beta.2",
"@mistralai/mistralai": "^1.5.0",
"@modelcontextprotocol/sdk": "^1.7.0",
"@opentelemetry/api": "^1.4.1",
+3472 -2
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -19,6 +19,7 @@
"dompurify": "^3.2.4",
"fast-deep-equal": "^3.1.3",
"firebase": "^11.3.0",
"framer-motion": "^12.5.0",
"fuse.js": "^7.0.0",
"fzf": "^0.5.2",
"mermaid": "^11.4.1",
+5 -7
View File
@@ -1,13 +1,13 @@
import { useCallback, useEffect, useState } from "react"
import { useEvent } from "react-use"
import { Providers } from "./Providers"
import { ExtensionMessage } from "@shared/ExtensionMessage"
import ChatView from "./components/chat/ChatView"
import HistoryView from "./components/history/HistoryView"
import SettingsView from "./components/settings/SettingsView"
import WelcomeView from "./components/welcome/WelcomeView"
import AccountView from "./components/account/AccountView"
import { ExtensionStateContextProvider, useExtensionState } from "./context/ExtensionStateContext"
import { FirebaseAuthProvider } from "./context/FirebaseAuthContext"
import { useExtensionState } from "./context/ExtensionStateContext"
import { vscode } from "./utils/vscode"
import McpView from "./components/mcp/configuration/McpConfigurationView"
@@ -113,11 +113,9 @@ const AppContent = () => {
const App = () => {
return (
<ExtensionStateContextProvider>
<FirebaseAuthProvider>
<AppContent />
</FirebaseAuthProvider>
</ExtensionStateContextProvider>
<Providers>
<AppContent />
</Providers>
)
}
+15
View File
@@ -0,0 +1,15 @@
import type { ReactNode } from "react"
import { ExtensionStateContextProvider } from "./context/ExtensionStateContext"
import { FirebaseAuthProvider } from "./context/FirebaseAuthContext"
import { HeroUIProvider } from "@heroui/react"
export function Providers({ children }: { children: ReactNode }) {
return (
<ExtensionStateContextProvider>
<FirebaseAuthProvider>
<HeroUIProvider>{children}</HeroUIProvider>
</FirebaseAuthProvider>
</ExtensionStateContextProvider>
)
}
@@ -20,7 +20,7 @@ const AccountView = ({ onDone }: AccountViewProps) => {
<h3 className="text-[var(--vscode-foreground)] m-0">Account</h3>
<VSCodeButton onClick={onDone}>Done</VSCodeButton>
</div>
<div className="flex-grow overflow-hidden pr-[8px] flex flex-col">
<div className="flex-grow overflow-y-scroll flex flex-col">
<div className="h-full mb-[5px]">
<ClineAccountView />
</div>
@@ -145,11 +145,9 @@ export const ClineAccountView = () => {
</div>
</div>
<VSCodeDivider className="mt-6 mb-3 w-full" />
<VSCodeDivider className="my-8 w-full" />
<div className="flex-grow flex flex-col min-h-0 pb-[0px]">
<CreditsHistoryTable isLoading={isLoading} usageData={usageData} paymentsData={paymentsData} />
</div>
<CreditsHistoryTable isLoading={isLoading} usageData={usageData} paymentsData={paymentsData} />
</div>
) : (
<div className="flex flex-col items-center pr-3">
@@ -1,5 +1,5 @@
import { VSCodeDataGrid, VSCodeDataGridRow, VSCodeDataGridCell } from "@vscode/webview-ui-toolkit/react"
import { useState } from "react"
import { useState, useMemo } from "react"
import { Table, TableHeader, TableColumn, TableBody, TableRow, TableCell, Pagination, Spinner } from "@heroui/react"
import { TabButton } from "../mcp/configuration/McpConfigurationView"
import { UsageTransaction, PaymentTransaction } from "@shared/ClineAccount"
import { formatDollars, formatTimestamp } from "@/utils/format"
@@ -12,6 +12,71 @@ interface CreditsHistoryTableProps {
const CreditsHistoryTable = ({ isLoading, usageData, paymentsData }: CreditsHistoryTableProps) => {
const [activeTab, setActiveTab] = useState<"usage" | "payments">("usage")
const [usagePage, setUsagePage] = useState(1)
const [paymentsPage, setPaymentsPage] = useState(1)
const rowsPerPage = 5
// Define columns for usage table
const usageColumns = [
{
key: "date",
label: "Date",
},
{
key: "model",
label: "Model",
},
{
key: "credits",
label: "Credits Used",
},
]
// Define columns for payments table
const paymentsColumns = [
{
key: "date",
label: "Date",
},
{
key: "totalCost",
label: "Total Cost",
},
{
key: "credits",
label: "Credits",
},
]
// Calculate pages for both tables
const usagePages = Math.ceil(usageData.length / rowsPerPage)
const paymentsPages = Math.ceil(paymentsData.length / rowsPerPage)
// Prepare data for usage table with pagination
const usageItems = useMemo(() => {
const start = (usagePage - 1) * rowsPerPage
const end = start + rowsPerPage
return usageData.slice(start, end).map((row, index) => ({
key: index.toString(),
date: formatTimestamp(row.spentAt),
model: `${row.modelProvider}/${row.model}`,
credits: `$${Number(row.credits).toFixed(7)}`,
}))
}, [usagePage, usageData])
// Prepare data for payments table with pagination
const paymentsItems = useMemo(() => {
const start = (paymentsPage - 1) * rowsPerPage
const end = start + rowsPerPage
return paymentsData.slice(start, end).map((row, index) => ({
key: index.toString(),
date: formatTimestamp(row.paidAt),
totalCost: `$${formatDollars(parseInt(row.amountCents))}`,
credits: `$${row.credits}`,
}))
}, [paymentsPage, paymentsData])
return (
<div className="flex flex-col flex-grow h-full">
@@ -26,85 +91,83 @@ const CreditsHistoryTable = ({ isLoading, usageData, paymentsData }: CreditsHist
</div>
{/* Content container */}
<div className="mt-[15px] mb-[0px] rounded-md overflow-auto flex-grow">
{isLoading ? (
<div className="flex justify-center items-center p-4">
<div className="text-[var(--vscode-descriptionForeground)]">Loading...</div>
</div>
) : (
<>
{activeTab === "usage" && (
<>
{usageData.length > 0 ? (
<VSCodeDataGrid>
<VSCodeDataGridRow row-type="header">
<VSCodeDataGridCell cell-type="columnheader" grid-column="1">
Date
</VSCodeDataGridCell>
<VSCodeDataGridCell cell-type="columnheader" grid-column="2">
Model
</VSCodeDataGridCell>
{/* <VSCodeDataGridCell cell-type="columnheader" grid-column="3">
Tokens Used
</VSCodeDataGridCell> */}
<VSCodeDataGridCell cell-type="columnheader" grid-column="3">
Credits Used
</VSCodeDataGridCell>
</VSCodeDataGridRow>
<div className="mt-4">
{activeTab === "usage" && (
<Table
removeWrapper
aria-label="Usage history table"
bottomContent={
usageData.length > 0 && (
<div className="flex w-full justify-center">
<Pagination
isCompact
showControls
showShadow
page={usagePage}
total={usagePages}
onChange={(page) => setUsagePage(page)}
classNames={{
item: "bg-[var(--vscode-button-secondaryBackground)] text-[var(--vscode-button-secondaryForeground)] hover:bg-[var(--vscode-button-secondaryHoverBackground)]",
cursor: "bg-[var(--vscode-button-background)] text-[var(--vscode-button-foreground)]",
}}
/>
</div>
)
}>
<TableHeader columns={usageColumns}>
{(column) => <TableColumn key={column.key}>{column.label}</TableColumn>}
</TableHeader>
<TableBody
items={usageItems}
isLoading={isLoading}
loadingContent={<Spinner />}
emptyContent={!isLoading && usageData.length === 0 && "No usage history"}>
{(item) => (
<TableRow key={item.key}>
{(columnKey) => <TableCell>{item[columnKey as keyof typeof item]}</TableCell>}
</TableRow>
)}
</TableBody>
</Table>
)}
{usageData.map((row, index) => (
<VSCodeDataGridRow key={index}>
<VSCodeDataGridCell grid-column="1">
{formatTimestamp(row.spentAt)}
</VSCodeDataGridCell>
<VSCodeDataGridCell grid-column="2">{`${row.modelProvider}/${row.model}`}</VSCodeDataGridCell>
{/* <VSCodeDataGridCell grid-column="3">{`${row.promptTokens} → ${row.completionTokens}`}</VSCodeDataGridCell> */}
<VSCodeDataGridCell grid-column="3">{`$${Number(row.credits).toFixed(7)}`}</VSCodeDataGridCell>
</VSCodeDataGridRow>
))}
</VSCodeDataGrid>
) : (
<div className="flex justify-center items-center p-4">
<div className="text-[var(--vscode-descriptionForeground)]">No usage history</div>
</div>
)}
</>
)}
{activeTab === "payments" && (
<>
{paymentsData.length > 0 ? (
<VSCodeDataGrid>
<VSCodeDataGridRow row-type="header">
<VSCodeDataGridCell cell-type="columnheader" grid-column="1">
Date
</VSCodeDataGridCell>
<VSCodeDataGridCell cell-type="columnheader" grid-column="2">
Total Cost
</VSCodeDataGridCell>
<VSCodeDataGridCell cell-type="columnheader" grid-column="3">
Credits
</VSCodeDataGridCell>
</VSCodeDataGridRow>
{paymentsData.map((row, index) => (
<VSCodeDataGridRow key={index}>
<VSCodeDataGridCell grid-column="1">
{formatTimestamp(row.paidAt)}
</VSCodeDataGridCell>
<VSCodeDataGridCell grid-column="2">{`$${formatDollars(parseInt(row.amountCents))}`}</VSCodeDataGridCell>
<VSCodeDataGridCell grid-column="3">{`${row.credits}`}</VSCodeDataGridCell>
</VSCodeDataGridRow>
))}
</VSCodeDataGrid>
) : (
<div className="flex justify-center items-center p-4">
<div className="text-[var(--vscode-descriptionForeground)]">No payment history</div>
</div>
)}
</>
)}
</>
{activeTab === "payments" && (
<Table
removeWrapper
aria-label="Payments history table"
bottomContent={
paymentsData.length > 0 && (
<div className="flex w-full justify-center">
<Pagination
isCompact
showControls
showShadow
page={paymentsPage}
total={paymentsPages}
onChange={(page) => setPaymentsPage(page)}
classNames={{
item: "bg-[var(--vscode-button-secondaryBackground)] text-[var(--vscode-button-secondaryForeground)] hover:bg-[var(--vscode-button-secondaryHoverBackground)]",
cursor: "bg-[var(--vscode-button-background)] text-[var(--vscode-button-foreground)]",
}}
/>
</div>
)
}>
<TableHeader columns={paymentsColumns}>
{(column) => <TableColumn key={column.key}>{column.label}</TableColumn>}
</TableHeader>
<TableBody
items={paymentsItems}
isLoading={isLoading}
loadingContent={<Spinner />}
emptyContent={!isLoading && paymentsData.length === 0 && "No payment history"}>
{(item) => (
<TableRow key={item.key}>
{(columnKey) => <TableCell>{item[columnKey as keyof typeof item]}</TableCell>}
</TableRow>
)}
</TableBody>
</Table>
)}
</div>
</div>
+1
View File
@@ -3,6 +3,7 @@
/* Disable Tailwind's CSS reset to preserve existing styles */
/* @import "tailwindcss/preflight.css" layer(base); */
@import "tailwindcss/utilities.css" layer(utilities);
@config "../tailwind.config.js";
textarea:focus {
outline: 1.5px solid var(--vscode-focusBorder, #007fd4);
+12
View File
@@ -0,0 +1,12 @@
// tailwind.config.js
const { heroui } = require("@heroui/react")
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{js,ts,jsx,tsx,mdx}", "./node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {},
},
darkMode: "class",
plugins: [heroui()],
}