diff --git a/web/src/components/code-viewer.tsx b/web/src/components/code-viewer.tsx
index dcd23792..c2547d76 100644
--- a/web/src/components/code-viewer.tsx
+++ b/web/src/components/code-viewer.tsx
@@ -31,10 +31,7 @@ export function CopyButton({ value, className, src, variant = "ghost", ...props
size="icon"
type="button"
variant={variant}
- className={cn(
- "relative z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50 [&_svg]:h-3 [&_svg]:w-3",
- className,
- )}
+ className={cn("relative z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50", className)}
onClick={() => {
copyToClipboardWithMeta(value);
setHasCopied(true);
diff --git a/web/src/components/copyable-field.tsx b/web/src/components/copyable-field.tsx
new file mode 100644
index 00000000..acb3240b
--- /dev/null
+++ b/web/src/components/copyable-field.tsx
@@ -0,0 +1,39 @@
+import { Button } from "@/components/ui/button";
+import { Label } from "@/components/ui/label";
+import { Check, Copy } from "lucide-react";
+import { useCallback, useState } from "react";
+import { toast } from "sonner";
+
+interface CopyableFieldProps {
+ label: string;
+ value?: string;
+ size?: number;
+}
+
+export function CopyableField({ label, value, size }: CopyableFieldProps) {
+ const [copied, setCopied] = useState(false);
+
+ const copyToClipboard = useCallback(() => {
+ if (typeof value === "string") {
+ navigator.clipboard.writeText(value).then(() => {
+ setCopied(true);
+ toast.success(`复制${label}成功`);
+ setTimeout(() => setCopied(false), 2000);
+ });
+ }
+ }, [value, label]);
+
+ return (
+
+
+
+
+ {value} {size != null && `(${size} bytes)`}
+
+
+
+
+ );
+}
diff --git a/web/src/components/main-config-card.tsx b/web/src/components/main-config-card.tsx
index 2ebcf520..5b9e5e8a 100644
--- a/web/src/components/main-config-card.tsx
+++ b/web/src/components/main-config-card.tsx
@@ -34,6 +34,47 @@ export function MainConfigCard({
const [shellTools, setShellTools] = useState([]);
const [shellTypes, setShellTypes] = useState([]);
+ const handleServerChange = (value: string) => {
+ if (mainConfig) {
+ setShellToolMap(mainConfig[value]);
+ setShellTools(Object.keys(mainConfig[value]));
+ setShellTypes([]);
+ form.resetField("shellTool");
+ form.resetField("shellType");
+ }
+ };
+
+ const handleShellToolChange = (value: string) => {
+ const resetCommand = () => {
+ form.resetField("commandParamName");
+ };
+
+ const resetGodzilla = () => {
+ form.resetField("godzillaKey");
+ form.resetField("godzillaPass");
+ form.resetField("godzillaHeaderName");
+ form.resetField("godzillaHeaderValue");
+ };
+
+ const resetBehinder = () => {
+ form.resetField("behinderPass");
+ form.resetField("behinderHeaderName");
+ form.resetField("behinderHeaderValue");
+ };
+
+ if (shellToolMap) {
+ setShellTypes(shellToolMap[value]);
+ form.resetField("urlPattern");
+ if (value === "Godzilla") {
+ resetGodzilla();
+ } else if (value === "Behinder") {
+ resetBehinder();
+ } else if (value === "Command") {
+ resetCommand();
+ }
+ }
+ };
+
return (
@@ -54,11 +95,7 @@ export function MainConfigCard({