release: v0.17.0
GitHub Pages / build (push) Canceled after 0s
GitHub Pages / deploy (push) Canceled after 0s
Docker image / meta (push) Successful in 24s
Docs Docker image / meta (push) Successful in 7s
Docker image / build (amd64, linux/amd64, ubuntu-latest) (push) Failing after 59s
Docs Docker image / build (amd64, linux/amd64, ubuntu-latest) (push) Failing after 30s
Docker image / merge (push) Canceled after 0s
Docs Docker image / merge (push) Canceled after 0s
Docker image / build (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
Docs Docker image / build (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
GitHub Pages / build (push) Canceled after 0s
GitHub Pages / deploy (push) Canceled after 0s
Docker image / meta (push) Successful in 24s
Docs Docker image / meta (push) Successful in 7s
Docker image / build (amd64, linux/amd64, ubuntu-latest) (push) Failing after 59s
Docs Docker image / build (amd64, linux/amd64, ubuntu-latest) (push) Failing after 30s
Docker image / merge (push) Canceled after 0s
Docs Docker image / merge (push) Canceled after 0s
Docker image / build (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
Docs Docker image / build (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
This commit is contained in:
+14
-36
@@ -1,10 +1,10 @@
|
||||
# Infinite Canvas 画布节点插件
|
||||
# MiragenFlow 画布节点插件
|
||||
|
||||
给画布扩展自定义节点。每个插件是一个**独立目录**,用 **TypeScript** 编写,自带 `package.json` / `build.mjs` / `src/index.tsx` / `dist/`,互不耦合,可单独构建、发布、升级。
|
||||
本目录保留画布节点的源码级内部扩展能力。每个扩展是一个独立 TypeScript 目录,自带 `package.json` / `build.mjs` / `src/index.tsx` / `dist/`,可单独开发和构建。
|
||||
|
||||
内置节点只有文本、图片、视频、音频、生成配置、组六种;其余节点(Markdown、SVG、HTML、3D 全景、便利贴……)都是插件。
|
||||
内置节点包括文本、图片、音频、生成配置和组。Markdown、SVG、HTML、3D 全景和便利贴目录是 SDK 与内部集成示例。
|
||||
|
||||
作者只写节点 UI 与逻辑,**类型、JSX、宿主 React、构建全部由 [`@infinite-canvas/plugin-sdk`](./sdk/README.md) 提供**,写 TSX 全程有代码提示;产物仍是宿主加载器现有契约的 ESM(React external、宿主单例)。
|
||||
作者只写节点 UI 与逻辑,类型、JSX、宿主 React 桥接和构建由 [`@miragenflow/plugin-sdk`](./sdk/README.md) 提供。当前产品不包含远程插件市场、注册表、自动发现或 URL 安装入口;需要上线的扩展应作为应用源码的一部分接入并随产品构建。
|
||||
|
||||
## 目录约定
|
||||
|
||||
@@ -21,55 +21,37 @@ plugins/canvas/
|
||||
svg/ html/ panorama/ sticky-note/ ...
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
## 创建内部扩展
|
||||
|
||||
```bash
|
||||
cp -r plugins/canvas/template plugins/canvas/my-plugin
|
||||
cd plugins/canvas/my-plugin
|
||||
# 改 package.json 的 name;改 src/index.tsx 里的 id / nodes[].type
|
||||
npm install
|
||||
npm run dev # watch 构建,产物同步到 web/public/plugins/my-plugin.js
|
||||
npm run dev # watch 构建 dist/my-plugin.js
|
||||
npm run typecheck # tsc --noEmit,类型自检
|
||||
```
|
||||
|
||||
产物名取**目录名**,复制后记得把目录改成插件名。
|
||||
|
||||
## 构建 / 发布 / 升级
|
||||
## 构建
|
||||
|
||||
```bash
|
||||
cd plugins/canvas/<name>
|
||||
npm install
|
||||
npm run build # → dist/<name>.js,并同步到 web/public/plugins/<name>.js
|
||||
npm run dev # watch,改动自动构建并同步
|
||||
npm run build # → dist/<name>.js
|
||||
npm run dev # watch,改动自动构建
|
||||
```
|
||||
|
||||
把 `dist/<name>.js` 托管到任意静态地址(CDN、GitHub Raw、对象存储),用户在画布「节点插件」管理器填该 URL 安装。升级时重新构建覆盖同一 URL,用户点「更新」即可。
|
||||
|
||||
## 官方插件注册表
|
||||
|
||||
本项目官方插件由 CI 集中构建后发布到孤儿分支 `plugins-dist`(**构建产物不进 git**),画布「节点插件」面板顶部的**官方插件**区经 jsDelivr 从该分支远程拉取并一键安装;第三方插件仍走下方「第三方插件」的 JS URL 安装。构建脚本与发布说明见 [`registry/`](./registry/README.md);清单地址可用 `VITE_PLUGIN_REGISTRY_URL` 覆盖成自建来源。
|
||||
|
||||
## 本地开发
|
||||
|
||||
`npm run dev` 起 watch,产物会同步到 `web/public/plugins/<name>.js`。此后有两种方式在画布里用到它:
|
||||
|
||||
**方式一(推荐):自动发现。** 画布启动时会扫描 `web/public/plugins/` 下的插件,自动加入「节点插件」管理器列表,**默认关闭**;打开开关即启用。无需手动填 URL,启用时会按文件重新拉取,配合 watch 改完刷新即最新。
|
||||
|
||||
**方式二:`VITE_DEV_PLUGINS`。** 在 `web/.env.local` 声明(逗号分隔多个),这些插件每次刷新页面都**重新拉取并直接激活**(不缓存、不落库、无开关):
|
||||
|
||||
```env
|
||||
VITE_DEV_PLUGINS=/plugins/markdown.js,/plugins/svg.js
|
||||
```
|
||||
|
||||
再起画布 `web`(`npm run dev`)。流程即:改 `src/index.tsx` → watch 自动构建 → 刷新画布看到最新效果,无需反复安装。
|
||||
`buildPlugin` 只生成 `dist/` 产物,不会写入 `web/public/` 或生成远程清单。示例扩展默认不在产品界面中出现;商业构建需要在应用源码中显式注册准备启用的节点定义。
|
||||
|
||||
## 用 SDK 写插件
|
||||
|
||||
默认导出 `definePlugin({...})`(对象形式,**无需再 `const { React } = runtime`**):
|
||||
|
||||
```tsx
|
||||
import { definePlugin, useState } from "@infinite-canvas/plugin-sdk";
|
||||
import type { CanvasNodeContentProps } from "@infinite-canvas/plugin-sdk";
|
||||
import { definePlugin, useState } from "@miragenflow/plugin-sdk";
|
||||
import type { CanvasNodeContentProps } from "@miragenflow/plugin-sdk";
|
||||
|
||||
function Content({ ctx }: CanvasNodeContentProps) {
|
||||
const [n, setN] = useState(0);
|
||||
@@ -106,7 +88,7 @@ SDK 导出的 hooks(`useState/useEffect/useMemo/useRef/...`)运行时转发宿
|
||||
showInCreateMenu?: boolean; // 默认 true
|
||||
hasSourceHandle?: boolean; // 右侧输出连接点,默认 true
|
||||
keepAspectRatio?: (node) => boolean;
|
||||
resource?: (node) => { kind: "text"|"image"|"video"|"audio", text?, url? } | null; // 作为上游输入被消费时输出什么
|
||||
resource?: (node) => { kind: "text"|"image"|"audio", text?, url? } | null; // 作为上游输入被消费时输出什么
|
||||
Content: ({ ctx }) => ReactNode; // 节点主体渲染
|
||||
Panel?: ({ ctx, onClose }) => ReactNode; // 可选:节点下方面板
|
||||
toolbar?: (ctx) => Array<{ id, title, label, icon, onClick, danger? }>; // 追加到 hover 工具栏
|
||||
@@ -153,11 +135,7 @@ ctx.applyOps([
|
||||
- **CSS**:写独立 `.css`,`import css from "./styles.css"` 拿到字符串(esbuild `text` loader),放到 `css` 字段自动注入/清理;`src/env.d.ts` 声明 `*.css`。参考 `markdown/`。
|
||||
- **HTML**:HTML 节点把 HTML 字符串塞进 sandbox iframe 的 `srcDoc`,自带 `<style>`,不需要插件级 CSS。参考 `html/`。
|
||||
|
||||
## 兼容说明
|
||||
|
||||
加载器仍接受**默认导出为工厂函数** `(runtime) => CanvasPlugin`(用 `runtime.React`)或**普通对象**;老的 JS 插件无需改动即可运行。SDK 的 automatic JSX 让新插件走对象形式,更简洁。
|
||||
|
||||
## 注意
|
||||
|
||||
- 插件代码会在画布页面内**直接执行**,可访问浏览器本地数据(含 AI API Key)。发布前请自审,用户也只应安装可信来源。
|
||||
- 扩展代码会在画布页面内直接执行,可以访问浏览器本地数据(含 AI API Key),接入商业构建前需要完成代码审查。
|
||||
- 交互控件记得 `onMouseDown={(e) => e.stopPropagation()}`(避免触发节点拖拽),滚动区域加 `onWheel={(e) => e.stopPropagation()}` 与容器 `data-canvas-no-zoom`(避免被画布缩放拦截)。
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
# HTML 节点插件
|
||||
|
||||
Infinite Canvas 画布节点插件:用沙箱 iframe 渲染 HTML。源码里的 `{{input}}` 会被替换为上游文本节点内容。
|
||||
MiragenFlow 画布节点插件:用沙箱 iframe 渲染 HTML。源码里的 `{{input}}` 会被替换为上游文本节点内容。
|
||||
|
||||
## 构建
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build # 产物 dist/html.js,并同步到 web/public/plugins/html.js
|
||||
npm run build # 产物 dist/html.js
|
||||
npm run dev # watch
|
||||
```
|
||||
|
||||
## 安装
|
||||
|
||||
画布 → 左上菜单「节点插件」→ 安装 URL 填 `/plugins/html.js`(或托管后的公网 URL)。
|
||||
|
||||
插件契约见 `plugins/canvas/README.md`。
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { buildPlugin } from "@infinite-canvas/plugin-sdk/build";
|
||||
import { buildPlugin } from "@miragenflow/plugin-sdk/build";
|
||||
|
||||
await buildPlugin(import.meta.url);
|
||||
|
||||
Generated
+3
-3
@@ -8,13 +8,13 @@
|
||||
"name": "canvas-plugin-html",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@infinite-canvas/plugin-sdk": "file:../sdk",
|
||||
"@miragenflow/plugin-sdk": "file:../sdk",
|
||||
"@types/react": "19.1.12",
|
||||
"typescript": "^5"
|
||||
}
|
||||
},
|
||||
"../sdk": {
|
||||
"name": "@infinite-canvas/plugin-sdk",
|
||||
"name": "@miragenflow/plugin-sdk",
|
||||
"version": "0.1.0",
|
||||
"dev": true,
|
||||
"devDependencies": {
|
||||
@@ -26,7 +26,7 @@
|
||||
"@types/react": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@infinite-canvas/plugin-sdk": {
|
||||
"node_modules/@miragenflow/plugin-sdk": {
|
||||
"resolved": "../sdk",
|
||||
"link": true
|
||||
},
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
"version": "1.2.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Infinite Canvas HTML 节点插件",
|
||||
"description": "MiragenFlow HTML 节点插件",
|
||||
"scripts": {
|
||||
"build": "node build.mjs",
|
||||
"dev": "node build.mjs --watch",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@infinite-canvas/plugin-sdk": "file:../sdk",
|
||||
"@miragenflow/plugin-sdk": "file:../sdk",
|
||||
"@types/react": "19.1.12",
|
||||
"typescript": "^5"
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// 交互:预览态 iframe 默认 pointer-events:none —— 鼠标事件穿透到宿主节点体,
|
||||
// 因此点节点任意位置都能拖动,无需在节点上加任何标题栏/按钮。
|
||||
// 「编辑/预览」与「交互」开关都放在节点外的悬浮工具条(toolbar 扩展点),状态存 metadata。
|
||||
import { definePlugin, useMemo, useRef, useState } from "@infinite-canvas/plugin-sdk";
|
||||
import type { CanvasNodeContentProps } from "@infinite-canvas/plugin-sdk";
|
||||
import { definePlugin, useMemo, useRef, useState } from "@miragenflow/plugin-sdk";
|
||||
import type { CanvasNodeContentProps } from "@miragenflow/plugin-sdk";
|
||||
|
||||
// 源码编辑器行高/字号,行号槽与文本域必须完全一致才能对齐
|
||||
const EDITOR_FONT = 12;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "@infinite-canvas/plugin-sdk",
|
||||
"jsxImportSource": "@miragenflow/plugin-sdk",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true,
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
# Markdown 节点插件
|
||||
|
||||
Infinite Canvas 画布节点插件:在画布里编辑与渲染 Markdown。
|
||||
MiragenFlow 画布节点插件:在画布里编辑与渲染 Markdown。
|
||||
|
||||
## 构建
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build # 产物 dist/markdown.js,并同步到 web/public/plugins/markdown.js
|
||||
npm run build # 产物 dist/markdown.js
|
||||
npm run dev # watch,改动自动构建
|
||||
```
|
||||
|
||||
## 安装
|
||||
|
||||
画布 → 左上菜单「节点插件」→ 安装 URL 填 `/plugins/markdown.js`(或托管后的公网 URL)。
|
||||
|
||||
## 本地开发
|
||||
|
||||
`npm run dev` 起 watch,在 `web/.env.local` 加 `VITE_DEV_PLUGINS=/plugins/markdown.js`,起画布后改 `src/index.jsx` 刷新页面即生效,无需反复安装。
|
||||
`npm run dev` 起 watch。将需要的节点定义在应用源码中注册后,启动 `web` 并刷新画布即可验证。
|
||||
|
||||
插件契约见 `plugins/canvas/README.md`。
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { buildPlugin } from "@infinite-canvas/plugin-sdk/build";
|
||||
import { buildPlugin } from "@miragenflow/plugin-sdk/build";
|
||||
|
||||
await buildPlugin(import.meta.url);
|
||||
|
||||
+3
-3
@@ -8,13 +8,13 @@
|
||||
"name": "canvas-plugin-markdown",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@infinite-canvas/plugin-sdk": "file:../sdk",
|
||||
"@miragenflow/plugin-sdk": "file:../sdk",
|
||||
"@types/react": "19.1.12",
|
||||
"typescript": "^5"
|
||||
}
|
||||
},
|
||||
"../sdk": {
|
||||
"name": "@infinite-canvas/plugin-sdk",
|
||||
"name": "@miragenflow/plugin-sdk",
|
||||
"version": "0.1.0",
|
||||
"dev": true,
|
||||
"devDependencies": {
|
||||
@@ -26,7 +26,7 @@
|
||||
"@types/react": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@infinite-canvas/plugin-sdk": {
|
||||
"node_modules/@miragenflow/plugin-sdk": {
|
||||
"resolved": "../sdk",
|
||||
"link": true
|
||||
},
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Infinite Canvas Markdown 节点插件",
|
||||
"description": "MiragenFlow Markdown 节点插件",
|
||||
"scripts": {
|
||||
"build": "node build.mjs",
|
||||
"dev": "node build.mjs --watch",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@infinite-canvas/plugin-sdk": "file:../sdk",
|
||||
"@miragenflow/plugin-sdk": "file:../sdk",
|
||||
"@types/react": "19.1.12",
|
||||
"typescript": "^5"
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
// 防闪烁:解析结果按源码模块级缓存,且只在 HTML 真正变化时写入 DOM——
|
||||
// 画布任何重渲染都不会重新解析或重载 Markdown 里的图片。
|
||||
// styles.css 由 esbuild 以 text 方式打进 bundle,通过 plugin.css 自动注入。
|
||||
import { definePlugin, useEffect, useRef, useState } from "@infinite-canvas/plugin-sdk";
|
||||
import type { CanvasNodeContentProps } from "@infinite-canvas/plugin-sdk";
|
||||
import { definePlugin, useEffect, useRef, useState } from "@miragenflow/plugin-sdk";
|
||||
import type { CanvasNodeContentProps } from "@miragenflow/plugin-sdk";
|
||||
|
||||
import css from "./styles.css";
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "@infinite-canvas/plugin-sdk",
|
||||
"jsxImportSource": "@miragenflow/plugin-sdk",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true,
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
# 3D 全景节点插件
|
||||
|
||||
Infinite Canvas 画布节点插件:查看 360° 等距柱状(equirectangular)全景图,可拖拽旋转。可从上游图片节点自动取图。three.js 运行时从 CDN 按需加载,不打进插件体积。
|
||||
MiragenFlow 画布节点插件:查看 360° 等距柱状(equirectangular)全景图,可拖拽旋转。可从上游图片节点自动取图。three.js 运行时从 CDN 按需加载,不打进插件体积。
|
||||
|
||||
## 构建
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build # 产物 dist/panorama.js,并同步到 web/public/plugins/panorama.js
|
||||
npm run build # 产物 dist/panorama.js
|
||||
npm run dev # watch
|
||||
```
|
||||
|
||||
## 安装
|
||||
|
||||
画布 → 左上菜单「节点插件」→ 安装 URL 填 `/plugins/panorama.js`(或托管后的公网 URL)。连接一个全景图片节点到本节点即可查看。
|
||||
构建后将节点定义作为应用源码的一部分注册。连接一个全景图片节点到本节点即可查看。
|
||||
|
||||
插件契约见 `plugins/canvas/README.md`。
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { buildPlugin } from "@infinite-canvas/plugin-sdk/build";
|
||||
import { buildPlugin } from "@miragenflow/plugin-sdk/build";
|
||||
|
||||
await buildPlugin(import.meta.url);
|
||||
|
||||
+3
-3
@@ -8,13 +8,13 @@
|
||||
"name": "canvas-plugin-panorama",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@infinite-canvas/plugin-sdk": "file:../sdk",
|
||||
"@miragenflow/plugin-sdk": "file:../sdk",
|
||||
"@types/react": "19.1.12",
|
||||
"typescript": "^5"
|
||||
}
|
||||
},
|
||||
"../sdk": {
|
||||
"name": "@infinite-canvas/plugin-sdk",
|
||||
"name": "@miragenflow/plugin-sdk",
|
||||
"version": "0.1.0",
|
||||
"dev": true,
|
||||
"devDependencies": {
|
||||
@@ -26,7 +26,7 @@
|
||||
"@types/react": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@infinite-canvas/plugin-sdk": {
|
||||
"node_modules/@miragenflow/plugin-sdk": {
|
||||
"resolved": "../sdk",
|
||||
"link": true
|
||||
},
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Infinite Canvas 3D 全景节点插件",
|
||||
"description": "MiragenFlow 3D 全景节点插件",
|
||||
"scripts": {
|
||||
"build": "node build.mjs",
|
||||
"dev": "node build.mjs --watch",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@infinite-canvas/plugin-sdk": "file:../sdk",
|
||||
"@miragenflow/plugin-sdk": "file:../sdk",
|
||||
"@types/react": "19.1.12",
|
||||
"typescript": "^5"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// 3D 全景节点:等距柱状(equirectangular)360° 全景查看器。
|
||||
// 三种取图方式:① 从上游图片节点自动取图 ② 本地上传 ③ AI 生成(复用宿主内置生成面板)。
|
||||
// three.js 从 CDN 按需加载,不打进 bundle。AI 生成通过 useBuiltinPanel 声明,结果写回本节点。
|
||||
import { definePlugin, useCallback, useEffect, useRef, useState } from "@infinite-canvas/plugin-sdk";
|
||||
import type { CanvasNodeContentProps, CanvasNodeContext } from "@infinite-canvas/plugin-sdk";
|
||||
import { definePlugin, useCallback, useEffect, useRef, useState } from "@miragenflow/plugin-sdk";
|
||||
import type { CanvasNodeContentProps, CanvasNodeContext } from "@miragenflow/plugin-sdk";
|
||||
|
||||
// three 从 CDN 动态加载(env.d.ts 里声明为 any),不打进 bundle
|
||||
let threePromise: Promise<any> | undefined;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "@infinite-canvas/plugin-sdk",
|
||||
"jsxImportSource": "@miragenflow/plugin-sdk",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true,
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
# 官方插件注册表(集中构建 + 远程发布)
|
||||
|
||||
本目录**只放构建脚本**,不放构建产物。官方插件由 CI 构建后发布到孤儿分支 `plugins-dist`,画布经 jsDelivr 从该分支远程拉取并一键安装。第三方插件不进本流程,由用户自行填 JS URL 安装。
|
||||
|
||||
```
|
||||
registry/
|
||||
package.json # 构建依赖(esbuild + SDK)
|
||||
build.mjs # 一次进程构建所有官方插件 → dist/(gitignore)+ 生成清单
|
||||
dist/ # 构建产物(gitignore,不提交;CI 发布到 plugins-dist 分支)
|
||||
```
|
||||
|
||||
**产物不进 git**:`dist/` 与 `node_modules/` 均被 `.gitignore` 覆盖。`main` 分支只有源码与脚本。
|
||||
|
||||
## 发布流程(CI 自动)
|
||||
|
||||
`.github/workflows/publish-plugins.yml` 在**打版本 tag(`v*`)**或手动触发(`workflow_dispatch`)时,与 GitHub Pages 发布一起跑:
|
||||
|
||||
1. `npm install && npm run build` → 在 `dist/` 产出各 `<id>.js` 与 `official-plugins.json`;
|
||||
2. 把 `dist/` 强推到孤儿分支 **`plugins-dist`**(仅含产物,force-push 覆盖)。
|
||||
|
||||
前端默认从下面地址读取(可用 `VITE_PLUGIN_REGISTRY_URL` 覆盖):
|
||||
|
||||
```
|
||||
https://cdn.jsdelivr.net/gh/basketikun/infinite-canvas@plugins-dist/official-plugins.json
|
||||
```
|
||||
|
||||
清单里每条的 `entry`(相对文件名)由前端解析成与清单同目录的绝对 URL,再走既有 URL 安装流程。jsDelivr 对分支有缓存(约数小时),需要立即生效可对该分支目录做 purge。
|
||||
|
||||
## 新增 / 更新官方插件
|
||||
|
||||
- 改完某官方插件源码后,在 `build.mjs` 的 `OFFICIAL` 里保持登记(新增插件在此加一条),提交到 `main`;
|
||||
- 下次打版本 tag(或手动 `workflow_dispatch`)时,CI 自动重新构建并发布到 `plugins-dist`。
|
||||
|
||||
## 本地自测官方面板
|
||||
|
||||
```bash
|
||||
cd plugins/canvas/registry && npm install && npm run build # 产出 dist/
|
||||
# 用任意静态服务器伺服 dist/,把 VITE_PLUGIN_REGISTRY_URL 指向其 official-plugins.json
|
||||
```
|
||||
@@ -1,69 +0,0 @@
|
||||
// 官方插件集中构建:一次进程构建所有官方插件 + 生成清单,产物进 dist/(已 gitignore)。
|
||||
// 官方插件目录本身不各自安装依赖:统一从本目录 node_modules 解析 SDK(nodePaths)。
|
||||
// CI(publish-plugins.yml)把 dist/ 强推到 plugins-dist 分支,前端经 jsDelivr 远程拉取。
|
||||
// 本地自测:`npm install && npm run build`,再把 VITE_PLUGIN_REGISTRY_URL 指向本地 dist。
|
||||
import { build } from "esbuild";
|
||||
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const root = dirname(fileURLToPath(import.meta.url));
|
||||
const outDir = join(root, "dist");
|
||||
const nodeModules = join(root, "node_modules");
|
||||
|
||||
// 官方插件登记表:新增官方插件在此登记即可。
|
||||
// 版本不在这里写死 —— 从各插件的 package.json 读取(单一真源),
|
||||
// 与插件 src 里 definePlugin({version}) 保持一致,避免清单与产物版本脱节。
|
||||
const OFFICIAL = [
|
||||
{ id: "markdown", dir: "markdown", name: "Markdown 节点", description: "在画布中编辑与渲染 Markdown", icon: "📝" },
|
||||
{ id: "svg", dir: "svg", name: "SVG 节点", description: "透明背景渲染 SVG,可接收上游文本节点的 SVG 源码", icon: "🔷" },
|
||||
{ id: "html", dir: "html", name: "HTML 节点", description: "沙箱 iframe 渲染 HTML,支持 {{input}} 注入上游文本", icon: "🌐" },
|
||||
{ id: "panorama", dir: "panorama", name: "3D 全景节点", description: "查看 360° 等距柱状全景图,可从上游图片节点取图", icon: "🧭" },
|
||||
{ id: "sticky-note", dir: "sticky-note", name: "便利贴节点", description: "可自选颜色、双击编辑、拖动即可移动的便利贴", icon: "📌" },
|
||||
];
|
||||
|
||||
// 读取插件 package.json 的 version 作为清单版本的唯一来源
|
||||
async function readPluginVersion(dir) {
|
||||
const pkg = JSON.parse(await readFile(join(root, "..", dir, "package.json"), "utf8"));
|
||||
if (!pkg.version) throw new Error(`插件 ${dir} 的 package.json 缺少 version`);
|
||||
return pkg.version;
|
||||
}
|
||||
|
||||
await rm(outDir, { recursive: true, force: true });
|
||||
await mkdir(outDir, { recursive: true });
|
||||
|
||||
for (const plugin of OFFICIAL) {
|
||||
await build({
|
||||
entryPoints: [join(root, "..", plugin.dir, "src", "index.tsx")],
|
||||
outfile: join(outDir, `${plugin.id}.js`),
|
||||
bundle: true,
|
||||
format: "esm",
|
||||
platform: "browser",
|
||||
target: "es2020",
|
||||
// automatic JSX → SDK 的 jsx-runtime(内部用宿主 React),react external
|
||||
jsx: "automatic",
|
||||
jsxImportSource: "@infinite-canvas/plugin-sdk",
|
||||
loader: { ".ts": "ts", ".tsx": "tsx", ".css": "text" },
|
||||
external: ["react", "react-dom"],
|
||||
minify: true,
|
||||
nodePaths: [nodeModules],
|
||||
});
|
||||
console.log(`built ${plugin.id}.js`);
|
||||
}
|
||||
|
||||
const manifest = {
|
||||
// 清单结构版本;entry 为相对本清单的 bundle 文件名,由前端解析成绝对 URL
|
||||
version: 1,
|
||||
plugins: await Promise.all(
|
||||
OFFICIAL.map(async (plugin) => ({
|
||||
id: plugin.id,
|
||||
name: plugin.name,
|
||||
version: await readPluginVersion(plugin.dir),
|
||||
description: plugin.description,
|
||||
icon: plugin.icon,
|
||||
entry: `${plugin.id}.js`,
|
||||
})),
|
||||
),
|
||||
};
|
||||
await writeFile(join(outDir, "official-plugins.json"), JSON.stringify(manifest, null, 4) + "\n");
|
||||
console.log(`wrote official-plugins.json (${OFFICIAL.length} plugins) → dist/`);
|
||||
-517
@@ -1,517 +0,0 @@
|
||||
{
|
||||
"name": "canvas-official-plugins",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "canvas-official-plugins",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@infinite-canvas/plugin-sdk": "file:../sdk",
|
||||
"esbuild": "^0.25.0"
|
||||
}
|
||||
},
|
||||
"../sdk": {
|
||||
"name": "@infinite-canvas/plugin-sdk",
|
||||
"version": "0.1.0",
|
||||
"dev": true,
|
||||
"devDependencies": {
|
||||
"@types/react": "19.1.12",
|
||||
"esbuild": "^0.25.0",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
|
||||
"integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
|
||||
"integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
|
||||
"integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
|
||||
"integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
|
||||
"integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
|
||||
"integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
|
||||
"integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
|
||||
"integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
|
||||
"integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
|
||||
"integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
|
||||
"integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
|
||||
"integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
|
||||
"integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
|
||||
"integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
|
||||
"integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
|
||||
"integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
|
||||
"integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
|
||||
"integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
|
||||
"integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
|
||||
"integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
|
||||
"integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openharmony-arm64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
|
||||
"integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
|
||||
"integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
|
||||
"integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
|
||||
"integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
|
||||
"integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@infinite-canvas/plugin-sdk": {
|
||||
"resolved": "../sdk",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
|
||||
"integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.25.12",
|
||||
"@esbuild/android-arm": "0.25.12",
|
||||
"@esbuild/android-arm64": "0.25.12",
|
||||
"@esbuild/android-x64": "0.25.12",
|
||||
"@esbuild/darwin-arm64": "0.25.12",
|
||||
"@esbuild/darwin-x64": "0.25.12",
|
||||
"@esbuild/freebsd-arm64": "0.25.12",
|
||||
"@esbuild/freebsd-x64": "0.25.12",
|
||||
"@esbuild/linux-arm": "0.25.12",
|
||||
"@esbuild/linux-arm64": "0.25.12",
|
||||
"@esbuild/linux-ia32": "0.25.12",
|
||||
"@esbuild/linux-loong64": "0.25.12",
|
||||
"@esbuild/linux-mips64el": "0.25.12",
|
||||
"@esbuild/linux-ppc64": "0.25.12",
|
||||
"@esbuild/linux-riscv64": "0.25.12",
|
||||
"@esbuild/linux-s390x": "0.25.12",
|
||||
"@esbuild/linux-x64": "0.25.12",
|
||||
"@esbuild/netbsd-arm64": "0.25.12",
|
||||
"@esbuild/netbsd-x64": "0.25.12",
|
||||
"@esbuild/openbsd-arm64": "0.25.12",
|
||||
"@esbuild/openbsd-x64": "0.25.12",
|
||||
"@esbuild/openharmony-arm64": "0.25.12",
|
||||
"@esbuild/sunos-x64": "0.25.12",
|
||||
"@esbuild/win32-arm64": "0.25.12",
|
||||
"@esbuild/win32-ia32": "0.25.12",
|
||||
"@esbuild/win32-x64": "0.25.12"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "canvas-official-plugins",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Infinite Canvas 官方插件的集中构建(CI 发布到 plugins-dist 分支)",
|
||||
"scripts": {
|
||||
"build": "node build.mjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@infinite-canvas/plugin-sdk": "file:../sdk",
|
||||
"esbuild": "^0.25.0"
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
# @infinite-canvas/plugin-sdk
|
||||
# @miragenflow/plugin-sdk
|
||||
|
||||
Infinite Canvas 画布节点插件的 **TypeScript SDK**。插件作者只写节点 UI 与逻辑,类型、JSX、运行时桥接、构建全部由 SDK 提供;产物仍是宿主加载器现有契约的 ESM(React external,宿主单例)。
|
||||
MiragenFlow 画布节点内部扩展的 **TypeScript SDK**。扩展作者只写节点 UI 与逻辑,类型、JSX、运行时桥接和构建由 SDK 提供。当前产品不提供远程加载或安装入口,扩展需要随应用源码集成。
|
||||
|
||||
## 提供什么
|
||||
|
||||
| 能力 | 说明 |
|
||||
| --- | --- |
|
||||
| **完整类型** | `CanvasPlugin` / `CanvasNodeDefinition` / `CanvasNodeContext` / `CanvasAgentOp` / `CanvasTheme` / `CanvasNodeData` … 全部有提示 |
|
||||
| **完整类型** | `CanvasPlugin` / `CanvasNodeDefinition` / `CanvasNodeContext` / `CanvasOp` / `CanvasTheme` / `CanvasNodeData` … 全部有提示 |
|
||||
| `definePlugin(...)` | 给插件对象(或工厂)补全类型;对象形式无需再 `const { React } = runtime` |
|
||||
| automatic JSX | `jsxImportSource` 指向本包,TSX 自动转发到宿主 React,**不打包第二份 React** |
|
||||
| 类型化 hooks | `import { useState, useEffect, useMemo, useRef, ... }`,运行时转发宿主 React |
|
||||
@@ -16,8 +16,8 @@ Infinite Canvas 画布节点插件的 **TypeScript SDK**。插件作者只写节
|
||||
|
||||
```tsx
|
||||
// src/index.tsx
|
||||
import { definePlugin, useState } from "@infinite-canvas/plugin-sdk";
|
||||
import type { CanvasNodeContentProps } from "@infinite-canvas/plugin-sdk";
|
||||
import { definePlugin, useState } from "@miragenflow/plugin-sdk";
|
||||
import type { CanvasNodeContentProps } from "@miragenflow/plugin-sdk";
|
||||
|
||||
function Content({ ctx }: CanvasNodeContentProps) {
|
||||
const [n, setN] = useState(0);
|
||||
@@ -46,11 +46,11 @@ export default definePlugin({
|
||||
|
||||
```js
|
||||
// build.mjs
|
||||
import { buildPlugin } from "@infinite-canvas/plugin-sdk/build";
|
||||
import { buildPlugin } from "@miragenflow/plugin-sdk/build";
|
||||
await buildPlugin(import.meta.url);
|
||||
```
|
||||
|
||||
`npm run build` 产出 `dist/<目录名>.js` 并同步到 `web/public/plugins/`。
|
||||
`npm run build` 只产出 `dist/<目录名>.js`,不会写入应用公共目录或远程清单。
|
||||
|
||||
## 依赖接入
|
||||
|
||||
@@ -61,17 +61,17 @@ await buildPlugin(import.meta.url);
|
||||
"type": "module",
|
||||
"scripts": { "build": "node build.mjs", "dev": "node build.mjs --watch", "typecheck": "tsc --noEmit" },
|
||||
"devDependencies": {
|
||||
"@infinite-canvas/plugin-sdk": "file:../sdk",
|
||||
"@miragenflow/plugin-sdk": "file:../sdk",
|
||||
"@types/react": "19.1.12",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
插件 `tsconfig.json` 关键项:`"jsx": "react-jsx"`、`"jsxImportSource": "@infinite-canvas/plugin-sdk"`、`"moduleResolution": "bundler"`。
|
||||
插件 `tsconfig.json` 关键项:`"jsx": "react-jsx"`、`"jsxImportSource": "@miragenflow/plugin-sdk"`、`"moduleResolution": "bundler"`。
|
||||
|
||||
## 设计约束
|
||||
|
||||
- **React 单例**:JSX 与 hooks 惰性读取 `globalThis.InfiniteCanvasRuntime.React`(宿主在加载插件前注入),react 全程 external,绝不打包第二份。
|
||||
- **React 单例**:JSX 与 hooks 惰性读取 `globalThis.MiragenFlowRuntime.React`(由源码集成层注入),react 全程 external,绝不打包第二份。
|
||||
- **重依赖**:three、marked 等在源码里 `await import("https://esm.sh/...")` 动态加载,esbuild 自动 external,不进 bundle。
|
||||
- **类型真源**:`src/types.ts` 是宿主 `web/src/types/canvas-plugin.ts` 公开契约的镜像;宿主契约变更时同步此处。
|
||||
|
||||
@@ -1,51 +1,21 @@
|
||||
// 共享构建助手:插件的 build.mjs 只需一行 `buildPlugin(import.meta.url)`。
|
||||
// 统一 esbuild 配置(automatic JSX 指向本 SDK、react external、TS/TSX/CSS loader、
|
||||
// 产物同步到 web/public/plugins),消除各插件重复的构建脚本。
|
||||
// 统一 esbuild 配置(automatic JSX 指向本 SDK、react external、TS/TSX/CSS loader)。
|
||||
|
||||
import { build, context } from "esbuild";
|
||||
import { cp, mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
import { basename, dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
/**
|
||||
* @param {string} metaUrl 插件 build.mjs 的 import.meta.url
|
||||
* @param {{ name?: string, entry?: string, publicDir?: string, esbuild?: object, plugins?: import("esbuild").Plugin[] }} [overrides]
|
||||
* @param {{ name?: string, entry?: string, esbuild?: object, plugins?: import("esbuild").Plugin[] }} [overrides]
|
||||
*/
|
||||
export async function buildPlugin(metaUrl, overrides = {}) {
|
||||
const root = dirname(fileURLToPath(metaUrl));
|
||||
const name = overrides.name ?? basename(root); // 目录名即产物名,如 markdown → markdown.js
|
||||
const distDir = join(root, "dist");
|
||||
// 默认同步到仓库内 web/public/plugins(plugins/canvas/<name> → 上溯三层到仓库根)
|
||||
const publicDir = overrides.publicDir ?? join(root, "..", "..", "..", "web", "public", "plugins");
|
||||
const watch = process.argv.includes("--watch");
|
||||
const entry = overrides.entry ?? join(root, "src", "index.tsx");
|
||||
|
||||
const syncToPublic = {
|
||||
name: "sync-to-public",
|
||||
setup(builder) {
|
||||
builder.onEnd(async (result) => {
|
||||
if (result.errors.length) return;
|
||||
await mkdir(publicDir, { recursive: true });
|
||||
await cp(join(distDir, `${name}.js`), join(publicDir, `${name}.js`));
|
||||
// 维护本地插件清单 index.json,供画布启动时自动发现(该目录已 gitignore,仅本地开发用)
|
||||
const indexPath = join(publicDir, "index.json");
|
||||
let list = [];
|
||||
try {
|
||||
const parsed = JSON.parse(await readFile(indexPath, "utf8"));
|
||||
if (Array.isArray(parsed)) list = parsed;
|
||||
} catch {
|
||||
// 无清单则新建
|
||||
}
|
||||
const entry = `/plugins/${name}.js`;
|
||||
if (!list.includes(entry)) {
|
||||
list.push(entry);
|
||||
await writeFile(indexPath, JSON.stringify(list, null, 2) + "\n");
|
||||
}
|
||||
console.log(`[${name}] synced → web/public/plugins/${name}.js`);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const options = {
|
||||
entryPoints: [entry],
|
||||
outfile: join(distDir, `${name}.js`),
|
||||
@@ -55,12 +25,12 @@ export async function buildPlugin(metaUrl, overrides = {}) {
|
||||
target: "es2020",
|
||||
// automatic JSX → 转发到本 SDK 的 jsx-runtime(内部用宿主 React),插件无需自带 React
|
||||
jsx: "automatic",
|
||||
jsxImportSource: "@infinite-canvas/plugin-sdk",
|
||||
jsxImportSource: "@miragenflow/plugin-sdk",
|
||||
loader: { ".js": "jsx", ".jsx": "jsx", ".ts": "ts", ".tsx": "tsx", ".css": "text" },
|
||||
// 宿主提供单例 React,插件不打包 react;https:// 依赖 esbuild 自动 external
|
||||
external: ["react", "react-dom"],
|
||||
minify: !watch,
|
||||
plugins: [syncToPublic, ...(overrides.plugins ?? [])],
|
||||
plugins: overrides.plugins ?? [],
|
||||
...overrides.esbuild,
|
||||
};
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "@infinite-canvas/plugin-sdk",
|
||||
"name": "@miragenflow/plugin-sdk",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@infinite-canvas/plugin-sdk",
|
||||
"name": "@miragenflow/plugin-sdk",
|
||||
"version": "0.1.0",
|
||||
"devDependencies": {
|
||||
"@types/react": "19.1.12",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "@infinite-canvas/plugin-sdk",
|
||||
"name": "@miragenflow/plugin-sdk",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Infinite Canvas 画布节点插件 SDK:类型、automatic JSX 运行时、构建助手",
|
||||
"description": "MiragenFlow 画布节点插件 SDK:类型、automatic JSX 运行时、构建助手",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./jsx-runtime": "./src/jsx-runtime.ts",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @infinite-canvas/plugin-sdk 公开入口。
|
||||
// @miragenflow/plugin-sdk 公开入口。
|
||||
//
|
||||
// 插件作者用 TS/TSX 编写,只关注节点 UI 与逻辑;类型、JSX、运行时桥接、构建
|
||||
// 全部由本 SDK 提供,产物仍是宿主 loader 现有契约的 ESM(React external,宿主单例)。
|
||||
|
||||
@@ -7,7 +7,7 @@ import type * as React from "react";
|
||||
import { getReact } from "./runtime";
|
||||
|
||||
// Fragment 哨兵:渲染时才解析为宿主 React.Fragment,避免模块顶层触碰运行时。
|
||||
export const Fragment = Symbol.for("infinite-canvas.jsx.fragment") as unknown as React.ExoticComponent<{ children?: React.ReactNode }>;
|
||||
export const Fragment = Symbol.for("miragenflow.jsx.fragment") as unknown as React.ExoticComponent<{ children?: React.ReactNode }>;
|
||||
|
||||
function createElement(type: unknown, props: Record<string, unknown> | null, key?: unknown): React.ReactElement {
|
||||
const react = getReact();
|
||||
|
||||
@@ -6,13 +6,13 @@ import type * as React from "react";
|
||||
|
||||
import type { PluginRuntime } from "./types";
|
||||
|
||||
type RuntimeGlobal = { InfiniteCanvasRuntime?: PluginRuntime };
|
||||
type RuntimeGlobal = { MiragenFlowRuntime?: PluginRuntime };
|
||||
|
||||
/** 取宿主注入的插件运行时(含 React、事件总线、injectCSS 等)。 */
|
||||
export function getRuntime(): PluginRuntime {
|
||||
const runtime = (globalThis as unknown as RuntimeGlobal).InfiniteCanvasRuntime;
|
||||
const runtime = (globalThis as unknown as RuntimeGlobal).MiragenFlowRuntime;
|
||||
if (!runtime) {
|
||||
throw new Error("[plugin-sdk] Infinite Canvas 运行时未就绪:请在画布宿主中加载本插件");
|
||||
throw new Error("[plugin-sdk] MiragenFlow 运行时未就绪:请在画布宿主中加载本插件");
|
||||
}
|
||||
return runtime;
|
||||
}
|
||||
@@ -23,7 +23,7 @@ export function getReact(): typeof React {
|
||||
}
|
||||
|
||||
// --- 类型完整的 hooks 转发:签名取自 @types/react,运行时转发到宿主 React ---
|
||||
// 这样插件作者可以直接 `import { useState } from "@infinite-canvas/plugin-sdk"`,
|
||||
// 这样插件作者可以直接 `import { useState } from "@miragenflow/plugin-sdk"`,
|
||||
// 无需再从 runtime 里解构 React。
|
||||
|
||||
export const useState: typeof React.useState = ((...args: unknown[]) => (getReact().useState as (...a: unknown[]) => unknown)(...args)) as typeof React.useState;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Infinite Canvas 插件公共契约类型。
|
||||
// MiragenFlow 插件公共契约类型。
|
||||
//
|
||||
// 这是插件作者面向的「公开接口」子集,自包含、不依赖宿主 `@/` 内部模块,
|
||||
// 因此可以被独立构建的插件包直接 import,获得完整的 TS 提示。
|
||||
@@ -17,11 +17,11 @@ export type Position = { x: number; y: number };
|
||||
export type ViewportTransform = { x: number; y: number; k: number };
|
||||
|
||||
// 内置节点类型;插件节点建议用 "<pluginId>:<name>"。放开为字符串以便扩展。
|
||||
export type CanvasBuiltinNodeType = "image" | "text" | "config" | "video" | "audio" | "group";
|
||||
export type CanvasBuiltinNodeType = "image" | "text" | "config" | "audio" | "group";
|
||||
export type CanvasNodeTypeId = CanvasBuiltinNodeType | (string & {});
|
||||
|
||||
export type CanvasNodeStatus = "idle" | "success" | "loading" | "error";
|
||||
export type CanvasGenerationMode = "text" | "image" | "video" | "audio";
|
||||
export type CanvasGenerationMode = "text" | "image" | "audio";
|
||||
export type CanvasImageGenerationType = "generation" | "edit";
|
||||
|
||||
// 节点 metadata 是扁平可选字段袋;插件自定义字段可直接写入(内容惯例放 content)。
|
||||
@@ -38,10 +38,6 @@ export type CanvasNodeMetadata = {
|
||||
size?: string;
|
||||
quality?: string;
|
||||
count?: number;
|
||||
seconds?: string;
|
||||
vquality?: string;
|
||||
generateAudio?: string;
|
||||
watermark?: string;
|
||||
audioVoice?: string;
|
||||
audioFormat?: string;
|
||||
audioSpeed?: string;
|
||||
@@ -115,10 +111,10 @@ export type CanvasTheme = {
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 画布指令集(ctx.applyOps):与 AI Agent 同级的画布操作能力
|
||||
// 画布操作集(ctx.applyOps)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export type CanvasAgentOp =
|
||||
export type CanvasOp =
|
||||
| { type: "add_node"; id?: string; nodeType?: CanvasNodeTypeId; title?: string; position?: { x: number; y: number }; x?: number; y?: number; width?: number; height?: number; metadata?: CanvasNodeMetadata }
|
||||
| { type: "update_node"; id: string; patch?: Partial<CanvasNodeData>; metadata?: CanvasNodeMetadata }
|
||||
| { type: "delete_node"; id?: string; ids?: string[]; nodeType?: CanvasNodeTypeId }
|
||||
@@ -132,18 +128,18 @@ export type CanvasAgentOp =
|
||||
// 资源:插件节点作为上游输入被消费时输出什么(接入生成/引用体系)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export type CanvasResourceKind = "image" | "video" | "audio" | "text";
|
||||
export type CanvasResourceKind = "image" | "audio" | "text";
|
||||
export type CanvasNodeResource = { kind: CanvasResourceKind; text?: string; url?: string };
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AI 生成:插件直接复用宿主的模型/密钥配置发起生成(生图/生视频/生文本/生音频)
|
||||
// AI 生成:插件直接复用宿主的模型/密钥配置发起生成(生图/生文本/生音频)
|
||||
//
|
||||
// 插件本身拿不到 API Key 与模型配置,这些能力由宿主注入。前置/系统提示词由
|
||||
// 插件自行拼进 prompt(宿主不感知),因此不同插件可各自定制自己的提示词策略。
|
||||
// 若宿主 AI 配置未就绪,会抛错(并由宿主提示用户去配置),插件用 try/catch 处理即可。
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// 生成公共可选项;references 为图生图/图生视频的参考图(dataURL 或可访问 URL)
|
||||
// 生成公共可选项;references 为图生图的参考图(dataURL 或可访问 URL)
|
||||
export type GenerateOptions = {
|
||||
signal?: AbortSignal;
|
||||
references?: string[];
|
||||
@@ -160,19 +156,6 @@ export type GenerateImageResult = {
|
||||
images: string[];
|
||||
};
|
||||
|
||||
export type GenerateVideoOptions = GenerateOptions & {
|
||||
size?: string;
|
||||
seconds?: string;
|
||||
};
|
||||
|
||||
export type GenerateVideoResult = {
|
||||
url: string; // 视频可访问 URL
|
||||
mimeType: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
durationMs?: number;
|
||||
};
|
||||
|
||||
export type GenerateTextOptions = {
|
||||
signal?: AbortSignal;
|
||||
model?: string;
|
||||
@@ -185,13 +168,12 @@ export type GenerateTextResult = {
|
||||
};
|
||||
|
||||
// 一个可选模型:value 传回给 generateXxx({ model }),label 用于展示
|
||||
export type ModelCapability = "image" | "video" | "text" | "audio";
|
||||
export type ModelCapability = "image" | "text" | "audio";
|
||||
export type ModelOption = { value: string; label: string };
|
||||
|
||||
// 宿主注入的 AI 生成能力,挂在 ctx.ai 下。任何插件均可调用。
|
||||
export type CanvasPluginAi = {
|
||||
generateImage: (prompt: string, options?: GenerateImageOptions) => Promise<GenerateImageResult>;
|
||||
generateVideo: (prompt: string, options?: GenerateVideoOptions) => Promise<GenerateVideoResult>;
|
||||
generateText: (prompt: string, options?: GenerateTextOptions) => Promise<GenerateTextResult>;
|
||||
// 列出某能力下用户已配置的可选模型;不传能力则返回全部
|
||||
listModels: (capability?: ModelCapability) => ModelOption[];
|
||||
@@ -223,12 +205,12 @@ export type CanvasNodeContext = {
|
||||
getConnections: () => CanvasConnection[];
|
||||
getUpstream: () => CanvasNodeData[];
|
||||
getDownstream: () => CanvasNodeData[];
|
||||
// 画布操作(复用 Agent 指令集)
|
||||
applyOps: (ops: CanvasAgentOp[]) => void;
|
||||
// 画布操作
|
||||
applyOps: (ops: CanvasOp[]) => void;
|
||||
// 节点间/插件间通信
|
||||
emit: (event: string, payload?: unknown) => void;
|
||||
on: (event: string, handler: (payload: unknown) => void) => () => void;
|
||||
// AI 生成能力(生图/生视频/生文本),复用宿主模型配置
|
||||
// AI 生成能力(生图/生文本/生音频),复用宿主模型配置
|
||||
ai: CanvasPluginAi;
|
||||
// 打开/关闭本节点下方的自定义 Panel(需在节点定义里提供 Panel)
|
||||
openPanel: () => void;
|
||||
@@ -254,10 +236,10 @@ export type CanvasNodeToolbarItem = {
|
||||
export type CanvasNodeContentProps = { ctx: CanvasNodeContext };
|
||||
export type CanvasNodePanelProps = { ctx: CanvasNodeContext; onClose: () => void };
|
||||
|
||||
// 复用宿主内置生成面板(与图片/视频/文本节点同一个组件:模型选择、参数设置、
|
||||
// 提示词库、运行/停止状态全部一致)。声明它即可获得完整生成体验,无需自写面板。
|
||||
// 复用宿主内置生成面板(与图片/文本/音频节点同一个组件:模型选择、参数设置、
|
||||
// 运行/停止状态全部一致)。声明它即可获得完整生成体验,无需自写面板。
|
||||
export type CanvasBuiltinPanelConfig = {
|
||||
mode: "image" | "video" | "text" | "audio"; // 生成类型,决定面板里的模型/设置项
|
||||
mode: "image" | "text" | "audio"; // 生成类型,决定面板里的模型/设置项
|
||||
// 提交给模型前自动拼在用户提示词前面的固定前缀(如全景图的 equirectangular 约束)
|
||||
promptPrefix?: string;
|
||||
// true(默认)时生成结果写回本节点自身 metadata.content;false 则按内置逻辑生成到下游新节点
|
||||
@@ -323,7 +305,7 @@ export type CanvasPlugin = {
|
||||
version: string;
|
||||
description?: string;
|
||||
minAppVersion?: string;
|
||||
css?: string; // 插件样式,启用时自动注入、卸载/禁用时自动清理
|
||||
css?: string; // 插件样式,注册时注入,集成释放时清理
|
||||
nodes: CanvasNodeDefinition[];
|
||||
setup?: (app: CanvasPluginApp) => void | (() => void);
|
||||
};
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
# 便利贴节点插件
|
||||
|
||||
Infinite Canvas 画布节点插件:彩色便利贴,可换色、编辑,并演示用 `ctx.applyOps` 衍生一个连到自己的文本节点(节点间交互示例)。
|
||||
MiragenFlow 画布节点插件:彩色便利贴,可换色、编辑,并演示用 `ctx.applyOps` 衍生一个连到自己的文本节点(节点间交互示例)。
|
||||
|
||||
## 构建
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build # 产物 dist/sticky-note.js,并同步到 web/public/plugins/sticky-note.js
|
||||
npm run build # 产物 dist/sticky-note.js
|
||||
npm run dev # watch
|
||||
```
|
||||
|
||||
## 安装
|
||||
|
||||
画布 → 左上菜单「节点插件」→ 安装 URL 填 `/plugins/sticky-note.js`(或托管后的公网 URL)。
|
||||
|
||||
插件契约见 `plugins/canvas/README.md`。
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { buildPlugin } from "@infinite-canvas/plugin-sdk/build";
|
||||
import { buildPlugin } from "@miragenflow/plugin-sdk/build";
|
||||
|
||||
await buildPlugin(import.meta.url);
|
||||
|
||||
+3
-3
@@ -8,13 +8,13 @@
|
||||
"name": "canvas-plugin-sticky-note",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@infinite-canvas/plugin-sdk": "file:../sdk",
|
||||
"@miragenflow/plugin-sdk": "file:../sdk",
|
||||
"@types/react": "19.1.12",
|
||||
"typescript": "^5"
|
||||
}
|
||||
},
|
||||
"../sdk": {
|
||||
"name": "@infinite-canvas/plugin-sdk",
|
||||
"name": "@miragenflow/plugin-sdk",
|
||||
"version": "0.1.0",
|
||||
"dev": true,
|
||||
"devDependencies": {
|
||||
@@ -26,7 +26,7 @@
|
||||
"@types/react": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@infinite-canvas/plugin-sdk": {
|
||||
"node_modules/@miragenflow/plugin-sdk": {
|
||||
"resolved": "../sdk",
|
||||
"link": true
|
||||
},
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Infinite Canvas 便利贴节点插件",
|
||||
"description": "MiragenFlow 便利贴节点插件",
|
||||
"scripts": {
|
||||
"build": "node build.mjs",
|
||||
"dev": "node build.mjs --watch",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@infinite-canvas/plugin-sdk": "file:../sdk",
|
||||
"@miragenflow/plugin-sdk": "file:../sdk",
|
||||
"@types/react": "19.1.12",
|
||||
"typescript": "^5"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// 便利贴节点:纯展示便利贴——整块可拖动、双击编辑、右上角自选颜色。
|
||||
// 不再声明 resource(避免宿主在右上角显示「文本N」资源角标),也不再衍生节点。
|
||||
import { definePlugin, useEffect, useRef, useState } from "@infinite-canvas/plugin-sdk";
|
||||
import type { CanvasNodeContentProps } from "@infinite-canvas/plugin-sdk";
|
||||
import { definePlugin, useEffect, useRef, useState } from "@miragenflow/plugin-sdk";
|
||||
import type { CanvasNodeContentProps } from "@miragenflow/plugin-sdk";
|
||||
|
||||
// 预设便签色(点选切换),并额外提供自定义取色
|
||||
const PRESET_COLORS = ["#fde68a", "#fca5a5", "#fdba74", "#a7f3d0", "#bfdbfe", "#ddd6fe", "#f9a8d4", "#e7e5e4"];
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "@infinite-canvas/plugin-sdk",
|
||||
"jsxImportSource": "@miragenflow/plugin-sdk",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true,
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
# SVG 节点插件
|
||||
|
||||
Infinite Canvas 画布节点插件:编辑与渲染 SVG,无自身内容时自动取上游文本节点里的 SVG 源码。
|
||||
MiragenFlow 画布节点插件:编辑与渲染 SVG,无自身内容时自动取上游文本节点里的 SVG 源码。
|
||||
|
||||
## 构建
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build # 产物 dist/svg.js,并同步到 web/public/plugins/svg.js
|
||||
npm run build # 产物 dist/svg.js
|
||||
npm run dev # watch
|
||||
```
|
||||
|
||||
## 安装
|
||||
|
||||
画布 → 左上菜单「节点插件」→ 安装 URL 填 `/plugins/svg.js`(或托管后的公网 URL)。
|
||||
|
||||
插件契约见 `plugins/canvas/README.md`。
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { buildPlugin } from "@infinite-canvas/plugin-sdk/build";
|
||||
import { buildPlugin } from "@miragenflow/plugin-sdk/build";
|
||||
|
||||
await buildPlugin(import.meta.url);
|
||||
|
||||
Generated
+3
-3
@@ -8,13 +8,13 @@
|
||||
"name": "canvas-plugin-svg",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@infinite-canvas/plugin-sdk": "file:../sdk",
|
||||
"@miragenflow/plugin-sdk": "file:../sdk",
|
||||
"@types/react": "19.1.12",
|
||||
"typescript": "^5"
|
||||
}
|
||||
},
|
||||
"../sdk": {
|
||||
"name": "@infinite-canvas/plugin-sdk",
|
||||
"name": "@miragenflow/plugin-sdk",
|
||||
"version": "0.1.0",
|
||||
"dev": true,
|
||||
"devDependencies": {
|
||||
@@ -26,7 +26,7 @@
|
||||
"@types/react": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@infinite-canvas/plugin-sdk": {
|
||||
"node_modules/@miragenflow/plugin-sdk": {
|
||||
"resolved": "../sdk",
|
||||
"link": true
|
||||
},
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Infinite Canvas SVG 节点插件",
|
||||
"description": "MiragenFlow SVG 节点插件",
|
||||
"scripts": {
|
||||
"build": "node build.mjs",
|
||||
"dev": "node build.mjs --watch",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@infinite-canvas/plugin-sdk": "file:../sdk",
|
||||
"@miragenflow/plugin-sdk": "file:../sdk",
|
||||
"@types/react": "19.1.12",
|
||||
"typescript": "^5"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SVG 节点:编辑与渲染 SVG,透明背景直接融入画布;无自身内容时可取上游文本节点里的 SVG 源码。
|
||||
import { definePlugin, useEffect, useRef, useState } from "@infinite-canvas/plugin-sdk";
|
||||
import type { CanvasNodeContentProps } from "@infinite-canvas/plugin-sdk";
|
||||
import { definePlugin, useEffect, useRef, useState } from "@miragenflow/plugin-sdk";
|
||||
import type { CanvasNodeContentProps } from "@miragenflow/plugin-sdk";
|
||||
|
||||
function SvgContent({ ctx }: CanvasNodeContentProps) {
|
||||
const [editing, setEditing] = useState(false);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "@infinite-canvas/plugin-sdk",
|
||||
"jsxImportSource": "@miragenflow/plugin-sdk",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true,
|
||||
|
||||
@@ -9,11 +9,11 @@ cp -r plugins/canvas/template plugins/canvas/my-plugin
|
||||
cd plugins/canvas/my-plugin
|
||||
# 改 package.json 的 name;改 src/index.tsx 里的 id / name / type
|
||||
npm install
|
||||
npm run dev # watch 构建,产物同步到 web/public/plugins/my-plugin.js
|
||||
npm run dev # watch 构建到 dist/my-plugin.js
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
然后启动画布 `web`,在「节点插件」管理器里启用(自动发现)即可看到你的节点。发布时把 `dist/<name>.js` 托管到任意静态地址,用户填 URL 安装。
|
||||
然后将需要的节点定义在应用源码中注册,启动画布 `web` 即可看到节点。扩展随应用源码和构建产物发布,不通过 URL 安装。
|
||||
|
||||
> 产物名取**目录名**(`my-plugin/` → `my-plugin.js`),所以复制后记得把目录改成你的插件名。
|
||||
|
||||
@@ -22,4 +22,4 @@ npm run typecheck
|
||||
- `src/index.tsx` 里 `Content({ ctx })` 的节点 UI 与逻辑;
|
||||
- `definePlugin({...})` 里的 `id` / `nodes[].type` / `defaultSize` 等元信息。
|
||||
|
||||
类型、JSX、宿主 React、构建都由 `@infinite-canvas/plugin-sdk` 提供,写 TSX 全程有补全。`ctx` 的完整能力见 `plugins/canvas/README.md` 与 SDK 的类型定义。
|
||||
类型、JSX、宿主 React、构建都由 `@miragenflow/plugin-sdk` 提供,写 TSX 全程有补全。`ctx` 的完整能力见 `plugins/canvas/README.md` 与 SDK 的类型定义。
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { buildPlugin } from "@infinite-canvas/plugin-sdk/build";
|
||||
import { buildPlugin } from "@miragenflow/plugin-sdk/build";
|
||||
|
||||
await buildPlugin(import.meta.url);
|
||||
|
||||
+3
-3
@@ -8,13 +8,13 @@
|
||||
"name": "canvas-plugin-template",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@infinite-canvas/plugin-sdk": "file:../sdk",
|
||||
"@miragenflow/plugin-sdk": "file:../sdk",
|
||||
"@types/react": "19.1.12",
|
||||
"typescript": "^5"
|
||||
}
|
||||
},
|
||||
"../sdk": {
|
||||
"name": "@infinite-canvas/plugin-sdk",
|
||||
"name": "@miragenflow/plugin-sdk",
|
||||
"version": "0.1.0",
|
||||
"dev": true,
|
||||
"devDependencies": {
|
||||
@@ -26,7 +26,7 @@
|
||||
"@types/react": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@infinite-canvas/plugin-sdk": {
|
||||
"node_modules/@miragenflow/plugin-sdk": {
|
||||
"resolved": "../sdk",
|
||||
"link": true
|
||||
},
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Infinite Canvas 插件起步模板(TypeScript + SDK)",
|
||||
"description": "MiragenFlow 插件起步模板(TypeScript + SDK)",
|
||||
"scripts": {
|
||||
"build": "node build.mjs",
|
||||
"dev": "node build.mjs --watch",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@infinite-canvas/plugin-sdk": "file:../sdk",
|
||||
"@miragenflow/plugin-sdk": "file:../sdk",
|
||||
"@types/react": "19.1.12",
|
||||
"typescript": "^5"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// 插件模板:复制本目录 → 改 id/name/type → 写你的节点。
|
||||
// 这是一个演示节点:编辑文本、跟随主题、读取上游节点、用画布指令衍生新节点。
|
||||
import { definePlugin, useState } from "@infinite-canvas/plugin-sdk";
|
||||
import type { CanvasNodeContentProps } from "@infinite-canvas/plugin-sdk";
|
||||
import { definePlugin, useState } from "@miragenflow/plugin-sdk";
|
||||
import type { CanvasNodeContentProps } from "@miragenflow/plugin-sdk";
|
||||
|
||||
function TemplateContent({ ctx }: CanvasNodeContentProps) {
|
||||
const [editing, setEditing] = useState(false);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "@infinite-canvas/plugin-sdk",
|
||||
"jsxImportSource": "@miragenflow/plugin-sdk",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true,
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"name": "infinite-canvas",
|
||||
"version": "0.1.0",
|
||||
"description": "Use Codex to read and operate Infinite Canvas through the local Canvas Agent MCP server.",
|
||||
"author": {
|
||||
"name": "basketikun",
|
||||
"url": "https://canvas.best/"
|
||||
},
|
||||
"homepage": "https://canvas.best/",
|
||||
"repository": "https://github.com/basketikun/infinite-canvas",
|
||||
"license": "AGPL-3.0",
|
||||
"keywords": ["infinite-canvas", "canvas", "mcp", "agent"],
|
||||
"skills": "./skills/",
|
||||
"mcpServers": "./.mcp.json",
|
||||
"interface": {
|
||||
"displayName": "Infinite Canvas",
|
||||
"shortDescription": "Use Codex to operate the current Infinite Canvas board.",
|
||||
"longDescription": "Connects Codex to the local Canvas Agent MCP server so Codex can inspect the current Infinite Canvas board, create nodes, connect flows, and trigger generation through the web canvas confirmation flow.",
|
||||
"developerName": "basketikun",
|
||||
"category": "Productivity",
|
||||
"capabilities": ["Canvas", "MCP", "Creative Workflow"],
|
||||
"websiteURL": "https://canvas.best/",
|
||||
"defaultPrompt": [
|
||||
"打开 Infinite Canvas",
|
||||
"读取当前画布并总结节点结构",
|
||||
"根据选中节点创建一组生图提示词"
|
||||
],
|
||||
"brandColor": "#2563EB",
|
||||
"composerIcon": "./assets/icon.png",
|
||||
"logo": "./assets/icon.png"
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"infinite-canvas": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@basketikun/canvas-agent", "mcp"],
|
||||
"startup_timeout_sec": 20,
|
||||
"tool_timeout_sec": 90
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
# Infinite Canvas Codex Plugin
|
||||
|
||||
让 Codex 可以打开并操作 Infinite Canvas。
|
||||
|
||||
## 安装
|
||||
|
||||
macOS / Linux:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/basketikun/infinite-canvas.git
|
||||
cd infinite-canvas
|
||||
codex plugin marketplace add "$(pwd)"
|
||||
codex plugin add infinite-canvas@infinite-canvas-local
|
||||
```
|
||||
|
||||
Windows PowerShell:
|
||||
|
||||
```powershell
|
||||
git clone https://github.com/basketikun/infinite-canvas.git
|
||||
cd infinite-canvas
|
||||
codex plugin marketplace add "$PWD"
|
||||
codex plugin add infinite-canvas@infinite-canvas-local
|
||||
```
|
||||
|
||||
Windows CMD 将 `$PWD` 替换为 `%cd%`。
|
||||
|
||||
安装后新建一个 Codex 任务,然后输入:
|
||||
|
||||
```text
|
||||
帮我打开并连接到 Infinite Canvas
|
||||
```
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1,27 +0,0 @@
|
||||
---
|
||||
name: canvas
|
||||
description: 操作 Infinite Canvas 当前网页画布,读取节点、选区、创建文本节点、创建生成流程、连接节点或触发生成。
|
||||
---
|
||||
|
||||
# Infinite Canvas
|
||||
|
||||
你正在帮助用户操作 Infinite Canvas 网页画布。需要理解或改动画布时,优先使用已配置的 `infinite-canvas` MCP 工具;不要让用户手动复制 JSON、URL 或 token。
|
||||
|
||||
## 工作流
|
||||
|
||||
- 如果用户还没有打开或连接网页画布,使用 `open-canvas` 技能打开 Infinite Canvas,不要要求用户手动复制 URL 或 token。
|
||||
- 操作前先用 `canvas_get_state` 读取当前画布;如果用户明确提到选中内容、当前节点或“这个”,先用 `canvas_get_selection`。
|
||||
- 创建单个文本内容优先用 `canvas_create_text_node`。
|
||||
- 创建生成内容优先用 `canvas_generate_text`、`canvas_generate_image`、`canvas_generate_video`、`canvas_generate_audio`。
|
||||
- 需要把提示词、配置和生成节点串成流程时,使用 `canvas_create_generation_flow` 或项目已有的流程工具。
|
||||
- 需要批量增删改、移动、连接节点或设置视口时,使用 `canvas_apply_ops`。
|
||||
- 不要模拟鼠标点击,不要要求用户手动复制 JSON。
|
||||
- 写入画布的操作会由网页侧边栏做二次确认,按当前工具结果继续推进即可。
|
||||
|
||||
## 风格
|
||||
|
||||
- 页面文案和画布节点内容默认使用中文。
|
||||
- 生成节点、配置节点和提示词节点要保持结构清晰,方便用户继续编辑。
|
||||
- 批量创建节点时注意给节点留出间距,不要堆叠在同一个位置。
|
||||
- 图片、视频、音频等媒体节点默认保留原始比例;只有用户明确要求自由变形时才改变比例。
|
||||
- 生成流程尽量少而清楚,优先让用户一眼能看懂节点关系。
|
||||
@@ -1,58 +0,0 @@
|
||||
---
|
||||
name: open-canvas
|
||||
description: 打开 Infinite Canvas 在线或本地画布,并自动连接本地 Canvas Agent。用户要求打开、启动、进入或使用 Infinite Canvas 画布时使用。
|
||||
---
|
||||
|
||||
# Open Infinite Canvas
|
||||
|
||||
默认打开在线版。只有用户明确要求使用本地项目时,才启动本地前端。
|
||||
|
||||
## 在线版
|
||||
|
||||
1. 启动本地 Canvas Agent 并保持运行:
|
||||
|
||||
```bash
|
||||
npx -y @basketikun/canvas-agent
|
||||
```
|
||||
|
||||
2. 从启动输出取得 `Local URL` 和 `Connect token`。
|
||||
|
||||
3. 在 Codex 右侧浏览器打开:
|
||||
|
||||
```text
|
||||
https://canvas.best/canvas?mode=new&agentUrl=<Local URL>&agentToken=<Connect token>
|
||||
```
|
||||
|
||||
## 本地版
|
||||
|
||||
1. 在 Infinite Canvas 项目中启动前端,并使用 Vite 输出的 `Local` 地址:
|
||||
|
||||
```bash
|
||||
cd web
|
||||
bun install
|
||||
bun run dev
|
||||
```
|
||||
|
||||
2. 启动本地 Canvas Agent:
|
||||
|
||||
```bash
|
||||
npx -y @basketikun/canvas-agent
|
||||
```
|
||||
|
||||
3. 从启动输出取得 `Local URL` 和 `Connect token`,在 Codex 右侧浏览器打开:
|
||||
|
||||
```text
|
||||
<Vite Local 地址>/canvas?mode=new&agentUrl=<Local URL>&agentToken=<Connect token>
|
||||
```
|
||||
|
||||
## MCP 与连接地址
|
||||
|
||||
插件在新的 Codex 任务中加载时会自动启动 `npx -y @basketikun/canvas-agent mcp`。这个 MCP 进程负责提供画布工具,不提供网页连接服务;
|
||||
上面启动的普通 Canvas Agent 负责提供 `Local URL` 和 `Connect token`。两个进程读取同一份本地配置,因此不需要用户手动填写地址或 token。
|
||||
|
||||
## 打开模式
|
||||
|
||||
用户没有明确指定打开方式时,始终使用 `mode=new` 新建画布。只有用户明确要求时才替换为:
|
||||
|
||||
- 最近画布:`mode=recent`
|
||||
- 自己选择:`mode=choose`
|
||||
Reference in New Issue
Block a user