chore(lint): biome cleanup across services — format, sort imports, drop unused

- discord-gateway: 74 lint errors -> 0 (format, import sorting, unused
  imports/vars, dead breath var)
- backend: format + sort imports (11 warnings left: noExplicitAny)
- frontend: remove unused imports, drop dead breathing var, fix
  useExhaustiveDependencies (scroll keyed on messages), a11y biome-ignore
  for drag surface + stopPropagation container (mouse-only gestures)
- remaining warnings are false positives: index keys on static lists,
  <img> in static export (next/image unsupported), noExplicitAny

tsc --noEmit clean on all 3 services; vitest green (60+36).
This commit is contained in:
asepharyana
2026-08-01 22:09:02 +07:00
parent 2357421841
commit 6293d588bc
123 changed files with 466 additions and 308 deletions
@@ -35,25 +35,37 @@ export function AiAnalysisPanel({
if (!status || status === "pending") {
return (
<GlassPanel dense>
<span className="text-xs text-text-secondary/50">AI analysis pending</span>
<span className="text-xs text-text-secondary/50">
AI analysis pending
</span>
</GlassPanel>
);
}
const flagsArray = typeof flags === "string" ? (flags ? JSON.parse(flags) : []) : (flags || []);
const categoriesArray = typeof categories === "string" ? (categories ? JSON.parse(categories) : []) : (categories || []);
const flagsArray =
typeof flags === "string" ? (flags ? JSON.parse(flags) : []) : flags || [];
const categoriesArray =
typeof categories === "string"
? categories
? JSON.parse(categories)
: []
: categories || [];
return (
<GlassPanel dense className="space-y-2">
<div className="flex items-center gap-2">
<span className="text-xs font-semibold tracking-wide uppercase text-text-secondary">AI Analysis</span>
<span className={cn(
"text-[10px] font-mono px-1.5 py-0.5 rounded",
status === "clean" && "bg-emerald-500/10 text-emerald-500",
status === "flagged" && "bg-accent-purple/10 text-accent-purple",
status === "warn" && "bg-accent-amber/10 text-accent-amber",
status === "error" && "bg-destructive/10 text-destructive",
)}>
<span className="text-xs font-semibold tracking-wide uppercase text-text-secondary">
AI Analysis
</span>
<span
className={cn(
"text-[10px] font-mono px-1.5 py-0.5 rounded",
status === "clean" && "bg-emerald-500/10 text-emerald-500",
status === "flagged" && "bg-accent-purple/10 text-accent-purple",
status === "warn" && "bg-accent-amber/10 text-accent-amber",
status === "error" && "bg-destructive/10 text-destructive",
)}
>
{status}
</span>
</div>
@@ -61,7 +73,14 @@ export function AiAnalysisPanel({
{severity && (
<div className="flex items-center gap-2 text-xs">
<span className="text-text-secondary/60">Severity:</span>
<span className={cn("font-mono font-medium", severityColor[severity] || "")}>{severity}</span>
<span
className={cn(
"font-mono font-medium",
severityColor[severity] || "",
)}
>
{severity}
</span>
</div>
)}
@@ -82,7 +101,12 @@ export function AiAnalysisPanel({
{flagsArray.length > 0 && (
<div className="flex flex-wrap gap-1">
{flagsArray.map((f: string) => (
<span key={f} className="text-[10px] font-mono px-1.5 py-0.5 rounded bg-destructive/10 text-destructive">{f}</span>
<span
key={f}
className="text-[10px] font-mono px-1.5 py-0.5 rounded bg-destructive/10 text-destructive"
>
{f}
</span>
))}
</div>
)}
@@ -90,7 +114,12 @@ export function AiAnalysisPanel({
{categoriesArray.length > 0 && (
<div className="flex flex-wrap gap-1">
{categoriesArray.map((c: string) => (
<span key={c} className="text-[10px] font-mono px-1.5 py-0.5 rounded bg-primary/10 text-primary">{c}</span>
<span
key={c}
className="text-[10px] font-mono px-1.5 py-0.5 rounded bg-primary/10 text-primary"
>
{c}
</span>
))}
</div>
)}
@@ -3,11 +3,13 @@
import { cn } from "@/lib/utils";
const STATUS_STYLES: Record<string, string> = {
clean: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/20",
clean:
"bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/20",
flagged: "bg-red-500/10 text-red-600 dark:text-red-400 border-red-500/20",
warn: "bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/20",
pending: "bg-muted text-muted-foreground border-border",
processing: "bg-sky-500/10 text-sky-600 dark:text-sky-400 border-sky-500/20 animate-pulse",
processing:
"bg-sky-500/10 text-sky-600 dark:text-sky-400 border-sky-500/20 animate-pulse",
error: "bg-destructive/10 text-destructive border-destructive/20",
};
@@ -12,7 +12,10 @@ export function AttachmentsGrid({ attachments }: AttachmentsGridProps) {
return (
<div className="grid grid-cols-2 gap-2">
{attachments.map((att) => (
<div key={att.id} className="glass rounded-lg overflow-hidden group relative">
<div
key={att.id}
className="glass rounded-lg overflow-hidden group relative"
>
{att.type?.startsWith("image/") ? (
<img
src={att.uploaded_url || att.discord_url}
@@ -2,10 +2,10 @@
import { ArrowLeft, MessageSquare, MessagesSquare } from "lucide-react";
import { GlassCard } from "@/components/glass/card";
import { AttachmentsGrid } from "./attachments-grid";
import { AiAnalysisPanel } from "./ai-analysis-panel";
import { getMessageChannelLabel, renderMessageContent } from "@/lib/format";
import type { AttachmentRecord, MessageRecord } from "@/lib/types";
import { AiAnalysisPanel } from "./ai-analysis-panel";
import { AttachmentsGrid } from "./attachments-grid";
interface MessageDetailViewProps {
message: MessageRecord;
@@ -13,11 +13,19 @@ interface MessageDetailViewProps {
onBack?: () => void;
}
export function MessageDetailView({ message, attachments, onBack }: MessageDetailViewProps) {
export function MessageDetailView({
message,
attachments,
onBack,
}: MessageDetailViewProps) {
return (
<GlassCard variant="base" className="h-full">
{onBack && (
<button type="button" onClick={onBack} className="flex items-center gap-1 text-xs text-text-secondary/60 hover:text-text-primary mb-3 transition-colors">
<button
type="button"
onClick={onBack}
className="flex items-center gap-1 text-xs text-text-secondary/60 hover:text-text-primary mb-3 transition-colors"
>
<ArrowLeft className="size-3" /> Back
</button>
)}
@@ -25,7 +33,9 @@ export function MessageDetailView({ message, attachments, onBack }: MessageDetai
{/* Message header */}
<div className="flex items-center gap-2 mb-3">
<MessageSquare className="size-4 text-primary" />
<span className="font-semibold text-sm text-text-primary">{message.username}</span>
<span className="font-semibold text-sm text-text-primary">
{message.username}
</span>
<span className="text-[10px] text-text-secondary/40 font-mono inline-flex items-center gap-1">
{message.thread_id && <MessagesSquare className="size-3" />}
{getMessageChannelLabel(message)}
@@ -34,7 +44,8 @@ export function MessageDetailView({ message, attachments, onBack }: MessageDetai
{/* Content */}
<div className="text-sm text-text-primary/90 leading-relaxed mb-4 whitespace-pre-wrap">
{renderMessageContent(message.content, message.metadata) || "(no text content)"}
{renderMessageContent(message.content, message.metadata) ||
"(no text content)"}
</div>
{/* Attachments */}
@@ -2,10 +2,10 @@
import { ArrowLeft, MessageSquare, MessagesSquare } from "lucide-react";
import { GlassCard } from "@/components/glass/card";
import { AttachmentsGrid } from "./attachments-grid";
import { AiAnalysisPanel } from "./ai-analysis-panel";
import { getMessageChannelLabel, renderMessageContent } from "@/lib/format";
import type { AttachmentRecord, MessageRecord } from "@/lib/types";
import { AiAnalysisPanel } from "./ai-analysis-panel";
import { AttachmentsGrid } from "./attachments-grid";
interface MessageDetailProps {
message: MessageRecord;
@@ -13,11 +13,19 @@ interface MessageDetailProps {
onBack?: () => void;
}
export function MessageDetail({ message, attachments, onBack }: MessageDetailProps) {
export function MessageDetail({
message,
attachments,
onBack,
}: MessageDetailProps) {
return (
<GlassCard variant="base" className="h-full">
{onBack && (
<button type="button" onClick={onBack} className="flex items-center gap-1 text-xs text-text-secondary/60 hover:text-text-primary mb-3 transition-colors">
<button
type="button"
onClick={onBack}
className="flex items-center gap-1 text-xs text-text-secondary/60 hover:text-text-primary mb-3 transition-colors"
>
<ArrowLeft className="size-3" /> Back
</button>
)}
@@ -25,7 +33,9 @@ export function MessageDetail({ message, attachments, onBack }: MessageDetailPro
{/* Message header */}
<div className="flex items-center gap-2 mb-3">
<MessageSquare className="size-4 text-primary" />
<span className="font-semibold text-sm text-text-primary">{message.username}</span>
<span className="font-semibold text-sm text-text-primary">
{message.username}
</span>
<span className="text-[10px] text-text-secondary/40 font-mono inline-flex items-center gap-1">
{message.thread_id && <MessagesSquare className="size-3" />}
{getMessageChannelLabel(message)}
@@ -34,7 +44,8 @@ export function MessageDetail({ message, attachments, onBack }: MessageDetailPro
{/* Content */}
<div className="text-sm text-text-primary/90 leading-relaxed mb-4 whitespace-pre-wrap">
{renderMessageContent(message.content, message.metadata) || "(no text content)"}
{renderMessageContent(message.content, message.metadata) ||
"(no text content)"}
</div>
{/* Attachments */}
@@ -1,9 +1,9 @@
"use client";
import { Loader2 } from "lucide-react";
import { MessageCard } from "./message-card";
import { Button } from "@/components/ui/button";
import type { MessageRecord } from "@/lib/types";
import { MessageCard } from "./message-card";
interface MessageListProps {
messages: MessageRecord[];
@@ -4,7 +4,6 @@ import { Search, X } from "lucide-react";
import { useEffect, useRef, useState } from "react";
import { useMessageSearch } from "@/hooks";
import { getMessageChannelLabel, renderMessageContent } from "@/lib/format";
import type { MessageRecord } from "@/lib/types";
interface SearchOverlayProps {
open: boolean;