partforge 0.138.0 → 0.138.1

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "partforge",
3
- "version": "0.138.0",
3
+ "version": "0.138.1",
4
4
  "description": "Turn a declarative part definition into a parametric-CAD web app (three.js + Manifold/Replicad). Requires a Vite-based consumer.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -4,20 +4,42 @@
4
4
  // without dragging in the part-model/jobs layer (purity).
5
5
  import { probeSubPartPose } from "./pose-probe-core.js";
6
6
  import { viewSubParts, resolveParams } from "./part-model.js";
7
+ import { fontsFor } from "./fonts.js";
8
+ import { imagesFor } from "./images.js";
9
+ import { vectorsFor } from "./vectors.js";
10
+ import { h, byteAwareReplacer } from "./geometry/solid-hash.js";
11
+
12
+ // The sources behind every NAMED asset a build can reach. A build says
13
+ // `k.text2d(s, { font: "face" })`, `k.heightfield("img")` or `k.vector2d("art")`,
14
+ // and the probe sees only the name — which stays the same whichever font,
15
+ // image or artwork a control has picked. Without this, a font pick hashed
16
+ // identically before and after, so the fast path re-posed the OLD letters
17
+ // instead of rebuilding them (the Lattice Box's raised text kept its previous
18
+ // typeface while the pocket cut from the same text updated, 2026-10-02). Folded
19
+ // into every trusted sub-part's baseHash: coarser than tracking which sub-part
20
+ // names which asset, and the cost of that is only a rebuild on an asset change.
21
+ // Serialized with byteAwareReplacer, not h(): h() reads an ArrayBuffer as {},
22
+ // so two byte-valued fonts would hash alike — the same bug one level down.
23
+ function assetSources(part, p) {
24
+ return [fontsFor(part, p) ?? null, imagesFor(part, p) ?? null, vectorsFor(part, p) ?? null];
25
+ }
7
26
 
8
27
  // Probe every subpart the view shows. Never throws; a failing/queried/weird
9
28
  // subpart yields { trusted: false } and the others still probe.
10
29
  export function probePoses(part, view, params) {
11
30
  const out = new Map();
12
- let resolved;
13
- try { resolved = resolveParams(part, params); }
14
- catch {
31
+ let resolved, assets;
32
+ try {
33
+ resolved = resolveParams(part, params);
34
+ assets = h("assets", JSON.stringify(assetSources(part, resolved.p), byteAwareReplacer));
35
+ } catch {
15
36
  for (const name of viewSubParts(part, view, params)) out.set(name, { trusted: false });
16
37
  return out;
17
38
  }
18
39
  const { p, d } = resolved;
19
40
  for (const name of viewSubParts(part, view, params)) {
20
- out.set(name, probeSubPartPose(part.parts[name], { view, purpose: "display", p, d }));
41
+ const entry = probeSubPartPose(part.parts[name], { view, purpose: "display", p, d });
42
+ out.set(name, entry.trusted ? { ...entry, baseHash: h("with-assets", entry.baseHash, assets) } : entry);
21
43
  }
22
44
  return out;
23
45
  }