
OpenMAIC 只读幻灯片渲染包 openmaic/renderer从设计稿到 React 接入的完整实战指南【免费下载链接】OpenMAICOpen Multi-Agent Interactive Classroom — Get an immersive, multi-agent learning experience in just one click项目地址: https://gitcode.com/GitHub_Trending/op/OpenMAIC导读本指南以 OpenMAIC 仓库中 packages/openmaic/renderer/DESIGN.md 设计稿为主线完整讲解openmaic/renderer规划名maic-renderer这一只读画布渲染包的诞生动机、八大设计决策、对外 API 与源码组织并结合包内真实实现SlideCanvas、SlideRendererProvider、九个Base*Element、四类播放特效给出可直接复制运行的接入示例。读完本文你将掌握如何在任意 React Tailwind 4 项目中装包 → 传 Slide JSON → 渲染出 OpenMAIC 同款幻灯片画布以及如何通过renderImage/renderVideo插槽把业务媒体管线注入只读画布。1. 设计目标把只读画布抽成独立 workspace 包设计稿开篇即明确了 v1 的核心目标把 OpenMAIC 主仓components/maic-renderer/中的只读画布部分抽成独立 workspace 包让任意 React Tailwind 4 项目都能装包 → 传 Slide → 直接渲染。v1 的范围被严格限定为只读画布设计稿中对应主仓Editor/ScreenCanvas.tsx子树而编辑能力Editor/Canvas/*、Operate/*、ProseMirror 实时编辑器整体留给 v2。这一只读先行的取舍基于三条理由闭环最小可用与pptxtojson-pro形成产 Slide → 渲 Slide的对子打通端到端演示链路编辑器接口面难定型选中态、命令、撤销、剪贴板、协作等接口一旦定下再改代价极高第一版强行定型大概率返工只读包本身有独立价值可以独立用于课件嵌入、报告展示、课堂回放、PDF 导出预览等场景。从当前仓库的目录结构看这个设计已经落地为独立的 workspace 包packages/openmaic/renderer包名openmaic/renderer版本0.1.4见 package.json。包内包含完整的src/源码、test/测试、rollup.config.js构建配置与README.md/DESIGN.md/FONTS.md文档。其配套的openmaic/importer对应设计稿中的pptxtojson-pro则负责将.pptx转为同样的Slide[]结构实现.pptx → openmaic/renderer的端到端渲染。2. 八大设计决策D1–D8设计稿用一张决策表锁定了包的架构方向当前实现几乎逐条兑现#决策点选择落地情况源码证据D1范围分两步v1 只读 v2 编辑包内只有SlideCanvas等只读组件README.md 明确editing lives in the separateopenmaic/editorpackageD2数据 API纯 Props 主同时导出可选 ProviderSlideCanvas读取全部数据于 propscontext.tsx 提供SlideRendererProvideruseSlideContextD3样式要求消费者使用 Tailwind 4package.json将tailwindcss 4列为 peerDependencyREADME 强调emits Tailwind 4 arbitrary-value classesD4特效层包含4 个全做成可选 props默认关effects.ts 定义 laser/spotlight/highlight/zoomSlideCanvas默认不启用D5业务耦合元素暴露renderImage/renderVideo插槽包内默认渲原生标签BaseImageElement.tsx 默认渲染纯img插槽返回值具有权威性D6i18n包不带文案包内无任何 UI 文案alt等均由消费者自行处理D7ProseMirrorv1 不依赖package.json依赖表中没有 ProseMirror 系列D8包发布workspace 包未来可发 npm已作为openmaic/renderer发布publishConfig.access: public其中 D2 的实现非常典型SlideCanvas通过useOptionalSlideContext()先尝试读取上下文props 优先、context 兜底props.slide ?? ctx?.slide既保证了纯 props 模式的上手速度也兼顾了需要共享 slide 数据的进阶场景。3. 对外 API三层使用方式设计稿将对外 API 分为三层源码中的 src/index.ts 是这三层的总出口。3.1 主入口SlideCanvas /设计稿规划的 props 在 SlideCanvas.tsx 中已完整实现且在实际落地时又扩充了若干面向生产场景的选项。完整签名如下import { SlideCanvas, type SlideCanvasProps } from openmaic/renderer; interface SlideCanvasProps { /** 单页幻灯片数据PPTist 风格置于 Provider 内时可省略 */ slide?: Slide; /** 画布缩放省略时按容器自动适配见 useViewportSize */ scale?: number; /** 自动适配时画布占父容器的百分比默认 100 */ canvasPercentage?: number; /** 自动适配计算出的 scale 通过该回调上报 */ onScaleChange?: (scale: number) void; /** 覆盖 slide.background */ background?: SlideBackground; /** 4 个播放特效默认全关 */ effects?: SlideEffects; /** 图片渲染插槽可注入 placeholder / retry 逻辑 */ renderImage?: (el, resolvedSrc, defaultContent) ReactNode; /** 视频渲染插槽 */ renderVideo?: (el) ReactNode; /** 文本/形状标签/表格内容插槽源码中额外补充 */ renderText?: (el, defaultContent) ReactNode; renderShapeLabel?: (el, defaultContent) ReactNode; renderTable?: (el, defaultContent) ReactNode; /** 允许视频控件或自定义视频 UI 接收指针事件默认 true */ videoInteractive?: boolean; /** 元素点击回调 */ onElementClick?: (el, event: React.MouseEvent) void; /** 元素根 DOM id 前缀默认 slide-element- */ elementIdPrefix?: string; /** 拖拽偏移编辑面移动手势使用 */ dragOffsets?: ReadonlyMapstring, { x: number; y: number }; /** 隐藏指定元素 id不渲染且不参与特效定位 */ hiddenElementIds?: readonly string[]; /** 卡片式外壳阴影圆角默认 true快照管线应传 false 以精确还原 PPT 边缘 */ chrome?: boolean; className?: string; style?: React.CSSProperties; }几个容易忽略但很实用的细节自动适配省略scale时useViewportSize.ts 依据slide.viewportSize设计稿宽默认 1000与slide.viewportRatio宽高比默认 0.5625 即 16:9用ResizeObserver监听容器尺寸变化并居中适配容器高宽比与画布不一致时自动取宽限或高限中较紧的一边。chrome开关默认为画布外层叠加 1px 淡描边 圆角0 0 0 1px rgba(0,0,0,0.01), 0 0 12px 0 rgba(0,0,0,0.1)。快照/导出场景应传false否则html2canvas会把这层阴影和 0.5rem 圆角烘焙进 PNG导致与原 PPT 边缘比对时出现假性差异。hiddenElementIds用于在渲染层直接过滤元素源码用useMemoSet过滤可服务于渐进披露等播放编排需求。3.2 Provider 高阶模式当需要多个兄弟层共享同一份 slide 数据时使用SlideRendererProvider包裹子组件通过useSlideContext()取值import { SlideRendererProvider, SlideCanvas, useSlideContext } from openmaic/renderer; function MyAnnotationLayer() { const { slide, scale } useSlideContext(); return divAnnotations for {slide.id} at scale {scale}/div; } export default function Demo() { return ( SlideRendererProvider slide{slide} scale{0.9} SlideCanvas / {/* slide/scale 从 context 读取 */} MyAnnotationLayer / /SlideRendererProvider ); }实现上context.tsx 定义SlideContextValueslide、scale、background、effects、各渲染插槽、videoInteractive、onElementClickSlideRendererProvider将其整体作为 context value 下发。useSlideContext()在 Provider 外调用会抛出明确错误useOptionalSlideContext()则返回null供SlideCanvas这类可选消费场景使用。值得注意Provider 的 props 与SlideCanvas的 props 一一对应意味着插槽函数也可以经由 context 向下传递适合大型应用在顶层统一注入媒体行为。3.3 元素子组件细粒度复用设计稿规划的 9 个基础元素组件在 elements/index.ts 中全部导出并在实现时额外补充了BaseAudioElementimport { BaseTextElement, BaseShapeElement, BaseImageElement, BaseLineElement, BaseChartElement, BaseLatexElement, BaseTableElement, BaseVideoElement, BaseCodeElement, BaseAudioElement, // 实现时补充 ElementOutline, // 共享描边 useElementFill, useElementOutline, useElementShadow, useElementFlip, } from openmaic/renderer/elements;每个Base*Element统一接收{ elementInfo: PPTXxxElement }图片/视频元素额外接收渲染插槽。这些组件可以脱离SlideCanvas单独使用用于自建布局。此外SlideElementSlideElement.tsx作为画布内部的元素分派器依据elementInfo.typeIMAGE/TEXT/SHAPE/LINE/CHART/LATEX/TABLE/VIDEO/AUDIO/CODE分发到对应组件整个组件树用memo包裹以隔离元素级重渲染未传入onElementClick时元素默认pointerEvents: none保持纯展示性能。3.4 类型导出设计稿列出的类型在openmaic/renderer/types子路径完整提供import type { Slide, PPTElement, SlideBackground, SlideTheme, PPTTextElement, PPTShapeElement, PPTImageElement, PPTLineElement, PPTChartElement, PPTLatexElement, PPTTableElement, PPTVideoElement, PPTCodeElement, ImageElementClip, ImageElementFilters, Gradient, GradientType, PPTElementOutline, PPTElementShadow, SlideEffects, LaserEffectOptions, SpotlightEffectOptions, HighlightEffectOptions, ZoomEffectOptions, } from openmaic/renderer/types;设计稿中的幻灯片数据结构类型在实现中从/lib/types/slides拷贝并收敛到openmaic/dsl包见package.json的 dependencies由openmaic/dsl统一维护Slide/PPTElement族避免多包重复定义导致的类型漂移——这正是设计稿风险一节中预测的演进方向。4. 包结构与源码组织设计稿规划的目录树与当前实现基本一致关键差异是包名从maic-renderer定稿为openmaic/renderer并新增了audio元素、snapshot快照子路径与styles.ts。当前 packages/openmaic/renderer 的源码结构如下packages/openmaic/renderer/ ├── package.json # name: openmaic/renderer, v0.1.4 ├── README.md / DESIGN.md / FONTS.md ├── fonts.config.mjs # 字体白名单与 CDN 地址配置 ├── rollup.config.js # 构建rollup -c tsc --emitDeclarationOnly ├── src/ │ ├── index.ts # 主入口导出 SlideCanvas / Provider / effects / hooks / utils / types │ ├── SlideCanvas.tsx # 只读画布主组件 │ ├── SlideElement.tsx # 元素分派器memo 化 │ ├── context.tsx # SlideRendererProvider useSlideContext │ ├── styles.ts # 注入画布的全局样式SLIDE_RENDERER_STYLES │ ├── effects/ # HighlightOverlay / SpotlightOverlay / LaserOverlay / ZoomWrapper │ ├── elements/ # 91 个 Base*Element 与共享 hooks │ ├── hooks/ # useSlideBackgroundStyle / useViewportSize │ ├── snapshot/ # 快照katex-fonts-embed / measure / index │ ├── types/ # effects.ts 与 slides 类型 │ └── utils/ # cn / geometry / element / richText └── test/ # SlideCanvas、SlideElement、各元素与特效的 vitest 用例与设计稿一一对应的是effects/、elements/、hooks/、utils/四个核心目录elements/下每个元素一个子目录text/、shape/含GradientDefs.tsx、PatternDefs.tsx、image/含ImageOutline.tsx、useClipImage.ts、useFilter.ts、clipPaths.ts、line/、chart/、latex/、table/、video/、code/、audio/以及共享的ElementOutline.tsx、useElementFill.ts、useElementOutline.ts、useElementShadow.ts、useElementFlip.tseffects/四个文件对应四类播放特效utils/geometry.ts正是设计稿 7.1 中从findElementGeometry(...)拷贝进来的工具index.ts对外导出findElementGeometry、findNearestCorner、getElementPercentageGeometry。5. 依赖与构建5.1 依赖声明package.json中的依赖与设计稿高度吻合并标注了可选 peerpeerDependencies消费者需自行安装react 18、react-dom 18、motion 11、tailwindcss 4echarts 5与shiki 1.0.0标记为optional仅在幻灯片含 chart / code 元素时才需要见 README.mddependencies运行时必备openmaic/dslSlide 类型与常量、katexLaTeX 渲染、tinycolor2颜色工具、lucide-react图标、clsxtailwind-mergecnhelper另含html-to-image与html2canvas-pro快照/导出辅助ProseMirror 系列如设计稿 D7 所承诺v1 完全不依赖。5.2 构建与产物构建命令与设计稿一致rollup -c tsc --emitDeclarationOnly实际package.json中的build脚本还增加了两步字体 CSS 生成pnpm --filter openmaic/renderer build # node scripts/generate-fonts-css.mjs # node scripts/generate-katex-fonts.mjs # rm -rf dist rollup -c tsc --emitDeclarationOnly --declarationDir dist产物通过exports字段对外暴露五个入口.主入口、./elements、./types、./snapshot、./fonts.css每个 JS 入口同时产出index.jsESM与.d.ts类型声明。测试用 vitest 运行pnpm testprepublishOnly会在发布前自动执行构建。6. 关键解耦点从主仓画布到独立包设计稿第 7 节用表格明确了原实现 → 包内实现的映射关系这也是理解包架构的钥匙。6.1 ScreenCanvas → SlideCanvas全局 store 换成 props设计稿列出了五组替换关系核心是把 zustand 全局状态全部改为 props 驱动原主仓 store新包内 propsuseCanvasStore.use.canvasScale()props.scale ?? 1useSceneSelector(c c.canvas.elements)props.slide.elementsuseSceneSelector(c c.canvas.background)props.background ?? props.slide.backgrounduseCanvasStore.use.laserElementId/Options()props.effects?.laseruseCanvasStore.use.zoomTarget()props.effects?.zoomfindElementGeometry(...)拷贝进utils/geometry.ts在 SlideCanvas.tsx 中可以看到零全局状态的兑现画布只做纯函数式派生visibleElements、elementIndexById、laserGeometry、zoomGeometry均为useMemo派生并注释说明了在 React Compiler 构建下这些派生会自动 memo 化。6.2 ScreenElement → SlideElement主题从 props 读取theme.fontColor/theme.fontName从 slide 数据读取props.slide.theme移除useSceneSelector。SlideElement实现中还提供了DEFAULT_THEME#333333/Microsoft YaHei作为无主题时的兜底。6.3 BaseImageElement退化成纯img 插槽注入这是最核心的解耦。设计稿要求包内退化成纯img版本含 clip/filter/outline/shadow所有 placeholder / retry / i18n 逻辑剥离。当前 BaseImageElement.tsx 的实现细节默认内容为原生img带loadinglazy与decodingasync服务缩略图/侧栏等大量挂载场景拖拽被preventDefault禁止通过useClipImage、useFilter、ImageOutline、useElementShadow、useElementFlip组装裁剪路径、滤镜、描边、阴影、翻转额外支持softEdge羽化两条线性渐变 mask 交叉四个边缘渐隐与colorMask色彩蒙层——注释说明html2canvas-pro忽略 CSS mask因此slideToPng快照路径通过data-soft-edge钩子把羽化烘焙进像素renderImage插槽收到(element, resolvedSrc, defaultContent)其中defaultContent是已经过裁剪/滤镜/羽化/蒙层处理的完整默认渲染插槽返回值具有权威性返回null可刻意隐藏图片。6.4 BaseVideoElement 与其余元素BaseVideoElement同样退化为纯video业务行为由renderVideo注入videoInteractive控制视频控件是否接收指针事件。其余Base*Element只是把类型导入路径从/lib/types/slides改为openmaic/dsl无业务耦合。7. 四类播放特效OpenMAIC 同款交互还原设计稿 D4 将特效层纳入包内但默认零负担。类型定义见 effects.tsexport interface LaserEffectOptions { elementId: string; color?: string; duration?: number; } export interface SpotlightEffectOptions { elementId: string; dimness?: number; } export interface HighlightEffectOptions { elementId: string; color?: string; opacity?: number; borderWidth?: number; animated?: boolean; } export interface ZoomEffectOptions { elementId: string; scale: number; } export interface SlideEffects { laser?: LaserEffectOptions; spotlight?: SpotlightEffectOptions; highlight?: HighlightEffectOptions; highlights?: HighlightEffectOptions[]; // 支持同时高亮多个元素 zoom?: ZoomEffectOptions; }接入方式SlideCanvas slide{slide} effects{{ laser: { elementId: t1, color: #ff3b30 }, spotlight: { elementId: t1 }, highlight: { elementId: t1, color: #ff6b6b, animated: true }, zoom: { elementId: t1, scale: 1.5 }, }} /实现要点均有源码佐证激光笔LaserOverlay.tsx默认颜色#ff3b30、默认时长 3000ms。用motion实现从画布对角飞入 → 到达目标元素中心 → 呼吸脉冲 → 淡出的动画初始位置根据元素几何中心百分比坐标自动选择从左上或右下进入聚焦光斑SpotlightOverlay目标元素外压暗dimness可调用ResizeObserver感知尺寸变化高亮HighlightOverlay支持单元素highlight与多元素highlights数组可设颜色、透明度、边框宽与是否动画缩放ZoomWrapper / 画布级 transformSlideCanvas根据zoomGeometry的百分比中心设置transformOrigin再对画布整体做scale()变换并配 700ms 的 transform 过渡。特效定位依赖findElementGeometry把元素的像素坐标换算为百分比几何PercentageGeometry因此缩放/旋转等变换后的元素也能被准确命中。8. 接入实战三步跑通只读画布8.1 安装与 Tailwind 4 配置pnpm add openmaic/renderer # 或 npm install openmaic/renderer由于包输出的是 Tailwind 4 arbitrary-value 类必须在tailwind.config.{ts,js}的content中包含包产物目录export default { content: [ ./src/**/*.{ts,tsx}, ./node_modules/openmaic/renderer/dist/**/*.{js,cjs}, ], };8.2 最小可运行示例以 README.md 的 Quickstart 为基础并补全类型字段使其更贴近openmaic/dsl的真实结构import { SlideCanvas, type Slide } from openmaic/renderer; const slide: Slide { id: demo-1, viewportSize: 1000, // 画布设计宽度 viewportRatio: 0.5625, // 16:9 theme: { backgroundColor: #ffffff, themeColors: [#5b8def], fontColor: #222222, fontName: sans-serif, }, elements: [ { type: text, id: t1, left: 100, top: 80, width: 800, height: 60, rotate: 0, content: pHello, Slide/p, defaultFontName: sans-serif, defaultColor: #222, }, ], background: { type: solid, color: #ffffff }, }; export default function Demo() { return ( div style{{ width: 800, height: 450 }} SlideCanvas slide{slide} / /div ); }关键约定父容器必须具有确定的宽高。SlideCanvas外层容器width/height: 100%内层画布依据viewportSize × viewportRatio自动适配并居中省略scale即进入自动适配模式。8.3 注入业务媒体行为真实业务中图片可能处于占位 → 生成中 → 就绪状态用renderImage插槽接管即可SlideCanvas slide{slide} renderImage{(el, src, defaultContent) src.startsWith(placeholder:) ? MyPlaceholder taskId{src} / : defaultContent } /defaultContent已包含裁剪、滤镜、羽化、colorMask与阴影等全部画布级处理业务侧只需在需要时覆盖。8.4 中文字体可选但建议了解PowerPoint 导入的幻灯片常引用本机未安装的中文字体。包提供fonts.css声明 6 个可再分发字体的font-face思源黑体、思源宋体、霞鹜文楷、朱雀仿宋、站酷快乐体、文鼎 PL 简中楷在应用入口导入一次即可import openmaic/renderer/fonts.css;需要注意README 与 FONTS.md 均有明确警告font-face的srcURL 指向外部字体托管https://file.maic.chat/fonts/name.woff2woff2 文件并不打包在包内因此该导入是一份硬运行时依赖——托管地址必须可达且允许 CORS否则浏览器会静默回退系统字体不报错但字形/度量不同。如需私有 CDN 或离线内网可修改fonts.config.mjs中的FONT_CDN_BASE_URL并运行pnpm run genfonts重新生成。不导入该 CSS 也能正常渲染回退系统字体。各字体的许可与再分发条件记录在 FONTS.md 中含 OFL 1.1 文本与文鼎 Public License 全文。8.5 快照与导出从package.json的exports与 src/snapshot/ 目录可以看出包还提供openmaic/renderer/snapshot子路径含 katex 字体嵌入与 DOM 测量工具配合html2canvas-pro/html-to-image依赖支撑幻灯片截图为 PNG 的场景。快照管线传入chrome{false}可避免阴影与圆角被烘焙进输出见SlideCanvasProps.chrome的源码注释。9. 验收标准、v2 预告与风险清单9.1 验收标准设计稿第 8 节定义了三条验收标准均可在当前仓库核验pnpm --filter openmaic/renderer build通过并产出干净的dist/含index.js、elements/index.js、types/index.js及对应.d.ts用该包渲染手写 Slide 时视觉效果与主仓ScreenCanvas一致v1 不替换主仓既有调用——设计稿明确本次任务不动主仓页面主仓继续使用自己的BaseImageElement因此砍掉业务逻辑不会影响既有功能。9.2 v2 预告设计稿预告的 v2 编辑能力包括Editor/Canvas/*抽入包内editing/、ProseMirror 实时编辑器、选中/拖拽/resize/旋转/裁剪/对齐线/标尺、撤销/重做/剪贴板以及新的 API 形态SlideEditor slide editable onChange{...} /。从 README.md 看编辑能力已独立到openmaic/editor包0.1.0起移除实验性的openmaic/renderer/editing子路径只读的SlideCanvasAPI 保持稳定。9.3 风险与缓解设计稿原文风险等级缓解Chart 库依赖不明包体积可能膨胀中实现阶段已确认echarts与shiki标记为optional peer仅在用到对应元素时才安装Tailwind 4 强依赖筛掉很多消费者中README 显著说明未来版本再考虑编译 CSS主仓Slide类型未来演进包内拷贝版会漂移低实现阶段已通过openmaic/dsl三方共享从源头消除漂移BaseImageElement 砍业务后主仓现有页面会丢功能无影响v1 不替换主仓调用主仓继续使用自己的实现可以看到低风险的两项Chart 依赖、类型漂移在实现阶段已按缓解方案落地echarts/shiki改为 optional peerSlide类型收敛到openmaic/dsl。10. 结语openmaic/renderer是 OpenMAIC产 Slide → 渲 Slide闭环中的渲染半边它以设计稿 DESIGN.md 的八大决策为骨架落地为一个零全局状态、纯 Props 驱动、特效默认零负担、媒体行为由插槽注入的只读画布包。对开发者而言从 README.md 的三步 Quickstart 即可接入想要深入原理可按SlideCanvasSlideCanvas.tsx→ 元素分派SlideElement.tsx→ 各Base*Elementelements/index.ts→ 特效层effects.ts的路径阅读源码与 test/ 目录下的测试用例即可获得课件嵌入、报告展示、课堂回放、PDF 导出预览等场景的完整技术储备。【免费下载链接】OpenMAICOpen Multi-Agent Interactive Classroom — Get an immersive, multi-agent learning experience in just one click项目地址: https://gitcode.com/GitHub_Trending/op/OpenMAIC创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考