ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

EmDash 插件自定义 Portable Text 块类型:从声明、编辑到站点渲染的完整实战

EmDash 插件自定义 Portable Text 块类型:从声明、编辑到站点渲染的完整实战 EmDash 插件自定义 Portable Text 块类型从声明、编辑到站点渲染的完整实战【免费下载链接】emdashEmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress项目地址: https://gitcode.com/gh_mirrors/emdas/emdash导读本文以 EmDash 的 Portable Text 富文本块系统为核心系统讲解如何在原生插件native plugin中通过definePlugin()声明自定义块类型使其出现在编辑器斜杠命令菜单中并可插入任意portableText字段同时深入剖析站点侧的 Astro 渲染组件如何通过componentsEntry自动装配进PortableText最终实现编辑器插入 → 数据存储 → 前端渲染的完整闭环。读完本文你将掌握块配置字段的完整语义、Block Kit 表单字段的用法、blockComponents导出约定、组件合并优先级以及沙箱插件与原生插件在此功能上的边界。适用范围与能力边界在动手之前必须先明确一条硬性约束Plugin CLI 与 registry 包无法定义 Portable Text 块类型。emdash-plugin build会对此发出警告——portableTextBlocks需要受信任模式trusted mode因此在沙箱化清单中会被直接忽略。该警告逻辑位于 packages/plugin-cli/src/bundle/api.ts当解析出的插件admin.portableTextBlocks非空时CLI 提示这些块需要 trusted mode、在沙箱插件中会被忽略。从架构上看站点侧渲染还需要一个 AstrocomponentsEntry它会在站点构建时被加载。Core 虽然可以从配置声明的标准插件描述符中转发声明式的块元数据但这条路不会让定义经由 registry 变得可移植也不会提供站点渲染器。因此实践上的准则是将自定义 Portable Text 块视作原生插件native plugin特性除非站点同时拥有描述符与渲染组件、且已验证从编辑器到渲染的完整链路否则不要走 registry 分发路线。这与 skills/creating-plugins/SKILL.md 中对何时选择原生插件的指导一致需要 Astro 渲染组件、自定义 Portable Text 块定义等功能时应使用原生插件——原生插件以站点权限运行不能从 registry 安装。声明块类型在definePlugin()中通过admin.portableTextBlocks声明块admin: { portableTextBlocks: [ { type: youtube, label: YouTube Video, icon: video, placeholder: Paste YouTube URL..., fields: [ { type: text_input, action_id: id, label: YouTube URL }, { type: text_input, action_id: title, label: Title }, { type: text_input, action_id: poster, label: Poster Image URL }, ], }, { type: codepen, label: CodePen, icon: code, placeholder: Paste CodePen URL..., }, ], }块配置字段对应源码中的PortableTextBlockConfig接口见 packages/core/src/plugins/types.ts各字段语义如下字段类型说明typestring块类型名用作 Portable Text 的_type。必填。labelstring斜杠命令菜单中显示的名称。必填。iconstring图标 key。可选。descriptionstring斜杠命令菜单中的描述。可选。placeholderstring输入框占位文本。可选。fieldsarray用于编辑 UI 的 Block Kit 表单字段。可选。categorystring斜杠菜单中的展示分组。可选默认Embeds。值得注意的增量是源码中新增的category字段插作者应选择有意义的分类如Sections、Marketing、Media、Embeds、Layout拥有相同category的块会在编辑器的斜杠菜单中被归组显示。图标命名图标支持video、code、link、link-external。未知或缺失的图标会回退到通用的立方体图标。字段Fields当声明了fields时编辑器会渲染一个 Block Kit 表单用于编辑省略fields时编辑器显示一个简单的 URL 输入框。字段使用 Block Kit 元素语法下面是一个覆盖主要元素类型的完整示例fields: [ { type: text_input, action_id: id, label: URL, placeholder: https://..., }, { type: text_input, action_id: title, label: Title }, { type: text_input, action_id: poster, label: Poster Image }, { type: number_input, action_id: start, label: Start Time (seconds) }, { type: toggle, action_id: autoplay, label: Autoplay }, { type: select, action_id: size, label: Size, options: [ { label: Small, value: small }, { label: Medium, value: medium }, { label: Large, value: large }, ], }, ];有关各元素的具体形态参见 Block Kit 参考文档。注意Portable Text 编辑器还会额外渲染repeater与media_picker两个创作类元素——不要假设共享联合类型shared union接受的每个元素都会在所有 Block Kit 表面上渲染skills/creating-plugins/references/block-kit.md 中明确说明repeater与media_picker是 admin 创作元素其中repeater的嵌套字段仅限text_input、number_input、select、toggle而media_picker会打开媒体库并把选中资源的 URL 字符串存入数据。每个字段的action_id会成为 Portable Text 块数据中的 key。其中action_id: id的字段被视为主要标识符通常是 URL。数据流用户在编辑器中输入/并选择一个块类型弹出模态框展示 Block Kit 表单若未声明fields则为简单 URL 输入框用户填写字段并提交块被插入_type设为块类型名字段值作为块属性编辑已有块时模态框会以已有数据预填充重新打开插入后的 Portable Text 输出示例{ _type: youtube, _key: abc123, id: https://youtube.com/watch?vdQw4w9WgXcQ, title: Never Gonna Give You Up, poster: https://img.youtube.com/vi/dQw4w9WgXcQ/0.jpg }站点侧渲染要在站点上渲染这些块类型需要从componentsEntry导出 Astro 组件。组件文件componentsEntry指向的模块必须导出名为blockComponents的对象其 key 与块type一一对应// src/astro/index.ts import YouTube from ./YouTube.astro; import CodePen from ./CodePen.astro; // This export name is required export const blockComponents { youtube: YouTube, codepen: CodePen, };Astro 组件每个渲染组件通过Astro.props.node接收完整的块数据包括_type、_key及各字段值--- // src/astro/YouTube.astro const { id, title, poster } Astro.props.node; // Extract video ID from URL const videoId id?.match(/(?:v|youtu\.be\/)([^])/)?.[1] ?? id; --- div classyoutube-embed iframe src{https://www.youtube-nocookie.com/embed/${videoId}} title{title || YouTube Video} allowaccelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture allowfullscreen /iframe /div插件描述符在描述符工厂中设置componentsEntry指向上面导出blockComponents的模块export function myPlugin(options {}): PluginDescriptor { return { id: my-plugin, entrypoint: my-org/my-plugin, componentsEntry: my-org/my-plugin/astro, version: 1.0.0, options, }; }包导出配置在插件的package.json中为./astro添加导出子路径确保站点构建器能解析到组件模块{ exports: { .: { types: ./dist/index.d.ts, import: ./dist/index.js }, ./admin: { types: ./dist/admin.d.ts, import: ./dist/admin.js }, ./astro: { types: ./dist/astro/index.d.ts, import: ./dist/astro/index.js } } }自动装配原理插件块组件会被自动合并进站点上的PortableText合并顺序如下EmDash 默认组件优先级最低插件块组件用户提供的组件优先级最高站点作者无需手动 import 任何东西用户组件优先于插件默认组件。这一机制的底层实现位于 packages/core/src/components/PortableText.astro组件通过mergeComponents依次合并emdashComponents、来自virtual:emdash/block-components的pluginBlockComponents以及用户传入的components形成默认 插件 用户的优先级链。而虚拟模块virtual:emdash/block-components的内容由 packages/core/src/astro/integration/virtual-modules.ts 中的generateBlockComponentsModule在构建期生成它筛选所有声明了componentsEntry的描述符为每个描述符生成import { blockComponents as bcN } from componentsEntry语句再以展开形式合并为pluginBlockComponents对象若无任何描述符声明componentsEntry则生成空对象export const pluginBlockComponents {};。这正是站点构建时加载组件入口这一要求的实现所在。完整示例一个完整的 embeds 插件同时声明块类型与站点渲染组件// src/index.ts import { definePlugin } from emdash; import type { PluginDescriptor } from emdash; export function embedsPlugin(options {}): PluginDescriptor { return { id: embeds, version: 1.0.0, entrypoint: my-org/plugin-embeds, componentsEntry: my-org/plugin-embeds/astro, options, }; } export function createPlugin() { return definePlugin({ id: embeds, version: 1.0.0, admin: { portableTextBlocks: [ { type: youtube, label: YouTube Video, icon: video, placeholder: Paste YouTube URL..., fields: [ { type: text_input, action_id: id, label: YouTube URL }, { type: text_input, action_id: title, label: Title }, { type: text_input, action_id: poster, label: Poster Image URL, }, ], }, { type: linkPreview, label: Link Preview, icon: link-external, placeholder: Paste any URL..., }, ], }, }); } export default createPlugin;// src/astro/index.ts import YouTube from ./YouTube.astro; import LinkPreview from ./LinkPreview.astro; export const blockComponents { youtube: YouTube, linkPreview: LinkPreview, };注意linkPreview未声明fields因此编辑器会为其显示简单的 URL 输入框youtube则声明了三个text_input字段编辑器会渲染完整的 Block Kit 表单。验证要点与进阶阅读从源码角度看本主题的关键验证点是类型定义PortableTextBlockConfig与PluginAdminConfig.portableTextBlocks位于 packages/core/src/plugins/types.ts组件装配generateBlockComponentsModule与虚拟模块 IDvirtual:emdash/block-components位于 packages/core/src/astro/integration/virtual-modules.ts渲染合并PortableText.astro的三层合并逻辑位于 packages/core/src/components/PortableText.astro沙箱边界emdash-plugin build对portableTextBlocks的 trusted-mode 警告位于 packages/plugin-cli/src/bundle/api.ts。由于自定义 Portable Text 块依赖 Astro 渲染组件与站点构建期装配它只能作为原生插件特性分发。若你需要编写运行在 Plugin CLI / registry 沙箱环境中的插件请参考 skills/creating-plugins/SKILL.md 中关于沙箱插件能力与声明式 Block Kit 管理界面的说明以及 Block Kit 参考文档 了解所有可用的块与元素形态。在把自定义块交付给站点前务必完整验证编辑器插入 → 数据落库 → 站点渲染这条链路确保action_id、_type与blockComponents的 key 三者严格一致。【免费下载链接】emdashEmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress项目地址: https://gitcode.com/gh_mirrors/emdas/emdash创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表