diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e.meta new file mode 100644 index 000000000..2cf5c9545 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 56444d14b6c5a3e47a3952005eb45071 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor.meta new file mode 100644 index 000000000..dd9e2ceab --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: aaf22bd5628ec0b48846a14ad7d42515 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/HapPlayerEditor.cs b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/HapPlayerEditor.cs new file mode 100644 index 000000000..b9957ba0d --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/HapPlayerEditor.cs @@ -0,0 +1,156 @@ +using UnityEngine; +using UnityEditor; + +namespace Klak.Hap +{ + [CanEditMultipleObjects] + [CustomEditor(typeof(HapPlayer))] + sealed class HapPlayerEditor : Editor + { + SerializedProperty _filePath; + SerializedProperty _pathMode; + SerializedProperty _time; + SerializedProperty _speed; + SerializedProperty _loop; + SerializedProperty _targetTexture; + SerializedProperty _flipHorizontal; + SerializedProperty _flipVertical; + + static class Labels + { + public static readonly GUIContent Property = new GUIContent("Property"); + public static readonly GUIContent Select = new GUIContent("Select"); + } + + string _sourceInfo; + + void ShowSourceInfo(HapPlayer player) + { + if (!player.enabled || !player.gameObject.activeInHierarchy) return; + + if (!player.isValid) + { + EditorGUILayout.HelpBox( + "Failed to open file. " + + "Please specify a valid HAP-encoded .mov file.", + MessageType.Warning + ); + return; + } + + if (_sourceInfo == null) + _sourceInfo = string.Format( + "Codec: {0}\n" + + "Frame dimensions: {1} x {2}\n" + + "Stream duration: {3:0.00}\n" + + "Frame rate: {4:0.00}", + player.codecType, + player.frameWidth, player.frameHeight, + player.streamDuration, + player.frameCount / player.streamDuration + ); + + EditorGUILayout.HelpBox(_sourceInfo, MessageType.None); + } + + void OnEnable() + { + _filePath = serializedObject.FindProperty("_filePath"); + _pathMode = serializedObject.FindProperty("_pathMode"); + _time = serializedObject.FindProperty("_time"); + _speed = serializedObject.FindProperty("_speed"); + _loop = serializedObject.FindProperty("_loop"); + _targetTexture = serializedObject.FindProperty("_targetTexture"); + _flipHorizontal = serializedObject.FindProperty("_flipHorizontal"); + _flipVertical = serializedObject.FindProperty("_flipVertical"); + } + + public override void OnInspectorGUI() + { + var reload = false; + + serializedObject.Update(); + + // Source infomation + if (!_filePath.hasMultipleDifferentValues && + !_pathMode.hasMultipleDifferentValues && + !string.IsNullOrEmpty(_filePath.stringValue)) + { + ShowSourceInfo((HapPlayer)target); + } + + // Source file (드래그 앤 드롭 + 파일 브라우저) + EditorGUILayout.BeginHorizontal(); + EditorGUI.BeginChangeCheck(); + EditorGUILayout.DelayedTextField(_filePath); + if (GUILayout.Button("파일 선택", GUILayout.Width(80))) + { + string path = EditorUtility.OpenFilePanel("HAP MOV 파일 선택", Application.dataPath, "mov"); + if (!string.IsNullOrEmpty(path)) + { + string streamingPath = Application.streamingAssetsPath; + if (path.StartsWith(streamingPath)) + path = path.Substring(streamingPath.Length + 1); + _filePath.stringValue = path; + } + } + EditorGUILayout.EndHorizontal(); + if (EditorGUI.EndChangeCheck()) reload = true; + + // 드래그 앤 드롭 지원 + Rect dropRect = GUILayoutUtility.GetLastRect(); + if (Event.current.type == EventType.DragUpdated || Event.current.type == EventType.DragPerform) + { + if (dropRect.Contains(Event.current.mousePosition)) + { + DragAndDrop.visualMode = DragAndDropVisualMode.Copy; + if (Event.current.type == EventType.DragPerform) + { + DragAndDrop.AcceptDrag(); + foreach (var dragged in DragAndDrop.paths) + { + if (dragged.ToLower().EndsWith(".mov")) + { + string path = dragged; + string streamingPath = Application.streamingAssetsPath; + if (path.StartsWith(streamingPath)) + path = path.Substring(streamingPath.Length + 1); + _filePath.stringValue = path; + GUI.FocusControl(null); + reload = true; + break; + } + } + Event.current.Use(); + } + } + } + + // Playback control + EditorGUILayout.PropertyField(_time); + EditorGUILayout.PropertyField(_speed); + EditorGUILayout.PropertyField(_loop); + + // Flip options + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Flip Options", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(_flipHorizontal, new GUIContent("Flip Horizontal")); + EditorGUILayout.PropertyField(_flipVertical, new GUIContent("Flip Vertical")); + + // Target texture + EditorGUILayout.PropertyField(_targetTexture); + + serializedObject.ApplyModifiedProperties(); + + if (reload) + { + foreach (HapPlayer hp in targets) + { + hp.SendMessage("OnDestroy"); + hp.SendMessage("LateUpdate"); + } + _sourceInfo = null; + } + } + } +} diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/HapPlayerEditor.cs.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/HapPlayerEditor.cs.meta new file mode 100644 index 000000000..b01351e5b --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/HapPlayerEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0a374e606a0d24345b93bc348a6fbf54 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/Klak.Hap.Editor.asmdef b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/Klak.Hap.Editor.asmdef new file mode 100644 index 000000000..99912cc79 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/Klak.Hap.Editor.asmdef @@ -0,0 +1,16 @@ +{ + "name": "Klak.Hap.Editor", + "references": [ + "Klak.Hap" + ], + "optionalUnityReferences": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [] +} \ No newline at end of file diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/Klak.Hap.Editor.asmdef.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/Klak.Hap.Editor.asmdef.meta new file mode 100644 index 000000000..cca620028 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/Klak.Hap.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 26694ad9b328c7148a8fb73421baa7fe +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/MaterialPropertySelector.cs b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/MaterialPropertySelector.cs new file mode 100644 index 000000000..c26b97308 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/MaterialPropertySelector.cs @@ -0,0 +1,87 @@ +using UnityEngine; +using UnityEditor; +using System; +using System.Collections.Generic; + +namespace Klak.Hap +{ + static class MaterialPropertySelector + { + #region Public method + + // Material property drop-down list + public static void DropdownList( + SerializedProperty rendererProperty, + SerializedProperty materialProperty + ) + { + // Try retrieving the target shader. + var shader = RetrieveTargetShader(rendererProperty); + + // Abandon the current value if it failed to get a shader. + if (shader == null) + { + materialProperty.stringValue = ""; + return; + } + + // Cache property names found in the target shader. + CachePropertyNames(shader); + + // Abandon the current value if there is no property candidate. + if (_propertyNames.Length == 0) + { + materialProperty.stringValue = ""; + return; + } + + // Show the dropdown list. + var index = Array.IndexOf(_propertyNames, materialProperty.stringValue); + var newIndex = EditorGUILayout.Popup("Property", index, _propertyNames); + + // Update the serialized property if the selection was changed. + if (index != newIndex) materialProperty.stringValue = _propertyNames[newIndex]; + } + + #endregion + + #region Private members + + static string[] _propertyNames; // Property name list + static Shader _cachedShader; // Shader used to cache the name list + + // Retrieve a shader from a given renderer. + static Shader RetrieveTargetShader(SerializedProperty rendererProperty) + { + var renderer = rendererProperty.objectReferenceValue as Renderer; + if (renderer == null) return null; + + var material = renderer.sharedMaterial; + if (material == null) return null; + + return material.shader; + } + + // Cache property names provided within a specified shader. + static void CachePropertyNames(Shader shader) + { + // Exit early when the shader is same to the cached one. + if (shader == _cachedShader) return; + + var temp = new List(); + + var count = ShaderUtil.GetPropertyCount(shader); + for (var i = 0; i < count; i++) + { + var propType = ShaderUtil.GetPropertyType(shader, i); + if (propType == ShaderUtil.ShaderPropertyType.TexEnv) + temp.Add(ShaderUtil.GetPropertyName(shader, i)); + } + + _propertyNames = temp.ToArray(); + _cachedShader = shader; + } + + #endregion + } +} diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/MaterialPropertySelector.cs.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/MaterialPropertySelector.cs.meta new file mode 100644 index 000000000..d1fca01fb --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Editor/MaterialPropertySelector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fe3e2d486287f2f40936cd00c558818d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/LICENSE b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/LICENSE new file mode 100644 index 000000000..4c024e58f --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/LICENSE @@ -0,0 +1,215 @@ +License summary: + +KlakHAP - MIT license +HAP codec - FreeBSD license +Snappy - BSD 3-clause license +MP4 demuxer - CC0 (public domain) + +------------------------------------------------------------------------------- +KlakHAP +https://github.com/keijiro/KlakHap +------------------------------------------------------------------------------- + +Copyright (c) 2019 Unity Technologies + +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. + +------------------------------------------------------------------------------- +HAP +https://github.com/Vidvox/hap +------------------------------------------------------------------------------- + +Copyright (c) 2012-2013, Tom Butterworth and Vidvox LLC. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- +Snappy +https://github.com/google/snappy +------------------------------------------------------------------------------- + +Copyright 2011, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- +Minimalistic MP4 muxer & demuxer +https://github.com/aspt/mp4 +------------------------------------------------------------------------------- + +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/LICENSE.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/LICENSE.meta new file mode 100644 index 000000000..5f9e30a83 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/LICENSE.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: cafc978caabfcd84e8dc32d52b4f2b79 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin.meta new file mode 100644 index 000000000..fd08e25c4 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dd3bd64373abe6d4bac999a0b5b69bcd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Linux.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Linux.meta new file mode 100644 index 000000000..110f51641 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Linux.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 47a9aaa064c67514da9f5ff173be35fb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Linux/libKlakHap.so b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Linux/libKlakHap.so new file mode 100644 index 000000000..e125e30e7 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Linux/libKlakHap.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56982cab77f6b521c20177404e0f0b1a60cef6c34f420ca55e4c077f628c8dc5 +size 78368 diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Linux/libKlakHap.so.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Linux/libKlakHap.so.meta new file mode 100644 index 000000000..d5b1a6f3c --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Linux/libKlakHap.so.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 467f13044a0a9364795fef0b526e5eed +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + platformData: + - first: + '': Any + second: + enabled: 0 + settings: + Exclude Editor: 0 + Exclude Linux: 1 + Exclude Linux64: 0 + Exclude LinuxUniversal: 0 + Exclude OSXUniversal: 1 + Exclude Win: 0 + Exclude Win64: 0 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: x86_64 + DefaultValueInitialized: true + OS: Linux + - first: + Facebook: Win + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Facebook: Win64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Linux + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Linux64 + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + Standalone: LinuxUniversal + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: Win64 + second: + enabled: 1 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/MacOS.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/MacOS.meta new file mode 100644 index 000000000..78213a109 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/MacOS.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 93e3b14ab64306d4a85ce4feb6c9b2fd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/MacOS/KlakHap.bundle b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/MacOS/KlakHap.bundle new file mode 100644 index 000000000..68368e09e --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/MacOS/KlakHap.bundle @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c52025cb26f0455b8fe8b105798055538261a1e05603f6c44e318ff81fc68441 +size 216103 diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/MacOS/KlakHap.bundle.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/MacOS/KlakHap.bundle.meta new file mode 100644 index 000000000..af6713182 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/MacOS/KlakHap.bundle.meta @@ -0,0 +1,73 @@ +fileFormatVersion: 2 +guid: 849058cd649269543a8d95c4703cf3d9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 0 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: OSX + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: x86 + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: x86_64 + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Windows.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Windows.meta new file mode 100644 index 000000000..c53cd3ac4 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Windows.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: afcc650d93eee184aa34e7d62096bf27 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Windows/KlakHap.dll b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Windows/KlakHap.dll new file mode 100644 index 000000000..93fccce9e --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Windows/KlakHap.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c68247a7b2fef9ab9a2a6b62c7802d51d497536e80aff232f8a74228c59d19c2 +size 39936 diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Windows/KlakHap.dll.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Windows/KlakHap.dll.meta new file mode 100644 index 000000000..8dc658b02 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Plugin/Windows/KlakHap.dll.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: e794ded0c0aa5a4449367e5b2b2d96ed +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + platformData: + - first: + '': Any + second: + enabled: 0 + settings: + Exclude Editor: 0 + Exclude Linux: 0 + Exclude Linux64: 0 + Exclude LinuxUniversal: 0 + Exclude OSXUniversal: 0 + Exclude Win: 1 + Exclude Win64: 0 + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: x86_64 + DefaultValueInitialized: true + OS: Windows + - first: + Facebook: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Facebook: Win64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Linux + second: + enabled: 1 + settings: + CPU: x86 + - first: + Standalone: Linux64 + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + Standalone: LinuxUniversal + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 1 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/README.md b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/README.md new file mode 100644 index 000000000..baef6d225 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/README.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78248f00256d9b5f21740b1df8c6fc0e812aaeeeb1e3358cf66cd7266f0ec3d4 +size 5324 diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/README.md.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/README.md.meta new file mode 100644 index 000000000..8c2bb8f4d --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 76ac0e324de156547a580e86fdd8ea2f +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources.meta new file mode 100644 index 000000000..a77ea6a99 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2b78bdcad9737cf48a8f9b23254b7f84 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/Hap1.shader b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/Hap1.shader new file mode 100644 index 000000000..a0bebd544 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/Hap1.shader @@ -0,0 +1,57 @@ +Shader "Klak/HAP" +{ + Properties + { + _MainTex("Texture", 2D) = "white" {} + } + + CGINCLUDE + + #include "UnityCG.cginc" + + struct Attributes + { + float4 position : POSITION; + float2 texcoord : TEXCOORD; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct Varyings + { + float4 position : SV_Position; + float2 texcoord : TEXCOORD; + }; + + sampler2D _MainTex; + float4 _MainTex_ST; + + Varyings Vertex(Attributes input) + { + UNITY_SETUP_INSTANCE_ID(input); + Varyings output; + output.position = UnityObjectToClipPos(input.position); + output.texcoord = TRANSFORM_TEX(input.texcoord, _MainTex); + output.texcoord.y = 1 - output.texcoord.y; + return output; + } + + fixed4 Fragment(Varyings input) : SV_Target + { + return tex2D(_MainTex, input.texcoord); + } + + ENDCG + + SubShader + { + Tags { "RenderType"="Opaque" } + Pass + { + CGPROGRAM + #pragma vertex Vertex + #pragma fragment Fragment + #pragma multi_compile_instancing + ENDCG + } + } +} diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/Hap1.shader.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/Hap1.shader.meta new file mode 100644 index 000000000..ea1186872 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/Hap1.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 49f216cf9a0966d4493e7ba1af11c3d6 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/Hap5.shader b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/Hap5.shader new file mode 100644 index 000000000..18f42cfca --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/Hap5.shader @@ -0,0 +1,59 @@ +Shader "Klak/HAP Alpha" +{ + Properties + { + _MainTex("Texture", 2D) = "white" {} + } + + CGINCLUDE + + #include "UnityCG.cginc" + + struct Attributes + { + float4 position : POSITION; + float2 texcoord : TEXCOORD; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct Varyings + { + float4 position : SV_Position; + float2 texcoord : TEXCOORD; + }; + + sampler2D _MainTex; + float4 _MainTex_ST; + + Varyings Vertex(Attributes input) + { + UNITY_SETUP_INSTANCE_ID(input); + Varyings output; + output.position = UnityObjectToClipPos(input.position); + output.texcoord = TRANSFORM_TEX(input.texcoord, _MainTex); + output.texcoord.y = 1 - output.texcoord.y; + return output; + } + + fixed4 Fragment(Varyings input) : SV_Target + { + return tex2D(_MainTex, input.texcoord); + } + + ENDCG + + SubShader + { + Tags { "RenderType"="Transparent" "Queue"="Transparent" } + Pass + { + ZWrite Off + Blend SrcAlpha OneMinusSrcAlpha + CGPROGRAM + #pragma vertex Vertex + #pragma fragment Fragment + #pragma multi_compile_instancing + ENDCG + } + } +} diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/Hap5.shader.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/Hap5.shader.meta new file mode 100644 index 000000000..1efcf8801 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/Hap5.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 90b82a87346a5254286d43f76863782c +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/HapY.shader b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/HapY.shader new file mode 100644 index 000000000..4cfb31101 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/HapY.shader @@ -0,0 +1,72 @@ +Shader "Klak/HAP Q" +{ + Properties + { + _MainTex("Texture", 2D) = "white" {} + } + + CGINCLUDE + + #include "UnityCG.cginc" + + struct Attributes + { + float4 position : POSITION; + float2 texcoord : TEXCOORD; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct Varyings + { + float4 position : SV_Position; + float2 texcoord : TEXCOORD; + }; + + sampler2D _MainTex; + float4 _MainTex_ST; + + half3 CoCgSY2RGB(half4 i) + { + #if !defined(UNITY_COLORSPACE_GAMMA) + i.xyz = LinearToGammaSpace(i.xyz); + #endif + i.xy -= half2(0.50196078431373, 0.50196078431373); + half s = 1 / ((i.z * (255.0 / 8)) + 1); + half3 rgb = half3(i.x - i.y, i.y, -i.x - i.y) * s + i.w; + #if !defined(UNITY_COLORSPACE_GAMMA) + rgb = GammaToLinearSpace(rgb); + #endif + return rgb; + } + + Varyings Vertex(Attributes input) + { + UNITY_SETUP_INSTANCE_ID(input); + Varyings output; + output.position = UnityObjectToClipPos(input.position); + output.texcoord = TRANSFORM_TEX(input.texcoord, _MainTex); + output.texcoord.y = 1 - output.texcoord.y; + return output; + } + + fixed4 Fragment(Varyings input) : SV_Target + { + return fixed4(CoCgSY2RGB(tex2D(_MainTex, input.texcoord)), 1); + } + + ENDCG + + SubShader + { + Tags { "RenderType"="Opaque" } + Pass + { + CGPROGRAM + #pragma multi_compile _ UNITY_COLORSPACE_GAMMA + #pragma vertex Vertex + #pragma fragment Fragment + #pragma multi_compile_instancing + ENDCG + } + } +} diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/HapY.shader.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/HapY.shader.meta new file mode 100644 index 000000000..1bfd3cb58 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Resources/HapY.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 807e261a8905cde469501ab123338e05 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime.meta new file mode 100644 index 000000000..39c1fbb70 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3a1ee959e394fc54bb43e56fdae7a146 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Common.cs b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Common.cs new file mode 100644 index 000000000..30acd6f01 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Common.cs @@ -0,0 +1,4 @@ +namespace Klak.Hap +{ + public enum CodecType { Unsupported, Hap, HapQ, HapAlpha } +} diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Common.cs.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Common.cs.meta new file mode 100644 index 000000000..87e1b20d5 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Common.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ec81190dde6b4d24fb7fdb8f562fccbe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/FlipBlit.shader b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/FlipBlit.shader new file mode 100644 index 000000000..d3e2c2518 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/FlipBlit.shader @@ -0,0 +1,45 @@ +Shader "Hidden/FlipBlit" +{ + Properties + { + _MainTex ("Texture", 2D) = "white" {} + _FlipX ("Flip X", Float) = 0 + _FlipY ("Flip Y", Float) = 0 + } + SubShader + { + Pass + { + ZTest Always Cull Off ZWrite Off + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #include "UnityCG.cginc" + + sampler2D _MainTex; + float4 _MainTex_ST; + float _FlipX; + float _FlipY; + + struct appdata_t { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; + struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; + + v2f vert (appdata_t v) + { + v2f o; + o.vertex = UnityObjectToClipPos(v.vertex); + o.uv = TRANSFORM_TEX(v.uv, _MainTex); + return o; + } + + fixed4 frag (v2f i) : SV_Target + { + float2 uv = i.uv; + if (_FlipX > 0.5) uv.x = 1.0 - uv.x; + if (_FlipY > 0.5) uv.y = 1.0 - uv.y; + return tex2D(_MainTex, uv); + } + ENDCG + } + } +} \ No newline at end of file diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/FlipBlit.shader.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/FlipBlit.shader.meta new file mode 100644 index 000000000..7921aac39 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/FlipBlit.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 83fc09b934829ee489c1947480fcc03e +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/HapPlayer.cs b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/HapPlayer.cs new file mode 100644 index 000000000..a19b27103 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/HapPlayer.cs @@ -0,0 +1,339 @@ +using UnityEngine; +using UnityEngine.Playables; + +#if KLAKHAP_HAS_TIMELINE +using UnityEngine.Timeline; +#endif + +namespace Klak.Hap +{ + [ExecuteInEditMode, AddComponentMenu("Klak/HAP/HAP Player")] + #if KLAKHAP_HAS_TIMELINE + public sealed class HapPlayer : MonoBehaviour , ITimeControl, IPropertyPreview + #else + public sealed class HapPlayer : MonoBehaviour + #endif + { + #region Editable attributes + + public enum PathMode { StreamingAssets, LocalFileSystem } + + [SerializeField] PathMode _pathMode = PathMode.StreamingAssets; + [SerializeField] string _filePath = ""; + + [SerializeField] float _time = 0; + [SerializeField, Range(-10, 10)] float _speed = 1; + [SerializeField] bool _loop = true; + + [SerializeField] RenderTexture _targetTexture = null; + [SerializeField] string _targetMaterialProperty = "_MainTex"; + + [SerializeField] bool _flipHorizontal = false; + [SerializeField] bool _flipVertical = true; + + #endregion + + #region Public properties + + public float time { + get { return _time; } + set { _time = value; } + } + + public float speed { + get { return _speed; } + set { _speed = value; } + } + + public bool loop { + get { return _loop; } + set { _loop = value; } + } + + public RenderTexture targetTexture { + get { return _targetTexture; } + set { _targetTexture = value; } + } + + public string targetMaterialProperty { + get { return _targetMaterialProperty; } + set { _targetMaterialProperty = value; } + } + + public bool flipHorizontal { + get { return _flipHorizontal; } + set { _flipHorizontal = value; } + } + + public bool flipVertical { + get { return _flipVertical; } + set { _flipVertical = value; } + } + + #endregion + + #region Read-only properties + + public bool isValid { get { return _demuxer != null; } } + public int frameWidth { get { return _demuxer?.Width ?? 0; } } + public int frameHeight { get { return _demuxer?.Height ?? 0; } } + public int frameCount { get { return _demuxer?.FrameCount ?? 0; } } + public double streamDuration { get { return _demuxer?.Duration ?? 0; } } + + public CodecType codecType { get { + return Utility.DetermineCodecType(_demuxer?.VideoType ?? 0); + } } + + public string resolvedFilePath { get { + if (_pathMode == PathMode.StreamingAssets) + return System.IO.Path.Combine(Application.streamingAssetsPath, _filePath); + else + return _filePath; + } } + + public Texture2D texture { get { return _texture; } } + + #endregion + + #region Public methods + + public void Open(string filePath, PathMode pathMode = PathMode.StreamingAssets) + { + if (_demuxer != null) + { + Debug.LogError("Stream has already been opened."); + return; + } + + _filePath = filePath; + _pathMode = pathMode; + + OpenInternal(); + } + + public void UpdateNow() + => LateUpdate(); + + #endregion + + #region Private members + + Demuxer _demuxer; + StreamReader _stream; + Decoder _decoder; + + Texture2D _texture; + TextureUpdater _updater; + + float _storedTime; + float _storedSpeed; + + // Flip-related variables + Material _flipMaterial; + + void OpenInternal() + { + // Demuxer instantiation + _demuxer = new Demuxer(resolvedFilePath); + + if (!_demuxer.IsValid) + { + if (Application.isPlaying) + { + Debug.LogError("Failed to open stream (" + resolvedFilePath + ")."); + enabled = false; + } + _demuxer.Dispose(); + _demuxer = null; + return; + } + + _stream = new StreamReader(_demuxer, _time, _speed / 60); + (_storedTime, _storedSpeed) = (_time, _speed); + + _decoder = new Decoder( + _stream, _demuxer.Width, _demuxer.Height, _demuxer.VideoType + ); + + _texture = new Texture2D( + _demuxer.Width, _demuxer.Height, + Utility.DetermineTextureFormat(_demuxer.VideoType), false + ); + _texture.wrapMode = TextureWrapMode.Clamp; + _texture.hideFlags = HideFlags.DontSave; + + _updater = new TextureUpdater(_texture, _decoder); + } + + void UpdateTargetTexture() + { + if (_targetTexture == null || _texture == null || _demuxer == null) return; + if (this == null || !enabled) return; + + if (_flipMaterial == null) + { + _flipMaterial = new Material(Shader.Find("Hidden/FlipBlit")); + _flipMaterial.hideFlags = HideFlags.DontSave; + } + _flipMaterial.SetFloat("_FlipX", _flipHorizontal ? 1f : 0f); + _flipMaterial.SetFloat("_FlipY", _flipVertical ? 1f : 0f); + + try + { + Graphics.Blit(_texture, _targetTexture, _flipMaterial); + } + catch (System.NullReferenceException) + { + return; + } + } + + #endregion + + #region ITimeControl implementation + + bool _externalTime; + + public void OnControlTimeStart() + { + _externalTime = true; + + // In the external time mode, we can't know the actual playback + // speed but sure that it's positive (Control Track doesn't support + // reverse playback), so we assume that the speed is 1.0. + // Cons: Resync could happen every frame for high speed play back. + _speed = 1; + } + + public void OnControlTimeStop() + { + _externalTime = false; + } + + public void SetTime(double time) + { + _time = (float)time; + _speed = 1; + } + + #endregion + + #region IPropertyPreview implementation + + #if KLAKHAP_HAS_TIMELINE + public void GatherProperties(PlayableDirector director, IPropertyCollector driver) + { + driver.AddFromName(gameObject, "_time"); + } + #endif + + #endregion + + #region MonoBehaviour implementation + + void OnDestroy() + { + if (_updater != null) + { + _updater.Dispose(); + _updater = null; + } + + if (_decoder != null) + { + _decoder.Dispose(); + _decoder = null; + } + + if (_stream != null) + { + _stream.Dispose(); + _stream = null; + } + + if (_demuxer != null) + { + _demuxer.Dispose(); + _demuxer = null; + } + + Utility.Destroy(_texture); + Utility.Destroy(_flipMaterial); + } + + #if UNITY_EDITOR + void OnValidate() + { + // 더 이상 필요 없음: 렌더러 관련 클리어 코드 제거 + } + #endif + + int _lastUpdateFrameCount = -1; + + void LateUpdate() + { + if (!enabled || this == null) return; + if (Time.frameCount == _lastUpdateFrameCount) return; + _lastUpdateFrameCount = Time.frameCount; + + if (_demuxer == null && !string.IsNullOrEmpty(_filePath)) + OpenInternal(); + if (_demuxer == null) return; + + var duration = (float)_demuxer.Duration; + var dt = duration / _demuxer.FrameCount; + var resync = _time < _storedTime || _time > _storedTime + dt; + if (_speed != _storedSpeed) + { + resync = true; + _storedSpeed = _speed; + } + var t = _loop ? _time : Mathf.Clamp(_time, 0, duration - 1e-4f); + var bgdec = !resync && Application.isPlaying; + if (resync && _stream != null) _stream.Restart(t, _speed / 60); + if (_decoder != null && _updater != null) + { + if (TextureUpdater.AsyncSupport) + { + if (bgdec) _decoder.UpdateAsync(t); else _decoder.UpdateSync(t); + _updater.RequestAsyncUpdate(); + } +#if !HAP_NO_DELAY + else if (bgdec) + { + _updater.UpdateNow(); + _decoder.UpdateAsync(t); + } +#endif + else + { + _decoder.UpdateSync(t); + _updater.UpdateNow(); + } + } + if (Application.isPlaying && !_externalTime) + _time += Time.deltaTime * _speed; + _storedTime = _time; + + // 렌더 텍스처만 업데이트 + if (this != null && enabled) + { + try + { + if (_targetTexture != null) + UpdateTargetTexture(); + } + catch (System.NullReferenceException ex) + { + Debug.LogWarning($"HapPlayer: Null reference in external object updates - {ex.Message}"); + } + catch (System.Exception ex) + { + Debug.LogWarning($"HapPlayer: Unexpected error in external object updates - {ex.Message}"); + } + } + } + + #endregion + } +} diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/HapPlayer.cs.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/HapPlayer.cs.meta new file mode 100644 index 000000000..f07f95d72 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/HapPlayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b8ea76979ef31fb42882178890076b16 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal.meta new file mode 100644 index 000000000..f53010d22 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9826f2ed5102c594db39cb240eb1a004 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Decoder.cs b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Decoder.cs new file mode 100644 index 000000000..ada891213 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Decoder.cs @@ -0,0 +1,146 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Klak.Hap +{ + internal sealed class Decoder : IDisposable + { + #region Initialization/finalization + + public Decoder(StreamReader stream, int width, int height, int videoType) + { + _stream = stream; + + // Plugin initialization + _plugin = KlakHap_CreateDecoder(width, height, videoType); + _id = ++_instantiationCount; + KlakHap_AssignDecoder(_id, _plugin); + + // By default, start from the first frame. + _time = 0; + + // Decoder thread startup + _resume.req = new AutoResetEvent(true); + _resume.ack = new AutoResetEvent(false); + _thread = new Thread(DecoderThread); + _thread.Start(); + } + + public void Dispose() + { + if (_thread != null) + { + _terminate = true; + _resume.req.Set(); + _thread.Join(); + _thread = null; + } + + if (_plugin != IntPtr.Zero) + { + KlakHap_AssignDecoder(_id, IntPtr.Zero); + KlakHap_DestroyDecoder(_plugin); + _plugin = IntPtr.Zero; + } + } + + #endregion + + #region Public members + + public uint CallbackID { get { return _id; } } + + public int BufferSize { get { + return KlakHap_GetDecoderBufferSize(_plugin); + } } + + public void UpdateSync(float time) + { + _time = time; + var buffer = _stream.Advance(_time); + if (buffer != null) + KlakHap_DecodeFrame(_plugin, buffer.PluginPointer); + } + + public void UpdateAsync(float time) + { + _time = time; + _resume.req.Set(); + _resume.ack.WaitOne(); + } + + public IntPtr LockBuffer() + { + return KlakHap_LockDecoderBuffer(_plugin); + } + + public void UnlockBuffer() + { + KlakHap_UnlockDecoderBuffer(_plugin); + } + + #endregion + + #region Private members + + static uint _instantiationCount; + + IntPtr _plugin; + uint _id; + + Thread _thread; + (AutoResetEvent req, AutoResetEvent ack) _resume; + bool _terminate; + + StreamReader _stream; + float _time; + + #endregion + + #region Thread function + + void DecoderThread() + { + while (true) + { + _resume.req.WaitOne(); + _resume.ack.Set(); + + if (_terminate) break; + + var buffer = _stream.Advance(_time); + if (buffer == null) continue; + + KlakHap_DecodeFrame(_plugin, buffer.PluginPointer); + } + } + + #endregion + + #region Native plugin entry points + + [DllImport("KlakHap")] + internal static extern IntPtr KlakHap_CreateDecoder(int width, int height, int typeID); + + [DllImport("KlakHap")] + internal static extern void KlakHap_DestroyDecoder(IntPtr decoder); + + [DllImport("KlakHap")] + internal static extern void KlakHap_AssignDecoder(uint id, IntPtr decoder); + + [DllImport("KlakHap")] + internal static extern void KlakHap_DecodeFrame(IntPtr decoder, IntPtr input); + + [DllImport("KlakHap")] + internal static extern IntPtr KlakHap_LockDecoderBuffer(IntPtr decoder); + + [DllImport("KlakHap")] + internal static extern void KlakHap_UnlockDecoderBuffer(IntPtr decoder); + + [DllImport("KlakHap")] + internal static extern int KlakHap_GetDecoderBufferSize(IntPtr decoder); + + #endregion + } +} diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Decoder.cs.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Decoder.cs.meta new file mode 100644 index 000000000..8c323a8ec --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Decoder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fb63cbce06802f644820b09291d8dfb0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Demuxer.cs b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Demuxer.cs new file mode 100644 index 000000000..575917f7b --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Demuxer.cs @@ -0,0 +1,103 @@ +using System; +using System.Runtime.InteropServices; + +namespace Klak.Hap +{ + internal sealed class Demuxer : IDisposable + { + #region Public properties + + public bool IsValid { get { return _plugin != IntPtr.Zero; } } + public int Width { get { return _width; } } + public int Height { get { return _height; } } + public int VideoType { get { return _videoType; } } + public double Duration { get { return _duration; } } + public int FrameCount { get { return _frameCount; } } + + #endregion + + #region Initialization/finalization + + public Demuxer(string filePath) + { + _plugin = KlakHap_OpenDemuxer(filePath); + + if (KlakHap_DemuxerIsValid(_plugin) == 0) + { + // Instantiation failed; Close and stop. + KlakHap_CloseDemuxer(_plugin); + _plugin = IntPtr.Zero; + return; + } + + // Video properties + _width = KlakHap_GetVideoWidth(_plugin); + _height = KlakHap_GetVideoHeight(_plugin); + _videoType = KlakHap_AnalyzeVideoType(_plugin); + _duration = KlakHap_GetDuration(_plugin); + _frameCount = KlakHap_CountFrames(_plugin); + } + + public void Dispose() + { + if (_plugin != IntPtr.Zero) + { + KlakHap_CloseDemuxer(_plugin); + _plugin = IntPtr.Zero; + } + } + + #endregion + + #region Public methods + + public void ReadFrame(ReadBuffer buffer, int index, float time) + { + KlakHap_ReadFrame(_plugin, index, buffer.PluginPointer); + buffer.Index = index; + buffer.Time = time; + } + + #endregion + + #region Private members + + IntPtr _plugin; + int _width, _height, _videoType; + double _duration; + int _frameCount; + + #endregion + + #region Native plugin entry points + + [DllImport("KlakHap")] + internal static extern IntPtr KlakHap_OpenDemuxer(string filepath); + + [DllImport("KlakHap")] + internal static extern void KlakHap_CloseDemuxer(IntPtr demuxer); + + [DllImport("KlakHap")] + internal static extern int KlakHap_DemuxerIsValid(IntPtr demuxer); + + [DllImport("KlakHap")] + internal static extern int KlakHap_CountFrames(IntPtr demuxer); + + [DllImport("KlakHap")] + internal static extern double KlakHap_GetDuration(IntPtr demuxer); + + [DllImport("KlakHap")] + internal static extern int KlakHap_GetVideoWidth(IntPtr demuxer); + + [DllImport("KlakHap")] + internal static extern int KlakHap_GetVideoHeight(IntPtr demuxer); + + [DllImport("KlakHap")] + internal static extern int KlakHap_AnalyzeVideoType(IntPtr demuxer); + + [DllImport("KlakHap")] + internal static extern void KlakHap_ReadFrame(IntPtr demuxer, int frameNumber, IntPtr buffer); + + #endregion + } +} diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Demuxer.cs.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Demuxer.cs.meta new file mode 100644 index 000000000..c16ac2ceb --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Demuxer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 01e2f3e55fcbb10448e3b4976da8097d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/ReadBuffer.cs b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/ReadBuffer.cs new file mode 100644 index 000000000..3a5b74bfe --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/ReadBuffer.cs @@ -0,0 +1,48 @@ +using System; +using System.Runtime.InteropServices; + +namespace Klak.Hap +{ + internal sealed class ReadBuffer : IDisposable + { + #region Initialization/finalization + + public IntPtr PluginPointer { get { return _plugin; } } + public int Index { get; set; } + public float Time { get; set; } + + #endregion + + #region Initialization/finalization + + public ReadBuffer() + { + _plugin = KlakHap_CreateReadBuffer(); + Index = Int32.MaxValue; + Time = Single.MaxValue; + } + + public void Dispose() + { + KlakHap_DestroyReadBuffer(_plugin); + } + + #endregion + + #region Private members + + IntPtr _plugin; + + #endregion + + #region Native plugin entry points + + [DllImport("KlakHap")] + internal static extern IntPtr KlakHap_CreateReadBuffer(); + + [DllImport("KlakHap")] + internal static extern void KlakHap_DestroyReadBuffer(IntPtr buffer); + + #endregion + } +} diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/ReadBuffer.cs.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/ReadBuffer.cs.meta new file mode 100644 index 000000000..78161f0c7 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/ReadBuffer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4489c695e7711e44bbe985cac12755c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/StreamReader.cs b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/StreamReader.cs new file mode 100644 index 000000000..ca4fd3751 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/StreamReader.cs @@ -0,0 +1,266 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Klak.Hap +{ + internal sealed class StreamReader : IDisposable + { + #region Public methods + + public StreamReader(Demuxer demuxer, float time, float delta) + { + _demuxer = demuxer; + + _leadQueue = new Queue(); + _freeBuffers = new List(); + + // Initial buffer entry allocation + _freeBuffers.Add(new ReadBuffer()); + _freeBuffers.Add(new ReadBuffer()); + _freeBuffers.Add(new ReadBuffer()); + _freeBuffers.Add(new ReadBuffer()); + + // Initial playback settings + _restart = (time, SafeDelta(delta)); + + // Reader thread startup + _updateEvent = new AutoResetEvent(true); + _readEvent = new AutoResetEvent(false); + _thread = new Thread(ReaderThread); + _thread.Start(); + } + + public void Dispose() + { + if (_thread != null) + { + _terminate = true; + _updateEvent.Set(); + _thread.Join(); + _thread = null; + } + + if (_updateEvent != null) + { + _updateEvent.Dispose(); + _updateEvent = null; + } + + if (_readEvent != null) + { + _readEvent.Dispose(); + _readEvent = null; + } + + if (_current != null) + { + _current.Dispose(); + _current = null; + } + + if (_leadQueue != null) + { + foreach (var rb in _leadQueue) rb.Dispose(); + _leadQueue.Clear(); + _leadQueue = null; + } + + if (_freeBuffers != null) + { + foreach (var rb in _freeBuffers) rb.Dispose(); + _freeBuffers.Clear(); + _freeBuffers = null; + } + } + + public void Restart(float time, float delta) + { + // Restart request + lock (_restartLock) _restart = (time, SafeDelta(delta)); + + // Wait for reset/read on the reader thread. + _readEvent.Reset(); + while (_restart != null) + { + _updateEvent.Set(); + _readEvent.WaitOne(); + } + } + + public ReadBuffer Advance(float time) + { + // Add an epsilon-ish value to avoid rounding error. + time += 1e-6f; + + var changed = false; + + // There is no slow path in this function, so we prefer holding + // the queue lock for the entire function block rather than + // acquiring/releasing it for each operation. + lock (_queueLock) + { + // Scan the lead queue. + while (_leadQueue.Count > 0) + { + var peek = _leadQueue.Peek(); + + if (_current != null) + { + if (_current.Time <= peek.Time) + { + // Forward playback case: + // Break if it hasn't reached the next frame. + if (time < peek.Time) break; + } + else + { + // Reverse playback case: + // Break if it's still on the current frame. + if (_current.Time < time) break; + } + + // Free the current frame before replacing it. + _freeBuffers.Add(_current); + } + + _current = _leadQueue.Dequeue(); + changed = true; + } + } + + // Poke the reader thread. + _updateEvent.Set(); + + // Only returns a buffer object when the frame was changed. + return changed ? _current : null; + } + + #endregion + + #region Private members + + // Assigned demuxer + Demuxer _demuxer; + + // Thread and synchronization objects + Thread _thread; + AutoResetEvent _updateEvent; + AutoResetEvent _readEvent; + bool _terminate; + + // Read buffer objects + ReadBuffer _current; + Queue _leadQueue; + List _freeBuffers; + readonly object _queueLock = new object(); + + // Restart request + (float, float)? _restart; + readonly object _restartLock = new object(); + + // Used to avoid too small delta time values. + float SafeDelta(float delta) + { + var min = (float)(_demuxer.Duration / _demuxer.FrameCount); + return Math.Max(Math.Abs(delta), min) * (delta < 0 ? -1 : 1); + } + + #endregion + + #region Thread function + + void ReaderThread() + { + // Initial time settings from the restart request tuple + var (time, delta) = _restart.Value; + _restart = null; + + // Stream attributes + var totalTime = _demuxer.Duration; + var totalFrames = _demuxer.FrameCount; + + while (true) + { + // Synchronization with the parent thread + _updateEvent.WaitOne(); + if (_terminate) break; + + // Check if there is a restart request. + lock (_restartLock) if (_restart != null) + { + // Flush out the current contents of the lead queue. + lock (_queueLock) while (_leadQueue.Count > 0) + _freeBuffers.Add(_leadQueue.Dequeue()); + + // Apply the restart request. + (time, delta) = _restart.Value; + _restart = null; + } + + // Time -> Frame count + // Rounding strategy: We don't prefer Math.Round because it can + // show a frame before the playhead reaches it (especially when + // using slow-mo). On the other hand, Math.Floor causes frame + // skipping due to rounding errors. To avoid these problems, + // we use the "adding a very-very small fractional frame" + // approach. 1/1000 might be safe and enough for all the cases. + var frameCount = (int)(time * totalFrames / totalTime + 1e-3f); + + // Frame count -> Frame snapped time + var snappedTime = (float)(frameCount * totalTime / totalFrames); + + // Frame count -> Wrapped frame number + var frameNumber = frameCount % totalFrames; + if (frameNumber < 0) frameNumber += totalFrames; + + lock (_queueLock) + { + // Do nothing if there is no free buffer; It indicates that + // the lead queue is fully filled. + if (_freeBuffers.Count == 0) continue; + + ReadBuffer buffer = null; + + // Look for a free buffer that has the same frame number. + foreach (var temp in _freeBuffers) + { + if (temp.Index == frameNumber) + { + buffer = temp; + break; + } + } + + if (buffer != null) + { + // Reuse the found buffer; Although we can use it + // without reading frame data, the time field should + // be updated to handle wrapping-around hits. + _freeBuffers.Remove(buffer); + buffer.Time = snappedTime; + } + else + { + // Allocate a buffer from the free buffer list. + buffer = _freeBuffers[_freeBuffers.Count - 1]; + _freeBuffers.RemoveAt(_freeBuffers.Count - 1); + + // Frame data read + _demuxer.ReadFrame(buffer, frameNumber, snappedTime); + } + + // Push the buffer to the lead queue. + _leadQueue.Enqueue(buffer); + } + + _readEvent.Set(); + + time += delta; + } + } + + #endregion + } +} diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/StreamReader.cs.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/StreamReader.cs.meta new file mode 100644 index 000000000..bfd898d6f --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/StreamReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a5b51807ae8c7e749b98a930666f136b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/TextureUpdater.cs b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/TextureUpdater.cs new file mode 100644 index 000000000..54163f082 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/TextureUpdater.cs @@ -0,0 +1,77 @@ +using System; +using System.Runtime.InteropServices; +using UnityEngine; +using UnityEngine.Rendering; + +namespace Klak.Hap +{ + internal sealed class TextureUpdater : IDisposable + { + #region Public properties + + public static bool AsyncSupport { get { + return SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11; + } } + + #endregion + + #region Public methods + + public TextureUpdater(Texture2D texture, Decoder decoder) + { + _texture = texture; + _decoder = decoder; + + if (AsyncSupport) + { + _command = new CommandBuffer(); + _command.name = "Klak HAP"; + _command.IssuePluginCustomTextureUpdateV2( + KlakHap_GetTextureUpdateCallback(), + texture, decoder.CallbackID + ); + } + } + + public void Dispose() + { + if (_command != null) + { + _command.Dispose(); + _command = null; + } + } + + public void UpdateNow() + { + _texture.LoadRawTextureData( + _decoder.LockBuffer(), + _decoder.BufferSize + ); + _texture.Apply(); + _decoder.UnlockBuffer(); + } + + public void RequestAsyncUpdate() + { + if (_command != null) Graphics.ExecuteCommandBuffer(_command); + } + + #endregion + + #region Private fields + + Texture2D _texture; + Decoder _decoder; + CommandBuffer _command; + + #endregion + + #region Native plugin entry points + + [DllImport("KlakHap")] + internal static extern IntPtr KlakHap_GetTextureUpdateCallback(); + + #endregion + } +} diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/TextureUpdater.cs.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/TextureUpdater.cs.meta new file mode 100644 index 000000000..e180145a0 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/TextureUpdater.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 430fee5c2bdcf0c4492d6b8d742439f8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Utility.cs b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Utility.cs new file mode 100644 index 000000000..90a9491f3 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Utility.cs @@ -0,0 +1,48 @@ +using UnityEngine; + +namespace Klak.Hap +{ + internal static class Utility + { + public static void Destroy(Object o) + { + if (o == null) return; + if (Application.isPlaying) + Object.Destroy(o); + else + Object.DestroyImmediate(o); + } + + public static CodecType DetermineCodecType(int videoType) + { + switch (videoType & 0xf) + { + case 0xb: return CodecType.Hap; + case 0xe: return CodecType.HapAlpha; + case 0xf: return CodecType.HapQ; + } + return CodecType.Unsupported; + } + + public static TextureFormat DetermineTextureFormat(int videoType) + { + switch (videoType & 0xf) + { + case 0xb: return TextureFormat.DXT1; + case 0xe: return TextureFormat.DXT5; + case 0xf: return TextureFormat.DXT5; + case 0xc: return TextureFormat.BC7; + case 0x1: return TextureFormat.BC4; + } + return TextureFormat.DXT1; + } + + public static Shader DetermineBlitShader(int videoType) + { + if ((videoType & 0xf) == 0xf) + return Shader.Find("Klak/HAP Q"); + else + return Shader.Find("Klak/HAP"); + } + } +} diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Utility.cs.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Utility.cs.meta new file mode 100644 index 000000000..4bb645004 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Internal/Utility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f6abcc4e09417604ca245a0c5a5a1f0c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Klak.Hap.asmdef b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Klak.Hap.asmdef new file mode 100644 index 000000000..e6b36b4da --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Klak.Hap.asmdef @@ -0,0 +1,21 @@ +{ + "name": "Klak.Hap", + "references": [ + "GUID:f06555f75b070af458a003d92f9efb00" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.unity.timeline", + "expression": "1.0.0", + "define": "KLAKHAP_HAS_TIMELINE" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Klak.Hap.asmdef.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Klak.Hap.asmdef.meta new file mode 100644 index 000000000..d1f21cf0e --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/Runtime/Klak.Hap.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d30a992d73d108f4eaaafff3ed9490e3 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/package.json b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/package.json new file mode 100644 index 000000000..8de3014b1 --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/package.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0dda45a68fc632cfb5dcaab2c2ece70c8c7d1c8c7c05bb43f9838f5a9dc79dac +size 401 diff --git a/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/package.json.meta b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/package.json.meta new file mode 100644 index 000000000..984214dfb --- /dev/null +++ b/Assets/External/jp.keijiro.klak.hap@9a47acf6295e/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 52bab5075038b8c46ac3f7bb51b068b2 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Streamdeck.meta b/Assets/Scripts/Streamdeck.meta new file mode 100644 index 000000000..bffe04f15 --- /dev/null +++ b/Assets/Scripts/Streamdeck.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4fcd9eb3395efa844aedc41188018b29 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Streamdeck/DebugTest.cs b/Assets/Scripts/Streamdeck/DebugTest.cs new file mode 100644 index 000000000..7b6782fd3 --- /dev/null +++ b/Assets/Scripts/Streamdeck/DebugTest.cs @@ -0,0 +1,9 @@ +using UnityEngine; + +public class DebugTest : MonoBehaviour +{ + public void DebugLog() + { + Debug.Log("message"); + } +} diff --git a/Assets/Scripts/Streamdeck/DebugTest.cs.meta b/Assets/Scripts/Streamdeck/DebugTest.cs.meta new file mode 100644 index 000000000..2a94d5f49 --- /dev/null +++ b/Assets/Scripts/Streamdeck/DebugTest.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 88aefb43401e8a94f820bc478c2202d5 \ No newline at end of file diff --git a/Assets/Scripts/Streamdeck/SimpleEventExample.cs b/Assets/Scripts/Streamdeck/SimpleEventExample.cs new file mode 100644 index 000000000..111200963 --- /dev/null +++ b/Assets/Scripts/Streamdeck/SimpleEventExample.cs @@ -0,0 +1,101 @@ +using UnityEngine; + +public class SimpleEventExample : MonoBehaviour +{ + [Header("StreamDock 통신")] + [SerializeField] private SimpleStreamDockCommunicator streamDock; + + void Start() + { + // StreamDock 이벤트 구독 + if (streamDock != null) + { + streamDock.OnStreamDockMessageReceived.AddListener(OnStreamDockMessage); + streamDock.OnConnected.AddListener(OnStreamDockConnected); + streamDock.OnDisconnected.AddListener(OnStreamDockDisconnected); + } + } + + /// + /// StreamDock에서 메시지 수신 시 처리 + /// + private void OnStreamDockMessage(string eventType, object data) + { + //Debug.Log($"StreamDock 이벤트 수신: {eventType}"); + + switch (eventType) + { + case "button_clicked": + // 버튼 클릭 시 실행할 코드 + Debug.Log("버튼 클릭 이벤트 실행!"); + DoSomething(); + break; + + case "dial_rotate": + // 다이얼 회전 시 실행할 코드 + Debug.Log("다이얼 회전 이벤트 실행!"); + DoSomethingElse(); + break; + + case "dial_press": + // 다이얼 누름 시 실행할 코드 + Debug.Log("다이얼 누름 이벤트 실행!"); + DoAnotherThing(); + break; + } + } + + /// + /// StreamDock 연결 시 처리 + /// + private void OnStreamDockConnected() + { + Debug.Log("StreamDock에 연결되었습니다!"); + } + + /// + /// StreamDock 연결 해제 시 처리 + /// + private void OnStreamDockDisconnected() + { + Debug.Log("StreamDock 연결이 해제되었습니다."); + } + + // 여기에 원하는 동작들을 구현하세요 + private void DoSomething() + { + Debug.Log("버튼 클릭으로 실행된 동작!"); + // 예: 오브젝트 활성화/비활성화, 애니메이션 재생, 사운드 재생 등 + } + + private void DoSomethingElse() + { + Debug.Log("다이얼 회전으로 실행된 동작!"); + // 예: 볼륨 조절, 카메라 회전, 값 변경 등 + } + + private void DoAnotherThing() + { + Debug.Log("다이얼 누름으로 실행된 동작!"); + // 예: 특수 기능 실행, 모드 변경 등 + } + + // 공개 메서드들 (Inspector에서 호출 가능) + [ContextMenu("테스트 동작 1")] + public void TestAction1() + { + DoSomething(); + } + + [ContextMenu("테스트 동작 2")] + public void TestAction2() + { + DoSomethingElse(); + } + + [ContextMenu("테스트 동작 3")] + public void TestAction3() + { + DoAnotherThing(); + } +} \ No newline at end of file diff --git a/Assets/Scripts/Streamdeck/SimpleEventExample.cs.meta b/Assets/Scripts/Streamdeck/SimpleEventExample.cs.meta new file mode 100644 index 000000000..ab7c1e73e --- /dev/null +++ b/Assets/Scripts/Streamdeck/SimpleEventExample.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e65616cccbfc27348b6c11acf939a5f4 \ No newline at end of file diff --git a/Assets/Scripts/Streamdeck/SimpleStreamDockCommunicator.cs b/Assets/Scripts/Streamdeck/SimpleStreamDockCommunicator.cs new file mode 100644 index 000000000..73541a708 --- /dev/null +++ b/Assets/Scripts/Streamdeck/SimpleStreamDockCommunicator.cs @@ -0,0 +1,274 @@ +using UnityEngine; +using UnityEngine.Events; +using System; +using System.Collections; +using System.Text; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; + +[System.Serializable] +public class StreamDockEvent : UnityEvent { } + +public class SimpleStreamDockCommunicator : MonoBehaviour +{ + [Header("연결 설정")] + [SerializeField] private string serverUrl = "ws://localhost:15732"; + [SerializeField] private bool autoConnect = true; + [SerializeField] private float reconnectInterval = 5f; + + [Header("이벤트")] + public StreamDockEvent OnStreamDockMessageReceived; + public UnityEvent OnConnected; + public UnityEvent OnDisconnected; + + // 내부 변수 + private ClientWebSocket webSocket; + private CancellationTokenSource cancellationTokenSource; + private bool isConnecting = false; + private bool isConnected = false; + + // 프로퍼티 + public bool IsConnected => isConnected; + + void Start() + { + if (autoConnect) + { + ConnectToStreamDock(); + } + } + + void OnDestroy() + { + DisconnectFromStreamDock(); + } + + /// + /// StreamDock에 연결 + /// + public async void ConnectToStreamDock() + { + if (isConnecting || isConnected) return; + + isConnecting = true; + + try + { + webSocket = new ClientWebSocket(); + cancellationTokenSource = new CancellationTokenSource(); + + Debug.Log($"StreamDock에 연결 중... {serverUrl}"); + + await webSocket.ConnectAsync(new Uri(serverUrl), cancellationTokenSource.Token); + + isConnected = true; + isConnecting = false; + + Debug.Log("StreamDock에 연결되었습니다!"); + OnConnected?.Invoke(); + + // 메시지 수신 시작 + _ = ReceiveMessages(); + + } + catch (Exception e) + { + Debug.LogError($"StreamDock 연결 실패: {e.Message}"); + isConnecting = false; + OnDisconnected?.Invoke(); + + // 재연결 시도 + StartCoroutine(TryReconnect()); + } + } + + /// + /// StreamDock 연결 해제 + /// + public async void DisconnectFromStreamDock() + { + if (!isConnected) return; + + try + { + cancellationTokenSource?.Cancel(); + + if (webSocket != null && webSocket.State == WebSocketState.Open) + { + await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Unity 종료", CancellationToken.None); + } + } + catch (Exception e) + { + Debug.LogError($"연결 해제 중 오류: {e.Message}"); + } + finally + { + isConnected = false; + webSocket?.Dispose(); + webSocket = null; + OnDisconnected?.Invoke(); + } + } + + /// + /// StreamDock으로 메시지 전송 + /// + public async void SendMessageToStreamDock(string eventType, object data = null) + { + if (!isConnected) + { + Debug.LogWarning("StreamDock에 연결되지 않았습니다."); + return; + } + + try + { + var message = new + { + type = eventType, + data = data, + timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + }; + + string jsonMessage = JsonUtility.ToJson(message); + byte[] buffer = Encoding.UTF8.GetBytes(jsonMessage); + + await webSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, cancellationTokenSource.Token); + + Debug.Log($"StreamDock으로 메시지 전송: {eventType}"); + } + catch (Exception e) + { + Debug.LogError($"메시지 전송 실패: {e.Message}"); + } + } + + /// + /// 메시지 수신 처리 + /// + private async Task ReceiveMessages() + { + var buffer = new byte[4096]; + + try + { + while (webSocket.State == WebSocketState.Open) + { + var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), cancellationTokenSource.Token); + + if (result.MessageType == WebSocketMessageType.Text) + { + string message = Encoding.UTF8.GetString(buffer, 0, result.Count); + ProcessReceivedMessage(message); + } + else if (result.MessageType == WebSocketMessageType.Close) + { + Debug.Log("StreamDock에서 연결을 종료했습니다."); + break; + } + } + } + catch (Exception e) + { + Debug.LogError($"메시지 수신 중 오류: {e.Message}"); + } + finally + { + isConnected = false; + OnDisconnected?.Invoke(); + StartCoroutine(TryReconnect()); + } + } + + /// + /// 수신된 메시지 처리 + /// + private void ProcessReceivedMessage(string message) + { + try + { + //Debug.Log($"StreamDock에서 메시지 수신: {message}"); + + // JSON 파싱 (간단한 구조) + if (message.Contains("type")) + { + // UnityEvent 호출 + OnStreamDockMessageReceived?.Invoke("streamdock_message", message); + + // 특정 이벤트 타입 처리 + if (message.Contains("streamdock_button_clicked")) + { + HandleButtonClick(message); + } + else if (message.Contains("dial_rotate")) + { + HandleDialRotate(message); + } + else if (message.Contains("dial_press")) + { + HandleDialPress(message); + } + } + } + catch (Exception e) + { + Debug.LogError($"메시지 처리 중 오류: {e.Message}"); + } + } + + /// + /// 버튼 클릭 이벤트 처리 + /// + private void HandleButtonClick(string message) + { + Debug.Log("StreamDock 버튼이 클릭되었습니다!"); + OnStreamDockMessageReceived?.Invoke("button_clicked", message); + } + + /// + /// 다이얼 회전 이벤트 처리 + /// + private void HandleDialRotate(string message) + { + Debug.Log("StreamDock 다이얼이 회전했습니다!"); + OnStreamDockMessageReceived?.Invoke("dial_rotate", message); + } + + /// + /// 다이얼 누름 이벤트 처리 + /// + private void HandleDialPress(string message) + { + Debug.Log("StreamDock 다이얼이 눌렸습니다!"); + OnStreamDockMessageReceived?.Invoke("dial_press", message); + } + + /// + /// 재연결 시도 + /// + private IEnumerator TryReconnect() + { + yield return new WaitForSeconds(reconnectInterval); + + if (!isConnected && !isConnecting) + { + Debug.Log("StreamDock 재연결 시도..."); + ConnectToStreamDock(); + } + } + + // 테스트용 메서드들 + [ContextMenu("테스트 메시지 전송")] + public void SendTestMessage() + { + SendMessageToStreamDock("test_message", new { message = "Unity에서 테스트 메시지" }); + } + + [ContextMenu("커스텀 이벤트 전송")] + public void SendCustomEvent() + { + SendMessageToStreamDock("custom_event", new { action = "test_action", value = 123 }); + } +} \ No newline at end of file diff --git a/Assets/Scripts/Streamdeck/SimpleStreamDockCommunicator.cs.meta b/Assets/Scripts/Streamdeck/SimpleStreamDockCommunicator.cs.meta new file mode 100644 index 000000000..f197e15ef --- /dev/null +++ b/Assets/Scripts/Streamdeck/SimpleStreamDockCommunicator.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: dd2d6415e76fac64ea5a022f3cfefa97 \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/.gitignore b/StreamDock-Plugin-SDK/.gitignore new file mode 100644 index 000000000..cd5d3a6c3 --- /dev/null +++ b/StreamDock-Plugin-SDK/.gitignore @@ -0,0 +1,62 @@ +**/.vscode/ +**/.vs/ + +# Package files +**/package-lock.json + +# Non-npm lockfiles +**/pnpm-lock.yaml +**/yarn.lock +**/deno.lock +**/bun.lockb +**/bun.lock + +# Dependencies +**/node_modules/ + +# Build outputs +**/build/ +**/dist/ +**/*.build/ +**/out/ +**/__pycache__/ + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# IDE and editor files +**/.idea/ +**/*.swp +**/*.swo +**/*.swn +**/.history/ +*.sublime-workspace +*.sublime-project + +# Logs and databases +**/logs +**/*.log +**/*.sqlite +**/*.db + +# Environment variables +.env +.env.local +.env.*.local +.env.development +.env.test +.env.production + +# Testing +**/coverage/ +**/.nyc_output/ + +# Temporary files +**/tmp/ +**/temp/ \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/Icon/unity.png b/StreamDock-Plugin-SDK/Icon/unity.png new file mode 100644 index 000000000..08a405a10 --- /dev/null +++ b/StreamDock-Plugin-SDK/Icon/unity.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c6a70ce470c92e666d43f4491ca55dafccc7811e0e1672351eb23fcc6d910b8a +size 17016 diff --git a/StreamDock-Plugin-SDK/LICENSE b/StreamDock-Plugin-SDK/LICENSE new file mode 100644 index 000000000..10ec242c0 --- /dev/null +++ b/StreamDock-Plugin-SDK/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 MiraboxSpace + +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/StreamDock-Plugin-SDK/README.md b/StreamDock-Plugin-SDK/README.md new file mode 100644 index 000000000..b93ad58ed --- /dev/null +++ b/StreamDock-Plugin-SDK/README.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78f5efe5da71208421dd520f30b32cfb82ee6a5039620005516e799d44646c34 +size 2501 diff --git a/StreamDock-Plugin-SDK/README.zh-CN.md b/StreamDock-Plugin-SDK/README.zh-CN.md new file mode 100644 index 000000000..bcecddf76 --- /dev/null +++ b/StreamDock-Plugin-SDK/README.zh-CN.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87db6498359f8dc83830a898fab29a5698893d50d038e2607a76672bdfa48eb2 +size 2313 diff --git a/StreamDock-Plugin-SDK/README_한국어.md b/StreamDock-Plugin-SDK/README_한국어.md new file mode 100644 index 000000000..f46c7cdfe --- /dev/null +++ b/StreamDock-Plugin-SDK/README_한국어.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0bf7c4f0672a785e5309d8fa4a10b6768c90e49c869f9174481b434c204fcbf +size 4757 diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/en.json b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/en.json new file mode 100644 index 000000000..4be973af1 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/en.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e29e5f0ee2766ff2d69fc387040ecb763ddf2b599504aadd86c01415919a6317 +size 232 diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/manifest.json b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/manifest.json new file mode 100644 index 000000000..5fa2a82d6 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/manifest.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f775b80527545c848bb320829aa9dd715ed5efb082b9b2f29018a65dcca68c8 +size 1106 diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/plugin/index.html b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/plugin/index.html new file mode 100644 index 000000000..6c926ad84 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/plugin/index.html @@ -0,0 +1,14 @@ + + + + + + 插件调试 + + + + + + + + \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/plugin/index.js b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/plugin/index.js new file mode 100644 index 000000000..a8c33004b --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/plugin/index.js @@ -0,0 +1,19 @@ +/// +/// + +const plugin = new Plugins("xxx"); + +// 操作一 +plugin.action1 = new Actions({ + default: {}, + _willAppear({ context }) { + window.socket.setTitle(context, "Hello world!"); + }, + _willDisappear({ context }) { }, + dialRotate(data) {//旋钮旋转 + console.log(data); + }, + dialDown(data) {//旋钮按下 + console.log(data); + } +}); \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/plugin/utils/axios.js b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/plugin/utils/axios.js new file mode 100644 index 000000000..78aa7b89d --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/plugin/utils/axios.js @@ -0,0 +1 @@ +!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/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/plugin/utils/common.js b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/plugin/utils/common.js new file mode 100644 index 000000000..beac8cc17 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/plugin/utils/common.js @@ -0,0 +1,155 @@ +const Timer = new Worker('./utils/worker.js'); +const TimerSubscribe = { setTimeout: {}, setInterval: {} }; +Timer.addEventListener('message', function ({ data }) { + TimerSubscribe[data.event][data.id]?.(); +}); + +class Actions { + constructor(data) { + this.data = {}; + this.default = {}; + Object.assign(this, data); + } + // 属性检查器显示时 + static currentAction = null; + static currentContext = null; + propertyInspectorDidAppear({ action, context }) { + Actions.currentAction = action; + Actions.currentContext = context; + this._propertyInspectorDidAppear?.(data); + } + // 初始化数据 + willAppear(data) { + const { context, payload: { settings } } = data; + this.data[context] = Object.assign({ ...this.default }, settings); + this._willAppear?.(data); + } + // 行动销毁 + willDisappear(data) { + this._willDisappear?.(data); + delete this.data[data.context]; + } +} + +class Plugins { + constructor(name) { + this.name = name; + } + // 延时器/清除延时 + clearTimeout(id) { Timer.postMessage({ event: 'clearTimeout', id }); } + setTimeout(id, callback, delay) { + this.clearTimeout(id); + TimerSubscribe.setTimeout[id] = callback; + Timer.postMessage({ + event: 'setTimeout', + id, delay + }); + } + // 定时器/清除定时器 + clearInterval(id) { Timer.postMessage({ event: 'clearInterval', id }); } + setInterval(id, callback, delay) { + this.clearInterval(id); + TimerSubscribe.setInterval[id] = callback; + Timer.postMessage({ + event: 'setInterval', + id, delay + }); + } +} + +// 软件通信 +window.connectElgatoStreamDeckSocket = function () { + const uuid = arguments[1], event = arguments[2]; + window.info = JSON.parse(arguments[3]); + window.socket = new WebSocket("ws://127.0.0.1:" + arguments[0]); + + // 打开网页 + WebSocket.prototype.openUrl = function (url) { + this.send(JSON.stringify({ + event: "openUrl", + payload: { url } + })); + }; + + // 与当前属性检查器通信 + WebSocket.prototype.sendToPropertyInspector = function (payload) { + this.send(JSON.stringify({ + payload, + event: "sendToPropertyInspector", + action: Actions.currentAction, + context: Actions.currentContext + })); + }; + + // 保存持久化数据 + WebSocket.prototype.setSettings = function (context, payload) { + this.send(JSON.stringify({ + event: "setSettings", + context, payload + })); + }; + + // 设置背景 + WebSocket.prototype.setImage = function (context, url) { + const image = new Image(); + image.src = url; + image.onload = () => { + const canvas = document.createElement("canvas"); + canvas.width = image.naturalWidth; + canvas.height = image.naturalHeight; + const ctx = canvas.getContext("2d"); + ctx.drawImage(image, 0, 0); + this.send(JSON.stringify({ + event: "setImage", + context, payload: { + target: 0, + image: canvas.toDataURL("image/png") + } + })); + }; + }; + + // 设置标题 + WebSocket.prototype.setTitle = function (context, str, row = 0, num = 6) { + 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, payload: { + target: 0, + title: newStr || str + } + })); + }; + + // 设置状态 + WebSocket.prototype.setState = function (context, state) { + this.send(JSON.stringify({ + event: "setState", + payload: { state }, + context + })); + }; + + window.socket.onopen = () => window.socket.send(JSON.stringify({ uuid, event })); + window.socket.onmessage = e => { + const data = JSON.parse(e.data); + plugin[data.action?.split('.').pop()]?.[data.event]?.(data); + plugin[data.event]?.(data); + }; +}; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/plugin/utils/worker.js b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/plugin/utils/worker.js new file mode 100644 index 000000000..241ed1a82 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/plugin/utils/worker.js @@ -0,0 +1,30 @@ +const that = this, Timer = {} + +const handle = { + setTimeout(data) { + Timer[data.id] = setTimeout(() => { + that.self.postMessage({ + event: 'setTimeout', + id: data.id + }) + }, data.delay) + }, + setInterval(data) { + Timer[data.id] = setInterval(() => { + that.self.postMessage({ + event: 'setInterval', + id: data.id + }) + }, data.delay) + }, + clearTimeout(data) { + clearTimeout(Timer[data.id]) + }, + clearInterval(data) { + clearInterval(Timer[data.id]) + } +} + +this.self.onmessage = function ({ data }) { + handle[data?.event]?.(data) +} \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/propertyInspector/action1/index.html b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/propertyInspector/action1/index.html new file mode 100644 index 000000000..1a2cb39d2 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/propertyInspector/action1/index.html @@ -0,0 +1,19 @@ + + + + + + 模板 - 属性检查器 + + + + + + + + + + + \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/propertyInspector/action1/index.js b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/propertyInspector/action1/index.js new file mode 100644 index 000000000..4d5fa0f8f --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/propertyInspector/action1/index.js @@ -0,0 +1,21 @@ +/// +/// + +/** + * 基础参数说明: + * @global websocket uuid action context settings lang + * @settings local back 是否国际化 | 是否自行回显 + * @policy dom propEvent 缓存文档元素 | 软件触发事件 - 策略模式 + * =======================================================================> + */ + +const $local = false, $back = false, $dom = { + main: $('.sdpi-wrapper') +}; + +const $propEvent = { + didReceiveSettings(data) { + console.log(data); + }, + sendToPropertyInspector(data) { } +}; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/propertyInspector/utils/action.js b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/propertyInspector/utils/action.js new file mode 100644 index 000000000..d81fc1db7 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/propertyInspector/utils/action.js @@ -0,0 +1,130 @@ +/** + * PropertyInspector 2.5.0 新特性 => + * + * 1 => 工具与主文件相分离 - 按需引入 + * 2 => $settings - 全局持久化数据代理 ※ + * 3 => 无需关注上下文 - 随时随地与插件通信 + * 4 => 注意事项: 为了避免命名冲突,请勿使用 $ 相关的名称以及JQuery库 + * + * ===== CJHONG ========================================== 2023.10.10 =====> + */ + +let $websocket, $uuid, $action, $context, $settings, $lang, $FileID = ''; + +// 与插件通信 +WebSocket.prototype.sendToPlugin = function (payload) { + this.send(JSON.stringify({ + event: "sendToPlugin", + action: $action, + context: $uuid, + payload + })); +}; + +// 设置状态 +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 软件入口函数 +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/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/propertyInspector/utils/common.js b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/propertyInspector/utils/common.js new file mode 100644 index 000000000..8dda3c8fe --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/propertyInspector/utils/common.js @@ -0,0 +1,81 @@ +// 自定义事件类 +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/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/readme.md b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/readme.md new file mode 100644 index 000000000..b5c84fdc6 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/readme.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e862724d53e761e916a709132de6f5f823abb0e8149f9115745ab26250d82cc7 +size 6416 diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/CH.png b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/CH.png new file mode 100644 index 000000000..eb9607878 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/CH.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4eba213b920f94f5cc2927e6204a3bf23eec1b4458f3be14aaefaa97ed31f3b +size 5465 diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/caret.svg b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/caret.svg new file mode 100644 index 000000000..b69162a4f --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/caret.svg @@ -0,0 +1,3 @@ + + + diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/check.png b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/check.png new file mode 100644 index 000000000..ece8509f5 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/check.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5fb6fbe1417751ca7dd87552398d4585cb002654aa69632fdf6f43dbc65220c +size 234 diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/check.svg b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/check.svg new file mode 100644 index 000000000..5b96af052 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/elg_calendar.svg b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/elg_calendar.svg new file mode 100644 index 000000000..157e01bb5 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/elg_calendar.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/elg_calendar_inv.svg b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/elg_calendar_inv.svg new file mode 100644 index 000000000..4f8af68d3 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/elg_calendar_inv.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/g_d8d8d8.svg b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/g_d8d8d8.svg new file mode 100644 index 000000000..d99031408 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/g_d8d8d8.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/rcheck.svg b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/rcheck.svg new file mode 100644 index 000000000..af478ee58 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/rcheck.svg @@ -0,0 +1,3 @@ + + + diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/sdpi.css b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/sdpi.css new file mode 100644 index 000000000..26b725b1e --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/css/sdpi.css @@ -0,0 +1,230 @@ +:root{--sdpi-bgcolor:#2D2D2D;--sdpi-background:#3D3D3D;--sdpi-color:#d8d8d8;--sdpi-bordercolor:#3a3a3a;--sdpi-buttonbordercolor:#969696;--sdpi-borderradius:0px;--sdpi-width:224px;--sdpi-fontweight:600;--sdpi-letterspacing:-0.25pt} +html{--sdpi-bgcolor:#2D2D2D;--sdpi-background:#3D3D3D;--sdpi-color:#d8d8d8;--sdpi-bordercolor:#3a3a3a;--sdpi-buttonbordercolor:#969696;--sdpi-borderradius:0px;--sdpi-width:224px;--sdpi-fontweight:600;--sdpi-letterspacing:-0.25pt;height:100%;width:100%;overflow:hidden;touch-action:none;user-select:none} +html,body{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:9pt;background-color:var(--sdpi-bgcolor);color:#9a9a9a} +body{height:100%;padding:0;overflow-x:hidden;overflow-y:auto;margin:0;-webkit-overflow-scrolling:touch;-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased} +mark{background-color:var(--sdpi-bgcolor);color:var(--sdpi-color)} +hr,hr2{-webkit-margin-before:1em;-webkit-margin-after:1em;border-style:none;background:var(--sdpi-background);height:1px} +hr2,.sdpi-heading{display:flex;flex-basis:100%;align-items:center;color:inherit;font-size:9pt;margin:8px 0px} +.sdpi-heading::before,.sdpi-heading::after{content:"";flex-grow:1;background:var(--sdpi-background);height:1px;font-size:0px;line-height:0px;margin:0px 16px} +hr2{height:2px} +hr,hr2{margin-left:16px;margin-right:16px} +.sdpi-item-value,option,input,select,button{font-size:10pt;font-weight:var(--sdpi-fontweight);letter-spacing:var(--sdpi-letterspacing)} +.win .sdpi-item-value,.win option,.win input,.win select,.win button{font-size:11px;font-style:normal;letter-spacing:inherit;font-weight:100} +.win button{font-size:12px} +::-webkit-progress-value,meter::-webkit-meter-optimum-value{border-radius:2px} +::-webkit-progress-bar,meter::-webkit-meter-bar{border-radius:3px;background:var(--sdpi-background)} +::-webkit-progress-bar:active,meter::-webkit-meter-bar:active{border-radius:3px;background:#222222} +::-webkit-progress-value:active,meter::-webkit-meter-optimum-value:active{background:#99f} +progress,progress.sdpi-item-value{min-height:5px !important;height:5px;background-color:#303030} +progress{margin-top:8px !important;margin-bottom:8px !important} +.full progress,progress.full{margin-top:3px !important} +::-webkit-progress-inner-element{background-color:transparent} +.sdpi-item[type="progress"]{margin-top:4px !important;margin-bottom:12px;min-height:15px} +.sdpi-item-child.full:last-child{margin-bottom:4px} +.tabs{display:flex;border-bottom:1px solid #D7DBDD} +.tab{cursor:pointer;padding:5px 30px;color:#16a2d7;font-size:9pt;border-bottom:2px solid transparent} +.tab.is-tab-selected{border-bottom-color:#4ebbe4} +select{width:100%;-webkit-appearance:none;-moz-appearance:none;-o-appearance:none;appearance:none;background:url(caret.svg) no-repeat 97% center} +label.sdpi-file-label,input[type="button"],input[type="submit"],input[type="reset"],input[type="file"],input[type=file]::-webkit-file-upload-button,button,select{color:var(--sdpi-color);border:1pt solid #303030;font-size:8pt;background-color:var(--sdpi-background);border-radius:var(--sdpi-borderradius)} +label.sdpi-file-label,input[type="button"],input[type="submit"],input[type="reset"],input[type="file"],input[type=file]::-webkit-file-upload-button,button{border:1pt solid var(--sdpi-buttonbordercolor);border-radius:var(--sdpi-borderradius);border-color:var(--sdpi-buttonbordercolor);min-height:23px !important;height:23px !important;margin-right:8px} +input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0} +input[type="file"]{border-radius:var(--sdpi-borderradius);max-width:220px} +option{height:1.5em;padding:4px} +.sdpi-wrapper{overflow-x:hidden;height:100%} +.sdpi-item{display:flex;flex-direction:row;min-height:32px;align-items:center;margin-top:2px;max-width:344px;-webkit-user-drag:none} +.sdpi-item:first-child{margin-top:-1px} +.sdpi-item:last-child{margin-bottom:0px} +.sdpi-item>*:not(.sdpi-item-label):not(meter):not(details):not(canvas){min-height:26px;padding:0px 4px 0px 4px} +.sdpi-item>*:not(.sdpi-item-label.empty):not(meter){min-height:26px;padding:0px 4px 0px 4px} +.sdpi-item-group{padding:0 !important} +meter.sdpi-item-value{margin-left:6px} +.sdpi-item[type="group"]{display:block;margin-top:12px;margin-bottom:12px;flex-direction:unset;text-align:left} +.sdpi-item[type="group"]>.sdpi-item-label,.sdpi-item[type="group"].sdpi-item-label{width:96%;text-align:left;font-weight:700;margin-bottom:4px;padding-left:4px} +dl,ul,ol{-webkit-margin-before:0px;-webkit-margin-after:4px;-webkit-padding-start:1em;max-height:90px;overflow-y:scroll;cursor:pointer;user-select:none} +table.sdpi-item-value,dl.sdpi-item-value,ul.sdpi-item-value,ol.sdpi-item-value{-webkit-margin-before:4px;-webkit-margin-after:8px;-webkit-padding-start:1em;width:var(--sdpi-width);text-align:center} +table>caption{margin:2px} +.list,.sdpi-item[type="list"]{align-items:baseline} +.sdpi-item-label{text-align:right;flex:none;width:94px;padding-right:4px;font-weight:600} +.win .sdpi-item-label,.sdpi-item-label>small{font-weight:normal} +.sdpi-item-label:after{content:":"} +.sdpi-item-label.empty:after{content:""} +.sdpi-test,.sdpi-item-value{flex:1 0 0;margin-right:14px;margin-left:4px;justify-content:space-evenly} +canvas.sdpi-item-value{max-width:144px;max-height:144px;width:144px;height:144px;margin:0 auto;cursor:pointer} +input.sdpi-item-value{margin-left:5px} +.sdpi-item-value button,button.sdpi-item-value{margin-left:6px;margin-right:14px} +.sdpi-item-value.range{margin-left:0px} +table,dl.sdpi-item-value,ul.sdpi-item-value,ol.sdpi-item-value,.sdpi-item-value>dl,.sdpi-item-value>ul,.sdpi-item-value>ol{list-style-type:none;list-style-position:outside;margin-left:-4px;margin-right:-4px;padding:4px;border:1px solid var(--sdpi-bordercolor)} +dl.sdpi-item-value,ul.sdpi-item-value,ol.sdpi-item-value,.sdpi-item-value>ol{list-style-type:none;list-style-position:inside;margin-left:5px;margin-right:12px;padding:4px !important;display:flex;flex-direction:column} +.two-items li{display:flex} +.two-items li>*:first-child{flex:0 0 50%;text-align:left} +.two-items.thirtyseventy li>*:first-child{flex:0 0 30%} +ol.sdpi-item-value,.sdpi-item-value>ol[listtype="none"]{list-style-type:none} +ol.sdpi-item-value[type="decimal"],.sdpi-item-value>ol[type="decimal"]{list-style-type:decimal} +ol.sdpi-item-value[type="decimal-leading-zero"],.sdpi-item-value>ol[type="decimal-leading-zero"]{list-style-type:decimal-leading-zero} +ol.sdpi-item-value[type="lower-alpha"],.sdpi-item-value>ol[type="lower-alpha"]{list-style-type:lower-alpha} +ol.sdpi-item-value[type="upper-alpha"],.sdpi-item-value>ol[type="upper-alpha"]{list-style-type:upper-alpha} +ol.sdpi-item-value[type="upper-roman"],.sdpi-item-value>ol[type="upper-roman"]{list-style-type:upper-roman} +ol.sdpi-item-value[type="lower-roman"],.sdpi-item-value>ol[type="lower-roman"]{list-style-type:upper-roman} +tr:nth-child(even),.sdpi-item-value>ul>li:nth-child(even),.sdpi-item-value>ol>li:nth-child(even),li:nth-child(even){background-color:rgba(0,0,0,.2)} +td:hover,.sdpi-item-value>ul>li:hover:nth-child(even),.sdpi-item-value>ol>li:hover:nth-child(even),li:hover:nth-child(even),li:hover{background-color:rgba(255,255,255,.1)} +td.selected,td.selected:hover,li.selected:hover,li.selected{color:white;background-color:#77f} +tr{border:1px solid var(--sdpi-bordercolor)} +td{border-right:1px solid var(--sdpi-bordercolor)} +tr:last-child,td:last-child{border:none} +.sdpi-item-value.select,.sdpi-item-value>select{margin-right:13px;margin-left:4px} +.sdpi-item-child,.sdpi-item-group>.sdpi-item>input[type="color"]{margin-top:0.4em;margin-right:4px} +.full,.full *,.sdpi-item-value.full,.sdpi-item-child>full>*,.sdpi-item-child.full,.sdpi-item-child.full>*,.full>.sdpi-item-child,.full>.sdpi-item-child>*{display:flex;flex:1 1 0;margin-bottom:4px;margin-left:0px;width:100%;justify-content:space-evenly} +.sdpi-item-group>.sdpi-item>input[type="color"]{margin-top:0px} +::-webkit-calendar-picker-indicator:focus,input[type=file]::-webkit-file-upload-button:focus,button:focus,textarea:focus,input:focus,select:focus,option:focus,details:focus,summary:focus,.custom-select select{outline:none} +summary{cursor:default;padding-left:90px;padding-right:70px} +.pointer,summary .pointer{cursor:pointer} +details *{font-size:12px;font-weight:normal;word-break:break-all;} +details.message{padding:4px 18px 4px 12px} +details.message summary{min-height:18px} +details.message:first-child{margin-top:4px;margin-left:0;padding-left:102px} +details.message h1{text-align:left} +.message>summary::-webkit-details-marker{display:none} +.info20,.question,.caution,.info{background-repeat:no-repeat;background-position:72px center} +.info20{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,20 C4.4771525,20 0,15.5228475 0,10 C0,4.4771525 4.4771525,0 10,0 C15.5228475,0 20,4.4771525 20,10 C20,15.5228475 15.5228475,20 10,20 Z M10,8 C8.8954305,8 8,8.84275812 8,9.88235294 L8,16.1176471 C8,17.1572419 8.8954305,18 10,18 C11.1045695,18 12,17.1572419 12,16.1176471 L12,9.88235294 C12,8.84275812 11.1045695,8 10,8 Z M10,3 C8.8954305,3 8,3.88165465 8,4.96923077 L8,5.03076923 C8,6.11834535 8.8954305,7 10,7 C11.1045695,7 12,6.11834535 12,5.03076923 L12,4.96923077 C12,3.88165465 11.1045695,3 10,3 Z'/%3E%3C/svg%3E%0A")} +.info{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,18 C5.581722,18 2,14.418278 2,10 C2,5.581722 5.581722,2 10,2 C14.418278,2 18,5.581722 18,10 C18,14.418278 14.418278,18 10,18 Z M10,8 C9.44771525,8 9,8.42137906 9,8.94117647 L9,14.0588235 C9,14.5786209 9.44771525,15 10,15 C10.5522847,15 11,14.5786209 11,14.0588235 L11,8.94117647 C11,8.42137906 10.5522847,8 10,8 Z M10,5 C9.44771525,5 9,5.44082732 9,5.98461538 L9,6.01538462 C9,6.55917268 9.44771525,7 10,7 C10.5522847,7 11,6.55917268 11,6.01538462 L11,5.98461538 C11,5.44082732 10.5522847,5 10,5 Z'/%3E%3C/svg%3E%0A")} +.info2{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 15 15'%3E%3Cpath fill='%23999' d='M7.5,15 C3.35786438,15 0,11.6421356 0,7.5 C0,3.35786438 3.35786438,0 7.5,0 C11.6421356,0 15,3.35786438 15,7.5 C15,11.6421356 11.6421356,15 7.5,15 Z M7.5,2 C6.67157287,2 6,2.66124098 6,3.47692307 L6,3.52307693 C6,4.33875902 6.67157287,5 7.5,5 C8.32842705,5 9,4.33875902 9,3.52307693 L9,3.47692307 C9,2.66124098 8.32842705,2 7.5,2 Z M5,6 L5,7.02155172 L6,7 L6,12 L5,12.0076778 L5,13 L10,13 L10,12 L9,12.0076778 L9,6 L5,6 Z'/%3E%3C/svg%3E%0A")} +.sdpi-more-info{background-image:linear-gradient(to right,#00000000 0%,#00000040 80%),url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpolygon fill='%23999' points='4 7 8 7 8 5 12 8 8 11 8 9 4 9'/%3E%3C/svg%3E%0A")} +.caution{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' fill-rule='evenodd' d='M9.03952676,0.746646542 C9.57068894,-0.245797319 10.4285735,-0.25196227 10.9630352,0.746646542 L19.7705903,17.2030214 C20.3017525,18.1954653 19.8777595,19 18.8371387,19 L1.16542323,19 C0.118729947,19 -0.302490098,18.2016302 0.231971607,17.2030214 L9.03952676,0.746646542 Z M10,2.25584053 L1.9601405,17.3478261 L18.04099,17.3478261 L10,2.25584053 Z M10,5.9375 C10.531043,5.9375 10.9615385,6.37373537 10.9615385,6.91185897 L10.9615385,11.6923077 C10.9615385,12.2304313 10.531043,12.6666667 10,12.6666667 C9.46895697,12.6666667 9.03846154,12.2304313 9.03846154,11.6923077 L9.03846154,6.91185897 C9.03846154,6.37373537 9.46895697,5.9375 10,5.9375 Z M10,13.4583333 C10.6372516,13.4583333 11.1538462,13.9818158 11.1538462,14.6275641 L11.1538462,14.6641026 C11.1538462,15.3098509 10.6372516,15.8333333 10,15.8333333 C9.36274837,15.8333333 8.84615385,15.3098509 8.84615385,14.6641026 L8.84615385,14.6275641 C8.84615385,13.9818158 9.36274837,13.4583333 10,13.4583333 Z'/%3E%3C/svg%3E%0A")} +.question{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,18 C5.581722,18 2,14.418278 2,10 C2,5.581722 5.581722,2 10,2 C14.418278,2 18,5.581722 18,10 C18,14.418278 14.418278,18 10,18 Z M6.77783203,7.65332031 C6.77783203,7.84798274 6.85929281,8.02888914 7.0222168,8.19604492 C7.18514079,8.36320071 7.38508996,8.44677734 7.62207031,8.44677734 C8.02409055,8.44677734 8.29703704,8.20768468 8.44091797,7.72949219 C8.59326248,7.27245865 8.77945854,6.92651485 8.99951172,6.69165039 C9.2195649,6.45678594 9.56233491,6.33935547 10.027832,6.33935547 C10.4256205,6.33935547 10.7006836,6.37695313 11.0021973,6.68847656 C11.652832,7.53271484 10.942627,8.472229 10.3750916,9.1321106 C9.80755615,9.79199219 8.29492188,11.9897461 10.027832,12.1347656 C10.4498423,12.1700818 10.7027991,11.9147157 10.7832031,11.4746094 C11.0021973,9.59857178 13.1254883,8.82415771 13.1254883,7.53271484 C13.1254883,7.07568131 12.9974785,6.65250846 12.7414551,6.26318359 C12.4854317,5.87385873 12.1225609,5.56600048 11.652832,5.33959961 C11.1831031,5.11319874 10.6414419,5 10.027832,5 C9.36767248,5 8.79004154,5.13541531 8.29492187,5.40625 C7.79980221,5.67708469 7.42317837,6.01879677 7.16503906,6.43139648 C6.90689975,6.8439962 6.77783203,7.25130007 6.77783203,7.65332031 Z M10.0099668,15 C10.2713191,15 10.5016601,14.9108147 10.7009967,14.7324415 C10.9003332,14.5540682 11,14.3088087 11,13.9966555 C11,13.7157177 10.9047629,13.4793767 10.7142857,13.2876254 C10.5238086,13.0958742 10.2890379,13 10.0099668,13 C9.72646591,13 9.48726565,13.0958742 9.2923588,13.2876254 C9.09745196,13.4793767 9,13.7157177 9,13.9966555 C9,14.313268 9.10077419,14.5596424 9.30232558,14.735786 C9.50387698,14.9119295 9.73975502,15 10.0099668,15 Z'/%3E%3C/svg%3E%0A")} +.sdpi-more-info{position:fixed;left:0px;right:0px;bottom:0px;min-height:16px;padding-right:16px;text-align:right;-webkit-touch-callout:none;cursor:pointer;user-select:none;background-position:right center;background-repeat:no-repeat;border-radius:var(--sdpi-borderradius);text-decoration:none;color:var(--sdpi-color)} +.sdpi-more-info-button{display:flex;align-self:right;margin-left:auto;position:fixed;right:17px;bottom:0px;user-select:none} +.sdpi-bottom-bar{display:flex;align-self:right;margin-left:auto;position:fixed;right:17px;bottom:0px;user-select:none} +.sdpi-bottom-bar.right{right:0px} +.sdpi-bottom-bar button{min-height:20px !important;height:20px !important} +details a{background-position:right !important;min-height:24px;display:inline-block;line-height:24px;padding-right:28px} +input:not([type="range"]),textarea{-webkit-appearance:none;appearance:none;background:var(--sdpi-background);color:var(--sdpi-color);font-weight:normal;font-size:9pt;border:none;margin-top:2px;margin-bottom:2px;min-width:219px} +textarea+label{display:flex;justify-content:flex-end} +input[type="radio"],input[type="checkbox"]{display:none} +input[type="radio"]+label,input[type="checkbox"]+label{font-size:9pt;color:var(--sdpi-color);font-weight:normal;margin-right:8px} +input[type="radio"]+label:after,input[type="checkbox"]+label:after{content:" " !important} +.sdpi-item[type="radio"]>.sdpi-item-value,.sdpi-item[type="checkbox"]>.sdpi-item-value{padding-top:2px} +.sdpi-item[type="checkbox"]>.sdpi-item-value>*{margin-top:4px} +.sdpi-item[type="checkbox"] .sdpi-item-child,.sdpi-item[type="radio"] .sdpi-item-child{display:inline-block} +.sdpi-item[type="range"] .sdpi-item-value,.sdpi-item[type="meter"] .sdpi-item-child,.sdpi-item[type="progress"] .sdpi-item-child{display:flex} +.sdpi-item[type="range"] .sdpi-item-value{min-height:26px} +.sdpi-item[type="range"] .sdpi-item-value span,.sdpi-item[type="meter"] .sdpi-item-child span,.sdpi-item[type="progress"] .sdpi-item-child span{margin-top:-2px;min-width:8px;text-align:right;user-select:none;cursor:pointer;-webkit-user-select:none;user-select:none} +.sdpi-item[type="range"] .sdpi-item-value span{margin-top:7px;text-align:right} +span+input[type="range"]{display:flex;max-width:168px} +.sdpi-item[type="range"] .sdpi-item-value span:first-child,.sdpi-item[type="meter"] .sdpi-item-child span:first-child,.sdpi-item[type="progress"] .sdpi-item-child span:first-child{margin-right:4px} +.sdpi-item[type="range"] .sdpi-item-value span:last-child,.sdpi-item[type="meter"] .sdpi-item-child span:last-child,.sdpi-item[type="progress"] .sdpi-item-child span:last-child{margin-left:4px} +.reverse{transform:rotate(180deg)} +.sdpi-item[type="meter"] .sdpi-item-child meter+span:last-child{margin-left:-10px} +.sdpi-item[type="progress"] .sdpi-item-child meter+span:last-child{margin-left:-14px} +.sdpi-item[type="radio"]>.sdpi-item-value>*{margin-top:2px} +details{padding:8px 18px 8px 12px;min-width:86px} +details>h4{border-bottom:1px solid var(--sdpi-bordercolor)} +legend{display:none} +.sdpi-item-value>textarea{padding:0px;width:219px;margin-left:1px;margin-top:3px;padding:4px} +input[type="radio"]+label span,input[type="checkbox"]+label span{display:inline-block;width:16px;height:16px;margin:2px 4px 2px 0;border-radius:3px;vertical-align:middle;background:var(--sdpi-background);cursor:pointer;border:1px solid rgb(0,0,0,.2)} +input[type="radio"]+label span{border-radius:100%} +input[type="radio"]:checked+label span,input[type="checkbox"]:checked+label span{background-color:#77f;background-image:url(check.svg);background-repeat:no-repeat;background-position:center center;border:1px solid rgb(0,0,0,.4)} +input[type="radio"]:active:checked+label span,input[type="radio"]:active+label span,input[type="checkbox"]:active:checked+label span,input[type="checkbox"]:active+label span{background-color:#303030} +input[type="radio"]:checked+label span{background-image:url(rcheck.svg)} +input[type="range"]{width:var(--sdpi-width);height:30px;overflow:hidden;cursor:pointer;background:transparent !important} +.sdpi-item>input[type="range"]{margin-left:2px;max-width:var(--sdpi-width);width:var(--sdpi-width);padding:0px;margin-top:2px} +input[type="range"]::-webkit-slider-runnable-track{height:5px;background:#979797;border-radius:3px;padding:0px !important;border:1px solid var(--sdpi-background)} +input[type="range"]::-webkit-slider-thumb{position:relative;-webkit-appearance:none;background-color:var(--sdpi-color);width:12px;height:12px;border-radius:20px;margin-top:-5px;border:none} +input[type="range" i]{margin:0} +input[type="range"]::-webkit-slider-thumb::before{position:absolute;content:"";height:5px;width:500px;left:-502px;top:8px;background:#77f} +input[type="color"]{min-width:32px;min-height:32px;width:32px;height:32px;padding:0;background-color:var(--sdpi-bgcolor);flex:none} +::-webkit-color-swatch{min-width:24px} +textarea{height:3em;word-break:break-word;line-height:1.5em} +.textarea{padding:0px !important} +textarea{width:219px;height:96%;min-height:6em;resize:none;border-radius:var(--sdpi-borderradius)} +.sdpi-item.card-carousel-wrapper,.sdpi-item>.card-carousel-wrapper{padding:0} +.card-carousel-wrapper{display:flex;align-items:center;justify-content:center;margin:12px auto;color:#666a73} +.card-carousel{display:flex;justify-content:center;width:278px} +.card-carousel--overflow-container{overflow:hidden} +.card-carousel--nav__left,.card-carousel--nav__right{width:12px;height:12px;border-top:2px solid #42b883;border-right:2px solid #42b883;cursor:pointer;margin:0 4px;transition:transform 150ms linear} +.card-carousel--nav__left[disabled],.card-carousel--nav__right[disabled]{opacity:0.2;border-color:black} +.card-carousel--nav__left{transform:rotate(-135deg)} +.card-carousel--nav__left:active{transform:rotate(-135deg) scale(0.85)} +.card-carousel--nav__right{transform:rotate(45deg)} +.card-carousel--nav__right:active{transform:rotate(45deg) scale(0.85)} +.card-carousel-cards{display:flex;transition:transform 150ms ease-out;transform:translatex(0px)} +.card-carousel-cards .card-carousel--card{margin:0 5px;cursor:pointer;background-color:#fff;border-radius:4px;z-index:3} +.xxcard-carousel-cards .card-carousel--card:first-child{margin-left:0} +.xxcard-carousel-cards .card-carousel--card:last-child{margin-right:0} +.card-carousel-cards .card-carousel--card img{vertical-align:bottom;border-top-left-radius:4px;border-top-right-radius:4px;transition:opacity 150ms linear;width:60px} +.card-carousel-cards .card-carousel--card img:hover{opacity:0.5} +.card-carousel-cards .card-carousel--card--footer{border-top:0;max-width:80px;overflow:hidden;display:flex;height:100%;flex-direction:column} +.card-carousel-cards .card-carousel--card--footer p{padding:3px 0;margin:0;margin-bottom:2px;font-size:15px;font-weight:500;color:#2c3e50} +.card-carousel-cards .card-carousel--card--footer p:nth-of-type(2){font-size:12px;font-weight:300;padding:6px;color:#666a73} +h1{font-size:1.3em;font-weight:500;text-align:center;margin-bottom:12px} +::-webkit-datetime-edit{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";background:url(elg_calendar_inv.svg) no-repeat left center;padding-right:1em;padding-left:25px;background-position:4px 0px} +::-webkit-calendar-picker-indicator{background:transparent;font-size:17px} +::-webkit-calendar-picker-indicator:focus{background-color:rgba(0,0,0,0.2)} +input[type="date"]{-webkit-align-items:center;align-items:center;display:-webkit-inline-flex;font-family:monospace;overflow:hidden;padding:0;-webkit-padding-start:1px} +input::-webkit-datetime-edit{-webkit-flex:1;-webkit-user-modify:read-only !important;display:inline-block;min-width:0;overflow:hidden} +input[type="file"]{opacity:0;display:none} +.sdpi-item>input[type="file"]{opacity:1;display:flex} +input[type="file"]+span{display:flex;flex:0 1 auto;background-color:#0000ff50} +label.sdpi-file-label{cursor:pointer;user-select:none;display:inline-block;min-height:21px !important;height:21px !important;line-height:20px;padding:0px 4px;margin:auto;margin-right:0px} +.sdpi-file-label>label:active,.sdpi-file-label.file:active,label.sdpi-file-label:active,label.sdpi-file-info:active,input[type="file"]::-webkit-file-upload-button:active,button:active{background-color:var(--sdpi-color);color:#303030} +input:required:invalid,input:focus:invalid{background:var(--sdpi-background) url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5IiBoZWlnaHQ9IjkiIHZpZXdCb3g9IjAgMCA5IDkiPgogICAgPHBhdGggZmlsbD0iI0Q4RDhEOCIgZD0iTTQuNSwwIEM2Ljk4NTI4MTM3LC00LjU2NTM4NzgyZS0xNiA5LDIuMDE0NzE4NjMgOSw0LjUgQzksNi45ODUyODEzNyA2Ljk4NTI4MTM3LDkgNC41LDkgQzIuMDE0NzE4NjMsOSAzLjA0MzU5MTg4ZS0xNiw2Ljk4NTI4MTM3IDAsNC41IEMtMy4wNDM1OTE4OGUtMTYsMi4wMTQ3MTg2MyAyLjAxNDcxODYzLDQuNTY1Mzg3ODJlLTE2IDQuNSwwIFogTTQsMSBMNCw2IEw1LDYgTDUsMSBMNCwxIFogTTQuNSw4IEM0Ljc3NjE0MjM3LDggNSw3Ljc3NjE0MjM3IDUsNy41IEM1LDcuMjIzODU3NjMgNC43NzYxNDIzNyw3IDQuNSw3IEM0LjIyMzg1NzYzLDcgNCw3LjIyMzg1NzYzIDQsNy41IEM0LDcuNzc2MTQyMzcgNC4yMjM4NTc2Myw4IDQuNSw4IFoiLz4KICA8L3N2Zz4) no-repeat 98% center} +input:required:valid{background:var(--sdpi-background) url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5IiBoZWlnaHQ9IjkiIHZpZXdCb3g9IjAgMCA5IDkiPjxwb2x5Z29uIGZpbGw9IiNEOEQ4RDgiIHBvaW50cz0iNS4yIDEgNi4yIDEgNi4yIDcgMy4yIDcgMy4yIDYgNS4yIDYiIHRyYW5zZm9ybT0icm90YXRlKDQwIDQuNjc3IDQpIi8+PC9zdmc+) no-repeat 98% center} +.tooltip,:tooltip,:title{color:yellow} +.sdpi-item-group.file{width:232px;display:flex;align-items:center} +.sdpi-file-info{overflow-wrap:break-word;word-wrap:break-word;hyphens:auto;min-width:132px;max-width:144px;max-height:32px;margin-top:0px;margin-left:5px;display:inline-block;overflow:hidden;padding:6px 4px;background-color:var(--sdpi-background)} +::-webkit-scrollbar{width:8px} +::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 6px rgba(0,0,0,0.3)} +::-webkit-scrollbar-thumb{background-color:#999999;outline:1px solid slategrey;border-radius:8px} +a{color:#7397d2} +.testcontainer{display:flex;background-color:#0000ff20;max-width:400px;height:200px;align-content:space-evenly} +input[type=range]{-webkit-appearance:none;appearance:none;height:6px;margin-top:12px;z-index:0;overflow:visible} +:-webkit-slider-thumb{-webkit-appearance:none;background-color:var(--sdpi-color);width:16px;height:16px;border-radius:20px;margin-top:-6px;border:1px solid #999999} +.sdpi-item[type="range"] .sdpi-item-group{display:flex;flex-direction:column} +.xxsdpi-item[type="range"] .sdpi-item-group input{max-width:204px} +.sdpi-item[type="range"] .sdpi-item-group span{margin-left:0px !important} +.sdpi-item[type="range"] .sdpi-item-group>.sdpi-item-child{display:flex;flex-direction:row} +.rangeLabel{position:absolute;font-weight:normal;margin-top:22px} +:disabled{color:#993333} +select,select option{color:var(--sdpi-color)} +select.disabled,select option:disabled{color:#fd9494;font-style:italic} +.runningAppsContainer{display:none} +.one-line{min-height:1.5em} +.two-lines{min-height:3em} +.three-lines{min-height:4.5em} +.four-lines{min-height:6em} +.min80>.sdpi-item-child{min-width:80px} +.min100>.sdpi-item-child{min-width:100px} +.min120>.sdpi-item-child{min-width:120px} +.min140>.sdpi-item-child{min-width:140px} +.min160>.sdpi-item-child{min-width:160px} +.min200>.sdpi-item-child{min-width:200px} +.max40{flex-basis:40%;flex-grow:0} +.max30{flex-basis:30%;flex-grow:0} +.max20{flex-basis:20%;flex-grow:0} +.up20{margin-top:-20px} +.alignCenter{align-items:center} +.alignTop{align-items:flex-start} +.alignBaseline{align-items:baseline} +.noMargins,.noMargins *,.noInnerMargins *{margin:0;padding:0} +.hidden{display:none} +.icon-help,.icon-help-line,.icon-help-fill,.icon-help-inv,.icon-brighter,.icon-darker,.icon-warmer,.icon-cooler{min-width:20px;width:20px;background-repeat:no-repeat;opacity:1} +.icon-help:active,.icon-help-line:active,.icon-help-fill:active,.icon-help-inv:active,.icon-brighter:active,.icon-darker:active,.icon-warmer:active,.icon-cooler:active{opacity:0.5} +.icon-brighter,.icon-darker,.icon-warmer,.icon-cooler{margin-top:5px !important} +.icon-help,.icon-help-line,.icon-help-fill,.icon-help-inv{cursor:pointer;margin:0px;margin-left:4px} +.icon-brighter{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Ccircle cx='10' cy='10' r='4'/%3E%3Cpath d='M14.8532861,7.77530426 C14.7173255,7.4682615 14.5540843,7.17599221 14.3666368,6.90157083 L16.6782032,5.5669873 L17.1782032,6.4330127 L14.8532861,7.77530426 Z M10.5,4.5414007 C10.2777625,4.51407201 10.051423,4.5 9.82179677,4.5 C9.71377555,4.5 9.60648167,4.50311409 9.5,4.50925739 L9.5,2 L10.5,2 L10.5,4.5414007 Z M5.38028092,6.75545367 C5.18389364,7.02383457 5.01124349,7.31068015 4.86542112,7.61289977 L2.82179677,6.4330127 L3.32179677,5.5669873 L5.38028092,6.75545367 Z M4.86542112,12.3871002 C5.01124349,12.6893198 5.18389364,12.9761654 5.38028092,13.2445463 L3.32179677,14.4330127 L2.82179677,13.5669873 L4.86542112,12.3871002 Z M9.5,15.4907426 C9.60648167,15.4968859 9.71377555,15.5 9.82179677,15.5 C10.051423,15.5 10.2777625,15.485928 10.5,15.4585993 L10.5,18 L9.5,18 L9.5,15.4907426 Z M14.3666368,13.0984292 C14.5540843,12.8240078 14.7173255,12.5317385 14.8532861,12.2246957 L17.1782032,13.5669873 L16.6782032,14.4330127 L14.3666368,13.0984292 Z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-darker{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10 14C7.790861 14 6 12.209139 6 10 6 7.790861 7.790861 6 10 6 12.209139 6 14 7.790861 14 10 14 12.209139 12.209139 14 10 14zM10 13C11.6568542 13 13 11.6568542 13 10 13 8.34314575 11.6568542 7 10 7 8.34314575 7 7 8.34314575 7 10 7 11.6568542 8.34314575 13 10 13zM14.8532861 7.77530426C14.7173255 7.4682615 14.5540843 7.17599221 14.3666368 6.90157083L16.6782032 5.5669873 17.1782032 6.4330127 14.8532861 7.77530426zM10.5 4.5414007C10.2777625 4.51407201 10.051423 4.5 9.82179677 4.5 9.71377555 4.5 9.60648167 4.50311409 9.5 4.50925739L9.5 2 10.5 2 10.5 4.5414007zM5.38028092 6.75545367C5.18389364 7.02383457 5.01124349 7.31068015 4.86542112 7.61289977L2.82179677 6.4330127 3.32179677 5.5669873 5.38028092 6.75545367zM4.86542112 12.3871002C5.01124349 12.6893198 5.18389364 12.9761654 5.38028092 13.2445463L3.32179677 14.4330127 2.82179677 13.5669873 4.86542112 12.3871002zM9.5 15.4907426C9.60648167 15.4968859 9.71377555 15.5 9.82179677 15.5 10.051423 15.5 10.2777625 15.485928 10.5 15.4585993L10.5 18 9.5 18 9.5 15.4907426zM14.3666368 13.0984292C14.5540843 12.8240078 14.7173255 12.5317385 14.8532861 12.2246957L17.1782032 13.5669873 16.6782032 14.4330127 14.3666368 13.0984292z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-warmer{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M12.3247275 11.4890349C12.0406216 11.0007637 11.6761954 10.5649925 11.2495475 10.1998198 11.0890394 9.83238991 11 9.42659309 11 9 11 7.34314575 12.3431458 6 14 6 15.6568542 6 17 7.34314575 17 9 17 10.6568542 15.6568542 12 14 12 13.3795687 12 12.8031265 11.8116603 12.3247275 11.4890349zM17.6232392 11.6692284C17.8205899 11.4017892 17.9890383 11.1117186 18.123974 10.8036272L20.3121778 12.0669873 19.8121778 12.9330127 17.6232392 11.6692284zM18.123974 7.19637279C17.9890383 6.88828142 17.8205899 6.5982108 17.6232392 6.33077158L19.8121778 5.0669873 20.3121778 5.9330127 18.123974 7.19637279zM14.5 4.52746439C14.3358331 4.50931666 14.1690045 4.5 14 4.5 13.8309955 4.5 13.6641669 4.50931666 13.5 4.52746439L13.5 2 14.5 2 14.5 4.52746439zM13.5 13.4725356C13.6641669 13.4906833 13.8309955 13.5 14 13.5 14.1690045 13.5 14.3358331 13.4906833 14.5 13.4725356L14.5 16 13.5 16 13.5 13.4725356zM14 11C15.1045695 11 16 10.1045695 16 9 16 7.8954305 15.1045695 7 14 7 12.8954305 7 12 7.8954305 12 9 12 10.1045695 12.8954305 11 14 11zM9.5 11C10.6651924 11.4118364 11.5 12.5 11.5 14 11.5 16 10 17.5 8 17.5 6 17.5 4.5 16 4.5 14 4.5 12.6937812 5 11.5 6.5 11L6.5 7 9.5 7 9.5 11z'/%3E%3Cpath d='M12,14 C12,16.209139 10.209139,18 8,18 C5.790861,18 4,16.209139 4,14 C4,12.5194353 4.80439726,11.2267476 6,10.5351288 L6,4 C6,2.8954305 6.8954305,2 8,2 C9.1045695,2 10,2.8954305 10,4 L10,10.5351288 C11.1956027,11.2267476 12,12.5194353 12,14 Z M11,14 C11,12.6937812 10.1651924,11.5825421 9,11.1707057 L9,4 C9,3.44771525 8.55228475,3 8,3 C7.44771525,3 7,3.44771525 7,4 L7,11.1707057 C5.83480763,11.5825421 5,12.6937812 5,14 C5,15.6568542 6.34314575,17 8,17 C9.65685425,17 11,15.6568542 11,14 Z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-cooler{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10.4004569 11.6239517C10.0554735 10.9863849 9.57597206 10.4322632 9 9.99963381L9 9.7450467 9.53471338 9.7450467 10.8155381 8.46422201C10.7766941 8.39376637 10.7419749 8.32071759 10.7117062 8.2454012L9 8.2454012 9 6.96057868 10.6417702 6.96057868C10.6677696 6.86753378 10.7003289 6.77722682 10.7389179 6.69018783L9.44918707 5.40045694 9 5.40045694 9 4.34532219 9.32816127 4.34532219 9.34532219 2.91912025 10.4004569 2.91912025 10.4004569 4.53471338 11.6098599 5.74411634C11.7208059 5.68343597 11.8381332 5.63296451 11.9605787 5.59396526L11.9605787 3.8884898 10.8181818 2.74609294 11.5642748 2 12.5727518 3.00847706 13.5812289 2 14.3273218 2.74609294 13.2454012 3.82801356 13.2454012 5.61756719C13.3449693 5.65339299 13.4408747 5.69689391 13.5324038 5.74735625L14.7450467 4.53471338 14.7450467 2.91912025 15.8001815 2.91912025 15.8001815 4.34532219 17.2263834 4.34532219 17.2263834 5.40045694 15.6963166 5.40045694 14.4002441 6.69652946C14.437611 6.78161093 14.4692249 6.86979146 14.4945934 6.96057868L16.2570138 6.96057868 17.3994107 5.81818182 18.1455036 6.56427476 17.1370266 7.57275182 18.1455036 8.58122888 17.3994107 9.32732182 16.3174901 8.2454012 14.4246574 8.2454012C14.3952328 8.31861737 14.3616024 8.38969062 14.3240655 8.45832192L15.6107903 9.7450467 17.2263834 9.7450467 17.2263834 10.8001815 15.8001815 10.8001815 15.8001815 12.2263834 14.7450467 12.2263834 14.7450467 10.6963166 13.377994 9.32926387C13.3345872 9.34850842 13.2903677 9.36625331 13.2454012 9.38243281L13.2454012 11.3174901 14.3273218 12.3994107 13.5812289 13.1455036 12.5848864 12.1491612 11.5642748 13.1455036 10.8181818 12.3994107 11.9605787 11.2570138 11.9605787 9.40603474C11.8936938 9.38473169 11.828336 9.36000556 11.7647113 9.33206224L10.4004569 10.6963166 10.4004569 11.6239517zM12.75 8.5C13.3022847 8.5 13.75 8.05228475 13.75 7.5 13.75 6.94771525 13.3022847 6.5 12.75 6.5 12.1977153 6.5 11.75 6.94771525 11.75 7.5 11.75 8.05228475 12.1977153 8.5 12.75 8.5zM9.5 14C8.5 16.3333333 7.33333333 17.5 6 17.5 4.66666667 17.5 3.5 16.3333333 2.5 14L9.5 14z'/%3E%3Cpath d='M10,14 C10,16.209139 8.209139,18 6,18 C3.790861,18 2,16.209139 2,14 C2,12.5194353 2.80439726,11.2267476 4,10.5351288 L4,4 C4,2.8954305 4.8954305,2 6,2 C7.1045695,2 8,2.8954305 8,4 L8,10.5351288 C9.19560274,11.2267476 10,12.5194353 10,14 Z M9,14 C9,12.6937812 8.16519237,11.5825421 7,11.1707057 L7,4 C7,3.44771525 6.55228475,3 6,3 C5.44771525,3 5,3.44771525 5,4 L5,11.1707057 C3.83480763,11.5825421 3,12.6937812 3,14 C3,15.6568542 4.34314575,17 6,17 C7.65685425,17 9,15.6568542 9,14 Z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-help{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cpath fill='%23999' d='M11.292 12.516l.022 1.782H9.07v-1.804c0-1.98 1.276-2.574 2.662-3.278h-.022c.814-.44 1.65-.88 1.694-2.2.044-1.386-1.122-2.728-3.234-2.728-1.518 0-2.662.902-3.366 2.354L5 5.608C5.946 3.584 7.662 2 10.17 2c3.564 0 5.632 2.442 5.588 5.06-.066 2.618-1.716 3.41-3.102 4.158-.704.374-1.364.682-1.364 1.298zm-1.122 2.442c.858 0 1.452.594 1.452 1.452 0 .682-.594 1.408-1.452 1.408-.77 0-1.386-.726-1.386-1.408 0-.858.616-1.452 1.386-1.452z'/%3E%3C/svg%3E")} +.icon-help-line{background-image:url("data:image/svg+xml,%3Csvg width='20' height='20' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-1a9 9 0 1 0 0-18 9 9 0 0 0 0 18z'/%3E%3Cpath d='M10.848 12.307l.02 1.578H8.784v-1.597c0-1.753 1.186-2.278 2.474-2.901h-.02c.756-.39 1.533-.78 1.574-1.948.041-1.226-1.043-2.414-3.006-2.414-1.41 0-2.474.798-3.128 2.083L5 6.193C5.88 4.402 7.474 3 9.805 3 13.118 3 15.04 5.161 15 7.478c-.061 2.318-1.595 3.019-2.883 3.68-.654.332-1.268.604-1.268 1.15zM9.805 14.47c.798 0 1.35.525 1.35 1.285 0 .603-.552 1.246-1.35 1.246-.715 0-1.288-.643-1.288-1.246 0-.76.573-1.285 1.288-1.285z' fill-rule='nonzero'/%3E%3C/g%3E%3C/svg%3E")} +.icon-help-fill{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Ccircle cx='10' cy='10' r='10' fill='%23999'/%3E%3Cpath fill='%23FFF' fill-rule='nonzero' d='M8.368 7.189H5C5 3.5 7.668 2 10.292 2 13.966 2 16 4.076 16 7.012c0 3.754-3.849 3.136-3.849 5.211v1.656H8.455v-1.832c0-2.164 1.4-2.893 2.778-3.6.437-.242 1.006-.574 1.006-1.236 0-2.208-3.871-2.142-3.871-.022zM10.25 18a1.75 1.75 0 1 1 0-3.5 1.75 1.75 0 0 1 0 3.5z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-help-inv{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cpath fill='%23999' fill-rule='evenodd' d='M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zM8.368 7.189c0-2.12 3.87-2.186 3.87.022 0 .662-.568.994-1.005 1.236-1.378.707-2.778 1.436-2.778 3.6v1.832h3.696v-1.656c0-2.075 3.849-1.457 3.849-5.21C16 4.075 13.966 2 10.292 2 7.668 2 5 3.501 5 7.189h3.368zM10.25 18a1.75 1.75 0 1 0 0-3.5 1.75 1.75 0 0 0 0 3.5z'/%3E%3C/svg%3E")} +.kelvin::after{content:"K"} +.mired::after{content:" Mired"} +.percent::after{content:"%"} +.sdpi-item-value+.icon-cooler,.sdpi-item-value+.icon-warmer{margin-left:0px !important;margin-top:15px !important} +input[type="range"].colorbrightness::-webkit-slider-runnable-track,input[type="range"].colortemperature::-webkit-slider-runnable-track{height:8px;background:#979797;border-radius:4px;background-image:linear-gradient(to right,#94d0ec,#ffb165)} +input[type="range"].colorbrightness::-webkit-slider-runnable-track{background-color:#efefef;background-image:linear-gradient(to right,black,rgba(0,0,0,0))} +input[type="range"].colorbrightness::-webkit-slider-thumb,input[type="range"].colortemperature::-webkit-slider-thumb{width:16px;height:16px;border-radius:20px;margin-top:-5px;background-color:#86c6e8;box-shadow:0px 0px 1px #000000;border:1px solid #d8d8d8} +.sdpi-info-label{display:inline-block;user-select:none;position:absolute;height:15px;width:auto;text-align:center;border-radius:4px;min-width:44px;max-width:80px;background:white;font-size:11px;color:black;z-index:1000;box-shadow:0px 0px 12px rgba(0,0,0,.8);padding:2px} +.sdpi-info-label.hidden{opacity:0;transition:opacity 0.25s linear} +.sdpi-info-label.shown{position:absolute;opacity:1;transition:opacity 0.25s ease-out} \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/default.jpg b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/default.jpg new file mode 100644 index 000000000..8061f9cfe --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/static/default.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb5c4d9b90e10680cb8f13a6c12ad0f426601fd2ee4130494fa06b6e6bb1677c +size 16885 diff --git a/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/zh_CN.json b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/zh_CN.json new file mode 100644 index 000000000..104618f56 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDJavaScriptSDK/com.mirabox.streamdock.xxx.sdPlugin/zh_CN.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ddbfde60d0dfe2e9f9c4f9c2d09cc2fd0595dff4e750dd022e199abe0a49732d +size 227 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/manifest.json b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/manifest.json new file mode 100644 index 000000000..ef455a1f6 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/manifest.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c81a18e0a41fd7695e99992f63e2941ddea7e9cd585e2fdb93d4a5583d12c69 +size 1104 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/package.json b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/package.json new file mode 100644 index 000000000..239cbfede --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/package.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae2850b8dcf1dcc9c3053cce726dd5031d4a499021b7a9e6ad40c558ed3c9925 +size 653 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/plugin/app.exe b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/plugin/app.exe new file mode 100644 index 000000000..eec58f080 Binary files /dev/null and b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/plugin/app.exe differ diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/plugin/autofile.js b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/plugin/autofile.js new file mode 100644 index 000000000..340d29540 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/plugin/autofile.js @@ -0,0 +1,46 @@ +const path = require('path'); +const fs = require('fs-extra'); + +console.log('开始执行自动化构建...'); + +const currentDir = __dirname; + +// 获取父文件夹的路径 +const parentDir = path.join(currentDir, '..'); +// 获取父文件夹的名称 +const PluginName = path.basename(parentDir); + + +const PluginPath = path.join(process.env.APPDATA, 'HotSpot/StreamDock/plugins', PluginName); + +try { + // 删除旧的插件目录 + fs.removeSync(PluginPath); + + // 确保目标目录存在 + fs.ensureDirSync(path.dirname(PluginPath)); + + // 复制当前目录到目标路径,排除 node_modules + fs.copySync(path.resolve(__dirname, '..'), PluginPath, { + filter: (src) => { + const relativePath = path.relative(path.resolve(__dirname, '..'), src); + // 排除 'node_modules' 和 '.git' 目录及其子文件 + return !relativePath.startsWith('plugin\\node_modules') + &&!relativePath.startsWith('plugin\\index.js') + &&!relativePath.startsWith('plugin\\package.json') + &&!relativePath.startsWith('plugin\\package-lock.json') + &&!relativePath.startsWith('plugin\\yarn.lock') + &&!relativePath.startsWith('plugin\\build') + &&!relativePath.startsWith('plugin\\log') + &&!relativePath.startsWith('.git') + &&!relativePath.startsWith('.vscode'); + } + }); + + fs.copySync( path.join(__dirname, "build"), path.join(PluginPath,'plugin')) + + console.log(`插件 "${PluginName}" 已成功复制到 "${PluginPath}"`); + console.log('构建成功-------------'); +} catch (err) { + console.error(`复制出错 "${PluginName}":`, err); +} \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/plugin/index.js b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/plugin/index.js new file mode 100644 index 000000000..4ca6043e4 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/plugin/index.js @@ -0,0 +1,19 @@ +const { Plugins, Actions, log } = require('./utils/plugin'); + +const plugin = new Plugins(); + +// 操作一 +plugin.action1 = new Actions({ + default: {}, + _willAppear({ context }) { + plugin.setTitle(context, "Hello world!"); + }, + _willDisappear(data) { }, + _propertyInspectorDidAppear(data) { }, + dialRotate(data) {//旋钮旋转 + log.info(data); + }, + dialDown(data) {//旋钮按下 + log.info(data); + } +}); \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/plugin/package.json b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/plugin/package.json new file mode 100644 index 000000000..3cd6b89f6 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/plugin/package.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa8df7d0d76e6c5f57e359387e760eed78baedb0af1def63c4b62ef17b3bb9a5 +size 344 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/plugin/utils/plugin.js b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/plugin/utils/plugin.js new file mode 100644 index 000000000..6c1b67056 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/plugin/utils/plugin.js @@ -0,0 +1,139 @@ +// 配置日志文件 +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[5]; + constructor() { + if (Plugins.instance) { + return Plugins.instance; + } + this.ws = new ws("ws://127.0.0.1:" + process.argv[2]); + this.ws.on('open', () => this.ws.send(JSON.stringify({ uuid: process.argv[3], event: process.argv[4] }))); + this.ws.on('close', process.exit); + this.ws.on('message', e => { + const data = JSON.parse(e.toString()); + const action = data.action?.split('.').pop(); + this[action]?.[data.event]?.(data); + this[data.event]?.(data); + }); + Plugins.instance = this; + } + // 设置标题 + setTitle(context, str, row = 0, num = 6) { + 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.ws.send(JSON.stringify({ + event: "setTitle", + context, payload: { + target: 0, + title: newStr || str + } + })); + } + // 设置背景 + setImage(context, url) { + const image = new Image(); + image.src = url; image.onload = () => { + const canvas = document.createElement("canvas"); + canvas.width = image.naturalWidth; + canvas.height = image.naturalHeight; + const ctx = canvas.getContext("2d"); + ctx.drawImage(image, 0, 0); + this.ws.send(JSON.stringify({ + event: "setImage", + context, payload: { + target: 0, + image: canvas.toDataURL("image/png") + } + })); + }; + } + // 设置状态 + 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 + })); + } + // 发送给属性检测器 + 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; + propertyInspectorDidAppear(data) { + Actions.currentAction = data.action; + Actions.currentContext = data.context; + this._propertyInspectorDidAppear?.(data); + } + // 初始化数据 + willAppear(data) { + const { context, payload: { settings } } = data; + this.data[context] = Object.assign({ ...this.default }, settings); + this._willAppear?.(data); + } + // 行动销毁 + willDisappear(data) { + this._willDisappear?.(data); + delete this.data[data.context]; + } +} + +module.exports = { + log, + Plugins, + Actions, +}; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/action1/index.html b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/action1/index.html new file mode 100644 index 000000000..1a2cb39d2 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/action1/index.html @@ -0,0 +1,19 @@ + + + + + + 模板 - 属性检查器 + + + + + + + + + + + \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/action1/index.js b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/action1/index.js new file mode 100644 index 000000000..ff58dd017 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/action1/index.js @@ -0,0 +1,14 @@ +/// +/// + +// $local 是否国际化 +// $back 是否自行决定回显时机 +// $dom 获取文档元素 - 不是动态的都写在这里面 +const $local = false, $back = false, $dom = { + main: $('.sdpi-wrapper') +}; + +const $propEvent = { + didReceiveSettings(data) { }, + sendToPropertyInspector(data) { } +}; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/action.js b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/action.js new file mode 100644 index 000000000..76798ecba --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/action.js @@ -0,0 +1,131 @@ +/** + * PropertyInspector 2.5.0 新特性 => + * + * 1 => 工具与主文件相分离 - 按需引入 + * 2 => $settings - 全局持久化数据代理 ※ + * 3 => 无需关注上下文 - 随时随地与插件通信 + * 4 => 注意事项: 为了避免命名冲突,请勿使用 $ 相关的名称以及JQuery库 + * + * ===== CJHONG ========================================== 2023.10.10 =====> + */ + +let $websocket, $uuid, $action, $context, $settings, $lang, $FileID = ''; + +// 与插件通信 +WebSocket.prototype.sendToPlugin = function (payload) { + this.send(JSON.stringify({ + event: "sendToPlugin", + action: $action, + context: $uuid, + payload + })); +}; + +// 设置状态 +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 软件入口函数 +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/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/common.js b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/common.js new file mode 100644 index 000000000..8dda3c8fe --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/common.js @@ -0,0 +1,81 @@ +// 自定义事件类 +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/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/readme.md b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/readme.md new file mode 100644 index 000000000..b5c84fdc6 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/readme.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e862724d53e761e916a709132de6f5f823abb0e8149f9115745ab26250d82cc7 +size 6416 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/CH.png b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/CH.png new file mode 100644 index 000000000..eb9607878 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/CH.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4eba213b920f94f5cc2927e6204a3bf23eec1b4458f3be14aaefaa97ed31f3b +size 5465 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/caret.svg b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/caret.svg new file mode 100644 index 000000000..b69162a4f --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/caret.svg @@ -0,0 +1,3 @@ + + + diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/check.png b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/check.png new file mode 100644 index 000000000..ece8509f5 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/check.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5fb6fbe1417751ca7dd87552398d4585cb002654aa69632fdf6f43dbc65220c +size 234 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/check.svg b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/check.svg new file mode 100644 index 000000000..5b96af052 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/elg_calendar.svg b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/elg_calendar.svg new file mode 100644 index 000000000..157e01bb5 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/elg_calendar.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/elg_calendar_inv.svg b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/elg_calendar_inv.svg new file mode 100644 index 000000000..4f8af68d3 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/elg_calendar_inv.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/g_d8d8d8.svg b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/g_d8d8d8.svg new file mode 100644 index 000000000..d99031408 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/g_d8d8d8.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/rcheck.svg b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/rcheck.svg new file mode 100644 index 000000000..af478ee58 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/rcheck.svg @@ -0,0 +1,3 @@ + + + diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/sdpi.css b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/sdpi.css new file mode 100644 index 000000000..26b725b1e --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/css/sdpi.css @@ -0,0 +1,230 @@ +:root{--sdpi-bgcolor:#2D2D2D;--sdpi-background:#3D3D3D;--sdpi-color:#d8d8d8;--sdpi-bordercolor:#3a3a3a;--sdpi-buttonbordercolor:#969696;--sdpi-borderradius:0px;--sdpi-width:224px;--sdpi-fontweight:600;--sdpi-letterspacing:-0.25pt} +html{--sdpi-bgcolor:#2D2D2D;--sdpi-background:#3D3D3D;--sdpi-color:#d8d8d8;--sdpi-bordercolor:#3a3a3a;--sdpi-buttonbordercolor:#969696;--sdpi-borderradius:0px;--sdpi-width:224px;--sdpi-fontweight:600;--sdpi-letterspacing:-0.25pt;height:100%;width:100%;overflow:hidden;touch-action:none;user-select:none} +html,body{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:9pt;background-color:var(--sdpi-bgcolor);color:#9a9a9a} +body{height:100%;padding:0;overflow-x:hidden;overflow-y:auto;margin:0;-webkit-overflow-scrolling:touch;-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased} +mark{background-color:var(--sdpi-bgcolor);color:var(--sdpi-color)} +hr,hr2{-webkit-margin-before:1em;-webkit-margin-after:1em;border-style:none;background:var(--sdpi-background);height:1px} +hr2,.sdpi-heading{display:flex;flex-basis:100%;align-items:center;color:inherit;font-size:9pt;margin:8px 0px} +.sdpi-heading::before,.sdpi-heading::after{content:"";flex-grow:1;background:var(--sdpi-background);height:1px;font-size:0px;line-height:0px;margin:0px 16px} +hr2{height:2px} +hr,hr2{margin-left:16px;margin-right:16px} +.sdpi-item-value,option,input,select,button{font-size:10pt;font-weight:var(--sdpi-fontweight);letter-spacing:var(--sdpi-letterspacing)} +.win .sdpi-item-value,.win option,.win input,.win select,.win button{font-size:11px;font-style:normal;letter-spacing:inherit;font-weight:100} +.win button{font-size:12px} +::-webkit-progress-value,meter::-webkit-meter-optimum-value{border-radius:2px} +::-webkit-progress-bar,meter::-webkit-meter-bar{border-radius:3px;background:var(--sdpi-background)} +::-webkit-progress-bar:active,meter::-webkit-meter-bar:active{border-radius:3px;background:#222222} +::-webkit-progress-value:active,meter::-webkit-meter-optimum-value:active{background:#99f} +progress,progress.sdpi-item-value{min-height:5px !important;height:5px;background-color:#303030} +progress{margin-top:8px !important;margin-bottom:8px !important} +.full progress,progress.full{margin-top:3px !important} +::-webkit-progress-inner-element{background-color:transparent} +.sdpi-item[type="progress"]{margin-top:4px !important;margin-bottom:12px;min-height:15px} +.sdpi-item-child.full:last-child{margin-bottom:4px} +.tabs{display:flex;border-bottom:1px solid #D7DBDD} +.tab{cursor:pointer;padding:5px 30px;color:#16a2d7;font-size:9pt;border-bottom:2px solid transparent} +.tab.is-tab-selected{border-bottom-color:#4ebbe4} +select{width:100%;-webkit-appearance:none;-moz-appearance:none;-o-appearance:none;appearance:none;background:url(caret.svg) no-repeat 97% center} +label.sdpi-file-label,input[type="button"],input[type="submit"],input[type="reset"],input[type="file"],input[type=file]::-webkit-file-upload-button,button,select{color:var(--sdpi-color);border:1pt solid #303030;font-size:8pt;background-color:var(--sdpi-background);border-radius:var(--sdpi-borderradius)} +label.sdpi-file-label,input[type="button"],input[type="submit"],input[type="reset"],input[type="file"],input[type=file]::-webkit-file-upload-button,button{border:1pt solid var(--sdpi-buttonbordercolor);border-radius:var(--sdpi-borderradius);border-color:var(--sdpi-buttonbordercolor);min-height:23px !important;height:23px !important;margin-right:8px} +input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0} +input[type="file"]{border-radius:var(--sdpi-borderradius);max-width:220px} +option{height:1.5em;padding:4px} +.sdpi-wrapper{overflow-x:hidden;height:100%} +.sdpi-item{display:flex;flex-direction:row;min-height:32px;align-items:center;margin-top:2px;max-width:344px;-webkit-user-drag:none} +.sdpi-item:first-child{margin-top:-1px} +.sdpi-item:last-child{margin-bottom:0px} +.sdpi-item>*:not(.sdpi-item-label):not(meter):not(details):not(canvas){min-height:26px;padding:0px 4px 0px 4px} +.sdpi-item>*:not(.sdpi-item-label.empty):not(meter){min-height:26px;padding:0px 4px 0px 4px} +.sdpi-item-group{padding:0 !important} +meter.sdpi-item-value{margin-left:6px} +.sdpi-item[type="group"]{display:block;margin-top:12px;margin-bottom:12px;flex-direction:unset;text-align:left} +.sdpi-item[type="group"]>.sdpi-item-label,.sdpi-item[type="group"].sdpi-item-label{width:96%;text-align:left;font-weight:700;margin-bottom:4px;padding-left:4px} +dl,ul,ol{-webkit-margin-before:0px;-webkit-margin-after:4px;-webkit-padding-start:1em;max-height:90px;overflow-y:scroll;cursor:pointer;user-select:none} +table.sdpi-item-value,dl.sdpi-item-value,ul.sdpi-item-value,ol.sdpi-item-value{-webkit-margin-before:4px;-webkit-margin-after:8px;-webkit-padding-start:1em;width:var(--sdpi-width);text-align:center} +table>caption{margin:2px} +.list,.sdpi-item[type="list"]{align-items:baseline} +.sdpi-item-label{text-align:right;flex:none;width:94px;padding-right:4px;font-weight:600} +.win .sdpi-item-label,.sdpi-item-label>small{font-weight:normal} +.sdpi-item-label:after{content:":"} +.sdpi-item-label.empty:after{content:""} +.sdpi-test,.sdpi-item-value{flex:1 0 0;margin-right:14px;margin-left:4px;justify-content:space-evenly} +canvas.sdpi-item-value{max-width:144px;max-height:144px;width:144px;height:144px;margin:0 auto;cursor:pointer} +input.sdpi-item-value{margin-left:5px} +.sdpi-item-value button,button.sdpi-item-value{margin-left:6px;margin-right:14px} +.sdpi-item-value.range{margin-left:0px} +table,dl.sdpi-item-value,ul.sdpi-item-value,ol.sdpi-item-value,.sdpi-item-value>dl,.sdpi-item-value>ul,.sdpi-item-value>ol{list-style-type:none;list-style-position:outside;margin-left:-4px;margin-right:-4px;padding:4px;border:1px solid var(--sdpi-bordercolor)} +dl.sdpi-item-value,ul.sdpi-item-value,ol.sdpi-item-value,.sdpi-item-value>ol{list-style-type:none;list-style-position:inside;margin-left:5px;margin-right:12px;padding:4px !important;display:flex;flex-direction:column} +.two-items li{display:flex} +.two-items li>*:first-child{flex:0 0 50%;text-align:left} +.two-items.thirtyseventy li>*:first-child{flex:0 0 30%} +ol.sdpi-item-value,.sdpi-item-value>ol[listtype="none"]{list-style-type:none} +ol.sdpi-item-value[type="decimal"],.sdpi-item-value>ol[type="decimal"]{list-style-type:decimal} +ol.sdpi-item-value[type="decimal-leading-zero"],.sdpi-item-value>ol[type="decimal-leading-zero"]{list-style-type:decimal-leading-zero} +ol.sdpi-item-value[type="lower-alpha"],.sdpi-item-value>ol[type="lower-alpha"]{list-style-type:lower-alpha} +ol.sdpi-item-value[type="upper-alpha"],.sdpi-item-value>ol[type="upper-alpha"]{list-style-type:upper-alpha} +ol.sdpi-item-value[type="upper-roman"],.sdpi-item-value>ol[type="upper-roman"]{list-style-type:upper-roman} +ol.sdpi-item-value[type="lower-roman"],.sdpi-item-value>ol[type="lower-roman"]{list-style-type:upper-roman} +tr:nth-child(even),.sdpi-item-value>ul>li:nth-child(even),.sdpi-item-value>ol>li:nth-child(even),li:nth-child(even){background-color:rgba(0,0,0,.2)} +td:hover,.sdpi-item-value>ul>li:hover:nth-child(even),.sdpi-item-value>ol>li:hover:nth-child(even),li:hover:nth-child(even),li:hover{background-color:rgba(255,255,255,.1)} +td.selected,td.selected:hover,li.selected:hover,li.selected{color:white;background-color:#77f} +tr{border:1px solid var(--sdpi-bordercolor)} +td{border-right:1px solid var(--sdpi-bordercolor)} +tr:last-child,td:last-child{border:none} +.sdpi-item-value.select,.sdpi-item-value>select{margin-right:13px;margin-left:4px} +.sdpi-item-child,.sdpi-item-group>.sdpi-item>input[type="color"]{margin-top:0.4em;margin-right:4px} +.full,.full *,.sdpi-item-value.full,.sdpi-item-child>full>*,.sdpi-item-child.full,.sdpi-item-child.full>*,.full>.sdpi-item-child,.full>.sdpi-item-child>*{display:flex;flex:1 1 0;margin-bottom:4px;margin-left:0px;width:100%;justify-content:space-evenly} +.sdpi-item-group>.sdpi-item>input[type="color"]{margin-top:0px} +::-webkit-calendar-picker-indicator:focus,input[type=file]::-webkit-file-upload-button:focus,button:focus,textarea:focus,input:focus,select:focus,option:focus,details:focus,summary:focus,.custom-select select{outline:none} +summary{cursor:default;padding-left:90px;padding-right:70px} +.pointer,summary .pointer{cursor:pointer} +details *{font-size:12px;font-weight:normal;word-break:break-all;} +details.message{padding:4px 18px 4px 12px} +details.message summary{min-height:18px} +details.message:first-child{margin-top:4px;margin-left:0;padding-left:102px} +details.message h1{text-align:left} +.message>summary::-webkit-details-marker{display:none} +.info20,.question,.caution,.info{background-repeat:no-repeat;background-position:72px center} +.info20{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,20 C4.4771525,20 0,15.5228475 0,10 C0,4.4771525 4.4771525,0 10,0 C15.5228475,0 20,4.4771525 20,10 C20,15.5228475 15.5228475,20 10,20 Z M10,8 C8.8954305,8 8,8.84275812 8,9.88235294 L8,16.1176471 C8,17.1572419 8.8954305,18 10,18 C11.1045695,18 12,17.1572419 12,16.1176471 L12,9.88235294 C12,8.84275812 11.1045695,8 10,8 Z M10,3 C8.8954305,3 8,3.88165465 8,4.96923077 L8,5.03076923 C8,6.11834535 8.8954305,7 10,7 C11.1045695,7 12,6.11834535 12,5.03076923 L12,4.96923077 C12,3.88165465 11.1045695,3 10,3 Z'/%3E%3C/svg%3E%0A")} +.info{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,18 C5.581722,18 2,14.418278 2,10 C2,5.581722 5.581722,2 10,2 C14.418278,2 18,5.581722 18,10 C18,14.418278 14.418278,18 10,18 Z M10,8 C9.44771525,8 9,8.42137906 9,8.94117647 L9,14.0588235 C9,14.5786209 9.44771525,15 10,15 C10.5522847,15 11,14.5786209 11,14.0588235 L11,8.94117647 C11,8.42137906 10.5522847,8 10,8 Z M10,5 C9.44771525,5 9,5.44082732 9,5.98461538 L9,6.01538462 C9,6.55917268 9.44771525,7 10,7 C10.5522847,7 11,6.55917268 11,6.01538462 L11,5.98461538 C11,5.44082732 10.5522847,5 10,5 Z'/%3E%3C/svg%3E%0A")} +.info2{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 15 15'%3E%3Cpath fill='%23999' d='M7.5,15 C3.35786438,15 0,11.6421356 0,7.5 C0,3.35786438 3.35786438,0 7.5,0 C11.6421356,0 15,3.35786438 15,7.5 C15,11.6421356 11.6421356,15 7.5,15 Z M7.5,2 C6.67157287,2 6,2.66124098 6,3.47692307 L6,3.52307693 C6,4.33875902 6.67157287,5 7.5,5 C8.32842705,5 9,4.33875902 9,3.52307693 L9,3.47692307 C9,2.66124098 8.32842705,2 7.5,2 Z M5,6 L5,7.02155172 L6,7 L6,12 L5,12.0076778 L5,13 L10,13 L10,12 L9,12.0076778 L9,6 L5,6 Z'/%3E%3C/svg%3E%0A")} +.sdpi-more-info{background-image:linear-gradient(to right,#00000000 0%,#00000040 80%),url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpolygon fill='%23999' points='4 7 8 7 8 5 12 8 8 11 8 9 4 9'/%3E%3C/svg%3E%0A")} +.caution{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' fill-rule='evenodd' d='M9.03952676,0.746646542 C9.57068894,-0.245797319 10.4285735,-0.25196227 10.9630352,0.746646542 L19.7705903,17.2030214 C20.3017525,18.1954653 19.8777595,19 18.8371387,19 L1.16542323,19 C0.118729947,19 -0.302490098,18.2016302 0.231971607,17.2030214 L9.03952676,0.746646542 Z M10,2.25584053 L1.9601405,17.3478261 L18.04099,17.3478261 L10,2.25584053 Z M10,5.9375 C10.531043,5.9375 10.9615385,6.37373537 10.9615385,6.91185897 L10.9615385,11.6923077 C10.9615385,12.2304313 10.531043,12.6666667 10,12.6666667 C9.46895697,12.6666667 9.03846154,12.2304313 9.03846154,11.6923077 L9.03846154,6.91185897 C9.03846154,6.37373537 9.46895697,5.9375 10,5.9375 Z M10,13.4583333 C10.6372516,13.4583333 11.1538462,13.9818158 11.1538462,14.6275641 L11.1538462,14.6641026 C11.1538462,15.3098509 10.6372516,15.8333333 10,15.8333333 C9.36274837,15.8333333 8.84615385,15.3098509 8.84615385,14.6641026 L8.84615385,14.6275641 C8.84615385,13.9818158 9.36274837,13.4583333 10,13.4583333 Z'/%3E%3C/svg%3E%0A")} +.question{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,18 C5.581722,18 2,14.418278 2,10 C2,5.581722 5.581722,2 10,2 C14.418278,2 18,5.581722 18,10 C18,14.418278 14.418278,18 10,18 Z M6.77783203,7.65332031 C6.77783203,7.84798274 6.85929281,8.02888914 7.0222168,8.19604492 C7.18514079,8.36320071 7.38508996,8.44677734 7.62207031,8.44677734 C8.02409055,8.44677734 8.29703704,8.20768468 8.44091797,7.72949219 C8.59326248,7.27245865 8.77945854,6.92651485 8.99951172,6.69165039 C9.2195649,6.45678594 9.56233491,6.33935547 10.027832,6.33935547 C10.4256205,6.33935547 10.7006836,6.37695313 11.0021973,6.68847656 C11.652832,7.53271484 10.942627,8.472229 10.3750916,9.1321106 C9.80755615,9.79199219 8.29492188,11.9897461 10.027832,12.1347656 C10.4498423,12.1700818 10.7027991,11.9147157 10.7832031,11.4746094 C11.0021973,9.59857178 13.1254883,8.82415771 13.1254883,7.53271484 C13.1254883,7.07568131 12.9974785,6.65250846 12.7414551,6.26318359 C12.4854317,5.87385873 12.1225609,5.56600048 11.652832,5.33959961 C11.1831031,5.11319874 10.6414419,5 10.027832,5 C9.36767248,5 8.79004154,5.13541531 8.29492187,5.40625 C7.79980221,5.67708469 7.42317837,6.01879677 7.16503906,6.43139648 C6.90689975,6.8439962 6.77783203,7.25130007 6.77783203,7.65332031 Z M10.0099668,15 C10.2713191,15 10.5016601,14.9108147 10.7009967,14.7324415 C10.9003332,14.5540682 11,14.3088087 11,13.9966555 C11,13.7157177 10.9047629,13.4793767 10.7142857,13.2876254 C10.5238086,13.0958742 10.2890379,13 10.0099668,13 C9.72646591,13 9.48726565,13.0958742 9.2923588,13.2876254 C9.09745196,13.4793767 9,13.7157177 9,13.9966555 C9,14.313268 9.10077419,14.5596424 9.30232558,14.735786 C9.50387698,14.9119295 9.73975502,15 10.0099668,15 Z'/%3E%3C/svg%3E%0A")} +.sdpi-more-info{position:fixed;left:0px;right:0px;bottom:0px;min-height:16px;padding-right:16px;text-align:right;-webkit-touch-callout:none;cursor:pointer;user-select:none;background-position:right center;background-repeat:no-repeat;border-radius:var(--sdpi-borderradius);text-decoration:none;color:var(--sdpi-color)} +.sdpi-more-info-button{display:flex;align-self:right;margin-left:auto;position:fixed;right:17px;bottom:0px;user-select:none} +.sdpi-bottom-bar{display:flex;align-self:right;margin-left:auto;position:fixed;right:17px;bottom:0px;user-select:none} +.sdpi-bottom-bar.right{right:0px} +.sdpi-bottom-bar button{min-height:20px !important;height:20px !important} +details a{background-position:right !important;min-height:24px;display:inline-block;line-height:24px;padding-right:28px} +input:not([type="range"]),textarea{-webkit-appearance:none;appearance:none;background:var(--sdpi-background);color:var(--sdpi-color);font-weight:normal;font-size:9pt;border:none;margin-top:2px;margin-bottom:2px;min-width:219px} +textarea+label{display:flex;justify-content:flex-end} +input[type="radio"],input[type="checkbox"]{display:none} +input[type="radio"]+label,input[type="checkbox"]+label{font-size:9pt;color:var(--sdpi-color);font-weight:normal;margin-right:8px} +input[type="radio"]+label:after,input[type="checkbox"]+label:after{content:" " !important} +.sdpi-item[type="radio"]>.sdpi-item-value,.sdpi-item[type="checkbox"]>.sdpi-item-value{padding-top:2px} +.sdpi-item[type="checkbox"]>.sdpi-item-value>*{margin-top:4px} +.sdpi-item[type="checkbox"] .sdpi-item-child,.sdpi-item[type="radio"] .sdpi-item-child{display:inline-block} +.sdpi-item[type="range"] .sdpi-item-value,.sdpi-item[type="meter"] .sdpi-item-child,.sdpi-item[type="progress"] .sdpi-item-child{display:flex} +.sdpi-item[type="range"] .sdpi-item-value{min-height:26px} +.sdpi-item[type="range"] .sdpi-item-value span,.sdpi-item[type="meter"] .sdpi-item-child span,.sdpi-item[type="progress"] .sdpi-item-child span{margin-top:-2px;min-width:8px;text-align:right;user-select:none;cursor:pointer;-webkit-user-select:none;user-select:none} +.sdpi-item[type="range"] .sdpi-item-value span{margin-top:7px;text-align:right} +span+input[type="range"]{display:flex;max-width:168px} +.sdpi-item[type="range"] .sdpi-item-value span:first-child,.sdpi-item[type="meter"] .sdpi-item-child span:first-child,.sdpi-item[type="progress"] .sdpi-item-child span:first-child{margin-right:4px} +.sdpi-item[type="range"] .sdpi-item-value span:last-child,.sdpi-item[type="meter"] .sdpi-item-child span:last-child,.sdpi-item[type="progress"] .sdpi-item-child span:last-child{margin-left:4px} +.reverse{transform:rotate(180deg)} +.sdpi-item[type="meter"] .sdpi-item-child meter+span:last-child{margin-left:-10px} +.sdpi-item[type="progress"] .sdpi-item-child meter+span:last-child{margin-left:-14px} +.sdpi-item[type="radio"]>.sdpi-item-value>*{margin-top:2px} +details{padding:8px 18px 8px 12px;min-width:86px} +details>h4{border-bottom:1px solid var(--sdpi-bordercolor)} +legend{display:none} +.sdpi-item-value>textarea{padding:0px;width:219px;margin-left:1px;margin-top:3px;padding:4px} +input[type="radio"]+label span,input[type="checkbox"]+label span{display:inline-block;width:16px;height:16px;margin:2px 4px 2px 0;border-radius:3px;vertical-align:middle;background:var(--sdpi-background);cursor:pointer;border:1px solid rgb(0,0,0,.2)} +input[type="radio"]+label span{border-radius:100%} +input[type="radio"]:checked+label span,input[type="checkbox"]:checked+label span{background-color:#77f;background-image:url(check.svg);background-repeat:no-repeat;background-position:center center;border:1px solid rgb(0,0,0,.4)} +input[type="radio"]:active:checked+label span,input[type="radio"]:active+label span,input[type="checkbox"]:active:checked+label span,input[type="checkbox"]:active+label span{background-color:#303030} +input[type="radio"]:checked+label span{background-image:url(rcheck.svg)} +input[type="range"]{width:var(--sdpi-width);height:30px;overflow:hidden;cursor:pointer;background:transparent !important} +.sdpi-item>input[type="range"]{margin-left:2px;max-width:var(--sdpi-width);width:var(--sdpi-width);padding:0px;margin-top:2px} +input[type="range"]::-webkit-slider-runnable-track{height:5px;background:#979797;border-radius:3px;padding:0px !important;border:1px solid var(--sdpi-background)} +input[type="range"]::-webkit-slider-thumb{position:relative;-webkit-appearance:none;background-color:var(--sdpi-color);width:12px;height:12px;border-radius:20px;margin-top:-5px;border:none} +input[type="range" i]{margin:0} +input[type="range"]::-webkit-slider-thumb::before{position:absolute;content:"";height:5px;width:500px;left:-502px;top:8px;background:#77f} +input[type="color"]{min-width:32px;min-height:32px;width:32px;height:32px;padding:0;background-color:var(--sdpi-bgcolor);flex:none} +::-webkit-color-swatch{min-width:24px} +textarea{height:3em;word-break:break-word;line-height:1.5em} +.textarea{padding:0px !important} +textarea{width:219px;height:96%;min-height:6em;resize:none;border-radius:var(--sdpi-borderradius)} +.sdpi-item.card-carousel-wrapper,.sdpi-item>.card-carousel-wrapper{padding:0} +.card-carousel-wrapper{display:flex;align-items:center;justify-content:center;margin:12px auto;color:#666a73} +.card-carousel{display:flex;justify-content:center;width:278px} +.card-carousel--overflow-container{overflow:hidden} +.card-carousel--nav__left,.card-carousel--nav__right{width:12px;height:12px;border-top:2px solid #42b883;border-right:2px solid #42b883;cursor:pointer;margin:0 4px;transition:transform 150ms linear} +.card-carousel--nav__left[disabled],.card-carousel--nav__right[disabled]{opacity:0.2;border-color:black} +.card-carousel--nav__left{transform:rotate(-135deg)} +.card-carousel--nav__left:active{transform:rotate(-135deg) scale(0.85)} +.card-carousel--nav__right{transform:rotate(45deg)} +.card-carousel--nav__right:active{transform:rotate(45deg) scale(0.85)} +.card-carousel-cards{display:flex;transition:transform 150ms ease-out;transform:translatex(0px)} +.card-carousel-cards .card-carousel--card{margin:0 5px;cursor:pointer;background-color:#fff;border-radius:4px;z-index:3} +.xxcard-carousel-cards .card-carousel--card:first-child{margin-left:0} +.xxcard-carousel-cards .card-carousel--card:last-child{margin-right:0} +.card-carousel-cards .card-carousel--card img{vertical-align:bottom;border-top-left-radius:4px;border-top-right-radius:4px;transition:opacity 150ms linear;width:60px} +.card-carousel-cards .card-carousel--card img:hover{opacity:0.5} +.card-carousel-cards .card-carousel--card--footer{border-top:0;max-width:80px;overflow:hidden;display:flex;height:100%;flex-direction:column} +.card-carousel-cards .card-carousel--card--footer p{padding:3px 0;margin:0;margin-bottom:2px;font-size:15px;font-weight:500;color:#2c3e50} +.card-carousel-cards .card-carousel--card--footer p:nth-of-type(2){font-size:12px;font-weight:300;padding:6px;color:#666a73} +h1{font-size:1.3em;font-weight:500;text-align:center;margin-bottom:12px} +::-webkit-datetime-edit{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";background:url(elg_calendar_inv.svg) no-repeat left center;padding-right:1em;padding-left:25px;background-position:4px 0px} +::-webkit-calendar-picker-indicator{background:transparent;font-size:17px} +::-webkit-calendar-picker-indicator:focus{background-color:rgba(0,0,0,0.2)} +input[type="date"]{-webkit-align-items:center;align-items:center;display:-webkit-inline-flex;font-family:monospace;overflow:hidden;padding:0;-webkit-padding-start:1px} +input::-webkit-datetime-edit{-webkit-flex:1;-webkit-user-modify:read-only !important;display:inline-block;min-width:0;overflow:hidden} +input[type="file"]{opacity:0;display:none} +.sdpi-item>input[type="file"]{opacity:1;display:flex} +input[type="file"]+span{display:flex;flex:0 1 auto;background-color:#0000ff50} +label.sdpi-file-label{cursor:pointer;user-select:none;display:inline-block;min-height:21px !important;height:21px !important;line-height:20px;padding:0px 4px;margin:auto;margin-right:0px} +.sdpi-file-label>label:active,.sdpi-file-label.file:active,label.sdpi-file-label:active,label.sdpi-file-info:active,input[type="file"]::-webkit-file-upload-button:active,button:active{background-color:var(--sdpi-color);color:#303030} +input:required:invalid,input:focus:invalid{background:var(--sdpi-background) url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5IiBoZWlnaHQ9IjkiIHZpZXdCb3g9IjAgMCA5IDkiPgogICAgPHBhdGggZmlsbD0iI0Q4RDhEOCIgZD0iTTQuNSwwIEM2Ljk4NTI4MTM3LC00LjU2NTM4NzgyZS0xNiA5LDIuMDE0NzE4NjMgOSw0LjUgQzksNi45ODUyODEzNyA2Ljk4NTI4MTM3LDkgNC41LDkgQzIuMDE0NzE4NjMsOSAzLjA0MzU5MTg4ZS0xNiw2Ljk4NTI4MTM3IDAsNC41IEMtMy4wNDM1OTE4OGUtMTYsMi4wMTQ3MTg2MyAyLjAxNDcxODYzLDQuNTY1Mzg3ODJlLTE2IDQuNSwwIFogTTQsMSBMNCw2IEw1LDYgTDUsMSBMNCwxIFogTTQuNSw4IEM0Ljc3NjE0MjM3LDggNSw3Ljc3NjE0MjM3IDUsNy41IEM1LDcuMjIzODU3NjMgNC43NzYxNDIzNyw3IDQuNSw3IEM0LjIyMzg1NzYzLDcgNCw3LjIyMzg1NzYzIDQsNy41IEM0LDcuNzc2MTQyMzcgNC4yMjM4NTc2Myw4IDQuNSw4IFoiLz4KICA8L3N2Zz4) no-repeat 98% center} +input:required:valid{background:var(--sdpi-background) url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5IiBoZWlnaHQ9IjkiIHZpZXdCb3g9IjAgMCA5IDkiPjxwb2x5Z29uIGZpbGw9IiNEOEQ4RDgiIHBvaW50cz0iNS4yIDEgNi4yIDEgNi4yIDcgMy4yIDcgMy4yIDYgNS4yIDYiIHRyYW5zZm9ybT0icm90YXRlKDQwIDQuNjc3IDQpIi8+PC9zdmc+) no-repeat 98% center} +.tooltip,:tooltip,:title{color:yellow} +.sdpi-item-group.file{width:232px;display:flex;align-items:center} +.sdpi-file-info{overflow-wrap:break-word;word-wrap:break-word;hyphens:auto;min-width:132px;max-width:144px;max-height:32px;margin-top:0px;margin-left:5px;display:inline-block;overflow:hidden;padding:6px 4px;background-color:var(--sdpi-background)} +::-webkit-scrollbar{width:8px} +::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 6px rgba(0,0,0,0.3)} +::-webkit-scrollbar-thumb{background-color:#999999;outline:1px solid slategrey;border-radius:8px} +a{color:#7397d2} +.testcontainer{display:flex;background-color:#0000ff20;max-width:400px;height:200px;align-content:space-evenly} +input[type=range]{-webkit-appearance:none;appearance:none;height:6px;margin-top:12px;z-index:0;overflow:visible} +:-webkit-slider-thumb{-webkit-appearance:none;background-color:var(--sdpi-color);width:16px;height:16px;border-radius:20px;margin-top:-6px;border:1px solid #999999} +.sdpi-item[type="range"] .sdpi-item-group{display:flex;flex-direction:column} +.xxsdpi-item[type="range"] .sdpi-item-group input{max-width:204px} +.sdpi-item[type="range"] .sdpi-item-group span{margin-left:0px !important} +.sdpi-item[type="range"] .sdpi-item-group>.sdpi-item-child{display:flex;flex-direction:row} +.rangeLabel{position:absolute;font-weight:normal;margin-top:22px} +:disabled{color:#993333} +select,select option{color:var(--sdpi-color)} +select.disabled,select option:disabled{color:#fd9494;font-style:italic} +.runningAppsContainer{display:none} +.one-line{min-height:1.5em} +.two-lines{min-height:3em} +.three-lines{min-height:4.5em} +.four-lines{min-height:6em} +.min80>.sdpi-item-child{min-width:80px} +.min100>.sdpi-item-child{min-width:100px} +.min120>.sdpi-item-child{min-width:120px} +.min140>.sdpi-item-child{min-width:140px} +.min160>.sdpi-item-child{min-width:160px} +.min200>.sdpi-item-child{min-width:200px} +.max40{flex-basis:40%;flex-grow:0} +.max30{flex-basis:30%;flex-grow:0} +.max20{flex-basis:20%;flex-grow:0} +.up20{margin-top:-20px} +.alignCenter{align-items:center} +.alignTop{align-items:flex-start} +.alignBaseline{align-items:baseline} +.noMargins,.noMargins *,.noInnerMargins *{margin:0;padding:0} +.hidden{display:none} +.icon-help,.icon-help-line,.icon-help-fill,.icon-help-inv,.icon-brighter,.icon-darker,.icon-warmer,.icon-cooler{min-width:20px;width:20px;background-repeat:no-repeat;opacity:1} +.icon-help:active,.icon-help-line:active,.icon-help-fill:active,.icon-help-inv:active,.icon-brighter:active,.icon-darker:active,.icon-warmer:active,.icon-cooler:active{opacity:0.5} +.icon-brighter,.icon-darker,.icon-warmer,.icon-cooler{margin-top:5px !important} +.icon-help,.icon-help-line,.icon-help-fill,.icon-help-inv{cursor:pointer;margin:0px;margin-left:4px} +.icon-brighter{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Ccircle cx='10' cy='10' r='4'/%3E%3Cpath d='M14.8532861,7.77530426 C14.7173255,7.4682615 14.5540843,7.17599221 14.3666368,6.90157083 L16.6782032,5.5669873 L17.1782032,6.4330127 L14.8532861,7.77530426 Z M10.5,4.5414007 C10.2777625,4.51407201 10.051423,4.5 9.82179677,4.5 C9.71377555,4.5 9.60648167,4.50311409 9.5,4.50925739 L9.5,2 L10.5,2 L10.5,4.5414007 Z M5.38028092,6.75545367 C5.18389364,7.02383457 5.01124349,7.31068015 4.86542112,7.61289977 L2.82179677,6.4330127 L3.32179677,5.5669873 L5.38028092,6.75545367 Z M4.86542112,12.3871002 C5.01124349,12.6893198 5.18389364,12.9761654 5.38028092,13.2445463 L3.32179677,14.4330127 L2.82179677,13.5669873 L4.86542112,12.3871002 Z M9.5,15.4907426 C9.60648167,15.4968859 9.71377555,15.5 9.82179677,15.5 C10.051423,15.5 10.2777625,15.485928 10.5,15.4585993 L10.5,18 L9.5,18 L9.5,15.4907426 Z M14.3666368,13.0984292 C14.5540843,12.8240078 14.7173255,12.5317385 14.8532861,12.2246957 L17.1782032,13.5669873 L16.6782032,14.4330127 L14.3666368,13.0984292 Z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-darker{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10 14C7.790861 14 6 12.209139 6 10 6 7.790861 7.790861 6 10 6 12.209139 6 14 7.790861 14 10 14 12.209139 12.209139 14 10 14zM10 13C11.6568542 13 13 11.6568542 13 10 13 8.34314575 11.6568542 7 10 7 8.34314575 7 7 8.34314575 7 10 7 11.6568542 8.34314575 13 10 13zM14.8532861 7.77530426C14.7173255 7.4682615 14.5540843 7.17599221 14.3666368 6.90157083L16.6782032 5.5669873 17.1782032 6.4330127 14.8532861 7.77530426zM10.5 4.5414007C10.2777625 4.51407201 10.051423 4.5 9.82179677 4.5 9.71377555 4.5 9.60648167 4.50311409 9.5 4.50925739L9.5 2 10.5 2 10.5 4.5414007zM5.38028092 6.75545367C5.18389364 7.02383457 5.01124349 7.31068015 4.86542112 7.61289977L2.82179677 6.4330127 3.32179677 5.5669873 5.38028092 6.75545367zM4.86542112 12.3871002C5.01124349 12.6893198 5.18389364 12.9761654 5.38028092 13.2445463L3.32179677 14.4330127 2.82179677 13.5669873 4.86542112 12.3871002zM9.5 15.4907426C9.60648167 15.4968859 9.71377555 15.5 9.82179677 15.5 10.051423 15.5 10.2777625 15.485928 10.5 15.4585993L10.5 18 9.5 18 9.5 15.4907426zM14.3666368 13.0984292C14.5540843 12.8240078 14.7173255 12.5317385 14.8532861 12.2246957L17.1782032 13.5669873 16.6782032 14.4330127 14.3666368 13.0984292z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-warmer{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M12.3247275 11.4890349C12.0406216 11.0007637 11.6761954 10.5649925 11.2495475 10.1998198 11.0890394 9.83238991 11 9.42659309 11 9 11 7.34314575 12.3431458 6 14 6 15.6568542 6 17 7.34314575 17 9 17 10.6568542 15.6568542 12 14 12 13.3795687 12 12.8031265 11.8116603 12.3247275 11.4890349zM17.6232392 11.6692284C17.8205899 11.4017892 17.9890383 11.1117186 18.123974 10.8036272L20.3121778 12.0669873 19.8121778 12.9330127 17.6232392 11.6692284zM18.123974 7.19637279C17.9890383 6.88828142 17.8205899 6.5982108 17.6232392 6.33077158L19.8121778 5.0669873 20.3121778 5.9330127 18.123974 7.19637279zM14.5 4.52746439C14.3358331 4.50931666 14.1690045 4.5 14 4.5 13.8309955 4.5 13.6641669 4.50931666 13.5 4.52746439L13.5 2 14.5 2 14.5 4.52746439zM13.5 13.4725356C13.6641669 13.4906833 13.8309955 13.5 14 13.5 14.1690045 13.5 14.3358331 13.4906833 14.5 13.4725356L14.5 16 13.5 16 13.5 13.4725356zM14 11C15.1045695 11 16 10.1045695 16 9 16 7.8954305 15.1045695 7 14 7 12.8954305 7 12 7.8954305 12 9 12 10.1045695 12.8954305 11 14 11zM9.5 11C10.6651924 11.4118364 11.5 12.5 11.5 14 11.5 16 10 17.5 8 17.5 6 17.5 4.5 16 4.5 14 4.5 12.6937812 5 11.5 6.5 11L6.5 7 9.5 7 9.5 11z'/%3E%3Cpath d='M12,14 C12,16.209139 10.209139,18 8,18 C5.790861,18 4,16.209139 4,14 C4,12.5194353 4.80439726,11.2267476 6,10.5351288 L6,4 C6,2.8954305 6.8954305,2 8,2 C9.1045695,2 10,2.8954305 10,4 L10,10.5351288 C11.1956027,11.2267476 12,12.5194353 12,14 Z M11,14 C11,12.6937812 10.1651924,11.5825421 9,11.1707057 L9,4 C9,3.44771525 8.55228475,3 8,3 C7.44771525,3 7,3.44771525 7,4 L7,11.1707057 C5.83480763,11.5825421 5,12.6937812 5,14 C5,15.6568542 6.34314575,17 8,17 C9.65685425,17 11,15.6568542 11,14 Z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-cooler{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10.4004569 11.6239517C10.0554735 10.9863849 9.57597206 10.4322632 9 9.99963381L9 9.7450467 9.53471338 9.7450467 10.8155381 8.46422201C10.7766941 8.39376637 10.7419749 8.32071759 10.7117062 8.2454012L9 8.2454012 9 6.96057868 10.6417702 6.96057868C10.6677696 6.86753378 10.7003289 6.77722682 10.7389179 6.69018783L9.44918707 5.40045694 9 5.40045694 9 4.34532219 9.32816127 4.34532219 9.34532219 2.91912025 10.4004569 2.91912025 10.4004569 4.53471338 11.6098599 5.74411634C11.7208059 5.68343597 11.8381332 5.63296451 11.9605787 5.59396526L11.9605787 3.8884898 10.8181818 2.74609294 11.5642748 2 12.5727518 3.00847706 13.5812289 2 14.3273218 2.74609294 13.2454012 3.82801356 13.2454012 5.61756719C13.3449693 5.65339299 13.4408747 5.69689391 13.5324038 5.74735625L14.7450467 4.53471338 14.7450467 2.91912025 15.8001815 2.91912025 15.8001815 4.34532219 17.2263834 4.34532219 17.2263834 5.40045694 15.6963166 5.40045694 14.4002441 6.69652946C14.437611 6.78161093 14.4692249 6.86979146 14.4945934 6.96057868L16.2570138 6.96057868 17.3994107 5.81818182 18.1455036 6.56427476 17.1370266 7.57275182 18.1455036 8.58122888 17.3994107 9.32732182 16.3174901 8.2454012 14.4246574 8.2454012C14.3952328 8.31861737 14.3616024 8.38969062 14.3240655 8.45832192L15.6107903 9.7450467 17.2263834 9.7450467 17.2263834 10.8001815 15.8001815 10.8001815 15.8001815 12.2263834 14.7450467 12.2263834 14.7450467 10.6963166 13.377994 9.32926387C13.3345872 9.34850842 13.2903677 9.36625331 13.2454012 9.38243281L13.2454012 11.3174901 14.3273218 12.3994107 13.5812289 13.1455036 12.5848864 12.1491612 11.5642748 13.1455036 10.8181818 12.3994107 11.9605787 11.2570138 11.9605787 9.40603474C11.8936938 9.38473169 11.828336 9.36000556 11.7647113 9.33206224L10.4004569 10.6963166 10.4004569 11.6239517zM12.75 8.5C13.3022847 8.5 13.75 8.05228475 13.75 7.5 13.75 6.94771525 13.3022847 6.5 12.75 6.5 12.1977153 6.5 11.75 6.94771525 11.75 7.5 11.75 8.05228475 12.1977153 8.5 12.75 8.5zM9.5 14C8.5 16.3333333 7.33333333 17.5 6 17.5 4.66666667 17.5 3.5 16.3333333 2.5 14L9.5 14z'/%3E%3Cpath d='M10,14 C10,16.209139 8.209139,18 6,18 C3.790861,18 2,16.209139 2,14 C2,12.5194353 2.80439726,11.2267476 4,10.5351288 L4,4 C4,2.8954305 4.8954305,2 6,2 C7.1045695,2 8,2.8954305 8,4 L8,10.5351288 C9.19560274,11.2267476 10,12.5194353 10,14 Z M9,14 C9,12.6937812 8.16519237,11.5825421 7,11.1707057 L7,4 C7,3.44771525 6.55228475,3 6,3 C5.44771525,3 5,3.44771525 5,4 L5,11.1707057 C3.83480763,11.5825421 3,12.6937812 3,14 C3,15.6568542 4.34314575,17 6,17 C7.65685425,17 9,15.6568542 9,14 Z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-help{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cpath fill='%23999' d='M11.292 12.516l.022 1.782H9.07v-1.804c0-1.98 1.276-2.574 2.662-3.278h-.022c.814-.44 1.65-.88 1.694-2.2.044-1.386-1.122-2.728-3.234-2.728-1.518 0-2.662.902-3.366 2.354L5 5.608C5.946 3.584 7.662 2 10.17 2c3.564 0 5.632 2.442 5.588 5.06-.066 2.618-1.716 3.41-3.102 4.158-.704.374-1.364.682-1.364 1.298zm-1.122 2.442c.858 0 1.452.594 1.452 1.452 0 .682-.594 1.408-1.452 1.408-.77 0-1.386-.726-1.386-1.408 0-.858.616-1.452 1.386-1.452z'/%3E%3C/svg%3E")} +.icon-help-line{background-image:url("data:image/svg+xml,%3Csvg width='20' height='20' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-1a9 9 0 1 0 0-18 9 9 0 0 0 0 18z'/%3E%3Cpath d='M10.848 12.307l.02 1.578H8.784v-1.597c0-1.753 1.186-2.278 2.474-2.901h-.02c.756-.39 1.533-.78 1.574-1.948.041-1.226-1.043-2.414-3.006-2.414-1.41 0-2.474.798-3.128 2.083L5 6.193C5.88 4.402 7.474 3 9.805 3 13.118 3 15.04 5.161 15 7.478c-.061 2.318-1.595 3.019-2.883 3.68-.654.332-1.268.604-1.268 1.15zM9.805 14.47c.798 0 1.35.525 1.35 1.285 0 .603-.552 1.246-1.35 1.246-.715 0-1.288-.643-1.288-1.246 0-.76.573-1.285 1.288-1.285z' fill-rule='nonzero'/%3E%3C/g%3E%3C/svg%3E")} +.icon-help-fill{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Ccircle cx='10' cy='10' r='10' fill='%23999'/%3E%3Cpath fill='%23FFF' fill-rule='nonzero' d='M8.368 7.189H5C5 3.5 7.668 2 10.292 2 13.966 2 16 4.076 16 7.012c0 3.754-3.849 3.136-3.849 5.211v1.656H8.455v-1.832c0-2.164 1.4-2.893 2.778-3.6.437-.242 1.006-.574 1.006-1.236 0-2.208-3.871-2.142-3.871-.022zM10.25 18a1.75 1.75 0 1 1 0-3.5 1.75 1.75 0 0 1 0 3.5z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-help-inv{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cpath fill='%23999' fill-rule='evenodd' d='M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zM8.368 7.189c0-2.12 3.87-2.186 3.87.022 0 .662-.568.994-1.005 1.236-1.378.707-2.778 1.436-2.778 3.6v1.832h3.696v-1.656c0-2.075 3.849-1.457 3.849-5.21C16 4.075 13.966 2 10.292 2 7.668 2 5 3.501 5 7.189h3.368zM10.25 18a1.75 1.75 0 1 0 0-3.5 1.75 1.75 0 0 0 0 3.5z'/%3E%3C/svg%3E")} +.kelvin::after{content:"K"} +.mired::after{content:" Mired"} +.percent::after{content:"%"} +.sdpi-item-value+.icon-cooler,.sdpi-item-value+.icon-warmer{margin-left:0px !important;margin-top:15px !important} +input[type="range"].colorbrightness::-webkit-slider-runnable-track,input[type="range"].colortemperature::-webkit-slider-runnable-track{height:8px;background:#979797;border-radius:4px;background-image:linear-gradient(to right,#94d0ec,#ffb165)} +input[type="range"].colorbrightness::-webkit-slider-runnable-track{background-color:#efefef;background-image:linear-gradient(to right,black,rgba(0,0,0,0))} +input[type="range"].colorbrightness::-webkit-slider-thumb,input[type="range"].colortemperature::-webkit-slider-thumb{width:16px;height:16px;border-radius:20px;margin-top:-5px;background-color:#86c6e8;box-shadow:0px 0px 1px #000000;border:1px solid #d8d8d8} +.sdpi-info-label{display:inline-block;user-select:none;position:absolute;height:15px;width:auto;text-align:center;border-radius:4px;min-width:44px;max-width:80px;background:white;font-size:11px;color:black;z-index:1000;box-shadow:0px 0px 12px rgba(0,0,0,.8);padding:2px} +.sdpi-info-label.hidden{opacity:0;transition:opacity 0.25s linear} +.sdpi-info-label.shown{position:absolute;opacity:1;transition:opacity 0.25s ease-out} \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/default.jpg b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/default.jpg new file mode 100644 index 000000000..8061f9cfe --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/static/default.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb5c4d9b90e10680cb8f13a6c12ad0f426601fd2ee4130494fa06b6e6bb1677c +size 16885 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/zh_CN.json b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/zh_CN.json new file mode 100644 index 000000000..b85b2571b --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/com.mirabox.streamdock.demo.sdPlugin/zh_CN.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:686b48184fcd8189da15ab7bf71fb5529a033ef238298a074fb24513ca1681ab +size 231 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/manifest.json b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/manifest.json new file mode 100644 index 000000000..6344acf83 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/manifest.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8777f19cf23aeae91083ccfae4e0f0d5bd6647dfe3ca2989c1e1d82ef4cbcf51 +size 1078 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/package.json b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/package.json new file mode 100644 index 000000000..2a1a7fbb8 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/package.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0cfa62142e327e04cd21d255ba127678105570d4ffb4dae98c02e8ffa6dd05f5 +size 826 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/plugin/app.exe b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/plugin/app.exe new file mode 100644 index 000000000..eec58f080 Binary files /dev/null and b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/plugin/app.exe differ diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/plugin/autofile.js b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/plugin/autofile.js new file mode 100644 index 000000000..340d29540 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/plugin/autofile.js @@ -0,0 +1,46 @@ +const path = require('path'); +const fs = require('fs-extra'); + +console.log('开始执行自动化构建...'); + +const currentDir = __dirname; + +// 获取父文件夹的路径 +const parentDir = path.join(currentDir, '..'); +// 获取父文件夹的名称 +const PluginName = path.basename(parentDir); + + +const PluginPath = path.join(process.env.APPDATA, 'HotSpot/StreamDock/plugins', PluginName); + +try { + // 删除旧的插件目录 + fs.removeSync(PluginPath); + + // 确保目标目录存在 + fs.ensureDirSync(path.dirname(PluginPath)); + + // 复制当前目录到目标路径,排除 node_modules + fs.copySync(path.resolve(__dirname, '..'), PluginPath, { + filter: (src) => { + const relativePath = path.relative(path.resolve(__dirname, '..'), src); + // 排除 'node_modules' 和 '.git' 目录及其子文件 + return !relativePath.startsWith('plugin\\node_modules') + &&!relativePath.startsWith('plugin\\index.js') + &&!relativePath.startsWith('plugin\\package.json') + &&!relativePath.startsWith('plugin\\package-lock.json') + &&!relativePath.startsWith('plugin\\yarn.lock') + &&!relativePath.startsWith('plugin\\build') + &&!relativePath.startsWith('plugin\\log') + &&!relativePath.startsWith('.git') + &&!relativePath.startsWith('.vscode'); + } + }); + + fs.copySync( path.join(__dirname, "build"), path.join(PluginPath,'plugin')) + + console.log(`插件 "${PluginName}" 已成功复制到 "${PluginPath}"`); + console.log('构建成功-------------'); +} catch (err) { + console.error(`复制出错 "${PluginName}":`, err); +} \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/plugin/index.js b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/plugin/index.js new file mode 100644 index 000000000..6f9bf42f1 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/plugin/index.js @@ -0,0 +1,172 @@ +const { Plugins, Actions, log } = require('./utils/plugin'); +const WebSocket = require('ws'); + +// 플러그인 인스턴스 +const plugin = new Plugins(); + +// Unity WebSocket 서버 +const WS_PORT = 15732; +let wss = null; +let unityConnections = new Set(); +let isUnityConnected = false; + +// Unity 서버 시작 +function startUnityServer() { + try { + wss = new WebSocket.Server({ port: WS_PORT }); + log.info(`Unity WebSocket 서버 시작: 포트 ${WS_PORT}`); + + // 서버 연결 시 기존 연결 확인 + setTimeout(() => { + if (wss.clients.size > 0) { + log.info(`기존 Unity 연결 발견: ${wss.clients.size}개`); + isUnityConnected = true; + wss.clients.forEach(ws => { + unityConnections.add(ws); + }); + updateConnectionStatus(); + } + }, 1000); + + wss.on('connection', (ws, req) => { + const clientIP = req.socket.remoteAddress; + log.info(`Unity 클라이언트 연결됨 - IP: ${clientIP}`); + unityConnections.add(ws); + isUnityConnected = true; + updateConnectionStatus(); + + // 연결 확인 메시지 전송 + ws.send(JSON.stringify({ + type: 'connection_confirmed', + data: { + message: '플러그인에 성공적으로 연결되었습니다', + timestamp: Date.now() + } + })); + + ws.on('message', (message) => { + log.info(`Unity에서 메시지 수신: ${message}`); + }); + + ws.on('close', () => { + log.info(`Unity 클라이언트 연결 해제 - IP: ${clientIP}`); + unityConnections.delete(ws); + + if (unityConnections.size === 0) { + isUnityConnected = false; + updateConnectionStatus(); + } + }); + + ws.on('error', (error) => { + log.error(`Unity 연결 오류: ${error.message}`); + }); + }); + + wss.on('error', (error) => { + log.error(`WebSocket 서버 오류: ${error.message}`); + }); + + } catch (error) { + log.error('WebSocket 서버 오류: ' + error.message); + } +} + +// Unity로 메시지 전송 +function sendToUnity(message) { + if (unityConnections.size === 0) { + log.warn('Unity 연결 없음'); + return false; + } + + const messageStr = JSON.stringify(message); + unityConnections.forEach(ws => { + if (ws.readyState === WebSocket.OPEN) { + ws.send(messageStr); + log.info('Unity로 전송: ' + messageStr); + } + }); + return true; +} + +// 연결 상태 업데이트 +function updateConnectionStatus() { + const status = isUnityConnected ? '연결됨' : '연결안됨'; + log.info(`Unity 연결 상태: ${status}`); + + // 모든 버튼의 제목 업데이트 + if (plugin.action1 && plugin.action1.currentContext) { + plugin.setTitle(plugin.action1.currentContext, isUnityConnected ? "Unity 버튼" : "연결안됨"); + } +} + +// 버튼 액션 +plugin.action1 = new Actions({ + currentContext: null, + + _willAppear({ context }) { + this.currentContext = context; + const title = isUnityConnected ? "Unity 버튼" : "연결안됨"; + plugin.setTitle(context, title); + log.info('Unity 버튼 로드됨'); + }, + + _keyUp({ context }) { + log.info('버튼 클릭됨!'); + + if (!isUnityConnected) { + plugin.setTitle(context, "연결없음"); + setTimeout(() => { + const title = isUnityConnected ? "Unity 버튼" : "연결안됨"; + plugin.setTitle(context, title); + }, 1000); + return; + } + + const success = sendToUnity({ + type: 'button_clicked', + data: { + message: 'StreamDock 버튼이 클릭되었습니다!', + timestamp: Date.now() + } + }); + + if (success) { + plugin.setTitle(context, "전송됨!"); + setTimeout(() => plugin.setTitle(context, "Unity 버튼"), 1000); + } else { + plugin.setTitle(context, "실패"); + setTimeout(() => plugin.setTitle(context, "Unity 버튼"), 1000); + } + }, + + _willDisappear({ context }) { + this.currentContext = null; + } +}); + +// 서버 시작 +startUnityServer(); + +// 주기적으로 연결 상태 확인 (5초마다) +setInterval(() => { + const actualConnections = wss ? wss.clients.size : 0; + const wasConnected = isUnityConnected; + + if (actualConnections > 0 && !isUnityConnected) { + log.info(`연결 상태 동기화: Unity 연결됨 (${actualConnections}개)`); + isUnityConnected = true; + updateConnectionStatus(); + } else if (actualConnections === 0 && isUnityConnected) { + log.info('연결 상태 동기화: Unity 연결 해제'); + isUnityConnected = false; + unityConnections.clear(); + updateConnectionStatus(); + } + + if (wasConnected !== isUnityConnected) { + log.info(`연결 상태 변경: ${wasConnected ? '연결됨' : '연결안됨'} → ${isUnityConnected ? '연결됨' : '연결안됨'}`); + } +}, 5000); + +log.info('Unity 통신 플러그인 시작됨'); \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/plugin/package.json b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/plugin/package.json new file mode 100644 index 000000000..3cd6b89f6 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/plugin/package.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa8df7d0d76e6c5f57e359387e760eed78baedb0af1def63c4b62ef17b3bb9a5 +size 344 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/plugin/utils/plugin.js b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/plugin/utils/plugin.js new file mode 100644 index 000000000..0451860ea --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/plugin/utils/plugin.js @@ -0,0 +1,240 @@ +// 配置日志文件 +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()); + log.info('Received event:', data); + + const action = data.action?.split('.').pop(); + + // 액션별 이벤트 처리 + if (this[action] && this[action][data.event]) { + log.info(`Calling ${action}.${data.event}`); + this[action][data.event](data); + } + + // 전역 이벤트 처리 + if (data.event === 'didReceiveGlobalSettings') { + Plugins.globalSettings = data.payload.settings; + } + if (this[data.event]) { + 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" + })); + } + + // URL 열기 + 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); + } + + // 키 이벤트 + keyUp(data) { + log.info('keyUp called with data:', data); + if (typeof this._keyUp === 'function') { + this._keyUp(data); + } + } + + keyDown(data) { + log.info('keyDown called with data:', data); + if (typeof this._keyDown === 'function') { + this._keyDown(data); + } + } + + // 다이얼 이벤트 + dialRotate(data) { + log.info('dialRotate called with data:', data); + if (typeof this._dialRotate === 'function') { + this._dialRotate(data); + } + } + + dialDown(data) { + log.info('dialDown called with data:', data); + if (typeof this._dialDown === 'function') { + this._dialDown(data); + } + } + + // 액션 해제 + willDisappear(data) { + this._willDisappear?.(data); + delete this.data[data.context]; + } +} + +module.exports = { + log, + Plugins, + Actions, +}; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/propertyInspector/action1/index.html b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/propertyInspector/action1/index.html new file mode 100644 index 000000000..0aed492a1 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/propertyInspector/action1/index.html @@ -0,0 +1,118 @@ + + + + + + Unity 이벤트 전송 설정 + + + + +
+

Unity 이벤트 전송

+ +
+ + +
+ +
+ + +
+ +
+ 사용법:
+ • Unity에서 StreamDockCommunicator 스크립트를 사용하세요
+ • WebSocket 연결: ws://localhost:15732
+ • 이 버튼을 누르면 Unity로 이벤트가 전송됩니다 +
+
+ + + + + \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/propertyInspector/action1/index.js b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/propertyInspector/action1/index.js new file mode 100644 index 000000000..ff58dd017 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/propertyInspector/action1/index.js @@ -0,0 +1,14 @@ +/// +/// + +// $local 是否国际化 +// $back 是否自行决定回显时机 +// $dom 获取文档元素 - 不是动态的都写在这里面 +const $local = false, $back = false, $dom = { + main: $('.sdpi-wrapper') +}; + +const $propEvent = { + didReceiveSettings(data) { }, + sendToPropertyInspector(data) { } +}; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/propertyInspector/action2/index.html b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/propertyInspector/action2/index.html new file mode 100644 index 000000000..d31b1cf0d --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/propertyInspector/action2/index.html @@ -0,0 +1,152 @@ + + + + + Unity 상태 수신 설정 + + + +
+

Unity 상태 수신

+ +
+ + +
+ +
+ + +
+ +
+ 연결 상태:
+ 연결 안됨

+ + 기능:
+ • Unity에서 게임 상태를 실시간으로 받아옵니다
+ • 버튼을 누르면 최신 상태를 요청합니다
+ • WebSocket: ws://localhost:15732 +
+
+ + + + \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/propertyInspector/utils/action.js b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/propertyInspector/utils/action.js new file mode 100644 index 000000000..76798ecba --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/propertyInspector/utils/action.js @@ -0,0 +1,131 @@ +/** + * PropertyInspector 2.5.0 新特性 => + * + * 1 => 工具与主文件相分离 - 按需引入 + * 2 => $settings - 全局持久化数据代理 ※ + * 3 => 无需关注上下文 - 随时随地与插件通信 + * 4 => 注意事项: 为了避免命名冲突,请勿使用 $ 相关的名称以及JQuery库 + * + * ===== CJHONG ========================================== 2023.10.10 =====> + */ + +let $websocket, $uuid, $action, $context, $settings, $lang, $FileID = ''; + +// 与插件通信 +WebSocket.prototype.sendToPlugin = function (payload) { + this.send(JSON.stringify({ + event: "sendToPlugin", + action: $action, + context: $uuid, + payload + })); +}; + +// 设置状态 +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 软件入口函数 +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/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/propertyInspector/utils/common.js b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/propertyInspector/utils/common.js new file mode 100644 index 000000000..8dda3c8fe --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/propertyInspector/utils/common.js @@ -0,0 +1,81 @@ +// 自定义事件类 +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/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/readme.md b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/readme.md new file mode 100644 index 000000000..b5c84fdc6 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/readme.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e862724d53e761e916a709132de6f5f823abb0e8149f9115745ab26250d82cc7 +size 6416 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/@unity.png b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/@unity.png new file mode 100644 index 000000000..eb9607878 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/@unity.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4eba213b920f94f5cc2927e6204a3bf23eec1b4458f3be14aaefaa97ed31f3b +size 5465 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/CH.png b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/CH.png new file mode 100644 index 000000000..eb9607878 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/CH.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4eba213b920f94f5cc2927e6204a3bf23eec1b4458f3be14aaefaa97ed31f3b +size 5465 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/caret.svg b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/caret.svg new file mode 100644 index 000000000..b69162a4f --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/caret.svg @@ -0,0 +1,3 @@ + + + diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/check.png b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/check.png new file mode 100644 index 000000000..ece8509f5 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/check.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5fb6fbe1417751ca7dd87552398d4585cb002654aa69632fdf6f43dbc65220c +size 234 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/check.svg b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/check.svg new file mode 100644 index 000000000..5b96af052 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/elg_calendar.svg b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/elg_calendar.svg new file mode 100644 index 000000000..157e01bb5 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/elg_calendar.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/elg_calendar_inv.svg b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/elg_calendar_inv.svg new file mode 100644 index 000000000..4f8af68d3 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/elg_calendar_inv.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/g_d8d8d8.svg b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/g_d8d8d8.svg new file mode 100644 index 000000000..d99031408 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/g_d8d8d8.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/rcheck.svg b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/rcheck.svg new file mode 100644 index 000000000..af478ee58 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/rcheck.svg @@ -0,0 +1,3 @@ + + + diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/sdpi.css b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/sdpi.css new file mode 100644 index 000000000..26b725b1e --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/css/sdpi.css @@ -0,0 +1,230 @@ +:root{--sdpi-bgcolor:#2D2D2D;--sdpi-background:#3D3D3D;--sdpi-color:#d8d8d8;--sdpi-bordercolor:#3a3a3a;--sdpi-buttonbordercolor:#969696;--sdpi-borderradius:0px;--sdpi-width:224px;--sdpi-fontweight:600;--sdpi-letterspacing:-0.25pt} +html{--sdpi-bgcolor:#2D2D2D;--sdpi-background:#3D3D3D;--sdpi-color:#d8d8d8;--sdpi-bordercolor:#3a3a3a;--sdpi-buttonbordercolor:#969696;--sdpi-borderradius:0px;--sdpi-width:224px;--sdpi-fontweight:600;--sdpi-letterspacing:-0.25pt;height:100%;width:100%;overflow:hidden;touch-action:none;user-select:none} +html,body{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:9pt;background-color:var(--sdpi-bgcolor);color:#9a9a9a} +body{height:100%;padding:0;overflow-x:hidden;overflow-y:auto;margin:0;-webkit-overflow-scrolling:touch;-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased} +mark{background-color:var(--sdpi-bgcolor);color:var(--sdpi-color)} +hr,hr2{-webkit-margin-before:1em;-webkit-margin-after:1em;border-style:none;background:var(--sdpi-background);height:1px} +hr2,.sdpi-heading{display:flex;flex-basis:100%;align-items:center;color:inherit;font-size:9pt;margin:8px 0px} +.sdpi-heading::before,.sdpi-heading::after{content:"";flex-grow:1;background:var(--sdpi-background);height:1px;font-size:0px;line-height:0px;margin:0px 16px} +hr2{height:2px} +hr,hr2{margin-left:16px;margin-right:16px} +.sdpi-item-value,option,input,select,button{font-size:10pt;font-weight:var(--sdpi-fontweight);letter-spacing:var(--sdpi-letterspacing)} +.win .sdpi-item-value,.win option,.win input,.win select,.win button{font-size:11px;font-style:normal;letter-spacing:inherit;font-weight:100} +.win button{font-size:12px} +::-webkit-progress-value,meter::-webkit-meter-optimum-value{border-radius:2px} +::-webkit-progress-bar,meter::-webkit-meter-bar{border-radius:3px;background:var(--sdpi-background)} +::-webkit-progress-bar:active,meter::-webkit-meter-bar:active{border-radius:3px;background:#222222} +::-webkit-progress-value:active,meter::-webkit-meter-optimum-value:active{background:#99f} +progress,progress.sdpi-item-value{min-height:5px !important;height:5px;background-color:#303030} +progress{margin-top:8px !important;margin-bottom:8px !important} +.full progress,progress.full{margin-top:3px !important} +::-webkit-progress-inner-element{background-color:transparent} +.sdpi-item[type="progress"]{margin-top:4px !important;margin-bottom:12px;min-height:15px} +.sdpi-item-child.full:last-child{margin-bottom:4px} +.tabs{display:flex;border-bottom:1px solid #D7DBDD} +.tab{cursor:pointer;padding:5px 30px;color:#16a2d7;font-size:9pt;border-bottom:2px solid transparent} +.tab.is-tab-selected{border-bottom-color:#4ebbe4} +select{width:100%;-webkit-appearance:none;-moz-appearance:none;-o-appearance:none;appearance:none;background:url(caret.svg) no-repeat 97% center} +label.sdpi-file-label,input[type="button"],input[type="submit"],input[type="reset"],input[type="file"],input[type=file]::-webkit-file-upload-button,button,select{color:var(--sdpi-color);border:1pt solid #303030;font-size:8pt;background-color:var(--sdpi-background);border-radius:var(--sdpi-borderradius)} +label.sdpi-file-label,input[type="button"],input[type="submit"],input[type="reset"],input[type="file"],input[type=file]::-webkit-file-upload-button,button{border:1pt solid var(--sdpi-buttonbordercolor);border-radius:var(--sdpi-borderradius);border-color:var(--sdpi-buttonbordercolor);min-height:23px !important;height:23px !important;margin-right:8px} +input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0} +input[type="file"]{border-radius:var(--sdpi-borderradius);max-width:220px} +option{height:1.5em;padding:4px} +.sdpi-wrapper{overflow-x:hidden;height:100%} +.sdpi-item{display:flex;flex-direction:row;min-height:32px;align-items:center;margin-top:2px;max-width:344px;-webkit-user-drag:none} +.sdpi-item:first-child{margin-top:-1px} +.sdpi-item:last-child{margin-bottom:0px} +.sdpi-item>*:not(.sdpi-item-label):not(meter):not(details):not(canvas){min-height:26px;padding:0px 4px 0px 4px} +.sdpi-item>*:not(.sdpi-item-label.empty):not(meter){min-height:26px;padding:0px 4px 0px 4px} +.sdpi-item-group{padding:0 !important} +meter.sdpi-item-value{margin-left:6px} +.sdpi-item[type="group"]{display:block;margin-top:12px;margin-bottom:12px;flex-direction:unset;text-align:left} +.sdpi-item[type="group"]>.sdpi-item-label,.sdpi-item[type="group"].sdpi-item-label{width:96%;text-align:left;font-weight:700;margin-bottom:4px;padding-left:4px} +dl,ul,ol{-webkit-margin-before:0px;-webkit-margin-after:4px;-webkit-padding-start:1em;max-height:90px;overflow-y:scroll;cursor:pointer;user-select:none} +table.sdpi-item-value,dl.sdpi-item-value,ul.sdpi-item-value,ol.sdpi-item-value{-webkit-margin-before:4px;-webkit-margin-after:8px;-webkit-padding-start:1em;width:var(--sdpi-width);text-align:center} +table>caption{margin:2px} +.list,.sdpi-item[type="list"]{align-items:baseline} +.sdpi-item-label{text-align:right;flex:none;width:94px;padding-right:4px;font-weight:600} +.win .sdpi-item-label,.sdpi-item-label>small{font-weight:normal} +.sdpi-item-label:after{content:":"} +.sdpi-item-label.empty:after{content:""} +.sdpi-test,.sdpi-item-value{flex:1 0 0;margin-right:14px;margin-left:4px;justify-content:space-evenly} +canvas.sdpi-item-value{max-width:144px;max-height:144px;width:144px;height:144px;margin:0 auto;cursor:pointer} +input.sdpi-item-value{margin-left:5px} +.sdpi-item-value button,button.sdpi-item-value{margin-left:6px;margin-right:14px} +.sdpi-item-value.range{margin-left:0px} +table,dl.sdpi-item-value,ul.sdpi-item-value,ol.sdpi-item-value,.sdpi-item-value>dl,.sdpi-item-value>ul,.sdpi-item-value>ol{list-style-type:none;list-style-position:outside;margin-left:-4px;margin-right:-4px;padding:4px;border:1px solid var(--sdpi-bordercolor)} +dl.sdpi-item-value,ul.sdpi-item-value,ol.sdpi-item-value,.sdpi-item-value>ol{list-style-type:none;list-style-position:inside;margin-left:5px;margin-right:12px;padding:4px !important;display:flex;flex-direction:column} +.two-items li{display:flex} +.two-items li>*:first-child{flex:0 0 50%;text-align:left} +.two-items.thirtyseventy li>*:first-child{flex:0 0 30%} +ol.sdpi-item-value,.sdpi-item-value>ol[listtype="none"]{list-style-type:none} +ol.sdpi-item-value[type="decimal"],.sdpi-item-value>ol[type="decimal"]{list-style-type:decimal} +ol.sdpi-item-value[type="decimal-leading-zero"],.sdpi-item-value>ol[type="decimal-leading-zero"]{list-style-type:decimal-leading-zero} +ol.sdpi-item-value[type="lower-alpha"],.sdpi-item-value>ol[type="lower-alpha"]{list-style-type:lower-alpha} +ol.sdpi-item-value[type="upper-alpha"],.sdpi-item-value>ol[type="upper-alpha"]{list-style-type:upper-alpha} +ol.sdpi-item-value[type="upper-roman"],.sdpi-item-value>ol[type="upper-roman"]{list-style-type:upper-roman} +ol.sdpi-item-value[type="lower-roman"],.sdpi-item-value>ol[type="lower-roman"]{list-style-type:upper-roman} +tr:nth-child(even),.sdpi-item-value>ul>li:nth-child(even),.sdpi-item-value>ol>li:nth-child(even),li:nth-child(even){background-color:rgba(0,0,0,.2)} +td:hover,.sdpi-item-value>ul>li:hover:nth-child(even),.sdpi-item-value>ol>li:hover:nth-child(even),li:hover:nth-child(even),li:hover{background-color:rgba(255,255,255,.1)} +td.selected,td.selected:hover,li.selected:hover,li.selected{color:white;background-color:#77f} +tr{border:1px solid var(--sdpi-bordercolor)} +td{border-right:1px solid var(--sdpi-bordercolor)} +tr:last-child,td:last-child{border:none} +.sdpi-item-value.select,.sdpi-item-value>select{margin-right:13px;margin-left:4px} +.sdpi-item-child,.sdpi-item-group>.sdpi-item>input[type="color"]{margin-top:0.4em;margin-right:4px} +.full,.full *,.sdpi-item-value.full,.sdpi-item-child>full>*,.sdpi-item-child.full,.sdpi-item-child.full>*,.full>.sdpi-item-child,.full>.sdpi-item-child>*{display:flex;flex:1 1 0;margin-bottom:4px;margin-left:0px;width:100%;justify-content:space-evenly} +.sdpi-item-group>.sdpi-item>input[type="color"]{margin-top:0px} +::-webkit-calendar-picker-indicator:focus,input[type=file]::-webkit-file-upload-button:focus,button:focus,textarea:focus,input:focus,select:focus,option:focus,details:focus,summary:focus,.custom-select select{outline:none} +summary{cursor:default;padding-left:90px;padding-right:70px} +.pointer,summary .pointer{cursor:pointer} +details *{font-size:12px;font-weight:normal;word-break:break-all;} +details.message{padding:4px 18px 4px 12px} +details.message summary{min-height:18px} +details.message:first-child{margin-top:4px;margin-left:0;padding-left:102px} +details.message h1{text-align:left} +.message>summary::-webkit-details-marker{display:none} +.info20,.question,.caution,.info{background-repeat:no-repeat;background-position:72px center} +.info20{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,20 C4.4771525,20 0,15.5228475 0,10 C0,4.4771525 4.4771525,0 10,0 C15.5228475,0 20,4.4771525 20,10 C20,15.5228475 15.5228475,20 10,20 Z M10,8 C8.8954305,8 8,8.84275812 8,9.88235294 L8,16.1176471 C8,17.1572419 8.8954305,18 10,18 C11.1045695,18 12,17.1572419 12,16.1176471 L12,9.88235294 C12,8.84275812 11.1045695,8 10,8 Z M10,3 C8.8954305,3 8,3.88165465 8,4.96923077 L8,5.03076923 C8,6.11834535 8.8954305,7 10,7 C11.1045695,7 12,6.11834535 12,5.03076923 L12,4.96923077 C12,3.88165465 11.1045695,3 10,3 Z'/%3E%3C/svg%3E%0A")} +.info{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,18 C5.581722,18 2,14.418278 2,10 C2,5.581722 5.581722,2 10,2 C14.418278,2 18,5.581722 18,10 C18,14.418278 14.418278,18 10,18 Z M10,8 C9.44771525,8 9,8.42137906 9,8.94117647 L9,14.0588235 C9,14.5786209 9.44771525,15 10,15 C10.5522847,15 11,14.5786209 11,14.0588235 L11,8.94117647 C11,8.42137906 10.5522847,8 10,8 Z M10,5 C9.44771525,5 9,5.44082732 9,5.98461538 L9,6.01538462 C9,6.55917268 9.44771525,7 10,7 C10.5522847,7 11,6.55917268 11,6.01538462 L11,5.98461538 C11,5.44082732 10.5522847,5 10,5 Z'/%3E%3C/svg%3E%0A")} +.info2{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 15 15'%3E%3Cpath fill='%23999' d='M7.5,15 C3.35786438,15 0,11.6421356 0,7.5 C0,3.35786438 3.35786438,0 7.5,0 C11.6421356,0 15,3.35786438 15,7.5 C15,11.6421356 11.6421356,15 7.5,15 Z M7.5,2 C6.67157287,2 6,2.66124098 6,3.47692307 L6,3.52307693 C6,4.33875902 6.67157287,5 7.5,5 C8.32842705,5 9,4.33875902 9,3.52307693 L9,3.47692307 C9,2.66124098 8.32842705,2 7.5,2 Z M5,6 L5,7.02155172 L6,7 L6,12 L5,12.0076778 L5,13 L10,13 L10,12 L9,12.0076778 L9,6 L5,6 Z'/%3E%3C/svg%3E%0A")} +.sdpi-more-info{background-image:linear-gradient(to right,#00000000 0%,#00000040 80%),url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpolygon fill='%23999' points='4 7 8 7 8 5 12 8 8 11 8 9 4 9'/%3E%3C/svg%3E%0A")} +.caution{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' fill-rule='evenodd' d='M9.03952676,0.746646542 C9.57068894,-0.245797319 10.4285735,-0.25196227 10.9630352,0.746646542 L19.7705903,17.2030214 C20.3017525,18.1954653 19.8777595,19 18.8371387,19 L1.16542323,19 C0.118729947,19 -0.302490098,18.2016302 0.231971607,17.2030214 L9.03952676,0.746646542 Z M10,2.25584053 L1.9601405,17.3478261 L18.04099,17.3478261 L10,2.25584053 Z M10,5.9375 C10.531043,5.9375 10.9615385,6.37373537 10.9615385,6.91185897 L10.9615385,11.6923077 C10.9615385,12.2304313 10.531043,12.6666667 10,12.6666667 C9.46895697,12.6666667 9.03846154,12.2304313 9.03846154,11.6923077 L9.03846154,6.91185897 C9.03846154,6.37373537 9.46895697,5.9375 10,5.9375 Z M10,13.4583333 C10.6372516,13.4583333 11.1538462,13.9818158 11.1538462,14.6275641 L11.1538462,14.6641026 C11.1538462,15.3098509 10.6372516,15.8333333 10,15.8333333 C9.36274837,15.8333333 8.84615385,15.3098509 8.84615385,14.6641026 L8.84615385,14.6275641 C8.84615385,13.9818158 9.36274837,13.4583333 10,13.4583333 Z'/%3E%3C/svg%3E%0A")} +.question{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,18 C5.581722,18 2,14.418278 2,10 C2,5.581722 5.581722,2 10,2 C14.418278,2 18,5.581722 18,10 C18,14.418278 14.418278,18 10,18 Z M6.77783203,7.65332031 C6.77783203,7.84798274 6.85929281,8.02888914 7.0222168,8.19604492 C7.18514079,8.36320071 7.38508996,8.44677734 7.62207031,8.44677734 C8.02409055,8.44677734 8.29703704,8.20768468 8.44091797,7.72949219 C8.59326248,7.27245865 8.77945854,6.92651485 8.99951172,6.69165039 C9.2195649,6.45678594 9.56233491,6.33935547 10.027832,6.33935547 C10.4256205,6.33935547 10.7006836,6.37695313 11.0021973,6.68847656 C11.652832,7.53271484 10.942627,8.472229 10.3750916,9.1321106 C9.80755615,9.79199219 8.29492188,11.9897461 10.027832,12.1347656 C10.4498423,12.1700818 10.7027991,11.9147157 10.7832031,11.4746094 C11.0021973,9.59857178 13.1254883,8.82415771 13.1254883,7.53271484 C13.1254883,7.07568131 12.9974785,6.65250846 12.7414551,6.26318359 C12.4854317,5.87385873 12.1225609,5.56600048 11.652832,5.33959961 C11.1831031,5.11319874 10.6414419,5 10.027832,5 C9.36767248,5 8.79004154,5.13541531 8.29492187,5.40625 C7.79980221,5.67708469 7.42317837,6.01879677 7.16503906,6.43139648 C6.90689975,6.8439962 6.77783203,7.25130007 6.77783203,7.65332031 Z M10.0099668,15 C10.2713191,15 10.5016601,14.9108147 10.7009967,14.7324415 C10.9003332,14.5540682 11,14.3088087 11,13.9966555 C11,13.7157177 10.9047629,13.4793767 10.7142857,13.2876254 C10.5238086,13.0958742 10.2890379,13 10.0099668,13 C9.72646591,13 9.48726565,13.0958742 9.2923588,13.2876254 C9.09745196,13.4793767 9,13.7157177 9,13.9966555 C9,14.313268 9.10077419,14.5596424 9.30232558,14.735786 C9.50387698,14.9119295 9.73975502,15 10.0099668,15 Z'/%3E%3C/svg%3E%0A")} +.sdpi-more-info{position:fixed;left:0px;right:0px;bottom:0px;min-height:16px;padding-right:16px;text-align:right;-webkit-touch-callout:none;cursor:pointer;user-select:none;background-position:right center;background-repeat:no-repeat;border-radius:var(--sdpi-borderradius);text-decoration:none;color:var(--sdpi-color)} +.sdpi-more-info-button{display:flex;align-self:right;margin-left:auto;position:fixed;right:17px;bottom:0px;user-select:none} +.sdpi-bottom-bar{display:flex;align-self:right;margin-left:auto;position:fixed;right:17px;bottom:0px;user-select:none} +.sdpi-bottom-bar.right{right:0px} +.sdpi-bottom-bar button{min-height:20px !important;height:20px !important} +details a{background-position:right !important;min-height:24px;display:inline-block;line-height:24px;padding-right:28px} +input:not([type="range"]),textarea{-webkit-appearance:none;appearance:none;background:var(--sdpi-background);color:var(--sdpi-color);font-weight:normal;font-size:9pt;border:none;margin-top:2px;margin-bottom:2px;min-width:219px} +textarea+label{display:flex;justify-content:flex-end} +input[type="radio"],input[type="checkbox"]{display:none} +input[type="radio"]+label,input[type="checkbox"]+label{font-size:9pt;color:var(--sdpi-color);font-weight:normal;margin-right:8px} +input[type="radio"]+label:after,input[type="checkbox"]+label:after{content:" " !important} +.sdpi-item[type="radio"]>.sdpi-item-value,.sdpi-item[type="checkbox"]>.sdpi-item-value{padding-top:2px} +.sdpi-item[type="checkbox"]>.sdpi-item-value>*{margin-top:4px} +.sdpi-item[type="checkbox"] .sdpi-item-child,.sdpi-item[type="radio"] .sdpi-item-child{display:inline-block} +.sdpi-item[type="range"] .sdpi-item-value,.sdpi-item[type="meter"] .sdpi-item-child,.sdpi-item[type="progress"] .sdpi-item-child{display:flex} +.sdpi-item[type="range"] .sdpi-item-value{min-height:26px} +.sdpi-item[type="range"] .sdpi-item-value span,.sdpi-item[type="meter"] .sdpi-item-child span,.sdpi-item[type="progress"] .sdpi-item-child span{margin-top:-2px;min-width:8px;text-align:right;user-select:none;cursor:pointer;-webkit-user-select:none;user-select:none} +.sdpi-item[type="range"] .sdpi-item-value span{margin-top:7px;text-align:right} +span+input[type="range"]{display:flex;max-width:168px} +.sdpi-item[type="range"] .sdpi-item-value span:first-child,.sdpi-item[type="meter"] .sdpi-item-child span:first-child,.sdpi-item[type="progress"] .sdpi-item-child span:first-child{margin-right:4px} +.sdpi-item[type="range"] .sdpi-item-value span:last-child,.sdpi-item[type="meter"] .sdpi-item-child span:last-child,.sdpi-item[type="progress"] .sdpi-item-child span:last-child{margin-left:4px} +.reverse{transform:rotate(180deg)} +.sdpi-item[type="meter"] .sdpi-item-child meter+span:last-child{margin-left:-10px} +.sdpi-item[type="progress"] .sdpi-item-child meter+span:last-child{margin-left:-14px} +.sdpi-item[type="radio"]>.sdpi-item-value>*{margin-top:2px} +details{padding:8px 18px 8px 12px;min-width:86px} +details>h4{border-bottom:1px solid var(--sdpi-bordercolor)} +legend{display:none} +.sdpi-item-value>textarea{padding:0px;width:219px;margin-left:1px;margin-top:3px;padding:4px} +input[type="radio"]+label span,input[type="checkbox"]+label span{display:inline-block;width:16px;height:16px;margin:2px 4px 2px 0;border-radius:3px;vertical-align:middle;background:var(--sdpi-background);cursor:pointer;border:1px solid rgb(0,0,0,.2)} +input[type="radio"]+label span{border-radius:100%} +input[type="radio"]:checked+label span,input[type="checkbox"]:checked+label span{background-color:#77f;background-image:url(check.svg);background-repeat:no-repeat;background-position:center center;border:1px solid rgb(0,0,0,.4)} +input[type="radio"]:active:checked+label span,input[type="radio"]:active+label span,input[type="checkbox"]:active:checked+label span,input[type="checkbox"]:active+label span{background-color:#303030} +input[type="radio"]:checked+label span{background-image:url(rcheck.svg)} +input[type="range"]{width:var(--sdpi-width);height:30px;overflow:hidden;cursor:pointer;background:transparent !important} +.sdpi-item>input[type="range"]{margin-left:2px;max-width:var(--sdpi-width);width:var(--sdpi-width);padding:0px;margin-top:2px} +input[type="range"]::-webkit-slider-runnable-track{height:5px;background:#979797;border-radius:3px;padding:0px !important;border:1px solid var(--sdpi-background)} +input[type="range"]::-webkit-slider-thumb{position:relative;-webkit-appearance:none;background-color:var(--sdpi-color);width:12px;height:12px;border-radius:20px;margin-top:-5px;border:none} +input[type="range" i]{margin:0} +input[type="range"]::-webkit-slider-thumb::before{position:absolute;content:"";height:5px;width:500px;left:-502px;top:8px;background:#77f} +input[type="color"]{min-width:32px;min-height:32px;width:32px;height:32px;padding:0;background-color:var(--sdpi-bgcolor);flex:none} +::-webkit-color-swatch{min-width:24px} +textarea{height:3em;word-break:break-word;line-height:1.5em} +.textarea{padding:0px !important} +textarea{width:219px;height:96%;min-height:6em;resize:none;border-radius:var(--sdpi-borderradius)} +.sdpi-item.card-carousel-wrapper,.sdpi-item>.card-carousel-wrapper{padding:0} +.card-carousel-wrapper{display:flex;align-items:center;justify-content:center;margin:12px auto;color:#666a73} +.card-carousel{display:flex;justify-content:center;width:278px} +.card-carousel--overflow-container{overflow:hidden} +.card-carousel--nav__left,.card-carousel--nav__right{width:12px;height:12px;border-top:2px solid #42b883;border-right:2px solid #42b883;cursor:pointer;margin:0 4px;transition:transform 150ms linear} +.card-carousel--nav__left[disabled],.card-carousel--nav__right[disabled]{opacity:0.2;border-color:black} +.card-carousel--nav__left{transform:rotate(-135deg)} +.card-carousel--nav__left:active{transform:rotate(-135deg) scale(0.85)} +.card-carousel--nav__right{transform:rotate(45deg)} +.card-carousel--nav__right:active{transform:rotate(45deg) scale(0.85)} +.card-carousel-cards{display:flex;transition:transform 150ms ease-out;transform:translatex(0px)} +.card-carousel-cards .card-carousel--card{margin:0 5px;cursor:pointer;background-color:#fff;border-radius:4px;z-index:3} +.xxcard-carousel-cards .card-carousel--card:first-child{margin-left:0} +.xxcard-carousel-cards .card-carousel--card:last-child{margin-right:0} +.card-carousel-cards .card-carousel--card img{vertical-align:bottom;border-top-left-radius:4px;border-top-right-radius:4px;transition:opacity 150ms linear;width:60px} +.card-carousel-cards .card-carousel--card img:hover{opacity:0.5} +.card-carousel-cards .card-carousel--card--footer{border-top:0;max-width:80px;overflow:hidden;display:flex;height:100%;flex-direction:column} +.card-carousel-cards .card-carousel--card--footer p{padding:3px 0;margin:0;margin-bottom:2px;font-size:15px;font-weight:500;color:#2c3e50} +.card-carousel-cards .card-carousel--card--footer p:nth-of-type(2){font-size:12px;font-weight:300;padding:6px;color:#666a73} +h1{font-size:1.3em;font-weight:500;text-align:center;margin-bottom:12px} +::-webkit-datetime-edit{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";background:url(elg_calendar_inv.svg) no-repeat left center;padding-right:1em;padding-left:25px;background-position:4px 0px} +::-webkit-calendar-picker-indicator{background:transparent;font-size:17px} +::-webkit-calendar-picker-indicator:focus{background-color:rgba(0,0,0,0.2)} +input[type="date"]{-webkit-align-items:center;align-items:center;display:-webkit-inline-flex;font-family:monospace;overflow:hidden;padding:0;-webkit-padding-start:1px} +input::-webkit-datetime-edit{-webkit-flex:1;-webkit-user-modify:read-only !important;display:inline-block;min-width:0;overflow:hidden} +input[type="file"]{opacity:0;display:none} +.sdpi-item>input[type="file"]{opacity:1;display:flex} +input[type="file"]+span{display:flex;flex:0 1 auto;background-color:#0000ff50} +label.sdpi-file-label{cursor:pointer;user-select:none;display:inline-block;min-height:21px !important;height:21px !important;line-height:20px;padding:0px 4px;margin:auto;margin-right:0px} +.sdpi-file-label>label:active,.sdpi-file-label.file:active,label.sdpi-file-label:active,label.sdpi-file-info:active,input[type="file"]::-webkit-file-upload-button:active,button:active{background-color:var(--sdpi-color);color:#303030} +input:required:invalid,input:focus:invalid{background:var(--sdpi-background) url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5IiBoZWlnaHQ9IjkiIHZpZXdCb3g9IjAgMCA5IDkiPgogICAgPHBhdGggZmlsbD0iI0Q4RDhEOCIgZD0iTTQuNSwwIEM2Ljk4NTI4MTM3LC00LjU2NTM4NzgyZS0xNiA5LDIuMDE0NzE4NjMgOSw0LjUgQzksNi45ODUyODEzNyA2Ljk4NTI4MTM3LDkgNC41LDkgQzIuMDE0NzE4NjMsOSAzLjA0MzU5MTg4ZS0xNiw2Ljk4NTI4MTM3IDAsNC41IEMtMy4wNDM1OTE4OGUtMTYsMi4wMTQ3MTg2MyAyLjAxNDcxODYzLDQuNTY1Mzg3ODJlLTE2IDQuNSwwIFogTTQsMSBMNCw2IEw1LDYgTDUsMSBMNCwxIFogTTQuNSw4IEM0Ljc3NjE0MjM3LDggNSw3Ljc3NjE0MjM3IDUsNy41IEM1LDcuMjIzODU3NjMgNC43NzYxNDIzNyw3IDQuNSw3IEM0LjIyMzg1NzYzLDcgNCw3LjIyMzg1NzYzIDQsNy41IEM0LDcuNzc2MTQyMzcgNC4yMjM4NTc2Myw4IDQuNSw4IFoiLz4KICA8L3N2Zz4) no-repeat 98% center} +input:required:valid{background:var(--sdpi-background) url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5IiBoZWlnaHQ9IjkiIHZpZXdCb3g9IjAgMCA5IDkiPjxwb2x5Z29uIGZpbGw9IiNEOEQ4RDgiIHBvaW50cz0iNS4yIDEgNi4yIDEgNi4yIDcgMy4yIDcgMy4yIDYgNS4yIDYiIHRyYW5zZm9ybT0icm90YXRlKDQwIDQuNjc3IDQpIi8+PC9zdmc+) no-repeat 98% center} +.tooltip,:tooltip,:title{color:yellow} +.sdpi-item-group.file{width:232px;display:flex;align-items:center} +.sdpi-file-info{overflow-wrap:break-word;word-wrap:break-word;hyphens:auto;min-width:132px;max-width:144px;max-height:32px;margin-top:0px;margin-left:5px;display:inline-block;overflow:hidden;padding:6px 4px;background-color:var(--sdpi-background)} +::-webkit-scrollbar{width:8px} +::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 6px rgba(0,0,0,0.3)} +::-webkit-scrollbar-thumb{background-color:#999999;outline:1px solid slategrey;border-radius:8px} +a{color:#7397d2} +.testcontainer{display:flex;background-color:#0000ff20;max-width:400px;height:200px;align-content:space-evenly} +input[type=range]{-webkit-appearance:none;appearance:none;height:6px;margin-top:12px;z-index:0;overflow:visible} +:-webkit-slider-thumb{-webkit-appearance:none;background-color:var(--sdpi-color);width:16px;height:16px;border-radius:20px;margin-top:-6px;border:1px solid #999999} +.sdpi-item[type="range"] .sdpi-item-group{display:flex;flex-direction:column} +.xxsdpi-item[type="range"] .sdpi-item-group input{max-width:204px} +.sdpi-item[type="range"] .sdpi-item-group span{margin-left:0px !important} +.sdpi-item[type="range"] .sdpi-item-group>.sdpi-item-child{display:flex;flex-direction:row} +.rangeLabel{position:absolute;font-weight:normal;margin-top:22px} +:disabled{color:#993333} +select,select option{color:var(--sdpi-color)} +select.disabled,select option:disabled{color:#fd9494;font-style:italic} +.runningAppsContainer{display:none} +.one-line{min-height:1.5em} +.two-lines{min-height:3em} +.three-lines{min-height:4.5em} +.four-lines{min-height:6em} +.min80>.sdpi-item-child{min-width:80px} +.min100>.sdpi-item-child{min-width:100px} +.min120>.sdpi-item-child{min-width:120px} +.min140>.sdpi-item-child{min-width:140px} +.min160>.sdpi-item-child{min-width:160px} +.min200>.sdpi-item-child{min-width:200px} +.max40{flex-basis:40%;flex-grow:0} +.max30{flex-basis:30%;flex-grow:0} +.max20{flex-basis:20%;flex-grow:0} +.up20{margin-top:-20px} +.alignCenter{align-items:center} +.alignTop{align-items:flex-start} +.alignBaseline{align-items:baseline} +.noMargins,.noMargins *,.noInnerMargins *{margin:0;padding:0} +.hidden{display:none} +.icon-help,.icon-help-line,.icon-help-fill,.icon-help-inv,.icon-brighter,.icon-darker,.icon-warmer,.icon-cooler{min-width:20px;width:20px;background-repeat:no-repeat;opacity:1} +.icon-help:active,.icon-help-line:active,.icon-help-fill:active,.icon-help-inv:active,.icon-brighter:active,.icon-darker:active,.icon-warmer:active,.icon-cooler:active{opacity:0.5} +.icon-brighter,.icon-darker,.icon-warmer,.icon-cooler{margin-top:5px !important} +.icon-help,.icon-help-line,.icon-help-fill,.icon-help-inv{cursor:pointer;margin:0px;margin-left:4px} +.icon-brighter{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Ccircle cx='10' cy='10' r='4'/%3E%3Cpath d='M14.8532861,7.77530426 C14.7173255,7.4682615 14.5540843,7.17599221 14.3666368,6.90157083 L16.6782032,5.5669873 L17.1782032,6.4330127 L14.8532861,7.77530426 Z M10.5,4.5414007 C10.2777625,4.51407201 10.051423,4.5 9.82179677,4.5 C9.71377555,4.5 9.60648167,4.50311409 9.5,4.50925739 L9.5,2 L10.5,2 L10.5,4.5414007 Z M5.38028092,6.75545367 C5.18389364,7.02383457 5.01124349,7.31068015 4.86542112,7.61289977 L2.82179677,6.4330127 L3.32179677,5.5669873 L5.38028092,6.75545367 Z M4.86542112,12.3871002 C5.01124349,12.6893198 5.18389364,12.9761654 5.38028092,13.2445463 L3.32179677,14.4330127 L2.82179677,13.5669873 L4.86542112,12.3871002 Z M9.5,15.4907426 C9.60648167,15.4968859 9.71377555,15.5 9.82179677,15.5 C10.051423,15.5 10.2777625,15.485928 10.5,15.4585993 L10.5,18 L9.5,18 L9.5,15.4907426 Z M14.3666368,13.0984292 C14.5540843,12.8240078 14.7173255,12.5317385 14.8532861,12.2246957 L17.1782032,13.5669873 L16.6782032,14.4330127 L14.3666368,13.0984292 Z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-darker{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10 14C7.790861 14 6 12.209139 6 10 6 7.790861 7.790861 6 10 6 12.209139 6 14 7.790861 14 10 14 12.209139 12.209139 14 10 14zM10 13C11.6568542 13 13 11.6568542 13 10 13 8.34314575 11.6568542 7 10 7 8.34314575 7 7 8.34314575 7 10 7 11.6568542 8.34314575 13 10 13zM14.8532861 7.77530426C14.7173255 7.4682615 14.5540843 7.17599221 14.3666368 6.90157083L16.6782032 5.5669873 17.1782032 6.4330127 14.8532861 7.77530426zM10.5 4.5414007C10.2777625 4.51407201 10.051423 4.5 9.82179677 4.5 9.71377555 4.5 9.60648167 4.50311409 9.5 4.50925739L9.5 2 10.5 2 10.5 4.5414007zM5.38028092 6.75545367C5.18389364 7.02383457 5.01124349 7.31068015 4.86542112 7.61289977L2.82179677 6.4330127 3.32179677 5.5669873 5.38028092 6.75545367zM4.86542112 12.3871002C5.01124349 12.6893198 5.18389364 12.9761654 5.38028092 13.2445463L3.32179677 14.4330127 2.82179677 13.5669873 4.86542112 12.3871002zM9.5 15.4907426C9.60648167 15.4968859 9.71377555 15.5 9.82179677 15.5 10.051423 15.5 10.2777625 15.485928 10.5 15.4585993L10.5 18 9.5 18 9.5 15.4907426zM14.3666368 13.0984292C14.5540843 12.8240078 14.7173255 12.5317385 14.8532861 12.2246957L17.1782032 13.5669873 16.6782032 14.4330127 14.3666368 13.0984292z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-warmer{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M12.3247275 11.4890349C12.0406216 11.0007637 11.6761954 10.5649925 11.2495475 10.1998198 11.0890394 9.83238991 11 9.42659309 11 9 11 7.34314575 12.3431458 6 14 6 15.6568542 6 17 7.34314575 17 9 17 10.6568542 15.6568542 12 14 12 13.3795687 12 12.8031265 11.8116603 12.3247275 11.4890349zM17.6232392 11.6692284C17.8205899 11.4017892 17.9890383 11.1117186 18.123974 10.8036272L20.3121778 12.0669873 19.8121778 12.9330127 17.6232392 11.6692284zM18.123974 7.19637279C17.9890383 6.88828142 17.8205899 6.5982108 17.6232392 6.33077158L19.8121778 5.0669873 20.3121778 5.9330127 18.123974 7.19637279zM14.5 4.52746439C14.3358331 4.50931666 14.1690045 4.5 14 4.5 13.8309955 4.5 13.6641669 4.50931666 13.5 4.52746439L13.5 2 14.5 2 14.5 4.52746439zM13.5 13.4725356C13.6641669 13.4906833 13.8309955 13.5 14 13.5 14.1690045 13.5 14.3358331 13.4906833 14.5 13.4725356L14.5 16 13.5 16 13.5 13.4725356zM14 11C15.1045695 11 16 10.1045695 16 9 16 7.8954305 15.1045695 7 14 7 12.8954305 7 12 7.8954305 12 9 12 10.1045695 12.8954305 11 14 11zM9.5 11C10.6651924 11.4118364 11.5 12.5 11.5 14 11.5 16 10 17.5 8 17.5 6 17.5 4.5 16 4.5 14 4.5 12.6937812 5 11.5 6.5 11L6.5 7 9.5 7 9.5 11z'/%3E%3Cpath d='M12,14 C12,16.209139 10.209139,18 8,18 C5.790861,18 4,16.209139 4,14 C4,12.5194353 4.80439726,11.2267476 6,10.5351288 L6,4 C6,2.8954305 6.8954305,2 8,2 C9.1045695,2 10,2.8954305 10,4 L10,10.5351288 C11.1956027,11.2267476 12,12.5194353 12,14 Z M11,14 C11,12.6937812 10.1651924,11.5825421 9,11.1707057 L9,4 C9,3.44771525 8.55228475,3 8,3 C7.44771525,3 7,3.44771525 7,4 L7,11.1707057 C5.83480763,11.5825421 5,12.6937812 5,14 C5,15.6568542 6.34314575,17 8,17 C9.65685425,17 11,15.6568542 11,14 Z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-cooler{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10.4004569 11.6239517C10.0554735 10.9863849 9.57597206 10.4322632 9 9.99963381L9 9.7450467 9.53471338 9.7450467 10.8155381 8.46422201C10.7766941 8.39376637 10.7419749 8.32071759 10.7117062 8.2454012L9 8.2454012 9 6.96057868 10.6417702 6.96057868C10.6677696 6.86753378 10.7003289 6.77722682 10.7389179 6.69018783L9.44918707 5.40045694 9 5.40045694 9 4.34532219 9.32816127 4.34532219 9.34532219 2.91912025 10.4004569 2.91912025 10.4004569 4.53471338 11.6098599 5.74411634C11.7208059 5.68343597 11.8381332 5.63296451 11.9605787 5.59396526L11.9605787 3.8884898 10.8181818 2.74609294 11.5642748 2 12.5727518 3.00847706 13.5812289 2 14.3273218 2.74609294 13.2454012 3.82801356 13.2454012 5.61756719C13.3449693 5.65339299 13.4408747 5.69689391 13.5324038 5.74735625L14.7450467 4.53471338 14.7450467 2.91912025 15.8001815 2.91912025 15.8001815 4.34532219 17.2263834 4.34532219 17.2263834 5.40045694 15.6963166 5.40045694 14.4002441 6.69652946C14.437611 6.78161093 14.4692249 6.86979146 14.4945934 6.96057868L16.2570138 6.96057868 17.3994107 5.81818182 18.1455036 6.56427476 17.1370266 7.57275182 18.1455036 8.58122888 17.3994107 9.32732182 16.3174901 8.2454012 14.4246574 8.2454012C14.3952328 8.31861737 14.3616024 8.38969062 14.3240655 8.45832192L15.6107903 9.7450467 17.2263834 9.7450467 17.2263834 10.8001815 15.8001815 10.8001815 15.8001815 12.2263834 14.7450467 12.2263834 14.7450467 10.6963166 13.377994 9.32926387C13.3345872 9.34850842 13.2903677 9.36625331 13.2454012 9.38243281L13.2454012 11.3174901 14.3273218 12.3994107 13.5812289 13.1455036 12.5848864 12.1491612 11.5642748 13.1455036 10.8181818 12.3994107 11.9605787 11.2570138 11.9605787 9.40603474C11.8936938 9.38473169 11.828336 9.36000556 11.7647113 9.33206224L10.4004569 10.6963166 10.4004569 11.6239517zM12.75 8.5C13.3022847 8.5 13.75 8.05228475 13.75 7.5 13.75 6.94771525 13.3022847 6.5 12.75 6.5 12.1977153 6.5 11.75 6.94771525 11.75 7.5 11.75 8.05228475 12.1977153 8.5 12.75 8.5zM9.5 14C8.5 16.3333333 7.33333333 17.5 6 17.5 4.66666667 17.5 3.5 16.3333333 2.5 14L9.5 14z'/%3E%3Cpath d='M10,14 C10,16.209139 8.209139,18 6,18 C3.790861,18 2,16.209139 2,14 C2,12.5194353 2.80439726,11.2267476 4,10.5351288 L4,4 C4,2.8954305 4.8954305,2 6,2 C7.1045695,2 8,2.8954305 8,4 L8,10.5351288 C9.19560274,11.2267476 10,12.5194353 10,14 Z M9,14 C9,12.6937812 8.16519237,11.5825421 7,11.1707057 L7,4 C7,3.44771525 6.55228475,3 6,3 C5.44771525,3 5,3.44771525 5,4 L5,11.1707057 C3.83480763,11.5825421 3,12.6937812 3,14 C3,15.6568542 4.34314575,17 6,17 C7.65685425,17 9,15.6568542 9,14 Z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-help{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cpath fill='%23999' d='M11.292 12.516l.022 1.782H9.07v-1.804c0-1.98 1.276-2.574 2.662-3.278h-.022c.814-.44 1.65-.88 1.694-2.2.044-1.386-1.122-2.728-3.234-2.728-1.518 0-2.662.902-3.366 2.354L5 5.608C5.946 3.584 7.662 2 10.17 2c3.564 0 5.632 2.442 5.588 5.06-.066 2.618-1.716 3.41-3.102 4.158-.704.374-1.364.682-1.364 1.298zm-1.122 2.442c.858 0 1.452.594 1.452 1.452 0 .682-.594 1.408-1.452 1.408-.77 0-1.386-.726-1.386-1.408 0-.858.616-1.452 1.386-1.452z'/%3E%3C/svg%3E")} +.icon-help-line{background-image:url("data:image/svg+xml,%3Csvg width='20' height='20' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-1a9 9 0 1 0 0-18 9 9 0 0 0 0 18z'/%3E%3Cpath d='M10.848 12.307l.02 1.578H8.784v-1.597c0-1.753 1.186-2.278 2.474-2.901h-.02c.756-.39 1.533-.78 1.574-1.948.041-1.226-1.043-2.414-3.006-2.414-1.41 0-2.474.798-3.128 2.083L5 6.193C5.88 4.402 7.474 3 9.805 3 13.118 3 15.04 5.161 15 7.478c-.061 2.318-1.595 3.019-2.883 3.68-.654.332-1.268.604-1.268 1.15zM9.805 14.47c.798 0 1.35.525 1.35 1.285 0 .603-.552 1.246-1.35 1.246-.715 0-1.288-.643-1.288-1.246 0-.76.573-1.285 1.288-1.285z' fill-rule='nonzero'/%3E%3C/g%3E%3C/svg%3E")} +.icon-help-fill{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Ccircle cx='10' cy='10' r='10' fill='%23999'/%3E%3Cpath fill='%23FFF' fill-rule='nonzero' d='M8.368 7.189H5C5 3.5 7.668 2 10.292 2 13.966 2 16 4.076 16 7.012c0 3.754-3.849 3.136-3.849 5.211v1.656H8.455v-1.832c0-2.164 1.4-2.893 2.778-3.6.437-.242 1.006-.574 1.006-1.236 0-2.208-3.871-2.142-3.871-.022zM10.25 18a1.75 1.75 0 1 1 0-3.5 1.75 1.75 0 0 1 0 3.5z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-help-inv{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cpath fill='%23999' fill-rule='evenodd' d='M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zM8.368 7.189c0-2.12 3.87-2.186 3.87.022 0 .662-.568.994-1.005 1.236-1.378.707-2.778 1.436-2.778 3.6v1.832h3.696v-1.656c0-2.075 3.849-1.457 3.849-5.21C16 4.075 13.966 2 10.292 2 7.668 2 5 3.501 5 7.189h3.368zM10.25 18a1.75 1.75 0 1 0 0-3.5 1.75 1.75 0 0 0 0 3.5z'/%3E%3C/svg%3E")} +.kelvin::after{content:"K"} +.mired::after{content:" Mired"} +.percent::after{content:"%"} +.sdpi-item-value+.icon-cooler,.sdpi-item-value+.icon-warmer{margin-left:0px !important;margin-top:15px !important} +input[type="range"].colorbrightness::-webkit-slider-runnable-track,input[type="range"].colortemperature::-webkit-slider-runnable-track{height:8px;background:#979797;border-radius:4px;background-image:linear-gradient(to right,#94d0ec,#ffb165)} +input[type="range"].colorbrightness::-webkit-slider-runnable-track{background-color:#efefef;background-image:linear-gradient(to right,black,rgba(0,0,0,0))} +input[type="range"].colorbrightness::-webkit-slider-thumb,input[type="range"].colortemperature::-webkit-slider-thumb{width:16px;height:16px;border-radius:20px;margin-top:-5px;background-color:#86c6e8;box-shadow:0px 0px 1px #000000;border:1px solid #d8d8d8} +.sdpi-info-label{display:inline-block;user-select:none;position:absolute;height:15px;width:auto;text-align:center;border-radius:4px;min-width:44px;max-width:80px;background:white;font-size:11px;color:black;z-index:1000;box-shadow:0px 0px 12px rgba(0,0,0,.8);padding:2px} +.sdpi-info-label.hidden{opacity:0;transition:opacity 0.25s linear} +.sdpi-info-label.shown{position:absolute;opacity:1;transition:opacity 0.25s ease-out} \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/default.jpg b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/default.jpg new file mode 100644 index 000000000..8061f9cfe --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/static/default.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb5c4d9b90e10680cb8f13a6c12ad0f426601fd2ee4130494fa06b6e6bb1677c +size 16885 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/zh_CN.json b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/zh_CN.json new file mode 100644 index 000000000..b85b2571b --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDK/unity-communication-plugin/zh_CN.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:686b48184fcd8189da15ab7bf71fb5529a033ef238298a074fb24513ca1681ab +size 231 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/.gitignore b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/.gitignore new file mode 100644 index 000000000..92880eef9 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/.gitignore @@ -0,0 +1,4 @@ +plugin/node_modules/** +plugin/log/ +plugin/build/ +plugin/data/ \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/de.json b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/de.json new file mode 100644 index 000000000..93b4ad0a0 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/de.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b74f7f82fe43323df30fd165e18482698fc38d190101678399b28c0c3affae63 +size 181 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/en.json b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/en.json new file mode 100644 index 000000000..bb47e0a5c --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/en.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6ed1e01915a66d36e53d8d9864a4e9143948e8e81a5a50112ded41041099234 +size 600 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/es.json b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/es.json new file mode 100644 index 000000000..93b4ad0a0 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/es.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b74f7f82fe43323df30fd165e18482698fc38d190101678399b28c0c3affae63 +size 181 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/fr.json b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/fr.json new file mode 100644 index 000000000..93b4ad0a0 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/fr.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b74f7f82fe43323df30fd165e18482698fc38d190101678399b28c0c3affae63 +size 181 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/it.json b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/it.json new file mode 100644 index 000000000..93b4ad0a0 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/it.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b74f7f82fe43323df30fd165e18482698fc38d190101678399b28c0c3affae63 +size 181 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/ja.json b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/ja.json new file mode 100644 index 000000000..93b4ad0a0 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/ja.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b74f7f82fe43323df30fd165e18482698fc38d190101678399b28c0c3affae63 +size 181 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/ko.json b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/ko.json new file mode 100644 index 000000000..93b4ad0a0 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/ko.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b74f7f82fe43323df30fd165e18482698fc38d190101678399b28c0c3affae63 +size 181 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/manifest.json b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/manifest.json new file mode 100644 index 000000000..f0d245409 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/manifest.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f20e152ca88282a328d204c57763cd4807ed6853e62de555396e8203abb6120c +size 1293 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/pl.json b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/pl.json new file mode 100644 index 000000000..93b4ad0a0 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/pl.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b74f7f82fe43323df30fd165e18482698fc38d190101678399b28c0c3affae63 +size 181 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/plugin/autofile.js b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/plugin/autofile.js new file mode 100644 index 000000000..38c45b652 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/plugin/autofile.js @@ -0,0 +1,47 @@ +const path = require('path'); +const fs = require('fs-extra'); + +console.log('开始执行自动化构建...'); + +const currentDir = __dirname; + +// 获取父文件夹的路径 +const parentDir = path.join(currentDir, '..'); +// 获取父文件夹的名称 +const PluginName = path.basename(parentDir); + + +const PluginPath = path.join(process.env.APPDATA, 'HotSpot/StreamDock/plugins', PluginName); + +try { + // 删除旧的插件目录 + fs.removeSync(PluginPath); + + // 确保目标目录存在 + fs.ensureDirSync(path.dirname(PluginPath)); + + // 复制当前目录到目标路径,排除 node_modules + fs.copySync(path.resolve(__dirname, '..'), PluginPath, { + filter: (src) => { + const relativePath = path.relative(path.resolve(__dirname, '..'), src); + // 排除 'node_modules' 和 '.git' 目录及其子文件 + return !relativePath.startsWith('plugin\\node_modules') + &&!relativePath.startsWith('plugin\\index.js') + &&!relativePath.startsWith('plugin\\package.json') + &&!relativePath.startsWith('plugin\\package-lock.json') + &&!relativePath.startsWith('plugin\\pnpm-lock.yaml') + &&!relativePath.startsWith('plugin\\yarn.lock') + &&!relativePath.startsWith('plugin\\build') + &&!relativePath.startsWith('plugin\\log') + &&!relativePath.startsWith('.git') + &&!relativePath.startsWith('.vscode'); + } + }); + + fs.copySync( path.join(__dirname, "build"), path.join(PluginPath,'plugin')) + + console.log(`插件 "${PluginName}" 已成功复制到 "${PluginPath}"`); + console.log('构建成功-------------'); +} catch (err) { + console.error(`复制出错 "${PluginName}":`, err); +} \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/plugin/index.js b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/plugin/index.js new file mode 100644 index 000000000..c40582031 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/plugin/index.js @@ -0,0 +1,42 @@ +const { Plugins, Actions, log, EventEmitter } = require('./utils/plugin'); +const { execSync } = require('child_process'); + +const plugin = new Plugins('demo'); + + +plugin.didReceiveGlobalSettings = ({ payload: { settings } }) => { + log.info('didReceiveGlobalSettings', settings); +}; + +const createSvg = (text) => ` + + ${text} + +`; +const timers = {}; + +plugin.demo = new Actions({ + default: { + }, + async _willAppear({ context, payload }) { + // log.info("demo: ", context); + let n = 0; + timers[context] = setInterval(async () => { + const svg = createSvg(++n); + plugin.setImage(context, `data:image/svg+xml;charset=utf8,${svg}`); + }, 1000); + }, + _willDisappear({ context }) { + // log.info('willDisAppear', context) + timers[context] && clearInterval(timers[context]); + }, + _propertyInspectorDidAppear({ context }) { + }, + sendToPlugin({ payload, context }) { + }, + keyUp({ context, payload }) { + }, + dialDown({ context, payload }) {}, + dialRotate({ context, payload }) {} +}); \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/plugin/package.json b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/plugin/package.json new file mode 100644 index 000000000..006d4239d --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/plugin/package.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f2c682faaa3cac41dac54d7d00ba6a748e57952accb55bcd851f61b32e051fa4 +size 374 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/plugin/utils/plugin.js b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/plugin/utils/plugin.js new file mode 100644 index 000000000..673de5c29 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/plugin/utils/plugin.js @@ -0,0 +1,213 @@ +// 配置日志文件 +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 = JSON.parse(process.argv[9]).application.language; + 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/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/demo/index.html b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/demo/index.html new file mode 100644 index 000000000..a99749bcf --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/demo/index.html @@ -0,0 +1,40 @@ + + + + + + demo Controller - Property Inspector + + + + + +
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/demo/index.js b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/demo/index.js new file mode 100644 index 000000000..48b8d22a8 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/demo/index.js @@ -0,0 +1,18 @@ +/// +/// + +// $local 是否国际化 +// $back 是否自行决定回显时机 +// $dom 获取文档元素 - 不是动态的都写在这里面 +const $local = true, $back = false, $dom = { + main: $('.sdpi-wrapper') +}; + +const $propEvent = { + didReceiveGlobalSettings({ settings }) { + }, + didReceiveSettings(data) { + }, + sendToPropertyInspector(data) { + } +}; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/action.js b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/action.js new file mode 100644 index 000000000..7cc9620ab --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/action.js @@ -0,0 +1,157 @@ +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/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/axios.js b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/axios.js new file mode 100644 index 000000000..78aa7b89d --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/axios.js @@ -0,0 +1 @@ +!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/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/bootstrap-icons.css b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/bootstrap-icons.css new file mode 100644 index 000000000..3c1537f1b --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/bootstrap-icons.css @@ -0,0 +1,1556 @@ +@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/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/bootstrap.min.css b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/bootstrap.min.css new file mode 100644 index 000000000..1472dec05 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/bootstrap.min.css @@ -0,0 +1,7 @@ +@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/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/common.js b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/common.js new file mode 100644 index 000000000..8dda3c8fe --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/common.js @@ -0,0 +1,81 @@ +// 自定义事件类 +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/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff new file mode 100644 index 000000000..1f5d54300 Binary files /dev/null and b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff differ diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff2 b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff2 new file mode 100644 index 000000000..b3897eff0 Binary files /dev/null and b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/propertyInspector/utils/fonts/bootstrap-icons.woff2 differ diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/pt.json b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/pt.json new file mode 100644 index 000000000..93b4ad0a0 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/pt.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b74f7f82fe43323df30fd165e18482698fc38d190101678399b28c0c3affae63 +size 181 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/readme.md b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/readme.md new file mode 100644 index 000000000..04576a348 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/readme.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:213f5bfd3d20968401d85bb01d2910c269f426f68d15f363678b1935d6a4ea64 +size 699 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/ru.json b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/ru.json new file mode 100644 index 000000000..93b4ad0a0 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/ru.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b74f7f82fe43323df30fd165e18482698fc38d190101678399b28c0c3affae63 +size 181 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/static/App-logo.png b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/static/App-logo.png new file mode 100644 index 000000000..21b45e88f --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/static/App-logo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1494c343d3cf81aaf4bc38935d377d61cc5e9a75548ac40484b028b4f271005b +size 5218 diff --git a/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/zh_CN.json b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/zh_CN.json new file mode 100644 index 000000000..5e3da7de4 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDNodeJsSDKV2/com.mirabox.streamdock.demo.sdPlugin/zh_CN.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e2b4049f7acd94a2079225ddf5111ee2176d95141cb0b5716cf5315017fc99a5 +size 608 diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/README.md b/StreamDock-Plugin-SDK/SDPythonSDK/README.md new file mode 100644 index 000000000..3d5f6a083 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/README.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:990182a450314893b372aa5106ac0da4857b1f33f404e717fb70d50692cfca29 +size 3608 diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/README.zh_CN.md b/StreamDock-Plugin-SDK/SDPythonSDK/README.zh_CN.md new file mode 100644 index 000000000..1bbf431ff --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/README.zh_CN.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3101176c353a87ddbae503599d0af31f19d18850a4e5e467a9d35bd8bcdac43 +size 3284 diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/DemoPlugin.exe b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/DemoPlugin.exe new file mode 100644 index 000000000..43dbec873 Binary files /dev/null and b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/DemoPlugin.exe differ diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/en.json b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/en.json new file mode 100644 index 000000000..627284798 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/en.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d6c149bef18873a2596e92b1c670fa668f3222e7125d71fc34f8f7ab7bee94f +size 235 diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/manifest.json b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/manifest.json new file mode 100644 index 000000000..0b489a4de --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/manifest.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9ef0115ef9bf16238d8b3aec6bba1b5c88f536212d552752160c90e23d3bf688 +size 2045 diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/propertyInspector/custom/index.html b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/propertyInspector/custom/index.html new file mode 100644 index 000000000..13717318c --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/propertyInspector/custom/index.html @@ -0,0 +1,26 @@ + + + + + + Python Demo - 属性检查器 + + + + + + + + + + + + \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/propertyInspector/custom/index.js b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/propertyInspector/custom/index.js new file mode 100644 index 000000000..f71016d1f --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/propertyInspector/custom/index.js @@ -0,0 +1,27 @@ +/** + * 基础参数说明: + * @local 是否国际化 + * @back 自主决定回显时机 + * @dom 保存需要的文档元素 + * @propEvent 软件回调事件 - 策略模式 + * ==================================================> + */ +const $local = true, $back = false, + $dom = { + main: $('.sdpi-wrapper'), + }, + $propEvent = { + didReceiveSettings(data) { + console.log("didReceiveSettings",data); + + $websocket.sendToPlugin({ PropertyInspector: 121}); + + $websocket.setGlobalSettings({ PropertyInspector: 165415 }); + }, + sendToPropertyInspector(data) { + console.log("sendToPropertyInspector",data); + }, + didReceiveGlobalSettings(data) { + console.log("didReceiveGlobalSettings",data); + }, + }; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/propertyInspector/readme.md b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/propertyInspector/readme.md new file mode 100644 index 000000000..baca1a983 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/propertyInspector/readme.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ec6a4d29d10abb8d41d799e7ba93e726df619f493078d6936adc32a372ea316 +size 2473 diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/propertyInspector/time/index.html b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/propertyInspector/time/index.html new file mode 100644 index 000000000..6ad966dbe --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/propertyInspector/time/index.html @@ -0,0 +1,26 @@ + + + + + + Python Demo - 属性检查器 + + + + + + + + + + + + \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/propertyInspector/time/index.js b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/propertyInspector/time/index.js new file mode 100644 index 000000000..af0ea0126 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/propertyInspector/time/index.js @@ -0,0 +1,26 @@ +/** + * 基础参数说明: + * @local 是否国际化 + * @back 自主决定回显时机 + * @dom 保存需要的文档元素 + * @propEvent 软件回调事件 - 策略模式 + * ==================================================> + */ +const $local = true, $back = false, + $dom = { + main: $('.sdpi-wrapper'), + }, + $propEvent = { + didReceiveSettings(data) { + console.log("didReceiveSettings",data); + $settings.test = 121; + $websocket.sendToPlugin({ PropertyInspector: 121}); + $websocket.setGlobalSettings({ PropertyInspector: 165415 }); + }, + sendToPropertyInspector(data) { + console.log("sendToPropertyInspector",data); + }, + didReceiveGlobalSettings(data) { + console.log("didReceiveGlobalSettings",data); + }, + }; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/action.js b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/action.js new file mode 100644 index 000000000..c333fd18d --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/action.js @@ -0,0 +1,150 @@ +/** + * PropertyInspector 2.5.0 新特性 => + * + * 1 => 工具与主文件相分离 - 按需引入 + * 2 => $settings - 全局持久化数据代理 ※ + * 3 => 无需关注上下文 - 随时随地与插件通信 + * 4 => 注意事项: 为了避免命名冲突,请勿使用 $ 相关的名称以及JQuery库 + * + * ===== MiraBox ========================================== 2025.4.9 =====> + */ + +let $websocket, $uuid, $action, $context, $settings, $lang; + +// 设置全局持久化数据 +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.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 + })) +}, 0) + +// 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) { + // console.log(req.responseText); + 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 文件路径回调 +let $FileID = ''; 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/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/caret.svg b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/caret.svg new file mode 100644 index 000000000..b69162a4f --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/caret.svg @@ -0,0 +1,3 @@ + + + diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/check.png b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/check.png new file mode 100644 index 000000000..ece8509f5 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/check.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5fb6fbe1417751ca7dd87552398d4585cb002654aa69632fdf6f43dbc65220c +size 234 diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/check.svg b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/check.svg new file mode 100644 index 000000000..5b96af052 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/elg_calendar.svg b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/elg_calendar.svg new file mode 100644 index 000000000..157e01bb5 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/elg_calendar.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/elg_calendar_inv.svg b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/elg_calendar_inv.svg new file mode 100644 index 000000000..4f8af68d3 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/elg_calendar_inv.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/g_d8d8d8.svg b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/g_d8d8d8.svg new file mode 100644 index 000000000..d99031408 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/g_d8d8d8.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/rcheck.svg b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/rcheck.svg new file mode 100644 index 000000000..af478ee58 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/rcheck.svg @@ -0,0 +1,3 @@ + + + diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/sdpi.css b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/sdpi.css new file mode 100644 index 000000000..26b725b1e --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/css/sdpi.css @@ -0,0 +1,230 @@ +:root{--sdpi-bgcolor:#2D2D2D;--sdpi-background:#3D3D3D;--sdpi-color:#d8d8d8;--sdpi-bordercolor:#3a3a3a;--sdpi-buttonbordercolor:#969696;--sdpi-borderradius:0px;--sdpi-width:224px;--sdpi-fontweight:600;--sdpi-letterspacing:-0.25pt} +html{--sdpi-bgcolor:#2D2D2D;--sdpi-background:#3D3D3D;--sdpi-color:#d8d8d8;--sdpi-bordercolor:#3a3a3a;--sdpi-buttonbordercolor:#969696;--sdpi-borderradius:0px;--sdpi-width:224px;--sdpi-fontweight:600;--sdpi-letterspacing:-0.25pt;height:100%;width:100%;overflow:hidden;touch-action:none;user-select:none} +html,body{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:9pt;background-color:var(--sdpi-bgcolor);color:#9a9a9a} +body{height:100%;padding:0;overflow-x:hidden;overflow-y:auto;margin:0;-webkit-overflow-scrolling:touch;-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased} +mark{background-color:var(--sdpi-bgcolor);color:var(--sdpi-color)} +hr,hr2{-webkit-margin-before:1em;-webkit-margin-after:1em;border-style:none;background:var(--sdpi-background);height:1px} +hr2,.sdpi-heading{display:flex;flex-basis:100%;align-items:center;color:inherit;font-size:9pt;margin:8px 0px} +.sdpi-heading::before,.sdpi-heading::after{content:"";flex-grow:1;background:var(--sdpi-background);height:1px;font-size:0px;line-height:0px;margin:0px 16px} +hr2{height:2px} +hr,hr2{margin-left:16px;margin-right:16px} +.sdpi-item-value,option,input,select,button{font-size:10pt;font-weight:var(--sdpi-fontweight);letter-spacing:var(--sdpi-letterspacing)} +.win .sdpi-item-value,.win option,.win input,.win select,.win button{font-size:11px;font-style:normal;letter-spacing:inherit;font-weight:100} +.win button{font-size:12px} +::-webkit-progress-value,meter::-webkit-meter-optimum-value{border-radius:2px} +::-webkit-progress-bar,meter::-webkit-meter-bar{border-radius:3px;background:var(--sdpi-background)} +::-webkit-progress-bar:active,meter::-webkit-meter-bar:active{border-radius:3px;background:#222222} +::-webkit-progress-value:active,meter::-webkit-meter-optimum-value:active{background:#99f} +progress,progress.sdpi-item-value{min-height:5px !important;height:5px;background-color:#303030} +progress{margin-top:8px !important;margin-bottom:8px !important} +.full progress,progress.full{margin-top:3px !important} +::-webkit-progress-inner-element{background-color:transparent} +.sdpi-item[type="progress"]{margin-top:4px !important;margin-bottom:12px;min-height:15px} +.sdpi-item-child.full:last-child{margin-bottom:4px} +.tabs{display:flex;border-bottom:1px solid #D7DBDD} +.tab{cursor:pointer;padding:5px 30px;color:#16a2d7;font-size:9pt;border-bottom:2px solid transparent} +.tab.is-tab-selected{border-bottom-color:#4ebbe4} +select{width:100%;-webkit-appearance:none;-moz-appearance:none;-o-appearance:none;appearance:none;background:url(caret.svg) no-repeat 97% center} +label.sdpi-file-label,input[type="button"],input[type="submit"],input[type="reset"],input[type="file"],input[type=file]::-webkit-file-upload-button,button,select{color:var(--sdpi-color);border:1pt solid #303030;font-size:8pt;background-color:var(--sdpi-background);border-radius:var(--sdpi-borderradius)} +label.sdpi-file-label,input[type="button"],input[type="submit"],input[type="reset"],input[type="file"],input[type=file]::-webkit-file-upload-button,button{border:1pt solid var(--sdpi-buttonbordercolor);border-radius:var(--sdpi-borderradius);border-color:var(--sdpi-buttonbordercolor);min-height:23px !important;height:23px !important;margin-right:8px} +input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0} +input[type="file"]{border-radius:var(--sdpi-borderradius);max-width:220px} +option{height:1.5em;padding:4px} +.sdpi-wrapper{overflow-x:hidden;height:100%} +.sdpi-item{display:flex;flex-direction:row;min-height:32px;align-items:center;margin-top:2px;max-width:344px;-webkit-user-drag:none} +.sdpi-item:first-child{margin-top:-1px} +.sdpi-item:last-child{margin-bottom:0px} +.sdpi-item>*:not(.sdpi-item-label):not(meter):not(details):not(canvas){min-height:26px;padding:0px 4px 0px 4px} +.sdpi-item>*:not(.sdpi-item-label.empty):not(meter){min-height:26px;padding:0px 4px 0px 4px} +.sdpi-item-group{padding:0 !important} +meter.sdpi-item-value{margin-left:6px} +.sdpi-item[type="group"]{display:block;margin-top:12px;margin-bottom:12px;flex-direction:unset;text-align:left} +.sdpi-item[type="group"]>.sdpi-item-label,.sdpi-item[type="group"].sdpi-item-label{width:96%;text-align:left;font-weight:700;margin-bottom:4px;padding-left:4px} +dl,ul,ol{-webkit-margin-before:0px;-webkit-margin-after:4px;-webkit-padding-start:1em;max-height:90px;overflow-y:scroll;cursor:pointer;user-select:none} +table.sdpi-item-value,dl.sdpi-item-value,ul.sdpi-item-value,ol.sdpi-item-value{-webkit-margin-before:4px;-webkit-margin-after:8px;-webkit-padding-start:1em;width:var(--sdpi-width);text-align:center} +table>caption{margin:2px} +.list,.sdpi-item[type="list"]{align-items:baseline} +.sdpi-item-label{text-align:right;flex:none;width:94px;padding-right:4px;font-weight:600} +.win .sdpi-item-label,.sdpi-item-label>small{font-weight:normal} +.sdpi-item-label:after{content:":"} +.sdpi-item-label.empty:after{content:""} +.sdpi-test,.sdpi-item-value{flex:1 0 0;margin-right:14px;margin-left:4px;justify-content:space-evenly} +canvas.sdpi-item-value{max-width:144px;max-height:144px;width:144px;height:144px;margin:0 auto;cursor:pointer} +input.sdpi-item-value{margin-left:5px} +.sdpi-item-value button,button.sdpi-item-value{margin-left:6px;margin-right:14px} +.sdpi-item-value.range{margin-left:0px} +table,dl.sdpi-item-value,ul.sdpi-item-value,ol.sdpi-item-value,.sdpi-item-value>dl,.sdpi-item-value>ul,.sdpi-item-value>ol{list-style-type:none;list-style-position:outside;margin-left:-4px;margin-right:-4px;padding:4px;border:1px solid var(--sdpi-bordercolor)} +dl.sdpi-item-value,ul.sdpi-item-value,ol.sdpi-item-value,.sdpi-item-value>ol{list-style-type:none;list-style-position:inside;margin-left:5px;margin-right:12px;padding:4px !important;display:flex;flex-direction:column} +.two-items li{display:flex} +.two-items li>*:first-child{flex:0 0 50%;text-align:left} +.two-items.thirtyseventy li>*:first-child{flex:0 0 30%} +ol.sdpi-item-value,.sdpi-item-value>ol[listtype="none"]{list-style-type:none} +ol.sdpi-item-value[type="decimal"],.sdpi-item-value>ol[type="decimal"]{list-style-type:decimal} +ol.sdpi-item-value[type="decimal-leading-zero"],.sdpi-item-value>ol[type="decimal-leading-zero"]{list-style-type:decimal-leading-zero} +ol.sdpi-item-value[type="lower-alpha"],.sdpi-item-value>ol[type="lower-alpha"]{list-style-type:lower-alpha} +ol.sdpi-item-value[type="upper-alpha"],.sdpi-item-value>ol[type="upper-alpha"]{list-style-type:upper-alpha} +ol.sdpi-item-value[type="upper-roman"],.sdpi-item-value>ol[type="upper-roman"]{list-style-type:upper-roman} +ol.sdpi-item-value[type="lower-roman"],.sdpi-item-value>ol[type="lower-roman"]{list-style-type:upper-roman} +tr:nth-child(even),.sdpi-item-value>ul>li:nth-child(even),.sdpi-item-value>ol>li:nth-child(even),li:nth-child(even){background-color:rgba(0,0,0,.2)} +td:hover,.sdpi-item-value>ul>li:hover:nth-child(even),.sdpi-item-value>ol>li:hover:nth-child(even),li:hover:nth-child(even),li:hover{background-color:rgba(255,255,255,.1)} +td.selected,td.selected:hover,li.selected:hover,li.selected{color:white;background-color:#77f} +tr{border:1px solid var(--sdpi-bordercolor)} +td{border-right:1px solid var(--sdpi-bordercolor)} +tr:last-child,td:last-child{border:none} +.sdpi-item-value.select,.sdpi-item-value>select{margin-right:13px;margin-left:4px} +.sdpi-item-child,.sdpi-item-group>.sdpi-item>input[type="color"]{margin-top:0.4em;margin-right:4px} +.full,.full *,.sdpi-item-value.full,.sdpi-item-child>full>*,.sdpi-item-child.full,.sdpi-item-child.full>*,.full>.sdpi-item-child,.full>.sdpi-item-child>*{display:flex;flex:1 1 0;margin-bottom:4px;margin-left:0px;width:100%;justify-content:space-evenly} +.sdpi-item-group>.sdpi-item>input[type="color"]{margin-top:0px} +::-webkit-calendar-picker-indicator:focus,input[type=file]::-webkit-file-upload-button:focus,button:focus,textarea:focus,input:focus,select:focus,option:focus,details:focus,summary:focus,.custom-select select{outline:none} +summary{cursor:default;padding-left:90px;padding-right:70px} +.pointer,summary .pointer{cursor:pointer} +details *{font-size:12px;font-weight:normal;word-break:break-all;} +details.message{padding:4px 18px 4px 12px} +details.message summary{min-height:18px} +details.message:first-child{margin-top:4px;margin-left:0;padding-left:102px} +details.message h1{text-align:left} +.message>summary::-webkit-details-marker{display:none} +.info20,.question,.caution,.info{background-repeat:no-repeat;background-position:72px center} +.info20{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,20 C4.4771525,20 0,15.5228475 0,10 C0,4.4771525 4.4771525,0 10,0 C15.5228475,0 20,4.4771525 20,10 C20,15.5228475 15.5228475,20 10,20 Z M10,8 C8.8954305,8 8,8.84275812 8,9.88235294 L8,16.1176471 C8,17.1572419 8.8954305,18 10,18 C11.1045695,18 12,17.1572419 12,16.1176471 L12,9.88235294 C12,8.84275812 11.1045695,8 10,8 Z M10,3 C8.8954305,3 8,3.88165465 8,4.96923077 L8,5.03076923 C8,6.11834535 8.8954305,7 10,7 C11.1045695,7 12,6.11834535 12,5.03076923 L12,4.96923077 C12,3.88165465 11.1045695,3 10,3 Z'/%3E%3C/svg%3E%0A")} +.info{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,18 C5.581722,18 2,14.418278 2,10 C2,5.581722 5.581722,2 10,2 C14.418278,2 18,5.581722 18,10 C18,14.418278 14.418278,18 10,18 Z M10,8 C9.44771525,8 9,8.42137906 9,8.94117647 L9,14.0588235 C9,14.5786209 9.44771525,15 10,15 C10.5522847,15 11,14.5786209 11,14.0588235 L11,8.94117647 C11,8.42137906 10.5522847,8 10,8 Z M10,5 C9.44771525,5 9,5.44082732 9,5.98461538 L9,6.01538462 C9,6.55917268 9.44771525,7 10,7 C10.5522847,7 11,6.55917268 11,6.01538462 L11,5.98461538 C11,5.44082732 10.5522847,5 10,5 Z'/%3E%3C/svg%3E%0A")} +.info2{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 15 15'%3E%3Cpath fill='%23999' d='M7.5,15 C3.35786438,15 0,11.6421356 0,7.5 C0,3.35786438 3.35786438,0 7.5,0 C11.6421356,0 15,3.35786438 15,7.5 C15,11.6421356 11.6421356,15 7.5,15 Z M7.5,2 C6.67157287,2 6,2.66124098 6,3.47692307 L6,3.52307693 C6,4.33875902 6.67157287,5 7.5,5 C8.32842705,5 9,4.33875902 9,3.52307693 L9,3.47692307 C9,2.66124098 8.32842705,2 7.5,2 Z M5,6 L5,7.02155172 L6,7 L6,12 L5,12.0076778 L5,13 L10,13 L10,12 L9,12.0076778 L9,6 L5,6 Z'/%3E%3C/svg%3E%0A")} +.sdpi-more-info{background-image:linear-gradient(to right,#00000000 0%,#00000040 80%),url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpolygon fill='%23999' points='4 7 8 7 8 5 12 8 8 11 8 9 4 9'/%3E%3C/svg%3E%0A")} +.caution{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' fill-rule='evenodd' d='M9.03952676,0.746646542 C9.57068894,-0.245797319 10.4285735,-0.25196227 10.9630352,0.746646542 L19.7705903,17.2030214 C20.3017525,18.1954653 19.8777595,19 18.8371387,19 L1.16542323,19 C0.118729947,19 -0.302490098,18.2016302 0.231971607,17.2030214 L9.03952676,0.746646542 Z M10,2.25584053 L1.9601405,17.3478261 L18.04099,17.3478261 L10,2.25584053 Z M10,5.9375 C10.531043,5.9375 10.9615385,6.37373537 10.9615385,6.91185897 L10.9615385,11.6923077 C10.9615385,12.2304313 10.531043,12.6666667 10,12.6666667 C9.46895697,12.6666667 9.03846154,12.2304313 9.03846154,11.6923077 L9.03846154,6.91185897 C9.03846154,6.37373537 9.46895697,5.9375 10,5.9375 Z M10,13.4583333 C10.6372516,13.4583333 11.1538462,13.9818158 11.1538462,14.6275641 L11.1538462,14.6641026 C11.1538462,15.3098509 10.6372516,15.8333333 10,15.8333333 C9.36274837,15.8333333 8.84615385,15.3098509 8.84615385,14.6641026 L8.84615385,14.6275641 C8.84615385,13.9818158 9.36274837,13.4583333 10,13.4583333 Z'/%3E%3C/svg%3E%0A")} +.question{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,18 C5.581722,18 2,14.418278 2,10 C2,5.581722 5.581722,2 10,2 C14.418278,2 18,5.581722 18,10 C18,14.418278 14.418278,18 10,18 Z M6.77783203,7.65332031 C6.77783203,7.84798274 6.85929281,8.02888914 7.0222168,8.19604492 C7.18514079,8.36320071 7.38508996,8.44677734 7.62207031,8.44677734 C8.02409055,8.44677734 8.29703704,8.20768468 8.44091797,7.72949219 C8.59326248,7.27245865 8.77945854,6.92651485 8.99951172,6.69165039 C9.2195649,6.45678594 9.56233491,6.33935547 10.027832,6.33935547 C10.4256205,6.33935547 10.7006836,6.37695313 11.0021973,6.68847656 C11.652832,7.53271484 10.942627,8.472229 10.3750916,9.1321106 C9.80755615,9.79199219 8.29492188,11.9897461 10.027832,12.1347656 C10.4498423,12.1700818 10.7027991,11.9147157 10.7832031,11.4746094 C11.0021973,9.59857178 13.1254883,8.82415771 13.1254883,7.53271484 C13.1254883,7.07568131 12.9974785,6.65250846 12.7414551,6.26318359 C12.4854317,5.87385873 12.1225609,5.56600048 11.652832,5.33959961 C11.1831031,5.11319874 10.6414419,5 10.027832,5 C9.36767248,5 8.79004154,5.13541531 8.29492187,5.40625 C7.79980221,5.67708469 7.42317837,6.01879677 7.16503906,6.43139648 C6.90689975,6.8439962 6.77783203,7.25130007 6.77783203,7.65332031 Z M10.0099668,15 C10.2713191,15 10.5016601,14.9108147 10.7009967,14.7324415 C10.9003332,14.5540682 11,14.3088087 11,13.9966555 C11,13.7157177 10.9047629,13.4793767 10.7142857,13.2876254 C10.5238086,13.0958742 10.2890379,13 10.0099668,13 C9.72646591,13 9.48726565,13.0958742 9.2923588,13.2876254 C9.09745196,13.4793767 9,13.7157177 9,13.9966555 C9,14.313268 9.10077419,14.5596424 9.30232558,14.735786 C9.50387698,14.9119295 9.73975502,15 10.0099668,15 Z'/%3E%3C/svg%3E%0A")} +.sdpi-more-info{position:fixed;left:0px;right:0px;bottom:0px;min-height:16px;padding-right:16px;text-align:right;-webkit-touch-callout:none;cursor:pointer;user-select:none;background-position:right center;background-repeat:no-repeat;border-radius:var(--sdpi-borderradius);text-decoration:none;color:var(--sdpi-color)} +.sdpi-more-info-button{display:flex;align-self:right;margin-left:auto;position:fixed;right:17px;bottom:0px;user-select:none} +.sdpi-bottom-bar{display:flex;align-self:right;margin-left:auto;position:fixed;right:17px;bottom:0px;user-select:none} +.sdpi-bottom-bar.right{right:0px} +.sdpi-bottom-bar button{min-height:20px !important;height:20px !important} +details a{background-position:right !important;min-height:24px;display:inline-block;line-height:24px;padding-right:28px} +input:not([type="range"]),textarea{-webkit-appearance:none;appearance:none;background:var(--sdpi-background);color:var(--sdpi-color);font-weight:normal;font-size:9pt;border:none;margin-top:2px;margin-bottom:2px;min-width:219px} +textarea+label{display:flex;justify-content:flex-end} +input[type="radio"],input[type="checkbox"]{display:none} +input[type="radio"]+label,input[type="checkbox"]+label{font-size:9pt;color:var(--sdpi-color);font-weight:normal;margin-right:8px} +input[type="radio"]+label:after,input[type="checkbox"]+label:after{content:" " !important} +.sdpi-item[type="radio"]>.sdpi-item-value,.sdpi-item[type="checkbox"]>.sdpi-item-value{padding-top:2px} +.sdpi-item[type="checkbox"]>.sdpi-item-value>*{margin-top:4px} +.sdpi-item[type="checkbox"] .sdpi-item-child,.sdpi-item[type="radio"] .sdpi-item-child{display:inline-block} +.sdpi-item[type="range"] .sdpi-item-value,.sdpi-item[type="meter"] .sdpi-item-child,.sdpi-item[type="progress"] .sdpi-item-child{display:flex} +.sdpi-item[type="range"] .sdpi-item-value{min-height:26px} +.sdpi-item[type="range"] .sdpi-item-value span,.sdpi-item[type="meter"] .sdpi-item-child span,.sdpi-item[type="progress"] .sdpi-item-child span{margin-top:-2px;min-width:8px;text-align:right;user-select:none;cursor:pointer;-webkit-user-select:none;user-select:none} +.sdpi-item[type="range"] .sdpi-item-value span{margin-top:7px;text-align:right} +span+input[type="range"]{display:flex;max-width:168px} +.sdpi-item[type="range"] .sdpi-item-value span:first-child,.sdpi-item[type="meter"] .sdpi-item-child span:first-child,.sdpi-item[type="progress"] .sdpi-item-child span:first-child{margin-right:4px} +.sdpi-item[type="range"] .sdpi-item-value span:last-child,.sdpi-item[type="meter"] .sdpi-item-child span:last-child,.sdpi-item[type="progress"] .sdpi-item-child span:last-child{margin-left:4px} +.reverse{transform:rotate(180deg)} +.sdpi-item[type="meter"] .sdpi-item-child meter+span:last-child{margin-left:-10px} +.sdpi-item[type="progress"] .sdpi-item-child meter+span:last-child{margin-left:-14px} +.sdpi-item[type="radio"]>.sdpi-item-value>*{margin-top:2px} +details{padding:8px 18px 8px 12px;min-width:86px} +details>h4{border-bottom:1px solid var(--sdpi-bordercolor)} +legend{display:none} +.sdpi-item-value>textarea{padding:0px;width:219px;margin-left:1px;margin-top:3px;padding:4px} +input[type="radio"]+label span,input[type="checkbox"]+label span{display:inline-block;width:16px;height:16px;margin:2px 4px 2px 0;border-radius:3px;vertical-align:middle;background:var(--sdpi-background);cursor:pointer;border:1px solid rgb(0,0,0,.2)} +input[type="radio"]+label span{border-radius:100%} +input[type="radio"]:checked+label span,input[type="checkbox"]:checked+label span{background-color:#77f;background-image:url(check.svg);background-repeat:no-repeat;background-position:center center;border:1px solid rgb(0,0,0,.4)} +input[type="radio"]:active:checked+label span,input[type="radio"]:active+label span,input[type="checkbox"]:active:checked+label span,input[type="checkbox"]:active+label span{background-color:#303030} +input[type="radio"]:checked+label span{background-image:url(rcheck.svg)} +input[type="range"]{width:var(--sdpi-width);height:30px;overflow:hidden;cursor:pointer;background:transparent !important} +.sdpi-item>input[type="range"]{margin-left:2px;max-width:var(--sdpi-width);width:var(--sdpi-width);padding:0px;margin-top:2px} +input[type="range"]::-webkit-slider-runnable-track{height:5px;background:#979797;border-radius:3px;padding:0px !important;border:1px solid var(--sdpi-background)} +input[type="range"]::-webkit-slider-thumb{position:relative;-webkit-appearance:none;background-color:var(--sdpi-color);width:12px;height:12px;border-radius:20px;margin-top:-5px;border:none} +input[type="range" i]{margin:0} +input[type="range"]::-webkit-slider-thumb::before{position:absolute;content:"";height:5px;width:500px;left:-502px;top:8px;background:#77f} +input[type="color"]{min-width:32px;min-height:32px;width:32px;height:32px;padding:0;background-color:var(--sdpi-bgcolor);flex:none} +::-webkit-color-swatch{min-width:24px} +textarea{height:3em;word-break:break-word;line-height:1.5em} +.textarea{padding:0px !important} +textarea{width:219px;height:96%;min-height:6em;resize:none;border-radius:var(--sdpi-borderradius)} +.sdpi-item.card-carousel-wrapper,.sdpi-item>.card-carousel-wrapper{padding:0} +.card-carousel-wrapper{display:flex;align-items:center;justify-content:center;margin:12px auto;color:#666a73} +.card-carousel{display:flex;justify-content:center;width:278px} +.card-carousel--overflow-container{overflow:hidden} +.card-carousel--nav__left,.card-carousel--nav__right{width:12px;height:12px;border-top:2px solid #42b883;border-right:2px solid #42b883;cursor:pointer;margin:0 4px;transition:transform 150ms linear} +.card-carousel--nav__left[disabled],.card-carousel--nav__right[disabled]{opacity:0.2;border-color:black} +.card-carousel--nav__left{transform:rotate(-135deg)} +.card-carousel--nav__left:active{transform:rotate(-135deg) scale(0.85)} +.card-carousel--nav__right{transform:rotate(45deg)} +.card-carousel--nav__right:active{transform:rotate(45deg) scale(0.85)} +.card-carousel-cards{display:flex;transition:transform 150ms ease-out;transform:translatex(0px)} +.card-carousel-cards .card-carousel--card{margin:0 5px;cursor:pointer;background-color:#fff;border-radius:4px;z-index:3} +.xxcard-carousel-cards .card-carousel--card:first-child{margin-left:0} +.xxcard-carousel-cards .card-carousel--card:last-child{margin-right:0} +.card-carousel-cards .card-carousel--card img{vertical-align:bottom;border-top-left-radius:4px;border-top-right-radius:4px;transition:opacity 150ms linear;width:60px} +.card-carousel-cards .card-carousel--card img:hover{opacity:0.5} +.card-carousel-cards .card-carousel--card--footer{border-top:0;max-width:80px;overflow:hidden;display:flex;height:100%;flex-direction:column} +.card-carousel-cards .card-carousel--card--footer p{padding:3px 0;margin:0;margin-bottom:2px;font-size:15px;font-weight:500;color:#2c3e50} +.card-carousel-cards .card-carousel--card--footer p:nth-of-type(2){font-size:12px;font-weight:300;padding:6px;color:#666a73} +h1{font-size:1.3em;font-weight:500;text-align:center;margin-bottom:12px} +::-webkit-datetime-edit{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";background:url(elg_calendar_inv.svg) no-repeat left center;padding-right:1em;padding-left:25px;background-position:4px 0px} +::-webkit-calendar-picker-indicator{background:transparent;font-size:17px} +::-webkit-calendar-picker-indicator:focus{background-color:rgba(0,0,0,0.2)} +input[type="date"]{-webkit-align-items:center;align-items:center;display:-webkit-inline-flex;font-family:monospace;overflow:hidden;padding:0;-webkit-padding-start:1px} +input::-webkit-datetime-edit{-webkit-flex:1;-webkit-user-modify:read-only !important;display:inline-block;min-width:0;overflow:hidden} +input[type="file"]{opacity:0;display:none} +.sdpi-item>input[type="file"]{opacity:1;display:flex} +input[type="file"]+span{display:flex;flex:0 1 auto;background-color:#0000ff50} +label.sdpi-file-label{cursor:pointer;user-select:none;display:inline-block;min-height:21px !important;height:21px !important;line-height:20px;padding:0px 4px;margin:auto;margin-right:0px} +.sdpi-file-label>label:active,.sdpi-file-label.file:active,label.sdpi-file-label:active,label.sdpi-file-info:active,input[type="file"]::-webkit-file-upload-button:active,button:active{background-color:var(--sdpi-color);color:#303030} +input:required:invalid,input:focus:invalid{background:var(--sdpi-background) url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5IiBoZWlnaHQ9IjkiIHZpZXdCb3g9IjAgMCA5IDkiPgogICAgPHBhdGggZmlsbD0iI0Q4RDhEOCIgZD0iTTQuNSwwIEM2Ljk4NTI4MTM3LC00LjU2NTM4NzgyZS0xNiA5LDIuMDE0NzE4NjMgOSw0LjUgQzksNi45ODUyODEzNyA2Ljk4NTI4MTM3LDkgNC41LDkgQzIuMDE0NzE4NjMsOSAzLjA0MzU5MTg4ZS0xNiw2Ljk4NTI4MTM3IDAsNC41IEMtMy4wNDM1OTE4OGUtMTYsMi4wMTQ3MTg2MyAyLjAxNDcxODYzLDQuNTY1Mzg3ODJlLTE2IDQuNSwwIFogTTQsMSBMNCw2IEw1LDYgTDUsMSBMNCwxIFogTTQuNSw4IEM0Ljc3NjE0MjM3LDggNSw3Ljc3NjE0MjM3IDUsNy41IEM1LDcuMjIzODU3NjMgNC43NzYxNDIzNyw3IDQuNSw3IEM0LjIyMzg1NzYzLDcgNCw3LjIyMzg1NzYzIDQsNy41IEM0LDcuNzc2MTQyMzcgNC4yMjM4NTc2Myw4IDQuNSw4IFoiLz4KICA8L3N2Zz4) no-repeat 98% center} +input:required:valid{background:var(--sdpi-background) url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5IiBoZWlnaHQ9IjkiIHZpZXdCb3g9IjAgMCA5IDkiPjxwb2x5Z29uIGZpbGw9IiNEOEQ4RDgiIHBvaW50cz0iNS4yIDEgNi4yIDEgNi4yIDcgMy4yIDcgMy4yIDYgNS4yIDYiIHRyYW5zZm9ybT0icm90YXRlKDQwIDQuNjc3IDQpIi8+PC9zdmc+) no-repeat 98% center} +.tooltip,:tooltip,:title{color:yellow} +.sdpi-item-group.file{width:232px;display:flex;align-items:center} +.sdpi-file-info{overflow-wrap:break-word;word-wrap:break-word;hyphens:auto;min-width:132px;max-width:144px;max-height:32px;margin-top:0px;margin-left:5px;display:inline-block;overflow:hidden;padding:6px 4px;background-color:var(--sdpi-background)} +::-webkit-scrollbar{width:8px} +::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 6px rgba(0,0,0,0.3)} +::-webkit-scrollbar-thumb{background-color:#999999;outline:1px solid slategrey;border-radius:8px} +a{color:#7397d2} +.testcontainer{display:flex;background-color:#0000ff20;max-width:400px;height:200px;align-content:space-evenly} +input[type=range]{-webkit-appearance:none;appearance:none;height:6px;margin-top:12px;z-index:0;overflow:visible} +:-webkit-slider-thumb{-webkit-appearance:none;background-color:var(--sdpi-color);width:16px;height:16px;border-radius:20px;margin-top:-6px;border:1px solid #999999} +.sdpi-item[type="range"] .sdpi-item-group{display:flex;flex-direction:column} +.xxsdpi-item[type="range"] .sdpi-item-group input{max-width:204px} +.sdpi-item[type="range"] .sdpi-item-group span{margin-left:0px !important} +.sdpi-item[type="range"] .sdpi-item-group>.sdpi-item-child{display:flex;flex-direction:row} +.rangeLabel{position:absolute;font-weight:normal;margin-top:22px} +:disabled{color:#993333} +select,select option{color:var(--sdpi-color)} +select.disabled,select option:disabled{color:#fd9494;font-style:italic} +.runningAppsContainer{display:none} +.one-line{min-height:1.5em} +.two-lines{min-height:3em} +.three-lines{min-height:4.5em} +.four-lines{min-height:6em} +.min80>.sdpi-item-child{min-width:80px} +.min100>.sdpi-item-child{min-width:100px} +.min120>.sdpi-item-child{min-width:120px} +.min140>.sdpi-item-child{min-width:140px} +.min160>.sdpi-item-child{min-width:160px} +.min200>.sdpi-item-child{min-width:200px} +.max40{flex-basis:40%;flex-grow:0} +.max30{flex-basis:30%;flex-grow:0} +.max20{flex-basis:20%;flex-grow:0} +.up20{margin-top:-20px} +.alignCenter{align-items:center} +.alignTop{align-items:flex-start} +.alignBaseline{align-items:baseline} +.noMargins,.noMargins *,.noInnerMargins *{margin:0;padding:0} +.hidden{display:none} +.icon-help,.icon-help-line,.icon-help-fill,.icon-help-inv,.icon-brighter,.icon-darker,.icon-warmer,.icon-cooler{min-width:20px;width:20px;background-repeat:no-repeat;opacity:1} +.icon-help:active,.icon-help-line:active,.icon-help-fill:active,.icon-help-inv:active,.icon-brighter:active,.icon-darker:active,.icon-warmer:active,.icon-cooler:active{opacity:0.5} +.icon-brighter,.icon-darker,.icon-warmer,.icon-cooler{margin-top:5px !important} +.icon-help,.icon-help-line,.icon-help-fill,.icon-help-inv{cursor:pointer;margin:0px;margin-left:4px} +.icon-brighter{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Ccircle cx='10' cy='10' r='4'/%3E%3Cpath d='M14.8532861,7.77530426 C14.7173255,7.4682615 14.5540843,7.17599221 14.3666368,6.90157083 L16.6782032,5.5669873 L17.1782032,6.4330127 L14.8532861,7.77530426 Z M10.5,4.5414007 C10.2777625,4.51407201 10.051423,4.5 9.82179677,4.5 C9.71377555,4.5 9.60648167,4.50311409 9.5,4.50925739 L9.5,2 L10.5,2 L10.5,4.5414007 Z M5.38028092,6.75545367 C5.18389364,7.02383457 5.01124349,7.31068015 4.86542112,7.61289977 L2.82179677,6.4330127 L3.32179677,5.5669873 L5.38028092,6.75545367 Z M4.86542112,12.3871002 C5.01124349,12.6893198 5.18389364,12.9761654 5.38028092,13.2445463 L3.32179677,14.4330127 L2.82179677,13.5669873 L4.86542112,12.3871002 Z M9.5,15.4907426 C9.60648167,15.4968859 9.71377555,15.5 9.82179677,15.5 C10.051423,15.5 10.2777625,15.485928 10.5,15.4585993 L10.5,18 L9.5,18 L9.5,15.4907426 Z M14.3666368,13.0984292 C14.5540843,12.8240078 14.7173255,12.5317385 14.8532861,12.2246957 L17.1782032,13.5669873 L16.6782032,14.4330127 L14.3666368,13.0984292 Z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-darker{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10 14C7.790861 14 6 12.209139 6 10 6 7.790861 7.790861 6 10 6 12.209139 6 14 7.790861 14 10 14 12.209139 12.209139 14 10 14zM10 13C11.6568542 13 13 11.6568542 13 10 13 8.34314575 11.6568542 7 10 7 8.34314575 7 7 8.34314575 7 10 7 11.6568542 8.34314575 13 10 13zM14.8532861 7.77530426C14.7173255 7.4682615 14.5540843 7.17599221 14.3666368 6.90157083L16.6782032 5.5669873 17.1782032 6.4330127 14.8532861 7.77530426zM10.5 4.5414007C10.2777625 4.51407201 10.051423 4.5 9.82179677 4.5 9.71377555 4.5 9.60648167 4.50311409 9.5 4.50925739L9.5 2 10.5 2 10.5 4.5414007zM5.38028092 6.75545367C5.18389364 7.02383457 5.01124349 7.31068015 4.86542112 7.61289977L2.82179677 6.4330127 3.32179677 5.5669873 5.38028092 6.75545367zM4.86542112 12.3871002C5.01124349 12.6893198 5.18389364 12.9761654 5.38028092 13.2445463L3.32179677 14.4330127 2.82179677 13.5669873 4.86542112 12.3871002zM9.5 15.4907426C9.60648167 15.4968859 9.71377555 15.5 9.82179677 15.5 10.051423 15.5 10.2777625 15.485928 10.5 15.4585993L10.5 18 9.5 18 9.5 15.4907426zM14.3666368 13.0984292C14.5540843 12.8240078 14.7173255 12.5317385 14.8532861 12.2246957L17.1782032 13.5669873 16.6782032 14.4330127 14.3666368 13.0984292z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-warmer{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M12.3247275 11.4890349C12.0406216 11.0007637 11.6761954 10.5649925 11.2495475 10.1998198 11.0890394 9.83238991 11 9.42659309 11 9 11 7.34314575 12.3431458 6 14 6 15.6568542 6 17 7.34314575 17 9 17 10.6568542 15.6568542 12 14 12 13.3795687 12 12.8031265 11.8116603 12.3247275 11.4890349zM17.6232392 11.6692284C17.8205899 11.4017892 17.9890383 11.1117186 18.123974 10.8036272L20.3121778 12.0669873 19.8121778 12.9330127 17.6232392 11.6692284zM18.123974 7.19637279C17.9890383 6.88828142 17.8205899 6.5982108 17.6232392 6.33077158L19.8121778 5.0669873 20.3121778 5.9330127 18.123974 7.19637279zM14.5 4.52746439C14.3358331 4.50931666 14.1690045 4.5 14 4.5 13.8309955 4.5 13.6641669 4.50931666 13.5 4.52746439L13.5 2 14.5 2 14.5 4.52746439zM13.5 13.4725356C13.6641669 13.4906833 13.8309955 13.5 14 13.5 14.1690045 13.5 14.3358331 13.4906833 14.5 13.4725356L14.5 16 13.5 16 13.5 13.4725356zM14 11C15.1045695 11 16 10.1045695 16 9 16 7.8954305 15.1045695 7 14 7 12.8954305 7 12 7.8954305 12 9 12 10.1045695 12.8954305 11 14 11zM9.5 11C10.6651924 11.4118364 11.5 12.5 11.5 14 11.5 16 10 17.5 8 17.5 6 17.5 4.5 16 4.5 14 4.5 12.6937812 5 11.5 6.5 11L6.5 7 9.5 7 9.5 11z'/%3E%3Cpath d='M12,14 C12,16.209139 10.209139,18 8,18 C5.790861,18 4,16.209139 4,14 C4,12.5194353 4.80439726,11.2267476 6,10.5351288 L6,4 C6,2.8954305 6.8954305,2 8,2 C9.1045695,2 10,2.8954305 10,4 L10,10.5351288 C11.1956027,11.2267476 12,12.5194353 12,14 Z M11,14 C11,12.6937812 10.1651924,11.5825421 9,11.1707057 L9,4 C9,3.44771525 8.55228475,3 8,3 C7.44771525,3 7,3.44771525 7,4 L7,11.1707057 C5.83480763,11.5825421 5,12.6937812 5,14 C5,15.6568542 6.34314575,17 8,17 C9.65685425,17 11,15.6568542 11,14 Z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-cooler{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10.4004569 11.6239517C10.0554735 10.9863849 9.57597206 10.4322632 9 9.99963381L9 9.7450467 9.53471338 9.7450467 10.8155381 8.46422201C10.7766941 8.39376637 10.7419749 8.32071759 10.7117062 8.2454012L9 8.2454012 9 6.96057868 10.6417702 6.96057868C10.6677696 6.86753378 10.7003289 6.77722682 10.7389179 6.69018783L9.44918707 5.40045694 9 5.40045694 9 4.34532219 9.32816127 4.34532219 9.34532219 2.91912025 10.4004569 2.91912025 10.4004569 4.53471338 11.6098599 5.74411634C11.7208059 5.68343597 11.8381332 5.63296451 11.9605787 5.59396526L11.9605787 3.8884898 10.8181818 2.74609294 11.5642748 2 12.5727518 3.00847706 13.5812289 2 14.3273218 2.74609294 13.2454012 3.82801356 13.2454012 5.61756719C13.3449693 5.65339299 13.4408747 5.69689391 13.5324038 5.74735625L14.7450467 4.53471338 14.7450467 2.91912025 15.8001815 2.91912025 15.8001815 4.34532219 17.2263834 4.34532219 17.2263834 5.40045694 15.6963166 5.40045694 14.4002441 6.69652946C14.437611 6.78161093 14.4692249 6.86979146 14.4945934 6.96057868L16.2570138 6.96057868 17.3994107 5.81818182 18.1455036 6.56427476 17.1370266 7.57275182 18.1455036 8.58122888 17.3994107 9.32732182 16.3174901 8.2454012 14.4246574 8.2454012C14.3952328 8.31861737 14.3616024 8.38969062 14.3240655 8.45832192L15.6107903 9.7450467 17.2263834 9.7450467 17.2263834 10.8001815 15.8001815 10.8001815 15.8001815 12.2263834 14.7450467 12.2263834 14.7450467 10.6963166 13.377994 9.32926387C13.3345872 9.34850842 13.2903677 9.36625331 13.2454012 9.38243281L13.2454012 11.3174901 14.3273218 12.3994107 13.5812289 13.1455036 12.5848864 12.1491612 11.5642748 13.1455036 10.8181818 12.3994107 11.9605787 11.2570138 11.9605787 9.40603474C11.8936938 9.38473169 11.828336 9.36000556 11.7647113 9.33206224L10.4004569 10.6963166 10.4004569 11.6239517zM12.75 8.5C13.3022847 8.5 13.75 8.05228475 13.75 7.5 13.75 6.94771525 13.3022847 6.5 12.75 6.5 12.1977153 6.5 11.75 6.94771525 11.75 7.5 11.75 8.05228475 12.1977153 8.5 12.75 8.5zM9.5 14C8.5 16.3333333 7.33333333 17.5 6 17.5 4.66666667 17.5 3.5 16.3333333 2.5 14L9.5 14z'/%3E%3Cpath d='M10,14 C10,16.209139 8.209139,18 6,18 C3.790861,18 2,16.209139 2,14 C2,12.5194353 2.80439726,11.2267476 4,10.5351288 L4,4 C4,2.8954305 4.8954305,2 6,2 C7.1045695,2 8,2.8954305 8,4 L8,10.5351288 C9.19560274,11.2267476 10,12.5194353 10,14 Z M9,14 C9,12.6937812 8.16519237,11.5825421 7,11.1707057 L7,4 C7,3.44771525 6.55228475,3 6,3 C5.44771525,3 5,3.44771525 5,4 L5,11.1707057 C3.83480763,11.5825421 3,12.6937812 3,14 C3,15.6568542 4.34314575,17 6,17 C7.65685425,17 9,15.6568542 9,14 Z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-help{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cpath fill='%23999' d='M11.292 12.516l.022 1.782H9.07v-1.804c0-1.98 1.276-2.574 2.662-3.278h-.022c.814-.44 1.65-.88 1.694-2.2.044-1.386-1.122-2.728-3.234-2.728-1.518 0-2.662.902-3.366 2.354L5 5.608C5.946 3.584 7.662 2 10.17 2c3.564 0 5.632 2.442 5.588 5.06-.066 2.618-1.716 3.41-3.102 4.158-.704.374-1.364.682-1.364 1.298zm-1.122 2.442c.858 0 1.452.594 1.452 1.452 0 .682-.594 1.408-1.452 1.408-.77 0-1.386-.726-1.386-1.408 0-.858.616-1.452 1.386-1.452z'/%3E%3C/svg%3E")} +.icon-help-line{background-image:url("data:image/svg+xml,%3Csvg width='20' height='20' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-1a9 9 0 1 0 0-18 9 9 0 0 0 0 18z'/%3E%3Cpath d='M10.848 12.307l.02 1.578H8.784v-1.597c0-1.753 1.186-2.278 2.474-2.901h-.02c.756-.39 1.533-.78 1.574-1.948.041-1.226-1.043-2.414-3.006-2.414-1.41 0-2.474.798-3.128 2.083L5 6.193C5.88 4.402 7.474 3 9.805 3 13.118 3 15.04 5.161 15 7.478c-.061 2.318-1.595 3.019-2.883 3.68-.654.332-1.268.604-1.268 1.15zM9.805 14.47c.798 0 1.35.525 1.35 1.285 0 .603-.552 1.246-1.35 1.246-.715 0-1.288-.643-1.288-1.246 0-.76.573-1.285 1.288-1.285z' fill-rule='nonzero'/%3E%3C/g%3E%3C/svg%3E")} +.icon-help-fill{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Ccircle cx='10' cy='10' r='10' fill='%23999'/%3E%3Cpath fill='%23FFF' fill-rule='nonzero' d='M8.368 7.189H5C5 3.5 7.668 2 10.292 2 13.966 2 16 4.076 16 7.012c0 3.754-3.849 3.136-3.849 5.211v1.656H8.455v-1.832c0-2.164 1.4-2.893 2.778-3.6.437-.242 1.006-.574 1.006-1.236 0-2.208-3.871-2.142-3.871-.022zM10.25 18a1.75 1.75 0 1 1 0-3.5 1.75 1.75 0 0 1 0 3.5z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-help-inv{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cpath fill='%23999' fill-rule='evenodd' d='M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zM8.368 7.189c0-2.12 3.87-2.186 3.87.022 0 .662-.568.994-1.005 1.236-1.378.707-2.778 1.436-2.778 3.6v1.832h3.696v-1.656c0-2.075 3.849-1.457 3.849-5.21C16 4.075 13.966 2 10.292 2 7.668 2 5 3.501 5 7.189h3.368zM10.25 18a1.75 1.75 0 1 0 0-3.5 1.75 1.75 0 0 0 0 3.5z'/%3E%3C/svg%3E")} +.kelvin::after{content:"K"} +.mired::after{content:" Mired"} +.percent::after{content:"%"} +.sdpi-item-value+.icon-cooler,.sdpi-item-value+.icon-warmer{margin-left:0px !important;margin-top:15px !important} +input[type="range"].colorbrightness::-webkit-slider-runnable-track,input[type="range"].colortemperature::-webkit-slider-runnable-track{height:8px;background:#979797;border-radius:4px;background-image:linear-gradient(to right,#94d0ec,#ffb165)} +input[type="range"].colorbrightness::-webkit-slider-runnable-track{background-color:#efefef;background-image:linear-gradient(to right,black,rgba(0,0,0,0))} +input[type="range"].colorbrightness::-webkit-slider-thumb,input[type="range"].colortemperature::-webkit-slider-thumb{width:16px;height:16px;border-radius:20px;margin-top:-5px;background-color:#86c6e8;box-shadow:0px 0px 1px #000000;border:1px solid #d8d8d8} +.sdpi-info-label{display:inline-block;user-select:none;position:absolute;height:15px;width:auto;text-align:center;border-radius:4px;min-width:44px;max-width:80px;background:white;font-size:11px;color:black;z-index:1000;box-shadow:0px 0px 12px rgba(0,0,0,.8);padding:2px} +.sdpi-info-label.hidden{opacity:0;transition:opacity 0.25s linear} +.sdpi-info-label.shown{position:absolute;opacity:1;transition:opacity 0.25s ease-out} \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/img/App-logo.png b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/img/App-logo.png new file mode 100644 index 000000000..21b45e88f --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/img/App-logo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1494c343d3cf81aaf4bc38935d377d61cc5e9a75548ac40484b028b4f271005b +size 5218 diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/img/icon.png b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/img/icon.png new file mode 100644 index 000000000..4c012b2c4 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/img/icon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6680297ad6133e276cdc43d3ffa74bfbaf9efe7dbf2cb9b979a314ee58525831 +size 1068 diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/utils/axios.js b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/utils/axios.js new file mode 100644 index 000000000..78aa7b89d --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/utils/axios.js @@ -0,0 +1 @@ +!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/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/utils/color.js b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/utils/color.js new file mode 100644 index 000000000..52db3485f --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/utils/color.js @@ -0,0 +1,78 @@ +// 霓虹数组 +const $neonColorArr = [ + rgb(255, 0, 0), + rgb(255, 0, 255), + rgb(0, 0, 255), + rgb(0, 255, 255), + rgb(0, 255, 0), + rgb(255, 255, 0), + rgb(255, 0, 0) +] + +// RGB转16进制颜色值 +const $rgbToHex = (r, g, b) => { + return ((1 << 24) | (r << 16) | (g << 8) | b).toString(16).replace('1', '#'); +}; + +// 16进制颜色值转RGB +const $hexToRgb = (hex) => { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16) + }; +}; + +// 根据色相转RGB +const $hueToRgb = (p, q, t) => { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; + return p; +}; + +// 根据RGB转色调 +const $rgbToHsl = (rgb) => { + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + + // 计算亮度饱和度 + let saturation = 0; + const lightness = (max + min) / 2; + if (max !== min) { + const delta = max - min; + saturation = delta / (1 - Math.abs(2 * lightness - 1)); + } + + // 计算色调 + let hue = 0; + if (max === r) hue = ((g - b) / (max - min)) % 6; + else if (max === g) hue = (b - r) / (max - min) + 2; + else hue = (r - g) / (max - min) + 4; hue *= 60; + if (hue < 0) hue += 360; + return [hue, saturation * 100, lightness * 100]; +}; + +// 根据色调转rgb +const $hslToRgb = (hsl) => { + const hue = hsl[0] / 360; + const saturation = hsl[1] / 100; + const lightness = hsl[2] / 100; + let r, g, b; + if (saturation === 0) { + r = g = b = lightness; + } else { + const q = lightness < 0.5 ? lightness * (1 + saturation) : lightness + saturation - lightness * saturation; + const p = 2 * lightness - q; + r = $hueToRgb(p, q, hue + 1 / 3); + g = $hueToRgb(p, q, hue); + b = $hueToRgb(p, q, hue - 1 / 3); + } + return [r * 255, g * 255, b * 255]; +}; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/utils/common.js b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/utils/common.js new file mode 100644 index 000000000..7e50d09fc --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/static/utils/common.js @@ -0,0 +1,84 @@ +// 自定义事件类 +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); + }; +}; + +// 限制数字 +$.num = (selector) => { + if (!selector.value || /^\d+$/.test(selector.value)) return; + selector.value = selector.value.slice(0, -1); + $.num(selector); +}; + +// 绑定限制数字方法 +Array.from($('input[type="num"]', true)).forEach(item => { + item.addEventListener('input', () => $.num(item)); +}); \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/zh_CN.json b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/zh_CN.json new file mode 100644 index 000000000..627284798 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/com.mirabox.streamdock.time.sdPlugin/zh_CN.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d6c149bef18873a2596e92b1c670fa668f3222e7125d71fc34f8f7ab7bee94f +size 235 diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/hiddenimports.png b/StreamDock-Plugin-SDK/SDPythonSDK/hiddenimports.png new file mode 100644 index 000000000..32e63639f --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/hiddenimports.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c2ec74914d0bb5299f123a10b393301c30deeb0c30bfed26dafe00e965d93a4 +size 52800 diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/main.py b/StreamDock-Plugin-SDK/SDPythonSDK/main.py new file mode 100644 index 000000000..595a2dc1a --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/main.py @@ -0,0 +1,35 @@ +from src.core.plugin import Plugin +import argparse +import sys +import threading +from src.core.logger import Logger +import time + +def main(): + Logger.info("Plugin Start") + parser = argparse.ArgumentParser(description='Stream Dock Plugin') + parser.add_argument('-port', type=int, required=True, help='WebSocket port number') + parser.add_argument('-pluginUUID', type=str, required=True, help='Unique identifier for the plugin') + parser.add_argument('-registerEvent', type=str, required=True, help='Event type for plugin registration') + parser.add_argument('-info', type=str, required=True, help='JSON string containing Stream Dock and device information') + args = parser.parse_args() + + try: + # Logger.info(f"Plugin parameters - Port: {args.port}, UUID: {args.pluginUUID}, Event: {args.registerEvent}, Info: {args.info}") + time.sleep(1) + plugin = Plugin(args.port, args.pluginUUID, args.registerEvent, args.info) + stop_event = threading.Event() + def on_close(ws, close_status_code, close_msg): + plugin.stop() + stop_event.set() + Logger.info('Plugin stopped') + + plugin.ws.on_close = on_close + stop_event.wait() + + except Exception as e: + Logger.info(e) + sys.exit(0) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/main.spec b/StreamDock-Plugin-SDK/SDPythonSDK/main.spec new file mode 100644 index 000000000..7db74104d --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/main.spec @@ -0,0 +1,41 @@ +# -*- mode: python ; coding: utf-8 -*- + + +a = Analysis( + ['main.py'], + pathex=[], + binaries=[], + datas=[ + ('src/actions', 'src/actions'), + ('src/core', 'src/core') + ], + hiddenimports=['websocket-client','PIL', 'PIL.Image', 'PIL.ImageDraw','requests'], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.datas, + [], + name='DemoPlugin', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/requirements.txt b/StreamDock-Plugin-SDK/SDPythonSDK/requirements.txt new file mode 100644 index 000000000..603d8b178 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/requirements.txt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a8bfbc98d73874c7d88bdc3176fa0c7124383b87fd862fa13f5b6bf2d8f23aa2 +size 81 diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/src/actions/custom.py b/StreamDock-Plugin-SDK/SDPythonSDK/src/actions/custom.py new file mode 100644 index 000000000..10c843f30 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/src/actions/custom.py @@ -0,0 +1,57 @@ +import json +import time +from PIL import Image, ImageDraw +import io +import base64 +import requests +from src.core.action import Action +from src.core.logger import Logger + +class Custom(Action): + def __init__(self, action: str, context: str, settings: dict, plugin): + super().__init__(action, context, settings, plugin) + # Set up timer to update time every second + + self.plugin.set_global_settings({"test": "test"}) + # 1. 画图 + img = Image.new('RGB', (200, 200), color='white') + draw = ImageDraw.Draw(img) + draw.rectangle((50, 50, 150, 150), outline='blue', width=3) + draw.text((60, 60), "Hello", fill='black') + + # 2. 转成 base64 + buffered = io.BytesIO() + img.save(buffered, format="PNG") # 保存成PNG格式到内存 + img_base64 = base64.b64encode(buffered.getvalue()).decode('utf-8') + self.set_image( f"data:image/png;base64,{img_base64}") + self.set_settings({"test": "test"}) + self.log_message("---------------test--------------") + self.set_title("test") + self.show_alert() + Logger.info(f"[Custom] Initialized with context {context}") + + def on_will_disappear(self): + # Clear the timer when action disappears + self.plugin.timer.clear_interval(f'time_update_{self.context}') + Logger.info(f"[Custom] Will disappear for context {self.context}") + + def on_did_receive_global_settings(self, settings: dict): + # Handle global settings update + Logger.info(f"[Custom] Received global settings: {settings}") + + def on_key_down(self, payload: dict): + res = requests.get("https://localhost:8000/api", verify=False) + response = requests.get("https://geoapi.qweather.com/v2/city/lookup?location=广州&key=bdd98ec1d87747f3a2e8b1741a5af796") + if response.status_code == 200: + Logger.info(response.json()) # 假设响应内容是 JSON 格式 + else: + Logger.info(f"请求失败,状态码: {response.status_code}") + Logger.info(f"[Custom] Key down event with payload: {payload}") + + def on_key_up(self, payload: dict): + self.plugin.set_global_settings({"test": "tedasdasdst"}) + self.set_settings({"test": "dasdsada"}) + self.send_to_property_inspector({"test": "asdas"}) + self.open_url("https://sdk.key123.vip/guide/get-started.html") + self.show_ok() + Logger.info(f"[Custom] Key up event with payload: {payload}") \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/src/actions/time.py b/StreamDock-Plugin-SDK/SDPythonSDK/src/actions/time.py new file mode 100644 index 000000000..c2e2baef5 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/src/actions/time.py @@ -0,0 +1,80 @@ +import json +import time +from src.core.action import Action +from src.core.logger import Logger + +class Time(Action): + def __init__(self, action: str, context: str, settings: dict, plugin): + super().__init__(action, context, settings, plugin) + # Set up timer to update time every second + # 已验证 + self.plugin.timer.set_interval( + f'time_update_{context}', + 1000, + lambda: self.set_title(time.strftime('%H:%M:%S')) + ) + Logger.info(f"[TimeAction] Initialized with context {context}") + + def on_will_disappear(self): + # 已验证 + # Clear the timer when action disappears + self.plugin.timer.clear_interval(f'time_update_{self.context}') + Logger.info(f"[TimeAction] Will disappear for context {self.context}") + + def on_did_receive_global_settings(self, settings: dict): + # 已验证 + # Handle global settings update + Logger.info(f"[TimeAction] Received global settings: {settings}") + + def on_key_down(self, payload: dict): + # 已验证 + Logger.info(f"[TimeAction] Key down event with payload: {payload}") + + def on_key_up(self, payload: dict): + # 已验证 + self.set_state(1) + Logger.info(f"[TimeAction] Key up event with payload: {payload}") + + def on_dial_down(self, payload: dict): + # 已验证 + Logger.info(f"[TimeAction] Dial down event with payload: {payload}") + + def on_dial_up(self, payload: dict): + # 已验证 + Logger.info(f"[TimeAction] Dial up event with payload: {payload}") + + def on_dial_rotate(self, payload: dict): + # 已验证 + Logger.info(f"[TimeAction] Dial rotate event with payload: {payload}") + + def on_device_did_connect(self, payload: dict): + # 已验证 + Logger.info(f"[TimeAction] Device connected with payload: {payload}") + + def on_device_did_disconnect(self, data: dict): + # 已验证 + Logger.info(f"[TimeAction] Device disconnected with data: {data}") + + def on_application_did_launch(self, data: dict): + # 已验证 + Logger.info(f"[TimeAction] Application launched with data: {data}") + + def on_application_did_terminate(self, data: dict): + # 已验证 + Logger.info(f"[TimeAction] Application terminated with data: {data}") + + def on_system_did_wake_up(self, data: dict): + # 已验证 + Logger.info(f"[TimeAction] System woke up with data: {data}") + + def on_property_inspector_did_appear(self, data: dict): + # 已验证 + Logger.info(f"[TimeAction] Property inspector appeared with data: {data}") + + def on_property_inspector_did_disappear(self, data: dict): + # 已验证 + Logger.info(f"[TimeAction] Property inspector disappeared with data: {data}") + + def on_send_to_plugin(self, payload: dict): + # 已验证 + Logger.info(f"[TimeAction] Received message from property inspector with payload: {payload}") \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/src/core/__init__.py b/StreamDock-Plugin-SDK/SDPythonSDK/src/core/__init__.py new file mode 100644 index 000000000..140ddc510 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/src/core/__init__.py @@ -0,0 +1,6 @@ +from .timer import Timer +from .action import Action +from .plugin import Plugin +from .logger import Logger + +__all__ = ['Timer', 'Action', 'Plugin','Logger'] \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/src/core/action.py b/StreamDock-Plugin-SDK/SDPythonSDK/src/core/action.py new file mode 100644 index 000000000..284d0f226 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/src/core/action.py @@ -0,0 +1,82 @@ +import json +from typing import Any, Dict + +class Action: + def __init__(self, action: str, context: str, settings: Dict, plugin): + self.action = action + self.context = context + self.settings = settings + self.title = "" + self.title_parameters = {} + self._server = plugin.ws + self.plugin = plugin + + def send_to_property_inspector(self, payload: Any): + if self._server: + self._server.send(json.dumps({ + 'event': 'sendToPropertyInspector', + 'action': self.action, + 'context': self.context, + 'payload': payload + })) + + def set_state(self, state: int): + if self._server: + self._server.send(json.dumps({ + 'event': 'setState', + 'context': self.context, + 'payload': {'state': state} + })) + + def set_title(self, title: str): + if self._server: + self._server.send(json.dumps({ + 'event': 'setTitle', + 'context': self.context, + 'payload': {'title': title, 'target': 0} + })) + + def set_settings(self, payload: Any): + if self._server: + self.settings = payload + self._server.send(json.dumps({ + 'event': 'setSettings', + 'context': self.context, + 'payload': payload + })) + + def open_url(self, url: str): + if self._server: + self._server.send(json.dumps({ + 'event': 'openUrl', + 'payload': {'url': url} + })) + + def show_ok(self): + if self._server: + self._server.send(json.dumps({ + 'event': 'showOk', + 'context': self.context + })) + + def show_alert(self): + if self._server: + self._server.send(json.dumps({ + 'event': 'showAlert', + 'context': self.context + })) + + def set_image(self, url: str): + if self._server: + self._server.send(json.dumps({ + 'event': 'setImage', + 'context': self.context, + 'payload': {'target': 0, 'image': url} + })) + + def log_message(self, message: str): + if self._server: + self._server.send(json.dumps({ + 'event': 'logMessage', + 'payload': {'message': message} + })) \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/src/core/action_factory.py b/StreamDock-Plugin-SDK/SDPythonSDK/src/core/action_factory.py new file mode 100644 index 000000000..4eb363a9e --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/src/core/action_factory.py @@ -0,0 +1,102 @@ +import os +import importlib +import inspect +from typing import Dict, Type, Optional +from .action import Action +from .logger import Logger + +class ActionFactory: + """Action工厂类,负责管理和创建不同类型的Action实例 + + 该类维护action类型到具体Action类的映射关系,提供动态创建Action实例的功能。 + 可以通过register_action方法注册新的Action类型,通过create_action方法创建Action实例。 + """ + + _action_types: Dict[str, Type[Action]] = {} + + @classmethod + def register_action(cls, action_type: str, action_class: Type[Action]): + """注册一个新的Action类型 + + Args: + action_type: Action的类型标识符 + action_class: Action的具体实现类 + """ + cls._action_types[action_type] = action_class + + @classmethod + def create_action(cls, action: str, context: str, settings: dict, plugin) -> Optional[Action]: + """创建一个Action实例 + + Args: + action: Action的标识符,可以是完整的action字符串(如com.xxx.xxx.time) + context: Action的上下文标识符 + settings: Action的设置 + + Returns: + 如果action_type注册成工则返回对应的Action实例,否则返回None + """ + try: + # 从完整的action字符串中提取action名称 + action_name = action.split('.')[-1] + + action_class = cls._action_types.get(action_name) + if action_class: + action_instance = action_class(action, context, settings, plugin) + if not isinstance(action_instance, Action): + Logger.error(f"Created instance is not an Action type: {action_name}") + return None + return action_instance + else: + Logger.error(f"Action type not found: {action_name}") + return None + except Exception as e: + Logger.error(f"Error creating action {action}: {str(e)}") + return None + + @classmethod + def scan_and_register_actions(cls): + """扫描actions目录并自动注册所有Action类型""" + import sys + import traceback + + # 获取正确的actions目录路径 + if getattr(sys, 'frozen', False): + # 如果是打包后的环境,使用sys._MEIPASS + base_path = sys._MEIPASS + # 在打包环境下,从src目录下查找actions目录 + actions_dir = os.path.join(base_path, 'src', 'actions') + else: + # 开发环境下使用相对路径 + actions_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'actions') + if not os.path.exists(actions_dir): + Logger.error(f"Actions directory not found: {actions_dir}") + return + + # 将src目录添加到Python路径中 + src_dir = os.path.dirname(actions_dir) + if src_dir not in sys.path: + sys.path.insert(0, src_dir) + + for file_name in os.listdir(actions_dir): + if file_name.endswith('.py') and not file_name.startswith('__'): + module_name = file_name[:-3] # 移除.py后缀 + + try: + module = importlib.import_module(f'actions.{module_name}') + Logger.info(f"Loading action module: {module_name}") + for name, obj in inspect.getmembers(module): + if (inspect.isclass(obj) and + issubclass(obj, Action) and + obj != Action): + action_type = module_name.lower() + cls.register_action(action_type, obj) + Logger.info(f"Successfully registered action: {action_type} -> {obj.__name__}") + Logger.info(f"Registered action types: {cls._action_types}") + except Exception as e: + import traceback + Logger.error(f"Error loading action module {module_name}: {str(e)}") + Logger.error(traceback.format_exc()) + +# 自动扫描并注册所有Action类型 +ActionFactory.scan_and_register_actions() \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/src/core/logger.py b/StreamDock-Plugin-SDK/SDPythonSDK/src/core/logger.py new file mode 100644 index 000000000..c0b414a36 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/src/core/logger.py @@ -0,0 +1,119 @@ +import logging +import os +import sys +from typing import Optional + +class Logger: + """全局日志管理类 + + 使用单例模式实现的日志管理器,提供统一的日志记录接口。 + 可以在应用的任何位置使用该类记录日志。 + """ + + _instance: Optional['Logger'] = None + _logger: Optional[logging.Logger] = None + + def __new__(cls) -> 'Logger': + if cls._instance is None: + cls._instance = super().__new__(cls) + cls._setup_logger() + return cls._instance + + @classmethod + def get_instance(cls) -> 'Logger': + """获取Logger单例实例 + + Returns: + Logger实例 + """ + if cls._instance is None: + cls._instance = Logger() + return cls._instance + + @classmethod + def _setup_logger(cls): + """设置日志记录器 + + 配置日志记录器的输出格式、日志级别和输出文件。 + """ + if cls._logger is None: + cls._logger = logging.getLogger('StreamDock') + cls._logger.setLevel(logging.INFO) + + # 获取日志目录路径 + if getattr(sys, 'frozen', False): + # 如果是打包后的exe + base_path = os.path.join(os.path.dirname(sys.executable), 'logs') + else: + # 如果是开发环境 + base_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'logs') + + # 确保日志目录存在 + try: + os.makedirs(base_path, exist_ok=True) + + # 设置日志文件路径 + log_file = os.path.join(base_path, 'plugin.log') + + # 创建文件处理器 + handler = logging.FileHandler(log_file, encoding='utf-8') + handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) + cls._logger.addHandler(handler) + + # 添加控制台输出 + console_handler = logging.StreamHandler() + console_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) + cls._logger.addHandler(console_handler) + except Exception as e: + print(f"Failed to setup file handler: {e}") + # 如果文件处理器设置失败,至少确保控制台输出正常工作 + console_handler = logging.StreamHandler() + console_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) + cls._logger.addHandler(console_handler) + + @classmethod + def get_logger(cls) -> logging.Logger: + """获取日志记录器实例 + + Returns: + 配置好的日志记录器实例 + """ + if cls._logger is None: + cls._setup_logger() + return cls._logger + + @classmethod + def info(cls, message: str): + """记录INFO级别的日志 + + Args: + message: 日志消息 + """ + cls.get_instance().get_logger().info(message) + + @classmethod + def error(cls, message: str): + """记录ERROR级别的日志 + + Args: + message: 日志消息 + """ + cls.get_instance().get_logger().error(message) + + @classmethod + def warning(cls, message: str): + """记录WARNING级别的日志 + + Args: + message: 日志消息 + """ + cls.get_instance().get_logger().warning(message) + + @classmethod + def debug(cls, message: str): + """记录DEBUG级别的日志 + + Args: + message: 日志消息 + """ + cls.get_instance().get_logger().debug(message) \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/src/core/plugin.py b/StreamDock-Plugin-SDK/SDPythonSDK/src/core/plugin.py new file mode 100644 index 000000000..e6d5d0867 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/src/core/plugin.py @@ -0,0 +1,326 @@ +import json +import threading +import websocket +import logging +import os +import sys +from typing import Any, Dict, List, Optional +from http.server import HTTPServer, BaseHTTPRequestHandler +from .timer import Timer +from .action import Action +from .logger import Logger + +class Plugin: + """Stream Dock插件的核心类,负责管理WebSocket连接和处理Stream Dock事件。 + + 该类维护与Stream Dock软件的WebSocket连接,处理各种事件(如按钮出现、消失、设置更改等), + 并管理插件的动作(Action)实例。每个动作实例对应Stream Dock界面上的一个按钮。 + """ + + def __init__(self, port: int, plugin_uuid: str, event: str, info: Dict[str, Any]): + """初始化插件实例 + + Args: + port: WebSocket服务器端口号 + plugin_uuid: 插件的唯一标识符 + event: 事件类型 + info: 包含插件信息的对象 + """ + self.actions: Dict[str, Action] = {} + self.global_settings: Any = None + self.timer = Timer() + self.plugin_uuid = plugin_uuid + self.http_server = None + self.http_server_thread = None + + # 启动HTTP服务 + # self._start_http_server() + + # Initialize WebSocket + self.ws = websocket.WebSocketApp( + f'ws://127.0.0.1:{port}', + on_open=lambda ws: self._on_open(ws, event, plugin_uuid), + on_message=self._on_message, + on_error=lambda ws, error: Logger.error(f"WebSocket error: {error}") + ) + + # Start WebSocket connection in a separate thread + threading.Thread(target=self.ws.run_forever, daemon=True).start() + + def _on_open(self, ws, event: str, plugin_uuid: str): + """WebSocket连接建立时的回调函数 + + 向Stream Dock注册插件,发送初始化事件。 + + Args: + ws: WebSocket连接实例 + event: 事件类型 + plugin_uuid: 插件UUID + """ + Logger.info("WebSocket connected") + + ws.send(json.dumps({'event': event, 'uuid': plugin_uuid})) + + def _on_message(self, ws, message): + """处理从Stream Dock接收到的WebSocket消息 + + 根据接收到的事件类型执行相应的操作,包括: + - 处理全局settings更新 + - 处理按钮出现/消失事件 + - 处理按钮settings更改 + - 处理标题参数更改 + + Args: + ws: WebSocket连接实例 + message: 接收到的JSON消息 + """ + data = json.loads(message) + event = data.get('event') + Logger.info(event) + if event == 'didReceiveGlobalSettings': + self.global_settings = data.get('payload', {}).get('settings') + for action in self.actions.values(): + if hasattr(action, 'on_did_receive_global_settings'): + action.on_did_receive_global_settings(self.global_settings) + elif event == 'willAppear': + context = data.get('context') + if context not in self.actions: + from .action_factory import ActionFactory + action = ActionFactory.create_action( + data.get('action'), + context, + data.get('payload', {}).get('settings', {}), + self + ) + if action: + self.actions[context] = action + else: + Logger.error(f"Failed to create action for context: {context}") + elif event == 'willDisappear': + context = data.get('context') + if context in self.actions: + action = self.actions[context] + if hasattr(action, 'on_will_disappear'): + action.on_will_disappear() + del self.actions[context] + elif event == 'didReceiveSettings': + context = data.get('context') + if context in self.actions: + action = self.actions[context] + settings = data.get('payload', {}).get('settings', {}) + if hasattr(action, 'on_did_receive_settings'): + action.on_did_receive_settings(settings) + else: + action.settings = settings + elif event == 'titleParametersDidChange': + context = data.get('context') + if context in self.actions: + action = self.actions[context] + payload = data.get('payload', {}) + if hasattr(action, 'on_title_parameters_did_change'): + action.on_title_parameters_did_change(payload) + else: + action.title = payload.get('title', '') + action.title_parameters = payload.get('titleParameters', {}) + # Handle context-specific events + context_events = { + 'keyDown': 'on_key_down', + 'keyUp': 'on_key_up', + 'dialDown': 'on_dial_down', + 'dialUp': 'on_dial_up', + 'dialRotate': 'on_dial_rotate' + } + + if event in context_events: + context = data.get('context') + if context in self.actions: + action = self.actions[context] + handler = context_events[event] + if hasattr(action, handler): + getattr(action, handler)(data.get('payload', {})) + # Handle global events + global_events = { + 'deviceDidConnect': 'on_device_did_connect', + 'deviceDidDisconnect': 'on_device_did_disconnect', + 'applicationDidLaunch': 'on_application_did_launch', + 'applicationDidTerminate': 'on_application_did_terminate', + 'systemDidWakeUp': 'on_system_did_wake_up' + } + + if event in global_events: + handler = global_events[event] + for action in self.actions.values(): + if hasattr(action, handler): + getattr(action, handler)(data) + elif event == 'propertyInspectorDidAppear': + context = data.get('context') + if context in self.actions: + action = self.actions[context] + if hasattr(action, 'on_property_inspector_did_appear'): + action.on_property_inspector_did_appear(data) + elif event == 'propertyInspectorDidDisappear': + context = data.get('context') + if context in self.actions: + action = self.actions[context] + if hasattr(action, 'on_property_inspector_did_disappear'): + action.on_property_inspector_did_disappear(data) + elif event == 'sendToPlugin': + context = data.get('context') + if context in self.actions: + action = self.actions[context] + if hasattr(action, 'on_send_to_plugin'): + action.on_send_to_plugin(data.get('payload', {})) + + def set_global_settings(self, payload: Any): + """更新插件的全局设置 + + Args: + payload: 新的全局设置值 + """ + self.ws.send(json.dumps({ + 'event': 'setGlobalSettings', + 'context': self.plugin_uuid, + 'payload': payload + })) + self.global_settings = payload + + def get_global_settings(self): + """请求获取插件的当前全局设置 + + 发送请求后,设置值将通过WebSocket消息返回 + """ + self.ws.send(json.dumps({ + 'event': 'getGlobalSettings', + 'context': self.plugin_uuid + })) + + def get_action(self, context: str) -> Optional[Action]: + """获取指定上下文的Action实例 + + Args: + context: Action的上下文标识符 + + Returns: + 如果存在则返回Action实例,否则返回None + """ + return self.actions.get(context) + + def get_actions(self, action: str) -> List[Action]: + """获取所有指定类型的Action实例列表 + + Args: + action: Action的类型标识符 + + Returns: + 符合指定类型的Action实例列表 + """ + return [a for a in self.actions.values() if a.action == action] + + def _start_http_server(self, port: int = 8000): + """启动HTTPS服务器 + + Args: + port: HTTPS服务器端口号,默认为8000 + """ + import ssl + import os + + class RequestHandler(BaseHTTPRequestHandler): + def do_GET(self): + if self.path == '/api': + self.send_response(200) + self.send_header('Content-type', 'application/json') + self.end_headers() + response = {'status': 'ok', 'message': 'API endpoint'} + self.wfile.write(json.dumps(response).encode()) + else: + self.send_response(404) + self.send_header('Content-type', 'application/json') + self.end_headers() + response = {'status': 'error', 'message': 'Not found'} + self.wfile.write(json.dumps(response).encode()) + + try: + self.http_server = HTTPServer(('0.0.0.0', port), RequestHandler) + + # 配置SSL + import tempfile + + # 创建临时文件来存储证书和密钥 + cert_file = tempfile.NamedTemporaryFile(delete=False) + key_file = tempfile.NamedTemporaryFile(delete=False) + + # 生成自签名证书和私钥 + from cryptography import x509 + from cryptography.x509.oid import NameOID + from cryptography.hazmat.primitives import hashes + from cryptography.hazmat.primitives.asymmetric import rsa + from cryptography.hazmat.primitives import serialization + from datetime import datetime, timedelta + + # 生成私钥 + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048 + ) + + # 生成证书 + subject = issuer = x509.Name([ + x509.NameAttribute(NameOID.COMMON_NAME, u"localhost"), + x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"Stream Dock Plugin"), + x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u"Development"), + x509.NameAttribute(NameOID.COUNTRY_NAME, u"CN"), + x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Development State"), + x509.NameAttribute(NameOID.LOCALITY_NAME, u"Development City"), + ]) + + cert = x509.CertificateBuilder().subject_name( + subject + ).issuer_name( + issuer + ).public_key( + private_key.public_key() + ).serial_number( + x509.random_serial_number() + ).not_valid_before( + datetime.utcnow() + ).not_valid_after( + datetime.utcnow() + timedelta(days=365) + ).sign(private_key, hashes.SHA256()) + + # 将证书和私钥写入临时文件 + cert_file.write(cert.public_bytes(serialization.Encoding.PEM)) + key_file.write(private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption() + )) + cert_file.close() + key_file.close() + + # 配置SSL上下文 + context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + context.load_cert_chain(cert_file.name, key_file.name) + + # 包装socket + self.http_server.socket = context.wrap_socket(self.http_server.socket, server_side=True) + + # 删除临时文件 + os.unlink(cert_file.name) + os.unlink(key_file.name) + + self.http_server_thread = threading.Thread(target=self.http_server.serve_forever, daemon=True) + self.http_server_thread.start() + Logger.info(f"HTTPS server started on port {port}") + except Exception as e: + Logger.error(f"Failed to start HTTPS server: {e}") + + def stop(self): + """停止插件服务 + + 停止HTTP服务器和WebSocket连接 + """ + if self.http_server: + self.http_server.shutdown() + self.http_server.server_close() + Logger.info("HTTP server stopped") diff --git a/StreamDock-Plugin-SDK/SDPythonSDK/src/core/timer.py b/StreamDock-Plugin-SDK/SDPythonSDK/src/core/timer.py new file mode 100644 index 000000000..5a9d98dcb --- /dev/null +++ b/StreamDock-Plugin-SDK/SDPythonSDK/src/core/timer.py @@ -0,0 +1,29 @@ +import threading +import time +from typing import Dict, Callable + +class Timer: + def __init__(self): + self._intervals: Dict[str, Dict] = {} + self._thread = threading.Thread(target=self._run, daemon=True) + self._thread.start() + + def _run(self): + while True: + current_time = time.time() + for uuid, data in list(self._intervals.items()): + if current_time - data['last_run'] >= data['delay']: + data['callback']() + data['last_run'] = current_time + time.sleep(0.1) + + def set_interval(self, uuid: str, delay: float, callback: Callable): + self._intervals[uuid] = { + 'delay': delay / 1000, # Convert ms to seconds + 'callback': callback, + 'last_run': time.time() + } + + def clear_interval(self, uuid: str): + if uuid in self._intervals: + del self._intervals[uuid] \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDQtSDK/.gitignore b/StreamDock-Plugin-SDK/SDQtSDK/.gitignore new file mode 100644 index 000000000..b1e2da7b0 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/.gitignore @@ -0,0 +1,56 @@ +# C++ objects and libs +*.slo +*.lo +*.o +*.la +*.lai +*.so.* + +# Qt-es +object_script.*.Release +object_script.*.Debug +*_plugin_import.cpp +/.qmake.cache +/.qmake.stash +*.pro.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +moc_*.cpp +moc_*.h +qrc_*.cpp +ui_*.h +*.qmlc +*.jsc +Makefile* +*build-* +#*.qm +*.prl + +# Qt unit tests +target_wrapper.* + +# QtCreator +*.autosave + +# QtCreator Qml +*.qmlproject.user +*.qmlproject.user.* + +# QtCreator CMake +CMakeLists.txt.user* + +# QtCreator 4.8< compilation database +compile_commands.json + +# QtCreator local machine specific files for imported projects +*creator.user* + +*_qmlcache.qrc + +# Visual Studio Code +.vscode + +#macOS +.DS_Store diff --git a/StreamDock-Plugin-SDK/SDQtSDK/SDK/Action.cpp b/StreamDock-Plugin-SDK/SDQtSDK/SDK/Action.cpp new file mode 100644 index 000000000..6abb7aee8 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/SDK/Action.cpp @@ -0,0 +1,101 @@ +#include "Action.h" +#include "ConnectionManager.h" + +Action::Action(ConnectionManager *connection, const QString &action, const QString &context) + : mConnectionManager(connection), mAction(action), mContext(context) +{ +} + +Action::~Action() +{ +} + +QString Action::GetContext() const +{ + return mContext; +} + +QString Action::GetAction() const +{ + return mAction; +} + +ConnectionManager *Action::GetConnectionManager() const +{ + return mConnectionManager; +} + +void Action::DidReceiveSettings(const QJsonObject &payload) +{ +} + +void Action::KeyDown(const QJsonObject &payload) +{ +} + +void Action::KeyUp(const QJsonObject &payload) +{ +} + +void Action::DialUp(const QJsonObject &payload) +{ +} + +void Action::DialDown(const QJsonObject &payload) +{ +} + +void Action::RotateClockwise(const QJsonObject &payload, const unsigned int ticks, const bool pressed) +{ +} + +void Action::RotateCounterClockwise(const QJsonObject &payload, const unsigned int ticks, const bool pressed) +{ +} + +void Action::SendToPlugin(const QJsonObject &payload) +{ +} + +void Action::WillAppear(const QJsonObject &payload) +{ +} + +void Action::WillDisappear(const QJsonObject &payload) +{ +} + +void Action::SetState(int state) +{ + GetConnectionManager()->SetState(state, mContext); +} + +void Action::SetTitle(const QString &title, mSDKTarget target, int state) +{ + GetConnectionManager()->SetTitle(title, mContext, target, state); +} + +void Action::SetImage(const QString &inBase64ImageString, mSDKTarget target, int state) +{ + GetConnectionManager()->SetImage(inBase64ImageString, mContext, target, state); +} + +void Action::SetSettings(const QJsonObject &inPayload) +{ + GetConnectionManager()->SetSettings(inPayload, mContext); +} + +void Action::ShowAlert() +{ + GetConnectionManager()->ShowAlertForContext(mContext); +} + +void Action::ShowOK() +{ + GetConnectionManager()->ShowOKForContext(mContext); +} + +void Action::SendToPropertyInspector(const QJsonObject &inPayload) +{ + GetConnectionManager()->SendToPropertyInspector(mAction, mContext, inPayload); +} diff --git a/StreamDock-Plugin-SDK/SDQtSDK/SDK/Action.h b/StreamDock-Plugin-SDK/SDQtSDK/SDK/Action.h new file mode 100644 index 000000000..39a49de19 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/SDK/Action.h @@ -0,0 +1,53 @@ +#ifndef ACTION_H +#define ACTION_H + +#include "SDKDefines.h" +#include +#include + +class ConnectionManager; + +class Action +{ +public: + Action(ConnectionManager *connection, const QString &action, const QString &context); + virtual ~Action(); + + // Get action information + QString GetAction() const; + QString GetContext() const; + +protected: + // Get connection manager + ConnectionManager *GetConnectionManager() const; + +public: + // Events Received + virtual void DidReceiveSettings(const QJsonObject &payload); + virtual void KeyDown(const QJsonObject &payload); + virtual void KeyUp(const QJsonObject &payload); + virtual void DialUp(const QJsonObject &payload); + virtual void DialDown(const QJsonObject &payload); + virtual void SendToPlugin(const QJsonObject &payload); + virtual void WillAppear(const QJsonObject &payload); + virtual void WillDisappear(const QJsonObject &payload); + virtual void RotateClockwise(const QJsonObject &payload, const unsigned int ticks, const bool pressed); + virtual void RotateCounterClockwise(const QJsonObject &payload, const unsigned int ticks, const bool pressed); + +protected: + // Events Sent + void SetState(int state); + void SetTitle(const QString &title, mSDKTarget = mSDKTarget_HardwareAndSoftware, int state = -1); + void SetImage(const QString &inBase64ImageString, mSDKTarget = mSDKTarget_HardwareAndSoftware, int state = -1); + void ShowAlert(); + void ShowOK(); + void SetSettings(const QJsonObject &inPayload); + void SendToPropertyInspector(const QJsonObject &inPayload); + +private: + QString mAction; + QString mContext; + ConnectionManager *mConnectionManager = nullptr; +}; + +#endif // ACTION_H diff --git a/StreamDock-Plugin-SDK/SDQtSDK/SDK/BasePlugin.cpp b/StreamDock-Plugin-SDK/SDQtSDK/SDK/BasePlugin.cpp new file mode 100644 index 000000000..43c19470d --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/SDK/BasePlugin.cpp @@ -0,0 +1,96 @@ +#include "BasePlugin.h" + +BasePlugin::BasePlugin() +{ +} + +BasePlugin::~BasePlugin() +{ +} + +void BasePlugin::SetConnectionManager(ConnectionManager *inConnectionManager) +{ + mConnectionManager = inConnectionManager; +} + +void BasePlugin::DidReceiveGlobalSettings(const QJsonObject &inPayload) +{ +} + +void BasePlugin::DidReceiveSettings(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) +{ +} + +void BasePlugin::KeyDownForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) +{ +} + +void BasePlugin::KeyUpForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) +{ +} + +void BasePlugin::DialDownForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) +{ +} + + +void BasePlugin::DialUpForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) +{ +} + + +void BasePlugin::DialRotateForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) +{ +} + +void BasePlugin::WillAppearForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) +{ +} + +void BasePlugin::WillDisappearForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) +{ +} + +void BasePlugin::DeviceDidConnect(const QString &inDeviceID, + const QJsonObject &inDeviceInfo) +{ +} + +void BasePlugin::DeviceDidDisconnect(const QString &inDeviceID) +{ +} + +void BasePlugin::SystemDidWakeUp() +{ +} + +void BasePlugin::SendToPlugin(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) +{ +} diff --git a/StreamDock-Plugin-SDK/SDQtSDK/SDK/BasePlugin.h b/StreamDock-Plugin-SDK/SDQtSDK/SDK/BasePlugin.h new file mode 100644 index 000000000..285d244d3 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/SDK/BasePlugin.h @@ -0,0 +1,75 @@ +#ifndef BASEPLUGIN_H +#define BASEPLUGIN_H + +#include +#include + +class ConnectionManager; + +class BasePlugin +{ +public: + BasePlugin(); + virtual ~BasePlugin(); + + void SetConnectionManager(ConnectionManager *inConnectionManager); + + virtual void DidReceiveGlobalSettings(const QJsonObject &inPayload); + + virtual void DidReceiveSettings(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID); + + virtual void KeyDownForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID); + + virtual void KeyUpForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID); + + virtual void DialDownForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID); + + virtual void DialUpForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID); + + virtual void DialRotateForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID); + + virtual void WillAppearForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID); + + virtual void WillDisappearForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID); + + virtual void DeviceDidConnect(const QString &inDeviceID, + const QJsonObject &inDeviceInfo); + + virtual void DeviceDidDisconnect(const QString &inDeviceID); + + virtual void SystemDidWakeUp(); + + virtual void SendToPlugin(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID); + +protected: + ConnectionManager *mConnectionManager = nullptr; +}; + +#endif // BASEPLUGIN_H diff --git a/StreamDock-Plugin-SDK/SDQtSDK/SDK/ConnectionManager.cpp b/StreamDock-Plugin-SDK/SDQtSDK/SDK/ConnectionManager.cpp new file mode 100644 index 000000000..b44f15ac7 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/SDK/ConnectionManager.cpp @@ -0,0 +1,249 @@ +#include "ConnectionManager.h" +#include "Logger.h" +#include +#include + +ConnectionManager::ConnectionManager(int inPort, + const QString &inPluginUUID, + const QString &inRegisterEvent, + const QString &inInfo, + BasePlugin* inPlugin, + QObject *parent) + : QObject{parent}, + mPort(inPort), + mPluginUUID(inPluginUUID), + mRegisterEvent(inRegisterEvent), + mPlugin(inPlugin) +{ + if (inPlugin) { + inPlugin->SetConnectionManager(this); + } +} + +ConnectionManager::~ConnectionManager() +{ + mWebsocketClient->abort(); + delete mWebsocketClient; + mWebsocketClient = nullptr; +} + +void ConnectionManager::Run() +{ + if (!mWebsocketClient) { + mWebsocketClient = new QWebSocket(); + connect(mWebsocketClient, &QWebSocket::connected, this, &ConnectionManager::OnConnected); + connect(mWebsocketClient, &QWebSocket::disconnected, this, &ConnectionManager::OnDisonnected); + connect(mWebsocketClient, QOverload::of(&QWebSocket::error), this, &ConnectionManager::OnError); + connect(mWebsocketClient, &QWebSocket::textMessageReceived, this, &ConnectionManager::OnMessage); + + QString urlString = QString("ws://127.0.0.1:%1").arg(mPort); + mWebsocketClient->open(QUrl(urlString)); + } +} + +void ConnectionManager::SetTitle(const QString &inTitle, + const QString &inContext, + mSDKTarget inTarget, + int inState) +{ + QJsonObject jsonObject; + jsonObject[mSDKCommonEvent] = mSDKEventSetTitle; + jsonObject[mSDKCommonContext] = inContext; + QJsonObject payload; + payload[mSDKPayloadTarget] = inTarget; + payload[mSDKPayloadTitle] = inTitle; + if (inState >= 0) { + payload[mSDKPayloadState] = inState; + } + jsonObject[mSDKCommonPayload] = payload; + SendMessage(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact)); +} + +void ConnectionManager::SetImage(const QString &inBase64ImageString, + const QString &inContext, + mSDKTarget inTarget, + int inState) +{ + QJsonObject jsonObject; + jsonObject[mSDKCommonEvent] = mSDKEventSetImage; + jsonObject[mSDKCommonContext] = inContext; + QJsonObject payload; + payload[mSDKPayloadTarget] = inTarget; + const QString prefix = "data:image/png;base64,"; + if (inBase64ImageString.isEmpty() || inBase64ImageString.startsWith(prefix)) { + payload[mSDKPayloadImage] = inBase64ImageString; + } else { + payload[mSDKPayloadImage] = "data:image/png;base64," + inBase64ImageString; + } + if (inState >= 0) { + payload[mSDKPayloadState] = inState; + } + jsonObject[mSDKCommonPayload] = payload; + SendMessage(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact)); +} + +void ConnectionManager::ShowAlertForContext(const QString &inContext) +{ + QJsonObject jsonObject; + jsonObject[mSDKCommonEvent] = mSDKEventShowAlert; + jsonObject[mSDKCommonContext] = inContext; + SendMessage(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact)); +} + +void ConnectionManager::ShowOKForContext(const QString &inContext) +{ + QJsonObject jsonObject; + jsonObject[mSDKCommonEvent] = mSDKEventShowOK; + jsonObject[mSDKCommonContext] = inContext; + SendMessage(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact)); +} + +void ConnectionManager::SetSettings(const QJsonObject &inPayload, + const QString &inContext) +{ + QJsonObject jsonObject; + jsonObject[mSDKCommonEvent] = mSDKEventSetSettings; + jsonObject[mSDKCommonContext] = inContext; + jsonObject[mSDKCommonPayload] = inPayload; + SendMessage(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact)); +} + +void ConnectionManager::GetGlobalSettings() +{ + QJsonObject jsonObject{{mSDKCommonEvent, mSDKEventGetGlobalSettings}, {mSDKCommonContext, mPluginUUID}}; + SendMessage(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact)); +} + +void ConnectionManager::SetGlobalSettings(const QJsonObject &inPayload) +{ + QJsonObject jsonObject; + jsonObject[mSDKCommonEvent] = mSDKEventSetGlobalSettings; + jsonObject[mSDKCommonContext] = mPluginUUID; + jsonObject[mSDKCommonPayload] = inPayload; + SendMessage(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact)); +} + +void ConnectionManager::SetState(int inState, const QString &inContext) +{ + QJsonObject jsonObject; + jsonObject[mSDKCommonEvent] = mSDKEventSetState; + jsonObject[mSDKCommonContext] = inContext; + QJsonObject payload; + payload[mSDKPayloadState] = inState; + jsonObject[mSDKCommonPayload] = payload; + SendMessage(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact)); +} + +void ConnectionManager::SendToPropertyInspector(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload) +{ + QJsonObject jsonObject; + jsonObject[mSDKCommonEvent] = mSDKEventSendToPropertyInspector; + jsonObject[mSDKCommonContext] = inContext; + jsonObject[mSDKCommonAction] = inAction; + jsonObject[mSDKCommonPayload] = inPayload; + SendMessage(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact)); +} + +void ConnectionManager::SwitchToProfile(const QString &inDeviceID, const QString &inProfileName) +{ + if (!inDeviceID.isEmpty()) { + QJsonObject jsonObject; + jsonObject[mSDKCommonEvent] = mSDKEventSwitchToProfile; + jsonObject[mSDKCommonContext] = mPluginUUID; + jsonObject[mSDKCommonDevice] = inDeviceID; + if (!inProfileName.isEmpty()) { + QJsonObject payload; + payload[mSDKPayloadProfile] = inProfileName; + jsonObject[mSDKCommonPayload] = payload; + } + SendMessage(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact)); + } +} + +void ConnectionManager::LogMessage(const QString &inMessage) +{ + if (!inMessage.isEmpty()) { + QJsonObject jsonObject; + jsonObject[mSDKCommonEvent] = mSDKEventLogMessage; + QJsonObject payload; + payload[mSDKPayloadMessage] = inMessage; + jsonObject[mSDKCommonPayload] = payload; + SendMessage(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact)); + } +} + +void ConnectionManager::OnConnected() +{ + // Register plugin with Software + QJsonObject jsonObject; + jsonObject["event"] = mRegisterEvent; + jsonObject["uuid"] = mPluginUUID; + SendMessage(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact)); +} + +void ConnectionManager::OnDisonnected() +{ + QString messageString = "Socket disconnected: " + mWebsocketClient->errorString(); + Logger::LogToFile(messageString); + + qApp->quit(); + this->deleteLater(); +} + +void ConnectionManager::OnError(QAbstractSocket::SocketError error) +{ + QString errorString = "Socket error: " + mWebsocketClient->errorString(); + Logger::LogToFile(errorString); +} + +void ConnectionManager::OnMessage(const QString &message) +{ + Logger::LogToServer("OnMessage: " + message); + + try { + QJsonObject receivedJson = QJsonDocument::fromJson(message.toUtf8()).object(); + QString event = receivedJson.value(mSDKCommonEvent).toString(); + QString context = receivedJson.value(mSDKCommonContext).toString(); + QString action = receivedJson.value(mSDKCommonAction).toString(); + QString deviceID = receivedJson.value(mSDKCommonDevice).toString(); + QJsonObject payload = receivedJson.value(mSDKCommonPayload).toObject(); + if (event == mSDKEventKeyDown) { + mPlugin->KeyDownForAction(action, context, payload, deviceID); + } else if (event == mSDKEventKeyUp) { + mPlugin->KeyUpForAction(action, context, payload, deviceID); + } else if (event == mSDKEventWillAppear) { + mPlugin->WillAppearForAction(action, context, payload, deviceID); + } else if (event == mSDKEventWillDisappear) { + mPlugin->WillDisappearForAction(action, context, payload, deviceID); + } else if (event == mSDKEventDidReceiveSettings) { + mPlugin->DidReceiveSettings(action, context, payload, deviceID); + } else if (event == mSDKEventDidReceiveGlobalSettings) { + mPlugin->DidReceiveGlobalSettings(payload); + } else if (event == mSDKEventDeviceDidConnect) { + QJsonObject deviceInfo = receivedJson.value(mSDKCommonDeviceInfo).toObject(); + mPlugin->DeviceDidConnect(deviceID, deviceInfo); + } else if (event == mSDKEventDeviceDidDisconnect) { + mPlugin->DeviceDidDisconnect(deviceID); + } else if (event == mSDKEventSendToPlugin) { + mPlugin->SendToPlugin(action, context, payload, deviceID); + } else if (event == mSDKEventSystemDidWakeUp) { + mPlugin->SystemDidWakeUp(); + } else if (event == mSDKEventDialDown) { + mPlugin->DialDownForAction(action, context, payload, deviceID); + } else if (event == mSDKEventDialDown) { + mPlugin->DialUpForAction(action, context, payload, deviceID); + } else if (event == mSDKEventDialRotate) { + mPlugin->DialRotateForAction(action, context, payload, deviceID); + } + } catch (...) { + } +} + +void ConnectionManager::SendMessage(const QString &message) +{ + if (mWebsocketClient) { + mWebsocketClient->sendTextMessage(message); + } +} diff --git a/StreamDock-Plugin-SDK/SDQtSDK/SDK/ConnectionManager.h b/StreamDock-Plugin-SDK/SDQtSDK/SDK/ConnectionManager.h new file mode 100644 index 000000000..2b9352f98 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/SDK/ConnectionManager.h @@ -0,0 +1,67 @@ +#ifndef CONNECTIONMANAGER_H +#define CONNECTIONMANAGER_H + +#include +#include +#include +#include +#include "BasePlugin.h" +#include "SDKDefines.h" + +class ConnectionManager : public QObject +{ + Q_OBJECT + +public: + ConnectionManager(int inPort, + const QString &inPluginUUID, + const QString &inRegisterEvent, + const QString &inInfo, + BasePlugin *inPlugin, + QObject *parent = nullptr); + ~ConnectionManager(); + + // API to communicate with the Stream Dock application + void SetTitle(const QString &inTitle, + const QString &inContext, + mSDKTarget inTarget, + int state = -1); + void SetImage(const QString &inBase64ImageString, + const QString &inContext, + mSDKTarget inTarget, + int state = -1); + void ShowAlertForContext(const QString &inContext); + void ShowOKForContext(const QString &inContext); + void SetSettings(const QJsonObject &inPayload, + const QString &inContext); + void GetGlobalSettings(); + void SetGlobalSettings(const QJsonObject &inPayload); + void SetState(int inState, const QString &inContext); + void SendToPropertyInspector(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload); + void SwitchToProfile(const QString &inDeviceID, + const QString &inProfileName); + void LogMessage(const QString &inMessage); + + // Run + void Run(); + +private: + // Websocket callbacks + void OnConnected(); + void OnDisonnected(); + void OnError(QAbstractSocket::SocketError error); + void OnMessage(const QString &message); + // Send websocket message + void SendMessage(const QString &message); + + // Member variables + int mPort = 0; + QString mPluginUUID; + QString mRegisterEvent; + BasePlugin *mPlugin = nullptr; + QWebSocket *mWebsocketClient = nullptr; +}; + +#endif // CONNECTIONMANAGER_H diff --git a/StreamDock-Plugin-SDK/SDQtSDK/SDK/CustomMain.cpp b/StreamDock-Plugin-SDK/SDQtSDK/SDK/CustomMain.cpp new file mode 100644 index 000000000..5ea4cea43 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/SDK/CustomMain.cpp @@ -0,0 +1,74 @@ +#include "CustomMain.h" +#include +#include +#include +#include +#include +#include + +int CustomMain(int argc, const char **argv, BasePlugin *plugin) +{ + if (argc != 9) { + Logger::LogToFile(QString("Invalid number of parameters %1 instead of 9").arg(argc)); + return 1; + } + + int port = 0; + QString pluginUUID; + QString registerEvent; + QString info; + for (int argumentIndex = 0; argumentIndex < 4; argumentIndex++) { + QString parameter(argv[1 + 2 * argumentIndex]); + QString value(argv[1 + 2 * argumentIndex + 1]); + + if (parameter == mSDKPortParameter) { + port = value.toInt(); + } else if (parameter == mSDKPluginUUIDParameter) { + pluginUUID = value; + } else if (parameter == mSDKRegisterEventParameter) { + registerEvent = value; + } else if (parameter == mSDKInfoParameter) { + info = value; + } + } + + if (port == 0) { + Logger::LogToFile(QString("Invalid port number")); + return 1; + } + + if (pluginUUID.isEmpty()) { + Logger::LogToFile(QString("Invalid plugin UUID")); + return 1; + } + + if (registerEvent.isEmpty()) { + Logger::LogToFile(QString("Invalid registerEvent")); + return 1; + } + + if (info.isEmpty()) { + Logger::LogToFile(QString("Invalid info")); + return 1; + } + + // Initialize localization helper + QString language = "en"; + try { + QJsonObject infoJsonObject = QJsonDocument::fromJson(info.toUtf8()).object(); + QJsonObject applicationInfo = infoJsonObject.value(mSDKApplicationInfo).toObject(); + if (applicationInfo.contains(mSDKApplicationInfoLanguage)) { + language = applicationInfo.value(mSDKApplicationInfoLanguage).toString("en"); + } + } catch (...) { + } + Localizer::Initialize(language); + + // Create the connection manager + ConnectionManager *connectionManager = new ConnectionManager(port, pluginUUID, registerEvent, info, plugin); + Logger::SetConnectionManager(connectionManager); + + // Connect and start the event loop + connectionManager->Run(); + return 0; +} diff --git a/StreamDock-Plugin-SDK/SDQtSDK/SDK/CustomMain.h b/StreamDock-Plugin-SDK/SDQtSDK/SDK/CustomMain.h new file mode 100644 index 000000000..2e35a3fef --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/SDK/CustomMain.h @@ -0,0 +1,8 @@ +#ifndef CUSTOMMAIN_H +#define CUSTOMMAIN_H + +class BasePlugin; + +int CustomMain(int argc, const char **argv, BasePlugin *plugin); + +#endif // CUSTOMMAIN_H diff --git a/StreamDock-Plugin-SDK/SDQtSDK/SDK/Localizer.cpp b/StreamDock-Plugin-SDK/SDQtSDK/SDK/Localizer.cpp new file mode 100644 index 000000000..c3f749b10 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/SDK/Localizer.cpp @@ -0,0 +1,49 @@ +#include "Localizer.h" +#include +#include +#include + +static Localizer *sLocalizer = nullptr; + +void Localizer::Initialize(const QString &inLanguageCode) +{ + if (!sLocalizer) { + sLocalizer = new Localizer(inLanguageCode); + } +} + +QString Localizer::GetLocalizedString(const QString &inDefaultString) +{ + if (sLocalizer) { + return sLocalizer->GetLocalizedStringIntern(inDefaultString); + } + + return inDefaultString; +} + +Localizer::Localizer(const QString &inLanguageCode) +{ + try { + QString pluginPath = qApp->applicationDirPath(); + if (!inLanguageCode.isEmpty() && !pluginPath.isEmpty()) { + QString localizationFilePath = pluginPath + inLanguageCode + ".json"; + QFile file(localizationFilePath); + if (file.open(QFile::ReadOnly)) { + QJsonDocument languageJsonDocument = QJsonDocument::fromJson(file.readAll()); + QJsonObject languageJsonObject = languageJsonDocument.object(); + if (languageJsonObject.contains("Localization")) { + mLocalizationData = languageJsonObject.value("Localization").toObject(); + } else { + mLocalizationData = QJsonObject(); + } + } + file.close(); + } + } catch (...) { + } +} + +QString Localizer::GetLocalizedStringIntern(const QString &inDefaultString) +{ + return mLocalizationData.value(inDefaultString).toString(inDefaultString); +} diff --git a/StreamDock-Plugin-SDK/SDQtSDK/SDK/Localizer.h b/StreamDock-Plugin-SDK/SDQtSDK/SDK/Localizer.h new file mode 100644 index 000000000..c04dc8142 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/SDK/Localizer.h @@ -0,0 +1,21 @@ +#ifndef LOCALIZER_H +#define LOCALIZER_H + +#include +#include + +class Localizer +{ +public: + static void Initialize(const QString &inLanguageCode); + static QString GetLocalizedString(const QString &inDefaultString); + +private: + Localizer(const QString &inLanguageCode); + QString GetLocalizedStringIntern(const QString &inDefaultString); + +private: + QJsonObject mLocalizationData; +}; + +#endif // LOCALIZER_H diff --git a/StreamDock-Plugin-SDK/SDQtSDK/SDK/Logger.cpp b/StreamDock-Plugin-SDK/SDQtSDK/SDK/Logger.cpp new file mode 100644 index 000000000..3d436b9d9 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/SDK/Logger.cpp @@ -0,0 +1,37 @@ +#include "Logger.h" +#include +#include +#include +#include +#include + +namespace { + ConnectionManager *mConnectionManager = nullptr; + QMutex LogMutex; +}// namespace + +void Logger::SetConnectionManager(ConnectionManager *connection) +{ + mConnectionManager = connection; +} + +void Logger::LogToServer(const QString &message) +{ + if (mConnectionManager) { + mConnectionManager->LogMessage(message); + } +} + +void Logger::LogToFile(const QString &message) +{ + QMutexLocker LogMutexLocker(&LogMutex); + + QFile file("log.txt"); + if (file.open(QFile::ReadWrite | QFile::Append)) { + QString timeString = QString("[%1]").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")); + QString messageString = QString("%1 %2 %3\r\n").arg(timeString).arg(__FILEW__).arg(message); + file.write(messageString.toUtf8()); + file.flush(); + } + file.close(); +} diff --git a/StreamDock-Plugin-SDK/SDQtSDK/SDK/Logger.h b/StreamDock-Plugin-SDK/SDQtSDK/SDK/Logger.h new file mode 100644 index 000000000..d8869225f --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/SDK/Logger.h @@ -0,0 +1,18 @@ +#ifndef LOGGER_H +#define LOGGER_H + +#include + +class ConnectionManager; + +class Logger +{ +public: + Logger() = delete; + static void SetConnectionManager(ConnectionManager *connection); + + static void LogToServer(const QString &message); + static void LogToFile(const QString &message); +}; + +#endif // LOGGER_H diff --git a/StreamDock-Plugin-SDK/SDQtSDK/SDK/Plugin.cpp b/StreamDock-Plugin-SDK/SDQtSDK/SDK/Plugin.cpp new file mode 100644 index 000000000..281dab852 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/SDK/Plugin.cpp @@ -0,0 +1,167 @@ +#include "Plugin.h" +#include "Action.h" +#include "ConnectionManager.h" +#include "Logger.h" + +Plugin::Plugin() + : BasePlugin() +{ +} + +Plugin::~Plugin() +{ +} + +void Plugin::KeyDownForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + Logger::LogToFile(QString("No action for keydown - %1 %2") + .arg(inAction) + .arg(inContext)); + return; + } + action->KeyDown(inPayload); +} + +void Plugin::KeyUpForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + Logger::LogToFile(QString("No action for keyup - %1 %2") + .arg(inAction) + .arg(inContext)); + return; + } + action->KeyUp(inPayload); +} + +void Plugin::DialDownForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + Logger::LogToFile(QString("No action for dialPress - %1 %2") + .arg(inAction) + .arg(inContext)); + return; + } + action->DialDown(inPayload); +} + +void Plugin::DialUpForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + Logger::LogToFile(QString("No action for dialPress - %1 %2") + .arg(inAction) + .arg(inContext)); + return; + } + action->DialUp(inPayload); +} + +void Plugin::DialRotateForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + Logger::LogToFile(QString("No action for dialRotate - %1 %2") + .arg(inAction) + .arg(inContext)); + return; + } + int ticks = inPayload.value("ticks").toInt(); + bool pressed = inPayload.value("pressed").toBool(); + if (ticks < 0) + { + action->RotateCounterClockwise(inPayload, static_cast(-ticks), pressed); + } + else + { + action->RotateClockwise(inPayload, static_cast(ticks), pressed); + } +} + +void Plugin::WillAppearForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + Logger::LogToFile(QString("No action for WillAppear - %1 %2") + .arg(inAction) + .arg(inContext)); + return; + } + action->WillAppear(inPayload); +} + +void Plugin::WillDisappearForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + Logger::LogToFile(QString("No action for WillDisappear - %1 %2") + .arg(inAction) + .arg(inContext)); + return; + } + action->WillDisappear(inPayload); + RemoveAction(inAction, inContext); +} + +void Plugin::SendToPlugin(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDevice) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + Logger::LogToFile(QString("Received plugin request for unknown action %1 %2") + .arg(inAction) + .arg(inContext)); + return; + } + action->SendToPlugin(inPayload); +} + +void Plugin::DidReceiveSettings(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDevice) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + Logger::LogToFile(QString("No action for DidReceiveSettings: %1 %2") + .arg(inAction) + .arg(inContext)); + return; + } + action->DidReceiveSettings(inPayload); +} diff --git a/StreamDock-Plugin-SDK/SDQtSDK/SDK/Plugin.h b/StreamDock-Plugin-SDK/SDQtSDK/SDK/Plugin.h new file mode 100644 index 000000000..5be340aa2 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/SDK/Plugin.h @@ -0,0 +1,74 @@ +#ifndef PLUGIN_H +#define PLUGIN_H + +#include "BasePlugin.h" +#include +#include + +class Action; + +class Plugin : public BasePlugin +{ +protected: + /** + * Create or retrieve an Action instance for the given action/context. + * + * Return a null/empty ptr if the action is unrecognized. + */ + virtual Action *GetOrCreateAction(const QString &action, const QString &context) = 0; + /** + * Remove an Action instance for the given action/context. + */ + virtual bool RemoveAction(const QString &action, const QString &context) = 0; + +public: + Plugin(); + virtual ~Plugin(); + + virtual void KeyDownForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) override; + + virtual void KeyUpForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) override; + + virtual void DialDownForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) override; + + virtual void DialUpForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) override; + + virtual void DialRotateForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) override; + + virtual void WillAppearForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) override; + + virtual void WillDisappearForAction(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDeviceID) override; + + virtual void SendToPlugin(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDevice) override; + + virtual void DidReceiveSettings(const QString &inAction, + const QString &inContext, + const QJsonObject &inPayload, + const QString &inDevice) override; +}; + +#endif // PLUGIN_H diff --git a/StreamDock-Plugin-SDK/SDQtSDK/SDK/SDK.pri b/StreamDock-Plugin-SDK/SDQtSDK/SDK/SDK.pri new file mode 100644 index 000000000..7bd00eff8 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/SDK/SDK.pri @@ -0,0 +1,25 @@ +# websocket +QT += websockets + +CONFIG += c++11 + +HEADERS += \ + $$PWD/Action.h \ + $$PWD/BasePlugin.h \ + $$PWD/ConnectionManager.h \ + $$PWD/CustomMain.h \ + $$PWD/Localizer.h \ + $$PWD/Logger.h \ + $$PWD/Plugin.h \ + $$PWD/SDKDefines.h + +SOURCES += \ + $$PWD/Action.cpp \ + $$PWD/BasePlugin.cpp \ + $$PWD/ConnectionManager.cpp \ + $$PWD/CustomMain.cpp \ + $$PWD/Localizer.cpp \ + $$PWD/Logger.cpp \ + $$PWD/Plugin.cpp + +INCLUDEPATH += $$PWD diff --git a/StreamDock-Plugin-SDK/SDQtSDK/SDK/SDKDefines.h b/StreamDock-Plugin-SDK/SDQtSDK/SDK/SDKDefines.h new file mode 100644 index 000000000..e41570322 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/SDK/SDKDefines.h @@ -0,0 +1,140 @@ +#ifndef SDKDEFINES_H +#define SDKDEFINES_H + +// +// Common base-interface +// +#define mSDKCommonAction "action" +#define mSDKCommonEvent "event" +#define mSDKCommonContext "context" +#define mSDKCommonPayload "payload" +#define mSDKCommonDevice "device" +#define mSDKCommonDeviceInfo "deviceInfo" + +// +// Events +// +#define mSDKEventKeyDown "keyDown" +#define mSDKEventKeyUp "keyUp" +#define mSDKEventWillAppear "willAppear" +#define mSDKEventWillDisappear "willDisappear" +#define mSDKEventDeviceDidConnect "deviceDidConnect" +#define mSDKEventDeviceDidDisconnect "deviceDidDisconnect" +#define mSDKEventApplicationDidLaunch "applicationDidLaunch" +#define mSDKEventApplicationDidTerminate "applicationDidTerminate" +#define mSDKEventSystemDidWakeUp "systemDidWakeUp" +#define mSDKEventTitleParametersDidChange "titleParametersDidChange" +#define mSDKEventDidReceiveSettings "didReceiveSettings" +#define mSDKEventDidReceiveGlobalSettings "didReceiveGlobalSettings" +#define mSDKEventPropertyInspectorDidAppear "propertyInspectorDidAppear" +#define mSDKEventPropertyInspectorDidDisappear "propertyInspectorDidDisappear" +#define mSDKEventDialRotate "dialRotate" +#define mSDKEventDialDown "dialDown" +#define mSDKEventDialUp "dialUp" +#define mSDKEventTouchTap "touchTap" + +// +// Functions +// +#define mSDKEventSetTitle "setTitle" +#define mSDKEventSetImage "setImage" +#define mSDKEventShowAlert "showAlert" +#define mSDKEventShowOK "showOk" +#define mSDKEventGetSettings "getSettings" +#define mSDKEventSetSettings "setSettings" +#define mSDKEventGetGlobalSettings "getGlobalSettings" +#define mSDKEventSetGlobalSettings "setGlobalSettings" +#define mSDKEventSetState "setState" +#define mSDKEventSwitchToProfile "switchToProfile" +#define mSDKEventSendToPropertyInspector "sendToPropertyInspector" +#define mSDKEventSendToPlugin "sendToPlugin" +#define mSDKEventOpenURL "openUrl" +#define mSDKEventLogMessage "logMessage" +#define mSDKEventSetFeedback "setFeedback" +#define mSDKEventSetText "setText" + +// +// Payloads +// +#define mSDKPayloadSettings "settings" +#define mSDKPayloadCoordinates "coordinates" +#define mSDKPayloadState "state" +#define mSDKPayloadUserDesiredState "userDesiredState" +#define mSDKPayloadTitle "title" +#define mSDKPayloadTitleParameters "titleParameters" +#define mSDKPayloadImage "image" +#define mSDKPayloadURL "url" +#define mSDKPayloadTarget "target" +#define mSDKPayloadProfile "profile" +#define mSDKPayloadApplication "application" +#define mSDKPayloadIsInMultiAction "isInMultiAction" +#define mSDKPayloadMessage "message" + +#define mSDKPayloadCoordinatesColumn "column" +#define mSDKPayloadCoordinatesRow "row" + +// +// Device Info +// +#define mSDKDeviceInfoID "id" +#define mSDKDeviceInfoType "type" +#define mSDKDeviceInfoSize "size" +#define mSDKDeviceInfoName "name" + +#define mSDKDeviceInfoSizeColumns "columns" +#define mSDKDeviceInfoSizeRows "rows" + +// +// Title Parameters +// +#define mSDKTitleParametersShowTitle "showTitle" +#define mSDKTitleParametersTitleColor "titleColor" +#define mSDKTitleParametersTitleAlignment "titleAlignment" +#define mSDKTitleParametersFontFamily "fontFamily" +#define mSDKTitleParametersFontSize "fontSize" +#define mSDKTitleParametersCustomFontSize "customFontSize" +#define mSDKTitleParametersFontStyle "fontStyle" +#define mSDKTitleParametersFontUnderline "fontUnderline" + +// +// Connection +// +#define mSDKConnectSocketFunction "connectElgatoStreamDeckSocket" +#define mSDKRegisterPlugin "registerPlugin" +#define mSDKRegisterPropertyInspector "registerPropertyInspector" +#define mSDKPortParameter "-port" +#define mSDKPluginUUIDParameter "-pluginUUID" +#define mSDKRegisterEventParameter "-registerEvent" +#define mSDKInfoParameter "-info" +#define mSDKRegisterUUID "uuid" + +#define mSDKApplicationInfo "application" +#define mSDKPluginInfo "plugin" +#define mSDKDevicesInfo "devices" +#define mSDKColorsInfo "colors" +#define mSDKDevicePixelRatio "devicePixelRatio" + +#define mSDKApplicationInfoSoftwareUUID "softwareUUID" +#define mSDKApplicationInfoVersion "version" +#define mSDKApplicationInfoLanguage "language" +#define mSDKApplicationInfoPlatform "platform" +#define mSDKApplicationInfoPlatformVersion "platformVersion" +#define mSDKApplicationInfoPlatformMac "mac" +#define mSDKApplicationInfoPlatformWindows "windows" + +#define mSDKColorsInfoHighlightColor "highlightColor" +#define mSDKColorsInfoMouseDownColor "mouseDownColor" +#define mSDKColorsInfoDisabledColor "disabledColor" +#define mSDKColorsInfoButtonPressedTextColor "buttonPressedTextColor" +#define mSDKColorsInfoButtonPressedBackgroundColor "buttonPressedBackgroundColor" +#define mSDKColorsInfoButtonMouseOverBackgroundColor "buttonMouseOverBackgroundColor" +#define mSDKColorsInfoButtonPressedBorderColor "buttonPressedBorderColor" + +typedef int mSDKTarget; +enum { + mSDKTarget_HardwareAndSoftware = 0, + mSDKTarget_HardwareOnly = 1, + mSDKTarget_SoftwareOnly = 2 +}; + +#endif // SDKDEFINES_H diff --git a/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/CMakeLists.txt b/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/CMakeLists.txt new file mode 100644 index 000000000..1edd71dd2 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/CMakeLists.txt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f778da74d3ae8bbef6f9856c4e1ce753120c62ec64d6f35cc01e45428082f183 +size 3041 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/ExampleAction.cpp b/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/ExampleAction.cpp new file mode 100644 index 000000000..881854a21 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/ExampleAction.cpp @@ -0,0 +1,46 @@ +#include "ExampleAction.h" +#include +#include + +ExampleAction::ExampleAction(ConnectionManager *connection, const QString &action, const QString &context) + : Action{connection, action, context} +{ + +} + +void ExampleAction::DidReceiveSettings(const QJsonObject &payload) +{ + Logger::LogToServer("DidReceiveSettings"); +} + +void ExampleAction::KeyDown(const QJsonObject &payload) +{ + Logger::LogToServer("KeyDown"); + if (payload.value("state").toInt() == 0) { + SetState(1); + } else { + SetState(0); + } +} + +void ExampleAction::KeyUp(const QJsonObject &payload) +{ + // Log in release and debug builds + Logger::LogToServer("KeyUp"); +} + +void ExampleAction::WillAppear(const QJsonObject &payload) +{ + Logger::LogToServer("WillAppear"); +} + +void ExampleAction::WillDisappear(const QJsonObject &payload) +{ + Logger::LogToServer("WillAppear"); +} + +void ExampleAction::SendToPlugin(const QJsonObject &payload) +{ + QString payloadString = QJsonDocument(payload).toJson(QJsonDocument::Compact); + Logger::LogToServer(QString("Received message from property inspector: %1").arg(payloadString)); +} diff --git a/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/ExampleAction.h b/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/ExampleAction.h new file mode 100644 index 000000000..ded8eab44 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/ExampleAction.h @@ -0,0 +1,20 @@ +#ifndef EXAMPLEACTION_H +#define EXAMPLEACTION_H + +#include +#include + +class ExampleAction : public Action +{ +public: + ExampleAction(ConnectionManager *connection, const QString &action, const QString &context); + + virtual void DidReceiveSettings(const QJsonObject &payload); + virtual void KeyDown(const QJsonObject &payload); + virtual void KeyUp(const QJsonObject &payload); + virtual void SendToPlugin(const QJsonObject &payload); + virtual void WillAppear(const QJsonObject &payload); + virtual void WillDisappear(const QJsonObject &payload); +}; + +#endif // EXAMPLEACTION_H diff --git a/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/ExamplePlugin.cpp b/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/ExamplePlugin.cpp new file mode 100644 index 000000000..665568300 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/ExamplePlugin.cpp @@ -0,0 +1,53 @@ +#include "ExamplePlugin.h" +#include +#include + +ExamplePlugin::ExamplePlugin() + : Plugin() +{ +} + +ExamplePlugin::~ExamplePlugin() +{ + QMutexLocker mMutexLocker(&mVisibleContextsMutex); + + QStringList keys = mActions.keys(); + for (qint32 i = 0; i < keys.size(); ++i) { + delete mActions.value(keys[i]); + } +} + +Action *ExamplePlugin::GetOrCreateAction(const QString &action, const QString &context) +{ + QMutexLocker mMutexLocker(&mVisibleContextsMutex); + + if (action == "com.hotspot.streamdockcppsdk.demo.myaction1" || + action == "com.hotspot.streamdockcppsdk.demo.myaction2") { + if (!mActions.contains(context)) { + ExampleAction *tmpAction = new ExampleAction(mConnectionManager, action, context); + mActions.insert(context, tmpAction); + } + return mActions.value(context); + } + + Logger::LogToServer(QString("Asked to get or create unknown action: ").append(action)); + return nullptr; +} + +bool ExamplePlugin::RemoveAction(const QString &action, const QString &context) +{ + QMutexLocker mMutexLocker(&mVisibleContextsMutex); + + if (action == "com.hotspot.streamdockcppsdk.demo.myaction1" || + action == "com.hotspot.streamdockcppsdk.demo.myaction2") { + if (mActions.contains(context)) { + delete mActions.take(context); + return true; + } else { + return false; + } + } + + Logger::LogToServer(QString("Asked to get or create unknown action: ").append(action)); + return false; +} diff --git a/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/ExamplePlugin.h b/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/ExamplePlugin.h new file mode 100644 index 000000000..42eb70ba3 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/ExamplePlugin.h @@ -0,0 +1,22 @@ +#ifndef EXAMPLEPLUGIN_H +#define EXAMPLEPLUGIN_H + +#include +#include +#include +#include + +class ExamplePlugin : public Plugin +{ +public: + ExamplePlugin(); + ~ExamplePlugin(); + virtual Action *GetOrCreateAction(const QString &action, const QString &context) override; + virtual bool RemoveAction(const QString &action, const QString &context) override; + +private: + QMutex mVisibleContextsMutex; + QHash mActions; +}; + +#endif // EXAMPLEPLUGIN_H diff --git a/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/main.cpp b/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/main.cpp new file mode 100644 index 000000000..eedec31af --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/cmake/StreamDockQtPlugin/main.cpp @@ -0,0 +1,18 @@ +#include +#include +#include "ExamplePlugin.h" + +int main(int argc, const char **argv) +{ + QCoreApplication a(argc, (char **)argv); + + ExamplePlugin *myPlugin = new ExamplePlugin(); + QObject::connect(&a, &QCoreApplication::destroyed, [myPlugin](){ + delete myPlugin; + }); + if (CustomMain(argc, argv, myPlugin)) { + return 1; + } + + return a.exec(); +} diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/StreamDockQtPlugin.exe b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/StreamDockQtPlugin.exe new file mode 100644 index 000000000..e1e02a61d Binary files /dev/null and b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/StreamDockQtPlugin.exe differ diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/de.json b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/de.json new file mode 100644 index 000000000..468723cb4 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/de.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c60505a0e25f53bb86d88f8ab9e1089077a7e0141ebdfa76f0e65a92ebc7de90 +size 377 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/en.json b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/en.json new file mode 100644 index 000000000..468723cb4 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/en.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c60505a0e25f53bb86d88f8ab9e1089077a7e0141ebdfa76f0e65a92ebc7de90 +size 377 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/es.json b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/es.json new file mode 100644 index 000000000..468723cb4 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/es.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c60505a0e25f53bb86d88f8ab9e1089077a7e0141ebdfa76f0e65a92ebc7de90 +size 377 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/fr.json b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/fr.json new file mode 100644 index 000000000..468723cb4 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/fr.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c60505a0e25f53bb86d88f8ab9e1089077a7e0141ebdfa76f0e65a92ebc7de90 +size 377 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State0.png b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State0.png new file mode 100644 index 000000000..8af588dab --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21aa938de7025453a5c6c72b3f1f27e2d7caef59beb9566d3d213374b8d6026e +size 1522 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State0@2x.png b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State0@2x.png new file mode 100644 index 000000000..12c9dd281 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State0@2x.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d98dc120319df2e1cdedf83ad8fbc3edf7afddf04877fa4995d35e9a9ffaa96 +size 3907 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State1.png b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State1.png new file mode 100644 index 000000000..640e1f5c2 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:692037d1ae3b91c2b4c8f9824317820df079320d3236fd0c6112e18870d56bf3 +size 1653 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State1@2x.png b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State1@2x.png new file mode 100644 index 000000000..57122a59a --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State1@2x.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d7dfc03ab4f9e0d1f53191aca6fb5886a931c8ade933372c3de0cf07d9e2f687 +size 3547 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State0.png b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State0.png new file mode 100644 index 000000000..4c012b2c4 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6680297ad6133e276cdc43d3ffa74bfbaf9efe7dbf2cb9b979a314ee58525831 +size 1068 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State0@2x.png b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State0@2x.png new file mode 100644 index 000000000..1293caf5c --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State0@2x.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:538c1afaf8ff00e7dd575edeb6bc81ebe563353151fb3a1e56b662f8efdf6a87 +size 2994 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State1.png b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State1.png new file mode 100644 index 000000000..a0a5241f8 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b6a260194b148d4782c97ac6129c7c9649a8fd4c803533cc47e8a1091b8125a +size 1528 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State1@2x.png b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State1@2x.png new file mode 100644 index 000000000..b2f477415 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State1@2x.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ceaf9102ba83f45539e61a27ce5ebf073c1a9fcb27c8412416964b5a30fcead +size 3841 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon1.png b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon1.png new file mode 100644 index 000000000..9fd80c422 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0de468471d4ec9eba9e19fcb1fe6592cc9efb97f7d89b6ad75f4f845d5bed146 +size 229 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon1@2x.png b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon1@2x.png new file mode 100644 index 000000000..d7bd6f3ad --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon1@2x.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f1b35a7cd5280f9d3395282d40c8cb0ef6d15d10d3751719f75ad8d4533c1cf7 +size 344 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon2.png b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon2.png new file mode 100644 index 000000000..f2c55c785 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21195acda939241241d1f7be7a2c129e301be27ce7c961f31612404eb25b7674 +size 171 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon2@2x.png b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon2@2x.png new file mode 100644 index 000000000..c068060a9 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon2@2x.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:236df1df665479b69cd17baac9ff60d73b7c08758529ee0b7df42f49a127d186 +size 259 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/icons/pluginIcon.png b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/icons/pluginIcon.png new file mode 100644 index 000000000..b60cbf7af --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/icons/pluginIcon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e9a867177835ae4bab6648dcdebaeb097170850a322a9ff0dc15ecfb1182305 +size 306 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/icons/pluginIcon@2x.png b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/icons/pluginIcon@2x.png new file mode 100644 index 000000000..b7e2ace4f --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/images/icons/pluginIcon@2x.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59944b10f1da39ba5721b00797e4c79bce7a6f023385d6c8923fa927b0525976 +size 492 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/it.json b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/it.json new file mode 100644 index 000000000..468723cb4 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/it.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c60505a0e25f53bb86d88f8ab9e1089077a7e0141ebdfa76f0e65a92ebc7de90 +size 377 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/ja.json b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/ja.json new file mode 100644 index 000000000..468723cb4 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/ja.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c60505a0e25f53bb86d88f8ab9e1089077a7e0141ebdfa76f0e65a92ebc7de90 +size 377 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/manifest.json b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/manifest.json new file mode 100644 index 000000000..4eb5a2dab --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/manifest.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb46144c9ea145965b93c12deb8a0bbee7117909bfc215cf0cf72a4ba5a1df19 +size 1641 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/pt.json b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/pt.json new file mode 100644 index 000000000..468723cb4 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/pt.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c60505a0e25f53bb86d88f8ab9e1089077a7e0141ebdfa76f0e65a92ebc7de90 +size 377 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/zh_CN.json b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/zh_CN.json new file mode 100644 index 000000000..369bb3dbd --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/com.hotspot.cppsdk.demo.sdPlugin/zh_CN.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:125925a02dd79cb90ccc1d3958bf32ba9b33da2c7030e7749a7d8b86084fe8e6 +size 379 diff --git a/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/ExampleAction.cpp b/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/ExampleAction.cpp new file mode 100644 index 000000000..881854a21 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/ExampleAction.cpp @@ -0,0 +1,46 @@ +#include "ExampleAction.h" +#include +#include + +ExampleAction::ExampleAction(ConnectionManager *connection, const QString &action, const QString &context) + : Action{connection, action, context} +{ + +} + +void ExampleAction::DidReceiveSettings(const QJsonObject &payload) +{ + Logger::LogToServer("DidReceiveSettings"); +} + +void ExampleAction::KeyDown(const QJsonObject &payload) +{ + Logger::LogToServer("KeyDown"); + if (payload.value("state").toInt() == 0) { + SetState(1); + } else { + SetState(0); + } +} + +void ExampleAction::KeyUp(const QJsonObject &payload) +{ + // Log in release and debug builds + Logger::LogToServer("KeyUp"); +} + +void ExampleAction::WillAppear(const QJsonObject &payload) +{ + Logger::LogToServer("WillAppear"); +} + +void ExampleAction::WillDisappear(const QJsonObject &payload) +{ + Logger::LogToServer("WillAppear"); +} + +void ExampleAction::SendToPlugin(const QJsonObject &payload) +{ + QString payloadString = QJsonDocument(payload).toJson(QJsonDocument::Compact); + Logger::LogToServer(QString("Received message from property inspector: %1").arg(payloadString)); +} diff --git a/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/ExampleAction.h b/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/ExampleAction.h new file mode 100644 index 000000000..ded8eab44 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/ExampleAction.h @@ -0,0 +1,20 @@ +#ifndef EXAMPLEACTION_H +#define EXAMPLEACTION_H + +#include +#include + +class ExampleAction : public Action +{ +public: + ExampleAction(ConnectionManager *connection, const QString &action, const QString &context); + + virtual void DidReceiveSettings(const QJsonObject &payload); + virtual void KeyDown(const QJsonObject &payload); + virtual void KeyUp(const QJsonObject &payload); + virtual void SendToPlugin(const QJsonObject &payload); + virtual void WillAppear(const QJsonObject &payload); + virtual void WillDisappear(const QJsonObject &payload); +}; + +#endif // EXAMPLEACTION_H diff --git a/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/ExamplePlugin.cpp b/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/ExamplePlugin.cpp new file mode 100644 index 000000000..665568300 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/ExamplePlugin.cpp @@ -0,0 +1,53 @@ +#include "ExamplePlugin.h" +#include +#include + +ExamplePlugin::ExamplePlugin() + : Plugin() +{ +} + +ExamplePlugin::~ExamplePlugin() +{ + QMutexLocker mMutexLocker(&mVisibleContextsMutex); + + QStringList keys = mActions.keys(); + for (qint32 i = 0; i < keys.size(); ++i) { + delete mActions.value(keys[i]); + } +} + +Action *ExamplePlugin::GetOrCreateAction(const QString &action, const QString &context) +{ + QMutexLocker mMutexLocker(&mVisibleContextsMutex); + + if (action == "com.hotspot.streamdockcppsdk.demo.myaction1" || + action == "com.hotspot.streamdockcppsdk.demo.myaction2") { + if (!mActions.contains(context)) { + ExampleAction *tmpAction = new ExampleAction(mConnectionManager, action, context); + mActions.insert(context, tmpAction); + } + return mActions.value(context); + } + + Logger::LogToServer(QString("Asked to get or create unknown action: ").append(action)); + return nullptr; +} + +bool ExamplePlugin::RemoveAction(const QString &action, const QString &context) +{ + QMutexLocker mMutexLocker(&mVisibleContextsMutex); + + if (action == "com.hotspot.streamdockcppsdk.demo.myaction1" || + action == "com.hotspot.streamdockcppsdk.demo.myaction2") { + if (mActions.contains(context)) { + delete mActions.take(context); + return true; + } else { + return false; + } + } + + Logger::LogToServer(QString("Asked to get or create unknown action: ").append(action)); + return false; +} diff --git a/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/ExamplePlugin.h b/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/ExamplePlugin.h new file mode 100644 index 000000000..42eb70ba3 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/ExamplePlugin.h @@ -0,0 +1,22 @@ +#ifndef EXAMPLEPLUGIN_H +#define EXAMPLEPLUGIN_H + +#include +#include +#include +#include + +class ExamplePlugin : public Plugin +{ +public: + ExamplePlugin(); + ~ExamplePlugin(); + virtual Action *GetOrCreateAction(const QString &action, const QString &context) override; + virtual bool RemoveAction(const QString &action, const QString &context) override; + +private: + QMutex mVisibleContextsMutex; + QHash mActions; +}; + +#endif // EXAMPLEPLUGIN_H diff --git a/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/StreamDockQtPlugin.pro b/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/StreamDockQtPlugin.pro new file mode 100644 index 000000000..0feb98375 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/StreamDockQtPlugin.pro @@ -0,0 +1,68 @@ +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++17 + +# You can make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +# 版本 +VERSION = 1.0.0 +# 版本号(VERSION_STR可以在代码中调用) +DEFINES += VERSION_STR=\\\"$$VERSION\\\" +# Static/compile-time major version of the Stream Dock. +DEFINES += SD_VERSION_MAJOR=1 +# Static/compile-time minor version of the Stream Dock. +DEFINES += SD_VERSION_MINOR=0 +# Static/compile-time patch version of the Stream Dock. +DEFINES += SD_VERSION_PATCH=0 +# 目标文件名 +TARGET = "StreamDockQtPlugin" +# 根据CONFIG选择DESTDIR的路径 +CONFIG(debug, debug|release) { + # DESTDIR(可执行文件存放的文件路径) + DESTDIR = $$shell_path(./debug) +} else { + # 以管理员身份运行 + # QMAKE_LFLAGS += /MANIFESTUAC:\"level=\'requireAdministrator\' uiAccess=\'false\'\" + # 屏蔽 QDebug + # qDebug() QT_NO_DEBUG_OUTPUT + # qWarning() QT_NO_WARNING_OUTPUT + DEFINES += QT_NO_DEBUG_OUTPUT + # 在Release模式下,也能输出文件信息,行数 + DEFINES += QT_MESSAGELOGCONTEXT + # DESTDIR(可执行文件存放的文件路径) + DESTDIR = $$shell_path(./release) +} +# MOC命令将含Q_OBJECT的头文件转换为标准的头文件寄存的目录 +MOC_DIR = ./temp/moc +# RCC将qrc文件转化为头文件所寄存的目录 +RCC_DIR = ./temp/rcc +# UIC将ui转化为头文件所寄存的目录 +UI_DIR = ./temp/ui +# 指定所有中间文件.o(.obj)放置的目录。 +OBJECTS_DIR = ./temp/obj + +SOURCES += \ + ExampleAction.cpp \ + ExamplePlugin.cpp \ + main.cpp + +HEADERS += \ + ExampleAction.h \ + ExamplePlugin.h + +include(../../SDK/SDK.pri) + +# Win +win32 { + QMAKE_CFLAGS += /utf-8 + QMAKE_CXXFLAGS += /utf-8 +} + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target diff --git a/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/main.cpp b/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/main.cpp new file mode 100644 index 000000000..eedec31af --- /dev/null +++ b/StreamDock-Plugin-SDK/SDQtSDK/qmake/StreamDockQtPlugin/main.cpp @@ -0,0 +1,18 @@ +#include +#include +#include "ExamplePlugin.h" + +int main(int argc, const char **argv) +{ + QCoreApplication a(argc, (char **)argv); + + ExamplePlugin *myPlugin = new ExamplePlugin(); + QObject::connect(&a, &QCoreApplication::destroyed, [myPlugin](){ + delete myPlugin; + }); + if (CustomMain(argc, argv, myPlugin)) { + return 1; + } + + return a.exec(); +} diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/.prettierignore b/StreamDock-Plugin-SDK/SDVueSDK/vue/.prettierignore new file mode 100644 index 000000000..55564ecca --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/.prettierignore @@ -0,0 +1,8 @@ +.vscode +.gitignore +dist +node_modules +public +tsconfig.json +tsconfig.*.json +auto-imports.d.ts \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/.prettierrc.yaml b/StreamDock-Plugin-SDK/SDVueSDK/vue/.prettierrc.yaml new file mode 100644 index 000000000..46dcdb421 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/.prettierrc.yaml @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4772855d068eb18c7211a3b9edf15d1867a4ee47390250cad9f566c1e1b0e135 +size 199 diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/README.md b/StreamDock-Plugin-SDK/SDVueSDK/vue/README.md new file mode 100644 index 000000000..e1e40550d --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/README.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bef78e9ad5535dcfee0896b32122098a46f50e636f6454c0bb48852e8e529d1d +size 205 diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/index.html b/StreamDock-Plugin-SDK/SDVueSDK/vue/index.html new file mode 100644 index 000000000..0d2a46801 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/index.html @@ -0,0 +1,13 @@ + + + + + + + VUE + + +
+ + + diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/package.json b/StreamDock-Plugin-SDK/SDVueSDK/vue/package.json new file mode 100644 index 000000000..6f604efbc --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/package.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f7efbf99cf8754599ce22f789431471da516e4e19695695322ac366e48ee3921 +size 688 diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/postcss.config.js b/StreamDock-Plugin-SDK/SDVueSDK/vue/postcss.config.js new file mode 100644 index 000000000..2e7af2b7f --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/public/images/default.png b/StreamDock-Plugin-SDK/SDVueSDK/vue/public/images/default.png new file mode 100644 index 000000000..5bc66756e --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/public/images/default.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1513c1692f502385764eab81c97d21911d54e44e235c54bbd53f878ad0826da9 +size 3060 diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/public/images/icon.png b/StreamDock-Plugin-SDK/SDVueSDK/vue/public/images/icon.png new file mode 100644 index 000000000..faef2a09a --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/public/images/icon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cd70e22115dfc209b226f8f3a7ec95b345531038abf25c68e2efbbb3bcaf8195 +size 973 diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/public/interval.js b/StreamDock-Plugin-SDK/SDVueSDK/vue/public/interval.js new file mode 100644 index 000000000..9cc4fb582 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/public/interval.js @@ -0,0 +1,19 @@ +// 开辟一个新线程: 解决原生定时器延迟问题 +const TimerPond = {}; + +const Handle = { + setInterval(data) { + if (TimerPond[data.uuid]) return; + TimerPond[data.uuid] = setInterval(() => { + self.postMessage({ uuid: data.uuid, event: 'setInterval' }); + }, data.delay); + }, + clearInterval(data) { + clearInterval(TimerPond[data.uuid]); + delete TimerPond[data.uuid]; + } +}; + +self.onmessage = function ({ data }) { + Handle[data.event](data); +}; diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/script/_.html b/StreamDock-Plugin-SDK/SDVueSDK/vue/script/_.html new file mode 100644 index 000000000..dffa321b1 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/script/_.html @@ -0,0 +1,11 @@ + + + + Loading... + + + + + diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/script/autofile.cjs b/StreamDock-Plugin-SDK/SDVueSDK/vue/script/autofile.cjs new file mode 100644 index 000000000..40f959fd5 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/script/autofile.cjs @@ -0,0 +1,73 @@ +const path = require('path'); +const fs = require('fs-extra'); + +const manifest = {}; +const { PUUID, Actions, i18n, CategoryIcon, Version, Software, ApplicationsToMonitor } = require('../src/manifest.cjs'); +console.log('开始执行自动化构建...'); + +// 开发环境处理 +if (process.argv[2] === 'dev') { + fs.removeSync('./dist') || fs.mkdirSync('./dist') || fs.copySync('./public', './dist'); + fs.copyFileSync('./script/_.html', './dist/_.html'); +} + +// 根据用户生成 +manifest.Actions = Actions.map((item) => { + item.Name = item.i18n['en'].Name; + item.Tooltip = item.i18n['en'].Tooltip; + item.UUID = `${PUUID}.` + item.UUID; + item.PropertyInspectorPath = process.argv[2] === 'dev' ? '_.html' : 'index.html'; + return item; +}); +manifest.Version = Version; +manifest.Name = i18n['en'].Name; +manifest.Icon = CategoryIcon; +manifest.CategoryIcon = CategoryIcon; +manifest.Category = i18n['en'].Name; +manifest.Description = i18n['en'].Description; +manifest.CodePath = process.argv[2] === 'dev' ? '_.html' : 'index.html'; + +// 版本固定生成 +manifest.SDKVersion = 2; +manifest.Author = 'MiraBox'; +manifest.URL = 'http://video.hotspotek.com.cn/'; +manifest.OS = [ + { + Platform: 'mac', + MinimumVersion: '10.11' + }, + { + Platform: 'windows', + MinimumVersion: '7' + } +]; + +// 语言文件生成 +Object.keys(i18n).forEach((item) => { + const obj = {}; + obj.Name = i18n[item].Name; + obj.Category = i18n[item].Name; + obj.Description = i18n[item].Description; + manifest.Actions.forEach((action) => { + obj[action.UUID] = { + Name: action.i18n[item].Name, + Tooltip: action.i18n[item].Tooltip + }; + }); + obj.Localization = {}; + fs.writeJSONSync(`./dist/${item}.json`, obj); +}); + +// 生成清单文件 +manifest.Actions = manifest.Actions.map((item) => { + delete item.i18n; + return item; +}); +manifest.Software = Software; +manifest.ApplicationsToMonitor = ApplicationsToMonitor +fs.writeJSONSync('./dist/manifest.json', manifest, { spaces: 2, EOL: '\r\n' }); + +// 复制到插件文件夹 +const PluginName = `${PUUID}.sdPlugin`; +const PluginPath = path.join(process.env.APPDATA, 'HotSpot/StreamDock/plugins', PluginName); +fs.removeSync(PluginPath) || fs.mkdirSync(PluginPath) || fs.copySync('./dist', PluginPath); diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/src/components/tab-view.vue b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/components/tab-view.vue new file mode 100644 index 000000000..da857b5bc --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/components/tab-view.vue @@ -0,0 +1,38 @@ + + + + + + diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/src/hooks/i18n.ts b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/hooks/i18n.ts new file mode 100644 index 000000000..f4617c349 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/hooks/i18n.ts @@ -0,0 +1,12 @@ +export const useI18nStore = () => { + const language = window.argv[3].application.language; + const localString = { + en: { + 操作选项: 'Operation options' + }, + zh_CN: { + 操作选项: '操作选项' + } + }; + return localString[language] || localString['en']; +}; diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/src/hooks/plugin.ts b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/hooks/plugin.ts new file mode 100644 index 000000000..ab73666fd --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/hooks/plugin.ts @@ -0,0 +1,195 @@ +import { defineStore } from 'pinia'; + +// 插件钩子 +export const usePluginStore = defineStore('pluginStore', () => { + document.title = window.argv[3].plugin.uuid + ' - 插件'; + + // 定时器线程 + const Timer = new Worker('interval.js'); + const TimerSubscribe: { uuid: string; fn: () => void }[] = []; + Timer.addEventListener('message', ({ data: { event, uuid } }: { data: { event: string; uuid: string } }) => { + const subIndex = TimerSubscribe.findIndex((item) => item.uuid === uuid); + subIndex !== -1 && event === 'setInterval' && TimerSubscribe[subIndex].fn(); + }); + + // 创建定时器 + const Interval = (uuid: string, delay: number, fn: () => void) => { + TimerSubscribe.findIndex((item) => item.uuid === uuid) === -1 && TimerSubscribe.push({ uuid, fn }); + Timer.postMessage({ event: 'setInterval', uuid, delay }); + }; + + // 销毁定时器 + const Unterval = (uuid: string) => { + const subIndex = TimerSubscribe.findIndex((item) => item.uuid === uuid); + subIndex !== -1 && TimerSubscribe.splice(subIndex, 1); + Timer.postMessage({ event: 'clearInterval', uuid }); + }; + + // 连接软件 + const message = ref(); + const server = new WebSocket('ws://127.0.0.1:' + window.argv[0]); + server.onopen = () => server.send(JSON.stringify({ event: window.argv[2], uuid: window.argv[1] })); + server.onmessage = (e) => ( + message.value = JSON.parse(e.data) + ); + + //全局设置数据 + const globalSettings = ref(); + //设置全局设置数据 + const setGlobalSettings = (payload: any) => { + server.send(JSON.stringify({ event: 'setGlobalSettings', context: window.argv[1], payload })); + globalSettings.value = payload; + }; + + //获取全局设置数据 + const getGlobalSettings = () => { + server.send(JSON.stringify({ event: 'getGlobalSettings', context: window.argv[1] })); + } + + // 操作数据存储 + class Actions { + settings: {}; + action: string; + context: string; + title: string; + titleParameters = {} as titleParameters; + constructor(action: string, context: string, settings: {}) { + this.action = action; + this.context = context; + this.settings = settings; + } + + // 添加操作 + static list: Actions[] = []; + static addAction = (action: string, context: string, settings: {}) => { + const instance = new Actions(action, context, settings); + this.list.push(instance); + return instance; + }; + + // 删除操作 + static delAction = (context: string) => { + const i = this.list.findIndex((item) => item.context === context); + i !== -1 && this.list.splice(i, 1); + }; + + // 获取操作数据 + static getAction = (context: string) => { + return Actions.list.find((item) => item.context === context); + }; + + // 获取所有操作数据 + static getActions = (action: string) => { + return this.list.filter((item) => item.action === action); + }; + + // 通知属性检查器 + sendToPropertyInspector = (payload: any) => { + server.send(JSON.stringify({ event: 'sendToPropertyInspector', action: this.action, context: this.context, payload })); + }; + + // 切换状态 + setState = (state: number) => { + server.send(JSON.stringify({ event: 'setState', context: this.context, payload: { state } })); + }; + + // 设置标题 + setTitle = (title: string) => { + server.send(JSON.stringify({ event: 'setTitle', context: this.context, payload: { title, target: 0 } })); + }; + + // 设置图片 + setImage = (url: string) => { + if (url.includes('data:')) { + server.send(JSON.stringify({ event: 'setImage', context: this.context, payload: { target: 0, image: url } })); + return; + } + const image = new Image(); + image.src = url; + image.onload = () => { + const canvas = document.createElement('canvas'); + canvas.width = image.naturalWidth; + canvas.height = image.naturalHeight; + const ctx = canvas.getContext('2d'); + ctx.drawImage(image, 0, 0); + server.send(JSON.stringify({ event: 'setImage', context: this.context, payload: { target: 0, image: canvas.toDataURL('image/png') } })); + }; + }; + + // 设置持久化数据 + setSettings = (payload: any) => { + this.settings = payload; + server.send(JSON.stringify({ event: 'setSettings', context: this.context, payload })); + }; + + // 默认浏览器打开 + openUrl = (url: string) => { + server.send(JSON.stringify({ event: 'openUrl', payload: { url } })); + }; + } + + return { + message, + globalSettings, + Interval, + Unterval, + setGlobalSettings, + getGlobalSettings, + ActionArr: Actions.list, + getAction: Actions.getAction, + addAction: Actions.addAction, + delAction: Actions.delAction, + getActions: Actions.getActions + }; +}); + +// !! 请勿更改此处 !! +type MessageTypes = { plugin: StreamDock.PluginMessage; action: StreamDock.ActionMessage }; +type payload = { + settings: any; +} +export const useWatchEvent = (type: T, MessageEvents: MessageTypes[T]) => { + const plugin = usePluginStore(); + + + if (type === 'plugin') { + return watch( + () => plugin.message, + () => { + if (!plugin.message) return; + if (plugin.message.action) return; + MessageEvents[plugin.message.event]?.(JSON.parse(JSON.stringify(plugin.message))); + if (plugin.message.event === 'didGetGlobalSettings') { + plugin.globalSettings = (plugin.message.payload as payload).settings; + } + } + ); + } + const Events: StreamDock.ActionMessage = { + willAppear({ action, context, payload }) { + !plugin.getAction(context) && plugin.addAction(action, context, payload.settings); + }, + willDisappear({ context }) { + plugin.delAction(context); + }, + didReceiveSettings({ context, payload }) { + const action = plugin.getAction(context); + action.settings = payload.settings; + }, + titleParametersDidChange({ context, payload }) { + const action = plugin.getAction(context); + action.title = payload.title; + action.titleParameters = payload.titleParameters; + } + }; + return watch( + () => plugin.message, + () => { + if (!plugin.message) return; + if (plugin.message.action !== MessageEvents['ActionID']) return; + const data = JSON.parse(JSON.stringify(plugin.message)); + Events[plugin.message.event]?.(data); + MessageEvents[plugin.message.event]?.(data); + } + ); +}; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/src/hooks/property.ts b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/hooks/property.ts new file mode 100644 index 000000000..47f4ec419 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/hooks/property.ts @@ -0,0 +1,143 @@ +import TabView from '@/components/tab-view.vue'; +import { defineStore } from 'pinia'; + +// 属性钩子 +export { TabView }; +export const usePropertyStore = defineStore('propertyStore', () => { + document.title = window.argv[3].plugin.uuid + ' - 属性检查器'; + + // 监听数据 + const preventWatch = ref(false); + const settings = ref(window.argv[4].payload.settings); + watch( + settings, + () => { + if (preventWatch.value) return; + server.send( + JSON.stringify({ + event: 'setSettings', + context: window.argv[1], + payload: settings.value + }) + ); + }, + { deep: true } + ); + + // 连接软件 + const message = ref(); + const server = new WebSocket('ws://127.0.0.1:' + window.argv[0]); + server.onopen = () => server.send(JSON.stringify({ event: window.argv[2], uuid: window.argv[1] })); + server.onmessage = (e) => { + message.value = JSON.parse(e.data) + }; + + // 通知插件 + const sendToPlugin = (payload: any) => { + server.send( + JSON.stringify({ + event: 'sendToPlugin', + action: window.argv[4].action, + context: window.argv[1], + payload + }) + ); + }; + + // 更改状态 + const setState = (state: number) => { + server.send( + JSON.stringify({ + event: 'setState', + context: window.argv[4].context, + payload: { state } + }) + ); + }; + + // 设置标题 + const setTitle = (title: string) => { + server.send( + JSON.stringify({ + event: 'setTitle', + context: window.argv[4].context, + payload: { + title, + target: 0 + } + }) + ); + }; + + const getGlobalSettings = () => { + server.send( + JSON.stringify({ + event: 'getGlobalSettings', + context: window.argv[1], + }) + ); + }; + + // 设置图片 + const setImage = (url: string) => { + if (url.includes('data:')) { + server.send(JSON.stringify({ event: 'setImage', context: window.argv[4].context, payload: { target: 0, image: url } })); + return; + } + const image = new Image(); + image.src = url; + image.onload = () => { + const canvas = document.createElement('canvas'); + canvas.width = image.naturalWidth; + canvas.height = image.naturalHeight; + const ctx = canvas.getContext('2d'); + ctx.drawImage(image, 0, 0); + server.send(JSON.stringify({ event: 'setImage', context: window.argv[4].context, payload: { target: 0, image: canvas.toDataURL('image/png') } })); + }; + }; + + // 用默认浏览器打开 + const openUrl = (url: string) => { + server.send( + JSON.stringify({ + event: 'openUrl', + payload: { url } + }) + ); + }; + + return { + message, + preventWatch, + settings, + sendToPlugin, + getGlobalSettings, + setState, + setTitle, + setImage, + openUrl + }; +}); + +// !! 请勿更改此处 !! +export const useWatchEvent = (MessageEvents: StreamDock.ProperMessage) => { + const property = usePropertyStore(); + const Events: StreamDock.ProperMessage = { + didReceiveSettings(data) { + property.preventWatch = true; + property.settings = data.payload.settings; + nextTick(() => { + property.preventWatch = false; + }); + } + }; + watch( + () => property.message, + () => { + if (!property.message) return; + const data = JSON.parse(JSON.stringify(property.message)); + Events[property.message.event]?.(data); + MessageEvents[property.message.event]?.(data); + } + ); +}; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/src/main.css b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/main.css new file mode 100644 index 000000000..ea1f5adfc --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/main.css @@ -0,0 +1,5 @@ +/* ./src/index.css */ + +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/src/main.ts b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/main.ts new file mode 100644 index 000000000..edc32d1f1 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/main.ts @@ -0,0 +1,16 @@ +import { createPinia } from 'pinia'; +import Plugin from '@/plugin/index.vue'; +import Property from '@/pages/index.vue'; +import './main.css'; + + +// 软件接口 +window.connectSDSocket = function () { + window.argv = [arguments[0], arguments[1], arguments[2], JSON.parse(arguments[3]), arguments[4] && JSON.parse(arguments[4])]; + const app = arguments[4] ? createApp(Property) : createApp(Plugin); + app.use(createPinia()).mount('#app'); +}; + +// 兼容Elgato接口 +window.connectSocket = window.connectSDSocket; +window.connectElgatoStreamDeckSocket = window.connectSDSocket; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/src/manifest.cjs b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/manifest.cjs new file mode 100644 index 000000000..7b152d0cb --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/manifest.cjs @@ -0,0 +1,68 @@ +/** + * StreamDock Plugin Template V1.2.1 说明文件 => + * + * 1 => 开发环境支持热更新 => 修改代码无需重启服务器和软件 ( 修改图片/配置文件时需要重启 ) ! + * 2 => 自动打包到插件目录 => 使用 pnpm dev/build 即可自动打包到插件目录,无需手动复制删除。 + * 3 => 数据持久化驱动视图 => 通过 v-model 绑定 settings 的值即可实现双向绑定持久化数据回显啦 ! + * 4 => 完美集成 Navie UI 组件库 => 主题可调,无需穿透样式,有超过 90 个组件,希望能帮你少写点代码。 + * + * !! 注意事项 !! => 自动化含有许多约定配置 => 以下内容请务必认真填写 => 祝你开发愉快 _> + * + * =========== Kriac =================================================================================== 于 2024.03.30 更新 ===========> + */ + +const Plugin = { + UUID: 'com.streamdock.demo', + version: '1.0.0', + Icon: 'images/icon.png', + i18n: { + en: { + Name: 'Keyboard Action', + Description: 'StreamDock Actions' + }, + zh_CN: { + Name: '键盘操作', + Description: 'StreamDock 的操作示例列表' + } + }, + Software: { + MinimumVersion: "6.5" + }, + ApplicationsToMonitor: { + windows: [ + ] + } +}; + +// 操作数组 +const Actions = [ + { + UUID: 'action1', + Icon: 'images/icon.png', + i18n: { + en: { + Name: 'Action1', + Tooltip: 'This is an action' + }, + zh_CN: { + Name: '操作一', + Tooltip: '这是一个操作' + } + }, + state: 0, + States: [ + { + FontSize: '10', + TitleAlignment: 'bottom', + Image: 'images/default.png' + } + ], + Settings: {}, + UserTitleEnabled: false, + SupportedInMultiActions: false, + Controllers: ['Keypad', 'Information'] + } +]; + +// !! 请勿修改 !! +module.exports = { PUUID: Plugin.UUID, ApplicationsToMonitor: Plugin.ApplicationsToMonitor, Software: Plugin.Software, Version: Plugin.version, CategoryIcon: Plugin.Icon, i18n: Plugin.i18n, Actions }; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/src/pages/actions/action1.vue b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/pages/actions/action1.vue new file mode 100644 index 000000000..ccfa7e4e2 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/pages/actions/action1.vue @@ -0,0 +1,22 @@ + + + + + diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/src/pages/index.vue b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/pages/index.vue new file mode 100644 index 000000000..5b5508a28 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/pages/index.vue @@ -0,0 +1,74 @@ + + + + + + diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/src/plugin/actions/action1.ts b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/plugin/actions/action1.ts new file mode 100644 index 000000000..45ad06967 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/plugin/actions/action1.ts @@ -0,0 +1,16 @@ +import { usePluginStore, useWatchEvent } from '@/hooks/plugin'; + +export default function (name: string) { + const ActionID = `${window.argv[3].plugin.uuid}.${name}`; + + // 事件侦听器 + const plugin = usePluginStore(); + useWatchEvent('action', { + ActionID, + willAppear({ context }) { + console.log('创建:', context); + }, + keyUp({ context }){ + } + }); +} diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/src/plugin/index.vue b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/plugin/index.vue new file mode 100644 index 000000000..9706dab5f --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/plugin/index.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/src/types/_streamdock.d.ts b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/types/_streamdock.d.ts new file mode 100644 index 000000000..504774423 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/types/_streamdock.d.ts @@ -0,0 +1,261 @@ +/// + +interface Window { + argv: StreamDock.Argv; // 入口参数 + connectSDSocket(): void; // 入口函数 + connectMiraBoxSDSocket(): void; // 入口函数 + connectSocket(): void; //兼容Elgato接口 + connectElgatoStreamDeckSocket(): void; //兼容Elgato接口 + onFilePickerReturn(files: string): void; // 文件上传触发 => 用于获取绝对路径 +} + +// 标题参数 +type titleParameters = EventPayload.titleParametersDidChange['payload']['titleParameters']; + +// 事件负载 +declare namespace EventPayload { + // 操作持久化数据 + type didReceiveSettings = { + action: string; + event: string; + device: string; + context: string; + payload: { + settings: {}; + coordinates: { + column: number; + row: number; + }; + }; + isInMultiAction: boolean; + }; + type didReceiveGlobalSettings = { + event: string; + payload: { + settings: {}; + }; + }; + type applicationDidLaunch = { + event: string; + payload: { + application: string + }; + }; + type applicationDidTerminate = { + event: string; + payload: { + application: string + }; + }; + type systemDidWakeUp = { + event: string; + }; + // 按下|抬起|触摸 + type keyDownUpTouchTap = { + action: string; + event: string; + context: string; + device: string; + payload: { + settings: {}; + coordinates: { + column: number; + row: number; + }; + state: number; + userDesiredState: number; + isInMultiAction: boolean; + }; + }; + // 创建|注销 操作 + type willAppearDisappear = { + action: string; + event: string; + context: string; + device: string; + payload: { + settings: {}; + coordinates: { + column: number; + row: number; + }; + state: number; + isInMultiAction: boolean; + }; + }; + // 修改标题参数 + type titleParametersDidChange = { + action: string; + event: string; + context: string; + device: string; + payload: { + coordinates: { + column: number; + row: number; + }; + settings: {}; + state: number; + title: string; + titleParameters: { + fontFamily: string; + fontSize: number; + fontStyle: string; + fontUnderline: boolean; + showTitle: boolean; + titleAlignment: string; + titleColor: string; + }; + }; + }; + // 连接|断开 设备 + type deviceDidConnectDisconnect = { + event: string; + device: string; + deviceInfo: { + name: string; + type: number; + size: { + columns: number; + rows: number; + }; + }; + }; + // 显示|注销 属性检查器 + type propertyInspectorDidAppearDisappear = { + action: string; + event: string; + context: string; + device: string; + }; + // 接收属性检查器消息 + type sendToPlugin = { + event: string; + action: string; + context: string; + payload: { [k: string]: any }; + }; + // 接收插件消息 + type sendToPropertyInspector = { + action: string; + event: string; + context: string; + payload: { [k: string]: any }; + }; + //旋钮按下抬起 + type KnobUPDown = { + action: string; + event: string; + device: string; + context: string; + payload: { + controller: "Knob"; + isInMultiAction: boolean; + coordinates: { + column: number; + row: number; + }; + userDesiredState: number; + setting: {}; + state: number; + }; + }; + //旋钮旋转 + type dialRotate = { + action: string; + event: string; + device: string; + context: string; + payload: { + pressed: boolean; + coordinates: { + column: number; + row: number; + }; + setting: {}; + ticks: number; + } + }; +} + +// 软件相关 +declare namespace StreamDock { + // 入口参数 + type Argv = [ + string, + string, + string, + { + application: { + font: string; + language: string; + platform: string; + platformVersion: string; + version: string; + }; + plugin: { + uuid: string; + version: string; + }; + }, + { + action: string; + context: string; + payload: { + controller: string; + coordinates: { + column: number; + row: number; + }; + isInMultiAction: boolean; + settings: any; + state: number; + }; + } + ]; + + // 消息通信 + type Message = { + event: string; + action?: string; + context?: string; + payload?: unknown; + }; + + // 属性检查器事件 + type ProperMessage = { + didReceiveSettings?(this: ProperMessage, data: EventPayload.didReceiveSettings): void; + didReceiveGlobalSettings?(this: ProperMessage, data: EventPayload.didReceiveGlobalSettings): void; + sendToPropertyInspector?(this: ProperMessage, data: EventPayload.sendToPropertyInspector): void; + }; + + // 操作触发事件 + type ActionMessage = { + ActionID?: string; + didReceiveSettings?(this: ActionMessage, data: EventPayload.didReceiveSettings): void; + keyDown?(this: ActionMessage, data: EventPayload.keyDownUpTouchTap): void; + keyUp?(this: ActionMessage, data: EventPayload.keyDownUpTouchTap): void; + touchTap?(this: ActionMessage, data: EventPayload.keyDownUpTouchTap): void; + willAppear?(this: ActionMessage, data: EventPayload.willAppearDisappear): void; + willDisappear?(this: ActionMessage, data: EventPayload.willAppearDisappear): void; + titleParametersDidChange?(this: ActionMessage, data: EventPayload.titleParametersDidChange): void; + propertyInspectorDidAppear?(this: ActionMessage, data: EventPayload.propertyInspectorDidAppearDisappear): void; + propertyInspectorDidDisappear?(this: ActionMessage, data: EventPayload.propertyInspectorDidAppearDisappear): void; + sendToPlugin?(this: ActionMessage, data: EventPayload.sendToPlugin): void; + dialDown?(this: ActionMessage, data: EventPayload.KnobUPDown): void; + keyUp?(this: ActionMessage, data: EventPayload.KnobUPDown): void; + dialRotate?(this: ActionMessage, data: EventPayload.dialRotate): void; + }; + + // 插件触发事件 + type PluginMessage = { + // 设备连接 TODO: 参数未知 + deviceDidConnect?(this: PluginMessage, data: EventPayload.deviceDidConnectDisconnect): void; + // 设备断开 TODO: 参数未知 + deviceDidDisconnect?(this: PluginMessage, data: EventPayload.deviceDidConnectDisconnect): void; + didReceiveGlobalSettings?(this: PluginMessage, data: EventPayload.didReceiveGlobalSettings): void; + applicationDidLaunch?(this: PluginMessage, data: EventPayload.applicationDidLaunch): void; + applicationDidTerminate?(this: PluginMessage, data: EventPayload.applicationDidTerminate): void; + systemDidWakeUp?(this: PluginMessage, data: EventPayload.systemDidWakeUp): void; + }; +} diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/src/types/auto-imports.d.ts b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/types/auto-imports.d.ts new file mode 100644 index 000000000..d298c3cca --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/src/types/auto-imports.d.ts @@ -0,0 +1,66 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// noinspection JSUnusedGlobalSymbols +// Generated by unplugin-auto-import +export {} +declare global { + const EffectScope: typeof import('vue')['EffectScope'] + const computed: typeof import('vue')['computed'] + const createApp: typeof import('vue')['createApp'] + const customRef: typeof import('vue')['customRef'] + const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] + const defineComponent: typeof import('vue')['defineComponent'] + const effectScope: typeof import('vue')['effectScope'] + const getCurrentInstance: typeof import('vue')['getCurrentInstance'] + const getCurrentScope: typeof import('vue')['getCurrentScope'] + const h: typeof import('vue')['h'] + const inject: typeof import('vue')['inject'] + const isProxy: typeof import('vue')['isProxy'] + const isReactive: typeof import('vue')['isReactive'] + const isReadonly: typeof import('vue')['isReadonly'] + const isRef: typeof import('vue')['isRef'] + const markRaw: typeof import('vue')['markRaw'] + const nextTick: typeof import('vue')['nextTick'] + const onActivated: typeof import('vue')['onActivated'] + const onBeforeMount: typeof import('vue')['onBeforeMount'] + const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] + const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] + const onDeactivated: typeof import('vue')['onDeactivated'] + const onErrorCaptured: typeof import('vue')['onErrorCaptured'] + const onMounted: typeof import('vue')['onMounted'] + const onRenderTracked: typeof import('vue')['onRenderTracked'] + const onRenderTriggered: typeof import('vue')['onRenderTriggered'] + const onScopeDispose: typeof import('vue')['onScopeDispose'] + const onServerPrefetch: typeof import('vue')['onServerPrefetch'] + const onUnmounted: typeof import('vue')['onUnmounted'] + const onUpdated: typeof import('vue')['onUpdated'] + const provide: typeof import('vue')['provide'] + const reactive: typeof import('vue')['reactive'] + const readonly: typeof import('vue')['readonly'] + const ref: typeof import('vue')['ref'] + const resolveComponent: typeof import('vue')['resolveComponent'] + const shallowReactive: typeof import('vue')['shallowReactive'] + const shallowReadonly: typeof import('vue')['shallowReadonly'] + const shallowRef: typeof import('vue')['shallowRef'] + const toRaw: typeof import('vue')['toRaw'] + const toRef: typeof import('vue')['toRef'] + const toRefs: typeof import('vue')['toRefs'] + const toValue: typeof import('vue')['toValue'] + const triggerRef: typeof import('vue')['triggerRef'] + const unref: typeof import('vue')['unref'] + const useAttrs: typeof import('vue')['useAttrs'] + const useCssModule: typeof import('vue')['useCssModule'] + const useCssVars: typeof import('vue')['useCssVars'] + const useSlots: typeof import('vue')['useSlots'] + const watch: typeof import('vue')['watch'] + const watchEffect: typeof import('vue')['watchEffect'] + const watchPostEffect: typeof import('vue')['watchPostEffect'] + const watchSyncEffect: typeof import('vue')['watchSyncEffect'] +} +// for type re-export +declare global { + // @ts-ignore + export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue' + import('vue') +} diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/tailwind.config.js b/StreamDock-Plugin-SDK/SDVueSDK/vue/tailwind.config.js new file mode 100644 index 000000000..791757f52 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/tailwind.config.js @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ["./src/**/*.{html,js,ts,vue}"], + theme: { + extend: {}, + }, + plugins: [], +} \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/tsconfig.json b/StreamDock-Plugin-SDK/SDVueSDK/vue/tsconfig.json new file mode 100644 index 000000000..0150d6106 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/tsconfig.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6cc3f6385e7dbe60a06c3bde332cb12e80742359ae0ea3378231d29e7542ca35 +size 323 diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/tsconfig.node.json b/StreamDock-Plugin-SDK/SDVueSDK/vue/tsconfig.node.json new file mode 100644 index 000000000..5bd231357 --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/tsconfig.node.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b8f797460d78ad2ce71d4372c02a0af2b6b552c28d57c40aa38eaa60090d3fa +size 213 diff --git a/StreamDock-Plugin-SDK/SDVueSDK/vue/vite.config.ts b/StreamDock-Plugin-SDK/SDVueSDK/vue/vite.config.ts new file mode 100644 index 000000000..945a052cf --- /dev/null +++ b/StreamDock-Plugin-SDK/SDVueSDK/vue/vite.config.ts @@ -0,0 +1,21 @@ +import vue from '@vitejs/plugin-vue'; +import AutoImport from 'unplugin-auto-import/vite'; +import { defineConfig } from 'vite'; +import { fileURLToPath, URL } from 'node:url'; +import { viteSingleFile } from 'vite-plugin-singlefile'; + +export default defineConfig({ + plugins: [ + vue(), + AutoImport({ + imports: ['vue'], + dts: './src/types/auto-imports.d.ts' + }), + viteSingleFile() + ], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)) + } + } +}); diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/.gitignore b/StreamDock-Plugin-SDK/StreamDockCPPSDK/.gitignore new file mode 100644 index 000000000..bf7038541 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/.gitignore @@ -0,0 +1,60 @@ +# C++ objects and libs +#*.slo +#*.lo +#*.o +#*.a +#*.la +#*.lai +#*.so +#*.so.* +#*.dll +#*.dylib + +# Qt-es +object_script.*.Release +object_script.*.Debug +*_plugin_import.cpp +/.qmake.cache +/.qmake.stash +*.pro.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +moc_*.cpp +moc_*.h +qrc_*.cpp +ui_*.h +*.qmlc +*.jsc +Makefile* +*build-* +#*.qm +*.prl + +# Qt unit tests +target_wrapper.* + +# QtCreator +*.autosave + +# QtCreator Qml +*.qmlproject.user +*.qmlproject.user.* + +# QtCreator CMake +CMakeLists.txt.user* + +# QtCreator 4.8< compilation database +compile_commands.json + +# QtCreator local machine specific files for imported projects +*creator.user* + +#macOS +.DS_Store + +# Visual Studio Code +build/ +.vscode +*.swp \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/CMakeLists.txt b/StreamDock-Plugin-SDK/StreamDockCPPSDK/CMakeLists.txt new file mode 100644 index 000000000..412264966 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/CMakeLists.txt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:decab242b428a8de8cc9f865d628687448f3cdcb2af33cddc3728489ce6e76bc +size 862 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/HSDExampleAction.cpp b/StreamDock-Plugin-SDK/StreamDockCPPSDK/HSDExampleAction.cpp new file mode 100644 index 000000000..fadc3ff9c --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/HSDExampleAction.cpp @@ -0,0 +1,39 @@ +#include "HSDExampleAction.h" + +#include "StreamDockCPPSDK/StreamDockSDK/NlohmannJSONUtils.h" +#include "StreamDockCPPSDK/StreamDockSDK/HSDLogger.h" + +void HSDExampleAction::DidReceiveSettings(const nlohmann::json& payload) { + HSDLogger::LogMessage("DidReceiveSettings"); +} + +void HSDExampleAction::KeyDown(const nlohmann::json& payload) { + HSDLogger::LogMessage("KeyDown"); + if (NlohmannJSONUtils::GetIntByName(payload, "state") == 0) { + SetState(1); + } + else { + SetState(0); + } +} + +void HSDExampleAction::KeyUp(const nlohmann::json& payload) { + // Log in release and debug builds + HSDLogger::LogMessage("KeyUp"); + ShowOK(); + // Only log in debug builds (C++20-style format strings): + nlohmann::json settings = payload["settings"]; + HSDLogger::LogMessage("Settings: " + settings.dump()); +} + +void HSDExampleAction::WillAppear(const nlohmann::json& payload) { + HSDLogger::LogMessage("WillAppear"); +} + +void HSDExampleAction::WillDisAppear(const nlohmann::json& payload) { + HSDLogger::LogMessage("WillAppear"); +} + +void HSDExampleAction::SendToPlugin(const nlohmann::json& payload) { + HSDLogger::LogMessage("Received message from property inspector: " + payload.dump()); +} \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/HSDExampleAction.h b/StreamDock-Plugin-SDK/StreamDockCPPSDK/HSDExampleAction.h new file mode 100644 index 000000000..2215f2bd1 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/HSDExampleAction.h @@ -0,0 +1,16 @@ +#pragma once + +#include "StreamDockSDK/HSDAction.h" + +class HSDExampleAction : public HSDAction +{ + using HSDAction::HSDAction; + + virtual void DidReceiveSettings(const nlohmann::json& payload); + virtual void KeyDown(const nlohmann::json& payload); + virtual void KeyUp(const nlohmann::json& payload); + virtual void SendToPlugin(const nlohmann::json& payload); + virtual void WillAppear(const nlohmann::json& payload); + virtual void WillDisAppear(const nlohmann::json& payload); +}; + diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/HSDExamplePlugin.cpp b/StreamDock-Plugin-SDK/StreamDockCPPSDK/HSDExamplePlugin.cpp new file mode 100644 index 000000000..2ae2244d5 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/HSDExamplePlugin.cpp @@ -0,0 +1,40 @@ +#include "HSDExamplePlugin.h" + +#include "StreamDockCPPSDK/StreamDockSDK/NlohmannJSONUtils.h" +#include "StreamDockCPPSDK/StreamDockSDK/HSDLogger.h" + +std::shared_ptr HSDExamplePlugin::GetOrCreateAction(const std::string& action, const std::string& context) +{ + auto it = mActions.find(context); + if (it != mActions.end()) { + return it->second; + } + + if (action == "com.hotspot.streamdockcppsdk.demo.myaction1" || + action == "com.hotspot.streamdockcppsdk.demo.myaction2") { + auto impl = std::make_shared( + mConnectionManager, + action, + context + ); + mActions.emplace(context, impl); + return impl; + } + + HSDLogger::LogMessage("Asked to get or create unknown action: " + action); + return nullptr; +} + +void HSDExamplePlugin::DidReceiveGlobalSettings(const nlohmann::json& payload) +{ + HSDLogger::LogMessage("Received global settings"); + HSDLogger::LogMessage("Global settings: " + payload.dump()); + + // Do plugin-wide stuff here, e.g. reconnect to application being + // controlled + + for (const auto& action : mActions) { + // ... then pass it on to each action, e.g.: + // action->setApplicationConnection(myConnectionObject); + } +} diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/HSDExamplePlugin.h b/StreamDock-Plugin-SDK/StreamDockCPPSDK/HSDExamplePlugin.h new file mode 100644 index 000000000..410880e78 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/HSDExamplePlugin.h @@ -0,0 +1,24 @@ +#pragma once + +#include "StreamDockCPPSDK/StreamDockSDK/HSDPlugin.h" +#include "HSDExampleAction.h" +#include +#include + + +class HSDExamplePlugin : public HSDPlugin +{ +public: + using HSDPlugin::HSDPlugin; + + virtual std::shared_ptr GetOrCreateAction(const std::string& action, const std::string& context) override; + + // Overriding from ESDBasePlugin + virtual void DidReceiveGlobalSettings(const nlohmann::json& payload) override; + +private: + std::mutex mVisibleContextsMutex; + std::set mVisibleContexts; + std::map> mActions; +}; + diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/LICENSE b/StreamDock-Plugin-SDK/StreamDockCPPSDK/LICENSE new file mode 100644 index 000000000..02f6c4272 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/LICENSE @@ -0,0 +1,9 @@ +The MIT License + +Copyright 2018 Corsair Memory, Inc + +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. \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/README.md b/StreamDock-Plugin-SDK/StreamDockCPPSDK/README.md new file mode 100644 index 000000000..becd5b9e5 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/README.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:84a7518df9adfd27496758cea26bdaf8e44a8d5fe088e2a8e2de4c304ccbb867 +size 445 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/CMakeLists.txt b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/CMakeLists.txt new file mode 100644 index 000000000..0f0e1c555 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/CMakeLists.txt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1bc4fca70debfe4339bf4ef5f392c75afcd2d3996fc926c41a4d5e811292cf1e +size 771 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDAction.cpp b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDAction.cpp new file mode 100644 index 000000000..001d66d52 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDAction.cpp @@ -0,0 +1,80 @@ +// @copyright (c) 2022, ljj +// This source code is licensed under the MIT-style license found in the LICENSE file. + +#include "HSDAction.h" + +#include "HSDConnectionManager.h" + +HSDAction::HSDAction(HSDConnectionManager* hsd_connection, const std::string& action, const std::string& context) + : mHSDConnection(hsd_connection), mAction(action), mContext(context) { +} + +HSDAction::~HSDAction() { +} + +std::string HSDAction::GetContext() const { + return mContext; +} + +std::string HSDAction::GetAction() const { + return mAction; +} + +void HSDAction::DidReceiveSettings(const nlohmann::json& payload) { +} + +void HSDAction::KeyDown(const nlohmann::json& payload) { +} + +void HSDAction::KeyUp(const nlohmann::json& payload) { +} + +void HSDAction::DialUp(const nlohmann::json& payload) { +} + +void HSDAction::DialDown(const nlohmann::json& payload) { +} + +void HSDAction::RotateClockwise(const nlohmann::json& payload, const unsigned int ticks, const bool pressed) { +} + +void HSDAction::RotateCounterClockwise(const nlohmann::json& payload, const unsigned int ticks, const bool pressed) { +} + +void HSDAction::SendToPlugin(const nlohmann::json& payload) { +} + +void HSDAction::WillAppear(const nlohmann::json& payload) { +} + +HSDConnectionManager* HSDAction::GetHSDConnection() const { + return mHSDConnection; +} + +void HSDAction::SetState(int state) { + GetHSDConnection()->SetState(state, mContext); +} + +void HSDAction::SetTitle(const std::string& title, ESDSDKTarget target, int state) { + GetHSDConnection()->SetTitle(title, mContext, target, state); +} + +void HSDAction::SetImage(const std::string& inBase64ImageString, ESDSDKTarget target, int state) { + GetHSDConnection()->SetImage(inBase64ImageString, mContext, target, state); +} + +void HSDAction::ShowAlert() { + GetHSDConnection()->ShowAlertForContext(mContext); +} + +void HSDAction::ShowOK() { + GetHSDConnection()->ShowOKForContext(mContext); +} + +void HSDAction::SetSettings(const nlohmann::json& inSettings) { + GetHSDConnection()->SetSettings(inSettings, mContext); +} + +void HSDAction::SendToPropertyInspector(const nlohmann::json& payload) { + GetHSDConnection()->SendToPropertyInspector(mAction, mContext, payload); +} diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDAction.h b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDAction.h new file mode 100644 index 000000000..003f47acc --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDAction.h @@ -0,0 +1,64 @@ +// @copyright (c) 2022, ljj +// This source code is licensed under the MIT-style license found in the LICENSE file. + +#pragma once + +#include "HSDSDKDefines.h" + +#include +#include + +class HSDConnectionManager; + +/** Class representing a specific action (kind of button). + * + * A plugin can provide multiple actions, e.g.: + * - 'mute on' + * - 'mute off' + * - 'toggle mute' + * + * Each of these can be represented by an `HSDAction` subclass. + * + * This class is intended to be used in conjunction with an `HSDPlugin` + * subclass; your `HSDPlugin` subclass should contain minimum logic beyond + * storing and instantiating `HSDAction` subclass instances as needed. + * + * If your action reflects state outside of the plugin (e.g. hardware state, + * the current time, state in another application such as OBS), you may want + * to use `HSDActionWithExternalState`. + */ +class HSDAction { + public: + HSDAction(HSDConnectionManager* hsd_connection, const std::string& action, const std::string& context); + virtual ~HSDAction(); + + std::string GetAction() const; + std::string GetContext() const; + + virtual void DidReceiveSettings(const nlohmann::json& payload); + virtual void KeyDown(const nlohmann::json& payload); + virtual void KeyUp(const nlohmann::json& payload); + virtual void DialUp(const nlohmann::json& payload); + virtual void DialDown(const nlohmann::json& payload); + virtual void SendToPlugin(const nlohmann::json& payload); + virtual void WillAppear(const nlohmann::json& payload); + virtual void RotateClockwise(const nlohmann::json& payload, const unsigned int ticks, const bool pressed); + virtual void RotateCounterClockwise(const nlohmann::json& payload, const unsigned int ticks, const bool pressed); + + protected: + HSDConnectionManager* GetHSDConnection() const; + + // Convenience wrappers for GetHSDConnection()->foo() + void SetState(int state); + void SetTitle(const std::string& title, ESDSDKTarget = kESDSDKTarget_HardwareAndSoftware, int state = -1); + void SetImage(const std::string& inBase64ImageString, ESDSDKTarget = kESDSDKTarget_HardwareAndSoftware, int state = -1); + void ShowAlert(); + void ShowOK(); + void SetSettings(const nlohmann::json& inSettings); + void SendToPropertyInspector(const nlohmann::json& payload); + + private: + std::string mAction; + std::string mContext; + HSDConnectionManager* mHSDConnection = nullptr; +}; diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDActionWithExternalState.h b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDActionWithExternalState.h new file mode 100644 index 000000000..94c32af30 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDActionWithExternalState.h @@ -0,0 +1,82 @@ +/* Copyright (c) 2020-present, Fred Emmott + * + * This source code is licensed under the MIT-style license found in the + * LICENSE file. + */ + +#pragma once + +#include "HSDAction.h" + +/** An `HSDAction` where state can be changed by something other than the + * StreamDock software. + * + * For example: + * - hardware events + * - time + * - changes in other applications that the plugin interacts with (e.g. OBS) + * + * In these cases, you likely want to consider settings to be persistent, and + * 'settings changed' to be an event in itself. This class removes the settings + * parameter (which may be changed or unchanged) from most events, and adds a + * new event for when they've changed. + */ +template +class HSDActionWithExternalState : public HSDAction { + protected: + virtual void SettingsDidChange( + const TSettings& old_settings, + const TSettings& new_settings) = 0; + + virtual void WillAppear() { + } + + virtual void KeyDown() { + } + + virtual void KeyUp() { + } + + const TSettings& GetSettings() const { + return mSettings; + } + + public: + HSDActionWithExternalState( + HSDConnectionManager* hsd_connection, + const std::string& action, + const std::string& context) + : HSDAction(hsd_connection, action, context) { + } + + virtual ~HSDActionWithExternalState() { + } + + virtual void DidReceiveSettings(const nlohmann::json& json_settings) final { + TSettings new_settings(json_settings); + if (new_settings == mSettings) { + return; + } + const auto old_settings = std::move(mSettings); + mSettings = std::move(new_settings); + SettingsDidChange(old_settings, mSettings); + } + + virtual void WillAppear(const nlohmann::json& settings) final { + DidReceiveSettings(settings); + WillAppear(); + } + + virtual void KeyUp(const nlohmann::json& settings) final { + DidReceiveSettings(settings); + KeyUp(); + } + + virtual void KeyDown(const nlohmann::json& settings) final { + DidReceiveSettings(settings); + KeyDown(); + } + + private: + TSettings mSettings; +}; diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDBasePlugin.h b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDBasePlugin.h new file mode 100644 index 000000000..6f55c86b9 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDBasePlugin.h @@ -0,0 +1,110 @@ +//============================================================================== +/** +@file ESDBasePlugin.h + +@brief Plugin base class + +@copyright (c) 2018, Corsair Memory, Inc. + This source code is licensed under the MIT-style license found in the +LICENSE file. + +**/ +//============================================================================== + +#pragma once + +#include + +class HSDConnectionManager; + +class HSDBasePlugin { + public: + HSDBasePlugin() { + } + virtual ~HSDBasePlugin() { + } + + void SetConnectionManager(HSDConnectionManager* inConnectionManager) { + mConnectionManager = inConnectionManager; + } + + virtual void DidReceiveGlobalSettings(const nlohmann::json& inPayload) { + } + + virtual void DidReceiveSettings( + const std::string& inAction, + const std::string& inContext, + const nlohmann::json& inPayload, + const std::string& inDeviceID) { + } + + virtual void KeyDownForAction( + const std::string& inAction, + const std::string& inContext, + const nlohmann::json& inPayload, + const std::string& inDeviceID) { + } + + virtual void KeyUpForAction( + const std::string& inAction, + const std::string& inContext, + const nlohmann::json& inPayload, + const std::string& inDeviceID) { + } + + virtual void DialDownForAction( + const std::string& inAction, + const std::string& inContext, + const nlohmann::json& inPayload, + const std::string& inDeviceID) { + } + + virtual void DialUpForAction( + const std::string& inAction, + const std::string& inContext, + const nlohmann::json& inPayload, + const std::string& inDeviceID) { + } + + virtual void DialRotateForAction( + const std::string& inAction, + const std::string& inContext, + const nlohmann::json& inPayload, + const std::string& inDeviceID) { + } + + virtual void WillAppearForAction( + const std::string& inAction, + const std::string& inContext, + const nlohmann::json& inPayload, + const std::string& inDeviceID) { + } + + virtual void WillDisappearForAction( + const std::string& inAction, + const std::string& inContext, + const nlohmann::json& inPayload, + const std::string& inDeviceID) { + } + + virtual void DeviceDidConnect( + const std::string& inDeviceID, + const nlohmann::json& inDeviceInfo) { + } + + virtual void DeviceDidDisconnect(const std::string& inDeviceID) { + } + + virtual void SystemDidWakeUp() { + } + + virtual void SendToPlugin( + const std::string& inAction, + const std::string& inContext, + const nlohmann::json& inPayload, + const std::string& inDeviceID) { + } + + protected: + HSDConnectionManager* mConnectionManager = nullptr; +}; diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDConnectionManager.cpp b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDConnectionManager.cpp new file mode 100644 index 000000000..19dd13792 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDConnectionManager.cpp @@ -0,0 +1,358 @@ +//============================================================================== +/** +@file HSDConnectionManager.cpp + +@brief Wrapper to implement the communication with the Stream Dock application +**/ +//============================================================================== + +#include "HSDConnectionManager.h" + +#include "NlohmannJSONUtils.h" +#include "HSDLogger.h" + +void HSDConnectionManager::OnOpen(WebsocketClient* inClient, websocketpp::connection_hdl inConnectionHandler) { + HSDLogger::LogMessage("OnOpen"); + + // Register plugin with StreamDock + json jsonObject; + jsonObject["event"] = mRegisterEvent; + jsonObject["uuid"] = mPluginUUID; + + websocketpp::lib::error_code ec; + mWebsocket.send(mConnectionHandle, jsonObject.dump(), websocketpp::frame::opcode::text, ec); +} + +void HSDConnectionManager::OnFail(WebsocketClient* inClient, websocketpp::connection_hdl inConnectionHandler) { + std::string reason; + + if (inClient) { + WebsocketClient::connection_ptr connection = inClient->get_con_from_hdl(inConnectionHandler); + if (connection) { + reason = connection->get_ec().message(); + } + } + + HSDLogger::LogMessage("Failed with reason: {}" + reason); + + if (mAsioContext) { + HSDLogger::LogMessage("Stopping ASIO context"); + mAsioContext->stop(); + } +} + +void HSDConnectionManager::OnClose(WebsocketClient* inClient, websocketpp::connection_hdl inConnectionHandler) { + std::string reason; + + if (inClient) { + WebsocketClient::connection_ptr connection = inClient->get_con_from_hdl(inConnectionHandler); + if (connection) { + reason = connection->get_remote_close_reason(); + } + } + + HSDLogger::LogMessage("Close with reason: " + reason); + + if (mAsioContext) { + HSDLogger::LogMessage("Stopping ASIO context"); + mAsioContext->stop(); + } +} + +void HSDConnectionManager::OnMessage(websocketpp::connection_hdl, WebsocketClient::message_ptr inMsg) { + if (inMsg && inMsg->get_opcode() == websocketpp::frame::opcode::text) { + std::string message = inMsg->get_payload(); + HSDLogger::LogMessage("OnMessage: " + message); + + try { + json receivedJson = json::parse(message); + + std::string event = NlohmannJSONUtils::GetStringByName(receivedJson, kESDSDKCommonEvent); + std::string context = NlohmannJSONUtils::GetStringByName(receivedJson, kESDSDKCommonContext); + std::string action = NlohmannJSONUtils::GetStringByName(receivedJson, kESDSDKCommonAction); + std::string deviceID = NlohmannJSONUtils::GetStringByName(receivedJson, kESDSDKCommonDevice); + json payload; + NlohmannJSONUtils::GetObjectByName(receivedJson, kESDSDKCommonPayload, payload); + + if (event == kESDSDKEventKeyDown) { + mPlugin->KeyDownForAction(action, context, payload, deviceID); + } + else if (event == kESDSDKEventKeyUp) { + mPlugin->KeyUpForAction(action, context, payload, deviceID); + } + else if (event == kESDSDKEventWillAppear) { + mPlugin->WillAppearForAction(action, context, payload, deviceID); + } + else if (event == kESDSDKEventWillDisappear) { + mPlugin->WillDisappearForAction(action, context, payload, deviceID); + } + else if (event == kESDSDKEventDidReceiveSettings) { + mPlugin->DidReceiveSettings(action, context, payload, deviceID); + } + else if (event == kESDSDKEventDidReceiveGlobalSettings) { + mPlugin->DidReceiveGlobalSettings(payload); + } + else if (event == kESDSDKEventDeviceDidConnect) { + json deviceInfo; + NlohmannJSONUtils::GetObjectByName(receivedJson, kESDSDKCommonDeviceInfo, deviceInfo); + mPlugin->DeviceDidConnect(deviceID, deviceInfo); + } + else if (event == kESDSDKEventDeviceDidDisconnect) { + mPlugin->DeviceDidDisconnect(deviceID); + } + else if (event == kESDSDKEventSendToPlugin) { + mPlugin->SendToPlugin(action, context, payload, deviceID); + } + else if (event == kESDSDKEventSystemDidWakeUp) { + mPlugin->SystemDidWakeUp(); + } + else if (event == kESDSDKEventDialDown) { + mPlugin->DialDownForAction(action, context, payload, deviceID); + } + else if (event == kESDSDKEventDialUp) { + mPlugin->DialUpForAction(action, context, payload, deviceID); + } + else if (event == kESDSDKEventDialRotate) { + mPlugin->DialRotateForAction(action, context, payload, deviceID); + } + } + catch (...) { + } + } +} + +HSDConnectionManager::HSDConnectionManager( + int inPort, + const std::string& inPluginUUID, + const std::string& inRegisterEvent, + const std::string& inInfo, + HSDBasePlugin* inPlugin) + : mPort(inPort), + mPluginUUID(inPluginUUID), + mRegisterEvent(inRegisterEvent), + mPlugin(inPlugin) { + if (inPlugin) { + inPlugin->SetConnectionManager(this); + } +} + +void HSDConnectionManager::Run() { + try { + // Create the endpoint + mWebsocket.clear_access_channels(websocketpp::log::alevel::all); + mWebsocket.clear_error_channels(websocketpp::log::elevel::all); + + // Initialize ASIO + auto ctx = std::make_shared(); + mWebsocket.init_asio(ctx.get()); + mAsioContext = ctx; + + // Register our message handler + mWebsocket.set_open_handler(websocketpp::lib::bind( + &HSDConnectionManager::OnOpen, this, + &mWebsocket, websocketpp::lib::placeholders::_1)); + mWebsocket.set_fail_handler(websocketpp::lib::bind( + &HSDConnectionManager::OnFail, this, + &mWebsocket, websocketpp::lib::placeholders::_1)); + mWebsocket.set_close_handler(websocketpp::lib::bind( + &HSDConnectionManager::OnClose, this, + &mWebsocket, websocketpp::lib::placeholders::_1)); + mWebsocket.set_message_handler(websocketpp::lib::bind( + &HSDConnectionManager::OnMessage, this, + websocketpp::lib::placeholders::_1, websocketpp::lib::placeholders::_2)); + + // Get connection handle + websocketpp::lib::error_code ec; + std::string uri = "ws://127.0.0.1:" + std::to_string(mPort); + WebsocketClient::connection_ptr connection = mWebsocket.get_connection(uri, ec); + if (ec) { + HSDLogger::LogMessage("Connect initialization error: " + ec.message()); + return; + } + mConnectionHandle = connection->get_handle(); + + // Note that connect here only requests a connection. No network messages + // are exchanged until the event loop starts running in the next line. + mWebsocket.connect(connection); + + // Start the ASIO io_service run loop + // this will cause a single connection to be made to the server. + // mWebsocket.run() will exit when this connection is closed. + mWebsocket.run(); + } + catch (websocketpp::exception const& e) { + // Prevent an unused variable warning in release builds + (void)e; + std::string whatString = e.what(); + HSDLogger::LogMessage("Websocket threw an exception: " + whatString); + } +} + +void HSDConnectionManager::SetTitle( + const std::string& inTitle, + const std::string& inContext, + ESDSDKTarget inTarget, + int inState) { + json jsonObject; + + jsonObject[kESDSDKCommonEvent] = kESDSDKEventSetTitle; + jsonObject[kESDSDKCommonContext] = inContext; + + json payload; + payload[kESDSDKPayloadTarget] = inTarget; + payload[kESDSDKPayloadTitle] = inTitle; + if (inState >= 0) { + payload[kESDSDKPayloadState] = inState; + } + jsonObject[kESDSDKCommonPayload] = payload; + + websocketpp::lib::error_code ec; + mWebsocket.send(mConnectionHandle, jsonObject.dump(), websocketpp::frame::opcode::text, ec); +} + +void HSDConnectionManager::SetImage( + const std::string& inBase64ImageString, + const std::string& inContext, + ESDSDKTarget inTarget, + int inState) { + json jsonObject; + + jsonObject[kESDSDKCommonEvent] = kESDSDKEventSetImage; + jsonObject[kESDSDKCommonContext] = inContext; + + json payload; + payload[kESDSDKPayloadTarget] = inTarget; + const std::string prefix = "data:image/png;base64,"; + if (inBase64ImageString.empty() || + inBase64ImageString.substr(0, prefix.length()).find(prefix) == 0) { + payload[kESDSDKPayloadImage] = inBase64ImageString; + } + else { + payload[kESDSDKPayloadImage] = "data:image/png;base64," + inBase64ImageString; + } + if (inState >= 0) { + payload[kESDSDKPayloadState] = inState; + } + jsonObject[kESDSDKCommonPayload] = payload; + + websocketpp::lib::error_code ec; + mWebsocket.send(mConnectionHandle, jsonObject.dump(), websocketpp::frame::opcode::text, ec); +} + +void HSDConnectionManager::ShowAlertForContext(const std::string& inContext) { + json jsonObject; + + jsonObject[kESDSDKCommonEvent] = kESDSDKEventShowAlert; + jsonObject[kESDSDKCommonContext] = inContext; + + websocketpp::lib::error_code ec; + mWebsocket.send(mConnectionHandle, jsonObject.dump(), websocketpp::frame::opcode::text, ec); +} + +void HSDConnectionManager::ShowOKForContext(const std::string& inContext) { + json jsonObject; + + jsonObject[kESDSDKCommonEvent] = kESDSDKEventShowOK; + jsonObject[kESDSDKCommonContext] = inContext; + + websocketpp::lib::error_code ec; + mWebsocket.send(mConnectionHandle, jsonObject.dump(), websocketpp::frame::opcode::text, ec); +} + +void HSDConnectionManager::SetSettings( + const json& inSettings, + const std::string& inContext) { + json jsonObject; + + jsonObject[kESDSDKCommonEvent] = kESDSDKEventSetSettings; + jsonObject[kESDSDKCommonContext] = inContext; + jsonObject[kESDSDKCommonPayload] = inSettings; + + websocketpp::lib::error_code ec; + mWebsocket.send(mConnectionHandle, jsonObject.dump(), websocketpp::frame::opcode::text, ec); +} + +void HSDConnectionManager::SetState(int inState, const std::string& inContext) { + json jsonObject; + + json payload; + payload[kESDSDKPayloadState] = inState; + + jsonObject[kESDSDKCommonEvent] = kESDSDKEventSetState; + jsonObject[kESDSDKCommonContext] = inContext; + jsonObject[kESDSDKCommonPayload] = payload; + + websocketpp::lib::error_code ec; + mWebsocket.send(mConnectionHandle, jsonObject.dump(), websocketpp::frame::opcode::text, ec); +} + +void HSDConnectionManager::SendToPropertyInspector( + const std::string& inAction, + const std::string& inContext, + const json& inPayload) { + json jsonObject; + + jsonObject[kESDSDKCommonEvent] = kESDSDKEventSendToPropertyInspector; + jsonObject[kESDSDKCommonContext] = inContext; + jsonObject[kESDSDKCommonAction] = inAction; + jsonObject[kESDSDKCommonPayload] = inPayload; + + websocketpp::lib::error_code ec; + mWebsocket.send(mConnectionHandle, jsonObject.dump(), websocketpp::frame::opcode::text, ec); +} + +void HSDConnectionManager::SwitchToProfile(const std::string& inDeviceID, const std::string& inProfileName) { + if (!inDeviceID.empty()) { + json jsonObject; + + jsonObject[kESDSDKCommonEvent] = kESDSDKEventSwitchToProfile; + jsonObject[kESDSDKCommonContext] = mPluginUUID; + jsonObject[kESDSDKCommonDevice] = inDeviceID; + + if (!inProfileName.empty()) { + json payload; + payload[kESDSDKPayloadProfile] = inProfileName; + jsonObject[kESDSDKCommonPayload] = payload; + } + + websocketpp::lib::error_code ec; + mWebsocket.send(mConnectionHandle, jsonObject.dump(), websocketpp::frame::opcode::text, ec); + } +} + +void HSDConnectionManager::LogMessage(const std::string& inMessage) { + if (!inMessage.empty()) { + json jsonObject; + + jsonObject[kESDSDKCommonEvent] = kESDSDKEventLogMessage; + + json payload; + payload[kESDSDKPayloadMessage] = inMessage; + jsonObject[kESDSDKCommonPayload] = payload; + + websocketpp::lib::error_code ec; + mWebsocket.send(mConnectionHandle, jsonObject.dump(), websocketpp::frame::opcode::text, ec); + } +} + +void HSDConnectionManager::GetGlobalSettings() { + json jsonObject{ {kESDSDKCommonEvent, kESDSDKEventGetGlobalSettings}, + {kESDSDKCommonContext, mPluginUUID} }; + websocketpp::lib::error_code ec; + mWebsocket.send(mConnectionHandle, jsonObject.dump(), websocketpp::frame::opcode::text, ec); +} + +void HSDConnectionManager::SetGlobalSettings(const json& inSettings) { + json jsonObject; + + jsonObject[kESDSDKCommonEvent] = kESDSDKEventSetGlobalSettings; + jsonObject[kESDSDKCommonContext] = mPluginUUID; + jsonObject[kESDSDKCommonPayload] = inSettings; + + websocketpp::lib::error_code ec; + mWebsocket.send(mConnectionHandle, jsonObject.dump(), websocketpp::frame::opcode::text, ec); +} + +std::shared_ptr HSDConnectionManager::GetAsioContext() const { + return mAsioContext; +} diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDConnectionManager.h b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDConnectionManager.h new file mode 100644 index 000000000..61f32f1fd --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDConnectionManager.h @@ -0,0 +1,93 @@ +//============================================================================== +/** +@file HSDConnectionManager.h + +@brief Wrapper to implement the communication with the Stream Dock +application + +@copyright (c) 2018, Corsair Memory, Inc. + This source code is licensed under the MIT-style license found in the +LICENSE file. + +**/ +//============================================================================== + +#pragma once + +#include +#include +#include +#include + +#include "HSDBasePlugin.h" +#include "HSDSDKDefines.h" + +typedef websocketpp::config::asio_client::message_type::ptr message_ptr; +typedef websocketpp::client WebsocketClient; + +class HSDConnectionManager { + public: + HSDConnectionManager( + int inPort, + const std::string& inPluginUUID, + const std::string& inRegisterEvent, + const std::string& inInfo, + HSDBasePlugin* inPlugin); + + // Start the event loop + void Run(); + + // API to communicate with the Stream Dock application + void SetTitle( + const std::string& inTitle, + const std::string& inContext, + ESDSDKTarget inTarget, + int state = -1); + void SetImage( + const std::string& inBase64ImageString, + const std::string& inContext, + ESDSDKTarget inTarget, + int state = -1); + void ShowAlertForContext(const std::string& inContext); + void ShowOKForContext(const std::string& inContext); + void SetSettings( + const nlohmann::json& inSettings, + const std::string& inContext); + void GetGlobalSettings(); + void SetGlobalSettings(const nlohmann::json& inSettings); + void SetState(int inState, const std::string& inContext); + void SendToPropertyInspector( + const std::string& inAction, + const std::string& inContext, + const nlohmann::json& inPayload); + void SwitchToProfile( + const std::string& inDeviceID, + const std::string& inProfileName); + void LogMessage(const std::string& inMessage); + + std::shared_ptr GetAsioContext() const; + + private: + // Websocket callbacks + void OnOpen( + WebsocketClient* inClient, + websocketpp::connection_hdl inConnectionHandler); + void OnFail( + WebsocketClient* inClient, + websocketpp::connection_hdl inConnectionHandler); + void OnClose( + WebsocketClient* inClient, + websocketpp::connection_hdl inConnectionHandler); + void OnMessage( + websocketpp::connection_hdl, + WebsocketClient::message_ptr inMsg); + + // Member variables + int mPort = 0; + std::string mPluginUUID; + std::string mRegisterEvent; + websocketpp::connection_hdl mConnectionHandle; + WebsocketClient mWebsocket; + HSDBasePlugin* mPlugin = nullptr; + std::shared_ptr mAsioContext; +}; diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDLogger.cpp b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDLogger.cpp new file mode 100644 index 000000000..85c301e96 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDLogger.cpp @@ -0,0 +1,20 @@ +// @copyright (c) 2022, ljj +// This source code is licensed under the MIT-style license found in the LICENSE file. + +#include "HSDLogger.h" + +#include "HSDConnectionManager.h" + +namespace { +HSDConnectionManager* sConnectionManager = nullptr; +}// namespace + +void HSDLogger::SetConnectionManager(HSDConnectionManager* hsd_connection) { + sConnectionManager = hsd_connection; +} + +void HSDLogger::LogMessage(const std::string& message) { + if (sConnectionManager) { + sConnectionManager->LogMessage(message); + } +} diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDLogger.h b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDLogger.h new file mode 100644 index 000000000..504a70671 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDLogger.h @@ -0,0 +1,15 @@ +// @copyright (c) 2020, Frederick Emmott +// This source code is licensed under the MIT-style license found in the LICENSE file. +#pragma once + +#include + +class HSDConnectionManager; + +class HSDLogger final { + public: + HSDLogger() = delete; + static void SetConnectionManager(HSDConnectionManager* hsd_connection); + + static void LogMessage(const std::string& message); +}; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDMain.cpp b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDMain.cpp new file mode 100644 index 000000000..d5eceea8e --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDMain.cpp @@ -0,0 +1,78 @@ +//============================================================================== +/** +@file main.cpp + +@brief Parse arguments and start the plugin +**/ +//============================================================================== + +#include "NlohmannJSONUtils.h" +#include "HSDConnectionManager.h" +#include "HSDLogger.h" + +int esd_main(int argc, const char** argv, HSDBasePlugin* plugin) { + if (argc != 9) { + HSDLogger::LogMessage("Invalid number of parameters"); + return 1; + } + + int port = 0; + std::string pluginUUID; + std::string registerEvent; + std::string info; + + for (int argumentIndex = 0; argumentIndex < 4; argumentIndex++) { + std::string parameter(argv[1 + 2 * argumentIndex]); + std::string value(argv[1 + 2 * argumentIndex + 1]); + + if (parameter == kESDSDKPortParameter) { + port = std::atoi(value.c_str()); + } else if (parameter == kESDSDKPluginUUIDParameter) { + pluginUUID = value; + } else if (parameter == kESDSDKRegisterEventParameter) { + registerEvent = value; + } else if (parameter == kESDSDKInfoParameter) { + info = value; + } + } + + if (port == 0) { + HSDLogger::LogMessage("Invalid port number"); + return 1; + } + + if (pluginUUID.empty()) { + HSDLogger::LogMessage("Invalid plugin UUID"); + return 1; + } + + if (registerEvent.empty()) { + HSDLogger::LogMessage("Invalid registerEvent"); + return 1; + } + + if (info.empty()) { + HSDLogger::LogMessage("Invalid info"); + return 1; + } + + // Initialize localization helper + std::string language = "en"; + try { + json infoJson = json::parse(info); + json applicationInfo; + if (NlohmannJSONUtils::GetObjectByName(infoJson, kESDSDKApplicationInfo, applicationInfo)) { + language = NlohmannJSONUtils::GetStringByName(applicationInfo, kESDSDKApplicationInfoLanguage, language); + } + } catch (...) { + } + + // Create the connection manager + HSDConnectionManager* connectionManager = new HSDConnectionManager(port, pluginUUID, registerEvent, info, plugin); + HSDLogger::SetConnectionManager(connectionManager); + + // Connect and start the event loop + connectionManager->Run(); + + return 0; +} diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDMain.h b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDMain.h new file mode 100644 index 000000000..01d6f38c0 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDMain.h @@ -0,0 +1,17 @@ +//============================================================================== +/** +@file ESDMain.h + +@brief Parse arguments and start the plugin + +@copyright (c) 2018, Corsair Memory, Inc. + This source code is licensed under the MIT-style license found in the +LICENSE file. + +**/ + +#pragma once + +class HSDBasePlugin; + +int esd_main(int argc, const char** argv, HSDBasePlugin* plugin); diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDPlugin.cpp b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDPlugin.cpp new file mode 100644 index 000000000..915dfc84d --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDPlugin.cpp @@ -0,0 +1,167 @@ +// @copyright (c) 2022, ljj +// This source code is licensed under the MIT-style license found in the LICENSE file. + +#include "HSDPlugin.h" + +#include "NlohmannJSONUtils.h" +#include "HSDConnectionManager.h" +#include "HSDLogger.h" + +#include +#include + +#include "HSDAction.h" + +using json = nlohmann::json; + +HSDPlugin::HSDPlugin() : HSDBasePlugin() +{ +} + +HSDPlugin::~HSDPlugin() +{ +} + +void HSDPlugin::KeyDownForAction( + const std::string &inAction, + const std::string &inContext, + const json &inPayload, + const std::string &inDeviceID) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + HSDLogger::LogMessage("No action for keydown - " + inAction + " " + inContext); + return; + } + action->KeyDown(inPayload); +} + +void HSDPlugin::KeyUpForAction( + const std::string &inAction, + const std::string &inContext, + const json &inPayload, + const std::string &inDeviceID) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + HSDLogger::LogMessage("No action for keyup - " + inAction + " " + inContext); + return; + } + action->KeyUp(inPayload); +} + +void HSDPlugin::DialDownForAction( + const std::string &inAction, + const std::string &inContext, + const json &inPayload, + const std::string &inDeviceID) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + HSDLogger::LogMessage("No action for dialPress - " + inAction + " " + inContext); + return; + } + action->DialDown(inPayload); +} + +void HSDPlugin::DialUpForAction( + const std::string &inAction, + const std::string &inContext, + const json &inPayload, + const std::string &inDeviceID) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + HSDLogger::LogMessage("No action for dialPress - " + inAction + " " + inContext); + return; + } + action->DialUp(inPayload); +} + +void HSDPlugin::DialRotateForAction( + const std::string &inAction, + const std::string &inContext, + const json &inPayload, + const std::string &inDeviceID) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + HSDLogger::LogMessage("No action for dialRotate - " + inAction + " " + inContext); + return; + } + int ticks(inPayload["ticks"]); + bool pressed(inPayload["pressed"]); + if (inPayload["ticks"] < 0) + { + action->RotateCounterClockwise(inPayload, static_cast(-ticks), pressed); + } + else + { + action->RotateClockwise(inPayload, static_cast(ticks), pressed); + } +} + +void HSDPlugin::WillAppearForAction( + const std::string &inAction, + const std::string &inContext, + const json &inPayload, + const std::string &inDeviceID) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + HSDLogger::LogMessage("No action for WillAppear - " + inAction + " " + inContext); + return; + } + action->WillAppear(inPayload); +} + +void HSDPlugin::WillDisappearForAction( + const std::string& inAction, + const std::string& inContext, + const json& inPayload, + const std::string& inDeviceID) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + HSDLogger::LogMessage("No action for WillDisAppear - " + inAction + " " + inContext); + return; + } + action->WillDisAppear(inPayload); +} + +void HSDPlugin::SendToPlugin( + const std::string &inAction, + const std::string &inContext, + const json &inPayload, + const std::string &inDevice) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + HSDLogger::LogMessage("Received plugin request for unknown action " + inAction + " " + inContext); + return; + } + action->SendToPlugin(inPayload); +} + +void HSDPlugin::DidReceiveSettings( + const std::string &inAction, + const std::string &inContext, + const json &inPayload, + const std::string &inDevice) +{ + auto action = GetOrCreateAction(inAction, inContext); + if (!action) + { + HSDLogger::LogMessage("No action for DidReceiveSettings: " + inAction + " " + inContext); + return; + } + action->DidReceiveSettings(inPayload); +} diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDPlugin.h b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDPlugin.h new file mode 100644 index 000000000..658143b45 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDPlugin.h @@ -0,0 +1,88 @@ +/* Copyright (c) 2018, Corsair Memory, Inc. + * Copyright (c) 2020-present, Fred Emmott + * + * This source code is licensed under the MIT-style license found in the + * LICENSE file. + */ + +#include "HSDBasePlugin.h" + +#include + +class HSDAction; + +/** Coordinator class for `HSDAction`-based plugins. + * + * Plugins should: + * - create one or more subclasses of `HSDAction`, one for each action type. + * - create exactly one subclass of `HSDPlugin`, which implements + * `GetOrCreateAction()` to retrieve instances of an `HSDAction`. + */ +class HSDPlugin : public HSDBasePlugin +{ +protected: + /** Create or retrieve an HSDAction instance for the given action/context. + * + * Return a null/empty shared_ptr if the action is unrecognized. + */ + virtual std::shared_ptr + GetOrCreateAction(const std::string &action, const std::string &context) = 0; + +public: + HSDPlugin(); + virtual ~HSDPlugin(); + + virtual void KeyDownForAction( + const std::string &inAction, + const std::string &inContext, + const nlohmann::json &inPayload, + const std::string &inDeviceID) override; + + virtual void KeyUpForAction( + const std::string &inAction, + const std::string &inContext, + const nlohmann::json &inPayload, + const std::string &inDeviceID) override; + + virtual void DialDownForAction( + const std::string &inAction, + const std::string &inContext, + const nlohmann::json &inPayload, + const std::string &inDeviceID) override; + + virtual void DialUpForAction( + const std::string &inAction, + const std::string &inContext, + const nlohmann::json &inPayload, + const std::string &inDeviceID) override; + + virtual void DialRotateForAction( + const std::string &inAction, + const std::string &inContext, + const nlohmann::json &inPayload, + const std::string &inDeviceID) override; + + virtual void WillAppearForAction( + const std::string &inAction, + const std::string &inContext, + const nlohmann::json &inPayload, + const std::string &inDeviceID) override; + + virtual void WillDisappearForAction( + const std::string& inAction, + const std::string& inContext, + const nlohmann::json& inPayload, + const std::string& inDeviceID) override; + + virtual void SendToPlugin( + const std::string &inAction, + const std::string &inContext, + const nlohmann::json &inPayload, + const std::string &inDevice) override; + + virtual void DidReceiveSettings( + const std::string &inAction, + const std::string &inContext, + const nlohmann::json &inPayload, + const std::string &inDevice) override; +}; diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDSDKDefines.h b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDSDKDefines.h new file mode 100644 index 000000000..b8a4fdbbe --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/HSDSDKDefines.h @@ -0,0 +1,170 @@ +//============================================================================== +/** +@file ESDSDKDefines.h + +@brief Defines used for the Stream Dock communication + +@copyright (c) 2018, Corsair Memory, Inc. + This source code is licensed under the MIT-style license found in the +LICENSE file. + +**/ +//============================================================================== + +#pragma once + +// +// Common base-interface +// + +#define kESDSDKCommonAction "action" +#define kESDSDKCommonEvent "event" +#define kESDSDKCommonContext "context" +#define kESDSDKCommonPayload "payload" +#define kESDSDKCommonDevice "device" +#define kESDSDKCommonDeviceInfo "deviceInfo" + +// +// Events +// + +#define kESDSDKEventKeyDown "keyDown" +#define kESDSDKEventKeyUp "keyUp" +#define kESDSDKEventWillAppear "willAppear" +#define kESDSDKEventWillDisappear "willDisappear" +#define kESDSDKEventDeviceDidConnect "deviceDidConnect" +#define kESDSDKEventDeviceDidDisconnect "deviceDidDisconnect" +#define kESDSDKEventApplicationDidLaunch "applicationDidLaunch" +#define kESDSDKEventApplicationDidTerminate "applicationDidTerminate" +#define kESDSDKEventSystemDidWakeUp "systemDidWakeUp" +#define kESDSDKEventTitleParametersDidChange "titleParametersDidChange" +#define kESDSDKEventDidReceiveSettings "didReceiveSettings" +#define kESDSDKEventDidReceiveGlobalSettings "didReceiveGlobalSettings" +#define kESDSDKEventPropertyInspectorDidAppear "propertyInspectorDidAppear" +#define kESDSDKEventPropertyInspectorDidDisappear \ + "propertyInspectorDidDisappear" +#define kESDSDKEventDialRotate "dialRotate" +#define kESDSDKEventDialDown "dialDown" +#define kESDSDKEventDialUp "dialUp" +#define kESDSDKEventTouchTap "touchTap" + +// +// Functions +// + +#define kESDSDKEventSetTitle "setTitle" +#define kESDSDKEventSetImage "setImage" +#define kESDSDKEventShowAlert "showAlert" +#define kESDSDKEventShowOK "showOk" +#define kESDSDKEventGetSettings "getSettings" +#define kESDSDKEventSetSettings "setSettings" +#define kESDSDKEventGetGlobalSettings "getGlobalSettings" +#define kESDSDKEventSetGlobalSettings "setGlobalSettings" +#define kESDSDKEventSetState "setState" +#define kESDSDKEventSwitchToProfile "switchToProfile" +#define kESDSDKEventSendToPropertyInspector "sendToPropertyInspector" +#define kESDSDKEventSendToPlugin "sendToPlugin" +#define kESDSDKEventOpenURL "openUrl" +#define kESDSDKEventLogMessage "logMessage" +#define kESDSDKEventSetFeedback "setFeedback" + +// +// Payloads +// + +#define kESDSDKPayloadSettings "settings" +#define kESDSDKPayloadCoordinates "coordinates" +#define kESDSDKPayloadState "state" +#define kESDSDKPayloadUserDesiredState "userDesiredState" +#define kESDSDKPayloadTitle "title" +#define kESDSDKPayloadTitleParameters "titleParameters" +#define kESDSDKPayloadImage "image" +#define kESDSDKPayloadURL "url" +#define kESDSDKPayloadTarget "target" +#define kESDSDKPayloadProfile "profile" +#define kESDSDKPayloadApplication "application" +#define kESDSDKPayloadIsInMultiAction "isInMultiAction" +#define kESDSDKPayloadMessage "message" + +#define kESDSDKPayloadCoordinatesColumn "column" +#define kESDSDKPayloadCoordinatesRow "row" + +// +// Device Info +// + +#define kESDSDKDeviceInfoID "id" +#define kESDSDKDeviceInfoType "type" +#define kESDSDKDeviceInfoSize "size" +#define kESDSDKDeviceInfoName "name" + +#define kESDSDKDeviceInfoSizeColumns "columns" +#define kESDSDKDeviceInfoSizeRows "rows" + +// +// Title Parameters +// + +#define kESDSDKTitleParametersShowTitle "showTitle" +#define kESDSDKTitleParametersTitleColor "titleColor" +#define kESDSDKTitleParametersTitleAlignment "titleAlignment" +#define kESDSDKTitleParametersFontFamily "fontFamily" +#define kESDSDKTitleParametersFontSize "fontSize" +#define kESDSDKTitleParametersCustomFontSize "customFontSize" +#define kESDSDKTitleParametersFontStyle "fontStyle" +#define kESDSDKTitleParametersFontUnderline "fontUnderline" + +// +// Connection +// + +#define kESDSDKConnectSocketFunction "connectElgatoStreamDeckSocket" +#define kESDSDKRegisterPlugin "registerPlugin" +#define kESDSDKRegisterPropertyInspector "registerPropertyInspector" +#define kESDSDKPortParameter "-port" +#define kESDSDKPluginUUIDParameter "-pluginUUID" +#define kESDSDKRegisterEventParameter "-registerEvent" +#define kESDSDKInfoParameter "-info" +#define kESDSDKRegisterUUID "uuid" + +#define kESDSDKApplicationInfo "application" +#define kESDSDKPluginInfo "plugin" +#define kESDSDKDevicesInfo "devices" +#define kESDSDKColorsInfo "colors" +#define kESDSDKDevicePixelRatio "devicePixelRatio" + +#define kESDSDKApplicationInfoVersion "version" +#define kESDSDKApplicationInfoLanguage "language" +#define kESDSDKApplicationInfoPlatform "platform" + +#define kESDSDKApplicationInfoPlatformMac "mac" +#define kESDSDKApplicationInfoPlatformWindows "windows" + +#define kESDSDKColorsInfoHighlightColor "highlightColor" +#define kESDSDKColorsInfoMouseDownColor "mouseDownColor" +#define kESDSDKColorsInfoDisabledColor "disabledColor" +#define kESDSDKColorsInfoButtonPressedTextColor "buttonPressedTextColor" +#define kESDSDKColorsInfoButtonPressedBackgroundColor \ + "buttonPressedBackgroundColor" +#define kESDSDKColorsInfoButtonMouseOverBackgroundColor \ + "buttonMouseOverBackgroundColor" +#define kESDSDKColorsInfoButtonPressedBorderColor "buttonPressedBorderColor" + +typedef int ESDSDKTarget; +enum { + kESDSDKTarget_HardwareAndSoftware = 0, + kESDSDKTarget_HardwareOnly = 1, + kESDSDKTarget_SoftwareOnly = 2 +}; + +typedef int ESDSDKDeviceType; +enum { + kESDSDKDeviceType_StreamDeck = 0, + kESDSDKDeviceType_StreamDeckMini = 1, + kESDSDKDeviceType_StreamDeckXL = 2, + kESDSDKDeviceType_StreamDeckMobile = 3, + kESDSDKDeviceType_CorsairGKeys = 4, + kESDSDKDeviceType_StreamDeckPedal = 5, + kESDSDKDeviceType_CorsairVoyager = 6, + kESDSDKDeviceType_StreamDeckPlus = 7// From volume controller manifest.json +}; diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/NlohmannJSONUtils.h b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/NlohmannJSONUtils.h new file mode 100644 index 000000000..0a6fd492b --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/StreamDockSDK/NlohmannJSONUtils.h @@ -0,0 +1,161 @@ +//============================================================================== +/** +@file EPLJSONUtils.h + +@brief Utility methods for JSON parser from N.Lohmann + https://github.com/nlohmann/json +**/ +//============================================================================== + +#pragma once + +//------------------------------------------------------------------------------ +// Includes +//------------------------------------------------------------------------------ + +#include +using json = nlohmann::json; + +class NlohmannJSONUtils { + public: + //! Get object by name + static bool GetObjectByName( + const json& inJSON, + const std::string& inName, + json& outObject) { + // Check desired value exists + json::const_iterator iter(inJSON.find(inName)); + if (iter == inJSON.end()) + return false; + + // Check value is an array + if (!iter->is_object()) + return false; + + // Assign value + outObject = *iter; + + return true; + } + + //! Get array by name + static bool GetArrayByName( + const json& inJSON, + const std::string& inName, + json& outArray) { + // Check desired value exists + json::const_iterator iter(inJSON.find(inName)); + if (iter == inJSON.end()) + return false; + + // Check value is an array + if (!iter->is_array()) + return false; + + // Assign value + outArray = *iter; + + return true; + } + + //! Get string by name + static std::string GetStringByName( + const json& inJSON, + const std::string& inName, + const std::string& defaultValue = "") { + // Check desired value exists + json::const_iterator iter(inJSON.find(inName)); + if (iter == inJSON.end()) + return defaultValue; + + // Check value is a string + if (!iter->is_string()) + return defaultValue; + + // Return value + return iter->get(); + } + + //! Get string + static std::string GetString( + const json& j, + const std::string& defaultString = "") { + // Check value is a string + if (!j.is_string()) + return defaultString; + + return j.get(); + } + + //! Get bool by name + static bool GetBoolByName( + const json& inJSON, + const std::string& inName, + bool defaultValue = false) { + // Check desired value exists + json::const_iterator iter(inJSON.find(inName)); + if (iter == inJSON.end()) + return defaultValue; + + // Check value is a bool + if (!iter->is_boolean()) + return defaultValue; + + // Return value + return iter->get(); + } + + //! Get integer by name + static int GetIntByName( + const json& inJSON, + const std::string& inName, + int defaultValue = 0) { + // Check desired value exists + json::const_iterator iter(inJSON.find(inName)); + if (iter == inJSON.end()) + return defaultValue; + + // Check value is an integer + if (!iter->is_number_integer()) + return defaultValue; + + // Return value + return iter->get(); + } + + //! Get unsigned integer by name + static unsigned int GetUnsignedIntByName( + const json& inJSON, + const std::string& inName, + unsigned int defaultValue = 0) { + // Check desired value exists + json::const_iterator iter(inJSON.find(inName)); + if (iter == inJSON.end()) + return defaultValue; + + // Check value is an unsigned integer + if (!iter->is_number_unsigned()) + return defaultValue; + + // Return value + return iter->get(); + } + + //! Get float by name + static float GetFloatByName( + const json& inJSON, + const std::string& inName, + float defaultValue = 0.0) { + // Check desired value exists + json::const_iterator iter(inJSON.find(inName)); + if (iter == inJSON.end()) + return defaultValue; + + // Check value is an integer + if (!iter->is_number_float() && !iter->is_number_integer()) + return defaultValue; + + // Return value + return iter->get(); + } +}; diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/CMakeLists.txt b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/CMakeLists.txt new file mode 100644 index 000000000..533f5089b --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/CMakeLists.txt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cfe5f321a188540355e52270231f4ac2e72666f8ccb62e30b6f14cd76b8f18e5 +size 98 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/asio-7377f941cc9831c4c3cba1e71d3bc29bddf1a700.patch b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/asio-7377f941cc9831c4c3cba1e71d3bc29bddf1a700.patch new file mode 100644 index 000000000..e5aada22c --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/asio-7377f941cc9831c4c3cba1e71d3bc29bddf1a700.patch @@ -0,0 +1,23 @@ +From 7377f941cc9831c4c3cba1e71d3bc29bddf1a700 Mon Sep 17 00:00:00 2001 +From: Christopher Kohlhoff +Date: Tue, 29 Dec 2020 18:32:50 +1100 +Subject: [PATCH] Add missing inline keyword in MSVC-specific workaround. + +Fixes duplicate symbols when building with coroutine support. +--- + asio/include/asio/impl/use_awaitable.hpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/asio/include/asio/impl/use_awaitable.hpp b/asio/include/asio/impl/use_awaitable.hpp +index 60a6f5cd2..af7be635e 100644 +--- a/asio/include/asio/impl/use_awaitable.hpp ++++ b/asio/include/asio/impl/use_awaitable.hpp +@@ -236,7 +236,7 @@ T dummy_return() + } + + template <> +-void dummy_return() ++inline void dummy_return() + { + } + #endif // defined(_MSC_VER) diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/asio-asio-1-27-0.tar.gz b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/asio-asio-1-27-0.tar.gz new file mode 100644 index 000000000..f93b96466 Binary files /dev/null and b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/asio-asio-1-27-0.tar.gz differ diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/asio-asio-1-27-0.zip b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/asio-asio-1-27-0.zip new file mode 100644 index 000000000..1e0bf64e3 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/asio-asio-1-27-0.zip @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9bee8e50236ce7752325efde38cae223cbc85759ed49509d9cbc5d55458332d9 +size 4122145 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/asio.cmake b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/asio.cmake new file mode 100644 index 000000000..b2a7ef733 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/asio.cmake @@ -0,0 +1,52 @@ +include(ExternalProject) + +# Download url +# https://github.com/chriskohlhoff/asio/archive/refs/tags/asio-1-27-0.zip +# https://github.com/chriskohlhoff/asio/archive/refs/tags/asio-1-27-0.tar.gz + +ExternalProject_Add( + asio_source + URL ${CMAKE_CURRENT_LIST_DIR}/asio-asio-1-27-0.zip + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" +) +ExternalProject_Get_Property(asio_source SOURCE_DIR) +add_library(asio INTERFACE) +add_dependencies(asio asio_source) +set_target_properties( + asio + PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIOSN OFF +) +target_compile_definitions( + asio + INTERFACE + ASIO_STANDALONE=1 +) +target_include_directories( + asio + INTERFACE + ${SOURCE_DIR}/asio/include +) + +install( + DIRECTORY + ${SOURCE_DIR}/asio/include/ + DESTINATION + "${CMAKE_INSTALL_INCLUDEDIR}" + FILES_MATCHING + PATTERN "*.hpp" + PATTERN "*.ipp" +) + +# Work around https://github.com/chriskohlhoff/asio/issues/1090 +if(APPLE AND "${CMAKE_OSX_DEPLOYMENT_TARGET}" VERSION_LESS "10.15") + target_compile_definitions( + asio + INTERFACE + "ASIO_DISABLE_STD_ALIGNED_ALLOC=1" + ) +endif() diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/include.zip b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/include.zip new file mode 100644 index 000000000..a904066d9 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/include.zip @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e5c7a9f49a16814be27e4ed0ee900ecd0092bfb7dbfca65b5a421b774dccaaed +size 293810 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/json.cmake b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/json.cmake new file mode 100644 index 000000000..2475bcda2 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/json.cmake @@ -0,0 +1,27 @@ +include(ExternalProject) + +# Download url +# https://github.com/nlohmann/json/releases/download/v3.11.2/include.zip + +ExternalProject_Add( + json_source + URL ${CMAKE_CURRENT_LIST_DIR}/include.zip + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" +) +ExternalProject_Get_Property(json_source SOURCE_DIR) +add_library(json INTERFACE) +add_dependencies(json json_source) +target_include_directories( + json + INTERFACE + "${SOURCE_DIR}/single_include" +) + +install( + DIRECTORY + "${SOURCE_DIR}/single_include/" + DESTINATION + "${CMAKE_INSTALL_INCLUDEDIR}" +) diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/websocketpp-0.8.2.tar.gz b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/websocketpp-0.8.2.tar.gz new file mode 100644 index 000000000..cee3568dc Binary files /dev/null and b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/websocketpp-0.8.2.tar.gz differ diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/websocketpp-0.8.2.zip b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/websocketpp-0.8.2.zip new file mode 100644 index 000000000..602348354 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/websocketpp-0.8.2.zip @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0281b1d1278f01138805279fd2277901ae3c71b76e28364575356061098111c6 +size 955285 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/websocketpp.cmake b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/websocketpp.cmake new file mode 100644 index 000000000..5097d6ac2 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/StreamDockCPPSDK/Vendor/websocketpp.cmake @@ -0,0 +1,52 @@ +include(ExternalProject) + +# Download url +# https://github.com/zaphoyd/websocketpp/archive/refs/tags/0.8.2.zip +# https://github.com/zaphoyd/websocketpp/archive/refs/tags/0.8.2.tar.gz + +ExternalProject_Add( + websocketpp_source + URL ${CMAKE_CURRENT_LIST_DIR}/websocketpp-0.8.2.zip + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" +) +ExternalProject_Get_Property(websocketpp_source SOURCE_DIR) +add_library(websocketpp INTERFACE) +add_dependencies(websocketpp websocketpp_source) +set_target_properties( + websocketpp + PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF +) +target_include_directories( + websocketpp + INTERFACE + "${SOURCE_DIR}" +) +target_link_libraries( + websocketpp + INTERFACE + asio +) +target_compile_definitions( + websocketpp + INTERFACE + ASIO_STANDALONE=1 +) +if(MSVC) + target_compile_options( + websocketpp + INTERFACE + "/Zc:__cplusplus" + ) +endif() + +install( + DIRECTORY + "${SOURCE_DIR}/websocketpp" + DESTINATION + "${CMAKE_INSTALL_INCLUDEDIR}" +) diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/action1/index.html b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/action1/index.html new file mode 100644 index 000000000..035ba22bd --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/action1/index.html @@ -0,0 +1,27 @@ + + + + + + dome - 属性检查器 + + + + + + + + + + + + \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/action1/index.js b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/action1/index.js new file mode 100644 index 000000000..7d607d076 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/action1/index.js @@ -0,0 +1,18 @@ +/** + * 基础参数说明: + * @local 是否国际化 + * @back 自主决定回显时机 + * @dom 保存需要的文档元素 + * @propEvent 软件回调事件 - 策略模式 + * ==================================================> + */ +const $local = true, $back = false, + $dom = { + main: $('.sdpi-wrapper'), + }, + $propEvent = { + didReceiveSettings() { + console.log($settings); + }, + sendToPropertyInspector(data) { } + }; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/action2/index.html b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/action2/index.html new file mode 100644 index 000000000..035ba22bd --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/action2/index.html @@ -0,0 +1,27 @@ + + + + + + dome - 属性检查器 + + + + + + + + + + + + \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/action2/index.js b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/action2/index.js new file mode 100644 index 000000000..7d607d076 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/action2/index.js @@ -0,0 +1,18 @@ +/** + * 基础参数说明: + * @local 是否国际化 + * @back 自主决定回显时机 + * @dom 保存需要的文档元素 + * @propEvent 软件回调事件 - 策略模式 + * ==================================================> + */ +const $local = true, $back = false, + $dom = { + main: $('.sdpi-wrapper'), + }, + $propEvent = { + didReceiveSettings() { + console.log($settings); + }, + sendToPropertyInspector(data) { } + }; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/de.json b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/de.json new file mode 100644 index 000000000..468723cb4 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/de.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c60505a0e25f53bb86d88f8ab9e1089077a7e0141ebdfa76f0e65a92ebc7de90 +size 377 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/en.json b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/en.json new file mode 100644 index 000000000..468723cb4 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/en.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c60505a0e25f53bb86d88f8ab9e1089077a7e0141ebdfa76f0e65a92ebc7de90 +size 377 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/es.json b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/es.json new file mode 100644 index 000000000..468723cb4 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/es.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c60505a0e25f53bb86d88f8ab9e1089077a7e0141ebdfa76f0e65a92ebc7de90 +size 377 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/fr.json b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/fr.json new file mode 100644 index 000000000..468723cb4 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/fr.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c60505a0e25f53bb86d88f8ab9e1089077a7e0141ebdfa76f0e65a92ebc7de90 +size 377 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State0.png b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State0.png new file mode 100644 index 000000000..8af588dab --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21aa938de7025453a5c6c72b3f1f27e2d7caef59beb9566d3d213374b8d6026e +size 1522 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State0@2x.png b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State0@2x.png new file mode 100644 index 000000000..12c9dd281 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State0@2x.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d98dc120319df2e1cdedf83ad8fbc3edf7afddf04877fa4995d35e9a9ffaa96 +size 3907 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State1.png b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State1.png new file mode 100644 index 000000000..640e1f5c2 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:692037d1ae3b91c2b4c8f9824317820df079320d3236fd0c6112e18870d56bf3 +size 1653 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State1@2x.png b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State1@2x.png new file mode 100644 index 000000000..57122a59a --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action1DefaultImage_State1@2x.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d7dfc03ab4f9e0d1f53191aca6fb5886a931c8ade933372c3de0cf07d9e2f687 +size 3547 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State0.png b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State0.png new file mode 100644 index 000000000..4c012b2c4 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6680297ad6133e276cdc43d3ffa74bfbaf9efe7dbf2cb9b979a314ee58525831 +size 1068 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State0@2x.png b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State0@2x.png new file mode 100644 index 000000000..1293caf5c --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State0@2x.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:538c1afaf8ff00e7dd575edeb6bc81ebe563353151fb3a1e56b662f8efdf6a87 +size 2994 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State1.png b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State1.png new file mode 100644 index 000000000..a0a5241f8 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b6a260194b148d4782c97ac6129c7c9649a8fd4c803533cc47e8a1091b8125a +size 1528 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State1@2x.png b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State1@2x.png new file mode 100644 index 000000000..b2f477415 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/action2DefaultImage_State1@2x.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ceaf9102ba83f45539e61a27ce5ebf073c1a9fcb27c8412416964b5a30fcead +size 3841 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon1.png b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon1.png new file mode 100644 index 000000000..9fd80c422 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0de468471d4ec9eba9e19fcb1fe6592cc9efb97f7d89b6ad75f4f845d5bed146 +size 229 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon1@2x.png b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon1@2x.png new file mode 100644 index 000000000..d7bd6f3ad --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon1@2x.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f1b35a7cd5280f9d3395282d40c8cb0ef6d15d10d3751719f75ad8d4533c1cf7 +size 344 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon2.png b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon2.png new file mode 100644 index 000000000..f2c55c785 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21195acda939241241d1f7be7a2c129e301be27ce7c961f31612404eb25b7674 +size 171 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon2@2x.png b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon2@2x.png new file mode 100644 index 000000000..c068060a9 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/actions/actionIcon2@2x.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:236df1df665479b69cd17baac9ff60d73b7c08758529ee0b7df42f49a127d186 +size 259 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/icons/pluginIcon.png b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/icons/pluginIcon.png new file mode 100644 index 000000000..b60cbf7af --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/icons/pluginIcon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e9a867177835ae4bab6648dcdebaeb097170850a322a9ff0dc15ecfb1182305 +size 306 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/icons/pluginIcon@2x.png b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/icons/pluginIcon@2x.png new file mode 100644 index 000000000..b7e2ace4f --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/images/icons/pluginIcon@2x.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59944b10f1da39ba5721b00797e4c79bce7a6f023385d6c8923fa927b0525976 +size 492 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/it.json b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/it.json new file mode 100644 index 000000000..468723cb4 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/it.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c60505a0e25f53bb86d88f8ab9e1089077a7e0141ebdfa76f0e65a92ebc7de90 +size 377 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/ja.json b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/ja.json new file mode 100644 index 000000000..468723cb4 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/ja.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c60505a0e25f53bb86d88f8ab9e1089077a7e0141ebdfa76f0e65a92ebc7de90 +size 377 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/manifest.json b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/manifest.json new file mode 100644 index 000000000..beb0a52d8 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/manifest.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b8b946e1bc6eff777eecaf74dffd8ccc47f35451bf07594e234ea32ef9f9d19 +size 1768 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/pt.json b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/pt.json new file mode 100644 index 000000000..468723cb4 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/pt.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c60505a0e25f53bb86d88f8ab9e1089077a7e0141ebdfa76f0e65a92ebc7de90 +size 377 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/readme.md b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/readme.md new file mode 100644 index 000000000..b5c84fdc6 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/readme.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e862724d53e761e916a709132de6f5f823abb0e8149f9115745ab26250d82cc7 +size 6416 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/action.js b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/action.js new file mode 100644 index 000000000..612871fe6 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/action.js @@ -0,0 +1,131 @@ +/** + * PropertyInspector 2.5.0 新特性 => + * + * 1 => 工具与主文件相分离 - 按需引入 + * 2 => $settings - 全局持久化数据代理 ※ + * 3 => 无需关注上下文 - 随时随地与插件通信 + * 4 => 注意事项: 为了避免命名冲突,请勿使用 $ 相关的名称以及JQuery库 + * + * ===== CJHONG ========================================== 2023.10.10 =====> + */ + +let $websocket, $uuid, $action, $context, $settings, $lang; + +// 与插件通信 +WebSocket.prototype.sendToPlugin = function (payload) { + this.send(JSON.stringify({ + event: "sendToPlugin", + action: $action, + context: $uuid, + payload + })); +} + +// 设置状态 +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 + })) +}, 0) + +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 文件路径回调 +let $FileID = ''; 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/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/caret.svg b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/caret.svg new file mode 100644 index 000000000..b69162a4f --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/caret.svg @@ -0,0 +1,3 @@ + + + diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/check.png b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/check.png new file mode 100644 index 000000000..ece8509f5 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/check.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5fb6fbe1417751ca7dd87552398d4585cb002654aa69632fdf6f43dbc65220c +size 234 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/check.svg b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/check.svg new file mode 100644 index 000000000..5b96af052 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/elg_calendar.svg b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/elg_calendar.svg new file mode 100644 index 000000000..157e01bb5 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/elg_calendar.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/elg_calendar_inv.svg b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/elg_calendar_inv.svg new file mode 100644 index 000000000..4f8af68d3 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/elg_calendar_inv.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/g_d8d8d8.svg b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/g_d8d8d8.svg new file mode 100644 index 000000000..d99031408 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/g_d8d8d8.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/rcheck.svg b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/rcheck.svg new file mode 100644 index 000000000..af478ee58 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/rcheck.svg @@ -0,0 +1,3 @@ + + + diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/sdpi.css b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/sdpi.css new file mode 100644 index 000000000..26b725b1e --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/css/sdpi.css @@ -0,0 +1,230 @@ +:root{--sdpi-bgcolor:#2D2D2D;--sdpi-background:#3D3D3D;--sdpi-color:#d8d8d8;--sdpi-bordercolor:#3a3a3a;--sdpi-buttonbordercolor:#969696;--sdpi-borderradius:0px;--sdpi-width:224px;--sdpi-fontweight:600;--sdpi-letterspacing:-0.25pt} +html{--sdpi-bgcolor:#2D2D2D;--sdpi-background:#3D3D3D;--sdpi-color:#d8d8d8;--sdpi-bordercolor:#3a3a3a;--sdpi-buttonbordercolor:#969696;--sdpi-borderradius:0px;--sdpi-width:224px;--sdpi-fontweight:600;--sdpi-letterspacing:-0.25pt;height:100%;width:100%;overflow:hidden;touch-action:none;user-select:none} +html,body{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:9pt;background-color:var(--sdpi-bgcolor);color:#9a9a9a} +body{height:100%;padding:0;overflow-x:hidden;overflow-y:auto;margin:0;-webkit-overflow-scrolling:touch;-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased} +mark{background-color:var(--sdpi-bgcolor);color:var(--sdpi-color)} +hr,hr2{-webkit-margin-before:1em;-webkit-margin-after:1em;border-style:none;background:var(--sdpi-background);height:1px} +hr2,.sdpi-heading{display:flex;flex-basis:100%;align-items:center;color:inherit;font-size:9pt;margin:8px 0px} +.sdpi-heading::before,.sdpi-heading::after{content:"";flex-grow:1;background:var(--sdpi-background);height:1px;font-size:0px;line-height:0px;margin:0px 16px} +hr2{height:2px} +hr,hr2{margin-left:16px;margin-right:16px} +.sdpi-item-value,option,input,select,button{font-size:10pt;font-weight:var(--sdpi-fontweight);letter-spacing:var(--sdpi-letterspacing)} +.win .sdpi-item-value,.win option,.win input,.win select,.win button{font-size:11px;font-style:normal;letter-spacing:inherit;font-weight:100} +.win button{font-size:12px} +::-webkit-progress-value,meter::-webkit-meter-optimum-value{border-radius:2px} +::-webkit-progress-bar,meter::-webkit-meter-bar{border-radius:3px;background:var(--sdpi-background)} +::-webkit-progress-bar:active,meter::-webkit-meter-bar:active{border-radius:3px;background:#222222} +::-webkit-progress-value:active,meter::-webkit-meter-optimum-value:active{background:#99f} +progress,progress.sdpi-item-value{min-height:5px !important;height:5px;background-color:#303030} +progress{margin-top:8px !important;margin-bottom:8px !important} +.full progress,progress.full{margin-top:3px !important} +::-webkit-progress-inner-element{background-color:transparent} +.sdpi-item[type="progress"]{margin-top:4px !important;margin-bottom:12px;min-height:15px} +.sdpi-item-child.full:last-child{margin-bottom:4px} +.tabs{display:flex;border-bottom:1px solid #D7DBDD} +.tab{cursor:pointer;padding:5px 30px;color:#16a2d7;font-size:9pt;border-bottom:2px solid transparent} +.tab.is-tab-selected{border-bottom-color:#4ebbe4} +select{width:100%;-webkit-appearance:none;-moz-appearance:none;-o-appearance:none;appearance:none;background:url(caret.svg) no-repeat 97% center} +label.sdpi-file-label,input[type="button"],input[type="submit"],input[type="reset"],input[type="file"],input[type=file]::-webkit-file-upload-button,button,select{color:var(--sdpi-color);border:1pt solid #303030;font-size:8pt;background-color:var(--sdpi-background);border-radius:var(--sdpi-borderradius)} +label.sdpi-file-label,input[type="button"],input[type="submit"],input[type="reset"],input[type="file"],input[type=file]::-webkit-file-upload-button,button{border:1pt solid var(--sdpi-buttonbordercolor);border-radius:var(--sdpi-borderradius);border-color:var(--sdpi-buttonbordercolor);min-height:23px !important;height:23px !important;margin-right:8px} +input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0} +input[type="file"]{border-radius:var(--sdpi-borderradius);max-width:220px} +option{height:1.5em;padding:4px} +.sdpi-wrapper{overflow-x:hidden;height:100%} +.sdpi-item{display:flex;flex-direction:row;min-height:32px;align-items:center;margin-top:2px;max-width:344px;-webkit-user-drag:none} +.sdpi-item:first-child{margin-top:-1px} +.sdpi-item:last-child{margin-bottom:0px} +.sdpi-item>*:not(.sdpi-item-label):not(meter):not(details):not(canvas){min-height:26px;padding:0px 4px 0px 4px} +.sdpi-item>*:not(.sdpi-item-label.empty):not(meter){min-height:26px;padding:0px 4px 0px 4px} +.sdpi-item-group{padding:0 !important} +meter.sdpi-item-value{margin-left:6px} +.sdpi-item[type="group"]{display:block;margin-top:12px;margin-bottom:12px;flex-direction:unset;text-align:left} +.sdpi-item[type="group"]>.sdpi-item-label,.sdpi-item[type="group"].sdpi-item-label{width:96%;text-align:left;font-weight:700;margin-bottom:4px;padding-left:4px} +dl,ul,ol{-webkit-margin-before:0px;-webkit-margin-after:4px;-webkit-padding-start:1em;max-height:90px;overflow-y:scroll;cursor:pointer;user-select:none} +table.sdpi-item-value,dl.sdpi-item-value,ul.sdpi-item-value,ol.sdpi-item-value{-webkit-margin-before:4px;-webkit-margin-after:8px;-webkit-padding-start:1em;width:var(--sdpi-width);text-align:center} +table>caption{margin:2px} +.list,.sdpi-item[type="list"]{align-items:baseline} +.sdpi-item-label{text-align:right;flex:none;width:94px;padding-right:4px;font-weight:600} +.win .sdpi-item-label,.sdpi-item-label>small{font-weight:normal} +.sdpi-item-label:after{content:":"} +.sdpi-item-label.empty:after{content:""} +.sdpi-test,.sdpi-item-value{flex:1 0 0;margin-right:14px;margin-left:4px;justify-content:space-evenly} +canvas.sdpi-item-value{max-width:144px;max-height:144px;width:144px;height:144px;margin:0 auto;cursor:pointer} +input.sdpi-item-value{margin-left:5px} +.sdpi-item-value button,button.sdpi-item-value{margin-left:6px;margin-right:14px} +.sdpi-item-value.range{margin-left:0px} +table,dl.sdpi-item-value,ul.sdpi-item-value,ol.sdpi-item-value,.sdpi-item-value>dl,.sdpi-item-value>ul,.sdpi-item-value>ol{list-style-type:none;list-style-position:outside;margin-left:-4px;margin-right:-4px;padding:4px;border:1px solid var(--sdpi-bordercolor)} +dl.sdpi-item-value,ul.sdpi-item-value,ol.sdpi-item-value,.sdpi-item-value>ol{list-style-type:none;list-style-position:inside;margin-left:5px;margin-right:12px;padding:4px !important;display:flex;flex-direction:column} +.two-items li{display:flex} +.two-items li>*:first-child{flex:0 0 50%;text-align:left} +.two-items.thirtyseventy li>*:first-child{flex:0 0 30%} +ol.sdpi-item-value,.sdpi-item-value>ol[listtype="none"]{list-style-type:none} +ol.sdpi-item-value[type="decimal"],.sdpi-item-value>ol[type="decimal"]{list-style-type:decimal} +ol.sdpi-item-value[type="decimal-leading-zero"],.sdpi-item-value>ol[type="decimal-leading-zero"]{list-style-type:decimal-leading-zero} +ol.sdpi-item-value[type="lower-alpha"],.sdpi-item-value>ol[type="lower-alpha"]{list-style-type:lower-alpha} +ol.sdpi-item-value[type="upper-alpha"],.sdpi-item-value>ol[type="upper-alpha"]{list-style-type:upper-alpha} +ol.sdpi-item-value[type="upper-roman"],.sdpi-item-value>ol[type="upper-roman"]{list-style-type:upper-roman} +ol.sdpi-item-value[type="lower-roman"],.sdpi-item-value>ol[type="lower-roman"]{list-style-type:upper-roman} +tr:nth-child(even),.sdpi-item-value>ul>li:nth-child(even),.sdpi-item-value>ol>li:nth-child(even),li:nth-child(even){background-color:rgba(0,0,0,.2)} +td:hover,.sdpi-item-value>ul>li:hover:nth-child(even),.sdpi-item-value>ol>li:hover:nth-child(even),li:hover:nth-child(even),li:hover{background-color:rgba(255,255,255,.1)} +td.selected,td.selected:hover,li.selected:hover,li.selected{color:white;background-color:#77f} +tr{border:1px solid var(--sdpi-bordercolor)} +td{border-right:1px solid var(--sdpi-bordercolor)} +tr:last-child,td:last-child{border:none} +.sdpi-item-value.select,.sdpi-item-value>select{margin-right:13px;margin-left:4px} +.sdpi-item-child,.sdpi-item-group>.sdpi-item>input[type="color"]{margin-top:0.4em;margin-right:4px} +.full,.full *,.sdpi-item-value.full,.sdpi-item-child>full>*,.sdpi-item-child.full,.sdpi-item-child.full>*,.full>.sdpi-item-child,.full>.sdpi-item-child>*{display:flex;flex:1 1 0;margin-bottom:4px;margin-left:0px;width:100%;justify-content:space-evenly} +.sdpi-item-group>.sdpi-item>input[type="color"]{margin-top:0px} +::-webkit-calendar-picker-indicator:focus,input[type=file]::-webkit-file-upload-button:focus,button:focus,textarea:focus,input:focus,select:focus,option:focus,details:focus,summary:focus,.custom-select select{outline:none} +summary{cursor:default;padding-left:90px;padding-right:70px} +.pointer,summary .pointer{cursor:pointer} +details *{font-size:12px;font-weight:normal;word-break:break-all;} +details.message{padding:4px 18px 4px 12px} +details.message summary{min-height:18px} +details.message:first-child{margin-top:4px;margin-left:0;padding-left:102px} +details.message h1{text-align:left} +.message>summary::-webkit-details-marker{display:none} +.info20,.question,.caution,.info{background-repeat:no-repeat;background-position:72px center} +.info20{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,20 C4.4771525,20 0,15.5228475 0,10 C0,4.4771525 4.4771525,0 10,0 C15.5228475,0 20,4.4771525 20,10 C20,15.5228475 15.5228475,20 10,20 Z M10,8 C8.8954305,8 8,8.84275812 8,9.88235294 L8,16.1176471 C8,17.1572419 8.8954305,18 10,18 C11.1045695,18 12,17.1572419 12,16.1176471 L12,9.88235294 C12,8.84275812 11.1045695,8 10,8 Z M10,3 C8.8954305,3 8,3.88165465 8,4.96923077 L8,5.03076923 C8,6.11834535 8.8954305,7 10,7 C11.1045695,7 12,6.11834535 12,5.03076923 L12,4.96923077 C12,3.88165465 11.1045695,3 10,3 Z'/%3E%3C/svg%3E%0A")} +.info{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,18 C5.581722,18 2,14.418278 2,10 C2,5.581722 5.581722,2 10,2 C14.418278,2 18,5.581722 18,10 C18,14.418278 14.418278,18 10,18 Z M10,8 C9.44771525,8 9,8.42137906 9,8.94117647 L9,14.0588235 C9,14.5786209 9.44771525,15 10,15 C10.5522847,15 11,14.5786209 11,14.0588235 L11,8.94117647 C11,8.42137906 10.5522847,8 10,8 Z M10,5 C9.44771525,5 9,5.44082732 9,5.98461538 L9,6.01538462 C9,6.55917268 9.44771525,7 10,7 C10.5522847,7 11,6.55917268 11,6.01538462 L11,5.98461538 C11,5.44082732 10.5522847,5 10,5 Z'/%3E%3C/svg%3E%0A")} +.info2{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 15 15'%3E%3Cpath fill='%23999' d='M7.5,15 C3.35786438,15 0,11.6421356 0,7.5 C0,3.35786438 3.35786438,0 7.5,0 C11.6421356,0 15,3.35786438 15,7.5 C15,11.6421356 11.6421356,15 7.5,15 Z M7.5,2 C6.67157287,2 6,2.66124098 6,3.47692307 L6,3.52307693 C6,4.33875902 6.67157287,5 7.5,5 C8.32842705,5 9,4.33875902 9,3.52307693 L9,3.47692307 C9,2.66124098 8.32842705,2 7.5,2 Z M5,6 L5,7.02155172 L6,7 L6,12 L5,12.0076778 L5,13 L10,13 L10,12 L9,12.0076778 L9,6 L5,6 Z'/%3E%3C/svg%3E%0A")} +.sdpi-more-info{background-image:linear-gradient(to right,#00000000 0%,#00000040 80%),url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpolygon fill='%23999' points='4 7 8 7 8 5 12 8 8 11 8 9 4 9'/%3E%3C/svg%3E%0A")} +.caution{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' fill-rule='evenodd' d='M9.03952676,0.746646542 C9.57068894,-0.245797319 10.4285735,-0.25196227 10.9630352,0.746646542 L19.7705903,17.2030214 C20.3017525,18.1954653 19.8777595,19 18.8371387,19 L1.16542323,19 C0.118729947,19 -0.302490098,18.2016302 0.231971607,17.2030214 L9.03952676,0.746646542 Z M10,2.25584053 L1.9601405,17.3478261 L18.04099,17.3478261 L10,2.25584053 Z M10,5.9375 C10.531043,5.9375 10.9615385,6.37373537 10.9615385,6.91185897 L10.9615385,11.6923077 C10.9615385,12.2304313 10.531043,12.6666667 10,12.6666667 C9.46895697,12.6666667 9.03846154,12.2304313 9.03846154,11.6923077 L9.03846154,6.91185897 C9.03846154,6.37373537 9.46895697,5.9375 10,5.9375 Z M10,13.4583333 C10.6372516,13.4583333 11.1538462,13.9818158 11.1538462,14.6275641 L11.1538462,14.6641026 C11.1538462,15.3098509 10.6372516,15.8333333 10,15.8333333 C9.36274837,15.8333333 8.84615385,15.3098509 8.84615385,14.6641026 L8.84615385,14.6275641 C8.84615385,13.9818158 9.36274837,13.4583333 10,13.4583333 Z'/%3E%3C/svg%3E%0A")} +.question{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,18 C5.581722,18 2,14.418278 2,10 C2,5.581722 5.581722,2 10,2 C14.418278,2 18,5.581722 18,10 C18,14.418278 14.418278,18 10,18 Z M6.77783203,7.65332031 C6.77783203,7.84798274 6.85929281,8.02888914 7.0222168,8.19604492 C7.18514079,8.36320071 7.38508996,8.44677734 7.62207031,8.44677734 C8.02409055,8.44677734 8.29703704,8.20768468 8.44091797,7.72949219 C8.59326248,7.27245865 8.77945854,6.92651485 8.99951172,6.69165039 C9.2195649,6.45678594 9.56233491,6.33935547 10.027832,6.33935547 C10.4256205,6.33935547 10.7006836,6.37695313 11.0021973,6.68847656 C11.652832,7.53271484 10.942627,8.472229 10.3750916,9.1321106 C9.80755615,9.79199219 8.29492188,11.9897461 10.027832,12.1347656 C10.4498423,12.1700818 10.7027991,11.9147157 10.7832031,11.4746094 C11.0021973,9.59857178 13.1254883,8.82415771 13.1254883,7.53271484 C13.1254883,7.07568131 12.9974785,6.65250846 12.7414551,6.26318359 C12.4854317,5.87385873 12.1225609,5.56600048 11.652832,5.33959961 C11.1831031,5.11319874 10.6414419,5 10.027832,5 C9.36767248,5 8.79004154,5.13541531 8.29492187,5.40625 C7.79980221,5.67708469 7.42317837,6.01879677 7.16503906,6.43139648 C6.90689975,6.8439962 6.77783203,7.25130007 6.77783203,7.65332031 Z M10.0099668,15 C10.2713191,15 10.5016601,14.9108147 10.7009967,14.7324415 C10.9003332,14.5540682 11,14.3088087 11,13.9966555 C11,13.7157177 10.9047629,13.4793767 10.7142857,13.2876254 C10.5238086,13.0958742 10.2890379,13 10.0099668,13 C9.72646591,13 9.48726565,13.0958742 9.2923588,13.2876254 C9.09745196,13.4793767 9,13.7157177 9,13.9966555 C9,14.313268 9.10077419,14.5596424 9.30232558,14.735786 C9.50387698,14.9119295 9.73975502,15 10.0099668,15 Z'/%3E%3C/svg%3E%0A")} +.sdpi-more-info{position:fixed;left:0px;right:0px;bottom:0px;min-height:16px;padding-right:16px;text-align:right;-webkit-touch-callout:none;cursor:pointer;user-select:none;background-position:right center;background-repeat:no-repeat;border-radius:var(--sdpi-borderradius);text-decoration:none;color:var(--sdpi-color)} +.sdpi-more-info-button{display:flex;align-self:right;margin-left:auto;position:fixed;right:17px;bottom:0px;user-select:none} +.sdpi-bottom-bar{display:flex;align-self:right;margin-left:auto;position:fixed;right:17px;bottom:0px;user-select:none} +.sdpi-bottom-bar.right{right:0px} +.sdpi-bottom-bar button{min-height:20px !important;height:20px !important} +details a{background-position:right !important;min-height:24px;display:inline-block;line-height:24px;padding-right:28px} +input:not([type="range"]),textarea{-webkit-appearance:none;appearance:none;background:var(--sdpi-background);color:var(--sdpi-color);font-weight:normal;font-size:9pt;border:none;margin-top:2px;margin-bottom:2px;min-width:219px} +textarea+label{display:flex;justify-content:flex-end} +input[type="radio"],input[type="checkbox"]{display:none} +input[type="radio"]+label,input[type="checkbox"]+label{font-size:9pt;color:var(--sdpi-color);font-weight:normal;margin-right:8px} +input[type="radio"]+label:after,input[type="checkbox"]+label:after{content:" " !important} +.sdpi-item[type="radio"]>.sdpi-item-value,.sdpi-item[type="checkbox"]>.sdpi-item-value{padding-top:2px} +.sdpi-item[type="checkbox"]>.sdpi-item-value>*{margin-top:4px} +.sdpi-item[type="checkbox"] .sdpi-item-child,.sdpi-item[type="radio"] .sdpi-item-child{display:inline-block} +.sdpi-item[type="range"] .sdpi-item-value,.sdpi-item[type="meter"] .sdpi-item-child,.sdpi-item[type="progress"] .sdpi-item-child{display:flex} +.sdpi-item[type="range"] .sdpi-item-value{min-height:26px} +.sdpi-item[type="range"] .sdpi-item-value span,.sdpi-item[type="meter"] .sdpi-item-child span,.sdpi-item[type="progress"] .sdpi-item-child span{margin-top:-2px;min-width:8px;text-align:right;user-select:none;cursor:pointer;-webkit-user-select:none;user-select:none} +.sdpi-item[type="range"] .sdpi-item-value span{margin-top:7px;text-align:right} +span+input[type="range"]{display:flex;max-width:168px} +.sdpi-item[type="range"] .sdpi-item-value span:first-child,.sdpi-item[type="meter"] .sdpi-item-child span:first-child,.sdpi-item[type="progress"] .sdpi-item-child span:first-child{margin-right:4px} +.sdpi-item[type="range"] .sdpi-item-value span:last-child,.sdpi-item[type="meter"] .sdpi-item-child span:last-child,.sdpi-item[type="progress"] .sdpi-item-child span:last-child{margin-left:4px} +.reverse{transform:rotate(180deg)} +.sdpi-item[type="meter"] .sdpi-item-child meter+span:last-child{margin-left:-10px} +.sdpi-item[type="progress"] .sdpi-item-child meter+span:last-child{margin-left:-14px} +.sdpi-item[type="radio"]>.sdpi-item-value>*{margin-top:2px} +details{padding:8px 18px 8px 12px;min-width:86px} +details>h4{border-bottom:1px solid var(--sdpi-bordercolor)} +legend{display:none} +.sdpi-item-value>textarea{padding:0px;width:219px;margin-left:1px;margin-top:3px;padding:4px} +input[type="radio"]+label span,input[type="checkbox"]+label span{display:inline-block;width:16px;height:16px;margin:2px 4px 2px 0;border-radius:3px;vertical-align:middle;background:var(--sdpi-background);cursor:pointer;border:1px solid rgb(0,0,0,.2)} +input[type="radio"]+label span{border-radius:100%} +input[type="radio"]:checked+label span,input[type="checkbox"]:checked+label span{background-color:#77f;background-image:url(check.svg);background-repeat:no-repeat;background-position:center center;border:1px solid rgb(0,0,0,.4)} +input[type="radio"]:active:checked+label span,input[type="radio"]:active+label span,input[type="checkbox"]:active:checked+label span,input[type="checkbox"]:active+label span{background-color:#303030} +input[type="radio"]:checked+label span{background-image:url(rcheck.svg)} +input[type="range"]{width:var(--sdpi-width);height:30px;overflow:hidden;cursor:pointer;background:transparent !important} +.sdpi-item>input[type="range"]{margin-left:2px;max-width:var(--sdpi-width);width:var(--sdpi-width);padding:0px;margin-top:2px} +input[type="range"]::-webkit-slider-runnable-track{height:5px;background:#979797;border-radius:3px;padding:0px !important;border:1px solid var(--sdpi-background)} +input[type="range"]::-webkit-slider-thumb{position:relative;-webkit-appearance:none;background-color:var(--sdpi-color);width:12px;height:12px;border-radius:20px;margin-top:-5px;border:none} +input[type="range" i]{margin:0} +input[type="range"]::-webkit-slider-thumb::before{position:absolute;content:"";height:5px;width:500px;left:-502px;top:8px;background:#77f} +input[type="color"]{min-width:32px;min-height:32px;width:32px;height:32px;padding:0;background-color:var(--sdpi-bgcolor);flex:none} +::-webkit-color-swatch{min-width:24px} +textarea{height:3em;word-break:break-word;line-height:1.5em} +.textarea{padding:0px !important} +textarea{width:219px;height:96%;min-height:6em;resize:none;border-radius:var(--sdpi-borderradius)} +.sdpi-item.card-carousel-wrapper,.sdpi-item>.card-carousel-wrapper{padding:0} +.card-carousel-wrapper{display:flex;align-items:center;justify-content:center;margin:12px auto;color:#666a73} +.card-carousel{display:flex;justify-content:center;width:278px} +.card-carousel--overflow-container{overflow:hidden} +.card-carousel--nav__left,.card-carousel--nav__right{width:12px;height:12px;border-top:2px solid #42b883;border-right:2px solid #42b883;cursor:pointer;margin:0 4px;transition:transform 150ms linear} +.card-carousel--nav__left[disabled],.card-carousel--nav__right[disabled]{opacity:0.2;border-color:black} +.card-carousel--nav__left{transform:rotate(-135deg)} +.card-carousel--nav__left:active{transform:rotate(-135deg) scale(0.85)} +.card-carousel--nav__right{transform:rotate(45deg)} +.card-carousel--nav__right:active{transform:rotate(45deg) scale(0.85)} +.card-carousel-cards{display:flex;transition:transform 150ms ease-out;transform:translatex(0px)} +.card-carousel-cards .card-carousel--card{margin:0 5px;cursor:pointer;background-color:#fff;border-radius:4px;z-index:3} +.xxcard-carousel-cards .card-carousel--card:first-child{margin-left:0} +.xxcard-carousel-cards .card-carousel--card:last-child{margin-right:0} +.card-carousel-cards .card-carousel--card img{vertical-align:bottom;border-top-left-radius:4px;border-top-right-radius:4px;transition:opacity 150ms linear;width:60px} +.card-carousel-cards .card-carousel--card img:hover{opacity:0.5} +.card-carousel-cards .card-carousel--card--footer{border-top:0;max-width:80px;overflow:hidden;display:flex;height:100%;flex-direction:column} +.card-carousel-cards .card-carousel--card--footer p{padding:3px 0;margin:0;margin-bottom:2px;font-size:15px;font-weight:500;color:#2c3e50} +.card-carousel-cards .card-carousel--card--footer p:nth-of-type(2){font-size:12px;font-weight:300;padding:6px;color:#666a73} +h1{font-size:1.3em;font-weight:500;text-align:center;margin-bottom:12px} +::-webkit-datetime-edit{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";background:url(elg_calendar_inv.svg) no-repeat left center;padding-right:1em;padding-left:25px;background-position:4px 0px} +::-webkit-calendar-picker-indicator{background:transparent;font-size:17px} +::-webkit-calendar-picker-indicator:focus{background-color:rgba(0,0,0,0.2)} +input[type="date"]{-webkit-align-items:center;align-items:center;display:-webkit-inline-flex;font-family:monospace;overflow:hidden;padding:0;-webkit-padding-start:1px} +input::-webkit-datetime-edit{-webkit-flex:1;-webkit-user-modify:read-only !important;display:inline-block;min-width:0;overflow:hidden} +input[type="file"]{opacity:0;display:none} +.sdpi-item>input[type="file"]{opacity:1;display:flex} +input[type="file"]+span{display:flex;flex:0 1 auto;background-color:#0000ff50} +label.sdpi-file-label{cursor:pointer;user-select:none;display:inline-block;min-height:21px !important;height:21px !important;line-height:20px;padding:0px 4px;margin:auto;margin-right:0px} +.sdpi-file-label>label:active,.sdpi-file-label.file:active,label.sdpi-file-label:active,label.sdpi-file-info:active,input[type="file"]::-webkit-file-upload-button:active,button:active{background-color:var(--sdpi-color);color:#303030} +input:required:invalid,input:focus:invalid{background:var(--sdpi-background) url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5IiBoZWlnaHQ9IjkiIHZpZXdCb3g9IjAgMCA5IDkiPgogICAgPHBhdGggZmlsbD0iI0Q4RDhEOCIgZD0iTTQuNSwwIEM2Ljk4NTI4MTM3LC00LjU2NTM4NzgyZS0xNiA5LDIuMDE0NzE4NjMgOSw0LjUgQzksNi45ODUyODEzNyA2Ljk4NTI4MTM3LDkgNC41LDkgQzIuMDE0NzE4NjMsOSAzLjA0MzU5MTg4ZS0xNiw2Ljk4NTI4MTM3IDAsNC41IEMtMy4wNDM1OTE4OGUtMTYsMi4wMTQ3MTg2MyAyLjAxNDcxODYzLDQuNTY1Mzg3ODJlLTE2IDQuNSwwIFogTTQsMSBMNCw2IEw1LDYgTDUsMSBMNCwxIFogTTQuNSw4IEM0Ljc3NjE0MjM3LDggNSw3Ljc3NjE0MjM3IDUsNy41IEM1LDcuMjIzODU3NjMgNC43NzYxNDIzNyw3IDQuNSw3IEM0LjIyMzg1NzYzLDcgNCw3LjIyMzg1NzYzIDQsNy41IEM0LDcuNzc2MTQyMzcgNC4yMjM4NTc2Myw4IDQuNSw4IFoiLz4KICA8L3N2Zz4) no-repeat 98% center} +input:required:valid{background:var(--sdpi-background) url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5IiBoZWlnaHQ9IjkiIHZpZXdCb3g9IjAgMCA5IDkiPjxwb2x5Z29uIGZpbGw9IiNEOEQ4RDgiIHBvaW50cz0iNS4yIDEgNi4yIDEgNi4yIDcgMy4yIDcgMy4yIDYgNS4yIDYiIHRyYW5zZm9ybT0icm90YXRlKDQwIDQuNjc3IDQpIi8+PC9zdmc+) no-repeat 98% center} +.tooltip,:tooltip,:title{color:yellow} +.sdpi-item-group.file{width:232px;display:flex;align-items:center} +.sdpi-file-info{overflow-wrap:break-word;word-wrap:break-word;hyphens:auto;min-width:132px;max-width:144px;max-height:32px;margin-top:0px;margin-left:5px;display:inline-block;overflow:hidden;padding:6px 4px;background-color:var(--sdpi-background)} +::-webkit-scrollbar{width:8px} +::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 6px rgba(0,0,0,0.3)} +::-webkit-scrollbar-thumb{background-color:#999999;outline:1px solid slategrey;border-radius:8px} +a{color:#7397d2} +.testcontainer{display:flex;background-color:#0000ff20;max-width:400px;height:200px;align-content:space-evenly} +input[type=range]{-webkit-appearance:none;appearance:none;height:6px;margin-top:12px;z-index:0;overflow:visible} +:-webkit-slider-thumb{-webkit-appearance:none;background-color:var(--sdpi-color);width:16px;height:16px;border-radius:20px;margin-top:-6px;border:1px solid #999999} +.sdpi-item[type="range"] .sdpi-item-group{display:flex;flex-direction:column} +.xxsdpi-item[type="range"] .sdpi-item-group input{max-width:204px} +.sdpi-item[type="range"] .sdpi-item-group span{margin-left:0px !important} +.sdpi-item[type="range"] .sdpi-item-group>.sdpi-item-child{display:flex;flex-direction:row} +.rangeLabel{position:absolute;font-weight:normal;margin-top:22px} +:disabled{color:#993333} +select,select option{color:var(--sdpi-color)} +select.disabled,select option:disabled{color:#fd9494;font-style:italic} +.runningAppsContainer{display:none} +.one-line{min-height:1.5em} +.two-lines{min-height:3em} +.three-lines{min-height:4.5em} +.four-lines{min-height:6em} +.min80>.sdpi-item-child{min-width:80px} +.min100>.sdpi-item-child{min-width:100px} +.min120>.sdpi-item-child{min-width:120px} +.min140>.sdpi-item-child{min-width:140px} +.min160>.sdpi-item-child{min-width:160px} +.min200>.sdpi-item-child{min-width:200px} +.max40{flex-basis:40%;flex-grow:0} +.max30{flex-basis:30%;flex-grow:0} +.max20{flex-basis:20%;flex-grow:0} +.up20{margin-top:-20px} +.alignCenter{align-items:center} +.alignTop{align-items:flex-start} +.alignBaseline{align-items:baseline} +.noMargins,.noMargins *,.noInnerMargins *{margin:0;padding:0} +.hidden{display:none} +.icon-help,.icon-help-line,.icon-help-fill,.icon-help-inv,.icon-brighter,.icon-darker,.icon-warmer,.icon-cooler{min-width:20px;width:20px;background-repeat:no-repeat;opacity:1} +.icon-help:active,.icon-help-line:active,.icon-help-fill:active,.icon-help-inv:active,.icon-brighter:active,.icon-darker:active,.icon-warmer:active,.icon-cooler:active{opacity:0.5} +.icon-brighter,.icon-darker,.icon-warmer,.icon-cooler{margin-top:5px !important} +.icon-help,.icon-help-line,.icon-help-fill,.icon-help-inv{cursor:pointer;margin:0px;margin-left:4px} +.icon-brighter{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Ccircle cx='10' cy='10' r='4'/%3E%3Cpath d='M14.8532861,7.77530426 C14.7173255,7.4682615 14.5540843,7.17599221 14.3666368,6.90157083 L16.6782032,5.5669873 L17.1782032,6.4330127 L14.8532861,7.77530426 Z M10.5,4.5414007 C10.2777625,4.51407201 10.051423,4.5 9.82179677,4.5 C9.71377555,4.5 9.60648167,4.50311409 9.5,4.50925739 L9.5,2 L10.5,2 L10.5,4.5414007 Z M5.38028092,6.75545367 C5.18389364,7.02383457 5.01124349,7.31068015 4.86542112,7.61289977 L2.82179677,6.4330127 L3.32179677,5.5669873 L5.38028092,6.75545367 Z M4.86542112,12.3871002 C5.01124349,12.6893198 5.18389364,12.9761654 5.38028092,13.2445463 L3.32179677,14.4330127 L2.82179677,13.5669873 L4.86542112,12.3871002 Z M9.5,15.4907426 C9.60648167,15.4968859 9.71377555,15.5 9.82179677,15.5 C10.051423,15.5 10.2777625,15.485928 10.5,15.4585993 L10.5,18 L9.5,18 L9.5,15.4907426 Z M14.3666368,13.0984292 C14.5540843,12.8240078 14.7173255,12.5317385 14.8532861,12.2246957 L17.1782032,13.5669873 L16.6782032,14.4330127 L14.3666368,13.0984292 Z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-darker{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10 14C7.790861 14 6 12.209139 6 10 6 7.790861 7.790861 6 10 6 12.209139 6 14 7.790861 14 10 14 12.209139 12.209139 14 10 14zM10 13C11.6568542 13 13 11.6568542 13 10 13 8.34314575 11.6568542 7 10 7 8.34314575 7 7 8.34314575 7 10 7 11.6568542 8.34314575 13 10 13zM14.8532861 7.77530426C14.7173255 7.4682615 14.5540843 7.17599221 14.3666368 6.90157083L16.6782032 5.5669873 17.1782032 6.4330127 14.8532861 7.77530426zM10.5 4.5414007C10.2777625 4.51407201 10.051423 4.5 9.82179677 4.5 9.71377555 4.5 9.60648167 4.50311409 9.5 4.50925739L9.5 2 10.5 2 10.5 4.5414007zM5.38028092 6.75545367C5.18389364 7.02383457 5.01124349 7.31068015 4.86542112 7.61289977L2.82179677 6.4330127 3.32179677 5.5669873 5.38028092 6.75545367zM4.86542112 12.3871002C5.01124349 12.6893198 5.18389364 12.9761654 5.38028092 13.2445463L3.32179677 14.4330127 2.82179677 13.5669873 4.86542112 12.3871002zM9.5 15.4907426C9.60648167 15.4968859 9.71377555 15.5 9.82179677 15.5 10.051423 15.5 10.2777625 15.485928 10.5 15.4585993L10.5 18 9.5 18 9.5 15.4907426zM14.3666368 13.0984292C14.5540843 12.8240078 14.7173255 12.5317385 14.8532861 12.2246957L17.1782032 13.5669873 16.6782032 14.4330127 14.3666368 13.0984292z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-warmer{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M12.3247275 11.4890349C12.0406216 11.0007637 11.6761954 10.5649925 11.2495475 10.1998198 11.0890394 9.83238991 11 9.42659309 11 9 11 7.34314575 12.3431458 6 14 6 15.6568542 6 17 7.34314575 17 9 17 10.6568542 15.6568542 12 14 12 13.3795687 12 12.8031265 11.8116603 12.3247275 11.4890349zM17.6232392 11.6692284C17.8205899 11.4017892 17.9890383 11.1117186 18.123974 10.8036272L20.3121778 12.0669873 19.8121778 12.9330127 17.6232392 11.6692284zM18.123974 7.19637279C17.9890383 6.88828142 17.8205899 6.5982108 17.6232392 6.33077158L19.8121778 5.0669873 20.3121778 5.9330127 18.123974 7.19637279zM14.5 4.52746439C14.3358331 4.50931666 14.1690045 4.5 14 4.5 13.8309955 4.5 13.6641669 4.50931666 13.5 4.52746439L13.5 2 14.5 2 14.5 4.52746439zM13.5 13.4725356C13.6641669 13.4906833 13.8309955 13.5 14 13.5 14.1690045 13.5 14.3358331 13.4906833 14.5 13.4725356L14.5 16 13.5 16 13.5 13.4725356zM14 11C15.1045695 11 16 10.1045695 16 9 16 7.8954305 15.1045695 7 14 7 12.8954305 7 12 7.8954305 12 9 12 10.1045695 12.8954305 11 14 11zM9.5 11C10.6651924 11.4118364 11.5 12.5 11.5 14 11.5 16 10 17.5 8 17.5 6 17.5 4.5 16 4.5 14 4.5 12.6937812 5 11.5 6.5 11L6.5 7 9.5 7 9.5 11z'/%3E%3Cpath d='M12,14 C12,16.209139 10.209139,18 8,18 C5.790861,18 4,16.209139 4,14 C4,12.5194353 4.80439726,11.2267476 6,10.5351288 L6,4 C6,2.8954305 6.8954305,2 8,2 C9.1045695,2 10,2.8954305 10,4 L10,10.5351288 C11.1956027,11.2267476 12,12.5194353 12,14 Z M11,14 C11,12.6937812 10.1651924,11.5825421 9,11.1707057 L9,4 C9,3.44771525 8.55228475,3 8,3 C7.44771525,3 7,3.44771525 7,4 L7,11.1707057 C5.83480763,11.5825421 5,12.6937812 5,14 C5,15.6568542 6.34314575,17 8,17 C9.65685425,17 11,15.6568542 11,14 Z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-cooler{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10.4004569 11.6239517C10.0554735 10.9863849 9.57597206 10.4322632 9 9.99963381L9 9.7450467 9.53471338 9.7450467 10.8155381 8.46422201C10.7766941 8.39376637 10.7419749 8.32071759 10.7117062 8.2454012L9 8.2454012 9 6.96057868 10.6417702 6.96057868C10.6677696 6.86753378 10.7003289 6.77722682 10.7389179 6.69018783L9.44918707 5.40045694 9 5.40045694 9 4.34532219 9.32816127 4.34532219 9.34532219 2.91912025 10.4004569 2.91912025 10.4004569 4.53471338 11.6098599 5.74411634C11.7208059 5.68343597 11.8381332 5.63296451 11.9605787 5.59396526L11.9605787 3.8884898 10.8181818 2.74609294 11.5642748 2 12.5727518 3.00847706 13.5812289 2 14.3273218 2.74609294 13.2454012 3.82801356 13.2454012 5.61756719C13.3449693 5.65339299 13.4408747 5.69689391 13.5324038 5.74735625L14.7450467 4.53471338 14.7450467 2.91912025 15.8001815 2.91912025 15.8001815 4.34532219 17.2263834 4.34532219 17.2263834 5.40045694 15.6963166 5.40045694 14.4002441 6.69652946C14.437611 6.78161093 14.4692249 6.86979146 14.4945934 6.96057868L16.2570138 6.96057868 17.3994107 5.81818182 18.1455036 6.56427476 17.1370266 7.57275182 18.1455036 8.58122888 17.3994107 9.32732182 16.3174901 8.2454012 14.4246574 8.2454012C14.3952328 8.31861737 14.3616024 8.38969062 14.3240655 8.45832192L15.6107903 9.7450467 17.2263834 9.7450467 17.2263834 10.8001815 15.8001815 10.8001815 15.8001815 12.2263834 14.7450467 12.2263834 14.7450467 10.6963166 13.377994 9.32926387C13.3345872 9.34850842 13.2903677 9.36625331 13.2454012 9.38243281L13.2454012 11.3174901 14.3273218 12.3994107 13.5812289 13.1455036 12.5848864 12.1491612 11.5642748 13.1455036 10.8181818 12.3994107 11.9605787 11.2570138 11.9605787 9.40603474C11.8936938 9.38473169 11.828336 9.36000556 11.7647113 9.33206224L10.4004569 10.6963166 10.4004569 11.6239517zM12.75 8.5C13.3022847 8.5 13.75 8.05228475 13.75 7.5 13.75 6.94771525 13.3022847 6.5 12.75 6.5 12.1977153 6.5 11.75 6.94771525 11.75 7.5 11.75 8.05228475 12.1977153 8.5 12.75 8.5zM9.5 14C8.5 16.3333333 7.33333333 17.5 6 17.5 4.66666667 17.5 3.5 16.3333333 2.5 14L9.5 14z'/%3E%3Cpath d='M10,14 C10,16.209139 8.209139,18 6,18 C3.790861,18 2,16.209139 2,14 C2,12.5194353 2.80439726,11.2267476 4,10.5351288 L4,4 C4,2.8954305 4.8954305,2 6,2 C7.1045695,2 8,2.8954305 8,4 L8,10.5351288 C9.19560274,11.2267476 10,12.5194353 10,14 Z M9,14 C9,12.6937812 8.16519237,11.5825421 7,11.1707057 L7,4 C7,3.44771525 6.55228475,3 6,3 C5.44771525,3 5,3.44771525 5,4 L5,11.1707057 C3.83480763,11.5825421 3,12.6937812 3,14 C3,15.6568542 4.34314575,17 6,17 C7.65685425,17 9,15.6568542 9,14 Z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-help{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cpath fill='%23999' d='M11.292 12.516l.022 1.782H9.07v-1.804c0-1.98 1.276-2.574 2.662-3.278h-.022c.814-.44 1.65-.88 1.694-2.2.044-1.386-1.122-2.728-3.234-2.728-1.518 0-2.662.902-3.366 2.354L5 5.608C5.946 3.584 7.662 2 10.17 2c3.564 0 5.632 2.442 5.588 5.06-.066 2.618-1.716 3.41-3.102 4.158-.704.374-1.364.682-1.364 1.298zm-1.122 2.442c.858 0 1.452.594 1.452 1.452 0 .682-.594 1.408-1.452 1.408-.77 0-1.386-.726-1.386-1.408 0-.858.616-1.452 1.386-1.452z'/%3E%3C/svg%3E")} +.icon-help-line{background-image:url("data:image/svg+xml,%3Csvg width='20' height='20' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-1a9 9 0 1 0 0-18 9 9 0 0 0 0 18z'/%3E%3Cpath d='M10.848 12.307l.02 1.578H8.784v-1.597c0-1.753 1.186-2.278 2.474-2.901h-.02c.756-.39 1.533-.78 1.574-1.948.041-1.226-1.043-2.414-3.006-2.414-1.41 0-2.474.798-3.128 2.083L5 6.193C5.88 4.402 7.474 3 9.805 3 13.118 3 15.04 5.161 15 7.478c-.061 2.318-1.595 3.019-2.883 3.68-.654.332-1.268.604-1.268 1.15zM9.805 14.47c.798 0 1.35.525 1.35 1.285 0 .603-.552 1.246-1.35 1.246-.715 0-1.288-.643-1.288-1.246 0-.76.573-1.285 1.288-1.285z' fill-rule='nonzero'/%3E%3C/g%3E%3C/svg%3E")} +.icon-help-fill{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Ccircle cx='10' cy='10' r='10' fill='%23999'/%3E%3Cpath fill='%23FFF' fill-rule='nonzero' d='M8.368 7.189H5C5 3.5 7.668 2 10.292 2 13.966 2 16 4.076 16 7.012c0 3.754-3.849 3.136-3.849 5.211v1.656H8.455v-1.832c0-2.164 1.4-2.893 2.778-3.6.437-.242 1.006-.574 1.006-1.236 0-2.208-3.871-2.142-3.871-.022zM10.25 18a1.75 1.75 0 1 1 0-3.5 1.75 1.75 0 0 1 0 3.5z'/%3E%3C/g%3E%3C/svg%3E")} +.icon-help-inv{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cpath fill='%23999' fill-rule='evenodd' d='M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zM8.368 7.189c0-2.12 3.87-2.186 3.87.022 0 .662-.568.994-1.005 1.236-1.378.707-2.778 1.436-2.778 3.6v1.832h3.696v-1.656c0-2.075 3.849-1.457 3.849-5.21C16 4.075 13.966 2 10.292 2 7.668 2 5 3.501 5 7.189h3.368zM10.25 18a1.75 1.75 0 1 0 0-3.5 1.75 1.75 0 0 0 0 3.5z'/%3E%3C/svg%3E")} +.kelvin::after{content:"K"} +.mired::after{content:" Mired"} +.percent::after{content:"%"} +.sdpi-item-value+.icon-cooler,.sdpi-item-value+.icon-warmer{margin-left:0px !important;margin-top:15px !important} +input[type="range"].colorbrightness::-webkit-slider-runnable-track,input[type="range"].colortemperature::-webkit-slider-runnable-track{height:8px;background:#979797;border-radius:4px;background-image:linear-gradient(to right,#94d0ec,#ffb165)} +input[type="range"].colorbrightness::-webkit-slider-runnable-track{background-color:#efefef;background-image:linear-gradient(to right,black,rgba(0,0,0,0))} +input[type="range"].colorbrightness::-webkit-slider-thumb,input[type="range"].colortemperature::-webkit-slider-thumb{width:16px;height:16px;border-radius:20px;margin-top:-5px;background-color:#86c6e8;box-shadow:0px 0px 1px #000000;border:1px solid #d8d8d8} +.sdpi-info-label{display:inline-block;user-select:none;position:absolute;height:15px;width:auto;text-align:center;border-radius:4px;min-width:44px;max-width:80px;background:white;font-size:11px;color:black;z-index:1000;box-shadow:0px 0px 12px rgba(0,0,0,.8);padding:2px} +.sdpi-info-label.hidden{opacity:0;transition:opacity 0.25s linear} +.sdpi-info-label.shown{position:absolute;opacity:1;transition:opacity 0.25s ease-out} \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/utils/color.js b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/utils/color.js new file mode 100644 index 000000000..52db3485f --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/utils/color.js @@ -0,0 +1,78 @@ +// 霓虹数组 +const $neonColorArr = [ + rgb(255, 0, 0), + rgb(255, 0, 255), + rgb(0, 0, 255), + rgb(0, 255, 255), + rgb(0, 255, 0), + rgb(255, 255, 0), + rgb(255, 0, 0) +] + +// RGB转16进制颜色值 +const $rgbToHex = (r, g, b) => { + return ((1 << 24) | (r << 16) | (g << 8) | b).toString(16).replace('1', '#'); +}; + +// 16进制颜色值转RGB +const $hexToRgb = (hex) => { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16) + }; +}; + +// 根据色相转RGB +const $hueToRgb = (p, q, t) => { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; + return p; +}; + +// 根据RGB转色调 +const $rgbToHsl = (rgb) => { + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + + // 计算亮度饱和度 + let saturation = 0; + const lightness = (max + min) / 2; + if (max !== min) { + const delta = max - min; + saturation = delta / (1 - Math.abs(2 * lightness - 1)); + } + + // 计算色调 + let hue = 0; + if (max === r) hue = ((g - b) / (max - min)) % 6; + else if (max === g) hue = (b - r) / (max - min) + 2; + else hue = (r - g) / (max - min) + 4; hue *= 60; + if (hue < 0) hue += 360; + return [hue, saturation * 100, lightness * 100]; +}; + +// 根据色调转rgb +const $hslToRgb = (hsl) => { + const hue = hsl[0] / 360; + const saturation = hsl[1] / 100; + const lightness = hsl[2] / 100; + let r, g, b; + if (saturation === 0) { + r = g = b = lightness; + } else { + const q = lightness < 0.5 ? lightness * (1 + saturation) : lightness + saturation - lightness * saturation; + const p = 2 * lightness - q; + r = $hueToRgb(p, q, hue + 1 / 3); + g = $hueToRgb(p, q, hue); + b = $hueToRgb(p, q, hue - 1 / 3); + } + return [r * 255, g * 255, b * 255]; +}; \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/utils/common.js b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/utils/common.js new file mode 100644 index 000000000..7e50d09fc --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/utils/common.js @@ -0,0 +1,84 @@ +// 自定义事件类 +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); + }; +}; + +// 限制数字 +$.num = (selector) => { + if (!selector.value || /^\d+$/.test(selector.value)) return; + selector.value = selector.value.slice(0, -1); + $.num(selector); +}; + +// 绑定限制数字方法 +Array.from($('input[type="num"]', true)).forEach(item => { + item.addEventListener('input', () => $.num(item)); +}); \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/utils/worker.js b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/utils/worker.js new file mode 100644 index 000000000..b01b4f37e --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/static/utils/worker.js @@ -0,0 +1,5 @@ +self.onmessage = function (e) { + setInterval(() => { + self.postMessage(1) + }, e.data); +} \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/streamdockPluginExample.exe b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/streamdockPluginExample.exe new file mode 100644 index 000000000..803b6f51d Binary files /dev/null and b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/streamdockPluginExample.exe differ diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/zh_CN.json b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/zh_CN.json new file mode 100644 index 000000000..369bb3dbd --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/com.hotspot.cppsdk.demo.sdPlugin/zh_CN.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:125925a02dd79cb90ccc1d3958bf32ba9b33da2c7030e7749a7d8b86084fe8e6 +size 379 diff --git a/StreamDock-Plugin-SDK/StreamDockCPPSDK/main.cpp b/StreamDock-Plugin-SDK/StreamDockCPPSDK/main.cpp new file mode 100644 index 000000000..aa4e174d8 --- /dev/null +++ b/StreamDock-Plugin-SDK/StreamDockCPPSDK/main.cpp @@ -0,0 +1,8 @@ +#include "HSDExamplePlugin.h" + +#include "StreamDockCPPSDK/StreamDockSDK/HSDMain.h" + +int main(int argc, const char** argv) { + auto plugin = std::make_unique(); + return esd_main(argc, argv, plugin.get()); +} diff --git a/StreamDock-Plugin-SDK/Unity_Scripts/SimpleEventExample.cs b/StreamDock-Plugin-SDK/Unity_Scripts/SimpleEventExample.cs new file mode 100644 index 000000000..c34d111db --- /dev/null +++ b/StreamDock-Plugin-SDK/Unity_Scripts/SimpleEventExample.cs @@ -0,0 +1,101 @@ +using UnityEngine; + +public class SimpleEventExample : MonoBehaviour +{ + [Header("StreamDock 통신")] + [SerializeField] private SimpleStreamDockCommunicator streamDock; + + void Start() + { + // StreamDock 이벤트 구독 + if (streamDock != null) + { + streamDock.OnStreamDockMessageReceived.AddListener(OnStreamDockMessage); + streamDock.OnConnected.AddListener(OnStreamDockConnected); + streamDock.OnDisconnected.AddListener(OnStreamDockDisconnected); + } + } + + /// + /// StreamDock에서 메시지 수신 시 처리 + /// + private void OnStreamDockMessage(string eventType, object data) + { + Debug.Log($"StreamDock 이벤트 수신: {eventType}"); + + switch (eventType) + { + case "button_clicked": + // 버튼 클릭 시 실행할 코드 + Debug.Log("버튼 클릭 이벤트 실행!"); + DoSomething(); + break; + + case "dial_rotate": + // 다이얼 회전 시 실행할 코드 + Debug.Log("다이얼 회전 이벤트 실행!"); + DoSomethingElse(); + break; + + case "dial_press": + // 다이얼 누름 시 실행할 코드 + Debug.Log("다이얼 누름 이벤트 실행!"); + DoAnotherThing(); + break; + } + } + + /// + /// StreamDock 연결 시 처리 + /// + private void OnStreamDockConnected() + { + Debug.Log("StreamDock에 연결되었습니다!"); + } + + /// + /// StreamDock 연결 해제 시 처리 + /// + private void OnStreamDockDisconnected() + { + Debug.Log("StreamDock 연결이 해제되었습니다."); + } + + // 여기에 원하는 동작들을 구현하세요 + private void DoSomething() + { + Debug.Log("버튼 클릭으로 실행된 동작!"); + // 예: 오브젝트 활성화/비활성화, 애니메이션 재생, 사운드 재생 등 + } + + private void DoSomethingElse() + { + Debug.Log("다이얼 회전으로 실행된 동작!"); + // 예: 볼륨 조절, 카메라 회전, 값 변경 등 + } + + private void DoAnotherThing() + { + Debug.Log("다이얼 누름으로 실행된 동작!"); + // 예: 특수 기능 실행, 모드 변경 등 + } + + // 공개 메서드들 (Inspector에서 호출 가능) + [ContextMenu("테스트 동작 1")] + public void TestAction1() + { + DoSomething(); + } + + [ContextMenu("테스트 동작 2")] + public void TestAction2() + { + DoSomethingElse(); + } + + [ContextMenu("테스트 동작 3")] + public void TestAction3() + { + DoAnotherThing(); + } +} \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/Unity_Scripts/SimpleStreamDockCommunicator.cs b/StreamDock-Plugin-SDK/Unity_Scripts/SimpleStreamDockCommunicator.cs new file mode 100644 index 000000000..2de1421a1 --- /dev/null +++ b/StreamDock-Plugin-SDK/Unity_Scripts/SimpleStreamDockCommunicator.cs @@ -0,0 +1,282 @@ +using UnityEngine; +using UnityEngine.Events; +using System; +using System.Collections; +using System.Text; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; + +[System.Serializable] +public class StreamDockEvent : UnityEvent { } + +public class SimpleStreamDockCommunicator : MonoBehaviour +{ + [Header("연결 설정")] + [SerializeField] private string serverUrl = "ws://localhost:15732"; + [SerializeField] private bool autoConnect = true; + [SerializeField] private float reconnectInterval = 5f; + + [Header("이벤트")] + public StreamDockEvent OnStreamDockMessageReceived; + public UnityEvent OnConnected; + public UnityEvent OnDisconnected; + + // 내부 변수 + private ClientWebSocket webSocket; + private CancellationTokenSource cancellationTokenSource; + private bool isConnecting = false; + private bool isConnected = false; + + // 프로퍼티 + public bool IsConnected => isConnected; + + void Start() + { + if (autoConnect) + { + ConnectToStreamDock(); + } + } + + void OnDestroy() + { + DisconnectFromStreamDock(); + } + + /// + /// StreamDock에 연결 + /// + public async void ConnectToStreamDock() + { + if (isConnecting || isConnected) return; + + isConnecting = true; + + try + { + webSocket = new ClientWebSocket(); + cancellationTokenSource = new CancellationTokenSource(); + + Debug.Log($"StreamDock에 연결 중... {serverUrl}"); + + await webSocket.ConnectAsync(new Uri(serverUrl), cancellationTokenSource.Token); + + isConnected = true; + isConnecting = false; + + Debug.Log("StreamDock에 연결되었습니다!"); + OnConnected?.Invoke(); + + // 메시지 수신 시작 + _ = ReceiveMessages(); + + } + catch (Exception e) + { + Debug.LogError($"StreamDock 연결 실패: {e.Message}"); + isConnecting = false; + OnDisconnected?.Invoke(); + + // 재연결 시도 + StartCoroutine(TryReconnect()); + } + } + + /// + /// StreamDock 연결 해제 + /// + public async void DisconnectFromStreamDock() + { + if (!isConnected) return; + + try + { + cancellationTokenSource?.Cancel(); + + if (webSocket != null && webSocket.State == WebSocketState.Open) + { + await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Unity 종료", CancellationToken.None); + } + } + catch (Exception e) + { + Debug.LogError($"연결 해제 중 오류: {e.Message}"); + } + finally + { + isConnected = false; + webSocket?.Dispose(); + webSocket = null; + OnDisconnected?.Invoke(); + } + } + + /// + /// StreamDock으로 메시지 전송 + /// + public async void SendMessageToStreamDock(string eventType, object data = null) + { + if (!isConnected) + { + Debug.LogWarning("StreamDock에 연결되지 않았습니다."); + return; + } + + try + { + var message = new + { + type = eventType, + data = data, + timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + }; + + string jsonMessage = JsonUtility.ToJson(message); + byte[] buffer = Encoding.UTF8.GetBytes(jsonMessage); + + await webSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, cancellationTokenSource.Token); + + Debug.Log($"StreamDock으로 메시지 전송: {eventType}"); + } + catch (Exception e) + { + Debug.LogError($"메시지 전송 실패: {e.Message}"); + } + } + + /// + /// 메시지 수신 처리 + /// + private async Task ReceiveMessages() + { + var buffer = new byte[4096]; + + try + { + while (webSocket.State == WebSocketState.Open) + { + var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), cancellationTokenSource.Token); + + if (result.MessageType == WebSocketMessageType.Text) + { + string message = Encoding.UTF8.GetString(buffer, 0, result.Count); + ProcessReceivedMessage(message); + } + else if (result.MessageType == WebSocketMessageType.Close) + { + Debug.Log("StreamDock에서 연결을 종료했습니다."); + break; + } + } + } + catch (Exception e) + { + Debug.LogError($"메시지 수신 중 오류: {e.Message}"); + } + finally + { + isConnected = false; + OnDisconnected?.Invoke(); + StartCoroutine(TryReconnect()); + } + } + + /// + /// 수신된 메시지 처리 + /// + private void ProcessReceivedMessage(string message) + { + try + { + Debug.Log($"StreamDock에서 메시지 수신: {message}"); + + // JSON 파싱 (간단한 구조) + if (message.Contains("type")) + { + // UnityEvent 호출 + OnStreamDockMessageReceived?.Invoke("streamdock_message", message); + + // 특정 이벤트 타입 처리 + if (message.Contains("connection_confirmed")) + { + Debug.Log("StreamDock 플러그인 연결 확인됨!"); + } + else if (message.Contains("button_clicked")) + { + HandleButtonClick(message); + } + else if (message.Contains("streamdock_button_clicked")) + { + HandleButtonClick(message); + } + else if (message.Contains("dial_rotate")) + { + HandleDialRotate(message); + } + else if (message.Contains("dial_press")) + { + HandleDialPress(message); + } + } + } + catch (Exception e) + { + Debug.LogError($"메시지 처리 중 오류: {e.Message}"); + } + } + + /// + /// 버튼 클릭 이벤트 처리 + /// + private void HandleButtonClick(string message) + { + Debug.Log("StreamDock 버튼이 클릭되었습니다!"); + OnStreamDockMessageReceived?.Invoke("button_clicked", message); + } + + /// + /// 다이얼 회전 이벤트 처리 + /// + private void HandleDialRotate(string message) + { + Debug.Log("StreamDock 다이얼이 회전했습니다!"); + OnStreamDockMessageReceived?.Invoke("dial_rotate", message); + } + + /// + /// 다이얼 누름 이벤트 처리 + /// + private void HandleDialPress(string message) + { + Debug.Log("StreamDock 다이얼이 눌렸습니다!"); + OnStreamDockMessageReceived?.Invoke("dial_press", message); + } + + /// + /// 재연결 시도 + /// + private IEnumerator TryReconnect() + { + yield return new WaitForSeconds(reconnectInterval); + + if (!isConnected && !isConnecting) + { + Debug.Log("StreamDock 재연결 시도..."); + ConnectToStreamDock(); + } + } + + // 테스트용 메서드들 + [ContextMenu("테스트 메시지 전송")] + public void SendTestMessage() + { + SendMessageToStreamDock("test_message", new { message = "Unity에서 테스트 메시지" }); + } + + [ContextMenu("커스텀 이벤트 전송")] + public void SendCustomEvent() + { + SendMessageToStreamDock("custom_event", new { action = "test_action", value = 123 }); + } +} \ No newline at end of file diff --git a/StreamDock-Plugin-SDK/Unity_StreamDock_통신_가이드.md b/StreamDock-Plugin-SDK/Unity_StreamDock_통신_가이드.md new file mode 100644 index 000000000..27d501238 --- /dev/null +++ b/StreamDock-Plugin-SDK/Unity_StreamDock_통신_가이드.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:57dbf8a7ce2579a8d535fa455b29840944cd8dd76f74746cdfdfd9e88ff35790 +size 7438 diff --git a/StreamDock-Plugin-SDK/개발환경_설정_가이드.md b/StreamDock-Plugin-SDK/개발환경_설정_가이드.md new file mode 100644 index 000000000..b80b43c3b --- /dev/null +++ b/StreamDock-Plugin-SDK/개발환경_설정_가이드.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02b74155abfe52c46e8b39cad2a7b629c49a93b28c22dfca83118dd29b39ad65 +size 3165 diff --git a/StreamDock-Plugin-SDK/샘플_플러그인_개발_가이드.md b/StreamDock-Plugin-SDK/샘플_플러그인_개발_가이드.md new file mode 100644 index 000000000..943e682e1 --- /dev/null +++ b/StreamDock-Plugin-SDK/샘플_플러그인_개발_가이드.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5b9735aaac3c54383e2905f2f3b73575ec019adad3095dc59238f325341e653 +size 7973