chore: import upstream snapshot with attribution
FreeBSD Smoke / FreeBSD Smoke (x86_64) (push) Has been cancelled
CI / Quality Guardrails (push) Has been cancelled
CI / Build & Test (macos-latest) (push) Has been cancelled
CI / Build & Test (ubuntu-latest) (push) Has been cancelled
CI / Build & Test (windows-latest) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / PowerShell Syntax (push) Has been cancelled
CI / Windows Cross-Target Check (Linux) (push) Has been cancelled
FreeBSD Smoke / FreeBSD Smoke (x86_64) (push) Has been cancelled
CI / Quality Guardrails (push) Has been cancelled
CI / Build & Test (macos-latest) (push) Has been cancelled
CI / Build & Test (ubuntu-latest) (push) Has been cancelled
CI / Build & Test (windows-latest) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / PowerShell Syntax (push) Has been cancelled
CI / Windows Cross-Target Check (Linux) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import JCodeKit
|
||||
import SwiftUI
|
||||
|
||||
/// Friendly placeholder for a fresh session, centered in the canvas.
|
||||
struct EmptyTranscript: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 16) {
|
||||
Image(systemName: "terminal")
|
||||
.font(Theme.icon(40, weight: .light))
|
||||
.foregroundStyle(Theme.textSecondary)
|
||||
Text("Ready when you are")
|
||||
.font(Theme.mono(16, weight: .medium))
|
||||
.foregroundStyle(Theme.textPrimary)
|
||||
Text("Send a message to start driving this session.")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(Theme.textSecondary)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.padding(32)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
}
|
||||
|
||||
/// One transcript entry: user bubble, assistant markdown, or system note.
|
||||
struct EntryView: View {
|
||||
let entry: TranscriptEntry
|
||||
|
||||
var body: some View {
|
||||
switch entry.role {
|
||||
case .user:
|
||||
HStack {
|
||||
Spacer(minLength: 48)
|
||||
VStack(alignment: .trailing, spacing: 4) {
|
||||
Text(entry.text)
|
||||
.font(.body)
|
||||
.foregroundStyle(Theme.textPrimary)
|
||||
.padding(12)
|
||||
.background(Theme.mintTint)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||
.copyContextMenu(entry.text)
|
||||
if entry.isQueued {
|
||||
Label("queued", systemImage: "clock")
|
||||
.font(Theme.mono(11))
|
||||
.foregroundStyle(Theme.textTertiary)
|
||||
.accessibilityLabel("Queued")
|
||||
.accessibilityHint("Delivers after the current response")
|
||||
}
|
||||
}
|
||||
}
|
||||
case .assistant:
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
if !entry.reasoning.isEmpty {
|
||||
Text(entry.reasoning)
|
||||
.font(Theme.mono(12))
|
||||
.italic()
|
||||
.foregroundStyle(Theme.textTertiary)
|
||||
.lineLimit(4)
|
||||
.copyContextMenu(entry.reasoning)
|
||||
}
|
||||
ForEach(entry.toolCalls) { call in
|
||||
ToolCallCard(call: call)
|
||||
}
|
||||
if !entry.text.isEmpty {
|
||||
MarkdownText(entry.text)
|
||||
.copyContextMenu(entry.text)
|
||||
}
|
||||
}
|
||||
case .system:
|
||||
Text(entry.text)
|
||||
.font(.footnote)
|
||||
.foregroundStyle(Theme.textTertiary)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
.copyContextMenu(entry.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
/// Long-press context menu offering to copy the given text.
|
||||
func copyContextMenu(_ text: String) -> some View {
|
||||
contextMenu {
|
||||
Button {
|
||||
UIPasteboard.general.string = text
|
||||
} label: {
|
||||
Label("Copy", systemImage: "doc.on.doc")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user