opentakeoff-mcp 0.9.46 → 0.9.47

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.
Files changed (2) hide show
  1. package/dist/server-core.js +35 -16
  2. package/package.json +1 -1
@@ -2678,11 +2678,13 @@ function clusterRows(spans) {
2678
2678
  }
2679
2679
  var rowY = (r) => r.reduce((s, t) => s + t.y, 0) / r.length;
2680
2680
  var SURFACE_WORDS = /* @__PURE__ */ new Set(["FLOOR", "BASE", "WALL", "WALLS", "CEILING", "NORTH", "SOUTH", "EAST", "WEST", "WAINSCOT"]);
2681
- var ROOM_HEADERS = ["ROOM", "NO", "NUMBER", "NAME", "MARK", "LOCATION", "FLOOR", "BASE", "WALL", "WALLS", "NORTH", "SOUTH", "EAST", "WEST", "CEILING", "WAINSCOT", "REMARKS", "CLG", "HT", "HEIGHT", "FINISH", "BLDG", "BUILDING"];
2681
+ var ROOM_HEADERS = ["ROOM", "NO", "NUMBER", "NAME", "MARK", "LOCATION", "FLOOR", "BASE", "WALL", "WALLS", "NORTH", "SOUTH", "EAST", "WEST", "CEILING", "WAINSCOT", "REMARKS", "CLG", "HT", "HEIGHT", "FINISH", "CASEWORK", "CABINET", "COUNTER", "COUNTERTOP", "BLDG", "BUILDING"];
2682
2682
  var FINISH_HEADERS = ["CODE", "MARK", "SYMBOL", "MATERIAL", "MANUFACTURER", "PRODUCT", "STYLE", "COLOR", "SIZE", "REMARKS", "DESCRIPTION", "PATTERN", "COMMENTS"];
2683
- var headerLabel = (s, vocab) => {
2684
- for (const w of norm(s).split(/[^A-Z]+/)) if (w && vocab.includes(w)) return w;
2685
- return null;
2683
+ var headerLabel = (s, vocab) => headerLabels(s, vocab)[0] ?? null;
2684
+ var headerLabels = (s, vocab) => {
2685
+ const out = [];
2686
+ for (const w of norm(s).split(/[^A-Z]+/)) if (w && vocab.includes(w) && !out.includes(w)) out.push(w);
2687
+ return out;
2686
2688
  };
2687
2689
  function headerHits(row, vocab) {
2688
2690
  const out = [];
@@ -2728,6 +2730,10 @@ function findHeaderRow(rows, vocab, required, minHits) {
2728
2730
  const parent = parentLabelOver(rows, idx, i, h.span.x, hi, vocab);
2729
2731
  if (parent && parent !== h.label) label = `${parent} ${h.label}`;
2730
2732
  }
2733
+ if (used.has(label)) {
2734
+ const alt = headerLabels(h.span.str, vocab).find((w) => !used.has(w));
2735
+ if (alt) label = alt;
2736
+ }
2731
2737
  if (used.has(label)) continue;
2732
2738
  used.add(label);
2733
2739
  anchors.push({ label, x: h.span.x + (h.span.w || 0) / 2 });
@@ -2877,14 +2883,15 @@ function rowKeyOf(raw, kind, buildings) {
2877
2883
  }
2878
2884
  var numOf = (key) => key.match(QUALIFIED_KEY_RE)?.[2] ?? key;
2879
2885
  var centerX = (t) => t.x + (t.w || 0) / 2;
2880
- function columnStarts(rows, anchors, cfg, x0, x1) {
2886
+ function columnMapFor(rows, anchors, cfg, x0, x1, coord) {
2887
+ const at = (t) => coord === "left" ? t.x : t.x + (t.w || 0) / 2;
2881
2888
  const xs = [];
2882
2889
  const hs = [];
2883
2890
  for (let i = Math.max(cfg.fromIdx, 0); i < rows.length; i++) {
2884
2891
  if (rowY(rows[i]) <= cfg.belowY) continue;
2885
2892
  for (const t of rows[i]) {
2886
2893
  if (t.x < x0 || t.x > x1 || revisionOf(t.str) != null) continue;
2887
- xs.push(t.x);
2894
+ xs.push(at(t));
2888
2895
  hs.push(t.h || 8);
2889
2896
  }
2890
2897
  }
@@ -2912,23 +2919,35 @@ function columnStarts(rows, anchors, cfg, x0, x1) {
2912
2919
  if (cur == null || c.start < cur) byLabel.set(own.label, c.start);
2913
2920
  }
2914
2921
  if (byLabel.size !== anchors.length) return null;
2915
- const named = [...byLabel.entries()].map(([label, start]) => ({ label, start })).sort((a, b) => a.start - b.start);
2916
- const order = anchors.map((a) => a.label).join("|");
2917
- if (named.map((n) => n.label).join("|") !== order) return null;
2918
- return named;
2922
+ const cols = [...byLabel.entries()].map(([label, start]) => ({ label, start })).sort((a, b) => a.start - b.start);
2923
+ if (cols.map((c) => c.label).join("|") !== anchors.map((a) => a.label).join("|")) return null;
2924
+ const starts = kept.map((c) => c.start);
2925
+ let on = 0;
2926
+ for (const x of xs) if (starts.some((st) => Math.abs(x - st) <= tol)) on++;
2927
+ return { coord, cols, score: on / xs.length };
2928
+ }
2929
+ function columnStarts(rows, anchors, cfg, x0, x1) {
2930
+ const FIT_FLOOR = 0.7;
2931
+ const fits = (m) => m && m.score >= FIT_FLOOR ? m : null;
2932
+ const left = fits(columnMapFor(rows, anchors, cfg, x0, x1, "left"));
2933
+ const center = fits(columnMapFor(rows, anchors, cfg, x0, x1, "center"));
2934
+ if (!left) return center;
2935
+ if (!center) return left;
2936
+ return center.score > left.score + 0.05 ? center : left;
2919
2937
  }
2920
2938
  function bandDataRows(rows, anchors, kind, sheetKey, buildings, cfg) {
2921
2939
  const { x0, x1, medGap } = bandLimits(anchors);
2922
2940
  const cols = columnStarts(rows, anchors, cfg, x0, x1);
2923
- const keyTol = cols && cols.length > 1 ? Math.max(8, (cols[1].start - cols[0].start) * 0.5) : 40;
2941
+ const keyTol = cols && cols.cols.length > 1 ? Math.max(8, (cols.cols[1].start - cols.cols[0].start) * 0.5) : 40;
2924
2942
  const out = [];
2925
2943
  const outY = [];
2926
2944
  let region = null;
2927
2945
  const columnOf = (t) => {
2928
2946
  if (!cols) return nearestAnchor(centerX(t), anchors);
2929
- let label = cols[0].label;
2930
- for (const c of cols) {
2931
- if (t.x + 1 >= c.start) label = c.label;
2947
+ const at = cols.coord === "left" ? t.x : centerX(t);
2948
+ let label = cols.cols[0].label;
2949
+ for (const c of cols.cols) {
2950
+ if (at + 1 >= c.start) label = c.label;
2932
2951
  else break;
2933
2952
  }
2934
2953
  return label;
@@ -2962,7 +2981,7 @@ function bandDataRows(rows, anchors, kind, sheetKey, buildings, cfg) {
2962
2981
  orphans.push({ toks: banded, y: rowY(rows[i]) });
2963
2982
  continue;
2964
2983
  }
2965
- if (cols && Math.abs(banded[0].x - cols[0].start) > keyTol) continue;
2984
+ if (cols && Math.abs((cols.coord === "left" ? banded[0].x : centerX(banded[0])) - cols.cols[0].start) > keyTol) continue;
2966
2985
  if (cfg.keyAlign && Math.abs(centerX(banded[0]) - cfg.keyAlign.x) > cfg.keyAlign.tol) continue;
2967
2986
  const row = { key: keyed.key, sheet: sheetKey, cells: {} };
2968
2987
  if (keyed.building) row.building = keyed.building;
@@ -11100,7 +11119,7 @@ function applyStagedTools(server, registered) {
11100
11119
  // package.json
11101
11120
  var package_default = {
11102
11121
  name: "opentakeoff-mcp",
11103
- version: "0.9.46",
11122
+ version: "0.9.47",
11104
11123
  mcpName: "io.github.Kentucky-ai/opentakeoff",
11105
11124
  type: "module",
11106
11125
  description: "OpenTakeoff MCP server \u2014 drive the takeoff engine from your MCP client over stdio.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opentakeoff-mcp",
3
- "version": "0.9.46",
3
+ "version": "0.9.47",
4
4
  "mcpName": "io.github.Kentucky-ai/opentakeoff",
5
5
  "type": "module",
6
6
  "description": "OpenTakeoff MCP server — drive the takeoff engine from your MCP client over stdio.",