mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-23 19:37:13 +00:00
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:
@@ -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 { DeleteOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons';
|
||||||
import type { FormInstance } from 'antd/es/form';
|
import type { FormInstance } from 'antd/es/form';
|
||||||
import type { NamePath } from 'antd/es/form/interface';
|
import type { NamePath } from 'antd/es/form/interface';
|
||||||
@@ -205,13 +205,18 @@ function TcpMaskItem({
|
|||||||
if (type === 'fragment') {
|
if (type === 'fragment') {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form.Item label="Packets" name={[fieldName, 'settings', 'packets']}>
|
<Form.Item
|
||||||
<Select
|
label="Packets"
|
||||||
|
name={[fieldName, 'settings', 'packets']}
|
||||||
|
rules={[{ validator: validateFragmentPackets }]}
|
||||||
|
>
|
||||||
|
<AutoComplete
|
||||||
options={[
|
options={[
|
||||||
{ value: 'tlshello', label: 'tlshello' },
|
{ value: 'tlshello', label: 'tlshello' },
|
||||||
{ value: '1-3', label: '1-3' },
|
{ value: '1-3', label: '1-3' },
|
||||||
{ value: '1-5', label: '1-5' },
|
{ value: '1-5', label: '1-5' },
|
||||||
]}
|
]}
|
||||||
|
placeholder="tlshello or n-m, e.g. 1-3"
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<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
|
// Walks a deep object path safely. Used inside shouldUpdate which gets
|
||||||
// the whole form values blob; we need to compare a deep field across
|
// the whole form values blob; we need to compare a deep field across
|
||||||
// prev/curr without crashing on missing intermediates.
|
// prev/curr without crashing on missing intermediates.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
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 { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
import { OutboundDomainStrategies } from '@/schemas/primitives';
|
import { OutboundDomainStrategies } from '@/schemas/primitives';
|
||||||
@@ -67,12 +67,24 @@ export default function FreedomFields({ form }: { form: FormInstance<OutboundFor
|
|||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('pages.settings.subFormats.packets')}
|
label={t('pages.settings.subFormats.packets')}
|
||||||
name={['settings', 'fragment', '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={[
|
options={[
|
||||||
{ value: '1-3', label: '1-3' },
|
|
||||||
{ value: 'tlshello', label: 'tlshello' },
|
{ 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>
|
||||||
<Form.Item label={t('pages.settings.subFormats.length')} name={['settings', 'fragment', 'length']}>
|
<Form.Item label={t('pages.settings.subFormats.length')} name={['settings', 'fragment', 'length']}>
|
||||||
|
|||||||
Reference in New Issue
Block a user