chat design
This commit is contained in:
@@ -10,7 +10,10 @@
|
|||||||
<div class="message-list" aria-live="polite">
|
<div class="message-list" aria-live="polite">
|
||||||
@for (message of messages(); track $index) {
|
@for (message of messages(); track $index) {
|
||||||
<article class="message" [class.user-message]="message.role === 'user'">
|
<article class="message" [class.user-message]="message.role === 'user'">
|
||||||
<p>{{ message.content }}</p>
|
<div
|
||||||
|
class="message-content"
|
||||||
|
[innerHTML]="formatMessage(message.content)"
|
||||||
|
></div>
|
||||||
|
|
||||||
@if (message.actions?.length) {
|
@if (message.actions?.length) {
|
||||||
<div class="action-list">
|
<div class="action-list">
|
||||||
|
|||||||
@@ -68,10 +68,45 @@
|
|||||||
color: var(--mat-sys-on-surface);
|
color: var(--mat-sys-on-surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.message p {
|
.message-content {
|
||||||
margin: 0;
|
display: grid;
|
||||||
|
gap: 0.5rem;
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
line-height: 1.4;
|
line-height: 1.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content :where(p, h3, h4, h5, ol, ul) {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content :where(h3, h4, h5) {
|
||||||
|
font-size: 0.98rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content :where(ol, ul) {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.35rem;
|
||||||
|
padding-left: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content li {
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content hr {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0.15rem 0;
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px solid color-mix(in srgb, var(--mat-sys-outline-variant) 75%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content code {
|
||||||
|
padding: 0.08rem 0.25rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: color-mix(in srgb, var(--mat-sys-outline-variant) 22%, transparent);
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Consolas, 'Liberation Mono', monospace;
|
||||||
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-list {
|
.action-list {
|
||||||
|
|||||||
@@ -103,4 +103,93 @@ export class AssistantChatComponent {
|
|||||||
|
|
||||||
return `Item hinzugefuegt: ${action.itemTitle}`;
|
return `Item hinzugefuegt: ${action.itemTitle}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected formatMessage(content: string): string {
|
||||||
|
const lines = this.escapeHtml(content).split(/\r?\n/);
|
||||||
|
const html: string[] = [];
|
||||||
|
let listMode: 'ol' | 'ul' | null = null;
|
||||||
|
|
||||||
|
const closeList = () => {
|
||||||
|
if (!listMode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.push(`</${listMode}>`);
|
||||||
|
listMode = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const rawLine of lines) {
|
||||||
|
const line = rawLine.trim();
|
||||||
|
|
||||||
|
if (!line) {
|
||||||
|
closeList();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^-{3,}$/.test(line)) {
|
||||||
|
closeList();
|
||||||
|
html.push('<hr>');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const headingMatch = line.match(/^(#{1,3})\s+(.+)$/);
|
||||||
|
|
||||||
|
if (headingMatch) {
|
||||||
|
closeList();
|
||||||
|
const level = headingMatch[1].length + 2;
|
||||||
|
html.push(
|
||||||
|
`<h${level}>${this.formatInlineMarkdown(headingMatch[2])}</h${level}>`,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const orderedMatch = line.match(/^\d+\.\s+(.+)$/);
|
||||||
|
|
||||||
|
if (orderedMatch) {
|
||||||
|
if (listMode !== 'ol') {
|
||||||
|
closeList();
|
||||||
|
html.push('<ol>');
|
||||||
|
listMode = 'ol';
|
||||||
|
}
|
||||||
|
|
||||||
|
html.push(`<li>${this.formatInlineMarkdown(orderedMatch[1])}</li>`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unorderedMatch = line.match(/^[-*]\s+(.+)$/);
|
||||||
|
|
||||||
|
if (unorderedMatch) {
|
||||||
|
if (listMode !== 'ul') {
|
||||||
|
closeList();
|
||||||
|
html.push('<ul>');
|
||||||
|
listMode = 'ul';
|
||||||
|
}
|
||||||
|
|
||||||
|
html.push(`<li>${this.formatInlineMarkdown(unorderedMatch[1])}</li>`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
closeList();
|
||||||
|
html.push(`<p>${this.formatInlineMarkdown(line)}</p>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
closeList();
|
||||||
|
return html.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
private formatInlineMarkdown(value: string): string {
|
||||||
|
return value
|
||||||
|
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
||||||
|
.replace(/\*(.+?)\*/g, '<em>$1</em>')
|
||||||
|
.replace(/`(.+?)`/g, '<code>$1</code>');
|
||||||
|
}
|
||||||
|
|
||||||
|
private escapeHtml(value: string): string {
|
||||||
|
return value
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user