Releases: vimaec/vim-web
1.00 beta
vim-web 1.0.0-beta.1
⚠️ Breaking Changes
See migration guide at the end
API Changes
createVieweris now async — ReturnsPromise<ViewerApi>instead ofViewerApiviewer.settingsremoved — Useviewer.uifor runtime UI togglesviewer.renderSettings— Outline, selection fill, transparency, and room rendering moved out ofIsolationApiinto a dedicated APIVimSettings.transparencyremoved — The load-timeTransparencyModeoption is gone. Useviewer.isolation.showTransparentfor runtime controlIsolationSettings.transparencyrenamed toshowTransparent
Peer Dependencies
- React
^18.3.1 || ^19.0.0required (was^18.3.1)
✨ New Features
- Reactive UI controls — Every UI panel toggle is now a
StateRefyou can read, write, and subscribe to - Configurable transparent opacity — Glass/window opacity is now adjustable (was hardcoded 0.25)
- BIM parameter preloading —
viewer.load({ url }, { prewarmBim: true })eliminates the first-query delay vim.load()is idempotent — Safe to call multiple times; clears previous geometry automaticallygetElementFromUniqueId()— Look up elements by Revit unique ID stringhasGeometryvshasMesh—hasGeometryis true after VIM parsing;hasMeshis true after geometry is loadedonGeometryLoadedsignal — Fires aftervim.load(subset)completes- Portal-based tooltips — Consistent styled tooltips everywhere, never clipped by overflow containers
- Reactive control bar tooltips — Toggle buttons show contextual tips (e.g. "Disable Section Box" when active)
exportsfield in package.json —import 'vim-web/style.css'andimport type { ... } from 'vim-web/bim'
⚡ Performance
- BIM tree virtualization — Only visible rows are rendered. Handles 100k+ elements smoothly
- BIM parameter caching — Entity table columns cached at the Vim level. Per-query cost reduced from ~300ms to <1ms
- Debounced BIM info panel — 50ms debounce prevents main thread blocking during rapid selection
- Tree click optimization — Selecting in the tree skips the expensive viewer→tree sync loop
- Camera lerp fix —
onProgress(1)now fires before the callback is cleared
🏗️ Architecture
- Headless BIM tree — Replaced
react-complex-treewith@headless-tree/react. Full DOM control, no!importantoverrides, flat item list ready for virtualization - Clean tree data model — String IDs, semantic visibility (
visible/partial/hidden), O(1) range lookups, parent-first ordering - Extracted tree hooks —
useBimTree,useBimVirtualizer,useBimVisibility,useBimSelectionSync,useBimClickHandler - Native UI components — Side panel resize and context menu reimplemented without libraries (~30 lines each)
- Tailwind CSS removed — All styling uses semantic CSS classes with design tokens
- No linter/formatter — ESLint and Prettier removed. TypeScript compiler is the only build gate
📦 Dependencies
Removed (11)
react-tooltip · re-resizable · @firefox-devtools/react-contextmenu · react-complex-tree · ste-events · eslint · prettier · @typescript-eslint/* · postcss · tailwindcss · autoprefixer
Added (3)
@headless-tree/core · @headless-tree/react · @tanstack/react-virtual
Updated
| Package | From | To |
|---|---|---|
| React | 18.3 | 19.2 |
| TypeScript | 5.8 | 6.0 |
| Vite | 6.4 | 8.0 |
| Three.js | 0.171 | 0.183 |
| vim-format | 1.0.15-dev.5 | 1.0.16-dev.6 |
Result
407 → 133 installed packages (67% reduction)
=====================
Migration Guide: vim-web 0.5 → 1.0.0-beta.1
Install
npm install vim-web@betaReact 18.3+ still works. React 19 is also supported.
Breaking Changes
1. createViewer is now async
// Before (0.5)
const viewer = VIM.React.Webgl.createViewer(div, settings)
// After (1.0)
const viewer = await VIM.React.Webgl.createViewer(div, settings)Same for Ultra:
const viewer = await VIM.React.Ultra.createViewer(div, settings)2. viewer.settings removed
The SettingsApi (viewer.settings.update(), viewer.settings.register(), viewer.settings.customize()) no longer exists.
For UI visibility toggles, use viewer.ui:
// Before
viewer.settings.update(s => { s.ui.panelBimTree = false })
viewer.settings.update(s => { s.ui.panelControlBar = false })
// After
viewer.ui.bimTree.set(false)
viewer.ui.controlBar.set(false)
// Subscribe to changes
viewer.ui.axes.onChange.subscribe(visible => { ... })
// Read current state
const showing = viewer.ui.bimTree.get()Available UI controls: logo, controlBar, bimTree, bimInfo, axes, performance, axesOrthographic, axesHome, cursorOrbit, cursorLookAround, cursorPan, cursorZoom, cameraAuto, cameraFrameScene, cameraFrameSelection, sectioningEnable, sectioningFitToSelection, sectioningReset, sectioningShow, sectioningAuto, sectioningSettings, measureEnable, visibilityClearSelection, visibilityShowAll, visibilityToggle, visibilityIsolate, visibilityAutoIsolate, visibilitySettings, miscProjectInspector, miscSettings, miscHelp, miscMaximise
For render settings (outline, selection fill, transparency), use viewer.renderSettings:
// Before
viewer.isolation.outlineEnabled.set(false)
viewer.isolation.selectionFillMode.set('xray')
viewer.isolation.showTransparent.set(false)
// After
viewer.renderSettings.outlineEnabled.set(false)
viewer.renderSettings.selectionFillMode.set('xray')
viewer.renderSettings.showTransparent.set(false)3. VimSettings.transparency removed
The load-time TransparencyMode option ('opaqueOnly', 'transparentOnly', 'allAsOpaque', 'all') is gone.
// Before
viewer.load({ url }, { transparency: 'opaqueOnly' })
// After — control at runtime instead
viewer.load({ url })
viewer.renderSettings.showTransparent.set(false)4. IsolationSettings.transparency renamed
// Before
const viewer = await VIM.React.Webgl.createViewer(div, {
isolation: { transparency: false }
})
// After
const viewer = await VIM.React.Webgl.createViewer(div, {
isolation: { showTransparent: false }
})5. IsolationApi slimmed down
Render-related settings moved out of IsolationApi. The isolation API now only handles visibility:
// These still work on viewer.isolation:
viewer.isolation.showAll()
viewer.isolation.hideSelection()
viewer.isolation.isolateSelection()
viewer.isolation.autoIsolate.set(true)
viewer.isolation.showGhost.set(true)
viewer.isolation.ghostOpacity.set(0.5)
// These moved to viewer.renderSettings:
viewer.renderSettings.showTransparent
viewer.renderSettings.transparentOpacity
viewer.renderSettings.outlineEnabled
viewer.renderSettings.outlineQuality
viewer.renderSettings.outlineThickness
viewer.renderSettings.selectionFillMode
viewer.renderSettings.selectionOverlayOpacity
viewer.renderSettings.showRoomsNew Features
BIM Parameter Preloading
Eliminates the ~300ms delay on the first property query:
// Option A: at load time
const request = viewer.load({ url }, { prewarmBim: true })
// Option B: manually after load
const vim = await request.getVim()
vim.prewarmBimCache()Configurable Transparent Opacity
Glass/window opacity was hardcoded at 0.25. Now configurable:
viewer.materials.transparentOpacity = 0.5New Element APIs
// Look up by Revit unique ID
const element = vim.getElementFromUniqueId('abc-123-def')
// Distinguish parsed geometry from loaded geometry
element.hasGeometry // true after VIM is parsed (instances exist)
element.hasMesh // true after vim.load() builds the mesh
// React to geometry loading
vim.onGeometryLoaded.subscribe(() => { ... })Idempotent vim.load()
Safe to call multiple times — clears previous geometry automatically:
await vim.load() // loads all
await vim.load() // clears and reloads (no duplicates)
await vim.load(subset) // clears and loads subsetCSS Import
// Before — import path depended on bundler setup
import 'vim-web/dist/style.css'
// After — clean named export
import 'vim-web/style.css'BIM Types Import
// Type-only import for BIM data types
import type { ... } from 'vim-web/bim'CSS Changes
All Tailwind utility classes (vc-flex, vc-text-sm, etc.) have been replaced with semantic CSS classes. If you were targeting vim-web's internal CSS classes in your stylesheets, they have changed. The component structure and vim- prefixed classes are stable.
Peer Dependencies
| 0.5 | 1.0-beta.1 | |
|---|---|---|
| react | ^18.3.1 | ^18.3.1 || ^19.0.0 |
| react-dom | ^18.3.1 | ^18.3.1 || ^19.0.0 |
React 18.3+ continues to work. React 19 is now also supported.
1.0.0-alpha.1
Selection & Outline Improvements
Smoother Selection Outlines
Selection outlines now use Sobel convolution edge detection with bilinear texture sampling, replacing the previous Chebyshev ring approach. This produces smoother, antialiased outlines with natural soft falloff at the edges.
The outline shader supports a scale uniform so that outline thickness remains consistent in screen pixels regardless of the render target resolution.
Selection Overlay Modes
New selection fill modes that go beyond simple outlines:
- None — Outline only (default)
- Default — Tint selected meshes directly in the main render pass (zero extra cost)
- X-Ray — Renders selected elements on top of everything with a semi-transparent tint
- See-Through — Renders a ghost of selected elements where they are behind other geometry
Overlay opacity is adjustable (0–1). Both X-Ray and See-Through render directly into the scene target, preserving the existing depth buffer from the main render pass.
Render Settings Panel
The Render Settings panel (accessible from the control bar) now includes:
| Setting | Type | Range | Default |
|---|---|---|---|
| Transparency | Toggle | — | On |
| Show Ghost | Toggle | — | Off |
| Ghost Opacity | Slider | 0–1 | — |
| Selection Outline | Toggle | — | On |
| Outline Quality | Select | Low / Medium / High | High |
| Outline Thickness | Number | 1–5 | 3 |
| Selection Fill | Select | None / Default / X-Ray / See-Through | None |
| Selection Opacity | Slider | 0–1 | 0.25 |
Outline Quality controls the render target resolution for the outline pipeline:
- Low (0.5x) — Fastest, softer outlines
- Medium (1x) — Balanced
- High (2x) — Sharpest outlines, 4x more pixels to process
All settings are available programmatically via viewer.isolation:
viewer.isolation.outlineQuality.set('high')
viewer.isolation.outlineThickness.set(4)
viewer.isolation.selectionFillMode.set('xray')
viewer.isolation.selectionOverlayOpacity.set(0.3)Performance
GPU Picker Scissor Optimization
The GPU picker now uses a 1x1 pixel scissor rect when picking, instead of rendering the full scene to the pick buffer. The GPU skips fragment processing for all pixels outside the scissor region and can cull geometry that doesn't intersect it. This significantly reduces the cost of each pick operation.
Dependencies
- Three.js updated from 0.171 to 0.183
@types/threeupdated to 0.183.0- Migrated
THREE.ClocktoTHREE.Timer(breaking change in r183)
1.0.0-alpha.0
This branch is a major architectural overhaul preparing vim-web for the 1.0 release. It covers five broad areas: a new public API surface, a complete outline rendering rewrite, a GPU picking system, geometry loading performance, and a thorough dead code purge.
Public API & Exports
ViewerApiabstraction: Both WebGL and Ultra viewers now expose a unifiedViewerApihandle (WebglViewerApi/UltraViewerApi) throughVIM.React.Webgl.createViewer()andVIM.React.Ultra.createViewer(). Direct class access is no longer needed for any common use case.- Interface-driven: Core types are now backed by
I-prefixed interfaces (IVim,ICamera,ISelectable,IMarker,IMaterials, etc.). Concrete classes are@internal. - Tighter barrel files: Internal modules are no longer leaked through re-exports. The public surface is intentionally narrow.
- Unique class names: Renamed internal classes for cleaner API bundles and better TypeScript declaration output.
viewer.core.settingsremoved: Settings are consumed at construction time only — exposing them as a field was misleading. Removed fromIWebglViewer.- Material API cleanup:
IMaterialsnow only exposes user-facing properties (ghostOpacity,ghostColor,outlineOpacity,outlineThickness,outlineColor,clippingPlanes). Internal system materials are hidden behind asystemgetter. Element3Dinterface:IElement3Dexposes only BIM-facing properties; mesh internals are hidden.- Input API: Inputs organized under
viewer.core.inputswith typedPointerModeenum, configurable key handlers, and clean mouse/touch callbacks. Removed spacebar bindings. - Camera API: Fluent
snap()/lerp(duration)API for instant vs animated camera moves. Unifiedframe(),orbit(),orbitTowards(),zoomTo(). RemovedlookAtparameter. Rotation axes clarified (x = horizontal/yaw, y = vertical/pitch). - Framing renamed:
viewer.react.camera→viewer.framingfor clarity. - Signal interfaces:
onXxxevent properties typed as read-only signal interfaces, not mutable dispatchers. - Ultra API tightened: Ultra viewer API aligned with WebGL — matching fields (
container,isolation,sectionBox, etc.), control bar customization via hook param, no leaked RPC internals.
GPU Picking (WebGL)
Replaced CPU raycasting as the primary picking method with a GPU-based approach:
- Float32 render target stores packed element ID (
vimIndex << 24 | elementIndex), depth, and surface normals per fragment. packPickingId()/unpackPickingId()utilities pre-pack IDs at mesh build time — zero overhead per frame.- Markers support GPU picking (
packedIdattribute withMARKER_VIM_INDEXsentinel). GpuPickerreads a 1×1 region on click, resolves vim + element in one readback.- CPU raycaster (
raycaster.ts) retained as fallback.
Outline Rendering Rewrite
The outline effect was completely rewritten from depth-based to mask-based silhouette detection:
Pipeline: Scene (MSAA) → Selection Mask → Outline Edge Detect → Merge → Screen
- Mask-based: Selected objects render with a flat white
maskMaterialto a dedicated render target. Edge detection runs on the mask, not the depth buffer — eliminates depth precision artifacts at long range. scene.backgroundfix: Background color was leaking into the mask buffer and breaking edge detection. Background is now nulled during the outline composer render and restored after.- Configurable thickness: New
thicknessuniform (range 1–5) controls how many Chebyshev ring levels are sampled per fragment. Higher values = thicker outlines at a proportional sampling cost. - Full grid (Chebyshev ring) sampling: Replaced 8-point star sampling with a full NxN grid ring. Eliminates diagonal artifacts from the star pattern.
- Opacity and color:
outlineOpacity(0–1) andoutlineColorexposed onIMaterials. Both live on the merge pass for clean separation. - Dead properties removed:
camera,depthBuffer,color,precisionand associated uniforms removed fromOutlineMaterial.OutlinePassno longer receives or wires a camera.
Material System Cleanup
StandardMaterialremoved:_opaque/_transparentStandardMaterialfields deleted fromMaterials. VIM meshes have always usedModelMaterial(custom GLSL3 shader) —StandardMaterialwas never in the rendering path.- Dead settings removed:
skybox,skylight,sunlights— defined, parsed, never consumeduseFastMaterials,materials.standard.color— toggle with no effectmaterials.section.strokeWidth/Falloff/Color— only applied toStandardMaterialshader, which was unused
modelColor,sectionStroke*removed fromIMaterials— dead wiring to removed materials.MaterialSettingsnow contains onlyghostandoutline.MaterialSet: New value type{ opaque, transparent, hidden }for per-mesh material assignment.applyMaterial()helper is the single place that callsMaterialSet.get().
Geometry Loading & Performance
- Color palette texture: Per-element colors stored in a shared 128×128
DataTexture(25³ = 15,625 quantized colors).ModelMaterialreads colors viatexelFetch— no per-vertex color upload on color changes. texelFetch/ WebGL2: Replacedtexture2DwithtexelFetchin shaders for direct texel access.G3dSubsetand offsets: IntroducedG3dSubsetas a filtered view of G3d instances.G3dMeshOffsetspre-computes vertex/index counts per mesh for buffer allocation.InsertableGeometrychunking: Merged mesh geometry split at 16M indices to stay within buffer limits.InstancedMeshFactory: Instanced meshes (>5 instances) built with per-instancepackedIdandignoreattributes pre-packed at build time.- Faster element mapping, bounding boxes, and array lookups: Hot paths replaced with typed array maps.
- Removed
vimx: Legacy vimx loading code removed. - No empty meshes: Meshes with zero instances are no longer created.
Camera & Controls
- Orbit target on select: Orbit target snaps to selected element center.
- Zoom resets orbit target: Scroll-to-zoom updates the orbit target to the cursor hit point.
- Floating target fix: Orbit target no longer drifts after camera reset.
- Touch: Pinch and pan gestures both fire correctly. Faster scroll.
lockMovement/lockRotation: Per-axis movement and rotation locks onViewerSettings.camera.- Delta time: Fixed camera animation delta time calculation.
Ultra Viewer Updates
- Color overrides: Ultra viewer supports
element.color = new RGBA32(...)overrides, aligned with WebGL API. nodeState→state: Renamed for clarity.element.state = VisibilityState.GHOSTED(enum:VISIBLE,HIDDEN,GHOSTED,HIGHLIGHTED).- Remove overrides: Ultra viewer supports removing overrides, matching WebGL.
- Error messages: Improved error/status display. Errors not minimized by default.
- Ultra 6.0.0 RPC: Updated RPC client to match Ultra 6 protocol.
Gizmos
- Marker GPU picking: Markers register
packedIdattribute for GPU picker hit detection. - Orbit gizmo: Camera gizmo rebuilt to match Ultra's visual style. Uses sphere coordinates.
- Section box / axes cleanup: Gizmo classes tightened, internals hidden behind
IGizmo*interfaces.
Developer Experience
- CLAUDE.md: Comprehensive architecture documentation. Covers loading pipeline, rendering pipeline, GPU picker, mesh type hierarchy, React patterns, and camera API.
- INPUT.md: Dedicated input system documentation.
@internaltags: System-only classes and methods marked throughout.- TypeDoc comments: Cleaned up and expanded across public API.
Breaking Changes
viewer.core.settingsremoved fromIWebglViewerStandardMaterial,modelColor,sectionStroke*removed fromIMaterialsMaterialSettings.useFastMaterials,.standard,.sectionremovedskybox,skylight,sunlightsremoved fromViewerSettingsviewer.react.camerarenamed toviewer.framingnodeStaterenamed tostate(Ultra); useVisibilityStateenum- Input key binding API changed — register via
inputs.keyboard.registerKeyDown() - Camera
lookAtparameter removed — useorbitTowards()orset(position, target)
Ultra Auth Token
Ultra Version: 5.0.0
Webgl
- Updated skybox color
- Fixed bug with section box face highlight when mouse leaving canvas
Ultra
- Implemented support for files with auth tokens
React
- Improved logger to show caller info
Various bugfixes
**WebG
- Fixed bug where the tooltip text was getting cut at the bottom
- The orbit ball gizmo no longer appears in first person mode
- Updated error messages and fixed typos
- Fixed a bug where outline wasn't correctly displayed when the section box was active
Ultra
- Updated ghost color to match desktop viewer
0.3.39
ULTRA requires Server 5.0.0
Webgl
- Updated threejs to latest version 0.171.0
- Updated all dependencies to recent versions
- Meshes with few instances are now merged along the unique meshes increasing performance
- Unique merged mesh is now chunked into chunks of 4M vertices
- Skybox material no longer writes to depth
- Changed base material from Phong to Lambert
- Removed the custom AA pass when camera was idle
- Updated rendering pipeline to use default Web2 render target AA
Migration Note:
The new threejs light model uses PI as the default value.
React
- Fixed a bug in error message where clicking the link would not open a new tab.
- Message box is now reactive to the scale of the app
- Updated the loading box to display if it is loading in webgl or ultra mode.
Ultra
- Updated client to match server 5.0.0
- Client will now disconnect and show an error message if no streams are available on the server
- Fixed a bug which caused flickering of the progress bar
- Removed mechanism where messages sent before calling connect would be sent after. (You can now await Connect())
Others
-Updated cdn urls to storage.cdn.vimaec.com
Package
- Made the output css file be called style.css
Initial vim-web release
0.3.27 Build website[ci skip]