{/* ── File Operations ── */}
Open IFC File
{/* Add Model button - only shown when models are loaded */}
{hasModelsLoaded && (
Add Model to Scene (Multi-select supported)
)}
e.preventDefault()}>
Export IFC (with changes)
}
/>
e.preventDefault()}>
Export GLB (3D Model)
}
/>
e.preventDefault()}>
Export HBJSON (Energy Model)
}
/>
Export CSV
handleExportCSV('entities')}>
Entities
handleExportCSV('properties')}>
Properties
handleExportCSV('quantities')}>
Quantities
handleExportCSV('spatial')}>
Spatial Hierarchy
Export JSON (All Data)
Screenshot
{/* Edit Menu - Bulk editing and data import */}
Edit Properties
e.preventDefault()}>
Bulk Property Editor
}
/>
e.preventDefault()}>
Import Data (CSV)
}
/>
{/* Export Changes Button - shows when there are pending mutations */}
{/* ── Panels ── */}
{workspacePanelLabel ? `Panels: ${workspacePanelLabel}` : 'Panels'}
Workspace
handleToggleBottomPanel('script')}
>
Script Editor
handleToggleBottomPanel('list')}
>
Lists
handleToggleBottomPanel('gantt')}
>
Schedule (Gantt)
Inspect & validate
handleToggleRightPanel('bcf')}
>
BCF Issues
handleToggleRightPanel('ids')}
>
IDS Validation
handleToggleRightPanel('lens')}
>
Lens Rules
handleToggleRightPanel('clash')}
>
Clash Detection
handleToggleRightPanel('compare')}
>
Compare Models
Author
handleToggleRightPanel('addElement')}
>
Add Element
handleToggleRightPanel('extensions')}
>
Extensions
{(rightAnalysisExtensions.length > 0 || bottomAnalysisExtensions.length > 0) && (
<>
Analysis extensions
{rightAnalysisExtensions.map((extension) => {
const Icon = extension.icon;
return (
handleToggleAnalysisExtension(extension.id)}
>
{extension.label}
);
})}
{bottomAnalysisExtensions.map((extension) => {
const Icon = extension.icon;
return (
handleToggleAnalysisExtension(extension.id)}
>
{extension.label}
);
})}
>
)}
{/* ── Search (Tier-0 inline; ⌘F or / to focus) ── */}
{/* ── Navigation Tools ── */}
{/* ── Edit Mode pill ──
Single global switch that unlocks every authoring affordance
(inline property/attribute editors in the Properties panel,
the add-element draw tools, georeference placement, and
future geometry manipulators). Off by default — viewer-only
users never see edit chrome. Press E to toggle.
See `uiSlice.editEnabled`. */}
{editEnabled ? 'Exit Edit Mode' : 'Edit Mode'} E
{/* Undo / Redo — always visible (any authoring op pushes a
mutation; the buttons read disabled when the active
model's undo stack is empty). Pinned next to Edit so the
user has a one-click recovery for any change. */}
{/* Space Sketch is authoring chrome (it bakes IfcSpace
entities), so like every other authoring affordance it only
surfaces in edit mode — keeping the default toolbar lean.
It lives next to the Edit pill that reveals it, with the
same purple accent, and a drafting icon distinct from the
square/grid icons (Panels, Basket, View options). */}
{editEnabled && (
)}
{/* Draw / modify gestures live in the existing Add Element
panel (right-side `AddElementPanel`, opened via the Add
Element button) and in the contextual Geometry edit card
inside the Properties panel — splitting a selected wall,
duplicating, rotating, etc. all happen there. Keeping the
toolbar minimal: just the Edit mode switch + the
navigation tools. Per-element-type draw pills duplicated
the AddElement panel and added clutter. */}
{/* (no draw pills here — by design) */}
{/* ── Measurement & Section ── */}
{/* Storey navigation + level display (Stacked / Exploded / Solo) moved
into the Hierarchy panel's Building Storeys section so every "level"
concept lives in one place — see `StoreyDisplayControls`. The two
adjacent storey buttons that used to sit here (Quick Floorplan +
Level display) were retired to fix the duplicate-button confusion. */}
{/* ── Basket Presentation ── */}
Basket Presentation Dock (Views: {basketViewCount}, Entities: {pinboardEntities.size})
{/*
Selection action cluster — Hide / Frame / Isolate only make
sense with a selection, so they don't get to live in the
toolbar chrome at rest. When a user selects anything, the
slot opens with a "N selected" pill + the three actions next
to it. Hotkeys (Del / F / I / =) keep working regardless of
whether the chip is rendered, so power users feel no change.
The chip lives in the same separator zone the buttons used to
occupy so the spatial location is familiar to muscle memory.
*/}
{selectionCount > 0 && (
{selectionCount} sel
cameraCallbacks.frameSelection?.()}
shortcut="F"
/>
)}
cameraCallbacks.fitAll?.()} shortcut="Z" />
{mergeLayers ? 'Visibility · Merge Multilayer Walls is on' : 'Visibility'}
{/*
Settings-style panel (not a list of menu-items): each row is a
plain
{/* ── Camera & View ── */}
{/*
Cesium 3D World Context — sits next to Home as a raw button so
the world-context affordance is one click away when a model has
georeferencing. When active, the "Move georeference" sub-toggle
appears beside it (its amber tint signals a modal pose whose
exit affordance must stay visible).
*/}
{cesiumAvailable && (
<>
{cesiumEnabled ? 'Hide' : 'Show'} 3D World Context (Cesium)
{cesiumEnabled && (
{cesiumPlacementEditMode ? 'Stop moving georeference' : 'Move georeference'}
)}
>
)}
{/* Sun & Sky panel — sky, lighting presets and the sun-path study.
Available for every model, georeferenced or not. */}
Sun & sky
{/*
Consolidated View dropdown — holds projection toggle, preset
views, and hover tooltips. These are "view options" the user
reaches for occasionally, and rendering each as a raw icon
button used to dominate the toolbar's right half. Cesium stayed
inline (above) because the world-context overlay is a primary
affordance, not a tucked-away view setting.
*/}
View options
Preset views
Isometric H
cameraCallbacks.setPresetView?.('top')}>
Top 1
cameraCallbacks.setPresetView?.('bottom')}>
Bottom 2
cameraCallbacks.setPresetView?.('front')}>
Front 3
cameraCallbacks.setPresetView?.('back')}>
Back 4
cameraCallbacks.setPresetView?.('left')}>
Left 5
cameraCallbacks.setPresetView?.('right')}>
Right 6
Projection
toggleProjectionMode()}
>
Orthographic
Helpers
toggleHoverTooltips()}
>
Hover tooltips
{/* Spacer */}
{/* Extension toolbar contributions (right-aligned) */}
{/* Loading Progress */}
{loading && (geometryProgress || metadataProgress || progress) && (
{(geometryProgress ?? metadataProgress ?? progress)?.phase}
{geometryProgress && metadataProgress ? ` | ${metadataProgress.phase}` : ''}
{(geometryProgress ?? metadataProgress ?? progress)?.indeterminate ? (
) : (
<>
{Math.round((geometryProgress ?? metadataProgress ?? progress)?.percent ?? 0)}%
>
)}
)}
{/* Error Display */}
{error && (
{error}
)}
{/* Right Side Actions — /mcp moved to the Info dialog header so
the toolbar's meta cluster stays focused on shell chrome
(Settings · Theme · Help). */}
Toggle theme (Shift+click for secret mode)
Info (?)
);
}