Merge branch 'master' of https://github.com/clouDr-f2e/rubick into master

This commit is contained in:
徐志鹏 2021-07-27 21:19:52 +08:00
commit 129f19bf39
5 changed files with 140 additions and 112 deletions

108
README.md
View File

@ -15,12 +15,16 @@
基于 electron 的工具箱,媲美 utools的开源插件已实现 utools 大部分的 API 能力,所以可以做到无缝适配 utools 开源的插件。 基于 electron 的工具箱,媲美 utools的开源插件已实现 utools 大部分的 API 能力,所以可以做到无缝适配 utools 开源的插件。
之所以做这个工具箱一方面是 utools 本身并未开源,但是公司内部的工具库又无法发布到 utools 插件中,所以为了既要享受 utools 生态又要有定制化需求,我们自己参考 utools 设计,做了 Rubick 之所以做这个工具箱一方面是 utools 本身并未开源,但是公司内部的工具库又无法发布到 utools 插件中,所以为了既要享受 utools 生态又要有定制化需求,我们自己参考 utools 设计,做了 Rubick.
Rubick(拉比克) 是 dota 里面的英雄之一,其核心技能是插件化使用其他英雄的技能,用完即走。非常符合本工具的设计理念,所以取名 Rubick。
## 安装包 ## 安装包
* [Rubick Mac OS V0.0.2](https://github.com/clouDr-f2e/rubick/releases/download/v0.0.2/rubick2-0.0.2.pkg) * [Rubick Mac OS V0.0.2](https://github.com/clouDr-f2e/rubick/releases/download/v0.0.2/rubick2-0.0.2.pkg)
## 支持能力 ## 支持能力
- [x] 支持 uTools 90% API。可直接按照 uTools 文档开发 Rubick 插件
- [x] 支持 uTools github 开源插件。
- [x] 支持远程下载安装插件,支持插件开发者模式 - [x] 支持远程下载安装插件,支持插件开发者模式
- [x] 支持插件分离 - [x] 支持插件分离
- [x] 支持系统命令取色、截屏、帮助 - [x] 支持系统命令取色、截屏、帮助
@ -39,106 +43,42 @@
1. 目前 `Rubick` 插件市场 server 端还没有部署,所以目前看不到插件市场的插件。 1. 目前 `Rubick` 插件市场 server 端还没有部署,所以目前看不到插件市场的插件。
2. 依赖于 `robotjs` dev 环境运行请在 `install` 后执行 `npm run rebuild` 2. 依赖于 `robotjs` dev 环境运行请在 `install` 后执行 `npm run rebuild`
## utools 插件支持 ## 目前支持能力
### plugin.json ### 加载utools生态插件
在你觉得合适的地方新建一个文件夹,并创建 `plugin.json` 文件。这是最重要的一个文件,用来说明这个插件将如何与 `rubick` 集成,最基本的格式如下: `github` 上开源的 斗图 插件举例,要加载斗图插件,只需要将代码 clone下来后复制其 `plugin.json` 进入搜索框即可使用
```json
{
"pluginName": "helloWorld",
"description": "我的第一个uTools插件",
"main": "index.html",
"version": "0.0.1",
"logo": "logo.png",
"features": [
{
"code": "hello",
"explain": "hello world",
"cmds":["hello", "你好"]
}
]
}
```
所有字段和 `utools` 保持一致,这将可以完美使用`utools`的插件生态
## utools api 支持 斗图https://github.com/vst93/doutu-uToolsPlugin
### 事件 <img src=https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cba1eb758180433294c93b59724adefd~tplv-k3u1fbpfcp-watermark.image width=500 />
#### onPluginReady(callback) ### 超级面板
* `callback` Function 长按鼠标右键,即可呼起超级面板,可以根据当前鼠标选择内容,匹配对应插件能力。比如当前选择图片后长按右击,则会呼起上传图床插件:
> 当插件装载成功uTools 将会主动调用这个方法(生命周期内仅调用一次),所有的 `api` 都应该在 `onPluginReady` 之后进行调用。 ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1706cc730f1f46078cb700a445211317~tplv-k3u1fbpfcp-watermark.image)
##### 示例 ### 模板
为了更贴合 `uTools` 的插件能力,需要实现模板功能,模板即是一个内置 UI 样式的功能插件。
```js <img src=https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0b113ad547974699b9c73c28bc09b9b1~tplv-k3u1fbpfcp-watermark.image width=500 />
utools.onPluginReady(() => {
console.log('插件装配完成,已准备好')
})
```
#### onPluginEnter(callback) ### utools 自带的系统命令
* `callback` Function #### 取色
基于 `robot.js` 以及 `iohook` 实现。未使用 C++ 扩展。
> 每当插件从后台进入到前台时uTools 将会主动调用这个方法。 <img src=https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8c3e00e5f08545bcb96613689649b337~tplv-k3u1fbpfcp-watermark.image width=500 />
##### 示例 #### 截屏
```js <img src=https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/18023dab52e1420c9e87362cefddb2a1~tplv-k3u1fbpfcp-watermark.image width=500 />
utools.onPluginEnter(({code, type, payload, optional}) => {
console.log('用户进入插件', code, type, payload)
})
```
更多可以参考 `utools` 文档https://u.tools/docs/developer/api.html#%E4%BA%8B%E4%BB%B6
### 窗口交互
### 本地数据库 #### 全局快捷键
类似于 `utools` 的功能,我们也提供了本地数据库的 api 能力
#### utools.db.put(doc) <img src=https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/62cc424eacac4c9eb178f0e055e87d9a~tplv-k3u1fbpfcp-watermark.image width=500 />
##### 示例
```js
// 创建请求
utools.db.put({
_id: "demo",
data: "demo"
})
// 返回 {id: "demo", ok: true, rev: "1-05c9b92e6f24287dc1f4ec79d9a34fa8"}
// 更新请求
utools.db.put({
_id: "demo",
data: "demo",
_rev: "1-05c9b92e6f24287dc1f4ec79d9a34fa8"
})
```
#### utools.db.get(id)
#### utools.db.remove(doc)
#### utools.db.bulkDocs(docs)
### ubrowser
可编程浏览器
```js
const page = await utools.ubrowser.goto('https://www.baidu.com')
await page.value('#kw', 'uTools');
await page.click('#su');
page.run({
width: 600,
height: 800,
})
```
### 最后 ### 最后
utools过于强大目前还没有完全实现其所有功能不过我们会根据需要不断更新。欢迎小伙伴一起 `pr``star` utools过于强大目前还没有完全实现其所有功能不过我们会根据需要不断更新。欢迎小伙伴一起 `pr``star`
## License ## License
This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/clouDr-f2e/rubick/blob/master/LICENSE) file for details. This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/clouDr-f2e/rubick/blob/master/LICENSE) file for details.

View File

@ -181,7 +181,7 @@ class Listener {
// 锁屏 // 锁屏
ipcMain.on('lock-screen', () => { ipcMain.on('lock-screen', () => {
const lockCommands = { const lockCommands = {
darwin: '/System/Library/CoreServices/"Menu Extras"/User.menu/Contents/Resources/CGSession -suspend', darwin: '/System/Library/CoreServices/ScreenSaverEngine.app/Contents/MacOS/ScreenSaverEngine',
win32: 'rundll32.exe user32.dll, LockWorkStation', win32: 'rundll32.exe user32.dll, LockWorkStation',
linux: '(hash gnome-screensaver-command 2>/dev/null && gnome-screensaver-command -l) || (hash dm-tool 2>/dev/null && dm-tool lock)' linux: '(hash gnome-screensaver-command 2>/dev/null && gnome-screensaver-command -l) || (hash dm-tool 2>/dev/null && dm-tool lock)'
}; };

View File

@ -7,7 +7,20 @@ function createTray(window) {
const appIcon = new Tray(path.join(__static, './rocket.png')); const appIcon = new Tray(path.join(__static, './rocket.png'));
const contextMenu = Menu.buildFromTemplate([ const contextMenu = Menu.buildFromTemplate([
{ {
id: 3, label: "帮助文档", click: () => {
process.nextTick((() => {
shell.openExternal('https://github.com/clouDr-f2e/rubick');
}))
}
}, {
label: "意见反馈", click: () => {
process.nextTick((() => {
shell.openExternal('https://github.com/clouDr-f2e/rubick/issues')
}))
}
},
{type: "separator"},
{
label: '显示窗口', label: '显示窗口',
accelerator: "Alt+R", accelerator: "Alt+R",
click() { click() {
@ -15,23 +28,25 @@ function createTray(window) {
} }
}, },
{ {
id: 4, role: 'quit',
label: '文档', label: '退出'
},
{
label: '重启',
click() { click() {
shell.openExternal('https://muwoo.github.io/rubick-doc/'); app.relaunch();
app.quit();
} }
}, },
{type: "separator"},
{ {
id: 5, label: '偏好设置',
label: '显示窗口',
click() { click() {
window.show(); window.show();
} window.webContents.send('tray-setting');
},
}, },
{ {
id: 6,
label: '关于', label: '关于',
click() { click() {
dialog.showMessageBox({ dialog.showMessageBox({
@ -39,21 +54,8 @@ function createTray(window) {
message: '极简、插件化的现代桌面软件', message: '极简、插件化的现代桌面软件',
detail: `Version: ${pkg.version}\nAuthor: muwoo` detail: `Version: ${pkg.version}\nAuthor: muwoo`
}); });
} },
}, },
{
id: 7,
role: 'quit',
label: '退出'
},
{
id: 7,
label: '重启',
click() {
app.relaunch();
app.exit();
}
}
]); ]);
appIcon.on('click', () => { appIcon.on('click', () => {
appIcon.popUpContextMenu(contextMenu); appIcon.popUpContextMenu(contextMenu);

View File

@ -108,6 +108,7 @@ export default {
ipcRenderer.on('new-window', this.newWindow); ipcRenderer.on('new-window', this.newWindow);
// //
ipcRenderer.on('superPanel-openPlugin', (e, args) => { ipcRenderer.on('superPanel-openPlugin', (e, args) => {
this.closeTag();
ipcRenderer.send('msg-trigger', { ipcRenderer.send('msg-trigger', {
type: 'showMainWindow', type: 'showMainWindow',
}); });
@ -145,6 +146,11 @@ export default {
}); });
config && this.openPlugin(config); config && this.openPlugin(config);
}); });
//
ipcRenderer.on('tray-setting', () => {
this.showMainUI();
this.changePath({key: 'settings'});
});
const searchNd = document.getElementById('search'); const searchNd = document.getElementById('search');
searchNd && searchNd.addEventListener('keydown', this.checkNeedInit) searchNd && searchNd.addEventListener('keydown', this.checkNeedInit)
}, },

View File

@ -26,7 +26,7 @@
<a-list-item-meta <a-list-item-meta
@click="showPannel(item)" @click="showPannel(item, index)"
:description="item.description" :description="item.description"
> >
<div slot="title">{{ item.pluginName }}</div> <div slot="title">{{ item.pluginName }}</div>
@ -37,12 +37,43 @@
</a-list-item-meta> </a-list-item-meta>
</a-list-item> </a-list-item>
</a-list> </a-list>
<a-drawer
placement="right"
:visible="show"
@close="show=false"
width="100%"
>
<div class="plugin-market-desc" slot="title">
<img width="80" :src="currentSelect.logo"/>
<div class="desc">
<h4>{{currentSelect.pluginName}}</h4>
<div class="info">
<div class="actor">
开发者{{currentSelect.author}}
<a-button
v-if="showButton(currentSelect)"
:loading="loading[currentSelect.index]"
@click="download(currentSelect.index, currentSelect)"
icon="cloud-download"
type="primary"
>
获取
</a-button>
</div>
<div>{{currentSelect.description}}</div>
</div>
</div>
</div>
<div class="market-plugin-detail" v-html="readme"></div>
</a-drawer>
</div> </div>
</template> </template>
<script> <script>
import api from '../../../assets/api'; import api from '../../../assets/api';
import {mapActions, mapState} from 'vuex'; import {mapActions, mapState} from 'vuex';
import marked from "marked";
const rendererMD = new marked.Renderer();
export default { export default {
data() { data() {
@ -78,19 +109,68 @@ export default {
showButton(item) { showButton(item) {
return !this.devPlugins.filter(plugin => (plugin.name === item.name && plugin.type === 'prod')).length; return !this.devPlugins.filter(plugin => (plugin.name === item.name && plugin.type === 'prod')).length;
}, },
showPannel(item) { showPannel(item, index) {
this.show = true; this.show = true;
this.currentSelect = item; this.currentSelect = item;
this.currentSelect.index = index;
}, },
...mapActions('main', ['downloadPlugin']) ...mapActions('main', ['downloadPlugin'])
}, },
computed: { computed: {
...mapState('main', ['devPlugins']) ...mapState('main', ['devPlugins']),
readme() {
marked.setOptions({
renderer: rendererMD,
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: false,
smartLists: true,
smartypants: false
});
try {
return marked(this.currentSelect.detail);
} catch (e) {
return '暂无描述信息'
}
}
} }
} }
</script> </script>
<style lang="less"> <style lang="less">
.market-plugin-detail {
img {
width: 100%;
}
height: 430px;
overflow: auto;
&::-webkit-scrollbar {
width: 0;
}
}
.plugin-market-desc {
display: flex;
img {
margin-right: 20px;
}
.desc {
flex: 1;
}
.info {
font-size: 12px;
color: #999;
font-weight: normal;
padding-right: 40px;
}
.actor {
flex: 1;
display: flex;
justify-content: space-between;
}
}
.market { .market {
height: calc(~'100vh - 110px'); height: calc(~'100vh - 110px');
background: #fff; background: #fff;