Skip to content
Merged
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
4 changes: 2 additions & 2 deletions IPASignCraft.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 2.0;
PRODUCT_BUNDLE_IDENTIFIER = com.sn.IPASignCraft;
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
Expand Down Expand Up @@ -331,7 +331,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 2.0;
PRODUCT_BUNDLE_IDENTIFIER = com.sn.IPASignCraft;
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
Expand Down
2 changes: 2 additions & 0 deletions IPASignCraft/App/RootView/RootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ struct RootView: View {
switch selection {
case .home:
HomeView()
case .ipaInspector:
IPAInspectorView()
}
}
}
3 changes: 2 additions & 1 deletion IPASignCraft/DesignSystem/Components/Cards/AppCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ struct AppCard<Content: View>: View {
.padding(Spacing.base)
.background(
RoundedRectangle(cornerRadius: Radius.xl)
.fill(.ultraThinMaterial)
.fill(AppColors.cardSurface)
)
.overlay(
RoundedRectangle(cornerRadius: Radius.xl)
.stroke(AppColors.border, lineWidth: 1)
.shadow(color: AppColors.cardShadow, radius: 8, x: 0, y: 4)
)
.clipShape(
RoundedRectangle(cornerRadius: Radius.xl)
Expand Down
89 changes: 89 additions & 0 deletions IPASignCraft/DesignSystem/Components/Common/ConsoleView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//
// ConsoleView.swift
// IPASignCraft
//
// Created by Saurav Nagpal on 09/06/26.
//

import SwiftUI

/// A reusable console/logs component for displaying terminal-style output.
///
/// Features:
/// - Displays monospaced log text
/// - Dark background with green text (terminal style)
/// - Clear button to reset logs
/// - Scrollable content area
/// - Customizable title and icon
struct ConsoleView: View {

// MARK: - Properties

/// Title displayed in the header
let title: String

/// SF Symbol icon name
let icon: String

/// Log content to display
@Binding var logContent: String

/// Callback when clear button is tapped
var onClear: (() -> Void)?

// MARK: - Body

var body: some View {

AppCard {

VStack(spacing: Spacing.sm) {

// Header with title and clear button
HStack {

Label(title, systemImage: icon)
.font(AppFont.body)

Spacer()

Button("Clear") {

logContent = ""
onClear?()
}
.font(AppFont.secondary)
}

Divider()
.opacity(0.3)

// Console output
ScrollView {

Text(logContent.isEmpty ? "No logs yet" : logContent)
.font(.system(.caption, design: .monospaced))
.frame(maxWidth: .infinity, alignment: .leading)
.padding(Spacing.base)
}
.frame(maxHeight: 260)
.background(Color.black.opacity(0.9))
.cornerRadius(8)
.foregroundColor(.green)
}
}
}
}

// MARK: - Preview

#Preview {

@State var sampleLogs = "Initializing inspection...\nLoading IPA file...\nAnalyzing bundle...\n✓ Inspection complete"

return ConsoleView(
title: "Console",
icon: "terminal",
logContent: $sampleLogs
)
}
77 changes: 77 additions & 0 deletions IPASignCraft/DesignSystem/Components/Common/ListRow/InfoRow.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//
// InfoRow.swift
// IPASignCraft
//
// Created by Saurav Nagpal on 09/06/26.
//

import SwiftUI

/// A reusable row component for displaying file or item information.
///
/// Typically used to show:
/// - Selected file summaries
/// - Status information
/// - Item details with icon and label
///
/// Components:
/// - Icon on the left (with custom color)
/// - Title and subtitle in the middle
/// - Spacer on the right
/// - Container styling from the design system
struct InfoRow: View {

// MARK: - Properties

/// SF Symbol icon name
let icon: String

/// Icon color
let color: Color

/// Primary text (main title)
let title: String

/// Secondary text (description)
let subtitle: String

// MARK: - Body

var body: some View {

HStack(spacing: Spacing.sm) {

Image(systemName: icon)
.foregroundColor(color)

VStack(alignment: .leading, spacing: 2) {

Text(title)
.font(AppFont.secondary)
.lineLimit(1)

Text(subtitle)
.font(AppFont.secondary)
.foregroundColor(AppColors.secondaryText)
}

Spacer()
}
.fieldContainer()
.accessibilityElement(children: .combine)
.accessibilityLabel(title)
.accessibilityValue(subtitle)
}
}

// MARK: - Preview

#Preview {

InfoRow(
icon: "doc.fill",
color: AppColors.accent,
title: "ExampleApp.ipa",
subtitle: "Ready for inspection"
)
}
33 changes: 33 additions & 0 deletions IPASignCraft/DesignSystem/Components/Common/ScreenShell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import SwiftUI

/// A small screen shell that provides a background and scrollable centered content area.
/// Use this to standardize page layout across screens.
struct ScreenShell<Content: View>: View {

private let background: AnyView
private let content: () -> Content

init<B: View>(background: B = WatercolorBackground(), @ViewBuilder content: @escaping () -> Content) {
self.background = AnyView(background)
self.content = content
}

var body: some View {
ZStack {
background

ScrollView {
content()
}
}
}
}

#Preview {
ScreenShell {
VStack {
Text("Screen Shell")
}
.frame(maxWidth: 800)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import SwiftUI

/// Reusable watercolor background used across multiple screens.
struct WatercolorBackground: View {

var body: some View {
GeometryReader { geo in
Image("homeBgWatercolor")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: geo.size.width, height: geo.size.height)
.clipped()
.overlay(Color.white.opacity(0.6))
}
}
}

#Preview {
WatercolorBackground()
}
52 changes: 52 additions & 0 deletions IPASignCraft/DesignSystem/Components/FileBrowserView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import SwiftUI
internal import UniformTypeIdentifiers

/// A small composed control used across the app for file selection.
/// Combines the existing FilePickerView and FileDropView into a single reusable component.
struct FileBrowserView: View {
let title: String?
@Binding var filePath: String
let supportedTypes: [UTType]

init(title: String? = nil, filePath: Binding<String>, supportedTypes: [UTType]) {
self.title = title
self._filePath = filePath
self.supportedTypes = supportedTypes
}

var body: some View {
VStack(alignment: .leading, spacing: Spacing.sm) {
if let title {
Text(title)
.font(AppFont.secondary)
}

HStack(spacing: Spacing.base) {
FilePickerView(
title: "Browse...",
supportedTypes: supportedTypes,
filePath: $filePath
)

FileDropView(
title: nil,
filePath: $filePath,
supportedTypes: supportedTypes
)
}
.frame(maxHeight: 100)
}
}
}

// MARK: - Preview

#Preview {
VStack(spacing: Spacing.base) {
FileBrowserView(title: "IPA File", filePath: .constant("/path/to/app.ipa"), supportedTypes: [.ipa])
.padding()

FileBrowserView(title: nil, filePath: .constant(""), supportedTypes: [.ipa])
.padding()
}
}
33 changes: 29 additions & 4 deletions IPASignCraft/DesignSystem/Components/FileDropView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ struct FileDropView: View {
style: StrokeStyle(lineWidth: 1.2, dash: [6])
)
.foregroundColor(
isHovering ? Color.blue.opacity(0.6) : Color.gray.opacity(0.4)
isHovering ? AppColors.accent.opacity(0.6) : Color.gray.opacity(0.38)
)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(Color(NSColor.controlBackgroundColor))
.fill(AppColors.cardSurface)
)

VStack(spacing: 6) {
Expand All @@ -48,7 +48,7 @@ struct FileDropView: View {

Text("or click to browse")
.font(.caption)
.foregroundColor(.secondary)
.foregroundColor(AppColors.secondaryText)
} else {
Text("IPA package loaded successfully")
.font(.caption)
Expand All @@ -57,9 +57,20 @@ struct FileDropView: View {
}
.padding(.vertical, 18)
}
.contentShape(RoundedRectangle(cornerRadius: 12))
.onTapGesture {
// optional: trigger file picker
openPanel()
}
.focusable(true)
.onHover { hovering in
withAnimation(.easeInOut(duration: 0.18)) {
isHovering = hovering
}
}
.accessibilityElement(children: .combine)
.accessibilityAddTraits(.isButton)
.accessibilityLabel(filePath.isEmpty ? "File drop target" : "File loaded")
.accessibilityHint("Click to browse, or drop an IPA file here.")
.onDrop(of: ["public.file-url"], isTargeted: $isHovering) { providers in

providers.first?.loadItem(forTypeIdentifier: "public.file-url",
Expand Down Expand Up @@ -89,6 +100,20 @@ struct FileDropView: View {
}
}
}

#if os(macOS)
private func openPanel() {
let panel = NSOpenPanel()
panel.allowedContentTypes = supportedTypes
panel.allowsMultipleSelection = false
panel.canChooseDirectories = false
panel.begin { response in
if response == .OK, let url = panel.url {
filePath = url.path
}
}
}
#endif
}

#Preview {
Expand Down
Loading
Loading