ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

HyperFrames SDK 内嵌覆盖模式实战:模板基座之上只保存每个实例的差异 Delta

HyperFrames SDK 内嵌覆盖模式实战:模板基座之上只保存每个实例的差异 Delta HyperFrames SDK 内嵌覆盖模式实战模板基座之上只保存每个实例的差异 Delta【免费下载链接】hyperframesWrite HTML. Render video. Built for agents.项目地址: https://gitcode.com/GitHub_Trending/hy/hyperframes如果你在做模板驱动的产品——一份基座 composition HTML 文件被很多用户实例共享每个实例只改动标题、logo、颜色等少量元素——那么 HyperFrames SDK 的内嵌覆盖模式Embedded Override Mode就是官方给出的持久化方案你在openComposition时传入存储的 deltaOverrideSetSDK 把它一次性叠加到基座模板上之后所有新编辑继续累积进同一个 delta基座文件本身始终不被修改。你的数据库里只需要保存这个稀疏的稀疏 map而不是整份 HTML。文档中给出的真实用例是画布嵌入类产品例如 AI Studio把 composition 内的编辑持久化为一个稳定、可重新打开的 delta。本文的任务在 Node.js 环境中用hyperframes/sdk完成“用存储的 delta 打开实例 → 编辑 → 取回新 delta → 保存 → 宿主接管撤销”这条完整链路并说明每一步的验证方式和边界。三种会话模式什么条件激活内嵌覆盖模式openComposition(html, opts?)是所有 SDK 会话的唯一入口。它解析 composition HTML给缺少data-hf-id的元素打上稳定的hf-id然后返回一个Composition会话。同一个函数覆盖三种部署模式区别完全由opts决定来源docs/sdk/reference/open-composition.mdx模式激活条件行为Standalone默认不传overridesSDK 自己持有 history 和持久化Embedded / override mode传入overridesSDK 把 delta 叠加到基座 HTML 上后续编辑累积进 override sethistory 默认关闭SDK 只发 patch 事件不持有撤销栈Headless / agent既无persist也无overrides纯内存变换完成后调用serialize()并丢弃会话也就是说传递overrides本身就激活了内嵌模式SDK 发出 patch 而不是自己管撤销栈宿主只存 delta、每次打开时重放基座模板因此可以独立更新。准备安装 SDK 与准备基座模板按 SDK 快速入门 安装包npm install hyperframes/sdk你还需要两个东西基座模板 HTML 字符串下文代码里的templateHtml/baseTemplateHtml一份完整的 composition HTML。该实例已存储的 delta下文代码里的storedOverrides/savedOverrides从你自己的数据库或存储层加载的OverrideSet。delta 的键必须引用模板里元素实际的hf-id值如hf-title.text。如果模板元素没有预置data-hf-idSDK 会在打开时自动打上稳定 id之后的编辑就能持续命中同一批元素。注意openComposition是异步的——它返回前会先跑一遍 id 打标所以必须await。用存储的 delta 打开实例把存储的 delta 作为overrides传给openCompositionSDK 会在会话开始前把它叠加到基座模板上会话立刻暴露用户上次编辑后的精确状态import { openComposition } from hyperframes/sdk; // Stored delta — loaded from your database or storage layer. const storedOverrides { hf-title.text: Acme Corp Launch, hf-logo.attr.src: /customers/acme/logo.png, hf-subtitle.style.color: #0EA5E9, }; const comp await openComposition(templateHtml, { overrides: storedOverrides, history: false, // host owns undo — see below });判断下一步openComposition返回后调用comp.getElements()它会反映出已被覆盖的状态。每个ElementSnapshot带有id、tag、inlineStyles、attributes、text等字段你可以逐个核对目标元素例如hf-title的text是否等于存储的Acme Corp Launch。关于history: false内嵌模式下 SDK 本来就默认关闭 history任何带overrides打开的会话都是如此所以通常不用做任何事。显式传history: false只用于两种场合——在独立代码路径里让意图更明显或者当调用点可能传也可能不传overrides时作为保险。此时comp.undo()、comp.redo()、canUndo()、canRedo()都会变成 no-op / 返回false这也是内嵌会话的一种可核对的行为。读懂 OverrideSet 的键格式OverrideSet的类型是一个稀疏 map来源docs/sdk/reference/types.mdxtype OverrideSet Record string, string | number | boolean | Recordstring, unknown | null ;键遵循hfId.prop.path模式常见形态如下来源docs/sdk/guides/embedded-override-mode.mdxKey含义hf-title.text元素hf-title的内部文本hf-logo.attr.srchf-logo上的src属性hf-x.style.fontSizehf-x上的内联fontSize样式hf-card且值为null删除标记——元素被用户删掉了三个需要特别留意的规则null是删除标记。SDK 序列化 composition 时会完全省略该元素。这让宿主能区分“从未动过”键不存在和“用户删了”键存在、值null。子 composition 用 scoped IDhost 与 leaf 用/分隔如hf-host/hf-leaf.text。顶层元素的scopedId id内嵌子 composition 的元素必须用scopedId寻址。var.{id}键存变量覆盖来自setVariableValue。字体和图片变量的值是对象而不是字符串import type { FontValue, ImageValue } from hyperframes/sdk; // Font variable — name is the CSS font-family, source is the stylesheet URL. const fontOverride: FontValue { name: Inter, source: https://fonts.googleapis.com/css2?familyInter:wght400;700displayswap, }; // Image variable — url is the src; alt and fit are optional. const imageOverride: ImageValue { url: /customers/acme/hero.jpg, alt: Acme hero image, fit: cover, }; comp.setVariableValue(brand-font, fontOverride); comp.setVariableValue(hero-image, imageOverride); const overrides comp.getOverrides(); // { // var.brand-font: { name: Inter, source: https://… }, // var.hero-image: { url: /customers/acme/hero.jpg, alt: …, fit: cover }, // }因为值联合类型里包含Recordstring, unknown代码读取var.*键的值时必须先做类型收窄再当标量用——对象值在任何接受unknown的位置都能通过类型检查。编辑后取回新的 delta 并保存getOverrides()返回当前 override set 的浅拷贝可以直接序列化并落库。之后的每次编辑都会自动累积进同一个 deltacomp.setText(hf-cta, Get started free); comp.setStyle(hf-cta, { backgroundColor: #22C55E }); const nextOverrides comp.getOverrides(); // { hf-title.text: Acme Corp Launch, ..., hf-cta.text: Get started free, ... } await db.saveOverrides(sessionId, nextOverrides);db是文档示例中的宿主存储层占位不是 SDK API——替换成你自己的数据库写入。下一次打开会话时把nextOverrides再作为overrides传入用户看到的就是自己上次离开时的精确状态。这是该模式的完整闭环只存 delta不存整份 HTML基座模板可以独立演进。openComposition 参考 中的内嵌模式示例还展示了会话收尾保存 delta 后调用comp.dispose()。dispose()会拆掉 preview 监听、停止 persist 队列并清空所有事件处理器文档明确建议会话结束时总是调用它跳过会造成 preview 适配器监听器泄漏。dispose()之后Composition对象即失效继续调用会是 no-op 或报错。宿主接管撤销patch 事件与 applyPatches()内嵌模式下宿主通常自己持有撤销栈。SDK 每次已提交的变更都会发出patch事件携带 RFC 6902 正向补丁patches和反向补丁inversePatches宿主需要回滚时把inversePatches通过applyPatches()重放回 SDKSDK 会把补丁应用到活动文档、更新内部 override set并发出一个打上ORIGIN_APPLY_PATCHES标记的patch事件来源docs/sdk/guides/undo-redo-and-patches.mdx。必须做的防回环保护在patch监听器里跳过origin ORIGIN_APPLY_PATCHES的事件否则宿主每次applyPatches()都会往自己栈里再压一条新记录形成无限循环。文档对此有明确的 Warning 级别提示import { ORIGIN_APPLY_PATCHES } from hyperframes/sdk; // Host undo stack entry — inversePatches came from a prior patch event. function hostUndo(inversePatches: JsonPatchOp[]) { comp.applyPatches(inversePatches); } // Guard against undo loops — skip ORIGIN_APPLY_PATCHES events in the patch listener. comp.on(patch, ({ patches, inversePatches, origin }) { if (origin ORIGIN_APPLY_PATCHES) return; // already from host undo — do not re-push hostHistory.push({ patches, inversePatches }); });几个与撤销流程相关的细节ORIGIN_APPLY_PATCHES的值是命名空间字符串hyperframes/sdk:applyPatches刻意不用Symbol以便跨越postMessage、structured clone 和 JSON 往返宿主跨 frame / worker 转发 patch 事件时需要。JsonPatchOp只有add/remove/replace三种 op。SDK 从不发出move、copy、testapplyPatches()遇到这三种也会静默忽略——宿主回填补丁时必须限定在这三者内。applyPatches()接受可选的opts.origin覆盖事件标记但默认值ORIGIN_APPLY_PATCHES就是宿主 undo/redo 流程的预期值。patch事件的formatVersion恒为1文档建议宿主启动时检查一次并拒绝未知版本。如果某次撤销移除的是一个var.id覆盖需要恢复变量默认值时用getVariableValue(id, { base: true })读取打开时的原始 default而不是被setVariableValue折叠过的值。完整会话示例下面是 内嵌覆盖模式指南 给出的完整示例打开会话、宿主撤销栈记录、undo/redo 重放、保存时只返回 delta。hostHistory与historyIndex都是宿主侧状态不属于 SDK API。import { openComposition, ORIGIN_APPLY_PATCHES } from hyperframes/sdk; import type { JsonPatchOp, OverrideSet } from hyperframes/sdk; async function openUserSession( templateHtml: string, storedOverrides: OverrideSet, ) { const hostHistory: Array{ patches: readonly JsonPatchOp[]; inversePatches: readonly JsonPatchOp[] } []; let historyIndex hostHistory.length; const comp await openComposition(templateHtml, { overrides: storedOverrides, history: false, }); // Record every user edit in the host undo stack. comp.on(patch, ({ patches, inversePatches, origin }) { if (origin ORIGIN_APPLY_PATCHES) return; // Discard any redo entries ahead of the cursor. hostHistory.splice(historyIndex); hostHistory.push({ patches, inversePatches }); historyIndex hostHistory.length; }); return { comp, undo() { if (historyIndex 0) return; const { inversePatches } hostHistory[--historyIndex]; comp.applyPatches([...inversePatches]); }, redo() { if (historyIndex hostHistory.length) return; const { patches } hostHistory[historyIndex]; comp.applyPatches([...patches]); }, async save() { // Only store the delta — not the full HTML. return comp.getOverrides(); }, }; }save()的返回值就是你该落库的东西只有 delta。验证与边界文档给出的可核对点打开后comp.getElements()的快照字段text、inlineStyles、attributes与存储的 delta 一致。编辑后comp.getOverrides()是一个浅拷贝包含累积后的全部键含新的var.*键可序列化。撤销链路canUndo()/canRedo()在内嵌模式下返回falsehistory 未附加宿主靠hostHistory长度判断能否回滚applyPatches()触发的patch事件origin为ORIGIN_APPLY_PATCHES且不会重新进入宿主栈。重开把上次save()的 delta 再传入openComposition状态与上次会话离开时一致。边界与限制基座 HTML 本身永不被修改serialize()始终返回活动文档的新序列化结果。history: false只关 SDK 内部撤销栈。persist适配器是独立的——内嵌模式下宿主通常同时接管 undo 和持久化两者一般都不传给 SDK。内嵌模式与 Canvas integration 配合使用时宿主可以把 patch 事件桥接到预览 iframe跨进程转发时依赖ORIGIN_APPLY_PATCHES的字符串形态。如果这条链路已经跑通下一步可以深入 Undo, Redo, and Patches 查看完整 patch 事件契约或查 openComposition 参考 与 Types 参考 核对OverrideSet、FontValue、ImageValue、PatchEvent的完整定义。【免费下载链接】hyperframesWrite HTML. Render video. Built for agents.项目地址: https://gitcode.com/GitHub_Trending/hy/hyperframes创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表