- {t("prompts.count", { count: promptEntries.length })} ·{" "}
- {enabledPrompt
- ? t("prompts.enabledName", { name: enabledPrompt[1].name })
- : t("prompts.noneEnabled")}
+ return (
+
+
+
+ {t("prompts.count", { count: promptEntries.length })} ·{" "}
+ {enabledPrompt
+ ? t("prompts.enabledName", { name: enabledPrompt[1].name })
+ : t("prompts.noneEnabled")}
+
-
-
- {loading ? (
-
- {t("prompts.loading")}
-
- ) : promptEntries.length === 0 ? (
-
-
-
+
+ {loading ? (
+
+ {t("prompts.loading")}
-
- {t("prompts.empty")}
-
-
- {t("prompts.emptyDescription")}
-
-
- ) : (
-
- {promptEntries.map(([id, prompt]) => (
-
- ))}
-
+ ) : promptEntries.length === 0 ? (
+
+
+
+
+
+ {t("prompts.empty")}
+
+
+ {t("prompts.emptyDescription")}
+
+
+ ) : (
+
+ {promptEntries.map(([id, prompt]) => (
+
+ ))}
+
+ )}
+
+
+ {isFormOpen && (
+
setIsFormOpen(false)}
+ />
+ )}
+
+ {confirmDialog && (
+ setConfirmDialog(null)}
+ />
)}
-
- {isFormOpen && (
-
setIsFormOpen(false)}
- />
- )}
-
- {confirmDialog && (
- setConfirmDialog(null)}
- />
- )}
-
- );
-});
+ );
+ },
+);
PromptPanel.displayName = "PromptPanel";
diff --git a/src/components/skills/RepoManagerPanel.tsx b/src/components/skills/RepoManagerPanel.tsx
index a29b554..bb3a7b5 100644
--- a/src/components/skills/RepoManagerPanel.tsx
+++ b/src/components/skills/RepoManagerPanel.tsx
@@ -9,199 +9,211 @@ import { FullScreenPanel } from "@/components/common/FullScreenPanel";
import type { Skill, SkillRepo } from "@/lib/api/skills";
interface RepoManagerPanelProps {
- repos: SkillRepo[];
- skills: Skill[];
- onAdd: (repo: SkillRepo) => Promise
;
- onRemove: (owner: string, name: string) => Promise;
- onClose: () => void;
+ repos: SkillRepo[];
+ skills: Skill[];
+ onAdd: (repo: SkillRepo) => Promise;
+ onRemove: (owner: string, name: string) => Promise;
+ onClose: () => void;
}
export function RepoManagerPanel({
- repos,
- skills,
- onAdd,
- onRemove,
- onClose,
+ repos,
+ skills,
+ onAdd,
+ onRemove,
+ onClose,
}: RepoManagerPanelProps) {
- const { t } = useTranslation();
- const [repoUrl, setRepoUrl] = useState("");
- const [branch, setBranch] = useState("");
- const [skillsPath, setSkillsPath] = useState("");
- const [error, setError] = useState("");
+ const { t } = useTranslation();
+ const [repoUrl, setRepoUrl] = useState("");
+ const [branch, setBranch] = useState("");
+ const [skillsPath, setSkillsPath] = useState("");
+ const [error, setError] = useState("");
- const getSkillCount = (repo: SkillRepo) =>
- skills.filter(
- (skill) =>
- skill.repoOwner === repo.owner &&
- skill.repoName === repo.name &&
- (skill.repoBranch || "main") === (repo.branch || "main"),
- ).length;
+ const getSkillCount = (repo: SkillRepo) =>
+ skills.filter(
+ (skill) =>
+ skill.repoOwner === repo.owner &&
+ skill.repoName === repo.name &&
+ (skill.repoBranch || "main") === (repo.branch || "main"),
+ ).length;
- const parseRepoUrl = (
- url: string,
- ): { owner: string; name: string } | null => {
- let cleaned = url.trim();
- cleaned = cleaned.replace(/^https?:\/\/github\.com\//, "");
- cleaned = cleaned.replace(/\.git$/, "");
+ const parseRepoUrl = (
+ url: string,
+ ): { owner: string; name: string } | null => {
+ let cleaned = url.trim();
+ cleaned = cleaned.replace(/^https?:\/\/github\.com\//, "");
+ cleaned = cleaned.replace(/\.git$/, "");
- const parts = cleaned.split("/");
- if (parts.length === 2 && parts[0] && parts[1]) {
- return { owner: parts[0], name: parts[1] };
- }
+ const parts = cleaned.split("/");
+ if (parts.length === 2 && parts[0] && parts[1]) {
+ return { owner: parts[0], name: parts[1] };
+ }
- return null;
- };
+ return null;
+ };
- const handleAdd = async () => {
- setError("");
+ const handleAdd = async () => {
+ setError("");
- const parsed = parseRepoUrl(repoUrl);
- if (!parsed) {
- setError(t("skills.repo.invalidUrl"));
- return;
- }
+ const parsed = parseRepoUrl(repoUrl);
+ if (!parsed) {
+ setError(t("skills.repo.invalidUrl"));
+ return;
+ }
- try {
- await onAdd({
- owner: parsed.owner,
- name: parsed.name,
- branch: branch || "main",
- enabled: true,
- skillsPath: skillsPath.trim() || undefined,
- });
+ try {
+ await onAdd({
+ owner: parsed.owner,
+ name: parsed.name,
+ branch: branch || "main",
+ enabled: true,
+ skillsPath: skillsPath.trim() || undefined,
+ });
- setRepoUrl("");
- setBranch("");
- setSkillsPath("");
- } catch (e) {
- setError(e instanceof Error ? e.message : t("skills.repo.addFailed"));
- }
- };
+ setRepoUrl("");
+ setBranch("");
+ setSkillsPath("");
+ } catch (e) {
+ setError(e instanceof Error ? e.message : t("skills.repo.addFailed"));
+ }
+ };
- const handleOpenRepo = async (owner: string, name: string) => {
- try {
- await settingsApi.openExternal(`https://github.com/${owner}/${name}`);
- } catch (error) {
- console.error("Failed to open URL:", error);
- }
- };
+ const handleOpenRepo = async (owner: string, name: string) => {
+ try {
+ await settingsApi.openExternal(`https://github.com/${owner}/${name}`);
+ } catch (error) {
+ console.error("Failed to open URL:", error);
+ }
+ };
- return (
-
- {/* 添加仓库表单 */}
-
-
添加技能仓库
-
-
-
- setRepoUrl(e.target.value)}
- className="mt-2"
- />
-
-
- {error &&
{error}
}
-
+ return (
+
+ {/* 添加仓库表单 */}
+
+
+ 添加技能仓库
+
+
+
+
+ setRepoUrl(e.target.value)}
+ className="mt-2"
+ />
+
+
+ {error && (
+
{error}
+ )}
+
+
+
+
+ {/* 仓库列表 */}
+
+
+ {t("skills.repo.list")}
+
+ {repos.length === 0 ? (
+
+
+ {t("skills.repo.empty")}
+
+
+ ) : (
+
+ {repos.map((repo) => (
+
+
+
+ {repo.owner}/{repo.name}
+
+
+ {t("skills.repo.branch")}: {repo.branch || "main"}
+ {repo.skillsPath && (
+ <>
+ •
+ {t("skills.repo.path")}: {repo.skillsPath}
+ >
+ )}
+
+ {t("skills.repo.skillCount", {
+ count: getSkillCount(repo),
+ })}
+
+
-
-
- {/* 仓库列表 */}
-
-
{t("skills.repo.list")}
- {repos.length === 0 ? (
-
-
- {t("skills.repo.empty")}
-
-
- ) : (
-
- {repos.map((repo) => (
-
-
-
- {repo.owner}/{repo.name}
-
-
- {t("skills.repo.branch")}: {repo.branch || "main"}
- {repo.skillsPath && (
- <>
- •
- {t("skills.repo.path")}: {repo.skillsPath}
- >
- )}
-
- {t("skills.repo.skillCount", {
- count: getSkillCount(repo),
- })}
-
-
-
-
-
-
-
-
- ))}
-
- )}
-
-
- );
+
+
+
+
+
+ ))}
+
+ )}
+
+
+ );
}
diff --git a/src/components/skills/SkillsPage.tsx b/src/components/skills/SkillsPage.tsx
index 497ffa9..3f3f889 100644
--- a/src/components/skills/SkillsPage.tsx
+++ b/src/components/skills/SkillsPage.tsx
@@ -16,155 +16,160 @@ export interface SkillsPageHandle {
openRepoManager: () => void;
}
-export const SkillsPage = forwardRef
(({ onClose: _onClose }, ref) => {
- const { t } = useTranslation();
- const [skills, setSkills] = useState([]);
- const [repos, setRepos] = useState([]);
- const [loading, setLoading] = useState(true);
- const [repoManagerOpen, setRepoManagerOpen] = useState(false);
+export const SkillsPage = forwardRef(
+ ({ onClose: _onClose }, ref) => {
+ const { t } = useTranslation();
+ const [skills, setSkills] = useState([]);
+ const [repos, setRepos] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [repoManagerOpen, setRepoManagerOpen] = useState(false);
- const loadSkills = async (afterLoad?: (data: Skill[]) => void) => {
- try {
- setLoading(true);
- const data = await skillsApi.getAll();
- setSkills(data);
- if (afterLoad) {
- afterLoad(data);
+ const loadSkills = async (afterLoad?: (data: Skill[]) => void) => {
+ try {
+ setLoading(true);
+ const data = await skillsApi.getAll();
+ setSkills(data);
+ if (afterLoad) {
+ afterLoad(data);
+ }
+ } catch (error) {
+ toast.error(t("skills.loadFailed"), {
+ description:
+ error instanceof Error ? error.message : t("common.error"),
+ });
+ } finally {
+ setLoading(false);
}
- } catch (error) {
- toast.error(t("skills.loadFailed"), {
- description: error instanceof Error ? error.message : t("common.error"),
- });
- } finally {
- setLoading(false);
- }
- };
+ };
- const loadRepos = async () => {
- try {
- const data = await skillsApi.getRepos();
- setRepos(data);
- } catch (error) {
- console.error("Failed to load repos:", error);
- }
- };
+ const loadRepos = async () => {
+ try {
+ const data = await skillsApi.getRepos();
+ setRepos(data);
+ } catch (error) {
+ console.error("Failed to load repos:", error);
+ }
+ };
- useEffect(() => {
- Promise.all([loadSkills(), loadRepos()]);
- }, []);
+ useEffect(() => {
+ Promise.all([loadSkills(), loadRepos()]);
+ }, []);
- useImperativeHandle(ref, () => ({
- refresh: () => loadSkills(),
- openRepoManager: () => setRepoManagerOpen(true)
- }));
+ useImperativeHandle(ref, () => ({
+ refresh: () => loadSkills(),
+ openRepoManager: () => setRepoManagerOpen(true),
+ }));
- const handleInstall = async (directory: string) => {
- try {
- await skillsApi.install(directory);
- toast.success(t("skills.installSuccess", { name: directory }));
- await loadSkills();
- } catch (error) {
- toast.error(t("skills.installFailed"), {
- description: error instanceof Error ? error.message : t("common.error"),
- });
- }
- };
+ const handleInstall = async (directory: string) => {
+ try {
+ await skillsApi.install(directory);
+ toast.success(t("skills.installSuccess", { name: directory }));
+ await loadSkills();
+ } catch (error) {
+ toast.error(t("skills.installFailed"), {
+ description:
+ error instanceof Error ? error.message : t("common.error"),
+ });
+ }
+ };
- const handleUninstall = async (directory: string) => {
- try {
- await skillsApi.uninstall(directory);
- toast.success(t("skills.uninstallSuccess", { name: directory }));
- await loadSkills();
- } catch (error) {
- toast.error(t("skills.uninstallFailed"), {
- description: error instanceof Error ? error.message : t("common.error"),
- });
- }
- };
+ const handleUninstall = async (directory: string) => {
+ try {
+ await skillsApi.uninstall(directory);
+ toast.success(t("skills.uninstallSuccess", { name: directory }));
+ await loadSkills();
+ } catch (error) {
+ toast.error(t("skills.uninstallFailed"), {
+ description:
+ error instanceof Error ? error.message : t("common.error"),
+ });
+ }
+ };
- const handleAddRepo = async (repo: SkillRepo) => {
- await skillsApi.addRepo(repo);
+ const handleAddRepo = async (repo: SkillRepo) => {
+ await skillsApi.addRepo(repo);
- let repoSkillCount = 0;
- await Promise.all([
- loadRepos(),
- loadSkills((data) => {
- repoSkillCount = data.filter(
- (skill) =>
- skill.repoOwner === repo.owner &&
- skill.repoName === repo.name &&
- (skill.repoBranch || "main") === (repo.branch || "main"),
- ).length;
- }),
- ]);
+ let repoSkillCount = 0;
+ await Promise.all([
+ loadRepos(),
+ loadSkills((data) => {
+ repoSkillCount = data.filter(
+ (skill) =>
+ skill.repoOwner === repo.owner &&
+ skill.repoName === repo.name &&
+ (skill.repoBranch || "main") === (repo.branch || "main"),
+ ).length;
+ }),
+ ]);
- toast.success(
- t("skills.repo.addSuccess", {
- owner: repo.owner,
- name: repo.name,
- count: repoSkillCount,
- }),
- );
- };
+ toast.success(
+ t("skills.repo.addSuccess", {
+ owner: repo.owner,
+ name: repo.name,
+ count: repoSkillCount,
+ }),
+ );
+ };
- const handleRemoveRepo = async (owner: string, name: string) => {
- await skillsApi.removeRepo(owner, name);
- toast.success(t("skills.repo.removeSuccess", { owner, name }));
- await Promise.all([loadRepos(), loadSkills()]);
- };
+ const handleRemoveRepo = async (owner: string, name: string) => {
+ await skillsApi.removeRepo(owner, name);
+ toast.success(t("skills.repo.removeSuccess", { owner, name }));
+ await Promise.all([loadRepos(), loadSkills()]);
+ };
- return (
-
- {/* 顶部操作栏(固定区域)已移除,由 App.tsx 接管 */}
+ return (
+
+ {/* 顶部操作栏(固定区域)已移除,由 App.tsx 接管 */}
- {/* 技能网格(可滚动详情区域) */}
-
- {loading ? (
-
-
-
- ) : skills.length === 0 ? (
-
-
- {t("skills.empty")}
-
-
- {t("skills.emptyDescription")}
-
-
-
- ) : (
-
- {skills.map((skill) => (
-
- ))}
-
+ {/* 技能网格(可滚动详情区域) */}
+
+ {loading ? (
+
+
+
+ ) : skills.length === 0 ? (
+
+
+ {t("skills.empty")}
+
+
+ {t("skills.emptyDescription")}
+
+
+
+ ) : (
+
+ {skills.map((skill) => (
+
+ ))}
+
+ )}
+
+
+ {/* 仓库管理面板 */}
+ {repoManagerOpen && (
+
setRepoManagerOpen(false)}
+ />
)}
-
- {/* 仓库管理面板 */}
- {repoManagerOpen && (
-
setRepoManagerOpen(false)}
- />
- )}
-
- );
-});
+ );
+ },
+);
SkillsPage.displayName = "SkillsPage";
diff --git a/src/types.ts b/src/types.ts
index b4cb57f..ff0fcbf 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -52,6 +52,14 @@ export interface UsageScript {
accessToken?: string; // 访问令牌(NewAPI 模板使用)
userId?: string; // 用户ID(NewAPI 模板使用)
autoQueryInterval?: number; // 自动查询间隔(单位:分钟,0 表示禁用)
+ autoIntervalMinutes?: number; // 自动查询间隔(分钟)- 别名字段
+ request?: {
+ // 请求配置
+ url?: string; // 请求 URL
+ method?: string; // HTTP 方法
+ headers?: Record
; // 请求头
+ body?: any; // 请求体
+ };
}
// 单个套餐用量数据
@@ -101,6 +109,8 @@ export interface Settings {
geminiConfigDir?: string;
// 首选语言(可选,默认中文)
language?: "en" | "zh";
+ // 是否开机自启
+ launchOnStartup?: boolean;
// Claude 自定义端点列表
customEndpointsClaude?: Record;
// Codex 自定义端点列表