mirror of
https://github.com/brunopostle/ifcurl.git
synced 2026-07-12 10:18:14 +00:00
viewer: add model metadata panel with type counts and storey isolation
A new '☰' toolbar button opens a panel showing the IFC type breakdown (sorted by element count) and storey list. Clicking a storey isolates its elements via OBC.Hider; clicking again restores full visibility. Closes ifcurl-dag. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a5d14222a5
commit
14c8412074
2 changed files with 122 additions and 1 deletions
|
|
@ -45,7 +45,7 @@
|
||||||
{"id":"ifcurl-ach","title":"Deploy Phase 3b footer template to Forgejo","description":"The footer.tmpl file that injects a 'View in 3D' button on .ifc file pages has been written but not yet deployed. Deploy it to /etc/forgejo/templates/custom/footer.tmpl and verify the button appears when viewing an .ifc file on localhost:3000.","status":"closed","priority":2,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-04-23T05:47:53Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:14:35Z","closed_at":"2026-04-23T06:14:35Z","close_reason":"Split into atomic tasks: ifcurl-ohq, ifcurl-49o","dependencies":[{"issue_id":"ifcurl-ach","depends_on_id":"ifcurl-ahd","type":"blocks","created_at":"2026-04-23T07:02:40Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
|
{"id":"ifcurl-ach","title":"Deploy Phase 3b footer template to Forgejo","description":"The footer.tmpl file that injects a 'View in 3D' button on .ifc file pages has been written but not yet deployed. Deploy it to /etc/forgejo/templates/custom/footer.tmpl and verify the button appears when viewing an .ifc file on localhost:3000.","status":"closed","priority":2,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-04-23T05:47:53Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:14:35Z","closed_at":"2026-04-23T06:14:35Z","close_reason":"Split into atomic tasks: ifcurl-ohq, ifcurl-49o","dependencies":[{"issue_id":"ifcurl-ach","depends_on_id":"ifcurl-ahd","type":"blocks","created_at":"2026-04-23T07:02:40Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
|
||||||
{"id":"ifcurl-6ys","title":"Research alternative URL schemes before ifc:// is committed to","description":"The ifc:// scheme name is not yet set in stone. It's worth researching whether a different scheme would be more appropriate or less likely to conflict with existing tooling.\\n\\nLook at how other tools handle similar problems:\\n- pip uses git+https:// and git+ssh:// (prefix on top of existing schemes)\\n- Are there existing ifc:// registrations or tools that would conflict?\\n- What scheme patterns are used by other domain-specific version-controlled asset tools?\\n- Alternatives to consider: ifcgit://, git+ifc://, ifc+git://, or a vendor-prefixed form\\n- Check IANA registered schemes and common-use unregistered schemes for conflicts\\n\\nGoal: make a confident decision to keep ifc:// or switch, before the scheme is baked into any external integrations.","notes":"Research complete. Summary:\n\n- ifc is NOT registered with IANA. bcf, bim also unregistered. Clean namespace.\n- No existing tool or buildingSMART standard uses ifc:// — no conflict risk.\n- BCF (bcf://) would be confusing — BCF is a live buildingSMART standard, don't use it.\n- pip-style compound schemes (ifc+git://, ifc+https://) look appealing but hurt more than they help: they fragment the transport-inference design, improve nothing for OS handlers or terminal clickability, and are harder to explain.\n- Forgejo: CUSTOM_URL_SCHEMES defaults to allowing all schemes — ifc:// links render fine out of the box.\n- GitHub/terminals: bare custom URLs are never auto-linked regardless of scheme choice. Explicitly formatted markdown links work fine.\n\nDecision: keep ifc://\n\nAction to add to roadmap: submit a provisional IANA registration (First Come First Served, ~1 hour, just an email to iana@iana.org with scheme name, syntax summary, security notes, and a spec URL). Do this when Phase 1 spec stabilises.","status":"closed","priority":3,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-25T09:08:13Z","created_by":"Bruno Postle","updated_at":"2026-04-25T11:17:44Z","started_at":"2026-04-25T11:04:50Z","closed_at":"2026-04-25T11:17:44Z","close_reason":"Keep ifc://. Namespace is clean (no IANA registration, no prior art). Compound schemes (ifc+git://) would break transport-inference design with no practical benefit. Action item: provisional IANA registration when Phase 1 spec stabilises.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"id":"ifcurl-6ys","title":"Research alternative URL schemes before ifc:// is committed to","description":"The ifc:// scheme name is not yet set in stone. It's worth researching whether a different scheme would be more appropriate or less likely to conflict with existing tooling.\\n\\nLook at how other tools handle similar problems:\\n- pip uses git+https:// and git+ssh:// (prefix on top of existing schemes)\\n- Are there existing ifc:// registrations or tools that would conflict?\\n- What scheme patterns are used by other domain-specific version-controlled asset tools?\\n- Alternatives to consider: ifcgit://, git+ifc://, ifc+git://, or a vendor-prefixed form\\n- Check IANA registered schemes and common-use unregistered schemes for conflicts\\n\\nGoal: make a confident decision to keep ifc:// or switch, before the scheme is baked into any external integrations.","notes":"Research complete. Summary:\n\n- ifc is NOT registered with IANA. bcf, bim also unregistered. Clean namespace.\n- No existing tool or buildingSMART standard uses ifc:// — no conflict risk.\n- BCF (bcf://) would be confusing — BCF is a live buildingSMART standard, don't use it.\n- pip-style compound schemes (ifc+git://, ifc+https://) look appealing but hurt more than they help: they fragment the transport-inference design, improve nothing for OS handlers or terminal clickability, and are harder to explain.\n- Forgejo: CUSTOM_URL_SCHEMES defaults to allowing all schemes — ifc:// links render fine out of the box.\n- GitHub/terminals: bare custom URLs are never auto-linked regardless of scheme choice. Explicitly formatted markdown links work fine.\n\nDecision: keep ifc://\n\nAction to add to roadmap: submit a provisional IANA registration (First Come First Served, ~1 hour, just an email to iana@iana.org with scheme name, syntax summary, security notes, and a spec URL). Do this when Phase 1 spec stabilises.","status":"closed","priority":3,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-25T09:08:13Z","created_by":"Bruno Postle","updated_at":"2026-04-25T11:17:44Z","started_at":"2026-04-25T11:04:50Z","closed_at":"2026-04-25T11:17:44Z","close_reason":"Keep ifc://. Namespace is clean (no IANA registration, no prior art). Compound schemes (ifc+git://) would break transport-inference design with no practical benefit. Action item: provisional IANA registration when Phase 1 spec stabilises.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"id":"ifcurl-gy8","title":"Viewer: 'View in 3D' uses commit hash but no UI affordance to switch to branch tip","description":"The 'View in 3D' button in footer.tmpl always generates an ifc:// URL with a commit hash (@abc123...). This is correct for reproducibility, but users who want to track 'the latest version of main' must manually change the URL from @\u003chash\u003e to @heads/main. There is no indication in the viewer that the model is pinned to a specific commit. Add a visual indicator (e.g. 'commit abc1234 · switch to heads/main') in the viewer toolbar when the ref is a commit hash, offering a one-click switch to the branch tip. The branch name could be inferred from Forgejo's API given the commit hash.","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:04:58Z","created_by":"Bruno Postle","updated_at":"2026-04-25T18:12:57Z","started_at":"2026-04-25T18:02:24Z","closed_at":"2026-04-25T18:12:57Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"id":"ifcurl-gy8","title":"Viewer: 'View in 3D' uses commit hash but no UI affordance to switch to branch tip","description":"The 'View in 3D' button in footer.tmpl always generates an ifc:// URL with a commit hash (@abc123...). This is correct for reproducibility, but users who want to track 'the latest version of main' must manually change the URL from @\u003chash\u003e to @heads/main. There is no indication in the viewer that the model is pinned to a specific commit. Add a visual indicator (e.g. 'commit abc1234 · switch to heads/main') in the viewer toolbar when the ref is a commit hash, offering a one-click switch to the branch tip. The branch name could be inferred from Forgejo's API given the commit hash.","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:04:58Z","created_by":"Bruno Postle","updated_at":"2026-04-25T18:12:57Z","started_at":"2026-04-25T18:02:24Z","closed_at":"2026-04-25T18:12:57Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"id":"ifcurl-dag","title":"Viewer: no model metadata panel — disciplines, spatial structure, element counts","description":"Beyond individual element selection, reviewers and designers need a model overview: what disciplines/types are present, how many elements, what building storeys exist, what's the spatial structure (site/building/storey/space hierarchy). A collapsible side panel showing IFC spatial tree and type breakdown would give immediate context and allow navigating by storey or discipline. Without this, reviewers must already know the model structure before opening the viewer.","status":"open","priority":3,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-24T11:04:55Z","created_by":"Bruno Postle","updated_at":"2026-04-24T11:04:55Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"id":"ifcurl-dag","title":"Viewer: no model metadata panel — disciplines, spatial structure, element counts","description":"Beyond individual element selection, reviewers and designers need a model overview: what disciplines/types are present, how many elements, what building storeys exist, what's the spatial structure (site/building/storey/space hierarchy). A collapsible side panel showing IFC spatial tree and type breakdown would give immediate context and allow navigating by storey or discipline. Without this, reviewers must already know the model structure before opening the viewer.","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:04:55Z","created_by":"Bruno Postle","updated_at":"2026-04-25T19:04:25Z","started_at":"2026-04-25T18:38:17Z","closed_at":"2026-04-25T19:04:25Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"id":"ifcurl-ov6","title":"Viewer selector only supports type names — property filters require server round-trip","description":"The viewer's applySelector() only supports IFC type names and their unions ('IfcWall+IfcSlab'). Full IfcOpenShell selector syntax — filter by property value (Pset_WallCommon.FireRating=2HR), by GlobalId, by name (.Name='Core Wall'), by spatial containment — is not supported. For collaboration, reviewers need to select specific elements ('all walls with fire rating 2HR that aren't compliant') not just all walls. The service's /preview endpoint already handles full selector syntax server-side. The viewer should forward complex selectors to the service and use the rendered image, or fetch the matching GlobalIds from a new /resolve-selector service endpoint and apply them in the viewer.","status":"open","priority":3,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-24T11:04:38Z","created_by":"Bruno Postle","updated_at":"2026-04-24T11:04:38Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"id":"ifcurl-ov6","title":"Viewer selector only supports type names — property filters require server round-trip","description":"The viewer's applySelector() only supports IFC type names and their unions ('IfcWall+IfcSlab'). Full IfcOpenShell selector syntax — filter by property value (Pset_WallCommon.FireRating=2HR), by GlobalId, by name (.Name='Core Wall'), by spatial containment — is not supported. For collaboration, reviewers need to select specific elements ('all walls with fire rating 2HR that aren't compliant') not just all walls. The service's /preview endpoint already handles full selector syntax server-side. The viewer should forward complex selectors to the service and use the rendered image, or fetch the matching GlobalIds from a new /resolve-selector service endpoint and apply them in the viewer.","status":"open","priority":3,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-24T11:04:38Z","created_by":"Bruno Postle","updated_at":"2026-04-24T11:04:38Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"id":"ifcurl-f3p","title":"footer.tmpl: 'View in 3D' button absent from file history (git log) page","description":"The 'View in 3D' button is injected on the file view page (showing one commit of a file). It is not shown on the file history page (the list of commits that touched a file), which is where users naturally go to see how a model evolved over time. Each entry in the history list already has a commit hash in its href. Injecting a 'View in 3D' link or button alongside each history entry would let reviewers quickly compare model states across commits — which is a primary collaboration use case.","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:04:23Z","created_by":"Bruno Postle","updated_at":"2026-04-24T14:04:56Z","started_at":"2026-04-24T13:21:51Z","closed_at":"2026-04-24T14:04:56Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"id":"ifcurl-f3p","title":"footer.tmpl: 'View in 3D' button absent from file history (git log) page","description":"The 'View in 3D' button is injected on the file view page (showing one commit of a file). It is not shown on the file history page (the list of commits that touched a file), which is where users naturally go to see how a model evolved over time. Each entry in the history list already has a commit hash in its href. Injecting a 'View in 3D' link or button alongside each history entry would let reviewers quickly compare model states across commits — which is a primary collaboration use case.","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:04:23Z","created_by":"Bruno Postle","updated_at":"2026-04-24T14:04:56Z","started_at":"2026-04-24T13:21:51Z","closed_at":"2026-04-24T14:04:56Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"id":"ifcurl-4rj","title":"Viewer: no branch/ref switcher — can't navigate model history or compare versions","description":"The viewer loads a single ifc:// URL and has no UI to switch to a different ref. A reviewer wanting to see 'what did this look like on main before the PR' or 'show me the v1.2 tag' must manually edit the URL. For a collaboration tool, being able to flip between 'current PR branch' and 'base branch' at the same camera view is essential for review. Consider a ref dropdown or input in the toolbar that reconstructs the URL with a different @ref, preserving camera/clip/selector state. The structured fields already expose repo/ref/path — the ref field is editable but not prominently labelled as a version picker.","status":"open","priority":3,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-24T11:04:20Z","created_by":"Bruno Postle","updated_at":"2026-04-24T11:04:20Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"id":"ifcurl-4rj","title":"Viewer: no branch/ref switcher — can't navigate model history or compare versions","description":"The viewer loads a single ifc:// URL and has no UI to switch to a different ref. A reviewer wanting to see 'what did this look like on main before the PR' or 'show me the v1.2 tag' must manually edit the URL. For a collaboration tool, being able to flip between 'current PR branch' and 'base branch' at the same camera view is essential for review. Consider a ref dropdown or input in the toolbar that reconstructs the URL with a different @ref, preserving camera/clip/selector state. The structured fields already expose repo/ref/path — the ref field is editable but not prominently labelled as a version picker.","status":"open","priority":3,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-24T11:04:20Z","created_by":"Bruno Postle","updated_at":"2026-04-24T11:04:20Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,35 @@
|
||||||
background: rgba(28,28,30,0.7);
|
background: rgba(28,28,30,0.7);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
/* Model metadata panel */
|
||||||
|
#meta-panel {
|
||||||
|
position: fixed; bottom: 44px; left: 12px; z-index: 10;
|
||||||
|
background: rgba(28,28,30,0.92); backdrop-filter: blur(8px);
|
||||||
|
border: 1px solid rgba(255,255,255,0.12); border-radius: 6px;
|
||||||
|
padding: 8px 10px; min-width: 180px; max-width: 260px;
|
||||||
|
max-height: calc(100vh - 130px); overflow-y: auto;
|
||||||
|
}
|
||||||
|
#meta-panel h3 {
|
||||||
|
font-size: 10px; color: #606068;
|
||||||
|
text-transform: uppercase; letter-spacing: 0.06em;
|
||||||
|
margin-top: 10px; margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
#meta-panel h3:first-child { margin-top: 0; }
|
||||||
|
.meta-row {
|
||||||
|
display: flex; align-items: baseline; gap: 4px;
|
||||||
|
margin-top: 2px; padding: 1px 3px; border-radius: 3px;
|
||||||
|
}
|
||||||
|
.meta-row.storey-row { cursor: pointer; }
|
||||||
|
.meta-row.storey-row:hover { background: rgba(255,255,255,0.07); }
|
||||||
|
.meta-row.storey-row.active { background: rgba(80,120,255,0.2);
|
||||||
|
color: #a8c8ff; }
|
||||||
|
.meta-name {
|
||||||
|
font-size: 11px; color: #d0d0d8; font-family: monospace;
|
||||||
|
flex: 1; min-width: 0; overflow: hidden;
|
||||||
|
text-overflow: ellipsis; white-space: nowrap;
|
||||||
|
}
|
||||||
|
.meta-row.storey-row.active .meta-name { color: #a8c8ff; }
|
||||||
|
.meta-count { font-size: 10px; color: #808088; flex-shrink: 0; }
|
||||||
/* Element properties panel */
|
/* Element properties panel */
|
||||||
#props-panel {
|
#props-panel {
|
||||||
position: fixed; bottom: 44px; right: 12px; z-index: 10;
|
position: fixed; bottom: 44px; right: 12px; z-index: 10;
|
||||||
|
|
@ -113,6 +142,7 @@
|
||||||
spellcheck="false">
|
spellcheck="false">
|
||||||
<button id="clip-btn" class="tb-btn" title="Double-click model surface to add clipping plane">✂ clip</button>
|
<button id="clip-btn" class="tb-btn" title="Double-click model surface to add clipping plane">✂ clip</button>
|
||||||
<button id="clip-clear-btn" class="tb-btn" title="Remove all clipping planes" style="display:none">⊗ clips</button>
|
<button id="clip-clear-btn" class="tb-btn" title="Remove all clipping planes" style="display:none">⊗ clips</button>
|
||||||
|
<button id="meta-btn" class="tb-btn" title="Model overview: types and storeys">☰</button>
|
||||||
<button id="bcf-btn" class="tb-btn" title="Export current view as a BCF issue">BCF</button>
|
<button id="bcf-btn" class="tb-btn" title="Export current view as a BCF issue">BCF</button>
|
||||||
<button id="copy-url-btn" class="tb-btn" title="Copy ifc:// URL to clipboard">⎘</button>
|
<button id="copy-url-btn" class="tb-btn" title="Copy ifc:// URL to clipboard">⎘</button>
|
||||||
<button id="new-issue-btn" class="tb-btn" title="Open new issue with this view URL">Issue</button>
|
<button id="new-issue-btn" class="tb-btn" title="Open new issue with this view URL">Issue</button>
|
||||||
|
|
@ -165,6 +195,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="viewer"></div>
|
<div id="viewer"></div>
|
||||||
|
<div id="meta-panel" style="display:none"></div>
|
||||||
<div id="status"></div>
|
<div id="status"></div>
|
||||||
<div id="props-panel" style="display:none">
|
<div id="props-panel" style="display:none">
|
||||||
<div id="props-panel-header">
|
<div id="props-panel-header">
|
||||||
|
|
@ -283,6 +314,14 @@
|
||||||
// Disable camera controls while any input has focus.
|
// Disable camera controls while any input has focus.
|
||||||
function onInputFocus() { if (cameraControls) cameraControls.enabled = false; }
|
function onInputFocus() { if (cameraControls) cameraControls.enabled = false; }
|
||||||
function onInputBlur() { if (cameraControls) cameraControls.enabled = true; }
|
function onInputBlur() { if (cameraControls) cameraControls.enabled = true; }
|
||||||
|
const metaBtn = document.getElementById("meta-btn");
|
||||||
|
const metaPanel = document.getElementById("meta-panel");
|
||||||
|
metaBtn.addEventListener("click", () => {
|
||||||
|
const open = metaPanel.style.display === "none";
|
||||||
|
metaPanel.style.display = open ? "" : "none";
|
||||||
|
metaBtn.classList.toggle("active", open);
|
||||||
|
});
|
||||||
|
|
||||||
const bcfBtn = document.getElementById("bcf-btn");
|
const bcfBtn = document.getElementById("bcf-btn");
|
||||||
const bcfRow = document.getElementById("bcf-row");
|
const bcfRow = document.getElementById("bcf-row");
|
||||||
const bcfTitleIn = document.getElementById("bcf-title");
|
const bcfTitleIn = document.getElementById("bcf-title");
|
||||||
|
|
@ -833,6 +872,84 @@
|
||||||
propsPanel.style.display = "";
|
propsPanel.style.display = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Model metadata panel population.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
async function populateMetaPanel(components) {
|
||||||
|
const classifier = components.get(OBC.Classifier);
|
||||||
|
const hider = components.get(OBC.Hider);
|
||||||
|
|
||||||
|
// --- Type breakdown ---
|
||||||
|
await classifier.byCategory();
|
||||||
|
const categories = classifier.list.get("Categories");
|
||||||
|
const types = [];
|
||||||
|
if (categories) {
|
||||||
|
for (const [name, groupData] of categories) {
|
||||||
|
let count = 0;
|
||||||
|
for (const ids of Object.values(groupData.map)) count += ids.length;
|
||||||
|
if (count > 0) types.push({ name, count });
|
||||||
|
}
|
||||||
|
types.sort((a, b) => b.count - a.count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Storey list ---
|
||||||
|
await classifier.byIfcBuildingStorey();
|
||||||
|
const storeyGroups = classifier.list.get("Storeys");
|
||||||
|
const storeyNames = storeyGroups ? [...storeyGroups.keys()] : [];
|
||||||
|
|
||||||
|
// --- Render ---
|
||||||
|
const html = [];
|
||||||
|
|
||||||
|
if (types.length) {
|
||||||
|
html.push("<h3>Types</h3>");
|
||||||
|
for (const { name, count } of types) {
|
||||||
|
html.push(
|
||||||
|
`<div class="meta-row">` +
|
||||||
|
`<span class="meta-name">${name}</span>` +
|
||||||
|
`<span class="meta-count">${count}</span>` +
|
||||||
|
`</div>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storeyNames.length) {
|
||||||
|
html.push("<h3>Storeys</h3>");
|
||||||
|
for (const name of storeyNames) {
|
||||||
|
html.push(
|
||||||
|
`<div class="meta-row storey-row" data-storey="${name.replace(/"/g, """)}">` +
|
||||||
|
`<span class="meta-name">${name}</span>` +
|
||||||
|
`</div>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!types.length && !storeyNames.length) {
|
||||||
|
html.push('<div class="meta-row"><span class="meta-name" style="color:#505058">No metadata</span></div>');
|
||||||
|
}
|
||||||
|
|
||||||
|
metaPanel.innerHTML = html.join("");
|
||||||
|
|
||||||
|
// Wire up storey click-to-isolate.
|
||||||
|
let activeRow = null;
|
||||||
|
for (const row of metaPanel.querySelectorAll(".storey-row")) {
|
||||||
|
row.addEventListener("click", async () => {
|
||||||
|
const name = row.dataset.storey;
|
||||||
|
if (activeRow === row) {
|
||||||
|
// Toggle off: show all.
|
||||||
|
activeRow = null;
|
||||||
|
row.classList.remove("active");
|
||||||
|
await hider.set(true);
|
||||||
|
} else {
|
||||||
|
activeRow?.classList.remove("active");
|
||||||
|
activeRow = row;
|
||||||
|
row.classList.add("active");
|
||||||
|
const found = await classifier.find({ Storeys: [name] });
|
||||||
|
await hider.isolate(found);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// Commit-pin indicator helpers.
|
// Commit-pin indicator helpers.
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|
@ -989,6 +1106,10 @@
|
||||||
syncCameraUrl(threeCamera);
|
syncCameraUrl(threeCamera);
|
||||||
cameraControls.addEventListener("rest", () => syncCameraUrl(threeCamera));
|
cameraControls.addEventListener("rest", () => syncCameraUrl(threeCamera));
|
||||||
|
|
||||||
|
populateMetaPanel(components).catch(err => {
|
||||||
|
console.warn("Metadata panel unavailable:", err.message);
|
||||||
|
});
|
||||||
|
|
||||||
statusEl.textContent = "";
|
statusEl.textContent = "";
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
statusEl.textContent = `Render error: ${err.message}`;
|
statusEl.textContent = `Render error: ${err.message}`;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue