diff options
| author | Haishan <[email protected]> | 2018-10-26 01:02:59 +0800 |
|---|---|---|
| committer | Haishan <[email protected]> | 2018-10-27 14:28:25 +0800 |
| commit | abfab4f1adb40e3463fa9e90aff3e92bfdf03693 (patch) | |
| tree | 46347cc195d59a2b0622752c71742b82476cce69 /src | |
| parent | 4e7efe2accc67a63f9e3bc4ed4365acefb4fb883 (diff) | |
ui(proxy): new UI for proxy page
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/APIConfig.js | 23 | ||||
| -rw-r--r-- | src/components/APIConfig.module.scss | 4 | ||||
| -rw-r--r-- | src/components/Proxies.js | 73 | ||||
| -rw-r--r-- | src/components/Proxies.module.scss | 40 | ||||
| -rw-r--r-- | src/components/Proxy.js | 117 | ||||
| -rw-r--r-- | src/components/Proxy.module.scss | 32 | ||||
| -rw-r--r-- | src/components/ProxyGroup.js | 80 | ||||
| -rw-r--r-- | src/components/ProxyGroup.module.scss | 20 | ||||
| -rw-r--r-- | src/components/ProxyLatency.js | 44 | ||||
| -rw-r--r-- | src/components/ProxyLatency.module.scss | 8 | ||||
| -rw-r--r-- | src/components/StyleGuide.js | 11 | ||||
| -rw-r--r-- | src/ducks/proxies.js | 42 | ||||
| -rw-r--r-- | src/svg/auto.svg | 3 | ||||
| -rw-r--r-- | src/svg/fallback.svg | 3 | ||||
| -rw-r--r-- | src/svg/ss.svg | 5 | ||||
| -rw-r--r-- | src/svg/vmess.svg | 10 |
16 files changed, 324 insertions, 191 deletions
diff --git a/src/components/APIConfig.js b/src/components/APIConfig.js index ce2b7fe..4e85a69 100644 --- a/src/components/APIConfig.js +++ b/src/components/APIConfig.js @@ -33,6 +33,10 @@ class APIConfig extends Component { secret: this.props.apiConfig.secret }; + componentDidMount() { + this.content.focus(); + } + handleInputOnChange = e => { const target = e.target; const { name } = target; @@ -46,15 +50,30 @@ class APIConfig extends Component { this.setState({ [name]: value }); }; - handleConfirmOnClick = () => { + updateClashAPIConfig() { const { hostname, port, secret } = this.state; this.props.updateClashAPIConfig({ hostname, port, secret }); + } + + handleConfirmOnClick = () => { + this.updateClashAPIConfig(); + }; + + handleContentOnKeyDown = e => { + // enter keyCode is 13 + if (e.keyCode !== 13) return; + this.updateClashAPIConfig(); }; render() { const { hostname, port, secret } = this.state; return ( - <div className={s0.root}> + <div + className={s0.root} + ref={e => (this.content = e)} + tabIndex="1" + onKeyDown={this.handleContentOnKeyDown} + > <div className={s0.header}>RESTful API config for Clash</div> <div className={s0.body}> <div className={s0.group}> diff --git a/src/components/APIConfig.module.scss b/src/components/APIConfig.module.scss index d723016..0edf1b6 100644 --- a/src/components/APIConfig.module.scss +++ b/src/components/APIConfig.module.scss @@ -1,5 +1,7 @@ .root { - // + &:focus { + outline: none; + } } .header { diff --git a/src/components/Proxies.js b/src/components/Proxies.js index f6d7caf..98876ae 100644 --- a/src/components/Proxies.js +++ b/src/components/Proxies.js @@ -2,22 +2,24 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import ContentHeader from 'c/ContentHeader'; -import Proxy from 'c/Proxy'; +import ProxyGroup from 'c/ProxyGroup'; import Button from 'c/Button'; -import cx from 'classnames'; import s0 from 'c/Proxies.module.scss'; -const th = cx(s0.row, s0.th, 'border-bottom'); -// const colItem = cx(s0.colItem, 'border-bottom'); - import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import { getUserProxies, fetchProxies, requestDelayAll } from 'd/proxies'; +import { + getUserProxies, + getProxyGroupNames, + fetchProxies, + requestDelayAll +} from 'd/proxies'; function mapStateToProps(s) { return { - proxies: getUserProxies(s) + proxies: getUserProxies(s), + groupNames: getProxyGroupNames(s) }; } @@ -30,7 +32,7 @@ function mapDispatchToProps(dispatch) { class Proxies extends Component { static propTypes = { - proxies: PropTypes.object.isRequired, + groupNames: PropTypes.array.isRequired, fetchProxies: PropTypes.func.isRequired, requestDelayAll: PropTypes.func.isRequired }; @@ -40,64 +42,27 @@ class Proxies extends Component { } render() { - const { proxies, requestDelayAll } = this.props; - + const { groupNames, requestDelayAll } = this.props; return ( <div> <ContentHeader title="Proxies" /> - - <div className={s0.root}> + <div> <div className={s0.btnGroup}> <Button label="Test Latency" onClick={requestDelayAll} /> </div> - <div className={th}> - <div className={s0.col1}>Name</div> - <div className={s0.col2}>Type</div> - <div className={s0.col3}>All</div> - </div> - - <div> - {Object.keys(proxies).map(k => { - const o = proxies[k]; - return <ProxyRow name={k} key={k} {...o} />; - })} - </div> + {groupNames.map(groupName => { + return ( + <div className={s0.group} key={groupName}> + <ProxyGroup name={groupName} /> + </div> + ); + })} </div> </div> ); } } -class ProxyRow extends Component { - static propTypes = { - name: PropTypes.string.isRequired, - type: PropTypes.string.isRequired, - all: PropTypes.array, - now: PropTypes.string - }; - - render() { - const { name, type, all, now } = this.props; - return ( - <div className={s0.row}> - <div className={s0.col1}>{name}</div> - <div className={s0.col2}>{type}</div> - {all ? ( - <div className={s0.col3 + ' border-left'}> - {all.map(p => { - return ( - <div className={s0.colItem} key={p}> - <Proxy name={p} parentName={name} checked={p === now} /> - </div> - ); - })} - </div> - ) : null} - </div> - ); - } -} - export default connect( mapStateToProps, mapDispatchToProps diff --git a/src/components/Proxies.module.scss b/src/components/Proxies.module.scss index 3b11056..9a73ee6 100644 --- a/src/components/Proxies.module.scss +++ b/src/components/Proxies.module.scss @@ -1,47 +1,13 @@ -$heightHeader: 76px; - .root { - color: #eee; - padding: 10px 40px; - height: calc(100vh - #{$heightHeader}); - overflow: scroll; } -.row { - display: flex; -} - -.th { - font-weight: bold; -} - -.col1 { - width: 150px; - padding: 8px; - display: flex; - align-items: center; -} - -.col2 { - width: 150px; - padding: 8px; - display: flex; - align-items: center; -} - -.col3 { - width: 350px; - margin: 16px 8px; - padding-left: 20px; -} - -.colItem { - padding: 6px; +.group { + padding: 10px 40px; } .btnGroup { color: #eee; - // padding: 10px 40px; + padding: 0 40px; display: flex; justify-content: flex-end; } diff --git a/src/components/Proxy.js b/src/components/Proxy.js index b3d071b..2bb9435 100644 --- a/src/components/Proxy.js +++ b/src/components/Proxy.js @@ -1,92 +1,71 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import s0 from 'c/Proxy.module.scss'; +import Icon from 'c/Icon'; +import ProxyLatency from 'c/ProxyLatency'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import { getDelay, switchProxy, requestDelayForProxy } from 'd/proxies'; +import globe from 's/globe.svg'; +import ss from 's/ss.svg'; +import vmess from 's/vmess.svg'; +import auto from 's/auto.svg'; +import fallback from 's/fallback.svg'; -const mapStateToProps = state => { - const delay = getDelay(state); - return { delay }; -}; +import s0 from './Proxy.module.scss'; -const mapDispatchToProps = dispatch => { - return { - switchProxy: bindActionCreators(switchProxy, dispatch), - requestDelay: bindActionCreators(requestDelayForProxy, dispatch) - }; +import { connect } from 'react-redux'; +import { getDelay, getUserProxies } from 'd/proxies'; + +const colors = { + Vmess: '#ca3487', + Shadowsocks: '#1a7dc0', + Socks5: '#2a477a', + URLTest: '#3483e8', + Fallback: '#3483e8' }; -const colorMap = { - good: '#67C23A', - normal: '#E6A23C', - bad: '#F56C6C', - na: '#909399' +const icons = { + Vmess: vmess.id, + Shadowsocks: ss.id, + Socks5: globe.id, + URLTest: auto.id, + Fallback: fallback.id }; -class Proxy extends Component { - static propTypes = { - name: PropTypes.string.isRequired, - parentName: PropTypes.string, - checked: PropTypes.bool, - switchProxy: PropTypes.func, - requestDelay: PropTypes.func, - delay: PropTypes.object - }; +// typeof Proxy = 'Shadowsocks' | 'Vmess' | 'Socks5'; - handleRadioOnChange = () => { - const { name, parentName, checked, switchProxy } = this.props; - if (checked) return; - switchProxy(parentName, name); +const mapStateToProps = s => { + return { + proxies: getUserProxies(s), + delay: getDelay(s) }; +}; - render() { - const { name, parentName, checked, delay } = this.props; - const id = parentName + ':' + name; - const latency = delay[name] || 0; - return ( - <label className={s0.Proxy} htmlFor={id}> - <input - type="radio" - id={id} - checked={checked} - value={name} - onChange={this.handleRadioOnChange} - /> - <div className={s0.name}>{name}</div> - <LatencyLabel val={latency} /> - </label> - ); - } -} +const mapDispatchToProps = null; -class LatencyLabel extends Component { +class Proxy extends Component { static propTypes = { - val: PropTypes.number + now: PropTypes.bool, + delay: PropTypes.object, + proxies: PropTypes.object, + name: PropTypes.string }; render() { - const { val } = this.props; - let bg = colorMap.na; + const { name, proxies, delay, now } = this.props; + const latency = delay[name]; + const proxy = proxies[name]; + const color = now ? colors[proxy.type] : '#555'; + const iconId = icons[proxy.type]; - if (val < 100) { - bg = colorMap.good; - } else if (val < 300) { - bg = colorMap.normal; - } else { - bg = colorMap.bad; - } - const style = { background: bg }; - if (val === 0 || !val) { - style.opacity = '0'; - style.visibility = 'hidden'; - } return ( - <div className={s0.LatencyLabel} style={style}> - <div>{val}</div> - <div>ms</div> + <div className={s0.proxy}> + <div className={s0.left} style={{ color }}> + <Icon id={iconId} width={80} height={80} /> + </div> + <div className={s0.right}> + <div className={s0.proxyName}>{name}</div> + {latency ? <ProxyLatency latency={latency} /> : null} + </div> </div> ); } diff --git a/src/components/Proxy.module.scss b/src/components/Proxy.module.scss index 735c348..20cb494 100644 --- a/src/components/Proxy.module.scss +++ b/src/components/Proxy.module.scss @@ -1,22 +1,28 @@ -.Proxy { +.proxy { display: flex; - align-items: center; + cursor: pointer; - .name { - flex: 1; - // width: 100px; - padding-left: 10px; + svg { + transition: transform 0.4s ease, color 0.4s ease; + } + &:hover { + svg { + transform: scale(1.1); + color: #aaa; + } } } -.LatencyLabel { +.left { display: flex; align-items: center; - padding: 5px; - border-radius: 5px; - background: #e6a23c; + justify-content: center; +} - div:nth-child(2) { - padding-left: 4px; - } +.right { + padding-left: 20px; +} + +.proxyName { + margin: 10px 0; } diff --git a/src/components/ProxyGroup.js b/src/components/ProxyGroup.js new file mode 100644 index 0000000..b697b27 --- /dev/null +++ b/src/components/ProxyGroup.js @@ -0,0 +1,80 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import memoize from 'memoize-one'; + +import Proxy from 'c/Proxy'; + +import s0 from './ProxyGroup.module.scss'; + +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { getUserProxies, switchProxy } from 'd/proxies'; + +const mapStateToProps = s => { + return { + proxies: getUserProxies(s) + }; +}; + +const mapDispatchToProps = dispatch => { + return { + switchProxy: bindActionCreators(switchProxy, dispatch) + }; +}; + +// should move this to sth like constants.js +// const userProxyTypes = ['Shadowsocks', 'Vmess', 'Socks5']; + +class ProxyGroup extends Component { + static propTypes = { + // group name + name: PropTypes.string.isRequired, + proxies: PropTypes.object, + switchProxy: PropTypes.func + }; + + reOrderProxies = memoize((list, now) => { + const a = [now]; + list.forEach(i => i !== now && a.push(i)); + return a; + }); + + render() { + const { name, proxies, switchProxy } = this.props; + const group = proxies[name]; + let list; + if (group.all) { + list = this.reOrderProxies(group.all, group.now); + } else { + list = [group.now]; + } + return ( + <div className={s0.group}> + <div className={s0.header}> + <h2> + <span>{name}</span> + <span>{group.type}</span> + </h2> + </div> + <div className={s0.list}> + {list.map(proxyName => { + return ( + <div + className={s0.proxy} + key={proxyName} + onClick={() => switchProxy(name, proxyName)} + > + <Proxy name={proxyName} now={proxyName === group.now} /> + </div> + ); + })} + </div> + </div> + ); + } +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(ProxyGroup); diff --git a/src/components/ProxyGroup.module.scss b/src/components/ProxyGroup.module.scss new file mode 100644 index 0000000..43cbdaa --- /dev/null +++ b/src/components/ProxyGroup.module.scss @@ -0,0 +1,20 @@ +.header { + h2 { + span:nth-child(2) { + font-size: 12px; + color: #777; + font-weight: normal; + margin: 0 0.3em; + } + } +} + +.list { + display: flex; + flex-wrap: wrap; +} + +.proxy { + width: 300px; + padding: 10px 5px; +} diff --git a/src/components/ProxyLatency.js b/src/components/ProxyLatency.js new file mode 100644 index 0000000..5d1aaee --- /dev/null +++ b/src/components/ProxyLatency.js @@ -0,0 +1,44 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import s0 from './ProxyLatency.module.scss'; + +const colorMap = { + good: '#67C23A', + normal: '#E6A23C', + bad: '#F56C6C', + na: '#909399' +}; + +class C extends Component { + static propTypes = { + latency: PropTypes.shape({ + number: PropTypes.number, + error: PropTypes.string + }) + }; + + render() { + const { latency } = this.props; + const { number, error } = latency; + let bg; + + if (error !== '') { + bg = colorMap.na; + } else if (number < 200) { + bg = colorMap.good; + } else if (number < 400) { + bg = colorMap.normal; + } else { + bg = colorMap.bad; + } + + return ( + <span className={s0.proxyLatency} style={{ color: bg }}> + {error !== '' ? <span>{error}</span> : <span>{number} ms</span>} + </span> + ); + } +} + +export default C; diff --git a/src/components/ProxyLatency.module.scss b/src/components/ProxyLatency.module.scss new file mode 100644 index 0000000..61485bd --- /dev/null +++ b/src/components/ProxyLatency.module.scss @@ -0,0 +1,8 @@ +.proxyLatency { + border-radius: 20px; + margin: 10px 0; + // padding: 3px 5px; + padding: 3px 0; + color: #eee; + // background: #ccc; +} diff --git a/src/components/StyleGuide.js b/src/components/StyleGuide.js index 3fe142d..0bb3813 100644 --- a/src/components/StyleGuide.js +++ b/src/components/StyleGuide.js @@ -5,8 +5,9 @@ import ToggleSwitch from 'c/ToggleSwitch'; import Input from 'c/Input'; import Switch from 'c/Switch'; import Button from 'c/Button'; -import Modal from 'c/Modal'; -import APIConfig from 'c/APIConfig'; +// import Modal from 'c/Modal'; +// import APIConfig from 'c/APIConfig'; +import Proxy2 from 'c/Proxy2'; const paneStyle = { padding: '20px 0' @@ -37,6 +38,9 @@ class StyleGuide extends PureComponent { return ( <div> <Pane> + <Proxy2 /> + </Pane> + <Pane> <Switch /> </Pane> <Pane> @@ -53,9 +57,6 @@ class StyleGuide extends PureComponent { <Pane> <Button label="Test Latency" /> </Pane> - <Modal isOpen={true} onRequestClose={() => {}}> - <APIConfig /> - </Modal> </div> ); } diff --git a/src/ducks/proxies.js b/src/ducks/proxies.js index dc38e73..83f6b00 100644 --- a/src/ducks/proxies.js +++ b/src/ducks/proxies.js @@ -1,10 +1,15 @@ import { createSelector } from 'reselect'; import * as proxiesAPI from 'a/proxies'; +// see all types: +// https://github.com/Dreamacro/clash/blob/master/constant/adapters.go + const ProxyTypeBuiltin = ['DIRECT', 'GLOBAL', 'REJECT']; +const ProxyGroupTypes = ['Fallback', 'URLTest', 'Selector']; export const getProxies = s => s.proxies.proxies; export const getDelay = s => s.proxies.delay; +export const getProxyGroupNames = s => s.proxies.groupNames; export const getUserProxies = createSelector(getProxies, proxies => { let o = {}; for (const prop in proxies) { @@ -19,6 +24,21 @@ const CompletedFetchProxies = 'proxies/CompletedFetchProxies'; const OptimisticSwitchProxy = 'proxies/OptimisticSwitchProxy'; const CompletedRequestDelayForProxy = 'proxies/CompletedRequestDelayForProxy'; +function retrieveGroupNamesFrom(proxies) { + const groupNames = []; + for (const prop in proxies) { + // not builtin proxy + if (ProxyTypeBuiltin.indexOf(prop) < 0) { + const p = proxies[prop]; + // is group + if (ProxyGroupTypes.indexOf(p.type) >= 0) { + groupNames.push(prop); + } + } + } + return groupNames; +} + export function fetchProxies() { return async (dispatch, getState) => { // TODO handle errors @@ -30,9 +50,12 @@ export function fetchProxies() { // TODO show loading animation? const json = await proxiesAPI.fetchProxies(); let { proxies = {} } = json; + + const groupNames = retrieveGroupNamesFrom(proxies); + dispatch({ type: CompletedFetchProxies, - payload: { proxies } + payload: { proxies, groupNames } }); dispatch(requestDelayAll()); }; @@ -73,16 +96,19 @@ export function switchProxy(name1, name2) { function requestDelayForProxyOnce(name) { return async (dispatch, getState) => { const res = await proxiesAPI.requestDelayForProxy(name); + let error = ''; if (res.ok === false) { - console.log('Error', res.statusText); - return; + error = res.statusText; } const { delay } = await res.json(); const delayPrev = getDelay(getState()); const delayNext = { ...delayPrev, - [name]: delay + [name]: { + error, + number: delay + } }; dispatch({ @@ -92,11 +118,6 @@ function requestDelayForProxyOnce(name) { }; } -// const proxyTypeListTo = [ -// 'Vmess', -// 'Shadowsocks' -// ]; - export function requestDelayForProxy(name) { return async dispatch => { await dispatch(requestDelayForProxyOnce(name)); @@ -122,7 +143,8 @@ export function requestDelayAll() { const initialState = { proxies: {}, - delay: {} + delay: {}, + groupNames: [] }; export default function reducer(state = initialState, { type, payload }) { diff --git a/src/svg/auto.svg b/src/svg/auto.svg new file mode 100644 index 0000000..e98f936 --- /dev/null +++ b/src/svg/auto.svg @@ -0,0 +1,3 @@ +<svg width="326" height="326" viewBox="0 0 326 326" xmlns="http://www.w3.org/2000/svg"> + <path d="M127.773 130.955c-10.107 0-16.796-6.25-16.796-15.771 0-9.229 6.591-14.746 18.212-15.284l15.235-.683v-4.98c0-7.569-4.346-11.67-12.403-11.67-6.64 0-11.23 2.831-12.695 7.91h-6.347C113.955 82.615 121.865 77 132.02 77c11.915 0 18.848 6.25 18.848 16.943v36.133h-6.152v-8.3h-.684c-2.49 5.908-8.3 9.18-16.26 9.18zm1.563-5.664c8.496 0 15.088-5.86 15.088-13.428v-7.324l-14.795.635c-7.861.342-12.06 3.857-12.06 10.01 0 6.25 4.492 10.107 11.767 10.107zm85.693-47.852v52.588h-6.103v-9.472h-.635c-2.88 6.64-9.033 10.351-17.187 10.351-11.475 0-17.676-6.787-17.676-19.287V77.44h6.396v32.569c0 10.303 4.15 15.137 12.988 15.137 9.424 0 15.82-6.69 15.82-16.456V77.44h6.397zM124.16 182.768h6.397v13.671h22.021v5.42h-22.021v31.787c0 6.495 4.492 10.01 12.841 10.01 3.614 0 7.471-.146 9.18-.39v5.468c-1.562.293-6.494.586-9.521.586-12.647 0-18.897-5.127-18.897-15.478v-31.983H110v-5.42h14.16v-13.671zm70.069 67.138c-13.575 0-22.266-9.082-22.266-23.242v-7.812c0-14.16 8.691-23.243 22.266-23.243 13.574 0 22.265 9.082 22.265 23.243v7.812c0 14.16-8.691 23.242-22.265 23.242zm0-48.584c-9.766 0-15.87 6.983-15.87 18.36v6.152c0 11.377 6.104 18.36 15.87 18.36 9.765 0 15.869-6.983 15.869-18.36v-6.152c0-11.377-6.104-18.36-15.87-18.36z" fill="currentColor" fill-rule="evenodd"/> +</svg> diff --git a/src/svg/fallback.svg b/src/svg/fallback.svg new file mode 100644 index 0000000..3ab822f --- /dev/null +++ b/src/svg/fallback.svg @@ -0,0 +1,3 @@ +<svg width="326" height="326" viewBox="0 0 326 326" xmlns="http://www.w3.org/2000/svg"> + <path d="M61.16 139.535V92.367H47v-5.42h14.16v-6.054c0-9.327 6.543-14.258 19.043-14.258 3.711 0 9.229.342 11.328.683v5.371c-2.783-.341-7.763-.585-11.328-.585-8.3 0-12.646 3.076-12.646 8.984v5.86H91.53v5.42H67.557v47.167H61.16zm64.453.928c-10.107 0-16.797-6.25-16.797-15.772 0-9.228 6.592-14.746 18.213-15.283l15.235-.683v-4.98c0-7.57-4.346-11.67-12.403-11.67-6.64 0-11.23 2.831-12.695 7.91h-6.348c.977-7.862 8.887-13.477 19.043-13.477 11.914 0 18.848 6.25 18.848 16.943v36.133h-6.152v-8.3h-.684c-2.49 5.907-8.3 9.179-16.26 9.179zm1.563-5.664c8.496 0 15.088-5.86 15.088-13.428v-7.324l-14.795.635c-7.862.341-12.06 3.857-12.06 10.01 0 6.25 4.491 10.107 11.767 10.107zm87.793 4.736h-42.92v-5.176h18.506V71.176H171.95V66h25v68.36h18.018v5.175zm61.816 0h-42.92v-5.176h18.506V71.176h-18.603V66h25v68.36h18.017v5.175zM70.78 259.415c-7.47 0-13.086-2.93-16.162-8.448h-.879v7.568h-6.103V185h6.396v28.71h.88c2.49-5.517 8.251-8.642 15.868-8.642 12.207 0 20.508 8.985 20.508 22.12v10.107c0 13.135-8.3 22.12-20.508 22.12zm-1.416-5.714c9.424 0 15.528-7.08 15.528-17.92v-7.031c0-10.84-6.104-17.92-15.528-17.92-9.375 0-15.478 7.031-15.478 17.92v7.031c0 10.889 6.103 17.92 15.478 17.92zm56.25 5.762c-10.107 0-16.797-6.25-16.797-15.772 0-9.228 6.592-14.746 18.213-15.283l15.235-.683v-4.98c0-7.57-4.346-11.67-12.403-11.67-6.64 0-11.23 2.831-12.695 7.91h-6.348c.977-7.862 8.887-13.477 19.043-13.477 11.914 0 18.848 6.25 18.848 16.943v36.133h-6.152v-8.3h-.684c-2.49 5.907-8.3 9.179-16.26 9.179zm1.563-5.664c8.496 0 15.088-5.86 15.088-13.428v-7.324l-14.795.635c-7.862.341-12.06 3.857-12.06 10.01 0 6.25 4.491 10.107 11.767 10.107zm86.084-32.373h-6.348c-1.074-6.25-6.885-10.596-14.062-10.596-10.01 0-15.577 6.69-15.577 18.75v5.322c0 12.06 5.567 18.75 15.577 18.75 7.177 0 12.988-4.345 14.062-10.595h6.348c-1.367 9.863-9.375 16.357-20.264 16.357-13.818 0-22.119-8.789-22.119-23.437v-7.471c0-14.649 8.3-23.438 22.12-23.438 10.888 0 18.896 6.494 20.263 16.358zm65.38 37.11h-7.714l-19.043-27.833-8.106 7.91v19.922h-6.396V185h6.396v45.703h.586l24.707-24.756h8.106l-20.801 20.362 22.266 32.226z" fill="currentColor" fill-rule="evenodd"/> +</svg> diff --git a/src/svg/ss.svg b/src/svg/ss.svg new file mode 100644 index 0000000..4311956 --- /dev/null +++ b/src/svg/ss.svg @@ -0,0 +1,5 @@ +<svg width="326" height="326" viewBox="0 0 326 326" xmlns="http://www.w3.org/2000/svg"> + <g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke="currentColor" stroke-width="7"> + <path d="M144.176 233.92l30.67 9.576-30.67 46.467zM145.414 209.789l99.04 30.2 48.29-202.404L29.589 171.717l80.11 27.613 121.73-96.816z"/> + </g> +</svg> diff --git a/src/svg/vmess.svg b/src/svg/vmess.svg new file mode 100644 index 0000000..00f7709 --- /dev/null +++ b/src/svg/vmess.svg @@ -0,0 +1,10 @@ +<svg width="326" height="326" viewBox="0 0 326 326" xmlns="http://www.w3.org/2000/svg"> + <g stroke="currentColor" stroke-width="8" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"> + <path d="M318.342 151.555L140.45 284.581l22.442 22.452z"/> + <path d="M80.768 224.894l32.993 33.003 204.581-106.34z"/> + <path d="M7.442 151.557h310.902L47.25 191.366z"/> + <g> + <path d="M318.337 151.439L47.52 111.555l33.483-33.59zM318.336 151.436L113.971 44.896l26.66-26.72z"/> + </g> + </g> +</svg> |
