blob: 0ee69c3a52f5587cb074dda248e2173a463ac127 [file] [log] [blame]
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {css, html, LitElement} from 'lit';
import {customElement, property, state} from 'lit/decorators.js';
import {chatModelToken, Turn} from '../../models/chat/chat-model';
import {resolve} from '../../models/dependency';
import {subscribe} from '../lit/subscription-controller';
@customElement('citations-box')
export class CitationsBox extends LitElement {
private readonly getChatModel = resolve(this, chatModelToken);
@property({type: Number}) turnIndex = 0;
@state() turns: readonly Turn[] = [];
@state() citation_url?: string;
constructor() {
super();
subscribe(
this,
() => this.getChatModel().turns$,
turns => (this.turns = turns ?? [])
);
subscribe(
this,
() => this.getChatModel().models$,
models => (this.citation_url = models?.citation_url)
);
}
static override styles = [
css`
:host {
.citations-display-box {
display: block;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: var(--spacing-s) var(--spacing-xl) var(--spacing-xl)
var(--spacing-xl);
margin-top: var(--spacing-xl);
margin-bottom: var(--spacing-xl);
background-color: var(--background-color-tertiary);
}
.citations-summary-message {
font-size: var(--font-size-small);
line-height: var(--line-height-small);
font-weight: var(--font-weight-medium);
letter-spacing: 0;
color: var(--primary-text-color);
margin-bottom: var(--spacing-m);
}
.citation-entry-list {
list-style-type: disc;
padding-left: 20px;
margin-top: 0;
margin-bottom: 0;
}
li {
font-size: var(--font-size-small);
line-height: var(--line-height-small);
font-weight: var(--font-weight-normal);
letter-spacing: 0;
color: var(--deemphasized-text-color);
overflow-wrap: break-word; /* Prevent long unbreakable strings from overflowing */
margin-bottom: var(--spacing-xs);
}
li:last-child {
margin-bottom: 0;
}
a {
color: var(--link-color);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
}
`,
];
override render() {
if (!this.citation_url) return;
const citations =
this.turns[this.turnIndex]?.geminiMessage?.citations ?? [];
if (citations.length === 0) return;
const count = citations.length;
return html`
<div class="citations-display-box">
<p class="citations-summary-message">
Use
<a
href=${this.citation_url}
target="_blank"
rel="noopener noreferrer"
>
with caution</a
>
. The model answer includes ${count} citation${count > 1 ? 's' : ''}
from other sources:
</p>
<ul class="citation-entry-list">
${citations.map(
citationUrl => html`
<li class="citation-item">
<a
.href=${citationUrl}
target="_blank"
rel="noopener noreferrer"
>${citationUrl}</a
>
</li>
`
)}
</ul>
</div>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'citations-box': CitationsBox;
}
}