Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Excel viewer displaying data #76

Open
wants to merge 3 commits into
base: new-api
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion examples/rag-ui/frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 35 additions & 11 deletions examples/rag-ui/frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ const customTheme = createTheme({

const App: FC = () => {
const { sources: mockSources } = useMockData();

const mockActionResult: ActionHandlerResponse = {
response: Promise.resolve("This is what jarvis is about"),
messages: [""],
sources: Promise.resolve<Source[]>(mockSources),
}
// @ts-ignore
const handleAction = (actionHandlerFunction: UserAction, messages: Message[], sources: Source[], activeSources: Source[], selectedSource: (Source | null)): ActionHandlerResponse | undefined => {
return {
response: Promise.resolve("This is what jarvis is about"),
messages: [""],
sources: Promise.resolve<Source[]>(mockSources),
};
return mockActionResult;
}

return (
Expand All @@ -40,26 +42,34 @@ type MockData = {
sources: Source[];
}
const useMockData = (): MockData => {
const [jarvisPdfBufferData, setJarvisPdfBufferData] = useState<ArrayBuffer | undefined>(undefined);
const [dummyPdfBufferData, setDummyPdfBufferData] = useState<ArrayBuffer | undefined>(undefined);
const [jarvisPdfBuffer, setJarvisPdfBuffer] = useState<ArrayBuffer | undefined>(undefined);
const [dummyPdfBuffer, setDummyPdfBuffer] = useState<ArrayBuffer | undefined>(undefined);
const [excelSampleBuffer, setExcelSampleBuffer] = useState<ArrayBuffer | undefined>(undefined);

// Pdf content mocks
useEffect(() => {
// Jarvis pdf
const getJarvisPdfSource = async () => {
const response = await fetch("http://localhost:5173/Current_State_Of_LLM-based_Assistants_For_Engineering.pdf");
const data = await response.arrayBuffer() as Uint8Array<ArrayBufferLike>;
setJarvisPdfBufferData(data);
setJarvisPdfBuffer(data);
}
// Dummy pdf
const getDummyPdfSource = async () => {
const response = await fetch("http://localhost:5173/dummy.pdf");
const data = await response.arrayBuffer() as Uint8Array<ArrayBufferLike>;
setDummyPdfBufferData(data);
setDummyPdfBuffer(data);
}
// Excel file
const getExcelSampleSource = async () => {
const response = await fetch("http://localhost:5173/excel_sample.xlsx");
const data = await response.arrayBuffer();
setExcelSampleBuffer(data);
}

getJarvisPdfSource();
getDummyPdfSource();
getExcelSampleSource();
}, []);

const sources: Source[] = [
Expand All @@ -69,7 +79,7 @@ const useMockData = (): MockData => {
title: "Current_State_Of_LLM-based_Assistants_For_Engineering.pdf",
type: "pdf",
relevance: 40,
data: new Uint8Array(jarvisPdfBufferData as ArrayBuffer),
data: new Uint8Array(jarvisPdfBuffer as ArrayBuffer),
metadata: {
page: 4,
}
Expand All @@ -79,11 +89,25 @@ const useMockData = (): MockData => {
title: "dummy.pdf",
type: "pdf",
relevance: 30,
data: new Uint8Array(dummyPdfBufferData as ArrayBuffer),
data: new Uint8Array(dummyPdfBuffer as ArrayBuffer),
metadata: {
page: 1,
}
},
// Excel Sources
{
id: uuid(),
title: "excel_sample.xlsx",
type: "xlsx",
relevance: 30,
data: excelSampleBuffer,
rangesHighlights: [
{
sheetName: "Equipment List",
ranges: ["A1:B8", "B9:C15", "C18:E20", "D5:F15", "F9:H15", "B42:E50"],
}
],
},
] as Source[];

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ const ApplicationMainContent: FunctionComponent = () => {
const gridColumns = "grid-cols-[1fr_max-content_1fr]";

return (
<div className={`grid gap-4 ${gridColumns} h-full max-w-[1700px]`}>
<div
className={`grid gap-4 ${gridColumns} h-full`}
style={{
maxWidth: "var(--app-main-content-max-width)",
}}
>
<CardContainer className={"p-1 border-none overflow-hidden gap-0"}>
<div className={"overflow-auto pt-4"}>
<ChatWindow />
Expand Down
96 changes: 68 additions & 28 deletions lexio/lib/components/ContentDisplay/ContentDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React, { useContext } from 'react';
import React, {FC, useContext} from "react";
import { PdfViewer } from "../Viewers/PdfViewer";
import { HtmlViewer } from "../Viewers/HtmlViewer";
import { MarkdownViewer } from "../Viewers/MarkdownViewer";
import { useRAGSources } from "../../hooks/hooks";
import { ThemeContext, removeUndefined } from "../../theme/ThemeContext";
import {SpreadsheetViewer} from "../Viewers/SpreadsheetViewer";
import {Source} from "../../types.ts";

export interface ContentDisplayStyles extends React.CSSProperties {
backgroundColor?: string;
Expand All @@ -17,7 +19,7 @@ interface ContentDisplayProps {
}

const ContentDisplay: React.FC<ContentDisplayProps> = ({ styleOverrides = {} }) => {
const { selectedSource } = useRAGSources();
const { selectedSource, sources, selectedSourceId } = useRAGSources();

// use theme
const theme = useContext(ThemeContext);
Expand All @@ -35,44 +37,82 @@ const ContentDisplay: React.FC<ContentDisplayProps> = ({ styleOverrides = {} })
...removeUndefined(styleOverrides),
};

const source = selectedSourceId ? (sources.find((s) => s.id === selectedSourceId) ?? undefined) : undefined;
const filename = source?.title ?? "";

if (!selectedSource) {
return null;
}

const renderContent = () => {
if (selectedSource.type === 'pdf' && selectedSource.data && selectedSource.data instanceof Uint8Array) {
// Prefer 'page' over '_page' if both are defined
const page = selectedSource.metadata?.page ?? selectedSource.metadata?._page;

return (
<PdfViewer
data={selectedSource.data}
page={page}
highlights={selectedSource.highlights}
return (
<div className="w-full h-full" style={style}>
<FileViewerRenderer
fileName={filename}
selectedSource={selectedSource}
/>
);
}
</div>
);
};

type PropsFileViewerRenderer = {
fileName: string;
selectedSource: Source;
}
const FileViewerRenderer: FC<PropsFileViewerRenderer> = (props) => {
const {
fileName,
selectedSource,
} = props;

if (selectedSource.type === 'pdf' && selectedSource.data && selectedSource.data instanceof Uint8Array) {
// Prefer 'page' over '_page' if both are defined
const page = selectedSource.metadata?.page ?? selectedSource.metadata?._page;

if (selectedSource.type === 'html' && selectedSource.data && typeof selectedSource.data === 'string') {
return <HtmlViewer htmlContent={selectedSource.data} />;
}
return (
<PdfViewer
data={selectedSource.data}
page={page}
highlights={selectedSource.highlights}
/>
);
}

if (selectedSource.type === 'markdown' && selectedSource.data && typeof selectedSource.data === 'string') {
return <MarkdownViewer markdownContent={selectedSource.data} />;
}
if (selectedSource.type === 'html' && selectedSource.data && typeof selectedSource.data === 'string') {
return <HtmlViewer htmlContent={selectedSource.data} />;
}

if (selectedSource.type === 'text' && selectedSource.data && typeof selectedSource.data === 'string') {
return <MarkdownViewer markdownContent={selectedSource.data} />;
}
if (selectedSource.type === 'markdown' && selectedSource.data && typeof selectedSource.data === 'string') {
return <MarkdownViewer markdownContent={selectedSource.data} />;
}

if (selectedSource.type === 'text' && selectedSource.data && typeof selectedSource.data === 'string') {
return <MarkdownViewer markdownContent={selectedSource.data} />;
}

if (
selectedSource.type === "xlsx" &&
selectedSource.data &&
selectedSource.data instanceof ArrayBuffer
) {
const {data, rangesHighlights} = selectedSource;
const ranges = rangesHighlights?.map((h) => h.ranges).flat();
const defaultSheetName = rangesHighlights ? rangesHighlights[0].sheetName : undefined;

return (
<SpreadsheetViewer
fileName={fileName}
fileBufferArray={data as ArrayBuffer}
rangesToHighlight={ranges}
defaultSelectedSheet={defaultSheetName}
/>
)
}

return (
<div className="flex justify-center items-center w-full h-full">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div>
</div>
);
};

return <div className="w-full h-full" style={style}>{renderContent()}</div>;
};
);
}

export { ContentDisplay };
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {FC} from "react";
import {Tabs, TabsList, TabsTrigger} from "./ui/tabs";

type Tab = {
value: string,
onClick: () => void;
}
type PropsSpreadsheetSelection = {
spreadsheets: string[];
selectedSpreadsheet: string
setSelectedSpreadsheet: (spreadsheet: string) => void;
}
const SpreadsheetSelection: FC<PropsSpreadsheetSelection> = (props) => {
const {
spreadsheets,
selectedSpreadsheet,
setSelectedSpreadsheet,
} = props;

const tabs: Tab[] = spreadsheets.map((key) => ({
value: key,
onClick: () => setSelectedSpreadsheet(key),
}));

return (
<Tabs
value={selectedSpreadsheet}
defaultValue={selectedSpreadsheet}
className="w-full bg-gray-100 rounded-md"
>
<TabsList
className={"w-full"}
style={{
display: "grid",
gridTemplateColumns: `repeat(${tabs.length},1fr)`,
}}
>
{tabs.map((tab) =>
<TabsTrigger
key={tab.value}
value={tab.value}
onClick={tab.onClick}
className={`${selectedSpreadsheet === tab.value ? "bg-white" : "bg-transparent"}`}
>
{tab.value}
</TabsTrigger>
)}
</TabsList>
</Tabs>

);
};
SpreadsheetSelection.displayName = "SpreadsheetSelectionComponent";

export { SpreadsheetSelection };
Loading