nvrsion: promote trunk to dev

Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
2026-06-27 20:24:15 -07:00
co-authored by Nucleic
parent 9ceb563df7
commit 415443e8ce
3 changed files with 101 additions and 21 deletions
@@ -96,7 +96,7 @@ struct HomeView: View {
StatCard(value: running.count, label: "In progress", icon: "circle.lefthalf.filled")
StatCard(value: counts.activeChats, label: "Active", icon: "bolt")
StatCard(value: counts.messages, label: "Messages", icon: "paperplane")
StatCard(value: TokenCount.abbreviated(counts.tokens), label: "Tokens", icon: "number")
StatCard(values: TokenCount.adaptiveCandidates(counts.tokens), label: "Tokens", icon: "text.word.spacing")
}
}
}
@@ -128,18 +128,23 @@ private struct InProgressSessions: View {
}
private struct StatCard: View {
let value: String
/// Candidate renderings of the value, widest first: the card shows the fullest one its width
/// allows (via `ViewThatFits`) and falls back to the narrowest. A single entry for the plain
/// integer cards; several for the token card, so a wide card shows more digits than "1.6B".
let values: [String]
let label: String
let icon: String
init(value: Int, label: String, icon: String) {
self.value = "\(value)"
self.label = label
self.icon = icon
self.init(values: ["\(value)"], label: label, icon: icon)
}
init(value: String, label: String, icon: String) {
self.value = value
self.init(values: [value], label: label, icon: icon)
}
init(values: [String], label: String, icon: String) {
self.values = values.isEmpty ? [""] : values
self.label = label
self.icon = icon
}
@@ -147,7 +152,14 @@ private struct StatCard: View {
var body: some View {
VStack(alignment: .leading, spacing: 4) {
Image(systemName: icon).foregroundStyle(Palette.accent)
// Show the fullest value the card's width fits, shrinking toward the compact form when
// narrow; ViewThatFits renders the last candidate if none fit, so it never overflows.
ViewThatFits(in: .horizontal) {
ForEach(Array(values.enumerated()), id: \.offset) { _, value in
Text(value).font(.title2.weight(.semibold).monospacedDigit())
.lineLimit(1)
}
}
Text(label).font(.caption).foregroundStyle(.secondary)
}
.frame(maxWidth: .infinity, alignment: .leading)
@@ -59,17 +59,23 @@ struct HostExecToolCard: View {
.font(.caption).foregroundStyle(Palette.accent).frame(width: 16)
VStack(alignment: .leading, spacing: 3) {
HStack(alignment: .firstTextBaseline, spacing: 6) {
Text("Host").font(.caption.weight(.semibold))
Text("HOST").font(.caption2.weight(.bold)).foregroundStyle(Palette.accent)
if let parsed {
// The command-derived purpose leads as the bold headline; "HOST" is just a
// quiet tag (the desktop glyph already marks it as a host call).
Text(MessageBubble.markdown(parsed.purpose))
.font(.caption).lineLimit(1).truncationMode(.tail)
.font(.callout.weight(.semibold)).lineLimit(1).truncationMode(.tail)
} else {
Text("Host").font(.callout.weight(.semibold))
}
}
HStack(alignment: .firstTextBaseline, spacing: 5) {
Text("$").font(.caption.monospaced()).foregroundStyle(Palette.accent.opacity(0.7))
Text(command)
.font(.caption.monospaced()).foregroundStyle(.secondary)
.lineLimit(expanded ? nil : 1).truncationMode(.middle)
// Expanded, the literal stays bounded — a long chain's command is itself
// long, and the compact step list above carries the structure.
.lineLimit(expanded ? 6 : 1).truncationMode(.middle)
.textSelection(.enabled)
}
}
@@ -106,29 +112,91 @@ struct HostCommandBreakdown: View {
if let dir = summary.workingDirectory {
detailRow(icon: "folder", lines: ["in \(dir)"])
}
ForEach(Array(summary.invocations.enumerated()), id: \.element.id) { index, inv in
if index > 0 { Divider().overlay(Color.primary.opacity(0.08)) }
invocationView(inv)
}
steps
}
.frame(maxWidth: .infinity, alignment: .leading)
}
/// At most this many steps render in the compact chain list before the rest fold into a
/// "+N more" tail — so a long `cmd1 && cmd2 && …` pipeline can't grow the card unbounded
/// (the exact command shown alongside is always the complete source of truth).
private static let maxVisibleSteps = 7
/// The invocations, rendered to fit the chain length: a lone command gets the full block
/// (program + actions + every env/flag/operand spelled out); a *chain* collapses to one
/// compact line per command so a long pipeline stays short, not a tall stack of blocks.
@ViewBuilder private var steps: some View {
if summary.invocations.count <= 1 {
if let only = summary.invocations.first { invocationView(only) }
} else {
VStack(alignment: .leading, spacing: 6) {
ForEach(Array(summary.invocations.prefix(Self.maxVisibleSteps))) { inv in
stepRow(inv)
}
let hidden = summary.invocations.count - Self.maxVisibleSteps
if hidden > 0 {
Text("+\(hidden) more step\(hidden == 1 ? "" : "s")")
.font(.caption2).foregroundStyle(.tertiary)
.padding(.leading, 22)
}
}
}
}
/// One command in a chain, compacted to a single line: a step glyph, the program + actions
/// emphasized, then a truncated tail of its env/flags/operands. A destructive/elevated step is
/// tinted and glyphed in the danger color so it still stands out.
private func stepRow(_ inv: HostCommandSummary.Invocation) -> some View {
let risky = inv.destructive || inv.elevated
let tail = stepTail(inv)
return HStack(alignment: .firstTextBaseline, spacing: 8) {
Image(systemName: stepGlyph(inv))
.font(.caption2).foregroundStyle(risky ? Palette.danger : accent).frame(width: 14)
(Text(inv.headline)
.font(.caption.monospaced().weight(.semibold))
.foregroundColor(.primary)
+ Text(tail.isEmpty ? "" : " " + tail)
.font(.caption.monospaced())
.foregroundColor(.secondary))
.lineLimit(1).truncationMode(.middle)
.textSelection(.enabled)
Spacer(minLength: 0)
}
}
/// The trailing detail of a compacted step — its env, flags, and operands joined on one line,
/// truncated by the row. The literal command carries the untruncated form.
private func stepTail(_ inv: HostCommandSummary.Invocation) -> String {
(inv.env.map(\.display) + inv.flags.map(\.display) + inv.arguments).joined(separator: " ")
}
/// The leading glyph for a compacted step: a delete/elevation marker takes priority so the
/// risky step is unmistakable; otherwise a plain terminal glyph.
private func stepGlyph(_ inv: HostCommandSummary.Invocation) -> String {
if inv.destructive { return "trash" }
if inv.elevated { return "lock.shield" }
return "terminal"
}
/// The deterministic, command-derived purpose — labeled so the user knows it's *Nucleic's*
/// reading of the command, not the agent's claim about it.
private var purposeRow: some View {
HStack(alignment: .top, spacing: 9) {
Image(systemName: "sparkles").font(.callout).foregroundStyle(accent).frame(width: 16)
VStack(alignment: .leading, spacing: 2) {
Text("Likely purpose · inferred from the command")
.font(.caption2.weight(.semibold)).foregroundStyle(.secondary)
HStack(alignment: .top, spacing: 10) {
Image(systemName: "sparkles").font(.title3).foregroundStyle(accent).frame(width: 22)
VStack(alignment: .leading, spacing: 3) {
Text(MessageBubble.markdown(summary.purpose))
.font(.callout.weight(.semibold))
.font(.title3.weight(.semibold))
.fixedSize(horizontal: false, vertical: true)
.textSelection(.enabled)
Text("Likely purpose · inferred from the command")
.font(.caption2.weight(.medium)).foregroundStyle(.secondary)
}
Spacer(minLength: 0)
}
.padding(.horizontal, 11).padding(.vertical, 9)
.frame(maxWidth: .infinity, alignment: .leading)
.background(accent.opacity(0.12), in: RoundedRectangle(cornerRadius: 8))
.overlay(RoundedRectangle(cornerRadius: 8).strokeBorder(accent.opacity(0.22), lineWidth: 1))
}
/// A red banner for the two things a host command most needs flagged before "Allow": running
@@ -338,7 +338,7 @@ struct OrchestrationCard: View {
Image(systemName: "rectangle.3.group.fill")
.font(.callout).foregroundStyle(Palette.orchestra).frame(width: 18)
VStack(alignment: .leading, spacing: 1) {
Text("Multi-agent fan-out").font(.callout.weight(.semibold))
Text("Multiple Subagents").font(.callout.weight(.semibold))
Text("\(groups.count) subagents · \(doneCount) done")
.font(.caption2).foregroundStyle(.secondary)
}