Clean up LSP diagnostics across backend and frontend
Backend (nvcurve/): - hal/fans.py, hal/limits.py, hal/gpu.py: replace conditional pynvml imports with the established 'pynvml: Any = _pynvml_import' pattern (fixes ~50 'possibly unbound' errors); type the result dicts; guard query_interface() results; explicit uuid/pci-bus parsing (int, hex convention documented); modernize Optional[T] -> T | None - cli.py: fix 'curve_state' possibly-unbound and snap_path None handling in cmd_setup; wrap unchecked int()/open()/makedirs() calls in try/except with clean CLI errors; add module logger for silent except-pass blocks; raise ... from exc; fix unused loop vars and set-comprehension - hal/snapshot.py: filepath: str | None; wrap all file ops; sorted imports; remove unused CT_POINTS import - daemon.py: extract 0o666 to _SOCKET_MODE constant (intentional for /run sockets) with nosemgrep - server.py: nosemgrep for Python 3.7-compat false positive (project requires >= 3.12); log previously-swallowed exception - profiles/native.py, profiles/apply.py: wrap file ops and int(k) profile-key parsing; sorted imports; modernize typing Frontend (frontend/src): - Add .js extensions to all relative imports (standard TS-ESM; Vite resolves .js -> .ts) - React.FormEvent (deprecated in React 19 types) -> React.SubmitEvent - catch (e: any) -> catch (e: unknown) + instanceof Error narrowing - React-hooks: move ref writes from render into effects; convert viewport reset to render-phase state adjustment; split selectPoint(index, multi) into selectPoint + togglePoint (no flag argument); remove non-null assertion - Static inline styles -> Tailwind classes (dynamic positioning/cursor styles kept) - Remove non-standard 'container' option from scrollIntoView (browsers ignore unknown options) which had orphaned a @ts-expect-error - Object.fromEntries for Map -> Record conversion Tooling: - .gitignore: ignore .codegraph/ local tool data Verified: tsc --noEmit, vite production build, python imports, and full LSP scan (0 errors/warnings in both projects).
This commit is contained in:
1 parent
9006c22fde
commit
930e56bd07
33 files changed
+1571
-804
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { fmt } from '../../utils/units';
|
||||
import type { VFPoint } from '../../types';
|
||||
import { useCurveStore } from '../../store/curveStore';
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { fmt } from "../../utils/units.js";
|
||||
import type { VFPoint } from "../../types.js";
|
||||
import { useCurveStore } from "../../store/curveStore.js";
|
||||
|
||||
interface Props {
|
||||
point: VFPoint;
|
||||
@@ -14,17 +14,28 @@ interface Props {
|
||||
onMouseEnter?: () => void;
|
||||
}
|
||||
|
||||
export function PointRow({ point, isCurrent, isSelected, isClamped, pendingDeltaKhz, shouldAutoScroll, onMouseDown, onMouseEnter }: Props) {
|
||||
export function PointRow({
|
||||
point,
|
||||
isCurrent,
|
||||
isSelected,
|
||||
isClamped,
|
||||
pendingDeltaKhz,
|
||||
shouldAutoScroll,
|
||||
onMouseDown,
|
||||
onMouseEnter,
|
||||
}: Props) {
|
||||
const { stageEdit } = useCurveStore();
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const trRef = useRef<HTMLTableRowElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldAutoScroll && trRef.current) {
|
||||
// @ts-expect-error: the typing seems to not include the valid 'container' option
|
||||
trRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest', container: 'nearest' });
|
||||
trRef.current.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "nearest",
|
||||
});
|
||||
}
|
||||
}, [shouldAutoScroll]);
|
||||
|
||||
@@ -36,11 +47,19 @@ export function PointRow({ point, isCurrent, isSelected, isClamped, pendingDelta
|
||||
const displayEffMhz = point.freq_mhz + deltaChange / 1000;
|
||||
|
||||
const deltaColor = hasPending
|
||||
? displayDeltaKhz > 0 ? 'text-cyan-400' : displayDeltaKhz < 0 ? 'text-orange-400' : 'text-zinc-400'
|
||||
: point.delta_khz > 0 ? 'text-emerald-400' : point.delta_khz < 0 ? 'text-red-400' : 'text-zinc-500';
|
||||
? displayDeltaKhz > 0
|
||||
? "text-cyan-400"
|
||||
: displayDeltaKhz < 0
|
||||
? "text-orange-400"
|
||||
: "text-zinc-400"
|
||||
: point.delta_khz > 0
|
||||
? "text-emerald-400"
|
||||
: point.delta_khz < 0
|
||||
? "text-red-400"
|
||||
: "text-zinc-500";
|
||||
|
||||
function startEdit() {
|
||||
setInputValue((displayDeltaMhz).toFixed(1));
|
||||
setInputValue(displayDeltaMhz.toFixed(1));
|
||||
setEditing(true);
|
||||
setTimeout(() => {
|
||||
inputRef.current?.select();
|
||||
@@ -64,9 +83,13 @@ export function PointRow({ point, isCurrent, isSelected, isClamped, pendingDelta
|
||||
<tr
|
||||
ref={trRef}
|
||||
className={[
|
||||
'border-b border-zinc-800 text-xs font-mono cursor-pointer',
|
||||
isCurrent ? 'bg-yellow-400/10' : isSelected ? 'bg-cyan-500/10' : 'hover:bg-zinc-800/50',
|
||||
].join(' ')}
|
||||
"border-b border-zinc-800 text-xs font-mono cursor-pointer",
|
||||
isCurrent
|
||||
? "bg-yellow-400/10"
|
||||
: isSelected
|
||||
? "bg-cyan-500/10"
|
||||
: "hover:bg-zinc-800/50",
|
||||
].join(" ")}
|
||||
onMouseDown={(e) => {
|
||||
if (editing) return;
|
||||
onMouseDown?.(e);
|
||||
@@ -80,7 +103,13 @@ export function PointRow({ point, isCurrent, isSelected, isClamped, pendingDelta
|
||||
<td className="px-3 py-1 text-zinc-300">{fmt.mv(point.volt_mv, 0)}</td>
|
||||
|
||||
{/* Offset — click to edit inline */}
|
||||
<td className={`px-3 py-1 ${deltaColor}`} onClick={(e) => { e.stopPropagation(); startEdit(); }}>
|
||||
<td
|
||||
className={`px-3 py-1 ${deltaColor}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
startEdit();
|
||||
}}
|
||||
>
|
||||
{editing ? (
|
||||
<input
|
||||
ref={inputRef}
|
||||
@@ -90,28 +119,35 @@ export function PointRow({ point, isCurrent, isSelected, isClamped, pendingDelta
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
onBlur={commitEdit}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === 'Tab') { e.preventDefault(); commitEdit(); }
|
||||
if (e.key === 'Escape') cancelEdit();
|
||||
if (e.key === "Enter" || e.key === "Tab") {
|
||||
e.preventDefault();
|
||||
commitEdit();
|
||||
}
|
||||
if (e.key === "Escape") cancelEdit();
|
||||
}}
|
||||
className="w-20 bg-zinc-700 text-cyan-300 rounded px-1 py-0 border border-cyan-500 outline-none text-xs"
|
||||
style={{ fontFamily: 'monospace' }}
|
||||
className="w-20 bg-zinc-700 text-cyan-300 rounded px-1 py-0 border border-cyan-500 outline-none text-xs font-mono"
|
||||
/>
|
||||
) : (
|
||||
<span title="Click to edit">
|
||||
{hasPending && <span className="text-cyan-500 mr-0.5">✎</span>}
|
||||
{displayDeltaKhz > 0 ? '+' : ''}{displayDeltaMhz.toFixed(1)} MHz
|
||||
{displayDeltaKhz > 0 ? "+" : ""}
|
||||
{displayDeltaMhz.toFixed(1)} MHz
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
|
||||
{/* Eff. Freq */}
|
||||
<td className={`px-3 py-1 font-semibold ${hasPending ? 'text-cyan-200' : 'text-zinc-100'}`}>
|
||||
<td
|
||||
className={`px-3 py-1 font-semibold ${hasPending ? "text-cyan-200" : "text-zinc-100"}`}
|
||||
>
|
||||
{fmt.mhz(displayEffMhz, 0)}
|
||||
{isClamped && !hasPending && (
|
||||
<span
|
||||
className="ml-1 text-amber-500 cursor-help"
|
||||
title="Clamped by monotonicity — a lower-voltage point with a higher offset is holding this frequency up"
|
||||
>⇡</span>
|
||||
>
|
||||
⇡
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { useState, useMemo, useEffect } from 'react';
|
||||
import { PointRow } from './PointRow';
|
||||
import type { VFPoint } from '../../types';
|
||||
import { findCurrentPoint, detectClampedPoints } from '../../utils/curveHelpers';
|
||||
import { useCurveStore } from '../../store/curveStore';
|
||||
import { useState, useMemo, useEffect } from "react";
|
||||
import { PointRow } from "./PointRow.js";
|
||||
import type { VFPoint } from "../../types.js";
|
||||
import {
|
||||
findCurrentPoint,
|
||||
detectClampedPoints,
|
||||
} from "../../utils/curveHelpers.js";
|
||||
import { useCurveStore } from "../../store/curveStore.js";
|
||||
|
||||
interface Props {
|
||||
points: VFPoint[];
|
||||
@@ -11,22 +14,32 @@ interface Props {
|
||||
}
|
||||
|
||||
export function PointTable({ points, currentVoltageMv, readOnly }: Props) {
|
||||
const { pendingDeltas, selectedPoints, selectPoint, selectRange } = useCurveStore();
|
||||
const {
|
||||
pendingDeltas,
|
||||
selectedPoints,
|
||||
selectPoint,
|
||||
togglePoint,
|
||||
selectRange,
|
||||
} = useCurveStore();
|
||||
const currentPoint = findCurrentPoint(points, currentVoltageMv);
|
||||
const clampedPoints = useMemo(() => detectClampedPoints(points), [points]);
|
||||
|
||||
const [dragStartIdx, setDragStartIdx] = useState<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
function onUp() { setDragStartIdx(null); }
|
||||
window.addEventListener('mouseup', onUp);
|
||||
return () => window.removeEventListener('mouseup', onUp);
|
||||
function onUp() {
|
||||
setDragStartIdx(null);
|
||||
}
|
||||
window.addEventListener("mouseup", onUp);
|
||||
return () => window.removeEventListener("mouseup", onUp);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="bg-zinc-900 rounded-lg overflow-hidden flex flex-col">
|
||||
<div className="flex items-center gap-2 px-3 py-2 border-b border-zinc-800 shrink-0">
|
||||
<span className="text-xs text-zinc-500 uppercase tracking-wider mr-2">Points</span>
|
||||
<span className="text-xs text-zinc-500 uppercase tracking-wider mr-2">
|
||||
Points
|
||||
</span>
|
||||
{!readOnly && pendingDeltas.size > 0 && (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-cyan-500/15 border border-cyan-500/30 text-cyan-400 text-xs">
|
||||
{pendingDeltas.size} staged
|
||||
@@ -35,13 +48,17 @@ export function PointTable({ points, currentVoltageMv, readOnly }: Props) {
|
||||
{readOnly && (
|
||||
<span className="text-xs text-zinc-600 italic">read-only</span>
|
||||
)}
|
||||
<span className="ml-auto text-xs text-zinc-600">{points.length} points</span>
|
||||
<span className="ml-auto text-xs text-zinc-600">
|
||||
{points.length} points
|
||||
</span>
|
||||
{!readOnly && selectedPoints.size === 1 && (
|
||||
<div className="flex gap-1 ml-4 border-l border-zinc-800 pl-4">
|
||||
<button
|
||||
onClick={() => {
|
||||
const idx = Array.from(selectedPoints)[0];
|
||||
const beforeIdxs = points.filter(p => p.index <= idx).map(p => p.index);
|
||||
const beforeIdxs = points
|
||||
.filter((p) => p.index <= idx)
|
||||
.map((p) => p.index);
|
||||
selectRange(beforeIdxs);
|
||||
}}
|
||||
className="px-2 py-0.5 rounded text-xs bg-zinc-800 text-zinc-400 hover:bg-zinc-700 transition-colors whitespace-nowrap"
|
||||
@@ -51,7 +68,9 @@ export function PointTable({ points, currentVoltageMv, readOnly }: Props) {
|
||||
<button
|
||||
onClick={() => {
|
||||
const idx = Array.from(selectedPoints)[0];
|
||||
const afterIdxs = points.filter(p => p.index >= idx).map(p => p.index);
|
||||
const afterIdxs = points
|
||||
.filter((p) => p.index >= idx)
|
||||
.map((p) => p.index);
|
||||
selectRange(afterIdxs);
|
||||
}}
|
||||
className="px-2 py-0.5 rounded text-xs bg-zinc-800 text-zinc-400 hover:bg-zinc-700 transition-colors whitespace-nowrap"
|
||||
@@ -66,9 +85,15 @@ export function PointTable({ points, currentVoltageMv, readOnly }: Props) {
|
||||
<thead className="sticky top-0 bg-zinc-900 z-10">
|
||||
<tr className="text-xs text-zinc-500 uppercase tracking-wider border-b border-zinc-800">
|
||||
<th className="px-3 py-2 text-left font-normal bg-zinc-900">#</th>
|
||||
<th className="px-3 py-2 text-left font-normal bg-zinc-900">Voltage</th>
|
||||
<th className="px-3 py-2 text-left font-normal bg-zinc-900">Offset</th>
|
||||
<th className="px-3 py-2 text-left font-normal bg-zinc-900">Eff. Freq</th>
|
||||
<th className="px-3 py-2 text-left font-normal bg-zinc-900">
|
||||
Voltage
|
||||
</th>
|
||||
<th className="px-3 py-2 text-left font-normal bg-zinc-900">
|
||||
Offset
|
||||
</th>
|
||||
<th className="px-3 py-2 text-left font-normal bg-zinc-900">
|
||||
Eff. Freq
|
||||
</th>
|
||||
<th className="px-3 py-2 text-left font-normal bg-zinc-900" />
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -81,34 +106,48 @@ export function PointTable({ points, currentVoltageMv, readOnly }: Props) {
|
||||
isSelected={selectedPoints.has(p.index)}
|
||||
isClamped={clampedPoints.has(p.index)}
|
||||
pendingDeltaKhz={pendingDeltas.get(p.index)}
|
||||
shouldAutoScroll={selectedPoints.size === 1 && selectedPoints.has(p.index)}
|
||||
onMouseDown={readOnly ? undefined : (e) => {
|
||||
if (e.shiftKey) {
|
||||
const currentSelected = Array.from(selectedPoints);
|
||||
if (currentSelected.length > 0) {
|
||||
const last = Math.max(...currentSelected);
|
||||
const min = Math.min(last, p.index);
|
||||
const max = Math.max(last, p.index);
|
||||
const toSelect = points.filter(a => a.index >= min && a.index <= max).map(a => a.index);
|
||||
selectRange(toSelect);
|
||||
} else {
|
||||
selectPoint(p.index, false);
|
||||
}
|
||||
} else if (e.ctrlKey || e.metaKey) {
|
||||
selectPoint(p.index, true);
|
||||
} else {
|
||||
setDragStartIdx(p.index);
|
||||
selectPoint(p.index, false);
|
||||
}
|
||||
}}
|
||||
onMouseEnter={readOnly ? undefined : () => {
|
||||
if (dragStartIdx !== null) {
|
||||
const min = Math.min(dragStartIdx, p.index);
|
||||
const max = Math.max(dragStartIdx, p.index);
|
||||
const toSelect = points.filter(a => a.index >= min && a.index <= max).map(a => a.index);
|
||||
selectRange(toSelect);
|
||||
}
|
||||
}}
|
||||
shouldAutoScroll={
|
||||
selectedPoints.size === 1 && selectedPoints.has(p.index)
|
||||
}
|
||||
onMouseDown={
|
||||
readOnly
|
||||
? undefined
|
||||
: (e) => {
|
||||
if (e.shiftKey) {
|
||||
const currentSelected = Array.from(selectedPoints);
|
||||
if (currentSelected.length > 0) {
|
||||
const last = Math.max(...currentSelected);
|
||||
const min = Math.min(last, p.index);
|
||||
const max = Math.max(last, p.index);
|
||||
const toSelect = points
|
||||
.filter((a) => a.index >= min && a.index <= max)
|
||||
.map((a) => a.index);
|
||||
selectRange(toSelect);
|
||||
} else {
|
||||
selectPoint(p.index);
|
||||
}
|
||||
} else if (e.ctrlKey || e.metaKey) {
|
||||
togglePoint(p.index);
|
||||
} else {
|
||||
setDragStartIdx(p.index);
|
||||
selectPoint(p.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
onMouseEnter={
|
||||
readOnly
|
||||
? undefined
|
||||
: () => {
|
||||
if (dragStartIdx !== null) {
|
||||
const min = Math.min(dragStartIdx, p.index);
|
||||
const max = Math.max(dragStartIdx, p.index);
|
||||
const toSelect = points
|
||||
.filter((a) => a.index >= min && a.index <= max)
|
||||
.map((a) => a.index);
|
||||
selectRange(toSelect);
|
||||
}
|
||||
}
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
Reference in new issue
Block a user