├── src-tauri ├── src │ ├── services │ │ └── mod.rs │ ├── filesystem │ │ ├── file_parsers │ │ │ ├── mod.rs │ │ │ ├── pdf.rs │ │ │ └── csv.rs │ │ ├── mod.rs │ │ └── file_types.rs │ ├── codex_commands │ │ ├── config.rs │ │ ├── session_files │ │ │ ├── usage.rs │ │ │ ├── scanner.rs │ │ │ ├── mod.rs │ │ │ ├── update.rs │ │ │ ├── cache.rs │ │ │ └── notes.rs │ │ ├── state.rs │ │ ├── config │ │ │ ├── provider.rs │ │ │ ├── project.rs │ │ │ └── profile.rs │ │ ├── check.rs │ │ ├── mcp.rs │ │ ├── mod.rs │ │ ├── initialize.rs │ │ ├── listeners.rs │ │ └── reviews.rs │ ├── commands │ │ ├── mod.rs │ │ ├── file.rs │ │ ├── window.rs │ │ ├── remote.rs │ │ ├── env.rs │ │ └── terminal.rs │ ├── main.rs │ └── state.rs ├── build.rs ├── icons │ ├── icon.ico │ ├── icon.png │ ├── 32x32.png │ ├── 64x64.png │ ├── icon.icns │ ├── 128x128.png │ ├── StoreLogo.png │ ├── 128x128@2x.png │ ├── Square30x30Logo.png │ ├── Square44x44Logo.png │ ├── Square71x71Logo.png │ ├── Square89x89Logo.png │ ├── Square107x107Logo.png │ ├── Square142x142Logo.png │ ├── Square150x150Logo.png │ ├── Square284x284Logo.png │ └── Square310x310Logo.png ├── .gitignore ├── capabilities │ ├── desktop.json │ └── default.json ├── Info.plist ├── Cargo.toml └── entitlements.plist ├── src ├── vite-env.d.ts ├── components │ ├── review │ │ ├── type.ts │ │ ├── ReviewPatchOutputIcon.tsx │ │ └── ReviewFilters.tsx │ ├── notes │ │ ├── index.ts │ │ └── NoteToChat.tsx │ ├── mcp │ │ ├── index.tsx │ │ ├── index.ts │ │ └── McpLinkerButton.tsx │ ├── settings │ │ ├── index.ts │ │ └── SettingsSidebar.tsx │ ├── panels │ │ ├── index.tsx │ │ ├── UsagePanel.tsx │ │ ├── McpPanel.tsx │ │ ├── SettingsPanel.tsx │ │ └── FileExplorerPanel.tsx │ ├── cc │ │ ├── ToolWidgets.new.tsx │ │ ├── index.ts │ │ ├── widgets │ │ │ └── index.ts │ │ ├── TokenCounter.tsx │ │ └── ProjectViewErrorBoundary.tsx │ ├── common │ │ ├── SimpleMarkdown.tsx │ │ ├── TokenCountInfo.tsx │ │ ├── SearchInput.tsx │ │ ├── LanguageSelector.tsx │ │ ├── AccentColorSelector.tsx │ │ └── UserDropdown.tsx │ ├── config │ │ └── config-tip.tsx │ ├── events │ │ ├── OutputBlock.tsx │ │ ├── AccordionMsg.tsx │ │ ├── PatchApplyBeginItem.tsx │ │ ├── EventBubble.tsx │ │ ├── TurnDiffPanel.tsx │ │ ├── PatchItem.tsx │ │ ├── helpers.ts │ │ └── ExecCommandBeginItem.tsx │ ├── chat │ │ ├── EventMsgType.tsx │ │ ├── actions │ │ │ └── ScrollButtons.tsx │ │ ├── BouncingDotsLoader.tsx │ │ └── EventFilterPopover.tsx │ ├── ui │ │ ├── toaster.tsx │ │ ├── label.tsx │ │ ├── separator.tsx │ │ ├── textarea.tsx │ │ ├── progress.tsx │ │ ├── collapsible.tsx │ │ ├── input.tsx │ │ ├── switch.tsx │ │ ├── checkbox.tsx │ │ ├── use-toast.ts │ │ ├── badge.tsx │ │ ├── scroll-area.tsx │ │ └── pagination.tsx │ ├── usage │ │ ├── index.tsx │ │ ├── SummaryCard.tsx │ │ ├── common.tsx │ │ ├── TimelineChart.tsx │ │ ├── MostUsedModelsCard.tsx │ │ ├── TopProjectsCard.tsx │ │ ├── ModelUsageChart.tsx │ │ └── ProjectUsageChart.tsx │ ├── filetree │ │ └── languageMap.ts │ └── SourceControl.tsx ├── assets │ ├── nfo │ │ ├── opcode-nfo.ogg │ │ └── asterisk-logo.png │ └── fonts │ │ └── inter │ │ └── Inter.ttf ├── pages │ ├── dxt.tsx │ ├── review.tsx │ └── chat.tsx ├── lib │ ├── utils.ts │ ├── supabase.ts │ └── i18n.ts ├── stores │ ├── config │ │ ├── index.ts │ │ ├── useExecCommandStore.ts │ │ └── useSandboxStore.ts │ ├── settings │ │ ├── index.ts │ │ ├── LocaleStore.ts │ │ ├── PromptOptimizerStore.ts │ │ ├── ThemeStore.ts │ │ └── RemoteAccessStore.ts │ ├── index.ts │ ├── codex │ │ ├── useLayoutStore.ts │ │ ├── useResumeConversationStore.ts │ │ ├── useCodexStore.ts │ │ ├── index.ts │ │ ├── useTaskStore.ts │ │ ├── useTokenCountStore.ts │ │ ├── useConversationMetadataStore.ts │ │ ├── useTurnDiffStore.ts │ │ ├── useConversationListenerStore.ts │ │ └── useSessionStore.ts │ ├── useAuthStore.ts │ ├── README.md │ ├── ContextFilesStore.ts │ └── FolderStore.ts ├── types │ ├── Message.ts │ ├── index.ts │ ├── config.ts │ ├── review.ts │ ├── mcp.ts │ ├── remote.ts │ └── chat.ts ├── utils │ ├── formatDuration.ts │ ├── beep.ts │ ├── runCommand.ts │ ├── formatSessionSource.ts │ ├── errorUtils.ts │ ├── getDurationLable.ts │ ├── formater.ts │ ├── buildParams.ts │ ├── backendErrorListener.ts │ ├── renameConversation.ts │ └── handleTaskComplete.ts ├── hooks │ ├── codex │ │ ├── index.ts │ │ ├── useTokenCount.ts │ │ └── useBuildNewConversationParams.ts │ ├── use-mobile.ts │ ├── useFileTokens.ts │ ├── index.ts │ ├── useTheme.ts │ ├── useAuth.ts │ ├── useDebounce.ts │ └── useLoadingState.ts ├── locales │ ├── index.ts │ ├── zh.ts │ ├── ja.ts │ └── en.ts ├── App.tsx ├── services │ └── remoteAccessService.ts └── main.tsx ├── cc ├── src │ ├── process │ │ └── mod.rs │ ├── commands │ │ └── mod.rs │ ├── lib.rs │ └── bin │ │ └── codexia-web.rs └── Cargo.toml ├── codex-client ├── src │ ├── services │ │ ├── mod.rs │ │ ├── coder.rs │ │ └── codex.rs │ ├── utils │ │ └── mod.rs │ ├── session_files │ │ └── mod.rs │ ├── lib.rs │ ├── config │ │ └── mod.rs │ ├── db │ │ └── scan_info.rs │ └── transport │ │ └── mod.rs └── Cargo.toml ├── public ├── grok.png ├── btc-qr.jpeg ├── codexia.png ├── usdc-qr.jpeg ├── codexia-reason.png ├── codexia-web-search.png ├── reddit.svg ├── discord.svg ├── vite.svg └── codex.svg ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── 4-docs-issue.yml │ └── 5-feature-request.yml └── workflows │ ├── ci-windows.yml │ ├── ci-windows-portable.yml │ └── ci-linux-arm.yml ├── .vscode └── extensions.json ├── docs ├── README.md ├── USAGE.md ├── pre-commit ├── config.toml ├── vision.md └── auth-codexia.md ├── .env.example ├── scripts ├── build_codexia_web.sh └── release_codex_bindings.sh ├── tsconfig.app.json ├── tsconfig.node.json ├── codex-bindings ├── Cargo.toml └── src │ └── bin │ └── codex_export_bindings.rs ├── index.html ├── components.json ├── .gitignore ├── tsconfig.json ├── CLAUDE.md ├── AGENTS.md ├── LICENSE └── vite.config.ts /src-tauri/src/services/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod remote; 2 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /cc/src/process/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod registry; 2 | 3 | pub use registry::*; 4 | -------------------------------------------------------------------------------- /codex-client/src/services/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod codex; 2 | pub mod coder; 3 | -------------------------------------------------------------------------------- /src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /public/grok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/public/grok.png -------------------------------------------------------------------------------- /src/components/review/type.ts: -------------------------------------------------------------------------------- 1 | export type RawMessage = Record; 2 | -------------------------------------------------------------------------------- /codex-client/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod codex_discovery; 2 | pub mod coder_discovery; 3 | -------------------------------------------------------------------------------- /public/btc-qr.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/public/btc-qr.jpeg -------------------------------------------------------------------------------- /public/codexia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/public/codexia.png -------------------------------------------------------------------------------- /public/usdc-qr.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/public/usdc-qr.jpeg -------------------------------------------------------------------------------- /src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /src-tauri/src/filesystem/file_parsers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod csv; 2 | pub mod pdf; 3 | pub mod xlsx; 4 | -------------------------------------------------------------------------------- /public/codexia-reason.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/public/codexia-reason.png -------------------------------------------------------------------------------- /src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /src-tauri/icons/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/64x64.png -------------------------------------------------------------------------------- /src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /src-tauri/src/codex_commands/config.rs: -------------------------------------------------------------------------------- 1 | pub mod profile; 2 | pub mod project; 3 | pub mod provider; 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ["https://buy.polar.sh/polar_cl_4hLxfGGjyEuiyX6zBlVp2HNzygaGJZuPT3AvC2cUzlH"] -------------------------------------------------------------------------------- /public/codexia-web-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/public/codexia-web-search.png -------------------------------------------------------------------------------- /src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /src-tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /src/assets/nfo/opcode-nfo.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src/assets/nfo/opcode-nfo.ogg -------------------------------------------------------------------------------- /src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"] 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/fonts/inter/Inter.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src/assets/fonts/inter/Inter.ttf -------------------------------------------------------------------------------- /src/assets/nfo/asterisk-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src/assets/nfo/asterisk-logo.png -------------------------------------------------------------------------------- /src/components/notes/index.ts: -------------------------------------------------------------------------------- 1 | export { NoteList } from "./NoteList"; 2 | export { NoteEditor } from "./NoteEditor"; -------------------------------------------------------------------------------- /src/pages/dxt.tsx: -------------------------------------------------------------------------------- 1 | export default function DxtPage() { 2 | return ( 3 |
hi dxt
4 | ) 5 | } -------------------------------------------------------------------------------- /src-tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milisp/codexia/HEAD/src-tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /src-tauri/src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod env; 2 | pub mod file; 3 | pub mod remote; 4 | pub mod window; 5 | pub mod terminal; 6 | -------------------------------------------------------------------------------- /src/components/mcp/index.tsx: -------------------------------------------------------------------------------- 1 | export { McpServerForm } from './McpServerForm'; 2 | export { McpLinkerButton } from './McpLinkerButton'; -------------------------------------------------------------------------------- /src/components/settings/index.ts: -------------------------------------------------------------------------------- 1 | export { CodexAuth } from "./CodexAuth" 2 | export { RateLimitSettings } from "./RateLimitSettings" -------------------------------------------------------------------------------- /src/components/panels/index.tsx: -------------------------------------------------------------------------------- 1 | export { ProjectPanel } from "./ProjectPanel"; 2 | export { FileExplorerPanel } from "./FileExplorerPanel"; 3 | -------------------------------------------------------------------------------- /src/pages/review.tsx: -------------------------------------------------------------------------------- 1 | import { Review } from "@/components/review"; 2 | 3 | export default function ReviewPage() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /src/pages/chat.tsx: -------------------------------------------------------------------------------- 1 | import { ChatView } from "@/components/chat/ChatView"; 2 | 3 | export default function ChatPage() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /cc/src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod agents; 2 | pub mod claude; 3 | pub mod mcp; 4 | pub mod proxy; 5 | pub mod slash_commands; 6 | pub mod storage; 7 | pub mod usage; 8 | -------------------------------------------------------------------------------- /src/components/mcp/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./McpServerForm"; 2 | export * from "./McpLinkerButton"; 3 | export * from "./DefaultMcpServers"; 4 | export * from "./McpServerCard"; 5 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Codexia Docs 2 | 3 | - [USAGE](./USAGE.md) — Installation, development, and how to use the app 4 | - [ARCHITECTURE](./ARCHITECTURE.md) — High-level design and components 5 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | VITE_REDIRECT_URL=https://milisp.vercel.app/auth-success 2 | VITE_SUPABASE_URL=https://xxx.supabase.co 3 | VITE_SUPABASE_ANON_KEY=eyJhbxxx 4 | 5 | VITE_ENABLE_AUTH=false # force login if true -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /src/components/cc/ToolWidgets.new.tsx: -------------------------------------------------------------------------------- 1 | // This file re-exports all widgets from the widgets directory 2 | // It maintains backward compatibility with the original ToolWidgets.tsx 3 | 4 | export * from './widgets'; -------------------------------------------------------------------------------- /src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Generated by Tauri 6 | # will have schema files for capabilities auto-completion 7 | /gen/schemas 8 | -------------------------------------------------------------------------------- /scripts/build_codexia_web.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | VERSION=$(grep -A 1 '\[workspace.package\]' Cargo.toml | grep version | sed 's/.*"\([^"]*\)".*/\1/') 5 | cargo build -p cc@$VERSION --bin codexia-web --release -------------------------------------------------------------------------------- /src-tauri/src/codex_commands/session_files/usage.rs: -------------------------------------------------------------------------------- 1 | use serde_json::Value; 2 | 3 | #[tauri::command] 4 | pub async fn read_token_usage() -> Result, String> { 5 | codex_client::db::read_token_usage().await 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // ... 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": [ 7 | "./src/*" 8 | ] 9 | } 10 | // ... 11 | } 12 | } -------------------------------------------------------------------------------- /src-tauri/src/codex_commands/session_files/scanner.rs: -------------------------------------------------------------------------------- 1 | use serde_json::Value; 2 | 3 | #[tauri::command] 4 | pub async fn scan_projects() -> Result, String> { 5 | codex_client::session_files::scanner::scan_projects().await 6 | } 7 | -------------------------------------------------------------------------------- /src-tauri/src/filesystem/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod directory_ops; 2 | pub mod file_analysis; 3 | pub mod file_io; 4 | pub mod file_parsers; 5 | pub mod file_types; 6 | pub mod git_diff; 7 | pub mod git_status; 8 | pub mod watch; 9 | pub mod git_worktree; 10 | -------------------------------------------------------------------------------- /src-tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | // Prevents additional console window on Windows in release, DO NOT REMOVE!! 2 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 3 | 4 | fn main() { 5 | let _ = fix_path_env::fix(); 6 | codexia_lib::run() 7 | } 8 | -------------------------------------------------------------------------------- /docs/USAGE.md: -------------------------------------------------------------------------------- 1 | # Using Codexia 2 | 3 | [Architecture](./ARCHITECTURE.md) 4 | 5 | ## Download from github release 6 | 7 | [github release](https://github.com/milisp/codexia/releases) 8 | 9 | ## Troubleshooting 10 | 11 | - Ask ChatGPT or any AI 12 | - google 13 | -------------------------------------------------------------------------------- /src-tauri/src/codex_commands/session_files/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cache; 2 | pub mod scanner; 3 | pub mod update; 4 | pub mod usage; 5 | pub mod notes; 6 | 7 | pub use cache::*; 8 | pub use scanner::*; 9 | pub use update::*; 10 | pub use usage::*; 11 | pub use notes::*; 12 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /codex-bindings/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "codex-bindings" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | 7 | [dependencies] 8 | codex-protocol.workspace = true 9 | codex-app-server-protocol.workspace = true 10 | ts-rs.workspace = true 11 | -------------------------------------------------------------------------------- /src-tauri/capabilities/desktop.json: -------------------------------------------------------------------------------- 1 | { 2 | "identifier": "desktop-capability", 3 | "platforms": [ 4 | "macOS", 5 | "windows", 6 | "linux" 7 | ], 8 | "windows": [ 9 | "main", 10 | "main-*" 11 | ], 12 | "permissions": [ 13 | "updater:default" 14 | ] 15 | } -------------------------------------------------------------------------------- /src/stores/config/index.ts: -------------------------------------------------------------------------------- 1 | export { useApprovalStore } from "./useApprovalStore"; 2 | export { useExecCommandStore } from "./useExecCommandStore"; 3 | export { useSandboxStore } from "./useSandboxStore"; 4 | export { 5 | type ProviderStateModelProvider, 6 | useProviderStore, 7 | } from "./useProviderStore"; 8 | -------------------------------------------------------------------------------- /src-tauri/src/codex_commands/session_files/update.rs: -------------------------------------------------------------------------------- 1 | #[tauri::command] 2 | pub async fn update_cache_title( 3 | project_path: String, 4 | session_path: String, 5 | preview: String, 6 | ) -> Result<(), String> { 7 | codex_client::db::update_session_preview(&project_path, &session_path, &preview) 8 | } 9 | -------------------------------------------------------------------------------- /src/types/Message.ts: -------------------------------------------------------------------------------- 1 | import { EventMsg } from "@/bindings/EventMsg"; 2 | 3 | export type EventWithId = { id: string; msg: EventMsg; request_id?: number }; 4 | 5 | export interface Message { 6 | id: string; 7 | conversationId: string; 8 | type: string; 9 | timestamp: number; 10 | payload: any; 11 | } 12 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | export type { Message} from "./Message" 2 | export type { McpServerConfig } from "./mcp" 3 | import { EventMsg } from "@/bindings/EventMsg"; 4 | 5 | export interface Line { 6 | method: string; 7 | params: { 8 | id: string; 9 | msg: EventMsg; 10 | conversationId: string; 11 | }; 12 | } -------------------------------------------------------------------------------- /cc/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod claude_binary; 2 | pub mod claude_discovery; 3 | pub mod commands; 4 | pub mod checkpoint; 5 | pub mod process; 6 | pub mod web_server; 7 | 8 | // Re-export commonly used types and functions 9 | pub use claude_discovery::discover_claude_command; 10 | pub use checkpoint::manager::CheckpointManager; 11 | -------------------------------------------------------------------------------- /src/utils/formatDuration.ts: -------------------------------------------------------------------------------- 1 | export const formatDurationMs = (durationMs: number): string => { 2 | const totalSeconds = Math.max(0, Math.floor(durationMs / 1000)); 3 | const minutes = Math.floor(totalSeconds / 60); 4 | const seconds = totalSeconds % 60; 5 | return `${minutes}:${seconds.toString().padStart(2, "0")}`; 6 | }; 7 | -------------------------------------------------------------------------------- /src/types/config.ts: -------------------------------------------------------------------------------- 1 | export interface ModelProvider { 2 | name: string; 3 | base_url: string; 4 | env_key?: string; 5 | } 6 | 7 | export interface Profile { 8 | model_provider: string; 9 | model?: string; 10 | } 11 | 12 | export interface ProviderConfig { 13 | provider: ModelProvider; 14 | profile?: Profile; 15 | } 16 | -------------------------------------------------------------------------------- /src/stores/settings/index.ts: -------------------------------------------------------------------------------- 1 | export { useLocaleStore } from "./LocaleStore"; 2 | export { useLayoutStore } from "./layoutStore"; 3 | export { usePromptOptimizerStore } from "./PromptOptimizerStore"; 4 | export { useRemoteAccessStore } from "./RemoteAccessStore"; 5 | export { useThemeStore } from "./ThemeStore"; 6 | export { useSettingsStore } from "./SettingsStore"; 7 | -------------------------------------------------------------------------------- /src/stores/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | useLocaleStore, 3 | useLayoutStore, 4 | usePromptOptimizerStore, 5 | useRemoteAccessStore, 6 | useThemeStore, 7 | useSettingsStore, 8 | } from "./settings"; 9 | export { 10 | type ProviderStateModelProvider, 11 | useApprovalStore, 12 | useExecCommandStore, 13 | useProviderStore, 14 | useSandboxStore, 15 | } from "./config"; 16 | -------------------------------------------------------------------------------- /src/utils/beep.ts: -------------------------------------------------------------------------------- 1 | export function playBeep() { 2 | const audioCtx = new window.AudioContext(); 3 | const oscillator = audioCtx.createOscillator(); 4 | oscillator.type = "sine"; 5 | oscillator.frequency.setValueAtTime(440, audioCtx.currentTime); 6 | oscillator.connect(audioCtx.destination); 7 | oscillator.start(); 8 | oscillator.stop(audioCtx.currentTime + 0.3); 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/runCommand.ts: -------------------------------------------------------------------------------- 1 | import { invoke } from "@/lib/tauri-proxy"; 2 | 3 | export const runCommand = async (conversationId: string, cwd: string) => { 4 | const cmd = `codex resume ${conversationId}`; 5 | try { 6 | await invoke("open_terminal_with_command", { command: cmd, cwd: cwd }); 7 | } catch (err) { 8 | console.error("Failed to open terminal:", err); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /src/components/common/SimpleMarkdown.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from "react"; 2 | import ReactMarkdown from "react-markdown"; 3 | import remarkGfm from "remark-gfm"; 4 | 5 | interface SimpleMarkdownProps { 6 | content: string; 7 | } 8 | export const SimpleMarkdown = memo(({ content }) => { 9 | return {content}; 10 | }); 11 | -------------------------------------------------------------------------------- /src/components/config/config-tip.tsx: -------------------------------------------------------------------------------- 1 | import { open } from "@tauri-apps/plugin-shell"; 2 | import { Button } from "../ui/button"; 3 | 4 | export function ConfigTip() { 5 | return ( 6 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/formatSessionSource.ts: -------------------------------------------------------------------------------- 1 | import type { SessionSource } from "@/bindings/SessionSource"; 2 | 3 | export function formatSessionSource(source: SessionSource) { 4 | if (typeof source === "string") { 5 | return source; 6 | } 7 | 8 | const { subagent } = source; 9 | if (typeof subagent === "string") { 10 | return `subagent:${subagent}`; 11 | } 12 | 13 | return `subagent:${subagent.other}`; 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/errorUtils.ts: -------------------------------------------------------------------------------- 1 | export function getErrorMessage(error: unknown): string { 2 | if (typeof error === "string") { 3 | return error; 4 | } 5 | 6 | if (error instanceof Error) { 7 | return error.message; 8 | } 9 | 10 | try { 11 | return JSON.stringify(error); 12 | } catch (jsonError) { 13 | console.error("Failed to stringify error:", jsonError); 14 | return String(error); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/types/review.ts: -------------------------------------------------------------------------------- 1 | export interface Plan { 2 | step: string; 3 | status: string; 4 | } 5 | 6 | export type MessageRole = "user" | "assistant"; 7 | export type MessageVariant = "reasoning" | "response" | "plan"; 8 | 9 | export interface ReviewMessage { 10 | id: string; 11 | role?: MessageRole; 12 | type: string; 13 | content?: string; 14 | plan?: Plan[]; 15 | variant?: MessageVariant; 16 | title?: string; 17 | } 18 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tauri + React + Typescript 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/hooks/codex/index.ts: -------------------------------------------------------------------------------- 1 | export { useBuildNewConversationParams } from "./useBuildNewConversationParams"; 2 | export { useChatScroll } from "./useChatScroll"; 3 | export { useCodexApprovalRequests } from "./useCodexApprovalRequests"; 4 | export { useConversation } from "./useConversation"; 5 | export { useConversationEvents } from "./useConversationEvents"; 6 | export { useSendMessage } from "./useSendMessage"; 7 | export { useTokenCount } from "./useTokenCount"; 8 | -------------------------------------------------------------------------------- /src/locales/index.ts: -------------------------------------------------------------------------------- 1 | import { en } from "./en"; 2 | import { zh } from "./zh"; 3 | import { ja } from "./ja"; 4 | 5 | export const localeResources = { 6 | en: { translation: en }, 7 | zh: { translation: zh }, 8 | ja: { translation: ja }, 9 | } as const; 10 | 11 | export type AppLocale = keyof typeof localeResources; 12 | 13 | export const localeLabels: Record = { 14 | en: "English", 15 | zh: "中文", 16 | ja: "日本語", 17 | }; 18 | -------------------------------------------------------------------------------- /src-tauri/src/filesystem/file_types.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Serialize, Deserialize)] 4 | pub struct FileEntry { 5 | pub name: String, 6 | pub path: String, 7 | pub is_directory: bool, 8 | pub size: Option, 9 | pub extension: Option, 10 | } 11 | 12 | #[derive(Debug, Serialize, Deserialize)] 13 | pub struct GitDiff { 14 | pub original_content: String, 15 | pub current_content: String, 16 | pub has_changes: bool, 17 | } 18 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "", 8 | "css": "src/App.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } -------------------------------------------------------------------------------- /src/stores/codex/useLayoutStore.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | import { persist } from "zustand/middleware"; 3 | 4 | interface CodexLayoutState { 5 | showHistory: boolean; 6 | setShowHistory: (showHistory: boolean) => void; 7 | } 8 | 9 | export const useCodexLayoutStore = create()( 10 | persist( 11 | (set) => ({ 12 | showHistory: true, 13 | setShowHistory: (showHistory) => set({ showHistory }), 14 | }), 15 | { 16 | name: "codex-layout", 17 | }, 18 | ), 19 | ); 20 | -------------------------------------------------------------------------------- /src/types/mcp.ts: -------------------------------------------------------------------------------- 1 | type StdioMcpServerConfig = { 2 | type?: "stdio"; 3 | command: string; 4 | args: string[]; 5 | env?: Record; 6 | enabled?: boolean; 7 | }; 8 | 9 | type HttpMcpServerConfig = { 10 | type: "http"; 11 | url: string; 12 | enabled?: boolean; 13 | }; 14 | 15 | type SseMcpServerConfig = { 16 | type: "sse"; 17 | url: string; 18 | enabled?: boolean; 19 | }; 20 | 21 | export type McpServerConfig = 22 | | StdioMcpServerConfig 23 | | HttpMcpServerConfig 24 | | SseMcpServerConfig; 25 | -------------------------------------------------------------------------------- /src/components/events/OutputBlock.tsx: -------------------------------------------------------------------------------- 1 | export function OutputBlock({ label, value }: { label: string; value: string }) { 2 | if (!value || !value.trim()) { 3 | return null; 4 | } 5 | 6 | return ( 7 |
8 |
9 | {label} 10 |
11 |
12 |         {value}
13 |       
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/stores/settings/LocaleStore.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | import { persist } from "zustand/middleware"; 3 | import { AppLocale } from "@/locales"; 4 | 5 | interface LocaleState { 6 | locale: AppLocale; 7 | setLocale: (locale: AppLocale) => void; 8 | } 9 | 10 | export const useLocaleStore = create()( 11 | persist( 12 | (set) => ({ 13 | locale: "en", 14 | setLocale: (locale: AppLocale) => set({ locale }), 15 | }), 16 | { 17 | name: "locale-storage", 18 | }, 19 | ), 20 | ); 21 | -------------------------------------------------------------------------------- /codex-bindings/src/bin/codex_export_bindings.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::PathBuf; 3 | 4 | fn main() { 5 | let args: Vec = env::args().collect(); 6 | 7 | if args.len() > 1 && args[1] == "-v" { 8 | println!("{}", env!("CARGO_PKG_VERSION")); 9 | return; 10 | } 11 | 12 | let out = if args.len() > 1 { 13 | Some(PathBuf::from(&args[1])) 14 | } else { 15 | None 16 | }; 17 | 18 | codex_bindings::export_ts_types(out.as_ref()); 19 | println!("Exported TypeScript bindings"); 20 | } 21 | -------------------------------------------------------------------------------- /src/components/mcp/McpLinkerButton.tsx: -------------------------------------------------------------------------------- 1 | import { isRemoteRuntime } from "@/lib/tauri-proxy"; 2 | import { open as openUrl } from "@tauri-apps/plugin-shell" 3 | import { Button } from "../ui/button"; 4 | 5 | export function McpLinkerButton() { 6 | return ( 7 | 19 | ) 20 | } -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { Layout } from "@/components/layout"; 3 | import { useDeepLink } from "./hooks/useDeepLink"; 4 | import { initializeActiveConversationSubscription } from "@/stores/codex/useActiveConversationStore"; 5 | import "./App.css"; 6 | 7 | export default function App() { 8 | // Initialize deep linking - must be called at top level, not conditionally 9 | useDeepLink(); 10 | 11 | useEffect(() => { 12 | // Initialize store subscriptions 13 | initializeActiveConversationSubscription(); 14 | }, []); 15 | 16 | return ; 17 | } -------------------------------------------------------------------------------- /src/components/chat/EventMsgType.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import type { CodexEvent } from "@/types/chat"; 3 | 4 | interface EventMsgTypeProps { 5 | msgType: CodexEvent['payload']['params']['msg']['type']; 6 | } 7 | 8 | export const EventMsgType: React.FC = ({ msgType }) => { 9 | if ( 10 | import.meta.env.VITE_SHOW_EVENT_FOOTER === "true" && 11 | !["token_count", "exec_command_output_delta"].includes(msgType) && 12 | !msgType.startsWith("item_") 13 | ) { 14 | return

{msgType}

; 15 | } 16 | return null; 17 | }; -------------------------------------------------------------------------------- /src-tauri/src/codex_commands/state.rs: -------------------------------------------------------------------------------- 1 | //! Tauri state wrapper for codex-client 2 | //! 3 | //! Wraps ClientState in a Tauri-managed state container 4 | 5 | use codex_client::ClientState; 6 | use std::sync::Arc; 7 | 8 | /// Tauri-managed state wrapper 9 | pub struct CodexState { 10 | pub client_state: Arc, 11 | } 12 | 13 | impl CodexState { 14 | pub fn new() -> Self { 15 | Self { 16 | client_state: Arc::new(ClientState::new()), 17 | } 18 | } 19 | } 20 | 21 | impl Default for CodexState { 22 | fn default() -> Self { 23 | Self::new() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/components/ui/toaster.tsx: -------------------------------------------------------------------------------- 1 | import { Toast, ToastContainer } from "@/components/ui/toast"; 2 | import { useToastStore } from "@/components/ui/use-toast"; 3 | 4 | export function Toaster() { 5 | const { toasts, removeToast } = useToastStore(); 6 | 7 | return ( 8 | 9 | {toasts.map((toast) => ( 10 | removeToast(toast.id)} 16 | /> 17 | ))} 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /src/components/usage/index.tsx: -------------------------------------------------------------------------------- 1 | export { SummaryCard } from "@/components/usage/SummaryCard"; 2 | export { TokenBreakdownCard } from "@/components/usage/TokenBreakdownCard"; 3 | export { MostUsedModelsCard } from "@/components/usage/MostUsedModelsCard"; 4 | export { TopProjectsCard } from "@/components/usage/TopProjectsCard"; 5 | export { ModelUsageChart } from "@/components/usage/ModelUsageChart"; 6 | export { ProjectUsageChart } from "@/components/usage/ProjectUsageChart"; 7 | export { TimelineChart } from "@/components/usage/TimelineChart"; 8 | export { TokenDistributionChart } from "@/components/usage/TokenDistributionChart"; -------------------------------------------------------------------------------- /src/stores/codex/useResumeConversationStore.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | 3 | interface ResumeConversationState { 4 | resumingConversationId: string | null; 5 | setResumingConversationId: (conversationId: string | null) => void; 6 | clearResumingConversationId: () => void; 7 | } 8 | 9 | export const useResumeConversationStore = create((set) => ({ 10 | resumingConversationId: null, 11 | setResumingConversationId: (conversationId) => set({ resumingConversationId: conversationId }), 12 | clearResumingConversationId: () => set({ resumingConversationId: null }), 13 | })); 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | target 27 | 28 | src/bindings 29 | 30 | # Local agent notes 31 | # AGENTS.md 32 | # CLAUDE.md 33 | .claude/ 34 | .gemini/ 35 | 36 | .env 37 | .env.local 38 | .env.development 39 | .env.production 40 | 41 | docs/design.md 42 | docs/DELTA_MERGE_FIX.md 43 | -------------------------------------------------------------------------------- /src/types/remote.ts: -------------------------------------------------------------------------------- 1 | export type RemoteOriginOption = "localhost" | "direct" | "any"; 2 | 3 | export interface RemoteUiConfigInput { 4 | port: number; 5 | allowedOrigin: RemoteOriginOption; 6 | minimizeApp: boolean; 7 | applicationUi: boolean; 8 | enableInfoUrl: boolean; 9 | bundlePath?: string; 10 | externalHost?: string; 11 | } 12 | 13 | export interface RemoteUiStatus { 14 | running: boolean; 15 | port: number | null; 16 | bindAddress: string | null; 17 | publicUrl: string | null; 18 | infoUrl: string | null; 19 | bundlePath: string | null; 20 | minimizeApp: boolean; 21 | applicationUi: boolean; 22 | } 23 | -------------------------------------------------------------------------------- /codex-client/src/session_files/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cache; 2 | pub mod file; 3 | pub mod scanner; 4 | pub mod utils; 5 | 6 | // Re-export commonly used functions 7 | pub use cache::load_project_sessions; 8 | pub use scanner::scan_projects; 9 | 10 | // Re-export database functions 11 | pub use crate::db::{ 12 | // Session functions 13 | remove_project_session, update_project_favorites, update_session_preview, 14 | // Usage functions 15 | read_token_usage, 16 | // Note functions 17 | create_note, get_notes, get_note_by_id, update_note, delete_note, 18 | toggle_favorite, mark_notes_synced, get_unsynced_notes, Note, 19 | }; 20 | -------------------------------------------------------------------------------- /src/services/remoteAccessService.ts: -------------------------------------------------------------------------------- 1 | import { invoke } from "@/lib/tauri-proxy"; 2 | import type { RemoteUiConfigInput, RemoteUiStatus } from "@/types/remote"; 3 | 4 | export async function enableRemoteAccess( 5 | config: RemoteUiConfigInput, 6 | ): Promise { 7 | return invoke("enable_remote_ui", { config }); 8 | } 9 | 10 | export async function disableRemoteAccess(): Promise { 11 | return invoke("disable_remote_ui"); 12 | } 13 | 14 | export async function fetchRemoteAccessStatus(): Promise { 15 | return invoke("get_remote_ui_status"); 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/getDurationLable.ts: -------------------------------------------------------------------------------- 1 | import { CodexEvent } from "@/types/chat"; 2 | 3 | export const getStreamDurationLabel = (event: CodexEvent): string | null => { 4 | const durationMs = event.meta?.streamDurationMs; 5 | if (durationMs === undefined) { 6 | return null; 7 | } 8 | 9 | if (durationMs <= 0) { 10 | return "Stream duration: <0.01s"; 11 | } 12 | 13 | const seconds = durationMs / 1000; 14 | const formatted = 15 | seconds >= 10 16 | ? `${seconds.toFixed(1)}s` 17 | : seconds >= 1 18 | ? `${seconds.toFixed(2)}s` 19 | : `${durationMs.toFixed(0)}ms`; 20 | 21 | return formatted; 22 | }; 23 | -------------------------------------------------------------------------------- /src/hooks/use-mobile.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | const MOBILE_BREAKPOINT = 768 4 | 5 | export function useIsMobile() { 6 | const [isMobile, setIsMobile] = React.useState(undefined) 7 | 8 | React.useEffect(() => { 9 | const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) 10 | const onChange = () => { 11 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) 12 | } 13 | mql.addEventListener("change", onChange) 14 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) 15 | return () => mql.removeEventListener("change", onChange) 16 | }, []) 17 | 18 | return !!isMobile 19 | } 20 | -------------------------------------------------------------------------------- /src/utils/formater.ts: -------------------------------------------------------------------------------- 1 | export function formatCurrency(amount: number): string { 2 | return `$${amount.toFixed(3)}`; 3 | } 4 | 5 | export function formatNumber(num: number): string { 6 | if (num >= 1_000_000) { 7 | return `${(num / 1_000_000).toFixed(2)}M`; 8 | } else if (num >= 1_000) { 9 | return `${(num / 1_000).toFixed(1)}K`; 10 | } 11 | return num.toString(); 12 | } 13 | 14 | export function formatTokens(tokens: number): string { 15 | if (tokens >= 1_000_000) { 16 | return `${(tokens / 1_000_000).toFixed(2)}M`; 17 | } else if (tokens >= 1_000) { 18 | return `${(tokens / 1_000).toFixed(1)}K`; 19 | } 20 | return tokens.toString(); 21 | } 22 | -------------------------------------------------------------------------------- /scripts/release_codex_bindings.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | VERSION=$(grep -A 1 '\[workspace.package\]' Cargo.toml | grep version | sed 's/.*"\([^"]*\)".*/\1/') 5 | cargo build -p codex-bindings --bin codex_export_bindings --release 6 | 7 | BINARY_PATH="target/release/codex_export_bindings" 8 | CARGO_BIN_DIR="${CARGO_HOME:-$HOME/.cargo}/bin" 9 | 10 | if [ -f "$BINARY_PATH" ]; then 11 | mkdir -p "$CARGO_BIN_DIR" 12 | cp "$BINARY_PATH" "$CARGO_BIN_DIR/" 13 | chmod +x "$CARGO_BIN_DIR/codex_export_bindings" 14 | echo "Released codex_export_bindings v$VERSION to $CARGO_BIN_DIR" 15 | else 16 | echo "Error: Binary not found at $BINARY_PATH" 17 | exit 1 18 | fi 19 | -------------------------------------------------------------------------------- /src-tauri/src/commands/file.rs: -------------------------------------------------------------------------------- 1 | use log::{error, info, warn}; 2 | use std::path::PathBuf; 3 | 4 | #[tauri::command] 5 | pub async fn delete_file(path: String) -> Result<(), String> { 6 | let trimmed = path.trim(); 7 | if trimmed.is_empty() { 8 | warn!("delete_file invoked with empty path"); 9 | return Err("Path is empty.".to_string()); 10 | } 11 | 12 | info!("Deleting conversation file {}", trimmed); 13 | let path_buf = PathBuf::from(trimmed); 14 | tokio::fs::remove_file(path_buf) 15 | .await 16 | .map_err(|err| { 17 | error!("Failed to delete file {trimmed}: {err}"); 18 | format!("Failed to delete file: {err}") 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as LabelPrimitive from "@radix-ui/react-label" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | function Label({ 7 | className, 8 | ...props 9 | }: React.ComponentProps) { 10 | return ( 11 | 19 | ) 20 | } 21 | 22 | export { Label } 23 | -------------------------------------------------------------------------------- /src/locales/zh.ts: -------------------------------------------------------------------------------- 1 | export const zh = { 2 | header: { 3 | chat: "聊天", 4 | projects: "项目", 5 | task: "任务", 6 | review: "审查", 7 | openNewWindow: "打开新窗口", 8 | codexUnavailable: "不可用", 9 | usage: "使用情况", 10 | toggleChatPane: "切换聊天面板", 11 | toggleTheme: "切换主题", 12 | signOut: "退出登录", 13 | login: "登录", 14 | mcp: "MCP", 15 | statusBadgeLabel: "Codex 状态", 16 | codexVersion: "版本", 17 | language: "语言", 18 | changeLanguage: "切换语言", 19 | menu: "菜单", 20 | settings: "设置", 21 | remoteControl: "远程控制", 22 | donate: "捐助", 23 | accentColor: "主题颜色", 24 | }, 25 | donationPurpose: 26 | "您的捐赠将用于支持 Codexia 的持续开发、服务器费用、新功能研发以及长期维护。感谢您帮助这个项目成长!", 27 | }; 28 | -------------------------------------------------------------------------------- /codex-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "codex-client" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | 7 | [dependencies] 8 | # Removed tauri dependency - codex-client is now independent 9 | tokio.workspace = true 10 | serde.workspace = true 11 | serde_json.workspace = true 12 | log.workspace = true 13 | walkdir.workspace = true 14 | chrono.workspace = true 15 | dirs.workspace = true 16 | toml.workspace = true 17 | toml_edit.workspace = true 18 | base64.workspace = true 19 | regex.workspace = true 20 | rusqlite.workspace = true 21 | async-trait.workspace = true 22 | anyhow.workspace = true 23 | 24 | codex-app-server-protocol.workspace = true 25 | codex-protocol.workspace = true -------------------------------------------------------------------------------- /src/components/common/TokenCountInfo.tsx: -------------------------------------------------------------------------------- 1 | import type { TokenUsage } from "@/bindings/TokenUsage"; 2 | 3 | interface TokenCountInfoProps { 4 | usage: TokenUsage | null | undefined; 5 | className?: string; 6 | } 7 | 8 | export function TokenCountInfo({ usage, className }: TokenCountInfoProps) { 9 | if (!usage || typeof usage.total_tokens !== "number") { 10 | return null; 11 | } 12 | 13 | return ( 14 | 15 | 16 | {usage.total_tokens.toLocaleString()} 17 | tokens 18 | 19 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App"; 4 | import { ThemeProvider } from "@/contexts/ThemeContext"; 5 | import "./App.css"; 6 | import { I18nextProvider } from "react-i18next"; 7 | import { i18n } from "@/lib/i18n"; 8 | import { UpdateChecker } from "./components/settings/UpdateChecker"; 9 | import { Toaster } from "@/components/ui/toaster"; 10 | 11 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | , 21 | ); 22 | -------------------------------------------------------------------------------- /src/stores/settings/PromptOptimizerStore.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | import { persist } from "zustand/middleware"; 3 | 4 | interface PromptOptimizerState { 5 | provider: string; 6 | model: string; 7 | setProvider: (provider: string) => void; 8 | setModel: (model: string) => void; 9 | } 10 | 11 | const DEFAULT_PROVIDER = "openai"; 12 | const DEFAULT_MODEL = "gpt-5"; 13 | 14 | export const usePromptOptimizerStore = create()( 15 | persist( 16 | (set) => ({ 17 | provider: DEFAULT_PROVIDER, 18 | model: DEFAULT_MODEL, 19 | setProvider: (provider) => set({ provider }), 20 | setModel: (model) => set({ model }), 21 | }), 22 | { 23 | name: "prompt-optimizer", 24 | }, 25 | ), 26 | ); 27 | -------------------------------------------------------------------------------- /src-tauri/src/codex_commands/session_files/cache.rs: -------------------------------------------------------------------------------- 1 | use serde_json::Value; 2 | 3 | #[tauri::command] 4 | pub fn update_project_favorites( 5 | project_path: String, 6 | favorites: Vec, 7 | ) -> Result<(), String> { 8 | codex_client::db::update_project_favorites(project_path, favorites) 9 | } 10 | 11 | #[tauri::command] 12 | pub fn remove_project_session( 13 | project_path: String, 14 | conversation_id: String, 15 | ) -> Result<(), String> { 16 | codex_client::db::remove_project_session(project_path, conversation_id) 17 | } 18 | 19 | #[tauri::command] 20 | pub async fn load_project_sessions( 21 | project_path: String, 22 | ) -> Result { 23 | codex_client::session_files::load_project_sessions(project_path).await 24 | } 25 | -------------------------------------------------------------------------------- /src/locales/ja.ts: -------------------------------------------------------------------------------- 1 | export const ja = { 2 | header: { 3 | chat: "チャット", 4 | projects: "プロジェクト", 5 | task: "タスク", 6 | review: "レビュー", 7 | openNewWindow: "新しいウィンドウを開く", 8 | codexUnavailable: "利用不可", 9 | usage: "使用状況", 10 | toggleChatPane: "チャットペインを切り替え", 11 | toggleTheme: "テーマを切り替え", 12 | signOut: "サインアウト", 13 | login: "ログイン", 14 | mcp: "MCP", 15 | statusBadgeLabel: "Codex ステータス", 16 | codexVersion: "バージョン", 17 | language: "言語", 18 | changeLanguage: "言語を変更", 19 | menu: "メニュー", 20 | settings: "設定", 21 | remoteControl: "リモコン", 22 | donate: "寄贈する", 23 | accentColor: "アクセントカラー", 24 | }, 25 | donationPurpose: 26 | "ご寄付は Codexia の継続的な開発、サーバー費用、新機能の研究、長期的な保守に活用されます。このプロジェクトの成長を支えていただきありがとうございます!", 27 | }; 28 | -------------------------------------------------------------------------------- /docs/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "🛡️ Running pre-commit hook: Checking for relevant changes..." 4 | 5 | cd "$(git rev-parse --show-toplevel)" 6 | 7 | # check bun run build 8 | RELEVANT_FILES=$(git diff --cached --name-only | grep -E '\.(ts|tsx|js|json|rs|html|css|scss|toml|vue)$|^src/') 9 | 10 | if [ -z "$RELEVANT_FILES" ]; then 11 | echo "✅ No relevant frontend/tauri changes detected – skipping bun run build" 12 | exit 0 13 | fi 14 | 15 | echo "🔧 Relevant changes detected – executing: bun run build" 16 | bun run build 17 | 18 | if [ $? -eq 0 ]; then 19 | echo "✅ bun run build successful - proceeding with commit" 20 | exit 0 21 | else 22 | echo "❌ bun run build failed - commit aborted" 23 | echo "Please fix the build errors before committing" 24 | exit 1 25 | fi 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true, 22 | 23 | "baseUrl": ".", 24 | "paths": { 25 | "@/*": ["./src/*"] 26 | } 27 | }, 28 | "include": ["src"], 29 | "references": [{ "path": "./tsconfig.node.json" }] 30 | } 31 | -------------------------------------------------------------------------------- /codex-client/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod client; 2 | pub mod config; 3 | pub mod db; 4 | pub mod events; 5 | pub mod services; 6 | pub mod session_files; 7 | pub mod state; 8 | pub mod transport; 9 | pub mod utils; 10 | pub mod mcp; 11 | 12 | pub use client::CodexAppServerClient; 13 | pub use events::EventBus; 14 | pub use state::ClientState; 15 | 16 | // Re-export commonly used types 17 | pub use config::CodexConfig; 18 | pub use config::project::ProjectConfig; 19 | pub use config::provider::ModelProvider as ProviderConfig; 20 | pub use config::profile::Profile as ProfileConfig; 21 | pub use mcp::McpServerConfig; 22 | 23 | // Re-export database types for easy access 24 | pub use db::notes::Note; 25 | 26 | // Re-export protocol types for Tauri adapters 27 | pub use codex_app_server_protocol; 28 | pub use codex_protocol; 29 | -------------------------------------------------------------------------------- /src-tauri/src/codex_commands/config/provider.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use codex_client::ProviderConfig; 3 | 4 | 5 | #[tauri::command] 6 | pub async fn read_model_providers( 7 | ) -> Result, String> { 8 | codex_client::config::provider::read_model_providers().await 9 | } 10 | 11 | #[tauri::command] 12 | pub async fn add_or_update_model_provider( 13 | provider_name: String, 14 | provider: ProviderConfig, 15 | ) -> Result<(), String> { 16 | codex_client::config::provider::add_or_update_model_provider(provider_name, provider).await 17 | } 18 | 19 | #[tauri::command] 20 | pub async fn delete_model_provider( 21 | provider_name: String, 22 | ) -> Result<(), String> { 23 | codex_client::config::provider::delete_model_provider(provider_name).await 24 | } 25 | -------------------------------------------------------------------------------- /src-tauri/src/filesystem/file_parsers/pdf.rs: -------------------------------------------------------------------------------- 1 | use pdf_extract::extract_text; 2 | use std::path::Path; 3 | 4 | #[tauri::command] 5 | pub async fn read_pdf_content(file_path: String) -> Result { 6 | let expanded_path = if file_path.starts_with("~/") { 7 | let home = dirs::home_dir().ok_or_else(|| "Cannot find home directory".to_string())?; 8 | home.join(&file_path[2..]) 9 | } else { 10 | Path::new(&file_path).to_path_buf() 11 | }; 12 | 13 | if !expanded_path.exists() || expanded_path.is_dir() { 14 | return Err("File does not exist or is a directory".to_string()); 15 | } 16 | 17 | match extract_text(&expanded_path) { 18 | Ok(content) => Ok(content), 19 | Err(e) => Err(format!("Failed to extract PDF content: {}", e)), 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as SeparatorPrimitive from "@radix-ui/react-separator" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | function Separator({ 7 | className, 8 | orientation = "horizontal", 9 | decorative = true, 10 | ...props 11 | }: React.ComponentProps) { 12 | return ( 13 | 23 | ) 24 | } 25 | 26 | export { Separator } 27 | -------------------------------------------------------------------------------- /src-tauri/src/commands/window.rs: -------------------------------------------------------------------------------- 1 | use chrono::Utc; 2 | use tauri::{AppHandle, WebviewUrl, WebviewWindowBuilder}; 3 | 4 | #[tauri::command] 5 | pub async fn create_new_window(app: AppHandle) -> Result<(), String> { 6 | let window_label = format!("main-{}", Utc::now().timestamp_millis()); 7 | 8 | let window = WebviewWindowBuilder::new(&app, &window_label, WebviewUrl::default()) 9 | .title("Codexia") 10 | .inner_size(1200.0, 800.0) 11 | .min_inner_size(800.0, 600.0) 12 | .decorations(true) 13 | .resizable(true) 14 | .fullscreen(false) 15 | .build() 16 | .map_err(|e| format!("Failed to create new window: {}", e))?; 17 | 18 | window 19 | .set_focus() 20 | .map_err(|e| format!("Failed to focus window: {}", e))?; 21 | 22 | Ok(()) 23 | } 24 | -------------------------------------------------------------------------------- /src/components/events/AccordionMsg.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Accordion, 3 | AccordionContent, 4 | AccordionItem, 5 | AccordionTrigger, 6 | } from "@/components/ui/accordion"; 7 | import { MarkdownRenderer } from "../chat/MarkdownRenderer"; 8 | 9 | interface AccordionMsgProps { 10 | title: string; 11 | content: string; 12 | } 13 | 14 | export function AccordionMsg({ title, content }: AccordionMsgProps) { 15 | return ( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/stores/codex/useCodexStore.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | import { persist } from "zustand/middleware"; 3 | 4 | interface CodexState { 5 | cwd: string; 6 | webSearchEnabled: boolean; 7 | clientName: "codex" | "coder"; 8 | setCwd: (cwd: string) => void; 9 | toggleWebSearch: () => void; 10 | setClientName: (name: "codex" | "coder") => void; 11 | } 12 | 13 | export const useCodexStore = create()( 14 | persist( 15 | (set) => ({ 16 | cwd: "", 17 | webSearchEnabled: false, 18 | clientName: "codex", 19 | setCwd: (cwd) => set({ cwd }), 20 | toggleWebSearch: () => 21 | set((state) => ({ webSearchEnabled: !state.webSearchEnabled })), 22 | setClientName: (name) => set({ clientName: name }), 23 | }), 24 | { 25 | name: "codex", 26 | }, 27 | ), 28 | ); 29 | -------------------------------------------------------------------------------- /src/hooks/useFileTokens.ts: -------------------------------------------------------------------------------- 1 | import { useState, useCallback } from "react"; 2 | import { invoke } from "@/lib/tauri-proxy"; 3 | 4 | export function useFileTokens() { 5 | const [tokenCache, setTokenCache] = useState>(new Map()); 6 | 7 | const calculateTokens = useCallback(async (filePath: string): Promise => { 8 | if (tokenCache.has(filePath)) { 9 | return tokenCache.get(filePath) || null; 10 | } 11 | 12 | try { 13 | const tokens = await invoke("calculate_file_tokens", { 14 | filePath, 15 | }); 16 | if (tokens !== null) { 17 | setTokenCache((prev) => new Map(prev).set(filePath, tokens)); 18 | } 19 | return tokens; 20 | } catch { 21 | return null; 22 | } 23 | }, [tokenCache]); 24 | 25 | return { calculateTokens }; 26 | } -------------------------------------------------------------------------------- /src/stores/useAuthStore.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | import { persist } from 'zustand/middleware'; 3 | 4 | export type OAuthProvider = 'github' | 'google'; 5 | 6 | interface AuthState { 7 | lastOAuthProvider: OAuthProvider | null; 8 | setLastOAuthProvider: (provider: OAuthProvider) => void; 9 | clearLastOAuthProvider: () => void; 10 | } 11 | 12 | export const useAuthStore = create()( 13 | persist( 14 | (set) => ({ 15 | lastOAuthProvider: null, 16 | setLastOAuthProvider: (provider: OAuthProvider) => set({ lastOAuthProvider: provider }), 17 | clearLastOAuthProvider: () => set({ lastOAuthProvider: null }), 18 | }), 19 | { 20 | name: 'auth-store', 21 | partialize: (state) => ({ 22 | lastOAuthProvider: state.lastOAuthProvider, 23 | }), 24 | } 25 | ) 26 | ); 27 | 28 | -------------------------------------------------------------------------------- /src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | function Textarea({ className, ...props }: React.ComponentProps<"textarea">) { 6 | return ( 7 |