mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-20 23:21:06 +02:00
Merge remote-tracking branch 'upstream/dev' into fix/onboarding
This commit is contained in:
commit
2d837ea18c
185 changed files with 4729 additions and 3630 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
import { AppearanceContent } from "../components/AppearanceContent";
|
||||
|
||||
export default function Page() {
|
||||
return <AppearanceContent />;
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue