mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-27 08:14:33 +08:00
feat(skills): add skill sync method setting (symlink/copy)
- Add SyncMethod enum (Auto/Symlink/Copy) in Rust backend - Implement sync_to_app_dir with symlink support (cross-platform) - Add SkillSyncMethodSettings UI component (simplified 2-button selector) - Add i18n support for zh/en/ja - Replace copy_to_app with configurable sync_to_app_dir - Add skill_sync_method field to AppSettings User can now choose between symlink (disk space saving) or copy (best compatibility) in Settings > General.
This commit is contained in:
@@ -51,19 +51,19 @@ export function useModelState({
|
||||
}: UseModelStateProps) {
|
||||
// Initialize state by parsing config directly (fixes edit mode backfill)
|
||||
const [claudeModel, setClaudeModel] = useState(
|
||||
() => parseModelsFromConfig(settingsConfig).model
|
||||
() => parseModelsFromConfig(settingsConfig).model,
|
||||
);
|
||||
const [reasoningModel, setReasoningModel] = useState(
|
||||
() => parseModelsFromConfig(settingsConfig).reasoning
|
||||
() => parseModelsFromConfig(settingsConfig).reasoning,
|
||||
);
|
||||
const [defaultHaikuModel, setDefaultHaikuModel] = useState(
|
||||
() => parseModelsFromConfig(settingsConfig).haiku
|
||||
() => parseModelsFromConfig(settingsConfig).haiku,
|
||||
);
|
||||
const [defaultSonnetModel, setDefaultSonnetModel] = useState(
|
||||
() => parseModelsFromConfig(settingsConfig).sonnet
|
||||
() => parseModelsFromConfig(settingsConfig).sonnet,
|
||||
);
|
||||
const [defaultOpusModel, setDefaultOpusModel] = useState(
|
||||
() => parseModelsFromConfig(settingsConfig).opus
|
||||
() => parseModelsFromConfig(settingsConfig).opus,
|
||||
);
|
||||
|
||||
const isUserEditingRef = useRef(false);
|
||||
|
||||
@@ -35,6 +35,7 @@ import { LanguageSettings } from "@/components/settings/LanguageSettings";
|
||||
import { ThemeSettings } from "@/components/settings/ThemeSettings";
|
||||
import { WindowSettings } from "@/components/settings/WindowSettings";
|
||||
import { AppVisibilitySettings } from "@/components/settings/AppVisibilitySettings";
|
||||
import { SkillSyncMethodSettings } from "@/components/settings/SkillSyncMethodSettings";
|
||||
import { DirectorySettings } from "@/components/settings/DirectorySettings";
|
||||
import { ImportExportSection } from "@/components/settings/ImportExportSection";
|
||||
import { AboutSection } from "@/components/settings/AboutSection";
|
||||
@@ -249,6 +250,12 @@ export function SettingsPage({
|
||||
settings={settings}
|
||||
onChange={handleAutoSave}
|
||||
/>
|
||||
<SkillSyncMethodSettings
|
||||
value={settings.skillSyncMethod ?? "auto"}
|
||||
onChange={(method) =>
|
||||
handleAutoSave({ skillSyncMethod: method })
|
||||
}
|
||||
/>
|
||||
</motion.div>
|
||||
) : null}
|
||||
</TabsContent>
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { SkillSyncMethod } from "@/types";
|
||||
|
||||
export interface SkillSyncMethodSettingsProps {
|
||||
value: SkillSyncMethod;
|
||||
onChange: (value: SkillSyncMethod) => void;
|
||||
}
|
||||
|
||||
export function SkillSyncMethodSettings({
|
||||
value,
|
||||
onChange,
|
||||
}: SkillSyncMethodSettingsProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Handle default values: undefined or "auto" defaults to symlink display
|
||||
const displayValue = value === "copy" ? "copy" : "symlink";
|
||||
|
||||
return (
|
||||
<section className="space-y-2">
|
||||
<header className="space-y-1">
|
||||
<h3 className="text-sm font-medium">{t("settings.skillSync.title")}</h3>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("settings.skillSync.description")}
|
||||
</p>
|
||||
</header>
|
||||
<div className="inline-flex gap-1 rounded-md border border-border-default bg-background p-1">
|
||||
<SyncMethodButton
|
||||
active={displayValue === "symlink"}
|
||||
onClick={() => onChange("symlink")}
|
||||
>
|
||||
{t("settings.skillSync.symlink")}
|
||||
</SyncMethodButton>
|
||||
<SyncMethodButton
|
||||
active={displayValue === "copy"}
|
||||
onClick={() => onChange("copy")}
|
||||
>
|
||||
{t("settings.skillSync.copy")}
|
||||
</SyncMethodButton>
|
||||
</div>
|
||||
{displayValue === "symlink" && (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("settings.skillSync.symlinkHint")}
|
||||
</p>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
interface SyncMethodButtonProps {
|
||||
active: boolean;
|
||||
onClick: () => void;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
function SyncMethodButton({
|
||||
active,
|
||||
onClick,
|
||||
children,
|
||||
}: SyncMethodButtonProps) {
|
||||
return (
|
||||
<Button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
size="sm"
|
||||
variant={active ? "default" : "ghost"}
|
||||
className={cn(
|
||||
"min-w-[96px]",
|
||||
active
|
||||
? "shadow-sm"
|
||||
: "text-muted-foreground hover:text-foreground hover:bg-muted",
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user