Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,13 @@
"regex.metricConfig": "{{title}} parsing config",
"regex.addMetric": "+ Add Metric",
"regex.xRange": "X-axis range",
"regex.yRange": "Y-axis range",
"regex.min": "Min",
"regex.max": "Max",
"regex.auto": "Auto",
"regex.reset": "Reset",
"regex.xRangeHint": "Hold <0>Shift</0> and drag on the chart to select range, or input values directly.",
"regex.yRangeHint": "Leave blank for auto-scale, or input one/both bounds to manually lock Y-axis range.",
"regex.matchPreview": "Match Preview",
"regex.matchCount": "{{count}} matches",
"regex.lineNumber": "(line {{line}})",
Expand Down
5 changes: 5 additions & 0 deletions public/locales/zh/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,13 @@
"regex.metricConfig": "{{title}} 解析配置",
"regex.addMetric": "+ 添加指标",
"regex.xRange": "X轴范围",
"regex.yRange": "Y轴范围",
"regex.min": "最小值",
"regex.max": "最大值",
"regex.auto": "自动",
"regex.reset": "复位",
"regex.xRangeHint": "在图表上按住 <0>Shift</0> 键并拖动鼠标可选择范围,或直接输入数值。",
"regex.yRangeHint": "留空即自动缩放,也可以输入一个或两个边界手动固定 Y 轴范围。",
"regex.matchPreview": "匹配预览",
"regex.matchCount": "({{count}} 个匹配)",
"regex.lineNumber": "(第{{line}}行)",
Expand Down
4 changes: 4 additions & 0 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
const LARGE_FILE_THRESHOLD = 5 * 1024 * 1024; // 5MB of content

// Default global parsing configuration
export const DEFAULT_GLOBAL_PARSING_CONFIG = {

Check warning on line 18 in src/App.jsx

View workflow job for this annotation

GitHub Actions / test

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
metrics: [
{
name: 'Loss',
Expand Down Expand Up @@ -72,6 +72,7 @@
const [globalDragOver, setGlobalDragOver] = useState(false);
const [, setDragCounter] = useState(0);
const [xRange, setXRange] = useState({ min: undefined, max: undefined });
const [yRange, setYRange] = useState({ min: undefined, max: undefined });
const [maxStep, setMaxStep] = useState(0);
const [sidebarVisible, setSidebarVisible] = useState(true);
const savingDisabledRef = useRef(false);
Expand Down Expand Up @@ -535,6 +536,8 @@
uploadedFiles={uploadedFiles}
xRange={xRange}
onXRangeChange={setXRange}
yRange={yRange}
onYRangeChange={setYRange}
maxStep={maxStep}
/>

Expand Down Expand Up @@ -644,6 +647,7 @@
absoluteBaseline={absoluteBaseline}
xRange={xRange}
onXRangeChange={setXRange}
yRange={yRange}
onMaxStepChange={setMaxStep}
/>
</section>
Expand Down
31 changes: 26 additions & 5 deletions src/components/ChartContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export default function ChartContainer({
relativeBaseline = 0.002,
absoluteBaseline = 0.005,
xRange = { min: undefined, max: undefined },
yRange = { min: undefined, max: undefined },
onXRangeChange,
onMaxStepChange
}) {
Expand Down Expand Up @@ -393,6 +394,24 @@ export default function ChartContainer({
return { min: niceMin, max: niceMax, step };
}, []);

const getFinalYScale = useCallback((autoScale) => {
const hasManualMin = Number.isFinite(yRange?.min);
const hasManualMax = Number.isFinite(yRange?.max);

const min = hasManualMin ? yRange.min : autoScale.min;
const max = hasManualMax ? yRange.max : autoScale.max;

if (!Number.isFinite(min) || !Number.isFinite(max) || min >= max) {
return autoScale;
}

return {
...autoScale,
min,
max
};
Comment on lines +408 to +412
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recompute Y tick step after overriding min/max

After applying manual bounds, this function keeps autoScale.step unchanged while replacing min/max, so the chart can use a step size that no longer matches the displayed range. In practice, widening bounds (for example from an auto range near 0..1 to a manual max in the thousands) can produce extremely dense ticks and hurt chart readability/performance.

Useful? React with 👍 / 👎.

}, [yRange]);

const chartOptions = useMemo(() => ({
responsive: true,
maintainAspectRatio: false,
Expand Down Expand Up @@ -668,7 +687,8 @@ export default function ChartContainer({
});
});

const yRange = calculateNiceScale(min, max);
const autoYRange = calculateNiceScale(min, max);
const finalYRange = getFinalYScale(autoYRange);

const options = {
...chartOptions,
Expand All @@ -690,10 +710,10 @@ export default function ChartContainer({
...chartOptions.scales,
y: {
...chartOptions.scales.y,
min: yRange.min,
max: yRange.max,
min: finalYRange.min,
max: finalYRange.max,
ticks: {
stepSize: yRange.step,
stepSize: finalYRange.step,
callback: (value) => Number(value.toFixed(yDecimals))
}
}
Expand Down Expand Up @@ -721,7 +741,8 @@ export default function ChartContainer({
});
});

const compRange = calculateNiceScale(cMin, cMax);
const autoCompRange = calculateNiceScale(cMin, cMax);
const compRange = getFinalYScale(autoCompRange);
const compDecimals = Math.max(4, getMaxDecimals(compResult.datasets)); // Ensure at least 4 for diffs

const compOptions = {
Expand Down
46 changes: 46 additions & 0 deletions src/components/RegexControls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export function RegexControls({
uploadedFiles = [],
xRange,
onXRangeChange,
yRange,
onYRangeChange,
maxStep
}) {
const [showPreview, setShowPreview] = useState(false);
Expand Down Expand Up @@ -182,6 +184,11 @@ export function RegexControls({
onXRangeChange(newRange);
};

const handleYRangeChange = (field, value) => {
const newRange = { ...yRange, [field]: value === '' ? undefined : Number(value) };
onYRangeChange(newRange);
Comment on lines +187 to +189
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve raw Y input text until value is complete

Parsing e.target.value with Number(...) on every keystroke drops intermediate numeric states ("-", "0.", etc.), so users cannot reliably type negative or decimal Y bounds; the control snaps to empty/integer values before the input is complete. This breaks manual range entry for common datasets (especially comparison/relative values) unless users paste the full number in one shot.

Useful? React with 👍 / 👎.

};

// Function to render config panel
const renderConfigPanel = (type, config, onConfigChange, index) => {
const ModeIcon = MODE_CONFIG[config.mode].icon;
Expand Down Expand Up @@ -353,6 +360,45 @@ export function RegexControls({
</div>
</div>

<div className="border border-gray-200 dark:border-gray-700 rounded-lg p-3">
<div className="flex items-center gap-2 mb-2">
<ZoomIn
size={16}
className="text-gray-600 dark:text-gray-300"
aria-hidden="true"
/>
<h4 className="text-base font-semibold text-gray-800 dark:text-gray-100">
{t('regex.yRange')}
</h4>
</div>
<div className="flex items-center gap-2">
<input
type="number"
placeholder={t('regex.min')}
value={yRange?.min ?? ''}
onChange={(e) => handleYRangeChange('min', e.target.value)}
className="input-field"
/>
<span className="text-gray-500 dark:text-gray-400">-</span>
<input
type="number"
placeholder={t('regex.max')}
value={yRange?.max ?? ''}
onChange={(e) => handleYRangeChange('max', e.target.value)}
className="input-field"
/>
<button
onClick={() => onYRangeChange({ min: undefined, max: undefined })}
className="px-2 py-1 text-xs bg-gray-200 dark:bg-gray-600 text-gray-700 dark:text-gray-300 rounded-md hover:bg-gray-300 dark:hover:bg-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 whitespace-nowrap"
>
{t('regex.auto')}
</button>
</div>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
{t('regex.yRangeHint')}
</p>
</div>

<div className="border border-gray-200 dark:border-gray-700 rounded-lg p-3">
<div className="flex items-center gap-2 mb-2">
<ZoomIn
Expand Down
Loading