增加提示词管理
This commit is contained in:
@@ -7,7 +7,6 @@ pub mod language;
|
|||||||
pub mod mcp;
|
pub mod mcp;
|
||||||
pub mod packycode_nodes;
|
pub mod packycode_nodes;
|
||||||
pub mod prompt_files;
|
pub mod prompt_files;
|
||||||
pub mod prompt_files_v2;
|
|
||||||
pub mod proxy;
|
pub mod proxy;
|
||||||
pub mod relay_adapters;
|
pub mod relay_adapters;
|
||||||
pub mod relay_stations;
|
pub mod relay_stations;
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ fn get_claude_config_dir() -> Result<PathBuf, String> {
|
|||||||
Ok(claude_dir)
|
Ok(claude_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 应用提示词文件(替换本地 CLAUDE.md)
|
/// 应用提示词文件(替换本地 CLAUDE.md 或指定目标路径)
|
||||||
#[command]
|
#[command]
|
||||||
pub async fn prompt_file_apply(
|
pub async fn prompt_file_apply(
|
||||||
id: String,
|
id: String,
|
||||||
@@ -309,14 +309,40 @@ pub async fn prompt_file_apply(
|
|||||||
// 1. 从数据库读取提示词文件
|
// 1. 从数据库读取提示词文件
|
||||||
let file = prompt_file_get(id.clone(), db.clone()).await?;
|
let file = prompt_file_get(id.clone(), db.clone()).await?;
|
||||||
|
|
||||||
// 2. 确定目标路径
|
// 2. 确定目标路径(兼容传入目录或文件)
|
||||||
|
// - 若传入目录:拼接 CLAUDE.md
|
||||||
|
// - 若传入文件:直接写入该文件
|
||||||
|
// - 若未传入:默认 ~/.claude/CLAUDE.md
|
||||||
let claude_md_path = if let Some(path) = target_path {
|
let claude_md_path = if let Some(path) = target_path {
|
||||||
PathBuf::from(path).join("CLAUDE.md")
|
let p = PathBuf::from(path);
|
||||||
|
let is_dir = p.is_dir();
|
||||||
|
// 对于不存在路径,依据文件名扩展名进行语义判断
|
||||||
|
let looks_like_file = p
|
||||||
|
.file_name()
|
||||||
|
.and_then(|n| n.to_str())
|
||||||
|
.map(|n| n.eq_ignore_ascii_case("claude.md") || n.to_lowercase().ends_with(".md"))
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if is_dir || (!looks_like_file && !p.exists()) {
|
||||||
|
// 目录(或看起来像目录的不存在路径)
|
||||||
|
p.join("CLAUDE.md")
|
||||||
|
} else {
|
||||||
|
// 明确的文件路径
|
||||||
|
p
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 默认使用 ~/.claude/CLAUDE.md(和 settings.json 同目录)
|
// 默认使用 ~/.claude/CLAUDE.md(和 settings.json 同目录)
|
||||||
get_claude_config_dir()?.join("CLAUDE.md")
|
get_claude_config_dir()?.join("CLAUDE.md")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 确保父目录存在
|
||||||
|
if let Some(parent) = claude_md_path.parent() {
|
||||||
|
if !parent.exists() {
|
||||||
|
fs::create_dir_all(parent)
|
||||||
|
.map_err(|e| format!("创建目标目录失败: {}", e))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 3. 备份现有文件(如果存在)- 使用时间戳避免触发文件监视
|
// 3. 备份现有文件(如果存在)- 使用时间戳避免触发文件监视
|
||||||
if claude_md_path.exists() {
|
if claude_md_path.exists() {
|
||||||
let timestamp = Utc::now().format("%Y%m%d_%H%M%S");
|
let timestamp = Utc::now().format("%Y%m%d_%H%M%S");
|
||||||
@@ -512,4 +538,3 @@ pub async fn prompt_files_import_batch(
|
|||||||
|
|
||||||
Ok(imported)
|
Ok(imported)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,266 +0,0 @@
|
|||||||
use anyhow::Result;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::fs;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use tauri::command;
|
|
||||||
use log::{info, warn};
|
|
||||||
|
|
||||||
/// 提示词文件信息(从文件系统读取)
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct PromptFileInfo {
|
|
||||||
pub project_id: String, // 项目 ID(来自 projects 目录名)
|
|
||||||
pub project_path: String, // 实际项目路径
|
|
||||||
pub has_claude_md: bool, // 是否存在 .claude/CLAUDE.md
|
|
||||||
pub content: Option<String>, // CLAUDE.md 文件内容
|
|
||||||
pub file_size: Option<u64>, // 文件大小(字节)
|
|
||||||
pub modified_at: Option<i64>, // 最后修改时间
|
|
||||||
pub claude_md_path: String, // .claude/CLAUDE.md 完整路径
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取 Claude 目录
|
|
||||||
fn get_claude_dir() -> Result<PathBuf, String> {
|
|
||||||
dirs::home_dir()
|
|
||||||
.map(|p| p.join(".claude"))
|
|
||||||
.ok_or_else(|| "无法获取 home 目录".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 从项目名解码实际路径
|
|
||||||
fn decode_project_path(encoded_name: &str) -> String {
|
|
||||||
// 简单的路径解码 - 将编码的斜杠 (%2F 或 -) 转回斜杠
|
|
||||||
encoded_name.replace("%2F", "/").replace("-", "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 从会话文件中获取项目路径
|
|
||||||
fn get_project_path_from_sessions(project_dir: &PathBuf) -> Result<String, String> {
|
|
||||||
let entries = fs::read_dir(project_dir)
|
|
||||||
.map_err(|e| format!("无法读取项目目录: {}", e))?;
|
|
||||||
|
|
||||||
for entry in entries.flatten() {
|
|
||||||
let path = entry.path();
|
|
||||||
if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("jsonl") {
|
|
||||||
// 读取 JSONL 文件的第一行
|
|
||||||
if let Ok(content) = fs::read_to_string(&path) {
|
|
||||||
if let Some(first_line) = content.lines().next() {
|
|
||||||
if let Ok(json) = serde_json::from_str::<serde_json::Value>(first_line) {
|
|
||||||
if let Some(cwd) = json.get("cwd").and_then(|v| v.as_str()) {
|
|
||||||
return Ok(cwd.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err("未找到项目路径".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 扫描所有项目的提示词文件
|
|
||||||
#[command]
|
|
||||||
pub async fn scan_prompt_files() -> Result<Vec<PromptFileInfo>, String> {
|
|
||||||
info!("扫描项目提示词文件");
|
|
||||||
|
|
||||||
let claude_dir = get_claude_dir()?;
|
|
||||||
let projects_dir = claude_dir.join("projects");
|
|
||||||
|
|
||||||
if !projects_dir.exists() {
|
|
||||||
warn!("项目目录不存在: {:?}", projects_dir);
|
|
||||||
return Ok(Vec::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut prompt_files = Vec::new();
|
|
||||||
|
|
||||||
// 读取所有项目目录
|
|
||||||
let entries = fs::read_dir(&projects_dir)
|
|
||||||
.map_err(|e| format!("无法读取项目目录: {}", e))?;
|
|
||||||
|
|
||||||
for entry in entries {
|
|
||||||
let entry = entry.map_err(|e| format!("无法读取目录条目: {}", e))?;
|
|
||||||
let path = entry.path();
|
|
||||||
|
|
||||||
if path.is_dir() {
|
|
||||||
let project_id = path
|
|
||||||
.file_name()
|
|
||||||
.and_then(|n| n.to_str())
|
|
||||||
.ok_or_else(|| "无效的目录名".to_string())?
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
// 获取实际项目路径
|
|
||||||
let project_path = match get_project_path_from_sessions(&path) {
|
|
||||||
Ok(p) => p.clone(),
|
|
||||||
Err(_) => {
|
|
||||||
warn!("无法从会话获取项目路径,使用解码: {}", project_id);
|
|
||||||
decode_project_path(&project_id)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 检查 .claude/CLAUDE.md 是否存在
|
|
||||||
let claude_md_path = PathBuf::from(&project_path).join(".claude").join("CLAUDE.md");
|
|
||||||
let has_claude_md = claude_md_path.exists();
|
|
||||||
|
|
||||||
let (content, file_size, modified_at) = if has_claude_md {
|
|
||||||
// 读取文件内容
|
|
||||||
let content = fs::read_to_string(&claude_md_path)
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
// 获取文件元数据
|
|
||||||
let metadata = fs::metadata(&claude_md_path).ok();
|
|
||||||
let file_size = metadata.as_ref().map(|m| m.len());
|
|
||||||
let modified_at = metadata
|
|
||||||
.and_then(|m| m.modified().ok())
|
|
||||||
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
|
|
||||||
.map(|d| d.as_secs() as i64);
|
|
||||||
|
|
||||||
(content, file_size, modified_at)
|
|
||||||
} else {
|
|
||||||
(None, None, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
prompt_files.push(PromptFileInfo {
|
|
||||||
project_id,
|
|
||||||
project_path: project_path.clone(),
|
|
||||||
has_claude_md,
|
|
||||||
content,
|
|
||||||
file_size,
|
|
||||||
modified_at,
|
|
||||||
claude_md_path: claude_md_path.to_string_lossy().to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按最后修改时间排序(最新的在前)
|
|
||||||
prompt_files.sort_by(|a, b| {
|
|
||||||
match (b.modified_at, a.modified_at) {
|
|
||||||
(Some(b_time), Some(a_time)) => b_time.cmp(&a_time),
|
|
||||||
(Some(_), None) => std::cmp::Ordering::Less,
|
|
||||||
(None, Some(_)) => std::cmp::Ordering::Greater,
|
|
||||||
(None, None) => a.project_path.cmp(&b.project_path),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
info!("找到 {} 个项目,其中 {} 个有 CLAUDE.md",
|
|
||||||
prompt_files.len(),
|
|
||||||
prompt_files.iter().filter(|p| p.has_claude_md).count()
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(prompt_files)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 读取指定项目的 CLAUDE.md 文件
|
|
||||||
#[command]
|
|
||||||
pub async fn read_prompt_file(project_path: String) -> Result<String, String> {
|
|
||||||
info!("读取提示词文件: {}", project_path);
|
|
||||||
|
|
||||||
let claude_md_path = PathBuf::from(&project_path).join(".claude").join("CLAUDE.md");
|
|
||||||
|
|
||||||
if !claude_md_path.exists() {
|
|
||||||
return Err(format!("文件不存在: {:?}", claude_md_path));
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::read_to_string(&claude_md_path)
|
|
||||||
.map_err(|e| format!("读取文件失败: {}", e))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 保存 CLAUDE.md 文件
|
|
||||||
#[command]
|
|
||||||
pub async fn save_prompt_file(project_path: String, content: String) -> Result<(), String> {
|
|
||||||
info!("保存提示词文件: {}", project_path);
|
|
||||||
|
|
||||||
let claude_dir = PathBuf::from(&project_path).join(".claude");
|
|
||||||
let claude_md_path = claude_dir.join("CLAUDE.md");
|
|
||||||
|
|
||||||
// 确保 .claude 目录存在
|
|
||||||
if !claude_dir.exists() {
|
|
||||||
fs::create_dir_all(&claude_dir)
|
|
||||||
.map_err(|e| format!("创建 .claude 目录失败: {}", e))?;
|
|
||||||
info!("创建 .claude 目录: {:?}", claude_dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 备份现有文件
|
|
||||||
if claude_md_path.exists() {
|
|
||||||
let backup_path = claude_md_path.with_extension("md.backup");
|
|
||||||
fs::copy(&claude_md_path, &backup_path)
|
|
||||||
.map_err(|e| format!("备份文件失败: {}", e))?;
|
|
||||||
info!("备份现有文件到: {:?}", backup_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 写入新内容
|
|
||||||
fs::write(&claude_md_path, content)
|
|
||||||
.map_err(|e| format!("写入文件失败: {}", e))?;
|
|
||||||
|
|
||||||
info!("成功保存文件: {:?}", claude_md_path);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 创建新的 CLAUDE.md 文件
|
|
||||||
#[command]
|
|
||||||
pub async fn create_prompt_file(project_path: String, content: String) -> Result<(), String> {
|
|
||||||
info!("创建提示词文件: {}", project_path);
|
|
||||||
|
|
||||||
let claude_dir = PathBuf::from(&project_path).join(".claude");
|
|
||||||
let claude_md_path = claude_dir.join("CLAUDE.md");
|
|
||||||
|
|
||||||
// 检查文件是否已存在
|
|
||||||
if claude_md_path.exists() {
|
|
||||||
return Err("CLAUDE.md 文件已存在,请使用编辑功能".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确保 .claude 目录存在
|
|
||||||
if !claude_dir.exists() {
|
|
||||||
fs::create_dir_all(&claude_dir)
|
|
||||||
.map_err(|e| format!("创建 .claude 目录失败: {}", e))?;
|
|
||||||
info!("创建 .claude 目录: {:?}", claude_dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 写入内容
|
|
||||||
fs::write(&claude_md_path, content)
|
|
||||||
.map_err(|e| format!("写入文件失败: {}", e))?;
|
|
||||||
|
|
||||||
info!("成功创建文件: {:?}", claude_md_path);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 删除 CLAUDE.md 文件
|
|
||||||
#[command]
|
|
||||||
pub async fn delete_prompt_file(project_path: String) -> Result<(), String> {
|
|
||||||
info!("删除提示词文件: {}", project_path);
|
|
||||||
|
|
||||||
let claude_md_path = PathBuf::from(&project_path).join(".claude").join("CLAUDE.md");
|
|
||||||
|
|
||||||
if !claude_md_path.exists() {
|
|
||||||
return Err("文件不存在".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 备份到 .backup
|
|
||||||
let backup_path = claude_md_path.with_extension("md.backup");
|
|
||||||
fs::copy(&claude_md_path, &backup_path)
|
|
||||||
.map_err(|e| format!("备份文件失败: {}", e))?;
|
|
||||||
|
|
||||||
// 删除文件
|
|
||||||
fs::remove_file(&claude_md_path)
|
|
||||||
.map_err(|e| format!("删除文件失败: {}", e))?;
|
|
||||||
|
|
||||||
info!("成功删除文件(已备份到 {:?})", backup_path);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 复制 CLAUDE.md 到另一个项目
|
|
||||||
#[command]
|
|
||||||
pub async fn copy_prompt_file(
|
|
||||||
source_project_path: String,
|
|
||||||
target_project_path: String,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
info!("复制提示词文件: {} -> {}", source_project_path, target_project_path);
|
|
||||||
|
|
||||||
let source_path = PathBuf::from(&source_project_path).join(".claude").join("CLAUDE.md");
|
|
||||||
if !source_path.exists() {
|
|
||||||
return Err("源文件不存在".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取源文件
|
|
||||||
let content = fs::read_to_string(&source_path)
|
|
||||||
.map_err(|e| format!("读取源文件失败: {}", e))?;
|
|
||||||
|
|
||||||
// 保存到目标路径
|
|
||||||
save_prompt_file(target_project_path, content).await
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { save } from '@tauri-apps/plugin-dialog';
|
||||||
|
import { usePromptFilesStore } from '@/stores/promptFilesStore';
|
||||||
|
import { useTranslation } from '@/hooks/useTranslation';
|
||||||
import { Edit, Play, Tag as TagIcon, Clock, Calendar } from 'lucide-react';
|
import { Edit, Play, Tag as TagIcon, Clock, Calendar } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
@@ -28,6 +31,8 @@ export const PromptFilePreview: React.FC<PromptFilePreviewProps> = ({
|
|||||||
onEdit,
|
onEdit,
|
||||||
onApply,
|
onApply,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { applyFile } = usePromptFilesStore();
|
||||||
|
const { t } = useTranslation();
|
||||||
const formatDate = (timestamp: number) => {
|
const formatDate = (timestamp: number) => {
|
||||||
return new Date(timestamp * 1000).toLocaleString('zh-CN', {
|
return new Date(timestamp * 1000).toLocaleString('zh-CN', {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
@@ -38,6 +43,19 @@ export const PromptFilePreview: React.FC<PromptFilePreviewProps> = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleApplyToCustom = async () => {
|
||||||
|
const selectedPath = await save({
|
||||||
|
defaultPath: 'CLAUDE.md',
|
||||||
|
filters: [
|
||||||
|
{ name: 'Markdown', extensions: ['md'] },
|
||||||
|
{ name: 'All Files', extensions: ['*'] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (!selectedPath) return;
|
||||||
|
await applyFile(file.id, String(selectedPath));
|
||||||
|
onOpenChange(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
|
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
|
||||||
@@ -100,6 +118,11 @@ export const PromptFilePreview: React.FC<PromptFilePreviewProps> = ({
|
|||||||
使用此文件
|
使用此文件
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
<Button variant="outline" onClick={handleApplyToCustom}>
|
||||||
|
<Play className="mr-2 h-4 w-4" />
|
||||||
|
{/** 使用管理页 i18n key,避免重复 */}
|
||||||
|
{t('promptFiles.applyToCustomPath')}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
@@ -108,4 +131,3 @@ export const PromptFilePreview: React.FC<PromptFilePreviewProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default PromptFilePreview;
|
export default PromptFilePreview;
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import type { PromptFile } from '@/lib/api';
|
|||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { PromptFileEditor } from './PromptFileEditor';
|
import { PromptFileEditor } from './PromptFileEditor';
|
||||||
import { PromptFilePreview } from './PromptFilePreview';
|
import { PromptFilePreview } from './PromptFilePreview';
|
||||||
|
import { save } from '@tauri-apps/plugin-dialog';
|
||||||
|
|
||||||
interface PromptFilesManagerProps {
|
interface PromptFilesManagerProps {
|
||||||
onBack?: () => void;
|
onBack?: () => void;
|
||||||
@@ -96,6 +97,29 @@ export const PromptFilesManager: React.FC<PromptFilesManagerProps> = ({ onBack,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 应用到自定义路径(文件路径),跨平台
|
||||||
|
const handleApplyToCustom = async (file: PromptFile) => {
|
||||||
|
try {
|
||||||
|
const selectedPath = await save({
|
||||||
|
defaultPath: 'CLAUDE.md',
|
||||||
|
filters: [
|
||||||
|
{ name: 'Markdown', extensions: ['md'] },
|
||||||
|
{ name: 'All Files', extensions: ['*'] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (!selectedPath) return; // 用户取消
|
||||||
|
|
||||||
|
setApplyingFileId(file.id);
|
||||||
|
const resultPath = await applyFile(file.id, String(selectedPath));
|
||||||
|
showToast(`已应用到: ${resultPath}`, 'success');
|
||||||
|
await loadFiles();
|
||||||
|
} catch (error) {
|
||||||
|
showToast(t('promptFiles.applyToCustomPathFailed'), 'error');
|
||||||
|
} finally {
|
||||||
|
setApplyingFileId(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleDeactivate = async () => {
|
const handleDeactivate = async () => {
|
||||||
try {
|
try {
|
||||||
await deactivateAll();
|
await deactivateAll();
|
||||||
@@ -183,8 +207,8 @@ export const PromptFilesManager: React.FC<PromptFilesManagerProps> = ({ onBack,
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold">提示词文件管理</h1>
|
<h1 className="text-3xl font-bold">{t('promptFiles.title')}</h1>
|
||||||
<p className="text-muted-foreground">管理和切换 Claude 项目提示词文件</p>
|
<p className="text-muted-foreground">{t('promptFiles.description')}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
@@ -289,6 +313,15 @@ export const PromptFilesManager: React.FC<PromptFilesManagerProps> = ({ onBack,
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handleApplyToCustom(file)}
|
||||||
|
disabled={applyingFileId === file.id}
|
||||||
|
>
|
||||||
|
<Play className="mr-2 h-4 w-4" />
|
||||||
|
{t('promptFiles.applyToCustomPath')}
|
||||||
|
</Button>
|
||||||
<Button variant="outline" size="sm" onClick={() => openPreview(file)}>
|
<Button variant="outline" size="sm" onClick={() => openPreview(file)}>
|
||||||
<Eye className="mr-2 h-4 w-4" />
|
<Eye className="mr-2 h-4 w-4" />
|
||||||
查看内容
|
查看内容
|
||||||
@@ -374,6 +407,16 @@ export const PromptFilesManager: React.FC<PromptFilesManagerProps> = ({ onBack,
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="w-full"
|
||||||
|
onClick={() => handleApplyToCustom(file)}
|
||||||
|
disabled={applyingFileId === file.id}
|
||||||
|
>
|
||||||
|
<Play className="mr-2 h-4 w-4" />
|
||||||
|
{t('promptFiles.applyToCustomPath')}
|
||||||
|
</Button>
|
||||||
<div className="flex gap-2 justify-center">
|
<div className="flex gap-2 justify-center">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -586,4 +629,3 @@ const ImportFromClaudeMdDialog: React.FC<{
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default PromptFilesManager;
|
export default PromptFilesManager;
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,7 @@
|
|||||||
"usage": "Usage Dashboard",
|
"usage": "Usage Dashboard",
|
||||||
"mcp": "MCP Manager",
|
"mcp": "MCP Manager",
|
||||||
"relayStations": "Relay Stations",
|
"relayStations": "Relay Stations",
|
||||||
"promptFiles": "Prompt Files",
|
"promptFiles": "CLAUDE.md",
|
||||||
"about": "About"
|
"about": "About"
|
||||||
},
|
},
|
||||||
"welcome": {
|
"welcome": {
|
||||||
@@ -139,8 +139,8 @@
|
|||||||
"mcpBrokerDesc": "Manage MCP servers",
|
"mcpBrokerDesc": "Manage MCP servers",
|
||||||
"claudeMd": "CLAUDE.md",
|
"claudeMd": "CLAUDE.md",
|
||||||
"claudeMdDesc": "Edit Claude configuration files",
|
"claudeMdDesc": "Edit Claude configuration files",
|
||||||
"promptFiles": "Prompt Files",
|
"promptFiles": "CLAUDE.md",
|
||||||
"promptFilesDesc": "Manage and switch CLAUDE.md prompt files",
|
"promptFilesDesc": "Manage and switch CLAUDE.md files",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"settingsDesc": "App settings and configuration",
|
"settingsDesc": "App settings and configuration",
|
||||||
"quickStartSession": "Quick Start New Session",
|
"quickStartSession": "Quick Start New Session",
|
||||||
@@ -321,8 +321,8 @@
|
|||||||
"createFirstCommand": "Create your first command"
|
"createFirstCommand": "Create your first command"
|
||||||
},
|
},
|
||||||
"promptFiles": {
|
"promptFiles": {
|
||||||
"title": "Prompt Files Management",
|
"title": "CLAUDE.md",
|
||||||
"description": "Manage and switch Claude project prompt files",
|
"description": "Manage and switch CLAUDE.md files",
|
||||||
"create": "New",
|
"create": "New",
|
||||||
"createFile": "Create Prompt File",
|
"createFile": "Create Prompt File",
|
||||||
"editFile": "Edit Prompt File",
|
"editFile": "Edit Prompt File",
|
||||||
@@ -358,6 +358,8 @@
|
|||||||
"preview": "Preview",
|
"preview": "Preview",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"inUse": "In Use",
|
"inUse": "In Use",
|
||||||
|
"applyToCustomPath": "Apply to Custom Path",
|
||||||
|
"applyToCustomPathFailed": "Apply to Custom Path Failed",
|
||||||
"lastUsed": "Last Used",
|
"lastUsed": "Last Used",
|
||||||
"createdAt": "Created At",
|
"createdAt": "Created At",
|
||||||
"updatedAt": "Updated At",
|
"updatedAt": "Updated At",
|
||||||
|
|||||||
@@ -118,7 +118,7 @@
|
|||||||
"usage": "用量仪表板",
|
"usage": "用量仪表板",
|
||||||
"mcp": "MCP 管理器",
|
"mcp": "MCP 管理器",
|
||||||
"relayStations": "中转站",
|
"relayStations": "中转站",
|
||||||
"promptFiles": "提示词文件",
|
"promptFiles": "CLAUDE.md",
|
||||||
"about": "关于"
|
"about": "关于"
|
||||||
},
|
},
|
||||||
"welcome": {
|
"welcome": {
|
||||||
@@ -134,8 +134,8 @@
|
|||||||
"mcpBrokerDesc": "管理 MCP 服务器",
|
"mcpBrokerDesc": "管理 MCP 服务器",
|
||||||
"claudeMd": "CLAUDE.md",
|
"claudeMd": "CLAUDE.md",
|
||||||
"claudeMdDesc": "编辑 Claude 配置文件",
|
"claudeMdDesc": "编辑 Claude 配置文件",
|
||||||
"promptFiles": "提示词管理",
|
"promptFiles": "CLAUDE.md",
|
||||||
"promptFilesDesc": "管理和切换 CLAUDE.md 提示词文件",
|
"promptFilesDesc": "管理和切换 CLAUDE.md 文件",
|
||||||
"settings": "设置",
|
"settings": "设置",
|
||||||
"settingsDesc": "应用设置和配置",
|
"settingsDesc": "应用设置和配置",
|
||||||
"quickStartSession": "快速开始新会话",
|
"quickStartSession": "快速开始新会话",
|
||||||
@@ -308,8 +308,8 @@
|
|||||||
"createFirstCommand": "创建您的第一个命令"
|
"createFirstCommand": "创建您的第一个命令"
|
||||||
},
|
},
|
||||||
"promptFiles": {
|
"promptFiles": {
|
||||||
"title": "提示词文件管理",
|
"title": "CLAUDE.md",
|
||||||
"description": "管理和切换 Claude 项目提示词文件",
|
"description": "管理和切换 CLAUDE.md 文件",
|
||||||
"create": "新建",
|
"create": "新建",
|
||||||
"createFile": "创建提示词文件",
|
"createFile": "创建提示词文件",
|
||||||
"editFile": "编辑提示词文件",
|
"editFile": "编辑提示词文件",
|
||||||
@@ -351,7 +351,9 @@
|
|||||||
"syncToClaudeDir": "同步到 .claude",
|
"syncToClaudeDir": "同步到 .claude",
|
||||||
"syncing": "同步中...",
|
"syncing": "同步中...",
|
||||||
"syncSuccess": "已同步到 {{path}}",
|
"syncSuccess": "已同步到 {{path}}",
|
||||||
"syncFailed": "同步失败"
|
"syncFailed": "同步失败",
|
||||||
|
"applyToCustomPath": "应用到自定义路径",
|
||||||
|
"applyToCustomPathFailed": "应用到自定义路径失败"
|
||||||
},
|
},
|
||||||
"hooks": {
|
"hooks": {
|
||||||
"hooksConfiguration": "钩子配置",
|
"hooksConfiguration": "钩子配置",
|
||||||
|
|||||||
Reference in New Issue
Block a user