@barivia/barmesh-mcp 0.5.0 → 0.5.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/dist/shared.js CHANGED
@@ -22,7 +22,7 @@ export const FETCH_TIMEOUT_MS = parseInt(process.env.BARIVIA_FETCH_TIMEOUT_MS ??
22
22
  export const MAX_RETRIES = 2;
23
23
  export const RETRYABLE_STATUS = new Set([502, 503, 504]);
24
24
  /** Single source of truth for the proxy version. Keep in sync with package.json on bump. */
25
- export const CLIENT_VERSION = "0.5.0";
25
+ export const CLIENT_VERSION = "0.5.1";
26
26
  export const PUBLIC_SITE_ORIGIN = "https://barivia.se";
27
27
  /** Large per-cell CSV uploads may exceed the default fetch timeout. */
28
28
  export const UPLOAD_DATASET_TIMEOUT_MS = 180_000;
@@ -62,23 +62,51 @@ function buildHighlights(summary) {
62
62
  out.push("Advisory: SOM distances complement, not replace, numerical uncertainty analysis (Richardson/GCI).");
63
63
  return out;
64
64
  }
65
- export function buildPayload(jobId, data) {
66
- const summary = (data.summary ?? {});
67
- const files = (summary.files ?? []).filter((f) => /\.(png|pdf|svg)$/i.test(f));
68
- // Embedded view: never set `url` (sandboxed iframe cannot load localhost) figures
69
- // lazy-load as base64 through the _barmesh_fetch_figure bridge tool.
70
- const figures = files.map((filename) => {
71
- const base = filename.replace(/\.(png|pdf|svg)$/i, "");
72
- const isRaster = /\.(png|jpe?g|gif|webp)$/i.test(filename);
65
+ /**
66
+ * Group `<base>.<ext>` figure files into one logical figure per base, collecting the
67
+ * available formats. Preview is the inline-displayable raster (PNG, else SVG); the
68
+ * download target is the best quality (SVG > PDF > PNG). Order follows first appearance
69
+ * in `files` (combined first, then diagnostics, then component planes).
70
+ */
71
+ function groupFigures(files) {
72
+ const byBase = new Map();
73
+ for (const f of files) {
74
+ const m = f.match(/^(.*)\.(png|pdf|svg)$/i);
75
+ if (!m)
76
+ continue;
77
+ const base = m[1];
78
+ const ext = m[2].toLowerCase();
79
+ const set = byBase.get(base) ?? new Set();
80
+ set.add(ext);
81
+ byBase.set(base, set);
82
+ }
83
+ return [...byBase.entries()].map(([base, extSet]) => {
84
+ const formats = [...extSet];
85
+ const preview = extSet.has("png") ? `${base}.png` : extSet.has("svg") ? `${base}.svg` : `${base}.${formats[0]}`;
86
+ const downloadFilename = extSet.has("svg") ? `${base}.svg` : extSet.has("pdf") ? `${base}.pdf` : `${base}.png`;
87
+ const vector = formats.filter((e) => e === "pdf" || e === "svg");
88
+ const baseCaption = getCaptionForImage(preview) || "";
89
+ const vectorNote = vector.length > 0
90
+ ? `High-quality ${vector.join("/").toUpperCase()} available — download it (standalone page) or fetch with barmesh_results(action=image, filename="${downloadFilename}").`
91
+ : "";
73
92
  return {
74
93
  key: base,
75
- label: labelForFigure(filename),
76
- filename,
94
+ label: labelForFigure(preview),
95
+ filename: preview,
96
+ downloadFilename,
97
+ formats,
77
98
  kind: figureKind(base),
78
- caption: getCaptionForImage(filename) ||
79
- (!isRaster ? "Vector figure — available for download, not inline-displayable." : undefined),
99
+ caption: [baseCaption, vectorNote].filter(Boolean).join(" ") || undefined,
80
100
  };
81
101
  });
102
+ }
103
+ export function buildPayload(jobId, data) {
104
+ const summary = (data.summary ?? {});
105
+ const files = (summary.files ?? []).filter((f) => /\.(png|pdf|svg)$/i.test(f));
106
+ // Embedded view: never set `url` (sandboxed iframe cannot load localhost) — figures
107
+ // lazy-load as base64 through the _barmesh_fetch_figure bridge tool. One entry per
108
+ // logical figure; PNG previews inline, vector (PDF/SVG) is offered for download.
109
+ const figures = groupFigures(files);
82
110
  const port = getVizPort();
83
111
  return {
84
112
  type: "barmesh-results-explorer",
@@ -128,7 +156,7 @@ async function handleResultsExplorer(job_id) {
128
156
  export function registerResultsExplorerTool(server) {
129
157
  const toolConfig = {
130
158
  title: "Mesh Convergence Results Explorer",
131
- description: "PREFERRED way to browse a completed mesh_convergence job after a first barmesh_results(get) glance. Interactive explorer of the distances, convergence reading, and every figure (combined overview, KL/EMD vs reference and stepwise, volume fingerprints, component planes). Embeds as an MCP App or falls back to a standalone localhost page. barmesh_results(action=get) remains the headless/metrics path.",
159
+ description: "PREFERRED way to browse a completed mesh_convergence job after a first barmesh_results(get) glance. Interactive explorer of the distances, convergence reading, and every figure (combined overview, KL/EMD vs reference and stepwise, volume fingerprints, component planes). Each figure shows its PNG preview inline (PDFs cannot render in the sandboxed panel) and offers the best-quality vector (PDF/SVG) for download when it has been rendered (barmesh_results(action=render, format=pdf)). Embeds as an MCP App or falls back to a standalone localhost page. barmesh_results(action=get) remains the headless/metrics path.",
132
160
  inputSchema: {
133
161
  job_id: z.string().describe("Job ID of a completed cfd_mesh_convergence job"),
134
162
  },