openclaw/apps/ios-github-mobile/Sources/Components/StatusIndicator.swift
Claude cffaf0b904
feat: add SwiftUI GitHub mobile app inspired by Claude Code
- Create new iOS app at apps/ios-github-mobile
- Session list view with repository info and status
- Chat view with collapsible tool executions (Write, Bash, Read)
- Message input with model/repo/branch selection chips
- PR creation flow simulation
- Dark theme matching Claude Code aesthetic
- Mock data for repos, sessions, and tool calls
- Modern SwiftUI with @Observable, NavigationStack

https://claude.ai/code/session_018qmTB96S8xvNRQFrhcvzJp
2026-01-23 08:22:15 +00:00

109 lines
2.8 KiB
Swift

import SwiftUI
struct StatusIndicator: View {
let status: SessionStatus
var body: some View {
HStack(spacing: AppTheme.spacingXS) {
Circle()
.fill(statusColor)
.frame(width: 8, height: 8)
Text(status.rawValue)
.font(.subheadline)
.foregroundStyle(AppTheme.secondaryText)
}
}
private var statusColor: Color {
switch status {
case .idle:
return AppTheme.secondaryText
case .running:
return AppTheme.accent
case .completed:
return AppTheme.success
case .error:
return AppTheme.error
}
}
}
// MARK: - Loading Indicator
struct LoadingIndicator: View {
@State private var isAnimating = false
var body: some View {
HStack(spacing: AppTheme.spacingXS) {
ForEach(0..<3) { index in
Circle()
.fill(AppTheme.accent)
.frame(width: 6, height: 6)
.scaleEffect(isAnimating ? 1.0 : 0.5)
.animation(
.easeInOut(duration: 0.6)
.repeatForever()
.delay(Double(index) * 0.2),
value: isAnimating
)
}
}
.onAppear {
isAnimating = true
}
}
}
// MARK: - Tool Status Badge
struct ToolStatusBadge: View {
let status: ToolCallStatus
var body: some View {
HStack(spacing: AppTheme.spacingXS) {
switch status {
case .pending:
Image(systemName: "clock")
.foregroundStyle(AppTheme.secondaryText)
case .running:
ProgressView()
.scaleEffect(0.7)
.tint(AppTheme.accent)
case .completed:
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(AppTheme.success)
case .failed:
Image(systemName: "xmark.circle.fill")
.foregroundStyle(AppTheme.error)
}
}
.font(.caption)
}
}
#Preview {
VStack(spacing: 20) {
StatusIndicator(status: .idle)
StatusIndicator(status: .running)
StatusIndicator(status: .completed)
StatusIndicator(status: .error)
Divider()
LoadingIndicator()
Divider()
HStack(spacing: 20) {
ToolStatusBadge(status: .pending)
ToolStatusBadge(status: .running)
ToolStatusBadge(status: .completed)
ToolStatusBadge(status: .failed)
}
}
.padding()
.background(AppTheme.background)
.preferredColorScheme(.dark)
}