mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-29 08:33:20 +00:00
Merge pull request #1317 from utin-francis-peter/feat/sources-in-react-widget
Feat/sources in react widget
This commit is contained in:
@@ -41,6 +41,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/plugin-transform-flow-strip-types": "^7.23.3",
|
||||
"@bpmn-io/snarkdown": "^2.2.0",
|
||||
"@parcel/resolver-glob": "^2.12.0",
|
||||
"@parcel/transformer-svg-react": "^2.12.0",
|
||||
"@parcel/transformer-typescript-tsc": "^2.12.0",
|
||||
|
||||
128
extensions/react-widget/src/components/QuerySources.tsx
Normal file
128
extensions/react-widget/src/components/QuerySources.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import {Query} from "../types/index"
|
||||
import styled from 'styled-components';
|
||||
import { ExternalLinkIcon } from '@radix-ui/react-icons'
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
|
||||
|
||||
const SourcesWrapper = styled.div`
|
||||
margin: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
`
|
||||
|
||||
const SourcesGrid = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0.5rem;
|
||||
max-width: 90vw;
|
||||
overflow-x: scroll;
|
||||
|
||||
@media(min-width: 768px){
|
||||
max-width: 70vw;
|
||||
}
|
||||
`
|
||||
|
||||
const SourceItem = styled.div`
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
border-radius: 6px;
|
||||
background-color: ${props =>props.theme.secondary.bg};
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
color:${props => props.theme.text};
|
||||
transform: background-color .2s, color .2s;
|
||||
|
||||
&:hover{
|
||||
background-color: ${props => props.theme.primary.bg};
|
||||
color: ${props => props.theme.primary.text};
|
||||
}
|
||||
`
|
||||
|
||||
const SourceLink = styled.div<{$hasExternalSource: boolean}>`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
text-decoration: ${({$hasExternalSource}) => ($hasExternalSource? "underline": "none")};
|
||||
text-underline-offset: 2px;
|
||||
`
|
||||
|
||||
const SourceLinkText = styled.p`
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
`
|
||||
|
||||
const OtherSources = styled.button`
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
color: #8860DB;
|
||||
border: none;
|
||||
outline: none;
|
||||
margin-top: 0.5rem;
|
||||
align-self: flex-start;
|
||||
`
|
||||
|
||||
type TQuerySources = {
|
||||
sources: Pick<Query, "sources">["sources"],
|
||||
}
|
||||
|
||||
const QuerySources = ({sources}:TQuerySources) => {
|
||||
const [showAllSources, setShowAllSources] = useState(false)
|
||||
|
||||
const visibleSources = useMemo(() => {
|
||||
if(!sources) return [];
|
||||
|
||||
return showAllSources? sources : sources.slice(0, 3)
|
||||
}, [sources, showAllSources])
|
||||
|
||||
const handleToggleShowAll = () => {
|
||||
setShowAllSources(prev => !prev)
|
||||
}
|
||||
|
||||
if(!sources || sources.length === 0){
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SourcesWrapper>
|
||||
<SourcesGrid>
|
||||
{visibleSources?.map((source, index) => (
|
||||
<SourceItem
|
||||
key={index}
|
||||
>
|
||||
<SourceLink
|
||||
$hasExternalSource={!!source.source && source.source !== "local"}
|
||||
onClick={() =>
|
||||
source.source && source.source !== 'local'
|
||||
? window.open(source.source, '_blank', 'noopener,noreferrer')
|
||||
: null
|
||||
}
|
||||
>
|
||||
<ExternalLinkIcon />
|
||||
<SourceLinkText title={source.source && source.source !== 'local' ? source.source : source.title}>
|
||||
{source.source && source.source !== 'local' ? source.source : source.title}
|
||||
</SourceLinkText>
|
||||
</SourceLink>
|
||||
</SourceItem>
|
||||
))}
|
||||
</SourcesGrid>
|
||||
|
||||
{
|
||||
sources.length > 3 && (
|
||||
<OtherSources onClick={handleToggleShowAll}>{
|
||||
showAllSources ? `Show less` : `+ ${sources.length - 3} more`
|
||||
}</OtherSources>
|
||||
|
||||
)
|
||||
}
|
||||
</SourcesWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export default QuerySources
|
||||
@@ -1,7 +1,8 @@
|
||||
|
||||
//development
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { App } from "./App";
|
||||
import React from "react";
|
||||
const container = document.getElementById("app") as HTMLElement;
|
||||
const root = createRoot(container)
|
||||
root.render(<App />);
|
||||
root.render(<App />);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { FEEDBACK } from "@/types";
|
||||
|
||||
interface HistoryItem {
|
||||
prompt: string;
|
||||
response?: string;
|
||||
}
|
||||
|
||||
interface FetchAnswerStreamingProps {
|
||||
question?: string;
|
||||
apiKey?: string;
|
||||
@@ -12,12 +14,14 @@ interface FetchAnswerStreamingProps {
|
||||
apiHost?: string;
|
||||
onEvent?: (event: MessageEvent) => void;
|
||||
}
|
||||
|
||||
interface FeedbackPayload {
|
||||
question: string;
|
||||
answer: string;
|
||||
apikey: string;
|
||||
feedback: FEEDBACK;
|
||||
}
|
||||
|
||||
export function fetchAnswerStreaming({
|
||||
question = '',
|
||||
apiKey = '',
|
||||
@@ -46,7 +50,7 @@ export function fetchAnswerStreaming({
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
let counterrr = 0;
|
||||
let counter = 0;
|
||||
const processStream = ({
|
||||
done,
|
||||
value,
|
||||
@@ -56,7 +60,7 @@ export function fetchAnswerStreaming({
|
||||
return;
|
||||
}
|
||||
|
||||
counterrr += 1;
|
||||
counter += 1;
|
||||
|
||||
const chunk = decoder.decode(value);
|
||||
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
export type MESSAGE_TYPE = 'QUESTION' | 'ANSWER' | 'ERROR';
|
||||
|
||||
export type Status = 'idle' | 'loading' | 'failed';
|
||||
|
||||
export type FEEDBACK = 'LIKE' | 'DISLIKE';
|
||||
|
||||
export type THEME = 'light' | 'dark';
|
||||
|
||||
export interface Query {
|
||||
prompt: string;
|
||||
response?: string;
|
||||
feedback?: FEEDBACK;
|
||||
error?: string;
|
||||
sources?: { title: string; text: string }[];
|
||||
sources?: { title: string; text: string, source:string }[];
|
||||
conversationId?: string | null;
|
||||
title?: string | null;
|
||||
}
|
||||
|
||||
export interface WidgetProps {
|
||||
apiHost?: string;
|
||||
apiKey?: string;
|
||||
@@ -32,6 +37,8 @@ export interface WidgetProps {
|
||||
buttonText?:string;
|
||||
buttonBg?:string;
|
||||
collectFeedback?:boolean;
|
||||
deafultOpen?: boolean;
|
||||
showSources?: boolean
|
||||
defaultOpen?: boolean;
|
||||
}
|
||||
export interface WidgetCoreProps extends WidgetProps {
|
||||
@@ -54,4 +61,4 @@ export interface Result {
|
||||
text:string;
|
||||
title:string;
|
||||
source:string;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user