mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-11-03 21:39:22 +00:00
86 lines
4.3 KiB
HTML
86 lines
4.3 KiB
HTML
|
|
<li ngbDropdown class="nav-item" display="dynamic" placement="bottom-end" [popperOptions]="popperOptions" (openChange)="onOpenChange($event)">
|
|
<button class="btn navbar-action border-0" id="chatDropdown" ngbDropdownToggle aria-label="Chat" i18n-aria-label>
|
|
<pngx-bs-icon width="1.3em" height="1.3em" name="chatSquareDots"></pngx-bs-icon>
|
|
</button>
|
|
<div ngbDropdownMenu class="dropdown-menu-end shadow p-0 overflow-hidden" aria-labelledby="chatDropdown">
|
|
<div class="d-flex align-items-center border-bottom px-3 py-2">
|
|
<pngx-bs-icon width="1em" height="1em" name="stars" class="text-primary me-2"></pngx-bs-icon>
|
|
<span class="fw-semibold">
|
|
@if (documentId()) {
|
|
<ng-container i18n>Ask about this document</ng-container>
|
|
} @else {
|
|
<ng-container i18n>Ask about your documents</ng-container>
|
|
}
|
|
</span>
|
|
@if (messages().length) {
|
|
<button class="btn btn-sm btn-link text-muted ms-auto p-0" type="button" (click)="clearChat()" title="Clear chat" i18n-title>
|
|
<pngx-bs-icon width="1em" height="1em" name="arrow-counterclockwise"></pngx-bs-icon>
|
|
</button>
|
|
}
|
|
</div>
|
|
|
|
<div class="chat-messages px-2">
|
|
@if (!messages().length) {
|
|
<p class="text-muted text-center py-4 px-3 mb-0" i18n>Answers come from your documents, with links to their sources.</p>
|
|
}
|
|
@for (message of messages(); track message) {
|
|
<div class="message d-flex flex-row" [class.justify-content-end]="message.role === 'user'" [class.mt-3]="message.role === 'user' && !$first">
|
|
<div class="p-2 m-2" [class.bg-body-secondary]="message.role === 'user'" [class.rounded-3]="message.role === 'user'" [class.flex-grow-1]="message.role === 'assistant'">
|
|
@if (message.isStreaming && !message.content) {
|
|
<div class="d-flex align-items-center text-muted">
|
|
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
|
|
@if (slowResponse()) {
|
|
<ng-container i18n>Still thinking...</ng-container>
|
|
} @else {
|
|
<ng-container i18n>Thinking...</ng-container>
|
|
}
|
|
</div>
|
|
} @else if (message.role === 'assistant') {
|
|
<div class="chat-markdown text-break text-wrap" [class.streaming]="message.isStreaming" [innerHTML]="message.content | markdown"></div>
|
|
} @else {
|
|
<span class="text-break">{{ message.content }}</span>
|
|
}
|
|
@if (message.role === 'assistant' && !message.isStreaming && message.references?.length) {
|
|
<div class="chat-references list-group mt-3">
|
|
<div class="list-group-item text-muted small py-1" i18n>Sources</div>
|
|
@for (reference of message.references; track reference.id) {
|
|
<a class="list-group-item list-group-item-action d-flex align-items-center text-primary" [routerLink]="['/documents', reference.id]" [title]="reference.title">
|
|
<pngx-bs-icon width="0.9em" height="0.9em" name="file-text" class="me-2 flex-shrink-0"></pngx-bs-icon><span class="text-truncate">{{ reference.title }}</span>
|
|
</a>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
<div #scrollAnchor></div>
|
|
</div>
|
|
|
|
<form class="chat-input border-top p-2">
|
|
<label class="visually-hidden" for="chatInput" i18n>Message</label>
|
|
<div class="input-group">
|
|
<input
|
|
#chatInput
|
|
id="chatInput"
|
|
class="form-control" name="chatInput" type="text"
|
|
[placeholder]="placeholder"
|
|
[disabled]="loading()"
|
|
[ngModel]="input()"
|
|
(ngModelChange)="input.set($event)"
|
|
(keydown)="searchInputKeyDown($event)"
|
|
/>
|
|
@if (loading()) {
|
|
<button class="btn btn-outline-secondary" type="button" (click)="stopStreaming()" title="Stop" i18n-title aria-label="Stop" i18n-aria-label>
|
|
<pngx-bs-icon width="1em" height="1em" name="x-lg"></pngx-bs-icon>
|
|
</button>
|
|
} @else {
|
|
<button class="btn btn-primary" type="button" (click)="sendMessage()" title="Send" i18n-title aria-label="Send" i18n-aria-label>
|
|
<pngx-bs-icon width="1em" height="1em" name="arrow-return-left"></pngx-bs-icon>
|
|
</button>
|
|
}
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</li>
|