sitepong 0.2.3 → 0.2.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 (57) hide show
  1. package/android/build.gradle +39 -0
  2. package/android/src/main/java/com/sitepong/deviceid/DeviceIdHelper.kt +45 -0
  3. package/android/src/main/java/com/sitepong/deviceid/DeviceIdModule.kt +45 -0
  4. package/android/src/main/java/com/sitepong/screenrecorder/ChunkUploader.kt +78 -0
  5. package/android/src/main/java/com/sitepong/screenrecorder/H264Encoder.kt +163 -0
  6. package/android/src/main/java/com/sitepong/screenrecorder/ScreenCapture.kt +103 -0
  7. package/android/src/main/java/com/sitepong/screenrecorder/ScreenRecorderModule.kt +80 -0
  8. package/android/src/main/java/com/sitepong/screenrecorder/SensitiveViewManager.kt +19 -0
  9. package/app.plugin.js +426 -0
  10. package/dist/cdn/sitepong.min.js +5 -5
  11. package/dist/cdn/sitepong.min.js.map +1 -1
  12. package/dist/entries/rn.d.ts +188 -16
  13. package/dist/entries/rn.js +188 -35
  14. package/dist/entries/rn.js.map +1 -1
  15. package/dist/entries/web.js +2 -1
  16. package/dist/entries/web.js.map +1 -1
  17. package/dist/entries/web.mjs +2 -1
  18. package/dist/entries/web.mjs.map +1 -1
  19. package/dist/index.d.mts +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.js +2 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/index.mjs +2 -1
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/react/index.js +2 -1
  26. package/dist/react/index.js.map +1 -1
  27. package/dist/react/index.mjs +2 -1
  28. package/dist/react/index.mjs.map +1 -1
  29. package/expo-module.config.json +16 -0
  30. package/ios/DeviceId/DeviceIdModule.swift +55 -0
  31. package/ios/DeviceId/KeychainHelper.swift +68 -0
  32. package/ios/LiveActivity/ConfigStore.swift +43 -0
  33. package/ios/LiveActivity/DSLNode.swift +188 -0
  34. package/ios/LiveActivity/SitePongActivityAttributes.swift +112 -0
  35. package/ios/LiveActivity/SitePongLiveActivityModule.swift +250 -0
  36. package/ios/LiveActivity/widget/ColorHex.swift +30 -0
  37. package/ios/LiveActivity/widget/DSLAnimations.swift +128 -0
  38. package/ios/LiveActivity/widget/DSLRenderer.swift +538 -0
  39. package/ios/LiveActivity/widget/SitePongLiveActivityWidget.swift +393 -0
  40. package/ios/LiveActivity/widget/SitePongWidgetBundle.swift +84 -0
  41. package/ios/ScreenRecorder/ChunkUploader.swift +52 -0
  42. package/ios/ScreenRecorder/H264Encoder.swift +229 -0
  43. package/ios/ScreenRecorder/ScreenCapture.swift +149 -0
  44. package/ios/ScreenRecorder/ScreenRecorderModule.swift +179 -0
  45. package/ios/ScreenRecorder/SensitiveViewManager.swift +63 -0
  46. package/ios/Sitepong.podspec +57 -0
  47. package/ios/WatchtowerCore/Screenshotter.swift +163 -0
  48. package/ios/WatchtowerCore/SwiftUIModifiers.swift +78 -0
  49. package/ios/WatchtowerCore/Swizzling.swift +59 -0
  50. package/ios/WatchtowerCore/TapEvent.swift +61 -0
  51. package/ios/WatchtowerCore/Transport.swift +136 -0
  52. package/ios/WatchtowerCore/UIViewExtensions.swift +31 -0
  53. package/ios/WatchtowerCore/Watchtower.swift +80 -0
  54. package/ios/WatchtowerCore/WatchtowerEngine.swift +565 -0
  55. package/ios/WatchtowerCore/WatchtowerHash.swift +120 -0
  56. package/ios/WatchtowerCore/WatchtowerRegistry.swift +87 -0
  57. package/package.json +17 -10
@@ -0,0 +1,16 @@
1
+ {
2
+ "platforms": ["ios", "android"],
3
+ "ios": {
4
+ "modules": [
5
+ "SitePongScreenRecorderModule",
6
+ "SitePongDeviceIdModule",
7
+ "SitePongLiveActivityModule"
8
+ ]
9
+ },
10
+ "android": {
11
+ "modules": [
12
+ "com.sitepong.screenrecorder.ScreenRecorderModule",
13
+ "com.sitepong.deviceid.DeviceIdModule"
14
+ ]
15
+ }
16
+ }
@@ -0,0 +1,55 @@
1
+ import ExpoModulesCore
2
+ import UIKit
3
+
4
+ public class SitePongDeviceIdModule: Module {
5
+ public func definition() -> ModuleDefinition {
6
+ Name("SitePongDeviceId")
7
+
8
+ AsyncFunction("getDeviceId") { (promise: Promise) in
9
+ let deviceId = KeychainHelper.getOrCreateDeviceId()
10
+ promise.resolve(deviceId)
11
+ }
12
+
13
+ AsyncFunction("getDeviceSignals") { (promise: Promise) in
14
+ let deviceId = KeychainHelper.getOrCreateDeviceId()
15
+ let device = UIDevice.current
16
+ let screen = UIScreen.main
17
+
18
+ var signals: [String: Any] = [
19
+ "deviceId": deviceId,
20
+ "platform": "ios",
21
+ "osVersion": device.systemVersion,
22
+ "model": self.modelIdentifier(),
23
+ "manufacturer": "Apple",
24
+ "isEmulator": self.isSimulator(),
25
+ "screenScale": screen.scale,
26
+ ]
27
+
28
+ if let idfv = device.identifierForVendor?.uuidString {
29
+ signals["identifierForVendor"] = idfv
30
+ }
31
+
32
+ promise.resolve(signals)
33
+ }
34
+ }
35
+
36
+ /// Returns the hardware model identifier (e.g. "iPhone15,2")
37
+ private func modelIdentifier() -> String {
38
+ var systemInfo = utsname()
39
+ uname(&systemInfo)
40
+ let mirror = Mirror(reflecting: systemInfo.machine)
41
+ let identifier = mirror.children.reduce("") { identifier, element in
42
+ guard let value = element.value as? Int8, value != 0 else { return identifier }
43
+ return identifier + String(UnicodeScalar(UInt8(value)))
44
+ }
45
+ return identifier
46
+ }
47
+
48
+ private func isSimulator() -> Bool {
49
+ #if targetEnvironment(simulator)
50
+ return true
51
+ #else
52
+ return false
53
+ #endif
54
+ }
55
+ }
@@ -0,0 +1,68 @@
1
+ import Foundation
2
+ import Security
3
+
4
+ /// Manages a persistent UUID in the iOS Keychain.
5
+ /// Keychain items survive app uninstall/reinstall — they persist until the device
6
+ /// is factory-reset or the item is explicitly deleted.
7
+ final class KeychainHelper {
8
+ private static let service = "com.sitepong.device-id"
9
+ private static let account = "persistent_uuid"
10
+
11
+ /// Read or create the persistent device UUID from Keychain.
12
+ static func getOrCreateDeviceId() -> String {
13
+ // Try to read existing
14
+ if let existing = read() {
15
+ return existing
16
+ }
17
+
18
+ // Generate new UUID and store it
19
+ let uuid = UUID().uuidString
20
+ save(uuid)
21
+ return uuid
22
+ }
23
+
24
+ private static func read() -> String? {
25
+ let query: [String: Any] = [
26
+ kSecClass as String: kSecClassGenericPassword,
27
+ kSecAttrService as String: service,
28
+ kSecAttrAccount as String: account,
29
+ kSecReturnData as String: true,
30
+ kSecMatchLimit as String: kSecMatchLimitOne,
31
+ ]
32
+
33
+ var item: CFTypeRef?
34
+ let status = SecItemCopyMatching(query as CFDictionary, &item)
35
+
36
+ guard status == errSecSuccess,
37
+ let data = item as? Data,
38
+ let value = String(data: data, encoding: .utf8) else {
39
+ return nil
40
+ }
41
+
42
+ return value
43
+ }
44
+
45
+ private static func save(_ value: String) {
46
+ guard let data = value.data(using: .utf8) else { return }
47
+
48
+ let query: [String: Any] = [
49
+ kSecClass as String: kSecClassGenericPassword,
50
+ kSecAttrService as String: service,
51
+ kSecAttrAccount as String: account,
52
+ kSecValueData as String: data,
53
+ kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
54
+ ]
55
+
56
+ // Delete any existing item first (in case of corruption)
57
+ SecItemDelete([
58
+ kSecClass as String: kSecClassGenericPassword,
59
+ kSecAttrService as String: service,
60
+ kSecAttrAccount as String: account,
61
+ ] as CFDictionary)
62
+
63
+ let status = SecItemAdd(query as CFDictionary, nil)
64
+ if status != errSecSuccess {
65
+ print("[SitePong DeviceId] Keychain save failed: \(status)")
66
+ }
67
+ }
68
+ }
@@ -0,0 +1,43 @@
1
+ import Foundation
2
+
3
+ /// Reads and writes widget DSL configs via App Group UserDefaults.
4
+ /// Shared between the main app (Expo module) and the widget extension.
5
+ struct WidgetConfigStore {
6
+ private static let appGroupId = "group.com.sitepong.widgets"
7
+ private static let configsKey = "sitepong_widget_configs"
8
+ private static let dataKey = "sitepong_widget_data"
9
+
10
+ private static var defaults: UserDefaults? {
11
+ UserDefaults(suiteName: appGroupId)
12
+ }
13
+
14
+ // MARK: - Config (DSLNode trees)
15
+
16
+ /// Save raw config JSON data to the shared App Group container.
17
+ static func saveConfigs(_ configs: Data) {
18
+ defaults?.set(configs, forKey: configsKey)
19
+ defaults?.synchronize()
20
+ }
21
+
22
+ /// Load and decode widget configs from the App Group container.
23
+ static func loadConfigs() -> [DSLNode]? {
24
+ guard let data = defaults?.data(forKey: configsKey) else { return nil }
25
+ let decoder = JSONDecoder()
26
+ return try? decoder.decode([DSLNode].self, from: data)
27
+ }
28
+
29
+ // MARK: - Data (variable values for interpolation)
30
+
31
+ /// Save variable data JSON for widget rendering.
32
+ static func saveData(_ data: Data) {
33
+ defaults?.set(data, forKey: dataKey)
34
+ defaults?.synchronize()
35
+ }
36
+
37
+ /// Load variable data dictionary from the App Group container.
38
+ static func loadData() -> [String: AnyCodable] {
39
+ guard let data = defaults?.data(forKey: dataKey) else { return [:] }
40
+ let decoder = JSONDecoder()
41
+ return (try? decoder.decode([String: AnyCodable].self, from: data)) ?? [:]
42
+ }
43
+ }
@@ -0,0 +1,188 @@
1
+ import Foundation
2
+
3
+ // MARK: - AnyCodable
4
+
5
+ /// A simple JSON-compatible value wrapper with typed accessors.
6
+ public struct AnyCodable: Codable, Hashable {
7
+ public let value: Any
8
+
9
+ public init(_ value: Any) {
10
+ self.value = value
11
+ }
12
+
13
+ public var stringValue: String? { value as? String }
14
+ public var doubleValue: Double? {
15
+ if let d = value as? Double { return d }
16
+ if let i = value as? Int { return Double(i) }
17
+ if let s = value as? String { return Double(s) }
18
+ return nil
19
+ }
20
+ public var intValue: Int? {
21
+ if let i = value as? Int { return i }
22
+ if let d = value as? Double { return Int(d) }
23
+ return nil
24
+ }
25
+ public var boolValue: Bool? { value as? Bool }
26
+ public var arrayValue: [AnyCodable]? { value as? [AnyCodable] }
27
+ public var dictionaryValue: [String: AnyCodable]? { value as? [String: AnyCodable] }
28
+
29
+ // MARK: - Codable
30
+
31
+ public init(from decoder: Decoder) throws {
32
+ let container = try decoder.singleValueContainer()
33
+ if container.decodeNil() {
34
+ value = NSNull()
35
+ } else if let b = try? container.decode(Bool.self) {
36
+ value = b
37
+ } else if let i = try? container.decode(Int.self) {
38
+ value = i
39
+ } else if let d = try? container.decode(Double.self) {
40
+ value = d
41
+ } else if let s = try? container.decode(String.self) {
42
+ value = s
43
+ } else if let arr = try? container.decode([AnyCodable].self) {
44
+ value = arr
45
+ } else if let dict = try? container.decode([String: AnyCodable].self) {
46
+ value = dict
47
+ } else {
48
+ throw DecodingError.dataCorruptedError(in: container, debugDescription: "Unsupported JSON value")
49
+ }
50
+ }
51
+
52
+ public func encode(to encoder: Encoder) throws {
53
+ var container = encoder.singleValueContainer()
54
+ if value is NSNull {
55
+ try container.encodeNil()
56
+ } else if let b = value as? Bool {
57
+ try container.encode(b)
58
+ } else if let i = value as? Int {
59
+ try container.encode(i)
60
+ } else if let d = value as? Double {
61
+ try container.encode(d)
62
+ } else if let s = value as? String {
63
+ try container.encode(s)
64
+ } else if let arr = value as? [AnyCodable] {
65
+ try container.encode(arr)
66
+ } else if let dict = value as? [String: AnyCodable] {
67
+ try container.encode(dict)
68
+ } else {
69
+ try container.encodeNil()
70
+ }
71
+ }
72
+
73
+ // MARK: - Hashable
74
+
75
+ public static func == (lhs: AnyCodable, rhs: AnyCodable) -> Bool {
76
+ // Use JSON encoding for equality comparison
77
+ let encoder = JSONEncoder()
78
+ encoder.outputFormatting = .sortedKeys
79
+ guard let lData = try? encoder.encode(lhs),
80
+ let rData = try? encoder.encode(rhs) else {
81
+ return false
82
+ }
83
+ return lData == rData
84
+ }
85
+
86
+ public func hash(into hasher: inout Hasher) {
87
+ let encoder = JSONEncoder()
88
+ encoder.outputFormatting = .sortedKeys
89
+ if let data = try? encoder.encode(self) {
90
+ hasher.combine(data)
91
+ }
92
+ }
93
+ }
94
+
95
+ // MARK: - DSLNode
96
+
97
+ public struct DSLNode: Codable, Hashable, Identifiable {
98
+ public var id: String
99
+ public var type: String
100
+ public var props: [String: AnyCodable]?
101
+ public var style: DSLStyle?
102
+ public var animation: DSLAnimation?
103
+ public var children: [DSLNode]?
104
+ public var binding: String?
105
+ }
106
+
107
+ // MARK: - DSLStyle
108
+
109
+ public struct DSLStyle: Codable, Hashable {
110
+ public var padding: DSLPadding?
111
+ public var backgroundColor: String?
112
+ public var backgroundGradient: DSLGradient?
113
+ public var cornerRadius: Double?
114
+ public var opacity: Double?
115
+ public var shadow: DSLShadow?
116
+ public var frame: DSLFrame?
117
+ public var foregroundColor: String?
118
+ }
119
+
120
+ public struct DSLPadding: Codable, Hashable {
121
+ public var all: Double?
122
+ public var horizontal: Double?
123
+ public var vertical: Double?
124
+ public var top: Double?
125
+ public var bottom: Double?
126
+ public var leading: Double?
127
+ public var trailing: Double?
128
+
129
+ public init(from decoder: Decoder) throws {
130
+ let container = try decoder.singleValueContainer()
131
+ if let single = try? container.decode(Double.self) {
132
+ self.all = single
133
+ return
134
+ }
135
+ if let array = try? container.decode([Double].self), array.count == 4 {
136
+ self.top = array[0]
137
+ self.trailing = array[1]
138
+ self.bottom = array[2]
139
+ self.leading = array[3]
140
+ return
141
+ }
142
+ let keyed = try decoder.container(keyedBy: CodingKeys.self)
143
+ self.all = try keyed.decodeIfPresent(Double.self, forKey: .all)
144
+ self.horizontal = try keyed.decodeIfPresent(Double.self, forKey: .horizontal)
145
+ self.vertical = try keyed.decodeIfPresent(Double.self, forKey: .vertical)
146
+ self.top = try keyed.decodeIfPresent(Double.self, forKey: .top)
147
+ self.bottom = try keyed.decodeIfPresent(Double.self, forKey: .bottom)
148
+ self.leading = try keyed.decodeIfPresent(Double.self, forKey: .leading)
149
+ self.trailing = try keyed.decodeIfPresent(Double.self, forKey: .trailing)
150
+ }
151
+
152
+ private enum CodingKeys: String, CodingKey {
153
+ case all, horizontal, vertical, top, bottom, leading, trailing
154
+ }
155
+ }
156
+
157
+ public struct DSLGradient: Codable, Hashable {
158
+ public var colors: [String]
159
+ public var startPoint: String?
160
+ public var endPoint: String?
161
+ }
162
+
163
+ public struct DSLShadow: Codable, Hashable {
164
+ public var color: String?
165
+ public var radius: Double?
166
+ public var x: Double?
167
+ public var y: Double?
168
+ }
169
+
170
+ public struct DSLFrame: Codable, Hashable {
171
+ public var width: Double?
172
+ public var height: Double?
173
+ public var minWidth: Double?
174
+ public var minHeight: Double?
175
+ public var maxWidth: Double?
176
+ public var maxHeight: Double?
177
+ }
178
+
179
+ // MARK: - DSLAnimation
180
+
181
+ public struct DSLAnimation: Codable, Hashable {
182
+ public var entrance: String?
183
+ public var entranceDuration: Double?
184
+ public var entranceDelay: Double?
185
+ public var valueTransition: String?
186
+ public var ambient: String?
187
+ public var ambientDuration: Double?
188
+ }
@@ -0,0 +1,112 @@
1
+ import ActivityKit
2
+ import Foundation
3
+
4
+ /// Single shared ActivityAttributes type used by every SitePong Live Activity.
5
+ ///
6
+ /// Every visual element is optional — the SwiftUI template hides slots whose
7
+ /// value is nil, so customers can drive layout density entirely from data
8
+ /// without modifying the widget.
9
+ @available(iOS 16.2, *)
10
+ public struct SitePongActivityAttributes: ActivityAttributes, Codable {
11
+ public typealias ContentState = State
12
+
13
+ public struct State: Codable, Hashable {
14
+ public var title: String?
15
+ public var subtitle: String?
16
+ public var titleStyle: TextStyleSpec?
17
+ public var subtitleStyle: TextStyleSpec?
18
+
19
+ public var leadingIcon: IconSpec?
20
+ public var trailingIcon: IconSpec?
21
+
22
+ public var progress: ProgressSpec?
23
+
24
+ public var backgroundColor: String?
25
+
26
+ public var dynamicIsland: DynamicIslandSpec?
27
+
28
+ /// Optional DSL layout. When non-nil, the renderer uses DSLView
29
+ /// instead of the fixed slot-based layout.
30
+ public var dslLayout: DSLNode?
31
+
32
+ public init(
33
+ title: String? = nil,
34
+ subtitle: String? = nil,
35
+ titleStyle: TextStyleSpec? = nil,
36
+ subtitleStyle: TextStyleSpec? = nil,
37
+ leadingIcon: IconSpec? = nil,
38
+ trailingIcon: IconSpec? = nil,
39
+ progress: ProgressSpec? = nil,
40
+ backgroundColor: String? = nil,
41
+ dynamicIsland: DynamicIslandSpec? = nil,
42
+ dslLayout: DSLNode? = nil
43
+ ) {
44
+ self.title = title
45
+ self.subtitle = subtitle
46
+ self.titleStyle = titleStyle
47
+ self.subtitleStyle = subtitleStyle
48
+ self.leadingIcon = leadingIcon
49
+ self.trailingIcon = trailingIcon
50
+ self.progress = progress
51
+ self.backgroundColor = backgroundColor
52
+ self.dynamicIsland = dynamicIsland
53
+ self.dslLayout = dslLayout
54
+ }
55
+ }
56
+
57
+ public var id: String?
58
+ public var metadata: [String: String]?
59
+
60
+ public init(id: String? = nil, metadata: [String: String]? = nil) {
61
+ self.id = id
62
+ self.metadata = metadata
63
+ }
64
+ }
65
+
66
+ // MARK: - Visual specs
67
+
68
+ public struct IconSpec: Codable, Hashable {
69
+ public var symbol: String
70
+ public var color: String?
71
+ public var weight: String?
72
+ public var size: Double?
73
+ }
74
+
75
+ public struct TextStyleSpec: Codable, Hashable {
76
+ public var size: Double?
77
+ public var weight: String?
78
+ public var color: String?
79
+ public var italic: Bool?
80
+ public var monospacedDigit: Bool?
81
+ }
82
+
83
+ public struct ProgressSpec: Codable, Hashable {
84
+ /// "bar", "bar-with-icon", "circular", "timer"
85
+ public var style: String
86
+
87
+ public var value: Double?
88
+ public var icon: String?
89
+ public var iconColor: String?
90
+ public var iconWeight: String?
91
+ public var trackColor: String?
92
+ public var fillColor: String?
93
+ public var label: String?
94
+ public var labelStyle: TextStyleSpec?
95
+
96
+ /// Epoch milliseconds for "timer" style.
97
+ public var endDate: Double?
98
+ public var startDate: Double?
99
+ }
100
+
101
+ public struct DynamicIslandSpec: Codable, Hashable {
102
+ public var compactLeading: DynamicIslandSlot?
103
+ public var compactTrailing: DynamicIslandSlot?
104
+ public var minimal: IconSpec?
105
+ }
106
+
107
+ public struct DynamicIslandSlot: Codable, Hashable {
108
+ // One of `icon` or `text` is set.
109
+ public var icon: IconSpec?
110
+ public var text: String?
111
+ public var style: TextStyleSpec?
112
+ }