better special citation styling

This commit is contained in:
Timothy Carambat 2026-04-15 15:20:54 -07:00
parent f5fa03f472
commit 649c94298c
2 changed files with 25 additions and 7 deletions

View File

@ -39,21 +39,31 @@ const CIRCLE_IMAGES = {
outlookAttachment: OutlookLogo, outlookAttachment: OutlookLogo,
}; };
/**
* Returns the custom image for a given type, or null if no custom image is available.
* @param {string} type
* @returns {string|null}
*/
export function getCustomImage(type) {
return CIRCLE_IMAGES[type] ?? null;
}
/** /**
* Renders a circle with a source type icon inside, or a favicon if URL is provided. * Renders a circle with a source type icon inside, or a favicon if URL is provided.
* @param {"file"|"link"|"youtube"|"github"|"gitlab"|"confluence"|"drupalwiki"|"obsidian"|"paperlessNgx"} props.type * @param {"file"|"link"|"youtube"|"github"|"gitlab"|"confluence"|"drupalwiki"|"obsidian"|"paperlessNgx"} props.type
* @param {number} [props.size] - Circle diameter in px * @param {number} [props.size] - Circle diameter in px
* @param {number} [props.iconSize] - Icon size in px * @param {number} [props.iconSize] - Icon size in px
* @param {string} [props.url] - Optional URL to fetch favicon from * @param {string} [props.url] - Optional URL to fetch favicon from
* @param {string} [props.customImage] - Optional custom image to display
*/ */
export function SourceTypeCircle({ export function SourceTypeCircle({
type = "file", type = "file",
size = 22, size = 22,
iconSize = 12, iconSize = 12,
url = null, url = null,
customImage = null,
}) { }) {
const Icon = CIRCLE_ICONS[type] || CIRCLE_ICONS.file; const Icon = CIRCLE_ICONS[type] || CIRCLE_ICONS.file;
const customImage = CIRCLE_IMAGES[type];
const [imgError, setImgError] = useState(false); const [imgError, setImgError] = useState(false);
let faviconUrl = null; let faviconUrl = null;
@ -72,7 +82,7 @@ export function SourceTypeCircle({
return ( return (
<div <div
className="bg-white light:bg-slate-100 rounded-full flex items-center justify-center overflow-hidden" className={`${customImage ? "bg-transparent border-none" : "bg-white light:bg-slate-100 border-zinc-800 light:border-white rounded-full"} flex items-center justify-center overflow-hidden`}
style={{ width: size, height: size }} style={{ width: size, height: size }}
> >
{faviconUrl && !imgError ? ( {faviconUrl && !imgError ? (
@ -87,8 +97,8 @@ export function SourceTypeCircle({
<img <img
src={customImage} src={customImage}
alt={type} alt={type}
style={{ width: iconSize, height: iconSize }} style={{ width: size, height: size }}
className="object-contain" className="object-contain bg-transparent"
/> />
) : ( ) : (
<Icon size={iconSize} weight="bold" className="text-black" /> <Icon size={iconSize} weight="bold" className="text-black" />
@ -152,10 +162,11 @@ export default function Citations({ sources = [] }) {
> >
{visibleSources.map((source, idx) => { {visibleSources.map((source, idx) => {
const info = parseChunkSource(source); const info = parseChunkSource(source);
const customImage = CIRCLE_IMAGES[info.icon];
return ( return (
<div <div
key={source.title || idx} key={source.title || idx}
className="absolute top-0 size-[22px] rounded-full border-2 border-zinc-800 light:border-white" className={`absolute top-0 size-[22px] rounded-full ${customImage ? "border-none" : "border-2 border-zinc-800 light:border-white"}`}
style={{ left: `${idx * 17}px`, zIndex: 3 - idx }} style={{ left: `${idx * 17}px`, zIndex: 3 - idx }}
> >
<SourceTypeCircle <SourceTypeCircle
@ -163,6 +174,7 @@ export default function Citations({ sources = [] }) {
size={18} size={18}
iconSize={10} iconSize={10}
url={info.href} url={info.href}
customImage={customImage}
/> />
</div> </div>
); );

View File

@ -1,10 +1,15 @@
import { parseChunkSource, SourceTypeCircle } from "../../ChatHistory/Citation"; import {
parseChunkSource,
SourceTypeCircle,
getCustomImage,
} from "../../ChatHistory/Citation";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
export default function SourceItem({ source, onClick }) { export default function SourceItem({ source, onClick }) {
const { t } = useTranslation(); const { t } = useTranslation();
const info = parseChunkSource(source); const info = parseChunkSource(source);
const subtitle = info.isUrl ? info.text : t("chat_window.document"); const customImage = getCustomImage(info?.icon);
const subtitle = info?.isUrl ? info?.text : t("chat_window.document");
return ( return (
<button <button
@ -18,6 +23,7 @@ export default function SourceItem({ source, onClick }) {
size={16} size={16}
iconSize={10} iconSize={10}
url={info.href} url={info.href}
customImage={customImage}
/> />
<p className="flex-1 font-medium text-sm text-white light:text-slate-900 leading-[15px] truncate"> <p className="flex-1 font-medium text-sm text-white light:text-slate-900 leading-[15px] truncate">
{source.title} {source.title}