本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究!

本系列前面的两篇文章《概念篇》、《中心篇》中现已详细阐明晰猎豹跨渠道插件开发所需求的技能,以及针对对不同渠道做的中心模块抽取流程。

本文首要解说怎么接入咱们抽取的中心模块怎么运用在 uTools 渠道的插件开发中,而且介绍一下 uTools 插件开发用到的 API,以及 uTools 插件的发布流程。

项目装备

uTools 为开发者供给了一个东西插件,能够在插件市场直接查找“uTools 开发者东西”装置,项意图新建、运转,发布都在这个插件内操作。

猎豹Cheetah插件开发 - uTools 篇

创立项目

装置完开发者东西后,直接在 uTools 输入框查找翻开开发者东西,点击左下角的新建项目,翻开项目创立面板,依据提示完结项目创立。

猎豹Cheetah插件开发 - uTools 篇

依据官网文档指引,需求为项目新建一个 plugin.json 文件,用于装备项目信息,功用进口等等。
开发者能够围绕 plugin.json 文件创立项目,运用自己了解的构建东西完结项目资源构建,plugin.json 中的 preload 字段指向构建成果中的进口文件即可。

plugin 装备

{
  "version": "1.0.0", // 插件版本
  "preload": "index.js", // 插件功用进口
  "logo": "assets/logo.png", // 插件 Logo
  "platform": [ // 插件支撑的渠道
    "win32", // Windows
    "darwin" // Mac OS
  ],
  "features": [ // 插件运用功用
    {
      "code": "open", // 功用仅有标识,查找项目并运用与指令相关的软件翻开
      "explain": "查找并翻开项目",
      "cmds": [ // 查找这些字符都能够进入 open 功用,其值将作为 action 参数传入后续履行的函数
        "open",
        "git_gui_open",
        "terminal_open",
        "folder_open",
        "set_application",
        "编辑器",
        "Git运用",
        "终端",
        "文件夹",
        "设置项目默许运用"
      ]
    },
    {
      "code": "setting", // 功用仅有标识,翻开插件设置面板
      "explain": "翻开设置面板",
      "cmds": [ // 这俩指令都是翻开设置面板
        "setting",
        "设置"
      ]
    }
  ]
}

设置面板

从上面装备中能够看出,除了 open 功用外,还有一个翻开设置面板功用,这么做的原因是 uTools 的插件办理没有供给现成的偏好设置功用,一些个性化的装备想要用户自己输入时比较费事,所以这边添加了一个指令,用于翻开咱们自己开发的装备面板,装备的数据能够存储在 uTools 供给的存储环境中。

指令面板能够运用恣意结构开发,最终构建成 htmlcssjs 即可经过 uTools 供给的 API utools.createBrowserWindow 翻开窗口。

插一句:uTools 基于 Electron 开发,了解 Electron 的小伙伴应该很容易上手。

下面看一看设置面板翻开今后得样子:

猎豹Cheetah插件开发 - uTools 篇

文件结构

首要的功用文件运用 rollup.js 构建,设置面板运用 vite + vue3,输出到 package 目录下,将 plugin.json 也放置其内,处理好引证联系,插件的项目装备就基本完结了,结构如下:

cheetah-for-utools
├─ lib
│  ├─ common
│  │  ├─ constant.ts
│  │  ├─ core.ts
│  │  ├─ index.ts
│  │  └─ utils.ts
│  ├─ index.ts
│  └─ mount.ts
├─ package
│  ├─ assets
│  │  ├─ empty.png
│  │  ├─ logo.png
│  │  ├─ refresh.png
│  │  └─ type
│  │     ├─ android.png
│  │     ├─ applescript.png
│  │     ├─ dart.png
│  │     ├─ hexo.png
│  │     ├─ javascript.png
│  │     ├─ nuxt.png
│  │     ├─ react.png
│  │     ├─ react_ts.png
│  │     ├─ rust.png
│  │     ├─ typescript.png
│  │     ├─ unknown.png
│  │     ├─ vscode.png
│  │     └─ vue.png
│  ├─ plugin.json
├─ package.json
├─ rollup.config.js
├─ tsconfig.json
├─ types.d.ts
├─ vite.config.ts
└─ window
   ├─ index.html
   └─ src
      ├─ App.vue
      ├─ assets
      │  └─ logo.png
      ├─ components
      │  ├─ app-choose-item.vue
      │  ├─ cell-button.vue
      │  └─ workspace-manager.vue
      ├─ env.d.ts
      ├─ main.ts
      ├─ pages
      │  └─ index.vue
      └─ router
         └─ index.ts

rollup 的装备与中心篇大同小异,最大的区别是,构建成果不能压缩,因为发布时要保证 preload.js 明文可读,不然审阅会不经过,审阅人员会检查插件是否会造成对用户系统的危害。

引进中心模块

中心篇中现已论述了怎么开发、发布插件,而且现已将猎豹的中心模块 cheetah-core 发布到 npm,现在直接在 uTools 插件项目中装置运用即可:

npm install cheetah-core
# or
yarn add cheetah-core

功用完结

设置面板

上面说到设置面板首要是为了让用户进行一些插件功用的设置,首要包括下面这些选项。

工作区设置

东西将在以下工作区目录查找 Git 项目,可装备多个,每个工作区目录能够包括多个 Git 项目。

默许编辑器

open 指令挑选项目默许由此编辑器翻开

Git GUI 运用

git_gui_open 指令挑选项目由此运用翻开

终端

terminal_open 指令挑选项目由此终端翻开

以上这些设置今后经过 utools.dbStorage.setItem 写入本地数据库,便利 open 指令履行时获取运用。

那么怎么翻开设置面板呢?

preload.ts 中添加:

window.exports = {
  open: {...}, // open 指令后续解说
  setting: {
    mode: 'none',
    args: {
      enter: (action: any) => {
        const settingWindow = utools.createBrowserWindow(
          './window/index.html',
          {
            show: false,
            title: '设置',
            resizable: false,
            minimizable: false,
            maximizable: false,
            height: 800,
            width: 500,
            webPreferences: {
              preload: 'index.js',
            },
          },
          () => {
            settingWindow.show();
          }
        );
        utools.hideMainWindow();
      },
    },
  },
}

上面的代码表明在 uTools 输入框中输入 setting 相关指令并运转后,运用 utools.createBrowserWindow 翻开咱们构建的页面,而且载入 preload 文件为 rollup 构建的进口文件。

在进口文件中引进了 mount.ts 文件,其作用是向 window 对象挂载一系列东西函数:

// index.ts
import mount.ts
...
// mount.ts
import {
  chooseFile,
  chooseFolder,
  getValue,
  setValue,
  notice,
  getAllDefaultApp,
  setDefaultApp,
  platform,
  onClearCache
} from './common';
Object.assign(window, {
  platform,
  chooseFile,
  chooseFolder,
  notice,
  getValue,
  setValue,
  getAllDefaultApp,
  setDefaultApp,
  onClearCache,
});

这样在设置面板中就能够直接运用 window 上挂载的函数完结装备了。

查找项目

项目查找的逻辑在中心篇中有详细解说,此处不多赘述,只解说用法。

其实前面 open 功用中界说的指令,触发的查找都一致,只是在查找成果挑选今后履行的操作不同,所以不同的指令进来只需履行相同的查找函数即可。

uTools 为开发者供给了几个插件模板,猎豹首要运用了 list 这个模板完结功用开发。

import cp from 'child_process';
import './mount'; // 在 window 挂载上挂载东西函数 
import {
  filterWithCache,
  filterWithSearchResult,
  updateHits,
  Project,
  ResultItem,
  COMMAND,
  getOpenCommand,
  setProjectApp,
} from 'cheetah-core'; // 从中心模块中引进
import {
  initCore,
  output,
  chooseFile,
  commandMap,
  getAllDefaultApp,
  errorHandle,
} from './common'; // uTools 功用适配的函数
const refreshKeyword = '[refresh]'; // 改写关键字,查找字符串中包括此关键字时会疏忽缓存,从头查找项目
/**
 * @description: 查找项目
 * @param {any} action
 * @param {string} keyword 查找关键字
 * @param {any} callbackSetList 烘托候选列表的回调
 * @return {*}
 */
async function search(
  action: any,
  keyword: string,
  callbackSetList: any
): Promise<void> {
  try {
    initCore();
    const needRefresh: boolean = keyword.includes(refreshKeyword);
    const searchKeyword = keyword.replace(refreshKeyword, '');
    if (!searchKeyword) return callbackSetList();
    let projects: Project[] = await filterWithCache(searchKeyword);
    let fromCache = true;
    // 假如缓存成果为空或许需求改写缓存,则从头查找
    if (!projects.length || needRefresh) {
      projects = await filterWithSearchResult(searchKeyword);
      fromCache = false;
    }
    const result: ResultItem[] = output(projects);
    if (fromCache) {
      result.push({
        title: '疏忽缓存从头查找',
        description: '以上成果从缓存中获得,挑选本条将从头查找项目并更新缓存',
        icon: 'assets/refresh.png',
        arg: searchKeyword,
      });
    }
    if (!result.length) {
      result.push({
        title: `没有找到称号包括 ${searchKeyword} 的项目`,
        description: '请测验更换关键词,回车回来从头查找',
        icon: 'assets/empty.png',
        type: 'empty',
      });
    }
    callbackSetList(result);
  } catch (error: any) {
    errorHandle(error);
  }
}
/**
 * @description: 处理点击成果,假如是从头查找会回来跳过
 * @param {ResultItem} itemData 被点击的条目
 * @return {boolean} 是否需求跳过
 */
function commonSelect(itemData: ResultItem): boolean {
  const { arg, type } = itemData;
  // 查找成果为空的条目被点击则置空输入框
  if (type === 'empty') {
    utools.setSubInputValue('');
    return true;
  }
  // 从头查找时重置查找框内容为 [refresh]+关键字
  const skip = arg !== null && arg !== undefined;
  if (skip) {
    utools.setSubInputValue(`${refreshKeyword}${arg}`);
  }
  return skip;
}
window.exports = {
  open: {
    mode: 'list',
    args: {
      search,
      // 在查找列表内挑选项目并回车今后,将履行此函数,action 为当时履行
      select: async (action: any, itemData: ResultItem) => {
        try {
          if (commonSelect(itemData)) return;
          initCore();
          const { payload }: { payload: string } = action;
          const commandType = commandMap[payload];
          if (commandType === COMMAND.SET_APPLICATION) {
            const appPath: string = chooseFile();
            setProjectApp(itemData.path!, appPath);
            utools.hideMainWindow();
            utools.outPlugin();
            return;
          }
          const defaultAppPath = getAllDefaultApp()?.[commandType] ?? '';
          const command = await getOpenCommand(
            itemData,
            commandType,
            defaultAppPath
          );
          cp.exec(command, { windowsHide: true }, (error: any) => {
            if (error) {
              utools.showNotification(error?.message ?? '不知道过错');
            }
            updateHits(itemData.path!);
            utools.hideMainWindow();
            utools.outPlugin();
          });
        } catch (error: any) {
          errorHandle(error);
        }
      },
    },
  },
  setting: {...}
}

open 下的 mode 字段装备为 list 表明运用 list 模板, args 下为 list 模板在运用时触发的函数,界说如下:

search

uTools 插件子输入框内容变化时调用,第一个参数是描绘当时翻开 open 功用的指令,第二个参数是输入框的值,第三个参数为 Callback 函数,将列表依照文档要求传入即可烘托。

猎豹Cheetah插件开发 - uTools 篇

select

search 回来的列表中挑选项目后回车时履行,第一个参数与 search 一致,第二个参数为被挑选项意图详细信息。

明白了 list 模板的运用后,就能开端功用的开发了。

首先在 search 中运用中心模块供给的项目查找函数,filterWithSearchResult,履行查找后成果会被缓存,下一次运用时将直接调用 filterWithCache 从缓存中挑选成果,为了避免项目变动后查找成果不精确,添加了一个手动改写的候选项,最终将查找成果经过 Callback 回来给 uTools 烘托列表。

猎豹Cheetah插件开发 - uTools 篇

在查找成果中挑选目标项目回车后履行 select 函数,依据 action.payload 的值以及 itemData 履行不同的操作:

  • itemData.typeempty 时,查找成果为空,挑选空提示项目时仅将子输入框内容置空,能够从头输入关键字查找。
  • itemData.arg 不为空时,将子输入框内容改为 [refresh]${itemData.arg},触发新一轮查找,因为关键词中包括改写关键字,将直接调用 filterWithSearchResult 疏忽缓存直接查找项目。
  • 排除以上两种状况后,将依据 action.payload 的值运用关联的运用翻开项目。

运用指定运用翻开项目

Mac OS 下运用指定运用翻开文件或许文件夹十分便利,运用以下指令即可:

open -a 运用称号 项目绝对途径
# 例
open -a "Visual Studio Code" /Users/***/Documents/works/test

Windows 下可用的软件比较少,编辑器可用的有 VSCodeWebStormSublime,其余未做更多测验,Git GUI 仅支撑 Fork,终端仅 CMDPowerShell 可用,软件的运用指令为:

"运用途径" "项目途径"
# 例
"D:\Program Files JetBrains\WebStorm 2021.1.1\webstorm.exe" "D:\works\test"

Windows 下终端运转需求经过特别处理:

/**
 * @description: windows 下终端指令处理
 * @param {string} realAppPath 运用途径
 * @param {string} projectPath 项目途径
 * @return {*}
 */
function windowsTerminal(realAppPath: string, projectPath: string): string {
  if (/powershell/i.test(realAppPath)) {
    return `start powershell -NoExit -Command "cd ${projectPath}"`;
  }
  if (/cmd/i.test(realAppPath)) {
    return `start /D ${projectPath}`;
  }
  throw new Error('109');
}

编辑器翻开项意图优先级规则为:缓存文件内装备的项目编辑器 > 缓存文件内装备的项目类型编辑器 > 设置面板内装备的默许编辑器。
在没有装备任何编辑器时,兜底的是文件浏览器,Mac OS 下为 Finder,Windows 下为 explorer.exe

运用 Git GUI 与终端时也会从本地存储中读取设置面板中装备的相关运用。

铲除缓存

设置面板最下方有一个铲除缓存按钮,能够将缓存文件删去,中心模块中也供给了相关函数,在 uTools 项目中直接调用即可,调用成功后运用 uTools 供给的告诉方式提示铲除成功。

export async function onClearCache(): Promise<void> {
  try {
    initCore();
    await clearCache();
    notice('缓存铲除成功');
  } catch (error: any) {
    errorHandle(error);
  }
}

为项目设置软件

猎豹能够为每个项目装备编辑器,在缓存的项目描绘中有个字段是 idePath,将编辑器称号或许途径装备在此即可。 但是每次装备都要翻开缓存文件找到项目,仿制编辑器称号张贴十分费事,所以直接装备了设置项目运用的指令,此指令也属于 open 功用下,在项目查找列表挑选项目后,直接调用 uTools 供给的文件挑选 API utools.showOpenDialog,获取到运用信息后直接设置,便利快捷,白叟小孩都会用。

export function chooseFile() {
  return utools.showOpenDialog({
    filters: [{ extensions: ['app', 'exe', 'lnk'] }],
    properties: ['openFile'],
  });
}
...
const { payload }: { payload: string } = action;
const commandType = commandMap[payload];
if (commandType === COMMAND.SET_APPLICATION) {
  const appPath: string = chooseFile();
  setProjectApp(itemData.path!, appPath); // 此函数由中心模块供给
  utools.hideMainWindow();
  utools.outPlugin();
  return;
}
...

过错处理

中心模块中函数会在某些状况下抛出过错,过错信息是以 code 及映射 Map 方式供给,便利后续做国际化,在获取到提示文本后,经过 uTools 供给的告诉 API utools.showNotification 提示用户。

import { ErrorCodeMessage } from 'cheetah-core';
export function notice(message: string) {
  utools.showNotification(message)
}
export function errorHandle(error: any) {
  const errorCode: number = error.message;
  notice(ErrorCodeMessage[errorCode]);
  utools.hideMainWindow();
}

提交审阅

完结功用开发后,后边的工作就是发布到插件市场啦~

发布流程如下:

  1. 构建项目
  2. 翻开 uTools 开发者东西
  3. 挑选需求发布的项目
  4. 选中“发布”选项卡
  5. 点击右下角的“发布版本”按钮
  6. 填写版本号,承认打包 plugin.json 所在目录
  7. 填写版本阐明、插件介绍,上传插件截图
  8. 提交审阅

⚠️ preload.js 需求坚持明文可读,不然审阅直接不经过。
提交今后就能够去 uTools 开发者交流群催办理员审阅了,哈哈哈哈哈。

小结

至此 Cheetah for uTools 的开发就完结了,当时版本现已更新到 1.2.0,欢迎各位看官前往下载体验,Cheetah 项目现已开源,假如对插件安全方面有疑虑的能够逐行检查。
Cheetah 插件只运用了 uTools 很小一部分能力,还有很多强壮的功用没有用到,有兴趣的朋友能够移步开发者文档。
下一篇将介绍猎豹 for Alfred 适配中心模块的过程,敬请期待~