Merge remote-tracking branch 'upstream/dev' into fix/onboarding

This commit is contained in:
Anish Sarkar 2026-07-14 10:18:03 +05:30
commit 2d837ea18c
185 changed files with 4729 additions and 3630 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

@ -0,0 +1,5 @@
import { AppearanceContent } from "../components/AppearanceContent";
export default function Page() {
return <AppearanceContent />;
}

View file

@ -0,0 +1,43 @@
"use client";
import { useAtom } from "jotai";
import { showMessageTimestampsAtom } from "@/atoms/chat/show-timestamps.atom";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
export function AppearanceContent() {
const [showTimestamps, setShowTimestamps] = useAtom(showMessageTimestampsAtom);
return (
<div className="flex flex-col gap-4 md:gap-6">
<section>
<div className="pb-2 md:pb-3">
<h2 className="text-base md:text-lg font-semibold">Chat</h2>
<p className="text-xs md:text-sm text-muted-foreground">
Control how messages are displayed in your conversations.
</p>
</div>
<div className="flex flex-col gap-3">
<div className="flex items-center justify-between rounded-lg bg-accent p-4">
<div className="space-y-0.5">
<Label
htmlFor="show-timestamps-toggle"
className="text-sm font-medium cursor-pointer"
>
Show message timestamps
</Label>
<p className="text-xs text-muted-foreground">
Display the time under each message in a chat. Saved on this device.
</p>
</div>
<Switch
id="show-timestamps-toggle"
checked={showTimestamps}
onCheckedChange={setShowTimestamps}
/>
</div>
</div>
</section>
</div>
);
}

View file

@ -7,6 +7,7 @@ import {
Library,
MessageCircle,
Monitor,
Palette,
ReceiptText,
ShieldCheck,
WandSparkles,
@ -20,6 +21,7 @@ import { usePlatform } from "@/hooks/use-platform";
export type UserSettingsTab =
| "profile"
| "appearance"
| "api-key"
| "prompts"
| "community-prompts"
@ -49,6 +51,12 @@ export function UserSettingsLayoutShell({ workspaceId, children }: UserSettingsL
href: `/dashboard/${workspaceId}/user-settings/profile`,
icon: <CircleUser className="h-4 w-4" />,
},
{
value: "appearance" as const,
label: "Appearance",
href: `/dashboard/${workspaceId}/user-settings/appearance`,
icon: <Palette className="h-4 w-4" />,
},
{
value: "api-key" as const,
label: t("api_key_nav_label"),

View file

@ -50,7 +50,7 @@ export const metadata: Metadata = {
alternates: {
canonical: "https://www.surfsense.com",
},
title: "SurfSense - Competitive Intelligence Platform for AI Agents",
title: "SurfSense - NotebookLM for Competitive Intelligence",
description:
"SurfSense is an open-source competitive intelligence platform. Your AI agents monitor competitors, track rankings, and listen to your market through one API or MCP server.",
keywords: [
@ -68,7 +68,7 @@ export const metadata: Metadata = {
"SurfSense",
],
openGraph: {
title: "SurfSense - Competitive Intelligence Platform for AI Agents",
title: "SurfSense - NotebookLM for Competitive Intelligence",
description:
"SurfSense is an open-source competitive intelligence platform. Your AI agents monitor competitors, track rankings, and listen to your market through one API or MCP server.",
url: "https://www.surfsense.com",
@ -86,7 +86,7 @@ export const metadata: Metadata = {
},
twitter: {
card: "summary_large_image",
title: "SurfSense - Competitive Intelligence Platform for AI Agents",
title: "SurfSense - NotebookLM for Competitive Intelligence",
description:
"SurfSense is an open-source competitive intelligence platform. Your AI agents monitor competitors, track rankings, and listen to your market through one API or MCP server.",
creator: "@SurfSenseAI",