Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ This is an educational reverse-engineering project that recreates @anthropic-ai/
- 用户的能力并不如你,当他提出的方案不正确时,必须直接指出问题

## 自我感知能力
- 你可以用 Browser 工具访问 `http://localhost:3456` 来查看自己的 Web UI(导航守卫已对自身端口开白名单)
- 你可以用 Browser 工具访问 `https://localhost:3456` 来查看自己的 Web UI(导航守卫已对自身端口开白名单)
- 当用户反馈 UI 问题时,应该主动用 Browser 截图确认,而不是盲猜
- 流程:`Browser start` → `Browser goto http://localhost:3456` → `Browser screenshot` → 看到自己的界面
- 流程:`Browser start` → `Browser goto https://localhost:3456` → `Browser screenshot` → 看到自己的界面
## Development Commands

```bash
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,30 @@ node dist/web-cli.js # Web IDE
```
</details>

<details>
<summary>Uninstall</summary>

**macOS / Linux:**
```bash
curl -fsSL https://raw.githubusercontent.com/kill136/claude-code-open/private_web_ui/uninstall.sh | bash
```

**China mirror:**
```bash
curl -fsSL https://gitee.com/lubanbbs/claude-code-open/raw/private_web_ui/uninstall.sh | bash
```

**Windows (PowerShell):**
```powershell
irm https://raw.githubusercontent.com/kill136/claude-code-open/private_web_ui/uninstall.ps1 | iex
```

**Windows (cmd):**
```cmd
curl -fsSL https://raw.githubusercontent.com/kill136/claude-code-open/private_web_ui/uninstall.bat -o uninstall.bat && uninstall.bat
```
</details>

## Key Features

### Web IDE
Expand Down
24 changes: 24 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,30 @@ node dist/web-cli.js # Web IDE
```
</details>

<details>
<summary>卸载</summary>

**macOS / Linux:**
```bash
curl -fsSL https://raw.githubusercontent.com/kill136/claude-code-open/private_web_ui/uninstall.sh | bash
```

**国内镜像:**
```bash
curl -fsSL https://gitee.com/lubanbbs/claude-code-open/raw/private_web_ui/uninstall.sh | bash
```

**Windows(PowerShell):**
```powershell
irm https://raw.githubusercontent.com/kill136/claude-code-open/private_web_ui/uninstall.ps1 | iex
```

**Windows(cmd):**
```cmd
curl -fsSL https://raw.githubusercontent.com/kill136/claude-code-open/private_web_ui/uninstall.bat -o uninstall.bat && uninstall.bat
```
</details>

## 核心功能

### Web IDE
Expand Down
8 changes: 8 additions & 0 deletions src/core/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,14 @@ export class ClaudeClient {
'anthropic-dangerous-direct-browser-access': 'true',
};

// 第三方兼容 API(如 MiniMax)要求 Authorization: Bearer 格式,而非 x-api-key
const isThirdPartyUrl = !!(config.baseUrl &&
!config.baseUrl.includes('api.anthropic.com') &&
!config.baseUrl.includes('anthropic.com'));
if (isThirdPartyUrl && apiKey) {
defaultHeaders['Authorization'] = `Bearer ${apiKey}`;
}

// 如果使用 OAuth,标记模式
if (authToken) {
this.isOAuth = true;
Expand Down
50 changes: 50 additions & 0 deletions src/web/client/src/components/GitPanel/GitPanel.css
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,56 @@
.diff-td-ln.diff-td--removed { background: rgba(239, 68, 68, 0.08); color: rgba(239, 68, 68, 0.6); }
.diff-td-ln.diff-td--empty { background: var(--bg-secondary); }

/* ---- 语法高亮 ---- */
.hl-comment { color: #6b7280; font-style: italic; }
.hl-string { color: #10b981; }
.hl-keyword { color: #c084fc; font-weight: 600; }
.hl-number { color: #f59e0b; }
.hl-type { color: #38bdf8; }
.hl-function { color: #818cf8; }
.hl-prefix { color: var(--text-muted); opacity: 0.5; }

/* ---- Word-level diff ---- */
.diff-word-added {
background: rgba(16, 185, 129, 0.3);
border-radius: 2px;
padding: 0 1px;
}

.diff-word-removed {
background: rgba(239, 68, 68, 0.3);
border-radius: 2px;
padding: 0 1px;
}

.diff-prefix {
color: var(--text-muted);
opacity: 0.5;
margin-right: 4px;
}

/* ---- Diff 统计 ---- */
.git-diff-stats {
display: flex;
gap: 8px;
font-size: 12px;
font-weight: 600;
}

.git-diff-stat-added {
color: #10b981;
background: rgba(16, 185, 129, 0.1);
padding: 2px 8px;
border-radius: 4px;
}

.git-diff-stat-removed {
color: #ef4444;
background: rgba(239, 68, 68, 0.1);
padding: 2px 8px;
border-radius: 4px;
}

/* ============================================
输入对话框(用于 Stash Save、Commit 等)
============================================ */
Expand Down
27 changes: 16 additions & 11 deletions src/web/client/src/components/GitPanel/StatusView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,12 @@ export function StatusView({ gitStatus, send, projectPath }: StatusViewProps) {
source: 'staged' | 'unstaged' | 'untracked' | 'conflict';
} | null>(null);

// 如果没有 git status 数据,显示加载或空状态
if (!gitStatus) {
return (
<div className="git-status-view">
<div className="git-status-empty">{t('git.loading')}</div>
</div>
);
}

const { staged, unstaged, untracked, conflicts, currentBranch, remoteStatus } = gitStatus;
const staged = gitStatus?.staged ?? [];
const unstaged = gitStatus?.unstaged ?? [];
const untracked = gitStatus?.untracked ?? [];
const conflicts = gitStatus?.conflicts ?? [];
const currentBranch = gitStatus?.currentBranch;
const remoteStatus = gitStatus?.remoteStatus;

// 获取文件状态标记
const getFileStatusBadge = (file: string, type: 'staged' | 'unstaged' | 'untracked' | 'conflict') => {
Expand All @@ -85,7 +81,7 @@ export function StatusView({ gitStatus, send, projectPath }: StatusViewProps) {
return file;
};

// 构建所有文件条目
// 构建所有文件条目(必须在条件 return 之前,保持 hooks 数量一致)
const allEntries: FileEntry[] = useMemo(() => {
const entries: FileEntry[] = [];
for (const f of conflicts) {
Expand Down Expand Up @@ -172,6 +168,15 @@ export function StatusView({ gitStatus, send, projectPath }: StatusViewProps) {
// 获取平铺的文件列表(用于 Shift 多选)
const flatFileList = useMemo(() => allEntries.map(e => e.cleanFile), [allEntries]);

// 如果没有 git status 数据,显示加载或空状态(必须在所有 hooks 之后)
if (!gitStatus) {
return (
<div className="git-status-view">
<div className="git-status-empty">{t('git.loading')}</div>
</div>
);
}

// ---- 操作处理函数 ----

const handleStage = (file: string) => {
Expand Down
51 changes: 51 additions & 0 deletions uninstall.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
@echo off
rem ============================================
rem Axon - Windows One-Click Uninstaller
rem
rem Usage:
rem Double-click this file, or run in cmd:
rem uninstall.bat
rem
rem Or one-liner from cmd (GitHub):
rem curl -fsSL https://raw.githubusercontent.com/kill136/claude-code-open/private_web_ui/uninstall.bat -o uninstall.bat && uninstall.bat
rem Or one-liner from cmd (Gitee, for China):
rem curl -fsSL https://gitee.com/lubanbbs/claude-code-open/raw/private_web_ui/uninstall.bat -o uninstall.bat && uninstall.bat
rem ============================================

chcp 65001 >nul 2>&1

echo.
echo +=============================================+
echo ^| Axon Uninstaller ^|
echo +=============================================+
echo.

rem --- Check if uninstall.ps1 exists alongside this bat file ---
set "SCRIPT_DIR=%~dp0"
if exist "%SCRIPT_DIR%uninstall.ps1" (
echo [INFO] Found uninstall.ps1 in %SCRIPT_DIR%
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT_DIR%uninstall.ps1"
goto :end
)

rem --- uninstall.ps1 not found locally, download to temp and execute ---
echo [INFO] uninstall.ps1 not found locally, downloading from remote...
set "PS1_TEMP=%TEMP%\claude-code-uninstall.ps1"

powershell.exe -NoProfile -ExecutionPolicy Bypass -Command ^
"$ErrorActionPreference = 'Stop'; " ^
"try { Invoke-WebRequest -Uri 'https://github.com' -UseBasicParsing -TimeoutSec 5 -ErrorAction Stop | Out-Null; $url = 'https://raw.githubusercontent.com/kill136/claude-code-open/private_web_ui/uninstall.ps1' } catch { $url = 'https://gitee.com/lubanbbs/claude-code-open/raw/private_web_ui/uninstall.ps1' }; " ^
"Write-Host \"[INFO] Downloading from $url\"; " ^
"Invoke-WebRequest -Uri $url -OutFile '%PS1_TEMP%' -UseBasicParsing"

if not exist "%PS1_TEMP%" (
echo [ERROR] Failed to download uninstall script.
goto :end
)

powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%PS1_TEMP%"

del "%PS1_TEMP%" >nul 2>&1

:end
if "%1"=="" pause
109 changes: 109 additions & 0 deletions uninstall.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# ============================================
# Axon - Windows One-Click Uninstaller
#
# Method 1 - PowerShell (irm pipe):
# irm https://raw.githubusercontent.com/kill136/claude-code-open/private_web_ui/uninstall.ps1 | iex
#
# Method 2 - Batch file:
# uninstall.bat
#
# China mirrors (Gitee):
# irm https://gitee.com/lubanbbs/claude-code-open/raw/private_web_ui/uninstall.ps1 | iex
# ============================================

$ErrorActionPreference = "Continue"

$DockerImage = "wbj66/axon:latest"
$InstallDir = if ($env:AXON_CONFIG_DIR) { $env:AXON_CONFIG_DIR } else { "$env:USERPROFILE\.axon" }

function Write-Info { param($msg) Write-Host "[INFO] " -ForegroundColor Blue -NoNewline; Write-Host $msg }
function Write-Ok { param($msg) Write-Host "[OK] " -ForegroundColor Green -NoNewline; Write-Host $msg }
function Write-Warn { param($msg) Write-Host "[WARN] " -ForegroundColor Yellow -NoNewline; Write-Host $msg }

Write-Host ""
Write-Host " +=============================================+" -ForegroundColor Cyan
Write-Host " | Axon Uninstaller |" -ForegroundColor Cyan
Write-Host " +=============================================+" -ForegroundColor Cyan
Write-Host ""

Write-Info "Uninstalling Axon from: $InstallDir"
Write-Host ""

# --- Remove source directory (runs npm unlink first) ---
if (Test-Path $InstallDir) {
$packageJson = Join-Path $InstallDir "package.json"
if (Test-Path $packageJson) {
Write-Info "Running npm unlink..."
Push-Location $InstallDir
try { npm.cmd unlink 2>$null } catch {}
Pop-Location
}
Remove-Item -Recurse -Force $InstallDir
Write-Ok "Removed installation directory: $InstallDir"
} else {
Write-Warn "Installation directory not found: $InstallDir (already removed?)"
}

# --- Remove CLI wrapper scripts ---
$binDir = "$env:USERPROFILE\.local\bin"
foreach ($file in @("claude.bat", "claude-web-start.bat", "claude-web.bat")) {
$path = Join-Path $binDir $file
if (Test-Path $path) {
Remove-Item -Force $path
Write-Ok "Removed $path"
}
}

# --- Remove desktop shortcut ---
$desktopPath = [Environment]::GetFolderPath("Desktop")
$shortcutPath = Join-Path $desktopPath "Axon WebUI.lnk"
if (Test-Path $shortcutPath) {
Remove-Item -Force $shortcutPath
Write-Ok "Removed desktop shortcut: $shortcutPath"
}

# --- Remove PATH entry from user environment ---
$userPath = [Environment]::GetEnvironmentVariable("Path", "User")
if ($userPath -like "*$binDir*") {
$newPath = ($userPath -split ';' | Where-Object { $_ -ne $binDir -and $_ -ne "" }) -join ';'
[Environment]::SetEnvironmentVariable("Path", $newPath, "User")
Write-Ok "Removed $binDir from user PATH"
}

# Also check for npm global dir that may have been added
$npmGlobalDir = try { (npm.cmd config get prefix 2>$null).Trim() } catch { "" }
if ($npmGlobalDir -and $userPath -like "*$npmGlobalDir*") {
$newPath = ($userPath -split ';' | Where-Object { $_ -ne $npmGlobalDir -and $_ -ne "" }) -join ';'
[Environment]::SetEnvironmentVariable("Path", $newPath, "User")
Write-Ok "Removed npm global dir from user PATH: $npmGlobalDir"
}

# --- Remove AXON_CONFIG_DIR from user environment ---
$axonConfigDir = [Environment]::GetEnvironmentVariable("AXON_CONFIG_DIR", "User")
if ($axonConfigDir) {
[Environment]::SetEnvironmentVariable("AXON_CONFIG_DIR", $null, "User")
Write-Ok "Removed AXON_CONFIG_DIR from user environment variables"
}

# --- Remove Docker image (optional) ---
try {
$dockerAvailable = $null -ne (Get-Command docker -ErrorAction SilentlyContinue)
if ($dockerAvailable) {
$imageExists = docker image inspect $DockerImage 2>$null
if ($LASTEXITCODE -eq 0) {
Write-Info "Removing Docker image: $DockerImage"
docker rmi $DockerImage 2>$null
if ($LASTEXITCODE -eq 0) {
Write-Ok "Removed Docker image: $DockerImage"
} else {
Write-Warn "Could not remove Docker image (may be in use)"
}
}
}
} catch {}

Write-Host ""
Write-Ok "Axon has been fully uninstalled!"
Write-Host ""
Write-Host " Note: Open a new terminal for PATH changes to take effect." -ForegroundColor Yellow
Write-Host ""
Loading
Loading