diff --git a/ios/TaskWraithKit/Sources/TaskWraithUI/TWSharedViews.swift b/ios/TaskWraithKit/Sources/TaskWraithUI/TWSharedViews.swift index 92416a78..bbff3925 100644 --- a/ios/TaskWraithKit/Sources/TaskWraithUI/TWSharedViews.swift +++ b/ios/TaskWraithKit/Sources/TaskWraithUI/TWSharedViews.swift @@ -5191,6 +5191,54 @@ public struct QueuedComposerPromptsStack: View { /// model picker; an active guest renders as a green-accent chip /// — tap to change provider/model, × to remove. One guest per thread /// (desktop set/remove semantics). +/// Compact, generic diff summary shown above the ONE-LINE composer when it's +/// blurred and there are active changes — a stand-in for the (composer-specific) +/// changes row, which only returns on focus. Same look for every shell. Tap +/// opens the diff. (Codex-app-style pill, TaskWraith twist.) +public struct ComposerDiffPill: View { + let filesChanged: Int + let additions: Int + let deletions: Int + var onTap: (() -> Void)? = nil + + public init( + filesChanged: Int, additions: Int, deletions: Int, onTap: (() -> Void)? = nil + ) { + self.filesChanged = filesChanged + self.additions = additions + self.deletions = deletions + self.onTap = onTap + } + + /// 2_100 → "2.1k", 25_000 → "25k", 718 → "718". + private func compact(_ n: Int) -> String { + guard n >= 1000 else { return "\(n)" } + let k = Double(n) / 1000 + return String(format: k >= 10 ? "%.0fk" : "%.1fk", k) + } + + public var body: some View { + Button { onTap?() } label: { + HStack(spacing: 8) { + Text("\(filesChanged) file\(filesChanged == 1 ? "" : "s")") + .foregroundStyle(TWTheme.textMuted) + Text("+\(compact(additions))") + .foregroundStyle(TWTheme.statusSuccess) + Text("−\(compact(deletions))") + .foregroundStyle(TWTheme.statusFailed) + } + .font(.caption.weight(.semibold).monospacedDigit()) + .padding(.horizontal, 12) + .padding(.vertical, 6) + .background(TWTheme.surface3, in: Capsule()) + .overlay(Capsule().strokeBorder(TWTheme.border.opacity(0.6))) + } + .buttonStyle(.plain) + .accessibilityLabel( + "\(filesChanged) files changed, \(additions) added, \(deletions) removed. Open diff.") + } +} + public struct GuestParticipantControl: View { @ObservedObject var model: RemoteSessionModel let card: RemoteTaskCard diff --git a/ios/TaskWraithKit/Sources/TaskWraithUI/ThreadDetailViews.swift b/ios/TaskWraithKit/Sources/TaskWraithUI/ThreadDetailViews.swift index 8bdd82a4..cd990bb5 100644 --- a/ios/TaskWraithKit/Sources/TaskWraithUI/ThreadDetailViews.swift +++ b/ios/TaskWraithKit/Sources/TaskWraithUI/ThreadDetailViews.swift @@ -939,6 +939,23 @@ struct ThreadDetailView: View { .padding(.bottom, tuckedTab ? 14 : 0) .composerShellIf(tuckedTab, resolved, topCornersOnly: true) .padding(.horizontal, tuckedTab ? 18 : 0) + // Explicit zIndex: the core below carries .zIndex(tuckedTab ? 1 : 0), + // and a conditionally-inserted sibling with an IMPLICIT zIndex + // renders behind an explicit-zIndex sibling on insertion — which + // ate grok's tucked tab when it returned on focus. Make it explicit. + .zIndex(0) + } else if hasDiff { + // Blurred + active changes: a generic mini diff pill stands in + // for the full (composer-specific) changes row, which only + // returns on focus. Same pill for every shell. + ComposerDiffPill( + filesChanged: changedFileCount, + additions: diff?.additions ?? 0, + deletions: diff?.deletions ?? 0, + onTap: { openComposerDiff(workspaceId: primaryWorkspaceId) } + ) + .padding(.bottom, 8) + .zIndex(0) } // end focus-gated above-rows group // Composer core (input + telemetry rail). In detached // mode this is its OWN card under the floating above-rows;