@shortkitsdk/react-native 0.2.16 → 0.2.23

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 (57) hide show
  1. package/android/build.gradle.kts +1 -1
  2. package/android/libs/shortkit-release.aar +0 -0
  3. package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +5 -9
  4. package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +8 -0
  5. package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +3 -2
  6. package/android/src/main/java/com/shortkit/reactnative/ShortKitWidgetNativeView.kt +4 -0
  7. package/ios/DebugPanelView.swift +302 -0
  8. package/ios/ShortKitBridge.swift +5 -3
  9. package/ios/ShortKitFeedView.swift +15 -0
  10. package/ios/ShortKitModule.mm +3 -2
  11. package/ios/ShortKitSDK.xcframework/Info.plist +5 -5
  12. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Info.plist +2 -2
  13. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +7196 -1210
  14. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +118 -5
  15. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  16. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +118 -5
  17. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
  18. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources +9 -9
  19. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Info.plist +2 -2
  20. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +7196 -1210
  21. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +118 -5
  22. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  23. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +118 -5
  24. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +7196 -1210
  25. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +118 -5
  26. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  27. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +118 -5
  28. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
  29. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/_CodeSignature/CodeResources +17 -17
  30. package/ios/ShortKitWidgetNativeView.swift +7 -1
  31. package/package.json +1 -1
  32. package/src/ShortKitFeed.tsx +0 -13
  33. package/src/ShortKitProvider.tsx +2 -0
  34. package/src/ShortKitWidget.tsx +1 -0
  35. package/src/index.ts +0 -3
  36. package/src/serialization.ts +0 -1
  37. package/src/specs/NativeShortKitModule.ts +1 -7
  38. package/src/types.ts +3 -11
  39. package/ios/ShortKitSDK.xcframework.dev-backup/Info.plist +0 -43
  40. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +0 -418
  41. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/Info.plist +0 -16
  42. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +0 -32983
  43. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +0 -915
  44. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  45. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +0 -915
  46. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/Modules/module.modulemap +0 -4
  47. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
  48. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources +0 -168
  49. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +0 -418
  50. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/Info.plist +0 -16
  51. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +0 -32983
  52. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +0 -915
  53. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  54. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +0 -915
  55. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/Modules/module.modulemap +0 -4
  56. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
  57. package/ios/ShortKitSDK.xcframework.dev-backup/ios-arm64-simulator/ShortKitSDK.framework/_CodeSignature/CodeResources +0 -168
@@ -34,7 +34,7 @@ dependencies {
34
34
  // When Reco's settings.gradle has the composite build, Gradle substitutes
35
35
  // this with the live source from android_sdk/shortkit automatically.
36
36
  // implementation("dev.shortkit:shortkit:0.2.11")
37
- implementation(files("/Users/michaelseleman/shortkit/android_sdk/shortkit/build/outputs/aar/shortkit-release.aar"))
37
+ implementation(files("libs/shortkit-release.aar"))
38
38
  // Transitive deps needed when using local AAR (Maven artifact bundles these automatically)
39
39
  implementation("androidx.media3:media3-exoplayer:1.5.1")
40
40
  implementation("androidx.media3:media3-exoplayer-hls:1.5.1")
@@ -20,13 +20,13 @@ import com.shortkit.sdk.model.ImageCarouselItem
20
20
  import com.shortkit.sdk.model.VideoCarouselItem
21
21
  import com.shortkit.sdk.model.JsonValue
22
22
  import com.shortkit.sdk.model.PlayerState
23
+ import com.shortkit.sdk.config.SurveyOverlayMode
23
24
  import com.shortkit.sdk.config.AdOverlayMode
24
25
  import com.shortkit.sdk.config.CarouselOverlayMode
25
26
  import com.shortkit.sdk.config.FeedConfig
26
27
  import com.shortkit.sdk.config.FeedHeight
27
28
  import com.shortkit.sdk.config.FeedSource
28
29
  import com.shortkit.sdk.config.ScrollAxis
29
- import com.shortkit.sdk.config.SurveyOverlayMode
30
30
  import com.shortkit.sdk.config.VideoCarouselOverlayMode
31
31
  import com.shortkit.sdk.feed.ShortKitRefreshState
32
32
  import com.shortkit.sdk.config.VideoOverlayMode
@@ -57,6 +57,7 @@ class ShortKitBridge(
57
57
  clientAppName: String?,
58
58
  clientAppVersion: String?,
59
59
  customDimensionsJSON: String?,
60
+ debugPanel: Boolean,
60
61
  private val emitEvent: (String, WritableMap) -> Unit
61
62
  ) {
62
63
 
@@ -511,7 +512,8 @@ class ShortKitBridge(
511
512
  adProvider = null,
512
513
  clientAppName = clientAppName,
513
514
  clientAppVersion = clientAppVersion,
514
- customDimensions = dims
515
+ customDimensions = dims,
516
+ debugPanelEnabled = debugPanel
515
517
  )
516
518
  this.shortKit = sdk
517
519
  shared = this
@@ -883,13 +885,7 @@ class ShortKitBridge(
883
885
  callback(uuid)
884
886
  return
885
887
  }
886
- val filterJSON = config.filter?.let { org.json.JSONObject().apply {
887
- it.tags?.let { tags -> put("tags", org.json.JSONArray(tags)) }
888
- it.section?.let { s -> put("section", s) }
889
- it.author?.let { a -> put("author", a) }
890
- it.contentType?.let { ct -> put("contentType", ct) }
891
- }.toString() }
892
- val preload = sdk.preloadFeed(filter = filterJSON)
888
+ val preload = sdk.preloadFeed(filter = config.filter)
893
889
  val uuid = java.util.UUID.randomUUID().toString()
894
890
  preloadHandles[uuid] = preload
895
891
  callback(uuid)
@@ -191,6 +191,14 @@ class ShortKitFeedView(context: Context) : FrameLayout(context) {
191
191
 
192
192
  val fragment = ShortKitFeedFragment.newInstance(sdk, feedConfig, startAtItemId)
193
193
 
194
+ if (sdk.debugPanelEnabled) {
195
+ fragment.debugPanelFactory = { active, prev, next, lifecycleOwner ->
196
+ com.shortkit.sdk.debug.DebugPanelView(context).also { panel ->
197
+ panel.subscribe(active, prev, next, lifecycleOwner)
198
+ }
199
+ }
200
+ }
201
+
194
202
  try {
195
203
  activity.supportFragmentManager
196
204
  .beginTransaction()
@@ -71,7 +71,8 @@ class ShortKitModule(reactContext: ReactApplicationContext) :
71
71
  hasLoadingView: Boolean,
72
72
  clientAppName: String?,
73
73
  clientAppVersion: String?,
74
- customDimensions: String?
74
+ customDimensions: String?,
75
+ debugPanel: Boolean?
75
76
  ) {
76
77
  bridge?.teardown()
77
78
  bridge = null
@@ -86,6 +87,7 @@ class ShortKitModule(reactContext: ReactApplicationContext) :
86
87
  clientAppName = clientAppName,
87
88
  clientAppVersion = clientAppVersion,
88
89
  customDimensionsJSON = customDimensions,
90
+ debugPanel = debugPanel ?: false,
89
91
  emitEvent = { name, body -> sendEvent(name, body) }
90
92
  )
91
93
 
@@ -258,7 +260,6 @@ class ShortKitModule(reactContext: ReactApplicationContext) :
258
260
  "onFormatChange" -> emitOnFormatChange(params)
259
261
  "onPrefetchedAheadCountChanged" -> emitOnPrefetchedAheadCountChanged(params)
260
262
  "onRemainingContentCountChanged" -> emitOnRemainingContentCountChanged(params)
261
- "onSurveyResponse" -> emitOnSurveyResponse(params)
262
263
  "onContentTapped" -> emitOnContentTapped(params)
263
264
  "onDismiss" -> emitOnDismiss(params)
264
265
  "onRefreshStateChanged" -> emitOnRefreshStateChanged(params)
@@ -7,6 +7,7 @@ import com.shortkit.sdk.config.VideoOverlayMode
7
7
  import com.shortkit.sdk.config.WidgetConfig
8
8
  import com.shortkit.sdk.model.ContentItem
9
9
  import com.shortkit.sdk.widget.ShortKitWidgetView
10
+ import com.shortkit.sdk.model.FeedFilter
10
11
  import org.json.JSONArray
11
12
  import org.json.JSONObject
12
13
 
@@ -87,6 +88,9 @@ class ShortKitWidgetNativeView(context: Context) : FrameLayout(context) {
87
88
  else -> PlayerClickAction.FEED
88
89
  },
89
90
  cardOverlay = parseOverlay(obj),
91
+ filter = obj.optString("filter", "").let { filterStr ->
92
+ if (filterStr.isNotEmpty()) ShortKitBridge.parseFeedFilterToModel(filterStr) else null
93
+ },
90
94
  )
91
95
  } catch (_: Exception) {
92
96
  WidgetConfig()
@@ -0,0 +1,302 @@
1
+ import UIKit
2
+ import Combine
3
+ import ShortKitSDK
4
+
5
+ private enum AdjacentSlot {
6
+ case previous, next
7
+ }
8
+
9
+ final class DebugPanelView: UIView {
10
+ static let panelWidth: CGFloat = 310
11
+ static let panelHeight: CGFloat = 296 // +20 for collapsed header row
12
+ static let expandedExtraHeight: CGFloat = 80 // 4 labels × ~20pt
13
+
14
+ /// Currently selected swipe curve — FeedViewController reads SwipeCurve.selected.
15
+ static var selectedSwipeCurve: SwipeCurve {
16
+ get { SwipeCurve.selected }
17
+ set { SwipeCurve.selected = newValue }
18
+ }
19
+
20
+ private let stackView = UIStackView()
21
+ private let headerLabel = UILabel()
22
+ private let curveSegment = UISegmentedControl()
23
+ private let positionLabel = UILabel()
24
+ private let intentTtffLabel = UILabel()
25
+ private let transitionTtffLabel = UILabel()
26
+ private let prerollBufferLabel = UILabel()
27
+ private let videoQualityLabel = UILabel()
28
+ private let thumbnailLabel = UILabel()
29
+ private let prefetchLabel = UILabel()
30
+ private let bitrateLabel = UILabel()
31
+ private let rebufferLabel = UILabel()
32
+ private let loopWatchLabel = UILabel()
33
+
34
+ // Adjacent players section
35
+ private let adjacentHeader = UILabel()
36
+ private let prevHeaderLabel = UILabel()
37
+ private let prevDetailLabel = UILabel()
38
+ private let nextHeaderLabel = UILabel()
39
+ private let nextDetailLabel = UILabel()
40
+ private let separatorView = UIView()
41
+ private var adjacentLabels: [UIView] = []
42
+ private var isAdjacentExpanded = false
43
+ private var prevCancellable: AnyCancellable?
44
+ private var nextCancellable: AnyCancellable?
45
+
46
+ private var cancellable: AnyCancellable?
47
+ private var dragOffset: CGPoint = .zero
48
+
49
+ override init(frame: CGRect) {
50
+ super.init(frame: frame)
51
+ setupViews()
52
+ }
53
+
54
+ required init?(coder: NSCoder) {
55
+ fatalError("init(coder:) has not been implemented")
56
+ }
57
+
58
+ private func setupViews() {
59
+ backgroundColor = UIColor.black.withAlphaComponent(0.75)
60
+ layer.cornerRadius = 10
61
+ clipsToBounds = true
62
+
63
+ let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
64
+ addGestureRecognizer(pan)
65
+
66
+ let labels = [headerLabel, positionLabel, intentTtffLabel, transitionTtffLabel, prerollBufferLabel, videoQualityLabel, thumbnailLabel, prefetchLabel, bitrateLabel, rebufferLabel, loopWatchLabel]
67
+ for label in labels {
68
+ label.font = UIFont.monospacedSystemFont(ofSize: 12, weight: .regular)
69
+ label.textColor = .white
70
+ label.numberOfLines = 1
71
+ }
72
+ headerLabel.font = UIFont.monospacedSystemFont(ofSize: 12, weight: .bold)
73
+
74
+ stackView.axis = .vertical
75
+ stackView.spacing = 4
76
+ stackView.alignment = .leading
77
+ stackView.translatesAutoresizingMaskIntoConstraints = false
78
+ addSubview(stackView)
79
+
80
+ for label in labels {
81
+ stackView.addArrangedSubview(label)
82
+ }
83
+
84
+ // Swipe curve picker
85
+ for (i, curve) in SwipeCurve.allCases.enumerated() {
86
+ curveSegment.insertSegment(withTitle: curve.label, at: i, animated: false)
87
+ }
88
+ curveSegment.selectedSegmentIndex = SwipeCurve.quarticOut.rawValue
89
+ curveSegment.setTitleTextAttributes([.foregroundColor: UIColor.white, .font: UIFont.monospacedSystemFont(ofSize: 10, weight: .medium)], for: .normal)
90
+ curveSegment.setTitleTextAttributes([.foregroundColor: UIColor.black], for: .selected)
91
+ curveSegment.backgroundColor = UIColor.white.withAlphaComponent(0.15)
92
+ curveSegment.selectedSegmentTintColor = UIColor.white.withAlphaComponent(0.9)
93
+ curveSegment.addTarget(self, action: #selector(curveChanged), for: .valueChanged)
94
+ stackView.addArrangedSubview(curveSegment)
95
+
96
+ // Separator
97
+ separatorView.backgroundColor = UIColor.white.withAlphaComponent(0.3)
98
+ separatorView.translatesAutoresizingMaskIntoConstraints = false
99
+ separatorView.heightAnchor.constraint(equalToConstant: 0.5).isActive = true
100
+ stackView.addArrangedSubview(separatorView)
101
+
102
+ // Adjacent header (tap target)
103
+ adjacentHeader.font = UIFont.monospacedSystemFont(ofSize: 12, weight: .bold)
104
+ adjacentHeader.textColor = .white
105
+ adjacentHeader.text = "▶ Adjacent Players"
106
+ adjacentHeader.isUserInteractionEnabled = true
107
+ adjacentHeader.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(toggleAdjacent)))
108
+ stackView.addArrangedSubview(adjacentHeader)
109
+
110
+ // Adjacent player labels (initially hidden)
111
+ let adjLabels = [prevHeaderLabel, prevDetailLabel, nextHeaderLabel, nextDetailLabel]
112
+ for label in adjLabels {
113
+ label.font = UIFont.monospacedSystemFont(ofSize: 11, weight: .regular)
114
+ label.textColor = UIColor.white.withAlphaComponent(0.85)
115
+ label.numberOfLines = 1
116
+ label.isHidden = true
117
+ stackView.addArrangedSubview(label)
118
+ }
119
+ adjacentLabels = adjLabels
120
+
121
+ prevHeaderLabel.text = "▲ Prev — not assigned"
122
+ prevDetailLabel.text = ""
123
+ nextHeaderLabel.text = "▼ Next — not assigned"
124
+ nextDetailLabel.text = ""
125
+
126
+ NSLayoutConstraint.activate([
127
+ stackView.topAnchor.constraint(equalTo: topAnchor, constant: 10),
128
+ stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
129
+ stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12),
130
+ ])
131
+
132
+ // Initial state
133
+ update(DebugMetrics())
134
+ }
135
+
136
+ @objc private func handlePan(_ gesture: UIPanGestureRecognizer) {
137
+ guard let parent = superview else { return }
138
+ switch gesture.state {
139
+ case .began:
140
+ dragOffset = gesture.location(in: self)
141
+ case .changed:
142
+ let location = gesture.location(in: parent)
143
+ var newCenter = CGPoint(
144
+ x: location.x - dragOffset.x + bounds.width / 2,
145
+ y: location.y - dragOffset.y + bounds.height / 2
146
+ )
147
+ // Keep panel within parent bounds
148
+ let hw = bounds.width / 2, hh = bounds.height / 2
149
+ newCenter.x = max(hw, min(parent.bounds.width - hw, newCenter.x))
150
+ newCenter.y = max(hh, min(parent.bounds.height - hh, newCenter.y))
151
+ center = newCenter
152
+ default:
153
+ break
154
+ }
155
+ }
156
+
157
+ @objc private func curveChanged(_ sender: UISegmentedControl) {
158
+ guard let curve = SwipeCurve(rawValue: sender.selectedSegmentIndex) else { return }
159
+ Self.selectedSwipeCurve = curve
160
+ }
161
+
162
+ @objc private func toggleAdjacent() {
163
+ isAdjacentExpanded.toggle()
164
+ adjacentHeader.text = isAdjacentExpanded ? "▼ Adjacent Players" : "▶ Adjacent Players"
165
+
166
+ UIView.animate(withDuration: 0.25) {
167
+ for label in self.adjacentLabels {
168
+ label.isHidden = !self.isAdjacentExpanded
169
+ label.alpha = self.isAdjacentExpanded ? 1 : 0
170
+ }
171
+ // Resize panel
172
+ var newFrame = self.frame
173
+ let baseHeight = Self.panelHeight
174
+ newFrame.size.height = self.isAdjacentExpanded
175
+ ? baseHeight + Self.expandedExtraHeight
176
+ : baseHeight
177
+ self.frame = newFrame
178
+ self.layoutIfNeeded()
179
+ }
180
+ }
181
+
182
+ func subscribe(to subject: CurrentValueSubject<DebugMetrics, Never>) {
183
+ cancellable = subject
184
+ .receive(on: DispatchQueue.main)
185
+ .sink { [weak self] metrics in
186
+ self?.update(metrics)
187
+ }
188
+ }
189
+
190
+ func subscribeAdjacent(
191
+ prev: CurrentValueSubject<DebugMetrics, Never>,
192
+ next: CurrentValueSubject<DebugMetrics, Never>
193
+ ) {
194
+ prevCancellable = prev
195
+ .receive(on: DispatchQueue.main)
196
+ .sink { [weak self] metrics in
197
+ self?.updateAdjacent(metrics, slot: .previous)
198
+ }
199
+ nextCancellable = next
200
+ .receive(on: DispatchQueue.main)
201
+ .sink { [weak self] metrics in
202
+ self?.updateAdjacent(metrics, slot: .next)
203
+ }
204
+ }
205
+
206
+ private func updateAdjacent(_ m: DebugMetrics, slot: AdjacentSlot) {
207
+ let headerLabel = slot == .previous ? prevHeaderLabel : nextHeaderLabel
208
+ let detailLabel = slot == .previous ? prevDetailLabel : nextDetailLabel
209
+ let arrow = slot == .previous ? "▲ Prev" : "▼ Next"
210
+
211
+ guard m.contentId != nil else {
212
+ headerLabel.text = "\(arrow) — not assigned"
213
+ detailLabel.text = ""
214
+ return
215
+ }
216
+
217
+ let id = m.contentId.map { String($0.prefix(8)) } ?? "—"
218
+ let stateIcon: String
219
+ switch m.playerState {
220
+ case .playing: stateIcon = "▶"
221
+ case .paused: stateIcon = "⏸"
222
+ case .buffering: stateIcon = "⏳"
223
+ default: stateIcon = "○"
224
+ }
225
+ headerLabel.text = "\(arrow) #\(m.feedIndex) \(id) \(stateIcon) \(m.playerState)"
226
+
227
+ let buf = String(format: "%.1f", m.bufferAhead)
228
+ let preroll = m.prerollCompleted ? "✓" : "✗"
229
+ let mbps = m.observedBitrateBps / 1_000_000
230
+ var detail = " buf: \(buf)s preroll: \(preroll)"
231
+ if mbps > 0 {
232
+ detail += String(format: " %.1f Mbps", mbps)
233
+ }
234
+ if m.videoWidth > 0 {
235
+ let codec = m.codec ?? ""
236
+ detail += " \(m.videoWidth)×\(m.videoHeight) \(codec)"
237
+ }
238
+ detailLabel.text = detail
239
+ }
240
+
241
+ private func update(_ m: DebugMetrics) {
242
+ let id = m.contentId.map { String($0.prefix(8)) } ?? "—"
243
+ let stateIcon: String
244
+ switch m.playerState {
245
+ case .playing: stateIcon = "▶"
246
+ case .paused: stateIcon = "⏸"
247
+ case .buffering: stateIcon = "⏳"
248
+ case .seeking: stateIcon = "⏩"
249
+ case .error: stateIcon = "⚠️"
250
+ default: stateIcon = "○"
251
+ }
252
+ headerLabel.text = "#\(m.feedIndex) \(id) \(stateIcon) \(m.playerState)"
253
+
254
+ let cur = String(format: "%.1f", m.currentTime)
255
+ let dur = String(format: "%.1f", m.duration)
256
+ positionLabel.text = "Position: \(cur)s / \(dur)s"
257
+
258
+ if let intent = m.intentToFrameMs {
259
+ intentTtffLabel.text = "Intent: \(intent)ms"
260
+ } else {
261
+ intentTtffLabel.text = "Intent: N/A"
262
+ }
263
+
264
+ if let transition = m.transitionToFrameMs {
265
+ let net = m.networkMs.map { "\($0)" } ?? "?"
266
+ let dec = m.decodeMs.map { "\($0)" } ?? "?"
267
+ transitionTtffLabel.text = "Transition: \(transition)ms (net: \(net) + dec: \(dec))"
268
+ } else {
269
+ transitionTtffLabel.text = "Transition: measuring..."
270
+ }
271
+
272
+ let preroll = m.prerollCompleted ? "✓" : "✗"
273
+ let buffer = String(format: "%.1f", m.bufferAhead)
274
+ prerollBufferLabel.text = "Preroll: \(preroll) Buffer: \(buffer)s"
275
+
276
+ let codec = m.codec ?? "—"
277
+ if m.videoWidth > 0 {
278
+ videoQualityLabel.text = "Video: \(m.videoWidth)×\(m.videoHeight) \(codec)"
279
+ } else {
280
+ videoQualityLabel.text = "Video: —"
281
+ }
282
+
283
+ thumbnailLabel.text = "Thumbnail: \(m.thumbnailVisible ? "visible" : "removed")"
284
+ prefetchLabel.text = "Prefetch: \(m.thumbsCachedAhead)/\(m.metadataAhead) thumbs \(m.metadataAhead) meta"
285
+
286
+ let obsMbps = m.observedBitrateBps / 1_000_000
287
+ let indMbps = m.indicatedBitrateBps / 1_000_000
288
+ if obsMbps > 0 {
289
+ bitrateLabel.text = String(format: "Bitrate: %.1f Mbps (ind: %.1f)", obsMbps, indMbps)
290
+ } else {
291
+ bitrateLabel.text = "Bitrate: —"
292
+ }
293
+
294
+ if let lastMs = m.lastRebufferDurationMs {
295
+ rebufferLabel.text = "Rebuffers: \(m.rebufferCount) (last: \(lastMs)ms)"
296
+ } else {
297
+ rebufferLabel.text = "Rebuffers: \(m.rebufferCount)"
298
+ }
299
+
300
+ loopWatchLabel.text = "Loops: \(m.loopCount)"
301
+ }
302
+ }
@@ -97,6 +97,7 @@ import ShortKitSDK
97
97
  clientAppName: String?,
98
98
  clientAppVersion: String?,
99
99
  customDimensions customDimensionsJSON: String?,
100
+ debugPanel: Bool,
100
101
  delegate: ShortKitBridgeDelegateProtocol,
101
102
  surfacePresenter: AnyObject?
102
103
  ) {
@@ -110,7 +111,8 @@ import ShortKitSDK
110
111
  apiKey: apiKey,
111
112
  clientAppName: clientAppName,
112
113
  clientAppVersion: clientAppVersion,
113
- customDimensions: dimensions
114
+ customDimensions: dimensions,
115
+ debugPanelEnabled: debugPanel
114
116
  )
115
117
  self.shortKit = sdk
116
118
 
@@ -676,7 +678,7 @@ import ShortKitSDK
676
678
  /// Expected JSON structure:
677
679
  /// ```json
678
680
  /// {"feedHeight":"{\"type\":\"fullscreen\"}","overlay":"\"none\"",
679
- /// "carouselMode":"\"none\"","surveyMode":"\"none\"","muteOnStart":true}
681
+ /// "carouselMode":"\"none\"","muteOnStart":true}
680
682
  /// ```
681
683
  public static func parseFeedConfig(_ json: String) -> FeedConfig {
682
684
  guard let data = json.data(using: .utf8),
@@ -842,7 +844,7 @@ import ShortKitSDK
842
844
  }
843
845
 
844
846
  /// Parse a JSON string of FeedFilter from the JS bridge.
845
- private static func parseFeedFilter(_ json: String?) -> FeedFilter? {
847
+ static func parseFeedFilter(_ json: String?) -> FeedFilter? {
846
848
  guard let json,
847
849
  let data = json.data(using: .utf8),
848
850
  let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
@@ -104,6 +104,21 @@ import ShortKitSDK
104
104
  let feedVC = ShortKitFeedViewController(shortKit: sdk, config: feedConfig, startAtItemId: startAtItemId)
105
105
  feedVC.setBridgeManaged()
106
106
 
107
+ if sdk.debugPanelEnabled {
108
+ feedVC.debugPanelFactory = { active, prev, next in
109
+ let panel = DebugPanelView(frame: CGRect(
110
+ x: 0, y: 0,
111
+ width: DebugPanelView.panelWidth,
112
+ height: DebugPanelView.panelHeight
113
+ ))
114
+ panel.center = CGPoint(x: UIScreen.main.bounds.width / 2,
115
+ y: UIScreen.main.bounds.height / 2)
116
+ panel.subscribe(to: active)
117
+ panel.subscribeAdjacent(prev: prev, next: next)
118
+ return panel
119
+ }
120
+ }
121
+
107
122
  NSLog("[ShortKit FeedView] VC created successfully (bridge-managed)")
108
123
 
109
124
  feedVC.onDismiss = {
@@ -68,7 +68,6 @@ RCT_EXPORT_MODULE(ShortKitModule)
68
68
  @"onFormatChange",
69
69
  @"onPrefetchedAheadCountChanged",
70
70
  @"onRemainingContentCountChanged",
71
- @"onSurveyResponse",
72
71
  @"onContentTapped",
73
72
  @"onDismiss",
74
73
  @"onFeedReady",
@@ -124,7 +123,8 @@ RCT_EXPORT_METHOD(initialize:(NSString *)apiKey
124
123
  hasLoadingView:(BOOL)hasLoadingView
125
124
  clientAppName:(NSString *)clientAppName
126
125
  clientAppVersion:(NSString *)clientAppVersion
127
- customDimensions:(NSString *)customDimensions) {
126
+ customDimensions:(NSString *)customDimensions
127
+ debugPanel:(NSNumber *)debugPanel) {
128
128
  // Tear down any existing instance to prevent leaks on re-initialize
129
129
  [_shortKitBridge teardown];
130
130
 
@@ -133,6 +133,7 @@ RCT_EXPORT_METHOD(initialize:(NSString *)apiKey
133
133
  clientAppName:clientAppName
134
134
  clientAppVersion:clientAppVersion
135
135
  customDimensions:customDimensions
136
+ debugPanel:[debugPanel boolValue]
136
137
  delegate:self
137
138
  surfacePresenter:_surfacePresenter];
138
139
 
@@ -8,32 +8,32 @@
8
8
  <key>BinaryPath</key>
9
9
  <string>ShortKitSDK.framework/ShortKitSDK</string>
10
10
  <key>LibraryIdentifier</key>
11
- <string>ios-arm64</string>
11
+ <string>ios-arm64_x86_64-simulator</string>
12
12
  <key>LibraryPath</key>
13
13
  <string>ShortKitSDK.framework</string>
14
14
  <key>SupportedArchitectures</key>
15
15
  <array>
16
16
  <string>arm64</string>
17
+ <string>x86_64</string>
17
18
  </array>
18
19
  <key>SupportedPlatform</key>
19
20
  <string>ios</string>
21
+ <key>SupportedPlatformVariant</key>
22
+ <string>simulator</string>
20
23
  </dict>
21
24
  <dict>
22
25
  <key>BinaryPath</key>
23
26
  <string>ShortKitSDK.framework/ShortKitSDK</string>
24
27
  <key>LibraryIdentifier</key>
25
- <string>ios-arm64_x86_64-simulator</string>
28
+ <string>ios-arm64</string>
26
29
  <key>LibraryPath</key>
27
30
  <string>ShortKitSDK.framework</string>
28
31
  <key>SupportedArchitectures</key>
29
32
  <array>
30
33
  <string>arm64</string>
31
- <string>x86_64</string>
32
34
  </array>
33
35
  <key>SupportedPlatform</key>
34
36
  <string>ios</string>
35
- <key>SupportedPlatformVariant</key>
36
- <string>simulator</string>
37
37
  </dict>
38
38
  </array>
39
39
  <key>CFBundlePackageType</key>
@@ -11,9 +11,9 @@
11
11
  <key>CFBundlePackageType</key>
12
12
  <string>FMWK</string>
13
13
  <key>CFBundleVersion</key>
14
- <string>0.2.16</string>
14
+ <string>0.2.23</string>
15
15
  <key>CFBundleShortVersionString</key>
16
- <string>0.2.16</string>
16
+ <string>0.2.23</string>
17
17
  <key>MinimumOSVersion</key>
18
18
  <string>16.0</string>
19
19
  </dict>