Compare commits

..

No commits in common. "master" and "old" have entirely different histories.
master ... old

67 changed files with 1131 additions and 1454 deletions

13
.gitignore vendored
View File

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

16
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Main Process",
"type": "node",
"request": "launch",
"cwd": "${workspaceRoot}",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
},
"args": ["."]
}
]
}

View File

@ -1,26 +1,11 @@
# Hosts切换器
> linux专用。 macos/windows自己找别的。
![preview](./preview/image.png)
## 安装
添加[`repo.debianx.in`](https://repo.debianx.in)源之后, 可直接使用`apt install hosts-switch`直接安装.
# 伪域名解析工具
> macos & linux专用。windows自己找别的。
## Update Logs
### v3.0.0 - 2023-09-05
- 放弃electron, 改用`gtk3+webkit2`开发
### v2.0.0 - 2022-03-31
- 分离electron, 独立打包
- 优化hosts文件检测
- 优化域名操作
### v1.1.0 - 2019-12-11
# v1.1.0 - 2019-12-11
- 支持域名删除、编辑
- 支持克隆记录
- 优化记录编辑、UI

View File

@ -1,32 +0,0 @@
#!/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
View File

@ -1,12 +0,0 @@
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

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 11 KiB

BIN
icons/512x512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
icons/app.icns Normal file

Binary file not shown.

36
icons/dns-host.svg Normal file
View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
<style type="text/css">
.st0{fill:#00A0EF;}
.st1{fill:#FFFFFF;}
.st2{fill:#0195DE;}
</style>
<path class="st0" d="M895.4,974.5H104.6c-43.6-0.1-79-35.5-79.1-79.1V104.6c0.1-43.6,35.5-79,79.1-79.1h790.8
c43.6,0.1,79,35.5,79.1,79.1v790.8C974.3,939,939,974.4,895.4,974.5L895.4,974.5z"/>
<path class="st1" d="M155,370.5h90.9c19.8,0,35.6,2,47.4,4c14.6,4.1,27.6,12.3,37.6,23.7c10.2,11.7,18.2,25,23.7,39.5
c5.9,15.8,7.9,35.6,7.9,59.3c0,19.8-2,37.6-7.9,53.4c-5.2,16.2-14,31.1-25.7,43.5c-9.9,9.7-22.2,16.5-35.6,19.8
c-11.9,4-25.7,5.9-43.5,5.9h-94.9V370.5H155z"/>
<polygon class="st1" points="406.1,619.6 406.1,370.5 455.5,370.5 556.2,536.6 556.2,370.5 603.7,370.5 603.7,619.6 554.2,619.6
453.5,457.5 453.5,619.6 "/>
<path class="st1" d="M643.2,538.6l49.4-4c2,15.8,7.9,27.7,17.8,35.6c10.1,8,22.7,12.2,35.6,11.9c12.6,0.6,25.1-2.9,35.6-9.9
c7.3-5.7,11.7-14.4,11.9-23.7c0-5.9-2-9.9-5.9-15.8c-4-4-9.9-7.9-17.8-9.9c-5.9-2-19.8-5.9-39.5-9.9c-20.5-4.1-39.9-12.1-57.3-23.7
c-15.1-12-23.8-30.2-23.7-49.4c0.5-12.5,3.9-24.7,9.9-35.6c7.6-11.6,18.7-20.6,31.6-25.7c15.8-6,32.6-8.7,49.4-7.9
c31.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.7c-7.9-5.9-17.8-7.9-31.6-7.9s-25.7,4-33.6,9.9
c-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.8c23.7,5.9,41.5,11.9,53.4,17.8s21.7,13.8,27.7,25.7
c6.7,12.1,10.2,25.7,9.9,39.5c0,13.8-4,27.7-11.9,39.5c-8.3,12.3-20,21.9-33.6,27.7c-17,6.9-35.1,10.2-53.4,9.9
c-31.6,0-57.1-5.9-73.2-21.7C655,590,647.3,566.2,643.2,538.6L643.2,538.6z"/>
<path class="st2" d="M273.6,576.1c7.9-2,15.8-5.9,19.8-9.9c5.9-4,9.9-13.8,13.8-23.7c4-11.9,5.9-25.7,5.9-45.5s-2-33.6-5.9-43.5
c-3-8.7-7.7-16.8-13.8-23.7c-6.5-6.2-14.8-10.4-23.7-11.9c-7.9-2-19.8-2-41.5-2h-21.7V578h37.6C257.8,578.1,267.7,578.1,273.6,576.1
L273.6,576.1z"/>
<path class="st2" d="M502.1,974.5h393.4c43.6-0.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,2
c-2-11.9-5.9-21.7-13.8-27.7c-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.8
c5.9,5.9,21.7,9.9,45.5,15.8s41.5,11.9,53.4,17.8s21.7,13.8,27.7,25.7c6.7,12.1,10.2,25.7,9.9,39.5c0,13.8-4,27.7-11.9,39.5
c-8.3,12.3-20,21.9-33.6,27.7c-17,6.9-35.1,10.2-53.4,9.9c-31.6,0-57.1-5.9-73.2-21.7c-18-17.8-25.7-41.5-29.7-69.2l49.4-4
c2,15.8,7.9,27.7,17.8,35.6c10.1,8,22.7,12.2,35.6,11.9c12.6,0.6,25.1-2.9,35.6-9.9c7.3-5.7,11.7-14.4,11.9-23.7
c0-5.9-2-9.9-5.9-15.8c-4-4-9.9-7.9-17.8-9.9c-5.9-2-19.8-5.9-39.5-9.9c-20.5-4.1-39.9-12.1-57.3-23.7
c-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
c0,19.8-2,37.6-7.9,53.4c-5.2,16.2-14,31.1-25.7,43.5c-9.9,9.7-22.2,16.5-35.6,19.8c-11.9,4-25.7,5.9-43.5,5.9H155v-6.9l-0.4,6.9
L502.1,974.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

55
package.json Normal file
View File

@ -0,0 +1,55 @@
{
"name": "dns-host",
"version": "1.2.2",
"description": "伪域名解析",
"main": "src/main.js",
"scripts": {
"start": "electron .",
"pack": "electron-builder",
"pack:mac": "electron-builder --mac",
"pack:linux": "electron-builder --linux"
},
"author": {
"name": "yutent",
"email": "yutent@doui.cc"
},
"homepage": "https://yutent.me",
"license": "MIT",
"dependencies": {
"iofs": "^1.3.2"
},
"build": {
"appId": "dns.host",
"productName": "dns-host",
"copyright": "Copyright © 2019 ${author}",
"directories": {
"buildResources": "icons",
"output": "build"
},
"electronDownload": {
"version": "9.4.4",
"mirror": "https://npm.taobao.org/mirrors/electron/"
},
"files": ["src/**/*", "node_modules/iofs/*"],
"mac": {
"category": "public.app-category.developer-tools",
"target": "dmg",
"icon": "icons/app.icns",
"darkModeSupport": false
},
"linux": {
"category": "Development",
"target": [
{
"target": "deb",
"arch": "x64"
}
],
"icon": "./icons/"
}
},
"devDependencies": {
"electron": "9.4.4",
"electron-builder": "22.7.0"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

1
src/css/app.css Normal file
View File

@ -0,0 +1 @@
html{font-size:1vw;width:100%;height:100vh}body{position:fixed;left:0;top:0;display:flex;width:100%;height:100%;line-height:1.5;font-size:1.4rem;color:#62778d}.layout-left{display:flex;flex-direction:column;width:18rem;height:100vh;background:#526273}.layout-left wc-scroll{flex:1}.layout-left .domain-list{width:100%}.layout-left .domain-list .item{display:flex;justify-content:flex-end;align-items:center;height:4rem;padding:0 2rem 0 1rem;color:#f3f5fb;cursor:pointer;transition:background .1s ease-in-out}.layout-left .domain-list .item wc-icon{--size:1.2rem;margin:auto -1.5rem auto .5rem;color:#7e909a}.layout-left .domain-list .item:hover,.layout-left .domain-list .item.active{background:#62778d}.layout-left .domain-list .item.active{border-left:0.3rem solid #ffb618;color:#ffb618;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:5rem;padding:0 1rem}.layout-right{flex:1;display:flex;flex-direction:column;background:#f7f8fb}.layout-right .toolbar{display:flex;align-items:center;justify-content:space-between;height:4rem;padding:0 1.5rem;background:#e8ebf4;box-shadow:0 0.2rem 0.5rem rgba(0,0,0,0.1)}.layout-right .toolbar wc-input{width:20rem}.layout-right .main{overflow:hidden;flex:1;display:flex;flex-direction:column;margin:2rem 1rem;padding:0 .5rem;background:#fff}.layout-right .thead{display:flex;align-items:center;justify-content:center;height:4rem;margin-bottom:.8rem;border-bottom:0.1rem solid #e8ebf4;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:4rem}.layout-right .record-list .item{display:flex;justify-content:center;align-items:center;height:4rem;padding:0 1rem;border-bottom:0.1rem solid #e8ebf4;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:.5rem}.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:5rem;background:rgba(255,233,233,0.95);-webkit-backdrop-filter:blur(0.5rem);backdrop-filter:blur(0.5rem)}.permission-error pre{font-family:'Courier New', Courier, monospace;font-size:1.6rem;color:#eb3b48}.permission-error fieldset{width:60rem;padding:0 3rem 3rem;border:0.1rem solid #ffb618;border-radius:.8rem}.permission-error fieldset legend{padding:0 1rem;font-size:1.6rem}.permission-error fieldset dt{margin-top:2rem;font-weight:bold}.permission-error fieldset code{display:block;padding:.8rem 1rem;margin-top:.8rem;border-left:0.3rem solid #dae1e9;background:rgba(255,255,255,0.3);font-family:'Courier New', Courier, monospace}.permission-error.show{display:flex}.context-menu{display:flex;flex-direction:column;width:10rem;padding:.5rem 0;background:#fff}.context-menu .item{height:3rem;line-height:3rem;padding:0 1.5rem;cursor:pointer}.context-menu .item:hover{background:#f2f5fc}

148
src/css/app.scss Normal file
View File

@ -0,0 +1,148 @@
@charset "UTF-8";
/**
* {sonist app style}
* @authors yutent<yutent@doui.cc>
* @date 2018/12/16 17:15:07
*/
@import "./var.scss";
html {font-size:1vw;width:100%;height:100vh;}
body {
position:fixed;left:0;top:0;
display:flex;
width:100%;height:100%;line-height:1.5;
font-size:1.4rem;color:nth($cd, 1);
}
.layout-left {
display:flex;flex-direction:column;
width:18rem;height:100vh;
background:nth($cd, 2);
wc-scroll {flex:1;}
.domain-list {
width:100%;
.item {
display:flex;justify-content:flex-end;align-items:center;
height:4rem;padding:0 2rem 0 1rem;
color:nth($cp, 1);cursor:pointer;
@include ts(background);
wc-icon {--size:1.2rem;margin:auto -1.5rem auto .5rem;color:nth($cgr, 3);}
&:hover,
&.active {background:nth($cd, 1);}
&.active {border-left:.3rem solid nth($co, 1);color:nth($co, 1);font-weight:bold;}
&.blank {
justify-content:center;cursor:default;
&:hover {background:none;}
}
}
}
.action {
display:flex;align-items:center;
height:5rem;padding:0 1rem;
}
}
.layout-right {
flex:1;display:flex;flex-direction:column;
background:#f7f8fb;
.toolbar {
display:flex;align-items:center;justify-content:space-between;
height:4rem;padding:0 1.5rem;
background:nth($cp, 2);box-shadow:0 .2rem .5rem rgba(0, 0, 0, .1);
wc-input {width:20rem;}
}
.main {
overflow:hidden;
flex:1;display:flex;flex-direction:column;margin:2rem 1rem;padding:0 .5rem;
background:#fff;
}
.thead {
display:flex;align-items:center;justify-content:center;
height:4rem;margin-bottom:.8rem;
border-bottom:.1rem solid nth($cp, 2);text-align:center;
span {flex:1;}
.long {flex:1.5;}
}
wc-scroll {flex:1;}
.record-list {
width:100%;line-height:4rem;
.item {
display:flex;justify-content:center;align-items:center;
height:4rem;padding:0 1rem;
border-bottom:.1rem solid nth($cp, 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:.5rem;}
&: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:5rem;
background:rgba(255, 233, 233, .95);
backdrop-filter:blur(.5rem);
pre {font-family:'Courier New', Courier, monospace;font-size:1.6rem;color:nth($cr, 2);}
fieldset {
width:60rem;padding:0 3rem 3rem;
border:.1rem solid nth($co, 1);border-radius:.8rem;
legend {padding:0 1rem;font-size:1.6rem;}
dt {margin-top:2rem;font-weight:bold;}
code {
display:block;
padding:.8rem 1rem;margin-top:.8rem;
border-left:.3rem solid nth($cp, 3);
background:rgba(255, 255, 255, .3);
font-family:'Courier New', Courier, monospace;
}
}
&.show {display:flex;}
}
.context-menu {
display:flex;
flex-direction:column;
width:10rem;
padding:.5rem 0;
background:#fff;
.item {
height:3rem;line-height:3rem;padding:0 1.5rem;cursor:pointer;
&:hover {background:#f2f5fc;}
}
}

15
src/css/var.scss Normal file
View File

@ -0,0 +1,15 @@
$ct: #3fc2a7 #19b491 #16967a;
$cg: #58d68d #2ecc71 #27ae60;
$cpp: #ac61ce #9b59b6 #8e44ad;
$cb: #66b1ff #409eff #3a8ee6;
$cr: #ff5061 #eb3b48 #ce3742;
$co: #ffb618 #f39c12 #e67e22;
$cp: #f3f5fb #e8ebf4 #dae1e9;
$cgr: #aabac3 #90a3ae #7e909a;
$cd: #62778d #526273 #425064;
@mixin ts($c: all, $t: .1s, $m: ease-in-out){
transition:$c $t $m;
}

BIN
src/images/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

106
src/index.html Normal file
View File

@ -0,0 +1,106 @@
<!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="新增域名"
color="blue"
circle icon="plus"
@active="addDomain"></wc-button>
</section>
</div>
<div class="layout-right">
<header class="toolbar">
<wc-button icon="plus" color="teal" @active="addRecord">新增记录</wc-button>
<wc-button icon="fly" color="blue" @active="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 auto-border autofocus class="long" @change="updateCacheDict(it)" :duplex="it.record" label="根域请填 @"></wc-input>
<span>A</span>
<wc-input auto-border class="long" :duplex="it.value" label="请填写IP"></wc-input>
<section class="long">
<wc-switch :duplex="it.enabled" @change="recordChanges(it)"></wc-switch>
<wc-button size="mini" color="red" @active="$remove">删除</wc-button>
<wc-button size="mini" color="blue" @active="clone(it)">克隆</wc-button>
</section>
<wc-input auto-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" @active="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>

223
src/js/app.js Normal file
View File

@ -0,0 +1,223 @@
/**
* {sonist app}
* @author yutent<yutent@doui.cc>
* @date 2018/12/16 17:15:57
*/
import '/lib/anot.js'
import '/lib/form/button.js'
import '/lib/form/input.js'
import '/lib/form/switch.js'
import layer from '/lib/layer/index.js'
import Utils from '/lib/utils.js'
const log = console.log
const { ipcRenderer } = require('electron')
const $doc = Anot(document)
var dict = {}
var 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' || /^[\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}」?`, (val, done) => {
if (dict[this.editDomain].length > 0) {
return layer.toast(
'该域名下有主机记录, 请先删除主机记录后再删除域名',
'error'
)
}
done()
})
.then(res => {
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 && val !== this.editDomain) {
if (/^[\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 = ipcRenderer.sendSync('dns-host', { type: 'check' })
if (check) {
dict = ipcRenderer.sendSync('dns-host', { type: 'history' })
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') || '')
} 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
}
ipcRenderer.send('dns-host', { type: 'set', data: dict })
layer.toast('保存成功', 'success')
}
}
})

8
src/lib/anot.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,60 @@
@charset "UTF-8";
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2014-10-10 00:45:09
*
* douiCSS
*
* ,
* do-st-*
* do-fn-*
* do-mod-modname
* UIdo-uiname, .do-uiname
* .do-layer .body { ... }
*
*
* 1 display float position overflow z-index /
* 2 width height margin padding border
* 3 line-height font-size vertical-align text-align user-select outline ....
* 4 color background opacity cursor ...
* 5 content list-style quotes ...
*
*/
* {margin: 0;padding: 0;vertical-align: baseline;box-sizing:border-box;}
::before,::after {box-sizing:border-box;}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section,content {display: block;}
img {border:0;display:inline-block;}
ol, ul {list-style: none;}
blockquote, q {quotes: none;}
blockquote::before, blockquote::after,
q::before, q::after {content: '';content: none;}
table {border-collapse: collapse;border-spacing: 0;}
a:focus,input,textarea,button:focus,input:focus,textarea:focus {outline:none;}
::-moz-focus-inner {
border:none;outline:none;
}
body {font-family:Helvetica, Arial,"WenQuanYi Micro Hei","PingFang SC","Hiragino Sans GB","Segoe UI", "Microsoft Yahei", sans-serif;-webkit-font-smoothing: antialiased;text-size-adjust: 100%;-webkit-tap-highlight-color: transparent;}
code,pre,samp {font-family:Menlo,Monaco,Consolas,"Courier New",monospace;}
[anot],[\:repeat],[\:if],[is-widget],slot {visibility:hidden;}
.do-fn-cl { *zoom: 1; }
.do-fn-cl::after { content: "."; display: block; height: 0; clear: both; visibility: hidden; overflow:hidden;}
.do-fn-clear {clear:both;display:inline;}
.do-fn-show{display:block;}
.do-fn-hide{display:none;}
.do-fn-fl{float:left;}
.do-fn-fr{float:right;}
.do-fn-noselect {-webkit-touch-callout: none;-webkit-user-select: none;-moz-user-select: none;user-select: none;}
.do-fn-noselect img, .do-fn-noselect a {-webkit-user-drag:none;}
.do-fn-ell {overflow:hidden; white-space:nowrap; text-overflow:ellipsis }
.do-st-thin {-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}
.do-st-hand {cursor:pointer;}
[class^="do-icon-"], [class*=" do-icon-"] {display:inline-block;font-family:"uifont" !important;font-style:normal;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}

1
src/lib/drag/core.js Normal file
View File

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

1
src/lib/drag/index.js Normal file
View File

@ -0,0 +1 @@
"use strict";import Drag 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}, 解析异常[元素不存在]`),"WC-LAYER"===t.tagName&&"layer"===e){t=t.root.children[1];break}if(t.classList.contains(e)||t.id===e)break;t=t.parentNode}new Drag(t).by(this.element,{limit:this.limit,axis:this.axis,overflow:this.overflow})}});

15
src/lib/form/button.js Normal file

File diff suppressed because one or more lines are too long

15
src/lib/form/checkbox.js Normal file
View File

@ -0,0 +1,15 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2020-03-27 14:02:14
* @version v2.0.1
*
*/
'use strict'
import"../icon/index.js";import $ from"../utils.js";export default class Checkbox extends HTMLElement{static get observedAttributes(){return["label","color","value","checked","readonly","disabled"]}constructor(){super(),Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(this,"props",{value:{label:"",color:"",value:[],checked:!1,readonly:!1,disabled:!1},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-block;line-height:1;font-size:14px}:host label{display:flex;justify-content:center;align-items:center;min-width:32px;height:32px;padding:0 5px;line-height:0;-moz-user-select:none;user-select:none;white-space:nowrap;cursor:inherit;color:#7e909a}:host .dot{--size: 18px;padding:2px;margin-right:3px}:host([readonly]){opacity:0.8}:host([disabled]){cursor:not-allowed;opacity:0.6}:host([size='large']){font-size:16px}:host([size='large']) label{height:42px}:host([size='large']) .dot{--size: 22px}:host([size='medium']) label{height:38px}:host([size='medium']) .dot{--size: 20px}:host([size='mini']){font-size:12px}:host([size='mini']) label{height:20px}:host([size='mini']) .dot{--size: 14px}:host([color='red']) label.checked{color:#ff5061}:host([color='red']) label.checked .dot{border-color:#ff5061}:host([color='red']) label.checked .dot::after{background:#ff5061}:host([color='blue']) label.checked{color:#66b1ff}:host([color='blue']) label.checked .dot{border-color:#66b1ff}:host([color='blue']) label.checked .dot::after{background:#66b1ff}:host([color='green']) label.checked{color:#58d68d}:host([color='green']) label.checked .dot{border-color:#58d68d}:host([color='green']) label.checked .dot::after{background:#58d68d}:host([color='teal']) label.checked{color:#3fc2a7}:host([color='teal']) label.checked .dot{border-color:#3fc2a7}:host([color='teal']) label.checked .dot::after{background:#3fc2a7}:host([color='orange']) label.checked{color:#ffb618}:host([color='orange']) label.checked .dot{border-color:#ffb618}:host([color='orange']) label.checked .dot::after{background:#ffb618}:host([color='dark']) label.checked{color:#62778d}:host([color='dark']) label.checked .dot{border-color:#62778d}:host([color='dark']) label.checked .dot::after{background:#62778d}:host([color='purple']) label.checked{color:#ac61ce}:host([color='purple']) label.checked .dot{border-color:#ac61ce}:host([color='purple']) label.checked .dot::after{background:#ac61ce}</style> <label> <wc-icon class=\"dot\" is=\"checkbox-off\"></wc-icon> <slot></slot> </label> ",this.__SWITCH__=this.root.lastElementChild,this.__ICO__=this.__SWITCH__.children[0]}get value(){return this.props.value}set value(e){log(e,this,this.props.label),Array.isArray(e)?(this.props.value=e,this.checked=this.props.value.includes(this.props.label)):console.error("checkbox组件的value必须是数组, 当前为: "+typeof e)}get checked(){return this.props.checked}set checked(e){this.props.checked=!!e;var{value:o,checked:t,label:l,color:r}=this.props;this.__SWITCH__.classList.toggle("checked",t),this.__ICO__.setAttribute("is","checkbox-"+(t?"on":"off"));var s=o.indexOf(l);t?(this.__ICO__.setAttribute("color",r),s<0&&o.push(l)):(this.__ICO__.removeAttribute("color"),~s&&o.splice(s,1))}get readonly(){return this.props.readonly}set readonly(e){var o=typeof e;e!==this.props.readonly&&("boolean"===o&&e||"boolean"!==o?(this.props.readonly=!0,this.setAttribute("readonly","")):(this.props.readonly=!1,this.removeAttribute("readonly")))}get disabled(){return this.props.disabled}set disabled(e){var o=typeof e;e!==this.props.disabled&&("boolean"===o&&e||"boolean"!==o?(this.props.disabled=!0,this.setAttribute("disabled","")):(this.props.disabled=!1,this.removeAttribute("disabled")))}connectedCallback(){this._handlClick=$.bind(this,"click",e=>{e.preventDefault(),this.disabled||this.readonly||(this.checked=!this.checked,this.dispatchEvent(new CustomEvent("input")))})}disconnectedCallback(){$.unbind(this,"click",this._handlClick)}attributeChangedCallback(e,o,t){if(null!==t&&o!==t)switch(e){case"label":case"color":this.props[e]=t;break;case"checked":case"readonly":case"disabled":this[e]=!0}}};
if(!customElements.get('wc-checkbox')){
customElements.define('wc-checkbox', Checkbox)
}

15
src/lib/form/input.js Normal file

File diff suppressed because one or more lines are too long

15
src/lib/form/number.js Normal file

File diff suppressed because one or more lines are too long

15
src/lib/form/progress.js Normal file
View File

@ -0,0 +1,15 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2020-03-27 14:02:14
* @version v2.0.1
*
*/
'use strict'
export default class Progress extends HTMLElement{static get observedAttributes(){return["value","max"]}constructor(){super(),Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(this,"props",{value:{value:0,max:1},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;align-items:center}:host label{flex:1;height:var(--size, 10px);border-radius:9px;background:#e8ebf4}:host label span{display:block;width:0;height:100%;border-radius:9px;background:#3fc2a7}:host([size='large']) label{height:18px}:host([size='medium']) label{height:14px}:host([size='mini']) label{height:6px}:host([color='red']) label span{background:#ff5061}:host([color='blue']) label span{background:#66b1ff}:host([color='green']) label span{background:#58d68d}:host([color='orange']) label span{background:#ffb618}:host([color='dark']) label span{background:#62778d}:host([color='purple']) label span{background:#ac61ce}</style> <label><span></span></label> ",this.__THUMB__=this.root.children[1].lastElementChild}get value(){return this.props.value}set value(e){this.props.value=+e,this.calculate()}calculate(){var{max:e,value:a}=this.props;this.__THUMB__.style.width=`${100*a/e}%`}connectedCallback(){this.calculate()}attributeChangedCallback(e,a,l){if(null!==l&&a!==l)switch(e){case t:var t=+l;(t!=t||t<1)&&(t=1),this.props.max=t,this.calculate();break;case"value":var r=+l;r==r&&(this.props.value=r,this.calculate())}}};
if(!customElements.get('wc-progress')){
customElements.define('wc-progress', Progress)
}

15
src/lib/form/radio.js Normal file
View File

@ -0,0 +1,15 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2020-03-27 14:02:14
* @version v2.0.1
*
*/
'use strict'
import $ from"../utils.js";export default class Radio extends HTMLElement{static get observedAttributes(){return["label","checked","readonly","disabled"]}constructor(){super(),Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(this,"props",{value:{label:"",checked:!1,readonly:!1,disabled:!1},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-block;line-height:1;font-size:14px}:host label{display:flex;justify-content:center;align-items:center;min-width:32px;height:32px;padding:0 5px;line-height:0;-moz-user-select:none;user-select:none;white-space:nowrap;cursor:inherit;color:#7e909a}:host label.checked .dot::after{display:block;width:12px;height:12px;border-radius:50%;background:#aabac3;content:''}:host .dot{width:18px;height:18px;padding:2px;margin-right:3px;border:1px solid #aabac3;border-radius:50%;background:#fff}:host([readonly]){opacity:0.8}:host([disabled]){cursor:not-allowed;opacity:0.6}:host([size='large']) label{width:58px;height:32px}:host([size='large']) .dot{width:26px;height:26px}:host([size='medium']) label{width:50px;height:28px}:host([size='medium']) .dot{width:22px;height:22px}:host([size='mini']) label{width:22px;height:14px;padding:2px}:host([size='mini']) .dot{width:10px;height:10px}:host([color='red']) label.checked{color:#ff5061}:host([color='red']) label.checked .dot{border-color:#ff5061}:host([color='red']) label.checked .dot::after{background:#ff5061}:host([color='blue']) label.checked{color:#66b1ff}:host([color='blue']) label.checked .dot{border-color:#66b1ff}:host([color='blue']) label.checked .dot::after{background:#66b1ff}:host([color='green']) label.checked{color:#58d68d}:host([color='green']) label.checked .dot{border-color:#58d68d}:host([color='green']) label.checked .dot::after{background:#58d68d}:host([color='teal']) label.checked{color:#3fc2a7}:host([color='teal']) label.checked .dot{border-color:#3fc2a7}:host([color='teal']) label.checked .dot::after{background:#3fc2a7}:host([color='orange']) label.checked{color:#ffb618}:host([color='orange']) label.checked .dot{border-color:#ffb618}:host([color='orange']) label.checked .dot::after{background:#ffb618}:host([color='dark']) label.checked{color:#62778d}:host([color='dark']) label.checked .dot{border-color:#62778d}:host([color='dark']) label.checked .dot::after{background:#62778d}:host([color='purple']) label.checked{color:#ac61ce}:host([color='purple']) label.checked .dot{border-color:#ac61ce}:host([color='purple']) label.checked .dot::after{background:#ac61ce}</style> <label> <span class=\"dot\"></span> <slot></slot> </label> ",this.__SWITCH__=this.root.lastElementChild}get value(){return this.props.label}set value(e){this.checked=this.props.label===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 o=typeof e;e!==this.props.readonly&&("boolean"===o&&e||"boolean"!==o?(this.props.readonly=!0,this.setAttribute("readonly","")):(this.props.readonly=!1,this.removeAttribute("readonly")))}get disabled(){return this.props.disabled}set disabled(e){var o=typeof e;e!==this.props.disabled&&("boolean"===o&&e||"boolean"!==o?(this.props.disabled=!0,this.setAttribute("disabled","")):(this.props.disabled=!1,this.removeAttribute("disabled")))}connectedCallback(){this._handleClick=$.bind(this,"click",e=>{this.disabled||this.readonly||this.checked||(this.checked=!0,this.dispatchEvent(new CustomEvent("input")))})}disconnectedCallback(){$.unbind(this,"click",this._handleClick)}attributeChangedCallback(e,o,t){if(null!==t&&o!==t)switch(e){case"label":this.props.label=t;break;case"checked":case"readonly":case"disabled":this[e]=!0}}};
if(!customElements.get('wc-radio')){
customElements.define('wc-radio', Radio)
}

15
src/lib/form/select.js Normal file

File diff suppressed because one or more lines are too long

15
src/lib/form/star.js Normal file
View File

@ -0,0 +1,15 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2020-03-27 14:02:14
* @version v2.0.1
*
*/
'use strict'
import $ from"../utils.js";export default class Star extends HTMLElement{static get observedAttributes(){return["value","text","size","color","allow-half","show-value","starSize","disabled"]}constructor(){super(),Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(this,"props",{value:{value:0,text:[],size:"",color:"","allow-half":!1,"show-value":!1,starSize:32,disabled:!1},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;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;cursor:pointer;font-size:14px;--size: 24px}label{display:flex;align-items:center;line-height:0;cursor:inherit}label wc-icon{margin:0 3px;transition:transform .1s ease-in-out}label wc-icon:hover{transform:scale(1.05)}label span{padding:0 8px;margin:0 3px}:host([size=\'large\']){font-size:16px;--size: 36px}:host([size=\'medium\']){--size: 30px}:host([size=\'mini\']){font-size:12px;--size: 20px}:host([color=\'red\']) label span{color:#ff5061}:host([color=\'teal\']) label span{color:#3fc2a7}:host([color=\'green\']) label span{color:#58d68d}:host([color=\'grey\']) label span{color:#aabac3}:host([color=\'blue\']) label span{color:#66b1ff}:host([color=\'purple\']) label span{color:#ac61ce}:host([color=\'orange\']) label span{color:#ffb618}:host([disabled]){cursor:default;opacity:0.6}:host([disabled]) label wc-icon:hover{transform:none}</style> <label> <wc-icon data-idx="0" is="star" color="grey"></wc-icon> <wc-icon data-idx="1" is="star" color="grey"></wc-icon> <wc-icon data-idx="2" is="star" color="grey"></wc-icon> <wc-icon data-idx="3" is="star" color="grey"></wc-icon> <wc-icon data-idx="4" is="star" color="grey"></wc-icon> <span class="text"></span> </label> ',this.__BOX__=this.root.children[1],this.__STARS__=Array.from(this.__BOX__.children),this.__TEXT__=this.__STARS__.pop()}get value(){return this.props.value}set value(t){var e=+t;(t=e==e&&e>0?e:0)>5&&(t=5),this.props.value=t,this._updateDraw(-1)}_updateDraw(t,e=0){var s="star-half",{value:o,tmp:a={i:0,f:0}}=this.props;-1===t&&(t=Math.floor(o),e=+(o%1).toFixed(1),t>0&&t===o&&(t--,e=1)),this.props["allow-half"]||(e=e>0?1:0),t===a.i&&e===a.f||(e>.5&&(s="star-full"),this.__STARS__.forEach((e,s)=>{e.setAttribute("is",s<t?"star-full":"star"),e.setAttribute("color",s<t?this.props.color:"grey")}),e>0&&(this.__STARS__[t].setAttribute("is",s),this.__STARS__[t].setAttribute("color",this.props.color)),this.props.tmp={i:t,f:e},0===t&&0===e?this.__TEXT__.textContent="":5===this.props.text.length?this.__TEXT__.textContent=this.props.text[t]:this.props["show-value"]&&(this.__TEXT__.textContent=t+e))}connectedCallback(){$.catch(this.__BOX__,"mousemove",t=>{if(!this.props.disabled&&"WC-ICON"===t.target.tagName){let e=+t.target.dataset.idx;this._updateDraw(e,+(t.offsetX/this.props.starSize).toFixed(1))}}),$.catch(this.__BOX__,"click",t=>{var{tmp:e,disabled:s}=this.props;s||"WC-ICON"===t.target.tagName&&(this.props.value=e.i+e.f,this.dispatchEvent(new CustomEvent("input")))}),$.catch(this.__BOX__,"mouseleave",t=>{this.props.disabled||this._updateDraw(-1)})}attributeChangedCallback(t,e,s){if(null!==s&&e!==s)switch(t){case"size":this.props.starSize=this.__STARS__[0].clientWidth;break;case"allow-half":case"show-value":case"disabled":this.props[t]=!0;break;case"color":s&&(this.props.color=s);break;case"text":s&&5===(s=s.split("|")).length&&(this.props.text=s.map(t=>t.trim()));break;case"value":this.value=s}}};
if(!customElements.get('wc-star')){
customElements.define('wc-star', Star)
}

15
src/lib/form/switch.js Normal file
View File

@ -0,0 +1,15 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2020-03-27 14:02:14
* @version v2.0.1
*
*/
'use strict'
import $ from"../utils.js";export default class Switch extends HTMLElement{static get observedAttributes(){return["active-text","inactive-text","checked","disabled"]}constructor(){super(),Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(this,"props",{value:{"active-text":null,"inactive-text":null,checked:!1,disabled:!1},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-block}:host section{display:flex;justify-content:center;align-items:center;white-space:nowrap}:host label{display:flex;width:38px;height:22px;padding:3px;margin:5px;line-height:0;border-radius:21px;background:#dae1e9;cursor:inherit}:host label.checked{flex-direction:row-reverse;background:#7e909a}:host .dot{width:16px;height:16px;border-radius:50%;background:#fff}:host([disabled]){cursor:not-allowed;opacity:0.6}:host([size='large']) label{width:58px;height:32px}:host([size='large']) .dot{width:26px;height:26px}:host([size='medium']) label{width:50px;height:28px}:host([size='medium']) .dot{width:22px;height:22px}:host([size='mini']) label{width:22px;height:14px;padding:2px}:host([size='mini']) .dot{width:10px;height:10px}:host([color='red']) label.checked{background:#ff5061}:host([color='blue']) label.checked{background:#66b1ff}:host([color='green']) label.checked{background:#58d68d}:host([color='teal']) label.checked{background:#3fc2a7}:host([color='orange']) label.checked{background:#ffb618}:host([color='dark']) label.checked{background:#62778d}:host([color='purple']) label.checked{background:#ac61ce}</style> <section> <label> <span class=\"dot\"></span> </label> <slot></slot> </section> ",this.__SWITCH__=this.root.lastElementChild.firstElementChild}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 t=typeof e;e!==this.props.disabled&&("boolean"===t&&e||"boolean"!==t?(this.props.disabled=!0,this.setAttribute("disabled","")):(this.props.disabled=!1,this.removeAttribute("disabled")))}connectedCallback(){this._handleClick=$.bind(this,"click",e=>{this.disabled||(this.checked=!this.checked,this.checked?null!==this.props["active-text"]&&(this.textContent=this.props["active-text"]):null!==this.props["inactive-text"]&&(this.textContent=this.props["inactive-text"]),this.dispatchEvent(new CustomEvent("input")))})}disconnectedCallback(){$.unbind(this,"click",this._handleClick)}attributeChangedCallback(e,t,i){if(null!==i&&t!==i)switch(e){case"checked":case"disabled":this[e]=!0;break;case"active-text":case"inactive-text":this.props[e]=i+""}}};
if(!customElements.get('wc-switch')){
customElements.define('wc-switch', Switch)
}

15
src/lib/icon/index.js Normal file
View File

@ -0,0 +1,15 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2020-03-27 14:02:14
* @version v2.0.1
*
*/
'use strict'
import SVG_DICT from"./svg.js";export default class Icon extends HTMLElement{static get observedAttributes(){return["is"]}constructor(){super(),Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(this,"props",{value:{is:""},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-block;color:inherit}:host(:not([is])){display:none}.icon{display:block;width:var(--size, 32px);height:var(--size, 32px);margin:var(--pad, auto);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']) .icon{width:42px;height:42px}:host([size='medium']) .icon{width:38px;height:38px}:host([size='mini']) .icon{width:20px;height:20px}:host([color='red']){color:#ff5061}:host([color='blue']){color:#66b1ff}:host([color='green']){color:#58d68d}:host([color='teal']){color:#3fc2a7}:host([color='orange']){color:#ffb618}:host([color='dark']){color:#62778d}:host([color='purple']){color:#ac61ce}:host([color='grey']){color:#aabac3}@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()}get is(){return this.props.is}set is(o){o&&this.setAttribute("is",o)}drawPath(){var{is:o}=this.props,t=SVG_DICT[o];this.__ICO__&&o&&t&&(this.__ICO__.innerHTML="loading"===o?t:`<path d="${t}" />`,this.__ICO__.classList.toggle("load","loading"===o))}attributeChangedCallback(o,t,e){if(null!==e&&t!==e)switch(o){case"is":this.props.is=e,e?this.drawPath():this.removeAttribute("is")}}};
if(!customElements.get('wc-icon')){
customElements.define('wc-icon', Icon)
}

1
src/lib/icon/svg.js Normal file

File diff suppressed because one or more lines are too long

15
src/lib/layer/index.js Normal file

File diff suppressed because one or more lines are too long

15
src/lib/scroll/index.js Normal file
View File

@ -0,0 +1,15 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2020-03-27 14:02:14
* @version v2.0.1
*
*/
'use strict'
import $ from"../utils.js";const IS_FF=!!window.sidebar;export default class Scroll extends HTMLElement{static get observedAttributes(){return["axis"]}constructor(){super(),Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(this,"props",{value:{axis:"xy"},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{overflow:hidden;position:relative;display:flex;width:100%}:host .container{overflow:hidden;position:relative;width:100%;height:100%}.is-horizontal,.is-vertical{visibility:hidden;position:absolute;display:flex;justify-content:flex-end;z-index:10240;opacity:0;user-select:none;transition:opacity 0.3s linear, visibility 0.3s linear}.is-horizontal .thumb,.is-vertical .thumb{display:block;border-radius:5px;background:rgba(44,47,53,0.25);cursor:default;transition:width 0.1s linear, height 0.1s linear}.is-horizontal .thumb:hover,.is-vertical .thumb:hover{background:rgba(44,47,53,0.5)}.is-horizontal{flex-direction:column;left:0;bottom:1px;width:100%;height:10px}.is-horizontal .thumb{width:0;height:6px}.is-horizontal .thumb:hover{height:10px}.is-vertical{top:0;right:1px;width:10px;height:100%}.is-vertical .thumb{width:6px;height:0}.is-vertical .thumb:hover{width:10px}:host(:hover) .is-horizontal,:host(:hover) .is-vertical{visibility:visible;opacity:1}:host([axis=\'x\']) .is-vertical{display:none}:host([axis=\'y\']) .is-horizontal{display:none}</style> <div class="container"><slot></slot></div> <div class="is-horizontal"><span class="thumb"></span></div> <div class="is-vertical"><span class="thumb"></span></div> ',this.__BOX__=this.root.children[1],this.__X__=this.root.children[2].children[0],this.__Y__=this.root.children[3].children[0]}get scrollTop(){return this.__BOX__.scrollTop}set scrollTop(t){if((t=+t)==t){var{sh:s,oh:i,yh:e}=this.props;this.__BOX__.scrollTop=t;var o=this.__BOX__.scrollTop/(s-i)*(i-e);this.props.thumbY=o,this.__Y__.style.transform=`translateY(${o}px)`}}get scrollLeft(){return this.__BOX__.scrollLeft}set scrollLeft(t){if((t=+t)==t){var{sw:s,ow:i,xw:e}=this.props;this.__BOX__.scrollLeft=t;var o=this.__BOX__.scrollLeft/(s-i)*(i-e);this.props.thumbX=o,this.__X__.style.transform=`translateX(${o}px)`}}get scrollHeight(){return this.__BOX__.scrollHeight}_fetchScrollX(t){var{sw:s,ow:i,xw:e}=this.props;return t<0?t=0:t>i-e&&(t=i-e),this.__BOX__.scrollLeft=t/(i-e)*(s-i),this.__X__.style.transform=`translateX(${t}px)`,t}_fetchScrollY(t){var{sh:s,oh:i,yh:e}=this.props;return t<0?t=0:t>i-e&&(t=i-e),this.__BOX__.scrollTop=t/(i-e)*(s-i),this.__Y__.style.transform=`translateY(${t}px)`,t}connectedCallback(){this._initFn=$.bind(this.__BOX__,"mouseenter",t=>{var s=this.__BOX__.offsetWidth,i=this.__BOX__.scrollWidth,e=this.__BOX__.offsetHeight,o=this.__BOX__.scrollHeight,r=e*e/o>>0,h=s*s/i>>0;r<50&&(r=50),h<50&&(h=50),h===s&&(h=0),r===e&&(r=0),this.props.oh=e,this.props.sh=o,this.props.ow=s,this.props.sw=i,this.props.yh=r,this.props.xw=h,this.__X__.style.width=h+"px",this.__Y__.style.height=r+"px"}),this._wheelFn=$.catch(this.__BOX__,"wheel",t=>{var{sh:s,oh:i,yh:e,sw:o,ow:r,xw:h}=this.props;if(h||e){var l,_;if(t.preventDefault(),IS_FF)l=t.deltaMode?10*t.deltaX:t.deltaX,_=t.deltaMode?10*t.deltaY:t.deltaY;else{var a=Math.abs(t.wheelDelta);a<120?(l=t.deltaX,_=t.deltaY):(l=t.deltaX/(a/120),_=t.deltaY/(a/120))}if("x"!==this.props.axis&&(this.__BOX__.scrollTop+=_,e)){var n=this.__BOX__.scrollTop/(s-i)*(i-e);this.props.thumbY=n,this.__Y__.style.transform=`translateY(${n}px)`}if("y"!==this.props.axis&&(this.__BOX__.scrollLeft+=l,h)){var p=this.__BOX__.scrollLeft/(o-r)*(r-h);this.props.thumbX=p,this.__X__.style.transform=`translateX(${p}px)`}}});var t,s,i,e,o=o=>{var{thumbY:r,thumbX:h}=this.props;null!==t&&(i=this._fetchScrollX(h+o.pageX-t)),null!==s&&(e=this._fetchScrollY(r+o.pageY-s))},r=h=>{t=null,s=null,this.props.thumbX=i,this.props.thumbY=e,$.unbind(document,"mousemove",o),$.unbind(document,"mouseup",r)};$.bind(this.__Y__,"mousedown",t=>{s=t.pageY,this.props.thumbY||(this.props.thumbY=0),$.bind(document,"mousemove",o),$.bind(document,"mouseup",r)}),$.bind(this.__X__,"mousedown",s=>{t=s.pageX,this.props.thumbX||(this.props.thumbX=0),$.bind(document,"mousemove",o),$.bind(document,"mouseup",r)}),this.__observer=new MutationObserver(this._initFn),this.__observer.observe(this,{childList:!0,subtree:!0,characterData:!0})}disconnectedCallback(){this.__observer.disconnect(),$.unbind(this.__BOX__,"mouseenter",this._initFn),$.unbind(this.__BOX__,"wheel",this._wheelFn)}attributeChangedCallback(t,s,i){if(null!==i&&s!==i)switch(t){case"axis":this.props.axis=i}}};
if(!customElements.get('wc-scroll')){
customElements.define('wc-scroll', Scroll)
}

1
src/lib/utils.js Normal file
View File

@ -0,0 +1 @@
function noop(){}export default{nextTick:function(){let t=[];let e=document.createTextNode("\x3c!-- --\x3e");new MutationObserver(function(){let e=t.length;for(let n=0;n<e;n++)t[n]();t=t.slice(e)}).observe(e,{characterData:!0});let n=!1;return function(i){t.push(i),n=!n,e.data=n}}(),each(t,e){if(t)if(Array.isArray(t))for(let n,i=0;(n=t[i++])&&!1!==e(n,i-1););else for(let n in t)if(t.hasOwnProperty(n)&&!1===e(t[n],n))break},bind(t,e,n=noop,i=!1){let o=e.split(",");return this.each(o,function(e){e=e.trim(),t.addEventListener(e,n,i)}),n},catch(t,e,n,i){return this.bind(t,e,function(t){t.stopPropagation(),n&&n(t)},i)},unbind(t,e,n=noop,i=!1){let o=e.split(",");this.each(o,function(e){e=e.trim(),t.removeEventListener(e,n,i)})},outside(t,e=noop){return this.bind(document,"mousedown",n=>{if(n)if(n.path){for(var i=n.path.concat();i.length>3;)if(i.shift()===t)return}else{var o=n.explicitOriginalTarget||n.target;if(t===o||t.contains(o)||t.root&&t.root.contains(o))return}e(n)})},clearOutside(t=noop){this.unbind(document,"mousedown",t)}};

77
src/main.js Normal file
View File

@ -0,0 +1,77 @@
/**
*
* @author yutent<yutent@doui.cc>
* @date 2019/09/16 20:51:19
*/
const { app, BrowserWindow, protocol, Menu } = require('electron')
const path = require('path')
const fs = require('iofs')
require('./tools/init')
const createMenu = require('./tools/menu')
const log = console.log
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
/* ----------------------------------------------------- */
app.commandLine.appendSwitch('--lang', 'zh-CN')
app.commandLine.appendSwitch('--autoplay-policy', 'no-user-gesture-required')
protocol.registerSchemesAsPrivileged([
{ scheme: 'app', privileges: { secure: true, standard: true } }
])
/* ----------------------------------------------------- */
// 初始化应用
app.once('ready', () => {
// 注册协议
protocol.registerBufferProtocol('app', (req, cb) => {
let file = req.url.replace(/^app:\/\/local\//, '')
let ext = path.extname(req.url)
let buff = fs.cat(path.resolve(ROOT, file))
cb({ data: buff, mimeType: MIME_TYPES[ext] })
})
Menu.setApplicationMenu(null)
// 创建浏览器窗口
let win = new BrowserWindow({
title: '伪域名解析',
width: 1000,
height: 640,
resizable: false,
maximizable: false,
icon: path.resolve(ROOT, './images/app.png'),
webPreferences: {
webSecurity: false,
experimentalFeatures: true,
nodeIntegration: true
}
})
win.on('closed', () => {
app.exit()
win = null
})
// win.openDevTools()
// 然后加载应用的 index.html
win.loadURL('app://local/index.html')
if (process.platform === 'darwin') {
createMenu(win)
}
})

124
src/tools/init.js Normal file
View File

@ -0,0 +1,124 @@
/**
* 配置/DB通讯
* @author yutent<yutent@doui.cc>
* @date 2019/01/26 18:11:26
*/
'use strict'
const { app, ipcMain } = require('electron')
const fs = require('iofs')
const path = require('path')
const HOST_FILE = path.resolve(app.getPath('userData'), 'host.cache')
const LOCK_FILE = path.resolve(app.getPath('userData'), 'lock')
if (!fs.exists(HOST_FILE)) {
fs.echo('{}', HOST_FILE)
}
/* ********** 修复环境变量 start *********** */
let PATH_SET = new Set()
process.env.PATH.split(':').forEach(_ => {
PATH_SET.add(_)
})
PATH_SET.add('/usr/local/bin')
PATH_SET.add('/usr/local/sbin')
process.env.PATH = Array.from(PATH_SET).join(':')
PATH_SET = null
/* ********** 修复环境变量 end *********** */
var timer = null
ipcMain.on('dns-host', (ev, conn) => {
switch (conn.type) {
// 获取指定目录下的文件和目录
case 'get':
var cache = fs.cat(HOST_FILE)
ev.returnValue = JSON.parse(cache)
break
// 设置应用配置
case 'set':
clearTimeout(timer)
timer = setTimeout(() => {
var txt = ''
for (let k in conn.data) {
for (let it of conn.data[k]) {
if (it.enabled) {
var name = it.record === '@' ? '' : it.record
if (name) {
name += '.'
}
txt += `${it.value.padEnd(15, ' ')} ${name + k}\n`
}
}
txt += '\n\n'
}
fs.echo(JSON.stringify(conn.data), HOST_FILE)
fs.echo(txt, '/etc/hosts')
}, 1000)
break
case 'check':
var stat = fs.echo('', '/etc/hosts', true)
ev.returnValue = stat
break
case 'history':
if (fs.exists(LOCK_FILE)) {
var cache = fs.cat(HOST_FILE)
ev.returnValue = JSON.parse(cache)
return
}
var cache = fs.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
ev.returnValue = dict
fs.echo(JSON.stringify(dict), HOST_FILE)
fs.echo('', LOCK_FILE)
}
})

67
src/tools/menu.js Normal file
View File

@ -0,0 +1,67 @@
/**
* 菜单项
* @author yutent<yutent@doui.cc>
* @date 2019/01/21 20:34:04
*/
'use strict'
const { Menu } = require('electron')
module.exports = function(win) {
let menuList = Menu.buildFromTemplate([
{
label: '伪域名解析',
submenu: [
{ role: 'about', label: '关于 伪域名解析' },
{ 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: '伪域名解析' },
{
type: 'separator'
},
{ label: '伪域名解析' }
]
},
{
label: '窗口',
submenu: [
{
role: 'minimize',
label: '最小化',
click() {
win.minimize()
}
}
]
},
{
role: 'help',
label: '帮助',
submenu: [
{
label: '官网',
click() {}
}
]
}
])
Menu.setApplicationMenu(menuList)
}

View File

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

View File

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

View File

@ -1,9 +0,0 @@
[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

@ -1,23 +0,0 @@
#!/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

@ -1,67 +0,0 @@
#!/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

@ -1,41 +0,0 @@
/**
* {}
* @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

@ -1,262 +0,0 @@
/**
* {}
* @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

@ -1,109 +0,0 @@
/**
* {}
* @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

@ -1,158 +0,0 @@
/**
* {}
* @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

@ -1,165 +0,0 @@
/**
* {}
* @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

@ -1,82 +0,0 @@
@charset "UTF-8";
/**
*
* @authors yutent (yutent.io@gmail.com)
* @date 2014-10-10 00:45:09
*
* CSS
*
* ,
*
*
* 1 display float position overflow z-index /
* 2 width height margin padding border
* 3 line-height font-size vertical-align text-align user-select outline ....
* 4 color background opacity cursor ...
* 5 content list-style quotes ...
*
*/
* {margin: 0;padding: 0;vertical-align: baseline;box-sizing: border-box;}
::before, ::after {box-sizing: border-box;}
/* HTML5 display-role reset for older browsers */
article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section,content {display: block;}
img {border: 0;display: inline-block;}
ol,ul {list-style: none;}
blockquote, q {quotes: none;}
blockquote::before, blockquote::after, q::before, q::after {content: '';content: none;}
table {border-collapse: collapse;border-spacing: 0;}
a:focus,input,textarea,button:focus,input:focus,textarea:focus {outline: none;}
::-moz-focus-inner {border: none;outline: none;}
body {font-family: 'Helvetica Neue', Arial, 'WenQuanYi Micro Hei', 'PingFang SC', 'Hiragino Sans GB', 'Segoe UI', 'Microsoft Yahei', sans-serif;-webkit-font-smoothing: antialiased;text-size-adjust: 100%;-webkit-tap-highlight-color: transparent;}
code, pre, samp {font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;white-space:pre-wrap;}
[anot],[\:repeat],[\:if] {visibility: hidden;}
.noselect {-webkit-touch-callout: none;-webkit-user-select: none;user-select: none;}
.noselect img, .noselect a {-webkit-user-drag: none;}
.text-ell {overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}
.text-thin {-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}
:root {
/* primary */
--color-teal-a: rgba(72, 201, 176, 0.35);
--color-teal-1: rgb(72, 201, 176);
--color-teal-2: rgb(26, 188, 156);
--color-teal-3: rgb(22, 160, 133);
/* success */
--color-green-a: rgba(70, 221, 126, 0.35);
--color-green-1: rgb(70, 221, 126);
--color-green-2: rgb(47, 208, 105);
--color-green-3: rgb(26, 196, 88);
/* info */
--color-blue-a: rgba(100, 181, 246, 0.35);
--color-blue-1: rgb(100, 181, 246);
--color-blue-2: rgb(66, 165, 245);
--color-blue-3: rgb(33, 150, 243);
/* danger */
--color-red-a: rgba(252, 118, 97, 0.35);
--color-red-1: #fc7661;
--color-red-2: #ff5f45;
--color-red-3: #f33e22;
/* warning */
--color-orange-a: rgba(254, 174, 117, 0.35);
--color-orange-1: #feae75;
--color-orange-2: #fd964b;
--color-orange-3: #f97316;
/* default1 */
--color-plain-a: rgba(150, 204, 248, 0.35);
--color-plain-1: rgb(242, 245, 252);
--color-plain-2: rgb(232, 235, 244);
--color-plain-3: rgb(218, 225, 233);
/* default2 */
--color-grey-a: rgba(206, 214, 224, 0.35);
--color-grey-1: rgb(206, 214, 224);
--color-grey-2: rgb(164, 176, 190);
--color-grey-3: rgb(134, 144, 155);
/* inverse */
--color-dark-a: rgba(100, 116, 139, 0.35);
--color-dark-1: #64748B;
--color-dark-2: #475569;
--color-dark-3: #2c3441;
}

View File

@ -1,35 +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">
<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

@ -1,16 +0,0 @@
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

@ -1,35 +0,0 @@
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

@ -1,5 +0,0 @@
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

@ -1,13 +0,0 @@
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

@ -1,31 +0,0 @@
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

@ -1,13 +0,0 @@
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

@ -1,8 +0,0 @@
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

@ -1,54 +0,0 @@
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

@ -1,15 +0,0 @@
/**
* {}
* @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

@ -1,100 +0,0 @@
/**
* {}
* @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)
}
})
}