craft-native 0.0.369 → 0.0.375
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.
- package/dist/android/templates/CraftNativeCapabilities.kt.template +50 -28
- package/dist/android/templates/CraftNativeSounds.kt.template +44 -19
- package/dist/api/mobile.d.ts +3 -1
- package/dist/cli.js +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/ios/src/index.d.ts +6 -2
- package/dist/ios/src/index.js +338 -179
- package/dist/ios/src/runtime-profile.d.ts +13 -0
- package/dist/ios/templates/CraftApp.swift +279 -3
- package/dist/ios/templates/CraftBluetooth.swift +12 -0
- package/dist/ios/templates/CraftHealth.swift +124 -8
- package/dist/ios/templates/CraftHeartRate.swift +13 -0
- package/dist/ios/templates/CraftHybrid.swift +52 -11
- package/dist/ios/templates/CraftLiveActivities.swift +2 -0
- package/dist/ios/templates/CraftLiveActivityWidget.swift.template +13 -1
- package/dist/ios/templates/CraftMusic.swift +23 -0
- package/dist/ios/templates/CraftNativeActions.swift +54 -8
- package/dist/ios/templates/CraftNativeFiles.swift +7 -0
- package/dist/ios/templates/CraftNativeScreen.swift +155 -0
- package/dist/ios/templates/CraftNativeServices.swift +19 -13
- package/dist/ios/templates/CraftVoiceRecording.swift +13 -1
- package/dist/mobile.js +6 -0
- package/package.json +1 -1
|
@@ -222,7 +222,7 @@ internal class CraftNativeCapabilities(
|
|
|
222
222
|
"pickVideo" -> pickVideo(requestToken, args, completion)
|
|
223
223
|
"pickFile" -> pickFile(requestToken, args, completion)
|
|
224
224
|
"download", "write", "remove" -> networkExecutor.execute {
|
|
225
|
-
|
|
225
|
+
completeFile(requestToken, files(requestToken, method, args), completion)
|
|
226
226
|
}
|
|
227
227
|
else -> complete(requestToken, failure("UNKNOWN_ACTION", "Unsupported Files method"), completion)
|
|
228
228
|
}
|
|
@@ -492,6 +492,7 @@ internal class CraftNativeCapabilities(
|
|
|
492
492
|
|
|
493
493
|
private fun pickVideo(requestToken: String, args: JSONArray, completion: (CraftNativeCapabilityResult) -> Unit) {
|
|
494
494
|
mainHandler.post {
|
|
495
|
+
if (fileRequestCancelled(requestToken)) return@post
|
|
495
496
|
val host = activity ?: return@post complete(requestToken, failure("NEEDS_ACTIVITY", "No screen is showing to pick from"), completion)
|
|
496
497
|
if (pendingFilePick != null) {
|
|
497
498
|
complete(requestToken, failure("BUSY", "Another file picker is already open"), completion)
|
|
@@ -518,7 +519,7 @@ internal class CraftNativeCapabilities(
|
|
|
518
519
|
private fun copyVideo(requestToken: String, uri: Uri, limit: Long): CraftNativeCapabilityResult {
|
|
519
520
|
var target: File? = null
|
|
520
521
|
return try {
|
|
521
|
-
if (
|
|
522
|
+
if (fileRequestCancelled(requestToken)) throw IllegalStateException("CANCELLED: Video selection was cancelled")
|
|
522
523
|
val name = context.contentResolver.query(uri, arrayOf(android.provider.OpenableColumns.DISPLAY_NAME), null, null, null)?.use {
|
|
523
524
|
if (it.moveToFirst()) it.getString(0) else null
|
|
524
525
|
} ?: "video.mp4"
|
|
@@ -535,7 +536,7 @@ internal class CraftNativeCapabilities(
|
|
|
535
536
|
while (true) {
|
|
536
537
|
val count = source.read(buffer)
|
|
537
538
|
if (count < 0) break
|
|
538
|
-
if (
|
|
539
|
+
if (fileRequestCancelled(requestToken)) throw IllegalStateException("CANCELLED: Video selection was cancelled")
|
|
539
540
|
size += count
|
|
540
541
|
require(size <= limit) { "Choose a video under ${limit / 1024 / 1024}MB." }
|
|
541
542
|
output.write(buffer, 0, count)
|
|
@@ -558,6 +559,7 @@ internal class CraftNativeCapabilities(
|
|
|
558
559
|
completion: (CraftNativeCapabilityResult) -> Unit,
|
|
559
560
|
) {
|
|
560
561
|
mainHandler.post {
|
|
562
|
+
if (fileRequestCancelled(requestToken)) return@post
|
|
561
563
|
val host = activity ?: return@post complete(requestToken, failure("NEEDS_ACTIVITY", "No screen is showing to pick from"), completion)
|
|
562
564
|
if (pendingFilePick != null) {
|
|
563
565
|
complete(requestToken, failure("BUSY", "Another file picker is already open"), completion)
|
|
@@ -594,6 +596,7 @@ internal class CraftNativeCapabilities(
|
|
|
594
596
|
|
|
595
597
|
private fun pickFile(requestToken: String, args: JSONArray, completion: (CraftNativeCapabilityResult) -> Unit) {
|
|
596
598
|
mainHandler.post {
|
|
599
|
+
if (fileRequestCancelled(requestToken)) return@post
|
|
597
600
|
val host = activity ?: return@post complete(requestToken, failure("NEEDS_ACTIVITY", "No screen is showing to pick from"), completion)
|
|
598
601
|
if (pendingFilePick != null) {
|
|
599
602
|
complete(requestToken, failure("BUSY", "Another file picker is already open"), completion)
|
|
@@ -674,7 +677,7 @@ internal class CraftNativeCapabilities(
|
|
|
674
677
|
networkExecutor.execute {
|
|
675
678
|
val result = data?.data?.let { copyVideo(pending.requestToken, it, pending.videoLimit) }
|
|
676
679
|
?: failure("PICKER_FAILED", "The selected video could not be read")
|
|
677
|
-
|
|
680
|
+
completeFile(pending.requestToken, result, pending.completion)
|
|
678
681
|
}
|
|
679
682
|
return true
|
|
680
683
|
}
|
|
@@ -687,30 +690,28 @@ internal class CraftNativeCapabilities(
|
|
|
687
690
|
data?.data?.let { add(it) }
|
|
688
691
|
}.distinct().take(pending.selectionLimit)
|
|
689
692
|
val result = copyDocuments(pending.requestToken, uris, pending.fileLimit)
|
|
690
|
-
|
|
693
|
+
completeFile(pending.requestToken, result, pending.completion)
|
|
691
694
|
}
|
|
692
695
|
return true
|
|
693
696
|
}
|
|
694
697
|
networkExecutor.execute {
|
|
695
|
-
val
|
|
696
|
-
val
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
pending.completion,
|
|
707
|
-
)
|
|
698
|
+
val result = runCatching {
|
|
699
|
+
val descriptor = if (pending.source == "camera") {
|
|
700
|
+
val bitmap = data?.extras?.get("data") as? Bitmap
|
|
701
|
+
bitmap?.let { writeImage(it, pending.maxPixels, pending.quality, "photo.jpg", pending.requestToken) }
|
|
702
|
+
} else {
|
|
703
|
+
data?.data?.let { copyUri(pending.requestToken, it, pending.maxPixels, pending.quality) }
|
|
704
|
+
}
|
|
705
|
+
descriptor?.let(::success) ?: if (fileRequestCancelled(pending.requestToken)) failure("CANCELLED", "Image selection was cancelled")
|
|
706
|
+
else failure("PICKER_FAILED", "The selected image could not be read")
|
|
707
|
+
}.getOrElse { failure("READ_ERROR", it.message ?: "The selected image could not be read") }
|
|
708
|
+
completeFile(pending.requestToken, result, pending.completion)
|
|
708
709
|
}
|
|
709
710
|
return true
|
|
710
711
|
}
|
|
711
712
|
|
|
712
713
|
private fun copyUri(requestToken: String, uri: Uri, maxPixels: Int, quality: Float): JSONObject? {
|
|
713
|
-
if (
|
|
714
|
+
if (fileRequestCancelled(requestToken)) return null
|
|
714
715
|
val name = context.contentResolver.query(uri, arrayOf(android.provider.OpenableColumns.DISPLAY_NAME), null, null, null)?.use {
|
|
715
716
|
if (it.moveToFirst()) it.getString(0) else null
|
|
716
717
|
} ?: "photo"
|
|
@@ -729,7 +730,7 @@ internal class CraftNativeCapabilities(
|
|
|
729
730
|
val copied = JSONArray()
|
|
730
731
|
return try {
|
|
731
732
|
uris.forEach { copied.put(copyDocument(requestToken, it, limit)) }
|
|
732
|
-
if (
|
|
733
|
+
if (fileRequestCancelled(requestToken)) throw IllegalStateException("CANCELLED: File selection was cancelled")
|
|
733
734
|
success(copied)
|
|
734
735
|
} catch (error: Exception) {
|
|
735
736
|
for (index in 0 until copied.length()) {
|
|
@@ -745,6 +746,7 @@ internal class CraftNativeCapabilities(
|
|
|
745
746
|
private fun copyDocument(requestToken: String, uri: Uri, limit: Long): JSONObject {
|
|
746
747
|
var target: File? = null
|
|
747
748
|
return try {
|
|
749
|
+
if (fileRequestCancelled(requestToken)) throw IllegalStateException("CANCELLED: File selection was cancelled")
|
|
748
750
|
val name = context.contentResolver.query(uri, arrayOf(android.provider.OpenableColumns.DISPLAY_NAME), null, null, null)?.use {
|
|
749
751
|
if (it.moveToFirst()) it.getString(0) else null
|
|
750
752
|
} ?: "file"
|
|
@@ -759,7 +761,7 @@ internal class CraftNativeCapabilities(
|
|
|
759
761
|
while (true) {
|
|
760
762
|
val count = source.read(buffer)
|
|
761
763
|
if (count < 0) break
|
|
762
|
-
if (
|
|
764
|
+
if (fileRequestCancelled(requestToken)) throw IllegalStateException("CANCELLED: File selection was cancelled")
|
|
763
765
|
size += count
|
|
764
766
|
require(size <= limit) { "FILE_TOO_LARGE: Choose a file under ${limit / 1024 / 1024}MB." }
|
|
765
767
|
output.write(buffer, 0, count)
|
|
@@ -785,7 +787,7 @@ internal class CraftNativeCapabilities(
|
|
|
785
787
|
}
|
|
786
788
|
|
|
787
789
|
private fun writeImage(bitmap: Bitmap, maxPixels: Int, quality: Float, name: String, requestToken: String? = null): JSONObject? {
|
|
788
|
-
if (requestToken != null &&
|
|
790
|
+
if (requestToken != null && fileRequestCancelled(requestToken)) {
|
|
789
791
|
bitmap.recycle()
|
|
790
792
|
return null
|
|
791
793
|
}
|
|
@@ -809,7 +811,7 @@ internal class CraftNativeCapabilities(
|
|
|
809
811
|
file.parentFile?.deleteRecursively()
|
|
810
812
|
return null
|
|
811
813
|
}
|
|
812
|
-
if (requestToken != null &&
|
|
814
|
+
if (requestToken != null && fileRequestCancelled(requestToken)) {
|
|
813
815
|
file.parentFile?.deleteRecursively()
|
|
814
816
|
return null
|
|
815
817
|
}
|
|
@@ -819,7 +821,7 @@ internal class CraftNativeCapabilities(
|
|
|
819
821
|
private fun store(bytes: ByteArray, name: String, type: String): CraftNativeCapabilityResult {
|
|
820
822
|
val file = File(fileRoot, UUID.randomUUID().toString()).apply { mkdirs() }.resolve(safeFileName(name))
|
|
821
823
|
return runCatching { file.writeBytes(bytes); success(descriptor(file, type)) }
|
|
822
|
-
.getOrElse { failure("WRITE_ERROR", it.message ?: "File could not be written") }
|
|
824
|
+
.getOrElse { file.parentFile?.deleteRecursively(); failure("WRITE_ERROR", it.message ?: "File could not be written") }
|
|
823
825
|
}
|
|
824
826
|
|
|
825
827
|
private fun descriptor(file: File, type: String): JSONObject = JSONObject()
|
|
@@ -860,16 +862,36 @@ internal class CraftNativeCapabilities(
|
|
|
860
862
|
private fun headerValue(value: String): String = value.replace("\r", "").replace("\n", "")
|
|
861
863
|
private fun validURL(raw: String): URL? = runCatching { URL(raw) }.getOrNull()?.takeIf { it.protocol.lowercase() in setOf("http", "https") && it.host.isNotBlank() }
|
|
862
864
|
|
|
865
|
+
private fun fileRequestCancelled(requestToken: String): Boolean =
|
|
866
|
+
closed.get() || cancelled.contains(requestToken) || Thread.currentThread().isInterrupted
|
|
867
|
+
|
|
868
|
+
private fun discardFiles(result: CraftNativeCapabilityResult) {
|
|
869
|
+
if (result.code != null) return
|
|
870
|
+
val data = result.data
|
|
871
|
+
val files = if (data is JSONArray) (0 until data.length()).mapNotNull { data.optJSONObject(it) }
|
|
872
|
+
else listOfNotNull(data as? JSONObject)
|
|
873
|
+
files.forEach { file ->
|
|
874
|
+
ownedFile(file.optString("path"))?.parentFile?.deleteRecursively()
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
private fun completeFile(requestToken: String, result: CraftNativeCapabilityResult, completion: (CraftNativeCapabilityResult) -> Unit) {
|
|
879
|
+
complete(requestToken, result, completion, discard = { discardFiles(result) })
|
|
880
|
+
}
|
|
881
|
+
|
|
863
882
|
private fun complete(
|
|
864
883
|
requestToken: String,
|
|
865
884
|
result: CraftNativeCapabilityResult,
|
|
866
885
|
completion: (CraftNativeCapabilityResult) -> Unit,
|
|
886
|
+
discard: (() -> Unit)? = null,
|
|
867
887
|
) {
|
|
868
|
-
|
|
869
|
-
if (
|
|
870
|
-
|
|
871
|
-
if (!cancelled.remove(requestToken) && !closed.get()) completion(result)
|
|
888
|
+
val deliver: () -> Unit = {
|
|
889
|
+
if (cancelled.remove(requestToken) || closed.get()) discard?.invoke()
|
|
890
|
+
else completion(result)
|
|
872
891
|
}
|
|
892
|
+
if (android.os.Looper.myLooper() == android.os.Looper.getMainLooper()) {
|
|
893
|
+
deliver()
|
|
894
|
+
} else mainHandler.post { deliver() }
|
|
873
895
|
}
|
|
874
896
|
|
|
875
897
|
private fun storage(method: String, args: JSONArray): CraftNativeCapabilityResult = synchronized(CraftNativeStorage.lock) {
|
|
@@ -20,7 +20,10 @@ import kotlin.math.min
|
|
|
20
20
|
* invalidates the previous load. The player is released on every terminal
|
|
21
21
|
* path, including cancellation, decode failure and screen teardown.
|
|
22
22
|
*/
|
|
23
|
-
internal class CraftNativeSounds(
|
|
23
|
+
internal class CraftNativeSounds(
|
|
24
|
+
private val context: Context,
|
|
25
|
+
private val requestFocus: (AudioManager, AudioFocusRequest) -> Int = { manager, request -> manager.requestAudioFocus(request) },
|
|
26
|
+
) {
|
|
24
27
|
private data class Sound(
|
|
25
28
|
val player: MediaPlayer,
|
|
26
29
|
var volume: Float,
|
|
@@ -33,6 +36,8 @@ internal class CraftNativeSounds(private val context: Context) {
|
|
|
33
36
|
|
|
34
37
|
private val main = Handler(Looper.getMainLooper())
|
|
35
38
|
private val sounds = mutableMapOf<String, Sound>()
|
|
39
|
+
private var focusSuspended = false
|
|
40
|
+
private var ducked = false
|
|
36
41
|
private val audioManager by lazy { context.getSystemService(Context.AUDIO_SERVICE) as AudioManager }
|
|
37
42
|
private val focusRequest by lazy {
|
|
38
43
|
AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
|
|
@@ -74,12 +79,13 @@ internal class CraftNativeSounds(private val context: Context) {
|
|
|
74
79
|
emit?.invoke(JSONObject().put("id", id).put("event", "error").put("message", message))
|
|
75
80
|
}
|
|
76
81
|
|
|
77
|
-
if (audioManager
|
|
82
|
+
if (requestFocus(audioManager, focusRequest) != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
|
|
78
83
|
settle(failure("AUDIO_FOCUS_DENIED", "Audio focus was denied by the operating system"))
|
|
79
84
|
remove(id)
|
|
80
85
|
emit?.invoke(JSONObject().put("id", id).put("event", "error").put("message", "Audio focus was denied by the operating system"))
|
|
81
86
|
return
|
|
82
87
|
}
|
|
88
|
+
onAudioFocusChange(AudioManager.AUDIOFOCUS_GAIN)
|
|
83
89
|
|
|
84
90
|
runCatching {
|
|
85
91
|
player.setAudioAttributes(AudioAttributes.Builder()
|
|
@@ -87,7 +93,7 @@ internal class CraftNativeSounds(private val context: Context) {
|
|
|
87
93
|
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
|
|
88
94
|
.build())
|
|
89
95
|
player.isLooping = options.optBoolean("loop", false)
|
|
90
|
-
|
|
96
|
+
applyVolume(sound)
|
|
91
97
|
player.setOnPreparedListener {
|
|
92
98
|
if (sounds[id] !== sound) return@setOnPreparedListener
|
|
93
99
|
sound.prepared = true
|
|
@@ -95,7 +101,7 @@ internal class CraftNativeSounds(private val context: Context) {
|
|
|
95
101
|
settle(CraftNativeCapabilityResult(data = true))
|
|
96
102
|
if (fadeIn > 0) {
|
|
97
103
|
if (!sound.paused) ramp(sound, volume, fadeIn / 1000.0)
|
|
98
|
-
else { sound.volume = volume;
|
|
104
|
+
else { sound.volume = volume; applyVolume(sound) }
|
|
99
105
|
}
|
|
100
106
|
}
|
|
101
107
|
player.setOnCompletionListener {
|
|
@@ -136,6 +142,7 @@ internal class CraftNativeSounds(private val context: Context) {
|
|
|
136
142
|
|
|
137
143
|
fun resume(id: String): Boolean {
|
|
138
144
|
val sound = sounds[id] ?: return false
|
|
145
|
+
if (focusSuspended) return false
|
|
139
146
|
if (sound.prepared) runCatching { sound.player.start() }.onFailure { return false }
|
|
140
147
|
sound.paused = false
|
|
141
148
|
sound.interrupted = false
|
|
@@ -151,7 +158,7 @@ internal class CraftNativeSounds(private val context: Context) {
|
|
|
151
158
|
if (fade > 0 && !sound.paused) ramp(sound, target, fade / 1000.0)
|
|
152
159
|
else {
|
|
153
160
|
sound.volume = target
|
|
154
|
-
sound
|
|
161
|
+
applyVolume(sound)
|
|
155
162
|
}
|
|
156
163
|
return true
|
|
157
164
|
}
|
|
@@ -195,7 +202,11 @@ internal class CraftNativeSounds(private val context: Context) {
|
|
|
195
202
|
sound.player.stop()
|
|
196
203
|
}
|
|
197
204
|
sound.player.release()
|
|
198
|
-
if (sounds.isEmpty())
|
|
205
|
+
if (sounds.isEmpty()) {
|
|
206
|
+
audioManager.abandonAudioFocusRequest(focusRequest)
|
|
207
|
+
focusSuspended = false
|
|
208
|
+
ducked = false
|
|
209
|
+
}
|
|
199
210
|
val answer = sound.pending
|
|
200
211
|
sound.pending = null
|
|
201
212
|
answer?.invoke(failure("CANCELLED", "Audio playback was stopped or replaced"))
|
|
@@ -211,21 +222,28 @@ internal class CraftNativeSounds(private val context: Context) {
|
|
|
211
222
|
}
|
|
212
223
|
}
|
|
213
224
|
}
|
|
214
|
-
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT ->
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (sound.
|
|
225
|
+
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
|
|
226
|
+
focusSuspended = true
|
|
227
|
+
sounds.values.toList().forEach { sound ->
|
|
228
|
+
if (!sound.paused) {
|
|
229
|
+
sound.interrupted = true
|
|
230
|
+
if (sound.prepared) runCatching { sound.player.pause() }
|
|
231
|
+
}
|
|
218
232
|
}
|
|
219
233
|
}
|
|
220
|
-
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK ->
|
|
221
|
-
|
|
234
|
+
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
|
|
235
|
+
ducked = true
|
|
236
|
+
sounds.values.toList().forEach { sound -> runCatching { applyVolume(sound) } }
|
|
222
237
|
}
|
|
223
|
-
AudioManager.AUDIOFOCUS_GAIN ->
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
238
|
+
AudioManager.AUDIOFOCUS_GAIN -> {
|
|
239
|
+
focusSuspended = false
|
|
240
|
+
ducked = false
|
|
241
|
+
sounds.values.toList().forEach { sound ->
|
|
242
|
+
runCatching { applyVolume(sound) }
|
|
243
|
+
if (sound.interrupted && !sound.paused) {
|
|
244
|
+
sound.interrupted = false
|
|
245
|
+
if (sound.prepared) runCatching { sound.player.start() }
|
|
246
|
+
}
|
|
229
247
|
}
|
|
230
248
|
}
|
|
231
249
|
}
|
|
@@ -241,8 +259,8 @@ internal class CraftNativeSounds(private val context: Context) {
|
|
|
241
259
|
if (!sounds.containsValue(sound)) return
|
|
242
260
|
val progress = min(1.0, (android.os.SystemClock.uptimeMillis() - began).toDouble() / duration).toFloat()
|
|
243
261
|
val value = start + (target - start) * progress
|
|
244
|
-
sound.player.setVolume(value, value)
|
|
245
262
|
sound.volume = value
|
|
263
|
+
applyVolume(sound)
|
|
246
264
|
if (progress >= 1f) {
|
|
247
265
|
sound.fade = null
|
|
248
266
|
done?.invoke()
|
|
@@ -256,5 +274,12 @@ internal class CraftNativeSounds(private val context: Context) {
|
|
|
256
274
|
main.post(tick)
|
|
257
275
|
}
|
|
258
276
|
|
|
277
|
+
private fun outputVolume(sound: Sound): Float = sound.volume * if (ducked) 0.2f else 1f
|
|
278
|
+
|
|
279
|
+
private fun applyVolume(sound: Sound) {
|
|
280
|
+
val value = outputVolume(sound)
|
|
281
|
+
sound.player.setVolume(value, value)
|
|
282
|
+
}
|
|
283
|
+
|
|
259
284
|
private fun failure(code: String, message: String) = CraftNativeCapabilityResult(code = code, message = message)
|
|
260
285
|
}
|
package/dist/api/mobile.d.ts
CHANGED
|
@@ -1774,7 +1774,7 @@ export interface HealthLiveWorkoutEvent {
|
|
|
1774
1774
|
message?: string;
|
|
1775
1775
|
}
|
|
1776
1776
|
/** The health reads and writes a host may or may not have, depending on the app build it runs in. */
|
|
1777
|
-
export type HealthReadMethod = 'getWorkouts' | 'getDailyStatistics' | 'getHeartRateSamples' | 'getSamples' | 'getSleepIntervals' | 'saveData' | 'saveMindfulSession' | 'getMindfulSessions' | 'startHeartRateUpdates' | 'stopHeartRateUpdates' | 'startWorkout' | 'pauseWorkout' | 'resumeWorkout' | 'endWorkout';
|
|
1777
|
+
export type HealthReadMethod = 'getWorkouts' | 'getDailyStatistics' | 'getHeartRateSamples' | 'getSamples' | 'getSleepIntervals' | 'saveData' | 'saveMindfulSession' | 'getMindfulSessions' | 'startHeartRateUpdates' | 'stopHeartRateUpdates' | 'startWorkout' | 'pauseWorkout' | 'resumeWorkout' | 'endWorkout' | 'discardWorkout';
|
|
1778
1778
|
export declare const health: {
|
|
1779
1779
|
/**
|
|
1780
1780
|
* Whether this app build answers `method`. An app installed before a read
|
|
@@ -1834,6 +1834,8 @@ export declare const health: {
|
|
|
1834
1834
|
resumeWorkout(): Promise<boolean>;
|
|
1835
1835
|
/** Ends and saves the current live workout. */
|
|
1836
1836
|
endWorkout(): Promise<HealthLiveWorkoutResult>;
|
|
1837
|
+
/** iOS: ends the live session without saving a workout. Does not delete existing health samples. */
|
|
1838
|
+
discardWorkout(): Promise<boolean>;
|
|
1837
1839
|
/** Receives OS workout state transitions; returns the unsubscribe. */
|
|
1838
1840
|
onWorkout(callback: (event: HealthLiveWorkoutEvent) => void): () => void;
|
|
1839
1841
|
/**
|
package/dist/cli.js
CHANGED
|
@@ -3047,7 +3047,7 @@ function pinCraftNativeDependency(packagePath, craftVersion) {
|
|
|
3047
3047
|
`);
|
|
3048
3048
|
}
|
|
3049
3049
|
// package.json
|
|
3050
|
-
var version = "0.0.
|
|
3050
|
+
var version = "0.0.375";
|
|
3051
3051
|
|
|
3052
3052
|
// bin/cli.ts
|
|
3053
3053
|
var spawnedFrom = process6.env[CRAFT_CLI_SPAWN_MARKER];
|
|
@@ -790,6 +790,8 @@ export interface SwitchProps extends BaseProps {
|
|
|
790
790
|
};
|
|
791
791
|
thumbColor?: string;
|
|
792
792
|
ios_backgroundColor?: string;
|
|
793
|
+
/** iOS-only solid UIControl switch: opaque thumb and immediate transitions. Defaults to native UISwitch. */
|
|
794
|
+
ios_solid?: boolean;
|
|
793
795
|
}
|
|
794
796
|
export interface CheckboxProps extends BaseProps {
|
|
795
797
|
style?: StyleValue<ViewStyle>;
|
package/dist/index.cjs
CHANGED
package/dist/index.js
CHANGED
package/dist/ios/src/index.d.ts
CHANGED
|
@@ -408,6 +408,8 @@ export interface InitOptions {
|
|
|
408
408
|
* monorepo dev loop uses.
|
|
409
409
|
*/
|
|
410
410
|
runtimeDir?: string | null;
|
|
411
|
+
/** Explicit Craft Zig source package for a per-app runtime build (or CRAFT_IOS_RUNTIME_SOURCE). */
|
|
412
|
+
runtimeSource?: string | null;
|
|
411
413
|
}
|
|
412
414
|
export interface BuildOptions {
|
|
413
415
|
htmlPath?: string;
|
|
@@ -422,6 +424,8 @@ export interface BuildOptions {
|
|
|
422
424
|
* leaves whatever `init` installed exactly as it is.
|
|
423
425
|
*/
|
|
424
426
|
runtimeDir?: string | null;
|
|
427
|
+
/** Rebuild the installed runtime from this source using the app's current feature flags. */
|
|
428
|
+
runtimeSource?: string | null;
|
|
425
429
|
}
|
|
426
430
|
/**
|
|
427
431
|
* Run the app's declared generator hook without invoking a shell.
|
|
@@ -498,7 +502,7 @@ export declare function renderRuntimeSettings(): string;
|
|
|
498
502
|
* behind the `CRAFT_SCHEDULED_WORKOUTS` condition, so an app that does not
|
|
499
503
|
* schedule workouts does not link it. `$(inherited)` keeps Debug's `DEBUG`.
|
|
500
504
|
*/
|
|
501
|
-
export declare function renderFeatureSettings(config:
|
|
505
|
+
export declare function renderFeatureSettings(config: Partial<CraftConfig>): string;
|
|
502
506
|
export declare function renderPrivacyManifest(config: CraftConfig): string;
|
|
503
507
|
/**
|
|
504
508
|
* Whether a PNG carries an alpha channel, from its header: colour type 4
|
|
@@ -563,7 +567,7 @@ export declare function resolveRuntimeDir(override?: string | null): string | nu
|
|
|
563
567
|
*
|
|
564
568
|
* Returns true when a runtime was installed.
|
|
565
569
|
*/
|
|
566
|
-
export declare function installRuntime(output: string, runtimeDir: string): Promise<boolean>;
|
|
570
|
+
export declare function installRuntime(output: string, runtimeDir: string, config?: CraftConfig): Promise<boolean>;
|
|
567
571
|
/**
|
|
568
572
|
* The identifier an app's display name becomes wherever Xcode or Swift needs
|
|
569
573
|
* one: the project, the target, the product and the `@main` type.
|