mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-05-21 04:40:18 +08:00
155532ea8c
* feat(prompts): add prompt management across Tauri service and React UI
- backend: add commands/prompt.rs, services/prompt.rs, register in commands/mod.rs and lib.rs, refine app_config.rs
- frontend: add PromptPanel, PromptFormModal, PromptListItem, MarkdownEditor, usePromptActions, integrate in App.tsx
- api: add src/lib/api/prompts.ts
- i18n: update src/i18n/locales/{en,zh}.json
- build: update package.json and pnpm-lock.yaml
* feat(i18n): improve i18n for prompts and Markdown editor
- update src/i18n/locales/{en,zh}.json keys and strings
- apply i18n in PromptFormModal, PromptPanel, and MarkdownEditor
- align prompt text with src-tauri/src/services/prompt.rs
* feat(prompts): add enable/disable toggle and simplify panel UI
- Add PromptToggle component and integrate in prompt list items
- Implement toggleEnabled with optimistic update; enable via API, disable via upsert with enabled=false;
reload after success
- Simplify PromptPanel: remove file import and current-file preview to keep CRUD flow focused
- Tweak header controls style (use mcp variant) and minor copy: rename “Prompt Management” to “Prompts”
- i18n: add disableSuccess/disableFailed messages
- Backend (Tauri): prevent duplicate backups when importing original prompt content
* style: unify code formatting with trailing commas
* feat(prompts): add Gemini filename support to PromptFormModal
Update filename mapping to use Record<AppId, string> pattern, supporting
GEMINI.md alongside CLAUDE.md and AGENTS.md.
* fix(prompts): sync enabled prompt to file when updating
When updating a prompt that is currently enabled, automatically sync
the updated content to the corresponding live file (CLAUDE.md/AGENTS.md/GEMINI.md).
This ensures the active prompt file always reflects the latest content
when editing enabled prompts.
153 lines
3.9 KiB
TypeScript
153 lines
3.9 KiB
TypeScript
import { useState, useCallback } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { toast } from "sonner";
|
|
import { promptsApi, type Prompt, type AppId } from "@/lib/api";
|
|
|
|
export function usePromptActions(appId: AppId) {
|
|
const { t } = useTranslation();
|
|
const [prompts, setPrompts] = useState<Record<string, Prompt>>({});
|
|
const [loading, setLoading] = useState(false);
|
|
const [currentFileContent, setCurrentFileContent] = useState<string | null>(
|
|
null,
|
|
);
|
|
|
|
const reload = useCallback(async () => {
|
|
setLoading(true);
|
|
try {
|
|
const data = await promptsApi.getPrompts(appId);
|
|
setPrompts(data);
|
|
|
|
// 同时加载当前文件内容
|
|
try {
|
|
const content = await promptsApi.getCurrentFileContent(appId);
|
|
setCurrentFileContent(content);
|
|
} catch (error) {
|
|
setCurrentFileContent(null);
|
|
}
|
|
} catch (error) {
|
|
toast.error(t("prompts.loadFailed"));
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [appId, t]);
|
|
|
|
const savePrompt = useCallback(
|
|
async (id: string, prompt: Prompt) => {
|
|
try {
|
|
await promptsApi.upsertPrompt(appId, id, prompt);
|
|
await reload();
|
|
toast.success(t("prompts.saveSuccess"));
|
|
} catch (error) {
|
|
toast.error(t("prompts.saveFailed"));
|
|
throw error;
|
|
}
|
|
},
|
|
[appId, reload, t],
|
|
);
|
|
|
|
const deletePrompt = useCallback(
|
|
async (id: string) => {
|
|
try {
|
|
await promptsApi.deletePrompt(appId, id);
|
|
await reload();
|
|
toast.success(t("prompts.deleteSuccess"));
|
|
} catch (error) {
|
|
toast.error(t("prompts.deleteFailed"));
|
|
throw error;
|
|
}
|
|
},
|
|
[appId, reload, t],
|
|
);
|
|
|
|
const enablePrompt = useCallback(
|
|
async (id: string) => {
|
|
try {
|
|
await promptsApi.enablePrompt(appId, id);
|
|
await reload();
|
|
toast.success(t("prompts.enableSuccess"));
|
|
} catch (error) {
|
|
toast.error(t("prompts.enableFailed"));
|
|
throw error;
|
|
}
|
|
},
|
|
[appId, reload, t],
|
|
);
|
|
|
|
const toggleEnabled = useCallback(
|
|
async (id: string, enabled: boolean) => {
|
|
// Optimistic update
|
|
const previousPrompts = prompts;
|
|
|
|
// 如果要启用当前提示词,先禁用其他所有提示词
|
|
if (enabled) {
|
|
const updatedPrompts = Object.keys(prompts).reduce(
|
|
(acc, key) => {
|
|
acc[key] = {
|
|
...prompts[key],
|
|
enabled: key === id,
|
|
};
|
|
return acc;
|
|
},
|
|
{} as Record<string, Prompt>,
|
|
);
|
|
setPrompts(updatedPrompts);
|
|
} else {
|
|
setPrompts((prev) => ({
|
|
...prev,
|
|
[id]: {
|
|
...prev[id],
|
|
enabled: false,
|
|
},
|
|
}));
|
|
}
|
|
|
|
try {
|
|
if (enabled) {
|
|
await promptsApi.enablePrompt(appId, id);
|
|
toast.success(t("prompts.enableSuccess"));
|
|
} else {
|
|
// 禁用提示词 - 需要后端支持
|
|
await promptsApi.upsertPrompt(appId, id, {
|
|
...prompts[id],
|
|
enabled: false,
|
|
});
|
|
toast.success(t("prompts.disableSuccess"));
|
|
}
|
|
await reload();
|
|
} catch (error) {
|
|
// Rollback on failure
|
|
setPrompts(previousPrompts);
|
|
toast.error(
|
|
enabled ? t("prompts.enableFailed") : t("prompts.disableFailed"),
|
|
);
|
|
throw error;
|
|
}
|
|
},
|
|
[appId, prompts, reload, t],
|
|
);
|
|
|
|
const importFromFile = useCallback(async () => {
|
|
try {
|
|
const id = await promptsApi.importFromFile(appId);
|
|
await reload();
|
|
toast.success(t("prompts.importSuccess"));
|
|
return id;
|
|
} catch (error) {
|
|
toast.error(t("prompts.importFailed"));
|
|
throw error;
|
|
}
|
|
}, [appId, reload, t]);
|
|
|
|
return {
|
|
prompts,
|
|
loading,
|
|
currentFileContent,
|
|
reload,
|
|
savePrompt,
|
|
deletePrompt,
|
|
enablePrompt,
|
|
toggleEnabled,
|
|
importFromFile,
|
|
};
|
|
}
|