<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!--
Privacy posture (VW1-21): this page performs ZERO network fetches
beyond loading its own JS/WASM bundles from the origin. No
analytics, no CDNs for fonts or scripts, no telemetry. Every
dropped RVT is parsed in this tab and discarded when the tab
closes. See docs/viewer-privacy-posture.md.
-->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; worker-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self' blob:;" />
<title>rvt-rs · client-side Revit viewer</title>
<link rel="stylesheet" href="/src/tokens.css" />
<link rel="stylesheet" href="/src/styles.css" />
</head>
<body>
<a class="skip-link" href="#viewport">Skip to viewport</a>
<div id="app">
<header role="banner">
<h1>rvt-rs <span>viewer</span></h1>
<span class="meta" id="file-meta">Drop a .rvt, .rfa, .rte or .rft file anywhere</span>
<span style="flex: 1"></span>
<span class="quality-pill" id="export-quality" aria-live="polite">Quality pending</span>
<label class="meta" for="export-mode" title="IFC export quality bar (Lane Seven). Stronger bars reject scaffold-only files.">
IFC bar
</label>
<select class="mode-select" id="export-mode" aria-label="IFC export quality mode">
<option value="scaffold" selected>scaffold</option>
<option value="typed-no-geometry">typed-no-geometry</option>
<option value="geometry">geometry</option>
<option value="strict">strict</option>
</select>
<button class="export-btn" id="export-glb" disabled title="Download 3D model as glTF 2.0 binary" aria-label="Export glTF">
Export glTF
</button>
<button class="export-btn primary" id="export-ifc" disabled title="Download as IFC4 STEP · quality label reflects decoded coverage" aria-label="Export IFC">
Export IFC
</button>
<button class="export-btn" id="export-svg" disabled title="Download 2D plan view as SVG" aria-label="Export plan SVG">
Export plan
</button>
</header>
<aside id="sidebar-left" aria-label="Scene tree and categories">
<h2 id="scene-tree-heading">Scene tree</h2>
<div class="tree-filter">
<input
id="tree-filter"
type="search"
placeholder="Filter by name, type or ElementId"
aria-label="Filter the scene tree"
aria-controls="tree"
aria-describedby="tree-filter-status"
autocomplete="off"
spellcheck="false"
disabled
/>
<div id="tree-filter-status" class="tree-filter-status" aria-live="polite"></div>
</div>
<div id="tree" role="tree" aria-labelledby="scene-tree-heading"></div>
<h2 id="categories-heading">Categories</h2>
<div id="categories" role="group" aria-labelledby="categories-heading"></div>
</aside>
<main id="viewport" tabindex="-1" aria-label="3D viewport">
<div id="dropzone" role="region" aria-label="Open a Revit file">
<div class="big">Drop a Revit file here</div>
<div class="hint">
Client-side only — every byte is parsed in this tab. No upload, no account, no
telemetry. Revit 2023, 2024 and 2025 projects open with their walls, doors, windows,
floors, roofs, stairs, furniture and fixtures under Revit's own ElementIds and
names; other releases and family files open with their metadata and schema. See
<a href="https://github.com/DrunkOnJava/rvt-rs/blob/main/docs/viewer-privacy-posture.md"
>privacy</a
>
and
<a href="https://github.com/DrunkOnJava/rvt-rs/blob/main/docs/status.md"
>status</a
>.
</div>
<div id="app-version" class="hint"></div>
<button id="pick-file" type="button" aria-controls="file-input">Choose file…</button>
<input id="file-input" type="file" accept=".rvt,.rfa,.rte,.rft" hidden aria-label="Revit file picker" />
<section id="demo-gallery" aria-label="Redistributable demo gallery">
<h2>Demo gallery</h2>
<div class="gallery-attribution" id="demo-attribution">Loading redistributable demos…</div>
<div id="demo-list" role="list"></div>
</section>
</div>
<!--
Loading treatment. Visually indeterminate (no fake percentage —
the decoder cannot report one), but it carries elapsed seconds
and a size-derived expectation so a long decode reads as work,
not as a hang. aria-hidden because #status already announces
every worker step on the same polite channel.
-->
<div id="load-overlay" class="hidden" aria-hidden="true">
<div class="load-card">
<div id="load-step">Opening file</div>
<div class="sweep-rail"></div>
<div id="load-metrics">
<span id="load-elapsed">0 s elapsed</span>
<span id="load-hint"></span>
</div>
<div class="load-privacy">Parsed in this tab. Nothing is uploaded.</div>
</div>
</div>
<div id="scaffold-note" class="hidden">
<div class="note-card" role="note">
<div class="note-title">Nothing to draw</div>
<p id="scaffold-note-body"></p>
<div class="note-actions">
<button class="note-btn" id="show-real-projects" type="button">
Show real projects
</button>
<button class="note-btn quiet" id="dismiss-scaffold-note" type="button">
Dismiss
</button>
</div>
</div>
</div>
<div id="toast-region" aria-live="polite" aria-atomic="false"></div>
<div id="status" role="status" aria-live="polite">idle</div>
</main>
<aside id="sidebar-right" aria-label="File status and element details">
<h2>File status</h2>
<div class="status-panel" id="status-panel" aria-live="polite"></div>
<button class="side-btn" id="download-diagnostics" disabled type="button" aria-label="Download diagnostics JSON">Download diagnostics</button>
<details id="diagnostics-details">
<summary>Diagnostics details</summary>
<pre id="diagnostics-json" aria-label="Diagnostics JSON"></pre>
</details>
<h2>Supported MVP workflow</h2>
<div class="mvp-workflow" id="mvp-workflow">
<strong>Open locally or pick a demo</strong> → read Decode / Export confidence →
inspect decoded elements → choose an IFC bar (scaffold is honest for synthetics) →
export only after checking the quality label → download diagnostics for partial
exports. Typed elements come from Revit 2024 and 2025 projects; other releases export
a scaffold.
</div>
<h2>Supported profile</h2>
<div class="support-matrix" aria-label="Supported Revit file profile">
<div class="support-row">
<span class="k">Formats</span>
<span class="v">.rvt, .rfa, .rte, .rft — metadata/schema across verified Revit 2016–2026 family samples.</span>
</div>
<div class="support-row">
<span class="k">Real projects</span>
<span class="v">Core Interior (2024) opens from the gallery with walls, slabs, columns, doors, windows, rooms and real storeys, and Einhoven (2023) with its walls and storeys. Both come from magnetar-io/revit-test-datasets (MIT) and are the files the CI browser regression gates.</span>
</div>
<div class="support-row">
<span class="k">Synthetics</span>
<span class="v">Tier1 demos (architectural-2024, structural-2023, mep-2024) open cleanly but decode as scaffold ~25% — that is expected, not a bug.</span>
</div>
<div class="support-row">
<span class="k">IFC now</span>
<span class="v">Revit 2024 and 2025 projects export walls, doors, windows, columns, slabs, roofs, ceilings, stairs, railings, rooms, curtain-wall parts, furniture, casework, lighting and plumbing fixtures, equipment, ducts, pipes, structural framing and foundations, and site elements, each under the ElementId and, for family instances, the Family:Type name Revit's own export gives it. Bodies are bounding boxes, refined to sketched outlines for slabs and roofs, trimmed runs for walls, beams along their axes, steps for stair flights and a shed roof along its slope. Other releases export ArcWalls (2023) or a valid scaffold. The typed / geometry / strict bars refuse a file that does not meet them.</span>
</div>
<div class="support-row">
<span class="k">3-D view</span>
<span class="v">Revit 2024 and 2025 walls, floors, roofs and ceilings are drawn as their layers, each in its material's shading colour, where the layers add up to the element's thickness. Other elements take their category's colour.</span>
</div>
<div class="support-row">
<span class="k">MVP target</span>
<span class="v">Revit 2024/2025 projects: levels, walls, floors, doors, windows, rooms, materials, and the building products around them.</span>
</div>
<div class="support-row">
<span class="k">Not ready</span>
<span class="v">Element records before Revit 2024; true element geometry beyond bounding boxes and sketched outlines for most categories; most parameters; layer sets for elements drawn whole or sloped; some elements Revit's own export leaves out, such as opening-cut families and curtain panels with no resolved type, which rvt-rs still exports; phase filtering; and semantic Revit editing.</span>
</div>
<a class="support-link" href="https://github.com/DrunkOnJava/rvt-rs/blob/main/docs/supported-profile.md">
Profile details
</a>
<a class="support-link" href="https://github.com/DrunkOnJava/rvt-rs/blob/main/docs/user-guide.md">
User guide
</a>
<a class="support-link" href="https://github.com/DrunkOnJava/rvt-rs/blob/main/docs/status.md">
Project status
</a>
<a class="support-link" href="https://github.com/DrunkOnJava/rvt-rs/blob/main/docs/diagnostic-semantics.md">
Diagnostic terms
</a>
</div>
<h2>Element info</h2>
<div id="info">Select an element in the 3-D view or scene tree (Enter / Space on a tree row). Double-click it or press F to zoom to it.</div>
<h2>Schedule</h2>
<div id="schedule-summary">
<!--
Only the total line announces. The per-type rows below it
are navigable content, not a status: reading the whole
breakdown aloud on every load would bury the one sentence
that actually changed.
-->
<div id="schedule-total" class="schedule-total" aria-live="polite">
Open a file to see the element breakdown.
</div>
<div id="schedule-groups" class="schedule-groups"></div>
<button class="side-btn" id="download-schedule" disabled type="button" aria-label="Download element schedule CSV" title="Every decoded element: type, level, material, placement, size, host. Opens in Excel, Sheets or LibreOffice.">Download element schedule (CSV)</button>
<button class="side-btn" id="download-rooms" disabled type="button" aria-label="Download room schedule CSV" title="Every decoded room: number, name, level.">Download room schedule (CSV)</button>
</div>
</aside>
</div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>