diff --git a/apps/rowboat/app/lib/components/datasource-icon.tsx b/apps/rowboat/app/lib/components/datasource-icon.tsx index 7b445061..18543b51 100644 --- a/apps/rowboat/app/lib/components/datasource-icon.tsx +++ b/apps/rowboat/app/lib/components/datasource-icon.tsx @@ -1,10 +1,10 @@ -import { FileIcon, FilesIcon, GlobeIcon } from "lucide-react"; +import { FileIcon, FilesIcon, FileTextIcon, GlobeIcon } from "lucide-react"; export function DataSourceIcon({ type = undefined, size = "sm", }: { - type?: "crawl" | "urls" | "files" | undefined; + type?: "crawl" | "urls" | "files" | "text" | undefined; size?: "sm" | "md"; }) { const sizeClass = size === "sm" ? "w-4 h-4" : "w-6 h-6"; @@ -13,5 +13,6 @@ export function DataSourceIcon({ {type == "crawl" && } {type == "urls" && } {type == "files" && } + {type == "text" && } ; } diff --git a/apps/rowboat/app/lib/types/datasource_types.ts b/apps/rowboat/app/lib/types/datasource_types.ts index bc9ccbef..f6316c3b 100644 --- a/apps/rowboat/app/lib/types/datasource_types.ts +++ b/apps/rowboat/app/lib/types/datasource_types.ts @@ -1,4 +1,5 @@ import { z } from "zod"; + export const DataSource = z.object({ name: z.string(), projectId: z.string(), @@ -23,8 +24,13 @@ export const DataSource = z.object({ z.object({ type: z.literal('files'), }), + z.object({ + type: z.literal('text'), + }) ]), -});export const DataSourceDoc = z.object({ +}); + +export const DataSourceDoc = z.object({ sourceId: z.string(), name: z.string(), version: z.number(), @@ -50,8 +56,13 @@ export const DataSource = z.object({ mimeType: z.string(), s3Key: z.string(), }), + z.object({ + type: z.literal('text'), + content: z.string(), + }), ]), }); + export const EmbeddingDoc = z.object({ content: z.string(), sourceId: z.string(), @@ -74,5 +85,4 @@ export const EmbeddingRecord = z.object({ title: z.string(), name: z.string(), }), -}); - +}); \ No newline at end of file diff --git a/apps/rowboat/app/projects/[projectId]/sources/[sourceId]/source-page.tsx b/apps/rowboat/app/projects/[projectId]/sources/[sourceId]/source-page.tsx index 1e65495e..81b5a642 100644 --- a/apps/rowboat/app/projects/[projectId]/sources/[sourceId]/source-page.tsx +++ b/apps/rowboat/app/projects/[projectId]/sources/[sourceId]/source-page.tsx @@ -13,6 +13,7 @@ import { TableLabel, TableValue } from "./shared"; import { ScrapeSource } from "./scrape-source"; import { FilesSource } from "./files-source"; import { getDataSource } from "../../../../actions/datasource_actions"; +import { TextSource } from "./text-source"; export function SourcePage({ sourceId, @@ -118,6 +119,10 @@ export function SourcePage({
File upload
} + {source.data.type === 'text' &&
+ +
Text
+
} @@ -131,6 +136,7 @@ export function SourcePage({ {source.data.type === 'urls' && } {source.data.type === 'files' && } + {source.data.type === 'text' && }
diff --git a/apps/rowboat/app/projects/[projectId]/sources/[sourceId]/text-source.tsx b/apps/rowboat/app/projects/[projectId]/sources/[sourceId]/text-source.tsx new file mode 100644 index 00000000..d16549f6 --- /dev/null +++ b/apps/rowboat/app/projects/[projectId]/sources/[sourceId]/text-source.tsx @@ -0,0 +1,128 @@ +"use client"; +import { PageSection } from "../../../../lib/components/page-section"; +import { WithStringId } from "../../../../lib/types/types"; +import { DataSource } from "../../../../lib/types/datasource_types"; +import { z } from "zod"; +import { useState, useEffect } from "react"; +import { Textarea } from "@heroui/react"; +import { FormStatusButton } from "../../../../lib/components/form-status-button"; +import { Spinner } from "@heroui/react"; +import { addDocsToDataSource, deleteDocsFromDataSource, listDocsInDataSource } from "../../../../actions/datasource_actions"; + +export function TextSource({ + projectId, + dataSource, + handleReload, +}: { + projectId: string, + dataSource: WithStringId>, + handleReload: () => void; +}) { + const [content, setContent] = useState(""); + const [docId, setDocId] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [isSaving, setIsSaving] = useState(false); + + useEffect(() => { + let ignore = false; + + async function fetchContent() { + setIsLoading(true); + try { + const { files } = await listDocsInDataSource({ + projectId, + sourceId: dataSource._id, + limit: 1, + }); + + console.log('got data', files); + + if (!ignore && files.length > 0) { + const doc = files[0]; + if (doc.data.type === 'text') { + setContent(doc.data.content); + setDocId(doc._id); + } + } + } catch (error) { + console.error('Error fetching content:', error); + } finally { + setIsLoading(false); + } + } + + fetchContent(); + return () => { + ignore = true; + }; + }, [projectId, dataSource._id]); + + async function handleSubmit(formData: FormData) { + setIsSaving(true); + try { + const newContent = formData.get('content') as string; + + // Delete existing doc if it exists + if (docId) { + await deleteDocsFromDataSource({ + projectId, + sourceId: dataSource._id, + docIds: [docId], + }); + } + + // Add new doc + await addDocsToDataSource({ + projectId, + sourceId: dataSource._id, + docData: [{ + name: 'text', + data: { + type: 'text', + content: newContent, + }, + }], + }); + + handleReload(); + } finally { + setIsSaving(false); + } + } + + if (isLoading) { + return ( + +
+ +

Loading content...

+
+
+ ); + } + + return ( + +
+