feat: mark each speaker with a role icon in the transcript (#5)

This commit was merged in pull request #5.
This commit is contained in:
2026-10-03 21:03:26 +00:00
parent 31eb688455
commit d448a4377b
3 changed files with 30 additions and 2 deletions
+14
View File
@@ -1,6 +1,8 @@
import { describe, expect, it } from "vitest";
import {
ASSISTANT_MARKER,
STREAMING_CURSOR,
USER_MARKER,
buildThreadMarkdown,
previewText,
quote,
@@ -44,6 +46,18 @@ describe("buildThreadMarkdown", () => {
expect(markdown).toContain("4");
});
it("marks each speaker with a role icon", () => {
const markdown = buildThreadMarkdown([
message({ role: "user", text: "q" }),
message({ role: "assistant", text: "a" }),
]);
expect(markdown).toContain(USER_MARKER);
expect(markdown).toContain(ASSISTANT_MARKER);
// The user marker must not leak onto the answer or vice versa.
expect(markdown.indexOf(USER_MARKER)).toBeLessThan(markdown.indexOf(ASSISTANT_MARKER));
});
it("keeps the conversation in order", () => {
const markdown = buildThreadMarkdown([
message({ id: "a", role: "user", text: "first" }),
+12 -2
View File
@@ -14,6 +14,16 @@ import type { ChatTurn, ThreadMessage } from "./types";
export const STREAMING_CURSOR = "▍";
/**
* Role markers for the transcript.
*
* Emoji rather than icon images: Vicinae's markdown renderer is minimal and
* there is no reliable way to inline an `icon://` reference into markdown, but
* plain text always renders.
*/
export const USER_MARKER = "👤";
export const ASSISTANT_MARKER = "✨";
/** Prefixes every line so a multi-line message stays one continuous quote block. */
export function quote(text: string): string {
return text
@@ -31,11 +41,11 @@ export function previewText(text: string, max: number): string {
function renderMessage(message: ThreadMessage): string {
if (message.role === "user") {
return `**You**\n\n${quote(message.text)}`;
return `${USER_MARKER} **You**\n\n${quote(message.text)}`;
}
const body = message.text.trim() || (message.isStreaming ? "_Thinking…_" : "_No response._");
const sections = ["**Lam**", "", message.isStreaming ? `${body} ${STREAMING_CURSOR}` : body];
const sections = [`${ASSISTANT_MARKER} **Lam**`, "", message.isStreaming ? `${body} ${STREAMING_CURSOR}` : body];
if (message.error) {
sections.push("", `**Error** — ${message.error}`);