Nucleic-Session: FFDE42E0-22AB-4478-B2E2-FF69B2753CEE Co-authored-by: Nucleic <[email protected]>
95 lines
4.3 KiB
Swift
95 lines
4.3 KiB
Swift
import SwiftUI
|
|
|
|
/// A `git commit` rendered as a structured card — the remote's echo of the desktop `GitBlockCard`:
|
|
/// the commit subject reads as a headline and the message body as Markdown, so a commit in the
|
|
/// transcript is legible at a glance instead of a raw `git commit -F - <<'EOF' …` blob. The literal
|
|
/// command stays one tap away under "Show command", so the card never hides what actually ran.
|
|
struct GitCommitCard: View {
|
|
let commit: GitCommitSummary.Commit
|
|
/// The literal command, revealed under "Show command". Omit to hide the disclosure.
|
|
var rawCommand: String? = nil
|
|
/// The tool result text (e.g. the hash a trailing `git log --oneline -1` prints), shown
|
|
/// beneath the card once the command has run. Omit inside an expanded tool card, where the
|
|
/// result already shows as its own block.
|
|
var output: String? = nil
|
|
|
|
private var accent: Color { Palette.accent }
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
HStack(alignment: .firstTextBaseline, spacing: 9) {
|
|
Image(systemName: commit.amend ? "pencil.circle" : "checkmark.seal")
|
|
.font(.callout).foregroundStyle(accent).frame(width: 16)
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text(commit.amend ? "Amend commit" : "Commit")
|
|
.font(.caption.weight(.semibold))
|
|
.foregroundStyle(.secondary)
|
|
if !commit.subject.isEmpty {
|
|
Text(commit.subject)
|
|
.font(.callout.weight(.semibold))
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
.textSelection(.enabled)
|
|
}
|
|
}
|
|
}
|
|
if !commit.body.isEmpty {
|
|
// The body is real commit-message Markdown (paragraphs, bullet lists) — render it
|
|
// formatted, indented under the subject.
|
|
MarkdownText(markdown: commit.body)
|
|
.padding(.leading, 25)
|
|
.textSelection(.enabled)
|
|
}
|
|
if let output, !output.isEmpty { outputRow(output) }
|
|
if let rawCommand, !rawCommand.isEmpty {
|
|
CommandDisclosure(command: rawCommand, accent: accent)
|
|
}
|
|
}
|
|
.padding(.horizontal, 12).padding(.vertical, 10)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.background(accent.opacity(0.06))
|
|
.overlay(RoundedRectangle(cornerRadius: 8).strokeBorder(accent.opacity(0.16), lineWidth: 1))
|
|
.clipShape(RoundedRectangle(cornerRadius: 8))
|
|
}
|
|
}
|
|
|
|
/// A disclosure that reveals the literal shell command beneath a structured command card — so a
|
|
/// command rendered as a GUI element never hides what's actually being run. The remote's port of
|
|
/// the desktop `CommandDisclosure`.
|
|
struct CommandDisclosure: View {
|
|
/// The literal command to reveal.
|
|
let command: String
|
|
/// The card's accent, applied to the toggle and the revealed block.
|
|
let accent: Color
|
|
|
|
@State private var showCommand = false
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 6) {
|
|
Button {
|
|
withAnimation(.easeInOut(duration: 0.15)) { showCommand.toggle() }
|
|
} label: {
|
|
HStack(spacing: 5) {
|
|
Image(systemName: "chevron.right")
|
|
.font(.caption2.weight(.semibold))
|
|
.rotationEffect(.degrees(showCommand ? 90 : 0))
|
|
Text(showCommand ? "Hide command" : "Show command")
|
|
.font(.caption.weight(.medium))
|
|
}
|
|
.foregroundStyle(accent)
|
|
.contentShape(Rectangle())
|
|
}
|
|
.buttonStyle(.plain)
|
|
if showCommand {
|
|
Text(command)
|
|
.font(.system(.caption, design: .monospaced))
|
|
.foregroundStyle(.secondary)
|
|
.textSelection(.enabled)
|
|
.multilineTextAlignment(.leading)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.padding(8)
|
|
.background(accent.opacity(0.05), in: RoundedRectangle(cornerRadius: 6))
|
|
}
|
|
}
|
|
}
|
|
}
|