Skip to content

Commit 3d75a71

Browse files
jaysukclaude
andcommitted
feat: persist UI selections across navigation
Save the driver, loop mode, encoder type, mode D-values, PID values, capture settings (samples/rate/move/record vars), plot variables and the current step/wizard position to localStorage (debounced), and restore them as the refs' initial values on mount — so leaving the page and coming back keeps your place. Restore seeds the initial values (non-immediate watchers), so it doesn't re-trigger the driver-change PID reload and clobber in-progress edits. The captured CSV/chart is intentionally not persisted (re-run a capture). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 67edde9 commit 3d75a71

1 file changed

Lines changed: 44 additions & 14 deletions

File tree

src/components/ClosedLoopTuning.vue

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ import { LogLevel, useUiStore } from "@/stores/ui";
329329
import { HelpTip } from "dwc-plugin-runtime";
330330
331331
import CaptureChart from "./CaptureChart.vue";
332-
import { CAPTURE_DIR, DOCS } from "../model/constants";
332+
import { CAPTURE_DIR, DOCS, LS_STATE } from "../model/constants";
333333
import {
334334
buildCalibrationCommand, buildCaptureCommand, buildModeCommand, buildPidCommand,
335335
CALIBRATION_MOVES, CAPTURE_VARIABLES, DEFAULT_MODE_D, ENCODER_TYPES, MODE_LABELS,
@@ -357,30 +357,60 @@ const modeHelp: Record<LoopMode, string> = {
357357
};
358358
const stepTitles = ["1. Driver", "2. Loop mode", "3. Calibrate", "4. Tune PID", "5. Test & save"];
359359
360-
const step = ref(1);
361-
const selectedDriver = ref<string | null>(null);
362-
const currentMode = ref<LoopMode | null>(null);
363-
const encoderType = ref<EncoderType>(2);
364-
const modeD = reactive({ ...DEFAULT_MODE_D });
365-
const pid = reactive<PidConfig>({ p: 100, i: 0, d: 0, v: 0, a: 0, warn: null, err: null });
360+
// Persisted UI state — restored on mount so navigating away and back keeps your place.
361+
interface SavedState {
362+
step?: number; wizardIndex?: number; selectedDriver?: string | null; currentMode?: LoopMode | null;
363+
encoderType?: EncoderType; modeD?: Partial<typeof DEFAULT_MODE_D>; pid?: Partial<PidConfig>;
364+
samples?: number; sampleRate?: number; moveMode?: "step" | "custom"; customMove?: string;
365+
recordKeys?: Array<string>; viewKeys?: Array<string>;
366+
}
367+
function loadState(): SavedState {
368+
try { return JSON.parse(localStorage.getItem(LS_STATE) ?? "{}") as SavedState; } catch { return {}; }
369+
}
370+
const saved = loadState();
371+
372+
const step = ref(saved.step ?? 1);
373+
const selectedDriver = ref<string | null>(saved.selectedDriver ?? null);
374+
const currentMode = ref<LoopMode | null>(saved.currentMode ?? null);
375+
const encoderType = ref<EncoderType>(saved.encoderType ?? 2);
376+
const modeD = reactive({ ...DEFAULT_MODE_D, ...(saved.modeD ?? {}) });
377+
const pid = reactive<PidConfig>({ p: 100, i: 0, d: 0, v: 0, a: 0, warn: null, err: null, ...(saved.pid ?? {}) });
366378
const applyingPid = ref(false);
367379
368-
const samples = ref(2000);
369-
const sampleRate = ref(2000);
370-
const moveMode = ref<"step" | "custom">("step");
371-
const customMove = ref("G91 G1 H2 X50 F6000 G90");
372-
const recordKeys = ref<Array<string>>(["measuredMotorSteps", "targetMotorSteps", "currentError", "pidPTerm"]);
380+
const samples = ref(saved.samples ?? 2000);
381+
const sampleRate = ref(saved.sampleRate ?? 2000);
382+
const moveMode = ref<"step" | "custom">(saved.moveMode ?? "step");
383+
const customMove = ref(saved.customMove ?? "G91 G1 H2 X50 F6000 G90");
384+
const recordKeys = ref<Array<string>>(saved.recordKeys ?? ["measuredMotorSteps", "targetMotorSteps", "currentError", "pidPTerm"]);
373385
const recording = ref(false);
374386
375387
const capture = ref<ParsedCapture | null>(null);
376388
const overlayCapture = ref<ParsedCapture | null>(null);
377389
const rawText = ref<string>("");
378390
const metrics = ref<StepMetrics | null>(null);
379-
const viewKeys = ref<Array<string>>(["measuredMotorSteps", "targetMotorSteps", "currentError"]);
391+
const viewKeys = ref<Array<string>>(saved.viewKeys ?? ["measuredMotorSteps", "targetMotorSteps", "currentError"]);
380392
381-
const wizardIndex = ref(0);
393+
const wizardIndex = ref(saved.wizardIndex ?? 0);
382394
const recommendation = ref<Recommendation | null>(null);
383395
396+
// Save the lightweight selections (not the captured CSV) whenever they change, debounced.
397+
let saveTimer: ReturnType<typeof setTimeout> | undefined;
398+
function persistState(): void {
399+
if (saveTimer) { clearTimeout(saveTimer); }
400+
saveTimer = setTimeout(() => {
401+
try {
402+
localStorage.setItem(LS_STATE, JSON.stringify({
403+
step: step.value, wizardIndex: wizardIndex.value, selectedDriver: selectedDriver.value,
404+
currentMode: currentMode.value, encoderType: encoderType.value, modeD: { ...modeD }, pid: { ...pid },
405+
samples: samples.value, sampleRate: sampleRate.value, moveMode: moveMode.value,
406+
customMove: customMove.value, recordKeys: recordKeys.value, viewKeys: viewKeys.value,
407+
} satisfies SavedState));
408+
} catch { /* storage unavailable */ }
409+
}, 300);
410+
}
411+
watch([step, wizardIndex, selectedDriver, currentMode, encoderType, modeD, pid, samples, sampleRate, moveMode, customMove, recordKeys, viewKeys],
412+
persistState, { deep: true });
413+
384414
// --- Auto-tune ---
385415
const autoRunning = ref(false);
386416
const autoCancel = ref(false);

0 commit comments

Comments
 (0)