feat(ui): allow custom fragment packets ranges, not just presets (#5075)

The fragment "packets" field was a locked dropdown (tlshello / 1-3 / 1-5)
in both the finalmask TCP-mask form and the Freedom outbound form, while
xray-core accepts any "n-m" packet range. Replace both with an
AutoComplete that keeps the presets as suggestions and validates free
input as "tlshello" or a numeric range.
This commit is contained in:
MHSanaei
2026-06-12 09:04:17 +02:00
parent 0e0e41197f
commit bade1fcef6
2 changed files with 33 additions and 6 deletions
@@ -1,4 +1,4 @@
import { Button, Divider, Form, Input, InputNumber, Select, Space, Switch } from 'antd';
import { AutoComplete, Button, Divider, Form, Input, InputNumber, Select, Space, Switch } from 'antd';
import { DeleteOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons';
import type { FormInstance } from 'antd/es/form';
import type { NamePath } from 'antd/es/form/interface';
@@ -205,13 +205,18 @@ function TcpMaskItem({
if (type === 'fragment') {
return (
<>
<Form.Item label="Packets" name={[fieldName, 'settings', 'packets']}>
<Select
<Form.Item
label="Packets"
name={[fieldName, 'settings', 'packets']}
rules={[{ validator: validateFragmentPackets }]}
>
<AutoComplete
options={[
{ value: 'tlshello', label: 'tlshello' },
{ value: '1-3', label: '1-3' },
{ value: '1-5', label: '1-5' },
]}
placeholder="tlshello or n-m, e.g. 1-3"
/>
</Form.Item>
<Form.Item
@@ -264,6 +269,16 @@ function TcpMaskItem({
);
}
// xray's fragment `packets` accepts "tlshello" or an arbitrary packet-number
// range like "1-3" (#5075 — presets only covered the common cases).
function validateFragmentPackets(_rule: unknown, value: unknown): Promise<void> {
const str = typeof value === 'string' ? value.trim() : String(value ?? '').trim();
if (str.length === 0 || str === 'tlshello' || /^\d+-\d+$/.test(str)) {
return Promise.resolve();
}
return Promise.reject(new Error('Use "tlshello" or a packet range like 1-3'));
}
// Walks a deep object path safely. Used inside shouldUpdate which gets
// the whole form values blob; we need to compare a deep field across
// prev/curr without crashing on missing intermediates.
@@ -1,5 +1,5 @@
import { useTranslation } from 'react-i18next';
import { Button, Form, Input, InputNumber, Select, Switch, type FormInstance } from 'antd';
import { AutoComplete, Button, Form, Input, InputNumber, Select, Switch, type FormInstance } from 'antd';
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import { OutboundDomainStrategies } from '@/schemas/primitives';
@@ -67,12 +67,24 @@ export default function FreedomFields({ form }: { form: FormInstance<OutboundFor
<Form.Item
label={t('pages.settings.subFormats.packets')}
name={['settings', 'fragment', 'packets']}
rules={[{
validator: (_rule, value) => {
const str = String(value ?? '').trim();
// xray accepts "tlshello" or any packet-number range (#5075)
if (str === '' || str === 'tlshello' || /^\d+-\d+$/.test(str)) {
return Promise.resolve();
}
return Promise.reject(new Error('Use "tlshello" or a packet range like 1-3'));
},
}]}
>
<Select
<AutoComplete
options={[
{ value: '1-3', label: '1-3' },
{ value: 'tlshello', label: 'tlshello' },
{ value: '1-3', label: '1-3' },
{ value: '1-5', label: '1-5' },
]}
placeholder="tlshello or n-m, e.g. 1-3"
/>
</Form.Item>
<Form.Item label={t('pages.settings.subFormats.length')} name={['settings', 'fragment', 'length']}>