From 3eccff42e4f884ed21ac9260dffce5c2c10bde9a Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Tue, 17 Feb 2026 23:48:52 +0100 Subject: [PATCH] chore: fix linting and build errors --- apps/web/package.json | 1 + apps/web/scripts/fix-fenced-mermaid.ts | 62 ++++++++++ apps/web/scripts/optimize-blog-post.ts | 107 ++++++++++++++++++ apps/web/src/components/ArticleMeme.tsx | 56 +++++++++ apps/web/src/components/IframeSection.tsx | 15 ++- .../src/components/Landing/AbstractLines.tsx | 2 +- .../Illustrations/ConceptAutomation.tsx | 2 +- .../Landing/Illustrations/ConceptMessy.tsx | 2 +- .../Landing/Illustrations/ConceptSystem.tsx | 2 +- .../Landing/Illustrations/ConceptTarget.tsx | 2 +- .../Landing/Illustrations/ConceptWebsite.tsx | 2 +- .../Illustrations/HeroArchitecture.tsx | 2 +- .../Illustrations/HeroMainIllustration.tsx | 2 +- .../components/Landing/Illustrations/types.ts | 2 +- .../components/Landing/ParticleNetwork.tsx | 26 ++--- apps/web/src/components/PageHeader.tsx | 1 - apps/web/src/components/SearchBar.tsx | 9 +- apps/web/src/components/Section.tsx | 1 - apps/web/src/components/ShareModal.tsx | 4 +- .../web/src/components/TextSelectionShare.tsx | 2 +- .../src/components/blog/BlogCommandBar.tsx | 6 +- .../web/src/components/blog/BlogFilterBar.tsx | 4 +- .../src/components/blog/BlogThumbnailSVG.tsx | 5 +- apps/web/src/content-engine/components.ts | 2 + apps/web/src/lib/og-helper.tsx | 2 +- apps/web/src/logic/content-provider.ts | 2 +- apps/web/src/utils/analytics/interfaces.ts | 2 +- apps/web/src/utils/cache/file-adapter.ts | 2 +- apps/web/src/utils/cache/interfaces.ts | 6 +- .../src/utils/error-tracking/interfaces.ts | 10 +- apps/web/src/utils/imgproxy-loader.ts | 4 +- eslint.config.js | 2 +- pnpm-lock.yaml | 20 +++- 33 files changed, 303 insertions(+), 66 deletions(-) create mode 100644 apps/web/scripts/fix-fenced-mermaid.ts create mode 100644 apps/web/scripts/optimize-blog-post.ts create mode 100644 apps/web/src/components/ArticleMeme.tsx diff --git a/apps/web/package.json b/apps/web/package.json index f73ffff..6a2ad55 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -30,6 +30,7 @@ }, "dependencies": { "@directus/sdk": "21.0.0", + "@emotion/is-prop-valid": "^1.4.0", "@mdx-js/loader": "^3.1.1", "@mdx-js/react": "^3.1.1", "@mintel/cloner": "^1.8.0", diff --git a/apps/web/scripts/fix-fenced-mermaid.ts b/apps/web/scripts/fix-fenced-mermaid.ts new file mode 100644 index 0000000..cb403b7 --- /dev/null +++ b/apps/web/scripts/fix-fenced-mermaid.ts @@ -0,0 +1,62 @@ + +import * as fs from 'fs'; +import * as path from 'path'; + +const MDX_DIR = path.join(process.cwd(), 'content/blog'); +const TARGET_FILE = process.argv[2] ? path.resolve(process.argv[2]) : null; + +function fixFencedMermaid(content: string): string { + // Regex to find fenced mermaid blocks + // ```mermaid + // graph TD... + // ``` + const fencedRegex = /```mermaid\n([\s\S]*?)\n```/g; + + return content.replace(fencedRegex, (match, code) => { + // Generate a random ID or use a placeholder + const id = `diagram-${Math.random().toString(36).substr(2, 9)}`; + return `
+ +${code.trim()} + +
`; + }); +} + +function processFiles() { + if (TARGET_FILE) { + console.log(`Processing single file: ${TARGET_FILE}`); + if (fs.existsSync(TARGET_FILE)) { + const content = fs.readFileSync(TARGET_FILE, 'utf8'); + const fixed = fixFencedMermaid(content); + if (content !== fixed) { + fs.writeFileSync(TARGET_FILE, fixed); + console.log(`βœ… Fixed fenced mermaid in: ${TARGET_FILE}`); + } else { + console.log(`- No changes needed.`); + } + } else { + console.error(`File not found: ${TARGET_FILE}`); + } + return; + } + + const files = fs.readdirSync(MDX_DIR).filter(f => f.endsWith('.mdx')); + let fixCount = 0; + + for (const file of files) { + const filePath = path.join(MDX_DIR, file); + const content = fs.readFileSync(filePath, 'utf8'); + const fixed = fixFencedMermaid(content); + + if (content !== fixed) { + fs.writeFileSync(filePath, fixed); + fixCount++; + console.log(`βœ… Fixed fenced mermaid in: ${file}`); + } + } + + console.log(`\nTotal fixed: ${fixCount}`); +} + +processFiles(); diff --git a/apps/web/scripts/optimize-blog-post.ts b/apps/web/scripts/optimize-blog-post.ts new file mode 100644 index 0000000..5f521f7 --- /dev/null +++ b/apps/web/scripts/optimize-blog-post.ts @@ -0,0 +1,107 @@ + +import { ContentGenerator, OptimizationOptions } from "@mintel/content-engine"; +import * as fs from "node:fs/promises"; +import * as path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +async function main() { + const OPENROUTER_KEY = process.env.OPENROUTER_KEY; + if (!OPENROUTER_KEY) { + console.error("❌ Error: OPENROUTER_KEY not found in environment."); + process.exit(1); + } + + const args = process.argv.slice(2); + let targetFile = args[0]; + + if (!targetFile) { + console.error("❌ Usage: npx tsx scripts/optimize-blog-post.ts "); + process.exit(1); + } + + // Resolve absolute path + if (!path.isAbsolute(targetFile)) { + targetFile = path.resolve(process.cwd(), targetFile); + } + + console.log(`πŸ“„ Reading file: ${targetFile}`); + let content = ""; + try { + content = await fs.readFile(targetFile, "utf8"); + } catch (err: any) { + console.error(`❌ Could not read file: ${err.message}`); + process.exit(1); + } + + // Backup original + const backupPath = `${targetFile}.bak`; + await fs.writeFile(backupPath, content); + console.log(`πŸ’Ύ Backup created at: ${backupPath}`); + + // Instantiate Generator + const generator = new ContentGenerator(OPENROUTER_KEY); + + const context = ` + Project: Mintel.me + Style: Industrial, Technical, High-Performance, "No-BS". + Author: Marc Mintel (Digital Architect). + Focus: Web Architecture, PageSpeed, Core Web Vitals, Data-Driven Design. + `; + + // Define Optimization Options based on user request ("astrein verbessert mit daten gestΓΌtzt, links zu quellen usw... mermaid, memes") + const options: OptimizationOptions = { + enhanceFacts: true, + addMemes: true, + addDiagrams: true, + projectContext: context, + availableComponents: [ + { + name: "StatsDisplay", + description: "A row of 3 clear statistic cards with values and labels.", + usageExample: ` 3s" }, + { value: "0.1s", label: "Latency", description: "Edge Network" } + ]} +/>` + }, + { + name: "ComparisonRow", + description: "A comparison component showing a negative 'Standard' vs a positive 'Mintel' approach.", + usageExample: `` + } + ] + }; + + // 1. Separate Frontmatter from Body + const fmMatch = content.match(/^---\n([\s\S]*?)\n---/); + const frontmatter = fmMatch ? fmMatch[0] : ""; + const body = fmMatch ? content.replace(frontmatter, "").trim() : content; + + console.log("πŸš€ Starting Optimization via ContentEngine..."); + const result = await generator.optimizePost(body, options); + + console.log("βœ… Optimization Complete!"); + console.log(` - Added ${result.research.length} facts`); + console.log(` - Added ${result.memes.length} meme concepts`); + console.log(` - Generated ${result.diagrams.length} diagrams`); + + // We write the content back (re-attaching frontmatter if it was there) + const finalContent = frontmatter ? `${frontmatter}\n\n${result.content}` : result.content; + + await fs.writeFile(targetFile, finalContent); + + console.log(`πŸ’Ύ Saved optimized content to: ${targetFile}`); +} + +main().catch(console.error); diff --git a/apps/web/src/components/ArticleMeme.tsx b/apps/web/src/components/ArticleMeme.tsx new file mode 100644 index 0000000..2930eae --- /dev/null +++ b/apps/web/src/components/ArticleMeme.tsx @@ -0,0 +1,56 @@ + +import React from 'react'; + +interface ArticleMemeProps { + template: string; + captions: string[]; + className?: string; +} + +export const ArticleMeme: React.FC = ({ template, captions, className = '' }) => { + return ( +
+ {/* Meme "Image" Placeholder with Industrial Styling */} +
+ {/* Decorative Grid */} +
+ + {/* Top Caption */} +
+

+ {captions[0]} +

+
+ + {/* Meme Figure/Avatar Placeholder */} +
+ πŸ€– +
+ + {/* Bottom Caption */} +
+

+ {captions[1] || ''} +

+
+
+ + {/* Meme Footer / Metadata */} +
+
+
+ AI +
+
+

Meme Template

+

{template}

+
+
+
+ +
+
+
+ ); +}; diff --git a/apps/web/src/components/IframeSection.tsx b/apps/web/src/components/IframeSection.tsx index bac698c..9d02e8d 100644 --- a/apps/web/src/components/IframeSection.tsx +++ b/apps/web/src/components/IframeSection.tsx @@ -1,6 +1,6 @@ "use client"; -/* eslint-disable no-unused-vars */ + import * as React from "react"; import { cn } from "../utils/cn"; import { ShieldCheck, ArrowLeft, ArrowRight, RefreshCw } from "lucide-react"; @@ -125,8 +125,8 @@ export const IframeSection: React.FC = ({ desktopWidth = 1200, minimal = false, perspective = false, - rotate = 0, - delay = 0, + rotate: _rotate = 0, + delay: _delay = 0, noScale = false, dynamicGlow = true, minHeight = 400, @@ -142,7 +142,6 @@ export const IframeSection: React.FC = ({ React.useState(desktopWidth); const [isLoading, setIsLoading] = React.useState(true); const [isIframeLoaded, setIsIframeLoaded] = React.useState(false); - const [isMinTimePassed, setIsMinTimePassed] = React.useState(false); const [glowColors, setGlowColors] = React.useState([ "rgba(148, 163, 184, 0.1)", "rgba(148, 163, 184, 0.1)", @@ -213,7 +212,7 @@ export const IframeSection: React.FC = ({ const isScrollable = doc.scrollHeight > doc.clientHeight + 10; setScrollState({ atTop, atBottom, isScrollable }); } - } catch (e) {} + } catch (_e) { } }, []); // Ambilight effect (sampled from iframe if same-origin) @@ -258,7 +257,7 @@ export const IframeSection: React.FC = ({ ); updateScrollState(); - } catch (e) {} + } catch (_e) { } }, [dynamicGlow, offsetY, updateScrollState]); // Height parse helper @@ -377,9 +376,9 @@ export const IframeSection: React.FC = ({ "w-full relative flex flex-col z-10", minimal ? "bg-transparent" : "bg-slate-50", !minimal && - "rounded-[2.5rem] border border-slate-200/50 shadow-[0_80px_160px_-40px_rgba(0,0,0,0.18),0_0_1px_rgba(0,0,0,0.1)]", + "rounded-[2.5rem] border border-slate-200/50 shadow-[0_80px_160px_-40px_rgba(0,0,0,0.18),0_0_1px_rgba(0,0,0,0.1)]", perspective && - "hover:scale-[1.03] hover:-translate-y-3 transition-[transform,shadow,scale] duration-1000 ease-[cubic-bezier(0.23,1,0.32,1)]", + "hover:scale-[1.03] hover:-translate-y-3 transition-[transform,shadow,scale] duration-1000 ease-[cubic-bezier(0.23,1,0.32,1)]", "overflow-hidden", )} style={chassisStyle} diff --git a/apps/web/src/components/Landing/AbstractLines.tsx b/apps/web/src/components/Landing/AbstractLines.tsx index f7eeebf..97d5ea1 100644 --- a/apps/web/src/components/Landing/AbstractLines.tsx +++ b/apps/web/src/components/Landing/AbstractLines.tsx @@ -1,6 +1,6 @@ "use client"; -/* eslint-disable no-unused-vars */ + import * as React from "react"; import { motion } from "framer-motion"; diff --git a/apps/web/src/components/Landing/Illustrations/ConceptAutomation.tsx b/apps/web/src/components/Landing/Illustrations/ConceptAutomation.tsx index 05aebc4..9a02b61 100644 --- a/apps/web/src/components/Landing/Illustrations/ConceptAutomation.tsx +++ b/apps/web/src/components/Landing/Illustrations/ConceptAutomation.tsx @@ -1,6 +1,6 @@ "use client"; -/* eslint-disable no-unused-vars */ + import * as React from "react"; import { motion } from "framer-motion"; diff --git a/apps/web/src/components/Landing/Illustrations/ConceptMessy.tsx b/apps/web/src/components/Landing/Illustrations/ConceptMessy.tsx index 01593ef..a7724ed 100644 --- a/apps/web/src/components/Landing/Illustrations/ConceptMessy.tsx +++ b/apps/web/src/components/Landing/Illustrations/ConceptMessy.tsx @@ -1,6 +1,6 @@ "use client"; -/* eslint-disable no-unused-vars */ + import * as React from "react"; import { motion } from "framer-motion"; diff --git a/apps/web/src/components/Landing/Illustrations/ConceptSystem.tsx b/apps/web/src/components/Landing/Illustrations/ConceptSystem.tsx index 2f06410..e8368c2 100644 --- a/apps/web/src/components/Landing/Illustrations/ConceptSystem.tsx +++ b/apps/web/src/components/Landing/Illustrations/ConceptSystem.tsx @@ -1,6 +1,6 @@ "use client"; -/* eslint-disable no-unused-vars */ + import * as React from "react"; import { motion } from "framer-motion"; diff --git a/apps/web/src/components/Landing/Illustrations/ConceptTarget.tsx b/apps/web/src/components/Landing/Illustrations/ConceptTarget.tsx index 9fcad44..52ae39f 100644 --- a/apps/web/src/components/Landing/Illustrations/ConceptTarget.tsx +++ b/apps/web/src/components/Landing/Illustrations/ConceptTarget.tsx @@ -1,6 +1,6 @@ "use client"; -/* eslint-disable no-unused-vars */ + import * as React from "react"; import { motion } from "framer-motion"; diff --git a/apps/web/src/components/Landing/Illustrations/ConceptWebsite.tsx b/apps/web/src/components/Landing/Illustrations/ConceptWebsite.tsx index 53ae674..cdd9054 100644 --- a/apps/web/src/components/Landing/Illustrations/ConceptWebsite.tsx +++ b/apps/web/src/components/Landing/Illustrations/ConceptWebsite.tsx @@ -1,6 +1,6 @@ "use client"; -/* eslint-disable no-unused-vars */ + import * as React from "react"; import { motion } from "framer-motion"; diff --git a/apps/web/src/components/Landing/Illustrations/HeroArchitecture.tsx b/apps/web/src/components/Landing/Illustrations/HeroArchitecture.tsx index c39953d..3e0cac1 100644 --- a/apps/web/src/components/Landing/Illustrations/HeroArchitecture.tsx +++ b/apps/web/src/components/Landing/Illustrations/HeroArchitecture.tsx @@ -1,6 +1,6 @@ "use client"; -/* eslint-disable no-unused-vars */ + import * as React from "react"; import { motion } from "framer-motion"; diff --git a/apps/web/src/components/Landing/Illustrations/HeroMainIllustration.tsx b/apps/web/src/components/Landing/Illustrations/HeroMainIllustration.tsx index ab74f88..8a48fb8 100644 --- a/apps/web/src/components/Landing/Illustrations/HeroMainIllustration.tsx +++ b/apps/web/src/components/Landing/Illustrations/HeroMainIllustration.tsx @@ -1,6 +1,6 @@ "use client"; -/* eslint-disable no-unused-vars */ + import * as React from "react"; import { motion } from "framer-motion"; diff --git a/apps/web/src/components/Landing/Illustrations/types.ts b/apps/web/src/components/Landing/Illustrations/types.ts index 3a29d95..405cfdd 100644 --- a/apps/web/src/components/Landing/Illustrations/types.ts +++ b/apps/web/src/components/Landing/Illustrations/types.ts @@ -1,7 +1,7 @@ "use client"; /* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable no-unused-vars */ + import * as React from "react"; export interface IllustrationProps { diff --git a/apps/web/src/components/Landing/ParticleNetwork.tsx b/apps/web/src/components/Landing/ParticleNetwork.tsx index 64c6e98..d8a88fc 100644 --- a/apps/web/src/components/Landing/ParticleNetwork.tsx +++ b/apps/web/src/components/Landing/ParticleNetwork.tsx @@ -45,15 +45,15 @@ export const ParticleNetwork: React.FC = ({ className = '' const initParticles = useCallback((width: number, height: number) => { const particles: Particle[] = []; const count = config.particleCount; - + // Create particles primarily in the side gutters for (let i = 0; i < count; i++) { const isLeft = Math.random() > 0.5; // Random position within the gutter (0-25% or 75-100%) - const gutterX = isLeft - ? Math.random() * (width * config.gutterWidth) + const gutterX = isLeft + ? Math.random() * (width * config.gutterWidth) : width - (Math.random() * (width * config.gutterWidth)); - + // Add some occasional strays near the content but not IN it const x = gutterX; const y = Math.random() * height; @@ -89,7 +89,7 @@ export const ParticleNetwork: React.FC = ({ className = '' ctx.clearRect(0, 0, width, height); // Update and draw particles - particles.forEach((p, i) => { + particles.forEach((p, _i) => { // 1. Movement // Apply downward flow p.y += p.vy; @@ -111,7 +111,7 @@ export const ParticleNetwork: React.FC = ({ className = '' const angle = Math.atan2(dy, dx); const pushX = Math.cos(angle) * force * 2; const pushY = Math.sin(angle) * force * 2; - + p.x += pushX; p.y += pushY; } @@ -145,17 +145,17 @@ export const ParticleNetwork: React.FC = ({ className = '' // Draw Connections // We only connect particles that are close enough ctx.lineWidth = 0.5; - + for (let i = 0; i < particles.length; i++) { const p1 = particles[i]; // Optimization: only check particles after this one for (let j = i + 1; j < particles.length; j++) { const p2 = particles[j]; - + // Quick check for distance const dx = p1.x - p2.x; const dy = p1.y - p2.y; - + // Optimization: skip sqrt if obviously too far if (Math.abs(dx) > config.connectionDistance || Math.abs(dy) > config.connectionDistance) continue; @@ -164,7 +164,7 @@ export const ParticleNetwork: React.FC = ({ className = '' if (dist < config.connectionDistance) { // Calculate opacity based on distance const opacity = (1 - dist / config.connectionDistance) * 0.3; - + ctx.beginPath(); ctx.moveTo(p1.x, p1.y); ctx.lineTo(p2.x, p2.y); @@ -185,12 +185,12 @@ export const ParticleNetwork: React.FC = ({ className = '' const rect = container.getBoundingClientRect(); const dpr = window.devicePixelRatio || 1; - + canvas.width = rect.width * dpr; canvas.height = rect.height * dpr; canvas.style.width = `${rect.width}px`; canvas.style.height = `${rect.height}px`; - + const ctx = canvas.getContext('2d'); if (ctx) { ctx.scale(dpr, dpr); @@ -209,7 +209,7 @@ export const ParticleNetwork: React.FC = ({ className = '' const handleMouseMove = useCallback((e: MouseEvent) => { const container = containerRef.current; if (!container) return; - + const rect = container.getBoundingClientRect(); mouseRef.current = { x: e.clientX - rect.left, diff --git a/apps/web/src/components/PageHeader.tsx b/apps/web/src/components/PageHeader.tsx index d165e2f..cd729d3 100644 --- a/apps/web/src/components/PageHeader.tsx +++ b/apps/web/src/components/PageHeader.tsx @@ -2,7 +2,6 @@ import * as React from "react"; import { Reveal } from "./Reveal"; -import { H1, LeadText } from "./Typography"; import { cn } from "../utils/cn"; import { Share2, Clock, Calendar } from "lucide-react"; import { ShareModal } from "./ShareModal"; diff --git a/apps/web/src/components/SearchBar.tsx b/apps/web/src/components/SearchBar.tsx index ad4956b..002c891 100644 --- a/apps/web/src/components/SearchBar.tsx +++ b/apps/web/src/components/SearchBar.tsx @@ -54,11 +54,10 @@ export const SearchBar: React.FC = ({ type="text" placeholder="Suchen..." value={value} - className={`w-full max-w-full px-8 py-4 font-bold text-slate-900 bg-white rounded-2xl border transition-all focus:outline-none placeholder:text-slate-300 ${ - size === "large" - ? "text-2xl md:text-4xl py-6 rounded-3xl" - : "text-lg" - } ${isFocused ? "border-slate-400" : "border-slate-200"}`} + className={`w-full max-w-full px-8 py-4 font-bold text-slate-900 bg-white rounded-2xl border transition-all focus:outline-none placeholder:text-slate-300 ${size === "large" + ? "text-2xl md:text-4xl py-6 rounded-3xl" + : "text-lg" + } ${isFocused ? "border-slate-400" : "border-slate-200"}`} onChange={handleInput} onKeyDown={handleKeyDown} onFocus={() => setIsFocused(true)} diff --git a/apps/web/src/components/Section.tsx b/apps/web/src/components/Section.tsx index 467c354..a398609 100644 --- a/apps/web/src/components/Section.tsx +++ b/apps/web/src/components/Section.tsx @@ -2,7 +2,6 @@ import * as React from "react"; import { Reveal } from "./Reveal"; import { Label } from "./Typography"; import { cn } from "../utils/cn"; -import { GlitchText } from "./GlitchText"; interface SectionProps { number?: string; diff --git a/apps/web/src/components/ShareModal.tsx b/apps/web/src/components/ShareModal.tsx index b76a9bd..4464624 100644 --- a/apps/web/src/components/ShareModal.tsx +++ b/apps/web/src/components/ShareModal.tsx @@ -123,8 +123,8 @@ export function ShareModal({ } await navigator.share(shareData); - } catch (e) { - console.error("Share failed", e); + } catch (_e) { + console.error("Share failed", _e); } } }; diff --git a/apps/web/src/components/TextSelectionShare.tsx b/apps/web/src/components/TextSelectionShare.tsx index e3b9e48..bc303d2 100644 --- a/apps/web/src/components/TextSelectionShare.tsx +++ b/apps/web/src/components/TextSelectionShare.tsx @@ -1,7 +1,7 @@ "use client"; import React, { useEffect, useState, useRef } from "react"; -import { Share2, Copy, Check } from "lucide-react"; +import { Copy, Check } from "lucide-react"; import { useAnalytics } from "./analytics/useAnalytics"; import { motion, AnimatePresence } from "framer-motion"; diff --git a/apps/web/src/components/blog/BlogCommandBar.tsx b/apps/web/src/components/blog/BlogCommandBar.tsx index f6e0098..89c2b75 100644 --- a/apps/web/src/components/blog/BlogCommandBar.tsx +++ b/apps/web/src/components/blog/BlogCommandBar.tsx @@ -1,6 +1,4 @@ "use client"; -/* eslint-disable react/prop-types */ - import * as React from "react"; import { motion, AnimatePresence } from "framer-motion"; import { cn } from "../../utils/cn"; @@ -8,10 +6,10 @@ import { Search } from "lucide-react"; interface BlogCommandBarProps { searchQuery: string; - onSearchChange: (_value: string) => void; + onSearchChange: (value: string) => void; tags: string[]; activeTags: string[]; - onTagToggle: (_tag: string) => void; + onTagToggle: (tag: string) => void; className?: string; } diff --git a/apps/web/src/components/blog/BlogFilterBar.tsx b/apps/web/src/components/blog/BlogFilterBar.tsx index a1be102..b5f34c9 100644 --- a/apps/web/src/components/blog/BlogFilterBar.tsx +++ b/apps/web/src/components/blog/BlogFilterBar.tsx @@ -6,10 +6,10 @@ import { cn } from "../../utils/cn"; interface BlogFilterBarProps { searchQuery: string; - onSearchChange: (value: string) => void; + onSearchChange: (_value: string) => void; tags: string[]; activeTags: string[]; - onTagToggle: (tag: string) => void; + onTagToggle: (_tag: string) => void; className?: string; } diff --git a/apps/web/src/components/blog/BlogThumbnailSVG.tsx b/apps/web/src/components/blog/BlogThumbnailSVG.tsx index 1d1403e..a2010f3 100644 --- a/apps/web/src/components/blog/BlogThumbnailSVG.tsx +++ b/apps/web/src/components/blog/BlogThumbnailSVG.tsx @@ -1,7 +1,6 @@ /* eslint-disable react/prop-types */ import type { ThumbnailIcon, - BlogThumbnailConfig, } from "./blogThumbnails"; import { blogThumbnails } from "./blogThumbnails"; @@ -602,7 +601,7 @@ function renderGear(cx: number, cy: number, accent: string) { const angle = (i * 2 * Math.PI) / teeth; const a1 = angle - toothWidth; const a2 = angle + toothWidth; - const midAngle = (a1 + a2) / 2; + const _midAngle = (a1 + a2) / 2; if (i === 0) { d += `M ${cx + outerR * Math.cos(a1)} ${cy + outerR * Math.sin(a1)} `; @@ -954,7 +953,7 @@ function renderSync(cx: number, cy: number, accent: string) { const iconRenderers: Record< ThumbnailIcon, - (cx: number, cy: number, accent: string) => React.ReactNode + (_cx: number, _cy: number, _accent: string) => React.ReactNode > = { gauge: renderGauge, bottleneck: renderBottleneck, diff --git a/apps/web/src/content-engine/components.ts b/apps/web/src/content-engine/components.ts index 5e63495..d749fdd 100644 --- a/apps/web/src/content-engine/components.ts +++ b/apps/web/src/content-engine/components.ts @@ -13,6 +13,7 @@ import { DiagramGantt } from '../components/DiagramGantt'; import { DiagramPie } from '../components/DiagramPie'; import { DiagramSequence } from '../components/DiagramSequence'; import { IconList, IconListItem } from '../components/IconList'; +import { ArticleMeme } from '../components/ArticleMeme'; import { Section } from '../components/Section'; import { Reveal } from '../components/Reveal'; @@ -36,6 +37,7 @@ export const mdxComponents = { DiagramSequence, IconList, IconListItem, + ArticleMeme, Section, Reveal }; diff --git a/apps/web/src/lib/og-helper.tsx b/apps/web/src/lib/og-helper.tsx index 2bd1ab7..cdfb512 100644 --- a/apps/web/src/lib/og-helper.tsx +++ b/apps/web/src/lib/og-helper.tsx @@ -25,7 +25,7 @@ export async function getOgFonts() { regularFontPath = r; break; } - } catch (e) { + } catch (_e) { continue; } } diff --git a/apps/web/src/logic/content-provider.ts b/apps/web/src/logic/content-provider.ts index f4bb041..e1ed57f 100644 --- a/apps/web/src/logic/content-provider.ts +++ b/apps/web/src/logic/content-provider.ts @@ -1,7 +1,7 @@ import fs from "fs"; import path from "path"; -/* eslint-disable no-unused-vars */ + const DOCS_DIR = path.join(process.cwd(), "docs"); diff --git a/apps/web/src/utils/analytics/interfaces.ts b/apps/web/src/utils/analytics/interfaces.ts index 2ecd23f..72adbb1 100644 --- a/apps/web/src/utils/analytics/interfaces.ts +++ b/apps/web/src/utils/analytics/interfaces.ts @@ -2,7 +2,7 @@ * Analytics interfaces - decoupled contracts */ -/* eslint-disable no-unused-vars */ + export interface AnalyticsEvent { name: string; diff --git a/apps/web/src/utils/cache/file-adapter.ts b/apps/web/src/utils/cache/file-adapter.ts index eec1397..a7c554d 100644 --- a/apps/web/src/utils/cache/file-adapter.ts +++ b/apps/web/src/utils/cache/file-adapter.ts @@ -4,7 +4,7 @@ import * as path from "node:path"; import { existsSync } from "node:fs"; import * as crypto from "node:crypto"; -/* eslint-disable no-unused-vars */ + export class FileCacheAdapter implements CacheAdapter { private cacheDir: string; diff --git a/apps/web/src/utils/cache/interfaces.ts b/apps/web/src/utils/cache/interfaces.ts index 5bc73fb..b474bb2 100644 --- a/apps/web/src/utils/cache/interfaces.ts +++ b/apps/web/src/utils/cache/interfaces.ts @@ -3,9 +3,9 @@ */ export interface CacheAdapter { - get(key: string): Promise; - set(key: string, value: T, ttl?: number): Promise; - del(key: string): Promise; + get(_key: string): Promise; + set(_key: string, _value: T, _ttl?: number): Promise; + del(_key: string): Promise; clear(): Promise; } diff --git a/apps/web/src/utils/error-tracking/interfaces.ts b/apps/web/src/utils/error-tracking/interfaces.ts index b0030e6..58926cb 100644 --- a/apps/web/src/utils/error-tracking/interfaces.ts +++ b/apps/web/src/utils/error-tracking/interfaces.ts @@ -14,11 +14,11 @@ export interface ErrorContext { } export interface ErrorTrackingAdapter { - captureException(error: any, context?: ErrorContext): void; - captureMessage(message: string, context?: ErrorContext): void; - setUser(user: ErrorContext['user']): void; - setTag(key: string, value: string): void; - setExtra(key: string, value: any): void; + captureException(_error: any, _context?: ErrorContext): void; + captureMessage(_message: string, _context?: ErrorContext): void; + setUser(_user: ErrorContext['user']): void; + setTag(_key: string, _value: string): void; + setExtra(_key: string, _value: any): void; } export interface ErrorTrackingConfig { diff --git a/apps/web/src/utils/imgproxy-loader.ts b/apps/web/src/utils/imgproxy-loader.ts index 643871a..f05107e 100644 --- a/apps/web/src/utils/imgproxy-loader.ts +++ b/apps/web/src/utils/imgproxy-loader.ts @@ -11,11 +11,11 @@ import { getImgproxyUrl } from "./imgproxy"; export default function imgproxyLoader({ src, width, - quality, + _quality, }: { src: string; width: number; - quality?: number; + _quality?: number; }) { // We use the width provided by Next.js for responsive images // Height is set to 0 to maintain aspect ratio diff --git a/eslint.config.js b/eslint.config.js index e7e57fd..607513a 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -56,7 +56,7 @@ export default tseslint.config( "typescript-eslint": tseslint.plugin, }, rules: { - "no-unused-vars": "warn", + "no-unused-vars": "off", "@typescript-eslint/no-unused-vars": [ "warn", { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7435355..9ba531e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,6 +98,9 @@ importers: '@directus/sdk': specifier: 21.0.0 version: 21.0.0 + '@emotion/is-prop-valid': + specifier: ^1.4.0 + version: 1.4.0 '@mdx-js/loader': specifier: ^3.1.1 version: 3.1.1(webpack@5.96.1(esbuild@0.27.3)) @@ -190,7 +193,7 @@ importers: version: 0.27.3 framer-motion: specifier: ^12.29.2 - version: 12.31.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 12.31.2(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) ioredis: specifier: ^5.9.1 version: 5.9.2 @@ -858,6 +861,12 @@ packages: '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + '@emotion/is-prop-valid@1.4.0': + resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + '@esbuild-plugins/node-resolve@0.2.2': resolution: {integrity: sha512-+t5FdX3ATQlb53UFDBRb4nqjYBz492bIrnVWvpQHpzZlu9BQL5HasMZhqc409ygUwOWCXZhrWr6NyZ6T6Y+cxw==} peerDependencies: @@ -9004,6 +9013,12 @@ snapshots: tslib: 2.8.1 optional: true + '@emotion/is-prop-valid@1.4.0': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.9.0': {} + '@esbuild-plugins/node-resolve@0.2.2(esbuild@0.27.3)': dependencies: '@types/resolve': 1.20.6 @@ -13784,12 +13799,13 @@ snapshots: fraction.js@5.3.4: {} - framer-motion@12.31.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + framer-motion@12.31.2(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: motion-dom: 12.31.2 motion-utils: 12.29.2 tslib: 2.8.1 optionalDependencies: + '@emotion/is-prop-valid': 1.4.0 react: 19.2.4 react-dom: 19.2.4(react@19.2.4)