Update On Sat Jun 29 20:29:47 CEST 2024

This commit is contained in:
github-action[bot]
2024-06-29 20:29:47 +02:00
parent 40a44d0943
commit e448b07b19
80 changed files with 2912 additions and 1310 deletions

View File

@@ -20,6 +20,7 @@
66193A8923EE46BC00289B6A /* PreferenceRouting.xib in Resources */ = {isa = PBXBuildFile; fileRef = 66193A8723EE46BC00289B6A /* PreferenceRouting.xib */; };
6633A43F2C0A120000C54CA5 /* Sparkle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6633A43E2C0A120000C54CA5 /* Sparkle.swift */; };
663F040625ED4B2C00687600 /* V2rayLaunch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663F040525ED4B2C00687600 /* V2rayLaunch.swift */; };
664BAC472C2DB0E100654FB7 /* V2rayRouting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BAC462C2DB0E100654FB7 /* V2rayRouting.swift */; };
664EB375216C9A5E00B6AE0D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664EB374216C9A5E00B6AE0D /* AppDelegate.swift */; };
664EB377216C9A5F00B6AE0D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664EB376216C9A5F00B6AE0D /* Assets.xcassets */; };
664EB392216CA9E800B6AE0D /* ConfigWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664EB390216CA9E800B6AE0D /* ConfigWindow.swift */; };
@@ -124,6 +125,7 @@
663F040525ED4B2C00687600 /* V2rayLaunch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = V2rayLaunch.swift; sourceTree = "<group>"; };
664666A021CBD6C60094F0B7 /* libPods-V2rayUTool.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libPods-V2rayUTool.a"; sourceTree = BUILT_PRODUCTS_DIR; };
664B95DD217062A500DBC941 /* Alamofire */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Alamofire; path = Pods/Alamofire; sourceTree = "<group>"; };
664BAC462C2DB0E100654FB7 /* V2rayRouting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = V2rayRouting.swift; sourceTree = "<group>"; };
664EB371216C9A5E00B6AE0D /* V2rayU.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = V2rayU.app; sourceTree = BUILT_PRODUCTS_DIR; };
664EB374216C9A5E00B6AE0D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
664EB376216C9A5F00B6AE0D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -272,6 +274,7 @@
660D0E59216E0158000C2922 /* V2rayServer.swift */,
663F040525ED4B2C00687600 /* V2rayLaunch.swift */,
66BC2B88228C589E00FBB716 /* V2raySubscription.swift */,
664BAC462C2DB0E100654FB7 /* V2rayRouting.swift */,
66784AFB2170486D00AD307F /* Util.swift */,
6D6DF87840CDA04AF0E9D36E /* Import.swift */,
664EB37B216C9A5F00B6AE0D /* Info.plist */,
@@ -544,6 +547,7 @@
66AD5335241497000070529C /* Shortcut.m in Sources */,
663F040625ED4B2C00687600 /* V2rayLaunch.swift in Sources */,
6608D9E22182C9C100A0E0DD /* v2rayStream.swift in Sources */,
664BAC472C2DB0E100654FB7 /* V2rayRouting.swift in Sources */,
66784AFC2170486D00AD307F /* Util.swift in Sources */,
660D0E5A216E0158000C2922 /* V2rayServer.swift in Sources */,
6608D9DC2182C6D200A0E0DD /* v2rayInbound.swift in Sources */,

View File

@@ -35,8 +35,8 @@ let preferencesWindowController = PreferencesWindowController(
PreferenceGeneralViewController(),
PreferenceAdvanceViewController(),
PreferenceSubscribeViewController(),
PreferencePacViewController(),
PreferenceRoutingViewController(),
PreferencePacViewController(),
PreferenceDnsViewController(),
PreferenceAboutViewController(),
]
@@ -112,6 +112,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
UserDefaults.set(forKey: .runMode, value: RunMode.global.rawValue)
}
V2rayServer.loadConfig()
V2rayRoutings.loadConfig()
V2raySubscription.loadConfig()
}
@objc func handleAppleEvent(event: NSAppleEventDescriptor, replyEvent: NSAppleEventDescriptor) {

View File

@@ -24,7 +24,7 @@
<outlet property="manualMode" destination="hTu-27-5OL" id="lHt-aQ-ZNU"/>
<outlet property="newVersionItem" destination="SdL-DD-qZe" id="WXr-Gw-0HS"/>
<outlet property="pacMode" destination="NrZ-oB-7yd" id="d3C-wO-jAI"/>
<outlet property="routingMenu" destination="ykF-uD-Nb1" id="qTS-BH-9rV"/>
<outlet property="routingMenu" destination="VNg-as-9we" id="NO1-L3-jsN"/>
<outlet property="serverItems" destination="cio-ej-HL5" id="0Z8-41-eCi"/>
<outlet property="statusMenu" destination="9n3-TW-9ur" id="Hgg-sa-fM8"/>
<outlet property="toggleV2rayItem" destination="SML-EF-rdT" id="am7-ue-GN3"/>
@@ -88,38 +88,7 @@
<menuItem isSeparatorItem="YES" id="EF9-7H-2Xy"/>
<menuItem title="Routing" id="VNg-as-9we" userLabel="Routing">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Routing" id="ykF-uD-Nb1">
<items>
<menuItem title="Routing..." tag="-1" id="tKM-4Z-Gyr">
<attributedString key="attributedTitle"/>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="goRouting:" target="hcZ-V7-Dui" id="gVK-Fb-7uq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="pSb-Qp-dXC"/>
<menuItem title="Global" identifier="routingGlobal" id="qhf-Kw-wBL" userLabel="Global">
<connections>
<action selector="switchRouting:" target="hcZ-V7-Dui" id="j4n-az-30A"/>
</connections>
</menuItem>
<menuItem title="Bypassing the LAN Address" tag="1" identifier="routingLan" id="xcE-uG-T3N">
<connections>
<action selector="switchRouting:" target="hcZ-V7-Dui" id="ssU-OD-c6I"/>
</connections>
</menuItem>
<menuItem title="Bypassing mainland address" tag="2" identifier="routingCn" id="Y6v-yx-HRg">
<connections>
<action selector="switchRouting:" target="hcZ-V7-Dui" id="oYE-YR-VSp"/>
</connections>
</menuItem>
<menuItem title="Bypassing LAN and mainland address" tag="3" identifier="routingLanAncCn" id="YQd-ir-Ilb">
<connections>
<action selector="switchRouting:" target="hcZ-V7-Dui" id="OVM-pr-94W"/>
</connections>
</menuItem>
</items>
</menu>
<menu key="submenu" title="Routing" id="ykF-uD-Nb1"/>
<connections>
<action selector="switchRouting:" target="hcZ-V7-Dui" id="cPc-yB-1qO"/>
</connections>

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21225" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21225"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22690"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -23,7 +23,7 @@
<rect key="frame" x="0.0" y="238" width="700" height="122"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dD5-t7-MtS">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dD5-t7-MtS">
<rect key="frame" x="18" y="79" width="192" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="GFW List URL" id="L8s-Y6-NP1">
@@ -32,7 +32,7 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="D20-Eu-3f8">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="D20-Eu-3f8">
<rect key="frame" x="98" y="99" width="251" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" id="NE5-JO-Q74">
@@ -41,7 +41,7 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Uoq-RS-CX7">
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Uoq-RS-CX7">
<rect key="frame" x="20" y="20" width="660" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="Mzi-Yb-HhA">
@@ -94,7 +94,7 @@
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LeF-Sd-TXL">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LeF-Sd-TXL">
<rect key="frame" x="18" y="206" width="294" height="21"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="User Rules" id="W0g-B7-neT">

View File

@@ -8,11 +8,18 @@
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="PreferenceRoutingViewController" customModule="V2rayU" customModuleProvider="target">
<connections>
<outlet property="blockTextView" destination="UmH-oj-OeF" id="F4L-ai-VLz"/>
<outlet property="directTextView" destination="vYm-hy-3PV" id="duZ-Ja-Hlh"/>
<outlet property="domainStrategy" destination="xjS-y0-GA2" id="R83-kT-QA0"/>
<outlet property="proxyTextView" destination="yaM-kZ-d1d" id="7bQ-0C-yFh"/>
<outlet property="routingRule" destination="y7v-vA-AiE" id="OTD-E9-5mu"/>
<outlet property="addRemoveButton" destination="yay-tY-9RI" id="27u-4O-Oni"/>
<outlet property="blockTextView" destination="Gg4-pf-FBq" id="sNf-j8-228"/>
<outlet property="customView" destination="vIL-rw-NyK" id="eGd-9R-VkR"/>
<outlet property="defaulRoutingRuleName" destination="Ghx-HQ-ZRg" id="yS4-cX-uLm"/>
<outlet property="defaultView" destination="6dV-kQ-mRx" id="QLs-jh-yuK"/>
<outlet property="directTextView" destination="bBl-6J-dn4" id="JKH-R5-yhZ"/>
<outlet property="domainStrategy" destination="3TP-iM-Ceg" id="MRD-X0-E0s"/>
<outlet property="errTip" destination="4I8-WZ-oKx" id="UFS-dt-gAX"/>
<outlet property="proxyTextView" destination="OBa-Gy-6Sf" id="CB2-mh-AkQ"/>
<outlet property="routingRuleContent" destination="HPL-Ee-PgQ" id="9Lu-aP-6ak"/>
<outlet property="routingRuleName" destination="4qj-OR-LZe" id="aEK-PJ-Z3V"/>
<outlet property="routingsTableView" destination="L49-ez-XJl" id="um4-rf-4zV"/>
<outlet property="view" destination="bXz-rK-jao" id="sLH-5Q-KUz"/>
</connections>
</customObject>
@@ -23,187 +30,332 @@
<rect key="frame" x="0.0" y="0.0" width="700" height="360"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jcb-hK-ckn">
<rect key="frame" x="18" y="323" width="109" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Domain Strategy:" id="22k-l7-G7a">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4I8-WZ-oKx" userLabel="errtip">
<rect key="frame" x="179" y="335" width="337" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" id="h1X-Lz-CYG">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oz6-O3-feh">
<rect key="frame" x="42" y="293" width="85" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Routing Rule:" id="4rB-9k-291">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xjS-y0-GA2" userLabel="domainStrategy">
<rect key="frame" x="146" y="316" width="173" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="AsIs" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="LX1-ye-51i" id="Z6S-ke-Pf5" userLabel="domainStrategy">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="bA6-aC-f9X">
<items>
<menuItem title="AsIs" state="on" id="LX1-ye-51i">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="IPIfNonMatch" id="92h-JU-wqn">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="IPOnDemand" id="Js7-cb-bWa">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="y7v-vA-AiE" userLabel="routingRule">
<rect key="frame" x="146" y="285" width="173" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Global" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="zLf-pT-xlm" id="1JH-TF-90a" userLabel="routingRule">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="s7q-On-wcL">
<items>
<menuItem title="Global" state="on" id="zLf-pT-xlm" userLabel="Global"/>
<menuItem title="Bypassing the LAN Address" tag="1" id="CoE-cp-7fn"/>
<menuItem title="Bypassing mainland address" tag="2" id="EaD-tR-ldB"/>
<menuItem title="Bypassing LAN and mainland address" tag="3" id="3qq-dt-TS9"/>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
<tabView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Xt6-4C-beT">
<rect key="frame" x="13" y="42" width="670" height="236"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<font key="font" metaFont="system"/>
<tabViewItems>
<tabViewItem label="Proxy" identifier="" id="pnL-Z8-fIK">
<view key="view" id="OH4-JV-ZAt">
<rect key="frame" x="10" y="33" width="650" height="190"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fKu-4w-nfA">
<rect key="frame" x="12" y="12" width="625" height="171"/>
<autoresizingMask key="autoresizingMask"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="52j-ry-0qA">
<rect key="frame" x="0.0" y="0.0" width="625" height="171"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="yaM-kZ-d1d">
<rect key="frame" x="0.0" y="0.0" width="625" height="171"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="625" height="171"/>
<size key="maxSize" width="640" height="10000000"/>
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
</textView>
</subviews>
<color key="backgroundColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="lEt-YZ-dkb">
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="pbO-5s-0Pk">
<rect key="frame" x="609" y="0.0" width="16" height="171"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
</subviews>
</view>
</tabViewItem>
<tabViewItem label="Direct" identifier="" id="yc0-Po-cG1">
<view key="view" id="mlE-HX-BcJ">
<rect key="frame" x="10" y="33" width="650" height="190"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kkR-uc-Qja">
<rect key="frame" x="12" y="12" width="627" height="171"/>
<autoresizingMask key="autoresizingMask"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="oot-vM-Y6a">
<rect key="frame" x="0.0" y="0.0" width="627" height="171"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="vYm-hy-3PV">
<rect key="frame" x="0.0" y="0.0" width="627" height="171"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="627" height="171"/>
<size key="maxSize" width="628" height="10000000"/>
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
</textView>
</subviews>
<color key="backgroundColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="J9m-5q-Nwm">
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="9vp-fC-VPR">
<rect key="frame" x="611" y="0.0" width="16" height="171"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
</subviews>
</view>
</tabViewItem>
<tabViewItem label="Block" identifier="" id="nsT-BW-R2Z">
<view key="view" id="OgP-Jb-qTh">
<rect key="frame" x="10" y="33" width="650" height="190"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GOX-0f-UYf">
<rect key="frame" x="12" y="12" width="627" height="171"/>
<autoresizingMask key="autoresizingMask"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="lep-5k-Lk2">
<rect key="frame" x="0.0" y="0.0" width="627" height="171"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="UmH-oj-OeF">
<rect key="frame" x="0.0" y="0.0" width="627" height="171"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="627" height="171"/>
<size key="maxSize" width="633" height="10000000"/>
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
</textView>
</subviews>
<color key="backgroundColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="KHQ-uv-0yY">
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="c7l-Gi-Vwn">
<rect key="frame" x="611" y="0.0" width="16" height="171"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
</subviews>
</view>
</tabViewItem>
</tabViewItems>
</tabView>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="abB-zF-WFz" userLabel="Set the rules line by line: domain or ip">
<rect key="frame" x="18" y="20" width="241" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="* Set the rules line by line: domain or ip" id="ccq-gn-C6Z" userLabel="Set the rules line by line: domain or ip">
<font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="textColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="RRU-nZ-itS">
<rect key="frame" x="553" y="13" width="120" height="32"/>
<view hidden="YES" id="6dV-kQ-mRx" userLabel="Default">
<rect key="frame" x="260" y="20" width="420" height="320"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6af-jf-tia">
<rect key="frame" x="18" y="259" width="109" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Domain Strategy:" id="Qp3-K0-xA9">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3TP-iM-Ceg" userLabel="domainStrategy">
<rect key="frame" x="140" y="251" width="173" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="AsIs" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Kl2-gr-bsh" id="FFA-RR-8Un" userLabel="domainStrategy">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="B2F-Wu-yuF">
<items>
<menuItem title="AsIs" state="on" id="Kl2-gr-bsh">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="IPIfNonMatch" id="dSb-GT-n2n">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="IPOnDemand" id="2ST-s0-RrI">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="64w-Z6-GSi" userLabel="block">
<rect key="frame" x="143" y="83" width="240" height="60"/>
<autoresizingMask key="autoresizingMask"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="G9y-OO-6y5">
<rect key="frame" x="0.0" y="0.0" width="240" height="60"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="bBl-6J-dn4">
<rect key="frame" x="0.0" y="0.0" width="240" height="60"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="240" height="60"/>
<size key="maxSize" width="633" height="10000000"/>
</textView>
</subviews>
<color key="backgroundColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="bsW-PV-AtG">
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="njY-6I-wSH">
<rect key="frame" x="224" y="0.0" width="16" height="60"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ig4-By-hS9" userLabel="direct">
<rect key="frame" x="143" y="10" width="240" height="60"/>
<autoresizingMask key="autoresizingMask"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="GwS-SF-CQa">
<rect key="frame" x="0.0" y="0.0" width="240" height="60"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="Gg4-pf-FBq">
<rect key="frame" x="0.0" y="0.0" width="240" height="60"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="240" height="60"/>
<size key="maxSize" width="628" height="10000000"/>
</textView>
</subviews>
<color key="backgroundColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="9gZ-fO-VEZ">
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="KPQ-8d-YOS">
<rect key="frame" x="224" y="0.0" width="16" height="60"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TMv-k8-PCk" userLabel="proxy">
<rect key="frame" x="143" y="153" width="240" height="60"/>
<autoresizingMask key="autoresizingMask"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="uGK-ha-GOm">
<rect key="frame" x="0.0" y="0.0" width="240" height="60"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="OBa-Gy-6Sf">
<rect key="frame" x="0.0" y="0.0" width="240" height="60"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="240" height="60"/>
<size key="maxSize" width="640" height="10000000"/>
</textView>
</subviews>
<color key="backgroundColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="709-IY-EcB">
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="GFh-eb-Tsf">
<rect key="frame" x="224" y="0.0" width="16" height="60"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jFM-iJ-CtS">
<rect key="frame" x="18" y="289" width="109" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Routing Rule:" id="J2T-Pf-s5D">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="gjh-yh-Xfh">
<rect key="frame" x="23" y="200" width="100" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Proxy:" id="cfS-8I-8Au">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="aDJ-Pb-mQj">
<rect key="frame" x="23" y="119" width="100" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Direct:" id="3hZ-qS-PUG">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fcp-vz-Yqj">
<rect key="frame" x="23" y="51" width="100" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Block:" id="0Cd-bg-07K">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qba-nL-flY" userLabel="Set the rules line by line: domain or ip">
<rect key="frame" x="141" y="225" width="241" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="* Set the rules line by line: domain or ip" id="ymo-ds-No9" userLabel="Set the rules line by line: domain or ip">
<font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mvt-K4-emn">
<rect key="frame" x="385" y="221" width="25" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="help" bezelStyle="helpButton" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Yjg-fg-Qnb">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="goHelp2:" target="-2" id="dbI-AV-ozZ"/>
</connections>
</button>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ghx-HQ-ZRg">
<rect key="frame" x="143" y="287" width="265" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" enabled="NO" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="C2A-lA-Xek">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</view>
<view id="vIL-rw-NyK" userLabel="Custom">
<rect key="frame" x="260" y="20" width="420" height="320"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="seP-0K-2PM" userLabel="rule_content">
<rect key="frame" x="12" y="36" width="396" height="197"/>
<autoresizingMask key="autoresizingMask"/>
<clipView key="contentView" drawsBackground="NO" id="sw0-56-vxm">
<rect key="frame" x="0.0" y="0.0" width="396" height="197"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView wantsLayer="YES" importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="HPL-Ee-PgQ">
<rect key="frame" x="0.0" y="0.0" width="396" height="197"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="396" height="197"/>
<size key="maxSize" width="602" height="10000000"/>
</textView>
</subviews>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="vl1-cY-N6c">
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="Kd9-5H-Ti7">
<rect key="frame" x="380" y="0.0" width="16" height="197"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4qj-OR-LZe" userLabel="rule_name">
<rect key="frame" x="12" y="266" width="396" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="rZe-pT-kYm">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2vP-Kp-xfO">
<rect key="frame" x="10" y="294" width="120" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Custom Rule Name" id="Ecy-wl-v5i">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="q31-RU-ik5">
<rect key="frame" x="10" y="240" width="149" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Custom Rule JSON Text" id="Q0v-Ic-TRt">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1MS-Rp-MSG">
<rect key="frame" x="10" y="4" width="318" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="like: { &quot;domainStrategy&quot;: &quot;IPOnDemand&quot;, &quot;rules&quot;: []}" id="ejH-X7-8Fb">
<font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3IT-GN-64p">
<rect key="frame" x="385" y="-4" width="25" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="help" bezelStyle="helpButton" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Ecr-ZM-mqE">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="goHelp2:" target="-2" id="qbe-WP-16k"/>
</connections>
</button>
</subviews>
</view>
<scrollView focusRingType="none" fixedFrame="YES" borderType="line" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="in5-Uo-f8c">
<rect key="frame" x="25" y="66" width="220" height="241"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<clipView key="contentView" copiesOnScroll="NO" id="vCh-HN-u7f">
<rect key="frame" x="1" y="1" width="218" height="239"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" id="L49-ez-XJl">
<rect key="frame" x="0.0" y="0.0" width="218" height="239"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="remark" width="206" minWidth="40" maxWidth="1000" id="0WD-Qv-ia2">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" title="Text Cell" usesSingleLineMode="YES" id="YQ1-7i-YOl">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</tableColumn>
</tableColumns>
</tableView>
</subviews>
</clipView>
<edgeInsets key="contentInsets" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="dpI-Rq-Xuz">
<rect key="frame" x="1" y="224" width="218" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="ys3-G3-wz7">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<segmentedControl verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="yay-tY-9RI">
<rect key="frame" x="25" y="55" width="220" height="23"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<segmentedCell key="cell" borderStyle="border" alignment="left" style="smallSquare" trackingMode="selectOne" id="GHx-a5-Me0">
<font key="font" metaFont="system"/>
<segments>
<segment image="NSAddTemplate" imageScaling="none" width="62"/>
<segment image="NSMenuMixedStateTemplate" imageScaling="none" width="61" tag="1"/>
<segment width="89" enabled="NO"/>
</segments>
<connections>
<action selector="addRemoveServer:" target="-2" id="icc-rY-Qe0"/>
</connections>
</segmentedCell>
</segmentedControl>
<button verticalHuggingPriority="750" id="RRU-nZ-itS">
<rect key="frame" x="75" y="11" width="120" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="save" bezelStyle="rounded" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="8g2-Nj-m07">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
@@ -213,28 +365,15 @@
<action selector="saveRouting:" target="-2" id="q5Z-CO-QXQ"/>
</connections>
</button>
<button horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Hdf-GC-RW1">
<rect key="frame" x="330" y="315" width="25" height="25"/>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ntV-Ym-8MI">
<rect key="frame" x="27" y="315" width="83" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="help" bezelStyle="helpButton" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="4Ml-by-67E">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Rouing Rules" id="E4W-rN-0eq">
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="goHelp:" target="-2" id="wWf-8M-Cj5"/>
</connections>
</button>
<button horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uaE-tl-Qk5">
<rect key="frame" x="273" y="16" width="25" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="help" bezelStyle="helpButton" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="198-iA-PZS">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="goHelp2:" target="-2" id="w5e-XR-QvL"/>
</connections>
</button>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<point key="canvasLocation" x="-130" y="-187"/>
</view>
@@ -245,4 +384,8 @@
</connections>
</popover>
</objects>
<resources>
<image name="NSAddTemplate" width="18" height="17"/>
<image name="NSMenuMixedStateTemplate" width="18" height="5"/>
</resources>
</document>

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21225" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21225"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22690"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -28,7 +28,7 @@
<rect key="frame" x="0.0" y="0.0" width="700" height="360"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Hdz-z4-E7U">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Hdz-z4-E7U">
<rect key="frame" x="20" y="320" width="100" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Update Logs" id="7e8-mr-923">
@@ -107,11 +107,11 @@
</textFieldCell>
<prototypeCellViews>
<tableCellView id="sbc-Q5-Cgb">
<rect key="frame" x="1" y="1" width="151.5" height="20"/>
<rect key="frame" x="1" y="1" width="152" height="20"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uV2-VE-pu4">
<rect key="frame" x="0.0" y="3" width="152" height="17"/>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uV2-VE-pu4">
<rect key="frame" x="0.0" y="3" width="153" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" identifier="firstName" title="Table View Cell" id="2SR-49-K11">
<font key="font" metaFont="system"/>
@@ -139,11 +139,11 @@
</textFieldCell>
<prototypeCellViews>
<tableCellView id="FLk-I9-55S">
<rect key="frame" x="154.5" y="1" width="499.5" height="16"/>
<rect key="frame" x="154.5" y="1" width="499" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9CU-oY-puz">
<rect key="frame" x="0.0" y="0.0" width="499" height="16"/>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9CU-oY-puz">
<rect key="frame" x="0.0" y="0.0" width="498" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="justified" identifier="lastName" title="Table View Cell" id="OER-yL-Osv">
<font key="font" metaFont="system"/>
@@ -179,7 +179,7 @@
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NPq-js-UOI">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NPq-js-UOI">
<rect key="frame" x="18" y="286" width="47" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="remark" id="8Lu-YS-kql">
@@ -188,7 +188,7 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="G8V-7i-NEX">
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="G8V-7i-NEX">
<rect key="frame" x="71" y="283" width="96" height="22"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="M3g-EG-XMY">
@@ -197,7 +197,7 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8Kr-tk-3lE">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8Kr-tk-3lE">
<rect key="frame" x="18" y="316" width="45" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="url" id="J2v-eb-RUX">
@@ -206,7 +206,7 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mlO-5h-gcH">
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mlO-5h-gcH">
<rect key="frame" x="71" y="316" width="608" height="22"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="dnA-jR-ayC">

View File

@@ -188,23 +188,23 @@ class ConfigWindowController: NSWindowController, NSWindowDelegate, NSTabViewDel
// delete server config
case 1:
// get seleted index
let idx = self.serversTableView.selectedRow
// remove
V2rayServer.remove(idx: idx)
// reload
V2rayServer.loadConfig()
menuController.showServers()
// selected prev row
let cnt: Int = V2rayServer.count()
var rowIndex: Int = idx - 1
if idx > 0 && idx < cnt {
rowIndex = idx
}
DispatchQueue.main.sync {
// get seleted index
let idx = self.serversTableView.selectedRow
// remove
V2rayServer.remove(idx: idx)
// reload
V2rayServer.loadConfig()
menuController.showServers()
// selected prev row
let cnt: Int = V2rayServer.count()
var rowIndex: Int = idx - 1
if idx > 0 && idx < cnt {
rowIndex = idx
}
// reload
self.serversTableView.reloadData()
// fix

View File

@@ -27,7 +27,7 @@ class MenuController: NSObject, NSMenuDelegate {
@IBOutlet weak var v2rayStatusItem: NSMenuItem!
@IBOutlet weak var serverItems: NSMenuItem!
@IBOutlet weak var newVersionItem: NSMenuItem!
@IBOutlet weak var routingMenu: NSMenu!
@IBOutlet weak var routingMenu: NSMenuItem!
// when menu.xib loaded
override func awakeFromNib() {
@@ -39,7 +39,7 @@ class MenuController: NSObject, NSMenuDelegate {
// hide new version
newVersionItem.isHidden = true
self.showRouring();
self.showRouting();
// windowWillClose Notification
NotificationCenter.default.addObserver(self, selector: #selector(configWindowWillClose(notification:)), name: NSWindow.willCloseNotification, object: nil)
@@ -127,21 +127,40 @@ class MenuController: NSObject, NSMenuDelegate {
}
}
func showRouring() {
let routingRule = Int(UserDefaults.get(forKey: .routingRule) ?? "0") ?? 0
DispatchQueue.main.async {
// routingMenu
if self.routingMenu.items != nil {
// routingMenu
for item in self.routingMenu.items {
if item.tag == routingRule {
item.state = .on
} else {
item.state = .off
}
}
}
}
func showRouting() {
DispatchQueue.global().async {
let rules = V2rayRoutings.all()
print("showRouting",rules)
let sumMenus = NSMenu()
// add Routing... menu and click event is goRouting
let routingMenuItem = NSMenuItem()
routingMenuItem.title = "Routing..."
routingMenuItem.target = self
routingMenuItem.action = #selector(self.goRouting(_:))
sumMenus.addItem(routingMenuItem)
// add separator menu item
let separator = NSMenuItem.separator()
sumMenus.addItem(separator)
// now add all rules
let routingRule = UserDefaults.get(forKey: .routingSelectedRule)
for rule in rules {
let menuItem = NSMenuItem()
menuItem.title = rule.remark
menuItem.target = self
menuItem.action = #selector(self.switchRouting(_:))
menuItem.representedObject = rule
menuItem.isEnabled = true
if rule.name == routingRule {
menuItem.state = NSControl.StateValue.on
}
sumMenus.addItem(menuItem)
}
print("showRouting",sumMenus)
// routingMenu
DispatchQueue.main.async {
self.routingMenu.submenu = sumMenus
}
}
}
func getServerMenus() -> NSMenu {
@@ -261,6 +280,16 @@ class MenuController: NSObject, NSMenuDelegate {
V2rayLaunch.restartV2ray()
}
@IBAction func switchRouting(_ sender: NSMenuItem) {
guard let obj = sender.representedObject as? RoutingItem else {
NSLog("switchRouting err")
return
}
UserDefaults.set(forKey: .routingSelectedRule, value: obj.name);
self.showRouting();
V2rayLaunch.restartV2ray()
}
@IBAction func openConfig(_ sender: NSMenuItem) {
OpenConfigWindow()
}
@@ -300,12 +329,6 @@ class MenuController: NSObject, NSMenuDelegate {
}
}
@IBAction func switchRouting(_ sender: NSMenuItem) {
UserDefaults.set(forKey: .routingRule, value: String(sender.tag));
self.showRouring();
V2rayLaunch.restartV2ray()
}
@IBAction func switchGlobalMode(_ sender: NSMenuItem) {
UserDefaults.set(forKey: .runMode, value: RunMode.global.rawValue)
V2rayLaunch.restartV2ray()

View File

@@ -9,18 +9,26 @@
import Cocoa
import Preferences
final class PreferenceRoutingViewController: NSViewController, PreferencePane {
final class PreferenceRoutingViewController: NSViewController, PreferencePane, NSTabViewDelegate {
let preferencePaneIdentifier = PreferencePane.Identifier.routingTab
let preferencePaneTitle = "Routing"
let toolbarItemIcon = NSImage(named: NSImage.networkName)!
let tableViewDragType: String = "v2ray.routing"
@IBOutlet weak var domainStrategy: NSPopUpButton!
@IBOutlet weak var routingRule: NSPopUpButton!
@IBOutlet var proxyTextView: NSTextView!
@IBOutlet var directTextView: NSTextView!
@IBOutlet var blockTextView: NSTextView!
@IBOutlet weak var proxyTextView: NSTextView!
@IBOutlet weak var directTextView: NSTextView!
@IBOutlet weak var blockTextView: NSTextView!
@IBOutlet weak var routingRuleContent: NSTextView!
@IBOutlet weak var routingRuleName: NSTextField!
@IBOutlet weak var defaulRoutingRuleName: NSTextField!
@IBOutlet weak var errTip: NSTextField!
@IBOutlet weak var routingsTableView: NSTableView!
@IBOutlet weak var addRemoveButton: NSSegmentedControl!
@IBOutlet weak var customView: NSView!
@IBOutlet weak var defaultView: NSView!
override var nibName: NSNib.Name? {
return "PreferenceRouting"
}
@@ -29,30 +37,115 @@ final class PreferenceRoutingViewController: NSViewController, PreferencePane {
super.viewDidLoad()
// fix: https://github.com/sindresorhus/Preferences/issues/31
self.preferredContentSize = NSMakeSize(self.view.frame.size.width, self.view.frame.size.height);
// set table drag style
self.routingsTableView.registerForDraggedTypes([NSPasteboard.PasteboardType(rawValue: tableViewDragType)])
self.routingsTableView.allowsMultipleSelection = true
let domainStrategy = UserDefaults.get(forKey: .routingDomainStrategy) ?? "AsIs"
self.domainStrategy.selectItem(withTitle: domainStrategy)
let routingRule = Int(UserDefaults.get(forKey: .routingRule) ?? "0") ?? 0
self.routingRule.selectItem(withTag: routingRule)
let routingProxyDomains = UserDefaults.getArray(forKey: .routingProxyDomains) ?? [];
let routingProxyIps = UserDefaults.getArray(forKey: .routingProxyIps) ?? [];
let routingDirectDomains = UserDefaults.getArray(forKey: .routingDirectDomains) ?? [];
let routingDirectIps = UserDefaults.getArray(forKey: .routingDirectIps) ?? [];
let routingBlockDomains = UserDefaults.getArray(forKey: .routingBlockDomains) ?? [];
let routingBlockIps = UserDefaults.getArray(forKey: .routingBlockIps) ?? [];
let routingProxy = routingProxyDomains + routingProxyIps
let routingDirect = routingDirectDomains + routingDirectIps
let routingBlock = routingBlockDomains + routingBlockIps
print("routingProxy", routingProxy, routingDirect, routingBlock)
self.proxyTextView.string = routingProxy.joined(separator: "\n")
self.directTextView.string = routingDirect.joined(separator: "\n")
self.blockTextView.string = routingBlock.joined(separator: "\n")
// reload tableview
V2rayRoutings.loadConfig()
// table view
self.routingsTableView.delegate = self
self.routingsTableView.dataSource = self
self.routingsTableView.reloadData()
}
func set_tip(str: String) {
self.errTip.stringValue = str
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.errTip.stringValue = ""
}
}
func loadJsonData(rowIndex: Int) {
print("loadJsonData", rowIndex)
DispatchQueue.main.async {
self.defaultView.isHidden = true
self.customView.isHidden = true
if let item = V2rayRoutings.load(idx: rowIndex) {
let isDefaultRule = V2rayRoutings.isDefaultRule(name: item.name)
print("loadJsonData", item.domainStrategy,item.proxy,item.direct,item.block,item.json,isDefaultRule)
if isDefaultRule {
self.defaultView.isHidden = false
self.domainStrategy.selectItem(withTitle: item.domainStrategy)
self.proxyTextView.string = item.proxy
self.directTextView.string = item.direct
self.blockTextView.string = item.block
self.defaulRoutingRuleName.stringValue = item.remark
} else {
self.customView.isHidden = false
self.routingRuleName.stringValue = item.remark
self.routingRuleContent.string = item.json
}
}
}
}
@IBAction func addRemoveServer(_ sender: NSSegmentedCell) {
// 0 add,1 remove
let seg = addRemoveButton.indexOfSelectedItem
print("addRemoveServer",seg)
DispatchQueue.global().async {
switch seg {
// add server config
case 0:
// add
V2rayRoutings.add(remark: "new-rule", json: V2rayRoutings.default_rule_content)
DispatchQueue.main.sync {
V2rayRoutings.loadConfig()
// reload data
self.routingsTableView.reloadData()
// selected current row
self.routingsTableView.selectRowIndexes(NSIndexSet(index: V2rayRoutings.count() - 1) as IndexSet, byExtendingSelection: false)
}
break
// delete server config
case 1:
DispatchQueue.main.sync {
// get seleted index
let idx = self.routingsTableView.selectedRow
// remove
V2rayRoutings.remove(idx: idx)
// reload
V2rayRoutings.loadConfig()
// selected prev row
let cnt: Int = V2rayRoutings.count()
var rowIndex: Int = idx - 1
if idx > 0 && idx < cnt {
rowIndex = idx
}
// reload
self.routingsTableView.reloadData()
// fix
if cnt > 1 {
// selected row
self.routingsTableView.selectRowIndexes(NSIndexSet(index: rowIndex) as IndexSet, byExtendingSelection: false)
}
if rowIndex >= 0 {
self.loadJsonData(rowIndex: rowIndex)
} else {
self.routingsTableView.becomeFirstResponder()
}
}
// refresh menu
menuController.showRouting()
break
// unknown action
default:
return
}
}
}
@IBAction func goHelp(_ sender: Any) {
guard let url = URL(string: "https://toutyrater.github.io/basic/routing/") else {
return
@@ -68,73 +161,144 @@ final class PreferenceRoutingViewController: NSViewController, PreferencePane {
}
@IBAction func saveRouting(_ sender: Any) {
UserDefaults.set(forKey: .routingDomainStrategy, value: self.domainStrategy.titleOfSelectedItem!)
UserDefaults.set(forKey: .routingRule, value: String(self.routingRule.selectedTag()))
let selectedRule = self.routingsTableView.selectedRow
if selectedRule == -1 {
return
}
guard let rule = V2rayRoutings.load(idx: selectedRule) else {
return
}
var (domains, ips) = self.parseDomainOrIp(domainIpStr: self.proxyTextView.string)
UserDefaults.setArray(forKey: .routingProxyDomains, value: domains)
UserDefaults.setArray(forKey: .routingProxyIps, value: ips)
(domains, ips) = self.parseDomainOrIp(domainIpStr: self.directTextView.string)
UserDefaults.setArray(forKey: .routingDirectDomains, value: domains)
UserDefaults.setArray(forKey: .routingDirectIps, value: ips)
(domains, ips) = self.parseDomainOrIp(domainIpStr: self.blockTextView.string)
UserDefaults.setArray(forKey: .routingBlockDomains, value: domains)
UserDefaults.setArray(forKey: .routingBlockIps, value: ips)
if V2rayRoutings.isDefaultRule(name: rule.name) {
let domainStrategy = self.domainStrategy.titleOfSelectedItem
if domainStrategy != nil {
rule.domainStrategy = domainStrategy!
}
rule.proxy = self.proxyTextView.string
rule.direct = self.directTextView.string
rule.block = self.blockTextView.string
} else {
rule.remark = self.routingRuleName.stringValue
let (res, err) = parseRoutingRuleJson(json: self.routingRuleContent.string)
if err != nil {
print("parseRoutingRuleJson err", err)
self.errTip.stringValue = "parse json err: \(err)"
// hide err
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.errTip.stringValue = ""
}
return
} else {
self.errTip.stringValue = ""
rule.json = self.routingRuleContent.string
}
}
// save update
V2rayRoutings.save(routing: rule)
// reload table
self.routingsTableView.reloadData()
// set selected
self.routingsTableView.selectRowIndexes(NSIndexSet(index: selectedRule) as IndexSet, byExtendingSelection: false)
//
menuController.showRouring();
menuController.showRouting();
// if is selected rule name == rule.name, restart v2ray
let selectedRuleName = UserDefaults.get(forKey: .routingSelectedRule)
if selectedRuleName == rule.name {
// restart v2ray
V2rayLaunch.restartV2ray()
}
}
}
// set current server item and reload v2ray-core
V2rayLaunch.restartV2ray()
// NSnameSource
extension PreferenceRoutingViewController: NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return V2rayRoutings.count()
}
func parseDomainOrIp(domainIpStr: String) -> (domains: [String], ips: [String]) {
let all = domainIpStr.split(separator: "\n")
// show cell
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
let v2rayItemList = V2rayRoutings.list()
// set cell data
if v2rayItemList.count >= row {
return v2rayItemList[row].remark
}
return nil
}
}
var domains: [String] = []
var ips: [String] = []
// NSTableViewDelegate
extension PreferenceRoutingViewController: NSTableViewDelegate {
// For NSTableViewDelegate
func tableViewSelectionDidChange(_ notification: Notification) {
// Ensure there's a valid selected row before calling loadJsonData
if self.routingsTableView.selectedRow >= 0 {
self.loadJsonData(rowIndex: self.routingsTableView.selectedRow)
} else {
// Handle the case where no row is selected or add a default behavior
print("No row selected")
}
self.errTip.stringValue = ""
}
for item in all {
let tmp = item.trimmingCharacters(in: .whitespacesAndNewlines)
// Drag & Drop reorder rows
func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
let item = NSPasteboardItem()
item.setString(String(row), forType: NSPasteboard.PasteboardType(rawValue: tableViewDragType))
return item
}
// is ip
if isIp(str: tmp) || tmp.contains("geoip:") {
ips.append(tmp)
continue
func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation {
if dropOperation == .above {
return .move
}
return NSDragOperation()
}
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {
var oldIndexes = [Int]()
info.enumerateDraggingItems(options: [], for: tableView, classes: [NSPasteboardItem.self], searchOptions: [:], using: {
(draggingItem: NSDraggingItem, idx: Int, stop: UnsafeMutablePointer<ObjCBool>) in
if let str = (draggingItem.item as! NSPasteboardItem).string(forType: NSPasteboard.PasteboardType(rawValue: self.tableViewDragType)),
let index = Int(str) {
oldIndexes.append(index)
}
})
// is domain
if tmp.contains("domain:") || tmp.contains("geosite:") {
domains.append(tmp)
continue
}
var oldIndexOffset = 0
var newIndexOffset = 0
var oldIndexLast = 0
var newIndexLast = 0
if isDomain(str: tmp) {
domains.append(tmp)
continue
// For simplicity, the code below uses `tableView.moveRowAtIndex` to move rows around directly.
// You may want to move rows in your content array and then call `tableView.reloadData()` instead.
for oldIndex in oldIndexes {
if oldIndex < row {
oldIndexLast = oldIndex + oldIndexOffset
newIndexLast = row - 1
oldIndexOffset -= 1
} else {
oldIndexLast = oldIndex
newIndexLast = row + newIndexOffset
newIndexOffset += 1
}
}
print("ips", ips, "domains", domains)
return (domains, ips)
}
func isIp(str: String) -> Bool {
let pattern = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(/[0-9]{2})?$"
if ((str.count == 0) || (str.range(of: pattern, options: .regularExpression) == nil)) {
return false
}
return true
}
func isDomain(str: String) -> Bool {
let pattern = "[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+"
if ((str.count == 0) || (str.range(of: pattern, options: .regularExpression) == nil)) {
return false
DispatchQueue.global().async {
print("move",oldIndexLast,newIndexLast)
// move
V2rayRoutings.move(oldIndex: oldIndexLast, newIndex: newIndexLast)
DispatchQueue.main.async {
// set selected
self.routingsTableView.selectRowIndexes(NSIndexSet(index: newIndexLast) as IndexSet, byExtendingSelection: false)
// reload table
self.routingsTableView.reloadData()
}
// reload menu
menuController.showRouting()
}
return true
}

View File

@@ -63,16 +63,10 @@ extension UserDefaults {
// pacPort
case localPacPort
// for routing rule
case routingDomainStrategy
case routingRule
case routingProxyDomains
case routingProxyIps
case routingDirectDomains
case routingDirectIps
case routingBlockDomains
case routingBlockIps
case Exception
// custom routing list
case routingCustomList
// routing selected rule
case routingSelectedRule
}
static func setBool(forKey key: KEY, value: Bool) {

View File

@@ -0,0 +1,519 @@
//
// V2rayRouting.swift
// V2rayU
//
// Created by yanue on 2024/6/27.
// Copyright © 2024 yanue. All rights reserved.
//
import Foundation
let RoutingRuleGlobal = "routing.global"
let RoutingRuleLAN = "routing.lan"
let RoutingRuleCn = "routing.cn"
let RoutingRuleLANAndCn = "routing.lanAndCn"
let defaultRuleCn = Dictionary(uniqueKeysWithValues: [
(RoutingRuleGlobal, "🌏全局"),
(RoutingRuleLAN, "🌏 绕过局域网"),
(RoutingRuleCn, "🌏 绕过中国大陆"),
(RoutingRuleLANAndCn, "🌏 绕过局域网和中国大陆")
])
let defaultRuleEn = Dictionary(uniqueKeysWithValues: [
(RoutingRuleGlobal, "🌏 Global"),
(RoutingRuleLAN, "🌏 Bypassing the LAN Address"),
(RoutingRuleCn, "🌏 Bypassing mainland address"),
(RoutingRuleLANAndCn, "🌏 Bypassing LAN and mainland address")
])
let defaultRules = Dictionary(uniqueKeysWithValues: [
(RoutingRuleGlobal,RoutingItem(name: RoutingRuleGlobal, remark: "")),
(RoutingRuleLAN,RoutingItem(name: RoutingRuleLAN, remark: "")),
(RoutingRuleCn,RoutingItem(name: RoutingRuleCn, remark: "")),
(RoutingRuleLANAndCn,RoutingItem(name: RoutingRuleLANAndCn,remark:""))
])
// ----- routing server manager -----
class V2rayRoutings: NSObject {
static var shared = V2rayRoutings()
static let lock = NSLock()
static let default_rule_content = """
{
"domainStrategy": "AsIs",
"rules": [
]
}
"""
// Initialization
override init() {
super.init()
V2rayRoutings.loadConfig()
}
// routing server list
static private var routings: [RoutingItem] = []
// (init) load routing server list from UserDefaults
static func loadConfig() {
self.lock.lock()
defer {
self.lock.unlock()
}
// static reset
self.routings = []
// load name list from UserDefaults
var list = UserDefaults.getArray(forKey: .routingCustomList) ?? [];
print("V2rayRoutings-loadConfig", list)
let langStr = Locale.current.languageCode
let isMainland = langStr == "zh-CN" || langStr == "zh" || langStr == "zh-Hans" || langStr == "zh-Hant"
// for defaultRules
for (key, rule) in defaultRules {
// load and check
if nil == RoutingItem.load(name: key) {
if isMainland {
rule.remark = defaultRuleCn[key] ?? rule.remark
} else {
rule.remark = defaultRuleEn[key] ?? rule.remark
}
// create new
rule.store()
}
// if not in list, append
if !list.contains(key) {
list.append(key)
}
}
// load each RoutingItem
for item in list {
guard let routing = RoutingItem.load(name: item) else {
// delete from UserDefaults
RoutingItem.remove(name: item)
continue
}
// append
self.routings.append(routing)
}
print("V2rayRoutings-loadConfig", self.routings.count)
}
static func isDefaultRule(name: String) -> Bool {
return defaultRules.keys.contains(name)
}
// get list from routing server list
static func list() -> [RoutingItem] {
return self.routings
}
static func all() -> [RoutingItem] {
// static reset
var items : [RoutingItem] = []
// load name list from UserDefaults
var list = UserDefaults.getArray(forKey: .routingCustomList)
if list == nil {
list = []
}
// load each V2rayItem
for item in list! {
guard let routing = RoutingItem.load(name: item) else {
// delete from UserDefaults
RoutingItem.remove(name: item)
continue
}
// append
items.append(routing)
}
return items
}
// get count from routing server list
static func count() -> Int {
return self.routings.count
}
// move item to new index
static func move(oldIndex: Int, newIndex: Int) {
if !V2rayRoutings.routings.indices.contains(oldIndex) {
NSLog("index out of range", oldIndex)
return
}
if !V2rayRoutings.routings.indices.contains(newIndex) {
NSLog("index out of range", newIndex)
return
}
let o = self.routings[oldIndex]
self.routings.remove(at: oldIndex)
self.routings.insert(o, at: newIndex)
// update server list UserDefaults
self.saveItemList()
}
// add routing server (by scan qrcode)
static func add(remark: String, json: String) {
var remark_ = remark
if remark.count == 0 {
remark_ = "new routing"
}
// name is : routing. + uuid
let name = "routing." + UUID().uuidString
let routing = RoutingItem(name: name, remark: remark_, json: json)
// save to routing UserDefaults
routing.store()
// just add to mem
self.routings.append(routing)
print("routing", name, self.routings)
// update server list UserDefaults
self.saveItemList()
}
// remove routing server (tmp and UserDefaults and config json file)
static func remove(idx: Int) {
if !V2rayRoutings.routings.indices.contains(idx) {
NSLog("index out of range", idx)
return
}
let routing = V2rayRoutings.routings[idx]
// delete from tmp
self.routings.remove(at: idx)
// delete from routing UserDefaults
RoutingItem.remove(name: routing.name)
// update server list UserDefaults
self.saveItemList()
// if cuerrent item is default
let curName = UserDefaults.get(forKey: .routingSelectedRule)
if curName != nil && routing.name == curName {
UserDefaults.del(forKey: .routingSelectedRule)
}
}
// update server list UserDefaults
static func saveItemList() {
var routingCustomList: Array<String> = []
for item in V2rayRoutings.list() {
routingCustomList.append(item.name)
}
print("routingCustomList", routingCustomList);
UserDefaults.setArray(forKey: .routingCustomList, value: routingCustomList)
}
// load json file data
static func load(idx: Int) -> RoutingItem? {
if !V2rayRoutings.routings.indices.contains(idx) {
NSLog("index out of range", idx)
return nil
}
return self.routings[idx]
}
static func save(routing: RoutingItem) {
// store
routing.store()
// refresh data
for (idx, item) in self.routings.enumerated() {
if item.name == routing.name {
self.routings[idx].remark = routing.remark
break
}
}
}
// get by name
static func getIndex(name: String) -> Int {
for (idx, item) in self.routings.enumerated() {
if item.name == name {
return idx
}
}
return -1
}
}
// ----- routing routing item -----
class RoutingItem: NSObject, NSCoding {
var name: String
var remark: String
var json: String
var domainStrategy: String
var block: String
var proxy: String
var direct: String
// Initializer
init(name: String, remark: String, json: String = "", domainStrategy: String = "AsIs", block: String="", proxy: String="", direct: String="") {
self.name = name
self.remark = remark
self.json = json
self.domainStrategy = domainStrategy
self.block = block
self.proxy = proxy
self.direct = direct
}
// NSCoding required initializer (decoding)
required init(coder decoder: NSCoder) {
self.name = decoder.decodeObject(forKey: "Name") as? String ?? ""
self.remark = decoder.decodeObject(forKey: "Remark") as? String ?? ""
self.json = decoder.decodeObject(forKey: "Json") as? String ?? ""
self.domainStrategy = decoder.decodeObject(forKey: "DomainStrategy") as? String ?? "AsIs"
self.block = decoder.decodeObject(forKey: "Block") as? String ?? ""
self.proxy = decoder.decodeObject(forKey: "Proxy") as? String ?? ""
self.direct = decoder.decodeObject(forKey: "Direct") as? String ?? ""
}
// NSCoding required method (encoding)
func encode(with coder: NSCoder) {
coder.encode(name, forKey: "Name")
coder.encode(remark, forKey: "Remark")
coder.encode(json, forKey: "Json")
coder.encode(domainStrategy, forKey: "DomainStrategy")
coder.encode(block, forKey: "Block")
coder.encode(proxy, forKey: "Proxy")
coder.encode(direct, forKey: "Direct")
}
// Store into UserDefaults
func store() {
let modelData = NSKeyedArchiver.archivedData(withRootObject: self)
UserDefaults.standard.set(modelData, forKey: self.name)
}
func parseRule() -> V2rayRouting {
if defaultRules.keys.contains(self.name) {
return self.parseDefaultSettings()
}
let (res, err) = parseRoutingRuleJson(json: self.json)
if err != nil {
print("parseRule err", err)
}
return res
}
// parse default settings
func parseDefaultSettings() -> V2rayRouting {
var rules: [V2rayRoutingSettingRule] = []
let (blockDomains, blockIps) = parseDomainOrIp(domainIpStr: self.block)
let (proxyDomains, proxyIps) = parseDomainOrIp(domainIpStr: self.proxy)
let (directDomains, directIps) = parseDomainOrIp(domainIpStr: self.direct)
// // rules
var ruleProxyDomain, ruleProxyIp, ruleDirectDomain, ruleDirectIp, ruleBlockDomain, ruleBlockIp, ruleDirectIpDefault, ruleDirectDomainDefault: V2rayRoutingSettingRule?
// proxy
if proxyDomains.count > 0 {
ruleProxyDomain = getRoutingRule(outTag: "proxy", domain: proxyDomains, ip: nil, port: nil)
}
if proxyIps.count > 0 {
ruleProxyIp = getRoutingRule(outTag: "proxy", domain: nil, ip: proxyIps, port: nil)
}
// direct
if directDomains.count > 0 {
ruleDirectDomain = getRoutingRule(outTag: "direct", domain: directDomains, ip: nil, port: nil)
}
if directIps.count > 0 {
ruleDirectIp = getRoutingRule(outTag: "direct", domain: nil, ip: directIps, port: nil)
}
// block
if blockDomains.count > 0 {
ruleBlockDomain = getRoutingRule(outTag: "block", domain: blockDomains, ip: nil, port: nil)
}
if blockIps.count > 0 {
ruleBlockIp = getRoutingRule(outTag: "block", domain: nil, ip: blockIps, port: nil)
}
switch self.name {
case RoutingRuleGlobal:
break
case RoutingRuleLAN:
ruleDirectIpDefault = getRoutingRule(outTag: "direct", domain: nil, ip: ["geoip:private"], port: nil)
ruleDirectDomainDefault = getRoutingRule(outTag: "direct", domain: ["localhost"], ip: nil, port: nil)
break
case RoutingRuleCn:
ruleDirectIpDefault = getRoutingRule(outTag: "direct", domain: nil, ip: ["geoip:cn"], port: nil)
ruleDirectDomainDefault = getRoutingRule(outTag: "direct", domain: ["geosite:cn"], ip: nil, port: nil)
break
case RoutingRuleLANAndCn:
ruleDirectIpDefault = getRoutingRule(outTag: "direct", domain: nil, ip: ["geoip:cn","geoip:private"], port: nil)
ruleDirectDomainDefault = getRoutingRule(outTag: "direct", domain: ["geosite:cn","localhost"], ip: nil, port: nil)
break
default: break
}
// -> -> -> IP -> IP -> IP
//
if ruleBlockDomain != nil {
ruleBlockDomain?.ip = nil
rules.append(ruleBlockDomain!)
}
//
if ruleProxyDomain != nil {
ruleProxyDomain?.ip = nil
rules.append(ruleProxyDomain!)
}
//
if ruleDirectDomain != nil {
ruleDirectDomain!.ip = nil
rules.append(ruleDirectDomain!)
}
// IP
if ruleBlockIp != nil {
ruleBlockIp!.domain = nil
rules.append(ruleBlockIp!)
}
// IP
if ruleProxyIp != nil {
ruleProxyIp!.domain = nil
rules.append(ruleProxyIp!)
}
// IP
if ruleDirectIp != nil {
ruleDirectIp!.domain = nil
rules.append(ruleDirectIp!)
}
//
if ruleDirectIpDefault != nil {
ruleDirectIpDefault!.domain = nil
rules.append(ruleDirectIpDefault!)
}
if ruleDirectDomainDefault != nil {
ruleDirectDomainDefault!.ip = nil
rules.append(ruleDirectDomainDefault!)
}
// ,
var settings = V2rayRouting()
if V2rayRouting.domainStrategy(rawValue: self.domainStrategy) == nil {
settings.domainStrategy = .AsIs
} else {
settings.domainStrategy = V2rayRouting.domainStrategy(rawValue: self.domainStrategy) ?? .AsIs
}
settings.rules = rules
return settings
}
func getRoutingRule(outTag: String, domain:[String]?, ip: [String]?, port:String?) -> V2rayRoutingSettingRule {
var rule = V2rayRoutingSettingRule()
rule.outboundTag = outTag
rule.type = "field"
rule.domain = domain
rule.ip = ip
rule.port = port
return rule
}
func parseDomainOrIp(domainIpStr: String) -> (domains: [String], ips: [String]) {
let all = domainIpStr.split(separator: "\n")
var domains: [String] = []
var ips: [String] = []
for item in all {
let tmp = item.trimmingCharacters(in: .whitespacesAndNewlines)
// is ip
if isIp(str: tmp) || tmp.contains("geoip:") {
ips.append(tmp)
continue
}
// is domain
if tmp.contains("domain:") || tmp.contains("geosite:") {
domains.append(tmp)
continue
}
if isDomain(str: tmp) {
domains.append(tmp)
continue
}
}
print("ips", ips, "domains", domains)
return (domains, ips)
}
func isIp(str: String) -> Bool {
let pattern = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(/[0-9]{2})?$"
if ((str.count == 0) || (str.range(of: pattern, options: .regularExpression) == nil)) {
return false
}
return true
}
func isDomain(str: String) -> Bool {
let pattern = "[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+"
if ((str.count == 0) || (str.range(of: pattern, options: .regularExpression) == nil)) {
return false
}
return true
}
// static load from UserDefaults
static func load(name: String) -> RoutingItem? {
guard let myModelData = UserDefaults.standard.data(forKey: name) else {
print("load userDefault not found:",name)
return nil
}
do {
// unarchivedObject(ofClass:from:)
let result = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(myModelData)
return result as? RoutingItem
} catch let error {
print("load userDefault error:", error)
return nil
}
}
// remove from UserDefaults
static func remove(name: String) {
UserDefaults.standard.removeObject(forKey: name)
}
}
// parse json to V2rayRouting
func parseRoutingRuleJson(json: String) -> (V2rayRouting, err: Error?) {
// utf8
let jsonData = json.data(using: String.Encoding.utf8, allowLossyConversion: false)
if jsonData == nil {
return (V2rayRouting(), nil)
}
let jsonDecoder = JSONDecoder()
var res = V2rayRouting()
var err: Error?
do {
res = try jsonDecoder.decode(V2rayRouting.self, from: jsonData!)
} catch let error {
print("parseJson err",error)
err = error
}
return (res, err)
}

View File

@@ -81,13 +81,6 @@ let jsSourceFormatConfig =
class V2rayConfig: NSObject {
// routing rule tag
enum RoutingRule: Int {
case RoutingRuleGlobal = 0 // Global
case RoutingRuleLAN = 1 // Bypassing the LAN Address
case RoutingRuleCn = 2 // Bypassing mainland address
case RoutingRuleLANAndCn = 3 // Bypassing LAN and mainland address
}
var v2ray: V2rayStruct = V2rayStruct()
var isValid = false
@@ -131,15 +124,6 @@ class V2rayConfig: NSObject {
var streamSecurity = "none" // none|tls|xtls|reality
var securityTls = TlsSettings() // tls|xtls
var securityReality = RealitySettings() // reality
var routingDomainStrategy: V2rayRoutingSetting.domainStrategy = .AsIs
var routingRule: RoutingRule = .RoutingRuleGlobal
let routingProxyDomains = UserDefaults.getArray(forKey: .routingProxyDomains) ?? [];
let routingProxyIps = UserDefaults.getArray(forKey: .routingProxyIps) ?? [];
let routingDirectDomains = UserDefaults.getArray(forKey: .routingDirectDomains) ?? [];
let routingDirectIps = UserDefaults.getArray(forKey: .routingDirectIps) ?? [];
let routingBlockDomains = UserDefaults.getArray(forKey: .routingBlockDomains) ?? [];
let routingBlockIps = UserDefaults.getArray(forKey: .routingBlockIps) ?? [];
private var foundHttpPort = false
private var foundSockPort = false
@@ -161,10 +145,6 @@ class V2rayConfig: NSObject {
self.mux = Int(UserDefaults.get(forKey: .muxConcurrent) ?? "8") ?? 8
self.logLevel = UserDefaults.get(forKey: .v2rayLogLevel) ?? "info"
// routing
self.routingDomainStrategy = V2rayRoutingSetting.domainStrategy(rawValue: UserDefaults.get(forKey: .routingDomainStrategy) ?? "AsIs") ?? .AsIs
self.routingRule = RoutingRule(rawValue: Int(UserDefaults.get(forKey: .routingRule) ?? "0") ?? 0) ?? .RoutingRuleGlobal
}
// combine manual edited data
@@ -292,112 +272,14 @@ class V2rayConfig: NSObject {
// ------------------------------------- outbound end ---------------------------------------------
// ------------------------------------- routing start --------------------------------------------
self.routing.settings.domainStrategy = self.routingDomainStrategy
var rules: [V2rayRoutingSettingRule] = []
// rules
var ruleProxyDomain, ruleProxyIp, ruleDirectDomain, ruleDirectIp, ruleBlockDomain, ruleBlockIp, ruleDirectIpDefault, ruleDirectDomainDefault: V2rayRoutingSettingRule?
print("ruleBlockDomain",self.routingBlockDomains)
// proxy
if self.routingProxyDomains.count > 0 {
ruleProxyDomain = getRoutingRule(outTag: "proxy", domain: self.routingProxyDomains, ip: nil, port: nil)
let routingRule = UserDefaults.get(forKey: .routingSelectedRule) ?? RoutingRuleGlobal
let rule = RoutingItem.load(name: routingRule)
if rule != nil{
self.v2ray.routing = rule!.parseRule()
}
if self.routingProxyIps.count > 0 {
ruleProxyIp = getRoutingRule(outTag: "proxy", domain: nil, ip: self.routingProxyIps, port: nil)
}
// direct
if self.routingDirectDomains.count > 0 {
ruleDirectDomain = getRoutingRule(outTag: "direct", domain: self.routingDirectDomains, ip: nil, port: nil)
}
if self.routingDirectIps.count > 0 {
ruleDirectIp = getRoutingRule(outTag: "direct", domain: nil, ip: self.routingDirectIps, port: nil)
}
// block
if self.routingBlockDomains.count > 0 {
ruleBlockDomain = getRoutingRule(outTag: "block", domain: self.routingBlockDomains, ip: nil, port: nil)
}
if self.routingBlockIps.count > 0 {
ruleBlockIp = getRoutingRule(outTag: "block", domain: nil, ip: self.routingBlockIps, port: nil)
}
switch self.routingRule {
case .RoutingRuleGlobal:
break
case .RoutingRuleLAN:
ruleDirectIpDefault = getRoutingRule(outTag: "direct", domain: nil, ip: ["geoip:private"], port: nil)
ruleDirectDomainDefault = getRoutingRule(outTag: "direct", domain: ["localhost"], ip: nil, port: nil)
break
case .RoutingRuleCn:
ruleDirectIpDefault = getRoutingRule(outTag: "direct", domain: nil, ip: ["geoip:cn"], port: nil)
ruleDirectDomainDefault = getRoutingRule(outTag: "direct", domain: ["geosite:cn"], ip: nil, port: nil)
break
case .RoutingRuleLANAndCn:
ruleDirectIpDefault = getRoutingRule(outTag: "direct", domain: nil, ip: ["geoip:cn","geoip:private"], port: nil)
ruleDirectDomainDefault = getRoutingRule(outTag: "direct", domain: ["geosite:cn","localhost"], ip: nil, port: nil)
break
}
// -> -> -> IP -> IP -> IP
//
if ruleBlockDomain != nil {
ruleBlockDomain?.ip = nil
rules.append(ruleBlockDomain!)
}
//
if ruleProxyDomain != nil {
ruleProxyDomain?.ip = nil
rules.append(ruleProxyDomain!)
}
//
if ruleDirectDomain != nil {
ruleDirectDomain!.ip = nil
rules.append(ruleDirectDomain!)
}
// IP
if ruleBlockIp != nil {
ruleBlockIp!.domain = nil
rules.append(ruleBlockIp!)
}
// IP
if ruleProxyIp != nil {
ruleProxyIp!.domain = nil
rules.append(ruleProxyIp!)
}
// IP
if ruleDirectIp != nil {
ruleDirectIp!.domain = nil
rules.append(ruleDirectIp!)
}
//
if ruleDirectIpDefault != nil {
ruleDirectIpDefault!.domain = nil
rules.append(ruleDirectIpDefault!)
}
if ruleDirectDomainDefault != nil {
ruleDirectDomainDefault!.ip = nil
rules.append(ruleDirectDomainDefault!)
}
// ,
//
self.routing.settings.rules = rules
// set v2ray routing
self.v2ray.routing = self.routing
// ------------------------------------- routing end ----------------------------------------------
}
func getRoutingRule(outTag: String, domain:[String]?, ip: [String]?, port:String?) -> V2rayRoutingSettingRule {
var rule = V2rayRoutingSettingRule()
rule.outboundTag = outTag
rule.type = "field"
rule.domain = domain
rule.ip = ip
rule.port = port
return rule
}
func checkManualValid() {
defer {
if self.error != "" {

View File

@@ -60,11 +60,6 @@ struct V2rayStats: Codable {
}
struct V2rayRouting: Codable {
// var strategy: String = "rules"
var settings: V2rayRoutingSetting = V2rayRoutingSetting()
}
struct V2rayRoutingSetting: Codable {
enum domainStrategy: String, Codable {
case AsIs
case IPIfNonMatch