Update citations UI (#3837)
* new ui for citations * move onclick to parent * lint * change div to button * update citations UI and parsing to inherit user text size and design specs * new render animation for citations fix bug with citation on score on modal --------- Co-authored-by: Timothy Carambat <rambat1010@gmail.com>
This commit is contained in:
parent
dc8b204cd1
commit
202462fb6f
@ -10,15 +10,17 @@ import {
|
||||
Info,
|
||||
ArrowSquareOut,
|
||||
GithubLogo,
|
||||
Link,
|
||||
X,
|
||||
YoutubeLogo,
|
||||
LinkSimple,
|
||||
GitlabLogo,
|
||||
} from "@phosphor-icons/react";
|
||||
import ConfluenceLogo from "@/media/dataConnectors/confluence.png";
|
||||
import DrupalWikiLogo from "@/media/dataConnectors/drupalwiki.png";
|
||||
import ObsidianLogo from "@/media/dataConnectors/obsidian.png";
|
||||
import { toPercentString } from "@/utils/numbers";
|
||||
import pluralize from "pluralize";
|
||||
import useTextSize from "@/hooks/useTextSize";
|
||||
|
||||
function combineLikeSources(sources) {
|
||||
const combined = {};
|
||||
@ -42,29 +44,33 @@ export default function Citations({ sources = [] }) {
|
||||
if (sources.length === 0) return null;
|
||||
const [open, setOpen] = useState(false);
|
||||
const [selectedSource, setSelectedSource] = useState(null);
|
||||
const { textSizeClass } = useTextSize();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col mt-4 justify-left">
|
||||
<button
|
||||
onClick={() => setOpen(!open)}
|
||||
className={`border-none text-white/50 light:text-black/50 font-medium italic text-sm text-left ml-14 pt-2 ${
|
||||
className={`border-none font-semibold text-white/50 light:text-black/50 font-medium italic ${textSizeClass} text-left ml-14 pt-2 ${
|
||||
open ? "pb-2" : ""
|
||||
} hover:text-white/75 hover:light:text-black/75 transition-all duration-300`}
|
||||
>
|
||||
{open ? "Hide Citations" : "Show Citations"}
|
||||
<CaretRight
|
||||
className={`w-3.5 h-3.5 inline-block ml-1 transform transition-transform duration-300 ${
|
||||
weight="bold"
|
||||
size={14}
|
||||
className={`inline-block ml-1 transform transition-transform duration-300 ${
|
||||
open ? "rotate-90" : ""
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
{open && (
|
||||
<div className="flex flex-wrap md:flex-row md:items-center gap-4 overflow-x-scroll mt-1 doc__source ml-14">
|
||||
<div className="flex flex-wrap flex-col items-start overflow-x-scroll mt-1 ml-14 gap-y-2">
|
||||
{combineLikeSources(sources).map((source) => (
|
||||
<Citation
|
||||
key={v4()}
|
||||
source={source}
|
||||
onClick={() => setSelectedSource(source)}
|
||||
textSizeClass={textSizeClass}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@ -79,8 +85,8 @@ export default function Citations({ sources = [] }) {
|
||||
);
|
||||
}
|
||||
|
||||
const Citation = memo(({ source, onClick }) => {
|
||||
const { title } = source;
|
||||
const Citation = memo(({ source, onClick, textSizeClass }) => {
|
||||
const { title, references = 1 } = source;
|
||||
if (!title) return null;
|
||||
const chunkSourceInfo = parseChunkSource(source);
|
||||
const truncatedTitle = chunkSourceInfo?.text ?? middleTruncate(title, 25);
|
||||
@ -89,13 +95,25 @@ const Citation = memo(({ source, onClick }) => {
|
||||
: ICONS.file;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="w-fit flex flex-row justify-center items-center cursor-pointer text-sky-400"
|
||||
<button
|
||||
className={`flex doc__source gap-x-1 ${textSizeClass}`}
|
||||
onClick={onClick}
|
||||
type="button"
|
||||
>
|
||||
<CitationIcon className="w-6 h-6" weight="bold" />
|
||||
<p className="text-sm font-medium whitespace-nowrap">{truncatedTitle}</p>
|
||||
</div>
|
||||
<div className="flex items-start flex-1 pt-[4px]">
|
||||
<CitationIcon size={16} />
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-y-[0.2px] px-1">
|
||||
<p
|
||||
className={`!m-0 font-semibold whitespace-nowrap text-theme-text-primary hover:opacity-55 ${textSizeClass}`}
|
||||
>
|
||||
{truncatedTitle}
|
||||
</p>
|
||||
<p
|
||||
className={`!m-0 text-[10px] font-medium text-theme-text-secondary ${textSizeClass}`}
|
||||
>{`${references} ${pluralize("Reference", Number(references) || 1)}`}</p>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
||||
@ -118,12 +136,14 @@ function CitationDetailModal({ source, onClose }) {
|
||||
href={linkTo}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-xl font-semibold text-white overflow-hidden overflow-ellipsis whitespace-nowrap hover:underline hover:text-blue-300 flex items-center gap-x-1"
|
||||
className="text-xl w-[90%] font-semibold text-white whitespace-nowrap hover:underline hover:text-blue-300 flex items-center gap-x-1"
|
||||
>
|
||||
<h3 className="flex items-center gap-x-1">
|
||||
{webpageUrl}
|
||||
<ArrowSquareOut />
|
||||
</h3>
|
||||
<div className="flex items-center gap-x-1 max-w-full overflow-hidden">
|
||||
<h3 className="truncate text-ellipsis whitespace-nowrap overflow-hidden w-full">
|
||||
{webpageUrl}
|
||||
</h3>
|
||||
<ArrowSquareOut className="flex-shrink-0" />
|
||||
</div>
|
||||
</a>
|
||||
) : (
|
||||
<h3 className="text-xl font-semibold text-white overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
@ -222,7 +242,6 @@ function parseChunkSource({ title = "", chunks = [] }) {
|
||||
const sourceID = supportedSources.find((source) =>
|
||||
chunks[0].chunkSource?.startsWith(source)
|
||||
);
|
||||
console.log({ sourceID });
|
||||
let url, text, icon;
|
||||
|
||||
// Try to parse the URL from the chunk source
|
||||
@ -286,21 +305,19 @@ function parseChunkSource({ title = "", chunks = [] }) {
|
||||
return nullResponse;
|
||||
}
|
||||
|
||||
// Patch to render Confluence icon as a element like we do with Phosphor
|
||||
const ConfluenceIcon = ({ ...props }) => (
|
||||
<img src={ConfluenceLogo} {...props} />
|
||||
const ConfluenceIcon = ({ size = 16, ...props }) => (
|
||||
<img src={ConfluenceLogo} {...props} width={size} height={size} />
|
||||
);
|
||||
|
||||
// Patch to render DrupalWiki icon as a element like we do with Phosphor
|
||||
const DrupalWikiIcon = ({ ...props }) => (
|
||||
<img src={DrupalWikiLogo} {...props} />
|
||||
const DrupalWikiIcon = ({ size = 16, ...props }) => (
|
||||
<img src={DrupalWikiLogo} {...props} width={size} height={size} />
|
||||
);
|
||||
const ObsidianIcon = ({ size = 16, ...props }) => (
|
||||
<img src={ObsidianLogo} {...props} width={size} height={size} />
|
||||
);
|
||||
|
||||
const ObsidianIcon = ({ ...props }) => <img src={ObsidianLogo} {...props} />;
|
||||
|
||||
const ICONS = {
|
||||
file: FileText,
|
||||
link: Link,
|
||||
link: LinkSimple,
|
||||
youtube: YoutubeLogo,
|
||||
github: GithubLogo,
|
||||
gitlab: GitlabLogo,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
/**
|
||||
* Set the tooltips for the chat container in bulk.
|
||||
@ -53,14 +54,6 @@ export function ChatTooltips() {
|
||||
delayShow={300}
|
||||
className="tooltip !text-xs"
|
||||
/>
|
||||
<Tooltip
|
||||
id="similarity-score"
|
||||
place="top"
|
||||
delayShow={100}
|
||||
// z-[100] to ensure it renders above the chat history
|
||||
// as the citation modal is z-indexed above the chat history
|
||||
className="tooltip !text-xs z-[100]"
|
||||
/>
|
||||
<Tooltip
|
||||
id="metrics-visibility"
|
||||
place="bottom"
|
||||
@ -79,6 +72,29 @@ export function ChatTooltips() {
|
||||
delayShow={500}
|
||||
className="tooltip !text-xs"
|
||||
/>
|
||||
<DocumentLevelTooltip />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a document level tooltip that is rendered at the top most level of the document
|
||||
* to ensure it is rendered above the chat history and other elements. Anytime we have tooltips
|
||||
* in modals the z-indexing can be recalculated and we need to ensure it is rendered at the top most level
|
||||
* so it positions correctly.
|
||||
*/
|
||||
function DocumentLevelTooltip() {
|
||||
return createPortal(
|
||||
<>
|
||||
<Tooltip
|
||||
id="similarity-score"
|
||||
place="top"
|
||||
delayShow={100}
|
||||
// z-[100] to ensure it renders above the chat history
|
||||
// as the citation modal is z-indexed above the chat history
|
||||
className="tooltip !text-xs z-[100]"
|
||||
/>
|
||||
</>,
|
||||
document.body
|
||||
);
|
||||
}
|
||||
|
||||
@ -277,25 +277,22 @@ a {
|
||||
}
|
||||
|
||||
.doc__source {
|
||||
transform-origin: 0 100%;
|
||||
transform: scale(0);
|
||||
animation: message2 0.15s ease-out 0s forwards;
|
||||
opacity: 0;
|
||||
animation-delay: 50ms;
|
||||
animation: citationAnimation 0.15s ease-out 0s forwards;
|
||||
}
|
||||
|
||||
@keyframes message2 {
|
||||
@keyframes citationAnimation {
|
||||
0% {
|
||||
max-height: 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
80% {
|
||||
transform: scale(1.1);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
max-height: 100%;
|
||||
overflow: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user