@floless/app 0.35.1 → 0.37.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.
@@ -52856,7 +52856,7 @@ function appVersion() {
52856
52856
  return resolveVersion({
52857
52857
  isSea: isSea2(),
52858
52858
  sqVersionXml: readSqVersionXml(),
52859
- define: true ? "0.35.1" : void 0,
52859
+ define: true ? "0.37.0" : void 0,
52860
52860
  pkgVersion: readPkgVersion()
52861
52861
  });
52862
52862
  }
@@ -52866,7 +52866,7 @@ function resolveChannel(s) {
52866
52866
  return "dev";
52867
52867
  }
52868
52868
  function appChannel() {
52869
- return resolveChannel({ isSea: isSea2(), define: true ? "0.35.1" : void 0 });
52869
+ return resolveChannel({ isSea: isSea2(), define: true ? "0.37.0" : void 0 });
52870
52870
  }
52871
52871
 
52872
52872
  // oauth-presets.ts
@@ -53759,6 +53759,10 @@ function bakeContractIntoApp(sourcePath, contract) {
53759
53759
  // contract-to-scene.ts
53760
53760
  var FT_TO_MM = 304.8;
53761
53761
  var IN_TO_MM = 25.4;
53762
+ function depthPosition(explicit, dzMm, horizMm) {
53763
+ if (explicit === "top" || explicit === "middle" || explicit === "bottom") return explicit;
53764
+ return dzMm > horizMm * 0.5774 ? "middle" : "top";
53765
+ }
53762
53766
  function profileDims(profile) {
53763
53767
  if (!profile) return null;
53764
53768
  const p = profile.trim().toUpperCase();
@@ -53826,8 +53830,10 @@ function contractToScene(contractInput) {
53826
53830
  } else {
53827
53831
  const z0 = m.ends?.[0]?.tos != null ? m.ends[0].tos * IN_TO_MM : defaultTosMm;
53828
53832
  const z1 = m.ends?.[1]?.tos != null ? m.ends[1].tos * IN_TO_MM : defaultTosMm;
53829
- from = [ax, ay, z0];
53830
- to = [bx, by, z1];
53833
+ const pos = depthPosition(m.position, Math.abs(z1 - z0), Math.hypot(bx - ax, by - ay));
53834
+ const zoff = pos === "top" ? -dims.d / 2 : pos === "bottom" ? dims.d / 2 : 0;
53835
+ from = [ax, ay, z0 + zoff];
53836
+ to = [bx, by, z1 + zoff];
53831
53837
  }
53832
53838
  elements.push({ id: m.id, group: profile, kind: "box", from, to, section: { w: dims.w, d: dims.d }, meta: { profile } });
53833
53839
  }
@@ -58653,6 +58659,8 @@ async function startServer() {
58653
58659
  });
58654
58660
  app.post(
58655
58661
  "/api/contract/:appId/score",
58662
+ { bodyLimit: 25 * 1024 * 1024 },
58663
+ // contracts embed rasters (matches /scene + PUT) — the editor/host-AI POSTs the live contract to score
58656
58664
  async (req, reply) => {
58657
58665
  const doc2 = req.body && "contract" in req.body ? req.body.contract : readContract(req.params.appId);
58658
58666
  if (doc2 == null) return reply.status(404).send({ ok: false, error: "no contract to score" });
@@ -58682,6 +58690,8 @@ async function startServer() {
58682
58690
  );
58683
58691
  app.post(
58684
58692
  "/api/contract/:appId/scene",
58693
+ { bodyLimit: 25 * 1024 * 1024 },
58694
+ // contracts embed rasters (matches PUT /api/contract) — the editor POSTs the live contract on every 3D rebuild
58685
58695
  async (req, reply) => {
58686
58696
  const doc2 = req.body && "contract" in req.body ? req.body.contract : readContract(req.params.appId);
58687
58697
  if (doc2 == null) return reply.status(404).send({ ok: false, error: "no contract to render" });
@@ -125,6 +125,7 @@
125
125
  },
126
126
  "angle": { "enum": ["H", "V", "D"] },
127
127
  "role": { "enum": ["beam", "column"], "description": "Defaults to beam when absent." },
128
+ "position": { "enum": ["top", "middle", "bottom"], "description": "Profile placement relative to the reference line (the work-line between the two work-points), like Tekla's depth position. top = line at the section top (steel hangs below); middle = line through the centroid; bottom = line at the section bottom. When absent the editor defaults a flat beam to top (line = top of steel) and a column / steep brace to middle." },
128
129
  "rfi": { "type": "boolean", "description": "True when the size is unresolved (excluded from the BOM)." },
129
130
  "mf": { "type": "boolean", "description": "Moment-frame member (persistent flag)." },
130
131
  "ends": {
@@ -222,6 +222,14 @@ Important optional fields:
222
222
  Key field rules:
223
223
  - All `wp` / `disp` / `a` / `b` coordinates are **display space** (not native PDF coords, not
224
224
  millimetres).
225
+ - **Endpoint direction (canonical order).** `wp[0]`/`ends[0]` is the member's **START** (drawn as a
226
+ **yellow** dot in the editor); `wp[1]`/`ends[1]` is the **END** (drawn **magenta**). Always emit
227
+ endpoints in a consistent direction so start/end read the same across the whole takeoff:
228
+ - **Columns** — bottom → top (start at the lower elevation): `wp`/`col` go `bos` (start) → `tos` (end).
229
+ - **Beams** in plan — **left → right** (start = smaller X); for a vertical-in-plan run, **bottom → up**
230
+ (start = the lower end on the sheet, i.e. the **larger** display-Y since display Y grows downward).
231
+ This is the on-screen reading direction (→ or ↑). It costs nothing to get right at authoring time and
232
+ makes the yellow/magenta end handles meaningful in both the 2D and 3D editor.
225
233
  - Elevations are **decimal inches** (canonical); the editor accepts/displays ft-in strings.
226
234
  `tosDef: false` means the value came from a drawing callout; `true` means it follows the plan
227
235
  default.