diff --git a/.gitignore b/.gitignore index d3bf97f3..ccc1e90f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,11 @@ target/ dist/ .DS_Store -Cargo.lock dhat-heap.json build.sh shell.nix node_modules/ runtime_metrics.csv compile_metrics.csv -.idea/ \ No newline at end of file +.idea/ +electron/dist-electron/ diff --git a/electron/src/components/graph/GraphControls.tsx b/electron/src/components/graph/GraphControls.tsx index a5daa43d..d171702c 100644 --- a/electron/src/components/graph/GraphControls.tsx +++ b/electron/src/components/graph/GraphControls.tsx @@ -80,7 +80,7 @@ export function GraphControls({ )} onSwitchToHistorical("button")} variant="outline" className={`h-auto px-3 py-3 text-base font-medium transition-colors ${ !isLiveMode @@ -197,7 +197,7 @@ export function FloatingControlPanel({ )} onSwitchToHistorical("button")} variant="outline" className={`h-auto px-3 py-3 text-base font-medium transition-colors ${ !isLiveMode diff --git a/electron/src/components/graph/historicalMode.ts b/electron/src/components/graph/historicalMode.ts index a2708f97..d02ca236 100644 --- a/electron/src/components/graph/historicalMode.ts +++ b/electron/src/components/graph/historicalMode.ts @@ -3,7 +3,12 @@ import { useRef, useCallback } from "react"; import uPlot from "uplot"; import { seriesToUPlotData } from "@/lib/timeseries"; import { getPrimarySeriesData, stopAnimations } from "./animation"; -import { BigGraphProps, HistoricalModeHandlers, AnimationRefs } from "./types"; +import { + BigGraphProps, + HistoricalModeHandlers, + AnimationRefs, + SwitchOrigin, +} from "./types"; export function useHistoricalMode({ newData, @@ -120,36 +125,43 @@ export function useHistoricalMode({ ); // Switch to historical mode - const switchToHistoricalMode = useCallback(() => { - isInHistoricalModeRef.current = true; + const switchToHistoricalMode = useCallback( + (origin?: SwitchOrigin) => { + isInHistoricalModeRef.current = true; - // Preserve current scale - try { - if (uplotRef.current) { - const xScale = uplotRef.current.scales.x; - const yScale = uplotRef.current.scales.y; - manualScaleRef.current = { - x: { - min: xScale?.min ?? 0, - max: xScale?.max ?? 0, - }, - y: { - min: yScale?.min ?? 0, - max: yScale?.max ?? 1, - }, - }; + // Preserve current scale + if (origin === "gesture") { + try { + if (uplotRef.current) { + const xScale = uplotRef.current.scales.x; + const yScale = uplotRef.current.scales.y; + manualScaleRef.current = { + x: { + min: xScale?.min ?? 0, + max: xScale?.max ?? 0, + }, + y: { + min: yScale?.min ?? 0, + max: yScale?.max ?? 1, + }, + }; + } + } catch (err) { + console.warn( + "Error preserving chart scales when switching to historical mode:", + err, + ); + } + } else { + manualScaleRef.current = null; } - } catch (err) { - console.warn( - "Error preserving chart scales when switching to historical mode:", - err, - ); - } - captureHistoricalFreezeTimestamp(); - stopAnimations(animationRefs); - lastProcessedCountRef.current = 0; // Reset processed count - }, [captureHistoricalFreezeTimestamp, animationRefs, lastProcessedCountRef]); + captureHistoricalFreezeTimestamp(); + stopAnimations(animationRefs); + lastProcessedCountRef.current = 0; // Reset processed count + }, + [captureHistoricalFreezeTimestamp, animationRefs, lastProcessedCountRef], + ); // Switch back to live mode const switchToLiveMode = useCallback(() => { diff --git a/electron/src/components/graph/types.ts b/electron/src/components/graph/types.ts index beff7280..dcd8efd6 100644 --- a/electron/src/components/graph/types.ts +++ b/electron/src/components/graph/types.ts @@ -3,6 +3,8 @@ import { Unit } from "@/control/units"; import { TimeSeries } from "@/lib/timeseries"; import { RefObject } from "react"; +export type SwitchOrigin = "button" | "gesture"; + // Prop-based sync types export type PropGraphSync = { timeWindow: number | "all"; @@ -11,6 +13,7 @@ export type PropGraphSync = { xRange?: { min: number; max: number }; historicalFreezeTimestamp?: number | null; showFromTimestamp?: number | null; + historicalSwitchOrigin: SwitchOrigin; onTimeWindowChange?: (graphId: string, timeWindow: number | "all") => void; onViewModeChange?: ( graphId: string, @@ -83,7 +86,7 @@ export type ControlProps = { isLiveMode: boolean; onTimeWindowChange: (timeWindow: number | "all") => void; onSwitchToLive: () => void; - onSwitchToHistorical: () => void; + onSwitchToHistorical: (origin: SwitchOrigin) => void; onExport?: () => void; timeWindowOptions?: TimeWindowOption[]; showFromTimestamp?: number | null; @@ -100,7 +103,7 @@ export interface HistoricalModeHandlers { captureHistoricalFreezeTimestamp: () => number; getHistoricalEndTimestamp: () => number; handleHistoricalTimeWindow: (timeWindow: number | "all") => void; - switchToHistoricalMode: () => void; + switchToHistoricalMode: (origin?: SwitchOrigin) => void; switchToLiveMode: () => void; } diff --git a/electron/src/components/graph/useBigGraphEffects.ts b/electron/src/components/graph/useBigGraphEffects.ts index 730bf153..aab0476b 100644 --- a/electron/src/components/graph/useBigGraphEffects.ts +++ b/electron/src/components/graph/useBigGraphEffects.ts @@ -175,7 +175,7 @@ export function useBigGraphEffects({ const newIsLiveMode = syncGraph.isLiveMode; if (lastState.isLiveMode && !newIsLiveMode) { - historicalMode.switchToHistoricalMode(); + historicalMode.switchToHistoricalMode(syncGraph.historicalSwitchOrigin); } else if (!lastState.isLiveMode && newIsLiveMode) { historicalMode.switchToLiveMode(); diff --git a/electron/src/components/graph/useGraphSync.ts b/electron/src/components/graph/useGraphSync.ts index a86ce7d3..c03f1522 100644 --- a/electron/src/components/graph/useGraphSync.ts +++ b/electron/src/components/graph/useGraphSync.ts @@ -1,5 +1,5 @@ import { useState, useCallback, useRef } from "react"; -import { PropGraphSync } from "./types"; +import { PropGraphSync, SwitchOrigin } from "./types"; import { GraphExportData, exportGraphsToExcel } from "./excelExport"; import { useGraphSettingsStore } from "@/stores/graphSettingsStore"; @@ -19,6 +19,7 @@ export function useGraphSync(exportGroupId?: string) { null, ); + const historicalSwitchOriginRef = useRef("gesture"); const historicalFreezeTimestampRef = useRef(null); const graphDataRef = useRef GraphExportData | null>>( new Map(), @@ -65,6 +66,7 @@ export function useGraphSync(exportGroupId?: string) { showFromTimestamp?: number | null; clearHistoricalFreeze?: boolean; setHistoricalFreeze?: boolean; + historicalSwitchOrigin?: SwitchOrigin; }, requestId?: number, ) => { @@ -104,6 +106,10 @@ export function useGraphSync(exportGroupId?: string) { historicalFreezeTimestampRef.current = Date.now(); } + if (updates.historicalSwitchOrigin !== undefined) { + historicalSwitchOriginRef.current = updates.historicalSwitchOrigin; + } + clearChangeSource(); }); }, @@ -164,6 +170,7 @@ export function useGraphSync(exportGroupId?: string) { viewMode: "manual", isLiveMode: false, setHistoricalFreeze: historicalFreezeTimestampRef.current === null, + historicalSwitchOrigin: "gesture", }); }, [updateSyncState], @@ -210,6 +217,7 @@ export function useGraphSync(exportGroupId?: string) { isLiveMode: false, viewMode: "manual", setHistoricalFreeze: true, + historicalSwitchOrigin: "button", }); }, [updateSyncState]); @@ -242,6 +250,7 @@ export function useGraphSync(exportGroupId?: string) { xRange, historicalFreezeTimestamp: historicalFreezeTimestampRef.current, showFromTimestamp, + historicalSwitchOrigin: historicalSwitchOriginRef.current, onTimeWindowChange: handleTimeWindowChange, onViewModeChange: handleViewModeChange, onZoomChange: handleZoomChangeThrottled,