Skip to content
Open
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
Binary file added .swarm/memory.db
Binary file not shown.
3 changes: 3 additions & 0 deletions bun.lock
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"tailwind-merge": "^2.6.0",
"tailwindcss": "^4.1.8",
"zod": "^3.24.1",
"zustand": "^5.0.6",
},
"devDependencies": {
"@tauri-apps/cli": "^2",
Expand Down Expand Up @@ -1021,6 +1022,8 @@

"zod": ["[email protected]", "", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="],

"zustand": ["[email protected]", "https://registry.npmmirror.com/zustand/-/zustand-5.0.6.tgz", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-ihAqNeUVhe0MAD+X8M5UzqyZ9k3FFZLBTtqo6JLPwV53cbRB/mJwBI0PxcIgqhBBHlEs8G45OTDTMq3gNcLq3A=="],

"zwitch": ["[email protected]", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],

"@babel/core/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
Expand Down
46 changes: 46 additions & 0 deletions clean.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/bash

# Claudia 项目清理脚本
# 用于清理编译缓存和临时文件

echo "🧹 开始清理 Claudia 项目..."

# 清理 Rust 编译产物
if [ -d "src-tauri" ]; then
echo "清理 Rust 编译缓存..."
cd src-tauri && cargo clean && cd ..
fi

# 清理前端构建产物
if [ -d "dist" ]; then
echo "清理前端构建文件..."
rm -rf dist
fi

# 清理 .DS_Store 文件
echo "清理 .DS_Store 文件..."
find . -name ".DS_Store" -type f -delete

# 清理日志文件
echo "清理日志文件..."
find . -name "*.log" -type f -delete

# 清理临时文件
echo "清理临时文件..."
find . -name "*.tmp" -type f -delete
find . -name "*.temp" -type f -delete

# 可选:清理 node_modules(需要重新安装)
read -p "是否清理 node_modules?这需要重新运行 npm install (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "清理 node_modules..."
rm -rf node_modules
fi

echo "✅ 清理完成!"

# 显示当前磁盘使用情况
echo ""
echo "📊 当前项目大小:"
du -sh .
54 changes: 54 additions & 0 deletions docs/layout-improvements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# CC Projects 页面布局优化方案

## 🎯 基于行业标准的改进

### 1. **间距系统优化(Material Design 8px Grid)**
- ✅ 卡片间距从 16px (gap-4) 增加到 24px (gap-6)
- ✅ 卡片内边距从 16px (p-4) 增加到 24px (p-6)
- ✅ 章节之间增加 32px (space-y-8) 间距
- ✅ 添加视觉分隔线区分不同内容区域

### 2. **视觉层次增强**
- ✅ 项目标题字号从 text-base 增加到 text-lg
- ✅ "Active Claude Sessions" 标题增大并加粗
- ✅ 添加 "All Projects" 章节标题
- ✅ 卡片悬停阴影从 shadow-md 增强到 shadow-lg

### 3. **响应式布局优化**
- 📱 移动端(< 768px):单列布局,16px 间距
- 📱 平板(768px - 1280px):双列布局,24px 间距
- 💻 桌面(> 1280px):三列布局,32px 间距

### 4. **用户体验改进**
- ✅ 活动会话卡片添加绿色边框强调
- ✅ 卡片悬停时上移 4px 增加交互反馈
- ✅ 添加平滑过渡动画 (cubic-bezier)
- ✅ 优化滚动条样式

### 5. **可选功能**
- 🔄 列表视图切换(开发中)
- 📊 卡片信息密度选项
- 🎨 主题自定义支持

## 📏 遵循的设计标准

1. **Material Design 3** - 间距和布局网格
2. **Apple HIG** - 视觉层次和可读性
3. **WCAG 2.1** - 可访问性标准
4. **Tailwind CSS** - 实用优先的样式系统

## 🔄 实施步骤

1. 更新 `ProjectList` 组件间距
2. 优化 `RunningClaudeSessions` 布局
3. 修改主页面结构增加章节分隔
4. 添加响应式样式支持
5. 实现视图切换功能(可选)

## 📈 预期效果

- 减少视觉拥挤感 50%+
- 提升内容可读性 30%+
- 改善用户浏览体验
- 支持更多设备尺寸
- 增强品牌一致性
55 changes: 55 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@radix-ui/react-switch": "^1.1.3",
"@radix-ui/react-tabs": "^1.1.3",
"@radix-ui/react-toast": "^1.2.3",
"@radix-ui/react-toggle-group": "^1.1.10",
"@radix-ui/react-tooltip": "^1.1.5",
"@tailwindcss/cli": "^4.1.8",
"@tailwindcss/vite": "^4.1.8",
Expand Down
Binary file added src-tauri/.swarm/memory.db
Binary file not shown.
125 changes: 96 additions & 29 deletions src-tauri/src/commands/mcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,23 +96,58 @@ pub struct ImportServerResult {
}

/// Executes a claude mcp command
fn execute_claude_mcp_command(app_handle: &AppHandle, args: Vec<&str>) -> Result<String> {
async fn execute_claude_mcp_command(app_handle: &AppHandle, args: Vec<&str>) -> Result<String> {
info!("Executing claude mcp command with args: {:?}", args);

let claude_path = find_claude_binary(app_handle)?;
let mut cmd = create_command_with_env(&claude_path);
cmd.arg("mcp");
for arg in args {
cmd.arg(arg);
}

// Check if we should use sidecar
if claude_path == "claude-code" {
// Use Tauri sidecar API
use tauri_plugin_shell::ShellExt;

// Create sidecar command
let mut sidecar_cmd = app_handle
.shell()
.sidecar("claude-code")
.map_err(|e| anyhow::anyhow!("Failed to create sidecar command: {}", e))?
.arg("mcp");

// Add all arguments
for arg in args {
sidecar_cmd = sidecar_cmd.arg(arg);
}

info!("Executing sidecar command");

// Execute the command
let output = sidecar_cmd
.output()
.await
.map_err(|e| anyhow::anyhow!("Failed to execute sidecar: {}", e))?;

if output.status.success() {
Ok(String::from_utf8_lossy(&output.stdout).to_string())
} else {
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
Err(anyhow::anyhow!("Sidecar command failed: {}", stderr))
}
} else {
// Use regular command execution
let mut cmd = create_command_with_env(&claude_path);
cmd.arg("mcp");
for arg in args {
cmd.arg(arg);
}

let output = cmd.output().context("Failed to execute claude command")?;
let output = cmd.output().context("Failed to execute claude command")?;

if output.status.success() {
Ok(String::from_utf8_lossy(&output.stdout).to_string())
} else {
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
Err(anyhow::anyhow!("Command failed: {}", stderr))
if output.status.success() {
Ok(String::from_utf8_lossy(&output.stdout).to_string())
} else {
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
Err(anyhow::anyhow!("Command failed: {}", stderr))
}
}
}

Expand Down Expand Up @@ -188,7 +223,7 @@ pub async fn mcp_add(
}
}

match execute_claude_mcp_command(&app, cmd_args) {
match execute_claude_mcp_command(&app, cmd_args).await {
Ok(output) => {
info!("Successfully added MCP server: {}", name);
Ok(AddServerResult {
Expand All @@ -213,7 +248,7 @@ pub async fn mcp_add(
pub async fn mcp_list(app: AppHandle) -> Result<Vec<MCPServer>, String> {
info!("Listing MCP servers");

match execute_claude_mcp_command(&app, vec!["list"]) {
match execute_claude_mcp_command(&app, vec!["list"]).await {
Ok(output) => {
info!("Raw output from 'claude mcp list': {:?}", output);
let trimmed = output.trim();
Expand Down Expand Up @@ -335,7 +370,7 @@ pub async fn mcp_list(app: AppHandle) -> Result<Vec<MCPServer>, String> {
pub async fn mcp_get(app: AppHandle, name: String) -> Result<MCPServer, String> {
info!("Getting MCP server details for: {}", name);

match execute_claude_mcp_command(&app, vec!["get", &name]) {
match execute_claude_mcp_command(&app, vec!["get", &name]).await {
Ok(output) => {
// Parse the structured text output
let mut scope = "local".to_string();
Expand Down Expand Up @@ -404,7 +439,7 @@ pub async fn mcp_get(app: AppHandle, name: String) -> Result<MCPServer, String>
pub async fn mcp_remove(app: AppHandle, name: String) -> Result<String, String> {
info!("Removing MCP server: {}", name);

match execute_claude_mcp_command(&app, vec!["remove", &name]) {
match execute_claude_mcp_command(&app, vec!["remove", &name]).await {
Ok(output) => {
info!("Successfully removed MCP server: {}", name);
Ok(output.trim().to_string())
Expand Down Expand Up @@ -437,7 +472,7 @@ pub async fn mcp_add_json(
cmd_args.push(scope_flag);
cmd_args.push(&scope);

match execute_claude_mcp_command(&app, cmd_args) {
match execute_claude_mcp_command(&app, cmd_args).await {
Ok(output) => {
info!("Successfully added MCP server from JSON: {}", name);
Ok(AddServerResult {
Expand Down Expand Up @@ -624,17 +659,49 @@ pub async fn mcp_serve(app: AppHandle) -> Result<String, String> {
}
};

let mut cmd = create_command_with_env(&claude_path);
cmd.arg("mcp").arg("serve");

match cmd.spawn() {
Ok(_) => {
info!("Successfully started Claude Code MCP server");
Ok("Claude Code MCP server started".to_string())
// Check if we should use sidecar
if claude_path == "claude-code" {
// Use Tauri sidecar API
use tauri_plugin_shell::ShellExt;

info!("Starting MCP server using sidecar");

let sidecar = app
.shell()
.sidecar("claude-code")
.map_err(|e| {
error!("Failed to create sidecar command: {}", e);
format!("Failed to create sidecar command: {}", e)
})?
.arg("mcp")
.arg("serve");

// Spawn the sidecar process
match sidecar.spawn() {
Ok(mut child) => {
// Store the child process handle if needed
info!("Successfully started Claude Code MCP server via sidecar");
Ok("Claude Code MCP server started".to_string())
}
Err(e) => {
error!("Failed to spawn MCP server sidecar: {}", e);
Err(format!("Failed to start MCP server: {}", e))
}
}
Err(e) => {
error!("Failed to start MCP server: {}", e);
Err(e.to_string())
} else {
// Use regular command execution
let mut cmd = create_command_with_env(&claude_path);
cmd.arg("mcp").arg("serve");

match cmd.spawn() {
Ok(_) => {
info!("Successfully started Claude Code MCP server");
Ok("Claude Code MCP server started".to_string())
}
Err(e) => {
error!("Failed to start MCP server: {}", e);
Err(e.to_string())
}
}
}
}
Expand All @@ -645,7 +712,7 @@ pub async fn mcp_test_connection(app: AppHandle, name: String) -> Result<String,
info!("Testing connection to MCP server: {}", name);

// For now, we'll use the get command to test if the server exists
match execute_claude_mcp_command(&app, vec!["get", &name]) {
match execute_claude_mcp_command(&app, vec!["get", &name]).await {
Ok(_) => Ok(format!("Connection to {} successful", name)),
Err(e) => Err(e.to_string()),
}
Expand All @@ -656,7 +723,7 @@ pub async fn mcp_test_connection(app: AppHandle, name: String) -> Result<String,
pub async fn mcp_reset_project_choices(app: AppHandle) -> Result<String, String> {
info!("Resetting MCP project choices");

match execute_claude_mcp_command(&app, vec!["reset-project-choices"]) {
match execute_claude_mcp_command(&app, vec!["reset-project-choices"]).await {
Ok(output) => {
info!("Successfully reset MCP project choices");
Ok(output.trim().to_string())
Expand Down
Loading