diff --git a/.changeset/drawer-graphs-before-lists.md b/.changeset/drawer-graphs-before-lists.md new file mode 100644 index 00000000000..90b0f25ae52 --- /dev/null +++ b/.changeset/drawer-graphs-before-lists.md @@ -0,0 +1,5 @@ +--- +"@hashintel/petrinaut": patch +--- + +Reorganize the experiment and optimization result drawers: the summary, the navigator, and the surface hold still at the top, the surface moves above the step/metric lists, and only those lists scroll — the optimization step table keeps its header pinned while scrolling. diff --git a/libs/@hashintel/petrinaut/docs/experiments.md b/libs/@hashintel/petrinaut/docs/experiments.md index 6ae9147380d..9d3616dc8bd 100644 --- a/libs/@hashintel/petrinaut/docs/experiments.md +++ b/libs/@hashintel/petrinaut/docs/experiments.md @@ -68,7 +68,9 @@ Every selection uses the same seed sequence (common random numbers), and a run's #### The surface view -A sweep with two or more swept parameters grows a **Surface** section under the metrics: a contour plot of one metric's final value over two parameters you pick, with every other parameter held at the middle of its selected range. The plot fills in live — points are sampled a few runs at a time (8 runs each), coarse shape first — and **clicking the surface moves the navigator**: both shown parameters collapse to a point at the clicked position, which then refines with more runs. Changing the fixed parameters, the axes, or the metric restarts the fill for the new slice. +A sweep with two or more swept parameters grows a **Surface** section between the parameter strip and the metric charts: a contour plot of one metric's final value over two parameters you pick, with every other parameter held at the middle of its selected range. The plot fills in live — points are sampled a few runs at a time (8 runs each), coarse shape first — and **clicking the surface moves the navigator**: both shown parameters collapse to a point at the clicked position, which then refines with more runs. Changing the fixed parameters, the axes, or the metric restarts the fill for the new slice. + +The summary, the parameter strip, and the surface hold still at the top of the drawer; the metric charts scroll on their own below them, so the graphs stay in view while you browse the charts. ### Compute backend (experimental) diff --git a/libs/@hashintel/petrinaut/docs/optimization.md b/libs/@hashintel/petrinaut/docs/optimization.md index 628c06ad7a2..fdb70b2a50a 100644 --- a/libs/@hashintel/petrinaut/docs/optimization.md +++ b/libs/@hashintel/petrinaut/docs/optimization.md @@ -70,8 +70,10 @@ steps arrive and shows: - The current best metric value. - The best flat scenario-parameter assignment. - The latest received steps, including their parameters, objective values, and - colored state indicators. For long runs, the drawer displays the newest 200 - steps while retaining aggregate progress and the current best result. + colored state indicators. The step list sits at the bottom of the drawer and + scrolls on its own — the summary, the best parameters, and the surface above + it hold still. For long runs, the drawer displays the newest 200 steps while + retaining aggregate progress and the current best result. Closing the drawer does not stop the optimization. Use **Cancel** to abort an active run. Completed, cancelled, and failed records can be removed from their @@ -85,8 +87,8 @@ kept, and a **Retry** action starts a fresh run with the same settings. ## The surface view A study with two or more optimized numeric parameters grows a **Surface** -section at the bottom of its drawer: an Optuna-style contour of the objective -over two parameters you pick. The study's own trials appear as rings (the best +section between the best parameters and the step list: an Optuna-style contour +of the objective over two parameters you pick. The study's own trials appear as rings (the best trial highlighted), and the filled contour comes from points **computed locally on your machine** — the study's model snapshot runs on a background worker, a few runs per point, and the plot fills in coarse shape first. diff --git a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/experiments/sweep-surface.tsx b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/experiments/sweep-surface.tsx index 9194164e9a1..fada344d23b 100644 --- a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/experiments/sweep-surface.tsx +++ b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/experiments/sweep-surface.tsx @@ -62,6 +62,9 @@ const controlsStyle = css({ flexWrap: "wrap", // Compact inline controls; the ds Select otherwise stretches to the row. "& [data-scope='select']": { width: "[170px]" }, + // The Select's root insists on min-content width, which overflows the + // 170px box over the next label; a long option name fits by ellipsis. + "& > div > div": { minWidth: "[0]" }, }); const controlLabelStyle = css({ diff --git a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/experiments/view-experiment-drawer.tsx b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/experiments/view-experiment-drawer.tsx index 26e9a59c965..018399be43f 100644 --- a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/experiments/view-experiment-drawer.tsx +++ b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/experiments/view-experiment-drawer.tsx @@ -92,6 +92,25 @@ const errorStyle = css({ whiteSpace: "pre-wrap", }); +// The drawer body is a column: the summary, the navigator, and the surface +// hold still at the top, and the metric charts alone scroll below them. +const drawerBodyStyle = css({ + paddingTop: "[0]", + display: "flex", + flexDirection: "column", +}); + +const fixedSectionStyle = css({ + flexShrink: "0", +}); + +const metricsScrollStyle = css({ + flex: "[1]", + minHeight: "[160px]", + overflowY: "auto", + scrollbarWidth: "[thin]", +}); + const metricGridStyle = css({ display: "grid", gridTemplateColumns: "repeat(2, minmax(0, 1fr))", @@ -365,12 +384,13 @@ export const ViewExperimentDrawer = ({ title={experiment.name} description="Monte Carlo experiment metrics" /> - +
( @@ -383,8 +403,9 @@ export const ViewExperimentDrawer = ({
experiment.sweep ? ( ) : null} - {experiment.metricFrames.length > 0 ? ( -
- -
- ) : null} {experiment.sweep && experiment.parameterAxes.length >= 2 ? (
) : null} + {experiment.metricFrames.length > 0 ? ( +
+
+ +
+
+ ) : null} div > div": { minWidth: "[0]" }, }); const controlLabelStyle = css({ diff --git a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/optimizations/view-optimization-drawer.tsx b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/optimizations/view-optimization-drawer.tsx index 20a354a32ae..1f681f3e994 100644 --- a/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/optimizations/view-optimization-drawer.tsx +++ b/libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/optimizations/view-optimization-drawer.tsx @@ -69,6 +69,37 @@ const stepHintStyle = css({ color: "neutral.s80", }); +// The drawer body is a column: the summary and surface hold still at the +// top, and the steps list alone scrolls in the space that remains. +const drawerBodyStyle = css({ + paddingTop: "[0]", + display: "flex", + flexDirection: "column", +}); + +const fixedSectionStyle = css({ + flexShrink: "0", +}); + +const stepsScrollStyle = css({ + flex: "[1]", + minHeight: "[160px]", + overflowY: "auto", + scrollbarWidth: "[thin]", + borderWidth: "[1px]", + borderStyle: "solid", + borderColor: "neutral.bd.subtle", + borderRadius: "md", + // Pin the table's header while the steps scroll beneath it. The sticky + // element must be the header's rowgroup: a sticky row could only move + // within that rowgroup, which is exactly as tall as the row itself. + "& [role='table'] > [role='rowgroup']:first-child": { + position: "sticky", + top: "[0]", + zIndex: "[1]", + }, +}); + const stepStateStyle = css({ display: "inline-flex", alignItems: "center", @@ -319,13 +350,23 @@ export const ViewOptimizationDrawer = ({ title={optimization.input.name} description="Optimization progress and results" /> - + -
+
{optimization.best ? ( -
+
{Object.entries(optimization.best.parameters).map( ([identifier, value]) => ( @@ -342,32 +383,35 @@ export const ViewOptimizationDrawer = ({
) : null} - {optimization.trials.length > 0 ? ( -
- {optimization.trials.length > displayedSteps.length ? ( - - Showing the latest {displayedSteps.length} of{" "} - {optimization.trials.length} received steps. - - ) : null} - String(trial.trial)} - rows={displayedSteps} - /> - - ) : null} {surfaceEligible ? (
) : null} + {optimization.trials.length > 0 ? ( +
+ {optimization.trials.length > displayedSteps.length ? ( + + Showing the latest {displayedSteps.length} of{" "} + {optimization.trials.length} received steps. + + ) : null} +
+
String(trial.trial)} + rows={displayedSteps} + /> + + + ) : null}