{/* Content area */}
diff --git a/src/components/prompts/prompt-card.tsx b/src/components/prompts/prompt-card.tsx
index ecd777e2..bfd09e80 100644
--- a/src/components/prompts/prompt-card.tsx
+++ b/src/components/prompts/prompt-card.tsx
@@ -2,9 +2,7 @@
import { useState, useRef, useEffect } from "react";
import Link from "next/link";
-import Image from "next/image";
import { useTranslations, useLocale } from "next-intl";
-import { formatDistanceToNow } from "@/lib/date";
import { getPromptUrl } from "@/lib/urls";
import { ArrowBigUp, Lock, Copy, ImageIcon, Download, Play, BadgeCheck, Volume2, Link2 } from "lucide-react";
import { Badge } from "@/components/ui/badge";
@@ -91,7 +89,7 @@ export interface PromptCardProps {
export function PromptCard({ prompt, showPinButton = false, isPinned = false }: PromptCardProps) {
const t = useTranslations("prompts");
const tCommon = useTranslations("common");
- const locale = useLocale();
+ const _locale = useLocale();
const outgoingCount = prompt._count?.outgoingConnections || 0;
const incomingCount = prompt._count?.incomingConnections || 0;
const isFlowStart = outgoingCount > 0 && incomingCount === 0;
@@ -104,7 +102,7 @@ export function PromptCard({ prompt, showPinButton = false, isPinned = false }:
const isVideo = prompt.type === "VIDEO";
const hasMediaBackground = prompt.type === "IMAGE" || isVideo || (isStructuredInput && !!prompt.mediaUrl && !isAudio);
const videoRef = useRef
(null);
- const [isVisible, setIsVisible] = useState(false);
+ const [_isVisible, setIsVisible] = useState(false);
// Autoplay video when visible in viewport
useEffect(() => {
diff --git a/src/components/prompts/prompt-connections.tsx b/src/components/prompts/prompt-connections.tsx
index fc382c6a..b802f8ad 100644
--- a/src/components/prompts/prompt-connections.tsx
+++ b/src/components/prompts/prompt-connections.tsx
@@ -794,6 +794,7 @@ function FlowGraph({ nodes, edges, currentPromptId, currentUserId, isAdmin, onNo
return (
+ {/* eslint-disable-next-line react-hooks/refs -- Container dimensions needed for tooltip positioning */}
{hoveredNode && (() => {
// Calculate position with viewport awareness
const tooltipWidth = 320;
diff --git a/src/components/prompts/prompt-form.tsx b/src/components/prompts/prompt-form.tsx
index 999220cf..e27147d7 100644
--- a/src/components/prompts/prompt-form.tsx
+++ b/src/components/prompts/prompt-form.tsx
@@ -6,7 +6,7 @@ import { useTranslations } from "next-intl";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
-import { Loader2, Upload, X, ArrowDown, Play, Image as ImageIcon, Video, Volume2, Paperclip, Search, Sparkles, BookOpen, ExternalLink, ChevronDown, Settings2 } from "lucide-react";
+import { Loader2, Upload, X, ArrowDown, Image as ImageIcon, Video, Volume2, Paperclip, Search, Sparkles, BookOpen, ExternalLink, ChevronDown, Settings2 } from "lucide-react";
import Link from "next/link";
import { VariableToolbar } from "./variable-toolbar";
import { VariableWarning } from "./variable-warning";
@@ -21,17 +21,10 @@ import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Switch } from "@/components/ui/switch";
import {
- parseSkillFiles,
- serializeSkillFiles,
- getLanguageFromFilename,
- validateFilename,
- suggestFilename,
generateSkillContentWithFrontmatter,
updateSkillFrontmatter,
validateSkillFrontmatter,
DEFAULT_SKILL_FILE,
- DEFAULT_SKILL_CONTENT,
- type SkillFile,
} from "@/lib/skill-files";
import {
Form,
@@ -61,7 +54,7 @@ import { toast } from "sonner";
import { prettifyJson } from "@/lib/format";
import { analyticsPrompt } from "@/lib/analytics";
import { getPromptUrl } from "@/lib/urls";
-import { AI_MODELS, getModelsByProvider, type PromptMCPConfig } from "@/lib/works-best-with";
+import { AI_MODELS, getModelsByProvider } from "@/lib/works-best-with";
interface MediaFieldProps {
form: ReturnType>;
@@ -1324,6 +1317,7 @@ export function PromptForm({ categories, tags, initialData, initialContributors
{/* Code output content */}
+ {/* eslint-disable-next-line react/jsx-no-comment-textnodes -- Intentional code preview text */}
// Code generated by skill...
export function handler() {'{'}
return "...";
diff --git a/src/components/prompts/related-prompts.tsx b/src/components/prompts/related-prompts.tsx
index 7ed0e48a..f98c7f04 100644
--- a/src/components/prompts/related-prompts.tsx
+++ b/src/components/prompts/related-prompts.tsx
@@ -1,6 +1,6 @@
import Link from "next/link";
import { useTranslations } from "next-intl";
-import { Sparkles, ArrowBigUp } from "lucide-react";
+import { ArrowBigUp } from "lucide-react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { getPromptUrl } from "@/lib/urls";
diff --git a/src/components/prompts/run-prompt-button.tsx b/src/components/prompts/run-prompt-button.tsx
index 2d9bdb62..63cf3e17 100644
--- a/src/components/prompts/run-prompt-button.tsx
+++ b/src/components/prompts/run-prompt-button.tsx
@@ -316,6 +316,7 @@ export function RunPromptButton({
if (url.startsWith("http://") || url.startsWith("https://")) {
window.open(url, "_blank");
} else {
+ // eslint-disable-next-line react-hooks/immutability -- Valid browser navigation for custom URL schemes
window.location.href = url;
}
analyticsPrompt.run(promptId, platform.name);
diff --git a/src/components/prompts/skill-diff-viewer.tsx b/src/components/prompts/skill-diff-viewer.tsx
index c30299d9..59040cb1 100644
--- a/src/components/prompts/skill-diff-viewer.tsx
+++ b/src/components/prompts/skill-diff-viewer.tsx
@@ -1,6 +1,6 @@
"use client";
-import { useState, useCallback, useMemo, useEffect } from "react";
+import { useState, useCallback, useMemo } from "react";
import { useTranslations } from "next-intl";
import { useTheme } from "next-themes";
import { DiffEditor } from "@monaco-editor/react";
diff --git a/src/components/prompts/skill-editor.tsx b/src/components/prompts/skill-editor.tsx
index 882cd0c9..40e16968 100644
--- a/src/components/prompts/skill-editor.tsx
+++ b/src/components/prompts/skill-editor.tsx
@@ -32,7 +32,6 @@ import {
validateFilename,
suggestFilename,
DEFAULT_SKILL_FILE,
- DEFAULT_SKILL_CONTENT,
type SkillFile,
} from "@/lib/skill-files";
@@ -357,6 +356,7 @@ export function SkillEditor({ value, onChange, className }: SkillEditorProps) {
// Only update if the value changed externally
if (value !== currentSerialized) {
+ // eslint-disable-next-line react-hooks/set-state-in-effect -- Intentional sync from external prop
setFiles(parsed);
// Ensure active file exists
if (!parsed.some((f) => f.filename === activeFile)) {
@@ -372,7 +372,7 @@ export function SkillEditor({ value, onChange, className }: SkillEditorProps) {
// File icon based on extension
const getFileIcon = (filename: string) => {
- const ext = filename.split(".").pop()?.toLowerCase();
+ const _ext = filename.split(".").pop()?.toLowerCase();
// Could add more specific icons here
return
;
};
diff --git a/src/components/prompts/structured-format-warning.tsx b/src/components/prompts/structured-format-warning.tsx
index 04274484..4a0e1ab6 100644
--- a/src/components/prompts/structured-format-warning.tsx
+++ b/src/components/prompts/structured-format-warning.tsx
@@ -2,7 +2,7 @@
import { useMemo } from "react";
import { useTranslations } from "next-intl";
-import { AlertTriangle, Braces, FileCode } from "lucide-react";
+import { Braces, FileCode } from "lucide-react";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
diff --git a/src/components/ui/code-view.tsx b/src/components/ui/code-view.tsx
index 8808c7bf..af849a06 100644
--- a/src/components/ui/code-view.tsx
+++ b/src/components/ui/code-view.tsx
@@ -101,12 +101,12 @@ export function CodeView({ content, language = "json", className, maxLines, font
)}
{viewMode === "tree" && isValidJson ? (
-
) : (
{
+ if (value === null) return "null";
+ if (Array.isArray(value)) return "array";
+ if (typeof value === "object") return "object";
+ return typeof value as "string" | "number" | "boolean";
+};
+
+// Recursive helper function - defined outside component
+const collectExpandablePaths = (value: unknown, path: string, maxDepth: number, depth: number = 0): string[] => {
+ const paths: string[] = [];
+ const type = getNodeType(value);
+
+ if ((type === "object" || type === "array") && depth < maxDepth) {
+ paths.push(path);
+
+ if (type === "array") {
+ (value as unknown[]).forEach((item, index) => {
+ paths.push(...collectExpandablePaths(item, `${path}.${index}`, maxDepth, depth + 1));
+ });
+ } else {
+ Object.entries(value as Record).forEach(([k, v]) => {
+ paths.push(...collectExpandablePaths(v, `${path}.${k}`, maxDepth, depth + 1));
+ });
+ }
+ }
+
+ return paths;
+};
+
interface JsonTreeViewProps {
data: unknown;
className?: string;
fontSize?: "xs" | "sm" | "base";
maxDepth?: number;
- onExpandAll?: React.MutableRefObject<(() => void) | undefined>;
- onCollapseAll?: React.MutableRefObject<(() => void) | undefined>;
+ onExpandAllRef?: React.MutableRefObject<(() => void) | undefined>;
+ onCollapseAllRef?: React.MutableRefObject<(() => void) | undefined>;
}
-function JsonTreeView({ data, className, fontSize = "xs", maxDepth = 10, onExpandAll, onCollapseAll }: JsonTreeViewProps) {
- const t = useTranslations("common");
+function JsonTreeView({ data, className, fontSize = "xs", maxDepth = 10, onExpandAllRef, onCollapseAllRef }: JsonTreeViewProps) {
const [expandedPaths, setExpandedPaths] = useState>(new Set(["root"]));
const togglePath = (path: string) => {
@@ -39,38 +66,9 @@ function JsonTreeView({ data, className, fontSize = "xs", maxDepth = 10, onExpan
});
};
- const getNodeType = (value: unknown): JsonNode["type"] => {
- if (value === null) return "null";
- if (Array.isArray(value)) return "array";
- if (typeof value === "object") return "object";
- return typeof value as "string" | "number" | "boolean";
- };
-
- // Collect all expandable paths recursively
- const collectExpandablePaths = useCallback((value: unknown, path: string, depth: number = 0): string[] => {
- const paths: string[] = [];
- const type = getNodeType(value);
-
- if ((type === "object" || type === "array") && depth < maxDepth) {
- paths.push(path);
-
- if (type === "array") {
- (value as unknown[]).forEach((item, index) => {
- paths.push(...collectExpandablePaths(item, `${path}.${index}`, depth + 1));
- });
- } else {
- Object.entries(value as Record).forEach(([k, v]) => {
- paths.push(...collectExpandablePaths(v, `${path}.${k}`, depth + 1));
- });
- }
- }
-
- return paths;
- }, [maxDepth, getNodeType]);
-
const allExpandablePaths = useMemo(() => {
- return collectExpandablePaths(data, "root");
- }, [data, collectExpandablePaths]);
+ return collectExpandablePaths(data, "root", maxDepth);
+ }, [data, maxDepth]);
const expandAll = useCallback(() => {
setExpandedPaths(new Set(allExpandablePaths));
@@ -109,7 +107,7 @@ function JsonTreeView({ data, className, fontSize = "xs", maxDepth = 10, onExpan
}
};
- const renderNode = (node: JsonNode, depth: number = 0, isLast: boolean = true): React.ReactNode => {
+ const renderNode = (node: JsonNode, depth: number = 0, _isLast: boolean = true): React.ReactNode => {
const { key, value, type, path } = node;
const isExpanded = expandedPaths.has(path);
const isComplex = type === "object" || type === "array";
@@ -239,13 +237,13 @@ function JsonTreeView({ data, className, fontSize = "xs", maxDepth = 10, onExpan
// Expose expand/collapse functions via useEffect
useEffect(() => {
- if (onExpandAll) {
- onExpandAll.current = expandAll;
+ if (onExpandAllRef) {
+ onExpandAllRef.current = expandAll;
}
- if (onCollapseAll) {
- onCollapseAll.current = collapseAll;
+ if (onCollapseAllRef) {
+ onCollapseAllRef.current = collapseAll;
}
- }, [expandAll, collapseAll, onExpandAll, onCollapseAll]);
+ }, [expandAll, collapseAll, onExpandAllRef, onCollapseAllRef]);
return (
void) | undefined>;
- onCollapseAll?: React.MutableRefObject<(() => void) | undefined>;
+ onExpandAllRef?: React.MutableRefObject<(() => void) | undefined>;
+ onCollapseAllRef?: React.MutableRefObject<(() => void) | undefined>;
}) {
const parsedData = useMemo(() => {
try {
@@ -294,12 +292,12 @@ export function JsonTreeViewWrapper({
}
return (
-
);
}
diff --git a/src/lib/auth/index.ts b/src/lib/auth/index.ts
index a0041979..d7d8c58f 100644
--- a/src/lib/auth/index.ts
+++ b/src/lib/auth/index.ts
@@ -3,7 +3,6 @@ import { PrismaAdapter } from "@auth/prisma-adapter";
import { db } from "@/lib/db";
import { getConfig } from "@/lib/config";
import { initializePlugins, getAuthPlugin } from "@/lib/plugins";
-import type { User } from "@prisma/client";
import type { Adapter, AdapterUser } from "next-auth/adapters";
// Initialize plugins before use
diff --git a/src/pages/api/mcp.ts b/src/pages/api/mcp.ts
index bb16d0fa..5c3c4dca 100644
--- a/src/pages/api/mcp.ts
+++ b/src/pages/api/mcp.ts
@@ -11,7 +11,7 @@ import { z } from "zod";
import { db } from "@/lib/db";
import { isValidApiKeyFormat } from "@/lib/api-key";
import { improvePrompt } from "@/lib/ai/improve-prompt";
-import { parseSkillFiles, serializeSkillFiles, DEFAULT_SKILL_FILE, DEFAULT_SKILL_CONTENT } from "@/lib/skill-files";
+import { parseSkillFiles, serializeSkillFiles, DEFAULT_SKILL_FILE } from "@/lib/skill-files";
interface AuthenticatedUser {
id: string;