feat(editor): add JSON format button to editors

Add one-click format functionality for JSON editors with toast notifications:

- Add format button to JsonEditor (CodeMirror)
- Add format button to CodexAuthSection (auth.json)
- Add format button to CommonConfigEditor (settings + modal)
- Add formatJSON utility function
- Add i18n keys: format, formatSuccess, formatError

Note: TOML formatting was intentionally NOT added to avoid losing comments
during parse/stringify operations. TOML validation remains available via
the existing useCodexTomlValidation hook.
This commit is contained in:
Jason
2025-11-01 21:05:01 +08:00
parent b1f7840e45
commit 87f408c163
6 changed files with 194 additions and 23 deletions

View File

@@ -8,7 +8,9 @@ import {
} from "@/components/ui/dialog";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
import { Save } from "lucide-react";
import { Save, Wand2 } from "lucide-react";
import { toast } from "sonner";
import { formatJSON } from "@/utils/formatters";
interface CommonConfigEditorProps {
value: string;
@@ -37,6 +39,44 @@ export function CommonConfigEditor({
}: CommonConfigEditorProps) {
const { t } = useTranslation();
const handleFormatMain = () => {
if (!value.trim()) return;
try {
const formatted = formatJSON(value);
onChange(formatted);
toast.success(t("common.formatSuccess", { defaultValue: "格式化成功" }));
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : String(error);
toast.error(
t("common.formatError", {
defaultValue: "格式化失败:{{error}}",
error: errorMessage,
}),
);
}
};
const handleFormatModal = () => {
if (!commonConfigSnippet.trim()) return;
try {
const formatted = formatJSON(commonConfigSnippet);
onCommonConfigSnippetChange(formatted);
toast.success(t("common.formatSuccess", { defaultValue: "格式化成功" }));
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : String(error);
toast.error(
t("common.formatError", {
defaultValue: "格式化失败:{{error}}",
error: errorMessage,
}),
);
}
};
return (
<>
<div className="space-y-2">
@@ -97,11 +137,21 @@ export function CommonConfigEditor({
data-gramm_editor="false"
data-enable-grammarly="false"
/>
<p className="text-xs text-muted-foreground">
{t("claudeConfig.fullSettingsHint", {
defaultValue: "请填写完整的 Claude Code 配置",
})}
</p>
<div className="flex items-center justify-between">
<button
type="button"
onClick={handleFormatMain}
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
>
<Wand2 className="w-3.5 h-3.5" />
{t("common.format", { defaultValue: "格式化" })}
</button>
<p className="text-xs text-muted-foreground">
{t("claudeConfig.fullSettingsHint", {
defaultValue: "请填写完整的 Claude Code 配置",
})}
</p>
</div>
</div>
<Dialog
@@ -137,11 +187,21 @@ export function CommonConfigEditor({
data-gramm_editor="false"
data-enable-grammarly="false"
/>
{commonConfigError && (
<p className="text-sm text-red-500 dark:text-red-400">
{commonConfigError}
</p>
)}
<div className="flex items-center justify-between">
<button
type="button"
onClick={handleFormatModal}
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
>
<Wand2 className="w-3.5 h-3.5" />
{t("common.format", { defaultValue: "格式化" })}
</button>
{commonConfigError && (
<p className="text-sm text-red-500 dark:text-red-400">
{commonConfigError}
</p>
)}
</div>
</div>
<DialogFooter>
<Button type="button" variant="outline" onClick={onModalClose}>