diff --git a/docs/images/firebase_google_signin.jpg b/docs/images/firebase_google_signin.jpg index 47188dd49..6f24015dd 100644 Binary files a/docs/images/firebase_google_signin.jpg and b/docs/images/firebase_google_signin.jpg differ diff --git a/docs/images/firebase_product_services.jpg b/docs/images/firebase_product_services.jpg index d1e2a6a3a..fda594951 100644 Binary files a/docs/images/firebase_product_services.jpg and b/docs/images/firebase_product_services.jpg differ diff --git a/ios/OpenBot/OpenBot/Controllers/Autopilot/AutopilotFragment.swift b/ios/OpenBot/OpenBot/Controllers/Autopilot/AutopilotFragment.swift index 7623f90d1..ff244e7e3 100644 --- a/ios/OpenBot/OpenBot/Controllers/Autopilot/AutopilotFragment.swift +++ b/ios/OpenBot/OpenBot/Controllers/Autopilot/AutopilotFragment.swift @@ -283,4 +283,3 @@ class AutopilotFragment: CameraController { protocol autopilotDelegate: AnyObject { func didPerformAction() } - diff --git a/ios/OpenBot/OpenBot/Controllers/DataCollection/DataCollectionController.swift b/ios/OpenBot/OpenBot/Controllers/DataCollection/DataCollectionController.swift index 8bc5db63b..3d3657f9c 100644 --- a/ios/OpenBot/OpenBot/Controllers/DataCollection/DataCollectionController.swift +++ b/ios/OpenBot/OpenBot/Controllers/DataCollection/DataCollectionController.swift @@ -60,7 +60,7 @@ class DataCollectionController: CameraController { let newBackButton = UIBarButtonItem(image: backNavigationIcon, title: Strings.dataCollection, target: self, action: #selector(DataCollectionController.back(sender:)), titleColor: Colors.navigationColor ?? .white) navigationItem.leftBarButtonItem = newBackButton } - + if let value = preferencesManager.getSensorData(sensor: "isVehicleLogSelected"){ dataLogger.isVehicleLogSelected = value as! Bool; } @@ -75,7 +75,7 @@ class DataCollectionController: CameraController { if let value = preferencesManager.getSensorData(sensor: "isAccelerationLogSelected"){ dataLogger.isAccelerationLogSelected = value as! Bool; } - + DeviceCurrentOrientation.shared.findDeviceOrientation() NotificationCenter.default.addObserver(self, selector: #selector(switchCamera), name: .switchCamera, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(openBluetoothSettings), name: .ble, object: nil) @@ -509,4 +509,3 @@ class DataCollectionController: CameraController { } - diff --git a/ios/OpenBot/OpenBot/Controllers/HomePage/HomePageViewController.swift b/ios/OpenBot/OpenBot/Controllers/HomePage/HomePageViewController.swift index 8bcb1d1d0..115ecc9ec 100644 --- a/ios/OpenBot/OpenBot/Controllers/HomePage/HomePageViewController.swift +++ b/ios/OpenBot/OpenBot/Controllers/HomePage/HomePageViewController.swift @@ -308,4 +308,3 @@ extension UIBarButtonItem { } - diff --git a/ios/OpenBot/OpenBot/Controllers/ObjectTracking/ObjectTrackingFragment.swift b/ios/OpenBot/OpenBot/Controllers/ObjectTracking/ObjectTrackingFragment.swift index df8c3f573..2d1b2d770 100644 --- a/ios/OpenBot/OpenBot/Controllers/ObjectTracking/ObjectTrackingFragment.swift +++ b/ios/OpenBot/OpenBot/Controllers/ObjectTracking/ObjectTrackingFragment.swift @@ -405,7 +405,7 @@ class ObjectTrackingFragment: CameraController { gameController.sendControlFromPhoneController(control: Control(left: Float(Double(leftSpeed ?? "0.0") ?? 0.0), right: Float(Double(rightSpeed ?? "0.0") ?? 0.0))) } } - + /// update screen command data coming from application @objc func updateLightsCommandFromControllerApp(_ notification: Notification) { if gameController.selectedControlMode == ControlMode.GAMEPAD { @@ -438,4 +438,4 @@ class ObjectTrackingFragment: CameraController { } } -} +} \ No newline at end of file diff --git a/ios/OpenBot/OpenBot/Controllers/Project/ProjectFragement.swift b/ios/OpenBot/OpenBot/Controllers/Project/ProjectFragement.swift index 92153ac24..f4cffcbf5 100644 --- a/ios/OpenBot/OpenBot/Controllers/Project/ProjectFragement.swift +++ b/ios/OpenBot/OpenBot/Controllers/Project/ProjectFragement.swift @@ -821,4 +821,3 @@ extension UserDefaults { - diff --git a/ios/OpenBot/OpenBot/Controllers/QrScanner/ScannerFragment.swift b/ios/OpenBot/OpenBot/Controllers/QrScanner/ScannerFragment.swift index 8a540878f..0f9f1e629 100644 --- a/ios/OpenBot/OpenBot/Controllers/QrScanner/ScannerFragment.swift +++ b/ios/OpenBot/OpenBot/Controllers/QrScanner/ScannerFragment.swift @@ -357,4 +357,4 @@ class scannerFragment: CameraController,autopilotDelegate { } -} +} \ No newline at end of file diff --git a/ios/OpenBot/OpenBot/Controllers/WebView/WebViewFragment.swift b/ios/OpenBot/OpenBot/Controllers/WebView/WebViewFragment.swift index b0dff4527..59c92b302 100644 --- a/ios/OpenBot/OpenBot/Controllers/WebView/WebViewFragment.swift +++ b/ios/OpenBot/OpenBot/Controllers/WebView/WebViewFragment.swift @@ -150,4 +150,4 @@ class openCodeWebView: UIViewController, WKUIDelegate, WKNavigationDelegate { present(alertController, animated: true, completion: nil) } -} +} \ No newline at end of file diff --git a/ios/OpenBot/OpenBot/Controllers/runRobot/RunRobot.swift b/ios/OpenBot/OpenBot/Controllers/runRobot/RunRobot.swift index abadc572d..e2bfee8cc 100644 --- a/ios/OpenBot/OpenBot/Controllers/runRobot/RunRobot.swift +++ b/ios/OpenBot/OpenBot/Controllers/runRobot/RunRobot.swift @@ -1054,4 +1054,4 @@ class runRobot: CameraController, ARSCNViewDelegate, UITextFieldDelegate { isMultipleAi = false isDetection = false } -} +} \ No newline at end of file diff --git a/ios/OpenBot/OpenBot/Data/SharedPreferences.swift b/ios/OpenBot/OpenBot/Data/SharedPreferences.swift index 42cf253d3..00a2da1fa 100644 --- a/ios/OpenBot/OpenBot/Data/SharedPreferences.swift +++ b/ios/OpenBot/OpenBot/Data/SharedPreferences.swift @@ -38,95 +38,95 @@ public class SharedPreferencesManager { public func setControlMode(value:String){ userDefaults.set(value, forKey: control_mode) } - + public func getControlMode() -> String? { return userDefaults.string(forKey: control_mode); } - + public func setDriveMode(value:String){ userDefaults.set(value, forKey: drive_mode); } - + public func getDriveMode() -> String? { return userDefaults.string(forKey: drive_mode); } - + public func setSpeedMode(value:Float){ userDefaults.set(value, forKey: speed_mode); } - + public func getSpeedMode() -> Float? { return userDefaults.float(forKey: speed_mode); } - + public func updateSensorData(value:Bool,sensor:String){ userDefaults.set(value, forKey: sensor); } - + public func getSensorData(sensor:String) -> Any? { return userDefaults.value(forKey: sensor) } - + public func setObjectTrackModel(value:String){ userDefaults.set(value, forKey: object_tracking_model); } - + public func getObjectTrackModel() -> String? { return userDefaults.string(forKey: object_tracking_model) } - + public func setObjectTrackingObject(value:String){ userDefaults.set(value, forKey: object_type) } - + public func getObjectTrackingObject() -> String? { return userDefaults.string(forKey: object_type); } - + public func setObjectTrackConfidence(value:Int){ userDefaults.set(value, forKey: confidence); } - + public func getObjectTrackConfidence() -> Any? { return userDefaults.value(forKey: confidence); } - + public func setDevice(value:String){ userDefaults.set(value, forKey: device); } - + public func getDevice() -> String? { return userDefaults.string(forKey: device); } - + public func setThreads(value:String){ userDefaults.set(value, forKey: threads); } - + public func getThreads() -> String? { return userDefaults.string(forKey: threads); } - + public func setAutopilotModel(value:String){ userDefaults.set(value, forKey: autopilot_model); } - + public func getAutopilotModel() -> String? { return userDefaults.string(forKey: autopilot_model); } - + public func setCameraSwitch(value:String){ userDefaults.set(value, forKey: camera_switch); } - + public func getCameraSwitch() -> String?{ userDefaults.string(forKey: camera_switch); } - + public func setDynamicSpeed(value:Bool){ userDefaults.set(value, forKey: dynamic_speed); } - + public func getDynamicSpeed() -> Any?{ userDefaults.value(forKey: dynamic_speed); } @@ -134,8 +134,8 @@ public class SharedPreferencesManager { public func setBlocklyCode(value:String){ userDefaults.set(value, forKey: blocklyCode); } - + public func getBlocklyCode() -> String?{ userDefaults.string(forKey: blocklyCode); } -} +} \ No newline at end of file diff --git a/ios/OpenBot/OpenBot/Data/SignalingMessage.swift b/ios/OpenBot/OpenBot/Data/SignalingMessage.swift index 9bc1cbaca..f7f641dfb 100644 --- a/ios/OpenBot/OpenBot/Data/SignalingMessage.swift +++ b/ios/OpenBot/OpenBot/Data/SignalingMessage.swift @@ -110,4 +110,4 @@ struct CandidateEvent: Codable { struct OpenBotSignal : Codable { var openbot : Int -} +} \ No newline at end of file diff --git a/ios/OpenBot/OpenBot/Info.plist b/ios/OpenBot/OpenBot/Info.plist index 84bd2ad3e..3d45733e7 100644 --- a/ios/OpenBot/OpenBot/Info.plist +++ b/ios/OpenBot/OpenBot/Info.plist @@ -107,4 +107,4 @@ kTCCServiceMediaLibrary Allow Audio Player for sound mode - + \ No newline at end of file diff --git a/ios/OpenBot/OpenBot/JsEvaluator/JsEvaluator.swift b/ios/OpenBot/OpenBot/JsEvaluator/JsEvaluator.swift index 3358c2caa..3cee0fdd7 100644 --- a/ios/OpenBot/OpenBot/JsEvaluator/JsEvaluator.swift +++ b/ios/OpenBot/OpenBot/JsEvaluator/JsEvaluator.swift @@ -63,7 +63,7 @@ class jsEvaluator { runOpenBotThreadClass = nil bluetooth.sendDataFromJs(payloadData: "c" + String(0) + "," + String(0) + "\n"); } - + /** function defined for all the methods of openBot blockly */ @@ -272,11 +272,11 @@ class jsEvaluator { let onLostFrames: @convention(block) (String, Int, String) -> Void = { (object, frames, task) in self.runOpenBotThreadClass?.onLostFrames(object: object, frames: frames, task: task); } - + let displaySensorData: @convention(block) (String) -> Void = { (inputString) in self.runOpenBotThreadClass?.displaySensorData(inputString: inputString); } - + let displayString: @convention(block) (String) -> Void = { (inputString) in self.runOpenBotThreadClass?.displayString(inputString: inputString); } @@ -1079,14 +1079,14 @@ class jsEvaluator { } taskStorage.addAttribute(classType: object, task: task, frames: frames, type: "unDetect"); } - + func displaySensorData(inputString: String){ if isCancelled { return } NotificationCenter.default.post(name: .displayItems, object: inputString); } - + func displayString(inputString: String){ if isCancelled { return @@ -1095,4 +1095,3 @@ class jsEvaluator { } } } - diff --git a/ios/OpenBot/OpenBot/Utils/AudioPlayer.swift b/ios/OpenBot/OpenBot/Utils/AudioPlayer.swift index bd41e7ff0..084fa5006 100644 --- a/ios/OpenBot/OpenBot/Utils/AudioPlayer.swift +++ b/ios/OpenBot/OpenBot/Utils/AudioPlayer.swift @@ -149,11 +149,10 @@ class AudioPlayer : AVPlayer, AVSpeechSynthesizerDelegate { speechSynthesizer.speak(utterance) } } - + func playInputString(input:String){ if(input != ""){ playString(input: input) } } } - diff --git a/ios/OpenBot/OpenBot/Utils/extensions/Notifications.swift b/ios/OpenBot/OpenBot/Utils/extensions/Notifications.swift index aebba04cd..00b8e0bd2 100644 --- a/ios/OpenBot/OpenBot/Utils/extensions/Notifications.swift +++ b/ios/OpenBot/OpenBot/Utils/extensions/Notifications.swift @@ -59,4 +59,4 @@ extension Notification.Name { static let server = Notification.Name("server"); static let saveAs = Notification.Name("saveAs"); static let displayItems = Notification.Name("displayItems"); -} +} \ No newline at end of file diff --git a/ios/OpenBot/Podfile.lock b/ios/OpenBot/Podfile.lock new file mode 100644 index 000000000..d73fc25d5 --- /dev/null +++ b/ios/OpenBot/Podfile.lock @@ -0,0 +1,1307 @@ +PODS: + - abseil/algorithm (1.20240116.2): + - abseil/algorithm/algorithm (= 1.20240116.2) + - abseil/algorithm/container (= 1.20240116.2) + - abseil/algorithm/algorithm (1.20240116.2): + - abseil/base/config + - abseil/xcprivacy + - abseil/algorithm/container (1.20240116.2): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/base/nullability + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/base (1.20240116.2): + - abseil/base/atomic_hook (= 1.20240116.2) + - abseil/base/base (= 1.20240116.2) + - abseil/base/base_internal (= 1.20240116.2) + - abseil/base/config (= 1.20240116.2) + - abseil/base/core_headers (= 1.20240116.2) + - abseil/base/cycleclock_internal (= 1.20240116.2) + - abseil/base/dynamic_annotations (= 1.20240116.2) + - abseil/base/endian (= 1.20240116.2) + - abseil/base/errno_saver (= 1.20240116.2) + - abseil/base/fast_type_id (= 1.20240116.2) + - abseil/base/log_severity (= 1.20240116.2) + - abseil/base/malloc_internal (= 1.20240116.2) + - abseil/base/no_destructor (= 1.20240116.2) + - abseil/base/nullability (= 1.20240116.2) + - abseil/base/prefetch (= 1.20240116.2) + - abseil/base/pretty_function (= 1.20240116.2) + - abseil/base/raw_logging_internal (= 1.20240116.2) + - abseil/base/spinlock_wait (= 1.20240116.2) + - abseil/base/strerror (= 1.20240116.2) + - abseil/base/throw_delegate (= 1.20240116.2) + - abseil/base/atomic_hook (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/base/base (1.20240116.2): + - abseil/base/atomic_hook + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/cycleclock_internal + - abseil/base/dynamic_annotations + - abseil/base/log_severity + - abseil/base/nullability + - abseil/base/raw_logging_internal + - abseil/base/spinlock_wait + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/base/base_internal (1.20240116.2): + - abseil/base/config + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/base/config (1.20240116.2): + - abseil/xcprivacy + - abseil/base/core_headers (1.20240116.2): + - abseil/base/config + - abseil/xcprivacy + - abseil/base/cycleclock_internal (1.20240116.2): + - abseil/base/base_internal + - abseil/base/config + - abseil/xcprivacy + - abseil/base/dynamic_annotations (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/base/endian (1.20240116.2): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/nullability + - abseil/xcprivacy + - abseil/base/errno_saver (1.20240116.2): + - abseil/base/config + - abseil/xcprivacy + - abseil/base/fast_type_id (1.20240116.2): + - abseil/base/config + - abseil/xcprivacy + - abseil/base/log_severity (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/base/malloc_internal (1.20240116.2): + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/raw_logging_internal + - abseil/xcprivacy + - abseil/base/no_destructor (1.20240116.2): + - abseil/base/config + - abseil/xcprivacy + - abseil/base/nullability (1.20240116.2): + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/base/prefetch (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/base/pretty_function (1.20240116.2): + - abseil/xcprivacy + - abseil/base/raw_logging_internal (1.20240116.2): + - abseil/base/atomic_hook + - abseil/base/config + - abseil/base/core_headers + - abseil/base/errno_saver + - abseil/base/log_severity + - abseil/xcprivacy + - abseil/base/spinlock_wait (1.20240116.2): + - abseil/base/base_internal + - abseil/base/core_headers + - abseil/base/errno_saver + - abseil/xcprivacy + - abseil/base/strerror (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/errno_saver + - abseil/xcprivacy + - abseil/base/throw_delegate (1.20240116.2): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/xcprivacy + - abseil/cleanup/cleanup (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/cleanup/cleanup_internal + - abseil/xcprivacy + - abseil/cleanup/cleanup_internal (1.20240116.2): + - abseil/base/base_internal + - abseil/base/core_headers + - abseil/utility/utility + - abseil/xcprivacy + - abseil/container/common (1.20240116.2): + - abseil/meta/type_traits + - abseil/types/optional + - abseil/xcprivacy + - abseil/container/common_policy_traits (1.20240116.2): + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/container/compressed_tuple (1.20240116.2): + - abseil/utility/utility + - abseil/xcprivacy + - abseil/container/container_memory (1.20240116.2): + - abseil/base/config + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/utility/utility + - abseil/xcprivacy + - abseil/container/fixed_array (1.20240116.2): + - abseil/algorithm/algorithm + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/throw_delegate + - abseil/container/compressed_tuple + - abseil/memory/memory + - abseil/xcprivacy + - abseil/container/flat_hash_map (1.20240116.2): + - abseil/algorithm/container + - abseil/base/core_headers + - abseil/container/container_memory + - abseil/container/hash_function_defaults + - abseil/container/raw_hash_map + - abseil/memory/memory + - abseil/xcprivacy + - abseil/container/flat_hash_set (1.20240116.2): + - abseil/algorithm/container + - abseil/base/core_headers + - abseil/container/container_memory + - abseil/container/hash_function_defaults + - abseil/container/raw_hash_set + - abseil/memory/memory + - abseil/xcprivacy + - abseil/container/hash_function_defaults (1.20240116.2): + - abseil/base/config + - abseil/hash/hash + - abseil/strings/cord + - abseil/strings/strings + - abseil/xcprivacy + - abseil/container/hash_policy_traits (1.20240116.2): + - abseil/container/common_policy_traits + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/container/hashtable_debug_hooks (1.20240116.2): + - abseil/base/config + - abseil/xcprivacy + - abseil/container/hashtablez_sampler (1.20240116.2): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/debugging/stacktrace + - abseil/memory/memory + - abseil/profiling/exponential_biased + - abseil/profiling/sample_recorder + - abseil/synchronization/synchronization + - abseil/time/time + - abseil/utility/utility + - abseil/xcprivacy + - abseil/container/inlined_vector (1.20240116.2): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/base/throw_delegate + - abseil/container/inlined_vector_internal + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/container/inlined_vector_internal (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/container/compressed_tuple + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/types/span + - abseil/xcprivacy + - abseil/container/layout (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/debugging/demangle_internal + - abseil/meta/type_traits + - abseil/strings/strings + - abseil/types/span + - abseil/utility/utility + - abseil/xcprivacy + - abseil/container/raw_hash_map (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/throw_delegate + - abseil/container/container_memory + - abseil/container/raw_hash_set + - abseil/xcprivacy + - abseil/container/raw_hash_set (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/endian + - abseil/base/prefetch + - abseil/base/raw_logging_internal + - abseil/container/common + - abseil/container/compressed_tuple + - abseil/container/container_memory + - abseil/container/hash_policy_traits + - abseil/container/hashtable_debug_hooks + - abseil/container/hashtablez_sampler + - abseil/hash/hash + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/utility/utility + - abseil/xcprivacy + - abseil/crc/cpu_detect (1.20240116.2): + - abseil/base/base + - abseil/base/config + - abseil/xcprivacy + - abseil/crc/crc32c (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/prefetch + - abseil/crc/cpu_detect + - abseil/crc/crc_internal + - abseil/crc/non_temporal_memcpy + - abseil/strings/str_format + - abseil/strings/strings + - abseil/xcprivacy + - abseil/crc/crc_cord_state (1.20240116.2): + - abseil/base/config + - abseil/crc/crc32c + - abseil/numeric/bits + - abseil/strings/strings + - abseil/xcprivacy + - abseil/crc/crc_internal (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/prefetch + - abseil/base/raw_logging_internal + - abseil/crc/cpu_detect + - abseil/memory/memory + - abseil/numeric/bits + - abseil/xcprivacy + - abseil/crc/non_temporal_arm_intrinsics (1.20240116.2): + - abseil/base/config + - abseil/xcprivacy + - abseil/crc/non_temporal_memcpy (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/crc/non_temporal_arm_intrinsics + - abseil/xcprivacy + - abseil/debugging/debugging_internal (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/errno_saver + - abseil/base/raw_logging_internal + - abseil/xcprivacy + - abseil/debugging/demangle_internal (1.20240116.2): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/debugging/stacktrace (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/raw_logging_internal + - abseil/debugging/debugging_internal + - abseil/xcprivacy + - abseil/debugging/symbolize (1.20240116.2): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/malloc_internal + - abseil/base/raw_logging_internal + - abseil/debugging/debugging_internal + - abseil/debugging/demangle_internal + - abseil/strings/strings + - abseil/xcprivacy + - abseil/flags/commandlineflag (1.20240116.2): + - abseil/base/config + - abseil/base/fast_type_id + - abseil/flags/commandlineflag_internal + - abseil/strings/strings + - abseil/types/optional + - abseil/xcprivacy + - abseil/flags/commandlineflag_internal (1.20240116.2): + - abseil/base/config + - abseil/base/fast_type_id + - abseil/xcprivacy + - abseil/flags/config (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/flags/path_util + - abseil/flags/program_name + - abseil/strings/strings + - abseil/synchronization/synchronization + - abseil/xcprivacy + - abseil/flags/flag (1.20240116.2): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/flags/config + - abseil/flags/flag_internal + - abseil/flags/reflection + - abseil/strings/strings + - abseil/xcprivacy + - abseil/flags/flag_internal (1.20240116.2): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/flags/commandlineflag + - abseil/flags/commandlineflag_internal + - abseil/flags/config + - abseil/flags/marshalling + - abseil/flags/reflection + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/strings/strings + - abseil/synchronization/synchronization + - abseil/utility/utility + - abseil/xcprivacy + - abseil/flags/marshalling (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/log_severity + - abseil/numeric/int128 + - abseil/strings/str_format + - abseil/strings/strings + - abseil/types/optional + - abseil/xcprivacy + - abseil/flags/path_util (1.20240116.2): + - abseil/base/config + - abseil/strings/strings + - abseil/xcprivacy + - abseil/flags/private_handle_accessor (1.20240116.2): + - abseil/base/config + - abseil/flags/commandlineflag + - abseil/flags/commandlineflag_internal + - abseil/strings/strings + - abseil/xcprivacy + - abseil/flags/program_name (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/flags/path_util + - abseil/strings/strings + - abseil/synchronization/synchronization + - abseil/xcprivacy + - abseil/flags/reflection (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/no_destructor + - abseil/container/flat_hash_map + - abseil/flags/commandlineflag + - abseil/flags/commandlineflag_internal + - abseil/flags/config + - abseil/flags/private_handle_accessor + - abseil/strings/strings + - abseil/synchronization/synchronization + - abseil/xcprivacy + - abseil/functional/any_invocable (1.20240116.2): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/utility/utility + - abseil/xcprivacy + - abseil/functional/bind_front (1.20240116.2): + - abseil/base/base_internal + - abseil/container/compressed_tuple + - abseil/meta/type_traits + - abseil/utility/utility + - abseil/xcprivacy + - abseil/functional/function_ref (1.20240116.2): + - abseil/base/base_internal + - abseil/base/core_headers + - abseil/functional/any_invocable + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/hash/city (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/xcprivacy + - abseil/hash/hash (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/container/fixed_array + - abseil/functional/function_ref + - abseil/hash/city + - abseil/hash/low_level_hash + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/strings/strings + - abseil/types/optional + - abseil/types/variant + - abseil/utility/utility + - abseil/xcprivacy + - abseil/hash/low_level_hash (1.20240116.2): + - abseil/base/config + - abseil/base/endian + - abseil/base/prefetch + - abseil/numeric/int128 + - abseil/xcprivacy + - abseil/memory (1.20240116.2): + - abseil/memory/memory (= 1.20240116.2) + - abseil/memory/memory (1.20240116.2): + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/meta (1.20240116.2): + - abseil/meta/type_traits (= 1.20240116.2) + - abseil/meta/type_traits (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/numeric/bits (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/numeric/int128 (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/numeric/bits + - abseil/xcprivacy + - abseil/numeric/representation (1.20240116.2): + - abseil/base/config + - abseil/xcprivacy + - abseil/profiling/exponential_biased (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/profiling/sample_recorder (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/synchronization/synchronization + - abseil/time/time + - abseil/xcprivacy + - abseil/random/bit_gen_ref (1.20240116.2): + - abseil/base/core_headers + - abseil/base/fast_type_id + - abseil/meta/type_traits + - abseil/random/internal/distribution_caller + - abseil/random/internal/fast_uniform_bits + - abseil/random/random + - abseil/xcprivacy + - abseil/random/distributions (1.20240116.2): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/random/internal/distribution_caller + - abseil/random/internal/fast_uniform_bits + - abseil/random/internal/fastmath + - abseil/random/internal/generate_real + - abseil/random/internal/iostream_state_saver + - abseil/random/internal/traits + - abseil/random/internal/uniform_helper + - abseil/random/internal/wide_multiply + - abseil/strings/strings + - abseil/xcprivacy + - abseil/random/internal/distribution_caller (1.20240116.2): + - abseil/base/config + - abseil/base/fast_type_id + - abseil/utility/utility + - abseil/xcprivacy + - abseil/random/internal/fast_uniform_bits (1.20240116.2): + - abseil/base/config + - abseil/meta/type_traits + - abseil/random/internal/traits + - abseil/xcprivacy + - abseil/random/internal/fastmath (1.20240116.2): + - abseil/numeric/bits + - abseil/xcprivacy + - abseil/random/internal/generate_real (1.20240116.2): + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/random/internal/fastmath + - abseil/random/internal/traits + - abseil/xcprivacy + - abseil/random/internal/iostream_state_saver (1.20240116.2): + - abseil/meta/type_traits + - abseil/numeric/int128 + - abseil/xcprivacy + - abseil/random/internal/nonsecure_base (1.20240116.2): + - abseil/base/core_headers + - abseil/container/inlined_vector + - abseil/meta/type_traits + - abseil/random/internal/pool_urbg + - abseil/random/internal/salted_seed_seq + - abseil/random/internal/seed_material + - abseil/types/span + - abseil/xcprivacy + - abseil/random/internal/pcg_engine (1.20240116.2): + - abseil/base/config + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/random/internal/fastmath + - abseil/random/internal/iostream_state_saver + - abseil/xcprivacy + - abseil/random/internal/platform (1.20240116.2): + - abseil/base/config + - abseil/xcprivacy + - abseil/random/internal/pool_urbg (1.20240116.2): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/random/internal/randen + - abseil/random/internal/seed_material + - abseil/random/internal/traits + - abseil/random/seed_gen_exception + - abseil/types/span + - abseil/xcprivacy + - abseil/random/internal/randen (1.20240116.2): + - abseil/base/raw_logging_internal + - abseil/random/internal/platform + - abseil/random/internal/randen_hwaes + - abseil/random/internal/randen_slow + - abseil/xcprivacy + - abseil/random/internal/randen_engine (1.20240116.2): + - abseil/base/endian + - abseil/meta/type_traits + - abseil/random/internal/iostream_state_saver + - abseil/random/internal/randen + - abseil/xcprivacy + - abseil/random/internal/randen_hwaes (1.20240116.2): + - abseil/base/config + - abseil/random/internal/platform + - abseil/random/internal/randen_hwaes_impl + - abseil/xcprivacy + - abseil/random/internal/randen_hwaes_impl (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/numeric/int128 + - abseil/random/internal/platform + - abseil/xcprivacy + - abseil/random/internal/randen_slow (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/numeric/int128 + - abseil/random/internal/platform + - abseil/xcprivacy + - abseil/random/internal/salted_seed_seq (1.20240116.2): + - abseil/container/inlined_vector + - abseil/meta/type_traits + - abseil/random/internal/seed_material + - abseil/types/optional + - abseil/types/span + - abseil/xcprivacy + - abseil/random/internal/seed_material (1.20240116.2): + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/raw_logging_internal + - abseil/random/internal/fast_uniform_bits + - abseil/strings/strings + - abseil/types/optional + - abseil/types/span + - abseil/xcprivacy + - abseil/random/internal/traits (1.20240116.2): + - abseil/base/config + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/xcprivacy + - abseil/random/internal/uniform_helper (1.20240116.2): + - abseil/base/config + - abseil/meta/type_traits + - abseil/numeric/int128 + - abseil/random/internal/traits + - abseil/xcprivacy + - abseil/random/internal/wide_multiply (1.20240116.2): + - abseil/base/config + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/random/internal/traits + - abseil/xcprivacy + - abseil/random/random (1.20240116.2): + - abseil/random/distributions + - abseil/random/internal/nonsecure_base + - abseil/random/internal/pcg_engine + - abseil/random/internal/pool_urbg + - abseil/random/internal/randen_engine + - abseil/random/seed_sequences + - abseil/xcprivacy + - abseil/random/seed_gen_exception (1.20240116.2): + - abseil/base/config + - abseil/xcprivacy + - abseil/random/seed_sequences (1.20240116.2): + - abseil/base/config + - abseil/random/internal/pool_urbg + - abseil/random/internal/salted_seed_seq + - abseil/random/internal/seed_material + - abseil/random/seed_gen_exception + - abseil/types/span + - abseil/xcprivacy + - abseil/status/status (1.20240116.2): + - abseil/base/atomic_hook + - abseil/base/config + - abseil/base/core_headers + - abseil/base/no_destructor + - abseil/base/nullability + - abseil/base/raw_logging_internal + - abseil/base/strerror + - abseil/container/inlined_vector + - abseil/debugging/stacktrace + - abseil/debugging/symbolize + - abseil/functional/function_ref + - abseil/memory/memory + - abseil/strings/cord + - abseil/strings/str_format + - abseil/strings/strings + - abseil/types/optional + - abseil/types/span + - abseil/xcprivacy + - abseil/status/statusor (1.20240116.2): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/nullability + - abseil/base/raw_logging_internal + - abseil/meta/type_traits + - abseil/status/status + - abseil/strings/has_ostream_operator + - abseil/strings/str_format + - abseil/strings/strings + - abseil/types/variant + - abseil/utility/utility + - abseil/xcprivacy + - abseil/strings/charset (1.20240116.2): + - abseil/base/core_headers + - abseil/strings/string_view + - abseil/xcprivacy + - abseil/strings/cord (1.20240116.2): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/nullability + - abseil/base/raw_logging_internal + - abseil/container/inlined_vector + - abseil/crc/crc32c + - abseil/crc/crc_cord_state + - abseil/functional/function_ref + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/strings/cord_internal + - abseil/strings/cordz_functions + - abseil/strings/cordz_info + - abseil/strings/cordz_statistics + - abseil/strings/cordz_update_scope + - abseil/strings/cordz_update_tracker + - abseil/strings/internal + - abseil/strings/strings + - abseil/types/optional + - abseil/types/span + - abseil/xcprivacy + - abseil/strings/cord_internal (1.20240116.2): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/base/throw_delegate + - abseil/container/compressed_tuple + - abseil/container/container_memory + - abseil/container/inlined_vector + - abseil/container/layout + - abseil/crc/crc_cord_state + - abseil/functional/function_ref + - abseil/meta/type_traits + - abseil/strings/strings + - abseil/types/span + - abseil/xcprivacy + - abseil/strings/cordz_functions (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/profiling/exponential_biased + - abseil/xcprivacy + - abseil/strings/cordz_handle (1.20240116.2): + - abseil/base/base + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/synchronization/synchronization + - abseil/xcprivacy + - abseil/strings/cordz_info (1.20240116.2): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/container/inlined_vector + - abseil/debugging/stacktrace + - abseil/strings/cord_internal + - abseil/strings/cordz_functions + - abseil/strings/cordz_handle + - abseil/strings/cordz_statistics + - abseil/strings/cordz_update_tracker + - abseil/synchronization/synchronization + - abseil/time/time + - abseil/types/span + - abseil/xcprivacy + - abseil/strings/cordz_statistics (1.20240116.2): + - abseil/base/config + - abseil/strings/cordz_update_tracker + - abseil/xcprivacy + - abseil/strings/cordz_update_scope (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/strings/cord_internal + - abseil/strings/cordz_info + - abseil/strings/cordz_update_tracker + - abseil/xcprivacy + - abseil/strings/cordz_update_tracker (1.20240116.2): + - abseil/base/config + - abseil/xcprivacy + - abseil/strings/has_ostream_operator (1.20240116.2): + - abseil/base/config + - abseil/xcprivacy + - abseil/strings/internal (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/strings/str_format (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/nullability + - abseil/strings/str_format_internal + - abseil/strings/string_view + - abseil/types/span + - abseil/xcprivacy + - abseil/strings/str_format_internal (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/container/fixed_array + - abseil/container/inlined_vector + - abseil/functional/function_ref + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/numeric/representation + - abseil/strings/strings + - abseil/types/optional + - abseil/types/span + - abseil/utility/utility + - abseil/xcprivacy + - abseil/strings/string_view (1.20240116.2): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/nullability + - abseil/base/throw_delegate + - abseil/xcprivacy + - abseil/strings/strings (1.20240116.2): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/nullability + - abseil/base/raw_logging_internal + - abseil/base/throw_delegate + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/strings/charset + - abseil/strings/internal + - abseil/strings/string_view + - abseil/xcprivacy + - abseil/synchronization/graphcycles_internal (1.20240116.2): + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/malloc_internal + - abseil/base/raw_logging_internal + - abseil/xcprivacy + - abseil/synchronization/kernel_timeout_internal (1.20240116.2): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/time/time + - abseil/xcprivacy + - abseil/synchronization/synchronization (1.20240116.2): + - abseil/base/atomic_hook + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/malloc_internal + - abseil/base/raw_logging_internal + - abseil/debugging/stacktrace + - abseil/debugging/symbolize + - abseil/synchronization/graphcycles_internal + - abseil/synchronization/kernel_timeout_internal + - abseil/time/time + - abseil/xcprivacy + - abseil/time (1.20240116.2): + - abseil/time/internal (= 1.20240116.2) + - abseil/time/time (= 1.20240116.2) + - abseil/time/internal (1.20240116.2): + - abseil/time/internal/cctz (= 1.20240116.2) + - abseil/time/internal/cctz (1.20240116.2): + - abseil/time/internal/cctz/civil_time (= 1.20240116.2) + - abseil/time/internal/cctz/time_zone (= 1.20240116.2) + - abseil/time/internal/cctz/civil_time (1.20240116.2): + - abseil/base/config + - abseil/xcprivacy + - abseil/time/internal/cctz/time_zone (1.20240116.2): + - abseil/base/config + - abseil/time/internal/cctz/civil_time + - abseil/xcprivacy + - abseil/time/time (1.20240116.2): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/numeric/int128 + - abseil/strings/strings + - abseil/time/internal/cctz/civil_time + - abseil/time/internal/cctz/time_zone + - abseil/types/optional + - abseil/xcprivacy + - abseil/types (1.20240116.2): + - abseil/types/any (= 1.20240116.2) + - abseil/types/bad_any_cast (= 1.20240116.2) + - abseil/types/bad_any_cast_impl (= 1.20240116.2) + - abseil/types/bad_optional_access (= 1.20240116.2) + - abseil/types/bad_variant_access (= 1.20240116.2) + - abseil/types/compare (= 1.20240116.2) + - abseil/types/optional (= 1.20240116.2) + - abseil/types/span (= 1.20240116.2) + - abseil/types/variant (= 1.20240116.2) + - abseil/types/any (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/fast_type_id + - abseil/meta/type_traits + - abseil/types/bad_any_cast + - abseil/utility/utility + - abseil/xcprivacy + - abseil/types/bad_any_cast (1.20240116.2): + - abseil/base/config + - abseil/types/bad_any_cast_impl + - abseil/xcprivacy + - abseil/types/bad_any_cast_impl (1.20240116.2): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/xcprivacy + - abseil/types/bad_optional_access (1.20240116.2): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/xcprivacy + - abseil/types/bad_variant_access (1.20240116.2): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/xcprivacy + - abseil/types/compare (1.20240116.2): + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/types/optional (1.20240116.2): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/nullability + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/types/bad_optional_access + - abseil/utility/utility + - abseil/xcprivacy + - abseil/types/span (1.20240116.2): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/base/nullability + - abseil/base/throw_delegate + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/types/variant (1.20240116.2): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/types/bad_variant_access + - abseil/utility/utility + - abseil/xcprivacy + - abseil/utility/utility (1.20240116.2): + - abseil/base/base_internal + - abseil/base/config + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/xcprivacy (1.20240116.2) + - AppAuth (1.7.5): + - AppAuth/Core (= 1.7.5) + - AppAuth/ExternalUserAgent (= 1.7.5) + - AppAuth/Core (1.7.5) + - AppAuth/ExternalUserAgent (1.7.5): + - AppAuth/Core + - BoringSSL-GRPC (0.0.32): + - BoringSSL-GRPC/Implementation (= 0.0.32) + - BoringSSL-GRPC/Interface (= 0.0.32) + - BoringSSL-GRPC/Implementation (0.0.32): + - BoringSSL-GRPC/Interface (= 0.0.32) + - BoringSSL-GRPC/Interface (0.0.32) + - DropDown (2.3.13) + - Firebase/Auth (10.27.0): + - Firebase/CoreOnly + - FirebaseAuth (~> 10.27.0) + - Firebase/Core (10.27.0): + - Firebase/CoreOnly + - FirebaseAnalytics (~> 10.27.0) + - Firebase/CoreOnly (10.27.0): + - FirebaseCore (= 10.27.0) + - Firebase/Storage (10.27.0): + - Firebase/CoreOnly + - FirebaseStorage (~> 10.27.0) + - FirebaseAnalytics (10.27.0): + - FirebaseAnalytics/AdIdSupport (= 10.27.0) + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30911.0, >= 2.30908.0) + - FirebaseAnalytics/AdIdSupport (10.27.0): + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleAppMeasurement (= 10.27.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30911.0, >= 2.30908.0) + - FirebaseAppCheckInterop (10.27.0) + - FirebaseAuth (10.27.0): + - FirebaseAppCheckInterop (~> 10.17) + - FirebaseCore (~> 10.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.8) + - GoogleUtilities/Environment (~> 7.8) + - GTMSessionFetcher/Core (< 4.0, >= 2.1) + - RecaptchaInterop (~> 100.0) + - FirebaseAuthInterop (10.27.0) + - FirebaseCore (10.27.0): + - FirebaseCoreInternal (~> 10.0) + - GoogleUtilities/Environment (~> 7.12) + - GoogleUtilities/Logger (~> 7.12) + - FirebaseCoreExtension (10.27.0): + - FirebaseCore (~> 10.0) + - FirebaseCoreInternal (10.27.0): + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - FirebaseFirestore (10.27.0): + - FirebaseCore (~> 10.0) + - FirebaseCoreExtension (~> 10.0) + - FirebaseFirestoreInternal (= 10.27.0) + - FirebaseSharedSwift (~> 10.0) + - FirebaseFirestoreInternal (10.27.0): + - abseil/algorithm (~> 1.20240116.1) + - abseil/base (~> 1.20240116.1) + - abseil/container/flat_hash_map (~> 1.20240116.1) + - abseil/memory (~> 1.20240116.1) + - abseil/meta (~> 1.20240116.1) + - abseil/strings/strings (~> 1.20240116.1) + - abseil/time (~> 1.20240116.1) + - abseil/types (~> 1.20240116.1) + - FirebaseAppCheckInterop (~> 10.17) + - FirebaseCore (~> 10.0) + - "gRPC-C++ (~> 1.62.0)" + - gRPC-Core (~> 1.62.0) + - leveldb-library (~> 1.22) + - nanopb (< 2.30911.0, >= 2.30908.0) + - FirebaseInstallations (10.27.0): + - FirebaseCore (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/UserDefaults (~> 7.8) + - PromisesObjC (~> 2.1) + - FirebaseSharedSwift (10.27.0) + - FirebaseStorage (10.27.0): + - FirebaseAppCheckInterop (~> 10.0) + - FirebaseAuthInterop (~> 10.25) + - FirebaseCore (~> 10.0) + - FirebaseCoreExtension (~> 10.0) + - GoogleUtilities/Environment (~> 7.12) + - GTMSessionFetcher/Core (< 4.0, >= 2.1) + - GoogleAPIClientForREST/Core (3.5.4): + - GTMSessionFetcher/Full (< 4.0, >= 1.6.1) + - GoogleAPIClientForREST/Drive (3.5.4): + - GoogleAPIClientForREST/Core + - GTMSessionFetcher/Full (< 4.0, >= 1.6.1) + - GoogleAppMeasurement (10.27.0): + - GoogleAppMeasurement/AdIdSupport (= 10.27.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30911.0, >= 2.30908.0) + - GoogleAppMeasurement/AdIdSupport (10.27.0): + - GoogleAppMeasurement/WithoutAdIdSupport (= 10.27.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30911.0, >= 2.30908.0) + - GoogleAppMeasurement/WithoutAdIdSupport (10.27.0): + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30911.0, >= 2.30908.0) + - GoogleSignIn (7.1.0): + - AppAuth (< 2.0, >= 1.7.3) + - GTMAppAuth (< 5.0, >= 4.1.1) + - GTMSessionFetcher/Core (~> 3.3) + - GoogleSignInSwiftSupport (7.1.0): + - GoogleSignIn (~> 7.1) + - GoogleUtilities/AppDelegateSwizzler (7.13.3): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Privacy + - GoogleUtilities/Environment (7.13.3): + - GoogleUtilities/Privacy + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.13.3): + - GoogleUtilities/Environment + - GoogleUtilities/Privacy + - GoogleUtilities/MethodSwizzler (7.13.3): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleUtilities/Network (7.13.3): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Privacy + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (7.13.3)": + - GoogleUtilities/Privacy + - GoogleUtilities/Privacy (7.13.3) + - GoogleUtilities/Reachability (7.13.3): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleUtilities/UserDefaults (7.13.3): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleWebRTC (1.1.32000) + - "gRPC-C++ (1.62.5)": + - "gRPC-C++/Implementation (= 1.62.5)" + - "gRPC-C++/Interface (= 1.62.5)" + - "gRPC-C++/Implementation (1.62.5)": + - abseil/algorithm/container (~> 1.20240116.2) + - abseil/base/base (~> 1.20240116.2) + - abseil/base/config (~> 1.20240116.2) + - abseil/base/core_headers (~> 1.20240116.2) + - abseil/cleanup/cleanup (~> 1.20240116.2) + - abseil/container/flat_hash_map (~> 1.20240116.2) + - abseil/container/flat_hash_set (~> 1.20240116.2) + - abseil/container/inlined_vector (~> 1.20240116.2) + - abseil/flags/flag (~> 1.20240116.2) + - abseil/flags/marshalling (~> 1.20240116.2) + - abseil/functional/any_invocable (~> 1.20240116.2) + - abseil/functional/bind_front (~> 1.20240116.2) + - abseil/functional/function_ref (~> 1.20240116.2) + - abseil/hash/hash (~> 1.20240116.2) + - abseil/memory/memory (~> 1.20240116.2) + - abseil/meta/type_traits (~> 1.20240116.2) + - abseil/random/bit_gen_ref (~> 1.20240116.2) + - abseil/random/distributions (~> 1.20240116.2) + - abseil/random/random (~> 1.20240116.2) + - abseil/status/status (~> 1.20240116.2) + - abseil/status/statusor (~> 1.20240116.2) + - abseil/strings/cord (~> 1.20240116.2) + - abseil/strings/str_format (~> 1.20240116.2) + - abseil/strings/strings (~> 1.20240116.2) + - abseil/synchronization/synchronization (~> 1.20240116.2) + - abseil/time/time (~> 1.20240116.2) + - abseil/types/optional (~> 1.20240116.2) + - abseil/types/span (~> 1.20240116.2) + - abseil/types/variant (~> 1.20240116.2) + - abseil/utility/utility (~> 1.20240116.2) + - "gRPC-C++/Interface (= 1.62.5)" + - "gRPC-C++/Privacy (= 1.62.5)" + - gRPC-Core (= 1.62.5) + - "gRPC-C++/Interface (1.62.5)" + - "gRPC-C++/Privacy (1.62.5)" + - gRPC-Core (1.62.5): + - gRPC-Core/Implementation (= 1.62.5) + - gRPC-Core/Interface (= 1.62.5) + - gRPC-Core/Implementation (1.62.5): + - abseil/algorithm/container (~> 1.20240116.2) + - abseil/base/base (~> 1.20240116.2) + - abseil/base/config (~> 1.20240116.2) + - abseil/base/core_headers (~> 1.20240116.2) + - abseil/cleanup/cleanup (~> 1.20240116.2) + - abseil/container/flat_hash_map (~> 1.20240116.2) + - abseil/container/flat_hash_set (~> 1.20240116.2) + - abseil/container/inlined_vector (~> 1.20240116.2) + - abseil/flags/flag (~> 1.20240116.2) + - abseil/flags/marshalling (~> 1.20240116.2) + - abseil/functional/any_invocable (~> 1.20240116.2) + - abseil/functional/bind_front (~> 1.20240116.2) + - abseil/functional/function_ref (~> 1.20240116.2) + - abseil/hash/hash (~> 1.20240116.2) + - abseil/memory/memory (~> 1.20240116.2) + - abseil/meta/type_traits (~> 1.20240116.2) + - abseil/random/bit_gen_ref (~> 1.20240116.2) + - abseil/random/distributions (~> 1.20240116.2) + - abseil/random/random (~> 1.20240116.2) + - abseil/status/status (~> 1.20240116.2) + - abseil/status/statusor (~> 1.20240116.2) + - abseil/strings/cord (~> 1.20240116.2) + - abseil/strings/str_format (~> 1.20240116.2) + - abseil/strings/strings (~> 1.20240116.2) + - abseil/synchronization/synchronization (~> 1.20240116.2) + - abseil/time/time (~> 1.20240116.2) + - abseil/types/optional (~> 1.20240116.2) + - abseil/types/span (~> 1.20240116.2) + - abseil/types/variant (~> 1.20240116.2) + - abseil/utility/utility (~> 1.20240116.2) + - BoringSSL-GRPC (= 0.0.32) + - gRPC-Core/Interface (= 1.62.5) + - gRPC-Core/Privacy (= 1.62.5) + - gRPC-Core/Interface (1.62.5) + - gRPC-Core/Privacy (1.62.5) + - GTMAppAuth (4.1.1): + - AppAuth/Core (~> 1.7) + - GTMSessionFetcher/Core (< 4.0, >= 3.3) + - GTMSessionFetcher/Core (3.4.1) + - GTMSessionFetcher/Full (3.4.1): + - GTMSessionFetcher/Core + - leveldb-library (1.22.5) + - nanopb (2.30910.0): + - nanopb/decode (= 2.30910.0) + - nanopb/encode (= 2.30910.0) + - nanopb/decode (2.30910.0) + - nanopb/encode (2.30910.0) + - PromisesObjC (2.4.0) + - RecaptchaInterop (100.0.0) + - Starscream (3.0.6) + - TensorFlowLiteC (0.0.1-nightly.20240615): + - TensorFlowLiteC/Core (= 0.0.1-nightly.20240615) + - TensorFlowLiteC/Core (0.0.1-nightly.20240615) + - TensorFlowLiteC/CoreML (0.0.1-nightly.20240615): + - TensorFlowLiteC/Core + - TensorFlowLiteC/Metal (0.0.1-nightly.20240615): + - TensorFlowLiteC/Core + - TensorFlowLiteSwift/Core (0.0.1-nightly.20240615): + - TensorFlowLiteC (= 0.0.1-nightly.20240615) + - TensorFlowLiteSwift/Privacy (= 0.0.1-nightly.20240615) + - TensorFlowLiteSwift/CoreML (0.0.1-nightly.20240615): + - TensorFlowLiteC/CoreML (= 0.0.1-nightly.20240615) + - TensorFlowLiteSwift/Core (= 0.0.1-nightly.20240615) + - TensorFlowLiteSwift/Privacy (= 0.0.1-nightly.20240615) + - TensorFlowLiteSwift/Metal (0.0.1-nightly.20240615): + - TensorFlowLiteC/Metal (= 0.0.1-nightly.20240615) + - TensorFlowLiteSwift/Core (= 0.0.1-nightly.20240615) + - TensorFlowLiteSwift/Privacy (= 0.0.1-nightly.20240615) + - TensorFlowLiteSwift/Privacy (0.0.1-nightly.20240615) + - ZIPFoundation (0.9.19) + +DEPENDENCIES: + - DropDown + - Firebase/Auth + - Firebase/Core + - Firebase/Storage + - FirebaseFirestore + - GoogleAPIClientForREST/Drive + - GoogleSignIn + - GoogleSignInSwiftSupport + - GoogleWebRTC + - Starscream (~> 3.0.1) + - TensorFlowLiteSwift/CoreML (~> 0.0.1-nightly) + - TensorFlowLiteSwift/Metal (~> 0.0.1-nightly) + - ZIPFoundation (~> 0.9) + +SPEC REPOS: + trunk: + - abseil + - AppAuth + - BoringSSL-GRPC + - DropDown + - Firebase + - FirebaseAnalytics + - FirebaseAppCheckInterop + - FirebaseAuth + - FirebaseAuthInterop + - FirebaseCore + - FirebaseCoreExtension + - FirebaseCoreInternal + - FirebaseFirestore + - FirebaseFirestoreInternal + - FirebaseInstallations + - FirebaseSharedSwift + - FirebaseStorage + - GoogleAPIClientForREST + - GoogleAppMeasurement + - GoogleSignIn + - GoogleSignInSwiftSupport + - GoogleUtilities + - GoogleWebRTC + - "gRPC-C++" + - gRPC-Core + - GTMAppAuth + - GTMSessionFetcher + - leveldb-library + - nanopb + - PromisesObjC + - RecaptchaInterop + - Starscream + - TensorFlowLiteC + - TensorFlowLiteSwift + - ZIPFoundation + +SPEC CHECKSUMS: + abseil: d121da9ef7e2ff4cab7666e76c5a3e0915ae08c3 + AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa + BoringSSL-GRPC: 1e2348957acdbcad360b80a264a90799984b2ba6 + DropDown: 8a2116376c1981888557f72ec2ffc9a5e0e456ec + Firebase: 26b040b20866a55f55eb3611b9fcf3ae64816b86 + FirebaseAnalytics: f9211b719db260cc91aebee8bb539cb367d0dfd1 + FirebaseAppCheckInterop: 0dd062c9926a76332ca5711dbed6f1a9ac540b54 + FirebaseAuth: 77a012b7e08042bf44d0db835ca2e86e6ca7bbd3 + FirebaseAuthInterop: 0344acef098654eaf59f6add4c93254349bc5de4 + FirebaseCore: a2b95ae4ce7c83ceecfbbbe3b6f1cddc7415a808 + FirebaseCoreExtension: 4ec89dd0c6de93d6becde32122d68b7c35f6bf5d + FirebaseCoreInternal: 4b297a2d56063dbea2c1d0d04222d44a8d058862 + FirebaseFirestore: 7169b75e7db8f9796d4130e3c2157ed444f100d4 + FirebaseFirestoreInternal: 7ba63f170a554ae49392da44f9430e5b7915a7ff + FirebaseInstallations: 766dabca09fd94aef922538aaf144cc4a6fb6869 + FirebaseSharedSwift: a03fe7a59ee646fef71099a887f774fe25d745b8 + FirebaseStorage: 255526c3d04c49874d7a5e3886964a79f77d6f33 + GoogleAPIClientForREST: 82fc3df6bbb59b226ac0bf6b638d38b31004f07e + GoogleAppMeasurement: f65fc137531af9ad647f1c0a42f3b6a4d3a98049 + GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db + GoogleSignInSwiftSupport: bbbd5afe90c9e22e91d2fa5a55498e00e3227cef + GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 + GoogleWebRTC: 4d261187b30512efdb711d21fa961a6adf138717 + "gRPC-C++": e725ef63c4475d7cdb7e2cf16eb0fde84bd9ee51 + gRPC-Core: eee4be35df218649fe66d721a05a7f27a28f069b + GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de + GTMSessionFetcher: 8000756fc1c19d2e5697b90311f7832d2e33f6cd + leveldb-library: e8eadf9008a61f9e1dde3978c086d2b6d9b9dc28 + nanopb: 438bc412db1928dac798aa6fd75726007be04262 + PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 + RecaptchaInterop: 7d1a4a01a6b2cb1610a47ef3f85f0c411434cb21 + Starscream: ef3ece99d765eeccb67de105bfa143f929026cf5 + TensorFlowLiteC: 4dc256afab23aad84320ffb0d23c1d09c86406d5 + TensorFlowLiteSwift: 67694795ccaff670f210f7905ab5b1fb48de17ab + ZIPFoundation: b8c29ea7ae353b309bc810586181fd073cb3312c + +PODFILE CHECKSUM: 65b4210950c2f88ab79eef61076af6b64791f78f + +COCOAPODS: 1.15.2 diff --git a/open-code/.gitignore b/open-code/.gitignore index 1a9ef355b..8a3405fe1 100644 --- a/open-code/.gitignore +++ b/open-code/.gitignore @@ -13,7 +13,6 @@ # misc .DS_Store -.env .env.local .env.development.local .env.test.local @@ -24,3 +23,4 @@ npm-debug.log* yarn-debug.log* yarn-error.log* ./node_modules +.env \ No newline at end of file diff --git a/open-code/package.json b/open-code/package.json index 82b26940e..9137b2c43 100644 --- a/open-code/package.json +++ b/open-code/package.json @@ -33,10 +33,12 @@ "react-dev-utils": "^12.0.1", "react-dom": "^18.2.0", "react-firebase-hooks": "^5.1.1", + "react-markdown": "^9.0.1", "react-responsive-carousel": "^3.2.23", "react-router-dom": "^6.15.0", "react-scripts": "^5.0.1", - "react-toastify": "^9.1.3" + "react-toastify": "^9.1.3", + "remark-gfm": "^4.0.0" }, "devDependencies": { "source-map-loader": "^4.0.1" diff --git a/open-code/src/App.js b/open-code/src/App.js index 7886971db..c97635d23 100644 --- a/open-code/src/App.js +++ b/open-code/src/App.js @@ -8,7 +8,6 @@ import {ToastContainer} from "react-toastify"; export const ThemeContext = createContext(null); - /** * Main function where the application Code is started * @returns {JSX.Element} diff --git a/open-code/src/assets/images/icon/aiSupport.png b/open-code/src/assets/images/icon/aiSupport.png new file mode 100644 index 000000000..16ecff65c Binary files /dev/null and b/open-code/src/assets/images/icon/aiSupport.png differ diff --git a/open-code/src/assets/images/icon/aiSupportWhite.png b/open-code/src/assets/images/icon/aiSupportWhite.png new file mode 100644 index 000000000..06008a1df Binary files /dev/null and b/open-code/src/assets/images/icon/aiSupportWhite.png differ diff --git a/open-code/src/assets/images/icon/chatIcon.png b/open-code/src/assets/images/icon/chatIcon.png new file mode 100644 index 000000000..c871d5768 Binary files /dev/null and b/open-code/src/assets/images/icon/chatIcon.png differ diff --git a/open-code/src/assets/images/icon/darkChatIcon.png b/open-code/src/assets/images/icon/darkChatIcon.png new file mode 100644 index 000000000..010484e85 Binary files /dev/null and b/open-code/src/assets/images/icon/darkChatIcon.png differ diff --git a/open-code/src/assets/images/icon/pause.png b/open-code/src/assets/images/icon/pause.png new file mode 100644 index 000000000..5e96c0c53 Binary files /dev/null and b/open-code/src/assets/images/icon/pause.png differ diff --git a/open-code/src/assets/images/sendIcon.png b/open-code/src/assets/images/sendIcon.png new file mode 100644 index 000000000..9c6bcbb04 Binary files /dev/null and b/open-code/src/assets/images/sendIcon.png differ diff --git a/open-code/src/components/blockly/BlocklyComponent.jsx b/open-code/src/components/blockly/BlocklyComponent.jsx index a05cc7bfc..0f33a06c4 100644 --- a/open-code/src/components/blockly/BlocklyComponent.jsx +++ b/open-code/src/components/blockly/BlocklyComponent.jsx @@ -13,7 +13,6 @@ import useMediaQuery from "@mui/material/useMediaQuery"; import {checkFileExistsInFolder, getFolderId, getShareableLink} from "../../services/googleDrive"; import {RightDrawer} from "../drawer/drawer"; import {useLocation} from "react-router-dom"; - Blockly.setLocale(locale); /** @@ -241,6 +240,7 @@ function BlocklyComponent(props) { } }, [theme, toolbox, blocklyDiv, props, isAutoSyncEnabled]); + // Return the blockly div and hidden toolbox return ( diff --git a/open-code/src/components/blockly/blocks/customblocks.js b/open-code/src/components/blockly/blocks/customblocks.js index 8eb1aded2..c906a0dd6 100644 --- a/open-code/src/components/blockly/blocks/customblocks.js +++ b/open-code/src/components/blockly/blocks/customblocks.js @@ -878,7 +878,7 @@ Blockly.Blocks["multipleObjectTracking"] = { }; //Blockly json structure for multiple object detection -Blockly.Blocks["variableDetection"] = { +Blockly.Blocks["objectDetection"] = { init: function () { this.jsonInit({ "type": "block_type", diff --git a/open-code/src/components/blockly/generator/javascriptGenerator.js b/open-code/src/components/blockly/generator/javascriptGenerator.js index cab51fffc..356aa9a3c 100644 --- a/open-code/src/components/blockly/generator/javascriptGenerator.js +++ b/open-code/src/components/blockly/generator/javascriptGenerator.js @@ -324,7 +324,7 @@ javascriptGenerator.forBlock['multipleObjectTracking'] = function (block) { }; //Javascript generated function for AI on frame detection -javascriptGenerator.forBlock['variableDetection'] = function (block, generator) { +javascriptGenerator.forBlock['objectDetection'] = function (block, generator) { let labels = block.getFieldValue('labels'); let models = block.getFieldValue('models'); let detect_tasks = javascriptGenerator.statementToCode(block, 'detect_tasks'); diff --git a/open-code/src/components/blockly/generator/pythonGenerator.js b/open-code/src/components/blockly/generator/pythonGenerator.js index 0693555c7..e226e2d9f 100644 --- a/open-code/src/components/blockly/generator/pythonGenerator.js +++ b/open-code/src/components/blockly/generator/pythonGenerator.js @@ -278,7 +278,7 @@ pythonGenerator.forBlock['multipleObjectTracking'] = function (block) { return code; }; -pythonGenerator.forBlock['variableDetection'] = function (block, generator) { +pythonGenerator.forBlock['objectDetection'] = function (block, generator) { let labels = block.getFieldValue('labels'); let models = block.getFieldValue('models'); let detect_tasks = pythonGenerator.statementToCode(block, 'detect_tasks'); diff --git a/open-code/src/components/blockly/imageConverter/index.js b/open-code/src/components/blockly/imageConverter/index.js new file mode 100644 index 000000000..dcbd747a0 --- /dev/null +++ b/open-code/src/components/blockly/imageConverter/index.js @@ -0,0 +1,57 @@ +import Blockly from "blockly/core"; + +/** + * addBlocksToWorkspace function extracts XML data from a given message and adds it to a Blockly workspace. + * @param message + * @param workspace + * @returns {Promise} + */ +export const addBlocksToWorkspace = async (message, workspace) => { + let xmlData = null; + + try { + const parsedMessage = JSON.parse(message); + xmlData = parsedMessage.$$RESPONSE$$ || null; + } catch (jsonParseError) { + console.warn("Message is not valid JSON, falling back to regex extraction"); + } + + // If no XML was extracted from the JSON, use regex to find the XML + if (!xmlData) { + const regex = /$$RESPONSE$$":"([\s\S]*?<\/xml>)"/; + const match = message.match(regex); + if (match) { + xmlData = match[1].replace(/\\"/g, '"'); + } + } + + if (xmlData) { + try { + workspace.clear(); + const xmlDom = Blockly.utils.xml.textToDom(xmlData); + Blockly.Xml.domToWorkspace(xmlDom, workspace); + } catch (error) { + console.error("Error parsing XML or adding to workspace:", error); + } + } else { + console.log("No Blockly XML code found in the message"); + } +}; + +// export const addBlocksToWorkspace = async (message, workspace) => { +// const regex = /[\s\S]*?<\/xml>/; +// const match = message.match(regex); +// if (match) { +// try { +// // workspace.clear() +// workspace.clear() +// const xmlDom = Blockly.utils.xml.textToDom(match[0]); +// Blockly.Xml.domToWorkspace(xmlDom, workspace); +// } catch (error) { +// console.error("Error parsing XML or adding to workspace:", error); +// } +// } else { +// console.log('No match found'); +// } +// +// }; diff --git a/open-code/src/components/blockly/toolbox/Toolbox.js b/open-code/src/components/blockly/toolbox/Toolbox.js index d76d6f035..4db087b8e 100644 --- a/open-code/src/components/blockly/toolbox/Toolbox.js +++ b/open-code/src/components/blockly/toolbox/Toolbox.js @@ -139,7 +139,7 @@ export const Toolbox = (props) => { - + diff --git a/open-code/src/components/bottomBar/bottomBar.js b/open-code/src/components/bottomBar/bottomBar.js index 88afd7656..76159b7e7 100644 --- a/open-code/src/components/bottomBar/bottomBar.js +++ b/open-code/src/components/bottomBar/bottomBar.js @@ -13,14 +13,12 @@ import {Images} from "../../utils/images"; import useMediaQuery from "@mui/material/useMediaQuery"; import {uploadToGoogleDrive} from "../../services/googleDrive"; import { - getCurrentProject, - handleChildBlockInWorkspace + getCurrentProject, handleChildBlockInWorkspace } from "../../services/workspace"; import navbarStyle from "../navBar/navbar.module.css"; import BlueText from "../fonts/blueText"; import {ModelUploadingComponent} from "./modelUploadingComponent"; import SubscriptionModel from "../subscription/subscriptionModel"; -import {setProjectDetails} from "../../apis/projects"; /** * Bottom Bar contains generate code, upload on drive icon , zoom in-out and undo redo functionality. @@ -41,14 +39,7 @@ export const BottomBar = () => { const [error, setError] = useState(""); const [isSubscriptionExpire, setIsSubscriptionExpire] = useState(null); const { - isOnline, - generate, - setGenerateCode, - setCode, - setDrawer, - workspace, - isError, - setIsError, setCategory, + isOnline, generate, setGenerateCode, setCode, setDrawer, workspace, isError, setIsError, setCategory, } = useContext(StoreContext); //handling error states on playground @@ -99,18 +90,16 @@ export const BottomBar = () => { setDrawer(false); setIsLoader(true); //javaScript generator - let code = javascriptGenerator.workspaceToCode( - workspace - ); + let code = javascriptGenerator.workspaceToCode(workspace); const start = workspace.getBlocksByType(PlaygroundConstants.start); const forever = workspace.getBlocksByType(PlaygroundConstants.forever); const detection = workspace.getBlocksByType(PlaygroundConstants.detectionOrUndetection); const multipleObjectTracking = workspace.getBlocksByType(PlaygroundConstants.multipleObjectTracking); - const variableDetection = workspace.getBlocksByType(PlaygroundConstants.variableDetection); + const objectDetection = workspace.getBlocksByType(PlaygroundConstants.objectDetection); let objNameArray = []; - if (variableDetection?.length > 0) { - for (let i = 0; i < variableDetection.length; i++) { - objNameArray.push(variableDetection[i].getFieldValue(PlaygroundConstants.labels)); + if (objectDetection?.length > 0) { + for (let i = 0; i < objectDetection.length; i++) { + objNameArray.push(objectDetection[i].getFieldValue(PlaygroundConstants.labels)); } } let isClassesSimiliar = false; @@ -138,7 +127,7 @@ export const BottomBar = () => { object_2 = multipleObjectTrackingEnabledBlocks[0].getFieldValue(PlaygroundConstants.labels2) } - if (start.length === 0 && forever.length === 0 && detection.length === 0 && variableDetection.length === 0) { + if (start.length === 0 && forever.length === 0 && detection.length === 0 && objectDetection.length === 0) { handleError(Errors.error1); } else if (isAIBlocksAdjacent === true && start.length > 0) { handleError(Errors.error2); @@ -152,36 +141,32 @@ export const BottomBar = () => { // Replace comments with an empty string let codeWithoutComments = code.replace(/\/\/.*$/gm, ''); - if (start.length > 0) - codeWithoutComments += "\nstart();"; - if (forever.length > 0) - codeWithoutComments += "\nforever();"; + if (start.length > 0) codeWithoutComments += "\nstart();"; + if (forever.length > 0) codeWithoutComments += "\nforever();"; setGenerateCode(!generate); console.log(codeWithoutComments); uploadToGoogleDrive(codeWithoutComments, "js").then((res) => { - let linkCode = { - driveLink: res, - projectName: getCurrentProject().projectName - } - const data = { - projectName: getCurrentProject().projectName, - xmlValue: getCurrentProject().xmlValue, - createdDate: new Date().toLocaleDateString() // Todo on create button add newly created date and time - } - uploadToGoogleDrive(data, "xml") // Call function to upload xml data to Google Drive - .then(async () => { - setCode(linkCode); - setCategory(Constants.qr); - setIsLoader(false); - setDrawer(true); - }) - .catch((err) => { - errorToast("Failed to upload"); - console.log(err); - setIsLoader(false); - }) + let linkCode = { + driveLink: res, projectName: getCurrentProject().projectName } - ).catch((err) => { + const data = { + projectName: getCurrentProject().projectName, + xmlValue: getCurrentProject().xmlValue, + createdDate: new Date().toLocaleDateString() // Todo on create button add newly created date and time + } + uploadToGoogleDrive(data, "xml") // Call function to upload xml data to Google Drive + .then(async () => { + setCode(linkCode); + setCategory(Constants.qr); + setIsLoader(false); + setDrawer(true); + }) + .catch((err) => { + errorToast("Failed to upload"); + console.log(err); + setIsLoader(false); + }) + }).catch((err) => { console.log("err::", err) setIsLoader(false); errorToast("Failed to Upload"); @@ -202,9 +187,7 @@ export const BottomBar = () => { useEffect(() => { const handleOrientationChange = () => { - setIsLandscape( - window.matchMedia("(max-height: 500px) and (max-width: 1000px) and (orientation: landscape)").matches - ); + setIsLandscape(window.matchMedia("(max-height: 500px) and (max-width: 1000px) and (orientation: landscape)").matches); }; window.addEventListener("resize", handleOrientationChange); }, []); @@ -224,8 +207,7 @@ export const BottomBar = () => { const isUndo = () => { const workspace = Blockly.getMainWorkspace(); if (workspace) { - return workspace.undoStack_.length !== 0 || - (workspace.undoStack_.length === 0 && workspace.redoStack_.length === 0); + return workspace.undoStack_.length !== 0 || (workspace.undoStack_.length === 0 && workspace.redoStack_.length === 0); } return false; }; @@ -308,8 +290,7 @@ export const BottomBar = () => { } - return ( - <> + return (<> {isSubscriptionExpire && }
{ className={styles.errorDiv}>
Compilation failed due to following error(s).
error :    {error}
-
- } - {isLoader && -
- - {theme === "dark" ? - : - } -
- } + } + {isLoader &&
+ + {theme === "dark" ? : + } +
}
{/*generate code*/} @@ -336,12 +313,10 @@ export const BottomBar = () => { buttonActive={buttonActive} clickedButton={clickedButton} setIsAIModelComponent={setIsAIModelComponent} setFile={setFile}/> {/*model upload pop up */} - { - isAIModelComponent && - - } + {isAIModelComponent && }
{/*undo redo*/} @@ -351,12 +326,10 @@ export const BottomBar = () => { {isMobile || isLandscape || isDesktopSmallerScreen ? "" : } -
- - ); + ); } /** @@ -402,12 +375,8 @@ function UploadCodeButton(params) { useEffect(() => { const handleOrientationChange = () => { - setIsLandscape( - window.matchMedia("(max-height: 500px) and (max-width: 1000px) and (orientation: landscape)").matches - ); - setIsTabletQuery( - window.matchMedia("(min-width: 768px) and (max-width: 1024px)").matches - ); + setIsLandscape(window.matchMedia("(max-height: 500px) and (max-width: 1000px) and (orientation: landscape)").matches); + setIsTabletQuery(window.matchMedia("(min-width: 768px) and (max-width: 1024px)").matches); }; window.addEventListener("resize", handleOrientationChange); }, []); @@ -431,18 +400,14 @@ function UploadCodeButton(params) { }; }, [popUpRef, arrowClick]); - return ( -
+ return (
{/*generate QR code*/} @@ -461,20 +426,16 @@ function UploadCodeButton(params) { > Icon - {theme.theme === "dark" ? - : - - } + {theme.theme === "dark" ? : + }
handleLanguageDropDown(Constants.py, event)} className={`${styles.langItem} ${styles.pyDivMargin} ${(theme.theme === "dark" ? navbarStyle.darkItem : navbarStyle.lightItem)}`} > Icon - {theme.theme === "dark" ? - : - - } + {theme.theme === "dark" ? : + }
inputRef.current?.click()} style={{marginTop: 0}} @@ -482,19 +443,17 @@ function UploadCodeButton(params) { > Icon - {theme.theme === "dark" ? - : - - } + {theme.theme === "dark" ? : } +
+
- - ) + ) } @@ -503,8 +462,7 @@ function UploadCodeButton(params) { */ function UndoRedo(params) { const {clickedButton, buttonSelected, buttonActive} = params - return ( -
+ return (
-
- ) +
) } @@ -530,8 +487,7 @@ function UndoRedo(params) { */ function ZoomInOut(params) { const {clickedButton, buttonSelected, buttonActive} = params; - return ( -
+ return (
-
- ); +
); } diff --git a/open-code/src/components/chatBox/markDown.module.css b/open-code/src/components/chatBox/markDown.module.css new file mode 100644 index 000000000..4e0492948 --- /dev/null +++ b/open-code/src/components/chatBox/markDown.module.css @@ -0,0 +1,141 @@ +/* Light Theme */ +.heading1 { + font-size: 18px; + margin-bottom: 0.5em; + color: #0071c5; +} + +.heading2 { + font-size: 15px; + margin-bottom: 0.5em; + color:#0071c5; +} +.heading3 { + font-size: 16px; + margin-bottom: 0.5em; + color:#0071c5; +} + +.paragraph { + margin-bottom: 0.5em; + font-size: 15px; +} + +.strong { + font-weight: bold; + color: #000000; +} + +.em { + font-style: italic; + color: #555555; +} + +.list { + padding-left: 20px; + color: #000000; + margin-bottom: 0.5em; +} + +.orderedList { + margin-bottom: 0.5em; + padding-left: 20px; + list-style-type: decimal; + color:#0071c5; +} + +.listItem { + margin-bottom: 0.5em; + color: #000000; + +} + +.blockquote { + margin: 1em 0; + padding-left: 1em; + border-left: 4px solid #CCCCCC; + background-color: #F5F5F5; + color: #666666; +} + +/* Light Theme */ +.code { + font-family: monospace; + background-color: #F5F5F5; + padding: 2px 4px; + border-radius: 3px; + color: #d56235; + font-size: 14px; +} + +/* Dark Theme */ +.darkTheme .heading1 { + font-size: 18px; + margin-bottom: 0.5em; + color: #458ff7; + +} + +.darkTheme .heading2 { + font-size: 16px; + margin-bottom: 0.5em; + color:#458ff7; +} + +.darkTheme.heading3 { + font-size: 16px; + margin-bottom: 0.5em; + color: #458ff7; +} + +.darkTheme .paragraph { + margin-bottom: 1em; + color: #FFFFFF; + font-size: 15px; +} + +.darkTheme .strong { + font-weight: bold; + color: #FFFFFF; +} + +.darkTheme .em { + font-style: italic; + color: #1d6c01; +} + +.darkTheme .list { + padding-left: 20px; + color: #FFFFFF; + margin-bottom: 0.5em; +} + +.darkTheme .orderedList { + padding-left: 20px; + list-style-type: decimal; + color: #FFFFFF; + margin-bottom: 0.5em; +} + +.darkTheme .listItem { + margin-bottom: 0.5em; + color: #FFFFFF; + +} + +.darkTheme .blockquote { + margin: 1em 0; + padding-left: 1em; + border-left: 4px solid #CCCCCC; + background-color: #F5F5F5; + color: #666666; +} + +.darkTheme .code { + font-family: monospace; + padding: 2px 4px; + border-radius: 3px; + color: #d56235; + background-color: transparent; + font-size: 14px; +} diff --git a/open-code/src/components/chatBox/messageBox.module.css b/open-code/src/components/chatBox/messageBox.module.css new file mode 100644 index 000000000..e0253b631 --- /dev/null +++ b/open-code/src/components/chatBox/messageBox.module.css @@ -0,0 +1,100 @@ +.responseBox { + padding: 15px; + border-radius: 10px 10px 10px 0; + overflow-x:auto; /* Enable horizontal scrolling */ + +} +.responseContent { + display: flex; + flex-direction: column; + overflow-x:auto; +} +.responseContent.timestamp { + text-align: right; + font-size: 12px; + color: #666; +} + +.chatBubble { + display: flex; + flex-direction: column; + gap: 28px; + padding: 28px 28px 0 28px; +} + +.userMessage { + align-self: flex-end; + background-color: #0071C5; + border-radius: 10px 10px 0 10px; + padding: 15px; + max-width: 70%; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + display: flex; + flex-direction: column; +} +.userTimestamp { + display: flex; + justify-content: flex-end; + font-size: 12px; + color: #FFFFFF; +} + +.loader { + width: 50px; + aspect-ratio: 2; + --_g: no-repeat radial-gradient(circle closest-side,#000 90%,#0000); + background: + var(--_g) 0% 50%, + var(--_g) 50% 50%, + var(--_g) 100% 50%; + background-size: calc(100%/3) 50%; + animation: l3 1s infinite linear; +} +@keyframes l3 { + 20%{background-position:0% 0%, 50% 50%,100% 50%} + 40%{background-position:0% 100%, 50% 0%,100% 50%} + 60%{background-position:0% 50%, 50% 100%,100% 0%} + 80%{background-position:0% 50%, 50% 50%,100% 100%} +} +.whiteLoader { + width: 50px; + aspect-ratio: 2; + --_g: no-repeat radial-gradient(circle closest-side, #FFFFFF 90%, #FFFFFF00); + background: + var(--_g) 0% 50%, + var(--_g) 50% 50%, + var(--_g) 100% 50%; + background-size: calc(100%/3) 50%; + animation: l3 1s infinite linear; +} +.loaderContainer { + display: flex; + justify-content: center; + align-items: center; + height: 100%; +} +.loaderHeading{ + display: flex; + align-items: center; + font-size: 16px; + color: black; + margin: 0; +} +.loaderHeadingDark{ + display: flex; + align-items: center; + font-size: 16px; + color: white; + margin: 0; +} +.timestamp { + font-size: 12px; + color: #666; + text-align: right; +} + +.responseButton { + padding: 8px 8px; + border-radius: 9px; + cursor: pointer; +} diff --git a/open-code/src/components/chatBox/messagebox.js b/open-code/src/components/chatBox/messagebox.js new file mode 100644 index 000000000..89c0cdfbc --- /dev/null +++ b/open-code/src/components/chatBox/messagebox.js @@ -0,0 +1,193 @@ +import React, {useEffect, useState, useContext} from 'react'; +import styles from './messageBox.module.css'; +import mstyles from './markDown.module.css'; +import {ThemeContext} from '../../App'; +import {ChatConstants, Themes} from '../../utils/constants'; +import {colors as Colors} from '../../utils/color'; +import ReactMarkdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; +import PersonaCard from "../personaCard/peronaCard"; + +/** + * ChatBox component renders a chat bubble containing user and assistant messages. + * @param props + * @returns {Element} + * @constructor + */ +const ChatBox = (props) => { + const { + conversation, + handlePauseClick, + setIsTyping, + setLoader, + loader, + allChatMessages, + chatContainerRef, + setCodeBufferLoader, + codeBufferLoader, + setPersona, + } = props; + const theme = useContext(ThemeContext); + return ( +
+ {conversation.userMessage && ( + + )} + handlePauseClick(conversation.id)} + setIsTyping={setIsTyping} + allChatMessages={allChatMessages} + id={conversation.id} + setCodeBufferLoader={setCodeBufferLoader} + codeBufferLoader={codeBufferLoader} + setLoader={setLoader} + loader={loader} + chatContainerRef={chatContainerRef} + setPersona={setPersona} + /> +
+ ); +}; + +/** + * UserMessage component renders a user message with timestamp. + * @param timestamp + * @param message + * @returns {React.JSX.Element} + * @constructor + */ +const UserMessage = ({timestamp, message}) => ( +
+
{message}
+
{timestamp}
+
+); + +/** + * AssistantResponse component renders assistant's response with typewriter effect. + * Manages display of response content, loader, and scrolling behavior. + * @returns {Element} + * @constructor + * @param props + */ +const AssistantResponse = (props) => { + const { + timestamp, + message, + paused, + setIsTyping, + setLoader, + loader, + codeBufferLoader, + allChatMessages, + id, + chatContainerRef, + setPersona + } = props; + const [displayedMessage, setDisplayedMessage] = useState(''); + const theme = useContext(ThemeContext); + //Effect hook to manage loading state and update the displayed message when dependencies change. + useEffect(() => { + setLoader(message === ''); + + if (message !== '') { + setDisplayedMessage(message); + } + + }, [message, paused, allChatMessages.length, id, setIsTyping, setLoader]); + + //useEffect for auto scrolling the content + useEffect(() => { + if (chatContainerRef.current && chatContainerRef.current.scrollHeight !== null) { + chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight; + } + }, [displayedMessage, chatContainerRef, loader, allChatMessages.length]); + + function handlePersonas(e) { + setPersona(e); + } + + return ( +
+ {loader && allChatMessages.length === id ? ( +
+ ) : ( +
+ +

, + h2: ({node, ...props}) =>

, + h3: ({node, ...props}) =>

, + p: ({node, ...props}) =>

, + strong: ({node, ...props}) => , + em: ({node, ...props}) => , + ul: ({node, ...props}) =>

    , + ol: ({node, ...props}) =>
      , + li: ({node, ...props}) =>
    1. , + blockquote: ({node, ...props}) =>
      , + code: ({node, ...props}) => , + }} + > + {displayedMessage} + + {id === 2 && } + {codeBufferLoader && allChatMessages.length === id && ( +
      Generating Code + + +
      + + )} +
      {timestamp}
      +

+ )} +
+ ); +}; + +export default ChatBox; diff --git a/open-code/src/components/codeAssistant/chat.js b/open-code/src/components/codeAssistant/chat.js new file mode 100644 index 000000000..378193ba3 --- /dev/null +++ b/open-code/src/components/codeAssistant/chat.js @@ -0,0 +1,323 @@ +import React, {useContext, useEffect, useRef, useState} from 'react'; +import styles from "./chat.module.css"; +import {getAIMessage} from "../../services/chatAssistant"; +import {Images} from "../../utils/images.js"; +import {Themes, Errors, ChatConstants} from '../../utils/constants.js'; +import {ThemeContext} from "../../App"; +import {colors as Colors} from "../../utils/color"; +import {StoreContext} from "../../context/context"; +import {addBlocksToWorkspace} from "../blockly/imageConverter"; +import {getCurrentProject} from "../../services/workspace"; +import {handler} from "../../utils/handler"; +import ChatBox from "../chatBox/messagebox"; + +/** + * Chat component handles user interactions and displays chat interface. + * @param props + * @returns {Element} + * @constructor + */ +const Chat = ({drawer}) => { + const theme = useContext(ThemeContext); + const {workspace, setDrawer} = useContext(StoreContext); + const [inputValue, setInputValue] = useState(''); + const [loader, setLoader] = useState(false); + const [isTyping, setIsTyping] = useState(false); + const timestamp = new Date().toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'}); + const abortControllerRef = useRef(null); + const chatContainerRef = useRef(null); + const [codeBufferLoader, setCodeBufferLoader] = useState(false); + const [allChatMessages, setAllChatMessages] = useState([{ + id: 1, + userMessage: "", + AIMessage: ChatConstants.Message, // Initial welcome message + userTimestamp: "", + AITimestamp: new Date().toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'}), + paused: false + }, { + id: 2, + userMessage: "", // Message for choosing persona + AIMessage: " ", + userTimestamp: "", + AITimestamp: new Date().toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'}), + paused: false + }]); + const [currentMessage, setCurrentMessage] = useState({ + id: 3, // Starting with id 3 since id 2 is reserved for persona selection + userMessage: "", + AIMessage: "", + userTimestamp: new Date().toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'}), + AITimestamp: "", + paused: false + }); + + const [persona, setPersona] = useState(""); + //Handles user click on send button + const handleSendClick = async () => { + const userInput = inputValue.trim().toLowerCase(); + if (userInput === '') { + return; + } + setIsTyping(true); + setCurrentMessage((prevState) => ({ + ...prevState, + userMessage: userInput, + userTimestamp: timestamp, + AIMessage: "", + id: allChatMessages.length + 1, + AITimestamp: "", + paused: false + })); + abortControllerRef.current = new AbortController(); + let messageBuffer = ''; + + //Handles incoming message chunks from the api on streaming. + const onMessage = (chunk) => { + + // Append current chunk to the message buffer + messageBuffer += chunk; + + + if (chunk === "n") { + chunk = ""; + } + + const formattedChunk = chunk + .replace(/\\n/g, ' \n\n') // Markdown requires two spaces before a newline for a line break + .replace(/\\t/g, '\t') // Replace escaped tabs if needed + .replace(/\\"/g, '"') // Replace escaped double quotes + .replace(/\\\\/g, '\n') // Replace escaped backslashes + .replace(/\\/g, ' '); // Markdown requires two spaces before a newline for a line break + + if (messageBuffer.includes('$$RESPONSE$$')) { + setCodeBufferLoader(true); + } else { + setCodeBufferLoader(false); + } + + if (messageBuffer.includes('$$CONTENT$$":"') && chunk !== '":"') { + setIsTyping(true); + + if (messageBuffer.includes('","')) { + messageBuffer = ''; + setIsTyping(false); + } else { + setCurrentMessage((prevState) => ({ + ...prevState, AIMessage: prevState.AIMessage + formattedChunk, AITimestamp: timestamp, + })); + } + } + }; + + // To add the blocks to the current workspace + getAIMessage(userInput, persona, getCurrentProject().xmlValue, abortControllerRef.current.signal, onMessage).then((res) => { + if (res !== undefined) { + let finalMessage = handler(res); + if (finalMessage !== undefined) { + setCodeBufferLoader(false); + setCurrentMessage((prevState) => ({ + ...prevState, AIMessage: finalMessage, AITimestamp: timestamp, + })); + } else { + setCodeBufferLoader(false); + } + addBlocksToWorkspace(res, workspace) + .catch((e) => { + console.log("Error in creating block png-->", e); + setCurrentMessage((prevState) => ({ + ...prevState, AIMessage: finalMessage + "\n" + Errors.error8, AITimestamp: timestamp, + })); + }) + } else { + setCurrentMessage((prevState) => ({ + ...prevState, AIMessage: Errors.error7, AITimestamp: timestamp + })); + } + }).catch((e) => { + setCodeBufferLoader(false); + console.log(e); + }); + + setInputValue(''); + }; + + //function to pause the content + const handlePauseClick = () => { + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + setIsTyping(false); + if (loader && allChatMessages.length === currentMessage.id) { + setCurrentMessage((prevState) => ({ + ...prevState, AIMessage: Errors.error7, AITimestamp: timestamp + })); + } + }; + + //Effect to update allChatMessages when currentMessage changes + useEffect(() => { + if (currentMessage.AIMessage) { + setAllChatMessages((prevMessages) => { + const updatedMessages = [...prevMessages]; + if (updatedMessages.length > 0) { + updatedMessages[updatedMessages.length - 1] = currentMessage; + } + return updatedMessages; + }); + } else if (currentMessage.userMessage) { + setAllChatMessages((prevMessages) => [...prevMessages, currentMessage]); + } + }, [currentMessage]); + + // Function to apply typing effect to the latest message in allChatMessages + const applyTypingEffect = (fullText, typingSpeed = 20) => { + let currentIndex = 0; + + const typeCharacter = () => { + setAllChatMessages((prevMessages) => { + const updatedMessages = [...prevMessages]; + const latestMessage = updatedMessages[updatedMessages.length - 1]; + + // Update only the latest message + latestMessage.AIMessage = fullText.slice(0, currentIndex + 1); + updatedMessages[updatedMessages.length - 1] = latestMessage; + + return updatedMessages; + }); + + currentIndex++; + + if (currentIndex < fullText.length) { + setTimeout(typeCharacter, typingSpeed); + } + }; + + typeCharacter(); + }; + + //Effect for setting persona character message + useEffect(() => { + if (persona) { + setAllChatMessages((prevMessages) => [...prevMessages, { + id: prevMessages.length + 1, + userMessage: "", + AIMessage: "", + userTimestamp: new Date().toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'}), + AITimestamp: "", + paused: false + }]); + setTimeout(() => { + applyTypingEffect(ChatConstants.PersonaMessage); + }, 80); + } + }, [persona]); + + + // Effect to scroll chat container to bottom when messages or AI message changes + useEffect(() => { + if (chatContainerRef.current && chatContainerRef.current.scrollHeight !== null) { + + chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight; + + + } + }, [allChatMessages, currentMessage.AIMessage]); + + // Renders chat interface + return (
+ {drawer ?
+
+ Chat Assistant Logo +

{ChatConstants.Playground}

+ setDrawer(false)} alt={"cross icon"} className={styles.crossIcon} + src={theme === Themes.dark ? Images.darkCrossIcon : Images.lightCrossIcon}/> +
+
: ""} +
+ {allChatMessages.map((conversation, index) => ())} +
+ {drawer ? handlePauseClick(currentMessage.id)} + /> : ""} +
); +}; + +/** + * Component for rendering input field and send/pause button + * @param inputValue + * @param handleSendClick + * @param setInputValue + * @param isTyping + * @param handlePauseClick + * @param codeBufferLoader + * @returns {Element} + * @constructor + */ +const ChatBottomBar = ({inputValue, handleSendClick, setInputValue, isTyping, handlePauseClick, codeBufferLoader}) => { + const theme = useContext(ThemeContext); + + return (
+ setInputValue(e.target.value)} + onKeyPress={(e) => { + if (e.key === 'Enter') { + handleSendClick(); + } + }} + disabled={isTyping || codeBufferLoader} + /> +
+ {isTyping +
+
); +}; +// Exports Chat component as default +export default Chat; diff --git a/open-code/src/components/codeAssistant/chat.module.css b/open-code/src/components/codeAssistant/chat.module.css new file mode 100644 index 000000000..477df8f6b --- /dev/null +++ b/open-code/src/components/codeAssistant/chat.module.css @@ -0,0 +1,75 @@ +.chatMainContainer { + display: flex; + justify-content: space-between; + height: 100%; + flex-direction: column; + font-family: Gilroy-Medium, sans-serif; +} + +.chatBottomBar { + display: flex; + align-items: center; + gap: 10px; + padding: 10px; +} + +.inputField { + flex: 1; + border: none; + border-radius: 20px; + font-size: 16px; + padding: 16px; + outline: none; + + +} + +.sendButton { + margin-left: 10px; + padding: 10px 20px; + border: none; + border-radius: 10px; + background-color: #4CAF50; + color: #fff; + cursor: pointer; + display: flex; + align-items: center; /* Center align icon vertically */ +} + +.sendButton:hover { + background-color: #3e8e41; +} + +.sendIcon { + width: 30px; + height: 30px; + gap: 5px; + cursor: pointer; +} + +.chatHeader { + text-align: center; + font-family: Gilroy-Medium, sans-serif; + padding: 10px; + display: flex; + align-items: center; + gap: 10px; + font-size: 12px; +} + +.sendButton.disabled { + pointer-events: none; + opacity: 0.5; + cursor: not-allowed; +} + + +.crossIcon { + height: 18px; + width: 18px; + cursor: pointer; + display: flex; + margin-left: auto; + +} + diff --git a/open-code/src/components/drawer/drawer.js b/open-code/src/components/drawer/drawer.js index 1886ce7d0..37e1215cf 100644 --- a/open-code/src/components/drawer/drawer.js +++ b/open-code/src/components/drawer/drawer.js @@ -13,6 +13,7 @@ import QrCode from "../qrcode/qrcode"; import {Constants} from "../../utils/constants"; import CodeEditor from "../editor/codeEditor"; import {Themes} from "../../utils/constants" +import Chat from "../codeAssistant/chat"; /** * QrDrawer component renders a drawer with a QR code and some instructions on how to use it. @@ -21,7 +22,7 @@ import {Themes} from "../../utils/constants" */ export function RightDrawer() { const {theme} = useContext(ThemeContext) // Retrieve the current theme from the ThemeContext - const {drawer, code, category} = useContext(StoreContext) // Retrieve the drawer state from the StoreContext + const {drawer, code, category, currentAssistantXml, setCurrentAssistantXml} = useContext(StoreContext) // Retrieve the drawer state from the StoreContext const themes = useTheme();// Get the current theme breakpoints using useTheme hook const isMobile = useMediaQuery(themes.breakpoints.down("sm"));// Determine if the screen is a mobile device using useMediaQuery hook const [isLandscape, setIsLandscape] = useState(window.matchMedia("(max-height: 450px) and (max-width: 1000px) and (orientation: landscape)").matches); @@ -34,6 +35,7 @@ export function RightDrawer() { window.addEventListener("resize", handleOrientationChange); }, []); + return ( <> {(code || category !== Constants.qr) && @@ -44,12 +46,13 @@ export function RightDrawer() { width: 0, flexShrink: 0, '& .MuiDrawer-paper': { - width: drawer ? category !== Constants.qr ? isMobile ? isLandscape ? '35%' : '62%' : '40%' : isMobile ? isLandscape ? '32%' : '62%' : isLandscape ? '50%' : isTabletQuery ? '45%' : '25%' : isMobile ? isLandscape ? '3%' : '6%' : '2%', + width: drawer ? category === Constants.chat ? isMobile ? '85%' : isLandscape ? '60%' : '40%' : category !== Constants.qr ? isMobile ? isLandscape ? '35%' : '62%' : '40%' : isMobile ? isLandscape ? '32%' : '62%' : isLandscape ? '50%' : isTabletQuery ? '45%' : '25%' : isMobile ? isLandscape ? '3%' : '6%' : category === Constants.chat ? "1.7%" : '2%', borderLeft: drawer ? theme === Themes.dark ? "0.5px solid gray" : '1px solid rgba(0, 0, 0, 0.2)' : "0.0", backgroundColor: theme === Themes.dark ? colors.blackBackground : colors.whiteBackground, color: theme === Themes.dark ? colors.whiteFont : colors.blackFont, - top: isLandscape ? "4rem" : isTabletQuery ? "6rem" : "5rem", - bottom: isMobile ? "9%" : isLandscape ? "18%" : isTabletQuery ? "4.4rem" : "4.4rem", + top: isTabletQuery ? "6rem" : isLandscape ? "4rem" : "5rem", + bottom: category !== Constants.chat ? (isMobile ? "9%" : isLandscape ? "18%" : "4.4rem") : 'none', + height: category === Constants.chat ? isMobile ? "calc(100% - (9% + 5rem))" : isTabletQuery ? "calc(100% - (10.4rem))" : isLandscape ? "calc(100% - (8.4rem))" : "calc(100% - 9.4rem)" : 'none' }, }} // Drawer is always visible and can only be closed programmatically @@ -67,14 +70,29 @@ export function RightDrawer() { : -
- -
+ category === Constants.chat ? +
+
+ +
+ : +
+ +
} @@ -137,4 +155,3 @@ export const DrawerBody = (props) => { ) } - diff --git a/open-code/src/components/homeComponents/header/profileOptionModal.js b/open-code/src/components/homeComponents/header/profileOptionModal.js index 640298325..456ed4a8d 100644 --- a/open-code/src/components/homeComponents/header/profileOptionModal.js +++ b/open-code/src/components/homeComponents/header/profileOptionModal.js @@ -33,7 +33,7 @@ export function ProfileOptionModal(props) { const location = useLocation(); const themes = useTheme(); const {theme, toggleTheme} = useContext(ThemeContext); - const {isOnline, setIsDob, isDob, setIsAutoSyncEnabled, setUser} = useContext(StoreContext); + const {isOnline, setIsDob, isDob, setIsAutoSyncEnabled, setUser, setCategory, setDrawer} = useContext(StoreContext); const isMobile = useMediaQuery(themes.breakpoints.down('md')); const isSignedIn = localStorage.getItem("isSigIn") === "true"; const isHomePage = location.pathname === PathName.home; @@ -128,7 +128,14 @@ export function ProfileOptionModal(props) { modelStyle={{width: "14px", height: "18px"}} icon={theme === Themes.dark ? Images.darkSyncIcon : Images.lightSyncIcon}/> } - {((isHomePage) || (isOnPlaygroundPage && isSignedIn)) && + {(isOnPlaygroundPage && isMobile) && + { + handleClose(); + setCategory(Constants.chat); + setDrawer(true) + }} text={"AI Assistant"} + icon={theme === Themes.dark ? Images.chatIcon : Images.darkChatIcon}/>} + {((isHomePage) || (isOnPlaygroundPage)) && handleOnclick(setIsLogoutModal)} text={"Logout"} icon={theme === Themes.dark ? Images.darkLogoutIcon : Images.logoutIcon}/> } diff --git a/open-code/src/components/navBar/header.js b/open-code/src/components/navBar/header.js index ca0fb9712..bfb1ca705 100644 --- a/open-code/src/components/navBar/header.js +++ b/open-code/src/components/navBar/header.js @@ -34,7 +34,7 @@ export function Header() { isOnline, isSessionExpireModal, setIsSessionExpireModal, - setTimeoutId + setTimeoutId, } = useContext(StoreContext); const [anchorEl, setAnchorEl] = useState(null); const [deleteProject, setDeleteProject] = useState(false); @@ -102,98 +102,82 @@ export function Header() { //Loader while getting date of birth function SimpleBackdrop() { - return ( -
- theme.zIndex.drawer + 1}} - open={editProfileLoaderOpen} - > - - -
- ); + return (
+ theme.zIndex.drawer + 1}} + open={editProfileLoaderOpen} + > + + +
); } useEffect(() => { auth.onAuthStateChanged(function (currentUser) { setUser({ - photoURL: currentUser?.photoURL, - displayName: currentUser?.displayName, - email: currentUser?.email, + photoURL: currentUser?.photoURL, displayName: currentUser?.displayName, email: currentUser?.email, }); }) }, [isEditProfileModal, setUser]) - return ( -
- {/*delete project modal*/} - {deleteProject && - - } - -
- {/*logo*/} - + return (
+ {/*delete project modal*/} + {deleteProject && } - {/*project name on center when screen is playground*/} - +
+ {/*logo*/} + + {/*project name on center when screen is playground*/} + +
+ -
- - - {/* delete edit profile option popup*/} - {isProfileModal && - - } - {/*edit profile pop up */} - {editProfileLoaderOpen && } - {isEditProfileModal && - - } - {/*log out pop up*/} - {isLogoutModal && - - } - {/*help icon pop up*/} - {isHelpCenterModal && } - {/*Session expire pop up*/} - {isSessionExpireModal && } -
+ {/* delete edit profile option popup*/} + {isProfileModal && } + {/*edit profile pop up */} + {editProfileLoaderOpen && } + {isEditProfileModal && } + {/*log out pop up*/} + {isLogoutModal && } + {/*help icon pop up*/} + {isHelpCenterModal && } + {/*Session expire pop up*/} + {isSessionExpireModal && }
- ); +
); } @@ -214,62 +198,58 @@ function RightSection(params) { location, isOnline, isAutoSync, - setIsAutoSync + setIsAutoSync, } = params const themes = useTheme(); const isMobile = useMediaQuery(themes.breakpoints.down('sm')); const tabletQuery = window.matchMedia("(min-width: 768px) and (max-width: 1024px)").matches; const isMobileLandscape = window.matchMedia("(max-height:440px) and (max-width: 1000px) and (orientation: landscape)").matches const isSignedIn = localStorage.getItem("isSigIn") === "true"; - const {setIsAutoSyncEnabled} = useContext(StoreContext); - return ( - <> - {location.pathname === PathName.playGround && isSignedIn && !isMobile && !tabletQuery && !isMobileLandscape && - {"syncIcon"} { - if (isOnline) { - if (localStorage.getItem("isSigIn") === "true") { - setIsAutoSync(true) - await autoSync().then(() => { - setIsAutoSyncEnabled(true); + const {setIsAutoSyncEnabled, setDrawer, setCategory,drawer} = useContext(StoreContext); + return (<> + {location.pathname === PathName.playGround && !isMobile && !isMobileLandscape && + } + {location.pathname === PathName.playGround && isSignedIn && !isMobile && !tabletQuery && !isMobileLandscape && + {"syncIcon"} { + if (isOnline) { + if (localStorage.getItem("isSigIn") === "true") { + setIsAutoSync(true) + await autoSync().then(() => { + setIsAutoSyncEnabled(true); + setIsAutoSync(false); + }) + .catch((e) => { + errorToast("Something went wrong!") setIsAutoSync(false); }) - .catch((e) => { - errorToast("Something went wrong!") - setIsAutoSync(false); - }) - } else { - errorToast("Please sign-In to auto sync.") - } } else { - errorToast(Constants.InternetOffMsg) + errorToast("Please sign-In to auto sync.") } + } else { + errorToast(Constants.InternetOffMsg) } - } - style={{height: 24}}/> - } - {/*help icon if screen is playground and device is not mobile*/} - {location.pathname === PathName.playGround && !isMobile && !tabletQuery && !isMobileLandscape && - {"helpCenter"} setIsHelpCenterModal(true)} - style={{height: 24}} title={"Help"}/> - } - {/*if screen is playground, and it's mobile than do not show change theme icon and divider*/} - {!(location.pathname === PathName.playGround && isMobile) && - <> - {/*change theme icon*/} - icon toggleTheme(!theme)} - src={theme === "dark" ? Images.lightThemeIcon : Images.darkThemeIcon} - className={`${theme === "dark" ? styles.lightThemeIcon : styles.darkThemeIcon} ${styles.iconMargin}`}/> - {/*divider*/} - icon - - } - {/*if signed in then show icon and name or else sign in option*/} - - - ) + }} + style={{height: 24}}/>} + {/*help icon if screen is playground and device is not mobile*/} + {location.pathname === PathName.playGround && !isMobile && !tabletQuery && !isMobileLandscape && + {"helpCenter"} setIsHelpCenterModal(true)} + style={{height: 24}} title={"Help"}/>} + {/*if screen is playground, and it's mobile than do not show change theme icon and divider*/} + {!(location.pathname === PathName.playGround && isMobile) && <> + {/*change theme icon*/} + icon toggleTheme(!theme)} + src={theme === "dark" ? Images.lightThemeIcon : Images.darkThemeIcon} + className={`${theme === "dark" ? styles.lightThemeIcon : styles.darkThemeIcon} ${styles.iconMargin}`}/> + {/*divider*/} + icon + } + {/*if signed in then show icon and name or else sign in option*/} + + + ) } @@ -282,14 +262,36 @@ function RightSection(params) { function ProjectNameSection(params) { const {anchorEl, handleClick, projectName, open, setOpen, setDeleteProject, theme, setProjectName} = params const location = useLocation(); - return ( - //when screen is playground then show project name and if clicked on project name then show projectName with popUp + return (//when screen is playground then show project name and if clicked on project name then show projectName with popUp location.pathname === PathName.playGround ? !open ? - - : - - : "" - ) + : + : "") } + +/** + * function for chatbot window + * @param params + * @returns {Element} + * @constructor + */ +function ChatBot(params) { + const { setDrawer, setCategory, drawer } = params; + + const handleChatClick = () => { + setCategory(Constants.chat); + setDrawer(!drawer); // Toggle the drawer state + }; + + return ( +
+ Chat Icon +
+ ); +} \ No newline at end of file diff --git a/open-code/src/components/navBar/navbar.module.css b/open-code/src/components/navBar/navbar.module.css index 0ed19112a..acaa378f9 100644 --- a/open-code/src/components/navBar/navbar.module.css +++ b/open-code/src/components/navBar/navbar.module.css @@ -4,6 +4,26 @@ cursor: pointer; } +.chatButton { + width: 3%; + align-items: center; + justify-content: center; + font-family: Gilroy-Medium, sans-serif; + position: fixed; + bottom: 675px; + right: 21em; + z-index: 1; + border: none; + display: flex; + transition: transform 0.3s, filter 0.3s; + +} + +.chatButton:hover { + transform: scale(1.3); + filter: brightness(2.5); +} + .extraStyles { margin: 10px; } diff --git a/open-code/src/components/personaCard/peronaCard.js b/open-code/src/components/personaCard/peronaCard.js new file mode 100644 index 000000000..498513757 --- /dev/null +++ b/open-code/src/components/personaCard/peronaCard.js @@ -0,0 +1,55 @@ +import React, { useContext, useState } from 'react'; +import styles from "./personaCard.module.css"; +import { personas } from "../../utils/persona"; +import { ThemeContext } from "../../App"; +import { Themes } from "../../utils/constants"; +import { Images } from "../../utils/images"; + +/** + * Component for choosing persona character + * @param handlePersonas + * @returns {Element} + * @constructor + */ +const PersonaCard = ({ handlePersonas }) => { + const theme = useContext(ThemeContext); + const [selectedPersona, setSelectedPersona] = useState(null); // State to track selected persona + + const handleClick = (key) => { + if (!selectedPersona) { // Only allow click if no persona is selected + setSelectedPersona(key); + handlePersonas(key); + } + }; + + return ( +
+
+ {"Person +

Choose Your Persona:

+
+
+ {personas.slice(0, 5).map((item) => ( +
handleClick(item.key)} + style={{ + fontSize: '14px', + pointerEvents: selectedPersona ? 'none' : 'auto', + cursor: selectedPersona ? 'not-allowed' : 'pointer' + }} + > +

{item.name}

+
+ ))} +
+
+ ); +}; + +export default PersonaCard; diff --git a/open-code/src/components/personaCard/personaCard.module.css b/open-code/src/components/personaCard/personaCard.module.css new file mode 100644 index 000000000..85b325712 --- /dev/null +++ b/open-code/src/components/personaCard/personaCard.module.css @@ -0,0 +1,77 @@ +.personaContainer { + display: flex; + flex-direction: column; + gap: 0.5rem; + padding: 1rem; +} + +.personaCard { + background-color: #f0f0f0; + border: 1px solid #ccc; + border-radius: 8px; + padding: 0.5rem; + width: 90%; + text-align: justify; + cursor: pointer; /* Default cursor */ + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); +} + +.personaCard:hover { + transform: scale(1.05); + background-color: #0071C5; /* Hover background color */ + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); + color: #FFFFFF; +} + +/* Disable hover effect for non-selected items when one is selected */ +.personaCard:not(.selected):hover { + transform: none; /* No scaling on hover */ + background-color:#0071C5 ; /* Keep the original background color */ + box-shadow: none; /* No shadow effect */ +} + +/* Selected persona style */ +.selected { + background-color: #0071C5; /* Highlight color when selected */ + color: #FFFFFF; + pointer-events: none; /* Prevent hover effects */ +} + +/* Dark Theme */ +.dark .personaContainer { + background-color: #1e1e1e; +} + +.dark .personaCard { + background-color: #121212; + border: 1px solid #444; + color: #FFFFFF; +} + +.dark .personaCard:hover { + background-color: #0071C5; /* Hover effect for non-selected items */ +} + +.dark .selected { + background-color: #0071C5; /* Keep selected color */ +} + +/* Media queries for responsiveness */ +@media (max-width: 600px) { + .personaContainer { + padding: 0.5rem; + } + .personaCard { + width: 100%; /* Full width on smaller screens */ + } +} +.headingContainer { + display: flex; + align-items: center; + gap: 8px; +} + +.icon { + width: 24px; + height: 24px; +} diff --git a/open-code/src/context/context.js b/open-code/src/context/context.js index 7f41b1e1e..bd09f446a 100644 --- a/open-code/src/context/context.js +++ b/open-code/src/context/context.js @@ -58,7 +58,7 @@ export default ({ isDob, setIsDob, isAutoSyncEnabled, setIsAutoSyncEnabled, isSessionExpireModal, setIsSessionExpireModal, - setIsSessionExpire, isTimeoutId, setTimeoutId + setIsSessionExpire, isTimeoutId, setTimeoutId, } return {children} } diff --git a/open-code/src/services/chatAssistant.js b/open-code/src/services/chatAssistant.js new file mode 100644 index 000000000..8eb88a0d6 --- /dev/null +++ b/open-code/src/services/chatAssistant.js @@ -0,0 +1,113 @@ +import {blocklyFinalPrompt,personaFinalPrompt} from "../utils/prompt"; + +/** + * API to get the assistant response with streaming + * @param userPrompt + * @param persona + * @param currentXML + * @param signal + * @param onMessage + * @returns {Promise} + */ +export const getAIMessage = async (userPrompt, persona, currentXML, signal, onMessage) => { + const url = `https://api.openai.com/v1/chat/completions`; + + try { + const response = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${process.env.REACT_APP_OPENAI_API_KEY}`, + }, + + body: JSON.stringify({ + "messages": [ + { + role: 'system', + content: persona ? personaFinalPrompt(persona) : blocklyFinalPrompt + "\nInput XML : " + currentXML + }, + {role: 'user', content: userPrompt} + ], + model: "gpt-4o-mini-2024-07-18", + stream: true, + response_format: { + type: "json_schema", + json_schema: { + name: "blockly_chat_assistant", + description: `Response structure should follow the given json schema structure: + $$CONTENT$$ key does not have any xml but $$RESPONSE$$ key have only xml code in it. + `, + schema: { + type: "object", + strict: true, + properties: { + $$CONTENT$$: { + type: "string", + description: `Make sure you Provide only an explanation in clear, simple text. This section should describe the purpose and usage of the Blockly blocks. Do not include any XML code or technical details in this part—just the explanation` + }, + $$RESPONSE$$: { + type: "string", + description: `Ensure you Provide only valid XML code for the Blockly blocks. Do not include any explanations in this section—only XML code.` + }, + }, + required: ["$$CONTENT$$", "$$RESPONSE$$"], + additionalProperties: false + }, + } + } + }), + signal + }); + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let resultText = ''; + + while (true) { + const {done, value} = await reader.read(); + if (done) break; + + // Decode the stream chunk + const chunk = decoder.decode(value, {stream: true}); + // Stop if the request was aborted + if (signal.aborted) { + return "Request was cancelled."; + + } + const parsedChunk = chunk + .split("\n") + .filter(line => line.trim()) // Filter out empty lines + .map(line => line.replace(/^data: /, "")) // Remove "data: " prefix + .map(line => { + try { + return JSON.parse(line); + } catch (e) { + console.error("JSON parse error:", e); + return null; + } + }) + .filter(parsed => parsed !== null); + for (const parsed of parsedChunk) { + const content = parsed?.choices[0]?.delta?.content; + if (content) { + onMessage(content); // Update UI with new content + resultText += content; + } + if (parsed?.choices[0]?.finish_reason === "stop") { + onMessage("Done"); + return resultText; + } + } + } + + return resultText; + + } catch (error) { + if (error.name === 'AbortError') { + return "Request was cancelled."; + } else { + console.error('Error occurred:', error); + return "Error occurred while processing your request."; + } + } +}; diff --git a/open-code/src/services/workspace.js b/open-code/src/services/workspace.js index c48384377..fd89bb39a 100644 --- a/open-code/src/services/workspace.js +++ b/open-code/src/services/workspace.js @@ -354,7 +354,6 @@ function filterModels(modelType, assetType) { filterLabels() let modelsArray = [] let updatedData = localStorage.getItem(localStorageKeys.configData) - console.log(updatedData); if (updatedData !== " " || null) { let data = JSON.parse(updatedData)?.filter(obj => (modelType.includes(obj.type) && obj.pathType === "FILE") || (obj.pathType === "ASSET" && obj.type === assetType)) if (data?.length === 0) { diff --git a/open-code/src/utils/blocks.json b/open-code/src/utils/blocks.json new file mode 100644 index 000000000..51d0dc62e --- /dev/null +++ b/open-code/src/utils/blocks.json @@ -0,0 +1,1063 @@ +[ + { + "blockType": "start", + "definition": { + "type": "block_type", + "message0": "%1 %2", + "args0": [ + { + "type": "field_label_serializable", + "name": "start", + "text": "start" + }, + { + "type": "input_statement", + "name": "start_blocks" + } + ], + "colour": "#4860b7", + "tooltip": "", + "helpUrl": "" + } + }, + { + "blockType": "forever", + "definition": { + "type": "block_type", + "message0": "%1 %2", + "args0": [ + { + "type": "field_label_serializable", + "name": "forever", + "text": "forever" + }, + { + "type": "input_statement", + "name": "forever_loop_blocks" + } + ], + "colour": "#4860b7", + "tooltip": "", + "helpUrl": "" + } + }, + { + "blockType": "wait", + "definition": { + "type": "block_type", + "message0": "%1 %2 ms", + "args0": [ + { + "type": "field_label_serializable", + "name": "wait", + "text": "wait for" + }, + { + "type": "field_number", + "name": "time", + "value": 3000, + "min": 0 + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#4860b7", + "tooltip": "", + "helpUrl": "" + }, + "working": "wait for chosen time" + }, + { + "blockType": "soundType", + "definition": { + "type": "block_type", + "message0": "play sound %1 speed", + "args0": [ + { + "type": "field_dropdown", + "name": "type", + "options": [ + [ + "slow", + "slow" + ], + [ + "medium", + "medium" + ], + [ + "fast", + "fast" + ] + ] + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#709662", + "tooltip": "", + "helpUrl": "" + }, + "working": "play chosen sound speed in robot phone" + }, + { + "blockType": "soundMode", + "definition": { + "type": "block_type", + "message0": "play sound %1 mode", + "args0": [ + { + "type": "field_dropdown", + "name": "mode_type", + "options": [ + [ + "dual drive", + "dual drive" + ], + [ + "joystick control", + "joystick control" + ], + [ + "gamepad", + "gamepad" + ] + ] + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#709662", + "tooltip": "", + "helpUrl": "" + }, + "working": "play chosen sound mode in robot phone" + }, + { + "blockType": "forward&BackwardAtSpeed", + "definition": { + "type": "block_type", + "message0": "move %1 at speed %2", + "args0": [ + { + "type": "field_dropdown", + "name": "direction_type", + "options": [ + [ + "forward", + "moveForward" + ], + [ + "backward", + "moveBackward" + ] + ] + }, + { + "type": "field_slider", + "name": "slider", + "value": 192, + "min": 0, + "max": 255 + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#d56235", + "tooltip": "", + "helpUrl": "" + }, + "working": "move robot in forward or backward direction at chosen speed" + }, + { + "blockType": "left&RightAtSpeed", + "definition": { + "type": "block_type", + "message0": "move %1 at speed %2", + "args0": [ + { + "type": "field_dropdown", + "name": "direction_type", + "options": [ + [ + "left", + "moveLeft" + ], + [ + "right", + "moveRight" + ] + ] + }, + { + "type": "field_slider", + "name": "slider", + "value": 192, + "min": 0, + "max": 255 + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#d56235", + "tooltip": "", + "helpUrl": "" + }, + "working": "move robot in left or right direction at chosen speed" + }, + { + "blockType": "moveLeft&Right", + "definition": { + "type": "block_type", + "message0": "move %1 %2 %3 %4", + "args0": [ + { + "type": "field_label_serializable", + "name": "left_name", + "text": "left at" + }, + { + "type": "field_slider", + "name": "left_distance", + "value": 192, + "min": -255, + "max": 255 + }, + { + "type": "field_label_serializable", + "name": "right_name", + "text": "and right at" + }, + { + "type": "field_slider", + "name": "right_distance", + "value": 192, + "min": -255, + "max": 255 + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#d56235", + "tooltip": "", + "helpUrl": "" + }, + "working": "move robot in left, right, forward, backward or circular direction at chosen speed" + }, + { + "blockType": "movementStop", + "definition": { + "type": "block_type", + "message0": "%1", + "args0": [ + { + "type": "field_label_serializable", + "name": "movement_stop", + "text": "stop car immediately" + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#ca3143", + "tooltip": "", + "helpUrl": "" + }, + "working": "stop robot immediately" + }, + { + "blockType": "sonarReading", + "definition": { + "type": "ellipse_block", + "message0": "sonar reading", + "output": "Number", + "colour": "#49a2a5", + "tooltip": "", + "helpUrl": "" + }, + "working": "return sonar sensor readings" + }, + { + "blockType": "speedReading", + "definition": { + "type": "ellipse_block", + "message0": "speed reading", + "output": "Number", + "colour": "#49a2a5", + "tooltip": "", + "helpUrl": "" + }, + "working": "return speed sensor readings" + }, + { + "blockType": "voltageDividerReading", + "definition": { + "type": "ellipse_block", + "message0": "voltage divider reading", + "output": "Number", + "colour": "#49a2a5", + "tooltip": "", + "helpUrl": "" + }, + "working": "return voltage divider sensor readings" + }, + { + "blockType": "wheelOdometerSensors", + "definition": { + "type": "ellipse_block", + "message0": " wheel odometry %1 ", + "args0": [ + { + "type": "field_dropdown", + "name": "wheel_sensors", + "options": [ + [ + "Front", + "frontWheelReading" + ], + [ + "Back", + "backWheelReading" + ] + ] + } + ], + "output": "Number", + "colour": "#49a2a5", + "tooltip": "", + "helpUrl": "" + }, + "working": "return wheel odometer sensor readings" + }, + { + "blockType": "gyroscope_reading", + "definition": { + "type": "ellipse_block", + "message0": "gyroscope reading %1", + "args0": [ + { + "type": "field_dropdown", + "name": "axis", + "options": [ + [ + "x axis", + "x" + ], + [ + "y axis", + "y" + ], + [ + "z axis", + "z" + ] + ] + } + ], + "output": "Number", + "colour": "#49a2a5", + "tooltip": "", + "helpUrl": "" + }, + "working": "return gyroscope sensor readings" + }, + { + "blockType": "acceleration_reading", + "definition": { + "type": "ellipse_block", + "message0": "acceleration reading %1", + "args0": [ + { + "type": "field_dropdown", + "name": "axis", + "options": [ + [ + "x axis", + "x" + ], + [ + "y axis", + "y" + ], + [ + "z axis", + "z" + ] + ] + } + ], + "output": "Number", + "colour": "#49a2a5", + "tooltip": "", + "helpUrl": "" + }, + "working": "return acceleration readings" + }, + { + "blockType": "magnetic_reading", + "definition": { + "type": "ellipse_block", + "message0": "magnetic reading %1", + "args0": [ + { + "type": "field_dropdown", + "name": "axis", + "options": [ + [ + "x axis", + "x" + ], + [ + "y axis", + "y" + ], + [ + "z axis", + "z" + ] + ] + } + ], + "output": "Number", + "colour": "#49a2a5", + "tooltip": "", + "helpUrl": "" + }, + "working": "return magnetic sensor readings" + }, + { + "blockType": "speedControl", + "definition": { + "type": "block_type", + "message0": "set speed limit to %1", + "args0": [ + { + "type": "field_dropdown", + "name": "type", + "options": [ + [ + "slow", + "'slow'" + ], + [ + "medium", + "'medium'" + ], + [ + "fast", + "'fast'" + ] + ] + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#bf778b", + "tooltip": "", + "helpUrl": "" + }, + "working": "set speed to chosen speed in robot app" + }, + { + "blockType": "controllerMode", + "definition": { + "type": "block_type", + "message0": "switch controller to %1", + "args0": [ + { + "type": "field_dropdown", + "name": "controller", + "options": [ + [ + { + "src": "", + "width": 25, + "height": 25, + "alt": "phone" + }, + "'phone'" + ], + [ + { + "src": "", + "width": 25, + "height": 25, + "alt": "gamepad" + }, + "'gamepad'" + ] + ] + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#bf778b", + "tooltip": "", + "helpUrl": "" + }, + "working": "set controller mode to chosen mode in robot app" + }, + { + "blockType": "driveModeControls", + "definition": { + "type": "block_type", + "message0": "switch drive mode to %1", + "args0": [ + { + "type": "field_dropdown", + "name": "controller", + "options": [ + [ + { + "src": "", + "width": 25, + "height": 25, + "alt": "dualDrive" + }, + "'dualDrive'" + ], + [ + { + "src": "", + "width": 25, + "height": 25, + "alt": "joystick" + }, + "'joystick'" + ], + [ + { + "src": "", + "width": 25, + "height": 25, + "alt": "game" + }, + "'game'" + ] + ] + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#bf778b", + "tooltip": "", + "helpUrl": "" + }, + "working": "set drive mode to chosen mode in robot app" + }, + { + "blockType": "brightness", + "definition": { + "type": "block_type", + "message0": "set brightness of tail and head LEDs %1", + "args0": [ + { + "type": "field_slider", + "name": "slider", + "value": 50, + "min": 0, + "max": 100 + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#687c9e", + "tooltip": "", + "helpUrl": "" + }, + "working": "set brightness for tail and head led lights" + }, + { + "blockType": "indicators", + "definition": { + "type": "block_type", + "message0": "turn %1 indicator %2", + "args0": [ + { + "type": "field_dropdown", + "name": "side", + "options": [ + [ + "left", + "left" + ], + [ + "right", + "right" + ] + ] + }, + { + "type": "field_toggle", + "name": "TOGGLE_STATE", + "state": true, + "onText": "ON", + "offText": "OFF" + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#687c9e", + "tooltip": "", + "helpUrl": "" + }, + "working": "to ON/OFF left or right indicators of the openbot" + }, + { + "blockType": "brightnessHighOrLow", + "definition": { + "type": "block_type", + "message0": "turn LED brightness %1", + "args0": [ + { + "type": "field_toggle", + "name": "TOGGLE_STATE", + "state": true, + "onText": "ON", + "offText": "OFF" + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#687c9e", + "tooltip": "", + "helpUrl": "" + }, + "working": "to ON/OFF tail and head led lights" + }, + { + "blockType": "disableAI", + "definition": { + "type": "block_type", + "message0": "disable AI", + "previousStatement": null, + "nextStatement": null, + "colour": "#ca3143", + "tooltip": "", + "helpUrl": "" + }, + "working": "disable the ongoing artificial intelligence" + }, + { + "blockType": "inputSound", + "definition": { + "type": "block_type", + "message0": "play sound %1", + "args0": [ + { + "type": "field_input", + "name": "text", + "text": "move straight" + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#709662", + "tooltip": "", + "helpUrl": "" + }, + "working": "Play a custom sound from robot phone that is input in the text field" + }, + { + "blockType": "display_sensors", + "definition": { + "type": "block_type", + "message0": "display sensor data %1", + "args0": [ + { + "type": "input_value", + "name": "value" + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#4860b7", + "tooltip": "", + "helpUrl": "" + }, + "working": "display input sensor data on the robot phone" + }, + { + "blockType": "display_string", + "definition": { + "type": "block_type", + "message0": "display string %1", + "args0": [ + { + "type": "field_input", + "name": "text", + "text": "Hello" + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#4860b7", + "tooltip": "", + "helpUrl": "" + }, + "working": "display input text on robot phone" + }, + { + "blockType": "objectTracking", + "definition": { + "type": "block_type", + "message0": "follow a %1 using %2", + "args0": [ + { + "type": "field_dropdown", + "name": "class", + "options": [ + [ + "person", + "person" + ], + [ + "bus", + "bus" + ], + [ + "birds", + "birds" + ] + ] + }, + { + "type": "field_dropdown", + "name": "models", + "options": [ + [ + "MobileNetV1-300", + "MobileNetV1-300" + ] + ] + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#458ff7", + "tooltip": "", + "helpUrl": "" + }, + "working": "enabling object tracking to follow a chosen object at chosen model" + }, + { + "blockType": "autopilot", + "definition": { + "type": "block_type", + "message0": "enable autopilot using %1", + "args0": [ + { + "type": "field_dropdown", + "name": "autopilot models", + "options": [ + [ + "CIL-Mobile-Cmd", + "CIL-Mobile-Cmd" + ] + ] + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#458ff7", + "tooltip": "", + "helpUrl": "" + }, + "working": "enabling autopilot to run at chosen model" + }, + { + "blockType": "navigateForwardAndLeft", + "definition": { + "type": "block_type", + "message0": "move to forward at %1 (cm) and left at %2 (cm) using %3", + "args0": [ + { + "type": "field_number", + "name": "forward", + "value": 0 + }, + { + "type": "field_number", + "name": "left", + "value": 0 + }, + { + "type": "field_dropdown", + "name": "navigation_models", + "options": [ + [ + "PilotNet-Goal", + "PilotNet-Goal" + ] + ] + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#458ff7", + "tooltip": "", + "helpUrl": "" + }, + "working": "enabling point goal navigation to run at chosen model" + }, + { + "blockType": "objectDetection", + "definition": { + "type": "block_type", + "message0": "on %1 detected using %2 %3 do %4 on lost %5 frames %6 do %7", + "args0": [ + { + "type": "field_dropdown", + "name": "labels", + "options": [ + [ + "person", + "person" + ], + [ + "bus", + "bus" + ], + [ + "birds", + "birds" + ] + ] + }, + { + "type": "field_dropdown", + "name": "models", + "options": [ + [ + "MobileNetV1-300", + "MobileNetV1-300" + ] + ] + }, + { + "type": "input_dummy" + }, + { + "type": "input_statement", + "name": "detect_tasks" + }, + { + "type": "field_slider", + "name": "frames", + "value": 192, + "min": 1, + "max": 90 + }, + { + "type": "input_dummy" + }, + { + "type": "input_statement", + "name": "framesLost_tasks" + } + ], + "colour": "#458ff7", + "tooltip": "", + "helpUrl": "" + }, + "working": "enable multiple object tracking for the main part and then colation of the given task to perform tasks upon detecting a chosen object and also execute different tasks when losing the chosen frames." + }, + { + "blockType": "multipleAIDetection", + "definition": { + "type": "block_type", + "message0": "enable autopilot using %1 , %2 on detecting %3 using %4 %5 Do %6", + "args0": [ + { + "type": "field_dropdown", + "name": "autopilot_models", + "options": [ + [ + "CIL-Mobile-Cmd", + "CIL-Mobile-Cmd" + ] + ] + }, + { + "type": "input_dummy" + }, + { + "type": "field_dropdown", + "name": "labels", + "options": [ + [ + "person", + "person" + ], + [ + "bus", + "bus" + ], + [ + "birds", + "birds" + ] + ] + }, + { + "type": "field_dropdown", + "name": "objectTracking_models", + "options": [ + [ + "MobileNetV1-300", + "MobileNetV1-300" + ] + ] + }, + { + "type": "input_dummy" + }, + { + "type": "input_statement", + "name": "tasks" + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#458ff7", + "tooltip": "", + "helpUrl": "" + }, + "working": "Enable object tracking and autopilot simultaneously to perform specified tasks upon detecting the chosen object. " + }, + { + "blockType": "multipleObjectTracking", + "definition": { + "type": "block_type", + "message0": "follow a %1 using %2 , %3 on detecting %4 %5 Do %6", + "args0": [ + { + "type": "field_dropdown", + "name": "labels1", + "options": [ + [ + "person", + "person" + ], + [ + "bus", + "bus" + ], + [ + "birds", + "birds" + ] + ] + }, + { + "type": "field_dropdown", + "name": "models", + "options": [ + [ + "MobileNetV1-300", + "MobileNetV1-300" + ] + ] + }, + { + "type": "input_dummy" + }, + { + "type": "field_dropdown", + "name": "labels2", + "options": [ + [ + "person", + "person" + ], + [ + "bus", + "bus" + ], + [ + "birds", + "birds" + ] + ] + }, + { + "type": "input_dummy" + }, + { + "type": "input_statement", + "name": "tasks" + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": "#458ff7", + "tooltip": "", + "helpUrl": "" + } + }, + { + "blockType": "controls_if" + }, + { + "blockType": "controls_ifelse" + }, + { + "blockType": "logic_compare" + }, + { + "blockType": "logic_operation" + }, + { + "blockType": "logic_negate" + }, + { + "blockType": "logic_boolean" + }, + { + "blockType": "controls_whileUntil" + }, + { + "blockType": "controls_repeat" + }, + { + "blockType": "controls_flow_statements" + }, + { + "blockType": "controls_for" + }, + { + "blockType": "math_arithmetic" + }, + { + "blockType": "math_number" + }, + { + "blockType": "math_modulo" + }, + { + "blockType": "math_single" + }, + { + "blockType": "math_constant" + }, + { + "blockType": "math_number_property" + }, + { + "blockType": "math_round" + }, + { + "blockType": "math_random_int" + }, + { + "blockType": "variables_set" + }, + { + "blockType": "variables_get" + }, + { + "blockType": "math_change" + }, + { + "blockType": "math_number" + } +] \ No newline at end of file diff --git a/open-code/src/utils/blocksPrompt.js b/open-code/src/utils/blocksPrompt.js new file mode 100644 index 000000000..96fe6c5c9 --- /dev/null +++ b/open-code/src/utils/blocksPrompt.js @@ -0,0 +1,159 @@ +const custom_blocks_prompt = `Description of some custom blocks : +start : The start block initiates the execution of program and execute the block code single time. +For example:- user : Move forward for 10 seconds and then stops. +assistant : "startforeverstartmoveForward192" + +forever : The forever will create an infinite loop which indicates that loop will continue indefinitely with each iteration +For example:- user : If the sonar reading is greater than 20 then move forward else stop. +assistant : "foreverGT20moveForward192stop car immediately" + +VERY IMPORTANT NOTE: The forever block is a root block, so it should not be connected inside any other root blocks like start. Ensure that all responses respect this constraint. + +display_sensors : This block is used to display given sensor readings on robot phone display. +For example:- user : Give live sonar reading on robot phone display. +assistant : "forever" + +display_string : This block used to display user input text on robot phone display. +For example :- user : Create a block to display "OpenBot" on robot screen. +assistant : "startOpenBot" + +brightness : This block used to set tail and head LED lights intensity. +For example :- user : Set brightness of LED to 50 for 5 seconds and then set brightness to 100 +assistant : "start50wait for5000100" + +indicators : This block used to ON/OFF the left/right robot indicators. +For example :- user : Turn left indicator on +assistant : "startleftON" + +brightnessHighOrLow : This block used to turn both tail and head LED lights ON/OFF. +For example :- user : Turn on LED light +assistant : "startON" + +speedControl: This block is used to set the robot's speed to static values: slow, medium, or fast. +For example:- user : Set the speed of robot to fast +assistant : "start'fast'" + +controllerMode : This block used to set controller mode in robot phone. +For example:- user : Create a block to set controller mode to gamepad +assistant : "start'gamepad'" + +driveModeControls : This block used to set the drive mode in robot phone. +For example:- user : Set the drive model to dual in robot phone. +assistant : "start'dualDrive'" + +soundType : This block used to play static sound from robot phone which are "slow", "medium", or "fast". +For example:- user : Play sound medium from robot phone +assistant : "startmedium" + +soundMode : This block used to play static sound from robot phone which are "dualDrive" , "joystick", or "game". +For example:- user : Play sound dual drive from robot phone +assistant : "startdual drive" + +inputSound : This block used to play custom user input sound from robot phone.When user ask to say or tell something that means input sound block is used. +For example:- user : Play sound "openbot" from robot phone. +assistant : "startopenbot" + +IMPORTANT NOTE:When user ask to say or tell something that means input sound block is used. + +sonarReading : This block used to return live sonar reading of robot. +For example :- user: Display live sonar reading of robot on robot screen. +assistant : "forever" + +speedReading : This block used to return live speed reading of robot. +For example :- user : Display live speed reading of robot on robot screen. +assistant : "forever" + +voltageDividerReading : This block used to return live voltage reading of robot. +For example :- user : Display live voltage reading of robot on robot screen. +assistant : "forever" + +wheelOdometerSensors : This block used to return live front or back wheel reading of robot. +For example :- Display live front wheel reading of robot on robot screen. +assistant : "foreverfrontWheelReading" + +gyroscope_reading : This block used to return live gyroscope reading of phone in the selected axis. +For example :- Display live gyroscope reading in x-axis on robot screen. +assistant : "foreverx" + +acceleration_reading : This block used to return live acceleration reading of phone in the selected axis. +For example :- Display live acceleration reading in x-axis on robot screen. +assistant : "foreverx" + +magnetic_reading : This block used to return live magnetic reading of phone in the selected axis. +For example :- Display live magnetic reading in x-axis on robot screen. +assistant : "foreverx" + +forward&BackwardAtSpeed : This block used to move robot forward or backward at input speed. +For example :- Move robot backward for 3 seconds at speed 192. +assistant : "startmoveForward192wait for3000stop car immediately" + +left&RightAtSpeed : This block used to move robot left or right at input speed. +For example :- Move robot left at speed 200 +assistant : "startmoveLeft200" + +moveLeft&Right : This block used to move robot in circular direction at input left and right speed. +For example :- Move robot circular for 10 seconds +assistant :- "startleft at192and right at255wait for10000stop car immediately" + +movementStop : This block used to stop robot immediately. +For example :- Stop robot movement now. +assistant :- "startstop car immediately" +` + +const AI_prompt = `AI blocks description:- + +disableAI: This block is used to stop the ongoing Artificial Intelligence (AI) process. It is essential to insert a disableAI block between two AI blocks to prevent compilation errors. Without a disableAI block between them, the system cannot handle two AI blocks properly. + +objectTracking : This block is used to enable object following, such as for a laptop, car, person, etc. It contains two fields within the block: +1. A dropdown field named "class", which lists all available objects. +2. A dropdown field named "models", which contains AI models for the algorithm. +For example: user : Follow a person for 10 seconds. +assistant : "startpersonMobileNetV1-300wait for10000" + +autopilot: This block is used to enable autopilot algorithm. It contains one field within the block: +1. A dropdown field named "autopilot models", which lists all available autopilot models for the algorithm. +For example: user : Enabled autopilot for 20 seconds. +assistant: "startCIL-Mobile-Cmdwait for20000" + +navigateForwardAndLeft: This block is used to enable point goal navigation, it means in 3D space a point is located and the robot will reach to that point using the given model. It contains three fields within the block: +1. A input text field named "forward" for forward distance from the reference i.e phone to goal. +2. A input text field named "left" for left distance from the reference to goal. +3. A dropdown field named "navigation_models", which lists all available models for the algorithm. +If user wants to given right from reference then "left" must be given in negative. If user wants to given backward from reference then "forward" must be given in negative. +For example: user : Enable point goal navigation and set goal as forward to 20 and left to 30 +assistant : "start2030PilotNet-Goal" + +objectDetection: This block is a multiple object tracking block that can be use multiple times in the workspace. The block is designed to enable multiple object detections, initializing the process for the specified object. +Once the chosen object is detected, the robot will execute all tasks outlined in the subsequent 'do' statement. If the specified class is not detected within the defined number of continuous frames, the robot will proceed to execute the tasks specified in the subsequent do statement. +The block can be use multiple times within the playground for different objects as well. It contains three fields within the block: +1. A dropdown field named "labels", which lists all available objects. +2. A dropdown field named "models", which contains object tracking models for the algorithm. +3. A input text field named "frames" which varies from 1 to 90. +This block is designed to run indefinitely. If it switches to another objectDetection object block and detects the previously detected object again, the instructions from the previous block will continue to execute. +For example:- user : When person is detected, move robot forward at speed 192 and when 90 frames are lost while detecting , set brightness of leds to 50. +assistant : "personMobileNetV1-30090moveForward19250" + +IMPORTANT NOTE: The objectDetection block is a root block, so it should not be connected inside any other root blocks like start. Ensure that all responses respect this constraint. + +multipleAIDetection: This block enables multiple artificial intelligence functions, allowing autopilot and object tracking to run simultaneously. +Initially, the block controls the car using the autopilot while the object detection algorithm monitors for a specified object. +When the specified object is detected, both the autopilot and detection algorithms stop, and the tasks specified in the block are executed. +It contains three fields within the block: +1. A dropdown field named "autopilot_models", which lists all available models. +2. A dropdown field named "labels", which lists all available objects. +3. A dropdown field named "objectTracking_models", which lists all available models. +For example:- user : Move forward for 10 seconds when a person is detected; until then, enable autopilot. +assistant: "startCIL-Mobile-CmdpersonMobileNetV1-300moveForward192wait for3000stop car immediately". + +VERY IMPORTANT NOTE: +Root Blocks: These are the block that can not be connected to inside other block +In cases when there is need to use start forever and object detection block together it should make their separate xml instead merging in one and should create individual blocks. +1.Start , forever ,object detection block these are root block hence they cannot be connected inside any other block. +for example: If a user asks like create a block to move in forward direction then wait for 10s and then move in backward direction forever and also a block to detect a person in this case there will be three blocks with respective functionality. +assistant:- foreverwait for10000moveBackward192startmoveForward192personMobileNetV1-30090moveForward192 + + + +` + +module.exports = {custom_blocks_prompt, AI_prompt}; diff --git a/open-code/src/utils/color.js b/open-code/src/utils/color.js index 4627f408a..ef150f399 100644 --- a/open-code/src/utils/color.js +++ b/open-code/src/utils/color.js @@ -10,6 +10,7 @@ export const colors = { whiteBackground: '#FFFFFF', blackPopupBackground: "#303030", blocklyBackground: "#202020", + chatBackgroundColor: "#d0e4f5" } export const buttonColor = { diff --git a/open-code/src/utils/constants.js b/open-code/src/utils/constants.js index b009d4ec8..8ea77c137 100644 --- a/open-code/src/utils/constants.js +++ b/open-code/src/utils/constants.js @@ -148,6 +148,7 @@ export const Constants = { js: "js", py: "py", qr: "qr", + chat: "chat", xml: "xml", json: "json", tflite: "tflite", @@ -156,10 +157,16 @@ export const Constants = { projects: "projects", models: "models", theme: "theme", - subscriptionEnded:"Your trial has just ended!", - subscriptionContinueService:"To continue using OpenBot Playground, you", - subscriptionContinueInfo:"will need to upgrade your plan.", - subscribeButton:"Subscribe Now" + subscriptionEnded: "Your trial has just ended!", + subscriptionContinueService: "To continue using OpenBot Playground, you", + subscriptionContinueInfo: "will need to upgrade your plan.", + subscribeButton: "Subscribe Now", +} + +export const ChatConstants = { + Playground: "Playground Support", + Message: "👋 Hi! I am here to assist you in creating blocks for OpenBot. Feel free to ask for any information you need regarding the playground.", + PersonaMessage:"Welcome aboard! Your journey with amazing characters begins now!" } export const Month = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; @@ -174,7 +181,11 @@ export const Errors = { error2: "Detected adjacent AI blocks. Please review the Start block configuration. When using adjacent AI blocks,insert a 'disable AI' block between them.", error3: "Identical objects for multiple detection AI block.", error4: "AI block present within the forever block. Please review the block code.", - error5: "Similar classes present in the Advanvced AI blocks" + error5: "Similar classes present in the advanced AI blocks", + error6: "Unable to connect to the server. Please try again later.", + error7: "Try again request interrupted", + error8: "Error in adding blocks to blockly playground", + error9: "An error occurred while processing your request." } export const PlaygroundConstants = { @@ -182,7 +193,7 @@ export const PlaygroundConstants = { forever: "forever", detectionOrUndetection: "detectionOrUndetection", multipleObjectTracking: "multipleObjectTracking", - variableDetection: "variableDetection", + objectDetection: "objectDetection", labels: "labels", disableAI: "disableAI", object_1: "object_1", @@ -353,11 +364,11 @@ export const errorToast = (message) => { export const aiBlocks = ["objectTracking", "autopilot", "multipleObjectTracking", "navigateForwardAndLeft", "multipleAIDetection"]; /** -user usage tables + user usage tables */ export const tables = { - users : "users", + users: "users", projects: "projects", - models:"models", - userUsage:"userUsage" + models: "models", + userUsage: "userUsage" } \ No newline at end of file diff --git a/open-code/src/utils/handler.js b/open-code/src/utils/handler.js new file mode 100644 index 000000000..33a1486bc --- /dev/null +++ b/open-code/src/utils/handler.js @@ -0,0 +1,41 @@ +//Function that hide the xml and provide complete formatted response on ui after response is completed +export const handler = (message) => { + try { + const parsedMessage = JSON.parse(message); + let content = parsedMessage?.$$CONTENT$$; + + // Define a regex to match XML content that starts with + const xmlRegex = //g; + + // If the content contains XML, replace it with an empty string + if (content && xmlRegex.test(content)) { + content = content.replace(xmlRegex, ''); + } + return content ?? undefined; + } catch (error) { + console.error('JSON parsing error:', error); + } +}; + +//This function is to provide formatting on clicking pause icon +export function cleanAndFormatResponse(response) { + // Replace multiple newlines (two or more) with double newlines to ensure paragraph breaks + let cleanedResponse = response.replace(/\n{2,}/g, '\n\n'); + + // Convert single newlines into spaces to prevent displaying "\n" in text + cleanedResponse = cleanedResponse.replace(/([^\n])\n([^\n])/g, '$1 $2'); + + // Ensure that numbered lists (e.g., "1. ...") are formatted correctly by keeping a newline before them + cleanedResponse = cleanedResponse.replace(/(\d+)\.\s+/g, '\n$1. '); + + // Properly format bullet points (e.g., "- ") by keeping a newline before them + cleanedResponse = cleanedResponse.replace(/\s*-\s+/g, '\n- '); + + // Add double newlines after periods for paragraph breaks, unless it's part of a list or bullet point + cleanedResponse = cleanedResponse.replace(/(?startmoveForward192wait for5000stop car immediatelywait for2000left at100and right at192"`; + +const blockly_prompt = ` +After explaining in simple and friendly way, update the input XML based on the user prompt according to the following rules. + + tag: All Blockly XML documents start with the tag and end with the tag. + + tag: Each block is represented by a tag. This tag includes a "type" attribute indicating the type of block. +Example: + + tag: Fields (such as text boxes or dropdown menus) within a block are represented by the tag. This tag includes a "name" attribute indicating the name of the field in lowercase letters. +Example: 10 + + tag: To connect other blocks as input, use the tag. This tag includes a "name" attribute indicating the name of the input. +Example: ... + + tag: To connect other blocks as statements, use the tag. This tag includes a "name" attribute indicating the name of the statement. +Example: ... + + tag: To connect consecutive blocks, use the tag. +Example: ... + + tag: Used to indicate shadow blocks (default blocks). + + tag: Used to save specific changes or configurations of the block. + +The Available blocks are: + +Control: start, forever, wait, display_sensors, display_string, controls_if, controls_ifelse, logic_compare, logic_operation, logic_negate, logic_boolean + +Loops: controls_whileUntil, controls_repeat, controls_flow_statements, controls_for + +Operators: math_arithmetic, math_number, math_modulo, math_single, math_constant, math_number_property, math_round, math_random_int + +Variables: variables_set, variables_get, math_change, math_number + +Lights: brightness, indicators, brightnessHighOrLow + +Controller: speedControl, controllerMode, driveModeControls + +Sound: soundType, soundMode, inputSound + +Sensors: sonarReading, speedReading, voltageDividerReading, wheelOdometerSensors, gyroscope_reading, acceleration_reading, magnetic_reading + +Movement: forward&BackwardAtSpeed, left&RightAtSpeed, moveLeft&Right, movementStop + +AI: disableAI, objectTracking, autopilot, navigateForwardAndLeft, objectDetection, multipleAIDetection + +All available blocks are also defined in the following blocklyJSON - ${blocksJSON}. Each object in the array refers to a block with its unique type and definition. The definition includes an "args0" array, which contains all the block fields with its "name" and "type". + +Important: Ensure that the field names in the generated Blockly XML match exactly with the provided field names in args0 in blockly JSON. + +${example_prompt} + +${custom_blocks_prompt} + +${AI_prompt} + +If the user asks something related to below prompts, provide the relevant links and answers as follows: +Build your own Robot Body: refer https://github.com/isl-org/OpenBot/blob/master/body/README.md +Flash the Arduino Firmware: https://github.com/isl-org/OpenBot/blob/master/firmware/README.md +Install the Android Apps: https://github.com/isl-org/OpenBot/blob/master/android/README.md +Drive the robot via a Controller: https://github.com/isl-org/OpenBot/blob/master/controller/README.md +Program your robot in the Playground: https://github.com/isl-org/OpenBot/blob/master/open-code/README.md +Train your own Driving Policy: https://github.com/isl-org/OpenBot/blob/master/policy/README.md +Related to open bot: OpenBot leverages smartphones as brains for low-cost robots. We have designed a small electric vehicle that costs about $50 and serves as a robot body. Our software stack for Android smartphones supports advanced +robotics workloads such as person following and real-time autonomous navigation and provide the documentation link https://github.com/isl-org/OpenBot?tab=readme-ov-file + +If the user greets or uses common pleasantries (e.g., 'hi,' 'hello,' 'how are you?','nice'), respond appropriately to acknowledge them before guiding them back to the relevant topic.`; + +const response_structure = `Explain the process according to following rules : +1.Explain the process of dragging each block from the toolbox and dropping it into the playground. Provide this explanation for each block. +2.Include a description of the use case for each block. +3.Suggest various additional blocks that can be added to the playground to enhance the given input. +4.When there are greetings or not related to blocks then it should not add the previous xml to the response and respond accordingly. +5.Ensure you are not giving xml tags in explanation part. +VERY IMPORTANT NOTE: Explain in friendly way as you are teaching kids. +IMPORTANT:Ensure you tell funny and quirky jokes only when user asks. +IMPORTANT NOTE: The objectDetection block is a root block, so it should not be connected inside any other root blocks like start. Ensure that all responses respect this constraint. +VERY IMPORTANT NOTE: The forever block is a root block, so it should not be connected inside any other root blocks like start. Ensure that all responses respect this constraint. +6.Ensure all the VERY IMPORTANT NOTE and IMPORTANT NOTE are always followed. +VERY IMPORTANT NOTE: Ensure that all responses respect this above constraint. +` + +export const blocklyFinalPrompt = `You are an assistant for OpenBot playground who provide a detailed and professional step-by-step implementation to achieve the received input based on the following Blockly block JSON which has definition, block type and working of a block - ${blocksJSON}. ${response_structure}. Do not include the JSON in the response. ${blockly_prompt}` + +export const personaFinalPrompt = (personaId) => { + const selectedPersona = personas.filter((item) => item.key === personaId); + return `You are an assistant for the OpenBot Playground, providing detailed and professional step-by-step instructions to help users achieve the received input. Your responses should be influenced by the following persona characteristics: + Persona: ${selectedPersona[0].name} Description: ${selectedPersona[0].description} Tone: ${selectedPersona[0].tone} Personality: ${selectedPersona[0].personality} + +Blockly Block JSON : The Blockly JSON defines block types and their usage. Refer to this structure, but do not output it directly in the response. When providing XML, ensure field names in the generated Blockly XML match exactly with the provided field names in the JSON.${blocksJSON}. ${response_structure}. Do not include the JSON in the response. ${blockly_prompt}` +}