diff --git a/CHANGELOG.md b/CHANGELOG.md
index 06d616c28..e88e39d71 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -62,6 +62,7 @@
- Gog calendar: format date ranges as RFC 3339 with timezone to satisfy Google Calendar API (thanks @jayhickey).
- macOS onboarding: add scrollable page gutter for overflowing content (#105) — thanks @thewilloftheshadow.
- Chat UI: keep the chat scrolled to the latest message after switching sessions.
+- Chat UI: show rich session display names in Web Chat + SwiftUI + Android.
- Auto-reply: stream completed reply blocks as soon as they finish (configurable default + break); skip empty tool-only blocks unless verbose.
- Discord: avoid duplicate sends when block streaming is enabled (race with typing hook).
- Providers: make outbound text chunk limits configurable via `*.textChunkLimit` (defaults remain 4000/Discord 2000).
diff --git a/apps/android/app/src/main/java/com/steipete/clawdis/node/ui/chat/ChatComposer.kt b/apps/android/app/src/main/java/com/steipete/clawdis/node/ui/chat/ChatComposer.kt
index 8ac6f0aca..3bba710cf 100644
--- a/apps/android/app/src/main/java/com/steipete/clawdis/node/ui/chat/ChatComposer.kt
+++ b/apps/android/app/src/main/java/com/steipete/clawdis/node/ui/chat/ChatComposer.kt
@@ -148,7 +148,7 @@ fun ChatComposer(
)
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
- ConnectionPill(sessionKey = sessionKey, healthOk = healthOk)
+ ConnectionPill(sessionLabel = currentSessionLabel, healthOk = healthOk)
Spacer(modifier = Modifier.weight(1f))
if (pendingRunCount > 0) {
@@ -186,7 +186,7 @@ fun ChatComposer(
}
@Composable
-private fun ConnectionPill(sessionKey: String, healthOk: Boolean) {
+private fun ConnectionPill(sessionLabel: String, healthOk: Boolean) {
Surface(
shape = RoundedCornerShape(999.dp),
color = MaterialTheme.colorScheme.surfaceContainerHighest,
@@ -201,7 +201,7 @@ private fun ConnectionPill(sessionKey: String, healthOk: Boolean) {
shape = androidx.compose.foundation.shape.CircleShape,
color = if (healthOk) Color(0xFF2ECC71) else Color(0xFFF39C12),
) {}
- Text(sessionKey, style = MaterialTheme.typography.labelSmall)
+ Text(sessionLabel, style = MaterialTheme.typography.labelSmall)
Text(
if (healthOk) "Connected" else "Connecting…",
style = MaterialTheme.typography.labelSmall,
diff --git a/apps/shared/ClawdisKit/Sources/ClawdisChatUI/ChatComposer.swift b/apps/shared/ClawdisKit/Sources/ClawdisChatUI/ChatComposer.swift
index d34c03a53..f7f919439 100644
--- a/apps/shared/ClawdisKit/Sources/ClawdisChatUI/ChatComposer.swift
+++ b/apps/shared/ClawdisKit/Sources/ClawdisChatUI/ChatComposer.swift
@@ -209,7 +209,7 @@ struct ClawdisChatComposer: View {
Circle()
.fill(self.viewModel.healthOK ? .green : .orange)
.frame(width: 7, height: 7)
- Text(self.viewModel.sessionKey)
+ Text(self.activeSessionLabel)
.font(.caption2.weight(.semibold))
Text(self.viewModel.healthOK ? "Connected" : "Connecting…")
.font(.caption2)
@@ -221,6 +221,12 @@ struct ClawdisChatComposer: View {
.clipShape(Capsule())
}
+ private var activeSessionLabel: String {
+ let match = self.viewModel.sessions.first { $0.key == self.viewModel.sessionKey }
+ let trimmed = match?.displayName?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
+ return trimmed.isEmpty ? self.viewModel.sessionKey : trimmed
+ }
+
private var editorOverlay: some View {
ZStack(alignment: .topLeading) {
if self.viewModel.input.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
diff --git a/ui/src/ui/views/chat.ts b/ui/src/ui/views/chat.ts
index 7fca2f9bd..70812b683 100644
--- a/ui/src/ui/views/chat.ts
+++ b/ui/src/ui/views/chat.ts
@@ -43,7 +43,10 @@ export function renderChat(props: ChatProps) {
props.onSessionKeyChange((e.target as HTMLSelectElement).value)}
>
${sessionOptions.map(
- (entry) => html``,
+ (entry) =>
+ html``,
)}
@@ -115,6 +118,7 @@ export function renderChat(props: ChatProps) {
type SessionOption = {
key: string;
updatedAt?: number | null;
+ displayName?: string;
};
function resolveSessionOptions(