Skip to content

Latest commit

 

History

History
480 lines (338 loc) · 28.3 KB

LOG.md

File metadata and controls

480 lines (338 loc) · 28.3 KB

参考

npm install --global --production windows-build-tools@4.0.0

难点

需要 C++ 实现的功能

  • 监听剪贴板变化(需要外部传入一个回调,当剪贴板变化时触发该回调函数),同时提供当前剪贴板的文件类型;同时也需要提供一个卸载监听的方法,在 App 关闭前卸载监听;
  • 将多个文件路径粘贴到剪贴板中(对于不存在的文件路径会怎样?);
  • 监听当前活动窗口的变化,比如,打开了 A 应用,然后打开剪贴板,选中某条内容后,关闭剪贴板,同时激活 A 应用。
  • 将剪贴版中的内容自动回填到当前活动应用中。

如何监听剪贴板

C++ 插件提供监听剪贴板变化的方法,需要外部传入一个回调,当剪贴板变化时触发该回调函数;同时也需要提供一个卸载监听的方法,在 App 关闭前卸载监听。

如何展示剪贴版中的内容

TODO

  • 100% -- 更新设置时,同步更新 settings.json 文件。
  • 100% -- 编辑 "激活热键" 时,禁用当前热键,防止误触关闭剪贴板。
  • 100% -- 设计数据库 API:查询、保存、删除、更新。
  • 100% -- 完善监听剪贴板 SDK:监听剪贴板变化、获取当前剪贴板类型。
  • 100% -- 粘贴到活动应用中。
  • 100% -- 原生模块打包问题及类型提示。
  • 100% -- 根据当前系统主题切换不同托盘图标。
  • 100% -- 首次启动时,首屏加载 loading 替换。
  • 100% -- fix: 过期数据清理,关闭应用前,清理过期的数据 每次新增数据成功后,清理一次过期数据。
  • 100% -- fix: 开机启动配置始终开启,无法关闭。
  • 100% -- feat: 记录文件的复制,并实现文件的粘贴。
  • 100% -- 如何检查更新,以及更新到最新版本。
  • 0% -- 功能测试。
  • 100% -- 图标更换,尽量不要中间留空,最好是实心的。
  • 0% -- 接入 husky。
  • -- fix: 某些系统应用获取不到当前应用程序,例如:计算器。
  • -- pref: 图片存储空间优化,改用 Buffer 存储而不是 Base64 字符串。
  • -- feat: 接入日志管理,记录运行日志。

log

11/22

  • 使用 pnpm 打包时,总是报错,最后选择使用 yarn,打包就成功了!

11/19

  • electron-rebuild 打包报错 ModuleNotFoundError: No module named 'distutils'

    # Solved
    python3 -m pip install --upgrade setuptools

10/07

  • 剪贴板 SDK

    • watch:开始监听

      function watch(
        callback: (
          type: 'Text' | 'Image' | 'File',
          data: string | string[],
          source: string
        ) => void
      ) {
        // 开启一个线程监听剪贴板变化,每当发生变化时
        // 获取当前剪贴板类型(type)、数据(data)、来源应用(source:应用程序 exe 文件的路径)
        // 将这三个作为回调的入参传入
        const type = ''
        const data = ''
        const source = ''
        callback(type, data, source)
      }
    • unwatch:停止监听

      function unwatch() {}
    • ``

09/28

  • 从剪贴板上获取到的纯文本能直接粘贴到窗口中,但是复制的文件无法粘贴到文件夹中。在考虑先不实现文件的复制了,初版只实现文本和图片的粘贴,那这样基本功能就已经实现完毕了。
  • 关于活动窗口的监听,之前是想着需要开启一个线程一直监听活动窗口的变化,但其实用不着这样。在打开剪贴板之前,获取一下当前的活动窗口就可以了,记录一下窗口的句柄,若要进行粘贴操作时,根据记录的句柄重新激活该窗口,然后就是模拟粘贴操作。

09/24

  • 昨天遇到一个问题时,当前剪贴板的来源进程是应用内部的后台进程。今天找到一个解决方案,由于只有应用进程才会有窗口,所以可以循环获取当前后台进程的父进程,直到当前进程有窗口时,说明该进程是一个应用进程。完美解决这个问题。同时还解决了系统进程无法获取到应用的问题。现在就算打开任务管理器也是能获取到应用名称的,Nice!
  • TODO 不过有一个 BUG,关闭窗口时,也会触发一次剪贴板变化,不知道为啥。
  • 得到应用可执行文件的路径后,可以用 extract-file-icon 这个包获取对应可执行文件的图标,返回的结果是一个 Buffer 对象,需要转成 base64 格式。这个包是五年前的,现在依旧很实用!

09/21

  • C++ 实现剪贴板监听小工具,每当剪贴板变化时,输出剪贴板的类型
  • C++ 实现监听当前窗口句柄,每当窗口句柄变化时,输出窗口句柄所在程序的名称

09/17

  • 数据库相关 API 设计与实现。

09/09

  • 先给数据库的 API 写单元测试,因为有分页查询,而且还能删除,如何保证删除后,分页查询后到结果还不会出错。还有就是当数据库更新后,再次打开剪贴板后,需要重新触发一次查询,但是这样可能会导致上次关闭剪贴板前的状态丢失。要不就是每次打开剪贴板后,都重制状态(选中的记录重置为第一项)。

09/07

  • 发现一个新玩法,通过 v-model 绑定的属性,传给子组件后,在子组件内部是可以直接更新这个值的。
  • 修改设置后,同步更新到 settings.json 文件中,已经完成了。

09/06

  • 关于 settings 的更新,考虑到在任务栏里也能更新一些配置,以及系统主题更新后,需要通知剪贴板更新。

08/28

  • 信息面板的展示算是完成了,不过有点不太好,等后续功能实现完了,再优化叭。

08/27

  • 菜单显隐的问题在 Windows 系统上不是很明显,可能在 Mac 系统上,鼠标右键的判定有些许不同导致的。
  • 因为是通过一个 Tooltip 组件实现的菜单,还需要跟随当前选中项,在右侧展示,于是需要手动控制显隐,必不可少就需要用到 v-click-outside 指令。指令加在哪个元素上也是一个问题,直接加在 Tooltip 上不会触发,于是就用一个 DIV 把 Tooltip 组件包裹起来,然后把指令绑定在这个 DIV 上,果然可以!
  • 今天突然想把输入框的默认光标样式给替换了,研究了一番:在内容不超出 Input 框宽度的前提下,完全可以用一个 DOM 元素用来模拟光标。而当内容超出 Input 框宽度时,移动光标时,由于 Input 内部会自动滚动内容,导致模拟光标的位置无法计算,实现不了。但是,又想到一招,可以用一个开启 contenteditable 的 DOM 元素用来模拟 Input,因为这个会有滚动条,所以也就能得到滚动的距离了,感觉可行,明天试试。

08/26

  • 可以通过鼠标右键弹出菜单列表,删除当前项;也可以通过 Tab 键激活菜单,UpDown 选择菜单项,然后点击 Enter 后出发菜单项对应的事件。
  • 菜单的显隐有点问题,逻辑有点乱乱的,得重新理一理。

08/25

  • 列表的展示,以及选中时的展示完成了。
  • 下一个功能就是删除当前选中项。

08/21

  • 文件路径过长时,展示头部和尾部,中间处展示省略号,用 JS 创建一个元素,然后二分分割字符长度,再与当前容器宽度做对比,直到小于当前元素宽度为止。

08/18

  • 用模拟数据把搜索功能完善了,以及展示当前选中的内容,还差文件类型的展示,需要整点图标完善一下。
  • 关于文件夹+文件的组合,暂定通过轮播图的形式展示。

08/14

  • 由于筛选时的文件类型对应:文件、文件夹和文件夹 + 文件,这三种类型都算作文件类型,于是就作为文件类型的下的三个子类型进行处理。

08/13

  • 添加了关于颜色校验的单元测试用例,以及把之前写的日期用例给分组了一下。
  • 关于查询逻辑的数据联动写了写,本来感觉用不上 Pinia 的,结果还是得用,多个组件间需要共享数据状态。
  • 引入了生成模拟数据的插件 faker-js,之前用过另一个 MockJS,但是已经五年没人维护了,于是就换了这个。参考官方文档生成了列表的模拟数据,还算简单。
  • 翻了翻,一个月前关于剪贴板类型的设计,发现现在多了一些类型,得重新设计了。但是数据库的结构应该大致不变,某些字段可能需要变动。

08/12

  • use hook snippet template:

    {
      "export use hook": {
        "prefix": "euh",
        "body": ["export function $TM_FILENAME_BASE() {", "\treturn", "}", ""],
        "description": "export use hook"
      }
    }
  • 新增了 Color 类型的图标展示和预览展示,还需要对颜色进行检测,判断是否是一个合法的颜色值,问了问 GPT,结果头一回知道还能用 Option 构造函数判断一个关键字是否是内置的颜色值。

    function isColorNameValid(colorName) {
      const s = new Option().style
      s.color = colorName
      return s.color !== ''
    }

    方法是有了,明天写写测试用例,测一下这个方法。

08/11

  • 研究了一下如何将剪贴板中选中的内容回填到当前活动活动窗口中,使用 C++ 内置的 SendMessage(hwnd, WM_PASTE, 0, 0) 事件不生效(有可能是不在同一个进程中?),然后又查到一种方案,手动触发 Ctrl + V 倒是能实现粘贴。好在能实现就行,(不过自动回填的前提是,需要判断当前窗口是否有光标聚焦,在考虑要不要判断🤔),后续可以再优化。同时还需要开启一个线程监听当前活动窗口,当需要回填时,聚焦该活动窗口。
  • 全局语言切换倒腾了好久,明明语言已经切换了,但是视图就是不更新,把 vue-i18n 的官方文档看了又看,结果还是没用。最后突然想到 t 方法使用的地方不太对,不能用 ref 定义变量,应该用 computed 定义变量,这样才不会丢失语言的响应式,麻了,真是个笨比 🙃。

08/07

  • 改了改配色(浅色主题还是白底好看),加上了设置面板,用一个自底向上的抽屉组件来实现。设置面板打开的时候,需要禁用全局快捷键,以及通过 ESC 键进行一些快捷操作时,还要选择合适的时机去关闭整个剪贴板,得想想怎么实现比较好,感觉通过一个栈去记录当前的浮层,应该可以实现。
    • 研究了一下 element-plus 里如果弹出多个弹窗时,点击 Esc,只会关闭最上层的弹窗。发现实现逻辑好复杂,看不太懂 [捂脸],明天再研究一下,如果再看不懂就换个方法。
  • 切换到系统主题后,修改当前系统主题也会同步更改剪贴板的主题色。
  • 设置之间的联动,通过在设置面板里修改主题,同步更改整个应用的主题,而且在系统托盘里也能修改。在想需不需要支持在配置文件中修改主题后,也能实现同步?
  • 把配置面板中的一些配置完善了一下,就是配置有点少,需要调整一下排版,怎么让它看起来好看一点。

08/06

  • 在 Mac 电脑上可能由于显示器分辨率很高,所以图标看起来贼清晰,然而在 windows 系统上,有些图标就糊糊的....
  • 大致知道原因了,因为导出 SVG 的时候,有些图标的宽高比不是 1:1,导致在一个正方形容器里撑开展示的时候,会有部分比例失调,重新导出一个宽高比为 1:1 的 SVG 应该就可以了。(重新导出了一下,还是有点糊糊的,感觉还是显示器的问题,单独针对不同的图标设置了宽和高,看着还可以,木得办法了...)
  • 底部快捷键提示组件做了一下,还差一个 logo,以及设置面板的打开逻辑。

08/05

  • 周日开始着手自己画图标,今天下班回来就给六个图标画完了,嘿嘿,贼好看~

08/03

  • 获取新数据时,直接将整个列表重新排序,这样处理比较方便点。
  • 关于内容区的展示,也按照类型进行分别展示,主要分为:纯文本、图片、颜色、文件、文件夹、文件列表。
  • 而在列表里也需要根据类型在前面加一个图标作为区分,翻了翻图标库,找到几个比较满意的,但是风格不太一致,在想要不要自己重新画一批图标。

08/02

  • 按下 UpDown 两个按键时,显示 el-scrollbar 滚动条,是通过额外追加 class 覆写样式实现的,导致没有动画效果,有些生硬。而原生的样式是通过鼠标移入和移除触发的,并且还有渐变动画。想到一个妙招:通过 dispatchEvent 手动触发元素上绑定的 mousemovemouseleave 事件,这样就可以和原样式保持一致,而且还不用写多余的样式,完美!
  • 加上了无限滚动,踩了一个坑:el-scrollbarv-infinite-scroll 同时使用时,指令不能直接写在 el-scrollbar 上,需要在内部新建一个元素,在这个元素上添加指令,而且还不能开启 overflow: auto

08/01

  • 尝试着给 date.ts 下的所有日期校验函数,写了些单元测试用例,感觉挺有意思的,越写越上头,嘿嘿。
  • 将数据按照创建日期进行分组展示,通过 UpDown 两个按键控制聚焦的项。
  • 浅色主题的背景色和边框颜色调整了一下,纯白色太刺眼了,稍微灰一点点,柔和一些。

07/31

  • 输入框自动聚焦,以及下拉菜单快捷键触发。

07/29

  • 安装 dayjshotkeys-js

    pnpm add -D dayjs hotkeys-js

07/28

  • 把界面布局整了整,tailwindcss 用的是越来越顺手了,以及两种主题的色调。

07/25

  • 周末浅浅地学习了 TDD 的相关知识,发现在项目里是可以用单元测试的,首先得把整个项目设计好。根据不同的功能拆分出一个个单独的模块,然后给这些单独的功能模块编写单元测试,这是可行的。在正式开发前,根据要实现的功能先写单元测试,然后再开发功能模块的具体实现。

07/20

  • 起初发现 Raycast 打开后,面板内的输入框居然也能聚焦,并且后面的浏览器地址栏中的光标依然也在,起初以为是 Raycast 做了啥处理,后来试了试:点击微信聊天输入框后,再打开 Raycast ,光标就消失了,打开终端也是如此。那估计就是 Chrome 做了啥处理。还有一个更呆的,由于是在 Windows 虚拟机里开发,打开 Raycast 在本机系统里,俩都不是一个系统,我还搁那纳闷呢,居然还能这样,我真是个憨憨 😅。
  • 测试 C++ 打包后的模块,总是需要启动 Electron 应用,后来发现,直接运行一个 node 脚本也是能测试的,这样就简单多了。
  • 另外,陷入了一个误区,查资料搜索功能实现的代码时,总是想着直接用 node-addon 实现,但是一步到位的话,每次都得重新打包,然后测试,就很麻烦。于是想了想,先通过原生 C++ 实现,然后再去研究 node-addon 的版本,主要功能逻辑还是需要通过 C++ 实现,node-addon 做的只是处理参数:传入参数和返回参数,基本上没啥需要特别处理的逻辑。
  • 需要实现选中某个记录后,自动填充到当前光标聚焦的应用中。

07/17

  • 居然可以将 Ctrl + C 录制为快捷键,嘶...,这样会导致 Ctrl + C 不会触发复制。
  • 默认的快捷键由 Alt + Shift + C 改为了 Alt + V,更方便一点。
  • 昨天看书《微前端的设计与实现》里提到了一种架构设计:前端层、API 层、持久层。然后联想到最近在预研这个剪贴板功能实现的时候,大多数都是通过主进程 ipcMain 和渲染进程 ipcRender 间进行通信,以及需要用到 IndexedDB 去存储剪贴板的历史记录,刚好和这种设计不谋而合。前端层对应的就是页面,持久层就是数据库,对应 IndexedDB,而 API 层就可以作为主进程和渲染进程间通信的抽象,以及前端层需要操作数据库的抽象。所以需要重点设计这个 API 层,下列操作都通过 API 层去实现:
    • 主进程与渲染进程通信(例如,获取/更新全局设置)
    • 操作数据库(查询/更改/删除/清空数据库)

07/16

  • 在 Word 中图片也不会变大,不过有一点就是,粘贴在 Word 中后,图片的尺寸默认会变大一点,导致再从 Word 中复制图片时,图片体积会增大很多。关于 Office 系列应用的复制粘贴在后续版本再实现叭,现在暂不考虑了。
  • 在配置中自定义录制全局快捷键,借鉴 VSCode 中快捷键的处理逻辑:
    • 按下某些键时,记录这些键到一个 Set 集合中,防止重复
    • 松开所有按键后,再按下新的键时,清空上一次记录的集合
    • 输入框聚焦时,移除所有全局快捷键
    • 输入框回车时,更新全局快捷键
    • 输入框失焦时,重新注册所有快捷键
  • Alt + D 触发不了,也没法录制,很怪!
  • 添加 eslint 配置
  • 想加一个单元测试,但是看了看 electron 官方推荐的好像有点麻烦。如果实在要搞的话,就用官方推荐的再加上 Vitest 去做测试。不过首要前提是,要写好测试用例,打算先写一个文档版的,后续无论做不做单元测试,都以这个文档版的为准。

07/15

  • 研究了好久,怎么减少复制后图片的体积。保存为二进制流也试了,最后发现同一张图片重复复制后,图片的体积稳定在一个 Copy 后的值左右,好奇怪!将从剪贴板获取到的图片(这时图片已经变大了)再写入剪贴板时,图片就不会增大了!感觉可能是,新图片的编码符合 electron 内部的要求,所以不会再对其进行重新编码了。试了一下,将剪贴板上的图片粘贴到飞书文档里,图片也不会变大。明天再用 Word 试一试,如果图片也不变大了,那就说明从剪贴板读取图片后,新图片 base64 的编码就已经符合要求了,后续再写入剪贴板时,就不会在对其进行编码了。

07/14

  • Electron 将多个文件复制到剪贴板,试了好多种方法,都不太行。于是该用通过 C++ 实现,无敌的 C++ !!!
  • 以及从剪贴板中获取文件路径时乱码了,又是通过 C++ 处理的。
  • 遇到一个问题,将 base64 写入剪贴板后,回导致图片变大。得想办法处理一下。

07/13

  • 剪贴板格式对标 Raycast 再新增两种:Link、Color。

  • 数据库表设计:设计一个主表记录一些公有属性字段,以及主键,还有若干不同类型的其他表,针对不同类型记录对应类型的特殊字段。

    • 查询:

      • 全类型查询:按关键词 keyword 查询,首先查询 ClipboardTable 表,查询条件为 typeText/File/Link/Color 的数据,然后再根据 type 查找对应的表。

        • 如果是 typeText,根据主键 id 查找表 TextTable,且字段 content 匹配关键字 keyword
        • 如果是 typeFile,根据主键 id 查找表 FileTable,且字段 path 匹配关键字 keyword
        • 如果是 typeLink,根据主键 id 查找表 LinkTable,且字段 content 匹配关键字 keyword
        • 如果是 typeColor,根据主键 id 查找表 ColorTable,且字段 content 匹配关键字 keyword
      • 锁定类型查询:根据 type 查找对应的表,且对应字段匹配关键字 keyword

    • 新增:

      • 先在 ClipboardTable 表中新增一条记录,获取主键后,再根据 type 向对应的表中插入数据。
    • 删除:

      • 先删除对应类型表中的记录,再删除 ClipboardTable 表中的数据。
    • 修改:

      • 选中某条记录时,将当前记录的创建时间更新为当前时间。
    • ClipboardTable,主表,记录主键和一些公有属性字段。

      字段名 类型 描述
      id Number 主键,自增
      type String,可选值:Text | File | Image | Link | Color 类型
      application StringNull 来源应用
      createTime String 创建时间,格式为 yyyy-MM-DD HH:mm:ss
    • TextTable,记录 type 为 Text 类型的数据

      字段名 类型 描述
      id Number 外键,来源自 ClipboardTable
      type String,值为固定值 Text 类型
      content String 文本内容
      characters Number 字符数
    • FileTable,记录 type 为 File 类型的数据

      字段名 类型 描述
      id Number 外键,来源自 ClipboardTable
      type String,值为固定值 File 类型
      subType String,可选值:file | folder | folder,file 类型
      path String | String[] 文件路径,例如 ~/x/xx...src/code
      size String 文件大小,例如 16 KB
    • ImageTable,记录 type 为 Image 类型的数据

      字段名 类型 描述
      id Number 外键,来源自 ClipboardTable
      type String,值为固定值 Image 类型
      url String 图片的 URL,Base64 格式
      dimensions String 图片尺寸,例如 200×400
      size String 图片大小,例如 20 KB
    • LinkTable,记录 type 为 Link 类型的数据

      字段名 类型 描述
      id Number 外键,来源自 ClipboardTable
      content String 文本内容
      characters Number 字符数
    • ColorTable,记录 type 为 Color 类型的数据

      字段名 类型 描述
      id Number 外键,来源自 ClipboardTable
      content String 文本内容

07/12

  • 设置的配置文件借助 electron-store 存储在 (app.getPath('userData'))下的 settings.json 中。

    // settings.json
    {
      // 开机启动
      "openAtLogin": true,
      // 主题
      "theme": "system", // "light" or "dark"
      // 语言
      "language": "zh_CN", // "en_US"
      // 最大保留天数
      "maxRetainDays": 7, // 30 or 90 or 365 or Infinity/Never
      // 激活快捷键
      "shortcutKey": "Alt+Shift+C"
    }
  • 预研:

    • 设置背景高斯模糊(不能设置全局背景色,否则模糊不生效)。
    • 任务栏右键菜单动态切换主题,改个属性就能实现,简简单单 ~
    • 借助 vue-i18n 实现语言切换。
  • 剪贴板记录存储,主要分为三种格式:

    • 纯文本:

      {
        "data": "abcdefg",
        "type": "Text",
        "Characters": 234
      }
    • 文件:

      {
        "data": null,
        "type": "File",
        "Path": "~/xx/xxx...src/demo.png",
        "File size": "16 KB"
      }
    • 图片:

      {
        "data": "data:image/jpeg;base64...",
        "type": "Image",
        "dimensions": "200×400",
        "Image size": "33 KB"
      }
  • 因为涉及到大数据的存储,使用 JSON 的话,不利于查询,就查了解决方案,找到两个库:

07/10

  • 系统托盘需要展示的菜单栏功能:
  • 如何删除记录。

07/04

  • 试了一下,直接用 Base64 格式展示,图片大小也不是很大,而且方便存储。刚好上次预研,看了一下如何把数据存储在本机上,找到一个库 electron-store,可以以 JSON 的格式记录,这样刚好记录 Base64 格式。
  • 关于历史记录存储在本机上,还有需要考虑:存在哪个目录下,以及是否按当前时间拆分文件夹(具体到日应该就行)因为有一个配置,当前记录最大留存多久,刚好可以根据天来判断。

07/03

  • 看了看 Raycast 中 Clipboard 的处理逻辑,如果是复制的某个文件,则直接引用这个文件的路径,进行展示什么的。如果文件被删除了,Clipboard 中也会同步删除剪贴板历史记录中的这个文件。
  • 重点工作就是:怎么记录图片以及展示。

07/02

  • 研究了一下主进程如何和渲染进程通信,试了一下把复制到的文本显示在渲染进程中,嘿嘿,效果不错。不过只能在 windows 虚拟机里开发,有些许麻烦,不过倒也还凑合。
  • 纯文本展示还得考虑转义字符的问题,图片还不知道怎么展示为好。今天搜了搜解决方案,倒是可以转成 Base64 的格式,但是如果一个图片很大,转换后的结果就更大了..... 以及如何展示缩略图
  • 文件的展示,之前想着直接引用复制的文件路径,但是如果源文件被删除了,那咋办?

07/01

  • 本来想弄一个工作区的,结果死活弄不好,不是这报错就是那报错,算了,放弃了.....
  • 格式化 C++ 代码配置 clang-format config