import { useMemo } from 'react'; import { Button, Divider, Form, Input, InputNumber, Select, Switch } from 'antd'; import { DeleteOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons'; import { RandomUtil } from '@/utils'; import { Protocols } from '@/models/outbound'; interface StreamShape { network?: string; kcp?: { mtu?: number }; finalmask: { tcp?: MaskRow[]; udp?: MaskRow[]; enableQuicParams?: boolean; quicParams?: QuicParams; }; addTcpMask: (type?: string) => void; delTcpMask: (index: number) => void; addUdpMask: (type?: string) => void; delUdpMask: (index: number) => void; } interface MaskRow { type: string; settings: Record; _getDefaultSettings: (type: string, settings: Record) => Record; } interface ItemRow { type: string; packet: string | unknown[]; delay?: number | string; rand?: number | string; randRange?: string; } interface QuicParams { congestion: string; debug?: boolean; brutalUp?: number | string; brutalDown?: number | string; hasUdpHop?: boolean; udpHop?: { ports: string; interval: string | number }; maxIdleTimeout?: number; keepAlivePeriod?: number; disablePathMTUDiscovery?: boolean; maxIncomingStreams?: number; initStreamReceiveWindow?: number; maxStreamReceiveWindow?: number; initConnectionReceiveWindow?: number; maxConnectionReceiveWindow?: number; } interface FinalMaskFormProps { stream: StreamShape; protocol: string; onChange: () => void; } function changeMaskType(mask: MaskRow, type: string) { mask.type = type; mask.settings = mask._getDefaultSettings(type, {}); } function changeItemType(item: ItemRow, type: string) { item.type = type; if (type === 'base64') item.packet = RandomUtil.randomBase64(); else if (type === 'array') { item.rand = 0; item.packet = []; } else item.packet = ''; } function newClientServerItem(): ItemRow { return { delay: 0, rand: 0, randRange: '0-255', type: 'array', packet: [] }; } function newUdpClientServerItem(): ItemRow { return { rand: 0, randRange: '0-255', type: 'array', packet: [] }; } function newNoiseItem(): ItemRow { return { rand: '1-8192', randRange: '0-255', type: 'array', packet: [], delay: '10-20' }; } export default function FinalMaskForm({ stream, protocol, onChange }: FinalMaskFormProps) { const isHysteria = protocol === Protocols.Hysteria || protocol === 'hysteria'; const network = stream?.network || ''; const showTcp = useMemo( () => ['raw', 'tcp', 'httpupgrade', 'ws', 'grpc', 'xhttp'].includes(network), [network], ); const showUdp = isHysteria || network === 'kcp'; const showQuic = isHysteria || network === 'xhttp'; function notify() { onChange(); } function changeUdpMaskType(mask: MaskRow, type: string) { changeMaskType(mask, type); if (network === 'kcp' && stream.kcp) { stream.kcp.mtu = type === 'xdns' ? 900 : 1350; } notify(); } function addUdpMaskWithDefault() { const def = isHysteria ? 'salamander' : 'mkcp-aes128gcm'; stream.addUdpMask(def); notify(); } const tcpMasks = stream.finalmask.tcp || []; const udpMasks = stream.finalmask.udp || []; if (!showTcp && !showUdp && !showQuic) return null; return (
{showTcp && ( <>