Compare commits

...

17 Commits

Author SHA1 Message Date
yutent 449353ce84 修改代码目录 2024-01-29 18:24:33 +08:00
yutent fa38bcb985 fixed icon 2024-01-26 13:22:54 +08:00
yutent da94875407 修复一处语法错误 2023-09-11 14:55:19 +08:00
yutent 842793ff79 修复在新设备上死循环的bug 2023-09-11 14:30:27 +08:00
yutent 1760fbd212 设置应用的窗口名和标题 2023-09-07 13:37:32 +08:00
yutent ff2be3b55f 3.0.1 2023-09-06 17:42:35 +08:00
yutent d91bd574f0 修复初始化异常的bug 2023-09-06 17:40:28 +08:00
yutent 26a340ac56 移除调试代码 2023-09-06 12:20:42 +08:00
yutent 409e5f1dde 修复一处异步逻辑 2023-09-05 18:52:29 +08:00
yutent 80d328eab4 update 2023-09-05 15:11:37 +08:00
yutent b15acfd5fe fixed 2023-09-05 14:53:10 +08:00
yutent ea5cc8f5c0 remove deb 2023-09-05 14:52:49 +08:00
yutent cd14bb788b 增加打包配置 2023-09-05 14:47:35 +08:00
yutent 8bde4b078c 完成重构 2023-09-05 14:17:37 +08:00
yutent 419a13e4e6 一大波更新 2023-09-04 19:21:47 +08:00
yutent 74f4cd22d4 一大波更新 2023-09-01 18:39:40 +08:00
yutent 4a80a18cc3 修改为python + gtk开发 2023-08-31 18:57:09 +08:00
70 changed files with 1364 additions and 1255 deletions

13
.gitignore vendored
View File

@ -3,14 +3,11 @@
.LSOverride .LSOverride
.idea .idea
._* ._*
*.deb
.Spotlight-V100 .Spotlight-V100
.Trashes .Trashes
we
build __pycache__
build/** *.txt
node_modules
node_modules/**
package-lock.json

View File

@ -1,5 +1,7 @@
# Hosts切换器 # Hosts切换器
> macos & linux专用。windows自己找别的。 > linux专用。 macos/windows自己找别的。
![preview](./preview/image.png)
## 安装 ## 安装
@ -8,6 +10,9 @@
## Update Logs ## Update Logs
### v3.0.0 - 2023-09-05
- 放弃electron, 改用`gtk3+webkit2`开发
### v2.0.0 - 2022-03-31 ### v2.0.0 - 2022-03-31
- 分离electron, 独立打包 - 分离electron, 独立打包

32
build.sh Executable file
View File

@ -0,0 +1,32 @@
#!/bin/bash
if [ -d unpack ]; then
sudo rm -rf unpack
fi
version="3.0.6"
mkdir -p unpack/DEBIAN
cp debian/control unpack/DEBIAN/
cp -r usr unpack/
mkdir -p unpack/usr/share/icons/hicolor/128x128/apps
mkdir -p unpack/usr/share/icons/hicolor/256x256/apps
cp icons/128x128.png unpack/usr/share/icons/hicolor/128x128/apps/hosts-switch.png
cp icons/256x256.png unpack/usr/share/icons/hicolor/256x256/apps/hosts-switch.png
cd unpack
find usr -type f | xargs md5sum > DEBIAN/md5sums
_size=$(du -d 0 usr | cut -f1)
sed -i "s/{{size}}/${_size}/" DEBIAN/control
sed -i "s/{{version}}/${version}/" DEBIAN/control
cd ..
sudo chown -R root:root unpack/
dpkg-deb -b unpack/ "hosts-switch-${version}.deb"
sudo rm -rf unpack

12
debian/control vendored Normal file
View File

@ -0,0 +1,12 @@
Package: hosts-switch
Version: {{version}}
Section: develop
Architecture: all
Priority: optional
Maintainer: Yutent <yutent.io@gmail.com>
Installed-Size: {{size}}
Depends: python3-webengine-gtk3 (>=0.4.0)
Homepage: https://git.wkit.fun/appcat/hosts-switch/
Author: yutent
Description: 一个hosts绑定切换工具
提供了一个类似域名DNS解析的操作界面.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

View File

@ -1,21 +0,0 @@
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" x="0" y="0"
viewBox="0 0 1000 1000" xml:space="preserve">
<style>
.st1 {
fill: #fff
}
.st2 {
fill: #0195de
}
</style>
<path
d="M895.4 974.5H104.6c-43.6-.1-79-35.5-79.1-79.1V104.6c.1-43.6 35.5-79 79.1-79.1h790.8c43.6.1 79 35.5 79.1 79.1v790.8c-.2 43.6-35.5 79-79.1 79.1z"
fill="#00a0ef" />
<path class="st1"
d="M155 370.5h90.9c19.8 0 35.6 2 47.4 4 14.6 4.1 27.6 12.3 37.6 23.7 10.2 11.7 18.2 25 23.7 39.5 5.9 15.8 7.9 35.6 7.9 59.3 0 19.8-2 37.6-7.9 53.4-5.2 16.2-14 31.1-25.7 43.5-9.9 9.7-22.2 16.5-35.6 19.8-11.9 4-25.7 5.9-43.5 5.9h-94.9V370.5h.1zM406.1 619.6V370.5h49.4l100.7 166.1V370.5h47.5v249.1h-49.5L453.5 457.5v162.1zM643.2 538.6l49.4-4c2 15.8 7.9 27.7 17.8 35.6 10.1 8 22.7 12.2 35.6 11.9 12.6.6 25.1-2.9 35.6-9.9 7.3-5.7 11.7-14.4 11.9-23.7 0-5.9-2-9.9-5.9-15.8-4-4-9.9-7.9-17.8-9.9-5.9-2-19.8-5.9-39.5-9.9-20.5-4.1-39.9-12.1-57.3-23.7-15.1-12-23.8-30.2-23.7-49.4.5-12.5 3.9-24.7 9.9-35.6 7.6-11.6 18.7-20.6 31.6-25.7 15.8-6 32.6-8.7 49.4-7.9 31.6 0 55.4 5.9 71.2 19.8s23.7 31.6 25.7 55.4l-49.4 2c-2-11.9-5.9-21.7-13.8-27.7-7.9-5.9-17.8-7.9-31.6-7.9s-25.7 4-33.6 9.9c-5.9 4-7.9 9.9-7.9 15.8s2 11.9 7.9 15.8c5.9 5.9 21.7 9.9 45.5 15.8 23.7 5.9 41.5 11.9 53.4 17.8s21.7 13.8 27.7 25.7c6.7 12.1 10.2 25.7 9.9 39.5 0 13.8-4 27.7-11.9 39.5-8.3 12.3-20 21.9-33.6 27.7-17 6.9-35.1 10.2-53.4 9.9-31.6 0-57.1-5.9-73.2-21.7-18.1-17.9-25.8-41.7-29.9-69.3z" />
<path class="st2"
d="M273.6 576.1c7.9-2 15.8-5.9 19.8-9.9 5.9-4 9.9-13.8 13.8-23.7 4-11.9 5.9-25.7 5.9-45.5s-2-33.6-5.9-43.5c-3-8.7-7.7-16.8-13.8-23.7-6.5-6.2-14.8-10.4-23.7-11.9-7.9-2-19.8-2-41.5-2h-21.7V578h37.6c13.7.1 23.6.1 29.5-1.9z" />
<path class="st2"
d="M502.1 974.5h393.4c43.6-.1 79-35.5 79.1-79.1V540.6l-156-143.1c11.3 13.5 17.8 30.5 18.5 48.1l-49.4 2c-2-11.9-5.9-21.7-13.8-27.7-7.9-5.9-17.8-7.9-31.6-7.9s-25.7 4-33.6 9.9c-5.9 4-7.9 9.9-7.9 15.8s2 11.9 7.9 15.8c5.9 5.9 21.7 9.9 45.5 15.8s41.5 11.9 53.4 17.8 21.7 13.8 27.7 25.7c6.7 12.1 10.2 25.7 9.9 39.5 0 13.8-4 27.7-11.9 39.5-8.3 12.3-20 21.9-33.6 27.7-17 6.9-35.1 10.2-53.4 9.9-31.6 0-57.1-5.9-73.2-21.7-18-17.8-25.7-41.5-29.7-69.2l49.4-4c2 15.8 7.9 27.7 17.8 35.6 10.1 8 22.7 12.2 35.6 11.9 12.6.6 25.1-2.9 35.6-9.9 7.3-5.7 11.7-14.4 11.9-23.7 0-5.9-2-9.9-5.9-15.8-4-4-9.9-7.9-17.8-9.9-5.9-2-19.8-5.9-39.5-9.9-20.5-4.1-39.9-12.1-57.3-23.7-21.6-17.2-29.5-46.5-19.3-72.2l-49.8-45.6v248.4h-49.4L453.5 457.5v162.1h-47.4V477.8l-51.4-40.1c5.9 15.8 7.9 35.6 7.9 59.3 0 19.8-2 37.6-7.9 53.4-5.2 16.2-14 31.1-25.7 43.5-9.9 9.7-22.2 16.5-35.6 19.8-11.9 4-25.7 5.9-43.5 5.9H155v-6.9l-.4 6.9 347.5 354.9z" />
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,15 +0,0 @@
{
"name": "hosts-switch",
"version": "2.0.0",
"description": "Hosts切换器",
"main": "src/main.js",
"scripts": {
"start": "electron ."
},
"author": {
"name": "yutent",
"email": "yutent.io@gmail.com"
},
"homepage": "https://yutent.me",
"license": "MIT"
}

BIN
preview/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -1 +0,0 @@
html{width:100%;height:100vh}body{position:fixed;left:0;top:0;display:flex;width:100%;height:100%;line-height:1.5;font-size:14px;color:var(--color-dark-1)}.layout-left{display:flex;flex-direction:column;width:180px;height:100vh;background:var(--color-dark-2)}.layout-left wc-scroll{overflow:hidden;flex:1}.layout-left .domain-list{width:100%}.layout-left .domain-list .item{display:flex;justify-content:flex-end;align-items:center;height:40px;padding:0 20px 0 10px;color:var(--color-plain-1);cursor:pointer;transition:background .1s ease-in-out}.layout-left .domain-list .item wc-icon{--size: 12px;margin:auto -15px auto 5px;color:var(--color-grey-3)}.layout-left .domain-list .item:hover,.layout-left .domain-list .item.active{background:var(--color-dark-1)}.layout-left .domain-list .item.active{border-left:.3px solid var(--color-orange-1);color:var(--color-orange-1);font-weight:bold}.layout-left .domain-list .item.blank{justify-content:center;cursor:default}.layout-left .domain-list .item.blank:hover{background:none}.layout-left .action{display:flex;align-items:center;height:50px;padding:0 10px}.layout-right{flex:1;display:flex;flex-direction:column;background:#f7f8fb}.layout-right .toolbar{display:flex;align-items:center;justify-content:space-between;height:40px;padding:0 15px;background:var(--color-plain-2);box-shadow:0 2px 5px rgba(0,0,0,.1)}.layout-right .toolbar wc-input{width:200px}.layout-right .main{overflow:hidden;flex:1;display:flex;flex-direction:column;margin:20px 10px;padding:0 5px;background:#fff}.layout-right .thead{display:flex;align-items:center;justify-content:center;height:40px;margin-bottom:8px;border-bottom:1px solid var(--color-plain-2);text-align:center}.layout-right .thead span{flex:1}.layout-right .thead .long{flex:1.5}.layout-right wc-scroll{flex:1}.layout-right .record-list{width:100%;line-height:40px}.layout-right .record-list .item{display:flex;justify-content:center;align-items:center;height:40px;padding:0 10px;border-bottom:1px solid var(--color-plain-2);text-align:center}.layout-right .record-list .item wc-input,.layout-right .record-list .item span,.layout-right .record-list .item section{flex:1}.layout-right .record-list .item .long{flex:1.5}.layout-right .record-list .item section{display:flex;align-items:center;justify-content:center}.layout-right .record-list .item wc-button{margin-left:5px}.layout-right .record-list .item:last-child{border-bottom:none}.permission-error{position:fixed;left:0;top:0;z-index:102401;display:flex;flex-direction:column;align-items:center;width:100%;height:100%;padding:50px;background:rgba(255,233,233,.95);-webkit-backdrop-filter:blur(0.5rem);backdrop-filter:blur(0.5rem)}.permission-error pre{font-family:"Courier New",Courier,monospace;font-size:16px;color:var(--color-red-1)}.permission-error fieldset{width:600px;padding:0 30px 30px;border:1px solid var(--color-orange-1);border-radius:8px}.permission-error fieldset legend{padding:0 10px;font-size:16px}.permission-error fieldset dt{margin-top:20px;font-weight:bold}.permission-error fieldset code{display:block;padding:8px 10px;margin-top:8px;border-left:3px solid var(--color-plain-3);background:rgba(255,255,255,.3);font-family:"Courier New",Courier,monospace}.permission-error.show{display:flex}.context-menu{display:flex;flex-direction:column;width:100px;padding:5px 0;background:#fff}.context-menu .item{height:30px;line-height:30px;padding:0 15px;cursor:pointer}.context-menu .item:hover{background:#f2f5fc}

View File

@ -1,240 +0,0 @@
@charset "UTF-8";
/**
* {sonist app style}
* @authors yutent<yutent.io@gmail.com>
* @date 2018/12/16 17:15:07
*/
@mixin ts($c: all, $t: 0.1s, $m: ease-in-out) {
transition: $c $t $m;
}
html {
width: 100%;
height: 100vh;
}
body {
position: fixed;
left: 0;
top: 0;
display: flex;
width: 100%;
height: 100%;
line-height: 1.5;
font-size: 14px;
color: var(--color-dark-1);
}
.layout-left {
display: flex;
flex-direction: column;
width: 180px;
height: 100vh;
background: var(--color-dark-2);
wc-scroll {
overflow: hidden;
flex: 1;
}
.domain-list {
width: 100%;
.item {
display: flex;
justify-content: flex-end;
align-items: center;
height: 40px;
padding: 0 20px 0 10px;
color: var(--color-plain-1);
cursor: pointer;
@include ts(background);
wc-icon {
--size: 12px;
margin: auto -15px auto 5px;
color: var(--color-grey-3);
}
&:hover,
&.active {
background: var(--color-dark-1);
}
&.active {
border-left: 0.3px solid var(--color-orange-1);
color: var(--color-orange-1);
font-weight: bold;
}
&.blank {
justify-content: center;
cursor: default;
&:hover {
background: none;
}
}
}
}
.action {
display: flex;
align-items: center;
height: 50px;
padding: 0 10px;
}
}
.layout-right {
flex: 1;
display: flex;
flex-direction: column;
background: #f7f8fb;
.toolbar {
display: flex;
align-items: center;
justify-content: space-between;
height: 40px;
padding: 0 15px;
background: var(--color-plain-2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
wc-input {
width: 200px;
}
}
.main {
overflow: hidden;
flex: 1;
display: flex;
flex-direction: column;
margin: 20px 10px;
padding: 0 5px;
background: #fff;
}
.thead {
display: flex;
align-items: center;
justify-content: center;
height: 40px;
margin-bottom: 8px;
border-bottom: 1px solid var(--color-plain-2);
text-align: center;
span {
flex: 1;
}
.long {
flex: 1.5;
}
}
wc-scroll {
flex: 1;
}
.record-list {
width: 100%;
line-height: 40px;
.item {
display: flex;
justify-content: center;
align-items: center;
height: 40px;
padding: 0 10px;
border-bottom: 1px solid var(--color-plain-2);
text-align: center;
wc-input,
span,
section {
flex: 1;
}
.long {
flex: 1.5;
}
section {
display: flex;
align-items: center;
justify-content: center;
}
wc-button {
margin-left: 5px;
}
&:last-child {
border-bottom: none;
}
}
}
}
.permission-error {
position: fixed;
left: 0;
top: 0;
z-index: 102401;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: 100%;
padding: 50px;
background: rgba(255, 233, 233, 0.95);
backdrop-filter: blur(0.5rem);
pre {
font-family: 'Courier New', Courier, monospace;
font-size: 16px;
color: var(--color-red-1);
}
fieldset {
width: 600px;
padding: 0 30px 30px;
border: 1px solid var(--color-orange-1);
border-radius: 8px;
legend {
padding: 0 10px;
font-size: 16px;
}
dt {
margin-top: 20px;
font-weight: bold;
}
code {
display: block;
padding: 8px 10px;
margin-top: 8px;
border-left: 3px solid var(--color-plain-3);
background: rgba(255, 255, 255, 0.3);
font-family: 'Courier New', Courier, monospace;
}
}
&.show {
display: flex;
}
}
.context-menu {
display: flex;
flex-direction: column;
width: 100px;
padding: 5px 0;
background: #fff;
.item {
height: 30px;
line-height: 30px;
padding: 0 15px;
cursor: pointer;
&:hover {
background: #f2f5fc;
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@ -1,108 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<title></title>
<link href="/lib/css/reset-basic.css" rel="stylesheet">
<link href="/css/app.css" rel="stylesheet">
<script src="/js/app.js" type="module"></script>
</head>
<body anot="app" class="do-fn-noselect">
<div class="layout-left">
<wc-scroll>
<ul class="domain-list" @contextmenu="showMenu">
<li
class="item"
:class="{active: it === activeDomain}"
:for="it in domains"
:data-name="it"
@click="toggleDomain(it)">
<span :text="it"></span>
<wc-icon is="right"></wc-icon>
</li>
<li :if="domains.size() < 1" class="item blank">没有域名</li>
</ul>
</wc-scroll>
<section class="action">
<wc-button
title="新增域名"
type="info"
circle
icon="plus"
@click="addDomain">
</wc-button>
</section>
</div>
<div class="layout-right">
<header class="toolbar">
<wc-button icon="plus" type="primary" @click="addRecord">新增记录</wc-button>
<wc-button icon="fly" type="info" @click="save">保存</wc-button>
</header>
<main class="main">
<header class="thead">
<span class="long">主机记录</span>
<span>类型</span>
<span class="long">记录值</span>
<span class="long">操作</span>
<span>备注</span>
</header>
<wc-scroll ref="records">
<ul class="record-list">
<li class="item" :for="it in records">
<wc-input no-border autofocus class="long" @change="updateCacheDict(it)" :duplex="it.record" label="根域请填 @"></wc-input>
<span>A</span>
<wc-input no-border class="long" :duplex="it.value" label="请填写IP"></wc-input>
<section class="long">
<wc-switch :duplex="it.enabled" @change="recordChanges(it)"></wc-switch>
<wc-link size="mini" type="danger" @click="$remove">删除</wc-link>
<wc-link size="mini" type="info" @click="clone(it)">克隆</wc-link>
</section>
<wc-input no-border :duplex="it.remark"></wc-input>
</li>
</ul>
</wc-scroll>
</main>
</div>
<div class="permission-error" :if="permissionShow">
<pre>
/************************************************************/
* hosts文件没有写权限 *
/************************************************************/
</pre>
<fieldset>
<legend>操作指引</legend>
<dl>
<dt>MacOS用户</dt>
<dd>打开终端, 执行以下命令</dd>
<dd><code>sudo chown $USER:admin /etc/hosts</code></dd>
<dt>Linux用户</dt>
<dd>打开终端, 执行以下命令</dd>
<dd><code>sudo chown $USER: /etc/hosts</code></dd>
<dt>完成之后</dt>
<dd>点击下面的按钮重新检测.</dd>
<dd>
<wc-button color="red" size="mini" @click="check">权限检测</wc-button>
</dd>
</dl>
</fieldset>
</div>
<wc-layer ref="context" left="100px" top="0" radius="0">
<ul class="context-menu" @click="confirmAction">
<li class="item" data-act="del">删除域名</li>
<li class="item" data-act="edit">修改域名</li>
</ul>
</wc-layer>
</body>
</html>

View File

@ -1,234 +0,0 @@
/**
* {sonist app}
* @author yutent<yutent.io@gmail.com>
* @date 2018/12/16 17:15:57
*/
import '/lib/anot.js'
import '/lib/form/button.js'
import '/lib/form/link.js'
import '/lib/form/input.js'
import '/lib/form/switch.js'
import '/lib/layer/index.js'
import Utils from '/lib/utils.js'
const $doc = Anot(document)
let dict = {}
let tmp_records = {}
Anot({
$id: 'app',
state: {
filter: '',
activeDomain: Anot.ls('last_domain') || '', //当前选中的域名
editDomain: '', // 当前临时要编辑的域名, 即右键菜单选择到的
domains: [],
records: [],
permissionShow: false
},
mounted() {
// this.$refs.context.show()
this.check()
Utils.outside(this.$refs.context, ev => {
this.$refs.context.close()
})
},
watch: {},
methods: {
addRecord() {
if (this.activeDomain) {
this.records.unshift({
record: '',
value: '',
enabled: true,
remark: ''
})
this.$refs.records.scrollTop = 0
} else {
layer.toast('请先选择域名', 'warn')
}
},
addDomain() {
layer
.prompt('请输入根域名', function (val, done) {
if (
val === 'localhost' ||
val === 'local' ||
val === electron.hostname ||
/^[\w.\-]+\.[a-z]+$/.test(val)
) {
done()
} else {
layer.toast('域名格式错误', 'error')
}
})
.then(val => {
this.domains.push(val)
dict[val] = []
if (!this.activeDomain) {
this.toggleDomain(val)
}
this.save()
})
.catch(Anot.noop)
},
toggleDomain(name) {
this.activeDomain = name
this.records = (dict[name] || []).sort((a, b) => a.record.localeCompare(b.record))
tmp_records = Object.create(null)
for (let it of this.records) {
if (tmp_records[it.record]) {
tmp_records[it.record].push(it)
} else {
tmp_records[it.record] = [it]
}
}
document.title = `伪域名解析 ${name} `
Anot.ls('last_domain', name)
setTimeout(() => {
this.$refs.records.scrollTop = 0
}, 50)
},
showMenu(ev) {
this.$refs.context.close()
var { pageX, pageY } = ev
if (pageY + 70 > 600) {
pageY -= 70
}
var elem = ev.target
if (elem.tagName !== 'LI') {
elem = elem.parentNode
}
this.editDomain = elem.dataset.name
Anot.nextTick(_ => {
this.$refs.context.moveTo({ left: pageX + 'px', top: pageY + 'px' })
this.$refs.context.show()
})
},
confirmAction(ev) {
this.$refs.context.close()
if (ev.target.tagName === 'LI') {
var act = ev.target.dataset.act
if (act === 'del') {
layer
.confirm(`是否要删除域名「${this.editDomain}」?`)
.then(res => {
if (this.editDomain === this.activeDomain) {
if (this.records.length) {
return layer.toast('该域名下有主机记录, 请先删除主机记录后再删除域名', 'error')
}
} else {
if (dict[this.editDomain].length > 0) {
return layer.toast('该域名下有主机记录, 请先删除主机记录后再删除域名', 'error')
}
}
delete dict[this.editDomain]
this.domains.remove(this.editDomain)
this.activeDomain = ''
this.editDomain = ''
this.records.clear()
this.save()
this.toggleDomain(this.domains[0])
})
.catch(Anot.noop)
} else if (act === 'edit') {
layer
.prompt(`请输入新的名字「${this.editDomain}`, (val, done) => {
if (val === this.editDomain || dict[val]) {
return
}
if (
val === 'localhost' ||
val === 'local' ||
val === electron.hostname ||
/^[\w.]+\.[a-z]+$/.test(val)
) {
done()
} else {
layer.toast('域名格式错误', 'error')
}
})
.then(val => {
var idx = this.domains.indexOf(this.editDomain)
this.domains.set(idx, val)
dict[val] = dict[this.editDomain]
delete dict[this.editDomain]
this.activeDomain = ''
this.editDomain = ''
this.save()
this.toggleDomain(val)
})
.catch(Anot.noop)
}
}
},
check() {
var check = electron.checkPermission()
if (check) {
dict = electron.getHistory()
var tmp = []
for (var k in dict) {
if (k) {
tmp.push(k)
} else {
delete dict[k]
}
}
this.domains = tmp
this.permissionShow = false
this.toggleDomain(Anot.ls('last_domain') || tmp[0])
} else {
this.permissionShow = true
}
},
updateCacheDict(item) {
clearTimeout(this.timer)
this.timer = setTimeout(_ => {
tmp_records = Object.create(null)
for (let it of this.records) {
if (tmp_records[it.record]) {
tmp_records[it.record].push(it)
} else {
tmp_records[it.record] = [it]
}
}
}, 1000)
},
clone(item) {
var params = { ...item }
params.enabled = false
this.records.unshift(params)
tmp_records[params.record].push(this.records[0])
this.$refs.records.scrollTop = 0
},
// 同一个记录, 允许一条被激活
recordChanges(item) {
if (item.enabled) {
if (tmp_records[item.record].length > 1) {
for (let it of tmp_records[item.record]) {
if (it.value !== item.value) {
it.enabled = false
}
}
}
}
},
save() {
if (this.activeDomain) {
dict[this.activeDomain] = this.records.$model
}
electron.saveHosts(dict)
layer.toast('保存成功', 'success')
}
}
})

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
import e from"../utils.js";const c={axis:"",limit:!1,overflow:!0};class g{constructor(t){this.$elem=t,this._init()}_init(){this.$elem.style.transform="";var{x:t,y:n}=this.$elem.getBoundingClientRect();this.pos={x:t,y:n,_x:0,_y:0}}by(t,n={}){return this.$drag=t,this.opt=Object.assign(Object.create(null),c,n),this.opt.limit!==!1&&(this.opt.overflow=!1),t.style.cursor="move",this._handleResize=e.bind(window,"resize",this._init.bind(this)),this._handleMousedown=e.bind(t,"mousedown",p=>{if(this.disabled)return;var l=this.$elem.getBoundingClientRect();l.x-this.pos._x!==this.pos.x&&(this.pos.x=l.x-this.pos._x),l.y-this.pos._y!==this.pos.y&&(this.pos.y=l.y-this.pos._y);let r=p.pageX,a=p.pageY,u=document.documentElement.clientWidth,f=document.documentElement.clientHeight,d=l.width,m=l.height,s=[0,u-d,f-m,0];if(this.opt.limit==="parent"){let i=this.$elem.parentNode.getBoundingClientRect();s=[i.top,i.right-d,i.bottom-m,i.left]}let x=e.bind(document,"mousemove",i=>{i.preventDefault();let o=i.pageX-r+(l.x-this.pos.x),h=i.pageY-a+(l.y-this.pos.y);this.opt.axis==="x"&&(h=0),this.opt.axis==="y"&&(o=0),this.opt.overflow===!1&&(o<s[3]-this.pos.x?o=s[3]-this.pos.x:o>s[1]-this.pos.x&&(o=s[1]-this.pos.x),h<s[0]-this.pos.y?h=s[0]-this.pos.y:h>s[2]-this.pos.y&&(h=s[2]-this.pos.y)),this.pos._x=o,this.pos._y=h,this.$elem.dispatchEvent(new CustomEvent("dragging",{detail:{offset:{x:this.pos.x+o,y:this.pos.y+h},move:{x:o,y:h}}})),this.$elem.style.transform=`translate(${o}px, ${h}px)`}),y=e.bind(document,"mouseup",i=>{this.$elem.dispatchEvent(new CustomEvent("dragged",{detail:{offset:{x:this.pos.x+this.pos._x,y:this.pos.y+this.pos._y},move:{x:this.pos._x,y:this.pos._y}}})),e.unbind(document,"mousemove",x),e.unbind(document,"mouseup",y)})}),this}on(t,n){if(!(!t||typeof n!="function"))return e.bind(this,t,n)}off(t,n){e.unbind(this,t,n)}destroy(){e.unbind(window,"resize",this._handleResize),e.unbind(this.$drag,"mousedown",this._handleMousedown),delete this.$elem,delete this.$drag}}export{g as default};

View File

@ -1,85 +0,0 @@
# 拖拽插件
> 该插件可以让任意一个元素可以被拖拽,而不需要该元素是否具有定位属性。
> 使用时,在目标元素上添加`:drag`属性即可以实现拖拽功能。
## 依赖
> 依赖`Anot`框架
## 浏览器兼容性
+ chrome
+ firefox
+ safari
+ IE10+
## 用法
> 只需要在要拖拽的元素上添加`:drag`即可;
> 如果要拖拽的元素不是当前元素,只需要给该属性增加一个值为想要拖拽元素的类名或ID。
> 具体请看示例:
> **注意:** `拖拽的元素不是本身时,只会往父级一级一级找相匹配的`
```html
<!DOCTYPE html>
<html>
<head>
<style>
* {margin:0;padding:0}
.box {width:200px;height:100px;background:#aaa;}
.box .handle {width:200px;height:30px;background:#f30;}
</style>
</head>
<body :controller="test">
<div class="box" :drag></div>
<div class="box">
<div class="handle" :drag="box"></div>
</div>
<script>
import Anot from 'lib/drag/index.js'
Anot({
$id: 'test'
})
</script>
</body>
</html>
```
## 额外参数
### `data-limit`
> 用于限制元素的拖动范围,默认没有限制。 可选值为 "window"和"parent", 分别为 "限制在可视区"和"限制在父级元素的范围"
### `data-axis`
> 用于限制拖动的方向, 默认值为 "xy",即不限制方向。可选值为 "x"和"y", 即只能在"x轴"或"y轴"方向拖动。
### `data-beforedrag`
> 拖动前的回调,如果有设置回调方法, 则该回调的返回值,可决定该元素是否能被拖拽, 可用于在特殊场景下,临时禁用拖拽。
> `注:`
> 1. 该回调方法,会传入3个参数, 第1个为被拖拽的元素(dom对象), 第2个参数为 该元素的x轴绝对坐标, 第3个元素为y轴绝对坐标;
> 2. 该回调方法, 返回false时, 本次拖拽将临时失效, 返回其他值,或没有返回值,则忽略。
### `data-dragging`
> 元素被拖动时的回调。
> `注:`
> 1.该回调方法,会传入3个参数, 第1个为被拖拽的元素(dom对象), 第2个参数为 该元素的x轴绝对坐标, 第3个元素为y轴绝对坐标;
### `data-dragged`
> 元素被拖动结束后的回调。
> `注:`
> 1. 该回调方法,会传入3个参数, 第1个为被拖拽的元素(dom对象), 第2个参数为 该元素的x轴绝对坐标, 第3个元素为y轴绝对坐标;

View File

@ -1 +0,0 @@
import a from"./core.js";Anot.directive("drag",{priority:1500,init:function(e){e.expr='"'+e.expr+'"',e.overflow=!0,e.axis="xy",e.element.dataset.axis&&(e.axis=e.element.dataset.axis,delete e.element.dataset.axis),e.limit=!1,e.element.dataset.limit&&(e.limit=e.element.dataset.limit,e.overflow=!1,delete e.element.dataset.limit)},update:function(e){var t=this.element;if(e)for(t=this.element.parentNode;t;){if(t.classList||Anot.error(`${this.name}=${this.expr}, \u89E3\u6790\u5F02\u5E38[\u5143\u7D20\u4E0D\u5B58\u5728]`),t.tagName==="WC-LAYER"&&e==="layer"){t=t.root.children[1];break}if(t.classList.contains(e)||t.id===e)break;t=t.parentNode}new a(t).by(this.element,{limit:this.limit,axis:this.axis,overflow:this.overflow})}});

File diff suppressed because one or more lines are too long

View File

@ -1,10 +0,0 @@
/**
*
* @authors yutent (yutent.io@gmail.com)
* @date 2022-06-23 15:46:28
* @version v1.0.6
*
*/
var d=Object.defineProperty;var n=(r,o,e)=>o in r?d(r,o,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[o]=e;var l=(r,o,e)=>(n(r,typeof o!="symbol"?o+"":o,e),e);import"../icon/index.js";import s from"../utils.js";class c extends HTMLElement{constructor(){super();l(this,"props",{value:"",checked:!1,readonly:!1,disabled:!1});Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML='<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{display:inline-flex;align-items:center;line-height:1;font-size:14px;cursor:pointer}:host label{display:flex;justify-content:center;align-items:center;min-width:32px;height:32px;padding-right:16px;line-height:1;-moz-user-select:none;user-select:none;white-space:nowrap;cursor:inherit;outline:none;color:var(--color-dark-1)}:host label.checked .dot{background:var(--color-dark-1)}:host label.checked .dot wc-icon{--size: 14px}:host .dot{display:flex;justify-content:center;align-items:center;width:16px;height:16px;margin-right:4px;border:1px solid var(--color-dark-1);border-radius:4px;background:#fff;color:#fff;transition:box-shadow .15s linear,background .15s linear}:host .dot wc-icon{--size: 0px;transition:width .15s linear}:host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-plain-a)}:host([type=danger]) label{color:var(--color-red-1)}:host([type=danger]) label .dot{border-color:var(--color-red-1)}:host([type=danger]) label.checked .dot{background:var(--color-red-1)}:host([type=danger]:focus-within) .dot{box-shadow:0 0 0 2px var(--color-red-a)}:host([type=info]) label{color:var(--color-blue-1)}:host([type=info]) label .dot{border-color:var(--color-blue-1)}:host([type=info]) label.checked .dot{background:var(--color-blue-1)}:host([type=info]:focus-within) .dot{box-shadow:0 0 0 2px var(--color-blue-a)}:host([type=success]) label{color:var(--color-green-1)}:host([type=success]) label .dot{border-color:var(--color-green-1)}:host([type=success]) label.checked .dot{background:var(--color-green-1)}:host([type=success]:focus-within) .dot{box-shadow:0 0 0 2px var(--color-green-a)}:host([type=primary]) label{color:var(--color-teal-1)}:host([type=primary]) label .dot{border-color:var(--color-teal-1)}:host([type=primary]) label.checked .dot{background:var(--color-teal-1)}:host([type=primary]:focus-within) .dot{box-shadow:0 0 0 2px var(--color-teal-a)}:host([type=warning]) label{color:var(--color-orange-1)}:host([type=warning]) label .dot{border-color:var(--color-orange-1)}:host([type=warning]) label.checked .dot{background:var(--color-orange-1)}:host([type=warning]:focus-within) .dot{box-shadow:0 0 0 2px var(--color-orange-a)}:host([readonly]){cursor:default;opacity:.8}:host([disabled]){cursor:not-allowed;opacity:.6}:host([disabled]) label{color:var(--color-grey-2)}</style> <label tabindex="0"> <span class="dot"><wc-icon is="get"></wc-icon></span> <slot /> </label> ',this.__SWITCH__=this.root.lastElementChild,this.__ICO__=this.__SWITCH__.children[0],this._isInGroup=!1}static get observedAttributes(){return["value","checked","readonly","disabled"]}_checkGroup(){this._isInGroup=this.parentNode.tagName==="WC-CHECKBOX-GROUP",this._isInGroup&&this.parentNode.root&&this.parentNode.value.includes(this.value)&&(this.checked=!0)}get value(){return this.props.value}set value(e){this.props.value=e}get checked(){return this.props.checked}set checked(e){this.props.checked=!!e,this.__SWITCH__.classList.toggle("checked",this.props.checked)}get readOnly(){return this.props.readonly}set readOnly(e){var t=typeof e;e!==this.props.readonly&&(t==="boolean"&&e||t!=="boolean"?(this.props.readonly=!0,this.setAttribute("readonly","")):(this.props.readonly=!1,this.removeAttribute("readonly")))}get disabled(){return this.props.disabled}set disabled(e){var t=typeof e;e!==this.props.disabled&&(t==="boolean"&&e||t!=="boolean"?(this.props.disabled=!0,this.setAttribute("disabled",""),this.__SWITCH__.removeAttribute("tabindex")):(this.props.disabled=!1,this.removeAttribute("disabled")))}_toggleCheck(){this.disabled||this.readOnly||(this.checked=!this.checked,this._isInGroup?this.parentNode.dispatchEvent(new CustomEvent("child-picked",{detail:{value:this.value,checked:this.checked}})):this.dispatchEvent(new CustomEvent("input")))}connectedCallback(){this._checkGroup(),this._handlClick=s.bind(this,"click",e=>{e.stopPropagation(),this._toggleCheck()}),this._handlKeydown=s.bind(this,"keydown",e=>{e.keyCode===32&&this._toggleCheck()})}disconnectedCallback(){s.unbind(this,"click",this._handlClick),s.unbind(this,"keydown",this._handlKeydown)}attributeChangedCallback(e,t,a){if(t!==a)switch(e){case"value":this.props[e]=a;break;case"checked":case"readonly":case"disabled":var i=e;i==="readonly"&&(i="readOnly"),this[i]=a!==null;break}}}customElements.get("wc-checkbox")||customElements.define("wc-checkbox",c);export{c as default};

View File

@ -1,10 +0,0 @@
/**
*
* @authors yutent (yutent.io@gmail.com)
* @date 2022-06-23 15:46:28
* @version v1.0.6
*
*/
var u=Object.defineProperty;var h=(i,r,e)=>r in i?u(i,r,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[r]=e;var l=(i,r,e)=>(h(i,typeof r!="symbol"?r+"":r,e),e);import d from"../utils.js";import"./checkbox-item.js";class o extends HTMLElement{constructor(){super();l(this,"props",{value:"",disabled:!1,readonly:!1});Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML="<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{display:inline-flex;flex-wrap:wrap}</style> <slot /> "}static get observedAttributes(){return["value","disabled","readonly"]}_updateChildrenStat(e){Array.from(this.children).forEach(t=>{t.tagName==="WC-CHECKBOX"?t.root&&(e&&(t.disabled=this.disabled,t.readOnly=this.readOnly),this.value.includes(t.value)?t.checked=!0:t.checked=!1):t.remove()})}get value(){return this.props.value}set value(e){e!==this.props.value&&(this.props.value=[...e],this._updateChildrenStat())}get readOnly(){return this.props.readonly}set readOnly(e){var t=typeof e;e!==this.props.readonly&&(t==="boolean"&&e||t!=="boolean"?(this.props.readonly=!0,this.setAttribute("readonly","")):(this.props.readonly=!1,this.removeAttribute("readonly")),this._updateChildrenStat(!0))}get disabled(){return this.props.disabled}set disabled(e){var t=typeof e;e!==this.props.disabled&&(t==="boolean"&&e||t!=="boolean"?(this.props.disabled=!0,this.setAttribute("disabled","")):(this.props.disabled=!1,this.removeAttribute("disabled")),this._updateChildrenStat(!0))}connectedCallback(){this._pickedFn=d.bind(this,"child-picked",e=>{var t=[...this.props.value],s=t.indexOf(e.detail.value);e.detail.checked?s<0&&t.push(e.detail.value):~s&&t.splice(s,1),this.value=t,this.dispatchEvent(new CustomEvent("input"))}),this.__observer=new MutationObserver(e=>{this._updateChildrenStat(!0)}),this.__observer.observe(this,{childList:!0,subtree:!0})}disconnectedCallback(){d.unbind(this,"child-picked",this._pickedFn),this.__observer.disconnect()}attributeChangedCallback(e,t,s){if(t!==s)switch(e){case"value":s&&(this.value=s.split(",").map(n=>n.trim()));break;case"readonly":case"disabled":var a=e;a==="readonly"&&(a="readOnly"),this[a]=s!==null;break}}}customElements.get("wc-checkbox-group")||customElements.define("wc-checkbox-group",o);export{o as default};

View File

@ -1,10 +0,0 @@
/**
*
* @authors yutent (yutent.io@gmail.com)
* @date 2022-06-23 15:46:28
* @version v1.0.6
*
*/
var h=Object.defineProperty;var d=(r,t,e)=>t in r?h(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e;var a=(r,t,e)=>(d(r,typeof t!="symbol"?t+"":t,e),e);import"../icon/index.js";import"./option.js";import o from"../utils.js";class n extends HTMLElement{constructor(){super();a(this,"props",{value:"",placeholder:"",size:"",disabled:!1});a(this,"state",{optionShow:!1});Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML='<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{display:inline-flex;min-width:128px;height:36px;border-radius:3px;user-select:none;-moz-user-select:none;color:var(--color-dark-1);font-size:14px;cursor:pointer;transition:box-shadow .15s linear}:host .label{position:relative;display:flex;width:100%;height:100%;line-height:1;border:1px solid var(--color-grey-2);border-radius:inherit;white-space:nowrap;background:#fff;cursor:inherit;transition:background .15s linear}:host .preview{display:flex;align-items:center;width:100%;height:100%;padding:0 8px}:host .preview input{flex:1;width:0;border:0;font:inherit;color:inherit;background:none;outline:none;cursor:inherit}:host .preview input::placeholder{color:var(--color-grey-1)}:host .preview wc-icon{--size: 14px;color:var(--color-grey-2)}:host .options-box{overflow:hidden;visibility:hidden;position:absolute;left:0;top:36px;z-index:99;width:100%;height:0;border-radius:3px;background:#fff;box-shadow:0 3px 5px rgba(0,0,0,.15);transition:height .15s linear}:host .options-box.active{visibility:visible;height:auto;padding:6px 0}:host([size=large]){min-width:234px;height:52px;font-size:18px}:host([size=large]) .options-box{top:52px}:host([size=medium]){min-width:160px;height:44px}:host([size=medium]) .options-box{top:44px}:host([size=small]){min-width:96px;height:32px}:host([size=small]) .options-box{top:32px}:host([size=mini]){min-width:72px;height:26px;font-size:12px}:host([size=mini]) .preview wc-icon{--size: 10px}:host([size=mini]) .options-box{top:26px}:host(:focus-within){box-shadow:0 0 0 2px var(--color-plain-a)}:host([disabled]){cursor:not-allowed}:host([disabled]) .label{border-color:var(--color-grey-1);background:var(--color-plain-1);opacity:.6}</style> <div class="label"> <section class="preview"> <input readonly /> <wc-icon is="trigon-down"></wc-icon> </section> <div class="options-box"><slot /></div> </div> ';var e=this.root.children[1];this.__PREVIEW__=e.children[0],this.__OPTIONS__=e.children[1],this.__INPUT__=this.__PREVIEW__.children[0],this.__ICO__=this.__PREVIEW__.children[1]}static get observedAttributes(){return["value","placeholder","size","disabled"]}_updateChildrenStat(){Array.from(this.children).forEach(e=>{e.tagName==="WC-OPTION"?e.root&&(e.value===this.props.value?(e.setAttribute("active",""),this.__INPUT__.value=e.label||e.textContent):e.removeAttribute("active")):e.remove()})}get value(){return this.props.value}set value(e){this.props.value=e,this._updateChildrenStat()}get disabled(){return this.props.disabled}set disabled(e){var i=typeof e;e!==this.props.disabled&&(i==="boolean"&&e||i!=="boolean"?(this.props.disabled=!0,this.setAttribute("disabled",""),this.__INPUT__.disabled=!0):(this.props.disabled=!1,this.removeAttribute("disabled"),this.__INPUT__.disabled=!1))}connectedCallback(){this._activeFn=o.bind(this.__PREVIEW__,"click",e=>{this.disabled||(this.state.optionShow=!this.state.optionShow,this.__OPTIONS__.classList.toggle("active",this.state.optionShow))}),this._pickedFn=o.bind(this.__OPTIONS__,"click",e=>{let i=e.target;if(i!==e.currentTarget){for(;i.tagName!=="WC-OPTION";)i=i.parentNode;this.props.value=i.value,this._updateChildrenStat(),this.dispatchEvent(new CustomEvent("input")),this.state.optionShow=!1,this.__OPTIONS__.classList.toggle("active",!1)}}),this._inactiveFn=o.outside(this,e=>{this.state.optionShow=!1,this.__OPTIONS__.classList.toggle("active",!1)}),o.nextTick(e=>this._updateChildrenStat())}disconnectedCallback(){this.state.options=[],o.unbind(this.__PREVIEW__,"click",this._activeFn),o.unbind(this.__OPTIONS__,"click",this._pickedFn),o.clearOutside(this._inactiveFn)}attributeChangedCallback(e,i,s){if(i!==s)switch(e){case"value":s!==null&&(this.value=s);break;case"placeholder":this.__INPUT__.placeholder=s||"";break;case"size":s&&Array.from(this.children).forEach(l=>{l.setAttribute("size",s)});break;case"disabled":this.disabled=s!==null;break}}}customElements.get("wc-dropdown")||customElements.define("wc-dropdown",n);export{n as default};

View File

@ -1 +0,0 @@
import"./button.js";import"./link.js";import"./input.js";import"./passwd.js";import"./textarea.js";import"./number.js";import"./radio.js";import"./checkbox.js";import"./switch.js";import"./select.js";import"./dropdown.js";import"./star.js";

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,10 +0,0 @@
/**
*
* @authors yutent (yutent.io@gmail.com)
* @date 2022-06-23 15:46:28
* @version v1.0.6
*
*/
var n=Object.defineProperty;var b=(s,t,e)=>t in s?n(s,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):s[t]=e;var r=(s,t,e)=>(b(s,typeof t!="symbol"?t+"":t,e),e);import o from"../utils.js";class a extends HTMLElement{constructor(){super();r(this,"props",{value:"",label:"",disabled:!1});Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML="<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{display:flex;justify-content:space-between;align-items:center;height:36px;transition:background .15s linear;color:var(--color-dark-1);cursor:pointer}:host label{flex:1;display:flex;justify-content:space-between;align-items:center;height:100%;padding:0 8px;cursor:inherit}:host([size=large]){height:52px}:host([size=medium]){height:44px}:host([size=small]){height:32px}:host([size=mini]){height:26px}:host(:hover) label,:host([active]) label{background:var(--color-plain-1)}:host([disabled]){color:var(--color-grey-1);opacity:.6;cursor:not-allowed}</style> <label><slot /></label> ",this.__LABEL__=this.root.children[1]}static get observedAttributes(){return["value","label","disabled"]}get value(){return this.props.value}set value(e){e!==this.props.value&&(this.props.value=e)}get label(){return this.props.label}set label(e){e!==this.props.label&&(this.props.label=e,this.__LABEL__.textContent=e)}get disabled(){return this.props.disabled}set disabled(e){var i=typeof e;e!==this.props.disabled&&(i==="boolean"&&e||i!=="boolean"?(this.props.disabled=!0,this.setAttribute("disabled","")):(this.props.disabled=!1,this.removeAttribute("disabled")))}connectedCallback(){this._clickFn=o.bind(this,"click",e=>{this.disabled&&e.stopPropagation()})}disconnectedCallback(){o.unbind(this,"click",this._clickFn)}attributeChangedCallback(e,i,l){if(i!==l)switch(e){case"value":case"label":l===null?this[e]="":this[e]=l;break;case"disabled":this.disabled=l!==null;break}}}customElements.get("wc-option")||customElements.define("wc-option",a);export{a as default};

File diff suppressed because one or more lines are too long

View File

@ -1,10 +0,0 @@
/**
*
* @authors yutent (yutent.io@gmail.com)
* @date 2022-06-23 15:46:28
* @version v1.0.6
*
*/
var n=Object.defineProperty;var c=(r,o,e)=>o in r?n(r,o,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[o]=e;var i=(r,o,e)=>(c(r,typeof o!="symbol"?o+"":o,e),e);import a from"../utils.js";class d extends HTMLElement{constructor(){super();i(this,"props",{value:"",checked:!1,readonly:!1,disabled:!1});Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML='<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{display:inline-flex;align-items:center;line-height:1;font-size:14px;cursor:pointer}:host label{display:flex;justify-content:center;align-items:center;min-width:32px;padding-right:16px;line-height:1;-moz-user-select:none;user-select:none;white-space:nowrap;cursor:inherit;outline:none;color:var(--color-dark-1)}:host label.checked .dot::after{visibility:visible;transform:scale(1)}:host .dot{display:flex;justify-content:center;align-items:center;width:16px;height:16px;margin-right:4px;border:1px solid var(--color-dark-1);border-radius:50%;background:#fff;transition:box-shadow .15s linear}:host .dot::after{display:block;visibility:hidden;width:6px;height:6px;border-radius:50%;background:var(--color-dark-1);content:"";transform:scale(0);transition:transform .15s linear}:host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-plain-a)}:host([type=danger]) label{color:var(--color-red-1)}:host([type=danger]) label .dot{border-color:var(--color-red-1)}:host([type=danger]) label .dot::after{background:var(--color-red-1)}:host([type=danger]:focus-within) .dot{box-shadow:0 0 0 2px var(--color-red-a)}:host([type=success]) label{color:var(--color-green-1)}:host([type=success]) label .dot{border-color:var(--color-green-1)}:host([type=success]) label .dot::after{background:var(--color-green-1)}:host([type=success]:focus-within) .dot{box-shadow:0 0 0 2px var(--color-green-a)}:host([type=primary]) label{color:var(--color-teal-1)}:host([type=primary]) label .dot{border-color:var(--color-teal-1)}:host([type=primary]) label .dot::after{background:var(--color-teal-1)}:host([type=primary]:focus-within) .dot{box-shadow:0 0 0 2px var(--color-teal-a)}:host([type=warning]) label{color:var(--color-orange-1)}:host([type=warning]) label .dot{border-color:var(--color-orange-1)}:host([type=warning]) label .dot::after{background:var(--color-orange-1)}:host([type=warning]:focus-within) .dot{box-shadow:0 0 0 2px var(--color-orange-a)}:host([type=info]) label{color:var(--color-blue-1)}:host([type=info]) label .dot{border-color:var(--color-blue-1)}:host([type=info]) label .dot::after{background:var(--color-blue-1)}:host([type=info]:focus-within) .dot{box-shadow:0 0 0 2px var(--color-blue-a)}:host([readonly]){cursor:default;opacity:.8}:host([disabled]){cursor:not-allowed;opacity:.6}:host([disabled]) label{color:var(--color-grey-2)}:host([disabled]) label .dot{border-color:var(--color-grey-1);box-shadow:none}:host([disabled]) label .dot::after{background:var(--color-grey-1)}</style> <label tabindex="0"> <span class="dot"></span> <slot /> </label> ',this.__SWITCH__=this.root.lastElementChild}static get observedAttributes(){return["value","checked","readonly","disabled"]}get value(){return this.props.value}set value(e){this.props.value=e}get checked(){return this.props.checked}set checked(e){this.props.checked=!!e,this.__SWITCH__.classList.toggle("checked",this.props.checked)}get readOnly(){return this.props.readonly}set readOnly(e){var t=typeof e;e!==this.props.readonly&&(t==="boolean"&&e||t!=="boolean"?(this.props.readonly=!0,this.setAttribute("readonly","")):(this.props.readonly=!1,this.removeAttribute("readonly")))}get disabled(){return this.props.disabled}set disabled(e){var t=typeof e;e!==this.props.disabled&&(t==="boolean"&&e||t!=="boolean"?(this.props.disabled=!0,this.setAttribute("disabled",""),this.__SWITCH__.removeAttribute("tabindex")):(this.props.disabled=!1,this.removeAttribute("disabled")))}_toggleCheck(){this.disabled||this.readOnly||this.checked||(this.checked=!0,this.parentNode.dispatchEvent(new CustomEvent("child-picked",{detail:this.value})))}connectedCallback(){this.value===this.parentNode.value&&(this.checked=!0),this._handleClick=a.catch(this,"click",e=>{e.stopPropagation(),this._toggleCheck()}),this._handlKeydown=a.bind(this,"keydown",e=>{e.keyCode===32&&this._toggleCheck()})}disconnectedCallback(){a.unbind(this,"click",this._handleClick),a.unbind(this,"keydown",this._handlKeydown)}attributeChangedCallback(e,t,s){if(t!==s)switch(e){case"value":this.value=s;break;case"checked":case"readonly":case"disabled":var l=e;l==="readonly"&&(l="readOnly"),this[l]=s!==null;break}}}customElements.get("wc-radio")||customElements.define("wc-radio",d);export{d as default};

View File

@ -1,10 +0,0 @@
/**
*
* @authors yutent (yutent.io@gmail.com)
* @date 2022-06-23 15:46:28
* @version v1.0.6
*
*/
var n=Object.defineProperty;var u=(s,r,e)=>r in s?n(s,r,{enumerable:!0,configurable:!0,writable:!0,value:e}):s[r]=e;var d=(s,r,e)=>(u(s,typeof r!="symbol"?r+"":r,e),e);import l from"../utils.js";import"./radio-item.js";class o extends HTMLElement{constructor(){super();d(this,"props",{value:null,disabled:!1,readonly:!1});Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML="<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{display:inline-flex;flex-wrap:wrap}</style> <slot /> "}static get observedAttributes(){return["value","disabled","readonly"]}_updateChildrenStat(e){Array.from(this.children).forEach(t=>{t.tagName==="WC-RADIO"?t.root&&(e&&(t.disabled=this.disabled,t.readOnly=this.readOnly),t.value===this.props.value?t.checked=!0:t.checked=!1):t.remove()})}get value(){return this.props.value}set value(e){e!==this.props.value&&(this.props.value=e,this._updateChildrenStat())}get readOnly(){return this.props.readonly}set readOnly(e){var t=typeof e;e!==this.props.readonly&&(t==="boolean"&&e||t!=="boolean"?(this.props.readonly=!0,this.setAttribute("readonly","")):(this.props.readonly=!1,this.removeAttribute("readonly")),this._updateChildrenStat(!0))}get disabled(){return this.props.disabled}set disabled(e){var t=typeof e;e!==this.props.disabled&&(t==="boolean"&&e||t!=="boolean"?(this.props.disabled=!0,this.setAttribute("disabled","")):(this.props.disabled=!1,this.removeAttribute("disabled")),this._updateChildrenStat(!0))}connectedCallback(){this._pickedFn=l.bind(this,"child-picked",e=>{this.value=e.detail,this.dispatchEvent(new CustomEvent("input"))}),this.__observer=new MutationObserver(e=>{this._updateChildrenStat(!0)}),this.__observer.observe(this,{childList:!0,subtree:!0})}disconnectedCallback(){l.unbind(this,"child-picked",this._pickedFn),this.__observer.disconnect()}attributeChangedCallback(e,t,i){if(t!==i)switch(e){case"value":this.value=i;break;case"readonly":case"disabled":var a=e;a==="readonly"&&(a="readOnly"),this[a]=i!==null;break}}}customElements.get("wc-radio-group")||customElements.define("wc-radio-group",o);export{o as default};

File diff suppressed because one or more lines are too long

View File

@ -1,10 +0,0 @@
/**
*
* @authors yutent (yutent.io@gmail.com)
* @date 2022-06-23 15:46:28
* @version v1.0.6
*
*/
var l=Object.defineProperty;var d=(i,t,e)=>t in i?l(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e;var a=(i,t,e)=>(d(i,typeof t!="symbol"?t+"":t,e),e);import c from"../utils.js";class o extends HTMLElement{constructor(){super();a(this,"props",{"active-text":null,"inactive-text":null,checked:!1,disabled:!1});Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML='<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{display:inline-flex;align-items:center}:host section{display:flex;justify-content:center;align-items:center;line-height:1;white-space:nowrap}:host label{display:flex;align-items:center;width:32px;height:18px;padding:3px;margin:5px;border-radius:21px;background:var(--color-plain-3);cursor:inherit}:host label.checked{flex-direction:row-reverse;background:var(--color-grey-3)}:host .dot{width:12px;height:12px;border-radius:50%;background:#fff}:host([disabled]){cursor:not-allowed;opacity:.6}:host([size=large]) label{width:46px;height:26px;padding:3px 5px}:host([size=large]) .dot{width:18px;height:18px}:host([size=medium]) label{width:38px;height:22px;padding:3px 4px}:host([size=medium]) .dot{width:16px;height:16px}:host([size=mini]) label{width:22px;height:14px;padding:2px}:host([size=mini]) .dot{width:10px;height:10px}:host([type=danger]) label.checked{background:var(--color-red-1)}:host([type=info]) label.checked{background:var(--color-blue-1)}:host([type=success]) label.checked{background:var(--color-green-1)}:host([type=primary]) label.checked{background:var(--color-teal-1)}:host([type=warning]) label.checked{background:var(--color-orange-1)}:host([type=inverse]) label.checked{background:var(--color-dark-1)}:host([color=purple]) label.checked{background:var(--color-purple-1)}</style> <section> <label> <span class="dot"></span> </label> <slot></slot> </section> ',this.__SWITCH__=this.root.lastElementChild.firstElementChild}static get observedAttributes(){return["active-text","inactive-text","checked","disabled"]}get value(){return this.props.checked}set value(e){this.checked=e}get checked(){return this.props.checked}set checked(e){this.props.checked=!!e,this.__SWITCH__.classList.toggle("checked",this.props.checked)}get disabled(){return this.props.disabled}set disabled(e){var s=typeof e;e!==this.props.disabled&&(s==="boolean"&&e||s!=="boolean"?(this.props.disabled=!0,this.setAttribute("disabled","")):(this.props.disabled=!1,this.removeAttribute("disabled")))}_updateStat(){this.checked?this.props["active-text"]!==null&&(this.textContent=this.props["active-text"]):this.props["inactive-text"]!==null&&(this.textContent=this.props["inactive-text"])}connectedCallback(){this._handleClick=c.bind(this,"click",e=>{this.disabled||(this.checked=!this.checked,this._updateStat(),this.dispatchEvent(new CustomEvent("input")))})}disconnectedCallback(){c.unbind(this,"click",this._handleClick)}attributeChangedCallback(e,s,r){if(s!==r)switch(e){case"checked":case"disabled":this[e]=r!==null;break;case"active-text":case"inactive-text":this.props[e]=r+"",this._updateStat();break}}}customElements.get("wc-switch")||customElements.define("wc-switch",o);export{o as default};

View File

@ -1,10 +0,0 @@
/**
*
* @authors yutent (yutent.io@gmail.com)
* @date 2022-06-23 15:46:28
* @version v1.0.6
*
*/
var b=Object.defineProperty;var c=(s,r,e)=>r in s?b(s,r,{enumerable:!0,configurable:!0,writable:!0,value:e}):s[r]=e;var h=(s,r,e)=>(c(s,typeof r!="symbol"?r+"":r,e),e);import n from"../utils.js";class d extends HTMLElement{constructor(){super();h(this,"props",{value:"",placeholder:"",maxlength:0,minlength:0,autofocus:!1,readonly:!1,disabled:!1,lazy:0});Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML='<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{display:flex;width:100%;height:80px;user-select:none;-moz-user-select:none;color:var(--color-dark-1);border-radius:3px;cursor:text;transition:box-shadow .15s linear}.label{position:relative;width:100%;height:100%;font-size:14px;border:1px solid var(--color-grey-2);border-radius:inherit;background:var(--bg-color, #fff);color:inherit;cursor:inherit}.label textarea{flex:1;min-width:36px;width:100%;height:100%;padding:5px 8px;border:0;border-radius:inherit;color:inherit;font:inherit;background:none;outline:none;box-shadow:none;cursor:inherit;resize:none}.label textarea::placeholder{color:var(--color-grey-1)}.label .input-stat{display:none;position:absolute;right:4px;bottom:2px;z-index:1;line-height:1;font-size:12px;color:var(--color-grey-2)}:host([show-limit]) .label{padding-bottom:14px}:host([show-limit]) .label .input-stat{display:block}:host([disabled]){cursor:not-allowed}:host([disabled]) .label{background:var(--color-plain-1);opacity:.6}:host([readonly]){cursor:default}:host(:focus-within){box-shadow:0 0 0 2px var(--color-plain-a)}:host([type=primary]:focus-within){box-shadow:0 0 0 2px var(--color-teal-a)}:host([type=info]:focus-within){box-shadow:0 0 0 2px var(--color-blue-a)}:host([type=success]:focus-within){box-shadow:0 0 0 2px var(--color-green-a)}:host([type=danger]:focus-within){box-shadow:0 0 0 2px var(--color-red-a)}:host([type=warning]:focus-within){box-shadow:0 0 0 2px var(--color-orange-a)}:host([type=primary]) .label{border-color:var(--color-teal-2)}:host([type=info]) .label{border-color:var(--color-blue-2)}:host([type=success]) .label{border-color:var(--color-green-2)}:host([type=danger]) .label{border-color:var(--color-red-2)}:host([type=warning]) .label{border-color:var(--color-orange-2)}:host([no-border]),:host(:focus-within[no-border]){box-shadow:none}:host([no-border]) .label,:host(:focus-within[no-border]) .label{border:0}</style> <div class="label"> <textarea spellcheck="false"></textarea> <div class="input-stat">0/\u221E</div> </div> ',this.__OUTER__=this.root.children[1],this.__INPUT__=this.__OUTER__.children[0],this.__STAT__=this.__OUTER__.children[1]}static get observedAttributes(){return["value","placeholder","maxlength","minlength","autofocus","readonly","disabled","lazy"]}get readOnly(){return this.props.readonly}set readOnly(e){var t=typeof e;e!==this.props.readonly&&(t==="boolean"&&e||t!=="boolean"?(this.props.readonly=!0,this.setAttribute("readonly",""),this.__INPUT__.setAttribute("readonly","")):(this.props.readonly=!1,this.removeAttribute("readonly"),this.__INPUT__.removeAttribute("readonly")))}get disabled(){return this.props.disabled}set disabled(e){var t=typeof e;e!==this.props.disabled&&(t==="boolean"&&e||t!=="boolean"?(this.props.disabled=!0,this.setAttribute("disabled",""),this.__INPUT__.setAttribute("disabled","")):(this.props.disabled=!1,this.removeAttribute("disabled"),this.__INPUT__.removeAttribute("disabled")))}get value(){return this.__INPUT__.value}set value(e){this.__INPUT__.value=e}connectedCallback(){this.stamp=0,this._handleSubmit=n.catch(this.__INPUT__,"keydown",e=>{let{minlength:t,lazy:o}=this.props,a=this.value,i=Date.now();if(e.keyCode===13&&(e.ctrlKey||e.metaKey)){if(this.disabled||this.readOnly||o&&i-this.stamp<o||t&&t>0&&a.length<t)return;this.stamp=i,this.dispatchEvent(new CustomEvent("submit",{detail:this.value}))}}),this._statFn=n.bind(this.__INPUT__,"input,change",e=>{let{maxlength:t}=this.props,o=this.value.length;t=t||"\u221E",this.__STAT__.textContent=`${o}/${t}`})}disconnectedCallback(){n.unbind(this.__INPUT__,"keydown",this._handleSubmit),n.unbind(this.__INPUT__,"input,change",this._statFn)}attributeChangedCallback(e,t,o){if(t!==o)switch(e){case"autofocus":this.__INPUT__.setAttribute("autofocus",""),setTimeout(i=>{this.__INPUT__.focus()},10);break;case"placeholder":this.__INPUT__.setAttribute("placeholder",o);break;case"value":this.value=o||"";break;case"maxlength":{let i=this.value.length;if(o===null)this.__INPUT__.removeAttribute(e),this.props.maxlength=0,this.__STAT__.textContent=`${i}/\u221E`;else{let l=+o;l>0?(this.__INPUT__.setAttribute(e,l),this.props.maxlength=l,this.__STAT__.textContent=`${i}/${l}`):this.removeAttribute(e)}}break;case"lazy":this.props.lazy=o>>0;break;case"readonly":case"disabled":var a=e;a==="readonly"&&(a="readOnly"),this[a]=o!==null;break}}}customElements.get("wc-textarea")||customElements.define("wc-textarea",d);export{d as default};

View File

@ -1,10 +0,0 @@
/**
*
* @authors yutent (yutent.io@gmail.com)
* @date 2022-06-23 15:46:28
* @version v1.0.6
*
*/
var n=Object.defineProperty;var l=(e,i,t)=>i in e?n(e,i,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[i]=t;var o=(e,i,t)=>(l(e,typeof i!="symbol"?i+"":i,t),t);import d from"./svg.js";let a=d;window.EXT_SVG_DICT&&Object.assign(a,EXT_SVG_DICT);class h extends HTMLElement{constructor(){super();o(this,"props",{is:""});Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML='<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{display:inline-flex;width:var(--size, 36px);height:var(--size, 36px);color:inherit}:host(:not([is])){display:none}.icon{display:block;width:100%;height:100%;fill:currentColor}.icon.load{animation:load 1.5s linear infinite}.icon circle{stroke:currentColor;animation:circle 1.5s ease-in-out infinite}:host([size=large]){width:52px;height:52px}:host([size=medium]){width:44px;height:44px}:host([size=small]){width:32px;height:32px}:host([size=mini]){width:26px;height:26px}@keyframes circle{0%{stroke-dasharray:0,3812px;stroke-dashoffset:0}50%{stroke-dasharray:1906px,3812px;stroke-dashoffset:-287px}100%{stroke-dasharray:1906px,3812px;stroke-dashoffset:-2393px}}@keyframes load{to{transform:rotate(360deg)}}</style> <svg class="icon" viewBox="0 0 1024 1024"></svg> ',this.__ICO__=this.root.lastElementChild,this.drawPath()}static get observedAttributes(){return["is"]}get is(){return this.props.is}set is(t){t&&this.setAttribute("is",t)}drawPath(){var{is:t}=this.props,s=a[t];!this.__ICO__||t&&s&&(this.__ICO__.innerHTML=t==="loading"?s:`<path d="${s}" />`,this.__ICO__.classList.toggle("load",t==="loading"))}attributeChangedCallback(t,s,r){if(s!==r)switch(t){case"is":this.props.is=r,r?this.drawPath():this.removeAttribute("is");break}}}customElements.get("wc-icon")||customElements.define("wc-icon",h);export{h as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
function l(){}var o={nextTick:function(){let t=[],n=document.createTextNode("<!-- -->"),e=!1;function r(){let i=t.length;for(let a=0;a<i;a++)t[a]();t=t.slice(i)}return new MutationObserver(r).observe(n,{characterData:!0}),function(i){t.push(i),e=!e,n.data=e}}(),offset(t){try{var n=t.getBoundingClientRect();if(n.width||n.height||t.getClientRects().length){var e=t.ownerDocument,r=e.documentElement,i=e.defaultView;return{top:n.top+i.pageYOffset-r.clientTop,left:n.left+i.pageXOffset-r.clientLeft}}}catch(a){return{left:0,top:0}}},each(t,n){if(t){if(Array.isArray(t))for(let e=0,r;(r=t[e++])&&n(r,e-1)!==!1;);else for(let e in t)if(t.hasOwnProperty(e)&&n(t[e],e)===!1)break}},bind(t,n,e=l,r=!1){let i=n.split(",");return this.each(i,function(a){a=a.trim(),t.addEventListener(a,e,r)}),e},catch(t,n,e,r){function i(a){a.stopPropagation&&a.stopPropagation(),e&&e(a)}return this.bind(t,n,i,r)},unbind(t,n,e=l,r=!1){let i=n.split(",");this.each(i,function(a){a=a.trim(),t.removeEventListener(a,e,r)})},outside(t,n=l){return this.bind(document,"mousedown",e=>{if(e)if(e.path){for(var r=e.path.concat();r.length>3;)if(r.shift()===t)return}else{var i=e.explicitOriginalTarget||e.target;if(t===i||t.contains(i)||t.root&&t.root.contains(i))return}n(e)})},clearOutside(t=l){this.unbind(document,"mousedown",t)}};export{o as default};

View File

@ -1,101 +0,0 @@
/**
*
* @author yutent<yutent.io@gmail.com>
* @date 2019/09/16 20:51:19
*/
const { app, BrowserWindow, protocol, Menu, ipcMain } = require('electron')
const { join, resolve, extname } = require('path')
const { cat, echo, isfile } = require('./tools/utils.js')
const os = require('os')
const createMenu = require('./tools/menu')
const MIME_TYPES = {
'.js': 'text/javascript',
'.html': 'text/html',
'.htm': 'text/plain',
'.css': 'text/css',
'.jpg': 'image/jpg',
'.png': 'image/png',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.ico': 'image/ico'
}
const ROOT = __dirname
const HOST_FILE = resolve(app.getPath('userData'), 'host.cache')
const LOCK_FILE = resolve(app.getPath('userData'), 'lock')
/* ********** 修复环境变量 start *********** */
let pathEnv = new Set()
process.env.PATH.split(':').forEach(_ => {
pathEnv.add(_)
})
pathEnv.add('/usr/local/bin')
pathEnv.add('/usr/local/sbin')
process.env.PATH = Array.from(pathEnv).join(':')
pathEnv = null
/* ********** 修复环境变量 end *********** */
/* ----------------------------------------------------- */
app.commandLine.appendSwitch('--lang', 'zh-CN')
app.commandLine.appendSwitch('--autoplay-policy', 'no-user-gesture-required')
protocol.registerSchemesAsPrivileged([
{ scheme: 'app', privileges: { secure: true, standard: true } }
])
/* ----------------------------------------------------- */
if (!isfile(HOST_FILE)) {
echo('{}', HOST_FILE)
}
ipcMain.on('ready', ev => {
ev.returnValue = { HOST_FILE, LOCK_FILE, HOST: os.hostname() }
})
// 初始化应用
app.once('ready', () => {
// 注册协议
protocol.registerBufferProtocol('app', (req, cb) => {
let file = req.url.replace(/^app:\/\/local\//, '')
let ext = extname(req.url)
let buff = cat(resolve(ROOT, file))
cb({ data: buff, mimeType: MIME_TYPES[ext] })
})
Menu.setApplicationMenu(null)
// 创建浏览器窗口
let win = new BrowserWindow({
title: 'Hosts切换器',
width: 1000,
height: 640,
resizable: false,
maximizable: false,
icon: resolve(ROOT, './images/app.png'),
webPreferences: {
// webSecurity: false,
// experimentalFeatures: true,
// nodeIntegration: true,
preload: join(ROOT, './tools/inject.js')
}
})
win.on('closed', () => {
app.exit()
win = null
})
// win.openDevTools()
// 然后加载应用的 index.html
win.loadURL('app://local/index.html')
if (process.platform === 'darwin') {
createMenu(win)
}
})

View File

@ -1,98 +0,0 @@
/**
* {}
* @author yutent<yutent.io@gmail.com>
* @date 2022/07/14 11:54:32
*/
const { ipcRenderer, shell, contextBridge } = require('electron')
const { resolve } = require('path')
const { cat, echo, isfile, writable } = require('./utils.js')
const { HOST_FILE, LOCK_FILE, HOST } = ipcRenderer.sendSync('ready')
let timer = null
contextBridge.exposeInMainWorld('electron', {
hostname: HOST,
open(url) {
shell.openExternal(url)
},
checkPermission() {
return writable('/etc/hosts')
},
getHistory() {
if (isfile(LOCK_FILE)) {
var cache = cat(HOST_FILE)
return JSON.parse(cache)
}
var cache = cat('/etc/hosts').toString()
var records = cache.split(/[\n\r]+/)
var list = []
var dict = {}
records.forEach(str => {
str = str.trim()
let matches = str.match(/^(#*?)\s*(\d+\.\d+\.\d+\.\d+)\s+(.*)/)
if (matches) {
let names = matches[3].split(/\s+/).map(it => it.trim())
let name
while ((name = names.pop())) {
list.push({ ip: matches[2], enabled: !matches[1], name })
}
}
})
records = null
list.forEach(it => {
it.name = it.name.split('.')
let domain = it.name.splice(-2, 2).join('.')
if (domain === 'com.cn' || domain === 'org.cn' || domain === 'net.cn') {
domain = it.name.pop() + '.' + domain
}
if (dict[domain]) {
dict[domain].push({
value: it.ip,
enabled: it.enabled,
record: it.name.join('.') || '@',
remark: ''
})
} else {
dict[domain] = [
{
value: it.ip,
enabled: it.enabled,
record: it.name.join('.') || '@',
remark: ''
}
]
}
})
list = null
echo(JSON.stringify(dict), HOST_FILE)
echo('', LOCK_FILE)
return dict
},
saveHosts(dict) {
clearTimeout(timer)
timer = setTimeout(() => {
var txt = ''
for (let k in dict) {
for (let it of dict[k]) {
if (it.enabled) {
var name = it.record === '@' ? '' : it.record
if (name) {
name += '.'
}
txt += `${it.value.padEnd(15, ' ')} ${name + k}\n`
}
}
txt += '\n'
}
echo(JSON.stringify(dict), HOST_FILE)
echo(txt, '/etc/hosts')
}, 1000)
}
})

View File

@ -1,65 +0,0 @@
/**
* 菜单项
* @author yutent<yutent.io@gmail.com>
* @date 2019/01/21 20:34:04
*/
const { Menu } = require('electron')
module.exports = function (win) {
let menuList = Menu.buildFromTemplate([
{
label: 'Hosts切换器',
submenu: [
{ role: 'about', label: '关于 Hosts切换器' },
{ type: 'separator' },
{ role: 'quit', label: '退出' }
]
},
{
label: '编辑',
submenu: [
{ role: 'undo', label: '撤消重做' },
{ role: 'redo', label: '重做' },
{ type: 'separator' },
{ role: 'cut', label: '剪切' },
{ role: 'copy', label: '复制' },
{ role: 'paste', label: '粘贴' },
{ role: 'selectall', label: '全选' }
]
},
{
label: '显示',
submenu: [
{ label: 'Hosts切换器' },
{
type: 'separator'
},
{ label: 'Hosts切换器' }
]
},
{
label: '窗口',
submenu: [
{
role: 'minimize',
label: '最小化',
click() {
win.minimize()
}
}
]
},
{
role: 'help',
label: '帮助',
submenu: [
{
label: '官网',
click() {}
}
]
}
])
Menu.setApplicationMenu(menuList)
}

View File

@ -1,87 +0,0 @@
/**
*
* @author yutent<yutent.io@gmail.com>
* @date 2022/07/14 11:06:47
*/
const fs = require('fs')
const { parse } = require('path')
/**
* [stat 返回文件/目录的状态信息]
* @param {[string]} path [目标路径]
*/
function stat(path) {
try {
return fs.statSync(path)
} catch (err) {
return EMPTY_STAT
}
}
/**
* [isdir 判断目标是否为目录]
* @param {String} path [目标路径]
*/
function isdir(path) {
try {
return stat(path).isDirectory()
} catch (err) {}
}
/**
* [mkdir 新建目录]
* @param {String} dir [目标路径]
* @param {Number} mode [目录权限, node v10.12起支持]
*/
function mkdir(dir, mode = 0o755) {
try {
fs.mkdirSync(dir, { recursive: true, mode: mode })
return true
} catch (err) {}
}
//
exports.cat = function (file) {
try {
return fs.readFileSync(file)
} catch (e) {}
}
exports.echo = function (data, file, append) {
if (!file) {
return data
}
let updir = parse(file).dir
let opt = {}
if (!isdir(updir)) {
mkdir(updir)
}
try {
if (append) {
fs.appendFileSync(file, data, opt)
} else {
fs.writeFileSync(file, data, opt)
}
} catch (err) {}
}
exports.isfile = function (path) {
try {
return stat(path).isFile()
} catch (err) {
return false
}
}
exports.writable = function (file) {
try {
fs.accessSync('/etc/hosts', fs.constants.W_OK)
return true
} catch (e) {
return false
}
}

7
test.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
export RUN_ENV='development'
./usr/lib/hosts-switch/hosts-switch.py

3
usr/bin/hosts-switch Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
/usr/share/hosts-switch/hosts-switch.py

View File

@ -0,0 +1,9 @@
[Desktop Entry]
Name=Hosts Switch
Comment[zh_CN]=Hosts文件修改器
Exec=hosts-switch
Icon=hosts-switch
Terminal=false
X-MultipleArgs=false
Type=Application
Categories=Development;

View File

@ -0,0 +1,23 @@
#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class Window(Gtk.Window):
def __init__(self, title = 'Untitled window', width = 840, height = 520):
Gtk.Window.__init__(self, title = title)
self.set_default_size(width, height)
self.resize(width, height)
self.set_wmclass('Hosts-Switch', 'Hosts-Switch')
self.set_title('Hosts-Switch')
self.set_icon_name('hosts-switch')
def toggle_visible(self, icon):
if self.is_visible():
self.hide()
else:
self.present()

View File

@ -0,0 +1,67 @@
#!/usr/bin/python3
import gi, os, sys
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GLib, Gio, GObject, GdkPixbuf
from webengine.gtk3 import WebEngine, create_setting, create_hmr_server
from _window import Window
APP_ID = 'fun.wkit.hosts-switch'
__dir__ = os.path.dirname(os.path.realpath(__file__))
web_root = os.path.join(__dir__, './webapp')
home_dir = os.getenv('HOME')
config_dir = os.path.join(home_dir, '.config/hosts-switch')
if not os.path.isdir(config_dir):
os.mkdir(config_dir)
class Application(Gtk.Application):
def __init__(self):
Gtk.Application.__init__(self, application_id = APP_ID)
self.window = Window()
web = WebEngine(self.window)
web.set_root(web_root)
if os.getenv('RUN_ENV') == 'development':
setting = create_setting({"devtools": True})
hmr = create_hmr_server()
web.use(setting).use(hmr)
web.connect('quit', self.quit_all)
web.load()
self.window.add(web)
def do_activate(self):
self.set_app_menu(None)
self.set_menubar(None)
self.add_window(self.window)
self.window.show_all()
def quit_all(self, widget):
self.remove_window(self.window)
if __name__ == "__main__":
try:
app = Application()
app.run(sys.argv)
except Exception as err:
print(err)

View File

@ -0,0 +1,41 @@
/**
* {}
* @author yutent<yutent.io@gmail.com>
* @date 2023/07/20 14:19:13
*/
import 'es.shim'
import { html, css, Component } from 'wkit'
import { createApp, createRouter } from 'wkitd'
import store from './store.js'
import './components/home.js'
import { noop } from './utils/index.js'
createApp({
data: {
input: '',
img: '',
content: ''
},
styles: [
css`
:host {
display: flex;
width: 100%;
height: 100vh;
}
`
],
methods: {
quit() {
native.quit()
}
},
render() {
return html` <wc-home @contextmenu.prevent=${noop}></wc-home> `
}
})
.use(store)
.mount()

View File

@ -0,0 +1,262 @@
/**
* {}
* @author yutent<yutent.io@gmail.com>
* @date 2023/08/08 18:19:17
*/
import { html, css, Component, classMap, nextTick, outsideClick } from 'wkit'
import 'ui/icon/index.js'
import 'ui/scroll/index.js'
import 'ui/layer/index.js'
import 'ui/space/index.js'
import 'ui/form/input.js'
import 'ui/form/switch.js'
import 'ui/form/button.js'
import 'ui/form/link.js'
import './sidebar.js'
import './permission.js'
import './records.js'
import { checkPermission, getHistory, saveHosts, noop } from '../utils/index.js'
const HOST_DATA = await getHistory()
class Home extends Component {
static props = {
editDomain: '', // 当前临时要编辑的域名, 即右键菜单选择到的
permissionShow: false
}
static styles = [
css`
:host {
flex: 1;
display: flex;
width: 100%;
height: 100%;
padding: 32px;
color: var(--color-dark-1);
background: #f0f0f0;
}
.visible {
display: block;
}
ul li {
list-style: none;
}
.noselect {
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
}
`,
css`
.main {
flex: 1;
display: flex;
flex-direction: column;
}
.main .toolbar {
display: flex;
align-items: center;
justify-content: space-between;
height: 40px;
padding: 0 15px;
border-bottom: 1px solid var(--color-plain-3);
background: var(--color-plain-1);
}
.main .list {
overflow: hidden;
flex: 1;
}
`,
css`
.context-menu {
display: flex;
flex-direction: column;
width: 100px;
padding: 5px 0;
background: #fff;
}
.context-menu .item {
height: 30px;
line-height: 30px;
padding: 0 15px;
cursor: pointer;
}
.context-menu .item :hover {
background: #f2f5fc;
}
`
]
async mounted() {
let writable = await checkPermission()
if (writable) {
this.$store.HOST_DATA = HOST_DATA
this.$store.domains = Object.keys(HOST_DATA)
this.$refs.domain.mounted()
outsideClick(this.$refs.context, _ => this.$refs.context.close())
} else {
this.permissionShow = true
}
}
addRecord() {
if (this.$store.activeDomain) {
this.$store.records.push({
record: '',
value: '',
enabled: true,
remark: ''
})
nextTick(_ => (this.$refs.list.scrollTop = 1e6))
} else {
layer.toast('请先选择域名', 'warn')
}
}
showMenu(ev) {
this.$refs.context.close()
var { pageX, pageY } = ev
if (pageY + 70 > 600) {
pageY -= 70
}
var elem = ev.target
if (elem.tagName !== 'LI') {
elem = elem.parentNode
}
this.editDomain = elem.dataset.name
setTimeout(_ => {
this.$refs.context.moveTo({ left: pageX + 'px', top: pageY + 'px' })
this.$refs.context.show()
})
}
confirmAction(ev) {
this.$refs.context.close()
if (ev.target.tagName === 'LI') {
let act = ev.target.dataset.act
let { HOST_DATA, records, domains } = this.$store
let idx = domains.indexOf(this.editDomain)
if (act === 'del') {
layer
.confirm(`是否要删除域名「${this.editDomain}」?`)
.then(res => {
if (this.editDomain === this.$store.activeDomain) {
if (records.length) {
return layer.toast(
'该域名下有主机记录, 请先删除主机记录后再删除域名',
'error'
)
}
} else {
if (HOST_DATA[this.editDomain].length > 0) {
return layer.toast(
'该域名下有主机记录, 请先删除主机记录后再删除域名',
'error'
)
}
}
delete HOST_DATA[this.editDomain]
domains.splice(idx, 1)
this.editDomain = ''
this.$store.records = []
this.$store.activeDomain = domains[0]
this.$refs.domain.mounted()
this.save()
})
.catch(noop)
} else if (act === 'edit') {
layer
.prompt(
`请输入新的名字「${this.editDomain}`,
this.editDomain,
(val, done) => {
if (val === this.editDomain || HOST_DATA[val]) {
return layer.toast(`${val} 域名没有变化, 或已经存在`)
}
if (
val === 'localhost' ||
val === 'local' ||
/^[\w.]+\.[a-z]+$/.test(val)
) {
done()
} else {
layer.toast('域名格式错误', 'error')
}
}
)
.then(val => {
domains[idx] = val
HOST_DATA[val] = HOST_DATA[this.editDomain]
delete HOST_DATA[this.editDomain]
this.$store.activeDomain = val
this.editDomain = ''
this.$refs.domain.mounted()
this.save()
})
.catch(noop)
}
}
}
save() {
let { HOST_DATA, activeDomain, records } = this.$store
if (activeDomain) {
HOST_DATA[activeDomain] = records.filter(it => it.record && it.value)
}
saveHosts(HOST_DATA)
layer.toast('保存成功', 'success')
}
render() {
return html`
<wc-sidebar
ref="domain"
@toggle-domain=${_ => (this.$refs.list.scrollTop = 0)}
@show-menu=${ev => this.showMenu(ev.event)}
@save=${this.save}
></wc-sidebar>
<main class="main noselect">
<header class="toolbar">
<wc-button size="m" icon="plus" @click=${this.addRecord}
>新增记录</wc-button
>
<wc-button size="m" icon="fly" @click=${this.save}>保存</wc-button>
</header>
<wc-records class="list" ref="list"></wc-records>
</main>
<wc-permission
class=${classMap({ visible: this.permissionShow })}
></wc-permission>
<wc-layer ref="context" left="100px" top="0" radius="0">
<ul class="context-menu" @click=${this.confirmAction}>
<li class="item" data-act="del">删除域名</li>
<li class="item" data-act="edit">修改域名</li>
</ul>
</wc-layer>
`
}
}
Home.reg('home')

View File

@ -0,0 +1,109 @@
/**
* {}
* @author yutent<yutent.io@gmail.com>
* @date 2023/08/08 18:19:17
*/
import { html, css, Component } from 'wkit'
import { checkPermission } from '../utils/index.js'
const tips_header = `/************************************************************/
* hosts文件没有写权限 *
/************************************************************/
`
class Permission extends Component {
static styles = css`
:host {
display: none;
}
.noselect {
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
}
.permission-error {
position: fixed;
left: 0;
top: 0;
z-index: 1024;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: 100%;
padding: 24px 52px;
line-height: 1.5;
background: rgba(255, 233, 233, 0.95);
-webkit-backdrop-filter: blur(5px);
}
pre {
font-family: 'Courier New', Courier, monospace;
font-size: 14px;
color: var(--color-red-1);
}
fieldset {
width: 600px;
padding: 0 30px 30px;
border: 1px solid var(--color-orange-1);
border-radius: 8px;
}
legend {
padding: 0 10px;
font-size: 16px;
}
dt {
margin-top: 20px;
font-weight: bold;
}
code {
display: block;
padding: 8px 10px;
margin-top: 8px;
border-left: 3px solid var(--color-plain-3);
background: rgba(255, 255, 255, 0.8);
font-family: 'Courier New', Courier, monospace;
}
`
async check() {
let writable = await checkPermission()
if (writable) {
location.reload()
} else {
layer.toast('hosts文件没有写权限, 请按提示修改', 'error')
}
}
render() {
return html`
<div class="permission-error">
<pre class="noselect">${tips_header}</pre>
<fieldset>
<legend>操作指引</legend>
<dl>
<dt>MacOS用户</dt>
<dd>打开终端, 执行以下命令</dd>
<dd><code>sudo chown $USER:admin /etc/hosts</code></dd>
<dt>Linux用户</dt>
<dd>打开终端, 执行以下命令</dd>
<dd><code>sudo chown $USER: /etc/hosts</code></dd>
<dt>完成之后</dt>
<dd>点击下面的按钮重新检测.</dd>
<dd>
<wc-button type="danger" @click=${this.check}>权限检测</wc-button>
</dd>
</dl>
</fieldset>
</div>
`
}
}
Permission.reg('permission')

View File

@ -0,0 +1,158 @@
/**
* {}
* @author yutent<yutent.io@gmail.com>
* @date 2023/08/08 18:19:17
*/
import { html, css, Component, classMap, nextTick } from 'wkit'
import { noop } from '../utils/index.js'
class Records extends Component {
static styles = css`
:host {
display: flex;
flex-direction: column;
padding: 8px;
background: #fff;
}
.thead {
flex-shrink: 0;
}
.records {
overflow: hidden;
flex: 1;
}
.item {
display: flex;
align-items: center;
justify-content: center;
height: 40px;
padding: 0 4px;
border-bottom: 1px solid var(--color-plain-2);
text-align: center;
}
.thead {
line-height: 40px;
}
.item span {
flex: 1;
}
.item .long {
flex: 1.5;
}
.item wc-input {
flex: 1;
min-width: 0;
--wc-input-border-color: transparent;
}
.item:last-child {
border-bottom: none;
}
`
set scrollTop(val) {
this.$refs.records.scrollTop = val
}
// 同一个记录, 允许一条被激活
recordChanges(ev, item) {
let { tmp_records } = this.$store
item.enabled = ev.target.value
if (item.enabled) {
if (tmp_records[item.record].length > 1) {
for (let it of tmp_records[item.record]) {
if (it.value !== item.value) {
it.enabled = false
}
}
}
}
}
updateCacheDict(ev, item) {
let { records } = this.$store
let tmp_records = Object.create(null)
item.record = ev.target.value
for (let it of records) {
if (tmp_records[it.record]) {
tmp_records[it.record].push(it)
} else {
tmp_records[it.record] = [it]
}
}
this.$store.tmp_records = tmp_records
}
clone(item) {
let { tmp_records, records } = this.$store
var params = { ...item }
params.enabled = false
records.push(params)
tmp_records[params.record].push(records.at(-1))
nextTick(_ => (this.$refs.records.scrollTop = 1e6))
}
render() {
return html`
<header class="thead item">
<span>主机记录</span>
<span>类型</span>
<span class="long">记录值</span>
<span>操作</span>
<span>备注</span>
</header>
<wc-scroll class="records" ref="records">
${this.$store.records.map(
(it, i) => html`
<div class="item">
<wc-input
size="m"
autofocus
@change=${ev => this.updateCacheDict(ev, it)}
:value=${it.record}
label="根域请填 @"
></wc-input>
<span>A</span>
<wc-input
size="m"
class="long"
:value=${it.value}
@change=${ev => (it.value = ev.target.value)}
label="请填写IP"
></wc-input>
<wc-space gap="s">
<wc-link
size="m"
type="danger"
@click=${ev => this.$store.records.splice(i, 1)}
>删除</wc-link
>
<wc-link size="m" type="info" @click=${ev => this.clone(it)}
>克隆</wc-link
>
<wc-switch
size="m"
:value=${it.enabled}
@change=${ev => this.recordChanges(ev, it)}
></wc-switch>
</wc-space>
<wc-input size="m" no-border :value=${it.remark}></wc-input>
</div>
`
)}
</wc-scroll>
`
}
}
Records.reg('records')

View File

@ -0,0 +1,165 @@
/**
* {}
* @author yutent<yutent.io@gmail.com>
* @date 2023/08/08 18:19:17
*/
import { html, css, Component, classMap, nextTick } from 'wkit'
import { noop } from '../utils/index.js'
class Sidebar extends Component {
static styles = css`
:host {
flex-shrink: 0;
display: flex;
flex-direction: column;
width: 180px;
height: 100vh;
background: var(--color-plain-2);
}
.noselect {
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
}
.domain-list {
overflow: hidden;
flex: 1;
width: 100%;
}
.domain-list .item {
display: flex;
justify-content: flex-end;
align-items: center;
height: 40px;
padding: 0 12px;
cursor: pointer;
transition: background 0.15s ease-in-out;
}
.item wc-icon {
--wc-icon-size: 12px;
margin-left: 8px;
color: var(--color-grey-2);
}
.item:hover,
.item.active {
background: #fff;
}
.item.active {
border-right: 2px solid var(--color-orange-1);
color: var(--color-orange-1);
}
.item.active wc-icon {
color: var(--color-orange-1);
}
.item.blank {
justify-content: center;
cursor: default;
}
.item.blank:hover {
background: none;
}
.action {
flex-shrink: 0;
display: flex;
align-items: center;
height: 50px;
padding: 0 10px;
}
`
addDomain() {
layer
.prompt('请输入根域名', function (val, done) {
if (
val === 'localhost' ||
val === 'local' ||
/^[\w.\-]+\.[a-z]+$/.test(val)
) {
done()
} else {
layer.toast('域名格式错误', 'error')
}
})
.then(val => {
let { HOST_DATA, records } = this.$store
this.$store.domains.push(val)
HOST_DATA[val] = []
if (!this.$store.activeDomain) {
this.toggleDomain(null, val)
}
this.$emit('save')
})
.catch(noop)
}
toggleDomain(ev, name) {
let { HOST_DATA, records } = this.$store
name = name ?? ev.currentTarget.dataset.name
this.$store.activeDomain = name
this.$store.records = records = (HOST_DATA[name] || []).sort((a, b) =>
a.record.localeCompare(b.record)
)
let tmp_records = Object.create(null)
for (let it of records) {
if (tmp_records[it.record]) {
tmp_records[it.record].push(it)
} else {
tmp_records[it.record] = [it]
}
}
this.$store.tmp_records = tmp_records
document.title = `伪域名解析 ${name} `
localStorage.setItem('last_domain', name)
nextTick(() => {
this.$emit('toggle-domain')
})
}
mounted() {
this.toggleDomain(null, this.$store.activeDomain)
}
render() {
return html`
<wc-scroll class="domain-list noselect">
<ul
@contextmenu.prevent=${ev => this.$emit('show-menu', { event: ev })}
>
${this.$store.domains.map(
it => html`
<li
class=${classMap({
item: true,
active: it === this.$store.activeDomain
})}
data-name=${it}
@click=${this.toggleDomain}
>
<span>${it}</span>
<wc-icon name="right"></wc-icon>
</li>
`
)}
${this.$store.domains.length < 1
? html`<li class="item blank">没有域名</li>`
: ''}
</ul>
</wc-scroll>
<section class="action">
<wc-button circle icon="plus" @click=${this.addDomain}> </wc-button>
</section>
`
}
}
Sidebar.reg('sidebar')

View File

@ -33,7 +33,7 @@ body {font-family: 'Helvetica Neue', Arial, 'WenQuanYi Micro Hei', 'PingFang SC'
code, pre, samp {font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;white-space:pre-wrap;} code, pre, samp {font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;white-space:pre-wrap;}
[anot],[\:repeat],[\:if] {visibility: hidden;} [anot],[\:repeat],[\:if] {visibility: hidden;}
.noselect {-webkit-touch-callout: none;-webkit-user-select: none;-moz-user-select: none;user-select: none;} .noselect {-webkit-touch-callout: none;-webkit-user-select: none;user-select: none;}
.noselect img, .noselect a {-webkit-user-drag: none;} .noselect img, .noselect a {-webkit-user-drag: none;}
.text-ell {overflow: hidden;white-space: nowrap;text-overflow: ellipsis;} .text-ell {overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}
.text-thin {-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;} .text-thin {-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}
@ -56,9 +56,9 @@ code, pre, samp {font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
--color-blue-3: rgb(33, 150, 243); --color-blue-3: rgb(33, 150, 243);
/* danger */ /* danger */
--color-red-a: rgba(252, 118, 97, 0.35); --color-red-a: rgba(252, 118, 97, 0.35);
--color-red-1: rgb(252, 118, 97); --color-red-1: #fc7661;
--color-red-2: rgb(255, 95, 69); --color-red-2: #ff5f45;
--color-red-3: rgb(243, 62, 34); --color-red-3: #f33e22;
/* warning */ /* warning */
--color-orange-a: rgba(254, 174, 117, 0.35); --color-orange-a: rgba(254, 174, 117, 0.35);
--color-orange-1: #feae75; --color-orange-1: #feae75;

View File

@ -0,0 +1,35 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<link rel="stylesheet" href="/css/reset.css">
<style>
html,body {
width:100%;
height: 100%;
line-height: 1.5;
font-size: 14px;
cursor:default;
}
a {color:inherit;text-decoration: none;}
</style>
<script type="importmap">
{
"imports":{
"es.shim":"app:///lib/es.shim.js",
"wkit":"app:///lib/wkit.js",
"wkitd":"app:///lib/wkitd.js",
"fetch":"app:///lib/fetch.js",
"crypto":"app:///lib/crypto.js",
"ui/":"app:///lib/ui/"
}
}
</script>
<script type="module" src="/app.js"></script>
</head>
<body>
<wc-app></wc-app>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,16 @@
import { css, html, Component } from "wkit";
class Card extends Component {
static props = {
header: ""
};
static styles = css`:host{display:flex;border-radius:3px}.card-box{display:flex;flex-direction:column;position:relative;width:100%;border:1px solid var(--color-plain-2);border-radius:inherit;background:#fff;color:var(--color-dark-1);transition:box-shadow .2s ease-in-out;box-shadow:0 0 12px rgba(0,0,0,.12)}.card-box .header{display:flex;align-items:center;justify-content:space-between;width:100%;min-height:52px;padding:var(--card-padding, 8px 16px);border-bottom:1px solid var(--color-plain-2);font-size:16px;user-select:none}.card-box .content{flex:1;min-height:64px;padding:var(--card-padding, 8px 16px);font-size:14px;color:var(--color-dark-1)}:host([shadow=never]) .card-box,:host([shadow=hover]) .card-box{box-shadow:none}:host([shadow=hover]:hover) .card-box{box-shadow:0 0 12px rgba(0,0,0,.12)}`;
render() {
return html`
<div class="card-box">
<div class="header"><slot name="header">${this.header}</slot></div>
<div class="content"><slot></slot></div>
</div>
`;
}
}
Card.reg("card");

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,35 @@
import{nextTick as r,css as l,html as s,Component as a,classMap as h,outsideClick as c}from"wkit";import"../icon/index.js";const d={duration:100,custom:[{transform:"scaleY(0)",opacity:0},{transform:"scaleY(1)",opacity:1}]};class u extends a{static props={readOnly:!1,autofocus:!1,disabled:!1,clearable:!1,icon:"",placeholder:"",maxlength:{type:Number,default:null},minlength:{type:Number,default:null},value:"str!",lazy:"num!0"};#t=[];#e=-1;#i=!1;#s=0;static styles=[l`:host{position:relative;display:inline-flex;min-width:188px;height:32px;user-select:none;-moz-user-select:none;color:var(--color-dark-1);border-radius:3px;cursor:text;transition:box-shadow .15s linear}.label{flex:1;display:flex;justify-content:center;align-items:center;height:100%;font-size:14px;border:1px solid var(--wc-input-border-color, var(--color-grey-2));border-radius:inherit;background:var(--bg-color, #fff);color:inherit;cursor:inherit}.label input{flex:1;min-width:36px;width:0;height:100%;padding:0 8px;line-height:1;border:0;border-radius:inherit;font:inherit;color:inherit;background:none;outline:none;box-shadow:none;cursor:inherit}.label input::placeholder{color:var(--color-grey-1)}.label .close{--wc-icon-size: 18px;margin:0 8px 0 4px;padding:4px;border-radius:50%;color:var(--color-grey-2);cursor:pointer;transition:background .15s linear}.label .close:hover{background:var(--color-plain-1)}.label .icon{--wc-icon-size: 16px;margin:0 8px 0 4px;color:var(--color-grey-2)}.suggestion{overflow:hidden;position:absolute;z-index:1;left:0;top:calc(100% + 4px);width:100%;padding:4px 0;border-radius:4px;box-shadow:0 2px 5px rgba(0,0,0,.15);transform-origin:top}.suggestion .list{width:100%;background:#fff}.suggestion.hide{display:none}.suggestion li{overflow:hidden;width:100%;height:30px;line-height:30px;padding:0 8px;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.suggestion li:hover,.suggestion li[focus]{background:var(--color-plain-2)}:host([round]){border-radius:26px}:host([round]) .label input{padding:0 10px}:host(:focus-within){box-shadow:0 0 0 2px var(--color-plain-a)}:host([disabled]){pointer-events:none;cursor:not-allowed}:host([disabled]) .label{border-color:var(--color-grey-1);background:var(--color-plain-1);opacity:.6}:host([readonly]){cursor:default}`,l`:host([size=m]){min-width:128px;height:24px;font-size:12px}:host([size=m]) .label{height:24px;font-size:12px}:host([size=m]) .icon{--wc-icon-size: 12px}:host([size=m][circle]){width:24px;height:24px}:host([size=xl]){min-width:224px;height:36px;font-size:14px}:host([size=xl]) .label{height:36px;font-size:14px}:host([size=xl]) .icon{--wc-icon-size: 14px}:host([size=xl][circle]){width:36px;height:36px}:host([size=xxl]){min-width:288px;height:44px;font-size:14px}:host([size=xxl]) .label{height:44px;font-size:14px}:host([size=xxl]) .icon{--wc-icon-size: 14px}:host([size=xxl][circle]){width:44px;height:44px}`];renderClear(){return s`<wc-icon class="close" name="close" @click=${this.clear} />`}render(){let e=h({suggestion:!0,hide:!this.#t.length});return s`
<div class="label">
<slot name="prepend">
<wc-icon class="icon" name=${this.icon}></wc-icon>
</slot>
<input
ref="input"
@input=${this.handleInput}
@change=${this.handleChange}
@keydown=${this.handleKeyDown}
@focus=${this.handleFocus}
placeholder=${this.placeholder}
maxlength=${this.maxlength}
minlength=${this.minlength}
disabled=${this.disabled}
readonly=${this.readOnly}
autofocus=${this.autofocus}
:value=${this.value}
/>
${this.clearable&&this.value?this.renderClear():""}
<slot name="append"></slot>
<div class=${e} ref="suggestion" #animation=${d}>
<div ref="scroller" class="scroller">
<ul class="list" @click=${this.handleClickItem} ref="list">
${this.#t.map((t,i)=>s`<li
focus=${this.#e===i?!0:null}
index=${i}
>
${t.value}
</li>`)}
</ul>
</div>
</div>
</div>
`}handleInput(e){let{lazy:t}=this;this.value=e.currentTarget.value,!(t&&Date.now()-this.#s<t)&&(this.#s=Date.now(),this.emitFetchSuggest())}handleClickItem(e){let t=e.target.getAttribute("index");this.#e=t,this.emitSelect()}clear(){this.$refs.input.value="",this.value="",this.$emit("change"),this.$emit("input")}handleChange(){this.$emit("change")}handleKeyDown(e){let{lazy:t,minlength:i,value:o}=this;if(e.keyCode===13)return e.preventDefault(),this.#e>-1&&this.#i?this.emitSelect():t&&Date.now()-this.#s<t||(this.#s=Date.now(),i&&o.length<i)?void 0:this.$emit("submit");if(e.keyCode===38||e.keyCode===40){e.preventDefault();let n=e.keyCode===38?-1:1;this.#e+=n,this.#e<0&&(this.#e=0),this.#e>this.#t.length-1&&(this.#e=this.#t.length-1),this.$requestUpdate()}}emitSelect(){let e=this.#t[this.#e];this.value=e.value,this.$refs.suggestion.$animate(!0),this.#i=!1,this.$requestUpdate(),this.$emit("change"),this.$emit("input"),this.$emit("select",{index:this.#e,value:e})}emitFetchSuggest(){this.$emit("fetch-suggest",{value:this.value,send:e=>{this.#t=e.slice(0,10),this.#e=-1,this.$requestUpdate()}})}handleFocus(){this.#i||(this.#i=!0,this.$refs.suggestion.$animate())}mounted(){this.autofocus&&r(e=>this.$refs.input.focus()),c(this,()=>{this.#i=!1,this.$refs.suggestion.$animate(!0)})}}u.reg("input");

View File

@ -0,0 +1,5 @@
import{css as r,html as s,Component as l,bind as i,nextTick as n}from"wkit";class c extends l{static props={type:"primary",to:"",autofocus:!1,disabled:!1,lazy:0};static styles=[r`:host{position:relative;display:inline-flex;border-radius:2px;user-select:none;-moz-user-select:none;font-size:inherit;cursor:pointer;transition:box-shadow .15s linear}:host .link{display:flex;justify-content:center;align-items:center;width:100%;padding:var(--padding, 0 2px);line-height:1;font-size:inherit;font-family:inherit;outline:none;color:inherit;cursor:inherit;text-decoration:none;transition:color .15s linear}:host .link::-moz-focus-inner{border:none}:host::after{position:absolute;bottom:-2px;left:0;width:100%;height:1px;border-bottom:1px dashed rgba(0,0,0,0);content:"";opacity:0;transition:opacity .15s linear}`,r`:host([type=primary]){color:var(--color-teal-2)}:host([type=primary])::after{border-color:var(--color-teal-1)}:host([type=primary]:not([disabled]):hover){color:var(--color-teal-1)}:host([type=primary]:not([disabled]):active){color:var(--color-teal-3)}:host([type=primary]:not([disabled]):focus-within){box-shadow:0 0 0 2px var(--color-teal-a)}:host([type=info]){color:var(--color-blue-2)}:host([type=info])::after{border-color:var(--color-blue-1)}:host([type=info]:not([disabled]):hover){color:var(--color-blue-1)}:host([type=info]:not([disabled]):active){color:var(--color-blue-3)}:host([type=info]:not([disabled]):focus-within){box-shadow:0 0 0 2px var(--color-blue-a)}:host([type=success]){color:var(--color-green-2)}:host([type=success])::after{border-color:var(--color-green-1)}:host([type=success]:not([disabled]):hover){color:var(--color-green-1)}:host([type=success]:not([disabled]):active){color:var(--color-green-3)}:host([type=success]:not([disabled]):focus-within){box-shadow:0 0 0 2px var(--color-green-a)}:host([type=warning]){color:var(--color-orange-2)}:host([type=warning])::after{border-color:var(--color-orange-1)}:host([type=warning]:not([disabled]):hover){color:var(--color-orange-1)}:host([type=warning]:not([disabled]):active){color:var(--color-orange-3)}:host([type=warning]:not([disabled]):focus-within){box-shadow:0 0 0 2px var(--color-orange-a)}:host([type=danger]){color:var(--color-red-2)}:host([type=danger])::after{border-color:var(--color-red-1)}:host([type=danger]:not([disabled]):hover){color:var(--color-red-1)}:host([type=danger]:not([disabled]):active){color:var(--color-red-3)}:host([type=danger]:not([disabled]):focus-within){box-shadow:0 0 0 2px var(--color-red-a)}:host([type=secondary]){color:var(--color-dark-2)}:host([type=secondary])::after{border-color:var(--color-dark-1)}:host([type=secondary]:not([disabled]):hover){color:var(--color-dark-1)}:host([type=secondary]:not([disabled]):active){color:var(--color-dark-3)}:host([type=secondary]:not([disabled]):focus-within){box-shadow:0 0 0 2px var(--color-dark-a)}:host([type=help]){color:var(--color-grey-2)}:host([type=help])::after{border-color:var(--color-grey-1)}:host([type=help]:not([disabled]):hover){color:var(--color-grey-1)}:host([type=help]:not([disabled]):active){color:var(--color-grey-3)}:host([type=help]:not([disabled]):focus-within){box-shadow:0 0 0 2px var(--color-grey-a)}`,r`:host(:not([disabled]):hover)::after,:host([underline])::after{opacity:1}:host([disabled]){cursor:not-allowed;opacity:.6}`];mounted(){this.stamp=0,this.autofocus&&n(o=>this.$refs.a.focus()),this._clickFn=i(this.$refs.a,"click",o=>{let{disabled:a,lazy:e}=this,t=Date.now();if(a){o.preventDefault(),o.stopPropagation();return}if(e>0&&t-this.stamp<e){o.preventDefault(),o.stopPropagation();return}this.stamp=t},!0)}render(){return s`
<a tabindex="0" ref="a" class="link" href=${this.to||"javascript:;"}>
<slot />
</a>
`}}c.reg("link");

View File

@ -0,0 +1,13 @@
import{css as e,html as i,Component as s,classMap as a}from"wkit";class n extends s{static props={value:{type:Boolean,default:!1},inactiveText:"",activeText:"",inlineText:!1,disabled:!1,readonly:!1};static styles=[e`:host{display:inline-flex;align-items:center;font-size:14px;cursor:pointer}:host label{display:flex;justify-content:center;align-items:center;min-width:32px;padding:0 8px 0 2px;line-height:1;-moz-user-select:none;user-select:none;white-space:nowrap;cursor:inherit;outline:none;color:var(--color-dark-1)}:host .dot{display:flex;align-items:center;justify-content:space-between;min-width:36px;height:18px;padding:0 4px;margin-right:5px;line-height:14px;border-radius:16px;background:var(--color-plain-3);transition:box-shadow .2s ease,background .2s ease}:host .dot::before{display:block;width:14px;height:14px;border-radius:50%;background:#fff;content:""}:host .dot::after{display:flex;padding:0 2px;font-size:12px;content:attr(st);color:#fff}:host .dot.open{flex-direction:row-reverse;background:var(--color-teal-1)}`,e`:host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-plain-a)}`,e`:host([size=m]){height:24px;font-size:12px}:host([size=m]) .dot{min-width:30px;height:16px;line-height:12px}:host([size=m]) .dot::before{width:12px;height:12px}:host([size=xl]){height:36px;font-size:14px}:host([size=xl]) .dot{min-width:35px;height:18px;line-height:14px}:host([size=xl]) .dot::before{width:14px;height:14px}:host([size=xxl]){height:44px;font-size:14px}:host([size=xxl]) .dot{min-width:35px;height:18px;line-height:14px}:host([size=xxl]) .dot::before{width:14px;height:14px}`,e`:host([type=primary]) .dot.open{background:var(--color-teal-1)}:host([type=primary]):host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-teal-a)}:host([type=info]) .dot.open{background:var(--color-blue-1)}:host([type=info]):host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-blue-a)}:host([type=success]) .dot.open{background:var(--color-green-1)}:host([type=success]):host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-green-a)}:host([type=warning]) .dot.open{background:var(--color-orange-1)}:host([type=warning]):host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-orange-a)}:host([type=danger]) .dot.open{background:var(--color-red-1)}:host([type=danger]):host(:focus-within) .dot{box-shadow:0 0 0 2px var(--color-red-a)}`,e`:host([readonly]),:host([disabled]){cursor:not-allowed;opacity:.6}:host([readonly]){cursor:default}`];toggleCheck(t){if(this.disabled||this.readOnly)return;t.stopPropagation(),this.value=!this.value;let o={value:this.value};this.$emit("input"),this.$emit("change",o)}handleClick(t){(t.type==="click"||t.keyCode===32)&&(t.preventDefault(),this.toggleCheck(t))}mounted(){}render(){let t=a({dot:!0,open:this.value});return i` <label
tabindex=${this.disabled||this.readOnly?"none":0}
@click=${this.handleClick}
@keydown=${this.handleClick}
>
<span
class=${t}
st=${this.inlineText?this.value?this.activeText:this.inactiveText:""}
></span>
<slot
>${this.inlineText?"":this.value?this.activeText:this.inactiveText}</slot
>
</label>`}}n.reg("switch");

View File

@ -0,0 +1,31 @@
import { css, svg, html, Component } from "wkit";
import SVG_DICT from "./svg.js";
let dict = SVG_DICT;
if (window.EXT_SVG_DICT) {
Object.assign(dict, EXT_SVG_DICT);
}
class Icon extends Component {
static props = {
name: {
type: String,
default: null,
observer(val) {
if (val === "") {
this.name = null;
}
}
}
};
static styles = css`:host{display:inline-flex;width:var(--wc-icon-size, 32px);height:var(--wc-icon-size, 32px)}:host(:not([name])){display:none}.icon{display:block;width:100%;height:100%;color:inherit;fill:currentColor}.icon.loading{animation:load 1.5s linear infinite}.icon circle{stroke:currentColor;animation:circle 1.5s ease-in-out infinite}:host([size=s]){width:20px;height:20px}:host([size=m]){width:24px;height:24px}:host([size=l]){width:32px;height:32px}:host([size=xl]){width:36px;height:36px}:host([size=xxl]){width:44px;height:44px}@keyframes circle{0%{stroke-dasharray:0,3812px;stroke-dashoffset:0}50%{stroke-dasharray:1906px,3812px;stroke-dashoffset:-287px}100%{stroke-dasharray:1906px,3812px;stroke-dashoffset:-2393px}}@keyframes load{to{transform:rotate(360deg)}}`;
render() {
return html`
<svg
class="icon ${this.name === "loading" ? "loading" : ""}"
viewBox="0 0 1024 1024"
>
${this.name === "loading" ? svg`<circle class="circle" cx="512" cy="512" r="384" fill="none" stroke-width="80" />` : svg`<path d="${dict[this.name]}" />`}
</svg>
`;
}
}
Icon.reg("icon");

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,13 @@
import{css as p,html as $,bind as a,unbind as x,Component as _}from"wkit";class z extends _{static props={axis:"xy",delay:1e3,distance:1};static styles=[p`:host{position:relative;display:block}:host .container{overflow:hidden;position:relative;width:100%;height:100%;max-height:inherit}:host .wrapper{overflow:auto;scrollbar-width:none;width:100%;height:100%;max-height:inherit;scrollbar-width:0}:host .wrapper::-webkit-scrollbar{display:none}:host .content{min-width:100%;width:fit-content;height:fit-content}`,p`.is-horizontal,.is-vertical{visibility:hidden;position:absolute;display:flex;justify-content:flex-end;opacity:0;user-select:none;transition:opacity .3s linear,visibility .3s linear}.is-horizontal .thumb,.is-vertical .thumb{display:block;border-radius:5px;background:rgba(44,47,53,.25);cursor:default;transition:width .1s linear,height .1s linear}.is-horizontal .thumb:hover,.is-vertical .thumb:hover{background:rgba(44,47,53,.5)}.is-horizontal{flex-direction:column;left:0;bottom:0;width:100%;height:10px}.is-horizontal .thumb{width:0;height:6px}.is-horizontal .thumb:hover{height:10px}.is-vertical{top:0;right:0;width:10px;height:100%}.is-vertical .thumb{width:6px;height:0}.is-vertical .thumb:hover{width:10px}`,p`:host(:hover) .is-horizontal,:host(:hover) .is-vertical{visibility:visible;opacity:1}:host([axis=x]) .wrapper{overflow-y:hidden}:host([axis=x]) .is-vertical{display:none}:host([axis=y]) .wrapper{overflow-x:hidden}:host([axis=y]) .is-horizontal{display:none}:host([disabled]) .wrapper{overflow:hidden}:host([disabled]) .is-vertical,:host([disabled]) .is-horizontal{display:none}`];stamp=0;cache={xBar:0,yBar:0,thumbX:0,thumbY:0};get scrollTop(){return this.$refs.box?.scrollTop||0}set scrollTop(t){t=+t,t===t&&(this.$refs.box.scrollTop=t)}get scrollLeft(){return this.$refs.box?.scrollLeft||0}set scrollLeft(t){t=+t,t===t&&(this.$refs.box.scrollLeft=t)}get scrollHeight(){return this.$refs.box?.scrollHeight}get scrollWidth(){return this.$refs.box?.scrollWidth}__init__(t){let e=this.offsetWidth,r=this.offsetHeight,s=this.scrollWidth,l=this.scrollHeight,h=50,i=50;h=r*(r/l)>>0,i=e*(e/s)>>0,h<50&&(h=50),i<50&&(i=50),i>=e&&(i=0),h>=r&&(h=0),this.cache.yBar=h,this.cache.xBar=i,i>0?(this.$refs.x.parentNode.style.display="flex",this.$refs.x.style.width=i+"px"):this.$refs.x.parentNode.style.display="none",h>0?(this.$refs.y.parentNode.style.display="flex",this.$refs.y.style.height=h+"px"):this.$refs.y.parentNode.style.display="none"}_fetchScrollX(t){let{xBar:e}=this.cache,{scrollWidth:r,offsetWidth:s}=this;return t<0?t=0:t>s-e&&(t=s-e),this.scrollLeft=(r-s)*(t/(s-e)),this.$refs.x.style.transform=`translateX(${t}px)`,t}_fetchScrollY(t){let{yBar:e}=this.cache,{scrollHeight:r,offsetHeight:s}=this;return t<0?t=0:t>s-e&&(t=s-e),this.scrollTop=(r-s)*(t/(s-e)),this.$refs.y.style.transform=`translateY(${t}px)`,t}_fireReachEnd(t="reach-bottom"){let e=this.delay,{scrollHeight:r,offsetHeight:s}=this,l=this.scrollTop,h=Date.now();if(h-this.stamp>e){if(t==="reach-bottom"){if(s+l<r)return}else if(l>0)return;this.stamp=h,this.$emit(t)}}mounted(){let t,e,r,s,l=i=>{let{thumbY:c,thumbX:n}=this.cache;t!==void 0&&(r=this._fetchScrollX(n+i.pageX-t)),e!==void 0&&(s=this._fetchScrollY(c+i.pageY-e))},h=i=>{Math.abs(i.pageY-e)>this.distance&&this._fireReachEnd(i.pageY>e?"reach-bottom":"reach-top"),t=void 0,e=void 0,this.cache.thumbX=r||0,this.cache.thumbY=s||0,delete this._active,x(document,"mousemove",l),x(document,"mouseup",h)};a(this.$refs.box,"scroll",i=>{if(i.stopPropagation(),this._active)return;let{xBar:c,yBar:n,thumbX:m,thumbY:f}=this.cache,{axis:d,scrollHeight:y,scrollWidth:g,offsetHeight:u,offsetWidth:b,scrollTop:v,scrollLeft:w}=this;if(!(c===0&&n===0)){if((d==="y"||d==="xy")&&n){let o=~~(v/(y-u)*(u-n));o!==f&&(this.cache.thumbY=o,this.$refs.y.style.transform=`translateY(${o}px)`,Math.abs(o-f)>this.distance&&this._fireReachEnd(o>f?"reach-bottom":"reach-top"))}if((d==="x"||d==="xy")&&c){let o=~~(w/(g-b)*(b-c));o!==m&&(this.cache.thumbX=o,this.$refs.x.style.transform=`translateX(${o}px)`)}this.$emit("scroll")}}),a(this.$refs.y,"mousedown",i=>{e=i.pageY,this._active=!0,a(document,"mousemove",l),a(document,"mouseup",h)}),a(this.$refs.x,"mousedown",i=>{t=i.pageX,this._active=!0,a(document,"mousemove",l),a(document,"mouseup",h)}),this.__observer=new ResizeObserver(this.__init__.bind(this)),this.__observer.observe(this.$refs.cont)}unmounted(){this.__observer?.disconnect()}render(){return $`
<div class="container">
<div class="wrapper" ref="box">
<div class="content" ref="cont"><slot></slot></div>
</div>
</div>
<div class="is-horizontal">
<span ref="x" class="thumb"></span>
</div>
<div class="is-vertical">
<span ref="y" class="thumb"></span>
</div>
`}}z.reg("scroll");

View File

@ -0,0 +1,8 @@
import { css, html, Component } from "wkit";
class Space extends Component {
static styles = css`:host{display:block}.container{display:flex;flex-wrap:wrap;align-items:center;width:100%;padding:6px 0;gap:12px}:host([vertical]) .container{flex-direction:column}:host([justify]) .container{justify-content:space-between}:host([gap=s]) .container{padding:2px 0;gap:4px}:host([gap=m]) .container{padding:4px 0;gap:8px}:host([gap=l]) .container{padding:6px 0;gap:12px}:host([gap=xl]) .container{padding:8px 0;gap:16px}:host([gap=xxl]) .container{padding:10px 0;gap:20px}:host([gap=xxxl]) .container{padding:12px 0;gap:24px}`;
render() {
return html`<div class="container"><slot /></div>`;
}
}
Space.reg("space");

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,54 @@
var J=Object.defineProperty;var V=(i,e,t)=>e in i?J(i,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):i[e]=t;var d=(i,e,t)=>(V(i,typeof e!="symbol"?e+"":e,t),t),q=(i,e,t)=>{if(!e.has(i))throw TypeError("Cannot "+t)};var b=(i,e,t)=>(q(i,e,"read from private field"),t?t.call(i):e.get(i)),m=(i,e,t)=>{if(e.has(i))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(i):e.set(i,t)},T=(i,e,t,o)=>(q(i,e,"write to private field"),o?o.call(i,t):e.set(i,t),t);var C=(i,e,t)=>(q(i,e,"access private method"),t);import"wkit";var c=Symbol("router"),h=Symbol("router-view"),y=Symbol("store"),x=new Set;var P=class extends WeakMap{broadcast(){for(let e of x)e.$requestUpdate()}assign(e){x.add(e)}deassign(e){x.add(e)}},Y=new P;Object.defineProperty(window,"wkitd",{get(){return Y},set(i){console.error("Can not set readonly property wkitd of window")},enumerable:!1});import{html as Z,css as ne,Component as F}from"wkit";var B=encodeURIComponent,W=decodeURIComponent;function w(){}function H(i,e,t){Object.defineProperty(i,e,{get(){return t},set(o){},enumerable:!1})}function U(i,e,t){var o;if(Array.isArray(e))e.forEach(function(r,s){o=i?`${i}[${Array.isArray(r)?s:""}]`:s,typeof r=="object"?U(o,r,t):t(o,r)});else for(let r in e)o=i?`${i}[${r}]`:r,typeof e[r]=="object"?U(o,e[r],t):t(o,e[r])}function M(i=""){let e=new URLSearchParams(i),t=Object.create(null);for(let[o,r]of e.entries()){let s=W(o),n=W(r),a=0;if(/(\w+)\[(\w*?)\]/.test(s)){let u=RegExp.$1,l=RegExp.$2;s=u,!l||+l==+l?(n=[n],a|=2):(a|=1,n={[l]:n})}t[s]?a&2?t[s]=t[s].concat(n):a&1?Object.assign(t[s],n):(Array.isArray(t[n])||(t[s]=[t[s]]),t[s].push(n)):t[s]=n}return t}function v(i={}){if(i===null)return"";if(typeof i=="string"||typeof i=="number"||typeof i=="boolean")return i;let e=[];return typeof i=="object"&&U("",i,function(o,r){e.push(o+"="+B(r))}),e.join("&")}import{Component as ie}from"wkit";import{bind as L}from"wkit";var Q=/^(#!|#)[\/]+?/,ee=/(\/[^/]*)(:[A-Za-z0-9_]+)(\?)?/g,k="hash",te="history",R=class{type=k;#e=new Map;#s=new Set;#n=new Map;#t=!1;#r=Object.create(null);#o;constructor(e=k){this.type=e,L(window,"popstate",this.#i.bind(this))}get route(){return this.#r}get views(){return Array.from(this.#s)}#i(e){this.#t&&this.#p()}#h(e){if(e.path==="!")e.regexp=null;else{let t=[],o;if(e.path.includes("?")&&e.path.at(-1)!=="?")throw new SyntaxError(`The exp "?" can only be used in the last.
${JSON.stringify(e)}
`);o=e.path.replace(ee,function(r,s,n,a){return t.push(n.slice(1)),s==="/"&&(s="/?"),s+"([A-Za-z0-9_]+)"+a}),o="^"+o+"$",e.regexp=new RegExp(o),e.vars=t}return e}#a(e){if(e.path!=="!"&&e.path[0]!=="/"){console.error('route path must start with "/"');return}e.path=e.path.replace(/^[\/]+|[\/]+$|\s+/g,"/"),this.#e.set(e.path,this.#h(e)),this.#s.add(e.name)}#p(){let e=this.type===k,t=window.wkitd.get(h),o=location.hash||"#/",r=e?o:location.href.replace(location.origin,"").replace(o,""),s;if(r.includes("?")&&([r,s]=r.split("?")),r=r.replace(Q,"/"),!(!t||r===this.#r.path)){for(let[n,a]of this.#e){let u=r.match(a.regexp);if(u){let l=Object.create(null);for(let E=1;E<u.length;E++)l[[a.vars[E-1]]]=u[E];let A={path:r,name:a.name,params:l,query:M(s)};return this.#o?this.#o(this.route,A,()=>{this.#l(A)}):this.#l(A)}}if(this.#e.get("!")){let n=this.#e.get("!");t.current=n.name,this.#r={path:r,name:n.name,params:{},query:{}}}}}#l(e){let t=window.wkitd.get(h);t.current=e.name,this.#r=e,this.#c()}#c(){for(let[e,t]of this.#n)t.call(e,this.route)}init(){this.#t=!0,this.#i()}rsync(e,t){this.#n.set(e,t),this.#t&&this.#c()}beforeEach(e=w){this.#o=e}addRoute(e){Array.isArray(e)?e.forEach(t=>{this.#a(t)}):this.#a(e),this.#t&&this.#i()}go(e=0){history.go(e)}back(){this.go(-1)}forward(){this.go(1)}push(e={path:"",query:{}},t=!1){let o="",r="";typeof e=="string"?o=e.trim():(r=v(e.query||""),o=e.path+(r?`?${r}`:"")),!(!o&&o===location.hash.slice(1))&&(this.type===k?t?location.replace(o.replace(/^\//,"#/")):location.hash=o:(t?window.history.replaceState({path:o},null,o+r):window.history.pushState({path:o},null,o+r),this.#p()))}replace(e={path:"",query:{}}){this.push(e,!0)}};function j(){return()=>new R}function re(){return()=>new R(te)}import{Component as D,html as oe,css as N,raw as I}from"wkit";var $,_=class extends D{constructor(){super(...arguments);m(this,$,[])}created(){window.wkitd.set(h,this)}sync(t){T(this,$,t)}render(){let t={immediate:!0,custom:[{transform:"translateX(-32px)",opacity:0},{transform:"translateX(0)",opacity:1}]};if(this.keepAlive){let o=b(this,$).map(r=>[this.transition?`<${r} ref="${r}" :__keep_alive__="%s" #animation="%s" style="%s"></${r}>`:`<${r} ref="${r}" :__keep_alive__="%s" style=%s></${r}>`,[this.current===r,{...t,immediate:this.current===r},this.current===r?"":"display:none"]]);return I(o.map(r=>r[0]).join(""),o.map(r=>r[1]).flat())}else if(this.current)return this.transition?I(`<${this.current} #animation="%s"></${this.current}>`,[t]):I(`<${this.current}></${this.current}>`)}};$=new WeakMap,d(_,"props",{keepAlive:!1,transition:!1,current:{type:String,default:"",attribute:!1,observer(t,o){this.keepAlive&&t&&(o&&this.$refs[o]&&this.$refs[o].deactivated(),this.$refs[t]?.$requestUpdate(),this.$refs[t]?.$animate(),this.$refs[t]?.activated())}}}),d(_,"styles",N`
:host {
display: block;
}
`);var f,O,X,S,z,g=class extends D{constructor(){super(...arguments);m(this,O);m(this,S);m(this,f,"")}mounted(){this.$router.rsync(this,t=>{this.classList.toggle("active",t.path===this.to.path)})}render(){return T(this,f,C(this,S,z).call(this)),oe`<a title=${b(this,f)} @click=${C(this,O,X)}>
<slot></slot
></a>`}};f=new WeakMap,O=new WeakSet,X=function(){let t=this.$router.type,{path:o}=this.to;this.disabled||(t==="hash"?location.hash=b(this,f):this.$router.push(this.to))},S=new WeakSet,z=function(){let t=this.$router.type,{path:o="",query:r={}}=this.to,s=typeof r=="string"?r.replaceAll("?",""):v(r);return o=o.replace(/^\//,""),s&&(o+="?"+s),"/"+o},d(g,"props",{to:Object,disabled:!1}),d(g,"styles",N`
:host {
display: inline-flex;
align-items: center;
-webkit-user-select: none;
user-select: none;
}
a {
display: flex;
align-items: center;
width: 100%;
height: 100%;
color: inherit;
text-decoration: inherit;
cursor: pointer;
}
:host([disabled]) a {
opacity: 0.6;
cursor: not-allowed;
}
`);customElements.get("router-view")||customElements.define("router-view",_);customElements.get("router-link")||customElements.define("router-link",g);function Re({history:i=j(),routes:e=[]}={}){let t=i();window.wkitd.set(c,t),t.addRoute(e);function o(){Object.defineProperty(ie.prototype,"$router",{get(){return window.wkitd.get(c)},set(r){console.error("Can not set readonly property $router of Component")},enumerable:!1})}return o.beforeEach=t.beforeEach.bind(t),o}import{Component as K}from"wkit";function G(i){return i===null?i:new Proxy(i,{get(e,t,o){let r=Reflect.get(e,t,o);return typeof r=="object"?G(r):r},set(e,t,o,r){return Reflect.set(e,t,o,r),window.wkitd.broadcast(),!0}})}function se(i={}){let e=!1;return function(){Object.defineProperty(K.prototype,"$store",{get(){return window.wkitd.get(y)},set(t){if(e)return console.error("Can not set readonly property $store of Component");window.wkitd.set(y,G(t)),e=!0},enumerable:!1}),K.prototype.$store=i}}var p=class extends F{};function Ie({data:i={},styles:e=[],methods:t={},mounted:o=w,render:r}={}){return new function(){p.props=i,p.styles=e,Object.assign(p.prototype,t,{mounted:o,created(){H(F.prototype,"$app",this)}}),this.use=function(s=w,...n){return s.apply(p.prototype,n),this},this.mount=function(){let s=window.wkitd.get(c);r?p.prototype.render=r:s?p.prototype.render=function(){return Z`<router-view></router-view>`}:(p.styles=ne`
:host {
font-family: monospace;
color: #647889;
}
.code {
margin: 16px 0;
background: #f7f8fb;
}
`,p.prototype.render=function(){return Z`
<h1>It works!!!</h1>
<cite>
If you don't use router, you may define the
<b>render</b> property.
</cite>
<div class="code">
<pre><code> createApp({</code></pre>
<pre><code> render() {</code></pre>
<pre><code> return html\`&lt;wc-home&gt;&lt;/wc-home&gt;\`</code></pre>
<pre><code> }</code></pre>
<pre><code> })</code></pre>
<pre><code> .mount()</code></pre>
</div>
`}),s&&(p.prototype.mounted=function(...n){let a=window.wkitd.get(h);if(a)a.sync(s.views),s.init(),o.call(this,...n);else throw new Error('<router-view /> not found, "Router" works Unexpected.')}),p.reg("app")}}}function We(){return window.wkitd.get(y)}function He(){return window.wkitd.get(c)}function Me(){return window.wkitd.get(c)?.route}export{Ie as createApp,Re as createRouter,se as createStore,j as createWebHashHistory,re as createWebHistory,Me as getCurrentPage,He as getRouter,We as getStore};

View File

@ -0,0 +1,15 @@
/**
* {}
* @author yutent<yutent.io@gmail.com>
* @date 2023/09/04 11:11:51
*/
import { createStore } from 'wkitd'
export default createStore({
HOST_DATA: {},
activeDomain: localStorage.getItem('last_domain') || '', //当前选中的域名
domains: [],
records: [],
tmp_records: []
})

View File

@ -0,0 +1,100 @@
/**
* {}
* @author yutent<yutent.io@gmail.com>
* @date 2023/09/04 19:18:28
*/
import { nextTick } from 'wkit'
const APP_CONFIG_DIR = `${native.env.CONFIG_DIR}/hosts-switch`
const HOST_FILE = `${APP_CONFIG_DIR}/host.cache`
const LOCK_FILE = `${APP_CONFIG_DIR}/lock`
export function noop() {}
export function checkPermission() {
return native.fs.access('/etc/hosts', 'a+')
}
export async function getHistory() {
if (await native.fs.isfile(LOCK_FILE)) {
let cache = await native.fs.read(HOST_FILE)
return JSON.parse(cache)
}
let cache = await native.fs.read('/etc/hosts')
let records = cache.split(/[\n\r]+/)
let list = []
let dict = {}
records.forEach(str => {
str = str.trim()
let matches = str.match(/^(#*?)\s*(\d+\.\d+\.\d+\.\d+)\s+(.*)/)
if (matches) {
let names = matches[3].split(/\s+/).map(it => it.trim())
let name
while ((name = names.pop())) {
list.push({ ip: matches[2], enabled: !matches[1], name })
}
}
})
records = null
list.forEach(it => {
it.name = it.name.split('.')
let domain = it.name.splice(-2, 2).join('.')
if (domain === 'com.cn' || domain === 'org.cn' || domain === 'net.cn') {
domain = it.name.pop() + '.' + domain
}
if (dict[domain]) {
dict[domain].push({
value: it.ip,
enabled: it.enabled,
record: it.name.join('.') || '@',
remark: ''
})
} else {
dict[domain] = [
{
value: it.ip,
enabled: it.enabled,
record: it.name.join('.') || '@',
remark: ''
}
]
}
})
list = null
try {
await native.fs.write(HOST_FILE, JSON.stringify(dict))
await native.fs.write(LOCK_FILE, '')
} catch (err) {}
return dict
}
export function saveHosts(dict) {
nextTick(async () => {
var txt = ''
for (let k in dict) {
for (let it of dict[k]) {
if (it.enabled) {
var name = it.record === '@' ? '' : it.record
if (name) {
name += '.'
}
txt += `${it.value.padEnd(15, ' ')} ${name + k}\n`
}
}
txt += '\n'
}
try {
await native.fs.write(HOST_FILE, JSON.stringify(dict))
await native.fs.write('/etc/hosts', txt)
} catch (err) {
layer.alert(err)
}
})
}