新增built脚本

This commit is contained in:
fofolee
2022-04-14 16:51:47 +08:00
parent 472c353b20
commit 8ee6057cc4
38 changed files with 136 additions and 132 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1,296 +0,0 @@
## V2.3.4
- 有子输入框的情况下如果5s内复制了内容再呼出会自动执行复制的内容
- 完善脚本的报错信息,更容易定位到错误点
- 修复4k屏下搜索到的图标模糊的 bug
- 修复暗黑模式下的一些配色 bug
- 修复了如果通过 newcommand 创建命令,会出现页面无法点击的 bug
- 修复 quickcommand.runInTerminal 当参数值含有双引号时命令执行不正常的 bug
## V2.3.3
- 修复新安装插件的用户,如不填写标签会无法保存的 bug
## V2.3.2
- 修复 icon8s 图标无法搜索的 bug
## v2.3.1
- win 下样式 bug 修复
## v2.3.0
- 新增快捷面板 beta
- 将某一个标签下的命令以面板形式展现
- 可快速实现网址导航面板、软件启动面板之类的功能
- 可以结合分享中心的`收藏网址``收藏文件`使用
![](https://ae01.alicdn.com/kf/U2312f3f62f2b4e3cb24f3caf7f8e9a4bs.jpg)
![](https://ae01.alicdn.com/kf/Ub8111ccc203b4eefb91baae44a7f9cadW.jpg)
- 新增 quickcommand.runInTermial
- 导入命令式会弹出选择窗口,而不是直接导入
- 代码格式调整
- 修复一个样式 bug
## v2.2.0
- 支持通过网络路径,本地路径和在线搜索三种形式来设置命令图标
![](https://ae01.alicdn.com/kf/Ue062ff565e594ec8a6be0ad26423e986r.jpg)
- 新增两个特殊变量 {{type}} 和 {{payload}},适用于非 quickcommand 环境下的专业模式
- 增加 quickcommand.runVbs可以运行 vbs 脚本
- 支持手动设置脚本保存时的文件编码和运行输出结果的解码方式
![](https://ae01.alicdn.com/kf/U6581b23a35c94d0f8ad39bd873f2231an.jpg)
- 修复分享中心调整图标显示bug
- 新增`NewCommand`功能,方便快速进入新建命令界面
- 去除`运行脚本`功能
- 在分享中心上线三个新的快捷命令,分别为`收藏网址`,`朗读`, 和新版的`运行脚本`
## v2.1.1
- 在新建命令支持选择任意文件作为图标
- 当选择 `.PNG` `.JPG` `.ICO` 等文件时,则以图片作为图标
- 当选择 `.EXE` `.APP` 时,则以程序的图标作为图标
- 当选择其他文件时,则以该文件的默认图标作为图标
- 新增 `quickcommand.enterData` 用以获取进入插件时匹配的类型和数据,用法详见文档
- 提供一个示例,作为`专业模式``配置`的默认值
## v2.1.0
### 新增功能
- 输出选项添加`忽略输出并保留窗口`
- 添加`专业模式`的匹配选项
- 可以通过 json 格式的配置实现同时匹配关键字、窗口、文件甚至图片
- 可以实现指定文件数量、窗口类等
- json 格式配置和插件开发的 `features.cmds` 一致
- 配置处可以点击旁边的按钮全屏,方便填写 json 格式的配置
- `quickcommand.showTextAera` 新增一个参数可以设置默认文本值
![0yxZIP.png](https://s1.ax1x.com/2020/10/10/0yxZIP.png)
![06SuuQ.png](https://s1.ax1x.com/2020/10/10/06SuuQ.png)
### BUG FIX
- 修复图标比例非 1:1 时排版出错的 bug
- 修复某些情况下无法启动 windows terminal 的问题
- 修复`MatchedFiles`匹配的文件路径中包含`$$`时会被处理成`$`的问题
## v2.0.1
- 修复当某个命令在新版本不兼容时,导致所有命令无法显示的 bug
## v2.0.0
本次带来了 海量更新,请仔细看完更新日志~
**更新后第一次进入插件会显示空白一段时间,是在对老版本的命令做兼容处理,属于正常现象**
### 新增功能
#### 分享中心
可以在线下载分享的命令
![UfDkdS.png](https://i.imgur.com/ikAxHY1.png)
#### 暗黑模式
全面兼容暗黑模式
#### 内置命令
内置`文本处理``find GUI``执行 shell 命令`等多个实用快捷命令,均使用新版本新增功能实现
![UfrkOx.png](https://s1.ax1x.com/2020/07/20/UfrkOx.png)
![ap24Hg.gif](https://s1.ax1x.com/2020/07/26/ap24Hg.gif)
#### RunCode
新增一个脚本编辑器,可以直接进行脚本的编辑及测试
支持通过关键词或文件匹配方式进入
![UfsGvR.png](https://s1.ax1x.com/2020/07/20/UfsGvR.png)
#### 帮助
新增一篇[帮助文档](./HELP.html)
#### 多开
分离插件后可以多开
### 新增特性
#### 匹配
- 新增文件匹配
#### 环境
##### quickcommand
- `模拟动作`模式改名为`quickcommand`
- 在 vm2 内运行,与插件环境隔离
- 无需本机预装环境,支持使用 nodejs/electron/utools 的 api
- 支持**通过列表、多输入框、多按钮、文本框**等多种形式获得用户的输入
- 现在和其他脚本一样可以针对输出进行不同处理或显示
- 封包了一些实用功能
- 现在添加按键通过监听按键进行添加,不再需要手动添加
- 添加更多的预设动作
- 重写延时函数,解决 utools 隐藏后延时不准的问题(其实是 electron 的问题)
- [API 文档](./quickcommand.html)
![UfrpY4.png](https://i.imgur.com/Gn7pr27.png)
##### 其他脚本
- 新增支持运行`C# `,需要安装 .net framework v4.0.30319
- `C`脚本, 需要安装 gcc 并加入环境变量
- windows 下可以通过 wsl 运行`shell`脚本
- 可以运行带参数的脚本
##### custom
- 环境选择`custom`可以自定义解释器的路径、参数、脚本后缀以及输出解码的方式
#### 标签
- 支持给命令添加标签,并在配置界面按标签进行分类显示
- 最多支持 3 个标签
#### 变量
- 新增` {{MatchedFiles}} `用来获取匹配的文件
- `{{MatchedFiles}}`` {{WindowInfo}} `支持获取特殊的键值,比如通过 `{{WindowInfo.id}}`获取匹配的窗口 id ,通过`{{MatchedFiles[0].path}}`获取匹配的第一个文件的路径
- 可以通过`{{subinput:placeholder}}`的格式来自定义占位符
- `{{pwd}} `获取失败时返回桌面路径
#### 输出
- 输出实时动态显示,无需等到命令执行结束
- 插件高度根据输出内容自适应,带子输入框的情况下,输出自动滚动
- 输出的文本或 html 结果可以按 ctrl+f 进行搜索
#### 平台
- 支持设置命令适配的平台
#### 运行
- 可以在新建命令的界面直接运行当前代码进行测试
#### 编辑器
- 支持 js、py 代码格式化
- 新增cmd、applescript 代码高亮(原先用的 shell 的)
- 支持代码提示和特殊变量提示,其中 cmd 和 shell 支持提示和补全当前环境变量下的命令js 和 py 支持智能提示,其他语言支持关键字提示
- 支持 vscode 快捷键
#### 导入导出
- 支持将命令导出到剪贴板,以及通过剪贴板导入命令
#### 其他
- 防误操作
- 删除命令时会再次确认
- 删除、清空命令时,会将删除的命令复制到剪贴板,可以通过导入命令快速恢复
- 数据库存储结构重构,解决某些情况下数据同步时产生的异常
## v1.6.1
- 现在执行快捷命令时会现将uTools的高度设置为0如果有输出再展开
- 模拟动作模式增加支持`require`
- 匹配主窗口输入模式下,会对输入的格式做简单校验
## v1.6.0
- 修复php乱码
- 现在可以自定义输出的编码方式,脚本里选择`custom`
- 模拟操作模式的`+延时`合并到`+动作中`
## v1.5.9
- 修复不能导入命令的bug
- 由于uTools 1.0.0版本匹配窗口时无法再使用正则,所以选择匹配窗口模式时,在填写进程时需填写进程全名,多个进程逗号隔开
- 忽略输出模式调整回自动隐藏窗口
- 更新示例命令库
## v1.5.8
- 适配新版本
## v1.5.7
- 添加`模拟操作`的功能,在`类型`下拉框内选取
- 该模式提供了模拟按键、打开文件、打开网站、定位文件、执行命令等实用功能
- 结合模拟按键和窗口匹配,可以实现针对不同的软件模拟不同的操作
- 该模式适用于没有编程基础的用户,只需要通过下拉框选取想要的功能,就可以快速编写一个简单的命令
- 该模式同样适用于本机没有任何语言环境的或环境变量失效的用户不需要安装nodejs即可执行js代码
- 虽然没有提供`特殊变量`的下拉框,但实际是支持的
- 脚本报错时提供跳转至临时脚本目录的选项
- 添加一个`下载命令`的按钮,可以跳转到[样例命令库]( https://github.com/fofolee/uTools-quickcommand/tree/master/CommandCollections ),如果你不知道怎么去编写一个目录,可以尝试下载导入进行参考
- 貌似修复了执行完命令后再次呼出uTools会短暂显示命令关键词的bug该bug可能是由先隐藏uTools再退出插件所引起
![JU0UAS.png](https://s1.ax1x.com/2020/04/22/JU0UAS.png)
## v1.5.1
- 修复macOS下新增命令界面排版错误的问题
- 修复macOS下使用shell脚本且发送输出结果到活动窗口时结果不正确的问题
## v1.5.0
- 注意本次更新对多处代码进行了重写,如果原有命令在上一版运行正常,在这一版出现了一些问题,请重新编辑该命令,修改命令的模式。如原有命令中使用了{{input}}变量的,关键字会变成[object object],请重新编辑该命令,将命令调整为`主输入框正则匹配`,如果原有命令中使用了{{pwd}}等变量的,请重新编辑该命令,将命令调整为`通过uTools呼出前的窗口匹配`
- 原先命令自定了图标的,如果图标显示异常,重新选择设置一次图标即可
![JU08Xt.png](https://s1.ax1x.com/2020/04/22/JU08Xt.png)
### 功能更新
- 新增`通过uTools呼出前的窗口匹配`的模式,现在可以快速编写一个应用到当前活动窗口的脚本
- 现在可以分别通过`关键字`,`主输入框正则匹配`,`活动窗口匹配`三种方式来调用自定义的脚本
- 新增一个特殊变量`当前窗口信息`,输出`json`格式的窗口相关信息,将原先的特殊变量`用户名`改为`本机唯一ID`方便针对不同电脑编写脚本两个变量均通过官方API获取
- 弃用原先模拟按键的方式获取资源管理器路径、选中文件、浏览器地址均借助官方API
- 新增`发送系统通知`的输出模式 By [imxiny](https://github.com/imxiny)
- 新增`在终端显示`的输出模式, 用以解决脚本需要显示动态输出的问题, 如 curl 命令 [issue](https://github.com/fofolee/uTools-quickcommand/issues/3)
- 上传一些命令至仓库的`CommandCollections`文件夹用以作为编写命令的样例同时大家可以提交PR丰富这个命令库 [CommandCollections](https://github.com/fofolee/uTools-quickcommand/tree/master/CommandCollections)
### BUG 修复
- 修复 php 无法输入 <? 问题修复MacOS环境变量问题修复脚本不能带参数问题 By [dofy](https://github.com/dofy)
- 修复中文乱码问题
- 修复当使用子输入框时,会多次触发脚本的问题 [issue](https://github.com/fofolee/uTools-quickcommand/issues/5)
- 修复当使用多行批处理脚本时,执行会报错的问题,该问题源于之前插件保存的批处理脚本的换行符是`LF`, windows 无法识别 [issue](https://yuanliao.info/d/424/70)
### 用户体验
- 获取选中文本,以及输出方式为发送到活动窗口时不再覆盖用户剪贴板
### 催更
- 之前了解到官方将出自动化插件,所以也有较长时间没有更新,在此以更催更
## v1.1.0
- 新增支持获取子输入框变量,感谢@ghostbody
- 支持自定义解释器的路径
## v1.0.0
- 上架 uTools 商店,去掉插件自带更新
- 精简了插件大小
- 可以自定义语言
## v0.0.2
- 修复uTools更新后进入插件空白的BUG
- 添加Linux支持
- 修复导入BUG
- 修复说明为空时无法启用命令的BUG
- 添加全部导出和全部删除的功能

File diff suppressed because one or more lines are too long

View File

@@ -1,195 +0,0 @@
[TOC]
# 一、更新日志
[更新日志戳我](CHANGELOG.html)
# 二、添加命令
## 基础
#### 常用动作
通过点击`+动作`<sup></sup>按钮进行添加
- 打开文件/文件夹/软件<sup></sup> (实现在主输入框启动自定义的软件名称及路径
- 在文件管理器中定位文件
- 用默认浏览器打开网址(实现类似网页快开的功能)
- 用 ubrowser 打开网址
- 执行系统命令
- 将内容写入剪贴板
- 发送系统消息
- 弹窗显示消息
- 发送文本到活动窗口
- 转至指定插件(实现自定义插件关键字)
- 添加延时
#### 模拟按键
通过点击`+按键`<sup></sup>按钮进行添加
![](https://i.imgur.com/SozshQL.png)
## 进阶
#### 匹配
决定通过何种方式进入插件,不同的匹配模式也会影响插值变量的使用
##### 关键字
- 在主输入框输入对应关键字进入插件,最通用的一种模式,关键字可以设置多个
##### 正则/划词
- 正则匹配主输入框文本或唤出超级面板时选中的文本,可以获取输入框文本或选中文本作为变量
##### 窗口/进程
- 匹配呼出 uTools 前或唤出超级面板时的活动窗口,可以获取窗口的信息或文件夹路径作为变量
##### 复制/选中文件
- 匹配拖入主输入框的文件或唤出超级面板时选中的文件,可以获取复制及选中的文件信息作为变量
##### 专业模式
- 匹配 JSON 格式的配置,等效于插件开发中的`features.cmds`
![06C726.png](https://s1.ax1x.com/2020/10/10/06C726.png)
#### 环境
##### quickcommand
- 可以快速执行打开网址、软件、文件夹、模拟按键等高频动作的命令
- 可以通过 quickcommand 的 api 来编写具有 UI 交互的脚本,详见[文档](./quickcommand.html)
- 可以使用nodejs、electron、uTools 的 api, 其中 os、fs、path、child_process、util、axios、electron 已经预先 require 了, 无需再次 require ,详见[文档中的上下文一览](./quickcommand.html#上下文一览)
##### python、cmd、shell 、php 等环境
- 本机装了相应环境即可执行相应的脚本
- 可以通过插值变量增强脚本的功能
- 支持 10+ 语言
- 可以通过 custom 手动设置解释器路径、参数、脚本后缀及编码方式
![](https://i.imgur.com/MPF1MdJ.png)
#### 插值变量
本插件内置了一些特殊的插值变量,可以获取一些特殊的值,能够加入到插件里的任意脚本中
##### 全模式可用
- `{{isWin}}` 是否Window系统 返回1或0
- `{{LocalId}}`本机唯一ID
- `{{BrowserUrl}}` 浏览器的当前链接
- `{{ClipText}}` 获取剪贴板的文本
- `{{SelectText}}` 获取选中的文本 (已弃用)
- `{{subinput}}`获取子输入框的文本,具有此变量时会在进入插件时自动启动子输入框
- 可以通过`{{subinput:placeholder}}`的格式来自定义占位符
##### 匹配窗口/进程时可用
- `{{pwd}}` 资源管理器或访达的当前目录
- `{{SelectFile}}` 文件管理器选中的文件,不支持 Linux
- `{{WindowInfo}}`当前窗口信息,返回 JSON 格式字符串
- 可以使用类似 `{{WindowInfo.id}}`的格式来直接读取相应的值
##### 匹配正则/划词时可用
- `{{input}} ` 获取主输入框的文本
##### 匹配复制/选中文件时可用
- `{{MatchedFiles}}` 匹配的文件,返回 JSON 格式字符串
- 可以使用类似`{{MatchedFiles[0].path}}`的格式来直接读取相应的值
![](https://i.imgur.com/PYjjYnR.png)
#### 输出
如果脚本有输出,则可以对输出内容做如下处理
- 隐藏并忽略输出
- 显示纯文本输出 (不解析 html 内容)
- 显示html格式的输出 (可以进一步编写简单的 GUI 界面,参考内置动作`特殊符号大全`)
- 复制到剪贴板
- 发送到活动窗口(可实现发送常用短语之类的功能)
- 发送到系统通知
- 在终端中显示
![](https://i.imgur.com/T45uJi5.png)
# 三、导出/分享/导入
#### 导出命令
点击命令旁的蓝色小箭头<sup></sup>即可导出命令,支持
- 导出到剪贴板<sup></sup>
- 导出到文件<sup></sup>
![](https://i.imgur.com/NoM6y5n.png)
#### 分享命令
点击分享命令即可一键分享当前的命令,初次分享命令,需要按照以下步骤设置 token
1.通过 [https://www.yuque.com/g/fofolee/qcshares/collaborator/join?token=6LZn2vc34dqfIQdC]( https://www.yuque.com/g/fofolee/qcshares/collaborator/join?token=6LZn2vc34dqfIQdC ) 成为知识库成员,如果没有语雀账号,需要先注册一个
![](https://i.imgur.com/H4Hh781.png)
2.生成一个具有编辑权限的 token
![U2Rw0P.png](https://s1.ax1x.com/2020/07/19/U2Rw0P.png)
3.点击命令旁的蓝色小箭头<sup></sup>,填入生成的 token <sup></sup> ,之后就可以尽情地分享命令啦
![](https://i.imgur.com/pQbFcvs.png)
**注意:**
1. 命令的分享基于语雀共享知识库实现,语雀的共享知识库对知识库的成员没有太大约束,用官方的话来说,是` 基于信任和一起共建的基础上 `的,目前加入成为成员不需要通过审批确认,后期如果出现捣乱的情况会踢出成员并开启加入审批。先在此约定:**命令的分享请通过插件实现,不要在 web 端(即语雀知识库内)直接修改编辑分享的命令,否则可能会导致一些不可预知的错误**。
2. 同时为了保证命令的安全和质量,分享后的命令必须经过快捷命令插件作者本人`发布`后才能出现在`分享中心`当中。所有已发布的命令在[ https://www.yuque.com/fofolee/qcreleases ]( https://www.yuque.com/fofolee/qcreleases )可以查看到。
#### 导入命令
- 可以通过点击底部的`导入命令`来导入命令
- 会优先识别剪贴板,如果剪贴板内有正确格式的命令会自动导入
- 如果剪贴板内没有,则会弹出文件选择框
- 支持自动识别是单个导出的命令还是全部导出的命令
#### 获取分享
可以通过以下两种方式来获取分享:
1. 点击设置界面底部的`分享中心`<sup></sup>即可获取并导入在线分享的命令
![UfBox1.png](https://i.imgur.com/pKKWqdT.png)
![UfDkdS.png](https://i.imgur.com/ikAxHY1.png)
2. 访问[ https://www.yuque.com/fofolee/qcreleases ]( https://www.yuque.com/fofolee/qcreleases )即可查看发布的命令
# 四、关于
#### 作者
<img width="30px" src="https://s1.ax1x.com/2020/07/14/UaDkdg.png">[github @fofolee]( https://github.com/fofolee )
<img width="30px" src=" https://yuanliao.info/assets/avatars/frbg0owd6t3mmejs.png ">[猿料社区 @fofolee]( https://yuanliao.info/u/fofolee/discussions )
#### 赞赏码
鉴于之前某位可爱的同学捐赠的时候捐错了对象,此处献上我的捐赠码~
<img width="500px" src="https://s1.ax1x.com/2020/07/15/Uacgqx.png">
#### 所有插件
所有由我制作的插件如下表所示,有兴趣的童鞋可以试一试~
<table> <tbody> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/c6e808470b6cbc778865e9ed1bebf339.png"></td> <td>快捷命令</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/11bf712cc79499549754586fff7c8db1.png"></td> <td>程序员手册</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/dd0977b7d74db32d7088795ef62a7769.png"></td> <td>bilibili</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/f26a31d11af3a54f9bddd7e781da46d5.png"></td> <td>关闭进程</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/80eae148109a4d7001232efebdd14aca.png"></td> <td>插件面板</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/223f27b647b184ffdb2cd9f05a99d50a.png"></td> <td>随机壁纸</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/480ac04e8ea522b7bb0dae6418e177a4.png"></td> <td>Github助手</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/0de374539c3c358d122ca652d26b5b6e.png"></td> <td>文件夹助手</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/5f9fd2f37445a462c0735b9dcca828cd.png"></td> <td>emoji搜索</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/50ef534638a9fc7fbed5274131afe503.png"></td> <td>😩能不能好好说话</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/9863ccc91f32b4ab660d6e58dd8b04ae.png"></td> <td>winget</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/6abb03b743259bd4c976d2a29da0a395.png"></td> <td>icons8搜索</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/527a20566499e7c3fb63e8705d60ccb7.png"></td> <td>kali 工具介绍</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/3ef0e794b7950193fc98289ea2b199e9.png"></td> <td>png转icon</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> </tbody> </table>

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,477 +0,0 @@
[TOC]
## quickcommand
### ❖ UI 交互
#### `showButtonBox(buttons, title)`
- buttons: Array 每一个元素对应一个按钮
- title: String | undefined 对话框标题
- 返回: Promise
- id: Integer 按钮的序号从0开始
- text: String 按钮的文本
显示一个按钮对话框,用来接收用户的输入
**示例**
```js
// then 写法
quickcommand.showButtonBox(["按钮1", "按钮2", "按钮3"]).then(({ id, text }) => {
console.log(`选择了第${id+1}个按钮`)
console.log(`按钮的文本为${text}`)
})
// async 写法
(async () =>{
let button = await quickcommand.showButtonBox(["按钮1", "按钮2", "按钮3"])
console.log(`选择了第${button.id+1}个按钮`)
console.log(`按钮的文本为${button.text}`)
})()
// 捕获错误
quickcommand.showButtonBox().catch(e => {
console.log(e)
})
```
**实例**
```js
// 截取自内置快捷命令: 通过 find 查找文件
quickcommand.showButtonBox(['打开文件', '在文件管理器中定位', '复制文件路径']).then(x => {
switch (x.id) {
case 0:
utools.shellOpenItem(file);
break;
case 1:
utools.shellShowItemInFolder(file);
break;
case 2:
utools.copyText(file);
break;
default:
break;
}
})
```
#### `showInputBox(placeHolders, title)`
- placeHolders: Array 每一个占位符对应一个输入框
- title: String | undefined 对话框标题
- 返回: Promise
- values: Array 所以输入框的值
显示一个输入框界面,用来接用户的输入
**示例**
```js
quickcommand.showInputBox(["输入框1", "输入框2", "输入框3"]).then(values => {
console.log(`输入的内容分别为${values}`)
})
```
**实例**
```js
// 截取自内置快捷命令: 文本替换
quickcommand.showInputBox(["要替换的内容,两边加 / 使用正则", "替换为的内容"]).then(inputs => {
var search = inputs[0]
var repl = inputs[1]
...
utools.hideMainWindow()
quickcommand.sleep(300)
quickcommand.simulateCopy()
quickcommand.sleep(100)
var source = electron.clipboard.readText()
source = source.replace(search, repl)
...
})
```
#### `showSelectList(selects, options)`
- selects: Array 每一个元素对应一个列表选项
- options: Object | undefined 列表的选项
- placeholder: String 搜索框占位符
- optionType: String 选项的格式,有`plaintext``html``json`三种,默认为`plaintext`
- 返回: Promise
- id: Integer 选择的序号从0开始
- text: String 选择的文本
- title/description/[…]: 当`optionType``json`时,对应`json`里的每一个属性
显示一个支持搜索的且可以动态更新的选项列表
当指定`optionType``json`时,类似于插件开发的`列表模式``title``description``icon`分别表示标题、描述和图标,其中`title`为必备属性
**示例**
```js
// plaintext
var opt = []
for (var i = 0; i < 15; i++) {
// 每一个选项为文本格式
opt.push(`选项` + i)
}
quickcommand.showSelectList(opt).then(choise => {
console.log(`选择的选项为${choise.text}`)
})
// json
var opt = []
for (var i = 0; i < 15; i++) {
// 每一个选项为 json 格式
opt.push({title: `选项${i}`, description: `选项${i}的描述`, abcd: `选项${i}的自定义属性`})
}
quickcommand.showSelectList(opt, {optionType: 'json'}).then(choise => {
console.log(`选择的选项为${choise.title}`)
})
// html
var opt = []
for (var i = 0; i < 15; i++) {
// 每一个选项为 html 格式
opt.push(`<div style="color: red">选项${i}</div>`)
}
quickcommand.showSelectList(opt, {optionType: 'html'}).then(choise => {
console.log(`选择的选项为${quickcommand.htmlParse(choise.text).body.innerText}`)
})
```
**实例**
```js
// 截取自内置快捷命令: 文本处理
let textManipulation = [ ... ]
let text = quickcommand.payload
let options = textManipulation.map(t => {
return {
title: t.name,
description: t.func(text)
}
})
quickcommand.showSelectList(options, { optionType: 'json' })
.then(choise => {
console.log(choise.description)
utools.copyText(choise.description)
})
axios.post('http://fy.iciba.com/ajax.php?a=fy', `f=auto&t=auto&w=${text}`)
.then(res => {
let content = res.data.content
let trans = content.out ? content.out : content.word_mean
let opt = textManipulation[0]
opt.description = trans
quickcommand.updateSelectList(opt, 0)
})
```
#### `updateSelectList(opt, id)`
- opt: String 要更新的选项
- id: Integer | undefined: 要更新的选项的序号,不赋值时则追加到最后一个选项后面
动态更新当前的选项列表的选项。
**示例**
```js
// 初始状态只有 1、2、3 三个选项
quickcommand.showSelectList(['1','2','3']).then(x=>{
console.log(x)
})
// 1s 后追加一个选项
quickcommand.setTimeout(()=>{
quickcommand.updateSelectList('4')
}, 1000)
// 2s 后更新第二个选项的值
quickcommand.setTimeout(()=>{
quickcommand.updateSelectList('updated', 1)
}, 2000)
```
#### `showTextAera(placeholder, value)`
- placeholder: String | undefined 文本框占位符
- value: String | undefined 默认的文本值
- 返回: Promise
- text: String 文本框的文本
显示一个文本框界面,用来接用户的输入
**示例**
```js
quickcommand.showTextAera("请输入文本").then(text=>{
console.log(`输入的文本为${text}`)
})
```
**实例**
```js
// 截取自内置快捷命令: vscode代码片段生成器
var snippet = {}
quickcommand.showTextAera("请输入代码片段").then(code => {
snippet.body = code.split("\n")
quickcommand.showInputBox(["代码片段的描述", "触发代码片段的关键词"])
.then(inputs => {
snippet.prefix = inputs[1]
snippet.description = inputs[0]
var result = `"${inputs[0]}": ` + JSON.stringify(snippet, null, '\t')
console.log(result)
utools.copyText(result)
quickcommand.showMessageBox('已复制')
})
})
```
#### `showMessageBox(message, icon, time)`
- message: String 显示的消息内容
- icon: String | undefined 图标,可为`success``error``warning``info``question`,默认为`success`
- time: Integer | undefined 多少毫秒后消失,默认为`3000`
显示一个自动消失的提示框
**示例**
```js
quickcommand.showMessageBox("这是一段3s后自动消失的成功提示")
quickcommand.showMessageBox("这是一段3s后自动消失的失败提示", "error")
```
#### `showConfirmBox(title)`
- title: String | undefined 提示的标题
- 返回: Promise
- confirmed: Boolean | undefined 是否点击了确定按钮
显示一个确认框
```js
quickcommand.showConfirmBox().then(confirmed => {
confirmed && console.log('点击了确定')
})
```
### ❖ 延时函数
#### `sleep(ms)`
- ms: Integer 等待的毫秒
由于`setTimeout`在electron中存在限制在隐藏到后台时不会被执行在vm2中也有bug所以在quickcommand的环境下被禁用了但对于模拟按键之类的场景延迟是不可缺少的所以提供了`sleep`函数来解决这个问题
**示例**
```js
utools.simulateKeyboardTap('d', 'alt')
quickcommand.sleep(200)
utools.simulateKeyboardTap('c', 'ctrl')
```
#### `setTimeout(callback, ms)`
- callback: Function 回调函数
- ms: Integer 延时的毫秒
用法和`setTimeout`一样,但实现原理不一样,`sleep`的异步版本
**示例**
```js
quickcommand.setTimeout(()=>{
console.log('2000毫秒后执行')
}, 2000)
```
### ❖ 前端封装
#### `htmlParse(html)`
- html: String 需要解析的`html`文本
- 返回: Object `DOM`对象
将给定的`html`字符串解析为`DOM`对象,用于快速编写爬虫脚本
**示例**
```js
var html = `<a href="https://u.tools/">uTools</a>`
var href = quickcommand.htmlParse(html).querySelector('a').href
console.log(`解析出来的a标签地址为${href}`)
```
#### `downloadFile(url, file | options)`
- url: String 地址
- file | options :
- file: String 当赋值为文件路径时,则表示下载文件的绝对路径
- options: Object | undefined 不赋值时,则会弹出对话框要求选择下载到的路径, 赋值为 `Object `时,表示弹出对话框的 `options `,格式和 `utools.showSaveDialog` 中的 `options `一致
- 返回: Promise
- content: Buffer 文件的内容
下载文件,可选直接下载到指定路径,或者弹出对话框选择下载路径
```js
// 下载文件到D:/
quickcommand.downloadFile('https://res.u-tools.cn/currentversion/uTools-1.1.3.exe', 'D:/')
// 下载文件,并弹出对话框询问保存路径
quickcommand.downloadFile('https://res.u-tools.cn/currentversion/uTools-1.1.3.exe')
```
#### `uploadFile(url, file | options, name, formData)`
- url: String 地址
- file | options :
- file: String 当赋值为文件路径时,则表示要上传的文件的绝对路径
- options: Object | undefined 不赋值时,则会弹出对话框要求选择要上传的文件的路径, 赋值为 `Object `时,表示弹出对话框的 `options `,格式和 `utools.showOpenDialog` 中的 `options `一致
- name: String | undefined 文件名,默认为`file`
- formData: Object | undefined 其他需要添加的表单数据
- 返回: Promise
- response: Object 响应内容
上传文件,可以直接上传指定文件,或者弹出对话框选择要上传的文件,可以自定义表单数据
```js
// 上传图片到图床
quickcommand.uploadFile("https://imgkr.com/api/v2/files/upload", "C:\\test.jpg").then(res=>{
console.log('上传成功,图片地址为:' + res.data.data)
})
// 包含额外表单数据
quickcommand.uploadFile("https://catbox.moe/user/api.php", "C:\\test.jpg", 'fileToUpload', {
"reqtype": "fileupload"
}).then(res=>{
console.log('上传成功,图片地址为:' + res.data)
})
```
### ❖ nodejs 封装
#### `loadRemoteScript(url)`
- url: String 脚本地址
- 返回: Promise
- Object: Object 返回从远程脚本加载的对象
加载一个远程脚本文件
```js
let remote = 'https://cdn.jsdelivr.net/npm/sweetalert2@9'
quickcommand.loadRemoteScript(remote).then(swal => {
swal.fire('已加载 sweetalert2 并成功弹窗')
})
// async/await
(async () => {
let remote = 'https://cdn.jsdelivr.net/npm/sweetalert2@9'
const swal = await quickcommand.loadRemoteScript(remote)
swal.fire('已加载 sweetalert2 并成功弹窗')
})()
```
#### `kill(pid, signal)`
- pid: Integer 进程 ID
- signal: String | Integer | undefined 将发送的信号,默认为 `'SIGTERM'`
`signal` 发送给 `pid` 标识的进程 , 默认为关闭进程,同`process.kill`
**示例**
```js
quickcommand.kill(16084)
```
#### `runVbs(script)`
- script: String VBS代码
- 返回: Promise
- Result: String 脚本运行结果
windows 下运行 VBS 脚本
**示例**
```js
quickcommand.runVbs(`CreateObject("SAPI.SpVoice").Speak"Hello"`)
```
### ❖ utools 封装
#### `enterData`
- Object 对应`utools.onPluginEnter``code` `type``payload`
- code: String 唯一标示
- type: 匹配模式,可以为 `text` `img` `files` `regex` `over` `window`
- payload: 当匹配模式为`关键字`时,返回进入插件的关键字;为`正则`时,返回匹配的文本;为`窗口`时,返回匹配的窗口信息;为`文件`时,返回匹配的文件信息
**示例**
```js
// 匹配模式为正则/划词时
if (quickcommand.enterData.type == 'regex'){
var text = quickcommand.enterData.payload
console.log(`主输入框匹配的文本为${text}`)
}
```
#### `simulateCopy()`
模拟复制操作
#### `simulatePaste()`
模拟粘贴操作
## 其他
### nodejs
#### ❖ <a href ="javascript:utools.ubrowser.goto('http://nodejs.cn/api/').run({width: 1280, height: 920})">文档</a>
#### ❖ 上下文
- **require**: *ƒ require(path)*
- **os**: {arch: *ƒ*, cpus: *ƒ*, endianness: *ƒ*, freemem: *ƒ*, getPriority: *ƒ*, …}
- **fs**: {appendFile: *ƒ*, appendFileSync: *ƒ*, access: *ƒ*, accessSync: *ƒ*, chown: *ƒ*, …}
- **path**: {resolve: *ƒ*, normalize: *ƒ*, isAbsolute: *ƒ*, join: *ƒ*, relative: *ƒ*, …}
- **child_process**: {_forkChild: *ƒ*, ChildProcess: *ƒ*, exec: *ƒ*, execFile: *ƒ*, execFileSync: *ƒ*, …}
- **util**: {_errnoException: *ƒ*, _exceptionWithHostPort: *ƒ*, _extend: *ƒ*, callbackify: *ƒ*, debuglog: *ƒ*, …}
- **Buffer**: *ƒ Buffer(arg, encodingOrOffset, length)*
- **process**: process {version: "v12.14.1", versions: {…}, arch: "x64", …}
- **TextDecoder**: *ƒ TextDecoder()*
- **TextEncoder**: *ƒ TextEncoder()*
- **URL**: *ƒ URL()*
- **URLSearchParams**: *ƒ URLSearchParams()*
- **axios**: *ƒ* *wrap()*
- [文档](./axios.html)
### electron
#### ❖ <a href ="javascript:utools.ubrowser.goto('http://www.electronjs.org/docs').run({width: 1280, height: 920})">文档</a>
#### ❖ 上下文
- **clipboard**: Object
- **contextBridge**: Object
- **crashReporter**: Object
- **desktopCapturer**: Object
- **ipcRenderer**: EventEmitter
- **nativeImage**: Object
- **shell**: Object
- **webFrame**: WebFrame
### utools
#### ❖ <a href ="javascript:utools.ubrowser.goto('https://u.tools/docs/developer/api.html').run({width: 1280, height: 920})">文档</a>
#### ❖ 上下文
- all except below
- ~~db~~
- ~~removeFeature~~
- ~~setFeature~~

View File

@@ -1,132 +0,0 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.pictureCompress = factory());
}(this, function () { 'use strict';
/**
* 将图片压缩为对应尺寸
* @param {Object} options
* @param {String} options.img 图片的url或者base64数据
* @param {Number} options.width 目标图片的宽度
* @param {Number} options.height 目标图片的高度
* @param {Number} options.quality 生成目标图片质量
* @param {String} options.fit 图片压缩填充模式默认 scale按比例缩放可选 fill按使用目标尺寸
* @param {String} options.type 图片压缩类型默认 jpg可选 png
* @param {Number} options.rotate 图片旋转,由于手机拍照的角度和我们使用的头像不一致,需要旋转 默认0 仅支持 90 180 -90
* @returns {Promise} then {width,height,img}
*/
function pictureCompress(options) {
return new Promise(function (resolve, reject) {
if (!options.img) {
reject(new Error('need img'));
return;
}
var imgSrc = options.img,
width = options.width || 640,
height = options.height || 640,
type = options.type || 'jpg',
quality = options.quality || 0.92,
fit = options.fit || 'scale',
rotate = options.rotate || 0;
if (width <= 0 || height <= 0) {
reject(new Error('dist width or height need > 0'));
return;
}
if (!/jpg|png|jpeg/.test(type)) {
reject(new Error('type need jpg or png!'));
return;
}
if (rotate !== 90 && rotate !== -90 && rotate !== 0 && rotate !== 180) {
reject(new Error('rotate mast be 0 90 -90 180!'));
return;
}
var changeWidthAndHeight = rotate === 90 || rotate === -90;
var image = new Image();
image.src = imgSrc;
image.onload = function () {
var distSize = getDistSize({
width: changeWidthAndHeight ? this.naturalHeight : this.naturalWidth,
height: changeWidthAndHeight ? this.naturalWidth : this.naturalHeight
}, {
width: changeWidthAndHeight ? height : width,
height: changeWidthAndHeight ? width : height
}, fit);
var imgData = compress(this, distSize.width, distSize.height, type, quality, rotate);
resolve({
width: distSize.width,
height: distSize.height,
img: imgData
});
};
image.onerror = function (err) {
reject(err);
};
});
}
/**
* 将图片转换为固定尺寸的
* @param {Image} img 图片数据
* @param {Number} width 转换之后的图片宽度
* @param {Number} height 转换之后的图片高度
* @param {String} type base64的图片类型 jpg png
* @param {Number} quality 转换之后的图片质量
*/
function compress(img, width, height, type, quality, rotate) {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var types = {
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png'
};
canvas.width = width;
canvas.height = height;
if (rotate === 90) {
ctx.translate(width, 0);
ctx.rotate(90 * Math.PI / 180);
ctx.drawImage(img, 0, 0, height, width);
} else if (rotate === -90) {
ctx.translate(0, height);
ctx.rotate(-90 * Math.PI / 180);
ctx.drawImage(img, 0, 0, height, width);
} else if (rotate === 180) {
ctx.translate(width, height);
ctx.rotate(180 * Math.PI / 180);
ctx.drawImage(img, 0, 0, width, height);
} else {
ctx.drawImage(img, 0, 0, width, height);
}
return canvas.toDataURL(types[type], quality);
}
/**
* 选择源尺寸与目标尺寸比例中较小的那个,保证图片可以完全显示
* 最大值不超过1如果图片源尺寸小于目标尺寸则不做处理返回图片原尺寸
* @param {Object} source 源图片的宽高
* @param {Object} dist 目标图片的宽高
*/
function getDistSize(source, dist, fit) {
if (fit === 'fill') return dist;
var scale = Math.min(dist.width / source.width, dist.height / source.height, 1);
return {
width: Math.round(source.width * scale),
height: Math.round(source.height * scale)
};
}
return pictureCompress;
}));

View File

@@ -1,3 +0,0 @@
if (parseInt(process.versions.node.split('.')[0]) < 6) throw new Error('vm2 requires Node.js version 6 or newer.');
module.exports = require('./lib/main');

View File

@@ -1,35 +0,0 @@
'use strict';
const pa = require('path');
const {NodeVM, VMError} = require('../');
if (process.argv[2]) {
const path = pa.resolve(process.argv[2]);
console.log(`\x1B[90m[vm] creating VM for ${path}\x1B[39m`);
const started = Date.now();
try {
NodeVM.file(path, {
verbose: true,
require: {
external: true
}
});
console.log(`\x1B[90m[vm] VM completed in ${Date.now() - started}ms\x1B[39m`);
} catch (ex) {
if (ex instanceof VMError) {
console.error(`\x1B[31m[vm:error] ${ex.message}\x1B[39m`);
} else {
const {stack} = ex;
if (stack) {
console.error(`\x1B[31m[vm:error] ${stack}\x1B[39m`);
} else {
console.error(`\x1B[31m[vm:error] ${ex}\x1B[39m`);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,83 +0,0 @@
'use strict';
// eslint-disable-next-line no-invalid-this, no-shadow
const {GeneratorFunction, AsyncFunction, AsyncGeneratorFunction, global, internal, host, hook} = this;
const {Contextify, Decontextify} = internal;
// eslint-disable-next-line no-shadow
const {Function, eval: eval_, Promise, Object, Reflect} = global;
const {getOwnPropertyDescriptor, defineProperty, assign} = Object;
const {apply: rApply, construct: rConstruct} = Reflect;
const FunctionHandler = {
__proto__: null,
apply(target, thiz, args) {
const type = this.type;
args = Decontextify.arguments(args);
try {
args = Contextify.value(hook(type, args));
} catch (e) {
throw Contextify.value(e);
}
return rApply(target, thiz, args);
},
construct(target, args, newTarget) {
const type = this.type;
args = Decontextify.arguments(args);
try {
args = Contextify.value(hook(type, args));
} catch (e) {
throw Contextify.value(e);
}
return rConstruct(target, args, newTarget);
}
};
function makeCheckFunction(type) {
return assign({
__proto__: null,
type
}, FunctionHandler);
}
function override(obj, prop, value) {
const desc = getOwnPropertyDescriptor(obj, prop);
desc.value = value;
defineProperty(obj, prop, desc);
}
const proxiedFunction = new host.Proxy(Function, makeCheckFunction('function'));
override(Function.prototype, 'constructor', proxiedFunction);
if (GeneratorFunction) {
Object.setPrototypeOf(GeneratorFunction, proxiedFunction);
override(GeneratorFunction.prototype, 'constructor', new host.Proxy(GeneratorFunction, makeCheckFunction('generator_function')));
}
if (AsyncFunction) {
Object.setPrototypeOf(AsyncFunction, proxiedFunction);
override(AsyncFunction.prototype, 'constructor', new host.Proxy(AsyncFunction, makeCheckFunction('async_function')));
}
if (AsyncGeneratorFunction) {
Object.setPrototypeOf(AsyncGeneratorFunction, proxiedFunction);
override(AsyncGeneratorFunction.prototype, 'constructor', new host.Proxy(AsyncGeneratorFunction, makeCheckFunction('async_generator_function')));
}
global.Function = proxiedFunction;
global.eval = new host.Proxy(eval_, makeCheckFunction('eval'));
if (Promise) {
Promise.prototype.then = new host.Proxy(Promise.prototype.then, makeCheckFunction('promise_then'));
// This seems not to work, and will produce
// UnhandledPromiseRejectionWarning: TypeError: Method Promise.prototype.then called on incompatible receiver [object Object].
// This is likely caused since the host.Promise.prototype.then cannot use the VM Proxy object.
// Contextify.connect(host.Promise.prototype.then, Promise.prototype.then);
if (Promise.prototype.finally) {
Promise.prototype.finally = new host.Proxy(Promise.prototype.finally, makeCheckFunction('promise_finally'));
// Contextify.connect(host.Promise.prototype.finally, Promise.prototype.finally);
}
if (Promise.prototype.catch) {
Promise.prototype.catch = new host.Proxy(Promise.prototype.catch, makeCheckFunction('promise_catch'));
// Contextify.connect(host.Promise.prototype.catch, Promise.prototype.catch);
}
}

View File

@@ -1,12 +0,0 @@
// source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
function escapeRegExp(string) {
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
function match(wildcard, s) {
const regexString = escapeRegExp(wildcard).replace(/\\\*/g, '\\S*').replace(/\\\?/g, '.');
const regex = new RegExp(regexString);
return regex.test(s);
}
module.exports = {match};

File diff suppressed because it is too large Load Diff

View File

@@ -1,682 +0,0 @@
/* eslint-disable no-shadow, no-invalid-this */
/* global vm, host, Contextify, Decontextify, VMError, options */
'use strict';
const {Script} = host.require('vm');
const fs = host.require('fs');
const pa = host.require('path');
const BUILTIN_MODULES = host.process.binding('natives');
const parseJSON = JSON.parse;
const importModuleDynamically = () => {
// We can't throw an error object here because since vm.Script doesn't store a context, we can't properly contextify that error object.
// eslint-disable-next-line no-throw-literal
throw 'Dynamic imports are not allowed.';
};
/**
* @param {Object} host Hosts's internal objects.
*/
return ((vm, host) => {
'use strict';
const global = this;
const TIMERS = new host.WeakMap(); // Contains map of timers created inside sandbox
const BUILTINS = {__proto__: null};
const CACHE = {__proto__: null};
const EXTENSIONS = {
__proto__: null,
['.json'](module, filename) {
try {
const code = fs.readFileSync(filename, 'utf8');
module.exports = parseJSON(code);
} catch (e) {
throw Contextify.value(e);
}
},
['.node'](module, filename) {
if (vm.options.require.context === 'sandbox') throw new VMError('Native modules can be required only with context set to \'host\'.');
try {
module.exports = Contextify.readonly(host.require(filename));
} catch (e) {
throw Contextify.value(e);
}
}
};
for (let i = 0; i < vm.options.sourceExtensions.length; i++) {
const ext = vm.options.sourceExtensions[i];
EXTENSIONS['.' + ext] = (module, filename, dirname) => {
if (vm.options.require.context !== 'sandbox') {
try {
module.exports = Contextify.readonly(host.require(filename));
} catch (e) {
throw Contextify.value(e);
}
} else {
let script;
try {
// Load module
let contents = fs.readFileSync(filename, 'utf8');
contents = vm._compiler(contents, filename);
const code = host.STRICT_MODULE_PREFIX + contents + host.MODULE_SUFFIX;
const ccode = vm._hook('run', [code]);
// Precompile script
script = new Script(ccode, {
__proto__: null,
filename: filename || 'vm.js',
displayErrors: false,
importModuleDynamically
});
} catch (ex) {
throw Contextify.value(ex);
}
const closure = script.runInContext(global, {
__proto__: null,
filename: filename || 'vm.js',
displayErrors: false,
importModuleDynamically
});
// run the script
closure(module.exports, module.require, module, filename, dirname);
}
};
}
const _parseExternalOptions = (options) => {
if (host.Array.isArray(options)) {
return {
__proto__: null,
external: options,
transitive: false
};
}
return {
__proto__: null,
external: options.modules,
transitive: options.transitive
};
};
/**
* Resolve filename.
*/
const _resolveFilename = (path) => {
if (!path) return null;
let hasPackageJson;
try {
path = pa.resolve(path);
const exists = fs.existsSync(path);
const isdir = exists ? fs.statSync(path).isDirectory() : false;
// direct file match
if (exists && !isdir) return path;
// load as file
for (let i = 0; i < vm.options.sourceExtensions.length; i++) {
const ext = vm.options.sourceExtensions[i];
if (fs.existsSync(`${path}.${ext}`)) return `${path}.${ext}`;
}
if (fs.existsSync(`${path}.json`)) return `${path}.json`;
if (fs.existsSync(`${path}.node`)) return `${path}.node`;
// load as module
hasPackageJson = fs.existsSync(`${path}/package.json`);
} catch (e) {
throw Contextify.value(e);
}
if (hasPackageJson) {
let pkg;
try {
pkg = fs.readFileSync(`${path}/package.json`, 'utf8');
} catch (e) {
throw Contextify.value(e);
}
try {
pkg = parseJSON(pkg);
} catch (ex) {
throw new VMError(`Module '${path}' has invalid package.json`, 'EMODULEINVALID');
}
let main;
if (pkg && pkg.main) {
main = _resolveFilename(`${path}/${pkg.main}`);
if (!main) main = _resolveFilename(`${path}/index`);
} else {
main = _resolveFilename(`${path}/index`);
}
return main;
}
// load as directory
try {
for (let i = 0; i < vm.options.sourceExtensions.length; i++) {
const ext = vm.options.sourceExtensions[i];
if (fs.existsSync(`${path}/index.${ext}`)) return `${path}/index.${ext}`;
}
if (fs.existsSync(`${path}/index.json`)) return `${path}/index.json`;
if (fs.existsSync(`${path}/index.node`)) return `${path}/index.node`;
} catch (e) {
throw Contextify.value(e);
}
return null;
};
/**
* Builtin require.
*/
const _requireBuiltin = (moduleName) => {
if (moduleName === 'buffer') return ({Buffer});
if (BUILTINS[moduleName]) return BUILTINS[moduleName].exports; // Only compiled builtins are stored here
if (moduleName === 'util') {
return Contextify.readonly(host.require(moduleName), {
// Allows VM context to use util.inherits
__proto__: null,
inherits: (ctor, superCtor) => {
ctor.super_ = superCtor;
Object.setPrototypeOf(ctor.prototype, superCtor.prototype);
}
});
}
if (moduleName === 'events' || moduleName === 'internal/errors') {
let script;
try {
script = new Script(`(function (exports, require, module, process, internalBinding) {
'use strict';
const primordials = global;
${BUILTIN_MODULES[moduleName]}
\n
});`, {
filename: `${moduleName}.vm.js`
});
} catch (e) {
throw Contextify.value(e);
}
// setup module scope
const module = BUILTINS[moduleName] = {
exports: {},
require: _requireBuiltin
};
// run script
try {
// FIXME binding should be contextified
script.runInContext(global)(module.exports, module.require, module, host.process, host.process.binding);
} catch (e) {
// e could be from inside or outside of sandbox
throw new VMError(`Error loading '${moduleName}'`);
}
return module.exports;
}
return Contextify.readonly(host.require(moduleName));
};
/**
* Prepare require.
*/
const _prepareRequire = (currentDirname, parentAllowsTransitive = false) => {
const _require = moduleName => {
let requireObj;
try {
const optionsObj = vm.options;
if (optionsObj.nesting && moduleName === 'vm2') return {VM: Contextify.readonly(host.VM), NodeVM: Contextify.readonly(host.NodeVM)};
requireObj = optionsObj.require;
} catch (e) {
throw Contextify.value(e);
}
if (!requireObj) throw new VMError(`Access denied to require '${moduleName}'`, 'EDENIED');
if (moduleName == null) throw new VMError("Module '' not found.", 'ENOTFOUND');
if (typeof moduleName !== 'string') throw new VMError(`Invalid module name '${moduleName}'`, 'EINVALIDNAME');
let filename;
let allowRequireTransitive = false;
// Mock?
try {
const {mock} = requireObj;
if (mock) {
const mockModule = mock[moduleName];
if (mockModule) {
return Contextify.readonly(mockModule);
}
}
} catch (e) {
throw Contextify.value(e);
}
// Builtin?
if (BUILTIN_MODULES[moduleName]) {
let allowed;
try {
const builtinObj = requireObj.builtin;
if (host.Array.isArray(builtinObj)) {
if (builtinObj.indexOf('*') >= 0) {
allowed = builtinObj.indexOf(`-${moduleName}`) === -1;
} else {
allowed = builtinObj.indexOf(moduleName) >= 0;
}
} else if (builtinObj) {
allowed = builtinObj[moduleName];
} else {
allowed = false;
}
} catch (e) {
throw Contextify.value(e);
}
if (!allowed) throw new VMError(`Access denied to require '${moduleName}'`, 'EDENIED');
return _requireBuiltin(moduleName);
}
// External?
let externalObj;
try {
externalObj = requireObj.external;
} catch (e) {
throw Contextify.value(e);
}
if (!externalObj) throw new VMError(`Access denied to require '${moduleName}'`, 'EDENIED');
if (/^(\.|\.\/|\.\.\/)/.exec(moduleName)) {
// Module is relative file, e.g. ./script.js or ../script.js
if (!currentDirname) throw new VMError('You must specify script path to load relative modules.', 'ENOPATH');
filename = _resolveFilename(`${currentDirname}/${moduleName}`);
} else if (/^(\/|\\|[a-zA-Z]:\\)/.exec(moduleName)) {
// Module is absolute file, e.g. /script.js or //server/script.js or C:\script.js
filename = _resolveFilename(moduleName);
} else {
// Check node_modules in path
if (!currentDirname) throw new VMError('You must specify script path to load relative modules.', 'ENOPATH');
if (typeof externalObj === 'object') {
let isWhitelisted;
try {
const { external, transitive } = _parseExternalOptions(externalObj);
isWhitelisted = external.some(ext => host.helpers.match(ext, moduleName)) || (transitive && parentAllowsTransitive);
} catch (e) {
throw Contextify.value(e);
}
if (!isWhitelisted) {
throw new VMError(`The module '${moduleName}' is not whitelisted in VM.`, 'EDENIED');
}
allowRequireTransitive = true;
}
// FIXME the paths array has side effects
const paths = currentDirname.split(pa.sep);
while (paths.length) {
const path = paths.join(pa.sep);
// console.log moduleName, "#{path}#{pa.sep}node_modules#{pa.sep}#{moduleName}"
filename = _resolveFilename(`${path}${pa.sep}node_modules${pa.sep}${moduleName}`);
if (filename) break;
paths.pop();
}
}
if (!filename) {
let resolveFunc;
try {
resolveFunc = requireObj.resolve;
} catch (e) {
throw Contextify.value(e);
}
if (resolveFunc) {
let resolved;
try {
resolved = requireObj.resolve(moduleName, currentDirname);
} catch (e) {
throw Contextify.value(e);
}
filename = _resolveFilename(resolved);
}
}
if (!filename) throw new VMError(`Cannot find module '${moduleName}'`, 'ENOTFOUND');
// return cache whenever possible
if (CACHE[filename]) return CACHE[filename].exports;
const dirname = pa.dirname(filename);
const extname = pa.extname(filename);
let allowedModule = true;
try {
const rootObj = requireObj.root;
if (rootObj) {
const rootPaths = host.Array.isArray(rootObj) ? rootObj : host.Array.of(rootObj);
allowedModule = rootPaths.some(path => host.String.prototype.startsWith.call(dirname, pa.resolve(path)));
}
} catch (e) {
throw Contextify.value(e);
}
if (!allowedModule) {
throw new VMError(`Module '${moduleName}' is not allowed to be required. The path is outside the border!`, 'EDENIED');
}
const module = CACHE[filename] = {
filename,
exports: {},
require: _prepareRequire(dirname, allowRequireTransitive)
};
// lookup extensions
if (EXTENSIONS[extname]) {
EXTENSIONS[extname](module, filename, dirname);
return module.exports;
}
throw new VMError(`Failed to load '${moduleName}': Unknown type.`, 'ELOADFAIL');
};
return _require;
};
/**
* Prepare sandbox.
*/
// This is a function and not an arrow function, since the original is also a function
global.setTimeout = function setTimeout(callback, delay, ...args) {
if (typeof callback !== 'function') throw new TypeError('"callback" argument must be a function');
let tmr;
try {
tmr = host.setTimeout(Decontextify.value(() => {
// FIXME ...args has side effects
callback(...args);
}), Decontextify.value(delay));
} catch (e) {
throw Contextify.value(e);
}
const local = Contextify.value(tmr);
TIMERS.set(local, tmr);
return local;
};
global.setInterval = function setInterval(callback, interval, ...args) {
if (typeof callback !== 'function') throw new TypeError('"callback" argument must be a function');
let tmr;
try {
tmr = host.setInterval(Decontextify.value(() => {
// FIXME ...args has side effects
callback(...args);
}), Decontextify.value(interval));
} catch (e) {
throw Contextify.value(e);
}
const local = Contextify.value(tmr);
TIMERS.set(local, tmr);
return local;
};
global.setImmediate = function setImmediate(callback, ...args) {
if (typeof callback !== 'function') throw new TypeError('"callback" argument must be a function');
let tmr;
try {
tmr = host.setImmediate(Decontextify.value(() => {
// FIXME ...args has side effects
callback(...args);
}));
} catch (e) {
throw Contextify.value(e);
}
const local = Contextify.value(tmr);
TIMERS.set(local, tmr);
return local;
};
global.clearTimeout = function clearTimeout(local) {
try {
host.clearTimeout(TIMERS.get(local));
} catch (e) {
throw Contextify.value(e);
}
};
global.clearInterval = function clearInterval(local) {
try {
host.clearInterval(TIMERS.get(local));
} catch (e) {
throw Contextify.value(e);
}
};
global.clearImmediate = function clearImmediate(local) {
try {
host.clearImmediate(TIMERS.get(local));
} catch (e) {
throw Contextify.value(e);
}
};
function addListener(name, handler) {
if (name !== 'beforeExit' && name !== 'exit') {
throw new Error(`Access denied to listen for '${name}' event.`);
}
try {
host.process.on(name, Decontextify.value(handler));
} catch (e) {
throw Contextify.value(e);
}
return this;
}
const {argv: optionArgv, env: optionsEnv} = options;
// FIXME wrong class structure
global.process = {
argv: optionArgv !== undefined ? Contextify.value(optionArgv) : [],
title: host.process.title,
version: host.process.version,
versions: Contextify.readonly(host.process.versions),
arch: host.process.arch,
platform: host.process.platform,
env: optionsEnv !== undefined ? Contextify.value(optionsEnv) : {},
pid: host.process.pid,
features: Contextify.readonly(host.process.features),
nextTick: function nextTick(callback, ...args) {
if (typeof callback !== 'function') {
throw new Error('Callback must be a function.');
}
try {
host.process.nextTick(Decontextify.value(() => {
// FIXME ...args has side effects
callback(...args);
}));
} catch (e) {
throw Contextify.value(e);
}
},
hrtime: function hrtime(time) {
try {
return Contextify.value(host.process.hrtime(Decontextify.value(time)));
} catch (e) {
throw Contextify.value(e);
}
},
cwd: function cwd() {
try {
return Contextify.value(host.process.cwd());
} catch (e) {
throw Contextify.value(e);
}
},
addListener,
on: addListener,
once: function once(name, handler) {
if (name !== 'beforeExit' && name !== 'exit') {
throw new Error(`Access denied to listen for '${name}' event.`);
}
try {
host.process.once(name, Decontextify.value(handler));
} catch (e) {
throw Contextify.value(e);
}
return this;
},
listeners: function listeners(name) {
if (name !== 'beforeExit' && name !== 'exit') {
// Maybe add ({__proto__:null})[name] to throw when name fails in https://tc39.es/ecma262/#sec-topropertykey.
return [];
}
// Filter out listeners, which were not created in this sandbox
try {
return Contextify.value(host.process.listeners(name).filter(listener => Contextify.isVMProxy(listener)));
} catch (e) {
throw Contextify.value(e);
}
},
removeListener: function removeListener(name, handler) {
if (name !== 'beforeExit' && name !== 'exit') {
return this;
}
try {
host.process.removeListener(name, Decontextify.value(handler));
} catch (e) {
throw Contextify.value(e);
}
return this;
},
umask: function umask() {
if (arguments.length) {
throw new Error('Access denied to set umask.');
}
try {
return Contextify.value(host.process.umask());
} catch (e) {
throw Contextify.value(e);
}
}
};
if (vm.options.console === 'inherit') {
global.console = Contextify.readonly(host.console);
} else if (vm.options.console === 'redirect') {
global.console = {
debug(...args) {
try {
// FIXME ...args has side effects
vm.emit('console.debug', ...Decontextify.arguments(args));
} catch (e) {
throw Contextify.value(e);
}
},
log(...args) {
try {
// FIXME ...args has side effects
vm.emit('console.log', ...Decontextify.arguments(args));
} catch (e) {
throw Contextify.value(e);
}
},
info(...args) {
try {
// FIXME ...args has side effects
vm.emit('console.info', ...Decontextify.arguments(args));
} catch (e) {
throw Contextify.value(e);
}
},
warn(...args) {
try {
// FIXME ...args has side effects
vm.emit('console.warn', ...Decontextify.arguments(args));
} catch (e) {
throw Contextify.value(e);
}
},
error(...args) {
try {
// FIXME ...args has side effects
vm.emit('console.error', ...Decontextify.arguments(args));
} catch (e) {
throw Contextify.value(e);
}
},
dir(...args) {
try {
vm.emit('console.dir', ...Decontextify.arguments(args));
} catch (e) {
throw Contextify.value(e);
}
},
time() {},
timeEnd() {},
trace(...args) {
try {
// FIXME ...args has side effects
vm.emit('console.trace', ...Decontextify.arguments(args));
} catch (e) {
throw Contextify.value(e);
}
}
};
}
/*
Return contextified require.
*/
return _prepareRequire;
})(vm, host);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 826 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -1,95 +0,0 @@
{
"name": "public",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"dependencies": {
"axios": "^0.24.0",
"iconv-lite": "^0.6.3",
"lodash": "^4.17.21"
}
},
"node_modules/axios": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
"dependencies": {
"follow-redirects": "^1.14.4"
}
},
"node_modules/follow-redirects": {
"version": "1.14.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
}
},
"dependencies": {
"axios": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
"requires": {
"follow-redirects": "^1.14.4"
}
},
"follow-redirects": {
"version": "1.14.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w=="
},
"iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
}
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
}
}
}

View File

@@ -1,7 +0,0 @@
{
"dependencies": {
"axios": "^0.24.0",
"iconv-lite": "^0.6.3",
"lodash": "^4.17.21"
}
}

View File

@@ -1,40 +0,0 @@
{
"pluginName": "快捷命令",
"description": "快速打开软件、网址及运行批处理、shell等脚本免编写插件使用utools的api及实现UI交互",
"main": "index.html",
"homepage": "https://github.com/fofolee/uTools-quickcommand",
"publishPage": "https://yuanliao.info/d/424",
"version": "3.0.0",
"development": {
"main": "http://127.0.0.1:8080/"
},
"author": "云之轩",
"unpack":"autopep8.py",
"logo": "logo.png",
"preload": "preload.js",
"pluginSetting": {
"single": false
},
"features": [
{
"code": "configuration",
"explain": "新建、编辑或获取快捷命令",
"cmds": [ "快捷命令", "QuickCommand"]
},
{
"code": "code",
"explain": "运行代码",
"cmds": [ "运行代码", "RunCode"]
},
{
"code": "newcommand",
"explain": "快速新建快捷命令",
"cmds": [ "新建快捷命令", "NewCommand", {
"type": "regex",
"label": "新建快捷命令",
"match": "/^\\{[\\s\\S]*\"program\" *: *\".*\"[\\s\\S]*\"cmd\" *: *\".*\"[\\s\\S]*\\}$/i",
"maxNum": 1
}]
}
]
}

View File

@@ -1,578 +0,0 @@
const fs = require('fs');
const os = require('os');
const child_process = require("child_process")
const iconv = require('iconv-lite')
const electron = require('electron')
const {
NodeVM,
VM
} = require('./lib/vm2')
const path = require("path")
const axios = require('axios');
const pictureCompress = require("./lib/picture-compressor")
window._ = require("lodash")
// axios.defaults.adapter = require('axios/lib/adapters/http')
if (!utools.isWindows()) process.env.PATH += ':/usr/local/bin:/usr/local/sbin'
// window.startTime = new Date().getTime()
const shortCodes = [
open = path => {
utools.shellOpenItem(path)
},
locate = path => {
utools.shellShowItemInFolder(path);
},
visit = url => {
utools.shellOpenExternal(url);
},
system = cmd => {
child_process.exec(cmd);
},
message = msg => {
utools.showNotification(msg)
},
keyTap = (key, ...modifier) => utools.simulateKeyboardTap(key, ...modifier),
copyTo = text => {
electron.clipboard.writeText(text)
},
send = text => {
copyTo(text);
quickcommand.simulatePaste();
}
]
const ctlKey = utools.isMacOs() ? 'command' : 'control'
window.quickcommand = {
// 模拟复制操作
simulateCopy: function() {
utools.simulateKeyboardTap('c', ctlKey);
},
// 模拟粘贴操作
simulatePaste: function() {
utools.simulateKeyboardTap('v', ctlKey);
},
// setTimout 不能在 vm2 中使用,同时在 electron 中有 bug
sleep: function(ms) {
var start = new Date().getTime()
try {
// node 16.13.1
child_process.execSync(getSleepCodeByShell(ms), {
timeout: ms,
windowsHide: true
})
} catch (ex) {}
var end = new Date().getTime()
return (end - start)
},
// 重写 setTimeout
setTimeout: function(callback, ms) {
var start = new Date().getTime()
child_process.exec(getSleepCodeByShell(ms), {
timeout: ms
}, (err, stdout, stderr) => {
var end = new Date().getTime()
callback(end - start)
})
},
// 关闭进程
kill: function(pid, signal = 'SIGTERM') {
process.kill(pid, signal)
},
// dom 解析
htmlParse: function(html) {
return new DOMParser().parseFromString(html, 'text/html')
},
// 下载文件
downloadFile: function(url, file = {}) {
return new Promise((reslove, reject) => {
if (file instanceof Object) file = utools.showSaveDialog(JSON.parse(JSON.stringify(file)))
axios({
method: 'get',
url: url,
responseType: 'arraybuffer'
}).then(res => {
var filebuffer = Buffer.from(res.data)
fs.writeFile(file, filebuffer, err => {
if (err) reject(err)
else reslove(filebuffer)
})
}).catch(err => {
reject(err)
})
})
},
// 上传文件
uploadFile: function(url, file = {}, name = 'file', formData = {}) {
return new Promise((reslove, reject) => {
var objfile
if (file instanceof File) {
objfile = file
} else {
if (file instanceof Object) file = utools.showOpenDialog(JSON.parse(JSON.stringify(file)))[0]
if (!fs.existsSync(file)) return reject('文件不存在')
var arraybuffer = fs.readFileSync(file).buffer
var objfile = new File([arraybuffer], path.basename(file))
}
var form = new FormData();
form.append(name, objfile)
var keys = Object.keys(formData)
if (keys.length) keys.forEach(k => form.append(k, formData[k]))
axios.post(url, form, {
headers: {
'accept': 'application/json',
'Content-Type': `multipart/form-data; boundary=${formData._boundary}`,
}
}).then(res => {
reslove(res)
}).catch(err => {
reject(err)
})
})
},
// 载入在线资源
loadRemoteScript: async function(url, forceUpdate = false) {
if (!/^((ht|f)tps?):\/\/([\w\-]+(\.[\w\-]+)*\/)*[\w\-]+(\.[\w\-]+)*\/?(\?([\w\-\.,@?^=%&:\/~\+#]*)+)?/.test(url)) throw 'url 不合法'
let remote = url
let root = path.join(os.tmpdir(), 'qcRemoteScript')
if (!fs.existsSync(root)) fs.mkdirSync(root)
let local = path.join(root, require('crypto').createHash('md5').update(url).digest('hex'))
if (forceUpdate || !fs.existsSync(local)) await this.downloadFile(remote, local)
return require(local)
}
}
// 运行vbs脚本
if (process.platform == 'win32') quickcommand.runVbs = function(script) {
return new Promise((reslove, reject) => {
var tempfile = path.join(os.tmpdir(), 'TempVBSScript.vbs')
fs.writeFile(tempfile, iconv.encode(script, 'gbk'), err => {
child_process.exec(`cscript.exe /nologo "${tempfile}"`, {
encoding: "buffer"
}, (err, stdout, stderr) => {
if (err) reject(iconv.decode(stderr, 'gbk'))
else reslove(iconv.decode(stdout, 'gbk'))
});
})
})
}
window.temporaryStore = {
listeners: {}
}
window.temporaryStoreSoldOut = () => {
_.forIn(temporaryStore.listeners, (listener, key) => {
document.removeEventListener(...listener)
})
window.temporaryStore = {
listeners: {}
}
}
// python -c
window.runPythonCommand = py => {
try {
let result = child_process.execFileSync("python", ["-c", py], {
windowsHide: true,
encoding: 'buffer'
})
return iconv.decode(result, utools.isWindows() ? 'gbk' : 'utf8').trim()
} catch (e) {
alert(e)
return ""
}
}
// 在终端中执行
if (process.platform !== 'linux') quickcommand.runInTerminal = function(cmdline, dir) {
let command = getCommandToLaunchTerminal(cmdline, dir)
child_process.exec(command)
}
let getCommandToLaunchTerminal = (cmdline, dir) => {
let cd = ''
if (utools.isWindows()) {
let appPath = path.join(utools.getPath('home'), '/AppData/Local/Microsoft/WindowsApps/')
// 直接 existsSync wt.exe 无效
if (fs.existsSync(appPath) && fs.readdirSync(appPath).includes('wt.exe')) {
cmdline = cmdline.replace(/"/g, `\\"`)
if (dir) cd = `-d "${dir.replace(/\\/g, '/')}"`
command = `${appPath}wt.exe ${cd} cmd /k "${cmdline}"`
} else {
cmdline = cmdline.replace(/"/g, `^"`)
if (dir) cd = `cd /d "${dir.replace(/\\/g, '/')}" &&`
command = `${cd} start "" cmd /k "${cmdline}"`
}
} else {
cmdline = cmdline.replace(/"/g, `\\"`)
if (dir) cd = `cd ${dir.replace(/ /g, `\\\\ `)} &&`
if (fs.existsSync('/Applications/iTerm.app')) {
command = `osascript -e 'tell application "iTerm"
create window with default profile
tell current session of current window to write text "clear && ${cd} ${cmdline}"
end tell'`
} else {
command = `osascript -e 'tell application "Terminal"
do script "clear && ${cd} ${cmdline}"
activate
end tell'`
}
}
console.log(command);
return command
}
window.pluginInfo = () => {
return JSON.parse(fs.readFileSync(path.join(__dirname, 'plugin.json')))
}
let getSleepCodeByShell = ms => {
var cmd, tempFilePath
if (utools.isWindows()) {
tempFilePath = getQuickcommandTempFile('vbs')
cmd = `echo set ws=CreateObject("Wscript.Shell") > ${tempFilePath} && echo Wscript.sleep ${ms} >> ${tempFilePath} && cscript /nologo ${tempFilePath}`
} else {
cmd = `sleep ${ms / 1000}`
}
return cmd
}
// 屏蔽危险函数
window.getuToolsLite = () => {
var utoolsLite = Object.assign({}, utools)
if (utools.isDev()) return utoolsLite
// 数据库相关接口
delete utoolsLite.db
delete utoolsLite.dbStorage
delete utoolsLite.removeFeature
delete utoolsLite.setFeature
delete utoolsLite.onDbPull
// 支付相关接口
delete utoolsLite.fetchUserServerTemporaryToken
delete utoolsLite.getUserServerTemporaryToken
delete utoolsLite.openPayment
delete utoolsLite.fetchUserPayments
return utoolsLite
}
let getSandboxFuns = () => {
var sandbox = {
utools: getuToolsLite(),
quickcommand: quickcommand,
electron: electron,
axios: axios,
Audio: Audio,
fetch: fetch
}
shortCodes.forEach(f => {
sandbox[f.name] = f
})
return sandbox
}
let createNodeVM = () => {
var sandbox = getSandboxFuns()
const vm = new NodeVM({
require: {
external: true,
builtin: ["*"],
},
console: 'redirect',
env: process.env,
sandbox: sandbox,
});
return vm
}
let stringifyAll = item => {
var cache = [];
var string = JSON.stringify(item, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) return
cache.push(value);
}
return value;
}, '\t')
if (string != "{}") return string
else return item.toString()
}
let parseItem = item => {
if (typeof item == "object") {
if (Buffer.isBuffer(item)) {
var bufferString = `[Buffer ${item.slice(0, 50).toString('hex').match(/\w{1,2}/g).join(" ")}`
if (item.length > 50) bufferString += `... ${(item.length / 1000).toFixed(2)}kb`
return bufferString + ']'
} else if (item instanceof ArrayBuffer) {
return `ArrayBuffer(${item.byteLength})`
} else if (item instanceof Blob) {
return `Blob {size: ${item.size}, type: "${item.type}"}`
} else {
try {
return stringifyAll(item)
} catch (error) {}
}
} else if (typeof item == "undefined") {
return "undefined"
}
return item.toString()
}
window.convertFilePathToUtoolsPayload = files => files.map(file => {
let isFile = fs.statSync(file).isFile()
return {
isFile: isFile,
isDirectory: !isFile,
name: path.basename(file),
path: file
}
})
let parseStdout = stdout => stdout.map(x => parseItem(x)).join("\n")
window.VmEval = (cmd, sandbox = {}) => new VM({
sandbox: sandbox
}).run(cmd)
// The vm module of Node.js is deprecated in the renderer process and will be removed
window.runCodeInVm = (cmd, cb) => {
const vm = createNodeVM()
//重定向 console
vm.on('console.log', (...stdout) => {
console.log(stdout);
cb(parseStdout(stdout), null)
});
vm.on('console.error', stderr => {
cb(null, stderr.toString())
});
let liteErr = e => {
if (!e) return
return e.stack.replace(/([ ] +at.+)|(.+\.js:\d+)/g, '').trim()
}
// 错误处理
try {
vm.run(cmd, path.join(__dirname, 'preload.js'));
} catch (e) {
console.log('Error: ', e)
cb(null, liteErr(e))
}
let cbUnhandledError = e => {
removeAllListener()
console.log('UnhandledError: ', e)
cb(null, liteErr(e.error))
}
let cbUnhandledRejection = e => {
removeAllListener()
console.log('UnhandledRejection: ', e)
cb(null, liteErr(e.reason))
}
let removeAllListener = () => {
window.removeEventListener('error', cbUnhandledError)
window.removeEventListener('unhandledrejection', cbUnhandledRejection)
delete window.isWatchingError
}
if (!window.isWatchingError) {
window.addEventListener('error', cbUnhandledError)
window.addEventListener('unhandledrejection', cbUnhandledRejection)
window.isWatchingError = true
}
}
window.htmlEncode = (value) => {
return String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;")
}
window.hexEncode = text => Buffer.from(text, 'utf8').toString('hex')
window.hexDecode = text => Buffer.from(text, 'hex').toString('utf8')
window.processPlatform = process.platform
window.getQuickcommandTempFile = ext => {
return path.join(os.tmpdir(), `quickcommandTempFile.${ext}`)
}
window.getBase64Ico = async (filepath, compressed = true) => {
let sourceImage, ext = path.extname(filepath).slice(1)
if (['png', 'jpg', 'jpeg', 'bmp', 'ico', 'gif', 'svg'].includes(ext)) {
if (ext == 'svg') ext = 'svg+xml'
sourceImage = `data:image/${ext};base64,` + fs.readFileSync(filepath, 'base64')
if (ext == 'png') return sourceImage
} else {
sourceImage = utools.getFileIcon(filepath)
return sourceImage
}
if (!compressed) return sourceImage
let compressedImage = await getCompressedIco(sourceImage)
return compressedImage
}
let getCompressedIco = async (img, width = 80) => {
let compressedImage = await pictureCompress({
img: img,
width: width,
height: width,
type: 'png',
quality: 1
})
return compressedImage.img
}
window.getFileInfo = options => {
var file
if (options.type == 'file') {
file = options.argvs
} else if (options.type == 'dialog') {
var dialog = utools.showOpenDialog(options.argvs);
if (!dialog) return false
file = dialog[0]
} else {
return false
}
var information = {
name: path.basename(file),
ext: path.extname(file),
path: file
}
if (options.readfile) {
var codec = (information.ext == '.bat' || information == '.ps1') ? 'gbk' : 'utf8'
information.data = iconv.decode(fs.readFileSync(file), codec)
}
return information
}
window.getCurrentFolderPathFix = () => {
let pwd = utools.getCurrentFolderPath()
let pwdFix = pwd ? pwd : path.join(utools.getPath('home'), 'desktop')
return pwdFix.replace(/\\/g, '\\\\')
}
window.getMatchedFilesFix = payload => {
let MatchedFiles = payload
let Matched = cmd.match(/\{\{MatchedFiles(\[\d+\]){0,1}(\.\w{1,11}){0,1}\}\}/g)
Matched && Matched.forEach(m => {
repl = eval(m.slice(2, -2))
typeof repl == 'object' ? (repl = JSON.stringify(repl)) : (repl = repl.replace('\\', '\\\\'))
cmd = cmd.replace(m, repl.replace('$', '$$$'))
})
}
window.saveFile = (content, file) => {
if (file instanceof Object) file = utools.showSaveDialog(file)
if (!file) return false
try {
fs.writeFileSync(file, content)
return true
} catch (error) {
return false
}
}
window.getSelectFile = hwnd => {
if (utools.isWindows()) {
var cmd = `powershell.exe -NoProfile "(New-Object -COM 'Shell.Application').Windows() | Where-Object { $_.HWND -eq ${hwnd} } | Select-Object -Expand Document | select @{ n='SelectItems'; e={$_.SelectedItems()} } | select -Expand SelectItems | select -Expand Path "`;
let result = child_process.execSync(cmd, {
encoding: "buffer",
windowsHide: true
})
return iconv.decode(result, 'GBK').trim().replace(/\\/g, '/');
} else {
var cmd = `osascript -e 'tell application "Finder" to set selectedItems to selection as alias list
if selectedItems is {} then return
set parentPath to do shell script "dirname " & quoted form of POSIX path of (item 1 of selectedItems)
set pathData to ""
repeat with theItem in selectedItems
set pathData to pathData & POSIX path of theItem & linefeed
end repeat
'
`
let result = child_process.execSync(cmd, {
encoding: "utf8",
windowsHide: true
})
console.log(result);
return result ? result.trim() : ""
}
}
window.clipboardReadText = () => electron.clipboard.readText()
window.runCodeFile = (cmd, option, terminal, callback) => {
var bin = option.bin,
argv = option.argv,
ext = option.ext,
charset = option.charset,
scptarg = option.scptarg || "";
let script = getQuickcommandTempFile(ext)
// 批处理和 powershell 默认编码为 GBK, 解决批处理的换行问题
if (charset.scriptCode) cmd = iconv.encode(cmd.replace(/\n/g, '\r\n'), charset.scriptCode);
fs.writeFileSync(script, cmd);
// var argvs = [script]
// if (argv) {
// argvs = argv.split(' ')
// argvs.push(script);
// }
var child, cmdline
if (bin.slice(-7) == 'csc.exe') {
cmdline = `${bin} ${argv} /out:"${script.slice(0, -2) + 'exe'}" "${script}" && "${script.slice(0, -2) + 'exe'}" ${scptarg}`
} else if (bin == 'gcc') {
var suffix = utools.isWindows() ? '.exe' : ''
cmdline = `${bin} ${argv} "${script.slice(0, -2)}" "${script}" && "${script.slice(0, -2) + suffix}" ${scptarg}`
} else if (utools.isWindows() && bin == 'bash') {
cmdline = `${bin} ${argv} "${script.replace(/\\/g, '/').replace(/C:/i, '/mnt/c')}" ${scptarg}`
} else {
cmdline = `${bin} ${argv} "${script}" ${scptarg}`
}
// 在终端中输出
if (terminal) cmdline = getCommandToLaunchTerminal(cmdline)
child = child_process.spawn(cmdline, {
encoding: 'buffer',
shell: true
})
// var chunks = [],
// err_chunks = [];
console.log('running: ' + cmdline);
child.stdout.on('data', chunk => {
if (charset.outputCode) chunk = iconv.decode(chunk, charset.outputCode)
callback(chunk.toString(), null)
// chunks.push(chunk)
})
child.stderr.on('data', stderr => {
if (charset.outputCode) stderr = iconv.decode(stderr, charset.outputCode)
callback(null, stderr.toString())
// err_chunks.push(err_chunk)
})
// child.on('close', code => {
// let stdout = chunks.join("");
// let stderr = err_chunks.join("");
// callback(stdout, stderr)
// })
}