All files / client/src/hooks use-pin-state.ts

96.66% Statements 29/30
100% Branches 10/10
100% Functions 7/7
96.66% Lines 29/30

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86                26x       26x         26x     26x 14x 14x             26x 14x 2x 2x 2x         14x 14x 14x         26x   2x   2x 1x 1x 1x             26x 14x 3x   11x 9x 9x 8x     3x     26x                          
import { useState, useEffect, useCallback } from "react";
 
export interface UsePinStateParams {
  resetPinStates: () => void;
}
 
export function usePinState({ resetPinStates }: UsePinStateParams) {
  // Analog pins detected in the code that need sliders (internal pin numbers 14..19)
  const [analogPinsUsed, setAnalogPinsUsed] = useState<number[]>([]);
 
  // Detected explicit pinMode(...) declarations found during parsing.
  // We store modes for pins so that we can apply them when the simulation starts.
  const [detectedPinModes, setDetectedPinModes] = useState<
    Record<number, "INPUT" | "OUTPUT" | "INPUT_PULLUP">
  >({});
 
  // Pins that have a detected pinMode(...) declaration which conflicts with analogRead usage
  const [pendingPinConflicts, setPendingPinConflicts] = useState<number[]>([]);
 
  // Pin Monitor visibility state (persisted to localStorage)
  const [pinMonitorVisible, setPinMonitorVisible] = useState<boolean>(() => {
    try {
      return window.localStorage.getItem("unoPinMonitorVisible") === "1";
    } catch {
      return false; // Hidden by default
    }
  });
 
  // Listen for pin monitor visibility change events from settings dialog
  useEffect(() => {
    const handler = (ev: any) => {
      try {
        const newValue = Boolean(ev?.detail?.value);
        setPinMonitorVisible(newValue);
      } catch {
        // ignore
      }
    };
    document.addEventListener("pinMonitorVisibleChange", handler as EventListener);
    return () =>
      document.removeEventListener("pinMonitorVisibleChange", handler as EventListener);
  }, []);
 
  // Centralized helper to reset UI pin-related state. Pass { keepDetected: true }
  // to preserve detected pinMode declarations and pending conflicts when desired.
  const resetPinUI = useCallback(
    (opts?: { keepDetected?: boolean }) => {
      resetPinStates();
      // Only clear detected/derived data when keepDetected is not requested.
      if (!opts?.keepDetected) {
        setAnalogPinsUsed([]);
        setDetectedPinModes({});
        setPendingPinConflicts([]);
      }
    },
    [resetPinStates],
  );
 
  // Helper function to convert pin strings to numbers (A0-A5 → 14-19, digital → as-is)
  const pinToNumber = (pinStr: string): number | null => {
    if (/^\d+$/.test(pinStr)) {
      return parseInt(pinStr, 10);
    }
    if (/^A\d+$/i.test(pinStr)) {
      const analogIndex = parseInt(pinStr.slice(1), 10);
      if (analogIndex >= 0 && analogIndex <= 5) {
        return 14 + analogIndex; // A0->14, A1->15, ..., A5->19
      }
    }
    return null;
  };
 
  return {
    analogPinsUsed,
    setAnalogPinsUsed,
    detectedPinModes,
    setDetectedPinModes,
    pendingPinConflicts,
    setPendingPinConflicts,
    pinMonitorVisible,
    setPinMonitorVisible,
    resetPinUI,
    pinToNumber,
  };
}