partforge 0.56.0 → 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -249,11 +249,19 @@ export function attachAnimationControls(viewer, part, { container, applyValues,
|
|
|
249
249
|
scrubWrap.addEventListener("pointerleave", onWrapPointerLeave);
|
|
250
250
|
scrubWrap.addEventListener("pointercancel", onWrapPointerLeave);
|
|
251
251
|
|
|
252
|
+
// Late-bound hook into the placement section below (a plain call would hit
|
|
253
|
+
// the TDZ on its `let placementRaf` — setup invokes syncStructure before the
|
|
254
|
+
// placement block runs). Wired to schedulePlacement once that block exists,
|
|
255
|
+
// so a view switch that shows/hides the bar re-publishes --pf-anim-clear
|
|
256
|
+
// even where ResizeObserver is absent.
|
|
257
|
+
let onStructureChanged = null;
|
|
258
|
+
|
|
252
259
|
// Per-view + per-animation chrome: which chooser shows, the picker's options,
|
|
253
260
|
// title, ⓘ description, pager labels, scrubber ticks. A view with no
|
|
254
261
|
// animations hides the whole bar rather than showing an empty transport.
|
|
255
262
|
function syncStructure() {
|
|
256
263
|
bar.style.display = current ? "" : "none";
|
|
264
|
+
onStructureChanged?.();
|
|
257
265
|
hideChapterBubble();
|
|
258
266
|
if (!current) return;
|
|
259
267
|
const paged = animations.length > 1;
|
|
@@ -536,10 +544,20 @@ export function attachAnimationControls(viewer, part, { container, applyValues,
|
|
|
536
544
|
bar.style.maxWidth = "";
|
|
537
545
|
bar.style.overflow = "";
|
|
538
546
|
bar.classList.remove("pf-squeezed");
|
|
539
|
-
const
|
|
547
|
+
const stageRect = container.getBoundingClientRect();
|
|
540
548
|
const barRect = bar.getBoundingClientRect();
|
|
549
|
+
// Publish the bar's vertical claim on the stage as --pf-anim-clear: the
|
|
550
|
+
// distance from the stage's bottom edge to the bar's top, 0px when the bar
|
|
551
|
+
// is hidden (a view with no animations). Hosts that float their own chrome
|
|
552
|
+
// at the stage's bottom-centre (partforge-cloud's status/forging stack)
|
|
553
|
+
// read it to sit above the bar instead of under it; with no bar mounted
|
|
554
|
+
// the property is never set and a var() fallback of 0px applies.
|
|
555
|
+
const clear = bar.style.display === "none"
|
|
556
|
+
? 0
|
|
557
|
+
: Math.max(0, Math.round(stageRect.bottom - barRect.top));
|
|
558
|
+
container.style.setProperty("--pf-anim-clear", `${clear}px`);
|
|
559
|
+
const vb = viewbarEl?.getBoundingClientRect();
|
|
541
560
|
if (!vb || barRect.top >= vb.bottom || barRect.bottom <= vb.top) return;
|
|
542
|
-
const stageRect = container.getBoundingClientRect();
|
|
543
561
|
const plan = planAnimBarPlacement({
|
|
544
562
|
stageWidth: stageRect.width,
|
|
545
563
|
barWidth: barRect.width,
|
|
@@ -581,6 +599,7 @@ export function attachAnimationControls(viewer, part, { container, applyValues,
|
|
|
581
599
|
placementObserver.observe(bar);
|
|
582
600
|
if (viewbarEl) placementObserver.observe(viewbarEl);
|
|
583
601
|
}
|
|
602
|
+
onStructureChanged = schedulePlacement; // see the hook's declaration above
|
|
584
603
|
schedulePlacement();
|
|
585
604
|
|
|
586
605
|
const runtime = {
|
|
@@ -662,6 +681,7 @@ export function attachAnimationControls(viewer, part, { container, applyValues,
|
|
|
662
681
|
scrubWrap.removeEventListener("pointercancel", onWrapPointerLeave);
|
|
663
682
|
hideChapterBubble(); // also clears hoverInside
|
|
664
683
|
chapterBubble.remove(); // a stage child, so the bar taking itself out misses it
|
|
684
|
+
container.style.removeProperty("--pf-anim-clear"); // no bar, no claim
|
|
665
685
|
bar.remove();
|
|
666
686
|
},
|
|
667
687
|
__viewer: viewer, // test hook only
|
package/src/framework/chrome.css
CHANGED
|
@@ -234,7 +234,13 @@
|
|
|
234
234
|
#viewbar's, so a host that re-anchors this bar still inherits its chrome.
|
|
235
235
|
animation-controls.js may inline-override left/transform/max-width (and
|
|
236
236
|
overflow while width-capped) to hold a 10px gap to #viewbar, and clears
|
|
237
|
-
the overrides whenever centered placement fits.
|
|
237
|
+
the overrides whenever centered placement fits.
|
|
238
|
+
|
|
239
|
+
animation-controls.js also publishes --pf-anim-clear on the STAGE element:
|
|
240
|
+
the px distance from the stage's bottom edge to the visible bar's top (0px
|
|
241
|
+
while the active view has no animations; unset when no view declares any).
|
|
242
|
+
A host floating its own chrome at the stage's bottom-centre should anchor it
|
|
243
|
+
at calc(var(--pf-anim-clear, 0px) + <gap>) to stack above the bar. */
|
|
238
244
|
.pf-anim-bar {
|
|
239
245
|
position: absolute; left: 50%; bottom: 14px; transform: translateX(-50%);
|
|
240
246
|
z-index: 15; max-width: calc(100% - 24px);
|
package/src/framework/mount.js
CHANGED
|
@@ -453,8 +453,10 @@ export function mount(part, { createWorker, elements = {}, onBuild, onPick, onDo
|
|
|
453
453
|
if (needed.every(isCurrent)) {
|
|
454
454
|
showView(needed);
|
|
455
455
|
ui.setExportEnabled(true);
|
|
456
|
-
|
|
457
|
-
|
|
456
|
+
// The line used to read "N triangles" here; that debug readout now goes
|
|
457
|
+
// to the console (see the `meshes` case). Clearing keeps a stale
|
|
458
|
+
// "phase…" from outliving the build it described.
|
|
459
|
+
ui.setStatus("");
|
|
458
460
|
} else if (needed.every((n) => viewer.hasSubMesh(n))) {
|
|
459
461
|
showView(needed); // stale but present — keep it visible during regenerate
|
|
460
462
|
ui.setExportEnabled(false);
|
|
@@ -498,7 +500,8 @@ export function mount(part, { createWorker, elements = {}, onBuild, onPick, onDo
|
|
|
498
500
|
ui.hideBusy();
|
|
499
501
|
refreshView();
|
|
500
502
|
if (data.ms && missingParts().length === 0) {
|
|
501
|
-
|
|
503
|
+
const tris = viewSubParts(part, view(), params).reduce((s, n) => s + viewer.subTriangles(n), 0);
|
|
504
|
+
console.debug(`partforge: built ${tris.toLocaleString()} triangles in ${(data.ms / 1000).toFixed(1)} s`);
|
|
502
505
|
}
|
|
503
506
|
dbg?.update({ ms: data.ms, hits: data.cache?.hits ?? 0, misses: data.cache?.misses ?? 0, skipped: lastGen.skipped, rebuilt: lastGen.rebuilt, posed: lastGen.posed });
|
|
504
507
|
onBuild?.({ status: "success", ms: data.ms });
|
|
@@ -545,8 +548,10 @@ export function mount(part, { createWorker, elements = {}, onBuild, onPick, onDo
|
|
|
545
548
|
case "error":
|
|
546
549
|
loop.buildDone();
|
|
547
550
|
ui.hideBusy();
|
|
548
|
-
|
|
551
|
+
// refreshView FIRST: its all-current branch clears the status line,
|
|
552
|
+
// so writing the failure after it keeps the message on screen.
|
|
549
553
|
refreshView();
|
|
554
|
+
ui.setStatus(`failed: ${data.message}`, true);
|
|
550
555
|
onBuild?.({ status: "error", error: data.message });
|
|
551
556
|
if (!readySettled) { readySettled = true; rejectReady(new Error(data.message)); }
|
|
552
557
|
break;
|
|
@@ -10,6 +10,5 @@ export function createStatusUi({ status, busy, phase, exports = [] }) {
|
|
|
10
10
|
showBusy(p) { phase.textContent = `${p}…`; busy.classList.add("show"); },
|
|
11
11
|
hideBusy() { busy.classList.remove("show"); },
|
|
12
12
|
setExportEnabled(on) { exportBtns.forEach((b) => { b.disabled = !on; }); },
|
|
13
|
-
statusText: () => status.textContent,
|
|
14
13
|
};
|
|
15
14
|
}
|