fix: Resolve all ESLint errors and warnings in web package
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

Fixes all 542 ESLint problems in the web package to achieve 0 errors and 0 warnings.

Changes:
- Fixed 144 issues: nullish coalescing, return types, unused variables
- Fixed 118 issues: unnecessary conditions, type safety, template literals
- Fixed 79 issues: non-null assertions, unsafe assignments, empty functions
- Fixed 67 issues: explicit return types, promise handling, enum comparisons
- Fixed 45 final warnings: missing return types, optional chains
- Fixed 25 typecheck-related issues: async/await, type assertions, formatting
- Fixed JSX.Element namespace errors across 90+ files

All Quality Rails violations resolved. Lint and typecheck both pass with 0 problems.

Files modified: 118 components, tests, hooks, and utilities

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-31 00:10:03 -06:00
parent f0704db560
commit ac1f2c176f
117 changed files with 749 additions and 505 deletions

View File

@@ -10,13 +10,13 @@ interface ExportButtonProps {
type ExportFormat = "json" | "mermaid" | "png" | "svg";
export function ExportButton({ graph, mermaid }: ExportButtonProps) {
export function ExportButton({ graph, mermaid }: ExportButtonProps): React.JSX.Element {
const [isOpen, setIsOpen] = useState(false);
const [isExporting, setIsExporting] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
useEffect((): (() => void) => {
const handleClickOutside = (event: MouseEvent): void => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
@@ -28,7 +28,7 @@ export function ExportButton({ graph, mermaid }: ExportButtonProps) {
};
}, []);
const downloadFile = (content: string, filename: string, mimeType: string) => {
const downloadFile = (content: string, filename: string, mimeType: string): void => {
const blob = new Blob([content], { type: mimeType });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
@@ -40,19 +40,19 @@ export function ExportButton({ graph, mermaid }: ExportButtonProps) {
URL.revokeObjectURL(url);
};
const exportAsJson = () => {
const exportAsJson = (): void => {
if (!graph) return;
const content = JSON.stringify(graph, null, 2);
downloadFile(content, "knowledge-graph.json", "application/json");
};
const exportAsMermaid = () => {
const exportAsMermaid = (): void => {
if (!mermaid) return;
downloadFile(mermaid.diagram, "knowledge-graph.mmd", "text/plain");
};
const exportAsPng = (): void => {
const svgElement = document.querySelector(".mermaid-container svg")!;
const svgElement = document.querySelector(".mermaid-container svg");
if (!svgElement) {
alert("Please switch to Diagram view first");
return;
@@ -69,7 +69,7 @@ export function ExportButton({ graph, mermaid }: ExportButtonProps) {
const url = URL.createObjectURL(svgBlob);
const img = new Image();
img.onload = () => {
img.onload = (): void => {
canvas.width = img.width * 2;
canvas.height = img.height * 2;
ctx.scale(2, 2);
@@ -78,7 +78,7 @@ export function ExportButton({ graph, mermaid }: ExportButtonProps) {
ctx.drawImage(img, 0, 0);
URL.revokeObjectURL(url);
canvas.toBlob((blob) => {
canvas.toBlob((blob): void => {
if (blob) {
const pngUrl = URL.createObjectURL(blob);
const link = document.createElement("a");
@@ -92,19 +92,19 @@ export function ExportButton({ graph, mermaid }: ExportButtonProps) {
setIsExporting(false);
}, "image/png");
};
img.onerror = () => {
img.onerror = (): void => {
setIsExporting(false);
alert("Failed to export image");
};
img.src = url;
} catch (_error) {
} catch {
setIsExporting(false);
alert("Failed to export image");
}
};
const exportAsSvg = () => {
const svgElement = document.querySelector(".mermaid-container svg")!;
const exportAsSvg = (): void => {
const svgElement = document.querySelector(".mermaid-container svg");
if (!svgElement) {
alert("Please switch to Diagram view first");
return;
@@ -114,7 +114,7 @@ export function ExportButton({ graph, mermaid }: ExportButtonProps) {
downloadFile(svgData, "knowledge-graph.svg", "image/svg+xml");
};
const handleExport = async (format: ExportFormat) => {
const handleExport = (format: ExportFormat): void => {
setIsOpen(false);
switch (format) {
case "json":
@@ -123,10 +123,9 @@ export function ExportButton({ graph, mermaid }: ExportButtonProps) {
case "mermaid":
exportAsMermaid();
break;
case "png": {
await exportAsPng();
case "png":
exportAsPng();
break;
}
case "svg":
exportAsSvg();
break;
@@ -136,7 +135,7 @@ export function ExportButton({ graph, mermaid }: ExportButtonProps) {
return (
<div className="relative" ref={dropdownRef}>
<button
onClick={() => {
onClick={(): void => {
setIsOpen(!isOpen);
}}
disabled={isExporting}
@@ -179,7 +178,9 @@ export function ExportButton({ graph, mermaid }: ExportButtonProps) {
{isOpen && (
<div className="absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 py-1 z-50">
<button
onClick={() => handleExport("json")}
onClick={(): void => {
handleExport("json");
}}
disabled={!graph}
className="w-full px-4 py-2 text-left text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed"
>
@@ -196,7 +197,9 @@ export function ExportButton({ graph, mermaid }: ExportButtonProps) {
</span>
</button>
<button
onClick={() => handleExport("mermaid")}
onClick={(): void => {
handleExport("mermaid");
}}
disabled={!mermaid}
className="w-full px-4 py-2 text-left text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed"
>
@@ -214,7 +217,9 @@ export function ExportButton({ graph, mermaid }: ExportButtonProps) {
</button>
<hr className="my-1 border-gray-200 dark:border-gray-700" />
<button
onClick={() => handleExport("svg")}
onClick={(): void => {
handleExport("svg");
}}
className="w-full px-4 py-2 text-left text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"
>
<span className="flex items-center gap-2">
@@ -230,7 +235,9 @@ export function ExportButton({ graph, mermaid }: ExportButtonProps) {
</span>
</button>
<button
onClick={() => handleExport("png")}
onClick={(): void => {
handleExport("png");
}}
className="w-full px-4 py-2 text-left text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"
>
<span className="flex items-center gap-2">

View File

@@ -18,7 +18,7 @@ interface NodeCreateModalProps {
onCreate: (node: NodeCreateInput) => void;
}
export function NodeCreateModal({ onClose, onCreate }: NodeCreateModalProps) {
export function NodeCreateModal({ onClose, onCreate }: NodeCreateModalProps): React.JSX.Element {
const [title, setTitle] = useState("");
const [nodeType, setNodeType] = useState("concept");
const [content, setContent] = useState("");
@@ -26,13 +26,13 @@ export function NodeCreateModal({ onClose, onCreate }: NodeCreateModalProps) {
const [domain, setDomain] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async (e: React.SyntheticEvent<HTMLFormElement>) => {
const handleSubmit = (e: React.SyntheticEvent<HTMLFormElement>): void => {
e.preventDefault();
if (!title.trim()) return;
setIsSubmitting(true);
try {
const result = await onCreate({
onCreate({
title: title.trim(),
node_type: nodeType,
content: content.trim() || null,
@@ -43,7 +43,6 @@ export function NodeCreateModal({ onClose, onCreate }: NodeCreateModalProps) {
domain: domain.trim() || null,
metadata: {},
});
return result;
} finally {
setIsSubmitting(false);
}