@willcgage/module-schematic 0.67.0 → 0.69.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/dist/index.d.cts CHANGED
@@ -673,7 +673,21 @@ declare function checkEndplateWidth(input: {
673
673
  config?: TrackConfig | "none" | null;
674
674
  trackOffsetInches?: number | null;
675
675
  }): EndplateWidthIssue[];
676
- /** Whether a doc is a single-endplate turnback (explicit flag or one endplate). */
676
+ /**
677
+ * Whether a doc is a balloon loop — **the authored flag, and only that**.
678
+ *
679
+ * ⚠️ This used to ALSO infer a loop from `endplates.length === 1`, which was
680
+ * safe only while the sole way to have one endplate was to be a turnback. #184
681
+ * ended that: an *end of the line* or a *pocket* presents one conforming face
682
+ * too, so every single-ended module was silently classified as a loop — drawn
683
+ * with a bulb, its endplate A relabelled "Entry", and positions past the throat
684
+ * read as being inside a balloon that doesn't exist (#191).
685
+ *
686
+ * A loop is now only ever a loop because someone said so: the Loop checkbox and
687
+ * the return-loop generator both set `loop: true`. Checked before removing the
688
+ * inference — no stored doc relied on it (no module in the catalogue has one
689
+ * endplate without the flag, and none is `category:"loop"` or `dead_end`).
690
+ */
677
691
  declare function isLoopDoc(doc: ModuleSchematicDoc): boolean;
678
692
  declare const MAIN_TRACK_ID = "main";
679
693
  /** The second main on double-track modules — a real track entity so turnouts
@@ -1091,6 +1105,12 @@ interface ModuleFeatures {
1091
1105
  atFrac: number;
1092
1106
  doubleSide: "west" | "east";
1093
1107
  } | null;
1108
+ /** Whether the module presents a far endplate at all. False for an *end of
1109
+ * the line* or a *pocket*, which offer one conforming face and simply stop
1110
+ * (#184) — a renderer must not label that end, or it announces a plate the
1111
+ * module hasn't got and invites something to be coupled to it (#191). A loop
1112
+ * is separate: see {@link loop} and {@link loopInterchange}. */
1113
+ hasEndplateB: boolean;
1094
1114
  /** Lane extents across every feature (mains included; negative = outside
1095
1115
  * Main 1). Renderers size their vertical space from these. */
1096
1116
  laneMin: number;
package/dist/index.d.ts CHANGED
@@ -673,7 +673,21 @@ declare function checkEndplateWidth(input: {
673
673
  config?: TrackConfig | "none" | null;
674
674
  trackOffsetInches?: number | null;
675
675
  }): EndplateWidthIssue[];
676
- /** Whether a doc is a single-endplate turnback (explicit flag or one endplate). */
676
+ /**
677
+ * Whether a doc is a balloon loop — **the authored flag, and only that**.
678
+ *
679
+ * ⚠️ This used to ALSO infer a loop from `endplates.length === 1`, which was
680
+ * safe only while the sole way to have one endplate was to be a turnback. #184
681
+ * ended that: an *end of the line* or a *pocket* presents one conforming face
682
+ * too, so every single-ended module was silently classified as a loop — drawn
683
+ * with a bulb, its endplate A relabelled "Entry", and positions past the throat
684
+ * read as being inside a balloon that doesn't exist (#191).
685
+ *
686
+ * A loop is now only ever a loop because someone said so: the Loop checkbox and
687
+ * the return-loop generator both set `loop: true`. Checked before removing the
688
+ * inference — no stored doc relied on it (no module in the catalogue has one
689
+ * endplate without the flag, and none is `category:"loop"` or `dead_end`).
690
+ */
677
691
  declare function isLoopDoc(doc: ModuleSchematicDoc): boolean;
678
692
  declare const MAIN_TRACK_ID = "main";
679
693
  /** The second main on double-track modules — a real track entity so turnouts
@@ -1091,6 +1105,12 @@ interface ModuleFeatures {
1091
1105
  atFrac: number;
1092
1106
  doubleSide: "west" | "east";
1093
1107
  } | null;
1108
+ /** Whether the module presents a far endplate at all. False for an *end of
1109
+ * the line* or a *pocket*, which offer one conforming face and simply stop
1110
+ * (#184) — a renderer must not label that end, or it announces a plate the
1111
+ * module hasn't got and invites something to be coupled to it (#191). A loop
1112
+ * is separate: see {@link loop} and {@link loopInterchange}. */
1113
+ hasEndplateB: boolean;
1094
1114
  /** Lane extents across every feature (mains included; negative = outside
1095
1115
  * Main 1). Renderers size their vertical space from these. */
1096
1116
  laneMin: number;
package/dist/index.js CHANGED
@@ -532,7 +532,7 @@ function endplateWidthFor(widths, id) {
532
532
  return typeof w === "number" && w > 0 ? w : FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
533
533
  }
534
534
  function isLoopDoc(doc) {
535
- return doc.loop === true || doc.endplates.length === 1;
535
+ return doc.loop === true;
536
536
  }
537
537
  var MAIN_TRACK_ID = "main";
538
538
  var MAIN2_TRACK_ID = "main2";
@@ -929,8 +929,14 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
929
929
  loopReturn: loop && d.loopReturn === "main2" ? "main2" : "same",
930
930
  mainsSwapped: d.mainsSwapped === true,
931
931
  configA: configOf("A"),
932
- // On a loop, a missing B means pure turnback; present = interchange loop.
933
- configB: loop && !hasB ? "none" : configOf("B"),
932
+ // NO ENDPLATE B ⇒ "none", whatever kind of module this is. On a loop that
933
+ // reads as a pure turnback (a present B makes it an interchange); on an end
934
+ // of the line or a pocket it simply means the module has one face (#184).
935
+ // ⚠️ This used to be `loop && !hasB`, so "none" only survived a round trip on
936
+ // a loop — which passed its test yesterday ONLY because a single-endplate
937
+ // module was still being misclassified as one (#191). The absence of the
938
+ // plate is the fact; why it's absent doesn't change what to read.
939
+ configB: hasB ? configOf("B") : "none",
934
940
  branches: branchEps.map((ep) => ({
935
941
  label: ep.label ?? "Branch",
936
942
  pos: sc(ep.at.pos),
@@ -1862,6 +1868,7 @@ function moduleFeatures(doc) {
1862
1868
  crossings,
1863
1869
  crossovers,
1864
1870
  branchConnectors,
1871
+ hasEndplateB: doc.endplates.some((e) => e.id === "B"),
1865
1872
  industries,
1866
1873
  laneMin: Math.min(...allLanes),
1867
1874
  laneMax: Math.max(...allLanes)