Ghost @tryghost/kg-default-cards 10.3.4:移除网页端视频卡片对 spacergif.org 的第三方网络请求
发布时间:2026/9/7 5:22:58 锦皓数字建站

Ghost tryghost/kg-default-cards 10.3.4移除网页端视频卡片对 spacergif.org 的第三方网络请求【免费下载链接】GhostIndependent technology for modern publishing, memberships, subscriptions and newsletters.项目地址: https://gitcode.com/GitHub_Trending/gh/Ghost本文以 changeset 发布说明 为主线解析 Ghost 编辑器 Mobiledoc 卡片包tryghost/kg-default-cards在 10.3.4 版本中的补丁变更网页端渲染的视频卡片不再请求第三方占位图服务 spacergif.org改用本地透明像素占位而邮件端渲染保持不变。读完本文你将理解这一改动的动机、在源码中的具体实现透明 GIF data URI、CSS aspect-ratio 撑盒、缩略图经 CSS background 透出、为什么邮件端仍保留 spacergif 依赖以及测试用例如何锁定这一行为。变更内容一条 Patch 说明背后的工程考量changeset 文档对 10.3.4 的完整描述只有一句话Removed a third-party network request (spacergif.org) from the web-rendered video cards poster image, replacing it with a local transparent placeholder. Email rendering is unaffected.把它拆开看包含三个关键信息改动对象是“网页端渲染”web-rendered的视频卡片 poster 图——即浏览器里渲染video元素时挂在poster属性上的占位图改动方式是“替换为本地透明占位”local transparent placeholder不再访问spacergif.org这个第三方服务邮件渲染Email rendering不受影响——这是本次变更的明确边界。这类变更在发布说明中虽然只有几行但从源码看它的意义在于网页端视频卡片过去依赖一个外部占位图服务来控制播放器在缩略图加载前的初始高度一旦该服务不可达、响应慢或被 CSP/网络策略拦截页面就可能产生布局抖动CLS或不可控的外部请求。把它替换成内联的 1×1 透明 GIF data URI 后网页端视频卡片的渲染对任何第三方网络不再有任何依赖。该补丁随后随10.3.5版本发布见 koenig/kg-default-cards/package.json 中当前版本10.3.5。背景tryghost/kg-default-cards包与视频卡片tryghost/kg-default-cards是 Ghost 编辑器Koenig的 Mobiledoc 卡片定义包。根据 README该包已被标记为 LegacyLegacy: this package supports posts that have never been converted from Mobiledoc. New editor work belongs in the Lexical packages.也就是说它服务于尚未从 Mobiledoc 转换的存量文章而新的编辑器工作属于 Lexical 系列包。包的核心 API 是从入口导出的卡片数组每张卡片暴露name、type和render函数含 URL 的卡片还暴露absoluteToRelative、relativeToAbsolute、toTransformReady三个 URL 转换钩子Ghost 在存取内容时调用它们完成相对/绝对路径与__GHOST_URL__占位符之间的转换。本次改动涉及的卡片是video卡片其实现位于 koenig/kg-default-cards/src/cards/video.ts。该卡片的 payload 结构如下源码中的 TypeScript 接口interface VideoPayload { src?: string; // 视频地址 loop?: boolean; // 是否循环播放 width?: number; // 视频宽度 height?: number; // 视频高度 cardWidth?: string; // 卡片宽度如 wide映射为 kg-width-* 类 caption?: string; // 说明文字HTML 片段 customThumbnailSrc?: string; // 自定义缩略图 thumbnailSrc?: string; // 默认缩略图 }核心实现一透明像素 data URI 取代 spacergif 占位图改动后的 video.ts 顶部定义了替换 spacergif 的核心常量与注释// A transparent 1x1 GIF, used as a local placeholder poster for the web video // element so no third-party request (e.g. spacergif.org) is needed. The video // element has an explicit CSS aspect ratio, so the browser scales the poster // invisibly to fill the videos box while the real thumbnail shows through via // the CSS background on the element. const TRANSPARENT_PIXEL_SRC data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7;这段注释把原理讲得很清楚可以归纳为三步poster 本身不再承担“视觉”职责poster只是一个 1×1 的透明 GIF内联 data URI零网络请求盒子大小由 CSSaspect-ratio决定video元素带有显式的aspect-ratio内联样式浏览器据此撑出正确比例的视频框并把透明 poster 不可见地缩放填满真正的缩略图通过 CSSbackground透出缩略图地址customThumbnailSrc || thumbnailSrc被写进元素的background样式以50% 50% / cover no-repeat居中覆盖。网页端模板中的对应行是video src{{payload.src}} poster{{posterSpacerSrc}} width{{payload.width}} height{{payload.height}}{{#if payload.loop}} loop autoplay muted{{/if}} playsinline preloadmetadata styleaspect-ratio: {{aspectRatio}}; background: transparent url({{thumbnailSrc}}) 50% 50% / cover no-repeat; /其中posterSpacerSrc在模板数据中被固定为那个透明像素const templateData { /* ... */ aspectRatio: getAspectRatio(payload.width, payload.height), posterSpacerSrc: TRANSPARENT_PIXEL_SRC, emailSpacerSrc: https://img.spacergif.org/v1/${emailSpacerWidth}x${emailSpacerHeight}/0a/spacer.png, /* ... */ };getAspectRatio的取值逻辑也值得一看当width和height都是大于 0 的有限数值时输出${width} / ${height}否则回退到16 / 9function getAspectRatio(width?: number | null, height?: number | null) { if ( typeof width number Number.isFinite(width) width 0 typeof height number Number.isFinite(height) height 0 ) { return ${width} / ${height}; } return 16 / 9; }这解释了旧方案为什么需要 spacergif在邮件这类不支持aspect-ratio的环境里占位图的真实像素尺寸是撑开布局的唯一手段所以必须有一个能按请求尺寸生成占位图的服务。而在浏览器端CSS 已经可以独立完成“撑盒”占位图退化成一个纯占位的透明像素即可。核心实现二为什么邮件端仍保留 spacergif.orgchangeset 明确写着 “Email rendering is unaffected”源码印证了这一点——视频卡片的render按options.target分叉成两套模板const renderTemplate options.target email ? emailTemplate : frontendTemplate; const html dedent(renderTemplate(templateData));网页端模板frontendTemplate输出自定义的video播放器播放/暂停、进度条、音量、倍速等控件由主题侧 JS 驱动poster使用TRANSPARENT_PIXEL_SRC。邮件端模板emailTemplate则是一个“点击跳回文章”的预览图结构包含两个条件注释分支!--[if !mso !vml]--分支标准邮件客户端用 table 背景图其中 25% 宽的占位单元格内放一张emailSpacerSrc图片。这张图的高度决定了整行的视觉高度因此必须是一张真实尺寸的spacer 图这正是 spacergif.org 的用途img src{{emailSpacerSrc}} alt width100% border0 styleheight: auto; opacity: 0; visibility: hidden; mso-hide: all;spacer 尺寸的计算逻辑也在这段代码里const emailTemplateMaxWidth 600; const aspectRatio (payload.width || 0) / (payload.height || 1); const emailSpacerWidth Math.round(emailTemplateMaxWidth / 4); const emailSpacerHeight Math.round(emailTemplateMaxWidth / aspectRatio);!--[if vml]分支OutlookVML 渲染引擎不支持aspect-ratio也不支持部分 CSS 背景能力所以用v:groupv:rect背景图 frame 填充v:oval半透明圆形播放按钮底座v:shape三角形播放键拼出等价的视频预览。测试用例 video.test.ts 中对邮件路径的断言甚至留下了直接注释解释了为什么邮件端不动// Outlook cant render aspect-ratio via CSS, so the email path still relies // on the third-party spacergif.org service to generate a correctly sized // spacer image - this is intentionally left untouched. expect(output).toContain(spacergif.org);从源码结构看同一套“emailSpacer spacergif”模式也存在于同包的 embed 卡片当options.target email且 embed 类型是带缩略图的video时走 table spacer 图 VML 的邮件模板其中的img srchttps://img.spacergif.org/v1/${spacerWidth}x${spacerHeight}/0a/spacer.png ...同样只在邮件路径出现。这也与 changeset 的表述自洽本次移除的是web-rendered视频卡片 poster 上的第三方请求邮件场景不在其列。测试验证三条断言锁住新行为video.test.ts 为本包的视频卡片提供了一组覆盖该补丁的单元测试核心有does not load the poster placeholder from a third-party service on web——网页端渲染输出必须不含spacergif.org且poster属性必须以data:image/gif;base64,开头const output serializer.serialize(card.render(opts)); expect(output).not.toContain(spacergif.org); expect(output).toContain(posterdata:image/gif;base64,);uses a default aspect ratio when dimensions are missing——缺失width/height时输出必须包含styleaspect-ratio: 16 / 9;即回退逻辑有回归保障renders for email target——邮件路径输出不含video标签、包含kg-video-preview跳转链接与缩略图背景且仍包含spacergif.org有意保留。此外renders、renders card widthkg-width-wide类、renders loop attributeloop属性、renders caption when providedkg-card-hascaption类与figcaption等用例覆盖了卡片类名与 payload 字段的映射transforms urls absolute to relative / relative to absolute / to transform-ready三个用例则验证了src、thumbnailSrc、customThumbnailSrc、caption四个字段在三种 URL 转换钩子下的行为例如toTransformReady会把站点内绝对 URL 归一化为__GHOST_URL__/video.mp4形式。在 monorepo 中运行这些测试的方式见 README包内pnpm test:unit跑单元测试NODE_ENVtesting vitest run --coveragepnpm test额外跑类型测试pnpm lint做 lint 检查包依赖simple-dom构造渲染文档、handlebars编译模板测试用HTMLSerializer把渲染结果序列化成字符串后做精确断言。同一修复在 Lexical 节点渲染器中的对应实现changeset 针对的是 Mobiledoc 遗留包但从源码结构看相同的“透明像素 poster CSS aspect-ratio”方案已经同步应用到 Koenig 的 Lexical 侧视频节点渲染器 video-renderer.ts// A transparent 1x1 GIF, used as a local placeholder poster for the web video // element so no third-party request (e.g. spacergif.org) is needed. ... const TRANSPARENT_PIXEL_SRC data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7;其cardTemplate网页端同样以poster${posterSpacerSrc}透明 GIF加styleaspect-ratio: ...; background: transparent url(...) 50% 50% / cover no-repeat;组合渲染而emailCardTemplate中posterSpacerSrc仍是https://img.spacergif.org/v1/${emailSpacerWidth}x${emailSpacerHeight}/0a/spacer.png并保留DEFAULT_EMAIL_ASPECT_RATIO 16 / 9回退。对应的测试 video-renderer.test.ts 中也有assert.ok(!result.html.includes(spacergif.org))网页端与assert.ok(result.html.includes(https://img.spacergif.org/v1/150x338/0a/spacer.png))邮件端这样的成对断言。这说明“网页端去第三方依赖、邮件端保留 spacer 服务”是 Ghost 在 Koenig 渲染体系内统一的工程决策而非某个包的一次性修补。适用边界与总结对使用或研读该仓库的开发者可以这样把握这次变更影响范围网页端options.target ! email的 video 卡片 HTML 输出——poster从外部 URL 变为内联 data URI其余 DOM 结构kg-video-container、播放器控件、figcaption不变不受影响options.target email的输出仍按 600px 邮件模板宽度计算 spacer 尺寸并请求 spacergif.org用于 Outlook/标准邮件客户端的视频预览撑盒行为保障16 / 9默认宽高比、kg-width-*宽度类、kg-card-hascaption说明类、loop属性等既有行为均有测试锁定版本语境该补丁记录于 changeset 文件包本身处于 Legacy 维护状态服务于未转换的 Mobiledoc 存量文章新编辑器功能开发在 Lexical 包中但相同修复已在 Lexical 侧的节点渲染器中落地。从工程视角看这次补丁是一次典型的“用平台能力替代外部服务”浏览器支持aspect-ratio之后占位图只需承担“占位”语义一个内联的 1×1 透明 GIF 即可完成任务从而消除了一次对第三方占位图服务的运行时依赖同时把“邮件端必须保留”的例外用测试注释和断言明确固化下来避免后续维护者误改邮件路径。【免费下载链接】GhostIndependent technology for modern publishing, memberships, subscriptions and newsletters.项目地址: https://gitcode.com/GitHub_Trending/gh/Ghost创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。