chat design
This commit is contained in:
@@ -10,7 +10,10 @@
|
||||
<div class="message-list" aria-live="polite">
|
||||
@for (message of messages(); track $index) {
|
||||
<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) {
|
||||
<div class="action-list">
|
||||
|
||||
@@ -68,10 +68,45 @@
|
||||
color: var(--mat-sys-on-surface);
|
||||
}
|
||||
|
||||
.message p {
|
||||
margin: 0;
|
||||
.message-content {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
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 {
|
||||
|
||||
@@ -103,4 +103,93 @@ export class AssistantChatComponent {
|
||||
|
||||
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