All blocks
Block
Streaming assistant message
A real assistant turn: user prompt above, the response streams in chunks with markdown + a fenced code block, and the hlmBtn below swaps from Stop to Regenerate when it finishes.
Live response with stream controls
<div hlmMessage align="end">
<div hlmMessageContent>
<div hlmBubble>
<div hlmBubbleContent>Show me a small computed() example.</div>
</div>
</div>
</div>
<div hlmMessage>
<div hlmMessageAvatar>
<hlm-avatar>
<span hlmAvatarFallback>AI</span>
</hlm-avatar>
</div>
<pk-response-stream
class="prose prose-sm dark:prose-invert max-w-none"
[textStream]="streamed()"
[markdown]="true"
[speed]="80"
/>
</div>
@if (state() === 'streaming') {
<button hlmBtn variant="secondary" size="sm" type="button" (click)="stop()">
<ng-icon name="lucideSquare" />
Stop
</button>
} @else {
<button hlmBtn variant="ghost" size="sm" type="button" (click)="start()">
<ng-icon name="lucideRefreshCw" />
Regenerate
</button>
}
// Component — chunked append while streaming.
// pk-markdown re-renders on each update; partial markdown still renders gracefully.
protected readonly state = signal<'idle' | 'streaming'>('idle');
protected readonly streamed = signal('');
protected start(): void {
this.streamed.set('');
this.state.set('streaming');
const chunks = FULL_RESPONSE.match(/.{1,40}/gs) ?? [];
let i = 0;
const tick = () => {
if (this.state() !== 'streaming') return;
if (i >= chunks.length) { this.state.set('idle'); return; }
this.streamed.update(v => v + chunks[i++]);
setTimeout(tick, 120);
};
setTimeout(tick, 120);
}
protected stop(): void { this.state.set('idle'); }