Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2025-12-31 14:45:41 -06:00
parent 1ecbbbf9ee
commit 0c5f27e33e
16 changed files with 375 additions and 176 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -1,7 +1,7 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "AppIcon-1024.png", "filename" : "AppIcon.png",
"idiom" : "universal", "idiom" : "universal",
"platform" : "ios", "platform" : "ios",
"size" : "1024x1024" "size" : "1024x1024"

View File

@ -117,6 +117,7 @@
}, },
"%lld." : { "%lld." : {
"comment" : "A numbered list item with a callout number and accompanying text. The first argument is the number of the item. The second argument is the text of the item.", "comment" : "A numbered list item with a callout number and accompanying text. The first argument is the number of the item. The second argument is the text of the item.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -140,6 +141,7 @@
}, },
"%lldpx" : { "%lldpx" : {
"comment" : "A text label displaying the size of the app icon. The argument is the size of the icon in pixels.", "comment" : "A text label displaying the size of the app icon. The argument is the size of the icon in pixels.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -186,6 +188,7 @@
}, },
"• Add to Assets.xcassets/AppIcon" : { "• Add to Assets.xcassets/AppIcon" : {
"comment" : "A step in the process of exporting app icons.", "comment" : "A step in the process of exporting app icons.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -208,6 +211,7 @@
} }
}, },
"• Call IconRenderer.renderAppIcon(config: .baccarat)" : { "• Call IconRenderer.renderAppIcon(config: .baccarat)" : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -230,6 +234,7 @@
} }
}, },
"• Run the preview in Xcode" : { "• Run the preview in Xcode" : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -253,6 +258,7 @@
}, },
"• Save the resulting UIImage to files" : { "• Save the resulting UIImage to files" : {
"comment" : "A step in the process of exporting app icons.", "comment" : "A step in the process of exporting app icons.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -276,6 +282,7 @@
}, },
"• Screenshot the 1024px icon" : { "• Screenshot the 1024px icon" : {
"comment" : "A step in the process of exporting app icons, describing how to take a screenshot of a 1024px icon.", "comment" : "A step in the process of exporting app icons, describing how to take a screenshot of a 1024px icon.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -299,6 +306,7 @@
}, },
"• Use an online tool to generate all sizes" : { "• Use an online tool to generate all sizes" : {
"comment" : "A bullet point in the \"How to Export Icons\" section, describing how to use an online tool to generate all sizes for an app icon.", "comment" : "A bullet point in the \"How to Export Icons\" section, describing how to use an online tool to generate all sizes for an app icon.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -528,6 +536,7 @@
}, },
"After generating:" : { "After generating:" : {
"comment" : "A heading for the instructions section of the icon generator view.", "comment" : "A heading for the instructions section of the icon generator view.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -551,6 +560,7 @@
}, },
"All Sizes" : { "All Sizes" : {
"comment" : "A heading that describes the various sizes of the app icon.", "comment" : "A heading that describes the various sizes of the app icon.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -597,6 +607,7 @@
}, },
"Alternative: Use an online tool" : { "Alternative: Use an online tool" : {
"comment" : "A section header that suggests using an online tool to generate app icon sizes.", "comment" : "A section header that suggests using an online tool to generate app icon sizes.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -666,6 +677,7 @@
}, },
"App Icon" : { "App Icon" : {
"comment" : "A label displayed above the preview of the app icon.", "comment" : "A label displayed above the preview of the app icon.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -689,6 +701,7 @@
}, },
"App Icon Preview" : { "App Icon Preview" : {
"comment" : "A header describing the preview of the app icon.", "comment" : "A header describing the preview of the app icon.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -1240,6 +1253,7 @@
}, },
"Blackjack" : { "Blackjack" : {
"comment" : "The name of a blackjack game.", "comment" : "The name of a blackjack game.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -1994,6 +2008,7 @@
}, },
"Generate & Save Icons" : { "Generate & Save Icons" : {
"comment" : "A button label that triggers the generation of app icons.", "comment" : "A button label that triggers the generation of app icons.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -2017,6 +2032,7 @@
}, },
"Generated Icons:" : { "Generated Icons:" : {
"comment" : "A label displayed above the list of generated icon filenames.", "comment" : "A label displayed above the list of generated icon filenames.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -2040,6 +2056,7 @@
}, },
"Generating..." : { "Generating..." : {
"comment" : "A text that appears while generating icons.", "comment" : "A text that appears while generating icons.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -2247,6 +2264,7 @@
}, },
"How to Export Icons" : { "How to Export Icons" : {
"comment" : "A section header explaining how to export app icons.", "comment" : "A section header explaining how to export app icons.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -2337,6 +2355,7 @@
}, },
"Icon" : { "Icon" : {
"comment" : "The title for the tab that displays the app icon preview.", "comment" : "The title for the tab that displays the app icon preview.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -2360,6 +2379,7 @@
}, },
"Icon Generator" : { "Icon Generator" : {
"comment" : "The title of the Icon Generator view.", "comment" : "The title of the Icon Generator view.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -2520,6 +2540,7 @@
}, },
"Launch" : { "Launch" : {
"comment" : "A tab label for the launch screen preview.", "comment" : "A tab label for the launch screen preview.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -2929,6 +2950,7 @@
}, },
"Option 1: Screenshot from Preview" : { "Option 1: Screenshot from Preview" : {
"comment" : "A description of one method for exporting app icons.", "comment" : "A description of one method for exporting app icons.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -2952,6 +2974,7 @@
}, },
"Option 2: Use IconRenderer in Code" : { "Option 2: Use IconRenderer in Code" : {
"comment" : "A description of how to use the `IconRenderer` in code to generate app icons.", "comment" : "A description of how to use the `IconRenderer` in code to generate app icons.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -2975,6 +2998,7 @@
}, },
"Other Game Icons" : { "Other Game Icons" : {
"comment" : "A label displayed above a section of the BrandingPreviewView", "comment" : "A label displayed above a section of the BrandingPreviewView",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -2998,6 +3022,7 @@
}, },
"Others" : { "Others" : {
"comment" : "The tab label for the section that previews branding assets for other games.", "comment" : "The tab label for the section that previews branding assets for other games.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -3411,6 +3436,7 @@
}, },
"Poker" : { "Poker" : {
"comment" : "The name of a poker game.", "comment" : "The name of a poker game.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -3640,6 +3666,7 @@
}, },
"Roulette" : { "Roulette" : {
"comment" : "The name of a roulette game.", "comment" : "The name of a roulette game.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -4326,6 +4353,7 @@
}, },
"These show how the same pattern works for other games" : { "These show how the same pattern works for other games" : {
"comment" : "A description below the section of the view that previews icons for other games.", "comment" : "A description below the section of the view that previews icons for other games.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -4691,6 +4719,7 @@
}, },
"Upload the 1024px icon to appicon.co or makeappicon.com to generate all sizes automatically." : { "Upload the 1024px icon to appicon.co or makeappicon.com to generate all sizes automatically." : {
"comment" : "A description of an alternative method for generating app icons.", "comment" : "A description of an alternative method for generating app icons.",
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {

View File

@ -350,6 +350,19 @@ struct SettingsView: View {
.padding(.horizontal) .padding(.horizontal)
.padding(.top, Design.Spacing.xSmall) .padding(.top, Design.Spacing.xSmall)
//
// DEBUG SECTION - Only visible in DEBUG builds
//
#if DEBUG
SheetSection(title: "DEBUG", icon: "ant.fill") {
BrandingDebugRows(
iconConfig: .baccarat,
launchConfig: .baccarat,
appName: "Baccarat"
)
}
#endif
// 11. App Version // 11. App Version
Text(appVersionString) Text(appVersionString)
.font(.system(size: Design.BaseFontSize.callout)) .font(.system(size: Design.BaseFontSize.callout))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -1,7 +1,7 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "AppIcon-1024.png", "filename" : "AppIcon.png",
"idiom" : "universal", "idiom" : "universal",
"platform" : "ios", "platform" : "ios",
"size" : "1024x1024" "size" : "1024x1024"

View File

@ -119,6 +119,7 @@
}, },
"%lld." : { "%lld." : {
"comment" : "A numbered list item with a callout number and accompanying text. The first argument is the number of the item. The second argument is the text of the item.", "comment" : "A numbered list item with a callout number and accompanying text. The first argument is the number of the item. The second argument is the text of the item.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -143,6 +144,7 @@
}, },
"%lldpx" : { "%lldpx" : {
"comment" : "A text label displaying the size of the app icon. The argument is the size of the icon in pixels.", "comment" : "A text label displaying the size of the app icon. The argument is the size of the icon in pixels.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -191,6 +193,7 @@
}, },
"• Add to Assets.xcassets/AppIcon" : { "• Add to Assets.xcassets/AppIcon" : {
"comment" : "A bullet point describing how to add an app icon to Xcode's asset catalog.", "comment" : "A bullet point describing how to add an app icon to Xcode's asset catalog.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -215,6 +218,7 @@
}, },
"• Call IconRenderer.renderAppIcon(config: .blackjack)" : { "• Call IconRenderer.renderAppIcon(config: .blackjack)" : {
"comment" : "A bullet point in the \"Option 2: Use IconRenderer in Code\" section of the BrandingPreviewView.", "comment" : "A bullet point in the \"Option 2: Use IconRenderer in Code\" section of the BrandingPreviewView.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -238,6 +242,7 @@
} }
}, },
"• Run the preview in Xcode" : { "• Run the preview in Xcode" : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -261,6 +266,7 @@
}, },
"• Save the resulting UIImage to files" : { "• Save the resulting UIImage to files" : {
"comment" : "A step in the process of exporting app icons.", "comment" : "A step in the process of exporting app icons.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -284,6 +290,7 @@
} }
}, },
"• Screenshot the 1024px icon" : { "• Screenshot the 1024px icon" : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -307,6 +314,7 @@
}, },
"• Use an online tool to generate all sizes" : { "• Use an online tool to generate all sizes" : {
"comment" : "A step in the process of exporting app icons.", "comment" : "A step in the process of exporting app icons.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -828,6 +836,7 @@
}, },
"After generating:" : { "After generating:" : {
"comment" : "A heading for instructions on how to use the IconGeneratorView.", "comment" : "A heading for instructions on how to use the IconGeneratorView.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -852,6 +861,7 @@
}, },
"All Sizes" : { "All Sizes" : {
"comment" : "A heading that describes the various sizes of the app icon.", "comment" : "A heading that describes the various sizes of the app icon.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -943,6 +953,7 @@
}, },
"Alternative: Use an online tool" : { "Alternative: Use an online tool" : {
"comment" : "A section header that suggests using an online tool to generate app icons.", "comment" : "A section header that suggests using an online tool to generate app icons.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -1013,6 +1024,7 @@
}, },
"App Icon" : { "App Icon" : {
"comment" : "A label displayed above the preview of the app icon.", "comment" : "A label displayed above the preview of the app icon.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -1037,6 +1049,7 @@
}, },
"App Icon Preview" : { "App Icon Preview" : {
"comment" : "A title for the preview of the app icon.", "comment" : "A title for the preview of the app icon.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -1105,6 +1118,7 @@
}, },
"Baccarat" : { "Baccarat" : {
"comment" : "The name of a casino game.", "comment" : "The name of a casino game.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -2385,9 +2399,6 @@
} }
} }
} }
},
"Deal Splittable Pair (8s)" : {
}, },
"DEALER" : { "DEALER" : {
"localizations" : { "localizations" : {
@ -3589,6 +3600,7 @@
}, },
"Generate & Save Icons" : { "Generate & Save Icons" : {
"comment" : "A button label that triggers icon generation and saving.", "comment" : "A button label that triggers icon generation and saving.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -3613,6 +3625,7 @@
}, },
"Generated Icons:" : { "Generated Icons:" : {
"comment" : "A label describing the list of icons that have been successfully generated.", "comment" : "A label describing the list of icons that have been successfully generated.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -3637,6 +3650,7 @@
}, },
"Generating..." : { "Generating..." : {
"comment" : "A label indicating that the app is currently generating icons.", "comment" : "A label indicating that the app is currently generating icons.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -3955,6 +3969,7 @@
}, },
"How to Export Icons" : { "How to Export Icons" : {
"comment" : "A section header explaining how to export app icons.", "comment" : "A section header explaining how to export app icons.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -4045,6 +4060,7 @@
}, },
"Icon" : { "Icon" : {
"comment" : "The label for the tab item representing the app icon preview.", "comment" : "The label for the tab item representing the app icon preview.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -4069,6 +4085,7 @@
}, },
"Icon Generator" : { "Icon Generator" : {
"comment" : "The title of the Icon Generator view.", "comment" : "The title of the Icon Generator view.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -4434,6 +4451,7 @@
}, },
"Launch" : { "Launch" : {
"comment" : "A tab in the BrandingPreviewView that links to the launch screen preview.", "comment" : "A tab in the BrandingPreviewView that links to the launch screen preview.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -5093,6 +5111,7 @@
}, },
"Option 1: Screenshot from Preview" : { "Option 1: Screenshot from Preview" : {
"comment" : "A description of one method for exporting app icons.", "comment" : "A description of one method for exporting app icons.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -5117,6 +5136,7 @@
}, },
"Option 2: Use IconRenderer in Code" : { "Option 2: Use IconRenderer in Code" : {
"comment" : "A subheading within the instructions section of the BrandingPreviewView.", "comment" : "A subheading within the instructions section of the BrandingPreviewView.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -5163,6 +5183,7 @@
}, },
"Other Game Icons" : { "Other Game Icons" : {
"comment" : "A label displayed above a section of the BrandingPreviewView that shows icons for other games.", "comment" : "A label displayed above a section of the BrandingPreviewView that shows icons for other games.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -5187,6 +5208,7 @@
}, },
"Others" : { "Others" : {
"comment" : "A tab label for the section displaying icons for other games.", "comment" : "A tab label for the section displaying icons for other games.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -5464,6 +5486,7 @@
} }
}, },
"Poker" : { "Poker" : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -5781,6 +5804,7 @@
}, },
"Roulette" : { "Roulette" : {
"comment" : "The name of a roulette card.", "comment" : "The name of a roulette card.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -7195,6 +7219,7 @@
}, },
"These show how the same pattern works for other games" : { "These show how the same pattern works for other games" : {
"comment" : "A description below the section of the view that previews icons for other games.", "comment" : "A description below the section of the view that previews icons for other games.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -7471,6 +7496,7 @@
}, },
"Upload the 1024px icon to appicon.co or makeappicon.com to generate all sizes automatically." : { "Upload the 1024px icon to appicon.co or makeappicon.com to generate all sizes automatically." : {
"comment" : "A description of an alternative method for generating app icons.", "comment" : "A description of an alternative method for generating app icons.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"localizations" : { "localizations" : {
"en" : { "en" : {

View File

@ -435,28 +435,27 @@ struct SettingsView: View {
// calls the corresponding debug function in GameState. // calls the corresponding debug function in GameState.
// //
#if DEBUG #if DEBUG
if let state = gameState {
SheetSection(title: "DEBUG", icon: "ant.fill") { SheetSection(title: "DEBUG", icon: "ant.fill") {
// Game-specific debug actions
if let state = gameState {
// Split Testing - deals a pair of 8s // Split Testing - deals a pair of 8s
Button { DebugActionRow(
title: "Deal Splittable Pair (8s)",
icon: "rectangle.split.2x1"
) {
triggerDebugDeal(state: state) triggerDebugDeal(state: state)
} label: {
HStack {
Text("Deal Splittable Pair (8s)")
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
.foregroundStyle(.orange)
Spacer()
Image(systemName: "rectangle.split.2x1")
.font(.system(size: Design.BaseFontSize.large))
.foregroundStyle(.orange)
}
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
} }
// Add new debug buttons here: Divider()
// Divider().background(Color.orange.opacity(Design.Opacity.hint)) .background(Color.orange.opacity(Design.Opacity.hint))
// Button { triggerDebugBlackjack(state: state) } label: { ... }
} }
// Branding tools (from CasinoKit)
BrandingDebugRows(
iconConfig: .blackjack,
launchConfig: .blackjack,
appName: "Blackjack"
)
} }
#endif #endif

View File

@ -72,6 +72,9 @@
// - SelectableRow (card-like selectable picker row) // - SelectableRow (card-like selectable picker row)
// - SelectionIndicator (checkmark circle) // - SelectionIndicator (checkmark circle)
// - BadgePill (capsule badge for values) // - BadgePill (capsule badge for values)
// - DebugNavigationRow (debug row with navigation)
// - DebugActionRow (debug row with action)
// - BrandingDebugRows (icon generator + branding preview rows)
// MARK: - Branding // MARK: - Branding

View File

@ -93,6 +93,10 @@
} }
} }
}, },
"%lld." : {
"comment" : "A numbered list item with a callout number and accompanying text. The first argument is the number of the item. The second argument is the text describing the item.",
"isCommentAutoGenerated" : true
},
"%lld%%" : { "%lld%%" : {
"comment" : "A text displaying the current volume percentage. The argument is a value between 0.0 (no volume) and 1.0 (full volume).", "comment" : "A text displaying the current volume percentage. The argument is a value between 0.0 (no volume) and 1.0 (full volume).",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
@ -141,6 +145,10 @@
} }
} }
}, },
"%lldpx" : {
"comment" : "A text label displaying the size of an app icon. The argument is the size of the icon in pixels.",
"isCommentAutoGenerated" : true
},
"•" : { "•" : {
"comment" : "A bullet point indicator.", "comment" : "A bullet point indicator.",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
@ -316,6 +324,10 @@
} }
} }
}, },
"1024 × 1024px" : {
"comment" : "A description of the size of the app icon.",
"isCommentAutoGenerated" : true
},
"Ace" : { "Ace" : {
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -365,6 +377,10 @@
} }
} }
}, },
"After generating:" : {
"comment" : "A heading describing the steps to take after generating app icons.",
"isCommentAutoGenerated" : true
},
"All game data is stored:" : { "All game data is stored:" : {
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -387,6 +403,10 @@
} }
} }
}, },
"App Icon" : {
"comment" : "The title of the section displaying the app icon preview.",
"isCommentAutoGenerated" : true
},
"App Icon Preview" : { "App Icon Preview" : {
"comment" : "A title for the preview section of the icon export view.", "comment" : "A title for the preview section of the icon export view.",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
@ -1099,6 +1119,14 @@
} }
} }
}, },
"Generate & Save Icon" : {
"comment" : "A button label that triggers icon generation and saving.",
"isCommentAutoGenerated" : true
},
"Generating..." : {
"comment" : "A button label indicating that the app is currently generating icons.",
"isCommentAutoGenerated" : true
},
"Global" : { "Global" : {
"comment" : "Title for the \"Global\" tab in the statistics view.", "comment" : "Title for the \"Global\" tab in the statistics view.",
"isCommentAutoGenerated" : true "isCommentAutoGenerated" : true
@ -1202,6 +1230,14 @@
} }
} }
}, },
"Icon" : {
"comment" : "The label for the tab that displays the app icon preview.",
"isCommentAutoGenerated" : true
},
"Icon Generator" : {
"comment" : "The title of the icon generator view.",
"isCommentAutoGenerated" : true
},
"If you choose to enable iCloud sync:" : { "If you choose to enable iCloud sync:" : {
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -1400,6 +1436,10 @@
} }
} }
}, },
"Launch" : {
"comment" : "A tab label for the launch screen preview.",
"isCommentAutoGenerated" : true
},
"Locally on your device using iOS standard storage" : { "Locally on your device using iOS standard storage" : {
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -1578,6 +1618,10 @@
}, },
"No Session History" : { "No Session History" : {
},
"Note: iOS uses a single 1024px icon" : {
"comment" : "A note explaining that iOS uses a single 1024px icon.",
"isCommentAutoGenerated" : true
}, },
"Our apps do not integrate with third-party services that collect user data. We do not share any information with third parties." : { "Our apps do not integrate with third-party services that collect user data. We do not share any information with third parties." : {
"localizations" : { "localizations" : {
@ -2106,6 +2150,10 @@
} }
} }
}, },
"To Export" : {
"comment" : "A section header explaining how to export branding assets.",
"isCommentAutoGenerated" : true
},
"TOTAL" : { "TOTAL" : {
"comment" : "A label displayed alongside the total winnings in the result banner.", "comment" : "A label displayed alongside the total winnings in the result banner.",
"isCommentAutoGenerated" : true "isCommentAutoGenerated" : true
@ -2148,6 +2196,10 @@
} }
} }
}, },
"Use the Icon Generator in Settings → DEBUG to save the 1024px icon to the Files app, then add it to Xcode's Assets.xcassets/AppIcon." : {
"comment" : "Instructions for exporting an app icon.",
"isCommentAutoGenerated" : true
},
"VIP" : { "VIP" : {
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -2344,6 +2396,10 @@
"comment" : "A label for the worst session amount in the statistics view.", "comment" : "A label for the worst session amount in the statistics view.",
"isCommentAutoGenerated" : true "isCommentAutoGenerated" : true
}, },
"Xcode automatically generates all required sizes from the 1024px source." : {
"comment" : "A footnote explaining that Xcode generates all app icon sizes from the 1024px source image.",
"isCommentAutoGenerated" : true
},
"You can disable iCloud sync at any time in the app settings" : { "You can disable iCloud sync at any time in the app settings" : {
"comment" : "Text in the Privacy Policy View explaining how to disable iCloud sync in the app settings.", "comment" : "Text in the Privacy Policy View explaining how to disable iCloud sync in the app settings.",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,

View File

@ -57,14 +57,27 @@ public struct AppIconView: View {
self.size = size self.size = size
} }
// iOS clips app icons with a superellipse mask. Inset decorative elements // Size calculations
// to prevent clipping. The inset is approximately 4% of the icon size.
private var borderInset: CGFloat { size * 0.04 }
private var insetCornerRadius: CGFloat { (size - borderInset * 2) * 0.18 }
private var iconSize: CGFloat { size * 0.35 } private var iconSize: CGFloat { size * 0.35 }
private var titleSize: CGFloat { size * 0.12 }
private var subtitleSize: CGFloat { size * 0.25 } private var subtitleSize: CGFloat { size * 0.25 }
private var borderWidth: CGFloat { size * 0.015 }
/// Dynamic title size based on text length.
/// Shorter titles get larger fonts, longer titles shrink to fit within the border.
private var titleSize: CGFloat {
let baseSize = size * 0.12
let length = config.title.count
// Scale factor: full size for 6 chars, progressively smaller for longer
let scaleFactor: CGFloat = switch length {
case ...6: 1.0 // "CASINO", "POKER"
case 7: 0.95 //
case 8: 0.85 // "BACCARAT"
case 9: 0.75 // "BLACKJACK"
default: 0.65 // Very long titles
}
return baseSize * scaleFactor
}
public var body: some View { public var body: some View {
ZStack { ZStack {
@ -83,22 +96,6 @@ public struct AppIconView: View {
DiamondPatternOverlay(size: size) DiamondPatternOverlay(size: size)
.opacity(0.08) .opacity(0.08)
// Decorative border - inset to avoid iOS mask clipping
RoundedRectangle(cornerRadius: insetCornerRadius)
.strokeBorder(
LinearGradient(
colors: [
config.accentColor,
config.accentColor.opacity(0.6),
config.accentColor
],
startPoint: .topLeading,
endPoint: .bottomTrailing
),
lineWidth: borderWidth
)
.padding(borderInset)
// Content // Content
VStack(spacing: size * 0.03) { VStack(spacing: size * 0.03) {
// Icon symbol // Icon symbol
@ -187,6 +184,38 @@ private struct DiamondPatternOverlay: View {
.background(Color.gray) .background(Color.gray)
} }
#Preview("Title Scaling") {
let shortTitle = AppIconConfig(title: "POKER", iconSymbol: "suit.diamond.fill")
let mediumTitle = AppIconConfig(title: "BACCARAT", iconSymbol: "suit.spade.fill")
let longTitle = AppIconConfig(
title: "BLACKJACK",
subtitle: "21",
iconSymbol: "suit.club.fill",
primaryColor: Color(red: 0.05, green: 0.35, blue: 0.15),
secondaryColor: Color(red: 0.03, green: 0.2, blue: 0.1)
)
return HStack(spacing: 20) {
VStack {
AppIconView(config: shortTitle, size: 180)
.clipShape(.rect(cornerRadius: 180 * 0.22))
Text("6 chars").font(.caption)
}
VStack {
AppIconView(config: mediumTitle, size: 180)
.clipShape(.rect(cornerRadius: 180 * 0.22))
Text("8 chars").font(.caption)
}
VStack {
AppIconView(config: longTitle, size: 180)
.clipShape(.rect(cornerRadius: 180 * 0.22))
Text("9 chars").font(.caption)
}
}
.padding()
.background(Color.gray)
}
#Preview("All Sizes") { #Preview("All Sizes") {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))], spacing: 20) { LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))], spacing: 20) {
ForEach([180, 120, 87, 60, 40], id: \.self) { size in ForEach([180, 120, 87, 60, 40], id: \.self) { size in

View File

@ -9,18 +9,14 @@
import SwiftUI import SwiftUI
/// Preview view for app branding assets. /// Preview view for app branding assets.
/// Use this during development to preview and export icons. /// Use this during development to preview icons and launch screens.
public struct BrandingPreviewView: View { public struct BrandingPreviewView: View {
let iconConfig: AppIconConfig let iconConfig: AppIconConfig
let launchConfig: LaunchScreenConfig let launchConfig: LaunchScreenConfig
let appName: String let appName: String
/// Other game configs for comparison preview.
let otherGames: [(name: String, config: AppIconConfig)]
// Development view: fixed sizes acceptable // Development view: fixed sizes acceptable
private let largePreviewSize: CGFloat = 300 private let largePreviewSize: CGFloat = 300
private let comparisonIconSize: CGFloat = 150
private let iconCornerRadiusRatio: CGFloat = 0.22 private let iconCornerRadiusRatio: CGFloat = 0.22
/// Creates a branding preview view. /// Creates a branding preview view.
@ -28,17 +24,14 @@ public struct BrandingPreviewView: View {
/// - iconConfig: The app icon configuration for this game. /// - iconConfig: The app icon configuration for this game.
/// - launchConfig: The launch screen configuration for this game. /// - launchConfig: The launch screen configuration for this game.
/// - appName: The app name for display purposes. /// - appName: The app name for display purposes.
/// - otherGames: Other game configs for side-by-side comparison (optional).
public init( public init(
iconConfig: AppIconConfig, iconConfig: AppIconConfig,
launchConfig: LaunchScreenConfig, launchConfig: LaunchScreenConfig,
appName: String, appName: String
otherGames: [(name: String, config: AppIconConfig)] = []
) { ) {
self.iconConfig = iconConfig self.iconConfig = iconConfig
self.launchConfig = launchConfig self.launchConfig = launchConfig
self.appName = appName self.appName = appName
self.otherGames = otherGames
} }
public var body: some View { public var body: some View {
@ -53,20 +46,9 @@ public struct BrandingPreviewView: View {
.clipShape(.rect(cornerRadius: largePreviewSize * iconCornerRadiusRatio)) .clipShape(.rect(cornerRadius: largePreviewSize * iconCornerRadiusRatio))
.shadow(radius: 20) .shadow(radius: 20)
Text("All Sizes") Text("1024 × 1024px")
.font(.title2.bold())
LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))], spacing: 20) {
ForEach([180, 120, 87, 60, 40], id: \.self) { size in
VStack {
AppIconView(config: iconConfig, size: CGFloat(size))
.clipShape(.rect(cornerRadius: CGFloat(size) * iconCornerRadiusRatio))
Text("\(size)px")
.font(.caption) .font(.caption)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
}
}
}
instructionsSection instructionsSection
} }
@ -81,60 +63,17 @@ public struct BrandingPreviewView: View {
.tabItem { .tabItem {
Label("Launch", systemImage: "rectangle.portrait.fill") Label("Launch", systemImage: "rectangle.portrait.fill")
} }
// Other Games Preview (if provided)
if !otherGames.isEmpty {
ScrollView {
VStack(spacing: 32) {
Text("Other Game Icons")
.font(.largeTitle.bold())
LazyVGrid(columns: [GridItem(.adaptive(minimum: 160))], spacing: 20) {
ForEach(otherGames, id: \.name) { game in
VStack {
AppIconView(config: game.config, size: comparisonIconSize)
.clipShape(.rect(cornerRadius: comparisonIconSize * iconCornerRadiusRatio))
Text(game.name)
.font(.caption)
}
}
}
Text("These show how the same pattern works for other games")
.font(.callout)
.foregroundStyle(.secondary)
}
.padding()
}
.tabItem {
Label("Others", systemImage: "square.grid.2x2")
}
}
} }
} }
private var instructionsSection: some View { private var instructionsSection: some View {
VStack(alignment: .leading, spacing: 12) { VStack(alignment: .leading, spacing: 12) {
Text("How to Export Icons") Text("To Export")
.font(.headline) .font(.headline)
VStack(alignment: .leading, spacing: 8) { Text("Use the Icon Generator in Settings → DEBUG to save the 1024px icon to the Files app, then add it to Xcode's Assets.xcassets/AppIcon.")
Text("Option 1: Screenshot from Preview")
.font(.subheadline.bold())
Text("• Run the preview in Xcode")
Text("• Screenshot the 1024px icon")
Text("• Use an online tool to generate all sizes")
}
VStack(alignment: .leading, spacing: 8) {
Text("Option 2: Use IconRenderer in Code")
.font(.subheadline.bold())
Text("• Call IconRenderer.renderAppIcon(config: .\(appName.lowercased()))")
Text("• Save the resulting UIImage to files")
Text("• Add to Assets.xcassets/AppIcon")
}
}
.font(.callout) .font(.callout)
}
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
.padding() .padding()

View File

@ -14,9 +14,9 @@ public struct IconGeneratorView: View {
let config: AppIconConfig let config: AppIconConfig
let appName: String let appName: String
@State private var status: String = "Tap the button to generate icons" @State private var status: String = "Tap the button to generate the icon"
@State private var isGenerating = false @State private var isGenerating = false
@State private var generatedIcons: [GeneratedIconInfo] = [] @State private var generatedIcon: GeneratedIconInfo?
// Development view: fixed sizes acceptable // Development view: fixed sizes acceptable
private let previewSize: CGFloat = 200 private let previewSize: CGFloat = 200
@ -46,7 +46,7 @@ public struct IconGeneratorView: View {
// Generate button // Generate button
Button { Button {
Task { Task {
await generateIcons() await generateIcon()
} }
} label: { } label: {
HStack { HStack {
@ -54,7 +54,7 @@ public struct IconGeneratorView: View {
ProgressView() ProgressView()
.tint(.white) .tint(.white)
} }
Text(isGenerating ? "Generating..." : "Generate & Save Icons") Text(isGenerating ? "Generating..." : "Generate & Save Icon")
} }
.font(.headline) .font(.headline)
.foregroundStyle(.white) .foregroundStyle(.white)
@ -73,25 +73,18 @@ public struct IconGeneratorView: View {
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
.padding(.horizontal) .padding(.horizontal)
// Generated icons // Generated icon confirmation
if !generatedIcons.isEmpty { if let icon = generatedIcon {
VStack(alignment: .leading, spacing: 12) {
Text("Generated Icons:")
.font(.headline)
ForEach(generatedIcons) { icon in
HStack { HStack {
Image(systemName: "checkmark.circle.fill") Image(systemName: "checkmark.circle.fill")
.foregroundStyle(.green) .foregroundStyle(.green)
Text(icon.filename) Text(icon.filename)
.font(.caption.monospaced()) .font(.callout.monospaced())
Spacer() Spacer()
Text("\(Int(icon.size))px") Text("\(Int(icon.size))px")
.font(.caption) .font(.callout)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
} }
}
}
.padding() .padding()
.background(Color.green.opacity(0.1)) .background(Color.green.opacity(0.1))
.clipShape(.rect(cornerRadius: 12)) .clipShape(.rect(cornerRadius: 12))
@ -115,16 +108,16 @@ public struct IconGeneratorView: View {
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
instructionRow(number: 1, text: "Open Files app on your device/simulator") instructionRow(number: 1, text: "Open Files app on your device/simulator")
instructionRow(number: 2, text: "Navigate to: On My iPhone → \(appName)") instructionRow(number: 2, text: "Navigate to: On My iPhone → \(appName)")
instructionRow(number: 3, text: "Find the AppIcon-1024.png file") instructionRow(number: 3, text: "Find AppIcon.png (1024×1024)")
instructionRow(number: 4, text: "AirDrop or share to your Mac") instructionRow(number: 4, text: "AirDrop or share to your Mac")
instructionRow(number: 5, text: "Drag into Xcode's Assets.xcassets/AppIcon") instructionRow(number: 5, text: "Drag into Xcode's Assets.xcassets/AppIcon")
} }
Divider() Divider()
Text("Alternative: Use an online tool") Text("Note: iOS uses a single 1024px icon")
.font(.subheadline.bold()) .font(.subheadline.bold())
Text("Upload the 1024px icon to appicon.co or makeappicon.com to generate all sizes automatically.") Text("Xcode automatically generates all required sizes from the 1024px source.")
.font(.caption) .font(.caption)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
} }
@ -145,50 +138,32 @@ public struct IconGeneratorView: View {
} }
@MainActor @MainActor
private func generateIcons() async { private func generateIcon() async {
isGenerating = true isGenerating = true
generatedIcons = [] generatedIcon = nil
status = "Generating icons..." status = "Generating icon..."
let sizes: [(CGFloat, String)] = [
(1024, "AppIcon-1024"),
(180, "AppIcon-180"),
(120, "AppIcon-120"),
(87, "AppIcon-87"),
(80, "AppIcon-80"),
(60, "AppIcon-60"),
(40, "AppIcon-40")
]
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
for (size, name) in sizes { // Render the 1024px icon (the only size needed for modern iOS)
// Render the icon let view = AppIconView(config: config, size: 1024)
let view = AppIconView(config: config, size: size)
let renderer = ImageRenderer(content: view) let renderer = ImageRenderer(content: view)
renderer.scale = 1.0 renderer.scale = 1.0
if let uiImage = renderer.uiImage, if let uiImage = renderer.uiImage,
let data = uiImage.pngData() { let data = uiImage.pngData() {
let filename = "\(name).png" let filename = "AppIcon.png"
let fileURL = documentsPath.appending(path: filename) let fileURL = documentsPath.appending(path: filename)
do { do {
try data.write(to: fileURL) try data.write(to: fileURL)
generatedIcons.append(GeneratedIconInfo(filename: filename, size: size)) generatedIcon = GeneratedIconInfo(filename: filename, size: 1024)
status = "✅ Icon saved to Documents folder!\nOpen Files app to find it."
} catch { } catch {
status = "Error saving \(filename): \(error.localizedDescription)" status = "Error saving icon: \(error.localizedDescription)"
} }
}
// Small delay for UI feedback
try? await Task.sleep(for: .milliseconds(100))
}
if generatedIcons.count == sizes.count {
status = "✅ All icons saved to Documents folder!\nOpen Files app to find them."
} else { } else {
status = "⚠️ Some icons failed to generate" status = "⚠️ Failed to render icon"
} }
isGenerating = false isGenerating = false

View File

@ -0,0 +1,130 @@
//
// DebugSection.swift
// CasinoKit
//
// Reusable debug section for settings views.
// Only visible in DEBUG builds.
//
import SwiftUI
/// A debug row that navigates to a destination view.
public struct DebugNavigationRow<Destination: View>: View {
let title: String
let icon: String
let destination: () -> Destination
public init(
title: String,
icon: String,
@ViewBuilder destination: @escaping () -> Destination
) {
self.title = title
self.icon = icon
self.destination = destination
}
public var body: some View {
NavigationLink {
destination()
} label: {
HStack {
Text(title)
.font(.system(size: CasinoDesign.BaseFontSize.body, weight: .medium))
.foregroundStyle(.orange)
Spacer()
Image(systemName: icon)
.font(.system(size: CasinoDesign.BaseFontSize.large))
.foregroundStyle(.orange)
}
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
}
}
}
/// A debug row that performs an action.
public struct DebugActionRow: View {
let title: String
let icon: String
let action: () -> Void
public init(
title: String,
icon: String,
action: @escaping () -> Void
) {
self.title = title
self.icon = icon
self.action = action
}
public var body: some View {
Button(action: action) {
HStack {
Text(title)
.font(.system(size: CasinoDesign.BaseFontSize.body, weight: .medium))
.foregroundStyle(.orange)
Spacer()
Image(systemName: icon)
.font(.system(size: CasinoDesign.BaseFontSize.large))
.foregroundStyle(.orange)
}
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
}
}
}
/// Branding debug rows for icon generation and preview.
/// Use these in your app's debug section.
public struct BrandingDebugRows: View {
let iconConfig: AppIconConfig
let launchConfig: LaunchScreenConfig
let appName: String
public init(
iconConfig: AppIconConfig,
launchConfig: LaunchScreenConfig,
appName: String
) {
self.iconConfig = iconConfig
self.launchConfig = launchConfig
self.appName = appName
}
public var body: some View {
DebugNavigationRow(
title: "Icon Generator",
icon: "app.badge.fill"
) {
IconGeneratorView(config: iconConfig, appName: appName)
}
Divider()
.background(Color.orange.opacity(0.3))
DebugNavigationRow(
title: "Branding Preview",
icon: "paintpalette.fill"
) {
BrandingPreviewView(
iconConfig: iconConfig,
launchConfig: launchConfig,
appName: appName
)
}
}
}
#Preview {
NavigationStack {
List {
Section("Debug") {
BrandingDebugRows(
iconConfig: .example,
launchConfig: .example,
appName: "Casino"
)
}
}
}
}