Publish a self-contained HTML dashboard so the user can view it in
ConstruChain Vision and optionally share it.
If you pass model_guid, the live 3D viewer is added automatically beside the
dashboard. Drive it ONLY via window.VisionViewer.isolate/color/zoomTo with
property filters {section, name, values} — never call parent/top.postMessage
yourself and never send raw object IDs/dbIds (publish is REJECTED, and even if
it weren't the viewer ignores them). See the "Vision 3D viewer" note below.
Build the HTML under these constraints (violations are rejected):
- One complete, self-contained document. All data INLINED as a static
snapshot (e.g. a const in a <script>). The page runs sandboxed with NO
network access — fetch/XHR/WebSocket all fail, so never load data or
assets at runtime; loaders that fetch a file (GLTF, d3.json, map
tiles) will not work. Inline geometry/data instead. Scripts run only
after the complete HTML has streamed, so incomplete/truncated documents
are rejected. There is no chunked/partial upload — the whole document
must arrive in one call, so keep it compact (see the size note below).
- Use any of the hosted libraries below, referenced from /assets/.
Combine only the few you actually need (referenced libs do NOT count
toward the size limit, but each one is extra weight the viewer loads).
No other external scripts, styles, fonts or CDNs — only /assets/ is
reachable, and no library here needs unsafe-eval.
Classic globals — add <script src="/assets/NAME"></script>, then use
the global:
charts: echarts-5.6.0.min.js (echarts), chart-4.4.9.umd.min.js
(Chart), apexcharts-3.54.1.min.js (ApexCharts),
plotly-2.35.2.min.js (Plotly), d3-7.9.0.min.js (d3; also d3.format
for number/currency), uplot-1.6.32.iife.min.js (uPlot, dense
time-series; + <link> uplot-1.6.32.min.css)
3D: babylon-7.34.4.min.js (BABYLON)
UI (React-like, no build): preact-10.24.3.umd.js (preact) +
preact-hooks-10.24.3.umd.js (preactHooks) + htm-3.1.1.umd.js (htm)
tables: tabulator-6.3.0.min.js (Tabulator; + <link>
tabulator-6.3.0.min.css), gridjs-6.2.0.umd.js (gridjs; + <link>
gridjs-mermaid-6.2.0.min.css)
animation: gsap-3.12.5.min.js (gsap), anime-3.2.2.min.js (anime)
dates: dayjs-1.11.13.min.js (dayjs)
Vision 3D viewer: pass model_guid to link the model and the trusted
dash shell renders the live 3D viewer AUTOMATICALLY in a resizable
split pane beside your dashboard (identical to the in-app viewer,
with a draggable divider). DO NOT add any viewer element to the
HTML — no <vision-model-viewer>, iframe, canvas, Autodesk/Forge
client, and no split/column/grid layout reserving space for 3D.
Build the report as if it fills the full width; the viewer is added
to its right. Drive it from your charts/buttons via
window.VisionViewer (the vision-viewer-bridge.js script is injected
automatically for model-linked dashboards): VisionViewer.isolate(
filter); VisionViewer.color(filter, '#2563EB');
VisionViewer.zoomTo(filter); VisionViewer.reset();
VisionViewer.clearColors(). A filter is {section, name,
values:[...]} — property filters only. Example (query_model first to
get the exact section/name/values, then wire a button):
<button id="op">EM OPERAÇÃO</button>
<script>op.onclick=()=>VisionViewer.isolate(
{section:'SmartBIM',name:'STATUS',values:['EM OPERAÇÃO']});</script>
Never call parent/top.postMessage yourself and never send raw object
IDs/dbIds, Autodesk URNs, access tokens, or any network call from
dashboard HTML (all are rejected at publish; the viewer accepts only
property filters). filter.values must match the model's stored text
EXACTLY (case, accents and even source typos included) — query the
real values first with query_model/get_model_schema; a value that
does not match simply isolates nothing. There is no
filter-by-object_id command by design: to target a specific
element, inline a map from object_id to a per-element unique
property (UID/GlobalId/Tag/…) and isolate by that property. Live
filtering needs the model's processed property index; if commands
do nothing and the viewer reports "property artifacts are not
available", the model must be re-processed once (Rerun Processing).
Cross-filter contract: combine active dashboard dimensions and status
in one filter array. Separate filter objects are intersected (AND);
values inside one filter are alternatives (OR). When an interaction
colors and selects, send color commands first with the same active
dimension filters, then finish each interaction with isolate or
zoomTo. Final isolate/zoomTo result becomes toolbar selection used by
"Zoom on selected" and "Hide unselected"; color never defines toolbar
selection. Example:
const selection=[
{section:'SmartBIM',name:'MODULE',values:['BOX1-MD03']},
{section:'SmartBIM',name:'Status',values:['AVAILABLE','BUSY']}
];
VisionViewer.color([
selection[0],
{section:'SmartBIM',name:'Status',values:['BUSY']}
], '#EF4444');
VisionViewer.isolate(selection);
ES modules — declare an import map, then import in a module script:
<script type="importmap">{"imports":{
"three":"/assets/three-0.169.0.esm.min.js",
"lit":"/assets/lit-3.2.1.esm.min.js",
"lit/decorators.js":"/assets/lit-3.2.1.esm.min.js"}}</script>
<script type="module">import * as THREE from 'three'; // WebGL/3D
import {LitElement, html, css} from 'lit'; // web components
</script>
- Fonts: @font-face with src url(/assets/inter-variable.woff2) — also
montserrat-variable.woff2, jetbrains-mono-variable.woff2,
geist-variable.woff2. No Google Fonts / remote fonts.
- CSS: inline (<style> or style=), or a hosted <link rel="stylesheet"
href="/assets/…css"> for the libraries above. Images only as data: URIs.
- Do not include ConstruChain/Vision product chrome: no topbar, navbar,
report button, legal banner, or shell around the dashboard. The dash
host adds the standard Vision topbar above your HTML; start your
document at the dashboard content.
- Not allowed: iframe/object/embed/form/base/applet, <meta http-equiv>,
srcset, and any external or relative URL (only '#...', 'data:' and
'/assets/...' pass). Inline <script>/<style> and import maps are fine.
- Max size 1.5 MB — aggregate with query_model instead of embedding raw rows.
- Charts inside hidden tabs/panels (display:none) often initialize at
0x0. Initialize them lazily when the tab becomes visible and call the
chart library's resize() method after showing the panel.
visibility may be "workspace" (default), "members", or "public".
"members" requires model_guid (from list_models) or project_id so Vision
can link the dashboard to a project; Phase 1 treats members as workspace
members, and Phase 2 tightens it to project access. Public dashboards are
shareable by URL, so use them only when the user explicitly asks.
Workspace dashboard limits come from the user's current plan.
By default the dashboard is created with workspace visibility: workspace
members view it in Vision (manage_url). Only set visibility="public" when
the user explicitly asks for a shareable public page. To update an
existing dashboard with fresh data pass its dashboard_id (URL and
visibility are preserved). Pass dry_run=true to validate the title/html and receive
errors without creating or replacing anything. For a large dashboard,
aggregate the data with query_model first (never embed raw rows) and run a
dry_run to catch truncation/size before spending the publish.
publish_dashboard