Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 67 additions & 4 deletions CodegiOS/DesignSystem/GlassComponents.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,68 @@
import SwiftUI

/// Keeps Liquid Glass call sites buildable on the app's iOS 18 deployment
/// target. iOS 26 uses the native effect; earlier systems receive a flat,
/// tinted surface that preserves contrast and shape.
extension View {
@ViewBuilder
func codegGlassEffect<S: Shape>(tint: Color? = nil, in shape: S) -> some View {
if #available(iOS 26.0, *) {
if let tint {
self.glassEffect(.regular.tint(tint), in: shape)
} else {
self.glassEffect(.regular, in: shape)
}
} else {
self.background {
ZStack {
shape.fill(Theme.bgElevated)

if let tint {
shape.fill(tint)
}
}
.allowsHitTesting(false)
}
}
}

/// Uses native glass button styles on iOS 26 and the closest system button
/// styles on iOS 18–25.
@ViewBuilder
func codegGlassButtonStyle(prominent: Bool = false) -> some View {
if #available(iOS 26.0, *) {
if prominent {
self.buttonStyle(.glassProminent)
} else {
self.buttonStyle(.glass)
}
} else if prominent {
self.buttonStyle(.borderedProminent)
} else {
self.buttonStyle(.bordered)
}
}
}

/// On iOS 26 this groups neighboring glass shapes so they can blend together.
/// Earlier systems do not have an equivalent container, so content is rendered
/// unchanged.
struct CodegGlassEffectContainer<Content: View>: View {
var spacing: CGFloat?
@ViewBuilder var content: () -> Content

@ViewBuilder
var body: some View {
if #available(iOS 26.0, *) {
GlassEffectContainer(spacing: spacing) {
content()
}
} else {
content()
}
}
}

/// A Liquid Glass surface for cards and rows, with a faint hairline for
/// definition on the dark backdrop.
struct GlassCard<Content: View>: View {
Expand All @@ -10,7 +73,7 @@ struct GlassCard<Content: View>: View {
var body: some View {
content()
.padding(padding)
.glassEffect(.regular, in: RoundedRectangle(cornerRadius: cornerRadius, style: .continuous))
.codegGlassEffect(in: RoundedRectangle(cornerRadius: cornerRadius, style: .continuous))
.hairlineBorder(cornerRadius)
}
}
Expand Down Expand Up @@ -93,8 +156,8 @@ struct GlassRow<Content: View>: View {
content()
.padding(.horizontal, 14)
.padding(.vertical, 12)
.glassEffect(
isSelected ? .regular.tint(Theme.accent.opacity(0.22)) : .regular,
.codegGlassEffect(
tint: isSelected ? Theme.accent.opacity(0.22) : nil,
in: RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
)
.hairlineBorder(cornerRadius, color: isSelected ? Theme.accent.opacity(0.45) : Theme.surfaceStroke)
Expand Down Expand Up @@ -247,7 +310,7 @@ struct PrimaryGlassButton: View {
.frame(maxWidth: .infinity)
.padding(.vertical, 4)
}
.buttonStyle(.glassProminent)
.codegGlassButtonStyle(prominent: true)
.tint(tint)
.disabled(isLoading)
}
Expand Down
8 changes: 4 additions & 4 deletions CodegiOS/DesignSystem/StateViews.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ struct EmptyStateView: View {
}
if let actionTitle, let action {
Button(actionTitle, action: action)
.buttonStyle(.glass)
.codegGlassButtonStyle()
.tint(Theme.accent)
.padding(.top, 4)
}
Expand Down Expand Up @@ -83,8 +83,8 @@ struct RefreshErrorBanner: View {
}
.padding(.horizontal, 14)
.padding(.vertical, 11)
.glassEffect(
.regular.tint(Theme.danger.opacity(0.16)),
.codegGlassEffect(
tint: Theme.danger.opacity(0.16),
in: RoundedRectangle(cornerRadius: Theme.Radius.md, style: .continuous)
)
.hairlineBorder(Theme.Radius.md, color: Theme.danger.opacity(0.35))
Expand All @@ -110,7 +110,7 @@ struct InlineErrorView: View {
.multilineTextAlignment(.center)
if let retry {
Button("Try Again", action: retry)
.buttonStyle(.glass)
.codegGlassButtonStyle()
.tint(Theme.accent)
.padding(.top, 4)
}
Expand Down
2 changes: 1 addition & 1 deletion CodegiOS/Features/Onboarding/OnboardingView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct OnboardingView: View {
.font(.system(size: 34, weight: .semibold))
.foregroundStyle(Theme.accent)
.frame(width: 84, height: 84)
.glassEffect(.regular.tint(Theme.accentDim), in: RoundedRectangle(cornerRadius: Theme.Radius.xl, style: .continuous))
.codegGlassEffect(tint: Theme.accentDim, in: RoundedRectangle(cornerRadius: Theme.Radius.xl, style: .continuous))
.hairlineBorder(Theme.Radius.xl, color: Theme.accent.opacity(0.3))
.padding(.bottom, 24)

Expand Down
2 changes: 1 addition & 1 deletion CodegiOS/Features/Projects/CloneRepoView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ struct CloneRepoView: View {
} label: {
Image(systemName: "folder")
}
.buttonStyle(.glass)
.codegGlassButtonStyle()
.tint(Theme.accent)
.disabled(cloning)
.accessibilityLabel("Browse")
Expand Down
2 changes: 1 addition & 1 deletion CodegiOS/Features/Projects/DirectoryBrowserView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ struct DirectoryBrowserView: View {
Image(systemName: "chevron.up")
.font(.body.weight(.semibold))
}
.buttonStyle(.glass)
.codegGlassButtonStyle()
.tint(Theme.accent)
.disabled(isRoot || isLoading)
.accessibilityLabel("Up")
Expand Down
2 changes: 1 addition & 1 deletion CodegiOS/Features/Servers/ServerEditorSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ struct ServerEditorSheet: View {
.frame(maxWidth: .infinity)
.padding(.vertical, 4)
}
.buttonStyle(.glass)
.codegGlassButtonStyle()
.tint(Theme.accent)
.disabled(model.isTesting || !model.canTest)

Expand Down
6 changes: 3 additions & 3 deletions CodegiOS/Features/SessionDetail/AgentOptionsButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ private struct AgentOptionsSheet: View {
}
.padding(14)
.frame(maxWidth: .infinity, alignment: .leading)
.glassEffect(.regular, in: RoundedRectangle(cornerRadius: Theme.Radius.md, style: .continuous))
.codegGlassEffect(in: RoundedRectangle(cornerRadius: Theme.Radius.md, style: .continuous))
.hairlineBorder(Theme.Radius.md)
}

Expand All @@ -387,7 +387,7 @@ private struct AgentOptionsSheet: View {
.foregroundStyle(Theme.danger)
.fixedSize(horizontal: false, vertical: true)
Button("Try Again") { options.load() }
.buttonStyle(.glass)
.codegGlassButtonStyle()
.tint(Theme.accent)
}
.padding(14)
Expand Down Expand Up @@ -511,7 +511,7 @@ struct OptionSection<Content: View>: View {
.foregroundStyle(Theme.textTertiary)
.padding(.horizontal, 4)
VStack(spacing: 0) { content() }
.glassEffect(.regular, in: RoundedRectangle(cornerRadius: Theme.Radius.md, style: .continuous))
.codegGlassEffect(in: RoundedRectangle(cornerRadius: Theme.Radius.md, style: .continuous))
.hairlineBorder(Theme.Radius.md)
}
}
Expand Down
4 changes: 2 additions & 2 deletions CodegiOS/Features/SessionDetail/BranchPickerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ struct BranchPickerView: View {
}
}
.padding(14)
.glassEffect(.regular, in: RoundedRectangle(cornerRadius: Theme.Radius.md, style: .continuous))
.codegGlassEffect(in: RoundedRectangle(cornerRadius: Theme.Radius.md, style: .continuous))
.hairlineBorder(Theme.Radius.md)
} else {
Button { showNewBranch = true } label: {
Expand All @@ -175,7 +175,7 @@ struct BranchPickerView: View {
.contentShape(Rectangle())
}
.buttonStyle(.plain)
.glassEffect(.regular, in: RoundedRectangle(cornerRadius: Theme.Radius.md, style: .continuous))
.codegGlassEffect(in: RoundedRectangle(cornerRadius: Theme.Radius.md, style: .continuous))
.hairlineBorder(Theme.Radius.md)
}
}
Expand Down
12 changes: 6 additions & 6 deletions CodegiOS/Features/SessionDetail/ComposeBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ struct ComposeBar: View {
.transition(.opacity.combined(with: .move(edge: .bottom)))
}

GlassEffectContainer(spacing: 8) {
CodegGlassEffectContainer(spacing: 8) {
HStack(alignment: .bottom, spacing: 8) {
addButton
TextField("Message", text: $text, axis: .vertical)
Expand All @@ -74,7 +74,7 @@ struct ComposeBar: View {
// `xl` radius clamps to a capsule while the field is one
// line (rhyming with the round +/send buttons) and relaxes
// to a rounded rect as it grows — no hard switch needed.
.glassEffect(.regular, in: RoundedRectangle(cornerRadius: Theme.Radius.xl, style: .continuous))
.codegGlassEffect(in: RoundedRectangle(cornerRadius: Theme.Radius.xl, style: .continuous))
.hairlineBorder(Theme.Radius.xl)

actionButton
Expand Down Expand Up @@ -161,7 +161,7 @@ struct ComposeBar: View {
.font(.system(size: 16, weight: .semibold))
.frame(width: 26, height: 26)
}
.buttonStyle(.glass)
.codegGlassButtonStyle()
.clipShape(Circle())
.tint(Theme.textSecondary)
.accessibilityLabel("Add or insert")
Expand All @@ -175,7 +175,7 @@ struct ComposeBar: View {
.font(.system(size: 16, weight: .bold))
.frame(width: 26, height: 26)
}
.buttonStyle(.glassProminent)
.codegGlassButtonStyle(prominent: true)
.tint(Theme.danger)
.clipShape(Circle())
.transition(.scale.combined(with: .opacity))
Expand All @@ -186,7 +186,7 @@ struct ComposeBar: View {
.font(.system(size: 16, weight: .bold))
.frame(width: 26, height: 26)
}
.buttonStyle(.glassProminent)
.codegGlassButtonStyle(prominent: true)
.tint(Theme.accent)
.clipShape(Circle())
.disabled(!canSend)
Expand Down Expand Up @@ -276,7 +276,7 @@ private struct NoticeBanner: View {
}
.padding(.horizontal, 12)
.padding(.vertical, 9)
.glassEffect(.regular, in: RoundedRectangle(cornerRadius: Theme.Radius.md, style: .continuous))
.codegGlassEffect(in: RoundedRectangle(cornerRadius: Theme.Radius.md, style: .continuous))
.hairlineBorder(Theme.Radius.md, color: Theme.accent.opacity(0.35))
.transition(.opacity.combined(with: .move(edge: .bottom)))
}
Expand Down
2 changes: 1 addition & 1 deletion CodegiOS/Features/SessionDetail/ComposeInsertSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ struct ComposeInsertSheet: View {
.multilineTextAlignment(.center)
.fixedSize(horizontal: false, vertical: true)
Button("Try Again") { model.load(source) }
.buttonStyle(.glass)
.codegGlassButtonStyle()
.tint(Theme.accent)
}
.padding(.horizontal, 32)
Expand Down
2 changes: 1 addition & 1 deletion CodegiOS/Features/SessionDetail/SessionDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ private struct JumpToLatestButton: View {
.font(.system(size: 15, weight: .bold))
.foregroundStyle(Theme.accent)
.frame(width: 40, height: 40)
.glassEffect(.regular, in: Circle())
.codegGlassEffect(in: Circle())
.hairlineBorder(20)
// An explicit hit shape so the whole disc is tappable (and the
// tap can't slip past its edge into the transcript underneath).
Expand Down
8 changes: 4 additions & 4 deletions CodegiOS/Features/Settings/Agents/AgentConfigKimiView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ struct KimiConfigSection: View {
else { Label("Fetch", systemImage: "arrow.clockwise").labelStyle(.titleAndIcon) }
}
.font(.subheadline.weight(.medium))
.buttonStyle(.glass).tint(Theme.accent)
.codegGlassButtonStyle().tint(Theme.accent)
.disabled(saving || fetchingModels)
}
}
Expand All @@ -168,7 +168,7 @@ struct KimiConfigSection: View {
Image(systemName: "list.bullet")
.font(.body.weight(.medium)).frame(width: 34, height: 30)
}
.buttonStyle(.glass).tint(Theme.textSecondary)
.codegGlassButtonStyle().tint(Theme.textSecondary)
.accessibilityLabel("Choose a fetched model")
}

Expand Down Expand Up @@ -246,9 +246,9 @@ struct KimiConfigSection: View {
HStack {
Spacer(minLength: 0)
if prominent {
saveButton(title, action).buttonStyle(.glassProminent).tint(Theme.accent).disabled(saving)
saveButton(title, action).codegGlassButtonStyle(prominent: true).tint(Theme.accent).disabled(saving)
} else {
saveButton(title, action).buttonStyle(.glass).tint(Theme.accent).disabled(saving)
saveButton(title, action).codegGlassButtonStyle().tint(Theme.accent).disabled(saving)
}
}
.padding(.horizontal, 16).padding(.vertical, 12)
Expand Down
8 changes: 4 additions & 4 deletions CodegiOS/Features/Settings/Agents/AgentConfigPiView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ struct PiConfigSection: View {
Button { Task { await detectPiBinary() } } label: {
Image(systemName: "arrow.clockwise").font(.body.weight(.medium))
}
.buttonStyle(.glass).tint(Theme.textSecondary)
.codegGlassButtonStyle().tint(Theme.textSecondary)
.disabled(checkingPi || piOp != nil)
.accessibilityLabel("Recheck")
if !checkingPi {
Expand All @@ -184,7 +184,7 @@ struct PiConfigSection: View {
if validating { ProgressView().controlSize(.small) }
else { Label("Validate", systemImage: "terminal").labelStyle(.titleAndIcon) }
}
.font(.subheadline.weight(.medium)).buttonStyle(.glass).tint(Theme.accent)
.font(.subheadline.weight(.medium)).codegGlassButtonStyle().tint(Theme.accent)
.disabled(validating || command.trimmingCharacters(in: .whitespaces).isEmpty)
}
}
Expand Down Expand Up @@ -313,9 +313,9 @@ struct PiConfigSection: View {
.padding(.horizontal, 14).padding(.vertical, 5)
}
if prominent {
label.buttonStyle(.glassProminent).tint(Theme.accent).disabled(disabled)
label.codegGlassButtonStyle(prominent: true).tint(Theme.accent).disabled(disabled)
} else {
label.buttonStyle(.glass).tint(Theme.accent).disabled(disabled)
label.codegGlassButtonStyle().tint(Theme.accent).disabled(disabled)
}
}

Expand Down
8 changes: 4 additions & 4 deletions CodegiOS/Features/Settings/Agents/AgentDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ struct AgentDetailView: View {
Text(primary.label).fontWeight(.semibold)
.padding(.horizontal, 18).padding(.vertical, 5)
}
.buttonStyle(.glassProminent).tint(Theme.accent)
.codegGlassButtonStyle(prominent: true).tint(Theme.accent)
.disabled(isInstalling || primary.disabled)
Spacer(minLength: 0)
if !secondary.isEmpty { versionMenu(secondary, iconOnly: true) }
Expand All @@ -334,7 +334,7 @@ struct AgentDetailView: View {
.font(.subheadline.weight(.medium)).padding(.horizontal, 16).padding(.vertical, 7)
}
}
.buttonStyle(.glass).tint(iconOnly ? Theme.textSecondary : Theme.accent)
.codegGlassButtonStyle().tint(iconOnly ? Theme.textSecondary : Theme.accent)
.accessibilityLabel(iconOnly ? "More version actions" : "Manage version")
.disabled(isInstalling)
}
Expand Down Expand Up @@ -499,7 +499,7 @@ struct AgentDetailView: View {
} label: {
Label("Clear Binary Cache", systemImage: "trash").frame(maxWidth: .infinity).padding(.vertical, 4)
}
.buttonStyle(.glass)
.codegGlassButtonStyle()
.tint(Theme.danger)
.disabled(isInstalling)
}
Expand Down Expand Up @@ -643,7 +643,7 @@ private struct FlowFixButtons: View {
Button { onTap(fix) } label: {
Text(fix.label).font(.caption.weight(.medium)).padding(.horizontal, 10).padding(.vertical, 5)
}
.buttonStyle(.glass)
.codegGlassButtonStyle()
.tint(Theme.accent)
.disabled(disabled)
}
Expand Down
2 changes: 1 addition & 1 deletion CodegiOS/Features/Settings/Agents/AgentsSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ struct AgentsSettingsView: View {
.multilineTextAlignment(.center)
.padding(.horizontal, 16)
.padding(.vertical, 11)
.glassEffect(.regular.tint(Theme.accent.opacity(0.18)), in: Capsule())
.codegGlassEffect(tint: Theme.accent.opacity(0.18), in: Capsule())
.padding(.horizontal, 24)
.padding(.bottom, 18)
.transition(.move(edge: .bottom).combined(with: .opacity))
Expand Down
Loading