@shortkitsdk/react-native 0.2.29 → 0.2.31

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 (37) hide show
  1. package/android/libs/shortkit-release.aar +0 -0
  2. package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +8 -1
  3. package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedViewManager.kt +6 -0
  4. package/ios/ShortKitBridge.swift +5 -1
  5. package/ios/ShortKitFeedView.swift +9 -13
  6. package/ios/ShortKitFeedViewManager.mm +1 -0
  7. package/ios/ShortKitModule.mm +5 -1
  8. package/ios/ShortKitSDK.xcframework/Info.plist +5 -5
  9. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Info.plist +2 -2
  10. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +4732 -872
  11. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +77 -8
  12. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  13. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +77 -8
  14. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
  15. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources +9 -9
  16. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Info.plist +2 -2
  17. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +4732 -872
  18. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +77 -8
  19. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  20. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +77 -8
  21. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +4732 -872
  22. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +77 -8
  23. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  24. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +77 -8
  25. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
  26. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/_CodeSignature/CodeResources +17 -17
  27. package/ios/ShortKitWidgetNativeView.swift +23 -0
  28. package/ios/ShortKitWidgetNativeViewManager.mm +1 -0
  29. package/package.json +1 -1
  30. package/src/ShortKitFeed.tsx +2 -0
  31. package/src/ShortKitProvider.tsx +4 -0
  32. package/src/ShortKitWidget.tsx +47 -2
  33. package/src/specs/NativeShortKitModule.ts +9 -0
  34. package/src/specs/ShortKitFeedViewNativeComponent.ts +5 -0
  35. package/src/specs/ShortKitWidgetViewNativeComponent.ts +6 -0
  36. package/src/types.ts +21 -0
  37. package/ios/DebugPanelView.swift +0 -302
package/src/types.ts CHANGED
@@ -293,6 +293,10 @@ export interface ShortKitProviderProps {
293
293
  loadingViewComponent?: React.ComponentType<{}>;
294
294
  /** Enable the debug panel overlay (shake device to toggle). Defaults to false. */
295
295
  debugPanel?: boolean;
296
+ /** Enable production trace logging to the ShortKit backend. Defaults to false. */
297
+ serverTracingEnabled?: boolean;
298
+ /** Enable [SKTrace] console output. For development only — defaults to false. */
299
+ consoleTracingEnabled?: boolean;
296
300
  }
297
301
 
298
302
  // --- Feed Component Props ---
@@ -342,6 +346,12 @@ export interface ShortKitFeedProps {
342
346
  * @default true
343
347
  */
344
348
  active?: boolean;
349
+ /**
350
+ * Enable the debug panel overlay on this specific feed surface (shake
351
+ * device to toggle). When omitted, falls back to the provider-level
352
+ * `debugPanel` flag on `<ShortKitProvider>`.
353
+ */
354
+ debugPanel?: boolean;
345
355
  onLoop?: (event: LoopEvent) => void;
346
356
  onFeedTransition?: (event: FeedTransitionEvent) => void;
347
357
  onFormatChange?: (event: FormatChangeEvent) => void;
@@ -474,6 +484,17 @@ export interface ShortKitWidgetProps {
474
484
  * how `<ShortKitFeed feedItems={...} />` works.
475
485
  */
476
486
  items?: WidgetInput[];
487
+ /**
488
+ * Per-card tap callback. When set, the SDK skips the built-in
489
+ * `clickAction: 'feed'` modal-presentation path and hands the tap to the
490
+ * host — typically used to push a feed screen via the host's navigator
491
+ * instead of letting the SDK present a UIKit modal that escapes the host's
492
+ * navigation hierarchy.
493
+ *
494
+ * Mirrors `ShortKitPlayer.onTap`. `clickAction: 'mute'` continues to work
495
+ * regardless (per-cell mute toggle runs alongside `onCardTap`).
496
+ */
497
+ onCardTap?: (playbackId: string, index: number) => void;
477
498
  style?: ViewStyle;
478
499
  }
479
500
 
@@ -1,302 +0,0 @@
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
- }