cuoral-ionic 0.0.7 → 0.0.8

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 (42) hide show
  1. package/android/build/.transforms/bb54161301273cf9b5b94a21c0fb3f23/transformed/classes/classes_dex/classes.dex +0 -0
  2. package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$4.dex +0 -0
  3. package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$5.dex +0 -0
  4. package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$6.dex +0 -0
  5. package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin.dex +0 -0
  6. package/android/build/intermediates/compile_library_classes_jar/debug/classes.jar +0 -0
  7. package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$4.class +0 -0
  8. package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$5.class +0 -0
  9. package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$6.class +0 -0
  10. package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin.class +0 -0
  11. package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$4.class +0 -0
  12. package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$5.class +0 -0
  13. package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$6.class +0 -0
  14. package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin.class +0 -0
  15. package/android/build/intermediates/runtime_library_classes_jar/debug/classes.jar +0 -0
  16. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/{CuoralPlugin$1.class.uniqueId1 → CuoralPlugin$1.class.uniqueId2} +0 -0
  17. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/{CuoralPlugin$2.class.uniqueId2 → CuoralPlugin$2.class.uniqueId5} +0 -0
  18. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/CuoralPlugin$3.class.uniqueId3 +0 -0
  19. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/CuoralPlugin$4.class.uniqueId6 +0 -0
  20. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/CuoralPlugin$5.class.uniqueId4 +0 -0
  21. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/CuoralPlugin$6.class.uniqueId8 +0 -0
  22. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/CuoralPlugin$InitiateCallback.class.uniqueId0 +0 -0
  23. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/CuoralPlugin$UploadCallback.class.uniqueId7 +0 -0
  24. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/CuoralPlugin.class.uniqueId1 +0 -0
  25. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  26. package/android/src/main/java/com/cuoral/ionic/CuoralPlugin.java +38 -35
  27. package/dist/bridge.d.ts.map +1 -1
  28. package/dist/cuoral.d.ts.map +1 -1
  29. package/dist/cuoral.js +31 -6
  30. package/dist/index.esm.js +75 -43
  31. package/dist/index.esm.js.map +1 -1
  32. package/dist/index.js +75 -43
  33. package/dist/index.js.map +1 -1
  34. package/dist/plugin.d.ts +2 -2
  35. package/dist/plugin.d.ts.map +1 -1
  36. package/dist/plugin.js +43 -39
  37. package/ios/Plugin/CuoralPlugin.swift +3 -3
  38. package/package.json +1 -1
  39. package/src/bridge.ts +1 -0
  40. package/src/cuoral.ts +33 -6
  41. package/src/plugin.ts +43 -38
  42. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/CuoralPlugin.class.uniqueId0 +0 -0
@@ -409,7 +409,7 @@ public class CuoralPlugin extends Plugin {
409
409
  if (autoUpload && !sessionId.isEmpty() && !publicKey.isEmpty()) {
410
410
  final String filePath = videoFilePath;
411
411
  final long finalDuration = duration;
412
-
412
+
413
413
  uploadRecording(filePath, sessionId, publicKey, customerId, (int) finalDuration, new UploadCallback() {
414
414
  @Override
415
415
  public void onSuccess() {
@@ -551,7 +551,9 @@ public class CuoralPlugin extends Plugin {
551
551
  mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
552
552
  mediaRecorder.setOutputFile(videoFilePath);
553
553
 
554
- int bitRate = (int) (6000000 * quality); // Base bitrate 6Mbps
554
+ // Optimized bitrate for screen recordings - 1.5 Mbps = ~11MB per minute
555
+ // 5 minutes = ~54MB (good quality, small size)
556
+ int bitRate = 1500000; // 1.5 Mbps (matches iOS settings)
555
557
  mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
556
558
  mediaRecorder.setVideoSize(metrics.widthPixels, metrics.heightPixels);
557
559
  mediaRecorder.setVideoFrameRate(30);
@@ -596,14 +598,15 @@ public class CuoralPlugin extends Plugin {
596
598
  // Upload callback interface
597
599
  private interface UploadCallback {
598
600
  void onSuccess();
601
+
599
602
  void onFailure(String error);
600
603
  }
601
604
 
602
605
  /**
603
606
  * Upload recording to Cuoral backend
604
607
  */
605
- private void uploadRecording(String filePath, String sessionId, String publicKey,
606
- String customerId, int duration, UploadCallback callback) {
608
+ private void uploadRecording(String filePath, String sessionId, String publicKey,
609
+ String customerId, int duration, UploadCallback callback) {
607
610
  File videoFile = new File(filePath);
608
611
  if (!videoFile.exists()) {
609
612
  callback.onFailure("File not found");
@@ -627,14 +630,15 @@ public class CuoralPlugin extends Plugin {
627
630
 
628
631
  private interface InitiateCallback {
629
632
  void onSuccess(String recordId);
633
+
630
634
  void onFailure(String error);
631
635
  }
632
636
 
633
637
  /**
634
638
  * Step 1: Initiate recording session
635
639
  */
636
- private void initiateRecording(String sessionId, String customerId, String publicKey,
637
- InitiateCallback callback) {
640
+ private void initiateRecording(String sessionId, String customerId, String publicKey,
641
+ InitiateCallback callback) {
638
642
  OkHttpClient client = new OkHttpClient();
639
643
 
640
644
  try {
@@ -643,17 +647,16 @@ public class CuoralPlugin extends Plugin {
643
647
  json.put("customer_id", customerId);
644
648
 
645
649
  RequestBody body = RequestBody.create(
646
- json.toString(),
647
- MediaType.parse("application/json")
648
- );
650
+ json.toString(),
651
+ MediaType.parse("application/json"));
649
652
 
650
653
  Request request = new Request.Builder()
651
- .url("https://api.cuoral.com/customer-intelligence/initiate/record")
652
- .post(body)
653
- .addHeader("Content-Type", "application/json")
654
- .addHeader("Accept", "*/*")
655
- .addHeader("x-org-id", publicKey)
656
- .build();
654
+ .url("https://api.cuoral.com/customer-intelligence/initiate/record")
655
+ .post(body)
656
+ .addHeader("Content-Type", "application/json")
657
+ .addHeader("Accept", "*/*")
658
+ .addHeader("x-org-id", publicKey)
659
+ .build();
657
660
 
658
661
  client.newCall(request).enqueue(new Callback() {
659
662
  @Override
@@ -672,7 +675,7 @@ public class CuoralPlugin extends Plugin {
672
675
  String responseBody = response.body().string();
673
676
  JSONObject json = new JSONObject(responseBody);
674
677
  String status = json.getString("status");
675
-
678
+
676
679
  if ("success".equals(status)) {
677
680
  String recordId = json.getString("record_id");
678
681
  callback.onSuccess(recordId);
@@ -692,29 +695,29 @@ public class CuoralPlugin extends Plugin {
692
695
  /**
693
696
  * Step 2: Upload video to the initiated recording
694
697
  */
695
- private void uploadVideo(File videoFile, String recordId, String publicKey,
696
- String customerId, int duration, UploadCallback callback) {
698
+ private void uploadVideo(File videoFile, String recordId, String publicKey,
699
+ String customerId, int duration, UploadCallback callback) {
697
700
  OkHttpClient client = new OkHttpClient.Builder()
698
- .connectTimeout(60, java.util.concurrent.TimeUnit.SECONDS)
699
- .writeTimeout(60, java.util.concurrent.TimeUnit.SECONDS)
700
- .readTimeout(60, java.util.concurrent.TimeUnit.SECONDS)
701
- .build();
701
+ .connectTimeout(60, java.util.concurrent.TimeUnit.SECONDS)
702
+ .writeTimeout(60, java.util.concurrent.TimeUnit.SECONDS)
703
+ .readTimeout(60, java.util.concurrent.TimeUnit.SECONDS)
704
+ .build();
702
705
 
703
706
  RequestBody requestBody = new MultipartBody.Builder()
704
- .setType(MultipartBody.FORM)
705
- .addFormDataPart("record_id", recordId)
706
- .addFormDataPart("console_error", "[]")
707
- .addFormDataPart("api_response_log", "[]")
708
- .addFormDataPart("page_view", "[]")
709
- .addFormDataPart("record_media", "recording.mp4",
710
- RequestBody.create(videoFile, MediaType.parse("video/mp4")))
711
- .build();
707
+ .setType(MultipartBody.FORM)
708
+ .addFormDataPart("record_id", recordId)
709
+ .addFormDataPart("console_error", "[]")
710
+ .addFormDataPart("api_response_log", "[]")
711
+ .addFormDataPart("page_view", "[]")
712
+ .addFormDataPart("record_media", "recording.mp4",
713
+ RequestBody.create(videoFile, MediaType.parse("video/mp4")))
714
+ .build();
712
715
 
713
716
  Request request = new Request.Builder()
714
- .url("https://api.cuoral.com/customer-intelligence/update/record")
715
- .post(requestBody)
716
- .addHeader("Accept", "*/*")
717
- .build();
717
+ .url("https://api.cuoral.com/customer-intelligence/update/record")
718
+ .post(requestBody)
719
+ .addHeader("Accept", "*/*")
720
+ .build();
718
721
 
719
722
  client.newCall(request).enqueue(new Callback() {
720
723
  @Override
@@ -733,7 +736,7 @@ public class CuoralPlugin extends Plugin {
733
736
  String responseBody = response.body().string();
734
737
  JSONObject json = new JSONObject(responseBody);
735
738
  String status = json.getString("status");
736
-
739
+
737
740
  if ("success".equals(status)) {
738
741
  callback.onSuccess();
739
742
  } else {
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEzE;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,eAAe,CAAoE;IAC3F,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,YAAY,CAAkC;gBAE1C,MAAM,EAAE,YAAY;IAKhC;;OAEG;IACI,UAAU,IAAI,IAAI;IAgBzB;;OAEG;IACI,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAoBjD;;OAEG;IACI,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAyCjD;;OAEG;IACI,EAAE,CAAC,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,GAAG,MAAM,IAAI;IAmB/E;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAqC5B;;OAEG;IACH,OAAO,CAAC,GAAG;IAMX;;OAEG;IACI,OAAO,IAAI,IAAI;CAKvB;AAGD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,MAAM,CAAC,EAAE;YACP,eAAe,CAAC,EAAE;gBAChB,MAAM,CAAC,EAAE;oBACP,WAAW,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC;iBACrC,CAAC;aACH,CAAC;SACH,CAAC;KACH;CACF"}
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEzE;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,eAAe,CAAoE;IAC3F,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,YAAY,CAAkC;gBAE1C,MAAM,EAAE,YAAY;IAKhC;;OAEG;IACI,UAAU,IAAI,IAAI;IAgBzB;;OAEG;IACI,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAoBjD;;OAEG;IACI,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAyCjD;;OAEG;IACI,EAAE,CAAC,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,GAAG,MAAM,IAAI;IAmB/E;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAsC5B;;OAEG;IACH,OAAO,CAAC,GAAG;IAMX;;OAEG;IACI,OAAO,IAAI,IAAI;CAKvB;AAGD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,MAAM,CAAC,EAAE;YACP,eAAe,CAAC,EAAE;gBAChB,MAAM,CAAC,EAAE;oBACP,WAAW,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC;iBACrC,CAAC;aACH,CAAC;SACH,CAAC;KACH;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"cuoral.d.ts","sourceRoot":"","sources":["../src/cuoral.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAeD;;GAEG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,KAAK,CAAC,CAAc;IAC5B,OAAO,CAAC,YAAY,CAAC,CAAqB;IAC1C,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAuC;IACpF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAwB;gBAElD,OAAO,EAAE,aAAa;IAiDlC;;OAEG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBxC;;OAEG;YACW,sBAAsB;IA2CpC;;OAEG;YACW,yBAAyB;IAoCvC;;OAEG;IACI,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,GAAG,IAAI;IAM1D;;OAEG;IACI,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,GAAG,IAAI;IAM7E;;;;;OAKG;IACU,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA0C7E;;;OAGG;IACU,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAS/C;;;;OAIG;IACU,aAAa,IAAI,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAC,GAAG,IAAI,CAAC;IAiBxG;;OAEG;IACI,SAAS,IAAI,IAAI;IASxB;;OAEG;IACI,UAAU,IAAI,IAAI;IAMzB;;OAEG;IACI,WAAW,IAAI,OAAO;IAI7B;;OAEG;IACI,YAAY,IAAI,MAAM;IAuB7B;;OAEG;IACU,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IA6C1C;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAsB5B;;OAEG;IACI,OAAO,IAAI,IAAI;IActB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAa5B;;OAEG;YACW,eAAe;IAkC7B;;OAEG;YACW,UAAU;IAmBxB;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAyF7B"}
1
+ {"version":3,"file":"cuoral.d.ts","sourceRoot":"","sources":["../src/cuoral.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAeD;;GAEG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,KAAK,CAAC,CAAc;IAC5B,OAAO,CAAC,YAAY,CAAC,CAAqB;IAC1C,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAuC;IACpF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAwB;gBAElD,OAAO,EAAE,aAAa;IA4DlC;;OAEG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBxC;;OAEG;YACW,sBAAsB;IA2CpC;;OAEG;YACW,yBAAyB;IAoCvC;;OAEG;IACI,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,GAAG,IAAI;IAM1D;;OAEG;IACI,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,GAAG,IAAI;IAM7E;;;;;OAKG;IACU,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA0C7E;;;OAGG;IACU,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAS/C;;;;OAIG;IACU,aAAa,IAAI,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAC,GAAG,IAAI,CAAC;IAiBxG;;OAEG;IACI,SAAS,IAAI,IAAI;IASxB;;OAEG;IACI,UAAU,IAAI,IAAI;IAMzB;;OAEG;IACI,WAAW,IAAI,OAAO;IAI7B;;OAEG;IACI,YAAY,IAAI,MAAM;IAuB7B;;OAEG;IACU,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IA6C1C;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAsB5B;;OAEG;IACI,OAAO,IAAI,IAAI;IActB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAa5B;;OAEG;YACW,eAAe;IAkC7B;;OAEG;YACW,UAAU;IAmBxB;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAyG7B"}
package/dist/cuoral.js CHANGED
@@ -9,6 +9,7 @@ import { Capacitor } from '@capacitor/core';
9
9
  */
10
10
  export class Cuoral {
11
11
  constructor(options) {
12
+ console.log('[Cuoral] Constructor called with options:', options);
12
13
  // Check if running on a mobile platform
13
14
  if (!Capacitor.isNativePlatform()) {
14
15
  throw new Error('Cuoral Ionic library only works on native mobile platforms (iOS/Android). Web is not supported.');
@@ -18,6 +19,7 @@ export class Cuoral {
18
19
  useModal: true,
19
20
  ...options
20
21
  };
22
+ console.log('[Cuoral] Merged options:', this.options);
21
23
  // Determine widget base URL
22
24
  const baseUrl = options.widgetBaseUrl || Cuoral.PRODUCTION_WIDGET_URL;
23
25
  const params = new URLSearchParams({
@@ -47,8 +49,14 @@ export class Cuoral {
47
49
  debug: options.debug || false
48
50
  });
49
51
  this.recorder = new CuoralRecorder();
52
+ // Expose bridge on window for widget detection and debugging
53
+ window.CuoralCapacitorBridge = true;
54
+ window.__cuoralBridge = this.bridge; // For debugging
55
+ window.__cuoralRecorder = this.recorder; // For debugging
56
+ console.log('[Cuoral] Bridge and recorder created, exposed on window');
50
57
  // Setup automatic message handlers
51
58
  this.setupMessageHandlers();
59
+ console.log('[Cuoral] Constructor complete');
52
60
  }
53
61
  /**
54
62
  * Initialize Cuoral
@@ -443,28 +451,45 @@ export class Cuoral {
443
451
  setupMessageHandlers() {
444
452
  // Handle start recording requests from widget
445
453
  this.bridge.on(CuoralMessageType.START_RECORDING, async () => {
446
- const success = await this.recorder.startRecording();
447
- if (!success) {
448
- // Error already handled by recorder
454
+ // Pass sendMessages: false to prevent duplicate messages
455
+ const success = await this.recorder.startRecording(undefined, false);
456
+ if (success) {
457
+ // Send RECORDING_STARTED back to widget
458
+ this.bridge.sendToWidget({
459
+ type: CuoralMessageType.RECORDING_STARTED,
460
+ payload: {
461
+ timestamp: Date.now()
462
+ }
463
+ });
464
+ }
465
+ else {
466
+ this.bridge.sendToWidget({
467
+ type: CuoralMessageType.RECORDING_ERROR,
468
+ payload: {
469
+ error: 'Failed to start recording'
470
+ }
471
+ });
449
472
  }
450
473
  });
451
474
  // Handle stop recording requests from widget
452
475
  this.bridge.on(CuoralMessageType.STOP_RECORDING, async () => {
453
476
  const sessionId = localStorage.getItem('__x_loadID');
454
477
  const customerId = localStorage.getItem('cuoralCustomerId');
478
+ // Pass sendMessages: false to prevent duplicate messages
455
479
  const result = await this.recorder.stopRecording({
456
480
  autoUpload: true,
457
481
  sessionId: sessionId || undefined,
458
482
  publicKey: this.options.publicKey,
459
483
  customerId: customerId || undefined,
460
- });
484
+ }, false);
461
485
  if (result && result.uploaded) {
462
- // Video was automatically uploaded, just notify widget
486
+ // Video was automatically uploaded, notify widget with RECORDING_STOPPED
463
487
  this.bridge.sendToWidget({
464
- type: CuoralMessageType.RECORDING_UPLOADED,
488
+ type: CuoralMessageType.RECORDING_STOPPED,
465
489
  payload: {
466
490
  duration: result.duration,
467
491
  uploaded: true,
492
+ uploadedToBackend: true,
468
493
  timestamp: Date.now()
469
494
  }
470
495
  });
package/dist/index.esm.js CHANGED
@@ -44,7 +44,7 @@ class CuoralRecorder {
44
44
  /**
45
45
  * Start recording with automatic permission handling
46
46
  */
47
- async startRecording(options) {
47
+ async startRecording(options, sendMessages = true) {
48
48
  try {
49
49
  // Check if already recording
50
50
  if (this.isRecording) {
@@ -66,20 +66,24 @@ class CuoralRecorder {
66
66
  if (result.success) {
67
67
  this.isRecording = true;
68
68
  this.recordingStartTime = Date.now();
69
- // Post message to widget
70
- this.postMessage({
71
- type: CuoralMessageType.RECORDING_STARTED,
72
- payload: { timestamp: this.recordingStartTime },
73
- });
69
+ // Post message to widget only if enabled (disabled when called from widget handler)
70
+ if (sendMessages) {
71
+ this.postMessage({
72
+ type: CuoralMessageType.RECORDING_STARTED,
73
+ payload: { timestamp: this.recordingStartTime },
74
+ });
75
+ }
74
76
  }
75
77
  else {
76
78
  // Recording failed - reset state and notify widget
77
79
  this.isRecording = false;
78
80
  this.recordingStartTime = undefined;
79
- this.postMessage({
80
- type: CuoralMessageType.RECORDING_ERROR,
81
- payload: { error: 'Failed to start recording' },
82
- });
81
+ if (sendMessages) {
82
+ this.postMessage({
83
+ type: CuoralMessageType.RECORDING_ERROR,
84
+ payload: { error: 'Failed to start recording' },
85
+ });
86
+ }
83
87
  }
84
88
  return result.success;
85
89
  }
@@ -87,24 +91,28 @@ class CuoralRecorder {
87
91
  // Exception occurred - reset state and notify widget
88
92
  this.isRecording = false;
89
93
  this.recordingStartTime = undefined;
90
- this.postMessage({
91
- type: CuoralMessageType.RECORDING_ERROR,
92
- payload: { error: error.message },
93
- });
94
+ if (sendMessages) {
95
+ this.postMessage({
96
+ type: CuoralMessageType.RECORDING_ERROR,
97
+ payload: { error: error.message },
98
+ });
99
+ }
94
100
  return false;
95
101
  }
96
102
  }
97
103
  /**
98
104
  * Stop recording
99
105
  */
100
- async stopRecording(options) {
106
+ async stopRecording(options, sendMessages = true) {
101
107
  try {
102
108
  if (!this.isRecording) {
103
109
  // Send error message to widget so it can exit "stopping" state
104
- this.postMessage({
105
- type: CuoralMessageType.RECORDING_ERROR,
106
- payload: { error: 'Not recording' },
107
- });
110
+ if (sendMessages) {
111
+ this.postMessage({
112
+ type: CuoralMessageType.RECORDING_ERROR,
113
+ payload: { error: 'Not recording' },
114
+ });
115
+ }
108
116
  return null;
109
117
  }
110
118
  const result = await CuoralPlugin$1.stopRecording(options);
@@ -113,23 +121,15 @@ class CuoralRecorder {
113
121
  const duration = this.recordingStartTime
114
122
  ? Math.floor((Date.now() - this.recordingStartTime) / 1000)
115
123
  : 0;
116
- // If uploaded, notify widget differently
117
- if (result.uploaded) {
118
- this.postMessage({
119
- type: CuoralMessageType.RECORDING_UPLOADED,
120
- payload: {
121
- duration: result.duration || duration,
122
- uploaded: true
123
- },
124
- });
125
- }
126
- else {
127
- // Post message to widget (old behavior)
124
+ // Send RECORDING_STOPPED message only if enabled
125
+ if (sendMessages) {
128
126
  this.postMessage({
129
127
  type: CuoralMessageType.RECORDING_STOPPED,
130
128
  payload: {
131
129
  filePath: result.filePath,
132
130
  duration: result.duration || duration,
131
+ uploaded: result.uploaded,
132
+ uploadedToBackend: result.uploaded,
133
133
  },
134
134
  });
135
135
  }
@@ -140,18 +140,22 @@ class CuoralRecorder {
140
140
  };
141
141
  }
142
142
  // If result.success is false, send error to widget
143
- this.postMessage({
144
- type: CuoralMessageType.RECORDING_ERROR,
145
- payload: { error: 'Failed to stop recording' },
146
- });
143
+ if (sendMessages) {
144
+ this.postMessage({
145
+ type: CuoralMessageType.RECORDING_ERROR,
146
+ payload: { error: 'Failed to stop recording' },
147
+ });
148
+ }
147
149
  return null;
148
150
  }
149
151
  catch (error) {
150
152
  console.error('[Cuoral] Failed to stop recording:', error);
151
- this.postMessage({
152
- type: CuoralMessageType.RECORDING_ERROR,
153
- payload: { error: error.message },
154
- });
153
+ if (sendMessages) {
154
+ this.postMessage({
155
+ type: CuoralMessageType.RECORDING_ERROR,
156
+ payload: { error: error.message },
157
+ });
158
+ }
155
159
  return null;
156
160
  }
157
161
  }
@@ -1430,6 +1434,7 @@ class CuoralIntelligence {
1430
1434
  */
1431
1435
  class Cuoral {
1432
1436
  constructor(options) {
1437
+ console.log('[Cuoral] Constructor called with options:', options);
1433
1438
  // Check if running on a mobile platform
1434
1439
  if (!Capacitor.isNativePlatform()) {
1435
1440
  throw new Error('Cuoral Ionic library only works on native mobile platforms (iOS/Android). Web is not supported.');
@@ -1439,6 +1444,7 @@ class Cuoral {
1439
1444
  useModal: true,
1440
1445
  ...options
1441
1446
  };
1447
+ console.log('[Cuoral] Merged options:', this.options);
1442
1448
  // Determine widget base URL
1443
1449
  const baseUrl = options.widgetBaseUrl || Cuoral.PRODUCTION_WIDGET_URL;
1444
1450
  const params = new URLSearchParams({
@@ -1468,8 +1474,14 @@ class Cuoral {
1468
1474
  debug: options.debug || false
1469
1475
  });
1470
1476
  this.recorder = new CuoralRecorder();
1477
+ // Expose bridge on window for widget detection and debugging
1478
+ window.CuoralCapacitorBridge = true;
1479
+ window.__cuoralBridge = this.bridge; // For debugging
1480
+ window.__cuoralRecorder = this.recorder; // For debugging
1481
+ console.log('[Cuoral] Bridge and recorder created, exposed on window');
1471
1482
  // Setup automatic message handlers
1472
1483
  this.setupMessageHandlers();
1484
+ console.log('[Cuoral] Constructor complete');
1473
1485
  }
1474
1486
  /**
1475
1487
  * Initialize Cuoral
@@ -1864,25 +1876,45 @@ class Cuoral {
1864
1876
  setupMessageHandlers() {
1865
1877
  // Handle start recording requests from widget
1866
1878
  this.bridge.on(CuoralMessageType.START_RECORDING, async () => {
1867
- await this.recorder.startRecording();
1879
+ // Pass sendMessages: false to prevent duplicate messages
1880
+ const success = await this.recorder.startRecording(undefined, false);
1881
+ if (success) {
1882
+ // Send RECORDING_STARTED back to widget
1883
+ this.bridge.sendToWidget({
1884
+ type: CuoralMessageType.RECORDING_STARTED,
1885
+ payload: {
1886
+ timestamp: Date.now()
1887
+ }
1888
+ });
1889
+ }
1890
+ else {
1891
+ this.bridge.sendToWidget({
1892
+ type: CuoralMessageType.RECORDING_ERROR,
1893
+ payload: {
1894
+ error: 'Failed to start recording'
1895
+ }
1896
+ });
1897
+ }
1868
1898
  });
1869
1899
  // Handle stop recording requests from widget
1870
1900
  this.bridge.on(CuoralMessageType.STOP_RECORDING, async () => {
1871
1901
  const sessionId = localStorage.getItem('__x_loadID');
1872
1902
  const customerId = localStorage.getItem('cuoralCustomerId');
1903
+ // Pass sendMessages: false to prevent duplicate messages
1873
1904
  const result = await this.recorder.stopRecording({
1874
1905
  autoUpload: true,
1875
1906
  sessionId: sessionId || undefined,
1876
1907
  publicKey: this.options.publicKey,
1877
1908
  customerId: customerId || undefined,
1878
- });
1909
+ }, false);
1879
1910
  if (result && result.uploaded) {
1880
- // Video was automatically uploaded, just notify widget
1911
+ // Video was automatically uploaded, notify widget with RECORDING_STOPPED
1881
1912
  this.bridge.sendToWidget({
1882
- type: CuoralMessageType.RECORDING_UPLOADED,
1913
+ type: CuoralMessageType.RECORDING_STOPPED,
1883
1914
  payload: {
1884
1915
  duration: result.duration,
1885
1916
  uploaded: true,
1917
+ uploadedToBackend: true,
1886
1918
  timestamp: Date.now()
1887
1919
  }
1888
1920
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.js CHANGED
@@ -65,7 +65,7 @@ class CuoralRecorder {
65
65
  /**
66
66
  * Start recording with automatic permission handling
67
67
  */
68
- async startRecording(options) {
68
+ async startRecording(options, sendMessages = true) {
69
69
  try {
70
70
  // Check if already recording
71
71
  if (this.isRecording) {
@@ -87,20 +87,24 @@ class CuoralRecorder {
87
87
  if (result.success) {
88
88
  this.isRecording = true;
89
89
  this.recordingStartTime = Date.now();
90
- // Post message to widget
91
- this.postMessage({
92
- type: exports.CuoralMessageType.RECORDING_STARTED,
93
- payload: { timestamp: this.recordingStartTime },
94
- });
90
+ // Post message to widget only if enabled (disabled when called from widget handler)
91
+ if (sendMessages) {
92
+ this.postMessage({
93
+ type: exports.CuoralMessageType.RECORDING_STARTED,
94
+ payload: { timestamp: this.recordingStartTime },
95
+ });
96
+ }
95
97
  }
96
98
  else {
97
99
  // Recording failed - reset state and notify widget
98
100
  this.isRecording = false;
99
101
  this.recordingStartTime = undefined;
100
- this.postMessage({
101
- type: exports.CuoralMessageType.RECORDING_ERROR,
102
- payload: { error: 'Failed to start recording' },
103
- });
102
+ if (sendMessages) {
103
+ this.postMessage({
104
+ type: exports.CuoralMessageType.RECORDING_ERROR,
105
+ payload: { error: 'Failed to start recording' },
106
+ });
107
+ }
104
108
  }
105
109
  return result.success;
106
110
  }
@@ -108,24 +112,28 @@ class CuoralRecorder {
108
112
  // Exception occurred - reset state and notify widget
109
113
  this.isRecording = false;
110
114
  this.recordingStartTime = undefined;
111
- this.postMessage({
112
- type: exports.CuoralMessageType.RECORDING_ERROR,
113
- payload: { error: error.message },
114
- });
115
+ if (sendMessages) {
116
+ this.postMessage({
117
+ type: exports.CuoralMessageType.RECORDING_ERROR,
118
+ payload: { error: error.message },
119
+ });
120
+ }
115
121
  return false;
116
122
  }
117
123
  }
118
124
  /**
119
125
  * Stop recording
120
126
  */
121
- async stopRecording(options) {
127
+ async stopRecording(options, sendMessages = true) {
122
128
  try {
123
129
  if (!this.isRecording) {
124
130
  // Send error message to widget so it can exit "stopping" state
125
- this.postMessage({
126
- type: exports.CuoralMessageType.RECORDING_ERROR,
127
- payload: { error: 'Not recording' },
128
- });
131
+ if (sendMessages) {
132
+ this.postMessage({
133
+ type: exports.CuoralMessageType.RECORDING_ERROR,
134
+ payload: { error: 'Not recording' },
135
+ });
136
+ }
129
137
  return null;
130
138
  }
131
139
  const result = await CuoralPlugin$1.stopRecording(options);
@@ -134,23 +142,15 @@ class CuoralRecorder {
134
142
  const duration = this.recordingStartTime
135
143
  ? Math.floor((Date.now() - this.recordingStartTime) / 1000)
136
144
  : 0;
137
- // If uploaded, notify widget differently
138
- if (result.uploaded) {
139
- this.postMessage({
140
- type: exports.CuoralMessageType.RECORDING_UPLOADED,
141
- payload: {
142
- duration: result.duration || duration,
143
- uploaded: true
144
- },
145
- });
146
- }
147
- else {
148
- // Post message to widget (old behavior)
145
+ // Send RECORDING_STOPPED message only if enabled
146
+ if (sendMessages) {
149
147
  this.postMessage({
150
148
  type: exports.CuoralMessageType.RECORDING_STOPPED,
151
149
  payload: {
152
150
  filePath: result.filePath,
153
151
  duration: result.duration || duration,
152
+ uploaded: result.uploaded,
153
+ uploadedToBackend: result.uploaded,
154
154
  },
155
155
  });
156
156
  }
@@ -161,18 +161,22 @@ class CuoralRecorder {
161
161
  };
162
162
  }
163
163
  // If result.success is false, send error to widget
164
- this.postMessage({
165
- type: exports.CuoralMessageType.RECORDING_ERROR,
166
- payload: { error: 'Failed to stop recording' },
167
- });
164
+ if (sendMessages) {
165
+ this.postMessage({
166
+ type: exports.CuoralMessageType.RECORDING_ERROR,
167
+ payload: { error: 'Failed to stop recording' },
168
+ });
169
+ }
168
170
  return null;
169
171
  }
170
172
  catch (error) {
171
173
  console.error('[Cuoral] Failed to stop recording:', error);
172
- this.postMessage({
173
- type: exports.CuoralMessageType.RECORDING_ERROR,
174
- payload: { error: error.message },
175
- });
174
+ if (sendMessages) {
175
+ this.postMessage({
176
+ type: exports.CuoralMessageType.RECORDING_ERROR,
177
+ payload: { error: error.message },
178
+ });
179
+ }
176
180
  return null;
177
181
  }
178
182
  }
@@ -1451,6 +1455,7 @@ class CuoralIntelligence {
1451
1455
  */
1452
1456
  class Cuoral {
1453
1457
  constructor(options) {
1458
+ console.log('[Cuoral] Constructor called with options:', options);
1454
1459
  // Check if running on a mobile platform
1455
1460
  if (!core.Capacitor.isNativePlatform()) {
1456
1461
  throw new Error('Cuoral Ionic library only works on native mobile platforms (iOS/Android). Web is not supported.');
@@ -1460,6 +1465,7 @@ class Cuoral {
1460
1465
  useModal: true,
1461
1466
  ...options
1462
1467
  };
1468
+ console.log('[Cuoral] Merged options:', this.options);
1463
1469
  // Determine widget base URL
1464
1470
  const baseUrl = options.widgetBaseUrl || Cuoral.PRODUCTION_WIDGET_URL;
1465
1471
  const params = new URLSearchParams({
@@ -1489,8 +1495,14 @@ class Cuoral {
1489
1495
  debug: options.debug || false
1490
1496
  });
1491
1497
  this.recorder = new CuoralRecorder();
1498
+ // Expose bridge on window for widget detection and debugging
1499
+ window.CuoralCapacitorBridge = true;
1500
+ window.__cuoralBridge = this.bridge; // For debugging
1501
+ window.__cuoralRecorder = this.recorder; // For debugging
1502
+ console.log('[Cuoral] Bridge and recorder created, exposed on window');
1492
1503
  // Setup automatic message handlers
1493
1504
  this.setupMessageHandlers();
1505
+ console.log('[Cuoral] Constructor complete');
1494
1506
  }
1495
1507
  /**
1496
1508
  * Initialize Cuoral
@@ -1885,25 +1897,45 @@ class Cuoral {
1885
1897
  setupMessageHandlers() {
1886
1898
  // Handle start recording requests from widget
1887
1899
  this.bridge.on(exports.CuoralMessageType.START_RECORDING, async () => {
1888
- await this.recorder.startRecording();
1900
+ // Pass sendMessages: false to prevent duplicate messages
1901
+ const success = await this.recorder.startRecording(undefined, false);
1902
+ if (success) {
1903
+ // Send RECORDING_STARTED back to widget
1904
+ this.bridge.sendToWidget({
1905
+ type: exports.CuoralMessageType.RECORDING_STARTED,
1906
+ payload: {
1907
+ timestamp: Date.now()
1908
+ }
1909
+ });
1910
+ }
1911
+ else {
1912
+ this.bridge.sendToWidget({
1913
+ type: exports.CuoralMessageType.RECORDING_ERROR,
1914
+ payload: {
1915
+ error: 'Failed to start recording'
1916
+ }
1917
+ });
1918
+ }
1889
1919
  });
1890
1920
  // Handle stop recording requests from widget
1891
1921
  this.bridge.on(exports.CuoralMessageType.STOP_RECORDING, async () => {
1892
1922
  const sessionId = localStorage.getItem('__x_loadID');
1893
1923
  const customerId = localStorage.getItem('cuoralCustomerId');
1924
+ // Pass sendMessages: false to prevent duplicate messages
1894
1925
  const result = await this.recorder.stopRecording({
1895
1926
  autoUpload: true,
1896
1927
  sessionId: sessionId || undefined,
1897
1928
  publicKey: this.options.publicKey,
1898
1929
  customerId: customerId || undefined,
1899
- });
1930
+ }, false);
1900
1931
  if (result && result.uploaded) {
1901
- // Video was automatically uploaded, just notify widget
1932
+ // Video was automatically uploaded, notify widget with RECORDING_STOPPED
1902
1933
  this.bridge.sendToWidget({
1903
- type: exports.CuoralMessageType.RECORDING_UPLOADED,
1934
+ type: exports.CuoralMessageType.RECORDING_STOPPED,
1904
1935
  payload: {
1905
1936
  duration: result.duration,
1906
1937
  uploaded: true,
1938
+ uploadedToBackend: true,
1907
1939
  timestamp: Date.now()
1908
1940
  }
1909
1941
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/plugin.d.ts CHANGED
@@ -59,7 +59,7 @@ export declare class CuoralRecorder {
59
59
  /**
60
60
  * Start recording with automatic permission handling
61
61
  */
62
- startRecording(options?: RecordingOptions): Promise<boolean>;
62
+ startRecording(options?: RecordingOptions, sendMessages?: boolean): Promise<boolean>;
63
63
  /**
64
64
  * Stop recording
65
65
  */
@@ -68,7 +68,7 @@ export declare class CuoralRecorder {
68
68
  sessionId?: string;
69
69
  publicKey?: string;
70
70
  customerId?: string;
71
- }): Promise<{
71
+ }, sendMessages?: boolean): Promise<{
72
72
  filePath?: string;
73
73
  duration?: number;
74
74
  uploaded?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,cAAc,EACf,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,cAAc,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAE1E;;OAEG;IACH,aAAa,CAAC,OAAO,CAAC,EAAE;QACtB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC;QACV,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IAEH;;OAEG;IACH,iBAAiB,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IAE7C;;OAEG;IACH,cAAc,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAErE;;OAEG;IACH,oBAAoB,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAExD;;OAEG;IACH,kBAAkB,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACrD;AAED;;GAEG;AACH,QAAA,MAAM,YAAY,uBAAwD,CAAC;AAE3E,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,kBAAkB,CAAC,CAAS;IAEpC;;OAEG;IACG,cAAc,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAyDlE;;OAEG;IACG,aAAa,CAAC,OAAO,CAAC,EAAE;QAC5B,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IA6DhF;;OAEG;IACG,cAAc,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAqBjF;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,cAAc,CAAC;IAIzC;;OAEG;IACH,OAAO,CAAC,WAAW;CAKpB"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,cAAc,EACf,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,cAAc,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAE1E;;OAEG;IACH,aAAa,CAAC,OAAO,CAAC,EAAE;QACtB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC;QACV,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IAEH;;OAEG;IACH,iBAAiB,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IAE7C;;OAEG;IACH,cAAc,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAErE;;OAEG;IACH,oBAAoB,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAExD;;OAEG;IACH,kBAAkB,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACrD;AAED;;GAEG;AACH,QAAA,MAAM,YAAY,uBAAwD,CAAC;AAE3E,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,kBAAkB,CAAC,CAAS;IAEpC;;OAEG;IACG,cAAc,CAAC,OAAO,CAAC,EAAE,gBAAgB,EAAE,YAAY,GAAE,OAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IA+DhG;;OAEG;IACG,aAAa,CAAC,OAAO,CAAC,EAAE;QAC5B,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,EAAE,YAAY,GAAE,OAAc,GAAG,OAAO,CAAC;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IA4D9G;;OAEG;IACG,cAAc,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAqBjF;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,cAAc,CAAC;IAIzC;;OAEG;IACH,OAAO,CAAC,WAAW;CAKpB"}
package/dist/plugin.js CHANGED
@@ -15,7 +15,7 @@ export class CuoralRecorder {
15
15
  /**
16
16
  * Start recording with automatic permission handling
17
17
  */
18
- async startRecording(options) {
18
+ async startRecording(options, sendMessages = true) {
19
19
  try {
20
20
  // Check if already recording
21
21
  if (this.isRecording) {
@@ -37,20 +37,24 @@ export class CuoralRecorder {
37
37
  if (result.success) {
38
38
  this.isRecording = true;
39
39
  this.recordingStartTime = Date.now();
40
- // Post message to widget
41
- this.postMessage({
42
- type: CuoralMessageType.RECORDING_STARTED,
43
- payload: { timestamp: this.recordingStartTime },
44
- });
40
+ // Post message to widget only if enabled (disabled when called from widget handler)
41
+ if (sendMessages) {
42
+ this.postMessage({
43
+ type: CuoralMessageType.RECORDING_STARTED,
44
+ payload: { timestamp: this.recordingStartTime },
45
+ });
46
+ }
45
47
  }
46
48
  else {
47
49
  // Recording failed - reset state and notify widget
48
50
  this.isRecording = false;
49
51
  this.recordingStartTime = undefined;
50
- this.postMessage({
51
- type: CuoralMessageType.RECORDING_ERROR,
52
- payload: { error: 'Failed to start recording' },
53
- });
52
+ if (sendMessages) {
53
+ this.postMessage({
54
+ type: CuoralMessageType.RECORDING_ERROR,
55
+ payload: { error: 'Failed to start recording' },
56
+ });
57
+ }
54
58
  }
55
59
  return result.success;
56
60
  }
@@ -58,24 +62,28 @@ export class CuoralRecorder {
58
62
  // Exception occurred - reset state and notify widget
59
63
  this.isRecording = false;
60
64
  this.recordingStartTime = undefined;
61
- this.postMessage({
62
- type: CuoralMessageType.RECORDING_ERROR,
63
- payload: { error: error.message },
64
- });
65
+ if (sendMessages) {
66
+ this.postMessage({
67
+ type: CuoralMessageType.RECORDING_ERROR,
68
+ payload: { error: error.message },
69
+ });
70
+ }
65
71
  return false;
66
72
  }
67
73
  }
68
74
  /**
69
75
  * Stop recording
70
76
  */
71
- async stopRecording(options) {
77
+ async stopRecording(options, sendMessages = true) {
72
78
  try {
73
79
  if (!this.isRecording) {
74
80
  // Send error message to widget so it can exit "stopping" state
75
- this.postMessage({
76
- type: CuoralMessageType.RECORDING_ERROR,
77
- payload: { error: 'Not recording' },
78
- });
81
+ if (sendMessages) {
82
+ this.postMessage({
83
+ type: CuoralMessageType.RECORDING_ERROR,
84
+ payload: { error: 'Not recording' },
85
+ });
86
+ }
79
87
  return null;
80
88
  }
81
89
  const result = await CuoralPlugin.stopRecording(options);
@@ -84,23 +92,15 @@ export class CuoralRecorder {
84
92
  const duration = this.recordingStartTime
85
93
  ? Math.floor((Date.now() - this.recordingStartTime) / 1000)
86
94
  : 0;
87
- // If uploaded, notify widget differently
88
- if (result.uploaded) {
89
- this.postMessage({
90
- type: CuoralMessageType.RECORDING_UPLOADED,
91
- payload: {
92
- duration: result.duration || duration,
93
- uploaded: true
94
- },
95
- });
96
- }
97
- else {
98
- // Post message to widget (old behavior)
95
+ // Send RECORDING_STOPPED message only if enabled
96
+ if (sendMessages) {
99
97
  this.postMessage({
100
98
  type: CuoralMessageType.RECORDING_STOPPED,
101
99
  payload: {
102
100
  filePath: result.filePath,
103
101
  duration: result.duration || duration,
102
+ uploaded: result.uploaded,
103
+ uploadedToBackend: result.uploaded,
104
104
  },
105
105
  });
106
106
  }
@@ -111,18 +111,22 @@ export class CuoralRecorder {
111
111
  };
112
112
  }
113
113
  // If result.success is false, send error to widget
114
- this.postMessage({
115
- type: CuoralMessageType.RECORDING_ERROR,
116
- payload: { error: 'Failed to stop recording' },
117
- });
114
+ if (sendMessages) {
115
+ this.postMessage({
116
+ type: CuoralMessageType.RECORDING_ERROR,
117
+ payload: { error: 'Failed to stop recording' },
118
+ });
119
+ }
118
120
  return null;
119
121
  }
120
122
  catch (error) {
121
123
  console.error('[Cuoral] Failed to stop recording:', error);
122
- this.postMessage({
123
- type: CuoralMessageType.RECORDING_ERROR,
124
- payload: { error: error.message },
125
- });
124
+ if (sendMessages) {
125
+ this.postMessage({
126
+ type: CuoralMessageType.RECORDING_ERROR,
127
+ payload: { error: error.message },
128
+ });
129
+ }
126
130
  return null;
127
131
  }
128
132
  }
@@ -110,10 +110,10 @@ public class CuoralPlugin: CAPPlugin {
110
110
  do {
111
111
  assetWriter = try AVAssetWriter(url: outputURL, fileType: .mp4)
112
112
 
113
- // Reduced quality settings to prevent huge file sizes
114
- // 2.5 Mbps bitrate = ~18MB per minute (down from ~600MB per minute)
113
+ // Optimized quality settings for screen recordings
114
+ // 1.5 Mbps bitrate = ~11MB per minute (~54MB for 5 min)
115
115
  let compressionProperties: [String: Any] = [
116
- AVVideoAverageBitRateKey: 2_500_000, // 2.5 Mbps
116
+ AVVideoAverageBitRateKey: 1_500_000, // 1.5 Mbps
117
117
  AVVideoMaxKeyFrameIntervalKey: 30,
118
118
  AVVideoProfileLevelKey: AVVideoProfileLevelH264BaselineAutoLevel
119
119
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cuoral-ionic",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "Cuoral Ionic Framework Library - Proactive customer success platform with support ticketing, customer intelligence, screen recording, and engagement tools",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/bridge.ts CHANGED
@@ -129,6 +129,7 @@ export class CuoralBridge {
129
129
  window.addEventListener('message', (event: MessageEvent) => {
130
130
  // Only process messages FROM the iframe
131
131
  const widgetFrame = document.querySelector('iframe[src*="mobile.html"]') as HTMLIFrameElement;
132
+
132
133
  if (widgetFrame && event.source === widgetFrame.contentWindow) {
133
134
  this.widgetIframe = widgetFrame;
134
135
  } else if (widgetFrame) {
package/src/cuoral.ts CHANGED
@@ -42,6 +42,8 @@ export class Cuoral {
42
42
  private static readonly DEV_WIDGET_URL = 'assets/mobile.html';
43
43
 
44
44
  constructor(options: CuoralOptions) {
45
+ console.log('[Cuoral] Constructor called with options:', options);
46
+
45
47
  // Check if running on a mobile platform
46
48
  if (!Capacitor.isNativePlatform()) {
47
49
  throw new Error('Cuoral Ionic library only works on native mobile platforms (iOS/Android). Web is not supported.');
@@ -53,6 +55,8 @@ export class Cuoral {
53
55
  ...options
54
56
  };
55
57
 
58
+ console.log('[Cuoral] Merged options:', this.options);
59
+
56
60
  // Determine widget base URL
57
61
  const baseUrl = options.widgetBaseUrl || Cuoral.PRODUCTION_WIDGET_URL;
58
62
  const params = new URLSearchParams({
@@ -86,8 +90,15 @@ export class Cuoral {
86
90
 
87
91
  this.recorder = new CuoralRecorder();
88
92
 
93
+ // Expose bridge on window for widget detection and debugging
94
+ (window as any).CuoralCapacitorBridge = true;
95
+ (window as any).__cuoralBridge = this.bridge; // For debugging
96
+ (window as any).__cuoralRecorder = this.recorder; // For debugging
97
+ console.log('[Cuoral] Bridge and recorder created, exposed on window');
98
+
89
99
  // Setup automatic message handlers
90
100
  this.setupMessageHandlers();
101
+ console.log('[Cuoral] Constructor complete');
91
102
  }
92
103
 
93
104
  /**
@@ -530,9 +541,23 @@ export class Cuoral {
530
541
  private setupMessageHandlers(): void {
531
542
  // Handle start recording requests from widget
532
543
  this.bridge.on(CuoralMessageType.START_RECORDING, async () => {
533
- const success = await this.recorder.startRecording();
534
- if (!success) {
535
- // Error already handled by recorder
544
+ // Pass sendMessages: false to prevent duplicate messages
545
+ const success = await this.recorder.startRecording(undefined, false);
546
+ if (success) {
547
+ // Send RECORDING_STARTED back to widget
548
+ this.bridge.sendToWidget({
549
+ type: CuoralMessageType.RECORDING_STARTED,
550
+ payload: {
551
+ timestamp: Date.now()
552
+ }
553
+ });
554
+ } else {
555
+ this.bridge.sendToWidget({
556
+ type: CuoralMessageType.RECORDING_ERROR,
557
+ payload: {
558
+ error: 'Failed to start recording'
559
+ }
560
+ });
536
561
  }
537
562
  });
538
563
 
@@ -541,20 +566,22 @@ export class Cuoral {
541
566
  const sessionId = localStorage.getItem('__x_loadID');
542
567
  const customerId = localStorage.getItem('cuoralCustomerId');
543
568
 
569
+ // Pass sendMessages: false to prevent duplicate messages
544
570
  const result = await this.recorder.stopRecording({
545
571
  autoUpload: true,
546
572
  sessionId: sessionId || undefined,
547
573
  publicKey: this.options.publicKey,
548
574
  customerId: customerId || undefined,
549
- });
575
+ }, false);
550
576
 
551
577
  if (result && result.uploaded) {
552
- // Video was automatically uploaded, just notify widget
578
+ // Video was automatically uploaded, notify widget with RECORDING_STOPPED
553
579
  this.bridge.sendToWidget({
554
- type: CuoralMessageType.RECORDING_UPLOADED,
580
+ type: CuoralMessageType.RECORDING_STOPPED,
555
581
  payload: {
556
582
  duration: result.duration,
557
583
  uploaded: true,
584
+ uploadedToBackend: true,
558
585
  timestamp: Date.now()
559
586
  }
560
587
  });
package/src/plugin.ts CHANGED
@@ -70,7 +70,7 @@ export class CuoralRecorder {
70
70
  /**
71
71
  * Start recording with automatic permission handling
72
72
  */
73
- async startRecording(options?: RecordingOptions): Promise<boolean> {
73
+ async startRecording(options?: RecordingOptions, sendMessages: boolean = true): Promise<boolean> {
74
74
  try {
75
75
  // Check if already recording
76
76
  if (this.isRecording) {
@@ -97,20 +97,24 @@ export class CuoralRecorder {
97
97
  this.isRecording = true;
98
98
  this.recordingStartTime = Date.now();
99
99
 
100
- // Post message to widget
101
- this.postMessage({
102
- type: CuoralMessageType.RECORDING_STARTED,
103
- payload: { timestamp: this.recordingStartTime },
104
- });
100
+ // Post message to widget only if enabled (disabled when called from widget handler)
101
+ if (sendMessages) {
102
+ this.postMessage({
103
+ type: CuoralMessageType.RECORDING_STARTED,
104
+ payload: { timestamp: this.recordingStartTime },
105
+ });
106
+ }
105
107
  } else {
106
108
  // Recording failed - reset state and notify widget
107
109
  this.isRecording = false;
108
110
  this.recordingStartTime = undefined;
109
111
 
110
- this.postMessage({
111
- type: CuoralMessageType.RECORDING_ERROR,
112
- payload: { error: 'Failed to start recording' },
113
- });
112
+ if (sendMessages) {
113
+ this.postMessage({
114
+ type: CuoralMessageType.RECORDING_ERROR,
115
+ payload: { error: 'Failed to start recording' },
116
+ });
117
+ }
114
118
  }
115
119
 
116
120
  return result.success;
@@ -119,10 +123,12 @@ export class CuoralRecorder {
119
123
  this.isRecording = false;
120
124
  this.recordingStartTime = undefined;
121
125
 
122
- this.postMessage({
123
- type: CuoralMessageType.RECORDING_ERROR,
124
- payload: { error: (error as Error).message },
125
- });
126
+ if (sendMessages) {
127
+ this.postMessage({
128
+ type: CuoralMessageType.RECORDING_ERROR,
129
+ payload: { error: (error as Error).message },
130
+ });
131
+ }
126
132
  return false;
127
133
  }
128
134
  }
@@ -135,14 +141,16 @@ export class CuoralRecorder {
135
141
  sessionId?: string;
136
142
  publicKey?: string;
137
143
  customerId?: string;
138
- }): Promise<{ filePath?: string; duration?: number; uploaded?: boolean } | null> {
144
+ }, sendMessages: boolean = true): Promise<{ filePath?: string; duration?: number; uploaded?: boolean } | null> {
139
145
  try {
140
146
  if (!this.isRecording) {
141
147
  // Send error message to widget so it can exit "stopping" state
142
- this.postMessage({
143
- type: CuoralMessageType.RECORDING_ERROR,
144
- payload: { error: 'Not recording' },
145
- });
148
+ if (sendMessages) {
149
+ this.postMessage({
150
+ type: CuoralMessageType.RECORDING_ERROR,
151
+ payload: { error: 'Not recording' },
152
+ });
153
+ }
146
154
  return null;
147
155
  }
148
156
 
@@ -153,22 +161,15 @@ export class CuoralRecorder {
153
161
  ? Math.floor((Date.now() - this.recordingStartTime) / 1000)
154
162
  : 0;
155
163
 
156
- // If uploaded, notify widget differently
157
- if (result.uploaded) {
158
- this.postMessage({
159
- type: CuoralMessageType.RECORDING_UPLOADED,
160
- payload: {
161
- duration: result.duration || duration,
162
- uploaded: true
163
- },
164
- });
165
- } else {
166
- // Post message to widget (old behavior)
164
+ // Send RECORDING_STOPPED message only if enabled
165
+ if (sendMessages) {
167
166
  this.postMessage({
168
167
  type: CuoralMessageType.RECORDING_STOPPED,
169
168
  payload: {
170
169
  filePath: result.filePath,
171
170
  duration: result.duration || duration,
171
+ uploaded: result.uploaded,
172
+ uploadedToBackend: result.uploaded,
172
173
  },
173
174
  });
174
175
  }
@@ -181,17 +182,21 @@ export class CuoralRecorder {
181
182
  }
182
183
 
183
184
  // If result.success is false, send error to widget
184
- this.postMessage({
185
- type: CuoralMessageType.RECORDING_ERROR,
186
- payload: { error: 'Failed to stop recording' },
187
- });
185
+ if (sendMessages) {
186
+ this.postMessage({
187
+ type: CuoralMessageType.RECORDING_ERROR,
188
+ payload: { error: 'Failed to stop recording' },
189
+ });
190
+ }
188
191
  return null;
189
192
  } catch (error) {
190
193
  console.error('[Cuoral] Failed to stop recording:', error);
191
- this.postMessage({
192
- type: CuoralMessageType.RECORDING_ERROR,
193
- payload: { error: (error as Error).message },
194
- });
194
+ if (sendMessages) {
195
+ this.postMessage({
196
+ type: CuoralMessageType.RECORDING_ERROR,
197
+ payload: { error: (error as Error).message },
198
+ });
199
+ }
195
200
  return null;
196
201
  }
197
202
  }