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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user