refactor: migrate radix-ui to base-ui

This commit is contained in:
ReaJason
2025-12-16 21:55:21 +08:00
parent 0a5cadf64a
commit 28c12102c2
55 changed files with 2117 additions and 3269 deletions
+328 -319
View File
@@ -10,8 +10,8 @@ import {
WaypointsIcon,
ZapIcon,
} from "lucide-react";
import { type JSX, useCallback, useEffect, useState } from "react";
import { FormProvider, type UseFormReturn } from "react-hook-form";
import { type JSX, useCallback, useRef, useState } from "react";
import { Controller, type UseFormReturn, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { AntSwordTabContent } from "@/components/memshell/tabs/antsword-tab";
import { BehinderTabContent } from "@/components/memshell/tabs/behinder-tab";
@@ -22,15 +22,12 @@ import { NeoRegTabContent } from "@/components/memshell/tabs/neoreg-tab";
import { Suo5TabContent } from "@/components/memshell/tabs/suo5-tab";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
FormControl,
FormDescription,
FormField,
FormFieldItem,
FormFieldLabel,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
Field,
FieldContent,
FieldDescription,
FieldError,
FieldLabel,
} from "@/components/ui/field";
import { Label } from "@/components/ui/label";
import {
Select,
@@ -52,6 +49,7 @@ import {
ShellToolType,
} from "@/types/memshell";
import type { MemShellFormSchema } from "@/types/schema";
import { Spinner } from "../ui/spinner";
const shellToolIcons: Record<ShellToolType, JSX.Element> = {
[ShellToolType.Behinder]: <ShieldOffIcon className="h-4 w-4" />,
@@ -67,7 +65,7 @@ const shellToolIcons: Record<ShellToolType, JSX.Element> = {
const defaultServerVersionOptions = [
{
name: "Unknown",
value: "unknown",
value: "Unknown",
},
];
@@ -80,32 +78,21 @@ export default function MainConfigCard({
form: UseFormReturn<MemShellFormSchema>;
servers?: ServerConfig;
}>) {
const { t } = useTranslation(["common", "memshell"]);
const [shellToolMap, setShellToolMap] = useState<{
[toolName: string]: string[];
}>();
const [shellTools, setShellTools] = useState<ShellToolType[]>([
ShellToolType.Godzilla,
ShellToolType.Behinder,
ShellToolType.AntSword,
ShellToolType.Command,
ShellToolType.Suo5,
ShellToolType.NeoreGeorg,
ShellToolType.Custom,
]);
const [shellTools, setShellTools] = useState<ShellToolType[]>([]);
const [shellTypes, setShellTypes] = useState<string[]>([]);
const shellTool = form.watch("shellTool");
const { t } = useTranslation(["common", "memshell"]);
const [serverVersionOptions, setServerVersionOptions] = useState(
defaultServerVersionOptions,
);
// 处理一下 shellTypes 由于 server 或 shellTool 变更时无法正常为 form.shellType 赋值的问题
useEffect(() => {
if (shellTypes.length > 0) {
form.setValue("shellType", shellTypes[0]);
}
}, [shellTypes, form]);
const shellTool = useWatch({
control: form.control,
name: "shellTool",
});
const handleServerChange = useCallback(
(value: string) => {
@@ -132,7 +119,10 @@ export default function MainConfigCard({
}
setShellTypes(currentShellTypes);
// 特殊环境的 JDK 版本
if (currentShellTypes && currentShellTypes.length > 0) {
form.setValue("shellType", currentShellTypes[0]);
}
if (
(value === "SpringWebFlux" || value === "XXLJOB") &&
Number.parseInt(form.getValues("targetJdkVersion") as string, 10) < 52
@@ -142,7 +132,6 @@ export default function MainConfigCard({
form.setValue("targetJdkVersion", "50");
}
// 特殊的服务需要指定版本
if (value === "TongWeb") {
setServerVersionOptions([
...defaultServerVersionOptions,
@@ -169,16 +158,6 @@ export default function MainConfigCard({
[form, mainConfig],
);
// 处理一下默认值 server 不刷新 shellType 的问题
useEffect(() => {
if (mainConfig) {
const initialServer = form.getValues("server");
if (initialServer && mainConfig[initialServer]) {
handleServerChange(initialServer);
}
}
}, [mainConfig, form, handleServerChange]);
const handleShellToolChange = useCallback(
(value: string) => {
const resetCommand = () => {
@@ -229,6 +208,11 @@ export default function MainConfigCard({
}
setShellTypes(currentShellTypes);
// 直接设置 shellType 而不是依赖 useEffect
if (currentShellTypes && currentShellTypes.length > 0) {
form.setValue("shellType", currentShellTypes[0]);
}
form.resetField("urlPattern");
form.resetField("shellClassName");
form.resetField("injectorClassName");
@@ -253,8 +237,17 @@ export default function MainConfigCard({
[form, servers, shellToolMap],
);
const initializedRef = useRef(false);
if (!initializedRef.current && mainConfig) {
const initialServer = form.getValues("server");
if (initialServer && mainConfig[initialServer]) {
handleServerChange(initialServer);
initializedRef.current = true;
}
}
return (
<FormProvider {...form}>
<>
<Card>
<CardHeader className="pb-1">
<CardTitle className="text-md flex items-center gap-2">
@@ -263,284 +256,300 @@ export default function MainConfigCard({
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<FormField
control={form.control}
name="server"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>{t("common:server")}</FormFieldLabel>
<Select
onValueChange={(v) => {
field.onChange(v);
handleServerChange(v);
}}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue
placeholder={t("common:placeholders.select")}
/>
</SelectTrigger>
</FormControl>
<SelectContent>
{Object.keys(servers ?? {}).map((server: string) => (
<SelectItem key={server} value={server}>
{server}
</SelectItem>
))}
</SelectContent>
</Select>
<FormDescription className="flex items-center">
{t("memshell:tips.targetServerNotFound")}&nbsp;
<a
href="https://github.com/ReaJason/MemShellParty/issues/new?template=%E8%AF%B7%E6%B1%82%E9%80%82%E9%85%8D.md"
target="_blank"
rel="noreferrer"
className="flex items-center underline"
{!mainConfig ? (
<div className="flex items-center justify-center p-4 gap-4 h-100">
<Spinner />
<span className="text-sm text-muted-foreground">
{t("loading")}
</span>
</div>
) : (
<div className="flex flex-col gap-2">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<Controller
control={form.control}
name="server"
render={({ field }) => (
<Field>
<FieldContent>
<FieldLabel htmlFor="server">
{t("common:server")}
</FieldLabel>
<Select
onValueChange={(v) => {
field.onChange(v);
handleServerChange(v as string);
}}
value={field.value}
>
<SelectTrigger id="server">
<SelectValue
data-placeholder={t("common:placeholders.select")}
/>
</SelectTrigger>
<SelectContent>
{Object.keys(servers ?? {}).map(
(server: string) => (
<SelectItem key={server} value={server}>
{server}
</SelectItem>
),
)}
</SelectContent>
</Select>
<FieldDescription className="flex items-center">
{t("memshell:tips.targetServerNotFound")}&nbsp;
<a
href="https://github.com/ReaJason/MemShellParty/issues/new?template=%E8%AF%B7%E6%B1%82%E9%80%82%E9%85%8D.md"
target="_blank"
rel="noreferrer"
className="flex items-center underline"
>
{t("memshell:tips.targetServerRequest")}
<ArrowUpRightIcon className="h-4" />
</a>
</FieldDescription>
</FieldContent>
</Field>
)}
/>
<Controller
control={form.control}
name="serverVersion"
render={({ field, fieldState }) => (
<Field
orientation="vertical"
data-invalid={fieldState.invalid}
>
{t("memshell:tips.targetServerRequest")}
<ArrowUpRightIcon className="h-4" />
</a>
</FormDescription>
</FormFieldItem>
)}
/>
<FormField
control={form.control}
name="serverVersion"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>{t("common:serverVersion")}</FormFieldLabel>
<Select onValueChange={field.onChange} value={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue
placeholder={t("common:placeholders.select")}
/>
</SelectTrigger>
</FormControl>
<SelectContent>
{serverVersionOptions.map((v) => (
<SelectItem key={v.value} value={v.value}>
{v.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormFieldItem>
)}
/>
</div>
<div className="grid grid-cols-1">
<FormField
control={form.control}
name="shellTool"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>{t("common:shellTool")}</FormFieldLabel>
<Select
value={field.value}
onValueChange={(v) => handleShellToolChange(v)}
>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{shellTools.map((tool) => (
<SelectItem key={tool} value={tool}>
<span className="flex items-center gap-2">
{shellToolIcons[tool]}
{tool}
</span>
</SelectItem>
))}
</SelectContent>
</Select>
</FormFieldItem>
)}
/>
</div>
<div className="flex gap-4 mt-4 flex-col lg:grid lg:grid-cols-2 2xl:grid 2xl:grid-cols-3">
<FormField
control={form.control}
name="debug"
render={({ field }) => (
<FormItem className="flex items-center space-x-2 space-y-0">
<FormControl>
<Switch
id="debug"
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<div className="flex items-center gap-1">
<FormLabel htmlFor="debug">{t("common:debug")}</FormLabel>
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>{t("common:debug.description")}</p>
</TooltipContent>
</Tooltip>
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="probe"
render={({ field }) => (
<FormItem className="flex items-center space-x-2 space-y-0">
<FormControl>
<Switch
id="probe"
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<div className="flex items-center gap-1">
<Label htmlFor="probe">{t("common:probe")}</Label>
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>{t("common:probe.description")}</p>
</TooltipContent>
</Tooltip>
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="byPassJavaModule"
render={({ field }) => (
<FormItem className="flex items-center space-x-2 space-y-0">
<FormControl>
<Switch
id="bypass"
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<div className="flex items-center gap-1">
<Label htmlFor="bypass">
{t("common:byPassJavaModule")}
</Label>
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>{t("common:byPassJavaModule.description")}</p>
</TooltipContent>
</Tooltip>
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="lambdaSuffix"
render={({ field }) => (
<FormItem className="flex items-center space-x-2 space-y-0">
<FormControl>
<Switch
id="lambdaSuffix"
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<div className="flex items-center gap-1">
<Label htmlFor="lambdaSuffix">
{t("common:lambdaSuffix")}
</Label>
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>{t("common:lambdaSuffix.description")}</p>
</TooltipContent>
</Tooltip>
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="shrink"
render={({ field }) => (
<FormItem className="flex items-center space-x-2 space-y-0">
<FormControl>
<Switch
id="shrink"
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<div className="flex items-center gap-1">
<Label htmlFor="shrink">{t("common:shrink")}</Label>
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>{t("common:shrink.description")}</p>
</TooltipContent>
</Tooltip>
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="staticInitialize"
render={({ field }) => (
<FormItem className="flex items-center space-x-2 space-y-0">
<FormControl>
<Switch
id="staticInitialize"
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<div className="flex items-center gap-1">
<Label htmlFor="staticInitialize">
{t("common:staticInitialize")}
</Label>
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>{t("common:staticInitialize.description")}</p>
</TooltipContent>
</Tooltip>
</div>
</FormItem>
)}
/>
</div>
<FieldContent>
<FieldLabel htmlFor="serverVersion">
{t("common:serverVersion")}
</FieldLabel>
<Select
onValueChange={field.onChange}
value={field.value}
>
<SelectTrigger
id="serverVersion"
aria-invalid={fieldState.invalid}
>
<SelectValue
data-placeholder={t("common:placeholders.select")}
/>
</SelectTrigger>
<SelectContent>
{serverVersionOptions.map((v) => (
<SelectItem key={v.value} value={v.value}>
{v.name}
</SelectItem>
))}
</SelectContent>
</Select>
{fieldState.error && (
<FieldError errors={[fieldState.error]} />
)}
</FieldContent>
</Field>
)}
/>
</div>
<div className="grid grid-cols-1">
<Controller
control={form.control}
name="shellTool"
render={({ field }) => (
<Field>
<FieldContent>
<FieldLabel htmlFor="shellTool">
{t("common:shellTool")}
</FieldLabel>
<Select
value={field.value}
onValueChange={(v) =>
handleShellToolChange(v as string)
}
>
<SelectTrigger id="shellTool">
<SelectValue
data-placeholder={t("common:placeholders.select")}
/>
</SelectTrigger>
<SelectContent>
{shellTools.map((tool) => (
<SelectItem key={tool} value={tool}>
<span className="flex items-center gap-2">
{shellToolIcons[tool]}
{tool}
</span>
</SelectItem>
))}
</SelectContent>
</Select>
</FieldContent>
</Field>
)}
/>
</div>
<div className="flex gap-4 mt-4 flex-col lg:grid lg:grid-cols-2 2xl:grid 2xl:grid-cols-3">
<Controller
control={form.control}
name="debug"
render={({ field }) => (
<div className="flex items-center gap-2">
<Switch
id="debug"
checked={field.value}
onCheckedChange={field.onChange}
/>
<Label htmlFor="debug">{t("common:debug")}</Label>
<Tooltip>
<TooltipTrigger>
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>{t("common:debug.description")}</p>
</TooltipContent>
</Tooltip>
</div>
)}
/>
<Controller
control={form.control}
name="probe"
render={({ field }) => (
<div className="flex items-center gap-2">
<Switch
id="probe"
checked={field.value}
onCheckedChange={field.onChange}
/>
<Label htmlFor="probe">{t("common:probe")}</Label>
<Tooltip>
<TooltipTrigger>
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>{t("common:probe.description")}</p>
</TooltipContent>
</Tooltip>
</div>
)}
/>
<Controller
control={form.control}
name="byPassJavaModule"
render={({ field }) => (
<div className="flex items-center gap-2">
<Switch
id="bypass"
checked={field.value}
onCheckedChange={field.onChange}
/>
<Label htmlFor="bypass">
{t("common:byPassJavaModule")}
</Label>
<Tooltip>
<TooltipTrigger>
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>{t("common:byPassJavaModule.description")}</p>
</TooltipContent>
</Tooltip>
</div>
)}
/>
<Controller
control={form.control}
name="lambdaSuffix"
render={({ field }) => (
<div className="flex items-center gap-2">
<Switch
id="lambdaSuffix"
checked={field.value}
onCheckedChange={field.onChange}
/>
<Label htmlFor="lambdaSuffix">
{t("common:lambdaSuffix")}
</Label>
<Tooltip>
<TooltipTrigger>
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>{t("common:lambdaSuffix.description")}</p>
</TooltipContent>
</Tooltip>
</div>
)}
/>
<Controller
control={form.control}
name="shrink"
render={({ field }) => (
<div className="flex items-center gap-2">
<Switch
id="shrink"
checked={field.value}
onCheckedChange={field.onChange}
/>
<Label htmlFor="shrink">{t("common:shrink")}</Label>
<Tooltip>
<TooltipTrigger>
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>{t("common:shrink.description")}</p>
</TooltipContent>
</Tooltip>
</div>
)}
/>
<Controller
control={form.control}
name="staticInitialize"
render={({ field }) => (
<div className="flex items-center gap-2">
<Switch
id="staticInitialize"
checked={field.value}
onCheckedChange={field.onChange}
/>
<Label htmlFor="staticInitialize">
{t("common:staticInitialize")}
</Label>
<Tooltip>
<TooltipTrigger>
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>{t("common:staticInitialize.description")}</p>
</TooltipContent>
</Tooltip>
</div>
)}
/>
</div>
</div>
)}
</CardContent>
</Card>
<Tabs value={shellTool} className="w-full">
<GodzillaTabContent form={form} shellTypes={shellTypes} />
<CommandTabContent form={form} shellTypes={shellTypes} />
<BehinderTabContent form={form} shellTypes={shellTypes} />
<AntSwordTabContent form={form} shellTypes={shellTypes} />
<Suo5TabContent tabValue="Suo5" form={form} shellTypes={shellTypes} />
<Suo5TabContent tabValue="Suo5v2" form={form} shellTypes={shellTypes} />
<NeoRegTabContent form={form} shellTypes={shellTypes} />
<CustomTabContent form={form} shellTypes={shellTypes} />
</Tabs>
</FormProvider>
{mainConfig && (
<Tabs value={shellTool} className="w-full">
<GodzillaTabContent form={form} shellTypes={shellTypes} />
<CommandTabContent form={form} shellTypes={shellTypes} />
<BehinderTabContent form={form} shellTypes={shellTypes} />
<AntSwordTabContent form={form} shellTypes={shellTypes} />
<Suo5TabContent tabValue="Suo5" form={form} shellTypes={shellTypes} />
<Suo5TabContent
tabValue="Suo5v2"
form={form}
shellTypes={shellTypes}
/>
<NeoRegTabContent form={form} shellTypes={shellTypes} />
<CustomTabContent form={form} shellTypes={shellTypes} />
</Tabs>
)}
</>
);
}
@@ -1,23 +1,14 @@
import { PackageIcon } from "lucide-react";
import { useEffect, useState } from "react";
import { FormProvider, type UseFormReturn } from "react-hook-form";
import { useMemo } from "react";
import { Controller, type UseFormReturn, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
FormControl,
FormField,
FormItem,
FormLabel,
} from "@/components/ui/form";
import { FieldLabel, FieldSet } from "@/components/ui/field";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Spinner } from "@/components/ui/spinner";
import type { PackerConfig } from "@/types/memshell";
import type { MemShellFormSchema } from "@/types/schema";
type Option = {
name: string;
value: string;
};
export default function PackageConfigCard({
packerConfig,
form,
@@ -25,13 +16,19 @@ export default function PackageConfigCard({
packerConfig: PackerConfig | undefined;
form: UseFormReturn<MemShellFormSchema>;
}>) {
const [options, setOptions] = useState<Array<Option>>([]);
const shellType = form.watch("shellType");
const server = form.watch("server");
const { t } = useTranslation("common");
useEffect(() => {
const shellType = useWatch({
control: form.control,
name: "shellType",
});
const server = useWatch({
control: form.control,
name: "server",
});
const options = useMemo(() => {
const filteredOptions = (packerConfig ?? []).filter((name) => {
if (!shellType || shellType === " ") {
return true;
@@ -44,23 +41,12 @@ export default function PackageConfigCard({
}
return !name.startsWith("Agent") && !name.toLowerCase().startsWith("xxl");
});
const mappedOptions = filteredOptions.map((name) => {
return {
name: t(name),
value: name,
};
});
setOptions(mappedOptions);
const currentValue = form.getValues("packingMethod");
if (
filteredOptions.length > 0 &&
(!currentValue || !filteredOptions.includes(currentValue))
) {
form.setValue("packingMethod", filteredOptions[0]);
}
}, [form, packerConfig, server, shellType, t]);
form.setValue("packingMethod", filteredOptions[0]);
return filteredOptions.map((name) => ({
name: t(name),
value: name,
}));
}, [packerConfig, shellType, server, t, form]);
return (
<Card className="w-full">
@@ -72,41 +58,34 @@ export default function PackageConfigCard({
</CardHeader>
<CardContent>
{options.length > 0 ? (
<FormProvider {...form}>
<FormField
control={form.control}
name="packingMethod"
render={({ field }) => (
<FormItem className="space-y-3">
<FormLabel>{t("packerMethod")}</FormLabel>
<FormControl>
<RadioGroup
onValueChange={field.onChange}
value={field.value}
className="grid grid-cols-2 md:grid-cols-3"
>
{options.map(({ name, value }) => (
<FormItem
key={value}
className="flex items-center space-x-3 space-y-0"
>
<FormControl>
<RadioGroupItem value={value} id={value} />
</FormControl>
<FormLabel className="text-xs" htmlFor={value}>
{name}
</FormLabel>
</FormItem>
))}
</RadioGroup>
</FormControl>
</FormItem>
)}
/>
</FormProvider>
<Controller
control={form.control}
name="packingMethod"
render={({ field }) => (
<FieldSet>
<FieldLabel>{t("packerMethod")}</FieldLabel>
<RadioGroup
name={field.name}
value={field.value}
defaultValue={options[0].value}
onValueChange={field.onChange}
className="grid grid-cols-2 md:grid-cols-3"
>
{options.map(({ name, value }) => (
<div key={value} className="flex items-center space-x-3">
<RadioGroupItem value={value} id={value} />
<FieldLabel className="text-xs" htmlFor={value}>
{name}
</FieldLabel>
</div>
))}
</RadioGroup>
</FieldSet>
)}
/>
) : (
<div className="flex items-center justify-center p-4 space-x-2">
<div className="h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" />
<div className="flex items-center justify-center p-4 gap-4 h-50">
<Spinner />
<span className="text-sm text-muted-foreground">
{t("loading")}
</span>
+2 -2
View File
@@ -7,13 +7,13 @@ export function QuickUsage() {
return (
<Card>
<CardHeader>
<CardTitle className="text-md flex items-center gap-2">
<CardTitle className="flex items-center gap-2">
<ScrollTextIcon className="h-5" />
<span>{t("common:quickUsage.title")}</span>
</CardTitle>
</CardHeader>
<CardContent>
<ol className="list-decimal list-inside space-y-4 text-sm">
<ol className="flex flex-col gap-4 list-decimal list-inside text-sm">
<li>{t("memshell:quickUsage.step1")}</li>
<li>{t("memshell:quickUsage.step2")}</li>
<li>{t("memshell:quickUsage.step3")}</li>
@@ -79,7 +79,7 @@ export function MultiPackResult({
<div className="flex items-center justify-between text-xs gap-2">
<Select
onValueChange={(value) => {
setSelectedMethod(value);
setSelectedMethod(value as string);
setPackResult(
allPackResults?.[value as keyof typeof allPackResults] ?? "",
);
@@ -90,7 +90,7 @@ export function MultiPackResult({
<span className="text-muted-foreground">
{t("common:packerMethod")}:&nbsp;
</span>
<SelectValue />
<SelectValue data-placeholder={t("common:placeholders.select")} />
</SelectTrigger>
<SelectContent>
{packMethods.map((method) => (
@@ -1,12 +1,7 @@
import { FormProvider, type UseFormReturn } from "react-hook-form";
import { Controller, type UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { Card, CardContent } from "@/components/ui/card";
import {
FormControl,
FormField,
FormFieldItem,
FormFieldLabel,
} from "@/components/ui/form";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { TabsContent } from "@/components/ui/tabs";
import type { MemShellFormSchema } from "@/types/schema";
@@ -23,65 +18,61 @@ export function AntSwordTabContent({
}>) {
const { t } = useTranslation(["memshell", "common"]);
return (
<FormProvider {...form}>
<TabsContent value="AntSword">
<Card>
<CardContent className="space-y-2 mt-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<ShellTypeFormField form={form} shellTypes={shellTypes} />
<UrlPatternFormField form={form} />
</div>
<FormField
<TabsContent value="AntSword">
<Card>
<CardContent className="space-y-2 mt-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<ShellTypeFormField form={form} shellTypes={shellTypes} />
<UrlPatternFormField form={form} />
</div>
<Controller
control={form.control}
name="antSwordPass"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel>
{t("shellToolConfig.antSword.pass")} {t("common:optional")}
</FieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</Field>
)}
/>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<Controller
control={form.control}
name="antSwordPass"
name="headerName"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>
{t("shellToolConfig.antSword.pass")} {t("common:optional")}
</FormFieldLabel>
<Field className="gap-1">
<FieldLabel>{t("common:headerName")}</FieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormFieldItem>
</Field>
)}
/>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<FormField
control={form.control}
name="headerName"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>{t("common:headerName")}</FormFieldLabel>
<FormControl>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormControl>
</FormFieldItem>
)}
/>
<FormField
control={form.control}
name="headerValue"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>
{t("common:headerValue")} {t("common:optional")}
</FormFieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormFieldItem>
)}
/>
</div>
<OptionalClassFormField form={form} />
</CardContent>
</Card>
</TabsContent>
</FormProvider>
<Controller
control={form.control}
name="headerValue"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel>
{t("common:headerValue")} {t("common:optional")}
</FieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</Field>
)}
/>
</div>
<OptionalClassFormField form={form} />
</CardContent>
</Card>
</TabsContent>
);
}
@@ -1,7 +1,7 @@
import { FormProvider, type UseFormReturn } from "react-hook-form";
import { Controller, type UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { Card, CardContent } from "@/components/ui/card";
import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { TabsContent } from "@/components/ui/tabs";
import type { MemShellFormSchema } from "@/types/schema";
@@ -18,63 +18,61 @@ export function BehinderTabContent({
}>) {
const { t } = useTranslation(["memshell", "common"]);
return (
<FormProvider {...form}>
<TabsContent value="Behinder">
<Card>
<CardContent className="space-y-2 mt-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<ShellTypeFormField form={form} shellTypes={shellTypes} />
<UrlPatternFormField form={form} />
</div>
<FormField
<TabsContent value="Behinder">
<Card>
<CardContent className="space-y-2 mt-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<ShellTypeFormField form={form} shellTypes={shellTypes} />
<UrlPatternFormField form={form} />
</div>
<Controller
control={form.control}
name="behinderPass"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel>
{t("shellToolConfig.behinder.pass")} {t("common:optional")}
</FieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</Field>
)}
/>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<Controller
control={form.control}
name="behinderPass"
name="headerName"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>
{t("shellToolConfig.behinder.pass")} {t("common:optional")}
</FormFieldLabel>
<Field className="gap-1">
<FieldLabel>{t("common:headerName")}</FieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormFieldItem>
</Field>
)}
/>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<FormField
control={form.control}
name="headerName"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>{t("common:headerName")}</FormFieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormFieldItem>
)}
/>
<FormField
control={form.control}
name="headerValue"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>
{t("common:headerValue")} {t("common:optional")}
</FormFieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormFieldItem>
)}
/>
</div>
<OptionalClassFormField form={form} />
</CardContent>
</Card>
</TabsContent>
</FormProvider>
<Controller
control={form.control}
name="headerValue"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel>
{t("common:headerValue")} {t("common:optional")}
</FieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</Field>
)}
/>
</div>
<OptionalClassFormField form={form} />
</CardContent>
</Card>
</TabsContent>
);
}
@@ -1,8 +1,8 @@
import { Shuffle } from "lucide-react";
import { Fragment, useEffect, useState } from "react";
import { FormProvider, type UseFormReturn } from "react-hook-form";
import { Controller, type UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { MemShellFormSchema } from "@/types/schema";
@@ -72,44 +72,43 @@ export function OptionalClassFormField({
onCheckedChange={handleToggleRandomClass}
/>
</div>
<FormProvider {...form}>
{!useRandomClassName && (
<FormField
control={form.control}
name="shellClassName"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel htmlFor="shellClassName">
{t("mainConfig.shellClassName")} {t("common:optional")}
</FormFieldLabel>
<Input
id="shellClassName"
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormFieldItem>
)}
/>
)}
{!useRandomClassName && (
<FormField
control={form.control}
name="injectorClassName"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel htmlFor="injectClassName">
{t("mainConfig.injectorClassName")} {t("common:optional")}
</FormFieldLabel>
<Input
id="injectClassName"
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormFieldItem>
)}
/>
)}
</FormProvider>
{!useRandomClassName && (
<Controller
control={form.control}
name="shellClassName"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel htmlFor="shellClassName">
{t("mainConfig.shellClassName")} {t("common:optional")}
</FieldLabel>
<Input
id="shellClassName"
{...field}
placeholder={t("common:placeholders.input")}
/>
</Field>
)}
/>
)}
{!useRandomClassName && (
<Controller
control={form.control}
name="injectorClassName"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel htmlFor="injectClassName">
{t("mainConfig.injectorClassName")} {t("common:optional")}
</FieldLabel>
<Input
id="injectClassName"
{...field}
placeholder={t("common:placeholders.input")}
/>
</Field>
)}
/>
)}
</Fragment>
);
}
+120 -137
View File
@@ -1,7 +1,7 @@
import { useQuery } from "@tanstack/react-query";
import { ChevronDown, ChevronRight, InfoIcon } from "lucide-react";
import { useState } from "react";
import { FormProvider, type UseFormReturn } from "react-hook-form";
import { Controller, type UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { Card, CardContent } from "@/components/ui/card";
import {
@@ -9,12 +9,7 @@ import {
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import {
FormControl,
FormField,
FormFieldItem,
FormFieldLabel,
} from "@/components/ui/form";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import {
Select,
@@ -56,141 +51,129 @@ export function CommandTabContent({
});
return (
<FormProvider {...form}>
<TabsContent value="Command">
<Card>
<CardContent className="space-y-2 mt-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<ShellTypeFormField form={form} shellTypes={shellTypes} />
<UrlPatternFormField form={form} />
</div>
<FormField
control={form.control}
name="commandParamName"
render={({ field }) => (
<FormFieldItem>
<div className="flex items-center gap-1">
<FormFieldLabel>
{t("common:paramName")} {t("common:optional")}
</FormFieldLabel>
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>{t("common:paramName.description")}</p>
</TooltipContent>
</Tooltip>
</div>
<FormControl>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormControl>
</FormFieldItem>
)}
/>
<Collapsible open={isAdvancedOpen} onOpenChange={setIsAdvancedOpen}>
<CollapsibleTrigger className="flex items-center gap-2 w-full py-2 text-sm font-medium hover:underline">
{isAdvancedOpen ? (
<ChevronDown className="h-4 w-4" />
) : (
<ChevronRight className="h-4 w-4" />
)}
{t("common:advancedConfig")}
</CollapsibleTrigger>
<CollapsibleContent className="space-y-2 pt-2">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<FormField
control={form.control}
name="encryptor"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>{t("common:encryptor")}</FormFieldLabel>
<Select
onValueChange={field.onChange}
value={field.value}
defaultValue="RAW"
>
<FormControl>
<SelectTrigger>
<SelectValue
placeholder={t("common:placeholders.select")}
/>
</SelectTrigger>
</FormControl>
<SelectContent>
{data?.encryptors?.map((v) => (
<SelectItem key={v} value={v}>
{v}
</SelectItem>
))}
</SelectContent>
</Select>
</FormFieldItem>
)}
/>
<FormField
control={form.control}
name="implementationClass"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>
{t("common:implementationClass")}
</FormFieldLabel>
<Select
onValueChange={field.onChange}
value={field.value}
defaultValue="RuntimeExec"
>
<FormControl>
<SelectTrigger>
<SelectValue
placeholder={t("common:placeholders.select")}
/>
</SelectTrigger>
</FormControl>
<SelectContent>
{data?.implementationClasses?.map((v) => (
<SelectItem key={v} value={v}>
{v}
</SelectItem>
))}
</SelectContent>
</Select>
</FormFieldItem>
)}
/>
<TabsContent value="Command">
<Card>
<CardContent className="space-y-2 mt-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<ShellTypeFormField form={form} shellTypes={shellTypes} />
<UrlPatternFormField form={form} />
</div>
<Controller
control={form.control}
name="commandParamName"
render={({ field }) => (
<Field className="gap-1">
<div className="flex items-center gap-1">
<FieldLabel>
{t("common:paramName")} {t("common:optional")}
</FieldLabel>
<Tooltip>
<TooltipTrigger>
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>{t("common:paramName.description")}</p>
</TooltipContent>
</Tooltip>
</div>
<FormField
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</Field>
)}
/>
<Collapsible open={isAdvancedOpen} onOpenChange={setIsAdvancedOpen}>
<CollapsibleTrigger className="flex items-center gap-2 w-full py-2 text-sm font-medium hover:underline">
{isAdvancedOpen ? (
<ChevronDown className="h-4 w-4" />
) : (
<ChevronRight className="h-4 w-4" />
)}
{t("common:advancedConfig")}
</CollapsibleTrigger>
<CollapsibleContent className="space-y-2 pt-2">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<Controller
control={form.control}
name="commandTemplate"
name="encryptor"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>
{t("common:commandTemplate")} {t("common:optional")}
</FormFieldLabel>
<FormControl>
<Input
{...field}
placeholder={t("common:commandTemplate.placeholder")}
/>
</FormControl>
<p className="text-xs text-muted-foreground mt-1">
{t("common:commandTemplate.description")}
</p>
</FormFieldItem>
<Field className="gap-1">
<FieldLabel>{t("common:encryptor")}</FieldLabel>
<Select
onValueChange={field.onChange}
value={field.value}
defaultValue="RAW"
>
<SelectTrigger>
<SelectValue
data-placeholder={t("common:placeholders.select")}
/>
</SelectTrigger>
<SelectContent>
{data?.encryptors?.map((v) => (
<SelectItem key={v} value={v}>
{v}
</SelectItem>
))}
</SelectContent>
</Select>
</Field>
)}
/>
</CollapsibleContent>
</Collapsible>
<Controller
control={form.control}
name="implementationClass"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel>{t("common:implementationClass")}</FieldLabel>
<Select
onValueChange={field.onChange}
value={field.value}
defaultValue="RuntimeExec"
>
<SelectTrigger>
<SelectValue
data-placeholder={t("common:placeholders.select")}
/>
</SelectTrigger>
<SelectContent>
{data?.implementationClasses?.map((v) => (
<SelectItem key={v} value={v}>
{v}
</SelectItem>
))}
</SelectContent>
</Select>
</Field>
)}
/>
</div>
<Controller
control={form.control}
name="commandTemplate"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel>
{t("common:commandTemplate")} {t("common:optional")}
</FieldLabel>
<Input
{...field}
placeholder={t("common:commandTemplate.placeholder")}
/>
<p className="text-xs text-muted-foreground mt-1">
{t("common:commandTemplate.description")}
</p>
</Field>
)}
/>
</CollapsibleContent>
</Collapsible>
<OptionalClassFormField form={form} />
</CardContent>
</Card>
</TabsContent>
</FormProvider>
<OptionalClassFormField form={form} />
</CardContent>
</Card>
</TabsContent>
);
}
+68 -77
View File
@@ -1,15 +1,9 @@
import { useEffect, useRef, useState } from "react";
import { FormProvider, type UseFormReturn } from "react-hook-form";
import { Controller, type UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
import { Card, CardContent } from "@/components/ui/card";
import {
FormControl,
FormField,
FormFieldItem,
FormFieldLabel,
FormMessage,
} from "@/components/ui/form";
import { Field, FieldError, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
@@ -90,74 +84,71 @@ export default function CustomTabContent({
}, [classNameEndpoint, form, shellClassBase64, t]);
return (
<FormProvider {...form}>
<TabsContent value="Custom">
<Card>
<CardContent className="space-y-2 mt-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<ShellTypeFormField form={form} shellTypes={shellTypes} />
<UrlPatternFormField form={form} />
</div>
<FormField
control={form.control}
name="shellClassBase64"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>{t("shellClass")}</FormFieldLabel>
<RadioGroup
value={isFile ? "file" : "base64"}
onValueChange={(value) => {
field.onChange("");
setIsFile(value === "file");
}}
className="flex items-center space-x-2"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="base64" id="optionOne" />
<Label htmlFor="optionOne">Base64</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="file" id="optionTwo" />
<Label htmlFor="optionTwo">File</Label>
</div>
</RadioGroup>
<FormControl className="mt-2">
{isFile ? (
<Input
onChange={(e) => {
const file = e.target.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = (event) => {
const base64String =
(event.target?.result as string)?.split(
",",
)[1] || "";
field.onChange(base64String);
};
reader.readAsDataURL(file);
}
}}
accept=".class"
placeholder={t("common:placeholders.input")}
type="file"
/>
) : (
<Textarea
{...field}
placeholder={t("common:placeholders.input")}
className="h-24"
/>
)}
</FormControl>
<FormMessage />
</FormFieldItem>
)}
/>
<OptionalClassFormField form={form} />
</CardContent>
</Card>
</TabsContent>
</FormProvider>
<TabsContent value="Custom">
<Card>
<CardContent className="space-y-2 mt-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<ShellTypeFormField form={form} shellTypes={shellTypes} />
<UrlPatternFormField form={form} />
</div>
<Controller
control={form.control}
name="shellClassBase64"
render={({ field, fieldState }) => (
<Field className="gap-1">
<FieldLabel>{t("shellClass")}</FieldLabel>
<RadioGroup
value={isFile ? "file" : "base64"}
onValueChange={(value) => {
field.onChange("");
setIsFile(value === "file");
}}
className="flex items-center"
>
<div className="flex items-center">
<RadioGroupItem value="base64" id="optionOne" />
<Label htmlFor="optionOne">Base64</Label>
</div>
<div className="flex items-center">
<RadioGroupItem value="file" id="optionTwo" />
<Label htmlFor="optionTwo">File</Label>
</div>
</RadioGroup>
<div className="mt-2">
{isFile ? (
<Input
onChange={(e) => {
const file = e.target.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = (event) => {
const base64String =
(event.target?.result as string)?.split(",")[1] ||
"";
field.onChange(base64String);
};
reader.readAsDataURL(file);
}
}}
accept=".class"
placeholder={t("common:placeholders.input")}
type="file"
/>
) : (
<Textarea
{...field}
placeholder={t("common:placeholders.input")}
className="h-24"
/>
)}
</div>
{fieldState.error && <FieldError errors={[fieldState.error]} />}
</Field>
)}
/>
<OptionalClassFormField form={form} />
</CardContent>
</Card>
</TabsContent>
);
}
@@ -1,7 +1,7 @@
import { FormProvider, type UseFormReturn } from "react-hook-form";
import { Controller, type UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { Card, CardContent } from "@/components/ui/card";
import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { TabsContent } from "@/components/ui/tabs";
import type { MemShellFormSchema } from "@/types/schema";
@@ -18,79 +18,76 @@ export function GodzillaTabContent({
}>) {
const { t } = useTranslation(["memshell", "common"]);
return (
<FormProvider {...form}>
<TabsContent value="Godzilla">
<Card>
<CardContent className="space-y-2 mt-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<ShellTypeFormField form={form} shellTypes={shellTypes} />
<UrlPatternFormField form={form} />
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<FormField
control={form.control}
name="godzillaPass"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>
{t("shellToolConfig.godzilla.pass")}{" "}
{t("common:optional")}
</FormFieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormFieldItem>
)}
/>
<FormField
control={form.control}
name="godzillaKey"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>
{t("shellToolConfig.godzilla.key")} {t("common:optional")}
</FormFieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormFieldItem>
)}
/>
<FormField
control={form.control}
name="headerName"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>{t("common:headerName")}</FormFieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormFieldItem>
)}
/>
<FormField
control={form.control}
name="headerValue"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>
{t("common:headerValue")} {t("common:optional")}
</FormFieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormFieldItem>
)}
/>
</div>
<OptionalClassFormField form={form} />
</CardContent>
</Card>
</TabsContent>
</FormProvider>
<TabsContent value="Godzilla">
<Card>
<CardContent className="flex flex-col gap-2 mt-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<ShellTypeFormField form={form} shellTypes={shellTypes} />
<UrlPatternFormField form={form} />
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<Controller
control={form.control}
name="godzillaPass"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel>
{t("shellToolConfig.godzilla.pass")} {t("common:optional")}
</FieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</Field>
)}
/>
<Controller
control={form.control}
name="godzillaKey"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel>
{t("shellToolConfig.godzilla.key")} {t("common:optional")}
</FieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</Field>
)}
/>
<Controller
control={form.control}
name="headerName"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel>{t("common:headerName")}</FieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</Field>
)}
/>
<Controller
control={form.control}
name="headerValue"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel>
{t("common:headerValue")} {t("common:optional")}
</FieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</Field>
)}
/>
</div>
<OptionalClassFormField form={form} />
</CardContent>
</Card>
</TabsContent>
);
}
+43 -45
View File
@@ -1,7 +1,7 @@
import { FormProvider, type UseFormReturn } from "react-hook-form";
import { Controller, type UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { Card, CardContent } from "@/components/ui/card";
import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { TabsContent } from "@/components/ui/tabs";
import type { MemShellFormSchema } from "@/types/schema";
@@ -18,48 +18,46 @@ export function NeoRegTabContent({
}>) {
const { t } = useTranslation(["memshell", "common"]);
return (
<FormProvider {...form}>
<TabsContent value="NeoreGeorg">
<Card>
<CardContent className="space-y-2 mt-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<ShellTypeFormField form={form} shellTypes={shellTypes} />
<UrlPatternFormField form={form} />
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<FormField
control={form.control}
name="headerName"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>{t("common:headerName")}</FormFieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormFieldItem>
)}
/>
<FormField
control={form.control}
name="headerValue"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>
{t("common:headerValue")} {t("common:optional")}
</FormFieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormFieldItem>
)}
/>
</div>
<OptionalClassFormField form={form} />
</CardContent>
</Card>
</TabsContent>
</FormProvider>
<TabsContent value="NeoreGeorg">
<Card>
<CardContent className="space-y-2 mt-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<ShellTypeFormField form={form} shellTypes={shellTypes} />
<UrlPatternFormField form={form} />
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<Controller
control={form.control}
name="headerName"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel>{t("common:headerName")}</FieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</Field>
)}
/>
<Controller
control={form.control}
name="headerValue"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel>
{t("common:headerValue")} {t("common:optional")}
</FieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</Field>
)}
/>
</div>
<OptionalClassFormField form={form} />
</CardContent>
</Card>
</TabsContent>
);
}
@@ -1,11 +1,6 @@
import { FormProvider, type UseFormReturn } from "react-hook-form";
import { Controller, type UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
import {
FormControl,
FormField,
FormFieldItem,
FormFieldLabel,
} from "@/components/ui/form";
import { Field, FieldLabel } from "@/components/ui/field";
import {
Select,
SelectContent,
@@ -24,42 +19,38 @@ export function ShellTypeFormField({
}>) {
const { t } = useTranslation(["memshell", "common"]);
return (
<FormProvider {...form}>
<FormField
control={form.control}
name="shellType"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>{t("mainConfig.shellMountType")}</FormFieldLabel>
<Select
onValueChange={(e) => {
form.resetField("urlPattern");
field.onChange(e);
}}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={t("common:placeholders.select")} />
</SelectTrigger>
</FormControl>
<SelectContent key={shellTypes?.join(",")}>
{shellTypes?.length ? (
shellTypes.map((v) => (
<SelectItem key={v} value={v}>
{v}
</SelectItem>
))
) : (
<SelectItem value=" ">
{t("tips.shellToolNotSelected")}
<Controller
control={form.control}
name="shellType"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel>{t("mainConfig.shellMountType")}</FieldLabel>
<Select
onValueChange={(e) => {
form.resetField("urlPattern");
field.onChange(e);
}}
value={field.value}
>
<SelectTrigger>
<SelectValue data-placeholder={t("common:placeholders.select")} />
</SelectTrigger>
<SelectContent key={shellTypes?.join(",")}>
{shellTypes?.length ? (
shellTypes.map((v) => (
<SelectItem key={v} value={v}>
{v}
</SelectItem>
)}
</SelectContent>
</Select>
</FormFieldItem>
)}
/>
</FormProvider>
))
) : (
<SelectItem value=" ">
{t("tips.shellToolNotSelected")}
</SelectItem>
)}
</SelectContent>
</Select>
</Field>
)}
/>
);
}
+43 -45
View File
@@ -1,7 +1,7 @@
import { FormProvider, type UseFormReturn } from "react-hook-form";
import { Controller, type UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { Card, CardContent } from "@/components/ui/card";
import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { TabsContent } from "@/components/ui/tabs";
import type { MemShellFormSchema } from "@/types/schema";
@@ -20,48 +20,46 @@ export function Suo5TabContent({
}>) {
const { t } = useTranslation(["memshell", "common"]);
return (
<FormProvider {...form}>
<TabsContent value={tabValue}>
<Card>
<CardContent className="space-y-2 mt-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<ShellTypeFormField form={form} shellTypes={shellTypes} />
<UrlPatternFormField form={form} />
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<FormField
control={form.control}
name="headerName"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>{t("common:headerName")}</FormFieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormFieldItem>
)}
/>
<FormField
control={form.control}
name="headerValue"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>
{t("common:headerValue")} {t("common:optional")}
</FormFieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</FormFieldItem>
)}
/>
</div>
<OptionalClassFormField form={form} />
</CardContent>
</Card>
</TabsContent>
</FormProvider>
<TabsContent value={tabValue}>
<Card>
<CardContent className="space-y-2 mt-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<ShellTypeFormField form={form} shellTypes={shellTypes} />
<UrlPatternFormField form={form} />
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<Controller
control={form.control}
name="headerName"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel>{t("common:headerName")}</FieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</Field>
)}
/>
<Controller
control={form.control}
name="headerValue"
render={({ field }) => (
<Field className="gap-1">
<FieldLabel>
{t("common:headerValue")} {t("common:optional")}
</FieldLabel>
<Input
{...field}
placeholder={t("common:placeholders.input")}
/>
</Field>
)}
/>
</div>
<OptionalClassFormField form={form} />
</CardContent>
</Card>
</TabsContent>
);
}
@@ -1,13 +1,8 @@
import { FormProvider, type UseFormReturn } from "react-hook-form";
import { Controller, type UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
import {
FormField,
FormFieldItem,
FormFieldLabel,
FormMessage,
} from "@/components/ui/form";
import { Field, FieldError, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { shouldHidden } from "@/lib/utils";
import { cn, shouldHidden } from "@/lib/utils";
import type { MemShellFormSchema } from "@/types/schema";
export function UrlPatternFormField({
@@ -16,20 +11,19 @@ export function UrlPatternFormField({
const { t } = useTranslation("common");
const shellType = form.watch("shellType");
return (
<FormProvider {...form}>
<FormField
control={form.control}
name="urlPattern"
render={({ field }) => (
<FormFieldItem
className={shouldHidden(shellType) ? "hidden" : "grid"}
>
<FormFieldLabel>{t("urlPattern")}</FormFieldLabel>
<Input {...field} placeholder={t("placeholders.input")} />
<FormMessage />
</FormFieldItem>
)}
/>
</FormProvider>
<Controller
control={form.control}
name="urlPattern"
render={({ field, fieldState }) => (
<Field
className={cn("gap-1", shouldHidden(shellType) ? "hidden" : "grid")}
data-invalid={fieldState.invalid}
>
<FieldLabel>{t("urlPattern")}</FieldLabel>
<Input {...field} placeholder={t("placeholders.input")} />
{fieldState.error && <FieldError errors={[fieldState.error]} />}
</Field>
)}
/>
);
}