+

+
+
+ {direction === 'horizontal' ? 'row' : 'col'} {displayRow}
+
+
+ );
+}
diff --git a/frontend/src/styles.css b/frontend/src/styles.css
index 27d0f67..c49e75b 100644
--- a/frontend/src/styles.css
+++ b/frontend/src/styles.css
@@ -1736,6 +1736,43 @@ html, body, #root {
transform: translate(-50%, -50%) scale(1.2);
}
+/* ── Multi Profile overlay ────────────────────────────────────────── */
+.multiprofile-overlay {
+ position: relative;
+ user-select: none;
+ touch-action: none;
+ overflow: hidden;
+}
+.multiprofile-overlay.cursor-row { cursor: row-resize; }
+.multiprofile-overlay.cursor-col { cursor: col-resize; }
+.multiprofile-image {
+ width: 100%;
+ display: block;
+}
+.multiprofile-line {
+ position: absolute;
+ pointer-events: none;
+}
+.multiprofile-line-horizontal {
+ border-top: 1.5px solid var(--marker);
+ box-shadow: 0 0 4px var(--marker-shadow);
+}
+.multiprofile-line-vertical {
+ border-left: 1.5px solid var(--marker);
+ box-shadow: 0 0 4px var(--marker-shadow);
+}
+.multiprofile-readout {
+ position: absolute;
+ top: 4px;
+ left: 4px;
+ font-size: 10px;
+ background: rgba(0, 0, 0, 0.55);
+ color: #fff;
+ padding: 2px 5px;
+ border-radius: 3px;
+ pointer-events: none;
+}
+
.angle-overlay {
--angle-line-color: #ff9800;
--angle-arc-color: rgb(255, 166, 77);
@@ -1992,7 +2029,8 @@ html, body, #root {
.is-panning .mask-paint-overlay,
.is-panning .markup-overlay,
.is-panning .radial-overlay,
-.is-panning .straighten-overlay {
+.is-panning .straighten-overlay,
+.is-panning .multiprofile-overlay {
pointer-events: none;
}
diff --git a/frontend/src/types.ts b/frontend/src/types.ts
index d14e3af..a6bed64 100644
--- a/frontend/src/types.ts
+++ b/frontend/src/types.ts
@@ -79,6 +79,9 @@ export interface OverlayData {
thickness?: number;
xres?: number;
yres?: number;
+ row?: number;
+ direction?: 'horizontal' | 'vertical';
+ max_index?: number;
section_title?: string;
line?: number[];
shape?: string;
diff --git a/frontend/src/workflowCapture.ts b/frontend/src/workflowCapture.ts
index 5c40bd0..9eeaafa 100644
--- a/frontend/src/workflowCapture.ts
+++ b/frontend/src/workflowCapture.ts
@@ -13,6 +13,7 @@ export const OVERLAY_CAPTURE_SELECTORS = [
'.angle-overlay', // AngleMeasureOverlay
'.radial-overlay', // RadialProfileOverlay
'.straighten-overlay', // StraightenPathOverlay
+ '.multiprofile-overlay', // MultiProfileOverlay
];
function encodeBase64(bytes: Uint8Array) {
diff --git a/tests/node_tests/multi_profile.py b/tests/node_tests/multi_profile.py
index b56e916..52b1cff 100644
--- a/tests/node_tests/multi_profile.py
+++ b/tests/node_tests/multi_profile.py
@@ -31,3 +31,41 @@ def test_vertical_direction():
field = make_field(shape=(80, 40))
(profile,) = node.process(field, field, row=-1, direction="vertical", mode="overlay")
assert len(profile.data) == 80, f"Vertical profile length should be field height (80), got {len(profile.data)}"
+
+
+def test_emits_blended_overlay():
+ from backend.execution_context import active_node, execution_callbacks
+ from backend.nodes.multi_profile import MultipleProfiles
+
+ node = MultipleProfiles()
+ field = make_field(shape=(64, 128))
+
+ overlays = []
+ with execution_callbacks(overlay=lambda nid, d: overlays.append(d)), active_node("test"):
+ node.process(field, field, row=10, direction="horizontal", mode="overlay")
+
+ assert len(overlays) == 1
+ ov = overlays[0]
+ assert ov["kind"] == "multi_profile"
+ assert ov["section_title"] == "Preview"
+ assert ov["image"].startswith("data:image/png;base64,")
+ assert ov["row"] == 10
+ assert ov["direction"] == "horizontal"
+ assert ov["max_index"] == 63 # height - 1
+
+
+def test_overlay_max_index_for_vertical():
+ from backend.execution_context import active_node, execution_callbacks
+ from backend.nodes.multi_profile import MultipleProfiles
+
+ node = MultipleProfiles()
+ field = make_field(shape=(80, 40))
+
+ overlays = []
+ with execution_callbacks(overlay=lambda nid, d: overlays.append(d)), active_node("test"):
+ node.process(field, field, row=-1, direction="vertical", mode="overlay")
+
+ ov = overlays[0]
+ assert ov["direction"] == "vertical"
+ assert ov["max_index"] == 39 # width - 1
+ assert ov["row"] == 20 # center column for 40 wide