chore: linting

This commit is contained in:
DESKTOP-RTLN3BA\$punk 2026-07-13 16:30:37 -07:00
parent 1131da5ed7
commit e8b3692b54
20 changed files with 74 additions and 82 deletions

View file

@ -3,7 +3,6 @@
import { Check, Copy, Download } from "lucide-react";
import { useMemo, useState } from "react";
import { Button } from "@/components/ui/button";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import {
Table,
TableBody,
@ -12,6 +11,7 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { downloadCsv, rowsToCsv } from "@/lib/playground/csv";
const MAX_TABLE_ROWS = 200;
@ -117,13 +117,7 @@ export function OutputViewer({ data, filenameBase }: { data: unknown; filenameBa
</Tabs>
<div className="flex items-center gap-1">
{items && items.length > 0 && (
<Button
type="button"
variant="ghost"
size="sm"
onClick={exportCsv}
className="gap-1.5"
>
<Button type="button" variant="ghost" size="sm" onClick={exportCsv} className="gap-1.5">
<Download className="h-3.5 w-3.5" />
Export CSV
</Button>

View file

@ -25,7 +25,8 @@ export function PlaygroundIndex({ workspaceId }: { workspaceId: number }) {
<Info />
<AlertDescription>
<p>
Manually run SurfSense's platform-native APIs and inspect their output. To use these APIs outside SurfSense,{" "}
Manually run SurfSense's platform-native APIs and inspect their output. To use these
APIs outside SurfSense,{" "}
<Link
href={`${base}/api-keys`}
className="font-medium text-foreground underline-offset-4 hover:underline"

View file

@ -35,13 +35,7 @@ function parseJsonl(text: string | null): { items: unknown[]; total: number } {
return { items, total: lines.length };
}
export function RunDetail({
workspaceId,
runId,
}: {
workspaceId: number;
runId: string;
}) {
export function RunDetail({ workspaceId, runId }: { workspaceId: number; runId: string }) {
const { data: run, isLoading, error } = useScraperRun(workspaceId, runId);
const parsed = useMemo(() => parseJsonl(run?.output_text ?? null), [run?.output_text]);

View file

@ -9,7 +9,9 @@ import { formatDuration } from "@/lib/playground/format";
function eventLabel(event: ScraperRunEvent): string {
const base =
event.message ||
(event.phase ? event.phase.replace(/_/g, " ").replace(/^\w/, (c) => c.toUpperCase()) : "Working");
(event.phase
? event.phase.replace(/_/g, " ").replace(/^\w/, (c) => c.toUpperCase())
: "Working");
if (event.current !== undefined && event.current !== null) {
const counter =
event.total !== undefined && event.total !== null

View file

@ -14,7 +14,10 @@ export function RunStatusBadge({ status }: { status: string }) {
}
if (normalized === "success") {
return (
<Badge variant="secondary" className="bg-emerald-500/15 text-emerald-600 dark:text-emerald-400">
<Badge
variant="secondary"
className="bg-emerald-500/15 text-emerald-600 dark:text-emerald-400"
>
Success
</Badge>
);

View file

@ -71,7 +71,8 @@ export function RunsTable({ workspaceId }: { workspaceId: number }) {
<Alert>
<Info />
<AlertDescription>
View all API runs for this workspace, including runs from the playground, API keys, and agents.
View all API runs for this workspace, including runs from the playground, API keys, and
agents.
</AlertDescription>
</Alert>

View file

@ -43,12 +43,7 @@ function FieldControl({
if (field.kind === "boolean") {
return (
<Switch
id={id}
checked={Boolean(value)}
onCheckedChange={onChange}
disabled={disabled}
/>
<Switch id={id} checked={Boolean(value)} onCheckedChange={onChange} disabled={disabled} />
);
}
@ -141,9 +136,7 @@ function FieldRow({
{field.required ? "required" : "optional"}
</Badge>
</div>
{field.description && (
<p className="text-xs text-muted-foreground">{field.description}</p>
)}
{field.description && <p className="text-xs text-muted-foreground">{field.description}</p>}
<FieldControl
field={field}
value={value}
@ -156,13 +149,7 @@ function FieldRow({
);
}
export function SchemaForm({
fields,
values,
onChange,
disabled,
fieldErrors,
}: SchemaFormProps) {
export function SchemaForm({ fields, values, onChange, disabled, fieldErrors }: SchemaFormProps) {
const [showAdvanced, setShowAdvanced] = useState(false);
const { primary, advanced } = useMemo(() => {

View file

@ -1018,10 +1018,7 @@ const ConnectedScraperIcons: FC<{ workspaceId: number }> = ({ workspaceId }) =>
return (
<Tooltip key={platform.id}>
<TooltipTrigger asChild>
<Avatar
className="size-5"
style={{ zIndex: platforms.length - i }}
>
<Avatar className="size-5" style={{ zIndex: platforms.length - i }}>
<AvatarFallback className="bg-popover text-[10px]">
<Icon className="size-3" />
</AvatarFallback>

View file

@ -27,7 +27,8 @@ export type HeroChatDemoScript = {
type Stage = "typing" | "steps" | "answer" | "done";
const PLACEHOLDER = "Track competitors, scrape platforms, automate briefs. Use / for prompts, @ for docs";
const PLACEHOLDER =
"Track competitors, scrape platforms, automate briefs. Use / for prompts, @ for docs";
/** Blinking caret for the typewriter (overlay only, never inside the real input). */
function Caret() {

View file

@ -717,10 +717,10 @@ export function HeroSection() {
"relative mb-8 max-w-2xl text-left text-sm text-neutral-600 antialiased sm:text-base md:text-lg dark:text-neutral-400"
)}
>
SurfSense is an open-source competitive intelligence platform, like NotebookLM but with
live scraping connectors. Your AI agents monitor competitors, track rankings, and listen
to your market with live data from platforms like Reddit, YouTube, Instagram, TikTok,
Google Maps, Google Search, and the open web.
SurfSense is an open-source competitive intelligence platform, like NotebookLM but
with live scraping connectors. Your AI agents monitor competitors, track rankings, and
listen to your market with live data from platforms like Reddit, YouTube, Instagram,
TikTok, Google Maps, Google Search, and the open web.
</p>
<div className="relative mb-4 flex w-full flex-col justify-center gap-y-2 sm:flex-row sm:justify-start sm:space-y-0 sm:space-x-4">

View file

@ -324,9 +324,7 @@ export function NotificationsDropdown({
)}
>
<span>{tab.label}</span>
<span
className="inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-muted px-1.5 text-[11px] font-semibold text-muted-foreground"
>
<span className="inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-muted px-1.5 text-[11px] font-semibold text-muted-foreground">
{formatNotificationCount(tab.count)}
</span>
</button>

View file

@ -198,8 +198,8 @@ export function AutoReloadSettings() {
<AlertTriangle className="h-4 w-4" />
<AlertTitle>Last top-up failed</AlertTitle>
<AlertDescription>
Your saved card was declined and top-ups were turned off. Update your card and
re-enable top-ups below.
Your saved card was declined and top-ups were turned off. Update your card and re-enable
top-ups below.
</AlertDescription>
</Alert>
)}
@ -290,7 +290,11 @@ export function AutoReloadSettings() {
</div>
<div className="flex justify-end">
<Button className="w-full sm:w-auto" onClick={handleSave} disabled={saveMutation.isPending}>
<Button
className="w-full sm:w-auto"
onClick={handleSave}
disabled={saveMutation.isPending}
>
{saveMutation.isPending ? (
<>
<Spinner size="xs" />

View file

@ -59,7 +59,12 @@ export function WorkspaceApiAccessControl({
if (isLoading) {
return (
<div className={cn("flex flex-col gap-3 md:flex-row md:items-center md:justify-between", className)}>
<div
className={cn(
"flex flex-col gap-3 md:flex-row md:items-center md:justify-between",
className
)}
>
<div className="space-y-2">
<Skeleton className="h-4 w-32" />
<Skeleton className="h-3 w-56" />
@ -71,7 +76,12 @@ export function WorkspaceApiAccessControl({
if (isError) {
return (
<div className={cn("flex flex-col gap-3 md:flex-row md:items-center md:justify-between", className)}>
<div
className={cn(
"flex flex-col gap-3 md:flex-row md:items-center md:justify-between",
className
)}
>
<div className="space-y-1">
<Label>API key access</Label>
<p className="text-xs text-destructive">Failed to load workspace API access.</p>
@ -84,7 +94,12 @@ export function WorkspaceApiAccessControl({
}
return (
<div className={cn("flex flex-col gap-3 md:flex-row md:items-center md:justify-between", className)}>
<div
className={cn(
"flex flex-col gap-3 md:flex-row md:items-center md:justify-between",
className
)}
>
<div className="space-y-1">
<Label htmlFor="api-access-enabled">API key access</Label>
<p className="text-xs text-muted-foreground">Allow API keys to access this workspace.</p>

View file

@ -1,5 +1,13 @@
{
"title": "Native Connectors",
"pages": ["reddit", "youtube", "instagram", "tiktok", "google-maps", "google-search", "web-crawl"],
"pages": [
"reddit",
"youtube",
"instagram",
"tiktok",
"google-maps",
"google-search",
"web-crawl"
],
"defaultOpen": false
}

View file

@ -89,17 +89,12 @@ export function useRunStream(workspaceId: number) {
setState((s) => ({
...s,
latest: ev,
events:
ev.type === "run.progress"
? [...s.events.slice(-(MAX_LOG - 1)), ev]
: s.events,
events: ev.type === "run.progress" ? [...s.events.slice(-(MAX_LOG - 1)), ev] : s.events,
}));
}
} catch (e) {
if (signal.aborted) return;
setState((s) =>
s.status === "running" ? { ...s, status: "error", error: e } : s
);
setState((s) => (s.status === "running" ? { ...s, status: "error", error: e } : s));
}
},
[workspaceId, queryClient]
@ -113,12 +108,7 @@ export function useRunStream(workspaceId: number) {
startedAtRef.current = Date.now();
setState({ ...INITIAL, status: "running" });
try {
const started = await scrapersApiService.runAsync(
workspaceId,
platform,
verb,
payload
);
const started = await scrapersApiService.runAsync(workspaceId, platform, verb, payload);
runIdRef.current = started.run_id;
trackWeeklyUser("api_run", workspaceId);
setState((s) => ({ ...s, runId: started.run_id }));

View file

@ -1,15 +1,15 @@
import {
type ListScraperRunsParams,
type ScraperRunEvent,
type StartAsyncRunResponse,
listCapabilitiesResponse,
listRunsResponse,
type ScraperRunEvent,
type StartAsyncRunResponse,
scraperRunDetail,
startAsyncRunResponse,
} from "@/contracts/types/scraper.types";
import { authenticatedFetch } from "@/lib/auth-fetch";
import { readSSEStream } from "@/lib/chat/streaming-state";
import { buildBackendUrl } from "@/lib/env-config";
import { authenticatedFetch } from "@/lib/auth-fetch";
import { baseApiService } from "./base-api.service";
const base = (workspaceId: number | string) => `/api/v1/workspaces/${workspaceId}/scrapers`;

View file

@ -62,7 +62,8 @@ export const tiktok: ConnectorPageContent = {
extractFields: [
{
label: "Videos",
description: "Caption text, canonical web URL, duration, and cover image for each TikTok video.",
description:
"Caption text, canonical web URL, duration, and cover image for each TikTok video.",
},
{
label: "Engagement",

View file

@ -23,7 +23,11 @@ assert.deepEqual(payload, {
query: "<query>",
});
const snippets = buildSnippets("https://api.example.com", "/api/v1/workspaces/1/scrapers/x/y", payload);
const snippets = buildSnippets(
"https://api.example.com",
"/api/v1/workspaces/1/scrapers/x/y",
payload
);
// Every popular language is present.
assert.deepEqual(

View file

@ -11,9 +11,7 @@ export function formatCost(costMicros: number | null | undefined): string {
/** One meter as a per-1k rate, e.g. 3500 micros/place -> "$3.50 / 1k places". */
export function formatRate(meter: ScraperPricingMeter): string {
const perThousand = (meter.micros_per_unit * 1000) / 1_000_000;
const dollars = Number.isInteger(perThousand)
? perThousand.toString()
: perThousand.toFixed(2);
const dollars = Number.isInteger(perThousand) ? perThousand.toString() : perThousand.toFixed(2);
return `$${dollars} / 1k ${meter.unit}s`;
}

View file

@ -8,13 +8,7 @@
type JsonObject = Record<string, unknown>;
export type FieldKind =
| "string"
| "string_array"
| "integer"
| "number"
| "boolean"
| "enum";
export type FieldKind = "string" | "string_array" | "integer" | "number" | "boolean" | "enum";
export interface FormField {
name: string;