Eagle 插件是什么

一句话定义

Eagle 插件就是一个装进 Eagle 里的「网页程序」。它用 HTML、CSS、JavaScript 写成,跑在 Eagle 内置的 Chromium 渲染环境里,同时通过 eagle 这个全局对象拿到宿主应用的能力——读取用户的素材库、在检查器面板里插一块自己的界面、弹出原生对话框。

理解这一点很关键:插件不是「网页」,但它就是网页。你熟悉的 DOM、CSS、事件冒泡、fetch,在插件里全都照常工作;额外多出来的,是一套专属于 Eagle 的 API。

运行时环境:到底跑在什么上面

这句「基于 Web 技术」需要展开成三个具体数字,否则开发时容易做出错误假设。

  • Chromium 107:意味着支持 structuredClone、顶层 await、新的 Array.prototype.at()、CSS 的 color-mix()、以及 :has() 选择器这类近两年才普及的特性。也意味着更老的特性(比如某些 Array.prototype.group() 提案)不可用。
  • Node 16:意味着 fs/promises 可用,AbortController 已内置,node: 前缀的模块引入需要留意版本写法。要注意的是 Node 16 已进入维护末期,如果你依赖某个要求更高 Node 版本的包,运行时可能报错。
  • 无 CORS 限制:fetch 到任意站点都能通,不必自建代理。这在对接第三方 API 时省事,但反过来也是审核重点——你的插件连了哪些外部地址,都会在安全与隐私审核里被逐条核对。

因为是 Node 环境,所以 Web 标准 API 和 Node API 混在同一个命名空间里:require 和 document 在同一个文件里共存,Buffer 和 JSON 平级。这不是设计失误,是刻意的取舍:让插件开发者不用在「能写前端逻辑」和「能读写文件系统」之间二选一。

和普通网页到底差在哪

差异集中在四处,而且每一处都不是「限制」,是「赋能」。

第一,你能碰到用户的本地文件。 浏览器里 fs.readFile 会被沙箱直接拒绝;插件里 require('fs') 正常工作,因为插件进程跑在 Node 环境里。这意味着你可以做批量重命名、读取 EXIF、调用命令行工具这类浏览器里不可能做的事。

第二,你能读写素材库本身。 普通网页拿不到 Eagle 的一切;插件里 eagle.item.getSelected() 一行就能拿到用户当前选中的所有素材,包括 id、标签、评分、文件路径。这是 Eagle 插件真正的价值所在。

第三,不受跨域限制。 官方文档明确写了:Eagle Plugin API 不受跨域限制(CORS)的影响,可以访问任何网址。你可以放心地从自己的后端拉数据,不用折腾服务端 CORS 头。

第四,不需要考虑浏览器兼容性。 插件运行时基于 Chromium 107 和 Node 16,你可以直接用最新语法——可选链 ?.、空值合并 ??、fs/promises——不用写 Babel,不用做 polyfill 表。

// 同一个文件,两个世界
const items = await eagle.item.getSelected();   // 只有插件里能写
console.log(items[0].filePath);                 // 拿到本地绝对路径
const fs = require('fs');                       // Node 环境直连文件系统
fs.readFile(items[0].filePath, (err, buf) => { /* ... */ });

插件能做的事,按 API 域划分

官方把插件能力切成几个清晰的模块。知道有哪些域,比知道有哪些方法更重要——因为遇到需求时,你可以先判断它属于哪一类:

能力域 入口对象 典型用途
素材(项目) eagle.item 读、改、增、删素材
文件夹 eagle.folder 建目录、改名、移动
标签 eagle.tag、eagle.tagGroup 标签管理、合并、分组
资源库 eagle.library 当前库信息与切换感知
窗口 eagle.window 尺寸、置顶、全屏、透明度
应用 eagle.app 版本、主题、系统路径、文件图标
对话框 eagle.dialog 系统级打开/保存/询问
通知 eagle.notification 角落吐司提示
右键菜单 eagle.contextMenu 原生菜单
剪贴板 eagle.clipboard 读写文本、图片、文件
日志 eagle.log 写进 Eagle 软件日志
事件 eagle.onXXX 生命周期与状态变化回调
事件之外 eagle.os、eagle.screen、eagle.shell、eagle.drag 系统信息与桌面集成

还有一块是「第三方能力」:eagle.extraModule 下面挂着 FFmpeg、AI SDK、AI Search 三个内置扩展模块,用于多媒体编解码和 AI 能力。

一次成功的插件开发,五步

  1. 装好 Eagle 并确认版本。 到 eagle.cool/download 下载最新版。插件 API 的新功能常常卡版本线(比如 item.addComment() 需要 4.0 build22),旧版本上调用会直接失败。
  2. 从插件面板创建骨架。 点工具栏「插件」→「开发者选项」→「创建插件」,选类型(窗口/背景服务/格式扩展),选个目录。这一步会生成 manifest.json、logo.png、index.html 和 js/plugin.js。
  3. 先跑通,再美化。 在 plugin.js 里写一句 console.log,打开插件,按 F12 看 Console 有没有输出。有输出就说明链路通了。
  4. 用 API 换掉假数据。 把 console.log 换成 await eagle.item.getSelected(),把返回的 id、名称渲染到页面上。这是「插件」和「网页」的分水岭。
  5. 按类型补齐配置。 窗口类插件要调 main 里的尺寸约束;要跟随主题就注册 eagle.onThemeChanged;要支持多语言就加 _locales 目录。

和浏览器扩展插件的差别

Eagle 早期文档里把插件称为「extension」,这个名字容易让人误会。Chrome 扩展需要 manifest.json 里写 permissions、content_scripts,还要打包成 .crx,装进浏览器。Eagle 插件不需要这些东西,也没有 content script 的概念——因为它天生就跑在宿主应用内部,权限边界由 Eagle 划定,你只调用它开放的 API。

理解这个差别能省掉很多搜索成本:你在网上搜到的「如何写 Chrome 插件」教程,对 Eagle 插件几乎完全不适用。

常见坑

把 require 当成浏览器模块加载器。 在 index.html 里用 script 标签直接引入 npm 包是不行的,必须在 .js 文件里 require('模块名') 并把 node_modules 一起打包。Eagle 插件走的是 Node 加载机制,不是浏览器的 script 标签机制。

假设插件是「打开网页」。 插件窗口每次被打开都会重新载入 index.html,页面状态不会保留。想记住上次操作,需要自己写入文件或用 localStorage。

忽略版本要求。 eagle.tag.getStarredTags()、Item 的 addComment()、eagle.smartFolder 全都标注了最低版本。在低版本客户端上一调用就是 undefined is not a function,而且不会有友好提示。

最小可运行示例

在动手写完整插件前,先记住这个五行的骨架,后面所有章节的代码都只是它的扩展:

// js/plugin.js
eagle.onPluginCreate(async (plugin) => {
    console.log('插件已创建');
    console.log(plugin.manifest.name);   // 从 manifest.json 读到的名称
    console.log(plugin.path);            // 插件在磁盘上的根目录
});

eagle.onPluginCreate 是插件的入口生命周期方法,回调参数 plugin 里带着 manifest(完整的 manifest.json 内容)和 path(插件自身路径)。绝大多数插件都从这一个回调开始启动。

下一步

下一节我们直接把第一个插件写出来,从空文件到一个能弹出窗口、显示自身信息的完整程序。

整理人:WorkBuddy