@momo-kits/native-kits 0.167.1-duo.3 → 0.167.1-duo.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CLAUDE.md +17 -8
  2. package/ios/Application/ApplicationEnvironment.swift +6 -0
  3. package/ios/Application/Components.swift +1 -3
  4. package/ios/Application/HeaderRight.swift +19 -27
  5. package/ios/Application/Navigation/BottomTab/BottomTab.swift +9 -30
  6. package/ios/Application/Navigation/BottomTab/BottomTabBar.swift +2 -5
  7. package/ios/Application/Navigation/HeaderUser.swift +1 -7
  8. package/ios/Application/Navigation/NavigationContainer.swift +45 -45
  9. package/ios/Application/Navigation/Navigator.swift +122 -29
  10. package/ios/Application/Navigation/NavigatorManager.swift +13 -6
  11. package/ios/Application/Navigation/Overplay/BottomSheet.swift +4 -41
  12. package/ios/Application/Navigation/Overplay/ModalScreen.swift +1 -5
  13. package/ios/Application/Navigation/component/Header.swift +4 -12
  14. package/ios/Application/Navigation/component/HeaderBackground.swift +2 -3
  15. package/ios/Application/Navigation/component/NavigationFooter.swift +1 -1
  16. package/ios/Application/Navigation/component/NavigationHeaderRight.swift +9 -61
  17. package/ios/Application/StackScreen.swift +18 -135
  18. package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +59 -5
  19. package/ios/Colors+Radius+Spacing/Colors.swift +1 -1
  20. package/ios/Extensions/Color++.swift +20 -9
  21. package/ios/Extensions/OptionalEnvironmentObject.swift +12 -0
  22. package/ios/Icon/Icon.swift +14 -22
  23. package/ios/Icon/Icons.swift +1329 -0
  24. package/ios/Image/Image.swift +16 -5
  25. package/ios/Lottie/LottieView.swift +4 -2
  26. package/ios/Popup/PopupDisplay.swift +68 -84
  27. package/ios/Popup/PopupNotify.swift +32 -43
  28. package/ios/Popup/PopupPromotion.swift +6 -18
  29. package/ios/Resources/dot_loading.json +558 -0
  30. package/ios/Resources/lottie_circle_loader.json +1 -0
  31. package/ios/Template/TrustBanner/TrustBanner.swift +69 -31
  32. package/ios/native-kits.podspec +1 -1
  33. package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
  34. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +0 -1
  35. package/package.json +1 -1
  36. package/scripts/gen-ios-icons.mjs +36 -0
  37. package/.claude/scheduled_tasks.lock +0 -1
  38. package/.claude/settings.local.json +0 -185
  39. package/ios/Application/Layout/ScreenLayout.swift +0 -196
  40. package/ios/Application/Navigation/BottomTab/DuoTabBar.swift +0 -275
  41. package/ios/Application/Navigation/component/DuoScrollGradient.swift +0 -55
  42. package/ios/Application/Navigation/component/SideBar.swift +0 -155
  43. package/ios/Popup/PopupLayout.swift +0 -45
  44. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DuoTabDemo.swift +0 -76
  45. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IPhoneDuoDemo.swift +0 -170
@@ -12,14 +12,14 @@ import SDWebImageSwiftUI
12
12
  let jsonUrl = "https://static.momocdn.net/app/json/component-kits/design_system.json"
13
13
 
14
14
  public struct TrustBannerData: Decodable {
15
- let content: [String: String]
16
- let subContent: [String: String]
17
- let pciImage: String
18
- let sslImage: String
19
- let urlConfig: String
20
- let icons: [String]
21
- let momoImage: String
22
-
15
+ public let content: [String: String]
16
+ public let subContent: [String: String]
17
+ public let pciImage: String?
18
+ public let momoImage: String?
19
+ public let sslImage: String?
20
+ public let icons: [String]?
21
+ public let urlConfig: String
22
+
23
23
  enum CodingKeys: String, CodingKey {
24
24
  case content
25
25
  case subContent
@@ -29,6 +29,38 @@ public struct TrustBannerData: Decodable {
29
29
  case momoImage
30
30
  case urlConfig = "url_config"
31
31
  }
32
+
33
+ public init(
34
+ content: [String: String] = ["vi": "", "en": ""],
35
+ subContent: [String: String] = ["vi": "", "en": ""],
36
+ pciImage: String? = nil,
37
+ momoImage: String? = nil,
38
+ sslImage: String? = nil,
39
+ icons: [String]? = nil,
40
+ urlConfig: String = ""
41
+ ) {
42
+ self.content = content
43
+ self.subContent = subContent
44
+ self.pciImage = pciImage
45
+ self.momoImage = momoImage
46
+ self.sslImage = sslImage
47
+ self.icons = icons
48
+ self.urlConfig = urlConfig
49
+ }
50
+
51
+ // Missing keys fall back to the init defaults, like the Compose data class.
52
+ public init(from decoder: Decoder) throws {
53
+ let container = try decoder.container(keyedBy: CodingKeys.self)
54
+ self.init(
55
+ content: try container.decodeIfPresent([String: String].self, forKey: .content) ?? ["vi": "", "en": ""],
56
+ subContent: try container.decodeIfPresent([String: String].self, forKey: .subContent) ?? ["vi": "", "en": ""],
57
+ pciImage: try container.decodeIfPresent(String.self, forKey: .pciImage),
58
+ momoImage: try container.decodeIfPresent(String.self, forKey: .momoImage),
59
+ sslImage: try container.decodeIfPresent(String.self, forKey: .sslImage),
60
+ icons: try container.decodeIfPresent([String].self, forKey: .icons),
61
+ urlConfig: try container.decodeIfPresent(String.self, forKey: .urlConfig) ?? ""
62
+ )
63
+ }
32
64
  }
33
65
 
34
66
  let defaultBanner = TrustBannerData(
@@ -39,15 +71,15 @@ let defaultBanner = TrustBannerData(
39
71
  "en": "Learn more"
40
72
  ],
41
73
  pciImage: "https://static.momocdn.net/app/img/kits/trustBanner/pci.png",
74
+ momoImage: "https://static.momocdn.net/app/img/kits/trustBanner/ic_secu.png",
42
75
  sslImage: "https://static.momocdn.net/app/img/kits/trustBanner/ssl.png",
43
- urlConfig: "login_and_security",
44
76
  icons: [
45
77
  "https://static.momocdn.net/app/img/kits/trustBanner/ic_viettinbank.png",
46
78
  "https://static.momocdn.net/app/img/kits/trustBanner/ic_agribank.png",
47
79
  "https://static.momocdn.net/app/img/kits/trustBanner/ic_vietcombank.png",
48
80
  "https://static.momocdn.net/app/img/kits/trustBanner/ic_bidv.png"
49
81
  ],
50
- momoImage: "https://static.momocdn.net/app/img/kits/trustBanner/ic_secu.png"
82
+ urlConfig: "login_and_security"
51
83
  )
52
84
 
53
85
  public struct TrustBanner: View {
@@ -60,22 +92,25 @@ public struct TrustBanner: View {
60
92
  self.onPress = onPress
61
93
  }
62
94
 
95
+ private var banner: TrustBannerData {
96
+ applicationEnvironment.config?.trustBanner ?? defaultBanner
97
+ }
98
+
63
99
  func onPressSecurity() {
64
- if (onPress != nil) {
65
- onPress!(applicationEnvironment.config?.trustBanner?.urlConfig ?? defaultBanner.urlConfig)
66
- }
100
+ onPress?(banner.urlConfig)
67
101
  }
68
-
69
-
102
+
103
+
70
104
  public var body: some View {
71
105
  HStack(alignment: .center, spacing: 8) {
72
- WebImage(url: URL(string: applicationEnvironment.config?.trustBanner?.momoImage ?? defaultBanner.momoImage))
73
- .resizable()
74
- .scaledToFit()
75
- .frame(width: 64, height: 64)
106
+ if let momoImage = banner.momoImage {
107
+ WebImage(url: URL(string: momoImage))
108
+ .resizable()
109
+ .scaledToFit()
110
+ .frame(width: 64, height: 64)
111
+ }
76
112
  VStack(alignment: .leading){
77
- Text(applicationEnvironment.config?.trustBanner?.content[localize.currentLanguage]
78
- ?? localize.translateData(defaultBanner.content))
113
+ Text(localize.translateData(banner.content))
79
114
  .foregroundColor(Color(hex: "484848"))
80
115
  .lineLimit(2)
81
116
  .font(.system(size: 13, weight: Font.Weight.regular))
@@ -84,8 +119,7 @@ public struct TrustBanner: View {
84
119
  .padding(.bottom, 8)
85
120
  HStack {
86
121
  HStack(spacing: 0) {
87
- Text(applicationEnvironment.config?.trustBanner?.subContent[localize.currentLanguage]
88
- ?? localize.translateData(defaultBanner.subContent))
122
+ Text(localize.translateData(banner.subContent))
89
123
  .foregroundColor(Colors.pink03)
90
124
  .font(.system(size: 13, weight: .bold))
91
125
  .ignoreBoldTextSetting()
@@ -98,14 +132,18 @@ public struct TrustBanner: View {
98
132
 
99
133
  Spacer()
100
134
  HStack{
101
- WebImage(url: URL(string: applicationEnvironment.config?.trustBanner?.pciImage ?? defaultBanner.pciImage))
102
- .resizable()
103
- .scaledToFit()
104
- .frame(width: 24, height: 20)
105
- WebImage(url: URL(string: applicationEnvironment.config?.trustBanner?.sslImage ?? defaultBanner.sslImage))
106
- .resizable()
107
- .scaledToFit()
108
- .frame(width: 52, height: 20)
135
+ if let pciImage = banner.pciImage {
136
+ WebImage(url: URL(string: pciImage))
137
+ .resizable()
138
+ .scaledToFit()
139
+ .frame(width: 24, height: 20)
140
+ }
141
+ if let sslImage = banner.sslImage {
142
+ WebImage(url: URL(string: sslImage))
143
+ .resizable()
144
+ .scaledToFit()
145
+ .frame(width: 52, height: 20)
146
+ }
109
147
  }
110
148
  }
111
149
 
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'MoMoUIKits'
3
- s.version = '0.167.1-duo.3'
3
+ s.version = '0.167.1-beta.4'
4
4
  s.summary = 'MoMoUIKits for iOS'
5
5
  s.homepage = 'https://momo.vn'
6
6
  s.license = { :type => 'MIT' }
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'MoMoUIKitsDemo'
3
- s.version = '0.167.1-duo.3'
3
+ s.version = '0.167.1-beta.4'
4
4
  s.summary = 'Demo browser for MoMoUIKits SwiftUI components'
5
5
  s.homepage = 'https://momo.vn'
6
6
  s.license = { :type => 'MIT' }
@@ -85,7 +85,6 @@ public let ScreenUsages: [ScreenUsage] = [
85
85
  ScreenUsage(title: "DateTimePicker", destination: AnyView(DateTimePickerDemo()), icon: getIcon(icon: "Layout"), hostsOwnChrome: true),
86
86
  ScreenUsage(title: "Navigation", destination: AnyView(NavigationDemo()), icon: getIcon(icon: "Layout")),
87
87
  ScreenUsage(title: "Bottom Tab", destination: AnyView(BottomTabDemo()), icon: getIcon(icon: "Layout"), hostsOwnChrome: true),
88
- ScreenUsage(title: "iPhone Duo", destination: AnyView(IPhoneDuoDemo()), icon: getIcon(icon: "Layout")),
89
88
  ScreenUsage(title: "Animated Header", destination: AnyView(AnimatedHeaderDemo()), icon: getIcon(icon: "Layout")),
90
89
  ScreenUsage(title: "Theme", destination: AnyView(ThemeDemo()), icon: getIcon(icon: "Typography")),
91
90
  ScreenUsage(title: "Localize", destination: AnyView(LocalizeDemo()), icon: getIcon(icon: "Typography")),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/native-kits",
3
- "version": "0.167.1-duo.3",
3
+ "version": "0.167.1-duo.4",
4
4
  "private": false,
5
5
  "dependeNavigationncies": {},
6
6
  "devDependencies": {},
@@ -0,0 +1,36 @@
1
+ // Generates ios/Icon/Icons.swift from the Compose icon map so both kits ship the same
2
+ // compiled-in icons. Run from anywhere: `node scripts/gen-ios-icons.mjs`.
3
+ import { readFileSync, writeFileSync } from "fs";
4
+ import { dirname, join } from "path";
5
+ import { fileURLToPath } from "url";
6
+
7
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
8
+ const source = join(root, "compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt");
9
+ const target = join(root, "ios/Icon/Icons.swift");
10
+
11
+ const kotlin = readFileSync(source, "utf8");
12
+ const kotlinString = String.raw`"((?:[^"\\]|\\.)*)"`;
13
+ const unescapeKotlin = (s) => s.replace(/\\(["\\$])/g, "$1");
14
+ const swiftString = (s) => `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
15
+
16
+ const icons = [...kotlin.matchAll(new RegExp(`^\\s*${kotlinString} to ${kotlinString}`, "gm"))]
17
+ .map(([, key, url]) => [unescapeKotlin(key), unescapeKotlin(url)]);
18
+ const noThemeBlock = kotlin.match(/val noThemeIcons = listOf\(([^)]*)\)/);
19
+ const noTheme = noThemeBlock
20
+ ? [...noThemeBlock[1].matchAll(new RegExp(kotlinString, "g"))].map(([, s]) => unescapeKotlin(s))
21
+ : [];
22
+
23
+ if (icons.length === 0) throw new Error(`No icons parsed from ${source}`);
24
+ if (new Set(icons.map(([key]) => key)).size !== icons.length) throw new Error("Duplicate icon keys");
25
+
26
+ const swift = `// Generated by scripts/gen-ios-icons.mjs from compose/.../utils/Icons.kt. Do not edit by hand.
27
+
28
+ let kitIcons: [String: String] = [
29
+ ${icons.map(([key, url]) => ` ${swiftString(key)}: ${swiftString(url)},`).join("\n")}
30
+ ]
31
+
32
+ let noThemeIcons: [String] = [${noTheme.map(swiftString).join(", ")}]
33
+ `;
34
+
35
+ writeFileSync(target, swift);
36
+ console.log(`Wrote ${icons.length} icons (${noTheme.length} untinted) to ${target}`);
@@ -1 +0,0 @@
1
- {"sessionId":"611d4121-c353-42bf-a58b-2fe5f02b02bb","pid":99784,"procStart":"Sun Oct 4 12:12:05 2026","acquiredAt":1791116444172}
@@ -1,185 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(curl -s -o /dev/null -w \"%{http_code}\" \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.144.6/skiko-android-0.144.6.pom\")",
5
- "Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/maven-metadata.xml\")",
6
- "Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko/maven-metadata.xml\")",
7
- "Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko/0.144.6/skiko-0.144.6.module\")",
8
- "Bash(curl -s -o /dev/null -w '%{http_code}' https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.144.6/skiko-android-0.144.6.__TRACKED_VAR__)",
9
- "Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.144.6/\")",
10
- "Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/\")",
11
- "Bash(curl -s -o /dev/null -w \"%{http_code}\\\\n\" \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.148.1/skiko-android-0.148.1.pom\")",
12
- "Bash(curl -s -o /dev/null -w \"%{http_code}\\\\n\" \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.144.5/skiko-android-0.144.5.pom\")",
13
- "Bash(curl -s -o /dev/null -w \"%{http_code}\\\\n\" \"https://dl.google.com/dl/android/maven2/org/jetbrains/skiko/skiko-android/0.144.6/skiko-android-0.144.6.pom\")",
14
- "Bash(curl -s \"https://search.maven.org/solrsearch/select?q=a:skiko-android&rows=5&wt=json\")",
15
- "Bash(curl -sL -o /dev/null -w \"%{http_code} %{url_effective}\\\\n\" \"https://repo1.maven.org/maven2/org/jetbrains/skiko/skiko-android/0.144.6/skiko-android-0.144.6.pom\")",
16
- "Bash(curl *)",
17
- "Bash(./gradlew :sample:androidApp:dependencies --configuration debugRuntimeClasspath)",
18
- "Bash(./gradlew :sample:shared:compileDebugKotlinAndroid)",
19
- "Bash(./gradlew :sample:androidApp:compileDebugKotlin)",
20
- "Bash(ls -d *.xcodeproj)",
21
- "Bash(grep -c ChipDemo.swift __TRACKED_VAR__/project.pbxproj)",
22
- "Bash(grep -c PBXFileSystemSynchronizedRootGroup __TRACKED_VAR__/project.pbxproj)",
23
- "Bash(grep -c InformationDemo.swift __TRACKED_VAR__/project.pbxproj)",
24
- "Bash(plutil -lint project.pbxproj)",
25
- "Bash(xcode-select -p)",
26
- "Bash(/usr/bin/xcrun xcodebuild *)",
27
- "Bash(echo \"exit: $?\")",
28
- "Bash(/Applications/Xcode26.app/Contents/Developer/usr/bin/xcodebuild -version)",
29
- "Bash(./gradlew :compose:tasks --all)",
30
- "Bash(grep -n \"vanniktech\\\\|mavenPublishing\\\\|^publishing\\\\|version = gitlabVersion\\\\|namespace =\\\\|compileSdk =\\\\|minSdk =\\\\|GitLabPackages\\\\|name = \\\\\"\\\\${gitlabRepo}Packages\\\\\"\")",
31
- "Bash(git checkout *)",
32
- "Bash(./gradlew :compose:publishAllPublicationsToGitLabPackagesRepository --dry-run)",
33
- "Bash(git fetch *)",
34
- "Bash(git pull *)",
35
- "Bash(git push *)",
36
- "Bash(git branch *)",
37
- "Bash(xcodebuild -workspace iosApp.xcworkspace -list)",
38
- "Bash(xcrun simctl *)",
39
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14 Pro' -configuration Debug build CODE_SIGNING_ALLOWED=NO)",
40
- "Bash(pod install *)",
41
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'id=1E6F9A79-5E64-4590-BEA2-828D75D99393' -configuration Debug build CODE_SIGNING_ALLOWED=NO)",
42
- "Bash(xcrun swiftc *)",
43
- "Bash(echo \"=== exit: $? \\(1=none found\\) ===\")",
44
- "Bash(cat Radius.swift)",
45
- "Bash(cat Spacing.swift)",
46
- "Bash(echo \"exit=$?\")",
47
- "Bash(ruby *)",
48
- "Bash(xcodebuild -list -workspace iosApp.xcworkspace)",
49
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14 Pro' build)",
50
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'platform=iOS Simulator,id=E6A899C0-098E-473A-95B8-A2BBD2D42A92' build)",
51
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'platform=iOS Simulator,id=E6A899C0-098E-473A-95B8-A2BBD2D42A92' build)",
52
- "Bash(xcodebuild build *)",
53
- "Bash(git stash *)",
54
- "Bash(plutil -lint sample/iosApp/iosApp.xcodeproj/project.pbxproj)",
55
- "Bash(git add *)",
56
- "Bash(git commit -m 'feat: DS-646 [ci build] *)",
57
- "Bash(git commit -q -m 'fix: DS-656 [ci build] *)",
58
- "Bash(xargs git log --oneline -1)",
59
- "Bash(git rev-list *)",
60
- "Bash(git status *)",
61
- "Bash(git rebase *)",
62
- "Bash(git merge *)",
63
- "Bash(git reset *)",
64
- "Bash(awk '/^ *)",
65
- "Bash(git ls-tree *)",
66
- "Bash(awk *)",
67
- "Read(//Users/sophia/Workspace/momo/momo-app/**)",
68
- "Bash(git log *)",
69
- "Bash(xcodebuild -version)",
70
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -configuration Debug build CODE_SIGNING_ALLOWED=NO)",
71
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -configuration Debug build CODE_SIGNING_ALLOWED=NO)",
72
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -configuration Debug clean build CODE_SIGNING_ALLOWED=NO)",
73
- "Bash(git commit *)",
74
- "Bash(npm view *)",
75
- "Bash(echo \"EXIT=$? -> not resolvable\")",
76
- "Bash(yarn install *)",
77
- "Bash(RCT_NEW_ARCH_ENABLED=1 pod install)",
78
- "Bash(xcodebuild -workspace MoMoPlatform.xcworkspace -list)",
79
- "Bash(git --no-pager diff --stat ios/Application/Localize.swift ios/Application/Navigation/NavigationContainer.swift ios/Popup/PopupDisplay.swift ios/Popup/PopupNotify.swift ios/Template/TrustBanner/TrustBanner.swift)",
80
- "Bash(git rm *)",
81
- "Bash(echo \"EXIT=$?\")",
82
- "Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits diff sample/iosApp/iosApp/Demo/ButtonDemo.swift)",
83
- "Bash(python3 *)",
84
- "Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits status --short)",
85
- "Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits log --oneline -3)",
86
- "Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits log --oneline -5 -- ios/Button/Button.swift)",
87
- "Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits diff ios/native-kits.podspec compose/compose.podspec gradle.properties package.json)",
88
- "Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits show HEAD:ios/Button/Button.swift)",
89
- "Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits diff package.json ios/native-kits.podspec)",
90
- "Bash(grep -n \"must be a \\\\`.background\\\\` of the label\" /Users/sophia/Workspace/momo/momo-app/node_modules/@momo-kits/native-kits/ios/Button/Button.swift)",
91
- "Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits show -s --format=\"%h %ci %s\" 953b234d)",
92
- "Bash(git -C /Users/sophia/Workspace/momo/momo-app show -s --format=\"%h %ci %s\" f8757a14437)",
93
- "Bash(git -C /Users/sophia/Workspace/momo/momo-app log -S '\"@momo-kits/native-kits\": \"0.163.1-beta.18\"' --format=\"%h %ci %s\" -- package.json)",
94
- "Bash(swiftc -O repro_isfull.swift -o repro_isfull)",
95
- "Bash(./repro_isfull)",
96
- "Bash(grep -n '^\\\\[32;1m$\\\\|section_start\\\\|xcodebuild.*-workspace\\\\|xcodebuild.*archive\\\\|Prebuilt\\\\|prebuilt\\\\|framework.*download\\\\|rugby\\\\|Rugby' /private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/7e2b5bd7-f8b3-42f7-b242-127fa8eabd12/scratchpad/job_trace.log)",
97
- "Bash(grep -n '\\\\[32;1m\\\\$' /private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/7e2b5bd7-f8b3-42f7-b242-127fa8eabd12/scratchpad/job_trace.log)",
98
- "Bash(npm pack *)",
99
- "Bash(mkdir -p b13 b18)",
100
- "Bash(tar xzf *)",
101
- "Bash(git *)",
102
- "Bash(tar -tzf momo-kits-native-kits-0.163.1-beta.18.tgz)",
103
- "Bash(tar -xzf momo-kits-native-kits-0.163.1-beta.18.tgz package/ios/Button/Button.swift)",
104
- "Bash(tar -xzf momo-kits-native-kits-0.162.28.tgz package/ios/Button/Button.swift -C .)",
105
- "Bash(mkdir -p old)",
106
- "Bash(tar -xzf momo-kits-native-kits-0.162.28.tgz -C old package/ios/Button/Button.swift)",
107
- "Bash(nm '/private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/7e2b5bd7-f8b3-42f7-b242-127fa8eabd12/scratchpad/ipa_extract/Payload/MoMoPlatform UAT.app/MoMoPlatform UAT')",
108
- "Bash(xcodebuild -workspace /Users/sophia/Workspace/momo/momo-native-kits/sample/iosApp/iosApp.xcworkspace -list)",
109
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' build)",
110
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -derivedDataPath build-verify build)",
111
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -derivedDataPath build-verify build)",
112
- "Bash(./gradlew :sample:shared:syncComposeResourcesForIos)",
113
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'platform=iOS Simulator,id=E6A899C0-098E-473A-95B8-A2BBD2D42A92' -derivedDataPath build-verify build)",
114
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -quiet build)",
115
- "Bash(xcodebuild -workspace sample/iosApp/iosApp.xcworkspace -list)",
116
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -destination 'id=0228A4B2-57E0-4E56-BFAD-DB9E95044AD9' -derivedDataPath /private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/6986983a-795d-419d-9d78-fcc100e87876/scratchpad/dd build)",
117
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -destination 'id=1E6F9A79-5E64-4590-BEA2-828D75D99393' -derivedDataPath /private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/6986983a-795d-419d-9d78-fcc100e87876/scratchpad/dd build)",
118
- "Bash([ -s $S/kb.png ])",
119
- "Bash(sips -g pixelWidth -g pixelHeight /private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/6986983a-795d-419d-9d78-fcc100e87876/scratchpad/kb.png)",
120
- "Bash(defaults read *)",
121
- "Bash(defaults write *)",
122
- "Bash(xcrun xcodebuild *)",
123
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -quiet build)",
124
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'platform=iOS Simulator,id=E6A899C0-098E-473A-95B8-A2BBD2D42A92' -quiet build)",
125
- "Read(//tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/3e8adc08-a7e7-4169-aee6-8b22a6996f38/scratchpad/**)",
126
- "Bash(echo \"parse-exit=$?\")",
127
- "Bash(echo \"=== exit $?\")",
128
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -configuration Debug build)",
129
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'platform=iOS Simulator,id=E6A899C0-098E-473A-95B8-A2BBD2D42A92' -configuration Debug build)",
130
- "Bash(./gradlew :sample:shared:compileCommonMainKotlinMetadata -q)",
131
- "Bash(echo \"gradle=$?\")",
132
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'id=0228A4B2-57E0-4E56-BFAD-DB9E95044AD9' -derivedDataPath build/dd build)",
133
- "Bash(osascript *)",
134
- "Bash(screencapture -x -R253,53,452,950 win.png)",
135
- "Bash(sips -g pixelWidth -g pixelHeight win.png)",
136
- "Bash(screencapture -x -R\"253,53,452,950\" win.png)",
137
- "Bash(screencapture -x full.png)",
138
- "Bash(sips -g pixelWidth -g pixelHeight full.png)",
139
- "Bash(sips -c 1900 904 --cropOffset 106 506 full.png --out crop.png)",
140
- "Bash(swiftc -O mouse.swift -o mouse)",
141
- "Bash(./mouse click *)",
142
- "Bash(./mouse drag *)",
143
- "Bash(sips -c 900 924 --cropOffset 0 0 s12.png --out s12crop.png)",
144
- "Bash(sips -c 300 1170 --cropOffset 300 0 s12.png --out s12top.png)",
145
- "Bash(sips -c 700 1170 --cropOffset 250 0 t1.png --out t1c.png)",
146
- "Bash(sips -c 700 1170 --cropOffset 250 0 t2.png --out t2c.png)",
147
- "Bash(sips -c 700 1170 --cropOffset 150 0 t3.png --out t3c.png)",
148
- "Bash(sips -c 1200 1170 --cropOffset 600 0 t4.png --out t4c.png)",
149
- "Bash(sips -c 600 1170 --cropOffset 150 0 t5.png --out t5c.png)",
150
- "Bash(sips -c 500 1170 --cropOffset 150 0 u1.png --out u1c.png)",
151
- "Bash(cp /Users/sophia/Workspace/momo/momo-native-kits/ios/Application/Navigation/NavigationContainer.swift /Users/sophia/Workspace/momo/momo-app/node_modules/@momo-kits/native-kits/ios/Application/Navigation/NavigationContainer.swift)",
152
- "Bash(cp /Users/sophia/Workspace/momo/momo-native-kits/ios/Application/Navigation/Navigator.swift /Users/sophia/Workspace/momo/momo-app/node_modules/@momo-kits/native-kits/ios/Application/Navigation/Navigator.swift)",
153
- "Bash(cp /Users/sophia/Workspace/momo/momo-native-kits/ios/Application/Navigation/NavigatorManager.swift /Users/sophia/Workspace/momo/momo-app/node_modules/@momo-kits/native-kits/ios/Application/Navigation/NavigatorManager.swift)",
154
- "Bash(cp /Users/sophia/Workspace/momo/momo-native-kits/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift /Users/sophia/Workspace/momo/momo-app/node_modules/@momo-kits/native-kits/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift)",
155
- "Bash(xcodebuild -workspace MoMoPlatform.xcworkspace -scheme MoMoPlatform-UAT -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -derivedDataPath build/DerivedData build)",
156
- "Bash(cp android/gradle/libs.versions.toml /private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/bfb380ee-54cb-4a02-bc5f-d152cce4b727/scratchpad/libs.versions.toml.bak)",
157
- "Bash(sed -i '' 's/kits = \"0.163.1-sp.8\"/kits = \"0.163.1-sp.7\"/' android/gradle/libs.versions.toml)",
158
- "Bash(xcrun --sdk iphonesimulator --show-sdk-path)",
159
- "Bash(chmod +x /private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/62b6dcd2-247f-4111-98ef-3847c8914676/scratchpad/tc.sh)",
160
- "Bash(/private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/62b6dcd2-247f-4111-98ef-3847c8914676/scratchpad/tc.sh)",
161
- "Bash(perl -0pi -e 's{^ static let \\(\\\\w+\\) = \\(appFont\\\\\\(size: [\\\\d.]+\\\\\\)\\(?:\\\\.weight\\\\\\(\\\\.\\\\w+\\\\\\)\\)?\\)\\(\\\\s*//.*\\)?$}{ static var $1: Font { $2 }$3}mg' ios/Typography/Typography.swift)",
162
- "Bash(perl -0pi -e 's{\\(\\\\s*\\)// AI-GENERATED START: route Localize injection through the observing modifier\\\\n\\(\\\\s*\\)\\\\.momoLocalize\\\\\\(localize\\\\\\)\\\\n\\(\\\\s*\\)// AI-GENERATED END: route Localize injection through the observing modifier}{$1// AI-GENERATED START: route Localize + font-scale injection through the observing modifiers\\\\n$2.momoLocalize\\(localize\\)\\\\n$2.momoFontScale\\(\\)\\\\n$3// AI-GENERATED END: route Localize + font-scale injection through the observing modifiers}g' ios/Application/Navigation/NavigationContainer.swift)",
163
- "Bash(perl -pi -e 's/Mirrors Compose.s `fontScale\\\\\\(\\\\.\\\\.\\\\.\\\\\\)`/Mirrors Compose'\"'\"'s `scaleSize\\(...\\)`/' ios/Chip/Chip.swift)",
164
- "Bash(perl -pi -e 's/fontScale\\\\\\(\\(size == \\\\.small \\\\? 48 : 56\\), 1\\\\.1\\\\\\)/fontScale\\($1\\)/' ios/Input/Input.swift ios/Input/InputPhoneNumber.swift)",
165
- "Bash(perl -pi -e 's/^\\(\\\\s*\\)\\\\.momoFontScale\\\\\\(\\\\\\)$/$1.momoFontScale\\(context: applicationEnvironment.applicationContext\\)/' ios/Application/Navigation/NavigationContainer.swift)",
166
- "Bash(chmod +x /private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/62b6dcd2-247f-4111-98ef-3847c8914676/scratchpad/tcdemo.sh)",
167
- "Bash(/private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/62b6dcd2-247f-4111-98ef-3847c8914676/scratchpad/tcdemo.sh)",
168
- "Bash(\"/Users/sophia/Workspace/momo-glab/momo-agent-flows/momo-agent/scripts/repo.sh\" find *)",
169
- "Read(//Users/sophia/.momo-agent/**)",
170
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -configuration Debug -destination 'platform=iOS Simulator,name=iPhone 16 Pro' -derivedDataPath /private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/62b6dcd2-247f-4111-98ef-3847c8914676/scratchpad/dd build)",
171
- "Bash(\"/Users/sophia/Workspace/momo-glab/momo-agent-flows/momo-agent/scripts/repo.sh\" prep *)",
172
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -configuration Debug -destination id=1E6F9A79-5E64-4590-BEA2-828D75D99393 -derivedDataPath /private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/62b6dcd2-247f-4111-98ef-3847c8914676/scratchpad/dd build)",
173
- "Bash(find /private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/62b6dcd2-247f-4111-98ef-3847c8914676/scratchpad/dd/Build/Products -maxdepth 2 -name *.app)",
174
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -configuration Debug -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -derivedDataPath build/dd build)",
175
- "Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -configuration Debug -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -derivedDataPath build/dd ARCHS=arm64 ONLY_ACTIVE_ARCH=YES build)",
176
- "Bash(./gradlew :compose:generateDummyFramework)",
177
- "Bash(break)",
178
- "Bash(\"/Users/sophia/Workspace/momo-glab/momo-agent-flows/momo-agent/scripts/gitlab.sh\" mr-view *)",
179
- "Bash(\"/Users/sophia/Workspace/momo-glab/momo-agent-flows/momo-agent/scripts/gitlab.sh\" mr-discussions *)",
180
- "Bash(command -v glab)",
181
- "Bash(glab --version)",
182
- "Bash(/Users/sophia/Workspace/momo-glab/momo-agent-flows/momo-agent/scripts/jira.sh transitions *)"
183
- ]
184
- }
185
- }
@@ -1,196 +0,0 @@
1
- //
2
- // ScreenLayout.swift
3
- // MoMoUIKits
4
- //
5
- // Port of momo-kits RN `useScreenLayout` (Layout/utils.ts) and `Application/device.ts`:
6
- // window size, safe-area insets, content width, header geometry and the iPhone Duo side bar.
7
- //
8
-
9
- import SwiftUI
10
- import UIKit
11
-
12
- public enum SideBarEdge: Equatable {
13
- case left, right
14
- }
15
-
16
- public struct SideBarLayout: Equatable {
17
- public let edge: SideBarEdge
18
- public let width: CGFloat
19
- /// Preferred window y of the first control; StackScreen still keeps the column above the bottom bars.
20
- public let top: CGFloat
21
- }
22
-
23
- public struct ScreenLayout: Equatable {
24
- public let width: CGFloat
25
- public let height: CGFloat
26
- public let isIPhoneDuo: Bool
27
- public let insets: EdgeInsets
28
- public let insetLeft: CGFloat
29
- public let insetRight: CGFloat
30
- public let contentWidth: CGFloat
31
- public let headerHeight: CGFloat
32
- /// 0 when the status bar is off the top edge, so the bar row fills the header and keeps the title centered.
33
- public let headerStatusBarHeight: CGFloat
34
- /// iPhone Duo moves back and toolkit into a column in the side safe area; nil while they stay in the bar.
35
- public let sideBar: SideBarLayout?
36
-
37
- public var headerBarHeight: CGFloat { headerHeight - headerStatusBarHeight }
38
- }
39
-
40
- // Measured by momo-kits RN (Layout/utils.ts) on the iOS 27.1 iPhone Duo simulator; keep both in sync.
41
- enum DuoMetrics {
42
- // Inner display is ~1.42, outer ~1.45; every slab iPhone is 1.77 or taller.
43
- static let maxAspectRatio: CGFloat = 1.6
44
- // Outer display is 466pt tall in landscape, inner 669pt.
45
- static let innerMinHeight: CGFloat = 600
46
- static let controlSize: CGFloat = 48
47
- static let itemPitch: CGFloat = 50
48
- static let firstItemTop: CGFloat = 170
49
- static let firstItemTopLandscape: CGFloat = 120
50
- // Widest short side of any single-display iPhone.
51
- static let iPhoneMaxShortSide: CGFloat = 440
52
- }
53
-
54
- enum HeaderMetrics {
55
- static let barHeight: CGFloat = HEADER_HEIGHT
56
- static let iPhoneMinHeight: CGFloat = 72
57
- // A smaller top inset means the status bar is not on the top edge.
58
- static let statusBarMinInset: CGFloat = 20
59
- }
60
-
61
- private var isPhoneIdiom: Bool { UIDevice.current.userInterfaceIdiom == .phone }
62
-
63
- private var keyWindow: UIWindow? {
64
- UIApplication.shared.connectedScenes
65
- .compactMap { ($0 as? UIWindowScene)?.keyWindow }
66
- .first
67
- }
68
-
69
- extension ScreenLayout {
70
- /// Snapshot of the key window; not reactive, so views read `\.screenLayout` instead.
71
- public static var current: ScreenLayout {
72
- guard let window = keyWindow else {
73
- return make(size: UIScreen.main.bounds.size, insets: EdgeInsets())
74
- }
75
- let inset = window.safeAreaInsets
76
- return make(
77
- size: window.bounds.size,
78
- insets: EdgeInsets(top: inset.top, leading: inset.left, bottom: inset.bottom, trailing: inset.right)
79
- )
80
- }
81
-
82
- static func make(size: CGSize, insets: EdgeInsets, isPhone: Bool = isPhoneIdiom) -> ScreenLayout {
83
- let width = size.width
84
- let height = size.height
85
- let (headerHeight, headerStatusBarHeight) = headerGeometry(topInset: insets.top, isPhone: isPhone)
86
- let isDuo = isPhone && isIPhoneDuoWindow(width: width, height: height, insets: insets)
87
- guard isDuo else {
88
- return ScreenLayout(
89
- width: width, height: height, isIPhoneDuo: false, insets: insets,
90
- insetLeft: 0, insetRight: 0, contentWidth: width,
91
- headerHeight: headerHeight, headerStatusBarHeight: headerStatusBarHeight, sideBar: nil
92
- )
93
- }
94
-
95
- var sideBar: SideBarLayout?
96
- let sideInset = max(insets.leading, insets.trailing)
97
- // narrower strips cannot host a control 12pt from the inner edge
98
- if sideInset >= DuoMetrics.controlSize + Spacing.M {
99
- var top = insets.top + HeaderMetrics.barHeight + Spacing.M
100
- if insets.top < HeaderMetrics.statusBarMinInset {
101
- // status bar lives in the strip: start under its cluster, as the native bar does
102
- let isInnerLandscape = width > height && height >= DuoMetrics.innerMinHeight
103
- top = isInnerLandscape ? DuoMetrics.firstItemTopLandscape : DuoMetrics.firstItemTop
104
- }
105
- sideBar = SideBarLayout(
106
- edge: insets.trailing >= insets.leading ? .right : .left,
107
- width: sideInset,
108
- top: top
109
- )
110
- }
111
-
112
- return ScreenLayout(
113
- width: width, height: height, isIPhoneDuo: true, insets: insets,
114
- insetLeft: insets.leading, insetRight: insets.trailing,
115
- contentWidth: width - insets.leading - insets.trailing,
116
- headerHeight: headerHeight, headerStatusBarHeight: headerStatusBarHeight, sideBar: sideBar
117
- )
118
- }
119
-
120
- private static func headerGeometry(topInset: CGFloat, isPhone: Bool) -> (CGFloat, CGFloat) {
121
- let height = topInset + HeaderMetrics.barHeight
122
- guard isPhone else { return (height, topInset) }
123
- return (
124
- max(height, HeaderMetrics.iPhoneMinHeight),
125
- topInset < HeaderMetrics.statusBarMinInset ? 0 : topInset
126
- )
127
- }
128
-
129
- // Slab iPhones report equal side insets in landscape; iPhone Duo moves the status bar to one side.
130
- private static func isIPhoneDuoWindow(width: CGFloat, height: CGFloat, insets: EdgeInsets) -> Bool {
131
- let shortSide = min(width, height)
132
- let isSquare = shortSide > 0 && max(width, height) / shortSide < DuoMetrics.maxAspectRatio
133
- return isSquare || insets.leading != insets.trailing
134
- }
135
-
136
- /// Below the side status bar: in portrait it is about twice as tall as its inset is wide.
137
- public var duoTopInset: CGFloat {
138
- max(insets.top, height > width ? max(insets.leading, insets.trailing) * 2 : 0)
139
- }
140
- }
141
-
142
- /// Port of RN `device.ts` `isIPhoneDuo()`: UIScreen stays on the outer display, so this
143
- /// still matches when Split View narrows the window. Used by popups and overlays.
144
- public func isIPhoneDuoDevice(windowSize: CGSize? = nil) -> Bool {
145
- guard isPhoneIdiom else { return false }
146
- let screen = UIScreen.main.bounds.size
147
- let window = windowSize ?? keyWindow?.bounds.size ?? screen
148
- return max(min(screen.width, screen.height), min(window.width, window.height)) > DuoMetrics.iPhoneMaxShortSide
149
- }
150
-
151
- // MARK: - Environment
152
-
153
- private struct ScreenLayoutKey: EnvironmentKey {
154
- // Computed so an un-injected subtree (bare UIHostingController) still reads the live window.
155
- static var defaultValue: ScreenLayout { .current }
156
- }
157
-
158
- public extension EnvironmentValues {
159
- var screenLayout: ScreenLayout {
160
- get { self[ScreenLayoutKey.self] }
161
- set { self[ScreenLayoutKey.self] = newValue }
162
- }
163
- }
164
-
165
- /// Re-reads the window whenever this view's size changes (rotation, fold/unfold, Split View).
166
- private struct ScreenLayoutReader: ViewModifier {
167
- @State private var layout: ScreenLayout = .current
168
-
169
- func body(content: Content) -> some View {
170
- content
171
- .environment(\.screenLayout, layout)
172
- .background(
173
- GeometryReader { proxy in
174
- Color.clear
175
- .onAppear { refresh() }
176
- .onChange(of: proxy.size) { _ in refresh() }
177
- .onChange(of: proxy.safeAreaInsets) { _ in refresh() }
178
- }
179
- )
180
- .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
181
- refresh()
182
- }
183
- }
184
-
185
- private func refresh() {
186
- let next = ScreenLayout.current
187
- if next != layout { layout = next }
188
- }
189
- }
190
-
191
- public extension View {
192
- /// Provides `\.screenLayout`; NavigationContainer applies it, a bare UIHostingController root should too.
193
- func momoScreenLayout() -> some View {
194
- modifier(ScreenLayoutReader())
195
- }
196
- }