chore: fix linting and build errors
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 5s
Build & Deploy / 🧪 QA (push) Failing after 1m31s
Build & Deploy / 🏗️ Build (push) Failing after 3m51s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 5s
Build & Deploy / 🧪 QA (push) Failing after 1m31s
Build & Deploy / 🏗️ Build (push) Failing after 3m51s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
This commit is contained in:
@@ -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",
|
||||
|
||||
62
apps/web/scripts/fix-fenced-mermaid.ts
Normal file
62
apps/web/scripts/fix-fenced-mermaid.ts
Normal file
@@ -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 `<div className="my-12">
|
||||
<Mermaid id="${id}" title="Generated Diagram" showShare={true}>
|
||||
${code.trim()}
|
||||
</Mermaid>
|
||||
</div>`;
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
107
apps/web/scripts/optimize-blog-post.ts
Normal file
107
apps/web/scripts/optimize-blog-post.ts
Normal file
@@ -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 <path/to/post.mdx>");
|
||||
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: `<StatsDisplay
|
||||
items={[
|
||||
{ value: "-20%", label: "Conversion", description: "Source: Google" },
|
||||
{ value: "53%", label: "Bounce Rate", description: "Mobile users > 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: `<ComparisonRow
|
||||
description="Architecture Comparison"
|
||||
negativeLabel="Legacy CMS"
|
||||
negativeText="Slow database queries, vulnerable plugins."
|
||||
positiveLabel="Mintel Stack"
|
||||
positiveText="Static generation, perfect security."
|
||||
/>`
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// 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);
|
||||
56
apps/web/src/components/ArticleMeme.tsx
Normal file
56
apps/web/src/components/ArticleMeme.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
interface ArticleMemeProps {
|
||||
template: string;
|
||||
captions: string[];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ArticleMeme: React.FC<ArticleMemeProps> = ({ template, captions, className = '' }) => {
|
||||
return (
|
||||
<div className={`not-prose relative overflow-hidden group rounded-2xl border border-slate-200 bg-white shadow-xl max-w-2xl mx-auto my-12 ${className}`}>
|
||||
{/* Meme "Image" Placeholder with Industrial Styling */}
|
||||
<div className="aspect-video bg-slate-900 flex flex-col items-center justify-between p-8 text-center relative overflow-hidden">
|
||||
{/* Decorative Grid */}
|
||||
<div className="absolute inset-0 opacity-10 pointer-events-none"
|
||||
style={{ backgroundImage: 'radial-gradient(#ffffff 1px, transparent 1px)', backgroundSize: '20px 20px' }} />
|
||||
|
||||
{/* Top Caption */}
|
||||
<div className="relative z-10 w-full">
|
||||
<h4 className="text-white text-3xl md:text-4xl font-black uppercase tracking-tighter leading-none [text-shadow:_2px_2px_0_rgb(0_0_0_/_80%)]">
|
||||
{captions[0]}
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
{/* Meme Figure/Avatar Placeholder */}
|
||||
<div className="relative z-10 w-32 h-32 md:w-40 md:h-40 bg-gradient-to-br from-indigo-500 to-purple-600 rounded-full flex items-center justify-center border-4 border-white transform group-hover:scale-105 transition-transform duration-500 shadow-2xl">
|
||||
<span className="text-5xl">🤖</span>
|
||||
</div>
|
||||
|
||||
{/* Bottom Caption */}
|
||||
<div className="relative z-10 w-full">
|
||||
<h4 className="text-white text-2xl md:text-3xl font-bold uppercase tracking-tight leading-none [text-shadow:_1px_1px_0_rgb(0_0_0_/_80%)]">
|
||||
{captions[1] || ''}
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Meme Footer / Metadata */}
|
||||
<div className="p-4 bg-slate-50 flex items-center justify-between border-t border-slate-100">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 rounded bg-slate-200 flex items-center justify-center text-[10px] font-mono text-slate-500 uppercase tracking-widest font-bold">
|
||||
AI
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[10px] font-bold text-slate-400 uppercase tracking-widest leading-none">Meme Template</p>
|
||||
<p className="text-xs font-bold text-slate-900 mt-1 uppercase">{template}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-slate-300">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 3h6v6" /><path d="M10 14 21 3" /><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -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<IframeSectionProps> = ({
|
||||
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<IframeSectionProps> = ({
|
||||
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<string[]>([
|
||||
"rgba(148, 163, 184, 0.1)",
|
||||
"rgba(148, 163, 184, 0.1)",
|
||||
@@ -213,7 +212,7 @@ export const IframeSection: React.FC<IframeSectionProps> = ({
|
||||
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<IframeSectionProps> = ({
|
||||
);
|
||||
|
||||
updateScrollState();
|
||||
} catch (e) {}
|
||||
} catch (_e) { }
|
||||
}, [dynamicGlow, offsetY, updateScrollState]);
|
||||
|
||||
// Height parse helper
|
||||
@@ -377,9 +376,9 @@ export const IframeSection: React.FC<IframeSectionProps> = ({
|
||||
"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}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
import * as React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
|
||||
import * as React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
|
||||
import * as React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
|
||||
import * as React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
|
||||
import * as React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
|
||||
import * as React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
|
||||
import * as React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
|
||||
import * as React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -45,15 +45,15 @@ export const ParticleNetwork: React.FC<ParticleNetworkProps> = ({ 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<ParticleNetworkProps> = ({ 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<ParticleNetworkProps> = ({ 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<ParticleNetworkProps> = ({ 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<ParticleNetworkProps> = ({ 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<ParticleNetworkProps> = ({ 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<ParticleNetworkProps> = ({ className = ''
|
||||
const handleMouseMove = useCallback((e: MouseEvent) => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
|
||||
|
||||
const rect = container.getBoundingClientRect();
|
||||
mouseRef.current = {
|
||||
x: e.clientX - rect.left,
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -54,11 +54,10 @@ export const SearchBar: React.FC<SearchBarProps> = ({
|
||||
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)}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export async function getOgFonts() {
|
||||
regularFontPath = r;
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Analytics interfaces - decoupled contracts
|
||||
*/
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
|
||||
export interface AnalyticsEvent {
|
||||
name: string;
|
||||
|
||||
2
apps/web/src/utils/cache/file-adapter.ts
vendored
2
apps/web/src/utils/cache/file-adapter.ts
vendored
@@ -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;
|
||||
|
||||
6
apps/web/src/utils/cache/interfaces.ts
vendored
6
apps/web/src/utils/cache/interfaces.ts
vendored
@@ -3,9 +3,9 @@
|
||||
*/
|
||||
|
||||
export interface CacheAdapter {
|
||||
get<T>(key: string): Promise<T | null>;
|
||||
set<T>(key: string, value: T, ttl?: number): Promise<void>;
|
||||
del(key: string): Promise<void>;
|
||||
get<T>(_key: string): Promise<T | null>;
|
||||
set<T>(_key: string, _value: T, _ttl?: number): Promise<void>;
|
||||
del(_key: string): Promise<void>;
|
||||
clear(): Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user