浏览代码

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.
MHSanaei 3 天之前
父节点
当前提交
bade1fcef6

+ 18 - 3
frontend/src/lib/xray/forms/transport/FinalMaskForm.tsx

@@ -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.

+ 15 - 3
frontend/src/pages/xray/outbounds/protocols/freedom.tsx

@@ -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']}>