设计实践

开发一个 Adobe Express 加载项(add-on)“Brand Swatches”

开发一个 Adobe Express 加载项(add-on)“Brand Swatches”:在面板里粘贴品牌色值,点一下就在当前页面插入一排色块。用官方 CLI 生成项目,改造成能校验输入、记住上次色板的版本,本地构建打包。

BINBHO · 图文教程

开发一个 Adobe Express 加载项(add-on)“Brand Swatches”:在面板里粘贴品牌色值,点一下就在当前页面插入一排色块。用官方 CLI 生成项目,改造成能校验输入、记住上次色板的版本,本地构建打包。

先看清成品

已在本机用 npx @adobe/create-ccweb-add-on@3.11.0 生成项目(模板 javascript-with-document-sandbox),改完后 npm run build、npm run package 均成功并生成 dist.zip;色值解析的 Node 测试通过。本文环境没有登录 Adobe,没有在 Express 里加载运行加载项,也没有提交到市场;npm run start 的本地 HTTPS 证书配置也没有执行。

注意:只使用你有权使用的照片、音乐、字体和标志。Adobe Stock 素材与 Firefly 生成内容的商用范围以 Adobe 条款和产品内标注为准(标为 beta 的生成功能可能只限个人用途)。不要共享账号,也不要用代理或改地区的方式绕过 Adobe 的服务地区限制。

做完之后你会得到什么?

成果 说明
加载项面板 文本框 + 插入按钮 + 提示信息
文档沙箱 API insertSwatches(hexList) 在页面上画色块
共用的解析模块 UI 和沙箱都用,带测试
打包文件 dist.zip,可用于提交

需要准备哪些环境?

项目 要求 说明
Node.js 当前 LTS 版本(本文在本机 Node v22.23.3 上验证) CLI 通过 npx 运行
Adobe 账号 能登录 Express 网页版 在设置里打开 Add-on Development
浏览器 桌面版 Chrome/Edge 等 本地开发服务器默认端口 5241

文件结构

18-adobe-express-C-src/brand-swatches/
├── package.json
├── tsconfig.json
├── test_palette.mjs          # 解析测试
└── src/
    ├── index.html            # 面板 UI
    ├── manifest.json
    ├── shared/palette.js     # 解析品牌色(新增)
    ├── ui/index.js           # UI 运行时
    └── sandbox/code.js       # 文档沙箱,操作文档

第 1 步:生成项目

npx @adobe/create-ccweb-add-on brand-swatches --template javascript-with-document-sandbox
cd brand-swatches

CLI 会先问是否发送分析数据,再问 SSL 证书怎么配置(自动生成或使用已有证书)。按需选择。

检查结果:出现 src/manifest.json、src/ui/index.js、src/sandbox/code.js。

生成的 manifest.json(testId 每次不同):

{
    "testId": "ed271a45-d9f6-4c48-b8ad-4bfa3062b602",
    "name": "Brand Swatches",
    "version": "1.0.0",
    "manifestVersion": 2,
    "requirements": {
        "apps": [
            {
                "name": "Express",
                "apiVersion": 1
            }
        ]
    },
    "entryPoints": [
        {
            "type": "panel",
            "id": "panel1",
            "main": "index.html",
            "documentSandbox": "sandbox/code.js"
        }
    ]
}

documentSandbox 指向沙箱代码;UI 运行在 iframe 里,不能直接改文档,必须通过沙箱暴露的 API。

第 2 步:写共用的解析模块

src/shared/palette.js:

// 纯函数:解析用户粘贴的品牌色,供 UI 校验与沙箱绘制共用
export function parsePalette(text) {
    const ok = [], bad = [];
    for (const raw of text.split(/[\s,,;;]+/).filter(Boolean)) {
        const m = /^#?([0-9a-f]{6})$/i.exec(raw);
        if (!m) { bad.push(raw); continue; }
        const hex = "#" + m[1].toUpperCase();
        if (!ok.includes(hex)) ok.push(hex);
    }
    return { ok: ok.slice(0, 12), bad, truncated: ok.length > 12 };
}
export function hexToRgb(hex) {
    const n = parseInt(hex.slice(1), 16);
    return { red: ((n >> 16) & 255) / 255, green: ((n >> 8) & 255) / 255, blue: (n & 255) / 255, alpha: 1 };
}

第 3 步:沙箱里画色块

src/sandbox/code.js:

import addOnSandboxSdk from "add-on-sdk-document-sandbox";
import { editor } from "express-document-sdk";
import { hexToRgb } from "../shared/palette.js";

const { runtime } = addOnSandboxSdk.instance;

runtime.exposeApi({
    // 在当前页面左上角插入一排色块,每块 80×80,间距 12
    insertSwatches(hexList) {
        const parent = editor.context.insertionParent;
        hexList.forEach((hex, i) => {
            const r = editor.createRectangle();
            r.width = 80;
            r.height = 80;
            r.translation = { x: 20 + i * 92, y: 20 };
            r.fill = editor.makeColorFill(hexToRgb(hex));
            parent.children.append(r);
        });
        return hexList.length;
    }
});

editor.createRectangle()、editor.makeColorFill()、editor.context.insertionParent 都来自 Document APIs;颜色分量是 01 的小数,所以要把 0255 除以 255。

第 4 步:面板 UI

src/ui/index.js:

import addOnUISdk from "https://new.express.adobe.com/static/add-on-sdk/sdk.js";
import { parsePalette } from "../shared/palette.js";

addOnUISdk.ready.then(async () => {
    const sandbox = await addOnUISdk.instance.runtime.apiProxy("documentSandbox");
    const input = document.getElementById("colors");
    const btn = document.getElementById("insert");
    const msg = document.getElementById("msg");
    input.value = localStorage.getItem("brand-colors") || "#1F3A5F #F4B400 #FFFFFF";
    btn.addEventListener("click", async () => {
        const { ok, bad, truncated } = parsePalette(input.value);
        if (!ok.length) { msg.textContent = "没有合法色值,格式如 #1F3A5F"; return; }
        localStorage.setItem("brand-colors", input.value);
        const n = await sandbox.insertSwatches(ok);
        msg.textContent = `已插入 ${n} 个色块` + (bad.length ? `;忽略:${bad.join(" ")}` : "") + (truncated ? ";超过 12 个已截断" : "");
    });
    btn.disabled = false;
});

src/index.html 里把模板的按钮换成:

<label for="colors">品牌色(空格或逗号分隔)</label>
<textarea id="colors" rows="3"></textarea>
<button id="insert" disabled>插入色块</button>
<p id="msg"></p>

按钮默认 disabled,等 SDK 就绪、沙箱代理拿到之后才启用,避免用户点了没反应。

第 5 步:测试与构建

test_palette.mjs:

import assert from "node:assert";
import { parsePalette, hexToRgb } from "./src/shared/palette.js";
const r = parsePalette("#1f3a5f, F4B400;#1F3A5F red #12345");
assert.deepStrictEqual(r.ok, ["#1F3A5F", "#F4B400"]);
assert.deepStrictEqual(r.bad, ["red", "#12345"]);
assert.deepStrictEqual(hexToRgb("#FF0000"), { red: 1, green: 0, blue: 0, alpha: 1 });
console.log("palette tests passed");
node test_palette.mjs
npm run build
npm run package

本机实际输出:

palette tests passed
Cleaning output directory dist/ ...
Done.
Building source directory src/ to dist/ ...
Done.
Creating a zip from dist/ ...
Done.

Node 会提示“Reparsing as ES module”,这是因为 package.json 没写 "type": "module",不影响测试结果。本文没有验证加上 "type": "module" 对 CLI 构建的影响,所以保持模板原样。

第 6 步:在 Express 里加载(本文未执行)

  1. 运行 npm run start,本地服务默认在 https://localhost:5241。
  2. 在 Adobe Express 设置中打开 Add-on Development。
  3. 左侧 Add-ons → Your add-ons → Add-on testing,连接本地加载项。
  4. 粘贴 #1F3A5F #F4B400 #FFFFFF,点“插入色块”。

预期结果:页面左上角出现 3 个 80×80 色块,提示“已插入 3 个色块”。改代码后会自动重新构建并刷新。

调试示例

输入 #1f3a5f, F4B400;red #12345,提示应为:

已插入 2 个色块;忽略:red #12345

小写会统一成大写、缺 # 会补上、重复会去掉,最多 12 个。

出错了怎么办?

现象 原因 解决
浏览器提示证书不受信任 本地 HTTPS 证书没配置好 重新运行 CLI 的 SSL 设置,或按文档手动信任证书
面板按钮一直是灰的 SDK 没就绪或 apiProxy 报错 打开浏览器开发者工具看控制台
色块颜色不对 直接用了 0~255 用 hexToRgb 转成 0~1
UI 里调用 editor 报错 UI 运行时不能直接访问文档 文档操作只写在 sandbox/code.js
构建后 shared 模块找不到 路径写错 沙箱和 UI 都用 ../shared/palette.js 相对路径

模板规则

  • 文档操作只放沙箱,UI 只做输入和提示。
  • 纯逻辑抽到 shared,写测试。
  • 提交前 npm run build && npm run package,用 dist.zip 提交。

项目资源

用途 地址
Hello World 入门(创建、构建、本地测试) https://developer.adobe.com/express/add-ons/docs/guides/getting-started/hello-world
项目结构与 CLI 模板 https://developer.adobe.com/express/add-ons/docs/guides/getting-started/addon-project-anatomy
Document APIs https://developer.adobe.com/express/add-ons/docs/references/document-sandbox/document-apis/
create-ccweb-add-on 源码 https://github.com/adobe/create-ccweb-add-on

这个加载项只做一件小事,但覆盖了 Express 加载项开发的完整链路:UI 运行时收输入,通过代理调用文档沙箱,沙箱用 Document APIs 改文档。把这条链路跑通,再做更复杂的功能就只是加 API。