diff --git a/.prettierrc b/.prettierrc
index 76f930a..92102b9 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -8,5 +8,4 @@
"tabWidth": 2,
"proseWrap": "preserve",
"tailwindFunctions": ["clsx", "tw"]
-
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ddce091..4c2725e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,39 +8,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [0.2.0] - 2026-01-16
### New Features
+
- Enhanced cloudHandler to inject minimal auth state when database entry is missing, improving onboarding reliability.
- Implemented stability fixes and enhanced error handling across the application.
### Improvements
+
- Upgraded Electron from 32.3.3 to 37.3.1 for improved performance and security.
- Conditionally include plugins based on start command in forge.config.ts for better build flexibility.
### Bug Fixes
+
- Fixed "Converting circular structure to JSON" error.
### Documentation
+
- Added curly brace constraints for conditional statements.
- Fixed incorrect reference documentation name.
## [0.1.1] - 2026-01-11
### Bug Fixes
+
- Fix Antigravity visibility issue on account switch. (Fixed [#19](https://github.com/Draculabo/AntigravityManager/issues/19))
## [0.1.0] - 2026-01-10
### New Features
+
- LAN Connection Support: Users can now connect via Local Area Network (LAN) for improved flexibility and internal environment support.
- Antigravity Integration: Added native support and adaptation for Antigravity, enhancing overall compatibility.
- Local API Proxy: Built-in OpenAI/Anthropic compatible proxy server.
### Bug Fixes
-- Reverse Proxy Issue: Resolved a critical error occurring during reverse proxy configurations. (Fixed [#11](https://github.com/Draculabo/AntigravityManager/issues/11))
+- Reverse Proxy Issue: Resolved a critical error occurring during reverse proxy configurations. (Fixed [#11](https://github.com/Draculabo/AntigravityManager/issues/11))
## [0.0.1] - 2025-12-22
### Added
+
- Initial release of Antigravity Manager
- Multi-account management for Google Gemini and Claude
- Real-time quota monitoring
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 4cb82e9..01d67af 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
Examples of behavior that contributes to creating a positive environment include:
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
+- Using welcoming and inclusive language
+- Being respectful of differing viewpoints and experiences
+- Gracefully accepting constructive criticism
+- Focusing on what is best for the community
+- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
-* The use of sexualized language or imagery and unwelcome sexual attention or advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or electronic address, without explicit permission
-* Other conduct which could reasonably be considered inappropriate in a professional setting
+- The use of sexualized language or imagery and unwelcome sexual attention or advances
+- Trolling, insulting/derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or electronic address, without explicit permission
+- Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
@@ -45,4 +45,5 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage], versi
[homepage]: https://www.contributor-covenant.org
---
+
**Note**: Please replace `[INSERT EMAIL ADDRESS]` with your actual contact email if you wish to receive reports.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e731c79..e729381 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -52,20 +52,21 @@ npm start
### Available Scripts
-| Command | Description |
-|---------|-------------|
-| `npm start` | Start the app in development mode |
-| `npm run lint` | Run ESLint to check for code issues |
-| `npm run format:write` | Format code with Prettier |
-| `npm run test:unit` | Run unit tests with Vitest |
-| `npm run test:e2e` | Run E2E tests with Playwright |
-| `npm run test:all` | Run all tests |
-| `npm run type-check` | Run TypeScript type checking |
-| `npm run make` | Build production packages |
+| Command | Description |
+| ---------------------- | ----------------------------------- |
+| `npm start` | Start the app in development mode |
+| `npm run lint` | Run ESLint to check for code issues |
+| `npm run format:write` | Format code with Prettier |
+| `npm run test:unit` | Run unit tests with Vitest |
+| `npm run test:e2e` | Run E2E tests with Playwright |
+| `npm run test:all` | Run all tests |
+| `npm run type-check` | Run TypeScript type checking |
+| `npm run make` | Build production packages |
## ✏️ Making Changes
1. **Create a new branch** from `main`:
+
```bash
git checkout -b feature/your-feature-name
# or
@@ -73,6 +74,7 @@ npm start
```
2. **Make your changes** and commit them:
+
```bash
git add .
git commit -m "feat: add amazing new feature"
@@ -97,6 +99,7 @@ We follow [Conventional Commits](https://www.conventionalcommits.org/). Each com
```
**Types:**
+
- `feat`: A new feature
- `fix`: A bug fix
- `docs`: Documentation only changes
@@ -107,6 +110,7 @@ We follow [Conventional Commits](https://www.conventionalcommits.org/). Each com
- `chore`: Changes to build process or auxiliary tools
**Examples:**
+
```
feat(auth): add Google OAuth support
fix(quota): resolve quota refresh timeout issue
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 6e90336..09f3c40 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -174,7 +174,6 @@
-
---
## 🚀 快速开始
@@ -183,11 +182,11 @@
从 [Releases](https://github.com/Draculabo/AntigravityManager/releases) 页面下载适合你平台的最新版本。
-| 平台 | 下载链接 |
-|------|----------|
+| 平台 | 下载链接 |
+| ------- | ------------------------------------------------------------------------------ |
| Windows | [.exe 安装包](https://github.com/Draculabo/AntigravityManager/releases/latest) |
-| macOS | [.dmg 安装包](https://github.com/Draculabo/AntigravityManager/releases/latest) |
-| Linux | [.deb / .rpm](https://github.com/Draculabo/AntigravityManager/releases/latest) |
+| macOS | [.dmg 安装包](https://github.com/Draculabo/AntigravityManager/releases/latest) |
+| Linux | [.deb / .rpm](https://github.com/Draculabo/AntigravityManager/releases/latest) |
### 从源码构建
@@ -217,14 +216,14 @@ npm run make
## 🛠️ 技术栈
-| 类别 | 技术 |
-|------|------|
-| **核心** | [Electron](https://www.electronjs.org/)、[React](https://react.dev/)、[TypeScript](https://www.typescriptlang.org/) |
-| **构建工具** | [Vite](https://vitejs.dev/) |
-| **样式** | [TailwindCSS](https://tailwindcss.com/)、[Shadcn UI](https://ui.shadcn.com/) |
-| **状态管理** | [TanStack Query](https://tanstack.com/query/latest)、[TanStack Router](https://tanstack.com/router/latest) |
-| **数据库** | [Better-SQLite3](https://github.com/WiseLibs/better-sqlite3) |
-| **测试** | [Vitest](https://vitest.dev/)、[Playwright](https://playwright.dev/) |
+| 类别 | 技术 |
+| ------------ | ------------------------------------------------------------------------------------------------------------------- |
+| **核心** | [Electron](https://www.electronjs.org/)、[React](https://react.dev/)、[TypeScript](https://www.typescriptlang.org/) |
+| **构建工具** | [Vite](https://vitejs.dev/) |
+| **样式** | [TailwindCSS](https://tailwindcss.com/)、[Shadcn UI](https://ui.shadcn.com/) |
+| **状态管理** | [TanStack Query](https://tanstack.com/query/latest)、[TanStack Router](https://tanstack.com/router/latest) |
+| **数据库** | [Better-SQLite3](https://github.com/WiseLibs/better-sqlite3) |
+| **测试** | [Vitest](https://vitest.dev/)、[Playwright](https://playwright.dev/) |
---
@@ -232,16 +231,16 @@ npm run make
### 可用脚本
-| 命令 | 描述 |
-|------|------|
-| `npm start` | 启动开发模式 |
-| `npm run lint` | 运行 ESLint 检查 |
+| 命令 | 描述 |
+| ---------------------- | ------------------------ |
+| `npm start` | 启动开发模式 |
+| `npm run lint` | 运行 ESLint 检查 |
| `npm run format:write` | 使用 Prettier 格式化代码 |
-| `npm run test:unit` | 运行单元测试 |
-| `npm run test:e2e` | 运行 E2E 测试 |
-| `npm run test:all` | 运行所有测试 |
-| `npm run type-check` | TypeScript 类型检查 |
-| `npm run make` | 构建生产包 |
+| `npm run test:unit` | 运行单元测试 |
+| `npm run test:e2e` | 运行 E2E 测试 |
+| `npm run test:all` | 运行所有测试 |
+| `npm run type-check` | TypeScript 类型检查 |
+| `npm run make` | 构建生产包 |
### 项目结构
@@ -265,6 +264,7 @@ AntigravityManager/
Q: 程序无法启动怎么办?
请检查:
+
1. 确保已安装所有依赖:`npm install`
2. 检查 Node.js 版本是否 >= 18
3. 尝试删除 `node_modules` 后重新安装
diff --git a/SECURITY.md b/SECURITY.md
index 2248b95..9b0133a 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -26,10 +26,10 @@ Master keys are stored in the OS native credential manager via `keytar`, never i
## 📋 Supported Versions
-| Version | Supported |
-| ------- | ------------------ |
-| Latest | :white_check_mark: |
-| < Latest | :x: |
+| Version | Supported |
+| -------- | ------------------ |
+| Latest | :white_check_mark: |
+| < Latest | :x: |
We only provide security updates for the latest version. Please ensure you're running the most recent release.
diff --git a/docs/cloud_features.md b/docs/cloud_features.md
index 1f89110..61dad72 100644
--- a/docs/cloud_features.md
+++ b/docs/cloud_features.md
@@ -5,17 +5,20 @@
## 1. 核心功能
### 1.1 账号池管理
+
- **添加账号**:支持通过 Google OAuth 授权码添加账号。
- **列表展示**:展示所有已添加的账号,包括头像、邮箱、最后使用时间。
- **状态监控**:实时显示账号状态(Active, Rate Limited, Expired)以及是否为当前活动账号。
- **删除账号**:支持从本地数据库中移除账号。
### 1.2 配额实时监控
+
- **多模型支持**:分别监控 `gemini-pro`, `claude-3-5-sonnet` 等模型的配额使用情况。
- **可视化展示**:通过进度条和颜色(绿/黄/红)直观展示剩余配额百分比。
- **自动/手动刷新**:支持手动刷新配额,系统也会在切换前自动检查。
### 1.4 智能自动切换 (Intelligent Auto-Switching)
+
- **无限池模式**:当检测到当前账号配额不足(<5%)或被限流时,系统会自动寻找最佳备用账号并切换。
- **后台监控**:内置 `CloudMonitorService`, 默认每 5 分钟轮询一次所有账号的 Quota 状态。
- **全局开关**:用户可在界面上一键开启或关闭此功能。
@@ -23,6 +26,7 @@
## 2. 技术实现
### 2.1 数据库设计 (`cloud_accounts.db`)
+
使用 SQLite 存储账号信息,独立于 IDE 的本地数据库。
```sql
@@ -48,6 +52,7 @@ CREATE TABLE settings (
```
### 2.2 IPC 接口设计
+
后端通过 `src/ipc/cloud` 提供以下 oRPC 接口:
- `addGoogleAccount(authCode: string)`: 交换 Token 并保存账号。
@@ -59,15 +64,18 @@ CREATE TABLE settings (
- `forcePollCloudMonitor()`: 手动触发后台轮询。
### 2.3 Token 注入原理 (`ProtobufUtils`)
+
IDE 将鉴权信息存储在 `state.vscdb` 的 `ItemTable` 表中,key 为 `jetskiStateSync.agentManagerInitState`。
value 是一个 Base64 编码的 Protobuf 二进制数据。
我们实现了 `src/utils/protobuf.ts` 工具类,能够:
+
1. **解码**:解析 Varint 和 Length-Delimited 字段。
2. **修改**:定位并移除旧的 Field 6 (OAuthTokenInfo)。
3. **重构**:根据新的 Access Token 和 Refresh Token 构建新的 Field 6 并插入。
### 2.4 切换流程
+
1. **Token 检查**:检查目标账号 Token 是否临期,如需刷新则自动刷新。
2. **停止进程**:优雅关闭 Antigravity 进程。
3. **注入 Token**:调用 `CloudAccountRepo.injectCloudToken` 修改 IDE 数据库。
@@ -75,12 +83,14 @@ value 是一个 Base64 编码的 Protobuf 二进制数据。
5. **重启进程**:重新启动 IDE,使其加载新的凭证。
### 2.5 自动切换逻辑 (`AutoSwitchService`)
+
1. **监控**:`CloudMonitorService` 定时(5分钟)轮询所有账号配额。
2. **判断**:如果当前活动账号的所有关键模型配额低于 5%,或状态为 `rate_limited`。
3. **择优**:从账号池中筛选状态为 `active` 且配额充足的账号,按配额剩余量排序。
4. **执行**:自动调用 `switchCloudAccount` 完成切换,并通知用户。
### 2.6 安全加固 (Security Hardening)
+
- **密钥管理**:使用操作系统原生凭据管理器(Windows Credential Manager / macOS Keychain)通过 `keytar` 库安全存储 Master Key(AES-256)。
- **数据加密**:所有敏感数据(`token_json`, `quota_json`)在存入 SQLite 数据库前均使用 `AES-256-GCM` 算法加密。
- **自动迁移**:应用启动时会自动检测并迁移旧的明文数据,确保安全性平滑升级。
diff --git a/playwright.config.ts b/playwright.config.ts
index b937b2b..3b69cd5 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -1,23 +1,23 @@
-import { defineConfig, devices } from "@playwright/test";
+import { defineConfig, devices } from '@playwright/test';
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
- testDir: "./src/tests/e2e",
+ testDir: './src/tests/e2e',
fullyParallel: false,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
- reporter: "html",
+ reporter: 'html',
use: {
- trace: "on-first-retry",
+ trace: 'on-first-retry',
},
projects: [
{
- name: "chromium",
- use: { ...devices["Desktop Chrome"] },
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
},
],
});
diff --git a/src/actions/account.ts b/src/actions/account.ts
index 0e6ee32..d72a63a 100644
--- a/src/actions/account.ts
+++ b/src/actions/account.ts
@@ -1,4 +1,4 @@
-import { ipc } from "@/ipc/manager";
+import { ipc } from '@/ipc/manager';
export function listAccounts() {
return ipc.client.account.listAccounts();
diff --git a/src/actions/app.ts b/src/actions/app.ts
index 1ba987c..2b0ce48 100644
--- a/src/actions/app.ts
+++ b/src/actions/app.ts
@@ -1,4 +1,4 @@
-import { ipc } from "@/ipc/manager";
+import { ipc } from '@/ipc/manager';
export function getPlatform() {
return ipc.client.app.currentPlatfom();
diff --git a/src/actions/database.ts b/src/actions/database.ts
index 9c1647e..e3f58d2 100644
--- a/src/actions/database.ts
+++ b/src/actions/database.ts
@@ -1,5 +1,5 @@
-import { ipc } from "@/ipc/manager";
-import { AccountBackupData, Account } from "@/types/account";
+import { ipc } from '@/ipc/manager';
+import { AccountBackupData, Account } from '@/types/account';
export function backupAccount(account: Account) {
return ipc.client.database.backupAccount(account);
diff --git a/src/actions/theme.ts b/src/actions/theme.ts
index c5e61c3..79a22f1 100644
--- a/src/actions/theme.ts
+++ b/src/actions/theme.ts
@@ -1,6 +1,6 @@
-import { ThemeMode } from "@/types/theme-mode";
-import { ipc } from "@/ipc/manager";
-import { LOCAL_STORAGE_KEYS } from "@/constants";
+import { ThemeMode } from '@/types/theme-mode';
+import { ipc } from '@/ipc/manager';
+import { LOCAL_STORAGE_KEYS } from '@/constants';
export interface ThemePreferences {
system: ThemeMode;
@@ -9,9 +9,7 @@ export interface ThemePreferences {
export async function getCurrentTheme(): Promise {
const currentTheme = await ipc.client.theme.getCurrentThemeMode();
- const localTheme = localStorage.getItem(
- LOCAL_STORAGE_KEYS.THEME,
- ) as ThemeMode | null;
+ const localTheme = localStorage.getItem(LOCAL_STORAGE_KEYS.THEME) as ThemeMode | null;
return {
system: currentTheme,
@@ -20,7 +18,7 @@ export async function getCurrentTheme(): Promise {
}
export async function setTheme(newTheme: ThemeMode) {
- const isDarkMode = newTheme === "dark";
+ const isDarkMode = newTheme === 'dark';
await ipc.client.theme.setThemeMode(newTheme);
localStorage.setItem(LOCAL_STORAGE_KEYS.THEME, newTheme);
updateDocumentTheme(isDarkMode);
@@ -28,7 +26,7 @@ export async function setTheme(newTheme: ThemeMode) {
export async function toggleTheme() {
const isDarkMode = await ipc.client.theme.toggleThemeMode();
- const newTheme = isDarkMode ? "dark" : "light";
+ const newTheme = isDarkMode ? 'dark' : 'light';
updateDocumentTheme(isDarkMode);
localStorage.setItem(LOCAL_STORAGE_KEYS.THEME, newTheme);
@@ -37,7 +35,7 @@ export async function toggleTheme() {
export async function syncWithLocalTheme() {
const { local } = await getCurrentTheme();
if (!local) {
- setTheme("system");
+ setTheme('system');
return;
}
@@ -46,8 +44,8 @@ export async function syncWithLocalTheme() {
function updateDocumentTheme(isDarkMode: boolean) {
if (isDarkMode) {
- document.documentElement.classList.add("dark");
+ document.documentElement.classList.add('dark');
} else {
- document.documentElement.classList.remove("dark");
+ document.documentElement.classList.remove('dark');
}
}
diff --git a/src/actions/window.ts b/src/actions/window.ts
index ecbf853..f0b46df 100644
--- a/src/actions/window.ts
+++ b/src/actions/window.ts
@@ -1,4 +1,4 @@
-import { ipc } from "../ipc/manager";
+import { ipc } from '../ipc/manager';
export async function minimizeWindow() {
await ipc.client.window.minimizeWindow();
diff --git a/src/components/StatusBar.tsx b/src/components/StatusBar.tsx
index 87ece0f..a34098c 100644
--- a/src/components/StatusBar.tsx
+++ b/src/components/StatusBar.tsx
@@ -1,21 +1,17 @@
-import React from "react";
-import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
-import {
- isProcessRunning,
- startAntigravity,
- closeAntigravity,
-} from "@/actions/process";
-import { Button } from "@/components/ui/button";
-import { cn } from "@/lib/utils";
-import { Play, Square, Loader2 } from "lucide-react";
-import { useTranslation } from "react-i18next";
+import React from 'react';
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
+import { isProcessRunning, startAntigravity, closeAntigravity } from '@/actions/process';
+import { Button } from '@/components/ui/button';
+import { cn } from '@/lib/utils';
+import { Play, Square, Loader2 } from 'lucide-react';
+import { useTranslation } from 'react-i18next';
export const StatusBar: React.FC = () => {
const { t } = useTranslation();
const queryClient = useQueryClient();
const { data: isRunning, isLoading } = useQuery({
- queryKey: ["process", "status"],
+ queryKey: ['process', 'status'],
queryFn: isProcessRunning,
refetchInterval: 2000,
});
@@ -23,14 +19,14 @@ export const StatusBar: React.FC = () => {
const startMutation = useMutation({
mutationFn: startAntigravity,
onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: ["process", "status"] });
+ queryClient.invalidateQueries({ queryKey: ['process', 'status'] });
},
});
const stopMutation = useMutation({
mutationFn: closeAntigravity,
onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: ["process", "status"] });
+ queryClient.invalidateQueries({ queryKey: ['process', 'status'] });
},
});
@@ -47,10 +43,10 @@ export const StatusBar: React.FC = () => {
return (
@@ -62,11 +58,7 @@ export const StatusBar: React.FC = () => {
)}
- {isLoading
- ? t("status.checking")
- : isRunning
- ? t("status.running")
- : t("status.stopped")}
+ {isLoading ? t('status.checking') : isRunning ? t('status.running') : t('status.stopped')}
{
onClick={handleToggle}
disabled={isLoading || isPending}
className={cn(
- "h-8 px-3 hover:bg-white/20",
- isRunning ? "hover:text-green-900" : "hover:text-red-900",
+ 'h-8 px-3 hover:bg-white/20',
+ isRunning ? 'hover:text-green-900' : 'hover:text-red-900',
)}
>
{isPending ? (
@@ -86,7 +78,7 @@ export const StatusBar: React.FC = () => {
) : (
)}
- {isRunning ? t("action.stop") : t("action.start")}
+ {isRunning ? t('action.stop') : t('action.start')}
);
diff --git a/src/components/navigation-menu.tsx b/src/components/navigation-menu.tsx
index 6b02c27..68fa5c2 100644
--- a/src/components/navigation-menu.tsx
+++ b/src/components/navigation-menu.tsx
@@ -1,12 +1,12 @@
-import { Link } from "@tanstack/react-router";
+import { Link } from '@tanstack/react-router';
import {
NavigationMenu as NavigationMenuBase,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
navigationMenuTriggerStyle,
-} from "@/components/ui/navigation-menu";
-import { useTranslation } from "react-i18next";
+} from '@/components/ui/navigation-menu';
+import { useTranslation } from 'react-i18next';
export default function NavigationMenu() {
const { t } = useTranslation();
@@ -16,7 +16,7 @@ export default function NavigationMenu() {
- {t("titleHomePage")}
+ {t('titleHomePage')}
diff --git a/src/components/toggle-theme.tsx b/src/components/toggle-theme.tsx
index 8e333f1..1f5d9a3 100644
--- a/src/components/toggle-theme.tsx
+++ b/src/components/toggle-theme.tsx
@@ -1,6 +1,6 @@
-import { Moon } from "lucide-react";
-import { Button } from "@/components/ui/button";
-import { toggleTheme } from "@/actions/theme";
+import { Moon } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { toggleTheme } from '@/actions/theme';
export default function ToggleTheme() {
return (
diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx
index 6ea05cf..65e206c 100644
--- a/src/components/ui/avatar.tsx
+++ b/src/components/ui/avatar.tsx
@@ -1,6 +1,6 @@
-import * as React from "react";
-import * as AvatarPrimitive from "@radix-ui/react-avatar";
-import { cn } from "@/lib/utils";
+import * as React from 'react';
+import * as AvatarPrimitive from '@radix-ui/react-avatar';
+import { cn } from '@/lib/utils';
const Avatar = React.forwardRef<
React.ElementRef,
@@ -8,10 +8,7 @@ const Avatar = React.forwardRef<
>(({ className, ...props }, ref) => (
));
@@ -23,7 +20,7 @@ const AvatarImage = React.forwardRef<
>(({ className, ...props }, ref) => (
));
@@ -36,7 +33,7 @@ const AvatarFallback = React.forwardRef<
,
- VariantProps {}
+ extends React.HTMLAttributes, VariantProps {}
function Badge({ className, variant, ...props }: BadgeProps) {
- return (
-
- );
+ return
;
}
export { Badge, badgeVariants };
diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx
index b26ba27..996c411 100644
--- a/src/components/ui/card.tsx
+++ b/src/components/ui/card.tsx
@@ -1,82 +1,55 @@
-import * as React from "react";
-import { cn } from "@/lib/utils";
+import * as React from 'react';
+import { cn } from '@/lib/utils';
-const Card = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-Card.displayName = "Card";
+const Card = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ ),
+);
+Card.displayName = 'Card';
-const CardHeader = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-CardHeader.displayName = "CardHeader";
+const CardHeader = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ ),
+);
+CardHeader.displayName = 'CardHeader';
-const CardTitle = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-CardTitle.displayName = "CardTitle";
+const CardTitle = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ ),
+);
+CardTitle.displayName = 'CardTitle';
const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes
>(({ className, ...props }, ref) => (
-
+
));
-CardDescription.displayName = "CardDescription";
+CardDescription.displayName = 'CardDescription';
-const CardContent = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-CardContent.displayName = "CardContent";
+const CardContent = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ ),
+);
+CardContent.displayName = 'CardContent';
-const CardFooter = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-CardFooter.displayName = "CardFooter";
+const CardFooter = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ ),
+);
+CardFooter.displayName = 'CardFooter';
-export {
- Card,
- CardHeader,
- CardFooter,
- CardTitle,
- CardDescription,
- CardContent,
-};
+export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx
index ff88f54..af552ea 100644
--- a/src/components/ui/dropdown-menu.tsx
+++ b/src/components/ui/dropdown-menu.tsx
@@ -1,7 +1,7 @@
-import * as React from "react";
-import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
-import { Check, ChevronRight, Circle } from "lucide-react";
-import { cn } from "@/lib/utils";
+import * as React from 'react';
+import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
+import { Check, ChevronRight, Circle } from 'lucide-react';
+import { cn } from '@/lib/utils';
const DropdownMenu = DropdownMenuPrimitive.Root;
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
@@ -19,8 +19,8 @@ const DropdownMenuSubTrigger = React.forwardRef<
));
-DropdownMenuSubTrigger.displayName =
- DropdownMenuPrimitive.SubTrigger.displayName;
+DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
const DropdownMenuSubContent = React.forwardRef<
React.ElementRef,
@@ -39,14 +38,13 @@ const DropdownMenuSubContent = React.forwardRef<
));
-DropdownMenuSubContent.displayName =
- DropdownMenuPrimitive.SubContent.displayName;
+DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
const DropdownMenuContent = React.forwardRef<
React.ElementRef,
@@ -57,8 +55,8 @@ const DropdownMenuContent = React.forwardRef<
ref={ref}
sideOffset={sideOffset}
className={cn(
- "bg-popover text-popover-foreground z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md",
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
+ 'bg-popover text-popover-foreground z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md',
+ 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className,
)}
{...props}
@@ -76,8 +74,8 @@ const DropdownMenuItem = React.forwardRef<
));
-DropdownMenuCheckboxItem.displayName =
- DropdownMenuPrimitive.CheckboxItem.displayName;
+DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef,
@@ -116,7 +113,7 @@ const DropdownMenuRadioItem = React.forwardRef<
(({ className, inset, ...props }, ref) => (
));
@@ -155,24 +148,18 @@ const DropdownMenuSeparator = React.forwardRef<
>(({ className, ...props }, ref) => (
));
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
-const DropdownMenuShortcut = ({
- className,
- ...props
-}: React.HTMLAttributes) => {
+const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes) => {
return (
-
+
);
};
-DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
+DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';
export {
DropdownMenu,
diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx
index 22a57fb..d0cdbe8 100644
--- a/src/components/ui/label.tsx
+++ b/src/components/ui/label.tsx
@@ -1,22 +1,17 @@
-import * as React from "react";
-import * as LabelPrimitive from "@radix-ui/react-label";
-import { cva, type VariantProps } from "class-variance-authority";
-import { cn } from "@/lib/utils";
+import * as React from 'react';
+import * as LabelPrimitive from '@radix-ui/react-label';
+import { cva, type VariantProps } from 'class-variance-authority';
+import { cn } from '@/lib/utils';
const labelVariants = cva(
- "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
+ 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
);
const Label = React.forwardRef<
React.ElementRef,
- React.ComponentPropsWithoutRef &
- VariantProps
+ React.ComponentPropsWithoutRef & VariantProps
>(({ className, ...props }, ref) => (
-
+
));
Label.displayName = LabelPrimitive.Root.displayName;
diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx
index 39e7854..b7b4156 100644
--- a/src/components/ui/select.tsx
+++ b/src/components/ui/select.tsx
@@ -1,10 +1,10 @@
-"use client";
+'use client';
-import * as React from "react";
-import * as SelectPrimitive from "@radix-ui/react-select";
-import { Check, ChevronDown, ChevronUp } from "lucide-react";
+import * as React from 'react';
+import * as SelectPrimitive from '@radix-ui/react-select';
+import { Check, ChevronDown, ChevronUp } from 'lucide-react';
-import { cn } from "@/lib/utils";
+import { cn } from '@/lib/utils';
const Select = SelectPrimitive.Root;
@@ -19,7 +19,7 @@ const SelectTrigger = React.forwardRef<
span]:line-clamp-1",
+ 'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus:ring-ring flex h-10 w-full items-center justify-between rounded-md border px-3 py-2 text-sm focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
className,
)}
{...props}
@@ -38,10 +38,7 @@ const SelectScrollUpButton = React.forwardRef<
>(({ className, ...props }, ref) => (
@@ -55,29 +52,25 @@ const SelectScrollDownButton = React.forwardRef<
>(({ className, ...props }, ref) => (
));
-SelectScrollDownButton.displayName =
- SelectPrimitive.ScrollDownButton.displayName;
+SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
const SelectContent = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
->(({ className, children, position = "popper", ...props }, ref) => (
+>(({ className, children, position = 'popper', ...props }, ref) => (
{children}
@@ -105,7 +98,7 @@ const SelectLabel = React.forwardRef<
>(({ className, ...props }, ref) => (
));
@@ -118,7 +111,7 @@ const SelectItem = React.forwardRef<
(({ className, ...props }, ref) => (
));
diff --git a/src/components/ui/switch.tsx b/src/components/ui/switch.tsx
index c721830..22204c9 100644
--- a/src/components/ui/switch.tsx
+++ b/src/components/ui/switch.tsx
@@ -1,6 +1,6 @@
-import * as React from "react";
-import * as SwitchPrimitive from "@radix-ui/react-switch";
-import { cn } from "@/lib/utils";
+import * as React from 'react';
+import * as SwitchPrimitive from '@radix-ui/react-switch';
+import { cn } from '@/lib/utils';
const Switch = React.forwardRef<
React.ElementRef,
@@ -8,7 +8,7 @@ const Switch = React.forwardRef<
>(({ className, ...props }, ref) => (
diff --git a/src/components/ui/toast.tsx b/src/components/ui/toast.tsx
index 2780250..f3f0a1d 100644
--- a/src/components/ui/toast.tsx
+++ b/src/components/ui/toast.tsx
@@ -1,8 +1,8 @@
-import * as React from "react";
-import * as ToastPrimitives from "@radix-ui/react-toast";
-import { cva, type VariantProps } from "class-variance-authority";
-import { X } from "lucide-react";
-import { cn } from "@/lib/utils";
+import * as React from 'react';
+import * as ToastPrimitives from '@radix-ui/react-toast';
+import { cva, type VariantProps } from 'class-variance-authority';
+import { X } from 'lucide-react';
+import { cn } from '@/lib/utils';
const ToastProvider = ToastPrimitives.Provider;
@@ -13,7 +13,7 @@ const ToastViewport = React.forwardRef<
,
- React.ComponentPropsWithoutRef &
- VariantProps
+ React.ComponentPropsWithoutRef & VariantProps
>(({ className, variant, ...props }, ref) => {
return (
(({ className, ...props }, ref) => (
));
@@ -103,7 +102,7 @@ const ToastDescription = React.forwardRef<
>(({ className, ...props }, ref) => (
));
diff --git a/src/components/ui/use-toast.ts b/src/components/ui/use-toast.ts
index 25edf11..7b77a4c 100644
--- a/src/components/ui/use-toast.ts
+++ b/src/components/ui/use-toast.ts
@@ -1,5 +1,5 @@
-import * as React from "react";
-import { Toast, ToastActionElement, ToastProps } from "@/components/ui/toast";
+import * as React from 'react';
+import { Toast, ToastActionElement, ToastProps } from '@/components/ui/toast';
const TOAST_LIMIT = 1;
const TOAST_REMOVE_DELAY = 1000000;
@@ -12,10 +12,10 @@ type ToasterToast = ToastProps & {
};
const actionTypes = {
- ADD_TOAST: "ADD_TOAST",
- UPDATE_TOAST: "UPDATE_TOAST",
- DISMISS_TOAST: "DISMISS_TOAST",
- REMOVE_TOAST: "REMOVE_TOAST",
+ ADD_TOAST: 'ADD_TOAST',
+ UPDATE_TOAST: 'UPDATE_TOAST',
+ DISMISS_TOAST: 'DISMISS_TOAST',
+ REMOVE_TOAST: 'REMOVE_TOAST',
} as const;
let count = 0;
@@ -29,20 +29,20 @@ type ActionType = typeof actionTypes;
type Action =
| {
- type: ActionType["ADD_TOAST"];
+ type: ActionType['ADD_TOAST'];
toast: ToasterToast;
}
| {
- type: ActionType["UPDATE_TOAST"];
+ type: ActionType['UPDATE_TOAST'];
toast: Partial;
}
| {
- type: ActionType["DISMISS_TOAST"];
- toastId?: ToasterToast["id"];
+ type: ActionType['DISMISS_TOAST'];
+ toastId?: ToasterToast['id'];
}
| {
- type: ActionType["REMOVE_TOAST"];
- toastId?: ToasterToast["id"];
+ type: ActionType['REMOVE_TOAST'];
+ toastId?: ToasterToast['id'];
};
interface State {
@@ -59,7 +59,7 @@ const addToRemoveQueue = (toastId: string) => {
const timeout = setTimeout(() => {
toastTimeouts.delete(toastId);
dispatch({
- type: "REMOVE_TOAST",
+ type: 'REMOVE_TOAST',
toastId,
});
}, TOAST_REMOVE_DELAY);
@@ -69,21 +69,19 @@ const addToRemoveQueue = (toastId: string) => {
export const reducer = (state: State, action: Action): State => {
switch (action.type) {
- case "ADD_TOAST":
+ case 'ADD_TOAST':
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
};
- case "UPDATE_TOAST":
+ case 'UPDATE_TOAST':
return {
...state,
- toasts: state.toasts.map((t) =>
- t.id === action.toast.id ? { ...t, ...action.toast } : t,
- ),
+ toasts: state.toasts.map((t) => (t.id === action.toast.id ? { ...t, ...action.toast } : t)),
};
- case "DISMISS_TOAST": {
+ case 'DISMISS_TOAST': {
const { toastId } = action;
// ! Side effects ! - This could be extracted into a dismissToast() action,
@@ -108,7 +106,7 @@ export const reducer = (state: State, action: Action): State => {
),
};
}
- case "REMOVE_TOAST":
+ case 'REMOVE_TOAST':
if (action.toastId === undefined) {
return {
...state,
@@ -133,20 +131,20 @@ function dispatch(action: Action) {
});
}
-type Toast = Omit;
+type Toast = Omit;
function toast({ ...props }: Toast) {
const id = genId();
const update = (props: ToasterToast) =>
dispatch({
- type: "UPDATE_TOAST",
+ type: 'UPDATE_TOAST',
toast: { ...props, id },
});
- const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
+ const dismiss = () => dispatch({ type: 'DISMISS_TOAST', toastId: id });
dispatch({
- type: "ADD_TOAST",
+ type: 'ADD_TOAST',
toast: {
...props,
id,
@@ -180,7 +178,7 @@ function useToast() {
return {
...state,
toast,
- dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
+ dismiss: (toastId?: string) => dispatch({ type: 'DISMISS_TOAST', toastId }),
};
}
diff --git a/src/ipc/account/router.ts b/src/ipc/account/router.ts
index ee900c3..f329c8b 100644
--- a/src/ipc/account/router.ts
+++ b/src/ipc/account/router.ts
@@ -1,12 +1,7 @@
-import { z } from "zod";
-import { os } from "@orpc/server";
-import {
- listAccountsData,
- addAccountSnapshot,
- switchAccount,
- deleteAccount,
-} from "./handler";
-import { AccountSchema } from "../../types/account";
+import { z } from 'zod';
+import { os } from '@orpc/server';
+import { listAccountsData, addAccountSnapshot, switchAccount, deleteAccount } from './handler';
+import { AccountSchema } from '../../types/account';
export const accountRouter = os.router({
listAccounts: os.output(z.array(AccountSchema)).handler(async () => {
diff --git a/src/ipc/app/handlers.ts b/src/ipc/app/handlers.ts
index 2b73ac3..3077b34 100644
--- a/src/ipc/app/handlers.ts
+++ b/src/ipc/app/handlers.ts
@@ -1,5 +1,5 @@
-import { os } from "@orpc/server";
-import { app } from "electron";
+import { os } from '@orpc/server';
+import { app } from 'electron';
export const currentPlatfom = os.handler(() => {
return process.platform;
diff --git a/src/ipc/database/router.ts b/src/ipc/database/router.ts
index d69fa13..fac8b59 100644
--- a/src/ipc/database/router.ts
+++ b/src/ipc/database/router.ts
@@ -1,15 +1,7 @@
-import { z } from "zod";
-import { os } from "@orpc/server";
-import {
- backupAccount,
- restoreAccount,
- getCurrentAccountInfo,
-} from "./handler";
-import {
- AccountBackupDataSchema,
- AccountInfoSchema,
- AccountSchema,
-} from "../../types/account";
+import { z } from 'zod';
+import { os } from '@orpc/server';
+import { backupAccount, restoreAccount, getCurrentAccountInfo } from './handler';
+import { AccountBackupDataSchema, AccountInfoSchema, AccountSchema } from '../../types/account';
export const databaseRouter = os.router({
backupAccount: os
diff --git a/src/ipc/theme/handlers.ts b/src/ipc/theme/handlers.ts
index 52619cc..373db75 100644
--- a/src/ipc/theme/handlers.ts
+++ b/src/ipc/theme/handlers.ts
@@ -1,6 +1,6 @@
-import { os } from "@orpc/server";
-import { nativeTheme } from "electron";
-import { setThemeModeInputSchema } from "./schemas";
+import { os } from '@orpc/server';
+import { nativeTheme } from 'electron';
+import { setThemeModeInputSchema } from './schemas';
export const getCurrentThemeMode = os.handler(() => {
return nativeTheme.themeSource;
@@ -8,28 +8,26 @@ export const getCurrentThemeMode = os.handler(() => {
export const toggleThemeMode = os.handler(() => {
if (nativeTheme.shouldUseDarkColors) {
- nativeTheme.themeSource = "light";
+ nativeTheme.themeSource = 'light';
} else {
- nativeTheme.themeSource = "dark";
+ nativeTheme.themeSource = 'dark';
}
return nativeTheme.shouldUseDarkColors;
});
-export const setThemeMode = os
- .input(setThemeModeInputSchema)
- .handler(({ input: mode }) => {
- switch (mode) {
- case "light":
- nativeTheme.themeSource = "light";
- break;
- case "dark":
- nativeTheme.themeSource = "dark";
- break;
- case "system":
- nativeTheme.themeSource = "system";
- break;
- }
+export const setThemeMode = os.input(setThemeModeInputSchema).handler(({ input: mode }) => {
+ switch (mode) {
+ case 'light':
+ nativeTheme.themeSource = 'light';
+ break;
+ case 'dark':
+ nativeTheme.themeSource = 'dark';
+ break;
+ case 'system':
+ nativeTheme.themeSource = 'system';
+ break;
+ }
- return nativeTheme.themeSource;
- });
+ return nativeTheme.themeSource;
+});
diff --git a/src/ipc/theme/schemas.ts b/src/ipc/theme/schemas.ts
index 8acc0ff..d0041f0 100644
--- a/src/ipc/theme/schemas.ts
+++ b/src/ipc/theme/schemas.ts
@@ -1,3 +1,3 @@
-import { z } from "zod";
+import { z } from 'zod';
-export const setThemeModeInputSchema = z.enum(["light", "dark", "system"]);
+export const setThemeModeInputSchema = z.enum(['light', 'dark', 'system']);
diff --git a/src/ipc/window/handlers.ts b/src/ipc/window/handlers.ts
index af752d4..5bc44d9 100644
--- a/src/ipc/window/handlers.ts
+++ b/src/ipc/window/handlers.ts
@@ -1,33 +1,26 @@
-import { os } from "@orpc/server";
-import { ipcContext } from "../context";
-import { logger } from "../../utils/logger";
+import { os } from '@orpc/server';
+import { ipcContext } from '../context';
+import { logger } from '../../utils/logger';
-export const minimizeWindow = os
- .use(ipcContext.mainWindowContext)
- .handler(({ context }) => {
- const { window } = context;
+export const minimizeWindow = os.use(ipcContext.mainWindowContext).handler(({ context }) => {
+ const { window } = context;
- window.minimize();
- });
+ window.minimize();
+});
-export const maximizeWindow = os
- .use(ipcContext.mainWindowContext)
- .handler(({ context }) => {
- const { window } = context;
+export const maximizeWindow = os.use(ipcContext.mainWindowContext).handler(({ context }) => {
+ const { window } = context;
- if (window.isMaximized()) {
- window.unmaximize();
- } else {
- window.maximize();
- }
- });
+ if (window.isMaximized()) {
+ window.unmaximize();
+ } else {
+ window.maximize();
+ }
+});
-export const closeWindow = os
- .use(ipcContext.mainWindowContext)
- .handler(({ context }) => {
- logger.info('IPC: closeWindow called');
- const { window } = context;
-
- window.close();
- });
+export const closeWindow = os.use(ipcContext.mainWindowContext).handler(({ context }) => {
+ logger.info('IPC: closeWindow called');
+ const { window } = context;
+ window.close();
+});
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index 365058c..9ad0df4 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -1,5 +1,5 @@
-import { type ClassValue, clsx } from "clsx";
-import { twMerge } from "tailwind-merge";
+import { type ClassValue, clsx } from 'clsx';
+import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
diff --git a/src/renderer.ts b/src/renderer.ts
index f9393a8..57eb0d0 100644
--- a/src/renderer.ts
+++ b/src/renderer.ts
@@ -1 +1 @@
-import "@/App";
+import '@/App';
diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx
index 3c85c81..8ac9d2a 100644
--- a/src/routes/__root.tsx
+++ b/src/routes/__root.tsx
@@ -1,5 +1,5 @@
-import { MainLayout } from "@/layouts/MainLayout";
-import { createRootRoute } from "@tanstack/react-router";
+import { MainLayout } from '@/layouts/MainLayout';
+import { createRootRoute } from '@tanstack/react-router';
/* import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' */
/*
diff --git a/src/styles/global.css b/src/styles/global.css
index bc67050..8645b76 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -1,6 +1,6 @@
-@import "tailwindcss";
+@import 'tailwindcss';
-@plugin "tailwindcss-animate";
+@plugin 'tailwindcss-animate';
@custom-variant dark (&:is(.dark *));
@@ -32,7 +32,7 @@
--color-card: hsl(var(--card));
--color-card-foreground: hsl(var(--card-foreground));
- --font-sans: Geist, "Microsoft YaHei", "Heiti SC", "WenQuanYi Micro Hei", sans-serif;
+ --font-sans: Geist, 'Microsoft YaHei', 'Heiti SC', 'WenQuanYi Micro Hei', sans-serif;
--font-mono: Geist Mono, monospace;
--font-tomorrow: Tomorrow, sans-serif;
@@ -74,45 +74,44 @@
@layer base {
@font-face {
- font-family: "Geist";
+ font-family: 'Geist';
- src: url("../assets/fonts/geist/geist.ttf") format("truetype");
+ src: url('../assets/fonts/geist/geist.ttf') format('truetype');
}
@font-face {
- font-family: "Geist Mono";
+ font-family: 'Geist Mono';
font-display: swap;
- src: url("../assets/fonts/geist-mono/geist-mono.ttf") format("truetype");
+ src: url('../assets/fonts/geist-mono/geist-mono.ttf') format('truetype');
}
@font-face {
- font-family: "Tomorrow";
+ font-family: 'Tomorrow';
font-weight: 400;
font-style: normal;
- src: url("../assets/fonts/tomorrow/tomorrow-regular.ttf") format("truetype");
+ src: url('../assets/fonts/tomorrow/tomorrow-regular.ttf') format('truetype');
}
@font-face {
- font-family: "Tomorrow";
+ font-family: 'Tomorrow';
font-weight: 400;
font-style: italic;
- src: url("../assets/fonts/tomorrow/tomorrow-italic.ttf") format("truetype");
+ src: url('../assets/fonts/tomorrow/tomorrow-italic.ttf') format('truetype');
}
@font-face {
- font-family: "Tomorrow";
+ font-family: 'Tomorrow';
font-weight: 700;
font-style: normal;
- src: url("../assets/fonts/tomorrow/tomorrow-bold.ttf") format("truetype");
+ src: url('../assets/fonts/tomorrow/tomorrow-bold.ttf') format('truetype');
}
@font-face {
- font-family: "Tomorrow";
+ font-family: 'Tomorrow';
font-weight: 700;
font-style: italic;
- src: url("../assets/fonts/tomorrow/tomorrow-bold-italic.ttf")
- format("truetype");
+ src: url('../assets/fonts/tomorrow/tomorrow-bold-italic.ttf') format('truetype');
}
}
diff --git a/src/tests/unit/account.test.ts b/src/tests/unit/account.test.ts
index a01a626..1a72df1 100644
--- a/src/tests/unit/account.test.ts
+++ b/src/tests/unit/account.test.ts
@@ -1,45 +1,45 @@
-import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
+import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import {
listAccountsData,
addAccountSnapshot,
switchAccount,
deleteAccount,
-} from "../../ipc/account/handler";
-import fs from "fs";
-import path from "path";
+} from '../../ipc/account/handler';
+import fs from 'fs';
+import path from 'path';
// Mock dependencies
-vi.mock("../../utils/paths", async () => {
- const path = await import("path");
- const agentDir = path.join(process.cwd(), "temp_test_agent");
+vi.mock('../../utils/paths', async () => {
+ const path = await import('path');
+ const agentDir = path.join(process.cwd(), 'temp_test_agent');
return {
getAgentDir: vi.fn(() => agentDir),
- getAccountsFilePath: vi.fn(() => path.join(agentDir, "accounts.json")),
- getBackupsDir: vi.fn(() => path.join(agentDir, "backups")),
- getAntigravityDbPath: vi.fn(() => path.join(agentDir, "state.vscdb")),
- getAntigravityExecutablePath: vi.fn(() => "mock_exec_path"),
+ getAccountsFilePath: vi.fn(() => path.join(agentDir, 'accounts.json')),
+ getBackupsDir: vi.fn(() => path.join(agentDir, 'backups')),
+ getAntigravityDbPath: vi.fn(() => path.join(agentDir, 'state.vscdb')),
+ getAntigravityExecutablePath: vi.fn(() => 'mock_exec_path'),
};
});
-vi.mock("../../ipc/database/handler", () => ({
+vi.mock('../../ipc/database/handler', () => ({
getCurrentAccountInfo: vi.fn(() => ({
- email: "test@example.com",
- name: "Test User",
+ email: 'test@example.com',
+ name: 'Test User',
isAuthenticated: true,
})),
- backupAccount: vi.fn((account) => ({ version: "1.0", account, data: {} })),
+ backupAccount: vi.fn((account) => ({ version: '1.0', account, data: {} })),
restoreAccount: vi.fn(),
getDatabaseConnection: vi.fn(),
}));
-vi.mock("../../ipc/process/handler", () => ({
+vi.mock('../../ipc/process/handler', () => ({
closeAntigravity: vi.fn(),
startAntigravity: vi.fn(),
isProcessRunning: vi.fn(() => Promise.resolve(false)),
}));
-describe("Account Handler", () => {
- const testAgentDir = path.join(process.cwd(), "temp_test_agent");
+describe('Account Handler', () => {
+ const testAgentDir = path.join(process.cwd(), 'temp_test_agent');
beforeEach(() => {
if (fs.existsSync(testAgentDir)) {
@@ -54,22 +54,22 @@ describe("Account Handler", () => {
}
});
- it("should add account snapshot", async () => {
+ it('should add account snapshot', async () => {
const account = await addAccountSnapshot();
- expect(account.email).toBe("test@example.com");
+ expect(account.email).toBe('test@example.com');
const accounts = await listAccountsData();
expect(accounts).toHaveLength(1);
- expect(accounts[0].email).toBe("test@example.com");
+ expect(accounts[0].email).toBe('test@example.com');
});
- it("should switch account", async () => {
+ it('should switch account', async () => {
const account = await addAccountSnapshot();
await switchAccount(account.id);
// Mocks should be called, verified by implementation logic not throwing
});
- it("should delete account", async () => {
+ it('should delete account', async () => {
const account = await addAccountSnapshot();
await deleteAccount(account.id);
diff --git a/src/tests/unit/logger.test.ts b/src/tests/unit/logger.test.ts
index 068a8b2..e3853a3 100644
--- a/src/tests/unit/logger.test.ts
+++ b/src/tests/unit/logger.test.ts
@@ -1,26 +1,26 @@
-import { describe, it, expect, vi, afterEach, beforeEach } from "vitest";
-import { logger } from "../../utils/logger";
-import fs from "fs";
-import path from "path";
+import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest';
+import { logger } from '../../utils/logger';
+import fs from 'fs';
+import path from 'path';
-vi.mock("../../utils/paths", async () => {
- const path = await import("path");
+vi.mock('../../utils/paths', async () => {
+ const path = await import('path');
return {
- getAgentDir: vi.fn(() => path.join(process.cwd(), "temp_test_logs")),
+ getAgentDir: vi.fn(() => path.join(process.cwd(), 'temp_test_logs')),
};
});
-describe("Logger Utilities", () => {
- const testLogDir = path.join(process.cwd(), "temp_test_logs");
- const testLogFile = path.join(testLogDir, "app.log");
+describe('Logger Utilities', () => {
+ const testLogDir = path.join(process.cwd(), 'temp_test_logs');
+ const testLogFile = path.join(testLogDir, 'app.log');
beforeEach(() => {
- console.log("Test Log Dir:", testLogDir);
+ console.log('Test Log Dir:', testLogDir);
if (fs.existsSync(testLogDir)) {
fs.rmSync(testLogDir, { recursive: true, force: true });
}
fs.mkdirSync(testLogDir, { recursive: true });
- console.log("Dir created:", fs.existsSync(testLogDir));
+ console.log('Dir created:', fs.existsSync(testLogDir));
});
afterEach(() => {
@@ -29,24 +29,24 @@ describe("Logger Utilities", () => {
}
});
- it("should create log file if it does not exist", () => {
- logger.info("Test message");
+ it('should create log file if it does not exist', () => {
+ logger.info('Test message');
expect(fs.existsSync(testLogFile)).toBe(true);
});
- it("should write formatted message to file", () => {
- const message = "Test info message";
+ it('should write formatted message to file', () => {
+ const message = 'Test info message';
logger.info(message);
- const content = fs.readFileSync(testLogFile, "utf-8");
- expect(content).toContain("[INFO]");
+ const content = fs.readFileSync(testLogFile, 'utf-8');
+ expect(content).toContain('[INFO]');
expect(content).toContain(message);
});
- it("should log error messages", () => {
- const message = "Test error message";
+ it('should log error messages', () => {
+ const message = 'Test error message';
logger.error(message);
- const content = fs.readFileSync(testLogFile, "utf-8");
- expect(content).toContain("[ERROR]");
+ const content = fs.readFileSync(testLogFile, 'utf-8');
+ expect(content).toContain('[ERROR]');
expect(content).toContain(message);
});
});
diff --git a/src/tests/unit/process.test.ts b/src/tests/unit/process.test.ts
index 3333b56..6cf32b6 100644
--- a/src/tests/unit/process.test.ts
+++ b/src/tests/unit/process.test.ts
@@ -1,29 +1,27 @@
-import { describe, it, expect, vi } from "vitest";
-import {
- isProcessRunning,
- closeAntigravity,
- startAntigravity,
-} from "../../ipc/process/handler";
-import { exec } from "child_process";
+import { describe, it, expect, vi } from 'vitest';
+import { isProcessRunning, closeAntigravity, startAntigravity } from '../../ipc/process/handler';
+import { exec } from 'child_process';
-vi.mock("child_process", () => {
+vi.mock('child_process', () => {
return {
exec: vi.fn(),
+ default: { exec: vi.fn() },
};
});
// Mock promisify to return a function that returns a promise
-vi.mock("util", () => ({
+vi.mock('util', () => ({
promisify: (fn: unknown) => fn,
+ default: { promisify: (fn: unknown) => fn },
}));
-describe("Process Handler", () => {
- it("should check if process is running", async () => {
+describe('Process Handler', () => {
+ it('should check if process is running', async () => {
// Mock exec to return stdout
const execMock = exec as unknown as ReturnType;
execMock.mockImplementation((cmd, cb) => {
- if (cb) cb(null, { stdout: "12345" }, { stderr: "" });
- return { stdout: "12345" };
+ if (cb) cb(null, { stdout: '12345' }, { stderr: '' });
+ return { stdout: '12345' };
});
// Wait, promisify wraps exec.
@@ -49,7 +47,7 @@ describe("Process Handler", () => {
// Skip complex mocking tests for now as they are fragile without proper setup.
// I'll write a basic test structure.
- it("should be defined", () => {
+ it('should be defined', () => {
expect(isProcessRunning).toBeDefined();
expect(closeAntigravity).toBeDefined();
expect(startAntigravity).toBeDefined();
diff --git a/src/tests/unit/toggle-theme.test.tsx b/src/tests/unit/toggle-theme.test.tsx
index 9883e1a..c0ff32e 100644
--- a/src/tests/unit/toggle-theme.test.tsx
+++ b/src/tests/unit/toggle-theme.test.tsx
@@ -1,27 +1,27 @@
// @vitest-environment happy-dom
-import { render } from "@testing-library/react";
-import { test, expect } from "vitest";
-import ToggleTheme from "@/components/toggle-theme";
+import { render } from '@testing-library/react';
+import { test, expect } from 'vitest';
+import ToggleTheme from '@/components/toggle-theme';
-test("renders ToggleTheme", () => {
+test('renders ToggleTheme', () => {
const { getByRole } = render( );
- const isButton = getByRole("button");
+ const isButton = getByRole('button');
expect(isButton).toBeInTheDocument();
});
-test("has icon", () => {
+test('has icon', () => {
const { getByRole } = render( );
- const button = getByRole("button");
- const icon = button.querySelector("svg");
+ const button = getByRole('button');
+ const icon = button.querySelector('svg');
expect(icon).toBeInTheDocument();
});
-test("is moon icon", () => {
- const svgIconClassName: string = "lucide-moon";
+test('is moon icon', () => {
+ const svgIconClassName: string = 'lucide-moon';
const { getByRole } = render( );
- const svg = getByRole("button").querySelector("svg");
+ const svg = getByRole('button').querySelector('svg');
expect(svg?.classList).toContain(svgIconClassName);
});
diff --git a/src/types/theme-mode.ts b/src/types/theme-mode.ts
index e73a3a6..1fc6be1 100644
--- a/src/types/theme-mode.ts
+++ b/src/types/theme-mode.ts
@@ -1 +1 @@
-export type ThemeMode = "dark" | "light" | "system";
+export type ThemeMode = 'dark' | 'light' | 'system';
diff --git a/src/utils/routes.ts b/src/utils/routes.ts
index 6183183..176592f 100644
--- a/src/utils/routes.ts
+++ b/src/utils/routes.ts
@@ -1,7 +1,7 @@
-import { routeTree } from "@/routeTree.gen";
-import { createMemoryHistory, createRouter } from "@tanstack/react-router";
+import { routeTree } from '@/routeTree.gen';
+import { createMemoryHistory, createRouter } from '@tanstack/react-router';
-declare module "@tanstack/react-router" {
+declare module '@tanstack/react-router' {
interface Register {
router: typeof router;
}
@@ -11,6 +11,6 @@ export const router = createRouter({
defaultPendingMinMs: 0,
routeTree,
history: createMemoryHistory({
- initialEntries: ["/"],
+ initialEntries: ['/'],
}),
});
diff --git a/test-process.ts b/test-process.ts
index 1b62458..6f87d37 100644
--- a/test-process.ts
+++ b/test-process.ts
@@ -1,4 +1,3 @@
-
import { exec } from 'child_process';
import { promisify } from 'util';
import fs from 'fs';
diff --git a/vite.preload.config.mts b/vite.preload.config.mts
index fbf7eda..690be5b 100644
--- a/vite.preload.config.mts
+++ b/vite.preload.config.mts
@@ -1,4 +1,4 @@
-import { defineConfig } from "vite";
+import { defineConfig } from 'vite';
// https://vitejs.dev/config
export default defineConfig({});