diff --git a/Assets/Scripts/Editor/BoneRendererContextMenu.cs b/Assets/Scripts/Editor/BoneRendererContextMenu.cs new file mode 100644 index 000000000..36e809ac7 --- /dev/null +++ b/Assets/Scripts/Editor/BoneRendererContextMenu.cs @@ -0,0 +1,305 @@ +using UnityEngine; +using UnityEditor; +using UnityEngine.Animations.Rigging; +using System.Collections.Generic; +using System.Linq; + +namespace EditorTools +{ + public static class BoneRendererContextMenu + { + [MenuItem("CONTEXT/BoneRenderer/휴머노이드 본 자동 추가")] + private static void AddHumanoidBones(MenuCommand command) + { + BoneRenderer boneRenderer = command.context as BoneRenderer; + if (boneRenderer == null) return; + + Animator animator = boneRenderer.GetComponent(); + if (animator == null || !animator.isHuman) + { + EditorUtility.DisplayDialog("오류", "휴머노이드 Animator가 필요합니다.", "확인"); + return; + } + + Undo.RecordObject(boneRenderer, "Add Humanoid Bones"); + + List humanoidBones = GetAvailableHumanoidBones(animator); + boneRenderer.transforms = humanoidBones.ToArray(); + + EditorUtility.SetDirty(boneRenderer); + Debug.Log($"{humanoidBones.Count}개의 휴머노이드 본이 BoneRenderer에 추가되었습니다."); + } + + [MenuItem("CONTEXT/BoneRenderer/본 목록 초기화")] + private static void ClearBones(MenuCommand command) + { + BoneRenderer boneRenderer = command.context as BoneRenderer; + if (boneRenderer == null) return; + + if (!EditorUtility.DisplayDialog("확인", "본 목록을 초기화하시겠습니까?", "예", "아니오")) + return; + + Undo.RecordObject(boneRenderer, "Clear Bones"); + + boneRenderer.transforms = new Transform[0]; + + EditorUtility.SetDirty(boneRenderer); + Debug.Log("BoneRenderer의 본 목록이 초기화되었습니다."); + } + + private static readonly HumanBodyBones[] HumanoidBoneTypes = new HumanBodyBones[] + { + HumanBodyBones.Hips, HumanBodyBones.Spine, HumanBodyBones.Chest, HumanBodyBones.UpperChest, + HumanBodyBones.Neck, HumanBodyBones.Head, + HumanBodyBones.LeftShoulder, HumanBodyBones.LeftUpperArm, HumanBodyBones.LeftLowerArm, HumanBodyBones.LeftHand, + HumanBodyBones.LeftThumbProximal, HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal, + HumanBodyBones.LeftIndexProximal, HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal, + HumanBodyBones.LeftMiddleProximal, HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleDistal, + HumanBodyBones.LeftRingProximal, HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal, + HumanBodyBones.LeftLittleProximal, HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal, + HumanBodyBones.RightShoulder, HumanBodyBones.RightUpperArm, HumanBodyBones.RightLowerArm, HumanBodyBones.RightHand, + HumanBodyBones.RightThumbProximal, HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal, + HumanBodyBones.RightIndexProximal, HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal, + HumanBodyBones.RightMiddleProximal, HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleDistal, + HumanBodyBones.RightRingProximal, HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal, + HumanBodyBones.RightLittleProximal, HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal, + HumanBodyBones.LeftUpperLeg, HumanBodyBones.LeftLowerLeg, HumanBodyBones.LeftFoot, HumanBodyBones.LeftToes, + HumanBodyBones.RightUpperLeg, HumanBodyBones.RightLowerLeg, HumanBodyBones.RightFoot, HumanBodyBones.RightToes + }; + + private static List GetAvailableHumanoidBones(Animator animator) + { + List humanoidBones = new List(); + + foreach (HumanBodyBones boneType in HumanoidBoneTypes) + { + Transform bone = animator.GetBoneTransform(boneType); + if (bone != null) + { + humanoidBones.Add(bone); + } + } + + return humanoidBones; + } + } + [CustomEditor(typeof(BoneRenderer))] + [CanEditMultipleObjects] + public class BoneRendererEditorExtension : Editor + { + static readonly GUIContent k_BoneSizeLabel = new GUIContent("Bone Size"); + static readonly GUIContent k_BoneColorLabel = new GUIContent("Color"); + static readonly GUIContent k_BoneShapeLabel = new GUIContent("Shape"); + static readonly GUIContent k_TripodSizeLabel = new GUIContent("Tripod Size"); + + SerializedProperty m_DrawBones; + SerializedProperty m_BoneShape; + SerializedProperty m_BoneSize; + SerializedProperty m_BoneColor; + + SerializedProperty m_DrawTripods; + SerializedProperty m_TripodSize; + + SerializedProperty m_Transforms; + + // 휴머노이드 본 타입 정의 + private static readonly HumanBodyBones[] HumanoidBoneTypes = new HumanBodyBones[] + { + // 스파인 체인 + HumanBodyBones.Hips, + HumanBodyBones.Spine, + HumanBodyBones.Chest, + HumanBodyBones.UpperChest, + HumanBodyBones.Neck, + HumanBodyBones.Head, + + // 왼쪽 팔 + HumanBodyBones.LeftShoulder, + HumanBodyBones.LeftUpperArm, + HumanBodyBones.LeftLowerArm, + HumanBodyBones.LeftHand, + + // 왼쪽 손가락 + HumanBodyBones.LeftThumbProximal, + HumanBodyBones.LeftThumbIntermediate, + HumanBodyBones.LeftThumbDistal, + HumanBodyBones.LeftIndexProximal, + HumanBodyBones.LeftIndexIntermediate, + HumanBodyBones.LeftIndexDistal, + HumanBodyBones.LeftMiddleProximal, + HumanBodyBones.LeftMiddleIntermediate, + HumanBodyBones.LeftMiddleDistal, + HumanBodyBones.LeftRingProximal, + HumanBodyBones.LeftRingIntermediate, + HumanBodyBones.LeftRingDistal, + HumanBodyBones.LeftLittleProximal, + HumanBodyBones.LeftLittleIntermediate, + HumanBodyBones.LeftLittleDistal, + + // 오른쪽 팔 + HumanBodyBones.RightShoulder, + HumanBodyBones.RightUpperArm, + HumanBodyBones.RightLowerArm, + HumanBodyBones.RightHand, + + // 오른쪽 손가락 + HumanBodyBones.RightThumbProximal, + HumanBodyBones.RightThumbIntermediate, + HumanBodyBones.RightThumbDistal, + HumanBodyBones.RightIndexProximal, + HumanBodyBones.RightIndexIntermediate, + HumanBodyBones.RightIndexDistal, + HumanBodyBones.RightMiddleProximal, + HumanBodyBones.RightMiddleIntermediate, + HumanBodyBones.RightMiddleDistal, + HumanBodyBones.RightRingProximal, + HumanBodyBones.RightRingIntermediate, + HumanBodyBones.RightRingDistal, + HumanBodyBones.RightLittleProximal, + HumanBodyBones.RightLittleIntermediate, + HumanBodyBones.RightLittleDistal, + + // 왼쪽 다리 + HumanBodyBones.LeftUpperLeg, + HumanBodyBones.LeftLowerLeg, + HumanBodyBones.LeftFoot, + HumanBodyBones.LeftToes, + + // 오른쪽 다리 + HumanBodyBones.RightUpperLeg, + HumanBodyBones.RightLowerLeg, + HumanBodyBones.RightFoot, + HumanBodyBones.RightToes + }; + + public void OnEnable() + { + m_DrawBones = serializedObject.FindProperty("drawBones"); + m_BoneSize = serializedObject.FindProperty("boneSize"); + m_BoneShape = serializedObject.FindProperty("boneShape"); + m_BoneColor = serializedObject.FindProperty("boneColor"); + + m_DrawTripods = serializedObject.FindProperty("drawTripods"); + m_TripodSize = serializedObject.FindProperty("tripodSize"); + + m_Transforms = serializedObject.FindProperty("m_Transforms"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + // 기본 BoneRenderer UI + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PropertyField(m_DrawBones, k_BoneSizeLabel); + using (new EditorGUI.DisabledScope(!m_DrawBones.boolValue)) + EditorGUILayout.PropertyField(m_BoneSize, GUIContent.none); + EditorGUILayout.EndHorizontal(); + + using (new EditorGUI.DisabledScope(!m_DrawBones.boolValue)) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_BoneShape, k_BoneShapeLabel); + EditorGUILayout.PropertyField(m_BoneColor, k_BoneColorLabel); + EditorGUI.indentLevel--; + } + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PropertyField(m_DrawTripods, k_TripodSizeLabel); + using (new EditorGUI.DisabledScope(!m_DrawTripods.boolValue)) + EditorGUILayout.PropertyField(m_TripodSize, GUIContent.none); + EditorGUILayout.EndHorizontal(); + + bool isDragPerformed = Event.current.type == EventType.DragPerform; + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(m_Transforms, true); + bool boneRendererDirty = EditorGUI.EndChangeCheck(); + boneRendererDirty |= Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed"; + boneRendererDirty |= Event.current.type == EventType.Used && isDragPerformed; + + // 휴머노이드 본 자동 설정 섹션 + EditorGUILayout.Space(); + EditorGUILayout.LabelField("휴머노이드 본 자동 설정", EditorStyles.boldLabel); + + BoneRenderer boneRenderer = target as BoneRenderer; + GameObject targetObject = boneRenderer.gameObject; + + // Animator 확인 및 상태 표시 + Animator animator = targetObject.GetComponent(); + if (animator == null) + { + EditorGUILayout.HelpBox("Animator 컴포넌트가 필요합니다.", MessageType.Warning); + } + else if (!animator.isHuman) + { + EditorGUILayout.HelpBox("휴머노이드 아바타로 설정되지 않았습니다.", MessageType.Warning); + } + else + { + // 사용 가능한 본 수 표시 + List availableBones = GetAvailableHumanoidBones(animator); + EditorGUILayout.HelpBox($"사용 가능한 휴머노이드 본: {availableBones.Count}개", MessageType.Info); + + // 버튼들 + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button("휴머노이드 본 추가")) + { + AddHumanoidBones(boneRenderer, animator); + } + if (GUILayout.Button("본 목록 초기화")) + { + ClearBones(boneRenderer); + } + EditorGUILayout.EndHorizontal(); + } + + serializedObject.ApplyModifiedProperties(); + + if (boneRendererDirty) + { + for (int i = 0; i < targets.Length; i++) + { + var renderer = targets[i] as BoneRenderer; + renderer.ExtractBones(); + } + } + } + + private List GetAvailableHumanoidBones(Animator animator) + { + List humanoidBones = new List(); + + foreach (HumanBodyBones boneType in HumanoidBoneTypes) + { + Transform bone = animator.GetBoneTransform(boneType); + if (bone != null) + { + humanoidBones.Add(bone); + } + } + + return humanoidBones; + } + + private void AddHumanoidBones(BoneRenderer boneRenderer, Animator animator) + { + Undo.RecordObject(boneRenderer, "Add Humanoid Bones"); + + List humanoidBones = GetAvailableHumanoidBones(animator); + boneRenderer.transforms = humanoidBones.ToArray(); + + EditorUtility.SetDirty(boneRenderer); + Debug.Log($"{humanoidBones.Count}개의 휴머노이드 본이 BoneRenderer에 추가되었습니다."); + } + + private void ClearBones(BoneRenderer boneRenderer) + { + Undo.RecordObject(boneRenderer, "Clear Bones"); + + boneRenderer.transforms = new Transform[0]; + + EditorUtility.SetDirty(boneRenderer); + Debug.Log("BoneRenderer의 본 목록이 초기화되었습니다."); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Editor/BoneRendererContextMenu.cs.meta b/Assets/Scripts/Editor/BoneRendererContextMenu.cs.meta new file mode 100644 index 000000000..85a213172 --- /dev/null +++ b/Assets/Scripts/Editor/BoneRendererContextMenu.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c2ae7891ff94b484aa3afa02e62e8792 \ No newline at end of file diff --git a/Assets/Scripts/Editor/StreamingleControllerSetupToolAdvanced.cs b/Assets/Scripts/Editor/StreamingleControllerSetupToolAdvanced.cs index f0e75ba1c..371ee5445 100644 --- a/Assets/Scripts/Editor/StreamingleControllerSetupToolAdvanced.cs +++ b/Assets/Scripts/Editor/StreamingleControllerSetupToolAdvanced.cs @@ -9,6 +9,7 @@ namespace Streamingle.Editor private bool createItemController = true; private bool createEventController = true; private bool createStreamDeckManager = true; + private bool createAvatarOutfitController = true; private string parentObjectName = "Streamingle 컨트롤러들"; private bool createParentObject = true; @@ -24,6 +25,7 @@ namespace Streamingle.Editor private CameraManager existingCameraManager; private ItemController existingItemController; private EventController existingEventController; + private AvatarOutfitController existingAvatarOutfitController; [MenuItem("Tools/Streamingle/고급 컨트롤러 설정 도구")] public static void ShowWindow() @@ -66,6 +68,7 @@ namespace Streamingle.Editor createCameraManager = EditorGUILayout.Toggle("카메라 매니저", createCameraManager); createItemController = EditorGUILayout.Toggle("아이템 컨트롤러", createItemController); createEventController = EditorGUILayout.Toggle("이벤트 컨트롤러", createEventController); + createAvatarOutfitController = EditorGUILayout.Toggle("아바타 의상 컨트롤러", createAvatarOutfitController); EditorGUILayout.EndVertical(); @@ -183,6 +186,19 @@ namespace Streamingle.Editor } EditorGUILayout.EndHorizontal(); + EditorGUILayout.BeginHorizontal(); + GUILayout.Label("아바타 의상 컨트롤러:", GUILayout.Width(200)); + if (existingAvatarOutfitController != null) + { + string parentInfo = GetParentInfo(existingAvatarOutfitController.transform); + EditorGUILayout.LabelField($"✓ 발견됨 {parentInfo}", EditorStyles.boldLabel); + } + else + { + EditorGUILayout.LabelField("✗ 발견되지 않음", EditorStyles.boldLabel); + } + EditorGUILayout.EndHorizontal(); + EditorGUILayout.EndVertical(); } @@ -204,6 +220,7 @@ namespace Streamingle.Editor existingCameraManager = FindObjectOfType(); existingItemController = FindObjectOfType(); existingEventController = FindObjectOfType(); + existingAvatarOutfitController = FindObjectOfType(); } private void CreateControllers() @@ -241,6 +258,12 @@ namespace Streamingle.Editor CreateEventController(parentObject); } + // Avatar Outfit Controller 생성 + if (createAvatarOutfitController && existingAvatarOutfitController == null) + { + CreateAvatarOutfitController(parentObject); + } + // 기존 컨트롤러들을 부모 하위로 이동 if (moveExistingControllersToParent && parentObject != null) { @@ -307,6 +330,14 @@ namespace Streamingle.Editor UnityEngine.Debug.Log($"이벤트 컨트롤러를 {parent.name} 하위로 이동"); } + // Avatar Outfit Controller 이동 + if (existingAvatarOutfitController != null && existingAvatarOutfitController.transform.parent != parent.transform) + { + existingAvatarOutfitController.transform.SetParent(parent.transform); + movedCount++; + UnityEngine.Debug.Log($"아바타 의상 컨트롤러를 {parent.name} 하위로 이동"); + } + if (movedCount > 0) { UnityEngine.Debug.Log($"{movedCount}개의 컨트롤러를 {parent.name} 하위로 이동했습니다."); @@ -363,6 +394,16 @@ namespace Streamingle.Editor } } + // Avatar Outfit Controller 연결 + if (existingAvatarOutfitController != null) + { + var avatarOutfitControllerProperty = serializedObject.FindProperty("avatarOutfitController"); + if (avatarOutfitControllerProperty != null) + { + avatarOutfitControllerProperty.objectReferenceValue = existingAvatarOutfitController; + } + } + serializedObject.ApplyModifiedProperties(); UnityEngine.Debug.Log("기존 컨트롤러들을 StreamDeck 서버 매니저에 연결했습니다!"); @@ -489,5 +530,41 @@ namespace Streamingle.Editor UnityEngine.Debug.Log("이벤트 컨트롤러 생성됨"); } + + private void CreateAvatarOutfitController(GameObject parent) + { + GameObject avatarOutfitObject = new GameObject("아바타 의상 컨트롤러"); + + if (parent != null) + { + avatarOutfitObject.transform.SetParent(parent.transform); + } + + // AvatarOutfitController 스크립트 추가 + var avatarOutfitController = avatarOutfitObject.AddComponent(); + + // 기본 설정 + SerializedObject serializedObject = new SerializedObject(avatarOutfitController); + serializedObject.Update(); + + // 아바타 리스트 초기화 + var avatarsProperty = serializedObject.FindProperty("avatars"); + if (avatarsProperty != null) + { + avatarsProperty.ClearArray(); + avatarsProperty.arraySize = 0; + } + + // 자동 찾기 비활성화 + var autoFindAvatarsProperty = serializedObject.FindProperty("autoFindAvatars"); + if (autoFindAvatarsProperty != null) + { + autoFindAvatarsProperty.boolValue = false; + } + + serializedObject.ApplyModifiedProperties(); + + UnityEngine.Debug.Log("아바타 의상 컨트롤러 생성됨"); + } } } \ No newline at end of file diff --git a/Assets/Scripts/KindRetargeting/SimplePoseTransfer.cs b/Assets/Scripts/KindRetargeting/SimplePoseTransfer.cs new file mode 100644 index 000000000..c493f21cc --- /dev/null +++ b/Assets/Scripts/KindRetargeting/SimplePoseTransfer.cs @@ -0,0 +1,136 @@ +using UnityEngine; + +[DefaultExecutionOrder(16001)] +public class SimplePoseTransfer : MonoBehaviour +{ + [Header("Pose Transfer Settings")] + public Animator sourceBone; + public Animator[] targetBones; + + // 캐싱된 Transform들 + private Transform[] cachedSourceBones; + private Transform[,] cachedTargetBones; + + // 각 targetBone의 초기 회전 차이를 저장 + private Quaternion[,] boneRotationDifferences; + + private void Start() + { + Init(); + } + + public void Init() + { + if (targetBones == null || targetBones.Length == 0) + { + Debug.LogError("Target bones are null or empty"); + return; + } + + if (sourceBone == null) + { + Debug.LogError("Source bone is null"); + return; + } + + InitializeTargetBones(); + CacheAllBoneTransforms(); + + Debug.Log($"SimplePoseTransfer initialized with {targetBones.Length} targets"); + } + + private void InitializeTargetBones() + { + boneRotationDifferences = new Quaternion[targetBones.Length, 55]; + + for (int i = 0; i < targetBones.Length; i++) + { + if (targetBones[i] == null) + { + Debug.LogError($"targetBones[{i}] is null"); + continue; + } + + // 55개의 휴머노이드 본에 대해 회전 차이 계산 + for (int j = 0; j < 55; j++) + { + Transform sourceBoneTransform = sourceBone.GetBoneTransform((HumanBodyBones)j); + Transform targetBoneTransform = targetBones[i].GetBoneTransform((HumanBodyBones)j); + + if (sourceBoneTransform != null && targetBoneTransform != null) + { + boneRotationDifferences[i, j] = Quaternion.Inverse(sourceBoneTransform.rotation) * targetBoneTransform.rotation; + } + } + } + } + + private void CacheAllBoneTransforms() + { + // 소스 본 캐싱 + cachedSourceBones = new Transform[55]; + for (int i = 0; i < 55; i++) + { + cachedSourceBones[i] = sourceBone.GetBoneTransform((HumanBodyBones)i); + } + + // 타겟 본 캐싱 + cachedTargetBones = new Transform[targetBones.Length, 55]; + for (int t = 0; t < targetBones.Length; t++) + { + for (int i = 0; i < 55; i++) + { + cachedTargetBones[t, i] = targetBones[t].GetBoneTransform((HumanBodyBones)i); + } + } + } + + private void LateUpdate() + { + TransferPoses(); + } + + private void TransferPoses() + { + if (sourceBone == null) + { + return; + } + + for (int i = 0; i < targetBones.Length; i++) + { + if (targetBones[i] != null && targetBones[i].gameObject.activeInHierarchy) + { + TransferPoseToTarget(i); + } + } + } + + private void TransferPoseToTarget(int targetIndex) + { + Animator targetBone = targetBones[targetIndex]; + + // 루트 회전 동기화 + targetBone.transform.rotation = sourceBone.transform.rotation; + + // 모든 본에 대해 포즈 전송 + for (int i = 0; i < 55; i++) + { + Transform targetBoneTransform = cachedTargetBones[targetIndex, i]; + Transform sourceBoneTransform = cachedSourceBones[i]; + + if (targetBoneTransform != null && sourceBoneTransform != null) + { + // 회전 적용 + targetBoneTransform.rotation = sourceBoneTransform.rotation * boneRotationDifferences[targetIndex, i]; + + // 펠비스 위치 동기화 (HumanBodyBones.Hips = 0) + if (i == 0) + { + targetBoneTransform.position = sourceBoneTransform.position; + } + } + } + } + +} \ No newline at end of file diff --git a/Assets/Scripts/KindRetargeting/SimplePoseTransfer.cs.meta b/Assets/Scripts/KindRetargeting/SimplePoseTransfer.cs.meta new file mode 100644 index 000000000..c13c628bd --- /dev/null +++ b/Assets/Scripts/KindRetargeting/SimplePoseTransfer.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: aa7ade46b95571a4a850f9b5d32c6886 \ No newline at end of file diff --git a/Assets/Scripts/Streamdeck/StreamDeckServerManager.cs b/Assets/Scripts/Streamdeck/StreamDeckServerManager.cs index f69ea59df..7eb7dfc56 100644 --- a/Assets/Scripts/Streamdeck/StreamDeckServerManager.cs +++ b/Assets/Scripts/Streamdeck/StreamDeckServerManager.cs @@ -15,6 +15,7 @@ public class StreamDeckServerManager : MonoBehaviour public CameraManager cameraManager { get; private set; } public ItemController itemController { get; private set; } public EventController eventController { get; private set; } + public AvatarOutfitController avatarOutfitController { get; private set; } // 싱글톤 패턴으로 StreamDeckService에서 접근 가능하도록 public static StreamDeckServerManager Instance { get; private set; } @@ -52,6 +53,13 @@ public class StreamDeckServerManager : MonoBehaviour Debug.LogWarning("[StreamDeckServerManager] EventController를 찾을 수 없습니다. 이벤트 컨트롤 기능이 비활성화됩니다."); } + // AvatarOutfitController 찾기 + avatarOutfitController = FindObjectOfType(); + if (avatarOutfitController == null) + { + Debug.LogWarning("[StreamDeckServerManager] AvatarOutfitController를 찾을 수 없습니다. 아바타 의상 컨트롤 기능이 비활성화됩니다."); + } + StartServer(); } @@ -162,7 +170,9 @@ public class StreamDeckServerManager : MonoBehaviour item_data = itemController?.GetItemListData(), current_item = itemController?.GetCurrentItemState(), event_data = eventController?.GetEventListData(), - current_event = eventController?.GetCurrentEventState() + current_event = eventController?.GetCurrentEventState(), + avatar_outfit_data = avatarOutfitController?.GetAvatarOutfitListData(), + current_avatar_outfit = avatarOutfitController?.GetCurrentAvatarOutfitState() } }; @@ -253,6 +263,28 @@ public class StreamDeckServerManager : MonoBehaviour Debug.Log("[StreamDeckServerManager] 이벤트 변경 알림 전송됨"); } + // 아바타 의상 변경 시 모든 클라이언트에게 알림 + public void NotifyAvatarOutfitChanged() + { + if (avatarOutfitController == null) return; + + var updateMessage = new + { + type = "avatar_outfit_changed", + timestamp = DateTime.UtcNow.ToString("o"), + version = "1.0", + data = new + { + avatar_outfit_data = avatarOutfitController.GetAvatarOutfitListData(), + current_avatar_outfit = avatarOutfitController.GetCurrentAvatarOutfitState() + } + }; + + string json = JsonConvert.SerializeObject(updateMessage); + BroadcastMessage(json); + Debug.Log("[StreamDeckServerManager] 아바타 의상 변경 알림 전송됨"); + } + // 메시지 처리 로직을 여기로 이동 private void ProcessMessage(string messageData, StreamDeckService service) { @@ -294,6 +326,15 @@ public class StreamDeckServerManager : MonoBehaviour HandleGetEventList(service); break; + case "set_avatar_outfit": + HandleSetAvatarOutfit(message); + break; + + + case "get_avatar_outfit_list": + HandleGetAvatarOutfitList(service); + break; + case "test": // 테스트 메시지 에코 응답 var response = new @@ -827,6 +868,129 @@ public class StreamDeckServerManager : MonoBehaviour string json = JsonConvert.SerializeObject(response); service.SendMessage(json); } + + private void HandleSetAvatarOutfit(Dictionary message) + { + Debug.Log($"[StreamDeckServerManager] 아바타 의상 설정 요청 수신"); + + if (avatarOutfitController == null) + { + Debug.LogError("[StreamDeckServerManager] avatarOutfitController가 null입니다!"); + return; + } + + try + { + if (message.ContainsKey("data")) + { + var dataObject = message["data"]; + + if (dataObject is Newtonsoft.Json.Linq.JObject jObject) + { + if (jObject.ContainsKey("avatar_index") && jObject.ContainsKey("outfit_index")) + { + var avatarIndexToken = jObject["avatar_index"]; + var outfitIndexToken = jObject["outfit_index"]; + + if (int.TryParse(avatarIndexToken?.ToString(), out int avatarIndex) && + int.TryParse(outfitIndexToken?.ToString(), out int outfitIndex)) + { + if (avatarIndex >= 0 && avatarIndex < (avatarOutfitController.avatars?.Count ?? 0)) + { + Debug.Log($"[StreamDeckServerManager] 아바타 {avatarIndex}번 의상을 {outfitIndex}번으로 설정"); + avatarOutfitController.SetAvatarOutfit(avatarIndex, outfitIndex); + } + else + { + Debug.LogError($"[StreamDeckServerManager] 잘못된 아바타 인덱스: {avatarIndex}, 유효 범위: 0-{(avatarOutfitController.avatars?.Count ?? 0) - 1}"); + } + } + else + { + Debug.LogError($"[StreamDeckServerManager] 인덱스 파싱 실패: avatar_index={avatarIndexToken}, outfit_index={outfitIndexToken}"); + } + } + else + { + Debug.LogError("[StreamDeckServerManager] data에 'avatar_index' 또는 'outfit_index' 키가 없습니다"); + } + } + else if (dataObject is Dictionary data) + { + if (data.ContainsKey("avatar_index") && data.ContainsKey("outfit_index")) + { + var avatarIndexObj = data["avatar_index"]; + var outfitIndexObj = data["outfit_index"]; + + if (int.TryParse(avatarIndexObj?.ToString(), out int avatarIndex) && + int.TryParse(outfitIndexObj?.ToString(), out int outfitIndex)) + { + if (avatarIndex >= 0 && avatarIndex < (avatarOutfitController.avatars?.Count ?? 0)) + { + Debug.Log($"[StreamDeckServerManager] 아바타 {avatarIndex}번 의상을 {outfitIndex}번으로 설정"); + avatarOutfitController.SetAvatarOutfit(avatarIndex, outfitIndex); + } + else + { + Debug.LogError($"[StreamDeckServerManager] 잘못된 아바타 인덱스: {avatarIndex}, 유효 범위: 0-{(avatarOutfitController.avatars?.Count ?? 0) - 1}"); + } + } + else + { + Debug.LogError($"[StreamDeckServerManager] 인덱스 파싱 실패: avatar_index={avatarIndexObj}, outfit_index={outfitIndexObj}"); + } + } + else + { + Debug.LogError("[StreamDeckServerManager] data에 'avatar_index' 또는 'outfit_index' 키가 없습니다"); + } + } + else + { + Debug.LogError($"[StreamDeckServerManager] data가 예상된 타입이 아닙니다: {dataObject?.GetType().Name}"); + } + } + else + { + Debug.LogError("[StreamDeckServerManager] 메시지에 'data' 키가 없습니다"); + } + } + catch (Exception ex) + { + Debug.LogError($"[StreamDeckServerManager] 아바타 의상 설정 실패: {ex.Message}"); + } + } + + + private void HandleGetAvatarOutfitList(StreamDeckService service) + { + Debug.Log("[StreamDeckServerManager] 아바타 의상 목록 요청 처리 시작"); + + if (avatarOutfitController == null) + { + Debug.LogError("[StreamDeckServerManager] avatarOutfitController가 null입니다!"); + return; + } + + var avatarData = avatarOutfitController.GetAvatarOutfitListData(); + Debug.Log($"[StreamDeckServerManager] 아바타 데이터: {JsonConvert.SerializeObject(avatarData)}"); + + var response = new + { + type = "avatar_outfit_list_response", + timestamp = DateTime.UtcNow.ToString("o"), + version = "1.0", + data = new + { + avatar_outfit_data = avatarData, + current_avatar_outfit = avatarOutfitController.GetCurrentAvatarOutfitState() + } + }; + + string json = JsonConvert.SerializeObject(response); + Debug.Log($"[StreamDeckServerManager] 아바타 목록 응답 전송: {json}"); + service.SendMessage(json); + } } public class StreamDeckService : WebSocketBehavior diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/AvatarOutfitController.cs b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/AvatarOutfitController.cs new file mode 100644 index 000000000..92b9dd04f --- /dev/null +++ b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/AvatarOutfitController.cs @@ -0,0 +1,376 @@ +using UnityEngine; +using System; +using System.Collections.Generic; +using System.Linq; +using Streamingle; +using Newtonsoft.Json; + +public class AvatarOutfitController : MonoBehaviour, IController +{ + #region Classes + [System.Serializable] + public class AvatarData + { + [Header("Avatar Settings")] + public string avatarName = "New Avatar"; + public GameObject avatarObject; + + [Header("Outfit Settings")] + public OutfitData[] outfits = new OutfitData[0]; + + + // 현재 착용 중인 의상 인덱스 + [SerializeField] private int currentOutfitIndex = 0; + + public int CurrentOutfitIndex => currentOutfitIndex; + public OutfitData CurrentOutfit => outfits != null && outfits.Length > currentOutfitIndex ? outfits[currentOutfitIndex] : null; + + public AvatarData(string name) + { + avatarName = name; + outfits = new OutfitData[0]; + } + + public void SetOutfit(int outfitIndex) + { + if (outfits == null || outfitIndex < 0 || outfitIndex >= outfits.Length) + { + Debug.LogWarning($"[AvatarData] 잘못된 의상 인덱스: {outfitIndex}"); + return; + } + + // 현재 의상 제거 + if (CurrentOutfit != null) + { + CurrentOutfit.RemoveOutfit(); + } + + // 새 의상 적용 + currentOutfitIndex = outfitIndex; + if (CurrentOutfit != null) + { + CurrentOutfit.ApplyOutfit(); + Debug.Log($"[AvatarData] {avatarName} 의상 변경: {CurrentOutfit.outfitName}"); + } + } + + + public override string ToString() => avatarName; + } + + [System.Serializable] + public class OutfitData + { + [Header("Outfit Info")] + public string outfitName = "New Outfit"; + + [Header("Clothing GameObjects")] + public GameObject[] clothingObjects = new GameObject[0]; // 활성화할 의상 오브젝트들 + public GameObject[] hideObjects = new GameObject[0]; // 비활성화할 오브젝트들 (기본 의상 등) + + public void ApplyOutfit() + { + // 의상 오브젝트들 활성화 + foreach (var obj in clothingObjects) + { + if (obj != null) obj.SetActive(true); + } + + // 숨겨야 할 오브젝트들 비활성화 + foreach (var obj in hideObjects) + { + if (obj != null) obj.SetActive(false); + } + } + + public void RemoveOutfit() + { + // 의상 오브젝트들 비활성화 + foreach (var obj in clothingObjects) + { + if (obj != null) obj.SetActive(false); + } + + // 숨겨진 오브젝트들 다시 활성화 + foreach (var obj in hideObjects) + { + if (obj != null) obj.SetActive(true); + } + } + } + + #endregion + + #region Events + public delegate void AvatarOutfitChangedEventHandler(AvatarData avatar, OutfitData oldOutfit, OutfitData newOutfit); + public event AvatarOutfitChangedEventHandler OnAvatarOutfitChanged; + #endregion + + #region Fields + [SerializeField] public List avatars = new List(); + + [Header("Avatar Control Settings")] + [SerializeField] private bool autoFindAvatars = false; + [SerializeField] private string avatarTag = "Avatar"; + + private AvatarData currentAvatar; + private StreamDeckServerManager streamDeckManager; + #endregion + + #region Properties + public AvatarData CurrentAvatar => currentAvatar; + public int CurrentAvatarIndex => avatars.IndexOf(currentAvatar); + #endregion + + #region Unity Messages + private void Awake() + { + InitializeAvatars(); + + // StreamDeckServerManager 찾기 + streamDeckManager = FindObjectOfType(); + if (streamDeckManager == null) + { + Debug.LogWarning("[AvatarOutfitController] StreamDeckServerManager를 찾을 수 없습니다. 스트림덱 연동이 비활성화됩니다."); + } + } + + + #endregion + + #region Initialization + public void InitializeAvatars() + { + if (avatars == null) + { + avatars = new List(); + } + + if (autoFindAvatars && avatars.Count == 0) + { + // "Avatar" 태그가 붙은 모든 오브젝트를 찾아서 아바타로 등록 + var avatarObjects = GameObject.FindGameObjectsWithTag(avatarTag); + foreach (var avatarObj in avatarObjects) + { + var avatar = new AvatarData(avatarObj.name); + avatar.avatarObject = avatarObj; + avatars.Add(avatar); + } + Debug.Log($"[AvatarOutfitController] {avatars.Count}개의 아바타를 자동으로 찾았습니다."); + } + + // 유효하지 않은 아바타 제거 + avatars.RemoveAll(avatar => avatar == null || avatar.avatarObject == null); + + // 첫 번째 아바타를 기본으로 설정 + if (avatars.Count > 0 && currentAvatar == null) + { + currentAvatar = avatars[0]; + } + + Debug.Log($"[AvatarOutfitController] 총 {avatars.Count}개의 아바타가 등록되었습니다."); + } + + #endregion + + + #region Public Methods + public void Set(int index) + { + // IController 인터페이스 구현 - 첫 번째 아바타의 특정 의상으로 설정 + if (avatars.Count > 0) + { + SetAvatarOutfit(0, index); + } + else + { + Debug.LogWarning("[AvatarOutfitController] 설정할 아바타가 없습니다."); + } + } + + public void SetAvatarOutfit(int avatarIndex, int outfitIndex) + { + if (avatarIndex < 0 || avatarIndex >= avatars.Count) + { + Debug.LogWarning($"[AvatarOutfitController] 잘못된 아바타 인덱스: {avatarIndex}"); + return; + } + + var avatar = avatars[avatarIndex]; + var oldOutfit = avatar.CurrentOutfit; + + avatar.SetOutfit(outfitIndex); + + // 이벤트 발생 + OnAvatarOutfitChanged?.Invoke(avatar, oldOutfit, avatar.CurrentOutfit); + + // StreamDeck에 알림 + NotifyAvatarOutfitChanged(avatar); + + Debug.Log($"[AvatarOutfitController] 아바타 의상 변경: {avatar.avatarName} -> {avatar.CurrentOutfit?.outfitName}"); + } + + + public void AddAvatar(string avatarName, GameObject avatarObject = null) + { + var newAvatar = new AvatarData(avatarName); + if (avatarObject != null) + { + newAvatar.avatarObject = avatarObject; + } + + avatars.Add(newAvatar); + NotifyAvatarOutfitChanged(newAvatar); + Debug.Log($"[AvatarOutfitController] 아바타 추가: {avatarName}"); + } + + public void RemoveAvatar(int index) + { + if (index < 0 || index >= avatars.Count) return; + + var removedAvatar = avatars[index]; + avatars.RemoveAt(index); + + // 현재 아바타가 제거된 경우 첫 번째 아바타로 변경 + if (removedAvatar == currentAvatar) + { + currentAvatar = avatars.Count > 0 ? avatars[0] : null; + } + + Debug.Log($"[AvatarOutfitController] 아바타 제거: {removedAvatar.avatarName}"); + } + + #endregion + + #region StreamDeck Integration + private void NotifyAvatarOutfitChanged(AvatarData avatar) + { + if (streamDeckManager != null) + { + streamDeckManager.NotifyAvatarOutfitChanged(); + } + } + + public AvatarOutfitListData GetAvatarOutfitListData() + { + return new AvatarOutfitListData + { + avatar_count = avatars.Count, + avatars = avatars.Select((a, i) => new AvatarPresetData + { + index = i, + name = a.avatarName, + current_outfit_index = a.CurrentOutfitIndex, + current_outfit_name = a.CurrentOutfit?.outfitName ?? "없음", + outfits = a.outfits?.Select((o, oi) => new OutfitPresetData + { + index = oi, + name = o.outfitName + }).ToArray() ?? new OutfitPresetData[0], + hotkey = "스트림덱 전용" + }).ToArray(), + current_avatar_index = CurrentAvatarIndex + }; + } + + public AvatarOutfitStateData GetCurrentAvatarOutfitState() + { + if (currentAvatar == null) return null; + + return new AvatarOutfitStateData + { + current_avatar_index = CurrentAvatarIndex, + avatar_name = currentAvatar.avatarName, + current_outfit_index = currentAvatar.CurrentOutfitIndex, + current_outfit_name = currentAvatar.CurrentOutfit?.outfitName ?? "없음", + total_avatars = avatars.Count + }; + } + + public string GetAvatarOutfitListJson() + { + return JsonConvert.SerializeObject(GetAvatarOutfitListData()); + } + + public string GetAvatarOutfitStateJson() + { + return JsonConvert.SerializeObject(GetCurrentAvatarOutfitState()); + } + #endregion + + #region Data Classes + [System.Serializable] + public class OutfitPresetData + { + public int index; + public string name; + } + + [System.Serializable] + public class AvatarPresetData + { + public int index; + public string name; + public int current_outfit_index; + public string current_outfit_name; + public OutfitPresetData[] outfits; + public string hotkey; + } + + [System.Serializable] + public class AvatarOutfitListData + { + public int avatar_count; + public AvatarPresetData[] avatars; + public int current_avatar_index; + } + + [System.Serializable] + public class AvatarOutfitStateData + { + public int current_avatar_index; + public string avatar_name; + public int current_outfit_index; + public string current_outfit_name; + public int total_avatars; + } + #endregion + + #region IController Implementation + public string GetControllerId() + { + return "avatar_outfit_controller"; + } + + public string GetControllerName() + { + return "Avatar Outfit Controller"; + } + + public object GetControllerData() + { + return GetAvatarOutfitListData(); + } + + public void ExecuteAction(string actionId, object parameters) + { + switch (actionId) + { + case "set_avatar_outfit": + if (parameters is Dictionary setParams) + { + if (setParams.ContainsKey("avatar_index") && setParams.ContainsKey("outfit_index")) + { + int avatarIndex = Convert.ToInt32(setParams["avatar_index"]); + int outfitIndex = Convert.ToInt32(setParams["outfit_index"]); + SetAvatarOutfit(avatarIndex, outfitIndex); + } + } + break; + default: + Debug.LogWarning($"[AvatarOutfitController] 알 수 없는 액션: {actionId}"); + break; + } + } + #endregion +} \ No newline at end of file diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/AvatarOutfitController.cs.meta b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/AvatarOutfitController.cs.meta new file mode 100644 index 000000000..43f0832e7 --- /dev/null +++ b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/AvatarOutfitController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 22f54f611910ea3468bd422844360c12 \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/README.md b/Streamdeck/com.mirabox.streamingle.sdPlugin/README.md deleted file mode 100644 index 910b61cf0..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/README.md +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b82b35887a538e416ea83662e653b0b0ff00cf70246f64f34e448efca02306be -size 4343 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/build.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/build.js deleted file mode 100644 index 08a0f2e01..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/build.js +++ /dev/null @@ -1,288 +0,0 @@ -#!/usr/bin/env node - -/** - * Streamingle Camera Plugin Build Script - * StreamDock 플러그인 폴더에 자동 복사 기능 포함 - */ - -const fs = require('fs'); -const path = require('path'); - -// 설정 -const PLUGIN_NAME = 'com.mirabox.streamingle.sdPlugin'; -const STREAMDOCK_PLUGINS_PATH = 'C:\\Users\\qscft\\AppData\\Roaming\\HotSpot\\StreamDock\\plugins'; -const CURRENT_DIR = __dirname; - -// 색상 출력 함수 -function log(message, color = 'white') { - const colors = { - red: '\x1b[31m', - green: '\x1b[32m', - yellow: '\x1b[33m', - blue: '\x1b[34m', - magenta: '\x1b[35m', - cyan: '\x1b[36m', - white: '\x1b[37m', - reset: '\x1b[0m' - }; - console.log(`${colors[color]}${message}${colors.reset}`); -} - -// 디렉토리 복사 함수 (제외할 파일/폴더 필터링 포함) -function copyDirectory(src, dest) { - // 제외할 파일/폴더 목록 - const excludePatterns = [ - 'dist', - 'dist-dev', - 'node_modules', - '.git', - '.gitignore', - 'build.js', - 'build.log', - 'README.md' - // package.json과 package-lock.json은 제외하지 않음 (MiraBox StreamDock에서 필요할 수 있음) - ]; - - if (!fs.existsSync(dest)) { - fs.mkdirSync(dest, { recursive: true }); - } - - const entries = fs.readdirSync(src, { withFileTypes: true }); - - for (const entry of entries) { - // 제외할 파일/폴더 체크 - if (excludePatterns.includes(entry.name)) { - console.log(`⏭️ 제외: ${entry.name}`); - continue; - } - - const srcPath = path.join(src, entry.name); - const destPath = path.join(dest, entry.name); - - if (entry.isDirectory()) { - copyDirectory(srcPath, destPath); - } else { - fs.copyFileSync(srcPath, destPath); - } - } - } - - // 디렉토리 삭제 함수 -function removeDirectory(dirPath) { - if (fs.existsSync(dirPath)) { - fs.rmSync(dirPath, { recursive: true, force: true }); - } - } - -// 빌드 함수 - StreamDock 플러그인 폴더에 직접 빌드 -function build() { - log('🔨 Streamingle Camera Plugin 빌드 시작...', 'cyan'); - - const buildDir = path.join(STREAMDOCK_PLUGINS_PATH, PLUGIN_NAME); - const pluginDir = CURRENT_DIR; - - // StreamDock 플러그인 폴더 확인 - if (!fs.existsSync(STREAMDOCK_PLUGINS_PATH)) { - log('❌ StreamDock 플러그인 폴더를 찾을 수 없습니다', 'red'); - log(`📁 예상 경로: ${STREAMDOCK_PLUGINS_PATH}`, 'yellow'); - return false; - } - - // 기존 플러그인 삭제 - if (fs.existsSync(buildDir)) { - removeDirectory(buildDir); - log('🗑️ 기존 플러그인 삭제 완료', 'yellow'); - } - - // 빌드 폴더 생성 - fs.mkdirSync(buildDir, { recursive: true }); - - // 플러그인 폴더 복사 - if (fs.existsSync(pluginDir)) { - copyDirectory(pluginDir, buildDir); - log('✅ 플러그인 파일 복사 완료', 'green'); - } else { - log('❌ 플러그인 폴더를 찾을 수 없습니다', 'red'); - return false; - } - - log('🎉 빌드 완료!', 'green'); - log(`📁 빌드 위치: ${buildDir}`, 'blue'); - log('💡 StreamDock을 재시작하면 플러그인이 활성화됩니다', 'cyan'); - return true; - } - -// 개발 빌드 함수 -function buildDev() { - log('🔨 Streamingle Camera Plugin 개발 빌드 시작...', 'cyan'); - - const buildDir = path.join(CURRENT_DIR, 'dist-dev'); - const pluginDir = CURRENT_DIR; - - // 기존 빌드 폴더 정리 - if (fs.existsSync(buildDir)) { - removeDirectory(buildDir); - } - - // 빌드 폴더 생성 - fs.mkdirSync(buildDir, { recursive: true }); - - // 플러그인 폴더 복사 - if (fs.existsSync(pluginDir)) { - copyDirectory(pluginDir, path.join(buildDir, PLUGIN_NAME)); - log('✅ 플러그인 파일 복사 완료', 'green'); - } else { - log('❌ 플러그인 폴더를 찾을 수 없습니다', 'red'); - return false; - } - - log('🎉 개발 빌드 완료!', 'green'); - log(`📁 빌드 위치: ${buildDir}`, 'blue'); - return true; -} - -// StreamDock에 배포 함수 -function deploy() { - log('🚀 StreamDock에 플러그인 배포 시작...', 'magenta'); - - const pluginDir = CURRENT_DIR; - const streamdockPluginPath = path.join(STREAMDOCK_PLUGINS_PATH, PLUGIN_NAME); - - // StreamDock 플러그인 폴더 확인 - if (!fs.existsSync(STREAMDOCK_PLUGINS_PATH)) { - log('❌ StreamDock 플러그인 폴더를 찾을 수 없습니다', 'red'); - log(`📁 예상 경로: ${STREAMDOCK_PLUGINS_PATH}`, 'yellow'); - return false; - } - - // 기존 플러그인 삭제 - if (fs.existsSync(streamdockPluginPath)) { - removeDirectory(streamdockPluginPath); - log('🗑️ 기존 플러그인 삭제 완료', 'yellow'); - } - - // 새 플러그인 복사 - if (fs.existsSync(pluginDir)) { - copyDirectory(pluginDir, streamdockPluginPath); - log('✅ 플러그인 복사 완료', 'green'); - } else { - log('❌ 플러그인 폴더를 찾을 수 없습니다', 'red'); - return false; - } - - log('🎉 StreamDock 배포 완료!', 'green'); - log(`📁 배포 위치: ${streamdockPluginPath}`, 'blue'); - log('💡 StreamDock을 재시작하면 플러그인이 활성화됩니다', 'cyan'); - return true; - } - -// 개발용 배포 함수 (빌드 후 배포) -function deployDev() { - log('🚀 개발용 StreamDock 배포 시작...', 'magenta'); - - // 먼저 빌드 - if (!buildDev()) { - return false; - } - - const buildDir = path.join(CURRENT_DIR, 'dist-dev', PLUGIN_NAME); - const streamdockPluginPath = path.join(STREAMDOCK_PLUGINS_PATH, PLUGIN_NAME); - - // StreamDock 플러그인 폴더 확인 - if (!fs.existsSync(STREAMDOCK_PLUGINS_PATH)) { - log('❌ StreamDock 플러그인 폴더를 찾을 수 없습니다', 'red'); - log(`📁 예상 경로: ${STREAMDOCK_PLUGINS_PATH}`, 'yellow'); - return false; - } - - // 기존 플러그인 삭제 - if (fs.existsSync(streamdockPluginPath)) { - removeDirectory(streamdockPluginPath); - log('🗑️ 기존 플러그인 삭제 완료', 'yellow'); - } - - // 새 플러그인 복사 - if (fs.existsSync(buildDir)) { - copyDirectory(buildDir, streamdockPluginPath); - log('✅ 플러그인 복사 완료', 'green'); - } else { - log('❌ 빌드된 플러그인 폴더를 찾을 수 없습니다', 'red'); - return false; - } - - log('🎉 개발용 StreamDock 배포 완료!', 'green'); - log(`📁 배포 위치: ${streamdockPluginPath}`, 'blue'); - log('💡 StreamDock을 재시작하면 플러그인이 활성화됩니다', 'cyan'); - return true; -} - -// 정리 함수 -function clean() { - log('🧹 빌드 폴더 정리 시작...', 'yellow'); - - const buildDir = path.join(CURRENT_DIR, 'dist'); - const buildDevDir = path.join(CURRENT_DIR, 'dist-dev'); - - if (fs.existsSync(buildDir)) { - removeDirectory(buildDir); - log('✅ dist 폴더 삭제 완료', 'green'); - } - - if (fs.existsSync(buildDevDir)) { - removeDirectory(buildDevDir); - log('✅ dist-dev 폴더 삭제 완료', 'green'); - } - - log('🎉 정리 완료!', 'green'); -} - -// 도움말 함수 -function help() { - log('📖 Streamingle Camera Plugin 빌드 도구', 'cyan'); - log('', 'white'); - log('사용법:', 'yellow'); - log(' node build.js <명령>', 'white'); - log('', 'white'); - log('명령:', 'yellow'); - log(' build - 프로덕션 빌드 (dist 폴더에 생성)', 'white'); - log(' build:dev - 개발 빌드 (dist-dev 폴더에 생성)', 'white'); - log(' deploy - StreamDock에 직접 배포', 'white'); - log(' deploy:dev - 개발 빌드 후 StreamDock에 배포', 'white'); - log(' clean - 빌드 폴더 정리', 'white'); - log(' help - 이 도움말 표시', 'white'); - log('', 'white'); - log('예시:', 'yellow'); - log(' node build.js deploy # 바로 StreamDock에 배포', 'white'); - log(' node build.js deploy:dev # 개발 빌드 후 배포', 'white'); -} - -// 메인 실행 -const command = process.argv[2]; - - switch (command) { - case 'build': - build(); - break; - case 'build:dev': - buildDev(); - break; - case 'deploy': - deploy(); - break; - case 'deploy:dev': - deployDev(); - break; - case 'clean': - clean(); - break; - case 'help': - case '--help': - case '-h': - help(); - break; - default: - log('❌ 알 수 없는 명령입니다', 'red'); - log('', 'white'); - help(); - break; - } \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/debug.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/debug.js deleted file mode 100644 index aca3e6b25..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/debug.js +++ /dev/null @@ -1,133 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -console.log('=== Streamingle 플러그인 디버깅 도구 ==='); - -// 1. 플러그인 파일 확인 -console.log('\n1. 플러그인 파일 확인:'); -const pluginDir = __dirname; -const requiredFiles = [ - 'manifest.json', - 'plugin.js', - 'package.json', - 'propertyinspector.html' -]; - -requiredFiles.forEach(file => { - const filePath = path.join(pluginDir, file); - if (fs.existsSync(filePath)) { - const stats = fs.statSync(filePath); - console.log(`✅ ${file} - ${stats.size} bytes`); - } else { - console.log(`❌ ${file} - 파일 없음`); - } -}); - -// 2. manifest.json 내용 확인 -console.log('\n2. manifest.json 내용:'); -try { - const manifestPath = path.join(pluginDir, 'manifest.json'); - const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')); - console.log('✅ manifest.json 파싱 성공'); - console.log(` SDK 버전: ${manifest.SDKVersion}`); - console.log(` 코드 경로: ${manifest.CodePath}`); - console.log(` 플러그인 이름: ${manifest.Name}`); - console.log(` 액션 UUID: ${manifest.Actions[0].UUID}`); -} catch (error) { - console.log(`❌ manifest.json 파싱 실패: ${error.message}`); -} - -// 3. package.json 내용 확인 -console.log('\n3. package.json 내용:'); -try { - const packagePath = path.join(pluginDir, 'package.json'); - const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8')); - console.log('✅ package.json 파싱 성공'); - console.log(` 이름: ${packageJson.name}`); - console.log(` 버전: ${packageJson.version}`); - console.log(` 메인 파일: ${packageJson.main}`); - console.log(` 의존성: ${Object.keys(packageJson.dependencies || {}).join(', ')}`); -} catch (error) { - console.log(`❌ package.json 파싱 실패: ${error.message}`); -} - -// 4. WebSocket 연결 테스트 -console.log('\n4. WebSocket 연결 테스트:'); -const WebSocket = require('ws'); - -function testWebSocketConnection() { - return new Promise((resolve) => { - console.log(' 연결 시도 중...'); - - const ws = new WebSocket('ws://127.0.0.1:10701/'); - - const timeout = setTimeout(() => { - console.log(' ❌ 연결 타임아웃 (5초)'); - ws.close(); - resolve(false); - }, 5000); - - ws.on('open', () => { - console.log(' ✅ WebSocket 연결 성공!'); - clearTimeout(timeout); - ws.close(); - resolve(true); - }); - - ws.on('error', (error) => { - console.log(` ❌ WebSocket 연결 실패: ${error.message}`); - clearTimeout(timeout); - resolve(false); - }); - - ws.on('close', (code, reason) => { - console.log(` 🔌 연결 종료 - 코드: ${code}, 이유: ${reason || '알 수 없음'}`); - }); - }); -} - -testWebSocketConnection().then((connected) => { - console.log(`\n결과: ${connected ? '✅ 연결 가능' : '❌ 연결 불가'}`); - - // 5. 포트 상태 확인 - console.log('\n5. 포트 상태 확인:'); - const { exec } = require('child_process'); - - exec('netstat -an | findstr :10701', (error, stdout, stderr) => { - if (error) { - console.log(` ❌ netstat 실행 실패: ${error.message}`); - return; - } - - if (stdout.trim()) { - console.log(' ✅ 포트 10701 사용 중:'); - stdout.split('\n').forEach(line => { - if (line.trim()) { - console.log(` ${line.trim()}`); - } - }); - } else { - console.log(' ❌ 포트 10701에서 서비스 없음'); - } - }); -}); - -// 6. StreamDock 플러그인 경로 확인 -console.log('\n6. StreamDock 플러그인 경로:'); -const streamDockPath = 'C:\\Users\\qscft\\AppData\\Roaming\\HotSpot\\StreamDock\\plugins\\com.mirabox.streamingle.sdPlugin'; -if (fs.existsSync(streamDockPath)) { - console.log(` ✅ StreamDock 플러그인 폴더 존재: ${streamDockPath}`); - - const files = fs.readdirSync(streamDockPath); - console.log(` 📁 파일 개수: ${files.length}개`); - files.forEach(file => { - const filePath = path.join(streamDockPath, file); - const stats = fs.statSync(filePath); - const type = stats.isDirectory() ? '📁' : '📄'; - console.log(` ${type} ${file} - ${stats.size} bytes`); - }); -} else { - console.log(` ❌ StreamDock 플러그인 폴더 없음: ${streamDockPath}`); -} - -console.log('\n=== 디버깅 완료 ==='); \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/images/action.png b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/images/action.png deleted file mode 100644 index 45b93e4ea..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/images/action.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:62da15b1333bcf15bb1ef33c05fb246406b76a8bf9710a4df6e7bd0a16ef62b5 -size 42151 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/manifest.json b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/manifest.json deleted file mode 100644 index fcc7d856a..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/manifest.json +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f646099cafd148b8c2b4c8dc1922ab6e98354df7194717b6d82812b05af66f43 -size 1353 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/plugin/index.html b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/plugin/index.html deleted file mode 100644 index 84e63f28a..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/plugin/index.html +++ /dev/null @@ -1,314 +0,0 @@ - - - - - Streamingle Plugin Main - - -
- 플러그인 메인 - 버튼 클릭 처리
- Property Inspector에서 설정 관리 -
- - - - \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/plugin/index.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/plugin/index.js deleted file mode 100644 index e32ba79db..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/plugin/index.js +++ /dev/null @@ -1,664 +0,0 @@ -/** - * Streamingle Camera Controller Plugin for StreamDeck - * 외부 패키지 없이 동작하도록 단순화 - */ - -// Global variables -let websocket = null; -let uuid = null; -let actionInfo = {}; -let connectionInfo = {}; - -// Context 관리 -const buttonContexts = new Map(); - -// Unity 연결 관리자 -let unityManager = null; - -// Unity 연결 관리 클래스 (내장 WebSocket만 사용) -class UnityConnectionManager { - constructor() { - this.isConnected = false; - this.socket = null; - this.reconnectInterval = null; - this.healthCheckInterval = null; - this.connectionAttempts = 0; - this.maxConsecutiveFailures = 10; - this.isShuttingDown = false; - this.cameraData = null; - this.currentCamera = 0; - this.eventListeners = new Map(); - this.startHealthCheck(); - } - - on(event, callback) { - if (!this.eventListeners.has(event)) { - this.eventListeners.set(event, []); - } - this.eventListeners.get(event).push(callback); - } - - emit(event, data) { - if (this.eventListeners.has(event)) { - this.eventListeners.get(event).forEach(callback => { - try { - callback(data); - } catch (error) { - console.log(`❌ Event listener error (${event}): ${error.message}`); - } - }); - } - } - - startHealthCheck() { - if (this.healthCheckInterval) { - clearInterval(this.healthCheckInterval); - } - this.healthCheckInterval = setInterval(() => { - console.log(`🩺 연결 상태 확인 - Unity 연결됨: ${this.isConnected}, 종료 중: ${this.isShuttingDown}, 재연결 중: ${!!this.reconnectInterval}`); - if (!this.isConnected && !this.isShuttingDown && !this.reconnectInterval) { - console.log('🩺 연결 상태 확인 - Unity 서버 재연결 시도'); - this.connectionAttempts = 0; - this.connect(); - } - }, 30000); - console.log('🩺 Unity 연결 상태 주기적 확인 시작 (30초 간격)'); - } - - connect() { - if (this.isShuttingDown) { - console.log('🛑 종료 중이므로 연결 시도 안함'); - return; - } - if (this.socket && this.socket.readyState === 0) { - console.log('⏳ 이미 연결 시도 중...'); - return; - } - if (this.socket) { - console.log('🔌 기존 소켓 닫기'); - this.socket.close(); - } - try { - console.log('🔌 Unity WebSocket 생성 시도: ws://localhost:10701'); - this.socket = new window.WebSocket('ws://localhost:10701'); - console.log(`🔌 Unity 연결 시도... (시도 ${this.connectionAttempts + 1}회)`); - this.socket.onopen = () => { - this.isConnected = true; - this.connectionAttempts = 0; - console.log('✅ Unity 연결 성공! WebSocket 연결됨'); - if (this.reconnectInterval) { - clearTimeout(this.reconnectInterval); - this.reconnectInterval = null; - } - this.emit('connectionChange', this.isConnected); - - // 연결 상태 변경을 모든 Property Inspector에 알림 - broadcastToPropertyInspectors('connection_status', { - connected: true - }); - - this.requestCameraList(); - }; - this.socket.onmessage = (event) => { - try { - console.log('📨 Unity 원본 메시지:', event.data); - const data = JSON.parse(event.data); - console.log(`📨 Unity 메시지 파싱 완료: ${data.type || 'unknown'}`); - this.handleUnityMessage(data); - } catch (error) { - console.log(`❌ Unity 메시지 파싱 오류: ${error.message}`); - console.log(`❌ 원본 메시지: ${event.data}`); - } - }; - this.socket.onclose = (event) => { - const wasConnected = this.isConnected; - this.isConnected = false; - if (wasConnected) { - console.log(`❌ Unity 연결 끊어짐 (코드: ${event.code}, 이유: ${event.reason || '알 수 없음'})`); - this.emit('connectionChange', this.isConnected); - } else { - console.log(`❌ Unity 연결 실패 (코드: ${event.code}, 이유: ${event.reason || '알 수 없음'})`); - } - if (!this.isShuttingDown) { - this.scheduleReconnect(); - } - }; - this.socket.onerror = (error) => { - console.log(`❌ Unity 연결 오류: ${error}`); - }; - } catch (error) { - console.log(`❌ Unity 연결 설정 오류: ${error.message}`); - if (!this.isShuttingDown) { - this.scheduleReconnect(); - } - } - } - - scheduleReconnect() { - if (this.isShuttingDown || this.reconnectInterval) return; - this.connectionAttempts++; - let delay; - if (this.connectionAttempts <= 3) { - delay = 2000; - } else if (this.connectionAttempts <= 10) { - delay = 5000; - } else { - delay = 10000; - } - console.log(`🔄 ${delay/1000}초 후 Unity 재연결 시도 (${this.connectionAttempts}/${this.maxConsecutiveFailures})`); - this.reconnectInterval = setTimeout(() => { - this.reconnectInterval = null; - this.connect(); - }, delay); - } - - handleUnityMessage(data) { - console.log('📨 Unity 메시지 수신:', data); - - switch (data.type) { - case 'cameraList': - this.cameraData = data; - this.currentCamera = data.currentCamera || 0; - console.log(`📹 카메라 목록 수신: ${data.cameras?.length || 0}개`); - console.log('📹 카메라 데이터 상세:', data); - - // 카메라 목록을 받았다는 것은 Unity 연결이 성공했다는 의미 - if (!this.isConnected) { - console.log('🔄 Unity 연결 상태 업데이트: 연결됨 (카메라 목록 수신으로 확인)'); - this.isConnected = true; - this.connectionAttempts = 0; - this.emit('connectionChange', this.isConnected); - } - - // Property Inspector들에게 카메라 데이터 전송 - console.log('📤 Property Inspector들에게 카메라 데이터 전송'); - broadcastToPropertyInspectors('camera_data', { - camera_data: data, - current_camera: this.currentCamera - }); - updateAllButtons(); - break; - case 'cameraSwitched': - this.currentCamera = data.cameraIndex; - console.log(`📹 카메라 전환 완료: ${data.cameraIndex}`); - break; - default: - console.log(`📨 알 수 없는 Unity 메시지 타입: ${data.type}`); - } - } - - requestCameraList() { - if (!this.isConnected || !this.socket) { - console.log('❌ Unity 연결 안됨 - 카메라 목록 요청 불가'); - console.log(`❌ 연결 상태: ${this.isConnected}, 소켓 상태: ${this.socket ? this.socket.readyState : 'null'}`); - return; - } - try { - const message = { - type: 'getCameraList', - requestId: Date.now().toString() - }; - console.log('📤 Unity에 카메라 목록 요청:', message); - this.socket.send(JSON.stringify(message)); - console.log('✅ 카메라 목록 요청 전송 완료'); - } catch (error) { - console.log(`❌ 카메라 목록 요청 실패: ${error.message}`); - } - } - - switchCamera(cameraIndex) { - if (!this.isConnected) { - console.log('❌ Unity 연결 안됨 - 카메라 전환 불가'); - return false; - } - try { - const message = { - type: 'switchCamera', - cameraIndex: cameraIndex - }; - this.socket.send(JSON.stringify(message)); - console.log(`📤 Unity에 카메라 전환 요청: ${cameraIndex}`); - return true; - } catch (error) { - console.log(`❌ 카메라 전환 요청 실패: ${error.message}`); - return false; - } - } - - forceReconnect() { - console.log('🔄 수동 재연결 요청'); - this.connectionAttempts = 0; - if (this.reconnectInterval) { - clearTimeout(this.reconnectInterval); - this.reconnectInterval = null; - } - this.connect(); - } - - getConnectionStatus() { - return { - isConnected: this.isConnected, - cameraData: this.cameraData, - currentCamera: this.currentCamera - }; - } - - disconnect() { - this.isShuttingDown = true; - if (this.healthCheckInterval) { - clearInterval(this.healthCheckInterval); - this.healthCheckInterval = null; - } - if (this.reconnectInterval) { - clearTimeout(this.reconnectInterval); - this.reconnectInterval = null; - } - if (this.socket) { - this.socket.close(); - this.socket = null; - } - this.isConnected = false; - console.log('🔌 Unity 연결 종료'); - } -} - -// Property Inspector로 메시지 전송 (단순화) -function sendToPropertyInspector(context, type, data) { - if (!websocket || !context) { - console.log(`⚠️ Property Inspector 전송 실패: ${!websocket ? 'websocket 없음' : 'context 없음'}`); - return; - } - try { - const message = { - type: type, - ...data - }; - websocket.send(JSON.stringify({ - event: 'sendToPropertyInspector', - context: context, - payload: message - })); - console.log(`📤 Property Inspector 전송: ${type} -> ${context.substring(0, 8)}...`); - } catch (error) { - console.log(`❌ Property Inspector 전송 실패: ${error.message}`); - } -} - -function broadcastToPropertyInspectors(type, data) { - buttonContexts.forEach((buttonData, context) => { - sendToPropertyInspector(context, type, data); - }); -} - -function updateButtonTitle(context, cameraName = null, cameraIndex = null) { - if (!websocket || !context) return; - if (!cameraName) { - const buttonData = buttonContexts.get(context); - if (buttonData && buttonData.settings && buttonData.settings.cameraList) { - const index = cameraIndex !== null ? cameraIndex : buttonData.settings.cameraIndex; - if (typeof index === 'number' && buttonData.settings.cameraList[index]) { - cameraName = buttonData.settings.cameraList[index].name; - } - } - } - let title = cameraName || '카메라\n선택'; - if (title.length > 8) { - const underscoreIndex = title.indexOf('_'); - if (underscoreIndex !== -1 && underscoreIndex > 0) { - const firstLine = title.substring(0, underscoreIndex); - const secondLine = title.substring(underscoreIndex + 1); - const maxLineLength = 8; - let line1 = firstLine.length > maxLineLength ? firstLine.substring(0, maxLineLength - 1) + '.' : firstLine; - let line2 = secondLine.length > maxLineLength ? secondLine.substring(0, maxLineLength - 1) + '.' : secondLine; - title = line1 + '\n' + line2; - } else { - const midPoint = Math.ceil(title.length / 2); - const firstLine = title.substring(0, midPoint); - const secondLine = title.substring(midPoint); - title = firstLine + '\n' + secondLine; - } - } - websocket.send(JSON.stringify({ - event: 'setTitle', - context: context, - payload: { - title: title, - target: 0, - titleParameters: { - fontSize: 18, - showTitle: true, - titleAlignment: "middle" - } - } - })); - console.log(`🏷️ 버튼 제목 업데이트: "${title.replace('\n', '\\n')}"`); -} - -function updateButtonUI(context, cameraIndex, isConnected) { - if (!websocket) return; - try { - const svgData = createSvgIcon(cameraIndex, isConnected); - const base64Data = btoa(svgData); - websocket.send(JSON.stringify({ - event: 'setImage', - context: context, - payload: { - image: `data:image/svg+xml;base64,${base64Data}`, - target: 0, - state: 0 - } - })); - updateButtonTitle(context, null, cameraIndex); - } catch (error) { - console.log(`❌ 버튼 UI 업데이트 실패: ${error.message}`); - } -} - -function createSvgIcon(cameraIndex, isConnected) { - const bgColor = isConnected ? '#4CAF50' : '#F44336'; - const textColor = '#FFFFFF'; - return ` - - - - ${cameraIndex + 1} - `; -} - -function updateAllButtons() { - buttonContexts.forEach((buttonData, context) => { - updateButtonUI(context, buttonData.cameraIndex, unityManager ? unityManager.isConnected : false); - }); -} - -const streamDeckEventHandlers = { - willAppear: function(data) { - const { context, payload } = data; - const settings = payload.settings || {}; - const cameraIndex = settings.cameraIndex || 0; - buttonContexts.set(context, { - cameraIndex: cameraIndex, - settings: settings - }); - console.log(`📱 버튼 추가됨: context=${context.substring(0, 8)}..., camera=${cameraIndex}`); - updateButtonUI(context, cameraIndex, unityManager ? unityManager.isConnected : false); - if (unityManager) { - const status = unityManager.getConnectionStatus(); - sendToPropertyInspector(context, 'connection_status', { - connected: status.isConnected - }); - if (status.cameraData) { - sendToPropertyInspector(context, 'camera_data', { - camera_data: status.cameraData, - current_camera: status.currentCamera - }); - } - } - }, - willDisappear: function(data) { - const { context } = data; - buttonContexts.delete(context); - console.log(`👋 버튼 제거됨: ${context.substring(0, 8)}... (남은 버튼: ${buttonContexts.size}개)`); - }, - keyUp: function(data) { - const { context, payload } = data; - const buttonData = buttonContexts.get(context); - if (!buttonData) { - console.log(`❌ 버튼 데이터 없음: ${context.substring(0, 8)}...`); - return; - } - const cameraIndex = buttonData.settings.cameraIndex || 0; - console.log(`🔘 버튼 클릭: context=${context.substring(0, 8)}..., camera=${cameraIndex}`); - if (!unityManager || !unityManager.isConnected) { - console.log('❌ Unity 연결 안됨 - 카메라 전환 불가'); - return; - } - const success = unityManager.switchCamera(cameraIndex); - if (success) { - console.log(`✅ 카메라 전환 요청 성공: ${cameraIndex}`); - } else { - console.log(`❌ 카메라 전환 요청 실패: ${cameraIndex}`); - } - }, - didReceiveSettings: function(data) { - const { context, payload } = data; - const settings = payload.settings || {}; - console.log(`📥 설정 수신: context=${context.substring(0, 8)}..., cameraIndex=${settings.cameraIndex}`); - - const buttonData = buttonContexts.get(context); - if (buttonData) { - // 기존 설정 유지하면서 카메라 인덱스만 업데이트 - const updatedSettings = { ...buttonData.settings }; - - // 카메라 인덱스 업데이트 - if (typeof settings.cameraIndex === 'number') { - updatedSettings.cameraIndex = settings.cameraIndex; - buttonData.cameraIndex = settings.cameraIndex; - console.log(`🎯 카메라 인덱스 업데이트: ${settings.cameraIndex}`); - } - - // 카메라 목록 업데이트 (있는 경우만) - if (settings.cameraList && settings.cameraList.length > 0) { - updatedSettings.cameraList = settings.cameraList; - console.log(`📹 카메라 목록 업데이트: ${settings.cameraList.length}개`); - } - - buttonData.settings = updatedSettings; - - // 버튼 UI 업데이트 - updateButtonUI(context, buttonData.cameraIndex, unityManager ? unityManager.isConnected : false); - - console.log(`✅ 버튼 설정 업데이트 완료: cameraIndex=${buttonData.cameraIndex}`); - } - }, - sendToPlugin: function(data) { - const { context, payload } = data; - try { - const message = typeof payload === 'string' ? JSON.parse(payload) : payload; - console.log(`📨 Property Inspector 메시지: ${message.command || message.type}`); - switch (message.command) { - case 'requestCameraList': - if (unityManager && unityManager.isConnected) { - unityManager.requestCameraList(); - } else { - console.log('❌ Unity 연결 안됨 - 카메라 목록 요청 불가'); - } - break; - case 'forceReconnect': - if (unityManager) { - unityManager.forceReconnect(); - } - break; - case 'getInitialSettings': - console.log('📥 Property Inspector에서 초기 설정 요청'); - // 현재 Unity 연결 상태와 카메라 데이터를 Property Inspector에 전송 - const isConnected = unityManager ? unityManager.isConnected : false; - sendToPropertyInspector(context, 'connection_status', { - connected: isConnected - }); - - if (unityManager && isConnected) { - const status = unityManager.getConnectionStatus(); - if (status.cameraData) { - sendToPropertyInspector(context, 'camera_data', { - camera_data: status.cameraData, - current_camera: status.currentCamera - }); - } - } - break; - default: - console.log(`❓ 알 수 없는 Property Inspector 명령: ${message.command}`); - } - } catch (error) { - console.log(`❌ Property Inspector 메시지 파싱 오류: ${error.message}`); - } - }, - - // Property Inspector가 열릴 때 자동으로 호출되는 핵심 이벤트 - propertyInspectorDidAppear: function(data) { - const { context } = data; - console.log(`📋 Property Inspector 열림: ${context.substring(0, 8)}...`); - - // 현재 버튼의 설정을 PI로 전송 - const buttonData = buttonContexts.get(context); - if (buttonData) { - console.log(`📤 PI에 현재 설정 전송: cameraIndex=${buttonData.settings.cameraIndex}`); - - // Unity 연결 강제 확인 및 시도 - if (unityManager) { - console.log(`🔍 Unity 연결 상태 강제 확인`); - const currentStatus = unityManager.getConnectionStatus(); - console.log(`🔍 현재 Unity 연결 상태: ${currentStatus.isConnected}`); - - // 연결되지 않았다면 연결 시도 - if (!currentStatus.isConnected) { - console.log(`🔄 Unity 연결 시도 중...`); - unityManager.connect(); - } - } - - // Unity 연결 상태 확인 - const isUnityConnected = unityManager ? unityManager.isConnected : false; - console.log(`🔍 Unity 연결 상태 확인: ${isUnityConnected}`); - - // 현재 설정 전송 (Unity 연결 상태 포함) - sendToPropertyInspector(context, 'current_settings', { - cameraIndex: buttonData.settings.cameraIndex || 0, - cameraList: buttonData.settings.cameraList || [], - isUnityConnected: isUnityConnected - }); - - // Unity 연결 상태 별도 전송 - sendToPropertyInspector(context, 'connection_status', { - connected: isUnityConnected - }); - - // Unity가 연결되어 있고 카메라 데이터가 있으면 전송 - if (unityManager && isUnityConnected) { - const status = unityManager.getConnectionStatus(); - if (status.cameraData) { - sendToPropertyInspector(context, 'camera_data', { - camera_data: status.cameraData, - current_camera: status.currentCamera - }); - } - } - } else { - console.log(`❌ 버튼 데이터 없음: ${context.substring(0, 8)}...`); - } - }, - - // Property Inspector가 닫힐 때 호출 - propertyInspectorDidDisappear: function(data) { - const { context } = data; - console.log(`📋 Property Inspector 닫힘: ${context.substring(0, 8)}...`); - }, - - // getSettings 이벤트 처리 (PI에서 설정 요청 시) - getSettings: function(data) { - const { context } = data; - console.log(`📥 설정 요청: ${context.substring(0, 8)}...`); - - const buttonData = buttonContexts.get(context); - if (buttonData) { - // 현재 설정을 PI로 전송 - sendToPropertyInspector(context, 'current_settings', { - cameraIndex: buttonData.settings.cameraIndex || 0, - cameraList: buttonData.settings.cameraList || [], - isUnityConnected: unityManager ? unityManager.isConnected : false - }); - } - } -}; - -function setupUnityEventListeners() { - if (!unityManager) return; - unityManager.on('connectionChange', (isConnected) => { - console.log(`🔄 Unity 연결 상태 변경: ${isConnected}`); - updateAllButtons(); - - // Property Inspector들에게 연결 상태 변경 알림 - broadcastToPropertyInspectors('connection_status', { - connected: isConnected - }); - - // 연결이 되면 카메라 목록 요청 - if (isConnected) { - console.log('📹 Unity 연결됨 - 카메라 목록 요청'); - unityManager.requestCameraList(); - } - }); -} - -function connectElgatoStreamDeckSocket(inPort, inUUID, inEvent, inInfo, inActionInfo) { - uuid = inUUID; - actionInfo = inActionInfo; - connectionInfo = inInfo; - console.log(`🔌 StreamDeck 소켓 연결: port=${inPort}, uuid=${uuid.substring(0, 8)}...`); - websocket = new window.WebSocket('ws://localhost:' + inPort); - websocket.onopen = function() { - console.log('✅ StreamDeck 소켓 연결 성공'); - const json = { - event: inEvent, - uuid: inUUID - }; - websocket.send(JSON.stringify(json)); - if (!unityManager) { - unityManager = new UnityConnectionManager(); - setupUnityEventListeners(); - // 즉시 Unity 연결 시도 - console.log('🚀 StreamDeck 연결 완료 - Unity 연결 시도'); - unityManager.connect(); - } - }; - websocket.onmessage = function(evt) { - try { - const jsonObj = JSON.parse(evt.data); - const event = jsonObj.event; - const context = jsonObj.context; - console.log(`📨 StreamDeck 이벤트: ${event} (context: ${context ? context.substring(0, 8) + '...' : 'N/A'})`); - console.log(`📨 전체 메시지:`, jsonObj); - - if (streamDeckEventHandlers[event]) { - streamDeckEventHandlers[event](jsonObj); - } else { - console.log(`❓ 알 수 없는 StreamDeck 이벤트: ${event}`); - } - } catch (error) { - console.log(`❌ StreamDeck 메시지 파싱 오류: ${error.message}`); - console.log(`❌ 원본 메시지: ${evt.data}`); - } - }; - websocket.onerror = function(evt) { - console.log(`❌ StreamDeck 소켓 오류: ${evt}`); - }; - websocket.onclose = function(evt) { - console.log(`🔌 StreamDeck 소켓 연결 종료: ${evt.code} - ${evt.reason}`); - if (unityManager) { - unityManager.disconnect(); - } - }; -} - -// 프로세스 종료 시 정리 -if (typeof process !== 'undefined' && process.on) { - process.on('SIGINT', function() { - console.log('🛑 프로세스 종료 신호 수신'); - if (unityManager) { - unityManager.disconnect(); - } - if (websocket) { - websocket.close(); - } - process.exit(0); -}); -} - -// 모듈 내보내기 (테스트/호환용) -if (typeof module !== 'undefined') { - module.exports = { - connectElgatoStreamDeckSocket - }; -} diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/plugin/log/streamingle_plugin b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/plugin/log/streamingle_plugin deleted file mode 100644 index e69de29bb..000000000 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/plugin/utils/plugin.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/plugin/utils/plugin.js deleted file mode 100644 index b7a9fcb1b..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/plugin/utils/plugin.js +++ /dev/null @@ -1,213 +0,0 @@ -// 配置日志文件 -const now = new Date(); -const log = require('log4js').configure({ - appenders: { - file: { type: 'file', filename: `./log/${now.getFullYear()}.${now.getMonth() + 1}.${now.getDate()}.log` } - }, - categories: { - default: { appenders: ['file'], level: 'info' } - } -}).getLogger(); - -//################################################## -//##################全局异常捕获##################### -process.on('uncaughtException', (error) => { - log.error('Uncaught Exception:', error); -}); -process.on('unhandledRejection', (reason) => { - log.error('Unhandled Rejection:', reason); -}); -//################################################## -//################################################## - - -// 插件类 -const ws = require('ws'); -class Plugins { - static language = process.argv[9] ? JSON.parse(process.argv[9]).application.language : 'en'; - static globalSettings = {}; - getGlobalSettingsFlag = true; - constructor() { - if (Plugins.instance) { - return Plugins.instance; - } - // log.info("process.argv", process.argv); - this.ws = new ws("ws://127.0.0.1:" + process.argv[3]); - this.ws.on('open', () => this.ws.send(JSON.stringify({ uuid: process.argv[5], event: process.argv[7] }))); - this.ws.on('close', process.exit); - this.ws.on('message', e => { - if (this.getGlobalSettingsFlag) { - // 只获取一次 - this.getGlobalSettingsFlag = false; - this.getGlobalSettings(); - } - const data = JSON.parse(e.toString()); - const action = data.action?.split('.').pop(); - this[action]?.[data.event]?.(data); - if (data.event === 'didReceiveGlobalSettings') { - Plugins.globalSettings = data.payload.settings; - } - this[data.event]?.(data); - }); - Plugins.instance = this; - } - - setGlobalSettings(payload) { - Plugins.globalSettings = payload; - this.ws.send(JSON.stringify({ - event: "setGlobalSettings", - context: process.argv[5], payload - })); - } - - getGlobalSettings() { - this.ws.send(JSON.stringify({ - event: "getGlobalSettings", - context: process.argv[5], - })); - } - // 设置标题 - setTitle(context, str, row = 0, num = 6) { - let newStr = null; - if (row && str) { - let nowRow = 1, strArr = str.split(''); - strArr.forEach((item, index) => { - if (nowRow < row && index >= nowRow * num) { nowRow++; newStr += '\n'; } - if (nowRow <= row && index < nowRow * num) { newStr += item; } - }); - if (strArr.length > row * num) { newStr = newStr.substring(0, newStr.length - 1); newStr += '..'; } - } - this.ws.send(JSON.stringify({ - event: "setTitle", - context, payload: { - target: 0, - title: newStr || str + '' - } - })); - } - // 设置背景 - setImage(context, url) { - this.ws.send(JSON.stringify({ - event: "setImage", - context, payload: { - target: 0, - image: url - } - })); - } - // 设置状态 - setState(context, state) { - this.ws.send(JSON.stringify({ - event: "setState", - context, payload: { state } - })); - } - // 保存持久化数据 - setSettings(context, payload) { - this.ws.send(JSON.stringify({ - event: "setSettings", - context, payload - })); - } - - // 在按键上展示警告 - showAlert(context) { - this.ws.send(JSON.stringify({ - event: "showAlert", - context - })); - } - - // 在按键上展示成功 - showOk(context) { - this.ws.send(JSON.stringify({ - event: "showOk", - context - })); - } - // 发送给属性检测器 - sendToPropertyInspector(payload) { - this.ws.send(JSON.stringify({ - action: Actions.currentAction, - context: Actions.currentContext, - payload, event: "sendToPropertyInspector" - })); - } - // 用默认浏览器打开网页 - openUrl(url) { - this.ws.send(JSON.stringify({ - event: "openUrl", - payload: { url } - })); - } -}; - -// 操作类 -class Actions { - constructor(data) { - this.data = {}; - this.default = {}; - Object.assign(this, data); - } - // 属性检查器显示时 - static currentAction = null; - static currentContext = null; - static actions = {}; - propertyInspectorDidAppear(data) { - Actions.currentAction = data.action; - Actions.currentContext = data.context; - this._propertyInspectorDidAppear?.(data); - } - // 初始化数据 - willAppear(data) { - Plugins.globalContext = data.context; - Actions.actions[data.context] = data.action - const { context, payload: { settings } } = data; - this.data[context] = Object.assign({ ...this.default }, settings); - this._willAppear?.(data); - } - - didReceiveSettings(data) { - this.data[data.context] = data.payload.settings; - this._didReceiveSettings?.(data); - } - // 行动销毁 - willDisappear(data) { - this._willDisappear?.(data); - delete this.data[data.context]; - } -} - -class EventEmitter { - constructor() { - this.events = {}; - } - - // 订阅事件 - subscribe(event, listener) { - if (!this.events[event]) { - this.events[event] = []; - } - this.events[event].push(listener); - } - - // 取消订阅 - unsubscribe(event, listenerToRemove) { - if (!this.events[event]) return; - - this.events[event] = this.events[event].filter(listener => listener !== listenerToRemove); - } - - // 发布事件 - emit(event, data) { - if (!this.events[event]) return; - this.events[event].forEach(listener => listener(data)); - } -} - -module.exports = { - log, - Plugins, - Actions, - EventEmitter -}; \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/camera/index.html b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/camera/index.html deleted file mode 100644 index 0e2f21618..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/camera/index.html +++ /dev/null @@ -1,187 +0,0 @@ - - - - - Streamingle Camera Inspector - - - - -
-
- Unity 연결 안됨 -
- - -
- -
- -
-
현재 카메라: -
-
- - -
- - -
- - -
- -
- - -
- -
-
- - - - - \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/camera/index.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/camera/index.js deleted file mode 100644 index 74a974510..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/camera/index.js +++ /dev/null @@ -1,778 +0,0 @@ -/** - * Streamingle Camera Controller - Property Inspector - * 엘가토 공식 구조 기반 단순화 버전 - */ - -// Global variables -let websocket = null; -let uuid = null; -let actionContext = null; // 현재 액션의 컨텍스트 -let settings = {}; - -// Context별 설정 관리 -const contextSettings = new Map(); -let currentActionContext = null; - -// Unity 연결 상태 (Plugin Main에서 받아옴) -let isUnityConnected = false; -let cameraData = []; -let currentCamera = 0; - -// DOM elements -let statusDot = null; -let connectionStatus = null; -let cameraSelect = null; -let currentCameraDisplay = null; -let refreshButton = null; - -// 화면에 로그를 표시하는 함수 -function logToScreen(msg, color = "#fff") { - let logDiv = document.getElementById('logArea'); - if (!logDiv) { - logDiv = document.createElement('div'); - logDiv.id = 'logArea'; - logDiv.style.background = '#111'; - logDiv.style.color = '#fff'; - logDiv.style.fontSize = '11px'; - logDiv.style.padding = '8px'; - logDiv.style.marginTop = '16px'; - logDiv.style.height = '120px'; - logDiv.style.overflowY = 'auto'; - document.body.appendChild(logDiv); - } - const line = document.createElement('div'); - line.style.color = color; - line.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`; - logDiv.appendChild(line); - logDiv.scrollTop = logDiv.scrollHeight; -} - -// 기존 console.log/console.error를 화면에도 출력 -const origLog = console.log; -console.log = function(...args) { - origLog.apply(console, args); - logToScreen(args.map(a => (typeof a === 'object' ? JSON.stringify(a) : a)).join(' '), '#0f0'); -}; -const origErr = console.error; -console.error = function(...args) { - origErr.apply(console, args); - logToScreen(args.map(a => (typeof a === 'object' ? JSON.stringify(a) : a)).join(' '), '#f55'); -}; - -console.log('🔧 Property Inspector script loaded'); - -// Initialize when DOM is loaded -document.addEventListener('DOMContentLoaded', function() { - console.log('📋 Property Inspector 초기화'); - initializePropertyInspector(); -}); - -// Initialize Property Inspector -function initializePropertyInspector() { - // Get DOM elements - statusDot = document.getElementById('statusDot'); - connectionStatus = document.getElementById('connection-status'); - cameraSelect = document.getElementById('camera-select'); - currentCameraDisplay = document.getElementById('current-camera'); - refreshButton = document.getElementById('refresh-button'); - - // Setup event listeners - if (cameraSelect) { - cameraSelect.addEventListener('change', onCameraSelectionChanged); - } - - if (refreshButton) { - refreshButton.addEventListener('click', onRefreshClicked); - } - - console.log('✅ Property Inspector 준비 완료'); -} - -// Send message to plugin -function sendToPlugin(command, data = {}) { - if (!websocket) { - console.error('❌ WebSocket not available'); - return; - } - - try { - const message = { - command: command, - context: uuid, - ...data - }; - - // StreamDeck SDK 표준 방식 - sendToPlugin 이벤트 사용 - const payload = { - event: 'sendToPlugin', - context: uuid, - payload: message - }; - - websocket.send(JSON.stringify(payload)); - console.log('📤 Message sent to plugin:', command, data); - } catch (error) { - console.error('❌ Failed to send message to plugin:', error); - } -} - -// Update connection status display -function updateConnectionStatus(isConnected) { - console.log('🔄 Connection status update:', isConnected); - - // 전역 변수도 업데이트 - isUnityConnected = isConnected; - - if (statusDot) { - statusDot.className = `dot ${isConnected ? 'green' : 'red'}`; - } - - if (connectionStatus) { - connectionStatus.textContent = isConnected ? 'Unity 연결됨' : 'Unity 연결 안됨'; - connectionStatus.className = isConnected ? 'connected' : 'disconnected'; - } - - if (cameraSelect) { - cameraSelect.disabled = !isConnected; - } - - if (refreshButton) { - refreshButton.disabled = !isConnected; - } -} - -// Update camera data display -function updateCameraData(cameraDataParam, currentCamera) { - console.log('📹 Camera data update:', cameraDataParam, currentCamera); - - if (cameraSelect && cameraDataParam) { - // Clear existing options - cameraSelect.innerHTML = ''; - - // cameraDataParam이 직접 배열인지 확인 - let cameras = cameraDataParam; - if (cameraDataParam.cameras) { - cameras = cameraDataParam.cameras; - } else if (Array.isArray(cameraDataParam)) { - cameras = cameraDataParam; - } - - console.log('📹 처리할 카메라 배열:', cameras); - - if (cameras && cameras.length > 0) { - // 전역 변수에 카메라 데이터 저장 - cameraData = cameras; - console.log('💾 전역 cameraData 저장됨:', cameraData.length + '개'); - - // Add camera options - cameras.forEach((camera, index) => { - const option = document.createElement('option'); - option.value = index; - option.textContent = `카메라 ${index + 1}`; - if (camera.name) { - option.textContent += ` (${camera.name})`; - } - cameraSelect.appendChild(option); - }); - - // Set current selection - if (typeof currentCamera === 'number') { - cameraSelect.value = currentCamera; - } - - cameraSelect.disabled = false; - console.log('✅ 카메라 목록 업데이트 완료:', cameras.length + '개'); - } else { - console.log('⚠️ 카메라 데이터가 없거나 빈 배열'); - cameraSelect.disabled = true; - } - } - - // Update current camera display - updateCurrentCameraDisplay(currentCamera); -} - -// Update current camera display -function updateCurrentCameraDisplay(currentCamera) { - if (currentCameraDisplay) { - if (typeof currentCamera === 'number') { - currentCameraDisplay.textContent = `현재 카메라: ${currentCamera + 1}`; - } else { - currentCameraDisplay.textContent = '현재 카메라: -'; - } - } -} - -// Handle camera selection change -function onCameraSelectionChanged() { - if (!cameraSelect || !currentActionContext) return; - - const selectedIndex = parseInt(cameraSelect.value, 10); - if (isNaN(selectedIndex)) return; - - console.log('🎯 카메라 선택 변경:', selectedIndex); - console.log('📋 현재 cameraData:', cameraData); - console.log('📋 cameraData 길이:', cameraData ? cameraData.length : 'undefined'); - - // StreamDeck에 설정 저장 (Plugin Main에서 didReceiveSettings 이벤트 발생) - if (websocket) { - const setSettingsMessage = { - event: 'setSettings', - context: currentActionContext, - payload: { - cameraIndex: selectedIndex, - cameraList: cameraData // 현재 카메라 목록 포함 - } - }; - - console.log('📤 Plugin Main으로 전송할 데이터:', setSettingsMessage.payload); - websocket.send(JSON.stringify(setSettingsMessage)); - console.log('💾 설정 저장됨 - Plugin Main에서 버튼 제목 업데이트됨'); - } - - // UI 업데이트 - updateCurrentCameraDisplay(selectedIndex); -} - -// Handle refresh button click -function onRefreshClicked() { - console.log('🔄 새로고침 버튼 클릭 - Plugin Main에 카메라 목록 요청'); - - // Plugin Main에 카메라 목록 요청 - sendToPlugin('requestCameraList'); -} - -// Unity 연결은 Plugin Main에서만 처리 -function startUnityAutoReconnect() { - console.log('🩺 Property Inspector에서는 Unity 연결을 직접 관리하지 않음 - Plugin Main에서 처리됨'); -} - -// Unity 재연결 시도 (제거) -function attemptUnityReconnect() { - if (isShuttingDown || isConnecting || unityReconnectInterval) return; - - unityConnectionAttempts++; - - // 재연결 간격 조정 - let delay; - if (unityConnectionAttempts <= 3) { - delay = 2000; // 처음 3번은 2초 간격 - } else if (unityConnectionAttempts <= 10) { - delay = 5000; // 4-10번은 5초 간격 - } else { - delay = 30000; // 그 이후는 30초 간격 - } - - console.log(`🔄 [Property Inspector] ${delay/1000}초 후 Unity 재연결 시도... (${unityConnectionAttempts}번째 시도)`); - - unityReconnectInterval = setTimeout(() => { - unityReconnectInterval = null; - connectToUnity().catch(error => { - console.error(`❌ [Property Inspector] Unity 재연결 실패:`, error); - // 실패해도 계속 시도 - if (!isShuttingDown) { - attemptUnityReconnect(); - } - }); - }, delay); -} - -// Unity WebSocket 연결 (개선된 버전) -function connectToUnity() { - return new Promise((resolve, reject) => { - // 글로벌 상태 확인 - if (window.sharedUnityConnected && window.sharedUnitySocket) { - console.log('✅ [Property Inspector] 기존 Unity 연결 재사용'); - isUnityConnected = true; - unitySocket = window.sharedUnitySocket; - updateConnectionStatus(true); - resolve(); - return; - } - - if (isUnityConnected) { - console.log('✅ [Property Inspector] Unity 이미 연결됨'); - resolve(); - return; - } - - if (isConnecting) { - console.log('⏳ [Property Inspector] Unity 연결 중... 대기'); - reject(new Error('이미 연결 중')); - return; - } - - isConnecting = true; - window.sharedIsConnecting = true; - console.log(`🔌 [Property Inspector] Unity 연결 시도... (시도 ${unityConnectionAttempts + 1}회)`); - - try { - unitySocket = new WebSocket('ws://localhost:10701'); - - const connectionTimeout = setTimeout(() => { - isConnecting = false; - window.sharedIsConnecting = false; - console.log('⏰ [Property Inspector] Unity 연결 타임아웃'); - if (unitySocket) { - unitySocket.close(); - } - reject(new Error('연결 타임아웃')); - }, 5000); - - unitySocket.onopen = function() { - clearTimeout(connectionTimeout); - isConnecting = false; - isUnityConnected = true; - unityConnectionAttempts = 0; // 성공 시 재시도 카운터 리셋 - - // 재연결 타이머 정리 - if (unityReconnectInterval) { - clearTimeout(unityReconnectInterval); - unityReconnectInterval = null; - } - - // 글로벌 상태 저장 - window.sharedUnitySocket = unitySocket; - window.sharedUnityConnected = true; - window.sharedIsConnecting = false; - - console.log('✅ [Property Inspector] Unity 연결 성공!'); - updateConnectionStatus(true); - resolve(); - }; - - unitySocket.onmessage = function(event) { - try { - const message = JSON.parse(event.data); - handleUnityMessage(message); - } catch (error) { - console.error('❌ [Property Inspector] Unity 메시지 파싱 오류:', error); - } - }; - - unitySocket.onclose = function(event) { - clearTimeout(connectionTimeout); - const wasConnected = isUnityConnected; - isConnecting = false; - isUnityConnected = false; - - // 글로벌 상태 정리 - window.sharedUnitySocket = null; - window.sharedUnityConnected = false; - window.sharedIsConnecting = false; - - if (wasConnected) { - console.log(`❌ [Property Inspector] Unity 연결 끊어짐 (코드: ${event.code}, 이유: ${event.reason || '알 수 없음'})`); - } - updateConnectionStatus(false); - unitySocket = null; - - if (!event.wasClean) { - reject(new Error('연결 실패')); - } - - // 자동 재연결 시도 - if (!isShuttingDown) { - attemptUnityReconnect(); - } - }; - - unitySocket.onerror = function(error) { - clearTimeout(connectionTimeout); - isConnecting = false; - window.sharedIsConnecting = false; - console.error('❌ [Property Inspector] Unity 연결 오류:', error); - isUnityConnected = false; - updateConnectionStatus(false); - reject(error); - }; - - } catch (error) { - isConnecting = false; - window.sharedIsConnecting = false; - console.error('❌ [Property Inspector] Unity WebSocket 생성 실패:', error); - reject(error); - } - }); -} - -// Unity 메시지 처리 -function handleUnityMessage(message) { - const messageType = message.type; - - if (messageType === 'connection_established') { - console.log('🎉 Unity 연결 확인됨'); - if (message.data && message.data.camera_data) { - console.log('📹 연결 시 카메라 데이터 수신 (초기 로드)'); - updateCameraUI(message.data.camera_data.presets, message.data.camera_data.current_index); - // 이미 카메라 데이터를 받았으므로 추가 요청하지 않음 - cameraData = message.data.camera_data.presets; // 글로벌 변수에 저장 - window.sharedCameraData = cameraData; // 브라우저 세션에서 공유 - } - } else if (messageType === 'camera_list_response') { - console.log('📹 카메라 목록 응답 수신 (요청에 대한 응답)'); - if (message.data && message.data.camera_data) { - updateCameraUI(message.data.camera_data.presets, message.data.camera_data.current_index); - cameraData = message.data.camera_data.presets; // 글로벌 변수에 저장 - window.sharedCameraData = cameraData; // 브라우저 세션에서 공유 - } - } else if (messageType === 'camera_changed') { - console.log('📹 카메라 변경 알림 수신'); - if (message.data && typeof message.data.camera_index === 'number') { - updateCurrentCamera(message.data.camera_index); - } - } -} - -function updateCameraUI(cameras, currentIndex) { - if (!cameras || !Array.isArray(cameras)) { - console.error('❌ 잘못된 카메라 데이터'); - return; - } - - console.log('📹 카메라 UI 업데이트:', cameras.length + '개'); - - const cameraSelect = document.getElementById('camera-select'); - const currentCameraDisplay = document.getElementById('current-camera'); - - if (!cameraSelect) { - console.error('❌ camera-select 요소를 찾을 수 없음'); - return; - } - - // 카메라 목록 업데이트 - cameraSelect.innerHTML = ''; - - cameras.forEach((camera, index) => { - const option = document.createElement('option'); - option.value = index; - option.textContent = `${index + 1}. ${camera.name}`; - cameraSelect.appendChild(option); - }); - - // 현재 컨텍스트의 설정 가져오기 - const currentSettings = getContextSettings(currentActionContext); - - // 카메라 목록 저장 - const newSettings = { - ...currentSettings, - cameraList: cameras - }; - saveContextSettings(currentActionContext, newSettings); - - // 이미 설정된 카메라가 있으면 선택 - if (currentSettings && typeof currentSettings.cameraIndex === 'number') { - cameraSelect.value = currentSettings.cameraIndex; - - // 설정된 카메라의 이름으로 현재 표시만 업데이트 (버튼 제목은 Plugin Main에서 처리) - const selectedCamera = cameras[currentSettings.cameraIndex]; - if (selectedCamera) { - currentCameraDisplay.textContent = `현재: ${selectedCamera.name}`; - console.log('📋 기존 카메라 설정 복원:', selectedCamera.name); - } - } else { - // 설정이 없으면 Unity의 현재 카메라 사용 - if (typeof currentIndex === 'number' && currentIndex >= 0 && cameras[currentIndex]) { - cameraSelect.value = currentIndex; - currentCameraDisplay.textContent = `현재: ${cameras[currentIndex].name}`; - } else { - currentCameraDisplay.textContent = '현재: 없음'; - } - } - - console.log('✅ 카메라 UI 업데이트 완료'); -} - -// Unity에서 카메라 목록 요청 -function requestCameraListFromUnity() { - if (isUnityConnected && unitySocket) { - const message = JSON.stringify({ type: 'get_camera_list' }); - unitySocket.send(message); - console.log('📤 Unity에 카메라 목록 요청:', message); - } -} - -// Unity에서 카메라 전환 -function switchCameraInUnity(cameraIndex) { - if (isUnityConnected && unitySocket) { - const message = JSON.stringify({ - type: 'switch_camera', - data: { - camera_index: cameraIndex - } - }); - unitySocket.send(message); - console.log('📤 Unity에 카메라 전환 요청:', message); - } -} - -// Unity에서 받은 카메라 데이터로 UI 업데이트 -function updateCameraDataFromUnity() { - console.log('📹 Unity 카메라 데이터로 UI 업데이트:', cameraData); - - if (cameraSelect && cameraData && cameraData.length > 0) { - // Clear existing options - cameraSelect.innerHTML = ''; - - // Add camera options - cameraData.forEach((camera, index) => { - const option = document.createElement('option'); - option.value = index; - option.textContent = camera.name || `카메라 ${index + 1}`; - cameraSelect.appendChild(option); - }); - - // Set current selection - cameraSelect.value = currentCamera; - cameraSelect.disabled = false; - - console.log('✅ 카메라 선택 목록 업데이트 완료'); - } - - // Update current camera display - updateCurrentCameraDisplay(currentCamera); -} - -// Handle messages from plugin -function handleMessage(jsonObj) { - console.log('📨 Message received:', jsonObj); - - try { - if (jsonObj.event === 'sendToPropertyInspector') { - // payload는 직접 객체로 전달됨 - const payload = jsonObj.payload; - - console.log('📋 Parsed payload:', payload); - - switch (payload.type || payload.command) { - case 'connection_status': - console.log('📡 연결 상태 업데이트:', payload.connected); - updateConnectionStatus(payload.connected); - break; - - case 'camera_data': - console.log('📹 카메라 데이터 업데이트 수신:', payload); - console.log('📹 카메라 데이터 상세:', payload.camera_data); - updateCameraData(payload.camera_data, payload.current_camera); - break; - - case 'current_settings': - console.log('⚙️ 현재 설정 수신:', payload); - // 현재 설정을 컨텍스트에 저장 - if (currentActionContext) { - const newSettings = { - cameraIndex: payload.cameraIndex || 0, - cameraList: payload.cameraList || [], - isUnityConnected: payload.isUnityConnected || false - }; - saveContextSettings(currentActionContext, newSettings); - - // UI 업데이트 - updateConnectionStatus(payload.isUnityConnected); - if (payload.cameraList && payload.cameraList.length > 0) { - updateCameraData({ cameras: payload.cameraList }, payload.cameraIndex); - } - } - break; - - case 'camera_changed': - console.log('📹 카메라 변경:', payload.current_camera); - updateCurrentCameraDisplay(payload.current_camera); - break; - - default: - console.log('❓ Unknown message type:', payload.type || payload.command); - } - } else if (jsonObj.event === 'didReceiveSettings') { - // didReceiveSettings 이벤트 처리 추가 - console.log('⚙️ didReceiveSettings 이벤트 수신:', jsonObj); - - const settings = jsonObj.payload.settings || {}; - console.log('📋 설정 데이터:', settings); - - // 컨텍스트에 설정 저장 - if (currentActionContext) { - saveContextSettings(currentActionContext, settings); - - // UI 업데이트 - if (settings.cameraList && settings.cameraList.length > 0) { - console.log('📹 카메라 목록으로 UI 업데이트:', settings.cameraList.length + '개'); - updateCameraData(settings.cameraList, settings.cameraIndex || 0); - - // 카메라 목록이 있다면 Unity가 연결되어 있다고 판단 - console.log('🔍 카메라 목록 존재 - Unity 연결됨으로 판단'); - updateConnectionStatus(true); - } - - // 연결 상태 업데이트 (카메라 목록이 없을 때만 설정값 사용) - if (typeof settings.isUnityConnected === 'boolean' && (!settings.cameraList || settings.cameraList.length === 0)) { - updateConnectionStatus(settings.isUnityConnected); - } - } - } - } catch (error) { - console.error('❌ Failed to handle message:', error); - } -} - -// StreamDeck SDK connection -function connectElgatoStreamDeckSocket(inPort, inPropertyInspectorUUID, inRegisterEvent, inInfo, inActionInfo) { - uuid = inPropertyInspectorUUID; - - console.log('🔌 StreamDeck 연결 중...'); - - // Parse info - try { - const info = JSON.parse(inInfo); - const actionInfo = JSON.parse(inActionInfo); - actionContext = actionInfo.context; // 액션 컨텍스트 저장 - currentActionContext = actionInfo.context; // 현재 액션 컨텍스트 설정 - settings = actionInfo.payload.settings || {}; - - // 컨텍스트별 설정 초기화 - saveContextSettings(currentActionContext, settings); - - console.log('📋 컨텍스트 설정 완료:', currentActionContext); - } catch (error) { - console.error('❌ 정보 파싱 실패:', error); - } - - // Connect to StreamDock - websocket = new WebSocket('ws://127.0.0.1:' + inPort); - - websocket.onopen = function() { - console.log('✅ StreamDeck 연결됨'); - - // Register - const json = { - event: inRegisterEvent, - uuid: uuid - }; - - websocket.send(JSON.stringify(json)); - - // Plugin Main에 초기 설정 요청 - console.log('📤 Plugin Main에 초기 설정 요청'); - sendToPlugin('getInitialSettings'); - }; - - websocket.onmessage = function(evt) { - try { - const jsonObj = JSON.parse(evt.data); - handleMessage(jsonObj); - } catch (error) { - console.error('❌ Failed to parse message:', error); - } - }; - - websocket.onclose = function() { - console.log('❌ StreamDeck WebSocket closed'); - isShuttingDown = true; // 종료 플래그 설정 - - // Unity 자동 재연결 정리 - if (unityReconnectInterval) { - clearTimeout(unityReconnectInterval); - unityReconnectInterval = null; - } - if (unityHealthCheckInterval) { - clearInterval(unityHealthCheckInterval); - unityHealthCheckInterval = null; - } - - websocket = null; - }; - - websocket.onerror = function(error) { - console.error('❌ StreamDeck WebSocket error:', error); - }; -} - -// 컨텍스트별 설정 관리 함수들 -function getContextSettings(context) { - if (!context) return {}; - return contextSettings.get(context) || {}; -} - -function saveContextSettings(context, newSettings) { - if (!context) return; - contextSettings.set(context, { ...newSettings }); - console.log('💾 컨텍스트 설정 저장:', context, newSettings); -} - -// 현재 컨텍스트의 설정에서 카메라 이름을 가져오는 공통 함수 -function getCurrentCameraName(context, cameraIndex = null) { - if (!context) return '카메라\n선택'; - - const settings = getContextSettings(context); - if (!settings || !settings.cameraList) return '카메라\n선택'; - - // cameraIndex가 제공되면 그것을 사용, 아니면 설정에서 가져옴 - const index = cameraIndex !== null ? cameraIndex : settings.cameraIndex; - if (typeof index !== 'number' || !settings.cameraList[index]) return '카메라\n선택'; - - return settings.cameraList[index].name || '카메라\n선택'; -} - -// 버튼 제목 업데이트 공통 함수 (Plugin Main과 동일한 로직) -function updateButtonTitle(context, cameraName = null, cameraIndex = null) { - if (!websocket || !context) return; - - // cameraName이 제공되지 않으면 현재 설정에서 가져옴 - if (!cameraName) { - cameraName = getCurrentCameraName(context, cameraIndex); - console.log(`🔍 [Property Inspector] getCurrentCameraName 결과: "${cameraName}"`); - - // 디버깅을 위해 현재 설정 상태도 출력 - const settings = getContextSettings(context); - console.log(`🔍 [Property Inspector] 컨텍스트 설정:`, settings); - console.log(`🔍 [Property Inspector] 카메라 인덱스: ${cameraIndex !== null ? cameraIndex : settings.cameraIndex}, 목록 길이: ${settings.cameraList ? settings.cameraList.length : 0}`); - } else { - console.log(`🔍 [Property Inspector] 직접 제공된 카메라 이름: "${cameraName}"`); - } - - // 기본값 설정 - let title = cameraName || '카메라\n선택'; - console.log(`🔍 [Property Inspector] 최종 사용할 제목 (가공 전): "${title}"`); - - // 긴 텍스트를 두 줄로 나누기 (Plugin Main과 동일한 로직) - if (title.length > 8) { - const underscoreIndex = title.indexOf('_'); - - if (underscoreIndex !== -1 && underscoreIndex > 0) { - // 언더스코어가 있으면 그 위치에서 분할하고 언더스코어는 제거 - const firstLine = title.substring(0, underscoreIndex); - const secondLine = title.substring(underscoreIndex + 1); // +1로 언더스코어 제거 - - // 각 줄이 너무 길면 적절히 자르기 - const maxLineLength = 8; - let line1 = firstLine.length > maxLineLength ? firstLine.substring(0, maxLineLength - 1) + '.' : firstLine; - let line2 = secondLine.length > maxLineLength ? secondLine.substring(0, maxLineLength - 1) + '.' : secondLine; - - title = line1 + '\n' + line2; - } else { - // 언더스코어가 없으면 중간 지점에서 분할 - const midPoint = Math.ceil(title.length / 2); - const firstLine = title.substring(0, midPoint); - const secondLine = title.substring(midPoint); - title = firstLine + '\n' + secondLine; - } - } - - // 버튼 제목 설정 (Plugin Main과 완전히 동일한 매개변수) - const message = { - event: 'setTitle', - context: context, - payload: { - title: title, - target: 0, // 하드웨어와 소프트웨어 모두 - titleParameters: { - fontSize: 18, - showTitle: true, - titleAlignment: "middle" - } - } - }; - - websocket.send(JSON.stringify(message)); - console.log('🏷️ [Property Inspector] 버튼 제목 업데이트:', title.replace('\n', '\\n')); -} \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/action.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/action.js deleted file mode 100644 index 7cc9620ab..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/action.js +++ /dev/null @@ -1,157 +0,0 @@ -let $websocket, $uuid, $action, $context, $settings, $lang, $FileID = ''; - -WebSocket.prototype.setGlobalSettings = function(payload) { - this.send(JSON.stringify({ - event: "setGlobalSettings", - context: $uuid, payload - })); -} - -WebSocket.prototype.getGlobalSettings = function() { - this.send(JSON.stringify({ - event: "getGlobalSettings", - context: $uuid, - })); -} - -// 与插件通信 -WebSocket.prototype.sendToPlugin = function (payload) { - this.send(JSON.stringify({ - event: "sendToPlugin", - action: $action, - context: $uuid, - payload - })); -}; - -//设置标题 -WebSocket.prototype.setTitle = function (str, row = 0, num = 6) { - console.log(str); - let newStr = ''; - if (row) { - let nowRow = 1, strArr = str.split(''); - strArr.forEach((item, index) => { - if (nowRow < row && index >= nowRow * num) { nowRow++; newStr += '\n'; } - if (nowRow <= row && index < nowRow * num) { newStr += item; } - }); - if (strArr.length > row * num) { newStr = newStr.substring(0, newStr.length - 1); newStr += '..'; } - } - this.send(JSON.stringify({ - event: "setTitle", - context: $context, - payload: { - target: 0, - title: newStr || str - } - })); -} - -// 设置状态 -WebSocket.prototype.setState = function (state) { - this.send(JSON.stringify({ - event: "setState", - context: $context, - payload: { state } - })); -}; - -// 设置背景 -WebSocket.prototype.setImage = function (url) { - let image = new Image(); - image.src = url; - image.onload = () => { - let canvas = document.createElement("canvas"); - canvas.width = image.naturalWidth; - canvas.height = image.naturalHeight; - let ctx = canvas.getContext("2d"); - ctx.drawImage(image, 0, 0); - this.send(JSON.stringify({ - event: "setImage", - context: $context, - payload: { - target: 0, - image: canvas.toDataURL("image/png") - } - })); - }; -}; - -// 打开网页 -WebSocket.prototype.openUrl = function (url) { - this.send(JSON.stringify({ - event: "openUrl", - payload: { url } - })); -}; - -// 保存持久化数据 -WebSocket.prototype.saveData = $.debounce(function (payload) { - this.send(JSON.stringify({ - event: "setSettings", - context: $uuid, - payload - })); -}); - -// StreamDock 软件入口函数 -const connectSocket = connectElgatoStreamDeckSocket; -async function connectElgatoStreamDeckSocket(port, uuid, event, app, info) { - info = JSON.parse(info); - $uuid = uuid; $action = info.action; - $context = info.context; - $websocket = new WebSocket('ws://127.0.0.1:' + port); - $websocket.onopen = () => $websocket.send(JSON.stringify({ event, uuid })); - - // 持久数据代理 - $websocket.onmessage = e => { - let data = JSON.parse(e.data); - if (data.event === 'didReceiveSettings') { - $settings = new Proxy(data.payload.settings, { - get(target, property) { - return target[property]; - }, - set(target, property, value) { - target[property] = value; - $websocket.saveData(data.payload.settings); - } - }); - if (!$back) $dom.main.style.display = 'block'; - } - $propEvent[data.event]?.(data.payload); - }; - - // 自动翻译页面 - if (!$local) return; - $lang = await new Promise(resolve => { - const req = new XMLHttpRequest(); - req.open('GET', `../../${JSON.parse(app).application.language}.json`); - req.send(); - req.onreadystatechange = () => { - if (req.readyState === 4) { - resolve(JSON.parse(req.responseText).Localization); - } - }; - }); - - // 遍历文本节点并翻译所有文本节点 - const walker = document.createTreeWalker($dom.main, NodeFilter.SHOW_TEXT, (e) => { - return e.data.trim() && NodeFilter.FILTER_ACCEPT; - }); - while (walker.nextNode()) { - console.log(walker.currentNode.data); - walker.currentNode.data = $lang[walker.currentNode.data]; - } - // placeholder 特殊处理 - const translate = item => { - if (item.placeholder?.trim()) { - console.log(item.placeholder); - item.placeholder = $lang[item.placeholder]; - } - }; - $('input', true).forEach(translate); - $('textarea', true).forEach(translate); -} - -// StreamDock 文件路径回调 -Array.from($('input[type="file"]', true)).forEach(item => item.addEventListener('click', () => $FileID = item.id)); -const onFilePickerReturn = (url) => $emit.send(`File-${$FileID}`, JSON.parse(url)); \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/axios.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/axios.js deleted file mode 100644 index 78aa7b89d..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/axios.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).axios=t()}(this,(function(){"use strict";function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(t)}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n2&&void 0!==arguments[2]?arguments[2]:{},a=i.allOwnKeys,s=void 0!==a&&a;if(null!=t)if("object"!==e(t)&&(t=[t]),p(t))for(r=0,o=t.length;r0;)if(t===(n=r[o]).toLowerCase())return n;return null}var C="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:global,N=function(e){return!h(e)&&e!==C};var x,P=(x="undefined"!=typeof Uint8Array&&c(Uint8Array),function(e){return x&&e instanceof x}),k=l("HTMLFormElement"),U=function(e){var t=Object.prototype.hasOwnProperty;return function(e,n){return t.call(e,n)}}(),_=l("RegExp"),F=function(e,t){var n=Object.getOwnPropertyDescriptors(e),r={};T(n,(function(n,o){var i;!1!==(i=t(n,o,e))&&(r[o]=i||n)})),Object.defineProperties(e,r)},B="abcdefghijklmnopqrstuvwxyz",L="0123456789",D={DIGIT:L,ALPHA:B,ALPHA_DIGIT:B+B.toUpperCase()+L};var I=l("AsyncFunction"),q={isArray:p,isArrayBuffer:m,isBuffer:function(e){return null!==e&&!h(e)&&null!==e.constructor&&!h(e.constructor)&&y(e.constructor.isBuffer)&&e.constructor.isBuffer(e)},isFormData:function(e){var t;return e&&("function"==typeof FormData&&e instanceof FormData||y(e.append)&&("formdata"===(t=f(e))||"object"===t&&y(e.toString)&&"[object FormData]"===e.toString()))},isArrayBufferView:function(e){return"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&m(e.buffer)},isString:v,isNumber:b,isBoolean:function(e){return!0===e||!1===e},isObject:g,isPlainObject:w,isUndefined:h,isDate:E,isFile:O,isBlob:S,isRegExp:_,isFunction:y,isStream:function(e){return g(e)&&y(e.pipe)},isURLSearchParams:A,isTypedArray:P,isFileList:R,forEach:T,merge:function e(){for(var t=N(this)&&this||{},n=t.caseless,r={},o=function(t,o){var i=n&&j(r,o)||o;w(r[i])&&w(t)?r[i]=e(r[i],t):w(t)?r[i]=e({},t):p(t)?r[i]=t.slice():r[i]=t},i=0,a=arguments.length;i3&&void 0!==arguments[3]?arguments[3]:{},o=r.allOwnKeys;return T(t,(function(t,r){n&&y(t)?e[r]=a(t,n):e[r]=t}),{allOwnKeys:o}),e},trim:function(e){return e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")},stripBOM:function(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e},inherits:function(e,t,n,r){e.prototype=Object.create(t.prototype,r),e.prototype.constructor=e,Object.defineProperty(e,"super",{value:t.prototype}),n&&Object.assign(e.prototype,n)},toFlatObject:function(e,t,n,r){var o,i,a,s={};if(t=t||{},null==e)return t;do{for(i=(o=Object.getOwnPropertyNames(e)).length;i-- >0;)a=o[i],r&&!r(a,e,t)||s[a]||(t[a]=e[a],s[a]=!0);e=!1!==n&&c(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},kindOf:f,kindOfTest:l,endsWith:function(e,t,n){e=String(e),(void 0===n||n>e.length)&&(n=e.length),n-=t.length;var r=e.indexOf(t,n);return-1!==r&&r===n},toArray:function(e){if(!e)return null;if(p(e))return e;var t=e.length;if(!b(t))return null;for(var n=new Array(t);t-- >0;)n[t]=e[t];return n},forEachEntry:function(e,t){for(var n,r=(e&&e[Symbol.iterator]).call(e);(n=r.next())&&!n.done;){var o=n.value;t.call(e,o[0],o[1])}},matchAll:function(e,t){for(var n,r=[];null!==(n=e.exec(t));)r.push(n);return r},isHTMLForm:k,hasOwnProperty:U,hasOwnProp:U,reduceDescriptors:F,freezeMethods:function(e){F(e,(function(t,n){if(y(e)&&-1!==["arguments","caller","callee"].indexOf(n))return!1;var r=e[n];y(r)&&(t.enumerable=!1,"writable"in t?t.writable=!1:t.set||(t.set=function(){throw Error("Can not rewrite read-only method '"+n+"'")}))}))},toObjectSet:function(e,t){var n={},r=function(e){e.forEach((function(e){n[e]=!0}))};return p(e)?r(e):r(String(e).split(t)),n},toCamelCase:function(e){return e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,(function(e,t,n){return t.toUpperCase()+n}))},noop:function(){},toFiniteNumber:function(e,t){return e=+e,Number.isFinite(e)?e:t},findKey:j,global:C,isContextDefined:N,ALPHABET:D,generateString:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:16,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:D.ALPHA_DIGIT,n="",r=t.length;e--;)n+=t[Math.random()*r|0];return n},isSpecCompliantForm:function(e){return!!(e&&y(e.append)&&"FormData"===e[Symbol.toStringTag]&&e[Symbol.iterator])},toJSONObject:function(e){var t=new Array(10);return function e(n,r){if(g(n)){if(t.indexOf(n)>=0)return;if(!("toJSON"in n)){t[r]=n;var o=p(n)?[]:{};return T(n,(function(t,n){var i=e(t,r+1);!h(i)&&(o[n]=i)})),t[r]=void 0,o}}return n}(e,0)},isAsyncFn:I,isThenable:function(e){return e&&(g(e)||y(e))&&y(e.then)&&y(e.catch)}};function M(e,t,n,r,o){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack,this.message=e,this.name="AxiosError",t&&(this.code=t),n&&(this.config=n),r&&(this.request=r),o&&(this.response=o)}q.inherits(M,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:q.toJSONObject(this.config),code:this.code,status:this.response&&this.response.status?this.response.status:null}}});var z=M.prototype,H={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach((function(e){H[e]={value:e}})),Object.defineProperties(M,H),Object.defineProperty(z,"isAxiosError",{value:!0}),M.from=function(e,t,n,r,o,i){var a=Object.create(z);return q.toFlatObject(e,a,(function(e){return e!==Error.prototype}),(function(e){return"isAxiosError"!==e})),M.call(a,e.message,t,n,r,o),a.cause=e,a.name=e.name,i&&Object.assign(a,i),a};function J(e){return q.isPlainObject(e)||q.isArray(e)}function W(e){return q.endsWith(e,"[]")?e.slice(0,-2):e}function K(e,t,n){return e?e.concat(t).map((function(e,t){return e=W(e),!n&&t?"["+e+"]":e})).join(n?".":""):t}var V=q.toFlatObject(q,{},null,(function(e){return/^is[A-Z]/.test(e)}));function G(t,n,r){if(!q.isObject(t))throw new TypeError("target must be an object");n=n||new FormData;var o=(r=q.toFlatObject(r,{metaTokens:!0,dots:!1,indexes:!1},!1,(function(e,t){return!q.isUndefined(t[e])}))).metaTokens,i=r.visitor||f,a=r.dots,s=r.indexes,u=(r.Blob||"undefined"!=typeof Blob&&Blob)&&q.isSpecCompliantForm(n);if(!q.isFunction(i))throw new TypeError("visitor must be a function");function c(e){if(null===e)return"";if(q.isDate(e))return e.toISOString();if(!u&&q.isBlob(e))throw new M("Blob is not supported. Use a Buffer instead.");return q.isArrayBuffer(e)||q.isTypedArray(e)?u&&"function"==typeof Blob?new Blob([e]):Buffer.from(e):e}function f(t,r,i){var u=t;if(t&&!i&&"object"===e(t))if(q.endsWith(r,"{}"))r=o?r:r.slice(0,-2),t=JSON.stringify(t);else if(q.isArray(t)&&function(e){return q.isArray(e)&&!e.some(J)}(t)||(q.isFileList(t)||q.endsWith(r,"[]"))&&(u=q.toArray(t)))return r=W(r),u.forEach((function(e,t){!q.isUndefined(e)&&null!==e&&n.append(!0===s?K([r],t,a):null===s?r:r+"[]",c(e))})),!1;return!!J(t)||(n.append(K(i,r,a),c(t)),!1)}var l=[],d=Object.assign(V,{defaultVisitor:f,convertValue:c,isVisitable:J});if(!q.isObject(t))throw new TypeError("data must be an object");return function e(t,r){if(!q.isUndefined(t)){if(-1!==l.indexOf(t))throw Error("Circular reference detected in "+r.join("."));l.push(t),q.forEach(t,(function(t,o){!0===(!(q.isUndefined(t)||null===t)&&i.call(n,t,q.isString(o)?o.trim():o,r,d))&&e(t,r?r.concat(o):[o])})),l.pop()}}(t),n}function $(e){var t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,(function(e){return t[e]}))}function X(e,t){this._pairs=[],e&&G(e,this,t)}var Q=X.prototype;function Z(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}function Y(e,t,n){if(!t)return e;var r,o=n&&n.encode||Z,i=n&&n.serialize;if(r=i?i(t,n):q.isURLSearchParams(t)?t.toString():new X(t,n).toString(o)){var a=e.indexOf("#");-1!==a&&(e=e.slice(0,a)),e+=(-1===e.indexOf("?")?"?":"&")+r}return e}Q.append=function(e,t){this._pairs.push([e,t])},Q.toString=function(e){var t=e?function(t){return e.call(this,t,$)}:$;return this._pairs.map((function(e){return t(e[0])+"="+t(e[1])}),"").join("&")};var ee,te=function(){function e(){t(this,e),this.handlers=[]}return r(e,[{key:"use",value:function(e,t,n){return this.handlers.push({fulfilled:e,rejected:t,synchronous:!!n&&n.synchronous,runWhen:n?n.runWhen:null}),this.handlers.length-1}},{key:"eject",value:function(e){this.handlers[e]&&(this.handlers[e]=null)}},{key:"clear",value:function(){this.handlers&&(this.handlers=[])}},{key:"forEach",value:function(e){q.forEach(this.handlers,(function(t){null!==t&&e(t)}))}}]),e}(),ne={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},re={isBrowser:!0,classes:{URLSearchParams:"undefined"!=typeof URLSearchParams?URLSearchParams:X,FormData:"undefined"!=typeof FormData?FormData:null,Blob:"undefined"!=typeof Blob?Blob:null},isStandardBrowserEnv:("undefined"==typeof navigator||"ReactNative"!==(ee=navigator.product)&&"NativeScript"!==ee&&"NS"!==ee)&&"undefined"!=typeof window&&"undefined"!=typeof document,isStandardBrowserWebWorkerEnv:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope&&"function"==typeof self.importScripts,protocols:["http","https","file","blob","url","data"]};function oe(e){function t(e,n,r,o){var i=e[o++],a=Number.isFinite(+i),s=o>=e.length;return i=!i&&q.isArray(r)?r.length:i,s?(q.hasOwnProp(r,i)?r[i]=[r[i],n]:r[i]=n,!a):(r[i]&&q.isObject(r[i])||(r[i]=[]),t(e,n,r[i],o)&&q.isArray(r[i])&&(r[i]=function(e){var t,n,r={},o=Object.keys(e),i=o.length;for(t=0;t-1,i=q.isObject(e);if(i&&q.isHTMLForm(e)&&(e=new FormData(e)),q.isFormData(e))return o&&o?JSON.stringify(oe(e)):e;if(q.isArrayBuffer(e)||q.isBuffer(e)||q.isStream(e)||q.isFile(e)||q.isBlob(e))return e;if(q.isArrayBufferView(e))return e.buffer;if(q.isURLSearchParams(e))return t.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),e.toString();if(i){if(r.indexOf("application/x-www-form-urlencoded")>-1)return function(e,t){return G(e,new re.classes.URLSearchParams,Object.assign({visitor:function(e,t,n,r){return re.isNode&&q.isBuffer(e)?(this.append(t,e.toString("base64")),!1):r.defaultVisitor.apply(this,arguments)}},t))}(e,this.formSerializer).toString();if((n=q.isFileList(e))||r.indexOf("multipart/form-data")>-1){var a=this.env&&this.env.FormData;return G(n?{"files[]":e}:e,a&&new a,this.formSerializer)}}return i||o?(t.setContentType("application/json",!1),function(e,t,n){if(q.isString(e))try{return(t||JSON.parse)(e),q.trim(e)}catch(e){if("SyntaxError"!==e.name)throw e}return(n||JSON.stringify)(e)}(e)):e}],transformResponse:[function(e){var t=this.transitional||ie.transitional,n=t&&t.forcedJSONParsing,r="json"===this.responseType;if(e&&q.isString(e)&&(n&&!this.responseType||r)){var o=!(t&&t.silentJSONParsing)&&r;try{return JSON.parse(e)}catch(e){if(o){if("SyntaxError"===e.name)throw M.from(e,M.ERR_BAD_RESPONSE,this,null,this.response);throw e}}}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:re.classes.FormData,Blob:re.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*","Content-Type":void 0}}};q.forEach(["delete","get","head","post","put","patch"],(function(e){ie.headers[e]={}}));var ae=ie,se=q.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),ue=Symbol("internals");function ce(e){return e&&String(e).trim().toLowerCase()}function fe(e){return!1===e||null==e?e:q.isArray(e)?e.map(fe):String(e)}function le(e,t,n,r,o){return q.isFunction(r)?r.call(this,t,n):(o&&(t=n),q.isString(t)?q.isString(r)?-1!==t.indexOf(r):q.isRegExp(r)?r.test(t):void 0:void 0)}var de=function(e,n){function i(e){t(this,i),e&&this.set(e)}return r(i,[{key:"set",value:function(e,t,n){var r=this;function o(e,t,n){var o=ce(t);if(!o)throw new Error("header name must be a non-empty string");var i=q.findKey(r,o);(!i||void 0===r[i]||!0===n||void 0===n&&!1!==r[i])&&(r[i||t]=fe(e))}var i,a,s,u,c,f=function(e,t){return q.forEach(e,(function(e,n){return o(e,n,t)}))};return q.isPlainObject(e)||e instanceof this.constructor?f(e,t):q.isString(e)&&(e=e.trim())&&!/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim())?f((c={},(i=e)&&i.split("\n").forEach((function(e){u=e.indexOf(":"),a=e.substring(0,u).trim().toLowerCase(),s=e.substring(u+1).trim(),!a||c[a]&&se[a]||("set-cookie"===a?c[a]?c[a].push(s):c[a]=[s]:c[a]=c[a]?c[a]+", "+s:s)})),c),t):null!=e&&o(t,e,n),this}},{key:"get",value:function(e,t){if(e=ce(e)){var n=q.findKey(this,e);if(n){var r=this[n];if(!t)return r;if(!0===t)return function(e){for(var t,n=Object.create(null),r=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;t=r.exec(e);)n[t[1]]=t[2];return n}(r);if(q.isFunction(t))return t.call(this,r,n);if(q.isRegExp(t))return t.exec(r);throw new TypeError("parser must be boolean|regexp|function")}}}},{key:"has",value:function(e,t){if(e=ce(e)){var n=q.findKey(this,e);return!(!n||void 0===this[n]||t&&!le(0,this[n],n,t))}return!1}},{key:"delete",value:function(e,t){var n=this,r=!1;function o(e){if(e=ce(e)){var o=q.findKey(n,e);!o||t&&!le(0,n[o],o,t)||(delete n[o],r=!0)}}return q.isArray(e)?e.forEach(o):o(e),r}},{key:"clear",value:function(e){for(var t=Object.keys(this),n=t.length,r=!1;n--;){var o=t[n];e&&!le(0,this[o],o,e,!0)||(delete this[o],r=!0)}return r}},{key:"normalize",value:function(e){var t=this,n={};return q.forEach(this,(function(r,o){var i=q.findKey(n,o);if(i)return t[i]=fe(r),void delete t[o];var a=e?function(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(function(e,t,n){return t.toUpperCase()+n}))}(o):String(o).trim();a!==o&&delete t[o],t[a]=fe(r),n[a]=!0})),this}},{key:"concat",value:function(){for(var e,t=arguments.length,n=new Array(t),r=0;r1?n-1:0),o=1;o1?"since :\n"+u.map(Oe).join("\n"):" "+Oe(u[0]):"as no adapter specified"),"ERR_NOT_SUPPORT")}return n};function Ae(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new ve(null,e)}function Te(e){return Ae(e),e.headers=pe.from(e.headers),e.data=he.call(e,e.transformRequest),-1!==["post","put","patch"].indexOf(e.method)&&e.headers.setContentType("application/x-www-form-urlencoded",!1),Re(e.adapter||ae.adapter)(e).then((function(t){return Ae(e),t.data=he.call(e,e.transformResponse,t),t.headers=pe.from(t.headers),t}),(function(t){return me(t)||(Ae(e),t&&t.response&&(t.response.data=he.call(e,e.transformResponse,t.response),t.response.headers=pe.from(t.response.headers))),Promise.reject(t)}))}var je=function(e){return e instanceof pe?e.toJSON():e};function Ce(e,t){t=t||{};var n={};function r(e,t,n){return q.isPlainObject(e)&&q.isPlainObject(t)?q.merge.call({caseless:n},e,t):q.isPlainObject(t)?q.merge({},t):q.isArray(t)?t.slice():t}function o(e,t,n){return q.isUndefined(t)?q.isUndefined(e)?void 0:r(void 0,e,n):r(e,t,n)}function i(e,t){if(!q.isUndefined(t))return r(void 0,t)}function a(e,t){return q.isUndefined(t)?q.isUndefined(e)?void 0:r(void 0,e):r(void 0,t)}function s(n,o,i){return i in t?r(n,o):i in e?r(void 0,n):void 0}var u={url:i,method:i,data:i,baseURL:a,transformRequest:a,transformResponse:a,paramsSerializer:a,timeout:a,timeoutMessage:a,withCredentials:a,adapter:a,responseType:a,xsrfCookieName:a,xsrfHeaderName:a,onUploadProgress:a,onDownloadProgress:a,decompress:a,maxContentLength:a,maxBodyLength:a,beforeRedirect:a,transport:a,httpAgent:a,httpsAgent:a,cancelToken:a,socketPath:a,responseEncoding:a,validateStatus:s,headers:function(e,t){return o(je(e),je(t),!0)}};return q.forEach(Object.keys(Object.assign({},e,t)),(function(r){var i=u[r]||o,a=i(e[r],t[r],r);q.isUndefined(a)&&i!==s||(n[r]=a)})),n}var Ne="1.5.1",xe={};["object","boolean","number","function","string","symbol"].forEach((function(t,n){xe[t]=function(r){return e(r)===t||"a"+(n<1?"n ":" ")+t}}));var Pe={};xe.transitional=function(e,t,n){function r(e,t){return"[Axios v1.5.1] Transitional option '"+e+"'"+t+(n?". "+n:"")}return function(n,o,i){if(!1===e)throw new M(r(o," has been removed"+(t?" in "+t:"")),M.ERR_DEPRECATED);return t&&!Pe[o]&&(Pe[o]=!0,console.warn(r(o," has been deprecated since v"+t+" and will be removed in the near future"))),!e||e(n,o,i)}};var ke={assertOptions:function(t,n,r){if("object"!==e(t))throw new M("options must be an object",M.ERR_BAD_OPTION_VALUE);for(var o=Object.keys(t),i=o.length;i-- >0;){var a=o[i],s=n[a];if(s){var u=t[a],c=void 0===u||s(u,a,t);if(!0!==c)throw new M("option "+a+" must be "+c,M.ERR_BAD_OPTION_VALUE)}else if(!0!==r)throw new M("Unknown option "+a,M.ERR_BAD_OPTION)}},validators:xe},Ue=ke.validators,_e=function(){function e(n){t(this,e),this.defaults=n,this.interceptors={request:new te,response:new te}}return r(e,[{key:"request",value:function(e,t){"string"==typeof e?(t=t||{}).url=e:t=e||{};var n=t=Ce(this.defaults,t),r=n.transitional,o=n.paramsSerializer,i=n.headers;void 0!==r&&ke.assertOptions(r,{silentJSONParsing:Ue.transitional(Ue.boolean),forcedJSONParsing:Ue.transitional(Ue.boolean),clarifyTimeoutError:Ue.transitional(Ue.boolean)},!1),null!=o&&(q.isFunction(o)?t.paramsSerializer={serialize:o}:ke.assertOptions(o,{encode:Ue.function,serialize:Ue.function},!0)),t.method=(t.method||this.defaults.method||"get").toLowerCase();var a=i&&q.merge(i.common,i[t.method]);i&&q.forEach(["delete","get","head","post","put","patch","common"],(function(e){delete i[e]})),t.headers=pe.concat(a,i);var s=[],u=!0;this.interceptors.request.forEach((function(e){"function"==typeof e.runWhen&&!1===e.runWhen(t)||(u=u&&e.synchronous,s.unshift(e.fulfilled,e.rejected))}));var c,f=[];this.interceptors.response.forEach((function(e){f.push(e.fulfilled,e.rejected)}));var l,d=0;if(!u){var p=[Te.bind(this),void 0];for(p.unshift.apply(p,s),p.push.apply(p,f),l=p.length,c=Promise.resolve(t);d0;)o._listeners[t](e);o._listeners=null}})),this.promise.then=function(e){var t,n=new Promise((function(e){o.subscribe(e),t=e})).then(e);return n.cancel=function(){o.unsubscribe(t)},n},n((function(e,t,n){o.reason||(o.reason=new ve(e,t,n),r(o.reason))}))}return r(e,[{key:"throwIfRequested",value:function(){if(this.reason)throw this.reason}},{key:"subscribe",value:function(e){this.reason?e(this.reason):this._listeners?this._listeners.push(e):this._listeners=[e]}},{key:"unsubscribe",value:function(e){if(this._listeners){var t=this._listeners.indexOf(e);-1!==t&&this._listeners.splice(t,1)}}}],[{key:"source",value:function(){var t;return{token:new e((function(e){t=e})),cancel:t}}}]),e}();var Le={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(Le).forEach((function(e){var t=o(e,2),n=t[0],r=t[1];Le[r]=n}));var De=Le;var Ie=function e(t){var n=new Fe(t),r=a(Fe.prototype.request,n);return q.extend(r,Fe.prototype,n,{allOwnKeys:!0}),q.extend(r,n,null,{allOwnKeys:!0}),r.create=function(n){return e(Ce(t,n))},r}(ae);return Ie.Axios=Fe,Ie.CanceledError=ve,Ie.CancelToken=Be,Ie.isCancel=me,Ie.VERSION=Ne,Ie.toFormData=G,Ie.AxiosError=M,Ie.Cancel=Ie.CanceledError,Ie.all=function(e){return Promise.all(e)},Ie.spread=function(e){return function(t){return e.apply(null,t)}},Ie.isAxiosError=function(e){return q.isObject(e)&&!0===e.isAxiosError},Ie.mergeConfig=Ce,Ie.AxiosHeaders=pe,Ie.formToJSON=function(e){return oe(q.isHTMLForm(e)?new FormData(e):e)},Ie.getAdapter=Re,Ie.HttpStatusCode=De,Ie.default=Ie,Ie})); \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/bootstrap-icons.css b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/bootstrap-icons.css deleted file mode 100644 index 3c1537f1b..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/bootstrap-icons.css +++ /dev/null @@ -1,1556 +0,0 @@ -@font-face { - font-family: "bootstrap-icons"; - src: url("./fonts/bootstrap-icons.woff2?30af91bf14e37666a085fb8a161ff36d") format("woff2"), -url("./fonts/bootstrap-icons.woff?30af91bf14e37666a085fb8a161ff36d") format("woff"); -} - -.bi::before, -[class^="bi-"]::before, -[class*=" bi-"]::before { - display: inline-block; - font-family: bootstrap-icons !important; - font-style: normal; - font-weight: normal !important; - font-variant: normal; - text-transform: none; - line-height: 1; - vertical-align: -.125em; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.bi-123::before { content: "\f67f"; } -.bi-alarm-fill::before { content: "\f101"; } -.bi-alarm::before { content: "\f102"; } -.bi-align-bottom::before { content: "\f103"; } -.bi-align-center::before { content: "\f104"; } -.bi-align-end::before { content: "\f105"; } -.bi-align-middle::before { content: "\f106"; } -.bi-align-start::before { content: "\f107"; } -.bi-align-top::before { content: "\f108"; } -.bi-alt::before { content: "\f109"; } -.bi-app-indicator::before { content: "\f10a"; } -.bi-app::before { content: "\f10b"; } -.bi-archive-fill::before { content: "\f10c"; } -.bi-archive::before { content: "\f10d"; } -.bi-arrow-90deg-down::before { content: "\f10e"; } -.bi-arrow-90deg-left::before { content: "\f10f"; } -.bi-arrow-90deg-right::before { content: "\f110"; } -.bi-arrow-90deg-up::before { content: "\f111"; } -.bi-arrow-bar-down::before { content: "\f112"; } -.bi-arrow-bar-left::before { content: "\f113"; } -.bi-arrow-bar-right::before { content: "\f114"; } -.bi-arrow-bar-up::before { content: "\f115"; } -.bi-arrow-clockwise::before { content: "\f116"; } -.bi-arrow-counterclockwise::before { content: "\f117"; } -.bi-arrow-down-circle-fill::before { content: "\f118"; } -.bi-arrow-down-circle::before { content: "\f119"; } -.bi-arrow-down-left-circle-fill::before { content: "\f11a"; } -.bi-arrow-down-left-circle::before { content: "\f11b"; } -.bi-arrow-down-left-square-fill::before { content: "\f11c"; } -.bi-arrow-down-left-square::before { content: "\f11d"; } -.bi-arrow-down-left::before { content: "\f11e"; } -.bi-arrow-down-right-circle-fill::before { content: "\f11f"; } -.bi-arrow-down-right-circle::before { content: "\f120"; } -.bi-arrow-down-right-square-fill::before { content: "\f121"; } -.bi-arrow-down-right-square::before { content: "\f122"; } -.bi-arrow-down-right::before { content: "\f123"; } -.bi-arrow-down-short::before { content: "\f124"; } -.bi-arrow-down-square-fill::before { content: "\f125"; } -.bi-arrow-down-square::before { content: "\f126"; } -.bi-arrow-down-up::before { content: "\f127"; } -.bi-arrow-down::before { content: "\f128"; } -.bi-arrow-left-circle-fill::before { content: "\f129"; } -.bi-arrow-left-circle::before { content: "\f12a"; } -.bi-arrow-left-right::before { content: "\f12b"; } -.bi-arrow-left-short::before { content: "\f12c"; } -.bi-arrow-left-square-fill::before { content: "\f12d"; } -.bi-arrow-left-square::before { content: "\f12e"; } -.bi-arrow-left::before { content: "\f12f"; } -.bi-arrow-repeat::before { content: "\f130"; } -.bi-arrow-return-left::before { content: "\f131"; } -.bi-arrow-return-right::before { content: "\f132"; } -.bi-arrow-right-circle-fill::before { content: "\f133"; } -.bi-arrow-right-circle::before { content: "\f134"; } -.bi-arrow-right-short::before { content: "\f135"; } -.bi-arrow-right-square-fill::before { content: "\f136"; } -.bi-arrow-right-square::before { content: "\f137"; } -.bi-arrow-right::before { content: "\f138"; } -.bi-arrow-up-circle-fill::before { content: "\f139"; } -.bi-arrow-up-circle::before { content: "\f13a"; } -.bi-arrow-up-left-circle-fill::before { content: "\f13b"; } -.bi-arrow-up-left-circle::before { content: "\f13c"; } -.bi-arrow-up-left-square-fill::before { content: "\f13d"; } -.bi-arrow-up-left-square::before { content: "\f13e"; } -.bi-arrow-up-left::before { content: "\f13f"; } -.bi-arrow-up-right-circle-fill::before { content: "\f140"; } -.bi-arrow-up-right-circle::before { content: "\f141"; } -.bi-arrow-up-right-square-fill::before { content: "\f142"; } -.bi-arrow-up-right-square::before { content: "\f143"; } -.bi-arrow-up-right::before { content: "\f144"; } -.bi-arrow-up-short::before { content: "\f145"; } -.bi-arrow-up-square-fill::before { content: "\f146"; } -.bi-arrow-up-square::before { content: "\f147"; } -.bi-arrow-up::before { content: "\f148"; } -.bi-arrows-angle-contract::before { content: "\f149"; } -.bi-arrows-angle-expand::before { content: "\f14a"; } -.bi-arrows-collapse::before { content: "\f14b"; } -.bi-arrows-expand::before { content: "\f14c"; } -.bi-arrows-fullscreen::before { content: "\f14d"; } -.bi-arrows-move::before { content: "\f14e"; } -.bi-aspect-ratio-fill::before { content: "\f14f"; } -.bi-aspect-ratio::before { content: "\f150"; } -.bi-asterisk::before { content: "\f151"; } -.bi-at::before { content: "\f152"; } -.bi-award-fill::before { content: "\f153"; } -.bi-award::before { content: "\f154"; } -.bi-back::before { content: "\f155"; } -.bi-backspace-fill::before { content: "\f156"; } -.bi-backspace-reverse-fill::before { content: "\f157"; } -.bi-backspace-reverse::before { content: "\f158"; } -.bi-backspace::before { content: "\f159"; } -.bi-badge-3d-fill::before { content: "\f15a"; } -.bi-badge-3d::before { content: "\f15b"; } -.bi-badge-4k-fill::before { content: "\f15c"; } -.bi-badge-4k::before { content: "\f15d"; } -.bi-badge-8k-fill::before { content: "\f15e"; } -.bi-badge-8k::before { content: "\f15f"; } -.bi-badge-ad-fill::before { content: "\f160"; } -.bi-badge-ad::before { content: "\f161"; } -.bi-badge-ar-fill::before { content: "\f162"; } -.bi-badge-ar::before { content: "\f163"; } -.bi-badge-cc-fill::before { content: "\f164"; } -.bi-badge-cc::before { content: "\f165"; } -.bi-badge-hd-fill::before { content: "\f166"; } -.bi-badge-hd::before { content: "\f167"; } -.bi-badge-tm-fill::before { content: "\f168"; } -.bi-badge-tm::before { content: "\f169"; } -.bi-badge-vo-fill::before { content: "\f16a"; } -.bi-badge-vo::before { content: "\f16b"; } -.bi-badge-vr-fill::before { content: "\f16c"; } -.bi-badge-vr::before { content: "\f16d"; } -.bi-badge-wc-fill::before { content: "\f16e"; } -.bi-badge-wc::before { content: "\f16f"; } -.bi-bag-check-fill::before { content: "\f170"; } -.bi-bag-check::before { content: "\f171"; } -.bi-bag-dash-fill::before { content: "\f172"; } -.bi-bag-dash::before { content: "\f173"; } -.bi-bag-fill::before { content: "\f174"; } -.bi-bag-plus-fill::before { content: "\f175"; } -.bi-bag-plus::before { content: "\f176"; } -.bi-bag-x-fill::before { content: "\f177"; } -.bi-bag-x::before { content: "\f178"; } -.bi-bag::before { content: "\f179"; } -.bi-bar-chart-fill::before { content: "\f17a"; } -.bi-bar-chart-line-fill::before { content: "\f17b"; } -.bi-bar-chart-line::before { content: "\f17c"; } -.bi-bar-chart-steps::before { content: "\f17d"; } -.bi-bar-chart::before { content: "\f17e"; } -.bi-basket-fill::before { content: "\f17f"; } -.bi-basket::before { content: "\f180"; } -.bi-basket2-fill::before { content: "\f181"; } -.bi-basket2::before { content: "\f182"; } -.bi-basket3-fill::before { content: "\f183"; } -.bi-basket3::before { content: "\f184"; } -.bi-battery-charging::before { content: "\f185"; } -.bi-battery-full::before { content: "\f186"; } -.bi-battery-half::before { content: "\f187"; } -.bi-battery::before { content: "\f188"; } -.bi-bell-fill::before { content: "\f189"; } -.bi-bell::before { content: "\f18a"; } -.bi-bezier::before { content: "\f18b"; } -.bi-bezier2::before { content: "\f18c"; } -.bi-bicycle::before { content: "\f18d"; } -.bi-binoculars-fill::before { content: "\f18e"; } -.bi-binoculars::before { content: "\f18f"; } -.bi-blockquote-left::before { content: "\f190"; } -.bi-blockquote-right::before { content: "\f191"; } -.bi-book-fill::before { content: "\f192"; } -.bi-book-half::before { content: "\f193"; } -.bi-book::before { content: "\f194"; } -.bi-bookmark-check-fill::before { content: "\f195"; } -.bi-bookmark-check::before { content: "\f196"; } -.bi-bookmark-dash-fill::before { content: "\f197"; } -.bi-bookmark-dash::before { content: "\f198"; } -.bi-bookmark-fill::before { content: "\f199"; } -.bi-bookmark-heart-fill::before { content: "\f19a"; } -.bi-bookmark-heart::before { content: "\f19b"; } -.bi-bookmark-plus-fill::before { content: "\f19c"; } -.bi-bookmark-plus::before { content: "\f19d"; } -.bi-bookmark-star-fill::before { content: "\f19e"; } -.bi-bookmark-star::before { content: "\f19f"; } -.bi-bookmark-x-fill::before { content: "\f1a0"; } -.bi-bookmark-x::before { content: "\f1a1"; } -.bi-bookmark::before { content: "\f1a2"; } -.bi-bookmarks-fill::before { content: "\f1a3"; } -.bi-bookmarks::before { content: "\f1a4"; } -.bi-bookshelf::before { content: "\f1a5"; } -.bi-bootstrap-fill::before { content: "\f1a6"; } -.bi-bootstrap-reboot::before { content: "\f1a7"; } -.bi-bootstrap::before { content: "\f1a8"; } -.bi-border-all::before { content: "\f1a9"; } -.bi-border-bottom::before { content: "\f1aa"; } -.bi-border-center::before { content: "\f1ab"; } -.bi-border-inner::before { content: "\f1ac"; } -.bi-border-left::before { content: "\f1ad"; } -.bi-border-middle::before { content: "\f1ae"; } -.bi-border-outer::before { content: "\f1af"; } -.bi-border-right::before { content: "\f1b0"; } -.bi-border-style::before { content: "\f1b1"; } -.bi-border-top::before { content: "\f1b2"; } -.bi-border-width::before { content: "\f1b3"; } -.bi-border::before { content: "\f1b4"; } -.bi-bounding-box-circles::before { content: "\f1b5"; } -.bi-bounding-box::before { content: "\f1b6"; } -.bi-box-arrow-down-left::before { content: "\f1b7"; } -.bi-box-arrow-down-right::before { content: "\f1b8"; } -.bi-box-arrow-down::before { content: "\f1b9"; } -.bi-box-arrow-in-down-left::before { content: "\f1ba"; } -.bi-box-arrow-in-down-right::before { content: "\f1bb"; } -.bi-box-arrow-in-down::before { content: "\f1bc"; } -.bi-box-arrow-in-left::before { content: "\f1bd"; } -.bi-box-arrow-in-right::before { content: "\f1be"; } -.bi-box-arrow-in-up-left::before { content: "\f1bf"; } -.bi-box-arrow-in-up-right::before { content: "\f1c0"; } -.bi-box-arrow-in-up::before { content: "\f1c1"; } -.bi-box-arrow-left::before { content: "\f1c2"; } -.bi-box-arrow-right::before { content: "\f1c3"; } -.bi-box-arrow-up-left::before { content: "\f1c4"; } -.bi-box-arrow-up-right::before { content: "\f1c5"; } -.bi-box-arrow-up::before { content: "\f1c6"; } -.bi-box-seam::before { content: "\f1c7"; } -.bi-box::before { content: "\f1c8"; } -.bi-braces::before { content: "\f1c9"; } -.bi-bricks::before { content: "\f1ca"; } -.bi-briefcase-fill::before { content: "\f1cb"; } -.bi-briefcase::before { content: "\f1cc"; } -.bi-brightness-alt-high-fill::before { content: "\f1cd"; } -.bi-brightness-alt-high::before { content: "\f1ce"; } -.bi-brightness-alt-low-fill::before { content: "\f1cf"; } -.bi-brightness-alt-low::before { content: "\f1d0"; } -.bi-brightness-high-fill::before { content: "\f1d1"; } -.bi-brightness-high::before { content: "\f1d2"; } -.bi-brightness-low-fill::before { content: "\f1d3"; } -.bi-brightness-low::before { content: "\f1d4"; } -.bi-broadcast-pin::before { content: "\f1d5"; } -.bi-broadcast::before { content: "\f1d6"; } -.bi-brush-fill::before { content: "\f1d7"; } -.bi-brush::before { content: "\f1d8"; } -.bi-bucket-fill::before { content: "\f1d9"; } -.bi-bucket::before { content: "\f1da"; } -.bi-bug-fill::before { content: "\f1db"; } -.bi-bug::before { content: "\f1dc"; } -.bi-building::before { content: "\f1dd"; } -.bi-bullseye::before { content: "\f1de"; } -.bi-calculator-fill::before { content: "\f1df"; } -.bi-calculator::before { content: "\f1e0"; } -.bi-calendar-check-fill::before { content: "\f1e1"; } -.bi-calendar-check::before { content: "\f1e2"; } -.bi-calendar-date-fill::before { content: "\f1e3"; } -.bi-calendar-date::before { content: "\f1e4"; } -.bi-calendar-day-fill::before { content: "\f1e5"; } -.bi-calendar-day::before { content: "\f1e6"; } -.bi-calendar-event-fill::before { content: "\f1e7"; } -.bi-calendar-event::before { content: "\f1e8"; } -.bi-calendar-fill::before { content: "\f1e9"; } -.bi-calendar-minus-fill::before { content: "\f1ea"; } -.bi-calendar-minus::before { content: "\f1eb"; } -.bi-calendar-month-fill::before { content: "\f1ec"; } -.bi-calendar-month::before { content: "\f1ed"; } -.bi-calendar-plus-fill::before { content: "\f1ee"; } -.bi-calendar-plus::before { content: "\f1ef"; } -.bi-calendar-range-fill::before { content: "\f1f0"; } -.bi-calendar-range::before { content: "\f1f1"; } -.bi-calendar-week-fill::before { content: "\f1f2"; } -.bi-calendar-week::before { content: "\f1f3"; } -.bi-calendar-x-fill::before { content: "\f1f4"; } -.bi-calendar-x::before { content: "\f1f5"; } -.bi-calendar::before { content: "\f1f6"; } -.bi-calendar2-check-fill::before { content: "\f1f7"; } -.bi-calendar2-check::before { content: "\f1f8"; } -.bi-calendar2-date-fill::before { content: "\f1f9"; } -.bi-calendar2-date::before { content: "\f1fa"; } -.bi-calendar2-day-fill::before { content: "\f1fb"; } -.bi-calendar2-day::before { content: "\f1fc"; } -.bi-calendar2-event-fill::before { content: "\f1fd"; } -.bi-calendar2-event::before { content: "\f1fe"; } -.bi-calendar2-fill::before { content: "\f1ff"; } -.bi-calendar2-minus-fill::before { content: "\f200"; } -.bi-calendar2-minus::before { content: "\f201"; } -.bi-calendar2-month-fill::before { content: "\f202"; } -.bi-calendar2-month::before { content: "\f203"; } -.bi-calendar2-plus-fill::before { content: "\f204"; } -.bi-calendar2-plus::before { content: "\f205"; } -.bi-calendar2-range-fill::before { content: "\f206"; } -.bi-calendar2-range::before { content: "\f207"; } -.bi-calendar2-week-fill::before { content: "\f208"; } -.bi-calendar2-week::before { content: "\f209"; } -.bi-calendar2-x-fill::before { content: "\f20a"; } -.bi-calendar2-x::before { content: "\f20b"; } -.bi-calendar2::before { content: "\f20c"; } -.bi-calendar3-event-fill::before { content: "\f20d"; } -.bi-calendar3-event::before { content: "\f20e"; } -.bi-calendar3-fill::before { content: "\f20f"; } -.bi-calendar3-range-fill::before { content: "\f210"; } -.bi-calendar3-range::before { content: "\f211"; } -.bi-calendar3-week-fill::before { content: "\f212"; } -.bi-calendar3-week::before { content: "\f213"; } -.bi-calendar3::before { content: "\f214"; } -.bi-calendar4-event::before { content: "\f215"; } -.bi-calendar4-range::before { content: "\f216"; } -.bi-calendar4-week::before { content: "\f217"; } -.bi-calendar4::before { content: "\f218"; } -.bi-camera-fill::before { content: "\f219"; } -.bi-camera-reels-fill::before { content: "\f21a"; } -.bi-camera-reels::before { content: "\f21b"; } -.bi-camera-video-fill::before { content: "\f21c"; } -.bi-camera-video-off-fill::before { content: "\f21d"; } -.bi-camera-video-off::before { content: "\f21e"; } -.bi-camera-video::before { content: "\f21f"; } -.bi-camera::before { content: "\f220"; } -.bi-camera2::before { content: "\f221"; } -.bi-capslock-fill::before { content: "\f222"; } -.bi-capslock::before { content: "\f223"; } -.bi-card-checklist::before { content: "\f224"; } -.bi-card-heading::before { content: "\f225"; } -.bi-card-image::before { content: "\f226"; } -.bi-card-list::before { content: "\f227"; } -.bi-card-text::before { content: "\f228"; } -.bi-caret-down-fill::before { content: "\f229"; } -.bi-caret-down-square-fill::before { content: "\f22a"; } -.bi-caret-down-square::before { content: "\f22b"; } -.bi-caret-down::before { content: "\f22c"; } -.bi-caret-left-fill::before { content: "\f22d"; } -.bi-caret-left-square-fill::before { content: "\f22e"; } -.bi-caret-left-square::before { content: "\f22f"; } -.bi-caret-left::before { content: "\f230"; } -.bi-caret-right-fill::before { content: "\f231"; } -.bi-caret-right-square-fill::before { content: "\f232"; } -.bi-caret-right-square::before { content: "\f233"; } -.bi-caret-right::before { content: "\f234"; } -.bi-caret-up-fill::before { content: "\f235"; } -.bi-caret-up-square-fill::before { content: "\f236"; } -.bi-caret-up-square::before { content: "\f237"; } -.bi-caret-up::before { content: "\f238"; } -.bi-cart-check-fill::before { content: "\f239"; } -.bi-cart-check::before { content: "\f23a"; } -.bi-cart-dash-fill::before { content: "\f23b"; } -.bi-cart-dash::before { content: "\f23c"; } -.bi-cart-fill::before { content: "\f23d"; } -.bi-cart-plus-fill::before { content: "\f23e"; } -.bi-cart-plus::before { content: "\f23f"; } -.bi-cart-x-fill::before { content: "\f240"; } -.bi-cart-x::before { content: "\f241"; } -.bi-cart::before { content: "\f242"; } -.bi-cart2::before { content: "\f243"; } -.bi-cart3::before { content: "\f244"; } -.bi-cart4::before { content: "\f245"; } -.bi-cash-stack::before { content: "\f246"; } -.bi-cash::before { content: "\f247"; } -.bi-cast::before { content: "\f248"; } -.bi-chat-dots-fill::before { content: "\f249"; } -.bi-chat-dots::before { content: "\f24a"; } -.bi-chat-fill::before { content: "\f24b"; } -.bi-chat-left-dots-fill::before { content: "\f24c"; } -.bi-chat-left-dots::before { content: "\f24d"; } -.bi-chat-left-fill::before { content: "\f24e"; } -.bi-chat-left-quote-fill::before { content: "\f24f"; } -.bi-chat-left-quote::before { content: "\f250"; } -.bi-chat-left-text-fill::before { content: "\f251"; } -.bi-chat-left-text::before { content: "\f252"; } -.bi-chat-left::before { content: "\f253"; } -.bi-chat-quote-fill::before { content: "\f254"; } -.bi-chat-quote::before { content: "\f255"; } -.bi-chat-right-dots-fill::before { content: "\f256"; } -.bi-chat-right-dots::before { content: "\f257"; } -.bi-chat-right-fill::before { content: "\f258"; } -.bi-chat-right-quote-fill::before { content: "\f259"; } -.bi-chat-right-quote::before { content: "\f25a"; } -.bi-chat-right-text-fill::before { content: "\f25b"; } -.bi-chat-right-text::before { content: "\f25c"; } -.bi-chat-right::before { content: "\f25d"; } -.bi-chat-square-dots-fill::before { content: "\f25e"; } -.bi-chat-square-dots::before { content: "\f25f"; } -.bi-chat-square-fill::before { content: "\f260"; } -.bi-chat-square-quote-fill::before { content: "\f261"; } -.bi-chat-square-quote::before { content: "\f262"; } -.bi-chat-square-text-fill::before { content: "\f263"; } -.bi-chat-square-text::before { content: "\f264"; } -.bi-chat-square::before { content: "\f265"; } -.bi-chat-text-fill::before { content: "\f266"; } -.bi-chat-text::before { content: "\f267"; } -.bi-chat::before { content: "\f268"; } -.bi-check-all::before { content: "\f269"; } -.bi-check-circle-fill::before { content: "\f26a"; } -.bi-check-circle::before { content: "\f26b"; } -.bi-check-square-fill::before { content: "\f26c"; } -.bi-check-square::before { content: "\f26d"; } -.bi-check::before { content: "\f26e"; } -.bi-check2-all::before { content: "\f26f"; } -.bi-check2-circle::before { content: "\f270"; } -.bi-check2-square::before { content: "\f271"; } -.bi-check2::before { content: "\f272"; } -.bi-chevron-bar-contract::before { content: "\f273"; } -.bi-chevron-bar-down::before { content: "\f274"; } -.bi-chevron-bar-expand::before { content: "\f275"; } -.bi-chevron-bar-left::before { content: "\f276"; } -.bi-chevron-bar-right::before { content: "\f277"; } -.bi-chevron-bar-up::before { content: "\f278"; } -.bi-chevron-compact-down::before { content: "\f279"; } -.bi-chevron-compact-left::before { content: "\f27a"; } -.bi-chevron-compact-right::before { content: "\f27b"; } -.bi-chevron-compact-up::before { content: "\f27c"; } -.bi-chevron-contract::before { content: "\f27d"; } -.bi-chevron-double-down::before { content: "\f27e"; } -.bi-chevron-double-left::before { content: "\f27f"; } -.bi-chevron-double-right::before { content: "\f280"; } -.bi-chevron-double-up::before { content: "\f281"; } -.bi-chevron-down::before { content: "\f282"; } -.bi-chevron-expand::before { content: "\f283"; } -.bi-chevron-left::before { content: "\f284"; } -.bi-chevron-right::before { content: "\f285"; } -.bi-chevron-up::before { content: "\f286"; } -.bi-circle-fill::before { content: "\f287"; } -.bi-circle-half::before { content: "\f288"; } -.bi-circle-square::before { content: "\f289"; } -.bi-circle::before { content: "\f28a"; } -.bi-clipboard-check::before { content: "\f28b"; } -.bi-clipboard-data::before { content: "\f28c"; } -.bi-clipboard-minus::before { content: "\f28d"; } -.bi-clipboard-plus::before { content: "\f28e"; } -.bi-clipboard-x::before { content: "\f28f"; } -.bi-clipboard::before { content: "\f290"; } -.bi-clock-fill::before { content: "\f291"; } -.bi-clock-history::before { content: "\f292"; } -.bi-clock::before { content: "\f293"; } -.bi-cloud-arrow-down-fill::before { content: "\f294"; } -.bi-cloud-arrow-down::before { content: "\f295"; } -.bi-cloud-arrow-up-fill::before { content: "\f296"; } -.bi-cloud-arrow-up::before { content: "\f297"; } -.bi-cloud-check-fill::before { content: "\f298"; } -.bi-cloud-check::before { content: "\f299"; } -.bi-cloud-download-fill::before { content: "\f29a"; } -.bi-cloud-download::before { content: "\f29b"; } -.bi-cloud-drizzle-fill::before { content: "\f29c"; } -.bi-cloud-drizzle::before { content: "\f29d"; } -.bi-cloud-fill::before { content: "\f29e"; } -.bi-cloud-fog-fill::before { content: "\f29f"; } -.bi-cloud-fog::before { content: "\f2a0"; } -.bi-cloud-fog2-fill::before { content: "\f2a1"; } -.bi-cloud-fog2::before { content: "\f2a2"; } -.bi-cloud-hail-fill::before { content: "\f2a3"; } -.bi-cloud-hail::before { content: "\f2a4"; } -.bi-cloud-haze-1::before { content: "\f2a5"; } -.bi-cloud-haze-fill::before { content: "\f2a6"; } -.bi-cloud-haze::before { content: "\f2a7"; } -.bi-cloud-haze2-fill::before { content: "\f2a8"; } -.bi-cloud-lightning-fill::before { content: "\f2a9"; } -.bi-cloud-lightning-rain-fill::before { content: "\f2aa"; } -.bi-cloud-lightning-rain::before { content: "\f2ab"; } -.bi-cloud-lightning::before { content: "\f2ac"; } -.bi-cloud-minus-fill::before { content: "\f2ad"; } -.bi-cloud-minus::before { content: "\f2ae"; } -.bi-cloud-moon-fill::before { content: "\f2af"; } -.bi-cloud-moon::before { content: "\f2b0"; } -.bi-cloud-plus-fill::before { content: "\f2b1"; } -.bi-cloud-plus::before { content: "\f2b2"; } -.bi-cloud-rain-fill::before { content: "\f2b3"; } -.bi-cloud-rain-heavy-fill::before { content: "\f2b4"; } -.bi-cloud-rain-heavy::before { content: "\f2b5"; } -.bi-cloud-rain::before { content: "\f2b6"; } -.bi-cloud-slash-fill::before { content: "\f2b7"; } -.bi-cloud-slash::before { content: "\f2b8"; } -.bi-cloud-sleet-fill::before { content: "\f2b9"; } -.bi-cloud-sleet::before { content: "\f2ba"; } -.bi-cloud-snow-fill::before { content: "\f2bb"; } -.bi-cloud-snow::before { content: "\f2bc"; } -.bi-cloud-sun-fill::before { content: "\f2bd"; } -.bi-cloud-sun::before { content: "\f2be"; } -.bi-cloud-upload-fill::before { content: "\f2bf"; } -.bi-cloud-upload::before { content: "\f2c0"; } -.bi-cloud::before { content: "\f2c1"; } -.bi-clouds-fill::before { content: "\f2c2"; } -.bi-clouds::before { content: "\f2c3"; } -.bi-cloudy-fill::before { content: "\f2c4"; } -.bi-cloudy::before { content: "\f2c5"; } -.bi-code-slash::before { content: "\f2c6"; } -.bi-code-square::before { content: "\f2c7"; } -.bi-code::before { content: "\f2c8"; } -.bi-collection-fill::before { content: "\f2c9"; } -.bi-collection-play-fill::before { content: "\f2ca"; } -.bi-collection-play::before { content: "\f2cb"; } -.bi-collection::before { content: "\f2cc"; } -.bi-columns-gap::before { content: "\f2cd"; } -.bi-columns::before { content: "\f2ce"; } -.bi-command::before { content: "\f2cf"; } -.bi-compass-fill::before { content: "\f2d0"; } -.bi-compass::before { content: "\f2d1"; } -.bi-cone-striped::before { content: "\f2d2"; } -.bi-cone::before { content: "\f2d3"; } -.bi-controller::before { content: "\f2d4"; } -.bi-cpu-fill::before { content: "\f2d5"; } -.bi-cpu::before { content: "\f2d6"; } -.bi-credit-card-2-back-fill::before { content: "\f2d7"; } -.bi-credit-card-2-back::before { content: "\f2d8"; } -.bi-credit-card-2-front-fill::before { content: "\f2d9"; } -.bi-credit-card-2-front::before { content: "\f2da"; } -.bi-credit-card-fill::before { content: "\f2db"; } -.bi-credit-card::before { content: "\f2dc"; } -.bi-crop::before { content: "\f2dd"; } -.bi-cup-fill::before { content: "\f2de"; } -.bi-cup-straw::before { content: "\f2df"; } -.bi-cup::before { content: "\f2e0"; } -.bi-cursor-fill::before { content: "\f2e1"; } -.bi-cursor-text::before { content: "\f2e2"; } -.bi-cursor::before { content: "\f2e3"; } -.bi-dash-circle-dotted::before { content: "\f2e4"; } -.bi-dash-circle-fill::before { content: "\f2e5"; } -.bi-dash-circle::before { content: "\f2e6"; } -.bi-dash-square-dotted::before { content: "\f2e7"; } -.bi-dash-square-fill::before { content: "\f2e8"; } -.bi-dash-square::before { content: "\f2e9"; } -.bi-dash::before { content: "\f2ea"; } -.bi-diagram-2-fill::before { content: "\f2eb"; } -.bi-diagram-2::before { content: "\f2ec"; } -.bi-diagram-3-fill::before { content: "\f2ed"; } -.bi-diagram-3::before { content: "\f2ee"; } -.bi-diamond-fill::before { content: "\f2ef"; } -.bi-diamond-half::before { content: "\f2f0"; } -.bi-diamond::before { content: "\f2f1"; } -.bi-dice-1-fill::before { content: "\f2f2"; } -.bi-dice-1::before { content: "\f2f3"; } -.bi-dice-2-fill::before { content: "\f2f4"; } -.bi-dice-2::before { content: "\f2f5"; } -.bi-dice-3-fill::before { content: "\f2f6"; } -.bi-dice-3::before { content: "\f2f7"; } -.bi-dice-4-fill::before { content: "\f2f8"; } -.bi-dice-4::before { content: "\f2f9"; } -.bi-dice-5-fill::before { content: "\f2fa"; } -.bi-dice-5::before { content: "\f2fb"; } -.bi-dice-6-fill::before { content: "\f2fc"; } -.bi-dice-6::before { content: "\f2fd"; } -.bi-disc-fill::before { content: "\f2fe"; } -.bi-disc::before { content: "\f2ff"; } -.bi-discord::before { content: "\f300"; } -.bi-display-fill::before { content: "\f301"; } -.bi-display::before { content: "\f302"; } -.bi-distribute-horizontal::before { content: "\f303"; } -.bi-distribute-vertical::before { content: "\f304"; } -.bi-door-closed-fill::before { content: "\f305"; } -.bi-door-closed::before { content: "\f306"; } -.bi-door-open-fill::before { content: "\f307"; } -.bi-door-open::before { content: "\f308"; } -.bi-dot::before { content: "\f309"; } -.bi-download::before { content: "\f30a"; } -.bi-droplet-fill::before { content: "\f30b"; } -.bi-droplet-half::before { content: "\f30c"; } -.bi-droplet::before { content: "\f30d"; } -.bi-earbuds::before { content: "\f30e"; } -.bi-easel-fill::before { content: "\f30f"; } -.bi-easel::before { content: "\f310"; } -.bi-egg-fill::before { content: "\f311"; } -.bi-egg-fried::before { content: "\f312"; } -.bi-egg::before { content: "\f313"; } -.bi-eject-fill::before { content: "\f314"; } -.bi-eject::before { content: "\f315"; } -.bi-emoji-angry-fill::before { content: "\f316"; } -.bi-emoji-angry::before { content: "\f317"; } -.bi-emoji-dizzy-fill::before { content: "\f318"; } -.bi-emoji-dizzy::before { content: "\f319"; } -.bi-emoji-expressionless-fill::before { content: "\f31a"; } -.bi-emoji-expressionless::before { content: "\f31b"; } -.bi-emoji-frown-fill::before { content: "\f31c"; } -.bi-emoji-frown::before { content: "\f31d"; } -.bi-emoji-heart-eyes-fill::before { content: "\f31e"; } -.bi-emoji-heart-eyes::before { content: "\f31f"; } -.bi-emoji-laughing-fill::before { content: "\f320"; } -.bi-emoji-laughing::before { content: "\f321"; } -.bi-emoji-neutral-fill::before { content: "\f322"; } -.bi-emoji-neutral::before { content: "\f323"; } -.bi-emoji-smile-fill::before { content: "\f324"; } -.bi-emoji-smile-upside-down-fill::before { content: "\f325"; } -.bi-emoji-smile-upside-down::before { content: "\f326"; } -.bi-emoji-smile::before { content: "\f327"; } -.bi-emoji-sunglasses-fill::before { content: "\f328"; } -.bi-emoji-sunglasses::before { content: "\f329"; } -.bi-emoji-wink-fill::before { content: "\f32a"; } -.bi-emoji-wink::before { content: "\f32b"; } -.bi-envelope-fill::before { content: "\f32c"; } -.bi-envelope-open-fill::before { content: "\f32d"; } -.bi-envelope-open::before { content: "\f32e"; } -.bi-envelope::before { content: "\f32f"; } -.bi-eraser-fill::before { content: "\f330"; } -.bi-eraser::before { content: "\f331"; } -.bi-exclamation-circle-fill::before { content: "\f332"; } -.bi-exclamation-circle::before { content: "\f333"; } -.bi-exclamation-diamond-fill::before { content: "\f334"; } -.bi-exclamation-diamond::before { content: "\f335"; } -.bi-exclamation-octagon-fill::before { content: "\f336"; } -.bi-exclamation-octagon::before { content: "\f337"; } -.bi-exclamation-square-fill::before { content: "\f338"; } -.bi-exclamation-square::before { content: "\f339"; } -.bi-exclamation-triangle-fill::before { content: "\f33a"; } -.bi-exclamation-triangle::before { content: "\f33b"; } -.bi-exclamation::before { content: "\f33c"; } -.bi-exclude::before { content: "\f33d"; } -.bi-eye-fill::before { content: "\f33e"; } -.bi-eye-slash-fill::before { content: "\f33f"; } -.bi-eye-slash::before { content: "\f340"; } -.bi-eye::before { content: "\f341"; } -.bi-eyedropper::before { content: "\f342"; } -.bi-eyeglasses::before { content: "\f343"; } -.bi-facebook::before { content: "\f344"; } -.bi-file-arrow-down-fill::before { content: "\f345"; } -.bi-file-arrow-down::before { content: "\f346"; } -.bi-file-arrow-up-fill::before { content: "\f347"; } -.bi-file-arrow-up::before { content: "\f348"; } -.bi-file-bar-graph-fill::before { content: "\f349"; } -.bi-file-bar-graph::before { content: "\f34a"; } -.bi-file-binary-fill::before { content: "\f34b"; } -.bi-file-binary::before { content: "\f34c"; } -.bi-file-break-fill::before { content: "\f34d"; } -.bi-file-break::before { content: "\f34e"; } -.bi-file-check-fill::before { content: "\f34f"; } -.bi-file-check::before { content: "\f350"; } -.bi-file-code-fill::before { content: "\f351"; } -.bi-file-code::before { content: "\f352"; } -.bi-file-diff-fill::before { content: "\f353"; } -.bi-file-diff::before { content: "\f354"; } -.bi-file-earmark-arrow-down-fill::before { content: "\f355"; } -.bi-file-earmark-arrow-down::before { content: "\f356"; } -.bi-file-earmark-arrow-up-fill::before { content: "\f357"; } -.bi-file-earmark-arrow-up::before { content: "\f358"; } -.bi-file-earmark-bar-graph-fill::before { content: "\f359"; } -.bi-file-earmark-bar-graph::before { content: "\f35a"; } -.bi-file-earmark-binary-fill::before { content: "\f35b"; } -.bi-file-earmark-binary::before { content: "\f35c"; } -.bi-file-earmark-break-fill::before { content: "\f35d"; } -.bi-file-earmark-break::before { content: "\f35e"; } -.bi-file-earmark-check-fill::before { content: "\f35f"; } -.bi-file-earmark-check::before { content: "\f360"; } -.bi-file-earmark-code-fill::before { content: "\f361"; } -.bi-file-earmark-code::before { content: "\f362"; } -.bi-file-earmark-diff-fill::before { content: "\f363"; } -.bi-file-earmark-diff::before { content: "\f364"; } -.bi-file-earmark-easel-fill::before { content: "\f365"; } -.bi-file-earmark-easel::before { content: "\f366"; } -.bi-file-earmark-excel-fill::before { content: "\f367"; } -.bi-file-earmark-excel::before { content: "\f368"; } -.bi-file-earmark-fill::before { content: "\f369"; } -.bi-file-earmark-font-fill::before { content: "\f36a"; } -.bi-file-earmark-font::before { content: "\f36b"; } -.bi-file-earmark-image-fill::before { content: "\f36c"; } -.bi-file-earmark-image::before { content: "\f36d"; } -.bi-file-earmark-lock-fill::before { content: "\f36e"; } -.bi-file-earmark-lock::before { content: "\f36f"; } -.bi-file-earmark-lock2-fill::before { content: "\f370"; } -.bi-file-earmark-lock2::before { content: "\f371"; } -.bi-file-earmark-medical-fill::before { content: "\f372"; } -.bi-file-earmark-medical::before { content: "\f373"; } -.bi-file-earmark-minus-fill::before { content: "\f374"; } -.bi-file-earmark-minus::before { content: "\f375"; } -.bi-file-earmark-music-fill::before { content: "\f376"; } -.bi-file-earmark-music::before { content: "\f377"; } -.bi-file-earmark-person-fill::before { content: "\f378"; } -.bi-file-earmark-person::before { content: "\f379"; } -.bi-file-earmark-play-fill::before { content: "\f37a"; } -.bi-file-earmark-play::before { content: "\f37b"; } -.bi-file-earmark-plus-fill::before { content: "\f37c"; } -.bi-file-earmark-plus::before { content: "\f37d"; } -.bi-file-earmark-post-fill::before { content: "\f37e"; } -.bi-file-earmark-post::before { content: "\f37f"; } -.bi-file-earmark-ppt-fill::before { content: "\f380"; } -.bi-file-earmark-ppt::before { content: "\f381"; } -.bi-file-earmark-richtext-fill::before { content: "\f382"; } -.bi-file-earmark-richtext::before { content: "\f383"; } -.bi-file-earmark-ruled-fill::before { content: "\f384"; } -.bi-file-earmark-ruled::before { content: "\f385"; } -.bi-file-earmark-slides-fill::before { content: "\f386"; } -.bi-file-earmark-slides::before { content: "\f387"; } -.bi-file-earmark-spreadsheet-fill::before { content: "\f388"; } -.bi-file-earmark-spreadsheet::before { content: "\f389"; } -.bi-file-earmark-text-fill::before { content: "\f38a"; } -.bi-file-earmark-text::before { content: "\f38b"; } -.bi-file-earmark-word-fill::before { content: "\f38c"; } -.bi-file-earmark-word::before { content: "\f38d"; } -.bi-file-earmark-x-fill::before { content: "\f38e"; } -.bi-file-earmark-x::before { content: "\f38f"; } -.bi-file-earmark-zip-fill::before { content: "\f390"; } -.bi-file-earmark-zip::before { content: "\f391"; } -.bi-file-earmark::before { content: "\f392"; } -.bi-file-easel-fill::before { content: "\f393"; } -.bi-file-easel::before { content: "\f394"; } -.bi-file-excel-fill::before { content: "\f395"; } -.bi-file-excel::before { content: "\f396"; } -.bi-file-fill::before { content: "\f397"; } -.bi-file-font-fill::before { content: "\f398"; } -.bi-file-font::before { content: "\f399"; } -.bi-file-image-fill::before { content: "\f39a"; } -.bi-file-image::before { content: "\f39b"; } -.bi-file-lock-fill::before { content: "\f39c"; } -.bi-file-lock::before { content: "\f39d"; } -.bi-file-lock2-fill::before { content: "\f39e"; } -.bi-file-lock2::before { content: "\f39f"; } -.bi-file-medical-fill::before { content: "\f3a0"; } -.bi-file-medical::before { content: "\f3a1"; } -.bi-file-minus-fill::before { content: "\f3a2"; } -.bi-file-minus::before { content: "\f3a3"; } -.bi-file-music-fill::before { content: "\f3a4"; } -.bi-file-music::before { content: "\f3a5"; } -.bi-file-person-fill::before { content: "\f3a6"; } -.bi-file-person::before { content: "\f3a7"; } -.bi-file-play-fill::before { content: "\f3a8"; } -.bi-file-play::before { content: "\f3a9"; } -.bi-file-plus-fill::before { content: "\f3aa"; } -.bi-file-plus::before { content: "\f3ab"; } -.bi-file-post-fill::before { content: "\f3ac"; } -.bi-file-post::before { content: "\f3ad"; } -.bi-file-ppt-fill::before { content: "\f3ae"; } -.bi-file-ppt::before { content: "\f3af"; } -.bi-file-richtext-fill::before { content: "\f3b0"; } -.bi-file-richtext::before { content: "\f3b1"; } -.bi-file-ruled-fill::before { content: "\f3b2"; } -.bi-file-ruled::before { content: "\f3b3"; } -.bi-file-slides-fill::before { content: "\f3b4"; } -.bi-file-slides::before { content: "\f3b5"; } -.bi-file-spreadsheet-fill::before { content: "\f3b6"; } -.bi-file-spreadsheet::before { content: "\f3b7"; } -.bi-file-text-fill::before { content: "\f3b8"; } -.bi-file-text::before { content: "\f3b9"; } -.bi-file-word-fill::before { content: "\f3ba"; } -.bi-file-word::before { content: "\f3bb"; } -.bi-file-x-fill::before { content: "\f3bc"; } -.bi-file-x::before { content: "\f3bd"; } -.bi-file-zip-fill::before { content: "\f3be"; } -.bi-file-zip::before { content: "\f3bf"; } -.bi-file::before { content: "\f3c0"; } -.bi-files-alt::before { content: "\f3c1"; } -.bi-files::before { content: "\f3c2"; } -.bi-film::before { content: "\f3c3"; } -.bi-filter-circle-fill::before { content: "\f3c4"; } -.bi-filter-circle::before { content: "\f3c5"; } -.bi-filter-left::before { content: "\f3c6"; } -.bi-filter-right::before { content: "\f3c7"; } -.bi-filter-square-fill::before { content: "\f3c8"; } -.bi-filter-square::before { content: "\f3c9"; } -.bi-filter::before { content: "\f3ca"; } -.bi-flag-fill::before { content: "\f3cb"; } -.bi-flag::before { content: "\f3cc"; } -.bi-flower1::before { content: "\f3cd"; } -.bi-flower2::before { content: "\f3ce"; } -.bi-flower3::before { content: "\f3cf"; } -.bi-folder-check::before { content: "\f3d0"; } -.bi-folder-fill::before { content: "\f3d1"; } -.bi-folder-minus::before { content: "\f3d2"; } -.bi-folder-plus::before { content: "\f3d3"; } -.bi-folder-symlink-fill::before { content: "\f3d4"; } -.bi-folder-symlink::before { content: "\f3d5"; } -.bi-folder-x::before { content: "\f3d6"; } -.bi-folder::before { content: "\f3d7"; } -.bi-folder2-open::before { content: "\f3d8"; } -.bi-folder2::before { content: "\f3d9"; } -.bi-fonts::before { content: "\f3da"; } -.bi-forward-fill::before { content: "\f3db"; } -.bi-forward::before { content: "\f3dc"; } -.bi-front::before { content: "\f3dd"; } -.bi-fullscreen-exit::before { content: "\f3de"; } -.bi-fullscreen::before { content: "\f3df"; } -.bi-funnel-fill::before { content: "\f3e0"; } -.bi-funnel::before { content: "\f3e1"; } -.bi-gear-fill::before { content: "\f3e2"; } -.bi-gear-wide-connected::before { content: "\f3e3"; } -.bi-gear-wide::before { content: "\f3e4"; } -.bi-gear::before { content: "\f3e5"; } -.bi-gem::before { content: "\f3e6"; } -.bi-geo-alt-fill::before { content: "\f3e7"; } -.bi-geo-alt::before { content: "\f3e8"; } -.bi-geo-fill::before { content: "\f3e9"; } -.bi-geo::before { content: "\f3ea"; } -.bi-gift-fill::before { content: "\f3eb"; } -.bi-gift::before { content: "\f3ec"; } -.bi-github::before { content: "\f3ed"; } -.bi-globe::before { content: "\f3ee"; } -.bi-globe2::before { content: "\f3ef"; } -.bi-google::before { content: "\f3f0"; } -.bi-graph-down::before { content: "\f3f1"; } -.bi-graph-up::before { content: "\f3f2"; } -.bi-grid-1x2-fill::before { content: "\f3f3"; } -.bi-grid-1x2::before { content: "\f3f4"; } -.bi-grid-3x2-gap-fill::before { content: "\f3f5"; } -.bi-grid-3x2-gap::before { content: "\f3f6"; } -.bi-grid-3x2::before { content: "\f3f7"; } -.bi-grid-3x3-gap-fill::before { content: "\f3f8"; } -.bi-grid-3x3-gap::before { content: "\f3f9"; } -.bi-grid-3x3::before { content: "\f3fa"; } -.bi-grid-fill::before { content: "\f3fb"; } -.bi-grid::before { content: "\f3fc"; } -.bi-grip-horizontal::before { content: "\f3fd"; } -.bi-grip-vertical::before { content: "\f3fe"; } -.bi-hammer::before { content: "\f3ff"; } -.bi-hand-index-fill::before { content: "\f400"; } -.bi-hand-index-thumb-fill::before { content: "\f401"; } -.bi-hand-index-thumb::before { content: "\f402"; } -.bi-hand-index::before { content: "\f403"; } -.bi-hand-thumbs-down-fill::before { content: "\f404"; } -.bi-hand-thumbs-down::before { content: "\f405"; } -.bi-hand-thumbs-up-fill::before { content: "\f406"; } -.bi-hand-thumbs-up::before { content: "\f407"; } -.bi-handbag-fill::before { content: "\f408"; } -.bi-handbag::before { content: "\f409"; } -.bi-hash::before { content: "\f40a"; } -.bi-hdd-fill::before { content: "\f40b"; } -.bi-hdd-network-fill::before { content: "\f40c"; } -.bi-hdd-network::before { content: "\f40d"; } -.bi-hdd-rack-fill::before { content: "\f40e"; } -.bi-hdd-rack::before { content: "\f40f"; } -.bi-hdd-stack-fill::before { content: "\f410"; } -.bi-hdd-stack::before { content: "\f411"; } -.bi-hdd::before { content: "\f412"; } -.bi-headphones::before { content: "\f413"; } -.bi-headset::before { content: "\f414"; } -.bi-heart-fill::before { content: "\f415"; } -.bi-heart-half::before { content: "\f416"; } -.bi-heart::before { content: "\f417"; } -.bi-heptagon-fill::before { content: "\f418"; } -.bi-heptagon-half::before { content: "\f419"; } -.bi-heptagon::before { content: "\f41a"; } -.bi-hexagon-fill::before { content: "\f41b"; } -.bi-hexagon-half::before { content: "\f41c"; } -.bi-hexagon::before { content: "\f41d"; } -.bi-hourglass-bottom::before { content: "\f41e"; } -.bi-hourglass-split::before { content: "\f41f"; } -.bi-hourglass-top::before { content: "\f420"; } -.bi-hourglass::before { content: "\f421"; } -.bi-house-door-fill::before { content: "\f422"; } -.bi-house-door::before { content: "\f423"; } -.bi-house-fill::before { content: "\f424"; } -.bi-house::before { content: "\f425"; } -.bi-hr::before { content: "\f426"; } -.bi-hurricane::before { content: "\f427"; } -.bi-image-alt::before { content: "\f428"; } -.bi-image-fill::before { content: "\f429"; } -.bi-image::before { content: "\f42a"; } -.bi-images::before { content: "\f42b"; } -.bi-inbox-fill::before { content: "\f42c"; } -.bi-inbox::before { content: "\f42d"; } -.bi-inboxes-fill::before { content: "\f42e"; } -.bi-inboxes::before { content: "\f42f"; } -.bi-info-circle-fill::before { content: "\f430"; } -.bi-info-circle::before { content: "\f431"; } -.bi-info-square-fill::before { content: "\f432"; } -.bi-info-square::before { content: "\f433"; } -.bi-info::before { content: "\f434"; } -.bi-input-cursor-text::before { content: "\f435"; } -.bi-input-cursor::before { content: "\f436"; } -.bi-instagram::before { content: "\f437"; } -.bi-intersect::before { content: "\f438"; } -.bi-journal-album::before { content: "\f439"; } -.bi-journal-arrow-down::before { content: "\f43a"; } -.bi-journal-arrow-up::before { content: "\f43b"; } -.bi-journal-bookmark-fill::before { content: "\f43c"; } -.bi-journal-bookmark::before { content: "\f43d"; } -.bi-journal-check::before { content: "\f43e"; } -.bi-journal-code::before { content: "\f43f"; } -.bi-journal-medical::before { content: "\f440"; } -.bi-journal-minus::before { content: "\f441"; } -.bi-journal-plus::before { content: "\f442"; } -.bi-journal-richtext::before { content: "\f443"; } -.bi-journal-text::before { content: "\f444"; } -.bi-journal-x::before { content: "\f445"; } -.bi-journal::before { content: "\f446"; } -.bi-journals::before { content: "\f447"; } -.bi-joystick::before { content: "\f448"; } -.bi-justify-left::before { content: "\f449"; } -.bi-justify-right::before { content: "\f44a"; } -.bi-justify::before { content: "\f44b"; } -.bi-kanban-fill::before { content: "\f44c"; } -.bi-kanban::before { content: "\f44d"; } -.bi-key-fill::before { content: "\f44e"; } -.bi-key::before { content: "\f44f"; } -.bi-keyboard-fill::before { content: "\f450"; } -.bi-keyboard::before { content: "\f451"; } -.bi-ladder::before { content: "\f452"; } -.bi-lamp-fill::before { content: "\f453"; } -.bi-lamp::before { content: "\f454"; } -.bi-laptop-fill::before { content: "\f455"; } -.bi-laptop::before { content: "\f456"; } -.bi-layer-backward::before { content: "\f457"; } -.bi-layer-forward::before { content: "\f458"; } -.bi-layers-fill::before { content: "\f459"; } -.bi-layers-half::before { content: "\f45a"; } -.bi-layers::before { content: "\f45b"; } -.bi-layout-sidebar-inset-reverse::before { content: "\f45c"; } -.bi-layout-sidebar-inset::before { content: "\f45d"; } -.bi-layout-sidebar-reverse::before { content: "\f45e"; } -.bi-layout-sidebar::before { content: "\f45f"; } -.bi-layout-split::before { content: "\f460"; } -.bi-layout-text-sidebar-reverse::before { content: "\f461"; } -.bi-layout-text-sidebar::before { content: "\f462"; } -.bi-layout-text-window-reverse::before { content: "\f463"; } -.bi-layout-text-window::before { content: "\f464"; } -.bi-layout-three-columns::before { content: "\f465"; } -.bi-layout-wtf::before { content: "\f466"; } -.bi-life-preserver::before { content: "\f467"; } -.bi-lightbulb-fill::before { content: "\f468"; } -.bi-lightbulb-off-fill::before { content: "\f469"; } -.bi-lightbulb-off::before { content: "\f46a"; } -.bi-lightbulb::before { content: "\f46b"; } -.bi-lightning-charge-fill::before { content: "\f46c"; } -.bi-lightning-charge::before { content: "\f46d"; } -.bi-lightning-fill::before { content: "\f46e"; } -.bi-lightning::before { content: "\f46f"; } -.bi-link-45deg::before { content: "\f470"; } -.bi-link::before { content: "\f471"; } -.bi-linkedin::before { content: "\f472"; } -.bi-list-check::before { content: "\f473"; } -.bi-list-nested::before { content: "\f474"; } -.bi-list-ol::before { content: "\f475"; } -.bi-list-stars::before { content: "\f476"; } -.bi-list-task::before { content: "\f477"; } -.bi-list-ul::before { content: "\f478"; } -.bi-list::before { content: "\f479"; } -.bi-lock-fill::before { content: "\f47a"; } -.bi-lock::before { content: "\f47b"; } -.bi-mailbox::before { content: "\f47c"; } -.bi-mailbox2::before { content: "\f47d"; } -.bi-map-fill::before { content: "\f47e"; } -.bi-map::before { content: "\f47f"; } -.bi-markdown-fill::before { content: "\f480"; } -.bi-markdown::before { content: "\f481"; } -.bi-mask::before { content: "\f482"; } -.bi-megaphone-fill::before { content: "\f483"; } -.bi-megaphone::before { content: "\f484"; } -.bi-menu-app-fill::before { content: "\f485"; } -.bi-menu-app::before { content: "\f486"; } -.bi-menu-button-fill::before { content: "\f487"; } -.bi-menu-button-wide-fill::before { content: "\f488"; } -.bi-menu-button-wide::before { content: "\f489"; } -.bi-menu-button::before { content: "\f48a"; } -.bi-menu-down::before { content: "\f48b"; } -.bi-menu-up::before { content: "\f48c"; } -.bi-mic-fill::before { content: "\f48d"; } -.bi-mic-mute-fill::before { content: "\f48e"; } -.bi-mic-mute::before { content: "\f48f"; } -.bi-mic::before { content: "\f490"; } -.bi-minecart-loaded::before { content: "\f491"; } -.bi-minecart::before { content: "\f492"; } -.bi-moisture::before { content: "\f493"; } -.bi-moon-fill::before { content: "\f494"; } -.bi-moon-stars-fill::before { content: "\f495"; } -.bi-moon-stars::before { content: "\f496"; } -.bi-moon::before { content: "\f497"; } -.bi-mouse-fill::before { content: "\f498"; } -.bi-mouse::before { content: "\f499"; } -.bi-mouse2-fill::before { content: "\f49a"; } -.bi-mouse2::before { content: "\f49b"; } -.bi-mouse3-fill::before { content: "\f49c"; } -.bi-mouse3::before { content: "\f49d"; } -.bi-music-note-beamed::before { content: "\f49e"; } -.bi-music-note-list::before { content: "\f49f"; } -.bi-music-note::before { content: "\f4a0"; } -.bi-music-player-fill::before { content: "\f4a1"; } -.bi-music-player::before { content: "\f4a2"; } -.bi-newspaper::before { content: "\f4a3"; } -.bi-node-minus-fill::before { content: "\f4a4"; } -.bi-node-minus::before { content: "\f4a5"; } -.bi-node-plus-fill::before { content: "\f4a6"; } -.bi-node-plus::before { content: "\f4a7"; } -.bi-nut-fill::before { content: "\f4a8"; } -.bi-nut::before { content: "\f4a9"; } -.bi-octagon-fill::before { content: "\f4aa"; } -.bi-octagon-half::before { content: "\f4ab"; } -.bi-octagon::before { content: "\f4ac"; } -.bi-option::before { content: "\f4ad"; } -.bi-outlet::before { content: "\f4ae"; } -.bi-paint-bucket::before { content: "\f4af"; } -.bi-palette-fill::before { content: "\f4b0"; } -.bi-palette::before { content: "\f4b1"; } -.bi-palette2::before { content: "\f4b2"; } -.bi-paperclip::before { content: "\f4b3"; } -.bi-paragraph::before { content: "\f4b4"; } -.bi-patch-check-fill::before { content: "\f4b5"; } -.bi-patch-check::before { content: "\f4b6"; } -.bi-patch-exclamation-fill::before { content: "\f4b7"; } -.bi-patch-exclamation::before { content: "\f4b8"; } -.bi-patch-minus-fill::before { content: "\f4b9"; } -.bi-patch-minus::before { content: "\f4ba"; } -.bi-patch-plus-fill::before { content: "\f4bb"; } -.bi-patch-plus::before { content: "\f4bc"; } -.bi-patch-question-fill::before { content: "\f4bd"; } -.bi-patch-question::before { content: "\f4be"; } -.bi-pause-btn-fill::before { content: "\f4bf"; } -.bi-pause-btn::before { content: "\f4c0"; } -.bi-pause-circle-fill::before { content: "\f4c1"; } -.bi-pause-circle::before { content: "\f4c2"; } -.bi-pause-fill::before { content: "\f4c3"; } -.bi-pause::before { content: "\f4c4"; } -.bi-peace-fill::before { content: "\f4c5"; } -.bi-peace::before { content: "\f4c6"; } -.bi-pen-fill::before { content: "\f4c7"; } -.bi-pen::before { content: "\f4c8"; } -.bi-pencil-fill::before { content: "\f4c9"; } -.bi-pencil-square::before { content: "\f4ca"; } -.bi-pencil::before { content: "\f4cb"; } -.bi-pentagon-fill::before { content: "\f4cc"; } -.bi-pentagon-half::before { content: "\f4cd"; } -.bi-pentagon::before { content: "\f4ce"; } -.bi-people-fill::before { content: "\f4cf"; } -.bi-people::before { content: "\f4d0"; } -.bi-percent::before { content: "\f4d1"; } -.bi-person-badge-fill::before { content: "\f4d2"; } -.bi-person-badge::before { content: "\f4d3"; } -.bi-person-bounding-box::before { content: "\f4d4"; } -.bi-person-check-fill::before { content: "\f4d5"; } -.bi-person-check::before { content: "\f4d6"; } -.bi-person-circle::before { content: "\f4d7"; } -.bi-person-dash-fill::before { content: "\f4d8"; } -.bi-person-dash::before { content: "\f4d9"; } -.bi-person-fill::before { content: "\f4da"; } -.bi-person-lines-fill::before { content: "\f4db"; } -.bi-person-plus-fill::before { content: "\f4dc"; } -.bi-person-plus::before { content: "\f4dd"; } -.bi-person-square::before { content: "\f4de"; } -.bi-person-x-fill::before { content: "\f4df"; } -.bi-person-x::before { content: "\f4e0"; } -.bi-person::before { content: "\f4e1"; } -.bi-phone-fill::before { content: "\f4e2"; } -.bi-phone-landscape-fill::before { content: "\f4e3"; } -.bi-phone-landscape::before { content: "\f4e4"; } -.bi-phone-vibrate-fill::before { content: "\f4e5"; } -.bi-phone-vibrate::before { content: "\f4e6"; } -.bi-phone::before { content: "\f4e7"; } -.bi-pie-chart-fill::before { content: "\f4e8"; } -.bi-pie-chart::before { content: "\f4e9"; } -.bi-pin-angle-fill::before { content: "\f4ea"; } -.bi-pin-angle::before { content: "\f4eb"; } -.bi-pin-fill::before { content: "\f4ec"; } -.bi-pin::before { content: "\f4ed"; } -.bi-pip-fill::before { content: "\f4ee"; } -.bi-pip::before { content: "\f4ef"; } -.bi-play-btn-fill::before { content: "\f4f0"; } -.bi-play-btn::before { content: "\f4f1"; } -.bi-play-circle-fill::before { content: "\f4f2"; } -.bi-play-circle::before { content: "\f4f3"; } -.bi-play-fill::before { content: "\f4f4"; } -.bi-play::before { content: "\f4f5"; } -.bi-plug-fill::before { content: "\f4f6"; } -.bi-plug::before { content: "\f4f7"; } -.bi-plus-circle-dotted::before { content: "\f4f8"; } -.bi-plus-circle-fill::before { content: "\f4f9"; } -.bi-plus-circle::before { content: "\f4fa"; } -.bi-plus-square-dotted::before { content: "\f4fb"; } -.bi-plus-square-fill::before { content: "\f4fc"; } -.bi-plus-square::before { content: "\f4fd"; } -.bi-plus::before { content: "\f4fe"; } -.bi-power::before { content: "\f4ff"; } -.bi-printer-fill::before { content: "\f500"; } -.bi-printer::before { content: "\f501"; } -.bi-puzzle-fill::before { content: "\f502"; } -.bi-puzzle::before { content: "\f503"; } -.bi-question-circle-fill::before { content: "\f504"; } -.bi-question-circle::before { content: "\f505"; } -.bi-question-diamond-fill::before { content: "\f506"; } -.bi-question-diamond::before { content: "\f507"; } -.bi-question-octagon-fill::before { content: "\f508"; } -.bi-question-octagon::before { content: "\f509"; } -.bi-question-square-fill::before { content: "\f50a"; } -.bi-question-square::before { content: "\f50b"; } -.bi-question::before { content: "\f50c"; } -.bi-rainbow::before { content: "\f50d"; } -.bi-receipt-cutoff::before { content: "\f50e"; } -.bi-receipt::before { content: "\f50f"; } -.bi-reception-0::before { content: "\f510"; } -.bi-reception-1::before { content: "\f511"; } -.bi-reception-2::before { content: "\f512"; } -.bi-reception-3::before { content: "\f513"; } -.bi-reception-4::before { content: "\f514"; } -.bi-record-btn-fill::before { content: "\f515"; } -.bi-record-btn::before { content: "\f516"; } -.bi-record-circle-fill::before { content: "\f517"; } -.bi-record-circle::before { content: "\f518"; } -.bi-record-fill::before { content: "\f519"; } -.bi-record::before { content: "\f51a"; } -.bi-record2-fill::before { content: "\f51b"; } -.bi-record2::before { content: "\f51c"; } -.bi-reply-all-fill::before { content: "\f51d"; } -.bi-reply-all::before { content: "\f51e"; } -.bi-reply-fill::before { content: "\f51f"; } -.bi-reply::before { content: "\f520"; } -.bi-rss-fill::before { content: "\f521"; } -.bi-rss::before { content: "\f522"; } -.bi-rulers::before { content: "\f523"; } -.bi-save-fill::before { content: "\f524"; } -.bi-save::before { content: "\f525"; } -.bi-save2-fill::before { content: "\f526"; } -.bi-save2::before { content: "\f527"; } -.bi-scissors::before { content: "\f528"; } -.bi-screwdriver::before { content: "\f529"; } -.bi-search::before { content: "\f52a"; } -.bi-segmented-nav::before { content: "\f52b"; } -.bi-server::before { content: "\f52c"; } -.bi-share-fill::before { content: "\f52d"; } -.bi-share::before { content: "\f52e"; } -.bi-shield-check::before { content: "\f52f"; } -.bi-shield-exclamation::before { content: "\f530"; } -.bi-shield-fill-check::before { content: "\f531"; } -.bi-shield-fill-exclamation::before { content: "\f532"; } -.bi-shield-fill-minus::before { content: "\f533"; } -.bi-shield-fill-plus::before { content: "\f534"; } -.bi-shield-fill-x::before { content: "\f535"; } -.bi-shield-fill::before { content: "\f536"; } -.bi-shield-lock-fill::before { content: "\f537"; } -.bi-shield-lock::before { content: "\f538"; } -.bi-shield-minus::before { content: "\f539"; } -.bi-shield-plus::before { content: "\f53a"; } -.bi-shield-shaded::before { content: "\f53b"; } -.bi-shield-slash-fill::before { content: "\f53c"; } -.bi-shield-slash::before { content: "\f53d"; } -.bi-shield-x::before { content: "\f53e"; } -.bi-shield::before { content: "\f53f"; } -.bi-shift-fill::before { content: "\f540"; } -.bi-shift::before { content: "\f541"; } -.bi-shop-window::before { content: "\f542"; } -.bi-shop::before { content: "\f543"; } -.bi-shuffle::before { content: "\f544"; } -.bi-signpost-2-fill::before { content: "\f545"; } -.bi-signpost-2::before { content: "\f546"; } -.bi-signpost-fill::before { content: "\f547"; } -.bi-signpost-split-fill::before { content: "\f548"; } -.bi-signpost-split::before { content: "\f549"; } -.bi-signpost::before { content: "\f54a"; } -.bi-sim-fill::before { content: "\f54b"; } -.bi-sim::before { content: "\f54c"; } -.bi-skip-backward-btn-fill::before { content: "\f54d"; } -.bi-skip-backward-btn::before { content: "\f54e"; } -.bi-skip-backward-circle-fill::before { content: "\f54f"; } -.bi-skip-backward-circle::before { content: "\f550"; } -.bi-skip-backward-fill::before { content: "\f551"; } -.bi-skip-backward::before { content: "\f552"; } -.bi-skip-end-btn-fill::before { content: "\f553"; } -.bi-skip-end-btn::before { content: "\f554"; } -.bi-skip-end-circle-fill::before { content: "\f555"; } -.bi-skip-end-circle::before { content: "\f556"; } -.bi-skip-end-fill::before { content: "\f557"; } -.bi-skip-end::before { content: "\f558"; } -.bi-skip-forward-btn-fill::before { content: "\f559"; } -.bi-skip-forward-btn::before { content: "\f55a"; } -.bi-skip-forward-circle-fill::before { content: "\f55b"; } -.bi-skip-forward-circle::before { content: "\f55c"; } -.bi-skip-forward-fill::before { content: "\f55d"; } -.bi-skip-forward::before { content: "\f55e"; } -.bi-skip-start-btn-fill::before { content: "\f55f"; } -.bi-skip-start-btn::before { content: "\f560"; } -.bi-skip-start-circle-fill::before { content: "\f561"; } -.bi-skip-start-circle::before { content: "\f562"; } -.bi-skip-start-fill::before { content: "\f563"; } -.bi-skip-start::before { content: "\f564"; } -.bi-slack::before { content: "\f565"; } -.bi-slash-circle-fill::before { content: "\f566"; } -.bi-slash-circle::before { content: "\f567"; } -.bi-slash-square-fill::before { content: "\f568"; } -.bi-slash-square::before { content: "\f569"; } -.bi-slash::before { content: "\f56a"; } -.bi-sliders::before { content: "\f56b"; } -.bi-smartwatch::before { content: "\f56c"; } -.bi-snow::before { content: "\f56d"; } -.bi-snow2::before { content: "\f56e"; } -.bi-snow3::before { content: "\f56f"; } -.bi-sort-alpha-down-alt::before { content: "\f570"; } -.bi-sort-alpha-down::before { content: "\f571"; } -.bi-sort-alpha-up-alt::before { content: "\f572"; } -.bi-sort-alpha-up::before { content: "\f573"; } -.bi-sort-down-alt::before { content: "\f574"; } -.bi-sort-down::before { content: "\f575"; } -.bi-sort-numeric-down-alt::before { content: "\f576"; } -.bi-sort-numeric-down::before { content: "\f577"; } -.bi-sort-numeric-up-alt::before { content: "\f578"; } -.bi-sort-numeric-up::before { content: "\f579"; } -.bi-sort-up-alt::before { content: "\f57a"; } -.bi-sort-up::before { content: "\f57b"; } -.bi-soundwave::before { content: "\f57c"; } -.bi-speaker-fill::before { content: "\f57d"; } -.bi-speaker::before { content: "\f57e"; } -.bi-speedometer::before { content: "\f57f"; } -.bi-speedometer2::before { content: "\f580"; } -.bi-spellcheck::before { content: "\f581"; } -.bi-square-fill::before { content: "\f582"; } -.bi-square-half::before { content: "\f583"; } -.bi-square::before { content: "\f584"; } -.bi-stack::before { content: "\f585"; } -.bi-star-fill::before { content: "\f586"; } -.bi-star-half::before { content: "\f587"; } -.bi-star::before { content: "\f588"; } -.bi-stars::before { content: "\f589"; } -.bi-stickies-fill::before { content: "\f58a"; } -.bi-stickies::before { content: "\f58b"; } -.bi-sticky-fill::before { content: "\f58c"; } -.bi-sticky::before { content: "\f58d"; } -.bi-stop-btn-fill::before { content: "\f58e"; } -.bi-stop-btn::before { content: "\f58f"; } -.bi-stop-circle-fill::before { content: "\f590"; } -.bi-stop-circle::before { content: "\f591"; } -.bi-stop-fill::before { content: "\f592"; } -.bi-stop::before { content: "\f593"; } -.bi-stoplights-fill::before { content: "\f594"; } -.bi-stoplights::before { content: "\f595"; } -.bi-stopwatch-fill::before { content: "\f596"; } -.bi-stopwatch::before { content: "\f597"; } -.bi-subtract::before { content: "\f598"; } -.bi-suit-club-fill::before { content: "\f599"; } -.bi-suit-club::before { content: "\f59a"; } -.bi-suit-diamond-fill::before { content: "\f59b"; } -.bi-suit-diamond::before { content: "\f59c"; } -.bi-suit-heart-fill::before { content: "\f59d"; } -.bi-suit-heart::before { content: "\f59e"; } -.bi-suit-spade-fill::before { content: "\f59f"; } -.bi-suit-spade::before { content: "\f5a0"; } -.bi-sun-fill::before { content: "\f5a1"; } -.bi-sun::before { content: "\f5a2"; } -.bi-sunglasses::before { content: "\f5a3"; } -.bi-sunrise-fill::before { content: "\f5a4"; } -.bi-sunrise::before { content: "\f5a5"; } -.bi-sunset-fill::before { content: "\f5a6"; } -.bi-sunset::before { content: "\f5a7"; } -.bi-symmetry-horizontal::before { content: "\f5a8"; } -.bi-symmetry-vertical::before { content: "\f5a9"; } -.bi-table::before { content: "\f5aa"; } -.bi-tablet-fill::before { content: "\f5ab"; } -.bi-tablet-landscape-fill::before { content: "\f5ac"; } -.bi-tablet-landscape::before { content: "\f5ad"; } -.bi-tablet::before { content: "\f5ae"; } -.bi-tag-fill::before { content: "\f5af"; } -.bi-tag::before { content: "\f5b0"; } -.bi-tags-fill::before { content: "\f5b1"; } -.bi-tags::before { content: "\f5b2"; } -.bi-telegram::before { content: "\f5b3"; } -.bi-telephone-fill::before { content: "\f5b4"; } -.bi-telephone-forward-fill::before { content: "\f5b5"; } -.bi-telephone-forward::before { content: "\f5b6"; } -.bi-telephone-inbound-fill::before { content: "\f5b7"; } -.bi-telephone-inbound::before { content: "\f5b8"; } -.bi-telephone-minus-fill::before { content: "\f5b9"; } -.bi-telephone-minus::before { content: "\f5ba"; } -.bi-telephone-outbound-fill::before { content: "\f5bb"; } -.bi-telephone-outbound::before { content: "\f5bc"; } -.bi-telephone-plus-fill::before { content: "\f5bd"; } -.bi-telephone-plus::before { content: "\f5be"; } -.bi-telephone-x-fill::before { content: "\f5bf"; } -.bi-telephone-x::before { content: "\f5c0"; } -.bi-telephone::before { content: "\f5c1"; } -.bi-terminal-fill::before { content: "\f5c2"; } -.bi-terminal::before { content: "\f5c3"; } -.bi-text-center::before { content: "\f5c4"; } -.bi-text-indent-left::before { content: "\f5c5"; } -.bi-text-indent-right::before { content: "\f5c6"; } -.bi-text-left::before { content: "\f5c7"; } -.bi-text-paragraph::before { content: "\f5c8"; } -.bi-text-right::before { content: "\f5c9"; } -.bi-textarea-resize::before { content: "\f5ca"; } -.bi-textarea-t::before { content: "\f5cb"; } -.bi-textarea::before { content: "\f5cc"; } -.bi-thermometer-half::before { content: "\f5cd"; } -.bi-thermometer-high::before { content: "\f5ce"; } -.bi-thermometer-low::before { content: "\f5cf"; } -.bi-thermometer-snow::before { content: "\f5d0"; } -.bi-thermometer-sun::before { content: "\f5d1"; } -.bi-thermometer::before { content: "\f5d2"; } -.bi-three-dots-vertical::before { content: "\f5d3"; } -.bi-three-dots::before { content: "\f5d4"; } -.bi-toggle-off::before { content: "\f5d5"; } -.bi-toggle-on::before { content: "\f5d6"; } -.bi-toggle2-off::before { content: "\f5d7"; } -.bi-toggle2-on::before { content: "\f5d8"; } -.bi-toggles::before { content: "\f5d9"; } -.bi-toggles2::before { content: "\f5da"; } -.bi-tools::before { content: "\f5db"; } -.bi-tornado::before { content: "\f5dc"; } -.bi-trash-fill::before { content: "\f5dd"; } -.bi-trash::before { content: "\f5de"; } -.bi-trash2-fill::before { content: "\f5df"; } -.bi-trash2::before { content: "\f5e0"; } -.bi-tree-fill::before { content: "\f5e1"; } -.bi-tree::before { content: "\f5e2"; } -.bi-triangle-fill::before { content: "\f5e3"; } -.bi-triangle-half::before { content: "\f5e4"; } -.bi-triangle::before { content: "\f5e5"; } -.bi-trophy-fill::before { content: "\f5e6"; } -.bi-trophy::before { content: "\f5e7"; } -.bi-tropical-storm::before { content: "\f5e8"; } -.bi-truck-flatbed::before { content: "\f5e9"; } -.bi-truck::before { content: "\f5ea"; } -.bi-tsunami::before { content: "\f5eb"; } -.bi-tv-fill::before { content: "\f5ec"; } -.bi-tv::before { content: "\f5ed"; } -.bi-twitch::before { content: "\f5ee"; } -.bi-twitter::before { content: "\f5ef"; } -.bi-type-bold::before { content: "\f5f0"; } -.bi-type-h1::before { content: "\f5f1"; } -.bi-type-h2::before { content: "\f5f2"; } -.bi-type-h3::before { content: "\f5f3"; } -.bi-type-italic::before { content: "\f5f4"; } -.bi-type-strikethrough::before { content: "\f5f5"; } -.bi-type-underline::before { content: "\f5f6"; } -.bi-type::before { content: "\f5f7"; } -.bi-ui-checks-grid::before { content: "\f5f8"; } -.bi-ui-checks::before { content: "\f5f9"; } -.bi-ui-radios-grid::before { content: "\f5fa"; } -.bi-ui-radios::before { content: "\f5fb"; } -.bi-umbrella-fill::before { content: "\f5fc"; } -.bi-umbrella::before { content: "\f5fd"; } -.bi-union::before { content: "\f5fe"; } -.bi-unlock-fill::before { content: "\f5ff"; } -.bi-unlock::before { content: "\f600"; } -.bi-upc-scan::before { content: "\f601"; } -.bi-upc::before { content: "\f602"; } -.bi-upload::before { content: "\f603"; } -.bi-vector-pen::before { content: "\f604"; } -.bi-view-list::before { content: "\f605"; } -.bi-view-stacked::before { content: "\f606"; } -.bi-vinyl-fill::before { content: "\f607"; } -.bi-vinyl::before { content: "\f608"; } -.bi-voicemail::before { content: "\f609"; } -.bi-volume-down-fill::before { content: "\f60a"; } -.bi-volume-down::before { content: "\f60b"; } -.bi-volume-mute-fill::before { content: "\f60c"; } -.bi-volume-mute::before { content: "\f60d"; } -.bi-volume-off-fill::before { content: "\f60e"; } -.bi-volume-off::before { content: "\f60f"; } -.bi-volume-up-fill::before { content: "\f610"; } -.bi-volume-up::before { content: "\f611"; } -.bi-vr::before { content: "\f612"; } -.bi-wallet-fill::before { content: "\f613"; } -.bi-wallet::before { content: "\f614"; } -.bi-wallet2::before { content: "\f615"; } -.bi-watch::before { content: "\f616"; } -.bi-water::before { content: "\f617"; } -.bi-whatsapp::before { content: "\f618"; } -.bi-wifi-1::before { content: "\f619"; } -.bi-wifi-2::before { content: "\f61a"; } -.bi-wifi-off::before { content: "\f61b"; } -.bi-wifi::before { content: "\f61c"; } -.bi-wind::before { content: "\f61d"; } -.bi-window-dock::before { content: "\f61e"; } -.bi-window-sidebar::before { content: "\f61f"; } -.bi-window::before { content: "\f620"; } -.bi-wrench::before { content: "\f621"; } -.bi-x-circle-fill::before { content: "\f622"; } -.bi-x-circle::before { content: "\f623"; } -.bi-x-diamond-fill::before { content: "\f624"; } -.bi-x-diamond::before { content: "\f625"; } -.bi-x-octagon-fill::before { content: "\f626"; } -.bi-x-octagon::before { content: "\f627"; } -.bi-x-square-fill::before { content: "\f628"; } -.bi-x-square::before { content: "\f629"; } -.bi-x::before { content: "\f62a"; } -.bi-youtube::before { content: "\f62b"; } -.bi-zoom-in::before { content: "\f62c"; } -.bi-zoom-out::before { content: "\f62d"; } -.bi-bank::before { content: "\f62e"; } -.bi-bank2::before { content: "\f62f"; } -.bi-bell-slash-fill::before { content: "\f630"; } -.bi-bell-slash::before { content: "\f631"; } -.bi-cash-coin::before { content: "\f632"; } -.bi-check-lg::before { content: "\f633"; } -.bi-coin::before { content: "\f634"; } -.bi-currency-bitcoin::before { content: "\f635"; } -.bi-currency-dollar::before { content: "\f636"; } -.bi-currency-euro::before { content: "\f637"; } -.bi-currency-exchange::before { content: "\f638"; } -.bi-currency-pound::before { content: "\f639"; } -.bi-currency-yen::before { content: "\f63a"; } -.bi-dash-lg::before { content: "\f63b"; } -.bi-exclamation-lg::before { content: "\f63c"; } -.bi-file-earmark-pdf-fill::before { content: "\f63d"; } -.bi-file-earmark-pdf::before { content: "\f63e"; } -.bi-file-pdf-fill::before { content: "\f63f"; } -.bi-file-pdf::before { content: "\f640"; } -.bi-gender-ambiguous::before { content: "\f641"; } -.bi-gender-female::before { content: "\f642"; } -.bi-gender-male::before { content: "\f643"; } -.bi-gender-trans::before { content: "\f644"; } -.bi-headset-vr::before { content: "\f645"; } -.bi-info-lg::before { content: "\f646"; } -.bi-mastodon::before { content: "\f647"; } -.bi-messenger::before { content: "\f648"; } -.bi-piggy-bank-fill::before { content: "\f649"; } -.bi-piggy-bank::before { content: "\f64a"; } -.bi-pin-map-fill::before { content: "\f64b"; } -.bi-pin-map::before { content: "\f64c"; } -.bi-plus-lg::before { content: "\f64d"; } -.bi-question-lg::before { content: "\f64e"; } -.bi-recycle::before { content: "\f64f"; } -.bi-reddit::before { content: "\f650"; } -.bi-safe-fill::before { content: "\f651"; } -.bi-safe2-fill::before { content: "\f652"; } -.bi-safe2::before { content: "\f653"; } -.bi-sd-card-fill::before { content: "\f654"; } -.bi-sd-card::before { content: "\f655"; } -.bi-skype::before { content: "\f656"; } -.bi-slash-lg::before { content: "\f657"; } -.bi-translate::before { content: "\f658"; } -.bi-x-lg::before { content: "\f659"; } -.bi-safe::before { content: "\f65a"; } -.bi-apple::before { content: "\f65b"; } -.bi-microsoft::before { content: "\f65d"; } -.bi-windows::before { content: "\f65e"; } -.bi-behance::before { content: "\f65c"; } -.bi-dribbble::before { content: "\f65f"; } -.bi-line::before { content: "\f660"; } -.bi-medium::before { content: "\f661"; } -.bi-paypal::before { content: "\f662"; } -.bi-pinterest::before { content: "\f663"; } -.bi-signal::before { content: "\f664"; } -.bi-snapchat::before { content: "\f665"; } -.bi-spotify::before { content: "\f666"; } -.bi-stack-overflow::before { content: "\f667"; } -.bi-strava::before { content: "\f668"; } -.bi-wordpress::before { content: "\f669"; } -.bi-vimeo::before { content: "\f66a"; } -.bi-activity::before { content: "\f66b"; } -.bi-easel2-fill::before { content: "\f66c"; } -.bi-easel2::before { content: "\f66d"; } -.bi-easel3-fill::before { content: "\f66e"; } -.bi-easel3::before { content: "\f66f"; } -.bi-fan::before { content: "\f670"; } -.bi-fingerprint::before { content: "\f671"; } -.bi-graph-down-arrow::before { content: "\f672"; } -.bi-graph-up-arrow::before { content: "\f673"; } -.bi-hypnotize::before { content: "\f674"; } -.bi-magic::before { content: "\f675"; } -.bi-person-rolodex::before { content: "\f676"; } -.bi-person-video::before { content: "\f677"; } -.bi-person-video2::before { content: "\f678"; } -.bi-person-video3::before { content: "\f679"; } -.bi-person-workspace::before { content: "\f67a"; } -.bi-radioactive::before { content: "\f67b"; } -.bi-webcam-fill::before { content: "\f67c"; } -.bi-webcam::before { content: "\f67d"; } -.bi-yin-yang::before { content: "\f67e"; } -.bi-bandaid-fill::before { content: "\f680"; } -.bi-bandaid::before { content: "\f681"; } -.bi-bluetooth::before { content: "\f682"; } -.bi-body-text::before { content: "\f683"; } -.bi-boombox::before { content: "\f684"; } -.bi-boxes::before { content: "\f685"; } -.bi-dpad-fill::before { content: "\f686"; } -.bi-dpad::before { content: "\f687"; } -.bi-ear-fill::before { content: "\f688"; } -.bi-ear::before { content: "\f689"; } -.bi-envelope-check-1::before { content: "\f68a"; } -.bi-envelope-check-fill::before { content: "\f68b"; } -.bi-envelope-check::before { content: "\f68c"; } -.bi-envelope-dash-1::before { content: "\f68d"; } -.bi-envelope-dash-fill::before { content: "\f68e"; } -.bi-envelope-dash::before { content: "\f68f"; } -.bi-envelope-exclamation-1::before { content: "\f690"; } -.bi-envelope-exclamation-fill::before { content: "\f691"; } -.bi-envelope-exclamation::before { content: "\f692"; } -.bi-envelope-plus-fill::before { content: "\f693"; } -.bi-envelope-plus::before { content: "\f694"; } -.bi-envelope-slash-1::before { content: "\f695"; } -.bi-envelope-slash-fill::before { content: "\f696"; } -.bi-envelope-slash::before { content: "\f697"; } -.bi-envelope-x-1::before { content: "\f698"; } -.bi-envelope-x-fill::before { content: "\f699"; } -.bi-envelope-x::before { content: "\f69a"; } -.bi-explicit-fill::before { content: "\f69b"; } -.bi-explicit::before { content: "\f69c"; } -.bi-git::before { content: "\f69d"; } -.bi-infinity::before { content: "\f69e"; } -.bi-list-columns-reverse::before { content: "\f69f"; } -.bi-list-columns::before { content: "\f6a0"; } -.bi-meta::before { content: "\f6a1"; } -.bi-mortorboard-fill::before { content: "\f6a2"; } -.bi-mortorboard::before { content: "\f6a3"; } -.bi-nintendo-switch::before { content: "\f6a4"; } -.bi-pc-display-horizontal::before { content: "\f6a5"; } -.bi-pc-display::before { content: "\f6a6"; } -.bi-pc-horizontal::before { content: "\f6a7"; } -.bi-pc::before { content: "\f6a8"; } -.bi-playstation::before { content: "\f6a9"; } -.bi-plus-slash-minus::before { content: "\f6aa"; } -.bi-projector-fill::before { content: "\f6ab"; } -.bi-projector::before { content: "\f6ac"; } -.bi-qr-code-scan::before { content: "\f6ad"; } -.bi-qr-code::before { content: "\f6ae"; } -.bi-quora::before { content: "\f6af"; } -.bi-quote::before { content: "\f6b0"; } -.bi-robot::before { content: "\f6b1"; } -.bi-send-check-fill::before { content: "\f6b2"; } -.bi-send-check::before { content: "\f6b3"; } -.bi-send-dash-fill::before { content: "\f6b4"; } -.bi-send-dash::before { content: "\f6b5"; } -.bi-send-exclamation-1::before { content: "\f6b6"; } -.bi-send-exclamation-fill::before { content: "\f6b7"; } -.bi-send-exclamation::before { content: "\f6b8"; } -.bi-send-fill::before { content: "\f6b9"; } -.bi-send-plus-fill::before { content: "\f6ba"; } -.bi-send-plus::before { content: "\f6bb"; } -.bi-send-slash-fill::before { content: "\f6bc"; } -.bi-send-slash::before { content: "\f6bd"; } -.bi-send-x-fill::before { content: "\f6be"; } -.bi-send-x::before { content: "\f6bf"; } -.bi-send::before { content: "\f6c0"; } -.bi-steam::before { content: "\f6c1"; } -.bi-terminal-dash-1::before { content: "\f6c2"; } -.bi-terminal-dash::before { content: "\f6c3"; } -.bi-terminal-plus::before { content: "\f6c4"; } -.bi-terminal-split::before { content: "\f6c5"; } -.bi-ticket-detailed-fill::before { content: "\f6c6"; } -.bi-ticket-detailed::before { content: "\f6c7"; } -.bi-ticket-fill::before { content: "\f6c8"; } -.bi-ticket-perforated-fill::before { content: "\f6c9"; } -.bi-ticket-perforated::before { content: "\f6ca"; } -.bi-ticket::before { content: "\f6cb"; } -.bi-tiktok::before { content: "\f6cc"; } -.bi-window-dash::before { content: "\f6cd"; } -.bi-window-desktop::before { content: "\f6ce"; } -.bi-window-fullscreen::before { content: "\f6cf"; } -.bi-window-plus::before { content: "\f6d0"; } -.bi-window-split::before { content: "\f6d1"; } -.bi-window-stack::before { content: "\f6d2"; } -.bi-window-x::before { content: "\f6d3"; } -.bi-xbox::before { content: "\f6d4"; } -.bi-ethernet::before { content: "\f6d5"; } -.bi-hdmi-fill::before { content: "\f6d6"; } -.bi-hdmi::before { content: "\f6d7"; } -.bi-usb-c-fill::before { content: "\f6d8"; } -.bi-usb-c::before { content: "\f6d9"; } -.bi-usb-fill::before { content: "\f6da"; } -.bi-usb-plug-fill::before { content: "\f6db"; } -.bi-usb-plug::before { content: "\f6dc"; } -.bi-usb-symbol::before { content: "\f6dd"; } -.bi-usb::before { content: "\f6de"; } -.bi-boombox-fill::before { content: "\f6df"; } -.bi-displayport-1::before { content: "\f6e0"; } -.bi-displayport::before { content: "\f6e1"; } -.bi-gpu-card::before { content: "\f6e2"; } -.bi-memory::before { content: "\f6e3"; } -.bi-modem-fill::before { content: "\f6e4"; } -.bi-modem::before { content: "\f6e5"; } -.bi-motherboard-fill::before { content: "\f6e6"; } -.bi-motherboard::before { content: "\f6e7"; } -.bi-optical-audio-fill::before { content: "\f6e8"; } -.bi-optical-audio::before { content: "\f6e9"; } -.bi-pci-card::before { content: "\f6ea"; } -.bi-router-fill::before { content: "\f6eb"; } -.bi-router::before { content: "\f6ec"; } -.bi-ssd-fill::before { content: "\f6ed"; } -.bi-ssd::before { content: "\f6ee"; } -.bi-thunderbolt-fill::before { content: "\f6ef"; } -.bi-thunderbolt::before { content: "\f6f0"; } -.bi-usb-drive-fill::before { content: "\f6f1"; } -.bi-usb-drive::before { content: "\f6f2"; } -.bi-usb-micro-fill::before { content: "\f6f3"; } -.bi-usb-micro::before { content: "\f6f4"; } -.bi-usb-mini-fill::before { content: "\f6f5"; } -.bi-usb-mini::before { content: "\f6f6"; } -.bi-cloud-haze2::before { content: "\f6f7"; } -.bi-device-hdd-fill::before { content: "\f6f8"; } -.bi-device-hdd::before { content: "\f6f9"; } -.bi-device-ssd-fill::before { content: "\f6fa"; } -.bi-device-ssd::before { content: "\f6fb"; } -.bi-displayport-fill::before { content: "\f6fc"; } -.bi-mortarboard-fill::before { content: "\f6fd"; } -.bi-mortarboard::before { content: "\f6fe"; } -.bi-terminal-x::before { content: "\f6ff"; } diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/bootstrap.min.css b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/bootstrap.min.css deleted file mode 100644 index 1472dec05..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/bootstrap.min.css +++ /dev/null @@ -1,7 +0,0 @@ -@charset "UTF-8";/*! - * Bootstrap v5.1.3 (https://getbootstrap.com/) - * Copyright 2011-2021 The Bootstrap Authors - * Copyright 2011-2021 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-body-color-rgb:33,37,41;--bs-body-bg-rgb:255,255,255;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-bg:#fff}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:#6c757d}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{width:100%;padding-right:var(--bs-gutter-x,.75rem);padding-left:var(--bs-gutter-x,.75rem);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.6666666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.6666666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.6666666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.6666666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-bg:transparent;--bs-table-accent-bg:transparent;--bs-table-striped-color:#212529;--bs-table-striped-bg:rgba(0, 0, 0, 0.05);--bs-table-active-color:#212529;--bs-table-active-bg:rgba(0, 0, 0, 0.1);--bs-table-hover-color:#212529;--bs-table-hover-bg:rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;color:#212529;vertical-align:top;border-color:#dee2e6}.table>:not(caption)>*>*{padding:.5rem .5rem;background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg)}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table>:not(:first-child){border-top:2px solid currentColor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:1px 0}.table-bordered>:not(caption)>*>*{border-width:0 1px}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-active{--bs-table-accent-bg:var(--bs-table-active-bg);color:var(--bs-table-active-color)}.table-hover>tbody>tr:hover>*{--bs-table-accent-bg:var(--bs-table-hover-bg);color:var(--bs-table-hover-color)}.table-primary{--bs-table-bg:#cfe2ff;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:#000;border-color:#bacbe6}.table-secondary{--bs-table-bg:#e2e3e5;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:#000;border-color:#cbccce}.table-success{--bs-table-bg:#d1e7dd;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:#000;border-color:#bcd0c7}.table-info{--bs-table-bg:#cff4fc;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:#000;border-color:#badce3}.table-warning{--bs-table-bg:#fff3cd;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:#000;border-color:#e6dbb9}.table-danger{--bs-table-bg:#f8d7da;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:#000;border-color:#dfc2c4}.table-light{--bs-table-bg:#f8f9fa;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:#000;border-color:#dfe0e1}.table-dark{--bs-table-bg:#212529;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:#fff;border-color:#373b3e}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:#6c757d}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:#212529;background-color:#fff;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{height:1.5em}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:#dde0e3}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + 2px)}textarea.form-control-sm{min-height:calc(1.5em + .5rem + 2px)}textarea.form-control-lg{min-height:calc(1.5em + 1rem + 2px)}.form-control-color{width:3rem;height:auto;padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{height:1.5em;border-radius:.25rem}.form-control-color::-webkit-color-swatch{height:1.5em;border-radius:.25rem}.form-select{display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;-moz-padding-start:calc(0.75rem - 3px);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:#e9ecef}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #212529}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:.2rem}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:.3rem}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-input{width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:#fff;background-repeat:no-repeat;background-position:center;background-size:contain;border:1px solid rgba(0,0,0,.25);-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{width:2em;margin-left:-2.5em;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.form-range:disabled::-moz-range-thumb{background-color:#adb5bd}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px);line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;height:100%;padding:1rem .75rem;pointer-events:none;border:1px solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control{padding:1rem .75rem}.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus{z-index:3}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:3}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#198754}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(25,135,84,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#198754;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:#198754}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:#198754}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:#198754}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#198754}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group .form-control.is-valid,.input-group .form-select.is-valid,.was-validated .input-group .form-control:valid,.was-validated .input-group .form-select:valid{z-index:1}.input-group .form-control.is-valid:focus,.input-group .form-select.is-valid:focus,.was-validated .input-group .form-control:valid:focus,.was-validated .input-group .form-select:valid:focus{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:#dc3545}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:#dc3545}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:#dc3545}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group .form-control.is-invalid,.input-group .form-select.is-invalid,.was-validated .input-group .form-control:invalid,.was-validated .input-group .form-select:invalid{z-index:2}.input-group .form-control.is-invalid:focus,.input-group .form-select.is-invalid:focus,.was-validated .input-group .form-control:invalid:focus,.was-validated .input-group .form-select:invalid:focus{z-index:3}.btn{display:inline-block;font-weight:400;line-height:1.5;color:#212529;text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529}.btn-check:focus+.btn,.btn:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{pointer-events:none;opacity:.65}.btn-primary{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-primary:hover{color:#fff;background-color:#0b5ed7;border-color:#0a58ca}.btn-check:focus+.btn-primary,.btn-primary:focus{color:#fff;background-color:#0b5ed7;border-color:#0a58ca;box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-check:active+.btn-primary,.btn-check:checked+.btn-primary,.btn-primary.active,.btn-primary:active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0a58ca;border-color:#0a53be}.btn-check:active+.btn-primary:focus,.btn-check:checked+.btn-primary:focus,.btn-primary.active:focus,.btn-primary:active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5c636a;border-color:#565e64}.btn-check:focus+.btn-secondary,.btn-secondary:focus{color:#fff;background-color:#5c636a;border-color:#565e64;box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-check:active+.btn-secondary,.btn-check:checked+.btn-secondary,.btn-secondary.active,.btn-secondary:active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#565e64;border-color:#51585e}.btn-check:active+.btn-secondary:focus,.btn-check:checked+.btn-secondary:focus,.btn-secondary.active:focus,.btn-secondary:active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-success{color:#fff;background-color:#198754;border-color:#198754}.btn-success:hover{color:#fff;background-color:#157347;border-color:#146c43}.btn-check:focus+.btn-success,.btn-success:focus{color:#fff;background-color:#157347;border-color:#146c43;box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-check:active+.btn-success,.btn-check:checked+.btn-success,.btn-success.active,.btn-success:active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#146c43;border-color:#13653f}.btn-check:active+.btn-success:focus,.btn-check:checked+.btn-success:focus,.btn-success.active:focus,.btn-success:active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#198754;border-color:#198754}.btn-info{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-info:hover{color:#000;background-color:#31d2f2;border-color:#25cff2}.btn-check:focus+.btn-info,.btn-info:focus{color:#000;background-color:#31d2f2;border-color:#25cff2;box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-check:active+.btn-info,.btn-check:checked+.btn-info,.btn-info.active,.btn-info:active,.show>.btn-info.dropdown-toggle{color:#000;background-color:#3dd5f3;border-color:#25cff2}.btn-check:active+.btn-info:focus,.btn-check:checked+.btn-info:focus,.btn-info.active:focus,.btn-info:active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-info.disabled,.btn-info:disabled{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-warning{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#000;background-color:#ffca2c;border-color:#ffc720}.btn-check:focus+.btn-warning,.btn-warning:focus{color:#000;background-color:#ffca2c;border-color:#ffc720;box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-check:active+.btn-warning,.btn-check:checked+.btn-warning,.btn-warning.active,.btn-warning:active,.show>.btn-warning.dropdown-toggle{color:#000;background-color:#ffcd39;border-color:#ffc720}.btn-check:active+.btn-warning:focus,.btn-check:checked+.btn-warning:focus,.btn-warning.active:focus,.btn-warning:active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#bb2d3b;border-color:#b02a37}.btn-check:focus+.btn-danger,.btn-danger:focus{color:#fff;background-color:#bb2d3b;border-color:#b02a37;box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-check:active+.btn-danger,.btn-check:checked+.btn-danger,.btn-danger.active,.btn-danger:active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#b02a37;border-color:#a52834}.btn-check:active+.btn-danger:focus,.btn-check:checked+.btn-danger:focus,.btn-danger.active:focus,.btn-danger:active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-light{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:focus+.btn-light,.btn-light:focus{color:#000;background-color:#f9fafb;border-color:#f9fafb;box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-check:active+.btn-light,.btn-check:checked+.btn-light,.btn-light.active,.btn-light:active,.show>.btn-light.dropdown-toggle{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:active+.btn-light:focus,.btn-check:checked+.btn-light:focus,.btn-light.active:focus,.btn-light:active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-light.disabled,.btn-light:disabled{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-dark{color:#fff;background-color:#212529;border-color:#212529}.btn-dark:hover{color:#fff;background-color:#1c1f23;border-color:#1a1e21}.btn-check:focus+.btn-dark,.btn-dark:focus{color:#fff;background-color:#1c1f23;border-color:#1a1e21;box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-check:active+.btn-dark,.btn-check:checked+.btn-dark,.btn-dark.active,.btn-dark:active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1a1e21;border-color:#191c1f}.btn-check:active+.btn-dark:focus,.btn-check:checked+.btn-dark:focus,.btn-dark.active:focus,.btn-dark:active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#212529;border-color:#212529}.btn-outline-primary{color:#0d6efd;border-color:#0d6efd}.btn-outline-primary:hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:focus+.btn-outline-primary,.btn-outline-primary:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-check:active+.btn-outline-primary,.btn-check:checked+.btn-outline-primary,.btn-outline-primary.active,.btn-outline-primary.dropdown-toggle.show,.btn-outline-primary:active{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:active+.btn-outline-primary:focus,.btn-check:checked+.btn-outline-primary:focus,.btn-outline-primary.active:focus,.btn-outline-primary.dropdown-toggle.show:focus,.btn-outline-primary:active:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#0d6efd;background-color:transparent}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:focus+.btn-outline-secondary,.btn-outline-secondary:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-check:active+.btn-outline-secondary,.btn-check:checked+.btn-outline-secondary,.btn-outline-secondary.active,.btn-outline-secondary.dropdown-toggle.show,.btn-outline-secondary:active{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:active+.btn-outline-secondary:focus,.btn-check:checked+.btn-outline-secondary:focus,.btn-outline-secondary.active:focus,.btn-outline-secondary.dropdown-toggle.show:focus,.btn-outline-secondary:active:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-success{color:#198754;border-color:#198754}.btn-outline-success:hover{color:#fff;background-color:#198754;border-color:#198754}.btn-check:focus+.btn-outline-success,.btn-outline-success:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-check:active+.btn-outline-success,.btn-check:checked+.btn-outline-success,.btn-outline-success.active,.btn-outline-success.dropdown-toggle.show,.btn-outline-success:active{color:#fff;background-color:#198754;border-color:#198754}.btn-check:active+.btn-outline-success:focus,.btn-check:checked+.btn-outline-success:focus,.btn-outline-success.active:focus,.btn-outline-success.dropdown-toggle.show:focus,.btn-outline-success:active:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#198754;background-color:transparent}.btn-outline-info{color:#0dcaf0;border-color:#0dcaf0}.btn-outline-info:hover{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:focus+.btn-outline-info,.btn-outline-info:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-check:active+.btn-outline-info,.btn-check:checked+.btn-outline-info,.btn-outline-info.active,.btn-outline-info.dropdown-toggle.show,.btn-outline-info:active{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:active+.btn-outline-info:focus,.btn-check:checked+.btn-outline-info:focus,.btn-outline-info.active:focus,.btn-outline-info.dropdown-toggle.show:focus,.btn-outline-info:active:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#0dcaf0;background-color:transparent}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:focus+.btn-outline-warning,.btn-outline-warning:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-check:active+.btn-outline-warning,.btn-check:checked+.btn-outline-warning,.btn-outline-warning.active,.btn-outline-warning.dropdown-toggle.show,.btn-outline-warning:active{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:active+.btn-outline-warning:focus,.btn-check:checked+.btn-outline-warning:focus,.btn-outline-warning.active:focus,.btn-outline-warning.dropdown-toggle.show:focus,.btn-outline-warning:active:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:focus+.btn-outline-danger,.btn-outline-danger:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-check:active+.btn-outline-danger,.btn-check:checked+.btn-outline-danger,.btn-outline-danger.active,.btn-outline-danger.dropdown-toggle.show,.btn-outline-danger:active{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:active+.btn-outline-danger:focus,.btn-check:checked+.btn-outline-danger:focus,.btn-outline-danger.active:focus,.btn-outline-danger.dropdown-toggle.show:focus,.btn-outline-danger:active:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:focus+.btn-outline-light,.btn-outline-light:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-check:active+.btn-outline-light,.btn-check:checked+.btn-outline-light,.btn-outline-light.active,.btn-outline-light.dropdown-toggle.show,.btn-outline-light:active{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:active+.btn-outline-light:focus,.btn-check:checked+.btn-outline-light:focus,.btn-outline-light.active:focus,.btn-outline-light.dropdown-toggle.show:focus,.btn-outline-light:active:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-dark{color:#212529;border-color:#212529}.btn-outline-dark:hover{color:#fff;background-color:#212529;border-color:#212529}.btn-check:focus+.btn-outline-dark,.btn-outline-dark:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-check:active+.btn-outline-dark,.btn-check:checked+.btn-outline-dark,.btn-outline-dark.active,.btn-outline-dark.dropdown-toggle.show,.btn-outline-dark:active{color:#fff;background-color:#212529;border-color:#212529}.btn-check:active+.btn-outline-dark:focus,.btn-check:checked+.btn-outline-dark:focus,.btn-outline-dark.active:focus,.btn-outline-dark.dropdown-toggle.show:focus,.btn-outline-dark:active:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#212529;background-color:transparent}.btn-link{font-weight:400;color:#0d6efd;text-decoration:underline}.btn-link:hover{color:#0a58ca}.btn-link.disabled,.btn-link:disabled{color:#6c757d}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropend,.dropstart,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;z-index:1000;display:none;min-width:10rem;padding:.5rem 0;margin:0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:.125rem}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,.15)}.dropdown-item{display:block;width:100%;padding:.25rem 1rem;clear:both;font-weight:400;color:#212529;text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#1e2125;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#0d6efd}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1rem;color:#212529}.dropdown-menu-dark{color:#dee2e6;background-color:#343a40;border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item{color:#dee2e6}.dropdown-menu-dark .dropdown-item:focus,.dropdown-menu-dark .dropdown-item:hover{color:#fff;background-color:rgba(255,255,255,.15)}.dropdown-menu-dark .dropdown-item.active,.dropdown-menu-dark .dropdown-item:active{color:#fff;background-color:#0d6efd}.dropdown-menu-dark .dropdown-item.disabled,.dropdown-menu-dark .dropdown-item:disabled{color:#adb5bd}.dropdown-menu-dark .dropdown-divider{border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item-text{color:#dee2e6}.dropdown-menu-dark .dropdown-header{color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem;color:#0d6efd;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:#0a58ca}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background:0 0;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6;isolation:isolate}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{background:0 0;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#0d6efd}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;text-decoration:none;white-space:nowrap}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem;transition:box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 .25rem}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas-header{display:none}.navbar-expand-sm .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-sm .offcanvas-bottom,.navbar-expand-sm .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas-header{display:none}.navbar-expand-md .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-md .offcanvas-bottom,.navbar-expand-md .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas-header{display:none}.navbar-expand-lg .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-lg .offcanvas-bottom,.navbar-expand-lg .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas-header{display:none}.navbar-expand-xl .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-xl .offcanvas-bottom,.navbar-expand-xl .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-xxl .offcanvas-bottom,.navbar-expand-xxl .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas-header{display:none}.navbar-expand .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand .offcanvas-bottom,.navbar-expand .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.55)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.55);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.55)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.55)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.55);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.55)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:1rem 1rem}.card-title{margin-bottom:.5rem}.card-subtitle{margin-top:-.25rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:1rem}.card-header{padding:.5rem 1rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.5rem 1rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.5rem;margin-bottom:-.5rem;margin-left:-.5rem;border-bottom:0}.card-header-pills{margin-right:-.5rem;margin-left:-.5rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-group>.card{margin-bottom:.75rem}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:1rem 1.25rem;font-size:1rem;color:#212529;text-align:left;background-color:#fff;border:0;border-radius:0;overflow-anchor:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,border-radius .15s ease}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:#0c63e4;background-color:#e7f1ff;box-shadow:inset 0 -1px 0 rgba(0,0,0,.125)}.accordion-button:not(.collapsed)::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");transform:rotate(-180deg)}.accordion-button::after{flex-shrink:0;width:1.25rem;height:1.25rem;margin-left:auto;content:"";background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-size:1.25rem;transition:transform .2s ease-in-out}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.accordion-header{margin-bottom:0}.accordion-item{background-color:#fff;border:1px solid rgba(0,0,0,.125)}.accordion-item:first-of-type{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.accordion-item:first-of-type .accordion-button{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-body{padding:1rem 1.25rem}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button{border-radius:0}.breadcrumb{display:flex;flex-wrap:wrap;padding:0 0;margin-bottom:1rem;list-style:none}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#6c757d;content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;color:#0d6efd;text-decoration:none;background-color:#fff;border:1px solid #dee2e6;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:#0a58ca;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;color:#0a58ca;background-color:#e9ecef;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item.active .page-link{z-index:3;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;background-color:#fff;border-color:#dee2e6}.page-link{padding:.375rem .75rem}.page-item:first-child .page-link{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{position:relative;padding:1rem 1rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{color:#084298;background-color:#cfe2ff;border-color:#b6d4fe}.alert-primary .alert-link{color:#06357a}.alert-secondary{color:#41464b;background-color:#e2e3e5;border-color:#d3d6d8}.alert-secondary .alert-link{color:#34383c}.alert-success{color:#0f5132;background-color:#d1e7dd;border-color:#badbcc}.alert-success .alert-link{color:#0c4128}.alert-info{color:#055160;background-color:#cff4fc;border-color:#b6effb}.alert-info .alert-link{color:#04414d}.alert-warning{color:#664d03;background-color:#fff3cd;border-color:#ffecb5}.alert-warning .alert-link{color:#523e02}.alert-danger{color:#842029;background-color:#f8d7da;border-color:#f5c2c7}.alert-danger .alert-link{color:#6a1a21}.alert-light{color:#636464;background-color:#fefefe;border-color:#fdfdfe}.alert-light .alert-link{color:#4f5050}.alert-dark{color:#141619;background-color:#d3d3d4;border-color:#bcbebf}.alert-dark .alert-link{color:#101214}@-webkit-keyframes progress-bar-stripes{0%{background-position-x:1rem}}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress{display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#0d6efd;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:1s linear infinite progress-bar-stripes;animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>li::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.5rem 1rem;color:#212529;text-decoration:none;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#084298;background-color:#cfe2ff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#084298;background-color:#bacbe6}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#084298;border-color:#084298}.list-group-item-secondary{color:#41464b;background-color:#e2e3e5}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#41464b;background-color:#cbccce}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#41464b;border-color:#41464b}.list-group-item-success{color:#0f5132;background-color:#d1e7dd}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#0f5132;background-color:#bcd0c7}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#0f5132;border-color:#0f5132}.list-group-item-info{color:#055160;background-color:#cff4fc}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#055160;background-color:#badce3}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#055160;border-color:#055160}.list-group-item-warning{color:#664d03;background-color:#fff3cd}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#664d03;background-color:#e6dbb9}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#664d03;border-color:#664d03}.list-group-item-danger{color:#842029;background-color:#f8d7da}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#842029;background-color:#dfc2c4}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#842029;border-color:#842029}.list-group-item-light{color:#636464;background-color:#fefefe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#636464;background-color:#e5e5e5}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#636464;border-color:#636464}.list-group-item-dark{color:#141619;background-color:#d3d3d4}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#141619;background-color:#bebebf}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.btn-close{box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:#000;background:transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat;border:0;border-radius:.25rem;opacity:.5}.btn-close:hover{color:#000;text-decoration:none;opacity:.75}.btn-close:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);opacity:1}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:.25}.btn-close-white{filter:invert(1) grayscale(100%) brightness(200%)}.toast{width:350px;max-width:100%;font-size:.875rem;pointer-events:auto;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .5rem 1rem rgba(0,0,0,.15);border-radius:.25rem}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:.75rem}.toast-header{display:flex;align-items:center;padding:.5rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-header .btn-close{margin-right:-.375rem;margin-left:.75rem}.toast-body{padding:.75rem;word-wrap:break-word}.modal{position:fixed;top:0;left:0;z-index:1055;display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1050;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .btn-close{padding:.5rem .5rem;margin:-.5rem -.5rem -.5rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;flex-wrap:wrap;flex-shrink:0;align-items:center;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{height:calc(100% - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}.modal-fullscreen .modal-footer{border-radius:0}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}.modal-fullscreen-sm-down .modal-footer{border-radius:0}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}.modal-fullscreen-md-down .modal-footer{border-radius:0}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}.modal-fullscreen-lg-down .modal-footer{border-radius:0}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}.modal-fullscreen-xl-down .modal-footer{border-radius:0}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}.modal-fullscreen-xxl-down .modal-footer{border-radius:0}}.tooltip{position:absolute;z-index:1080;display:block;margin:0;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .tooltip-arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[data-popper-placement^=right],.bs-tooltip-end{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[data-popper-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[data-popper-placement^=left],.bs-tooltip-start{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1070;display:block;max-width:276px;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .popover-arrow{position:absolute;display:block;width:1rem;height:.5rem}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f0f0f0}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem 1rem;margin-bottom:0;font-size:1rem;background-color:#f0f0f0;border-bottom:1px solid rgba(0,0,0,.2);border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:1rem 1rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%;list-style:none}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}@-webkit-keyframes spinner-border{to{transform:rotate(360deg)}}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:.75s linear infinite spinner-border;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:.75s linear infinite spinner-grow;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.offcanvas{position:fixed;bottom:0;z-index:1045;display:flex;flex-direction:column;max-width:100%;visibility:hidden;background-color:#fff;background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:1rem 1rem}.offcanvas-header .btn-close{padding:.5rem .5rem;margin-top:-.5rem;margin-right:-.5rem;margin-bottom:-.5rem}.offcanvas-title{margin-bottom:0;line-height:1.5}.offcanvas-body{flex-grow:1;padding:1rem 1rem;overflow-y:auto}.offcanvas-start{top:0;left:0;width:400px;border-right:1px solid rgba(0,0,0,.2);transform:translateX(-100%)}.offcanvas-end{top:0;right:0;width:400px;border-left:1px solid rgba(0,0,0,.2);transform:translateX(100%)}.offcanvas-top{top:0;right:0;left:0;height:30vh;max-height:100%;border-bottom:1px solid rgba(0,0,0,.2);transform:translateY(-100%)}.offcanvas-bottom{right:0;left:0;height:30vh;max-height:100%;border-top:1px solid rgba(0,0,0,.2);transform:translateY(100%)}.offcanvas.show{transform:none}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentColor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{-webkit-animation:placeholder-glow 2s ease-in-out infinite;animation:placeholder-glow 2s ease-in-out infinite}@-webkit-keyframes placeholder-glow{50%{opacity:.2}}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;-webkit-animation:placeholder-wave 2s linear infinite;animation:placeholder-wave 2s linear infinite}@-webkit-keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.link-primary{color:#0d6efd}.link-primary:focus,.link-primary:hover{color:#0a58ca}.link-secondary{color:#6c757d}.link-secondary:focus,.link-secondary:hover{color:#565e64}.link-success{color:#198754}.link-success:focus,.link-success:hover{color:#146c43}.link-info{color:#0dcaf0}.link-info:focus,.link-info:hover{color:#3dd5f3}.link-warning{color:#ffc107}.link-warning:focus,.link-warning:hover{color:#ffcd39}.link-danger{color:#dc3545}.link-danger:focus,.link-danger:hover{color:#b02a37}.link-light{color:#f8f9fa}.link-light:focus,.link-light:hover{color:#f9fafb}.link-dark{color:#212529}.link-dark:focus,.link-dark:hover{color:#1a1e21}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:75%}.ratio-16x9{--bs-aspect-ratio:56.25%}.ratio-21x9{--bs-aspect-ratio:42.8571428571%}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:1px;min-height:1em;background-color:currentColor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:1px solid #dee2e6!important}.border-0{border:0!important}.border-top{border-top:1px solid #dee2e6!important}.border-top-0{border-top:0!important}.border-end{border-right:1px solid #dee2e6!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:1px solid #dee2e6!important}.border-start-0{border-left:0!important}.border-primary{border-color:#0d6efd!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#198754!important}.border-info{border-color:#0dcaf0!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#212529!important}.border-white{border-color:#fff!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-light{font-weight:300!important}.fw-lighter{font-weight:lighter!important}.fw-normal{font-weight:400!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:#6c757d!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:.25rem!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:.2rem!important}.rounded-2{border-radius:.25rem!important}.rounded-3{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-end{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-start{border-bottom-left-radius:.25rem!important;border-top-left-radius:.25rem!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} -/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/common.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/common.js deleted file mode 100644 index 8dda3c8fe..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/common.js +++ /dev/null @@ -1,81 +0,0 @@ -// 自定义事件类 -class EventPlus { - constructor() { - this.event = new EventTarget(); - } - on(name, callback) { - this.event.addEventListener(name, e => callback(e.detail)); - } - send(name, data) { - this.event.dispatchEvent(new CustomEvent(name, { - detail: data, - bubbles: false, - cancelable: false - })); - } -} - -// 补零 -String.prototype.fill = function () { - return this >= 10 ? this : '0' + this; -}; - -// unicode编码转换字符串 -String.prototype.uTs = function () { - return eval('"' + Array.from(this).join('') + '"'); -}; - -// 字符串转换unicode编码 -String.prototype.sTu = function (str = '') { - Array.from(this).forEach(item => str += `\\u${item.charCodeAt(0).toString(16)}`); - return str; -}; - -// 全局变量/方法 -const $emit = new EventPlus(), $ = (selector, isAll = false) => { - const element = document.querySelector(selector), methods = { - on: function (event, callback) { - this.addEventListener(event, callback); - }, - attr: function (name, value = '') { - value && this.setAttribute(name, value); - return this; - } - }; - if (!isAll && element) { - return Object.assign(element, methods); - } else if (!isAll && !element) { - throw `HTML没有 ${selector} 元素! 请检查是否拼写错误`; - } - return Array.from(document.querySelectorAll(selector)).map(item => Object.assign(item, methods)); -}; - -// 节流函数 -$.throttle = (fn, delay) => { - let Timer = null; - return function () { - if (Timer) return; - Timer = setTimeout(() => { - fn.apply(this, arguments); - Timer = null; - }, delay); - }; -}; - -// 防抖函数 -$.debounce = (fn, delay) => { - let Timer = null; - return function () { - clearTimeout(Timer); - Timer = setTimeout(() => fn.apply(this, arguments), delay); - }; -}; - -// 绑定限制数字方法 -Array.from($('input[type="num"]', true)).forEach(item => { - item.addEventListener('input', function limitNum() { - if (!item.value || /^\d+$/.test(item.value)) return; - item.value = item.value.slice(0, -1); - limitNum(item); - }); -}); \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff deleted file mode 100644 index 1f5d54300..000000000 Binary files a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff and /dev/null differ diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff2 b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff2 deleted file mode 100644 index b3897eff0..000000000 Binary files a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff2 and /dev/null differ diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/streamingle_plugin b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/streamingle_plugin deleted file mode 100644 index 04440e927..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/streamingle_plugin +++ /dev/null @@ -1,9 +0,0 @@ -[2025-07-03T00:52:25.811] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 -[2025-07-03T00:58:26.107] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 -[2025-07-03T00:58:37.479] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 -[2025-07-03T00:58:50.582] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 -[2025-07-03T00:59:04.912] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 -[2025-07-03T01:00:19.924] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 -[2025-07-03T01:00:32.402] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 -[2025-07-03T01:01:32.400] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 -[2025-07-03T01:02:32.414] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/test.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/test.js deleted file mode 100644 index 0f6e2a5f3..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist-dev/com.mirabox.streamingle.sdPlugin/test.js +++ /dev/null @@ -1,65 +0,0 @@ -const StreaminglePlugin = require('./plugin.js'); - -console.log('=== Streamingle 플러그인 테스트 시작 ==='); - -const plugin = new StreaminglePlugin(); - -// 연결 상태 모니터링 -let connectionCheckInterval = setInterval(() => { - const status = plugin.getStatus(); - console.log(`📊 연결 상태: ${status.isConnected ? '✅ 연결됨' : '❌ 연결 안됨'}`); - - if (status.isConnected) { - console.log(`📷 카메라 개수: ${status.cameraCount}개`); - console.log(`🎯 현재 카메라: ${status.currentCamera >= 0 ? status.currentCamera : '없음'}`); - - if (status.cameraList && status.cameraList.length > 0) { - console.log('📋 카메라 목록:'); - status.cameraList.forEach((camera, index) => { - console.log(` ${index}: ${camera.name} ${camera.isActive ? '[활성]' : '[비활성]'}`); - }); - } - } - - console.log('---'); -}, 5000); - -// 3초 후 카메라 목록 요청 -setTimeout(() => { - console.log('🔍 카메라 목록 요청...'); - plugin.requestCameraList(); -}, 3000); - -// 8초 후 첫 번째 카메라로 전환 -setTimeout(() => { - console.log('🎬 첫 번째 카메라로 전환...'); - plugin.switchCamera(0); -}, 8000); - -// 13초 후 두 번째 카메라로 전환 -setTimeout(() => { - console.log('🎬 두 번째 카메라로 전환...'); - plugin.switchCamera(1); -}, 13000); - -// 18초 후 세 번째 카메라로 전환 (있다면) -setTimeout(() => { - console.log('🎬 세 번째 카메라로 전환...'); - plugin.switchCamera(2); -}, 18000); - -// 25초 후 종료 -setTimeout(() => { - console.log('🛑 테스트 종료...'); - clearInterval(connectionCheckInterval); - plugin.disconnect(); - process.exit(0); -}, 25000); - -// 프로세스 종료 시 정리 -process.on('SIGINT', () => { - console.log('🛑 테스트 중단...'); - clearInterval(connectionCheckInterval); - plugin.disconnect(); - process.exit(0); -}); \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/debug.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/debug.js deleted file mode 100644 index aca3e6b25..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/debug.js +++ /dev/null @@ -1,133 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -console.log('=== Streamingle 플러그인 디버깅 도구 ==='); - -// 1. 플러그인 파일 확인 -console.log('\n1. 플러그인 파일 확인:'); -const pluginDir = __dirname; -const requiredFiles = [ - 'manifest.json', - 'plugin.js', - 'package.json', - 'propertyinspector.html' -]; - -requiredFiles.forEach(file => { - const filePath = path.join(pluginDir, file); - if (fs.existsSync(filePath)) { - const stats = fs.statSync(filePath); - console.log(`✅ ${file} - ${stats.size} bytes`); - } else { - console.log(`❌ ${file} - 파일 없음`); - } -}); - -// 2. manifest.json 내용 확인 -console.log('\n2. manifest.json 내용:'); -try { - const manifestPath = path.join(pluginDir, 'manifest.json'); - const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')); - console.log('✅ manifest.json 파싱 성공'); - console.log(` SDK 버전: ${manifest.SDKVersion}`); - console.log(` 코드 경로: ${manifest.CodePath}`); - console.log(` 플러그인 이름: ${manifest.Name}`); - console.log(` 액션 UUID: ${manifest.Actions[0].UUID}`); -} catch (error) { - console.log(`❌ manifest.json 파싱 실패: ${error.message}`); -} - -// 3. package.json 내용 확인 -console.log('\n3. package.json 내용:'); -try { - const packagePath = path.join(pluginDir, 'package.json'); - const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8')); - console.log('✅ package.json 파싱 성공'); - console.log(` 이름: ${packageJson.name}`); - console.log(` 버전: ${packageJson.version}`); - console.log(` 메인 파일: ${packageJson.main}`); - console.log(` 의존성: ${Object.keys(packageJson.dependencies || {}).join(', ')}`); -} catch (error) { - console.log(`❌ package.json 파싱 실패: ${error.message}`); -} - -// 4. WebSocket 연결 테스트 -console.log('\n4. WebSocket 연결 테스트:'); -const WebSocket = require('ws'); - -function testWebSocketConnection() { - return new Promise((resolve) => { - console.log(' 연결 시도 중...'); - - const ws = new WebSocket('ws://127.0.0.1:10701/'); - - const timeout = setTimeout(() => { - console.log(' ❌ 연결 타임아웃 (5초)'); - ws.close(); - resolve(false); - }, 5000); - - ws.on('open', () => { - console.log(' ✅ WebSocket 연결 성공!'); - clearTimeout(timeout); - ws.close(); - resolve(true); - }); - - ws.on('error', (error) => { - console.log(` ❌ WebSocket 연결 실패: ${error.message}`); - clearTimeout(timeout); - resolve(false); - }); - - ws.on('close', (code, reason) => { - console.log(` 🔌 연결 종료 - 코드: ${code}, 이유: ${reason || '알 수 없음'}`); - }); - }); -} - -testWebSocketConnection().then((connected) => { - console.log(`\n결과: ${connected ? '✅ 연결 가능' : '❌ 연결 불가'}`); - - // 5. 포트 상태 확인 - console.log('\n5. 포트 상태 확인:'); - const { exec } = require('child_process'); - - exec('netstat -an | findstr :10701', (error, stdout, stderr) => { - if (error) { - console.log(` ❌ netstat 실행 실패: ${error.message}`); - return; - } - - if (stdout.trim()) { - console.log(' ✅ 포트 10701 사용 중:'); - stdout.split('\n').forEach(line => { - if (line.trim()) { - console.log(` ${line.trim()}`); - } - }); - } else { - console.log(' ❌ 포트 10701에서 서비스 없음'); - } - }); -}); - -// 6. StreamDock 플러그인 경로 확인 -console.log('\n6. StreamDock 플러그인 경로:'); -const streamDockPath = 'C:\\Users\\qscft\\AppData\\Roaming\\HotSpot\\StreamDock\\plugins\\com.mirabox.streamingle.sdPlugin'; -if (fs.existsSync(streamDockPath)) { - console.log(` ✅ StreamDock 플러그인 폴더 존재: ${streamDockPath}`); - - const files = fs.readdirSync(streamDockPath); - console.log(` 📁 파일 개수: ${files.length}개`); - files.forEach(file => { - const filePath = path.join(streamDockPath, file); - const stats = fs.statSync(filePath); - const type = stats.isDirectory() ? '📁' : '📄'; - console.log(` ${type} ${file} - ${stats.size} bytes`); - }); -} else { - console.log(` ❌ StreamDock 플러그인 폴더 없음: ${streamDockPath}`); -} - -console.log('\n=== 디버깅 완료 ==='); \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/images/action.png b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/images/action.png deleted file mode 100644 index 45b93e4ea..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/images/action.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:62da15b1333bcf15bb1ef33c05fb246406b76a8bf9710a4df6e7bd0a16ef62b5 -size 42151 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/manifest.json b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/manifest.json deleted file mode 100644 index fcc7d856a..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/manifest.json +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f646099cafd148b8c2b4c8dc1922ab6e98354df7194717b6d82812b05af66f43 -size 1353 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/plugin/index.html b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/plugin/index.html deleted file mode 100644 index 84e63f28a..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/plugin/index.html +++ /dev/null @@ -1,314 +0,0 @@ - - - - - Streamingle Plugin Main - - -
- 플러그인 메인 - 버튼 클릭 처리
- Property Inspector에서 설정 관리 -
- - - - \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/plugin/index.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/plugin/index.js deleted file mode 100644 index ee08eef3f..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/plugin/index.js +++ /dev/null @@ -1,228 +0,0 @@ -/** - * Streamingle Camera Controller Plugin for StreamDeck - * StreamDeck SDK 표준 구조로 수정 - */ - -const { Plugins, Actions, log } = require('./utils/plugin'); -const WebSocket = require('ws'); - -// 플러그인 인스턴스 생성 -const plugin = new Plugins('streamingle'); - -// Unity WebSocket 연결 관리 -let unityWebSocket = null; -let reconnectTimer = null; -let isUnityConnected = false; -let cameraData = null; -let currentCamera = 0; - -// 로그 함수 -function writeLog(message) { - const timestamp = new Date().toISOString(); - log.info(`[${timestamp}] ${message}`); -} - -// Unity WebSocket 서버 연결 함수 -function connectToUnity() { - if (unityWebSocket) { - unityWebSocket.close(); - } - - writeLog('🔌 Unity WebSocket 연결 시도: ws://localhost:10701'); - unityWebSocket = new WebSocket('ws://localhost:10701'); - - unityWebSocket.on('open', function() { - writeLog('✅ Unity WebSocket 서버에 연결됨'); - isUnityConnected = true; - requestCameraList(); - }); - - unityWebSocket.on('message', function(data) { - try { - writeLog('📨 Unity 메시지 수신: ' + data.toString()); - const message = JSON.parse(data.toString()); - handleUnityMessage(message); - } catch (error) { - writeLog('❌ Unity 메시지 파싱 오류: ' + error.message); - } - }); - - unityWebSocket.on('error', function(err) { - writeLog('❌ Unity WebSocket 연결 오류: ' + err.message); - isUnityConnected = false; - scheduleReconnect(); - }); - - unityWebSocket.on('close', function() { - writeLog('🔌 Unity WebSocket 연결 종료'); - isUnityConnected = false; - scheduleReconnect(); - }); -} - -// Unity 메시지 처리 -function handleUnityMessage(message) { - switch (message.type) { - case 'cameraList': - cameraData = message; - currentCamera = message.currentCamera || 0; - writeLog('📹 카메라 목록 수신: ' + JSON.stringify(message)); - break; - case 'cameraSwitched': - currentCamera = message.cameraIndex; - writeLog('📹 카메라 전환 완료: ' + message.cameraIndex); - break; - default: - writeLog('📨 알 수 없는 Unity 메시지: ' + message.type); - } -} - -// 카메라 목록 요청 -function requestCameraList() { - if (!isUnityConnected || !unityWebSocket) { - writeLog('❌ Unity 연결 안됨 - 카메라 목록 요청 불가'); - return; - } - - const message = { - type: 'getCameraList', - requestId: Date.now().toString() - }; - - writeLog('📤 Unity에 카메라 목록 요청: ' + JSON.stringify(message)); - unityWebSocket.send(JSON.stringify(message)); -} - -// 카메라 전환 -function switchCamera(cameraIndex) { - if (!isUnityConnected || !unityWebSocket) { - writeLog('❌ Unity 연결 안됨 - 카메라 전환 불가'); - return false; - } - - const message = { - type: 'switchCamera', - cameraIndex: cameraIndex - }; - - writeLog('📤 Unity에 카메라 전환 요청: ' + JSON.stringify(message)); - unityWebSocket.send(JSON.stringify(message)); - return true; -} - -// 재연결 스케줄링 -function scheduleReconnect() { - if (reconnectTimer) { - clearTimeout(reconnectTimer); - } - - writeLog('🔄 3초 후 Unity 재연결 시도'); - reconnectTimer = setTimeout(() => { - reconnectTimer = null; - connectToUnity(); - }, 3000); -} - -// 플러그인 설정 수신 시 Unity 연결 -plugin.didReceiveGlobalSettings = ({ payload: { settings } }) => { - log.info('didReceiveGlobalSettings', settings); - writeLog('플러그인 설정 수신됨'); - - // 플러그인 시작 시 Unity 연결 - connectToUnity(); -}; - -// Streamingle 액션 클래스 -plugin.streamingle = new Actions({ - default: { - cameraIndex: 0, - autoSwitch: true, - isUnityConnected: false, - cameraList: [], - currentCamera: null - }, - - // 버튼이 나타날 때 - async _willAppear({ context, payload }) { - writeLog(`🎯 버튼 나타남: ${context.substring(0, 8)}...`); - - // Unity 연결 시도 (첫 번째 버튼에서만) - if (!unityWebSocket) { - writeLog('🚀 첫 번째 버튼 - Unity 연결 시도'); - connectToUnity(); - } - }, - - // 버튼이 사라질 때 - _willDisappear({ context }) { - writeLog(`🎯 버튼 사라짐: ${context.substring(0, 8)}...`); - }, - - // Property Inspector가 열릴 때 - _propertyInspectorDidAppear({ context }) { - writeLog(`📋 Property Inspector 열림: ${context.substring(0, 8)}...`); - - // Unity 연결 강제 확인 - if (!isUnityConnected) { - writeLog('🔄 Unity 연결 시도 중...'); - connectToUnity(); - } - }, - - // Property Inspector에서 메시지 수신 - sendToPlugin({ payload, context }) { - writeLog(`📨 Property Inspector 메시지 수신: ${context.substring(0, 8)}...`); - - try { - const message = JSON.parse(payload); - writeLog('📨 파싱된 메시지: ' + JSON.stringify(message)); - - switch (message.command) { - case 'refreshCameraList': - requestCameraList(); - break; - case 'forceReconnect': - connectToUnity(); - break; - case 'getInitialSettings': - writeLog('📥 Property Inspector에서 초기 설정 요청'); - plugin.sendToPropertyInspector({ - type: 'connection_status', - connected: isUnityConnected - }); - - if (isUnityConnected && cameraData) { - plugin.sendToPropertyInspector({ - type: 'camera_data', - camera_data: cameraData, - current_camera: currentCamera - }); - } - break; - default: - writeLog('❓ 알 수 없는 Property Inspector 명령: ' + message.command); - } - } catch (error) { - writeLog('❌ Property Inspector 메시지 파싱 오류: ' + error.message); - } - }, - - // 버튼 클릭 시 - keyUp({ context, payload }) { - writeLog(`🎯 버튼 클릭: ${context.substring(0, 8)}...`); - - // 설정에서 카메라 인덱스 가져오기 - const settings = this.data[context] || {}; - const cameraIndex = settings.cameraIndex || 0; - - writeLog(`📹 카메라 전환 시도: ${cameraIndex}`); - - if (switchCamera(cameraIndex)) { - writeLog(`✅ 카메라 전환 요청 완료: ${cameraIndex}`); - } else { - writeLog(`❌ 카메라 전환 요청 실패: ${cameraIndex}`); - } - } -}); - -writeLog('🚀 Streamingle 플러그인 초기화 완료'); diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/plugin/log/streamingle_plugin b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/plugin/log/streamingle_plugin deleted file mode 100644 index e69de29bb..000000000 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/plugin/utils/plugin.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/plugin/utils/plugin.js deleted file mode 100644 index 60eac6753..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/plugin/utils/plugin.js +++ /dev/null @@ -1,223 +0,0 @@ -// 配置日志文件 -const now = new Date(); -const log = require('log4js').configure({ - appenders: { - file: { type: 'file', filename: `./log/${now.getFullYear()}.${now.getMonth() + 1}.${now.getDate()}.log` } - }, - categories: { - default: { appenders: ['file'], level: 'info' } - } -}).getLogger(); - -//################################################## -//##################全局异常捕获##################### -process.on('uncaughtException', (error) => { - log.error('Uncaught Exception:', error); -}); -process.on('unhandledRejection', (reason) => { - log.error('Unhandled Rejection:', reason); -}); -//################################################## -//################################################## - - -// 插件类 -const ws = require('ws'); -class Plugins { - static language = (() => { - try { - if (process.argv[9] && process.argv[9] !== 'undefined') { - const parsed = JSON.parse(process.argv[9]); - return parsed.application?.language || 'en'; - } - return 'en'; - } catch (error) { - return 'en'; - } - })(); - static globalSettings = {}; - getGlobalSettingsFlag = true; - constructor() { - if (Plugins.instance) { - return Plugins.instance; - } - // log.info("process.argv", process.argv); - this.ws = new ws("ws://127.0.0.1:" + process.argv[3]); - this.ws.on('open', () => this.ws.send(JSON.stringify({ uuid: process.argv[5], event: process.argv[7] }))); - this.ws.on('close', process.exit); - this.ws.on('message', e => { - if (this.getGlobalSettingsFlag) { - // 只获取一次 - this.getGlobalSettingsFlag = false; - this.getGlobalSettings(); - } - const data = JSON.parse(e.toString()); - const action = data.action?.split('.').pop(); - this[action]?.[data.event]?.(data); - if (data.event === 'didReceiveGlobalSettings') { - Plugins.globalSettings = data.payload.settings; - } - this[data.event]?.(data); - }); - Plugins.instance = this; - } - - setGlobalSettings(payload) { - Plugins.globalSettings = payload; - this.ws.send(JSON.stringify({ - event: "setGlobalSettings", - context: process.argv[5], payload - })); - } - - getGlobalSettings() { - this.ws.send(JSON.stringify({ - event: "getGlobalSettings", - context: process.argv[5], - })); - } - // 设置标题 - setTitle(context, str, row = 0, num = 6) { - let newStr = null; - if (row && str) { - let nowRow = 1, strArr = str.split(''); - strArr.forEach((item, index) => { - if (nowRow < row && index >= nowRow * num) { nowRow++; newStr += '\n'; } - if (nowRow <= row && index < nowRow * num) { newStr += item; } - }); - if (strArr.length > row * num) { newStr = newStr.substring(0, newStr.length - 1); newStr += '..'; } - } - this.ws.send(JSON.stringify({ - event: "setTitle", - context, payload: { - target: 0, - title: newStr || str + '' - } - })); - } - // 设置背景 - setImage(context, url) { - this.ws.send(JSON.stringify({ - event: "setImage", - context, payload: { - target: 0, - image: url - } - })); - } - // 设置状态 - setState(context, state) { - this.ws.send(JSON.stringify({ - event: "setState", - context, payload: { state } - })); - } - // 保存持久化数据 - setSettings(context, payload) { - this.ws.send(JSON.stringify({ - event: "setSettings", - context, payload - })); - } - - // 在按键上展示警告 - showAlert(context) { - this.ws.send(JSON.stringify({ - event: "showAlert", - context - })); - } - - // 在按键上展示成功 - showOk(context) { - this.ws.send(JSON.stringify({ - event: "showOk", - context - })); - } - // 发送给属性检测器 - sendToPropertyInspector(payload) { - this.ws.send(JSON.stringify({ - action: Actions.currentAction, - context: Actions.currentContext, - payload, event: "sendToPropertyInspector" - })); - } - // 用默认浏览器打开网页 - openUrl(url) { - this.ws.send(JSON.stringify({ - event: "openUrl", - payload: { url } - })); - } -}; - -// 操作类 -class Actions { - constructor(data) { - this.data = {}; - this.default = {}; - Object.assign(this, data); - } - // 属性检查器显示时 - static currentAction = null; - static currentContext = null; - static actions = {}; - propertyInspectorDidAppear(data) { - Actions.currentAction = data.action; - Actions.currentContext = data.context; - this._propertyInspectorDidAppear?.(data); - } - // 初始化数据 - willAppear(data) { - Plugins.globalContext = data.context; - Actions.actions[data.context] = data.action - const { context, payload: { settings } } = data; - this.data[context] = Object.assign({ ...this.default }, settings); - this._willAppear?.(data); - } - - didReceiveSettings(data) { - this.data[data.context] = data.payload.settings; - this._didReceiveSettings?.(data); - } - // 行动销毁 - willDisappear(data) { - this._willDisappear?.(data); - delete this.data[data.context]; - } -} - -class EventEmitter { - constructor() { - this.events = {}; - } - - // 订阅事件 - subscribe(event, listener) { - if (!this.events[event]) { - this.events[event] = []; - } - this.events[event].push(listener); - } - - // 取消订阅 - unsubscribe(event, listenerToRemove) { - if (!this.events[event]) return; - - this.events[event] = this.events[event].filter(listener => listener !== listenerToRemove); - } - - // 发布事件 - emit(event, data) { - if (!this.events[event]) return; - this.events[event].forEach(listener => listener(data)); - } -} - -module.exports = { - log, - Plugins, - Actions, - EventEmitter -}; \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/camera/index.html b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/camera/index.html deleted file mode 100644 index 0e2f21618..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/camera/index.html +++ /dev/null @@ -1,187 +0,0 @@ - - - - - Streamingle Camera Inspector - - - - -
-
- Unity 연결 안됨 -
- - -
- -
- -
-
현재 카메라: -
-
- - -
- - -
- - -
- -
- - -
- -
-
- - - - - \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/camera/index.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/camera/index.js deleted file mode 100644 index 74a974510..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/camera/index.js +++ /dev/null @@ -1,778 +0,0 @@ -/** - * Streamingle Camera Controller - Property Inspector - * 엘가토 공식 구조 기반 단순화 버전 - */ - -// Global variables -let websocket = null; -let uuid = null; -let actionContext = null; // 현재 액션의 컨텍스트 -let settings = {}; - -// Context별 설정 관리 -const contextSettings = new Map(); -let currentActionContext = null; - -// Unity 연결 상태 (Plugin Main에서 받아옴) -let isUnityConnected = false; -let cameraData = []; -let currentCamera = 0; - -// DOM elements -let statusDot = null; -let connectionStatus = null; -let cameraSelect = null; -let currentCameraDisplay = null; -let refreshButton = null; - -// 화면에 로그를 표시하는 함수 -function logToScreen(msg, color = "#fff") { - let logDiv = document.getElementById('logArea'); - if (!logDiv) { - logDiv = document.createElement('div'); - logDiv.id = 'logArea'; - logDiv.style.background = '#111'; - logDiv.style.color = '#fff'; - logDiv.style.fontSize = '11px'; - logDiv.style.padding = '8px'; - logDiv.style.marginTop = '16px'; - logDiv.style.height = '120px'; - logDiv.style.overflowY = 'auto'; - document.body.appendChild(logDiv); - } - const line = document.createElement('div'); - line.style.color = color; - line.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`; - logDiv.appendChild(line); - logDiv.scrollTop = logDiv.scrollHeight; -} - -// 기존 console.log/console.error를 화면에도 출력 -const origLog = console.log; -console.log = function(...args) { - origLog.apply(console, args); - logToScreen(args.map(a => (typeof a === 'object' ? JSON.stringify(a) : a)).join(' '), '#0f0'); -}; -const origErr = console.error; -console.error = function(...args) { - origErr.apply(console, args); - logToScreen(args.map(a => (typeof a === 'object' ? JSON.stringify(a) : a)).join(' '), '#f55'); -}; - -console.log('🔧 Property Inspector script loaded'); - -// Initialize when DOM is loaded -document.addEventListener('DOMContentLoaded', function() { - console.log('📋 Property Inspector 초기화'); - initializePropertyInspector(); -}); - -// Initialize Property Inspector -function initializePropertyInspector() { - // Get DOM elements - statusDot = document.getElementById('statusDot'); - connectionStatus = document.getElementById('connection-status'); - cameraSelect = document.getElementById('camera-select'); - currentCameraDisplay = document.getElementById('current-camera'); - refreshButton = document.getElementById('refresh-button'); - - // Setup event listeners - if (cameraSelect) { - cameraSelect.addEventListener('change', onCameraSelectionChanged); - } - - if (refreshButton) { - refreshButton.addEventListener('click', onRefreshClicked); - } - - console.log('✅ Property Inspector 준비 완료'); -} - -// Send message to plugin -function sendToPlugin(command, data = {}) { - if (!websocket) { - console.error('❌ WebSocket not available'); - return; - } - - try { - const message = { - command: command, - context: uuid, - ...data - }; - - // StreamDeck SDK 표준 방식 - sendToPlugin 이벤트 사용 - const payload = { - event: 'sendToPlugin', - context: uuid, - payload: message - }; - - websocket.send(JSON.stringify(payload)); - console.log('📤 Message sent to plugin:', command, data); - } catch (error) { - console.error('❌ Failed to send message to plugin:', error); - } -} - -// Update connection status display -function updateConnectionStatus(isConnected) { - console.log('🔄 Connection status update:', isConnected); - - // 전역 변수도 업데이트 - isUnityConnected = isConnected; - - if (statusDot) { - statusDot.className = `dot ${isConnected ? 'green' : 'red'}`; - } - - if (connectionStatus) { - connectionStatus.textContent = isConnected ? 'Unity 연결됨' : 'Unity 연결 안됨'; - connectionStatus.className = isConnected ? 'connected' : 'disconnected'; - } - - if (cameraSelect) { - cameraSelect.disabled = !isConnected; - } - - if (refreshButton) { - refreshButton.disabled = !isConnected; - } -} - -// Update camera data display -function updateCameraData(cameraDataParam, currentCamera) { - console.log('📹 Camera data update:', cameraDataParam, currentCamera); - - if (cameraSelect && cameraDataParam) { - // Clear existing options - cameraSelect.innerHTML = ''; - - // cameraDataParam이 직접 배열인지 확인 - let cameras = cameraDataParam; - if (cameraDataParam.cameras) { - cameras = cameraDataParam.cameras; - } else if (Array.isArray(cameraDataParam)) { - cameras = cameraDataParam; - } - - console.log('📹 처리할 카메라 배열:', cameras); - - if (cameras && cameras.length > 0) { - // 전역 변수에 카메라 데이터 저장 - cameraData = cameras; - console.log('💾 전역 cameraData 저장됨:', cameraData.length + '개'); - - // Add camera options - cameras.forEach((camera, index) => { - const option = document.createElement('option'); - option.value = index; - option.textContent = `카메라 ${index + 1}`; - if (camera.name) { - option.textContent += ` (${camera.name})`; - } - cameraSelect.appendChild(option); - }); - - // Set current selection - if (typeof currentCamera === 'number') { - cameraSelect.value = currentCamera; - } - - cameraSelect.disabled = false; - console.log('✅ 카메라 목록 업데이트 완료:', cameras.length + '개'); - } else { - console.log('⚠️ 카메라 데이터가 없거나 빈 배열'); - cameraSelect.disabled = true; - } - } - - // Update current camera display - updateCurrentCameraDisplay(currentCamera); -} - -// Update current camera display -function updateCurrentCameraDisplay(currentCamera) { - if (currentCameraDisplay) { - if (typeof currentCamera === 'number') { - currentCameraDisplay.textContent = `현재 카메라: ${currentCamera + 1}`; - } else { - currentCameraDisplay.textContent = '현재 카메라: -'; - } - } -} - -// Handle camera selection change -function onCameraSelectionChanged() { - if (!cameraSelect || !currentActionContext) return; - - const selectedIndex = parseInt(cameraSelect.value, 10); - if (isNaN(selectedIndex)) return; - - console.log('🎯 카메라 선택 변경:', selectedIndex); - console.log('📋 현재 cameraData:', cameraData); - console.log('📋 cameraData 길이:', cameraData ? cameraData.length : 'undefined'); - - // StreamDeck에 설정 저장 (Plugin Main에서 didReceiveSettings 이벤트 발생) - if (websocket) { - const setSettingsMessage = { - event: 'setSettings', - context: currentActionContext, - payload: { - cameraIndex: selectedIndex, - cameraList: cameraData // 현재 카메라 목록 포함 - } - }; - - console.log('📤 Plugin Main으로 전송할 데이터:', setSettingsMessage.payload); - websocket.send(JSON.stringify(setSettingsMessage)); - console.log('💾 설정 저장됨 - Plugin Main에서 버튼 제목 업데이트됨'); - } - - // UI 업데이트 - updateCurrentCameraDisplay(selectedIndex); -} - -// Handle refresh button click -function onRefreshClicked() { - console.log('🔄 새로고침 버튼 클릭 - Plugin Main에 카메라 목록 요청'); - - // Plugin Main에 카메라 목록 요청 - sendToPlugin('requestCameraList'); -} - -// Unity 연결은 Plugin Main에서만 처리 -function startUnityAutoReconnect() { - console.log('🩺 Property Inspector에서는 Unity 연결을 직접 관리하지 않음 - Plugin Main에서 처리됨'); -} - -// Unity 재연결 시도 (제거) -function attemptUnityReconnect() { - if (isShuttingDown || isConnecting || unityReconnectInterval) return; - - unityConnectionAttempts++; - - // 재연결 간격 조정 - let delay; - if (unityConnectionAttempts <= 3) { - delay = 2000; // 처음 3번은 2초 간격 - } else if (unityConnectionAttempts <= 10) { - delay = 5000; // 4-10번은 5초 간격 - } else { - delay = 30000; // 그 이후는 30초 간격 - } - - console.log(`🔄 [Property Inspector] ${delay/1000}초 후 Unity 재연결 시도... (${unityConnectionAttempts}번째 시도)`); - - unityReconnectInterval = setTimeout(() => { - unityReconnectInterval = null; - connectToUnity().catch(error => { - console.error(`❌ [Property Inspector] Unity 재연결 실패:`, error); - // 실패해도 계속 시도 - if (!isShuttingDown) { - attemptUnityReconnect(); - } - }); - }, delay); -} - -// Unity WebSocket 연결 (개선된 버전) -function connectToUnity() { - return new Promise((resolve, reject) => { - // 글로벌 상태 확인 - if (window.sharedUnityConnected && window.sharedUnitySocket) { - console.log('✅ [Property Inspector] 기존 Unity 연결 재사용'); - isUnityConnected = true; - unitySocket = window.sharedUnitySocket; - updateConnectionStatus(true); - resolve(); - return; - } - - if (isUnityConnected) { - console.log('✅ [Property Inspector] Unity 이미 연결됨'); - resolve(); - return; - } - - if (isConnecting) { - console.log('⏳ [Property Inspector] Unity 연결 중... 대기'); - reject(new Error('이미 연결 중')); - return; - } - - isConnecting = true; - window.sharedIsConnecting = true; - console.log(`🔌 [Property Inspector] Unity 연결 시도... (시도 ${unityConnectionAttempts + 1}회)`); - - try { - unitySocket = new WebSocket('ws://localhost:10701'); - - const connectionTimeout = setTimeout(() => { - isConnecting = false; - window.sharedIsConnecting = false; - console.log('⏰ [Property Inspector] Unity 연결 타임아웃'); - if (unitySocket) { - unitySocket.close(); - } - reject(new Error('연결 타임아웃')); - }, 5000); - - unitySocket.onopen = function() { - clearTimeout(connectionTimeout); - isConnecting = false; - isUnityConnected = true; - unityConnectionAttempts = 0; // 성공 시 재시도 카운터 리셋 - - // 재연결 타이머 정리 - if (unityReconnectInterval) { - clearTimeout(unityReconnectInterval); - unityReconnectInterval = null; - } - - // 글로벌 상태 저장 - window.sharedUnitySocket = unitySocket; - window.sharedUnityConnected = true; - window.sharedIsConnecting = false; - - console.log('✅ [Property Inspector] Unity 연결 성공!'); - updateConnectionStatus(true); - resolve(); - }; - - unitySocket.onmessage = function(event) { - try { - const message = JSON.parse(event.data); - handleUnityMessage(message); - } catch (error) { - console.error('❌ [Property Inspector] Unity 메시지 파싱 오류:', error); - } - }; - - unitySocket.onclose = function(event) { - clearTimeout(connectionTimeout); - const wasConnected = isUnityConnected; - isConnecting = false; - isUnityConnected = false; - - // 글로벌 상태 정리 - window.sharedUnitySocket = null; - window.sharedUnityConnected = false; - window.sharedIsConnecting = false; - - if (wasConnected) { - console.log(`❌ [Property Inspector] Unity 연결 끊어짐 (코드: ${event.code}, 이유: ${event.reason || '알 수 없음'})`); - } - updateConnectionStatus(false); - unitySocket = null; - - if (!event.wasClean) { - reject(new Error('연결 실패')); - } - - // 자동 재연결 시도 - if (!isShuttingDown) { - attemptUnityReconnect(); - } - }; - - unitySocket.onerror = function(error) { - clearTimeout(connectionTimeout); - isConnecting = false; - window.sharedIsConnecting = false; - console.error('❌ [Property Inspector] Unity 연결 오류:', error); - isUnityConnected = false; - updateConnectionStatus(false); - reject(error); - }; - - } catch (error) { - isConnecting = false; - window.sharedIsConnecting = false; - console.error('❌ [Property Inspector] Unity WebSocket 생성 실패:', error); - reject(error); - } - }); -} - -// Unity 메시지 처리 -function handleUnityMessage(message) { - const messageType = message.type; - - if (messageType === 'connection_established') { - console.log('🎉 Unity 연결 확인됨'); - if (message.data && message.data.camera_data) { - console.log('📹 연결 시 카메라 데이터 수신 (초기 로드)'); - updateCameraUI(message.data.camera_data.presets, message.data.camera_data.current_index); - // 이미 카메라 데이터를 받았으므로 추가 요청하지 않음 - cameraData = message.data.camera_data.presets; // 글로벌 변수에 저장 - window.sharedCameraData = cameraData; // 브라우저 세션에서 공유 - } - } else if (messageType === 'camera_list_response') { - console.log('📹 카메라 목록 응답 수신 (요청에 대한 응답)'); - if (message.data && message.data.camera_data) { - updateCameraUI(message.data.camera_data.presets, message.data.camera_data.current_index); - cameraData = message.data.camera_data.presets; // 글로벌 변수에 저장 - window.sharedCameraData = cameraData; // 브라우저 세션에서 공유 - } - } else if (messageType === 'camera_changed') { - console.log('📹 카메라 변경 알림 수신'); - if (message.data && typeof message.data.camera_index === 'number') { - updateCurrentCamera(message.data.camera_index); - } - } -} - -function updateCameraUI(cameras, currentIndex) { - if (!cameras || !Array.isArray(cameras)) { - console.error('❌ 잘못된 카메라 데이터'); - return; - } - - console.log('📹 카메라 UI 업데이트:', cameras.length + '개'); - - const cameraSelect = document.getElementById('camera-select'); - const currentCameraDisplay = document.getElementById('current-camera'); - - if (!cameraSelect) { - console.error('❌ camera-select 요소를 찾을 수 없음'); - return; - } - - // 카메라 목록 업데이트 - cameraSelect.innerHTML = ''; - - cameras.forEach((camera, index) => { - const option = document.createElement('option'); - option.value = index; - option.textContent = `${index + 1}. ${camera.name}`; - cameraSelect.appendChild(option); - }); - - // 현재 컨텍스트의 설정 가져오기 - const currentSettings = getContextSettings(currentActionContext); - - // 카메라 목록 저장 - const newSettings = { - ...currentSettings, - cameraList: cameras - }; - saveContextSettings(currentActionContext, newSettings); - - // 이미 설정된 카메라가 있으면 선택 - if (currentSettings && typeof currentSettings.cameraIndex === 'number') { - cameraSelect.value = currentSettings.cameraIndex; - - // 설정된 카메라의 이름으로 현재 표시만 업데이트 (버튼 제목은 Plugin Main에서 처리) - const selectedCamera = cameras[currentSettings.cameraIndex]; - if (selectedCamera) { - currentCameraDisplay.textContent = `현재: ${selectedCamera.name}`; - console.log('📋 기존 카메라 설정 복원:', selectedCamera.name); - } - } else { - // 설정이 없으면 Unity의 현재 카메라 사용 - if (typeof currentIndex === 'number' && currentIndex >= 0 && cameras[currentIndex]) { - cameraSelect.value = currentIndex; - currentCameraDisplay.textContent = `현재: ${cameras[currentIndex].name}`; - } else { - currentCameraDisplay.textContent = '현재: 없음'; - } - } - - console.log('✅ 카메라 UI 업데이트 완료'); -} - -// Unity에서 카메라 목록 요청 -function requestCameraListFromUnity() { - if (isUnityConnected && unitySocket) { - const message = JSON.stringify({ type: 'get_camera_list' }); - unitySocket.send(message); - console.log('📤 Unity에 카메라 목록 요청:', message); - } -} - -// Unity에서 카메라 전환 -function switchCameraInUnity(cameraIndex) { - if (isUnityConnected && unitySocket) { - const message = JSON.stringify({ - type: 'switch_camera', - data: { - camera_index: cameraIndex - } - }); - unitySocket.send(message); - console.log('📤 Unity에 카메라 전환 요청:', message); - } -} - -// Unity에서 받은 카메라 데이터로 UI 업데이트 -function updateCameraDataFromUnity() { - console.log('📹 Unity 카메라 데이터로 UI 업데이트:', cameraData); - - if (cameraSelect && cameraData && cameraData.length > 0) { - // Clear existing options - cameraSelect.innerHTML = ''; - - // Add camera options - cameraData.forEach((camera, index) => { - const option = document.createElement('option'); - option.value = index; - option.textContent = camera.name || `카메라 ${index + 1}`; - cameraSelect.appendChild(option); - }); - - // Set current selection - cameraSelect.value = currentCamera; - cameraSelect.disabled = false; - - console.log('✅ 카메라 선택 목록 업데이트 완료'); - } - - // Update current camera display - updateCurrentCameraDisplay(currentCamera); -} - -// Handle messages from plugin -function handleMessage(jsonObj) { - console.log('📨 Message received:', jsonObj); - - try { - if (jsonObj.event === 'sendToPropertyInspector') { - // payload는 직접 객체로 전달됨 - const payload = jsonObj.payload; - - console.log('📋 Parsed payload:', payload); - - switch (payload.type || payload.command) { - case 'connection_status': - console.log('📡 연결 상태 업데이트:', payload.connected); - updateConnectionStatus(payload.connected); - break; - - case 'camera_data': - console.log('📹 카메라 데이터 업데이트 수신:', payload); - console.log('📹 카메라 데이터 상세:', payload.camera_data); - updateCameraData(payload.camera_data, payload.current_camera); - break; - - case 'current_settings': - console.log('⚙️ 현재 설정 수신:', payload); - // 현재 설정을 컨텍스트에 저장 - if (currentActionContext) { - const newSettings = { - cameraIndex: payload.cameraIndex || 0, - cameraList: payload.cameraList || [], - isUnityConnected: payload.isUnityConnected || false - }; - saveContextSettings(currentActionContext, newSettings); - - // UI 업데이트 - updateConnectionStatus(payload.isUnityConnected); - if (payload.cameraList && payload.cameraList.length > 0) { - updateCameraData({ cameras: payload.cameraList }, payload.cameraIndex); - } - } - break; - - case 'camera_changed': - console.log('📹 카메라 변경:', payload.current_camera); - updateCurrentCameraDisplay(payload.current_camera); - break; - - default: - console.log('❓ Unknown message type:', payload.type || payload.command); - } - } else if (jsonObj.event === 'didReceiveSettings') { - // didReceiveSettings 이벤트 처리 추가 - console.log('⚙️ didReceiveSettings 이벤트 수신:', jsonObj); - - const settings = jsonObj.payload.settings || {}; - console.log('📋 설정 데이터:', settings); - - // 컨텍스트에 설정 저장 - if (currentActionContext) { - saveContextSettings(currentActionContext, settings); - - // UI 업데이트 - if (settings.cameraList && settings.cameraList.length > 0) { - console.log('📹 카메라 목록으로 UI 업데이트:', settings.cameraList.length + '개'); - updateCameraData(settings.cameraList, settings.cameraIndex || 0); - - // 카메라 목록이 있다면 Unity가 연결되어 있다고 판단 - console.log('🔍 카메라 목록 존재 - Unity 연결됨으로 판단'); - updateConnectionStatus(true); - } - - // 연결 상태 업데이트 (카메라 목록이 없을 때만 설정값 사용) - if (typeof settings.isUnityConnected === 'boolean' && (!settings.cameraList || settings.cameraList.length === 0)) { - updateConnectionStatus(settings.isUnityConnected); - } - } - } - } catch (error) { - console.error('❌ Failed to handle message:', error); - } -} - -// StreamDeck SDK connection -function connectElgatoStreamDeckSocket(inPort, inPropertyInspectorUUID, inRegisterEvent, inInfo, inActionInfo) { - uuid = inPropertyInspectorUUID; - - console.log('🔌 StreamDeck 연결 중...'); - - // Parse info - try { - const info = JSON.parse(inInfo); - const actionInfo = JSON.parse(inActionInfo); - actionContext = actionInfo.context; // 액션 컨텍스트 저장 - currentActionContext = actionInfo.context; // 현재 액션 컨텍스트 설정 - settings = actionInfo.payload.settings || {}; - - // 컨텍스트별 설정 초기화 - saveContextSettings(currentActionContext, settings); - - console.log('📋 컨텍스트 설정 완료:', currentActionContext); - } catch (error) { - console.error('❌ 정보 파싱 실패:', error); - } - - // Connect to StreamDock - websocket = new WebSocket('ws://127.0.0.1:' + inPort); - - websocket.onopen = function() { - console.log('✅ StreamDeck 연결됨'); - - // Register - const json = { - event: inRegisterEvent, - uuid: uuid - }; - - websocket.send(JSON.stringify(json)); - - // Plugin Main에 초기 설정 요청 - console.log('📤 Plugin Main에 초기 설정 요청'); - sendToPlugin('getInitialSettings'); - }; - - websocket.onmessage = function(evt) { - try { - const jsonObj = JSON.parse(evt.data); - handleMessage(jsonObj); - } catch (error) { - console.error('❌ Failed to parse message:', error); - } - }; - - websocket.onclose = function() { - console.log('❌ StreamDeck WebSocket closed'); - isShuttingDown = true; // 종료 플래그 설정 - - // Unity 자동 재연결 정리 - if (unityReconnectInterval) { - clearTimeout(unityReconnectInterval); - unityReconnectInterval = null; - } - if (unityHealthCheckInterval) { - clearInterval(unityHealthCheckInterval); - unityHealthCheckInterval = null; - } - - websocket = null; - }; - - websocket.onerror = function(error) { - console.error('❌ StreamDeck WebSocket error:', error); - }; -} - -// 컨텍스트별 설정 관리 함수들 -function getContextSettings(context) { - if (!context) return {}; - return contextSettings.get(context) || {}; -} - -function saveContextSettings(context, newSettings) { - if (!context) return; - contextSettings.set(context, { ...newSettings }); - console.log('💾 컨텍스트 설정 저장:', context, newSettings); -} - -// 현재 컨텍스트의 설정에서 카메라 이름을 가져오는 공통 함수 -function getCurrentCameraName(context, cameraIndex = null) { - if (!context) return '카메라\n선택'; - - const settings = getContextSettings(context); - if (!settings || !settings.cameraList) return '카메라\n선택'; - - // cameraIndex가 제공되면 그것을 사용, 아니면 설정에서 가져옴 - const index = cameraIndex !== null ? cameraIndex : settings.cameraIndex; - if (typeof index !== 'number' || !settings.cameraList[index]) return '카메라\n선택'; - - return settings.cameraList[index].name || '카메라\n선택'; -} - -// 버튼 제목 업데이트 공통 함수 (Plugin Main과 동일한 로직) -function updateButtonTitle(context, cameraName = null, cameraIndex = null) { - if (!websocket || !context) return; - - // cameraName이 제공되지 않으면 현재 설정에서 가져옴 - if (!cameraName) { - cameraName = getCurrentCameraName(context, cameraIndex); - console.log(`🔍 [Property Inspector] getCurrentCameraName 결과: "${cameraName}"`); - - // 디버깅을 위해 현재 설정 상태도 출력 - const settings = getContextSettings(context); - console.log(`🔍 [Property Inspector] 컨텍스트 설정:`, settings); - console.log(`🔍 [Property Inspector] 카메라 인덱스: ${cameraIndex !== null ? cameraIndex : settings.cameraIndex}, 목록 길이: ${settings.cameraList ? settings.cameraList.length : 0}`); - } else { - console.log(`🔍 [Property Inspector] 직접 제공된 카메라 이름: "${cameraName}"`); - } - - // 기본값 설정 - let title = cameraName || '카메라\n선택'; - console.log(`🔍 [Property Inspector] 최종 사용할 제목 (가공 전): "${title}"`); - - // 긴 텍스트를 두 줄로 나누기 (Plugin Main과 동일한 로직) - if (title.length > 8) { - const underscoreIndex = title.indexOf('_'); - - if (underscoreIndex !== -1 && underscoreIndex > 0) { - // 언더스코어가 있으면 그 위치에서 분할하고 언더스코어는 제거 - const firstLine = title.substring(0, underscoreIndex); - const secondLine = title.substring(underscoreIndex + 1); // +1로 언더스코어 제거 - - // 각 줄이 너무 길면 적절히 자르기 - const maxLineLength = 8; - let line1 = firstLine.length > maxLineLength ? firstLine.substring(0, maxLineLength - 1) + '.' : firstLine; - let line2 = secondLine.length > maxLineLength ? secondLine.substring(0, maxLineLength - 1) + '.' : secondLine; - - title = line1 + '\n' + line2; - } else { - // 언더스코어가 없으면 중간 지점에서 분할 - const midPoint = Math.ceil(title.length / 2); - const firstLine = title.substring(0, midPoint); - const secondLine = title.substring(midPoint); - title = firstLine + '\n' + secondLine; - } - } - - // 버튼 제목 설정 (Plugin Main과 완전히 동일한 매개변수) - const message = { - event: 'setTitle', - context: context, - payload: { - title: title, - target: 0, // 하드웨어와 소프트웨어 모두 - titleParameters: { - fontSize: 18, - showTitle: true, - titleAlignment: "middle" - } - } - }; - - websocket.send(JSON.stringify(message)); - console.log('🏷️ [Property Inspector] 버튼 제목 업데이트:', title.replace('\n', '\\n')); -} \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/action.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/action.js deleted file mode 100644 index 7cc9620ab..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/action.js +++ /dev/null @@ -1,157 +0,0 @@ -let $websocket, $uuid, $action, $context, $settings, $lang, $FileID = ''; - -WebSocket.prototype.setGlobalSettings = function(payload) { - this.send(JSON.stringify({ - event: "setGlobalSettings", - context: $uuid, payload - })); -} - -WebSocket.prototype.getGlobalSettings = function() { - this.send(JSON.stringify({ - event: "getGlobalSettings", - context: $uuid, - })); -} - -// 与插件通信 -WebSocket.prototype.sendToPlugin = function (payload) { - this.send(JSON.stringify({ - event: "sendToPlugin", - action: $action, - context: $uuid, - payload - })); -}; - -//设置标题 -WebSocket.prototype.setTitle = function (str, row = 0, num = 6) { - console.log(str); - let newStr = ''; - if (row) { - let nowRow = 1, strArr = str.split(''); - strArr.forEach((item, index) => { - if (nowRow < row && index >= nowRow * num) { nowRow++; newStr += '\n'; } - if (nowRow <= row && index < nowRow * num) { newStr += item; } - }); - if (strArr.length > row * num) { newStr = newStr.substring(0, newStr.length - 1); newStr += '..'; } - } - this.send(JSON.stringify({ - event: "setTitle", - context: $context, - payload: { - target: 0, - title: newStr || str - } - })); -} - -// 设置状态 -WebSocket.prototype.setState = function (state) { - this.send(JSON.stringify({ - event: "setState", - context: $context, - payload: { state } - })); -}; - -// 设置背景 -WebSocket.prototype.setImage = function (url) { - let image = new Image(); - image.src = url; - image.onload = () => { - let canvas = document.createElement("canvas"); - canvas.width = image.naturalWidth; - canvas.height = image.naturalHeight; - let ctx = canvas.getContext("2d"); - ctx.drawImage(image, 0, 0); - this.send(JSON.stringify({ - event: "setImage", - context: $context, - payload: { - target: 0, - image: canvas.toDataURL("image/png") - } - })); - }; -}; - -// 打开网页 -WebSocket.prototype.openUrl = function (url) { - this.send(JSON.stringify({ - event: "openUrl", - payload: { url } - })); -}; - -// 保存持久化数据 -WebSocket.prototype.saveData = $.debounce(function (payload) { - this.send(JSON.stringify({ - event: "setSettings", - context: $uuid, - payload - })); -}); - -// StreamDock 软件入口函数 -const connectSocket = connectElgatoStreamDeckSocket; -async function connectElgatoStreamDeckSocket(port, uuid, event, app, info) { - info = JSON.parse(info); - $uuid = uuid; $action = info.action; - $context = info.context; - $websocket = new WebSocket('ws://127.0.0.1:' + port); - $websocket.onopen = () => $websocket.send(JSON.stringify({ event, uuid })); - - // 持久数据代理 - $websocket.onmessage = e => { - let data = JSON.parse(e.data); - if (data.event === 'didReceiveSettings') { - $settings = new Proxy(data.payload.settings, { - get(target, property) { - return target[property]; - }, - set(target, property, value) { - target[property] = value; - $websocket.saveData(data.payload.settings); - } - }); - if (!$back) $dom.main.style.display = 'block'; - } - $propEvent[data.event]?.(data.payload); - }; - - // 自动翻译页面 - if (!$local) return; - $lang = await new Promise(resolve => { - const req = new XMLHttpRequest(); - req.open('GET', `../../${JSON.parse(app).application.language}.json`); - req.send(); - req.onreadystatechange = () => { - if (req.readyState === 4) { - resolve(JSON.parse(req.responseText).Localization); - } - }; - }); - - // 遍历文本节点并翻译所有文本节点 - const walker = document.createTreeWalker($dom.main, NodeFilter.SHOW_TEXT, (e) => { - return e.data.trim() && NodeFilter.FILTER_ACCEPT; - }); - while (walker.nextNode()) { - console.log(walker.currentNode.data); - walker.currentNode.data = $lang[walker.currentNode.data]; - } - // placeholder 特殊处理 - const translate = item => { - if (item.placeholder?.trim()) { - console.log(item.placeholder); - item.placeholder = $lang[item.placeholder]; - } - }; - $('input', true).forEach(translate); - $('textarea', true).forEach(translate); -} - -// StreamDock 文件路径回调 -Array.from($('input[type="file"]', true)).forEach(item => item.addEventListener('click', () => $FileID = item.id)); -const onFilePickerReturn = (url) => $emit.send(`File-${$FileID}`, JSON.parse(url)); \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/axios.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/axios.js deleted file mode 100644 index 78aa7b89d..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/axios.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).axios=t()}(this,(function(){"use strict";function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(t)}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n2&&void 0!==arguments[2]?arguments[2]:{},a=i.allOwnKeys,s=void 0!==a&&a;if(null!=t)if("object"!==e(t)&&(t=[t]),p(t))for(r=0,o=t.length;r0;)if(t===(n=r[o]).toLowerCase())return n;return null}var C="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:global,N=function(e){return!h(e)&&e!==C};var x,P=(x="undefined"!=typeof Uint8Array&&c(Uint8Array),function(e){return x&&e instanceof x}),k=l("HTMLFormElement"),U=function(e){var t=Object.prototype.hasOwnProperty;return function(e,n){return t.call(e,n)}}(),_=l("RegExp"),F=function(e,t){var n=Object.getOwnPropertyDescriptors(e),r={};T(n,(function(n,o){var i;!1!==(i=t(n,o,e))&&(r[o]=i||n)})),Object.defineProperties(e,r)},B="abcdefghijklmnopqrstuvwxyz",L="0123456789",D={DIGIT:L,ALPHA:B,ALPHA_DIGIT:B+B.toUpperCase()+L};var I=l("AsyncFunction"),q={isArray:p,isArrayBuffer:m,isBuffer:function(e){return null!==e&&!h(e)&&null!==e.constructor&&!h(e.constructor)&&y(e.constructor.isBuffer)&&e.constructor.isBuffer(e)},isFormData:function(e){var t;return e&&("function"==typeof FormData&&e instanceof FormData||y(e.append)&&("formdata"===(t=f(e))||"object"===t&&y(e.toString)&&"[object FormData]"===e.toString()))},isArrayBufferView:function(e){return"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&m(e.buffer)},isString:v,isNumber:b,isBoolean:function(e){return!0===e||!1===e},isObject:g,isPlainObject:w,isUndefined:h,isDate:E,isFile:O,isBlob:S,isRegExp:_,isFunction:y,isStream:function(e){return g(e)&&y(e.pipe)},isURLSearchParams:A,isTypedArray:P,isFileList:R,forEach:T,merge:function e(){for(var t=N(this)&&this||{},n=t.caseless,r={},o=function(t,o){var i=n&&j(r,o)||o;w(r[i])&&w(t)?r[i]=e(r[i],t):w(t)?r[i]=e({},t):p(t)?r[i]=t.slice():r[i]=t},i=0,a=arguments.length;i3&&void 0!==arguments[3]?arguments[3]:{},o=r.allOwnKeys;return T(t,(function(t,r){n&&y(t)?e[r]=a(t,n):e[r]=t}),{allOwnKeys:o}),e},trim:function(e){return e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")},stripBOM:function(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e},inherits:function(e,t,n,r){e.prototype=Object.create(t.prototype,r),e.prototype.constructor=e,Object.defineProperty(e,"super",{value:t.prototype}),n&&Object.assign(e.prototype,n)},toFlatObject:function(e,t,n,r){var o,i,a,s={};if(t=t||{},null==e)return t;do{for(i=(o=Object.getOwnPropertyNames(e)).length;i-- >0;)a=o[i],r&&!r(a,e,t)||s[a]||(t[a]=e[a],s[a]=!0);e=!1!==n&&c(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},kindOf:f,kindOfTest:l,endsWith:function(e,t,n){e=String(e),(void 0===n||n>e.length)&&(n=e.length),n-=t.length;var r=e.indexOf(t,n);return-1!==r&&r===n},toArray:function(e){if(!e)return null;if(p(e))return e;var t=e.length;if(!b(t))return null;for(var n=new Array(t);t-- >0;)n[t]=e[t];return n},forEachEntry:function(e,t){for(var n,r=(e&&e[Symbol.iterator]).call(e);(n=r.next())&&!n.done;){var o=n.value;t.call(e,o[0],o[1])}},matchAll:function(e,t){for(var n,r=[];null!==(n=e.exec(t));)r.push(n);return r},isHTMLForm:k,hasOwnProperty:U,hasOwnProp:U,reduceDescriptors:F,freezeMethods:function(e){F(e,(function(t,n){if(y(e)&&-1!==["arguments","caller","callee"].indexOf(n))return!1;var r=e[n];y(r)&&(t.enumerable=!1,"writable"in t?t.writable=!1:t.set||(t.set=function(){throw Error("Can not rewrite read-only method '"+n+"'")}))}))},toObjectSet:function(e,t){var n={},r=function(e){e.forEach((function(e){n[e]=!0}))};return p(e)?r(e):r(String(e).split(t)),n},toCamelCase:function(e){return e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,(function(e,t,n){return t.toUpperCase()+n}))},noop:function(){},toFiniteNumber:function(e,t){return e=+e,Number.isFinite(e)?e:t},findKey:j,global:C,isContextDefined:N,ALPHABET:D,generateString:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:16,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:D.ALPHA_DIGIT,n="",r=t.length;e--;)n+=t[Math.random()*r|0];return n},isSpecCompliantForm:function(e){return!!(e&&y(e.append)&&"FormData"===e[Symbol.toStringTag]&&e[Symbol.iterator])},toJSONObject:function(e){var t=new Array(10);return function e(n,r){if(g(n)){if(t.indexOf(n)>=0)return;if(!("toJSON"in n)){t[r]=n;var o=p(n)?[]:{};return T(n,(function(t,n){var i=e(t,r+1);!h(i)&&(o[n]=i)})),t[r]=void 0,o}}return n}(e,0)},isAsyncFn:I,isThenable:function(e){return e&&(g(e)||y(e))&&y(e.then)&&y(e.catch)}};function M(e,t,n,r,o){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack,this.message=e,this.name="AxiosError",t&&(this.code=t),n&&(this.config=n),r&&(this.request=r),o&&(this.response=o)}q.inherits(M,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:q.toJSONObject(this.config),code:this.code,status:this.response&&this.response.status?this.response.status:null}}});var z=M.prototype,H={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach((function(e){H[e]={value:e}})),Object.defineProperties(M,H),Object.defineProperty(z,"isAxiosError",{value:!0}),M.from=function(e,t,n,r,o,i){var a=Object.create(z);return q.toFlatObject(e,a,(function(e){return e!==Error.prototype}),(function(e){return"isAxiosError"!==e})),M.call(a,e.message,t,n,r,o),a.cause=e,a.name=e.name,i&&Object.assign(a,i),a};function J(e){return q.isPlainObject(e)||q.isArray(e)}function W(e){return q.endsWith(e,"[]")?e.slice(0,-2):e}function K(e,t,n){return e?e.concat(t).map((function(e,t){return e=W(e),!n&&t?"["+e+"]":e})).join(n?".":""):t}var V=q.toFlatObject(q,{},null,(function(e){return/^is[A-Z]/.test(e)}));function G(t,n,r){if(!q.isObject(t))throw new TypeError("target must be an object");n=n||new FormData;var o=(r=q.toFlatObject(r,{metaTokens:!0,dots:!1,indexes:!1},!1,(function(e,t){return!q.isUndefined(t[e])}))).metaTokens,i=r.visitor||f,a=r.dots,s=r.indexes,u=(r.Blob||"undefined"!=typeof Blob&&Blob)&&q.isSpecCompliantForm(n);if(!q.isFunction(i))throw new TypeError("visitor must be a function");function c(e){if(null===e)return"";if(q.isDate(e))return e.toISOString();if(!u&&q.isBlob(e))throw new M("Blob is not supported. Use a Buffer instead.");return q.isArrayBuffer(e)||q.isTypedArray(e)?u&&"function"==typeof Blob?new Blob([e]):Buffer.from(e):e}function f(t,r,i){var u=t;if(t&&!i&&"object"===e(t))if(q.endsWith(r,"{}"))r=o?r:r.slice(0,-2),t=JSON.stringify(t);else if(q.isArray(t)&&function(e){return q.isArray(e)&&!e.some(J)}(t)||(q.isFileList(t)||q.endsWith(r,"[]"))&&(u=q.toArray(t)))return r=W(r),u.forEach((function(e,t){!q.isUndefined(e)&&null!==e&&n.append(!0===s?K([r],t,a):null===s?r:r+"[]",c(e))})),!1;return!!J(t)||(n.append(K(i,r,a),c(t)),!1)}var l=[],d=Object.assign(V,{defaultVisitor:f,convertValue:c,isVisitable:J});if(!q.isObject(t))throw new TypeError("data must be an object");return function e(t,r){if(!q.isUndefined(t)){if(-1!==l.indexOf(t))throw Error("Circular reference detected in "+r.join("."));l.push(t),q.forEach(t,(function(t,o){!0===(!(q.isUndefined(t)||null===t)&&i.call(n,t,q.isString(o)?o.trim():o,r,d))&&e(t,r?r.concat(o):[o])})),l.pop()}}(t),n}function $(e){var t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,(function(e){return t[e]}))}function X(e,t){this._pairs=[],e&&G(e,this,t)}var Q=X.prototype;function Z(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}function Y(e,t,n){if(!t)return e;var r,o=n&&n.encode||Z,i=n&&n.serialize;if(r=i?i(t,n):q.isURLSearchParams(t)?t.toString():new X(t,n).toString(o)){var a=e.indexOf("#");-1!==a&&(e=e.slice(0,a)),e+=(-1===e.indexOf("?")?"?":"&")+r}return e}Q.append=function(e,t){this._pairs.push([e,t])},Q.toString=function(e){var t=e?function(t){return e.call(this,t,$)}:$;return this._pairs.map((function(e){return t(e[0])+"="+t(e[1])}),"").join("&")};var ee,te=function(){function e(){t(this,e),this.handlers=[]}return r(e,[{key:"use",value:function(e,t,n){return this.handlers.push({fulfilled:e,rejected:t,synchronous:!!n&&n.synchronous,runWhen:n?n.runWhen:null}),this.handlers.length-1}},{key:"eject",value:function(e){this.handlers[e]&&(this.handlers[e]=null)}},{key:"clear",value:function(){this.handlers&&(this.handlers=[])}},{key:"forEach",value:function(e){q.forEach(this.handlers,(function(t){null!==t&&e(t)}))}}]),e}(),ne={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},re={isBrowser:!0,classes:{URLSearchParams:"undefined"!=typeof URLSearchParams?URLSearchParams:X,FormData:"undefined"!=typeof FormData?FormData:null,Blob:"undefined"!=typeof Blob?Blob:null},isStandardBrowserEnv:("undefined"==typeof navigator||"ReactNative"!==(ee=navigator.product)&&"NativeScript"!==ee&&"NS"!==ee)&&"undefined"!=typeof window&&"undefined"!=typeof document,isStandardBrowserWebWorkerEnv:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope&&"function"==typeof self.importScripts,protocols:["http","https","file","blob","url","data"]};function oe(e){function t(e,n,r,o){var i=e[o++],a=Number.isFinite(+i),s=o>=e.length;return i=!i&&q.isArray(r)?r.length:i,s?(q.hasOwnProp(r,i)?r[i]=[r[i],n]:r[i]=n,!a):(r[i]&&q.isObject(r[i])||(r[i]=[]),t(e,n,r[i],o)&&q.isArray(r[i])&&(r[i]=function(e){var t,n,r={},o=Object.keys(e),i=o.length;for(t=0;t-1,i=q.isObject(e);if(i&&q.isHTMLForm(e)&&(e=new FormData(e)),q.isFormData(e))return o&&o?JSON.stringify(oe(e)):e;if(q.isArrayBuffer(e)||q.isBuffer(e)||q.isStream(e)||q.isFile(e)||q.isBlob(e))return e;if(q.isArrayBufferView(e))return e.buffer;if(q.isURLSearchParams(e))return t.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),e.toString();if(i){if(r.indexOf("application/x-www-form-urlencoded")>-1)return function(e,t){return G(e,new re.classes.URLSearchParams,Object.assign({visitor:function(e,t,n,r){return re.isNode&&q.isBuffer(e)?(this.append(t,e.toString("base64")),!1):r.defaultVisitor.apply(this,arguments)}},t))}(e,this.formSerializer).toString();if((n=q.isFileList(e))||r.indexOf("multipart/form-data")>-1){var a=this.env&&this.env.FormData;return G(n?{"files[]":e}:e,a&&new a,this.formSerializer)}}return i||o?(t.setContentType("application/json",!1),function(e,t,n){if(q.isString(e))try{return(t||JSON.parse)(e),q.trim(e)}catch(e){if("SyntaxError"!==e.name)throw e}return(n||JSON.stringify)(e)}(e)):e}],transformResponse:[function(e){var t=this.transitional||ie.transitional,n=t&&t.forcedJSONParsing,r="json"===this.responseType;if(e&&q.isString(e)&&(n&&!this.responseType||r)){var o=!(t&&t.silentJSONParsing)&&r;try{return JSON.parse(e)}catch(e){if(o){if("SyntaxError"===e.name)throw M.from(e,M.ERR_BAD_RESPONSE,this,null,this.response);throw e}}}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:re.classes.FormData,Blob:re.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*","Content-Type":void 0}}};q.forEach(["delete","get","head","post","put","patch"],(function(e){ie.headers[e]={}}));var ae=ie,se=q.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),ue=Symbol("internals");function ce(e){return e&&String(e).trim().toLowerCase()}function fe(e){return!1===e||null==e?e:q.isArray(e)?e.map(fe):String(e)}function le(e,t,n,r,o){return q.isFunction(r)?r.call(this,t,n):(o&&(t=n),q.isString(t)?q.isString(r)?-1!==t.indexOf(r):q.isRegExp(r)?r.test(t):void 0:void 0)}var de=function(e,n){function i(e){t(this,i),e&&this.set(e)}return r(i,[{key:"set",value:function(e,t,n){var r=this;function o(e,t,n){var o=ce(t);if(!o)throw new Error("header name must be a non-empty string");var i=q.findKey(r,o);(!i||void 0===r[i]||!0===n||void 0===n&&!1!==r[i])&&(r[i||t]=fe(e))}var i,a,s,u,c,f=function(e,t){return q.forEach(e,(function(e,n){return o(e,n,t)}))};return q.isPlainObject(e)||e instanceof this.constructor?f(e,t):q.isString(e)&&(e=e.trim())&&!/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim())?f((c={},(i=e)&&i.split("\n").forEach((function(e){u=e.indexOf(":"),a=e.substring(0,u).trim().toLowerCase(),s=e.substring(u+1).trim(),!a||c[a]&&se[a]||("set-cookie"===a?c[a]?c[a].push(s):c[a]=[s]:c[a]=c[a]?c[a]+", "+s:s)})),c),t):null!=e&&o(t,e,n),this}},{key:"get",value:function(e,t){if(e=ce(e)){var n=q.findKey(this,e);if(n){var r=this[n];if(!t)return r;if(!0===t)return function(e){for(var t,n=Object.create(null),r=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;t=r.exec(e);)n[t[1]]=t[2];return n}(r);if(q.isFunction(t))return t.call(this,r,n);if(q.isRegExp(t))return t.exec(r);throw new TypeError("parser must be boolean|regexp|function")}}}},{key:"has",value:function(e,t){if(e=ce(e)){var n=q.findKey(this,e);return!(!n||void 0===this[n]||t&&!le(0,this[n],n,t))}return!1}},{key:"delete",value:function(e,t){var n=this,r=!1;function o(e){if(e=ce(e)){var o=q.findKey(n,e);!o||t&&!le(0,n[o],o,t)||(delete n[o],r=!0)}}return q.isArray(e)?e.forEach(o):o(e),r}},{key:"clear",value:function(e){for(var t=Object.keys(this),n=t.length,r=!1;n--;){var o=t[n];e&&!le(0,this[o],o,e,!0)||(delete this[o],r=!0)}return r}},{key:"normalize",value:function(e){var t=this,n={};return q.forEach(this,(function(r,o){var i=q.findKey(n,o);if(i)return t[i]=fe(r),void delete t[o];var a=e?function(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(function(e,t,n){return t.toUpperCase()+n}))}(o):String(o).trim();a!==o&&delete t[o],t[a]=fe(r),n[a]=!0})),this}},{key:"concat",value:function(){for(var e,t=arguments.length,n=new Array(t),r=0;r1?n-1:0),o=1;o1?"since :\n"+u.map(Oe).join("\n"):" "+Oe(u[0]):"as no adapter specified"),"ERR_NOT_SUPPORT")}return n};function Ae(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new ve(null,e)}function Te(e){return Ae(e),e.headers=pe.from(e.headers),e.data=he.call(e,e.transformRequest),-1!==["post","put","patch"].indexOf(e.method)&&e.headers.setContentType("application/x-www-form-urlencoded",!1),Re(e.adapter||ae.adapter)(e).then((function(t){return Ae(e),t.data=he.call(e,e.transformResponse,t),t.headers=pe.from(t.headers),t}),(function(t){return me(t)||(Ae(e),t&&t.response&&(t.response.data=he.call(e,e.transformResponse,t.response),t.response.headers=pe.from(t.response.headers))),Promise.reject(t)}))}var je=function(e){return e instanceof pe?e.toJSON():e};function Ce(e,t){t=t||{};var n={};function r(e,t,n){return q.isPlainObject(e)&&q.isPlainObject(t)?q.merge.call({caseless:n},e,t):q.isPlainObject(t)?q.merge({},t):q.isArray(t)?t.slice():t}function o(e,t,n){return q.isUndefined(t)?q.isUndefined(e)?void 0:r(void 0,e,n):r(e,t,n)}function i(e,t){if(!q.isUndefined(t))return r(void 0,t)}function a(e,t){return q.isUndefined(t)?q.isUndefined(e)?void 0:r(void 0,e):r(void 0,t)}function s(n,o,i){return i in t?r(n,o):i in e?r(void 0,n):void 0}var u={url:i,method:i,data:i,baseURL:a,transformRequest:a,transformResponse:a,paramsSerializer:a,timeout:a,timeoutMessage:a,withCredentials:a,adapter:a,responseType:a,xsrfCookieName:a,xsrfHeaderName:a,onUploadProgress:a,onDownloadProgress:a,decompress:a,maxContentLength:a,maxBodyLength:a,beforeRedirect:a,transport:a,httpAgent:a,httpsAgent:a,cancelToken:a,socketPath:a,responseEncoding:a,validateStatus:s,headers:function(e,t){return o(je(e),je(t),!0)}};return q.forEach(Object.keys(Object.assign({},e,t)),(function(r){var i=u[r]||o,a=i(e[r],t[r],r);q.isUndefined(a)&&i!==s||(n[r]=a)})),n}var Ne="1.5.1",xe={};["object","boolean","number","function","string","symbol"].forEach((function(t,n){xe[t]=function(r){return e(r)===t||"a"+(n<1?"n ":" ")+t}}));var Pe={};xe.transitional=function(e,t,n){function r(e,t){return"[Axios v1.5.1] Transitional option '"+e+"'"+t+(n?". "+n:"")}return function(n,o,i){if(!1===e)throw new M(r(o," has been removed"+(t?" in "+t:"")),M.ERR_DEPRECATED);return t&&!Pe[o]&&(Pe[o]=!0,console.warn(r(o," has been deprecated since v"+t+" and will be removed in the near future"))),!e||e(n,o,i)}};var ke={assertOptions:function(t,n,r){if("object"!==e(t))throw new M("options must be an object",M.ERR_BAD_OPTION_VALUE);for(var o=Object.keys(t),i=o.length;i-- >0;){var a=o[i],s=n[a];if(s){var u=t[a],c=void 0===u||s(u,a,t);if(!0!==c)throw new M("option "+a+" must be "+c,M.ERR_BAD_OPTION_VALUE)}else if(!0!==r)throw new M("Unknown option "+a,M.ERR_BAD_OPTION)}},validators:xe},Ue=ke.validators,_e=function(){function e(n){t(this,e),this.defaults=n,this.interceptors={request:new te,response:new te}}return r(e,[{key:"request",value:function(e,t){"string"==typeof e?(t=t||{}).url=e:t=e||{};var n=t=Ce(this.defaults,t),r=n.transitional,o=n.paramsSerializer,i=n.headers;void 0!==r&&ke.assertOptions(r,{silentJSONParsing:Ue.transitional(Ue.boolean),forcedJSONParsing:Ue.transitional(Ue.boolean),clarifyTimeoutError:Ue.transitional(Ue.boolean)},!1),null!=o&&(q.isFunction(o)?t.paramsSerializer={serialize:o}:ke.assertOptions(o,{encode:Ue.function,serialize:Ue.function},!0)),t.method=(t.method||this.defaults.method||"get").toLowerCase();var a=i&&q.merge(i.common,i[t.method]);i&&q.forEach(["delete","get","head","post","put","patch","common"],(function(e){delete i[e]})),t.headers=pe.concat(a,i);var s=[],u=!0;this.interceptors.request.forEach((function(e){"function"==typeof e.runWhen&&!1===e.runWhen(t)||(u=u&&e.synchronous,s.unshift(e.fulfilled,e.rejected))}));var c,f=[];this.interceptors.response.forEach((function(e){f.push(e.fulfilled,e.rejected)}));var l,d=0;if(!u){var p=[Te.bind(this),void 0];for(p.unshift.apply(p,s),p.push.apply(p,f),l=p.length,c=Promise.resolve(t);d0;)o._listeners[t](e);o._listeners=null}})),this.promise.then=function(e){var t,n=new Promise((function(e){o.subscribe(e),t=e})).then(e);return n.cancel=function(){o.unsubscribe(t)},n},n((function(e,t,n){o.reason||(o.reason=new ve(e,t,n),r(o.reason))}))}return r(e,[{key:"throwIfRequested",value:function(){if(this.reason)throw this.reason}},{key:"subscribe",value:function(e){this.reason?e(this.reason):this._listeners?this._listeners.push(e):this._listeners=[e]}},{key:"unsubscribe",value:function(e){if(this._listeners){var t=this._listeners.indexOf(e);-1!==t&&this._listeners.splice(t,1)}}}],[{key:"source",value:function(){var t;return{token:new e((function(e){t=e})),cancel:t}}}]),e}();var Le={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(Le).forEach((function(e){var t=o(e,2),n=t[0],r=t[1];Le[r]=n}));var De=Le;var Ie=function e(t){var n=new Fe(t),r=a(Fe.prototype.request,n);return q.extend(r,Fe.prototype,n,{allOwnKeys:!0}),q.extend(r,n,null,{allOwnKeys:!0}),r.create=function(n){return e(Ce(t,n))},r}(ae);return Ie.Axios=Fe,Ie.CanceledError=ve,Ie.CancelToken=Be,Ie.isCancel=me,Ie.VERSION=Ne,Ie.toFormData=G,Ie.AxiosError=M,Ie.Cancel=Ie.CanceledError,Ie.all=function(e){return Promise.all(e)},Ie.spread=function(e){return function(t){return e.apply(null,t)}},Ie.isAxiosError=function(e){return q.isObject(e)&&!0===e.isAxiosError},Ie.mergeConfig=Ce,Ie.AxiosHeaders=pe,Ie.formToJSON=function(e){return oe(q.isHTMLForm(e)?new FormData(e):e)},Ie.getAdapter=Re,Ie.HttpStatusCode=De,Ie.default=Ie,Ie})); \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/bootstrap-icons.css b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/bootstrap-icons.css deleted file mode 100644 index 3c1537f1b..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/bootstrap-icons.css +++ /dev/null @@ -1,1556 +0,0 @@ -@font-face { - font-family: "bootstrap-icons"; - src: url("./fonts/bootstrap-icons.woff2?30af91bf14e37666a085fb8a161ff36d") format("woff2"), -url("./fonts/bootstrap-icons.woff?30af91bf14e37666a085fb8a161ff36d") format("woff"); -} - -.bi::before, -[class^="bi-"]::before, -[class*=" bi-"]::before { - display: inline-block; - font-family: bootstrap-icons !important; - font-style: normal; - font-weight: normal !important; - font-variant: normal; - text-transform: none; - line-height: 1; - vertical-align: -.125em; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.bi-123::before { content: "\f67f"; } -.bi-alarm-fill::before { content: "\f101"; } -.bi-alarm::before { content: "\f102"; } -.bi-align-bottom::before { content: "\f103"; } -.bi-align-center::before { content: "\f104"; } -.bi-align-end::before { content: "\f105"; } -.bi-align-middle::before { content: "\f106"; } -.bi-align-start::before { content: "\f107"; } -.bi-align-top::before { content: "\f108"; } -.bi-alt::before { content: "\f109"; } -.bi-app-indicator::before { content: "\f10a"; } -.bi-app::before { content: "\f10b"; } -.bi-archive-fill::before { content: "\f10c"; } -.bi-archive::before { content: "\f10d"; } -.bi-arrow-90deg-down::before { content: "\f10e"; } -.bi-arrow-90deg-left::before { content: "\f10f"; } -.bi-arrow-90deg-right::before { content: "\f110"; } -.bi-arrow-90deg-up::before { content: "\f111"; } -.bi-arrow-bar-down::before { content: "\f112"; } -.bi-arrow-bar-left::before { content: "\f113"; } -.bi-arrow-bar-right::before { content: "\f114"; } -.bi-arrow-bar-up::before { content: "\f115"; } -.bi-arrow-clockwise::before { content: "\f116"; } -.bi-arrow-counterclockwise::before { content: "\f117"; } -.bi-arrow-down-circle-fill::before { content: "\f118"; } -.bi-arrow-down-circle::before { content: "\f119"; } -.bi-arrow-down-left-circle-fill::before { content: "\f11a"; } -.bi-arrow-down-left-circle::before { content: "\f11b"; } -.bi-arrow-down-left-square-fill::before { content: "\f11c"; } -.bi-arrow-down-left-square::before { content: "\f11d"; } -.bi-arrow-down-left::before { content: "\f11e"; } -.bi-arrow-down-right-circle-fill::before { content: "\f11f"; } -.bi-arrow-down-right-circle::before { content: "\f120"; } -.bi-arrow-down-right-square-fill::before { content: "\f121"; } -.bi-arrow-down-right-square::before { content: "\f122"; } -.bi-arrow-down-right::before { content: "\f123"; } -.bi-arrow-down-short::before { content: "\f124"; } -.bi-arrow-down-square-fill::before { content: "\f125"; } -.bi-arrow-down-square::before { content: "\f126"; } -.bi-arrow-down-up::before { content: "\f127"; } -.bi-arrow-down::before { content: "\f128"; } -.bi-arrow-left-circle-fill::before { content: "\f129"; } -.bi-arrow-left-circle::before { content: "\f12a"; } -.bi-arrow-left-right::before { content: "\f12b"; } -.bi-arrow-left-short::before { content: "\f12c"; } -.bi-arrow-left-square-fill::before { content: "\f12d"; } -.bi-arrow-left-square::before { content: "\f12e"; } -.bi-arrow-left::before { content: "\f12f"; } -.bi-arrow-repeat::before { content: "\f130"; } -.bi-arrow-return-left::before { content: "\f131"; } -.bi-arrow-return-right::before { content: "\f132"; } -.bi-arrow-right-circle-fill::before { content: "\f133"; } -.bi-arrow-right-circle::before { content: "\f134"; } -.bi-arrow-right-short::before { content: "\f135"; } -.bi-arrow-right-square-fill::before { content: "\f136"; } -.bi-arrow-right-square::before { content: "\f137"; } -.bi-arrow-right::before { content: "\f138"; } -.bi-arrow-up-circle-fill::before { content: "\f139"; } -.bi-arrow-up-circle::before { content: "\f13a"; } -.bi-arrow-up-left-circle-fill::before { content: "\f13b"; } -.bi-arrow-up-left-circle::before { content: "\f13c"; } -.bi-arrow-up-left-square-fill::before { content: "\f13d"; } -.bi-arrow-up-left-square::before { content: "\f13e"; } -.bi-arrow-up-left::before { content: "\f13f"; } -.bi-arrow-up-right-circle-fill::before { content: "\f140"; } -.bi-arrow-up-right-circle::before { content: "\f141"; } -.bi-arrow-up-right-square-fill::before { content: "\f142"; } -.bi-arrow-up-right-square::before { content: "\f143"; } -.bi-arrow-up-right::before { content: "\f144"; } -.bi-arrow-up-short::before { content: "\f145"; } -.bi-arrow-up-square-fill::before { content: "\f146"; } -.bi-arrow-up-square::before { content: "\f147"; } -.bi-arrow-up::before { content: "\f148"; } -.bi-arrows-angle-contract::before { content: "\f149"; } -.bi-arrows-angle-expand::before { content: "\f14a"; } -.bi-arrows-collapse::before { content: "\f14b"; } -.bi-arrows-expand::before { content: "\f14c"; } -.bi-arrows-fullscreen::before { content: "\f14d"; } -.bi-arrows-move::before { content: "\f14e"; } -.bi-aspect-ratio-fill::before { content: "\f14f"; } -.bi-aspect-ratio::before { content: "\f150"; } -.bi-asterisk::before { content: "\f151"; } -.bi-at::before { content: "\f152"; } -.bi-award-fill::before { content: "\f153"; } -.bi-award::before { content: "\f154"; } -.bi-back::before { content: "\f155"; } -.bi-backspace-fill::before { content: "\f156"; } -.bi-backspace-reverse-fill::before { content: "\f157"; } -.bi-backspace-reverse::before { content: "\f158"; } -.bi-backspace::before { content: "\f159"; } -.bi-badge-3d-fill::before { content: "\f15a"; } -.bi-badge-3d::before { content: "\f15b"; } -.bi-badge-4k-fill::before { content: "\f15c"; } -.bi-badge-4k::before { content: "\f15d"; } -.bi-badge-8k-fill::before { content: "\f15e"; } -.bi-badge-8k::before { content: "\f15f"; } -.bi-badge-ad-fill::before { content: "\f160"; } -.bi-badge-ad::before { content: "\f161"; } -.bi-badge-ar-fill::before { content: "\f162"; } -.bi-badge-ar::before { content: "\f163"; } -.bi-badge-cc-fill::before { content: "\f164"; } -.bi-badge-cc::before { content: "\f165"; } -.bi-badge-hd-fill::before { content: "\f166"; } -.bi-badge-hd::before { content: "\f167"; } -.bi-badge-tm-fill::before { content: "\f168"; } -.bi-badge-tm::before { content: "\f169"; } -.bi-badge-vo-fill::before { content: "\f16a"; } -.bi-badge-vo::before { content: "\f16b"; } -.bi-badge-vr-fill::before { content: "\f16c"; } -.bi-badge-vr::before { content: "\f16d"; } -.bi-badge-wc-fill::before { content: "\f16e"; } -.bi-badge-wc::before { content: "\f16f"; } -.bi-bag-check-fill::before { content: "\f170"; } -.bi-bag-check::before { content: "\f171"; } -.bi-bag-dash-fill::before { content: "\f172"; } -.bi-bag-dash::before { content: "\f173"; } -.bi-bag-fill::before { content: "\f174"; } -.bi-bag-plus-fill::before { content: "\f175"; } -.bi-bag-plus::before { content: "\f176"; } -.bi-bag-x-fill::before { content: "\f177"; } -.bi-bag-x::before { content: "\f178"; } -.bi-bag::before { content: "\f179"; } -.bi-bar-chart-fill::before { content: "\f17a"; } -.bi-bar-chart-line-fill::before { content: "\f17b"; } -.bi-bar-chart-line::before { content: "\f17c"; } -.bi-bar-chart-steps::before { content: "\f17d"; } -.bi-bar-chart::before { content: "\f17e"; } -.bi-basket-fill::before { content: "\f17f"; } -.bi-basket::before { content: "\f180"; } -.bi-basket2-fill::before { content: "\f181"; } -.bi-basket2::before { content: "\f182"; } -.bi-basket3-fill::before { content: "\f183"; } -.bi-basket3::before { content: "\f184"; } -.bi-battery-charging::before { content: "\f185"; } -.bi-battery-full::before { content: "\f186"; } -.bi-battery-half::before { content: "\f187"; } -.bi-battery::before { content: "\f188"; } -.bi-bell-fill::before { content: "\f189"; } -.bi-bell::before { content: "\f18a"; } -.bi-bezier::before { content: "\f18b"; } -.bi-bezier2::before { content: "\f18c"; } -.bi-bicycle::before { content: "\f18d"; } -.bi-binoculars-fill::before { content: "\f18e"; } -.bi-binoculars::before { content: "\f18f"; } -.bi-blockquote-left::before { content: "\f190"; } -.bi-blockquote-right::before { content: "\f191"; } -.bi-book-fill::before { content: "\f192"; } -.bi-book-half::before { content: "\f193"; } -.bi-book::before { content: "\f194"; } -.bi-bookmark-check-fill::before { content: "\f195"; } -.bi-bookmark-check::before { content: "\f196"; } -.bi-bookmark-dash-fill::before { content: "\f197"; } -.bi-bookmark-dash::before { content: "\f198"; } -.bi-bookmark-fill::before { content: "\f199"; } -.bi-bookmark-heart-fill::before { content: "\f19a"; } -.bi-bookmark-heart::before { content: "\f19b"; } -.bi-bookmark-plus-fill::before { content: "\f19c"; } -.bi-bookmark-plus::before { content: "\f19d"; } -.bi-bookmark-star-fill::before { content: "\f19e"; } -.bi-bookmark-star::before { content: "\f19f"; } -.bi-bookmark-x-fill::before { content: "\f1a0"; } -.bi-bookmark-x::before { content: "\f1a1"; } -.bi-bookmark::before { content: "\f1a2"; } -.bi-bookmarks-fill::before { content: "\f1a3"; } -.bi-bookmarks::before { content: "\f1a4"; } -.bi-bookshelf::before { content: "\f1a5"; } -.bi-bootstrap-fill::before { content: "\f1a6"; } -.bi-bootstrap-reboot::before { content: "\f1a7"; } -.bi-bootstrap::before { content: "\f1a8"; } -.bi-border-all::before { content: "\f1a9"; } -.bi-border-bottom::before { content: "\f1aa"; } -.bi-border-center::before { content: "\f1ab"; } -.bi-border-inner::before { content: "\f1ac"; } -.bi-border-left::before { content: "\f1ad"; } -.bi-border-middle::before { content: "\f1ae"; } -.bi-border-outer::before { content: "\f1af"; } -.bi-border-right::before { content: "\f1b0"; } -.bi-border-style::before { content: "\f1b1"; } -.bi-border-top::before { content: "\f1b2"; } -.bi-border-width::before { content: "\f1b3"; } -.bi-border::before { content: "\f1b4"; } -.bi-bounding-box-circles::before { content: "\f1b5"; } -.bi-bounding-box::before { content: "\f1b6"; } -.bi-box-arrow-down-left::before { content: "\f1b7"; } -.bi-box-arrow-down-right::before { content: "\f1b8"; } -.bi-box-arrow-down::before { content: "\f1b9"; } -.bi-box-arrow-in-down-left::before { content: "\f1ba"; } -.bi-box-arrow-in-down-right::before { content: "\f1bb"; } -.bi-box-arrow-in-down::before { content: "\f1bc"; } -.bi-box-arrow-in-left::before { content: "\f1bd"; } -.bi-box-arrow-in-right::before { content: "\f1be"; } -.bi-box-arrow-in-up-left::before { content: "\f1bf"; } -.bi-box-arrow-in-up-right::before { content: "\f1c0"; } -.bi-box-arrow-in-up::before { content: "\f1c1"; } -.bi-box-arrow-left::before { content: "\f1c2"; } -.bi-box-arrow-right::before { content: "\f1c3"; } -.bi-box-arrow-up-left::before { content: "\f1c4"; } -.bi-box-arrow-up-right::before { content: "\f1c5"; } -.bi-box-arrow-up::before { content: "\f1c6"; } -.bi-box-seam::before { content: "\f1c7"; } -.bi-box::before { content: "\f1c8"; } -.bi-braces::before { content: "\f1c9"; } -.bi-bricks::before { content: "\f1ca"; } -.bi-briefcase-fill::before { content: "\f1cb"; } -.bi-briefcase::before { content: "\f1cc"; } -.bi-brightness-alt-high-fill::before { content: "\f1cd"; } -.bi-brightness-alt-high::before { content: "\f1ce"; } -.bi-brightness-alt-low-fill::before { content: "\f1cf"; } -.bi-brightness-alt-low::before { content: "\f1d0"; } -.bi-brightness-high-fill::before { content: "\f1d1"; } -.bi-brightness-high::before { content: "\f1d2"; } -.bi-brightness-low-fill::before { content: "\f1d3"; } -.bi-brightness-low::before { content: "\f1d4"; } -.bi-broadcast-pin::before { content: "\f1d5"; } -.bi-broadcast::before { content: "\f1d6"; } -.bi-brush-fill::before { content: "\f1d7"; } -.bi-brush::before { content: "\f1d8"; } -.bi-bucket-fill::before { content: "\f1d9"; } -.bi-bucket::before { content: "\f1da"; } -.bi-bug-fill::before { content: "\f1db"; } -.bi-bug::before { content: "\f1dc"; } -.bi-building::before { content: "\f1dd"; } -.bi-bullseye::before { content: "\f1de"; } -.bi-calculator-fill::before { content: "\f1df"; } -.bi-calculator::before { content: "\f1e0"; } -.bi-calendar-check-fill::before { content: "\f1e1"; } -.bi-calendar-check::before { content: "\f1e2"; } -.bi-calendar-date-fill::before { content: "\f1e3"; } -.bi-calendar-date::before { content: "\f1e4"; } -.bi-calendar-day-fill::before { content: "\f1e5"; } -.bi-calendar-day::before { content: "\f1e6"; } -.bi-calendar-event-fill::before { content: "\f1e7"; } -.bi-calendar-event::before { content: "\f1e8"; } -.bi-calendar-fill::before { content: "\f1e9"; } -.bi-calendar-minus-fill::before { content: "\f1ea"; } -.bi-calendar-minus::before { content: "\f1eb"; } -.bi-calendar-month-fill::before { content: "\f1ec"; } -.bi-calendar-month::before { content: "\f1ed"; } -.bi-calendar-plus-fill::before { content: "\f1ee"; } -.bi-calendar-plus::before { content: "\f1ef"; } -.bi-calendar-range-fill::before { content: "\f1f0"; } -.bi-calendar-range::before { content: "\f1f1"; } -.bi-calendar-week-fill::before { content: "\f1f2"; } -.bi-calendar-week::before { content: "\f1f3"; } -.bi-calendar-x-fill::before { content: "\f1f4"; } -.bi-calendar-x::before { content: "\f1f5"; } -.bi-calendar::before { content: "\f1f6"; } -.bi-calendar2-check-fill::before { content: "\f1f7"; } -.bi-calendar2-check::before { content: "\f1f8"; } -.bi-calendar2-date-fill::before { content: "\f1f9"; } -.bi-calendar2-date::before { content: "\f1fa"; } -.bi-calendar2-day-fill::before { content: "\f1fb"; } -.bi-calendar2-day::before { content: "\f1fc"; } -.bi-calendar2-event-fill::before { content: "\f1fd"; } -.bi-calendar2-event::before { content: "\f1fe"; } -.bi-calendar2-fill::before { content: "\f1ff"; } -.bi-calendar2-minus-fill::before { content: "\f200"; } -.bi-calendar2-minus::before { content: "\f201"; } -.bi-calendar2-month-fill::before { content: "\f202"; } -.bi-calendar2-month::before { content: "\f203"; } -.bi-calendar2-plus-fill::before { content: "\f204"; } -.bi-calendar2-plus::before { content: "\f205"; } -.bi-calendar2-range-fill::before { content: "\f206"; } -.bi-calendar2-range::before { content: "\f207"; } -.bi-calendar2-week-fill::before { content: "\f208"; } -.bi-calendar2-week::before { content: "\f209"; } -.bi-calendar2-x-fill::before { content: "\f20a"; } -.bi-calendar2-x::before { content: "\f20b"; } -.bi-calendar2::before { content: "\f20c"; } -.bi-calendar3-event-fill::before { content: "\f20d"; } -.bi-calendar3-event::before { content: "\f20e"; } -.bi-calendar3-fill::before { content: "\f20f"; } -.bi-calendar3-range-fill::before { content: "\f210"; } -.bi-calendar3-range::before { content: "\f211"; } -.bi-calendar3-week-fill::before { content: "\f212"; } -.bi-calendar3-week::before { content: "\f213"; } -.bi-calendar3::before { content: "\f214"; } -.bi-calendar4-event::before { content: "\f215"; } -.bi-calendar4-range::before { content: "\f216"; } -.bi-calendar4-week::before { content: "\f217"; } -.bi-calendar4::before { content: "\f218"; } -.bi-camera-fill::before { content: "\f219"; } -.bi-camera-reels-fill::before { content: "\f21a"; } -.bi-camera-reels::before { content: "\f21b"; } -.bi-camera-video-fill::before { content: "\f21c"; } -.bi-camera-video-off-fill::before { content: "\f21d"; } -.bi-camera-video-off::before { content: "\f21e"; } -.bi-camera-video::before { content: "\f21f"; } -.bi-camera::before { content: "\f220"; } -.bi-camera2::before { content: "\f221"; } -.bi-capslock-fill::before { content: "\f222"; } -.bi-capslock::before { content: "\f223"; } -.bi-card-checklist::before { content: "\f224"; } -.bi-card-heading::before { content: "\f225"; } -.bi-card-image::before { content: "\f226"; } -.bi-card-list::before { content: "\f227"; } -.bi-card-text::before { content: "\f228"; } -.bi-caret-down-fill::before { content: "\f229"; } -.bi-caret-down-square-fill::before { content: "\f22a"; } -.bi-caret-down-square::before { content: "\f22b"; } -.bi-caret-down::before { content: "\f22c"; } -.bi-caret-left-fill::before { content: "\f22d"; } -.bi-caret-left-square-fill::before { content: "\f22e"; } -.bi-caret-left-square::before { content: "\f22f"; } -.bi-caret-left::before { content: "\f230"; } -.bi-caret-right-fill::before { content: "\f231"; } -.bi-caret-right-square-fill::before { content: "\f232"; } -.bi-caret-right-square::before { content: "\f233"; } -.bi-caret-right::before { content: "\f234"; } -.bi-caret-up-fill::before { content: "\f235"; } -.bi-caret-up-square-fill::before { content: "\f236"; } -.bi-caret-up-square::before { content: "\f237"; } -.bi-caret-up::before { content: "\f238"; } -.bi-cart-check-fill::before { content: "\f239"; } -.bi-cart-check::before { content: "\f23a"; } -.bi-cart-dash-fill::before { content: "\f23b"; } -.bi-cart-dash::before { content: "\f23c"; } -.bi-cart-fill::before { content: "\f23d"; } -.bi-cart-plus-fill::before { content: "\f23e"; } -.bi-cart-plus::before { content: "\f23f"; } -.bi-cart-x-fill::before { content: "\f240"; } -.bi-cart-x::before { content: "\f241"; } -.bi-cart::before { content: "\f242"; } -.bi-cart2::before { content: "\f243"; } -.bi-cart3::before { content: "\f244"; } -.bi-cart4::before { content: "\f245"; } -.bi-cash-stack::before { content: "\f246"; } -.bi-cash::before { content: "\f247"; } -.bi-cast::before { content: "\f248"; } -.bi-chat-dots-fill::before { content: "\f249"; } -.bi-chat-dots::before { content: "\f24a"; } -.bi-chat-fill::before { content: "\f24b"; } -.bi-chat-left-dots-fill::before { content: "\f24c"; } -.bi-chat-left-dots::before { content: "\f24d"; } -.bi-chat-left-fill::before { content: "\f24e"; } -.bi-chat-left-quote-fill::before { content: "\f24f"; } -.bi-chat-left-quote::before { content: "\f250"; } -.bi-chat-left-text-fill::before { content: "\f251"; } -.bi-chat-left-text::before { content: "\f252"; } -.bi-chat-left::before { content: "\f253"; } -.bi-chat-quote-fill::before { content: "\f254"; } -.bi-chat-quote::before { content: "\f255"; } -.bi-chat-right-dots-fill::before { content: "\f256"; } -.bi-chat-right-dots::before { content: "\f257"; } -.bi-chat-right-fill::before { content: "\f258"; } -.bi-chat-right-quote-fill::before { content: "\f259"; } -.bi-chat-right-quote::before { content: "\f25a"; } -.bi-chat-right-text-fill::before { content: "\f25b"; } -.bi-chat-right-text::before { content: "\f25c"; } -.bi-chat-right::before { content: "\f25d"; } -.bi-chat-square-dots-fill::before { content: "\f25e"; } -.bi-chat-square-dots::before { content: "\f25f"; } -.bi-chat-square-fill::before { content: "\f260"; } -.bi-chat-square-quote-fill::before { content: "\f261"; } -.bi-chat-square-quote::before { content: "\f262"; } -.bi-chat-square-text-fill::before { content: "\f263"; } -.bi-chat-square-text::before { content: "\f264"; } -.bi-chat-square::before { content: "\f265"; } -.bi-chat-text-fill::before { content: "\f266"; } -.bi-chat-text::before { content: "\f267"; } -.bi-chat::before { content: "\f268"; } -.bi-check-all::before { content: "\f269"; } -.bi-check-circle-fill::before { content: "\f26a"; } -.bi-check-circle::before { content: "\f26b"; } -.bi-check-square-fill::before { content: "\f26c"; } -.bi-check-square::before { content: "\f26d"; } -.bi-check::before { content: "\f26e"; } -.bi-check2-all::before { content: "\f26f"; } -.bi-check2-circle::before { content: "\f270"; } -.bi-check2-square::before { content: "\f271"; } -.bi-check2::before { content: "\f272"; } -.bi-chevron-bar-contract::before { content: "\f273"; } -.bi-chevron-bar-down::before { content: "\f274"; } -.bi-chevron-bar-expand::before { content: "\f275"; } -.bi-chevron-bar-left::before { content: "\f276"; } -.bi-chevron-bar-right::before { content: "\f277"; } -.bi-chevron-bar-up::before { content: "\f278"; } -.bi-chevron-compact-down::before { content: "\f279"; } -.bi-chevron-compact-left::before { content: "\f27a"; } -.bi-chevron-compact-right::before { content: "\f27b"; } -.bi-chevron-compact-up::before { content: "\f27c"; } -.bi-chevron-contract::before { content: "\f27d"; } -.bi-chevron-double-down::before { content: "\f27e"; } -.bi-chevron-double-left::before { content: "\f27f"; } -.bi-chevron-double-right::before { content: "\f280"; } -.bi-chevron-double-up::before { content: "\f281"; } -.bi-chevron-down::before { content: "\f282"; } -.bi-chevron-expand::before { content: "\f283"; } -.bi-chevron-left::before { content: "\f284"; } -.bi-chevron-right::before { content: "\f285"; } -.bi-chevron-up::before { content: "\f286"; } -.bi-circle-fill::before { content: "\f287"; } -.bi-circle-half::before { content: "\f288"; } -.bi-circle-square::before { content: "\f289"; } -.bi-circle::before { content: "\f28a"; } -.bi-clipboard-check::before { content: "\f28b"; } -.bi-clipboard-data::before { content: "\f28c"; } -.bi-clipboard-minus::before { content: "\f28d"; } -.bi-clipboard-plus::before { content: "\f28e"; } -.bi-clipboard-x::before { content: "\f28f"; } -.bi-clipboard::before { content: "\f290"; } -.bi-clock-fill::before { content: "\f291"; } -.bi-clock-history::before { content: "\f292"; } -.bi-clock::before { content: "\f293"; } -.bi-cloud-arrow-down-fill::before { content: "\f294"; } -.bi-cloud-arrow-down::before { content: "\f295"; } -.bi-cloud-arrow-up-fill::before { content: "\f296"; } -.bi-cloud-arrow-up::before { content: "\f297"; } -.bi-cloud-check-fill::before { content: "\f298"; } -.bi-cloud-check::before { content: "\f299"; } -.bi-cloud-download-fill::before { content: "\f29a"; } -.bi-cloud-download::before { content: "\f29b"; } -.bi-cloud-drizzle-fill::before { content: "\f29c"; } -.bi-cloud-drizzle::before { content: "\f29d"; } -.bi-cloud-fill::before { content: "\f29e"; } -.bi-cloud-fog-fill::before { content: "\f29f"; } -.bi-cloud-fog::before { content: "\f2a0"; } -.bi-cloud-fog2-fill::before { content: "\f2a1"; } -.bi-cloud-fog2::before { content: "\f2a2"; } -.bi-cloud-hail-fill::before { content: "\f2a3"; } -.bi-cloud-hail::before { content: "\f2a4"; } -.bi-cloud-haze-1::before { content: "\f2a5"; } -.bi-cloud-haze-fill::before { content: "\f2a6"; } -.bi-cloud-haze::before { content: "\f2a7"; } -.bi-cloud-haze2-fill::before { content: "\f2a8"; } -.bi-cloud-lightning-fill::before { content: "\f2a9"; } -.bi-cloud-lightning-rain-fill::before { content: "\f2aa"; } -.bi-cloud-lightning-rain::before { content: "\f2ab"; } -.bi-cloud-lightning::before { content: "\f2ac"; } -.bi-cloud-minus-fill::before { content: "\f2ad"; } -.bi-cloud-minus::before { content: "\f2ae"; } -.bi-cloud-moon-fill::before { content: "\f2af"; } -.bi-cloud-moon::before { content: "\f2b0"; } -.bi-cloud-plus-fill::before { content: "\f2b1"; } -.bi-cloud-plus::before { content: "\f2b2"; } -.bi-cloud-rain-fill::before { content: "\f2b3"; } -.bi-cloud-rain-heavy-fill::before { content: "\f2b4"; } -.bi-cloud-rain-heavy::before { content: "\f2b5"; } -.bi-cloud-rain::before { content: "\f2b6"; } -.bi-cloud-slash-fill::before { content: "\f2b7"; } -.bi-cloud-slash::before { content: "\f2b8"; } -.bi-cloud-sleet-fill::before { content: "\f2b9"; } -.bi-cloud-sleet::before { content: "\f2ba"; } -.bi-cloud-snow-fill::before { content: "\f2bb"; } -.bi-cloud-snow::before { content: "\f2bc"; } -.bi-cloud-sun-fill::before { content: "\f2bd"; } -.bi-cloud-sun::before { content: "\f2be"; } -.bi-cloud-upload-fill::before { content: "\f2bf"; } -.bi-cloud-upload::before { content: "\f2c0"; } -.bi-cloud::before { content: "\f2c1"; } -.bi-clouds-fill::before { content: "\f2c2"; } -.bi-clouds::before { content: "\f2c3"; } -.bi-cloudy-fill::before { content: "\f2c4"; } -.bi-cloudy::before { content: "\f2c5"; } -.bi-code-slash::before { content: "\f2c6"; } -.bi-code-square::before { content: "\f2c7"; } -.bi-code::before { content: "\f2c8"; } -.bi-collection-fill::before { content: "\f2c9"; } -.bi-collection-play-fill::before { content: "\f2ca"; } -.bi-collection-play::before { content: "\f2cb"; } -.bi-collection::before { content: "\f2cc"; } -.bi-columns-gap::before { content: "\f2cd"; } -.bi-columns::before { content: "\f2ce"; } -.bi-command::before { content: "\f2cf"; } -.bi-compass-fill::before { content: "\f2d0"; } -.bi-compass::before { content: "\f2d1"; } -.bi-cone-striped::before { content: "\f2d2"; } -.bi-cone::before { content: "\f2d3"; } -.bi-controller::before { content: "\f2d4"; } -.bi-cpu-fill::before { content: "\f2d5"; } -.bi-cpu::before { content: "\f2d6"; } -.bi-credit-card-2-back-fill::before { content: "\f2d7"; } -.bi-credit-card-2-back::before { content: "\f2d8"; } -.bi-credit-card-2-front-fill::before { content: "\f2d9"; } -.bi-credit-card-2-front::before { content: "\f2da"; } -.bi-credit-card-fill::before { content: "\f2db"; } -.bi-credit-card::before { content: "\f2dc"; } -.bi-crop::before { content: "\f2dd"; } -.bi-cup-fill::before { content: "\f2de"; } -.bi-cup-straw::before { content: "\f2df"; } -.bi-cup::before { content: "\f2e0"; } -.bi-cursor-fill::before { content: "\f2e1"; } -.bi-cursor-text::before { content: "\f2e2"; } -.bi-cursor::before { content: "\f2e3"; } -.bi-dash-circle-dotted::before { content: "\f2e4"; } -.bi-dash-circle-fill::before { content: "\f2e5"; } -.bi-dash-circle::before { content: "\f2e6"; } -.bi-dash-square-dotted::before { content: "\f2e7"; } -.bi-dash-square-fill::before { content: "\f2e8"; } -.bi-dash-square::before { content: "\f2e9"; } -.bi-dash::before { content: "\f2ea"; } -.bi-diagram-2-fill::before { content: "\f2eb"; } -.bi-diagram-2::before { content: "\f2ec"; } -.bi-diagram-3-fill::before { content: "\f2ed"; } -.bi-diagram-3::before { content: "\f2ee"; } -.bi-diamond-fill::before { content: "\f2ef"; } -.bi-diamond-half::before { content: "\f2f0"; } -.bi-diamond::before { content: "\f2f1"; } -.bi-dice-1-fill::before { content: "\f2f2"; } -.bi-dice-1::before { content: "\f2f3"; } -.bi-dice-2-fill::before { content: "\f2f4"; } -.bi-dice-2::before { content: "\f2f5"; } -.bi-dice-3-fill::before { content: "\f2f6"; } -.bi-dice-3::before { content: "\f2f7"; } -.bi-dice-4-fill::before { content: "\f2f8"; } -.bi-dice-4::before { content: "\f2f9"; } -.bi-dice-5-fill::before { content: "\f2fa"; } -.bi-dice-5::before { content: "\f2fb"; } -.bi-dice-6-fill::before { content: "\f2fc"; } -.bi-dice-6::before { content: "\f2fd"; } -.bi-disc-fill::before { content: "\f2fe"; } -.bi-disc::before { content: "\f2ff"; } -.bi-discord::before { content: "\f300"; } -.bi-display-fill::before { content: "\f301"; } -.bi-display::before { content: "\f302"; } -.bi-distribute-horizontal::before { content: "\f303"; } -.bi-distribute-vertical::before { content: "\f304"; } -.bi-door-closed-fill::before { content: "\f305"; } -.bi-door-closed::before { content: "\f306"; } -.bi-door-open-fill::before { content: "\f307"; } -.bi-door-open::before { content: "\f308"; } -.bi-dot::before { content: "\f309"; } -.bi-download::before { content: "\f30a"; } -.bi-droplet-fill::before { content: "\f30b"; } -.bi-droplet-half::before { content: "\f30c"; } -.bi-droplet::before { content: "\f30d"; } -.bi-earbuds::before { content: "\f30e"; } -.bi-easel-fill::before { content: "\f30f"; } -.bi-easel::before { content: "\f310"; } -.bi-egg-fill::before { content: "\f311"; } -.bi-egg-fried::before { content: "\f312"; } -.bi-egg::before { content: "\f313"; } -.bi-eject-fill::before { content: "\f314"; } -.bi-eject::before { content: "\f315"; } -.bi-emoji-angry-fill::before { content: "\f316"; } -.bi-emoji-angry::before { content: "\f317"; } -.bi-emoji-dizzy-fill::before { content: "\f318"; } -.bi-emoji-dizzy::before { content: "\f319"; } -.bi-emoji-expressionless-fill::before { content: "\f31a"; } -.bi-emoji-expressionless::before { content: "\f31b"; } -.bi-emoji-frown-fill::before { content: "\f31c"; } -.bi-emoji-frown::before { content: "\f31d"; } -.bi-emoji-heart-eyes-fill::before { content: "\f31e"; } -.bi-emoji-heart-eyes::before { content: "\f31f"; } -.bi-emoji-laughing-fill::before { content: "\f320"; } -.bi-emoji-laughing::before { content: "\f321"; } -.bi-emoji-neutral-fill::before { content: "\f322"; } -.bi-emoji-neutral::before { content: "\f323"; } -.bi-emoji-smile-fill::before { content: "\f324"; } -.bi-emoji-smile-upside-down-fill::before { content: "\f325"; } -.bi-emoji-smile-upside-down::before { content: "\f326"; } -.bi-emoji-smile::before { content: "\f327"; } -.bi-emoji-sunglasses-fill::before { content: "\f328"; } -.bi-emoji-sunglasses::before { content: "\f329"; } -.bi-emoji-wink-fill::before { content: "\f32a"; } -.bi-emoji-wink::before { content: "\f32b"; } -.bi-envelope-fill::before { content: "\f32c"; } -.bi-envelope-open-fill::before { content: "\f32d"; } -.bi-envelope-open::before { content: "\f32e"; } -.bi-envelope::before { content: "\f32f"; } -.bi-eraser-fill::before { content: "\f330"; } -.bi-eraser::before { content: "\f331"; } -.bi-exclamation-circle-fill::before { content: "\f332"; } -.bi-exclamation-circle::before { content: "\f333"; } -.bi-exclamation-diamond-fill::before { content: "\f334"; } -.bi-exclamation-diamond::before { content: "\f335"; } -.bi-exclamation-octagon-fill::before { content: "\f336"; } -.bi-exclamation-octagon::before { content: "\f337"; } -.bi-exclamation-square-fill::before { content: "\f338"; } -.bi-exclamation-square::before { content: "\f339"; } -.bi-exclamation-triangle-fill::before { content: "\f33a"; } -.bi-exclamation-triangle::before { content: "\f33b"; } -.bi-exclamation::before { content: "\f33c"; } -.bi-exclude::before { content: "\f33d"; } -.bi-eye-fill::before { content: "\f33e"; } -.bi-eye-slash-fill::before { content: "\f33f"; } -.bi-eye-slash::before { content: "\f340"; } -.bi-eye::before { content: "\f341"; } -.bi-eyedropper::before { content: "\f342"; } -.bi-eyeglasses::before { content: "\f343"; } -.bi-facebook::before { content: "\f344"; } -.bi-file-arrow-down-fill::before { content: "\f345"; } -.bi-file-arrow-down::before { content: "\f346"; } -.bi-file-arrow-up-fill::before { content: "\f347"; } -.bi-file-arrow-up::before { content: "\f348"; } -.bi-file-bar-graph-fill::before { content: "\f349"; } -.bi-file-bar-graph::before { content: "\f34a"; } -.bi-file-binary-fill::before { content: "\f34b"; } -.bi-file-binary::before { content: "\f34c"; } -.bi-file-break-fill::before { content: "\f34d"; } -.bi-file-break::before { content: "\f34e"; } -.bi-file-check-fill::before { content: "\f34f"; } -.bi-file-check::before { content: "\f350"; } -.bi-file-code-fill::before { content: "\f351"; } -.bi-file-code::before { content: "\f352"; } -.bi-file-diff-fill::before { content: "\f353"; } -.bi-file-diff::before { content: "\f354"; } -.bi-file-earmark-arrow-down-fill::before { content: "\f355"; } -.bi-file-earmark-arrow-down::before { content: "\f356"; } -.bi-file-earmark-arrow-up-fill::before { content: "\f357"; } -.bi-file-earmark-arrow-up::before { content: "\f358"; } -.bi-file-earmark-bar-graph-fill::before { content: "\f359"; } -.bi-file-earmark-bar-graph::before { content: "\f35a"; } -.bi-file-earmark-binary-fill::before { content: "\f35b"; } -.bi-file-earmark-binary::before { content: "\f35c"; } -.bi-file-earmark-break-fill::before { content: "\f35d"; } -.bi-file-earmark-break::before { content: "\f35e"; } -.bi-file-earmark-check-fill::before { content: "\f35f"; } -.bi-file-earmark-check::before { content: "\f360"; } -.bi-file-earmark-code-fill::before { content: "\f361"; } -.bi-file-earmark-code::before { content: "\f362"; } -.bi-file-earmark-diff-fill::before { content: "\f363"; } -.bi-file-earmark-diff::before { content: "\f364"; } -.bi-file-earmark-easel-fill::before { content: "\f365"; } -.bi-file-earmark-easel::before { content: "\f366"; } -.bi-file-earmark-excel-fill::before { content: "\f367"; } -.bi-file-earmark-excel::before { content: "\f368"; } -.bi-file-earmark-fill::before { content: "\f369"; } -.bi-file-earmark-font-fill::before { content: "\f36a"; } -.bi-file-earmark-font::before { content: "\f36b"; } -.bi-file-earmark-image-fill::before { content: "\f36c"; } -.bi-file-earmark-image::before { content: "\f36d"; } -.bi-file-earmark-lock-fill::before { content: "\f36e"; } -.bi-file-earmark-lock::before { content: "\f36f"; } -.bi-file-earmark-lock2-fill::before { content: "\f370"; } -.bi-file-earmark-lock2::before { content: "\f371"; } -.bi-file-earmark-medical-fill::before { content: "\f372"; } -.bi-file-earmark-medical::before { content: "\f373"; } -.bi-file-earmark-minus-fill::before { content: "\f374"; } -.bi-file-earmark-minus::before { content: "\f375"; } -.bi-file-earmark-music-fill::before { content: "\f376"; } -.bi-file-earmark-music::before { content: "\f377"; } -.bi-file-earmark-person-fill::before { content: "\f378"; } -.bi-file-earmark-person::before { content: "\f379"; } -.bi-file-earmark-play-fill::before { content: "\f37a"; } -.bi-file-earmark-play::before { content: "\f37b"; } -.bi-file-earmark-plus-fill::before { content: "\f37c"; } -.bi-file-earmark-plus::before { content: "\f37d"; } -.bi-file-earmark-post-fill::before { content: "\f37e"; } -.bi-file-earmark-post::before { content: "\f37f"; } -.bi-file-earmark-ppt-fill::before { content: "\f380"; } -.bi-file-earmark-ppt::before { content: "\f381"; } -.bi-file-earmark-richtext-fill::before { content: "\f382"; } -.bi-file-earmark-richtext::before { content: "\f383"; } -.bi-file-earmark-ruled-fill::before { content: "\f384"; } -.bi-file-earmark-ruled::before { content: "\f385"; } -.bi-file-earmark-slides-fill::before { content: "\f386"; } -.bi-file-earmark-slides::before { content: "\f387"; } -.bi-file-earmark-spreadsheet-fill::before { content: "\f388"; } -.bi-file-earmark-spreadsheet::before { content: "\f389"; } -.bi-file-earmark-text-fill::before { content: "\f38a"; } -.bi-file-earmark-text::before { content: "\f38b"; } -.bi-file-earmark-word-fill::before { content: "\f38c"; } -.bi-file-earmark-word::before { content: "\f38d"; } -.bi-file-earmark-x-fill::before { content: "\f38e"; } -.bi-file-earmark-x::before { content: "\f38f"; } -.bi-file-earmark-zip-fill::before { content: "\f390"; } -.bi-file-earmark-zip::before { content: "\f391"; } -.bi-file-earmark::before { content: "\f392"; } -.bi-file-easel-fill::before { content: "\f393"; } -.bi-file-easel::before { content: "\f394"; } -.bi-file-excel-fill::before { content: "\f395"; } -.bi-file-excel::before { content: "\f396"; } -.bi-file-fill::before { content: "\f397"; } -.bi-file-font-fill::before { content: "\f398"; } -.bi-file-font::before { content: "\f399"; } -.bi-file-image-fill::before { content: "\f39a"; } -.bi-file-image::before { content: "\f39b"; } -.bi-file-lock-fill::before { content: "\f39c"; } -.bi-file-lock::before { content: "\f39d"; } -.bi-file-lock2-fill::before { content: "\f39e"; } -.bi-file-lock2::before { content: "\f39f"; } -.bi-file-medical-fill::before { content: "\f3a0"; } -.bi-file-medical::before { content: "\f3a1"; } -.bi-file-minus-fill::before { content: "\f3a2"; } -.bi-file-minus::before { content: "\f3a3"; } -.bi-file-music-fill::before { content: "\f3a4"; } -.bi-file-music::before { content: "\f3a5"; } -.bi-file-person-fill::before { content: "\f3a6"; } -.bi-file-person::before { content: "\f3a7"; } -.bi-file-play-fill::before { content: "\f3a8"; } -.bi-file-play::before { content: "\f3a9"; } -.bi-file-plus-fill::before { content: "\f3aa"; } -.bi-file-plus::before { content: "\f3ab"; } -.bi-file-post-fill::before { content: "\f3ac"; } -.bi-file-post::before { content: "\f3ad"; } -.bi-file-ppt-fill::before { content: "\f3ae"; } -.bi-file-ppt::before { content: "\f3af"; } -.bi-file-richtext-fill::before { content: "\f3b0"; } -.bi-file-richtext::before { content: "\f3b1"; } -.bi-file-ruled-fill::before { content: "\f3b2"; } -.bi-file-ruled::before { content: "\f3b3"; } -.bi-file-slides-fill::before { content: "\f3b4"; } -.bi-file-slides::before { content: "\f3b5"; } -.bi-file-spreadsheet-fill::before { content: "\f3b6"; } -.bi-file-spreadsheet::before { content: "\f3b7"; } -.bi-file-text-fill::before { content: "\f3b8"; } -.bi-file-text::before { content: "\f3b9"; } -.bi-file-word-fill::before { content: "\f3ba"; } -.bi-file-word::before { content: "\f3bb"; } -.bi-file-x-fill::before { content: "\f3bc"; } -.bi-file-x::before { content: "\f3bd"; } -.bi-file-zip-fill::before { content: "\f3be"; } -.bi-file-zip::before { content: "\f3bf"; } -.bi-file::before { content: "\f3c0"; } -.bi-files-alt::before { content: "\f3c1"; } -.bi-files::before { content: "\f3c2"; } -.bi-film::before { content: "\f3c3"; } -.bi-filter-circle-fill::before { content: "\f3c4"; } -.bi-filter-circle::before { content: "\f3c5"; } -.bi-filter-left::before { content: "\f3c6"; } -.bi-filter-right::before { content: "\f3c7"; } -.bi-filter-square-fill::before { content: "\f3c8"; } -.bi-filter-square::before { content: "\f3c9"; } -.bi-filter::before { content: "\f3ca"; } -.bi-flag-fill::before { content: "\f3cb"; } -.bi-flag::before { content: "\f3cc"; } -.bi-flower1::before { content: "\f3cd"; } -.bi-flower2::before { content: "\f3ce"; } -.bi-flower3::before { content: "\f3cf"; } -.bi-folder-check::before { content: "\f3d0"; } -.bi-folder-fill::before { content: "\f3d1"; } -.bi-folder-minus::before { content: "\f3d2"; } -.bi-folder-plus::before { content: "\f3d3"; } -.bi-folder-symlink-fill::before { content: "\f3d4"; } -.bi-folder-symlink::before { content: "\f3d5"; } -.bi-folder-x::before { content: "\f3d6"; } -.bi-folder::before { content: "\f3d7"; } -.bi-folder2-open::before { content: "\f3d8"; } -.bi-folder2::before { content: "\f3d9"; } -.bi-fonts::before { content: "\f3da"; } -.bi-forward-fill::before { content: "\f3db"; } -.bi-forward::before { content: "\f3dc"; } -.bi-front::before { content: "\f3dd"; } -.bi-fullscreen-exit::before { content: "\f3de"; } -.bi-fullscreen::before { content: "\f3df"; } -.bi-funnel-fill::before { content: "\f3e0"; } -.bi-funnel::before { content: "\f3e1"; } -.bi-gear-fill::before { content: "\f3e2"; } -.bi-gear-wide-connected::before { content: "\f3e3"; } -.bi-gear-wide::before { content: "\f3e4"; } -.bi-gear::before { content: "\f3e5"; } -.bi-gem::before { content: "\f3e6"; } -.bi-geo-alt-fill::before { content: "\f3e7"; } -.bi-geo-alt::before { content: "\f3e8"; } -.bi-geo-fill::before { content: "\f3e9"; } -.bi-geo::before { content: "\f3ea"; } -.bi-gift-fill::before { content: "\f3eb"; } -.bi-gift::before { content: "\f3ec"; } -.bi-github::before { content: "\f3ed"; } -.bi-globe::before { content: "\f3ee"; } -.bi-globe2::before { content: "\f3ef"; } -.bi-google::before { content: "\f3f0"; } -.bi-graph-down::before { content: "\f3f1"; } -.bi-graph-up::before { content: "\f3f2"; } -.bi-grid-1x2-fill::before { content: "\f3f3"; } -.bi-grid-1x2::before { content: "\f3f4"; } -.bi-grid-3x2-gap-fill::before { content: "\f3f5"; } -.bi-grid-3x2-gap::before { content: "\f3f6"; } -.bi-grid-3x2::before { content: "\f3f7"; } -.bi-grid-3x3-gap-fill::before { content: "\f3f8"; } -.bi-grid-3x3-gap::before { content: "\f3f9"; } -.bi-grid-3x3::before { content: "\f3fa"; } -.bi-grid-fill::before { content: "\f3fb"; } -.bi-grid::before { content: "\f3fc"; } -.bi-grip-horizontal::before { content: "\f3fd"; } -.bi-grip-vertical::before { content: "\f3fe"; } -.bi-hammer::before { content: "\f3ff"; } -.bi-hand-index-fill::before { content: "\f400"; } -.bi-hand-index-thumb-fill::before { content: "\f401"; } -.bi-hand-index-thumb::before { content: "\f402"; } -.bi-hand-index::before { content: "\f403"; } -.bi-hand-thumbs-down-fill::before { content: "\f404"; } -.bi-hand-thumbs-down::before { content: "\f405"; } -.bi-hand-thumbs-up-fill::before { content: "\f406"; } -.bi-hand-thumbs-up::before { content: "\f407"; } -.bi-handbag-fill::before { content: "\f408"; } -.bi-handbag::before { content: "\f409"; } -.bi-hash::before { content: "\f40a"; } -.bi-hdd-fill::before { content: "\f40b"; } -.bi-hdd-network-fill::before { content: "\f40c"; } -.bi-hdd-network::before { content: "\f40d"; } -.bi-hdd-rack-fill::before { content: "\f40e"; } -.bi-hdd-rack::before { content: "\f40f"; } -.bi-hdd-stack-fill::before { content: "\f410"; } -.bi-hdd-stack::before { content: "\f411"; } -.bi-hdd::before { content: "\f412"; } -.bi-headphones::before { content: "\f413"; } -.bi-headset::before { content: "\f414"; } -.bi-heart-fill::before { content: "\f415"; } -.bi-heart-half::before { content: "\f416"; } -.bi-heart::before { content: "\f417"; } -.bi-heptagon-fill::before { content: "\f418"; } -.bi-heptagon-half::before { content: "\f419"; } -.bi-heptagon::before { content: "\f41a"; } -.bi-hexagon-fill::before { content: "\f41b"; } -.bi-hexagon-half::before { content: "\f41c"; } -.bi-hexagon::before { content: "\f41d"; } -.bi-hourglass-bottom::before { content: "\f41e"; } -.bi-hourglass-split::before { content: "\f41f"; } -.bi-hourglass-top::before { content: "\f420"; } -.bi-hourglass::before { content: "\f421"; } -.bi-house-door-fill::before { content: "\f422"; } -.bi-house-door::before { content: "\f423"; } -.bi-house-fill::before { content: "\f424"; } -.bi-house::before { content: "\f425"; } -.bi-hr::before { content: "\f426"; } -.bi-hurricane::before { content: "\f427"; } -.bi-image-alt::before { content: "\f428"; } -.bi-image-fill::before { content: "\f429"; } -.bi-image::before { content: "\f42a"; } -.bi-images::before { content: "\f42b"; } -.bi-inbox-fill::before { content: "\f42c"; } -.bi-inbox::before { content: "\f42d"; } -.bi-inboxes-fill::before { content: "\f42e"; } -.bi-inboxes::before { content: "\f42f"; } -.bi-info-circle-fill::before { content: "\f430"; } -.bi-info-circle::before { content: "\f431"; } -.bi-info-square-fill::before { content: "\f432"; } -.bi-info-square::before { content: "\f433"; } -.bi-info::before { content: "\f434"; } -.bi-input-cursor-text::before { content: "\f435"; } -.bi-input-cursor::before { content: "\f436"; } -.bi-instagram::before { content: "\f437"; } -.bi-intersect::before { content: "\f438"; } -.bi-journal-album::before { content: "\f439"; } -.bi-journal-arrow-down::before { content: "\f43a"; } -.bi-journal-arrow-up::before { content: "\f43b"; } -.bi-journal-bookmark-fill::before { content: "\f43c"; } -.bi-journal-bookmark::before { content: "\f43d"; } -.bi-journal-check::before { content: "\f43e"; } -.bi-journal-code::before { content: "\f43f"; } -.bi-journal-medical::before { content: "\f440"; } -.bi-journal-minus::before { content: "\f441"; } -.bi-journal-plus::before { content: "\f442"; } -.bi-journal-richtext::before { content: "\f443"; } -.bi-journal-text::before { content: "\f444"; } -.bi-journal-x::before { content: "\f445"; } -.bi-journal::before { content: "\f446"; } -.bi-journals::before { content: "\f447"; } -.bi-joystick::before { content: "\f448"; } -.bi-justify-left::before { content: "\f449"; } -.bi-justify-right::before { content: "\f44a"; } -.bi-justify::before { content: "\f44b"; } -.bi-kanban-fill::before { content: "\f44c"; } -.bi-kanban::before { content: "\f44d"; } -.bi-key-fill::before { content: "\f44e"; } -.bi-key::before { content: "\f44f"; } -.bi-keyboard-fill::before { content: "\f450"; } -.bi-keyboard::before { content: "\f451"; } -.bi-ladder::before { content: "\f452"; } -.bi-lamp-fill::before { content: "\f453"; } -.bi-lamp::before { content: "\f454"; } -.bi-laptop-fill::before { content: "\f455"; } -.bi-laptop::before { content: "\f456"; } -.bi-layer-backward::before { content: "\f457"; } -.bi-layer-forward::before { content: "\f458"; } -.bi-layers-fill::before { content: "\f459"; } -.bi-layers-half::before { content: "\f45a"; } -.bi-layers::before { content: "\f45b"; } -.bi-layout-sidebar-inset-reverse::before { content: "\f45c"; } -.bi-layout-sidebar-inset::before { content: "\f45d"; } -.bi-layout-sidebar-reverse::before { content: "\f45e"; } -.bi-layout-sidebar::before { content: "\f45f"; } -.bi-layout-split::before { content: "\f460"; } -.bi-layout-text-sidebar-reverse::before { content: "\f461"; } -.bi-layout-text-sidebar::before { content: "\f462"; } -.bi-layout-text-window-reverse::before { content: "\f463"; } -.bi-layout-text-window::before { content: "\f464"; } -.bi-layout-three-columns::before { content: "\f465"; } -.bi-layout-wtf::before { content: "\f466"; } -.bi-life-preserver::before { content: "\f467"; } -.bi-lightbulb-fill::before { content: "\f468"; } -.bi-lightbulb-off-fill::before { content: "\f469"; } -.bi-lightbulb-off::before { content: "\f46a"; } -.bi-lightbulb::before { content: "\f46b"; } -.bi-lightning-charge-fill::before { content: "\f46c"; } -.bi-lightning-charge::before { content: "\f46d"; } -.bi-lightning-fill::before { content: "\f46e"; } -.bi-lightning::before { content: "\f46f"; } -.bi-link-45deg::before { content: "\f470"; } -.bi-link::before { content: "\f471"; } -.bi-linkedin::before { content: "\f472"; } -.bi-list-check::before { content: "\f473"; } -.bi-list-nested::before { content: "\f474"; } -.bi-list-ol::before { content: "\f475"; } -.bi-list-stars::before { content: "\f476"; } -.bi-list-task::before { content: "\f477"; } -.bi-list-ul::before { content: "\f478"; } -.bi-list::before { content: "\f479"; } -.bi-lock-fill::before { content: "\f47a"; } -.bi-lock::before { content: "\f47b"; } -.bi-mailbox::before { content: "\f47c"; } -.bi-mailbox2::before { content: "\f47d"; } -.bi-map-fill::before { content: "\f47e"; } -.bi-map::before { content: "\f47f"; } -.bi-markdown-fill::before { content: "\f480"; } -.bi-markdown::before { content: "\f481"; } -.bi-mask::before { content: "\f482"; } -.bi-megaphone-fill::before { content: "\f483"; } -.bi-megaphone::before { content: "\f484"; } -.bi-menu-app-fill::before { content: "\f485"; } -.bi-menu-app::before { content: "\f486"; } -.bi-menu-button-fill::before { content: "\f487"; } -.bi-menu-button-wide-fill::before { content: "\f488"; } -.bi-menu-button-wide::before { content: "\f489"; } -.bi-menu-button::before { content: "\f48a"; } -.bi-menu-down::before { content: "\f48b"; } -.bi-menu-up::before { content: "\f48c"; } -.bi-mic-fill::before { content: "\f48d"; } -.bi-mic-mute-fill::before { content: "\f48e"; } -.bi-mic-mute::before { content: "\f48f"; } -.bi-mic::before { content: "\f490"; } -.bi-minecart-loaded::before { content: "\f491"; } -.bi-minecart::before { content: "\f492"; } -.bi-moisture::before { content: "\f493"; } -.bi-moon-fill::before { content: "\f494"; } -.bi-moon-stars-fill::before { content: "\f495"; } -.bi-moon-stars::before { content: "\f496"; } -.bi-moon::before { content: "\f497"; } -.bi-mouse-fill::before { content: "\f498"; } -.bi-mouse::before { content: "\f499"; } -.bi-mouse2-fill::before { content: "\f49a"; } -.bi-mouse2::before { content: "\f49b"; } -.bi-mouse3-fill::before { content: "\f49c"; } -.bi-mouse3::before { content: "\f49d"; } -.bi-music-note-beamed::before { content: "\f49e"; } -.bi-music-note-list::before { content: "\f49f"; } -.bi-music-note::before { content: "\f4a0"; } -.bi-music-player-fill::before { content: "\f4a1"; } -.bi-music-player::before { content: "\f4a2"; } -.bi-newspaper::before { content: "\f4a3"; } -.bi-node-minus-fill::before { content: "\f4a4"; } -.bi-node-minus::before { content: "\f4a5"; } -.bi-node-plus-fill::before { content: "\f4a6"; } -.bi-node-plus::before { content: "\f4a7"; } -.bi-nut-fill::before { content: "\f4a8"; } -.bi-nut::before { content: "\f4a9"; } -.bi-octagon-fill::before { content: "\f4aa"; } -.bi-octagon-half::before { content: "\f4ab"; } -.bi-octagon::before { content: "\f4ac"; } -.bi-option::before { content: "\f4ad"; } -.bi-outlet::before { content: "\f4ae"; } -.bi-paint-bucket::before { content: "\f4af"; } -.bi-palette-fill::before { content: "\f4b0"; } -.bi-palette::before { content: "\f4b1"; } -.bi-palette2::before { content: "\f4b2"; } -.bi-paperclip::before { content: "\f4b3"; } -.bi-paragraph::before { content: "\f4b4"; } -.bi-patch-check-fill::before { content: "\f4b5"; } -.bi-patch-check::before { content: "\f4b6"; } -.bi-patch-exclamation-fill::before { content: "\f4b7"; } -.bi-patch-exclamation::before { content: "\f4b8"; } -.bi-patch-minus-fill::before { content: "\f4b9"; } -.bi-patch-minus::before { content: "\f4ba"; } -.bi-patch-plus-fill::before { content: "\f4bb"; } -.bi-patch-plus::before { content: "\f4bc"; } -.bi-patch-question-fill::before { content: "\f4bd"; } -.bi-patch-question::before { content: "\f4be"; } -.bi-pause-btn-fill::before { content: "\f4bf"; } -.bi-pause-btn::before { content: "\f4c0"; } -.bi-pause-circle-fill::before { content: "\f4c1"; } -.bi-pause-circle::before { content: "\f4c2"; } -.bi-pause-fill::before { content: "\f4c3"; } -.bi-pause::before { content: "\f4c4"; } -.bi-peace-fill::before { content: "\f4c5"; } -.bi-peace::before { content: "\f4c6"; } -.bi-pen-fill::before { content: "\f4c7"; } -.bi-pen::before { content: "\f4c8"; } -.bi-pencil-fill::before { content: "\f4c9"; } -.bi-pencil-square::before { content: "\f4ca"; } -.bi-pencil::before { content: "\f4cb"; } -.bi-pentagon-fill::before { content: "\f4cc"; } -.bi-pentagon-half::before { content: "\f4cd"; } -.bi-pentagon::before { content: "\f4ce"; } -.bi-people-fill::before { content: "\f4cf"; } -.bi-people::before { content: "\f4d0"; } -.bi-percent::before { content: "\f4d1"; } -.bi-person-badge-fill::before { content: "\f4d2"; } -.bi-person-badge::before { content: "\f4d3"; } -.bi-person-bounding-box::before { content: "\f4d4"; } -.bi-person-check-fill::before { content: "\f4d5"; } -.bi-person-check::before { content: "\f4d6"; } -.bi-person-circle::before { content: "\f4d7"; } -.bi-person-dash-fill::before { content: "\f4d8"; } -.bi-person-dash::before { content: "\f4d9"; } -.bi-person-fill::before { content: "\f4da"; } -.bi-person-lines-fill::before { content: "\f4db"; } -.bi-person-plus-fill::before { content: "\f4dc"; } -.bi-person-plus::before { content: "\f4dd"; } -.bi-person-square::before { content: "\f4de"; } -.bi-person-x-fill::before { content: "\f4df"; } -.bi-person-x::before { content: "\f4e0"; } -.bi-person::before { content: "\f4e1"; } -.bi-phone-fill::before { content: "\f4e2"; } -.bi-phone-landscape-fill::before { content: "\f4e3"; } -.bi-phone-landscape::before { content: "\f4e4"; } -.bi-phone-vibrate-fill::before { content: "\f4e5"; } -.bi-phone-vibrate::before { content: "\f4e6"; } -.bi-phone::before { content: "\f4e7"; } -.bi-pie-chart-fill::before { content: "\f4e8"; } -.bi-pie-chart::before { content: "\f4e9"; } -.bi-pin-angle-fill::before { content: "\f4ea"; } -.bi-pin-angle::before { content: "\f4eb"; } -.bi-pin-fill::before { content: "\f4ec"; } -.bi-pin::before { content: "\f4ed"; } -.bi-pip-fill::before { content: "\f4ee"; } -.bi-pip::before { content: "\f4ef"; } -.bi-play-btn-fill::before { content: "\f4f0"; } -.bi-play-btn::before { content: "\f4f1"; } -.bi-play-circle-fill::before { content: "\f4f2"; } -.bi-play-circle::before { content: "\f4f3"; } -.bi-play-fill::before { content: "\f4f4"; } -.bi-play::before { content: "\f4f5"; } -.bi-plug-fill::before { content: "\f4f6"; } -.bi-plug::before { content: "\f4f7"; } -.bi-plus-circle-dotted::before { content: "\f4f8"; } -.bi-plus-circle-fill::before { content: "\f4f9"; } -.bi-plus-circle::before { content: "\f4fa"; } -.bi-plus-square-dotted::before { content: "\f4fb"; } -.bi-plus-square-fill::before { content: "\f4fc"; } -.bi-plus-square::before { content: "\f4fd"; } -.bi-plus::before { content: "\f4fe"; } -.bi-power::before { content: "\f4ff"; } -.bi-printer-fill::before { content: "\f500"; } -.bi-printer::before { content: "\f501"; } -.bi-puzzle-fill::before { content: "\f502"; } -.bi-puzzle::before { content: "\f503"; } -.bi-question-circle-fill::before { content: "\f504"; } -.bi-question-circle::before { content: "\f505"; } -.bi-question-diamond-fill::before { content: "\f506"; } -.bi-question-diamond::before { content: "\f507"; } -.bi-question-octagon-fill::before { content: "\f508"; } -.bi-question-octagon::before { content: "\f509"; } -.bi-question-square-fill::before { content: "\f50a"; } -.bi-question-square::before { content: "\f50b"; } -.bi-question::before { content: "\f50c"; } -.bi-rainbow::before { content: "\f50d"; } -.bi-receipt-cutoff::before { content: "\f50e"; } -.bi-receipt::before { content: "\f50f"; } -.bi-reception-0::before { content: "\f510"; } -.bi-reception-1::before { content: "\f511"; } -.bi-reception-2::before { content: "\f512"; } -.bi-reception-3::before { content: "\f513"; } -.bi-reception-4::before { content: "\f514"; } -.bi-record-btn-fill::before { content: "\f515"; } -.bi-record-btn::before { content: "\f516"; } -.bi-record-circle-fill::before { content: "\f517"; } -.bi-record-circle::before { content: "\f518"; } -.bi-record-fill::before { content: "\f519"; } -.bi-record::before { content: "\f51a"; } -.bi-record2-fill::before { content: "\f51b"; } -.bi-record2::before { content: "\f51c"; } -.bi-reply-all-fill::before { content: "\f51d"; } -.bi-reply-all::before { content: "\f51e"; } -.bi-reply-fill::before { content: "\f51f"; } -.bi-reply::before { content: "\f520"; } -.bi-rss-fill::before { content: "\f521"; } -.bi-rss::before { content: "\f522"; } -.bi-rulers::before { content: "\f523"; } -.bi-save-fill::before { content: "\f524"; } -.bi-save::before { content: "\f525"; } -.bi-save2-fill::before { content: "\f526"; } -.bi-save2::before { content: "\f527"; } -.bi-scissors::before { content: "\f528"; } -.bi-screwdriver::before { content: "\f529"; } -.bi-search::before { content: "\f52a"; } -.bi-segmented-nav::before { content: "\f52b"; } -.bi-server::before { content: "\f52c"; } -.bi-share-fill::before { content: "\f52d"; } -.bi-share::before { content: "\f52e"; } -.bi-shield-check::before { content: "\f52f"; } -.bi-shield-exclamation::before { content: "\f530"; } -.bi-shield-fill-check::before { content: "\f531"; } -.bi-shield-fill-exclamation::before { content: "\f532"; } -.bi-shield-fill-minus::before { content: "\f533"; } -.bi-shield-fill-plus::before { content: "\f534"; } -.bi-shield-fill-x::before { content: "\f535"; } -.bi-shield-fill::before { content: "\f536"; } -.bi-shield-lock-fill::before { content: "\f537"; } -.bi-shield-lock::before { content: "\f538"; } -.bi-shield-minus::before { content: "\f539"; } -.bi-shield-plus::before { content: "\f53a"; } -.bi-shield-shaded::before { content: "\f53b"; } -.bi-shield-slash-fill::before { content: "\f53c"; } -.bi-shield-slash::before { content: "\f53d"; } -.bi-shield-x::before { content: "\f53e"; } -.bi-shield::before { content: "\f53f"; } -.bi-shift-fill::before { content: "\f540"; } -.bi-shift::before { content: "\f541"; } -.bi-shop-window::before { content: "\f542"; } -.bi-shop::before { content: "\f543"; } -.bi-shuffle::before { content: "\f544"; } -.bi-signpost-2-fill::before { content: "\f545"; } -.bi-signpost-2::before { content: "\f546"; } -.bi-signpost-fill::before { content: "\f547"; } -.bi-signpost-split-fill::before { content: "\f548"; } -.bi-signpost-split::before { content: "\f549"; } -.bi-signpost::before { content: "\f54a"; } -.bi-sim-fill::before { content: "\f54b"; } -.bi-sim::before { content: "\f54c"; } -.bi-skip-backward-btn-fill::before { content: "\f54d"; } -.bi-skip-backward-btn::before { content: "\f54e"; } -.bi-skip-backward-circle-fill::before { content: "\f54f"; } -.bi-skip-backward-circle::before { content: "\f550"; } -.bi-skip-backward-fill::before { content: "\f551"; } -.bi-skip-backward::before { content: "\f552"; } -.bi-skip-end-btn-fill::before { content: "\f553"; } -.bi-skip-end-btn::before { content: "\f554"; } -.bi-skip-end-circle-fill::before { content: "\f555"; } -.bi-skip-end-circle::before { content: "\f556"; } -.bi-skip-end-fill::before { content: "\f557"; } -.bi-skip-end::before { content: "\f558"; } -.bi-skip-forward-btn-fill::before { content: "\f559"; } -.bi-skip-forward-btn::before { content: "\f55a"; } -.bi-skip-forward-circle-fill::before { content: "\f55b"; } -.bi-skip-forward-circle::before { content: "\f55c"; } -.bi-skip-forward-fill::before { content: "\f55d"; } -.bi-skip-forward::before { content: "\f55e"; } -.bi-skip-start-btn-fill::before { content: "\f55f"; } -.bi-skip-start-btn::before { content: "\f560"; } -.bi-skip-start-circle-fill::before { content: "\f561"; } -.bi-skip-start-circle::before { content: "\f562"; } -.bi-skip-start-fill::before { content: "\f563"; } -.bi-skip-start::before { content: "\f564"; } -.bi-slack::before { content: "\f565"; } -.bi-slash-circle-fill::before { content: "\f566"; } -.bi-slash-circle::before { content: "\f567"; } -.bi-slash-square-fill::before { content: "\f568"; } -.bi-slash-square::before { content: "\f569"; } -.bi-slash::before { content: "\f56a"; } -.bi-sliders::before { content: "\f56b"; } -.bi-smartwatch::before { content: "\f56c"; } -.bi-snow::before { content: "\f56d"; } -.bi-snow2::before { content: "\f56e"; } -.bi-snow3::before { content: "\f56f"; } -.bi-sort-alpha-down-alt::before { content: "\f570"; } -.bi-sort-alpha-down::before { content: "\f571"; } -.bi-sort-alpha-up-alt::before { content: "\f572"; } -.bi-sort-alpha-up::before { content: "\f573"; } -.bi-sort-down-alt::before { content: "\f574"; } -.bi-sort-down::before { content: "\f575"; } -.bi-sort-numeric-down-alt::before { content: "\f576"; } -.bi-sort-numeric-down::before { content: "\f577"; } -.bi-sort-numeric-up-alt::before { content: "\f578"; } -.bi-sort-numeric-up::before { content: "\f579"; } -.bi-sort-up-alt::before { content: "\f57a"; } -.bi-sort-up::before { content: "\f57b"; } -.bi-soundwave::before { content: "\f57c"; } -.bi-speaker-fill::before { content: "\f57d"; } -.bi-speaker::before { content: "\f57e"; } -.bi-speedometer::before { content: "\f57f"; } -.bi-speedometer2::before { content: "\f580"; } -.bi-spellcheck::before { content: "\f581"; } -.bi-square-fill::before { content: "\f582"; } -.bi-square-half::before { content: "\f583"; } -.bi-square::before { content: "\f584"; } -.bi-stack::before { content: "\f585"; } -.bi-star-fill::before { content: "\f586"; } -.bi-star-half::before { content: "\f587"; } -.bi-star::before { content: "\f588"; } -.bi-stars::before { content: "\f589"; } -.bi-stickies-fill::before { content: "\f58a"; } -.bi-stickies::before { content: "\f58b"; } -.bi-sticky-fill::before { content: "\f58c"; } -.bi-sticky::before { content: "\f58d"; } -.bi-stop-btn-fill::before { content: "\f58e"; } -.bi-stop-btn::before { content: "\f58f"; } -.bi-stop-circle-fill::before { content: "\f590"; } -.bi-stop-circle::before { content: "\f591"; } -.bi-stop-fill::before { content: "\f592"; } -.bi-stop::before { content: "\f593"; } -.bi-stoplights-fill::before { content: "\f594"; } -.bi-stoplights::before { content: "\f595"; } -.bi-stopwatch-fill::before { content: "\f596"; } -.bi-stopwatch::before { content: "\f597"; } -.bi-subtract::before { content: "\f598"; } -.bi-suit-club-fill::before { content: "\f599"; } -.bi-suit-club::before { content: "\f59a"; } -.bi-suit-diamond-fill::before { content: "\f59b"; } -.bi-suit-diamond::before { content: "\f59c"; } -.bi-suit-heart-fill::before { content: "\f59d"; } -.bi-suit-heart::before { content: "\f59e"; } -.bi-suit-spade-fill::before { content: "\f59f"; } -.bi-suit-spade::before { content: "\f5a0"; } -.bi-sun-fill::before { content: "\f5a1"; } -.bi-sun::before { content: "\f5a2"; } -.bi-sunglasses::before { content: "\f5a3"; } -.bi-sunrise-fill::before { content: "\f5a4"; } -.bi-sunrise::before { content: "\f5a5"; } -.bi-sunset-fill::before { content: "\f5a6"; } -.bi-sunset::before { content: "\f5a7"; } -.bi-symmetry-horizontal::before { content: "\f5a8"; } -.bi-symmetry-vertical::before { content: "\f5a9"; } -.bi-table::before { content: "\f5aa"; } -.bi-tablet-fill::before { content: "\f5ab"; } -.bi-tablet-landscape-fill::before { content: "\f5ac"; } -.bi-tablet-landscape::before { content: "\f5ad"; } -.bi-tablet::before { content: "\f5ae"; } -.bi-tag-fill::before { content: "\f5af"; } -.bi-tag::before { content: "\f5b0"; } -.bi-tags-fill::before { content: "\f5b1"; } -.bi-tags::before { content: "\f5b2"; } -.bi-telegram::before { content: "\f5b3"; } -.bi-telephone-fill::before { content: "\f5b4"; } -.bi-telephone-forward-fill::before { content: "\f5b5"; } -.bi-telephone-forward::before { content: "\f5b6"; } -.bi-telephone-inbound-fill::before { content: "\f5b7"; } -.bi-telephone-inbound::before { content: "\f5b8"; } -.bi-telephone-minus-fill::before { content: "\f5b9"; } -.bi-telephone-minus::before { content: "\f5ba"; } -.bi-telephone-outbound-fill::before { content: "\f5bb"; } -.bi-telephone-outbound::before { content: "\f5bc"; } -.bi-telephone-plus-fill::before { content: "\f5bd"; } -.bi-telephone-plus::before { content: "\f5be"; } -.bi-telephone-x-fill::before { content: "\f5bf"; } -.bi-telephone-x::before { content: "\f5c0"; } -.bi-telephone::before { content: "\f5c1"; } -.bi-terminal-fill::before { content: "\f5c2"; } -.bi-terminal::before { content: "\f5c3"; } -.bi-text-center::before { content: "\f5c4"; } -.bi-text-indent-left::before { content: "\f5c5"; } -.bi-text-indent-right::before { content: "\f5c6"; } -.bi-text-left::before { content: "\f5c7"; } -.bi-text-paragraph::before { content: "\f5c8"; } -.bi-text-right::before { content: "\f5c9"; } -.bi-textarea-resize::before { content: "\f5ca"; } -.bi-textarea-t::before { content: "\f5cb"; } -.bi-textarea::before { content: "\f5cc"; } -.bi-thermometer-half::before { content: "\f5cd"; } -.bi-thermometer-high::before { content: "\f5ce"; } -.bi-thermometer-low::before { content: "\f5cf"; } -.bi-thermometer-snow::before { content: "\f5d0"; } -.bi-thermometer-sun::before { content: "\f5d1"; } -.bi-thermometer::before { content: "\f5d2"; } -.bi-three-dots-vertical::before { content: "\f5d3"; } -.bi-three-dots::before { content: "\f5d4"; } -.bi-toggle-off::before { content: "\f5d5"; } -.bi-toggle-on::before { content: "\f5d6"; } -.bi-toggle2-off::before { content: "\f5d7"; } -.bi-toggle2-on::before { content: "\f5d8"; } -.bi-toggles::before { content: "\f5d9"; } -.bi-toggles2::before { content: "\f5da"; } -.bi-tools::before { content: "\f5db"; } -.bi-tornado::before { content: "\f5dc"; } -.bi-trash-fill::before { content: "\f5dd"; } -.bi-trash::before { content: "\f5de"; } -.bi-trash2-fill::before { content: "\f5df"; } -.bi-trash2::before { content: "\f5e0"; } -.bi-tree-fill::before { content: "\f5e1"; } -.bi-tree::before { content: "\f5e2"; } -.bi-triangle-fill::before { content: "\f5e3"; } -.bi-triangle-half::before { content: "\f5e4"; } -.bi-triangle::before { content: "\f5e5"; } -.bi-trophy-fill::before { content: "\f5e6"; } -.bi-trophy::before { content: "\f5e7"; } -.bi-tropical-storm::before { content: "\f5e8"; } -.bi-truck-flatbed::before { content: "\f5e9"; } -.bi-truck::before { content: "\f5ea"; } -.bi-tsunami::before { content: "\f5eb"; } -.bi-tv-fill::before { content: "\f5ec"; } -.bi-tv::before { content: "\f5ed"; } -.bi-twitch::before { content: "\f5ee"; } -.bi-twitter::before { content: "\f5ef"; } -.bi-type-bold::before { content: "\f5f0"; } -.bi-type-h1::before { content: "\f5f1"; } -.bi-type-h2::before { content: "\f5f2"; } -.bi-type-h3::before { content: "\f5f3"; } -.bi-type-italic::before { content: "\f5f4"; } -.bi-type-strikethrough::before { content: "\f5f5"; } -.bi-type-underline::before { content: "\f5f6"; } -.bi-type::before { content: "\f5f7"; } -.bi-ui-checks-grid::before { content: "\f5f8"; } -.bi-ui-checks::before { content: "\f5f9"; } -.bi-ui-radios-grid::before { content: "\f5fa"; } -.bi-ui-radios::before { content: "\f5fb"; } -.bi-umbrella-fill::before { content: "\f5fc"; } -.bi-umbrella::before { content: "\f5fd"; } -.bi-union::before { content: "\f5fe"; } -.bi-unlock-fill::before { content: "\f5ff"; } -.bi-unlock::before { content: "\f600"; } -.bi-upc-scan::before { content: "\f601"; } -.bi-upc::before { content: "\f602"; } -.bi-upload::before { content: "\f603"; } -.bi-vector-pen::before { content: "\f604"; } -.bi-view-list::before { content: "\f605"; } -.bi-view-stacked::before { content: "\f606"; } -.bi-vinyl-fill::before { content: "\f607"; } -.bi-vinyl::before { content: "\f608"; } -.bi-voicemail::before { content: "\f609"; } -.bi-volume-down-fill::before { content: "\f60a"; } -.bi-volume-down::before { content: "\f60b"; } -.bi-volume-mute-fill::before { content: "\f60c"; } -.bi-volume-mute::before { content: "\f60d"; } -.bi-volume-off-fill::before { content: "\f60e"; } -.bi-volume-off::before { content: "\f60f"; } -.bi-volume-up-fill::before { content: "\f610"; } -.bi-volume-up::before { content: "\f611"; } -.bi-vr::before { content: "\f612"; } -.bi-wallet-fill::before { content: "\f613"; } -.bi-wallet::before { content: "\f614"; } -.bi-wallet2::before { content: "\f615"; } -.bi-watch::before { content: "\f616"; } -.bi-water::before { content: "\f617"; } -.bi-whatsapp::before { content: "\f618"; } -.bi-wifi-1::before { content: "\f619"; } -.bi-wifi-2::before { content: "\f61a"; } -.bi-wifi-off::before { content: "\f61b"; } -.bi-wifi::before { content: "\f61c"; } -.bi-wind::before { content: "\f61d"; } -.bi-window-dock::before { content: "\f61e"; } -.bi-window-sidebar::before { content: "\f61f"; } -.bi-window::before { content: "\f620"; } -.bi-wrench::before { content: "\f621"; } -.bi-x-circle-fill::before { content: "\f622"; } -.bi-x-circle::before { content: "\f623"; } -.bi-x-diamond-fill::before { content: "\f624"; } -.bi-x-diamond::before { content: "\f625"; } -.bi-x-octagon-fill::before { content: "\f626"; } -.bi-x-octagon::before { content: "\f627"; } -.bi-x-square-fill::before { content: "\f628"; } -.bi-x-square::before { content: "\f629"; } -.bi-x::before { content: "\f62a"; } -.bi-youtube::before { content: "\f62b"; } -.bi-zoom-in::before { content: "\f62c"; } -.bi-zoom-out::before { content: "\f62d"; } -.bi-bank::before { content: "\f62e"; } -.bi-bank2::before { content: "\f62f"; } -.bi-bell-slash-fill::before { content: "\f630"; } -.bi-bell-slash::before { content: "\f631"; } -.bi-cash-coin::before { content: "\f632"; } -.bi-check-lg::before { content: "\f633"; } -.bi-coin::before { content: "\f634"; } -.bi-currency-bitcoin::before { content: "\f635"; } -.bi-currency-dollar::before { content: "\f636"; } -.bi-currency-euro::before { content: "\f637"; } -.bi-currency-exchange::before { content: "\f638"; } -.bi-currency-pound::before { content: "\f639"; } -.bi-currency-yen::before { content: "\f63a"; } -.bi-dash-lg::before { content: "\f63b"; } -.bi-exclamation-lg::before { content: "\f63c"; } -.bi-file-earmark-pdf-fill::before { content: "\f63d"; } -.bi-file-earmark-pdf::before { content: "\f63e"; } -.bi-file-pdf-fill::before { content: "\f63f"; } -.bi-file-pdf::before { content: "\f640"; } -.bi-gender-ambiguous::before { content: "\f641"; } -.bi-gender-female::before { content: "\f642"; } -.bi-gender-male::before { content: "\f643"; } -.bi-gender-trans::before { content: "\f644"; } -.bi-headset-vr::before { content: "\f645"; } -.bi-info-lg::before { content: "\f646"; } -.bi-mastodon::before { content: "\f647"; } -.bi-messenger::before { content: "\f648"; } -.bi-piggy-bank-fill::before { content: "\f649"; } -.bi-piggy-bank::before { content: "\f64a"; } -.bi-pin-map-fill::before { content: "\f64b"; } -.bi-pin-map::before { content: "\f64c"; } -.bi-plus-lg::before { content: "\f64d"; } -.bi-question-lg::before { content: "\f64e"; } -.bi-recycle::before { content: "\f64f"; } -.bi-reddit::before { content: "\f650"; } -.bi-safe-fill::before { content: "\f651"; } -.bi-safe2-fill::before { content: "\f652"; } -.bi-safe2::before { content: "\f653"; } -.bi-sd-card-fill::before { content: "\f654"; } -.bi-sd-card::before { content: "\f655"; } -.bi-skype::before { content: "\f656"; } -.bi-slash-lg::before { content: "\f657"; } -.bi-translate::before { content: "\f658"; } -.bi-x-lg::before { content: "\f659"; } -.bi-safe::before { content: "\f65a"; } -.bi-apple::before { content: "\f65b"; } -.bi-microsoft::before { content: "\f65d"; } -.bi-windows::before { content: "\f65e"; } -.bi-behance::before { content: "\f65c"; } -.bi-dribbble::before { content: "\f65f"; } -.bi-line::before { content: "\f660"; } -.bi-medium::before { content: "\f661"; } -.bi-paypal::before { content: "\f662"; } -.bi-pinterest::before { content: "\f663"; } -.bi-signal::before { content: "\f664"; } -.bi-snapchat::before { content: "\f665"; } -.bi-spotify::before { content: "\f666"; } -.bi-stack-overflow::before { content: "\f667"; } -.bi-strava::before { content: "\f668"; } -.bi-wordpress::before { content: "\f669"; } -.bi-vimeo::before { content: "\f66a"; } -.bi-activity::before { content: "\f66b"; } -.bi-easel2-fill::before { content: "\f66c"; } -.bi-easel2::before { content: "\f66d"; } -.bi-easel3-fill::before { content: "\f66e"; } -.bi-easel3::before { content: "\f66f"; } -.bi-fan::before { content: "\f670"; } -.bi-fingerprint::before { content: "\f671"; } -.bi-graph-down-arrow::before { content: "\f672"; } -.bi-graph-up-arrow::before { content: "\f673"; } -.bi-hypnotize::before { content: "\f674"; } -.bi-magic::before { content: "\f675"; } -.bi-person-rolodex::before { content: "\f676"; } -.bi-person-video::before { content: "\f677"; } -.bi-person-video2::before { content: "\f678"; } -.bi-person-video3::before { content: "\f679"; } -.bi-person-workspace::before { content: "\f67a"; } -.bi-radioactive::before { content: "\f67b"; } -.bi-webcam-fill::before { content: "\f67c"; } -.bi-webcam::before { content: "\f67d"; } -.bi-yin-yang::before { content: "\f67e"; } -.bi-bandaid-fill::before { content: "\f680"; } -.bi-bandaid::before { content: "\f681"; } -.bi-bluetooth::before { content: "\f682"; } -.bi-body-text::before { content: "\f683"; } -.bi-boombox::before { content: "\f684"; } -.bi-boxes::before { content: "\f685"; } -.bi-dpad-fill::before { content: "\f686"; } -.bi-dpad::before { content: "\f687"; } -.bi-ear-fill::before { content: "\f688"; } -.bi-ear::before { content: "\f689"; } -.bi-envelope-check-1::before { content: "\f68a"; } -.bi-envelope-check-fill::before { content: "\f68b"; } -.bi-envelope-check::before { content: "\f68c"; } -.bi-envelope-dash-1::before { content: "\f68d"; } -.bi-envelope-dash-fill::before { content: "\f68e"; } -.bi-envelope-dash::before { content: "\f68f"; } -.bi-envelope-exclamation-1::before { content: "\f690"; } -.bi-envelope-exclamation-fill::before { content: "\f691"; } -.bi-envelope-exclamation::before { content: "\f692"; } -.bi-envelope-plus-fill::before { content: "\f693"; } -.bi-envelope-plus::before { content: "\f694"; } -.bi-envelope-slash-1::before { content: "\f695"; } -.bi-envelope-slash-fill::before { content: "\f696"; } -.bi-envelope-slash::before { content: "\f697"; } -.bi-envelope-x-1::before { content: "\f698"; } -.bi-envelope-x-fill::before { content: "\f699"; } -.bi-envelope-x::before { content: "\f69a"; } -.bi-explicit-fill::before { content: "\f69b"; } -.bi-explicit::before { content: "\f69c"; } -.bi-git::before { content: "\f69d"; } -.bi-infinity::before { content: "\f69e"; } -.bi-list-columns-reverse::before { content: "\f69f"; } -.bi-list-columns::before { content: "\f6a0"; } -.bi-meta::before { content: "\f6a1"; } -.bi-mortorboard-fill::before { content: "\f6a2"; } -.bi-mortorboard::before { content: "\f6a3"; } -.bi-nintendo-switch::before { content: "\f6a4"; } -.bi-pc-display-horizontal::before { content: "\f6a5"; } -.bi-pc-display::before { content: "\f6a6"; } -.bi-pc-horizontal::before { content: "\f6a7"; } -.bi-pc::before { content: "\f6a8"; } -.bi-playstation::before { content: "\f6a9"; } -.bi-plus-slash-minus::before { content: "\f6aa"; } -.bi-projector-fill::before { content: "\f6ab"; } -.bi-projector::before { content: "\f6ac"; } -.bi-qr-code-scan::before { content: "\f6ad"; } -.bi-qr-code::before { content: "\f6ae"; } -.bi-quora::before { content: "\f6af"; } -.bi-quote::before { content: "\f6b0"; } -.bi-robot::before { content: "\f6b1"; } -.bi-send-check-fill::before { content: "\f6b2"; } -.bi-send-check::before { content: "\f6b3"; } -.bi-send-dash-fill::before { content: "\f6b4"; } -.bi-send-dash::before { content: "\f6b5"; } -.bi-send-exclamation-1::before { content: "\f6b6"; } -.bi-send-exclamation-fill::before { content: "\f6b7"; } -.bi-send-exclamation::before { content: "\f6b8"; } -.bi-send-fill::before { content: "\f6b9"; } -.bi-send-plus-fill::before { content: "\f6ba"; } -.bi-send-plus::before { content: "\f6bb"; } -.bi-send-slash-fill::before { content: "\f6bc"; } -.bi-send-slash::before { content: "\f6bd"; } -.bi-send-x-fill::before { content: "\f6be"; } -.bi-send-x::before { content: "\f6bf"; } -.bi-send::before { content: "\f6c0"; } -.bi-steam::before { content: "\f6c1"; } -.bi-terminal-dash-1::before { content: "\f6c2"; } -.bi-terminal-dash::before { content: "\f6c3"; } -.bi-terminal-plus::before { content: "\f6c4"; } -.bi-terminal-split::before { content: "\f6c5"; } -.bi-ticket-detailed-fill::before { content: "\f6c6"; } -.bi-ticket-detailed::before { content: "\f6c7"; } -.bi-ticket-fill::before { content: "\f6c8"; } -.bi-ticket-perforated-fill::before { content: "\f6c9"; } -.bi-ticket-perforated::before { content: "\f6ca"; } -.bi-ticket::before { content: "\f6cb"; } -.bi-tiktok::before { content: "\f6cc"; } -.bi-window-dash::before { content: "\f6cd"; } -.bi-window-desktop::before { content: "\f6ce"; } -.bi-window-fullscreen::before { content: "\f6cf"; } -.bi-window-plus::before { content: "\f6d0"; } -.bi-window-split::before { content: "\f6d1"; } -.bi-window-stack::before { content: "\f6d2"; } -.bi-window-x::before { content: "\f6d3"; } -.bi-xbox::before { content: "\f6d4"; } -.bi-ethernet::before { content: "\f6d5"; } -.bi-hdmi-fill::before { content: "\f6d6"; } -.bi-hdmi::before { content: "\f6d7"; } -.bi-usb-c-fill::before { content: "\f6d8"; } -.bi-usb-c::before { content: "\f6d9"; } -.bi-usb-fill::before { content: "\f6da"; } -.bi-usb-plug-fill::before { content: "\f6db"; } -.bi-usb-plug::before { content: "\f6dc"; } -.bi-usb-symbol::before { content: "\f6dd"; } -.bi-usb::before { content: "\f6de"; } -.bi-boombox-fill::before { content: "\f6df"; } -.bi-displayport-1::before { content: "\f6e0"; } -.bi-displayport::before { content: "\f6e1"; } -.bi-gpu-card::before { content: "\f6e2"; } -.bi-memory::before { content: "\f6e3"; } -.bi-modem-fill::before { content: "\f6e4"; } -.bi-modem::before { content: "\f6e5"; } -.bi-motherboard-fill::before { content: "\f6e6"; } -.bi-motherboard::before { content: "\f6e7"; } -.bi-optical-audio-fill::before { content: "\f6e8"; } -.bi-optical-audio::before { content: "\f6e9"; } -.bi-pci-card::before { content: "\f6ea"; } -.bi-router-fill::before { content: "\f6eb"; } -.bi-router::before { content: "\f6ec"; } -.bi-ssd-fill::before { content: "\f6ed"; } -.bi-ssd::before { content: "\f6ee"; } -.bi-thunderbolt-fill::before { content: "\f6ef"; } -.bi-thunderbolt::before { content: "\f6f0"; } -.bi-usb-drive-fill::before { content: "\f6f1"; } -.bi-usb-drive::before { content: "\f6f2"; } -.bi-usb-micro-fill::before { content: "\f6f3"; } -.bi-usb-micro::before { content: "\f6f4"; } -.bi-usb-mini-fill::before { content: "\f6f5"; } -.bi-usb-mini::before { content: "\f6f6"; } -.bi-cloud-haze2::before { content: "\f6f7"; } -.bi-device-hdd-fill::before { content: "\f6f8"; } -.bi-device-hdd::before { content: "\f6f9"; } -.bi-device-ssd-fill::before { content: "\f6fa"; } -.bi-device-ssd::before { content: "\f6fb"; } -.bi-displayport-fill::before { content: "\f6fc"; } -.bi-mortarboard-fill::before { content: "\f6fd"; } -.bi-mortarboard::before { content: "\f6fe"; } -.bi-terminal-x::before { content: "\f6ff"; } diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/bootstrap.min.css b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/bootstrap.min.css deleted file mode 100644 index 1472dec05..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/bootstrap.min.css +++ /dev/null @@ -1,7 +0,0 @@ -@charset "UTF-8";/*! - * Bootstrap v5.1.3 (https://getbootstrap.com/) - * Copyright 2011-2021 The Bootstrap Authors - * Copyright 2011-2021 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-body-color-rgb:33,37,41;--bs-body-bg-rgb:255,255,255;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-bg:#fff}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:#6c757d}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{width:100%;padding-right:var(--bs-gutter-x,.75rem);padding-left:var(--bs-gutter-x,.75rem);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.6666666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.6666666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.6666666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.6666666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-bg:transparent;--bs-table-accent-bg:transparent;--bs-table-striped-color:#212529;--bs-table-striped-bg:rgba(0, 0, 0, 0.05);--bs-table-active-color:#212529;--bs-table-active-bg:rgba(0, 0, 0, 0.1);--bs-table-hover-color:#212529;--bs-table-hover-bg:rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;color:#212529;vertical-align:top;border-color:#dee2e6}.table>:not(caption)>*>*{padding:.5rem .5rem;background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg)}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table>:not(:first-child){border-top:2px solid currentColor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:1px 0}.table-bordered>:not(caption)>*>*{border-width:0 1px}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-active{--bs-table-accent-bg:var(--bs-table-active-bg);color:var(--bs-table-active-color)}.table-hover>tbody>tr:hover>*{--bs-table-accent-bg:var(--bs-table-hover-bg);color:var(--bs-table-hover-color)}.table-primary{--bs-table-bg:#cfe2ff;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:#000;border-color:#bacbe6}.table-secondary{--bs-table-bg:#e2e3e5;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:#000;border-color:#cbccce}.table-success{--bs-table-bg:#d1e7dd;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:#000;border-color:#bcd0c7}.table-info{--bs-table-bg:#cff4fc;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:#000;border-color:#badce3}.table-warning{--bs-table-bg:#fff3cd;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:#000;border-color:#e6dbb9}.table-danger{--bs-table-bg:#f8d7da;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:#000;border-color:#dfc2c4}.table-light{--bs-table-bg:#f8f9fa;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:#000;border-color:#dfe0e1}.table-dark{--bs-table-bg:#212529;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:#fff;border-color:#373b3e}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:#6c757d}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:#212529;background-color:#fff;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{height:1.5em}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:#dde0e3}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + 2px)}textarea.form-control-sm{min-height:calc(1.5em + .5rem + 2px)}textarea.form-control-lg{min-height:calc(1.5em + 1rem + 2px)}.form-control-color{width:3rem;height:auto;padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{height:1.5em;border-radius:.25rem}.form-control-color::-webkit-color-swatch{height:1.5em;border-radius:.25rem}.form-select{display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;-moz-padding-start:calc(0.75rem - 3px);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:#e9ecef}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #212529}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:.2rem}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:.3rem}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-input{width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:#fff;background-repeat:no-repeat;background-position:center;background-size:contain;border:1px solid rgba(0,0,0,.25);-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{width:2em;margin-left:-2.5em;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.form-range:disabled::-moz-range-thumb{background-color:#adb5bd}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px);line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;height:100%;padding:1rem .75rem;pointer-events:none;border:1px solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control{padding:1rem .75rem}.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus{z-index:3}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:3}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#198754}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(25,135,84,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#198754;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:#198754}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:#198754}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:#198754}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#198754}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group .form-control.is-valid,.input-group .form-select.is-valid,.was-validated .input-group .form-control:valid,.was-validated .input-group .form-select:valid{z-index:1}.input-group .form-control.is-valid:focus,.input-group .form-select.is-valid:focus,.was-validated .input-group .form-control:valid:focus,.was-validated .input-group .form-select:valid:focus{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:#dc3545}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:#dc3545}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:#dc3545}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group .form-control.is-invalid,.input-group .form-select.is-invalid,.was-validated .input-group .form-control:invalid,.was-validated .input-group .form-select:invalid{z-index:2}.input-group .form-control.is-invalid:focus,.input-group .form-select.is-invalid:focus,.was-validated .input-group .form-control:invalid:focus,.was-validated .input-group .form-select:invalid:focus{z-index:3}.btn{display:inline-block;font-weight:400;line-height:1.5;color:#212529;text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529}.btn-check:focus+.btn,.btn:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{pointer-events:none;opacity:.65}.btn-primary{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-primary:hover{color:#fff;background-color:#0b5ed7;border-color:#0a58ca}.btn-check:focus+.btn-primary,.btn-primary:focus{color:#fff;background-color:#0b5ed7;border-color:#0a58ca;box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-check:active+.btn-primary,.btn-check:checked+.btn-primary,.btn-primary.active,.btn-primary:active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0a58ca;border-color:#0a53be}.btn-check:active+.btn-primary:focus,.btn-check:checked+.btn-primary:focus,.btn-primary.active:focus,.btn-primary:active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5c636a;border-color:#565e64}.btn-check:focus+.btn-secondary,.btn-secondary:focus{color:#fff;background-color:#5c636a;border-color:#565e64;box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-check:active+.btn-secondary,.btn-check:checked+.btn-secondary,.btn-secondary.active,.btn-secondary:active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#565e64;border-color:#51585e}.btn-check:active+.btn-secondary:focus,.btn-check:checked+.btn-secondary:focus,.btn-secondary.active:focus,.btn-secondary:active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-success{color:#fff;background-color:#198754;border-color:#198754}.btn-success:hover{color:#fff;background-color:#157347;border-color:#146c43}.btn-check:focus+.btn-success,.btn-success:focus{color:#fff;background-color:#157347;border-color:#146c43;box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-check:active+.btn-success,.btn-check:checked+.btn-success,.btn-success.active,.btn-success:active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#146c43;border-color:#13653f}.btn-check:active+.btn-success:focus,.btn-check:checked+.btn-success:focus,.btn-success.active:focus,.btn-success:active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#198754;border-color:#198754}.btn-info{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-info:hover{color:#000;background-color:#31d2f2;border-color:#25cff2}.btn-check:focus+.btn-info,.btn-info:focus{color:#000;background-color:#31d2f2;border-color:#25cff2;box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-check:active+.btn-info,.btn-check:checked+.btn-info,.btn-info.active,.btn-info:active,.show>.btn-info.dropdown-toggle{color:#000;background-color:#3dd5f3;border-color:#25cff2}.btn-check:active+.btn-info:focus,.btn-check:checked+.btn-info:focus,.btn-info.active:focus,.btn-info:active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-info.disabled,.btn-info:disabled{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-warning{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#000;background-color:#ffca2c;border-color:#ffc720}.btn-check:focus+.btn-warning,.btn-warning:focus{color:#000;background-color:#ffca2c;border-color:#ffc720;box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-check:active+.btn-warning,.btn-check:checked+.btn-warning,.btn-warning.active,.btn-warning:active,.show>.btn-warning.dropdown-toggle{color:#000;background-color:#ffcd39;border-color:#ffc720}.btn-check:active+.btn-warning:focus,.btn-check:checked+.btn-warning:focus,.btn-warning.active:focus,.btn-warning:active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#bb2d3b;border-color:#b02a37}.btn-check:focus+.btn-danger,.btn-danger:focus{color:#fff;background-color:#bb2d3b;border-color:#b02a37;box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-check:active+.btn-danger,.btn-check:checked+.btn-danger,.btn-danger.active,.btn-danger:active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#b02a37;border-color:#a52834}.btn-check:active+.btn-danger:focus,.btn-check:checked+.btn-danger:focus,.btn-danger.active:focus,.btn-danger:active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-light{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:focus+.btn-light,.btn-light:focus{color:#000;background-color:#f9fafb;border-color:#f9fafb;box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-check:active+.btn-light,.btn-check:checked+.btn-light,.btn-light.active,.btn-light:active,.show>.btn-light.dropdown-toggle{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:active+.btn-light:focus,.btn-check:checked+.btn-light:focus,.btn-light.active:focus,.btn-light:active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-light.disabled,.btn-light:disabled{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-dark{color:#fff;background-color:#212529;border-color:#212529}.btn-dark:hover{color:#fff;background-color:#1c1f23;border-color:#1a1e21}.btn-check:focus+.btn-dark,.btn-dark:focus{color:#fff;background-color:#1c1f23;border-color:#1a1e21;box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-check:active+.btn-dark,.btn-check:checked+.btn-dark,.btn-dark.active,.btn-dark:active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1a1e21;border-color:#191c1f}.btn-check:active+.btn-dark:focus,.btn-check:checked+.btn-dark:focus,.btn-dark.active:focus,.btn-dark:active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#212529;border-color:#212529}.btn-outline-primary{color:#0d6efd;border-color:#0d6efd}.btn-outline-primary:hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:focus+.btn-outline-primary,.btn-outline-primary:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-check:active+.btn-outline-primary,.btn-check:checked+.btn-outline-primary,.btn-outline-primary.active,.btn-outline-primary.dropdown-toggle.show,.btn-outline-primary:active{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:active+.btn-outline-primary:focus,.btn-check:checked+.btn-outline-primary:focus,.btn-outline-primary.active:focus,.btn-outline-primary.dropdown-toggle.show:focus,.btn-outline-primary:active:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#0d6efd;background-color:transparent}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:focus+.btn-outline-secondary,.btn-outline-secondary:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-check:active+.btn-outline-secondary,.btn-check:checked+.btn-outline-secondary,.btn-outline-secondary.active,.btn-outline-secondary.dropdown-toggle.show,.btn-outline-secondary:active{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:active+.btn-outline-secondary:focus,.btn-check:checked+.btn-outline-secondary:focus,.btn-outline-secondary.active:focus,.btn-outline-secondary.dropdown-toggle.show:focus,.btn-outline-secondary:active:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-success{color:#198754;border-color:#198754}.btn-outline-success:hover{color:#fff;background-color:#198754;border-color:#198754}.btn-check:focus+.btn-outline-success,.btn-outline-success:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-check:active+.btn-outline-success,.btn-check:checked+.btn-outline-success,.btn-outline-success.active,.btn-outline-success.dropdown-toggle.show,.btn-outline-success:active{color:#fff;background-color:#198754;border-color:#198754}.btn-check:active+.btn-outline-success:focus,.btn-check:checked+.btn-outline-success:focus,.btn-outline-success.active:focus,.btn-outline-success.dropdown-toggle.show:focus,.btn-outline-success:active:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#198754;background-color:transparent}.btn-outline-info{color:#0dcaf0;border-color:#0dcaf0}.btn-outline-info:hover{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:focus+.btn-outline-info,.btn-outline-info:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-check:active+.btn-outline-info,.btn-check:checked+.btn-outline-info,.btn-outline-info.active,.btn-outline-info.dropdown-toggle.show,.btn-outline-info:active{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:active+.btn-outline-info:focus,.btn-check:checked+.btn-outline-info:focus,.btn-outline-info.active:focus,.btn-outline-info.dropdown-toggle.show:focus,.btn-outline-info:active:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#0dcaf0;background-color:transparent}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:focus+.btn-outline-warning,.btn-outline-warning:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-check:active+.btn-outline-warning,.btn-check:checked+.btn-outline-warning,.btn-outline-warning.active,.btn-outline-warning.dropdown-toggle.show,.btn-outline-warning:active{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:active+.btn-outline-warning:focus,.btn-check:checked+.btn-outline-warning:focus,.btn-outline-warning.active:focus,.btn-outline-warning.dropdown-toggle.show:focus,.btn-outline-warning:active:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:focus+.btn-outline-danger,.btn-outline-danger:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-check:active+.btn-outline-danger,.btn-check:checked+.btn-outline-danger,.btn-outline-danger.active,.btn-outline-danger.dropdown-toggle.show,.btn-outline-danger:active{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:active+.btn-outline-danger:focus,.btn-check:checked+.btn-outline-danger:focus,.btn-outline-danger.active:focus,.btn-outline-danger.dropdown-toggle.show:focus,.btn-outline-danger:active:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:focus+.btn-outline-light,.btn-outline-light:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-check:active+.btn-outline-light,.btn-check:checked+.btn-outline-light,.btn-outline-light.active,.btn-outline-light.dropdown-toggle.show,.btn-outline-light:active{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:active+.btn-outline-light:focus,.btn-check:checked+.btn-outline-light:focus,.btn-outline-light.active:focus,.btn-outline-light.dropdown-toggle.show:focus,.btn-outline-light:active:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-dark{color:#212529;border-color:#212529}.btn-outline-dark:hover{color:#fff;background-color:#212529;border-color:#212529}.btn-check:focus+.btn-outline-dark,.btn-outline-dark:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-check:active+.btn-outline-dark,.btn-check:checked+.btn-outline-dark,.btn-outline-dark.active,.btn-outline-dark.dropdown-toggle.show,.btn-outline-dark:active{color:#fff;background-color:#212529;border-color:#212529}.btn-check:active+.btn-outline-dark:focus,.btn-check:checked+.btn-outline-dark:focus,.btn-outline-dark.active:focus,.btn-outline-dark.dropdown-toggle.show:focus,.btn-outline-dark:active:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#212529;background-color:transparent}.btn-link{font-weight:400;color:#0d6efd;text-decoration:underline}.btn-link:hover{color:#0a58ca}.btn-link.disabled,.btn-link:disabled{color:#6c757d}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropend,.dropstart,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;z-index:1000;display:none;min-width:10rem;padding:.5rem 0;margin:0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:.125rem}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,.15)}.dropdown-item{display:block;width:100%;padding:.25rem 1rem;clear:both;font-weight:400;color:#212529;text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#1e2125;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#0d6efd}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1rem;color:#212529}.dropdown-menu-dark{color:#dee2e6;background-color:#343a40;border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item{color:#dee2e6}.dropdown-menu-dark .dropdown-item:focus,.dropdown-menu-dark .dropdown-item:hover{color:#fff;background-color:rgba(255,255,255,.15)}.dropdown-menu-dark .dropdown-item.active,.dropdown-menu-dark .dropdown-item:active{color:#fff;background-color:#0d6efd}.dropdown-menu-dark .dropdown-item.disabled,.dropdown-menu-dark .dropdown-item:disabled{color:#adb5bd}.dropdown-menu-dark .dropdown-divider{border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item-text{color:#dee2e6}.dropdown-menu-dark .dropdown-header{color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem;color:#0d6efd;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:#0a58ca}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background:0 0;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6;isolation:isolate}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{background:0 0;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#0d6efd}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;text-decoration:none;white-space:nowrap}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem;transition:box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 .25rem}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas-header{display:none}.navbar-expand-sm .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-sm .offcanvas-bottom,.navbar-expand-sm .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas-header{display:none}.navbar-expand-md .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-md .offcanvas-bottom,.navbar-expand-md .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas-header{display:none}.navbar-expand-lg .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-lg .offcanvas-bottom,.navbar-expand-lg .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas-header{display:none}.navbar-expand-xl .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-xl .offcanvas-bottom,.navbar-expand-xl .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-xxl .offcanvas-bottom,.navbar-expand-xxl .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas-header{display:none}.navbar-expand .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand .offcanvas-bottom,.navbar-expand .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.55)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.55);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.55)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.55)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.55);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.55)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:1rem 1rem}.card-title{margin-bottom:.5rem}.card-subtitle{margin-top:-.25rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:1rem}.card-header{padding:.5rem 1rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.5rem 1rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.5rem;margin-bottom:-.5rem;margin-left:-.5rem;border-bottom:0}.card-header-pills{margin-right:-.5rem;margin-left:-.5rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-group>.card{margin-bottom:.75rem}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:1rem 1.25rem;font-size:1rem;color:#212529;text-align:left;background-color:#fff;border:0;border-radius:0;overflow-anchor:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,border-radius .15s ease}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:#0c63e4;background-color:#e7f1ff;box-shadow:inset 0 -1px 0 rgba(0,0,0,.125)}.accordion-button:not(.collapsed)::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");transform:rotate(-180deg)}.accordion-button::after{flex-shrink:0;width:1.25rem;height:1.25rem;margin-left:auto;content:"";background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-size:1.25rem;transition:transform .2s ease-in-out}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.accordion-header{margin-bottom:0}.accordion-item{background-color:#fff;border:1px solid rgba(0,0,0,.125)}.accordion-item:first-of-type{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.accordion-item:first-of-type .accordion-button{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-body{padding:1rem 1.25rem}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button{border-radius:0}.breadcrumb{display:flex;flex-wrap:wrap;padding:0 0;margin-bottom:1rem;list-style:none}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#6c757d;content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;color:#0d6efd;text-decoration:none;background-color:#fff;border:1px solid #dee2e6;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:#0a58ca;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;color:#0a58ca;background-color:#e9ecef;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item.active .page-link{z-index:3;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;background-color:#fff;border-color:#dee2e6}.page-link{padding:.375rem .75rem}.page-item:first-child .page-link{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{position:relative;padding:1rem 1rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{color:#084298;background-color:#cfe2ff;border-color:#b6d4fe}.alert-primary .alert-link{color:#06357a}.alert-secondary{color:#41464b;background-color:#e2e3e5;border-color:#d3d6d8}.alert-secondary .alert-link{color:#34383c}.alert-success{color:#0f5132;background-color:#d1e7dd;border-color:#badbcc}.alert-success .alert-link{color:#0c4128}.alert-info{color:#055160;background-color:#cff4fc;border-color:#b6effb}.alert-info .alert-link{color:#04414d}.alert-warning{color:#664d03;background-color:#fff3cd;border-color:#ffecb5}.alert-warning .alert-link{color:#523e02}.alert-danger{color:#842029;background-color:#f8d7da;border-color:#f5c2c7}.alert-danger .alert-link{color:#6a1a21}.alert-light{color:#636464;background-color:#fefefe;border-color:#fdfdfe}.alert-light .alert-link{color:#4f5050}.alert-dark{color:#141619;background-color:#d3d3d4;border-color:#bcbebf}.alert-dark .alert-link{color:#101214}@-webkit-keyframes progress-bar-stripes{0%{background-position-x:1rem}}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress{display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#0d6efd;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:1s linear infinite progress-bar-stripes;animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>li::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.5rem 1rem;color:#212529;text-decoration:none;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#084298;background-color:#cfe2ff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#084298;background-color:#bacbe6}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#084298;border-color:#084298}.list-group-item-secondary{color:#41464b;background-color:#e2e3e5}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#41464b;background-color:#cbccce}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#41464b;border-color:#41464b}.list-group-item-success{color:#0f5132;background-color:#d1e7dd}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#0f5132;background-color:#bcd0c7}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#0f5132;border-color:#0f5132}.list-group-item-info{color:#055160;background-color:#cff4fc}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#055160;background-color:#badce3}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#055160;border-color:#055160}.list-group-item-warning{color:#664d03;background-color:#fff3cd}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#664d03;background-color:#e6dbb9}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#664d03;border-color:#664d03}.list-group-item-danger{color:#842029;background-color:#f8d7da}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#842029;background-color:#dfc2c4}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#842029;border-color:#842029}.list-group-item-light{color:#636464;background-color:#fefefe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#636464;background-color:#e5e5e5}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#636464;border-color:#636464}.list-group-item-dark{color:#141619;background-color:#d3d3d4}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#141619;background-color:#bebebf}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.btn-close{box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:#000;background:transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat;border:0;border-radius:.25rem;opacity:.5}.btn-close:hover{color:#000;text-decoration:none;opacity:.75}.btn-close:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);opacity:1}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:.25}.btn-close-white{filter:invert(1) grayscale(100%) brightness(200%)}.toast{width:350px;max-width:100%;font-size:.875rem;pointer-events:auto;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .5rem 1rem rgba(0,0,0,.15);border-radius:.25rem}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:.75rem}.toast-header{display:flex;align-items:center;padding:.5rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-header .btn-close{margin-right:-.375rem;margin-left:.75rem}.toast-body{padding:.75rem;word-wrap:break-word}.modal{position:fixed;top:0;left:0;z-index:1055;display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1050;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .btn-close{padding:.5rem .5rem;margin:-.5rem -.5rem -.5rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;flex-wrap:wrap;flex-shrink:0;align-items:center;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{height:calc(100% - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}.modal-fullscreen .modal-footer{border-radius:0}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}.modal-fullscreen-sm-down .modal-footer{border-radius:0}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}.modal-fullscreen-md-down .modal-footer{border-radius:0}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}.modal-fullscreen-lg-down .modal-footer{border-radius:0}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}.modal-fullscreen-xl-down .modal-footer{border-radius:0}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}.modal-fullscreen-xxl-down .modal-footer{border-radius:0}}.tooltip{position:absolute;z-index:1080;display:block;margin:0;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .tooltip-arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[data-popper-placement^=right],.bs-tooltip-end{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[data-popper-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[data-popper-placement^=left],.bs-tooltip-start{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1070;display:block;max-width:276px;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .popover-arrow{position:absolute;display:block;width:1rem;height:.5rem}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f0f0f0}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem 1rem;margin-bottom:0;font-size:1rem;background-color:#f0f0f0;border-bottom:1px solid rgba(0,0,0,.2);border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:1rem 1rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%;list-style:none}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}@-webkit-keyframes spinner-border{to{transform:rotate(360deg)}}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:.75s linear infinite spinner-border;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:.75s linear infinite spinner-grow;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.offcanvas{position:fixed;bottom:0;z-index:1045;display:flex;flex-direction:column;max-width:100%;visibility:hidden;background-color:#fff;background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:1rem 1rem}.offcanvas-header .btn-close{padding:.5rem .5rem;margin-top:-.5rem;margin-right:-.5rem;margin-bottom:-.5rem}.offcanvas-title{margin-bottom:0;line-height:1.5}.offcanvas-body{flex-grow:1;padding:1rem 1rem;overflow-y:auto}.offcanvas-start{top:0;left:0;width:400px;border-right:1px solid rgba(0,0,0,.2);transform:translateX(-100%)}.offcanvas-end{top:0;right:0;width:400px;border-left:1px solid rgba(0,0,0,.2);transform:translateX(100%)}.offcanvas-top{top:0;right:0;left:0;height:30vh;max-height:100%;border-bottom:1px solid rgba(0,0,0,.2);transform:translateY(-100%)}.offcanvas-bottom{right:0;left:0;height:30vh;max-height:100%;border-top:1px solid rgba(0,0,0,.2);transform:translateY(100%)}.offcanvas.show{transform:none}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentColor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{-webkit-animation:placeholder-glow 2s ease-in-out infinite;animation:placeholder-glow 2s ease-in-out infinite}@-webkit-keyframes placeholder-glow{50%{opacity:.2}}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;-webkit-animation:placeholder-wave 2s linear infinite;animation:placeholder-wave 2s linear infinite}@-webkit-keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.link-primary{color:#0d6efd}.link-primary:focus,.link-primary:hover{color:#0a58ca}.link-secondary{color:#6c757d}.link-secondary:focus,.link-secondary:hover{color:#565e64}.link-success{color:#198754}.link-success:focus,.link-success:hover{color:#146c43}.link-info{color:#0dcaf0}.link-info:focus,.link-info:hover{color:#3dd5f3}.link-warning{color:#ffc107}.link-warning:focus,.link-warning:hover{color:#ffcd39}.link-danger{color:#dc3545}.link-danger:focus,.link-danger:hover{color:#b02a37}.link-light{color:#f8f9fa}.link-light:focus,.link-light:hover{color:#f9fafb}.link-dark{color:#212529}.link-dark:focus,.link-dark:hover{color:#1a1e21}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:75%}.ratio-16x9{--bs-aspect-ratio:56.25%}.ratio-21x9{--bs-aspect-ratio:42.8571428571%}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:1px;min-height:1em;background-color:currentColor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:1px solid #dee2e6!important}.border-0{border:0!important}.border-top{border-top:1px solid #dee2e6!important}.border-top-0{border-top:0!important}.border-end{border-right:1px solid #dee2e6!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:1px solid #dee2e6!important}.border-start-0{border-left:0!important}.border-primary{border-color:#0d6efd!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#198754!important}.border-info{border-color:#0dcaf0!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#212529!important}.border-white{border-color:#fff!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-light{font-weight:300!important}.fw-lighter{font-weight:lighter!important}.fw-normal{font-weight:400!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:#6c757d!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:.25rem!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:.2rem!important}.rounded-2{border-radius:.25rem!important}.rounded-3{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-end{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-start{border-bottom-left-radius:.25rem!important;border-top-left-radius:.25rem!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} -/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/common.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/common.js deleted file mode 100644 index 8dda3c8fe..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/common.js +++ /dev/null @@ -1,81 +0,0 @@ -// 自定义事件类 -class EventPlus { - constructor() { - this.event = new EventTarget(); - } - on(name, callback) { - this.event.addEventListener(name, e => callback(e.detail)); - } - send(name, data) { - this.event.dispatchEvent(new CustomEvent(name, { - detail: data, - bubbles: false, - cancelable: false - })); - } -} - -// 补零 -String.prototype.fill = function () { - return this >= 10 ? this : '0' + this; -}; - -// unicode编码转换字符串 -String.prototype.uTs = function () { - return eval('"' + Array.from(this).join('') + '"'); -}; - -// 字符串转换unicode编码 -String.prototype.sTu = function (str = '') { - Array.from(this).forEach(item => str += `\\u${item.charCodeAt(0).toString(16)}`); - return str; -}; - -// 全局变量/方法 -const $emit = new EventPlus(), $ = (selector, isAll = false) => { - const element = document.querySelector(selector), methods = { - on: function (event, callback) { - this.addEventListener(event, callback); - }, - attr: function (name, value = '') { - value && this.setAttribute(name, value); - return this; - } - }; - if (!isAll && element) { - return Object.assign(element, methods); - } else if (!isAll && !element) { - throw `HTML没有 ${selector} 元素! 请检查是否拼写错误`; - } - return Array.from(document.querySelectorAll(selector)).map(item => Object.assign(item, methods)); -}; - -// 节流函数 -$.throttle = (fn, delay) => { - let Timer = null; - return function () { - if (Timer) return; - Timer = setTimeout(() => { - fn.apply(this, arguments); - Timer = null; - }, delay); - }; -}; - -// 防抖函数 -$.debounce = (fn, delay) => { - let Timer = null; - return function () { - clearTimeout(Timer); - Timer = setTimeout(() => fn.apply(this, arguments), delay); - }; -}; - -// 绑定限制数字方法 -Array.from($('input[type="num"]', true)).forEach(item => { - item.addEventListener('input', function limitNum() { - if (!item.value || /^\d+$/.test(item.value)) return; - item.value = item.value.slice(0, -1); - limitNum(item); - }); -}); \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff deleted file mode 100644 index 1f5d54300..000000000 Binary files a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff and /dev/null differ diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff2 b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff2 deleted file mode 100644 index b3897eff0..000000000 Binary files a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff2 and /dev/null differ diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/streamingle_plugin b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/streamingle_plugin deleted file mode 100644 index 04440e927..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/streamingle_plugin +++ /dev/null @@ -1,9 +0,0 @@ -[2025-07-03T00:52:25.811] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 -[2025-07-03T00:58:26.107] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 -[2025-07-03T00:58:37.479] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 -[2025-07-03T00:58:50.582] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 -[2025-07-03T00:59:04.912] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 -[2025-07-03T01:00:19.924] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 -[2025-07-03T01:00:32.402] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 -[2025-07-03T01:01:32.400] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 -[2025-07-03T01:02:32.414] [INFO] default - 🚀 Streamingle Camera Controller 플러그인 시작됨 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/test.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/test.js deleted file mode 100644 index 0f6e2a5f3..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/dist/com.mirabox.streamingle.sdPlugin/test.js +++ /dev/null @@ -1,65 +0,0 @@ -const StreaminglePlugin = require('./plugin.js'); - -console.log('=== Streamingle 플러그인 테스트 시작 ==='); - -const plugin = new StreaminglePlugin(); - -// 연결 상태 모니터링 -let connectionCheckInterval = setInterval(() => { - const status = plugin.getStatus(); - console.log(`📊 연결 상태: ${status.isConnected ? '✅ 연결됨' : '❌ 연결 안됨'}`); - - if (status.isConnected) { - console.log(`📷 카메라 개수: ${status.cameraCount}개`); - console.log(`🎯 현재 카메라: ${status.currentCamera >= 0 ? status.currentCamera : '없음'}`); - - if (status.cameraList && status.cameraList.length > 0) { - console.log('📋 카메라 목록:'); - status.cameraList.forEach((camera, index) => { - console.log(` ${index}: ${camera.name} ${camera.isActive ? '[활성]' : '[비활성]'}`); - }); - } - } - - console.log('---'); -}, 5000); - -// 3초 후 카메라 목록 요청 -setTimeout(() => { - console.log('🔍 카메라 목록 요청...'); - plugin.requestCameraList(); -}, 3000); - -// 8초 후 첫 번째 카메라로 전환 -setTimeout(() => { - console.log('🎬 첫 번째 카메라로 전환...'); - plugin.switchCamera(0); -}, 8000); - -// 13초 후 두 번째 카메라로 전환 -setTimeout(() => { - console.log('🎬 두 번째 카메라로 전환...'); - plugin.switchCamera(1); -}, 13000); - -// 18초 후 세 번째 카메라로 전환 (있다면) -setTimeout(() => { - console.log('🎬 세 번째 카메라로 전환...'); - plugin.switchCamera(2); -}, 18000); - -// 25초 후 종료 -setTimeout(() => { - console.log('🛑 테스트 종료...'); - clearInterval(connectionCheckInterval); - plugin.disconnect(); - process.exit(0); -}, 25000); - -// 프로세스 종료 시 정리 -process.on('SIGINT', () => { - console.log('🛑 테스트 중단...'); - clearInterval(connectionCheckInterval); - plugin.disconnect(); - process.exit(0); -}); \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/images/avatar_outfit_icon.png b/Streamdeck/com.mirabox.streamingle.sdPlugin/images/avatar_outfit_icon.png new file mode 100644 index 000000000..bc274c411 --- /dev/null +++ b/Streamdeck/com.mirabox.streamingle.sdPlugin/images/avatar_outfit_icon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2bba34d6fc3c3667de9627a39746b029f5f90279ee514b552207d1a71ad86712 +size 60680 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/images/avatar_outfit_icon_inactive.png b/Streamdeck/com.mirabox.streamingle.sdPlugin/images/avatar_outfit_icon_inactive.png new file mode 100644 index 000000000..871431bf2 --- /dev/null +++ b/Streamdeck/com.mirabox.streamingle.sdPlugin/images/avatar_outfit_icon_inactive.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c07929eeb7ff81bc56a47cc95fd6b001eb59dca547083ca6f2ea840ee93c5043 +size 62311 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/manifest.json b/Streamdeck/com.mirabox.streamingle.sdPlugin/manifest.json index 2651a4d75..6520d8921 100644 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/manifest.json +++ b/Streamdeck/com.mirabox.streamingle.sdPlugin/manifest.json @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d570925d43102b294ea6e23b83fc47ab44dcd46ad3fde70694d25d64515d2953 -size 3061 +oid sha256:cf09907ab34e2b80144ee1883f2db40c7c622dcdf7b8542d252f4a0f4bac452c +size 3982 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/.package-lock.json b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/.package-lock.json deleted file mode 100644 index 0568689f6..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/.package-lock.json +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6979b0a8a904c35ec359bc9b6dbe1a252fa0eb961d2e85b1c3bb5e7bc6a4bc9a -size 752 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/LICENSE b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/LICENSE deleted file mode 100644 index 1da5b96a1..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2011 Einar Otto Stangvik -Copyright (c) 2013 Arnout Kazemier and contributors -Copyright (c) 2016 Luigi Pinca and contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/README.md b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/README.md deleted file mode 100644 index 21282681a..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/README.md +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bb979132f3cbff08ce47f36d041e18071f8f534d01f591c0b129ba7abf1e480e -size 15306 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/browser.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/browser.js deleted file mode 100644 index ca4f628ac..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/browser.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - -module.exports = function () { - throw new Error( - 'ws does not work in the browser. Browser clients must use the native ' + - 'WebSocket object' - ); -}; diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/index.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/index.js deleted file mode 100644 index 41edb3b81..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/index.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -const WebSocket = require('./lib/websocket'); - -WebSocket.createWebSocketStream = require('./lib/stream'); -WebSocket.Server = require('./lib/websocket-server'); -WebSocket.Receiver = require('./lib/receiver'); -WebSocket.Sender = require('./lib/sender'); - -WebSocket.WebSocket = WebSocket; -WebSocket.WebSocketServer = WebSocket.Server; - -module.exports = WebSocket; diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/buffer-util.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/buffer-util.js deleted file mode 100644 index f7536e28e..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/buffer-util.js +++ /dev/null @@ -1,131 +0,0 @@ -'use strict'; - -const { EMPTY_BUFFER } = require('./constants'); - -const FastBuffer = Buffer[Symbol.species]; - -/** - * Merges an array of buffers into a new buffer. - * - * @param {Buffer[]} list The array of buffers to concat - * @param {Number} totalLength The total length of buffers in the list - * @return {Buffer} The resulting buffer - * @public - */ -function concat(list, totalLength) { - if (list.length === 0) return EMPTY_BUFFER; - if (list.length === 1) return list[0]; - - const target = Buffer.allocUnsafe(totalLength); - let offset = 0; - - for (let i = 0; i < list.length; i++) { - const buf = list[i]; - target.set(buf, offset); - offset += buf.length; - } - - if (offset < totalLength) { - return new FastBuffer(target.buffer, target.byteOffset, offset); - } - - return target; -} - -/** - * Masks a buffer using the given mask. - * - * @param {Buffer} source The buffer to mask - * @param {Buffer} mask The mask to use - * @param {Buffer} output The buffer where to store the result - * @param {Number} offset The offset at which to start writing - * @param {Number} length The number of bytes to mask. - * @public - */ -function _mask(source, mask, output, offset, length) { - for (let i = 0; i < length; i++) { - output[offset + i] = source[i] ^ mask[i & 3]; - } -} - -/** - * Unmasks a buffer using the given mask. - * - * @param {Buffer} buffer The buffer to unmask - * @param {Buffer} mask The mask to use - * @public - */ -function _unmask(buffer, mask) { - for (let i = 0; i < buffer.length; i++) { - buffer[i] ^= mask[i & 3]; - } -} - -/** - * Converts a buffer to an `ArrayBuffer`. - * - * @param {Buffer} buf The buffer to convert - * @return {ArrayBuffer} Converted buffer - * @public - */ -function toArrayBuffer(buf) { - if (buf.length === buf.buffer.byteLength) { - return buf.buffer; - } - - return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length); -} - -/** - * Converts `data` to a `Buffer`. - * - * @param {*} data The data to convert - * @return {Buffer} The buffer - * @throws {TypeError} - * @public - */ -function toBuffer(data) { - toBuffer.readOnly = true; - - if (Buffer.isBuffer(data)) return data; - - let buf; - - if (data instanceof ArrayBuffer) { - buf = new FastBuffer(data); - } else if (ArrayBuffer.isView(data)) { - buf = new FastBuffer(data.buffer, data.byteOffset, data.byteLength); - } else { - buf = Buffer.from(data); - toBuffer.readOnly = false; - } - - return buf; -} - -module.exports = { - concat, - mask: _mask, - toArrayBuffer, - toBuffer, - unmask: _unmask -}; - -/* istanbul ignore else */ -if (!process.env.WS_NO_BUFFER_UTIL) { - try { - const bufferUtil = require('bufferutil'); - - module.exports.mask = function (source, mask, output, offset, length) { - if (length < 48) _mask(source, mask, output, offset, length); - else bufferUtil.mask(source, mask, output, offset, length); - }; - - module.exports.unmask = function (buffer, mask) { - if (buffer.length < 32) _unmask(buffer, mask); - else bufferUtil.unmask(buffer, mask); - }; - } catch (e) { - // Continue regardless of the error. - } -} diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/constants.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/constants.js deleted file mode 100644 index 74214d466..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/constants.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; - -const BINARY_TYPES = ['nodebuffer', 'arraybuffer', 'fragments']; -const hasBlob = typeof Blob !== 'undefined'; - -if (hasBlob) BINARY_TYPES.push('blob'); - -module.exports = { - BINARY_TYPES, - EMPTY_BUFFER: Buffer.alloc(0), - GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', - hasBlob, - kForOnEventAttribute: Symbol('kIsForOnEventAttribute'), - kListener: Symbol('kListener'), - kStatusCode: Symbol('status-code'), - kWebSocket: Symbol('websocket'), - NOOP: () => {} -}; diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/event-target.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/event-target.js deleted file mode 100644 index fea4cbc52..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/event-target.js +++ /dev/null @@ -1,292 +0,0 @@ -'use strict'; - -const { kForOnEventAttribute, kListener } = require('./constants'); - -const kCode = Symbol('kCode'); -const kData = Symbol('kData'); -const kError = Symbol('kError'); -const kMessage = Symbol('kMessage'); -const kReason = Symbol('kReason'); -const kTarget = Symbol('kTarget'); -const kType = Symbol('kType'); -const kWasClean = Symbol('kWasClean'); - -/** - * Class representing an event. - */ -class Event { - /** - * Create a new `Event`. - * - * @param {String} type The name of the event - * @throws {TypeError} If the `type` argument is not specified - */ - constructor(type) { - this[kTarget] = null; - this[kType] = type; - } - - /** - * @type {*} - */ - get target() { - return this[kTarget]; - } - - /** - * @type {String} - */ - get type() { - return this[kType]; - } -} - -Object.defineProperty(Event.prototype, 'target', { enumerable: true }); -Object.defineProperty(Event.prototype, 'type', { enumerable: true }); - -/** - * Class representing a close event. - * - * @extends Event - */ -class CloseEvent extends Event { - /** - * Create a new `CloseEvent`. - * - * @param {String} type The name of the event - * @param {Object} [options] A dictionary object that allows for setting - * attributes via object members of the same name - * @param {Number} [options.code=0] The status code explaining why the - * connection was closed - * @param {String} [options.reason=''] A human-readable string explaining why - * the connection was closed - * @param {Boolean} [options.wasClean=false] Indicates whether or not the - * connection was cleanly closed - */ - constructor(type, options = {}) { - super(type); - - this[kCode] = options.code === undefined ? 0 : options.code; - this[kReason] = options.reason === undefined ? '' : options.reason; - this[kWasClean] = options.wasClean === undefined ? false : options.wasClean; - } - - /** - * @type {Number} - */ - get code() { - return this[kCode]; - } - - /** - * @type {String} - */ - get reason() { - return this[kReason]; - } - - /** - * @type {Boolean} - */ - get wasClean() { - return this[kWasClean]; - } -} - -Object.defineProperty(CloseEvent.prototype, 'code', { enumerable: true }); -Object.defineProperty(CloseEvent.prototype, 'reason', { enumerable: true }); -Object.defineProperty(CloseEvent.prototype, 'wasClean', { enumerable: true }); - -/** - * Class representing an error event. - * - * @extends Event - */ -class ErrorEvent extends Event { - /** - * Create a new `ErrorEvent`. - * - * @param {String} type The name of the event - * @param {Object} [options] A dictionary object that allows for setting - * attributes via object members of the same name - * @param {*} [options.error=null] The error that generated this event - * @param {String} [options.message=''] The error message - */ - constructor(type, options = {}) { - super(type); - - this[kError] = options.error === undefined ? null : options.error; - this[kMessage] = options.message === undefined ? '' : options.message; - } - - /** - * @type {*} - */ - get error() { - return this[kError]; - } - - /** - * @type {String} - */ - get message() { - return this[kMessage]; - } -} - -Object.defineProperty(ErrorEvent.prototype, 'error', { enumerable: true }); -Object.defineProperty(ErrorEvent.prototype, 'message', { enumerable: true }); - -/** - * Class representing a message event. - * - * @extends Event - */ -class MessageEvent extends Event { - /** - * Create a new `MessageEvent`. - * - * @param {String} type The name of the event - * @param {Object} [options] A dictionary object that allows for setting - * attributes via object members of the same name - * @param {*} [options.data=null] The message content - */ - constructor(type, options = {}) { - super(type); - - this[kData] = options.data === undefined ? null : options.data; - } - - /** - * @type {*} - */ - get data() { - return this[kData]; - } -} - -Object.defineProperty(MessageEvent.prototype, 'data', { enumerable: true }); - -/** - * This provides methods for emulating the `EventTarget` interface. It's not - * meant to be used directly. - * - * @mixin - */ -const EventTarget = { - /** - * Register an event listener. - * - * @param {String} type A string representing the event type to listen for - * @param {(Function|Object)} handler The listener to add - * @param {Object} [options] An options object specifies characteristics about - * the event listener - * @param {Boolean} [options.once=false] A `Boolean` indicating that the - * listener should be invoked at most once after being added. If `true`, - * the listener would be automatically removed when invoked. - * @public - */ - addEventListener(type, handler, options = {}) { - for (const listener of this.listeners(type)) { - if ( - !options[kForOnEventAttribute] && - listener[kListener] === handler && - !listener[kForOnEventAttribute] - ) { - return; - } - } - - let wrapper; - - if (type === 'message') { - wrapper = function onMessage(data, isBinary) { - const event = new MessageEvent('message', { - data: isBinary ? data : data.toString() - }); - - event[kTarget] = this; - callListener(handler, this, event); - }; - } else if (type === 'close') { - wrapper = function onClose(code, message) { - const event = new CloseEvent('close', { - code, - reason: message.toString(), - wasClean: this._closeFrameReceived && this._closeFrameSent - }); - - event[kTarget] = this; - callListener(handler, this, event); - }; - } else if (type === 'error') { - wrapper = function onError(error) { - const event = new ErrorEvent('error', { - error, - message: error.message - }); - - event[kTarget] = this; - callListener(handler, this, event); - }; - } else if (type === 'open') { - wrapper = function onOpen() { - const event = new Event('open'); - - event[kTarget] = this; - callListener(handler, this, event); - }; - } else { - return; - } - - wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute]; - wrapper[kListener] = handler; - - if (options.once) { - this.once(type, wrapper); - } else { - this.on(type, wrapper); - } - }, - - /** - * Remove an event listener. - * - * @param {String} type A string representing the event type to remove - * @param {(Function|Object)} handler The listener to remove - * @public - */ - removeEventListener(type, handler) { - for (const listener of this.listeners(type)) { - if (listener[kListener] === handler && !listener[kForOnEventAttribute]) { - this.removeListener(type, listener); - break; - } - } - } -}; - -module.exports = { - CloseEvent, - ErrorEvent, - Event, - EventTarget, - MessageEvent -}; - -/** - * Call an event listener - * - * @param {(Function|Object)} listener The listener to call - * @param {*} thisArg The value to use as `this`` when calling the listener - * @param {Event} event The event to pass to the listener - * @private - */ -function callListener(listener, thisArg, event) { - if (typeof listener === 'object' && listener.handleEvent) { - listener.handleEvent.call(listener, event); - } else { - listener.call(thisArg, event); - } -} diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/extension.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/extension.js deleted file mode 100644 index 3d7895c1b..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/extension.js +++ /dev/null @@ -1,203 +0,0 @@ -'use strict'; - -const { tokenChars } = require('./validation'); - -/** - * Adds an offer to the map of extension offers or a parameter to the map of - * parameters. - * - * @param {Object} dest The map of extension offers or parameters - * @param {String} name The extension or parameter name - * @param {(Object|Boolean|String)} elem The extension parameters or the - * parameter value - * @private - */ -function push(dest, name, elem) { - if (dest[name] === undefined) dest[name] = [elem]; - else dest[name].push(elem); -} - -/** - * Parses the `Sec-WebSocket-Extensions` header into an object. - * - * @param {String} header The field value of the header - * @return {Object} The parsed object - * @public - */ -function parse(header) { - const offers = Object.create(null); - let params = Object.create(null); - let mustUnescape = false; - let isEscaping = false; - let inQuotes = false; - let extensionName; - let paramName; - let start = -1; - let code = -1; - let end = -1; - let i = 0; - - for (; i < header.length; i++) { - code = header.charCodeAt(i); - - if (extensionName === undefined) { - if (end === -1 && tokenChars[code] === 1) { - if (start === -1) start = i; - } else if ( - i !== 0 && - (code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */ - ) { - if (end === -1 && start !== -1) end = i; - } else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) { - if (start === -1) { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - - if (end === -1) end = i; - const name = header.slice(start, end); - if (code === 0x2c) { - push(offers, name, params); - params = Object.create(null); - } else { - extensionName = name; - } - - start = end = -1; - } else { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - } else if (paramName === undefined) { - if (end === -1 && tokenChars[code] === 1) { - if (start === -1) start = i; - } else if (code === 0x20 || code === 0x09) { - if (end === -1 && start !== -1) end = i; - } else if (code === 0x3b || code === 0x2c) { - if (start === -1) { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - - if (end === -1) end = i; - push(params, header.slice(start, end), true); - if (code === 0x2c) { - push(offers, extensionName, params); - params = Object.create(null); - extensionName = undefined; - } - - start = end = -1; - } else if (code === 0x3d /* '=' */ && start !== -1 && end === -1) { - paramName = header.slice(start, i); - start = end = -1; - } else { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - } else { - // - // The value of a quoted-string after unescaping must conform to the - // token ABNF, so only token characters are valid. - // Ref: https://tools.ietf.org/html/rfc6455#section-9.1 - // - if (isEscaping) { - if (tokenChars[code] !== 1) { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - if (start === -1) start = i; - else if (!mustUnescape) mustUnescape = true; - isEscaping = false; - } else if (inQuotes) { - if (tokenChars[code] === 1) { - if (start === -1) start = i; - } else if (code === 0x22 /* '"' */ && start !== -1) { - inQuotes = false; - end = i; - } else if (code === 0x5c /* '\' */) { - isEscaping = true; - } else { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - } else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) { - inQuotes = true; - } else if (end === -1 && tokenChars[code] === 1) { - if (start === -1) start = i; - } else if (start !== -1 && (code === 0x20 || code === 0x09)) { - if (end === -1) end = i; - } else if (code === 0x3b || code === 0x2c) { - if (start === -1) { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - - if (end === -1) end = i; - let value = header.slice(start, end); - if (mustUnescape) { - value = value.replace(/\\/g, ''); - mustUnescape = false; - } - push(params, paramName, value); - if (code === 0x2c) { - push(offers, extensionName, params); - params = Object.create(null); - extensionName = undefined; - } - - paramName = undefined; - start = end = -1; - } else { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - } - } - - if (start === -1 || inQuotes || code === 0x20 || code === 0x09) { - throw new SyntaxError('Unexpected end of input'); - } - - if (end === -1) end = i; - const token = header.slice(start, end); - if (extensionName === undefined) { - push(offers, token, params); - } else { - if (paramName === undefined) { - push(params, token, true); - } else if (mustUnescape) { - push(params, paramName, token.replace(/\\/g, '')); - } else { - push(params, paramName, token); - } - push(offers, extensionName, params); - } - - return offers; -} - -/** - * Builds the `Sec-WebSocket-Extensions` header field value. - * - * @param {Object} extensions The map of extensions and parameters to format - * @return {String} A string representing the given object - * @public - */ -function format(extensions) { - return Object.keys(extensions) - .map((extension) => { - let configurations = extensions[extension]; - if (!Array.isArray(configurations)) configurations = [configurations]; - return configurations - .map((params) => { - return [extension] - .concat( - Object.keys(params).map((k) => { - let values = params[k]; - if (!Array.isArray(values)) values = [values]; - return values - .map((v) => (v === true ? k : `${k}=${v}`)) - .join('; '); - }) - ) - .join('; '); - }) - .join(', '); - }) - .join(', '); -} - -module.exports = { format, parse }; diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/limiter.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/limiter.js deleted file mode 100644 index 3fd35784e..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/limiter.js +++ /dev/null @@ -1,55 +0,0 @@ -'use strict'; - -const kDone = Symbol('kDone'); -const kRun = Symbol('kRun'); - -/** - * A very simple job queue with adjustable concurrency. Adapted from - * https://github.com/STRML/async-limiter - */ -class Limiter { - /** - * Creates a new `Limiter`. - * - * @param {Number} [concurrency=Infinity] The maximum number of jobs allowed - * to run concurrently - */ - constructor(concurrency) { - this[kDone] = () => { - this.pending--; - this[kRun](); - }; - this.concurrency = concurrency || Infinity; - this.jobs = []; - this.pending = 0; - } - - /** - * Adds a job to the queue. - * - * @param {Function} job The job to run - * @public - */ - add(job) { - this.jobs.push(job); - this[kRun](); - } - - /** - * Removes a job from the queue and runs it if possible. - * - * @private - */ - [kRun]() { - if (this.pending === this.concurrency) return; - - if (this.jobs.length) { - const job = this.jobs.shift(); - - this.pending++; - job(this[kDone]); - } - } -} - -module.exports = Limiter; diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/permessage-deflate.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/permessage-deflate.js deleted file mode 100644 index 41ff70e27..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/permessage-deflate.js +++ /dev/null @@ -1,528 +0,0 @@ -'use strict'; - -const zlib = require('zlib'); - -const bufferUtil = require('./buffer-util'); -const Limiter = require('./limiter'); -const { kStatusCode } = require('./constants'); - -const FastBuffer = Buffer[Symbol.species]; -const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]); -const kPerMessageDeflate = Symbol('permessage-deflate'); -const kTotalLength = Symbol('total-length'); -const kCallback = Symbol('callback'); -const kBuffers = Symbol('buffers'); -const kError = Symbol('error'); - -// -// We limit zlib concurrency, which prevents severe memory fragmentation -// as documented in https://github.com/nodejs/node/issues/8871#issuecomment-250915913 -// and https://github.com/websockets/ws/issues/1202 -// -// Intentionally global; it's the global thread pool that's an issue. -// -let zlibLimiter; - -/** - * permessage-deflate implementation. - */ -class PerMessageDeflate { - /** - * Creates a PerMessageDeflate instance. - * - * @param {Object} [options] Configuration options - * @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support - * for, or request, a custom client window size - * @param {Boolean} [options.clientNoContextTakeover=false] Advertise/ - * acknowledge disabling of client context takeover - * @param {Number} [options.concurrencyLimit=10] The number of concurrent - * calls to zlib - * @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the - * use of a custom server window size - * @param {Boolean} [options.serverNoContextTakeover=false] Request/accept - * disabling of server context takeover - * @param {Number} [options.threshold=1024] Size (in bytes) below which - * messages should not be compressed if context takeover is disabled - * @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on - * deflate - * @param {Object} [options.zlibInflateOptions] Options to pass to zlib on - * inflate - * @param {Boolean} [isServer=false] Create the instance in either server or - * client mode - * @param {Number} [maxPayload=0] The maximum allowed message length - */ - constructor(options, isServer, maxPayload) { - this._maxPayload = maxPayload | 0; - this._options = options || {}; - this._threshold = - this._options.threshold !== undefined ? this._options.threshold : 1024; - this._isServer = !!isServer; - this._deflate = null; - this._inflate = null; - - this.params = null; - - if (!zlibLimiter) { - const concurrency = - this._options.concurrencyLimit !== undefined - ? this._options.concurrencyLimit - : 10; - zlibLimiter = new Limiter(concurrency); - } - } - - /** - * @type {String} - */ - static get extensionName() { - return 'permessage-deflate'; - } - - /** - * Create an extension negotiation offer. - * - * @return {Object} Extension parameters - * @public - */ - offer() { - const params = {}; - - if (this._options.serverNoContextTakeover) { - params.server_no_context_takeover = true; - } - if (this._options.clientNoContextTakeover) { - params.client_no_context_takeover = true; - } - if (this._options.serverMaxWindowBits) { - params.server_max_window_bits = this._options.serverMaxWindowBits; - } - if (this._options.clientMaxWindowBits) { - params.client_max_window_bits = this._options.clientMaxWindowBits; - } else if (this._options.clientMaxWindowBits == null) { - params.client_max_window_bits = true; - } - - return params; - } - - /** - * Accept an extension negotiation offer/response. - * - * @param {Array} configurations The extension negotiation offers/reponse - * @return {Object} Accepted configuration - * @public - */ - accept(configurations) { - configurations = this.normalizeParams(configurations); - - this.params = this._isServer - ? this.acceptAsServer(configurations) - : this.acceptAsClient(configurations); - - return this.params; - } - - /** - * Releases all resources used by the extension. - * - * @public - */ - cleanup() { - if (this._inflate) { - this._inflate.close(); - this._inflate = null; - } - - if (this._deflate) { - const callback = this._deflate[kCallback]; - - this._deflate.close(); - this._deflate = null; - - if (callback) { - callback( - new Error( - 'The deflate stream was closed while data was being processed' - ) - ); - } - } - } - - /** - * Accept an extension negotiation offer. - * - * @param {Array} offers The extension negotiation offers - * @return {Object} Accepted configuration - * @private - */ - acceptAsServer(offers) { - const opts = this._options; - const accepted = offers.find((params) => { - if ( - (opts.serverNoContextTakeover === false && - params.server_no_context_takeover) || - (params.server_max_window_bits && - (opts.serverMaxWindowBits === false || - (typeof opts.serverMaxWindowBits === 'number' && - opts.serverMaxWindowBits > params.server_max_window_bits))) || - (typeof opts.clientMaxWindowBits === 'number' && - !params.client_max_window_bits) - ) { - return false; - } - - return true; - }); - - if (!accepted) { - throw new Error('None of the extension offers can be accepted'); - } - - if (opts.serverNoContextTakeover) { - accepted.server_no_context_takeover = true; - } - if (opts.clientNoContextTakeover) { - accepted.client_no_context_takeover = true; - } - if (typeof opts.serverMaxWindowBits === 'number') { - accepted.server_max_window_bits = opts.serverMaxWindowBits; - } - if (typeof opts.clientMaxWindowBits === 'number') { - accepted.client_max_window_bits = opts.clientMaxWindowBits; - } else if ( - accepted.client_max_window_bits === true || - opts.clientMaxWindowBits === false - ) { - delete accepted.client_max_window_bits; - } - - return accepted; - } - - /** - * Accept the extension negotiation response. - * - * @param {Array} response The extension negotiation response - * @return {Object} Accepted configuration - * @private - */ - acceptAsClient(response) { - const params = response[0]; - - if ( - this._options.clientNoContextTakeover === false && - params.client_no_context_takeover - ) { - throw new Error('Unexpected parameter "client_no_context_takeover"'); - } - - if (!params.client_max_window_bits) { - if (typeof this._options.clientMaxWindowBits === 'number') { - params.client_max_window_bits = this._options.clientMaxWindowBits; - } - } else if ( - this._options.clientMaxWindowBits === false || - (typeof this._options.clientMaxWindowBits === 'number' && - params.client_max_window_bits > this._options.clientMaxWindowBits) - ) { - throw new Error( - 'Unexpected or invalid parameter "client_max_window_bits"' - ); - } - - return params; - } - - /** - * Normalize parameters. - * - * @param {Array} configurations The extension negotiation offers/reponse - * @return {Array} The offers/response with normalized parameters - * @private - */ - normalizeParams(configurations) { - configurations.forEach((params) => { - Object.keys(params).forEach((key) => { - let value = params[key]; - - if (value.length > 1) { - throw new Error(`Parameter "${key}" must have only a single value`); - } - - value = value[0]; - - if (key === 'client_max_window_bits') { - if (value !== true) { - const num = +value; - if (!Number.isInteger(num) || num < 8 || num > 15) { - throw new TypeError( - `Invalid value for parameter "${key}": ${value}` - ); - } - value = num; - } else if (!this._isServer) { - throw new TypeError( - `Invalid value for parameter "${key}": ${value}` - ); - } - } else if (key === 'server_max_window_bits') { - const num = +value; - if (!Number.isInteger(num) || num < 8 || num > 15) { - throw new TypeError( - `Invalid value for parameter "${key}": ${value}` - ); - } - value = num; - } else if ( - key === 'client_no_context_takeover' || - key === 'server_no_context_takeover' - ) { - if (value !== true) { - throw new TypeError( - `Invalid value for parameter "${key}": ${value}` - ); - } - } else { - throw new Error(`Unknown parameter "${key}"`); - } - - params[key] = value; - }); - }); - - return configurations; - } - - /** - * Decompress data. Concurrency limited. - * - * @param {Buffer} data Compressed data - * @param {Boolean} fin Specifies whether or not this is the last fragment - * @param {Function} callback Callback - * @public - */ - decompress(data, fin, callback) { - zlibLimiter.add((done) => { - this._decompress(data, fin, (err, result) => { - done(); - callback(err, result); - }); - }); - } - - /** - * Compress data. Concurrency limited. - * - * @param {(Buffer|String)} data Data to compress - * @param {Boolean} fin Specifies whether or not this is the last fragment - * @param {Function} callback Callback - * @public - */ - compress(data, fin, callback) { - zlibLimiter.add((done) => { - this._compress(data, fin, (err, result) => { - done(); - callback(err, result); - }); - }); - } - - /** - * Decompress data. - * - * @param {Buffer} data Compressed data - * @param {Boolean} fin Specifies whether or not this is the last fragment - * @param {Function} callback Callback - * @private - */ - _decompress(data, fin, callback) { - const endpoint = this._isServer ? 'client' : 'server'; - - if (!this._inflate) { - const key = `${endpoint}_max_window_bits`; - const windowBits = - typeof this.params[key] !== 'number' - ? zlib.Z_DEFAULT_WINDOWBITS - : this.params[key]; - - this._inflate = zlib.createInflateRaw({ - ...this._options.zlibInflateOptions, - windowBits - }); - this._inflate[kPerMessageDeflate] = this; - this._inflate[kTotalLength] = 0; - this._inflate[kBuffers] = []; - this._inflate.on('error', inflateOnError); - this._inflate.on('data', inflateOnData); - } - - this._inflate[kCallback] = callback; - - this._inflate.write(data); - if (fin) this._inflate.write(TRAILER); - - this._inflate.flush(() => { - const err = this._inflate[kError]; - - if (err) { - this._inflate.close(); - this._inflate = null; - callback(err); - return; - } - - const data = bufferUtil.concat( - this._inflate[kBuffers], - this._inflate[kTotalLength] - ); - - if (this._inflate._readableState.endEmitted) { - this._inflate.close(); - this._inflate = null; - } else { - this._inflate[kTotalLength] = 0; - this._inflate[kBuffers] = []; - - if (fin && this.params[`${endpoint}_no_context_takeover`]) { - this._inflate.reset(); - } - } - - callback(null, data); - }); - } - - /** - * Compress data. - * - * @param {(Buffer|String)} data Data to compress - * @param {Boolean} fin Specifies whether or not this is the last fragment - * @param {Function} callback Callback - * @private - */ - _compress(data, fin, callback) { - const endpoint = this._isServer ? 'server' : 'client'; - - if (!this._deflate) { - const key = `${endpoint}_max_window_bits`; - const windowBits = - typeof this.params[key] !== 'number' - ? zlib.Z_DEFAULT_WINDOWBITS - : this.params[key]; - - this._deflate = zlib.createDeflateRaw({ - ...this._options.zlibDeflateOptions, - windowBits - }); - - this._deflate[kTotalLength] = 0; - this._deflate[kBuffers] = []; - - this._deflate.on('data', deflateOnData); - } - - this._deflate[kCallback] = callback; - - this._deflate.write(data); - this._deflate.flush(zlib.Z_SYNC_FLUSH, () => { - if (!this._deflate) { - // - // The deflate stream was closed while data was being processed. - // - return; - } - - let data = bufferUtil.concat( - this._deflate[kBuffers], - this._deflate[kTotalLength] - ); - - if (fin) { - data = new FastBuffer(data.buffer, data.byteOffset, data.length - 4); - } - - // - // Ensure that the callback will not be called again in - // `PerMessageDeflate#cleanup()`. - // - this._deflate[kCallback] = null; - - this._deflate[kTotalLength] = 0; - this._deflate[kBuffers] = []; - - if (fin && this.params[`${endpoint}_no_context_takeover`]) { - this._deflate.reset(); - } - - callback(null, data); - }); - } -} - -module.exports = PerMessageDeflate; - -/** - * The listener of the `zlib.DeflateRaw` stream `'data'` event. - * - * @param {Buffer} chunk A chunk of data - * @private - */ -function deflateOnData(chunk) { - this[kBuffers].push(chunk); - this[kTotalLength] += chunk.length; -} - -/** - * The listener of the `zlib.InflateRaw` stream `'data'` event. - * - * @param {Buffer} chunk A chunk of data - * @private - */ -function inflateOnData(chunk) { - this[kTotalLength] += chunk.length; - - if ( - this[kPerMessageDeflate]._maxPayload < 1 || - this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload - ) { - this[kBuffers].push(chunk); - return; - } - - this[kError] = new RangeError('Max payload size exceeded'); - this[kError].code = 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'; - this[kError][kStatusCode] = 1009; - this.removeListener('data', inflateOnData); - - // - // The choice to employ `zlib.reset()` over `zlib.close()` is dictated by the - // fact that in Node.js versions prior to 13.10.0, the callback for - // `zlib.flush()` is not called if `zlib.close()` is used. Utilizing - // `zlib.reset()` ensures that either the callback is invoked or an error is - // emitted. - // - this.reset(); -} - -/** - * The listener of the `zlib.InflateRaw` stream `'error'` event. - * - * @param {Error} err The emitted error - * @private - */ -function inflateOnError(err) { - // - // There is no need to call `Zlib#close()` as the handle is automatically - // closed when an error is emitted. - // - this[kPerMessageDeflate]._inflate = null; - - if (this[kError]) { - this[kCallback](this[kError]); - return; - } - - err[kStatusCode] = 1007; - this[kCallback](err); -} diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/receiver.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/receiver.js deleted file mode 100644 index 54d9b4fad..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/receiver.js +++ /dev/null @@ -1,706 +0,0 @@ -'use strict'; - -const { Writable } = require('stream'); - -const PerMessageDeflate = require('./permessage-deflate'); -const { - BINARY_TYPES, - EMPTY_BUFFER, - kStatusCode, - kWebSocket -} = require('./constants'); -const { concat, toArrayBuffer, unmask } = require('./buffer-util'); -const { isValidStatusCode, isValidUTF8 } = require('./validation'); - -const FastBuffer = Buffer[Symbol.species]; - -const GET_INFO = 0; -const GET_PAYLOAD_LENGTH_16 = 1; -const GET_PAYLOAD_LENGTH_64 = 2; -const GET_MASK = 3; -const GET_DATA = 4; -const INFLATING = 5; -const DEFER_EVENT = 6; - -/** - * HyBi Receiver implementation. - * - * @extends Writable - */ -class Receiver extends Writable { - /** - * Creates a Receiver instance. - * - * @param {Object} [options] Options object - * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether - * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted - * multiple times in the same tick - * @param {String} [options.binaryType=nodebuffer] The type for binary data - * @param {Object} [options.extensions] An object containing the negotiated - * extensions - * @param {Boolean} [options.isServer=false] Specifies whether to operate in - * client or server mode - * @param {Number} [options.maxPayload=0] The maximum allowed message length - * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or - * not to skip UTF-8 validation for text and close messages - */ - constructor(options = {}) { - super(); - - this._allowSynchronousEvents = - options.allowSynchronousEvents !== undefined - ? options.allowSynchronousEvents - : true; - this._binaryType = options.binaryType || BINARY_TYPES[0]; - this._extensions = options.extensions || {}; - this._isServer = !!options.isServer; - this._maxPayload = options.maxPayload | 0; - this._skipUTF8Validation = !!options.skipUTF8Validation; - this[kWebSocket] = undefined; - - this._bufferedBytes = 0; - this._buffers = []; - - this._compressed = false; - this._payloadLength = 0; - this._mask = undefined; - this._fragmented = 0; - this._masked = false; - this._fin = false; - this._opcode = 0; - - this._totalPayloadLength = 0; - this._messageLength = 0; - this._fragments = []; - - this._errored = false; - this._loop = false; - this._state = GET_INFO; - } - - /** - * Implements `Writable.prototype._write()`. - * - * @param {Buffer} chunk The chunk of data to write - * @param {String} encoding The character encoding of `chunk` - * @param {Function} cb Callback - * @private - */ - _write(chunk, encoding, cb) { - if (this._opcode === 0x08 && this._state == GET_INFO) return cb(); - - this._bufferedBytes += chunk.length; - this._buffers.push(chunk); - this.startLoop(cb); - } - - /** - * Consumes `n` bytes from the buffered data. - * - * @param {Number} n The number of bytes to consume - * @return {Buffer} The consumed bytes - * @private - */ - consume(n) { - this._bufferedBytes -= n; - - if (n === this._buffers[0].length) return this._buffers.shift(); - - if (n < this._buffers[0].length) { - const buf = this._buffers[0]; - this._buffers[0] = new FastBuffer( - buf.buffer, - buf.byteOffset + n, - buf.length - n - ); - - return new FastBuffer(buf.buffer, buf.byteOffset, n); - } - - const dst = Buffer.allocUnsafe(n); - - do { - const buf = this._buffers[0]; - const offset = dst.length - n; - - if (n >= buf.length) { - dst.set(this._buffers.shift(), offset); - } else { - dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset); - this._buffers[0] = new FastBuffer( - buf.buffer, - buf.byteOffset + n, - buf.length - n - ); - } - - n -= buf.length; - } while (n > 0); - - return dst; - } - - /** - * Starts the parsing loop. - * - * @param {Function} cb Callback - * @private - */ - startLoop(cb) { - this._loop = true; - - do { - switch (this._state) { - case GET_INFO: - this.getInfo(cb); - break; - case GET_PAYLOAD_LENGTH_16: - this.getPayloadLength16(cb); - break; - case GET_PAYLOAD_LENGTH_64: - this.getPayloadLength64(cb); - break; - case GET_MASK: - this.getMask(); - break; - case GET_DATA: - this.getData(cb); - break; - case INFLATING: - case DEFER_EVENT: - this._loop = false; - return; - } - } while (this._loop); - - if (!this._errored) cb(); - } - - /** - * Reads the first two bytes of a frame. - * - * @param {Function} cb Callback - * @private - */ - getInfo(cb) { - if (this._bufferedBytes < 2) { - this._loop = false; - return; - } - - const buf = this.consume(2); - - if ((buf[0] & 0x30) !== 0x00) { - const error = this.createError( - RangeError, - 'RSV2 and RSV3 must be clear', - true, - 1002, - 'WS_ERR_UNEXPECTED_RSV_2_3' - ); - - cb(error); - return; - } - - const compressed = (buf[0] & 0x40) === 0x40; - - if (compressed && !this._extensions[PerMessageDeflate.extensionName]) { - const error = this.createError( - RangeError, - 'RSV1 must be clear', - true, - 1002, - 'WS_ERR_UNEXPECTED_RSV_1' - ); - - cb(error); - return; - } - - this._fin = (buf[0] & 0x80) === 0x80; - this._opcode = buf[0] & 0x0f; - this._payloadLength = buf[1] & 0x7f; - - if (this._opcode === 0x00) { - if (compressed) { - const error = this.createError( - RangeError, - 'RSV1 must be clear', - true, - 1002, - 'WS_ERR_UNEXPECTED_RSV_1' - ); - - cb(error); - return; - } - - if (!this._fragmented) { - const error = this.createError( - RangeError, - 'invalid opcode 0', - true, - 1002, - 'WS_ERR_INVALID_OPCODE' - ); - - cb(error); - return; - } - - this._opcode = this._fragmented; - } else if (this._opcode === 0x01 || this._opcode === 0x02) { - if (this._fragmented) { - const error = this.createError( - RangeError, - `invalid opcode ${this._opcode}`, - true, - 1002, - 'WS_ERR_INVALID_OPCODE' - ); - - cb(error); - return; - } - - this._compressed = compressed; - } else if (this._opcode > 0x07 && this._opcode < 0x0b) { - if (!this._fin) { - const error = this.createError( - RangeError, - 'FIN must be set', - true, - 1002, - 'WS_ERR_EXPECTED_FIN' - ); - - cb(error); - return; - } - - if (compressed) { - const error = this.createError( - RangeError, - 'RSV1 must be clear', - true, - 1002, - 'WS_ERR_UNEXPECTED_RSV_1' - ); - - cb(error); - return; - } - - if ( - this._payloadLength > 0x7d || - (this._opcode === 0x08 && this._payloadLength === 1) - ) { - const error = this.createError( - RangeError, - `invalid payload length ${this._payloadLength}`, - true, - 1002, - 'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH' - ); - - cb(error); - return; - } - } else { - const error = this.createError( - RangeError, - `invalid opcode ${this._opcode}`, - true, - 1002, - 'WS_ERR_INVALID_OPCODE' - ); - - cb(error); - return; - } - - if (!this._fin && !this._fragmented) this._fragmented = this._opcode; - this._masked = (buf[1] & 0x80) === 0x80; - - if (this._isServer) { - if (!this._masked) { - const error = this.createError( - RangeError, - 'MASK must be set', - true, - 1002, - 'WS_ERR_EXPECTED_MASK' - ); - - cb(error); - return; - } - } else if (this._masked) { - const error = this.createError( - RangeError, - 'MASK must be clear', - true, - 1002, - 'WS_ERR_UNEXPECTED_MASK' - ); - - cb(error); - return; - } - - if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16; - else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64; - else this.haveLength(cb); - } - - /** - * Gets extended payload length (7+16). - * - * @param {Function} cb Callback - * @private - */ - getPayloadLength16(cb) { - if (this._bufferedBytes < 2) { - this._loop = false; - return; - } - - this._payloadLength = this.consume(2).readUInt16BE(0); - this.haveLength(cb); - } - - /** - * Gets extended payload length (7+64). - * - * @param {Function} cb Callback - * @private - */ - getPayloadLength64(cb) { - if (this._bufferedBytes < 8) { - this._loop = false; - return; - } - - const buf = this.consume(8); - const num = buf.readUInt32BE(0); - - // - // The maximum safe integer in JavaScript is 2^53 - 1. An error is returned - // if payload length is greater than this number. - // - if (num > Math.pow(2, 53 - 32) - 1) { - const error = this.createError( - RangeError, - 'Unsupported WebSocket frame: payload length > 2^53 - 1', - false, - 1009, - 'WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH' - ); - - cb(error); - return; - } - - this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4); - this.haveLength(cb); - } - - /** - * Payload length has been read. - * - * @param {Function} cb Callback - * @private - */ - haveLength(cb) { - if (this._payloadLength && this._opcode < 0x08) { - this._totalPayloadLength += this._payloadLength; - if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) { - const error = this.createError( - RangeError, - 'Max payload size exceeded', - false, - 1009, - 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH' - ); - - cb(error); - return; - } - } - - if (this._masked) this._state = GET_MASK; - else this._state = GET_DATA; - } - - /** - * Reads mask bytes. - * - * @private - */ - getMask() { - if (this._bufferedBytes < 4) { - this._loop = false; - return; - } - - this._mask = this.consume(4); - this._state = GET_DATA; - } - - /** - * Reads data bytes. - * - * @param {Function} cb Callback - * @private - */ - getData(cb) { - let data = EMPTY_BUFFER; - - if (this._payloadLength) { - if (this._bufferedBytes < this._payloadLength) { - this._loop = false; - return; - } - - data = this.consume(this._payloadLength); - - if ( - this._masked && - (this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) !== 0 - ) { - unmask(data, this._mask); - } - } - - if (this._opcode > 0x07) { - this.controlMessage(data, cb); - return; - } - - if (this._compressed) { - this._state = INFLATING; - this.decompress(data, cb); - return; - } - - if (data.length) { - // - // This message is not compressed so its length is the sum of the payload - // length of all fragments. - // - this._messageLength = this._totalPayloadLength; - this._fragments.push(data); - } - - this.dataMessage(cb); - } - - /** - * Decompresses data. - * - * @param {Buffer} data Compressed data - * @param {Function} cb Callback - * @private - */ - decompress(data, cb) { - const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; - - perMessageDeflate.decompress(data, this._fin, (err, buf) => { - if (err) return cb(err); - - if (buf.length) { - this._messageLength += buf.length; - if (this._messageLength > this._maxPayload && this._maxPayload > 0) { - const error = this.createError( - RangeError, - 'Max payload size exceeded', - false, - 1009, - 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH' - ); - - cb(error); - return; - } - - this._fragments.push(buf); - } - - this.dataMessage(cb); - if (this._state === GET_INFO) this.startLoop(cb); - }); - } - - /** - * Handles a data message. - * - * @param {Function} cb Callback - * @private - */ - dataMessage(cb) { - if (!this._fin) { - this._state = GET_INFO; - return; - } - - const messageLength = this._messageLength; - const fragments = this._fragments; - - this._totalPayloadLength = 0; - this._messageLength = 0; - this._fragmented = 0; - this._fragments = []; - - if (this._opcode === 2) { - let data; - - if (this._binaryType === 'nodebuffer') { - data = concat(fragments, messageLength); - } else if (this._binaryType === 'arraybuffer') { - data = toArrayBuffer(concat(fragments, messageLength)); - } else if (this._binaryType === 'blob') { - data = new Blob(fragments); - } else { - data = fragments; - } - - if (this._allowSynchronousEvents) { - this.emit('message', data, true); - this._state = GET_INFO; - } else { - this._state = DEFER_EVENT; - setImmediate(() => { - this.emit('message', data, true); - this._state = GET_INFO; - this.startLoop(cb); - }); - } - } else { - const buf = concat(fragments, messageLength); - - if (!this._skipUTF8Validation && !isValidUTF8(buf)) { - const error = this.createError( - Error, - 'invalid UTF-8 sequence', - true, - 1007, - 'WS_ERR_INVALID_UTF8' - ); - - cb(error); - return; - } - - if (this._state === INFLATING || this._allowSynchronousEvents) { - this.emit('message', buf, false); - this._state = GET_INFO; - } else { - this._state = DEFER_EVENT; - setImmediate(() => { - this.emit('message', buf, false); - this._state = GET_INFO; - this.startLoop(cb); - }); - } - } - } - - /** - * Handles a control message. - * - * @param {Buffer} data Data to handle - * @return {(Error|RangeError|undefined)} A possible error - * @private - */ - controlMessage(data, cb) { - if (this._opcode === 0x08) { - if (data.length === 0) { - this._loop = false; - this.emit('conclude', 1005, EMPTY_BUFFER); - this.end(); - } else { - const code = data.readUInt16BE(0); - - if (!isValidStatusCode(code)) { - const error = this.createError( - RangeError, - `invalid status code ${code}`, - true, - 1002, - 'WS_ERR_INVALID_CLOSE_CODE' - ); - - cb(error); - return; - } - - const buf = new FastBuffer( - data.buffer, - data.byteOffset + 2, - data.length - 2 - ); - - if (!this._skipUTF8Validation && !isValidUTF8(buf)) { - const error = this.createError( - Error, - 'invalid UTF-8 sequence', - true, - 1007, - 'WS_ERR_INVALID_UTF8' - ); - - cb(error); - return; - } - - this._loop = false; - this.emit('conclude', code, buf); - this.end(); - } - - this._state = GET_INFO; - return; - } - - if (this._allowSynchronousEvents) { - this.emit(this._opcode === 0x09 ? 'ping' : 'pong', data); - this._state = GET_INFO; - } else { - this._state = DEFER_EVENT; - setImmediate(() => { - this.emit(this._opcode === 0x09 ? 'ping' : 'pong', data); - this._state = GET_INFO; - this.startLoop(cb); - }); - } - } - - /** - * Builds an error object. - * - * @param {function(new:Error|RangeError)} ErrorCtor The error constructor - * @param {String} message The error message - * @param {Boolean} prefix Specifies whether or not to add a default prefix to - * `message` - * @param {Number} statusCode The status code - * @param {String} errorCode The exposed error code - * @return {(Error|RangeError)} The error - * @private - */ - createError(ErrorCtor, message, prefix, statusCode, errorCode) { - this._loop = false; - this._errored = true; - - const err = new ErrorCtor( - prefix ? `Invalid WebSocket frame: ${message}` : message - ); - - Error.captureStackTrace(err, this.createError); - err.code = errorCode; - err[kStatusCode] = statusCode; - return err; - } -} - -module.exports = Receiver; diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/sender.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/sender.js deleted file mode 100644 index a8b1da3a9..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/sender.js +++ /dev/null @@ -1,602 +0,0 @@ -/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex" }] */ - -'use strict'; - -const { Duplex } = require('stream'); -const { randomFillSync } = require('crypto'); - -const PerMessageDeflate = require('./permessage-deflate'); -const { EMPTY_BUFFER, kWebSocket, NOOP } = require('./constants'); -const { isBlob, isValidStatusCode } = require('./validation'); -const { mask: applyMask, toBuffer } = require('./buffer-util'); - -const kByteLength = Symbol('kByteLength'); -const maskBuffer = Buffer.alloc(4); -const RANDOM_POOL_SIZE = 8 * 1024; -let randomPool; -let randomPoolPointer = RANDOM_POOL_SIZE; - -const DEFAULT = 0; -const DEFLATING = 1; -const GET_BLOB_DATA = 2; - -/** - * HyBi Sender implementation. - */ -class Sender { - /** - * Creates a Sender instance. - * - * @param {Duplex} socket The connection socket - * @param {Object} [extensions] An object containing the negotiated extensions - * @param {Function} [generateMask] The function used to generate the masking - * key - */ - constructor(socket, extensions, generateMask) { - this._extensions = extensions || {}; - - if (generateMask) { - this._generateMask = generateMask; - this._maskBuffer = Buffer.alloc(4); - } - - this._socket = socket; - - this._firstFragment = true; - this._compress = false; - - this._bufferedBytes = 0; - this._queue = []; - this._state = DEFAULT; - this.onerror = NOOP; - this[kWebSocket] = undefined; - } - - /** - * Frames a piece of data according to the HyBi WebSocket protocol. - * - * @param {(Buffer|String)} data The data to frame - * @param {Object} options Options object - * @param {Boolean} [options.fin=false] Specifies whether or not to set the - * FIN bit - * @param {Function} [options.generateMask] The function used to generate the - * masking key - * @param {Boolean} [options.mask=false] Specifies whether or not to mask - * `data` - * @param {Buffer} [options.maskBuffer] The buffer used to store the masking - * key - * @param {Number} options.opcode The opcode - * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be - * modified - * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the - * RSV1 bit - * @return {(Buffer|String)[]} The framed data - * @public - */ - static frame(data, options) { - let mask; - let merge = false; - let offset = 2; - let skipMasking = false; - - if (options.mask) { - mask = options.maskBuffer || maskBuffer; - - if (options.generateMask) { - options.generateMask(mask); - } else { - if (randomPoolPointer === RANDOM_POOL_SIZE) { - /* istanbul ignore else */ - if (randomPool === undefined) { - // - // This is lazily initialized because server-sent frames must not - // be masked so it may never be used. - // - randomPool = Buffer.alloc(RANDOM_POOL_SIZE); - } - - randomFillSync(randomPool, 0, RANDOM_POOL_SIZE); - randomPoolPointer = 0; - } - - mask[0] = randomPool[randomPoolPointer++]; - mask[1] = randomPool[randomPoolPointer++]; - mask[2] = randomPool[randomPoolPointer++]; - mask[3] = randomPool[randomPoolPointer++]; - } - - skipMasking = (mask[0] | mask[1] | mask[2] | mask[3]) === 0; - offset = 6; - } - - let dataLength; - - if (typeof data === 'string') { - if ( - (!options.mask || skipMasking) && - options[kByteLength] !== undefined - ) { - dataLength = options[kByteLength]; - } else { - data = Buffer.from(data); - dataLength = data.length; - } - } else { - dataLength = data.length; - merge = options.mask && options.readOnly && !skipMasking; - } - - let payloadLength = dataLength; - - if (dataLength >= 65536) { - offset += 8; - payloadLength = 127; - } else if (dataLength > 125) { - offset += 2; - payloadLength = 126; - } - - const target = Buffer.allocUnsafe(merge ? dataLength + offset : offset); - - target[0] = options.fin ? options.opcode | 0x80 : options.opcode; - if (options.rsv1) target[0] |= 0x40; - - target[1] = payloadLength; - - if (payloadLength === 126) { - target.writeUInt16BE(dataLength, 2); - } else if (payloadLength === 127) { - target[2] = target[3] = 0; - target.writeUIntBE(dataLength, 4, 6); - } - - if (!options.mask) return [target, data]; - - target[1] |= 0x80; - target[offset - 4] = mask[0]; - target[offset - 3] = mask[1]; - target[offset - 2] = mask[2]; - target[offset - 1] = mask[3]; - - if (skipMasking) return [target, data]; - - if (merge) { - applyMask(data, mask, target, offset, dataLength); - return [target]; - } - - applyMask(data, mask, data, 0, dataLength); - return [target, data]; - } - - /** - * Sends a close message to the other peer. - * - * @param {Number} [code] The status code component of the body - * @param {(String|Buffer)} [data] The message component of the body - * @param {Boolean} [mask=false] Specifies whether or not to mask the message - * @param {Function} [cb] Callback - * @public - */ - close(code, data, mask, cb) { - let buf; - - if (code === undefined) { - buf = EMPTY_BUFFER; - } else if (typeof code !== 'number' || !isValidStatusCode(code)) { - throw new TypeError('First argument must be a valid error code number'); - } else if (data === undefined || !data.length) { - buf = Buffer.allocUnsafe(2); - buf.writeUInt16BE(code, 0); - } else { - const length = Buffer.byteLength(data); - - if (length > 123) { - throw new RangeError('The message must not be greater than 123 bytes'); - } - - buf = Buffer.allocUnsafe(2 + length); - buf.writeUInt16BE(code, 0); - - if (typeof data === 'string') { - buf.write(data, 2); - } else { - buf.set(data, 2); - } - } - - const options = { - [kByteLength]: buf.length, - fin: true, - generateMask: this._generateMask, - mask, - maskBuffer: this._maskBuffer, - opcode: 0x08, - readOnly: false, - rsv1: false - }; - - if (this._state !== DEFAULT) { - this.enqueue([this.dispatch, buf, false, options, cb]); - } else { - this.sendFrame(Sender.frame(buf, options), cb); - } - } - - /** - * Sends a ping message to the other peer. - * - * @param {*} data The message to send - * @param {Boolean} [mask=false] Specifies whether or not to mask `data` - * @param {Function} [cb] Callback - * @public - */ - ping(data, mask, cb) { - let byteLength; - let readOnly; - - if (typeof data === 'string') { - byteLength = Buffer.byteLength(data); - readOnly = false; - } else if (isBlob(data)) { - byteLength = data.size; - readOnly = false; - } else { - data = toBuffer(data); - byteLength = data.length; - readOnly = toBuffer.readOnly; - } - - if (byteLength > 125) { - throw new RangeError('The data size must not be greater than 125 bytes'); - } - - const options = { - [kByteLength]: byteLength, - fin: true, - generateMask: this._generateMask, - mask, - maskBuffer: this._maskBuffer, - opcode: 0x09, - readOnly, - rsv1: false - }; - - if (isBlob(data)) { - if (this._state !== DEFAULT) { - this.enqueue([this.getBlobData, data, false, options, cb]); - } else { - this.getBlobData(data, false, options, cb); - } - } else if (this._state !== DEFAULT) { - this.enqueue([this.dispatch, data, false, options, cb]); - } else { - this.sendFrame(Sender.frame(data, options), cb); - } - } - - /** - * Sends a pong message to the other peer. - * - * @param {*} data The message to send - * @param {Boolean} [mask=false] Specifies whether or not to mask `data` - * @param {Function} [cb] Callback - * @public - */ - pong(data, mask, cb) { - let byteLength; - let readOnly; - - if (typeof data === 'string') { - byteLength = Buffer.byteLength(data); - readOnly = false; - } else if (isBlob(data)) { - byteLength = data.size; - readOnly = false; - } else { - data = toBuffer(data); - byteLength = data.length; - readOnly = toBuffer.readOnly; - } - - if (byteLength > 125) { - throw new RangeError('The data size must not be greater than 125 bytes'); - } - - const options = { - [kByteLength]: byteLength, - fin: true, - generateMask: this._generateMask, - mask, - maskBuffer: this._maskBuffer, - opcode: 0x0a, - readOnly, - rsv1: false - }; - - if (isBlob(data)) { - if (this._state !== DEFAULT) { - this.enqueue([this.getBlobData, data, false, options, cb]); - } else { - this.getBlobData(data, false, options, cb); - } - } else if (this._state !== DEFAULT) { - this.enqueue([this.dispatch, data, false, options, cb]); - } else { - this.sendFrame(Sender.frame(data, options), cb); - } - } - - /** - * Sends a data message to the other peer. - * - * @param {*} data The message to send - * @param {Object} options Options object - * @param {Boolean} [options.binary=false] Specifies whether `data` is binary - * or text - * @param {Boolean} [options.compress=false] Specifies whether or not to - * compress `data` - * @param {Boolean} [options.fin=false] Specifies whether the fragment is the - * last one - * @param {Boolean} [options.mask=false] Specifies whether or not to mask - * `data` - * @param {Function} [cb] Callback - * @public - */ - send(data, options, cb) { - const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; - let opcode = options.binary ? 2 : 1; - let rsv1 = options.compress; - - let byteLength; - let readOnly; - - if (typeof data === 'string') { - byteLength = Buffer.byteLength(data); - readOnly = false; - } else if (isBlob(data)) { - byteLength = data.size; - readOnly = false; - } else { - data = toBuffer(data); - byteLength = data.length; - readOnly = toBuffer.readOnly; - } - - if (this._firstFragment) { - this._firstFragment = false; - if ( - rsv1 && - perMessageDeflate && - perMessageDeflate.params[ - perMessageDeflate._isServer - ? 'server_no_context_takeover' - : 'client_no_context_takeover' - ] - ) { - rsv1 = byteLength >= perMessageDeflate._threshold; - } - this._compress = rsv1; - } else { - rsv1 = false; - opcode = 0; - } - - if (options.fin) this._firstFragment = true; - - const opts = { - [kByteLength]: byteLength, - fin: options.fin, - generateMask: this._generateMask, - mask: options.mask, - maskBuffer: this._maskBuffer, - opcode, - readOnly, - rsv1 - }; - - if (isBlob(data)) { - if (this._state !== DEFAULT) { - this.enqueue([this.getBlobData, data, this._compress, opts, cb]); - } else { - this.getBlobData(data, this._compress, opts, cb); - } - } else if (this._state !== DEFAULT) { - this.enqueue([this.dispatch, data, this._compress, opts, cb]); - } else { - this.dispatch(data, this._compress, opts, cb); - } - } - - /** - * Gets the contents of a blob as binary data. - * - * @param {Blob} blob The blob - * @param {Boolean} [compress=false] Specifies whether or not to compress - * the data - * @param {Object} options Options object - * @param {Boolean} [options.fin=false] Specifies whether or not to set the - * FIN bit - * @param {Function} [options.generateMask] The function used to generate the - * masking key - * @param {Boolean} [options.mask=false] Specifies whether or not to mask - * `data` - * @param {Buffer} [options.maskBuffer] The buffer used to store the masking - * key - * @param {Number} options.opcode The opcode - * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be - * modified - * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the - * RSV1 bit - * @param {Function} [cb] Callback - * @private - */ - getBlobData(blob, compress, options, cb) { - this._bufferedBytes += options[kByteLength]; - this._state = GET_BLOB_DATA; - - blob - .arrayBuffer() - .then((arrayBuffer) => { - if (this._socket.destroyed) { - const err = new Error( - 'The socket was closed while the blob was being read' - ); - - // - // `callCallbacks` is called in the next tick to ensure that errors - // that might be thrown in the callbacks behave like errors thrown - // outside the promise chain. - // - process.nextTick(callCallbacks, this, err, cb); - return; - } - - this._bufferedBytes -= options[kByteLength]; - const data = toBuffer(arrayBuffer); - - if (!compress) { - this._state = DEFAULT; - this.sendFrame(Sender.frame(data, options), cb); - this.dequeue(); - } else { - this.dispatch(data, compress, options, cb); - } - }) - .catch((err) => { - // - // `onError` is called in the next tick for the same reason that - // `callCallbacks` above is. - // - process.nextTick(onError, this, err, cb); - }); - } - - /** - * Dispatches a message. - * - * @param {(Buffer|String)} data The message to send - * @param {Boolean} [compress=false] Specifies whether or not to compress - * `data` - * @param {Object} options Options object - * @param {Boolean} [options.fin=false] Specifies whether or not to set the - * FIN bit - * @param {Function} [options.generateMask] The function used to generate the - * masking key - * @param {Boolean} [options.mask=false] Specifies whether or not to mask - * `data` - * @param {Buffer} [options.maskBuffer] The buffer used to store the masking - * key - * @param {Number} options.opcode The opcode - * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be - * modified - * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the - * RSV1 bit - * @param {Function} [cb] Callback - * @private - */ - dispatch(data, compress, options, cb) { - if (!compress) { - this.sendFrame(Sender.frame(data, options), cb); - return; - } - - const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; - - this._bufferedBytes += options[kByteLength]; - this._state = DEFLATING; - perMessageDeflate.compress(data, options.fin, (_, buf) => { - if (this._socket.destroyed) { - const err = new Error( - 'The socket was closed while data was being compressed' - ); - - callCallbacks(this, err, cb); - return; - } - - this._bufferedBytes -= options[kByteLength]; - this._state = DEFAULT; - options.readOnly = false; - this.sendFrame(Sender.frame(buf, options), cb); - this.dequeue(); - }); - } - - /** - * Executes queued send operations. - * - * @private - */ - dequeue() { - while (this._state === DEFAULT && this._queue.length) { - const params = this._queue.shift(); - - this._bufferedBytes -= params[3][kByteLength]; - Reflect.apply(params[0], this, params.slice(1)); - } - } - - /** - * Enqueues a send operation. - * - * @param {Array} params Send operation parameters. - * @private - */ - enqueue(params) { - this._bufferedBytes += params[3][kByteLength]; - this._queue.push(params); - } - - /** - * Sends a frame. - * - * @param {(Buffer | String)[]} list The frame to send - * @param {Function} [cb] Callback - * @private - */ - sendFrame(list, cb) { - if (list.length === 2) { - this._socket.cork(); - this._socket.write(list[0]); - this._socket.write(list[1], cb); - this._socket.uncork(); - } else { - this._socket.write(list[0], cb); - } - } -} - -module.exports = Sender; - -/** - * Calls queued callbacks with an error. - * - * @param {Sender} sender The `Sender` instance - * @param {Error} err The error to call the callbacks with - * @param {Function} [cb] The first callback - * @private - */ -function callCallbacks(sender, err, cb) { - if (typeof cb === 'function') cb(err); - - for (let i = 0; i < sender._queue.length; i++) { - const params = sender._queue[i]; - const callback = params[params.length - 1]; - - if (typeof callback === 'function') callback(err); - } -} - -/** - * Handles a `Sender` error. - * - * @param {Sender} sender The `Sender` instance - * @param {Error} err The error - * @param {Function} [cb] The first pending callback - * @private - */ -function onError(sender, err, cb) { - callCallbacks(sender, err, cb); - sender.onerror(err); -} diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/stream.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/stream.js deleted file mode 100644 index 4c58c911b..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/stream.js +++ /dev/null @@ -1,161 +0,0 @@ -/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^WebSocket$" }] */ -'use strict'; - -const WebSocket = require('./websocket'); -const { Duplex } = require('stream'); - -/** - * Emits the `'close'` event on a stream. - * - * @param {Duplex} stream The stream. - * @private - */ -function emitClose(stream) { - stream.emit('close'); -} - -/** - * The listener of the `'end'` event. - * - * @private - */ -function duplexOnEnd() { - if (!this.destroyed && this._writableState.finished) { - this.destroy(); - } -} - -/** - * The listener of the `'error'` event. - * - * @param {Error} err The error - * @private - */ -function duplexOnError(err) { - this.removeListener('error', duplexOnError); - this.destroy(); - if (this.listenerCount('error') === 0) { - // Do not suppress the throwing behavior. - this.emit('error', err); - } -} - -/** - * Wraps a `WebSocket` in a duplex stream. - * - * @param {WebSocket} ws The `WebSocket` to wrap - * @param {Object} [options] The options for the `Duplex` constructor - * @return {Duplex} The duplex stream - * @public - */ -function createWebSocketStream(ws, options) { - let terminateOnDestroy = true; - - const duplex = new Duplex({ - ...options, - autoDestroy: false, - emitClose: false, - objectMode: false, - writableObjectMode: false - }); - - ws.on('message', function message(msg, isBinary) { - const data = - !isBinary && duplex._readableState.objectMode ? msg.toString() : msg; - - if (!duplex.push(data)) ws.pause(); - }); - - ws.once('error', function error(err) { - if (duplex.destroyed) return; - - // Prevent `ws.terminate()` from being called by `duplex._destroy()`. - // - // - If the `'error'` event is emitted before the `'open'` event, then - // `ws.terminate()` is a noop as no socket is assigned. - // - Otherwise, the error is re-emitted by the listener of the `'error'` - // event of the `Receiver` object. The listener already closes the - // connection by calling `ws.close()`. This allows a close frame to be - // sent to the other peer. If `ws.terminate()` is called right after this, - // then the close frame might not be sent. - terminateOnDestroy = false; - duplex.destroy(err); - }); - - ws.once('close', function close() { - if (duplex.destroyed) return; - - duplex.push(null); - }); - - duplex._destroy = function (err, callback) { - if (ws.readyState === ws.CLOSED) { - callback(err); - process.nextTick(emitClose, duplex); - return; - } - - let called = false; - - ws.once('error', function error(err) { - called = true; - callback(err); - }); - - ws.once('close', function close() { - if (!called) callback(err); - process.nextTick(emitClose, duplex); - }); - - if (terminateOnDestroy) ws.terminate(); - }; - - duplex._final = function (callback) { - if (ws.readyState === ws.CONNECTING) { - ws.once('open', function open() { - duplex._final(callback); - }); - return; - } - - // If the value of the `_socket` property is `null` it means that `ws` is a - // client websocket and the handshake failed. In fact, when this happens, a - // socket is never assigned to the websocket. Wait for the `'error'` event - // that will be emitted by the websocket. - if (ws._socket === null) return; - - if (ws._socket._writableState.finished) { - callback(); - if (duplex._readableState.endEmitted) duplex.destroy(); - } else { - ws._socket.once('finish', function finish() { - // `duplex` is not destroyed here because the `'end'` event will be - // emitted on `duplex` after this `'finish'` event. The EOF signaling - // `null` chunk is, in fact, pushed when the websocket emits `'close'`. - callback(); - }); - ws.close(); - } - }; - - duplex._read = function () { - if (ws.isPaused) ws.resume(); - }; - - duplex._write = function (chunk, encoding, callback) { - if (ws.readyState === ws.CONNECTING) { - ws.once('open', function open() { - duplex._write(chunk, encoding, callback); - }); - return; - } - - ws.send(chunk, callback); - }; - - duplex.on('end', duplexOnEnd); - duplex.on('error', duplexOnError); - return duplex; -} - -module.exports = createWebSocketStream; diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/subprotocol.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/subprotocol.js deleted file mode 100644 index d4381e886..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/subprotocol.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict'; - -const { tokenChars } = require('./validation'); - -/** - * Parses the `Sec-WebSocket-Protocol` header into a set of subprotocol names. - * - * @param {String} header The field value of the header - * @return {Set} The subprotocol names - * @public - */ -function parse(header) { - const protocols = new Set(); - let start = -1; - let end = -1; - let i = 0; - - for (i; i < header.length; i++) { - const code = header.charCodeAt(i); - - if (end === -1 && tokenChars[code] === 1) { - if (start === -1) start = i; - } else if ( - i !== 0 && - (code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */ - ) { - if (end === -1 && start !== -1) end = i; - } else if (code === 0x2c /* ',' */) { - if (start === -1) { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - - if (end === -1) end = i; - - const protocol = header.slice(start, end); - - if (protocols.has(protocol)) { - throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`); - } - - protocols.add(protocol); - start = end = -1; - } else { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - } - - if (start === -1 || end !== -1) { - throw new SyntaxError('Unexpected end of input'); - } - - const protocol = header.slice(start, i); - - if (protocols.has(protocol)) { - throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`); - } - - protocols.add(protocol); - return protocols; -} - -module.exports = { parse }; diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/validation.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/validation.js deleted file mode 100644 index 4a2e68d51..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/validation.js +++ /dev/null @@ -1,152 +0,0 @@ -'use strict'; - -const { isUtf8 } = require('buffer'); - -const { hasBlob } = require('./constants'); - -// -// Allowed token characters: -// -// '!', '#', '$', '%', '&', ''', '*', '+', '-', -// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~' -// -// tokenChars[32] === 0 // ' ' -// tokenChars[33] === 1 // '!' -// tokenChars[34] === 0 // '"' -// ... -// -// prettier-ignore -const tokenChars = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 - 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63 - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127 -]; - -/** - * Checks if a status code is allowed in a close frame. - * - * @param {Number} code The status code - * @return {Boolean} `true` if the status code is valid, else `false` - * @public - */ -function isValidStatusCode(code) { - return ( - (code >= 1000 && - code <= 1014 && - code !== 1004 && - code !== 1005 && - code !== 1006) || - (code >= 3000 && code <= 4999) - ); -} - -/** - * Checks if a given buffer contains only correct UTF-8. - * Ported from https://www.cl.cam.ac.uk/%7Emgk25/ucs/utf8_check.c by - * Markus Kuhn. - * - * @param {Buffer} buf The buffer to check - * @return {Boolean} `true` if `buf` contains only correct UTF-8, else `false` - * @public - */ -function _isValidUTF8(buf) { - const len = buf.length; - let i = 0; - - while (i < len) { - if ((buf[i] & 0x80) === 0) { - // 0xxxxxxx - i++; - } else if ((buf[i] & 0xe0) === 0xc0) { - // 110xxxxx 10xxxxxx - if ( - i + 1 === len || - (buf[i + 1] & 0xc0) !== 0x80 || - (buf[i] & 0xfe) === 0xc0 // Overlong - ) { - return false; - } - - i += 2; - } else if ((buf[i] & 0xf0) === 0xe0) { - // 1110xxxx 10xxxxxx 10xxxxxx - if ( - i + 2 >= len || - (buf[i + 1] & 0xc0) !== 0x80 || - (buf[i + 2] & 0xc0) !== 0x80 || - (buf[i] === 0xe0 && (buf[i + 1] & 0xe0) === 0x80) || // Overlong - (buf[i] === 0xed && (buf[i + 1] & 0xe0) === 0xa0) // Surrogate (U+D800 - U+DFFF) - ) { - return false; - } - - i += 3; - } else if ((buf[i] & 0xf8) === 0xf0) { - // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - if ( - i + 3 >= len || - (buf[i + 1] & 0xc0) !== 0x80 || - (buf[i + 2] & 0xc0) !== 0x80 || - (buf[i + 3] & 0xc0) !== 0x80 || - (buf[i] === 0xf0 && (buf[i + 1] & 0xf0) === 0x80) || // Overlong - (buf[i] === 0xf4 && buf[i + 1] > 0x8f) || - buf[i] > 0xf4 // > U+10FFFF - ) { - return false; - } - - i += 4; - } else { - return false; - } - } - - return true; -} - -/** - * Determines whether a value is a `Blob`. - * - * @param {*} value The value to be tested - * @return {Boolean} `true` if `value` is a `Blob`, else `false` - * @private - */ -function isBlob(value) { - return ( - hasBlob && - typeof value === 'object' && - typeof value.arrayBuffer === 'function' && - typeof value.type === 'string' && - typeof value.stream === 'function' && - (value[Symbol.toStringTag] === 'Blob' || - value[Symbol.toStringTag] === 'File') - ); -} - -module.exports = { - isBlob, - isValidStatusCode, - isValidUTF8: _isValidUTF8, - tokenChars -}; - -if (isUtf8) { - module.exports.isValidUTF8 = function (buf) { - return buf.length < 24 ? _isValidUTF8(buf) : isUtf8(buf); - }; -} /* istanbul ignore else */ else if (!process.env.WS_NO_UTF_8_VALIDATE) { - try { - const isValidUTF8 = require('utf-8-validate'); - - module.exports.isValidUTF8 = function (buf) { - return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF8(buf); - }; - } catch (e) { - // Continue regardless of the error. - } -} diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/websocket-server.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/websocket-server.js deleted file mode 100644 index 33e09858c..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/websocket-server.js +++ /dev/null @@ -1,550 +0,0 @@ -/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex$", "caughtErrors": "none" }] */ - -'use strict'; - -const EventEmitter = require('events'); -const http = require('http'); -const { Duplex } = require('stream'); -const { createHash } = require('crypto'); - -const extension = require('./extension'); -const PerMessageDeflate = require('./permessage-deflate'); -const subprotocol = require('./subprotocol'); -const WebSocket = require('./websocket'); -const { GUID, kWebSocket } = require('./constants'); - -const keyRegex = /^[+/0-9A-Za-z]{22}==$/; - -const RUNNING = 0; -const CLOSING = 1; -const CLOSED = 2; - -/** - * Class representing a WebSocket server. - * - * @extends EventEmitter - */ -class WebSocketServer extends EventEmitter { - /** - * Create a `WebSocketServer` instance. - * - * @param {Object} options Configuration options - * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether - * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted - * multiple times in the same tick - * @param {Boolean} [options.autoPong=true] Specifies whether or not to - * automatically send a pong in response to a ping - * @param {Number} [options.backlog=511] The maximum length of the queue of - * pending connections - * @param {Boolean} [options.clientTracking=true] Specifies whether or not to - * track clients - * @param {Function} [options.handleProtocols] A hook to handle protocols - * @param {String} [options.host] The hostname where to bind the server - * @param {Number} [options.maxPayload=104857600] The maximum allowed message - * size - * @param {Boolean} [options.noServer=false] Enable no server mode - * @param {String} [options.path] Accept only connections matching this path - * @param {(Boolean|Object)} [options.perMessageDeflate=false] Enable/disable - * permessage-deflate - * @param {Number} [options.port] The port where to bind the server - * @param {(http.Server|https.Server)} [options.server] A pre-created HTTP/S - * server to use - * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or - * not to skip UTF-8 validation for text and close messages - * @param {Function} [options.verifyClient] A hook to reject connections - * @param {Function} [options.WebSocket=WebSocket] Specifies the `WebSocket` - * class to use. It must be the `WebSocket` class or class that extends it - * @param {Function} [callback] A listener for the `listening` event - */ - constructor(options, callback) { - super(); - - options = { - allowSynchronousEvents: true, - autoPong: true, - maxPayload: 100 * 1024 * 1024, - skipUTF8Validation: false, - perMessageDeflate: false, - handleProtocols: null, - clientTracking: true, - verifyClient: null, - noServer: false, - backlog: null, // use default (511 as implemented in net.js) - server: null, - host: null, - path: null, - port: null, - WebSocket, - ...options - }; - - if ( - (options.port == null && !options.server && !options.noServer) || - (options.port != null && (options.server || options.noServer)) || - (options.server && options.noServer) - ) { - throw new TypeError( - 'One and only one of the "port", "server", or "noServer" options ' + - 'must be specified' - ); - } - - if (options.port != null) { - this._server = http.createServer((req, res) => { - const body = http.STATUS_CODES[426]; - - res.writeHead(426, { - 'Content-Length': body.length, - 'Content-Type': 'text/plain' - }); - res.end(body); - }); - this._server.listen( - options.port, - options.host, - options.backlog, - callback - ); - } else if (options.server) { - this._server = options.server; - } - - if (this._server) { - const emitConnection = this.emit.bind(this, 'connection'); - - this._removeListeners = addListeners(this._server, { - listening: this.emit.bind(this, 'listening'), - error: this.emit.bind(this, 'error'), - upgrade: (req, socket, head) => { - this.handleUpgrade(req, socket, head, emitConnection); - } - }); - } - - if (options.perMessageDeflate === true) options.perMessageDeflate = {}; - if (options.clientTracking) { - this.clients = new Set(); - this._shouldEmitClose = false; - } - - this.options = options; - this._state = RUNNING; - } - - /** - * Returns the bound address, the address family name, and port of the server - * as reported by the operating system if listening on an IP socket. - * If the server is listening on a pipe or UNIX domain socket, the name is - * returned as a string. - * - * @return {(Object|String|null)} The address of the server - * @public - */ - address() { - if (this.options.noServer) { - throw new Error('The server is operating in "noServer" mode'); - } - - if (!this._server) return null; - return this._server.address(); - } - - /** - * Stop the server from accepting new connections and emit the `'close'` event - * when all existing connections are closed. - * - * @param {Function} [cb] A one-time listener for the `'close'` event - * @public - */ - close(cb) { - if (this._state === CLOSED) { - if (cb) { - this.once('close', () => { - cb(new Error('The server is not running')); - }); - } - - process.nextTick(emitClose, this); - return; - } - - if (cb) this.once('close', cb); - - if (this._state === CLOSING) return; - this._state = CLOSING; - - if (this.options.noServer || this.options.server) { - if (this._server) { - this._removeListeners(); - this._removeListeners = this._server = null; - } - - if (this.clients) { - if (!this.clients.size) { - process.nextTick(emitClose, this); - } else { - this._shouldEmitClose = true; - } - } else { - process.nextTick(emitClose, this); - } - } else { - const server = this._server; - - this._removeListeners(); - this._removeListeners = this._server = null; - - // - // The HTTP/S server was created internally. Close it, and rely on its - // `'close'` event. - // - server.close(() => { - emitClose(this); - }); - } - } - - /** - * See if a given request should be handled by this server instance. - * - * @param {http.IncomingMessage} req Request object to inspect - * @return {Boolean} `true` if the request is valid, else `false` - * @public - */ - shouldHandle(req) { - if (this.options.path) { - const index = req.url.indexOf('?'); - const pathname = index !== -1 ? req.url.slice(0, index) : req.url; - - if (pathname !== this.options.path) return false; - } - - return true; - } - - /** - * Handle a HTTP Upgrade request. - * - * @param {http.IncomingMessage} req The request object - * @param {Duplex} socket The network socket between the server and client - * @param {Buffer} head The first packet of the upgraded stream - * @param {Function} cb Callback - * @public - */ - handleUpgrade(req, socket, head, cb) { - socket.on('error', socketOnError); - - const key = req.headers['sec-websocket-key']; - const upgrade = req.headers.upgrade; - const version = +req.headers['sec-websocket-version']; - - if (req.method !== 'GET') { - const message = 'Invalid HTTP method'; - abortHandshakeOrEmitwsClientError(this, req, socket, 405, message); - return; - } - - if (upgrade === undefined || upgrade.toLowerCase() !== 'websocket') { - const message = 'Invalid Upgrade header'; - abortHandshakeOrEmitwsClientError(this, req, socket, 400, message); - return; - } - - if (key === undefined || !keyRegex.test(key)) { - const message = 'Missing or invalid Sec-WebSocket-Key header'; - abortHandshakeOrEmitwsClientError(this, req, socket, 400, message); - return; - } - - if (version !== 13 && version !== 8) { - const message = 'Missing or invalid Sec-WebSocket-Version header'; - abortHandshakeOrEmitwsClientError(this, req, socket, 400, message, { - 'Sec-WebSocket-Version': '13, 8' - }); - return; - } - - if (!this.shouldHandle(req)) { - abortHandshake(socket, 400); - return; - } - - const secWebSocketProtocol = req.headers['sec-websocket-protocol']; - let protocols = new Set(); - - if (secWebSocketProtocol !== undefined) { - try { - protocols = subprotocol.parse(secWebSocketProtocol); - } catch (err) { - const message = 'Invalid Sec-WebSocket-Protocol header'; - abortHandshakeOrEmitwsClientError(this, req, socket, 400, message); - return; - } - } - - const secWebSocketExtensions = req.headers['sec-websocket-extensions']; - const extensions = {}; - - if ( - this.options.perMessageDeflate && - secWebSocketExtensions !== undefined - ) { - const perMessageDeflate = new PerMessageDeflate( - this.options.perMessageDeflate, - true, - this.options.maxPayload - ); - - try { - const offers = extension.parse(secWebSocketExtensions); - - if (offers[PerMessageDeflate.extensionName]) { - perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]); - extensions[PerMessageDeflate.extensionName] = perMessageDeflate; - } - } catch (err) { - const message = - 'Invalid or unacceptable Sec-WebSocket-Extensions header'; - abortHandshakeOrEmitwsClientError(this, req, socket, 400, message); - return; - } - } - - // - // Optionally call external client verification handler. - // - if (this.options.verifyClient) { - const info = { - origin: - req.headers[`${version === 8 ? 'sec-websocket-origin' : 'origin'}`], - secure: !!(req.socket.authorized || req.socket.encrypted), - req - }; - - if (this.options.verifyClient.length === 2) { - this.options.verifyClient(info, (verified, code, message, headers) => { - if (!verified) { - return abortHandshake(socket, code || 401, message, headers); - } - - this.completeUpgrade( - extensions, - key, - protocols, - req, - socket, - head, - cb - ); - }); - return; - } - - if (!this.options.verifyClient(info)) return abortHandshake(socket, 401); - } - - this.completeUpgrade(extensions, key, protocols, req, socket, head, cb); - } - - /** - * Upgrade the connection to WebSocket. - * - * @param {Object} extensions The accepted extensions - * @param {String} key The value of the `Sec-WebSocket-Key` header - * @param {Set} protocols The subprotocols - * @param {http.IncomingMessage} req The request object - * @param {Duplex} socket The network socket between the server and client - * @param {Buffer} head The first packet of the upgraded stream - * @param {Function} cb Callback - * @throws {Error} If called more than once with the same socket - * @private - */ - completeUpgrade(extensions, key, protocols, req, socket, head, cb) { - // - // Destroy the socket if the client has already sent a FIN packet. - // - if (!socket.readable || !socket.writable) return socket.destroy(); - - if (socket[kWebSocket]) { - throw new Error( - 'server.handleUpgrade() was called more than once with the same ' + - 'socket, possibly due to a misconfiguration' - ); - } - - if (this._state > RUNNING) return abortHandshake(socket, 503); - - const digest = createHash('sha1') - .update(key + GUID) - .digest('base64'); - - const headers = [ - 'HTTP/1.1 101 Switching Protocols', - 'Upgrade: websocket', - 'Connection: Upgrade', - `Sec-WebSocket-Accept: ${digest}` - ]; - - const ws = new this.options.WebSocket(null, undefined, this.options); - - if (protocols.size) { - // - // Optionally call external protocol selection handler. - // - const protocol = this.options.handleProtocols - ? this.options.handleProtocols(protocols, req) - : protocols.values().next().value; - - if (protocol) { - headers.push(`Sec-WebSocket-Protocol: ${protocol}`); - ws._protocol = protocol; - } - } - - if (extensions[PerMessageDeflate.extensionName]) { - const params = extensions[PerMessageDeflate.extensionName].params; - const value = extension.format({ - [PerMessageDeflate.extensionName]: [params] - }); - headers.push(`Sec-WebSocket-Extensions: ${value}`); - ws._extensions = extensions; - } - - // - // Allow external modification/inspection of handshake headers. - // - this.emit('headers', headers, req); - - socket.write(headers.concat('\r\n').join('\r\n')); - socket.removeListener('error', socketOnError); - - ws.setSocket(socket, head, { - allowSynchronousEvents: this.options.allowSynchronousEvents, - maxPayload: this.options.maxPayload, - skipUTF8Validation: this.options.skipUTF8Validation - }); - - if (this.clients) { - this.clients.add(ws); - ws.on('close', () => { - this.clients.delete(ws); - - if (this._shouldEmitClose && !this.clients.size) { - process.nextTick(emitClose, this); - } - }); - } - - cb(ws, req); - } -} - -module.exports = WebSocketServer; - -/** - * Add event listeners on an `EventEmitter` using a map of - * pairs. - * - * @param {EventEmitter} server The event emitter - * @param {Object.} map The listeners to add - * @return {Function} A function that will remove the added listeners when - * called - * @private - */ -function addListeners(server, map) { - for (const event of Object.keys(map)) server.on(event, map[event]); - - return function removeListeners() { - for (const event of Object.keys(map)) { - server.removeListener(event, map[event]); - } - }; -} - -/** - * Emit a `'close'` event on an `EventEmitter`. - * - * @param {EventEmitter} server The event emitter - * @private - */ -function emitClose(server) { - server._state = CLOSED; - server.emit('close'); -} - -/** - * Handle socket errors. - * - * @private - */ -function socketOnError() { - this.destroy(); -} - -/** - * Close the connection when preconditions are not fulfilled. - * - * @param {Duplex} socket The socket of the upgrade request - * @param {Number} code The HTTP response status code - * @param {String} [message] The HTTP response body - * @param {Object} [headers] Additional HTTP response headers - * @private - */ -function abortHandshake(socket, code, message, headers) { - // - // The socket is writable unless the user destroyed or ended it before calling - // `server.handleUpgrade()` or in the `verifyClient` function, which is a user - // error. Handling this does not make much sense as the worst that can happen - // is that some of the data written by the user might be discarded due to the - // call to `socket.end()` below, which triggers an `'error'` event that in - // turn causes the socket to be destroyed. - // - message = message || http.STATUS_CODES[code]; - headers = { - Connection: 'close', - 'Content-Type': 'text/html', - 'Content-Length': Buffer.byteLength(message), - ...headers - }; - - socket.once('finish', socket.destroy); - - socket.end( - `HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` + - Object.keys(headers) - .map((h) => `${h}: ${headers[h]}`) - .join('\r\n') + - '\r\n\r\n' + - message - ); -} - -/** - * Emit a `'wsClientError'` event on a `WebSocketServer` if there is at least - * one listener for it, otherwise call `abortHandshake()`. - * - * @param {WebSocketServer} server The WebSocket server - * @param {http.IncomingMessage} req The request object - * @param {Duplex} socket The socket of the upgrade request - * @param {Number} code The HTTP response status code - * @param {String} message The HTTP response body - * @param {Object} [headers] The HTTP response headers - * @private - */ -function abortHandshakeOrEmitwsClientError( - server, - req, - socket, - code, - message, - headers -) { - if (server.listenerCount('wsClientError')) { - const err = new Error(message); - Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError); - - server.emit('wsClientError', err, socket, req); - } else { - abortHandshake(socket, code, message, headers); - } -} diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/websocket.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/websocket.js deleted file mode 100644 index ad8764a02..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/lib/websocket.js +++ /dev/null @@ -1,1388 +0,0 @@ -/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex|Readable$", "caughtErrors": "none" }] */ - -'use strict'; - -const EventEmitter = require('events'); -const https = require('https'); -const http = require('http'); -const net = require('net'); -const tls = require('tls'); -const { randomBytes, createHash } = require('crypto'); -const { Duplex, Readable } = require('stream'); -const { URL } = require('url'); - -const PerMessageDeflate = require('./permessage-deflate'); -const Receiver = require('./receiver'); -const Sender = require('./sender'); -const { isBlob } = require('./validation'); - -const { - BINARY_TYPES, - EMPTY_BUFFER, - GUID, - kForOnEventAttribute, - kListener, - kStatusCode, - kWebSocket, - NOOP -} = require('./constants'); -const { - EventTarget: { addEventListener, removeEventListener } -} = require('./event-target'); -const { format, parse } = require('./extension'); -const { toBuffer } = require('./buffer-util'); - -const closeTimeout = 30 * 1000; -const kAborted = Symbol('kAborted'); -const protocolVersions = [8, 13]; -const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED']; -const subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/; - -/** - * Class representing a WebSocket. - * - * @extends EventEmitter - */ -class WebSocket extends EventEmitter { - /** - * Create a new `WebSocket`. - * - * @param {(String|URL)} address The URL to which to connect - * @param {(String|String[])} [protocols] The subprotocols - * @param {Object} [options] Connection options - */ - constructor(address, protocols, options) { - super(); - - this._binaryType = BINARY_TYPES[0]; - this._closeCode = 1006; - this._closeFrameReceived = false; - this._closeFrameSent = false; - this._closeMessage = EMPTY_BUFFER; - this._closeTimer = null; - this._errorEmitted = false; - this._extensions = {}; - this._paused = false; - this._protocol = ''; - this._readyState = WebSocket.CONNECTING; - this._receiver = null; - this._sender = null; - this._socket = null; - - if (address !== null) { - this._bufferedAmount = 0; - this._isServer = false; - this._redirects = 0; - - if (protocols === undefined) { - protocols = []; - } else if (!Array.isArray(protocols)) { - if (typeof protocols === 'object' && protocols !== null) { - options = protocols; - protocols = []; - } else { - protocols = [protocols]; - } - } - - initAsClient(this, address, protocols, options); - } else { - this._autoPong = options.autoPong; - this._isServer = true; - } - } - - /** - * For historical reasons, the custom "nodebuffer" type is used by the default - * instead of "blob". - * - * @type {String} - */ - get binaryType() { - return this._binaryType; - } - - set binaryType(type) { - if (!BINARY_TYPES.includes(type)) return; - - this._binaryType = type; - - // - // Allow to change `binaryType` on the fly. - // - if (this._receiver) this._receiver._binaryType = type; - } - - /** - * @type {Number} - */ - get bufferedAmount() { - if (!this._socket) return this._bufferedAmount; - - return this._socket._writableState.length + this._sender._bufferedBytes; - } - - /** - * @type {String} - */ - get extensions() { - return Object.keys(this._extensions).join(); - } - - /** - * @type {Boolean} - */ - get isPaused() { - return this._paused; - } - - /** - * @type {Function} - */ - /* istanbul ignore next */ - get onclose() { - return null; - } - - /** - * @type {Function} - */ - /* istanbul ignore next */ - get onerror() { - return null; - } - - /** - * @type {Function} - */ - /* istanbul ignore next */ - get onopen() { - return null; - } - - /** - * @type {Function} - */ - /* istanbul ignore next */ - get onmessage() { - return null; - } - - /** - * @type {String} - */ - get protocol() { - return this._protocol; - } - - /** - * @type {Number} - */ - get readyState() { - return this._readyState; - } - - /** - * @type {String} - */ - get url() { - return this._url; - } - - /** - * Set up the socket and the internal resources. - * - * @param {Duplex} socket The network socket between the server and client - * @param {Buffer} head The first packet of the upgraded stream - * @param {Object} options Options object - * @param {Boolean} [options.allowSynchronousEvents=false] Specifies whether - * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted - * multiple times in the same tick - * @param {Function} [options.generateMask] The function used to generate the - * masking key - * @param {Number} [options.maxPayload=0] The maximum allowed message size - * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or - * not to skip UTF-8 validation for text and close messages - * @private - */ - setSocket(socket, head, options) { - const receiver = new Receiver({ - allowSynchronousEvents: options.allowSynchronousEvents, - binaryType: this.binaryType, - extensions: this._extensions, - isServer: this._isServer, - maxPayload: options.maxPayload, - skipUTF8Validation: options.skipUTF8Validation - }); - - const sender = new Sender(socket, this._extensions, options.generateMask); - - this._receiver = receiver; - this._sender = sender; - this._socket = socket; - - receiver[kWebSocket] = this; - sender[kWebSocket] = this; - socket[kWebSocket] = this; - - receiver.on('conclude', receiverOnConclude); - receiver.on('drain', receiverOnDrain); - receiver.on('error', receiverOnError); - receiver.on('message', receiverOnMessage); - receiver.on('ping', receiverOnPing); - receiver.on('pong', receiverOnPong); - - sender.onerror = senderOnError; - - // - // These methods may not be available if `socket` is just a `Duplex`. - // - if (socket.setTimeout) socket.setTimeout(0); - if (socket.setNoDelay) socket.setNoDelay(); - - if (head.length > 0) socket.unshift(head); - - socket.on('close', socketOnClose); - socket.on('data', socketOnData); - socket.on('end', socketOnEnd); - socket.on('error', socketOnError); - - this._readyState = WebSocket.OPEN; - this.emit('open'); - } - - /** - * Emit the `'close'` event. - * - * @private - */ - emitClose() { - if (!this._socket) { - this._readyState = WebSocket.CLOSED; - this.emit('close', this._closeCode, this._closeMessage); - return; - } - - if (this._extensions[PerMessageDeflate.extensionName]) { - this._extensions[PerMessageDeflate.extensionName].cleanup(); - } - - this._receiver.removeAllListeners(); - this._readyState = WebSocket.CLOSED; - this.emit('close', this._closeCode, this._closeMessage); - } - - /** - * Start a closing handshake. - * - * +----------+ +-----------+ +----------+ - * - - -|ws.close()|-->|close frame|-->|ws.close()|- - - - * | +----------+ +-----------+ +----------+ | - * +----------+ +-----------+ | - * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING - * +----------+ +-----------+ | - * | | | +---+ | - * +------------------------+-->|fin| - - - - - * | +---+ | +---+ - * - - - - -|fin|<---------------------+ - * +---+ - * - * @param {Number} [code] Status code explaining why the connection is closing - * @param {(String|Buffer)} [data] The reason why the connection is - * closing - * @public - */ - close(code, data) { - if (this.readyState === WebSocket.CLOSED) return; - if (this.readyState === WebSocket.CONNECTING) { - const msg = 'WebSocket was closed before the connection was established'; - abortHandshake(this, this._req, msg); - return; - } - - if (this.readyState === WebSocket.CLOSING) { - if ( - this._closeFrameSent && - (this._closeFrameReceived || this._receiver._writableState.errorEmitted) - ) { - this._socket.end(); - } - - return; - } - - this._readyState = WebSocket.CLOSING; - this._sender.close(code, data, !this._isServer, (err) => { - // - // This error is handled by the `'error'` listener on the socket. We only - // want to know if the close frame has been sent here. - // - if (err) return; - - this._closeFrameSent = true; - - if ( - this._closeFrameReceived || - this._receiver._writableState.errorEmitted - ) { - this._socket.end(); - } - }); - - setCloseTimer(this); - } - - /** - * Pause the socket. - * - * @public - */ - pause() { - if ( - this.readyState === WebSocket.CONNECTING || - this.readyState === WebSocket.CLOSED - ) { - return; - } - - this._paused = true; - this._socket.pause(); - } - - /** - * Send a ping. - * - * @param {*} [data] The data to send - * @param {Boolean} [mask] Indicates whether or not to mask `data` - * @param {Function} [cb] Callback which is executed when the ping is sent - * @public - */ - ping(data, mask, cb) { - if (this.readyState === WebSocket.CONNECTING) { - throw new Error('WebSocket is not open: readyState 0 (CONNECTING)'); - } - - if (typeof data === 'function') { - cb = data; - data = mask = undefined; - } else if (typeof mask === 'function') { - cb = mask; - mask = undefined; - } - - if (typeof data === 'number') data = data.toString(); - - if (this.readyState !== WebSocket.OPEN) { - sendAfterClose(this, data, cb); - return; - } - - if (mask === undefined) mask = !this._isServer; - this._sender.ping(data || EMPTY_BUFFER, mask, cb); - } - - /** - * Send a pong. - * - * @param {*} [data] The data to send - * @param {Boolean} [mask] Indicates whether or not to mask `data` - * @param {Function} [cb] Callback which is executed when the pong is sent - * @public - */ - pong(data, mask, cb) { - if (this.readyState === WebSocket.CONNECTING) { - throw new Error('WebSocket is not open: readyState 0 (CONNECTING)'); - } - - if (typeof data === 'function') { - cb = data; - data = mask = undefined; - } else if (typeof mask === 'function') { - cb = mask; - mask = undefined; - } - - if (typeof data === 'number') data = data.toString(); - - if (this.readyState !== WebSocket.OPEN) { - sendAfterClose(this, data, cb); - return; - } - - if (mask === undefined) mask = !this._isServer; - this._sender.pong(data || EMPTY_BUFFER, mask, cb); - } - - /** - * Resume the socket. - * - * @public - */ - resume() { - if ( - this.readyState === WebSocket.CONNECTING || - this.readyState === WebSocket.CLOSED - ) { - return; - } - - this._paused = false; - if (!this._receiver._writableState.needDrain) this._socket.resume(); - } - - /** - * Send a data message. - * - * @param {*} data The message to send - * @param {Object} [options] Options object - * @param {Boolean} [options.binary] Specifies whether `data` is binary or - * text - * @param {Boolean} [options.compress] Specifies whether or not to compress - * `data` - * @param {Boolean} [options.fin=true] Specifies whether the fragment is the - * last one - * @param {Boolean} [options.mask] Specifies whether or not to mask `data` - * @param {Function} [cb] Callback which is executed when data is written out - * @public - */ - send(data, options, cb) { - if (this.readyState === WebSocket.CONNECTING) { - throw new Error('WebSocket is not open: readyState 0 (CONNECTING)'); - } - - if (typeof options === 'function') { - cb = options; - options = {}; - } - - if (typeof data === 'number') data = data.toString(); - - if (this.readyState !== WebSocket.OPEN) { - sendAfterClose(this, data, cb); - return; - } - - const opts = { - binary: typeof data !== 'string', - mask: !this._isServer, - compress: true, - fin: true, - ...options - }; - - if (!this._extensions[PerMessageDeflate.extensionName]) { - opts.compress = false; - } - - this._sender.send(data || EMPTY_BUFFER, opts, cb); - } - - /** - * Forcibly close the connection. - * - * @public - */ - terminate() { - if (this.readyState === WebSocket.CLOSED) return; - if (this.readyState === WebSocket.CONNECTING) { - const msg = 'WebSocket was closed before the connection was established'; - abortHandshake(this, this._req, msg); - return; - } - - if (this._socket) { - this._readyState = WebSocket.CLOSING; - this._socket.destroy(); - } - } -} - -/** - * @constant {Number} CONNECTING - * @memberof WebSocket - */ -Object.defineProperty(WebSocket, 'CONNECTING', { - enumerable: true, - value: readyStates.indexOf('CONNECTING') -}); - -/** - * @constant {Number} CONNECTING - * @memberof WebSocket.prototype - */ -Object.defineProperty(WebSocket.prototype, 'CONNECTING', { - enumerable: true, - value: readyStates.indexOf('CONNECTING') -}); - -/** - * @constant {Number} OPEN - * @memberof WebSocket - */ -Object.defineProperty(WebSocket, 'OPEN', { - enumerable: true, - value: readyStates.indexOf('OPEN') -}); - -/** - * @constant {Number} OPEN - * @memberof WebSocket.prototype - */ -Object.defineProperty(WebSocket.prototype, 'OPEN', { - enumerable: true, - value: readyStates.indexOf('OPEN') -}); - -/** - * @constant {Number} CLOSING - * @memberof WebSocket - */ -Object.defineProperty(WebSocket, 'CLOSING', { - enumerable: true, - value: readyStates.indexOf('CLOSING') -}); - -/** - * @constant {Number} CLOSING - * @memberof WebSocket.prototype - */ -Object.defineProperty(WebSocket.prototype, 'CLOSING', { - enumerable: true, - value: readyStates.indexOf('CLOSING') -}); - -/** - * @constant {Number} CLOSED - * @memberof WebSocket - */ -Object.defineProperty(WebSocket, 'CLOSED', { - enumerable: true, - value: readyStates.indexOf('CLOSED') -}); - -/** - * @constant {Number} CLOSED - * @memberof WebSocket.prototype - */ -Object.defineProperty(WebSocket.prototype, 'CLOSED', { - enumerable: true, - value: readyStates.indexOf('CLOSED') -}); - -[ - 'binaryType', - 'bufferedAmount', - 'extensions', - 'isPaused', - 'protocol', - 'readyState', - 'url' -].forEach((property) => { - Object.defineProperty(WebSocket.prototype, property, { enumerable: true }); -}); - -// -// Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes. -// See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface -// -['open', 'error', 'close', 'message'].forEach((method) => { - Object.defineProperty(WebSocket.prototype, `on${method}`, { - enumerable: true, - get() { - for (const listener of this.listeners(method)) { - if (listener[kForOnEventAttribute]) return listener[kListener]; - } - - return null; - }, - set(handler) { - for (const listener of this.listeners(method)) { - if (listener[kForOnEventAttribute]) { - this.removeListener(method, listener); - break; - } - } - - if (typeof handler !== 'function') return; - - this.addEventListener(method, handler, { - [kForOnEventAttribute]: true - }); - } - }); -}); - -WebSocket.prototype.addEventListener = addEventListener; -WebSocket.prototype.removeEventListener = removeEventListener; - -module.exports = WebSocket; - -/** - * Initialize a WebSocket client. - * - * @param {WebSocket} websocket The client to initialize - * @param {(String|URL)} address The URL to which to connect - * @param {Array} protocols The subprotocols - * @param {Object} [options] Connection options - * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether any - * of the `'message'`, `'ping'`, and `'pong'` events can be emitted multiple - * times in the same tick - * @param {Boolean} [options.autoPong=true] Specifies whether or not to - * automatically send a pong in response to a ping - * @param {Function} [options.finishRequest] A function which can be used to - * customize the headers of each http request before it is sent - * @param {Boolean} [options.followRedirects=false] Whether or not to follow - * redirects - * @param {Function} [options.generateMask] The function used to generate the - * masking key - * @param {Number} [options.handshakeTimeout] Timeout in milliseconds for the - * handshake request - * @param {Number} [options.maxPayload=104857600] The maximum allowed message - * size - * @param {Number} [options.maxRedirects=10] The maximum number of redirects - * allowed - * @param {String} [options.origin] Value of the `Origin` or - * `Sec-WebSocket-Origin` header - * @param {(Boolean|Object)} [options.perMessageDeflate=true] Enable/disable - * permessage-deflate - * @param {Number} [options.protocolVersion=13] Value of the - * `Sec-WebSocket-Version` header - * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or - * not to skip UTF-8 validation for text and close messages - * @private - */ -function initAsClient(websocket, address, protocols, options) { - const opts = { - allowSynchronousEvents: true, - autoPong: true, - protocolVersion: protocolVersions[1], - maxPayload: 100 * 1024 * 1024, - skipUTF8Validation: false, - perMessageDeflate: true, - followRedirects: false, - maxRedirects: 10, - ...options, - socketPath: undefined, - hostname: undefined, - protocol: undefined, - timeout: undefined, - method: 'GET', - host: undefined, - path: undefined, - port: undefined - }; - - websocket._autoPong = opts.autoPong; - - if (!protocolVersions.includes(opts.protocolVersion)) { - throw new RangeError( - `Unsupported protocol version: ${opts.protocolVersion} ` + - `(supported versions: ${protocolVersions.join(', ')})` - ); - } - - let parsedUrl; - - if (address instanceof URL) { - parsedUrl = address; - } else { - try { - parsedUrl = new URL(address); - } catch (e) { - throw new SyntaxError(`Invalid URL: ${address}`); - } - } - - if (parsedUrl.protocol === 'http:') { - parsedUrl.protocol = 'ws:'; - } else if (parsedUrl.protocol === 'https:') { - parsedUrl.protocol = 'wss:'; - } - - websocket._url = parsedUrl.href; - - const isSecure = parsedUrl.protocol === 'wss:'; - const isIpcUrl = parsedUrl.protocol === 'ws+unix:'; - let invalidUrlMessage; - - if (parsedUrl.protocol !== 'ws:' && !isSecure && !isIpcUrl) { - invalidUrlMessage = - 'The URL\'s protocol must be one of "ws:", "wss:", ' + - '"http:", "https:", or "ws+unix:"'; - } else if (isIpcUrl && !parsedUrl.pathname) { - invalidUrlMessage = "The URL's pathname is empty"; - } else if (parsedUrl.hash) { - invalidUrlMessage = 'The URL contains a fragment identifier'; - } - - if (invalidUrlMessage) { - const err = new SyntaxError(invalidUrlMessage); - - if (websocket._redirects === 0) { - throw err; - } else { - emitErrorAndClose(websocket, err); - return; - } - } - - const defaultPort = isSecure ? 443 : 80; - const key = randomBytes(16).toString('base64'); - const request = isSecure ? https.request : http.request; - const protocolSet = new Set(); - let perMessageDeflate; - - opts.createConnection = - opts.createConnection || (isSecure ? tlsConnect : netConnect); - opts.defaultPort = opts.defaultPort || defaultPort; - opts.port = parsedUrl.port || defaultPort; - opts.host = parsedUrl.hostname.startsWith('[') - ? parsedUrl.hostname.slice(1, -1) - : parsedUrl.hostname; - opts.headers = { - ...opts.headers, - 'Sec-WebSocket-Version': opts.protocolVersion, - 'Sec-WebSocket-Key': key, - Connection: 'Upgrade', - Upgrade: 'websocket' - }; - opts.path = parsedUrl.pathname + parsedUrl.search; - opts.timeout = opts.handshakeTimeout; - - if (opts.perMessageDeflate) { - perMessageDeflate = new PerMessageDeflate( - opts.perMessageDeflate !== true ? opts.perMessageDeflate : {}, - false, - opts.maxPayload - ); - opts.headers['Sec-WebSocket-Extensions'] = format({ - [PerMessageDeflate.extensionName]: perMessageDeflate.offer() - }); - } - if (protocols.length) { - for (const protocol of protocols) { - if ( - typeof protocol !== 'string' || - !subprotocolRegex.test(protocol) || - protocolSet.has(protocol) - ) { - throw new SyntaxError( - 'An invalid or duplicated subprotocol was specified' - ); - } - - protocolSet.add(protocol); - } - - opts.headers['Sec-WebSocket-Protocol'] = protocols.join(','); - } - if (opts.origin) { - if (opts.protocolVersion < 13) { - opts.headers['Sec-WebSocket-Origin'] = opts.origin; - } else { - opts.headers.Origin = opts.origin; - } - } - if (parsedUrl.username || parsedUrl.password) { - opts.auth = `${parsedUrl.username}:${parsedUrl.password}`; - } - - if (isIpcUrl) { - const parts = opts.path.split(':'); - - opts.socketPath = parts[0]; - opts.path = parts[1]; - } - - let req; - - if (opts.followRedirects) { - if (websocket._redirects === 0) { - websocket._originalIpc = isIpcUrl; - websocket._originalSecure = isSecure; - websocket._originalHostOrSocketPath = isIpcUrl - ? opts.socketPath - : parsedUrl.host; - - const headers = options && options.headers; - - // - // Shallow copy the user provided options so that headers can be changed - // without mutating the original object. - // - options = { ...options, headers: {} }; - - if (headers) { - for (const [key, value] of Object.entries(headers)) { - options.headers[key.toLowerCase()] = value; - } - } - } else if (websocket.listenerCount('redirect') === 0) { - const isSameHost = isIpcUrl - ? websocket._originalIpc - ? opts.socketPath === websocket._originalHostOrSocketPath - : false - : websocket._originalIpc - ? false - : parsedUrl.host === websocket._originalHostOrSocketPath; - - if (!isSameHost || (websocket._originalSecure && !isSecure)) { - // - // Match curl 7.77.0 behavior and drop the following headers. These - // headers are also dropped when following a redirect to a subdomain. - // - delete opts.headers.authorization; - delete opts.headers.cookie; - - if (!isSameHost) delete opts.headers.host; - - opts.auth = undefined; - } - } - - // - // Match curl 7.77.0 behavior and make the first `Authorization` header win. - // If the `Authorization` header is set, then there is nothing to do as it - // will take precedence. - // - if (opts.auth && !options.headers.authorization) { - options.headers.authorization = - 'Basic ' + Buffer.from(opts.auth).toString('base64'); - } - - req = websocket._req = request(opts); - - if (websocket._redirects) { - // - // Unlike what is done for the `'upgrade'` event, no early exit is - // triggered here if the user calls `websocket.close()` or - // `websocket.terminate()` from a listener of the `'redirect'` event. This - // is because the user can also call `request.destroy()` with an error - // before calling `websocket.close()` or `websocket.terminate()` and this - // would result in an error being emitted on the `request` object with no - // `'error'` event listeners attached. - // - websocket.emit('redirect', websocket.url, req); - } - } else { - req = websocket._req = request(opts); - } - - if (opts.timeout) { - req.on('timeout', () => { - abortHandshake(websocket, req, 'Opening handshake has timed out'); - }); - } - - req.on('error', (err) => { - if (req === null || req[kAborted]) return; - - req = websocket._req = null; - emitErrorAndClose(websocket, err); - }); - - req.on('response', (res) => { - const location = res.headers.location; - const statusCode = res.statusCode; - - if ( - location && - opts.followRedirects && - statusCode >= 300 && - statusCode < 400 - ) { - if (++websocket._redirects > opts.maxRedirects) { - abortHandshake(websocket, req, 'Maximum redirects exceeded'); - return; - } - - req.abort(); - - let addr; - - try { - addr = new URL(location, address); - } catch (e) { - const err = new SyntaxError(`Invalid URL: ${location}`); - emitErrorAndClose(websocket, err); - return; - } - - initAsClient(websocket, addr, protocols, options); - } else if (!websocket.emit('unexpected-response', req, res)) { - abortHandshake( - websocket, - req, - `Unexpected server response: ${res.statusCode}` - ); - } - }); - - req.on('upgrade', (res, socket, head) => { - websocket.emit('upgrade', res); - - // - // The user may have closed the connection from a listener of the - // `'upgrade'` event. - // - if (websocket.readyState !== WebSocket.CONNECTING) return; - - req = websocket._req = null; - - const upgrade = res.headers.upgrade; - - if (upgrade === undefined || upgrade.toLowerCase() !== 'websocket') { - abortHandshake(websocket, socket, 'Invalid Upgrade header'); - return; - } - - const digest = createHash('sha1') - .update(key + GUID) - .digest('base64'); - - if (res.headers['sec-websocket-accept'] !== digest) { - abortHandshake(websocket, socket, 'Invalid Sec-WebSocket-Accept header'); - return; - } - - const serverProt = res.headers['sec-websocket-protocol']; - let protError; - - if (serverProt !== undefined) { - if (!protocolSet.size) { - protError = 'Server sent a subprotocol but none was requested'; - } else if (!protocolSet.has(serverProt)) { - protError = 'Server sent an invalid subprotocol'; - } - } else if (protocolSet.size) { - protError = 'Server sent no subprotocol'; - } - - if (protError) { - abortHandshake(websocket, socket, protError); - return; - } - - if (serverProt) websocket._protocol = serverProt; - - const secWebSocketExtensions = res.headers['sec-websocket-extensions']; - - if (secWebSocketExtensions !== undefined) { - if (!perMessageDeflate) { - const message = - 'Server sent a Sec-WebSocket-Extensions header but no extension ' + - 'was requested'; - abortHandshake(websocket, socket, message); - return; - } - - let extensions; - - try { - extensions = parse(secWebSocketExtensions); - } catch (err) { - const message = 'Invalid Sec-WebSocket-Extensions header'; - abortHandshake(websocket, socket, message); - return; - } - - const extensionNames = Object.keys(extensions); - - if ( - extensionNames.length !== 1 || - extensionNames[0] !== PerMessageDeflate.extensionName - ) { - const message = 'Server indicated an extension that was not requested'; - abortHandshake(websocket, socket, message); - return; - } - - try { - perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]); - } catch (err) { - const message = 'Invalid Sec-WebSocket-Extensions header'; - abortHandshake(websocket, socket, message); - return; - } - - websocket._extensions[PerMessageDeflate.extensionName] = - perMessageDeflate; - } - - websocket.setSocket(socket, head, { - allowSynchronousEvents: opts.allowSynchronousEvents, - generateMask: opts.generateMask, - maxPayload: opts.maxPayload, - skipUTF8Validation: opts.skipUTF8Validation - }); - }); - - if (opts.finishRequest) { - opts.finishRequest(req, websocket); - } else { - req.end(); - } -} - -/** - * Emit the `'error'` and `'close'` events. - * - * @param {WebSocket} websocket The WebSocket instance - * @param {Error} The error to emit - * @private - */ -function emitErrorAndClose(websocket, err) { - websocket._readyState = WebSocket.CLOSING; - // - // The following assignment is practically useless and is done only for - // consistency. - // - websocket._errorEmitted = true; - websocket.emit('error', err); - websocket.emitClose(); -} - -/** - * Create a `net.Socket` and initiate a connection. - * - * @param {Object} options Connection options - * @return {net.Socket} The newly created socket used to start the connection - * @private - */ -function netConnect(options) { - options.path = options.socketPath; - return net.connect(options); -} - -/** - * Create a `tls.TLSSocket` and initiate a connection. - * - * @param {Object} options Connection options - * @return {tls.TLSSocket} The newly created socket used to start the connection - * @private - */ -function tlsConnect(options) { - options.path = undefined; - - if (!options.servername && options.servername !== '') { - options.servername = net.isIP(options.host) ? '' : options.host; - } - - return tls.connect(options); -} - -/** - * Abort the handshake and emit an error. - * - * @param {WebSocket} websocket The WebSocket instance - * @param {(http.ClientRequest|net.Socket|tls.Socket)} stream The request to - * abort or the socket to destroy - * @param {String} message The error message - * @private - */ -function abortHandshake(websocket, stream, message) { - websocket._readyState = WebSocket.CLOSING; - - const err = new Error(message); - Error.captureStackTrace(err, abortHandshake); - - if (stream.setHeader) { - stream[kAborted] = true; - stream.abort(); - - if (stream.socket && !stream.socket.destroyed) { - // - // On Node.js >= 14.3.0 `request.abort()` does not destroy the socket if - // called after the request completed. See - // https://github.com/websockets/ws/issues/1869. - // - stream.socket.destroy(); - } - - process.nextTick(emitErrorAndClose, websocket, err); - } else { - stream.destroy(err); - stream.once('error', websocket.emit.bind(websocket, 'error')); - stream.once('close', websocket.emitClose.bind(websocket)); - } -} - -/** - * Handle cases where the `ping()`, `pong()`, or `send()` methods are called - * when the `readyState` attribute is `CLOSING` or `CLOSED`. - * - * @param {WebSocket} websocket The WebSocket instance - * @param {*} [data] The data to send - * @param {Function} [cb] Callback - * @private - */ -function sendAfterClose(websocket, data, cb) { - if (data) { - const length = isBlob(data) ? data.size : toBuffer(data).length; - - // - // The `_bufferedAmount` property is used only when the peer is a client and - // the opening handshake fails. Under these circumstances, in fact, the - // `setSocket()` method is not called, so the `_socket` and `_sender` - // properties are set to `null`. - // - if (websocket._socket) websocket._sender._bufferedBytes += length; - else websocket._bufferedAmount += length; - } - - if (cb) { - const err = new Error( - `WebSocket is not open: readyState ${websocket.readyState} ` + - `(${readyStates[websocket.readyState]})` - ); - process.nextTick(cb, err); - } -} - -/** - * The listener of the `Receiver` `'conclude'` event. - * - * @param {Number} code The status code - * @param {Buffer} reason The reason for closing - * @private - */ -function receiverOnConclude(code, reason) { - const websocket = this[kWebSocket]; - - websocket._closeFrameReceived = true; - websocket._closeMessage = reason; - websocket._closeCode = code; - - if (websocket._socket[kWebSocket] === undefined) return; - - websocket._socket.removeListener('data', socketOnData); - process.nextTick(resume, websocket._socket); - - if (code === 1005) websocket.close(); - else websocket.close(code, reason); -} - -/** - * The listener of the `Receiver` `'drain'` event. - * - * @private - */ -function receiverOnDrain() { - const websocket = this[kWebSocket]; - - if (!websocket.isPaused) websocket._socket.resume(); -} - -/** - * The listener of the `Receiver` `'error'` event. - * - * @param {(RangeError|Error)} err The emitted error - * @private - */ -function receiverOnError(err) { - const websocket = this[kWebSocket]; - - if (websocket._socket[kWebSocket] !== undefined) { - websocket._socket.removeListener('data', socketOnData); - - // - // On Node.js < 14.0.0 the `'error'` event is emitted synchronously. See - // https://github.com/websockets/ws/issues/1940. - // - process.nextTick(resume, websocket._socket); - - websocket.close(err[kStatusCode]); - } - - if (!websocket._errorEmitted) { - websocket._errorEmitted = true; - websocket.emit('error', err); - } -} - -/** - * The listener of the `Receiver` `'finish'` event. - * - * @private - */ -function receiverOnFinish() { - this[kWebSocket].emitClose(); -} - -/** - * The listener of the `Receiver` `'message'` event. - * - * @param {Buffer|ArrayBuffer|Buffer[])} data The message - * @param {Boolean} isBinary Specifies whether the message is binary or not - * @private - */ -function receiverOnMessage(data, isBinary) { - this[kWebSocket].emit('message', data, isBinary); -} - -/** - * The listener of the `Receiver` `'ping'` event. - * - * @param {Buffer} data The data included in the ping frame - * @private - */ -function receiverOnPing(data) { - const websocket = this[kWebSocket]; - - if (websocket._autoPong) websocket.pong(data, !this._isServer, NOOP); - websocket.emit('ping', data); -} - -/** - * The listener of the `Receiver` `'pong'` event. - * - * @param {Buffer} data The data included in the pong frame - * @private - */ -function receiverOnPong(data) { - this[kWebSocket].emit('pong', data); -} - -/** - * Resume a readable stream - * - * @param {Readable} stream The readable stream - * @private - */ -function resume(stream) { - stream.resume(); -} - -/** - * The `Sender` error event handler. - * - * @param {Error} The error - * @private - */ -function senderOnError(err) { - const websocket = this[kWebSocket]; - - if (websocket.readyState === WebSocket.CLOSED) return; - if (websocket.readyState === WebSocket.OPEN) { - websocket._readyState = WebSocket.CLOSING; - setCloseTimer(websocket); - } - - // - // `socket.end()` is used instead of `socket.destroy()` to allow the other - // peer to finish sending queued data. There is no need to set a timer here - // because `CLOSING` means that it is already set or not needed. - // - this._socket.end(); - - if (!websocket._errorEmitted) { - websocket._errorEmitted = true; - websocket.emit('error', err); - } -} - -/** - * Set a timer to destroy the underlying raw socket of a WebSocket. - * - * @param {WebSocket} websocket The WebSocket instance - * @private - */ -function setCloseTimer(websocket) { - websocket._closeTimer = setTimeout( - websocket._socket.destroy.bind(websocket._socket), - closeTimeout - ); -} - -/** - * The listener of the socket `'close'` event. - * - * @private - */ -function socketOnClose() { - const websocket = this[kWebSocket]; - - this.removeListener('close', socketOnClose); - this.removeListener('data', socketOnData); - this.removeListener('end', socketOnEnd); - - websocket._readyState = WebSocket.CLOSING; - - let chunk; - - // - // The close frame might not have been received or the `'end'` event emitted, - // for example, if the socket was destroyed due to an error. Ensure that the - // `receiver` stream is closed after writing any remaining buffered data to - // it. If the readable side of the socket is in flowing mode then there is no - // buffered data as everything has been already written and `readable.read()` - // will return `null`. If instead, the socket is paused, any possible buffered - // data will be read as a single chunk. - // - if ( - !this._readableState.endEmitted && - !websocket._closeFrameReceived && - !websocket._receiver._writableState.errorEmitted && - (chunk = websocket._socket.read()) !== null - ) { - websocket._receiver.write(chunk); - } - - websocket._receiver.end(); - - this[kWebSocket] = undefined; - - clearTimeout(websocket._closeTimer); - - if ( - websocket._receiver._writableState.finished || - websocket._receiver._writableState.errorEmitted - ) { - websocket.emitClose(); - } else { - websocket._receiver.on('error', receiverOnFinish); - websocket._receiver.on('finish', receiverOnFinish); - } -} - -/** - * The listener of the socket `'data'` event. - * - * @param {Buffer} chunk A chunk of data - * @private - */ -function socketOnData(chunk) { - if (!this[kWebSocket]._receiver.write(chunk)) { - this.pause(); - } -} - -/** - * The listener of the socket `'end'` event. - * - * @private - */ -function socketOnEnd() { - const websocket = this[kWebSocket]; - - websocket._readyState = WebSocket.CLOSING; - websocket._receiver.end(); - this.end(); -} - -/** - * The listener of the socket `'error'` event. - * - * @private - */ -function socketOnError() { - const websocket = this[kWebSocket]; - - this.removeListener('error', socketOnError); - this.on('error', NOOP); - - if (websocket) { - websocket._readyState = WebSocket.CLOSING; - this.destroy(); - } -} diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/package.json b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/package.json deleted file mode 100644 index 9f14239e5..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/package.json +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aaedef2a72b60db8fb36d9b46c48d44986051785a2b6450c62994603c85dd959 -size 1723 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/wrapper.mjs b/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/wrapper.mjs deleted file mode 100644 index 7245ad15d..000000000 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/node_modules/ws/wrapper.mjs +++ /dev/null @@ -1,8 +0,0 @@ -import createWebSocketStream from './lib/stream.js'; -import Receiver from './lib/receiver.js'; -import Sender from './lib/sender.js'; -import WebSocket from './lib/websocket.js'; -import WebSocketServer from './lib/websocket-server.js'; - -export { createWebSocketStream, Receiver, Sender, WebSocket, WebSocketServer }; -export default WebSocket; diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/plugin/index.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/plugin/index.js index 9a586d8b8..288aef3af 100644 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/plugin/index.js +++ b/Streamdeck/com.mirabox.streamingle.sdPlugin/plugin/index.js @@ -9,18 +9,8 @@ let isUnityConnected = false; let cameraList = []; // 카메라 목록 저장 let itemList = []; // 아이템 목록 저장 let eventList = []; // 이벤트 목록 저장 +let avatarOutfitList = []; // 아바타 의상 목록 저장 -const fs = require('fs'); - -function getBase64Image(filePath) { - try { - const data = fs.readFileSync(filePath); - return 'data:image/png;base64,' + data.toString('base64'); - } catch (e) { - console.error('이미지 파일 읽기 실패:', filePath, e); - return ''; - } -} // StreamDock 연결 함수 (브라우저 기반) function connectElgatoStreamDeckSocket(inPort, inUUID, inEvent, inInfo, inActionInfo) { @@ -98,6 +88,9 @@ function connectElgatoStreamDeckSocket(inPort, inUUID, inEvent, inInfo, inAction } else if (jsonObj.action === 'com.mirabox.streamingle.event') { settings.actionType = 'event'; console.log('🎯 이벤트 컨트롤러 등록:', jsonObj.context); + } else if (jsonObj.action === 'com.mirabox.streamingle.avatar_outfit') { + settings.actionType = 'avatar_outfit'; + console.log('👗 아바타 의상 컨트롤러 등록:', jsonObj.context); } else { settings.actionType = 'camera'; console.log('📹 카메라 컨트롤러 등록:', jsonObj.context); @@ -117,9 +110,31 @@ function connectElgatoStreamDeckSocket(inPort, inUUID, inEvent, inInfo, inAction actionUUID = 'com.mirabox.streamingle.item'; } else if (actionType === 'event') { actionUUID = 'com.mirabox.streamingle.event'; + } else if (actionType === 'avatar_outfit') { + actionUUID = 'com.mirabox.streamingle.avatar_outfit'; } + console.log('📤 willAppear에서 Unity 연결 상태 전송:', actionType, actionUUID); sendToPropertyInspector(jsonObj.context, 'unity_connected', { connected: true }, actionUUID); + + // 아바타 의상 컨트롤러인 경우 데이터도 함께 전송 + if (actionType === 'avatar_outfit') { + console.log('📤 willAppear에서 아바타 데이터 전송:', avatarOutfitList.length, '개'); + if (avatarOutfitList.length > 0) { + sendToPropertyInspector(jsonObj.context, 'avatar_outfit_list', { + avatars: avatarOutfitList, + currentIndex: 0 + }, actionUUID); + } else { + // 아바타 목록이 없으면 Unity에 요청 + console.log('📤 willAppear에서 아바타 목록 요청'); + if (unitySocket && unitySocket.readyState === WebSocket.OPEN) { + const message = JSON.stringify({ type: 'get_avatar_outfit_list' }); + unitySocket.send(message); + console.log('📤 Unity에 아바타 목록 요청 전송 (willAppear)'); + } + } + } } break; @@ -129,6 +144,10 @@ function connectElgatoStreamDeckSocket(inPort, inUUID, inEvent, inInfo, inAction break; case 'sendToPlugin': + console.log('📨 sendToPlugin 이벤트 수신'); + console.log(' - context:', jsonObj.context); + console.log(' - action:', jsonObj.action); + console.log(' - payload:', jsonObj.payload); handlePropertyInspectorMessage(jsonObj.payload, jsonObj.context, jsonObj.action); break; @@ -204,6 +223,13 @@ function connectToUnity() { unitySocket.send(message); console.log('📋 이벤트 목록 요청:', message); }, 300); + + // 아바타 의상 목록 요청 + setTimeout(() => { + const message = JSON.stringify({ type: 'get_avatar_outfit_list' }); + unitySocket.send(message); + console.log('📋 아바타 의상 목록 요청:', message); + }, 400); }; unitySocket.onmessage = function(event) { @@ -219,6 +245,7 @@ function connectToUnity() { unitySocket.onclose = function() { isUnityConnected = false; + unitySocket = null; // 소켓을 null로 설정 console.log('❌ Unity 연결 끊어짐'); // Property Inspector들에게 Unity 연결 해제 알림 @@ -231,12 +258,16 @@ function connectToUnity() { actionUUID = 'com.mirabox.streamingle.item'; } else if (actionType === 'event') { actionUUID = 'com.mirabox.streamingle.event'; + } else if (actionType === 'avatar_outfit') { + actionUUID = 'com.mirabox.streamingle.avatar_outfit'; } - sendToPropertyInspector(context, 'unity_disconnected', { connected: false }, actionUUID); + sendToPropertyInspector(context, 'unity_connected', { connected: false }, actionUUID); } }; unitySocket.onerror = function(error) { + isUnityConnected = false; + unitySocket = null; // 소켓을 null로 설정 console.error('❌ Unity 연결 오류:', error); console.log('🔍 Unity가 실행 중인지 확인하세요 (포트 10701)'); }; @@ -252,7 +283,16 @@ function handleButtonClick(context) { console.log('🔌 Unity 연결 상태:', isUnityConnected); if (!isUnityConnected || !unitySocket) { - console.error('❌ Unity 연결되지 않음'); + console.log('🔄 Unity 연결되지 않음, 재연결 시도...'); + connectToUnity(); + // 연결 후 잠시 대기한 후 다시 시도 + setTimeout(() => { + if (isUnityConnected && unitySocket) { + handleButtonClick(context); + } else { + console.error('❌ Unity 재연결 실패'); + } + }, 1000); return; } @@ -272,6 +312,9 @@ function handleButtonClick(context) { case 'event': handleEventAction(settings); break; + case 'avatar_outfit': + handleAvatarOutfitAction(settings); + break; default: console.log('⚠️ 알 수 없는 액션 타입:', actionType); // 기본적으로 카메라 액션으로 처리 @@ -308,6 +351,8 @@ function handleCameraAction(settings) { console.log('✅ 메시지 전송 완료'); } else { console.error('❌ Unity 소켓이 연결되지 않음'); + console.log('🔄 Unity 재연결 시도...'); + connectToUnity(); } } @@ -346,6 +391,8 @@ function handleItemAction(settings) { console.log('✅ 메시지 전송 완료'); } else { console.error('❌ Unity 소켓이 연결되지 않음'); + console.log('🔄 Unity 재연결 시도...'); + connectToUnity(); } } @@ -384,64 +431,162 @@ function handleEventAction(settings) { console.log('✅ 메시지 전송 완료'); } else { console.error('❌ Unity 소켓이 연결되지 않음'); + console.log('🔄 Unity 재연결 시도...'); + connectToUnity(); + } +} + +// 아바타 의상 액션 처리 +function handleAvatarOutfitAction(settings) { + let avatarIndex = settings.avatarIndex; + let outfitIndex = settings.outfitIndex; + + // 설정값들 검증 + if (typeof avatarIndex !== 'number') { + avatarIndex = 0; + console.log('⚠️ 아바타 인덱스가 설정되지 않음, 기본값 0 사용'); + } + + if (typeof outfitIndex !== 'number') { + outfitIndex = 0; + console.log('⚠️ 의상 인덱스가 설정되지 않음, 기본값 0 사용'); + } + + console.log('👗 아바타 의상 전환:', { avatarIndex, outfitIndex }); + + // Unity에 아바타 의상 변경 요청 + const message = JSON.stringify({ + type: 'set_avatar_outfit', + data: { + avatar_index: avatarIndex, + outfit_index: outfitIndex + } + }); + + console.log('📤 Unity에 아바타 의상 변경 요청 전송:', message); + console.log('🔍 Unity 연결 상태:', isUnityConnected); + console.log('🔍 Unity 소켓 상태:', !!unitySocket); + + if (unitySocket && unitySocket.readyState === WebSocket.OPEN) { + unitySocket.send(message); + console.log('✅ 메시지 전송 완료'); + } else { + console.error('❌ Unity 소켓이 연결되지 않음'); + console.log('🔄 Unity 재연결 시도...'); + connectToUnity(); } } // Property Inspector 메시지 처리 function handlePropertyInspectorMessage(payload, context, actionUUID) { const command = payload.command; - console.log('📤 Property Inspector 명령 처리:', command, 'action:', actionUUID); + console.log('📤 Property Inspector 명령 처리 시작'); + console.log(' - payload:', payload); + console.log(' - context:', context); + console.log(' - actionUUID:', actionUUID); + console.log(' - command:', command); switch (command) { case 'get_unity_status': console.log('📡 Unity 상태 요청 - context:', context, 'action:', actionUUID); console.log('📡 Unity 연결 상태:', isUnityConnected); console.log('📡 카메라 목록 길이:', cameraList.length); console.log('📡 아이템 목록 길이:', itemList.length); + console.log('📡 아바타 목록 길이:', avatarOutfitList.length); sendToPropertyInspector(context, 'unity_connected', { connected: isUnityConnected }, actionUUID); + + // Unity 연결된 상태라면 무조건 아바타 목록 요청 + if (isUnityConnected && unitySocket && unitySocket.readyState === WebSocket.OPEN) { + console.log('📡 Unity 연결됨 - 모든 데이터 요청 시작'); + + // 아바타 목록이 비어있으면 무조건 요청 + if (avatarOutfitList.length === 0) { + console.log('📡 아바타 목록 비어있음 - Unity에 요청'); + const avatarMessage = JSON.stringify({ type: 'get_avatar_outfit_list' }); + unitySocket.send(avatarMessage); + console.log('📤 Unity에 아바타 의상 목록 강제 요청 전송'); + } + } + if (isUnityConnected) { - if (cameraList.length > 0) { - // 현재 활성 카메라 인덱스 찾기 - const currentCameraIndex = cameraList.findIndex(cam => cam.isActive) || 0; - console.log('📡 현재 활성 카메라 인덱스:', currentCameraIndex); - sendToPropertyInspector(context, 'camera_list', { - cameras: cameraList, - currentIndex: currentCameraIndex - }, actionUUID); - } - if (itemList.length > 0) { - // 현재 활성 아이템 인덱스 찾기 - const currentItemIndex = itemList.findIndex(item => item.isActive) || 0; - console.log('📡 현재 활성 아이템 인덱스:', currentItemIndex); - sendToPropertyInspector(context, 'item_list', { - items: itemList, - currentIndex: currentItemIndex - }, actionUUID); - } - if (eventList.length > 0) { - // 현재 활성 이벤트 인덱스 찾기 - const currentEventIndex = eventList.findIndex(event => event.isActive) || 0; - console.log('📡 현재 활성 이벤트 인덱스:', currentEventIndex); - sendToPropertyInspector(context, 'event_list', { - events: eventList, - currentIndex: currentEventIndex - }, actionUUID); + // actionUUID에 따라 해당하는 데이터만 전송 + console.log('📡 ActionUUID에 따른 데이터 전송:', actionUUID); + + if (actionUUID === 'com.mirabox.streamingle.camera') { + if (cameraList.length > 0) { + // 현재 활성 카메라 인덱스 찾기 + const currentCameraIndex = cameraList.findIndex(cam => cam.isActive) || 0; + console.log('📡 현재 활성 카메라 인덱스:', currentCameraIndex); + sendToPropertyInspector(context, 'camera_list', { + cameras: cameraList, + currentIndex: currentCameraIndex + }, actionUUID); + } + } else if (actionUUID === 'com.mirabox.streamingle.item') { + if (itemList.length > 0) { + // 현재 활성 아이템 인덱스 찾기 + const currentItemIndex = itemList.findIndex(item => item.isActive) || 0; + console.log('📡 현재 활성 아이템 인덱스:', currentItemIndex); + sendToPropertyInspector(context, 'item_list', { + items: itemList, + currentIndex: currentItemIndex + }, actionUUID); + } + } else if (actionUUID === 'com.mirabox.streamingle.event') { + if (eventList.length > 0) { + // 현재 활성 이벤트 인덱스 찾기 + const currentEventIndex = eventList.findIndex(event => event.isActive) || 0; + console.log('📡 현재 활성 이벤트 인덱스:', currentEventIndex); + sendToPropertyInspector(context, 'event_list', { + events: eventList, + currentIndex: currentEventIndex + }, actionUUID); + } + } else if (actionUUID === 'com.mirabox.streamingle.avatar_outfit') { + if (avatarOutfitList.length > 0) { + // 현재 활성 아바타 인덱스 찾기 + const currentAvatarIndex = avatarOutfitList.findIndex(avatar => avatar.current_outfit_index >= 0) || 0; + console.log('📡 현재 아바타 목록:', avatarOutfitList.length, '개'); + console.log('📡 현재 활성 아바타 인덱스:', currentAvatarIndex); + sendToPropertyInspector(context, 'avatar_outfit_list', { + avatars: avatarOutfitList, + currentIndex: currentAvatarIndex + }, actionUUID); + } else { + console.log('📡 아바타 목록이 비어있음, Unity에 요청'); + // 아바타 목록이 없으면 Unity에 요청 + if (unitySocket && unitySocket.readyState === WebSocket.OPEN) { + const message = JSON.stringify({ type: 'get_avatar_outfit_list' }); + unitySocket.send(message); + console.log('📤 Unity에 아바타 의상 목록 자동 요청 전송'); + } + } } } else { - // Unity가 연결되지 않은 경우에도 빈 목록 전송 - console.log('📡 Unity 연결 안됨 - 빈 목록 전송'); - sendToPropertyInspector(context, 'camera_list', { - cameras: [], - currentIndex: 0 - }, actionUUID); - sendToPropertyInspector(context, 'item_list', { - items: [], - currentIndex: 0 - }, actionUUID); - sendToPropertyInspector(context, 'event_list', { - events: [], - currentIndex: 0 - }, actionUUID); + // Unity가 연결되지 않은 경우에도 해당 컨트롤러에 맞는 빈 목록만 전송 + console.log('📡 Unity 연결 안됨 - ActionUUID에 따른 빈 목록 전송:', actionUUID); + + if (actionUUID === 'com.mirabox.streamingle.camera') { + sendToPropertyInspector(context, 'camera_list', { + cameras: [], + currentIndex: 0 + }, actionUUID); + } else if (actionUUID === 'com.mirabox.streamingle.item') { + sendToPropertyInspector(context, 'item_list', { + items: [], + currentIndex: 0 + }, actionUUID); + } else if (actionUUID === 'com.mirabox.streamingle.event') { + sendToPropertyInspector(context, 'event_list', { + events: [], + currentIndex: 0 + }, actionUUID); + } else if (actionUUID === 'com.mirabox.streamingle.avatar_outfit') { + sendToPropertyInspector(context, 'avatar_outfit_list', { + avatars: [], + currentIndex: 0 + }, actionUUID); + } } break; case 'get_camera_list': @@ -547,10 +692,58 @@ function handlePropertyInspectorMessage(payload, context, actionUUID) { console.log('📤 Unity에 이벤트 목록 요청 전송'); } break; + case 'refresh_avatar_outfit_list': + console.log('🔄 아바타 의상 목록 새로고침 요청'); + if (!isUnityConnected || !unitySocket) { + console.log('🔄 Unity 연결되지 않음, 재연결 시도...'); + connectToUnity(); + } else { + const message = JSON.stringify({ type: 'get_avatar_outfit_list' }); + if (unitySocket && unitySocket.readyState === WebSocket.OPEN) { + unitySocket.send(message); + console.log('📤 Unity에 아바타 의상 목록 요청 전송'); + } else { + console.error('❌ Unity 소켓이 연결되지 않음'); + console.log('🔄 Unity 재연결 시도...'); + connectToUnity(); + } + } + break; + case 'set_avatar_outfit': + console.log('👗 아바타 의상 설정 요청:', payload); + if (isUnityConnected && unitySocket) { + const message = JSON.stringify({ + type: 'set_avatar_outfit', + data: { + avatar_index: payload.avatarIndex, + outfit_index: payload.outfitIndex + } + }); + unitySocket.send(message); + console.log('📤 Unity에 아바타 의상 설정 요청 전송'); + } + break; case 'update_title': console.log('🏷️ 버튼 제목 업데이트 요청'); + console.log('🏷️ 요청 payload:', payload); + console.log('🏷️ 현재 Plugin의 avatarOutfitList:', avatarOutfitList); updateButtonTitle(context); break; + case 'debug_test': + console.log('🔍 디버그 테스트 메시지 수신!'); + console.log(' - context:', context); + console.log(' - actionUUID:', actionUUID); + console.log(' - payload:', payload); + console.log(' - 현재 등록된 컨텍스트들:', Array.from(buttonContexts.keys())); + console.log(' - 이 컨텍스트의 설정:', getCurrentSettings(context)); + + // 응답을 보내서 Property Inspector가 받을 수 있는지 확인 + sendToPropertyInspector(context, 'debug_response', { + message: 'Plugin received debug test', + receivedContext: context, + registeredContexts: Array.from(buttonContexts.keys()) + }, actionUUID); + break; default: console.log('❓ 알 수 없는 Property Inspector 명령:', command); @@ -559,7 +752,7 @@ function handlePropertyInspectorMessage(payload, context, actionUUID) { // Property Inspector로 메시지 전송 (action UUID 지정 가능) function sendToPropertyInspector(context, event, payload, actionUUID = null) { - console.log('📤 Property Inspector로 메시지 전송 시작 - context:', context, 'event:', event); + console.log('📤 Property Inspector로 메시지 전송 시작 - context:', context, 'event:', event, 'actionUUID:', actionUUID); if (!websocket || !context) { console.log('🚫 WebSocket 또는 context 없음 - Property Inspector 메시지 전송 건너뜀'); @@ -585,6 +778,8 @@ function sendToPropertyInspector(context, event, payload, actionUUID = null) { action = 'com.mirabox.streamingle.item'; } else if (actionType === 'event') { action = 'com.mirabox.streamingle.event'; + } else if (actionType === 'avatar_outfit') { + action = 'com.mirabox.streamingle.avatar_outfit'; } } @@ -652,6 +847,22 @@ function handleUnityMessage(data) { } } + // 아바타 의상 데이터 처리 + if (data.data.avatar_outfit_data) { + let avatars = data.data.avatar_outfit_data.avatars || data.data.avatar_outfit_data; + if (Array.isArray(avatars)) { + avatarOutfitList = avatars; + console.log('👗 아바타 의상 목록 저장됨:', avatarOutfitList.length, '개'); + } else { + avatarOutfitList = []; + console.log('⚠️ 아바타 의상 데이터가 배열이 아님:', avatars); + } + } else { + // 아바타 의상 데이터가 없어도 빈 배열로 초기화 + console.log('⚠️ 아바타 의상 데이터가 없음, 빈 배열로 초기화'); + avatarOutfitList = []; + } + updateAllButtonTitles(); // Property Inspector들에게 Unity 연결 상태 알림 @@ -673,6 +884,8 @@ function handleUnityMessage(data) { actionUUID = 'com.mirabox.streamingle.item'; } else if (actionType === 'event') { actionUUID = 'com.mirabox.streamingle.event'; + } else if (actionType === 'avatar_outfit') { + actionUUID = 'com.mirabox.streamingle.avatar_outfit'; } console.log('🔍 컨텍스트 분석:', context, 'Action Type:', actionType, 'Action UUID:', actionUUID); @@ -712,6 +925,19 @@ function handleUnityMessage(data) { events: eventList, currentIndex: currentEventIndex }, actionUUID); + } else if (actionUUID === 'com.mirabox.streamingle.avatar_outfit') { + // 아바타 의상 컨트롤러에는 아바타 의상 데이터만 전송 + let currentAvatarIndex = 0; + if (typeof data.data.avatar_outfit_data?.current_avatar_index === 'number' && data.data.avatar_outfit_data.current_avatar_index >= 0) { + currentAvatarIndex = data.data.avatar_outfit_data.current_avatar_index; + } + console.log('👗 아바타 의상 컨트롤러에 아바타 데이터 전송:', context, '아바타 수:', avatarOutfitList.length); + + // 아바타 목록이 비어있더라도 연결 상태와 함께 전송 + sendToPropertyInspector(context, 'avatar_outfit_list', { + avatars: avatarOutfitList || [], + currentIndex: currentAvatarIndex + }, actionUUID); } } console.log('✅ Property Inspector들에게 Unity 연결 알림 전송 완료'); @@ -942,6 +1168,76 @@ function handleUnityMessage(data) { } break; + case 'avatar_outfit_changed': + console.log('👗 아바타 의상 변경 알림'); + if (data.data && data.data.avatar_outfit_data) { + let avatars = data.data.avatar_outfit_data.avatars || data.data.avatar_outfit_data; + + if (Array.isArray(avatars)) { + avatarOutfitList = avatars; + console.log('👗 아바타 의상 목록 업데이트됨:', avatarOutfitList.length, '개'); + updateAllButtonTitles(); + + // Property Inspector들에게 아바타 의상 목록 전송 (아바타 의상 컨트롤러만) + for (const context of buttonContexts.keys()) { + const settings = getCurrentSettings(context); + const actionType = settings.actionType || 'camera'; + + if (actionType === 'avatar_outfit') { + sendToPropertyInspector(context, 'avatar_outfit_changed', { + avatars: avatarOutfitList, + currentIndex: data.data.avatar_outfit_data?.current_avatar_index || 0 + }, 'com.mirabox.streamingle.avatar_outfit'); + } + } + } else { + console.log('⚠️ 아바타 의상 변경 응답에서 아바타 데이터가 배열이 아님'); + console.log('📋 avatars:', avatars); + } + } + break; + + case 'avatar_outfit_list_response': + console.log('👗 아바타 의상 목록 응답'); + if (data.data && data.data.avatar_outfit_data) { + let avatars = data.data.avatar_outfit_data.avatars || data.data.avatar_outfit_data; + let currentIndex = data.data.avatar_outfit_data?.current_avatar_index || 0; + + if (Array.isArray(avatars)) { + avatarOutfitList = avatars; + console.log('👗 아바타 의상 목록 응답 처리됨:', avatarOutfitList.length, '개'); + + // 아바타 데이터 상세 로그 + avatarOutfitList.forEach((avatar, index) => { + console.log(`👗 아바타 ${index}:`, { + name: avatar.name, + nameType: typeof avatar.name, + current_outfit_name: avatar.current_outfit_name, + outfits: avatar.outfits ? avatar.outfits.length : 'no outfits' + }); + }); + + updateAllButtonTitles(); + + // Property Inspector들에게 아바타 의상 목록 전송 + for (const context of buttonContexts.keys()) { + const settings = getCurrentSettings(context); + const actionType = settings.actionType || 'camera'; + + if (actionType === 'avatar_outfit') { + sendToPropertyInspector(context, 'avatar_outfit_list', { + avatars: avatarOutfitList, + currentIndex: currentIndex + }, 'com.mirabox.streamingle.avatar_outfit'); + } + } + } else { + console.log('⚠️ 아바타 의상 목록 응답에서 아바타 데이터가 배열이 아님'); + console.log('📋 avatars:', avatars); + } + } + break; + default: console.log('❓ 알 수 없는 Unity 메시지 타입:', data.type); } @@ -1036,6 +1332,82 @@ function updateButtonTitle(context) { title = shortName || `이벤트 ${eventIndex + 1}`; } } + } else if (actionType === 'avatar_outfit') { + const avatarIndex = typeof settings.avatarIndex === 'number' ? settings.avatarIndex : 0; + const outfitIndex = typeof settings.outfitIndex === 'number' ? settings.outfitIndex : 0; + title = `아바타 ${avatarIndex + 1}`; + + console.log('👗 아바타 제목 업데이트 시작'); + console.log('👗 Context:', context); + console.log('👗 현재 설정:', settings); + console.log('👗 아바타 인덱스:', avatarIndex, '의상 인덱스:', outfitIndex); + console.log('👗 Plugin의 avatarOutfitList:', avatarOutfitList); + console.log('👗 avatarOutfitList 길이:', avatarOutfitList ? avatarOutfitList.length : 'null'); + + // 버튼별 설정 확인 + console.log('👗 [DEBUG] 모든 버튼 컨텍스트와 설정:'); + for (const [ctx, ctxSettings] of buttonContexts.entries()) { + if (ctxSettings.actionType === 'avatar_outfit') { + console.log(`👗 [DEBUG] Context: ${ctx} -> avatarIndex: ${ctxSettings.avatarIndex}, outfitIndex: ${ctxSettings.outfitIndex}`); + } + } + if (avatarOutfitList && avatarOutfitList.length > avatarIndex) { + const avatar = avatarOutfitList[avatarIndex]; + console.log('👗 선택된 아바타:', avatar); + + if (avatar && avatar.name) { + console.log('👗 원본 아바타 이름:', avatar.name); + console.log('👗 아바타 이름 타입:', typeof avatar.name); + + // 아바타 이름 안전하게 처리 + let avatarName = String(avatar.name); + console.log('👗 문자열 변환된 이름:', avatarName); + + // 아바타 이름 짧게 만들기 + let shortName = avatarName + .replace('Avatar', '') + .replace('Character', '') + .replace('_', ' ') + .substring(0, 8); // 최대 8글자 + + console.log('👗 처리된 아바타 이름:', shortName); + + // 버튼에 설정된 의상 정보 추가 + let outfitInfo = ''; + if (avatar.outfits && avatar.outfits.length > outfitIndex) { + const outfit = avatar.outfits[outfitIndex]; + console.log('👗 버튼에 설정된 의상:', outfit); + if (outfit && outfit.name) { + outfitInfo = '\n' + String(outfit.name).substring(0, 6); + console.log('👗 설정된 의상 이름:', outfit.name); + } + } else { + console.log('👗 의상 인덱스가 범위를 벗어남 또는 의상 목록이 없음'); + console.log('👗 outfitIndex:', outfitIndex, 'outfits 길이:', avatar.outfits ? avatar.outfits.length : 'null'); + } + + title = (shortName || `아바타${avatarIndex + 1}`) + outfitInfo; + console.log('👗 최종 제목:', title); + + // 아바타 의상 활성화 상태 확인 + if (avatar.outfits && avatar.outfits.length > outfitIndex) { + const outfit = avatar.outfits[outfitIndex]; + // 현재 활성화된 의상이 설정된 의상과 같으면 활성 상태 + isActive = (avatar.current_outfit_index === outfitIndex); + console.log('👗 아바타 의상 활성 상태:', isActive, + 'current:', avatar.current_outfit_index, 'target:', outfitIndex); + } else { + isActive = false; + console.log('👗 의상을 찾을 수 없어 비활성 상태로 설정'); + } + } else { + console.log('👗 아바타 이름이 없음:', avatar); + isActive = false; + } + } else { + console.log('👗 아바타 목록이 없거나 인덱스가 범위를 벗어남'); + console.log('👗 목록 길이:', avatarOutfitList ? avatarOutfitList.length : 'null'); + } } // StreamDock에 제목 업데이트 요청 @@ -1056,10 +1428,10 @@ function updateButtonTitle(context) { websocket.send(JSON.stringify(message)); console.log('🏷️ 버튼 제목 업데이트:', title, '(액션 타입:', actionType, ', 활성:', isActive, ')'); - // 아이템이나 카메라가 비활성화되어 있으면 아이콘을 어둡게 표시 (이벤트는 제외) - if ((actionType === 'item' || actionType === 'camera') && !isActive) { + // 아이템, 카메라, 아바타 의상이 비활성화되어 있으면 아이콘을 어둡게 표시 (이벤트는 제외) + if ((actionType === 'item' || actionType === 'camera' || actionType === 'avatar_outfit') && !isActive) { setButtonState(context, false); // 비활성 상태로 설정 - } else if (actionType === 'item' || actionType === 'camera') { + } else if (actionType === 'item' || actionType === 'camera' || actionType === 'avatar_outfit') { setButtonState(context, true); // 활성 상태로 설정 } // 이벤트 컨트롤러는 활성/비활성 상태 변경 없음 (항상 활성) @@ -1075,8 +1447,8 @@ function setButtonState(context, isActive) { const settings = getCurrentSettings(context); const actionType = settings.actionType || 'camera'; - // 아이템 컨트롤러와 카메라 컨트롤러만 상태 변경 적용 (이벤트 컨트롤러는 제외) - if (actionType === 'item' || actionType === 'camera') { + // 아이템, 카메라, 아바타 의상 컨트롤러만 상태 변경 적용 (이벤트 컨트롤러는 제외) + if (actionType === 'item' || actionType === 'camera' || actionType === 'avatar_outfit') { // 방법 1: setState 이벤트 사용 const stateMessage = { event: 'setState', @@ -1090,30 +1462,7 @@ function setButtonState(context, isActive) { websocket.send(JSON.stringify(stateMessage)); console.log('🎨 버튼 상태 업데이트 (setState):', context, '(활성:', isActive, ', 상태:', isActive ? 0 : 1, ')'); - // 방법 2: setImage 이벤트로 아이콘 직접 변경 (base64) - let imagePath; - if (actionType === 'item') { - imagePath = isActive ? 'images/item_icon.png' : 'images/item_icon_inactive.png'; - } else if (actionType === 'camera') { - imagePath = isActive ? 'images/camera_icon.png' : 'images/camera_icon_inactive.png'; - } - const imageBase64 = getBase64Image(imagePath); - const imageMessage = { - event: 'setImage', - context: context, - payload: { - image: imageBase64, - target: 0 // hardware and software - } - }; - - websocket.send(JSON.stringify(imageMessage)); - console.log('🖼️ 버튼 아이콘 업데이트 (setImage):', context, '(활성:', isActive, ', 이미지:', imagePath, ')'); - - // 추가 디버깅을 위한 로그 - setTimeout(() => { - console.log('🔍 상태 변경 후 확인 - Context:', context, 'Action Type:', actionType, '활성:', isActive); - }, 100); + console.log('🎨 버튼 상태 업데이트 완료:', context, '(활성:', isActive, ')'); } } diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/avatar_outfit/index.html b/Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/avatar_outfit/index.html new file mode 100644 index 000000000..e75458ece --- /dev/null +++ b/Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/avatar_outfit/index.html @@ -0,0 +1,254 @@ + + + + + Streamingle Avatar Outfit Inspector + + + + + + +
+
+
+
Unity 서버 연결 대기중...
+
연결을 확인하고 있습니다
+
+
+ + +
+
👤 아바타 선택
+
+ + +
현재: 선택되지 않음
+
+
+ + +
+
👗 의상 선택
+
+ + +
+
현재 의상: 정보 없음
+
+ + +
+
🔧 동작
+ +
+ + +
+ +
+
+ + + + + + \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/avatar_outfit/index.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/avatar_outfit/index.js new file mode 100644 index 000000000..d0a01f5b1 --- /dev/null +++ b/Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/avatar_outfit/index.js @@ -0,0 +1,576 @@ +console.log('👗 Avatar Outfit Property Inspector 로드됨'); + +// 화면에 로그를 표시하는 함수 +function logToScreen(msg, color = "#00ff00") { + let logDiv = document.getElementById('logArea'); + if (!logDiv) { + return; // 로그 영역이 아직 없으면 무시 + } + const line = document.createElement('div'); + line.style.color = color; + line.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`; + logDiv.appendChild(line); + logDiv.scrollTop = logDiv.scrollHeight; + + // 로그가 너무 많아지면 오래된 것 제거 (최대 100줄) + if (logDiv.children.length > 100) { + logDiv.removeChild(logDiv.firstChild); + } +} + +// 기존 console.log/console.error를 화면에도 출력 +const origLog = console.log; +console.log = function(...args) { + origLog.apply(console, args); + const msg = args.map(a => (typeof a === 'object' ? JSON.stringify(a, null, 2) : a)).join(' '); + logToScreen(msg, '#00ff00'); +}; + +const origError = console.error; +console.error = function(...args) { + origError.apply(console, args); + const msg = args.map(a => (typeof a === 'object' ? JSON.stringify(a, null, 2) : a)).join(' '); + logToScreen(msg, '#ff5555'); +}; + +const origWarn = console.warn; +console.warn = function(...args) { + origWarn.apply(console, args); + const msg = args.map(a => (typeof a === 'object' ? JSON.stringify(a, null, 2) : a)).join(' '); + logToScreen(msg, '#ffaa00'); +}; + +// 전역 변수 +let websocket = null; +let uuid = null; // Property Inspector UUID +let actionContext = null; // 버튼별 고유 컨텍스트 +let actionInfo = null; +let avatarList = []; +let isUnityConnected = false; +let settings = {}; + +// Stream Deck 연결 +function connectElgatoStreamDeckSocket(inPort, inPropertyInspectorUUID, inRegisterEvent, inInfo, inActionInfo) { + console.log('🔌 Avatar Outfit Property Inspector 연결 중...'); + console.log('📡 포트:', inPort, 'UUID:', inPropertyInspectorUUID); + + uuid = inPropertyInspectorUUID; + + // Parse action info - 버튼별 고유 컨텍스트와 설정 추출 + try { + if (inActionInfo) { + console.log('🔍 Raw actionInfo:', inActionInfo); + actionInfo = JSON.parse(inActionInfo); + actionContext = actionInfo.context; // 버튼별 고유 컨텍스트 + settings = actionInfo.payload?.settings || {}; + + // actionType이 없으면 강제로 설정 + if (!settings.actionType) { + settings.actionType = 'avatar_outfit'; + console.log('🔧 actionType이 없어서 강제로 설정:', settings.actionType); + } + + console.log('⚙️ Property Inspector UUID:', uuid); + console.log('⚙️ 버튼 컨텍스트:', actionContext); + console.log('⚙️ 초기 설정:', settings); + console.log('⚙️ actionInfo 전체:', actionInfo); + } else { + console.error('❌ actionInfo가 없습니다!'); + } + } catch (error) { + console.error('❌ ActionInfo 파싱 오류:', error); + console.log('🔍 파싱 시도한 문자열:', inActionInfo); + } + + websocket = new WebSocket('ws://localhost:' + inPort); + + websocket.onopen = function() { + console.log('✅ Property Inspector 연결됨'); + + // StreamDeck에 등록 (카메라와 동일한 패턴) + websocket.send(JSON.stringify({ + event: inRegisterEvent, + uuid: inPropertyInspectorUUID + })); + + // Unity 상태 요청 (카메라와 동일한 패턴) + setTimeout(() => { + console.log('🔍 Plugin에 디버그 메시지 전송 시작'); + + // actionType이 설정되었으면 즉시 Plugin에 저장 + if (settings.actionType === 'avatar_outfit') { + const actionTypeMessage = { + action: 'com.mirabox.streamingle.avatar_outfit', + event: 'setSettings', + context: uuid, + payload: settings + }; + websocket.send(JSON.stringify(actionTypeMessage)); + console.log('🔧 초기 actionType 설정을 Plugin에 저장:', settings); + } + + // 먼저 Plugin이 이 버튼을 인식하는지 확인 + sendToPlugin('debug_test', { + message: 'Avatar outfit controller test', + context: actionContext, + action: 'com.mirabox.streamingle.avatar_outfit' + }); + + // Unity 상태 요청 + sendToPlugin('get_unity_status'); + }, 500); + + // 초기 설정 로드 + loadInitialSettings(); + }; + + websocket.onmessage = function(evt) { + try { + const jsonObj = JSON.parse(evt.data); + handleMessage(jsonObj); + } catch (error) { + console.error('❌ 메시지 파싱 오류:', error); + } + }; + + websocket.onclose = function() { + console.log('❌ Property Inspector 연결 끊어짐'); + websocket = null; + // 연결 상태를 비연결로 표시 + handleUnityConnected(false); + }; + + websocket.onerror = function(error) { + console.error('❌ Property Inspector 연결 오류:', error); + websocket = null; + // 연결 상태를 비연결로 표시 + handleUnityConnected(false); + }; +} + +// 메시지 처리 +function handleMessage(jsonObj) { + console.log('📨 메시지 수신:', jsonObj.event); + console.log('📨 전체 메시지:', JSON.stringify(jsonObj, null, 2)); + + // sendToPropertyInspector 이벤트 처리 (카메라 방식과 동일) + if (jsonObj.event === 'sendToPropertyInspector' && jsonObj.payload && jsonObj.payload.event) { + const innerEvent = jsonObj.payload.event; + console.log('📨 내부 이벤트:', innerEvent, 'payload:', jsonObj.payload); + + switch (innerEvent) { + case 'unity_connected': + console.log('✅ Unity 연결 상태 업데이트'); + handleUnityConnected(jsonObj.payload.connected); + break; + case 'avatar_outfit_list': + console.log('👗 아바타 의상 목록 수신'); + handleAvatarOutfitList(jsonObj.payload); + break; + case 'avatar_outfit_changed': + console.log('🎭 아바타 의상 변경 알림'); + handleAvatarOutfitChanged(jsonObj.payload); + break; + case 'camera_list': + console.log('⚠️ 카메라 데이터를 받았지만 아바타 컨트롤러에서는 무시함'); + break; + case 'item_list': + console.log('⚠️ 아이템 데이터를 받았지만 아바타 컨트롤러에서는 무시함'); + break; + case 'event_list': + console.log('⚠️ 이벤트 데이터를 받았지만 아바타 컨트롤러에서는 무시함'); + break; + case 'debug_response': + console.log('✅ Plugin으로부터 디버그 응답 받음!'); + console.log('📨 응답 데이터:', jsonObj.payload); + break; + default: + console.log('❓ 알 수 없는 내부 이벤트:', innerEvent); + break; + } + return; + } + + switch(jsonObj.event) { + case 'sendToPropertyInspector': + if (jsonObj.payload) { + handlePluginMessage(jsonObj.payload); + } + break; + + case 'didReceiveSettings': + if (jsonObj.payload && jsonObj.payload.settings) { + updateUIFromSettings(jsonObj.payload.settings); + } + break; + + // 기존 방식도 유지 (호환성) + case 'unity_connected': + console.log('✅ Unity 연결 상태 업데이트 (기존 방식)'); + handleUnityConnected(jsonObj.payload?.connected); + break; + case 'avatar_outfit_list': + console.log('👗 아바타 의상 목록 수신 (기존 방식)'); + handleAvatarOutfitList(jsonObj.payload); + break; + case 'avatar_outfit_changed': + console.log('🎭 아바타 의상 변경 알림 (기존 방식)'); + handleAvatarOutfitChanged(jsonObj.payload); + break; + } +} + +// 플러그인 메시지 처리 +function handlePluginMessage(payload) { + console.log('📨 플러그인 메시지:', payload.event, payload); + + switch(payload.event) { + case 'unity_connected': + console.log('🔗 Unity 연결 이벤트 수신:', payload); + handleUnityConnected(payload.connected); + break; + + case 'avatar_outfit_list': + console.log('👗 아바타 의상 목록 이벤트 수신:', payload); + handleAvatarOutfitList(payload); + break; + + case 'avatar_outfit_changed': + console.log('🎭 아바타 의상 변경 이벤트 수신:', payload); + handleAvatarOutfitChanged(payload); + break; + + default: + console.log('❓ 알 수 없는 이벤트:', payload.event); + break; + } +} + +// Unity 연결 상태 처리 +function handleUnityConnected(connected) { + console.log('🔗 Unity 연결 상태:', connected); + isUnityConnected = connected; + + const statusIndicator = document.getElementById('connection-status'); + const statusDot = document.getElementById('status-dot'); + const statusText = document.getElementById('status-text'); + const connectionDetail = document.getElementById('connection-detail'); + + const avatarSelect = document.getElementById('avatar-select'); + const refreshButton = document.getElementById('refresh-button'); + + if (connected) { + statusIndicator.classList.add('connected'); + statusDot.classList.add('connected'); + statusText.textContent = 'Unity 서버 연결됨'; + connectionDetail.textContent = '아바타 데이터를 불러오는 중...'; + + avatarSelect.disabled = false; + refreshButton.disabled = false; + + avatarSelect.innerHTML = ''; + + // 연결 즉시 아바타 목록 요청 + console.log('🔄 Unity 연결됨 - 아바타 목록 요청'); + setTimeout(() => { + refreshAvatarList(); + }, 500); + + } else { + statusIndicator.classList.remove('connected'); + statusDot.classList.remove('connected'); + statusText.textContent = 'Unity 서버 연결 안됨'; + connectionDetail.textContent = 'Unity를 실행하고 AvatarOutfitController가 있는지 확인하세요'; + + avatarSelect.disabled = true; + refreshButton.disabled = true; + + avatarSelect.innerHTML = ''; + updateOutfitSelect(-1); + } +} + +// 아바타 의상 목록 처리 +function handleAvatarOutfitList(payload) { + console.log('👗 아바타 의상 목록 수신:', payload); + console.log('👗 payload.avatars:', payload.avatars); + console.log('👗 Array.isArray(payload.avatars):', Array.isArray(payload.avatars)); + + if (payload.avatars && Array.isArray(payload.avatars)) { + avatarList = payload.avatars; + console.log('👗 아바타 목록 저장됨:', avatarList.length, '개'); + console.log('👗 아바타 목록 상세:', avatarList); + updateAvatarSelect(); + + // 현재 설정된 아바타 확인 + const avatarSelect = document.getElementById('avatar-select'); + if (avatarSelect.value !== '') { + const selectedIndex = parseInt(avatarSelect.value); + if (!isNaN(selectedIndex)) { + updateOutfitSelect(selectedIndex); + } + } + } else { + console.log('❌ 아바타 데이터가 올바르지 않음:', payload.avatars); + avatarList = []; + updateAvatarSelect(); + } + + updateCurrentStatus(payload.currentIndex); +} + +// 아바타 의상 변경 처리 +function handleAvatarOutfitChanged(payload) { + console.log('🎭 아바타 의상 변경:', payload); + handleAvatarOutfitList(payload); // 같은 방식으로 처리 +} + +// 아바타 선택 업데이트 +function updateAvatarSelect() { + const avatarSelect = document.getElementById('avatar-select'); + const connectionDetail = document.getElementById('connection-detail'); + + avatarSelect.innerHTML = ''; + + if (avatarList.length === 0) { + avatarSelect.innerHTML = ''; + connectionDetail.textContent = 'Unity에서 AvatarOutfitController에 아바타를 추가하세요'; + return; + } + + avatarSelect.innerHTML = ''; + + avatarList.forEach((avatar, index) => { + const option = document.createElement('option'); + option.value = index; + option.textContent = `${avatar.name} (${avatar.outfits ? avatar.outfits.length : 0}개 의상)`; + avatarSelect.appendChild(option); + }); + + connectionDetail.textContent = `${avatarList.length}개의 아바타를 발견했습니다`; +} + +// 의상 선택 업데이트 +function updateOutfitSelect(avatarIndex) { + const outfitSelect = document.getElementById('outfit-select'); + + outfitSelect.innerHTML = ''; + + if (avatarIndex < 0 || avatarIndex >= avatarList.length) { + outfitSelect.innerHTML = ''; + outfitSelect.disabled = true; + return; + } + + const avatar = avatarList[avatarIndex]; + if (!avatar.outfits || avatar.outfits.length === 0) { + outfitSelect.innerHTML = ''; + outfitSelect.disabled = true; + return; + } + + // 기본 선택 옵션 추가 + outfitSelect.innerHTML = ''; + + // 의상 목록 추가 + avatar.outfits.forEach((outfit, index) => { + const option = document.createElement('option'); + option.value = index; + option.textContent = outfit.name; + outfitSelect.appendChild(option); + }); + + outfitSelect.disabled = false; + + // 기본값 설정 (첫 번째 의상) + if (avatar.outfits.length > 0) { + outfitSelect.value = 0; + } +} + +// 현재 상태 업데이트 +function updateCurrentStatus(currentAvatarIndex) { + const currentAvatar = document.getElementById('current-avatar'); + const currentOutfit = document.getElementById('current-outfit'); + + if (currentAvatarIndex >= 0 && currentAvatarIndex < avatarList.length) { + const avatar = avatarList[currentAvatarIndex]; + currentAvatar.textContent = `현재: ${avatar.name}`; + currentOutfit.textContent = `현재 의상: ${avatar.current_outfit_name || '정보 없음'}`; + } else { + currentAvatar.textContent = '현재: 선택되지 않음'; + currentOutfit.textContent = '현재 의상: 정보 없음'; + } +} + +// Send message to plugin +function sendToPlugin(command, data = {}) { + console.log('🚀 sendToPlugin 호출:', command, data); + console.log('🔍 WebSocket 상태:', websocket ? 'available' : 'null'); + console.log('🔍 actionContext 상태:', actionContext); + + if (!websocket) { + console.error('❌ WebSocket not available'); + return; + } + if (!uuid) { + console.error('❌ UUID not available'); + return; + } + try { + const message = { + action: 'com.mirabox.streamingle.avatar_outfit', // manifest.json의 Action UUID + event: 'sendToPlugin', + context: uuid, // Property Inspector UUID (Stream Deck 표준) + payload: { + command, + buttonContext: actionContext, // 버튼의 실제 context 정보도 함께 전송 + ...data + } + }; + websocket.send(JSON.stringify(message)); + console.log('📤 Message sent to plugin:', command, data, 'context:', uuid); + } catch (error) { + console.error('❌ Failed to send message to plugin:', error); + } +} + +// Unity 상태 요청 +function requestUnityStatus() { + sendToPlugin('get_unity_status'); +} + +// 아바타 목록 새로고침 +function refreshAvatarList() { + sendToPlugin('refresh_avatar_outfit_list'); +} + +// 설정 저장 +function saveSettings() { + if (!uuid) { + console.error('❌ No uuid available for saving settings'); + return; + } + + if (!websocket) { + console.log('⚠️ WebSocket이 연결되지 않음, 설정 저장 건너뜀'); + return; + } + + const avatarSelect = document.getElementById('avatar-select'); + const outfitSelect = document.getElementById('outfit-select'); + + const newSettings = { + avatarIndex: parseInt(avatarSelect.value || '0'), + outfitIndex: parseInt(outfitSelect.value || '0'), + actionType: 'avatar_outfit' // 🔥 중요: actionType 추가! + }; + + // 전역 설정도 업데이트 + settings = { ...settings, ...newSettings }; + + const message = { + action: 'com.mirabox.streamingle.avatar_outfit', + event: 'setSettings', + context: uuid, // Property Inspector UUID 사용 (Stream Deck 표준) + payload: newSettings + }; + + try { + websocket.send(JSON.stringify(message)); + console.log('📤 설정 저장됨 - Context:', uuid, 'Settings:', newSettings); + + // 설정이 저장되면 Plugin에게 아바타 의상 변경 요청도 보냄 + sendToPlugin('set_avatar_outfit', { + avatarIndex: newSettings.avatarIndex, + outfitIndex: newSettings.outfitIndex + }); + + // 버튼 제목 업데이트 요청 + sendToPlugin('update_title', { + avatarIndex: newSettings.avatarIndex, + outfitIndex: newSettings.outfitIndex + }); + } catch (error) { + console.error('❌ 설정 저장 실패:', error); + websocket = null; + handleUnityConnected(false); + } +} + +// 초기 설정 로드 +function loadInitialSettings() { + console.log('🔧 초기 설정 로드 시작'); + console.log('🔧 현재 settings:', settings); + + // 초기화가 완료된 후 UI 업데이트 + setTimeout(() => { + updateUIFromSettings(settings); + }, 100); +} + +// 설정에서 UI 업데이트 +function updateUIFromSettings(settingsToApply) { + console.log('🎨 UI 업데이트 시작:', settingsToApply); + + const avatarSelect = document.getElementById('avatar-select'); + const outfitSelect = document.getElementById('outfit-select'); + + if (settingsToApply.avatarIndex !== undefined && avatarSelect) { + console.log('👤 아바타 인덱스 적용:', settingsToApply.avatarIndex); + avatarSelect.value = settingsToApply.avatarIndex; + updateOutfitSelect(settingsToApply.avatarIndex); + } + + if (settingsToApply.outfitIndex !== undefined && outfitSelect) { + console.log('👗 의상 인덱스 적용:', settingsToApply.outfitIndex); + outfitSelect.value = settingsToApply.outfitIndex; + } + + console.log('✅ UI 업데이트 완료'); +} + + +// 이벤트 리스너 +document.addEventListener('DOMContentLoaded', function() { + console.log('👗 Avatar Outfit Property Inspector DOM 준비됨'); + + // 아바타 선택 변경 + const avatarSelect = document.getElementById('avatar-select'); + if (avatarSelect) { + avatarSelect.addEventListener('change', function() { + const selectedIndex = parseInt(this.value); + if (!isNaN(selectedIndex)) { + updateOutfitSelect(selectedIndex); + } else { + updateOutfitSelect(-1); + } + saveSettings(); + }); + } + + // 의상 선택 변경 + const outfitSelect = document.getElementById('outfit-select'); + if (outfitSelect) { + outfitSelect.addEventListener('change', saveSettings); + } + + // 새로고침 버튼 + const refreshButton = document.getElementById('refresh-button'); + if (refreshButton) { + refreshButton.addEventListener('click', function() { + console.log('🔄 새로고침 버튼 클릭됨'); + // Unity 상태를 다시 확인하고 아바타 목록도 새로고침 + requestUnityStatus(); + refreshAvatarList(); + + // 강제로 연결 상태를 true로 설정 (다른 컨트롤러가 연결되어 있다면 Unity는 작동 중) + setTimeout(() => { + console.log('🔧 새로고침 후 강제로 Unity 연결 상태를 true로 설정'); + handleUnityConnected(true); + }, 1000); + }); + } +}); + +console.log('✅ Avatar Outfit Property Inspector 스크립트 로드 완료'); \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/camera/index.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/camera/index.js index 10a010c48..337009290 100644 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/camera/index.js +++ b/Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/camera/index.js @@ -87,11 +87,15 @@ function sendToPlugin(command, data = {}) { console.error('❌ WebSocket not available'); return; } + if (!uuid) { + console.error('❌ UUID not available'); + return; + } try { const message = { action: 'com.mirabox.streamingle.camera', // manifest.json의 Action UUID event: 'sendToPlugin', - context: uuid, // connectElgatoStreamDeckSocket에서 받은 uuid + context: uuid, // 아이템과 동일한 패턴 payload: { command, ...data @@ -239,8 +243,16 @@ function handleMessage(jsonObj) { updateCurrentCameraDisplay(jsonObj.payload.cameraIndex); break; case 'item_list': - console.log('🎯 아이템 목록 수신 (카메라 Property Inspector에서는 무시)'); - console.log('⚠️ 카메라 컨트롤러에서 아이템 데이터를 받았습니다. 이는 잘못된 데이터 전송입니다.'); + console.log('⚠️ 아이템 데이터를 받았지만 카메라 컨트롤러에서는 무시함'); + break; + case 'event_list': + console.log('⚠️ 이벤트 데이터를 받았지만 카메라 컨트롤러에서는 무시함'); + break; + case 'avatar_outfit_list': + console.log('⚠️ 아바타 데이터를 받았지만 카메라 컨트롤러에서는 무시함'); + break; + case 'avatar_outfit_changed': + console.log('⚠️ 아바타 변경 데이터를 받았지만 카메라 컨트롤러에서는 무시함'); break; default: console.log('❓ 알 수 없는 내부 이벤트:', innerEvent); @@ -399,7 +411,7 @@ function initializePropertyInspector() { } // 현재 액션의 컨텍스트가 있으면 설정 요청 - if (typeof uuid === 'string') { + if (uuid) { requestSettings(); } diff --git a/Streamdeck/com.mirabox.streamingle.streamDeckPlugin b/Streamdeck/com.mirabox.streamingle.streamDeckPlugin new file mode 100644 index 000000000..672f21012 Binary files /dev/null and b/Streamdeck/com.mirabox.streamingle.streamDeckPlugin differ