reveal-3d
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReveal 3D Viewer
Reveal 3D查看器
Add a Cognite Reveal 3D viewer to a Dune app. Renders CAD models from CDF, with support for FDM-linked assets or direct model/revision IDs.
FDM instance to visualize: $ARGUMENTS
在Dune应用中添加Cognite Reveal 3D查看器。可渲染来自CDF的CAD模型,支持关联FDM的资产或直接使用模型/版本ID。
待可视化的FDM实例:$ARGUMENTS
Before you start
开始之前
Read these files before touching anything:
- — note
package.json/reactversions and existing depsreact-dom - — you will replace it entirely (new Dune apps have a standalone config, not a shared base config)
vite.config.ts - — you will prepend two lines to it
src/main.tsx
在进行任何操作前,请先阅读以下文件:
- — 注意
package.json/react版本及已有的依赖react-dom - — 你需要完全替换该文件(新版Dune应用使用独立配置,而非共享基础配置)
vite.config.ts - — 你需要在文件开头添加两行代码
src/main.tsx
Step 1 — Install packages
步骤1 — 安装依赖包
bash
pnpm add @cognite/reveal three process util assert ajv
pnpm add "@cognite/dune-industrial-components@github:cognitedata/dune-industrial-components#semver:*"
pnpm add -D @types/three
pnpm install --no-frozen-lockfileIf running inside Cursor (sandbox): the GitHub package install requires, which the Cursor sandbox blocks. If you seegit init, the install must be run with full permissions. In a Shell tool call, passgit init ... Operation not permitted.required_permissions: ["all"]
After install — check version matches what requires:
three@cognite/revealbash
node -e "const r=require('./node_modules/@cognite/reveal/package.json'); console.log(r.peerDependencies?.three)"
node -e "console.log(require('./node_modules/three/package.json').version)"If the installed version is lower than 's peer requirement, update it:
three@cognite/revealbash
pnpm add three@^<required-version> # e.g. three@^0.180.0
pnpm install --no-frozen-lockfileVerify now has all of: , (at the right version),
, , , , .
package.json@cognite/revealthreeprocessutilassertajv@cognite/dune-industrial-componentsWhy?ajvrequires@cognite/dune-industrial-components. The monorepo root hasajv@>=8as a transitive dep. Without a directajv@6in the app, pnpm picks up the root's v6 and you get a peer warning that can cause schema validation failures.ajv@^8
Do not install. It introduces a different set of transitive-dep conflicts. Use explicitvite-plugin-node-polyfills,process,utilpackage aliases instead.assert
bash
pnpm add @cognite/reveal three process util assert ajv
pnpm add "@cognite/dune-industrial-components@github:cognitedata/dune-industrial-components#semver:*"
pnpm add -D @types/three
pnpm install --no-frozen-lockfile如果在Cursor(沙箱)中运行: 安装GitHub包需要执行,但Cursor沙箱会阻止此操作。若出现git init错误,必须使用完整权限运行安装命令。在Shell工具调用中,传入git init ... Operation not permitted。required_permissions: ["all"]
安装后 — 检查版本是否符合的要求:
three@cognite/revealbash
node -e "const r=require('./node_modules/@cognite/reveal/package.json'); console.log(r.peerDependencies?.three)"
node -e "console.log(require('./node_modules/three/package.json').version)"若已安装的版本低于的对等依赖要求,请更新版本:
three@cognite/revealbash
pnpm add three@^<required-version> # 示例:three@^0.180.0
pnpm install --no-frozen-lockfile验证 中已包含所有依赖:、符合版本要求的、、、、、。
package.json@cognite/revealthreeprocessutilassertajv@cognite/dune-industrial-components为什么需要?ajv要求@cognite/dune-industrial-components。但 monorepo 根目录的传递依赖为ajv@>=8。若应用中未直接安装ajv@6,pnpm会使用根目录的v6版本,导致对等依赖警告,进而可能引发 schema 验证失败。ajv@^8
请勿安装。它会引入另一组传递依赖冲突。请改用显式的vite-plugin-node-polyfills、process、util包别名。assert
Step 2 — Vite config
步骤2 — Vite配置
Read vite-config.md for the complete . Apply it verbatim.
vite.config.tsKey points:
- includes
resolve.dedupe,react,react-dom,react/jsx-runtime— pnpm symlinks can create separate module instances in a monorepo;threeforces one copydedupe - Manual ,
util/,assert/aliases — not a plugin. These handle the top-level imports. Theprocess/browser,process,utilnpm packages must be inassert(Step 1)dependencies - lists
optimizeDeps.include,process,util,assert,three— pre-bundles them so esbuild handles CJS→ESM; Vite cannot auto-discover bare polyfill imports@cognite/reveal - — Reveal spawns ES module web workers; without this they fail silently
worker.format: 'es' - Never put in
@cognite/dune-industrial-components— forces raw ESM, re-introduces React duplication even with dedupeoptimizeDeps.exclude
阅读vite-config.md获取完整的代码,并完整套用。
vite.config.ts核心要点:
- 包含
resolve.dedupe、react、react-dom、react/jsx-runtime— 在monorepo中,pnpm的符号链接可能会创建多个模块实例;three强制使用单一副本dedupe - 手动配置、
util/、assert/别名 — 不使用插件,直接处理顶层导入。process/browser、process、utilnpm包必须已在步骤1中添加到assertdependencies - 列出
optimizeDeps.include、process、util、assert、three— 预打包这些依赖,让esbuild处理CJS到ESM的转换;Vite无法自动发现裸导入的polyfill@cognite/reveal - — Reveal会生成ES模块的Web Worker;若无此配置,Worker会静默失败
worker.format: 'es' - 切勿将加入
@cognite/dune-industrial-components— 这会强制使用原始ESM,即使配置了dedupe也会再次引发React重复实例问题optimizeDeps.exclude
Step 3 — main.tsx
步骤3 — main.tsx配置
Add the polyfill as the very first two lines — before any import, before React:
processtsx
import process from 'process';
(window as unknown as Record<string, unknown>).process = process;
// all other imports below ↓
import { DuneAuthProvider } from '@cognite/dune';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './styles.css';
const queryClient = new QueryClient({
defaultOptions: { queries: { staleTime: 5 * 60 * 1000, gcTime: 10 * 60 * 1000 } },
});
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<DuneAuthProvider>
<App />
</DuneAuthProvider>
</QueryClientProvider>
</React.StrictMode>
);Keep from as the auth provider — not .
DuneAuthProvider@cognite/duneCDFAuthenticationProvider将 polyfill添加为最开头的两行 — 早于任何import语句,早于React:
processtsx
import process from 'process';
(window as unknown as Record<string, unknown>).process = process;
// 以下是所有其他导入 ↓
import { DuneAuthProvider } from '@cognite/dune';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './styles.css';
const queryClient = new QueryClient({
defaultOptions: { queries: { staleTime: 5 * 60 * 1000, gcTime: 10 * 60 * 1000 } },
});
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<DuneAuthProvider>
<App />
</DuneAuthProvider>
</QueryClientProvider>
</React.StrictMode>
);保留来自的作为认证提供者 — 不要使用。
@cognite/duneDuneAuthProviderCDFAuthenticationProviderStep 4 — Provider placement (critical)
步骤4 — Provider放置(关键)
Getting this wrong causes at model load time.
ObjectUnsubscribedError: object unsubscribedCacheProviderRevealKeepAliveRevealProviderApp (always rendered)
CacheProvider ← always mounted
RevealKeepAlive ← always mounted
<sidebar> ← model picker lives here
{selected && (
RevealProvider ← conditional, only when model is ready
<RevealCanvas>
<Reveal3DResources />
)}Why: React StrictMode double-invokes effects on every component at first mount.
If is inside the same conditionally-rendered component as ,
StrictMode fires both cleanup cycles together — disposes the
viewer while 's async model-loading effect is still in-flight.
RevealKeepAliveRevealProviderRevealKeepAliveRevealProviderWhen is at App level, its StrictMode cycle completes at startup
with no viewer yet (nothing to dispose). By the time conditionally
mounts, 's is stable — and skips
viewer disposal when keepAlive context is present.
RevealKeepAliveRevealProviderRevealKeepAliveviewerRefRevealProviderPattern that breaks:
{selected && (
<MyViewerComponent> ← ❌ RevealKeepAlive co-located with RevealProvider
<CacheProvider>
<RevealKeepAlive>
<RevealProvider>配置错误会导致模型加载时出现错误。
ObjectUnsubscribedError: object unsubscribedCacheProviderRevealKeepAliveRevealProviderApp(始终渲染)
CacheProvider ← 始终挂载
RevealKeepAlive ← 始终挂载
<sidebar> ← 模型选择器在此处
{selected && (
RevealProvider ← 条件渲染,仅当模型就绪时
<RevealCanvas>
<Reveal3DResources />
)}原因: React StrictMode会在首次挂载时双重调用每个组件的effect。如果与位于同一个条件渲染组件中,StrictMode会同时触发两个清理周期 — 会在的异步模型加载effect仍在进行时销毁查看器。
RevealKeepAliveRevealProviderRevealKeepAliveRevealProvider当位于应用级别时,其StrictMode周期会在启动时完成(此时还没有查看器,无需销毁)。当条件挂载时,的已经稳定 — 并且当存在keepAlive上下文时,会跳过查看器销毁操作。
RevealKeepAliveRevealProviderRevealKeepAliveviewerRefRevealProvider错误的写法:
{selected && (
<MyViewerComponent> ← ❌ RevealKeepAlive与RevealProvider放在一起
<CacheProvider>
<RevealKeepAlive>
<RevealProvider>Step 5 — Implementation
步骤5 — 实现
Decide the pattern first — before reading any code.
Use Pattern B (model browser) unless you can answer YES to all three of these:
- The app already has a in scope — passed in as a prop or route param, not something to be fetched
DMInstanceRef - The user has confirmed that instance has linkage in their CDF data model
CogniteVisualizable.object3D → CogniteCADNode - The user explicitly asked for FDM-linked 3D, not just "show a 3D viewer"
If any answer is NO or uncertain — use Pattern B. It works with every CDF project that has 3D models, requires zero FDM setup, and is much easier to debug. Pattern A silently renders nothing when FDM linkage is missing.
Pattern B: Read the "Pattern B (default)" section of references/implementation.md.
Pattern A (only if gate above passed): Read the "Pattern A (fallback)" section of references/implementation.md.
Two files to create: (canvas only, no providers) and (owns all providers + model selection logic).
src/components/ViewerContent.tsxsrc/App.tsxCritical rules that both patterns share:
- must contain no providers —
ViewerContent,CacheProvider, andRevealKeepAliveall live inRevealProvider(see Step 5 for why)App.tsx - prop for
resourcesmust beReveal3DResources'd;useMemomust beonModelsLoaded'd — inline values cause infinite model reload loopsuseCallback - /
onSelectcallbacks passed into child components must beonLoad'd at the call site, and called fromuseCallbackinside the child — not during renderuseEffect - passed to
sdkmust beRevealProvider'd keyed onuseMemoclient.project - Lazy-load with
ViewerContent+React.lazyto avoid blocking the initial bundleSuspense
先确定实现模式 — 再阅读代码。
除非你能对以下三个问题全部回答YES,否则请使用模式B(模型浏览器):
- 应用中已存在作用域 — 作为props或路由参数传入,而非需要获取的内容
DMInstanceRef - 用户已确认其实例在CDF数据模型中存在关联
CogniteVisualizable.object3D → CogniteCADNode - 用户明确要求使用关联FDM的3D功能,而非仅要求“展示3D查看器”
若有任何问题答案为NO或不确定 — 使用模式B。它适用于所有拥有3D模型的CDF项目,无需任何FDM配置,且调试难度低。当FDM关联缺失时,模式A会静默不渲染任何内容。
模式B: 阅读references/implementation.md中的“Pattern B (default)”章节。
模式A(仅当满足上述所有条件时使用): 阅读references/implementation.md中的“Pattern A (fallback)”章节。
需要创建两个文件: (仅包含画布,无Provider)和(管理所有Provider + 模型选择逻辑)。
src/components/ViewerContent.tsxsrc/App.tsx两种模式共享的关键规则:
- 中不得包含任何Provider —
ViewerContent、CacheProvider和RevealKeepAlive都必须放在RevealProvider中(原因见步骤5)App.tsx - 的
Reveal3DResources属性必须用resources缓存;useMemo必须用onModelsLoaded缓存 — 内联值会导致模型无限重新加载循环useCallback - 传入子组件的/
onSelect回调必须在调用处用onLoad缓存,并在子组件内部的useCallback中调用 — 不得在渲染阶段调用useEffect - 传递给的
RevealProvider必须用sdk以useMemo为键进行缓存client.project - 使用+
React.lazy懒加载Suspense,避免阻塞初始包加载ViewerContent
Step 6 — Container height
步骤6 — 容器高度
RevealCanvaswidth: 100%; height: 100%tsx
<div style={{ width: '100%', height: '70vh', position: 'relative' }}>
<RevealProvider ...>
<ViewerContent modelId={...} revisionId={...} />
</RevealProvider>
</div>RevealCanvaswidth: 100%; height: 100%tsx
<div style={{ width: '100%', height: '70vh', position: 'relative' }}>
<RevealProvider ...>
<ViewerContent modelId={...} revisionId={...} />
</RevealProvider>
</div>Troubleshooting
故障排查
| Symptom | Cause | Fix |
|---|---|---|
| Cursor sandbox blocks git operations needed to clone the GitHub package | Re-run |
| Same GitHub package sandbox issue — pnpm is stuck waiting on a blocked syscall | Kill the process, re-run with |
| | Check |
| Monorepo root has | |
| | Move |
| Inline | |
| | Move the call into |
| | Add |
| Missing runtime polyfill | First two lines of |
| Used | Remove the plugin; use package aliases ( |
| Two Three.js copies loaded | |
| Black screen / workers fail silently | Missing ES worker format | Add |
| Canvas 0px tall | Container has no explicit height | |
| No model found (FDM mode) | Instance not linked via Core DM ( | Use model browser (Pattern B) with |
| 症状 | 原因 | 修复方案 |
|---|---|---|
| Cursor沙箱阻止了克隆GitHub包所需的git操作 | 在Shell工具调用中添加 |
| 同样是GitHub包沙箱问题 — pnpm卡在等待被阻止的系统调用 | 终止进程,添加 |
出现 | | 查看 |
出现 | Monorepo根目录有 | 在应用中执行 |
出现 | | 将 |
出现 | 内联的 | 在调用处使用 |
出现 | | 将调用移至 |
出现 | | 将 |
运行时出现 | 缺少运行时polyfill | 在 |
出现 | 使用了 | 移除该插件;使用包别名( |
出现 | 加载了两个Three.js副本 | 设置 |
| 黑屏 / Worker静默失败 | 缺少ES Worker格式配置 | 在vite配置中添加 |
| 画布高度为0px | 容器未设置明确高度 | 为父div设置 |
| 无模型显示(FDM模式) | 实例未通过Core DM关联( | 默认使用模型浏览器(模式B),调用 |
API reference
API参考
Page-level (always rendered): ,
CacheProviderRevealKeepAliveViewer-level (conditional): , , ,
RevealProviderRevealCanvasReveal3DResourcesInstanceStylingProviderHooks: , , , ,
useModelsForInstanceQueryuse3dModelsuseFdmAssetMappingsuseRevealuseOptionalRevealKeepAliveTypes: , , , (from )
AddCadResourceOptionsTaggedAddResourceOptionsViewerOptionsDMInstanceRef@cognite/revealAll exports from .
@cognite/dune-industrial-components/reveal页面级别(始终渲染): 、
CacheProviderRevealKeepAlive查看器级别(条件渲染): 、、、
RevealProviderRevealCanvasReveal3DResourcesInstanceStylingProvider钩子: 、、、、
useModelsForInstanceQueryuse3dModelsuseFdmAssetMappingsuseRevealuseOptionalRevealKeepAlive类型: 、、、(来自)
AddCadResourceOptionsTaggedAddResourceOptionsViewerOptionsDMInstanceRef@cognite/reveal以上所有API均从导出。
@cognite/dune-industrial-components/reveal