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