【免费下载链接】openui
The Open Standard for Generative UI
@openuidev/devtools是 OpenUI(生成式 UI 开源标准)生态中面向开发期的调试组件:它渲染一个悬浮按钮,打开OpenUI Inspect面板,列出由@openuidev/observability捕获的事件,并提供基于真实组件库的OpenUI Debug编辑器/校验工作台。本文以 packages/devtools/CHANGELOG.md 的 0.2.0 → 0.2.2 版本演进为主线,结合 packages/devtools/README.md 的接入指南与 packages/devtools/src 的源码实现,完整讲解它的 Props 配置、CDN 加载机制、单例选举、事件采集去重与 Debug 工作台原理,帮助你掌握如何在 OpenUI 应用中正确接入、自定义与深度排查生成式 UI 的运行时问题。
一、版本演进速览:0.2.0 → 0.2.2
CHANGELOG 是理解该组件演化脉络的最佳入口。三个版本分别对应「依赖契约」「交互体验」「视觉与转化」三类改进:
1.1 0.2.2:Reliability Banner CTA 拆分与 Inter 字体捆绑
- Reliability Banner 的 CTA 拆分(PR #1207):将可靠性横幅中的单一行动按钮拆分为主按钮「Get API key」(链接到 Thesys Console 的密钥管理页)与**次级按钮「View docs」**两个独立入口,让用户区分「立即获取密钥」与「先看文档」两条路径。对应实现见 packages/devtools/src/ui/ReliabilityBanner.tsx,两个
<a>分别携带cloud_banner_get_api_key与cloud_banner_view_docs归因参数。 - 捆绑 Inter 字体(PR #1208):将 Inter(latin variable subset)随 devtools widget 一起打包,使 widget 无论宿主应用使用何种字体都能以 Inter 渲染,而不再静默回退到系统字体。对应实现为 packages/devtools/src/ui/inter 目录中的
font-data.ts与index.ts,最终以INTER_FONT_FACE样式块形式注入 widget(见 OpenUIDevtoolsWidget.tsx)。
1.2 0.2.1:可关闭的本地部署提示与 Inspect 卡片间距
PR #1172 新增可关闭的本地部署提示(Deploy Hint),并统一了 Inspect 事件卡片的间距。部署提示只在本地产生「完整的响应」后出现一次(按 origin 记忆,见openui:deploy-hint:v1键),用于引导开发者执行npx @openuidev/cli@latest deploy将应用部署到 Vercel;Inspect 内部还有一个可独立关闭的部署横幅(openui:deploy-banner-dismissed:v1)。两者完整实现在 packages/devtools/src/ui/DeployHint.tsx。
1.3 0.2.0:peer 依赖改为有界测试兼容范围(破坏性变更)
0.2.0 是本组件依赖策略的分水岭:
@openuidev/devtools现在要求@openuidev/react-lang满足">=0.3.0 <0.4.0"(见 packages/devtools/package.json)。- 为何用 minor 而非 patch 升级:CHANGELOG 中给出了明确解释——
react-lang 0.2.x以^0.1.0依赖 devtools,若发布 0.1.x 补丁,会被旧的 react-lang 0.2.x 安装拉入并因 peer 不可满足而失败。升级到 0.2.0 后,旧 react-lang 继续与旧 devtools 配对;react-lang >=0.3.0才会采用新版本线,同时 devtools 0.2.0 一并更新了对@openuidev/react-lang@0.3.0的依赖。
这一策略的核心思想是:用版本线(release line)而非补丁号来表达「经过测试的兼容范围」,避免破坏性升级悄悄渗透进旧安装。
二、组件定位:开发期专属的 Inspect / Debug 工作台
从 packages/devtools/README.md 的定义看,这个包「Development-only UI widget for OpenUI apps」,由两部分组成:
- OpenUI Inspect:一个固定到屏幕角落的抽屉(drawer),实时列出 observability 事件总线捕获的事件——包括普通错误/警告、OpenUI Lang 流式事件、配额错误等,并按严重级别展示徽章计数。
- OpenUI Debug:一个针对某条流式响应的调试工作台,包含 Lang 编辑器(带宿主 CSS 的真实渲染预览)、以及 Render / Validation / Tree / JSON / Stream 五个面板,支持模拟流式回放(Stream replay)。
关键工程约束包括:
- 生产构建零渲染:widget 在
NODE_ENV === "production"下默认不渲染,除非显式传入enabled。 - 发布即 CDN:发布新版本会自动更新 jsDelivr 上的 CDN 文件,无需额外 CDN 配置;浏览器构建产物是发布包内的
dist/devtools.browser.js。
三、快速接入:自动挂载与手动挂载
3.1 自动挂载(推荐路径)
只要你的应用使用了@openuidev/react-lang,widget 就会自动出现。机制位于 packages/react-lang/src/devtoolsBootstrap.ts:
- 该模块是
@openuidev/react-lang的顶层副作用,仅在process.env.NODE_ENV === "development"时执行(严格等于"development",因此 Jest/jsdom 等NODE_ENV === "test"环境不会挂载)。 - 每个 JS realm 通过
Symbol.for("openui.devtools.autoMount")保证只自动挂载一次。 - 自动挂载时渲染
<OpenUIDevtools version="0" __autoMounted />:版本引脚固定为 major"0",这样未来破坏协议的大版本会以@1发布,而不是悄悄把每个应用拖垮;__autoMounted标记让自动实例在手动实例出现时让位。 - 生产构建时,整个块会被消费方打包器死代码消除,
@openuidev/devtools不会进入生产依赖图(react-native 入口也从不导入该模块)。
3.2 手动挂载
你也可以在宿主应用里自行挂载(例如希望自定义 Props 或固定 CDN 版本),所有 Props 都会原样转发进 CDN widget:
import { OpenUIDevtools } from "@openuidev/devtools"; function App() { return ( <> {/* your app */} <OpenUIDevtools theme="dark" position="bottom-left" maxEvents={100} /> </> ); }注意:手动挂载的实例永远胜过自动挂载实例——同一时刻只渲染一个实例。react-lang 自动挂载(以version: "0")与宿主手动挂载并存时,手动实例胜出;手动实例卸载后,自动实例自动接管(见下文单例机制)。
四、Props 完整参考与运行语义
4.1 公共 Props(CDN 引脚参数)
| Prop | 默认值 | 说明 |
|---|---|---|
version | @latest | 引脚 CDN tag:"0"(major)、"0.1"(minor)或"0.1.0"(精确)。省略则为@latest。 |
theme、position、maxEvents、errorsOnly、autoOpenOnError、enabled | 见下表 | 原样转发进 CDN widget。 |
4.2 Widget Props(转发进浏览器构建的参数)
| Prop | 默认值 | 说明 |
|---|---|---|
enabled | 仅开发环境 | 强制开/关 widget;显式传入后覆盖环境判断。 |
position | "bottom-right" | 悬浮按钮所在角:top-left/top-right/bottom-left/bottom-right。 |
maxEvents | 50 | 最多保留的事件数,超出时丢弃最旧的。 |
errorsOnly | true(README)/false(Widget 实现默认) | 仅显示 error/warning 事件,还是显示全部事件。 |
autoOpenOnError | true | 「出错自动打开抽屉」开关的初始状态。 |
theme | "light" | widget 界面主题:"light"或"dark"(可被设置菜单覆盖)。 |
细节说明:README 的 Props 表标注
errorsOnly默认true(过滤后仅保留 error/warning),而 OpenUIDevtoolsWidget.tsx 中函数签名默认值为false(显示全部)。实践建议:显式传值,避免依赖默认语义。
4.3 运行语义与设置持久化
- 启用判定:
enabled ?? (process 不存在或 NODE_ENV !== "production"),即默认仅开发环境渲染(见 OpenUIDevtoolsWidget.tsx 与 cdn.ts)。 - 设置持久化:抽屉内的「Auto-open on error」「Show errors only」「Theme」等开关存放在
localStorage的openui.devtools.config键中(见 lib/useDevtoolsConfig.ts)。SSR 水合后会重新读取存储,让上一会话的开关状态胜出;theme例外——显式传入的themeprop 优先于存储值,并被写回存储以保持设置菜单同步。editorPct(Debug 编辑器列宽百分比)同样持久化,且被限制在MIN_EDITOR_PCT与MAX_EDITOR_PCT之间。 - 主题规则:widget 主题从不从宿主页面或操作系统自动探测;优先级为
themeprop > 存储值 > 默认light。
五、CDN 浏览器构建:薄包装 + 运行时加载
npm 包入口 src/index.ts 只导出一个薄包装组件 OpenUIDevtools.tsx——它自身不渲染任何 UI,而是在useEffect中调用mountOpenUIDevtoolsFromCdn把 Props 转发进 CDN 浏览器构建,widget 随后挂载到document.body。
5.1 版本引脚与 URL 生成(cdn.ts)
normalizeCdnVersion用正则/^\d+(\.\d+){0,2}$/校验引脚,只接受 major / minor / exact 三种形态;空串视为latest,非法字符串返回null并打印警告、不挂载。browserBundleUrl拼接https://cdn.jsdelivr.net/npm/@openuidev/devtools@<tag>/dist/devtools.browser.js;对非精确版本(@latest、@0、@0.1),额外追加?t=<5分钟取整时间戳>查询参数做别名缓存失效,避免 jsDelivr 别名缓存把新发布挡住。- 加载失败绝不拖垮宿主:
Promise.all的.catch为空处理,CDN 挂载失败只意味着 widget 不出现。
5.2 浏览器构建的插槽注入(browser.ts)
dist/devtools.browser.js由 esbuild 在构建阶段通过 scripts/build-browser.mjs 生成:
react、react-dom、react/jsx-runtime、@openuidev/observability、@openuidev/react-lang全部被alias 到 src/browser-shims 的占位模块(只有lucide-react被真实打进包内),保证 CDN 文件自包含。process.env.NODE_ENV在构建时被define为"development"。- 宿主包装器加载 CDN 文件后调用
mountOpenUIDevtools,把宿主自己的React、ReactDOM、ReactDOMClient 以及loadReactLang(闭包宿主模块图的import("@openuidev/react-lang"))填入browser-shims/slots.ts定义的全局插槽,再动态import("./OpenUIDevtoolsWidget")渲染。因此浏览器构建从不自行 import@openuidev/react-lang,Debug 的解析/渲染能力完全来自宿主。 - observability 总线通过
Symbol.for("openui.observability")从globalThis取用;找不到总线时打印提示并要求先 import@openuidev/observability。
六、单例选举机制:全局唯一实例
由于自动挂载与手动挂载可能并存(甚至 ESM/CJS 双构建、多个包版本同时存在),widget 需要一个跨实例、跨 bundle 副本的选举机制,实现在 lib/singleton.ts:
- 注册表挂到
globalThis[Symbol.for("openui.devtools.singleton")],保证不同模块副本共享同一份注册表。 - 选举规则:手动挂载实例优先于自动挂载实例;同级别按挂载先后(后挂载的排队)。当前所有者卸载后,下一个候选接管。
- 测试(OpenUIDevtools.test.ts)验证了:多实例只渲染一个按钮;手动实例胜过自动实例;手动卸载后自动实例接管。
七、事件采集与去重:基于 observability 总线
widget 通过observability.listenAll订阅事件总线,核心逻辑在 OpenUIDevtoolsWidget.tsx:
- 稳定 ID 合并(lib/eventBuffer.ts):若事件
detail.id是字符串,新事件会替换同 ID 的旧事件(而非追加),再按maxEvents截断。这使流式事件的多次快照(streaming → settled)在列表中只占一行,最新状态覆盖旧状态。 - 库注册事件过滤:
isLibraryEvent(kind === "react-lang:library")的注册 ping 不会作为事件展示(lib/libraryRegistry.ts),它们只用于刷新 Debug 的组件库注册表。 - 徽章计数:仅统计
level === "error"的事件;按钮在无错误时显示 ShiroLogo,有错误时显示红底数字徽章(超过 99 显示99+)。 - 自动打开:收到 error 级事件且
autoOpen开启时,抽屉自动展开。 - 临时错误隐藏:流仍在
streaming阶段时,其携带的解析错误被视为瞬时状态而不展示;settled阶段的错误快照才作为最终诊断呈现。
八、OpenUI Inspect 面板:事件流的可视化
Inspect 抽屉(480px 固定宽度,见 OpenUIDevtoolsWidget.tsx)由头部、可靠性横幅、事件列表三部分组成:
- 头部操作:重置事件(清空列表)、设置菜单(Auto-open on error / Show errors only / Theme 三段式开关)、关闭。
- 事件行类型:普通
EventRow(含可展开的堆栈追踪与 Copy 按钮)、QuotaErrorRow(识别配额错误事件)、ReactLangStreamEventRow(展示 OpenUI Lang 流式事件,含 statements / orphaned / errors 概览统计,可展开查看完整响应与诊断)。分发逻辑见 inspect/index.ts 对应的getQuotaError/getReactLangStreamDetail。 - 交互细节:两个抽屉保持挂载以支持过渡动画,关闭时用
inert隐藏并移出焦点树;Escape优先收起 Debug 抽屉、再收 Inspect;设置菜单的 Escape 在捕获阶段处理,不会误触发抽屉关闭。
九、OpenUI Debug:基于真实组件库的调试工作台
流式事件行的Debug按钮会打开OpenUI Debug抽屉(360px 起、占据 Inspect 剩余空间的工作区),会话逻辑集中在 debug/useDebug.tsx:
- 入口依赖组件库注册:只有
createLibrary()注册过库(通过Symbol.for("openui.devtools.libraries")共享,见 lib/libraryRegistry.ts),Debug 按钮才可用;否则按钮禁用。 - 五个面板:Render(经宿主自身
Renderer渲染,预览不进入事件总线,因此流式回放不会向 Inspect 追加卡片)、Validation、Tree、JSON、Stream(带模拟流式回放控件与 Playback controls)。 - 编辑器:OpenUI Lang 文本编辑区(
textarea[aria-label="OpenUI Lang"]),支持编辑器/预览列宽比例拖拽(editorPct持久化)。 - 弹窗工作台:可将 Debugeject到独立命名窗口(debug/eject.ts)。弹出窗口会镜像宿主
documentElement/body的 class、属性和color-scheme,并复制宿主 head 中的样式表与 adopted style sheets,保证预览在独立窗口解析出与应用一致的 CSS;支持 tray ↔ popup 往返、窗口被拦截时留在抽屉内并提示「Allow popups for this origin」。该交互在 OpenUIDevtools.test.ts 中有完整测试覆盖。
十、部署提示与可靠性横幅
- Deploy Hint(ui/DeployHint.tsx):检测到一次本地完整响应(info 级、
react-lang:stream、phase === "settled"、响应非空、零错误、解析器无 incomplete/unresolved 且语句数 > 0)后,在按钮旁弹出一次性卡片,提示执行npx @openuidev/cli@latest deploy部署到 Vercel,带「Deployment docs」链接;按 origin 用localStorage记忆,可手动关闭。所有本地检测不产生任何分析事件,也从不离开浏览器。 - Reliability Banner(ui/ReliabilityBanner.tsx):Inspect 头部下方的横幅,文案为「Your users may see these errors in production」,提供主按钮「Get API key」(Thesys Console)与次级按钮「View docs」——正是 0.2.2 中拆分的两个 CTA。横幅中的功能特性文案(如「Automatically fix 88% of the errors」)属于产品宣传语,具体能力请以 Thesys Console 实际服务为准。
十一、CSP 与生产环境注意事项
- CSP:
script-src必须放行cdn.jsdelivr.net,否则运行时 fetch 被拦截,widget静默不出现——应用其余部分不受影响(见 packages/devtools/README.md)。若使用固定版本引脚可同时消除别名缓存的随机性。 - 生产构建:widget 在
NODE_ENV === "production"下默认不渲染;且由于@openuidev/devtools被声明为sideEffects: false(package.json),配合 react-lang 的devtoolsBootstrap.ts副作用仅保留在 web 入口的开发分支中,生产包可被完整 tree-shake 掉。
十二、测试保障:行为即规格
src/OpenUIDevtools.test.ts 是理解组件行为的完整规格文档,覆盖:禁用/启用渲染、错误徽章与计数、auto-open 行为及其跨会话持久化、errors-only 过滤、稳定 ID 事件合并(job 进度事件、流式更新、settled 诊断替换)、堆栈追踪展开与复制、Debug 面板的 Render/Validation/Tree/JSON/Stream 切换、库注册前的 Debug 禁用、单例选举与手动优先、独立窗口 eject 全流程(打开/聚焦/回收入口)。组件还在 React 中严格区分「真 checkbox 画成开关」(保留原生 input 在 DOM 中以获得焦点/表单/无障碍语义),体现了一致的可访问性设计。
结语
从 CHANGELOG 的版本线策略到源码层面的 CDN 插槽注入与 Symbol.for 单例选举,@openuidev/devtools展示了生成式 UI 开发工具链的典型工程形态:开发期自动出现、生产期零成本消失、任何失败都不影响宿主应用。接入时记住三件事:优先依赖 react-lang 的自动挂载(或用version引脚锁定 CDN 版本线)、显式传入需要的 Props、以及为 CSP 放行 jsDelivr——即可获得完整的 Inspect 事件检视与 Debug 调试体验。更进一步,你可以结合 packages/devtools/src/debug、packages/devtools/src/inspect 与 packages/devtools/src/ui 的源码,把 Debug 面板的能力内嵌到自己的开发工作流中。
【免费下载链接】openui
The Open Standard for Generative UI
相关推荐
使用 @openuidev/devtools 调试 OpenUI 应用:Inspect 事件面板与 Debug 工作台实战指南
使用 @openuidev/devtools 调试 OpenUI 应用:Inspect 事件面板与 Debug 工作台实战指南 @openuidev/devto
戴森球计划工厂蓝图:8000+优化方案打造你的星际工业帝国
戴森球计划工厂蓝图:8000+优化方案打造你的星际工业帝国 欢迎来到戴森球计划最全面的工厂蓝图资源库!这里汇集了超过8000个经过社区验证的优化蓝图,无论你是刚
游戏开发抖音无水印批量下载:三分钟备份一个创作者的完整主页
抖音无水印批量下载:三分钟备份一个创作者的完整主页 你有没有想过备份某位创作者的整套视频,结果只能手动一条条另存,还都是带水印的文件?或者只想留一份单个视频的无
网页爬虫CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考